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 Read
    r = 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; ;     			//开始写入GRAM
        for(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 size

void 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 user
      LCD->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使用的.

  • 11
    点赞
  • 83
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值