2022/11/12晚课讲到的使用中断的fputc的问题

韦老师提到的在fputc中不死等而是写入环形缓冲区的改进

我自己实现了一下,核心代码如下,但数据发送到一定量会卡住,无法继续发送

中断fputc.zip (607.7 KB)
这是我的代码,可以直接用keil打开,请老师帮忙看下

主要原因

  • 数据从TDR寄存器转移到位移寄存器的速度远小于cpu写入环形缓冲区的速度

还有一些问题

  • 如果在某些需要关中断的函数中,调用了printf,并且写入数据超过环形缓冲区大小时,会卡死,这种情况应该如何避免?关闭中断时system_tick中断也被关闭了,此时应该如何进行超时处理?

做了一些改进

  • 在fputc中不依据缓冲区为空来使能发送中断,而是没有使能发送中断就使能发送中断
  • 在中断服务函数中,缓冲区为空时关闭发送中断,同时置位发送完成标志txcplt_flag
  • 在fputc中缓冲区如果写入满了,则死等缓冲区中的数据全部发送完成
static volatile int txcplt_flag = 0;

int fputc(int ch, FILE *p)
{
    static RingBuffer_t rb;
    if (!rb) {
        rb = GetUsart1Rb();
    }

    // 如果环形缓冲区被写满,等环形缓冲区中的数据被发送完再写入数据
    if (RingBufferFull(rb)) {
        txcplt_flag = 0;
        while (txcplt_flag == 0);
    }

   // 把要发送的数据写入环形缓冲区
    RingBufferWrite(rb, (char *)&ch, sizeof(char));

    // 如果没有开启发送中断,则开启发送中断
    if ((__HAL_UART_GET_IT_SOURCE(&huart1, UART_IT_TXE)) == RESET) {
        __HAL_UART_ENABLE_IT(&huart1, UART_IT_TXE);
    }

    return ch;
}

void USART1_IRQHandler(void)
{
    static RingBuffer_t rb;
    char ch = 0;
    if (!rb) {
        rb = GetUsart1Rb();
    }
    
    if ((__HAL_UART_GET_FLAG(&huart1, UART_FLAG_TXE)) != RESET) {
        RingBufferReadByte(rb, &ch, 1);
        huart1.Instance->DR = ch;
        // 当环形缓冲区中的数据全部都发送完成,关闭发送中断,并置位发送完成标志
        if (RingBufferEmpty(rb)) {
            __HAL_UART_DISABLE_IT(&huart1, UART_IT_TXE);
            txcplt_flag = 1;
        }
    } 
    HAL_UART_IRQHandler(&huart1);
}

1 个赞