42. FSMC扩展SRAM

一、SRAM简介

  静态随机存取存储器(Static Random-Access Memory,SRAM)是随机存取存储器的一种。所谓的 “静态”,是指这种存储器只要保持通电,里面储存的数据就可以恒常保持。相对之下,动态随机存取存储器(DRAM)里面所储存的数据就需要周期性地更新。然而,当电力供应停止时,SRAM 储存的数据还是会消失(被称为 volatile memory),这与在断电后还能储存资料的 ROM 或闪存是不同的。

SRAM引脚图

二、SRAM读写流程

2.1、SRAM写时序

  FSMC 模式 A 读时序图:

FSMC模式A写操作时序图

  SRAM 写时序图:

SRAM写时序图

写时序时间要求

时间参数 IS62WV51216型号的时间要求 说明
\(t_{WC}\) 不小于 55ns 写操作的总时间,即\((ADDSET+DATAST+1) * t_{HCLK} > 55ns\)
\(t_{SA}\) 大于 0ns 从发送地址信号到给出写有使能信号的时间,即
\(ADDSET * t_{HCLK}> 0ns\)
\(t_{PWE}\) 不小于 40ns 从接收到写使能信号到数据采样的时间,即\((DATAST + 1) * t_{HCLK} > 40 ns\)

  对于 F4,\(t_{HCLK} = \frac{1}{168M}s ≈ 6ns\),综上考虑,ADDSET≥0,DATAST≥6。

2.2、SRAM读时序

  FSMC 模式 A 读时序图:

FSMC模式A读操作时序图

  SRAM 读时序图:

SRAM读时序图

读时序时间要求

  在读写时序中,有几个比较重要的时间参数,在使用STM32 控制的时候需要参考。

时间参数 IS62WV51216型号的时间要求 说明
\(t_{RC}\) 不小于 55ns 读操作的总时间,即\((ADDSET+DATAST) * t_{HCLK} > 55ns\)
\(t_{AA}\) 最迟不大于 55ns 从接收到地址信号到给出有效数据的时间,即\(ADDSET * t_{HCLK} < 55ns\)
\(t_{DOE}\) 最迟不大于 25ns 从接收到读使能信号到给出有效数据的时间,即\(DATAST * t_{HCLK} < 25ns\)

  对于 F4,\(t_{HCLK} = \frac{1}{168M}s ≈ 6ns\),综上考虑,ADDSET≤9,DATAST≤4。

三、FSMC驱动SRAM步骤

3.1、使能FSMC和相关GPIO时钟

  FSMC 时钟使能:

#define __HAL_RCC_FSMC_CLK_ENABLE()   do { \
                                      __IO uint32_t tmpreg = 0x00U; \
                                      SET_BIT(RCC->AHB3ENR, RCC_AHB3ENR_FSMCEN);\
                                      /* Delay after an RCC peripheral clock enabling */ \
                                      tmpreg = READ_BIT(RCC->AHB3ENR, RCC_AHB3ENR_FSMCEN);\
                                      UNUSED(tmpreg); \
                                      } while(0U)
#define __HAL_RCC_GPIOD_CLK_ENABLE()   do { \
                                        __IO uint32_t tmpreg = 0x00U; \
                                        SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIODEN);\
                                        /* Delay after an RCC peripheral clock enabling */ \
                                        tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIODEN);\
                                        UNUSED(tmpreg); \
                                      } while(0U)
#define __HAL_RCC_GPIOE_CLK_ENABLE()   do { \
                                        __IO uint32_t tmpreg = 0x00U; \
                                        SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOEEN);\
                                        /* Delay after an RCC peripheral clock enabling */ \
                                        tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOEEN);\
                                        UNUSED(tmpreg); \
                                      } while(0U)
#define __HAL_RCC_GPIOF_CLK_ENABLE()   do { \
                                       __IO uint32_t tmpreg = 0x00U; \
                                       SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOFEN);\
                                       /* Delay after an RCC peripheral clock enabling */ \
                                       tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOFEN);\
                                       UNUSED(tmpreg); \
                                       } while(0U)
#define __HAL_RCC_GPIOG_CLK_ENABLE()   do { \
                                       __IO uint32_t tmpreg = 0x00U; \
                                       SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOGEN);\
                                       /* Delay after an RCC peripheral clock enabling */ \
                                       tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOGEN);\
                                       UNUSED(tmpreg); \
                                       } while(0U)

3.2、配置FSMC工作参数

  设置 FSMC BANK1 区域 3 的相关寄存器。

HAL_StatusTypeDef HAL_SRAM_Init(SRAM_HandleTypeDef *hsram, FMC_NORSRAM_TimingTypeDef *Timing, FMC_NORSRAM_TimingTypeDef *ExtTiming);

  形参 hsramSRAM 句柄,它的定义如下:

typedef struct
{
  FMC_NORSRAM_TypeDef *Instance;            // FSMC寄存器基地址
  FMC_NORSRAM_EXTENDED_TypeDef *Extended;   // 扩展模式寄存器基地址
  FMC_NORSRAM_InitTypeDef Init;             // SRMA初始化结构体
  HAL_LockTypeDef Lock;                     // SRAM锁对象结构体
  __IO HAL_SRAM_StateTypeDef State;         // SRMA设备访问状态结构体
  DMA_HandleTypeDef *hdma;                  // DMA结构体
} SRAM_HandleTypeDef;

  Instance指向 FSMC 寄存器基地址。如果是 SRAM 设备,直接填写 FSMC_NORSRAM_DEVICE 标识符即可。

#define FSMC_NORSRAM_DEVICE             FSMC_Bank1

  Extended指向 FSMC 扩展模式寄存器基地址

#define FSMC_NORSRAM_EXTENDED_DEVICE    FSMC_Bank1E

  Init用于对 FSMC 的初始化配置。它是 FSMC_NORSRAM_InitTypeDef 结构体指针类型。它的定义如下:

typedef struct
{
    uint32_t NSBank;                // NORSRAM存储区块号
    uint32_t DataAddressMux;        // 地址/数据复用使能
    uint32_t MemoryType;            // 存储器类型
    uint32_t MemoryDataWidth;       // 存储器数据宽度
    uint32_t BurstAccessMode;       // 突发模式配置
    uint32_t WaitSignalPolarity;    // 设置等待信号的极性
    uint32_t WrapMode;              // 突发下存储器传输使能
    uint32_t WaitSignalActive;      // 等待状态之前或等待状态期间
    uint32_t WriteOperation;        // 存储器写使能
    uint32_t WaitSignal;            // 使能或者禁止通过等待信号来插入等待状态
    uint32_t ExtendedMode;          // 使能或者禁止使能扩展模式
    uint32_t AsynchronousWait;      // 用于异步传输期间,使能或者禁止等待信号
    uint32_t WriteBurst;            // 用于使能或者禁止异步的写突发操作
    uint32_t ContinuousClock;       // 使能或者禁止FSMC向外部存储器输出时钟
    uint32_t WriteFifo;             // 使能或者禁止写FIFO
    uint32_t PageSize;              // 设置页大小
} FSMC_NORSRAM_InitTypeDef;

  NSBank用来指定使用到的存储块区号

#define FSMC_NORSRAM_BANK1                       (0x00000000U)
#define FSMC_NORSRAM_BANK2                       (0x00000002U)
#define FSMC_NORSRAM_BANK3                       (0x00000004U)
#define FSMC_NORSRAM_BANK4                       (0x00000006U)

  DataAddressMux用来设置是否使能地址/数据复用,该变量仅对 NOR/PSRAM 有效。

#define FSMC_DATA_ADDRESS_MUX_DISABLE            (0x00000000U)
#define FSMC_DATA_ADDRESS_MUX_ENABLE             (0x00000002U)

  MemoryType用来设置存储器类型

#define FSMC_MEMORY_TYPE_SRAM                    (0x00000000U)
#define FSMC_MEMORY_TYPE_PSRAM                   (0x00000004U)
#define FSMC_MEMORY_TYPE_NOR                     (0x00000008U)

  MemoryDataWidth用来设置存储器数据总线宽度,可选 8 位、16 位还是 32 位。

#define FSMC_NORSRAM_MEM_BUS_WIDTH_8             (0x00000000U)
#define FSMC_NORSRAM_MEM_BUS_WIDTH_16            (0x00000010U)
#define FSMC_NORSRAM_MEM_BUS_WIDTH_32            (0x00000020U)

  BurstAccessMode是否使能突发访问,仅对同步突发存储器有效。

#define FSMC_BURST_ACCESS_MODE_DISABLE           (0x00000000U)
#define FSMC_BURST_ACCESS_MODE_ENABLE            (0x00000100U)

  WaitSignalPolarity等待信号的极性,仅在突发模式访问下有用。

#define FSMC_WAIT_SIGNAL_POLARITY_LOW            (0x00000000U)
#define FSMC_WAIT_SIGNAL_POLARITY_HIGH           (0x00000200U)

  WaitSignalActive存储器是在等待周期之前的一个时钟周期还是等待周期期间使能 NWAIT

#define FSMC_WAIT_TIMING_BEFORE_WS               (0x00000000U)
#define FSMC_WAIT_TIMING_DURING_WS               (0x00000800U)

  WriteOperation用来设置存储器写使能,也就是是否允许写入。

#define FSMC_WRITE_OPERATION_DISABLE             (0x00000000U)
#define FSMC_WRITE_OPERATION_ENABLE              (0x00001000U)

  WaitSignal等待使能位

#define FSMC_WAIT_SIGNAL_DISABLE                 (0x00000000U)
#define FSMC_WAIT_SIGNAL_ENABLE                  (0x00002000U)

  ExtendedMode用来设置是否使能扩展模式,也就是是否允许读写使用不同时序。

#define FSMC_EXTENDED_MODE_DISABLE               (0x00000000U)
#define FSMC_EXTENDED_MODE_ENABLE                (0x00004000U)

  AsynchronousWait是否使能同步传输模式下的等待信号

#define FSMC_ASYNCHRONOUS_WAIT_DISABLE           (0x00000000U)
#define FSMC_ASYNCHRONOUS_WAIT_ENABLE            (0x00008000U)

  WriteBurst是否禁止突发写

#define FSMC_WRITE_BURST_DISABLE                 (0x00000000U)
#define FSMC_WRITE_BURST_ENABLE                  (0x00080000U)

  形参 Timing 和形参 ExtTiming 都是 FSMC_NORSRAM_TimingTypeDef 结构体类型指针变量,其定义如下:

typedef struct
{
    uint32_t AddressSetupTime;          // 地址建立时间
    uint32_t AddressHoldTime;           // 地址保持时间
    uint32_t DataSetupTime;             // 数据建立时间
    uint32_t BusTurnAroundDuration;     // 总线周转阶段的持续时间
    uint32_t CLKDivision;               // CLK时钟输出信号的周期
    uint32_t DataLatency;               // 同步突发 NOR FLASH 的数据延迟
    uint32_t AccessMode;                // 异步模式配置
} FSMC_NORSRAM_TimingTypeDef;

  AddressSetupTime:用来 设置地址建立时间,可以理解为 RD/WR 的高电平时间。

  AddressHoldTime:用来 设置地址保持时间,模式 A 并没有用到。

  DataSetupTime:用来 设置数据建立时间,可以理解为 RD/WR 的低电平时间。

  BusTurnAroundDuration:用来 配置总线周转阶段的持续时间,NOR FLASH 用到。

  CLKDivision:用来 配置 CLK 时钟输出信号的周期,以 HCLK 周期数表示。若控制异步存储器,该参数无效。

  DataLatency:用来 设置同步突发 NOR FLASH 的数据延迟。若控制异步存储器,该参数无效。

  AccessMode:用来 设置异步模式

#define FSMC_ACCESS_MODE_A                       (0x00000000U)
#define FSMC_ACCESS_MODE_B                       (0x10000000U)
#define FSMC_ACCESS_MODE_C                       (0x20000000U)
#define FSMC_ACCESS_MODE_D                       (0x30000000U)

3.3、FSMC底层初始化

  HAL 库中,提供 HAL_GPIO_Init() 函数用于配置 GPIO 功能模式,初始化 GPIO。该函数的声明如下:

void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init);

  该函数的第一个形参 GPIOx 用来指定端口号,可选值如下:

#define GPIOD               ((GPIO_TypeDef *) GPIOD_BASE)
#define GPIOE               ((GPIO_TypeDef *) GPIOE_BASE)
#define GPIOF               ((GPIO_TypeDef *) GPIOF_BASE)
#define GPIOG               ((GPIO_TypeDef *) GPIOG_BASE)

  第二个参数是 GPIO_InitTypeDef 类型的结构体变量,用来设置 GPIO 的工作模式,其定义如下:

typedef struct
{
  uint32_t Pin;         // 引脚号
  uint32_t Mode;        // 模式设置
  uint32_t Pull;        // 上下拉设置
  uint32_t Speed;       // 速度设置
  uint32_t Alternate;   // 复用功能设置
}GPIO_InitTypeDef;

  成员 Pin 表示 引脚号,范围:GPIO_PIN_0 到 GPIO_PIN_15。

#define GPIO_PIN_0                 ((uint16_t)0x0001)  /* Pin 0 selected    */
#define GPIO_PIN_1                 ((uint16_t)0x0002)  /* Pin 1 selected    */
#define GPIO_PIN_2                 ((uint16_t)0x0004)  /* Pin 2 selected    */
#define GPIO_PIN_3                 ((uint16_t)0x0008)  /* Pin 3 selected    */
#define GPIO_PIN_4                 ((uint16_t)0x0010)  /* Pin 4 selected    */
#define GPIO_PIN_5                 ((uint16_t)0x0020)  /* Pin 5 selected    */
#define GPIO_PIN_6                 ((uint16_t)0x0040)  /* Pin 6 selected    */
#define GPIO_PIN_7                 ((uint16_t)0x0080)  /* Pin 7 selected    */
#define GPIO_PIN_8                 ((uint16_t)0x0100)  /* Pin 8 selected    */
#define GPIO_PIN_9                 ((uint16_t)0x0200)  /* Pin 9 selected    */
#define GPIO_PIN_10                ((uint16_t)0x0400)  /* Pin 10 selected   */
#define GPIO_PIN_11                ((uint16_t)0x0800)  /* Pin 11 selected   */
#define GPIO_PIN_12                ((uint16_t)0x1000)  /* Pin 12 selected   */
#define GPIO_PIN_13                ((uint16_t)0x2000)  /* Pin 13 selected   */
#define GPIO_PIN_14                ((uint16_t)0x4000)  /* Pin 14 selected   */
#define GPIO_PIN_15                ((uint16_t)0x8000)  /* Pin 15 selected   */
#define GPIO_PIN_All               ((uint16_t)0xFFFF)  /* All pins selected */

  成员 Mode 是 GPIO 的 模式选择,有以下选择项:

#define  GPIO_MODE_AF_PP                        0x00000002U     // 推挽式复用

  成员 Pull 用于 配置上下拉电阻,有以下选择项:

#define  GPIO_NOPULL        0x00000000U     // 无上下拉
#define  GPIO_PULLUP        0x00000001U     // 上拉
#define  GPIO_PULLDOWN      0x00000002U     // 下拉

  成员 Speed 用于 配置 GPIO 的速度,有以下选择项:

#define  GPIO_SPEED_FREQ_LOW         0x00000000U    // 低速
#define  GPIO_SPEED_FREQ_MEDIUM      0x00000001U    // 中速
#define  GPIO_SPEED_FREQ_HIGH        0x00000002U    // 高速
#define  GPIO_SPEED_FREQ_VERY_HIGH   0x00000003U    // 极速

  成员 Alternate 用于 配置具体的复用功能,不同的 GPIO 口可以复用的功能不同,具体可参考数据手册。

#define GPIO_AF12_FSMC          ((uint8_t)0x0C)  /* FSMC Alternate Function mapping                     */

四、原理图

SRAM模块

FSMC_NE3

FSMC_A0_A5

FSMC_A6_A15

FSMC_D0_D1_D13_D15_A16_A18

FSMC_D2_D3

FSMC_D4_D12

FSMC_NOE_NWE

FSMC_NBL0_NBL1

五、程序源码

/* SRAM基地址,根据SRAM_FSMC_NEX的设置来决定基址地址
 * 我们一般使用FSMC的块1(BANK1)来驱动SRAM, 块1地址范围总大小为256MB,均分成4块:
 * 存储块1(FSMC_NE1)地址范围: 0X6000 0000 ~ 0X63FF FFFF
 * 存储块2(FSMC_NE2)地址范围: 0X6400 0000 ~ 0X67FF FFFF
 * 存储块3(FSMC_NE3)地址范围: 0X6800 0000 ~ 0X6BFF FFFF
 * 存储块4(FSMC_NE4)地址范围: 0X6C00 0000 ~ 0X6FFF FFFF
 */
#define SRAM_FSMC_NEX           3
#define SRAM_BASE_ADDRESS       (0x60000000 + (0x4000000 * (SRAM_FSMC_NEX - 1)))

  SRAM 初始化函数:

/**
 * @brief SRAMc初始化
 * 
 */
void SRAM_Init(void)
{
    SRAM_GPIO_Init();
    FSMC_SRAM_Init();
}

  FSMC 驱动 SRAM 函数:

/**
 * @brief FSMC驱动SRMA初始化
 * 
 */
static void FSMC_SRAM_Init(void)
{
    SRAM_HandleTypeDef hsram = {0};   
    FSMC_NORSRAM_TimingTypeDef FSMC_NORSRAM_TimingStruct = {0};

    hsram.Instance = FSMC_NORSRAM_DEVICE;
    hsram.Extended = FSMC_NORSRAM_EXTENDED_DEVICE;

    hsram.Init.NSBank = (SRAM_FSMC_NEX == 1) ? FSMC_NORSRAM_BANK1 : \
                                 (SRAM_FSMC_NEX == 2) ? FSMC_NORSRAM_BANK2 : \
                                 (SRAM_FSMC_NEX == 3) ? FSMC_NORSRAM_BANK3 : 
                                                        FSMC_NORSRAM_BANK4;     // 根据配置选择FSMC_NE1~4
    hsram.Init.DataAddressMux = FSMC_DATA_ADDRESS_MUX_DISABLE;                  // 地址/数据线不复
    hsram.Init.MemoryType = FSMC_MEMORY_TYPE_SRAM;                              // SRAM
    hsram.Init.MemoryDataWidth = FSMC_NORSRAM_MEM_BUS_WIDTH_16;                 // 16位数据宽度
    hsram.Init.BurstAccessMode = FSMC_BURST_ACCESS_MODE_DISABLE;                // 是使能突发访问,仅对同步突发存储器有效
    hsram.Init.WaitSignalPolarity = FSMC_WAIT_SIGNAL_POLARITY_LOW;              // 等待信号的极性,仅在突发模式访问下有
    hsram.Init.WaitSignalActive = FSMC_WAIT_TIMING_BEFORE_WS;                   // 存储器是在等待周期之前的一个时钟周期还是等待周期期间使能NWAIT
    hsram.Init.WriteOperation = FSMC_WRITE_OPERATION_ENABLE;                    // 存储器写使能
    hsram.Init.WaitSignal = FSMC_WAIT_SIGNAL_DISABLE;                           // 等待使能位
    hsram.Init.ExtendedMode = FSMC_EXTENDED_MODE_DISABLE;                       // 读写使用相同的时序
    hsram.Init.AsynchronousWait = FSMC_ASYNCHRONOUS_WAIT_DISABLE;               // 是否使能同步传输模式下的等待信号
    hsram.Init.WriteBurst = FSMC_WRITE_BURST_DISABLE;                           // 禁止突发写
    hsram.Init.ContinuousClock = FSMC_CONTINUOUS_CLOCK_SYNC_ASYNC;

    FSMC_NORSRAM_TimingStruct.AccessMode = FSMC_ACCESS_MODE_A;                  // 模式A
    FSMC_NORSRAM_TimingStruct.AddressSetupTime = 0x00;                          // 地址建立时间(ADDSET
    FSMC_NORSRAM_TimingStruct.AddressHoldTime = 0x00;                           // 地址保持时间(ADDHLD)模式A未用到
    FSMC_NORSRAM_TimingStruct.DataSetupTime = 0x18;                             // 数据保存时间
    FSMC_NORSRAM_TimingStruct.BusTurnAroundDuration = 0x00;

    HAL_SRAM_Init(&hsram, &FSMC_NORSRAM_TimingStruct, &FSMC_NORSRAM_TimingStruct);
}

  SRAM 底层初始化函数:

/**
 * @brief FSMC驱动SRAM底层初始化
 * 
 */
static void SRAM_GPIO_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    __HAL_RCC_FSMC_CLK_ENABLE();

    __HAL_RCC_GPIOD_CLK_ENABLE();
    __HAL_RCC_GPIOE_CLK_ENABLE();
    __HAL_RCC_GPIOF_CLK_ENABLE();
    __HAL_RCC_GPIOG_CLK_ENABLE();

    /** FSMC GPIO Configuration
        PF0  ------> FSMC_A0    PF1  ------> FSMC_A1    PF2  ------> FSMC_A2
        PF3  ------> FSMC_A3    PF4  ------> FSMC_A4    PF5  ------> FSMC_A5
        PF12 ------> FSMC_A6    PF13 ------> FSMC_A7    PF14 ------> FSMC_A8
        PF15 ------> FSMC_A9  
  
        PG0  ------> FSMC_A10   PG1  ------> FSMC_A11   PG2  ------> FSMC_A12
        PG3  ------> FSMC_A13   PG4  ------> FSMC_A14   PG5  ------> FSMC_A15

        PG10 ------> FSMC_NE3

        PE7  ------> FSMC_D4    PE8  ------> FSMC_D5    PE9  ------> FSMC_D6
        PE10 ------> FSMC_D7    PE11 ------> FSMC_D8    PE12 ------> FSMC_D9
        PE13 ------> FSMC_D10   PE14 ------> FSMC_D11   PE15 ------> FSMC_D12

        PE0 ------> FSMC_NBL0   PE1  ------> FSMC_NBL1

        PD11 ------> FSMC_A16   PD12 ------> FSMC_A17   PD13 ------> FSMC_A18
  
        PD0  ------> FSMC_D2    PD1  ------> FSMC_D3    PD8  ------> FSMC_D13
        PD9  ------> FSMC_D14   PD10 ------> FSMC_D15   PD14 ------> FSMC_D0
        PD15 ------> FSMC_D1

        PD4  ------> FSMC_NOE   PD5  ------> FSMC_NWE   
    */
    // PF0~PF5 PF12~PF15
    GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | 
                            GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_12 | GPIO_PIN_13 |
                            GPIO_PIN_14 |GPIO_PIN_15;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF12_FSMC;
    HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);

    // PG0~PG5 PG10
    GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | 
                            GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_10;
    HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);

    // PE7~PE15 PE0~PE1
    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;
    HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

    // PD0~PD1 PD4~PD5 PD8~PD15
    GPIO_InitStruct.Pin = 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_PIN_4 | GPIO_PIN_5;
    HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
}

  往 SRAM 写数据函数:

/**
 * @brief 往SRAM指定地址写指定长度的数据
 * 
 * @param address 开始写入的地址
 * @param data 数据存储区
 * @param length 写入的字节数
 */
void SRAM_WriteData(uint32_t address, uint8_t *data, uint32_t length)
{
    for (; length != 0; length--)
    {
        *(volatile uint8_t *)(SRAM_BASE_ADDRESS + address) = *data++;
        address++;
    }
}

  从 SRAM 读取数据函数:

/**
 * @brief 从SRAM指定地址读取指定长度的数据
 * 
 * @param address 开始读取的地址
 * @param data 数据存储区
 * @param length 要读取的长度
 */
void SRAM_ReadData(uint32_t address, uint8_t *data, uint32_t length)
{
    for (; length != 0; length--)
    {
        *data++ = *(volatile uint8_t *)(SRAM_BASE_ADDRESS + address);
        address++;
    }
}

  main() 函数内容如下:

uint8_t a1 __attribute__((section(".ext_sram")));
uint32_t buffer[1024] __attribute__((section(".ext_sram")));

int main(void)
{
    uint8_t writeData[] = "Sakura";
    uint8_t readData[15] = {0};

    HAL_Init();
    System_Clock_Init(8, 336, 2, 7);
    Delay_Init(168);

    UART_Init(&g_usart1_handle, USART1, 115200);
    SRAM_Init();

    a1 = 10;
    printf("a1 address: %p, a1: %d\r\n", &a1, a1);

    printf("buffer address: %p\r\n", buffer);

    for (uint32_t i = 0; i < 1024; i++)
    {
        buffer[i] = i;
    }

    for (uint32_t i = 0; i < 1024; i++)
    {
        printf("%ld\t", buffer[i]);
    }
    printf("\r\n");

    SRAM_WriteData(100, writeData, sizeof(writeData));
    SRAM_ReadData(100, readData, sizeof(readData));
    printf("readData: %s\r\n", readData);

    while (1)
    {
  
    }
  
    return 0;
}

  如果使用的是 GCC 编译器的话,定义全局变量时,要想定义在外部 SRAM 中,需要修改 STM32F407ZGTx_FLASH.ld 文件的内容,给 GCC 添加外挂内存,这里我们起名字为:EXTSRAM。

/* Specify the memory areas */
MEMORY
{
...
EXTSRAM (rw)  : ORIGIN = 0x68000000, LENGTH = 1024
}

GCC添加外挂SRAM

  然后,添加 section 段:ext_sram(NOLOAD),注意 NOLOAD 的意思是储存在 ext_sram 段的内存变量,在启动的时候(就是 main() 之前的阶段)不需要初始化。这里容易出问题,标准 HAL 库是在进入 main() 才开始初始化(外挂 SRAM 需要调用自定义的 FSMC_SRAM_Init() 函数进行初始化后,才可以使用),不加 NOLOAD 的话,在启动阶段 GCC 默认加载内存变量(extern 或者 static 变量),找不到外挂 SRAM,导致 HardFault。

/* Define output sections */
SECTIONS
{
  ...
  /* external sram data, do not initialize at startup  */
  .ext_sram(NOLOAD) :
  {
    . = ALIGN(4);
    _sext_sram = .;        /* create a global symbol at ext_sram start */
    *(.ext_sram)
    *(.ext_sram*)
   
    . = ALIGN(4);
    _eext_sram = .;        /* define a global symbol at ext_sram end */
  } >EXTSRAM
  ...
}

添加section段

  然后,我们要想把全部变量定义在外部 SRAM 中时,需要通过如下方法定义:

uint8_t a1 __attribute__((section(".ext_sram")));
uint32_t buffer[1024] __attribute__((section(".ext_sram")));

  如果我们想把局部直接指定到外部 SRAM 中,可以通过指针的方式直接往 SRAM 对应的地址写数据即可。

*(uint32_t *)0x68000000 = 10;
data = *(uint32_t *)0x68000000;
posted @ 2024-01-11 19:26  星光樱梦  阅读(19)  评论(0编辑  收藏  举报