摘要:使用FSMC模拟8080时序,进行LCD芯片的基本驱动过程和代码。
1. FSMC介绍
1.1 概念
FSMC,Flexible Static Memory Controller,灵活的静态存储控制器。
用途:用于驱动SRAM,NOR FLASH,NAND FLASH及PC卡类型的存储器。
配置好FSMC,定义一个指向这些地址的指针,通过对指针操作就可以直接修改存储单元的内容,FSMC自动完成读写命令和数据访问操作,不需要程序去实现时序。
这里需要配置FSMC外设,模拟8080时序。
FSMC和FMC的区别是,后者可以控制SDRAM等,只在F4/F7/H7系列STM32芯片中
1.2 框图
最左边是时钟控制逻辑,FMC挂在AHB总线上(高速总线),时钟来自HCLK
中间是STM32内部是FMC控制单元,包括:
- FSMC配置寄存器
- NOR/PSRAM控制器
- NAMD/PC卡控制器
右边是通信引脚,包括不同存储器要用到的和公共的。
1.3 通信引脚
用于连接硬件设备的引脚。
FSMC信号 | 信号方向 | 功能 |
---|---|---|
FSMC_NE[x] | 输出 | 片选引脚,x=1…4,每个对应不同的内存块 |
FSMC_CLK | 输出 | 时钟(同步突发模式使用) |
FSMC_A[25:0] | 输出 | 地址总线 |
FSMC_D[15:0] | 输出/输入 | 双向数据总线 |
FSMC_NOE | 输出 | 输出使能(“N”表明低电平有效信号) |
FSMC_NWE | 输出 | 写使能 |
FSMC_NWAIT | 输入 | NOR闪存要求FSMC等待的信号 |
FSMC_NADV | 输出 | 地址、数据线复用时作锁存信号 |
LCD使用的是类似异步、地址与数据线独立的SRAM控制方式,因此使用的信号有NE, A, D, NOE, NWE
2. FSMC驱动LCD的方法(硬件层面)
2.1 FSMC时序(NOR/PSRAM)
NOR/PSRAM控制器使用的异步时序:
访问模式 | 对应的外部存储器 | 时序特性 |
---|---|---|
模式1 | SRAM/CRAM | OE在读时序片选过程不翻转,有NBL信号,无NADV信号 |
模式A | SRAM/PSRAM(CRAM) | OE在读时序片选过程翻转,有NBL信号,无NADV信号 |
模式B/2 | NOR FLASH | OE在读时序片选过程不翻转,无NBL信号,有NADV信号 |
模式C | NOR FLASH | OE在读时序片选过程翻转,无NBL信号,有NADV信号 |
模式D | 带地址扩展的异步操作 | OE在读时序片选过程翻转,无NBL信号,有NADV信号,存在地址保存时间 |
使用模式A通信LCD。(SRAM, 读使能需要上升沿触发)
模式A和8080时序对比:
设置ADDSET和DATASET参数来设置FSMC参数
2.2 LCD所需要的时序(ILI9341.pdf p232)
3 FSMC地址映射(软硬件接口)
3.1 STM32 内部地址和FSMC_NE/A/D信号线的关系
使用FSMC外接存储器,其存储单元是映射到STM32的内部寻址空间的。从FSMC角度看,可以把外部存储器划分为固定大小为256M字节的四个存储块。如下图所示。
其中,FSMC存储块1被分为4个区,每个区管理64M字节空间。
BANK1选区 | 片选信号 | 地址范围 | HADDR | |
---|---|---|---|---|
[27:26] | [25:0] | |||
第1区 | FSMC_NE1 | 0x6000 0000 ~ 0x63FF FFFF | 00 | FSMC_A[25:0] |
第2区 | FSMC_NE2 | 0x6400 0000 ~ 0x67FF FFFF | 01 | FSMC_A[25:0] |
第3区 | FSMC_NE3 | 0x6800 0000 ~ 0x6BFF FFFF | 10 | FSMC_A[25:0] |
第4区 | FSMC_NE4 | 0x6C00 0000 ~ 0x6FFF FFFF | 11 | FSMC_A[25:0] |
FSMC_A的26位对应4MB = 2^26Byte的数据空间。
地址映射的表现就是,如果在程序中访问上述地址,向这些地址写入值,能够直接根据地址控制NE和A引脚的电平,然后将数据写入D引脚,通过FSMC直接完成数据传输。
FSMC是连接外部存储器的CPU外设。NE是SRAM/CRAM这类外部存储器专用的引脚,而A/D是通用的。相当于,FSMC_A连接外部存储器(如LCD)的地址引脚,FSMC_D连接外部存储器的数据引脚,配置好以后,直接读写CPU的对于地址,就可以完成对外部存储器的读写。
3.2 HADDR和FSMC_A的关系
HADDR总线是转换到外部存储器的内部AHB地址线,简单来说就是从CPU通过AHB总线到外部信号线之间的关系。
3.1说过,FSMC的地址对应的是字节空间(即一个地址对应一个字节的数据);HADDR是字节地址,而存储器访问不都是按字节访问,接到存储器的地址线与其数据宽度相关。
例如在LCD中,数据位就是16位。数据宽度和地址线连接方法的关系如下。
地址少了一位,即变成了1/2,现在一个地址位能对应两个字节的数据了。
1字节(Byte) = 8位(bit)
3.3 地址的计算(以LCD为例)
上面说过,FSMC_A是对应地址的,对于LCD来说,CPU需要访问的地址只有两个:命令和数据,也就是说,一个FSMC_A引脚代替RS就足够了。
正点原子精英版 v2中,LCD使用NE4作为CS线,FSMC_A10作为RS线,以下计算使用这两个引脚为例。
当FSMC_A10为高电平时(即RS为高电平),FSMC_D[15:0]被理解为数据。当FSMC_A10为低电平时(即RS为低电平),FSMC_D[15:0]被理解为命令。
LCD命令/数据地址的计算方法如下。
- 确认FSMC_NE4的基地址:0x6C000000
- 确认FSMC_A10高电平时对应HADDR的地址值(注意偏移*2): 2^10 *2 = 0x0800
- 确认LCD地址
- 命令:RS置0,
#define FSMC_ADDR_CMD ((uint32_t) 0x6C000000)
- 数据:RS置1,
#define FSMC_ADDR_DATA ((uint32_t) 0x6C000800)
- 命令:RS置0,
4 FSMC相关寄存器(软件层面)
碎碎念:在了解寄存器之前,想一想在硬件层面连接完成后,FSMC寄存器要控制什么东西?首先肯定要确认访问模式来控制时序,需要这部分参数;然后就是地址映射相关的?这部分除了读写CPU地址之外还需要做什么?数据格式肯定要确认的,如果是16位的话依次读写需要读写两个字节的数据,这部分逻辑肯定是FSMC内部实现的
FSMC的存储块1(NOR_FLASH/PSRAM控制器)通过以下三种寄存器配置。(x = 1~4)
寄存器 | 名称 | 作用 |
---|---|---|
FSMC_BCRx | 片选控制寄存器 | 包含存储器块的信息(存储器类型/数据宽度等) |
FSMC_BTRx | 片选时序寄存器 | 设置读操作时序参数(ADDSET/DATAST) |
FSMC_BWTR4x | 写时序寄存器 | 设置写操作时序参数(ADDSET/DATAST) |
4.1 SRAM/NOR闪存片选控制寄存器(FSMC_BCRx )
如下图所示。(详见F103中文参考手册 19.5.6)
- 位14 EXTMOD:扩展模式使能位,控制是否允许读写使用不同的时序(需要置1)
- 位12 WREN:写使能位(需要置1)
- 位[5:4] MWID[1:0]:存储器数据总线宽度(00: 8位模式,01: 16位模式,10/11保留)(需要置01)
- 位[3:2] MTYP[1:0]:存储器类型(00: SRAM/ROM,01:PSRAM,10:NOR FLASH,11保留)(需要置00)
- 位0 MBKEN:存储块使能位(需要置1)
4.2 SRAM/NOR闪存片选时序寄存器(FSMC_BTRx )
(因为F1的FSMC性能有问题,所以设置位15已经可以达到355ns的要求。。。)
4.3 SRAM/NOR闪存写时序寄存器(FSMC_BWTRx )
4.4 解释
结合2.1 的FSMC时序图和2.2 LCD的时序规格图,可以看到,LCD的读信号和写信号都有两个规格约束:读/写信号的拉低持续时间和恢复高电平持续时间(到下一次拉低之前)。
对于FSMC的模式A而言:
- 写信号:拉低时间是 (DATAST+1)*HCLK 就是写信号的拉低时间,(ADDSET+1)*HCLK 就是写信号的拉高时间,这两个时间限制都是最小15ns。
- 读信号,同理,拉低时间和拉高时间分别是(DATASET+3)*HCLK和(ADDSET+1)*HCLK,具体见2.2。
- 寄存器里面的DATAST和ADDSET两个数据位,乘以HCLK就能直接得到拉低时间和拉高时间。
4.5 寄存器组合
同一类寄存器
5 编程要点
5.1 初始化结构体
见下。
Sram控制句柄
SRAM初始化结构体
FSMC时序结构体
5.2 CubeMX配置
使用CubeMX的话事情就会变得异常简单,这就是一键配置的优点;当然缺点就是对结构可能缺乏了解。
ChipSelect 选择NE4,Memory type 选择 LCD Interface,LCD Register选择A10,Data 选择16比特
下面打开写操作和扩展模式(读写时钟分离),然后按照下图配置就可以了。
最后把LCD背光的Pin加上就行,可以生成代码了。
5.3 功能函数
和上一节的GPIO模拟8080时序相比,GPIO基础操作函数删去,LCD写指令、读数据和写数据这三个二级函数按照下图修改,直接通过地址读写寄存器即可;具体指令操作的三级函数和上一节一模一样。除去CubeMx自动配置,比之前的代码少很多,也更直观。
5.4 出现的问题
keil的C/C++优化等级必须在Level01或以下,否则会出错。