您现在的位置是:首页 >技术教程 >嵌入式软件开发必备知识—存储器和寄存器器映射(重要)网站首页技术教程
嵌入式软件开发必备知识—存储器和寄存器器映射(重要)
一、存储器映射
存储器映射是一种计算机内存管理技术,它将计算机存储器的一部分映射到处理器的地址空间。这样,处理器就可以直接访问内存中映射的地址,而不需要进行复杂的寻址计算。存储器映射可以用于访问计算机的物理内存、设备寄存器、硬件设备等。在存储器映射中,不同的地址被映射到不同的实际内存位置或硬件设备,这样可以简化对内存的管理和操作。
存储器是可以存储数据的设备,本身是没有地址信息的,对存储器分配地址的过程称为存储器映射。
先复习一点知识:
1 Byte = 8 bit(位)
1 Kilobyte (KB) = 1,024 Bytes
1 Megabyte (MB) = 1,024 KB
1 Gigabyte (GB) = 1,024 M
1 Terabyte (TB) = 1,024 G
我们所说的单片机的的内存大小是说的是字节,而不是位。
假设一个某个芯片有19根地址线:A0-A18,数据线是:D0-D15(16位,两个字节),他所能访问的地址的标号数就是2^19字节,512 KB个地址,512K*2 = 1024bit = 1M.接下来,我们对其存储器进行映射。
地址范围 | 0-512K |
映射1 | 0-512K(0x0-0x80000) |
映射2 | 1-512K(0x0-0x80000) |
映射3 | 100k-612K |
映射3 | 512K-1024K |
存储器功能的划分(F1为例)
ST将4GB(2^32)地址空间分成8个块
ST的4GB的地址空间可以分成8个块,每个块为512MB。
具体分配如下:
1. 块0(Code(FLASH)): 0x00000000 ~ 0x1FFFFFFF(主要用于存储代码)
2. 块1(SRAM): 0x20000000 ~ 0x3FFFFFFF
3. 块2(片上外设): 0x40000000 ~ 0x5FFFFFFF(最重要)
4. 块3(FSMC Bank1&2): 0x60000000 ~ 0x7FFFFFFF
5. 块4(FSMC Bank3&4): 0x80000000 ~ 0x9FFFFFFF
6. 块5(FSMC寄存器): 0xA0000000 ~ 0xBFFFFFFF
7. 块6(没用到): 0xC0000000 ~ 0xDFFFFFFF
8. 块7(Cortex M3内部外设):0xE0000000 ~ 0xFFFFFFFF
每个块都有512MB的地址空间,可以用来存放不同的程序、数据以及内核代码等。这种分块的方式方便操作系统和应用程序进行内存管理,提高系统的稳定性与性能。
二、寄存器映射(重要)
寄存器是单片机内部一种特殊的内存,可以实现对单片机各个功能的控制。
简单来说:寄存器就是单片机内部的控制机构。
什么叫做寄存器映射?
寄存器是特殊的存储器,给寄存器地址命名的过程,就叫做寄存器映射。
例如我们一个寄存器的地址是0x4001080C,我们给寄存器命名叫做GPIOA_ODR(寄存器名字),这个过程就叫做寄存器映射。
如何直接操作寄存器
*(unsigned int*)(0x4001080C) = 0XFFFF;
即GPIOA_ODR所有的位都是1,即PA0-PA15都是1。
定义一个名字后再操作
# define GPIO_ODR *(unsigned int *)(0X4001080C)
GPIO_ODR = 0XFFFF;
三、寄存器映射计算
问题一:寄存器地址怎么寻找并计算呢?
问题二:如何映射?
解决一
为了方便编写代码及使用,我们寄存器地址分为三个部分:
1、总线基地址(BUS_BASE_ADDR)
2、外设基于总线基地址的偏移量(PERIPH_OFFSET)
3、寄存器相对与外设基地址的偏移量(REG_OFFSET)
寄存器地址=BUS_BASE_ADDR+PERIPH_OFFSET+REG_OFFSET
以F1为例,我们的总线基地址如下
总线 | 基地址 | 偏移量 |
APB1 | 0X4000 0000 | 0 |
APB2 | 0X4001 0000 | 0X1 0000 |
AHB | 0X4001 8000 | 0X1 8000 |
APB1总线的基地址,也叫做外设的基地址(PERIPH_BASE)
此表的偏移量:是相对外设的基地址。
GPIO外设基地址及偏移量
所属总线 | 外设 | 基地址 | 偏移量 |
APB2 0X4001 0000 | GPIOA | 0X4001 0800 | 0X0800 |
GPIOB | 0X4001 0C00 | 0X0C00 | |
GPIOC | 0X4001 1000 | 0X1000 | |
GPIOD | 0X4001 1400 | 0X1400 | |
GPIOE | 0X4001 1800 | 0X1800 | |
GPIOF | 0X4001 1C00 | 0X1C00 | |
GPIOG | 0X4001 2000 | 0X2000 |
此表的偏移量:是相对APB2外设基地址(APB2 PERIPH_OFFSET)偏移量
GPIO外设基地址及偏移量
所属总线 | 所属外设 | 寄存器 | 地址 | 偏移量 |
APB2 0X4001 0000 | GPIO 0X4001 0800 | GPIOA_CRL | 0X4001 0800 | 0X00 |
GPIOA_CRH | 0X4001 0804 | 0X04 | ||
GPIOA_IDR | 0X4001 0808 | 0X08 | ||
GPIOA_ODR | 0X4001 080C | 0X0C | ||
GPIOA_BSRR | 0X4001 0810 | 0X10 | ||
GPIO_BRR | 0X4001 0814 | 0X14 | ||
GPIO_LCKR | 0X4001 0818 | 0X18 |
GPIO_ODR寄存器地址计算过程:
1.获取外设挂在哪个总线上面?查:系统结构图
2.获取总线的基地址,APB2总线基地址是:0X4001 0000
3.获取外设地址偏移量,GPIOA相对APB2总线偏移量是:0X800
4.获取寄存器地址偏移量,ODR相对GPIOA外设基地址的偏移量是0X0C。
寄存器地址=BUS_BASE_ADDR+PERIPH_OFFSET+REG_OFFSET
GPIO_ODR = 0X4001 0000+0X800+0X0C=0X4001 080C
解决二:
使用结构体可以很方便的实现对寄存器的映射
typdef struct
{
_IO uint32_t CRL;
_IO uint32_t CRH;
_IO uint32_t IDR;
_IO uint32_t ODR;
_IO uint32_t BSRR;
_IO uint32_t LCKR;
}GPIO_TypeDef;
整体映射:
# define GPIOA ((GPIO_TypeDef*)GPIOA_BASE)
取里边的地址:
&GPIOA->CRL: 0X4001 0800
&GPIOA->CRH:0X4001 0804
&GPIOA->IDR: 0x40010808
&GPIOA->ODR:0X4001 080C
实际应用:
GPIO->ODR= 0XFFFF;