STM32CubeMX配置FSMC之TFTLCD驱动,并移植STemWin

目录

一  配置CRC与FSMC

二  添加STemWin代码,以及配置头文件路径

三  编写TFTLCD驱动文件

四 关联TFTLCD驱动函数与STemWin


一  配置CRC与FSMC

创建STM32F767+freeRTOS工程可参考:https://blog.csdn.net/Ningjianwen/article/details/90610373

本篇着重讲移植STemWin, 使用的平台是原子的STM32F767开发板.

1. 使用STemWin必须打开CRC,否则图形库运行不起来

2. FMC Mode是根据硬件连接进行配置的,我使用的是正点原子开发板,

  • LCD连在STM32F7的外部存储1区,地址范围0x6000,0000~63FF,FFFF,所以Chip Select选择NE1
  • LCD的寄存器控制线LCD _RS与MCU的FMC_A18(即PD13)相连,所以LCD Register Select选择A18
  • 使用的是4.3寸的电容触摸屏,该触摸屏数据线16位.

3. 接下来配置FMC时序, 这个需要根据ILI9341手册进行配置,最终配置如下图所示

  • Extended mode这里设置为Enabled,表示是否允许读写不同的时序.
  • NOR/PSRAM timing控制读时序, Extended控制写时序. 由于HCLK=216MHz, 计算得时钟周期约为4.6ns
    由下面截图可得:
    读数据时地址建立时间 >= 10 ns,所以需取值大于等于3.   ( 3 ×4.6 > 10 ) 
    写数据时地址建立时间 = 0,
    读写数据时建立时间 >= 15ns,所以取值需大于等于4
  • LCD控制器使用 Mode A ,该模式用来控制SRAM/PSRAM且OE会翻转

(经过实际实验,即使我将读写时序的地址建立时间,数据建立时间,数据读写时间都设置为0,依然可以正常驱动LCD ,然后控制模式,选择MODE_A,B,C,D任意模式都是可以的^_^)

下图截自NT35510手册 中的 "7.6.1 Parallel Interface Characteristics" 

4. 配置PB5(背光灯控制)引脚为输出模式, 该引脚输出高电平时点亮背光灯.

5. 当我们使用FMC访问外部地址时,还必须配置MPU(内存保护单元),配置如下:

  • Base Address配置为0x60000000
  • MPU Region Size最小配置为256M,刚好将BANK1所有地址覆盖(该项也可以配置为1M,因为1M = 0x100000, 而我们要操作的地址偏移为0x0007FFFE ~ 0x00080001,为什么是这个地址,后续有讲解)
  • SubRegion Disable配置必须为0
  • TEX field level可随意选择
  • Access Permission 配置为ALL_ACCESS PERMITTED表示在特权模式与非特权模式都是可读可写的.(该项也可配置为Priviliged READS\WRITES Permissions:特权模式下可读可写)
  • Instruction Access可随意选择
  • Shareability 配置为ENABLE 表示 在读写数据时不会将数据缓存到 D-Cache (该项也可配置为DISABLE)
  • Cacheable 配置为DISABLE  表示在执行指令时,指令不会缓存到I-Cache (必须配置为DISABLE)
  • Bufferable 可随意选择

6. 配置完成点击Generate Code生成代码.

二  添加STemWin代码,以及配置头文件路径

stm32cubemx官方下载地址

1.添加的代码如下:

2. 头文件路径如下:

三  编写TFTLCD驱动文件

1. 定义LCD操作结构体如下:

typedef struct
{volatile uint16_t cmd;volatile uint16_t data;
} LCD_TypeDef;#define LCD_BASE ((uint32_t)(0x60000000 | 0x0007FFFE))
#define LCD      ((LCD_TypeDef *) LCD_BASE)

       关于该段代码,最关键的是LCD_BASE的定义,从下面的截图我们看出对于16位数据总线,内部会进行进行右移一位处理,
即HADDR[25:1]操作FMC_A[24:0],从这里可以推理出来需要操作FMC_A[18]等价于需要操作HADDR[19],
我们将LCD_TypeDef结构体放在(0x60000000 | 0x0007FFFE)位置, 其中0x60000000 是因为我们的LCD连接的外部Bank1,其地址偏移为0x60000000(见下图2), LCD_TypeDef结构体有两个成员,reg成员占用了两字节,0x0007FFFE+2 = 0x80000,所以data成员在地址0x60080000上,而刚好0x80000 的bit19 = 1 <=> HADDR[19] = 1 <=> FMC_A[18] = 1 <=> RS=1, 而对LCD而言,RS=1表示传输数据,RS=0表示传输命令.

理解了上述原理,我们也可以这样定义:

#define LCD_CMD   (*(uint16_t *)(0x60000000 | 0))
#define LCD_DATA  (*(uint16_t *)(0x60000000 | 0x80000))

2. 定义了LCD操作结构体,接下来就可以编写lcd相关的基础函数了.我使用的NT35510控制器LCD.

主要实现,画点(LCD_DrawPoint),读点(LCD_ReadPoint),填充(LCD_Fill)函数,这几个函数在后续移植需要用到.

/**************************************************************************************** @brief   设置xy坐标* @input   Xpos:横坐标   Ypos:纵坐标* @return
***************************************************************************************/
void LCD_SetCursor(uint16_t Xpos, uint16_t Ypos)
{LCD->reg = (SET_X_CMD);LCD->data = (Xpos>>8); 		LCD->reg = (SET_X_CMD+1);LCD->data = (Xpos&0XFF);	LCD->reg = (SET_Y_CMD);LCD->data = (Ypos>>8);  		LCD->reg = (SET_Y_CMD+1);LCD->data = (Ypos&0XFF);
}/**************************************************************************************** @brief   读取个某点的颜色值* @input   x,y:坐标* @return  返回值:此点的颜色
***************************************************************************************/
uint32_t LCD_ReadPoint(uint16_t x, uint16_t y)
{uint16_t r = 0, g = 0, b = 0;if(x >= LCD_WIDTH || y >= LCD_LENGTH)return 0;	//超过了范围,直接返回LCD_SetCursor(x, y);LCD->reg = MEM_READ;r = LCD->data;		//dummy Readr = LCD->data;  	//实际坐标颜色b = LCD->data;g = (r & 0XFF) << 8;		//第一次读取的是RG的值,R在前,G在后,各占8位return (((r >> 11) << 11) | ((g >> 10) << 5) | (b >> 11)); //公式转换一下
}/**************************************************************************************** @brief   画点* @input   x,y:坐标  color:颜色* @return
***************************************************************************************/
void LCD_DrawPoint(uint16_t x, uint16_t y, uint32_t color)
{LCD_SetCursor(x,y);LCD->reg = MEM_WRITE;LCD->data = color; 
}/**************************************************************************************** @brief   在指定区域内填充单个颜色* @input   (sx,sy),(ex,ey):填充矩形对角坐标,区域大小为:(ex-sx+1)*(ey-sy+1)* @input   color:要填充的颜色* @return
***************************************************************************************/
void LCD_Fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint32_t color)
{uint16_t i, j;uint16_t xlen = 0;xlen = ex - sx + 1;for(i = sy; i <= ey; i++){LCD_SetCursor(sx, i);      				//设置光标位置LCD->reg = MEM_WRITE; ;     			//开始写入GRAMfor(j = 0; j < xlen; j++)LCD->data = color;	//显示颜色}
}

3. 实现LCD初始化函数,这个省略,一般官方会提供.

四 关联TFTLCD驱动函数与STemWin

     1.  注释掉LCD_ConfDefaults.h和LCD_Private.h文件中的#include "LCDConf.h"

     2. GUIConf.h 文件中添加 #define OS_SUPPORT 启用OS

     3.  从STemWin库中复制STemWin_Library_V1.2.0\Project\STM32469I-EVAL\RTOS\Config\GUI_X_FreeRTOS.c文件
         替代GUI_X_OS.c文件中的内容. (
stm32cubemx官方下载地址)

     4. 修改LCDConf_FlexColor_Template.c文件

在这个文件中我们要完成 STemWin 的打点(_SetPixelIndex())、读点(_GetPixelIndex())、填充(_FillRect())等函数的实现

static void _SetPixelIndex(GUI_DEVICE * pDevice, int x, int y, int PixelIndex) {......GUI_USE_PARA(PixelIndex);{LCD_DrawPoint(x,y,PixelIndex); //此处调用画点函数}......
}static unsigned int _GetPixelIndex(GUI_DEVICE * pDevice, int x, int y) {......GUI_USE_PARA(y);{PixelIndex = LCD_ReadPoint(x,y);  //次处调用读点函数}......
}static void _FillRect(GUI_DEVICE * pDevice, int x0, int y0, int x1, int y1) {......if (GUI_pContext->DrawMode & LCD_DRAWMODE_XOR) {......} else {LCD_Fill(x0,y0,x1,y1,LCD_COLORINDEX);//此处修改为画矩形函数}
}

5.修改LCDConf_FlexColor_Template.c文件 (方式一)

该文件主要修改LCD_X_Config函数 与XSIZE_PHYS /YSIZE_PHYS  两个宏,
有一点需要注意:GUI_DEVICE_CreateAndLink(&GUIDRV_Template_API, GUICC_M565, 0, 0);
该函数的第一个参数是有修改的,表示我们使用的驱动函数是GUIDRV_Template_API数组中的函数

#define XSIZE_PHYS  800 // To be adapted to x-screen size
#define YSIZE_PHYS  480 // To be adapted to y-screen sizevoid LCD_X_Config(void) {// Set display driver and color conversion//GUI_DEVICE_CreateAndLink(&GUIDRV_Template_API, GUICC_M565, 0, 0);//// Display driver configuration, required for Lin-driver//LCD_SetSizeEx (0, XSIZE_PHYS , YSIZE_PHYS);LCD_SetVSizeEx(0, XSIZE_PHYS, YSIZE_PHYS);
}

6.修改LCDConf_FlexColor_Template.c文件  (方式二)

LCD_X_Config函数的参数是GUIDRV_FLEXCOLOR时,表示使用GUI_PORT_API接口来驱动LCD(官方库使用该方式)
使用该方式就需要修改LcdWriteReg,LcdWriteData,LcdWriteDataMultiple,LcdReadDataMultiple几个函数.

另外需要注意的是该种方式只支持一些特定的芯片,支持的芯片如下截图所示:

修改后的LCDConf_FlexColor_Template.c文件中完整函数如下所示:

static void LcdWriteReg(U16 Data) {LCD->reg = Data;// ... TBD by user
}static void LcdWriteData(U16 Data) {LCD->data = Data;// ... TBD by user
}static void LcdWriteDataMultiple(U16 * pData, int NumItems) {while (NumItems--) {// ... TBD by userLCD->data = *pData;pData++;}
}static void LcdReadDataMultiple(U16 * pData, int NumItems) {while (NumItems--) {// ... TBD by user*pData = LCD->data;pData++;}
}void LCD_X_Config(void) {GUI_DEVICE * pDevice;CONFIG_FLEXCOLOR Config = {0};GUI_PORT_API PortAPI = {0};//// Set display driver and color conversion//pDevice = GUI_DEVICE_CreateAndLink(GUIDRV_FLEXCOLOR, GUICC_M565, 0, 0);//// Display driver configuration, required for Lin-driver//LCD_SetSizeEx (0, lcddev.width , lcddev.height);LCD_SetVSizeEx(0, lcddev.width, lcddev.height);//// Orientation//Config.Orientation = GUI_SWAP_XY | GUI_MIRROR_Y;GUIDRV_FlexColor_Config(pDevice, &Config);//// Set controller and operation mode//PortAPI.pfWrite16_A0  = LcdWriteReg;PortAPI.pfWrite16_A1  = LcdWriteData;PortAPI.pfWriteM16_A1 = LcdWriteDataMultiple;PortAPI.pfReadM16_A1  = LcdReadDataMultiple;GUIDRV_FlexColor_SetFunc(pDevice, &PortAPI, GUIDRV_FLEXCOLOR_F66708, GUIDRV_FLEXCOLOR_M16C0B16);
}int LCD_X_DisplayDriver(unsigned LayerIndex, unsigned Cmd, void * pData) {int r;(void) LayerIndex;(void) pData;switch (Cmd) {case LCD_X_INITCONTROLLER: {return 0;}default:r = -1;}return r;
}
void LCD_X_Config(void) {GUI_DEVICE * pDevice;CONFIG_FLEXCOLOR Config = {0};GUI_PORT_API PortAPI = {0};//// Set display driver and color conversion//pDevice = GUI_DEVICE_CreateAndLink(&GUIDRV_FLEXCOLOR, GUICC_M565, 0, 0);//// Display driver configuration, required for Lin-driver//LCD_SetSizeEx (0, lcddev.width , lcddev.height);LCD_SetVSizeEx(0, lcddev.width, lcddev.height);//// Orientation//Config.Orientation = GUI_SWAP_XY | GUI_MIRROR_Y;GUIDRV_FlexColor_Config(pDevice, &Config);//// Set controller and operation mode//PortAPI.pfWrite16_A0  = LcdWriteReg;PortAPI.pfWrite16_A1  = LcdWriteData;PortAPI.pfWriteM16_A1 = LcdWriteDataMultiple;PortAPI.pfReadM16_A1  = LcdReadDataMultiple;GUIDRV_FlexColor_SetFunc(pDevice, &PortAPI, GUIDRV_FLEXCOLOR_F66708, GUIDRV_FLEXCOLOR_M16C0B16);
}

6 . 根据需要修改GUIConfig.c文件

该文件主要修改GUI_NUMBYTES宏,用来配置STemWin使用的SRAM大小,一般最小都需要100k空间.

#define GUI_NUMBYTES  (1024*100)

GUI_NUMBYTES默认为0x200000 = 2M, 我们的使用的stm32f767肯定不可能分配这么大的空间给STemWIN使用的.


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部