Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
main.cpp
- Committer:
- bracz
- Date:
- 2013-09-24
- Revision:
- 15:c9b08298852f
- Parent:
- 14:2f2f9eb5532f
File content as of revision 15:c9b08298852f:
#include <queue> #include <algorithm> #include <string> #include "mbed.h" DigitalOut myled(LED1); DigitalOut led2(LED2); Serial pc(USBTX, USBRX); SPI spi(p5, p6, p20); // mosi, miso, sclk DigitalOut latch(p16); DigitalOut strobe(p17); Ticker g_ticker; void set_frequency(float f); #define LENGTH 160 uint8_t strip[160]; #define BLACK 0x80 #define BLUE (BLACK | 0x10) #define RED (BLACK | 0x04) #define GREEN (BLACK | 0x01) #define YELLOW (RED | GREEN) #define CYAN (GREEN | BLUE) #define MAGENTA (RED | BLUE) #define WHITE (RED | GREEN | BLUE) #define FAST 0x40 uint8_t fade_result[] = {0, 2, 3, 1}; uint8_t google_colors[] = {BLUE, RED, YELLOW, BLUE, GREEN, RED}; uint8_t getbit(uint8_t from, uint8_t to) { return fade_result[((from&1) << 1) | (to & 1)]; } uint8_t getcolor(uint8_t from, uint8_t to) { uint8_t result = 0x80; result |= getbit(from >> 0, to >> 0) << 0; result |= getbit(from >> 2, to >> 2) << 2; result |= getbit(from >> 4, to >> 4) << 4; return result; } void write_strip(uint8_t* data, int len) { latch = 0; for (int i = len - 1; i >= 0; i--) { spi.write(data[i]); } wait_us(1); latch = 1; wait_us(2); latch = 0; } class Schedulable { public: int time_; virtual void Run() = 0; bool operator<(const Schedulable& o) const { return time_ < o.time_; } }; struct comp { bool operator()(const Schedulable* a, const Schedulable* b) { return *b < *a; } }; priority_queue<Schedulable*, vector<Schedulable*>, comp> task_list; void Schedule(Schedulable* action) { task_list.push(action); } int global_tick = 0; bool strip_changed; void tick_cb() { ++global_tick; strobe = !strobe; } class RepeatedFadeInOut : public Schedulable { public: RepeatedFadeInOut(int start_time, int led, uint8_t a, uint8_t b, bool fast) : led_(led), a_(a), b_(b), fast_(fast) { time_ = start_time; Schedule(this); } virtual void Run() { strip[led_] = getcolor(a_, b_); if (fast_) { strip[led_] |= FAST; time_ += 128; } else { time_ += 256; } strip_changed = true; swap(a_,b_); Schedule(this); } private: int led_; uint8_t a_,b_; bool fast_; }; class WalkingFadeInOut : public Schedulable { public: WalkingFadeInOut(int start_time, int led, int stride, uint8_t a, uint8_t b, bool fast) : led_(led - stride), stride_(stride), a_(a), b_(b), fast_(fast), step_(true) { time_ = start_time; Schedule(this); } virtual void Run() { if (step_) { step_ = false; if (led_ >= 0) strip[led_] = a_; led_ += stride_; led_ %= LENGTH; } else { step_ = true; } strip[led_] = getcolor(a_, b_); if (fast_) { strip[led_] |= FAST; time_ += 128; } else { time_ += 256; } strip_changed = true; swap(a_,b_); Schedule(this); } private: int led_, stride_; uint8_t a_,b_; bool fast_, step_; }; class RegionWalkingFadeInOut : public Schedulable { public: RegionWalkingFadeInOut(int start_time, int led, int stride, int start_led, int length, uint8_t a, uint8_t b, bool fast, bool repeat, bool drop, Schedulable* next) : led_(led - stride), stride_(stride), start_led_(start_led), length_(length), a_(a), b_(b), fast_(fast), step_(true), repeat_(repeat), drop_(drop), next_(next) { time_ = start_time; Schedule(this); } virtual void Run() { if (step_) { step_ = false; if (led_ >= 0) strip[led_ + start_led_] = a_; led_ += stride_; if (repeat_) { led_ %= length_; } else { if (led_ >= length_) { /*if (next_ && led_ == (length_ + stride_)) { next_->time_ = global_tick + 1; Schedule(next_); }*/ delete this; return; } } } else { if (led_ == (length_ - 1) && drop_ && !repeat_) { if (next_) { next_->time_ = global_tick + 257; Schedule(next_); } delete this; return; } step_ = true; } strip[led_ + start_led_] = getcolor(a_, b_); if (fast_) { strip[led_ + start_led_] |= FAST; time_ += 128; } else { time_ += 256; } strip_changed = true; swap(a_,b_); Schedule(this); } private: int led_, stride_; int start_led_, length_; uint8_t a_,b_; bool fast_, step_; bool repeat_, drop_; Schedulable* next_; }; class WaitAndSetDone : public Schedulable { public: WaitAndSetDone(int start_time, bool* done) : done_(done) { time_ = start_time; Schedule(this); } virtual void Run() { strip_changed = true; if (done_) *done_ = true; delete this; } private: bool* done_; }; class FadeFillRegion : public Schedulable { public: FadeFillRegion(int start_time, int start_led, int length, uint8_t from_color, uint8_t to_color, bool fast, bool* done) : start_led_(start_led), length_(length), from_color_(from_color), to_color_(to_color), fast_(fast), done_(done) { time_ = start_time; Schedule(this); } virtual void Run() { for (int i = start_led_; i < start_led_ + length_; ++i) { strip[i] = getcolor(from_color_, to_color_) | (fast_ ? FAST : 0); } strip_changed = true; new WaitAndSetDone(global_tick + (fast_ ? 256 : 512), done_); delete this; } private: int start_led_, length_; uint8_t from_color_, to_color_; bool fast_; bool* done_; }; /* Keep dropping water drops in a bucket, until it fills up. */ class DropBucketFill : public Schedulable { public: DropBucketFill(int start_time, int start_led, int length, int drop_size, uint8_t from_color, uint8_t to_color, int fade_out_pause, uint8_t fade_out_color, bool* done) : start_led_(start_led), length_(length), original_length_(length), drop_size_(drop_size), from_color_(from_color), to_color_(to_color), fade_out_pause_(fade_out_pause), fade_out_color_(fade_out_color), done_(done) { time_ = start_time; Schedule(this); } virtual void Run() { // The bucket starts with a drop at its end. //strip[start_led_ + length_] = to_color_; //strip[start_led_ + length_] = RED; if (length_ > 0) { // There's still space in the bucket. Drop a new drop. for (int i = 0; i < min(drop_size_, length_); ++i) { Schedulable* next_drop = this; new RegionWalkingFadeInOut(time_ + (256 * i / drop_size_), i, drop_size_, start_led_, length_, from_color_, to_color_, true, false, true, next_drop); } length_--; } else if (length_ == 0) { // There's no more space in the bucket. Bail out. new FadeFillRegion(global_tick + fade_out_pause_, start_led_, original_length_, to_color_, fade_out_color_, false, done_); delete this; return; } } private: int start_led_, length_, original_length_; int drop_size_; uint8_t from_color_, to_color_; int fade_out_pause_; uint8_t fade_out_color_; bool* done_; }; Schedulable* g_watchdog; class ProgramSupervisor { public: ProgramSupervisor() { next_program_ = 0; } // The program should be alive forever. Programs do not delete themselves upon completion. Every scheduling of a program happens with global time set to zero. void RegisterProgram(Schedulable* program) { programs_.push_back(program); } // This should be called by the program executor when it is completed. // It is desired to leave all LEDs black after program completion. void CurrentProgramDone() { next_program_++; next_program_ %= programs_.size(); ScheduleProgram(); } void ScheduleProgram() { global_tick = 0; set_frequency(1000); while (!task_list.empty()) task_list.pop(); Schedule(g_watchdog); memset(strip, 0x80, sizeof(strip)); write_strip(strip, sizeof(strip)); if (programs_.empty()) return; programs_[next_program_]->time_ = 0; Schedule(programs_[next_program_]); } private: vector<Schedulable*> programs_; int next_program_; // indexes the programs_ array. } supervisor; class ProgramWatchdog : public Schedulable { public: ProgramWatchdog() { time_ = 5 * 60 * 1000; // 5 minutes deadline } virtual void Run() { supervisor.CurrentProgramDone(); if (this == g_watchdog) led2 = 1; } } g_watchdog_impl; const int CycleColorsRegions[][2] = { {5, 10}, {15, 10}, {35, 5}, {40, 50} }; class CycleColors : public Schedulable { public: CycleColors(bool fast, int cycle_count) : fast_(fast), cycle_count_(cycle_count), current_cycle_(0) { fade_out_time_ = fast_ ? 256 : 512; supervisor.RegisterProgram(this); } virtual void Run() { strip[100] = RED; strip_changed = true; int next_cycle_start_time = global_tick; for (int region = 0; region < sizeof(CycleColorsRegions) / sizeof(CycleColorsRegions[0]); ++region) { int region_start_led = CycleColorsRegions[region][0]; int region_length = CycleColorsRegions[region][1]; int region_from_color = google_colors[(current_cycle_ + region) % sizeof(google_colors)]; int region_to_color = google_colors[(region_from_color + 1) % sizeof(google_colors)]; new FadeFillRegion(next_cycle_start_time, region_start_led, region_length, region_from_color, region_to_color, fast_, NULL); } ++current_cycle_; if (current_cycle_ < cycle_count_) { time_ += fade_out_time_; Schedule(this); } else { supervisor.CurrentProgramDone(); } } private: bool fast_; int fade_out_time_; int cycle_count_; int current_cycle_; }; class MultiDropBucketFillProgram : public Schedulable { public: MultiDropBucketFillProgram() { // We repeat this program as it is three times each time it gets scheduled. supervisor.RegisterProgram(this); supervisor.RegisterProgram(this); supervisor.RegisterProgram(this); } virtual void Run() { int time = global_tick; const int kLength = sizeof(google_colors); memset(done_, 0, sizeof(done_)); for (int i = 0; i < kLength; i++) { new DropBucketFill(time, 1 + i * 26, 26, 4, BLACK, google_colors[i], 2560, BLACK, done_ + i); } new EndWatcher(done_, kLength); } private: class EndWatcher : public Schedulable { public: EndWatcher(bool* done_array, int len) : done_(done_array), len_(len) { time_ = 0; Schedule(this); } virtual void Run() { int i; for (i = 0; i < len_ && done_[i]; i++); if (i < len_) { // not done yet. time_ = global_tick + 2; Schedule(this); } else { supervisor.CurrentProgramDone(); delete this; } } private: bool* done_; int len_; }; bool done_[6]; }; class WalkingFade : public Schedulable { public: WalkingFade(int start_time, int led, int stride, int end, uint8_t a, uint8_t b, bool fast) : led_(led), start_(led), stride_(stride), end_(end), a_(a), b_(b), fast_(fast) { time_ = start_time; Schedule(this); } virtual void Run() { if (led_ >= end_ || led_ < 0) { delete this; return; } strip[led_] = getcolor(a_, b_); if (fast_) { strip[led_] |= FAST; time_ += 128; } else { time_ += 257; } led_ += stride_; strip_changed = true; Schedule(this); } private: int led_, start_, stride_, end_; uint8_t a_,b_; bool fast_, step_; }; class MorseGoogleProgram : public Schedulable { public: MorseGoogleProgram() { supervisor.RegisterProgram(this); } virtual void Run() { //const string t_code = "Y---"; const string t_code = "W.... .- .--. .--. -.-- / -... .. .-. - .... -.. .- -.-- --..-- / B--. R--- Y--- B--. G.-.. R."; const string code = (t_code + " " + t_code + " " + t_code); const int kSpaceTime = 512; const int kLedStart = 159; const int kStride = -2; const int kHalfStrideTime = 128; const int kDotSpaceTime = 256; int time = global_tick; uint8_t color = WHITE; for (int i = 0; i < code.size(); i++) { switch (code[i]) { case 'R': color = RED ; break; case 'G': color = GREEN ; break; case 'B': color = BLUE ; break; case 'Y': color = YELLOW ; break; case 'W': color = WHITE ; break; case 'C': color = CYAN ; break; case 'M': color = MAGENTA ; break; case '.': { new WalkingFade(time, kLedStart, kStride, 160, BLACK, color, false); time += kHalfStrideTime; new WalkingFade(time, kLedStart - 1, kStride, 160, BLACK, color, false); time += kHalfStrideTime; new WalkingFade(time, kLedStart, kStride / 2, 160, color, BLACK, true); time += kHalfStrideTime; //new WalkingFade(time, kLedStart - 1, kStride, 160, color, BLACK, true); time += kHalfStrideTime; time += kDotSpaceTime; break; } case '-': { new WalkingFade(time, kLedStart, kStride, 160, BLACK, color, false); time += kHalfStrideTime; new WalkingFade(time, kLedStart - 1, kStride, 160, BLACK, color, false); time += kHalfStrideTime; time += 3 * kHalfStrideTime; // should create three complete pixels new WalkingFade(time, kLedStart, kStride, 160, color, BLACK, false); time += kHalfStrideTime; new WalkingFade(time, kLedStart - 1, kStride, 160, color, BLACK, false); time += kHalfStrideTime; time += kDotSpaceTime; break; } case '/': // fall-through case ' ': time += kSpaceTime; break; } } finaliser_.time_ = time + kLedStart * 128 + 1000; Schedule(&finaliser_); } private: ProgramWatchdog finaliser_; }; class GoogleColorMarquee : public Schedulable { public: GoogleColorMarquee(int stride = 7, int offset = 0, int total_size = 159) : stride_(stride), offset_(offset), total_size_(total_size) { supervisor.RegisterProgram(this); } virtual void Run() { set_frequency(500); vector<uint8_t> colors; colors.push_back(BLACK); colors.insert(colors.end(), google_colors, google_colors + sizeof(google_colors)); colors.push_back(BLACK); int time = global_tick; const int kColorWaitTime = 2*512; for (int count = 0; count < 4; count++) { for (int i = 0; i < colors.size() - 1; i++) { for (int j = 0; j < stride_; j++) { new WalkingFade(time + j * 256 / stride_, offset_ + total_size_ - j, -stride_, offset_ + total_size_ + 1, colors[i], colors[i+1], false); } time += kColorWaitTime; } time += -kColorWaitTime + (total_size_ * 256 / stride_ ) / 2; } finaliser_.time_ = time + (total_size_ * 256 / stride_ ) / 2 + 1000; Schedule(&finaliser_); } private: ProgramWatchdog finaliser_; int stride_; int offset_; int total_size_; }; class PrintFProgram : public Schedulable { public: PrintFProgram() { supervisor.RegisterProgram(this); } virtual void Run() { strip[20] = strip[50] = strip[100] = RED; strip_changed = true; new WalkingFade(global_tick, 155, -2, 160, BLACK, GREEN, true); new WalkingFade(global_tick+64, 154, -2, 160, BLACK, GREEN, true); } }; void set_frequency(float freq) { g_ticker.detach(); g_ticker.attach(&tick_cb, 1.0/freq); } void init_board() { pc.baud(115200); myled = 0; latch = 0; spi.format(8, 0); spi.frequency(300000); wait_ms(500); myled = 1; memset(strip, BLACK, sizeof(strip)); write_strip(strip, sizeof(strip)); set_frequency(1000); memset(strip, 0x0, sizeof(strip)); } void run_loop() { while(1) { while (task_list.empty() || global_tick < task_list.top()->time_) { if (strip_changed) { write_strip(strip, sizeof(strip)); strip_changed = false; memset(strip, 0x0, sizeof(strip)); } } Schedulable* action = task_list.top(); task_list.pop(); action->Run(); } } int main() { init_board(); g_watchdog = &g_watchdog_impl; MultiDropBucketFillProgram multi_drop; //CycleColors cycle_colors(false, 5); MorseGoogleProgram morse; GoogleColorMarquee marquee; //PrintFProgram pr; supervisor.ScheduleProgram(); run_loop(); }