我可以将哪些 Cortex-M3 中断用于通用工作?
我有一些代码需要在特定中断发生时运行。
我不想在中断本身的上下文中执行它,但我也不希望它在线程模式下执行。
我想以低于促使其运行的高级中断的优先级运行它,但也以高于线程级别(以及其他一些中断)的优先级运行它。
我想我需要使用其他中断处理程序之一。
哪些是最好使用的以及调用它们的最佳方式是什么?
目前,我计划只对一些我没有使用的外设使用中断处理程序,并通过直接通过 NVIC 设置位来调用它们,但我希望有一种更好、更官方的方法。
谢谢,
I'd have some code that needs to be run as the result of a particular interrupt going off.
I don't want to execute it in the context of the interrupt itself but I also don't want it to execute in thread mode.
I would like to run it at a priority that's lower than the high level interrupt that precipitated its running but also a priority that higher than thread level (and some other interrupts as well).
I think I need to use one of the other interrupt handlers.
Which ones are the best to use and what the best way to invoke them?
At the moment I'm planning on just using the interrupt handlers for some peripherals that I'm not using and invoking them by setting bits directly through the NVIC but I was hoping there's a better, more official way.
Thanks,
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
ARM Cortex 支持一种非常特殊的异常,称为 PendSV。看来您可以完全使用此异常来完成您的工作。事实上,ARM Cortex 的所有抢占式 RTOS 都使用 PendSV 来实现上下文切换。
为了使其工作,您需要优先考虑 PendSV 低电平(将 0xFF 写入 NVIC 中的 PRI_14 寄存器)。您还应该将所有 IRQ 的优先级设置为高于 PendSV(在 NVIC 中的相应优先级寄存器中写入较低的数字)。当您准备好处理整个消息时,从高优先级 ISR 触发 PendSV:
然后 ARM Cortex CPU 将完成您的 ISR 以及可能被它抢占的所有其他 ISR,最终它将尾链到 PendSV例外。这是解析消息的代码应该所在的位置。
请注意,PendSV 可能会被其他 ISR 抢占。这一切都很好,但您显然需要记住通过代码的关键部分(短暂禁用和启用中断)来保护所有共享资源。在 ARM Cortex 中,可以通过执行 __asm("cpsid i") 禁用中断,并通过 __asm("cpsie i") 启用中断。 (大多数 C 编译器为此目的提供内置函数或宏。)
ARM Cortex supports a very special kind of exception called PendSV. It seems that you could use this exception exactly to do your work. Virtually all preemptive RTOSes for ARM Cortex use PendSV to implement the context switch.
To make it work, you need to prioritize PendSV low (write 0xFF to the PRI_14 register in the NVIC). You should also prioritize all IRQs above the PendSV (write lower numbers in the respective priority registers in the NVIC). When you are ready to process the whole message, trigger the PendSV from the high-priority ISR:
The ARM Cortex CPU will then finish your ISR and all other ISRs that possibly were preempted by it, and eventually it will tail-chain to the PendSV exception. This is where your code for parsing the message should be.
Please note that PendSV could be preempted by other ISRs. This is all fine, but you need to obviously remember to protect all shared resources by a critical section of code (briefly disabling and enabling interrupts). In ARM Cortex, you disable interrupts by executing __asm("cpsid i") and you enable interrupts by __asm("cpsie i"). (Most C compilers provide built-in intrinsic functions or macros for this purpose.)
您使用 RTOS 吗?一般来说,这种类型的事情可以通过一个高优先级线程来处理,该线程收到中断信号以执行某些工作。
如果您不使用 RTOS,则只有几个任务,并且由中断启动的工作不会占用太多资源,那么在中断处理程序的上下文中完成高优先级工作可能是最简单的方法。如果这些条件不成立,那么实现您所讨论的内容将是基本多任务操作系统本身的开始。这本身就是一个有趣的项目,但如果您只想完成工作,您可能需要考虑一个简单的 RTOS。
由于您提到了有关您正在做的工作的一些细节,这里概述了我过去如何处理类似的问题:
对于通过 UART 处理接收到的数据,我在处理更简单的系统时使用了一种方法,不完全支持任务分配(即任务在简单的
while
循环中进行循环)的方法是为从 UART 接收的数据提供一个共享队列。当 UART 中断触发时,将从 UART 的 RDR(接收数据寄存器)读取数据并将其放入队列中。以不损坏队列指针的方式处理此问题的技巧是小心地使队列指针易失性,并确保只有中断处理程序修改尾指针,并且只有正在读取数据的“前台”任务off 队列修改了头指针。高级概述:生产者(UART 中断处理程序):
queue.head
和queue.tail
读入局部变量;queue.tail
指针)。如果您的增量已超过队列缓冲区的末尾,请将其包装到队列缓冲区的开头。local.tail
和local.head
- 如果它们相等,则队列已满,您必须执行适当的错误处理。local.tail
指向的位置消费者(前台“任务”)
queue.head
和queue.tail
读入局部变量;local.head
==local.tail
队列为空;返回让下一个任务做一些工作local.head
指向的字节local.head
并在必要时将其包装;queue.head
=local.head
确保
queue.head
和queue.tail
易失性
(或在汇编中写入这些位)以确保不存在排序问题。现在只需确保您的 UART 接收数据队列足够大,足以容纳在前台任务有机会运行之前可以接收的所有字节。前台任务需要将数据从队列中提取到它自己的缓冲区中,以构建消息以提供给“消息处理器”任务。
Are you using an RTOS? Generally this type of thing would be handled by having a high priority thread that gets signaled to do some work by the interrupt.
If you're not using an RTOS, you only have a few tasks, and the work being kicked off by the interrupt isn't too resource intensive, it might be simplest having your high priority work done in the context of the interrupt handler. If those conditions don't hold, then implementing what you're talking about would be the start of a basic multitasking OS itself. That can be an interesting project in its own right, but if you're looking to just get work done, you might want to consider a simple RTOS.
Since you mentioned some specifics about the work you're doing, here's an overview of how I've handled a similar problem in the past:
For handling received data over a UART one method that I've used when dealing with a simpler system that doesn't have full support for tasking (ie., the tasks are round-robined i na simple
while
loop) is to have a shared queue for data that's received from the UART. When a UART interrupt fires, the data is read from the UART's RDR (Receive Data Register) and placed in the queue. The trick to deal with this in such a way that the queue pointers aren't corrupted is to carefully make the queue pointers volatile, and make certain that only the interrupt handler modifies the tail pointer and that only the 'foreground' task that's reading data off the queue modified the head pointer. A high-level overview:producer (the UART interrupt handler):
queue.head
andqueue.tail
into locals;queue.tail
pointer). Wrap it to the start of the queue buffer if you've incremented past the end of the queue's buffer.local.tail
andlocal.head
- if they're equal, the queue is full, and you'll have to do whatever error handing is appropriate.local.tail
pointsconsumer (the foreground 'task')
queue.head
andqueue.tail
into locals;local.head
==local.tail
the queue is empty; return to let the next task do some worklocal.head
local.head
and wrap it if necessary;queue.head
=local.head
Make sure that
queue.head
andqueue.tail
arevolatile
(or write these bits in assembly) to make sure there are no sequencing issues.Now just make sure that your UART received data queue is large enough that it'll hold all the bytes that could be received before the foreground task gets a chance to run. The foreground task needs to pull the data off the queue into it's own buffers to build up the messages to give to the 'message processor' task.
您所要求的在 Cortex-M3 上非常简单。您需要启用 STIR 寄存器,以便可以通过软件触发低优先级 ISR。当高优先级 ISR 完成关键任务时,它只会触发低优先级中断并退出。如果没有更重要的事情发生,NVIC 将尾链到低优先级处理程序。
What you are asking for is pretty straightforward on the Cortex-M3. You need to enable the STIR register so you can trigger the low priority ISR with software. When the high-priority ISR gets done with the critical stuff, it just triggers the low priority interrupt and exits. The NVIC will then tail-chain to the low-priority handler, if there is nothing more important going on.
“更正式的方法”或更确切地说传统方法是使用基于优先级的抢占式多任务调度程序和“延迟中断处理程序”模式。
The "more official way" or rather the conventional method is to use a priority based preemptive multi-tasking scheduler and the 'deferred interrupt handler' pattern.
检查您的处理器文档。如果您写入通常必须在中断内清除的位,某些处理器会中断。我目前使用的是 SiLabs c8051F344,在规格表第 9.3.1 节中:
“软件可以通过将任何中断挂起标志设置为逻辑 1 来模拟中断。如果为该标志启用了中断,则会生成一个中断请求,并且CPU 将引导至与中断挂起标志关联的 ISR 地址。”
Check your processor documentation. Some processors will interrupt if you write the bit that you normally have to clear inside the interrupt. I am presently using a SiLabs c8051F344 and in the spec sheet section 9.3.1:
"Software can simulate an interrupt by setting any interrupt-pending flag to logic 1. If interrupts are enabled for the flag, an interrupt request will be generated and the CPU will vector to the ISR address associated with the interrupt-pending flag."