From ca1d0cbc2bc4f3def5a109ab1f76f1b2c572e969 Mon Sep 17 00:00:00 2001 From: minlewang Date: Fri, 24 Jun 2022 15:45:58 +0800 Subject: [PATCH] [feat][mcu_lcd] add st7796 lcd driver --- bsp/board/bl702/board.c | 2 +- bsp/bsp_common/mcu_lcd/mcu_lcd.c | 19 ++ bsp/bsp_common/mcu_lcd/mcu_lcd.h | 10 +- bsp/bsp_common/mcu_lcd/st7796.c | 368 +++++++++++++++++++++++++++++++ bsp/bsp_common/mcu_lcd/st7796.h | 62 ++++++ examples/lvgl/CMakeLists.txt | 2 +- examples/lvgl/main.c | 1 + 7 files changed, 461 insertions(+), 3 deletions(-) create mode 100644 bsp/bsp_common/mcu_lcd/st7796.c create mode 100644 bsp/bsp_common/mcu_lcd/st7796.h diff --git a/bsp/board/bl702/board.c b/bsp/board/bl702/board.c index 6b23d3f8..ed611244 100644 --- a/bsp/board/bl702/board.c +++ b/bsp/board/bl702/board.c @@ -167,7 +167,7 @@ static void board_pin_mux_init(void) { GLB_GPIO_Cfg_Type gpio_cfg; uint32_t tmpVal; - gpio_cfg.drive = 0; + gpio_cfg.drive = 2; gpio_cfg.smtCtrl = 1; uint8_t hbn_gpio_mask = 0x1f; uint8_t hbn_aon_ie = 0; diff --git a/bsp/bsp_common/mcu_lcd/mcu_lcd.c b/bsp/bsp_common/mcu_lcd/mcu_lcd.c index 902a9fd0..f7e13bb6 100644 --- a/bsp/bsp_common/mcu_lcd/mcu_lcd.c +++ b/bsp/bsp_common/mcu_lcd/mcu_lcd.c @@ -74,6 +74,8 @@ int lcd_init(void) res = st7735s_init(); #elif defined(MCU_LCD_ST7789V) res = st7789v_init(); +#elif defined(MCU_LCD_ST7796) + res = st7796_spi_init(); #endif lcd_dev_ifs = device_find("lcd_dev_ifs"); return res; @@ -106,6 +108,9 @@ int lcd_set_dir(uint8_t dir, uint8_t mir_flag) #elif defined(MCU_LCD_ST7789V) st7789v_set_dir(dir); return 0; +#elif defined(MCU_LCD_ST7796) + st7796_spi_set_dir(dir); + return 0; #endif } @@ -128,6 +133,9 @@ int lcd_draw_point(uint16_t x, uint16_t y, lcd_color_t color) #elif defined(MCU_LCD_ST7789V) st7789v_draw_point(x, y, color); return 0; +#elif defined(MCU_LCD_ST7796) + st7796_spi_draw_point(x, y, color); + return 0; #endif } @@ -152,6 +160,9 @@ int lcd_draw_area(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, lcd_color_ #elif defined(MCU_LCD_ST7789V) st7789v_draw_area(x1, y1, x2, y2, color); return 0; +#elif defined(MCU_LCD_ST7796) + st7796_spi_draw_area(x1, y1, x2, y2, color); + return 0; #endif } @@ -176,6 +187,9 @@ int lcd_draw_picture_blocking(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2 #elif defined(MCU_LCD_ST7789V) st7789v_draw_picture_blocking(x1, y1, x2, y2, picture); return 0; +#elif defined(MCU_LCD_ST7796) + st7796_spi_draw_picture_blocking(x1, y1, x2, y2, picture); + return 0; #endif } @@ -201,6 +215,9 @@ int lcd_draw_picture_nonblocking(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t #elif defined(MCU_LCD_ST7789V) st7789v_draw_picture_nonblocking(x1, y1, x2, y2, picture); return 0; +#elif defined(MCU_LCD_ST7796) + st7796_spi_draw_picture_nonblocking(x1, y1, x2, y2, picture); + return 0; #endif } @@ -217,6 +234,8 @@ int lcd_draw_is_busy(void) return st7735s_draw_is_busy(); #elif defined(MCU_LCD_ST7789V) return st7789v_draw_is_busy(); +#elif defined(MCU_LCD_ST7796) + return st7796_spi_draw_is_busy(); #endif } diff --git a/bsp/bsp_common/mcu_lcd/mcu_lcd.h b/bsp/bsp_common/mcu_lcd/mcu_lcd.h index b0c1adbb..6b4ab85b 100644 --- a/bsp/bsp_common/mcu_lcd/mcu_lcd.h +++ b/bsp/bsp_common/mcu_lcd/mcu_lcd.h @@ -26,9 +26,10 @@ #include "font.h" -#define MCU_LCD_ILI9341 +// #define MCU_LCD_ILI9341 // #define MCU_LCD_ST7735S /* 未验证 */ // #define MCU_LCD_ST7789V +#define MCU_LCD_ST7796 extern struct device *lcd_dev_ifs; @@ -53,6 +54,13 @@ typedef uint16_t lcd_color_t; #define LCD_H ST7789V_H typedef uint16_t lcd_color_t; +#elif defined MCU_LCD_ST7796 + +#include "st7796.h" +#define LCD_W ST7796_SPI_W +#define LCD_H ST7796_SPI_H +typedef st7796_spi_color_t lcd_color_t; + #endif #define ABS(x) ((x) > 0 ? (x) : -(x)) diff --git a/bsp/bsp_common/mcu_lcd/st7796.c b/bsp/bsp_common/mcu_lcd/st7796.c new file mode 100644 index 00000000..b8b2ab61 --- /dev/null +++ b/bsp/bsp_common/mcu_lcd/st7796.c @@ -0,0 +1,368 @@ +/** + * @file st7796_spi.c + * @brief + * + * Copyright (c) 2021 Bouffalolab team + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ + +#include "st7796.h" +#include "mcu_lcd.h" + +#include "hal_spi.h" +#include "hal_dma.h" +#include "hal_gpio.h" + +#define ST7796_SPI_ID 0 +#define ST7796_SPI_INDEX SPI0_INDEX +#define ST7796_SPI_DMA_INDEX DMA0_CH3_INDEX + +static struct device *st7796_spi; +static struct device *dma_spi_tx; + +const st7796_spi_init_cmd_t st7796_spi_init_cmds[] = { + { 0x01, NULL, 0 }, + { 0xFF, NULL, 10 }, + { 0x11, NULL, 0 }, /* Exit sleep */ + { 0xFF, NULL, 120 }, + + { 0xF0, "\xC3", 1 }, + { 0xF0, "\x96", 1 }, + + { 0x36, "\x48", 1 }, + + { 0x3A, "\x05", 1 }, /* RGB565 */ + { 0xE6, "\x0F\xF2\x3F\x4F\x4F\x28\x0E\x00", 8 }, + { 0xC5, "\x2A", 1 }, + +/* Display Inversion */ +#if 1 + { 0xB4, "\x01", 1 }, + { 0x21, NULL, 0 }, +#endif + + { 0xE0, "\xF0\x03\x0A\x11\x14\x1C\x3B\x55\x4A\x0A\x13\x14\x1C\x1F", 14 }, /* Set Gamma */ + { 0XE1, "\xF0\x03\x0A\x0C\x0C\x09\x36\x54\x49\x0F\x1B\x18\x1B\x1F", 14 }, /* Set Gamma */ + + { 0xF0, "\x3C", 1 }, + { 0xF0, "\x69", 1 }, + + { 0x29, NULL, 0 }, /* Display on */ + { 0xFF, NULL, 10 }, +}; + +/** + * @brief st7796_spi_init + * + * @return int 0:succes 1:error + */ +static int st7796_spi_peripheral_init(void) +{ + gpio_set_mode(ST7796_SPI_CS_PIN, GPIO_OUTPUT_MODE); + gpio_set_mode(ST7796_SPI_DC_PIN, GPIO_OUTPUT_MODE); + ST7796_SPI_CS_HIGH; + ST7796_SPI_DC_HIGH; + + st7796_spi = device_find("lcd_dev_ifs"); + if (st7796_spi) { + device_close(st7796_spi); + } else { + spi_register(ST7796_SPI_INDEX, "lcd_dev_ifs"); + st7796_spi = device_find("lcd_dev_ifs"); + } + if (st7796_spi) { + SPI_DEV(st7796_spi)->mode = SPI_MASTER_MODE; + SPI_DEV(st7796_spi)->clk = (36 * 1000 * 1000); + SPI_DEV(st7796_spi)->direction = SPI_MSB_BYTE0_DIRECTION_FIRST; + SPI_DEV(st7796_spi)->datasize = SPI_DATASIZE_8BIT; + SPI_DEV(st7796_spi)->clk_polaraity = SPI_POLARITY_LOW; + SPI_DEV(st7796_spi)->clk_phase = SPI_PHASE_1EDGE; + SPI_DEV(st7796_spi)->fifo_threshold = 0; + device_open(st7796_spi, DEVICE_OFLAG_STREAM_TX); + } else { + return 1; + } + + dma_spi_tx = device_find("lcd_dev_ifs_dma"); + if (dma_spi_tx) { + device_close(dma_spi_tx); + } else { + dma_register(ST7796_SPI_DMA_INDEX, "lcd_dev_ifs_dma"); + dma_spi_tx = device_find("lcd_dev_ifs_dma"); + } + if (dma_spi_tx) { + DMA_DEV(dma_spi_tx)->direction = DMA_MEMORY_TO_PERIPH; + DMA_DEV(dma_spi_tx)->transfer_mode = DMA_LLI_ONCE_MODE; + DMA_DEV(dma_spi_tx)->src_req = DMA_REQUEST_NONE; + DMA_DEV(dma_spi_tx)->dst_req = DMA_REQUEST_SPI0_TX; + DMA_DEV(dma_spi_tx)->src_addr_inc = DMA_ADDR_INCREMENT_ENABLE; + DMA_DEV(dma_spi_tx)->dst_addr_inc = DMA_ADDR_INCREMENT_DISABLE; + DMA_DEV(dma_spi_tx)->src_burst_size = DMA_BURST_INCR1; + DMA_DEV(dma_spi_tx)->dst_burst_size = DMA_BURST_INCR1; + DMA_DEV(dma_spi_tx)->src_width = DMA_TRANSFER_WIDTH_8BIT; + DMA_DEV(dma_spi_tx)->dst_width = DMA_TRANSFER_WIDTH_8BIT; + + device_open(dma_spi_tx, 0); + device_set_callback(dma_spi_tx, NULL); + device_control(st7796_spi, DEVICE_CTRL_ATTACH_TX_DMA, dma_spi_tx); + device_control(dma_spi_tx, DEVICE_CTRL_CLR_INT, NULL); + } else { + return 1; + } + + device_control(st7796_spi, DEVICE_CTRL_TX_DMA_SUSPEND, NULL); + return 0; +} + +/** + * @brief st7796_spi_write_cmd + * + * @param cmd + * @return int 0:succes 1:error + */ +static int st7796_spi_write_cmd(uint8_t cmd) +{ + ST7796_SPI_DC_LOW; + ST7796_SPI_CS_LOW; + int res = spi_transmit(st7796_spi, &cmd, 1, SPI_TRANSFER_TYPE_8BIT); + ST7796_SPI_CS_HIGH; + ST7796_SPI_DC_HIGH; + return res; +} + +/** + * @brief st7796_spi_write_data_byte + * + * @param data + * @return int 0:succes 1:error + */ +static int st7796_spi_write_data_byte(uint8_t data) +{ + ST7796_SPI_CS_LOW; + int res = spi_transmit(st7796_spi, &data, 1, SPI_TRANSFER_TYPE_8BIT); + ST7796_SPI_CS_HIGH; + return res; +} + +/** + * @brief st7796_spi_draw_is_busy, After the call st7796_spi_draw_picture_dma must check this, + * if st7796_spi_draw_is_busy() == 1, Don't allow other draw !! + * can run in the DMA interrupt callback function. + * + * @return int 0:draw end; 1:Being draw + */ +int st7796_spi_draw_is_busy(void) +{ + if (dma_channel_check_busy(SPI_DEV(st7796_spi)->tx_dma)) { + return 1; + } else { + /* Wait for tx FIFO to be empty */ + while (device_control(st7796_spi, DEVICE_CTRL_SPI_GET_TX_FIFO, NULL) < SPI_FIFO_LEN) + ; + /* Wait for the SPI bus to be idle */ + while (device_control(st7796_spi, DEVICE_CTRL_SPI_GET_BUS_BUSY_STATUS, NULL) != 0) + ; + /* Switch the SPI to non-DMA mode */ + device_control(st7796_spi, DEVICE_CTRL_TX_DMA_SUSPEND, NULL); + ST7796_SPI_CS_HIGH; + + return 0; + } +} + +/** + * @brief st7796_spi_init + * + * @return int + */ +int st7796_spi_init() +{ + int res = st7796_spi_peripheral_init(); + if (res) { + return res; + } + + for (uint16_t i = 0; i < (sizeof(st7796_spi_init_cmds) / sizeof(st7796_spi_init_cmd_t)); i++) { + if (st7796_spi_init_cmds[i].cmd == 0xFF) { + bflb_platform_delay_ms(st7796_spi_init_cmds[i].databytes); + } else { + /* send register address */ + res |= st7796_spi_write_cmd(st7796_spi_init_cmds[i].cmd); + + /* send register data */ + for (uint8_t j = 0; j < (st7796_spi_init_cmds[i].databytes & 0x7F); j++) { + res |= st7796_spi_write_data_byte(st7796_spi_init_cmds[i].data[j]); + } + + if (res) { + return res; + } + } + } + st7796_spi_set_draw_window(0, 0, ST7796_SPI_H, ST7796_SPI_W); + return res; +} + +/** + * @brief + * + * @param dir + * @param mir_flag + */ +int st7796_spi_set_dir(uint8_t dir) +{ + st7796_spi_write_cmd(0x36); + switch (dir) { + case 0: + st7796_spi_write_data_byte(0x08); + break; + case 1: + st7796_spi_write_data_byte(0x28); + break; + case 2: + st7796_spi_write_data_byte(0x88); + break; + case 3: + st7796_spi_write_data_byte(0xE8); + break; + default: + return -1; + break; + } + return dir; +} + +/** + * @brief st7796_spi_set_draw_window + * + * @param x1 + * @param y1 + * @param x2 + * @param y2 + */ +void st7796_spi_set_draw_window(uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2) +{ +#if ST7796_SPI_OFFSET_X + x1 += ST7796_SPI_OFFSET_X; + x2 += ST7796_SPI_OFFSET_X; +#endif +#if ST7796_SPI_OFFSET_Y + y1 += ST7796_SPI_OFFSET_Y; + y2 += ST7796_SPI_OFFSET_Y; +#endif + + st7796_spi_write_cmd(0x2a); + st7796_spi_write_data_byte(x1 >> 8); + st7796_spi_write_data_byte(x1); + st7796_spi_write_data_byte(x2 >> 8); + st7796_spi_write_data_byte(x2); + + st7796_spi_write_cmd(0x2b); + st7796_spi_write_data_byte(y1 >> 8); + st7796_spi_write_data_byte(y1); + st7796_spi_write_data_byte(y2 >> 8); + st7796_spi_write_data_byte(y2); + + st7796_spi_write_cmd(0x2c); +} + +/** + * @brief st7796_spi_draw_point + * + * @param x + * @param y + * @param color + */ +void st7796_spi_draw_point(uint16_t x, uint16_t y, st7796_spi_color_t color) +{ + if (lcd_auto_swap_flag) { + color = ((color >> 8) & 0xFF) | color << 8; + } + + st7796_spi_set_draw_window(x, y, x, y); + ST7796_SPI_CS_LOW; + device_write(st7796_spi, 0, &color, 2); + ST7796_SPI_CS_HIGH; +} + +/** + * @brief st7796_draw_area + * + * @param x1 + * @param y1 + * @param x2 + * @param y2 + * @param color + */ +void st7796_spi_draw_area(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, st7796_spi_color_t color) +{ + if (lcd_auto_swap_flag) { + color = ((color >> 8) & 0xFF) | color << 8; + } + + st7796_spi_set_draw_window(x1, y1, x2, y2); + ST7796_SPI_CS_LOW; + for (uint16_t i = y1; i <= y2; i++) { + for (uint16_t j = x1; j <= x2; j++) + spi_transmit(st7796_spi, &color, 1, SPI_TRANSFER_TYPE_16BIT); + } + ST7796_SPI_CS_HIGH; +} + +/** + * @brief st7796_draw_picture_dma, Non-blocking! Using DMA acceleration, Not waiting for the draw end + * After the call, No other operations are allowed until (st7796_draw_is_busy()==0) + * + * @param x1 + * @param y1 + * @param x2 + * @param y2 + * @param picture + */ +void st7796_spi_draw_picture_nonblocking(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, st7796_spi_color_t *picture) +{ + size_t picture_size = (x2 - x1 + 1) * (y2 - y1 + 1); + + st7796_spi_set_draw_window(x1, y1, x2, y2); + + if (lcd_auto_swap_flag) { + lcd_swap_color_data16(picture, picture, picture_size); + } + + ST7796_SPI_CS_LOW; + device_control(st7796_spi, DEVICE_CTRL_TX_DMA_RESUME, NULL); + device_write(st7796_spi, 0, picture, (picture_size << 1)); +} + +/** + * @brief st7796_draw_picture,Blocking,Using DMA acceleration,Waiting for the draw end + * + * @param x1 + * @param y1 + * @param x2 + * @param y2 + * @param picture + */ +void st7796_spi_draw_picture_blocking(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, st7796_spi_color_t *picture) +{ + st7796_spi_draw_picture_nonblocking(x1, y1, x2, y2, picture); + while (st7796_spi_draw_is_busy()) { + BL_DRV_DUMMY; + BL_DRV_DUMMY; + }; +} diff --git a/bsp/bsp_common/mcu_lcd/st7796.h b/bsp/bsp_common/mcu_lcd/st7796.h new file mode 100644 index 00000000..a5e14360 --- /dev/null +++ b/bsp/bsp_common/mcu_lcd/st7796.h @@ -0,0 +1,62 @@ +/** + * @file st7796.h + * @brief + * + * Copyright (c) 2021 Bouffalolab team + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ + +#ifndef _ST7796_SPI_H_ +#define _ST7796_SPI_H_ + +#include "bflb_platform.h" + +#define ST7796_SPI_CS_PIN GPIO_PIN_18 +#define ST7796_SPI_DC_PIN GPIO_PIN_20 + +#define ST7796_SPI_CS_HIGH gpio_write(ST7796_SPI_CS_PIN, 1) +#define ST7796_SPI_CS_LOW gpio_write(ST7796_SPI_CS_PIN, 0) +#define ST7796_SPI_DC_HIGH gpio_write(ST7796_SPI_DC_PIN, 1) +#define ST7796_SPI_DC_LOW gpio_write(ST7796_SPI_DC_PIN, 0) + +#define ST7796_SPI_W 320 /* ST7796 LCD width */ +#define ST7796_SPI_H 480 /* ST7796 LCD height */ + +#define ST7796_SPI_OFFSET_X 0 +#define ST7796_SPI_OFFSET_Y 0 + +#define ST7796_SPI_COLOR_DEPTH 16 + +typedef struct { + uint8_t cmd; /* 0xFF : delay(databytes)ms */ + const char *data; + uint8_t databytes; /* Num of data in data; or delay time */ +} st7796_spi_init_cmd_t; + +typedef uint16_t st7796_spi_color_t; + +int st7796_spi_init(); +int st7796_spi_set_dir(uint8_t dir); +void st7796_spi_set_draw_window(uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2); +void st7796_spi_draw_point(uint16_t x, uint16_t y, st7796_spi_color_t color); +void st7796_spi_draw_area(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, st7796_spi_color_t color); +void st7796_spi_draw_picture_nonblocking(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, st7796_spi_color_t *picture); +void st7796_spi_draw_picture_blocking(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, st7796_spi_color_t *picture); +int st7796_spi_draw_is_busy(void); + +#endif \ No newline at end of file diff --git a/examples/lvgl/CMakeLists.txt b/examples/lvgl/CMakeLists.txt index 2fc0d39c..92e7c290 100644 --- a/examples/lvgl/CMakeLists.txt +++ b/examples/lvgl/CMakeLists.txt @@ -2,7 +2,7 @@ set(BSP_COMMON_DIR ${CMAKE_SOURCE_DIR}/bsp/bsp_common) set(TARGET_REQUIRED_LIBS lvgl) set(TARGET_REQUIRED_PRIVATE_INCLUDE ${BSP_COMMON_DIR}/mcu_lcd ${BSP_COMMON_DIR}/lvgl ${BSP_COMMON_DIR}/touch) file(GLOB_RECURSE sources "${CMAKE_CURRENT_SOURCE_DIR}/demo/*.c") -set(TARGET_REQUIRED_SRCS ${sources} ${BSP_COMMON_DIR}/mcu_lcd/mcu_lcd.c ${BSP_COMMON_DIR}/mcu_lcd/ili9341.c ${BSP_COMMON_DIR}/mcu_lcd/st7735s.c ${BSP_COMMON_DIR}/mcu_lcd/st7789v.c ${BSP_COMMON_DIR}/mcu_lcd/font.c +set(TARGET_REQUIRED_SRCS ${sources} ${BSP_COMMON_DIR}/mcu_lcd/mcu_lcd.c ${BSP_COMMON_DIR}/mcu_lcd/ili9341.c ${BSP_COMMON_DIR}/mcu_lcd/st7735s.c ${BSP_COMMON_DIR}/mcu_lcd/st7789v.c ${BSP_COMMON_DIR}/mcu_lcd/st7796.c ${BSP_COMMON_DIR}/mcu_lcd/font.c ${BSP_COMMON_DIR}/lvgl/lv_port_disp.c ${BSP_COMMON_DIR}/lvgl/lv_port_indev.c ${BSP_COMMON_DIR}/touch/xpt2046.c ${BSP_COMMON_DIR}/touch/touch.c ) list(APPEND GLOBAL_C_FLAGS -DLV_USING_FATFS=0 -DLV_USING_ROMFS=0) diff --git a/examples/lvgl/main.c b/examples/lvgl/main.c index 2d0ac059..614e0f53 100644 --- a/examples/lvgl/main.c +++ b/examples/lvgl/main.c @@ -80,6 +80,7 @@ int main(void) lv_demo_benchmark(); + lv_task_handler(); BL_CASE_SUCCESS; while (1) {