问题描述
我正在开展一个项目,在该项目中我通过 STM32f4 上的 DMA 连续获取数字样本。 DMA 在我做一些 DSP 的每个样本之后生成一个完整的回调中断。我的计划是允许 freeRTOS 在 DMA 等待回调时处理其他任务。但是,DMA 生成回调过于频繁,不允许 freeRTOS 运行。我想让它在每次 DMA 完成回调后,允许 freeRTOS 任务运行 6ms。我想从完整回调中调用 __disable_irq()
并从其中一项任务中调用 __enable_irq()
,但这并不能保证 6ms 我也有一个高优先级按钮中断。我还尝试仅禁用 DMA 中断调用 __set_BASEPRI(priority<<(8-__NVIC_PRIO_BITS))
,然后启动 6ms 的计时器。在调用 __set_BASEPRI(0)
中的定时器周期已过回调以启用 DMA 中断。但出于某种原因,这根本不允许 freeRTOS 运行。它在 DMA 完成回调和定时器周期已用回调之间来回切换。
我是嵌入式编程的新手,因此对此的任何评论都会有所帮助。谢谢。
解决方法
您不应该认为 DSP 进程与 RTOS 任务是分开的,在 RTOS 任务中执行 DSP - 信号处理是您系统中时间最关键的方面,您必须处理数据尽可能快地到达而不会丢失。
如果 DSP 是在中断上下文中完成并且使您的任务挨饿,那么很明显您在中断上下文中做了太多工作,并且中断率太高。你需要修正你的设计以获得更可调度的东西。
如果您的 DMA 传输是单个样本,则每个样本将获得一个中断 - ADC 会自行完成;因此,与直接 ADC 中断处理相比,以这种方式使用 DMA 没有任何优势。
相反,您应该使用块处理,因此您循环 DMA 一个 80 个样本的块,为此您在 40 个样本时获得半传输中断,在 80 个样本时获得全传输中断。因此,对于每个中断,您可能会触发一个任务事件或信号量,以将 DSP 处理推迟到高优先级 RTOS 任务。这实现了两件事;
- 在整个 n 个样本块采集时间中,RTOS 可以:
- 正在为前一个块执行 DSP 处理,
- 利用剩余的时间来处理优先级较低的任务。
- 用于上下文切换等的任何中断开销减少了 1/n,从而有更多时间执行核心信号处理和后台任务。
除了减少中断次数和软件开销外,信号处理算法本身在执行块处理时可以更容易地优化。
上述的一个变体是从 DMA 中断处理程序触发任务事件或信号量,您可以将新的样本块放在消息队列中,然后提供一些缓冲。如果 DSP 处理的确定性可能较低,则这很有用,因此不能总是保证在下一个块准备好之前完成一个块的处理。但是总体而言,平均而言,您仍然需要在获取块所需的时间内完成块处理,并留出时间用于其他任务。
如果您的低优先级任务仍处于饥饿状态,那么明显的迹象是您的 DSP 进程对于您的处理器来说太多了。可能有优化的余地,但那将是一个不同的问题。
使用建议的块处理策略,我过去曾将应用程序从以 200MHz 和 98% CPU 负载运行的 TI C2000 DSP 迁移到 60% CPU 负载下的 72MHz STM32F1xx。如果处理得当,性能提升可能非常显着。
关于您的“高优先级”按钮中断,我会质疑您的优先级分配。按钮是手动操作的,人类的响应和感知时间以 10 甚至 100 毫秒为单位进行测量。这几乎不是您的时间关键任务,而错过几微秒的 ADC 样本会导致您的信号处理严重出错。
您可能犯了将“高优先级”与“重要”混淆的错误。在上下文或实时系统中,它们不是一回事。您可以简单地轮询 低优先级 任务中的按钮,或者如果您使用中断,则中断应该只是向任务发出信号(或更实际地触发去抖动计时器)(请参阅Rising edge interrupt triggering multiple times on STM32 Nucleo 例如)。