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

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

原理图:

在这里插入图片描述

MCU引脚与SDRAM对应关系
在这里插入图片描述

引脚说明:

SDRAM引脚:
DQ[15:0]:数据线;数据位宽16
A[12:0]:地址线;行地址A[0:12],列地址A[0:8];
在这里插入图片描述
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;但是走等长确实有点困难,因为线束较多,分布比较散乱,不走等长时建议留出探测点
在这里插入图片描述
此图是我的SDRAM走线图
在这里插入图片描述
在这里插入图片描述
FMC引脚名参考:
在这里插入图片描述

CubeMx工程配置:

常规配置

配置工程时钟、串口、SWDIO
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

FMC配置

SDRAM硬件相关配置

Clock and chip enable:SDRAM选择:SDRAM1|SDRAM2
FMC内部包含两个SDRAM存储区
在这里插入图片描述
在这里插入图片描述
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根
在这里插入图片描述CAS latency:列地址选通延迟,简称CL。在发出读命令后需要等待几个时钟周期数据线DQ才是有效数据,仅在读命令时才有这个;
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
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
在这里插入图片描述
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等待:
在这里插入图片描述

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 初始化流程
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

W9825G6KH-6时序参数

在这里插入图片描述
本工程SDRAM配置的时钟等于FMC / SDRAM common clock = 200MHz / 2 = 100MHz = 10ns
在这里插入图片描述

SDRAM引脚全部上拉
在这里插入图片描述
在这里插入图片描述

MPU配置

在这里插入图片描述
注意看,SDRAM内存为Extern device
一定要配置SDRAM的MPU属性为Write back, Read allocate,Write allocate
在这里插入图片描述
生成工程代码…

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
  *
  * <h2><center>&copy; Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * 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****/

如此即可完成,见信息窗:
在这里插入图片描述
在这里插入图片描述

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

完整工程下载:

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

时间原因,本篇文章未完,后续内容见我的博客:20.2 FMC驱动SDRAM的时序初始化实现及内存测试

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
W9825G6KH-6I是一种外部SDRAM芯片,用于提供较大的运行内存,特别适用于需要缓存显示屏的情况。它具有1280*800分辨率的显示屏,每帧缓存需要1Mb~3Mb的内存。在STM32F4xx内部无法运行,因此需要使用外部缓存。需要注意的是,该芯片在掉电后数据会迅速丢失,重启后需要特别注意这一特性。SDRAM和SRAM有所区别,引脚和程序也不通用。一般情况下,我们使用SDRAM,因为它的性价比高且有丰富的资料支持。SDRAM的结构复杂,但使用起来相对简单。初始化后,只需要记住一个地址即可,程序上可以忘记它的存在。W9825G6KH-6I是一种常用的SDRAM芯片,市场上有很多可替代的芯片,价格在15元左右。在硬件连接方面,可以参考原理图和引脚说明进行连接。在程序使用方面,需要进行初始化和基本函数的调用,然后进行正常使用。总结起来,对于W9825G6KH-6I的使用,需要将SDRAM模式中的CAS latency设置为3,并将SDCLK周期设置为2个FMC周期。此外,还需要注意突发读使能等相关设置。在主函数中调用SDRAM_Cmd_Init()和SDRAM_Test()函数即可。如果初始化指令出现问题,可以通过修改下载方式为under reset,然后按住复位键,点击下载键,复位后立即松开来解决问题。如果有其他问题或需要源码,请在评论区下方留言或留下邮箱联系。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* [STM32F4xx SDRAM(更新中)](https://blog.csdn.net/zhouml_msn/article/details/90052820)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [STM32H750 SDRAM W9825G6KH-6](https://blog.csdn.net/smallerlang/article/details/128061724)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [STM32F429IGT6项目准备5——使用STM32CubeMX配置SDRAM](https://blog.csdn.net/qq_42039294/article/details/112221396)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值