epaper

Dependents:   epaper_mbed_test_copy1

Fork of EaEpaper by Peter Drescher

Revision:
0:fedcef5319f5
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/EPD.cpp	Sat Nov 09 23:37:43 2013 +0000
@@ -0,0 +1,613 @@
+// 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);
+//}