网上最好的printf? 移植和例程!

💡 原文中文,约3500字,阅读约需9分钟。
📝

内容提要

这篇文章介绍了一个由麻省理工的年轻工程师编写的printf函数,被称为嵌入式系统中最出色的printf函数。文章分享了两款芯片的移植例程和移植教程,并提供了代码和配置方法。

🔎

延伸解读

为何要替换传统 printf

文章指出,传统 printf 依赖 vsprintf 将格式化结果先存入缓冲区,再整体发送。在多任务环境中,若高优先级任务抢占并复用同一缓冲区,会导致数据错乱甚至程序死机。这种不安全源于共享缓冲区,而非 printf 本身。作者强调,通过临界区或信号量虽可缓解,但并非根本解决。

printf_u1 的线程安全思路

printf_u1 的核心是取消中间缓冲区,直接将格式化后的每个字符通过回调函数发送出去。这样即使任务被抢占,也不会出现多个任务同时操作同一块内存的情况。文章中的 _out_char 和 _putchar 函数就是实现这一机制的关键,用户需根据串口数量编写对应的输出函数。

移植时的版本与配置差异

作者使用 Keil 5.21a 和 IAR for 8051 10.10.1 进行演示,并提醒不同版本配置可能不同。在 Keil 中需选择 C99 标准并修改 _vsnprintf 相关代码;在 IAR 中则需添加文件、设置头文件路径,并处理 stdint.h 缺少 64 位无符号整型定义的问题。这些细节直接影响移植能否成功。

实际移植的灵活性与局限

文章提供了 stm32f4ZG 和 cc2530f256 两款芯片的例程,并指出方法可推广到其他芯片。但移植并非完全通用:需根据具体芯片的串口寄存器修改 _putchar 中的数据发送逻辑,且不同编译器版本可能引入额外配置步骤。读者应结合自身硬件和工具链调整,而非直接照搬。

❓

Q&A

这个printf函数有什么特别之处?

这个printf函数被称为嵌入式系统中最出色的printf函数,具有线程安全性,避免了传统printf在多线程环境中的冲突问题。

如何在stm32f4ZG上移植这个printf函数?

在stm32f4ZG上移植printf函数需要配置keil,添加文件路径和修改代码,具体步骤包括选择C99标准和修改_vsprintf函数。

为什么需要移植printf函数?

移植printf函数是为了防止多个寄存器同时访问同一块内存,避免程序死机或卡死的问题。

在IAR中如何配置printf函数?

在IAR中配置printf函数需要添加文件、设置编译器头文件路径,并处理可能出现的编译错误,如stdint.h未定义的问题。

新函数printf_u1是如何提高安全性的?

printf_u1通过直接发送参数,避免了使用缓冲区,从而提高了线程安全性,防止了数据冲突。

使用这个printf函数有什么潜在风险吗?

虽然新函数提高了安全性,但在不当配置或使用时,仍可能出现其他类型的错误,如内存访问冲突。

➡️

继续阅读