1-MicroLIB与printf重定向

参考文章

使用MicroLIB原因

当前的嵌入式系统主流由C语言进行开发,大多需要使用到C语言标准库的功能,然而C语言标准库过于庞大,难以直接应用于嵌入式系统当中,需要对其简化,进而形成了MicroLIB库。

MicroLIB与C标准库区别
  1. MicroLib 专为深度嵌入式应用而设计;
  2. MicroLib 经过优化,可以使用比使用 ARM 标准库更少的代码和数据存储器;
  3. MicroLib 设计为无需操作系统即可工作,但这并不妨碍它与任何操作系统或 RTOS(例如 Keil RTX)一起使用;
  4. MicroLib 不包含文件 I/O 或宽字符支持;
  5. 由于 MicroLib 已经过优化以最小化代码大小,因此某些函数的执行速度将比 ARM 编译工具中可用的标准 C 库例程更慢;
  6. MicroLib 和 ARM 标准库都包含在 Keil MDK-ARM 中;
  7. 有关更多详细信息,请参阅与默认 C 库的差异,参考
printf重定向目的

C语言程序编写过程中需要打印调试,最为常用的是printf函数,该函数是将信息打印到屏幕上,然而嵌入式系统没有计算机常规定义的屏幕,因此需要将printf函数进行改写。
此外,printf函数位于标准库中,基于嵌入式的printf同样位于MicroLIB中,在嵌入式系统中使用printf函数,需要添加MicroLIB。

printf重定向操作

如何对printf进行改写?具体printf需要经过多层函数的实现,其中有一个名为fputc的函数,

C 库函数 int fputc(int char, FILE *stream) 把参数 char 指定的字符(一个无符号字符)写入到指定的流 stream 中,并把位置标识符往前移动。

通过重写这个函数,将输出流指定到嵌入式系统的一个串口上,即可以通过这个串口输出打印信息,再通过这个串口连接到上位机,则可在上位机上观察到调试信息。

<think>嗯,用户的问题是关于如何在不使用MicroLib的情况下实现printf重定向。我需要先理解用户的需求。他们可能在使用ARM的MDK(Keil)开发环境,并且希望在没有MicroLib的情况下,仍然能够通过串口输出printf的内容。根据之前的引用资料,用户已经知道MicroLib需要重定向fputc,但现在他们想不用MicroLib,所以需要另一种方法。首先,我得回忆一下标准MicroLib的区别。MicroLib是专门为嵌入式系统设计的简化,而标准可能依赖半主机模式,这在嵌入式系统中通常不可行。用户提到引用5中的方法,即不使用MicroLib时如何避免半主机模式。这里的关键点可能是重定向底层函数,同时禁用半主机。根据引用4和5,不使用MicroLib的话,需要处理半主机的问题。半主机模式允许目标设备通过调试通道主机通信,但在实际嵌入式环境中可能不可用。因此,必须覆盖相关函数,如_sys_exit,并定义必要的结构体和文件描述符。此外,还需要实现fputc来将字符发送到串口。用户可能已经尝试过包含MicroLib的情况下的重定向,但现在想切换到标准。需要确保代码中不依赖MicroLib,同时正确重定向输出函数。这可能涉及到编译器设置,比如在Keil中取消勾选UseMicroLib,然后在代码中添加特定的重定向代码。另一个需要注意的点是,不同版本的编译器(如ARMCLIB版本)可能需要不同的结构体定义。例如,AC5和AC6编译器可能有差异,所以在代码中需要做条件编译处理。引用4提到如果使用AC5,需要定义struct__FILE,而AC6可能不需要。然后,用户可能需要实现fputc和可能的其他函数,如_ttywrch,以确保所有输出都正确重定向。此外,禁用半主机模式需要#pragma指令,如引用5中的#pragmaimport(__use_no_semihosting),这告诉编译器不要使用半主机功能。总结步骤应该是:1.禁用半主机模式;2.实现必要的系统调用函数(如_sys_exit);3.定义FILE结构体和__stdout;4.重定向fputc到串口发送函数;5.处理编译器版本差异。用户可能遇到的常见问题包括未正确禁用半主机导致链接错误,或者未正确实现所有必要的函数。需要确保所有步骤都被正确执行,特别是编译器设置和代码中的重定向部分。最后,验证方法也很重要,比如检查是否链接了半主机,或者通过调试查看字符是否正确发送到串口。这可能需要在代码中添加测试语句,并通过串口调试助手查看输出。</think>在不使用MicroLib的情况下实现`printf`重定向,需通过禁用半主机模式并手动重定向底层函数。以下是具体步骤: --- ### **步骤1:禁用半主机模式** 半主机模式依赖调试器主机通信,但嵌入式系统中通常需避免。在代码开头添加以下指令: ```c #pragma import(__use_no_semihosting) // 禁止使用半主机模式 ``` --- ### **步骤2:实现必要的系统调用函数** 标准依赖`_sys_exit`等系统函数,需手动定义空实现以避免链接错误: ```c // 定义空函数避免半主机依赖 void _sys_exit(int x) { x = x; // 仅用于占位,无实际操作 } // 若使用C++,需额外定义_ttywrch防止链接错误 void _ttywrch(int ch) { ch = ch; } ``` --- ### **步骤3:定义文件描述符结构体** 标准需要`FILE`结构体和`__stdout`对象: ```c // 定义FILE结构体(针对AC5编译器) struct __FILE { int handle; }; FILE __stdout; // 标准输出描述符 ``` --- ### **步骤4:重定向fputc函数** `printf`最终调用`fputc`输出字符,需将其重定向到串口: ```c int fputc(int ch, FILE *f) { // 将字符发送至USART1(需替换为实际串口发送函数) USART_SendData(USART1, (uint8_t)ch); // 等待发送完成 while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); return ch; } ``` --- ### **步骤5:编译器配置** 在Keil/MDK中取消勾选**Use MicroLib**: 1. 打开工程选项 → Target → 取消勾选**Use MicroLib**。 2. 确保代码中包含了上述重定向函数。 --- ### **验证调试** 1. **检查半主机依赖**:若未正确禁用半主机,链接时会报错`undefined symbol __use_no_semihosting`[^5]。 2. **串口输出测试**:通过`printf("Hello World\n");`发送数据,用串口调试工具查看是否接收成功[^2][^4]。 --- ### **常见问题** 1. **为何要定义`_ttywrch`?** C++代码可能依赖此函数输出宽字符,若未定义会导致链接错误[^5]。 2. **AC6编译器需要修改吗?** AC6编译器可能无需定义`struct __FILE`,但仍需重定向`fputc`[^4]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值