20、stm32使用FMC驱动SDRAM(IS42S32800G-6BLI)
本文将使用安富莱的STM32H743XIH板子驱动SDRAM
引脚连接情况
一、CubeMx配置工程
1、开启调试口
2、开启外部高速时钟
配置时钟树
3、开启串口1
4、配置MPU
按照安富莱的例程配置:
/*
*********************************************************************************************************
* 函 数 名: MPU_Config
* 功能说明: 配置MPU
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
static void MPU_Config( void )
{MPU_Region_InitTypeDef MPU_InitStruct;/* 禁止 MPU */HAL_MPU_Disable();/* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */MPU_InitStruct.Enable = MPU_REGION_ENABLE;MPU_InitStruct.BaseAddress = 0x24000000;MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;MPU_InitStruct.Number = MPU_REGION_NUMBER0;MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;MPU_InitStruct.SubRegionDisable = 0x00;MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;HAL_MPU_ConfigRegion(&MPU_InitStruct);/* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */MPU_InitStruct.Enable = MPU_REGION_ENABLE;MPU_InitStruct.BaseAddress = 0x60000000;MPU_InitStruct.Size = ARM_MPU_REGION_SIZE_64KB; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; /* 不能用MPU_ACCESS_CACHEABLE;会出现2次CS、WE信号 */MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;MPU_InitStruct.Number = MPU_REGION_NUMBER1;MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;MPU_InitStruct.SubRegionDisable = 0x00;MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;HAL_MPU_ConfigRegion(&MPU_InitStruct);/* 配置SDRAM的MPU属性为Write back, Read allocate,Write allocate */MPU_InitStruct.Enable = MPU_REGION_ENABLE;MPU_InitStruct.BaseAddress = 0xC0000000;MPU_InitStruct.Size = MPU_REGION_SIZE_32MB;MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;MPU_InitStruct.Number = MPU_REGION_NUMBER2;MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;MPU_InitStruct.SubRegionDisable = 0x00;MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;HAL_MPU_ConfigRegion(&MPU_InitStruct);/*使能 MPU */HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}/*
*********************************************************************************************************
* 函 数 名: CPU_CACHE_Enable
* 功能说明: 使能L1 Cache
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
static void CPU_CACHE_Enable(void)
{/* 使能 I-Cache */SCB_EnableICache();/* 使能 D-Cache */SCB_EnableDCache();
}
5、配置FMC
调整引脚和硬件接线图一致
注意FMC时钟
生成代码工程…
二、编写代码
1、添加文件至工程
common_driver.c|common_driver.h
bsp.c|bsp.h
sdram_driver.c|sdram_driver.h
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,假-0typedef 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
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_ParameterStructDefactual_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_ParameterStructDefactual_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
bsp.h
#ifndef _bsp_H_
#define _bsp_H_
#ifdef __Cplusplus
#extern "C" {
#endif
#include "stdint.h"int32_t bsp_init(void);#ifdef __Cplusplus
}
#endif
#endif
bsp.c
/**********************************************************************
*file:板级支持包文件
*author:残梦
*versions:V1.0
*date:2023.08.10
*note:
**********************************************************************/
#include "bsp.h"
#include "common_driver.h"/****************************************
@function:板硬件初始化
@param:void
@return:小于0--失败,0--成功
@note:
****************************************/
int32_t bsp_init(void)
{return 0;
}
sdram_driver.h
#ifndef _sdram_driver_H_
#define _sdram_driver_H_
#ifdef __Cplusplus
#extern "C" {
#endif
#include "main.h"#define EXT_SDRAM_ADDR ((uint32_t)0xC0000000)
#define EXT_SDRAM_SIZE (32 * 1024 * 1024)/* LCD显存,第1页, 分配2M字节 */
#define SDRAM_LCD_BUF1 EXT_SDRAM_ADDR/* LCD显存,第2页, 分配2M字节 */
#define SDRAM_LCD_BUF2 (EXT_SDRAM_ADDR + SDRAM_LCD_SIZE)#define SDRAM_LCD_SIZE (2 * 1024 * 1024) /* 每层2M */
#define SDRAM_LCD_LAYER 2 /* 2层 *//* 剩下的12M字节,提供给应用程序使用 */
#define SDRAM_APP_BUF (EXT_SDRAM_ADDR + SDRAM_LCD_SIZE * SDRAM_LCD_LAYER)
#define SDRAM_APP_SIZE (EXT_SDRAM_SIZE - SDRAM_LCD_SIZE * SDRAM_LCD_LAYER)void SDRAM_Initialization_Sequence(SDRAM_HandleTypeDef *hsdram);
uint32_t bsp_TestExtSDRAM1(void);#ifdef __Cplusplus
}
#endif
#endif
sdram_driver.c
/**********************************************************************
*file:外部SDRAM驱动文件:SDRAM型号IS42S32800G-6BLI, 32位带宽, 容量32MB, 6ns速度(166MHz)
*author:残梦
*versions:V1.0
*date:2023.06.02
*note:-- 安富莱STM32-V7发板 SDRAM GPIO 定义+-------------------+--------------------+--------------------+--------------------++ SDRAM pins assignment ++-------------------+--------------------+--------------------+--------------------+| PD0 <-> FMC_D2 | PE0 <-> FMC_NBL0 | PF0 <-> FMC_A0 | PG0 <-> FMC_A10 || PD1 <-> FMC_D3 | PE1 <-> FMC_NBL1 | PF1 <-> FMC_A1 | PG1 <-> FMC_A11 || PD8 <-> FMC_D13 | PE7 <-> FMC_D4 | PF2 <-> FMC_A2 | PG4 <-> FMC_A14 || PD9 <-> FMC_D14 | PE8 <-> FMC_D5 | PF3 <-> FMC_A3 | PG5 <-> FMC_A15 || PD10 <-> FMC_D15 | PE9 <-> FMC_D6 | PF4 <-> FMC_A4 | PG8 <-> FC_SDCLK || PD14 <-> FMC_D0 | PE10 <-> FMC_D7 | PF5 <-> FMC_A5 | PG15 <-> FMC_NCAS || PD15 <-> FMC_D1 | PE11 <-> FMC_D8 | PF11 <-> FC_NRAS |--------------------++-------------------| PE12 <-> FMC_D9 | PF12 <-> FMC_A6 | PG2 --- FMC_A12 (预留64M字节容量,和摇杆上键复用)| PE13 <-> FMC_D10 | PF13 <-> FMC_A7 || PE14 <-> FMC_D11 | PF14 <-> FMC_A8 || PE15 <-> FMC_D12 | PF15 <-> FMC_A9 |+-------------------+--------------------+--------------------+| PH2 <-> FMC_SDCKE0| PI4 <-> FMC_NBL2 || PH3 <-> FMC_SDNE0 | PI5 <-> FMC_NBL3 || PH5 <-> FMC_SDNW |--------------------++-------------------++-------------------+------------------++ 32-bits Mode: D31-D16 ++-------------------+------------------+| PH8 <-> FMC_D16 | PI0 <-> FMC_D24 || PH9 <-> FMC_D17 | PI1 <-> FMC_D25 || PH10 <-> FMC_D18 | PI2 <-> FMC_D26 || PH11 <-> FMC_D19 | PI3 <-> FMC_D27 || PH12 <-> FMC_D20 | PI6 <-> FMC_D28 || PH13 <-> FMC_D21 | PI7 <-> FMC_D29 || PH14 <-> FMC_D22 | PI9 <-> FMC_D30 || PH15 <-> FMC_D23 | PI10 <-> FMC_D31 |+------------------+-------------------++-------------------++ Pins remapping ++-------------------+| PC0 <-> FMC_SDNWE || PC2 <-> FMC_SDNE0 || PC3 <-> FMC_SDCKE0|+-------------------+hsdram1.Instance = FMC_SDRAM_DEVICE;hsdram1.Init.SDBank = FMC_SDRAM_BANK1;hsdram1.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_9;hsdram1.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_12;hsdram1.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_32;hsdram1.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;hsdram1.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_3;//CAS Latency可以设置Latency1,2和3,实际测试Latency3稳定hsdram1.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE;//禁止写保护hsdram1.Init.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_2;//FMC时钟200MHz,2分频后给SDRAM,即100MHzhsdram1.Init.ReadBurst = FMC_SDRAM_RBURST_ENABLE;//使能读突发hsdram1.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_0;//此位定CAS延时后延后多少个SDRAM时钟周期读取数据,实际测此位可以设置无需延迟FMC使用的HCLK3时钟,200MHz,用于SDRAM的话,至少2分频,也就是100MHz,即1个SDRAM时钟周期是10ns下面参数单位均为10ns。Timing.LoadToActiveDelay = 2; 20ns, TMRD定义加载模式寄存器的命令与激活命令或刷新命令之间的延迟Timing.ExitSelfRefreshDelay = 7; 70ns, TXSR定义从发出自刷新命令到发出激活命令之间的延迟Timing.SelfRefreshTime = 4; 50ns, TRAS定义最短的自刷新周期Timing.RowCycleDelay = 7; 70ns, TRC定义刷新命令和激活命令之间的延迟Timing.WriteRecoveryTime = 2; 20ns, TWR定义在写命令和预充电命令之间的延迟Timing.RPDelay = 2; 20ns, TRP定义预充电命令与其它命令之间的延迟Timing.RCDDelay = 2; 20ns, TRCD定义激活命令与读/写命令之间的延迟
*********************************************************************/
#include "sdram_driver.h"#define SDRAM_TIMEOUT ((uint32_t)0xFFFF)
#define REFRESH_COUNT ((uint32_t)1543) /* SDRAM自刷新计数 */ /* SDRAM的参数配置 */
#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)/****************************************************
@function:SDRAM初始化序列
@param:hsdram: SDRAM句柄
@return:void
@note:完成SDRAM序列初始化
****************************************************/
void SDRAM_Initialization_Sequence(SDRAM_HandleTypeDef *hsdram)
{FMC_SDRAM_CommandTypeDef Command;__IO uint32_t tmpmrd =0;/*##-1- 时钟使能命令 ##################################################*/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, SDRAM_TIMEOUT);/*##-2- 插入延迟,至少100us ##################################################*/HAL_Delay(1);/*##-3- 整个SDRAM预充电命令,PALL(precharge all) #############################*/Command.CommandMode = FMC_SDRAM_CMD_PALL;Command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;Command.AutoRefreshNumber = 1;Command.ModeRegisterDefinition = 0;/* 发送命令 */HAL_SDRAM_SendCommand(hsdram, &Command, SDRAM_TIMEOUT);/*##-4- 自动刷新命令 #######################################################*/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, SDRAM_TIMEOUT);/*##-5- 配置SDRAM模式寄存器 ###############################################*/tmpmrd = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_1 |SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL |SDRAM_MODEREG_CAS_LATENCY_3 |SDRAM_MODEREG_OPERATING_MODE_STANDARD |SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;Command.CommandMode = FMC_SDRAM_CMD_LOAD_MODE;Command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;Command.AutoRefreshNumber = 1;Command.ModeRegisterDefinition = tmpmrd;/* 发送命令 */HAL_SDRAM_SendCommand(hsdram, &Command, SDRAM_TIMEOUT);/*##-6- 设置自刷新率 ####################################################*//*SDRAM refresh period / Number of rows)*SDRAM时钟速度 – 20= 64ms / 4096 *100MHz - 20= 1542.5 取值1543*/HAL_SDRAM_ProgramRefreshRate(hsdram, REFRESH_COUNT);
}/*
*********************************************************************************************************
* 函 数 名: bsp_TestExtSDRAM
* 功能说明: 扫描测试外部SDRAM的全部单元。
* 形 参: 无
* 返 回 值: 0 表示测试通过; 大于0表示错误单元的个数。
*********************************************************************************************************
*/
uint32_t bsp_TestExtSDRAM1(void)
{uint32_t i;uint32_t *pSRAM;uint8_t *pBytes;uint32_t err;const uint8_t ByteBuf[4] = {0x55, 0xA5, 0x5A, 0xAA};/* 写SRAM */pSRAM = (uint32_t *)EXT_SDRAM_ADDR;for (i = 0; i < EXT_SDRAM_SIZE / 4; i++){*pSRAM++ = i;}/* 读SRAM */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);}/* 对SRAM 的数据求反并写入 */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;
}
2、fmc.c初始化后添加SDRAM初始序列
添加头文件
/* USER CODE BEGIN 0 */
#include "sdram_driver.h"
/* USER CODE END 0 */
void MX_FMC_Init(void)函数中添加
/* USER CODE BEGIN FMC_Init 2 */SDRAM_Initialization_Sequence(&hsdram1);//添加SDRAM初始序列/* USER CODE END FMC_Init 2 */
3、main.c文件对sdram读写测试
main.c文件
/* USER CODE BEGIN Header */
/********************************************************************************* @file : main.c* @brief : Main program body******************************************************************************* @attention** <h2><center>© 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 "bsp.h"
#include "common_driver.h"
#include "sdram_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 */if(bsp_init() < 0){printf("error:bsp_init()\r\n");Error_Handler();}/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */printf("错误数:%d\r\n",bsp_TestExtSDRAM1());//SDRAM读写测试HAL_Delay(1000);}/* 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_SCALE1);while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}/** 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 = 5;RCC_OscInitStruct.PLL.PLLN = 160;RCC_OscInitStruct.PLL.PLLP = 2;RCC_OscInitStruct.PLL.PLLQ = 2;RCC_OscInitStruct.PLL.PLLR = 2;RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;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_2) != 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 = 0x24000000;MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;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);/** Initializes and configures the Region and the memory to be protected*/MPU_InitStruct.Number = MPU_REGION_NUMBER1;MPU_InitStruct.BaseAddress = 0x60000000;MPU_InitStruct.Size = MPU_REGION_SIZE_64KB;MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;HAL_MPU_ConfigRegion(&MPU_InitStruct);/** Initializes and configures the Region and the memory to be protected*/MPU_InitStruct.Number = MPU_REGION_NUMBER2;MPU_InitStruct.BaseAddress = 0xC0000000;MPU_InitStruct.Size = MPU_REGION_SIZE_32MB;MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_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)\r\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****/
三、测试结果及完整工程
测试结果ok
完整工程:
链接:https://pan.baidu.com/s/1SfxQO7QM_e1GsVD_yJOckg
提取码:hk7u
三、本章学习笔记(待写)
相关文章:

20、stm32使用FMC驱动SDRAM(IS42S32800G-6BLI)
本文将使用安富莱的STM32H743XIH板子驱动SDRAM 引脚连接情况 一、CubeMx配置工程 1、开启调试口 2、开启外部高速时钟 配置时钟树 3、开启串口1 4、配置MPU 按照安富莱的例程配置: /* ********************************************************************…...
git仓库大文件导致仓库体积增大处理
一、删除大文件 git filter-branch --tree-filter rm -rf path/to/large/file --prune-empty HEAD二、提交到远程 git push -f origin main PS:-f必须参数,强制刷新PS:git设计是为了存储代码,一般不将大文件上传到仓库...
将游戏坐标转化成屏幕鼠标坐标
将游戏坐标转化成屏幕鼠标坐标 思路说明:转化其实是取得两点的相对位置,例如将游戏人物移动到另外一个位置(游戏人物初始位置坐标到目的位置坐标),鼠标需要移动到屏幕的某个位置。算出游戏的移动距离,游戏…...
springboot中Instant时间传参及序列化
在部分场景中,后台的时间属性用的不是Date或Long,而是Instant,Java8引入的一个精度极高的时间类型,可以精确到纳秒,但实际使用的时候不需要这么高的精确度,通常到毫秒就可以了。 而在前后端传参的时候需要…...

nacos安装与启动相关问题(启动闪退和显示此站点的连接不安全)
问题:启动闪退 尝试: 使用记事本打开cmd文件,在文件结尾处新增两行 pause endlocal 如果还有问题:ERROR Nacos failed to start, please see D:\dev\nacos\logs\nacos.log for more details 尝试: 在nacos的bin目…...

51单片机学习--DS18B20温度读取温度报警器
需要先编写OneWire模块,再在DS18B20模块中调用OneWire模块的函数 先根据原理图做好端口的声明: sbit OneWire_DQ P3^7;接下来像之前一样把时序结构用代码模拟出来: unsigned char OneWire_Init(void) {unsigned char i;unsigned char Ac…...

PYTHON专栏
PYTHON专栏 python基础教程 python基础教程 Python练手算法 Python练手算法 Python设计模式 Python设计模式 MySQL教程 MySQL教程 ORM框架SQLAlchemy Python ORM框架SQLAlchemy Python Web框架Django Python Web框架Django Web框架FastAPI Web框架FastAPI http库request…...

从初学者到专家:Java运算符的完整指南
目录 1.算数运算符 2.增量运算符 2.1自增/自减运算符 4. 逻辑运算符 5.位运算符 6.移位运算符 7. 条件运算符 导言: Java作为一门广泛使用的编程语言,其运算符是编写代码时必不可少的一部分。本篇博客将为你详细介绍Java中的各种运算符…...

Linux:shell脚本:基础使用(3)
for循环语句 语句格式 for for变量 in 取值列表(可以是变量或者自己定义) do 循环内容 done 工作方式就是通过取值列表去判断循环的次数,每次循环的同时把列表一行的值赋予到for变量。取值方式如果是数字,那就通过数字去…...

opencv基础46-图像金字塔02-拉普拉斯金字塔
前面我们介绍了高斯金字塔,高斯金字塔是通过对一幅图像一系列的向下采样所产生的。有时,我们希望通过对金字塔中的小图像进行向上采样以获取完整的大尺寸高分辨率图像,这时就需要用到拉普拉斯金字塔 前面我们已经介绍过,一幅图像在…...

到 2030 年API 攻击预计将激增近 1000%
导读云原生应用程序编程接口管理公司 Kong 联合外部经济学家的最新研究预计,截至 2030 年 API 攻击将激增 996%,意味着与 API 相关的网络威胁的频率和强度都显着升级。 这项研究由 Kong 分析师和布朗大学副教授 Christopher Whaley 博士合作进行&#x…...
环形队列+DMA空闲中断+接收串口数据
环形队列DMA空闲中断接收串口数据 一.序言二.实验原理三.实战是检验真理的唯一标准3.1 usart1.c3.2 串口中断 三.队列代码4.1 fifo.c4.2 fifo.h 五.结语 一.序言 本次实验利用环形队列DMA空闲中断串口。。通过这个实验可以非常深入的理解队列,DMA,串口的知识。如果…...

LeetCode 31题:下一个排列
目录 题目 思路 代码 题目 整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。 例如,arr [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1] 。 整数数组的 下一个排列 是指其整数的下一个字典序…...

CMake:检测python模块和包
CMake:检测python模块和包 导言项目结构CMakeLists.txt相关源码 导言 上一篇,我们基本了解了如何去检测python的解释器和python库。通常,代码是依赖于特定的python模块,无论是python工具、嵌入python的程序,还是扩展python的库。…...

02Mysql之多表查询--例题讲解
一、题目详情,以及表的建立 新增员工表emp和部门表deptcreate table dept (dept1 int ,dept_name varchar(11));create table emp (sid int ,name varchar(11),age int,worktime_start date,incoming int,dept2 int);insert into dept values(101,财务),(102,销售)…...

虹科方案 | 汽车总线协议转换解决方案
汽车总线: 汽车总线是一种用于在车辆电子系统中传输数据和控制信息的通信系统。它允许不同的电子控制单元(ECU)在车辆中相互通信,协调各个系统的操作,以实现功能的集成和协同工作。 在现代汽车中,综合通信…...

Mr. Cappuccino的第59杯咖啡——简单手写SpringIOC框架
简单手写SpringIOC框架 环境搭建基于XML方式项目结构项目代码运行结果 基于注解方式项目结构项目代码运行结果 简单手写SpringIOC框架核心原理基于XML方式原理项目结构项目代码运行结果 基于注解方式原理项目结构项目代码运行结果 环境搭建 基于XML方式 项目结构 项目代码 p…...
爬虫 学习HTML标签和元素的基本概念,了解网页的结构和内容
HTML(Hypertext Markup Language)是一种用于创建网页的标记语言,由一系列的标签组成。标签使用尖括号(< 和 >)包围,并且通常成对出现,一个是开始标签,一个是结束标签。 HTML文…...
mysql将id重新修改为递增
文章目录 场景解决,排序的话可以先按照一定大小改一下,然后将id字段删掉,再重新生成即可清空表数据,并将自增id改为1开始 场景 好比我有个配置表: CREATE TABLE config (id int NOT NULL AUTO_INCREMENT,config_key varchar(20) NOT NULL,config_value varchar(500) NOT NU…...

http、https笔记
目录 HTTP 基本概念状态码:get和post的区别:http 常⻅字段:http的缺点: HTTP/1.1HTTP/3HTTPSHTTPS和HTTP区别对称加密和⾮对称加密⾮对称加密 HTTP 基本概念 状态码: 1xx 中间状态,比如post的continue 20…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
Android Wi-Fi 连接失败日志分析
1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分: 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析: CTR…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...

现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...

在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)
考察一般的三次多项式,以r为参数: p[z_, r_] : z^3 (r - 1) z - r; roots[r_] : z /. Solve[p[z, r] 0, z]; 此多项式的根为: 尽管看起来这个多项式是特殊的,其实一般的三次多项式都是可以通过线性变换化为这个形式…...

uniapp 小程序 学习(一)
利用Hbuilder 创建项目 运行到内置浏览器看效果 下载微信小程序 安装到Hbuilder 下载地址 :开发者工具默认安装 设置服务端口号 在Hbuilder中设置微信小程序 配置 找到运行设置,将微信开发者工具放入到Hbuilder中, 打开后出现 如下 bug 解…...

SQL注入篇-sqlmap的配置和使用
在之前的皮卡丘靶场第五期SQL注入的内容中我们谈到了sqlmap,但是由于很多朋友看不了解命令行格式,所以是纯手动获取数据库信息的 接下来我们就用sqlmap来进行皮卡丘靶场的sql注入学习,链接:https://wwhc.lanzoue.com/ifJY32ybh6vc…...
Netty自定义协议解析
目录 自定义协议设计 实现消息解码器 实现消息编码器 自定义消息对象 配置ChannelPipeline Netty提供了强大的编解码器抽象基类,这些基类能够帮助开发者快速实现自定义协议的解析。 自定义协议设计 在实现自定义协议解析之前,需要明确协议的具体格式。例如,一个简单的…...