KEIL 环境下 printf 导致程序无法执行的解决方案

#【1024程序员节征文】以 1024 之名,写我与代码的「双向奔赴」#

1. 问题描述

在开发过程中,开发者常用 printf 函数进行串口输出调试信息,在 MDK Keil 环境下,客户在调试开发过程中发现,只要程序添加了 printf 函数调用,就会出现程序无法执行。通过调试,查看汇编,发现程序没有进入到 main 函数,而是停在了下面 BKPT 语句。当单步执行的时候,程序可以运行下去,但是全速执行,程序一直停在了 BKPT 语句。察看用户工程设置,没有选用 MicroLIB 选项。
在这里插入图片描述
图1. 调试信息
显然,这是一个与应用代码无关的问题,本文介绍产生该问题的原因并给出解决方案。

2. Semihosting 简介

Semihosting 是一种调试技术,主要用于嵌入式系统开发。它允许嵌入式设备在运行时通过调试器与主机系统(通常是开发者的计算机)进行通信,从而实现一些复杂的输入输出操作。在 STM32 开发中,最常见的 Semihosting 应用是通过 printf 函数输出信息到PC 的串口终端。其常规的调用路径如下图所示。
在这里插入图片描述

图2. Semihosting

当应用程序调用了 printf 函数,则编译器会默认启用的 Semihosting 功能,从而在初始化 C 库的时候,会执行与调试器进行交互操作的动作,应用程序是通过 SVC 或者 BKPT这些指令与调试器进行交互,如果初始化过程中得不到调试器的响应,那么应用程序就不会往下执行。

结合 ARM 的参考文档,既然是 Semihosting 引起的问题,那么 ARM 也提供了解决问题的方法,可以参考文档 ARM: Application Builds Without Error, But Does Not Run。

3. 问题解决

针对 Semihosting 导致的问题,通常情况下,在编译的过程中,不使用 C 标准库,而使用 MicroLIB 库是可以解决的,但是由于客户应用程序限制,不能使用 MicroLIB 库,而为了调试方便,又需要使用 printf 函数,所以只能采取其他的解决办法,在上述 ARM 提供的文档中,给出使用 CMSIS-Compiler pack 解决该问题,经过实验,该方法方便有效,按照如下步骤即可。

• 使能 CMSIS-Compiler 设置
在 Run-Time Environment 窗口中,使能 CMSIS-Compiler CORE 组件并设置STDOUT 为 Custom 选项,如下图所示。
在这里插入图片描述

图3. 使能 CMSIS-Compiler CORE component

• STDOUT 输出到用户定义的接口
这种方式应该是不调用底层的标准输出接口而是调用用户定义的接口,从而避免Semihosting 导致的问题。既然不再调用底层标准库的接口,那么我们需要按照ARM 提供的模板实现用户接口,如下所示。
在这里插入图片描述
图4. 生成 STDOUT 用户接口

• 重新编译代码,下载即可正常运行。

4. 总结

本文根据客户反馈的问题,结合 ARM 的帮助文档,给出了 printf 函数导致的程序不执行的解决方案,以供参考。


本文档参考ST官方的《LAT1472 KEIL环境下printf导致程序无法执行的解决方案_v1.0》文档。
参考下载地址:https://download.csdn.net/download/u014319604/92182037

Keil MDK-ARM (通常称为 Keil) 中使用 `printf` 函数时遇到的问题是比较常见的,这主要是因为标准库函数如 `printf` 需要有正确的重定向才能正常工作。以下是几种可能导致 `printf` 无法正常使用的原因及解决方案: ### 可能原因和解决办法 #### 1. **缺少必要的初始化** - 如果您使用的硬件平台需要特定的串口配置,则必须先对相应的外设进行适当的设置。 **处理步骤**: 检查您的启动文件(`startup_*.s`)或系统文件 (`system_*.c`) 确保所有外围设备得到了恰当的初始化。 #### 2. **未正确地重映射 STDIO 到 UART 或其他通信端口** 默认情况下,`printf` 发送的数据会尝试通过主机PC的标准输入输出流传输,但是嵌入式环境中并没有这样的机制存在。因此我们需要将这些I/O操作指向实际存在的物理接口例如USART/UART等。 **示例代码片段**(针对STM32系列MCU): ```cpp #include "usart.h" // 引入USAT驱动头文件 // 定义fputc用于重定位stdio到指定uart通道 int fputc(int ch, FILE *f){ HAL_UART_Transmit(&huart1,(uint8_t*)&ch,1,0xFFFF); return ch; } ``` 记得包含对应的HAL库或其他底层API,并调整具体的实例名称(huart1可能是另一个变量名取决于项目结构). #### 3. **调试模式下的断点影响了数据发送** 有时当处于单步执行状态时,在某些指令处暂停可能会阻碍消息的传递过程导致看似“卡住”。 **建议做法:** 尝试禁用所有的断点后再测试;另外也可以考虑增加短延时让缓冲区有足够的时间清空。 --- ### 进一步验证是否为printf问题的小技巧: 如果仍然怀疑是由于`printf()`本身引起的故障,可以暂时将其替换为更简单的串行打印函数来进行排除法排查,比如直接调用UART中断服务例程里的字符发送功能逐个发出字符串内容。 以上就是关于keil环境下集成printf可能出现的一些情况以及对应措施。希望这对解决问题有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值