一.简述
在stm32单片机中对ili9341的8080通讯接口提供了一种特殊的办法——用FSMC接口来实现8080时序。在stm32单片机中FSMC外设是用来管理扩展存储器的,而lcd液晶显示屏中正好有显存这个东西,这与控制存储器的方式十分的相似,所以在这里我们利用FSMC模拟8080时序来控制液晶屏。
二.FSMC的存储器驱动简介
FSMC的外设结构图
如上图所示右边是FSMC外设相关功能的控制引脚。在本章中,我们利用STM32的FSMC外设,在NOR/PSRAM模式下,特别是借鉴了NOR_FLASH类型的模式B的时序配置,来模拟ILI9341液晶屏所需的8080接口时序,从而实现对液晶屏的控制。
FSMC的信号名 | 信号方向 | 功能 |
CLK | 输出 | 时钟(同步突发模式使用) |
A[ 25:0 ] | 输出 | 地址总线 |
D[ 15:0 ] | 输入/输出 | 双向数据总线 |
NE[ X ] | 输出 | 片选,x=1...4 |
NOE | 输出 | 输出使能 |
NWE | 输出 | 写使能 |
NWAIT | 输入 | NOR闪存要求FSMC等待的信号 |
NADV | 输出 | 地址,数据线复用时作锁存信号 |
STM32的FSMC外设来模拟LCD显示屏(如ILI9341)的8080接口时序时,一个重要的概念是“异步通信”。在FSMC的配置中,虽然它支持多种模式,包括那些可能会使用到clk
(时钟)、nwait
(等待信号)、nadv
(地址选通)等引脚的同步或特定类型的异步模式,但在模拟8080时序时,我们实际上并不需要这些引脚。原因很简单:8080接口本身就是一个基于控制信号而非时钟信号的并行接口。因此,在FSMC的设置中,我们可以将这些引脚配置为未使用或忽略它们,因为它们对于模拟8080时序来说并不是必需的。
FSMC的内部存储器读取方式
FSMC访问存储器地址的方式与用i2c访问eeprom及spi访问flash的方式并不一样。后者是通过总线给存储器发送地址获得数据,这个方式在程序里地址和数据都要分开使用不同的变量存储,在访问时同时需要用代码控制发送读写命令。前者是FSMC外接存储器,而存储器的存储单元是映射到stm32的内部寻址空间的,此时在程序里只要定义指向该地址的指针,就可以直接通过指针直接修改该存储单元的内容,FSMC外设会自动完成数据的访问过程。
FSMC的地址映射
上面就是将外部地址映射到stm32单片机的地址映射图。在这里FSMC会自动输出地址和数据。 在图的左边是stm32的存储空间的分配,右侧是stm32 FSMC外设的地址映射。在这里FSMC的NOR/PSRAM/SRAM/NAND FLASH以及PC卡的地址都在External RAM地址空间内。
FSMC将External RAM存储区域分为4个bank区域,并给对应的存储器类型分配了其地址范围。而每个bank的内部又分成了4个小块,每个小块有对应的控制引脚连接片选信号。比如FSMC_NE[4:1]信号线可以用来选择bank1内的4小块地址区域如下图所示,当stm32访问0x6c000000-0xfffffff地址空间时,就会访问bank1的第1小块区域,相应的FSMC_NE1信号线会输出控制信号。
三.FSMC 控制异步NOR FLASH的时序简介
在上一节里面我们宏观的介绍了FSMC如何驱动存储器,在这一节里我们就要从微观的视角来了解FSMC控制异步NORFLASH的时序。FSMC外设支持输出多种不同的时序来控制不同的存储器,它具有ABCD4种模式。而我们只用到了B模式的功能,这里我们仅针对模式B进行讲解。
FSMC写NOR_FLASH的时序图
在stm32发出访问某个指向外部存储器地址时。FSMC外设会根据控制信号线产生时序访问存储器,上图就是访问外部异步NORFLASH(模式B)时FSMC外设的读写时序。以右图读时序为例,该图由一个存储器操作周期由地址建立周期(ADDSET),数据建立周期(DATAST),和2个HCLK周期组成。在地址建立周期的过程中,地址线发出要访问的地址,数据信号线指示要读取地址的高低字节部分,片选信号使能存储器芯片。地址建立的周期结束以后,读使能信号线发出读使能信号,接着存储器通过数据信号线把目标数据传输给FSMC,FSMC将其交给内核。
根据stm32对寻址空间的地址映射,地址0x60000000~0x9FFFFFFF是映射到外部存储器的,
将这些地址分配给NORFALSH,PSAM这类可直接寻址的器件。假如FSMC外设被配置成正常工作的形式,并且外接了NORFALSH时,若向0x60000000地址写入数据如0xABCD,FSMC会自动在各信号线上产生相应的电平信号写入数据。FSMC会控制片选信号NE1选择相应的NOR芯片,然后使用地址线A[25:0]输出0x60000000,在NWE使能信号线上发出低电平的写使能信号,而要写的数据信号0xABCD则从数据线D[15:0]输出,然后数据就被保存到NORFLASH中了。
四.用FSMC模拟8080时序
上一节我们讲解了FSMC是如何控制异步NORFLASH的时序的,其与8080时序十分的相似,只有FSMC的地址线与8080的D/CX线不一样。
FSMC的NOR和8080信号线对比
在上面的表格里我们可以看到,FSMC和8080的前4种信号线都是完全一样的,只有FSMC的地址信号线A[25:0]和8080的数据/命令选择线D/CX有区别。
D/CX线:它为高电平的时候表示数值,低电平的时候表示命令
所以我们只要修改A地址线不同情况产生对应的电平,就可以用FSMC来产生8080接口所要的时序了。
FSMC模拟8080时序的操作
模拟8080时序,我们将FSMC的A0地址线(可以使用A1/A2等地址线)和ili9341芯片8080接口的D/CX信号线连接,当A0为高电平时(即D/CX为高电平)数据线D[15:0]的信号会被ili9341理解为数值,当A0为低电平时(即D/CX为低电平)信号会被理解为命令。
因为FSMC会自动产生地址信号,所以当FSMC向0x6xxxxxx1,0x6xxxxxx3,0x6xxxxxx5等这些 奇数 地址写入数据时,地址最低位的值均为1,所以它会控制地址线A0(D/CX)输出高电平,此时通过数据线传输的信号就会被理解为数值。当向0x6xxxxxx0,0x6xxxxxx2,0x6xxxxxx4等这些偶数地址写入数据时,地址最低位的值均为0,所以它会控制地址线A0(D/CX)输出低电平,这个时候通过数据线传输的信号会被理解为命令。
有了这个基础,只要配置好 FSMC 外设,然后在代码中利用指针变量,向不同的地址单元写入数 据,就能够由 FSMC 模拟出的 8080 接口向 ILI9341 写入控制命令或 GRAM 的数据了。
五.代码详解
FSMC的初始化配置重点是将FSMC配置成异步MORFLSH使用的模式B,还要注意FSMC的时钟的配置。在异步的时序里只有地址与数据建立时间是有效的,在这里1个HCLK的时钟周期的时钟频率为72MHZ。
FSMC的初始化配置的相关代码
ili9341的时序参数图和ili9341的时序要求参数图
由上图我们可以知道ili9341的写周期最小twc=66ns,而读周期最小为trdl+trod=45+20=65ns。(对应读周期表中有参数要一个要求为trcfm和trc分别为450ns及160ns,但经过测试并不需要按照他们的指标要求)
FSMC的读写时序
我们结合ili9341的时序要求和FSMC的配置图,代码中按照读写时序周期均要求至少66ns来计算,配置为ADDSET=1和DATST=4,把时间单位1/72微秒(即1000/72纳秒)代入。
读写周期的时间配置:
注意:将这两个参数值写入到FSMC后,其控制的读写周期 都比ili9341的最低要求值大。(这两个参数值可以适当的减小)
计算控制液晶屏使用时的地址
当FSMC初始化完成后,FSMC模拟8080时序访问地址时,对应FSMC_Ax引脚会输出高电平(表示传输的是数据),FSMC_Ax引脚会输出低电平(表示传输的是命令)。
FSMC和8080的连接简图
计算地址的过程
1.确定地址范围,当访问0x60000000~0x63FFFFFF地址时,FSMC会对外产生片选有效的访问时序(FSMC_NE1作为8080_CS片选信号)。
2.在以上地址范围内,FSMC_A16输出高电平地址——数据;FSMC_A16输出低电平地址——命令(FSMC_A16作为数据/命令选择RS信号)。
•
要使
FSMC_A16
地址线为高电平,实质是访问内部
HADDR
地址的第
(16+1) 位
为
1
即可,
使用
0X6000 0000~0X63FF FFFF
内的任意地址,作如下运算:
使
FSMC_A16
地址线为高电平:
0X6000 0000 |= (1«(16+1)) = 0x6002 0000
•
要使
FSMC_A16
地址线为低电平,实质是访问内部
HADDR
地址的第
(16+1) 位
为
0
即可,
使用
0X6000 0000~0X63FF FFFF
内的任意地址,作如下运算:
使
FSMC_A16
地址线为低电平:
0X6000 0000 &= ~ (1«(16+1)) = 0x6000 0000
|
上述计算在工程代码中的应用
当 STM32 访问内部的 0x6002 0000 地址时,FSMC 自动输出 时序,且使得与液晶屏的数据/命令选择线 RS(即 D/CX) 相连的 FSMC_A16 输出高电平,使得液 晶屏会把传输过程理解为数据输;类似地,当 STM32 访问内部的 0X60000000 地址时,FSMC 自动输出时序,且使得与液晶屏的数据/命令选择线 RS(即 D/CX) 相连的 FSMC_A16 输出低电平, 使得液晶屏会把传输过程理解为命令传输。
/********** ILI934
命令
********************************/
#define
CMD_SetCoordinateX
0x2A
//
设置
X
坐标
#define
CMD_SetCoordinateY
0x2B
//
设置
Y
坐标
/**
* @brief 在 ILI9341
显示器上开辟一个窗口
* @param usX
:在特定扫描方向下窗口的起点
X
坐标
* @param usY
:在特定扫描方向下窗口的起点
Y
坐标
* @param usWidth
:窗口的宽度
* @param usHeight
:窗口的高度
* @retval
无
*/
void
ILI9341_OpenWindow
(
uint16_t
usX,
uint16_t
usY,
uint16_t
usWidth,
uint16_t
usHeight )
{
ILI9341_Write_Cmd ( CMD_SetCoordinateX );
/*
设置
X
坐标
*/
ILI9341_Write_Data ( usX
>>
8
);
/*
先高
8
位,然后低
8
位
*/
ILI9341_Write_Data ( usX
&
0xff
);
/*
设置起始点和结束点
*/
ILI9341_Write_Data ( ( usX
+
usWidth
-
1
)
>>
8
);
ILI9341_Write_Data ( ( usX
+
usWidth
-
1
)
&
0xff
);
ILI9341_Write_Cmd ( CMD_SetCoordinateY );
/*
设置
Y
坐标
*/
ILI9341_Write_Data ( usY
>>
8
);
ILI9341_Write_Data ( usY
&
0xff
);
ILI9341_Write_Data ( ( usY
+
usHeight
-
1
)
>>
8
);
ILI9341_Write_Data ( ( usY
+
usHeight
-
1
)
&
0xff
);
}
|
这些代码的本质起始就是对存储器地址内容修改,但是我们要注意的是数据和命令一个要高电平一个要低电平。
// uint32_t temp = 0x6C000000; // //向LCD发送命令 |