Balazs Racz / Mbed 2 deprecated LedStrip_test

Dependencies:   mbed

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();
}