有一段时间没有接触RT-Thread了, rtt网站做得更漂亮了,RT-Thread Studio也非常好用,文档资源也更丰富清晰简洁了(ps:也许是我变强了,幸好发量还可以)。
最近的工作中,要做一个STM32的控制设备,所以,又把rtt捡起来再认真学习一遍。
今天遇到一个跟PIN设备STM32的drv_gpio.c底仓代码相关问题,于是水上一帖。
PIN设备确实好用,减少了来回查看寄存器的麻烦。
问题描述
我使用PIN设备,开启了几个IO口的外部中断作为按键检测,由于STM32只支持边沿跳变中断,那么为了消抖,我就在中断触发后,中断处理程序中,先关闭中断,然后再使用信号量,唤醒消抖线程,进行IO的轮询操作。
我的按键是下降沿触发的,所以,在初始化的时候,把IO口初始化为上拉输入模式。
中断触发都很正常。
可是在IO轮询的过程中,发现大概率会出现IO口不为高电平的状况,实际测量电压发现,电压都是一个中间电平零点几伏左右,推测是进入了浮空输入状态。
经过一番折腾,确认,问题确定出在PIN设备的中断使能与禁止这里。
内核 版本 4.0.2:

中断使能禁止函数:
使能部分:

禁止部分:

好家伙! 原来在调用 rt_pin_irq_enable -> stm32_pin_irq_enable 的时候,
ENABLE会再次进行IO口的设置;DISABLE 会恢复IO口状态为复位后的浮空输入状态。
那我调用 rt_pin_mode 是调用了个寂寞?
所以,对于此处的代码,我认为是不够合理的。
最不合理的地方是:
/* Configure GPIO_InitStructure */
GPIO_InitStruct.Pin = index->pin;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
switch (pin_irq_hdr_tab[irqindex].mode)
{
case PIN_IRQ_MODE_RISING:
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
break;
case PIN_IRQ_MODE_FALLING:
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
break;
case PIN_IRQ_MODE_RISING_FALLING:
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING;
break;
}
HAL_GPIO_Init(index->gpio, &GPIO_InitStruct);
这代码太智能了,直接根据你选择的边沿模式给你上下拉电阻安排的妥妥的。
那么我们可以来看一下 STM32的 HAL层库是如何构建的。
/**
* @brief Initializes the GPIOx peripheral according to the specified parameters in the GPIO_Init.
* @param GPIOx: where x can be (A..G depending on device used) to select the GPIO peripheral
* @param GPIO_Init: pointer to a GPIO_InitTypeDef structure that contains
* the configuration information for the specified GPIO peripheral.
* @retval None
*/
void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init)
{
uint32_t position = 0x00u;
uint32_t ioposition;
uint32_t iocurrent;
uint32_t temp;
uint32_t config = 0x00u;
__IO uint32_t *configregister; /* Store the address of CRL or CRH register based on pin number */
uint32_t registeroffset; /* offset used during computation of CNF and MODE bits placement inside CRL or CRH register */
/* Check the parameters */
assert_param(IS_GPIO_ALL_INSTANCE(GPIOx));
assert_param(IS_GPIO_PIN(GPIO_Init->Pin));
assert_param(IS_GPIO_MODE(GPIO_Init->Mode));
/* Configure the port pins */
while (((GPIO_Init->Pin) >> position) != 0x00u)
{
/* Get the IO position */
ioposition = (0x01uL << position);
/* Get the current IO position */
iocurrent = (uint32_t)(GPIO_Init->Pin) & ioposition;
if (iocurrent == ioposition)
{
/* Check the Alternate function parameters */
assert_param(IS_GPIO_AF_INSTANCE(GPIOx));
/* Based on the required mode, filling config variable with MODEy[1:0] and CNFy[3:2] corresponding bits */
switch (GPIO_Init->Mode)
{
/* If we are configuring the pin in OUTPUT push-pull mode */
case GPIO_MODE_OUTPUT_PP:
/* Check the GPIO speed parameter */
assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
config = GPIO_Init->Speed + GPIO_CR_CNF_GP_OUTPUT_PP;
break;
/* If we are configuring the pin in OUTPUT open-drain mode */
case GPIO_MODE_OUTPUT_OD:
/* Check the GPIO speed parameter */
assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
config = GPIO_Init->Speed + GPIO_CR_CNF_GP_OUTPUT_OD;
break;
/* If we are configuring the pin in ALTERNATE FUNCTION push-pull mode */
case GPIO_MODE_AF_PP:
/* Check the GPIO speed parameter */
assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
config = GPIO_Init->Speed + GPIO_CR_CNF_AF_OUTPUT_PP;
break;
/* If we are configuring the pin in ALTERNATE FUNCTION open-drain mode */
case GPIO_MODE_AF_OD:
/* Check the GPIO speed parameter */
assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
config = GPIO_Init->Speed + GPIO_CR_CNF_AF_OUTPUT_OD;
break;
/* If we are configuring the pin in INPUT (also applicable to EVENT and IT mode) */
case GPIO_MODE_INPUT:
case GPIO_MODE_IT_RISING:
case GPIO_MODE_IT_FALLING:
case GPIO_MODE_IT_RISING_FALLING:
case GPIO_MODE_EVT_RISING:
case GPIO_MODE_EVT_FALLING:
case GPIO_MODE_EVT_RISING_FALLING:
/* Check the GPIO pull parameter */
assert_param(IS_GPIO_PULL(GPIO_Init->Pull));
if (GPIO_Init->Pull == GPIO_NOPULL)
{
config = GPIO_CR_MODE_INPUT + GPIO_CR_CNF_INPUT_FLOATING;
}
else if (GPIO_Init->Pull == GPIO_PULLUP)
{
config = GPIO_CR_MODE_INPUT + GPIO_CR_CNF_INPUT_PU_PD;
/* Set the corresponding ODR bit */
GPIOx->BSRR = ioposition;
}
else /* GPIO_PULLDOWN */
{
config = GPIO_CR_MODE_INPUT + GPIO_CR_CNF_INPUT_PU_PD;
/* Reset the corresponding ODR bit */
GPIOx->BRR = ioposition;
}
break;
/* If we are configuring the pin in INPUT analog mode */
case GPIO_MODE_ANALOG:
config = GPIO_CR_MODE_INPUT + GPIO_CR_CNF_ANALOG;
break;
/* Parameters are checked with assert_param */
default:
break;
}
/* Check if the current bit belongs to first half or last half of the pin count number
in order to address CRH or CRL register*/
configregister = (iocurrent < GPIO_PIN_8) ? &GPIOx->CRL : &GPIOx->CRH;
registeroffset = (iocurrent < GPIO_PIN_8) ? (position << 2u) : ((position - 8u) << 2u);
/* Apply the new configuration of the pin to the register */
MODIFY_REG((*configregister), ((GPIO_CRL_MODE0 | GPIO_CRL_CNF0) << registeroffset), (config << registeroffset));
/*--------------------- EXTI Mode Configuration ------------------------*/
/* Configure the External Interrupt or event for the current IO */
if ((GPIO_Init->Mode & EXTI_MODE) == EXTI_MODE)
{
/* Enable AFIO Clock */
__HAL_RCC_AFIO_CLK_ENABLE();
temp = AFIO->EXTICR[position >> 2u];
CLEAR_BIT(temp, (0x0Fu) << (4u * (position & 0x03u)));
SET_BIT(temp, (GPIO_GET_INDEX(GPIOx)) << (4u * (position & 0x03u)));
AFIO->EXTICR[position >> 2u] = temp;
/* Configure the interrupt mask */
if ((GPIO_Init->Mode & GPIO_MODE_IT) == GPIO_MODE_IT)
{
SET_BIT(EXTI->IMR, iocurrent);
}
else
{
CLEAR_BIT(EXTI->IMR, iocurrent);
}
/* Configure the event mask */
if ((GPIO_Init->Mode & GPIO_MODE_EVT) == GPIO_MODE_EVT)
{
SET_BIT(EXTI->EMR, iocurrent);
}
else
{
CLEAR_BIT(EXTI->EMR, iocurrent);
}
/* Enable or disable the rising trigger */
if ((GPIO_Init->Mode & RISING_EDGE) == RISING_EDGE)
{
SET_BIT(EXTI->RTSR, iocurrent);
}
else
{
CLEAR_BIT(EXTI->RTSR, iocurrent);
}
/* Enable or disable the falling trigger */
if ((GPIO_Init->Mode & FALLING_EDGE) == FALLING_EDGE)
{
SET_BIT(EXTI->FTSR, iocurrent);
}
else
{
CLEAR_BIT(EXTI->FTSR, iocurrent);
}
}
}
position++;
}
}
HAL层的库这里处理的时候,如果是输入模式,或者是外部中断模式,全部安排输入模式。
上下拉电阻的设置不依赖中断边沿方向,不做变更。
综上所述
目前针对STM32平台的drv_gpio.c 文件,做一些常见的功能 基本没有问题。
对于一些 特殊应用场合,需要大家额外注意。
举个栗子
在中断使能中,上升沿中断,自动给安排下拉电阻
case PIN_IRQ_MODE_RISING:
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
break;
如果我这个信号的外部器件是4054这种锂电池充电芯片,开漏输出,低有效,充电中的时候状态脚是拉低的,充满则恢复为高阻态。
那么,我如果想用外部上升沿中断来捕捉充满电的这个信号的话,可想而知,这里安排的下拉电阻会让我永远无法得到高电平。
我的建议是
stm32_pin_irq_enable
函数,不要有IO口硬件模式的设定操作,就是一个简单的中断开关就行了。
不过目前,来看有点困难,因为 IO口的边沿跳变中断硬件设定也包含在HAL_GPIO_Init
函数里。
好了,这个坑留给熊大 去掉头发吧。

不合理的代码应该尽快PR修掉