[开发工具] STM32CubeMX_17 | 使用硬件SPI驱动TFT-LCD(ST7789)

[复制链接]
6584|46
 楼主| programmable 发表于 2020-4-12 18:58 | 显示全部楼层
3.2. 宏定义屏幕分辨率和颜色值

  1. //LCD屏幕分辨率定义
  2. #define LCD_Width   240
  3. #define LCD_Height  240
  4. //颜色定义
  5. #define WHITE   0xFFFF    //白色
  6. #define YELLOW  0xFFE0    //黄色
  7. #define BRRED   0XFC07  //棕红色
  8. #define PINK    0XF81F    //粉色
  9. #define RED     0xF800    //红色
  10. #define BROWN   0XBC40  //棕色
  11. #define GRAY    0X8430  //灰色
  12. #define GBLUE   0X07FF    //兰色
  13. #define GREEN   0x07E0    //绿色
  14. #define BLUE    0x001F  //蓝色
  15. #define BLACK   0x0000    //黑色
 楼主| programmable 发表于 2020-4-12 19:00 | 显示全部楼层
接下来开始在 lcd_spi2_drv.c 编写驱动程序~
 楼主| programmable 发表于 2020-4-12 19:00 | 显示全部楼层
3.3. 封装LCD控制引脚初始化函数
首先包含必要的头文件:
  1. #include "lcd_spi2_drv.h"
  2. #include "gpio.h"
  3. #include "spi.h"
 楼主| programmable 发表于 2020-4-12 19:01 | 显示全部楼层
这个函数只能在本文件内由LCD初始化函数调用,所以使用static修饰为静态的:
  1. /**
  2. *[url=home.php?mod=space&uid=247401]@brief[/url]    LCD控制引脚和通信接口初始化
  3. *@param    none
  4. *@retval   none
  5. */
  6. static void LCD_GPIO_Init(void)
  7. {
  8.     /* 初始化引脚 */
  9.     MX_GPIO_Init();

  10.     /* 复位LCD */
  11.     LCD_PWR(0);
  12.     LCD_RST(0);
  13.     HAL_Delay(100);
  14.     LCD_RST(1);

  15.     /* 初始化SPI2接口 */
  16.     MX_SPI2_Init();
  17. }
 楼主| programmable 发表于 2020-4-12 19:01 | 显示全部楼层
3.4. 封装LCD发送数据和发送命令函数
数据都是由 SPI2 的MOSI发送,由 LCD_WR_RS 引脚指明该数据是命令还是数据。

首先在 spi.c 的最后调用HAL库封装一个函数,供驱动程序调用:
  1. /* USER CODE BEGIN 1 */
  2. /**
  3. * @brief    SPI 发送字节函数
  4. * @param    TxData    要发送的数据
  5. * @param    size    发送数据的字节大小
  6. * [url=home.php?mod=space&uid=266161]@return[/url]  0:写入成功,其他:写入失败
  7. */
  8. uint8_t SPI_WriteByte(uint8_t *TxData,uint16_t size)
  9. {
  10.     return HAL_SPI_Transmit(&hspi2,TxData,size,1000);
  11. }
  12. /* USER CODE END 1 */
 楼主| programmable 发表于 2020-4-12 19:02 | 显示全部楼层
不要忘了在spi.h中声明该函数!
 楼主| programmable 发表于 2020-4-12 19:03 | 显示全部楼层
然后基于spi发送字节函数,在驱动文件中继续封装一个向LCD发送数据的函数,一个向LCD发送命令的函数:

  1. /**
  2. * @brief   写命令到LCD
  3. * @param   cmd —— 需要发送的命令
  4. * @return  none
  5. */
  6. static void LCD_Write_Cmd(uint8_t cmd)
  7. {
  8.     LCD_WR_RS(0);
  9.     SPI_WriteByte(&cmd, 1);
  10. }

  11. /**
  12. * @brief   写数据到LCD
  13. * @param   dat —— 需要发送的数据
  14. * @return  none
  15. */
  16. static void LCD_Write_Data(uint8_t dat)
  17. {
  18.     LCD_WR_RS(1);
  19.     SPI_WriteByte(&dat, 1);
  20. }
 楼主| programmable 发表于 2020-4-12 19:03 | 显示全部楼层
4. 编写LCD驱动(ST7789)—— 对照datasheet编程
4.1. 打开/关闭背光函数
这两个函数比较简单,直接调用控制LCD背光的引脚控制宏即可:
  1. /**
  2. * @breif   打开LCD显示背光
  3. * @param   none
  4. * @return  none
  5. */
  6. void LCD_DisplayOn(void)
  7. {
  8.     LCD_PWR(1);
  9. }
  10. /**
  11. * @brief   关闭LCD显示背光
  12. * @param   none
  13. * @return  none
  14. */
  15. void LCD_DisplayOff(void)
  16. {
  17.     LCD_PWR(0);
  18. }
 楼主| programmable 发表于 2020-4-12 19:04 | 显示全部楼层
4.2. 指定显示RAM操作地址
根据数据手册,当要改变某个区域像素点的颜色时,首先应该确定X方向起始地址和X方向结束地址:
596285e92f5945db47.png
 楼主| programmable 发表于 2020-4-12 19:04 | 显示全部楼层
然后确定Y方向起始地址和Y方向结束地址:

964665e92f5b803c12.png
 楼主| programmable 发表于 2020-4-12 19:05 | 显示全部楼层
最后再确定该区域内每个像素点的值(16bit):

693215e92f5d869e95.png
 楼主| programmable 发表于 2020-4-12 19:07 | 显示全部楼层
综上,我们每次操作的时候都需要指定操作区域,所以编写该函数:

  1. /**
  2. * @brief   设置数据写入LCD显存区域
  3. * @param   x1,y1    —— 起点坐标
  4. * @param   x2,y2    —— 终点坐标
  5. * @return  none
  6. */
  7. void LCD_Address_Set(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
  8. {
  9.     /* 指定X方向操作区域 */
  10.     LCD_Write_Cmd(0x2a);
  11.     LCD_Write_Data(x1 >> 8);
  12.     LCD_Write_Data(x1);
  13.     LCD_Write_Data(x2 >> 8);
  14.     LCD_Write_Data(x2);

  15.     /* 指定Y方向操作区域 */
  16.     LCD_Write_Cmd(0x2b);
  17.     LCD_Write_Data(y1 >> 8);
  18.     LCD_Write_Data(y1);
  19.     LCD_Write_Data(y2 >> 8);
  20.     LCD_Write_Data(y2);

  21.     /* 发送该命令,LCD开始等待接收显存数据 */
  22.     LCD_Write_Cmd(0x2C);
  23. }
 楼主| programmable 发表于 2020-4-12 19:07 | 显示全部楼层
4.3. 清屏函数
编写完指定显存操作区域后,趁热打铁,编写清屏函数就很简单啦,直接调用上面编写的函数,指定操作地址为全屏幕,然后循环发送颜色值即可:

#define LCD_TOTAL_BUF_SIZE    (240*240*2)
#define LCD_Buf_Size 1152
static uint8_t lcd_buf[LCD_Buf_Size];
/**
* @brief   以一种颜色清空LCD屏
* @param   color —— 清屏颜色(16bit)
* @return  none
*/
void LCD_Clear(uint16_t color)
{
    uint16_t i, j;
    uint8_t data[2] = {0};  //color是16bit的,每个像素点需要两个字节的显存

    /* 将16bit的color值分开为两个单独的字节 */
    data[0] = color >> 8;
    data[1] = color;

    /* 显存的值需要逐字节写入 */
    for(j = 0; j < LCD_Buf_Size / 2; j++)
    {
        lcd_buf[j * 2] =  data[0];
        lcd_buf[j * 2 + 1] =  data[1];
    }
    /* 指定显存操作地址为全屏幕 */
    LCD_Address_Set(0, 0, LCD_Width - 1, LCD_Height - 1);
    /* 指定接下来的数据为数据 */
    LCD_WR_RS(1);
    /* 将显存缓冲区的数据全部写入缓冲区 */
    for(i = 0; i < (LCD_TOTAL_BUF_SIZE / LCD_Buf_Size); i++)
    {
        SPI_WriteByte(lcd_buf, (uint16_t)LCD_Buf_Size);
    }
}
 楼主| programmable 发表于 2020-4-12 19:08 | 显示全部楼层
4.4. LCD初始化函数
至此,LCD的一些操作函数全部编写完成,最后编写初始化LCD模式的函数:

  1. /**
  2. * @brief   LCD初始化
  3. * @param   none
  4. * @return  none
  5. */
  6. void LCD_Init(void)
  7. {
  8.     /* 初始化和LCD通信的引脚 */
  9.     LCD_GPIO_Init();
  10.     HAL_Delay(120);

  11.     /* 关闭睡眠模式 */
  12.     LCD_Write_Cmd(0x11);
  13.     HAL_Delay(120);

  14.     /* 开始设置显存扫描模式,数据格式等 */
  15.     LCD_Write_Cmd(0x36);
  16.     LCD_Write_Data(0x00);
  17.     /* RGB 5-6-5-bit格式  */
  18.     LCD_Write_Cmd(0x3A);
  19.     LCD_Write_Data(0x65);
  20.     /* porch 设置 */
  21.     LCD_Write_Cmd(0xB2);
  22.     LCD_Write_Data(0x0C);
  23.     LCD_Write_Data(0x0C);
  24.     LCD_Write_Data(0x00);
  25.     LCD_Write_Data(0x33);
  26.     LCD_Write_Data(0x33);
  27.     /* VGH设置 */
  28.     LCD_Write_Cmd(0xB7);
  29.     LCD_Write_Data(0x72);
  30.     /* VCOM 设置 */
  31.     LCD_Write_Cmd(0xBB);
  32.     LCD_Write_Data(0x3D);
  33.     /* LCM 设置 */
  34.     LCD_Write_Cmd(0xC0);
  35.     LCD_Write_Data(0x2C);
  36.     /* VDV and VRH 设置 */
  37.     LCD_Write_Cmd(0xC2);
  38.     LCD_Write_Data(0x01);
  39.     /* VRH 设置 */
  40.     LCD_Write_Cmd(0xC3);
  41.     LCD_Write_Data(0x19);
  42.     /* VDV 设置 */
  43.     LCD_Write_Cmd(0xC4);
  44.     LCD_Write_Data(0x20);
  45.     /* 普通模式下显存速率设置 60Mhz */
  46.     LCD_Write_Cmd(0xC6);
  47.     LCD_Write_Data(0x0F);
  48.     /* 电源控制 */
  49.     LCD_Write_Cmd(0xD0);
  50.     LCD_Write_Data(0xA4);
  51.     LCD_Write_Data(0xA1);
  52.     /* 电压设置 */
  53.     LCD_Write_Cmd(0xE0);
  54.     LCD_Write_Data(0xD0);
  55.     LCD_Write_Data(0x04);
  56.     LCD_Write_Data(0x0D);
  57.     LCD_Write_Data(0x11);
  58.     LCD_Write_Data(0x13);
  59.     LCD_Write_Data(0x2B);
  60.     LCD_Write_Data(0x3F);
  61.     LCD_Write_Data(0x54);
  62.     LCD_Write_Data(0x4C);
  63.     LCD_Write_Data(0x18);
  64.     LCD_Write_Data(0x0D);
  65.     LCD_Write_Data(0x0B);
  66.     LCD_Write_Data(0x1F);
  67.     LCD_Write_Data(0x23);
  68.     /* 电压设置 */
  69.     LCD_Write_Cmd(0xE1);
  70.     LCD_Write_Data(0xD0);
  71.     LCD_Write_Data(0x04);
  72.     LCD_Write_Data(0x0C);
  73.     LCD_Write_Data(0x11);
  74.     LCD_Write_Data(0x13);
  75.     LCD_Write_Data(0x2C);
  76.     LCD_Write_Data(0x3F);
  77.     LCD_Write_Data(0x44);
  78.     LCD_Write_Data(0x51);
  79.     LCD_Write_Data(0x2F);
  80.     LCD_Write_Data(0x1F);
  81.     LCD_Write_Data(0x1F);
  82.     LCD_Write_Data(0x20);
  83.     LCD_Write_Data(0x23);
  84.     /* 显示开 */
  85.     LCD_Write_Cmd(0x21);
  86.     LCD_Write_Cmd(0x29);

  87.     /* 清屏为白色 */
  88.     LCD_Clear(WHITE);

  89.     /*打开显示*/
  90.     LCD_PWR(1);
  91. }
 楼主| programmable 发表于 2020-4-12 19:09 | 显示全部楼层
至此,驱动编写完成。
 楼主| programmable 发表于 2020-4-12 19:10 | 显示全部楼层
5. 测试驱动程序
在 main函数 中编写驱动测试代码,在 while(1) 之前添加如下代码:

  1. /* USER CODE BEGIN 2 */
  2.   LCD_Init();
  3.   LCD_Clear(GREEN);
  4.   /* USER CODE END 2 */
 楼主| programmable 发表于 2020-4-12 19:12 | 显示全部楼层
测试结果如图:

786335e92f7847308c.png
 楼主| programmable 发表于 2020-4-12 19:12 | 显示全部楼层
绿绿的,是不是很好看哈哈(斜眼笑.jpg)~

至此,我们已经学会如何使用硬件SPI驱动LCD屏幕(ST7789),下一节将讲述如何使用硬件QSPI接口读写SPI Flash的数据。
xiaoqizi 发表于 2020-5-7 14:30 | 显示全部楼层
非常感谢楼主分享
磨砂 发表于 2020-5-7 14:30 | 显示全部楼层
不错的资料
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 在线客服 返回列表 返回顶部