Skip to content

Instantly share code, notes, and snippets.

@C47D
Last active September 17, 2020 01:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save C47D/fb6e4a07fc64434c94004ddd646a1d8b to your computer and use it in GitHub Desktop.
Save C47D/fb6e4a07fc64434c94004ddd646a1d8b to your computer and use it in GitHub Desktop.
Prototype implementation of an "platform-agnostic" lv_drivers for LVGL.
/**
* @file disp_spi.c
*
*/
/*********************
* INCLUDES
*********************/
#include <string.h>
#include "lvgl/lvgl.h"
#include "disp_spi.h"
#include "spi_dma.h"
/*********************
* DEFINES
*********************/
#define TAG "disp_spi"
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
static volatile disp_spi_send_flag_t spi_flags = 0;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void display_spi_transaction(const uint8_t *data, uint16_t length,
disp_spi_send_flag_t flags, disp_spi_read_data *out,
uint64_t addr)
{
(void) addr;
(void) out;
if (0 == length) {
return;
}
spi_flags = flags;
/* Wait for previous pending transaction results */
display_tl_poll_is_busy();
if (flags & DISP_SPI_RECEIVE) {
}
if (flags & DISP_SPI_ADDRESS_24) {
}
/* Poll/Complete/Queue transaction */
if (flags & DISP_SPI_SEND_POLLING) {
// NOTE: Platform dependent
send_spi(data, (size_t) length);
} else if (flags & DISP_SPI_SEND_SYNCHRONOUS) {
} else {
// NOTE: Platform dependent
send_spi_dma(data, NULL, (size_t) length);
}
}
void display_tl_poll_is_busy(void)
{
while (spi_xfer_is_ongoing()) {
}
}
void display_spi_acquire(void)
{
}
void display_spi_release(void)
{
}
/**********************
* STATIC FUNCTIONS
**********************/
/* Called on the spi complete callback */
void display_colors_sent_notification(void)
{
if (spi_flags & DISP_SPI_SIGNAL_FLUSH) {
lv_disp_t * disp = NULL;
#if (LVGL_VERSION_MAJOR >= 7)
disp = _lv_refr_get_disp_refreshing();
#else /* Before v7 */
disp = lv_refr_get_disp_refreshing();
#endif
lv_disp_flush_ready(&disp->driver);
}
}
/**
* @file disp_spi.h
*
*/
#ifndef DISP_SPI_H
#define DISP_SPI_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef enum _disp_spi_send_flag_t {
DISP_SPI_SEND_QUEUED = 0x00000000,
DISP_SPI_SEND_POLLING = 0x00000001,
DISP_SPI_SEND_SYNCHRONOUS = 0x00000002,
DISP_SPI_SIGNAL_FLUSH = 0x00000004,
DISP_SPI_RECEIVE = 0x00000008,
DISP_SPI_CMD_8 = 0x00000010, /* Reserved */
DISP_SPI_CMD_16 = 0x00000020, /* Reserved */
DISP_SPI_ADDRESS_8 = 0x00000040, /* Reserved */
DISP_SPI_ADDRESS_16 = 0x00000080, /* Reserved */
DISP_SPI_ADDRESS_24 = 0x00000100, /* Reserved */
DISP_SPI_ADDRESS_32 = 0x00000200, /* Reserved */
DISP_SPI_MODE_DIO = 0x00000400, /* Reserved */
DISP_SPI_MODE_QIO = 0x00000800, /* Reserved */
DISP_SPI_MODE_DIOQIO_ADDR = 0x00001000, /* Reserved */
} disp_spi_send_flag_t;
typedef struct _disp_spi_read_data {
uint8_t _dummy_byte;
union {
uint8_t byte;
uint16_t word;
uint32_t dword;
} __attribute__((packed));
} disp_spi_read_data __attribute__((aligned(4)));
/**********************
* GLOBAL PROTOTYPES
**********************/
void display_spi_transaction(const uint8_t *data, uint16_t length,
disp_spi_send_flag_t flags, disp_spi_read_data *out, uint64_t addr);
void display_tl_poll_is_busy(void);
inline void display_tl_send_data(uint8_t *data, uint16_t length) {
display_spi_transaction(data, length, DISP_SPI_SEND_POLLING, NULL, 0);
}
inline void display_tl_send_colors(uint8_t *data, uint16_t length) {
display_spi_transaction(data, length,
DISP_SPI_SEND_QUEUED | DISP_SPI_SIGNAL_FLUSH,
NULL, 0);
}
/* NOTE: Both functions can be used in a OSAL layer */
void display_spi_acquire(void);
void display_spi_release(void);
/* Called when the async data has been sent */
void display_colors_sent_notification(void);
/**********************
* MACROS
**********************/
/* Receive data helpers */
#define member_size(type, member) sizeof(((type *)0)->member)
#define SPI_READ_DUMMY_LEN member_size(disp_spi_read_data, _dummy_byte)
#define SPI_READ_BYTE_LEN (SPI_READ_DUMMY_LEN + member_size(disp_spi_read_data, byte))
#define SPI_READ_WORD_LEN (SPI_READ_DUMMY_LEN + member_size(disp_spi_read_data, word))
#define SPI_READ_DWORD_LEN (SPI_READ_DUMMY_LEN + member_size(disp_spi_read_data, dword))
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*DISP_SPI_H*/
#ifndef DRIVER_GENERIC_H
#define DRIVER_GENERIC_H
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lvgl/lvgl.h"
typedef enum {
DATA_MODE_CMD,
DATA_MODE_DATA,
} data_mode_t;
typedef enum {
DISPLAY_BACKLIGHT_CONTROL_DISABLE,
DISPLAY_BACKLIGHT_CONTROL_ENABLE,
} display_backlight_control_t;
typedef struct {
void (*display_backlight_control)(lv_disp_drv_t *drv, display_backlight_control_t backlight_control);
void (*data_mode)(lv_disp_drv_t *drv, data_mode_t mode);
void (*delay_ms)(lv_disp_drv_t *drv, uint32_t ms);
void (*write_blocking)(const uint8_t *tx_data, const size_t size);
void (*write_async)(const uint8_t *tx_data, uint8_t *rx_data, const size_t size);
void (*hw_reset)(lv_disp_drv_t *drv);
} driver_generic_t;
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* DRIVER_GENERIC_H */
/**
* @file ili9341.c
*/
/*********************
* INCLUDES
*********************/
#include "ili9341.h"
#include "disp_spi.h"
/*********************
* DEFINES
*********************/
#define DRIVER_NAME "ILI9341"
/**********************
* TYPEDEFS
**********************/
/* The LCD needs a bunch of command/argument values to be initialized.
* They are stored in this struct. */
typedef struct {
uint8_t cmd;
uint8_t data[16];
/* No of data in data; bit 7 = delay after set; 0xFF = end of cmds. */
uint8_t databytes;
} lcd_init_cmd_t;
/**********************
* STATIC PROTOTYPES
**********************/
static inline void ili9341_set_mode(lv_disp_drv_t *drv, data_mode_t mode);
static void ili9341_set_orientation(lv_disp_drv_t *drv, uint8_t orientation);
static void ili9341_send_cmd(lv_disp_drv_t *drv, uint8_t cmd);
static void ili9341_send_data(lv_disp_drv_t *drv, void *data, uint16_t length);
/* Sending color data to the display can be both blocking and async, but is
* better to use async. When we're done sending color data we need to call
* lv_disp_flush_ready(&disp->driver) */
static void ili9341_send_color(lv_disp_drv_t *drv, void *data, uint16_t length);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void ili9341_init(lv_disp_drv_t * drv)
{
driver_generic_t *driver = (driver_generic_t *) drv->user_data;
/* Display initialization descriptor */
lcd_init_cmd_t ili_init_cmds[] = {
{0xCF, {0x00, 0x83, 0X30}, 3},
{0xED, {0x64, 0x03, 0X12, 0X81}, 4},
{0xE8, {0x85, 0x01, 0x79}, 3},
{0xCB, {0x39, 0x2C, 0x00, 0x34, 0x02}, 5},
{0xF7, {0x20}, 1},
{0xEA, {0x00, 0x00}, 2},
{ILI9341_REG_PWCTRL1, {0x26}, 1},
{ILI9341_REG_PWCTRL2, {0x11}, 1},
{ILI9341_REG_VMCTRL1, {0x35, 0x3E}, 2},
{ILI9341_REG_VMCTRL2, {0xBE}, 1},
{ILI9341_REG_MADCTL, {0x28}, 1},
{ILI9341_REG_PIXSET, {0x55}, 1},
{ILI9341_REG_FRMCTR1, {0x00, 0x1B}, 2},
{0xF2, {0x08}, 1},
{ILI9341_REG_GAMSET, {0x01}, 1},
{ILI9341_REG_PGAMCTRL, {0x1F, 0x1A, 0x18, 0x0A, 0x0F, 0x06, 0x45, 0X87, 0x32, 0x0A, 0x07, 0x02, 0x07, 0x05, 0x00}, 15},
{ILI9341_REG_NGAMCTRL, {0x00, 0x25, 0x27, 0x05, 0x10, 0x09, 0x3A, 0x78, 0x4D, 0x05, 0x18, 0x0D, 0x38, 0x3A, 0x1F}, 15},
{ILI9341_REG_CASET, {0x00, 0x00, 0x00, 0xEF}, 4},
{ILI9341_REG_PASET, {0x00, 0x00, 0x01, 0x3f}, 4},
{ILI9341_REG_RAMWR, {0}, 0},
{ILI9341_REG_ETMOD, {0x07}, 1},
{ILI9341_REG_DISCTRL, {0x0A, 0x82, 0x27, 0x00}, 4},
{ILI9341_REG_SLPOUT, {0}, 0x80},
{ILI9341_REG_DISPON, {0}, 0x80},
{0, {0}, 0xff},
};
/* Reset the display.
* TODO: Software reset when no hardware nRST is available */
driver->hw_reset(drv);
/* Send the display initialization descriptor */
uint16_t cmd = 0;
while (ili_init_cmds[cmd].databytes != 0xff) {
ili9341_send_cmd(drv, ili_init_cmds[cmd].cmd);
ili9341_send_data(drv, ili_init_cmds[cmd].data, ili_init_cmds[cmd].databytes&0x1F);
if (ili_init_cmds[cmd].databytes & 0x80) {
driver->delay_ms(drv, 100);
}
cmd++;
}
ili9341_enable_backlight(drv, DISPLAY_BACKLIGHT_CONTROL_ENABLE);
/* Landscape */
ili9341_set_orientation(drv, 2);
ili9341_send_cmd(drv, ILI9341_REG_DINVOFF);
}
void ili9341_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
{
uint8_t data[4] = {0};
uint32_t pixels_to_update = lv_area_get_width(area) * lv_area_get_height(area);
/* times two because each pixel is 2 bytes when LV_COLOR_DEPTH is set to RGB565 */
uint32_t bytes_to_send = pixels_to_update * 2;
/*Column addresses*/
data[0] = (area->x1 >> 8) & 0xFF;
data[1] = area->x1 & 0xFF;
data[2] = (area->x2 >> 8) & 0xFF;
data[3] = area->x2 & 0xFF;
ili9341_send_cmd(drv, ILI9341_REG_CASET);
ili9341_send_data(drv, data, 4);
/*Page addresses*/
data[0] = (area->y1 >> 8) & 0xFF;
data[1] = area->y1 & 0xFF;
data[2] = (area->y2 >> 8) & 0xFF;
data[3] = area->y2 & 0xFF;
ili9341_send_cmd(drv, ILI9341_REG_PASET);
ili9341_send_data(drv, data, 4);
/*Memory write*/
ili9341_send_cmd(drv, ILI9341_REG_RAMWR);
ili9341_send_color(drv, (void*) color_map, bytes_to_send);
}
/* TODO: How we can check if the driver_generic_t have an display_backlight_control
* callback registered. */
void ili9341_enable_backlight(lv_disp_drv_t *drv, display_backlight_control_t backlight_en)
{
driver_generic_t *driver = (driver_generic_t *) drv->user_data;
driver->display_backlight_control(drv, backlight_en);
}
void ili9341_sleep_in(lv_disp_drv_t *drv)
{
uint8_t data[] = {0x08};
ili9341_send_cmd(drv, ILI9341_REG_SLPIN);
ili9341_send_data(drv, &data, 1);
}
void ili9341_sleep_out(lv_disp_drv_t *drv)
{
uint8_t data[] = {0x08};
ili9341_send_cmd(drv, ILI9341_REG_SLPOUT);
ili9341_send_data(drv, &data, 1);
}
/**********************
* STATIC FUNCTIONS
**********************/
/* NOTE:
* This is a blocking function, so maybe we could rename display_tl_send_data to
* display_tl_send_blocking. */
static void ili9341_send_cmd(lv_disp_drv_t *drv, uint8_t cmd)
{
display_tl_poll_is_busy();
ili9341_set_mode(drv, DATA_MODE_CMD);
display_tl_send_data(&cmd, 1);
}
/* NOTE:
* This is a blocking function, so maybe we could rename display_tl_send_data to
* display_tl_send_blocking. */
static void ili9341_send_data(lv_disp_drv_t *drv, void * data, uint16_t length)
{
display_tl_poll_is_busy();
ili9341_set_mode(drv, DATA_MODE_DATA);
display_tl_send_data(data, length);
}
/* NOTE:
* In this case it's better to send the data in async mode (because we send the
* pixel color information), so maybe we could rename display_tl_send_colors to
* display_tl_send_async. */
static void ili9341_send_color(lv_disp_drv_t *drv, void *data, uint16_t length)
{
display_tl_poll_is_busy();
ili9341_set_mode(drv, DATA_MODE_DATA);
display_tl_send_colors(data, length);
}
static void ili9341_set_orientation(lv_disp_drv_t *drv, uint8_t orientation)
{
(void) drv;
uint8_t orientation_code[] = {
0x48, /* Portrait */
0x88, /* Portrait inverted */
0x28, /* Landscape */
0xE8 /* Landscape inverted */
};
ili9341_send_cmd(drv, ILI9341_REG_MADCTL);
ili9341_send_data(drv, (void *) &orientation_code[orientation], 1);
}
static inline void ili9341_set_mode(lv_disp_drv_t *drv, data_mode_t mode)
{
driver_generic_t *driver = (driver_generic_t *) drv->user_data;
driver->data_mode(drv, mode);
}
/**
* @file ILI9341.h
*/
#ifndef ILI9341_H
#define ILI9341_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lvgl/lvgl.h"
/* Maybe rename to lv_driver_generic */
#include "driver_generic.h"
/*********************
* DEFINES
*********************/
#define ILI9341_REG_SLPIN (0x10U)
#define ILI9341_REG_SLPOUT (0x11U)
#define ILI9341_REG_DINVOFF (0x20U)
#define ILI9341_REG_GAMSET (0x26U)
#define ILI9341_REG_DISPON (0x29U)
#define ILI9341_REG_CASET (0x2AU)
#define ILI9341_REG_PASET (0x2BU)
#define ILI9341_REG_RAMWR (0x2CU)
#define ILI9341_REG_MADCTL (0x36U)
#define ILI9341_REG_PIXSET (0x3AU)
#define ILI9341_REG_FRMCTR1 (0xB1U)
#define ILI9341_REG_DISCTRL (0xB6U)
#define ILI9341_REG_ETMOD (0xB7U)
#define ILI9341_REG_PWCTRL1 (0xC0U)
#define ILI9341_REG_PWCTRL2 (0xC1U)
#define ILI9341_REG_VMCTRL1 (0xC5U)
#define ILI9341_REG_VMCTRL2 (0xC7U)
#define ILI9341_REG_PGAMCTRL (0xE0U)
#define ILI9341_REG_NGAMCTRL (0xE1U)
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
void ili9341_init(lv_disp_drv_t *drv);
void ili9341_flush(lv_disp_drv_t *drv, const lv_area_t * area, lv_color_t * color_map);
void ili9341_enable_backlight(lv_disp_drv_t *drv, display_backlight_control_t backlight_en);
void ili9341_sleep_in(lv_disp_drv_t *drv);
void ili9341_sleep_out(lv_disp_drv_t *drv);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*ILI9341_H*/
#include "project.h"
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include "lvgl/lvgl.h"
#include "ili9341.h"
#include "spi_dma.h"
#include "disp_spi.h"
#include "driver_generic.h"
#include "psoc_platform.h"
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
#define DISPLAY_BUFFER_SIZE (LV_HOR_RES_MAX * 10)
enum { FEED_FORM_CMD = 0x0C };
CY_ISR(spi_tx_dma_done);
CY_ISR(spi_tx_isr_handler);
CY_ISR(timer_tick_handler);
/* This struct is passed as lv_disp_drv_t.user_data, you should "register"
* your platform specific functions in here. */
driver_generic_t ili9341_driver = {
.display_backlight_control = psoc_display_backlight_control,
.data_mode = psoc_data_mode,
.delay_ms = psoc_delay_ms,
.hw_reset = psoc_hw_reset
};
int main(void)
{
isr_SPI_TX_DMA_StartEx(spi_tx_dma_done);
isr_SPI_TX_StartEx(spi_tx_isr_handler);
isr_Timer_StartEx(timer_tick_handler);
CyGlobalIntEnable;
UART_Start();
SPI_Start();
spi_initialize_dma();
lv_init();
static lv_color_t buf1[DISPLAY_BUFFER_SIZE];
static lv_color_t buf2[DISPLAY_BUFFER_SIZE];
static lv_disp_buf_t disp_buf;
lv_disp_buf_init(&disp_buf, buf1, buf2, DISPLAY_BUFFER_SIZE);
lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.flush_cb = ili9341_flush;
disp_drv.buffer = &disp_buf;
disp_drv.user_data = &ili9341_driver;
lv_disp_drv_register(&disp_drv);
ili9341_init(&disp_drv);
Timer_Start();
lv_obj_t *scr = lv_disp_get_scr_act(NULL);
lv_obj_t *label = lv_label_create(scr, NULL);
lv_label_set_text(label, "Protect DJ Smokey at all costs!");
lv_obj_align(label, NULL, LV_ALIGN_CENTER, 0, 0);
for(;;)
{
CyDelay(1);
lv_task_handler();
}
}
void timer_tick_handler(void)
{
lv_tick_inc(1);
LV_TICK_Write(~LV_TICK_Read());
Timer_ReadStatusRegister();
}
/* NOTE: The DMA done interrupt is being triggered when the DMA is done
* transferring data into the SPI TX FIFO, this means it's gets triggered
* even when the SPI TX FIFO still holds the last 4 byte of data.
*
* To know when spi is actually done transfering the data we can enable
* the SPI_INT_ON_SPI_DONE (and SPI_INT_ON_TX_NOT_FULL). */
void spi_tx_dma_done(void)
{
spi_xfer_set_status(SPI_XFER_DONE);
SPI_SetTxInterruptMode((0u << SPI_STS_TX_FIFO_NOT_FULL_SHIFT) | (1u << SPI_STS_SPI_DONE_SHIFT));
}
/* SPI interrupt handler */
void spi_tx_isr_handler(void)
{
/* Clear the interrupt source flag */
volatile uint8_t spi_tx_sts = SPI_ReadTxStatus();
/* If the DMA is done and we get the SPI_STS_SPI_DONE interrupt then
* the SPI FIFO is empty. */
if ((SPI_XFER_DONE == spi_xfer_get_status()) && (SPI_STS_SPI_DONE & spi_tx_sts)) {
SS_Write(1);
/* Data transfer is done, we can disable SPI interrupts */
SPI_SetTxInterruptMode(0);
spi_xfer_on_completion();
/* Let LVGL the flush has been finished */
display_colors_sent_notification();
}
}
/* [] END OF FILE */
#include "driver_generic.h"
#include "DISP_DC.h"
#include "DISP_RST.h"
#include "DISP_BCKL.h"
#include "UART.h"
#include "CyLib.h"
void psoc_display_backlight_control(lv_disp_drv_t *drv, display_backlight_control_t backlight_control)
{
(void) drv;
DISP_BCKL_Write(DISPLAY_BACKLIGHT_CONTROL_ENABLE == backlight_control ? 1 : 0);
}
void psoc_data_mode(lv_disp_drv_t *drv, data_mode_t mode)
{
(void) drv;
if (DATA_MODE_CMD == mode) {
DISP_DC_Write(0);
} else {
DISP_DC_Write(1);
}
}
void psoc_delay_ms(lv_disp_drv_t *drv, uint32_t ms)
{
(void) drv;
CyDelay(ms);
}
void psoc_write_blocking(const uint8_t *tx_data, const size_t size)
{
}
void psoc_write_async(const uint8_t *tx_data, uint8_t *rx_data, const size_t size)
{
}
void psoc_hw_reset(lv_disp_drv_t *drv)
{
(void) drv;
DISP_RST_Write(0);
CyDelay(100);
DISP_RST_Write(1);
CyDelay(100);
}
/* [] END OF FILE */
#ifndef PSOC_PLATFORM_H
#define PSOC_PLATFORM_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lvgl/lvgl.h"
#include "driver_generic.h"
void psoc_display_backlight_control(lv_disp_drv_t *drv, display_backlight_control_t backlight_control);
void psoc_data_mode(lv_disp_drv_t *drv, data_mode_t mode);
void psoc_delay_ms(lv_disp_drv_t *drv, uint32_t ms);
void psoc_write_blocking(const uint8_t *tx_data, const size_t size);
void psoc_write_async(const uint8_t *tx_data, uint8_t *rx_data, const size_t size);
void psoc_hw_reset(lv_disp_drv_t *drv);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* PSOC_PLATFORM_H */
@C47D
Copy link
Author

C47D commented Sep 17, 2020

This is meant to be a draft of an "platform-agnostic" lv_driver arch, it's somewhat based on the work done by zladay with the lvgl_esp32_drivers.

How does it works:

The user fills an driver_generic_t object with callbacks to some platform specific code, this object is passed as user_data in the lv_disp_drv_t struct, so we need to enable LV_USE_USER_DATA in lv_conf.h.

The user callbacks are called from the display specific, in this case the ili9341 display driver. Inside this driver we send the data through a transport layer so the display doesn't care if the display is connected to the MCU via SPI, I2C, parallel, etc. The display_tl layer is currently named disp_spi.

User (MCU) specific code.

The only files with platform specific code is psoc_platform header and source file. For now we currently need to provide the following callbacks:

typedef struct {
    void (*display_backlight_control)(lv_disp_drv_t *drv, display_backlight_control_t backlight_control);
    void (*data_mode)(lv_disp_drv_t *drv, data_mode_t mode);
    void (*delay_ms)(lv_disp_drv_t *drv, uint32_t ms);
    void (*write_blocking)(const uint8_t *tx_data, const size_t size);
    void (*write_async)(const uint8_t *tx_data, uint8_t *rx_data, const size_t size);
    void (*hw_reset)(lv_disp_drv_t *drv);
} driver_generic_t;

write_blocking and write_async will be used by the tl, not yet implemented.

TODO

  • Add a way to get data from the display driver.
  • Validate user callbacks (being not NULL) before trying to call them.
  • Add code for e-paper displays.
  • Indev devices (touch controllers).
  • zladay added a way to add both display and touch abstractions to a device, so it handle rotation of display and touch devices automatically.
  • Display controller software reset when no hardware reset is registered.
  • Pass a initialization descriptor to the display_init function when we don't want to use a default one?
  • OSAL layer.
  • Any other suggestion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment