本文主要讲解如何驱动GPIO外设的相应寄存器搭建GPIO的工作环境,牵及的各种代码本人都会逐条进行讲解。主控芯片采用STM32F103C8T6,外设采用普通的LED发光二极管。驱动LED发光二极管显示GPIO的工作状态。因本人的知识有限,文章中难免有错误,还请读者们谅解。在此本人非常希望读者能反馈错误,以便日后写出的文章更好。
文中出现的术语含义如下:
(1)外设:该术语如果出现在芯片外设中表示寄存器组,如出现在芯片外则表示外部设备。像LED、按键、显示屏等。
(2)内核外设:属于内核(CPU)中的外设。
(3)片上外设:属于内核(CPU)外的外设,即芯片厂家设计的各种寄存器组,如下面要讲的GPIO,还有像RCC、EXTI、USART/UART、I2C、SPI等。
(4)片外外设:属于芯片外的设备,如LED、按键、显示屏。
统一称谓:在讲解各芯片的片上外设时,都简称为外设。除开片上外设,其他外设都统称术语。
GPIO即通用输入输出端口,是STM32单片机中最重要也是最基本的外设之一,GPIO起到了与外部设备通信的作用。GPIO输入(检测)或输出(驱动)控制着STM32的可编程引脚的电平变换,从而驱动各个片外外设工作。GPIO除了可以控制可编程引脚,还支持复用功能。即与外设建立起链接作用,如一些外设需要与外界通信。像EXTI、ADC、USART等。
本文只介绍GPIO控制引脚输出相应的高低电平。
EXTI外部中断/事件控制器:需与GPIOA~G_Pinx 建立起链接关系。
ADC模拟数字转换器:把外界时时变化的模拟信号转换成STM32可处理的数字信号。
USART通用同步异步收发器:即串行口,是一个非常常用的一种通信方式。
GPIO框图如下:
该图片剪取于STM32F10x参考手册中的第8.1节GPIO功能描述
(1)输入讲解:输入模式有4种。1.上拉输入,2.下拉输入,3.浮空输入,4.模拟输入。
1.1 上拉输入
输入信号经过两个钳位二极管,上方进行过压保护,下方的进行欠压保护。之后来到两个电阻,上电阻为上拉电阻,下电阻为下拉电阻。现在设置为上拉输入,上拉电阻的开关将闭合。再到TTL肖特基触发器,到触发器之前有一路到模拟输入去了,这是通向ADC的在这不讨论。经过触发器有一路送到复用功能中,在这我们不讨论复用功能。再一路送到了GPIO中的IDR数据输入寄存器中。用户可驱动该寄存器读出数据。
1.2 下拉输入
下拉输入的信号流向与上拉输入一样的。但是有一点不同,下拉输入是下面的电阻开关闭合工作,上拉输入是上面的电阻工作。
1.3 浮空输入
浮空输入的信号流向与上下拉输入一样,但是开启浮空输入上下拉电阻都不闭合,输入状态随片外外设而定。
1.4.模拟输入
唯一一个输入信号流向不与先前所提的3种一样。上下拉电阻不闭合工作,信号流不经过TTL肖特基触发器,直接送到了模拟输入。即输入如何,检测就如何。此功能常用作ADC采集外界模拟信号。
(2)输出讲解: 输出模式有4种。1.推挽输出,2.开漏输出,3.复用推挽输出,4复用开漏输出。
2.1.推挽输出
数据写入到位设置/清除寄存器BSRR或数据输出寄存器ODR,ODR寄存器是可读可写的。之后送到一个选择开关中,选择开关还有一路由复用功能送来。再送到输出控制单元,控制单元类似于一个非门。当输入到控制单元中的信号为1时输出为0,上方的P-MOS管工作。输入为0,输出为1下方的N-MOS管工作。两管子轮流工作形成灌电流(P-MOS)与拉电流(N-MOS)。之后信号输出到引脚驱动外部设备。推挽输出模式具有驱动能力强。
2.2.开漏输出:
开漏输出模式在数据的写入,流向都与推挽输出一致。但是开漏输出只有下方的N-MOS管工作,无论内部送来的信号电平如何变化,输出都呈高阻态。在把开漏输出模式作为普通IO口使用时,必须外接上拉或下拉电阻。开漏输出具有电平兼容功能(外部上拉电阻可接+5v作为上拉电源)。
2.3.复用推挽输出
复用推挽输出的工作方式与普通推挽一致。只是输入源来自片上的一些外设,如EXTI,USART/UART等。
2.4.复用开漏输出:
复用开漏输出的工作方式与普通开漏一致。只是输入源来自片上的一些外设,如EXTI,USART/UART等。
在前面讲解了GPIO框图的信号流向,使读者大致了解了GPIO的工作方式。这里我们将学习如何控制GPIO的寄存器,以及一些必要的寄存器。
在讲解驱动GPIO寄存器之前我们先来了解一下STM32F103中的时钟。学过单片机的读者一定不会对时钟感到陌生,如典型的51单片机(8位)最高支持40MHz的时钟频率,典型的为11.0592MHz。51单片机的时钟理解起来比较简单,差不多全程采用相同的时钟频率工作。而STM32单片机的时钟就显得复杂很多,牵及到时钟频率的倍频分频以及选择。因为每一些外设使用的时钟频率都不一样。下面我们讲解常用的3大总线的常见时钟频率。
(1)AHB总线:即系统总线,管理分配各个外设的时钟来源除开USB。时钟频率最高为72MHz。
(2)APB1总线:低速外设总线。时钟频率最高为36MHz。
(3)APB2总线:高速外设总线。在STM32F103中全部的GPIO都悬挂在该总线中。时钟频率最高为72MHz。
这里我们不详细介绍时钟频率是如何分配供给各外设的,因为这已经超出了我们讨论的范畴
在STM32F103启动时,首先会进入启动文件中配置基本的运行环境。如初始化栈堆指针,用户程序指针,中断向量表,调用 SystemInit ()时钟配置函数等。 SystemInit ()函数在system_stm32f103.c文件中。该函数缺省值为:AHB=72MHz,APB1=36MHz,APB2=72MHz。其它时钟频率清查阅该函数。
(1)设置相应外设的时钟源。
(2)设置外设的工作参数。
设置RCC时驱动的寄存器有:RCC_APB2ENR
RCC_APB2ENR:APB2外设时钟使能寄存器。
在驱动RCC_APB2ENR的寄存器位中,只设置位2 GPIOA的时钟。
具体其它位功能请查阅STM32F10x中文参考手册时钟章节第7.3.7小节
设置GPIO时需驱动的寄存器有:CRL/CRH,ODR,BSRR,BRR。
CRL/CRH:低/高配置寄存器,用来配置GPIO的工作模式以及输出速度。如图2.4-1所示。
ODR:数据输出寄存器,用来驱动GPIO的输出状态。可读可写。如图2.4-2所示。
BSRR:位设置/清除寄存器,用来驱动GPIO的输出状态。只可写。
BRR:位清除寄存器,用来驱动GPIO的输出状态只能输出低电平。只可写。
图2.4-1
该图截取于STM32F10x参考手册GPIO章节的8.2.1
该寄存器属于端口配置低位寄存器CRL,控制着低8位GPIO口。高位寄存器CRH负责控制高8位GPIO口。两个寄存器的参数是一致的。设置的参数分析如下:
该寄存器4位控制着1个GPIO端口。MODEy[1:0]控制着端口的输出速度(频率),CNFy[1:0]控制着端口的工作模式,如输入/输出。下面我们来配置一下,我们把GPIOx_Pin_0(x=A~G)配置成输出速度为10MHz的通用推挽输出模式。我们先配置输出速度MODEy这里配置为01,再来配置工作模式CNFx这里为00。
在设置工作模式为输出时,是判断MODEy是否 >00,如大于则为输出模式,等于为输入模式。这里设置的MODEy大于0,所以为输出模式。总参数为0001,输出速度为10MHz的通用推挽输出模式。
注意:在设置工作模式为上拉/下拉输入模式时,该参数由GPIO_Init()初始化函数单独驱动BSRR和BRR寄存器实现。
图2.4-2
该图截取于STM32F10x参考手册GPIO章节的8.2.4
该寄存器负责控制(写入)和检测(读取)GPIO端口的状态(电平高低)。往ODR寄存器中写1输出高电平,写0输出低电平。每个位控制一个GPIO端口。
图2.4-3
该图截取于STM32F10x参考手册GPIO章节的8.2.5
该寄存器也是控制GPIO端口的电平状态只可写。往位【31:16】写1清0,写0不影响ODR位的状态,即ODR的位原值不变。【位15:0】BSy:写1置1,写1不影响ODR位的状态。
图2.4-4
该寄存器和BSRR差不多,只是把BSy置1位剪切掉了。保留了BRy清0位。每1位控制着1位GPIO端口。
下面我们采用官方固件库的方式来搭建编程环境。
所牵及的官方库文件:
(1)startup_stm32f10x_md.s 该文件属于汇编语言文件。里面包含了启动时需配置的参数,此文件也称为启动文件。不同型号,容量的STM32单片机需包含的启动文件都不一样。
*启动文件初始化的参数有:1 初始化堆栈指针,2 初始化程序指针,3 初始化中断向量表,设置时钟环境,调用库函数_main,最后进入c函数main。
(2)stm32f10x.h该文件包含了STM32F10x的所有寄存器映象以及一些宏定义,结构,枚举类型。
(3)system_stm32f10x.h和system_stm32f10x.c这两个文件用来初始化STM32时钟环境的。
默认配置为PCLK=72MHz,PCLK1=36MHz,PCLK2=72MHz
(4)stm32f10x_rcc.h和stm32f10x_rcc.c这两个文件用来设置各个外设的时钟源的。
该文件与第2点提到的有些区别,一个是配置时钟的环境,一个是控制各个外设的时钟开启或关闭
(5)stm32f10x_gpio.h和stm32f10x_gpio.h这两个文件用来配置GPIO外设的工作环境。
所牵及的库函数这里不做详细讲解
用户编写的文件:
(1)bsp_led.h和bsp_led.c这两个文件用来设置GPIO的工作环境。
(2)main.c这个文件用来设置LED的工作方式。
bsp_led.h
#ifndef __BSP_LED_H__
#define __BSP_LED_H__
#include "stm32f10x.h"
/* LED工作环境设置 */
void LED_GPIO_Config(void);
#endef /* __BSP_LED_H__ */
bsp_led.c
/******************* GPIOA端口的参数配置函数 ********************/
#include "bsp_led.h"
void LED_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure ;
/* 开启GPIO_A的时钟 */
RCC_APB2PeriphClockCmd(GPIOA, ENABLE);
/* 选择GPIO_0 端口 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 ;
/* 工作模式为 推挽式输出 */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;
/* 输出速度为 10MHz */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz ;
/* 初始化GPIOA端口 */
GPIO_Init(GPIOA , &GPIO_InitStructure);
}
GPIO_InitStructure.GPIO_Pin:用来指定哪个IO端口。以便在配置工作状态时能确定需驱动的寄存器位。如需配置Pin_0引脚的工作模式,这样根据Pin_0所对应的CRL相应的4个位就可以设置相应的IO端口工作模式。也可用作控制IO端口的工作状态。如控制Pin_1输出高电平,这样我们可以把该宏写如BSRR寄存器位1 中。GPIO_Pin_x对应x引脚为高电平,如Pin_0对应编号为0的引脚为高电平
GPIO_InitStructure.GPIO_Mode:设置GPIO的工作模式4输入4输出。在前面的GPIO框图中已做讲解。
GPIO_InitStructure.GPIO_Speed:设置输出速度。主要是配置CRL/CRH寄存器的CNFx[1:0]位。
最后调用GPIO参数初始化函数GPIO_Init写入以上参数到GPIO相应的寄存器中,该函数的配置过程这里不做讲解。
参数配置结构体与参数初始化函数结合使用是库开发的精髓所在
main.c
#include "bsp_led.h"
/* 延迟函数 */
void Delay(__IO u32 nCount );
void main(void)
{
LED_GPIO_Config();
while(1)
{
/* 置一GPIO端口 */
GPIOA->BSRR = GPIO_Pin_0;
Delay(0xFFFFF);
/* 清零GPIO端口 */
GPIOA->BRR = GPIO_Pin_0;
Delay(0xFFFFF);
}
}
void Delay(__IO u32 nCount )
{
for(; nCount > 0; nCount--);
}
(1)先使读者了解作者编写该文章的目的以及文章中出现些相关术语的介绍。
(2)之后带读者解开GPIO框图的工作流程。
(3)在了解GPIO框图之后,再使读者认识GPIO的相关寄存器的介绍和配置。
(4)最后是牵及需添加的官方库文件(函数),以及用户要编写文件。