1.一文解决printf()是源码如何与UART外设驱动函数“勾搭”起来的?
2.困惑多年,为什么printf可以重定向?
3.printfè¾åº
4.cè¯è¨è¾åº~~~~~
一文解决printf()是详解如何与UART外设驱动函数“勾搭”起来的?
在嵌入式编程中,输出调试信息是源码定位和分析问题的重要手段。本文将通过 IAR 开发环境探讨如何利用微控制器内的详解硬件 UART(通用异步接收/发送)外设实现打印信息输出。首先,源码让我们了解一下打印输出的详解信号灯生产源码整体软硬件结构。硬件方面,源码涉及到 PC 主机、详解目标板 MCU、源码串口线(RS 或 TTL 串口转 USB 模块)。详解在软件层面,源码PC 需要串口调试助手,详解目标板的源码crc线程检测源码 MCU 应用程序则需包含打印输出代码。当 MCU 程序运行时,详解通过 UART 外设将打印字符物理传输至 PC 上的源码调试助手,实现信息显示。
深入探讨到 C 标准头文件 stdio.h,这是 C 语言提供的输入输出标准库,由工具链自动提供,不需用户手动添加。stdio.h 包含了如 printf() 等函数的定义。在嵌入式 IAR 环境下,虽然这些函数的底层实现细节可能不为用户所见,但它们确实与 UART 外设驱动函数紧密相连。因此,著作发表网站源码了解 printf() 等函数如何与 UART 外设驱动交互是关键。
接下来,我们将关注 UART 外设驱动函数。例如,恩智浦 i.MXRT MCU 的 LPUART 驱动库提供了 LPUART_WriteBlocking() 和 LPUART_ReadBlocking() 等函数,用于数据发送和接收。虽然这些函数仅支持基本的数据传输,但通过结合 printf() 的格式化功能,可以实现更丰富的打印输出。
IAR 软件对 C 标准 I/O 库的支持是通过其预编译的底层接口实现的。在 IAR 中编译和链接程序时,用户可以通过查看生成的凯时源码程序 .map 文件来了解函数的来源。本文将通过一个示例工程演示如何配置 IAR,以轻松发现底层接口函数,并了解如何实现与硬件 UART 外设交互的底层接口 __write() 函数。通过配置 Library 设置、选择适当的实现选项,用户能够看到 __write() 函数的原型及其依赖的接口函数。
实现底层接口 __write() 函数需要关注 IAR 提供的 DLIB 库中关于 I/O 的相关源码实现。在 DLIB 库中,可以找到实现 __write() 函数原型及其示例代码的文件。通过将 LPUART_WriteBlocking() 函数集成到 __write() 实现中,可以解决报错问题。在工程编译完成后,通城个子游戏源码用户可以通过查看生成的 .map 文件来了解 DLIB 库的组成和具体实现。
通过上述步骤,用户可以轻松理解 IAR 环境下 printf() 函数与 UART 外设驱动函数之间的交互过程,实现高效的调试信息输出。本文旨在提供一个全面的视角,帮助嵌入式开发者深入理解这一关键组件的集成与工作原理。
困惑多年,为什么printf可以重定向?
在编程的世界里,printf函数的重定向问题一直是个让初学者疑惑的点。为什么printf函数可以重定向到fputc函数?这背后有什么原理?让我们一起来深入探讨。 首先,让我们理解printf函数的底层机制。在实际应用中,printf函数最终会调用fputc函数来实现字符串输出。然而,fputc函数是标准库提供的,你无法直接修改它的源码。那么,如何在不修改标准库源码的情况下,将输出重定向到特定的串口或其他设备呢? 答案在于符号属性弱化(weak)。这个特性允许程序员注册一个回调函数,使得printf函数调用这个回调函数进行字符串输出,从而实现输出位置的改变。标准库正是利用了这一特性,避免了直接修改源码的复杂操作。 那么,符号属性弱化(weak)到底有什么好处呢?让我们来一一列举:别人不需要提供源码,通过这个特性,你可以在不获取源码的情况下实现输出位置的改变。
即使没有源码,你也可以通过注册回调函数间接地改变输出位置,无需修改标准库。
如果有源码,你不需要删除别人的代码去重新实现,可以保留原有的代码,方便维护。
不需要使用回调函数进行注册,可以直接实现自己的版本,操作简单。
存在默认函数实现,即使不重新编写fputc函数,编译器也不会报错,保证了程序的稳定性。
要查看编译器链接的函数,只需打开map文件,搜索对应函数名即可。你会发现,即使主文件中也有同名函数,编译器链接的往往是其他文件中的函数,原因就在于主文件中函数的符号属性被弱化了。 理解了这个机制,你就能明白为什么在任何文件内定义中断处理函数,而即使没有定义,编译器也不会报错。这就是符号属性弱化在中断处理函数中的应用。 此外,对于实现不同的串口打印输出,使用vsprintf(或更安全的vsnprintf)函数是一个更好的选择。它允许你指定输出到特定的缓存空间,从而实现自定义的printf函数,灵活性更高。 通过深入理解符号属性弱化这一特性,你不仅能够解决printf函数重定向的问题,还能更好地理解C语言的动态链接机制。如果你对这个解释感到满意,不妨点赞以示鼓励吧!printfè¾åº
ä¸é¢æ¯printfçæºä»£ç ãå¯ä»¥çå°ï¼
1ï¼å¦æfield_widthè¾å ¥æ¯'*'çè¯ï¼ä¼ä»va_argå½æ°åå¼ã
å¦æåå¾çè¿åå¼field_widthå°äº0çè¯ï¼ååç»å¯¹å¼ã
è¿å¯ä»¥è§£éï¼ä¸ºä»ä¹-7å7çæææ¯ä¸æ ·çã
2ï¼å¦æprecision è¾å ¥æ¯'*'çè¯ï¼ä¼ä»va_argå½æ°åå¼ã
å¦æåå¾çè¿åå¼field_widthå°äº0çè¯ï¼åå0å¼ã
è¿å¯ä»¥è§£éï¼ä¸ºä»ä¹-2å0çæææ¯ä¸æ ·çã
å ¶å®ï¼åªè¦precision å¼å°äº0ï¼é½ä¼å0çæææ¯ä¸æ ·çã
楼主å¯ä»¥è¯è¯çã
/* get field width */
field_width = -1;
if (is_digit(*fmt))
field_width = skip_atoi(&fmt);
else if (*fmt == '*') {
++fmt;
/* it's the next argument */
field_width = va_arg(args, int);
if (field_width < 0) {
field_width = -field_width;
flags |= LEFT;
}
}
/* get the precision */
precision = -1;
if (*fmt == '.') {
++fmt;
if (is_digit(*fmt))
precision = skip_atoi(&fmt);
else if (*fmt == '*') {
++fmt;
/* it's the next argument */
precision = va_arg(args, int);
}
if (precision < 0)
precision = 0;
}
cè¯è¨è¾åº~~~~~
printf()å½æ°æ¯æ ¼å¼è¾åºå½æ°ï¼æå°è¾åºçææï¼"%æ ¼å¼\n"éé¢å å«ä¸¤å±ææï¼ï¼â%æ ¼å¼âè¡¨ç¤ºä½ è¾åºåéçæ ¼å¼ï¼æ¯å¦ä½ è¾åºæ´å½¢ï¼é£ä¹å°±æ¯%d,å¦æä½ åæ%Cé£ä¹å°±ä¸ä½ è¾åºåéçç±»åå°±ä¸å¹é ï¼å°±ä¸å¯¹äºï¼\næ¯å车æ¢è¡çææï¼æ¯å¦ä½ è¾å ¥ä¸¤ä¸ªåéprintfâ%d\n,%dâé£ä¹ç¬¬ä¸åéå°±ä¼æ¾ç¤ºå第ä¸è¡ï¼ç¬¬äºä¸ªåéå°±æ¾ç¤ºå¨ç¬¬äºè¡ãprintf("%æ ¼å¼\n"+åé)ä¸çåéä»£è¡¨ä½ è¦è¾åºçå 容