浅析STM32 FSMC操作LCD的过程

发布者:和谐共存最新更新时间:2023-08-10 来源: elecfans关键字:STM32  FSMC 手机看文章 扫描二维码
随时随地手机看文章

FSMC称为灵活的静态存储器,它能够与同步或异步存储器和16位PC存储器卡连接,STM32F4的FSMC接口支持包括SRAM、NAND FLASH、NOR FLASH和PSRAM等存储器。


FSMC框图

图片

从FSMC框图可以看到,FSMC将外部设备分为2类:NOR/PSRAM设备和NAND/PC卡设备。所有外部存储器共享地址、数据和控制信号,但有各自的片选信号。FSMC 一次只能访问一个外部器件。


NE4相连,即利用FSMC_NE4实现对LCD的片选;另外SRAM芯片的片选接口与FSMC_NE3相连,即利用FSMC_NE3实现对SRAM芯片的片选。FSMC本身就是静态存储器控制器,通过FSMC接口访问SRAM是理所当然的事,这里能将LCD也连接到FSMC,显然说明LCD在操作上与SRAM有相似之处。


SRAM的控制一般有:地址线(如A18A0)、数据线(如D15D0)、写信号(WE)、读信号(OE)、片选信号(CS),如果SRAM支持字节控制,那么还有UB/LB信号。

LCD的信号则包括:寄存器选择(RS)、数据线(D15-D0)、写信号(WE)、读信号(OE)、片选信号(CS)、复位信号RST和背光BL。


除去与访问过程无关的信号RST、BL,则两者的控制信号是极度的一致,区别仅在于SRAM有地址线(A18-A0),而LCD有RS信号线,从作用上看,两者也是一致的,都决定访问数据的位置。若假定SRAM仅一根地址线A0,则说明数据位置仅有两个,通过A0取0和取1,区分访问的数据到底在哪个地址空间;而LCD的RS取0和取1,也说明有两个存储空间,即ILI9341的寄存器的GRAM。显然,当把RS理解成一根地址线时,LCD就等效成SRAM了。RS与地址线A6进行相连,因此通过把地址线中的A6置0可以访问ILI9341的寄存器,而把A6置1则可以访问GRAM。


STM32F4xx的FSMC支持 8/16/32 位数据宽度,这里用到的 LCD 是 16 位宽度的,所以在设置数据宽度时,用户应选择16位宽。使用FSMC的目的,是把STM32F4XX的片外外设映射到片内ARM核可寻址的地址空间当中,这样通过访问片内对应的地址空间,就可以操作片外外设了。显然,这样的操作等同于把“片外外设”变成了“片内外设”,在访问方式上,和访问片内外设一模一样。


FSMC存储区域

图片

FSMC 的外部存储器被划分为 4 个固定大小的存储区域,每个存储区域的大小为256 MB。

● 存储区域 1 可连接多达 4 个 NOR Flash 或 PSRAM 存储器器件。此存储区域被划分为 4 个

NOR/PSRAM 区域,带 4 个专用片选信号。

● 存储区域 2 和 3 用于连接 NAND Flash 器件(每个存储区域一个器件)

● 存储区域 4 用于连接 PC 卡设备


对于每个存储区域,所要使用的存储器类型由用户在配置寄存器中定义。

Bank1的256M字节空间由 28 根地址线(HADDR[27:0])(注意:这里的HADDR是内部AHB的地址线,它的地址是按字节进行“编号”的)寻址。这里HADDR[25:0]来自外部存储器地址FSMC_A[25:0],而HADDR[27:26]对4个区进行寻址。

NOR/PSRAM 存储区域选择

图片

由于我的LCD片选信号与FSMC的FSMX_NE4连接,所以LCD对应寻址空间的首地址为0x60000000+0xC000000=0x6C000000。LCD的RS接到了地址A6,而没有其他地址线,因此按照道理,其他的地址线可取任意值。


HADDR[25:0] 包含外部存储器地址。由于 HADDR 为字节地址,而存储器按字寻址,所以根据存储器数据宽度不同,实际向存储器发送的地址也将有所不同,如下表所示。

图片

LCD配置为16位数据宽度,因此内部地址的偏移量是外部地址2倍,即假定FSMC_A的地址值取x,则映射到内部地址,则实际地址首地址应为0x6C000000+2x。由于此次仅用到A6,即FSMC_A[6],若该位取0时,其他地址线也取0,则FSMC_A的值为0,此时其映射的AHB地址为0x6C000000;若FSMC_A[6]取1,且其他地址线都取0,则FSMC_A的值为0x40,此时映射的AHB地址即为0x6C000000+2*0x40,即0x6C000080。找到了地址,就可以像访问片内外设那样去访问片外外设了。


这里直接宏定义地址,方便调用。


//未使用的地址线全部取0(注意:取任意值均可,这里取0)

#define   LCD_CMD_ADD      (volatile u16 *)0X6C000000

#define   LCD_DATA_ADD     (volatile u16 *)(0X6C000000 + 0X80)

为进一步简化操作,可为以上两个地址宏定义加地址访问符“*”,以便于访问这两个地址空间。


#define   LCD_CMD          *LCD_CMD_ADD

#define   LCD_DATA         *LCD_DATA_ADD

定义了这两个宏,以后写命令和写数据就方便多了。若要发命令,只需给LCD_CMD赋值即可,若要发数据,只需给LCD_DATA发数据就行了。


得到了片外设备(LCD)映射到片内AHB空间的地址,就方便用户对ILI9341的寄存器和GRAM进行访问了,但访问要有合适的时序才可以。SRAM的可编程访问参数如下表所示:

1.jpg

由于ILI9341在写入和读取时,速度差异较大,为合理地利用读写速度,这里采用异步模式A,如此以来,只需关注上表的前三个参数,即:地址建立时间、地址保持时间、数据建立时间。

ILI9341的8080-II读写时序

图片

上图中所对应电气参数的取值如表所示:

图片

从上图表可以得到ILI9341在8080-II时序下的读写控制信号电平状态为:写控制脉冲高电平持续的时间为:twrh<15ns(HCLK=168MHz,因此twrh<3个HCLK);写控制脉冲低电平持续的时间为:twrl<15ns(同理,twrl<3个HCLK)。读GRAM控制脉冲高电平持续的时间为:trdhfm<90ns(即:trdhfm<15个HCLK); 读GRAM控制脉冲低电平持续的时间为:trdlfm<355ns(即:trdlfm<60个HCLK)。读ID号控制脉冲高电平持续的时间为:trdh<90ns(即:trdh<15个HCLK); 读ID号控制脉冲低电平持续的时间为:trdl<45ns(即:trdl<8个HCLK)。

ILI9341的写操作周期相对于读操作周期要短很多,即写的速度要明显快于读的速度。另外,为保证读GRAM和读ID号可共用同一读时序,应保证读控制脉冲的低电平持续时间trd至少为60个HCLK。这里,为充分发挥写入速度快的优势,这里分开配置读时序和写时序。

FSMC在模式A下的读/写时序

图片

图片

读时序中地址建立时间ADDSET,即为读控制脉冲的高电平时间,由以上结论可知,ADDSET(RD)的值为90ns(15个HCLK周期);读时序中数据保持时间DATAST,即读控制脉冲的低电平时间,由以上结论可知,DATAST(RD)的值为355ns(60个HCLK周期);写时序对应的建立时间ADDSET,即为写控制信号的高电平,由以上结论可知,此时的ADDSET(WR)的值为15ns(3个HCLK周期);写时序对应的数据保持时间DATAST,即写控制脉冲的低电平时间,由以上结论可知,此时的DATAST(WR)的值为15ns(3个HCLK)。

有了以上的时序参数,就可以配置FSMC相关寄存器了。

下面对寄存器进行简要说明:

SRAM/NOR-Flash片选控制寄存器1..4(FSMC_BCR1..4):

图片

bit19:写入突发使能。对于CRAM(PSRAM),该位可在写操作时使能同步突发协议。读取访问期间同步突发协议的使能位为FSMC_BCRx寄存器中的BURSTEN位。0:始终在异步模式下进行写入操作;1:在同步模式下进行写入操作。操作LCD时,始终在异步模式下进行操作,因此这里设置为0。

bit15:异步传输期间的等待信号。该位可使能/禁止FSMC使用等待信号,即使是在异步协议期间该位也有作用。0:运行异步协议时不考虑NWAIT信号(复位后的默认值);1:运行异步协议时考虑NWAIT信号。这里不考虑NWAIT信号。

bit14:扩展模式使能。FSMC可对FSMC_BWTR寄存器中的写入时间进行配置,此配置由EXTMOD位使能,进而使读取和写入操作采用不同的时序。0:不考虑FSMC_BWTR寄存器中的值;1:考虑FSMC_BWTR寄存器中的值。由于此次采用的时序是模式A,因此采用扩展模式,并分开设置读写时序。这里将EXTMOD设置为1。

bit13:等待使能位。该位可使能/禁止在同步模式下访问FLASH时通过NWAIT信号插入等待周期。0:禁止NWAIT信号;1:使能NWAIT信号。这里禁止即可。

bit12:写入使能。用于指示FSMC是否在存储区域内使能/禁止写入操作。0:FSMC在存储区域内禁止写入操作;1:FSMC在存储区内使能写入操作。因此就是想利用FSMC读写ILI9341,因此,这里需使能存储区域的写入操作。

bit11:等待时序配置。NWAIT信号指示存储器中的数据是否有效,或者在同步模式下访问FLASH时是否必须插入等待周期。该配置位决定存储器是在等待周期之前的一个时钟周期还是等待周期期间使能NWAIT。0:NWAIT信号在等待周期之前的一个数据周期有效;1:NWAIT信号在等待周期期间有效。这里无需设置。

bit10:环回突发模式支持。定义控制器是否将一个AHB突发环回访问分割成两个线性访问。仅在突发模式下访问存储器时有效。0:未使能直接环回突发;1:使能直接环回突发。这里不采用环回突发模式。

bit9:等待信号极性位。定义存储器的等待信号极性。仅在突发模式下访问存储器时有效。0:NWAIT低电平有效;1:NWAIT高电平有效。该位无需设置。

bit8:BURSTEN,突发使能位。用于使能/禁止读取操作期间的同步突发访问。仅对同步突发存储器有效。0:禁止;1:有效。此用于同步模式,因此这里无需设置。

bit6:FLASH访问使能。用于使能NOR FLASH访问操作。0:禁止访问;1:使能访问。这里是把LCD等效成SRAM,因此禁止即可。

bit[5:4]:存储器数据总线宽度。00:8位;01:16位;10和11:保留,不使用。显然,这里需设置为16位。

bit[3:2]:存储器类型。定义与相应存储区域相连的外部存储器类型;00:SRAM、ROM;01:PSRAM;10:NOR FLASH/OneNAND FLASH。这里配置为SRAM。

bit1:地址/数据复用使能功能。该位置1时,仅对NOR和PSRAM存储器有效;0:地址/数据非复用;1:地址/数据在数据总线上复用。无关项,无需设置。

bit0:存储区域使能。0:禁止相应的存储区域;1:使能相应的存储区域。这里一定要使能的。

SRAM/NOR-Flash片选时序寄存器1..4(FSMC_BTR1..4):

图片

bit[29:28]:访问模式。00:模式A;01:模式B;10:模式C:11:模式D。显然,此次选择模式A。

bit[27:24]:DATLAT,同步突发NOR FLASH的数据延迟。这里不需要进行设置。

bit[23:20]:CLKDIV,CLK信号的时钟分频比。在异步模式上,该值为无关值,因此这里无需关注。

bit[19:16]:这里采用默认值即可。

bit[15:8]:DATST,数据阶段的持续时间。该寄存器是针对读操作的,由前面时序分析可知,这里取为60个HCLK。

bit[7:4]:ADDHLD,地址保持阶段持续的时间。在异步模式,该字段是无关项,因此无需设置。

bit[3:0]:ADDSET,地址建立时间。由前面时序分析可知,读操作时ADDSET可取15个HCLK。

SRAM/NOR-FLASH写入时序寄存器1..4(FSMC_BWTR1..4):

图片

bit[29:28]:ACCMOD,访问模式。这里同读时序,配置为模式A。

bit[27:24]:DATLAT,数据延迟。期用于同步突发NOR FLASH,因此这里可以不设置。

bit[23:20]:CLKDIV,CLK信号时钟分频比。这里仍是无关项,不设置。

bit[19:16]:总线周转阶段的持续时间。这里按默认值设置即可。

bit[15:8]:数据阶段的持续时间。由前面的时序分析可得,写操作时,DATST取3个HCLK。

bit[7:4]:地址阶段的保持时间。在异步模式,该字段是无关项,无需设置。

bit[3:0]:地址阶段的建立时间。由前面的时序分析可得,写操作时,ADDSET取3个HCLK。

分析完如何配置FSMC寄存器,开始GPIO引脚并复用为FSMC,并按上述分析配置寄存器,即可让FSMC模块为用户提供正确的时序。

但在stm32f4xx.h文件当中,ST公司并没有按照中文手册中那样,去命名FSMC的寄存器。

FSMC寄存器映射表

1.jpg

stm32f4xx.h当中定义FSMC的数据结构为:


typedef struct

{

     __IO uint32_t BTCR[8];    /*!< NOR/PSRAM chip-select control register(BCR) and chip-select timing register(BTR) */   

}FSMC_Bank1_TypeDef; 

#define   FSMC_Bank1          ((FSMC_Bank1_TypeDef *) FSMC_Bank1_R_BASE)

上述结构体当中成员为一个8元素的数组。由C数组知识易知,数组成员在内存当中地址是相邻的。宏定义FSMC_Bank1为FSMC_Bank1_TypeDef类型的地址,而FSMC_Bank1_R_BASE即为0xA0000000。FSMC_Bank1所指向的第一个成员即为FSMC_BCR1。由地址连续性,显然可得如下对应关系:


BTCR[0]对应FSMC_BCR1,BTCR[1]对应FSMC_BTR1;


BTCR[2]对应FSMC_BCR2,BTCR[3]对应FSMC_BTR2;


BTCR[4]对应FSMC_BCR3,BTCR[5]对应FSMC_BTR3;


BTCR[6]对应FSMC_BCR4,BTCR[7]对应FSMC_BTR4。


同理,写入时序寄存器对应的数据结构为:


BWTR[0]对应FSMC_BWTR1,BWTR[2]对应FSMC_BWTR2;


BWTR[4]对应FSMC_BWTR3,BWTR[6]对应FSMC_BWTR2;


这里元素存在跳跃性,原因是寄存器地址间隔为8,因此,为保证地址上的一致性,这里必须这样定义数据结构。


配置FSMC


static void ILI9341_GpioInit()

{

  //1. 开时钟PB/PD/PE/PF/PG

  RCC- >AHB1ENR  |= 1< < 1 | 0XF< < 3;

  //2. 背光引脚:通用推挽输出

    //端口设置(pb15)

    GPIOB- >MODER    &= ~(0X3< < 30);

    GPIOB- >MODER    |= 1< < 30;          //普通输出


    GPIOB- >OTYPER    &= ~(1< < 15);        //推挽

    GPIOB- >OSPEEDR    |= 0X3< < 30;

    GPIOB- >PUPDR    &= ~(0X3< < 30);        //无上下拉


    //其他所有引脚复用为FSMC

    /*

      LCD_CS:PG12

      RS:PF12  = >FSMC_A[6]

      WR:PD5

      RD:PD4


      D0-D1:PD14/PD15

      D2-D3:PD0/PD1

      D4-D12:PE7-PE15

      D13-D15:PD8-PD10

    */

    //2. PD(配置为复用)

    GPIOD- >MODER    &= ~(0XF< < 0 | 0XF< < 8 | 0X3F< < 16 | 0xf< < 28);

    GPIOD- >MODER    |=  0X0a< < 0 | 0xa< < 8 | 0x2a < < 16 |0xa< < 28;        //PD口复用


    GPIOD- >OTYPER    &= ~(0X3< < 0 | 0X3< < 4 | 0X7< < 8 | 0X3< < 14);          //推挽

    GPIOD- >OSPEEDR   |= (0XF< < 0 | 0XF< < 8 | 0X3F< < 16 | 0xf< < 28);        //速度100Mhz

    GPIOD- >PUPDR     &= ~(0XF< < 0 | 0XF< < 8 | 0X3F< < 16 | 0xf< < 28);        //无上下拉


    //PE口配置

    GPIOE- >MODER    &= 0X00003FFF;

    GPIOE- >MODER    |= 0Xaaaa8000;        //PE复用


    GPIOE- >OTYPER    &= 0X007F;        //PE7-15推挽

    GPIOE- >OSPEEDR  |= 0XFFFFC000;          //PE7-15速度为100Mhz

    GPIOE- >PUPDR    &= 0X00003FFF;      //PE7-15无上下拉


    //FP12

    GPIOF- >MODER    &= ~(0X3< < 24);

    GPIOF- >MODER    |= 2< < 24;


    GPIOF- >OTYPER    &= ~(1< < 12);        //推挽

    GPIOF- >OSPEEDR  |= 0X3< < 24;          //100mHZ

    GPIOF- >PUPDR    &=  ~(0X3< < 24);      //无上下拉


    //FG12

    GPIOG- >MODER    &= ~(0X3< < 24);

    GPIOG- >MODER    |= 2< < 24;


    GPIOG- >OTYPER    &= ~(1< < 12);        //推挽

    GPIOG- >OSPEEDR  |= 0X3< < 24;          //100mHZ

    GPIOG- >PUPDR    &=  ~(0X3< < 24);      //无上下拉


    //选择复用的功能:复用为FSMC

    //复用功能选择

    //PD:

    GPIOD- >AFR[0] &= 0XFF00FF00;

    GPIOD- >AFR[0]  |= 0x00cc00cc;        //PD0/1/4/5复用为FSMC


    GPIOD- >AFR[1] &= 0X00FFF000;

    GPIOD- >AFR[1] |= 0XCC000CCC;        //PD8/9/10/14/15复用为FSMC


    //PE:

    GPIOE- >AFR[0]  &= 0X0FFFFFFF;

    GPIOE- >AFR[0] |= 0XC0000000;          //PE7复用为FSMC

[1] [2]
关键字:STM32  FSMC 引用地址:浅析STM32 FSMC操作LCD的过程

上一篇:基于STM32单片机的路灯系统设计
下一篇:基于STM32H750的RTC自动唤醒

推荐阅读最新更新时间:2024-11-13 12:31

STM32 USB 之从0开始移植笔记
-----------------------------------动机----------------------------------- 写在前面的话:最近逛淘宝无意间发现RC522居然只要10元左右就可以包邮买到,真是太便宜了,就忍不住买了个回来玩玩。到货移植到我的板子上OK 后突然发现我的USB口紧张了,一个用来给板子供电一个插jlink 一个插入usb转串口给RC522下命令。就想着将板子供电和RC522传输用一个USB接口来实现。这就是这次折腾USB的来由~-~ ----------------------------------开始折腾USB----------------------------
[单片机]
STM32ADC单次转换DMA读取
DMA读取方式很适合高频率的ADC采样信号。 ADC的DMA读取方式,其实和上一篇的中断读取方式差不多,初始化代码更是相似。初始化代码如下: static void ADC_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //ʹÄÜPB,PE¶Ë¿ÚʱÖÓ GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN; GPIO_Init(
[单片机]
STM32连接射频si4438模块
SI4438射频模块参数: 1、频率范围:425-525 MHz 2、数字接收信号强度指示(RSSI) 3、64字节收发数据寄存器(FIFO) 4、跳频功能 等! 使用SI的WDS工具生成代码 1、 选择仿真模式 2、 芯片选择si4438 B1模式 3、 Radio Configuration Application 4、 Select Application 1、 Select Project 选择Bidirectional packet ,双向通信模式 2、 Configure project 配置工程 Frequency and power: 频率和功率的设置, base freq基频,中心频率, Chan
[单片机]
STM32 硬件I2C外设
I2C 框图 通信引脚   输入的引脚有三个,SDA 数据传输引脚,SCL 时钟引脚, SMBA用于SMBUS警告,在 I2C 中并未用到。这些引脚实际对应的硬件引脚可以在 数据手册 的引脚定义中查到。这里小结如下: 时钟控制   时钟控制决定了 SCL 的时钟频率,决定了数据的传输速率。其对应的的寄存器是 CCR 可以配置 I2C的模式。   这个寄存器的15位确定 I2C 处于什么模式,11:0位具体配置频率。 数据控制   发送数据,数据先被写入数据寄存器,移到数据移位寄存器,再一位一位交由数据控制从 SDA 端口发送出去。   而框图下面的比较器,是在STM32作为从机,主机呼叫时用于比较。再下面的地址寄
[单片机]
<font color='red'>STM32</font> 硬件I2C外设
STM32 串口中断接收数据
#include stm32f10x.h /*********************************************************************** ***********************************************************************/ void RCC_Configuration(void); void GPIO_Configuration(void); void NVIC_Configuration(void); void delay(vu32 nCount) { for(; nCount != 0; nCount--); }
[单片机]
STM32_RTC晶振不起振的原因及解决方法
STM32的RTC晶振经常出现不起振的问题,这已经是“业界共识”了。。。很多人在各种电子论坛上求助类似于“求高手指点!RTC晶振不起振怎么办”的问题,而其答案基本可以概括为“这次高手帮不了你了” 更有阴谋论者提出让人啼笑皆非的解释——STM32的RTC晶振不起振是ST与晶振厂商串通后故意搞出来的,目的是提高某晶振厂商高端晶振的销量。。。 最近做的几块板子也用到了STM32的RTC,前后两版一共做了大概6片,幸运的是并未遇到晶振不起振的现象。而我采用的是3毛钱一个的普通晶振,并未选用传说中低负载高精度晶振。。。后来在另外一片实验性质的板子上首次遇到了晶振不起振的问题,而且做了2片都不起振,这才让我意识到这个问题的严重性。
[单片机]
使用STM32hal库usart的接收中断分析及出现部分问题的解决
最近开始使用ST的hal库,这个库相较于之前的标准库,优缺点兼具吧,hal库封装了更多底层的细节,我们可以很轻易的实现我们需要的功能,但是由于封装了太多的细节,导致一旦出问题,你就很难发现问题,内部调用的复杂让你觉得懵逼。 (一)hal库接收中断的分析 这几天在使用hal库的USART,其中用到的是接收中断,官方推荐的使用接收中断的方式是:在初始化函数上面先开启接收中断(这里就不介绍串口的配置,网上一大推,我就不给世界增加无谓的存储量了),开启的函数如下: HAL_UART_Receive_IT(&huart2,(uint8_t*)aRxBuffer, 1);//该函数会开启接收中断:标志位UART_IT_RXNE,并且设
[单片机]
FreeRTOS移植,基于STM32 HAL库
一、硬件准备 一个STM32开发板(STM32F429IGT6),及其电源线等; 一个ST-Link下载器及其连接线等。 二、软件准备 FreeRTOS源码(V9.0.0); 一个基于STM32 HAL库的基础例程(跑马灯例程)。 三、移植FreeRTOS 3.1 添加FreeRTOS源码至工程 添加源代码至工程目录,添加至工程分组中,添加相关头文件路径 编译,提示找不到 FreeRTOSConfig.h 3.2 添加 FreeRTOSConfig.h 文件来源:事先参考众多例程中的 FreeRTOSConfig.h 后,总结出来的 编译结果为:2个error。SVC_Handler() 和 PendSV_Handler()
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
更多往期活动

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved