摘要:上一篇文章我们具体讲解了fsmc的原理配置,这一章主要是关于使用fsmc的sram初始化流程,以及使用stm32cubemx对fsmc进行配置。
一、sram初始化流程
通过上面的讲解,通过对 fsmc 相关的寄存器的描述,大家对 fsmc 的原理有了一个初步的认识,如果还不熟悉的朋友,请一定要搜索网络资料理解fsmc的原理。只有理解了原理,使用库函数才可以得心应手。那么在库函数中是怎么实现fsmc的配置的呢?fsmc_bcrx,fsmc_btrx 寄存器在库函数是通过什么函数来配置的呢?下面我们来讲解一下 fsmc 相关的库函数:
1.1 使能fsmc时钟
要使用fsmc,当然首先得开启其时钟。然后需要把fsmc_d0—15,fsmca0—18等相关io 口,全部配置为复用输出,并使能各 io 组的时钟。
rcc_ahb3periphclockcmd(rcc_ahb3periph_fsmc,enable);//使能 fsmc 时钟
配置 io 口为复用输出的关键行代码为:
gpio_initstructure.gpio_mode = gpio_mode_af;//复用输出
关于引脚复用映射配置,这在 lcd 实验章节也讲解非常详细,调用函数为:
void gpio_pinafconfig(gpio_typedef* gpiox, uint16_t gpio_pinsource, uint8_t gpio_af);
针对每个复用引脚调用这个函数即可,例如 gpiod.0引脚复用映射配置方法为:
gpio_pinafconfig(gpiod,gpio_pinsource0,gpio_af_fsmc);//pd0,af12
1.2 fsmc初始化函数
根据前面的讲解,初始化 fsmc 主要是初始化三个寄存器fsmc_bcrx,fsmc_btrx,fsmc_bwtrx,那么在固件库中是怎么初始化这三个参数的呢?
固件库提供了 3 个fsmc 初始化函数分别为:
fsmc_norsraminit();fsmc_nandinit();fsmc_pccardinit();
这三个函数分别用来初始化 4 种类型存储器。这里根据名字就很好判断对应关系。用来初始化nor和 sram 使用同一个函数 fsmc_norsraminit()。所以我们之后使用的 fsmc 初始化函数为fsmc_norsraminit()。下面我们看看函数定义:
void fsmc_norsraminit(fsmc_norsraminittypedef* fsmc_norsraminitstruct);
这个函数只有一个入口参数,也就是fsmc_norsraminittypedef类型指针变量,这个结构体的成员变量非常多,因为fsmc相关的配置项很多,但是对于sram我们只需要配置对应的成员就可以了,并不是所有结构体成员都需要配置。
1.3 sram 初始化结构体
typedef struct{ uint32_t fsmc_bank;/*设置要控制的 bank 区域 */ uint32_t fsmc_dataaddressmux;/*设置地址总线与数据总线是否复用 */ uint32_t fsmc_memorytype;/*设置存储器的类型 */ uint32_t fsmc_memorydatawidth;/*设置存储器的数据宽度*/ uint32_t fsmc_burstaccessmode;/*设置是否支持突发访问模式,只支持同步类型的存储器 */ uint32_t fsmc_asynchronouswait;/*设置是否使能在同步传输时的等待信号,*/ uint32_t fsmc_waitsignalpolarity;/*设置等待信号的极性*/ uint32_t fsmc_wrapmode;/*设置是否支持对齐的突发模式 */ uint32_t fsmc_waitsignalactive;/*配置等待信号在等待前有效还是等待期间有效 */ uint32_t fsmc_writeoperation;/*设置是否写使能 */ uint32_t fsmc_waitsignal;/*设置是否使能等待状态插入 */ uint32_t fsmc_extendedmode;/*设置是否使能扩展模式 */ uint32_t fsmc_writeburst;/*设置是否使能写突发操作*/ /*当不使用扩展模式时,本参数用于配置读写时序,否则用于配置读时序*/ fsmc_norsramtiminginittypedef* fsmc_readwritetimingstruct; /*当使用扩展模式时,本参数用于配置写时序*/ fsmc_norsramtiminginittypedef* fsmc_writetimingstruct;}fsmc_norsraminittypedef;
从这个初始化结构体我们可以看出,前面有13个基本类型(unit32_t)的成员变量,这 13 个参数是用来配置片选控制寄存器fsmc_bcrx。最后面还有两个fsmc_norsramtiminginittypedef 指针类型的成员变量。前面我们讲到,fsmc有读时序和写时序之分,所以这里就是用来设置读时序和写时序的参数了, 也就是说,这两个参数是用来配置寄存器smc_btrx和fsmc_bwtrx,后面我们会讲解到。下面我们主要来看看模式a下的相关配置参数:
fsmc_bank 用来设置使用到的存储块标号和区号,前面讲过,我们是使用的存储块 1 区号 3,所以选择值为fsmc_bank1_norsram3。
可以选择的存储器区域及区域对应的地址范围
fsmc_memorytype 用来设置存储器类型,我们这里是 sram,所以选择值为fsmc_memorytype_sram。
fsmc_memorydatawidth 用来设置数据宽度,可选 8 位还是 16 位,这里我们是 16 位数据宽度,所以选择值为 fsmc_memorydatawidth_16b。
fsmc_writeoperation 用来设置写使能,毫无疑问,我们前面讲解过我们要向sram写数据,所以要写使能,这里我们选择 fsmc_writeoperation_enable。
fsmc_extendedmode 是设置扩展模式使能位,也就是是否允许读写不同的时序,这里我们采取的读写相同的时序,所以设置值为 fsmc_extendedmode_disable。
上面的这些参数是与模式a相关的,下面我们也来稍微了解一下其他几个参数的意义吧:
fsmc_dataaddressmux 用来设置地址/数据复用使能,若设置为使能,那么地址的低 16位和数据将共用数据总线,仅对 nor 和 psram 有效,所以我们设置为默认值不复用,值fsmc_dataaddressmux_disable。
fsmc_burstaccessmode,fsmc_asynchronouswait,fsmc_waitsignalpolarity,fsmc_waitsignalactive,fsmc_wrapmode,fsmc_waitsignal,fsmc_writeburst和fsmc_waitsignal 这些参数在成组模式同步模式才需要设置,大家可以参考中文参考手册了解相关参数的意思。
读写时序参数的两个变量fsmc_readwritetimingstruct 和fsmc_writetimingstruct,它们都是 fsmc_norsramtiminginittypedef 结构体指针类型,这两个参数在初始化的时候分别用来初始化片选控制寄存器fsmc_btrx和写操作时序控制寄存器 fsmc_bwtrx。下面我们看看 fsmc_norsramtiminginittypedef 类型的定义:
1.4 sram时序结构体
控制 fsmc使用 sram 存储器时主要是配置时序寄存器以及控制寄存器,利用st 标准库的 sram 时序结构体以及初始化结构体可以很方便地写入参数。
typedef struct{ uint32_t fsmc_addresssetuptime;/*地址建立时间,0-0xf个hclk 周期*/ uint32_t fsmc_addressholdtime;/*地址保持时间,0-0xf个hclk 周期*/ uint32_t fsmc_datasetuptime;/*数据保持时间,0-0xf个hclk 周期*/ uint32_t fsmc_busturnaroundduration;/*总线转换周期,0-0xf个hclk 周期,在nor flash才用到,对于sram无效 */ uint32_t fsmc_clkdivision;/*时钟分频因子,1-0xf,若控制异步存储器,本参数无效 */ uint32_t fsmc_datalatency;/*数据延迟时间,若控制异步存储器,本参数无效 */ uint32_t fsmc_accessmode;/*设置访问模式 */}fsmc_norsramtiminginittypedef;
这个结构体成员定义的都是 sram 读写时序中的各项时间参数,这些成员的的参数都与 fsmc_brt 及 fsmc_bwtr 寄存器配置对应,各个成员介绍如下:
fsmc_addresssetuptime本成员设置地址建立时间,即fsmc读写时序图中的 addset 值,它可以被设置为 0-0xf个hclk周期数,按stm32标准库的默认配置,hclk的时钟频率为168mhz,即一个 hclk周期为 1/168微秒。
fsmc_addressholdtime本成员设置地址保持时间,它可以被设置为 0-0xf个hclk周期数。地址保持时间(addhld)模式a未用到,配置为0x00即可。
fsmc_datasetuptime本成员设置数据建立时间,即fsmc读写时序图中的 datast值,它可以被设置为0-0xf个hclk周期数。
fsmc_busturnaroundduration本成员设置总线转换周期,在nor flash存储器中,地址线与数据线可以分时复用,总线转换周期就是指总线在这两种状态间切换需要的延时,防止冲突。但是在控制其它存储器时(如sram)这个参数无效,配置为0即可。
fsmc_clkdivision本成员用于设置时钟分频,它以 hclk时钟作为输入,经过 fsmc_clkdivision分频后输出到 fsmc_clk引脚作为通讯使用的同步时钟。控制其它异步通讯的存储器时这个参数无效,我们的sram属于异步通讯的存储器,用不到这个参数,所以配置为 0即可。
fsmc_datalatency本成员设置数据保持时间,它表示在读取第一个数据之前要等待的周期数,该周期指同步时钟的周期,本参数仅用于同步 nor flash类型的存储器,控制其它类型的存储器时这个参数无效,我们的sram属于异步通讯的存储器,用不到这个参数,所以配置为0即可。
fsmc_accessmode本成员设置存储器访问模式,不同的模式下 fsmc访问存储器地址时引脚输出的时序不一样,可选 fsmc_accessmode_a/b/c/d 模式。一般来说控制 sram 时使用a模式,其实就是模式a,在控制sram是也没有见到过用其他模式的。
所以综上述所我们这7个成员变量,我们的sram使用到了其中的3个,其他的四个是nor flash需要配置的,其他的4个配置为0就可以了。具体的配置如下。
//地址建立时间(addset)为1个hclk,1/168m = 6nsreadwritetiming.fsmc_addresssetuptime = 0x00;//(0+1)个hclk周期=6ns >0ns 满足要求//数据保持时间(datast)+ 1个hclk = 9/168m=54ns(对em的sram芯片)readwritetiming.fsmc_datasetuptime = 0x08;//(8+1)个hclk周期=54ns >25ns 满足要求 //以上的设置满足6>0 54>25 54+6>55三个条件,所以设置是合理的。//以下配置跟异步sram无关,默认设置为0就可以了//地址保持时间(addhld)模式a未用到readwritetiming.fsmc_addressholdtime = 0x00; //设置总线转换周期,仅用于复用模式的nor操作(这里是sram,不用管)readwritetiming.fsmc_busturnaroundduration = 0x00;//设置时钟分频,仅用于同步类型的存储器(这里是异步型的sram,不用管)readwritetiming.fsmc_clkdivision = 0x00;//数据保持时间,仅用于同步型的nor(这里是异步型的sram,不用管)readwritetiming.fsmc_datalatency = 0x00;//选择匹配sram的模式readwritetiming.fsmc_accessmode = fsmc_accessmode_a;
上面我们设置了fsmc_addresssetuptime 地址建立时间为0,fsmc_datasetuptime 数据保持时间为8,为什么要这样设置呢?
这就需要看sram的时序图和fsmc模式a的时序图了,只有在时序图中才能找到答案,同时我们在操作别的存储器时也是需要通过时序图来配置具体的参数的。下面就来看看时序图吧。
is62wv51216数据手册上的sram 的读时序 is62wv51216翻译过来的sram 的读时序
is62wv51216数据手册上的sram 的写时序 is62wv51216翻译过来的sram 的写时序
stm32f4参考手册fsmc的读时序 stm32f4参考手册fsmc的写时序
is62wv51216读时序关键特性 is62wv51216写时序关键特性
使用模式a,读写共用时序(共用时序时,以写时序为准),配置代码如下:
//地址建立时间(addset)为1个hclk,1/168m = 6nsreadwritetiming.fsmc_addresssetuptime = 0x00;//(0+1)个hclk周期=6ns >0ns 满足要求//地址保持时间(addhld)模式a未用到readwritetiming.fsmc_addressholdtime = 0x00; //数据保持时间(datast)+ 1个hclk = 9/168m=54ns(对em的sram芯片)readwritetiming.fsmc_datasetuptime = 0x08;//(8+1)个hclk周期=54ns >25ns 满足要求
为什么地址建立时间是0ns呢?因为从is62wv51216写时序关键特性中可以看到,address setup time为0,所以配置时,地址建立时间要等于0。
地址保持时间模式a未用到,所以配置为0。
数据建立时间为什么是54ns呢?因为从is62wv51216写时序关键特性中可以看到,dataup to write end最小值为30ns,所以配置时,数据保持时间要大于30ns。至于选择的54ns并不是严格按照手册时间来的,这需要实际调试才能确定具体值。
那为什么数据建立时间配置为8,但却是9个hclk周期时间呢?从stm32中文参考手册的时序图中可以看到,模式a写入时序的数据建立时间为:数据建立时间=hclk周期+1,初始化程序时,如果设置datast=0,则实际数据建立周期为1,即实际数据建立周期比datast值多1个hclk周期。
经过上面的设置后写一次操作的时间就是(0+1)*6ns+(8+1)*6ns=60ns>55ns,是满足要求的。
参数确定大体就是这样的思路,其他模式及不同的芯片确定参数的思路是一样的。
最后是在帖子中看到的关于时序的讲解,挺有趣,一并放在文章中。
“
先搞清逻辑关系。这是cpu读存储器的时序。cpu索取,存储器付出。我有很多单元,你要读我,至少要把那个单元的门牌号告诉我吧?——这就是地址线,由cpu填写(输出);地址线上非1即0,你放不放地址都这样的,所以,你要告诉我什么时候是“地址”,什么时候是“垃圾”吧?——这就是oe线下降沿的作用,也要cpu填写。(一般来说,要在地址稳定以后,oe才发出下降沿。上面图没有给出这个差别。)好了,cpu该办的手续已经完了,球到了存储器一边。我要去这个单元找东西,总需要点时间吧?——这就是oe下降沿以后,ram发出数据的时间间隔。艾玛,累死我了,你要的东西找到了,我放上去了!按说,数据放到总线上了,可是cpu如何才能知道数据来了呢?因为不论是数据还是垃圾,那电线上面总有1、0这些东西。这时出现不同解决方法了。一种方法,由存储器给cpu发出一个通知信号:好了,快来拿!这个信号通常叫做 data available,数据可得,简写 dav,可能有人见过。cpu接到这个信号,自然会做。另一种方法,有老大强势规定:oe下降沿之后,你丫必须在1秒钟之内把东西交出来!我只管时间到就来拿!这是多数存储器遵守的规定。这两种方法,由于是不同的公司提出的,经常以公司名字来命名。motorola总线,intel总线,听说过吧?ok,明白这些意思,稍微想一想,应当就可以理解各种时序的时间了。以后再不用问人了。另外,手册里经常可以看到,这些时间一般会有最大值、最小值,有些只给出一项。比如上面提到,从oe下降沿到存储器交出数据的时间,在存储器手册里面,这个时间只会给出最大值。因为用户必须按“最长时间”操作,不能有侥幸心理。给出最小值没有实际意义。
”
1.5 fsmc使能
fsmc 对不同的存储器类型同样提供了不同的使能函数:
void fsmc_norsramcmd(uint32_t fsmc_bank, functionalstate newstate);void fsmc_nandcmd(uint32_t fsmc_bank, functionalstate newstate);void fsmc_pccardcmd(functionalstate newstate);
这个就比较好理解,我们这里不讲解,我们是sram,所以使用的是第一个函数。
fsmc_norsramcmd(fsmc_bank1_norsram3, enable); // 使能 bank3
通过以上几个步骤,我们就完成了fsmc的配置,可以访问is62wv51216有了,这里还需要注意,因为我们使用的是bank1的区域3,所以 haddr[27:26]=10,故外部内存的首地址为0x68000000。
二、硬件连接
下面来说一下在stm32f407中sram的硬件连接:对于fsmc来说,它已经集成到了单片机内部,它的提供给的管脚已经确定了,是不改动的,这个可以参考stm32对应芯片的 datasheet。唯具有灵活性的就是fsmc_ne,具体用哪个 fsmc_ne管脚来和你的sram相连,当然是你的自由,但是不要忘了,你要找到你选的 fsmc_ne所对应的地址范围,不然写程序的时候就搞不清喏!
我们需要将sram芯片和stm32单片机连接起来,就要来看看他们之间的连线和接口
sram功能框图 fsmc引脚图
sram芯片引脚图 sram的ad原理图
stm32fsmc外设 sram内存芯片 引脚说明
fsmc_ne3 片选信号,低电平有效
fsmc_oe 输出使能信号,低电平有效
nwe 写使能信号,低电平有效
fsmc_nbl0 低字节控制信号
fsmc_nbl1 高字节控制信号
fmsc_a[0:18] 地址信号线
smc_d[0:15] 数据信号线
备注:在mcu芯片手册中的信号引脚一般有前缀,前缀“ n ”表示相关的信号为低电平有效。在数字芯片手册中的信号引脚一般在引脚上面加一条横线表示低电平有效。
trc是读周期时间,它表示对芯片连续俩次读操作之间的最小间隔时间。
taa是读出时间,它表示地址线上的有效地址给出之后,经过译码电路,驱动电路的延时,到读出所选单元内容,并经i/o电路延时,直到数据在数据总线上稳定出现所需要的时间。
toha是输出保持时间,它表示地址失效之后,数据线上的有效数据维持的时间,以确保所读的数据真实可靠。
搜索并选中芯片stm32f407zgt6
配置时钟源
如果选择使用外部高速时钟(hse),则需要在system core中配置rcc;如果使用默认内部时钟(hsi),这一步可以略过;
这里我都使用外部时钟:
调试选项配置
默认没有配置下载引脚,烧录之后下载器将无法再检测到,这里我使用jlink,所以配置为sw选项:
配置串口
开发板板载了一个ch340换串口,连接到usart1,但是引脚不是默认引脚,需要手动修改。
接下来开始配置usart1:
配置fsmc外设
fsmc配置
我们板子上面的双口sram原理图如下:
通过原理图可以看出:
数据总线位宽使用了8bit:fsmc d0 - fsmc d7;
地址总线位宽使用了15bit:fsmc a0 - fsmc a14;
片选信号线:使用fsmc ne3,对应使用bank1的bank3子区域;
通用信号线:fsmc_noe、fsmc_nwe;
数据掩码信号线:使用 fmc nbl0 和 fmc nbl1,分别控制输出高8位还是低8位;
根据这些信息,在stm32cubemx中先配置sram1的基本设置:
sram基本参数配置
这部分信息直接配置即可:
配置情况如下:
配置时钟树
stm32f407zgt6的最高主频到168m,使hclk=168mhz即可:
生成工程设置
生成代码
点击generate code即可生成mdk-v5工程:
冬日呼吸新旅:连接器赋能制氧机的医疗之道
区块链的应用还存在着巨大鸿沟
脉冲驱动变压器是什么 脉冲驱动变压器电路图
CPU调频调压的设计与实现
贸泽赞助2021创造未来全球设计大赛 Intel、Analog Devices作为联合赞助商共同激励技术创新
微控制器的FSMC到底是咋回事?(下)
三安光电将在中国中部建设一个Mini/MicroLED研发基地 投资120亿元人民币
美国开发新型的带有新图层的锂金属电池,寿命提高三倍
美能TLM接触电阻测试仪,看它如何作用于电池生产!
同步复位电路和异步复位电路区别分析
知行科技把自家的L3级自动驾驶量产方案推向市场
泛林硅部件推动产业发展
华为1+1融合解决方案将助力运营商构建智能极简5G网络
风华2号国产显卡与国产系统统信UOS完成认证:开始量产
昆山同茂电子音圈马达无人机实现应急控制
以LabVIEW为平台和HKG-07B红外脉搏传感器实现无线心率测量系统的设计
浅谈面向5G通信的射频功放
魔珐科技:3D虚拟人AIGC原生产品,助力全新商业机遇
研究人员开发一种神经网络,能够读取食谱并生成烹饪完成后的熟食产品的图像
请给小米5C一点宽容,松果还只是个孩子!雷军的中国梦之路还很长