STM32使用printf重定向到USART(串口)并打印数据到串口助手

文章介绍了如何在STM32微控制器上使用printf函数将输出重定向到USART1,通过串口助手显示调试信息。关键步骤包括重定向printf函数到USART1的代码实现,以及在usart.c、usart.h和main.c文件中的配置。实验结果显示,通过这种方式可以在复位后看到串口助手接收到的hello,world!打印。这种方法提高了STM32程序调试的便利性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.背景知识

我们知道我们在进行编程的时候,遇到问题,经常通过打印信息进行调试,在java中使用的是System.out.println打印到输出窗口。在C语言中使用的是printf打印到输出窗口。而我们用keil进行编程的时候也是使用的C语言所以也可以使用printf,但是我们知道,keil中没有输出窗口。那我们如何使用printf呢?这里我们只需要重定向一下,将printf重定向到USART1(串口1)–这样我们就能通过串口1将信息打印到上位机(串口助手)。这里也有一个小知识点:下载程序也是通过串口1下载到单片机的内存中。

二. 重定向printf到USART1

这里我就直接给出重定向的代码。我们只需要包含这段代码。同时包含<stdio.h>这个头文件就能使用printf,信息将会输出到串口助手上面。

int fputc(int ch, FILE *f)
{      
	while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
    USART1->DR = (u8) ch;      
	return ch;
}

三.使用printf打印hello,world到串口助手

这里我就做一个小实验,打印hello,world到串口助手。注意串口1的引脚是PA9.PA10我们需要配置一下。代码如下:

3.1 usart.c

#include "usart.h"
#include "stm32f10x.h" 

int fputc(int ch, FILE *f)
{      
	while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
    USART1->DR = (u8) ch;      
	return ch;
}

void uart_init(u32 bound){
  //GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;	 
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	//使能USART1,GPIOA时钟  

//USART1_TX   GPIOA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
   
//USART1_RX	  GPIOA.10初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  

  //Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		//子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器
  
   //USART 初始化设置

USART_InitStructure.USART_BaudRate = bound;//串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式

USART_Init(USART1, &USART_InitStructure); //初始化串口1
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
USART_Cmd(USART1, ENABLE);                    //使能串口1 

}

3.2 usart.h

#ifndef __USART_H
#define __USART_H
#include "stdio.h"	
//如果想串口中断接收,请不要注释以下宏定义
void uart_init(u32 bound);
#endif

3.3 main.c

#include "usart.h"
#include "stdio.h"
void main()
{
uart_init(115200); 
printf("hello,world!\n");
printf("hello,world!\n");
printf("hello,world!\n");

}

四. 实验现象

我们这样只需要按一下复位键就能出现了打印信息了(由于我们这里没在while(1)中一直打印,所以程序跑一下就结束了。)为什么不在while(1)中一直打印呢,由于stm32运行速度较快,如果放入while(1)中,打印的数据量太多,串口助手容易死机。你们可以自己实验一下!
在这里插入图片描述

五.结语

整个程序到这里就结束了。学会这个printf重定向到串口1,对于你程序的调试就方便多了。你只需要加上如上代码就行了。你学会了吗?

### STM32 串口重定向实现方法 为了使 `printf` 函数能够在STM32上通过串口输出信息,需对标准输入/输出流进行重定向。具体操作涉及初始化串口并定义一个用于处理字符输出的新函数。 #### 初始化串口配置 在STM32的引脚配置中启用串口功能,并设置波特率、数据位、停止位和校验位等参数[^1]。这一步骤通常可以通过STM32CubeMX工具自动完成,也可以手动编写代码来设定这些参数。对于不同的STM32型号及其对应的开发板,建议参照官方提供的参考手册或开发指南以获得最准确的信息。 #### 定义新的字符输出函数 为了让 `printf` 能够正常工作于串口通信之上,需要覆盖 `_write()` 或者更常见的做法是在项目源文件内添加自定义版本的 `fputc()` 函数: ```c #include "stm32f4xx_hal.h" // 假设使用的是F4系列MCU #include <stdio.h> // 自定义 fputc() 来支持 printf() int fputc(int ch, FILE *f){ HAL_UART_Transmit(&huart2,(uint8_t *)&ch,1,0xFFFF); // 将字符发送至指定UART接口 return ch; } ``` 上述代码片段展示了如何创建一个新的 `fputc()` 函数以便将每个待打印的字符传递给硬件抽象层(HAL)中的 UART 发送命令来进行实际的数据传输[^2]。 #### 处理浮点数输出的支持 如果打算让 `printf` 支持浮点型变量,则还需要额外做一些调整。这是因为默认情况下某些编译器可能不会包含必要的库来解析这类格式化字符串[^3]。解决办法之一就是在链接选项里加入 `-u _printf_float` 参数,从而强制加载所需的子程序;另一种方式则是修改项目的预处理器宏定义,在启动文件(`startup_stm32xxxx.s`)或其他适当位置增加如下声明: ```assembly __attribute__((used)) void __aeabi_dmul(void); __attribute__((used)) void __aeabi_fdiv(void); ... ``` 以上措施能确保当调用带有 `%f` 类型说明符的 `printf` 语句时,系统知道去哪里寻找执行相应运算所需的功能模块。 #### 示例代码展示 这里给出一段完整的例子,它不仅实现了基本的串口重定向还包含了简单的测试逻辑: ```c /* USER CODE BEGIN Includes */ #include "main.h" #include <stdio.h> UART_HandleTypeDef huart2; void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_USART2_UART_Init(void); int main(void) { /* Reset of all peripherals, Initializes the Flash interface and Systick. */ HAL_Init(); /* Configure the system clock */ SystemClock_Config(); /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_USART2_UART_Init(); while (1) { printf("Hello World!\n"); HAL_Delay(1000); } } /** * @brief This function handles UART communication. */ int fputc(int ch, FILE *f) { HAL_UART_Transmit(&huart2, (uint8_t*)&ch, 1, 0xFFFF); return ch; } /* Other necessary initialization functions omitted for brevity */ ``` 这段代码首先完成了系统的初始化过程,接着进入无限循环不断向终端设备发送问候消息。每当遇到 `printf` 的时候就会触发之前定义好的 `fputc()` 方法负责把每一个字符逐个传送到USART外设上去[^5]。
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

VersionGod

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值