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

  

posted @ 2025-07-21 13:31  panda_w  阅读(63)  评论(0)    收藏  举报