本文使用stm32h723zgt6的fmc驱动sdram,实现内存扩展
sdram型号W9825G6KH-6
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引脚名参考:
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
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等待:
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配置的时钟等于FMC / SDRAM common clock = 200MHz / 2 = 100MHz = 10ns
注意看,SDRAM内存为Extern device
一定要配置SDRAM的MPU属性为Write back, Read allocate,Write allocate
生成工程代码…
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****/
未报错说明SDRAM内存正常,可以使用
链接:https://pan.baidu.com/s/1j173Z9Yx1D2yItmBCZNf9w
提取码:p2j4