前言
1:驱动程序参考自https://blog.csdn.net/BearPi/article/details/104311705.:
2:这是我的一个记录,实现的功能不多,只是将在内存中开辟的一片显存通过DMA的方式搬运到屏幕上。
3:DMA搬运的时候,我没有做任何事情,只是给了一段延时等他做完。实际应用的时候应该设置信号量并让出CPU,等待传输完成后再开始下一帧的传输。
CUBEMX
一些GPIO,控制背光,复位,指令or数据
RCC全部拉满,不粘贴时钟树了。
下面是SPI
DMA
代码部分
ST7789.c
#include "ST7789.h"
#include "string.h"
#include "spi.h"
#include "stdio.h"
/**
*@brief LCD控制引脚和通信接口初始化
*@param none
*@retval none
*/
void LCD_reset(void)
{
/* 复位LCD */
LCD_PWR(0);
LCD_RST(0);
HAL_Delay(100);
LCD_RST(1);
}
/**
* @brief SPI 发送字节函数
* @param TxData 要发送的数据
* @param size 发送数据的字节大小
* @return 0:写入成功,其他:写入失败
*/
uint8_t SPI_WriteByte(uint8_t *TxData,uint16_t size)
{
return HAL_SPI_Transmit(&hspi1,TxData,size,1000);
}
/**
* @brief 写命令到LCD
* @param cmd —— 需要发送的命令
* @return none
*/
static void LCD_Write_Cmd(uint8_t cmd)
{
LCD_WR_RS(0);
SPI_WriteByte(&cmd, 1);
}
/**
* @brief 写数据到LCD
* @param dat —— 需要发送的数据
* @return none
*/
static void LCD_Write_Data(uint8_t dat)
{
LCD_WR_RS(1);
SPI_WriteByte(&dat, 1);
}
/**
* @breif 打开LCD显示背光
* @param none
* @return none
*/
void LCD_DisplayOn(void)
{
LCD_PWR(1);
}
/**
* @brief 关闭LCD显示背光
* @param none
* @return none
*/
void LCD_DisplayOff(void)
{
LCD_PWR(0);
}
/**
* @brief 设置数据写入LCD显存区域
* @param x1,y1 —— 起点坐标
* @param x2,y2 —— 终点坐标
* @return none
*/
void LCD_Address_Set(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
/* 指定X方向操作区域 */
LCD_Write_Cmd(0x2a);
LCD_Write_Data(x1 >> 8);
LCD_Write_Data(x1);
LCD_Write_Data(x2 >> 8);
LCD_Write_Data(x2);
/* 指定Y方向操作区域 */
LCD_Write_Cmd(0x2b);
LCD_Write_Data(y1 >> 8);
LCD_Write_Data(y1);
LCD_Write_Data(y2 >> 8);
LCD_Write_Data(y2);
/* 发送该命令,LCD开始等待接收显存数据 */
LCD_Write_Cmd(0x2C);
}
/**
* @brief 以一种颜色清空LCD屏
* @param color —— 清屏颜色(16bit)
* @return none
*/
void LCD_Fill(uint16_t color)
static uint8_t data[LCD_RAM_SIZE];
for(uint16_t j = 0; j < Pixel_NUM; j++)
{
data[j * 2] = color >> 8;
data[j * 2 + 1] = color;
}
/* 指定显存操作地址为全屏*/
LCD_Address_Set(0, 0, LCD_Width - 1, LCD_Height - 1);
LCD_WR_RS(1);/* 指定接下来的数据为数据 */
/* 写前半屏*/
HAL_SPI_Transmit_DMA(&hspi1,data, LCD_RAM_SIZE/2);HAL_Delay(250);
/*写后半屏*/
HAL_SPI_Transmit_DMA(&hspi1,data+LCD_RAM_SIZE/2, LCD_RAM_SIZE/2);HAL_Delay(250);
}
/**
* @brief LCD初始化
* @param none
* @return none
*/
void LCD_Init(void)
{
/* 初始化和LCD通信的引脚 */
LCD_reset();
HAL_Delay(120);
/* 关闭睡眠模式 */
LCD_Write_Cmd(0x11);
HAL_Delay(120);
/* 开始设置显存扫描模式,数据格式等 */
LCD_Write_Cmd(0x36);
LCD_Write_Data(0x00);
/* RGB 5-6-5-bit格式 */
LCD_Write_Cmd(0x3A);
LCD_Write_Data(0x65);
/* porch 设置 */
LCD_Write_Cmd(0xB2);
LCD_Write_Data(0x0C);
LCD_Write_Data(0x0C);
LCD_Write_Data(0x00);
LCD_Write_Data(0x33);
LCD_Write_Data(0x33);
/* VGH设置 */
LCD_Write_Cmd(0xB7);
LCD_Write_Data(0x72);
/* VCOM 设置 */
LCD_Write_Cmd(0xBB);
LCD_Write_Data(0x3D);
/* LCM 设置 */
LCD_Write_Cmd(0xC0);
LCD_Write_Data(0x2C);
/* VDV and VRH 设置 */
LCD_Write_Cmd(0xC2);
LCD_Write_Data(0x01);
/* VRH 设置 */
LCD_Write_Cmd(0xC3);
LCD_Write_Data(0x19);
/* VDV 设置 */
LCD_Write_Cmd(0xC4);
LCD_Write_Data(0x20);
/* 普通模式下显存速率设置 60Mhz */
LCD_Write_Cmd(0xC6);
LCD_Write_Data(0x0F);
/* 电源控制 */
LCD_Write_Cmd(0xD0);
LCD_Write_Data(0xA4);
LCD_Write_Data(0xA1);
/* 电压设置 */
LCD_Write_Cmd(0xE0);
LCD_Write_Data(0xD0);
LCD_Write_Data(0x04);
LCD_Write_Data(0x0D);
LCD_Write_Data(0x11);
LCD_Write_Data(0x13);
LCD_Write_Data(0x2B);
LCD_Write_Data(0x3F);
LCD_Write_Data(0x54);
LCD_Write_Data(0x4C);
LCD_Write_Data(0x18);
LCD_Write_Data(0x0D);
LCD_Write_Data(0x0B);
LCD_Write_Data(0x1F);
LCD_Write_Data(0x23);
/* 电压设置 */
LCD_Write_Cmd(0xE1);
LCD_Write_Data(0xD0);
LCD_Write_Data(0x04);
LCD_Write_Data(0x0C);
LCD_Write_Data(0x11);
LCD_Write_Data(0x13);
LCD_Write_Data(0x2C);
LCD_Write_Data(0x3F);
LCD_Write_Data(0x44);
LCD_Write_Data(0x51);
LCD_Write_Data(0x2F);
LCD_Write_Data(0x1F);
LCD_Write_Data(0x1F);
LCD_Write_Data(0x20);
LCD_Write_Data(0x23);
/* 显示开 */
LCD_Write_Cmd(0x21);
LCD_Write_Cmd(0x29);
/* 清屏为白色 */
LCD_Fill(WHITE);
/*打开显示*/
LCD_PWR(1);
}
ST7789.h
#ifndef __ST7789_H
#define __ST7789_H
#include "main.h"
#include "spi.h"
#define LCD_PWR(n) (n?\
HAL_GPIO_WritePin(LCD_PWR_GPIO_Port,LCD_PWR_Pin,GPIO_PIN_SET):\
HAL_GPIO_WritePin(LCD_PWR_GPIO_Port,LCD_PWR_Pin,GPIO_PIN_RESET))
#define LCD_WR_RS(n) (n?\
HAL_GPIO_WritePin(LCD_DCX_GPIO_Port,LCD_DCX_Pin,GPIO_PIN_SET):\
HAL_GPIO_WritePin(LCD_DCX_GPIO_Port,LCD_DCX_Pin,GPIO_PIN_RESET))
#define LCD_RST(n) (n?\
HAL_GPIO_WritePin(LCD_RST_GPIO_Port,LCD_RST_Pin,GPIO_PIN_SET):\
HAL_GPIO_WritePin(LCD_RST_GPIO_Port,LCD_RST_Pin,GPIO_PIN_RESET))
//LCD屏幕分辨率定义
#define LCD_Width 240
#define LCD_Height 240
#define LCD_RAM_SIZE LCD_Width*LCD_Height*2 //长240 宽240 色深2bit
#define Pixel_NUM (LCD_RAM_SIZE/2)
//颜色定义
#define WHITE 0xFFFF //白色
#define YELLOW 0xFFE0 //黄色
#define BRRED 0XFC07 //棕红色
#define PINK 0XF81F //粉色
#define RED 0xF800 //红色
#define BROWN 0XBC40 //棕色
#define GRAY 0X8430 //灰色
#define GBLUE 0X07FF //兰色
#define GREEN 0x07E0 //绿色
#define BLUE 0x001F //蓝色
#define BLACK 0x0000 //黑色
void LCD_reset(void);
void LCD_Init(void);
void LCD_Fill(uint16_t color);
#endif
main
int main(void)
{
/* USER CODE BEGIN 1 */
uint16_t color = 0;
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_LPUART1_UART_Init();
MX_SPI1_Init();
/* USER CODE BEGIN 2 */
LCD_Init();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
printf("working \r\n");
color+= 0x2222;
LCD_Fill(color);
HAL_Delay(1000);
}
/* USER CODE END 3 */
}
一些解释:
1,cubemx生成的这个工程,无需自己手动开启SPI通道或者DMA传输,选择两种方式传输的唯一区别就是HAL_SPI_Transmit_DMA和HAL_SPI_Transmit的函数选择的区别而已。
2,为什么要分上下两半屏传输?因为一帧的ram为2402402,大约10万。而HAL_SPI_Transmit_DMA的size参数是使用uint16_t定义的,只能达到6万多,所以要掰两半传输。
3,主函数color啥意思?color负责变色,每一秒color+0x2222,屏幕显示对应的颜色。color会隔一段时间溢出,又从头开始变色。
4,为什么要在HAL_SPI_Transmit_DMA后面加延时?,cpu执行HAL_SPI_Transmit_DMA的过程只是配置一些寄存器的过程,很快,不会像HAL_SPI_Transmit一样死等传输完成。所以两次DMA传输有可能会冲突,造成显示异常。你可以尝试将这个延时改小,比如5ms,然后看一下现象。
5,如何优雅的使用SPI+DMA? 设置一个初值为1的信号量,在HAL_SPI_Transmit_DMA执行前获取,DMA传输完成的时候释放。可以看出,这个信号量保护的是DMA配置和传输两个过程,还有,你需要开启DMA中断。