我的系统足够简单,可以在没有操作系统的情况下运行,我只使用中断处理程序,就像在桌面程序中使用事件侦听器一样。在我在线阅读的所有内容中,人们都尽量少花时间在中断处理程序上,并将控制权交还给任务。但我没有操作系统或真正的任务系统,也找不到关于无操作系统目标的设计信息。
我基本上有一个中断处理程序,它从USB读取一块数据并将数据写入内存,还有一个中断处理器,它读取数据,在GPIO上发送数据,并再次在硬件计时器上调度自己。
以我的方式使用中断,并使用NVIC(我使用cortex-M3)来管理工作层次结构有什么问题?
首先,在这个问题的上下文中,让我们将操作系统称为调度器。
现在,与线程不同,中断服务例程"高于"调度方案。
换句话说,调度器对它们没有"控制权"。
ISR作为硬件中断的结果进入执行,硬件中断将PC设置为代码段中的不同地址(更准确地说,设置为中断向量,在调用ISR之前,您可以在其中"做一些事情")。
因此,本质上,任何ISR的优先级都高于具有最高优先级的线程的优先级。
因此,在ISR中花费尽可能少的时间的一个明显原因是,ISR对您为系统设计的调度方案有"副作用"。
由于您的系统完全是中断驱动的(即,没有调度程序,也没有线程),所以这不是问题。
但是,如果不允许嵌套ISR,则必须从中断发生的那一刻起禁用中断,直到相应的ISR完成为止。在这种情况下,如果ISR正在执行时发生任何中断,那么您的程序将有效地忽略它
因此,你在ISR中呆的时间越长,你"错过"中断的可能性就越高。
在许多桌面程序中,事件被发送到队列,并且有一些"事件循环"来处理这个队列。此事件循环逐个处理事件,因此不可能逐个中断一个事件。在事件驱动编程中,使所有事件处理程序尽可能短也是一种很好的做法,因为它们是不可中断的。
在裸机编程中,中断类似于事件,但不会发送到队列。
- 中断处理程序的执行不是顺序的,它们可以被优先级较高的中断中断(Cortex-M3中的数字较低)
- 没有相同中断的队列-例如,当您处于该中断中时,您无法检测到多个GPIO中断-这就是您应该使所有例程尽可能短的原因
可以自己实现队列,通过中断提供这些队列,并在超级循环中使用这些队列(在禁用所有中断的同时使用)。通过这种方法,您可以获得中断的顺序处理。如果您保持处理程序较短,那么这基本上是不需要的,您可以直接在处理程序中完成工作。
在基于操作系统的系统中,使用队列、信号量和"中断处理程序任务"来处理中断也是一种很好的做法。
使用裸机,只要您进行分析,就可以为应用程序绑定或中断/事件绑定进行设计。因此,如果你知道什么事件/中断以什么速率发生,并且你可以确保在所需/设计的时间内处理所有事件/中断,那么你当然可以在事件/中断处理程序中慢慢来,而不是快速地向前台任务发送标志。
当然,常见的方法是快速进出,只保存足够的信息来处理前台任务。前台任务必须旋转轮子,当然要寻找事件标志、优先级等。
当然,您可以让它变得更复杂,当中断/事件到来时,保存状态,并以forground模式而不是中断模式返回到forground处理程序。
现在这些都是通用的,但针对cortex-m3,我不认为真的有像老大哥ARM这样的模式。只要你采取实时方法,确保你的处理程序是确定的,你做你的系统工程,并确保不会发生事件/中断堆积的情况,从而导致响应不确定,不会太晚或太长或丢失东西,是可以的
你要问自己的是,在所有情况下,所有事件是否都能及时得到服务:
例如;
- 如果您的中断系统运行到完成,一个中断的服务是否会导致另一个中断服务出现不可接受的延迟
- 另一方面,如果中断系统是基于优先级和抢占式的,那么高优先级中断的服务是否会不可接受地延迟较低的中断
在后一种情况下,可以使用速率单调分析来分配优先级,以确保最大的响应性(执行时间最短的处理程序获得最高的优先级)。在第一种情况下,您的系统可能缺乏一定程度的确定性,并且在事件负载和代码更改下,性能都是可变的。
一种方法是将处理程序分为实时关键和非关键部分,时间关键代码可以在处理程序中完成,然后在"大循环"系统中设置一个标志,提示在"后台"非中断上下文中执行非关键操作,该系统只需轮询事件标志或共享数据即可完成工作。通常,中断处理程序中可能需要的只是复制一些数据来给一些事件加上时间戳,从而使数据可用于后台处理,而不会阻碍新事件的处理。
对于更复杂的调度,有许多简单、低成本或免费的RTOS调度器,它们以非常小的占地面积提供多任务、同步、IPC和定时服务,并且可以在非常低端的硬件上运行。如果你有一个硬件定时器和10K的代码空间(有时更少),你可以部署RTOS。
我首先处理您描述的问题
根据我的理解,您的目标是创建一个设备,通过从USB接收命令,输出一些GPIO,如LED、继电器等。对于这个简单的任务,您的方法似乎很好(如果USB层能够充分使用它)。
然而,存在优先级问题,在这种情况下,可能是因为如果USB端过载(来自电缆另一端的数据),并且中断处理的优先级高于定时器触发的优先级,即处理GPIO,GPIO端可能会错过滴答声(就像其他人解释的那样,中断无法排队)。
在你的情况下,这是关于可以考虑的内容。
一些通用指南
对于"在中断处理程序中花费尽可能少的时间",其原理正是其他人所说的:操作系统可以实现队列等,但硬件中断没有提供这样的概念。如果导致中断的事件发生,CPU将进入处理程序。然后,直到您处理它的源(例如在UART的情况下读取接收保持寄存器),您才会丢失该事件的任何进一步发生。在这一点之后,直到退出处理程序,您可能会收到事件是否发生,但不会收到发生了多少次(如果事件在CPU仍在处理处理程序时再次发生,则相关的中断行将再次处于活动状态,因此从处理程序返回后,CPU会立即重新输入,前提是没有更高的优先级在等待)。
上面我描述了在8位处理器和AVR 32位上可观察到的一般概念(我有这些方面的经验)。
当设计这样的低级别系统(没有操作系统、一个"后台"任务和一些中断)时,了解每个优先级上发生了什么是至关重要的(如果你使用这样的系统)。一般来说,你会把最实时的关键任务列为最高优先级,尽可能快速地为这些任务提供服务,同时在优先级较低的情况下更放松。
从另一个方面来看,通常在设计阶段,可以计划系统应该如何对错过的中断做出反应,因为在有中断的地方,错过一个最终会发生。通过通信线路的关键数据应该有足够的校验和,特别关键的计时器应该来自计数寄存器,而不是事件计数等等。
中断的另一个令人讨厌的部分是它们的异步性质。如果你不能正确设计相关的锁,它们最终会破坏一些东西,让那些不得不调试它的可怜的人做噩梦。"在中断处理程序中花费尽可能少的时间"语句也鼓励你保持中断代码的合理短度,这意味着要考虑的代码也更少。如果你也使用RTOS辅助的多任务处理,你应该知道这一部分(尽管有一些区别:较高优先级的中断处理程序的代码不需要针对较低优先级的处理程序的保护)。
如果你能就必要的异步任务正确地设计你的架构,那么在没有操作系统的情况下(从无多任务的角度来看)甚至可能是一个更好的解决方案。它需要更多的思考来正确地设计它,然而后来与锁定相关的问题要少得多。我完成了一些中型安全关键项目,这些项目是在一个背景"任务"上设计的,很少有中断,与公司中基于多任务概念构建的其他项目相比,这些项目的经验和维护需求(尤其是漏洞跟踪)相当令人满意。