目录
一 配置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是根据硬件连接进行配置的,我使用的是正点原子开发板,
3. 接下来配置FMC时序, 这个需要根据ILI9341手册进行配置,最终配置如下图所示
(经过实际实验,即使我将读写时序的地址建立时间,数据建立时间,数据读写时间都设置为0,依然可以正常驱动LCD ,然后控制模式,选择MODE_A,B,C,D任意模式都是可以的^_^)
下图截自NT35510手册 中的 "7.6.1 Parallel Interface Characteristics"
4. 配置PB5(背光灯控制)引脚为输出模式, 该引脚输出高电平时点亮背光灯.
5. 当我们使用FMC访问外部地址时,还必须配置MPU(内存保护单元),配置如下:
6. 配置完成点击Generate Code生成代码.
stm32cubemx官方下载地址
1.添加的代码如下:
2. 头文件路径如下:
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初始化函数,这个省略,一般官方会提供.
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使用的.