FSMC稱為靈活的靜態存儲器,它能夠與同步或異步存儲器和16位PC存儲器卡連接,STM32F4的FSMC接口支持包括SRAM、NAND FLASH、NOR FLASH和PSRAM等存儲器。
FSMC框圖
從FSMC框圖可以看到,FSMC將外部設備分為2類:NOR/PSRAM設備和NAND/PC卡設備。所有外部存儲器共享地址、數據和控制信號,但有各自的片選信號。FSMC 一次只能訪問一個外部器件。
這里將LCD的片選接口與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的可編程訪問參數如下表所示:
由于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寄存器映射表
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
GPIOE- >AFR[1] &= 0x00000000;
GPIOE- >AFR[1] |= 0XCCCCCCCC; //PE8-15復用,可以直接往AFR[1]中賦值
//PF:
GPIOF- >AFR[1] &= 0xfff0ffff;
GPIOF- >AFR[1] |= 0X000C0000; //PF12復用為FSMC
//PG:
GPIOG- >AFR[1] &= 0xfff0ffff;
GPIOG- >AFR[1] |= 0X000C0000; //PG12復用為FSMC
//3. 開FSMC時鐘
RCC- >AHB3ENR |= 1< 0;
//4. 配置FSMC寄存器
//BCR4
FSMC_Bank1- >BTCR[6] &= ~(1< 19); //始終在異步模式下操作
FSMC_Bank1- >BTCR[6] &= ~(1< 15); //不考慮等待信號
FSMC_Bank1- >BTCR[6] |= 1< 14; //使能擴展功能,即讀寫時序分開
FSMC_Bank1- >BTCR[6] &= ~(1< 13); //禁止等待nWait信號
FSMC_Bank1- >BTCR[6] |= 1< 12; //使能寫操作
FSMC_Bank1- >BTCR[6] &= ~(0x3< 4);
FSMC_Bank1- >BTCR[6] |= 1< 4; //16位數據寬度
FSMC_Bank1- >BTCR[6] &= ~(0x3< 2); //存儲器類型為:SRAM
//BTR4:
//BTR4(讀時序)
FSMC_Bank1- >BTCR[7] &= ~(0x3< 28); //異步模式A
FSMC_Bank1- >BTCR[7] |= 0xf< 16; //總線周轉階段持續時間為默認值
FSMC_Bank1- >BTCR[7] &= 0xffff00ff;
FSMC_Bank1- >BTCR[7] |= 60< 8; //DATAST為60HCLK
FSMC_Bank1- >BTCR[7] |= 0xf< 0; //ADDSET為15HCLK
//BWTR(寫時序)
FSMC_Bank1E- >BWTR[6] = 0;
FSMC_Bank1E- >BWTR[6] &= ~(0x3< 28); //異步模式A
FSMC_Bank1E- >BWTR[6] |= 0xf< 16; //總線周轉階段持續時間為默認值
FSMC_Bank1E- >BWTR[6] |= 3< 8; //DATAST為3個HCLK
FSMC_Bank1E- >BWTR[6] |= 3< 0; //ADDSET為3個HCLK
//使能存儲塊
FSMC_Bank1- >BTCR[6] |= 1< 0;
GPIOB- >BSRRH = 0X1< 15; //關背光
}
配置完FSMC,替代之前用IO口模擬8080時序的ILI9341GPIO初始化函數,并放入之前的圖片顯示和碰撞小球實驗中,可以發現刷屏速度得到了很大的提高。
可以看到,FSMC操作LCD比GPIO模擬8080時序會快很多,可能有人會說硬件實現就是比軟件實現快。但其實不是這樣的,用軟件模擬時序,給出的延時與標準時序可能不是完全一致,導致速度比較慢,如果用軟件模擬時序時使用邏輯分析儀對時序的延時時間進行優化,GPIO模擬8080時序也是可以達到FSMC的速度的,有興趣的可以自己嘗試一下。
評論
查看更多