MCU軟件最佳實踐——使用printf打印數據


在mcu上開發應用時,使用串口打印調試信息是最常用的調試手段之一。printf是c標准庫提供的函數,可以方便輸出格式化的信息。但針對不同的mcu芯片,printf函數要能正常工作,需要做一些移植和適配工作。本文以at89c51為例,講解printf的適配。

1. printf的原理

printf是一個可變參數函數,它根據用戶提供的格式化字符串、可變參數,構造出一個最終要輸出的字符串,然后調用stdio庫的putchar函數打印輸出信息到相應的設備。putchar針對不同的平台、不同的應用場景有不同的實現。例如在pc上,putchar打印輸出到屏幕。

2. mcu上串口通信的配置

2.1 使用keil提供的putchar.c

#include "reg51.h"
#include "stdio.h"
// 已at89c51為例,波特率的設置參照文章結尾附表
void uart_init(void)
{
	// 串口工作在10bit模式
	// 采用可變波特率,波特率為定時器T1溢出率
	// SM0 SM1 SM2 REN TB8 RB8 TI RI
	// SM0 SM1: 
	// 00--同步移位方式 
	// 01--10bit異步收發,可變波特率,T1溢出率決定
	// 10--11bit異步收發,固定波特率
	// 11--11bit異步收發,波特率可變,T1溢出率決定
	SCON = 0X50;
	
	// 設置波特率為9600bps,假設外部晶振為11.0592MHz
	// 1.T1工作在模式2,8bit自動充裝模式
	// 2.T1初始值為0xfd
	// GATE C/#T M1 M0 GATE C/#T M1 M0
	// M1M0:
	// 00--13bit計數模式,
	// 01--16bit計數模式,
	// 10--8bit自動重裝
	TMOD = (TMOD & 0X0F) | (1 << 5); 
	TH1 = TL1 = 0XFD;
	TR1 = 1; // 啟動定時器

	ES = 1;  
	EA = 1;
}

// 開啟了串口中斷,需要編寫串口中斷服務程序,否則程序跑飛
// 如果沒有開啟串口中斷,可以不用下面的中斷服務函數
void uart_isr(void) interrupt 4
{
	if(RI) // 中斷是接收數據觸發
	{
		// 處理數據
	}
	if(TI) // 中斷是發送數據觸發
	{
	
	}
}
void main(void)
{
	uart_init();
	while(1)
	{
		printf("hello,uart\r\n");
	}
}

編譯上述代碼,運行,發現單片機串口沒有輸出數據。

進入mdk安裝目錄,進入c51/lib目錄下,發現有一個文件為putchar.c,打開putchar.c,定義如下:

/***********************************************************************/
/*  This file is part of the C51 Compiler package                      */
/*  Copyright KEIL ELEKTRONIK GmbH 1990 - 2002                         */
/***********************************************************************/
/*                                                                     */
/*  PUTCHAR.C:  This routine is the general character output of C51.   */
/*  You may add this file to a uVision2 project.                       */
/*                                                                     */
/*  To translate this file use C51 with the following invocation:      */
/*     C51 PUTCHAR.C <memory model>                                    */
/*                                                                     */
/*  To link the modified PUTCHAR.OBJ file to your application use the  */
/*  following Lx51 invocation:                                         */
/*     Lx51 <your object file list>, PUTCHAR.OBJ <controls>            */
/*                                                                     */
/***********************************************************************/
#include <reg51.h>
#define XON  0x11
#define XOFF 0x13
/*
 * putchar (full version):  expands '\n' into CR LF and handles
 *                          XON/XOFF (Ctrl+S/Ctrl+Q) protocol
 */
char putchar (char c)  {

  if (c == '\n')  {
    if (RI)  {
      if (SBUF == XOFF)  {
        do  {
          RI = 0;
          while (!RI);
        }
        while (SBUF != XON);
        RI = 0; 
      }
    }
    while (!TI);
    TI = 0;
    SBUF = 0x0d;                         /* output CR  */
  }
  if (RI)  {
    if (SBUF == XOFF)  {
      do  {
        RI = 0;
        while (!RI);
      }
      while (SBUF != XON);
      RI = 0; 
    }
  }
  while (!TI);
  TI = 0;
  return (SBUF = c);
}
#if 0         // comment out versions below

/*
 * putchar (basic version): expands '\n' into CR LF
 */
char putchar (char c)  {
  if (c == '\n')  {
    while (!TI);
    TI = 0;
    SBUF = 0x0d;                         /* output CR  */
  }
  while (!TI);
  TI = 0;
  return (SBUF = c);
}
/*
 * putchar (mini version): outputs charcter only
 */
char putchar (char c)  {
  while (!TI);
  TI = 0;
  return (SBUF = c);
}
#endif

分析代碼后發現,程序運行到while(!TI)停止在該句,因為初始化后TI默認為0,而且還沒有發送過數據,TI一直為0,因此程序不會繼續向下執行。

解決方法:修改uart_init函數,添加TI = 1啟動發送。

void uart_init(void)
{
	// 串口工作在10bit模式
	// 采用可變波特率,波特率為定時器T1溢出率
	SCON = 0X50;
	
	// 設置波特率為9600bps,假設外部晶振為11.0592MHz
	// 1.T1工作在模式2,8bit自動充裝模式
	// 2.T1初始值為0xfd
	TMOD = (TMOD & 0X0F) | (1 << 5);
	TH1 = TL1 = 0XFD;
	TR1 = 1; // 啟動定時器
	
	ES = 1;
	EA = 1; 
	TI = 1; //#!!! 必須加這句,以啟動發送,否則無法使用printf輸出
}

2.2 用戶自定義putchar函數

keil提供的putchar,帶流控功能,同時,必須在初始化uart時候,保證調用了TI = 1以啟動發送,否則printf無法打印輸出。當然,用戶可以自定義putchar函數,簡單的實現如下:

char putchar(char c)
{
	SBUFF = c;
	while(!TI);
	TI = 0;
	return c;
}

附:51單片機常用波特率初指表(晶振11.0592MHz情形)

image


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM