单片机printf和scanf重定向和自定义打印函数的使用归纳

本文介绍了如何在Keil5的Version5和Version6中不使用useMicroLIB,以及使用useMicroLIB的情况下,通过重定义fputc和fgetc函数,以及自定义printf和scanf功能,实现在串口上进行数据打印和接收。同时提供了HAL库和标准库的代码示例。

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

理解:使用printf函数打印数据,会跳转到fputc函数,每个字符都会跳转一次。

一.不需要use MicroLIB的方式:

1.Version5

 Keil 5 version5且不使用use MicroLIB的配置界面

Keil 5 version5的代码:
#include <stdio.h>

//需包含#include "stdio.h"
#if 1
#pragma import(__use_no_semihosting)                
//标准库需要的支持函数                 
struct __FILE 
{ 
	int handle; 
}; 

FILE __stdout;  
FILE __stdin;   
//定义_sys_exit()以避免使用半主机模式    
void _sys_exit(int x) 
{ 
	x = x; 
} 
//重定义fputc函数 
int fputc(int ch, FILE *f)
{ 	
	while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
	USART1->DR = (uint8_t) ch;      
	return ch;
}

///重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
    uint8_t ch = 0;
	while((USART1->SR&0X20)==0);//循环发送,直到发送完毕   
	ch = (uint8_t) USART1->DR;   
    return ch;
}
#endif 

2. Version6

 Keil 5 version6且不使用use MicroLIB的配置界面

Keil 5 version6的代码:
#include <stdio.h>

//需包含#include "stdio.h"
#if 1            
struct FILE 
{ 
	int handle; 
}; 

FILE __stdout;  
FILE __stdin;   
//定义_sys_exit()以避免使用半主机模式    
void _sys_exit(int x) 
{ 
	x = x; 
} 
//重定义fputc函数 
int fputc(int ch, FILE *f)
{ 	
	while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
	USART1->DR = (uint8_t) ch;      
	return ch;
}

///重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
    uint8_t ch = 0;
	while((USART1->SR&0X20)==0);//循环发送,直到发送完毕   
	ch = (uint8_t) USART1->DR;   
    return ch;
}
#endif 

二.使用use MicroLIB的方式:

配置勾上Use MicroLIB

HAL库代码:
#include <stdio.h>

#if 1       
//需包含#include "stdio.h"
int fputc(int ch, FILE *f)
{
    uint8_t temp = ch;
    HAL_UART_Transmit(&huart1, &temp, 1, 0xffff);//huart1需要根据你的配置修改
    return ch;
}

int fgetc(FILE *f)
{
    uint8_t ch = 0;
    HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
    return ch;
}
#endif 

标准库代码: 
#include <stdio.h>

#if 1
/// 重定向c库函数printf到USART1
int fputc(int ch, FILE *f)
{
		/* 发送一个字节数据到USART1 */
		USART_SendData(USART1, (uint8_t) ch);
		
		/* 等待发送完毕 */
		while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);		
	
		return (ch);
}
 
/// 重定向c库函数scanf到USART1
int fgetc(FILE *f)
{
		/* 等待串口1输入数据 */
		while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
 
		return (int)USART_ReceiveData(USART1);
}
#endif

三、自定义函数的printf打印Debug方式:

(这里不需要使用微库)

        实际项目中,当用到多个串口时,使用printf来Dedug打印数据非常不方便,这里可以自己定义每个串口的打印函数。

HAL库代码: 

这里只要修改

HAL_UART_Transmit(&huart1, (uint8_t *)Uart_buf, len, 0xf);

​​​​中的串口句柄(&huart1)即可打印到指定串口

#include "usart.h"
#include "stdarg.h"
#include "stdio.h"

#define CONSOLEBUF_SIZE 64

void PrintfDebug1(const char *fmt, ...)
{
    char Uart_buf[CONSOLEBUF_SIZE];  // 用于存储格式化后的字符串
    uint16_t len = 0;  // 存储格式化后的字符串长度

    va_list args;  // 声明一个可变参数列表
    va_start(args, fmt);  // 初始化可变参数列表,使其指向参数 fmt

    // 使用 vsprintf 函数将格式化的字符串放入 Uart_buf 中,并获取格式化后的字符串长度
    len = vsprintf(Uart_buf, fmt, args);

    va_end(args);  // 结束可变参数的使用

    // 使用 HAL_UART_Transmit 函数将格式化后的字符串通过串口发送出去
    HAL_UART_Transmit(&huart1, (uint8_t *)Uart_buf, len, 0xf);
}
标准库代码: 


USART_SendData(USART1, (uint8_t)ch);

while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);

改变USART1即可打印到指定的串口

#include "stdarg.h"
#include "stm32f10x_conf.h"
#include "stdio.h"

#define CONSOLEBUF_SIZE 64

// 发送单个字符到 USART1,阻塞等待发送完毕
void Send_char(char ch) 
{
    // 使用 USART_SendData 函数发送单个字符到 USART1
    USART_SendData(USART1, (uint8_t)ch);

    // 等待发送完毕
    while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}

// 发送字符串到 USART1
// 参数 str: 要发送的字符串
// 参数 length: 字符串的长度
void Send_string(const char *str, uint16_t length) 
{
    while (length > 0) 
    {
        Send_char(*str);
        str++;
        length--;
    }
}

// 格式化输出到 USART1
// 参数 fmt: 格式字符串
// ...: 可变参数列表
void PrintfDebug1(const char *fmt, ...) 
{
    char Uart_buf[CONSOLEBUF_SIZE];
    uint16_t len = 0;
    
    va_list args;
    va_start(args, fmt);
    
    // 使用 vsnprintf 函数将格式化的字符串放入 Uart_buf 中,并获取格式化后的字符串长度
    len = vsnprintf(Uart_buf, CONSOLEBUF_SIZE, fmt, args);
    
    va_end(args);

    // 将格式化后的字符串发送到 USART1
    Send_string(Uart_buf, len);
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值