【问题标题】:Which Cortex-M3 interrupts can I use for general purpose work?我可以将哪些 Cortex-M3 中断用于通用工作?
【发布时间】:2011-02-14 16:19:38
【问题描述】:

由于特定中断发生,我需要运行一些代码。

我不想在中断本身的上下文中执行它,但我也不希望它在线程模式下执行。

我希望以低于促使其运行的高级中断但也高于线程级(以及其他一些中断)的优先级运行它。

我想我需要使用其他中断处理程序之一。

哪些是最好的使用,以及调用它们的最佳方式是什么?

目前,我计划只对一些我不使用的外围设备使用中断处理程序,并通过直接通过 NVIC 设置位来调用它们,但我希望有更好、更正式的方式。

谢谢,

【问题讨论】:

    标签: embedded interrupt cortex-m3


    【解决方案1】:

    您使用的是实时操作系统吗?通常,这种类型的事情将通过具有高优先级的线程来处理,该线程会通过中断发出信号以执行某些工作。

    如果您不使用 RTOS,您只有几个任务,并且由中断启动的工作不会占用太多资源,在中断处理程序。如果这些条件不成立,那么实现您所说的将是基本多任务操作系统本身的开始。这本身就是一个有趣的项目,但如果您只想完成工作,您可能需要考虑一个简单的 RTOS。

    由于您提到了有关您正在做的工作的一些细节,以下是我过去如何处理类似问题的概述:

    对于通过 UART 处理接收到的数据,我在处理不完全支持任务的更简单系统时使用的一种方法(即,任务在简单的while 循环中循环)是为从 UART 接收的数据建立一个共享队列。当 UART 中断触发时,数据从 UART 的 RDR(接收数据寄存器)中读取并放入队列中。以不损坏队列指针的方式处理此问题的技巧是小心地使队列指针易失,并确保只有中断处理程序修改尾指针,并且只有正在读取数据的“前台”任务离队修改了头指针。高级概述:

    • 生产者(UART 中断处理程序):

      1. 将queue.head 和queue.tail 读入本地;
      2. 增加本地尾指针(不是实际的queue.tail 指针)。如果您的增量超过队列缓冲区的末尾,则将其包装到队列缓冲区的开头。
      3. 比较 local.tail 和 local.head - 如果它们相等,则队列已满,您必须进行适当的错误处理。
      4. 否则您可以将新数据写入local.tail 指向的位置
      5. 现在才可以设置 queue.tail == local.tail
      6. 从中断返回(或处理其他与 UART 相关的任务,如果合适,例如从传输队列中读取)
    • 消费者(前台“任务”)

      1. 将queue.head 和queue.tail 读入本地;
      2. 如果local.head == local.tail 队列为空;返回让下一个任务做一些工作
      3. 读取local.head指向的字节
      4. 增加local.head 并在必要时将其包装起来;
      5. 设置queue.head = local.head
      6. 转到第 1 步

    确保queue.head 和queue.tail 是volatile(或在汇编中写入这些位)以确保没有排序问题。

    现在只需确保您的 UART 接收到的数据队列足够大,以便在前台任务有机会运行之前保存所有可能接收到的字节。前台任务需要将数据从队列中提取到自己的缓冲区中,以构建消息以提供给“消息处理器”任务。

    【讨论】:

    • 我没有使用 RTOS。我得到的是一个高优先级中断,每次我通过串行接收字节时都会关闭。它以高(低优先级)优先级运行,这样我就不会错过字节。收到完整的消息后,我需要做一些工作,虽然我打算让这项工作本质上是完全非阻塞的,但我不想限制自己拥有最多允许的时间(我赢了'不要写这段代码)。我没有做任何抢先式多任务处理。
    • 串口数据的速度是多少?速率真的如此之高或其他中断处理程序设计得如此糟糕以至于中断优先级会有所不同吗?为什么不使用 DMA 传输来降低中断频率或完全避免它们?
    • 我们在 57600 处运行,但可能会上升到 115200。我不能在进入的过程中使用 DMA 传输,因为表示消息结束的是字节之间的时间超过了某个阈值。我需要运行三项任务: 1. 串行消息接收器/发送器 2. 消息处理器 3. 一些后台繁重的工作。中间的是一个状态机,所以它假定几乎不做任何工作(只是更新状态)。我希望它们在不同的中断级别上运行,因为在较低级别错过截止日期更不容易调试(对我而言)。
    • 感谢您的深入回复。我现在正在做的实际上是一个深度队列。我的应用程序如此简单,以至于消息的解码、采取行动和编码回复都立即一个接一个地发生。这是主从总线上的从站。我拥有的内核只是一些充当消息接收传输代码的代码。
    • @Captain NedD:对不起,迈克尔;我以为我在原始问题中添加了评论!您可以使用 DMA 并定期轮询传输计数;如果它停止增加,则消息完成。无论哪种方式,115200 是每个字符 87 us;您可能过于担心缺少字符;特别是如果 UART 有 FIFO。
    【解决方案2】:

    “更正式的方式”或更确切地说是传统的方法是使用基于优先级的抢占式多任务调度程序和“延迟中断处理程序”模式。

    【讨论】:

    • @Clifford:你有“延迟中断模式”描述的链接吗?
    【解决方案3】:

    ARM Cortex 支持一种非常特殊的异常,称为 PendSV。您似乎可以完全使用此异常来完成您的工作。几乎所有 ARM Cortex 的抢占式 RTOS 都使用 PendSV 来实现上下文切换。

    要使其工作,您需要优先考虑 PendSV 低(将 0xFF 写入 NVIC 中的 PRI_14 寄存器)。您还应该优先处理 PendSV 之上的所有 IRQ(在 NVIC 的相应优先级寄存器中写入较低的数字)。当您准备好处理整个消息时,从高优先级 ISR 触发 PendSV:

    *((uint32_t volatile *)0xE000ED04) = 0x10000000; // trigger PendSV
    

    然后,ARM Cortex CPU 将完成您的 ISR 和所有其他可能被它抢占的 ISR,最终它将尾链到 PendSV 异常。这是您用于解析消息的代码所在的位置。

    请注意,PendSV 可能会被其他 ISR 抢占。这一切都很好,但是您显然需要记住通过代码的关键部分(简要禁用和启用中断)来保护所有共享资源。在 ARM Cortex 中,通过执行 __asm("cpsid i") 禁用中断,并通过 __asm("cpsie i") 启用中断。 (大多数 C 编译器为此提供了内置的内部函数或宏。)

    【讨论】:

    • 谢谢,米罗!我到处寻找有关如何触发 PendSV 中断的信息,这就是我终于找到它的地方!虽然我想如果我更仔细地阅读我的 Cortex M3 r1p1 技术参考手册,我会在描述中断控制状态寄存器的页面上找到它:infocenter.arm.com/help/topic/com.arm.doc.ddi0337e/…
    • 根据我的阅读,CortexM3 上的几乎所有 RTOS 都使用 Systick Timer 中断(十进制 45,十六进制 2D)来运行它们的调度程序。那么 PendSV 是启动一种特殊“系统调用”的方式吗?希望我明白这一点。
    • @WarrenP:PendSV 用于实际处理上下文切换。 sys tick 用于循环任务 - sys tick 通常依次设置 ​​PendSV。
    【解决方案4】:

    检查您的处理器文档。如果您写入通常必须在中断内清除的位,某些处理器将中断。我目前使用的是 SiLabs c8051F344,在规格表第 9.3.1 节中:

    "软件可以通过将任何中断挂起标志设置为逻辑 1 来模拟中断。如果为该标志启用中断,将产生中断请求,并且 CPU 将引导至与中断挂起标志关联的 ISR 地址。”

    【讨论】:

      【解决方案5】:

      您的要求在 Cortex-M3 上非常简单。您需要启用 STIR 寄存器,以便您可以使用软件触发低优先级 ISR。当高优先级 ISR 处理完关键内容后,它只会触发低优先级中断并退出。如果没有更重要的事情发生,NVIC 然后将尾链到低优先级处理程序。

      【讨论】:

        猜你喜欢
        • 2021-05-28
        • 2011-12-21
        • 1970-01-01
        • 2013-12-03
        • 1970-01-01
        • 2015-08-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多