# HG changeset patch # User Paul Boddie # Date 1369001200 0 # Node ID 488ce300977e8ce579cda213e61a767e9e0664d0 # Parent 6a559742d15396300f2302b0e0bcdec8a8775bc2 Added updated display library code. diff -r 6a559742d153 -r 488ce300977e EPD.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/EPD.c Sun May 19 22:06:40 2013 +0000 @@ -0,0 +1,714 @@ +// 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 "EPD.h" + +typedef enum { + LOW=0, + HIGH=1 +} EPD_pinstate; + +static void EPD_line(uint16_t line, const uint8_t *data, uint8_t fixed_value, uint8_t read_progmem, EPD_stage stage); +static void SPI_send(const uint8_t *buffer, uint16_t length); +static void SPI_put(uint8_t c); +static void SPI_put_wait(uint8_t c); + +static void EPD_Pin_init(); +static void EPD_Pin_EPD_CS(EPD_pinstate pin); +static void EPD_Pin_RESET(EPD_pinstate pin); +static void EPD_Pin_PANEL_ON(EPD_pinstate pin); +static void EPD_Pin_DISCHARGE(EPD_pinstate pin); +static void EPD_Pin_BORDER(EPD_pinstate pin); +static EPD_pinstate epd_get_busy(void); + +static void epd_pwm_active(uint16_t delayInMs); + + +// inline arrays +#define ARRAY(type, ...) ((type[]){__VA_ARGS__}) +#define CU8(...) (ARRAY(const uint8_t, __VA_ARGS__)) + +static COG_Parameters_t epd; + +/****************************************************************************** + * Local functions + *****************************************************************************/ + + +// convert a temperature in Celcius to +// the scale factor for frame_*_repeat methods +static int16_t EPD_temperature_to_factor_10x(int16_t 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 + +static void EPD_frame_fixed(uint8_t fixed_value, EPD_stage stage) { + uint8_t line; + for (line = 0; line < epd.lines_per_display ; ++line) { + EPD_line(line, 0, fixed_value, FALSE, stage); + } +} + +static void EPD_frame_data(PROGMEM const uint8_t *image, EPD_stage stage){ + uint8_t line; + for (line = 0; line < epd.lines_per_display ; ++line) { + EPD_line(line, &image[line * epd.bytes_per_line], 0, TRUE, stage); + } +} + + +#if defined(EPD_ENABLE_EXTRA_SRAM) +static void EPD_frame_sram(const uint8_t *image, EPD_stage stage){ + uint8_t line; + for (line = 0; line < epd.lines_per_display ; ++line) { + EPD_line(line, &image[line * epd.bytes_per_line], 0, FALSE, stage); + } +} +#endif + + +static void EPD_frame_fixed_repeat(uint8_t fixed_value, EPD_stage stage) { + int32_t stage_time = epd.factored_stage_time; + do { + uint32_t t_start = bsp_getMsTicks(); + EPD_frame_fixed(fixed_value, stage); + uint32_t t_end = bsp_getMsTicks(); + if (t_end > t_start) { + stage_time -= t_end - t_start; + } else { + stage_time -= t_start - t_end + 1 + 0xffffffffU; + } + } while (stage_time > 0); +} + + +static void EPD_frame_data_repeat(PROGMEM const uint8_t *image, EPD_stage stage) { + int32_t stage_time = epd.factored_stage_time; + do { + uint32_t t_start = bsp_getMsTicks(); + EPD_frame_data(image, stage); + uint32_t t_end = bsp_getMsTicks(); + if (t_end > t_start) { + stage_time -= t_end - t_start; + } else { + stage_time -= t_start - t_end + 1 + 0xffffffffU; + } + } while (stage_time > 0); +} + +#include +#if defined(EPD_ENABLE_EXTRA_SRAM) +static void EPD_frame_sram_repeat(const uint8_t *image, EPD_stage stage) { + int32_t stage_time = epd.factored_stage_time; + do { + uint32_t t_start = bsp_getMsTicks(); + EPD_frame_sram(image, stage); + uint32_t t_end = bsp_getMsTicks(); + if (t_end > t_start) { + stage_time -= t_end - t_start; + } else { + stage_time -= t_start - t_end + 1 + 0xffffffffU; + } + } while (stage_time > 0); +} +#endif + +#if 0 +typedef void EPD_reader(void *buffer, uint32_t address, uint16_t length); + +static void EPD_frame_cb(uint32_t address, EPD_reader *reader, EPD_stage stage) { + static uint8_t buffer[264 / 8]; + uint8_t line; + for (line = 0; line < epd.lines_per_display; ++line) { + reader(buffer, address + line * epd.bytes_per_line, epd.bytes_per_line); + EPD_line(line, buffer, 0, FALSE, stage); + } +} + +static void EPD_frame_cb_repeat(uint32_t address, EPD_reader *reader, EPD_stage stage) { + int32_t stage_time = epd.factored_stage_time; + do { + uint32_t t_start = bsp_getMsTicks(); + EPD_frame_cb(address, reader, stage); + uint32_t t_end = bsp_getMsTicks(); + if (t_end > t_start) { + stage_time -= t_end - t_start; + } else { + stage_time -= t_start - t_end + 1 + 0xffffffffU; + } + } while (stage_time > 0); +} +#endif + +static void EPD_line(uint16_t line, const uint8_t *data, uint8_t fixed_value, uint8_t read_progmem, EPD_stage stage) { + // charge pump voltage levels + bsp_delayUs(10); + SPI_send(CU8(0x70, 0x04), 2); + bsp_delayUs(10); + SPI_send(epd.gate_source, epd.gate_source_length); + + // send data + bsp_delayUs(10); + SPI_send(CU8(0x70, 0x0a), 2); + bsp_delayUs(10); + + // CS low + EPD_Pin_EPD_CS(LOW); + SPI_put_wait(0x72); + + // even pixels + uint16_t b; + for (b = epd.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); + } else { + SPI_put_wait(fixed_value); + } } + + // scan line + for (b = 0; b < epd.bytes_per_scan; ++b) { + if (line / 4 == b) { + SPI_put_wait(0xc0 >> (2 * (line & 0x03))); + } else { + SPI_put_wait(0x00); + } + } + + // odd pixels + for (b = 0; b < epd.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); + } else { + SPI_put_wait(fixed_value); + } + } + + if (epd.filler == TRUE) { + SPI_put_wait(0x00); + } + + // CS high + EPD_Pin_EPD_CS(HIGH); + + // output data to panel + bsp_delayUs(10); + SPI_send(CU8(0x70, 0x02), 2); + bsp_delayUs(10); + SPI_send(CU8(0x72, 0x2f), 2); +} + + +/****************************************************************************** + * Public functions + *****************************************************************************/ + + +void EPD_init(EPD_size size) { + static uint8_t cs[] = {0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00}; + static uint8_t gs[] = {0x72, 0x03}; + + EPD_Pin_init(); + + epd.size = size; + epd.stage_time = 480; // milliseconds + epd.lines_per_display = 96; + epd.dots_per_line = 128; + epd.bytes_per_line = 128 / 8; + epd.bytes_per_scan = 96 / 4; + epd.filler = FALSE; + epd.channel_select = cs; + epd.channel_select_length = sizeof(cs); + epd.gate_source = gs; + epd.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: { + epd.lines_per_display = 96; + epd.dots_per_line = 200; + epd.bytes_per_line = 200 / 8; + epd.bytes_per_scan = 96 / 4; + epd.filler = TRUE; + cs[0] = 0x72; + cs[1] = 0x00; + cs[2] = 0x00; + cs[3] = 0x00; + cs[4] = 0x00; + cs[5] = 0x01; + cs[6] = 0xff; + cs[7] = 0xe0; + cs[8] = 0x00; + gs[0] = 0x72; + gs[1] = 0x03; + break; + } + + case EPD_2_7: { + epd.stage_time = 630; // milliseconds + epd.lines_per_display = 176; + epd.dots_per_line = 264; + epd.bytes_per_line = 264 / 8; + epd.bytes_per_scan = 176 / 4; + epd.filler = TRUE; + cs[0] = 0x72; + cs[1] = 0x00; + cs[2] = 0x00; + cs[3] = 0x00; + cs[4] = 0x7f; + cs[5] = 0xff; + cs[6] = 0xfe; + cs[7] = 0x00; + cs[8] = 0x00; + gs[0] = 0x72; + gs[1] = 0x00; + break; + } + } + + epd.factored_stage_time = epd.stage_time; +} + + +void EPD_begin(void) +{ + // power up sequence + SPI_put(0x00); + + EPD_Pin_RESET(LOW); + EPD_Pin_PANEL_ON(LOW); + EPD_Pin_DISCHARGE(LOW); + EPD_Pin_BORDER(LOW); + EPD_Pin_EPD_CS(LOW); + + // PWM_start(this->EPD_Pin_PWM); + // Delay_ms(5); + epd_pwm_active(5); + EPD_Pin_PANEL_ON(HIGH); + // Delay_ms(10); + epd_pwm_active(10); + + EPD_Pin_RESET(HIGH); + EPD_Pin_BORDER(HIGH); + EPD_Pin_EPD_CS(HIGH); + // Delay_ms(5); + epd_pwm_active(5); + + EPD_Pin_RESET(LOW); + // Delay_ms(5); + epd_pwm_active(5); + + EPD_Pin_RESET(HIGH); + // Delay_ms(5); + epd_pwm_active(5); + + // wait for COG to become ready + while (HIGH == epd_get_busy()) { + epd_pwm_active(10); + } + + // channel select + bsp_delayUs(10); + SPI_send(CU8(0x70, 0x01), 2); + bsp_delayUs(10); + SPI_send(epd.channel_select, epd.channel_select_length); + + // DC/DC frequency + bsp_delayUs(10); + SPI_send(CU8(0x70, 0x06), 2); + bsp_delayUs(10); + SPI_send(CU8(0x72, 0xff), 2); + + // high power mode osc + bsp_delayUs(10); + SPI_send(CU8(0x70, 0x07), 2); + bsp_delayUs(10); + SPI_send(CU8(0x72, 0x9d), 2); + + + // disable ADC + bsp_delayUs(10); + SPI_send(CU8(0x70, 0x08), 2); + bsp_delayUs(10); + SPI_send(CU8(0x72, 0x00), 2); + + // Vcom level + bsp_delayUs(10); + SPI_send(CU8(0x70, 0x09), 2); + bsp_delayUs(10); + SPI_send(CU8(0x72, 0xd0, 0x00), 3); + + // gate and source voltage levels + bsp_delayUs(10); + SPI_send(CU8(0x70, 0x04), 2); + bsp_delayUs(10); + SPI_send(epd.gate_source, epd.gate_source_length); + + // Delay_ms(5); //??? + epd_pwm_active(5); + + // driver latch on + bsp_delayUs(10); + SPI_send(CU8(0x70, 0x03), 2); + bsp_delayUs(10); + SPI_send(CU8(0x72, 0x01), 2); + + // driver latch off + bsp_delayUs(10); + SPI_send(CU8(0x70, 0x03), 2); + bsp_delayUs(10); + SPI_send(CU8(0x72, 0x00), 2); + + // Delay_ms(5); + epd_pwm_active(5); + + // charge pump positive voltage on + bsp_delayUs(10); + SPI_send(CU8(0x70, 0x05), 2); + bsp_delayUs(10); + SPI_send(CU8(0x72, 0x01), 2); + + // final delay before PWM off + // Delay_ms(30); + // PWM_stop(this->EPD_Pin_PWM); + epd_pwm_active(30); + + // charge pump negative voltage on + bsp_delayUs(10); + SPI_send(CU8(0x70, 0x05), 2); + bsp_delayUs(10); + SPI_send(CU8(0x72, 0x03), 2); + + bsp_delayMs(30); + + // Vcom driver on + bsp_delayUs(10); + SPI_send(CU8(0x70, 0x05), 2); + bsp_delayUs(10); + SPI_send(CU8(0x72, 0x0f), 2); + + bsp_delayMs(30); + + // output enable to disable + bsp_delayUs(10); + SPI_send(CU8(0x70, 0x02), 2); + bsp_delayUs(10); + SPI_send(CU8(0x72, 0x24), 2); +} + + +void EPD_end(void) { + + EPD_frame_fixed(0x55, EPD_normal); // dummy frame + EPD_line(0x7fffu, 0, 0x55, FALSE, EPD_normal); // dummy_line + + bsp_delayMs(25); + + EPD_Pin_BORDER(LOW); + bsp_delayMs(30); + + EPD_Pin_BORDER(HIGH); + + // latch reset turn on + bsp_delayUs(10); + SPI_send(CU8(0x70, 0x03), 2); + bsp_delayUs(10); + SPI_send(CU8(0x72, 0x01), 2); + + // output enable off + bsp_delayUs(10); + SPI_send(CU8(0x70, 0x02), 2); + bsp_delayUs(10); + SPI_send(CU8(0x72, 0x05), 2); + + // Vcom power off + bsp_delayUs(10); + SPI_send(CU8(0x70, 0x05), 2); + bsp_delayUs(10); + SPI_send(CU8(0x72, 0x0e), 2); + + // power off negative charge pump + bsp_delayUs(10); + SPI_send(CU8(0x70, 0x05), 2); + bsp_delayUs(10); + SPI_send(CU8(0x72, 0x02), 2); + + // discharge + bsp_delayUs(10); + SPI_send(CU8(0x70, 0x04), 2); + bsp_delayUs(10); + SPI_send(CU8(0x72, 0x0c), 2); + + bsp_delayMs(120); + + // all charge pumps off + bsp_delayUs(10); + SPI_send(CU8(0x70, 0x05), 2); + bsp_delayUs(10); + SPI_send(CU8(0x72, 0x00), 2); + + // turn of osc + bsp_delayUs(10); + SPI_send(CU8(0x70, 0x07), 2); + bsp_delayUs(10); + SPI_send(CU8(0x72, 0x0d), 2); + + // discharge internal - 1 + bsp_delayUs(10); + SPI_send(CU8(0x70, 0x04), 2); + bsp_delayUs(10); + SPI_send(CU8(0x72, 0x50), 2); + + bsp_delayMs(40); + + // discharge internal - 2 + bsp_delayUs(10); + SPI_send(CU8(0x70, 0x04), 2); + bsp_delayUs(10); + SPI_send(CU8(0x72, 0xA0), 2); + + bsp_delayMs(40); + + // discharge internal - 3 + bsp_delayUs(10); + SPI_send(CU8(0x70, 0x04), 2); + bsp_delayUs(10); + SPI_send(CU8(0x72, 0x00), 2); + + // turn of power and all signals + EPD_Pin_RESET(LOW); + EPD_Pin_PANEL_ON(LOW); + EPD_Pin_BORDER(LOW); + EPD_Pin_EPD_CS(LOW); + + EPD_Pin_DISCHARGE(HIGH); + + SPI_put(0x00); + + bsp_delayMs(150); + + EPD_Pin_DISCHARGE(LOW); +} + + +void EPD_setFactor(int16_t temperature) { + epd.factored_stage_time = epd.stage_time * EPD_temperature_to_factor_10x(temperature) / 10; +} + +// clear display (anything -> white) +void EPD_clear() { + EPD_frame_fixed_repeat(0xff, EPD_compensate); + EPD_frame_fixed_repeat(0xff, EPD_white); + EPD_frame_fixed_repeat(0xaa, EPD_inverse); + EPD_frame_fixed_repeat(0xaa, EPD_normal); +} + +// assuming a clear (white) screen output an image (PROGMEM data) +void EPD_image(PROGMEM const uint8_t *image) { + EPD_frame_fixed_repeat(0xaa, EPD_compensate); + EPD_frame_fixed_repeat(0xaa, EPD_white); + EPD_frame_data_repeat(image, EPD_inverse); + EPD_frame_data_repeat(image, EPD_normal); +} + +// change from old image to new image (PROGMEM data) +void EPD_image_progmem(PROGMEM const uint8_t *old_image, PROGMEM const uint8_t *new_image) { + EPD_frame_data_repeat(old_image, EPD_compensate); + EPD_frame_data_repeat(old_image, EPD_white); + EPD_frame_data_repeat(new_image, EPD_inverse); + EPD_frame_data_repeat(new_image, EPD_normal); +} + +#if defined(EPD_ENABLE_EXTRA_SRAM) +// change from old image to new image (SRAM version) +void EPD_image_sram(const uint8_t *old_image, const uint8_t *new_image) { + EPD_frame_sram_repeat(old_image, EPD_compensate); + EPD_frame_sram_repeat(old_image, EPD_white); + EPD_frame_sram_repeat(new_image, EPD_inverse); + EPD_frame_sram_repeat(new_image, EPD_normal); +} +#endif + + +static void SPI_put(uint8_t c) { + uint8_t placeholder = c; + bsp_spiWrite(&placeholder, 1); +} + + +static void SPI_put_wait(uint8_t c) { + + SPI_put(c); + + // wait for COG ready + while (HIGH == epd_get_busy()) { + } +} + + +static void SPI_send(const uint8_t *buffer, uint16_t length) { + // CS low + EPD_Pin_EPD_CS(LOW); + + bsp_spiWrite((uint8_t*)buffer, length); + + // CS high + EPD_Pin_EPD_CS(HIGH); +} + +static void EPD_Pin_init() +{ + bsp_pinDir(6, 1); // CS + bsp_pinDir(7, 0); // Driver Busy + bsp_pinDir(8, 1); // Border + bsp_pinDir(11, 1); // Pwm + bsp_pinDir(12, 1); // Reset + bsp_pinDir(13, 1); // Panel On + bsp_pinDir(14, 1); // Discharge +} + +static void EPD_Pin_EPD_CS(EPD_pinstate pin) +{ + if (HIGH == pin) { + bsp_pinSet(6); + } + else { + bsp_pinClr(6); + } +} + +static void EPD_Pin_RESET(EPD_pinstate pin) +{ + if (HIGH == pin) { + bsp_pinSet(12); + } + else { + bsp_pinClr(12); + } +} + +static void EPD_Pin_PANEL_ON(EPD_pinstate pin) +{ + if (HIGH == pin) { + bsp_pinSet(13); + } + else { + bsp_pinClr(13); + } +} + +static void EPD_Pin_DISCHARGE(EPD_pinstate pin) +{ + if (HIGH == pin) { + bsp_pinSet(14); + } + else { + bsp_pinClr(14); + } +} + +static void EPD_Pin_BORDER(EPD_pinstate pin) +{ + if (HIGH == pin) { + bsp_pinSet(8); + } + else { + bsp_pinClr(8); + } +} + +static EPD_pinstate epd_get_busy(void) +{ + if (bsp_pinState(7)) + return HIGH; + else + return LOW; +} + +static void epd_pwm_active(uint16_t delayInMs) +{ + uint16_t numOfIterations; + + numOfIterations = delayInMs * 100; + + for(; numOfIterations > 0; numOfIterations--) + { + bsp_pinSet(11); + bsp_delayUs(5); //100kHz + bsp_pinClr(11); + bsp_delayUs(5); + } +} diff -r 6a559742d153 -r 488ce300977e EPD.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/EPD.h Sun May 19 22:06:40 2013 +0000 @@ -0,0 +1,86 @@ +// Copyright 2013 Pervasive Displays, Inc. +// Copyright 2013 Embedded Artists AB +// +// 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. + +#ifndef __EPD_H__ +#define __EPD_H__ + +#include "bsp.h" + +//TODO +#define PROGMEM /*const*/ + +// if more SRAM available (8 kBytes) +#define EPD_ENABLE_EXTRA_SRAM 1 + +//typedef enum { +// LOW=0, +// HIGH=1 +//} EPD_pinstate; + +typedef enum { + EPD_1_44, // 128 x 96 + EPD_2_0, // 200 x 96 + EPD_2_7 // 264 x 176 +} EPD_size; + +typedef enum { // Image pixel -> Display pixel + EPD_compensate, // B -> W, W -> B (Current Image) + EPD_white, // B -> N, W -> W (Current Image) + EPD_inverse, // B -> N, W -> B (New Image) + EPD_normal // B -> B, W -> W (New Image) +} EPD_stage; + +typedef struct +{ + EPD_size size; + uint16_t stage_time; + uint16_t factored_stage_time; + uint16_t lines_per_display; + uint16_t dots_per_line; + uint16_t bytes_per_line; + uint16_t bytes_per_scan; + PROGMEM const uint8_t *gate_source; + uint16_t gate_source_length; + PROGMEM const uint8_t *channel_select; + uint16_t channel_select_length; + uint8_t filler; +} COG_Parameters_t; + + +void EPD_init(EPD_size size); +void EPD_begin(void); +void EPD_end(void); + +// set temperature +void EPD_setFactor(int16_t temperature); + +// clear display (anything -> white) +void EPD_clear(void); + +// assuming a clear (white) screen output an image (PROGMEM data) +void EPD_image(PROGMEM const uint8_t *image); + +// change from old image to new image (PROGMEM data) +void EPD_image_progmem(PROGMEM const uint8_t *old_image, PROGMEM const uint8_t *new_image); + +#if defined(EPD_ENABLE_EXTRA_SRAM) +// change from old image to new image (SRAM version) +void EPD_image_sram(const uint8_t *old_image, const uint8_t *new_image); +#endif + + +#endif //__EPD_H__ + +