This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] MSP430F5529:如何退出中断#39;s 循环?

Guru**** 2847400 points
请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/1001518/msp430f5529-how-to-exit-from-an-interrupt-s-loop

器件型号:MSP430F5529

大家好。 我 不熟悉 MSP430和微控制器。  现在、我正在尝试编写一个程序、通过每次按下 P1.1按钮来改变 LED 的状态(关闭-闪烁-闪烁)。 我使用了"switch-case"语句来实现此目的并控制 switch 语句的变量、我使用了端口中断。 对于闪烁操作、我使用了计时器中断。 一切都正常工作。 但闪烁情况存在问题。

程序从 LED 关闭开始。 然后我按下按钮、LED 亮起。 再次按下按钮、LED 开始闪烁。 在闪烁情况之后、当再次按下按钮时、程序必须返回到初始状态、从而关闭 LED。 再按一次可打开 LED、再按一次可开始闪烁、依此类推。

但是、当 LED 开始闪烁时、程序停留在无限循环点、计时器参与切换 LED 状态、按下按钮无效、LED 继续闪烁。

在没有中断的情况下(通过 usigning Polling 等)、我的代码能够完美地工作。 因此、为了避免在这里过度拥挤、我不会 完全发布我的代码。

volatile unsigned int mode;

#pragma vector = timer_vector_here
__interrupt void someNameHere (void) {
    P1OUT ^= BIT0;
    TA0CTL &= ~TAIFG;
}

#pragma vector = port_vector_here
__interrupt void anotherNameHere (void) {
    mode++;
    P1IFG &= ~BIT1;
}

int main(void) {
    /* Port settings, P1.0 to output, P1.1 to input etc.
     * Port Interrupt settings, IES, IRQ etc.
     * Timer settings, TA0CTL, SMCLK clock etc.*/
    
    __enable_interrupt();
    
    mode = 0;
    
    while (1) {
        
        if (mode > 2) {
            mode = 0;
        }
        
        switch (mode) {
            case 0: P1OUT &= ~BIT0;
                    break;
            case 1: P1OUT |= BIT0;
                    break;
            case 2: TA0CTL = TASSEL_2 + MC_1 + TACLR + TAIE;
                    TA0CCR0 = 50000;
                    TA0CTL &= ~TAIFG;
                    for(;;);
                    break;
        }
    }
}

我在其他器件上写的内容并不重要。 重要的是、在案例2和计时器 ISR 中写入的内容。 总之、我的问题是、当我按下按钮关闭 LED 并继续编程时、如何退出计时器中断?

P.S. 我没有使用、也不想使用 LPM 或其他东西。 因此、请不要提供包含 LPM 的示例。 非常感谢您的参与  

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    在每个状态下、该程序会重复执行一些操作。 我认为您实际上希望它执行该操作一次、当它进入该状态时、情况0和1无关、因为无论执行一次还是重复(无效)、特定操作都是相同的、但情况2只应在进入状态2时执行一次。

    我建议您检查转换:

    1) 1)声明变量"unsigned OldState;"  

    2)作为 while (1)循环中的第一行:"if (state = OldState) continue;"以跳过非转换。

    3)作为 while (1)循环中的最后一行:" OldState = state;" 以提交到新状态。

    4) 4)在状态0中、添加:"TA0CTL = 0;//停止计时器"。

    --------------

    未经请求:

    a)这些:

    >P1OUT &= BIT0;

    >P1OUT |=~BIT0;

    不做你想要的事。 尝试:

    >P1OUT &=~BIT0; // P1.0 LED 关闭

    > P1OUT |= BIT0;  // P1.0 LED 亮起

    b)在 PORTn_vector 函数中、不要忘记清除适当的 PnIFG 位、否则 ISR 将继续触发。

    C)如果您使用的是物理按钮、则可能需要去抖。 一种粗略但相当有效的方法是在 ISR 中添加第一行:

    >_DELAY_CYCLES   (1000);// 1ms 让按钮停止抖动

    [编辑:我忘了提到停止计时器。]

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    Bruce、您好、感谢您的帮助。

    让我更清楚地说明一下。 我希望每个案例在按下按钮之前运行一次、但案例2除外。 我希望案例2中的流程永远持续到按下按钮为止。 因此、当程序运行情况2并且如果我在这之后再也不按下该按钮、LED 应该会一直闪烁。

    不幸的是、我不理解您为过渡提供的步骤。 您能更详细地解释一下吗?

    ------------

    我在 我的代码中写下了未经请求的"a"和"b"、正如 您所说的、我只是忘记了提及它们。 但我对"c"有疑问。 我能否使用案例2中使用的同一个计时器进行延迟?

    此致、

    EFE

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    它看起来是我换过的术语("mode-"->"state")。 如何处理:

    )声明变量“unsigned oldmode;”。

    2) 2)作为 while (1)循环的第一行、插入:if (mode = oldmode) continue;"以跳过非转换。

    3) 3)作为 while (1)循环中的最后一行、插入" oldmode = mode;" 以提交到新状态。

    4) 4)在"案例0:"中、添加:"TA0CTL = 0;//停止计时器"。

    根据您的伪代码、我认为这些更改将使您顺利进行。  

    ----------

    有更复杂的去抖方法。 使用 TA0可能需要不同的策略、因为我怀疑您不打算一直运行 TA0。

    由于我不能猜到该程序将会发展到什么、我提供了一种非常简单的方法来帮助您实现目标。 (如果您想查看其他一些选项、请使用 Google "ganssle debounce"。) 您现在也可以忽略开关抖动、但最终会遇到它。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    Bruce、您好。 我进行了您建议的更改。 现在 、我的代码的主函数如下所示:

    int main(void) {
        
        unsigned oldmode;
    
        /* Port settings, P1.0 to output, P1.1 to input etc.
         * Port Interrupt settings, IES, IRQ etc.
         * Timer settings, TA0CTL, SMCLK clock etc.*/
        
        __enable_interrupt();
        
        mode = 0;
        
        while (1) {
        
            if (mode == oldmode) {
                continue;
            }
            
            if (mode > 2) {
                mode = 0;
            }
            
            switch (mode) {
                case 0: P1OUT &= ~BIT0;
                        TA0CTL = 0;
                        break;
                case 1: P1OUT |= BIT0;
                        break;
                case 2: TA0CTL = TASSEL_2 + MC_1 + TACLR + TAIE;
                        TA0CCR0 = 50000;
                        TA0CTL &= ~TAIFG;
                        for(;;);
                        break;
            }
            oldmode = mode;
        }
    }

    但问题仍然存在。 我无法从案例2中退出、继续案例0。 LED 不会停止闪烁。 当我调试代码并逐步执行时、我看到程序卡在"for (;);"部分。 一旦我按下按钮、它就会结束计时器、并关闭 LED。

    此致、

    EFE

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您应该删除"for (;)"。 这是其他变化的目标。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    但是、当我移除它时、计时器不会触发。 LED 不会闪烁。  

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    它在我这么做的时候工作了。 (我确实必须将去抖延迟增加到50000以获得一致的行为。)

    我不知道我们在做什么不同。 您可能没有在代码中显示什么内容?

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    好的、它现在正在工作。 我不知道我改变了什么、但不知怎么说、它开始工作了、不过有点不好。 有时、它从案例2返回案例1、有时从案例0跳到案例2。 我不明白导致这个问题的原因、但我正在努力解决这个问题。 现在、我还有几个问题。 如果您也回答了这些问题、我会很高兴。

    首先、oldmode 变量的类型是什么? 我们刚刚写入 了无符号 oldmode、但没有什么? char、int、long? 或者它是否会根据它的均衡值发生变化?

    问题2是:当我调试程序并逐步执行时、我看到、在启动时、如果我不按任何按钮、程序将在 if (mode = oldmode)行中等待。 但是、只要我按下按钮、端口中断 就会触发并更改模式变量、从而使情况1变为活动状态。  为什么按下按钮时 mode=oldmode?

    最后但同样重要的是、该程序的逻辑是什么。 我的意思是、 什么触发计时器中断? 为什么我们将旧模式等同于 while 循环的最后一行中的模式?

    此致、

    EFE

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    额外的转换是由开关反弹引起的。 由于按钮的物理属性、当您按下按钮一次时、程序可能会看到2个(或10个或50个)联系人、因此它会经历多种模式转换。 正如我在上面提到的、Jack Ganssle 在这个主题上写了一篇很好的论文。

    "unsigned"表示"unsigned int"。

    最初您的程序处于模式0、之前的模式是模式0、并且初始条件与模式0一致(具体而言、LED 和计时器处于关闭状态)。 从概念上讲、您可以说它在程序启动之前转变为模式0。

    定时器中断是由您在启动定时器时设置 TAIE 引起的。 它将反复触发、每50000个计时器周期(大约50ms)触发一次、直到您在转换到模式0时停止计时器。

    您可以将 oldmode=mode 设置为指示模式转换已处理、因此不 会再次处理("边缘敏感")。

    我鼓励您在头脑中逐步完成此计划、看看它在做什么。 这些是您经常会看到的习语。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    非常感谢。 我从您那里学到了很多。

    [引用 userid="47378" URL"~/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/1001518/msp430f5529-how-to-exit-from-an-interrupt-s-loop/3700369 #3700369]计时 器中断是由您在启动计时器时设置 TAIE 引起的。 [/报价]

    如果我在主器件中设置计时器并在情况2中写入 TA0CTL = TAIE、该怎么办? 我是说、是的、我可以在 MSP430上尝试一下、看看会发生什么情况、但我要问的是、逻辑。 如果不这么做、我可以在主部件中设置我的计时器并在任何地方使用它吗?

    此致、

    EFE

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    的、你可以一直运行定时器、并且只打开和关闭 TBIE (但是使用"TA0CTL |= TAIE"和"TA0CTL &=~TAIE")。 当您设置 TAIE 时、TAIFG 很可能会被设置(过期)、因此第一个闪烁周期将很短。 在该程序中、人类将无法看到差异、但在不同的应用中、这可能很重要。