使用DMA方式,SPI驱动Aliyun Things 上的ST7789H2 LCD屏幕

前言

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中断。

おすすめ

転載: blog.csdn.net/qq_42039294/article/details/122273904