-
Notifications
You must be signed in to change notification settings - Fork 131
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactored display driver to be in separate files
This isn't as nice as the audio system (yet) but I'm not sure if adding indirection to previously inlined functions (get_buffer/send_data) will be a problem...
- Loading branch information
Showing
6 changed files
with
344 additions
and
282 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
static void lcd_init(void) | ||
{ | ||
} | ||
|
||
static void lcd_deinit(void) | ||
{ | ||
} | ||
|
||
static void lcd_set_backlight(float percent) | ||
{ | ||
} | ||
|
||
static inline uint16_t *lcd_get_buffer(size_t length) | ||
{ | ||
return (void *)0; | ||
} | ||
|
||
static inline void lcd_send_buffer(uint16_t *buffer, size_t length) | ||
{ | ||
} | ||
|
||
static void lcd_sync(void) | ||
{ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,259 @@ | ||
#include <freertos/FreeRTOS.h> | ||
#include <freertos/queue.h> | ||
#include <driver/spi_master.h> | ||
#include <driver/gpio.h> | ||
#include <driver/ledc.h> | ||
|
||
static spi_device_handle_t spi_dev; | ||
static QueueHandle_t spi_transactions; | ||
static QueueHandle_t spi_buffers; | ||
|
||
#define SPI_TRANSACTION_COUNT (10) | ||
#define SPI_BUFFER_COUNT (5) | ||
#define SPI_BUFFER_LENGTH (LCD_BUFFER_LENGTH * 2) | ||
|
||
static inline uint16_t *spi_take_buffer(void) | ||
{ | ||
uint16_t *buffer; | ||
if (xQueueReceive(spi_buffers, &buffer, pdMS_TO_TICKS(2500)) != pdTRUE) | ||
RG_PANIC("display"); | ||
return buffer; | ||
} | ||
|
||
static inline void spi_give_buffer(uint16_t *buffer) | ||
{ | ||
xQueueSend(spi_buffers, &buffer, portMAX_DELAY); | ||
} | ||
|
||
static inline void spi_queue_transaction(const void *data, size_t length, uint32_t type) | ||
{ | ||
if (!data || !length) | ||
return; | ||
|
||
spi_transaction_t *t; | ||
xQueueReceive(spi_transactions, &t, portMAX_DELAY); | ||
|
||
*t = (spi_transaction_t){ | ||
.tx_buffer = NULL, | ||
.length = length * 8, // In bits | ||
.user = (void *)type, | ||
.flags = 0, | ||
}; | ||
|
||
if (type & 2) | ||
{ | ||
t->tx_buffer = data; | ||
} | ||
else if (length < 5) | ||
{ | ||
memcpy(t->tx_data, data, length); | ||
t->flags = SPI_TRANS_USE_TXDATA; | ||
} | ||
else | ||
{ | ||
t->tx_buffer = memcpy(spi_take_buffer(), data, length); | ||
t->user = (void *)(type | 2); | ||
} | ||
|
||
if (spi_device_queue_trans(spi_dev, t, pdMS_TO_TICKS(2500)) != ESP_OK) | ||
{ | ||
RG_PANIC("display"); | ||
} | ||
} | ||
|
||
IRAM_ATTR | ||
static void spi_pre_transfer_cb(spi_transaction_t *t) | ||
{ | ||
// Set the data/command line accordingly | ||
gpio_set_level(RG_GPIO_LCD_DC, (int)t->user & 1); | ||
} | ||
|
||
IRAM_ATTR | ||
static void spi_task(void *arg) | ||
{ | ||
spi_transaction_t *t; | ||
|
||
while (spi_device_get_trans_result(spi_dev, &t, portMAX_DELAY) == ESP_OK) | ||
{ | ||
if ((int)t->user & 2) | ||
spi_give_buffer((uint16_t *)t->tx_buffer); | ||
xQueueSend(spi_transactions, &t, portMAX_DELAY); | ||
} | ||
} | ||
|
||
static void spi_init(void) | ||
{ | ||
spi_transactions = xQueueCreate(SPI_TRANSACTION_COUNT, sizeof(spi_transaction_t *)); | ||
spi_buffers = xQueueCreate(SPI_BUFFER_COUNT, sizeof(uint16_t *)); | ||
|
||
while (uxQueueSpacesAvailable(spi_transactions)) | ||
{ | ||
void *trans = malloc(sizeof(spi_transaction_t)); | ||
xQueueSend(spi_transactions, &trans, portMAX_DELAY); | ||
} | ||
|
||
while (uxQueueSpacesAvailable(spi_buffers)) | ||
{ | ||
void *buffer = rg_alloc(SPI_BUFFER_LENGTH, MEM_DMA); | ||
xQueueSend(spi_buffers, &buffer, portMAX_DELAY); | ||
} | ||
|
||
const spi_bus_config_t buscfg = { | ||
.miso_io_num = RG_GPIO_LCD_MISO, | ||
.mosi_io_num = RG_GPIO_LCD_MOSI, | ||
.sclk_io_num = RG_GPIO_LCD_CLK, | ||
.quadwp_io_num = -1, | ||
.quadhd_io_num = -1, | ||
}; | ||
|
||
const spi_device_interface_config_t devcfg = { | ||
.clock_speed_hz = RG_SCREEN_SPEED, // Typically SPI_MASTER_FREQ_40M or SPI_MASTER_FREQ_80M | ||
.mode = 0, // SPI mode 0 | ||
.spics_io_num = RG_GPIO_LCD_CS, // CS pin | ||
.queue_size = SPI_TRANSACTION_COUNT, // We want to be able to queue 5 transactions at a time | ||
.pre_cb = &spi_pre_transfer_cb, // Specify pre-transfer callback to handle D/C line and SPI lock | ||
.flags = SPI_DEVICE_NO_DUMMY, // SPI_DEVICE_HALFDUPLEX; | ||
}; | ||
|
||
esp_err_t ret; | ||
|
||
// Initialize the SPI bus | ||
ret = spi_bus_initialize(RG_SCREEN_HOST, &buscfg, SPI_DMA_CH_AUTO); | ||
RG_ASSERT(ret == ESP_OK || ret == ESP_ERR_INVALID_STATE, "spi_bus_initialize failed."); | ||
|
||
ret = spi_bus_add_device(RG_SCREEN_HOST, &devcfg, &spi_dev); | ||
RG_ASSERT(ret == ESP_OK, "spi_bus_add_device failed."); | ||
|
||
rg_task_create("rg_spi", &spi_task, NULL, 1.5 * 1024, RG_TASK_PRIORITY_7, 1); | ||
} | ||
|
||
static void spi_deinit(void) | ||
{ | ||
spi_bus_remove_device(spi_dev); | ||
spi_bus_free(RG_SCREEN_HOST); | ||
} | ||
|
||
#define ILI9341_CMD(cmd, data...) \ | ||
{ \ | ||
const uint8_t c = cmd, x[] = {data}; \ | ||
spi_queue_transaction(&c, 1, 0); \ | ||
if (sizeof(x)) \ | ||
spi_queue_transaction(&x, sizeof(x), 1); \ | ||
} | ||
|
||
static void lcd_set_backlight(float percent) | ||
{ | ||
float level = RG_MIN(RG_MAX(percent / 100.f, 0), 1.f); | ||
int error_code = 0; | ||
|
||
#if defined(RG_GPIO_LCD_BCKL) | ||
error_code = ledc_set_fade_time_and_start(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 0x1FFF * level, 50, 0); | ||
#elif defined(RG_TARGET_QTPY_GAMER) | ||
rg_i2c_gpio_set_direction(AW_TFT_BACKLIGHT, 0); | ||
rg_i2c_gpio_set_level(AW_TFT_BACKLIGHT, level * 255); | ||
#endif | ||
|
||
if (error_code) | ||
RG_LOGE("failed setting backlight to %d%% (0x%02X)\n", (int)(100 * level), error_code); | ||
else | ||
RG_LOGI("backlight set to %d%%\n", (int)(100 * level)); | ||
} | ||
|
||
static void lcd_set_window(int left, int top, int width, int height) | ||
{ | ||
int right = left + width - 1; | ||
int bottom = top + height - 1; | ||
|
||
if (left < 0 || top < 0 || right >= display.screen.real_width || bottom >= display.screen.real_height) | ||
RG_LOGW("Bad lcd window (x0=%d, y0=%d, x1=%d, y1=%d)\n", left, top, right, bottom); | ||
|
||
ILI9341_CMD(0x2A, left >> 8, left & 0xff, right >> 8, right & 0xff); // Horiz | ||
ILI9341_CMD(0x2B, top >> 8, top & 0xff, bottom >> 8, bottom & 0xff); // Vert | ||
ILI9341_CMD(0x2C); // Memory write | ||
} | ||
|
||
static inline uint16_t *lcd_get_buffer(size_t length) | ||
{ | ||
// RG_ASSERT(length < LCD_BUFFER_LENGTH, "Invalid length"); | ||
return spi_take_buffer(); | ||
} | ||
|
||
static inline void lcd_send_buffer(uint16_t *buffer, size_t length) | ||
{ | ||
if (length > 0) | ||
spi_queue_transaction(buffer, length * sizeof(*buffer), 3); | ||
else | ||
spi_give_buffer(buffer); | ||
} | ||
|
||
static void lcd_sync(void) | ||
{ | ||
// Unused for SPI LCD | ||
} | ||
|
||
static void lcd_init(void) | ||
{ | ||
#ifdef RG_GPIO_LCD_BCKL | ||
// Initialize backlight at 0% to avoid the lcd reset flash | ||
ledc_timer_config(&(ledc_timer_config_t){ | ||
.duty_resolution = LEDC_TIMER_13_BIT, | ||
.freq_hz = 5000, | ||
.speed_mode = LEDC_LOW_SPEED_MODE, | ||
.timer_num = LEDC_TIMER_0, | ||
}); | ||
ledc_channel_config(&(ledc_channel_config_t){ | ||
.channel = LEDC_CHANNEL_0, | ||
.duty = 0, | ||
.gpio_num = RG_GPIO_LCD_BCKL, | ||
.speed_mode = LEDC_LOW_SPEED_MODE, | ||
.timer_sel = LEDC_TIMER_0, | ||
}); | ||
ledc_fade_func_install(0); | ||
#endif | ||
|
||
spi_init(); | ||
|
||
// Setup Data/Command line | ||
gpio_set_direction(RG_GPIO_LCD_DC, GPIO_MODE_OUTPUT); | ||
gpio_set_level(RG_GPIO_LCD_DC, 1); | ||
|
||
#if defined(RG_GPIO_LCD_RST) | ||
gpio_set_direction(RG_GPIO_LCD_RST, GPIO_MODE_OUTPUT); | ||
gpio_set_level(RG_GPIO_LCD_RST, 0); | ||
rg_usleep(100 * 1000); | ||
gpio_set_level(RG_GPIO_LCD_RST, 1); | ||
rg_usleep(10 * 1000); | ||
#elif defined(RG_TARGET_QTPY_GAMER) | ||
rg_i2c_gpio_set_direction(AW_TFT_RESET, 0); | ||
rg_i2c_gpio_set_level(AW_TFT_RESET, 0); | ||
rg_usleep(100 * 1000); | ||
rg_i2c_gpio_set_level(AW_TFT_RESET, 1); | ||
rg_usleep(10 * 1000); | ||
#endif | ||
|
||
ILI9341_CMD(0x01); // Reset | ||
rg_usleep(5 * 1000); // Wait 5ms after reset | ||
ILI9341_CMD(0x3A, 0X05); // Pixel Format Set RGB565 | ||
#ifdef RG_SCREEN_INIT | ||
RG_SCREEN_INIT(); | ||
#else | ||
#warning "LCD init sequence is not defined for this device!" | ||
#endif | ||
ILI9341_CMD(0x11); // Exit Sleep | ||
rg_usleep(5 * 1000);// Wait 5ms after sleep out | ||
ILI9341_CMD(0x29); // Display on | ||
|
||
rg_display_clear(C_BLACK); | ||
rg_usleep(10 * 1000); | ||
lcd_set_backlight(config.backlight); | ||
} | ||
|
||
static void lcd_deinit(void) | ||
{ | ||
#ifdef RG_SCREEN_DEINIT | ||
RG_SCREEN_DEINIT(); | ||
#endif | ||
spi_deinit(); | ||
// gpio_reset_pin(RG_GPIO_LCD_BCKL); | ||
// gpio_reset_pin(RG_GPIO_LCD_DC); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
#include <SDL2/SDL.h> | ||
|
||
static void lcd_init(void) | ||
{ | ||
} | ||
|
||
static void lcd_deinit(void) | ||
{ | ||
} | ||
|
||
static void lcd_set_backlight(float percent) | ||
{ | ||
} | ||
|
||
static inline uint16_t *lcd_get_buffer(size_t length) | ||
{ | ||
return (void *)0; | ||
} | ||
|
||
static inline void lcd_send_buffer(uint16_t *buffer, size_t length) | ||
{ | ||
} | ||
|
||
static void lcd_sync(void) | ||
{ | ||
} |
Oops, something went wrong.