Using DMA mode, SPI drives the ST7789H2 LCD screen on Aliyun Things

preface

1: Driver reference from https://blog.csdn.net/BearPi/article/details/104311705.:
2: This is one of my records. I don't realize many functions. I just move a piece of video memory opened up in memory to the screen by DMA.
3: I didn't do anything during DMA handling, just gave him a delay to finish. In practical application, the semaphore should be set and the CPU should be released, and the transmission of the next frame can be started after the transmission is completed.

CUBEMX

Some GPIO, control backlight, reset, command or data

RCC is full, and the clock tree is not pasted.
Here is SPI


DMA

Code part

ST7789.c

#include "ST7789.h"
#include "string.h"
#include "spi.h"
#include "stdio.h"

/**
 *@brief    LCD Control pin and communication interface initialization
 *@param    none
 *@retval   none
*/
void LCD_reset(void)
{
	/* Reset LCD */
    LCD_PWR(0);
    LCD_RST(0);
    HAL_Delay(100);
    LCD_RST(1);
}
/**
 * @brief    SPI Send byte function
 * @param    TxData	Data to send
 * @param    size	Byte size of data sent
 * @return  0:Write succeeded, others: write failed
 */
uint8_t SPI_WriteByte(uint8_t *TxData,uint16_t size)
{
	return HAL_SPI_Transmit(&hspi1,TxData,size,1000);
}
/**
 * @brief   Write command to LCD
 * @param   cmd - Commands to send
 * @return  none
 */
static void LCD_Write_Cmd(uint8_t cmd)
{
    LCD_WR_RS(0);
    SPI_WriteByte(&cmd, 1);
}

/**
 * @brief   Write data to LCD
 * @param   dat - Data to be sent
 * @return  none
 */
static void LCD_Write_Data(uint8_t dat)
{
    LCD_WR_RS(1);
    SPI_WriteByte(&dat, 1);
}
/**
 * @breif   Turn on the LCD display backlight
 * @param   none
 * @return  none
 */
void LCD_DisplayOn(void)
{
    LCD_PWR(1);
}
/**
 * @brief   Turn off LCD display backlight
 * @param   none
 * @return  none
 */
void LCD_DisplayOff(void)
{
    LCD_PWR(0);
}
/**
 * @brief   Setting data writing to LCD memory area
 * @param   x1,y1	- Starting point coordinates
 * @param   x2,y2	- End coordinates
 * @return  none
 */
void LCD_Address_Set(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
    /* Specifies the X-direction operation area */
    LCD_Write_Cmd(0x2a);
    LCD_Write_Data(x1 >> 8);
    LCD_Write_Data(x1);
    LCD_Write_Data(x2 >> 8);
    LCD_Write_Data(x2);

    /* Specify the Y direction operation area */
    LCD_Write_Cmd(0x2b);
    LCD_Write_Data(y1 >> 8);
    LCD_Write_Data(y1);
    LCD_Write_Data(y2 >> 8);
    LCD_Write_Data(y2);

    /* When this command is sent, the LCD starts waiting to receive the video memory data */
    LCD_Write_Cmd(0x2C);
}
/**
 * @brief   Clear the LCD screen in one color
 * @param   color - Clear screen 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;
    }
    /* Specifies that the video memory operation address is full screen*/
    LCD_Address_Set(0, 0, LCD_Width - 1, LCD_Height - 1); 
    LCD_WR_RS(1);/* Specify the next data as data */
	/* Write first half screen*/
	HAL_SPI_Transmit_DMA(&hspi1,data, LCD_RAM_SIZE/2);HAL_Delay(250);
	/*Write back half screen*/
	HAL_SPI_Transmit_DMA(&hspi1,data+LCD_RAM_SIZE/2, LCD_RAM_SIZE/2);HAL_Delay(250);

}

/**
 * @brief   LCD initialization
 * @param   none
 * @return  none
 */
void LCD_Init(void)
{
    /* Initialization and LCD communication pin */
    LCD_reset();
    HAL_Delay(120);
	
    /* Turn off sleep mode */
    LCD_Write_Cmd(0x11);
    HAL_Delay(120);

    /* Start setting video memory scanning mode, data format, etc */
    LCD_Write_Cmd(0x36);
    LCD_Write_Data(0x00);
    /* RGB 5-6-5-bit format  */
    LCD_Write_Cmd(0x3A);
    LCD_Write_Data(0x65);
    /* porch set up */
    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 set up */
    LCD_Write_Cmd(0xB7);
    LCD_Write_Data(0x72);
    /* VCOM set up */
    LCD_Write_Cmd(0xBB);
    LCD_Write_Data(0x3D);
    /* LCM set up */
    LCD_Write_Cmd(0xC0);
    LCD_Write_Data(0x2C);
    /* VDV and VRH set up */
    LCD_Write_Cmd(0xC2);
    LCD_Write_Data(0x01);
    /* VRH set up */
    LCD_Write_Cmd(0xC3);
    LCD_Write_Data(0x19);
    /* VDV set up */
    LCD_Write_Cmd(0xC4);
    LCD_Write_Data(0x20);
    /* In normal mode, the video memory rate is set to 60Mhz */
    LCD_Write_Cmd(0xC6);
    LCD_Write_Data(0x0F);
    /* Power control */
    LCD_Write_Cmd(0xD0);
    LCD_Write_Data(0xA4);
    LCD_Write_Data(0xA1);
    /* Voltage setting */
    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);
    /* Voltage setting */
    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);
    /* Display on */
    LCD_Write_Cmd(0x21);
    LCD_Write_Cmd(0x29);
    
    /* The clear screen is white */
    LCD_Fill(WHITE);

    /*Open display*/
    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))


//Definition of LCD screen resolution
#define LCD_Width   240
#define LCD_Height  240
#define LCD_RAM_SIZE LCD_Width*LCD_Height*2 		// Length 240 width 240 color depth 2bit
#define Pixel_NUM (LCD_RAM_SIZE/2)

//Color definition
#define WHITE   0xFFFF 	// white
#define YELLOW  0xFFE0 	// yellow
#Define brred 0xfc07 / / brownish red
#define PINK    0XF81F 	// Pink
#define RED     0xF800 	// gules
#Define brown 0xbc40 / / Brown
#Define gray 0x8430 / / gray
#define GBLUE   0X07FF 	// Blue
#define GREEN   0x07E0 	// green
#Define blue 0x001f / / blue
#define BLACK   0x0000 	// black

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 */
}

Some explanations:
1. The project generated by cubemx does not need to manually start SPI channel or DMA transmission. The only difference between the two transmission modes is HAL_SPI_Transmit_DMA and Hal_ SPI_ The difference between the function selection of transmit.
2. Why split the upper and lower screens for transmission? Because the ram of one frame is 2402, about 100000. And Hal_ SPI_ Transmit_ The size parameter of DMA is uint16_t can only reach more than 60000, so it is necessary to break the two halves for transmission.
3. What does the main function color mean? Color is responsible for color change. color+0x2222 every second, and the screen displays the corresponding color. Color will overflow from the beginning and change color again after a period of time.
4. Why in HAL_SPI_Transmit_ Add delay after DMA?, cpu executes HAL_SPI_Transmit_ The DMA process is just a process of configuring some registers. It will not be like Hal soon_ SPI_ Like transmit, wait until the transmission is completed. Therefore, the two DMA transfers may conflict, resulting in abnormal display. You can try to reduce the delay, such as 5ms, and then look at the phenomenon.
5. How to use SPI+DMA gracefully? Set a semaphore with an initial value of 1 in Hal_ SPI_ Transmit_ It is obtained before DMA execution and released when DMA transmission is completed. It can be seen that this semaphore protects the two processes of DMA configuration and transmission. In addition, you need to turn on DMA interrupt.

Keywords: C Single-Chip Microcomputer stm32

Added by MikeUK on Tue, 04 Jan 2022 06:56:58 +0200