痞子衡嵌入式:探析i.MXRT1050在GPIO上增加RC延时电路后导致边沿中断误触发问题(上篇)

news/2024/9/21 10:45:28/文章来源:https://www.cnblogs.com/henjay724/p/18354026

  大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家分享的是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主页、知乎主页、微信公众号 平台上。

微信搜索"痞子衡嵌入式"或者扫描下面二维码,就可以在手机上第一时间看了哦。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/781468.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【书生浦语大模型实战营学习笔记】第二课 8G 显存玩转书生大模型 Demo

任务一:使用 Cli Demo 完成 InternLM2-Chat-1.8B 模型的部署,并生成 300 字小故事,记录复现过程并截图。配置好预置环境:300 字小故事:

Android网页投屏控制从入门到放弃

本文主要记录通过网页控制安卓设备相关的实践过程,通过从adb方案开始,到uiautomator2,以及最后放弃scrpy方案,在这个热闹的周末,正好闲暇的时间,了解过去不曾接触的知识,也是一个有趣的过程。背景 业务需要采集在app上执行任务的整个过程,原始方案相对复杂,修改需要协…

算法学习:矩阵快速幂/矩阵加速

1.前言其实本质上来说,矩阵快速幂或是矩阵加速的题目比较的模板化一些,大体上都是属于我们要先写出来一个递推式子(或者是我们需要递推的式子),然后由于递推的次数过大,1e18之类的,会导致复杂度的飚升,所以我们会用到矩阵来帮我们快速处理。另外,从题目的类型上大概是…

web渗透—RCE

一:代码执行相关函数1、eval()函数 assert()函数 (1)原理:将用户提交或者传递的字符串当作php代码执行 (2)passby:单引号绕过:闭合+注释;开启GPC的话就无法绕过(GPC就是将单引号转换为"反斜杠+单引号") eg: <?phphighlight_file(__FILE__);//高亮显示代码$…

代码块

代码块 概述:在Java中,使用{}括起来的代码被称为代码块,根据其位置和声明的不同,可以分为局部代码块,构造代码块,静态代码块,同步代码块(多线程讲解)。 (1)局部代码块 在方法中出现;限定变量生命周期,及早释放,提高内存利用率 (2)构造代码块 在类中方法外出现;多…

【书生浦语大模型实战营学习笔记】第一课 浦语大模型全链路开源开放体系

视频内容总结: 视频是由汪周谦主讲, 主题是介绍书生谱语大模型开源开放体系。内容主要包括以下几个方面: 1. **书生谱语大模型的发展历程**: - 从2023年7月6日起,书生谱语大模型(Interlm)开始免费开源并商用,提供了全链条的开源工具体系。 - 2023年9月底,发布了适…

u8g2字体库命名规则及符号库的使用

u8g2字体命名规则 <prefix> _ <name> _ <purpose> <char set> prefix:基本上都是 u8g2;name:一般会挂钩上字符像素使用量,比如5X7purpose: t(transparent)\h(height)\m(monospace)\8(8x8pixe)<purpose> Descriptiont Transparent font, Do not…

Python 虚拟环境安装flask框架 Read timed out.

cmd 输入workon env_name 激活 pip install flask 升级pip: python -m pip install --upgrade pip -i http://pypi.douban.com/simple --trusted-host pypi.douban.com 下载包:以flask-sqlalchemy为例: 方案一:pip install Flask 方案二:pip install flask-sqlalchemy -i…

web渗透—sql注入

一:union联合注入 1、万能密码and优先级高于or,先执行and; 则username = "用户提交" and password ="1"为假;or后面的条件恒为真;则where条件为真;输出admin表中所有的信息select * from admin where username = "用户提交" and password =…

一个好用的消息推送服务【Server 酱】

今天给大家介绍一个好用的消息推送服务Server 酱 Server 酱简介 Server 酱是什么 「Server 酱」,英文名「ServerChan」,是一款「手机」和「服务器」、「智能设备」之间的通信软件。 说人话?就是从服务器、路由器等设备上推消息到手机的工具。 开通并使用上它,只需要一分钟:…

数据接口安全风险监测技术

数据接口: 信息系统之间进行数据传输和交换的一种机制,它描述了一个由接口服务端和客户端端共同遵守的合约,通常会约定数据的格式、通信协议、传输结构等。 风险源: 可能导致危害数据的保密性、完整性、可用性和数据处理合理性等事件的威胁、脆弱性、问题、隐患等。 一、数…