一、STM32的系统框架
STM32F103 采用的是 Cortex-M3 内核,内核即 CPU,由 ARM 公司设计。ARM 公司并不生产芯片,而是出售其芯片技术授权。芯片生产厂商(SOC)如 ST、TI、NXP 等,负责在内核之外设计部件并生产整个芯片,这些内核之外的部件被称为核外外设或片上外设。如 GPIO、USART(串口)、I2C、SPI 等都叫做片上外设。
STM32F103 的系统主要由:4 个 驱动单元(可以主动发起通信)和 4 个 被动单元(只能被驱动工作)组成;
驱动单元 | 被动单元 |
---|---|
CortexM3 内核 DCode 总线(D-Bus) | 内部 Flash |
CortexM3 内核系统总线(S-Bus) | 内部 SRAM |
通用 DMA1 | FSMC |
通用 DMA2 | AHB 到 APB 的桥,所连接的所有 APB 设备 |
这里的驱动/被动单元都是指连接了总线矩阵的部分,未连接总线矩阵的部分,则不算作驱动/被驱动单元。
【1】、ICode总线(I-Bus)
ICode 中的 I 表示 Instruction,即指令。我们写好的程序经过编译之后都是一条条指令,存放在 Flash 中,内核要读取这些指令来执行程序就必须通过 ICode 总线,它几乎每时每刻都需要被使用,它是专门用来取指的。由于该总线功能单一,并没有直接连接到总线矩阵,因此被排除在驱动单元之外。
【2】、DCode总线(D-Bus)
DCode 中的 D 表示 Data,即数据,那说明这条总线是用来取数的。我们在写程序的时候,数据有常量和变量两种,常量就是固定不变的,用 C 语言中的 const 关键字修饰,是放到内部的 Flash 当中的,变量是可变的,不管是全局变量还是局部变量都放在内部的 SRAM。因为数据可以被 DCode 总线和 DMA 总线访问,所以为了避免访问冲突,在取数的时候需要经过一个总线矩阵来仲裁,决定哪个总线在取数。
【3】、系统总线
系统总线连接所有外设(如:GPIO、SPI、IIC、TIM等),用于控制各种外设工作,我们通常说的寄存器编程,即读写寄存器都是通过这根系统总线来完成的。
【4】、DMA 总线
DMA 总线也主要是用来传输数据,这个数据可以是在某个外设的数据寄存器,可以在 SRAM,可以在内部的 Flash。因为数据可以被 DCode 总线和 DMA 总线访问,所以为了避免访问冲突,在取数的时候需要经过一个总线矩阵来仲裁,决定哪个总线在取数。DMA 是直接存储访问控制器,可以实现数据的自动搬运,整个过程不需要 CPU 处理。
【5】、内部 Flash
内部的闪存存储器即 Flash,我们编写好的程序就放在这个地方。CPU 通过 ICode 总线经 Flash 接口访问内部 Flash,Flash 最高访问速度是 24MHz,因此以 72M 速度访问时,需要插入 2 个时钟周期延迟。
【6】、内部 SRAM
内部的 SRAM,即我们通常说的 RAM,程序的变量,堆栈等的开销都是基于内部的 SRAM。内部 SRAM 用于数据存储,直接挂载在总线矩阵上面,CPU 通过 DCode 总线实现 0 等待延时访问 SRAM,最快总线频率可达 72MHz,从而保证高效高速的访问内存。
【7】、FSMC
FSMC 的英文全称是 Flexible static memory controller,叫灵活的静态的存储器控制器。通过 FSMC,我们可以扩展内存,如外部的 SRAM,NAND-Flash 和 NORFlash。但有一点我们要注意的是,FSMC 只能扩展静态的内存,即名称里面的 S:static,不能是动态的内存,比如 SDRAM 就不能扩展。它实际上就是一个外部总线接口,也是直接挂在总线矩阵上面的,以方便 CPU 快速访问外挂器件。
【8】、AHB/APB桥
AHB 总线连接总线矩阵,同时通过 2 个 APB 桥连接 APB1 和 APB2,AHB 总线速度最大为 72MHz,APB2 总线速度最大也是 72MHz,但是 APB1 总线速度最大只能是 36MHz。这三个总线上面挂载了 STM32 内部绝大部分外设。
【9】、总线矩阵
总线矩阵协调内核系统总线和 DMA 主控总线之间的访问仲裁,仲裁利用轮换算法,保证各个总线之间的有序访问,从而确保工作正常。
二、存储器映射
STM32 是一个 32 位单片机,它有 32 根地址线,每个地址线有两个状态(导通或者不导通)。单片机内存地址的访问的存储单元是按地址编址的(而不是 bit)。因此,STM32 可以很方便的访问 4GB 以内的存储空间(2^32 = 4GB,即 0x0000 0000 ~ 0xffff ffff)。Cortex M3 内核的所有结构,包括:Flash、SRAM、外设及相关寄存器等全部组织在同一个 4GB 的线性地址空间内,我们可以通过 C 语言 来访问这些地址空间,从而操作相关外设(读/写)。数据字节以小端格式(小端模式)存放在存储器中,数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中。
存储器指可以存储数据的设备,它本身是没有地址信息的,我们对存储器分配地址的过程就叫 存储器映射。这个分配一般由芯片厂商做好了,ST 将所有的存储器及外设资源都映射在一个 4GB 的地址空间上(8 个块),从而可以通过访问对应的地址,访问具体的外设。如果给寄存器在分配一个地址就叫 存储器重映射。
ST 将 4GB 空间分成 8 个块,每个块 512MB,如上图所示,从图中我们可以看出有很多保留区域(Reserved),这是因为一般的芯片制造厂家是不可能把 4GB 空间用完的,同时,为了方便后续型号升级,会将一些空间预留(Reserved)。STM32 存储块功能及地址范围如下:
存储块 | 功能 | 地址范围 |
---|---|---|
Block 0 | Code | 0X0000 0000 ~ 0x1FFF FFFF(512MB) |
Block 1 | SRAM | 0X2000 0000 ~ 0x3FFF FFFF(512MB) |
Block 2 | 片上外设 | 0X4000 0000 ~ 0x5FFF FFFF(512MB) |
Block 3 | FSMC Bank1&2 | 0X6000 0000 ~ 0x7FFF FFFF(512MB) |
Block 4 | FSMC Bank3&4 | 0X8000 0000 ~ 0x9FFF FFFF(512MB) |
Block 5 | FSMC 寄存器 | 0XA000 0000 ~ 0xBFFF FFFF(512MB) |
Block 6 | 没用到 | 0XC000 0000 ~ 0xDFFF FFFF(512MB) |
Block 7 | Cortex M3 内部外设 | 0XE000 0000 ~ 0xFFFF FFFF(512MB) |
第一个块是 Block 0,用于存储代码,即 FLASH 空间,其功能划分如下:
存储器 | 功能 | 地址范围 |
---|---|---|
Block 0 | Flash 或系统存储器别名区,取决于 BOOT 脚的设置 | 0X0000 0000 ~ 0x0007 FFFF(512KB) |
保留 | 0X0008 0000 ~ 0x07FF FFFF | |
用户 Flash,用于存储用户代码 | 0X0800 0000 ~ 0x0807 FFFF(512KB) | |
保留 | 0X0808 0000 ~ 0x1FFF EFFF | |
系统存储器,用于存储 STM32 出厂固化的 Bootloader 程序,比如用于串口下载代码 | 0X1FFF F000 ~ 0X1FFF F7FF(2KB) | |
选项字节,用于配置读保护、设置看门狗等 | 0X1FFF F800 ~ 0X1FFF F80F(16B) | |
保留 | 0X1FFF F810 ~ 0X1FFF FFFF |
可以看到,我们用户 Flash 大小是 512KB,这是对于我们使用的 STM32F103ZET6 来说,如果是其他型号,可能 Flash 会更小,当然,如果 ST 喜欢,也是可以随时推出更大容量的 STM32F103 单片机的,因为这里保留了一大块地址空间。还有,STM32 的出厂固化 BootLoader 非常精简,整个 BootLoder 只占了 2KB Flash 空间。
第二个块是 Block 1,用于存储数据,即 SRAM 空间,其功能划分如下:
存储块 | 功能 | 地址范围 |
---|---|---|
Block 1 | SRAM | 0X2000 0000 ~ 0x2000 FFFF(64KB) |
保留 | 0X2001 0000 ~ 0x3FFF FFFF |
这 个 块 仅 使 用 了 64KB 大小 ( 仅 大 容 量 STM32F103 型 号 才 有 这 么 多 SRAM, 比 如 STM32F103ZET6 等),用于 SRAM 访问,同时也有大量保留地址用于扩展。
第三个块是 Block 2,用于外设访问,STM32 内部大部分的外设都是放在这个块里面的,该存储块里面包括了 AHB、APB1 和 APB2 三个总线相关的外设,其中 AHB 和 APB2 是高速总线(72MHz max),APB1 是低速总线(36M max)。其功能划分如下:
存储块 | 功能 | 地址范围 |
---|---|---|
Block 2 | APB1 总线外设 | 0X4000 0000 ~ 0x4000 77FF |
保留 | 0X4000 7800 ~ 0x4000 FFFF | |
APB2 总线外设 | 0X4001 0000 ~ 0x4000 3FFF | |
保留 | 0X4001 4000 ~ 0x4001 7FFF | |
AHB 总线外设 | 0X4001 8000 ~ 0x4002 33FF | |
保留 | 0X4002 3400 ~ 0x5FFF FFFF |
同样可以看到,各个总线之间,都有预留地址空间,方便后续扩展。
三、寄存器映射
3.1、什么是寄存器
寄存器(Register)是单片机内部一种特殊的内存,它可以实现对单片机各个功能的控制,简单的来说可以把寄存器当成一些控制开关,控制包括内核及外设的各种状态。所以无论是 51单片机 还是 STM32,都需要用寄存器来实现各种控制,以完成不同的功能。
由于寄存器资源非常宝贵,一般都是一个位或者几个位控制一个功能,对于 STM32 来说,其寄存器是 32 位的,一个 32 位的寄存器,可能会有 32 个控制功能,相当于 32 个开关;
从大方向来区分,STM32 寄存器分为两类:
大类 | 小类 | 说明 |
---|---|---|
内核寄存器 | 内核相关寄存器 | 包含 R0~R15、xPSR、特殊功能寄存器 |
中断控制寄存器 |
包含 NVIC 和 SCB 相关的寄存器 NVIC 有:ISER、ICER、ISPR、IP 等 SCB 有:VTOR、AIRCR、SCR 等 |
|
Systick 寄存器 | 包括 CTRL、LOAD、VAL 和 CALIB 四个寄存器 | |
内存保护寄存器 | 可选功能,STM32F103没有 | |
调试寄存器 | 包含 ETM、ITM、DWT、IPIU 等相关寄存器 | |
外设寄存器 | 包含 GPIO、UART、IIC、SPI、TIM、DMA、ADC、DAC、RTC、IWDG、WWDG、PWR、CAN、USB 等各种外设寄存器 |
3.2、寄存器描述解读
寄存器是一类特殊的存储器,它的每个位都有特定的功能,可以实现对外设/功能的控制,给寄存器的地址命名的过程就叫 寄存器映射。
【1】、寄存器名字
每个寄存器都有一个对应的名字,以简单表达其作用,并方便记忆,这里 GPIOx_CRL 表示寄存器英文名,x 可以从 A~G,说明有 7 个这样的寄存器。
【2】、偏移地址
地址偏移量表示相对该外设基地址的偏移,知道了外设基地址和地址偏移量,我们就可以知道任何一个寄存器的实际地址。
【3】、复位值
复位值表示该寄存器在系统复位后的默认值,可以用于分析外设的默认状态。
【4】、寄存器位表
描述寄存器每一个位的作用(共32bit),rw 表示该寄存器可读写(r,可读取;w,可写入)。
【5】、位功能描述
描述寄存器每个位的功能;
3.3、寄存器地址计算
为了方便编写代码及使用,我们将寄存器地址分为三个部分:
- 总线基地址(BUS_BASE_ADDR)
- 外设基于总线基地址的偏移量(PERIPH_OFFSET)
- 寄存器相对外设基地址的偏移量(REG_OFFSET)
总线基地址(BUS_BASE_ADDR),STM32F103 内部有三个总线(APB1、APB2 和 AHB),对应的总线基地址如下所示:
总线 | 基地址 | 偏移量 |
---|---|---|
APB1 | 0x4000 0000 | 0 |
APB2 | 0x4001 0000 | 0x1 0000 |
AHB | 0x4001 8000 | 0x1 8000 |
上表中 APB1 的基地址,也叫 外设基地址,表中的偏移量就是相对于外设基地址的偏移量。
AHB 的总线基地址是 0x4001 8000,从该基地址到 0x4002 0000,只挂了 SDIO 一个外设,后续的 AHB 外设基地址都大于等于 0X4002 0000。为了方便计算,我们可以将 AHB 的总线基地址改成:0x4002 0000,而 SDIO 则单独定义一个基地址给他即可。
外设基于总线基地址的偏移量(PERIPH_OFFSET),这个不同外设偏移量不一样,我们可以在 STM32F103 存储器映射图里面找到具体的偏移量,以 GPIO 为例,其偏移量如下所示:
所属总线 | 外设 | 基地址 | 偏移量 |
---|---|---|---|
APB2 0x4001 0000 |
GPIOA | 0x4001 0800 | 0x800 |
GPIOB | 0x4001 0C00 | 0xC00 | |
GPIOC | 0x4001 1000 | 0x1000 | |
GPIOD | 0x4001 1400 | 0x1400 | |
GPIOE | 0x4001 1800 | 0x1800 | |
GPIOF | 0x4001 1C00 | 0x1C00 | |
GPIOG | 0x4001 2000 | 0x2000 |
上表的偏移量,就是外设基于 APB2 总线基地址的偏移量(PERIPH_OFFSET)。知道了外设基地址,再在参考手册里面找到具体某个寄存器相对外设基地址的偏移量就可以知道该寄存器的实际地址了,以 GPIOB 的相关寄存器为例。
所属总线 | 所属外设 | 寄存器 | 地址 | 偏移量 |
---|---|---|---|---|
AP82 0x4001 0000 |
GPIOB 0x4001 0C00 |
GPIOB_CRL | 0x4001 0C00 | 0 |
GPIOB_CRH | 0x4001 0C04 | 0x04 | ||
GPIOB_IDR | 0x4001 0C08 | 0x08 | ||
GPIOB_ODR | 0x4001 0C0C | 0x0C | ||
GPIOB_BSRR | 0x4001 0C10 | 0x010 | ||
GPIOB_BRR | 0x4001 0C14 | 0x14 | ||
GPIOB_LCPR | 0x4001 0C18 | 0x18 |
上表的偏移量,就是寄存器基于外设基地址的偏移量(REG_OFFSET)。因此,我们根据前面的公式,很容易可以计算出 GPIOB_ODR 的地址:
\[GPIOB\_ODR 的地址 = APB2 的总线基地址+ GPIOB 的外设偏移量+ 寄存器偏移量 \]所以得到:\(GPIOB\_ODR 的地址= 0x4001 0000 + 0xC00 + 0x0C = 0x4001 0C0C\)
3.4、寄存器映射头文件
STM32F103 所有寄存器映射都在 stm32f103xe.h 里面完成,包括各种基地址定义、结构体定义、外设寄存器映射、寄存器位定义(占了绝大部分)等。
stm32f103xe.h 文件主要包含五个部分内容:
文件 | 主要组成部分 | 说明 |
---|---|---|
stm32f103xe.h | 中断编号定义 | 定义 IRQn_Type 枚举类型,包含 STM32F103 内部所有中断编号(中断号),方便后续编写代码 |
外设寄存器结构体类型定义 | 以外设为基本单位,使用结构体类型定义对每个外设的所有寄存器进行封装,方便后面的寄存器映射 | |
寄存器映射 |
1,定义总线地址和外设基地址 2,使用外设结构体类型定义将外设基地址强制转换成结构体指针,完成寄存器映射 |
|
寄存器位定义 | 定义外设寄存器每个功能位的位置及掩码 | |
外设判定 | 判断某个外设是否合法(即是否存在该外设) |
寄存器映射 主要涉及到表中的 外设寄存器结构体类型定义 和 寄存器映射 两个组成部分:外设寄存器结构体类型定义和寄存器映射,总结起来,包括 3 个步骤:
- 外设寄存器结构体类型定义
- 外设基地址定义
- 寄存器映射(通过将外设基地址强制转换为外设结构体类型指针即可)
以 GPIO 为例,其寄存器结构体类型定义如下:
typedef struct
{
__IO uint32_t CRL; // GPIO_CRL寄存器,相对外设基地址偏移量:0X00
__IO uint32_t CRH; // GPIO_CRH寄存器,相对外设基地址偏移量:0X04
__IO uint32_t IDR; // GPIO_IDR寄存器,相对外设基地址偏移量:0X08
__IO uint32_t ODR; // GPIO_ODR寄存器,相对外设基地址偏移量:0X0C
__IO uint32_t BSRR; // GPIO_BSRR寄存器,相对外设基地址偏移量:0X10
__IO uint32_t BRR; // GPIO_BRR寄存器,相对外设基地址偏移量:0X14
__IO uint32_t LCKR; // GPIO_LCKR寄存器,相对外设基地址偏移量:0X18
} GPIO_TypeDef;
GPIO 外设基地址定义如下:
#define PERIPH_BASE 0x40000000UL // 外设基地址
#define APB1PERIPH_BASE PERIPH_BASE // APB1总线基地址
#define APB2PERIPH_BASE (PERIPH_BASE + 0x00010000UL) // APB2总线基地址
#define AHBPERIPH_BASE (PERIPH_BASE + 0x00020000UL) // AHB总线基地址
#define GPIOA_BASE (APB2PERIPH_BASE + 0x00000800UL) // GPIOA基地址
#define GPIOB_BASE (APB2PERIPH_BASE + 0x00000C00UL) // GPIOB基地址
#define GPIOC_BASE (APB2PERIPH_BASE + 0x00001000UL) // GPIOC基地址
#define GPIOD_BASE (APB2PERIPH_BASE + 0x00001400UL) // GPIOD基地址
#define GPIOE_BASE (APB2PERIPH_BASE + 0x00001800UL) // GPIOE基地址
#define GPIOF_BASE (APB2PERIPH_BASE + 0x00001C00UL) // GPIOF基地址
#define GPIOG_BASE (APB2PERIPH_BASE + 0x00002000UL) // GPIOG基地址
GPIO 外设寄存器映射定义如下:
#define GPIOA ((GPIO_TypeDef *)GPIOA_BASE) // GPIOA寄存器地址映射
#define GPIOB ((GPIO_TypeDef *)GPIOB_BASE) // GPIOB寄存器地址映射
#define GPIOC ((GPIO_TypeDef *)GPIOC_BASE) // GPIOC寄存器地址映射
#define GPIOD ((GPIO_TypeDef *)GPIOD_BASE) // GPIOD寄存器地址映射
#define GPIOE ((GPIO_TypeDef *)GPIOE_BASE) // GPIOE寄存器地址映射
#define GPIOF ((GPIO_TypeDef *)GPIOF_BASE) // GPIOF寄存器地址映射
#define GPIOG ((GPIO_TypeDef *)GPIOG_BASE) // GPIOG寄存器地址映射
以上三部分代码,就完成了 STM32F103 内部 GPIOA~GPIOG 的寄存器映射,其原理其实是比较简单的,包括两个核心知识点:(1)、结构体地址自增;(2)、地址强制转换;
结构体地址自增,我们第一步就定义了 GPIO_TypeDef 结构体类型,其成员包括:CRL、CRH、IDR、ODR、BSRR、BRR和 LCKR,每个成员是 uint32_t 类型,也就是 4 个字节,这样假设:CRL 地址是 0 的话,CRH 就是 0X04,IDR 就是 0X08,ODR 就是 0X0C,以此类推。
地址强制转换,以 GPIOB 为例,GPIOB 外设的基地址为:GPIOB_BASE(0X4001 0C00),我们使用 GPIO_TypeDef 将该地址强制转换为 GPIO 结构体类型指针:GPIOB,这样 GPIOB->CRL 的地址就是:GPIOB_BASE(0X4001 0C00),GPIOB->CRH 的地址就是:GPIOB_BASE + 0X04(0X4001 0C04),GPIOB->IDR 的地址就是:GPIOB_BASE + 0X08(0X4001 0C08),以此类推。
标签:02,框架,总线,GPIOB,地址,BASE,寄存器,STM32F1,外设 From: https://www.cnblogs.com/kurome/p/17560958.html