关于串口发送的重定向

这一节我们讲一讲怎样将串口的输出重定向到printf。

对于printf这个函数大家一定非常熟悉,这个函数用于打印输出。一般c语言编程的入门程序,就是用printf打印hello world。

而在单片机中,裸机编程的条件下,并没有一套直接可以调用的打印输出函数,直接调用printf函数是会报错的,需要我们自己去实现。

这里我们以之前实现的串口为基础,在它上面改写,实现把串口输出的数据从printf函数输出。

1)轮询形式的串口重定向到printf

printf函数是在stdio.h文件中定义的,它又会调用更底层的fputc这个函数,去一个个地打印出字符;所以,我们只要将fputc函数实现,就可以使用printf函数方便地输出了。

具体实现的代码如下:

其实质就是把串口发送一个字符的函数,封装到了fputc函数中。

这样我们再设置一下工程,选上lib,就设置好了:

之后就可以直接调用printf函数了,代码和运行情况见下图:

可以看出,printf函数的功能它都已经具备了,可以打印字符串、整型数、也可以打印转义字符。

上述的使用方法虽然很简单、很方便,但是有个致命的问题,效率不高,因为它重写fputc函数的时候,使用的是轮询的方法,执行这个函数的时候,几乎需要一直等到串口数据完全发完才能向后执行。

由于这个fputc函数每调用一次是发送一个字符,直接用HAL_UART_Transmit_IT中断或DMA发送的函数也是不行的;因为cubemx生成的中断或DMA发送的实现方式,是一次性只能发送一串数据,再次调用时,要等到上次的一串数都发完,才能正确发送下一次。对于fputc这种每发送一个字符要调用一次的用法,中断发送、DMA发送和轮询发送的效率是一样的,几乎都是要一直等到所有数据都发完。

2)发送中断和fifo实现的方法

一种提高效率的办法是使用发送fifo缓冲区,例如我们之前讲到的方法:将要发送的数据都放入fifo,由发送完中断自动从fifo中取数发送,这样就不用一直等到数据发完。下次有新数据来时,也是填入fifo中,不需要等待。

例如在我们之前改写的uart中断和fifo的工程下,如下改写fputc就可以了。

其中send_uart_byte函数内部实现过程大致如下:

(具体实现过程可以参见我之前的文章《串口中断和fifo环形队列》的讲解)

然后就可以调用printf函数使用了。这种实现方法效率高,只要发送fifo不溢出,使用基本没有什么限制,是非常方便的。

3)自己编写重定向函数

另一种方法是自己编写一个重定向的函数。既然标准的重定向函数fputc的用法受限,那么我们可以自己定义一个函数,去实现printf的功能,底层实现时跳过fputc的限制。

在usart.c文件中添加如下代码:(定义数组 print_temp 时也可以更大一些以免溢出)

这部分代码实际上是将要发送的一串数据,转存到了printf_temp中,然后再使用HAL_UART_Transmit_IT发送出去(使用DMA方式也是可以的),这样就不用等到每个字符都发送完。

然后就可以使用Uart1_printf这个函数了,用法和printf函数一样,如下图:

但是,仍然需要注意的是,HAL_UART_Transmit_IT这个函数连续发送两串数据时,仍然需要等待前一串数据发完才能发送后一串,否则后面的数据会丢失。

如下图,连续调用两次printf,后一次的数据有可能会覆盖到第一次的数据上:

综合上述的几种方法,我们可以选择自己最合适的实现方式:

如果系统简单,串口一直等待也无妨,就使用方法1,实现起来最简单;如果系统里不会短时间调用两次printf可以使用方法3,这样可以提高效率;如果可能发生短时间多次调用printf,而且追求高效率,可以使用方法2,但是改写和添加的代码会多一些。

好了,这一节就讲到这里。

欢迎大家关注我的公众号,更多学习资源分享:

posted @ 2021-11-08 01:19  xiaobaibai_2021  阅读(633)  评论(0编辑  收藏  举报