STM32使用printf重定向

2023-05-16

最近用STM32CubeMX创建了一个demo工程,在调试过程中,printf打印功能一直不能正常打印,检查工程中也已将fputc函数进行了实现。奇怪的是用JTAG进行调试时打印恢复了正常。最后发现问题的原因是没有勾选MDK使用微库的配置,使用微库的话 ,不会使用半主机模式。
printf之类的函数,使用了半主机模式,MDK上开启半主机模式-需要SWO线(换言之,需要使用JTAG接线)当目标板脱离仿真器(jlink/ulink)单独运行时,不能使用半主机模式。

半主机是ARM的一种目标机制,它使得在ARM目标上跑的代码,如果主机电脑运行了调试器,那么该代码可以使用该主机电脑的输入输出设备。
这点非常重要,因为开发初期,可能开发者根本不知道该 ARM 器件上有什么输入输出设备,而半主基机制使得你不用知道ARM器件的外设,利用主机电脑的外设就可以实现输入输出调试。
一般单片机需要独立运行,使用时应去掉仿真器,把printf函数通过单片机的外设来实现,例如通过开发板的串口,lcd或者sd卡。首先要关掉半主机机制。然后再将输入输出重定向到ARM器件上,如 printf 和 scanf,需要重写fputc和fgetc函数。

printf 定义在 <stdio.h> 头文件中,如下:

int printf(const char *format, ...);

printf 函数根据 format 字符串给出的格式打印输出到 stdout(标准输出)中,当然,printf 函数是不会一个字符一个字符去输出,它会调用更底层的 I/O 函数:fputc去逐个字符打印
fputc 也定义于头文件 <stdio.h>中,如下:

int fputc(int ch, FILE *stream);

fputc 函数写入字符 ch 到给定输出流 stream,printf函数在调用该函数时,会向stream参数传入stdout从而打印数据到标准输出。
那么,要实现printf打印到串口就变得非常简单了,只需要重新定义fputc函数,在fputc的函数中将数据通过串口发送,称之为:fputc重定向或者printf重定向。

在MDK中使用重定向的方式:

使用微库

  • 勾选Use MicroLib
  • 重定义fputc到串口
#include <stdio.h>

int fputc(int ch, FILE *stream)
{
    while((USART1->ISR & UART_FLAG_TC) == 0);
    USART1->TDR = (uint8_t) ch;
    return ch;
}

使用标准库

系统 IO 一般指的是 Linux/Unix 系统调用中关于 I/O 操作的统称,其中包括 open、read、write、close 等操作。
与系统 IO 对应还有标准 IO,标准 IO 是 ISO 标准中 C 语言标准定义的 IO 访问接口,例如 fprintf/fgets 等 C 语言标准中定义的文件访问接口。

在 Linux 系统中 open/read/write 等函数的底层实现是通过系统调用访问的,在 STM32 的裸机中没有操作系统,更没有这些系统调用。
但是我们可以用一种其他的方式去实现这些系统 IO,而不需要操作系统。

/* 为确保没有从 C 库链接使用半主机的函数,如果仍然链接了使用半主机的函数,则链接器会报告错误 */   
#pragma import(__use_no_semihosting) 

/* 标准库需要的支持函数 */        
struct __FILE {
    int handle; 
}; 

/* FILE 在stdio.h文件 */    
FILE __stdout;    

/* 定义_sys_exit()以避免使用半主机模式 */                       
void _sys_exit(int x)                    
{  
    x = x;
}         

int fputc(int ch, FILE *stream)
{
    while((USART1->ISR & UART_FLAG_TC) == 0);
    USART1->TDR = (uint8_t) ch;
    return ch;
}

microlib 进行了高度优化以使代码变得很小。 它的功能比缺省 C 库少,并且根本不具备某些 ISO C 特性。某些库函数的运行速度也比较慢,例如,memcpy()。

不同的编译器对于C库的底层实现机制是不同的,所以上面两种在MDK中的实现方法,在使用Gcc编译器的时候是不可行的。

GCC使用标准库重定向

#include <stdio.h>

int _write(int fd, char *ptr, int len)  
{  
    HAL_UART_Transmit(&huart1, (uint8_t*)ptr, len, 0xFFFF);
    return len;
}

与microlib 方式不同的是,GCC重定向是一次写入多个字符,而fputc 中是一次写入一个字符。

在需要使用printf 频繁写入大量字符时,建议使用半主机模式或者使用GCC 编译器。GCC使用打印时要注意行缓冲。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32使用printf重定向 的相关文章

  • 添加自定义转换类型以进行字符串格式化

    python 中是否有向字符串格式添加额外的转换类型 使用的标准转换类型 基于字符串的格式化是这样的s对于字符串 d我想要做的是添加一个新字符 我可以为其指定一个自定义处理程序 例如 lambda 函数 该处理程序将返回要插入的字符串 例如
  • “fork()”后 printf 异常

    操作系统 Linux 语言 纯C 我正在继续学习一般的 C 编程 以及特殊情况下 UNIX 下的 C 编程 我发现了一个奇怪的 对我来说 行为printf 使用后的功能fork call Code include
  • 如何在 C 中使用递归打印星金字塔图案而不使用任何循环?

    我这样做 但它使用的是for环形 我不想要它 include
  • 对 getchar 和 scanf 感到困惑

    我真的很困惑的用法getchar and scanf 这两者有什么区别 我知道scanf 和家人 从用户 或文件 处获取一个字符一个字符并将其保存到一个变量中 但它是立即执行还是在按下某些内容后执行此操作 Enter 我不太理解这段代码 我
  • PHP Printf 作为浮点精度

    我正在尝试使用 PHPprintf功能打印出用户的存储容量 完整的公式看起来像这样 echo printf 02f size 1024 1024 GB 鉴于 size 10 1024 1024 这应该打印出来 10 00 GB 但事实并非如
  • C 复数和 printf

    如何打印 使用 printf 复数 例如 如果我有以下代码 include
  • 是否有一个“空”printf 代码不打印任何内容,用于跳过参数?

    如果我想要一个程序有多种文本输出格式 我可以这样做 const char fmtDefault u x s 2f each n const char fmtMultiLine Qty 3u nItem s nPrice per item 2
  • bash 脚本中变量的转义

    我的 bash 脚本使用 printf 编写了另一个 bash 脚本 printf bin bash HOME server file gromacs file name basename file date date m d Y for
  • 打印 C 字符串(UTF-8)时的 NSLog() 与 printf()

    我注意到 如果我尝试使用格式说明符 s 打印包含 UTF 8 字符串表示形式的字节数组 printf 说得对 但是NSLog 得到它乱码 即 每个字节按原样打印 因此例如 被打印为2个字符 这很奇怪 因为我一直认为NSLog 只是print
  • 如何使用 printf 打印字符串而不打印尾随换行符

    I m trying to print some strings using printf but they are null terminated having trailing newline and that messes with
  • HAL_Delay() 陷入无限循环

    我被 HAL Delay 函数困住了 当我调用此函数 HAL Delay 时 控制陷入无限循环 在寻找问题的过程中 我发现了这个 http www openstm32 org forumthread2145 threadId2146 htt
  • 如何使用“%f”将双精度值填充到具有正确精度的字符串中

    我正在尝试使用 a 来填充带有双精度值的字符串sprintf像这样 sprintf S f val 但精度被截断至小数点后六位 我需要大约 10 位小数来保证精度 如何才能做到这一点 宽度 精度 宽度应包括小数点 8 2表示8个字符宽 点前
  • printf 命令导致段错误? [复制]

    这个问题在这里已经有答案了 当我尝试初始化一个大型的二维字符数组时 它工作得很好 但是当我添加一个简单的打印命令时 它给了我一个分段错误 关于为什么会发生这种情况有什么想法吗 include
  • 使用 STM32F0 ADC 单独读取不同的输入

    STM32F072CBU 微控制器 我有多个 ADC 输入 并且希望单独读取它们 STMcubeMX 生成样板代码 假设我希望按顺序读取所有输入 但我无法弄清楚如何纠正这个问题 这篇博文 http blog koepi info 2015
  • 如何使用 sprintf 附加字符串?

    我面临着一个严重的问题sprintf 假设我的代码片段是 sprintf Buffer Hello World sprintf Buffer Good Morning sprintf Buffer Good Afternoon 几百次冲刺
  • 从 Visual Studio 的哪个版本开始 vsnprintf 基本上符合标准?

    根据微软的文档vsnprintf https msdn microsoft com en us library 1kt27hek aspx 至少从 2003 版 Visual Studio 开始 该函数就是 C 运行时库的一部分 int v
  • 当端点和 PMA 地址均更改时,CubeMX 生成的 USB HID 设备发送错误数据

    我正在调试我正在创建的复合设备的问题 并在新生成的仅 CubeMX 代码中重新创建了该问题 以使其更容易解决 我添加了少量代码main 让我发送 USB HID 鼠标点击 并在按下蓝色按钮时使 LED 闪烁 uint8 t click re
  • 格式化整数时 printf 中的精度字段

    当我执行这两行时 printf 5d n 3 use of precision filed printf 05d n 3 use of 0 flag to prepend with 0 我得到以下输出 00003 00003 结果相同 所以
  • printf 似乎忽略了字符串精度

    所以 我有点难受 根据man 3 printf在我的系统上 字符串格式 5s 应使用指定的精度来限制从给定字符串参数打印的字符数 man 3 printf PRINTF 3 BSD Library Functions Manual PRIN
  • fprintf 调试断言失败

    我有一个程序 如果我手动启动它 它可以正确运行 但是 如果我尝试添加注册表项以在启动过程中自动启动它 则会收到以下错误 Debug assertion failed str null fprintf c line 55 我尝试在发生任何事情

随机推荐

  • 网络编程04-UDP的广播、组播

    目录 一 UDP广播通信 1 什么是广播 2 特点 3 广播地址 4 实现广播的过程 xff08 一定是使用UDP协议 xff09 广播发送端 广播接收方 练习1 xff1a 把广播通信进行实现 发送端 接收端 二 UDP组播 xff08
  • JSON文件的生成与解析

    参考Json文件的生成和解析
  • C++ 设置double精度

    设置double精度 在这里插入代码片 span class token macro property span class token directive keyword include span span class token str
  • GitLab 出现错误Could not resolve host: xxx-xxx

    错误原因 xff1a 域名解析错误 解决办法找 打开hosts 在最后一行添加 192 30 xxx xxx gitlab
  • TeeChart控件_动态创建

    在安装目录下找到TeeChartxxxxx ocx 以管理员的身份打开cmd 注册TeeChartxxxxx ocx regsvr32 TeeChartxxxxx ocx VS2015 使用TeeChart绘图控件 CRect rect s
  • Nginx

    Nginx 介绍 Nginx是一款轻量级的Web 服务器 反向代理服务器 电子邮件 xff08 IMAP POP3 xff09 代理服务器 xff0c 并在一个BSD like 协议下发行 由俄罗斯的程序设计师Igor Sysoev所开发
  • 上电自动开机

    上电开机启动是指电脑主机在UPS恢复供电时可以自动开机 该功能必须要求电脑主板型号支持 xff0c 进入电脑的BIOS进行设置使用 不同型号的电脑的BIOS设置会有区别 xff0c 以下仅做参考 xff1a 第一步 xff1a 开机进入BI
  • Linux网络编程【TCP】

    文章目录 TCP特点TCP中CS架构TCP状态转换相关操作函数recv函数send函数 TCP特点 TCP是一种面向广域网的通信协议 xff0c 目的是在跨越多个网络通信时 xff0c 为两个通信端点之间提供一条具有下列特点的通信方式 xf
  • 博客资源整理

    文章目录 STLLinux基础命令linux系统编程Linux网络编程Docker容器技术数据库第三方库的使用Linux编程WebQt STL 基础概念 容器 duque stack map set vector 算法 查找算法 排序算法
  • Qt编译Mysql驱动

    1找到源码 2点击编译会看到报错 1解决方案 下载相关文件 也可以私信发给你 2打开配置文件添加下面的信息 相关文件下载 3点击编译 弹出的框直接关掉就行 4在安装qt的根目录下会生成如下目录 5 找到下面的库 6 将上面的库拷贝到如下目录
  • C++中的异常语法

    文章目录 概述异常的关键子自定义异常使用栈解旋异常的接口声明异常变量的生命周期C 43 43 标准异常库 概述 C语言的异常缺陷在于返回值只有一个 xff0c 可能出现二义性 xff0c 没有统一的标准 C 43 43 中的异常必须有处理
  • 处理鼠标连续点击的问题

    处理鼠标连续点击的问题 span class token comment 上次点击时间点 span DWORD m tmClick span class token punctuation span span class token com
  • FLOPS和FLOPs、GFLOPs区别与计算

    参考FLOPS和FLOPs GFLOPs区别与计算
  • VS远程调试

    文章目录 VS远程调试本地和虚拟机调试准备工作 xff1a 需要注意的几个地方 xff1a VS远程调试 在编程中由于环境 版本等各种原因 xff0c 我们需要模拟出来各种环境来跑不同的版本测试 本地和虚拟机调试 准备工作 xff1a 以V
  • AUTOSAR基础篇之CanNM

    前言 首先 xff0c 问大家几个问题 xff0c 你清楚 xff1a 为什么要引入网络管理呢 xff1f 上电同时启动 xff0c 下电同时关闭 xff0c 它不香吗 xff1f 你知道车上的ECU节点可以分为哪几种类型吗 xff1f 汽
  • CANoe应用案例之DoIP通信

    随着ECU功能和存储容量的不断提高 xff0c 主机厂对于ECU诊断和刷写有了更高的要求 由于带宽的限制 xff0c 传统的汽车总线 xff08 如CAN总线 xff09 存在刷写时间过长的缺点 xff0c 大大降低了生产和维修效率 DoI
  • TRACE32——常用操作

    TRACE 32常用操作 TRACE32软件打开后 xff0c 连上硬件环境 xff0c 我们就可以开始尝试和芯片建立连接 xff0c 并进行基本的调试操作 第一步 xff1a 确认目标板是否上电 第二步 xff1a 打开System Se
  • TRACE32——基于SNOOPer的变量记录

    TRACE32 基于SNOOPer的变量记录 在我们日常调试工作中 xff0c 经常会遇到一种场景 xff1a 对于某些变量或者内存的值 xff0c 希望对其进行监控 当这些变量发生写或者读的时候 xff0c 将这些操作记录下来 xff0c
  • TRACE32——内存填充测试Data.Pattern

    TRACE32 内存填充测试Data Pattern Data Pattern 命令可以用于对内存 xff08 SRAM DDR Flash等 xff09 写入随机值 xff0c 以快速地测试内存是否可以正确读写 命令格式 xff1a 示
  • STM32使用printf重定向

    最近用STM32CubeMX创建了一个demo工程 xff0c 在调试过程中 xff0c printf打印功能一直不能正常打印 xff0c 检查工程中也已将fputc函数进行了实现 奇怪的是用JTAG进行调试时打印恢复了正常 最后发现问题的