本例演示用的軟硬件:
- 片內外設驅動庫:STM32CubeF41.24.1的HAL庫1.7.6,2019年4月12日
- IDE:MDK-ARM 5.28.0.0,2019年5月
- 開發板:正點原子F407探索者,片外SRAM掛在FSMC_NORPSRAM3,16bit×219=1MiB
本例的目的是讓編程人員使用片外SRAM就像使用片內SRAM一樣,即不用把任何變量聲明到指定的RAM地址、連接器也能自動地把片外SRAM作為變量的存儲空間
如果把所有需要被放到片外SRAM的變量用__attribute__((at()))、指針等聲明到片外SRAM,那么完全不用像本例這樣
執行main()前執行片內Flash上初始化FSMC及其GPIO的指令:
- 有些指令由啟動文件的匯編代碼生成,例如對於本例的STM32F407ZG來說這個啟動文件就是startup_stm32f407xx.s(默認版本位於STM32CubeF4\Drivers\CMSIS\Device\ST\STM32F4xx\Source\Templates\arm)
- 有些函數在system_stm32f4xx.c(位於STM32CubeF4\Drivers\CMSIS\Device\ST\STM32F4xx\Source\Templates\)
對於本例的STM32F407來說修改上述的2個源文件的方法是:
1.換用官方的使用片外SRAM作運行內存的例程的啟動文件
例如STM32CubeF4\Projects\STM324xG_EVAL\Examples\FSMC\FSMC_SRAM_DataMemory\MDK-ARM\startup_stm32f407xx.s
可以看到適用於使用片外SRAM運存的啟動文件相較於默認版本的啟動文件變化的地方有
- 第52行、第75行:定義用於初始化片外SRAM的棧
- 第191行~第192行:在執行main()前、執行完啟動文件定義的指令后將棧頂指針恢復為默認值
52 __initial_spTop EQU 0x20000400 ; stack used for SystemInit & SystemInit_ExtMemCtl
75 __Vectors DCD __initial_spTop ; Top of Stack
177 __Vectors_End 178 179 __Vectors_Size EQU __Vectors_End - __Vectors 180 181 AREA |.text|, CODE, READONLY 182 183 ; Reset handler 184 Reset_Handler PROC 185 EXPORT Reset_Handler [WEAK] 186 IMPORT SystemInit 187 IMPORT __main 188 189 LDR R0, =SystemInit 190 BLX R0 191 LDR R0, =__initial_sp ; restore original stack pointer 192 MSR MSP, R0 193 LDR R0, =__main 194 BX R0 195 ENDP
2.取消注釋system_stm32f4xx.c的第96行
91 /************************* Miscellaneous Configuration ************************/ 92 /*!< Uncomment the following line if you need to use external SRAM or SDRAM as data memory */ 93 #if defined(STM32F405xx) || defined(STM32F415xx) || defined(STM32F407xx) || defined(STM32F417xx)\ 94 || defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || defined(STM32F439xx)\ 95 || defined(STM32F469xx) || defined(STM32F479xx) || defined(STM32F412Zx) || defined(STM32F412Vx) 96 /* #define DATA_IN_ExtSRAM */ 97 #endif /* STM32F40xxx || STM32F41xxx || STM32F42xxx || STM32F43xxx || STM32F469xx || STM32F479xx ||\ 98 STM32F412Zx || STM32F412Vx */
那么system_stm32f4xx.c的
- 第662行~第716行
- 開啟FSMC模塊用到的GPIO的時鍾(第662行)
- 配置相應GPIO口的模式、速度等信息(第667行~第712行)
- 開啟FSMC模塊的時鍾(第716行))
- 第737行~第741行
- 配置FSMC_NORPSRAM的控制、時序(第739行~第741行)
都會被執行
655 #if defined(STM32F405xx) || defined(STM32F415xx) || defined(STM32F407xx) || defined(STM32F417xx)\ 656 || defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || defined(STM32F439xx)\ 657 || defined(STM32F469xx) || defined(STM32F479xx) || defined(STM32F412Zx) || defined(STM32F412Vx) 658 659 #if defined(DATA_IN_ExtSRAM) 660 /*-- GPIOs Configuration -----------------------------------------------------*/ 661 /* Enable GPIOD, GPIOE, GPIOF and GPIOG interface clock */ 662 RCC->AHB1ENR |= 0x00000078; 663 /* Delay after an RCC peripheral clock enabling */ 664 tmp = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIODEN); 665 666 /* Connect PDx pins to FMC Alternate function */ 667 GPIOD->AFR[0] = 0x00CCC0CC; 668 GPIOD->AFR[1] = 0xCCCCCCCC; 669 /* Configure PDx pins in Alternate function mode */ 670 GPIOD->MODER = 0xAAAA0A8A; 671 /* Configure PDx pins speed to 100 MHz */ 672 GPIOD->OSPEEDR = 0xFFFF0FCF; 673 /* Configure PDx pins Output type to push-pull */ 674 GPIOD->OTYPER = 0x00000000; 675 /* No pull-up, pull-down for PDx pins */ 676 GPIOD->PUPDR = 0x00000000; 677 678 /* Connect PEx pins to FMC Alternate function */ 679 GPIOE->AFR[0] = 0xC00CC0CC; 680 GPIOE->AFR[1] = 0xCCCCCCCC; 681 /* Configure PEx pins in Alternate function mode */ 682 GPIOE->MODER = 0xAAAA828A; 683 /* Configure PEx pins speed to 100 MHz */ 684 GPIOE->OSPEEDR = 0xFFFFC3CF; 685 /* Configure PEx pins Output type to push-pull */ 686 GPIOE->OTYPER = 0x00000000; 687 /* No pull-up, pull-down for PEx pins */ 688 GPIOE->PUPDR = 0x00000000; 689 690 /* Connect PFx pins to FMC Alternate function */ 691 GPIOF->AFR[0] = 0x00CCCCCC; 692 GPIOF->AFR[1] = 0xCCCC0000; 693 /* Configure PFx pins in Alternate function mode */ 694 GPIOF->MODER = 0xAA000AAA; 695 /* Configure PFx pins speed to 100 MHz */ 696 GPIOF->OSPEEDR = 0xFF000FFF; 697 /* Configure PFx pins Output type to push-pull */ 698 GPIOF->OTYPER = 0x00000000; 699 /* No pull-up, pull-down for PFx pins */ 700 GPIOF->PUPDR = 0x00000000; 701 702 /* Connect PGx pins to FMC Alternate function */ 703 GPIOG->AFR[0] = 0x00CCCCCC; 704 GPIOG->AFR[1] = 0x000000C0; 705 /* Configure PGx pins in Alternate function mode */ 706 GPIOG->MODER = 0x00085AAA; 707 /* Configure PGx pins speed to 100 MHz */ 708 GPIOG->OSPEEDR = 0x000CAFFF; 709 /* Configure PGx pins Output type to push-pull */ 710 GPIOG->OTYPER = 0x00000000; 711 /* No pull-up, pull-down for PGx pins */ 712 GPIOG->PUPDR = 0x00000000; 713 714 /*-- FMC/FSMC Configuration --------------------------------------------------*/ 715 /* Enable the FMC/FSMC interface clock */ 716 RCC->AHB3ENR |= 0x00000001;
734 #if defined(STM32F405xx) || defined(STM32F415xx) || defined(STM32F407xx)|| defined(STM32F417xx)\ 735 || defined(STM32F412Zx) || defined(STM32F412Vx) 736 /* Delay after an RCC peripheral clock enabling */ 737 tmp = READ_BIT(RCC->AHB3ENR, RCC_AHB3ENR_FSMCEN); 738 /* Configure and enable Bank1_SRAM2 */ 739 FSMC_Bank1->BTCR[2] = 0x00001011; 740 FSMC_Bank1->BTCR[3] = 0x00000201; 741 FSMC_Bank1E->BWTR[2] = 0x0FFFFFFF;
從system_stm32f4xx.c的第738行的注釋可知,第739行~第741行分別配置的是FSMC_NORPSRAM2的控制寄存器、讀寫時序寄存器、寫時序寄存器,而我使用的開發板的片外SRAM掛在FSMC_NORPSRAM3,所以需要修改system_stm32f4xx.c上述的寫GPIO寄存器、FSMC寄存器的代碼,獲取正確的寄存器值的方法是核外片內外設只開啟FSMC_NORPSRAM3及其GPIO,再在硬件調試過程中復制出相應的寄存器值
3.更正system_stm32f4xx.c的上述代碼
(再次提醒沒仔細讀題的讀者:下文的代碼只被保證適用於( (STM32F407ZG) && (片外SRAM掛在FSMC_NORPSRAM3) && (片外SRAM是16bit×219=1MiB) )的情況,且應該根據你用的SRAM芯片、STM32的AHB總線時鍾頻率等信息修改第739行~第741行配置FSMC寄存器用的值。用上一段提到的方法獲取適用於你的開發板的寄存器值,用本文初提到的方法獲取適用於你的單片機的啟動文件):
655 #if defined(STM32F405xx) || defined(STM32F415xx) || defined(STM32F407xx) || defined(STM32F417xx)\ 656 || defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || defined(STM32F439xx)\ 657 || defined(STM32F469xx) || defined(STM32F479xx) || defined(STM32F412Zx) || defined(STM32F412Vx) 658 659 #if defined(DATA_IN_ExtSRAM) 660 /*-- GPIOs Configuration -----------------------------------------------------*/ 661 /* Enable GPIOD, GPIOE, GPIOF and GPIOG interface clock */ 662 RCC->AHB1ENR |= 0x00000078; 663 /* Delay after an RCC peripheral clock enabling */ 664 tmp = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIODEN); 665 666 /* Connect PDx pins to FMC Alternate function */ 667 GPIOD->AFR[0] = 0x00CC00CC; 668 GPIOD->AFR[1] = 0xCCCCCCCC; 669 /* Configure PDx pins in Alternate function mode */ 670 GPIOD->MODER = 0xAAAA0A0A; 671 /* Configure PDx pins speed to 100 MHz */ 672 GPIOD->OSPEEDR = 0xFFFF0F0F; 673 /* Configure PDx pins Output type to push-pull */ 674 GPIOD->OTYPER = 0x00000000; 675 /* No pull-up, pull-down for PDx pins */ 676 GPIOD->PUPDR = 0x00000000; 677 678 /* Connect PEx pins to FMC Alternate function */ 679 GPIOE->AFR[0] = 0xC00000CC; 680 GPIOE->AFR[1] = 0xCCCCCCCC; 681 /* Configure PEx pins in Alternate function mode */ 682 GPIOE->MODER = 0xAAAA800A; 683 /* Configure PEx pins speed to 100 MHz */ 684 GPIOE->OSPEEDR = 0xFFFFC00F; 685 /* Configure PEx pins Output type to push-pull */ 686 GPIOE->OTYPER = 0x00000000; 687 /* No pull-up, pull-down for PEx pins */ 688 GPIOE->PUPDR = 0x00000000; 689 690 /* Connect PFx pins to FMC Alternate function */ 691 GPIOF->AFR[0] = 0x00CCCCCC; 692 GPIOF->AFR[1] = 0xCCCC0000; 693 /* Configure PFx pins in Alternate function mode */ 694 GPIOF->MODER = 0xAA000AAA; 695 /* Configure PFx pins speed to 100 MHz */ 696 GPIOF->OSPEEDR = 0xFF000FFF; 697 /* Configure PFx pins Output type to push-pull */ 698 GPIOF->OTYPER = 0x00000000; 699 /* No pull-up, pull-down for PFx pins */ 700 GPIOF->PUPDR = 0x00000000; 701 702 /* Connect PGx pins to FMC Alternate function */ 703 GPIOG->AFR[0] = 0x00CCCCCC; 704 GPIOG->AFR[1] = 0x00000C00; 705 /* Configure PGx pins in Alternate function mode */ 706 GPIOG->MODER = 0x00200AAA; 707 /* Configure PGx pins speed to 100 MHz */ 708 GPIOG->OSPEEDR = 0x00300FFF; 709 /* Configure PGx pins Output type to push-pull */ 710 GPIOG->OTYPER = 0x00000000; 711 /* No pull-up, pull-down for PGx pins */ 712 GPIOG->PUPDR = 0x00000000; 713 714 /*-- FMC/FSMC Configuration --------------------------------------------------*/ 715 /* Enable the FMC/FSMC interface clock */ 716 RCC->AHB3ENR |= 0x00000001;
734 #if defined(STM32F405xx) || defined(STM32F415xx) || defined(STM32F407xx)|| defined(STM32F417xx)\ 735 || defined(STM32F412Zx) || defined(STM32F412Vx) 736 /* Delay after an RCC peripheral clock enabling */ 737 tmp = READ_BIT(RCC->AHB3ENR, RCC_AHB3ENR_FSMCEN); 738 /* Configure and enable Bank1_SRAM3 */ 739 FSMC_Bank1->BTCR[4] = 0x00001091; 740 FSMC_Bank1->BTCR[5] = 0x00100222;
4.在MDK把FSMC_NORPSRAM3映射的地址范圍0x68000000~0x6BFFFFFF的首1M設為運行內存
把0x68000000、0x100000分別填入下圖窗口右下角的"Read/Write Memory Areas"的”off-chip“的任一行:
5.修改完后Reuild
(注意:如果你用MDK-ARM以外的軟件修改了前述的2個源文件,那么MDK-ARM可能不知道你修改了那2個文件,所以如果僅Build,那么IDE可能會拿編譯舊版本的源文件得到的目標文件進行連接
或者你手動讓MDK-ARM知道你修改過那些文件再僅Build,比如在MDK-ARM打開前述的2個源文件,在里面隨便找個地方加個字再刪掉那個字了再Build)
從MAP文件可以看到,變量被分配到了片外SRAM映射的地址范圍0x68000000~0x68100000中,且單片機程序能正常
Exec Addr Load Addr Size Type Attr Idx E Section Name Object 0x68000000 0x0800628c 0x00000008 Data RW 19 .data main.o 0x68000008 0x08006294 0x00000008 Data RW 247 .data stm32f4xx_hal_msp.o 0x68000010 0x0800629c 0x0000000c Data RW 1539 .data stm32f4xx_hal.o 0x6800001c 0x080062a8 0x00000004 Data RW 1772 .data system_stm32f4xx.o 0x68000020 0x080062ac 0x00000004 Data RW 1842 .data tftlcd.o 0x68000024 - 0x00000050 Zero RW 18 .bss main.o 0x68000074 - 0x0000000e Zero RW 1840 .bss tftlcd.o 0x68000082 0x080062b0 0x00000006 PAD 0x68000088 - 0x00000400 Zero RW 1 STACK startup_stm32f407xx.o
希望CubeMX以后的版本能自動將片外RAM設為運存