这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » STM32 » 【转载】STM32外扩SRAM及用法--from森

共4条 1/1 1 跳转至

【转载】STM32外扩SRAM及用法--from森

工程师
2024-10-20 19:54:01     打赏

一.概述

     一般单片机有片内的RAM,但都不多,比如:STM32F407ZGT6 自带了 192K 字节的 RAM,对一般应用来说,已经足够了,不过在一些对内存要求高的场合,

比如做华丽效果的 GUI,处理大量数据的应用等,STM32 自带的这些内存就可能不太够用了。好在嵌入式方案提供了扩展芯片 RAM 的方法,使用 SRAM 芯片,并

驱动这个外部 SRAM 提供程序需要的一部分RAM 空间即可。

1.器的分类1.png

2.嵌入式程序主要对应ROM 和 RAM这两种存储器。对于 RAM,目前常见的是 SRAM 和 DRAM,它们因工作方式不同而得名,它们主要有以下的特性:2.png

二.SRAM芯片与国产替代

1.IS62WV51216 是 ISSI(Integrated Silicon Solution, Inc)公司生产的一颗 16 位宽 512K(512*16,即 1M 字节)容量的 CMOS 静态内存芯片。该芯片具有如下几个特点:

高速。具有 45ns/55ns 访问速度。低功耗。TTL 电平兼容。全静态操作。不需要刷新和时钟电路。三态输出。字节控制功能。支持高/低字节控制。

2.国产替代一直是国内嵌入式领域的一个话题,国产替代的优势一般是货源稳定,售价更低,也有专门研发对某款芯片作 Pin to Pin 兼容的厂家,使用时无需修改 PCB,直接更换元件即可,十分方便。

一款替代 IS62WV51216 的芯片是 XM8A5121,它与IS62WV51216 一样采用 TSOP44 封装,引脚顺序也与前者完全一致。

XM8A51216 是星忆存储生产的一颗 16 位宽 512K(512*16,即 1M 位)容量的 CMOS 静态内存芯片。采用异步 SRAM 接口并结合独有的 XRAM 免刷新专利技术,在大容量、

高性能和高可靠及品质方面完全可以匹敌同类 SRAM,具有较低功耗和低成本优势,可以与市面上同类型 SRAM 产品硬件完全兼容,并且满足各种应用系统对高性能和低成本的要求,XM8A51216也

可以当做异步 SRAM 使用,该芯片具有如下几个特点:

高速,具有最高访问速度 10/12ns。低功耗。TTL 电平兼容。全静态操作。不需要刷新和时钟电路。三态输出。字节控制功能。支持高/低字节控制。该芯片与 IS62WV51216 引脚和完全兼容,控制时序也类似,大家可以方便地直接替换。

三.FSMC介绍

FSMC 接口可以通过地址信号,快速地找到存储器对应存储块上的数据。STM32F407 的 FSMC接口支持包括 SRAM、NAND FLASH、NOR FLASH 和 PSRAM 等存储器。F4 系列的大容量型号,

且引脚数目在 100 脚及以上的 STM32F407 芯片都带有 FSMC 接口, STM32F407ZGT6是带有 FSMC 接口的。

3.png

4.png

5.png

四.使用 SRAM 的配置步骤:

1)使能 FSMC 时钟,并配置 FSMC 相关的 IO 及其时钟使能。

要使用 FSMC,当然首先得开启其时钟。然后需要把 FSMC_D0~15,FSMCA0~18 等相关

IO 口,全部配置为复用输出,并使能各 IO 组的时钟。

使能 FSMC 时钟的方法前面 LCD 实验已经讲解过,方法为:

 __HAL_RCC_FSMC_CLK_ENABLE();

配置 IO 口为复用输出的关键行代码为:

 gpio_init_struct.Mode = GPIO_MODE_AF_PP; /* 复用推挽输出 */

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

此部分包括设置区域 3 的存储器的工作模式、位宽和读写时序等。本章我们使用模式 A、

16 位宽,读写共用一个时序寄存器。

这个是通过调用函数 HAL_SRAM_Init 来实现的,函数原型为:

HAL_StatusTypeDef HAL_SRAM_Init(SRAM_HandleTypeDef *hsram,

FSMC_NORSRAM_TimingTypeDef *Timing, FSMC_NORSRAM_TimingTypeDef *ExtTiming)

通过以上几个步骤,我们就完成了 FSMC 的配置,初始化 FSMC 后就可以访问 SRAM 芯

片时行读写操作了,这里还需要注意,因为我们使用的是 BANK1 的区域 3,所以

HADDR[27:26]=10,故外部内存的首地址为 0X68000000。

五.SRAM 驱动

核心代码:

#define SRAM_WR_GPIO_PORT GPIOD

#define SRAM_WR_GPIO_PIN GPIO_PIN_5

#define SRAM_WR_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOD_CLK_ENABLE();}while(0)

#define SRAM_RD_GPIO_PORT GPIOD

#define SRAM_RD_GPIO_PIN GPIO_PIN_4

#define SRAM_RD_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOD_CLK_ENABLE(); }while(0)

/* SRAM_CS(需要根据 SRAM_FSMC_NEX 设置正确的 IO 口) 引脚 定义 */

#define SRAM_CS_GPIO_PORT GPIOG

#define SRAM_CS_GPIO_PIN GPIO_PIN_10

#define SRAM_CS_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOG_CLK_ENABLE();}while(0)

根据 STM32F4 参考手册,SRAM 可以选择 FSMC 对应的存储块 1 上的 4 个区域之一作为

访问地址,它上面有四块相互独立的 64M 的连续寻址空间,为了能灵活根据不同的计算出使用

的地址空间,我们定义了以下的宏:

/* FSMC 相关参数 定义

* 注意: 我们默认是通过 FSMC 块 3 来连接 SRAM, 块 1 有 4 个片选: FSMC_NE1~4

*

* 修改 SRAM_FSMC_NEX, 对应的 SRAM_CS_GPIO 相关设置也得改

*/

#define SRAM_FSMC_NEX 3 /* 使用 FSMC_NE3 接 SRAM_CS,取值范围只能是: 1~4 */

/*****************************************************************/

/* 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_BASE_ADDR (0X60000000 + (0X4000000 * (SRAM_FSMC_NEX - 1)))

上述定义 SRAM_FSMC_NEX 的值为 3,即使用 FSMC 存储块 1 的第 3 个地址范围,上面

的 SRAM_BASE_ADDR 则根据我们使用的存储块计算出 SRAM 空间的首地址,存储块 3 对应

的是 0X68000000 ~ 0X6BFFFFFF 的地址空间。

sram 的初始化函数我们编写如下:

/**

* @brief 初始化 外部 SRAM

* @param 无

* @retval 无

*/

void sram_init(void)

{

 GPIO_InitTypeDef GPIO_Initure;

 FSMC_NORSRAM_TimingTypeDef fsmc_readwritetim;

 SRAM_CS_GPIO_CLK_ENABLE(); /* SRAM_CS 脚时钟使能 */

 SRAM_WR_GPIO_CLK_ENABLE(); /* SRAM_WR 脚时钟使能 */

 SRAM_RD_GPIO_CLK_ENABLE(); /* SRAM_RD 脚时钟使能 */

 __HAL_RCC_FSMC_CLK_ENABLE(); /* 使能 FSMC 时钟 */

761

STM32F407 开发指南

正点原子探索者 STM32F407 开发板教程

 __HAL_RCC_GPIOD_CLK_ENABLE(); /* 使能 GPIOD 时钟 */

 __HAL_RCC_GPIOE_CLK_ENABLE(); /* 使能 GPIOE 时钟 */

 __HAL_RCC_GPIOF_CLK_ENABLE(); /* 使能 GPIOF 时钟 */

 __HAL_RCC_GPIOG_CLK_ENABLE(); /* 使能 GPIOG 时钟 */

 GPIO_Initure.Pin = SRAM_CS_GPIO_PIN;

 GPIO_Initure.Mode = GPIO_MODE_AF_PP;

 GPIO_Initure.Pull = GPIO_PULLUP;

 GPIO_Initure.Speed = GPIO_SPEED_FREQ_HIGH;

 HAL_GPIO_Init(SRAM_CS_GPIO_PORT, &GPIO_Initure); /* SRAM_CS 引脚模式设置 */

 GPIO_Initure.Pin = SRAM_WR_GPIO_PIN;

 HAL_GPIO_Init(SRAM_WR_GPIO_PORT, &GPIO_Initure); /* SRAM_WR 引脚模式设置 */

 GPIO_Initure.Pin = SRAM_RD_GPIO_PIN;

 HAL_GPIO_Init(SRAM_RD_GPIO_PORT, &GPIO_Initure); /* SRAM_CS 引脚模式设置 */

 /* PD0,1,4,5,8~15 */

GPIO_Initure.Pin = GPIO_PIN_0 | GPIO_PIN_1 | 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_Initure.Mode = GPIO_MODE_AF_PP; /* 推挽复用 */

 GPIO_Initure.Pull = GPIO_PULLUP; /* 上拉 */

 GPIO_Initure.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */

 HAL_GPIO_Init(GPIOD, &GPIO_Initure);

 /* PE0,1,7~15 */

GPIO_Initure.Pin = GPIO_PIN_0 | GPIO_PIN_1 | 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;

 HAL_GPIO_Init(GPIOE, &GPIO_Initure);

 /* PF0~5,12~15 */

GPIO_Initure.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;

 HAL_GPIO_Init(GPIOF, &GPIO_Initure);

 /* PG0~5,10 */

GPIO_Initure.Pin = GPIO_PIN_0 | GPIO_PIN_1 |

GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5;

 HAL_GPIO_Init(GPIOG, &GPIO_Initure);

 g_sram_handler.Instance = FSMC_NORSRAM_DEVICE;

 g_sram_handler.Extended = FSMC_NORSRAM_EXTENDED_DEVICE;

 g_sram_handler.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 */

 /* 地址/数据线不复用 */

 g_sram_handler.Init.DataAddressMux = FSMC_DATA_ADDRESS_MUX_DISABLE;

g_sram_handler.Init.MemoryType = FSMC_MEMORY_TYPE_SRAM; /* SRAM */

/* 16 位数据宽度 */

g_sram_handler.Init.MemoryDataWidth = SMC_NORSRAM_MEM_BUS_WIDTH_16;

/* 是否使能突发访问,仅对同步突发存储器有效,此处未用到 */

g_sram_handler.Init.BurstAccessMode = FSMC_BURST_ACCESS_MODE_DISABLE;

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

g_sram_handler.Init.WaitSignalPolarity = FSMC_WAIT_SIGNAL_POLARITY_LOW;

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

g_sram_handler.Init.WaitSignalActive = FSMC_WAIT_TIMING_BEFORE_WS;

/* 存储器写使能 */

g_sram_handler.Init.WriteOperation = FSMC_WRITE_OPERATION_ENABLE;

/* 等待使能位,此处未用到 */

g_sram_handler.Init.WaitSignal = FSMC_WAIT_SIGNAL_DISABLE;

/* 读写使用相同的时序 */

g_sram_handler.Init.ExtendedMode = FSMC_EXTENDED_MODE_DISABLE;

/* 是否使能同步传输模式下的等待信号,此处未用到 */

 g_sram_handler.Init.AsynchronousWait = FSMC_ASYNCHRONOUS_WAIT_DISABLE;

 g_sram_handler.Init.WriteBurst = FSMC_WRITE_BURST_DISABLE; /* 禁止突发写 */

/* FSMC 读时序控制寄存器 */

/* 地址建立时间(ADDSET)为 2 个 HCLK 1/168M=6ns*2=12ns */

 fsmc_readwritetim.AddressSetupTime = 0x02;

 fsmc_readwritetim.AddressHoldTime = 0x00;/* 地址保持时间(ADDHLD)模式 A 未用到 */

 fsmc_readwritetim.DataSetupTime = 0x08; /* 数据保存时间为 8 个 HCLK=6ns*8=48ns */

 fsmc_readwritetim.BusTurnAroundDuration = 0X00;

 fsmc_readwritetim.AccessMode = FSMC_ACCESS_MODE_A; /* 模式 A */

 HAL_SRAM_Init(&g_sram_handler,&fsmc_readwritetim,&fsmc_readwritetim);

}

初始化成功后,FSMC 控制器就能根据扩展的地址线访问 SRAM 的数据,于是我们可以直


接根据地址指针来访问 SRAM,我们定义 SRAM 的写函数如下;

void sram_write(uint8_t *pbuf, uint32_t addr, uint32_t datalen)

{

 for (; datalen != 0; datalen--)

 {

 *(volatile uint8_t *)(SRAM_BASE_ADDR + addr) = *pbuf;

 addr++;

 pbuf++;

 }

}

同样地,也是利用地址,可以构造出一个 SRAM 的连续读函数:

void sram_read(uint8_t *pbuf, uint32_t addr, uint32_t datalen)

{

 for (; datalen != 0; datalen--)

 {

 *pbuf++ = *(volatile uint8_t *)(SRAM_BASE_ADDR + addr);

 addr++;

 }

}

来源: 整理文章为传播相关技术,网络版权归原作者所有,如有侵权,请联系删除。



                           

                        



                          

                        




院士
2024-11-07 07:02:37     打赏
2楼

谢谢楼主分享~!


助工
2024-11-07 07:53:06     打赏
3楼

谢谢分享


高工
2024-11-07 09:03:33     打赏
4楼

谢谢分享


共4条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]