日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

STM32的FSMC详解

發布時間:2025/6/15 编程问答 17 豆豆
生活随笔 收集整理的這篇文章主要介紹了 STM32的FSMC详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

STM32的FSMC真是一個萬能的總線控制器,不僅可以控制SRAM,NOR FLASH,NAND FLASH,PC Card,還能控制LCD,TFT.

一般越是復雜的東西,理解起來就很困難,但是使用上卻很方便,如USB.

不過FSMC也有很詭異的地方.如

*(volatile uint16_t *)0x60400000=0x0; // 實際地址A21=1,而非A22.[注:0x60400000=0x60000000|(1UL<<22) ] *(volatile uint16_t *)0x60800000=0x0; // 實際地址A22=1,而非A23 [注:0x60800000=0x60000000|(1UL<<23) ]
  • 1
  • 2
  • 3
  • 4

為什么呢?那時我還以為軟件或硬件還是芯片有BUG,?
我就是從上面的不解中開始研究FSMC的…..

1.FSMC信號引腳

STM32的管腳排列很沒有規律,而且分布在多個不同端口上,初始化要十分小心.需要用到的引腳都要先初始化成”復用功能推挽輸出”模式.(GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP )?
并且開啟時鐘 (RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOx, ENABLE); ) 像STM32F103Z(144腳)芯片有獨立的地址和數據總線,而STM32F103V(100腳)就沒有, 地址和數據總線要像51單片機一樣分時復用,而在STM32F103R系列(64腳)就沒有FSMC模塊.

復用總線時管腳:?
PD14,//FSMC_DA0?
PD15,//FSMC_DA1?
PD0 ,//FSMC_DA2?
PD1 ,//FSMC_DA3?
PE7 ,//FSMC_DA4?
PE8 ,//FSMC_DA5?
PE9 ,//FSMC_DA6?
PE10,//FSMC_DA7?
PE11,//FSMC_DA8?
PE12,//FSMC_DA9?
PE13,//FSMC_DA10?
PE14,//FSMC_DA11?
PE15,//FSMC_DA12?
PD8 ,//FSMC_DA13?
PD9 ,//FSMC_DA14?
PD10,//FSMC_DA15?
PD11,//FSMC_A16?
PD12,//FSMC_A17?
PD13,//FSMC_A18?
PE3 ,//FSMC_A19?
PE4 ,//FSMC_A20?
PE5 ,//FSMC_A21?
PE6 ,//FSMC_A22?
PE2 ,//FSMC_A23?
PG13,//FSMC_A24//STM32F103Z?
PG14,//FSMC_A25//STM32F103Z

獨立的地址總線管腳:?
[注:總線是16Bit情況下,FSMC通過FSMC_NBL1,FSMC_NBL0,區分高低字節.下面W代表WORD,即16BIT字.]?
PF0 ,//FSMC_A0 //2^1=2W =4 Bytes //144PIN STM32F103Z?
PF1 ,//FSMC_A1 //2^2=4W =8 Bytes//144PIN STM32F103Z?
PF2 ,//FSMC_A2 //2^3=8W= 16 Bytes //144PIN STM32F103Z?
PF3 ,//FSMC_A3 //2^4=16W =32 Bytes//144PIN STM32F103Z?
PF4 ,//FSMC_A4 //2^5=32W =64 Bytes//144PIN STM32F103Z?
PF5 ,//FSMC_A5 //2^6=64W =128 Bytes//144PIN STM32F103Z?
PF12,//FSMC_A6 //2^7=128W =256 Bytes //144PIN STM32F103Z?
PF13,//FSMC_A7 //2^8=256W =512 Bytes //144PIN STM32F103Z?
PF14,//FSMC_A8 //2^9= 512W =1k Bytes//144PIN STM32F103Z?
PF15,//FSMC_A9 //2^10=1kW =2k Bytes//144PIN STM32F103Z?
PG0 ,//FSMC_A10 //2^11=2kW =4k Bytes//144PIN STM32F103Z?
PG1 ,//FSMC_A11 //2^12=4kW =8k Bytes//144PIN STM32F103Z?
PG2 ,//FSMC_A12 //2^13=8kW =16k Bytes//144PIN STM32F103Z?
PG3 ,//FSMC_A13 //2^14=16kW =32k Bytes//144PIN STM32F103Z?
PG4 ,//FSMC_A14 //2^15=32kW =64k Bytes//144PIN STM32F103Z?
PG5 ,//FSMC_A15 //2^16=64kW =128k Bytes//144PIN STM32F103Z?
PD11,//FSMC_A16 //2^17=128kW =256k Bytes?
PD12,//FSMC_A17 //2^18=256kW =512k Bytes?
PD13,//FSMC_A18 //2^19=512kW =1M Bytes?
PE3 ,//FSMC_A19 //2^20=1MW =2M Bytes?
PE4 ,//FSMC_A20 //2^21=2MW =4M Bytes?
PE5 ,//FSMC_A21 //2^22=4MW =8M Bytes?
PE6 ,//FSMC_A22 //2^23=8MW =16M Bytes?
PE2 ,//FSMC_A23 //2^24=16MW =32M Bytes //100PIN STM32F103V MAX?
PG13,//FSMC_A24 //2^25=32MW =64M Bytes //144PIN STM32F103Z?
PG14,//FSMC_A25 //2^26=64MW =128M Bytes //144PIN STM32F103Z

獨立的數據總線管腳:?
PD14,//FSMC_D0?
PD15,//FSMC_D1?
PD0 ,//FSMC_D2?
PD1 ,//FSMC_D3?
PE7 ,//FSMC_D4?
PE8 ,//FSMC_D5?
PE9 ,//FSMC_D6?
PE10,//FSMC_D7?
PE11,//FSMC_D8?
PE12,//FSMC_D9?
PE13,//FSMC_D10?
PE14,//FSMC_D11?
PE15,//FSMC_D12?
PD8 ,//FSMC_D13?
PD9 ,//FSMC_D14?
PD10,//FSMC_D15?
控制信號?
PD4,//FSMC_NOE,/RD?
PD5,//FSMC_NWE,/WR?
PB7,//FSMC_NADV,/ALE?
PE1,//FSMC_NBL1,/UB?
PE0,//FSMC_NBL0,/LB?
PD7,//FSMC_NE1,/CS1?
PG9,//FSMC_NE2,/CS2?
PG10,//FSMC_NE3,/CS3?
PG12,//FSMC_NE4,/CS4?
//PD3,//FSMC_CLK?
//PD6,//FSMC_NWAIT

2.地址的分配

地址與片選是掛勾的,也就是說器件掛載在哪個片選引腳上,就固定了訪問地址范圍和FsmcInitStructure.FSMC_Bank

//地址范圍:0x60000000~0x63FFFFFF,片選引腳PD7(FSMC_NE1),最大支持容量64MB, //[在STM32F103V(100腳)上地址范圍為A0~A23,最大容量16MB] FsmcInitStructure.FSMC_Bank =FSMC_Bank1_NORSRAM1;//地址范圍:0x64000000~0x67FFFFFF, 片選引腳PG9(FSMC_NE2),最大支持容量64MB FsmcInitStructure.FSMC_Bank =FSMC_Bank1_NORSRAM2;//地址范圍:0x68000000~0x6BFFFFFF,片選引腳PG10(FSMC_NE3),最大支持容量64MB FsmcInitStructure.FSMC_Bank =FSMC_Bank1_NORSRAM3; //地址范圍:0x6C000000~0x6FFFFFFF,片選引腳(PG12 FSMC_NE4),最大支持容量64MB FsmcInitStructure.FSMC_Bank =FSMC_Bank1_NORSRAM4;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

3.時序測量

簡單原理草圖?
?
寫數據的時序?

?
讀數據的時序?
?
1.數據總線設定為16位寬情況下測量FSMC時序,即

FsmcInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;
  • 1

使用邏輯分析儀測量(循環執行下面這條語句,下同)的波形

*(volatile uint16_t *)(0x60002468UL)=0xABCD;
  • 1
  • 2


可以看出NADV下降沿瞬間DATABUS上的數據被鎖存器鎖存,接著NWE低電平,總線輸出0xABCD,數據0xABCD被寫入0x1234這個地址.

*(volatile uint16_t*)(0x60002469UL )=0xABCD;
  • 1
  • 2


what?向這個地址寫出現了兩次總線操作.

為了一探究竟,我引出了控制線.

*(volatile uint16_t*)(0x60000468UL )=0xABCD;
  • 1
  • 2

向0x60000468UL寫入0xABCD到底會發什么??
?
從時序圖中我們可以看到, 向0x60000468UL在地址(在范圍:0x60000000~0x63FFFFFF內)寫入數據,片選引腳PD7(FSMC_NE1)被拉低.而在這之前,數據總線上先產生0x234,于是在NADV下降沿瞬間,數據被鎖存在地址鎖存器上(A0~A15),與A16~A25(如果有配置的話,會在NE1下降沿同時送出)組合成完整的地址信號.然而有人會問這個0x234是哪來的,你是否注意到它正好等于0x468/2,難道是巧合嗎?不是的,在16位數據總線情況下(NORSRAMInitStrc.FSMC_MemoryDataWidth=FSMC_MemoryDataWidth_16b;),?
像這樣

*(volatile uint16_t*)(0x60000000|addr)=0xABCD;
  • 1
  • 2

寫入一個值,實際在地址線上產生的值是addr/2(即addr>>2),?
所以如果我們一定要向addrx寫入0xABCD則我們要這樣寫

*(volatile uint16_t*)(0x60000000|addrx<<1)=0xABCD;
  • 1

NADV為高電平時, NEW被拉低,NOE為高,且NBL1,NBL0為低,隨后數據總線線上產生0xABCD于是0xABCD被寫進SRAM的地址0x234中

那如果我們向一個奇數地址像這樣

*(volatile uint16_t*)(0x60000469UL )=0xABCD;寫入值會發生什么呢?
  • 1
  • 2

?
從圖中我們可以看到,STM32其實分成了兩次字節寫的過程,第一次向0x469/2寫入0xCD,第二次向0x469/2+1寫入0xAB,?
有人會問你為什么這樣說,NWE為低時總線上不是0xCDAB嗎?沒錯,但是注意NBL1,NBL0的電平組合,NBL1連接到SRAM的nUB,NBL0連接到SRAM的nLB.第一次NEW為低時NBL1為低,NBL0為高,0xCDAB的高位被寫入SRAM的0x234,第二次NWE為低時NBL1為高,NBL0為低,0xCDAB的低位被寫入SRAM的0x235.?
當我們查看反匯編時發現,指令是相同的

0x080036C4 0468 DCW 0x0468 0x080036C6 6000 DCW 0x6000 MOVW r0,#0xABCD LDR r1,[pc,#420] ; @0x080036C4//r1=0x60000468 STRH r0,[r1,#0x00]0x080036C4 0469 DCW 0x0469 0x080036C6 6000 DCW 0x6000 MOVW r0,#0xABCD LDR r1,[pc,#420] ; @0x080036C4//r1=0x60000469 STRH r0,[r1,#0x00]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

以上是寫入的時序,下面測量讀取的時序

首先我們向SRAM的真實地址0x234,0x235分別寫入0x8824,0x6507

*(volatile uint16_t*)(0x60000000UL |0x234 <<1 )=0x8824;*(volatile uint16_t*)(0x60000000UL |0x235 <<1 )=0x6507;*(volatile uint16_t*)(0x60000000UL |0x236 <<1 )=0x6735;*(volatile uint16_t*)(0x60000000UL |0x237 <<1 )=0x2003;*(volatile uint16_t*)(0x60000000UL |0x238 <<1 )=0x6219;
  • 1
  • 2
  • 3
  • 4
  • 5

然后讀取:

tmp=*(volatile uint16_t*)(0x60000468UL );
  • 1
  • 2

?
如圖tmp結果為0x8824?
再試

tmp=*(volatile uint16_t*)(0x60000469UL );
  • 1
  • 2


nUB=nLB=0;按16bit讀?
從0x234讀得0X8824取高字節”88”作tmp低8位?
從0x235讀得0X6507取低字節”07”作tmp高8位?
最終tmp=0x0788

接下來驗證更特殊的

*(volatile uint8_t*)(0x60000469UL )=0xABCD;
  • 1
  • 2

?
由于NBL1=0,NBL0=1,0xCD被寫入0x234的高地址,?
數據總線上出現的值是0xCDNN, NN是隨機數據,不過一般是和高位一樣的值

*(volatile uint8_t*)(0x60000468UL )=0xABCD;
  • 1
  • 2

?
由于NBL1=1,NBL0=0,0xCD被寫入0x234的低地址,?
數據總線上出現的值是0xNNCD,NN是隨機數據

驗證字節讀取的?
首先我們向SRAM的真實地址0x234,0x235分別寫入0x8824,0x6507

*(volatile uint16_t*)(0x60000000UL |0x234 <<1 )=0x8824; *(volatile uint16_t*)(0x60000000UL |0x235 <<1 )=0x6507;
  • 1
  • 2

然后這樣讀取

tmp=*(volatile uint8_t*)(0x60000469UL );//對奇地址的單字節讀取,數據總線的高8位被返回 tmp=0x88
  • 1
  • 2

tmp=*(volatile uint8_t*)(0x60000468UL );//對偶地址的單字節讀取,數據總線的低8位被返回 tmp=0x24
  • 1
  • 2

還有更特殊的沒有,有!

*(volatile int64_t*)(0x60000468UL)=0XABCDEF1234567890;//0XABCD EF12 3456 7890,如圖,分別進行了4次操作才寫完:
  • 1
  • 2

*(volatile int64_t*)(0x60000469UL)=0XABCDEF1234567890;//0XABCD EF12 3456 7890,如圖,對奇地址寫比偶地址多一次操作:
  • 1
  • 2

*(volatile uint16_t*)(0x60000000UL |0x234 <<1 )=0x8824;*(volatile uint16_t*)(0x60000000UL |0x235 <<1 )=0x6507;*(volatile uint16_t*)(0x60000000UL |0x236 <<1 )=0x6735;*(volatile uint16_t*)(0x60000000UL |0x237 <<1 )=0x2003;*(volatile uint16_t*)(0x60000000UL |0x238 <<1 )=0x6219;
  • 1
  • 2
  • 3
  • 4
  • 5
tmp=*(volatile int64_t*)(0x60000469UL);// tmp=0x1920036735650788
  • 1
  • 2

tmp=*(volatile int64_t*)(0x60000468UL); //tmp=0x2003673565078824
  • 1
  • 2

1.數據總線設定為8位寬情況下測量FSMC時序,即?
FsmcInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_8b;

*(volatile uint16_t*)(0x60000468UL )=0xABCD;
  • 1
  • 2

*(volatile uint16_t*)(0x60000469UL )=0xABCD;
  • 1
  • 2

*(volatile uint16_t*)(0x60000468UL )=0x3344; *(volatile uint16_t*)(0x60000469UL )=0xABCD;
  • 1
  • 2
  • 3

tmp=(volatile uint16_t)(0x60000469UL ); //tmp=0xabcd?

tmp=*(volatile uint16_t*)(0x60000468UL );
  • 1
  • 2

tmp=0xcd44

tmp=*(volatile uint8_t*)(0x60000468UL );
  • 1
  • 2

tmp=0x44?

tmp=*(volatile uint8_t*)(0x60000469UL ); tmp=0xcd
  • 1
  • 2
  • 3

*(volatile uint8_t*)(0x60000469UL )=0xABCD;
  • 1
  • 2

*(volatile uint8_t*)(0x60000468UL )=0xABCD;
  • 1
  • 2

tmp=*(volatile uint64_t*)(0x60000468UL ); tmp=0x2003673565ABCD44
  • 1
  • 2
  • 3

tmp=*(volatile uint64_t*)(0x60000469UL );//tmp=0x192003673565ABCD
  • 1
  • 2

*(volatile uint64_t*)(0x60000469UL )=0XABCDEF1234567890;
  • 1
  • 2

*(volatile uint64_t*)(0x60000468UL )=0XABCDEF1234567890;
  • 1
  • 2

總結

以上是生活随笔為你收集整理的STM32的FSMC详解的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。