STM32CubeMX配置SDRAM

 

目录

一  STM32F7存储器映射

二  使用STM32CubeMX配置SDRAM

三 SDRAM初始化时序

四 为SDRAM空间配置MPU

五  通过自定义动态分配内存函数使用SDRAM中的空间


一  STM32F7存储器映射

ITCM-RAM0x0000 0000 ~ 0x400016 Kbytes只能CPU访问
Flash Memory On ITCM Interface0x0020 0000 ~ 0x003F FFFF2 Mbytes 
Flash Memory On AXIM Interface0x0800 0000 ~ 0x081F FFFF2 Mbytes 
Data TCM RAM0x2000 0000 ~ (0x2002 0000 - 1 )128 Kbytes 
Main internal SRAM10x2002 0000 ~ (0x2007 C000 -1)368 Kbytes 
Auxiliary internal SRAM20x2007 C00016 Kbytes 
Peripheral

0x4000 0000 ~ 0x5FFF FFFF

512MbytesRegister Addr
Bank 10x6000 0000 ~ 0x6FFF FFFF256 MbytesNOR/PSRAM/SRAM
Bank 20x7000 0000 ~ 0x7FFF FFFF Reserved
Bank 30x8000 0000 ~ 0x8FFF FFFF256 MbytesNAND
Bank 40x9000 0000 ~ 0x9FFF FFFF256 MbytesReserved
SDRAM Bank 10xC000 0000 ~ 0xCFFF FFFF256 MbytesSDRAM
SDRAM Bank 20xD000 0000 ~ 0xDFFF FFFF256 MbytesSDRAM

二  使用STM32CubeMX配置SDRAM

  • SDRAM MODE配置如下所示:   

该配置参照原理图就可以配置出来.
bank选择线: BA0与BA1都有相连,2bit可以表示四种变化,所以选择4 banks.
Byte enable:表示可以通过LDQM,UDQM线控制访问8bit数据,还是16bit数据.

  • SDRAM Configuration配置如下

以上配置主要通过查阅从W9825G6KH数据手册进行确定:

1.从W9825G6KH数据手册中可得知Row Address:A0-A12,共13bits; Column Address:A0-A8,共9bits.

2.CAS latency从W9825G6KH数据手册的目录页就可以看出

3.禁止写保护 并 使能突发读模式提高读效率

4.SDRAM common clock:

   SDRAM有4个bank, 在切换bank时,SDRAM需要延迟一定时间, SDRAM common clock就是用于配置该时间.在SDRAM手册中 
   对应= 2 tick , 所以该值配置为2.

5.SDRAM common read pipe delay: CAS 延迟后延后多少个 HCLK 时钟周期读取数据

6.时序配置

在stm32f7的数据手册有这么一句话"SDRAM clock can be HCLK/2 or HCLK/3" ,所以SDRAM的时钟最大为216/2=108MHz,所以对于SDRAM而言, 1tick = 9.26ns

STM32CubeMX参数说明SDRAM手册对应简写
Load mode register to active delay2加载模式寄存器到激活时间的延迟2 tick
Exit self-refresh delay8退出自我刷新后需要延迟的时间72ns
Self-refresh time6自我刷新周期55ns
SDRAM common row cycle delay6行循环延迟2 tick
Write recovery time2写恢复延迟2 tick
SDRAM common row precharge delay2行预充电延迟15ns
Row to Column delay6行到列延迟15ns

(tips: 按道理SDRAM common row cycle delay 与 Row to Column delay 可以最小设置为2的, 但是在实际使用中发现配置为2时SDRAM并不能正常工作, 经过试验这两个参数可设置为6 )

  • GPIO配置

gpio是根据原理图进行配置的, 可参照下图进行修改: 

  • 通过STM32CubeMX生成的相关SDRAM代码如下
/* FMC initialization function */
static 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_ENABLE;
  hsdram1.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_1;
  /* SdramTiming */
  SdramTiming.LoadToActiveDelay = 2;
  SdramTiming.ExitSelfRefreshDelay = 8;
  SdramTiming.SelfRefreshTime = 6;
  SdramTiming.RowCycleDelay = 6;
  SdramTiming.WriteRecoveryTime = 2;
  SdramTiming.RPDelay = 2;
  SdramTiming.RCDDelay = 6;

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

  /* USER CODE BEGIN FMC_Init 2 */

  /* USER CODE END FMC_Init 2 */
}


static void HAL_FMC_MspInit(void){
  /* USER CODE BEGIN FMC_MspInit 0 */

  /* USER CODE END FMC_MspInit 0 */
  GPIO_InitTypeDef GPIO_InitStruct ={0};
  if (FMC_Initialized) {
    return;
  }
  FMC_Initialized = 1;
  /* Peripheral clock enable */
  __HAL_RCC_FMC_CLK_ENABLE();
  
  /** FMC GPIO Configuration  
  PF0   ------> FMC_A0
  PF1   ------> FMC_A1
  PF2   ------> FMC_A2
  PF3   ------> FMC_A3
  PF4   ------> FMC_A4
  PF5   ------> FMC_A5
  PC0   ------> FMC_SDNWE
  PC2   ------> FMC_SDNE0
  PC3   ------> FMC_SDCKE0
  PF11   ------> FMC_SDNRAS
  PF12   ------> FMC_A6
  PF13   ------> FMC_A7
  PF14   ------> FMC_A8
  PF15   ------> FMC_A9
  PG0   ------> FMC_A10
  PG1   ------> FMC_A11
  PE7   ------> FMC_D4
  PE8   ------> FMC_D5
  PE9   ------> FMC_D6
  PE10   ------> FMC_D7
  PE11   ------> FMC_D8
  PE12   ------> FMC_D9
  PE13   ------> FMC_D10
  PE14   ------> FMC_D11
  PE15   ------> FMC_D12
  PD8   ------> FMC_D13
  PD9   ------> FMC_D14
  PD10   ------> FMC_D15
  PD14   ------> FMC_D0
  PD15   ------> FMC_D1
  PG2   ------> FMC_A12
  PG4   ------> FMC_BA0
  PG5   ------> FMC_BA1
  PG8   ------> FMC_SDCLK
  PD0   ------> FMC_D2
  PD1   ------> FMC_D3
  PG15   ------> FMC_SDNCAS
  PE0   ------> FMC_NBL0
  PE1   ------> FMC_NBL1
  */
  GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3 
                          |GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_11|GPIO_PIN_12 
                          |GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.Alternate = GPIO_AF12_FMC;
  HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_2|GPIO_PIN_3;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.Alternate = GPIO_AF12_FMC;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_4 
                          |GPIO_PIN_5|GPIO_PIN_8|GPIO_PIN_15;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.Alternate = GPIO_AF12_FMC;
  HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10 
                          |GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14 
                          |GPIO_PIN_15|GPIO_PIN_0|GPIO_PIN_1;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.Alternate = GPIO_AF12_FMC;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_14 
                          |GPIO_PIN_15|GPIO_PIN_0|GPIO_PIN_1;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.Alternate = GPIO_AF12_FMC;
  HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

  /* USER CODE BEGIN FMC_MspInit 1 */

  /* USER CODE END FMC_MspInit 1 */
}

三 SDRAM初始化时序

SDRAM数据手册中有这么一段话

从该段话中可以得出初始化时序如下:

STM32CubeMX自动生成的初始化函数如下:

(Tips: 如果只做以上配置是不会自动生成以下初始化代码的,如果要自动生成该段代码,需要在STM32CubeMX中使用SDRAM,之所以我这里生成了这段代码是因为我配置了TouchGFX的Parameter Settings中的SDRAM Instances:

如果没有自动生成,我们可以自己复制如下代码到自己的工程。

/**
  * @brief  Programs the SDRAM device.
  * @retval None
  */
void MX_SDRAM_InitEx(void)
{
  __IO uint32_t tmpmrd = 0;
  
  /* Step 1: Configure a clock configuration enable command */
  Command.CommandMode            = FMC_SDRAM_CMD_CLK_ENABLE;
  Command.CommandTarget          =  FMC_SDRAM_CMD_TARGET_BANK1;
  Command.AutoRefreshNumber      = 1;
  Command.ModeRegisterDefinition = 0;

  /* Send the command */
  HAL_SDRAM_SendCommand(&hsdram1, &Command, SDRAM_TIMEOUT);

  /* Step 2: Insert 100 us minimum delay */ 
  /* Inserted delay is equal to 1 ms due to systick time base unit (ms) */
  HAL_Delay(1);
    
  /* Step 3: Configure a PALL (precharge all) command */ 
  Command.CommandMode            = FMC_SDRAM_CMD_PALL;
  Command.CommandTarget          = FMC_SDRAM_CMD_TARGET_BANK1;
  Command.AutoRefreshNumber      = 1;
  Command.ModeRegisterDefinition = 0;

  /* Send the command */
  HAL_SDRAM_SendCommand(&hsdram1, &Command, SDRAM_TIMEOUT);  
  
  /* Step 4: Configure an Auto Refresh command */ 
  Command.CommandMode            = FMC_SDRAM_CMD_AUTOREFRESH_MODE;
  Command.CommandTarget          = FMC_SDRAM_CMD_TARGET_BANK1;
  Command.AutoRefreshNumber      = 8;
  Command.ModeRegisterDefinition = 0;

  /* Send the command */
  HAL_SDRAM_SendCommand(&hsdram1, &Command, SDRAM_TIMEOUT);
  
  /* Step 5: Program the external memory mode register */
  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;

  /* Send the command */
  HAL_SDRAM_SendCommand(&hsdram1, &Command, SDRAM_TIMEOUT);
  
  /* Step 6: Set the refresh rate counter */
  /* Set the device refresh rate */
  HAL_SDRAM_ProgramRefreshRate(&hsdram1, REFRESH_COUNT); 
}

到这里,SDRAM基本配置完成, 可以定义一个数组到SDRAM空间,对该数组进行读写操作, 以此判断SDRAM是否正常工作:

uint16_t test_buf[128] __attribute__((at(0xC0000000)));	//定义一个数组,该数组首地址为SDRAM首地址0xC0000000

//写该数组
for(uint8_t i = 0;i < 128 ; i++){
    test_buf[i] = i;
}

//将该数组的值通过串口打印出来
for(uint8_t i = 0;i < 128 ; i++){
    printf("%d ",test_buf[i]);
}

四 为SDRAM空间配置MPU

为了防止出现在访问SDRAM时出现莫名其妙的数据错误,建议配置MPU对SDRAM进行保护

附上配置截图:

五  通过自定义动态分配内存函数使用SDRAM中的空间

具体设计原理可以参考正点原子的例子,这里我就直接贴代码了

malloc.c代码如下所示

#include "malloc.h"

uint8_t   mem_rdy; 				//内存管理是否就绪

//内存池(32字节对齐), 外部SDRAM内存池,前面2M给LTDC用了(1280*800*2)
__align(32) uint8_t mem_base[MEM_MAX_SIZE] __attribute__((at(0xC0600000)));	

uint32_t mem_map[MEM_MAP_SIZE] __attribute__((at(0xC0600000 + MEM_MAX_SIZE)));//内存管理表

/***************************************************************************************
  * @brief   内存管理初始化  
  * @input   
  * @return  
***************************************************************************************/
static void mem_init(void)
{
    memset(mem_map, 0, MEM_MAP_SIZE *4);	//内存状态表数据清零  
 	mem_rdy = 1;						//内存管理初始化OK  
}


/***************************************************************************************
  * @brief   内存分配(内部调用)
  * @input   size:要分配的内存大小(字节)
  * @return  返回值:0XFFFFFFFF,代表错误;其他,内存偏移地址 
***************************************************************************************/
static uint32_t sdram_malloc(uint32_t size)  
{
    signed long offset=0;  
    uint32_t nmemb;	//需要的内存块数  
	uint32_t cmemb = 0;//连续空内存块数
    uint32_t i;
    if( !mem_rdy ){
        mem_init();//未初始化,先执行初始化 
    }
    if(size == 0){
        return 0xFFFFFFFF;//不需要分配
    }
    nmemb = size / MEM_BLOCK_SIZE;//获取需要分配的连续内存块数
    if(size % MEM_BLOCK_SIZE){
        nmemb++;  
    }
    for(offset = MEM_MAP_SIZE - 1; offset >= 0; offset--)//搜索整个内存控制区  
    {
		if( !mem_map[offset] )
            cmemb++;//连续空内存块数增加
		else
            cmemb=0;								//连续内存块清零
        
		if(cmemb == nmemb){							//找到了连续nmemb个空内存块
            for(i=0; i < nmemb; i++) { 					//标注内存块非空 
                mem_map[offset+i] = nmemb;  
            }
            return (offset * MEM_BLOCK_SIZE);//返回偏移地址  
		}
    }  
    return 0XFFFFFFFF;//未找到符合分配条件的内存块  
}


/***************************************************************************************
  * @brief   释放内存(内部调用) 
  * @input   memx:所属内存块;
             offset:内存地址偏移
  * @return  返回值:0,释放成功;1,释放失败;  
***************************************************************************************/
static uint8_t sdram_free(uint32_t offset)  
{
    int i;
    if( !mem_rdy )//未初始化,先执行初始化
	{
		mem_init();    
        return 1;//未初始化  
    }
    if(offset < MEM_MAX_SIZE)                 //偏移在内存池内. 
    {  
        int index = offset / MEM_BLOCK_SIZE;  //偏移所在内存块号码  
        int nmemb = mem_map[index];	  //内存块数量
        for(i=0; i < nmemb; i++)  			  //内存块清零
        {
            mem_map[index+i] = 0;  
        }  
        return 0;  
    }else 
        return 2;//偏移超区了.  
}  


/***************************************************************************************
  * @brief   释放内存(外部调用) 
  * @input   ptr:内存首地址 
  * @return  
***************************************************************************************/
void mem_free(void *ptr)  
{
	uint32_t offset;   
	if(ptr == NULL) 
        return;//地址为0.  
 	offset = (uint32_t)ptr - (uint32_t)mem_base;     
    sdram_free(offset);	//释放内存      
}


/***************************************************************************************
  * @brief   分配内存(外部调用)
  * @input   size:内存大小(字节)
  * @return  
***************************************************************************************/
void *mem_malloc(uint32_t size)  
{
    uint32_t offset;   
	offset = sdram_malloc(size);  	   	 	   
    if(offset == 0xFFFFFFFF)
        return NULL;  
    else 
        return (void*)((uint32_t)mem_base + offset);  
}

malloc.h文件内容如下所示

#ifndef __MALLOC_H
#define __MALLOC_H

#include "stdint.h"
#include "string.h"

#ifndef NULL
#define NULL 0
#endif

//SDRAM内存参数设定
#define MEM_MAX_SIZE			20*1024*1024  					//最大管理内存20M
#define MEM_BLOCK_SIZE			64  	  						//内存块大小为64字节
#define MEM_MAP_SIZE        	(MEM_MAX_SIZE / MEM_BLOCK_SIZE)   //内存表大小


//用户调用函数
void mem_free(void *ptr)  ;  			//内存释放(外部调用)
void *mem_malloc(uint32_t size);			//内存分配(外部调用)
#endif

 

  • 15
    点赞
  • 91
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
STM32CubeMX是一个用于配置STM32微控制器的图形化工具。在使用STM32CubeMX配置SDRAM时,需要进行以下步骤: 1. 配置FMC(Flexible Memory Controller):在STM32CubeMX中选择对应的STM32微控制器型号,然后在"Pinout & Configuration"选项卡中选择FMC功能,并根据SDRAM芯片的引脚连接情况进行引脚配置。 2. 配置SDRAM参数:在"Configuration"选项卡中选择"SDRAM",然后根据SDRAM芯片的规格书填写相关参数,如时序参数、刷新周期等。 3. 生成代码:在STM32CubeMX中点击"Project"菜单,选择"Generate Code",然后选择合适的开发环境(如Keil、IAR等)生成代码。 4. 初始化SDRAM:在生成的代码中,可以找到SDRAM初始化的函数。例如,在引用\[3\]中给出了一个SDRAM初始化的函数示例,其中包括了时钟使能命令、SDRAM预充电命令、自动刷新命令、配置SDRAM模式寄存器等步骤。 5. 编写SDRAM读写测试代码:根据需要,可以编写SDRAM读写测试代码来验证SDRAM的功能是否正常。 总结起来,使用STM32CubeMX配置SDRAM需要进行FMC配置SDRAM参数配置、生成代码以及编写初始化和测试代码等步骤。 #### 引用[.reference_title] - *1* [STM32CubeMX SDRAM的使用(一)](https://blog.csdn.net/tianizimark/article/details/126265260)[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,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [STM32CubeMX SDRAM的使用(二)](https://blog.csdn.net/tianizimark/article/details/126335762)[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,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [stm32cubemx读写SDRAM-W9825G6KH](https://blog.csdn.net/qq_45467083/article/details/109425825)[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,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值