一、LED灯点亮
(1)点亮一个LED
GPIO(general porpose intput output):通用输入输出端口的简称。可以通过软件控制其输出和输入。stm32芯片的GPIO引脚与外部设备连接起来,从而实现与外部通信,控制以及数据采集的功能。
(1)GPIO口的时钟开启和设置
在stm32中,为了节约性能和效率,其IO口等端口是默认禁止时钟的,如果我们在没有开启时钟的时候操作IO口是没有作用的,因此我们先开启IO口的时钟,在stm32中,点击x_rcc.h文件,打开时钟头文件,拉到最下端,可以找到我们常用的三个函数
我们可以鼠标右键点击到跳转函数定义中
我们可以发现,所有的GPIO口的时钟开启都在RCC_APB2PeriphClock函数中
我们stm32f103c8t6只有GPIOA和GPIOB这两个IO口,如果开启GPIOA的IO口时钟,我们要将LED灯连接在PAx口上,如果开启GPIOB这个IO口的时钟,LED应连接在PBx口上;
同时我们根据函数的定义可以知道函数的两个参数是控制的端口和是否使能,所以我们第一个参数给RCC_APD2Pperiph_GPIOA,同时使能选择ENABLE,使能GPIOA时钟
#include "stm32f10x.h" // Device header
#include "Delay.h"
int main()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //open clock
while(1)
{
}
return 0;
}
这样我们就使能了GPIOA的时钟,接下来我们就对GPIOA的端口进行初始化,同样,我们找到x_gpio.h文件,拉到最下端,找到GPIO_Init这个函数,跳转到定义
同样可以看到,这里有两个参数,第一个参数是要初始化的GPIOx端口,这里我们刚刚才打开GPIOA的时钟,自然是要使用GPIOA端口,因此第一个参数我们选择GPIOA,第二个参数是一个结构体类型,我们鼠标点击这个结构体GPIO_InitTypeDef,右键跳转到定义看一下
这里可以看到结构体重有三个参数,第一个参数是GPIO_Pin,表示要初始化的哪个端口,这里我们将LED插在PA1口上,因此我们选择初始化PA1口
因此我们GPIO_Pin应给GPIO_Pin_1,接着我们看下第二个变量,我们用一样的方法,右键变量点击跳转到定义
可以知道GPIO_Speed这个参数是选择GPIO的输出或翻转速度的,这里我们选择多少根据情况而定,但是点亮LED灯对翻转速度没有什么要求,因此我们选择10MHz的翻转速度,即GPIO_Speed=GPIO_Speed_10MHz;
接着我们使用同样的方法来查看第三个变量GPIO_Mode的信息
这个变量是GPIO的输入输出模式的选择,总共有八种输入输出模式,分别为
模拟输入:AIN(Analog input)一般用于AD转化种
浮空输入:IN_FLOATING,高低电平都有效
下拉输入:IPD(In Pull Down)高电平有效,默认低电平
上拉输入:IPU(In Pull Down)低电平有效,默认高电平,常用于通信协议中
开漏输出:Out_OD(Out Open Drain)输出低电平才有驱动能力,输出高电平没有驱动能力
推挽输出: Out_PP(Out Push Pull)输出的高低电平都有驱动能力
复用开漏:AF_OD(Atl Open Drain)
推挽输出:AF_PP(ATL Push Pull)
我们的LED可以一端接电源正极,一端接PA1口,这样我们使用的就是低电平驱动,使用开漏输出和推挽输出都可以,如果LED一端接电源负极,一端接PA1口,这样使用的就是高电平点亮,只能使用推挽输出,因为开漏输出是没有驱动能力的,因此我们第三个变量选择推挽输出,即GPIO_Mode=GPIO_Mode_Out_PP
这样就可以初始化一个GPIO端口了
#include "stm32f10x.h" // Device header
#include "Delay.h"
int main()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //open clock
GPIO_InitTypeDef gpio_init;
gpio_init.GPIO_Pin = GPIO_Pin_1;
gpio_init.GPIO_Speed = GPIO_Speed_10MHz;
gpio_init.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &gpio_init);
while(1)
{
}
return 0;
}
注意,这里第二个参数并不是要传递一个结构体,而是要传递一个结构体指针,因此我们对设置的结构体取地址作为第二个参数;
(2)点亮LED灯并让其闪烁
配置好GPIO口后,点亮一个LED灯就变得很简单了,我们先来看一下GPIO还有哪些常用的函数,同样打开gpio的头文件,下面还有几个常用的函数
同样可以用之前的方法查看函数的作用,这几个函数都是操作IO口的,我们先使用GPIO_SetBits和GPIO_ResetBits两个函数来实现一个闪烁的LED灯
#include "stm32f10x.h" // Device header
#include "Delay.h"
int main()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //open clock
GPIO_InitTypeDef gpio_init;
gpio_init.GPIO_Pin = GPIO_Pin_1;
gpio_init.GPIO_Speed = GPIO_Speed_10MHz;
gpio_init.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &gpio_init);
while(1)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_1);
Delay_ms(300);
GPIO_SetBits(GPIOA, GPIO_Pin_1);
Delay_ms(300);
}
return 0;
}
这样一个闪烁的LED灯就写好了,只要我们把LED灯正极接在电源上,负极接在PA1口上,就可以实现周期为600ms的闪烁
(3)流水灯
如果我们把多排LED灯连接到GPIO口上,再对对应的IO口进行初始化和赋值操作,那么我们就可以操作一个流水灯了,我们以200ms为间隔依次点亮五个LED灯
int main()
{
unsigned char i;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef gpio_init;
gpio_init.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5;
gpio_init.GPIO_Mode = GPIO_Mode_Out_PP;
gpio_init.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(GPIOA, &gpio_init);
while(1)
{
for (i = 1; i <= 5; i++)
{
GPIO_Write(GPIOA, ~(0x0001<<i));
Delay_ms(200);
}
for (i = 5; i >= 1; i--)
{
GPIO_Write(GPIOA, ~(0x0001<<i));
Delay_ms(200);
}
}
return 0;
}
这里有一个技巧,就是在初始化时,如果我们每次只对一个端口进行初始化那代码就十分冗余,我们可以用或的方式同时初始化多个IO端口,这里注意一下PA0口的地址是0x0001,因此我们的循环语句要注意开始和结束的条件
总结
通过点亮LED灯,我们基本了解到了GPIO的几种输出模式,初步认识了单片机的基本工作方式,现在我们应该可以自由地控制单片机的GPIO口输出,接下来我们将了解GPIO口的输入,通过我们传递的信号来控制单片机