今天主要是复习一下。
结合野火的《零基础开发指南》名字没记住大概是这个
先放一张结构图
存储器映射(初学重点):
我们的片内外设比如:Flash,Sram,Fsmc,以及挂在AHB 总线上的外设,我们都需要知道他的地址来操作这些器件。而这些外设的地址都被分配在一个4g的内存空间里(4g的存储器,下文中的)。
为什么是4g的?
2的32次方就是4g-byte。
存储器映射
存储器本身不具有地址信息,它的地址是由芯片厂商或用户分配,给存储器分配地址的过程就称为存储器映射,具体见图存储器映射。如果给存储器再分配一个地址就叫存储器重映射。
注意看4g的存储空间分位8个部分,每部分都给分配了各自的起始地址和末尾的地址信息。每块占用512MByte
显然8*512=4096MByte 4096MByte/1024=4g
拿其中几个举个例子:
第一部分就包含了Flash
第二部分就包含了Sram我的板子买的霸道,也就是F103zet6 64k的SRAM
第三部分就是FSMC
我是初学,咱时不管这部分(用到啥学啥,你没那么好的记性和时间)
其余的暂时不列出来。
重点来了!!!!!!!
我们操作的主要部分时在BLOCK2
我们首先得明白,分配的这些地址首先他是连续的。这一点很重要。
那么BLOCK2包含了哪些??
眼神好的自己看
《STMF103X英文数据手册》
简言之,这部分详细的描述了我们的外设及其分配到的地址信息。(有大用)
以端口举例,详细的描述了各端口的起始地址和终止地址,同样也是连续的。可以数数看。
对于这8个块,主要看三块就可以,BLOCK0对应的FLASH,BLOCK1对应的SRAM,BLOCK2对应的片上外设。
我们先直接看BLOCK2部分
BLOCK2上有两个总线,AHB和APB,这两个总线主要区别在于其速度不同。
APB又被分为APB1和APB2
而APB2和AHB被称为高速总线,挂高速外设,APB1是低速总线,挂低低速外设。
寄存器的映射
在存储器 Block2 这块区域,设计的是片上外设,它们以四个字节为一个单元,共 32bit,每一个单元对应不同的功能,当我们控制这些单元时就可以驱动外设工作(实际就是每32个位也就是4个字节为一个寄存器,而每一个寄存器负责一个具体的功能。巧了我们的单片机正好就是32位的,他正好能一次处理32位的数据)。
我们可以找到每个单元的起始地址,然后通过 C 语言指针的操作方式来访问这些单元,如果每次都是通过这种地址的方式来访问,不仅不好记忆还容易出错,这时我们可以根据每个单元功能的不同,以功能为名给这个
内存单元取一个别名,这个别名就是我们经常说的寄存器,这个给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射。(了解一下,后面在代码中一下就能明白)。
比如:
我们找到 GPIOB 端口的输出数据寄存器 ODR 的地址是 0x40010C0C(至于这个地址如何找到可以先跳过,后面我们会有详细的讲解),ODR 寄存器是 32bit,低 16bit 有效,对应着 16 个外部 IO,写 0/1 对应的的 IO 则输出低/高电平。现在我们通过 C 语言指针的操作方式,让 GPIOB的 16 个 IO 都输出高电平。
操作寄存器的方法一:通过绝对地址访问内存单元
// GPIOB 端口全部输出 高电平
*(unsigned int*)(0x4001 0C0C) = 0xFFFF;
简单解释一下
0x4001 0C0C是GPIOB的ODR寄存器的地址,先别管他怎么来的。只需要知道我们操作这个地址就可以控制寄存器对应的GPIO输出。
只不过对于单片机来说这个地址是一个变量,是一个立即数,而不是地址。(这里我没太明白。上网查了一下,我的理解就是说这个只是个数据而无实际意义,我们将他给Int变量那么这个就是int型数据,把他给指针变量那他就是地址,如果不赋值给一个具体的变量类型那他就啥也不是)。
我们需要强制转化为地址才行,所以使用了(unsigned int),也就是说这个地址是一个32位无符号整型的指针(指针就是地址)。*
对于这个地址空间其存储了一个数据0xFFFF。(一定要懂)
说明一下0xFFFF,我们知道对于一个16进制数 来说,一个F对应的4位。那么4个F对应的正好就是16位,而我们的ODR寄存器也正好能够操作的就只有16位。(后面针对ODR寄存器讲解的时候能用到)
而unsigned int 是32位的,对于0x4001 0C0C 是够用的!
那么使用绝对地址访问寄存器的方式缺点就很明显,地址不好记啊,也不好写啊。
操作寄存器的方法二:通过寄存器别名的方式访问寄存器
// GPIOB 端口全部输出 高电平
#define GPIOB_ODR (unsigned int*)(GPIOB_BASE+0x0C)
* GPIOB_ODR = 0xFF;
使用宏定义#define 给寄存器的地址重新命名为GPIOB_ODR
然后使用* 号去操作ODR的值
这就是基本的指针操作,不做解释对于(GPIOB_BASE+0x0C)暂时不必纠结。只需要知道他是寄存器ODR的地址。
上面通过寄存器别名访问寄存器更换好的写法是
// GPIOB 端口全部输出 高电平
#define GPIOB_ODR *(unsigned int*)(GPIOB_BASE+0x0C)//将* 号也封装在//宏定义里
GPIOB_ODR = 0xff; //直接操作即可
下面介绍STM32的外设地址映射
片上外设区分为三条总线AHB总线和APB1,APB2(APB1和APB2共同构成APB总线),根据外设速度的不同,不同总线挂载着不同的外设,APB1 挂载低速外设,APB2 和 AHB 挂载高速外设。
相应总线的最低地址我们称为该总线的基地址,总线基地址也是挂载在该总线上的首个外设的地址。(这句就很有用,记住要考)
其中 APB1 总线的地址最低,片上外设从这里开始,也叫外设基地址。
表格总线基地址 的“相对外设基地址偏移”即该总线地址与“片上外设”基地址 0x4000 0000 的差值。(这个差值就是相对总线基地址的偏移量)
这里还不够直观我重新描述一下(对比以下三张图)