From f2ba38316dd5136638a22179da9333dec05d8ff2 Mon Sep 17 00:00:00 2001 From: Nathael Pajani Date: Wed, 22 Jul 2015 03:03:34 +0200 Subject: [PATCH] Add epaper driver for pervasive display epaper screens on embedded artists adapter. --- extdrv/epaper.c | 454 ++++++++++++++++++++++++++++++++++++++++ include/extdrv/epaper.h | 121 +++++++++++ 2 files changed, 575 insertions(+) create mode 100644 extdrv/epaper.c create mode 100644 include/extdrv/epaper.h diff --git a/extdrv/epaper.c b/extdrv/epaper.c new file mode 100644 index 0000000..da1d0e6 --- /dev/null +++ b/extdrv/epaper.c @@ -0,0 +1,454 @@ +/**************************************************************************** + * extdrv/epaper.c + * + * + * + * Copyright 2013 Nathael Pajani + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + *************************************************************************** */ + + +/* This file holds all the code used to handle the 2.7" epaper display from embedded + * artists on the GPIO Demo module. + */ + +#include +#include "core/lpc_regs_12xx.h" +#include "core/lpc_core_cm0.h" +#include "core/system.h" +#include "core/pio.h" +#include "lib/stdio.h" +#include "drivers/gpio.h" +#include "drivers/timers.h" +#include "drivers/ssp.h" +#include "extdrv/epaper.h" + +#include "drivers/serial.h" /* for debug */ +#include "extdrv/status_led.h" + +struct epaper_definition* epd = NULL; + + +/* + * Configure all gpio used for e-paper display handling + * Also calls timer_setup() + * Keeps a pointer to the epaper_definition structure, which MUST stay available. + */ +void epaper_config(struct epaper_definition* ep_def) +{ + /* Get a pointer to the epaper definition structure */ + epd = ep_def; + + /* Reset : output */ + config_gpio(&(epd->pin_reset), LPC_IO_MODE_PULL_UP, GPIO_DIR_OUT, 0); + /* Power control : output */ + config_gpio(&(epd->pin_power_ctrl), LPC_IO_MODE_PULL_UP, GPIO_DIR_OUT, 0); + /* Discharge : output */ + config_gpio(&(epd->pin_discharge), LPC_IO_MODE_PULL_UP, GPIO_DIR_OUT, 0); + /* Border control : output */ + config_gpio(&(epd->pin_border_ctrl), LPC_IO_MODE_PULL_UP, GPIO_DIR_OUT, 0); + /* Busy : input */ + config_gpio(&(epd->pin_busy), LPC_IO_MODE_PULL_UP, GPIO_DIR_IN, 0); + /* SPI Chip Select : output */ + config_gpio(&(epd->pin_spi_cs), LPC_IO_MODE_PULL_UP, GPIO_DIR_OUT, 1); + + /* PWM Timer configuration */ + timer_setup(epd->pwm_timer_num, &(epd->pwm_timer_conf)); +} + + + +/*******************************************************************************/ +/* + * internal functions for SPI communication with COG driver + */ +static void epaper_spi_transfer_single_byte_wait(uint16_t data) +{ + struct lpc_gpio* gpio = LPC_GPIO_REGS(epd->pin_busy.port); + + do {} while (gpio->in & (1 << epd->pin_busy.pin)); + + data = spi_transfer_single_frame(epd->spi_num, data); +} + +static void epaper_spi_transfer(uint8_t* out, uint8_t* in, uint8_t size) +{ + /* Set CS Low */ + gpio_clear(epd->pin_spi_cs); + /* Perform transfer */ + spi_transfer_multiple_frames(epd->spi_num, out, in, size, 8); + /* Release CS */ + gpio_set(epd->pin_spi_cs); +} + +static void epaper_spi_send(uint8_t reg_index, uint8_t* data, uint8_t val, int length) +{ + uint8_t buff[9] = { 0 }; + + /* Send register index */ + buff[0] = 0x70; + buff[1] = reg_index; + epaper_spi_transfer(buff, NULL, 2); + usleep(10); + /* Send Data */ + buff[0] = 0x72; + if (data != NULL) { + memcpy(&(buff[1]), data, length); + epaper_spi_transfer(buff, NULL, (length + 1)); + } else { + buff[1] = val; + epaper_spi_transfer(buff, NULL, 2); + } + usleep(10); +} + + +/*******************************************************************************/ +/* + * internal functions for COG driver power on and initialisation + */ +static void epaper_cog_power_on() +{ + /* Put GPIO in the right states */ + gpio_clear(epd->pin_border_ctrl); + gpio_clear(epd->pin_power_ctrl); + gpio_clear(epd->pin_discharge); + gpio_clear(epd->pin_spi_cs); + gpio_clear(epd->pin_reset); + + /* COG Power On procedure, see Doc No. 4P008-00 From Pervasive display */ + /* Start PWM : used to eliminate possible negative voltage at low temperature. */ + timer_start(epd->pwm_timer_num); + msleep(5); /* Keep PWM on for >=5 ms before power up */ + + /* Power up display */ + gpio_set(epd->pin_power_ctrl); + msleep(10); /* Keep PWM on for 10 more ms */ + + /* Remove reset condition and border control */ + gpio_set(epd->pin_spi_cs); + gpio_set(epd->pin_border_ctrl); + gpio_set(epd->pin_reset); + msleep(5); /* >=5 ms delay */ + /* Toggle reset */ + gpio_clear(epd->pin_reset); + msleep(5); /* >=5 ms delay */ + gpio_set(epd->pin_reset); + msleep(5); /* >=5 ms delay */ +} + +static void epaper_cog_initialize() +{ + struct lpc_gpio* gpio = LPC_GPIO_REGS(epd->pin_busy.port); + uint8_t vcom[2] = {0xD0, 0x00}; + + /* Wait for ready condition */ + do {} while (gpio->in & (1 << epd->pin_busy.pin)); + + /* Select Channel */ + epaper_spi_send(0x01, epd->channel, 0, 8); + /* DC/DC Frequency setting */ + epaper_spi_send(0x06, NULL, 0xFF, 1); + /* High Power Mode Osc setting */ + epaper_spi_send(0x07, NULL, 0x9D, 1); + /* Disable ADC */ + epaper_spi_send(0x08, NULL, 0x00, 1); + /* Set Vcom level */ + epaper_spi_send(0x09, vcom, 0, 2); + /* Set Gate and Source Voltage level */ + epaper_spi_send(0x04, NULL, epd->gate_source_level, 1); + + msleep(5); /* >=5 ms delay */ + + /* Turn Driver Latch ON and then OFF */ + epaper_spi_send(0x03, NULL, 0x01, 1); + epaper_spi_send(0x03, NULL, 0x00, 1); + + /* Turn on chargepump for positive voltages VGH and VDH */ + epaper_spi_send(0x05, NULL, 0x01, 1); + msleep(30); /* >=30 ms delay */ + + /* Turn off PWM */ + timer_stop(epd->pwm_timer_num); + + /* Turn on chargepump for negative voltages VGL and VDL */ + epaper_spi_send(0x05, NULL, 0x03, 1); + msleep(30); /* >=30 ms delay */ + /* Turn on chargepump for Vcom driver */ + epaper_spi_send(0x05, NULL, 0x0F, 1); + msleep(30); /* >=30 ms delay */ + + /* Output enable */ + epaper_spi_send(0x02, NULL, 0x24, 1); +} + + + +/*******************************************************************************/ +/* + * Turn Epaper controller On. + * Perform both COG driver Power On and Initialization. + * + * Must be called before any of the display / send frame functions when the display has + * been previously turned off or before first call + */ +void epaper_on() +{ + epaper_cog_power_on(); + epaper_cog_initialize(); +} + + +/*******************************************************************************/ +/* + * internal function used to send a line to the display. + * Line must be long enougth for the display + * Line numbering starts at 0 + */ +static void epaper_send_data_line(const uint8_t line, uint8_t* line_data, + uint8_t fixed_data, uint8_t stage) +{ + int i = 0; + uint8_t pixels = 0; + + /* Set chargepump voltage level to reduce voltage shift */ + epaper_spi_send(0x04, NULL, epd->gate_source_level, 1); + + /* Start with data index register */ + /* Set CS Low */ + gpio_clear(epd->pin_spi_cs); + epaper_spi_transfer_single_byte_wait(0x70); + epaper_spi_transfer_single_byte_wait(0x0A); + /* Release CS */ + gpio_set(epd->pin_spi_cs); + usleep(10); + + /* Set CS Low */ + gpio_clear(epd->pin_spi_cs); + epaper_spi_transfer_single_byte_wait(0x72); + /* Put even data bits first */ + if (line_data != NULL) { + for (i = epd->bytes_per_line; i > 0; i--) { + pixels = line_data[i - 1] & 0xAA; + switch (stage) { + case epaper_compensate: /* B -> W, W -> B (Current Image) */ + pixels = 0xAA | ((pixels ^ 0xAA) >> 1); + break; + case epaper_white: /* B -> N, W -> W (Current Image) */ + pixels = 0x55 + ((pixels ^ 0xAA) >> 1); + break; + case epaper_inverse: /* B -> N, W -> B (New Image) */ + pixels = 0x55 | (pixels ^ 0xAA); + break; + case epaper_normal: /* B -> B, W -> W (New Image) */ + pixels = 0xAA | (pixels >> 1); + break; + } + pixels = pixels & 0xFF; + epaper_spi_transfer_single_byte_wait(pixels); + } + } else { + for (i = epd->bytes_per_line; i > 0; i--) { + epaper_spi_transfer_single_byte_wait(fixed_data); + } + } + + /* Send scan line ... All set to 0 but one */ + for (i = 0; i < epd->bytes_per_scan; i++) { + if (i == (line >> 2)) { + epaper_spi_transfer_single_byte_wait((0xC0 >> ((line & 0x03) * 2))); + } else { + epaper_spi_transfer_single_byte_wait(0x00); + } + } + + /* And then put odd data bits */ + if (line_data != NULL) { + for (i = 0; i < epd->bytes_per_line; i++) { + uint16_t tmp = 0; + pixels = line_data[i] & 0x55; + switch(stage) { + case epaper_compensate: /* B -> W, W -> B (Current Image) */ + tmp = 0xaa | (pixels ^ 0x55); + break; + case epaper_white: /* B -> N, W -> W (Current Image) */ + tmp = 0x55 + (pixels ^ 0x55); + break; + case epaper_inverse: /* B -> N, W -> B (New Image) */ + tmp = 0x55 | ((pixels ^ 0x55) << 1); + break; + case epaper_normal: /* B -> B, W -> W (New Image) */ + tmp = 0xaa | pixels; + break; + } + /* Revert order */ + pixels = (((tmp & 0x03) << 6) | ((tmp & 0x0C) << 2) | ((tmp & 0x30) >> 2) | ((tmp & 0xC0) >> 6)); + epaper_spi_transfer_single_byte_wait(pixels); + } + } else { + for (i = 0; i < epd->bytes_per_line; i++) { + epaper_spi_transfer_single_byte_wait(fixed_data); + } + } + + /* Line termination */ + if (epd->line_termination_required) { + epaper_spi_transfer_single_byte_wait(0x00); + } + + /* Done with the frame, release chip select */ + gpio_set(epd->pin_spi_cs); + + /* Turn on output enable to send data from CoG driver to panel */ + epaper_spi_send(0x02, NULL, 0x2F, 1); +} + + + +/*******************************************************************************/ +/* Epaper display functions */ + +/* + * Send whole image, performing only one stage. + * Only "epaper_normal" has been tested yet. + */ +void epaper_send_frame(uint8_t* image, uint8_t stage) +{ + uint32_t start_tick = systick_get_tick_count(); + int i = 0; + + do { + for (i = 0; i < epd->lines; i++) { + epaper_send_data_line(i, (image + (i * epd->bytes_per_line)), 0, stage); + } + /* FIXME : start_tick will wrapp after 50 days */ + } while ((systick_get_tick_count() - start_tick) < epd->stage_time); +} + +/* + * Update a few lines of the display. + * Lines numbering starts at 0. + * Only one stage is performed. Sending a set of "white lines" of the same size before the + * new lines gives better results. + */ +void epaper_send_partial_frame(uint8_t* image, uint8_t start_line, uint8_t nb_lines, uint8_t stage) +{ + uint32_t start_tick = systick_get_tick_count(); + int i = 0; + + do { + for (i = 0; i < nb_lines; i++) { + epaper_send_data_line((start_line + i), (image + (i * epd->bytes_per_line)), 0, stage); + } + /* FIXME : start_tick will wrapp after 50 days */ + } while ((systick_get_tick_count() - start_tick) < epd->stage_time); +} + +/* + * Send an image (whole screen), preforming all stages: + * - Compensate old image, + * - White, + * - Inversed new image, + * - New image. + * + * Note : unsupported yet. + */ +void epaper_display(uint8_t* old_image_data, uint8_t* new_image_data) +{ + /* FIXME: perform all stages: Compensate old image, white, inversed new image, new image */ +} + +/* + * Turn the whole display white or black + * Send 0xFF for black and 0xAA for white. + * Note : Other values will create vertical lines (or nothing) depending on the values. + * refer to Pervasive display documentation for more information. + */ +void epaper_uniform_display(uint8_t value) +{ + uint32_t start_tick = systick_get_tick_count(); + int i = 0; + + do { + for (i = 0; i < epd->lines; i++) { + epaper_send_data_line(i, NULL, value, 0); + } + /* FIXME : start_tick will wrapp after 50 days */ + } while ((systick_get_tick_count() - start_tick) < epd->stage_time); +} + + + +/*******************************************************************************/ +/* Turn Off Epaper controller */ +void epaper_off() +{ + int i = 0; + + /* Write a Nothing frame */ + for (i = 0; i < epd->lines; i++) { + epaper_send_data_line(i, NULL, 0x55, 0); /* Will send all 'N' */ + } + /* Clear register data before power off by writting a dummy line + * A line of 0xFF is beyond the number of lines, thus no scan line will be set */ + epaper_send_data_line(0xFF, NULL, 0x55, 0); + msleep(30); /* >25ms */ + + /* Toggle border control */ + gpio_clear(epd->pin_border_ctrl); + msleep(300); /* 200 - 300 ms */ + gpio_set(epd->pin_border_ctrl); + + /* Latch reset on */ + epaper_spi_send(0x03, NULL, 0x01, 1); + /* Turn off Output enable */ + epaper_spi_send(0x02, NULL, 0x05, 1); + /* Power off Vcom chargepump */ + epaper_spi_send(0x05, NULL, 0x0E, 1); + /* Power off negative voltage chargepump */ + epaper_spi_send(0x05, NULL, 0x02, 1); + + /* Discharge part 1 */ + epaper_spi_send(0x04, NULL, 0x0C, 1); + msleep(150); /* >=120ms */ + /* Power off all chargepump */ + epaper_spi_send(0x05, NULL, 0x00, 1); + + /* Turn off Oscilator */ + epaper_spi_send(0x07, NULL, 0x0D, 1); + + /* Discharge part 2 */ + /* internal */ + epaper_spi_send(0x04, NULL, 0x50, 1); + msleep(50); /* >=40ms */ + epaper_spi_send(0x04, NULL, 0xA0, 1); + msleep(50); /* >=40ms */ + epaper_spi_send(0x04, NULL, 0x00, 1); + + /* Turn everything to 0 */ + gpio_clear(epd->pin_border_ctrl); + gpio_clear(epd->pin_reset); + gpio_clear(epd->pin_power_ctrl); + + /* Discharge part 3 */ + gpio_set(epd->pin_discharge); + msleep(180); /* >150ms */ + gpio_clear(epd->pin_discharge); + +} + + diff --git a/include/extdrv/epaper.h b/include/extdrv/epaper.h new file mode 100644 index 0000000..00aa614 --- /dev/null +++ b/include/extdrv/epaper.h @@ -0,0 +1,121 @@ +/**************************************************************************** + * extdrv/epaper.h + * + * + * + * Copyright 2013 Nathael Pajani + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + *************************************************************************** */ + + +/* This file holds all the code used to handle the 2.7" epaper display from embedded + * artists on the GPIO Demo module. + */ + +#include +#include "core/pio.h" +#include "drivers/timers.h" + + +/***************************************************************************** */ +/* E-Paper */ +struct epaper_definition +{ + uint16_t pixels_per_line; + uint16_t bytes_per_line; + uint16_t bytes_per_scan; + uint16_t lines; + uint8_t channel[8]; /* Selects the screen size, see Doc. No. 4P008-00, section 4. */ + uint8_t gate_source_level; + uint8_t line_termination_required; + uint16_t stage_time; + uint8_t spi_num; + uint8_t pwm_timer_num; + struct timer_config pwm_timer_conf; + /* Input pin */ + struct pio pin_busy; + /* Output pins */ + struct pio pin_reset; + struct pio pin_power_ctrl; + struct pio pin_discharge; + struct pio pin_border_ctrl; + struct pio pin_spi_cs; +}; + +enum epaper_stages { /* mage pixel -> Display pixel */ + epaper_compensate = 0, /* B -> W, W -> B (Current Image) */ + epaper_white, /* B -> N, W -> W (Current Image) */ + epaper_inverse, /* B -> N, W -> B (New Image) */ + epaper_normal, /* B -> B, W -> W (New Image) */ +}; + + + +/* + * Configure all gpio used for e-paper display handling + * Also calls timer_setup() + * Keeps a pointer to the epaper_definition structure, which MUST stay available. + */ +void epaper_config(struct epaper_definition* epd); + + +/* + * Turn Epaper controller On. + * Perform both COG driver Power On and Initialization. + * + * Must be called before any of the display / send frame functions when the display has + * been previously turned off or before first call + */ +void epaper_on(); + +/* Turn Off Epaper controller */ +void epaper_off(); + + +/* + * Turn the whole display white or black + * Send 0xFF for black and 0xAA for white. + * Note : Other values will create vertical lines (or nothing) depending on the values. + * refer to Pervasive display documentation for more information. + */ +void epaper_uniform_display(uint8_t value); + +/* + * Send an image (whole screen), preforming all stages: + * - Compensate old image, + * - White, + * - Inversed new image, + * - New image. + * + * Note : unsupported yet. + */ +void epaper_display(uint8_t* old_image_data, uint8_t* new_image_data); + +/* + * Send whole image, performing only one stage. + * Only "epaper_normal" has been tested yet. + */ +void epaper_send_frame(uint8_t* image, uint8_t stage); + +/* + * Update a few lines of the display. + * Lines numbering starts at 0. + * Only one stage is performed. Sending a set of "white lines" of the same size before the + * new lines gives better results. + */ +void epaper_send_partial_frame(uint8_t* image, uint8_t start_line, uint8_t nb_lines, uint8_t stage); + -- 2.43.0