Peter Drescher's library for the Embedded Artists E-paper display, but modified to also support the LPC4088 QuickStart Board
Fork of EaEpaper by
Diff: EPD.cpp
- Revision:
- 0:fedcef5319f5
- Child:
- 3:6fb3e296a6fd
--- /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); +//}