STM32CubeMX SDRAM的使用(二)

一、目的

        上一篇中《STM32CubeMX SDRAM的使用(一)》我们介绍了SDRAM的基本知识以及一些关键时间参数,本篇我们将介绍FMC中关于SDRAM控制器的相关知识并通过STM32CubeMX进行配置来测试我们的SDRAM芯片读写。

        文章末尾有整个工程文件链接

        关于STM32CubeMX的使用请参考:
手把手系列--使用STM32CubeMX生成代码工程_coder.mark的博客-CSDN博客_cubemx生成代码https://blog.csdn.net/tianizimark/article/details/121663108        关于SDRAM的知识点务必先看此篇

STM32CubeMX SDRAM的使用(一)_coder.mark的博客-CSDN博客https://blog.csdn.net/tianizimark/article/details/126265260

二、硬件信息 

         参考资料

STM32H742, STM32H743/753 and STM32H750 Value line advanced Arm®-based 32-bit MCUs - Reference manualhttps://www.st.com/resource/en/reference_manual/dm00314099-stm32h742-stm32h743-753-and-stm32h750-value-line-advanced-arm-based-32-bit-mcus-stmicroelectronics.pdf

        开发板信息 

        STM32H750XBH6

        W9825G6KH-6 13行9列4Bank,位宽16bit 总共32MByte;最大时钟166MHz/CL3或者133MHz/CL2

        FMC--灵活存储控制器

                包含三个存储控制器:

  • NOR/PSRAM存储控制器
  • NAND存储控制器
  • 同步DRAM(SDRAM/Mobile LPSDR SDRAM)控制器

                主要特性:

  • 将AXI数据通信事务转换成适当的外部器件协议
  • 满足外部存储器器件的访问时间要求
  • 所有外部存储器共享地址、数据和控制信号,但有各自的片选信号,FMC一次只能访问一个外部器件
  • 支持突发模式
  • 支持8/16/32位宽的数据总线
  • 每个存储区域独立的片选控制
  • 每个存储区域可独立配置
  • 16 x 32位深度写FIFO

                FMC框图

                FMC存储区域

                FMC存储区域映射选项

                SDRAM控制器描述

        SDRAM 控制器的主要特性如下:

  • 两个 SDRAM 存储区域,可独立配置
  • 8 位、 16 位和 32 位数据总线宽度
  • 13 位地址行, 11 位地址列, 4 个内部存储区域: 4x16Mx32bit (256 MB)、 4x16Mx16bit(128 MB)、 4x16Mx8bit (64 MB)
  • 支持字、半字和字节访问
  • SDRAM 时钟可以是 fmc_ker_ck/2 或 fmc_ker_ck/3
  • 自动进行行和存储区域边界管理
  • 多存储区域乒乓访问
  • 可编程时序参数
  • 支持自动刷新操作,可编程刷新速率
  • 自刷新模式
  • 掉电模式
  • 通过软件进行 SDRAM 上电初始化
  • CAS 延迟 1,2,3
  • 读 FIFO 可缓存,支持 6 行 x 32 位深度( 6 x14 位地址标记)

        接口信号描述

                 突发写入时序图

                突发读时序

                 自刷新时序

     

                FMC SDRAM自动进行行和存储区域边界管理(控制器时序)

                 

三、实战        

        1、创建工程后配置外部时钟并设置系统主时钟480MHz

        

        低速外部时钟(LSE)设置为32.768KHz

        高速外部时钟(HSE)设置为25MHz 

         

         2、配置SDRAM时钟

        STM32H750XBH6 FMC有两个SDRAM控制器,我们的外部SDRAM芯片接在第一个控制器上,片选和时钟使能为SDCKE0+SDNE0(地址空间首地址为0XC0000000),内部有4个Bank,位宽16bit

        

        3、设置外部SDRAM芯片参数

               Bank,即FMC SDRAM控制器选择bank1(注意跟SDRAM芯片内部逻辑bank是不同的意思)

               Number of column address bits,即列地址为9位

               Number of row address bits,即行地址为13位

               CAS latency,即CL(读命令时数据潜伏期)为3个时钟周期

               Write protection(写保护):禁止写保护

               SDRAM common busrt read(突发读):使能突发读

               SDRAM common read pipe delay:一般设置为0,指不再额外添加读取延迟(某些情况下由于布局布线的问题导致信号线延迟较大时可以通过此参数调整)

               SDRAM common clock对FMC时钟进行2分频(因为FMC时钟现在是240MHz),这样设置后输入给SDRAM的就为120MHz(8.3ns)

                从上图可以看到FMC的时钟源可以从HCLK3/PLL1Q/PLL2R/PER_CK中选择,我们选择的HCLK3 

         

        4、设置SDRAM关键时间参数(都是以当前SDRAM时钟周期为单位,我们现在配置为120MHz)

        下表为W9825G6KH-6的时间信息表(注意-6那一列)        

        

        为了解释方便,将此处的每行配置对应到SDRAM相关时间参数以及代码中的字段做成表格方便大家理解

        

配置项FMC_SDRAM_TimingTypeDef结构体字段SDRAM手册中时间参数说明
Load mode register to active delayLoadToActiveDelaytRSC模式寄存器命令与激活命令或者刷新命令间的延时,即模式寄存器配置生效时间
Exit self-refresh delayExitSelfRefreshDelaytXSR退出自刷新命令与激活命令间的延时
Self-refresh timeSelfRefreshTimetRAS激活和预充电命令之间的时间(bank激活后必须及时预充电,否则电荷会流失)
SDRAM common row cycle delayRowCycleDelaytRC刷新命令和激活命令间、刷新命令间的延时
Write recovery timeWriteRecoveryTimetWR写操作后必须等待此时间后才能进行预充电
SDRAM common row precharge delayRPDelaytRP预充电和其他命令间的延时
Row to column delayRCDDelaytRCD激活命令与读写命令间的延时

                1、Load mode register to active delay,即tRSC为2个时钟(模式寄存器命令与激活命令或者刷新命令间的延时,即模式寄存器配置生效时间)

Load mode register to active delay
Load mode register to active delay must be between 1 and 16.
Parameter Description:
Specifies the delay between a Load Mode Register command and an Active or Refresh command in number of memory clock cycles.

                2、Exit self-refresh delay,即tXSR为72ns(9 * 8.3 = 74.7ns),9个时钟

Exit self-refresh delay
Exit self-refresh delay must be between 1 and 16.
Parameter Description:
Specifies the delay from releasing the Self-refresh command to issuing the Activate command in number of memory clock cycles.

                退出自刷新命令与激活命令间的延时

                3、Self-refresh time,即tRAS为42ns(6 * 8.3 = 49.8ns)6个时钟

Self-refresh time
Self-refresh time must be between 1 and 16.
Parameter Description:
Specifies the minimum Self-refresh period in number of memory clock cycles.

                自刷新周期
 

                4、SDRAM common row cycle delay,即tRC为60ns(8 * 8.3 = 66.4ns),8个时钟

SDRAM common row cycle delay
SDRAM common row cycle delay must be between 1 and 16.
Parameter Description:
Specifies the delay between the Refresh command and the Activate command, as well as the delay between two consecutive Refresh commands.

                刷新命令和激活命令间、刷新命令间的延时

                5、Write recovery time,即tWR为2个时钟

                6、SDRAM common row precharge delay,即tRP为15ns(2 * 8.3 = 16.6ns)2个时钟

SDRAM common row precharge delay
SDRAM common row precharge delay must be between 1 and 16.
Parameter Description:
Specifies the delay between a Precharge command and another command in number of memory clock cycles.

                预充电和其他命令间的延时

                7、Row to column delay,即tRCD为15ns(2 * 8.3 = 16.6ns)2个时钟

Row to column delay
Row to column delay must be between 1 and 16.
Parameter Description:
Specifies the delay between the Activate command and a Read/Write command in number of memory clock cycles.

                激活命令与读写命令间的延时

        5、设置引脚

                每一个引脚都要选中后,然后引脚速度必须全部设置成Very High,其他配置项保持默认值即可(修改后的引脚Modify列会打上勾)

         6、生成工程

        

        7、代码分析

        1、FMC引脚配置相关,一定和电路原理图一一对照(特别注意一下  PH5   ------> FMC_SDNWE,默认的配置是PC0,我们的开发板上用的是PH5)

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;
  RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};

  /** Initializes the peripherals clock
  */
    PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_FMC;
    PeriphClkInitStruct.FmcClockSelection = RCC_FMCCLKSOURCE_D1HCLK;
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
    {
      Error_Handler();
    }

  /* Peripheral clock enable */
  __HAL_RCC_FMC_CLK_ENABLE();

  /** FMC GPIO Configuration
  PE1   ------> FMC_NBL1
  PE0   ------> FMC_NBL0
  PG15   ------> FMC_SDNCAS
  PD0   ------> FMC_D2
  PD1   ------> FMC_D3
  PG8   ------> FMC_SDCLK
  PF2   ------> FMC_A2
  PF1   ------> FMC_A1
  PF0   ------> FMC_A0
  PG5   ------> FMC_BA1
  PF3   ------> FMC_A3
  PG4   ------> FMC_BA0
  PG2   ------> FMC_A12
  PF5   ------> FMC_A5
  PF4   ------> FMC_A4
  PC2   ------> FMC_SDNE0
  PC3   ------> FMC_SDCKE0
  PE10   ------> FMC_D7
  PH5   ------> FMC_SDNWE
  PF13   ------> FMC_A7
  PF14   ------> FMC_A8
  PE9   ------> FMC_D6
  PE11   ------> FMC_D8
  PD15   ------> FMC_D1
  PD14   ------> FMC_D0
  PF12   ------> FMC_A6
  PF15   ------> FMC_A9
  PE12   ------> FMC_D9
  PE15   ------> FMC_D12
  PF11   ------> FMC_SDNRAS
  PG0   ------> FMC_A10
  PE8   ------> FMC_D5
  PE13   ------> FMC_D10
  PD10   ------> FMC_D15
  PD9   ------> FMC_D14
  PG1   ------> FMC_A11
  PE7   ------> FMC_D4
  PE14   ------> FMC_D11
  PD8   ------> FMC_D13
  */
  /* GPIO_InitStruct */
  GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_0|GPIO_PIN_10|GPIO_PIN_9
                          |GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_15|GPIO_PIN_8
                          |GPIO_PIN_13|GPIO_PIN_7|GPIO_PIN_14;
  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 */
  GPIO_InitStruct.Pin = GPIO_PIN_15|GPIO_PIN_8|GPIO_PIN_5|GPIO_PIN_4
                          |GPIO_PIN_2|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(GPIOG, &GPIO_InitStruct);

  /* GPIO_InitStruct */
  GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_15|GPIO_PIN_14
                          |GPIO_PIN_10|GPIO_PIN_9|GPIO_PIN_8;
  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);

  /* GPIO_InitStruct */
  GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_1|GPIO_PIN_0|GPIO_PIN_3
                          |GPIO_PIN_5|GPIO_PIN_4|GPIO_PIN_13|GPIO_PIN_14
                          |GPIO_PIN_12|GPIO_PIN_15|GPIO_PIN_11;
  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 */
  GPIO_InitStruct.Pin = 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 */
  GPIO_InitStruct.Pin = GPIO_PIN_5;
  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(GPIOH, &GPIO_InitStruct);

  /* USER CODE BEGIN FMC_MspInit 1 */

  /* USER CODE END FMC_MspInit 1 */
}

void HAL_SDRAM_MspInit(SDRAM_HandleTypeDef* sdramHandle){
  /* USER CODE BEGIN SDRAM_MspInit 0 */

  /* USER CODE END SDRAM_MspInit 0 */
  HAL_FMC_MspInit();
  /* USER CODE BEGIN SDRAM_MspInit 1 */

  /* USER CODE END SDRAM_MspInit 1 */
}

        2、FMC时序参数相关

void MX_FMC_Init(void)
{
  /* USER CODE BEGIN FMC_Init 0 */
  FMC_SDRAM_CommandTypeDef Command = {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_0;
  /* SdramTiming */
  SdramTiming.LoadToActiveDelay = 2;
  SdramTiming.ExitSelfRefreshDelay = 9;
  SdramTiming.SelfRefreshTime = 6;
  SdramTiming.RowCycleDelay = 8;
  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_Initialization_Sequence(&hsdram1, &Command);
  sdram_test(); 
  /* USER CODE END FMC_Init 2 */
}

        3、关键结构体说明 

        SDRAM_HandleTypeDef结构体说明

#if (USE_HAL_SDRAM_REGISTER_CALLBACKS == 1)
typedef struct __SDRAM_HandleTypeDef
#else
typedef struct
#endif /* USE_HAL_SDRAM_REGISTER_CALLBACKS  */
{
  FMC_SDRAM_TypeDef             *Instance;  /*!< Register base address                 */

  FMC_SDRAM_InitTypeDef         Init;       /*!< SDRAM device configuration parameters */

  __IO HAL_SDRAM_StateTypeDef   State;      /*!< SDRAM access state                    */

  HAL_LockTypeDef               Lock;       /*!< SDRAM locking object                  */

  MDMA_HandleTypeDef             *hmdma;      /*!< Pointer DMA handler                   */

#if (USE_HAL_SDRAM_REGISTER_CALLBACKS == 1)
  void (* MspInitCallback)(struct __SDRAM_HandleTypeDef *hsdram);               /*!< SDRAM Msp Init callback              */
  void (* MspDeInitCallback)(struct __SDRAM_HandleTypeDef *hsdram);             /*!< SDRAM Msp DeInit callback            */
  void (* RefreshErrorCallback)(struct __SDRAM_HandleTypeDef *hsdram);          /*!< SDRAM Refresh Error callback         */
  void (* DmaXferCpltCallback)(MDMA_HandleTypeDef *hmdma);                        /*!< SDRAM DMA Xfer Complete callback     */
  void (* DmaXferErrorCallback)(MDMA_HandleTypeDef *hmdma);                       /*!< SDRAM DMA Xfer Error callback        */
#endif /* USE_HAL_SDRAM_REGISTER_CALLBACKS */
} SDRAM_HandleTypeDef;

        FMC_SDRAM_InitTypeDef结构体说明(对应上文实战第三小节)

typedef struct
{
  uint32_t SDBank;                      /*!< Specifies the SDRAM memory device that will be used.
                                             This parameter can be a value of @ref FMC_SDRAM_Bank                */

  uint32_t ColumnBitsNumber;            /*!< Defines the number of bits of column address.
                                             This parameter can be a value of @ref FMC_SDRAM_Column_Bits_number. */

  uint32_t RowBitsNumber;               /*!< Defines the number of bits of column address.
                                             This parameter can be a value of @ref FMC_SDRAM_Row_Bits_number.    */

  uint32_t MemoryDataWidth;             /*!< Defines the memory device width.
                                             This parameter can be a value of @ref FMC_SDRAM_Memory_Bus_Width.   */

  uint32_t InternalBankNumber;          /*!< Defines the number of the device's internal banks.
                                             This parameter can be of @ref FMC_SDRAM_Internal_Banks_Number.      */

  uint32_t CASLatency;                  /*!< Defines the SDRAM CAS latency in number of memory clock cycles.
                                             This parameter can be a value of @ref FMC_SDRAM_CAS_Latency.        */

  uint32_t WriteProtection;             /*!< Enables the SDRAM device to be accessed in write mode.
                                             This parameter can be a value of @ref FMC_SDRAM_Write_Protection.   */

  uint32_t SDClockPeriod;               /*!< Define the SDRAM Clock Period for both SDRAM devices and they allow
                                             to disable the clock before changing frequency.
                                             This parameter can be a value of @ref FMC_SDRAM_Clock_Period.       */

  uint32_t ReadBurst;                   /*!< This bit enable the SDRAM controller to anticipate the next read
                                             commands during the CAS latency and stores data in the Read FIFO.
                                             This parameter can be a value of @ref FMC_SDRAM_Read_Burst.         */

  uint32_t ReadPipeDelay;               /*!< Define the delay in system clock cycles on read data path.
                                             This parameter can be a value of @ref FMC_SDRAM_Read_Pipe_Delay.    */
} FMC_SDRAM_InitTypeDef;

        SDBank:可选择的SDRAM控制器,我们设置为FMC_SDRAM_BANK1

#define FMC_SDRAM_BANK1                         (0x00000000U)
#define FMC_SDRAM_BANK2                         (0x00000001U)

        ColumnBitsNumber:列地址数,我们设置为FMC_SDRAM_COLUMN_BITS_NUM_9

#define FMC_SDRAM_COLUMN_BITS_NUM_8             (0x00000000U)
#define FMC_SDRAM_COLUMN_BITS_NUM_9             (0x00000001U)
#define FMC_SDRAM_COLUMN_BITS_NUM_10            (0x00000002U)
#define FMC_SDRAM_COLUMN_BITS_NUM_11            (0x00000003U)

        RowBitsNumber:行地址数,我们设置为FMC_SDRAM_ROW_BITS_NUM_13

#define FMC_SDRAM_ROW_BITS_NUM_11               (0x00000000U)
#define FMC_SDRAM_ROW_BITS_NUM_12               (0x00000004U)
#define FMC_SDRAM_ROW_BITS_NUM_13               (0x00000008U)

         MemoryDataWidth:存储器数据位宽,我们设置为FMC_SDRAM_MEM_BUS_WIDTH_16

#define FMC_SDRAM_MEM_BUS_WIDTH_8               (0x00000000U)
#define FMC_SDRAM_MEM_BUS_WIDTH_16              (0x00000010U)
#define FMC_SDRAM_MEM_BUS_WIDTH_32              (0x00000020U)

        InternalBankNumber:外部SDRAM的Bank数,我们设置为FMC_SDRAM_INTERN_BANKS_NUM_4

#define FMC_SDRAM_INTERN_BANKS_NUM_2            (0x00000000U)
#define FMC_SDRAM_INTERN_BANKS_NUM_4            (0x00000040U)

        CASLatency:即CL,读数据潜伏期,我们设置为FMC_SDRAM_CAS_LATENCY_3

#define FMC_SDRAM_CAS_LATENCY_1                 (0x00000080U)
#define FMC_SDRAM_CAS_LATENCY_2                 (0x00000100U)
#define FMC_SDRAM_CAS_LATENCY_3                 (0x00000180U)

        WriteProtection:写保护使能,我们设置为FMC_SDRAM_WRITE_PROTECTION_DISABLE

#define FMC_SDRAM_WRITE_PROTECTION_DISABLE      (0x00000000U)
#define FMC_SDRAM_WRITE_PROTECTION_ENABLE       (0x00000200U)

        SDClockPeriod:FMC时钟分频系数,我们设置为FMC_SDRAM_CLOCK_PERIOD_2,即二分频

#define FMC_SDRAM_CLOCK_DISABLE                 (0x00000000U)
#define FMC_SDRAM_CLOCK_PERIOD_2                (0x00000800U)
#define FMC_SDRAM_CLOCK_PERIOD_3                (0x00000C00U)

        ReadBurst:读突发使能,我们设置为FMC_SDRAM_RBURST_ENABLE

#define FMC_SDRAM_RBURST_DISABLE                (0x00000000U)
#define FMC_SDRAM_RBURST_ENABLE                 (0x00001000U)

        ReadPipeDelay:读操作额外时延,我们设置为FMC_SDRAM_RPIPE_DELAY_0

#define FMC_SDRAM_RPIPE_DELAY_0                 (0x00000000U)
#define FMC_SDRAM_RPIPE_DELAY_1                 (0x00002000U)
#define FMC_SDRAM_RPIPE_DELAY_2                 (0x00004000U)

         其中FMC_SDRAM_TimingTypeDef结构体定义了关键时间参数(对应上文实战第四小节)

/**
  * @brief FMC SDRAM Timing parameters structure definition
  */
typedef struct
{
  uint32_t LoadToActiveDelay;            /*!< Defines the delay between a Load Mode Register command and
                                              an active or Refresh command in number of memory clock cycles.
                                              This parameter can be a value between Min_Data = 1 and Max_Data = 16  */

  uint32_t ExitSelfRefreshDelay;         /*!< Defines the delay from releasing the self refresh command to
                                              issuing the Activate command in number of memory clock cycles.
                                              This parameter can be a value between Min_Data = 1 and Max_Data = 16  */

  uint32_t SelfRefreshTime;              /*!< Defines the minimum Self Refresh period in number of memory clock
                                              cycles.
                                              This parameter can be a value between Min_Data = 1 and Max_Data = 16  */

  uint32_t RowCycleDelay;                /*!< Defines the delay between the Refresh command and the Activate command
                                              and the delay between two consecutive Refresh commands in number of
                                              memory clock cycles.
                                              This parameter can be a value between Min_Data = 1 and Max_Data = 16  */

  uint32_t WriteRecoveryTime;            /*!< Defines the Write recovery Time in number of memory clock cycles.
                                              This parameter can be a value between Min_Data = 1 and Max_Data = 16  */

  uint32_t RPDelay;                      /*!< Defines the delay between a Precharge Command and an other command
                                              in number of memory clock cycles.
                                              This parameter can be a value between Min_Data = 1 and Max_Data = 16  */

  uint32_t RCDDelay;                     /*!< Defines the delay between the Activate Command and a Read/Write
                                              command in number of memory clock cycles.
                                              This parameter can be a value between Min_Data = 1 and Max_Data = 16  */
} FMC_SDRAM_TimingTypeDef;

        有些小伙伴如果不能理解注释可以根据此信息找到对应的时间参数信息

        LoadToActiveDelay:即tRSC,模式寄存器设置命令生效时间,经过这么长时间后才能发起行激活或者刷新命令

        ExitSelfRefreshDelay:即tXSR,退出自刷新命令与行激活命令之间的延时

        SelfRefreshTime:即tRAS,自刷新周期,激活命令与预充电命令之间的时间;行激活后如果不及时充电,电荷就会消失

        RowCycleDelay:即tRC,刷新命令和激活命令之间的时延或者刷新命令之间的时延

        WriteRecoveryTime:即tWR,写恢复时间也就回写时间

        RPDelay:即tRP,预充电命令与其他命令之间的延时

        RCDDelay:即tRCD,行选通与读写命令之间的延时

        

        SDRAM上电初始化代码

        从之前博文中我们介绍过SDRAM需要进行上电初始化

          我们通过FMC提供的接口可以给外部SDRAM芯片发送指令,其上电初始化以及模式寄存器的设置的代码如下

static void SDRAM_Initialization_Sequence(SDRAM_HandleTypeDef *hsdram, FMC_SDRAM_CommandTypeDef *Command)
{
    __IO uint32_t tmpmrd = 0;
    uint32_t target_bank = 0;

    target_bank = FMC_SDRAM_CMD_TARGET_BANK1;

    /* Configure a clock configuration enable command */
    Command->CommandMode           = FMC_SDRAM_CMD_CLK_ENABLE;
    Command->CommandTarget         = target_bank;
    Command->AutoRefreshNumber     = 1;
    Command->ModeRegisterDefinition = 0;

    /* Send the command */
    HAL_SDRAM_SendCommand(hsdram, Command, 0x1000);

    /* Insert 100 ms delay */
    /* interrupt is not enable, just to delay some time. */
    for (tmpmrd = 0; tmpmrd < 0xffff; tmpmrd ++)
        ;

    /* Configure a PALL (precharge all) command */
    Command->CommandMode            = FMC_SDRAM_CMD_PALL;
    Command->CommandTarget          = target_bank;
    Command->AutoRefreshNumber      = 1;
    Command->ModeRegisterDefinition = 0;

    /* Send the command */
    HAL_SDRAM_SendCommand(hsdram, Command, 0x1000);

    /* Configure a Auto-Refresh command */
    Command->CommandMode            = FMC_SDRAM_CMD_AUTOREFRESH_MODE;
    Command->CommandTarget          = target_bank;
    Command->AutoRefreshNumber      = 8;
    Command->ModeRegisterDefinition = 0;

    /* Send the command */
    HAL_SDRAM_SendCommand(hsdram, Command, 0x1000);

    /* Program the external memory mode register */
    tmpmrd = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_2     |
             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          = target_bank;
    Command->AutoRefreshNumber      = 1;
    Command->ModeRegisterDefinition = tmpmrd;

    /* Send the command */
    HAL_SDRAM_SendCommand(hsdram, Command, 0x1000);

    /* Set the device refresh counter */
    HAL_SDRAM_ProgramRefreshRate(hsdram, SDRAM_REFRESH_COUNT);
}

        我们先介绍一下FMC_SDRAM_CommandTypeDef结构体

typedef struct
{
  uint32_t CommandMode;                  /*!< Defines the command issued to the SDRAM device.
                                              This parameter can be a value of @ref FMC_SDRAM_Command_Mode.          */

  uint32_t CommandTarget;                /*!< Defines which device (1 or 2) the command will be issued to.
                                              This parameter can be a value of @ref FMC_SDRAM_Command_Target.        */

  uint32_t AutoRefreshNumber;            /*!< Defines the number of consecutive auto refresh command issued
                                              in auto refresh mode.
                                              This parameter can be a value between Min_Data = 1 and Max_Data = 15   */

  uint32_t ModeRegisterDefinition;       /*!< Defines the SDRAM Mode register content                                */
} FMC_SDRAM_CommandTypeDef;

                ComandMode设置具体的操作命令,包括正常模式、时钟使能、所有Bank预充电、自动刷新、加载模式寄存器、自刷新模式、掉电模式

#define FMC_SDRAM_CMD_NORMAL_MODE               (0x00000000U)
#define FMC_SDRAM_CMD_CLK_ENABLE                (0x00000001U)
#define FMC_SDRAM_CMD_PALL                      (0x00000002U)
#define FMC_SDRAM_CMD_AUTOREFRESH_MODE          (0x00000003U)
#define FMC_SDRAM_CMD_LOAD_MODE                 (0x00000004U)
#define FMC_SDRAM_CMD_SELFREFRESH_MODE          (0x00000005U)
#define FMC_SDRAM_CMD_POWERDOWN_MODE            (0x00000006U)

                CommandTarget设置给哪个SDRAM控制器发送,我们选择FMC_SDRAM_CMD_TARGET_BANK1 

#define FMC_SDRAM_CMD_TARGET_BANK2              FMC_SDCMR_CTB2
#define FMC_SDRAM_CMD_TARGET_BANK1              FMC_SDCMR_CTB1
#define FMC_SDRAM_CMD_TARGET_BANK1_2            (0x00000018U)

                AutoRefreshNumber设置自动刷新命令时需要刷新的次数,其他命令设置为1即可

                ModeRegisterDefinition设置SDRAM芯片上模式寄存器的值,具体设置之前已经讲解,不再赘述

        

                通过HAL_SDRAM_ProgramRefreshRate设置自动刷新定时器周期  

​​​​​​​

 

/* 64ms / 8192 = 7.8125us
 * 7.8125 * 120MHz - 20 = 917.5 ==> 918
 */

                至此我们已经配置好了整个SDRAM相关的选项

                最后我们关注一下MPU的设置

void MPU_Config(void)
{
    MPU_Region_InitTypeDef MPU_InitStruct;

    /* Disable the MPU */
    HAL_MPU_Disable();

    /* Configure the MPU attributes as WT for AXI SRAM */
    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_NOT_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_LEVEL0;
    MPU_InitStruct.SubRegionDisable  = 0X00;
    MPU_InitStruct.DisableExec       = MPU_INSTRUCTION_ACCESS_ENABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);

    /* Configure the MPU attributes as WT for SDRAM */
    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_NOT_BUFFERABLE;
    MPU_InitStruct.IsCacheable       = MPU_ACCESS_CACHEABLE;
    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);

    /* Enable the MPU */
    HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

                 八、调试        

                 

                我们写了一个测试函数用于测试SDRAM的读写

static int sdram_test(void)
{
    int i = 0;
#if SDRAM_DATA_WIDTH == 8
    char data_width = 1;
    uint8_t data = 0;
#elif SDRAM_DATA_WIDTH == 16
    char data_width = 2;
    uint16_t data = 0;
#else
    char data_width = 4;
    uint32_t data = 0;
#endif

    /* write data */
    for (i = 0; i < SDRAM_SIZE / data_width; i++)
    {
#if SDRAM_DATA_WIDTH == 8
        *(__IO uint8_t *)(SDRAM_BANK_ADDR + i * data_width) = (uint8_t)0x55;
#elif SDRAM_DATA_WIDTH == 16
        *(__IO uint16_t *)(SDRAM_BANK_ADDR + i * data_width) = (uint16_t)0x5555;
#else
        *(__IO uint32_t *)(SDRAM_BANK_ADDR + i * data_width) = (uint32_t)0x55555555;
#endif
    }

    /* read data */
    for (i = 0; i < SDRAM_SIZE / data_width; i++)
    {
#if SDRAM_DATA_WIDTH == 8
        data = *(__IO uint8_t *)(SDRAM_BANK_ADDR + i * data_width);
        if (data != 0x55)
        {
			//ERROR
            break;
        }
#elif SDRAM_DATA_WIDTH == 16
        data = *(__IO uint16_t *)(SDRAM_BANK_ADDR + i * data_width);
        if (data != 0x5555)
        {
			//ERROR
            break;
        }
#else
        data = *(__IO uint32_t *)(SDRAM_BANK_ADDR + i * data_width);
        if (data != 0x55555555)
        {
			//ERROR
            break;
        }
#endif
    }

    if (i >= SDRAM_SIZE / data_width)
    {
		//OK
    }
}

        通过SDRAM_DATA_WIDTH 这个宏,我们可以分别测试8/16/32位读写测试

        最后工程代码如下

链接:https://pan.baidu.com/s/1MgM2DUKRCadZUOIBEhIZgg 
提取码:rnim

        记得要三连哦 

         

         

        

  • 9
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
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 ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值