zoukankan      html  css  js  c++  java
  • 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
    ");
    	}
    }
    

    编译上述代码,运行,发现单片机串口没有输出数据。

    进入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 '
    ' into CR LF and handles
     *                          XON/XOFF (Ctrl+S/Ctrl+Q) protocol
     */
    char putchar (char c)  {
    
      if (c == '
    ')  {
        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 '
    ' into CR LF
     */
    char putchar (char c)  {
      if (c == '
    ')  {
        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

  • 相关阅读:
    Notes of Daily Scrum Meeting(12.18)
    Notes of Daily Scrum Meeting(12.17)
    Notes of Daily Scrum Meeting(12.16)
    Notes of Daily Scrum Meeting(12.8)
    Notes of Daily Scrum Meeting(12.5)
    Notes of Daily Scrum Meeting(12.3)
    Notes of Daily Scrum Meeting(11.12)
    Linux中profile、bashrc、bash_profile之间的区别和联系
    Linux GCC编译
    mysql 5.7.16 远程连接
  • 原文地址:https://www.cnblogs.com/uil2liu/p/14695130.html
Copyright © 2011-2022 走看看