嵌入式系统重定向printf的三种方法

对printf()进行重定向的三种方法

  • 方法1: 使用MircoLib并重定义fputc
  • 方法2: 停用半主机模式,在MDK中使用标准库重定向printf()
  • 方法3: 在Gcc中使用标准库重定向printf

1. MDK使用MircoLib并重定义fputc

printf()函数实际上是调用了fputc()根据 format 字符串给出的格式打印输出到 stdout(标准输出)中。这两个函数都定义在<stdio.h>中:

int printf(const char *format, ...);
...
int fputc(int ch, FILE *stream);

fputc() 函数写入字符 ch 到给定输出流 streamprintf()函数在调用fputc() 时,会向stream参数传入stdout从而打印数据到标准输出。
KEIL-MDK中有一个Use MicroLIB选项,MicroLib是缺省c库的备选库,它可装入少量内存中,与嵌入式应用程序配合使用,且这些应用程序不在操作系统中运行。MicroLib提供了一个有限的stdio子系统,它仅支持未缓冲的stdinstdoutstderr
所以我们只需要勾选Use MicroLIB选项,并重写fputc() 函数就可以将printf()从串口输出了。
在这里插入图片描述

int fputc(int ch, FILE* f)
{
    USART_SendData(USART1, (unsigned char) ch);
    while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET)
    {
    }
    return ch;
}

2. 在MDK中使用标准库重定向printf()

如果不想使用MicroLib,也可以使用标准库,但是需要停用半主机模式。下面我们提供另外一种重定义fputc()的写法。

STM32或者其他类似的ARM芯片(比如航顺HK32系列)的USART串口外设有一个 ISR 寄存器,全名 Interrupt and status register, 用来指示当前串口的状态,可以通过判断该位来判断串口当前是否处于发送状态,代码如下:

while((USART1->ISR & 0X40) == 0);

为了提高发送效率,直接使用寄存器来操作串口发送字符ch:

USART1->TDR = (uint8_t) ch;
#include <stdio.h>

/* 告知连接器不从C库链接使用半主机的函数 */
#pragma import(__use_no_semihosting)

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

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

FILE __stdout;
/*重定义fputc的另外一种方式*/
int fputc(int ch, FILE *stream)
{
    /* 堵塞判断串口是否发送完成 */
    while((USART1->ISR & 0X40) == 0);

    /* 串口发送完成,将该字符发送 */
    USART1->TDR = (uint8_t) ch;

    return ch;
}

3. 在Gcc中使用标准库重定向printf()

如果你不使用MDK而是Gcc来编译项目,那么上述两种方法不可行。
那么需要怎么做呢:

  • 在使用Gcc编译器的时候,需要重新定义_write函数而不是fputs()函数。
  • 只能使用标准库,因为Gcc中没有MicroLib。
    代码如下:
#include <stdio.h>

int _write(int fd, char *ptr, int len)  
{  
  HAL_UART_Transmit(&huart1, (uint8_t*)ptr, len, 0xFFFF);
  return len;
}
  • 14
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
printf函数的输出重定向到串口输出是在嵌入式系统中非常常见的需求。主要是为了方便程序调试,将程序在内存中运行的各种状态信息实时输出到串口,以便进行调试和故障排查。下面介绍一下重定向printf函数到串口输出的多种实现方法。 1. 直接修改标准输出流: 在C语言中,printf函数输出结果的默认流是标准输出流stdout。可以通过修改stdout的指向来实现将printf输出重定向到串口模块中。用下面的宏定义可以实现该操作。 #define printf(format, ...) \ usart_printf(format, ## __VA_ARGS__) 其中,usart_printf就是输出到串口的函数,可以根据具体需要实现。在这也可以通过定义输出重定向函数putc的方法实现。 int fputc(int ch, FILE *f) { usart_putc(ch); return ch; } 在此方法中,usart_putc才是发送数据的函数。 2. 软串口: 软串口的实现方式是将1408体积小的芯片实现成一个UART硬件模块,软件控制TX与RX引脚。实现步骤如下: 1. 选择一个GPIO作为串口的TX引脚,例如PA2 2. 配置TX引脚为GPIO输出模式,速率为波特率(例如9600) 3. 在串口发送函数中,按照帧格式(起始位、数据位、校验位、停止位)发送数据。 3. 硬件串口: 硬件串口的实现方式是将STM32的串口模块外置,使用硬件实现串口数据的接收和发送。该实现方法在通讯速度、稳定性和灵活性等方面更加优越,但是需要使用外置的串口硬件模块,而且还需要进行配置硬件的复杂操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值