暂时禁用定时器中断。

3

我正在使用stm32f4xx微控制器进行C语言嵌入式项目开发。

我有一部分代码可以持续执行一个循环操作XYZ,而定时器TIM4中断会不时改变全局参数并导致操作XYZ重新启动。

代码大致如下:

for (;;) {
       //line A
    XYZ;
       //line B
}

XYZ是一个涉及数据传输和缓冲区之间的复杂操作。

TIM4中断处理程序做的事情是:停止XYZ,改变一些影响XYZ操作的全局变量。

因此,基本上我希望XYZ能够重复执行,并且TIM4中断可以停止XYZ,改变参数,然后通过使用新的全局参数重新启动XYZ循环。

问题是:由于XYZ有许多指令,TIM4 IRQ可能会在其中间发生,在IRQ处理程序更改全局变量后,操作将从XYZ的中途恢复,这样会破坏程序。

我的初始解决方案:在A行使用__disable_irq()禁用中断,在B行使用__enable_irq()使其恢复。

失败,因为XYZ复杂操作必须使用其他中断(而不是TIM4)。

下一个解决方案:仅在A行禁用TIM4中断,使用:

TIM_ITConfig(TIM4, TIM_IT_Update , DISABLE) 

使用以下方法在B线上重新启用它:

TIM_ITConfig(TIM4, TIM_IT_Update , ENABLE)

失败的原因是我失去了中断:当恢复int时,XYZ期间到达的中断会被忽略。这是一个大问题(其中一个原因是TIM4 IRQHandler更改全局变量,然后再次激活TIM4以稍后产生中断,我这样做是因为中断之间的周期不同)。

有没有人能给我解决这个问题的方案?是否有更好的方法来禁用/恢复TIM4 IRQ而不会丢失任何中断


XYZ需要停止处理的原因是什么?是因为中断覆盖信息导致数据无效,还是因为每当出现新的中断时实际上希望停止XYZ?如果只是由于数据覆盖,在ISR中,您可以将数据复制到临时变量并设置标志,然后在主循环中使用该标志来知道何时操作新数据... - Ross
5个回答

1
当我发现自己处于类似情况时,即处理时间可能比中断期间更长,我使用FIFO将处理与传入数据分离。即:TIM4填充FIFO。XYZ(或经理)消耗FIFO并处理数据。
(我觉得你的设计可能是错误的,因为你不应该使用全局变量来控制数据或流程。 研究此问题的书籍参考资料:《嵌入式系统设计模式:优秀软件的设计模式》)

1

在完成 XYZ 后,您可以对全局变量的副本进行操作,并从中断中交换新值。

从问题中无法清楚地知道当全局变量改变时是否需要立即停止处理 XYZ,还是可以等待 XYZ 处理完成后再交换变量的新副本。我将假设您需要中断处理 XYZ,但如果不需要,这也很容易实现。

代码应该类似于以下内容:

volatile int x;
int x_prime;

int main(void)
{
    while(1)
    {
        //copy in new global parameter value
        x_prime = x;

        while(1)
        {
            //do stuff with x_prime
            if (x_prime != x)
            {
                break;
            }

            //do more stuff with x_prime
            if (x_prime != x)
            {
                break;
            }
        }
    }
}

// interrupt handler
void TIM_IT_Update(void)
{
    x++;
}

break 模式假设您不会更改 x_prime。如果您需要修改 x_prime,则需要另一个副本。

由于中断从未被禁用,因此您永远不必担心丢失任何中断。并且由于您正在操作由中断更改的参数的副本,因此即使中断在执行过程中更改了参数,也没有关系,因为在进行复制之前,您不会查看这些值。


谢谢你的提示,但对我来说不太适用,因为我有一个由IQRHandler改变的缓冲区和全局变量的复杂系统,而XYZ是与之配合的。为了明确起见,我宁愿先完成XYZ,然后再处理IRQ。 - Bogdan Alexandru

1

可能有几个选项可供选择(我对ARM架构不是100%确定):

  • 更改中断优先级/级别掩码寄存器,仅屏蔽TIM4,使其他中断继续发生。希望如果在屏蔽时触发TIM4,在恢复级别掩码时它将记住并触发ISR。
  • 屏蔽中断并在XYZ期间手动检查TIM4中断标志是否已设置
  • 将XYZ分成较小的部分,并仅在绝对必要时屏蔽TIM4。
  • 在数据的副本上操作,可以选择检查TIM4中断标志以决定是否继续/保留结果或丢弃并重新启动。
  • 检查时间并避免在TIM4即将触发时启动XYZ,或者在TIM4触发后仅运行XYZ N次。

你好,你提到的第一个选项正是我想要的,但我的问题也在于如何实现。无论如何,我现在实现了一个解决方法,通过声明一个全局标志来改变它的值(IRQ除了改变全局标志外不做任何其他操作),然后在循环内,在完成XYZ后测试该标志,如果设置了,则执行原始的IRQ处理程序操作,然后重置该标志。它运行得非常好!但是我想知道是否有一种方法可以仅屏蔽TIM4 IRQ,同时不丢失IRQ。感谢您的建议! - Bogdan Alexandru
选项1可以通过将TIM4设置为低优先级,然后设置全局中断优先级掩码/级别(我不知道在ARM上会是什么)来忽略那些低优先级的中断,使您仍然关心的中断足够高,以至于它们不被屏蔽。请参见:http://stackoverflow.com/questions/13713064/how-to-disable-enable-interrupts-on-a-stm32f107-chip/13720554 以获取更多模糊的指导;) - John U
@BogdanAlexandru:我怀疑ARM在屏蔽时会忽略中断。 位仍然被设置,但不触发ISR。 我认为它至少会有1个中断的缓冲区,但这可能不够好,因为XYZ是一个非常耗时的函数。 或许可以在NVIC中设置此缓冲区的深度? 我可能错了,在这方面,你有没有查看文档? - uniquenamehere

0
在处理程序之前,XYZ从缓冲区中复制所有内容并使用副本进行操作。我相信这是最好的方式,在编写GPS解析器时非常有帮助。

0

你有没有考虑为你的系统使用RTOS?我知道这可能需要一些重构,但它可以为你的系统提供任务处理的灵活性和分辨率。如果你正在使用STM32的CubeIDE,启用、配置和运行RTOS相对简单。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接