无论是新手还是大佬,基于STM32单片机的开发,使用STM32CubeMX都是可以极大提升开发效率的,并且其界面化的开发,也大大降低了新手对STM32单片机的开发门槛。
本文主要讲述STM32芯片的GPIO的配置及其相关知识。GPIO(全称General-purpose input/output)通用型输入输出,是单片机中使用最为广泛,也是最为简单的一种外设。
STM32CubeMX是ST官方出的一款针对ST的MCU/MPU跨平台的图形化工具,支持在Linux、MacOS、Window系统下开发,其对接的底层接口是HAL库,另外习惯于寄存器开发的同学们,也可以使用LL库。STM32CubeMX除了集成MCU/MPU的硬件抽象层,另外还集成了像RTOS,文件系统,USB,网络,显示,嵌入式AI等中间件,这样开发者就能够很轻松的完成MCU/MPU的底层驱动的配置,留出更多精力开发上层功能逻辑,能够更进一步提高了嵌入式开发效率。
演示版本 6.7.0
GPIO(全称General-purpose input/output)通用型输入输出,作为单片机最常用的一种外设,我们得先看下它有哪些功能。首先从芯片手册上,我们可以找到这么一张图。
从英文名称不难看出,GPIO的基本功能有两个,一个是I(Input)输入,一个是O(Output)输出。因为嵌入式软件是最贴近硬件的驱动级软件,所以这里需要涉及一点硬件的知识。其中最常讲的就是上、下拉、高阻态,推挽、开漏。
下图圈出来的部分就是上拉电路,其实就是对VCC接一个电阻。常态未接任何东西的情况下,直接使用万用表测量地跟上拉电阻下端的电压,测出来的电压值就是VCC。这就是上拉电路最重要的功能,将电压稳定在高电平。当输出端对地时,VCC通过上拉电阻对地形成回路,此时就会在回路上产生电流,上拉电阻就作为一个分流的作用。
对地接一个电阻,就变成了下拉电路。常态未接任何东西的情况下,直接使用万用表测量地跟下拉电阻上端的电压,测出来的电压值就是0V。这就是下拉电路最重要的功能,将电压稳定在低电平。当输出端对VCC时,VCC通过下拉电阻对地形成回路,此时就会在回路上产生电流,下拉电阻就作为一个分流的作用。
对地和对VCC的电阻很大(一般是M欧级别的),接近断连,此时即称为高阻态,又称浮空。对于STM32的GPIO端口,初始未配置的情况下,默认一般是高阻态。
直接由单片机内部的MOS管输出高低电平,驱动力有限。
需要外部接上拉电阻才能进行控制,好处是外部器件的驱动电流可完全由外部的电压和上拉电阻决定,驱动力强。
基本功能
STM32的GPIO基本功能有下面这么几种:模拟输入/输出,复用输入/输出,IO输入/输出。本文重点介绍IO输入输出。
复用模式是用于将该端口连接到单片机的其他外设上,如定时器、外部中断等。而模拟输入/输出模式,则是将该端口连接到单片机的ADC/DAC模块上。这两部分会在其他外设的篇章中单独提及。
GPIO的相关配置项,除了上面说的上、下拉模式,推挽、开漏模式,复用模式,还有速度的配置。GPIO的速度模式需要根据实际使用的翻转速度进行选择。
下面以点个开发板上的LED灯为例子进行配置。首先看下开发板上是LED灯电路,是GND接发光二极管直接接到单片机口。所以当单片机GPIO口输出高电平时,因为LED灯两边存在电势差,电流通过R31电阻,经过LD2到地,使LED灯亮;当输出低电平时,因为LED灯两端无电势差,电路不导通,LED灯灭。
根据原理图所示,这里将PA5引脚配置成"GPIO_OUTPUT",即普通输出口。
再在"System Core"选项中,找到"GPIO",选择PA5,配置成初始电平配置为低电平,推挽输出(驱动LED灯的电流不大,可以直接驱动),无上下拉,速度为低速。
配置为HAL或LL库,并配置好时钟,输出Keil工程,生成代码工程。
在生成的工程中添加如下代码,每500ms翻转一下灯的状态。
int main(void)
{
/* 省略初始化部分 */
......
while(1)
{
/* 单片机界的Hello world! */
/******** HAL库的实现--CubeMX中配置为HAL库 ********/
HAL_GPIO_WritePin(GPIOA, /* GPIOx,x为A~H */
GPIO_PIN_5, /* GPIO_PIN_x,x为0~15 */
GPIO_PIN_SET); /* GPIO_PIN_SET为高电平,GPIO_PIN_RESET为低电平 */
HAL_Delay(500); /* 延时500ms */
HAL_GPIO_WritePin(GPIOA,
GPIO_PIN_5,
GPIO_PIN_RESET);
HAL_Delay(500);
/********* LL库的实现--CubeMX中配置为LL库 ********/
#if 0
LL_GPIO_SetOutputPin(GPIOA,
LL_GPIO_PIN_5); /* 跟HAL库比,多了个LL的标识 */
HAL_Delay(500);
LL_GPIO_ResetOutputPin(GPIOA, LL_GPIO_PIN_5);
HAL_Delay(500);
#endif
}
}
效果展示
上面的配置+代码已经可以让LED灯自己闪动起来,现在我们要增加点自己的存在感,通过一个按键来控制灯闪——按下按键灯亮,放开灯灭。
同样的,先查看原理图,找到按键连接的GPIO口。先分析下这个电路,按键B1按下时,GPIO口连接到地,也就是逻辑信号变0;松开B1键时,GPIO口上拉至VDD,即逻辑信号变为1。
根据原理图所示,这里将PC13引脚配置成"GPIO_INPUT",即普通输入口。
再在"System Core"选项中,找到"GPIO",选择PC13,配置为输入模式,无上下拉。
然后将刚才闪灯的代码稍微改造一下。
int main(void)
{
/* 省略初始化部分 */
......
while(1)
{
/******** HAL库的实现--CubeMX中配置为HAL库 ********/
/* 判断按键为按下-输入端口变为逻辑0,则点亮灯 */
if (GPIO_PIN_RESET == HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13))
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
}
/* 否则灭灯 */
else
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
}
/********* LL库的实现--CubeMX中配置为LL库 ********/
#if 0
if (0 == LL_GPIO_IsInputPinSet(GPIOC, GPIO_PIN_13))
{
LL_GPIO_SetOutputPin(GPIOA, LL_GPIO_PIN_5);
}
else
{
LL_GPIO_ResetOutputPin(GPIOA, LL_GPIO_PIN_5);
}
#endif
}
}
效果展示
1、GPIO的配置跟硬件电路相关性较大,需要根据不同的电路特性进行配置,所以想要搞好单片机,还是得先从硬件基础走起。
2、对于高频使用,比如用GPIO模拟SWD接口时,需要把GPIO的速度调快,否则可能会因为一些电容特性导致波形失真。