痞子衡嵌入式:探析i.MXRT1050在GPIO上增加RC延时电路后导致边沿中断误触发问题(上篇)
大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家分享的是i.MXRT1050在GPIO上增加RC延时电路后导致边沿中断误触发问题探析。
前段时间有一个 RT1052 客户反馈了一个有趣的问题,他们设计得是一个带 LCD 屏交互的应用,应用以官方 SDK 里的 lvgl_demo_widgets_bm 例程为基础。当客户在这个例程基础上增加了 GPIO 输入边沿中断检测,并且硬件上给 GPIO 增加了 RC 延时电路后,发现边沿中断触发得不太准确,这是怎么回事?今天痞子衡带大家还原现场:
一、问题描述
客户做得硬件改动很简单,在 GPIO_AD_B1_04 引脚和 GPIO_AD_B1_10 引脚之间加了如下的 RC 延时电路。GPIO_AD_B1_04 上产生得是 500Hz 的方波(既可以是 GPIO 模块输出,也可以去掉 R290 后直接接信号发生器),这个方波经过 RC 电路之后输出给 GPIO_AD_B1_10,然后通过其输入边沿中断来检测电平变化,并且在每个边沿中断里都翻转一次 GPIO_AD_B1_11 电平。
代码改动也足够简单,只需要在 \SDK_2_15_000_EVKB-IMXRT1050\boards\evkbimxrt1050\lvgl_examples\lvgl_demo_widgets_bm 工程里添加 test_gpio_irq() 函数调用即可(这里假定 GPIO_AD_B1_04 上的方波是由外部信号发生器提供的):
void GPIO1_Combined_16_31_IRQHandler(void)
{
// 检测到 GPIO_AD_B1_10 边沿
if ((GPIO1->ISR & (1U << 26)) && (GPIO1->IMR & (1U << 26)))
{
GPIO_PortClearInterruptFlags(GPIO1, 1U << 26);
// 翻转 GPIO_AD_B1_11 电平
GPIO_PortToggle(GPIO1, 1 << 27);
__DSB();
}
}
void config_rc_in_gpio(void)
{
// 配置 GPIO_AD_B1_10 为边沿中断输入检测模式
gpio_pin_config_t in_config = { kGPIO_DigitalInput, 1, kGPIO_NoIntmode };
IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_10_GPIO1_IO26, 1);
IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B1_10_GPIO1_IO26, 0x011030U);
GPIO_PinInit(GPIO1, 26, &in_config);
GPIO_SetPinInterruptConfig(GPIO1, 26, kGPIO_IntRisingOrFallingEdge);
EnableIRQ(GPIO1_Combined_16_31_IRQn);
GPIO_PortEnableInterrupts(GPIO1, 1U << 26);
}
void config_user_out_gpio(void)
{
// 配置 GPIO_AD_B1_11 为普通输出模式
gpio_pin_config_t out_config = { kGPIO_DigitalOutput, 1, kGPIO_NoIntmode };
IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_11_GPIO1_IO27, 0);
GPIO_PinInit(GPIO1, 27, &out_config);
GPIO_PinWrite(GPIO1, 27, 0U);
}
void test_gpio_irq(void)
{
config_rc_in_gpio();
config_user_out_gpio();
}
如果 GPIO_AD_B1_10 边沿中断检测无误,那么输出的 GPIO_AD_B1_11 信号应该是和原始输入 GPIO_AD_B1_04 完全同频的方波,而事实上客户用示波器抓到的 GPIO_AD_B1_11 信号偶尔会出现如下情况,很显然有边沿中断误触发的情况发生:
并且更有趣的是,这样的测试仅在 lvgl_demo_widgets_bm 工程里能复现,而在普通 input_interrupt 工程下没有任何问题。
- Note1:在 lvgl_demo_widgets_bm 工程下出现的 GPIO 边沿中断误触发问题仅在增加 RC 电路时存在。
- Note2:在普通 input_interrupt 工程下即使增加 RC 电路,GPIO 边沿中断误触发问题也不存在。
二、问题复现
理论上分析 GPIO_AD_B1_10 引脚输入的信号频率是 500Hz,那么其边沿中断应该是每 1ms 产生一次,而从上一节客户抓取的 GPIO_AD_B1_11 实际信号反推,似乎有时候边沿中断在 10us 内连续产生了两次。
为了从软件角度抓到这个中断误触发现象,痞子衡稍微改了一下代码,将 GPIO_AD_B1_04 上信号改为软件输出(在 SysTick 1ms 一次的中断响应里翻转电平),并且用了两个计数器 s_outputPinEdgeCount、s_inputRcPinIrqCount 来分别记录 GPIO_AD_B1_04、GPIO_AD_B1_10 边沿次数。如果边沿中断触发无误的话,这两个计数器的值应该是永远相等的,但是实际跑了一段时间后发现 s_inputRcPinIrqCount 会超过 s_outputPinEdgeCount,并且随着时间累积,差距会越来越大。这说明边沿中断误触发现象是一直存在的。
volatile uint32_t s_inputRcPinIrqCount = 0;
volatile uint32_t s_outputPinEdgeCount = 0;
void GPIO1_Combined_16_31_IRQHandler(void)
{
// 检测到 GPIO_AD_B1_10 边沿
if ((GPIO1->ISR & (1U << 26)) && (GPIO1->IMR & (1U << 26)))
{
GPIO_PortClearInterruptFlags(GPIO1, 1U << 26);
// 计数 GPIO_AD_B1_10 边沿
s_inputRcPinIrqCount++;
__DSB();
}
}
void config_rc_out_gpio(void)
{
// 配置 GPIO_AD_B1_04 为普通输出模式
IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_04_GPIO1_IO20, 0);
GPIO_PinInit(GPIO1, 20, &out_config);
GPIO_PinWrite(GPIO1, 20, 0U);
}
void test_gpio_irq(void)
{
config_rc_in_gpio();
config_rc_out_gpio();
}
void SysTick_Handler(void)
{
// 计数 GPIO_AD_B1_04 边沿
s_outputPinEdgeCount++;
GPIO_PortToggle(GPIO1, 1 << 20);
__DSB();
// 原应用代码省略
}
三、问题定位
描述至此,你的第一反应到底是哪里出了问题?痞子衡想你可能会觉得罪魁祸首是 RC 延时电路,它将标准的方波上升、下降过程变得平缓,导致信号电压处于临界区的时间变长(极端情况下,对于高频信号,可能会导致其一直处于临界区),这个可能会影响 GPIO 电平跳变判定。既如此,我们先翻看一下 RT1050 的 datasheet,找到如下 GPIO DC 参数表,其高、低电平判定值分别是 70%、30% NVCC_XXXX,此外备注里说明了只要电平变化是单调的(随着时间单向增大或减小),且转换时间范围在 0.1ns - 1s 内均会被认定为有效跳变。
这时候我们再根据 RC 延时电路标准时间常数公式 t = RC * $\ln (\frac{(V1-V0)}{V1-Vt})$ 来推算(V1 电源电压、V0 电容初始时刻电压、$V_t$ 为 t 时刻电容电压)。如果 NVCC 为 3.3V,那么上升沿时从 0V 充电到 2.31V 的时间是 12us,显然这个 12us 充电时间对于 500Hz 的方波来说不足以影响其跳变判定。
有没有方法能抓住这个异常边沿中断发生时,GPIO_AD_B1_10 信号当时的波形状态呢?当然是可以的,我们可以再修改一下边沿中断处理函数代码,在里面计算两次中断之间的 Tick 间隔,如果间隔 Tick 低于一定值,说明是误触发,此时翻转一次 GPIO_AD_B1_11 电平用作标记。
volatile uint32_t s_systickCurVal = 0;
volatile uint32_t s_systickLastVal = 0;
volatile uint32_t s_systickCurCount = 0;
volatile uint32_t s_systickLastCount = 0;
volatile uint32_t s_systickDeltaVal;
uint32_t s_systickReloadVal = 0;
void GPIO1_Combined_16_31_IRQHandler(void)
{
/* clear the interrupt status */
if ((GPIO1->ISR & (1U << 26)) && (GPIO1->IMR & (1U << 26)))
{
s_systickCurVal = SysTick->VAL;
s_systickCurCount = s_outputPinEdgeCount;
GPIO_PortClearInterruptFlags(GPIO1, 1U << 26);
// 计算两次中断之间的 Tick 间隔
s_systickDeltaVal = (s_outputPinEdgeCount - s_systickLastCount) * s_systickReloadVal + s_systickLastVal - s_systickCurVal;
s_systickLastVal = s_systickCurVal;
s_systickLastCount = s_systickCurCount;
// 当间隔 Tick 低于一定值时,说明是误触发,此时翻转一次 GPIO_AD_B1_11 电平
if (s_systickDeltaVal <= s_systickReloadVal / 2)
{
GPIO_PortToggle(GPIO1, 1 << 27);
}
__DSB();
}
}
int main(void)
{
// 应用代码省略...
test_gpio_irq();
s_systickReloadVal = SystemCoreClock / (LVGL_TICK_MS * 1000U);
s_inputRcPinIrqCount = 0;
s_systickLastVal = s_systickReloadVal;
DEMO_SetupTick();
// 应用代码省略...
}
如果用示波器以 GPIO_AD_B1_11 跳变为触发信号(ch2),即能看到案发现场 GPIO_AD_B1_10 状态(ch1),确实我们看到充放电时间内出现了短时脉冲波干扰(glitch),这个脉冲导致了电平变化不是单调的,因而产生了 GPIO 中断误触发。本篇仅是定位问题,下一篇我们会具体分析这个 glitch 是如何产生的!
至此,i.MXRT1050在GPIO上增加RC延时电路后导致边沿中断误触发问题探析(上篇)痞子衡便介绍完毕了,掌声在哪里~~~
欢迎订阅
文章会同时发布到我的 博客园主页、CSDN主页、知乎主页、微信公众号 平台上。
微信搜索"痞子衡嵌入式"或者扫描下面二维码,就可以在手机上第一时间看了哦。

痞子衡嵌入式:探析i.MXRT1050在GPIO上增加RC延时电路后导致边沿中断误触发问题(上篇)的更多相关文章
- 痞子衡嵌入式:其实i.MXRT1050,1020,1015系列ROM也提供了FlexSPI driver API
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT1050/1020/1015系列ROM中的FlexSPI驱动API使用. 今天痞子衡去4S店给爱车做保养了,保养一次要等两小 ...
- 痞子衡嵌入式:深入i.MXRT1050系列ROM中串行NOR Flash启动初始化流程
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是深入i.MXRT1050系列ROM中串行NOR Flash启动初始化流程. 从外部串行NOR Flash启动问题是i.MXRT系列开发最 ...
- 痞子衡嵌入式:16MB以上NOR Flash使用不当可能会造成软复位后i.MXRT无法正常启动
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是i.MXRT上使用16MB以上NOR Flash软复位无法正常启动问题的分析解决经验. 痞子衡这几天在支持一个i.MXRT1050客户项 ...
- 痞子衡嵌入式:揭秘i.MXRT600的ISP模式下用J-Link连接后PC总是停在0x1c04a的原因(Debug Mailbox)
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT600中的Debug Mailbox实现对JLink调试的影响. 事情缘起痞子衡的同事 - 喜欢打破砂锅问到底的Kerry小 ...
- 痞子衡嵌入式:以i.MXRT1xxx的GPIO模块为例谈谈中断处理函数(IRQHandler)的标准流程
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是以i.MXRT的GPIO模块为例谈谈中断处理函数(IRQHandler)的标准流程. 在痞子衡旧文 <串口(UART)自动波特率识 ...
- 痞子衡嵌入式:再测i.MXRT1060,1170上的普通GPIO与高速GPIO极限翻转频率
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT1060/1170上的普通GPIO与高速GPIO极限翻转频率. 按照上一篇文章 <实测i.MXRT1010上的普通GP ...
- 痞子衡嵌入式:探析开启CRC完整性校验的IAR工程生成.out和.bin文件先后顺序
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是开启CRC完整性校验的IAR工程生成.out和.bin文件先后顺序问题. 痞子衡之前写了一篇 <在IAR开发环境下为工程开启CRC ...
- 痞子衡嵌入式:简析i.MXRT1170 Cortex-M4 L-MEM ECC功能特点、开启步骤、性能影响
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是恩智浦i.MXRT1170上Cortex-M4内核的L-MEM ECC功能. 本篇是 <简析i.MXRT1170 Cortex-M ...
- 痞子衡嵌入式:简析i.MXRT1170 XECC功能特点及其保护串行NOR Flash和SDRAM之道
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是i.MXRT1170 XECC功能特点及其保护串行NOR Flash和SDRAM之道. ECC 是 "Error Correc ...
- 痞子衡嵌入式:简析i.MXRT1170 Cortex-M7 FlexRAM ECC功能特点、开启步骤、性能影响
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是恩智浦i.MXRT1170上Cortex-M7内核的FlexRAM ECC功能. ECC是"Error Correcting ...
随机推荐
- C# .NET core Avalonia 11.0版本,发布linux和MAC的简单记录
.net core 7.0+centos 7.0 cetnos目前运行在hyper V虚拟机里 虚拟机部署的注意事项 1 需要配置网络环境, 确保在同一局域网下 如果sftp无法连接 ctrl+shi ...
- 苹果应用商店上传应用卡在了“Authenticating with the iTunes Store”
在终端中依次运行下面代码 cd ~ mv .itmstransporter/ .old_itmstransporter/ "/Applications/Xcode.app/Contents/ ...
- mysql这个垃圾迁移数据费劲半天
mysql垃圾得很! 对于一些小系统还可以.大型系统自己找麻烦--必须有高昂的人工费! 没有高昂的人工(必须有好的程序员和工程师才能解决一些复杂的问题),构建基于大量数据的应用,非常麻烦. 而这些费 ...
- shell 编程简记
1. 环境变量 环境变量是指操作系统中记录一些配置信息的变量,这些变量在不同的程序之间共享,可以被操作系统或者 shell 脚本读取和修改. 环境变量也可以类比为各个语言中的全局变量,其作用域是全局的 ...
- Kubernetes(一)Overview
1. Kubernetes介绍 要了解Kubernetes,首先我们需要了解Container与Orchestration. Docker Docker的出现是为了解决:部署依赖.以及兼容性.以及繁琐 ...
- 在WPF中使用着色器
概念类比 范畴 CPU GPU 二进制文件 .exe .cso / .ps 二进制指令 机器码 CSO(shader指令) 助记符 汇编 SL 高级语言 C# HLSL 高级语言文件 .cs .hls ...
- 【论文阅读】IROS2022: Dynamics-Aware Spatiotemporal Occupancy Prediction in Urban Environments
0.参考与前言 完整题目: Dynamics-Aware Spatiotemporal Occupancy Prediction in Urban Environments 论文链接:https:// ...
- 【ClickHouse】4:clickhouse基本操作二 建库建表导数据
背景介绍: 有三台CentOS7服务器安装了ClickHouse HostName IP 安装程序 程序端口 centf8118.sharding1.db 192.168.81.18 clickhou ...
- P9120 题解
暴力容斥复活之路! \(k=1\) 这个你肯定会. \(k=2\) 大的放上去,小的放下来.简单贪心. \(k=3\) 考虑二分答案. 然后考虑判断是否合法. 令当前答案为 \(val\). 首先钦定 ...
- SpringBoot排查自动装配、Bean、Component、Configuration配置类
排除自动装配AutoConfiguration @SpringBootApplication( exclude = { DataSourceAutoConfiguration.class, Mybat ...