定制自己的printf函数——以stm32串口打印为例

printf这个函数相信学习过编程的人应该都用过,这是一个用来向终端打印数据的函数。这个函数不仅在调试软件代码的时候经常有使用,单片机开发时也经常用于串口打印调试。所以,在此就如何让单片机使用printf来调试代码,开始接下来的学习,在此,请允许我以stm32hal库为例。
首先,要求开发环境支持c语言的标准库函数。(这个应该都支持的吧,问题不大)

方案一 串口重定向

也是最常用的方案,这个操作我们重写fputc函数来重新定向printf的输出对象。

int fputc(int ch, FILE *f)
#endif
{
     
    //具体哪个串口可以更改huart1为其它串口
    HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1 , 0x0f);
    return ch;
}

但是问题来了,有些编译器不是用fput实现的,而是用__io_putchar实现的,比如stm32cubeide。所以我们将代码改写一下,以应对不同的开发环境。

#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE
{
     
    //具体哪个串口可以更改huart1为其它串口
    HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1 , 0x0f);
    return ch;
}

方案二 写自己的print函数

这个是本文重点讲述的方法。需要添加头文件stdarg.h、stdio.h和string.h。重定向有很大的局限性,比如,有些编译环境就是很奇怪,怎么写就是打印不出来。再者就是一个printf不足以满足我各种骚气的操作需求。比如,我想操作两个串口,又想打印在屏幕上一个printf就显得有些不够用。所以我们编写一个自己的printf函数来弥补这种不足。
首先需要了解一下可变参数函数,printf,scanf就是典型的可变参数函数。可变参数函数平时用的比较少,实际上用起来也很简单。

  • 函数声明的参数列表的最后一个参数用省略号“…”,作为可变参数。
  • 在函数体内创建va_list变量用于存储参数列表。
  • 调用va_start初始化va_list参数列表。
  • 操作va_list。
  • 调用va_end完成清理工作。

va_start(arg,nr):arg就是可变参数变量,nr是函数参数列表总可变参数的起始位置的参数,也就是可变参数的前一个参数。不是可变参数的个数哦,这里刚开始的时候我理解错了。
现在可以编写自己的print函数了。

void print(UART_HandleTypeDef* huart, const char* buf, ...)
{
     
  const char *p = buf;
  char str[255] = {
     0};
  va_list v;
  va_start(v, buf);
  vsprintf(str, buf, v); //使用可变参数的字符串打印。类似sprintf
  HAL_UART_Transmit(huart, str, strlen(str), 0xff);
  va_end(v);
}

你可能感兴趣的:(嵌入式)