ILI9341的8080通讯接口时序可以由STM32使用普通 I/O 接口进行模拟,但这样效率太低,STM32 提供了一种特别的控制方法,使用FSMC接口实现8080时序。
由于 FSMC 外设可以用于控制扩展的外部存储器,而 MCU 对液晶屏的操作实际上就是把显示数据写入到显存中,与控制SRAM 存储器非常类似,且 8080 接口的通讯时序完全可以使用 FSMC 外设产生,因而非常适合使用 FSMC控制液晶屏。
FSMC结构框图:
LCD — 液晶显示:
NADV引脚是专门拥堵地址 / 数据线复用时做锁存信号,如下。当我们不需要地址 / 数据线复用时,NADV就不用管。
NADV由以下寄存器位来配置:
液晶屏使用模式B的写操作时序:
模式B读操作时序:
根据 STM32 对寻址空间的地址映射,地址 0x6000 0000 ~0x9FFF FFFF 是映射到外部存储器的,而其中的 0x6000 0000 ~0x6FFF FFFF 则是分配给 NOR FLASH、PSRAM这类可直接寻址的器件。
当 FSMC 外设被配置成正常工作,并且外部接了 NOR FLASH 时,若向 0x60000000地址写入数据如 0xABCD,FSMC 会自动在各信号线上产生相应的电平信号,写入数据。FSMC 会控制片选信号 NE1 选择相应的 NOR 芯片,然后使用地址线 A[25:0]输出0x60000000,在 NWE 写使能信号线上发出低电平的写使能信号,而要写入的数据信号0xABCD 则从数据线 D[15:0]输出,然后数据就被保存到 NOR FLASH 中了。
使用FSMC模拟8080时序:
一个非常重要的问题:就是地址对齐。
回想之前在SRAM中使用到的一个函数:
/* 向整个SRAM写入数据 16位 */
for (counter = 0; counter < IS62WV51216_SIZE/2; counter++)
{
*(__IO uint16_t*) (Bank1_SRAM3_ADDR + 2*counter) = (uint16_t)(uhWritedata_16b + counter);
}
这个函数能够正常运行,没有问题。但是,值得注意的是,SRAM的数据线为16位,也就是说每个地址对应2个字节。这里在代码中直接写的是Bank1_SRAM3_ADDR + 2*counter,那么就会递增两个地址(4字节),是不是出了问题??应该只递增1地址才对啊?但是,无论是这个代码 or 下面的代码,都能够正确运行,非常奇怪。
/**
* @brief 以“字”为单位向sdram写入数据
* @param pBuffer: 指向数据的指针
* @param uwWriteAddress: 要写入的SRAM内部地址
* @param uwBufferSize: 要写入数据大小
* @retval None.
*/
void SRAM_WriteBuffer(uint32_t* pBuffer, uint32_t uwWriteAddress, uint32_t uwBufferSize)
{
__IO uint32_t write_pointer = (uint32_t)uwWriteAddress;
/* 循环写入数据 */
for (; uwBufferSize != 0; uwBufferSize--)
{
/* 发送数据到SRAM */
*(uint32_t *) (Bank1_SRAM3_ADDR + write_pointer) = *pBuffer++;
/* 地址自增*/
write_pointer += 4;
}
}
这里竟然地址递增了4,应该会跳过8字节,但程序依然正常运行。问题出在这里:
这里感觉很不好理解,简单地分析如下:
对于16位SRAM,FSMC地址线要向右移一位(非常重要)。
以0x6800 0000这个地址为例,它分解成二进制是0110 1000 0000 0000 0000 0000 0000 0000,由于一个BANK是64M的地址空间,而2^25=64M,故0x6800 0000的位[25:0]是FSMC向外部SRAM传递的真实地址,对于0x6800 0000,FSMC向外部SRAM发的地址是位[25:0],即00 0000 0000 0000 0000 0000 0000。
同理,对于0x6800 0002,FSMC向外部SRAM发送的地址是00 00000000 0000 0000 0000 0010。若FSMC不自动右移一位,这个地址明显发错了,因为期望读取的SRAM地址为0x0000 0001中的数据。
为了解决这一问题,当在初始化FSMC时,若选择外部SRAM为16位,则FSMC在向外部SRAM发地址时,会自动右移一位,例如刚才的0x6800 0002,FSMC在向外部发SRAM地址时,00 0000 0000 0000 0000 0000 0010会自动右移一位,变成00 0000 00000000 0000 0000 0001,即0x0000 0001,该地址正好是期望的外部SRAM地址。接着,外部SRAM从地址为0x0000 0001中取出16位数据传送给FSMC,由FSMC将这个16位数据保存在以映射地址0x6800 0002起始的两个8位存储单元中。
地址映射如下:
这样的话,当16位数据宽度时,地址的问题解决了,还有一个问题,是往高字节写入还是低字节写入呢?
这也就是NBL0和NBL1的作用了,如果你要进行字节操作 :
如stm32发送地址0x0001读取一个字节。右移一位对应的是sram地址0x0000处的16位数据, FSMC会根据A0(最后一根地址线)来控制NBL0和NBL1。当A0 = 1时,读取高字节数据(仅NBL1有效);A0 = 0时,读取低字节数据仅NBL0有效),当进行16位读写时,NBL0和NBL1都有效。
**********************************************************************************************************************************************
经过了上面的分析,再来重新观察数据的写入过程:
当地址为0x6800 0000,会访问到SRAM的第0个16位地址,而此时A0 = 0(低字节有效),实际会访问的是16位 0地址的低字节;当地址为0x6800 0001时,A0 = 1,访问16位 0地址的高字节。依次。
因此,想让地址线的最后一位产生0或1,应该在前一位做出改变(stm32会自动右移):
结论:
本工程中使用 FSMC_A16 地址线作为命令/数据选择线 RS 信号,所以在地址范围内,再选择出使得 FSMC_A16 输出高电平的地址,即可控制表示数据,选择出使得FSMC_A16输出低电平的地址,即可控制表示命令。
要使 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 访问内部的 0X6000 0000 地址时,FSMC 自动输出时序,且使得与液晶屏的数据/命令选择线 RS(即 D/CX)相连的 FSMC_A16输出低电平,使得液晶屏会把传输过程理解为命令传输。
使用这两个地址发送命令和数据:
上一篇:stm32专题二十一:LCD控制原理
下一篇:stm32专题二十一:FSMC液晶显示和扫描方向
推荐阅读最新更新时间:2024-11-13 10:27