请教一下串口printf的实现方案?

2019-08-23 15:43发布

这个问题已经想了很久了,一直没有一个好的方法,即使是在有RTOS的情况下。
这里说的串口printf,就是把printf retarget到串口上,但是不能用while(usart->tc!=1){}这样的代码来让CPU空转等待它完成。

到最后感觉应该靠DMA来完成,有两套方案,但是都有问题:

方案1、这个方案的时间不依赖实时系统的存在。建立一个buffer数组,在printf中寻找空的可用buffer,先用sprintf把 printf的内容传入这个可用buffer,然后开启DMA,在DMA的TC中断中再次检查buffer,如果有已经填充好的buffer,再次开启 DMA,然后形成这样一个循环。

问题:DMA传输虽然不占用CPU时间,但串口的DR寄存器是一直被占用了的,而且这个占用时间和直接用轮询等待的方式是一样的;如果在短时间内 连续大量的调用printf让缓冲区填满了,但DMA那边还在慢慢的传,串口DR寄存器还是被占用着,buffer又不能写了,丢失数据的问题就开始出现 了。

方案2:实现创建一个信号量。在printf函数中,先调用sprintf把内容传入字符串,开启DMA传送,然后无限时的等待获取信号量。这个 时候调用printf的这个任务事实上就被suspend了,系统可以切到其他任务执行。等DMA的TC中断发生了,释放这个信号量,printf那里的 阻塞被解除,调用printf函数的任务得以继续进行。

问题:很明显,单任务时没问题,多任务时直接完蛋。TC中断给出的信号量可能有好几个任务里的printf在等待,你到底要解除哪个printf 的阻塞呢?更严重的是,其他任务中的printf完全可能在第一个任务的printf对DMA的占用没有释放时就请求调用DMA,这个时候数据丢失也会发 生。

在有实时系统的情况下,到底要如何解决这种问题呢?目的很简单,printf本身的执行时间很短(不依靠轮询),执行后的等待由借助RTOS调度完成。
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
13条回答
ufbycd
2019-08-25 00:22
时间有点久远了,但还要说一下:
合理的实现方式:环形队列 + 中断发送
这应该很普通的算法呀    感觉搞MCU的  算法普遍不太强
我写的例子可以参考下:

[mw_shl_code=c,true]/*
* serial.c
*
*  Created on: 2016年8月5日
*      Author: chenss
*/

#include "serial.h"
#include "circular_buffer.h"
#include "semphr.h"

#define USE_CIRCULAR_BUFFER 1

// XXX 目前使用此功能有异常
#define USE_SAFE_PUT_CHAR 0

#define _TX_BUFFER_SIZE 128u

#if USE_CIRCULAR_BUFFER
static volatile bool _isTransmitting;
static uint8_t _txBuf[_TX_BUFFER_SIZE];
static cbuf_t _txCtrl;
#endif

#if USE_SAFE_PUT_CHAR
static SemaphoreHandle_t _sempHandle;
#endif

void Serial_Init(void)
{
        GPIO_InitTypeDef GPIO_InitStructure;
        USART_InitTypeDef uartConfig;
        NVIC_InitTypeDef nvicConfig;

#if USE_CIRCULAR_BUFFER
        _isTransmitting = false;
        CBUF_Init(&_txCtrl, _txBuf, sizeof(_txBuf));
#endif

#if USE_SAFE_PUT_CHAR
        _sempHandle = xSemaphoreCreateCounting(_TX_BUFFER_SIZE, _TX_BUFFER_SIZE);
        configASSERT(_sempHandle);
#endif

        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

        nvicConfig.NVIC_IRQChannel = USART1_IRQn;
        nvicConfig.NVIC_IRQChannelPreemptionPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY + 1;
        nvicConfig.NVIC_IRQChannelSubPriority = 8;
        nvicConfig.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(& nvicConfig);

        USART_StructInit(&uartConfig);
        uartConfig.USART_BaudRate = 115200;

        /* Configure USART Tx as push-pull */
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOA, &GPIO_InitStructure);

        /* Configure USART Rx as input floating */
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
        GPIO_Init(GPIOA, &GPIO_InitStructure);

        /* USART configuration */
        USART_Init(USART1, &uartConfig);

        /* Enable USART */
        USART_Cmd(USART1, ENABLE);
}

char Serial_GetChar(void)
{
        return '';
}

void Serial_PutChar(char c)
{
#if USE_SAFE_PUT_CHAR
        if(xSemaphoreTake(_sempHandle, 2))
#endif
        {
                if(c == ' ')
                {
                        Serial_PutChar(' ');
                }
#if USE_CIRCULAR_BUFFER
                int8_t ret;

                ret = CBUF_Write(& _txCtrl, & c);
                if((ret == 0) && (! _isTransmitting))
                {
                        _isTransmitting = true;
                        USART_ITConfig(USART1, USART_IT_TXE, ENABLE);
                }
#else
                USART_SendData(USART1, c);
                while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
#endif
        }
}

void USART1_IRQHandler(void)
{
#if USE_CIRCULAR_BUFFER
        elem_t c;

        if(CBUF_Read(& _txCtrl, & c) == 0)
        {
                USART_SendData(USART1, (uint16_t) c);
#if USE_SAFE_PUT_CHAR
                xSemaphoreGiveFromISR(_sempHandle, NULL);
#endif
        }
        else
        {
                USART_ITConfig(USART1, USART_IT_TXE, DISABLE);
                _isTransmitting = false;
        }
#endif
}[/mw_shl_code]

一周热门 更多>