之前搞过一次SPI刷新ILI9341的屏幕,但是感觉还是不太够看,SPI的带宽上限还是差点意思,于是整了一块FSMC接口的屏幕玩玩,发现效果还真就不错,于是开源分享一下。


设备STM32F407VET6   ILI9341屏幕

引脚连接

 PE7   ------> FSMC_D4
  PE8   ------> FSMC_D5
  PE9   ------> FSMC_D6
  PE10   ------> FSMC_D7
  PE11   ------> FSMC_D8
  PE12   ------> FSMC_D9
  PE13   ------> FSMC_D10
  PE14   ------> FSMC_D11
  PE15   ------> FSMC_D12
  PD8   ------> FSMC_D13
  PD9   ------> FSMC_D14
  PD10   ------> FSMC_D15
  PD11   ------> FSMC_A16
  PD14   ------> FSMC_D0
  PD15   ------> FSMC_D1
  PD0   ------> FSMC_D2
  PD1   ------> FSMC_D3
  PD4   ------> FSMC_NOE 输出使能
  PD5   ------> FSMC_NWE 写使能
  PD7   ------> FSMC_NE1 片选信号

  PD6   ------> GPIO_OUTPUT
  PD12  ------> GPIO_OUTPUT


CubeMX 图形配置

  1. 启用 FSMC
    ▸ Pinout → FSMC → “NE1” → Type = LCD;数据宽度 16 bit,
    ▸ GPIO配置OUTPUT,这里的背光用的PD12,复位用的PD6。

  2. 把 LCD 的“RS”脚接在 FSMC 地址线上(常用 A16)
    这样 LCD 的 寄存器地址 = 0x6000 0000GRAM 地址 = 0x6002 0000(A16=1)。

  3. 打开 DMA2,任意一条空闲 Stream
    ▸ 模式:Memory-to-Memory
    ▸ 数据宽度:Half-Word(16 bit)
    ▸ 源地址递增,目标地址 固定(FSMC 映射地址 0x6002 0000)
    ▸ Burst 可设 INCR8,FIFO 全开,优先级 High
    ▸ 开启 DMA 中断。

  4. 时钟配置168Mhz,堆栈拉到0X2000(少点其实也够)然后生成工程

Address Setup Time是地址建立时间,确保地址信号提前稳定;Data Setup Time是数据读取时的等待时间,保证数据被正确采样;Bus Turn-Around Time则是防止总线冲突,尤其在读写切换时。这里直接配置1,2,1足够了。


然后配置ARM2D,可以参考之前发过的这篇文章:STM32 ILI9341屏幕(HAL库)Arm-2D框架_stm32+ili9341-CSDN博客

#ifndef __LCD_H
#define __LCD_H		
#include "stm32f4xx.h"	 
#include "stdlib.h"


#ifndef u8
#define u8 uint8_t
#endif

#ifndef u16
#define u16 uint16_t
#endif

#ifndef u32
#define u32 uint32_t
#endif

//LCD重要参数集
typedef struct  
{										    
	u16 width;			//LCD 宽度
	u16 height;			//LCD 高度
	u16 id;				//LCD ID
	u8  dir;			//横屏还是竖屏控制:0,竖屏;1,横屏。	
	u16	 wramcmd;		//开始写gram指令
	u16  setxcmd;		//设置x坐标指令
	u16  setycmd;		//设置y坐标指令	 
}_lcd_dev; 	

//LCD参数
extern _lcd_dev lcddev;	//管理LCD重要参数
/////////////////////////////////////用户配置区///////////////////////////////////	 
#define USE_HORIZONTAL  	 1//定义液晶屏顺时针旋转方向 	0-0度旋转,1-90度旋转,2-180度旋转,3-270度旋转

//////////////////////////////////////////////////////////////////////////////////	  
//定义LCD的尺寸
#define LCD_W 240
#define LCD_H 320

//TFTLCD部分外要调用的函数		   
extern u16  POINT_COLOR;//默认红色    
extern u16  BACK_COLOR; //背景颜色.默认为白色

//-----------------LCD端口定义---------------- 
////QDtech全系列模块采用了三极管控制背光亮灭,用户也可以接PWM调节背光亮度
#define	LCD_LED_SET	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_SET)            //LCD背光    	

//GPIO置位(拉高)
#define	LCD_CS_SET  ((void)0)  //FSMC模式无需手动片选 	
#define	LCD_RS_SET	((void)0)  //FSMC模式由地址线控制 
#define	LCD_RST_SET	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_6, GPIO_PIN_SET)   //复位		 

//GPIO复位(拉低)							    
#define	LCD_CS_CLR  ((void)0)   //FSMC模式无需手动片选  	
#define	LCD_RS_CLR	((void)0)    //FSMC模式由地址线控制  
#define	LCD_RST_CLR	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_6, GPIO_PIN_RESET)  //复位	
				
// FSMC 映射:NE1, A16 作为寄存器/数据选择
#define TFTLCD_BASE   ((uint32_t)(0x60000000U))
#define TFTLCD_REG    (*((volatile uint16_t *)(TFTLCD_BASE)))
#define TFTLCD_RAM    (*((volatile uint16_t *)(TFTLCD_BASE | 0x00020000U)))
				

//画笔颜色
#define WHITE       0xFFFF
#define BLACK      	0x0000	  
#define BLUE       	0x001F  
#define BRED        0XF81F
#define GRED 			 	0XFFE0
#define GBLUE			 	0X07FF
#define RED         0xF800
#define MAGENTA     0xF81F
#define GREEN       0x07E0
#define CYAN        0x7FFF
#define YELLOW      0xFFE0
#define BROWN 			0XBC40 //棕色
#define BRRED 			0XFC07 //棕红色
#define GRAY  			0X8430 //灰色
//GUI颜色

#define DARKBLUE      	 0X01CF	//深蓝色
#define LIGHTBLUE      	 0X7D7C	//浅蓝色  
#define GRAYBLUE       	 0X5458 //灰蓝色
//以上三色为PANEL的颜色 
 
#define LIGHTGREEN     	0X841F //浅绿色
#define LIGHTGRAY     0XEF5B //浅灰色(PANNEL)
#define LGRAY 			 		0XC618 //浅灰色(PANNEL),窗体背景色

#define LGRAYBLUE      	0XA651 //浅灰蓝色(中间层颜色)
#define LBBLUE          0X2B12 //浅棕蓝色(选择条目的反色)
	    															  
void LCD_Init(void);
void LCD_DisplayOn(void);
void LCD_DisplayOff(void);
void LCD_Clear(u16 Color);	 
void LCD_SetCursor(u16 Xpos, u16 Ypos);
void LCD_DrawPoint(u16 x,u16 y);//画点
u16  LCD_ReadPoint(u16 x,u16 y); //读点
void LCD_DrawLine(u16 x1, u16 y1, u16 x2, u16 y2);
void LCD_DrawRectangle(u16 x1, u16 y1, u16 x2, u16 y2);		   
void LCD_SetWindows(u16 xStar, u16 yStar,u16 xEnd,u16 yEnd);

u16 LCD_RD_DATA(void);//读取LCD数据									    
void LCD_WriteReg(u8 LCD_Reg, u16 LCD_RegValue);
void LCD_WR_DATA(u8 data);
u16 LCD_ReadReg(u8 LCD_Reg);
void LCD_WriteRAM_Prepare(void);
void LCD_WriteRAM(u16 RGB_Code);
u16 LCD_ReadRAM(void);		   
u16 LCD_BGR2RGB(u16 c);
void LCD_SetParam(void);
void Lcd_WriteData_16Bit(u16 Data);
void LCD_direction(u8 direction );

int32_t Disp0_DrawBitmap(int16_t x, int16_t y,
                             int16_t w, int16_t h,
                             const uint16_t *rgb565);				  		 
#endif  
#include "lcd.h"
#include "stdlib.h"
#include "main.h"
#include "dma.h"

#define u8 uint8_t
#define u16 uint16_t	   
//管理LCD重要参数
//默认为竖屏
_lcd_dev lcddev;

//画笔颜色,背景颜色
u16 POINT_COLOR = 0x0000,BACK_COLOR = 0xFFFF;  
u16 DeviceCode;	 

/*****************************************************************************
 * @name       :void LCD_WR_REG(u8 data)
 * @date       :2018-08-09 
 * @function   :Write an 8-bit command to the LCD screen
 * @parameters :data:Command value to be written
 * @retvalue   :None
******************************************************************************/
void LCD_WR_REG(u8 data)
{
	// FSMC模式下:写寄存器地址到 A16=0 区域
	TFTLCD_REG = (uint16_t)data;
}

/*****************************************************************************
 * @name       :void LCD_WR_DATA(u8 data)
 * @date       :2018-08-09 
 * @function   :Write an 8-bit data to the LCD screen
 * @parameters :data:data value to be written
 * @retvalue   :None
******************************************************************************/
void LCD_WR_DATA(u8 data)
{
	// FSMC模式下:写数据到 A16=1 区域
	TFTLCD_RAM = (uint16_t)data;
}

/*****************************************************************************
 * @name       :void LCD_WriteReg(u8 LCD_Reg, u16 LCD_RegValue)
 * @date       :2018-08-09 
 * @function   :Write data into registers
 * @parameters :LCD_Reg:Register address
                LCD_RegValue:Data to be written
 * @retvalue   :None
******************************************************************************/
void LCD_WriteReg(u8 LCD_Reg, u16 LCD_RegValue)
{	
	LCD_WR_REG(LCD_Reg);  
	LCD_WR_DATA(LCD_RegValue);	    
}	   

/*****************************************************************************
 * @name       :void LCD_WriteRAM_Prepare(void)
 * @date       :2018-08-09 
 * @function   :Write GRAM
 * @parameters :None
 * @retvalue   :None
******************************************************************************/	 
void LCD_WriteRAM_Prepare(void)
{
	LCD_WR_REG(lcddev.wramcmd);
}	 

/*****************************************************************************
 * @name       :void Lcd_WriteData_16Bit(u16 Data)
 * @date       :2018-08-09 
 * @function   :Write an 16-bit command to the LCD screen
 * @parameters :Data:Data to be written
 * @retvalue   :None
******************************************************************************/	 
void Lcd_WriteData_16Bit(u16 Data)
{	
	TFTLCD_RAM = Data;
}

/*****************************************************************************
 * @name       :void LCD_DrawPoint(u16 x,u16 y)
 * @date       :2018-08-09 
 * @function   :Write a pixel data at a specified location
 * @parameters :x:the x coordinate of the pixel
                y:the y coordinate of the pixel
 * @retvalue   :None
******************************************************************************/	
void LCD_DrawPoint(u16 x,u16 y)
{
	LCD_SetCursor(x,y);//设置光标位置 
	Lcd_WriteData_16Bit(POINT_COLOR); 
}

/*****************************************************************************
 * @name       :void LCD_Clear(u16 Color)
 * @date       :2018-08-09 
 * @function   :Full screen filled LCD screen
 * @parameters :color:Filled color
 * @retvalue   :None
******************************************************************************/	
void LCD_Clear(u16 Color)
{
  unsigned int i,m;  
	LCD_SetWindows(0,0,lcddev.width-1,lcddev.height-1);   
	LCD_CS_CLR;
	LCD_RS_SET;
	for(i=0;i<lcddev.height;i++)
	{
    for(m=0;m<lcddev.width;m++)
    {	
			Lcd_WriteData_16Bit(Color);
		}
	}
	 LCD_CS_SET;
} 

void LCD_RESET(void)
{
	LCD_RST_CLR;
	HAL_Delay(100);	
	LCD_RST_SET;
	HAL_Delay(50);
}

/*****************************************************************************
 * @name       :void LCD_RESET(void)
 * @date       :2018-08-09 
 * @function   :Initialization LCD screen
 * @parameters :None
 * @retvalue   :None
******************************************************************************/	 	 
void LCD_Init(void)
{  
 	LCD_RESET(); //LCD 复位
//*************2.8inch ILI9341初始化**********//	
	LCD_WR_REG(0xCF);  
	LCD_WR_DATA(0x00); 
	LCD_WR_DATA(0xC9); //C1 
	LCD_WR_DATA(0X30); 
	LCD_WR_REG(0xED);  
	LCD_WR_DATA(0x64); 
	LCD_WR_DATA(0x03); 
	LCD_WR_DATA(0X12); 
	LCD_WR_DATA(0X81); 
	LCD_WR_REG(0xE8);  
	LCD_WR_DATA(0x85); 
	LCD_WR_DATA(0x10); 
	LCD_WR_DATA(0x7A); 
	LCD_WR_REG(0xCB);  
	LCD_WR_DATA(0x39); 
	LCD_WR_DATA(0x2C); 
	LCD_WR_DATA(0x00); 
	LCD_WR_DATA(0x34); 
	LCD_WR_DATA(0x02); 
	LCD_WR_REG(0xF7);  
	LCD_WR_DATA(0x20); 
	LCD_WR_REG(0xEA);  
	LCD_WR_DATA(0x00); 
	LCD_WR_DATA(0x00); 
	LCD_WR_REG(0xC0);    //Power control 
	LCD_WR_DATA(0x1B);   //VRH[5:0] 
	LCD_WR_REG(0xC1);    //Power control 
	LCD_WR_DATA(0x00);   //SAP[2:0];BT[3:0] 01 
	LCD_WR_REG(0xC5);    //VCM control 
	LCD_WR_DATA(0x30); 	 //3F
	LCD_WR_DATA(0x30); 	 //3C
	LCD_WR_REG(0xC7);    //VCM control2 
	LCD_WR_DATA(0XB7); 
	LCD_WR_REG(0x36);    // Memory Access Control 
	LCD_WR_DATA(0x08); 
	LCD_WR_REG(0x3A);   
	LCD_WR_DATA(0x55); 
	LCD_WR_REG(0xB1);   
	LCD_WR_DATA(0x00);   
	LCD_WR_DATA(0x1A); 
	LCD_WR_REG(0xB6);    // Display Function Control 
	LCD_WR_DATA(0x0A); 
	LCD_WR_DATA(0xA2); 
	LCD_WR_REG(0xF2);    // 3Gamma Function Disable 
	LCD_WR_DATA(0x00); 
	LCD_WR_REG(0x26);    //Gamma curve selected 
	LCD_WR_DATA(0x01); 
	LCD_WR_REG(0xE0);    //Set Gamma 
	LCD_WR_DATA(0x0F); 
	LCD_WR_DATA(0x2A); 
	LCD_WR_DATA(0x28); 
	LCD_WR_DATA(0x08); 
	LCD_WR_DATA(0x0E); 
	LCD_WR_DATA(0x08); 
	LCD_WR_DATA(0x54); 
	LCD_WR_DATA(0XA9); 
	LCD_WR_DATA(0x43); 
	LCD_WR_DATA(0x0A); 
	LCD_WR_DATA(0x0F); 
	LCD_WR_DATA(0x00); 
	LCD_WR_DATA(0x00); 
	LCD_WR_DATA(0x00); 
	LCD_WR_DATA(0x00); 		 
	LCD_WR_REG(0XE1);    //Set Gamma 
	LCD_WR_DATA(0x00); 
	LCD_WR_DATA(0x15); 
	LCD_WR_DATA(0x17); 
	LCD_WR_DATA(0x07); 
	LCD_WR_DATA(0x11); 
	LCD_WR_DATA(0x06); 
	LCD_WR_DATA(0x2B); 
	LCD_WR_DATA(0x56); 
	LCD_WR_DATA(0x3C); 
	LCD_WR_DATA(0x05); 
	LCD_WR_DATA(0x10); 
	LCD_WR_DATA(0x0F); 
	LCD_WR_DATA(0x3F); 
	LCD_WR_DATA(0x3F); 
	LCD_WR_DATA(0x0F); 
	LCD_WR_REG(0x2B); 
	LCD_WR_DATA(0x00);
	LCD_WR_DATA(0x00);
	LCD_WR_DATA(0x01);
	LCD_WR_DATA(0x3f);
	LCD_WR_REG(0x2A); 
	LCD_WR_DATA(0x00);
	LCD_WR_DATA(0x00);
	LCD_WR_DATA(0x00);
	LCD_WR_DATA(0xef);	 
	LCD_WR_REG(0x11); //Exit Sleep
	HAL_Delay(120);
	LCD_WR_REG(0x29); //display on		

  LCD_direction(USE_HORIZONTAL);//设置LCD显示方向
	LCD_LED_SET;//点亮背光	 
	LCD_Clear(WHITE);//清全屏白色
}
 
/*****************************************************************************
 * @name       :void LCD_SetWindows(u16 xStar, u16 yStar,u16 xEnd,u16 yEnd)
 * @date       :2018-08-09 
 * @function   :Setting LCD display window
 * @parameters :xStar:the bebinning x coordinate of the LCD display window
								yStar:the bebinning y coordinate of the LCD display window
								xEnd:the endning x coordinate of the LCD display window
								yEnd:the endning y coordinate of the LCD display window
 * @retvalue   :None
******************************************************************************/ 
void LCD_SetWindows(u16 xStar, u16 yStar,u16 xEnd,u16 yEnd)
{	
	LCD_WR_REG(lcddev.setxcmd);	
	LCD_WR_DATA(xStar>>8);
	LCD_WR_DATA(0x00FF&xStar);		
	LCD_WR_DATA(xEnd>>8);
	LCD_WR_DATA(0x00FF&xEnd);

	LCD_WR_REG(lcddev.setycmd);	
	LCD_WR_DATA(yStar>>8);
	LCD_WR_DATA(0x00FF&yStar);		
	LCD_WR_DATA(yEnd>>8);
	LCD_WR_DATA(0x00FF&yEnd);

	LCD_WriteRAM_Prepare();	//开始写入GRAM			
}   

/*****************************************************************************
 * @name       :void LCD_SetCursor(u16 Xpos, u16 Ypos)
 * @date       :2018-08-09 
 * @function   :Set coordinate value
 * @parameters :Xpos:the  x coordinate of the pixel
								Ypos:the  y coordinate of the pixel
 * @retvalue   :None
******************************************************************************/ 
void LCD_SetCursor(u16 Xpos, u16 Ypos)
{	  	    			
	LCD_SetWindows(Xpos,Ypos,Xpos,Ypos);	
} 

/*****************************************************************************
 * @name       :void LCD_direction(u8 direction)
 * @date       :2018-08-09 
 * @function   :Setting the display direction of LCD screen
 * @parameters :direction:0-0 degree
                          1-90 degree
													2-180 degree
													3-270 degree
 * @retvalue   :None
******************************************************************************/ 
void LCD_direction(u8 direction)
{ 
			lcddev.setxcmd=0x2A;
			lcddev.setycmd=0x2B;
			lcddev.wramcmd=0x2C;
	switch(direction){		  
		case 0:						 	 		
			lcddev.width=LCD_W;
			lcddev.height=LCD_H;		
			LCD_WriteReg(0x36,(1<<3)|(0<<6)|(0<<7));//BGR==1,MY==0,MX==0,MV==0
		break;
		case 1:
			lcddev.width=LCD_H;
			lcddev.height=LCD_W;
			LCD_WriteReg(0x36,(1<<3)|(0<<7)|(1<<6)|(1<<5));//BGR==1,MY==1,MX==0,MV==1
		break;
		case 2:						 	 		
			lcddev.width=LCD_W;
			lcddev.height=LCD_H;	
			LCD_WriteReg(0x36,(1<<3)|(1<<6)|(1<<7));//BGR==1,MY==0,MX==0,MV==0
		break;
		case 3:
			lcddev.width=LCD_H;
			lcddev.height=LCD_W;
			LCD_WriteReg(0x36,(1<<3)|(1<<7)|(1<<5));//BGR==1,MY==1,MX==0,MV==1
		break;	
		default:break;
	}		
}	 

到这一步其实屏幕可以正常驱动了,还没有用到ARM2D,LCD驱动是根据最早那篇博客修改的,主要就是读写底层逻辑改了一下,比我想象的还要简单。


一、无DMA算法

main.c的主体部分在三个算法中一样。

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "lcd.h"
#include "perf_counter.h"
#include "arm_2d.h"
#include "arm_2d_disp_adapter_0.h"
#include "arm_2d_helper.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* USER CODE BEGIN 1 */

  /* 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_FSMC_Init();
  /* USER CODE BEGIN 2 */
	LCD_Init();
	perfc_init(true);
  arm_irq_safe {
			arm_2d_init();
	}
	disp_adapter0_init();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		disp_adapter0_task();
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

此处需要在disp_adapter_0.h关闭异步刷新

int32_t Disp0_DrawBitmap(int16_t x, 
                         int16_t y, 
                         int16_t width, 
                         int16_t height, 
                         const uint8_t *bitmap)
{
    uint32_t total_pixels;
    const uint8_t *data_ptr;
    uint32_t i;

    // 设置LCD显示窗口
    LCD_SetWindows(x, y, x + width - 1, y + height - 1);

    // 准备写入GRAM
    LCD_WriteRAM_Prepare();

    // 逐像素通过FSMC写入 RGB565 数据
    total_pixels = (uint32_t)width * (uint32_t)height;
    data_ptr = bitmap;

    for (i = 0; i < total_pixels; i++)
    {
        uint16_t color = ((uint16_t)data_ptr[0] << 8) | data_ptr[1];
        TFTLCD_RAM = color;
        data_ptr += 2;
    }

    return 0;
}

45帧

二、轮询DMA

此处需要关闭交换高低字节

#define LCD_RAM_ADDR  (*(__IO uint16_t *)0x60020000)   /* FSMC 口 */
extern DMA_HandleTypeDef hdma_memtomem_dma2_stream0;   /* CubeMX 生成 */


int32_t Disp0_DrawBitmap(int16_t x, int16_t y,
                         int16_t w, int16_t h,
                         const uint16_t *rgb565)
{
    /* 1. 边界越界简单判断,这里省略 */


    /* 2. 开窗 */
    LCD_SetWindows(x, y, x + w - 1, y + h - 1);

    /* 3. 发写 GRAM 命令 */
    LCD_WriteRAM_Prepare();      // 寄存器口发 0x22
    __DSB();                     // 保证命令先写完

    /* 4. 启动 DMA 轮询 */
    HAL_DMA_Start(&hdma_memtomem_dma2_stream0,
                  (uint32_t)rgb565,          // 源
                  (uint32_t)&LCD_RAM_ADDR,   // 目标(固定)
                  w * h);                    // 像素个数

    /* 5. 阻塞等完成 */
    HAL_DMA_PollForTransfer(&hdma_memtomem_dma2_stream0,
                            HAL_DMA_FULL_TRANSFER,
                            HAL_MAX_DELAY);   // 或者给 10 ms 超时

    return 0;
}

53帧

三、异步刷新DMA

此处需要开启异步刷新

#define LCD_RAM_ADDR  (*(__IO uint16_t *)0x60020000)   /* FSMC 口 */
extern DMA_HandleTypeDef hdma_memtomem_dma2_stream0;   /* CubeMX 生成 */

int32_t Disp0_DrawBitmap(int16_t x, int16_t y,
                             int16_t w, int16_t h,
                             const uint16_t *rgb565)
{
    /* 1. 边界裁剪(略) */

    /* 2. 开窗 */
    LCD_SetWindows(x, y, x+w-1, y+h-1);

    /* 3. 准备写 GRAM */
    LCD_WriteRAM_Prepare();        // 拉低 RS,发 0x22 命令
    __DSB();                       // 保证命令先发完

    /* 4. 启动 DMA */
    HAL_DMA_Start_IT(&hdma_memtomem_dma2_stream0,
                     (uint32_t)rgb565,        // 源:内存图像
                     (uint32_t)&LCD_RAM_ADDR, // 目标:FSMC 映射口
                     w*h);                    // 长度:像素个数

    /* 5. 可选:等待完成,或放后台 */
    // osSemaphoreAcquire(&lcd_sem, osWaitForever); // RTOS 信号量
    return 0;
}

当然异步函数还需要加点东西

/* USER CODE BEGIN 4 */
void __disp_adapter0_request_async_flushing( 
    void *pTarget,
    bool bIsNewFrame,
    int16_t iX, 
    int16_t iY,
    int16_t iWidth,
    int16_t iHeight,
    const COLOUR_INT *pBuffer)
{
    Disp0_DrawBitmap(iX, iY, iWidth, iHeight, (const uint8_t *)pBuffer);  
}
void Disp0DmaCpltCallback(void)
{
    /* 释放信号量,或者切换下一帧缓冲 */
    disp_adapter0_insert_async_flushing_complete_event_handler();
}

/* USER CODE END 4 */

这个Disp0DmaCpltCallback函数需要在main.h声明并在stm32f4xx_it.c中加入到DMA的中断处理。

void DMA2_Stream0_IRQHandler(void)
{
  /* USER CODE BEGIN DMA2_Stream0_IRQn 0 */

  /* USER CODE END DMA2_Stream0_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_memtomem_dma2_stream0);
  /* USER CODE BEGIN DMA2_Stream0_IRQn 1 */
	Disp0DmaCpltCallback();
  /* USER CODE END DMA2_Stream0_IRQn 1 */
}

64帧,达成。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐