STM32CubeMX | 31-使用硬件FMC读写SDRAM(W9825G6KH)

发布者:qin199099最新更新时间:2021-08-13 来源: eefocus关键字:STM32CubeMX 手机看文章 扫描二维码
随时随地手机看文章

本篇详细的记录了如何使用STM32CubeMX配置 STM32F767IGT6 的硬件FMC外设与 SDRAM 通信(W9825G6KH)。


1. 准备工作

硬件准备

  • 开发板
    首先需要准备一个开发板,这里我准备的是STM32F767IGT6的核心板。

  • SDRAM
    核心板板载一片SDRAM,型号为 W9825G6KH,大小为 32 MB。

软件准备

需要准备一份 W9825G6KH-6 的数据手册。


2. STM32 FMC外设概述

2.1. 什么是FMC

FMC全称Flexible Memory Controller,灵活的内存控制器,顾名思义,其主要作用是:负责向外部扩展的存储类设备提供控制信号。

FMC内存控制器支持的存储设备有:

  • Nor Flash、SRAM、PSRAM

  • Nand Flash

  • SDRAM

  • 网卡DM9000(类存储设备)

此外,FMC外设还可以通过配置与LCD控制器连接,它提供Intel 8080并口模式和Motorola 6080并口模式,并且可以灵活的配置为指定的LCD接口类型。


2.2. FMC外设的功能框图

2.3. 外部设备的地址映射(重点)

从FMC的角度来看,外部的存储设备被分为几个固定大小的Bank,每个bank 256 MB。

整个FMC外设映射地址的划分如图:

2.3.1. Bank1

Bank1的地址空间为:0x6000 0000 - 0x6FFF FFFF,支持外接Nor Flash、PSRAM、SRAM等设备,还可以外接DM9000等类存储设备。


整个Bank1的地址空间被划分为四个子bank,每个子bank的大小为64MB,刚好对应FMC外设的地址总线(FMC_A[0:25])有26条(2^26=64MB)。


FMC还有两条内部总线ADDR[27:26],用这两路控制片选信号,如下表:

2.3.2. Bank3

只能外接Nand Flash设备。


2.3.3. SDRAM Bank

只能外接SDRAM设备。


3. 使用STM32CubeMX生成工程

选择芯片型号

打开STM32CubeMX,打开MCU选择器:

搜索并选中芯片STM32F767IGT6:

配置时钟源

  • 如果选择使用外部高速时钟(HSE),则需要在System Core中配置RCC;

  • 如果使用默认内部时钟(HSI),这一步可以略过;

这里我都使用外部时钟:

配置串口

开发板板载了一个CH340换串口,连接到USART1,但是引脚不是默认引脚,需要手动修改。


接下来开始配置USART1并修改引脚:

配置FMC外设

在配置FMC外设的时候,需要了解所用SDRAM的一些参数,如果你还对SDRAM的内部结构不熟悉,请阅读这篇博客:SDRAM学习笔记(eg. W9825G6KH)


FMC配置

开发板上SDRAM(W9825G6KH)的原理图如下:

通过原理图可以看出:

  • 数据总线位宽使用了16bit:FMC D0 - FMC D15;

  • 地址总线位宽使用了13bit:FMC A0 - FMC A12;

  • BANK选择信号线有两条:FMC BA0 和 FMC BA1;

  • 时钟使能信号使用FMC SDCKE0,片选信号使能使用FMC SDME0,可以看出使用SDRAM区域1;

  • 其它通用信号线:FMC SDNWE、FMC SDNCAS、FMC SDNRAS、FMC SDCLK;

  • 数据掩码信号线使用 FMC NBL0 和 FMC NBL1,分别控制输出高8位还是低8位;

根据这些信息,在STM32CubeMX中先配置SDRAM1的基本设置:

SDRAM基本参数配置

这部分信息从 SDRAM(W9825G6KH) 的数据手册中即可看到。

① 速度等级:当CL=3时最高速度为166Mhz。因为STM32F767的HCLK=216Mhz,所以需要进行二分频,使SDRAM的时钟频率为108Mhz。

② 行地址宽度和列地址宽度:有A0-A12 总共13条行地址线,有A0-A8总共9条列地址线。

最后配置如下:

SDRAM时序参数配置

通过之前的设置,SDRAM的时钟频率为108Mhz,一个时钟周期就是9.26 ns,所以下面参数的单位都是9.26ns。

① LoadToActiveDelay:TMRD 定义加载模式寄存器的命令与激活命令或刷新命令之间的延迟,最小值为2个clk。

② ExitSelfRefreshDelay:从退出自刷新到行有效的时间延迟,最小72ns,所以参数设置为8。

③ SelfRefreshTime:自刷新周期,最小是42ns,所以设置为6。

④ RowCycleDelay(tRC):刷新命令和激活命令之间的延迟,最小值为60,所以设置为7。

⑤ WriteRecoveryTime:写命令和预充电命令之间的延迟,在CL=3的情况下,最小是2个clk。

⑥ RPDelay(tRP):预充电命令与其它命令之间的延迟,最小15ns,所以此项设置为2。

⑦ RCDDelay(tRCD):激活命令与读/写命令之间的延迟,最小15ns,所以设置为2。

配置情况如下:

配置时钟树

STM32G070RB的最高主频到216M,使HCLK = 216Mhz即可:

生成工程设置

代码生成设置

最后设置生成独立的初始化文件:

生成代码

点击GENERATE CODE即可生成MDK-V5工程:

4. 测试SDRAM读写

4.1. 编写SDRAM初始化代码

新建SDRAM驱动文件sdram_fmc_drv.h:


/**

 *@file    sdram_fmc_drv.h

 *@brief   使用 FMC 操作 SDRAM

 *@author  mculover666

 *@date    2020-08-27

 *@note    此驱动测试 W9825G6KH SDRAM芯片通过

*/


#ifndef _SDRAM_FMC_DRV_H_

#define _SDRAM_FMC_DRV_H_


#include "fmc.h"


#define SDRAM_MODEREG_BURST_LENGTH_1             ((uint16_t)0x0000)

#define SDRAM_MODEREG_BURST_LENGTH_2             ((uint16_t)0x0001)

#define SDRAM_MODEREG_BURST_LENGTH_4             ((uint16_t)0x0002)

#define SDRAM_MODEREG_BURST_LENGTH_8             ((uint16_t)0x0004)

#define SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL      ((uint16_t)0x0000)

#define SDRAM_MODEREG_BURST_TYPE_INTERLEAVED     ((uint16_t)0x0008)

#define SDRAM_MODEREG_CAS_LATENCY_2              ((uint16_t)0x0020)

#define SDRAM_MODEREG_CAS_LATENCY_3              ((uint16_t)0x0030)

#define SDRAM_MODEREG_OPERATING_MODE_STANDARD    ((uint16_t)0x0000)

#define SDRAM_MODEREG_WRITEBURST_MODE_PROGRAMMED ((uint16_t)0x0000)

#define SDRAM_MODEREG_WRITEBURST_MODE_SINGLE     ((uint16_t)0x0200)


void SDRAM_Init(void);


#endif /* _SDRAM_FMC_DRV_H_ */


然后在c文件中封装一个向SDRAM发送命令的函数:


static int SDRAM_SendCommand(uint32_t CommandMode, uint32_t Bank, uint32_t RefreshNum, uint32_t RegVal)

{

    uint32_t CommandTarget;

    FMC_SDRAM_CommandTypeDef Command;

    

    if (Bank == 1) {

        CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;

    } else if (Bank == 2) {

        CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2;

    }

    

    Command.CommandMode = CommandMode;

    Command.CommandTarget = CommandTarget;

    Command.AutoRefreshNumber = RefreshNum;

    Command.ModeRegisterDefinition = RegVal;

    

    if (HAL_SDRAM_SendCommand(&hsdram1, &Command, 0x1000) != HAL_OK) {

        return -1;

    }

    

    return 0;

}


最后实现SDRAM初始化的函数:


void SDRAM_Init(void)

{

    uint32_t temp;

    

    /* 1. 时钟使能命令 */

    SDRAM_SendCommand(FMC_SDRAM_CMD_CLK_ENABLE, 1, 1, 0);

    

    /* 2. 延时,至少100us */

    HAL_Delay(1);

    

    /* 3. SDRAM全部预充电命令 */

    SDRAM_SendCommand(FMC_SDRAM_CMD_PALL, 1, 1, 0);

    

    /* 4. 自动刷新命令 */

    SDRAM_SendCommand(FMC_SDRAM_CMD_AUTOREFRESH_MODE, 1, 8, 0);

    

    /* 5. 配置SDRAM模式寄存器 */   

    temp = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_1            |          //设置突发长度:1

                     SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL     |          //设置突发类型:连续

                     SDRAM_MODEREG_CAS_LATENCY_3             |          //设置CL值:3

                     SDRAM_MODEREG_OPERATING_MODE_STANDARD   |          //设置操作模式:标准

                     SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;              //设置突发写模式:单点访问  

    SDRAM_SendCommand(FMC_SDRAM_CMD_LOAD_MODE, 1, 1, temp);

    

    /* 6. 设置自刷新频率 */

    /*

        SDRAM refresh period / Number of rows)*SDRAM时钟速度 – 20

      = 64000(64 ms) / 4096 *108MHz - 20

      = 1667.5 取值1668

    */

    HAL_SDRAM_ProgramRefreshRate(&hsdram1, 1668);

}


4.2. 编写SDRAM读写测试代码

接下来在main.c中添加SDRAM测试代码。


此测试代码来自安富莱电子。


① 引入SDRAM驱动头文件:


/* Private includes ----------------------------------------------------------*/

/* USER CODE BEGIN Includes */

#include

#include "sdram_fmc_drv.h"

/* USER CODE END Includes */


② 宏定义SDRAM的映射地址以及SDRAM的大小:


/* Private user code ---------------------------------------------------------*/

/* USER CODE BEGIN 0 */

#define EXT_SDRAM_ADDR  ((uint32_t)0xC0000000)

#define EXT_SDRAM_SIZE (32 * 1024 * 1024)


uint32_t bsp_TestExtSDRAM(void);

/* USER CODE END 0 */


③ 编写测试函数:


/* USER CODE BEGIN 4 */

/*

*********************************************************************************************************

* 函 数 名: bsp_TestExtSDRAM

* 功能说明: 扫描测试外部SDRAM的全部单元。

* 形    参: 无

* 返 回 值: 0 表示测试通过; 大于0表示错误单元的个数。

*********************************************************************************************************

*/

uint32_t bsp_TestExtSDRAM(void)

{

uint32_t i;

uint32_t *pSRAM;

uint8_t *pBytes;

uint32_t err;

const uint8_t ByteBuf[4] = {0x55, 0xA5, 0x5A, 0xAA};


/* 写SDRAM */

pSRAM = (uint32_t *)EXT_SDRAM_ADDR;

for (i = 0; i < EXT_SDRAM_SIZE / 4; i++)

{

*pSRAM++ = i;

}


/* 读SDRAM */

err = 0;

pSRAM = (uint32_t *)EXT_SDRAM_ADDR;

for (i = 0; i < EXT_SDRAM_SIZE / 4; i++)

{

if (*pSRAM++ != i)

{

err++;

}

}


if (err >  0)

{

return  (4 * err);

}


/* 对SDRAM 的数据求反并写入 */

pSRAM = (uint32_t *)EXT_SDRAM_ADDR;

for (i = 0; i < EXT_SDRAM_SIZE / 4; i++)

{

*pSRAM = ~*pSRAM;

pSRAM++;

}


/* 再次比较SDRAM的数据 */

err = 0;

pSRAM = (uint32_t *)EXT_SDRAM_ADDR;

for (i = 0; i < EXT_SDRAM_SIZE / 4; i++)

{

if (*pSRAM++ != (~i))

{

err++;

}

}


if (err >  0)

{

return (4 * err);

}


/* 测试按字节方式访问, 目的是验证 FSMC_NBL0 、 FSMC_NBL1 口线 */

pBytes = (uint8_t *)EXT_SDRAM_ADDR;

for (i = 0; i < sizeof(ByteBuf); i++)

{

*pBytes++ = ByteBuf[i];

}


/* 比较SDRAM的数据 */

err = 0;

pBytes = (uint8_t *)EXT_SDRAM_ADDR;

for (i = 0; i < sizeof(ByteBuf); i++)

{

if (*pBytes++ != ByteBuf[i])

{

err++;

}

}

if (err >  0)

{

return err;

}

return 0;

}

/* USER CODE END 4 */


④ 在main函数中调用:


/* USER CODE BEGIN 2 */

printf("STM32F767 SDRAM Test By Mculover666rn");


SDRAM_Init();


printf("SDRAM W9825G6KH Init successrn");


if (bsp_TestExtSDRAM() == 0) {

    printf("SDRAM Test successrn");

} else {

    printf("SDRAM Test failrn");

}

/* USER CODE END 2 */


4.3. 实验结果

编译,下载到开发板中,在串口助手中查看实验结果:

5. 直接指定变量存储到 SDRAM 空间

第4节中的测试方法是使用指针访问SDRAM空间,未免过于麻烦。在实际使用中,可以直接定义一个非常大的数组,将整个数组都存储到SDRAM上,然后动态的使用SDRAM内存空间。


要注意使用这种方法定义变量时,必须在函数外把它定义成全局变量,才可以存储到指定地址上。


测试过程如下:

① 定义全局变量并指定绝对地址:


/* 绝对定位方式访问 SDRAM,这种方式必须定义成全局变量 */

uint8_t testValue __attribute__((at(EXT_SDRAM_ADDR)));


② 在main函数中赋值,然后打印:


/* 操作在SDRAM的变量 */

testValue = 0x5a;

printf("testValue is %#xrn", testValue);


编译,下载,查看实验结果:

关键字:STM32CubeMX 引用地址:STM32CubeMX | 31-使用硬件FMC读写SDRAM(W9825G6KH)

上一篇:STM32 (零)--------STM32介绍
下一篇:mac下搭建stm32开发环境

推荐阅读最新更新时间:2024-11-08 12:07

STM32CubeMX学习教程之一:GPIO输出之跑马灯
完整源码下载: https://github.com/simonliu009/STM32CubeMX-GPIO-Control 软件版本: STM32CubeMX V4.25.0 System Workbench V2.4 固件库版本: STM32Cube FW_F1 V1.6.1 硬件:OneNet 麒麟座V2.3 在STM32CubeMX中新建项目,选择正确的MCU型号 首先设置RCC和SYS,如下图 然后根据板子实际情况设置时钟(麒麟座外部晶振是12M,STM32F103x的最高主频是72M),如下图 GPIO设置 PC7, PC8, PA12和 PC10为GPIO_OUTPUT, (这是麒
[单片机]
<font color='red'>STM32CubeMX</font>学习教程之一:GPIO输出之跑马灯
STM32CubeMX基本工程创建记录
1.创建工程 2.选择芯片 3.鼠标中键可以放大芯片,鼠标左键单击想要配置的引脚 4.然后可以配置初始电平,以及是否上拉和下拉 5.根据自己的需要进行配置,初始化引脚 6.进行项目配置 7.生成代码文件 8.在主函数添加自己的功能代码,此处是实现0.5SLED灯进行闪烁
[单片机]
<font color='red'>STM32CubeMX</font>基本工程创建记录
HAL库 STM32CubeMX教程五----看门狗(独立看门狗,窗口看门狗)
前言: 今天我们来学习看门狗的配置与函数,看门狗可以有效解决程序的跑飞,在使用过程中比较常见,是防止芯片故障的有效外设,我们一起来学习下HAL库 STM32CubeMX的独立看门狗,窗口看门狗的使用。本系列教程将HAL库与STM32CubeMX结合在一起讲解,使您可以更快速的学会各个模块的使用 所用工具: 1、芯片: STM32F407ZET6 2、STM32CubeMx软件 3、IDE: MDK-Keil软件 4、STM32F1xx/STM32F4xxHAL库 知识概括: 通过本篇博客您将学到: STM32CubeMX创建看门狗例程 独立看门狗,靠窗看门狗 工作原理 看门狗 在由单片机构成的微型计算机系统中单片
[单片机]
HAL库 <font color='red'>STM32CubeMX</font>教程五----看门狗(独立看门狗,窗口看门狗)
基于stm32CubeMX和keil5的stm32f103学习编程
0. 准备 先用st-link连接stm32核心板与PC,用于烧录 St-link Stm32 3.3V 3.3V GND GND SWDIO DIO SWCLK DCLK 再用USB串口板连接,用于查看串口输出 USB Stm32 RX TX(A9) TX RX(A10) 安装完成驱动并连接好以后,进入设备管理器可以看到它们都已被识别。 打开putty.exe,选择串口连接,用于查看之后的串口输出。 1. 编写Cube程序,配置UART0为9600,8n1,上电后向串口输出“Hello”,在PC上通过串口软件观察结果; 安装完成并打开CubeMX软件,选择New P
[单片机]
基于<font color='red'>stm32CubeMX</font>和keil5的stm32f103学习编程
STM32CubeMX(Keil5)开发之路——6外部中断
运行环境 Windows10 STM32CubeMX___Version 5.0.0 Keil5(MDK5)___Version 5.15 简介 本例程主要讲解如何设置外部中断,采用中断的方式按键是否按下,在中断中进行打印数据。 STM32CubeMx基本配置 基础配置过程请参考 STM32CubeMx(Keil5)开发之路—配置第一个项目 STM32CubeMx 外部中断配置 查看电路图,如下图所示 K1——K3为外部输入引脚,连接PE2——PE4,并且按下按键时为低电平,因此待会儿需要设置为下降沿触发 为了方便调试,重定向printf,进行usart设置 1——点击USART1进行设置 2——模式选择Asyn
[单片机]
<font color='red'>STM32CubeMX</font>(Keil5)开发之路——6外部中断
如何利用STM32CubeMX将TouchGFX移植到STM32F429IGT6并驱动RGB屏
TouchGFX的应用框架如下图所示: 1.STM32CubeMX配置 STM32CubeMX主要用来配置上面所示的应用框架图中的硬件抽象层和中间件层 RCC系统时钟:高速外部时钟(HSE)配置为外部晶振 由于要使用 FreeRTOS 操作系统 ,因此建议将HAL库的 Ti mebase Source从SysT ic k改为其他 定时器 ,选好定时器后,系统会自动配置TIM,此处设置为TIM7 FMC设置:配置外部 SD RAM 来作为RGBLCD的显存,根据自已的硬件进行参数以及引脚配置 DMA2D设置:激活DMA2D,配置颜色模式为RGB565,并开启DMA2D中断 LTDC参数设置:根据使用的屏幕参数
[单片机]
如何利用<font color='red'>STM32CubeMX</font>将TouchGFX移植到STM32F429IGT6并驱动RGB屏
STM32CubeMx新建工程(串口)
新建一个具有串口功能的 这个是在线更新,我们取消不用更新。 选择对应的板子型号 点击进行配置 ①②选择串口 ③使能串口 ④可以根据自己需求修改波特率及一些参数(默认也可以) 出现绿色表示配置OK ①选择工程 ②工程名称 ③工程路径 ④ 选择自己的开发环境 打钩 生成.c 和.h 点击右上角生成
[单片机]
<font color='red'>STM32CubeMx</font>新建工程(串口)
多路读写SDRAM接口设计
存储器是容量数据处理电路的重要组成部分。随着数据处理技术的进一步发展,对于存储器的容量和性能提出了越来越高的要求。同步动态随机存储器SDRAM(Synchronous Dynamic Random Access Memory)因其容量大、读写速度快、支持突发式读写及相对低廉的价格而得到了广泛的应用。SDRAM的控制比较复杂,其接口电路设计是关键。 本文首先介绍SDRAM的主要控制信号和基本命令;然后介绍接口电路对SDRAM的主要操作路径及操作过程,应用于解复用的SDRAM接口电路的设计方法;最后给出了实现结果。 1 SDRAM的主要控制信号和基本命令 SDRAM的主要控制信号为: ·CS:片选使能信号,低电平有效; ·R
[缓冲存储]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved