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.
Dependents: display-puck display-puck
Diff: EPD.cpp
- 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