Library for interacting with seeedstudio epaper

Dependents:   display-puck display-puck

Revision:
0:6ac5ba1343bf
Child:
1:2f62e2b80305
diff -r 000000000000 -r 6ac5ba1343bf EPD.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/EPD.cpp	Thu Jul 17 14:15:53 2014 +0000
@@ -0,0 +1,585 @@
+// 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 <SPI.h>
+
+#include "EPD.h"
+#include <mbed.h>
+
+// delays - more consistent naming
+#define Delay_ms(ms) delay(ms)
+#define Delay_us(us) delayMicroseconds(us)
+
+// inline arrays
+#define ARRAY(type, ...) ((type[]){__VA_ARGS__})
+#define CU8(...) (ARRAY(const uint8_t, __VA_ARGS__))
+
+Timer timer;
+SPI spi(p20, p22, p25);
+
+
+Serial pq(USBTX, USBRX);
+
+static void SPI_put(uint8_t c);
+static void SPI_put_wait(uint8_t c, DigitalIn busy_pin);
+static void SPI_send(DigitalOut cs_pin, const uint8_t *buffer, uint16_t length);
+static void SPI_on();
+
+
+EPD_Class::EPD_Class(PinName Pin_EPD_CS, PinName Pin_PANEL_ON, PinName Pin_BORDER, PinName Pin_DISCHARGE, PinName Pin_PWM, PinName Pin_RESET, PinName Pin_BUSY) :
+    EPD_Pin_EPD_CS(Pin_EPD_CS),
+    EPD_Pin_PANEL_ON(Pin_PANEL_ON),
+    EPD_Pin_BORDER(Pin_BORDER),
+    EPD_Pin_DISCHARGE(Pin_DISCHARGE),
+    EPD_Pin_PWM(Pin_PWM),
+    EPD_Pin_RESET(Pin_RESET),
+    EPD_Pin_BUSY(Pin_BUSY) {
+
+}
+
+void EPD_Class::begin(EPD_size sz)
+{
+    this->size = sz;
+    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;
+    timer.start();
+    this->EPD_Pin_PWM.period(1.0/300000.0);
+
+
+    // 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::start() {
+
+    this->EPD_Pin_PWM = 0.5;
+    this->EPD_Pin_RESET = 0;
+    this->EPD_Pin_PANEL_ON = 0;
+    this->EPD_Pin_DISCHARGE = 0;
+    this->EPD_Pin_BORDER = 0;
+    this->EPD_Pin_EPD_CS = 0;
+    
+    
+    spi.format(8,0);
+    spi.frequency(10000000);
+    SPI_on();
+    
+    wait_ms(5);
+    this->EPD_Pin_PANEL_ON = 1;
+    wait_ms(10);
+
+    this->EPD_Pin_RESET = 1;
+    this->EPD_Pin_BORDER = 1;
+    this->EPD_Pin_EPD_CS = 1;
+    wait_ms(5);
+
+    this->EPD_Pin_RESET = 0;
+    wait_ms(5);
+
+    this->EPD_Pin_RESET = 1;
+    wait_ms(5);
+
+    // wait for COG to become ready
+
+    
+    pq.printf("waiting...");
+    while (this->EPD_Pin_BUSY) {
+    }
+    
+    pq.printf(" OK!\n");
+
+    // channel select
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x01), 2);
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, this->channel_select, this->channel_select_length);
+
+    // DC/DC frequency
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x06), 2);
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0xff), 2);
+
+    // high power mode osc
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x07), 2);
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x9d), 2);
+
+
+    // disable ADC
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x08), 2);
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x00), 2);
+
+    // Vcom level
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x09), 2);
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0xd0, 0x00), 3);
+
+    // gate and source voltage levels
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x04), 2);
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, this->gate_source, this->gate_source_length);
+
+    
+    this->EPD_Pin_PWM = 0.5;
+    wait_ms(5);  // pwm toggle >= 5ms
+
+    // driver latch on
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x03), 2);
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x01), 2);
+
+    // driver latch off
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x03), 2);
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x00), 2);
+
+    wait_ms(5);
+
+    // charge pump positive voltage on
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x05), 2);
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x01), 2);
+
+    // final delay before PWM off
+    wait_ms(30);
+    this->EPD_Pin_PWM = 0;
+
+    // charge pump negative voltage on
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x05), 2);
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x03), 2);
+
+    wait_ms(30);
+
+    // Vcom driver on
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x05), 2);
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x0f), 2);
+
+    wait_ms(30);
+
+    // output enable to disable
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x02), 2);
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x24), 2);
+}
+
+
+void EPD_Class::end() 
+{
+    // dummy frame
+    //this->frame_fixed(0x55, EPD_normal);
+    // dummy line and border
+    if (EPD_1_44 == this->size) {
+        // only for 1.44" EPD
+        this->line(0x7fffu, 0, 0xaa, false, EPD_normal);
+
+        wait_ms(250);
+
+    } 
+    else {
+        // all other display sizes
+        this->line(0x7fffu, 0, 0x55, false, EPD_normal);
+
+        wait_ms(25);
+
+        this->EPD_Pin_BORDER = 0;
+        wait_ms(250);
+        this->EPD_Pin_BORDER = 1;
+    }
+    SPI_on();
+    // latch reset turn on
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x03), 2);
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x01), 2);
+
+    // output enable off
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x02), 2);
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x05), 2);
+
+    // Vcom power off
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x05), 2);
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x0e), 2);
+
+    // power off negative charge pump
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x05), 2);
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x02), 2);
+
+    // discharge
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x04), 2);
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x0c), 2);
+
+    wait_ms(120);
+
+    // all charge pumps off
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x05), 2);
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x00), 2);
+
+    // turn of osc
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x07), 2);
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x0d), 2);
+
+    // discharge internal - 1
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x04), 2);
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x50), 2);
+
+    wait_ms(40);
+
+    // discharge internal - 2
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x04), 2);
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0xA0), 2);
+
+    wait_ms(40);
+
+    // discharge internal - 3
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x04), 2);
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x00), 2);
+    // turn of power and all signals
+    //
+
+    wait_ms(10);
+    this->EPD_Pin_RESET = 0;
+    this->EPD_Pin_PANEL_ON = 0;
+    this->EPD_Pin_BORDER = 0;
+
+    // discharge pulse
+    this->EPD_Pin_DISCHARGE = 1;
+    wait_ms(250);
+    this->EPD_Pin_DISCHARGE = 0;
+    
+    EPD_Pin_EPD_CS = 1;
+}
+
+
+// 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, int from_line, int to_line) 
+{
+    for (uint8_t line = from_line; line < to_line; line++) 
+    {
+        this->line(line, 0, fixed_value, false, stage);
+    }
+}
+
+void EPD_Class::frame_data(const uint8_t *image, EPD_stage stage, int from_line, int to_line)
+{
+    for (uint8_t line = from_line; line < to_line; line++) 
+    {
+        this->line(line, &image[line * this->bytes_per_line], 0, true, stage);
+    }
+}
+
+
+void EPD_Class::frame_fixed_repeat(uint8_t fixed_value, EPD_stage stage, int from_line, int to_line) 
+{
+    long stage_time = this->factored_stage_time;
+    do {
+        unsigned long t_start = timer.read_ms();
+        this->frame_fixed(fixed_value, stage, from_line, to_line);
+        unsigned long t_end = timer.read_ms();
+        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(const uint8_t *image, EPD_stage stage, int from_line, int to_line) 
+{
+
+    long stage_time = this->factored_stage_time;
+
+    do {
+        unsigned long t_start = timer.read_ms();
+        this->frame_data(image, stage, from_line, to_line);
+        unsigned long t_end = timer.read_ms();
+        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
+    SPI_on();
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x04), 2);
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, this->gate_source, this->gate_source_length);
+
+    // send data
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x0a), 2);
+    wait_us(10);
+
+    // CS low
+    this->EPD_Pin_EPD_CS = 0;
+    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);
+            //wait_us(1);
+        } 
+        else
+        {
+            SPI_put_wait(fixed_value, this->EPD_Pin_BUSY);
+            //wait_ms(1);
+        }   
+    }
+
+    // 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);
+        }
+        //wait_ms(10);
+    }
+
+    // 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);
+        }
+        //wait_ms(10);
+    }
+
+    if (this->filler) 
+    {
+        SPI_put_wait(0x00, this->EPD_Pin_BUSY);
+    }
+
+    // CS high
+    this->EPD_Pin_EPD_CS = 1;
+
+    // output data to panel
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x02), 2);
+    wait_us(10);
+    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x2f), 2);
+}
+
+static void SPI_on() {
+    wait_us(10);
+}
+
+
+static void SPI_put(uint8_t c) {
+    spi.write(c);
+}
+
+
+static void SPI_put_wait(uint8_t c, DigitalIn busy_pin) {
+
+    SPI_put(c);
+
+    // wait for COG ready
+    while (1 == busy_pin) {
+    }
+}
+
+
+static void SPI_send(DigitalOut cs_pin, const uint8_t *buffer, uint16_t length) {
+    // CS low
+    cs_pin = 0;
+
+    // send all data
+    for (uint16_t i = 0; i < length; ++i) {
+        SPI_put(*buffer++);
+    }
+
+    // CS high
+    cs_pin = 1;
+}
\ No newline at end of file