使用的是野火,指南者 型号 STM32F103VET6
芯片引脚100个
STM32结构图
STM32由下面的结构组成。内核通过总线来操作各种各样的外设。
具体怎么操作,看下面的总线介绍。
系统架构图
内存单元
STM32把内存4G,分成八块。每块有其对应的用途。
我们所操作的对象有引脚、串口、时钟等等,这些东西本质上都是芯片外部的东西叫做外设。
每一块内存对应单片机中的一块,Block2中对应外设,也就是我们要操作的部分,这些内存中的地址就是操作外设的地址。
我们的外设在block2,如图
寄存器
什么是寄存器?什么是寄存器映射?
每个寄存器是32位,4B
STM32每32位作为一个单元,存储数据,给这个单元起个名字就是寄存器。
总线
不同外设,根据速度不同挂载在不同的总线上。由下到上,是APB1,APB2,AHB。
APB1 慢速
APB2 高速
AHB 高速
-
总线外设结构
- 注意看外设挂载在哪个总线上。每个外设都有对应的寄存器。操作外设就是操作寄存器的过程。
知道寄存器的地址,对该地址操作就行了,怎么得到寄存器地址?
寄存器的地址 = 总线基地址 + 外设偏移 +寄存器偏移 = 外设地址 + 寄存器偏移
-
总线基地址
-
外设基地址(=总线地址 + 偏移)
以GPIO为例,GPIO挂在APB2总线上(APB2总线基地址+偏移)
注意的是外设地址是一段地址,因为每个外设里面有若干个寄存器。所以操作外设就是操作寄存器,起始地址就是基地址
外设基地址汇总
下面给出了所有的总线下挂载的外设,起始地址就是基地址
AHB总线
APB2总线
APB1总线
寄存器地址(= 外设基地址 + 寄存器偏移)
具体寄存器相对于外设端口的偏移要看文档
案例:操控某个寄存器
得到GPIOB地址(APB2基地址+偏移),再得到GPIOB某个寄存器地址(GPIOB基地址+偏移),得到寄存器基地址后,就可以*(unsigned int*)操作从而进行编程。
GPIOB所在地址是0x4001 0c00
而DBR寄存器,相对于GPIOB偏移为0C(H),
故DBR寄存器地址为 0X4001 0C0C
操作该寄存器
对这个寄存器的16位操作就是
错误写法
0X4001 0C0C =0XFFFF //这是不对的,因为0x4001 0c0c不是指针
正确写法
*(unsinged int*)0X4001 0C0C = 0XFFFF //这样才对,转换为指针类型
转化为宏定义
更加方便了
#define GPIOB_DBR *(unsigned int*)0X4001 0C0C //相当于给内存中的这个单元起别名,寄存器映射
GPIOB_DBR = 0XFFFF
那么如何操作寄存器的某一位呢?
C语言操作
单独定义寄存器
C语言可以通过宏来定义好基地址。
1.定义好总线、外设的基地址。同时定义好外设对应寄存器的地址
2.外设是一段地址,外设内部有若干个寄存器,上面说过每个寄存器是4B,32位。
那么如何操作32位中的某一位呢?
答:通过 与或非和移位(& | ~ << >>)
如:让PB0输出对应低电平和高电平?
输出电平用的是GPIOX_ODR寄存器(端口数据输出寄存器):该寄存器低16位有效,高16位无效,因此只要编程低16位就行。
ODR寄存器第0位是1,则就把p0输出为1。反之第0位是0,则就把p0输出为0
GPIOB_ODR=(1<<0); //这是让1左移0位 0000 0000 0000 0001,则P0就是1,其他都是0
GPIOB_ODR = (1<<10); //这是让1左移10位,0000 0100 0000 0000则P10就是1
GPIOB_ODR = ~(1<<0); //这是取反
~(0000 0000 0000 0001)=1111 1111 1111 111
如果说要p0和P10都是1呢?
GPIOB_ODR =(1<<10); //P10是1
GPIOB_ODR | =(1<<0); //加上或运算就行
0000 0100 0000 0000 | 0000 0000 0000 0001 =0000 0100 0000 0001
或(|)就是加
与(&)就是乘
取反(~)可以反
结构体方法
每个外设有若干个寄存器,因此把寄存器按顺序封装到结构体中。
每个寄存器4B,因此结构体内部是uint32_t类型。这样刚好符合每个寄存器相对于外设基地址的偏移。
给出创建的结构体基地址,寄存器地址 = 结构体基地址 + 偏移
我们想结构体的基地址肯定要是外设的基地址。因此把外设定义成指向该结构体的指针。谁指向结构体,结构体的基地址就是这个指针的内容
调用直接用 GPIOA->ODR 就可使用GPIOA的ODR寄存器。