第二章 GPIO(HAL版本)
前言
GPIO口的控制可以说一切实际功能的基石,从本文开始着重讲解实际工程使用
提示:以下是本篇文章正文内容,下面案例可供参考
一、实际开发GPIO口准备工作
现在STM32开发有CubeMX来快速配置外设,但是我本人在实际开发中是很少用到STM32CubeMX,它不是不好用,为啥不用呢?其一是ST的芯片太贵了、其二呢HAL库太大了对比固件库会慢一点、其三国内容易断货。
在做一个完整的工程时,会用到大量的外设,例如:定时器生成PWM、USART、SPI、IIC等等,在考虑经济性下,一般工程中MCU的管脚都是会用的七七八八,在工程开始的时候,我们就得先把在工程中会用到的外设的管脚全部都定好。话不多说上图。
我们先看Pin name可以看到我们管脚,IO的Type呢一般都不用去理他,我们看到I/O level这里标注了FT说明这个管脚时兼容5V的,我们再往后面也看到Alternate functions(备用功能)项,这里分为了两类,一个是默认功能,一个是复用功能。假如我们需要使用ADC12的通道10,这时候我们通过上表可以看到PC0的默认功能就是ADC_IN10,以此类推,我所见过ARM单片机说明书基本都是这样的。这时候就会有人不信了,好我们再上雅特力的管脚图。
二、GPIO口HAL库配置
1.HAL库配置
假设我们现在需要使用PC0来做电源指示灯的拉高。
我们左键点击PA1后会提示这个我们按照这个提示选择GPIO_Output,说一个题外话在工程中GPIO_Input和GPIO_Output是用的最多的,在后续的文章中会详细的解说。言归正传选好后会提示如下图。
我们点击PC0,会提示如下图。
这里有四个参数一个是初始化电平(GPIO output level)、GPIO口模式(GPIO mode)、GSIO Pull-up(上下拉)、还有一个输出速度。最后的User Label是管脚用户定义最后生成代码。
2.HAL库版本初始化GPIO
代码如下(示例):
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/*Configure GPIO pin Output Level */
//初始化电平,GPIO_PIN_RESET初始化为低电平,GPIO_PIN_SET为高电平
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET);
/*Configure GPIO pin : PA0 */
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;//设置为推挽输出
GPIO_InitStruct.Pull = GPIO_NOPULL;//浮空状态
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;//低速IO口
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
这里可以着重看HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET);这句话,这句是我们使用最多的,拉高拉低IO口都是这个函数,我们来看看这个函数原型。
void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
{
/* Check the parameters */
/*assert_param为断言机制*/
assert_param(IS_GPIO_PIN(GPIO_Pin));
assert_param(IS_GPIO_PIN_ACTION(PinState));
if (PinState != GPIO_PIN_RESET)
{
GPIOx->BSRR = GPIO_Pin;
}
else
{
GPIOx->BSRR = (uint32_t)GPIO_Pin << 16u;
}
}
除去断言机制,后只剩下一个if条件语句
if (PinState != GPIO_PIN_RESET)
{
GPIOx->BSRR = GPIO_Pin;
}
else
{
GPIOx->BSRR = (uint32_t)GPIO_Pin << 16u;
}
我们可以看到GPIOx(x代表端口)BSRR为寄存器。说明了我们修改BSRR寄存器就可以改变IO口电平,假如我们要拉高PB0。代码是这样的BPIOB->BSRR = GPIO_Pin。
有的人又要说这个GPIO_Pin到底是啥,我们一起来看看。
typedef enum
{
GPIO_PIN_RESET = 0u,
GPIO_PIN_SET
} GPIO_PinState;
这个就是IO状态的枚举,这里有一句GPIO_PIN_RESET = 0u,这里的0u中的u代表无符号。
有的人就要问了为啥要说这个函数原型,因为我们调用函数时需要时间的,这里的调用是指进入函数到执行函数都是需要时间的,如果我们直接操作寄存器回避调用完整的函数要快。
我们举个例子,你要进入房间,如果房间门是关上的,那你是不是得开门,这个开门的动作是不是需要时间。如果我们的房门是开着的,是不是会快一些
三、总结
我们在实际情况下使用HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET);是最多的,例如我们的电源指示灯,我们的模拟PWM等等。