一、FSMC原理
大容量,且引脚数在 100 脚以上的 STM32F103 芯片都带有 FSMC 接口。
FSMC,即灵活的静态存储控制器,能够与同步或异步存储器和 16 位 PC 存储器卡连接, 接口支持包括 SRAM、 NAND FLASH、 NOR FLASH 和 PSRAM 等存储器。
1.为什么可以用FSMC驱动LCD?
外部 SRAM 的控制一般有:地址线(如 A0~A18)、数据线(如 D0~D15)、写信号(WE)、读信号(OE)、片选信号(CS),如果 SRAM 支持字节控制,那么还有 UB/LB 信号。而 TFTLCD包括: RS、 D0~D15、 WR、 RD、 CS、 RST 和 BL 等,其中真正在操作 LCD 的时候需要用到的就只有: RS、 D0~D15、 WR、 RD 和 CS。其操作时序和 SRAM的控制完全类似,唯一不同就是 TFTLCD 有 RS 信号,但是没有地址信号。
而RS信号是用来控制LCD是写命令还是写数据的,本质上可以理解为一个地址,我们可以将RS信号接在FSMC的地址线上,可以选任意一根地址线,但注意接在不同地址线,地址的值会不同,后面会具体讲到。
这样数据引脚,地址引脚,写信号(WE)、读信号(OE)、片选信号(CS)都可以和LCD对应脚连接起来,写程序时,这些引脚初始化时,复用FSMC输出即可。LCD中还有RST,BL,接单片机的普通IO口即可。
2.寻址问题
FSMC 总共管理 1GB 空间,拥有 4 个存储块(Bank),BANK1是属于NOR/PSRAM,BANK2、BANK3属于NAND,BANK4属于PC卡。
STM32 的 FSMC 存储块 1(Bank1)被分为 4 个区,每个区管理 64M 字节空间,每个区都有独立的寄存器对所连接的存储器进行配置。 Bank1 的 256M 字节空间由 28 根地址线(HADDR[27:0])寻址。这里 HADDR 是内部 AHB 地址 总线 ,其 中 HADDR[25:0] 来自外部存储器地 址FSMC_A[25:0],而 HADDR[26:27]对 4 个区进行寻址。
Bank1 所选区 | 片选信号 | 地址范围 |
---|---|---|
第一区 | FSMC_NE1 | 0X6000,0000~63FF,FFFF |
第二区 | FSMC_NE2 | 0X6400,0000~67FF,FFFF |
第三区 | FSMC_NE3 | 0X6800,0000~6BFF,FFFF |
第四区 | FSMC_NE4 | 0X6C00,0000~6FFF,FFFF |
那么为什么是这个地址呢?计算方法也很简单,之前说过,HADDR[27:26]是对4个区进行寻址,显然,00对应第一区,01对应第二区,10对应第三区,11对应第四区,注意是26位和27位。而最高4位为“6”,如果是BANK2便是“7”,BANK3便是8,BANK4,便是9.
我们要特别注意 HADDR[25:0]的对应关系:
当 Bank1 接的是 16 位宽度存储器的时候:
HADDR[25:1]->FSMC_A[24:0]。
当 Bank1 接的是 8 位宽度存储器的时候: HADDR[25:0]>FSMC_A[25:0]。
很多TFTLCD使用的是 16 位数据宽度,所以 HADDR[0]并没有用到,只有 HADDR[25:1]是有效的,对应关
系变为: HADDR[25:1]-> FSMC_A[24:0],相当于右移了一位,这里请大家特别留意。
3.时序模式
FSMC 的 NOR FLASH 控制器支持同步和异步突发两种访问方式。选用同步突发访问方式时, FSMC 将 HCLK(系统时钟)分频后,发送给外部存储器作为同步时钟信号 FSMC_CLK。
对于异步突发访问方式, FSMC 主要设置 3 个时间参数:地址建立时间(ADDSET)、数据建立时间(DATAST)和地址保持时间(ADDHLD)。 FSMC 综合了 SRAM/ROM、 PSRAM 和 NOR Flash 产品的信号特点,定义了 4 种不同的异步时序模型。选用不同的时序模型时,需要设置不同的时序参数
时序模型 | 简单描述 | 时间参数 |
---|---|---|
Mode1 | SRAM/CRAM 时序 | DATAST、 ADDSET |
ModeA | SRAM/CRAM OE 选通型时序 | DATAST、 ADDSET |
Mode2/B | NOR FLASH 时序 | DATAST、 ADDSET |
ModeC | NOR FLASH OE 选通型时序 | DATAST、 ADDSET |
ModeD | 延长地址保持时间的异步时序 | DATAST、 ADDSET、 ADDHLK |
我用的是异步突发模式,模式 A 支持独立的读写时序控制,这个对我们驱动 TFTLCD 来说非常有用,因为 TFTLCD在读的时候,一般比较慢,而在写的时候可以比较快,如果读写用一样的时序,那么只能以读的时序为基准,从而导致写的速度变慢,或者在读数据的时候,重新配置 FSMC 的延时,在读操作完成的时候,再配置回写的时序,这样虽然也不会降低写的速度,但是频繁配置,比较麻烦。而如果有独立的读写时序控制,那么我们只要初始化的时候配置好,之后就不用再配置,既可以满足速度要求,又不需要频繁改配置。
当然,我也试了一下模式B。
二、代码部分
该项目基于STM32F103
#define Bank1_LCD_D ((u32)0x6C010000) // 写数据对应地址
#define Bank1_LCD_C ((u32)0x6C000000) // 写命令对应地址
#define Set_Rst GPIOA->BSRR = GPIO_Pin_9;
#define Clr_Rst GPIOA->BRR = GPIO_Pin_9;
#define Lcd_Light_ON GPIOB->BSRR = GPIO_Pin_11;
#define Lcd_Light_OFF GPIOB->BRR = GPIO_Pin_11;
需要说明的是,我的项目选用的是BANK1的NE4,即第四区,LCD的RS引脚接的是PA15,因此地址从0x6C000000开始,而PA15置1为写数据,置0为写命令。注意,PA15对应寄存器D16位。因此写数据对应地址0x6C010000.
如果LCD的RS引脚接的是PA16,则该引脚对应寄存器的D17位,那么写数据的地址变为0x6C020000,需要换成二进制计算,在此不在赘述。
/*
* 函数名:LCD_GPIO_Config
* 描述 :根据FSMC配置LCD的I/O
* 输入 :无
* 输出 :无
* 调用 :内部调用
*/
void LCD_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable the FSMC Clock */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC, ENABLE);
/* config lcd gpio clock base on FSMC */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC |
RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE| RCC_APB2Periph_GPIOG , ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
/* config tft rst gpio */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* config tft back_light gpio base on the PT4101 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 ;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* config tft data lines base on FSMC
* data lines,FSMC-D0~D15: PD 14 15 0 1,PE 7 8 9 10 11 12 13 14 15,PD 8 9 10
*/
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_8 | GPIO_Pin_9 |
GPIO_Pin_10 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 |
GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 |
GPIO_Pin_15;
GPIO_Init(GPIOE, &GPIO_InitStructure);
/* config tft control lines base on FSMC
* PD4-FSMC_NOE :LCD-RD
* PD5-FSMC_NWE :LCD-WR
* PG12-FSMC_NE4 :LCD-CS
* PG5-FSMC_A15 :LCD-DC
*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_Init(GPIOG, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 ;
GPIO_Init(GPIOG, &GPIO_InitStructure);
/* tft control gpio init */
GPIO_SetBits(GPIOA, GPIO_Pin_9); // RST = 1
GPIO_SetBits(GPIOD, GPIO_Pin_4); // RD = 1
GPIO_SetBits(GPIOD, GPIO_Pin_5); // WR = 1
GPIO_SetBits(GPIOG, GPIO_Pin_12); // CS = 1
这是引脚初始化代码,FSMC部分数据引脚大部分相同,初始化复用为FSMC模式,读写数据,片选等引脚根据原理图进行配置,也复用为FSMC,特别注意LCD的RS引脚接FSMC的地址线,这个在上面也重点强调了。
/*
* 函数名:LCD_FSMC_Config
* 描述 :LCD FSMC 模式配置
* 输入 :无
* 输出 :无
* 调用 :内部调用
*/
static void LCD_FSMC_Config(void)
{
FSMC_NORSRAMInitTypeDef FSMC_NORSRAMInitStructure;
FSMC_NORSRAMTimingInitTypeDef p;
p.FSMC_AddressSetupTime = 0x02; //地址建立时间
p.FSMC_AddressHoldTime = 0x00; //地址保持时间
p.FSMC_DataSetupTime = 0x05; //数据建立时间
p.FSMC_BusTurnAroundDuration = 0x00;
p.FSMC_CLKDivision = 0x00;
p.FSMC_DataLatency = 0x00;
//norflash一般用模式B,SRAM一般用模式A,和时序有关
p.FSMC_AccessMode = FSMC_AccessMode_B; // 一般使用模式B来控制LCD
FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM4; //使用NE4
FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable; //不复用数据地址
FSMC_NORSRAMInitStructure.FSMC_MemoryType = FSMC_MemoryType_NOR; //设置为NOR类型
FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b; //存储器数据宽度为16bit
FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable; //
FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable;
FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;
FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable; //寄存器写使能
FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable;
FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable; //扩展位不使能,读写使用相同时序
FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable;
FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &p;
FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &p;
FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure);
/* Enable FSMC Bank1_SRAM Bank */
FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM4, ENABLE); //使能BNAK1,NE4
}
这是FSMC的配置代码,用的是异步模式B,读写时序相同,具体可以看备注
以下是使用模式A(参考正点原子代码)
FSMC_NORSRAMInitTypeDef FSMC_NORSRAMInitStructure;
FSMC_NORSRAMTimingInitTypeDef readWriteTiming;
FSMC_NORSRAMTimingInitTypeDef writeTiming;
readWriteTiming.FSMC_AddressSetupTime = 0x01; //地址建立时间(ADDSET)为2个HCLK 1/36M=27ns
readWriteTiming.FSMC_AddressHoldTime = 0x00; //地址保持时间(ADDHLD)模式A未用到
readWriteTiming.FSMC_DataSetupTime = 0x0f; // 数据保存时间为16个HCLK,因为液晶驱动IC的读数据的时候,速度不能太快,尤其对1289这个IC。
readWriteTiming.FSMC_BusTurnAroundDuration = 0x00;
readWriteTiming.FSMC_CLKDivision = 0x00;
readWriteTiming.FSMC_DataLatency = 0x00;
readWriteTiming.FSMC_AccessMode = FSMC_AccessMode_A; //模式A
writeTiming.FSMC_AddressSetupTime = 0x00; //地址建立时间(ADDSET)为1个HCLK
writeTiming.FSMC_AddressHoldTime = 0x00; //地址保持时间(A
writeTiming.FSMC_DataSetupTime = 0x03; 数据保存时间为4个HCLK
writeTiming.FSMC_BusTurnAroundDuration = 0x00;
writeTiming.FSMC_CLKDivision = 0x00;
writeTiming.FSMC_DataLatency = 0x00;
writeTiming.FSMC_AccessMode = FSMC_AccessMode_A; //模式A
FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM4;// 这里我们使用NE4 ,也就对应BTCR[6],[7]。
FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable; // 不复用数据地址
FSMC_NORSRAMInitStructure.FSMC_MemoryType =FSMC_MemoryType_SRAM;// FSMC_MemoryType_SRAM; //SRAM
FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;//存储器数据宽度为16bit
FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode =FSMC_BurstAccessMode_Disable;// FSMC_BurstAccessMode_Disable;
FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait=FSMC_AsynchronousWait_Disable;
FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable;
FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;
FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable; // 存储器写使能
FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable;
FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Enable; // 读写使用不同的时序
FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable;
FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &readWriteTiming; //读写时序
FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &writeTiming; //写时序
FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure); //初始化FSMC配置
FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM4, ENABLE); // 使能BANK1