需求描述
使用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)
{
}
}