• 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
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166

    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
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121

    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);}
        }
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87

    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
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    编写工程

    在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 */
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    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****/
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242

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

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

    完整工程下载:

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

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

  • 相关阅读:
    青海2022农民丰收节 国稻种芯:河湟第七届农产品展交会
    16: KEil5的配置
    flink: 从kafka读取数据
    外汇天眼:3天80%收益!推荐人还有10%的推荐奖金!CoinFxOnline被警告!
    JavaWeb——Cooike详解
    TIA博途中累计流量的两种计算方法示例
    ThreadPoolExecutor 源码分析
    MagnTek·新品 | 车规级高带宽高精度平行磁场感应电流传感器-MT9519系列
    多篇《Nature》和《Science》关于马约拉纳费米子的研究论文近日被撤稿
    Vue3+Vite实现工程化,attribute属性渲染v-bind指令
  • 原文地址:https://blog.csdn.net/qq_36561846/article/details/133810608