20.1CubeMx配置FMC控制SDRAM【W9825G6KH-6】

本文使用stm32h723zgt6的fmc驱动sdram,实现内存扩展
sdram型号W9825G6KH-6

原理图:

20.1CubeMx配置FMC控制SDRAM【W9825G6KH-6】_第1张图片

MCU引脚与SDRAM对应关系
20.1CubeMx配置FMC控制SDRAM【W9825G6KH-6】_第2张图片

引脚说明:

SDRAM引脚:
DQ[15:0]:数据线;数据位宽16
A[12:0]:地址线;行地址A[0:12],列地址A[0:8];
20.1CubeMx配置FMC控制SDRAM【W9825G6KH-6】_第3张图片
BS[1:0]:Bank 地址输入,选择要控制的 Bank;本芯片4个Bank;4M words x 4 banks x 16 bits ;块地址其它简写名称BA,
FMC_BA0 对应 BS0;
FMC_BA1 对应 BS1
LDQM,UDQM:数据输入/输出掩码信号,表示 DQ 信号线的有效部分;
FMC_NBL0 对应 LDQM
FMC_NBL1 对应 UDQM
WE:写入使能,低电平有效;对应FMC_SDNWE
RAS:行地址选通,为低电平时地址线表示的是行地址;对应FMC_SDNRAS
CAS:列地址选通,为低电平时地址线表示的是列地址;对应FMC_SDNCAS
CS:片选信号,低电平有效;对应FMC_SDNE0或FMC_SDNE1
CKE:时钟使能信号,禁止时钟信号时 SDRAM 会启动自刷新操作;对应FMC_SDCKE0或FMC_SDCKE1
CLK:同步时钟信号,所有输入信号都在 CLK 为上升沿的时候被采集;对应FMC_SDCLK
注:SDRAM的线尽量等长走线 阻抗50ohm,此款芯片最高频率166MHz,不走等长会达不到这么高的频率,我画的板最大稳定运行100MHz;但是走等长确实有点困难,因为线束较多,分布比较散乱,不走等长时建议留出探测点
20.1CubeMx配置FMC控制SDRAM【W9825G6KH-6】_第4张图片
此图是我的SDRAM走线图
20.1CubeMx配置FMC控制SDRAM【W9825G6KH-6】_第5张图片
20.1CubeMx配置FMC控制SDRAM【W9825G6KH-6】_第6张图片
FMC引脚名参考:
20.1CubeMx配置FMC控制SDRAM【W9825G6KH-6】_第7张图片

CubeMx工程配置:

常规配置

配置工程时钟、串口、SWDIO
20.1CubeMx配置FMC控制SDRAM【W9825G6KH-6】_第8张图片
20.1CubeMx配置FMC控制SDRAM【W9825G6KH-6】_第9张图片
20.1CubeMx配置FMC控制SDRAM【W9825G6KH-6】_第10张图片
20.1CubeMx配置FMC控制SDRAM【W9825G6KH-6】_第11张图片20.1CubeMx配置FMC控制SDRAM【W9825G6KH-6】_第12张图片
20.1CubeMx配置FMC控制SDRAM【W9825G6KH-6】_第13张图片
20.1CubeMx配置FMC控制SDRAM【W9825G6KH-6】_第14张图片

FMC配置

SDRAM硬件相关配置

Clock and chip enable:SDRAM选择:SDRAM1|SDRAM2
FMC内部包含两个SDRAM存储区
20.1CubeMx配置FMC控制SDRAM【W9825G6KH-6】_第15张图片
在这里插入图片描述
Clock and chip enable选择SDRAM1
在这里插入图片描述
Internal bank number:内部Bank数;本芯片4个banks
在这里插入图片描述
Address:地址线13根【A0-A12】
Data:数据线16根【DQ0-DQ15】
16-bit byte enable:使能数据掩码功能;见LDQM,UDQM

SDRAM1控制参数:

Bank:使用的是FMC的SDRAM bank1
Number of colum address bit:列地址线9根
Number of row address bits:行地址线13根
20.1CubeMx配置FMC控制SDRAM【W9825G6KH-6】_第16张图片CAS latency:列地址选通延迟,简称CL。在发出读命令后需要等待几个时钟周期数据线DQ才是有效数据,仅在读命令时才有这个;
20.1CubeMx配置FMC控制SDRAM【W9825G6KH-6】_第17张图片
20.1CubeMx配置FMC控制SDRAM【W9825G6KH-6】_第18张图片

20.1CubeMx配置FMC控制SDRAM【W9825G6KH-6】_第19张图片
CAS latency此芯片配置为3
Write protection:设置是否使能写保护模式,如果使能了写保护则不能向 SDRAM 写入数据,正常使用都是禁止写保护的;
SDRAM common clock:设置 FMC 与外部 SDRAM 通讯时的同步时钟参数,可以设置成 STM32的 HCLK 时钟频率的 1/2、1/3 或禁止输出时钟 (FMC_SDRAM_CLOCK_PERIOD_2/3 或FMC_SDRAM_CLOCK_DISABLE)。
FMC此工程配置的200MHz
20.1CubeMx配置FMC控制SDRAM【W9825G6KH-6】_第20张图片
SDRAM common clock选择2分频,所以SDRAM 时钟100MHz,此时钟不能大于芯片要求的166MHz
在这里插入图片描述
SDRAM common burst read:设置是否使能突发读取模式,禁止时等效于 BL=1,使能时 BL 的值等于模式寄存器中的配置。本配置禁止
SDRAM timing read pipe delay:配置在 CASLatency 个时钟周期后,再等待多少个 HCLK 时钟周期才进行数据采样,在确保正确的前提下,这个值设置为越短越好,可选择设置的参数值为 0、1 或 2 个 HCLK 时钟周期 (FMC_SDRAM_RPIPE_DELAY_0/1/2)。
本芯片0等待:
20.1CubeMx配置FMC控制SDRAM【W9825G6KH-6】_第21张图片

SDRAM1时序参数配置

Load mode register to active delay:[TMRD]加载模式寄存器命令与行或刷新命令之间的延迟。
Exit self-refresh delay:[TXSR]退出 TXSR 延迟 (Exit Self-refresh delay),即退出自我刷新命令后要等待的时间,过了这段时间才可以发送行有效命令
Self-refresh time:【TRAS】自我刷新时间 TRAS,即发送行有效命令后要等待的时间,过了这段时间才执行预充电命令。
SDRAM common row cycle delay:【TRC】 (Row cycle delay),即两个行有效命令之间的延迟,以及两个相邻刷新命令之间的延迟
Write recovery time:[TWR] (Recovery delay)即写命令和预充电命令之间的延迟,等待这段时间后才开始执行预充电命令
SDRAM common row precharge delay: TRP 延迟 (Row precharge delay),即预充电命令与其它命令之间的延迟
Row to column delay:TRCD 延迟 (Row to column delay),即行有效命令到列读写命令之间的延迟

SDRAM时序简单示意图:

SDRAM 初始化流程
20.1CubeMx配置FMC控制SDRAM【W9825G6KH-6】_第22张图片
20.1CubeMx配置FMC控制SDRAM【W9825G6KH-6】_第23张图片
20.1CubeMx配置FMC控制SDRAM【W9825G6KH-6】_第24张图片
20.1CubeMx配置FMC控制SDRAM【W9825G6KH-6】_第25张图片

W9825G6KH-6时序参数

20.1CubeMx配置FMC控制SDRAM【W9825G6KH-6】_第26张图片
本工程SDRAM配置的时钟等于FMC / SDRAM common clock = 200MHz / 2 = 100MHz = 10ns
20.1CubeMx配置FMC控制SDRAM【W9825G6KH-6】_第27张图片

SDRAM引脚全部上拉
20.1CubeMx配置FMC控制SDRAM【W9825G6KH-6】_第28张图片
20.1CubeMx配置FMC控制SDRAM【W9825G6KH-6】_第29张图片

MPU配置

20.1CubeMx配置FMC控制SDRAM【W9825G6KH-6】_第30张图片
注意看,SDRAM内存为Extern device
一定要配置SDRAM的MPU属性为Write back, Read allocate,Write allocate
20.1CubeMx配置FMC控制SDRAM【W9825G6KH-6】_第31张图片
生成工程代码…

CubeMx工程代码编写:

添加文件:

sdram_driver.c|sdram_driver.h:负责初始化SDRAM时序,及测试SDRAM内存
common_driver.c|common_driver.h:负责实现printf
common_driver.c

/**********************************************************************
*file:开发常用函数|宏文件
*author:残梦
*versions:V1.2
*date:2023.08.10
*note:
**********************************************************************/
#include "common_driver.h"

/*开始1、基础功能******************************************************/
/****************************************************
@function:计算数据的拟合系数
@param:*pA,*pB--系数
		x[],y[]--数据源
		dataSize--数据个数
@return:void
@note:拟合曲线y=Ax+B
****************************************************/
void LinearFitCoefficient(double *pA,double *pB,double x[],double y[],unsigned short int dataSize)
{
	unsigned short int i= 0;
	double AverX = 0.0f,AverY = 0.0f,a1 = 0.0f,a2 = 0.0f;

	if(dataSize == 0){*pA = *pB = 0.0;return;}
	else if(dataSize == 1){*pA = 0.0;*pB = y[0];return;}
	while(i < dataSize)	{AverX += x[i];AverY += y[i];i++;}
	AverX /= (double)(dataSize);AverY /= (double)(dataSize);

	a1 = a2 = 0.0f;
	for(i=0;i<dataSize;i++)
	{
		a1 += (x[i] - AverX)*(y[i] - AverY);
		a2 += (x[i] - AverX)*(x[i] - AverX);
	}
	*pA = a1/a2;
	*pB = AverY - (*pA)*AverX;
}

/****************************************
@function:二分法查找target在数组pdata中的最相邻位置
@param:target--目标数据,pdata--源数据,len--源数据长度
@return:[0,len-1]
@note:
****************************************/
unsigned long int DichotomyFindPos(float target,float *pdata,unsigned long int len)
{
	unsigned long int pos = 0,posl = 0,posr = 0;
	unsigned char flag = 0;

	//for(unsigned long int z = 0;z < len;z++){printf("[%d]=%f\n",z,*(pdata+z));}
	if(len <= 2){return 0;}
	//判定数据是否在区间外
	flag = (*(pdata + len -1) > *pdata)?1:0;
	if(flag == 1)//递增数据
	{
		if(target < *pdata)return 0;
		else if(target > *(pdata + len -1))return (len -1);
	}
	else
	{
		if(target > *pdata)return 0;
		else if(target < *(pdata + len -1))return (len -1);
	}

	unsigned long int num = 0;
	//区间内的数据
	posl = 0;posr = len -1;
	while((posl != (posr-1)) && (posl != posr))
	{
		pos = (posr + posl)/2;
		if(flag == 1)
		{
			if(target < (*(pdata + pos))){posr = pos;}
			else{posl = pos;}
		}
		else
		{
			if(target > (*(pdata + pos))){posr = pos;}
			else{posl = pos;}
		}
		num++;
		//printf("%d [%d,%d]=[%f,%f]\n",num,posl,posr,*(pdata + posl),*(pdata + posr));
	}
	//printf("[pos,tar]=[%d,%f] num=%d\n",posl,target,num);
	return posl;
}

/*结束****************************************************************/

/*开始1、STM32支持区***************************************************/
#ifdef dcommonEnable_STM32
#include "usart.h"

/******************************
@function:printf打印使用
@param:
@return:
@remark:
******************************/
int fputc(int ch,FILE *f)
{
	unsigned char temp[1] = {ch};
	HAL_UART_Transmit(&huart1,temp,1,2);
	return(ch);
}
#endif
/*结束****************************************************************/

/*开始1、PID功能支持区*************************************************/
#ifdef dcommonEnable_PID
/****************************************
@function:增量式PID算法
@param:	pid--PID_ParameterStructDef
		actual_val--当前采集值
		Min--输出限幅最小值
		Max--输出限幅最大值
@return:
@note:
****************************************/
float PID_realize_increment(PID_ParameterStructDef *pid,float actual_val,unsigned long int Min,unsigned long int Max)
{
	/*计算目标值与实际值的误差*/
	pid->err=pid->target-actual_val;
	/*PID算法实现*/
	pid->actual += pid->Kp*(pid->err - pid->err_next)
					+ pid->Ki*pid->err
					+ pid->Kd*(pid->err - 2 * pid->err_next + pid->err_last);
	/*传递误差*/
	pid->err_last = pid->err_next;
	pid->err_next = pid->err;

	pid->actual = (pid->actual < Min)?Min:pid->actual;
	pid->actual = (pid->actual > Max)?Max:pid->actual;

   /*返回当前实际值*/
   return pid->actual;
}

/****************************************
@function:位置式PID算法
@param:	pid--PID_ParameterStructDef
		actual_val--当前采集值
		Min--输出限幅最小值
		Max--输出限幅最大值
@return:
@note:
****************************************/
float PID_realize_location(PID_ParameterStructDef *pid,float actual_val,unsigned long int Min,unsigned long int Max)
{
	/*计算目标值与实际值的误差*/
	pid->err=pid->target-actual_val;
	/*误差累积*/
	pid->integral+=pid->err;
	/*PID算法实现*/
	pid->actual=pid->Kp*pid->err + pid->Ki*pid->integral + pid->Kd * (pid->err - pid->err_last);
	/*误差传递*/
	pid->err_last=pid->err;

	pid->actual = (pid->actual < Min)?Min:pid->actual;
	pid->actual = (pid->actual > Max)?Max:pid->actual;
	return pid->actual;
}

#endif


common_driver.h

#ifndef _common_driver_H_
#define _common_driver_H_
#ifdef __cplusplus
extern "C" {
#endif

//本文件使用宏的方式开启附加功能
#define dcommonEnable_STM32 //使能stm32功能
//#define dcommonEnable_PID //使能PID功能

#include "stdint.h"
#include "stdlib.h"
#include "stdio.h"
#include "string.h"
#include "math.h"

#define dBOOL(x) (x?eStatus_Valid:eStatus_Invalid)//逻辑值:真-1,假-0

typedef  uint8_t    u8;
typedef  uint16_t   u16;
typedef  uint32_t   u32;
typedef  int8_t    s8;
typedef  int16_t   s16;
typedef  int32_t   s32;

typedef struct
{
	unsigned char byte1;
	unsigned char byte2;
	unsigned char byte3;
	unsigned char byte4;
}Byte4_MemoryParameterStructDef;

typedef struct
{
	unsigned char byte1;
	unsigned char byte2;
}Byte2_MemoryParameterStructDef;

typedef union
{
	short int Value;
	Byte2_MemoryParameterStructDef Memory;
}Convert_ShortIntParameter_UnionDef;

typedef union
{
	unsigned short int Value;
	Byte2_MemoryParameterStructDef Memory;
}Convert_UnsignedShortIntParameter_UnionDef;

typedef union
{
	unsigned long int Value;
	Byte4_MemoryParameterStructDef Memory;
}Convert_UnsignedLongIntParameter_UnionDef;

typedef union
{
	float Value;
	Byte4_MemoryParameterStructDef Memory;
}Convert_FloatParameter_UnionDef;

typedef struct
{
    uint8_t hour;
    uint8_t minute;
    uint8_t second;
    uint8_t millisecond;
}Time24Format_StructDef;

typedef enum
{
    eStatus_Invalid = 0,
    eStatus_Valid = 1
}status_EnumDef;

void LinearFitCoefficient(double *pA,double *pB,double x[],double y[],unsigned short int dataSize);
unsigned long int DichotomyFindPos(float target,float *pdata,unsigned long int len);

//STM32支持区
#ifdef dcommonEnable_STM32
#include "main.h"
#pragma diag_suppress 177 //忽略编译时函数定义但是没有引用的警告

#define dSET_PIN(GPIOx,Pin)         GPIOx->BSRR = Pin  //引脚置1
#define dRESET_PIN(GPIOx,Pin)       GPIOx->BSRR =  ((uint32_t)Pin << 16u) //引脚置0
#define dPIN_WRITE(GPIOx,Pin,x)     GPIOx->BSRR = ((uint32_t)Pin << ((x)?0u:16u))
#define dPIN_READ(GPIOx,Pin)        (GPIOx->IDR & Pin)?1:0 //获取引脚状态
#define dxPIN_MODE_IN(gpio,pin)     {gpio->MODER &= ~(3<<(pin*2));gpio->MODER |= 0<<(pin*2);}//配置引脚为输入模式
#define dxPIN_MODE_OUT(gpio,pin)    {gpio->MODER &= ~(3<<(pin*2));gpio->MODER |= 1<<(pin*2);}//配置引脚为输出模式
#define dPIN_TURN(GPIOx,Pin)        HAL_GPIO_TogglePin(GPIOx,Pin)

#endif

//PID功能支持区
#ifdef dcommonEnable_PID
typedef struct
{
  float target;//目标值
  float actual;//当前输出值
  float err;//本次偏差值
  float err_last;//上一次偏差值
  float err_next;//上上次的偏差值
  float integral;//累计误差
  float Kp;
  float Ki;
  float Kd;
}PID_ParameterStructDef;//PID参数结构体

float PID_realize_increment(PID_ParameterStructDef *pid,float actual_val,unsigned long int Min,unsigned long int Max);
float PID_realize_location(PID_ParameterStructDef *pid,float actual_val,unsigned long int Min,unsigned long int Max);

#endif

#ifdef __cplusplus
}
#endif
#endif


sdram_driver.c

/**********************************************************************
*file:SDRAM驱动文件
*author:残梦
*versions:V1.2
*date:2023.10.12
*note:
**********************************************************************/
#include "sdram_driver.h"
#include "common_driver.h"

/****************************************************
@function:初始化SDRAM时序
@param:hsdram--sdram句柄
@return:void
@note:
****************************************************/
void sdram_InitialTimingSequence(SDRAM_HandleTypeDef *hsdram)
{
    FMC_SDRAM_CommandTypeDef Command;

    //时钟使能
    Command.CommandMode = FMC_SDRAM_CMD_CLK_ENABLE;
    Command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
    Command.AutoRefreshNumber = 1;
    Command.ModeRegisterDefinition = 0;
    HAL_SDRAM_SendCommand(hsdram,&Command,0xFFFF);

    //延时至少200us
    HAL_Delay(1);

    //对所有的Banks预充电
    Command.CommandMode = FMC_SDRAM_CMD_PALL;
    Command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
    Command.AutoRefreshNumber = 1;
    Command.ModeRegisterDefinition = 0;
    HAL_SDRAM_SendCommand(hsdram,&Command,0xFFFF);

    //插入8个自动刷新周期
    Command.CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE;
    Command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
    Command.AutoRefreshNumber = 8;
    Command.ModeRegisterDefinition = 0;
    HAL_SDRAM_SendCommand(hsdram,&Command,0xFFFF);

    //编程sdram的加载模式寄存器
    Command.CommandMode = FMC_SDRAM_CMD_LOAD_MODE;
    Command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
    Command.AutoRefreshNumber = 1;
    Command.ModeRegisterDefinition = 0x230;
    HAL_SDRAM_SendCommand(hsdram,&Command,0xFFFF);

    //配置stm32 FMC的sdram控制器的自动刷新周期
    //Refresh rate = (SDRAM refresh rate  * SDRAM clock frequency) - 20
    //SDRAM refresh rate = SDRAM refresh period / Number of rows
    //SDRAM refresh rate = 64ms / 8196(rows) = 7.81us
    //Refresh rate = 7.81us * 100Mhz  - 20 = 761
    HAL_SDRAM_ProgramRefreshRate(hsdram,761);
}

/****************************************************
@function:SDRAM内存简单测试
@param:void
@return:void
@note:
****************************************************/
uint32_t pbuffer[(32*1024*1024)/4] __attribute__((at(0xC0000000)));//0xC0000000是SDRAM1的起始地址
void sdram_test(void)
{
    uint32_t i = 0,err = 0;
    while(1)
    {
        for(i=0;i < (32*1024*1024)/4;i++)
        {
            pbuffer[i] = i;
        }

        err = 0;
        for(i=0;i < (32*1024*1024)/4;i++)
        {
            if(pbuffer[i] != i)err++;
            else if(i < 10)printf("pbuffer[%d]=%d\n",i,pbuffer[i]);
        }
        if(err){printf("err:%d\n",err);while(1)HAL_Delay(25);}
    }
}


sdram_driver.h

#ifndef _sdram_driver_H_
#define _sdram_driver_H_
#include "fmc.h"

void sdram_InitialTimingSequence(SDRAM_HandleTypeDef *hsdram);
void sdram_test(void);

#endif

编写工程

在fmc.c文件中MX_FMC_Init函数初始化SDRAM后添加SDRAM时序初始化函数,及添加相应头文件

#include "sdram_driver.h"
void MX_FMC_Init(void)
{
  /* USER CODE BEGIN FMC_Init 0 */

  /* USER CODE END FMC_Init 0 */

  FMC_SDRAM_TimingTypeDef SdramTiming = {0};

  /* USER CODE BEGIN FMC_Init 1 */

  /* USER CODE END FMC_Init 1 */

  /** Perform the SDRAM1 memory initialization sequence
  */
  hsdram1.Instance = FMC_SDRAM_DEVICE;
  /* hsdram1.Init */
  hsdram1.Init.SDBank = FMC_SDRAM_BANK1;
  hsdram1.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_9;
  hsdram1.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_13;
  hsdram1.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_16;
  hsdram1.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;
  hsdram1.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_3;
  hsdram1.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE;
  hsdram1.Init.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_2;
  hsdram1.Init.ReadBurst = FMC_SDRAM_RBURST_DISABLE;
  hsdram1.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_0;
  /* SdramTiming */
  SdramTiming.LoadToActiveDelay = 2;
  SdramTiming.ExitSelfRefreshDelay = 8;
  SdramTiming.SelfRefreshTime = 5;
  SdramTiming.RowCycleDelay = 6;
  SdramTiming.WriteRecoveryTime = 4;
  SdramTiming.RPDelay = 2;
  SdramTiming.RCDDelay = 2;

  if (HAL_SDRAM_Init(&hsdram1, &SdramTiming) != HAL_OK)
  {
    Error_Handler( );
  }

  /* USER CODE BEGIN FMC_Init 2 */
  sdram_InitialTimingSequence(&hsdram1);//SDRAM时序初始化
  /* USER CODE END FMC_Init 2 */
}

main.c文件添加SDRAM初始化及测试函数

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * 

© Copyright (c) 2023 STMicroelectronics. * All rights reserved.

* * This software component is licensed by ST under BSD 3-Clause license, * the "License"; You may not use this file except in compliance with the * License. You may obtain a copy of the License at: * opensource.org/licenses/BSD-3-Clause * ****************************************************************************** */
/* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "usart.h" #include "gpio.h" #include "fmc.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "sdram_driver.h" #include "common_driver.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MPU_Config(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MPU Configuration--------------------------------------------------------*/ MPU_Config(); /* Enable I-Cache---------------------------------------------------------*/ SCB_EnableICache(); /* Enable D-Cache---------------------------------------------------------*/ SCB_EnableDCache(); /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_FMC_Init(); MX_USART1_UART_Init(); /* USER CODE BEGIN 2 */ sdram_test();//测试SDRAM内存 /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Supply configuration update enable */ HAL_PWREx_ConfigSupply(PWR_LDO_SUPPLY); /** Configure the main internal regulator output voltage */ __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE0); while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {} /** Macro to configure the PLL clock source */ __HAL_RCC_PLL_PLLSOURCE_CONFIG(RCC_PLLSOURCE_HSE); /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLM = 2; RCC_OscInitStruct.PLL.PLLN = 44; RCC_OscInitStruct.PLL.PLLP = 1; RCC_OscInitStruct.PLL.PLLQ = 2; RCC_OscInitStruct.PLL.PLLR = 2; RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_3; RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE; RCC_OscInitStruct.PLL.PLLFRACN = 0; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2 |RCC_CLOCKTYPE_D3PCLK1|RCC_CLOCKTYPE_D1PCLK1; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2; RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2; RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK) { Error_Handler(); } } /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ /* MPU Configuration */ void MPU_Config(void) { MPU_Region_InitTypeDef MPU_InitStruct = {0}; /* Disables the MPU */ HAL_MPU_Disable(); /** Initializes and configures the Region and the memory to be protected */ MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.Number = MPU_REGION_NUMBER0; MPU_InitStruct.BaseAddress = 0xC0000000; MPU_InitStruct.Size = MPU_REGION_SIZE_32MB; MPU_InitStruct.SubRegionDisable = 0x0; MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE; MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct); /* Enables the MPU */ HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT); } /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); printf("void Error_Handler(void)\n"); while (1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

如此即可完成,见信息窗:
20.1CubeMx配置FMC控制SDRAM【W9825G6KH-6】_第32张图片
20.1CubeMx配置FMC控制SDRAM【W9825G6KH-6】_第33张图片

未报错说明SDRAM内存正常,可以使用

完整工程下载:

链接:https://pan.baidu.com/s/1j173Z9Yx1D2yItmBCZNf9w
提取码:p2j4

你可能感兴趣的:(STM32,5G,单片机,fpga开发)