這兩天調試STM32F103外擴SRAM,將調試過程中遇到的問題記錄下,SRAM的規格是256K*16的異步SRAM,地址總線為18,數據線寬度為16.
在調試過程中遇到一些小問題,希望讀者能少走些彎路。
先看一下FSMC內存映射圖:
由圖可知,stm32的FSMC模塊分為四個bank,每個bank的大小事64M。
下面這張圖是FSMC各個塊的信號分配圖。
由上圖可知,bank1 NOR/PSRAM可以分為4個子塊,由FSMC_NE[4:1]來選擇使能哪個子塊。
例如:每小塊NOR/PSRAM的64M地址范圍如下:
第一塊:6000 0000h--63ff ffffh
第二塊:6400 0000h--67ff ffffh
第二塊:6800 0000h--6bff ffffh
第三塊:6c00 0000h--6fff ffffh
我們可以通過選擇HADDR[27:26]來確定當前使用的是哪個64M的分地址塊,如下圖所示:
這里我使用的是100腳的stm32,只有NE1引腳可用,也就是說只能用Bank1的存儲塊1,即FSMC_Bank1_NORSRAM1。
我用的STM32是100腳的,地址和數據總線是復用的,那么需要用到鎖存器來區分地址和數據,而鎖存信號是NADV輸出的,注意看圖173和圖174的NADV信號在輸出地址時是低電平,輸出數據時是高電平,所以鎖存器要選擇一個高電平鎖存或者加個反向器。
在配置STM32的FSMC的寄存器時要注意下面兩句話:
//使能復用模式 FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Enable; //注意這里不是FSMC_MemoryType_SRAM而是FSMC_MemoryType_NOR,手冊上說要想輸出NADV信號只能設置成FSMC_MemoryType_NOR。 FSMC_NORSRAMInitStructure.FSMC_MemoryType = FSMC_MemoryType_NOR
SRAM參考原理圖如下圖:
stm32的FSMC功能用到的復用IO口比較多,配置時需要細點心:
/* *PD14(DA0) PD15(DA1) PD0(DA2) PD1(DA3) PE7~PE15(DA4~DA12) PD8~PD10(DA13~DA15) PD11(A16) PD12(A17) *PD5(WE) PD7(NE1) PD4(OE) *PE0(BL0) PE1(BL1) NADV(PB7) */ void sram_gpio_configration(void) { GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_14 | GPIO_Pin_15; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOD, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | 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_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOE, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); }
下面是具體的FSMC寄存器配置:
void fsmc_for_sram_configration(void) { FSMC_NORSRAMInitTypeDef FSMC_NORSRAMInitStructure; FSMC_NORSRAMTimingInitTypeDef FSMC_NORSRAMTimingInitStructure; FSMC_NORSRAMTimingInitStructure.FSMC_AddressSetupTime = 15; FSMC_NORSRAMTimingInitStructure.FSMC_AddressHoldTime = 15; FSMC_NORSRAMTimingInitStructure.FSMC_DataSetupTime = 255;
FSMC_NORSRAMTimingInitStructure.FSMC_BusTurnAroundDuration = 15;//以上這四個參數在調試的時候可以先設置為最大值,程序調通后在根據器件手冊和STM32手冊計算出合適的值 FSMC_NORSRAMTimingInitStructure.FSMC_CLKDivision = 0;//synchronize memory下面這兩個參數是為同步器件准備的 FSMC_NORSRAMTimingInitStructure.FSMC_DataLatency = 0; FSMC_NORSRAMTimingInitStructure.FSMC_AccessMode = FSMC_AccessMode_B;//復用總線模式下,訪問模式設置為ModeB FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM1;
FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Enable; FSMC_NORSRAMInitStructure.FSMC_MemoryType = FSMC_MemoryType_NOR;//注意這里要設置成NOR,因為只有設置為NOR,才能輸出NADV信號
FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b; FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable; FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait = FSMC_AsynchronousWait_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 = &FSMC_NORSRAMTimingInitStructure; FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &FSMC_NORSRAMTimingInitStructure; FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure); /* - BANK 1 (of NOR/SRAM Bank 0~3) is enabled */ FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM1, ENABLE); }
外部SRAM的訪問:
在訪問SRAM時糾結了許久,最后終於弄明白了。網上看了很多資料,有說在訪問時要先將地址偏移,有說直接訪問就行。最后經過測試發現訪問片外SRAM時跟訪問內部RAM一樣,直接訪問就可以了,FSMC都幫我們做好了。
當訪問字節數據時:*((u8*)(BANK1_SRAM_BASE+offset))。1.讀取SRAM數據時,將offset設為0x00,用示波器測試SRAM的A0地址信號是低,當offset為0x01時,用示波器測試SRAM的A0地址信號也是低,當offset為0x02和0x03時SRAM的A0地址信號是高,這說明當字節訪問時地址確實是偏移了1位,原因是SRAM是16位器件(SRAM的1個地址存儲16位數據),一次可以訪問2個字節的數據,這個時候FSMC將根據offset值判斷,訪問的是高8位還是低8位,即如果offset的最低位是0則訪問的是16位數據的低8位,如果offset的最低位是1則訪問的是16位數據的高8位。2. 向SRAM寫數據時,道理也是一樣的,只是寫的時候通過BL[1:0]兩個管腳控制寫高字節還是低字節。
當訪問半字數據時:*((u16*)(BANK1_SRAM_BASE+offset))。1.讀取SRAM數據時,這個時候要注意了,FSMC在訪問的時候要看offset的值是不是半字對齊的。如果offset是0x00,則直接訪問的是SRAM的0x00地址處的16位數據,當offset是0x01時,FSMC是分兩次訪問的,第一次是訪問SRAM的0x00地址的高字節,第二次是訪問SRAM的0x01地址處的低字節(SRAM的1個地址存儲16位數據),通過用示波器測量SRAM的A0地址信號得到,信號是先低在高的一個脈沖,驗證了前面的說法。2. 向SRAM寫數據時,道理也是一樣的,只是寫的時候通過BL[1:0]兩個管腳控制寫高字節還是低字節。
當訪問字數據時:訪問字時,FSMC分為2個半字訪問。
總結:外部SRAM就跟片內SRAM一樣用就行了。