Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
PIN_GPIO通用驱动
STM32
外部中断
5
关于4.0.2版本中STM32的PIN设备外部中断的相关问题
发布于 2021-01-26 17:37:04 浏览:2374
订阅该版
[tocm] 有一段时间没有接触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: ![V402.png](https://oss-club.rt-thread.org/uploads/20210126/425bcb665a114bcfec24b5b62f1132f7.png) 中断使能禁止函数: 使能部分: ![enable.png](https://oss-club.rt-thread.org/uploads/20210126/143076ae9795ff5a610c75c9ae9e6daf.png) 禁止部分: ![disable.png](https://oss-club.rt-thread.org/uploads/20210126/306f2f0106c11f8c1ba744c304ad342d.png) 好家伙! 原来在调用 rt_pin_irq_enable -> stm32_pin_irq_enable 的时候, ENABLE会再次进行IO口的设置;DISABLE 会恢复IO口状态为复位后的浮空输入状态。 那我调用 rt_pin_mode 是调用了个寂寞? 所以,对于此处的代码,我认为是不够合理的。 最不合理的地方是: ```c /* 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层库是如何构建的。 ```c /** * @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 文件,做一些常见的功能 基本没有问题。 对于一些 特殊应用场合,需要大家额外注意。 ### 举个栗子 在中断使能中,上升沿中断,自动给安排下拉电阻 ```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`函数里。 --- 好了,这个坑留给熊大 去掉头发吧。 ![d1d1136a47585d4887cd0afe875d89af.jpg](https://oss-club.rt-thread.org/uploads/20210126/d1d1136a47585d4887cd0afe875d89af.jpg)
查看更多
张世争
2021-01-26
学以致用
理论上 drv_gpio.c 是平台适配的驱动,与pin.c 配合使用。 所以,特殊情况下,建议直接修改:drv_gpio.c!!
6
个回答
默认排序
按发布时间排序
来一颗糖
2021-01-26
这家伙很懒,什么也没写!
以下都是个人看法: rtt 提供的驱动代码,都只是能用,满足文档中心里示例代码演示的基本需求,尽可能保证初学者能一下子就跑起来。 如果要追求与自己产品的完美适配,那么建议参考 rtt 的驱动,自己再实现一遍代码。
fubaojun2006
2021-01-27
哇(挖)~~坑~~~啊!
昨晚上思考了一下,怎么分离这个 IO口在中断使能与禁止过程中的重复进行硬件设置的问题。 PIN设备还有两个函数: rt_pin_attach_irq rt_pin_detach_irq 完全可以在 attach_irq 的时候,初始化硬件的边沿中断检测功能,在此之前,先调用 rt_pin_mode 设定IO状态。 detach_irq 的时候,清除硬件上设定的边沿中断检测,此处 建议是只改变中断边沿控制寄存器 其他的IO 输入输出 上下拉状态 保持为 rt_pin_mode 设定的状态。 如果这个IO口在 detach_irq 之后,又要做输出功能 或其他功能,就需要再次调用 rt_pin_mode 设定状态。 rt_pin_irq_enable 这个函数的职能,只负责,中断的NVIC的控制即可。
liuduanfei
2021-01-26
这家伙很懒,什么也没写!
如果能留出更多的配置选项就好了
红枫
认证专家
2021-01-27
这家伙很懒,什么也没写!
你可以直接使用qkey软件包驱动按键或外部信号输入,软件包已经做了去抖等功能,使用很方便的。
用户名由3_15位
2022-08-29
这家伙很懒,什么也没写!
可以先使能中断,再配置模式。不想改他源码,只能出此下策😬
撰写答案
登录
注册新账号
关注者
0
被浏览
2.4k
关于作者
fubaojun2006
哇(挖)~~坑~~~啊!
提问
8
回答
47
被采纳
2
关注TA
发私信
相关问题
1
裸机工程移植 RT-Thread
2
Keil MDK 移植 RT-Thread Nano
3
移植 Nano,rt_thread_mdelay()延迟时间不对
4
裸机工程移植 RT-Thread内核
5
关于利用0x68000000作为扩展sram?
6
STM32F413 SD 卡写入速度提升方法
7
STM32 RTC 闹钟
8
http_ota 提示no memory
9
studio中怎么把PB3 和PA15引脚设置为普通IO口使用?
10
求一份基于RTT系统封装好的STM32F1系列的FLASH操作程序
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
最新文章
1
使用百度AI助手辅助编写一个rt-thread下的ONVIF设备发现功能的功能代码
2
RT-Thread 发布 EtherKit开源以太网硬件!
3
rt-thread使用cherryusb实现虚拟串口
4
《C++20 图形界面程序:速度与渲染效率的双重优化秘籍》
5
《原子操作:程序世界里的“最小魔法单位”解析》
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
WIZnet_W5500
ota在线升级
UART
PWM
cubemx
freemodbus
flash
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
Debug
编译报错
msh
SFUD
keil_MDK
rt_mq_消息队列_msg_queue
at_device
ulog
C++_cpp
本月问答贡献
踩姑娘的小蘑菇
7
个答案
3
次被采纳
张世争
8
个答案
2
次被采纳
rv666
5
个答案
2
次被采纳
用户名由3_15位
11
个答案
1
次被采纳
KunYi
6
个答案
1
次被采纳
本月文章贡献
程序员阿伟
6
篇文章
2
次点赞
hhart
3
篇文章
4
次点赞
大龄码农
1
篇文章
2
次点赞
ThinkCode
1
篇文章
1
次点赞
Betrayer
1
篇文章
1
次点赞
回到
顶部
发布
问题
分享
好友
手机
浏览
扫码手机浏览
投诉
建议
回到
底部