Advertisement

STM32 CUBE SPI+DMA+LVGL驱动ST7789 LCD屏

阅读量:

SPI驱动ST7789屏的相关资料较多,相比之下较少使用DMA驱动方式。在尝试通过LVGL库和SPI进行数据传输时,由于屏幕刷新效果较为缓慢,在更换为DMA驱动后问题有所改善。然而,按照网上的部分配置方法实施后,发现持续出现屏幕显示颜色不准确的问题。经过一番努力排查后,找到了有效的解决方案,并对此进行了详细记录。

引脚参数设置

LCD_BL驱动背光,高电压水平起作用,低电压水平导致屏幕黑色显示。

SPI配置

半双工模式:接收发送一条数据线,与屏幕的半双工接口配合使用。

硬件上控制CS的话,另外一种方式也可以通过软件实现,具体采用哪种则取决于个人的偏好。

最大波特率设为60M以下,并以达到更快刷新速度为目标,追求最高水平。

可以选择的数据位数包括8和16,具体原因将在后续内容中阐述(大坑)。

DMA数据采用16位精度存储或处理,并等同于用半字节的长度表示数据块。

这些文件被用来...

st7789.c

复制代码
 #include "st7789.h"

    
 #include "spi.h"
    
  
    
 // ST7789命令定义
    
 #define ST7789_NOP     0x00
    
 #define ST7789_SWRESET 0x01
    
 #define ST7789_SLPIN   0x10
    
 #define ST7789_SLPOUT  0x11
    
 #define ST7789_INVOFF  0x20
    
 #define ST7789_INVON   0x21
    
 #define ST7789_DISPOFF 0x28
    
 #define ST7789_DISPON  0x29
    
 #define ST7789_CASET   0x2A
    
 #define ST7789_RASET   0x2B
    
 #define ST7789_RAMWR   0x2C
    
 #define ST7789_MADCTL  0x36
    
  
    
 // MADCTL参数
    
 #define ST7789_MADCTL_MY  0x80
    
 #define ST7789_MADCTL_MX  0x40
    
 #define ST7789_MADCTL_MV  0x20
    
 #define ST7789_MADCTL_ML  0x10
    
 #define ST7789_MADCTL_MH  0x04
    
 #define ST7789_MADCTL_RGB 0x00
    
 #define ST7789_MADCTL_BGR 0x08
    
  
    
 #define ST7789_SPI_INSTANCE  hspi1
    
    
    
 #define SPI_DC_LOW()        HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_RESET)
    
 #define SPI_DC_HIGH()       HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_SET)
    
 #define SPI_RST_LOW()       HAL_GPIO_WritePin(LCD_RES_GPIO_Port, LCD_RES_Pin, GPIO_PIN_RESET);
    
 #define SPI_RST_HIGH()      HAL_GPIO_WritePin(LCD_RES_GPIO_Port, LCD_RES_Pin, GPIO_PIN_SET);
    
 //#define SPI_WriteByte(cmd)  HAL_SPI_Transmit(&ST7789_SPI_INSTANCE, &cmd, 1, HAL_MAX_DELAY)
    
 #define SPI_WriteByte(cmd)  HAL_SPI_Transmit_DMA(&ST7789_SPI_INSTANCE, &cmd, 1)
    
  
    
 // 设置SPI数据大小为8位
    
 void SPI_SetDataSize_8bit(SPI_HandleTypeDef *hspi) {
    
     hspi->Init.DataSize = SPI_DATASIZE_8BIT;
    
     if (HAL_SPI_Init(hspi) != HAL_OK) {
    
     // 初始化失败处理
    
     }
    
 }
    
  
    
 // 设置SPI数据大小为16位
    
 void SPI_SetDataSize_16bit(SPI_HandleTypeDef *hspi) {
    
     hspi->Init.DataSize = SPI_DATASIZE_16BIT;
    
     if (HAL_SPI_Init(hspi) != HAL_OK) {
    
     // 初始化失败处理
    
     }
    
 }
    
  
    
 // 向ST7789发送命令
    
 void ST7789_SendCommand(uint8_t cmd) {
    
     SPI_SetDataSize_8bit(&ST7789_SPI_INSTANCE);
    
  
    
     SPI_DC_LOW();
    
     HAL_SPI_Transmit(&ST7789_SPI_INSTANCE, &cmd , 1, HAL_MAX_DELAY);
    
  
    
 }
    
  
    
 // 向ST7789发送命令
    
 void ST7789_SendData(uint8_t cmd) {
    
     SPI_SetDataSize_8bit(&ST7789_SPI_INSTANCE);
    
  
    
     SPI_DC_HIGH();
    
     HAL_SPI_Transmit(&ST7789_SPI_INSTANCE, &cmd , 1, HAL_MAX_DELAY);
    
  
    
 }
    
  
    
 // 向ST7789发送单个16位数据
    
 void ST7789_SendData_16bit(uint16_t data) {
    
     SPI_SetDataSize_16bit(&ST7789_SPI_INSTANCE);
    
     
    
     SPI_DC_HIGH();
    
     HAL_SPI_Transmit_DMA(&ST7789_SPI_INSTANCE, (uint8_t *)&data, 1);
    
     while (HAL_SPI_GetState(&ST7789_SPI_INSTANCE) != HAL_SPI_STATE_READY);
    
 }
    
  
    
 // 向ST7789发送多个16位数据(使用DMA)
    
 void ST7789_SendData_DMA_16bit(const uint16_t *pData, uint16_t Size) {
    
     SPI_SetDataSize_16bit(&ST7789_SPI_INSTANCE);
    
     SPI_DC_HIGH();
    
     HAL_SPI_Transmit_DMA(&ST7789_SPI_INSTANCE, (uint8_t *)pData, Size);
    
     while (HAL_SPI_GetState(&ST7789_SPI_INSTANCE) != HAL_SPI_STATE_READY);
    
  
    
 }
    
  
    
 void setRotation(uint8_t r)
    
 {
    
     uint8_t rotation = r % 4;
    
     ST7789_SendCommand(ST7789_MADCTL);
    
     switch(rotation)
    
     {
    
     case 0:
    
     ST7789_SendData(0x00);
    
     break;
    
  
    
     case 1:
    
     ST7789_SendData(0xC0);
    
     break;
    
  
    
     case 2:
    
     ST7789_SendData(0x70);
    
     break;
    
  
    
     case 3:
    
     ST7789_SendData(0xA0);
    
     break;
    
     }
    
 }
    
  
    
 // 初始化ST7789
    
 void ST7789_Init(void) {
    
     HAL_GPIO_WritePin(GPIOA, LCD_BL_Pin, GPIO_PIN_SET);
    
     SPI_CS_HIGH(); // 拉高CS
    
  
    
     SPI_RST_LOW();
    
     HAL_Delay(100);
    
     SPI_RST_HIGH();
    
     HAL_Delay(100);
    
   
    
     ST7789_SendCommand(ST7789_SLPOUT); //Sleep out
    
     HAL_Delay(120);
    
  
    
     // 设置颜色模式
    
     setRotation(0);
    
   
    
     ST7789_SendCommand(0x3A);
    
     ST7789_SendData(0x05);
    
  
    
 #if SCREEN_USE_LITTLE_ENDIAN
    
     /* Change to Little Endian */
    
     ST7789_SendCommand(0xB0);
    
     ST7789_SendData(0x00);  // RM = 0; DM = 00
    
     ST7789_SendData(0xF8);  // EPF = 11; ENDIAN = 1; RIM = 0; MDT = 00 (ENDIAN -> 0 MSBFirst; 1 LSB First)
    
 #endif
    
  
    
     ST7789_SendCommand(0xB2);
    
     ST7789_SendData(0x0C);
    
     ST7789_SendData(0x0C);
    
     ST7789_SendData(0x00);
    
     ST7789_SendData(0x33);
    
     ST7789_SendData(0x33);
    
  
    
     ST7789_SendCommand(0xB7);
    
     ST7789_SendData(0x35);
    
  
    
     ST7789_SendCommand(0xBB);
    
     ST7789_SendData(0x32); //Vcom=1.35V
    
  
    
     ST7789_SendCommand(0xC2);
    
     ST7789_SendData(0x01);
    
  
    
     ST7789_SendCommand(0xC3);
    
     ST7789_SendData(0x15); //GVDD=4.8V
    
  
    
     ST7789_SendCommand(0xC4);
    
     ST7789_SendData(0x20); //VDV, 0x20:0v
    
  
    
     ST7789_SendCommand(0xC6);
    
     ST7789_SendData(0x0F); //0x0F:60Hz
    
  
    
     ST7789_SendCommand(0xD0);
    
     ST7789_SendData(0xA4);
    
     ST7789_SendData(0xA1);
    
  
    
     ST7789_SendCommand(0xE0);
    
     ST7789_SendData(0xD0);
    
     ST7789_SendData(0x08);
    
     ST7789_SendData(0x0E);
    
     ST7789_SendData(0x09);
    
     ST7789_SendData(0x09);
    
     ST7789_SendData(0x05);
    
     ST7789_SendData(0x31);
    
     ST7789_SendData(0x33);
    
     ST7789_SendData(0x48);
    
     ST7789_SendData(0x17);
    
     ST7789_SendData(0x14);
    
     ST7789_SendData(0x15);
    
     ST7789_SendData(0x31);
    
     ST7789_SendData(0x34);
    
  
    
     ST7789_SendCommand(0xE1);
    
     ST7789_SendData(0xD0);
    
     ST7789_SendData(0x08);
    
     ST7789_SendData(0x0E);
    
     ST7789_SendData(0x09);
    
     ST7789_SendData(0x09);
    
     ST7789_SendData(0x15);
    
     ST7789_SendData(0x31);
    
     ST7789_SendData(0x33);
    
     ST7789_SendData(0x48);
    
     ST7789_SendData(0x17);
    
     ST7789_SendData(0x14);
    
     ST7789_SendData(0x15);
    
     ST7789_SendData(0x31);
    
     ST7789_SendData(0x34);
    
     ST7789_SendCommand(ST7789_INVON);
    
  
    
     ST7789_SendCommand(ST7789_DISPON);
    
 }
    
  
    
 // 设置显示窗口
    
 void ST7789_SetWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) {
    
     ST7789_SendCommand(ST7789_CASET);
    
     ST7789_SendData_16bit(x0);
    
     ST7789_SendData_16bit(x1);
    
  
    
     ST7789_SendCommand(ST7789_RASET);
    
     ST7789_SendData_16bit(y0);
    
     ST7789_SendData_16bit(y1);
    
  
    
     ST7789_SendCommand(ST7789_RAMWR);
    
 }
    
  
    
 // 填充屏幕
    
 void ST7789_FillScreen(uint16_t color) {
    
     ST7789_SetWindow(0, 0, ST7789_WIDTH - 1, ST7789_HEIGHT - 1);
    
     for (uint32_t i = 0; i < ST7789_WIDTH * ST7789_HEIGHT; i++) {
    
     ST7789_SendData_16bit(color);
    
     }
    
 }
    
  
    
 // 绘制像素
    
 void ST7789_DrawPixel(uint16_t x, uint16_t y, uint16_t color) {
    
     if (x >= ST7789_WIDTH || y >= ST7789_HEIGHT) return;
    
     ST7789_SetWindow(x, y, x, y);
    
     ST7789_SendData_16bit(color);
    
 }
    
  
    
 // 绘制矩形
    
 void ST7789_DrawRect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color) {
    
     for (uint16_t i = x; i < x + w; i++) {
    
     for (uint16_t j = y; j < y + h; j++) {
    
         ST7789_DrawPixel(i, j, color);
    
     }
    
     }
    
 }
    
  
    
 // 绘制图像
    
 void ST7789_DrawImage(uint16_t x, uint16_t y, uint16_t w, uint16_t h, const uint16_t *data) {
    
     ST7789_SetWindow(x, y, x + w - 1, y + h - 1);
    
     ST7789_SendData_DMA_16bit(data,(w*h));
    
 }
    
  
    
 // 绘制字符
    
 void ST7789_DrawChar(uint16_t x, uint16_t y, char ch, FontDef font, uint16_t color, uint16_t bgcolor) {
    
     uint32_t i, b, j;
    
     for (i = 0; i < font.height; i++) {
    
     b = font.data[(ch - 32) * font.height + i];
    
     for (j = 0; j < font.width; j++) {
    
         if ((b << j) & 0x8000) {
    
             ST7789_DrawPixel(x + j, y + i, color);
    
         } else {
    
             ST7789_DrawPixel(x + j, y + i, bgcolor);
    
         }
    
     }
    
     }
    
 }
    
  
    
 // 绘制字符串
    
 void ST7789_DrawString(uint16_t x, uint16_t y, const char *str, FontDef font, uint16_t color, uint16_t bgcolor) {
    
     while (*str) {
    
     ST7789_DrawChar(x, y, *str, font, color, bgcolor);
    
     x += font.width;
    
     str++;
    
     }
    
 }

software test 7789.h

复制代码
 #ifndef ST7789_H

    
 #define ST7789_H
    
  
    
 #include <stdint.h>
    
 #include <stdbool.h>
    
 #include "fonts.h" // 字体库
    
  
    
 // 定义屏幕尺寸
    
 #define ST7789_WIDTH  240
    
 #define ST7789_HEIGHT 320
    
  
    
 // 定义颜色格式
    
 #define ST7789_COLOR(r, g, b) (((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3))
    
  
    
 // 常用颜色
    
 #define ST7789_WHITE   ST7789_COLOR(255, 255, 255)
    
 #define ST7789_BLACK   ST7789_COLOR(0, 0, 0)
    
 #define ST7789_RED     ST7789_COLOR(255, 0, 0)
    
 #define ST7789_GREEN   ST7789_COLOR(0, 255, 0)
    
 #define ST7789_BLUE    ST7789_COLOR(0, 0, 255)
    
 #define ST7789_YELLOW  ST7789_COLOR(255, 255, 0)
    
 #define ST7789_CYAN    ST7789_COLOR(0, 255, 255)
    
 #define ST7789_MAGENTA ST7789_COLOR(255, 0, 255)
    
  
    
 // 初始化函数
    
 void ST7789_Init(void);
    
  
    
 // 设置显示区域
    
 void ST7789_SetWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
    
  
    
 // 填充屏幕
    
 void ST7789_FillScreen(uint16_t color);
    
  
    
 // 绘制像素
    
 void ST7789_DrawPixel(uint16_t x, uint16_t y, uint16_t color);
    
  
    
 // 绘制矩形
    
 void ST7789_DrawRect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color);
    
  
    
 // 绘制图像
    
 void ST7789_DrawImage(uint16_t x, uint16_t y, uint16_t w, uint16_t h, const uint16_t *data);
    
  
    
 // 绘制字符
    
 void ST7789_DrawChar(uint16_t x, uint16_t y, char ch, FontDef font, uint16_t color, uint16_t bgcolor);
    
  
    
 // 绘制字符串
    
 void ST7789_DrawString(uint16_t x, uint16_t y, const char *str, FontDef font, uint16_t color, uint16_t bgcolor);
    
  
    
 #endif // ST7789_H

字体文件就不放了,网上很多。

该文档旨在介绍其核心资源库的构建与应用框架

1. 数据信息的精度与分辨率由其数值信息的位宽决定

通过查看这段代码可以发现关键在于,在配置LCD寄存器并使用DMA传输像素数据的过程中,将SPI的数据总线宽度进行了调整。写寄存器时为8位,而DMA传输像素数据改成了16位。为什么要这样做呢?

主要原因是LCD控制器仅能处理8位操作指令。当尝试发送16位指令时,补充前导零至0x00后,该控制器无法识别此修改后的指令序列,因此显示屏未能点亮。

而LVGL的输出数据为16位精度,在采用DMA直接传输8位时会产生高低位翻转问题,从而导致显色不准确的现象。为了在传输过程中避免这种问题的发生,建议在数据前通过临时缓冲数组进行端序调整处理,这样既可有效防止因数据顺序颠倒而导致的显示异常现象发生,又可在一定程度上减少对系统资源和计算能力的占用。然而,在实际操作中若一味追求传输效率,则需要付出更多的内存占用和计算时间代价,最终导致整体传输速率明显降低。

2.SPI信号线的下降幅度

在初步对ST7789屏进行测试过程中,观察到屏控逻辑处于低电平状态,无法正常点亮。然而,在接通通信线后,屏控逻辑恢复至高电平状态,能够实现正常点亮。经分析判断,问题可能与屏线电阻有关,并采取措施将通信线的供电端接地,最终消除了故障

GPIOPinConfig.PULL = GPIO Pin Down;

复制代码
     /**SPI1 GPIO Configuration

    
     PA4     ------> SPI1_NSS
    
     PA5     ------> SPI1_SCK
    
     PA7     ------> SPI1_MOSI
    
     */
    
     GPIO_InitStruct.Pin = GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_7;
    
     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    
     GPIO_InitStruct.Pull = GPIO_PULLDOWN;
    
     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    
     GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
    
     HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

该部分的性能表现显著。

采用DMA技术进行图像数据传输后,LVGL能够以每秒53毫秒的速度完成整个屏幕的刷新过程

通过DMA机制传输图像信息是没有间隙的流程,最大传输速率达到48M,这一数值受制于逻辑分析仪的精度限制,实际测量值可能达到50M。

配置步骤所用时间为278微秒,采用基于标准SPI的通信方式,在此过程中存在较多的闲置时间。

系统将配置寄存器设定为采用8位编码模式。

全部评论 (0)

还没有任何评论哟~