STM32调试手段:重定向printf串口

引言

       C语言中经常使用printf来输出调试信息,打印到屏幕。由于在单片机中没有屏幕,但是我们可以重定向printf,把数据打印到串口,从而在电脑端接收调试信息。这是除了debug外,另外一个非常有效的调试手段。


一、什么是printf的重定向

       我们知道 C 语言中printf 函数默认输出设备是控制台,如果要实现在串口或者 LCD 上显示,必须重定义C语言标准库函数里调用的与输出设备相关的函数。比如使用 printf 输出到串口,则需要将 fputc 里面的输出指向串口,这一过程就叫 重定向

二、怎么样重定向printf

       那么如何让 STM32 使用 printf 函数呢?很简单,只需要将 fputc 里面的输出指向 STM32 串口即可,fputc 函数有固定的格式,我们只需要在函数内操作STM32串口即可。

2.1 配置microLIB

       由于我们使用的开发环境或者说工具链是keil-MDK,而在KEIL-MDK开发环境中,本身是没有包含C语言的一些标准库函数,如stdio.h这种文件等。       

       在KEIL-MDK开发环境中,可以选择使用MicroLIB库。MicroLIB是一个高度优化的C库,适用于嵌入式应用程序。它的特点是代码体积小,但功能较少,不支持某些ISO C特性。当然这里重定向printf要使用的库函数已经够用了。

我们可以直接在【魔法棒】中勾选上即可完成它的配置


2.2 重定向fputc函数(寄存器实现)

在使用MicroLIB库时,需要重定向fputc函数。fputc函数的原型如下:

int fputc(int ch, FILE* stream)
{
USART_SendChar(USART1, (uint8_t)ch);
return ch;
}

        那么,本次我们将printf重定向到串口上,这里使用的串口是USART1,只需要编写一个发送单字符数据的函数,接着重新定义一下fputc函数即可实现printf的重定向。

       也就是说,只需要在usart.c中编写三个函数,分别是串口的初始化发送一个字符函数fputc函数重写即可。

参考代码如下:

1、usart.h

#ifndef __USART_H
#define __USART_H#include "stm32f10x.h"
#include <stdio.h>// 初始化
void USART_Init(void);// 发送一个字符
void USART_SendChar(uint8_t ch);#endif

2、usart.c

#include "usart.h"// 初始化
void USART_Init(void)
{// 1. 开启GPIO时钟 PA9 PA10RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;RCC->APB2ENR |= RCC_APB2ENR_USART1EN;// 2. 设置GPIO工作模式// PA9 TX 输出,复用推挽输出 MODE-11 CNF-10// PA10 RX 输入,浮空输入 MODE-00 CNF-01GPIOA->CRH |= GPIO_CRH_MODE9;GPIOA->CRH |= GPIO_CRH_CNF9_1;GPIOA->CRH &= ~GPIO_CRH_CNF9_0;GPIOA->CRH &= ~GPIO_CRH_MODE10;GPIOA->CRH &= ~GPIO_CRH_CNF10_1;GPIOA->CRH |= GPIO_CRH_CNF10_0;// 3. 串口配置// 3.1 设置波特率USART1->BRR = 0x271;         // 115.2 Kpbs// 3.2 开启模块及收发使能USART1->CR1 |= USART_CR1_UE;USART1->CR1 |= USART_CR1_TE;USART1->CR1 |= USART_CR1_RE;// 3.3 其他配置(字长、奇偶校验、停止位)USART1->CR1 &= ~USART_CR1_M;USART1->CR1 &= ~USART_CR1_PCE;USART1->CR2 &= ~USART_CR2_STOP;
}// 发送一个字符
void USART_SendChar(uint8_t ch)
{// 当发送的数据不为空时等待,TXE为1则可以继续写入数据while ((USART1->SR & USART_SR_TXE) == 0){}// 发送一个字符USART1->DR = ch;    
}// 重定向fputc函数
int fputc(int ch, FILE * file)
{USART_SendChar((uint8_t)ch);return (int)ch;
}

3、main.c

#include "usart.h"int main(void)
{// 初始化USART_Init();// printfint a = 100;printf("a = %d", a);// 死循环保持状态while(1){		}
}

2.3 重定向fputc函数(HAL库实现)

        然后我们在借助HAL库实现。其实也非常简单,其在STM32CubeMX中需要进行的配置和前面串口通讯轮询案例的HAL库实现需要的配置是一样的,因为本次作重定向主要就只是多重写一个fputc函数,即STM32CubeMX软件自动生成代码后我们再在usart.c中添加重写fputc函数的代码即可,非常简单。

所以这里附上前面HAL库实现的串口通讯轮询案例的文章链接,可以直接去看看相关配置步骤USART_串口通讯轮询案例(HAL库实现)-CSDN博客https://blog.csdn.net/2301_79475128/article/details/145263748?spm=1001.2014.3001.5502

然后,根据上面所述原理,这里直接给上添加到usart.c中的参考代码如下:

/* USER CODE BEGIN 1 */int fputc(int ch, FILE * file)
{HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1000);return ch;
}/* USER CODE END 1 */

      值得注意的是,keil中要勾选上【microLIB】代码中引入#include<stdio.h> ,否则会提示找不到printf函数。


三、测试

编译以后,我们烧录然后在串口助手中看效果

1、寄存器实现的测试

2、HAL库实现的测试

       显然,通过重定向printf,我们可以通过printf将一些信息打印发送到电脑上,使用串口助手进行查看。


以上便是本次文章的所有内容,欢迎各位朋友在评论区讨论,本人也是一名初学小白,愿大家共同努力,一起进步吧!

鉴于笔者能力有限,难免出现一些纰漏和不足,望大家在评论区批评指正,谢谢!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/10838.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何使用 ChatBox AI 简化本地模型对话操作

部署模型请看上一篇帖子&#xff1a;本地部署DeepSeek教程&#xff08;Mac版本&#xff09;-CSDN博客 使用 ChatBox AI 简化本地模型对话操作&#xff1a; 打开 ChatBox AI 官网&#xff1a;Chatbox AI官网&#xff1a;办公学习的AI好助手&#xff0c;全平台AI客户端&#xf…

C++ Primer 自定义数据结构

欢迎阅读我的 【CPrimer】专栏 专栏简介&#xff1a;本专栏主要面向C初学者&#xff0c;解释C的一些基本概念和基础语言特性&#xff0c;涉及C标准库的用法&#xff0c;面向对象特性&#xff0c;泛型特性高级用法。通过使用标准库中定义的抽象设施&#xff0c;使你更加适应高级…

鸿蒙物流项目之基础结构

目录&#xff1a; 1、项目结构2、三种包的区别和使用场景3、静态资源的导入4、颜色样式设置5、修改项目名称和图标6、静态包基础目录7、组件的抽离8、在功能模块包里面引用静态资源包的组件 1、项目结构 2、三种包的区别和使用场景 3、静态资源的导入 放在har包中&#xff0c;那…

RK3568使用QT搭建TCP服务器和客户端

文章目录 一、让RK3568开发板先连接上wifi二、客户端代码1. `widget.h` 文件2. `widget.cpp` 文件**详细讲解**1. **`Widget` 类构造函数 (`Widget::Widget`)**2. **UI 布局 (`setupUI`)**3. **连接按钮的槽函数 (`onConnectClicked`)**4. **发送消息按钮的槽函数 (`onSendMess…

Redis|前言

文章目录 什么是 Redis&#xff1f;Redis 主流功能与应用 什么是 Redis&#xff1f; Redis&#xff0c;Remote Dictionary Server&#xff08;远程字典服务器&#xff09;。Redis 是完全开源的&#xff0c;使用 ANSIC 语言编写&#xff0c;遵守 BSD 协议&#xff0c;是一个高性…

【算法设计与分析】实验8:分支限界—TSP问题

目录 一、实验目的 二、实验环境 三、实验内容 四、核心代码 五、记录与处理 六、思考与总结 七、完整报告和成果文件提取链接 一、实验目的 掌握分支界限求解问题的思想&#xff1b;针对不同的问题&#xff0c;能够利用分支界限法进行问题拆分和求解以及时间复杂度分析…

2025年大年初一篇,C#调用GPU并行计算推荐

C#调用GPU库的主要目的是利用GPU的并行计算能力&#xff0c;加速计算密集型任务&#xff0c;提高程序性能&#xff0c;支持大规模数据处理&#xff0c;优化资源利用&#xff0c;满足特定应用场景的需求&#xff0c;并提升用户体验。在需要处理大量并行数据或进行复杂计算的场景…

2025:影刀RPA使用新实践--CSDN博客下载

文章目录 一键CSDN博客下载器程序说明指导说明使用步骤 获取方法 一键CSDN博客下载器 程序说明 配置信息&#xff1a;CSDN账号&#xff08;手机号/邮箱/用户名&#xff09;、密码、博客文件类型支持markdown格式、html格式&#xff08;默认值markdown格式&#xff09;、博客保…

游戏引擎 Unity - Unity 启动(下载 Unity Editor、生成 Unity Personal Edition 许可证)

Unity Unity 首次发布于 2005 年&#xff0c;属于 Unity Technologies Unity 使用的开发技术有&#xff1a;C# Unity 的适用平台&#xff1a;PC、主机、移动设备、VR / AR、Web 等 Unity 的适用领域&#xff1a;开发中等画质中小型项目 Unity 适合初学者或需要快速上手的开…

【Postman接口测试】Postman的安装和使用

在软件测试领域&#xff0c;接口测试是保障软件质量的关键环节之一&#xff0c;而Postman作为一款功能强大且广受欢迎的接口测试工具&#xff0c;能够帮助测试人员高效地进行接口测试工作。本文将详细介绍Postman的安装和使用方法&#xff0c;让你快速上手这款工具。 一、Pos…

边缘检测算法(candy)

人工智能例子汇总&#xff1a;AI常见的算法和例子-CSDN博客 Canny 边缘检测的步骤 1. 灰度转换 如果输入的是彩色图像&#xff0c;则需要先转换为 灰度图像&#xff0c;因为边缘检测通常在单通道图像上进行。 2. 高斯滤波&#xff08;Gaussian Blur&#xff09; 由于边缘…

WinDBG查找C++句柄泄露

C代码&#xff08;频繁点击About按钮导致Mutex句柄泄露&#xff09; HANDLE _mutexHandle;LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {switch (message){case WM_COMMAND:{int wmId LOWORD(wParam);// 分析菜单选择:switch (wmId){c…

基于微信小程序的酒店管理系统设计与实现(源码+数据库+文档)

酒店管理小程序目录 目录 基于微信小程序的酒店管理系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、管理员模块的实现 (1) 用户信息管理 (2) 酒店管理员管理 (3) 房间信息管理 2、小程序序会员模块的实现 &#xff08;1&#xff09;系统首页 &#xff…

大白话讲清楚embedding原理

Embedding&#xff08;嵌入&#xff09;是一种将高维数据&#xff08;如单词、句子、图像等&#xff09;映射到低维连续向量的技术&#xff0c;其核心目的是通过向量表示捕捉数据之间的语义或特征关系。以下从原理、方法和应用三个方面详细解释Embedding的工作原理。 一、Embe…

mysql中in和exists的区别?

大家好&#xff0c;我是锋哥。今天分享关于【mysql中in和exists的区别&#xff1f;】面试题。希望对大家有帮助&#xff1b; mysql中in和exists的区别&#xff1f; 在 MySQL 中&#xff0c;IN 和 EXISTS 都是用于子查询的操作符&#xff0c;但它们在执行原理和适用场景上有所不…

MySQL高可用

一、mysql路由 1.利用路由器的连接路由特性&#xff0c;用户可以编写应用程序来连接到路由器&#xff0c;并令路由器使用响应的路由策略来处理连接来使其连接到正确的mysql数据库服务器 2.mysql route的部署方式 需要在所有数据库主机之外再打开一台主机mysql-router 配置mysql…

DDD - 微服务架构模型_领域驱动设计(DDD)分层架构 vs 整洁架构(洋葱架构) vs 六边形架构(端口-适配器架构)

文章目录 引言1. 概述2. 领域驱动设计&#xff08;DDD&#xff09;分层架构模型2.1 DDD的核心概念2.2 DDD架构分层解析 3. 整洁架构&#xff1a;洋葱架构与依赖倒置3.1 整洁架构的核心思想3.2 整洁架构的层次结构 4. 六边形架构&#xff1a;解耦核心业务与外部系统4.1 六边形架…

LS和MMSE信道估计

1️⃣ LS&#xff08;最小二乘&#xff09;信道估计 OFDM系统的信道估计常在频域进行&#xff0c;因为OFDM本身就是基于频域的。频域模型可以表示为&#xff1a; Y ( f ) X ( f ) H ( f ) Z ( f ) Y(f)X(f) H(f)Z(f) Y(f)X(f)H(f)Z(f) 其中&#xff0c; Y ( f ) Y(f) Y(f)表…

C++ strcpy和strcat讲解

目录 一. strcpy 代码演示&#xff1a; 二.strcat 代码演示&#xff1a; 一. strcpy 使⽤字符数组可以存放字符串&#xff0c;但是字符数组能否直接赋值呢&#xff1f; ⽐如&#xff1a; char arr1[] "abcdef"; char arr2[20] {0}; arr2 arr1;//这样这节赋值可…

力扣257. 二叉树的所有路径(遍历思想解决)

Problem: 257. 二叉树的所有路径 文章目录 题目描述思路复杂度Code 题目描述 思路 遍历思想(利用二叉树的先序遍历) 利用先序遍历的思想&#xff0c;我门用一个List变量path记录当前先序遍历的节点&#xff0c;当遍历到根节点时&#xff0c;将其添加到另一个List变量res中&…