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详解相关推荐

  1. STM32 HAL库详解 及 手动移植

    源: STM32 HAL库详解 及 手动移植

  2. stm32 DMA使用详解

    转自:http://www.cnblogs.com/121792730applllo/p/3154447.html STM32 DMA使用详解 DMA部分我用到的相对简单,当然,可能这是新东西,我暂时 ...

  3. STM32启动文件详解-比较清晰的一篇

    STM32启动文件详解 启动文件使用的 ARM 汇编指令汇总 启动程序源码注释(点此下载) 1. Stack-栈 Stack_Size EQU 0x00000400AREA STACK, NOINIT ...

  4. STM32 CAN通信协议详解—小白入门(二)

    文章目录 (一)CAN通信协议简介 (二)CAN物理层 2.1.闭环总线网络2.2.开环总线网络2.3.通信节点2.4.差分信号2.5.CAN协议的差分信号 (三)协议层 3.1.CAN的波特率及位同 ...

  5. STM32串口通信详解以及通信异常或者卡死常见问题分析

    STM32串口通信详解以及通信异常或者卡死常见问题分析 目录 STM32串口通信详解以及通信异常或者卡死常见问题分析 一.常见的异常问题 二.STM32的串口简介 1.串口的通讯方式 ①按数据传输方向 ...

  6. STM32最小系统详解

    STM32最小系统详解 1. 电源电路 2. 晶振电路 3. 复位电路 4. 下载电路(串口下载) 本文章将以普中的STM32F103系列的开发板为载体,任何一款STM32开发板都是在其最小系统基础上 ...

  7. STM32的定时器详解(嵌入式学习)

    STM32的定时器详解 0. 前言 1. Systick定时器 概念 工作原理 时钟基准 Systick练习 2. HAL_Delay函数分析 3. 定时器 基本概念 定时器分类 定时器组成 计数器 ...

  8. STM32 FSMC 详解

    LCD有如下控制线: CS:Chip Select 片选,低电平有效 RS:Register Select 寄存器选择 WR:Write 写信号,低电平有效 RD:Read 读信号,低电平有效 RES ...

  9. STM32 FMC原理详解

    关于FSMC的基本原理已经在这两篇讲解了,如果有不懂的建议先看一下,这里我们对一些基本概念会说的少一些,主要就是针对FMC的特点和FSMC跟FMC的区别做主要的阐述(区别不大). STM32 FSMC ...

最新文章

  1. 网络状态代码数字的含义
  2. UNIX文件的实现——索引节点
  3. 破解必备之:MSIL指令集
  4. 输出任意一元二次函数的结果值
  5. C#.Net工作笔记019---葡萄城控件FlexGrid自定义风格_比如给某列设置某个颜色_该颜色不受选择行的影响
  6. Hadoop job任务分配
  7. 贝叶斯方法学习笔记(二)
  8. Excel VBA与VSTO基础实战指南 VBA和VSTO权威教材
  9. 【图文并茂】正版Win10 U盘重装系统教程
  10. 有趣的微分方程传之可分离变量的微分方程
  11. uniapp封装方法
  12. 基于RTL—SDR及Simulink的FM收音机仿真
  13. 2017计科01-04编译原理模拟测试一
  14. 自己动手搭建Fabric网络,修改当前工作目录名之后出现的错误
  15. 类加载及执行子系统的案例分析
  16. 当ChatGPT续写《红楼梦》,能替代原著吗?
  17. uni-app 轮播图视频+图片 视频图片全屏预览 两种方法
  18. 送货单打印软件如何导入格式
  19. Keil警告warning: #223-D: function “memcpy” declared implicitly
  20. 酷客scrm—马化腾首次撰文阐述私域流量

热门文章

  1. mysql decode encode 乱码问题
  2. java 基础 --静态
  3. mysql空间扩展 VS PostGIS
  4. ie下LI的间距问题
  5. POJ-2195(最小费用最大流)
  6. Matlab符号计算结果过长无法在屏幕中显示的问题
  7. Spring Boot集成Hazelcast实现集群与分布式内存缓存
  8. elk,logstash -elastcisearch 429
  9. 如何降低数据中心宕机事件的影响
  10. 基于clang插件的一种iOS包大小瘦身方案