几乎每个嵌入式系统都使用中断服务程序。如果你需要跟踪时间,你可能会有一个产生系统滴答的定时器中断。如果你有一个USART,你可能正在使用中断。使用DMA进行更高效的数据传输?你可能正在使用中断。
中断是嵌入式系统不可或缺的一部分。不幸的是,写得不好的ISR会导致系统出现争用情况、响应能力差,甚至CPU使用过度。
在本帖中,我们将探讨撰写高效ISR的几种最佳实践。
1.保持简短快速
中断超级酷!当你的代码正在执行并且发生重要事件时,程序会被中断以跳转到中断服务例程。发生跳转时,寄存器的当前状态需要存储在中断帧中。
中断帧被推送到堆栈上,并进入ISR。中断运行,然后中断帧恢复,应用程序恢复。可以想象,每次运行中断服务程序时,这个过程都会消耗CPU周期并产生开销。周期数因处理器和架构而异,但中断两端的周期数可能在12到数百个之间。
虽然我们对中断开销无能为力,但我们可以控制中断中使用的周期数。可以想象,ISR执行所花费的CPU周期越多,对我们的应用程序代码的影响就越大。长而慢的中断会导致程序不同部分的抖动和其他时序问题。它们甚至会导致其他中断丢失或延迟!
经验法则是让你的中断短而快!
2.不要调用函数
如果你希望你的ISR短而快,你应该避免在ISR内部进行函数调用。函数(尤其是那些开销巨大或执行复杂任务的函数)会大大增加ISR的执行时间。增加的执行时间会导致中断丢失或其他关键任务的延迟处理,从而可能导致系统不稳定。
当在ISR中调用一个函数时,它涉及到额外的步骤,如将当前上下文推送到堆栈上、跳转到函数代码并返回到ISR。这些额外的步骤消耗了宝贵的CPU周期。如果函数包含阻塞调用、等待I/O操作或依赖中断期间可能处于不一致状态的资源,则可能会严重影响系统的响应能力和可预测性。
现在,你可能认为编写ISR会违反软件开发最佳实践。毕竟,我们不应该模块化我们的代码吗?没有职能,我们的ISR不会变得混乱吗?
使用一些技巧来规避“不调用函数”的最佳实践:
1)使用静态编译或预处理程序。
根据你的语言,复杂的计算或至少部分计算可能会在编译时执行。通过在编译时执行这些计算,ISR在运行时需要执行的工作将会减少。
2)内嵌函数
你仍然可以将你的ISR代码放入函数中,但是不要将它们作为常规函数,而是使用inline关键字。inline关键字会建议编译器不要调用该函数,而是应该将该函数的内容“复制并粘贴”到调用者中。
内联函数将消除与函数调用相关的开销。被警告!只是给编译器的一个建议!你必须在程序集中验证该函数实际上是内联的。
注意:今天大多数编译器会采纳我们的建议,但你不能毫无疑问地相信它!
通过避免ISR中的函数调用,你可以保持中断处理的效率和可靠性,确保你的系统在各种条件下保持响应和稳定。
3.将进程卸载到其他线程
中断并不是用来做很多繁重的工作的。我们希望中断短而快,这意味着它应该做最少需要做的事情。例如,如果你正在通过USART接收作为数据包一部分的字节,则不会在中断中处理该数据包。你处理该字节,然后设置一个标志来指示程序的另一部分应该处理该数据。
通过将密集的进程卸载到其他线程,你可以确保ISR保持高效和快速响应。这种方法可以在裸机或多线程环境中使用。以下是你如何有效地将处理任务转移到ISR之外的方法:
1)设置标志
使用简单的标志来指示事件已经发生。主程序或另一个线程可以监视这些标志,并在安全的情况下进行必要的处理。
使用队列
实现队列将数据从ISR传递到其他线程。这样,ISR可以快速将数据排队并返回处理中断,同时主程序或专用工作线程可以处理排队的数据。
3)线程同步
确保ISR和其他线程之间的正确同步,以避免竞争情况和数据损坏。根据需要使用互斥、信号量或其他同步机制。
通过遵循这些实践,你可以在嵌入式系统中保持高水平的性能和可靠性。这将确保ISR保持快速和高效,同时安全地处理和控制更复杂的处理。
4.对共享变量使用volatile
当ISR和主程序共享变量时,将它们声明为volatile是至关重要的。volatile关键字告诉编译器,变量值可能会在程序流的控制之外随时更改,从而防止编译器应用假定值不会意外更改的特定优化。
如果没有volatile关键字,编译器可能会优化掉必要的读取或写入,从而导致不可预测的行为和难以诊断的错误。Volatile将为你做三件事:
1)阻止优化
编译器假定非易失性变量不会改变,除非程序显式修改它。对于共享变量,这种假设是错误的,因为ISR可以随时更改变量。将变量声明为volatile会阻止编译器优化必要的读取或写入。
2)确保数据是新鲜的
使用volatile时,编译器总是从内存中读取值,而不是使用寄存器中的缓存值。这确保了主程序看到ISR写入的最新值,反之亦然。
结论
中断服务例程对于每个嵌入式系统都至关重要。如果你想让你的系统反应灵敏和高效,你必须正确地实现你的中断。我无法告诉你我遇到性能差的系统的频率,根本原因是ISR写得不好。
如果你遵循本文中的最佳实践,你的中断会表现得更好,引起的问题也会更少。