stm32cubemx读写SDRAM-W9825G6KH

转自:https://blog.csdn.net/mculover666/article/details/108221735

使用f429:参考基于STM32F429的SDRAM使用-单片机-电子工程世界网
http://news.eeworld.com.cn/mcu/article_2018060439560.html
不知道为啥,这个地方一定要2分频,不然无法成功:
在这里插入图片描述

本篇详细的记录了如何使用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 <stdio.h>
#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 Mculover666\r\n");

SDRAM_Init();

printf("SDRAM W9825G6KH Init success\r\n");

if (bsp_TestExtSDRAM() == 0) {
    printf("SDRAM Test success\r\n");
} else {
    printf("SDRAM Test fail\r\n");
}
/* 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 %#x\r\n", testValue);

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

  • 10
    点赞
  • 100
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
en.stm32cubemx-win-v6-9-1是一个针对STMicroelectronics的STM32微控制器系列的软件工具。这个软件工具可在Windows操作系统上使用,其版本号为6.9.1。 STM32微控制器系列是STMicroelectronics推出的一种低功耗、高性能的微控制器芯片系列。这些微控制器芯片广泛用于嵌入式系统、物联网设备、工业自动化以及各种电子产品中。STM32微控制器系列有多个不同的型号和系列,每个型号都有不同的功能和性能特点。 en.stm32cubemx-win-v6-9-1是用于STM32微控制器系列的软件工具。它具有通过图形化界面进行配置和生成代码的功能,使得开发人员可以更加方便地进行STM32微控制器的软件开发。通过这个软件工具,开发人员可以选择不同的模块、外设和功能,然后生成相应的初始化代码。这个软件工具还可以提供一些自动生成的模版,使得开发人员可以快速开始开发。 除了代码生成外,en.stm32cubemx-win-v6-9-1还提供了一些其他功能。例如,它可以通过图形化界面配置时钟树、引脚映射、中断优先级等。此外,该软件工具还可以集成其他开发环境,例如Keil或IAR,以便更直接地进行代码编辑和编译。 总而言之,en.stm32cubemx-win-v6-9-1是一个用于STM32微控制器系列的软件工具,它提供了图形化界面配置和生成代码的功能,帮助开发人员更方便地进行STM32微控制器的软件开发。它是一个很有用的工具,可以提高开发效率。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值