这次主要来介绍一下STM32的GPIO,GPIO是通用IO口的意思,一般用来管理单片机的单个管脚的操作,也可以拿来模拟通信协议(比较麻烦,而且稳定性不如硬件),最常见的用处就用来操作LED灯和捕获按键状态。我们今天主要是操作LED灯。。。。
先上一张STM32关于GPIO的原理图:
看起来是比较复杂的,大概可以这样看左边是芯片的内部,右边是最外面暴露出来的真正的管脚。在管脚处有两个上下拉电阻,而再后面的部分,可以看成上面是输入,下面是输出。
单片机的IO口是有输入功能和输出功能的,并不是说输出功能就是输出电流,输入功能就是输入电流。我认为正确的说法应该是,输出状态下,单片机是主动的,它决定了管脚是高电平还是低电平,当输出高电平的时候往往会输出电流,而输出低电平的时候会输入电流;而输入状态下,单片机是被动的,由外界决定管脚的电平高低,这个时候一般是输入电流的,是不是有点像男生比较主动而女生比较被动的感觉。显然,单片机不可能同时处于输出又输入的状态!
其实,stm32的输出功能还更麻烦一些,有推挽和开漏两种模式,这里我就不从电路电路分析了,直接说结果,大概可以理解成,推挽(PP)可以输出高电平(拉电流),也可以输出低电平(灌电流);而开漏(OD)只能输出低电平(灌电流),它输出高电平是处于高阻态的。开漏其实可以看成一个与地相连接的开关,当开关闭合(输出低)时,把管脚与地相连,当开关断开(输出高)时,管脚悬空,这时外界可以很容易地改变这个管脚的电平,也可以说这个管脚的电平处于不稳定状态。
stm32的输入功能,也分成模拟输入和数字输入两种情况,当使用ADC功能的时候,要配置成模拟输入。
至于当使用管脚复用功能时,管脚基本是由硬件功能自己配置的,基本不用我们管。
接下来,我们来看看如何具体操作。
如果使用的是上一个教程建好的工程的话,需要做一些处理,如果只是单纯的想学GPIO的话可以跳过这部分,这样做只是更为了更优雅的编程而已。
1、首先,按照上一个教程把晶振设置好。
2、新建一个Global.h在User目录下,并添加到工程里。添加文件到工程里可以使用工具栏上的文件管理的图标,也可以在工程目录(左边那个)的响应目录上双击,然后选择文件。
在Global.h里写入以下代码
#ifndef _GLOBAL_H
#define _GLOBAL_H
#include "main.h"
#include "stm32f4xx.h"
#include "Bsp.h"
#endif
这是典型的H文件的写法,前两行和最后一行是为了防止重复包含,以后当工程里面添加其他的H文件时,就在那个H文件里面包含Global.h,然后在Global.h里面包含那个文件,这样就可以全面包含,使全局函数在随处可调了。
3、把main.c换成以下内容,就是把大部分东西都删了就可以了
#include "main.h"
int main(void)
{
while(1)
{
}
}
把main.h换成以下内容,只需要包含Global.h就可以了
#ifndef __MAIN_H
#define __MAIN_H
#include "Global.h"
#endif /* __MAIN_H */
4、然后需要修改Driver目录下的stm32f4xx_it.c里的SysTick_Handle()r函数,官方默认的这个函数里面调用了TimingDelay_Decrement(),这个函数在main.c里面定义,已经背我们删掉了,所以这里也需要删掉TimingDelay_Decrement()。
然后编译,应该是没有error,没有waring的。
5、在Driver目录下新建Bsp.c和Bsp.h两个文件,添加到工程里,并写入以下内容
Bsp.h
#ifndef _BSP_H
#define _BSP_H
#include "Global.h"
void Bsp_init(void);
static Bsp_gpioInit(void);
#endif
Bsp.c
#include "Bsp.h"
void Bsp_init(void)
{
Bsp_gpioInit();
}
static Bsp_gpioInit(void)
{
}
这样对工程的修整就差不多了。
接下来,我们就具体来编写GPIO部分的代码,只看GPIO的小伙伴们可以从这里开始。
首先,从我的原理图中可知我的板子的LED连接到了PD12-PD15
所以我就初始化这几个引脚
在Bsp_gpioInit函数里编写关于GPIO的初始化的部分。
static Bsp_gpioInit(void)
{
GPIO_InitTypeDef gpioInitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
gpioInitStructure.GPIO_Mode = GPIO_Mode_OUT;
gpioInitStructure.GPIO_OType = GPIO_OType_PP;
gpioInitStructure.GPIO_Speed = GPIO_Speed_2MHz;
gpioInitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_Init(GPIOD, &gpioInitStructure);
}
简单地说一下,第一句是定义一个GPIO的初始化类型变量,STM32基本为每种外设都设计了初始化类型。
然后是初始化时钟,使用大部分的模块前都需要初始化它的时钟。
然后就是配置那个初始化类型,首先是设置为输出(我们要驱动LED灯,显然单片机要主动),然后输出模式是推挽,由于我的板子的LED灯接法,开漏是亮不了滴,即需要输出高电平LED才会亮。再接着是配置管脚速度,2M基本够用,跑协议的话要快一些。再接着设置要初始化的管脚,就是LED对应的那个几个管脚。最后,调用初始化函数,初始化变量前面的&符合别漏了哦。
然后去main函数调用初始化函数,再调用一下写管脚高的指令,LED就可以亮起来了。
int main(void)
{
Bsp_init();
GPIO_WriteBit(GPIOD, GPIO_Pin_12, Bit_SET);
GPIO_WriteBit(GPIOD, GPIO_Pin_13, Bit_SET);
GPIO_WriteBit(GPIOD, GPIO_Pin_14, Bit_RESET);
GPIO_WriteBit(GPIOD, GPIO_Pin_15, Bit_RESET);
while(1)
{
}
}
由于我的LED是共阴接法,所以,当管脚设置成Bit_SET时亮,设置成Bit_RESET时灭。
GPIO还是很简单的,其它常用的函数大家可以参考技术手册,都是一句话的事。
效果图