• 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)

    1. static void HAL_FMC_MspInit(void){
    2. /* USER CODE BEGIN FMC_MspInit 0 */
    3. /* USER CODE END FMC_MspInit 0 */
    4. GPIO_InitTypeDef GPIO_InitStruct = {0};
    5. if (FMC_Initialized) {
    6. return;
    7. }
    8. FMC_Initialized = 1;
    9. RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
    10. /** Initializes the peripherals clock
    11. */
    12. PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_FMC;
    13. PeriphClkInitStruct.FmcClockSelection = RCC_FMCCLKSOURCE_D1HCLK;
    14. if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
    15. {
    16. Error_Handler();
    17. }
    18. /* Peripheral clock enable */
    19. __HAL_RCC_FMC_CLK_ENABLE();
    20. /** FMC GPIO Configuration
    21. PE1 ------> FMC_NBL1
    22. PE0 ------> FMC_NBL0
    23. PG15 ------> FMC_SDNCAS
    24. PD0 ------> FMC_D2
    25. PD1 ------> FMC_D3
    26. PG8 ------> FMC_SDCLK
    27. PF2 ------> FMC_A2
    28. PF1 ------> FMC_A1
    29. PF0 ------> FMC_A0
    30. PG5 ------> FMC_BA1
    31. PF3 ------> FMC_A3
    32. PG4 ------> FMC_BA0
    33. PG2 ------> FMC_A12
    34. PF5 ------> FMC_A5
    35. PF4 ------> FMC_A4
    36. PC2 ------> FMC_SDNE0
    37. PC3 ------> FMC_SDCKE0
    38. PE10 ------> FMC_D7
    39. PH5 ------> FMC_SDNWE
    40. PF13 ------> FMC_A7
    41. PF14 ------> FMC_A8
    42. PE9 ------> FMC_D6
    43. PE11 ------> FMC_D8
    44. PD15 ------> FMC_D1
    45. PD14 ------> FMC_D0
    46. PF12 ------> FMC_A6
    47. PF15 ------> FMC_A9
    48. PE12 ------> FMC_D9
    49. PE15 ------> FMC_D12
    50. PF11 ------> FMC_SDNRAS
    51. PG0 ------> FMC_A10
    52. PE8 ------> FMC_D5
    53. PE13 ------> FMC_D10
    54. PD10 ------> FMC_D15
    55. PD9 ------> FMC_D14
    56. PG1 ------> FMC_A11
    57. PE7 ------> FMC_D4
    58. PE14 ------> FMC_D11
    59. PD8 ------> FMC_D13
    60. */
    61. /* GPIO_InitStruct */
    62. GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_0|GPIO_PIN_10|GPIO_PIN_9
    63. |GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_15|GPIO_PIN_8
    64. |GPIO_PIN_13|GPIO_PIN_7|GPIO_PIN_14;
    65. GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    66. GPIO_InitStruct.Pull = GPIO_NOPULL;
    67. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    68. GPIO_InitStruct.Alternate = GPIO_AF12_FMC;
    69. HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
    70. /* GPIO_InitStruct */
    71. GPIO_InitStruct.Pin = GPIO_PIN_15|GPIO_PIN_8|GPIO_PIN_5|GPIO_PIN_4
    72. |GPIO_PIN_2|GPIO_PIN_0|GPIO_PIN_1;
    73. GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    74. GPIO_InitStruct.Pull = GPIO_NOPULL;
    75. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    76. GPIO_InitStruct.Alternate = GPIO_AF12_FMC;
    77. HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);
    78. /* GPIO_InitStruct */
    79. GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_15|GPIO_PIN_14
    80. |GPIO_PIN_10|GPIO_PIN_9|GPIO_PIN_8;
    81. GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    82. GPIO_InitStruct.Pull = GPIO_NOPULL;
    83. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    84. GPIO_InitStruct.Alternate = GPIO_AF12_FMC;
    85. HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
    86. /* GPIO_InitStruct */
    87. GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_1|GPIO_PIN_0|GPIO_PIN_3
    88. |GPIO_PIN_5|GPIO_PIN_4|GPIO_PIN_13|GPIO_PIN_14
    89. |GPIO_PIN_12|GPIO_PIN_15|GPIO_PIN_11;
    90. GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    91. GPIO_InitStruct.Pull = GPIO_NOPULL;
    92. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    93. GPIO_InitStruct.Alternate = GPIO_AF12_FMC;
    94. HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
    95. /* GPIO_InitStruct */
    96. GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3;
    97. GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    98. GPIO_InitStruct.Pull = GPIO_NOPULL;
    99. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    100. GPIO_InitStruct.Alternate = GPIO_AF12_FMC;
    101. HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
    102. /* GPIO_InitStruct */
    103. GPIO_InitStruct.Pin = GPIO_PIN_5;
    104. GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    105. GPIO_InitStruct.Pull = GPIO_NOPULL;
    106. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    107. GPIO_InitStruct.Alternate = GPIO_AF12_FMC;
    108. HAL_GPIO_Init(GPIOH, &GPIO_InitStruct);
    109. /* USER CODE BEGIN FMC_MspInit 1 */
    110. /* USER CODE END FMC_MspInit 1 */
    111. }
    112. void HAL_SDRAM_MspInit(SDRAM_HandleTypeDef* sdramHandle){
    113. /* USER CODE BEGIN SDRAM_MspInit 0 */
    114. /* USER CODE END SDRAM_MspInit 0 */
    115. HAL_FMC_MspInit();
    116. /* USER CODE BEGIN SDRAM_MspInit 1 */
    117. /* USER CODE END SDRAM_MspInit 1 */
    118. }

            2、FMC时序参数相关

    1. void MX_FMC_Init(void)
    2. {
    3. /* USER CODE BEGIN FMC_Init 0 */
    4. FMC_SDRAM_CommandTypeDef Command = {0};
    5. /* USER CODE END FMC_Init 0 */
    6. FMC_SDRAM_TimingTypeDef SdramTiming = {0};
    7. /* USER CODE BEGIN FMC_Init 1 */
    8. /* USER CODE END FMC_Init 1 */
    9. /** Perform the SDRAM1 memory initialization sequence
    10. */
    11. hsdram1.Instance = FMC_SDRAM_DEVICE;
    12. /* hsdram1.Init */
    13. hsdram1.Init.SDBank = FMC_SDRAM_BANK1;
    14. hsdram1.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_9;
    15. hsdram1.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_13;
    16. hsdram1.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_16;
    17. hsdram1.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;
    18. hsdram1.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_3;
    19. hsdram1.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE;
    20. hsdram1.Init.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_2;
    21. hsdram1.Init.ReadBurst = FMC_SDRAM_RBURST_ENABLE;
    22. hsdram1.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_0;
    23. /* SdramTiming */
    24. SdramTiming.LoadToActiveDelay = 2;
    25. SdramTiming.ExitSelfRefreshDelay = 9;
    26. SdramTiming.SelfRefreshTime = 6;
    27. SdramTiming.RowCycleDelay = 8;
    28. SdramTiming.WriteRecoveryTime = 4;
    29. SdramTiming.RPDelay = 2;
    30. SdramTiming.RCDDelay = 2;
    31. if (HAL_SDRAM_Init(&hsdram1, &SdramTiming) != HAL_OK)
    32. {
    33. Error_Handler( );
    34. }
    35. /* USER CODE BEGIN FMC_Init 2 */
    36. SDRAM_Initialization_Sequence(&hsdram1, &Command);
    37. sdram_test();
    38. /* USER CODE END FMC_Init 2 */
    39. }

            3、关键结构体说明 

            SDRAM_HandleTypeDef结构体说明

    1. #if (USE_HAL_SDRAM_REGISTER_CALLBACKS == 1)
    2. typedef struct __SDRAM_HandleTypeDef
    3. #else
    4. typedef struct
    5. #endif /* USE_HAL_SDRAM_REGISTER_CALLBACKS */
    6. {
    7. FMC_SDRAM_TypeDef *Instance; /*!< Register base address */
    8. FMC_SDRAM_InitTypeDef Init; /*!< SDRAM device configuration parameters */
    9. __IO HAL_SDRAM_StateTypeDef State; /*!< SDRAM access state */
    10. HAL_LockTypeDef Lock; /*!< SDRAM locking object */
    11. MDMA_HandleTypeDef *hmdma; /*!< Pointer DMA handler */
    12. #if (USE_HAL_SDRAM_REGISTER_CALLBACKS == 1)
    13. void (* MspInitCallback)(struct __SDRAM_HandleTypeDef *hsdram); /*!< SDRAM Msp Init callback */
    14. void (* MspDeInitCallback)(struct __SDRAM_HandleTypeDef *hsdram); /*!< SDRAM Msp DeInit callback */
    15. void (* RefreshErrorCallback)(struct __SDRAM_HandleTypeDef *hsdram); /*!< SDRAM Refresh Error callback */
    16. void (* DmaXferCpltCallback)(MDMA_HandleTypeDef *hmdma); /*!< SDRAM DMA Xfer Complete callback */
    17. void (* DmaXferErrorCallback)(MDMA_HandleTypeDef *hmdma); /*!< SDRAM DMA Xfer Error callback */
    18. #endif /* USE_HAL_SDRAM_REGISTER_CALLBACKS */
    19. } SDRAM_HandleTypeDef;

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

    1. typedef struct
    2. {
    3. uint32_t SDBank; /*!< Specifies the SDRAM memory device that will be used.
    4. This parameter can be a value of @ref FMC_SDRAM_Bank */
    5. uint32_t ColumnBitsNumber; /*!< Defines the number of bits of column address.
    6. This parameter can be a value of @ref FMC_SDRAM_Column_Bits_number. */
    7. uint32_t RowBitsNumber; /*!< Defines the number of bits of column address.
    8. This parameter can be a value of @ref FMC_SDRAM_Row_Bits_number. */
    9. uint32_t MemoryDataWidth; /*!< Defines the memory device width.
    10. This parameter can be a value of @ref FMC_SDRAM_Memory_Bus_Width. */
    11. uint32_t InternalBankNumber; /*!< Defines the number of the device's internal banks.
    12. This parameter can be of @ref FMC_SDRAM_Internal_Banks_Number. */
    13. uint32_t CASLatency; /*!< Defines the SDRAM CAS latency in number of memory clock cycles.
    14. This parameter can be a value of @ref FMC_SDRAM_CAS_Latency. */
    15. uint32_t WriteProtection; /*!< Enables the SDRAM device to be accessed in write mode.
    16. This parameter can be a value of @ref FMC_SDRAM_Write_Protection. */
    17. uint32_t SDClockPeriod; /*!< Define the SDRAM Clock Period for both SDRAM devices and they allow
    18. to disable the clock before changing frequency.
    19. This parameter can be a value of @ref FMC_SDRAM_Clock_Period. */
    20. uint32_t ReadBurst; /*!< This bit enable the SDRAM controller to anticipate the next read
    21. commands during the CAS latency and stores data in the Read FIFO.
    22. This parameter can be a value of @ref FMC_SDRAM_Read_Burst. */
    23. uint32_t ReadPipeDelay; /*!< Define the delay in system clock cycles on read data path.
    24. This parameter can be a value of @ref FMC_SDRAM_Read_Pipe_Delay. */
    25. } FMC_SDRAM_InitTypeDef;

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

    1. #define FMC_SDRAM_BANK1 (0x00000000U)
    2. #define FMC_SDRAM_BANK2 (0x00000001U)

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

    1. #define FMC_SDRAM_COLUMN_BITS_NUM_8 (0x00000000U)
    2. #define FMC_SDRAM_COLUMN_BITS_NUM_9 (0x00000001U)
    3. #define FMC_SDRAM_COLUMN_BITS_NUM_10 (0x00000002U)
    4. #define FMC_SDRAM_COLUMN_BITS_NUM_11 (0x00000003U)

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

    1. #define FMC_SDRAM_ROW_BITS_NUM_11 (0x00000000U)
    2. #define FMC_SDRAM_ROW_BITS_NUM_12 (0x00000004U)
    3. #define FMC_SDRAM_ROW_BITS_NUM_13 (0x00000008U)

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

    1. #define FMC_SDRAM_MEM_BUS_WIDTH_8 (0x00000000U)
    2. #define FMC_SDRAM_MEM_BUS_WIDTH_16 (0x00000010U)
    3. #define FMC_SDRAM_MEM_BUS_WIDTH_32 (0x00000020U)

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

    1. #define FMC_SDRAM_INTERN_BANKS_NUM_2 (0x00000000U)
    2. #define FMC_SDRAM_INTERN_BANKS_NUM_4 (0x00000040U)

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

    1. #define FMC_SDRAM_CAS_LATENCY_1 (0x00000080U)
    2. #define FMC_SDRAM_CAS_LATENCY_2 (0x00000100U)
    3. #define FMC_SDRAM_CAS_LATENCY_3 (0x00000180U)

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

    1. #define FMC_SDRAM_WRITE_PROTECTION_DISABLE (0x00000000U)
    2. #define FMC_SDRAM_WRITE_PROTECTION_ENABLE (0x00000200U)

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

    1. #define FMC_SDRAM_CLOCK_DISABLE (0x00000000U)
    2. #define FMC_SDRAM_CLOCK_PERIOD_2 (0x00000800U)
    3. #define FMC_SDRAM_CLOCK_PERIOD_3 (0x00000C00U)

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

    1. #define FMC_SDRAM_RBURST_DISABLE (0x00000000U)
    2. #define FMC_SDRAM_RBURST_ENABLE (0x00001000U)

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

    1. #define FMC_SDRAM_RPIPE_DELAY_0 (0x00000000U)
    2. #define FMC_SDRAM_RPIPE_DELAY_1 (0x00002000U)
    3. #define FMC_SDRAM_RPIPE_DELAY_2 (0x00004000U)

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

    1. /**
    2. * @brief FMC SDRAM Timing parameters structure definition
    3. */
    4. typedef struct
    5. {
    6. uint32_t LoadToActiveDelay; /*!< Defines the delay between a Load Mode Register command and
    7. an active or Refresh command in number of memory clock cycles.
    8. This parameter can be a value between Min_Data = 1 and Max_Data = 16 */
    9. uint32_t ExitSelfRefreshDelay; /*!< Defines the delay from releasing the self refresh command to
    10. issuing the Activate command in number of memory clock cycles.
    11. This parameter can be a value between Min_Data = 1 and Max_Data = 16 */
    12. uint32_t SelfRefreshTime; /*!< Defines the minimum Self Refresh period in number of memory clock
    13. cycles.
    14. This parameter can be a value between Min_Data = 1 and Max_Data = 16 */
    15. uint32_t RowCycleDelay; /*!< Defines the delay between the Refresh command and the Activate command
    16. and the delay between two consecutive Refresh commands in number of
    17. memory clock cycles.
    18. This parameter can be a value between Min_Data = 1 and Max_Data = 16 */
    19. uint32_t WriteRecoveryTime; /*!< Defines the Write recovery Time in number of memory clock cycles.
    20. This parameter can be a value between Min_Data = 1 and Max_Data = 16 */
    21. uint32_t RPDelay; /*!< Defines the delay between a Precharge Command and an other command
    22. in number of memory clock cycles.
    23. This parameter can be a value between Min_Data = 1 and Max_Data = 16 */
    24. uint32_t RCDDelay; /*!< Defines the delay between the Activate Command and a Read/Write
    25. command in number of memory clock cycles.
    26. This parameter can be a value between Min_Data = 1 and Max_Data = 16 */
    27. } FMC_SDRAM_TimingTypeDef;

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

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

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

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

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

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

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

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

            

            SDRAM上电初始化代码

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

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

    1. static void SDRAM_Initialization_Sequence(SDRAM_HandleTypeDef *hsdram, FMC_SDRAM_CommandTypeDef *Command)
    2. {
    3. __IO uint32_t tmpmrd = 0;
    4. uint32_t target_bank = 0;
    5. target_bank = FMC_SDRAM_CMD_TARGET_BANK1;
    6. /* Configure a clock configuration enable command */
    7. Command->CommandMode = FMC_SDRAM_CMD_CLK_ENABLE;
    8. Command->CommandTarget = target_bank;
    9. Command->AutoRefreshNumber = 1;
    10. Command->ModeRegisterDefinition = 0;
    11. /* Send the command */
    12. HAL_SDRAM_SendCommand(hsdram, Command, 0x1000);
    13. /* Insert 100 ms delay */
    14. /* interrupt is not enable, just to delay some time. */
    15. for (tmpmrd = 0; tmpmrd < 0xffff; tmpmrd ++)
    16. ;
    17. /* Configure a PALL (precharge all) command */
    18. Command->CommandMode = FMC_SDRAM_CMD_PALL;
    19. Command->CommandTarget = target_bank;
    20. Command->AutoRefreshNumber = 1;
    21. Command->ModeRegisterDefinition = 0;
    22. /* Send the command */
    23. HAL_SDRAM_SendCommand(hsdram, Command, 0x1000);
    24. /* Configure a Auto-Refresh command */
    25. Command->CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE;
    26. Command->CommandTarget = target_bank;
    27. Command->AutoRefreshNumber = 8;
    28. Command->ModeRegisterDefinition = 0;
    29. /* Send the command */
    30. HAL_SDRAM_SendCommand(hsdram, Command, 0x1000);
    31. /* Program the external memory mode register */
    32. tmpmrd = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_2 |
    33. SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL |
    34. SDRAM_MODEREG_CAS_LATENCY_3 |
    35. SDRAM_MODEREG_OPERATING_MODE_STANDARD |
    36. SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;
    37. Command->CommandMode = FMC_SDRAM_CMD_LOAD_MODE;
    38. Command->CommandTarget = target_bank;
    39. Command->AutoRefreshNumber = 1;
    40. Command->ModeRegisterDefinition = tmpmrd;
    41. /* Send the command */
    42. HAL_SDRAM_SendCommand(hsdram, Command, 0x1000);
    43. /* Set the device refresh counter */
    44. HAL_SDRAM_ProgramRefreshRate(hsdram, SDRAM_REFRESH_COUNT);
    45. }

            我们先介绍一下FMC_SDRAM_CommandTypeDef结构体

    1. typedef struct
    2. {
    3. uint32_t CommandMode; /*!< Defines the command issued to the SDRAM device.
    4. This parameter can be a value of @ref FMC_SDRAM_Command_Mode. */
    5. uint32_t CommandTarget; /*!< Defines which device (1 or 2) the command will be issued to.
    6. This parameter can be a value of @ref FMC_SDRAM_Command_Target. */
    7. uint32_t AutoRefreshNumber; /*!< Defines the number of consecutive auto refresh command issued
    8. in auto refresh mode.
    9. This parameter can be a value between Min_Data = 1 and Max_Data = 15 */
    10. uint32_t ModeRegisterDefinition; /*!< Defines the SDRAM Mode register content */
    11. } FMC_SDRAM_CommandTypeDef;

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

    1. #define FMC_SDRAM_CMD_NORMAL_MODE (0x00000000U)
    2. #define FMC_SDRAM_CMD_CLK_ENABLE (0x00000001U)
    3. #define FMC_SDRAM_CMD_PALL (0x00000002U)
    4. #define FMC_SDRAM_CMD_AUTOREFRESH_MODE (0x00000003U)
    5. #define FMC_SDRAM_CMD_LOAD_MODE (0x00000004U)
    6. #define FMC_SDRAM_CMD_SELFREFRESH_MODE (0x00000005U)
    7. #define FMC_SDRAM_CMD_POWERDOWN_MODE (0x00000006U)

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

    1. #define FMC_SDRAM_CMD_TARGET_BANK2 FMC_SDCMR_CTB2
    2. #define FMC_SDRAM_CMD_TARGET_BANK1 FMC_SDCMR_CTB1
    3. #define FMC_SDRAM_CMD_TARGET_BANK1_2 (0x00000018U)

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

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

            

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

    ​​​​​​​

     

    1. /* 64ms / 8192 = 7.8125us
    2. * 7.8125 * 120MHz - 20 = 917.5 ==> 918
    3. */

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

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

    1. void MPU_Config(void)
    2. {
    3. MPU_Region_InitTypeDef MPU_InitStruct;
    4. /* Disable the MPU */
    5. HAL_MPU_Disable();
    6. /* Configure the MPU attributes as WT for AXI SRAM */
    7. MPU_InitStruct.Enable = MPU_REGION_ENABLE;
    8. MPU_InitStruct.BaseAddress = 0x24000000;
    9. MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;
    10. MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    11. MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
    12. MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
    13. MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
    14. MPU_InitStruct.Number = MPU_REGION_NUMBER0;
    15. MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
    16. MPU_InitStruct.SubRegionDisable = 0X00;
    17. MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
    18. HAL_MPU_ConfigRegion(&MPU_InitStruct);
    19. /* Configure the MPU attributes as WT for SDRAM */
    20. MPU_InitStruct.Enable = MPU_REGION_ENABLE;
    21. MPU_InitStruct.BaseAddress = 0xC0000000;
    22. MPU_InitStruct.Size = MPU_REGION_SIZE_32MB;
    23. MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    24. MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
    25. MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
    26. MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
    27. MPU_InitStruct.Number = MPU_REGION_NUMBER1;
    28. MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
    29. MPU_InitStruct.SubRegionDisable = 0x00;
    30. MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
    31. HAL_MPU_ConfigRegion(&MPU_InitStruct);
    32. /* Enable the MPU */
    33. HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
    34. }

                     八、调试        

                     

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

    1. static int sdram_test(void)
    2. {
    3. int i = 0;
    4. #if SDRAM_DATA_WIDTH == 8
    5. char data_width = 1;
    6. uint8_t data = 0;
    7. #elif SDRAM_DATA_WIDTH == 16
    8. char data_width = 2;
    9. uint16_t data = 0;
    10. #else
    11. char data_width = 4;
    12. uint32_t data = 0;
    13. #endif
    14. /* write data */
    15. for (i = 0; i < SDRAM_SIZE / data_width; i++)
    16. {
    17. #if SDRAM_DATA_WIDTH == 8
    18. *(__IO uint8_t *)(SDRAM_BANK_ADDR + i * data_width) = (uint8_t)0x55;
    19. #elif SDRAM_DATA_WIDTH == 16
    20. *(__IO uint16_t *)(SDRAM_BANK_ADDR + i * data_width) = (uint16_t)0x5555;
    21. #else
    22. *(__IO uint32_t *)(SDRAM_BANK_ADDR + i * data_width) = (uint32_t)0x55555555;
    23. #endif
    24. }
    25. /* read data */
    26. for (i = 0; i < SDRAM_SIZE / data_width; i++)
    27. {
    28. #if SDRAM_DATA_WIDTH == 8
    29. data = *(__IO uint8_t *)(SDRAM_BANK_ADDR + i * data_width);
    30. if (data != 0x55)
    31. {
    32. //ERROR
    33. break;
    34. }
    35. #elif SDRAM_DATA_WIDTH == 16
    36. data = *(__IO uint16_t *)(SDRAM_BANK_ADDR + i * data_width);
    37. if (data != 0x5555)
    38. {
    39. //ERROR
    40. break;
    41. }
    42. #else
    43. data = *(__IO uint32_t *)(SDRAM_BANK_ADDR + i * data_width);
    44. if (data != 0x55555555)
    45. {
    46. //ERROR
    47. break;
    48. }
    49. #endif
    50. }
    51. if (i >= SDRAM_SIZE / data_width)
    52. {
    53. //OK
    54. }
    55. }

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

            最后工程代码如下

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

            记得要三连哦 

             

             

            

  • 相关阅读:
    动态获取input框的值
    前后端接口设计与配置中心系统<二十九>-------HiAbility SDK开发1【 基础推送能力集成、OEM厂商推送能力集成】
    Spring6整合JUnit5
    JAVA多线程基础学习三:volatile关键字
    (1)Jupyter Notebook 下载及安装
    军品-鉴定文件清单
    动手学习深度学习 06:卷积神经网络
    SpringCloud——Http客户端Feign
    超算/先进计算如何改变现如今对的生活
    OpenCV-Python学习(3)—— OpenCV 图像色彩空间转换
  • 原文地址:https://blog.csdn.net/tianizimark/article/details/126335762