扫码加入

  • 正文
  • 相关推荐
申请入驻 产业图谱

IAR 9.x 环境 STM32 printf 串口重定向实操:__write 函数替代方案

01/28 14:22
895
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

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. 关键注意事项

  1. 头文件依赖:必须包含<LowLevelIOInterface.h>,否则_LLIO_STDOUT_LLIO_ERROR等宏未定义;
  2. 模块名声明:#pragma module_name = "?__write"不可省略,避免 IAR 链接时找不到__write 函数定义;
  3. 串口句柄一致性:stm32_putc中使用的 UARTHandle 需与初始化的串口句柄一致,避免发送失败;
  4. 超时时间设置:HAL_UART_Transmit的超时参数(如 0xFFFF)需合理,避免阻塞过久或发送不完整;
  5. 库模式匹配:若选择 “Small” 库模式,需简化 printf 格式(如不支持浮点数),否则会出现格式化错误。
IAR 9.x 环境下 STM32 printf 串口重定向的核心是 “替换 fputc 为__write 函数”,关键在于两点:一是正确配置 IAR 的 DLIB 库选项,启用完整格式化支持;二是按 IAR 官方规范实现__write 函数,关联串口发送逻辑。
该方案适用于 STM32 全系列芯片(F1/F4/F7/H7 等),仅需替换串口句柄和初始化代码即可复用。通过本文步骤,可彻底解决 IAR 9.x 的 printf 输出问题,满足开发中的调试信息打印需求。

相关推荐

登录即可解锁
  • 海量技术文章
  • 设计资源下载
  • 产业链客户资源
  • 写文章/发需求
立即登录