epaper

Dependents:   epaper_mbed_test_copy1

Fork of EaEpaper by Peter Drescher

EPD.cpp

Committer:
fenoth
Date:
2014-04-08
Revision:
3:6e990a69c6cf
Parent:
0:fedcef5319f5

File content as of revision 3:6e990a69c6cf:

// Copyright 2013 Pervasive Displays, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied.  See the License for the specific language
// governing permissions and limitations under the License.



#include <limits.h>

#include "EPD.h"
#include "mbed.h"
#include "BurstSPI.h"


// delays - more consistent naming
#define Delay_ms(ms) wait_ms(ms)
#define Delay_us(us) wait_us(us)

// inline arrays
#define ARRAY(type, ...) ((type[]){__VA_ARGS__})
#define CU8(...) (ARRAY(const uint8_t, __VA_ARGS__))

#define LOW  (0)
#define HIGH (1)
#define digitalWrite(pin, state) (pin) = (state)
#define digitalRead(pin) (pin)

Timer _time;
#define millis() _time.read_ms()
#define millis_start() _time.start()


//static void PWM_start(int pin);
//static void PWM_stop(int pin);

//static void SPI_put(uint8_t c);
//static void SPI_put_wait(uint8_t c, int busy_pin);
//static void SPI_send(uint8_t cs_pin, const uint8_t *buffer, uint16_t length);


EPD_Class::EPD_Class(EPD_size size,
             PinName panel_on_pin,
             PinName border_pin,
             PinName discharge_pin,
             PinName pwm_pin,
             PinName reset_pin,
             PinName busy_pin,
             PinName chip_select_pin,
             PinName mosi,
             PinName miso,
             PinName sck) :
    EPD_Pin_PANEL_ON(panel_on_pin),
    EPD_Pin_BORDER(border_pin),
    EPD_Pin_DISCHARGE(discharge_pin),
    EPD_Pin_PWM(pwm_pin),
    EPD_Pin_RESET(reset_pin),
    EPD_Pin_BUSY(busy_pin),
    EPD_Pin_EPD_CS(chip_select_pin),
    spi_(mosi,miso,sck) {

    this->size = size;
    this->stage_time = 480; // milliseconds
    this->lines_per_display = 96;
    this->dots_per_line = 128;
    this->bytes_per_line = 128 / 8;
    this->bytes_per_scan = 96 / 4;
    this->filler = false;
    spi_.frequency(12000000);   // 12 MHz SPI clock

    // display size dependant items
    {
        static uint8_t cs[] = {0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00};
        static uint8_t gs[] = {0x72, 0x03};
        this->channel_select = cs;
        this->channel_select_length = sizeof(cs);
        this->gate_source = gs;
        this->gate_source_length = sizeof(gs);
    }

    // set up size structure
    switch (size) {
    default:
    case EPD_1_44:  // default so no change
        break;

    case EPD_2_0: {
        this->lines_per_display = 96;
        this->dots_per_line = 200;
        this->bytes_per_line = 200 / 8;
        this->bytes_per_scan = 96 / 4;
        this->filler = true;
        static uint8_t cs[] = {0x72, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xe0, 0x00};
        static uint8_t gs[] = {0x72, 0x03};
        this->channel_select = cs;
        this->channel_select_length = sizeof(cs);
        this->gate_source = gs;
        this->gate_source_length = sizeof(gs);
        break;
    }

    case EPD_2_7: {
        this->stage_time = 630; // milliseconds
        this->lines_per_display = 176;
        this->dots_per_line = 264;
        this->bytes_per_line = 264 / 8;
        this->bytes_per_scan = 176 / 4;
        this->filler = true;
        static uint8_t cs[] = {0x72, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xfe, 0x00, 0x00};
        static uint8_t gs[] = {0x72, 0x00};
        this->channel_select = cs;
        this->channel_select_length = sizeof(cs);
        this->gate_source = gs;
        this->gate_source_length = sizeof(gs);
        break;
    }
    }

    this->factored_stage_time = this->stage_time;
}


void EPD_Class::begin() {

    // power up sequence
    SPI_put(0x00);

    digitalWrite(this->EPD_Pin_RESET, LOW);
    digitalWrite(this->EPD_Pin_PANEL_ON, LOW);
    digitalWrite(this->EPD_Pin_DISCHARGE, LOW);
    digitalWrite(this->EPD_Pin_BORDER, LOW);
    digitalWrite(this->EPD_Pin_EPD_CS, LOW);

    //PWM_start(this->EPD_Pin_PWM);
    EPD_Pin_PWM = 0.5;
    Delay_ms(5);
    digitalWrite(this->EPD_Pin_PANEL_ON, HIGH);
    Delay_ms(10);

    digitalWrite(this->EPD_Pin_RESET, HIGH);
    digitalWrite(this->EPD_Pin_BORDER, HIGH);
    digitalWrite(this->EPD_Pin_EPD_CS, HIGH);
    Delay_ms(5);

    digitalWrite(this->EPD_Pin_RESET, LOW);
    Delay_ms(5);

    digitalWrite(this->EPD_Pin_RESET, HIGH);
    Delay_ms(5);

    // wait for COG to become ready
    while (HIGH == digitalRead(this->EPD_Pin_BUSY)) {
    }

    // channel select
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x01), 2);
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, this->channel_select, this->channel_select_length);

    // DC/DC frequency
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x06), 2);
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0xff), 2);

    // high power mode osc
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x07), 2);
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x9d), 2);


    // disable ADC
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x08), 2);
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x00), 2);

    // Vcom level
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x09), 2);
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0xd0, 0x00), 3);

    // gate and source voltage levels
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x04), 2);
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, this->gate_source, this->gate_source_length);

    Delay_ms(5);  //???

    // driver latch on
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x03), 2);
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x01), 2);

    // driver latch off
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x03), 2);
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x00), 2);

    Delay_ms(5);

    // charge pump positive voltage on
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x05), 2);
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x01), 2);

    // final delay before PWM off
    Delay_ms(30);
    //PWM_stop(this->EPD_Pin_PWM);
    EPD_Pin_PWM = 0.0;

    // charge pump negative voltage on
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x05), 2);
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x03), 2);

    Delay_ms(30);

    // Vcom driver on
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x05), 2);
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x0f), 2);

    Delay_ms(30);

    // output enable to disable
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x02), 2);
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x24), 2);
}


void EPD_Class::end() {

    this->frame_fixed(0x55, EPD_normal); // dummy frame
    this->line(0x7fffu, 0, 0x55, false, EPD_normal); // dummy_line

    Delay_ms(25);

    digitalWrite(this->EPD_Pin_BORDER, LOW);
    Delay_ms(30);

    digitalWrite(this->EPD_Pin_BORDER, HIGH);

    // latch reset turn on
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x03), 2);
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x01), 2);

    // output enable off
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x02), 2);
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x05), 2);

    // Vcom power off
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x05), 2);
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x0e), 2);

    // power off negative charge pump
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x05), 2);
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x02), 2);

    // discharge
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x04), 2);
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x0c), 2);

    Delay_ms(120);

    // all charge pumps off
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x05), 2);
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x00), 2);

    // turn of osc
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x07), 2);
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x0d), 2);

    // discharge internal - 1
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x04), 2);
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x50), 2);

    Delay_ms(40);

    // discharge internal - 2
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x04), 2);
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0xA0), 2);

    Delay_ms(40);

    // discharge internal - 3
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x04), 2);
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x00), 2);

    // turn of power and all signals
    digitalWrite(this->EPD_Pin_RESET, LOW);
    digitalWrite(this->EPD_Pin_PANEL_ON, LOW);
    digitalWrite(this->EPD_Pin_BORDER, LOW);
    digitalWrite(this->EPD_Pin_EPD_CS, LOW);

    digitalWrite(this->EPD_Pin_DISCHARGE, HIGH);

    SPI_put(0x00);

    Delay_ms(150);

    digitalWrite(this->EPD_Pin_DISCHARGE, LOW);
}


// convert a temperature in Celcius to
// the scale factor for frame_*_repeat methods
int EPD_Class::temperature_to_factor_10x(int temperature) {
    if (temperature <= -10) {
        return 170;
    } else if (temperature <= -5) {
        return 120;
    } else if (temperature <= 5) {
        return 80;
    } else if (temperature <= 10) {
        return 40;
    } else if (temperature <= 15) {
        return 30;
    } else if (temperature <= 20) {
        return 20;
    } else if (temperature <= 40) {
        return 10;
    }
    return 7;
}


// One frame of data is the number of lines * rows. For example:
// The 1.44” frame of data is 96 lines * 128 dots.
// The 2” frame of data is 96 lines * 200 dots.
// The 2.7” frame of data is 176 lines * 264 dots.

// the image is arranged by line which matches the display size
// so smallest would have 96 * 32 bytes

void EPD_Class::frame_fixed(uint8_t fixed_value, EPD_stage stage) {
    for (uint8_t line = 0; line < this->lines_per_display ; ++line) {
        this->line(line, 0, fixed_value, false, stage);
    }
}


void EPD_Class::frame_data(PROGMEM const uint8_t *image, EPD_stage stage){
    for (uint8_t line = 0; line < this->lines_per_display ; ++line) {
        this->line(line, &image[line * this->bytes_per_line], 0, true, stage);
    }
}


#if defined(EPD_ENABLE_EXTRA_SRAM)
void EPD_Class::frame_sram(const uint8_t *image, EPD_stage stage){
    for (uint8_t line = 0; line < this->lines_per_display ; ++line) {
        this->line(line, &image[line * this->bytes_per_line], 0, false, stage);
    }
}
#endif


void EPD_Class::frame_cb(uint32_t address, EPD_reader *reader, EPD_stage stage) {
    static uint8_t buffer[264 / 8];
    for (uint8_t line = 0; line < this->lines_per_display; ++line) {
        reader(buffer, address + line * this->bytes_per_line, this->bytes_per_line);
        this->line(line, buffer, 0, false, stage);
    }
}

void EPD_Class::frame_fixed_repeat(uint8_t fixed_value, EPD_stage stage) {
    long stage_time = this->factored_stage_time;
   
    do {         
        millis_start();
        unsigned long t_start = millis();
        this->frame_fixed(fixed_value, stage);        
        unsigned long t_end = millis();             
        if (t_end > t_start) {
            stage_time -= t_end - t_start;
        } else {
            stage_time -= t_start - t_end + 1 + ULONG_MAX;
        }
    } while (stage_time > 0);                
}


void EPD_Class::frame_data_repeat(PROGMEM const uint8_t *image, EPD_stage stage) {
    long stage_time = this->factored_stage_time;
    do {
        millis_start();
        unsigned long t_start = millis();
        this->frame_data(image, stage);
        unsigned long t_end = millis();
        if (t_end > t_start) {
            stage_time -= t_end - t_start;
        } else {
            stage_time -= t_start - t_end + 1 + ULONG_MAX;
        }
    } while (stage_time > 0);
}


#if defined(EPD_ENABLE_EXTRA_SRAM)
void EPD_Class::frame_sram_repeat(const uint8_t *image, EPD_stage stage) {
    long stage_time = this->factored_stage_time;
    do {
        millis_start();
        unsigned long t_start = millis();
        this->frame_sram(image, stage);
        unsigned long t_end = millis();
        if (t_end > t_start) {
            stage_time -= t_end - t_start;
        } else {
            stage_time -= t_start - t_end + 1 + ULONG_MAX;
        }
    } while (stage_time > 0);
}
#endif


void EPD_Class::frame_cb_repeat(uint32_t address, EPD_reader *reader, EPD_stage stage) {
    long stage_time = this->factored_stage_time;
    do {
        millis_start();
        unsigned long t_start = millis();
        this->frame_cb(address, reader, stage);
        unsigned long t_end = millis();
        if (t_end > t_start) {
            stage_time -= t_end - t_start;
        } else {
            stage_time -= t_start - t_end + 1 + ULONG_MAX;
        }
    } while (stage_time > 0);
}


void EPD_Class::line(uint16_t line, const uint8_t *data, uint8_t fixed_value, bool read_progmem, EPD_stage stage) {
    // charge pump voltage levels
    
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x04), 2);
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, this->gate_source, this->gate_source_length);

    // send data
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x0a), 2);
    Delay_us(10);

    // CS low
    digitalWrite(this->EPD_Pin_EPD_CS, LOW);
    SPI_put_wait(0x72, this->EPD_Pin_BUSY);

    // even pixels
    for (uint16_t b = this->bytes_per_line; b > 0; --b) {
        if (0 != data) {

            uint8_t pixels = data[b - 1] & 0xaa;

            switch(stage) {
            case EPD_compensate:  // B -> W, W -> B (Current Image)
                pixels = 0xaa | ((pixels ^ 0xaa) >> 1);
                break;
            case EPD_white:       // B -> N, W -> W (Current Image)
                pixels = 0x55 + ((pixels ^ 0xaa) >> 1);
                break;
            case EPD_inverse:     // B -> N, W -> B (New Image)
                pixels = 0x55 | (pixels ^ 0xaa);
                break;
            case EPD_normal:       // B -> B, W -> W (New Image)
                pixels = 0xaa | (pixels >> 1);
                break;
            }
            SPI_put_wait(pixels, this->EPD_Pin_BUSY);
        } else {
            SPI_put_wait(fixed_value, this->EPD_Pin_BUSY);
        }   }

    // scan line
    for (uint16_t b = 0; b < this->bytes_per_scan; ++b) {
        if (line / 4 == b) {
            SPI_put_wait(0xc0 >> (2 * (line & 0x03)), this->EPD_Pin_BUSY);
        } else {
            SPI_put_wait(0x00, this->EPD_Pin_BUSY);
        }
    }

    // odd pixels
    for (uint16_t b = 0; b < this->bytes_per_line; ++b) {
        if (0 != data) {

            uint8_t pixels = data[b] & 0x55;

            switch(stage) {
            case EPD_compensate:  // B -> W, W -> B (Current Image)
                pixels = 0xaa | (pixels ^ 0x55);
                break;
            case EPD_white:       // B -> N, W -> W (Current Image)
                pixels = 0x55 + (pixels ^ 0x55);
                break;
            case EPD_inverse:     // B -> N, W -> B (New Image)
                pixels = 0x55 | ((pixels ^ 0x55) << 1);
                break;
            case EPD_normal:       // B -> B, W -> W (New Image)
                pixels = 0xaa | pixels;
                break;
            }
            uint8_t p1 = (pixels >> 6) & 0x03;
            uint8_t p2 = (pixels >> 4) & 0x03;
            uint8_t p3 = (pixels >> 2) & 0x03;
            uint8_t p4 = (pixels >> 0) & 0x03;
            pixels = (p1 << 0) | (p2 << 2) | (p3 << 4) | (p4 << 6);
            SPI_put_wait(pixels, this->EPD_Pin_BUSY);
        } else {
            SPI_put_wait(fixed_value, this->EPD_Pin_BUSY);
        }
    }

    if (this->filler) {
        SPI_put_wait(0x00, this->EPD_Pin_BUSY);
    }

    // CS high
    digitalWrite(this->EPD_Pin_EPD_CS, HIGH);

    // output data to panel
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x02), 2);
    Delay_us(10);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x2f), 2);
}


void EPD_Class::SPI_put(uint8_t c) {
    
    spi_.write(c);
    //spi_.fastWrite(c);
    
    
}



void EPD_Class::SPI_put_wait(uint8_t c, DigitalIn busy_pin) {

    SPI_put(c);

    // wait for COG ready
    while (HIGH == digitalRead(busy_pin)) {
    }
}


void EPD_Class::SPI_send(DigitalOut cs_pin, const uint8_t *buffer, uint16_t length) {

    // CS low
    digitalWrite(cs_pin, LOW);

    // send all data
    for (uint16_t i = 0; i < length; ++i) {
        spi_.fastWrite(*buffer++);
        spi_.clearRX();
    }

    // CS high
    digitalWrite(cs_pin, HIGH);
}


//static void PWM_start(int pin) {
//    analogWrite(pin, 128);  // 50% duty cycle
//}


//static void PWM_stop(int pin) {
//    analogWrite(pin, 0);
//}