加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長(zhǎng)期合作伙伴
立即加入
  • 正文
    • 單片機(jī)printf打印輸出
    • 單片機(jī)常見自定義printf函數(shù)
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

分享一下單片機(jī)自定義printf函數(shù)

11/18 09:05
1104
閱讀需 8 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

微信公眾號(hào) | strongerHuang

單片機(jī)學(xué)習(xí)、做項(xiàng)目,經(jīng)常都會(huì)用到類似 printf 這種打印輸出的時(shí)候。

比如:

    • 打印輸出日志信息
    打印輸出調(diào)試信息查看實(shí)時(shí)數(shù)據(jù)等

而 printf 用的最多的就是UART(重定向串口)?。其實(shí)除了串口,也可以使用其他底層接口,I2C、SPI、CAN等這些常見通信接口也是可以的。

因?yàn)镃AN的通信速率相對(duì)更高,我之前很多項(xiàng)目不管是傳輸數(shù)據(jù),還是輸出信息都用到 CAN 總線進(jìn)行通信。所以,其實(shí)底層也可以“重定向CAN”

單片機(jī)printf打印輸出

單片機(jī) printf 打印輸出,最常見的一種是使用UART串口重定向,然后使用微庫,比如Keil環(huán)境下,只需要在配置界面勾選“微庫”即可:

然后串口進(jìn)行重定向:

#include <stdio.h>
int fputc(int ch, FILE *f){??USART_SendChar((uint8_t)ch);
  return ch;}

經(jīng)過上述簡(jiǎn)單配置,即可在應(yīng)用中直接調(diào)用 printf 函數(shù):

printf("公眾號(hào):strongerHuang");

以上只是最常見的一種方法,其實(shí)單片機(jī)打印輸出還有很多方法:1.UART打印輸出;2.仿真打印輸出;3.SWO打印輸出;4.JLink-RTT打印輸出

單片機(jī)常見自定義printf函數(shù)

上面分享的 printf 打印輸出都是標(biāo)準(zhǔn)的方法,有些時(shí)候,這些標(biāo)準(zhǔn)的方法可能不適合當(dāng)前的項(xiàng)目。

比如內(nèi)存、Flash不足了,輸出的方式不能滿足等,這個(gè)時(shí)候,就需要自定義printf函數(shù)了。這里簡(jiǎn)單分享一下常見的方式。

1、底層通信函數(shù)

UART串口是最常見的一種printf通信接口,其實(shí),CAN這種通信速率更高的接口也是可以的。

USART_SendChar((uint8_t)ch);
//或者CAN總線作為通信接口
CAN_SendChar((uint8_t)ch);

2、重定向底層通信接口

這里是通過UART串口重定向?qū)崿F(xiàn)一些基礎(chǔ)的字符和字符串輸出函數(shù)。

void putchar(char c){  USART_SendChar(c);} void puts(const char *str){  while(*str)  {    putchar(*str++);  }  putchar('n');}

3、實(shí)現(xiàn)整數(shù)輸出

我們這里使用簡(jiǎn)單的 itoa(整數(shù)轉(zhuǎn)字符串)算法。

void print_int(int num){  char buffer[12];  char *ptr = buffer + 11;  char is_negative = 0;
  if (num < 0) {      is_negative = 1;      num = -num;  }
  *ptr = '';  do {      *--ptr = (num % 10) + '0';      num /= 10;  } while (num);
  if (is_negative) {      *--ptr = '-';  }
  puts(ptr);}

4、實(shí)現(xiàn)小數(shù)點(diǎn)輸出

如果需要支持小數(shù)輸出,可以添加一個(gè)簡(jiǎn)單的浮點(diǎn)數(shù)打印函數(shù)。但是,考慮到有些單片機(jī)不支持浮點(diǎn)運(yùn)算硬件,這里使用定點(diǎn)數(shù)(整數(shù)和小數(shù)部分分離)來實(shí)現(xiàn)。

void print_fixed_point(int num, int fraction_digits){  int integer_part = num / (int)pow(10, fraction_digits);  int fractional_part = num % (int)pow(10, fraction_digits);
  print_int(integer_part);  putchar('.');
  for (int i = 0; i < fraction_digits; i++)  {    fractional_part *= 10;    putchar((fractional_part / (int)pow(10, fraction_digits - i)) + '0');  }}

5、自定義封裝成 printf 風(fēng)格的函數(shù)

將以上函數(shù)封裝成一個(gè)類似 printf 的函數(shù)。為了簡(jiǎn)單說明案例,這里只支持 %d 和 %f(使用定點(diǎn)數(shù))格式說明符。

void my_printf(const char *format, ...){  va_list args;  va_start(args, format);
  const char *ptr = format;  while (*ptr)  {      if (*ptr == '%')      {          ptr++;          switch (*ptr)          {              case 'd':              {                  int num = va_arg(args, int);                  print_int(num);                  break;              }              case 'f':              { // 使用定點(diǎn)數(shù)表示                  int num = va_arg(args, int); // 整數(shù)部分和小數(shù)部分合并的定點(diǎn)數(shù)                  int fraction_digits = va_arg(args, int); // 小數(shù)位數(shù)                  print_fixed_point(num, fraction_digits);                  break;              }              default:                  putchar('%');                  putchar(*ptr);                  break;          }      } else {          putchar(*ptr);      }      ptr++;  }
  va_end(args);}

最后,printf的基本功能就實(shí)現(xiàn)了。。。

最最后要說明一下,這里舉例只是簡(jiǎn)單給大家說明原理,真正項(xiàng)目,其實(shí)還需要添加很多內(nèi)容,比如:串口發(fā)送超時(shí)、參數(shù)驗(yàn)證等容錯(cuò)設(shè)計(jì)。

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

作者黃工,從事嵌入式軟件開發(fā)工作8年有余,高級(jí)嵌入式軟件工程師,業(yè)余維護(hù)公眾號(hào)『strongerHuang』,分享嵌入式軟硬件、單片機(jī)、物聯(lián)網(wǎng)等內(nèi)容。