STM32进阶 FSMC应用案例:扩展外部 SRAM

需求描述

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

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

IS62WV51216

19根地址线

16根数据线

fsmc_sram.h

cs 复制代码
#ifndef __FSMC_SRAM_H__
#define __FSMC_SRAM_H__

#include "stm32f10x.h"

void FSMC_SramInit(void);

void FSMC_SramGPIOInit(void);

#endif /* __FSMC_SRAM_H__ */

fsmc_sram.c

cs 复制代码
#include "fsmc_sram.h"


void FSMC_SramInit(void){
    // 0. 先进行GPIO的初始化
    FSMC_SramGPIOInit();
    // 1. 先放开FSMC的时钟
    RCC->AHBENR |= RCC_AHBENR_FSMCEN;

    // 对于BCR 看着表来配
    // 位19 突发访问 异步模式 没用 不需要配
    // 位18:16 CRAM页大小 SRAM MODE1模式下 不需要配置
    // 位15 异步等待 需要等待引脚 此处我们的SRAM不支持 因此应该配0 默认就是0
    // 位14 拓展模式 一旦开了 可以使用模式B C 等去操作FLASH 因为当前是SRAM模式1 此处必须为0 默认就是0
    // 位13 等待使能 异步模式下没用
    // 位12 写使能 要求是按需配置 我们想要复制 必须配1 不过默认就是1
    FSMC_Bank1->BTCR[4] |= FSMC_BCR3_WREN;
    // 位11 WAITCFG 无所谓
    // 位10 WRAPMOD 跟突发访问相关 文档要求必须配0 默认就是0s
    // 位9  WAITPOL 也是跟等待相关的 需要位15为1时 才有意义 但是现在位15是0 不需要管
    // 位8  BURSTEN 突发访问模式的使能位 文档要求必须为0 默认就是0
    // 位7 是一个默认值为1的保留位 不需要修改
    // 位6 FACCEN falsh访问使能 文档说不用管 但是我们可以试试
    FSMC_Bank1->BTCR[4] |= FSMC_BCR3_FACCEN;
    // 位5:4  外部存储器 数据总线的宽度 16位 默认就是 文档要求按需配置
    FSMC_Bank1->BTCR[4] &= ~FSMC_BCR3_MWID_1;
    FSMC_Bank1->BTCR[4] |= FSMC_BCR3_MWID_0;
    // 位3: 2  存储器类型 默认是flash 文档要求按需配置 我们配成00 表示SRAM
    FSMC_Bank1->BTCR[4] &= ~FSMC_BCR3_MTYP;
    // 位1 地址数据复用使能 默认是1 文档要求必须配成0
    FSMC_Bank1->BTCR[4] &= ~FSMC_BCR3_MUXEN;
    // 位0 对应bank的使能
    FSMC_Bank1->BTCR[4] |= FSMC_BCR3_MBKEN;



    /* 3. fsmc的 时序 */
    /* 3.1 地址建立时间 对同步读写来说,永远一个周期 */
    FSMC_Bank1->BTCR[5] &= ~FSMC_BTR3_ADDSET;
    /* 3.2 地址保持时间 对同步读写来说,永远一个周期 */

    /* 3.3 数据保持时间 手册不能低于55ns 我们设置1us*/
    FSMC_Bank1->BTCR[5] &= ~FSMC_BTR3_DATAST;
    FSMC_Bank1->BTCR[5] |= (2 << 8);
}


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


/* 1 配置 A0-A18 地址端口的输出模式 复用推挽输出CNF:10 50MHz速度 MODE:11*/
  /* =============MODE=============== */
  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);

  /* =============CNF=============== */
  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 数据端口 复用推挽输出
      在实际应用中,即使数据线被配置为输出模式,FSMC控制器仍然能够管理数据线的方向,使其在需要时成为输入线。
      这种自动切换是由FSMC控制器硬件管理的,不需要软件干预。
      因此,即使GPIO配置为复用推挽输出,FSMC依然可以实现读取操作。
  */
  /* =============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 其他控制端口 复用推挽输出 */
  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

cs 复制代码
#include "usart1.h"
#include "string.h"
#include <stdio.h>
#include "systick.h"
#include "fsmc_sram.h"

//0.声明一个变量 但是声明不是初始化
uint8_t v1 __attribute__((at(0x68000000)));
//1. 演示4个字节对齐
uint8_t v2 __attribute__((at(0x68000004)));
//3 一旦地址重复,编译器会自动为你安排一个没有分配过的地址
uint8_t v4 __attribute__((at(0x68000004)));

int main(void){
	Usart1_Init();
	FSMC_SramInit();
	
	// v1 = 4;
	// printf("v1_addr = %x\n",&v1);
	// printf("v1=%d\n",v1);

	// v2 = 100;
	// printf("v2_addr = %x\n",&v2);
	// printf("v2=%d\n",v2);

	
	// //2 attribute这个关键字它是编译器指令 
	// //只能在全局变量中使用 
	// //在局部变量使用会直接失效
	// uint8_t v3 __attribute__((at(0x68000004)));
	// v3 = 232;
	// printf("v3_addr = %x\n",&v3);
	// printf("v3=%d\n",v3);

	// v4 = 123;
	// printf("v4_addr = %x\n",&v4);
	// printf("v4=%d\n",v4);

	//4.直接通过地址访问sram 的值
	*(uint8_t *)(0x68000000) = 0x10;
	printf("00 = %x\n",*(uint8_t *)(0x68000000));

	*(uint8_t *)(0x68000001) = 0x33;
	printf("01 = %x\n",*(uint8_t *)(0x68000001));

	printf("16 00 = %x\n",*(uint16_t *)(0x68000000));

	while (1)
	{
	}
		
}
相关推荐
猿~~~1 小时前
STM32的HAL库开发---多通道ADC采集(DMA读取)实验
stm32·单片机·嵌入式硬件
Freak嵌入式2 小时前
开源一款I2C电机驱动扩展板-FreakStudio多米诺系列
嵌入式硬件·嵌入式·智能硬件·开源硬件·micropython·电机驱动·电子模块
kongba0073 小时前
Cursor提示词模板,开发GD32,C语言开发GD32 ARM单片机编程规范提示词 大厂风格代码规范
c语言·arm开发·单片机
LaoZhangGong1233 小时前
STM32的“Unique device ID“能否修改?
c语言·经验分享·stm32·单片机·嵌入式硬件
1101 11015 小时前
STM32-心知天气项目
stm32·单片机·嵌入式硬件
Ronin-Lotus5 小时前
嵌入式硬件篇---数字电子技术中的触发器
嵌入式硬件·fpga开发·触发器·数字电子技术·上位机知识
Ronin-Lotus5 小时前
嵌入式硬件篇---数字电子技术中的时序逻辑
单片机·嵌入式硬件·蓝桥杯·时序分析·数字电子技术
sinat_3607048215 小时前
STM32 看门狗
stm32·单片机·嵌入式硬件
亿道电子Emdoor15 小时前
【ARM】MDK如何生成指定大小的bin文件,并指定空区域的填充数据
arm开发·stm32·单片机
mftang16 小时前
STM32 CAN过滤器配置和应用方法介绍
stm32·单片机·嵌入式硬件