为啥此功能不能正确打开和关闭 LED?

Posted

技术标签:

【中文标题】为啥此功能不能正确打开和关闭 LED?【英文标题】:Why doesn't this function properly toggle an LED on and off?为什么此功能不能正确打开和关闭 LED? 【发布时间】:2015-04-25 23:40:22 【问题描述】:

我正在使用 Atmel SAM3x8E 微控制器,并尝试在按下按钮时进行简单的 LED 切换。我正在使用上拉配置按钮来触发中断程序。

这是中断的初始化:

// Set button pins as pull-up inputs 
pio_set_input(PIOC, BUTTON_1, PIO_PULLUP);
pio_set_input(PIOC, BUTTON_2, PIO_PULLUP); 

// Configure button input pin interrupt mode and handler (Rising Edge)
pio_handler_set(PIOC, ID_PIOC, BUTTON_1,  PIO_IT_RISE_EDGE, button_press_handler);
pio_handler_set(PIOC, ID_PIOC, BUTTON_2,  PIO_IT_RISE_EDGE, button_press_handler);

// Enable the interrupts
pio_enable_interrupt(PIOC, BUTTON_1); 
pio_enable_interrupt(PIOC, BUTTON_2); 
NVIC_EnableIRQ(PIOC_IRQn); 
NVIC_EnableIRQ(PIOC_IRQn); 

那么这是中断程序:

// Interrupt handler for button press
void button_press_handler(uint32_t a, uint32_t b)

   pio_toggle_pin_group(PIOC, BLUE_LED4); // NOT TOGGLING LED (ONLY TURNS IT ON)

然而,当我运行它时,我无法让 LED 切换。它只是打开并保持打开状态。 pio_toggle_pin_group 调用的函数如下:

 * \param p_pio Pointer to a PIO instance.
 * \param ul_mask Bitmask of one or more pin(s) to configure.
 */
void pio_toggle_pin_group(Pio *p_pio, uint32_t ul_mask)

    if (p_pio->PIO_ODSR & ul_mask) 
        /* Value to be driven on the I/O line: 0. */
        p_pio->PIO_CODR = ul_mask;
     else 
        /* Value to be driven on the I/O line: 1. */
        p_pio->PIO_SODR = ul_mask;
    

关于为什么我的 LED 没有按照我想要的方式切换有什么想法吗?我参考了Atmel ASF documentation,但我仍然无法弄清楚这一点。

【问题讨论】:

您是否检查过引脚是否实际更改了状态?中断真的被调用了吗? 是的,LED 最初设置为低,但是当我按下我的触觉按钮时,它会亮起,所以它一定是在调用中断。我可以通过在中断例程中更改它来打开已连接的任意数量的 LED (BLUE_LED1-4)。 【参考方案1】:

我无法帮助您处理实际的函数调用,但假设您使用边缘中断。据我所知,您为每个上升沿调用一个中断处理程序。但是,在第一个上升沿之后,您需要在按钮释放时触发,这将是一个下降沿,因此您需要在中断处理程序中更改沿。

但您必须考虑到机械按钮在按下或释放时不会生成干净的单边。相反,它会反弹。对于带有上拉(或下拉)电阻器的普通瞬时接触按钮,这会导致每个事件产生多个脉冲,因此 LED 可能会多次打开/关闭并保持在任意状态,该状态可能 - 偶然 - 远“开启”大多数时候。如果可用,请使用示波器检查。

这可以通过一个电容器在硬件中规避,或者在软件中使用一个定时器来规避,该定时器在相关边沿之后具有死区时间,然后再对任何其他按钮事件做出反应。 死区时间取决于按钮的类型,但典型值为 5 到 20ms,应在按钮的数据表中提及。如有疑问,请使用可接受的最高值。

【讨论】:

感谢您的帮助,我最终通过放弃 pio_toggle_pin_group 并改用 pio_set 来让它工作......现在唯一的问题,奇怪的是,LED 每 18 秒“重置”一次......因此,如果我在 18 秒后打开它们,无论我是否按下按钮,它们都会关闭。 这可能是一个竞争条件,另一个中断访问同一个端口寄存器。 ARM CPU 没有像 AVR 或 MSP430 那样的原子读-修改-写指令,因此要在一个字中设置一个位,它必须将其读入寄存器,修改该寄存器并写回。如果中间有中断,旧数据可能会被写回寄存器,从而消除嵌套中断的影响。要避免这种情况,请使用 stdatomic(C11,来自 4.7.2 的 gcc 带有外部标头,4.9.? 带有安装中包含的标头)。或者禁用 IRQ。 可能还有一个硬件问题(都已经看到),它会生成检测为边缘的随机尖峰。【参考方案2】:

这就是最终对我有用的东西:

// Interrupt handler for button press
void button_press_handler(uint32_t a, uint32_t b)

    // Turn the LED's on or off
        if (pio_get(PIOC, PIO_TYPE_PIO_OUTPUT_0, BLUE_LED4))
        pio_clear(PIOC, BLUE_LED4);
        else
        pio_set(PIOC, BLUE_LED4);

这里是用于打开 LED 的调用“设置”函数:

void pio_set(Pio *p_pio, const uint32_t ul_mask)

    p_pio->PIO_SODR = ul_mask;

【讨论】:

这看起来并没有什么不同,除了这里没有给出的被调用函数(在这里显示一个精简但完整的代码版本总是一个好主意 - 不要指望ppl 想先阅读工具包文档)。 对不起,我在 pio_set 函数中添加了。我同意,它看起来几乎相同,但如果我使用 pio_toggle_pin_group,它将无法正常工作。 好吧,我会试着弄清楚,究竟是什么造成了不同。你检查弹跳问题了吗?这实际上是按钮的一个主要问题,尤其是在使用边缘检测硬件时。如果你没有再采取任何措施,它会踢你在b???最终;-这就是为什么如果没有适当的对策(或没有必要的充分理由),我不会接受我的一位学生的代码。 终于想通了...我错过了所有重要的“board_init();”如果没有初始化电路板,事情就无法正常工作。我非常感谢您的帮助,它非常有见地,尤其是当您提到 ARM CPU 没有原子读取-修改-写入指令以及有关嵌套中断的事情时。你让我思考,但结果证明这是一个简单的遗漏。 这就是为什么我更喜欢使用动态模块初始化框架进行构建集成的原因。你不能忘记初始化一个模块,因为构建系统会自动关心它;-)【参考方案3】:

为避免弹跳按钮产生的随机尖峰,请尝试使用去抖输入过滤器。在 sam3x8e 上,您可以通过将寄存器 PIO?->PIO_DIFSR 设置为 BUTTON_1 或 BUTTON_2 来启用此功能。

还可以通过读取PIO?->PIO_ISR来清除PIO中断的状态寄存器。这会清除所有输入更改并允许多次输入中断。

【讨论】:

以上是关于为啥此功能不能正确打开和关闭 LED?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的输入功能无法正确输入? [关闭]

为啥此电子邮件在 Lotus Notes 中不能正确显示?

如何在按下并释放一次按钮时打开 LED,然后通过再次按下和释放将其关闭?

为啥我的 UISearchController 不能正确呈现?

为啥标签栏的 selectedImage 不能正确显示?

为啥会出现打印插件没有安装正确