历史上的今天
返回首页

历史上的今天

今天是:2024年09月21日(星期六)

2019年09月21日 | STM32的FSMC详解

2019-09-21 来源:eefocus

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) ]


为什么呢?那时我还以为软件或硬件还是芯片有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;


3.时序测量

简单原理草图 

原理草图 

写数据的时序 

写数据的时序
这里写图片描述 

读数据的时序 

这里写图片描述 

1.数据总线设定为16位宽情况下测量FSMC时序,即


FsmcInitStructure.FSMC_MemoryDataWidth =    FSMC_MemoryDataWidth_16b;   


使用逻辑分析仪测量(循环执行下面这条语句,下同)的波形


*(volatile uint16_t *)(0x60002468UL)=0xABCD;           

这里写图片描述

可以看出NADV下降沿瞬间DATABUS上的数据被锁存器锁存,接着NWE低电平,总线输出0xABCD,数据0xABCD被写入0x1234这个地址.


      *(volatile uint16_t*)(0x60002469UL )=0xABCD;              

这里写图片描述

what?向这个地址写出现了两次总线操作.


为了一探究竟,我引出了控制线.


 *(volatile uint16_t*)(0x60000468UL  )=0xABCD;           


向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;   


写入一个值,实际在地址线上产生的值是addr/2(即addr>>2), 

所以如果我们一定要向addrx写入0xABCD则我们要这样写


*(volatile uint16_t*)(0x60000000|addrx<<1)=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. 

当我们查看反汇编时发现,指令是相同的


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]


以上是写入的时序,下面测量读取的时序


首先我们向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;


然后读取:


tmp=*(volatile uint16_t*)(0x60000468UL  );

这里写图片描述 

如图tmp结果为0x8824 

再试


  tmp=*(volatile uint16_t*)(0x60000469UL  );

这里写图片描述

nUB=nLB=0;按16bit读 

从0x234读得0X8824取高字节”88”作tmp低8位 

从0x235读得0X6507取低字节”07”作tmp高8位 

最终tmp=0x0788


接下来验证更特殊的


*(volatile uint8_t*)(0x60000469UL   )=0xABCD; 

这里写图片描述 

由于NBL1=0,NBL0=1,0xCD被写入0x234的高地址, 

数据总线上出现的值是0xCDNN, NN是随机数据,不过一般是和高位一样的值


*(volatile uint8_t*)(0x60000468UL   )=0xABCD; 

这里写图片描述 

由于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;


然后这样读取


tmp=*(volatile uint8_t*)(0x60000469UL   );//对奇地址的单字节读取,数据总线的高8位被返回 tmp=0x88


这里写图片描述

tmp=*(volatile uint8_t*)(0x60000468UL   );//对偶地址的单字节读取,数据总线的低8位被返回 tmp=0x24

这里写图片描述

还有更特殊的没有,有!


 *(volatile int64_t*)(0x60000468UL)=0XABCDEF1234567890;//0XABCD EF12 3456 7890,如图,分别进行了4次操作才写完:

这里写图片描述

*(volatile int64_t*)(0x60000469UL)=0XABCDEF1234567890;//0XABCD EF12 3456 7890,如图,对奇地址写比偶地址多一次操作:

这里写图片描述

 *(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;


 tmp=*(volatile int64_t*)(0x60000469UL);// tmp=0x1920036735650788 

这里写图片描述

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

这里写图片描述


1.数据总线设定为8位宽情况下测量FSMC时序,即 

FsmcInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_8b;


*(volatile uint16_t*)(0x60000468UL  )=0xABCD; 

这里写图片描述

*(volatile uint16_t*)(0x60000469UL  )=0xABCD;

这里写图片描述

*(volatile uint16_t*)(0x60000468UL  )=0x3344;

*(volatile uint16_t*)(0x60000469UL  )=0xABCD;


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

这里写图片描述

tmp=*(volatile uint16_t*)(0x60000468UL  );


tmp=0xcd44

这里写图片描述


tmp=*(volatile uint8_t*)(0x60000468UL  );


tmp=0x44 

这里写图片描述

tmp=*(volatile uint8_t*)(0x60000469UL  ); 

tmp=0xcd

这里写图片描述


*(volatile uint8_t*)(0x60000469UL   )=0xABCD;

这里写图片描述


*(volatile uint8_t*)(0x60000468UL   )=0xABCD;

这里写图片描述

tmp=*(volatile uint64_t*)(0x60000468UL  );

tmp=0x2003673565ABCD44

这里写图片描述

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

这里写图片描述


*(volatile uint64_t*)(0x60000469UL  )=0XABCDEF1234567890;  

这里写图片描述

*(volatile uint64_t*)(0x60000468UL  )=0XABCDEF1234567890;  

这里写图片描述


推荐阅读

史海拾趣

Don Connex Electronics Co Ltd公司的发展小趣事

Don Connex Electronics Co Ltd成立于上世纪90年代初,当时电子行业正处于快速发展阶段。公司的创始人李明凭借其在半导体领域的深厚技术背景,带领团队研发出了一款高性能的电源管理芯片,这款芯片在业界引起了广泛关注。该芯片以其低功耗、高效率的特性,迅速在市场上打开了局面,为公司的初创阶段奠定了坚实的基础。

Achronix Semiconductor Corporation公司的发展小趣事

Don Connex Electronics Co Ltd成立于上世纪90年代初,当时电子行业正处于快速发展阶段。公司的创始人李明凭借其在半导体领域的深厚技术背景,带领团队研发出了一款高性能的电源管理芯片,这款芯片在业界引起了广泛关注。该芯片以其低功耗、高效率的特性,迅速在市场上打开了局面,为公司的初创阶段奠定了坚实的基础。

DL Instruments LLC公司的发展小趣事

DL Instruments的成功离不开其领导层的智慧与远见。David Woolston先生作为公司的创始人之一,凭借其丰富的行业经验和敏锐的市场洞察力,带领公司不断突破自我、超越竞争对手。他注重人才培养和技术创新,为公司的发展奠定了坚实的基础。

常州能动(ENDRIVE)公司的发展小趣事

常州能动(ENDRIVE)公司自创立之初,就将技术创新作为公司发展的核心驱动力。在公司发展的早期阶段,研发团队成功开发出一款高效能、低能耗的电源管理芯片,这一创新产品在市场上引起了广泛关注。通过不断的技术迭代和优化,这款芯片的性能逐渐提升,成本逐渐降低,成为了公司的明星产品,为公司的快速发展奠定了坚实基础。

Bay Linear Inc公司的发展小趣事

Bay Linear Inc公司一直注重人才培养和团队建设。公司建立了完善的人才培养和激励机制,吸引了一批批优秀的研发人才和管理人才。同时,公司还积极营造团结协作、创新进取的企业文化,使团队成员能够充分发挥自己的才能和潜力,共同推动公司的发展。

Component Research Co公司的发展小趣事

Component Research Co最初只是一家小型电子元件研发公司,面临着激烈的市场竞争。然而,公司研发团队在一次实验中意外发现了一种新型半导体材料,这种材料具有出色的导电性能和稳定性。公司迅速将这一发现转化为实际产品,并推向市场。新产品因其卓越性能迅速受到客户青睐,Component Research Co因此名声大噪,逐渐在电子元件市场占据一席之地。

问答坊 | AI 解惑

msp430BBSjin经典问答

本帖最后由 paulhyde 于 2014-9-15 09:25 编辑 msp430BBSjin经典问答  …

查看全部问答>

两个单片机共用同一个时钟

两个STC单片机共用一个有源晶振提供时钟,该怎么接入是XTAL1还是XTAL2?…

查看全部问答>

PIC16F73的PORTA在设置为模拟量输入口后,如何再设置为数字I/O口?

PIC16F73的PORTA可以做模拟量输入口,在刚加电时是模拟口还是数字I/O? 如果使用ACDON1设置为模拟量输入口后,如何将其改回数字I/O?…

查看全部问答>

在模拟器上安装SQL Server Compact Edition ?

怎样在wince6模拟器上安装数据库SQL Server Compact Edition …

查看全部问答>

激光测距仪

我是用激光测距仪获得一些数据后,怎么能用什么软降将其成像,请帮帮忙…

查看全部问答>

请教PCI moden卡驱动的编写.卡在一个地方了求解

看了武安河对PCI卡的驱动编写,也知道程序的流程入口:: 如果我插入的是moden我要初始化哪一些东西.com 、寄存器......?在哪里进行初始化.......? PCI的DMA的读和写是与对应的应用程序的读和写对吗? moden是怎么与网络上的另一个moden进行数据对 ...…

查看全部问答>

编译出错,第一次见到这种错误,请问什么意思?

程序是利用UART中断和485发送命令并接收数据的,定义了两个数组来存放命令和接收数据,第一次遇到这个错误,请问是什么意思?…

查看全部问答>

【EEWORLD大学堂TI教室】学习整理

以下是在学习视频的时候在本子上笔记下来的!现在整理一下,逐步补全和完善! 。。。。。。。。 贴不完了,文档内详(补充中) SRAM   430(16位) 250UA/MIPS(一般)  100UA/MIPS(430系统列,基于ARM) Value Line  1 ...…

查看全部问答>

寻线传感器

本帖最后由 paulhyde 于 2014-9-15 03:46 编辑 寻线传感器原理  …

查看全部问答>