[其他ST产品] stm32cubeMX配置stm32h743 SDRAM

[复制链接]
5871|81
 楼主| 和下土 发表于 2024-5-1 00:43 | 显示全部楼层
SelfRefreshTime:自刷新周期,最小是42ns,所以设置为6。
4169066311fb29416c.png
 楼主| 和下土 发表于 2024-5-1 00:43 | 显示全部楼层
RowCycleDelay(tRC):刷新命令和激活命令之间的延迟,最小值为60,所以设置为7。
238466311fbf96a6f.png
 楼主| 和下土 发表于 2024-5-1 00:43 | 显示全部楼层
WriteRecoveryTime:写命令和预充电命令之间的延迟,在CL=3的情况下,最小是2个clk。
4792566311fcebd1fa.png
 楼主| 和下土 发表于 2024-5-1 00:44 | 显示全部楼层
RPDelay(tRP):预充电命令与其它命令之间的延迟,最小15ns,所以此项设置为2。
4255366311fdb49de8.png
 楼主| 和下土 发表于 2024-5-1 00:44 | 显示全部楼层
RCDDelay(tRCD):激活命令与读/写命令之间的延迟,最小15ns,所以设置为2。
2101966311fe90461f.png
 楼主| 和下土 发表于 2024-5-1 00:44 | 显示全部楼层
配置情况如下:
8580966311ff425a3f.png
 楼主| 和下土 发表于 2024-5-1 00:44 | 显示全部楼层
配置时钟树
STM32G070RB的最高主频到216M,使HCLK = 216Mhz即可:
220366311fff9dcac.png
 楼主| 和下土 发表于 2024-5-1 00:45 | 显示全部楼层
生成工程设置
267386631200abcf44.png
 楼主| 和下土 发表于 2024-5-1 00:45 | 显示全部楼层
代码生成设置
最后设置生成独立的初始化文件:
3950066312016e3ba4.png
 楼主| 和下土 发表于 2024-5-1 00:45 | 显示全部楼层
生成代码
点击GENERATE CODE即可生成MDK-V5工程:
6560966312021ec23f.png
 楼主| 和下土 发表于 2024-5-1 00:45 | 显示全部楼层
测试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_ */
 楼主| 和下土 发表于 2024-5-1 00:45 | 显示全部楼层
然后在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;
}
 楼主| 和下土 发表于 2024-5-1 00:45 | 显示全部楼层
最后实现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);
}
 楼主| 和下土 发表于 2024-5-1 00:46 | 显示全部楼层
编写SDRAM读写测试代码
接下来在main.c中添加SDRAM测试代码。

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

① 引入SDRAM驱动头文件:

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include "sdram_fmc_drv.h"
/* USER CODE END Includes */
 楼主| 和下土 发表于 2024-5-1 00:46 | 显示全部楼层
宏定义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 */
 楼主| 和下土 发表于 2024-5-1 00:46 | 显示全部楼层
编写测试函数:
/* 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 */
 楼主| 和下土 发表于 2024-5-1 00:46 | 显示全部楼层
在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 */
 楼主| 和下土 发表于 2024-5-1 00:46 | 显示全部楼层
实验结果
编译,下载到开发板中,在串口助手中查看实验结果:
760546631207f01a6e.png
 楼主| 和下土 发表于 2024-5-1 00:47 | 显示全部楼层
. 直接指定变量存储到 SDRAM 空间
第4节中的测试方法是使用指针访问SDRAM空间,未免过于麻烦。在实际使用中,可以直接定义一个非常大的数组,将整个数组都存储到SDRAM上,然后动态的使用SDRAM内存空间。

要注意使用这种方法定义变量时,必须在函数外把它定义成全局变量,才可以存储到指定地址上。
 楼主| 和下土 发表于 2024-5-1 00:47 | 显示全部楼层
测试过程如下:

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

/* 绝对定位方式访问 SDRAM,这种方式必须定义成全局变量 */
uint8_t testValue __attribute__((at(EXT_SDRAM_ADDR)));
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 在线客服 返回列表 返回顶部