IAR 升级至 9.x 版本后,STM32 传统 printf 重定向使用的 fputc 函数会失效,出现 “Linker Error: no definition for __write” 等报错。核心原因是 IAR 9.x 的 DLIB 运行库对标准 IO 函数的调用逻辑发生变化,需通过重定向__write 函数实现串口输出。本文基于 ST 官方 LAT1295 应用笔记,以 STM32F746 芯片、IAR 9.3、STM32Cube_FW_F7_V1.17.0 为例,详解完整适配流程。
1. 核心背景:重定向逻辑变更原因
1.1 低版本 IAR 的 printf 重定向原理
低版本 IAR 中,printf 通过调用 fputc 函数实现字符输出,重定向 fputc 即可将数据导向串口,核心代码如下:
#include <stdio.h>
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE
{
HAL_UART_Transmit(&UartHandle, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
该代码在 IAR 9.x 前可正常工作,但 9.x 版本后 DLIB 库不再默认关联 fputc 与 printf,需通过__write 函数完成重定向。
1.2 IAR 9.x 重定向依据
根据 IAR 官方文档《EWARM_DevelopmentGuide.ENU.pdf》的 “BRIEFLY ABOUT RETARGETING” 章节说明:
- IAR 9.x 的 DLIB 库中,printf、fprintf 等函数会调用__write 函数处理输出;
- 需用户自定义__write 函数,指定输出设备(如串口),才能实现格式化信息打印。
2. 适配步骤:从工程配置到代码实现
第一步:IAR 工程库配置
需调整工程的库选项,确保 DLIB 支持完整格式化输出:
(1)Library Configuration 配置
- 打开工程选项(快捷键 ALT+F7),进入 “C/C++ Compiler → Library Configuration”;
- 选择 “Full” 模式(完整运行库),支持文件描述符、多字节格式化等功能,对应配置文件
DLib_Config_Full.h; - 该模式启用
_DLIB_FILE_DESCRIPTOR和_DLIB_FORMATTED_MULTIBYTE宏,满足 printf 复杂格式化需求。
(2)Library Option1 配置
- 进入 “C/C++ Compiler → Library Options 1”;
- “Printf formatter” 选择 “Full”(或 “Full, without multibyte support”),手动启用完整格式化功能;
- 若选择 “Auto”,IAR 可能未加载完整格式化模块,导致部分 printf 格式失效。
第二步:编写__write 函数与字符发送逻辑
核心是实现__write 函数(DLIB 库调用接口),并关联串口发送函数,完整代码如下:
#include <stdio.h>
#include <LowLevelIOInterface.h> // 需包含IAR底层IO接口头文件
// 串口句柄(需提前初始化,如UART1)
extern UART_HandleTypeDef UartHandle;
// 字符发送函数:单个字符通过串口输出
static int stm32_putc(int ch)
{
// 阻塞发送字符,超时时间0xFFFF(无限等待)
HAL_UART_Transmit(&UartHandle, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
// 重定向__write函数:DLIB库printf调用此函数输出数据
#pragma module_name = "?__write" // 指定模块名,避免链接冲突
size_t __write(int handle, const unsigned char *buffer, size_t size)
{
size_t sent_chars = 0;
// buffer为NULL时表示刷新缓冲区,直接返回
if (buffer == 0)
{
return 0;
}
// 仅处理标准输出(stdout)和标准错误(stderr)
if (handle != _LLIO_STDOUT && handle != _LLIO_STDERR)
{
return _LLIO_ERROR;
}
// 循环发送缓冲区所有字符
for (; size > 0; size--, buffer++)
{
if (stm32_putc(*buffer) < 0)
{
return _LLIO_ERROR; // 发送失败返回错误
}
sent_chars++;
}
return sent_chars; // 返回成功发送的字符数
}
第三步:串口初始化前提
需确保 UARTHandle 已完成初始化(波特率、数据位、校验位等配置),示例初始化代码:
UART_HandleTypeDef UartHandle;
void MX_USART3_UART_Init(void)
{
UartHandle.Instance = USART3;
UartHandle.Init.BaudRate = 115200;
UartHandle.Init.WordLength = UART_WORDLENGTH_8B;
UartHandle.Init.StopBits = UART_STOPBITS_1;
UartHandle.Init.Parity = UART_PARITY_NONE;
UartHandle.Init.Mode = UART_MODE_TX_RX;
UartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
if (HAL_UART_Init(&UartHandle) != HAL_OK)
{
Error_Handler();
}
}
3. 功能验证:printf 串口输出测试
3.1 测试代码
在 main 函数中调用 printf 输出格式化信息:
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_USART3_UART_Init(); // 串口初始化
while (1)
{
printf("STM32F746 UART Printf Test: %drn", 123);
printf("Float Test: %.2frn", 3.14f);
HAL_Delay(1000);
}
}
3.2 验证结果
- 编译工程(无链接错误),下载至开发板;
- 用串口工具(如 Tera Term)打开对应串口(波特率 115200、8N1);
- 可观察到每秒输出一行格式化信息,证明重定向成功。
4. 关键注意事项
- 头文件依赖:必须包含
<LowLevelIOInterface.h>,否则_LLIO_STDOUT、_LLIO_ERROR等宏未定义; - 模块名声明:
#pragma module_name = "?__write"不可省略,避免 IAR 链接时找不到__write 函数定义; - 串口句柄一致性:
stm32_putc中使用的 UARTHandle 需与初始化的串口句柄一致,避免发送失败; - 超时时间设置:
HAL_UART_Transmit的超时参数(如 0xFFFF)需合理,避免阻塞过久或发送不完整; - 库模式匹配:若选择 “Small” 库模式,需简化 printf 格式(如不支持浮点数),否则会出现格式化错误。
IAR 9.x 环境下 STM32 printf 串口重定向的核心是 “替换 fputc 为__write 函数”,关键在于两点:一是正确配置 IAR 的 DLIB 库选项,启用完整格式化支持;二是按 IAR 官方规范实现__write 函数,关联串口发送逻辑。
该方案适用于 STM32 全系列芯片(F1/F4/F7/H7 等),仅需替换串口句柄和初始化代码即可复用。通过本文步骤,可彻底解决 IAR 9.x 的 printf 输出问题,满足开发中的调试信息打印需求。
阅读全文
895