1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

epd_bw_spi: add generic black and white SPI e-Paper display driver

Add an initial implementation of a generic driver for black and white
e-paper displays. Includes parameters for the IL3829 display controller.
This commit is contained in:
Silke Hofstra 2019-10-19 16:35:03 +02:00
parent 90c2d405b0
commit f4791a0280
8 changed files with 924 additions and 0 deletions

View File

@ -11,6 +11,9 @@ warning: Member BTN[0-9]_PIN \(macro definition\) of
warning: Member BTN[0-9]_PORT \(macro definition\) of
warning: Member BTN[0-9]_PRESSED \(macro definition\) of
warning: Member BTN[0-9]_RELEASED \(macro definition\) of
warning: Member EPD_BW_SPI_CMD_[A-Z0-9_]* \(macro definition\) of
warning: Member EPD_BW_SPI_DISPLAY_UPDATE_OPTION_[A-Z0-9_]* \(macro definition\) of
warning: Member EPD_BW_SPI_WAIT_[A-Z0-9_]* \(macro definition\) of
warning: Member FXOS8700_PARAM_ADDR \(macro definition\) of
warning: Member FXOS8700_PARAM_I2C \(macro definition\) of
warning: Member FXOS8700_PARAM_RENEW_INTERVAL \(macro definition\) of

View File

@ -0,0 +1 @@
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,3 @@
FEATURES_REQUIRED += periph_spi
FEATURES_REQUIRED += periph_gpio
USEMODULE += ztimer_msec

View File

@ -0,0 +1 @@
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/epd_bw_spi/include

View File

@ -0,0 +1,342 @@
/*
* Copyright (C) 2022 Silke Hofstra
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup drivers_epd_bw_spi
*
* @{
* @file
* @brief Device driver implementation for the epd_bw_spi display controller
*
* @author Silke Hofstra <silke@slxh.eu>
* @}
*/
#include <string.h>
#include "byteorder.h"
#include "ztimer.h"
#include "epd_bw_spi.h"
#include "epd_bw_spi_internal.h"
#define ENABLE_DEBUG 0
#include "debug.h"
static void epd_bw_spi_cmd_start(epd_bw_spi_params_t *p, uint8_t cmd, bool cont)
{
DEBUG("[epd_bw_spi] cmd_start: command 0x%02x\n", cmd);
if (gpio_is_valid(p->busy_pin)) {
while ((bool)gpio_read(p->busy_pin) == p->busy_value) {}
}
gpio_clear(p->dc_pin);
spi_transfer_byte(p->spi, p->cs_pin, cont, (uint8_t)cmd);
gpio_set(p->dc_pin);
}
static void epd_bw_spi_write_cmd(epd_bw_spi_params_t *p, uint8_t cmd,
const uint8_t *params, size_t plen)
{
spi_acquire(p->spi, p->cs_pin, SPI_MODE_0, p->spi_clk);
epd_bw_spi_cmd_start(p, cmd, plen > 0);
if (plen) {
spi_transfer_bytes(p->spi, p->cs_pin, false, params, NULL, plen);
}
spi_release(p->spi);
}
static void epd_bw_spi_wait(epd_bw_spi_params_t *p, uint32_t msec)
{
if (gpio_is_valid(p->busy_pin)) {
DEBUG("[epd_bw_spi] wait: for busy bin\n");
while ((bool)gpio_read(p->busy_pin) == p->busy_value) {}
}
else {
DEBUG("[epd_bw_spi] wait: for %" PRIu32 " milliseconds\n", msec);
ztimer_sleep(ZTIMER_MSEC, msec);
}
}
static void epd_bw_spi_control2(epd_bw_spi_params_t *p, uint8_t option, uint32_t wait_msec)
{
DEBUG("[epd_bw_spi] control2: options 0x%02x, wait %" PRIu32" ms\n", option, wait_msec);
epd_bw_spi_write_cmd(p, EPD_BW_SPI_CMD_DISPLAY_UPDATE_CONTROL_2, &option, 1);
epd_bw_spi_write_cmd(p, EPD_BW_SPI_CMD_MASTER_ACTIVATION, NULL, 0);
epd_bw_spi_wait(p, wait_msec);
}
int epd_bw_spi_init(epd_bw_spi_t *dev, const epd_bw_spi_params_t *params)
{
memcpy(&dev->params, params, sizeof(epd_bw_spi_params_t));
/* Initialize the counter to the maximum + 1 to trigger full refresh */
dev->partial_refresh_count = dev->params.partial_refresh_max;
if (gpio_is_valid(dev->params.rst_pin)) {
if (gpio_init(dev->params.rst_pin, GPIO_OUT) != 0) {
DEBUG("[epd_bw_spi] init: error initializing the RST pin\n");
return EPD_BW_SPI_RST_FAIL;
}
gpio_set(dev->params.rst_pin);
}
if (gpio_is_valid(dev->params.busy_pin)) {
if (gpio_init(dev->params.busy_pin, GPIO_IN) != 0) {
DEBUG("[epd_bw_spi] init: error initializing the BUSY pin\n");
return EPD_BW_SPI_BUSY_FAIL;
}
}
if (!gpio_is_valid(dev->params.dc_pin) ||
gpio_init(dev->params.dc_pin, GPIO_OUT) != 0) {
DEBUG("[epd_bw_spi] init: error initializing the DC pin\n");
return EPD_BW_SPI_DC_FAIL;
}
gpio_set(dev->params.dc_pin);
int res = spi_init_cs(dev->params.spi, dev->params.cs_pin);
if (res != SPI_OK) {
DEBUG("[epd_bw_spi] init: error initializing the CS pin [%i]\n", res);
return res;
}
return 0;
}
void epd_bw_spi_display_init(epd_bw_spi_t *dev)
{
le_uint16_t y_data[2] = { 0 };
uint8_t y_size;
if (dev->params.controller.size_y <= 255) {
y_data[0].u8[0] = dev->params.size_y - 1;
y_size = 2;
}
else {
y_data[0] =
byteorder_btols(byteorder_htons((dev->params.size_y - 1) & 0x01FF));
y_size = 3;
}
epd_bw_spi_wake(dev);
epd_bw_spi_write_cmd(&dev->params, EPD_BW_SPI_CMD_DRIVER_OUTPUT_CONTROL,
(uint8_t *)y_data, y_size);
uint8_t data[] = {
0xD7, /* Phase 1: 30 ms phase, sel 3, 6.58 us off */
0xD6, /* Phase 2: 30 ms phase, sel 3, 3.34 us off */
0x9D, /* Phase 3: 10 ms phase, sel 4, 1.54 us off */
};
epd_bw_spi_write_cmd(&dev->params,
EPD_BW_SPI_CMD_BOOSTER_SOFT_START_CONTROL, data, 3);
data[0] = dev->params.controller.vcom;
epd_bw_spi_write_cmd(&dev->params, EPD_BW_SPI_CMD_WRITE_VCOM_REGISTER, data,
1);
data[0] = 0x1A; /* 4 dummy line per gate */
epd_bw_spi_write_cmd(&dev->params, EPD_BW_SPI_CMD_SET_DUMMY_LINE_PERIOD,
data, 1);
data[0] = 0x08; /* 2 µs per line */
epd_bw_spi_write_cmd(&dev->params, EPD_BW_SPI_CMD_SET_GATE_LINE_WIDTH, data,
1);
data[0] = (uint8_t)dev->params.entry_mode;
epd_bw_spi_write_cmd(&dev->params, EPD_BW_SPI_CMD_DATA_ENTRY_MODE_SETTING,
data, 1);
}
void epd_bw_spi_activate(epd_bw_spi_t *dev)
{
uint8_t option =
EPD_BW_SPI_DISPLAY_UPDATE_OPTION_ENABLE_CLOCK |
EPD_BW_SPI_DISPLAY_UPDATE_OPTION_ENABLE_CP;
epd_bw_spi_control2(&dev->params, option, EPD_BW_SPI_WAIT_ACTIVATION);
}
void epd_bw_spi_deactivate(epd_bw_spi_t *dev)
{
uint8_t option =
EPD_BW_SPI_DISPLAY_UPDATE_OPTION_DISABLE_CP |
EPD_BW_SPI_DISPLAY_UPDATE_OPTION_DISABLE_OSC;
epd_bw_spi_control2(&dev->params, option, EPD_BW_SPI_WAIT_ACTIVATION);
}
void epd_bw_spi_init_full(epd_bw_spi_t *dev)
{
epd_bw_spi_display_init(dev);
epd_bw_spi_set_area(dev, 0, dev->params.size_x, 0, dev->params.size_y);
epd_bw_spi_write_cmd(&dev->params, EPD_BW_SPI_CMD_WRITE_LUT_REGISTER,
dev->params.controller.lut_full,
dev->params.controller.lut_size);
}
void epd_bw_spi_update_full(epd_bw_spi_t *dev)
{
uint8_t option =
EPD_BW_SPI_DISPLAY_UPDATE_OPTION_ENABLE_CLOCK |
EPD_BW_SPI_DISPLAY_UPDATE_OPTION_ENABLE_CP |
EPD_BW_SPI_DISPLAY_UPDATE_OPTION_PATTERN_DISPLAY;
epd_bw_spi_control2(&dev->params, option, EPD_BW_SPI_WAIT_UPDATE_FULL);
epd_bw_spi_write_cmd(&dev->params, EPD_BW_SPI_CMD_NOP, NULL, 0);
}
void epd_bw_spi_init_part(epd_bw_spi_t *dev)
{
epd_bw_spi_display_init(dev);
epd_bw_spi_set_area(dev, 0, dev->params.size_x, 0, dev->params.size_y);
epd_bw_spi_write_cmd(&dev->params, EPD_BW_SPI_CMD_WRITE_LUT_REGISTER,
dev->params.controller.lut_part,
dev->params.controller.lut_size);
}
void epd_bw_spi_update_part(epd_bw_spi_t *dev)
{
epd_bw_spi_control2(&dev->params,
EPD_BW_SPI_DISPLAY_UPDATE_OPTION_PATTERN_DISPLAY,
EPD_BW_SPI_WAIT_UPDATE_PART);
epd_bw_spi_write_cmd(&dev->params, EPD_BW_SPI_CMD_NOP, NULL, 0);
}
void epd_bw_spi_init_auto(epd_bw_spi_t *dev)
{
if (dev->partial_refresh_count < dev->params.partial_refresh_max) {
epd_bw_spi_init_part(dev);
}
else {
epd_bw_spi_init_full(dev);
}
}
void epd_bw_spi_update_auto(epd_bw_spi_t *dev)
{
if (dev->partial_refresh_count < dev->params.partial_refresh_max) {
epd_bw_spi_update_part(dev);
dev->partial_refresh_count++;
}
else {
epd_bw_spi_update_full(dev);
dev->partial_refresh_count = 0;
}
}
void epd_bw_spi_write_ram(epd_bw_spi_t *dev)
{
epd_bw_spi_write_cmd(&dev->params, EPD_BW_SPI_CMD_WRITE_RAM, NULL, 0);
}
void epd_bw_spi_clear(epd_bw_spi_t *dev)
{
epd_bw_spi_fill(dev, 0, dev->params.size_x, 0, dev->params.size_y,
EPD_BW_SPI_COLOR_WHITE);
}
void epd_bw_spi_fill(epd_bw_spi_t *dev, uint8_t x1, uint8_t x2, uint16_t y1,
uint16_t y2, uint8_t color)
{
epd_bw_spi_set_area(dev, x1, x2, y1, y2);
spi_acquire(dev->params.spi, dev->params.cs_pin, SPI_MODE_0,
dev->params.spi_clk);
epd_bw_spi_cmd_start(&dev->params, EPD_BW_SPI_CMD_WRITE_RAM, true);
uint16_t size = ((x2 - x1) >> 3) * (y2 - y1);
for (uint16_t i = 0; i < size - 1; i++) {
spi_transfer_byte(dev->params.spi, dev->params.cs_pin, true, color);
}
spi_transfer_byte(dev->params.spi, dev->params.cs_pin, false, color);
spi_release(dev->params.spi);
}
void epd_bw_spi_fill_pixels(epd_bw_spi_t *dev, uint8_t x1, uint8_t x2,
uint16_t y1, uint16_t y2,
uint8_t *px)
{
epd_bw_spi_set_area(dev, x1, x2, y1, y2);
epd_bw_spi_write_buffer(dev, px, (x2 - x1) * (y2 - y1));
}
void epd_bw_spi_set_area(epd_bw_spi_t *dev, uint8_t x1, uint8_t x2, uint16_t y1,
uint16_t y2)
{
DEBUG("[epd_bw_spi] set_area: (%d,%d)-(%d,%d)\n", x1, y1, x2, y2);
/* Set X bounds */
uint8_t x_data[] = {
(x1 >> 3) & 0x1F,
((x2 - 1) >> 3) & 0x1F,
};
epd_bw_spi_write_cmd(&dev->params, EPD_BW_SPI_CMD_SET_RAM_X, x_data,
sizeof x_data);
/* Set Y bounds */
le_uint16_t y_data[2] = { 0 };
uint8_t y_size;
if (dev->params.controller.size_y <= 255) {
y_data[0].u8[0] = y1;
y_data[0].u8[1] = y2 - 1;
y_size = 2;
}
else {
y_data[0] = byteorder_btols(byteorder_htons(y1 & 0x01FF));
y_data[1] = byteorder_btols(byteorder_htons((y2 - 1) & 0x01FF));
y_size = 4;
}
epd_bw_spi_write_cmd(&dev->params, EPD_BW_SPI_CMD_SET_RAM_Y,
(uint8_t *)y_data, y_size);
/* Set counters to start positions */
epd_bw_spi_write_cmd(&dev->params, EPD_BW_SPI_CMD_SET_RAM_X_ADDR_COUNTER,
x_data, 1);
epd_bw_spi_write_cmd(&dev->params, EPD_BW_SPI_CMD_SET_RAM_Y_ADDR_COUNTER,
(uint8_t *)y_data, 2);
}
void epd_bw_spi_write_buffer(epd_bw_spi_t *dev, const uint8_t *buf, size_t len)
{
epd_bw_spi_write_cmd(&dev->params, EPD_BW_SPI_CMD_WRITE_RAM, buf, len);
}
void epd_bw_spi_sleep(epd_bw_spi_t *dev)
{
uint8_t data[] = { 0x01 };
epd_bw_spi_write_cmd(&dev->params, EPD_BW_SPI_CMD_DEEP_SLEEP_MODE, data, 1);
}
void epd_bw_spi_wake(epd_bw_spi_t *dev)
{
/* Give a low pulse on the reset pin */
if (gpio_is_valid(dev->params.rst_pin)) {
gpio_clear(dev->params.rst_pin);
ztimer_sleep(ZTIMER_MSEC, EPD_BW_SPI_WAIT_RESET);
gpio_set(dev->params.rst_pin);
ztimer_sleep(ZTIMER_MSEC, EPD_BW_SPI_WAIT_RESET);
}
/* Turn off sleep mode */
uint8_t data[] = { 0x00 };
epd_bw_spi_write_cmd(&dev->params, EPD_BW_SPI_CMD_DEEP_SLEEP_MODE, data, 1);
epd_bw_spi_wait(&dev->params, EPD_BW_SPI_WAIT_RESET);
}
void epd_bw_spi_swreset(epd_bw_spi_t *dev)
{
epd_bw_spi_write_cmd(&dev->params, EPD_BW_SPI_CMD_SWRESET, NULL, 0);
}

View File

@ -0,0 +1,103 @@
/*
* Copyright (C) 2019 Silke Hofstra
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup drivers_epd_bw_spi
* @{
*
* @file
* @brief Device driver implementation for the epd_bw_spi display controller
*
* @author Silke Hofstra <silke@slxh.eu>
*
* @}
*/
#ifndef EPD_BW_SPI_INTERNAL_H
#define EPD_BW_SPI_INTERNAL_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @name EPD_BW_SPI SPI commands
* @brief Commands used for controlling EPD displays.
* These are surprisingly portable.
* @{
*/
#define EPD_BW_SPI_CMD_DRIVER_OUTPUT_CONTROL (0x01)
#define EPD_BW_SPI_CMD_GATE_DRIVING_VOLTAGE_CONTROL (0x03) /* unused */
#define EPD_BW_SPI_CMD_SOURCE_DRIVING_VOLTAGE_CONTROL (0x04) /* unused */
#define EPD_BW_SPI_CMD_DISPLAY_CONTROL (0x07) /* unused */
#define EPD_BW_SPI_CMD_GATE_AND_SOURCE_NON_OVERLAP_PERIOD_CONTROL (0x0B) /* unused */
#define EPD_BW_SPI_CMD_BOOSTER_SOFT_START_CONTROL (0x0C)
#define EPD_BW_SPI_CMD_GATE_SCAN_START_POSITION (0x0F) /* unused */
#define EPD_BW_SPI_CMD_DEEP_SLEEP_MODE (0x10)
#define EPD_BW_SPI_CMD_DATA_ENTRY_MODE_SETTING (0x11)
#define EPD_BW_SPI_CMD_SWRESET (0x12)
#define EPD_BW_SPI_CMD_TEMPERATURE_SENSOR_CONTROL_WRITE (0x1A) /* unused */
#define EPD_BW_SPI_CMD_TEMPERATURE_SENSOR_CONTROL_READ (0x1B) /* unused */
#define EPD_BW_SPI_CMD_TEMPERATURE_SENSOR_CONTROL_WRITE_CMD (0x1C) /* unused */
#define EPD_BW_SPI_CMD_TEMPERATURE_SENSOR_CONTROL_LOAD (0x1D) /* unused */
#define EPD_BW_SPI_CMD_MASTER_ACTIVATION (0x20)
#define EPD_BW_SPI_CMD_DISPLAY_UPDATE_CONTROL_1 (0x21) /* unused */
#define EPD_BW_SPI_CMD_DISPLAY_UPDATE_CONTROL_2 (0x22)
#define EPD_BW_SPI_CMD_WRITE_RAM (0x24)
#define EPD_BW_SPI_CMD_READ_RAM (0x25) /* unused */
#define EPD_BW_SPI_CMD_VCOM_SENSE (0x28) /* unused */
#define EPD_BW_SPI_CMD_VCOM_SENSE_DURATION (0x29) /* unused */
#define EPD_BW_SPI_CMD_PROGRAM_VCOM_OTP (0x2A) /* unused */
#define EPD_BW_SPI_CMD_WRITE_VCOM_REGISTER (0x2C)
#define EPD_BW_SPI_CMD_READ_OTP_REGISTERS (0x2D) /* unused */
#define EPD_BW_SPI_CMD_PROGRAM_WS_OTP (0x30) /* unused */
#define EPD_BW_SPI_CMD_WRITE_LUT_REGISTER (0x32)
#define EPD_BW_SPI_CMD_READ_LUT_REGISTER (0x33) /* unused */
#define EPD_BW_SPI_CMD_PROGRAM_OTP_SELECTION (0x36) /* unused */
#define EPD_BW_SPI_CMD_OTP_SELECTION_CONTROL (0x37) /* unused */
#define EPD_BW_SPI_CMD_SET_DUMMY_LINE_PERIOD (0x3A)
#define EPD_BW_SPI_CMD_SET_GATE_LINE_WIDTH (0x3B)
#define EPD_BW_SPI_CMD_BORDER_WAVEFORM_CONTROL (0x3C) /* unused */
#define EPD_BW_SPI_CMD_SET_RAM_X (0x44)
#define EPD_BW_SPI_CMD_SET_RAM_Y (0x45)
#define EPD_BW_SPI_CMD_SET_RAM_X_ADDR_COUNTER (0x4E)
#define EPD_BW_SPI_CMD_SET_RAM_Y_ADDR_COUNTER (0x4F)
#define EPD_BW_SPI_CMD_NOP (0xFF)
/**@}*/
/**
* @name EPD_BW_SPI display update sequence option flags
* @brief Option flags for the EPD_BW_SPI_CMD_DISPLAY_UPDATE_CONTROL_2 command.
* The flags are executed in the order documented below.
* @{
*/
#define EPD_BW_SPI_DISPLAY_UPDATE_OPTION_ENABLE_CLOCK (1<<7)
#define EPD_BW_SPI_DISPLAY_UPDATE_OPTION_ENABLE_CP (1<<6)
#define EPD_BW_SPI_DISPLAY_UPDATE_OPTION_LOAD_TEMP (1<<5)
#define EPD_BW_SPI_DISPLAY_UPDATE_OPTION_LOAD_LUT (1<<4)
#define EPD_BW_SPI_DISPLAY_UPDATE_OPTION_INITIAL_DISPLAY (1<<3)
#define EPD_BW_SPI_DISPLAY_UPDATE_OPTION_PATTERN_DISPLAY (1<<2)
#define EPD_BW_SPI_DISPLAY_UPDATE_OPTION_DISABLE_CP (1<<1)
#define EPD_BW_SPI_DISPLAY_UPDATE_OPTION_DISABLE_OSC (1<<0)
/**@}*/
/**
* @name EPD_BW_SPI Waiting estimates
* @brief Waiting estimates in milliseconds which are used when the busy pin is not available.
* @{
*/
#define EPD_BW_SPI_WAIT_UPDATE_FULL 1200
#define EPD_BW_SPI_WAIT_UPDATE_PART 300
#define EPD_BW_SPI_WAIT_ACTIVATION 80
#define EPD_BW_SPI_WAIT_RESET 1
/**@}*/
#ifdef __cplusplus
}
#endif
#endif /* EPD_BW_SPI_INTERNAL_H */

View File

@ -0,0 +1,205 @@
/*
* Copyright (C) 2019 Silke Hofstra
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup drivers_epd_bw_spi
*
* @{
* @file
* @brief Default configuration for epd_bw_spi
*
* @author Silke Hofstra <silke@slxh.eu>
*/
#ifndef EPD_BW_SPI_PARAMS_H
#define EPD_BW_SPI_PARAMS_H
#include "board.h"
#include "epd_bw_spi.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Waveform lookup table for a full display refresh for IL3829.
*/
static const uint8_t epd_bw_spi_il3829_lut_default_full[] = {
0x50, 0xAA, 0x55, 0xAA, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
/**
* @brief Waveform lookup table for a partial display refresh for IL3829.
*/
static const uint8_t epd_bw_spi_il3829_lut_default_part[] = {
0x10, 0x18, 0x18, 0x08, 0x18, 0x18, 0x08, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x13, 0x14, 0x44, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
/**
* @brief Configuration for IL3829 e-paper display controller
*/
#define EPD_BW_SPI_CONTROLLER_IL3829 { \
.vcom = 0xA8, \
.size_x = 200, \
.size_y = 300, \
.lut_size = sizeof epd_bw_spi_il3829_lut_default_full, \
.lut_full = epd_bw_spi_il3829_lut_default_full, \
.lut_part = epd_bw_spi_il3829_lut_default_part, \
}
/**
* @brief Configuration for SSD1607 e-paper display controller
*/
#define EPD_BW_SPI_CONTROLLER_SSD1607 EPD_BW_SPI_CONTROLLER_IL3829
/**
* @brief Configuration for SSD1673 e-paper display controller
*/
#define EPD_BW_SPI_CONTROLLER_SSD1673 { \
.vcom = 0xA8, \
.size_x = 150, \
.size_y = 250, \
.lut_size = sizeof epd_bw_spi_il3829_lut_default_full, \
.lut_full = epd_bw_spi_il3829_lut_default_full, \
.lut_part = epd_bw_spi_il3829_lut_default_part, \
}
/**
* @brief Configuration for SSD1608 e-paper display controller
*/
#define EPD_BW_SPI_CONTROLLER_SSD1608 { \
.vcom = 0xA8, \
.size_x = 240, \
.size_y = 320, \
.lut_size = sizeof epd_bw_spi_il3829_lut_default_full, \
.lut_full = epd_bw_spi_il3829_lut_default_full, \
.lut_part = epd_bw_spi_il3829_lut_default_part, \
}
#ifndef EPD_BW_SPI_DISPLAY_X
/**
* @brief Width of the display in pixels.
*/
#define EPD_BW_SPI_DISPLAY_X (200)
#endif
#ifndef EPD_BW_SPI_DISPLAY_Y
/**
* @brief Height of the display in pixels.
*/
#define EPD_BW_SPI_DISPLAY_Y (200)
#endif
#ifndef EPD_BW_SPI_PARAM_SPI
/**
* @brief SPI device the display is connected to.
*/
#define EPD_BW_SPI_PARAM_SPI (SPI_DEV(0))
#endif
#ifndef EPD_BW_SPI_PARAM_SPI_CLK
/**
* @brief SPI device clock speed.
*/
#define EPD_BW_SPI_PARAM_SPI_CLK (SPI_CLK_5MHZ)
#endif
/**
* @brief SPI Chip select pin.
*/
#ifndef EPD_BW_SPI_PARAM_CS
#define EPD_BW_SPI_PARAM_CS (SPI_CS_UNDEF)
#endif
/**
* @brief Data/command pin of the display.
*/
#ifndef EPD_BW_SPI_PARAM_DC
#define EPD_BW_SPI_PARAM_DC (GPIO_UNDEF)
#endif
/**
* @brief Reset pin of the display.
*/
#ifndef EPD_BW_SPI_PARAM_RST
#define EPD_BW_SPI_PARAM_RST (GPIO_UNDEF)
#endif
#ifndef EPD_BW_SPI_PARAM_BUSY
/**
* @brief Busy pin of the display.
*/
#define EPD_BW_SPI_PARAM_BUSY (GPIO_UNDEF)
#endif
#ifndef EPD_BW_SPI_PARAM_BUSY_VAL
/**
* @brief Width of the display in pixels.
*/
#define EPD_BW_SPI_PARAM_BUSY_VAL (1)
#endif
#ifndef EPD_BW_SPI_CONTROLLER
/**
* @brief Display controller. See epd_bw_spi_controller_t.
*/
#define EPD_BW_SPI_CONTROLLER EPD_BW_SPI_CONTROLLER_IL3829
#endif
#ifndef EPD_BW_SPI_ENTRY_MODE
/**
* @brief Data entry mode. See epd_bw_spi_entry_mode_t.
*/
#define EPD_BW_SPI_ENTRY_MODE EPD_BW_SPI_Y_INC_X_INC
#endif
#ifndef EPD_BW_SPI_PARTIAL_REFRESH_MAX
/**
* @brief Maximum number of partial refreshes before a full refresh occurs.
*
* This is only used with epd_bw_spi_init_auto and @ref drivers_disp_dev.
*/
#define EPD_BW_SPI_PARTIAL_REFRESH_MAX (99)
#endif
#ifndef EPD_BW_SPI_PARAMS
/**
* @brief Parameters to initialize the display with.
*/
#define EPD_BW_SPI_PARAMS { .spi = EPD_BW_SPI_PARAM_SPI, \
.spi_clk = EPD_BW_SPI_PARAM_SPI_CLK, \
.cs_pin = EPD_BW_SPI_PARAM_CS, \
.dc_pin = EPD_BW_SPI_PARAM_DC, \
.rst_pin = EPD_BW_SPI_PARAM_RST, \
.busy_pin = EPD_BW_SPI_PARAM_BUSY, \
.busy_value = EPD_BW_SPI_PARAM_BUSY_VAL, \
.controller = EPD_BW_SPI_CONTROLLER, \
.entry_mode = EPD_BW_SPI_ENTRY_MODE, \
.size_x = EPD_BW_SPI_DISPLAY_X, \
.size_y = EPD_BW_SPI_DISPLAY_Y, \
.partial_refresh_max = \
EPD_BW_SPI_PARTIAL_REFRESH_MAX, \
}
#endif
/**
* @brief Display driver configuration.
*/
static const epd_bw_spi_params_t epd_bw_spi_params[] =
{
EPD_BW_SPI_PARAMS,
};
#ifdef __cplusplus
}
#endif
#endif /* EPD_BW_SPI_PARAMS_H */
/**@}*/

View File

@ -0,0 +1,266 @@
/*
* Copyright (C) 2019 Silke Hofstra
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup drivers_epd_bw_spi Generic black/white e-paper/e-ink SPI display driver.
* @ingroup drivers_display
* @brief Device driver for black/white e-ink/e-paper SPI displays.
*
* This driver provides functionality for working with black/white e-ink (e-paper) SPI displays.
* Various display controllers are currently supported out of the box, see @ref epd_bw_spi_params.h.
* Please open an issue or pull request with your controller details (size, Vcom, LUTs)
* if your display controller is not included yet.
*
* Use of this driver requires knowing the parameters of your display.
* See epd_bw_spi_params_t and @ref epd_bw_spi_params.h for more details on the parameters.
* Note that while the reset and busy pins are optional, using them is highly recommended.
*
*
* @{
* @file
* @brief Generic black/white e-paper/e-ink display SPI driver.
*
* @author Silke Hofstra <silke@slxh.eu>
*/
#ifndef EPD_BW_SPI_H
#define EPD_BW_SPI_H
#include "periph/spi.h"
#include "periph/gpio.h"
#ifdef __cplusplus
extern "C" {
#endif
#define EPD_BW_SPI_COLOR_WHITE (0xFF) /**< White (8x1 pixels) */
#define EPD_BW_SPI_COLOR_BLACK (0x00) /**< Black (8x1 pixels) */
/**
* @brief Data entry mode settings.
*
* This setting affect the automatic increment/decrement of the address counters.
*/
typedef enum {
EPD_BW_SPI_Y_DEC_X_DEC = 0x0, /**< Y decrement, X decrement */
EPD_BW_SPI_Y_DEC_X_INC = 0x1, /**< Y decrement, X increment */
EPD_BW_SPI_Y_INC_X_DEC = 0x2, /**< Y increment, X decrement */
EPD_BW_SPI_Y_INC_X_INC = 0x3, /**< Y increment, X increment */
} epd_bw_spi_entry_mode_t;
/**
* @brief Additional status codes for black/white SPI e-paper displays.
*/
enum {
EPD_BW_SPI_DC_FAIL = -5,
EPD_BW_SPI_RST_FAIL = -6,
EPD_BW_SPI_BUSY_FAIL = -7,
};
/**
* @brief Display controller parameters.
*/
typedef struct {
uint8_t vcom; /**< VCOM voltage level */
const uint16_t size_x; /**< supported number of horizontal pixels */
const uint16_t size_y; /**< supported number of vertical pixels */
const uint8_t lut_size; /**< size of the waveform lookup table */
const uint8_t *lut_full; /**< lookup table for a full display refresh */
const uint8_t *lut_part; /**< lookup table for a partial display refresh */
} epd_bw_spi_controller_t;
/**
* @brief SPI display device initialisation parameters.
*/
typedef struct {
spi_t spi; /**< SPI device that the display is connected to */
spi_clk_t spi_clk; /**< SPI clock speed to use */
gpio_t cs_pin; /**< pin connected to the CHIP SELECT line */
gpio_t dc_pin; /**< pin connected to the DC line */
gpio_t rst_pin; /**< pin connected to the reset line (optional) */
gpio_t busy_pin; /**< pin connected to the busy line (optional) */
bool busy_value; /**< expected value for the busy pin
when the display is busy */
bool dummy; /**< if device requires a dummy cycle before read */
epd_bw_spi_controller_t controller; /**< display controller of the e-Paper display */
epd_bw_spi_entry_mode_t entry_mode; /**< data entry mode */
uint16_t size_x; /**< number of horizontal pixels in the display */
uint16_t size_y; /**< number of vertical pixels in the display */
uint16_t partial_refresh_max; /**< maximum number of partial refreshes to perform
before triggering a full refresh */
} epd_bw_spi_params_t;
/**
* @brief Device initialisation parameters.
*/
typedef struct {
epd_bw_spi_params_t params; /**< SPI display parameters */
uint16_t partial_refresh_count; /**< number of partial refreshes since
the last full refresh */
} epd_bw_spi_t;
/**
* @brief Initialise the display.
*
* @param[out] dev Display to initialise.
* @param[in] params SPI Display parameters to use for initialisation.
*/
int epd_bw_spi_init(epd_bw_spi_t *dev, const epd_bw_spi_params_t *params);
/**
* @brief Activate the display.
*
* @param[in] dev Device descriptor.
*/
void epd_bw_spi_activate(epd_bw_spi_t *dev);
/**
* @brief Deactivate the display.
*
* @param[in] dev Device descriptor.
*/
void epd_bw_spi_deactivate(epd_bw_spi_t *dev);
/**
* @brief Initialise the display for a full refresh.
*
* @param[in] dev Device descriptor.
*/
void epd_bw_spi_init_full(epd_bw_spi_t *dev);
/**
* @brief Update the display with a full refresh.
*
* @param[in] dev Device descriptor.
*/
void epd_bw_spi_update_full(epd_bw_spi_t *dev);
/**
* @brief Initialise the display for a partial refresh.
*
* @param[in] dev Device descriptor.
*/
void epd_bw_spi_init_part(epd_bw_spi_t *dev);
/**
* @brief Update the display with a partial refresh.
*
* @param[in] dev Device descriptor.
*/
void epd_bw_spi_update_part(epd_bw_spi_t *dev);
/**
* @brief Initialise the display for an automatic partial/full refresh.
*
* @param[in] dev Device descriptor.
*/
void epd_bw_spi_init_auto(epd_bw_spi_t *dev);
/**
* @brief Update the display with an automatic partial/full refresh.
*
* @param[in] dev Device descriptor.
*/
void epd_bw_spi_update_auto(epd_bw_spi_t *dev);
/**
* @brief Clear the entire display.
*
* @param[in] dev Device descriptor.
*/
void epd_bw_spi_clear(epd_bw_spi_t *dev);
/**
* @brief Fill an area with a single color.
*
* @param[in] dev Device descriptor
* @param[in] x1 X coordinate of the first corner (multiple of 8).
* @param[in] x2 X coordinate of the opposite corner (multiple of 8).
* @param[in] y1 Y coordinate of the first corner.
* @param[in] y2 Y coordinate of the opposite corner.
* @param[in] color Color to use (`EPD_BW_SPI_COLOR_BLACK` or `EPD_BW_SPI_COLOR_WHITE`)
*/
void epd_bw_spi_fill(epd_bw_spi_t *dev, uint8_t x1, uint8_t x2, uint16_t y1,
uint16_t y2,
uint8_t color);
/**
* @brief Fill an area with an array of pixels.
*
* Note that the length of the array should be the same as the number of pixels
* in the given area.
*
* @param[in] dev Device descriptor.
* @param[in] x1 X coordinate of the first corner (multiple of 8).
* @param[in] x2 X coordinate of the opposite corner (multiple of 8).
* @param[in] y1 Y coordinate of the first corner.
* @param[in] y2 Y coordinate of the opposite corner.
* @param[in] px Array of pixels to use.
*/
void epd_bw_spi_fill_pixels(epd_bw_spi_t *dev, uint8_t x1, uint8_t x2,
uint16_t y1, uint16_t y2,
uint8_t *px);
/**
* @brief Set the area in which can be drawn.
*
* @param[in] dev Device descriptor.
* @param[in] x1 X coordinate of the first corner (multiple of 8).
* @param[in] x2 X coordinate of the opposite corner (multiple of 8).
* @param[in] y1 Y coordinate of the first corner.
* @param[in] y2 Y coordinate of the opposite corner.
*/
void epd_bw_spi_set_area(epd_bw_spi_t *dev, uint8_t x1, uint8_t x2, uint16_t y1,
uint16_t y2);
/**
* @brief Write to the RAM of the epd_bw_spi controller.
*
* Together with `epd_bw_spi_set_area()`, this allows one to draw a pregenerated
* image on the screen.
*
* @param[in] dev Device descriptor.
* @param[in] buf Buffer to write to the display.
* @param[in] len Size of the buffer to write to the display.
*/
void epd_bw_spi_write_buffer(epd_bw_spi_t *dev, const uint8_t *buf, size_t len);
/**
* @brief Set the display to deep sleep mode.
*
* After the display has gone to sleep, a wake can be triggered with the reset pin.
* Do not use this if no reset pin has been defined.
*
* @param[in] dev Device descriptor.
*/
void epd_bw_spi_sleep(epd_bw_spi_t *dev);
/**
* @brief Wake the device.
*
* This doesn't do anything without using the reset pin.
*
* @param[in] dev Device descriptor.
*/
void epd_bw_spi_wake(epd_bw_spi_t *dev);
/**
* @brief Perform a soft reset of the device.
*
* This resets all commands and parameters to their default values,
* except for sleep mode and the RAM.
*
* @param[in] dev Device descriptor.
*/
void epd_bw_spi_swreset(epd_bw_spi_t *dev);
#ifdef __cplusplus
}
#endif
#endif /* EPD_BW_SPI_H */
/** @} */