标题:
STM32总结(2)
[打印本页]
作者:
look_w
时间:
2017-9-23 14:43
标题:
STM32总结(2)
本帖最后由 look_w 于 2017-9-23 15:54 编辑
八、
STM32
的
GPIO
想要控制 LED 灯,当然是通过控制STM32 芯片的I/O 引脚
电平的高低
来实现。在 STM32 芯片上,I/O 引脚可以被软件设置成各种
不同的功能
,如输入或输出,所以被称为 GPIO(General-purpose I/O)。而 GPIO 引脚又被分GPIOA、GPIOB„„GPIOG 不同的组,每组端口分为0~15,共 16 个
不同的引
脚,对于不同型号的芯片,端口的组和引脚的数量不同,具体请参考相应芯片型号的 datasheet。
于是,控制 LED的步骤就自然整理出来了:
1. GPIO 端口引脚多 --> 就要选定需要控制的
特定引脚
2. GPIO 功能如此丰富 --> 配置需要的
特定功能
3. 控制 LED 的亮和灭 --> 设置 GPIO 输出电压的
高低
继续思考,要控制 GPIO端口,就要涉及到控制相关的寄存器。这时我们就要查一查与 GPIO 相关的寄存器了,可以通过《 STM32 参考手册》来查看。
1. 配置寄存器:选定GPIO 的
特定功能
,最基本的如:选择作为输入还是输出端口。
2. 数据寄存器:保存了GPIO 的
输入电平
或 将要
输出的电平
。
3. 位控制寄存器:设置某引脚的
数据
为 1 或 0,控制输出的电平。
4. 锁定寄存器:设置某
锁定引脚
后,就不能修改其配置。
一切要以官方的数据手册为依据。接下来查看各寄存器相应位的含义,进行设置即可。
例如:对 x端口的寄存器 GPIOx_BSRR 的
第
0
位
(BS0)
进行写1,则 x 端口的第 0 引脚被设置为 1,输出
高电平
,若要令第0 引脚再输出
低电平
,则需要向GPIOx_BSRR 的
第
16
位
(BR0)
写 1。
首先请大家回顾一下在 51 单片机上点亮LED 是怎样实现的。
<span style="font-size:18px;">#include<reg52.h>
int main (void)
{
P0=0;
while(1);
}
</span>
以上代码就可以点亮 P0端口与 LED 阴极相连的LED 灯了,当然,这里省略了启动代码。为什么这个
P0 =0;
句子就能控制P0 端口为低电平?很多刚入门 51 单片机的同学还真解释不来,关键之处在于这个代码所包含的头文件
<reg52.h>
。
在这个文件下有以下的定义:
1.
/* BYTE Registers */
2.
sfr P0 =
0x80
;
3.
sfr P1 =
0x90
;
.........
20.
sfr IP =
0xB8
;
21.
sfr SCON =
0x98
;
22.
sfr SBUF =
0x99
;
这些定义被称为
地址映射
。
所谓地址映射,就是将芯片上的
存储器
甚至
I/O
等资源与地址建立一一对应的关系。如果某地址对应着某
寄存器
,我们就可以运用c 语言的指针来寻址并修改这个地址上的内容,从而实现修改该寄存器的内容。
正是因为
<reg52.h>
头文件中有了对于各种
寄存器
和
I/O
端口的
地址映射
,我们才可以在 51 单片机程序中方便地使用
P0=0xFF; TMOD =0xFF
等赋值句子对寄存器进行配置,从而控制单片机。 Cortex-M3 的地址映射也是类似的。Cortex-M3 有 32 根地址线,所以它的寻址空间大小为 2^32 bit=4GB。 ARM 公司设计时,预先把这 4GB 的寻址空间大致地分配好了。它把地址从 0x4000 0000 至 0x5FFF FFFF( 512MB )的地址分配给片上外设。通过把片上外设的寄存器映射到这个地址区,就可以简单地以访问内存的方式,访问这些外设的寄存器,从而控制外设的工作。结果,片上外设可以使用C 语言来操作。 M3存储器映射见下图
stm32f10x.h
这个文件非常重要,是一个非常底层的文件。所有处理器厂商都会将对内存的操作封装成一个宏,即我们通常说的寄存器,并且把这些实现封装成一个系统文件,包含在相应的开发环境中。这样,我们在开发自己的应用程序的时候只要将这个文件包含进来就可以了。
stm32f10x.h
这个文件中重要的内容就是把 STM32 的所有寄存器进行地址映射。如同51 单片机的
<reg52.h>
头文件一样,
stm32f10x.h
像一个大表格,我们在使用的时候就是通过宏定义进行类似查表的操作,大家想像一下没有这个文件的话,我们要怎样访问 STM32的寄存器?有什么缺点?
不进行这些宏定义的缺点有:
1、地址容易写错
2、我们需要查大量的手册来确定哪个地址对应哪个寄存器
3、看起来还不好看,且容易造成编程的错误,效率低,影响开发进度。
在这里我们以外接了 LED灯的外设 GPIOC 为例,在这个文件中有这样的一系列宏定义:
1.
#define
GPIOC_BASE(APB2PERIPH_BASE + 0x1000)
2.
#define
APB2PERIPH_BASE(PERIPH_BASE + 0x10000)
3.
#define
PERIPH_BASE((uint32_t)0x40000000)
外设基地址
首先看到
PERIPH_BASE
这个宏,宏展开为
0x40000000
,并把它强制转换为 uint32_t 的 32 位类型数据,这是因为地 STM32 的地址是32 位的,是不是觉得
0x40000000
这个地址很熟?是的,这个是 Cortex-M3核分配给片上外设的从 0x40000000 至 0x5FFF FFFF 的 512MB 寻址空间中的第一个地址,我们把
0x4000 0000
称为外设基地址。
总线基地址
接下来是宏
APB2PERIPH_BASE
,宏展开为
PERIPH_BASE
(外设基地
址) 加上偏移地址
0x1 0000
,
即指向的地址为 0x40010000。这个
APB2PERIPH_BASE
宏是什么地址呢? STM32 不同的外设是挂载在不同的总线上的。有AHB 总线、 APB2 总线、 APB1 总线,挂载在这些总线上的外设有特定的地址范围。
其中像 GPIO、串口1、 ADC 及部分定时器是挂载这个被称为 APB2 的总线上,挂载到 APB2 总线上的外设地址空间是从 0x4001 0000 至地址0x4001 3FFF。这里的第一个地址,也就是0x4001 0000,被称为
APB2PERIPH_BASE
(APB2 总线外设的基地址)。而 APB2 总线基地址相对于外设基地址的偏移量为 0x1 0000 个地址,即为APB2 相对外设基地址的偏移地址。
寄存器组基地址
最后到了宏
GPIOC_BASE
,宏展开为
APB2PERIPH_BASE
(APB2 总线外设的基地址)加上相对 APB2 总线基地址的偏移量
0x1000
得到了GPIOC 端口的寄存器组的基地址。这个所谓的寄存器组又是什么呢?它包括什么寄存器?
注意到这个说明中有一个偏移地址: 0x04,这里的偏移地址的是相对哪个地址的偏移呢?下面进行举例说明。
对于 GPIOC 组的寄存器,GPIOC 含有的
端口配置高寄存器
(GPIOC_CRH)
寄存器地址为:
GPIOC_BASE +0x04
。
也就是说,这个偏移地址,就是该寄存器相对所在寄存器组基地址的偏移量。
ST 公司的工程师采用了更巧妙的方式来确定这些地址。
STM32
库对寄存器的封装
ST 的工程师用结构体的形式,封装了寄存器组,在 stm32f10x.h文件中,有以下代码:
1.
#define
GPIOA((GPIO_TypeDef *) GPIOA_BASE)
2.
#define
GPIOB((GPIO_TypeDef *) GPIOB_BASE)
3.
#define
GPIOC((GPIO_TypeDef *) GPIOC_BASE)
stm32f10x.h
文件中找到如下代码:
1.
typedef struct
2.
{
3.
__IO uint32_t CRL;
4.
__IO uint32_t CRH;
5.
__IO uint32_t IDR;
6.
__IO uint32_t ODR;
7.
__IO uint32_t BSRR;
8.
__IO uint32_t BRR;
9.
__IO uint32_t LCKR;
10.
} GPIO_TypeDef;
这个代码用
typedef
关键字声明了名为
GPIO_TypeDef
的结构体类型,结构体内又定义了 7 个
__IOuint32_t
类型的变量。这些变量每个都为 32位,也就是每个变量占内存空间 4个字节。
在 c 语言中,结构体内变量的存储空间是连续的,也就是说假如我们定义了一个
GPIO_TypeDef
,这个结构体的首地址(变量 CRL 的地址)若为
0x40011000
, 那么结构体中第二个变量(CRH)的地址即为
0x40011000 +0x04
,加上的这个
0x04
,正是代表 4 个字节地址的偏移量。这个 0x04 偏移量,正是 GPIOx_CRH 寄存器相对于所在寄存器组的偏移地址。同理,
GPIO_TypeDef
结构体内其它变量的偏移量,也和相应的寄存器偏移地址相符。于是,只要我们匹配了结构体的首地址,就可以确定各寄存器的具体地址了。
欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/)
Powered by Discuz! 7.0.0