【海东青电子原创文章,转载请注明出处:https://www.jianshu.com/p/62d1ef042d0c】
TouchGFX是一套图形中间件代码,属于嵌入式软件范畴,它不可避免的要跟STM32的硬件资源发生联系,在使用STM32CUBEMX配置TouchGFX时,要配置FMC(FSMC)、显示接口(DSI或LTDC等)、QSPI等,就是这个原因。归纳起来,TouchGFX主要用到的MCU的资源有:
1)内存(RAM):为了处理图像,需要RAM来作为图像的缓存(buffer)。这个RAM,可以是MCU片内的,也可以是片外扩展的。LCD的显示像素一般都比较多,需要的RAM也比较大,所以TouchGFX应用基本上都需要MCU扩展RAM,而MCU与RAM最方便的连接方式就是通过FMC(或FSMC)接口。
2)显示接口(DSI或LTDC等)。
3)代码和静态图形存储区。实际应用中,可能要用到不止一个背景图形,这些图形资源被编译到代码中,存储在flash里。如果MCU的片内flash不够用,就需要外扩flash,常用的接口是QSPI。
了解了上述几个接口,再使用CUBEMX配置TouchGFX时,就容易的多了。我们先从配置FMC接口开始,并且从F103具有的FSMC开始,因为它更简单一些。
FSMC,Flexible Static Memory Controller,灵活的静态存储控制器,是MCU里的一个硬件结构,它的作用主要是简化了连接外设的接口。如果使用过IIC总线,就会知道,大致有2种使用IIC的方式:一种是用2根IO口线模拟IIC的时序、用户自己写相关的代码。另一种方法是,利用MCU提供的IIC总线控制器,先配置好IIC有关的一些控制寄存器(可以使用CUBEMX),然后通过HAL函数(或中断)直接读写IIC数据即可。后者是“流程化”的,是IIC操作的标准流程,使用起来更方便,也更稳定。FSMC于此类似,它包含了连接外设(静态RAM,NOR,NAND)所必须的接口信号:
1)片选信号CS;
2)数据总线D0-D15(16bit,也支持8-bit);
3)地址总线(可以跟数据总线复用,接LCD显示屏时,经常用来配置LCD的指令信号,见下面例子);
4)读信号;
5)写信号。
本例中,我们将实现STM32F103通过FSMC接口对 ILI9325 (ILI9320) 显示屏的驱动,接口示意图如下:
图一对于 ILI9325 ,还需要一个reset复位信号,将MCU的一个GPIO配置成输出模式,并与 LCD 连接即可。
先来看看STM32F103的FSMC的结构(从103的用户手册中截图):
图二为了简化,我们只关心 NOR/SRAM 部分:
图三其中,做了标记的信号线是实例中要用到的。下面结合一个具体的例子来说明这些信号线的用法。本例是一个STM32F103通过FSMC连接了一个 ILI9325 LCD。在STM32F103板子上,MCU与LCD接口电路如图:
图四MCU电路:
图五16根数据线D0-D15不多说了,这里重点说一下几个控制信号:
图六上表说明了读、写、片选信号是如何对应MCU的管脚编号的(是pin脚的复用功能,通过CUBEMX配置FSMC时,这些pin的复用功能被自动配置好了)。RS控制信号需要单独解释一下:RS用来告诉 ILI9325 控制器,当前收到MCU送来的数据是“命令”、还是“数据”(详见 ILI9325 数据手册),MCU与RS信号连接有2种方式:一种是通过一个MCU的GPIO与RS相连,每次MCU向 ILI9325 发送数据之前,都根据是发送命令、还是数据,对应地设置GPIO为0或1;另一种方法是将MCU的一个地址线(本例中是A16)与RS相连,并且在代码中使用与该地址线(A16)对应的“地址”来访问 ILI9325 即可(下面的代码会详述如何实现)。A16对应的pin如下:
图七下面开始使用CUBEMX配置STM32F103的FSMC:
图八使用外部晶振:
图九FSMC的配置:
图十FSMC的NOR(这里用来驱动LCD)区块部分,内部又分成了4个“子区块”:
图十一这里要注意FSMC占用的地址,这是一个32位的地址,连接不同的外设,FSMC对应地使用不同的地址,比如连接NOR的话,MCU内部地址是从0x6000 0000 开始的,如果连接的是NAND,则地址是0x7000 0000 。而NOR的内部又分成了4个部分,起始地址由32位地址的bit[27:26]决定,用二进制表示为:
Bank1:0110-0000 0000-0000 0000-0000 0000-0000 ,即 60 00 00 00;
Bank1:0110-0100 0000-0000 0000-0000 0000-0000 ,即 64 00 00 00;
Bank2:0110-1000 0000-0000 0000-0000 0000-0000 ,即 68 00 00 00;
Bank3:0110-1100 0000-0000 0000-0000 0000-0000 ,即 6c 00 00 00;
本例中使用的是Bank1,则地址范围为0x6000 0000-0x63ff ffff (共64M个byte地址)。对应的,访问这个地址空间时,FSMC自动输出NE1片选信号(即,PD7输出0,见图六)。
再来看一个特殊地址:0x6002 0000,二进制为 0110-0000 0000-0010 0000-0000 0000-0000,注意,这里的bit[17]特意设置为1 。FSMC的地址,是“字节地址”,从低到高的地址依次为:
0x6000 0000,0x6000 0001,0x6000 0002,......,共有64M个地址。
但是,当把FSMC配置为16-bit“地址模式”时(由FSMC控制寄存器 FSMC_BCR1 的 MWID 位来配置),显然就应该只有32M个地址了。此时,FSMC做了一个特殊处理:“丢弃”地址的bit-0,从bit-1开始对地址计数(相当于只计算偶数地址,总共有32M个地址),FSMC在实际输出地址时,将地址的值右移一位(相当于除以2,变成了偶数地址),输出到实际的地址线上。F103的文档上是这么写的:
图十二以地址:0x6002 0000为例,FSMC为16-bit模式时,地址bit[17]的1右移一位后输出到地址线,即A16对应的pin(PD11,见图七)将输出1,也就是RS信号线将为1 。罗嗦了这么多,实际上就是想说明一件事:对 ILI9325 写数据时,使用地址 0x6002 0000 来访问;对 ILI9325 写命令时,使用地址 0x6000 0000 来访问!
最后配置LCD的复位管脚:
图十三在CUBEMX中生成KEIL的代码,在main.c中添加针对 ILI9325 读写的函数:
main.c
#define LCD_Data_Addr ((uint32_t)0x60020000) //写数据地址
#define LCD_Reg_Addr ((uint32_t)0x60000000) //写命令地址
//写索引(命令)寄存器
void LCD_WriteIndex(unsigned int index)
{
*(volatile uint16_t *)(LCD_Reg_Addr) = index;
}
//读寄存器
uint16_t LCD_ReadReg(uint16_t reg)
{
uint16_t ret;
LCD_WriteIndex(reg);
ret = *(volatile uint16_t *)(LCD_Data_Addr);
return ret;
}
程序初始化之后,复位LCD,然后读取 ILI9325 的版本,如果能正确读出数据 “9325”,即表明FSMC配置正确:
//复位LCD
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, GPIO_PIN_RESET);
HAL_Delay(500);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, GPIO_PIN_SET);
//读
tmp = LCD_ReadReg(0x0000); //0000为编码,详见 ILI9325手册
//tmp should be 0x9325 now!!!
if(tmp == 0x9325)
{
//led1 on
HAL_GPIO_WritePin(GPIOB , GPIO_PIN_8, GPIO_PIN_RESET);
} else
{
//led2 on
HAL_GPIO_WritePin(GPIOB , GPIO_PIN_9, GPIO_PIN_RESET);
}
printf("ret=0x%x\n", tmp);
//通过TM_SendChar() 在KEIL中观察变量数值,详见《STM32的ITM跟踪调试功能介绍及实现(一)KEIL篇》和《STM32的ITM跟踪调试功能介绍及实现(四)printf() 篇》。
......
运行结果:
图十四本例只是为了说明如何配置FSMC,所以只实现了对 ILI9325 的读出数据。增加对 ILI9325 的初始化代码后,就可以容易地实现 ILI9325 显示图像。
有了FSMC配置的基础知识,将方便我们进一步理解 STM32F746G-DISCO 板子上使用FMC访问外部DRAM的配置过程,这个内容将在下一节中介绍 -- STM32硬件基础--FMC读写片外SDRAM。
网友评论