diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 1950ad3..40f5b7d 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,3 +1,3 @@ -idf_component_register(SRCS "spincoat-plater-firmware.c" "dshot_esc_encoder.c" +idf_component_register(SRCS "spincoat-plater-firmware.c" "dshot_esc_encoder.c" "display.c" PRIV_REQUIRES esp_driver_rmt esp_driver_gpio esp_driver_uart esp_driver_spi esp_lcd unity INCLUDE_DIRS ".") diff --git a/main/display.c b/main/display.c new file mode 100644 index 0000000..cffae3d --- /dev/null +++ b/main/display.c @@ -0,0 +1,127 @@ +#include "display.h" + +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" + +#include "unity.h" +#include "unity_test_runner.h" + +#include "esp_log.h" +#include "driver/gpio.h" + +#include "hal/spi_types.h" +#include "esp_lcd_panel_ops.h" +#include "driver/spi_common.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_panel_commands.h" +#include "esp_lcd_ili9341.h" + +SemaphoreHandle_t refresh_finish = NULL; + +static const char * TAG = "spincoat-plater-firmware/display"; + +/** + * Callback for the TFT LCD, notifying when the screen is ready for another chunk of data and + * releasing the drawing semaphore. + */ +IRAM_ATTR static bool notify_refresh_ready(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx) +{ + BaseType_t need_yield = pdFALSE; + + xSemaphoreGiveFromISR(refresh_finish, &need_yield); + return (need_yield == pdTRUE); +} + +/** + * Draws a test bitmap of stripes of colors to the LCD. + */ +void test_draw_bitmap(esp_lcd_panel_handle_t panel_handle) +{ + refresh_finish = xSemaphoreCreateBinary(); + TEST_ASSERT_NOT_NULL(refresh_finish); + + uint16_t row_line = TFT_VRES / TFT_BPP; + uint8_t byte_per_pixel = TFT_BPP / 8; + uint8_t *color = (uint8_t *)heap_caps_calloc(1, row_line * TFT_VRES * byte_per_pixel, MALLOC_CAP_DMA); + TEST_ASSERT_NOT_NULL(color); + + for (int j = 0; j < TFT_BPP; j++) { + for (int i = 0; i < row_line * TFT_HRES ; i++) { + for (int k = 0; k < byte_per_pixel; k++) { + color[i * byte_per_pixel + k] = (SPI_SWAP_DATA_TX(BIT(j), TFT_BPP) >> (k * 8)) & 0xff; + } + } + TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, j * row_line, TFT_HRES , (j + 1) * row_line, color)); + xSemaphoreTake(refresh_finish, portMAX_DELAY); + } + free(color); + vSemaphoreDelete(refresh_finish); +} + +/** + * Initializes the SPI LCD in preparation for writing graphics to it. + */ +void init_spi_lcd(void) { + ESP_LOGI(TAG, "Turn on backlight"); + + gpio_config_t io_conf = { + .pin_bit_mask = (1ULL << GPIO_TFT_BL), + .mode = GPIO_MODE_OUTPUT, + .pull_up_en = GPIO_PULLUP_ENABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_DISABLE + }; + + gpio_config(&io_conf); + gpio_set_level(GPIO_TFT_BL, 1); + + ESP_LOGI(TAG, "Initialize SPI bus"); + const spi_bus_config_t bus_config = ILI9341_PANEL_BUS_SPI_CONFIG(GPIO_TFT_SCKL, + GPIO_TFT_MOSI, TFT_HRES * 80 * TFT_BPP / 8); + TEST_ESP_OK(spi_bus_initialize(LCD_SPI_HOST, &bus_config, SPI_DMA_CH_AUTO)); + + ESP_LOGI(TAG, "Install panel IO"); + esp_lcd_panel_io_handle_t io_handle = NULL; + const esp_lcd_panel_io_spi_config_t io_config = ILI9341_PANEL_IO_SPI_CONFIG(GPIO_TFT_CS, GPIO_TFT_DC, + notify_refresh_ready, NULL); + + TEST_ESP_OK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_SPI_HOST, &io_config, &io_handle)); + + ESP_LOGI(TAG, "Install ili9341 panel driver"); + esp_lcd_panel_handle_t panel_handle = NULL; + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = -1, // Shared with Touch reset +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) + .color_space = ESP_LCD_COLOR_SPACE_BGR, +#elif ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(6, 0, 0) + .rgb_endian = LCD_RGB_ENDIAN_BGR, +#else + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR, +#endif + .bits_per_pixel = TFT_BPP, + }; + TEST_ESP_OK(esp_lcd_new_panel_ili9341(io_handle, &panel_config, &panel_handle)); + TEST_ESP_OK(esp_lcd_panel_reset(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_init(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_mirror(panel_handle, true, true)); +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) + TEST_ESP_OK(esp_lcd_panel_disp_off(panel_handle, false)); +#else + TEST_ESP_OK(esp_lcd_panel_disp_on_off(panel_handle, true)); +#endif + + ESP_LOGI(TAG, "Finished init of spi LCD."); + ESP_LOGI(TAG, "Drawing bitmap.");; + test_draw_bitmap(panel_handle); + vTaskDelay(pdMS_TO_TICKS(3000)); + + // Tear it back down, move this into a function to clean up after ourselves if it's ever needed. + ESP_LOGI(TAG, "Destroying and cleaning up LCD/SPI handles."); + gpio_reset_pin(GPIO_TFT_BL); + TEST_ESP_OK(esp_lcd_panel_del(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_io_del(io_handle)); + TEST_ESP_OK(spi_bus_free(LCD_SPI_HOST)); +} diff --git a/main/display.h b/main/display.h new file mode 100644 index 0000000..f393d53 --- /dev/null +++ b/main/display.h @@ -0,0 +1,27 @@ +#pragma once + +#include "stdbool.h" + +#include "esp_lcd_panel_io.h" + +#define LCD_SPI_HOST SPI2_HOST + +#define GPIO_TFT_MISO CONFIG_TFT_MISO_PIN +#define GPIO_TFT_MOSI CONFIG_TFT_MOSI_PIN +#define GPIO_TFT_SCKL CONFIG_TFT_SCKL_PIN +#define GPIO_TFT_CS CONFIG_TFT_CS_PIN +#define GPIO_TFT_DC CONFIG_TFT_DC_PIN +#define GPIO_TFT_BL CONFIG_TFT_BL_PIN // Backlight +#define TFT_HRES CONFIG_TFT_HRES +#define TFT_VRES CONFIG_TFT_VRES +#define TFT_BPP CONFIG_TFT_BPP + +/** + * Draws a test bitmap of stripes of colors to the LCD. + */ +void test_draw_bitmap(esp_lcd_panel_handle_t panel_handle); + +/** + * Initializes the SPI LCD in preparation for writing graphics to it. + */ +void init_spi_lcd(void); diff --git a/main/spincoat-plater-firmware.c b/main/spincoat-plater-firmware.c index 29eb3b5..8337c22 100644 --- a/main/spincoat-plater-firmware.c +++ b/main/spincoat-plater-firmware.c @@ -26,6 +26,7 @@ #include "esp_lcd_ili9341.h" #include "dshot_esc_encoder.h" +#include "display.h" #if CONFIG_IDF_TARGET_ESP32H2 #define DSHOT_ESC_RESOLUTION_HZ 32000000 // 32MHz resolution, DSHot protocol needs a relative high resolution @@ -36,24 +37,10 @@ #define GPIO_ESC_CTRL CONFIG_ESC_CTRL_PIN #define GPIO_ESC_RX CONFIG_TELEMETRY_RX_PIN #define UART_NUM UART_NUM_2 -#define LCD_SPI_HOST SPI2_HOST - -#define GPIO_TFT_MISO CONFIG_TFT_MISO_PIN -#define GPIO_TFT_MOSI CONFIG_TFT_MOSI_PIN -#define GPIO_TFT_SCKL CONFIG_TFT_SCKL_PIN -#define GPIO_TFT_CS CONFIG_TFT_CS_PIN -#define GPIO_TFT_DC CONFIG_TFT_DC_PIN -#define GPIO_TFT_BL CONFIG_TFT_BL_PIN // Backlight -#define TFT_HRES CONFIG_TFT_HRES -#define TFT_VRES CONFIG_TFT_VRES -#define TFT_BPP CONFIG_TFT_BPP - #define ESP_INTR_FLAG_DEFAULT 0 static const char *TAG = "spincoat-plater-firmware"; -static SemaphoreHandle_t refresh_finish = NULL; - static QueueHandle_t uart_queue = NULL; const int uart_buffer_size = (1024 * 2); @@ -148,107 +135,6 @@ void init_telemetry_uart_rx(void) { ESP_ERROR_CHECK(uart_set_pin(UART_NUM, UART_PIN_NO_CHANGE, GPIO_ESC_RX, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE)); } -/** - * Callback for the TFT LCD, notifying when the screen is ready for another chunk of data and - * releasing the drawing semaphore. - */ -IRAM_ATTR static bool notify_refresh_ready(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx) -{ - BaseType_t need_yield = pdFALSE; - - xSemaphoreGiveFromISR(refresh_finish, &need_yield); - return (need_yield == pdTRUE); -} - -/** - * Draws a test bitmap of stripes of colors to the LCD. - */ -static void test_draw_bitmap(esp_lcd_panel_handle_t panel_handle) -{ - refresh_finish = xSemaphoreCreateBinary(); - TEST_ASSERT_NOT_NULL(refresh_finish); - - uint16_t row_line = TFT_VRES / TFT_BPP; - uint8_t byte_per_pixel = TFT_BPP / 8; - uint8_t *color = (uint8_t *)heap_caps_calloc(1, row_line * TFT_VRES * byte_per_pixel, MALLOC_CAP_DMA); - TEST_ASSERT_NOT_NULL(color); - - for (int j = 0; j < TFT_BPP; j++) { - for (int i = 0; i < row_line * TFT_HRES ; i++) { - for (int k = 0; k < byte_per_pixel; k++) { - color[i * byte_per_pixel + k] = (SPI_SWAP_DATA_TX(BIT(j), TFT_BPP) >> (k * 8)) & 0xff; - } - } - TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, j * row_line, TFT_HRES , (j + 1) * row_line, color)); - xSemaphoreTake(refresh_finish, portMAX_DELAY); - } - free(color); - vSemaphoreDelete(refresh_finish); -} - -/** - * Initializes the SPI LCD in preparation for writing graphics to it. - */ -void init_spi_lcd(void) { - ESP_LOGI(TAG, "Turn on backlight"); - - gpio_config_t io_conf = { - .pin_bit_mask = (1ULL << GPIO_TFT_BL), - .mode = GPIO_MODE_OUTPUT, - .pull_up_en = GPIO_PULLUP_ENABLE, - .pull_down_en = GPIO_PULLDOWN_DISABLE, - .intr_type = GPIO_INTR_DISABLE - }; - - gpio_config(&io_conf); - gpio_set_level(GPIO_TFT_BL, 1); - - ESP_LOGI(TAG, "Initialize SPI bus"); - const spi_bus_config_t bus_config = ILI9341_PANEL_BUS_SPI_CONFIG(GPIO_TFT_SCKL, - GPIO_TFT_MOSI, TFT_HRES * 80 * TFT_BPP / 8); - TEST_ESP_OK(spi_bus_initialize(LCD_SPI_HOST, &bus_config, SPI_DMA_CH_AUTO)); - - ESP_LOGI(TAG, "Install panel IO"); - esp_lcd_panel_io_handle_t io_handle = NULL; - const esp_lcd_panel_io_spi_config_t io_config = ILI9341_PANEL_IO_SPI_CONFIG(GPIO_TFT_CS, GPIO_TFT_DC, - - TEST_ESP_OK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_SPI_HOST, &io_config, &io_handle)); - - ESP_LOGI(TAG, "Install ili9341 panel driver"); - esp_lcd_panel_handle_t panel_handle = NULL; - const esp_lcd_panel_dev_config_t panel_config = { - .reset_gpio_num = -1, // Shared with Touch reset -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) - .color_space = ESP_LCD_COLOR_SPACE_BGR, -#elif ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(6, 0, 0) - .rgb_endian = LCD_RGB_ENDIAN_BGR, -#else - .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR, -#endif - .bits_per_pixel = TFT_BPP, - }; - TEST_ESP_OK(esp_lcd_new_panel_ili9341(io_handle, &panel_config, &panel_handle)); - TEST_ESP_OK(esp_lcd_panel_reset(panel_handle)); - TEST_ESP_OK(esp_lcd_panel_init(panel_handle)); - TEST_ESP_OK(esp_lcd_panel_mirror(panel_handle, true, true)); -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) - TEST_ESP_OK(esp_lcd_panel_disp_off(panel_handle, false)); -#else - TEST_ESP_OK(esp_lcd_panel_disp_on_off(panel_handle, true)); -#endif - - ESP_LOGI(TAG, "Finished init of spi LCD."); - ESP_LOGI(TAG, "Drawing bitmap.");; - test_draw_bitmap(panel_handle); - vTaskDelay(pdMS_TO_TICKS(3000)); - - // Tear it back down, move this into a function to clean up after ourselves if it's ever needed. - ESP_LOGI(TAG, "Destroying and cleaning up LCD/SPI handles."); - gpio_reset_pin(GPIO_TFT_BL); - TEST_ESP_OK(esp_lcd_panel_del(panel_handle)); - TEST_ESP_OK(esp_lcd_panel_io_del(io_handle)); - TEST_ESP_OK(spi_bus_free(LCD_SPI_HOST)); -} /** * Sends a DSHOT packet via the RMT. Make sure the RMT channel has been initialized