为什么STM32上的retarget write都禁用所有irq?

Why are all irq disabled for retarget write on STM32?

我正在查看以下将 stdout 重定向到 STM32 Std 外设库中的 UART 的函数。

int _write(int fd, char *ptr, int len) {
  uint32_t primask = __get_PRIMASK();
  __disable_irq();

  for (int i = 0; i < len; i++) {
    while (USART_GetFlagStatus(RETARGET_CFG_UART, USART_FLAG_TXE) == RESET) {
    }
    USART_SendData(RETARGET_CFG_UART, (uint8_t) * (ptr + i));
  }

  if (!primask) {
    __enable_irq();
  }

  return len;
}

在通过 UART 传输之前,它会屏蔽可以通过 __disable_irq() 设置优先级的异常(据我所知,这包括所有外设和 GPIO 中断)。

我的问题是,为什么这个UART tx是这样实现的? 是否可以删除 disable/enable irq 调用,以便程序中的其他功能不会因可能冗长的数据事务而延迟?

这是一个 quasi-bad 设计。

一方面,总是调用__disable_irq __enable_irq是可疑的。

它可以防止 嵌套 调用,其中调用者 正在做 __disable_irq/__enable_irq.

它假设 涉及 Tx ISR。也就是说,UART 在轮询模式下是运行。但是,如果 that 是真的,为什么代码 disable/enable 中断了?

如果要发送到 UART Tx 的数据多于可以放入 Tx FIFO 的数据,代码[如您所见]将阻塞

这意味着base/task级别不能做其他事情。


当我做这样的UART代码时,我使用了一个环形队列。我定义了一个足够大以容纳任何突发事件的队列(例如队列为 10,000)。

设计假设如果调用 _write 并且在添加所有数据之前环形队列已满,则应增加环形队列大小(即环形队列已满是[致命]设计错误) .

否则,base/task 关卡试图发送过多数据。也就是说,它生成的数据多于以 Tx 波特率可以发送的数据。

_write进程:

  1. 将字节复制到环形队列。
  2. 在 UART Tx FIFO 允许的 space 范围内,将尽可能多的字节从队列复制到 UART。
  3. 如果 space 可用,将调用 Tx ISR。它重复步骤 (2)

这样,如果 UART Tx FIFO 已满,_write 不会 阻塞。

Tx ISR 将填补空白。也就是说,当 FIFO 中有更多 space 可用且 Tx 已“准备就绪”时,将调用 Tx ISR。


下面是一些伪代码来说明我的意思:

// push_tx_data -- copy data from Tx ring queue into UART Tx FIFO
int
push_tx_data(void)
{

    // fill Tx FIFO with as much data as it can hold
    while (1) {
        // no free space in Tx FIFO
        if (USART_GetFlagStatus(RETARGET_CFG_UART, USART_FLAG_TXE) != RESET)
            break;

        // dequeue byte to transmit (stop if queue empty)
        int i = tx_queue_dequeue();
        if (i < 0)
            break;

        // put this data byte into the UART Tx FIFO
        uint8_t buf = i;
        USART_SendData(RETARGET_CFG_UART, buf);
    }
}

// tx_ISR -- handle interrupt from UART Tx
void
tx_ISR(void)
{

    // presumeably interrupts are already disabled in the ISR ...

    // copy all the data we can
    push_tx_data();

    // clear the interrupt pending flag in the interrupt controller or ISR if
    // necessary (i.e.) USART_SendData didn't clear it when the FIFO was full
}

// _write -- send data
int
_write(int fd, char *ptr, int len)
{
    uint32_t primask = __get_PRIMASK();

    // running from task level so disable interrupts
    if (! primask)
        __disable_irq();

    int sent = 0;

    // add to [S/W] tx ring queue
    for (int i = 0; i < len; i++) {
        if (! tx_queue_enqueue(ptr[i]))
            break;
        ++sent;
    }

    // send data from queue into FIFO
    push_tx_data();

    // reenable interrupts (task level)
    if (! primask)
        __enable_irq();

    return sent;
}

我怀疑该代码的作者禁用所有中断只是因为可能有一些中断将字节写入 UART,并且作者想确保发送到 _write 函数的所有字节都得到连续写入,而不是在中间有其他不相关的字节。

我假设您系统中定义的所有 ISR 与串行传输相比都相对较快,并且接收方不关心传输中是否存在一些小的延迟。因此,如果这些中断与 UART 无关,则让任何中断处于启用状态应该没问题。

您应该牢牢掌握系统中的所有 ISR 以及它们的作用,然后您应该使用它来决定在此操作期间应禁用哪些特定的 IRQ(如果有的话)。