
STM32的FSMC真是一个万能的总线控制器,不仅可以控制SRAM,NOR FLASH,NAND FLASH,PC Card,还能控制LCD,TFT. 一般越是复杂的东西,理解起来就很困难,但是使用上却很方便,如USB. 不过FSMC也有很诡异的地方.如
为什么呢?那时我还以为软件或硬件还是芯片有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
3.时序测量 简单原理草图 ![]() 写数据的时序 ![]() ![]() 读数据的时序 ![]() 1.数据总线设定为16位宽情况下测量FSMC时序,即
使用逻辑分析仪测量(循环执行下面这条语句,下同)的波形
![]() 可以看出NADV下降沿瞬间DATABUS上的数据被锁存器锁存,接着NWE低电平,总线输出0xABCD,数据0xABCD被写入0x1234这个地址.
![]() what?向这个地址写出现了两次总线操作. 为了一探究竟,我引出了控制线.
向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;), 像这样
写入一个值,实际在地址线上产生的值是addr/2(即addr>>2), 所以如果我们一定要向addrx写入0xABCD则我们要这样写
NADV为高电平时, NEW被拉低,NOE为高,且NBL1,NBL0为低,随后数据总线线上产生0xABCD于是0xABCD被写进SRAM的地址0x234中 那如果我们向一个奇数地址像这样 *(volatile uint16_t*)(0x60000469UL )=0xABCD;写入值会发生什么呢? ![]() 从图中我们可以看到,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. 当我们查看反汇编时发现,指令是相同的
以上是写入的时序,下面测量读取的时序 首先我们向SRAM的真实地址0x234,0x235分别写入0x8824,0x6507
然后读取:
![]() 如图tmp结果为0x8824 再试
![]() nUB=nLB=0;按16bit读 从0x234读得0X8824取高字节”88”作tmp低8位 从0x235读得0X6507取低字节”07”作tmp高8位 最终tmp=0x0788 接下来验证更特殊的
![]() 由于NBL1=0,NBL0=1,0xCD被写入0x234的高地址, 数据总线上出现的值是0xCDNN, NN是随机数据,不过一般是和高位一样的值
![]() 由于NBL1=1,NBL0=0,0xCD被写入0x234的低地址, 数据总线上出现的值是0xNNCD,NN是随机数据 验证字节读取的 首先我们向SRAM的真实地址0x234,0x235分别写入0x8824,0x6507
然后这样读取
![]()
![]() 还有更特殊的没有,有!
![]() *(volatile int64_t*)(0x60000469UL)=0XABCDEF1234567890;//0XABCD EF12 3456 7890,如图,对奇地址写比偶地址多一次操作: ![]()
![]()
![]() 1.数据总线设定为8位宽情况下测量FSMC时序,即 FsmcInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_8b;
![]()
![]()
![]()
tmp=0xcd44 ![]()
tmp=0x44 ![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]() ———————————————— 版权声明:魏波. |
STM32 GUI LTDC 最大像素时钟评估方法
【2025·STM32峰会】GUI解决方案实训分享1-对LVGL咖啡机例程的牛刀小试以及问题排查
OpenBLT移植到STM32F405开发板
为什么要先开启STM32外设时钟?
【STM32MP157】从ST官方例程中分析RPMsg-TTY/SDB核间通信的使用方法
【经验分享】STM32实例-RTC实时时钟实验④-获取RTC时间函数与中断服务函数
STM32 以太网 MAC Loopback 的实现
STM32功能安全设计包,助力产品功能安全认证
基于STM32启动过程startup_xxxx.s文件经验分享
HRTIM 指南