目录
一. 前言
二. APB2外设时钟使能寄存器
三. GPIO端口的描述
四. GPIO端口使用案例
一. 前言
基于库函数的开发方式就是使用ST官方提供的封装好的函数。而如果没有添加库函数,那就是基于寄存器的开发方式,这种方式一般不是很推荐。因为由于ST对寄存器封装的比较好,所以使用库函数的方法,既能满足对寄存器的配置,对开发人员也比较友好,有利于提高开发效率。
并且提一下,我们所讲的都是以STM32F10单片机系列作为案例来讲的。
今天我们所讲的GPIO都是APB2的外设,所谓APB2也就是桥接1。
二. APB2外设时钟使能寄存器
因为我们GPIO都是APB2的外设,并且要想使用我们的STM32单片机,就都得先开时钟,这是为了降低能耗。这在我关于深入了解STM32单片机博客中也有提到的。
APB2外设复位寄存器也就是RCC_APB2ENR。我们通过查询手册就可以得到如下信息:
举个例子,当我们想开启GPIOC端口的时候,我们就可以这样设置:
RCC_APB2ENR=0x00000010;
这样就开启了GPIOC的时钟使能。因为我们这里总共有32位,用16进制表示就有8个数字,而GPIOC的位置在倒数第二个数,并且它的值为1。学会查看并使用STM32手册对于我们来说是非常重要的,我们都需要具备这种能力。
由于我们的GPIO口用不到APB1外设时钟使能寄存器,这里我就不介绍了,想了解的小伙伴可以去查询下STM32F10xxx参考手册。
三. GPIO端口的描述
每个GPIO端口都有两个32位配置寄存器(GPIOx_CRL,GPIOx_CRH),两个32位数据寄存器(GPIOx_IDR,GPIOx_ODR),一个32位置位/复位寄存器(GPIOx_BSRR),一个16位复位寄存器(GPIOx_BRR)和一个32位锁定寄存器(GPIOx_LCKR)。
关于以上函数的使用说明,我们都可以使用手册来查询,如下所示:
我们只需要在上面书签查找处输入我们想要查询的内容,就可以得到相关使用说明了。大家慢慢培养这个习惯,就会使用芯片参考手册了。
这里还需要讲下GPIO端口的八种输入输出模式,在输出模式下可控制端口输出高低电平,用以驱动LED,控制蜂鸣器,模拟通信协议输出时序等。在输入模式下则可读取端口的高低电平或电压,用于读取按键输入,外接模块电平信号输入,ADC电压采集,模拟通信协议接受数据等。
八种输入输出模式如下所示:
- 浮空输入:可读取引脚电平,若引脚悬空,则电平不确定。
- 上拉输入:可读取引脚电平,内部连接上拉电阻,悬空时默认高电平。
- 下拉输入:可读取引脚电平,内部连接下拉电阻,悬空时默认低电平。
- 模拟输入:GPIO无效,引脚直接接入内部ADC。
- 开漏输出:可输出引脚电平,高电平为高阻态,低电平接VSS
- 推挽输出:可输出引脚电平,高电平接VDD,低电平接VSS
- 复用开漏输出:由片上外设控制,高电平为高阻态,低电平接VSS
- 复用推挽输出:由片上外设控制,高电平接VDD,低电平接VSS
值得注意的是,一个端口只能有一个输出,但可以有多个输入,也就是说为输入模式时,输出模式无效。
提到GPIO的描述,就不得不提及GPIO的位结构了,关于GPIO详细的位结构,感兴趣的小伙伴可自行在网上查找,很容易找到的。这里我主要讲下几个难以让人理解的。在GPIO位结构中,接了两个保护二极管,这个是对输入电压进行限幅的。
其中施密特触发器的作用就是对输入电压进行整形的,它的执行逻辑是,如果输入电压大于某一阈值,输出就会瞬间升为高电平。反之则相反。并且对于施密特触发器来说,只有高于上限或者低于下限,输出才会变化。
其中的MOS管就是一种电子开关,我们的信号来控制开关的导通和关闭,开关负责将IO口接到VDD或者VSS。
四. GPIO端口使用案例
为了方便大家理解,我们这里举两个使用GPIO端口的示例。分别是LED流水灯和蜂鸣器的使用。
这里简单介绍下这两种东西,LED就是一种发光二极管,正向通电点亮,反向通电不亮。
蜂鸣器有两种,一种是有源蜂鸣器,另外一种则就是无源蜂鸣器。其中有源蜂鸣器就是内部自带振荡源,将正负极接上直流电压即可持续发声,频率固定。无源蜂鸣器则是内部不带振荡源,需要控制器提供振荡脉冲才可发声,调整提供振荡脉冲的频率,可发出不同频率的声音。
由于要使用到GPIO口,并且使用的是库函数的开发方式,所以就不得不提下关于GPIO口的几个常用库函数了。这里简要介绍下,给大家留个印象,如果想深入了解,可以查询手册。
首先是GPIO_DeInit()函数,是用来复位GPIO外设的。
GPIO_ResetBits()函数和GPIO_SetBits()函数是用来设置端口状态和初值的。
GPIO_WriteBit()和GPIO_Write()两个函数作用跟上面两个函数差不多。在使用时,可点击鼠标右键来查看函数参数该如何设置。
看到这里,不知道大家有没有注意到GPIO_ResetBits()等函数后面都有一个s,代表可以同时设置多个端口的状态。
下面我们来看下LED流水灯实现代码:
#include "stm32f10x.h" // Device header
#include "delay.h"
int main(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_All;
GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
while(1)
{
//LED流水灯闪烁代码
GPIO_Write(GPIOA,~0x0001); //0000 0000 0000 0001
Delay_ms(100);
GPIO_Write(GPIOA,~0x0002); //0000 0000 0000 0010
Delay_ms(100);
GPIO_Write(GPIOA,~0x0004); //0000 0000 0000 0100
Delay_ms(100);
GPIO_Write(GPIOA,~0x0008); //0000 0000 0000 1000
Delay_ms(100);
GPIO_Write(GPIOA,~0x0010); //0000 0000 0001 0000
Delay_ms(100);
GPIO_Write(GPIOA,~0x0021); //0000 0000 0010 0000
Delay_ms(100);
GPIO_Write(GPIOA,~0x0041); //0000 0000 0100 0000
Delay_ms(100);
GPIO_Write(GPIOA,~0x0081); //0000 0000 1000 0000
Delay_ms(100);
}
}
下面我们再来看下实际效果:
下面我们再来看下关于蜂鸣器的相关源代码:
#include "stm32f10x.h" // Device header
#include "delay.h"
int main(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
while(1)
{
GPIO_ResetBits(GPIOB,GPIO_Pin_12);
Delay_ms(100);
GPIO_SetBits(GPIOB,GPIO_Pin_12);
Delay_ms(100);
GPIO_ResetBits(GPIOB,GPIO_Pin_12);
Delay_ms(100);
GPIO_SetBits(GPIOB,GPIO_Pin_12);
Delay_ms(700);
}
}
至于关于STM32F10参考手册,我已经放在了百度网盘中,如下:
链接:https://pan.baidu.com/s/1zrtAtLYEEnBNZ1QsuvvdNQ
提取码:3wjv