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

  • 相关阅读:
    文本替换
    国际时间转化为北京时间
    nginx 白名单
    System.Web.HttpException 超过了最大请求长度。
    nginx 优化
    nginx 502 504
    nginx 配置文件相关参数
    nginx location指令 正则表达式
    子网下连接路由器配置
    Java初学者
  • 原文地址:https://www.cnblogs.com/uil2liu/p/14695130.html
Copyright © 2011-2022 走看看