nordic
# Nordic nRF52840 SPI DMA驱动ST7789v2移植LVGL
下面是为nRF52840平台实现的SPI DMA驱动ST7789v2显示屏并移植LVGL的完整方案。该方案使用nRF5 SDK和Zephyr RTOS两种方式实现。
## 方案一:基于nRF5 SDK的实现
```c
#include "nrfx_spim.h"
#include "nrf_gpio.h"
#include "lvgl.h"
#include "app_timer.h"
// 屏幕尺寸
#define ST7789_WIDTH 240
#define ST7789_HEIGHT 320
// 引脚定义
#define LCD_CS_PIN NRF_GPIO_PIN_MAP(0, 5) // P0.05
#define LCD_DC_PIN NRF_GPIO_PIN_MAP(0, 6) // P0.06
#define LCD_RST_PIN NRF_GPIO_PIN_MAP(0, 7) // P0.07
#define LCD_BLK_PIN NRF_GPIO_PIN_MAP(0, 8) // P0.08
#define LCD_SCK_PIN NRF_GPIO_PIN_MAP(0, 13) // P0.13
#define LCD_MOSI_PIN NRF_GPIO_PIN_MAP(0, 15) // P0.15
// ST7789命令
#define ST7789_NOP 0x00
#define ST7789_SWRESET 0x01
#define ST7789_SLPOUT 0x11
#define ST7789_NORON 0x13
#define ST7789_INVOFF 0x20
#define ST7789_DISPON 0x29
#define ST7789_CASET 0x2A
#define ST7789_RASET 0x2B
#define ST7789_RAMWR 0x2C
#define ST7789_MADCTL 0x36
#define ST7789_COLMOD 0x3A
// SPI实例
static const nrfx_spim_t spim = NRFX_SPIM_INSTANCE(0);
// LVGL显示缓冲
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf1[ST7789_WIDTH * 20]; // 显示缓冲区1
static lv_color_t buf2[ST7789_WIDTH * 20]; // 显示缓冲区2
// DMA传输状态
static volatile bool spi_xfer_done = true;
/**
* @brief SPI事件处理函数
*/
static void spim_event_handler(nrfx_spim_evt_t const *p_event, void *p_context) {
if (p_event->type == NRFX_SPIM_EVENT_DONE) {
spi_xfer_done = true;
}
}
/**
* @brief 初始化SPIM外设
*/
static void spi_init(void) {
nrfx_spim_config_t spi_config = NRFX_SPIM_DEFAULT_CONFIG;
spi_config.sck_pin = LCD_SCK_PIN;
spi_config.mosi_pin = LCD_MOSI_PIN;
spi_config.miso_pin = NRFX_SPIM_PIN_NOT_USED;
spi_config.ss_pin = NRFX_SPIM_PIN_NOT_USED; // 手动控制CS
spi_config.frequency = NRF_SPIM_FREQ_8M;
spi_config.mode = NRF_SPIM_MODE_0;
spi_config.bit_order = NRF_SPIM_BIT_ORDER_MSB_FIRST;
APP_ERROR_CHECK(nrfx_spim_init(&spim, &spi_config, spim_event_handler, NULL));
}
/**
* @brief 发送命令到LCD
* @param cmd 命令字节
*/
static void lcd_write_cmd(uint8_t cmd) {
nrf_gpio_pin_clear(LCD_DC_PIN); // DC低: 命令模式
nrf_gpio_pin_clear(LCD_CS_PIN); // CS低
spi_xfer_done = false;
nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_SINGLE_XFER(&cmd, 1, NULL, 0);
APP_ERROR_CHECK(nrfx_spim_xfer(&spim, &xfer, 0));
while (!spi_xfer_done); // 等待传输完成
nrf_gpio_pin_set(LCD_CS_PIN); // CS高
}
/**
* @brief 发送数据到LCD
* @param data 数据指针
* @param len 数据长度
*/
static void lcd_write_data(uint8_t *data, size_t len) {
nrf_gpio_pin_set(LCD_DC_PIN); // DC高: 数据模式
nrf_gpio_pin_clear(LCD_CS_PIN); // CS低
spi_xfer_done = false;
nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_SINGLE_XFER(data, len, NULL, 0);
APP_ERROR_CHECK(nrfx_spim_xfer(&spim, &xfer, 0));
while (!spi_xfer_done); // 等待传输完成
nrf_gpio_pin_set(LCD_CS_PIN); // CS高
}
/**
* @brief 设置显示窗口
* @param x0 起始X坐标
* @param y0 起始Y坐标
* @param x1 结束X坐标
* @param y1 结束Y坐标
*/
static void lcd_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) {
uint8_t data[4];
// 设置列地址
lcd_write_cmd(ST7789_CASET);
data[0] = (x0 >> 8) & 0xFF;
data[1] = x0 & 0xFF;
data[2] = (x1 >> 8) & 0xFF;
data[3] = x1 & 0xFF;
lcd_write_data(data, 4);
// 设置行地址
lcd_write_cmd(ST7789_RASET);
data[0] = (y0 >> 8) & 0xFF;
data[1] = y0 & 0xFF;
data[2] = (y1 >> 8) & 0xFF;
data[3] = y1 & 0xFF;
lcd_write_data(data, 4);
}
/**
* @brief 初始化ST7789显示屏
*/
static void lcd_init(void) {
// 初始化GPIO
nrf_gpio_cfg_output(LCD_CS_PIN);
nrf_gpio_cfg_output(LCD_DC_PIN);
nrf_gpio_cfg_output(LCD_RST_PIN);
nrf_gpio_cfg_output(LCD_BLK_PIN);
// 复位显示屏
nrf_gpio_pin_clear(LCD_RST_PIN);
nrf_delay_ms(50);
nrf_gpio_pin_set(LCD_RST_PIN);
nrf_delay_ms(120);
// 退出睡眠模式
lcd_write_cmd(ST7789_SLPOUT);
nrf_delay_ms(120);
// 设置颜色模式为RGB565
lcd_write_cmd(ST7789_COLMOD);
uint8_t colmod_data = 0x55; // 16-bit/pixel
lcd_write_data(&colmod_data, 1);
// 设置内存访问控制
lcd_write_cmd(ST7789_MADCTL);
uint8_t madctl_data = 0x00; // 旋转设置
lcd_write_data(&madctl_data, 1);
// 设置列地址范围
lcd_set_window(0, 0, ST7789_WIDTH - 1, ST7789_HEIGHT - 1);
// 开启正常显示
lcd_write_cmd(ST7789_NORON);
nrf_delay_ms(10);
// 开启显示
lcd_write_cmd(ST7789_DISPON);
nrf_delay_ms(100);
// 开启背光
nrf_gpio_pin_set(LCD_BLK_PIN);
}
/**
* @brief LVGL刷新回调函数
*/
static void lcd_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) {
// 等待上一次传输完成
while (!spi_xfer_done);
spi_xfer_done = false;
// 设置显示窗口
lcd_set_window(area->x1, area->y1, area->x2, area->y2);
// 发送RAM写入命令
lcd_write_cmd(ST7789_RAMWR);
// 准备发送像素数据
nrf_gpio_pin_set(LCD_DC_PIN); // 数据模式
nrf_gpio_pin_clear(LCD_CS_PIN); // CS低
// 计算需要传输的数据量
size_t len = (area->x2 - area->x1 + 1) * (area->y2 - area->y1 + 1) * sizeof(lv_color_t);
// 启动SPI DMA传输
nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_SINGLE_XFER((uint8_t *)color_p, len, NULL, 0);
APP_ERROR_CHECK(nrfx_spim_xfer(&spim, &xfer, 0));
// 注意:传输完成后在事件处理函数中调用lv_disp_flush_ready
}
/**
* @brief SPI传输完成处理
*/
void spi_transfer_complete(void) {
nrf_gpio_pin_set(LCD_CS_PIN); // CS高
lv_disp_flush_ready(lv_disp_get_default());
}
/**
* @brief 初始化LVGL显示驱动
*/
static void lvgl_display_init(void) {
// 初始化显示缓冲区
lv_disp_draw_buf_init(&draw_buf, buf1, buf2, ST7789_WIDTH * 20);
// 注册显示驱动
lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
// 设置显示参数
disp_drv.hor_res = ST7789_WIDTH;
disp_drv.ver_res = ST7789_HEIGHT;
disp_drv.flush_cb = lcd_flush;
disp_drv.draw_buf = &draw_buf;
disp_drv.full_refresh = 0;
// 注册驱动
lv_disp_drv_register(&disp_drv);
}
/**
* @brief LVGL定时器处理
*/
static void lvgl_timer_handler(void *p_context) {
lv_timer_handler();
}
/**
* @brief 主函数
*/
int main(void) {
// 初始化GPIO
nrf_gpio_cfg_output(LCD_CS_PIN);
nrf_gpio_cfg_output(LCD_DC_PIN);
nrf_gpio_cfg_output(LCD_RST_PIN);
nrf_gpio_cfg_output(LCD_BLK_PIN);
// 初始状态
nrf_gpio_pin_set(LCD_CS_PIN);
nrf_gpio_pin_set(LCD_DC_PIN);
nrf_gpio_pin_set(LCD_RST_PIN);
nrf_gpio_pin_clear(LCD_BLK_PIN);
// 初始化SPI
spi_init();
// 初始化显示屏
lcd_init();
// 初始化LVGL
lv_init();
// 初始化LVGL显示驱动
lvgl_display_init();
// 创建简单界面
lv_obj_t *label = lv_label_create(lv_scr_act());
lv_label_set_text(label, "Hello nRF52840 & LVGL!");
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
// 创建LVGL定时器
APP_TIMER_DEF(lvgl_timer);
app_timer_create(&lvgl_timer, APP_TIMER_MODE_REPEA
Stay hungry, stay foolish
待续。。。

浙公网安备 33010602011771号