mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
ceeb787a6f
We just changed the API so that it returns bool anyway.
343 lines
10 KiB
C
343 lines
10 KiB
C
/*
|
|
* 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 (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 (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);
|
|
}
|