打印
[应用相关]

STM32硬件基础--FMC读写片外SDRAM

[复制链接]
楼主: programmable
手机看帖
扫描二维码
随时随地手机跟帖
21
programmable|  楼主 | 2020-6-26 09:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览
注意,FMC的信号线 FMC_SDCK0 分配给了 PH2 这个管脚。再来看之前提到的那个图五中的“关子”: FMC_SDCK0 分配给了 PC3!查F746的芯片手册知道,PC3、PH2都可以通过复用功能映射成 FMC_SDCK0 信号。

使用特权

评论回复
22
programmable|  楼主 | 2020-6-26 09:24 | 只看该作者
开发板上到底是用的哪个pin?原理图上找答案:


使用特权

评论回复
23
programmable|  楼主 | 2020-6-26 09:25 | 只看该作者
可见,图五中的管脚配置是“正确”的。但图二十一的配置也可以理解(不是CUBEMX的bug):因为是按选择芯片型号来让CUBEMX自动配置FMC的,CUBEMX并不知道用户实际的电路板上FMC是用哪个pin来对应 FMC_SDCK0 信号的,只能把 FMC_SDCK0 信号分配到某一个pin上(本例中是PH2)。如果与实际电路不符,需要由用户手动修改。

使用特权

评论回复
24
programmable|  楼主 | 2020-6-26 09:26 | 只看该作者
而在图二十一中,是按开发板来配置的,CUBEMX知道板子的具体型号,就可以按实际的管脚来正确分配 FMC_SDCK0 信号。其他所有具有复用功能的GPIO都有这个问题,希望大家重视。(这是通过血的教训换来的,第一次用CUBEMX配置FMC时,忽略了这个问题,导致编写代码调试SDRAM时,读出数据总是不稳定、或出错,并且难以调试、追踪,耗了一周时间才找到问题根源,教训深刻啊)

使用特权

评论回复
25
programmable|  楼主 | 2020-6-26 09:26 | 只看该作者
现在来修改图二十一中的管脚配置,同时介绍一个配置GPIO复用功能的小技巧。因为PH2不是我们想要的pin,需要查找其他复用功能为  FMC_SDCK0 的pin,可以先按下Ctrl 键,然后鼠标点击 PH2 管脚,CUBE界面上会有2个pin(PC3和PC5)改变了颜色:

使用特权

评论回复
26
programmable|  楼主 | 2020-6-26 09:27 | 只看该作者
修改PC3的功能为 FMC_SDCK0:

使用特权

评论回复
27
programmable|  楼主 | 2020-6-26 09:27 | 只看该作者
最后,别忘了把时钟配置到200MHz:

使用特权

评论回复
28
programmable|  楼主 | 2020-6-26 09:28 | 只看该作者
然后,生成KEIL工程。main.c中,

使用特权

评论回复
29
programmable|  楼主 | 2020-6-26 09:28 | 只看该作者
MX_FMC_Init() 函数实现对FMC的配置,它调用了stm32fxx_hal_sdram.c文件中的 HAL_SDRAM_Init() ,后者首先调用 HAL_SDRAM_MspInit() 对FMC用到的pin初始化,然后分别调用 FMC_SDRAM_Init() 和 FMC_SDRAM_Timing_Init() 对FMC的控制寄存器 FMC_SDCR 和时序寄存器 FMC_SDTR 进行初始化。

使用特权

评论回复
30
programmable|  楼主 | 2020-6-26 09:29 | 只看该作者
至此,FMC的配置全部结束,程序可以编译、通过,但还不能正确读写SDRAM,因为SDRAM在正式工作之前,还需要一个“SDRAM初始化”的过程。显然,这个初始化过程对于不同型号的SDRAM可能是不同的,CUBEMX就无能为力了。

使用特权

评论回复
31
programmable|  楼主 | 2020-6-26 09:30 | 只看该作者
但仍有捷径可走 ---- 找ST官方的例子:

使用特权

评论回复
32
programmable|  楼主 | 2020-6-26 09:31 | 只看该作者
先找到STM32CUBE的F7库的安装目录,然后进入F746G-DISCO 的项目目录,在例子中有关于FMC的例程。从 FMC_SDRAM 项目中的 main.c 文件中,copy 对SDRAM做初始化的函数:BSP_SDRAM_Initialization_Sequence() 到我们的main.c 中,并在 MX_FMC_Init() 函数尾部添加调用:

使用特权

评论回复
33
programmable|  楼主 | 2020-6-26 09:32 | 只看该作者
请注意,例程 FMC_SDRAM  中的 CAS Latency 是2,对应地设置SDRAM模式寄存器时也是2。而我们在CUBEMX中将CAS Latency 配置为3,则设置SDRAM模式寄存器时也必须是3:

使用特权

评论回复
34
programmable|  楼主 | 2020-6-26 09:33 | 只看该作者
刷新周期的计算:先看看 MT48LC4M32B2 手册对刷新周期的时间要求:

使用特权

评论回复
35
programmable|  楼主 | 2020-6-26 09:34 | 只看该作者
要求最慢在64ms内对4096行(对应12-bit行地址空间)完成刷新,就是说64ms要刷4096次,所以每刷一次的时间间隔是 64ms÷4096=15.625us。换算成SDRAM的CLK(即SDCLK,100MHz):15.625us÷(1/100us)=1562。为了保证刷新周期在SDRAM的任何工作状态下都能小于64ms,要再减去20个SDCLK(这是SDRAM规范文档要求的),即1562-20=1542。例程中用的数字是1292,则刷新周期在54ms左右。一旦完成刷新参数设置(写到 SDRTR 寄存器中),FMC立即自动开始刷新,即每隔大约60ms,MCU通过FMC向SDRAM发送一条刷新指令。

使用特权

评论回复
36
programmable|  楼主 | 2020-6-26 09:35 | 只看该作者
至此,完成了FMC配置、SDRAM初始化、SDRAM刷新3大步骤,到了最后一步:编程访问SDRAM。关键的一个问题是:外部SDRAM地址从0xC000 0000 开始,如何定义一个变量(数组),使得它的地址恰恰从0xC000 0000 开始?一种方法是定义一个uint16_t类型的数据指针,并且使得这个指针指向0xC000 0000地址,如:

#define    SDRAM_BANK_ADDR       ((uint32_t)0xC0000000)

uint16_t    *pSDRAM;

//set data pointer, pointing external sdram

pSDRAM = (uint16_t *)SDRAM_BANK_ADDR;

使用特权

评论回复
37
programmable|  楼主 | 2020-6-26 09:35 | 只看该作者
然后通过 (*pSDRAM + i) 或 pSDRAM[ i ] 即可访问数据了。在演示代码中,先将一些16位的无符号数据写入SDRAM,再从SDRAM中读出到一个内存数组,然后做比较,如果数据全部匹配,则LED每隔一秒钟慢闪一次;如果数据有误,则LED快闪。通过实验可以看到,LED慢闪,表明读写SDRAM是正确的。

完整的代码请从此处下载:STM32的FMC例程,包含 KEIL 和 IAR 两个版本。

使用特权

评论回复
38
programmable|  楼主 | 2020-6-26 09:36 | 只看该作者
最后,补充图四下方时序参数的说明。SDRAM的时序参数,有不同的描述方式,但在SDRAM规范中,它们都是有统一的“代号”的,比如图四中的 Load mode register to active delay,说的是装载模式寄存器需要花费的时间,代号是tMRD。图四中其他的代号如下:

使用特权

评论回复
39
programmable|  楼主 | 2020-6-26 09:36 | 只看该作者
然后,按图索骥,在MT48LC4M32B2 手册中查找:


使用特权

评论回复
40
programmable|  楼主 | 2020-6-26 09:37 | 只看该作者
单位换算关系:我们配置了SDRAM的时钟频率(SDCLK)是100MHz,等同于一个CK是10ns。反之,10ns的时间就是一个CK。可见,图四中的时序参数基本上都是稍微留了余量的。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则