51单片机应用开发(进阶)---51单片机串口重定向printf函数打印

实现目标

1、掌握51单片机的串口重定向

2、掌握printf 输出格式

3、具体目标:1)实现printf  “打印”各种常用的类型的数据变量


一、串口“打印”     

        UART串口通信协议是我们常用的通信协议(UART、I2C、SPI等)之一,全称叫做通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),是异步串行通信协议的一种,工作原理是将传输数据的每个字符一位接一位地传输。

        UART 在开发中应用最多莫过于“打印”程序信息,一般在硬件设计时都会预留一个UART 通信接口连接至电脑,用于在调试程序时可以把一些调试信息“打印”在电脑端的串口调试助手工具上,从而了解程序运行是否正确、如果出错,具体在哪里出错等等。

二、格式化输出函数printf

2.1  printf的一般格式

printf函数的一般格式为:
printf(格式控制字符串,输出值参数表);

如:

printf("f=%f,c=%f\n",f,c);

其中,f=%f,c=%f\n 是格式控制字符串,f,c 是输出值参数表。

(1)格式控制字符串是用双引号括起来的字符串,包括三类信息:
格式字符:格式字符由“%”引导,如%d、%f等。它的作用是控制输出字符的格式。
转义字符:格式控制字符串里的转义字符按照转义后的含义输出,如上面printf函数双引号内的换行符“\n”,即输出回车。
普通字符:普通字符即需要在输出时原样输出的字符,如上面printf函数中双引号内的“f=”和“c= ”部分。
(2)输出值参数表是需要输出的数据项的列表,输出数据项可以是常量、变量或表达式,输出值参数之间用逗号分隔,其类型应与格式字符相匹配。每个格式字符和输出值参数表中的输出值参数一一对应,没有输出参数时,格式控制字符串中不再需要格式字符。

2.2  格式字符

(1)d格式字符

输出带符号的十进制整数,正数的符号不输出。

int a = 256,b = -125;
printf("%d,%d",a,b);

输出结果为:256 ,-125                

可以在%和格式字符中间插入格式修饰符,用于指定输出数据的域宽(所占的列数),如用“%5d”,指定输出数据占5列,输出的数据在域内向右靠齐。如:

int a = 256,b = -125;
printf("%5d\n%5d",a,b);

其中256前面有2个空格,-125前面有一个空格。
若要输出long(长整型)数据,则在格式字符d前面加字母l(代表long),即“%ld”。

2)f格式符

输出一个实数(包括单精度、双精度、长双精度),以小数形式输出,有以下几种用法:

1)基本型,%f

不指定输出数据的长度,由系统根据数据的实际情况决定数据所占的列数。系统处理的方法一般是:实数中的整数部分全部输出,小数部分输出6位。如:

#include<stdio.h>
int main()
{
	double a = 1.0;
	printf("%f\n",a/3);
	return 0;
} 

 运行结果:
请添加图片描述
虽然a是double型,a/3的结果也是double型,但用 %f格式字符只能输出6位小数

2)指定数据宽度和小数位数用%m.nf 。

其中,m表示输出数据的宽度,即占m列,n表示小数点后保留n位小数。(若不需强调输出数据宽度,可直接用%.nf)    如:

#include<stdio.h>
int main()
{
	double a = 1.0;
	printf("%20.15f\n",a/3);
	return 0;
} 

运行结果为:
请添加图片描述
其中,在0前面有3个空格,小数点后输出了15位小数。
注意:一个double型数只能保证15位有效数字的精确度,即使指定小数位数为50(如用%.50f),也不能保证输出的50位都是有效数值。

3)输出的数据向左对齐,用%-m.nf。

即在m.n前面加一个负号,能够让输出数据在域内向左靠齐。如:

#include<stdio.h>
int main()
{
	double a = 1.0;
	printf("%-20.15f\n",a/3);
	return 0;
} 

 运行结果:
请添加图片描述

(3)c格式字符

用于输出一个字符,如:

#include<stdio.h>
int main()
{
	char ch = 'a';
	printf("%c",ch);
	return 0;
} 

 输出结果:a

(4)s格式符

用于输出一个字符串,如:

printf("%s","Hello!");

  输出结果:

2.3  附表

附表1:
printf函数中用到的格式字符

格式字符    说明
d    输出带符号的十进制整数,正数的符号省略
u    以无符号的十进制整数形式输出
o    以无符号的八进制整数形式输出,不输出前导符0
x    以无符号十六进制整数形式(小写)输出,不输出前导符0x
X    以无符号十六进制整数形式(大写)输出,不输出前导符0X
f    以小数形式输出单、双精度数,隐含输出6位小数
e    以指数形式(小写e表示指数部分)输出实数
E    以指数形式(大写E表示指数部分)输出实数
g    自动选取f或e中输出宽度较小的一种使用,且不输出无意义的0
c    输出一个字符
s    输出字符串

附表2:
printf函数中用到的格式修饰符

格式修饰符    说明
英文字母l       修饰格式字符d、u、o、x时,用于输出long型数据
英文字母L      修饰格式字符f、e、g时,用于输出long double型数据
英文字母h      修饰格式字符d、o、x时,用于输出short型数据
输出域宽m   (m为整数)    指定输出项输出时所占的列数
显示精度.n  (n为整数)    对于实数,表示输出n位小数;对于字符串,表示截取的字符个数
-(减号)    输出数字或字符在域内向左靠

 附表3:
printf函数中用到的转义字符


三、原理图设计

四、程序设计

4.1 方式1: 

4.1.1特别说明:

1、需要包含stdio.h头文件

2、串口初试化函数中需要有 TI = 1;

3、中断处理函数去掉TI 判断代码

//if (TI == 1)

//{

//   TI = 0; //Clear transmit interrupt flag

//}

4.1.2 参考程序设计(模块化)

uart.h文件

#ifndef __UART_H__
#define __UART_H__

void uart_init(void);
void Uart_send(unsigned char dat);
void Uart_send_str(unsigned char *p);
 
#endif

uart.c文件

#include "uart.h"
#include <REGX52.H>

#define FOSC 11059200L      //System frequency
#define BAUD 9600           //UART baudrate

void uart_init(void)
{
    PCON &= 0x7F;		    //波特率不倍速
    SCON = 0x50;            //8-bit variable UART
    TMOD = 0x20;//0010 0000 //Set Timer1 as 8-bit auto reload mode
    TH1 = TL1 = -(FOSC/12/32/BAUD); //Set auto-reload vaule
    TR1 = 1;                //Timer1 start run
    ES = 1;                 //Enable UART interrupt
    TI = 1;			        //发送中断标记位,必须设置
	EA = 1;                 //Open master interrupt switch
}

void Uart_send(unsigned char dat)
{
     SBUF = dat;
	 while(!TI);
	 TI = 0;
}
 
void Uart_send_str(unsigned char *p)
{
    while(*p!='\0')
    {
       Uart_send(*p);	
       p++;	
    }
}
 
void Uart_Isr() interrupt 4 
{
    if (RI == 1)
    {
        RI = 0;             //Clear receive interrupt flag      
    }

}

main.c 文件

#include <REGX52.H>
#include <stdio.h> //printf 函数的头文件
#include "uart.h"
 
unsigned int m = 0; 
float n = 3.14;
char i = 'k';

void Delay500ms();
 
void  main()
{
    uart_init();//串口初始化
    while(1)
	 {
		   printf("串口打印调试! \r\n");
		   printf("n = %.2f \r\n",n);
		   printf("i = %c \r\n",i);		
		   m++;
		   printf("m = %d \r\n",m);
	       Delay500ms();		
	 }
}
 
void Delay500ms()		//@11.0592MHz
{
	unsigned char i, j, k;
	i = 4;
	j = 129;
	k = 119;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

 4.2 方式2:

   方式1出现卡顿情况,可以采用方式2

     去掉:TI = 1;     //发送中断标记位,必须设置

     添加:下面代码

char putchar (char c)
{
  SBUF = c; // 把字符c写入发送寄存器
  while(!TI); // 等待发送数据完成
  TI = 0; // 清除发送中断标志
}

uart.h文件

#ifndef __UART_H__
#define __UART_H__

void uart_init(void);
void Uart_send(unsigned char dat);
void Uart_send_str(unsigned char *p);
 
#endif

uart.c文件

#include "uart.h"
#include <REGX52.H>

#define FOSC 11059200L      //System frequency
#define BAUD 9600           //UART baudrate

void uart_init(void)
{
    PCON &= 0x7F;		    //波特率不倍速
    SCON = 0x50;            //8-bit variable UART
    TMOD = 0x20;//0010 0000 //Set Timer1 as 8-bit auto reload mode
    TH1 = TL1 = -(FOSC/12/32/BAUD); //Set auto-reload vaule
    TR1 = 1;                //Timer1 start run
    ES = 1;                 //Enable UART interrupt
	EA = 1;                 //Open master interrupt switch
}


char putchar (char c)
{
	SBUF = c;             // 把字符c写入发送寄存器
	while(!TI);           // 等待发送数据完成
	TI = 0;               // 清除发送中断标志位
}

void Uart_send(unsigned char dat)
{
     SBUF = dat;
	 while(!TI);
	 TI = 0;
}
 
void Uart_send_str(unsigned char *p)
{
    while(*p!='\0')
    {
       Uart_send(*p);	
       p++;	
    }
}
 
void Uart_Isr() interrupt 4 
{
    if (RI == 1)
    {
        RI = 0;             //Clear receive interrupt flag      
    }

}

main.c 文件

#include <REGX52.H>
#include <stdio.h> //printf 函数的头文件
#include "uart.h"
 
unsigned int m = 0; 
float n = 3.14;
char i = 'k';

void Delay500ms();
 
void  main()
{
    uart_init();//串口初始化
    while(1)
	 {
		   printf("串口打印调试! \r\n");
		   printf("n = %.2f \r\n",n);
		   printf("i = %c \r\n",i);		
		   m++;
		   printf("m = %d \r\n",m);
	       Delay500ms();		
	 }
}
 
void Delay500ms()		//@11.0592MHz
{
	unsigned char i, j, k;
	i = 4;
	j = 129;
	k = 119;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

五、实验效果

六、仿真实现


总结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

面包板扎

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值