Nuru_Banmian
Nuru_Banmian
Published on 2025-08-05 / 90 Visits
0
0

FSMC-扩展外部SRAM-寄存器&HAL库实现

需求描述

​ 使用FSMC扩展外部SRAM。然后把内存数据存储到外部SRAM中。

​ STM32F1 系列的芯片不支持扩展SDRAM(STM32F429 系列支持),它仅支持使用 FSMC 外设扩展 SRAM。由于引脚数量的限制,只有 STM32F103ZE 或以上型号的芯片才可以扩展外部 SRAM。

SRAM芯片IS62WV51216

SRAM介绍

我们使用的SRAM型号为IS62WV51216,我们就以这个为例来介绍SRAM

FSMC-SRAM芯片介绍-芯片图

功能框图

FSMC-SRAM芯片介绍-功能框图

信号线

信号线

类型

说明

A0-A18

I

地址输入

I/O0-I/O7

I/O

低8位字节的数据输入输出信号

I/O8-I/O15

I/O

高8位字节的数据输入输出信号

$\overline{CS1}$和CS2

I

片选信号CS2高电平有效, $\overline{CS1}$低电平有效

$\overline{OE}$

I

输出使能信号,低电平有效

$\overline{WE}$

I

写使能信号,低电平有效

$\overline{UB}$

O

数据掩码信号,高位字节允许访问,低电平有效

$\overline{LB}$

O

数据掩码信号,低位字节允许访问,低电平有效

​ SRAM的控制比较简单,只要控制信号线使能访问,从地址线输入要访问的地址,即可从I/O数据线写入或读出数据。

几个重要的时间参数

这几个时间参数比较重要,设置FSMC参数的要用。

FSMC-SRAM芯片介绍-几个重要的时间参数-1

FSMC-SRAM芯片介绍-几个重要的时间参数-2

硬件电路设计

原理图中的芯片连接

FSMC-SRAM芯片介绍-原理图中的芯片连接

芯片的CS1使能引脚对应着PG10

  1. 芯片引脚连接情况

    FSMC-扩展外部SRAM-芯片引脚连接情况

  2. STM32F103ZET6引脚连接情况

    FSMC-扩展外部SRAM-STM32F103ZET6引脚连接情况

  3. 数据手册中的服用引脚说明

    FSMC-扩展外部SRAM-STM32F103ZET6引脚连接情况

地址映射范围

64MB:FSMC_Bank1_NORSRAM1:0X6000 0000 ~ 0X63FF FFFF
64MB:FSMC_Bank1_NORSRAM2:0X6400 0000 ~ 0X67FF FFFF
64MB:FSMC_Bank1_NORSRAM3:0X6800 0000 ~ 0X6BFF FFFF
64MB:FSMC_Bank1_NORSRAM4:0X6C00 0000 ~ 0X6FFF FFFF

NE3对应的地址范围就是0x6800 0000 ~ 0x6BFF FFFF

SRAM软件设计(寄存器)

存储块使能

代码

/* 4.1 存储器块使能 */
FSMC_Bank1->BTCR[4] |= FSMC_BCR3_MBKEN;

寄存器

FSMC-SRAM软件设计-存储块使能-1

FSMC-SRAM软件设计-存储块使能-2

存储器类型

代码

/* 4.2 设置存储器类型 00: SRAM ROM 01:PSRAM 10:NOR闪存*/
FSMC_Bank1->BTCR[4] &= ~FSMC_BCR3_MTYP;

寄存器

FSMC-SRAM软件设计-存储器类型-1

FSMC-SRAM软件设计-存储器类型-2

内存访问使能

代码

/* 4.3 闪存访问使能: 禁止访问闪存 */
FSMC_Bank1->BTCR[4] &= ~FSMC_BCR3_FACCEN;

寄存器

FSMC-SRAM软件设计-内存访问使能

地址数据总线复用

代码

/* 4.4 地址和数据总线复用: 不复用 */
FSMC_Bank1->BTCR[4] &= ~FSMC_BCR3_MUXEN;

寄存器

FSMC-SRAM软件设计-地址数据总线复用-1

FSMC-SRAM软件设计-地址数据总线复用-2

数据总线宽度

代码

/* 4.5 数据总线宽度 00:8位 01:16位*/
FSMC_Bank1->BTCR[4] &= ~FSMC_BCR3_MWID_1;
FSMC_Bank1->BTCR[4] |= FSMC_BCR3_MWID_0;

寄存器

FSMC-SRAM软件设计-数据总线宽度-1

FSMC-SRAM软件设计-数据总线宽度-2

写使能

代码

/* 4.6 写使能 */
FSMC_Bank1->BTCR[4] |= FSMC_BCR3_WREN;

寄存器

FSMC-SRAM软件设计-写使能-1

FSMC-SRAM软件设计-写使能-2

实现产生

代码

/* 5. 配置SRAM的时序参数 BTCR[5] 访问bank1 3区对应的BTR寄存器 */
/* 5.1 地址建立时间(时钟周期) 0:表示一个时钟周期。 对同步读写来说无效,永远是1个时钟周期*/
FSMC_Bank1->BTCR[5] &= ~FSMC_BTR3_ADDSET;
/* 5.2 地址保持时间(时钟周期) 对同步读写来说无效,永远是1个时钟周期*/
FSMC_Bank1->BTCR[5] &= ~FSMC_BTR3_ADDHLD;
/* 5.3 数据保持时间(时钟周期)数据在总线上的停留时间 SRAM要求不能低于50ns */
FSMC_Bank1->BTCR[5] &= ~FSMC_BTR3_DATAST; /* 对应的位置位0 */
FSMC_Bank1->BTCR[5] |= (71 << 8); /* 设置为1个us. 72个时钟周期 */

寄存器

FSMC-SRAM软件设计-实现产生-1

FSMC-SRAM软件设计-实现产生-2

FSMC-SRAM软件设计-实现产生-3

FSMC-SRAM软件设计-实现产生-4

​ 在访问FSMC的寄存器的时stm32f10x.h并没有给所有的寄存器起名字,而是用了一个数组存储了所有的寄存器。

​ 每个数组长度为8,表示一共存储了8个寄存器。

    typedef struct
    {
        __IO uint32_t BTCR[8];
    } FSMC_Bank1_TypeDef;

​ 这个8个寄存器是按照下面的顺序来存储的。

FSMC-SRAM软件设计-FSMC寄存器数组

​ 比如,你要找Bank1的3区的寄存器:FSMC_BCR3和FSMC_BTR3。它们分别对应BTCR[4]和BTCR[5]。

寄存器实现

fsmc.h

#ifndef __FSMC_H
#define __FSMC_H

#include "stm32f10x.h"

void FSMC_Init(void);

#endif

fsmc.c

看起来很多,其实大部分代码是用来设置引脚输入输出模式的。

#include "fsmc.h"

void FSMC_GPIO_Init(void);

void FSMC_Init(void)
{
    // 1. 开启时钟
    RCC->AHBENR |= RCC_AHBENR_FSMCEN;
    RCC->APB2ENR |= (RCC_APB2ENR_IOPDEN |
                     RCC_APB2ENR_IOPEEN |
                     RCC_APB2ENR_IOPFEN |
                     RCC_APB2ENR_IOPGEN);

    // 2. 配置GPIO工作模式
    FSMC_GPIO_Init();

    // 3. FSMC 配置 BCR3 - BTCR[4]
    // 3.1 存储块使能
    FSMC_Bank1->BTCR[4] |= FSMC_BCR3_MBKEN;

    // 3.2 设置存储器类型 00 - SRAM/ROM
    FSMC_Bank1->BTCR[4] &= ~FSMC_BCR3_MTYP;

    // 3.3 禁止访问闪存
    FSMC_Bank1->BTCR[4] &= ~FSMC_BCR3_FACCEN;

    // 3.4 数据宽度:16位 - 01
    FSMC_Bank1->BTCR[4] &= ~FSMC_BCR3_MWID_1;
    FSMC_Bank1->BTCR[4] |= FSMC_BCR3_MWID_0;

    // 3.5 地址数据线不复用
    FSMC_Bank1->BTCR[4] &= ~FSMC_BCR3_MUXEN;

    // 3.6 开启写使能
    FSMC_Bank1->BTCR[4] |= FSMC_BCR3_WREN;

    // 4. FSMC 配置 BTR3 - BTCR[5]
    // 4.1 地址建立时间 ADDSET
    FSMC_Bank1->BTCR[5] &= ~FSMC_BTR3_ADDSET;

    // 4.2 数据保持时间 DATAST
    FSMC_Bank1->BTCR[5] &= ~FSMC_BTR3_DATAST;
    FSMC_Bank1->BTCR[5] |= (71 << 8);
}

void FSMC_GPIO_Init(void)
{
    // 1. 地址线 A0~A18,复用推挽输出,CNF-10,MODE-11
    // 1.1 MODE = 11
    GPIOF->CRL |= (GPIO_CRL_MODE0 |
                   GPIO_CRL_MODE1 |
                   GPIO_CRL_MODE2 |
                   GPIO_CRL_MODE3 |
                   GPIO_CRL_MODE4 |
                   GPIO_CRL_MODE5);

    GPIOF->CRH |= (GPIO_CRH_MODE12 |
                   GPIO_CRH_MODE13 |
                   GPIO_CRH_MODE14 |
                   GPIO_CRH_MODE15);

    GPIOG->CRL |= (GPIO_CRL_MODE0 |
                   GPIO_CRL_MODE1 |
                   GPIO_CRL_MODE2 |
                   GPIO_CRL_MODE3 |
                   GPIO_CRL_MODE4 |
                   GPIO_CRL_MODE5);

    GPIOD->CRH |= (GPIO_CRH_MODE11 |
                   GPIO_CRH_MODE12 |
                   GPIO_CRH_MODE13);

    // 1.2 CNF = 10
    GPIOF->CRL |= (GPIO_CRL_CNF0_1 |
                   GPIO_CRL_CNF1_1 |
                   GPIO_CRL_CNF2_1 |
                   GPIO_CRL_CNF3_1 |
                   GPIO_CRL_CNF4_1 |
                   GPIO_CRL_CNF5_1);
    GPIOF->CRL &= ~(GPIO_CRL_CNF0_0 |
                    GPIO_CRL_CNF1_0 |
                    GPIO_CRL_CNF2_0 |
                    GPIO_CRL_CNF3_0 |
                    GPIO_CRL_CNF4_0 |
                    GPIO_CRL_CNF5_0);

    GPIOF->CRH |= (GPIO_CRH_CNF12_1 |
                   GPIO_CRH_CNF13_1 |
                   GPIO_CRH_CNF14_1 |
                   GPIO_CRH_CNF15_1);
    GPIOF->CRH &= ~(GPIO_CRH_CNF12_0 |
                    GPIO_CRH_CNF13_0 |
                    GPIO_CRH_CNF14_0 |
                    GPIO_CRH_CNF15_0);

    GPIOG->CRL |= (GPIO_CRL_CNF0_1 |
                   GPIO_CRL_CNF1_1 |
                   GPIO_CRL_CNF2_1 |
                   GPIO_CRL_CNF3_1 |
                   GPIO_CRL_CNF4_1 |
                   GPIO_CRL_CNF5_1);
    GPIOG->CRL &= ~(GPIO_CRL_CNF0_0 |
                    GPIO_CRL_CNF1_0 |
                    GPIO_CRL_CNF2_0 |
                    GPIO_CRL_CNF3_0 |
                    GPIO_CRL_CNF4_0 |
                    GPIO_CRL_CNF5_0);

    GPIOD->CRH |= (GPIO_CRH_CNF11_1 |
                   GPIO_CRH_CNF12_1 |
                   GPIO_CRH_CNF13_1);
    GPIOD->CRH &= ~(GPIO_CRH_CNF11_0 |
                    GPIO_CRH_CNF12_0 |
                    GPIO_CRH_CNF13_0);

    // 2. 数据线,复用推挽输出,CNF-10, MODE-11
    /* =============MODE=============== */
    GPIOD->CRL |= (GPIO_CRL_MODE0 |
                   GPIO_CRL_MODE1);
    GPIOD->CRH |= (GPIO_CRH_MODE8 |
                   GPIO_CRH_MODE9 |
                   GPIO_CRH_MODE10 |
                   GPIO_CRH_MODE14 |
                   GPIO_CRH_MODE15);

    GPIOE->CRL |= (GPIO_CRL_MODE7);
    GPIOE->CRH |= (GPIO_CRH_MODE8 |
                   GPIO_CRH_MODE9 |
                   GPIO_CRH_MODE10 |
                   GPIO_CRH_MODE11 |
                   GPIO_CRH_MODE12 |
                   GPIO_CRH_MODE13 |
                   GPIO_CRH_MODE14 |
                   GPIO_CRH_MODE15);

    /* =============CNF=============== */
    GPIOD->CRL |= (GPIO_CRL_CNF0_1 |
                   GPIO_CRL_CNF1_1);
    GPIOD->CRL &= ~(GPIO_CRL_CNF0_0 |
                    GPIO_CRL_CNF1_0);

    GPIOD->CRH |= (GPIO_CRH_CNF8_1 |
                   GPIO_CRH_CNF9_1 |
                   GPIO_CRH_CNF10_1 |
                   GPIO_CRH_CNF14_1 |
                   GPIO_CRH_CNF15_1);
    GPIOD->CRH &= ~(GPIO_CRH_CNF8_0 |
                    GPIO_CRH_CNF9_0 |
                    GPIO_CRH_CNF10_0 |
                    GPIO_CRH_CNF14_0 |
                    GPIO_CRH_CNF15_0);

    GPIOE->CRL |= (GPIO_CRL_CNF7_1);
    GPIOE->CRL &= ~(GPIO_CRL_CNF7_0);

    GPIOE->CRH |= (GPIO_CRH_CNF8_1 |
                   GPIO_CRH_CNF9_1 |
                   GPIO_CRH_CNF10_1 |
                   GPIO_CRH_CNF11_1 |
                   GPIO_CRH_CNF12_1 |
                   GPIO_CRH_CNF13_1 |
                   GPIO_CRH_CNF14_1 |
                   GPIO_CRH_CNF15_1);
    GPIOE->CRH &= ~(GPIO_CRH_CNF8_0 |
                    GPIO_CRH_CNF9_0 |
                    GPIO_CRH_CNF10_0 |
                    GPIO_CRH_CNF11_0 |
                    GPIO_CRH_CNF12_0 |
                    GPIO_CRH_CNF13_0 |
                    GPIO_CRH_CNF14_0 |
                    GPIO_CRH_CNF15_0);

    // 3. 控制信号,复用推挽输出,CNF-10, MODE-11
    GPIOD->CRL |= (GPIO_CRL_MODE4 | GPIO_CRL_MODE5);
    GPIOD->CRL |= (GPIO_CRL_CNF4_1 | GPIO_CRL_CNF5_1);
    GPIOD->CRL &= ~(GPIO_CRL_CNF4_0 | GPIO_CRL_CNF5_0);

    GPIOE->CRL |= (GPIO_CRL_MODE0 | GPIO_CRL_MODE1);
    GPIOE->CRL |= (GPIO_CRL_CNF0_1 | GPIO_CRL_CNF1_1);
    GPIOE->CRL &= ~(GPIO_CRL_CNF0_0 | GPIO_CRL_CNF1_0);

    GPIOG->CRH |= GPIO_CRH_MODE10;
    GPIOG->CRH |= GPIO_CRH_CNF10_1;
    GPIOG->CRH &= ~GPIO_CRH_CNF10_0;
}

main.c

#include "usart.h"
#include "fsmc.h"

// 方式一:使用关键字 __attribute__ at 指定全局变量的地址
uint8_t v1 __attribute__((at(0x68000000)));
uint8_t v2 __attribute__((at(0x68000004)));

uint16_t v3 = 30;

int main(void)
{
	// 1. 初始化
	USART_Init();
	FSMC_Init();

	printf("FSMC实验开始...\n");

	// 定义各种全局和局部变量,打印值和地址进行对比
	v1 = 10;
	v2 = 20;

	uint8_t v4 __attribute__((at(0x68000008)));
	v4 = 40;
	uint8_t v5 = 50;

	printf("v1 = %d, @%p\n", v1, &v1);
	printf("v2 = %d, @%p\n", v2, &v2);
	printf("v3 = %d, @%p\n", v3, &v3);
	printf("v4 = %d, @%p\n", v4, &v4);
	printf("v5 = %d, @%p\n", v5, &v5);

	// 方法二:定义指针,用地址赋值
	uint8_t * p = (uint8_t *)0x68000001;
	*p = 100;

	printf("*p = %d, @%p\n", *p, p);

	while (1)
	{
	}
}

HAL库实现

FSMC-SRAM软件设计-HAL库设置

main.c

/* USER CODE BEGIN PV */
// 方式一:使用关键字 __attribute__ at 指定全局变量的地址
uint8_t v1 __attribute__((at(0x68000000)));
uint8_t v2 __attribute__((at(0x68000004)));

uint16_t v3 = 30;
/* USER CODE END PV */

/* USER CODE BEGIN 2 */
printf("FSMC实验开始...\n");

// 定义各种全局和局部变量,打印值和地址进行对比
v1 = 10;
v2 = 20;

uint8_t v4 __attribute__((at(0x68000008)));
v4 = 40;
  uint8_t v5 = 50;

printf("v1 = %d, @%p\n", v1, &v1);
printf("v2 = %d, @%p\n", v2, &v2);
printf("v3 = %d, @%p\n", v3, &v3);
printf("v4 = %d, @%p\n", v4, &v4);
printf("v5 = %d, @%p\n", v5, &v5);

// 方法二:定义指针,用地址赋值
uint8_t *p = (uint8_t *)0x68000001;
*p = 100;

printf("*p = %d, @%p\n", *p, p);
/* USER CODE END 2 */


Comment