学习资料
野火所有产品资料链接:
- 网盘资料连接: http://products.embedfire.com
- 教学视频可直接 B 站在线观看,无需下载: https://space.bilibili.com/356820657
- 为更加方便的下载资料,请安装“野火大学堂”客户端,下载资料速度远超百度云,还可以在线看视频
下载地址: [https://www.firebbs.cn/forum.php?mod=viewthread&tid=29500&extra]简介
STM32,从字面上来理解,ST 是意法半导体,M 是 Microelectronics 的缩写,32 表示 32 位,合起来理解,STM32 就是指 ST 公司开发的32 位微控制器。微控制器和微处理器的区别: 微处理器有MMU 内存管理单元,可以运行Linux系统,主频比较高,400M 微控制器主频低,没有MMU
STM32分类
命名方法
引脚定义
寄存器
STM32长什么样
开发板中使用的芯片是100pin 的STM32F103VET6。芯片正面是丝印,ARM是芯片使用的是ARM内核,STM32F103VET6是芯片型号, 后面的字应该是跟生产批次相关,最上面的是ST的LOGO。芯片四周是引脚,左下角的小圆点表示1 脚,然后从1 脚起按照逆时针的顺序排列(所有芯片的引脚顺序都是逆时针排列的)。开发板中把芯片的引脚引出来,连接到各种传感器上,然后在 STM32 上编程(实际就是通过程序控制这些引脚输出高电平或者低电平)来控制各种传感器工作
芯片结构
:::info
内核与外设就如同电脑上的CPU 与主板、内存、显卡、硬盘的关系。
:::
STM32F429 采用的是Cortex-M4 内核,内核即CPU,由ARM 公司设计。ARM 公司并不生产芯片,而是出售其芯片技术授权(IP厂商)。芯片生产厂商(SOC)如ST、TI、Freescale,负责在内核之外设计部件并生产整个芯片,这些内核之外的部件被称为核外外设或片上外设。 如GPIO、USART(串口)、I2C、SPI 等都叫做片上外设
芯片(这里指内核,或者叫CPU)和外设之间通过各种总线连接,其中驱动单元有4 个,被动单元也有4 个
存储器映射
在图STM32F10xx 系统框图中,被控单元的FLASH,RAM,FSMC 和AHB 到APB 的桥(即片上外设),这些功能部件共同排列在一个4GB(232)的地址空间内。我们在编程的时候,可以通过他们的地址找到他们,然后来操作他们(通过C语言对它们进行数据的读和写)。
存储器本身不具有地址信息,它的地址是由芯片厂商或用户分配,给存储器分配地址的过程就称 为存储器映射,具体见图存储器映射。如果给存储器再分配一个地址就叫存储器重映射。
在这8 个Block 里面,有3个块非常重要,也是我们最关心的三个块。Block0 用来设计成内部 FLASH,Block1 用来设计成内部RAM,Block2 用来设计成片上的外设。
寄存器映射
在存储器Block2这块区域,设计的是片上外设,它们以四个字节为一个单元,共32bit,每一个单元对应不同的功能,当我们控制这些单元时就可以驱动外设工作。我们可以找到每个单元的起始地址,然后通过C 语言指针的操作方式来访问这些单元,如果每次都是通过这种地址的方式来访问,不仅不好记忆还容易出错,这时我们可以根据每个单元功能的不同,以功能为名给这个内存单元取一个别名,这个别名就是我们经常说的寄存器,这个给已经分配好地址的有特定功能 的内存单元取别名的过程就叫寄存器映射。
比如,我们找到GPIOB 端口的输出数据寄存器ODR 的地址是0x40010C0C(至于这个地址如何找到可以先跳过,后面我们会有详细的讲解)。ODR 寄存器是32bit,低16bit 有效,对应着16 个外部IO,写0/1 对应的的IO 则输出低/高电平。现在我们通过C 语言指针的操作方式,让GPIOB 的16 个IO 都输出高电平。
刚刚我们说了,通过绝对地址访问内存单元不好记忆且容易出错,我们可以通过寄存器的方式来操作
//GPIOB端口全部输出高电平
#define GPIOB_ODR (unsigned int ) (GPIOB_BASE+0x0C) GPIOB_ODR = 0xFFFF;外设地址映射
外设地址映射
片上外设区分为三条总线,根据外设速度的不同,不同总线挂载着不同的外设,APB1 挂载低速外设,APB2 和AHB 挂载高速外设。相应总线的最低地址我们称为该总线的基地址,总线基地址也是挂载在该总线上的首个外设的地址。其中APB1总线的地址最低,片上外设从这里开始, 也叫外设基地址。
- 总线基地址
表格总线基地址的“相对外设基地址偏移”即该总线地址与“片上外设”基地址0x4000 0000 的差值
- 外设基地址
总线上挂载着各种外设,这些外设也有自己的地址范围,特定外设的首个地址称为“XX 外设基地址”,也叫XX外设的边界地址。具体有关STM32F10xx 外设的边界地址请参考《STM32F10xx 参考手册》的2.3 小节的存储器映射的表1:STM32F10xx 寄存器边界地址。
这里面我们以GPIO这个外设来讲解外设的基地址,GPIO属于高速的外设,挂载到APB2总线上:
- 外设寄存器
在XX外设的地址范围内,分布着的就是该外设的寄存器。以GPIO 外设为例,GPIO 是通用输入输出端口的简称,简单来说就是STM32可控制的引脚,基本功能是控制引脚输出高电平或者低电平。
GPIO 有很多个寄存器,每一个都有特定的功能。每个寄存器为32bit,占四个字节,在该外设的 基地址上按照顺序排列,寄存器的位置都以相对该外设基地址的偏移地址来描述。
有关外设的寄存器说明可参考《STM32F10xx 参考手册》中具体章节的寄存器描述部分。
如何理解寄存器的说明
- 名称
寄存器说明中首先列出了该寄存器中的名称,“(GPIOx_BSRR)(x=A…E)”这段的意思是该寄存器名为“GPIOx_BSRR”其中的“x”可以为A-E,也就是说这个寄存器说明适用于GPIOA、GPIOB 至GPIOE,这些GPIO 端口都有这样的一个寄存器。
- 偏移地址
偏移地址是指本寄存器相对于这个外设的基地址的偏移。本寄存器的偏移地址是 0x18,从参考手册中我们可以查到GPIOA 外设的基地址为0x4001 0800 ,我们就可以算出GPIOA 的这个GPIOA_BSRR 寄存器的地址为:0x4001 0800+0x18;同理,由于GPIOB 的外设基地址为0x4001 0C00,可算出GPIOB_BSRR 寄存器的 地址为:0x4001 0C00+0x18 。其他GPIO 端口以此类推即可。
1.
寄存器位表
紧接着的是本寄存器的位表,表中列出它的0-31 位的名称及权限。表上方的数字为位编号,中间为位名称,最下方为读写权限,其中w 表示只写,r 表示只读, rw 表示可读写。本寄存器中的位权限都是w,所以只能写,如果读本寄存器,是无法保证读取到它真正内容的。而有的寄存器位只读,一般是用于表示STM32 外设的某种工作状态的,由STM32 硬件自动更改,程序通过读取那些寄存器位来判断外设的工作状态。
1.
位功能说明
位功能是寄存器说明中最重要的部分,它详细介绍了寄存器每一个位的功能。例如本寄存器中有两种寄存器位,分别为BRy 及BSy,其中的y 数值可以是0-15, 这里的0-15 表示端口的引脚号,如BR0、BS0 用于控制GPIOx 的第0 个引脚,若 x 表示GPIOA,那就是控制GPIOA 的第0 引脚,而BR1、BS1 就是控制GPIOA 第1 个引脚。其中BRy 引脚的说明是“0:不会对相应的ODRx 位执行任何操作;1:对相应 ODRx 位进行复位”。这里的“复位”是将该位设置为0 的意思,而“置位”表示 将该位设置为1;说明中的ODRx 是另一个寄存器的寄存器位,我们只需要知道 ODRx 位为1 的时候,对应的引脚x 输出高电平,为0 的时候对应的引脚输出低 电平即可(感兴趣的读者可以查询该寄存器GPIOx_ODR 的说明了解)。所以,如 果对BR0 写入“1”的话,那么GPIOx 的第0 个引脚就会输出“低电平”,但是 对BR0 写入“0”的话,却不会影响ODR0 位,所以引脚电平不会改变。要想该 引脚输出“高电平”,就需要对“BS0”位写入“1”,寄存器位BSy 与BRy 是相 反的操作。
C语言对寄存器的封装
*
封装总线和外设基地址
在编程上为了方便理解和记忆,我们把总线基地址和外设基地址都以相应的宏定义起来,总线或者外设都以他们的名字作为宏名
*
封装寄存器列表
用上面的方法去定义地址,还是稍显繁琐,例如GPIOA-GPIOE 都各有一组功能相同的寄存器, 如GPIOA_ODR/GPIOB_ODR/GPIOC_ODR 等等,它们只是地址不一样,但却要为每个寄存器都 定义它的地址。为了更方便地访问寄存器,我们引入C 语言中的结构体语法对寄存器进行封装
这段代码用typedef 关键字声明了名为GPIO_TypeDef 的结构体类型,结构体内有7 个成员变量, 变量名正好对应寄存器的名字。C 语言的语法规定,结构体内变量的存储空间是连续的,其中32 位的变量占用4 个字节,16 位的变量占用2 个字节。
也就是说,我们定义的这个GPIO_TypeDef ,假如这个结构体的首地址为0x4001 0C00(这也是第 一个成员变量CRL 的地址),那么结构体中第二个成员变量CRH 的地址即为0x4001 0C00 +0x04 ,加上的这个0x04,正是代表CRL 所占用的4 个字节地址的偏移量。这样的地址偏移与STM32 GPIO 外设定义的寄存器地址偏移一一对应,只要给结构体设置好首地 址,就能把结构体内成员的地址确定下来,然后就能以结构体的形式访问寄存器
这段代码先用 GPIO_TypeDef 类型定义一个结构体指针 GPIOx,并让指针指向地址 GPIOB_BASE(0x4001 0C00),使用地址确定下来,然后根据C 语言访问结构体的语法,用GPIOx- >ODR 及GPIOx->IDR 等方式读写寄存器。
更进一步,直接使用宏定义好GPIO_TypeDef 类型的指针,而且指针指向各个GPIO 端口的首地址,使用时我们直接用该宏访问寄存器即可
以此类推,其他外设也同样可以用这种方法来封装。好消息是,这部分工作都由固件库帮我们完成了,这里我们只是分析了下这个封装的过程