前言
在学习STM32的过程中,刚好学到了LCD屏,我使用的是STM32F103ZET6,屏幕是正点原子的。但是在我自己新建工程点亮显示LCD屏时遇到了很多问题。解决之后分享在此,希望能帮助到遇到此困惑的朋友。
想要快速驱动LCD屏请直接跳转到CubeMX配置
FSMC简单介绍
FSMC全名叫可变静态存储控制器(Flexible Static Memory Controller,FSMC)是单片机的一种接口,它能够连接同步或异步存储器、16位PC存储卡和LCD模块。FSMC连接的所有外部存储器共享地址、数据和控制信号,但有各自的片选信号,所以,FSMC一次只能访问一个外部器件。
FSMC接口用于驱动外部存储器,也可以用于驱动8080接口的TFT LCD。
从FSMC的角度来看,外部存储器被划分为4个固定大小的存储区域,每个存储区域大小为256MB,各个区域的作用描述如下。
Bank1可连接多达4个NOR Flash或PSRAM/SRAM存储器件,因为它被分为4个子区(subbank),每个子区容量为64MB,有专用的片选信号。这些子区适合于连接TFT LCD。
Bank 1 -NOR/PSRAM 1,片选信号NE1。
Bank 1 -NOR/PSRAM 2,片选信号NE2。
Bank 1 -NOR/PSRAM 3,片选信号NE3。(开发板上用于连接外部SRAM)
Bank 1 -NOR/PSRAM 4,片选信号NE4。(开发板上用于连接TFT LCD)
Bank2 和 Bank3用于访问NAND Flash存储器,每个存储区域连接一个设备。
Bank4 用于连接PC卡设备。
TFT LCD简单介绍
TFT LCD 即薄膜晶体管(Thin Film Transistor)LCD,具有辐射低、功耗低、全彩色等优点, 是各种电子设备常用的一种显示设备。TFT LCD(后面也会简称为LCD)通常使用标准的8080并口,这种接口有16位数据线,还有几根控制线。TFTLCD的8080并行接口线的功能如下图所示。
CubeMX配置
FSMC子区配置
开发板上LCD原理图
通过原理图也可知要将子区配置为NE4。
LCD_Register Select寄存器根据原理图RS位可知设为A10.。
同时要将LCD_BL对应的引脚设置为输出模式即可(背光引脚)。(我这里对应的是PB0)
FSMC相关参数配置
RCC时钟配置
配置时钟树时默认将HCLK配置为72MHz(最大)即可。
LCD驱动程序改写
因为每块开发板对应的LCD历程会有所不一样,且为了保证不同芯片驱动的LCD都能够点亮。所以请下载正点原子资料下载中心的STM32F103最小系统板的TFT LCDHAL库历程进行改写。
将LCD相关文件导入到新建的例程中。
头文件的替换
(1)lcd.c文件中包含lcd.h、font.h和main.h即可
(2)lcd.h文件中只需包含main.h即可
基础数据类型的替换
使用快捷健CTRL+F来到替换界面。两个文件中将u8、u16和u32替换成uint8_t、uint16_t和uint32_t。
再将vuint16_t替换成__IO uint16_t。
函数的替换
采用上一点的方法将所有的delay_ms和delay_us替换成HAL_Delay(实测不影响程序运行)。
注释lcd.c中重复的代码
此环节说是重复代码注释,其实主要目的是为了便于移植程序。因为fsmc.
文件和原始的lcd.c文件中会有相同的初始化部分。
1.注释LCD_Init()函数最开始的fsmc初始化部分
即将以下部分的代码全部注释(此部分在MX_FSMC_Init()函数中已执行过)。
// GPIO_InitTypeDef GPIO_Initure;
// FSMC_NORSRAM_TimingTypeDef FSMC_ReadWriteTim;
// FSMC_NORSRAM_TimingTypeDef FSMC_WriteTim;
// __HAL_RCC_GPIOB_CLK_ENABLE(); //开启GPIOB时钟
// GPIO_Initure.Pin=GPIO_PIN_0; //PB0,背光控制
// GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出
// GPIO_Initure.Pull=GPIO_PULLUP; //上拉
// GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH;//高速
// HAL_GPIO_Init(GPIOB,&GPIO_Initure);
// TFTSRAM_Handler.Instance=FSMC_NORSRAM_DEVICE;
// TFTSRAM_Handler.Extended=FSMC_NORSRAM_EXTENDED_DEVICE;
// TFTSRAM_Handler.Init.NSBank=FSMC_NORSRAM_BANK4; //使用NE4
// TFTSRAM_Handler.Init.DataAddressMux=FSMC_DATA_ADDRESS_MUX_DISABLE; //地址/数据线不复用
// TFTSRAM_Handler.Init.MemoryType=FSMC_MEMORY_TYPE_SRAM; //SRAM
// TFTSRAM_Handler.Init.MemoryDataWidth=FSMC_NORSRAM_MEM_BUS_WIDTH_16; //16位数据宽度
// TFTSRAM_Handler.Init.BurstAccessMode=FSMC_BURST_ACCESS_MODE_DISABLE; //是否使能突发访问,仅对同步突发存储器有效,此处未用到
// TFTSRAM_Handler.Init.WaitSignalPolarity=FSMC_WAIT_SIGNAL_POLARITY_LOW; //等待信号的极性,仅在突发模式访问下有用
// TFTSRAM_Handler.Init.WaitSignalActive=FSMC_WAIT_TIMING_BEFORE_WS; //存储器是在等待周期之前的一个时钟周期还是等待周期期间使能NWAIT
// TFTSRAM_Handler.Init.WriteOperation=FSMC_WRITE_OPERATION_ENABLE; //存储器写使能
// TFTSRAM_Handler.Init.WaitSignal=FSMC_WAIT_SIGNAL_DISABLE; //等待使能位,此处未用到
// TFTSRAM_Handler.Init.ExtendedMode=FSMC_EXTENDED_MODE_ENABLE; //读写使用不同的时序
// TFTSRAM_Handler.Init.AsynchronousWait=FSMC_ASYNCHRONOUS_WAIT_DISABLE; //是否使能同步传输模式下的等待信号,此处未用到
// TFTSRAM_Handler.Init.WriteBurst=FSMC_WRITE_BURST_DISABLE; //禁止突发写
//
// //FMC读时序控制寄存器
// FSMC_ReadWriteTim.AddressSetupTime=0x06; //地址建立时间(ADDSET)为7个HCLK 13.8ns*7=96.6ns
// FSMC_ReadWriteTim.AddressHoldTime=0;
// FSMC_ReadWriteTim.DataSetupTime=26; //数据保存时间为27个HCLK =13.8*27=372.6ns
// FSMC_ReadWriteTim.AccessMode=FSMC_ACCESS_MODE_A;//模式A
// //FMC写时序控制寄存器
// FSMC_WriteTim.BusTurnAroundDuration=0; //总线周转阶段持续时间为0,此变量不赋值的话会莫名其妙的自动修改为4。导致程序运行正常
// FSMC_WriteTim.AddressSetupTime=3; //地址建立时间(ADDSET)为4个HCLK =55.2ns
// FSMC_WriteTim.AddressHoldTime=0;
// FSMC_WriteTim.DataSetupTime=0x06; //数据保存时间为13.8ns*7个HCLK=96.6ns
// FSMC_WriteTim.AccessMode=FSMC_ACCESS_MODE_A; //模式A
// HAL_SRAM_Init(&TFTSRAM_Handler,&FSMC_ReadWriteTim,&FSMC_WriteTim);
// HAL_Delay(50); // delay 50 ms
2.注释函数void HAL_SRAM_MspInit(SRAM_HandleTypeDef *hsram)
此函数是对fsmc相关的引脚进行GPIO配置。
收尾工作
1.将LCD_Init()函数最后面的代码注释(如下)。
LCD_LED = 1;
改为一下代码点亮背光(让屏幕亮起来)。
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,1);
2.将LCD_Init()函数中的printf代码行注释,如下。
printf(" LCD ID:%x\r\n", lcddev.id); //打印LCD ID
因为原历程使用串口一进行串口调试,要是没有初始化串口一的话,运行时会一直卡在这里。
3.最后在主函数中包含lcd.h头文件,添加以下代码即可运行。
LCD_Init();
LCD_ShowString(100,100,100,24,24,"114514");
4.大家以后要移植LCD程序时,复制相关文件到对应工程,包含头文件,调用初始化函数即可使用。节省时间!
源码我放在网盘了,有需要的可以自取:
链接:https://pan.baidu.com/s/1EfmoR1q8q69twGvVbt93_Q?pwd=g8tk
提取码:g8tk