单片机C语言实现printf
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
单片机串口实现printf函数
背景叙述:
在初学C语言时,利用printf函数向屏幕上输出字符,非常的好用和方便。
学会单片机以后,尤其在学会串口通信之后,通过单片机的串口向PC机发送数据,怎么实现printf函数的功能。
对于学习单片机来说,如果没有仿真器,出错误的时候不好找,只能用最笨的办法,使用printf函数,可是在KEIL里面使用printf却不像VC里面那么简单,因为其发送和硬件是有关系的。
所以今天就简单来说说怎么在51单片机中使用"printf"函数:包括使用自带的printf函数和自己动手写一个printf函数功能描述:
通过printf函数向串口发送数据,显示在终端或者串口助手上Proteus中串口终端的介绍
2、串口的配置
尤其注意配置的波特率一定要和程序代码中所写的一样(否则会造成
无法显示字符)
数据位
停止位
奇偶校验位
极性(这些属性基本上是默认的配置)
3、串口终端的使用
proteusvirtualterminal使用注意事项
a.virtualterminal默认显示字符,如果单片机发送的是非显示字符,则虚拟终端不会显示,会导致用户认为通讯未通。
运行程序,在虚拟终端窗口里面点击右键,在弹出菜单里面:
选HEXDISPLAYMODEU显示按十六进制显示,能显示所有字符。
b.virtualterminal默认情况下不显示回显字符,
运行程序,在虚拟终端窗口里面点击右键,在弹出菜单里面:
选EchoTypedCharacters显示回显字符
c.virtualterminal和模拟物理串口COMPIM不能同时使用,同时使用
会发生相互影响。
d.启动仿真后可以右键单击终端,最后一栏打开和关闭串口显示终
直接使用系统自带的printf函数:特别注意需要置位TI=1,否则是无法发送的,程序如下(此程序在proteus终端上不能显示汉字,但在串口调试助
/******decription ******************
手上可以)
利用printf函数向串口打印信息
51单片机晶振频率11.0592MHZ 串口通信波特率9600bps
******* includesfile ************* **********************************『
#include<reg51.h>
#include<intrins.h>
#include<string.h>
#include<stdio.h>
#defineucharunsignedchar #defineuintunsignedint
voiddelay_1ms(uintz)
{
uintx,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--); }
voiduart_init(void)
TMOD=0x20;〃定时计数器1,工作方式2
TH1=0xFD;〃设置波特率9600
TL1=0xFD;
TR1=1;
SCON=0x50;
PCON=0x00;
EA=1;
ES=1;
TI=1;〃若利用printf函数则TI初始值必须置1 )
voidmain()
(
uart_init();
while(1)
(
//SBUF='H';
//while(TI==0);
//TI=0;
printf("iamherro!\n\r");
delay_1ms(1000);
(
ff=0;
printf("%c",num);
)
)
)
voidser_interrupt()interrupt4
(
if(RI==1)
(
ES=0;
RI=0;
num=SBUF;
ES=1;
ff=1;
)
)
4.编写自己的printf函数(这个函数不能再proteus中仿真,但实际中
可以使用)
a.预备知识
①C语言函数:vsprintf,
其原型为intvsprintf(char*string,char*format,va_listparam);,作用为将param按格式format写入字符串string中,因此他可以用于将任何格式数
据转化为字符串数据,比如把整数97转化为ASCII码的97的
程序就像下面这样vsprintf(string,"%d",97),当然还有一点需要注意:在keil 里面使用sprintf需要包含stdio.h这个头文件(当然这里也可以使用sprintf,作用完全差不多,只需要稍加修改即可,不多介绍)。
说
到这里,再来说说另一个函数itoa吧,其实他比vsprintf更简单,其原型为char*itoa(intvalue,char*string,intradix),作用为将value按radix进制写入到string字符串中,使用时需要包含头文件stdlib.h,
可是在keil里面却无法使用,头文件里面不包含itoa,即使把VC里
面的复制进来貌似还是不行,呵呵,目前不知道原因。
②可变参数函数
具体来说就是stdarg.h里面的这几个函数va_start,va_arg,va_list,va_end 这几个参数用于开辟一段内存区域,可以配合vsprintf使用,但是对内存使用较大,需要单片机具备一定的RAM,否则程序就算能编译通过也是无法运行的。
函数形参列表中的变量在内存中的位置是顺次排列的。
头文件Stdarg.h里的几个宏定义就是利用了这么一点,顺次获取多个参数Keil中上述函数的定义如下
typedefchar*va_list;//va_list即为字符指针类型
#defineva_start(ap,v)ap=(va_list)&v+sizeof(v)
#defineva_arg(ap,t)(((int*)ap)++[0])
#defineva_end(ap)//keil中什么也没有做
每个平台下面的Stdarg头文件的定义都是不相同的。
就拿keil那里的来入手。
先看一个最简单的可变参数列表的函数:
voidmytest(inta,...)
{
intb;
va_listap;
va_start(ap,a);
b=va_arg(ap,int);
va_end(ap);
printf("%d,%d",a,b);
}
i. 首先定义一个va_list型的变量ap,也就是char*。
ii. va_start(ap,a彦替换之后就是ap=(va_list)&a+sizeof(a);
首先取a的地址,即第一个固定参数的地址,然后强制类型转换为va_list,接着后移a的内存大小,把当前这个地址值赋给ap。
很明显,就是第一个参数a后面的那个地方,按照上面说的,也就是第一个可变参数。
即现在把ap指向第一个可变参数。
iii. b=va_arg(ap,int宏替换为b=((int*)ap)++[0];
自加在后,因此是获取第一个参数的值赋给b,然后ap后移一个
类型的位置,即指向下一个元素的地址。
iiii.va_end(ap),这里什么都没有做,在ADS那个版本里是将ap指向NULL防止误操作。
具体内容详见/googlemi/article/details/8988567
③内部拓展RAM
定义:集成在单片机内部的数据存储器,在物理上是内部,但逻辑上
是外部,访问时需要使用MOVX或者xdata访问,具体可以看STC8051手册。
89C52单片机内含有1024byte=1Kbyte的内部拓展RAM,在访问的时候,使用C语言的时候,需要加上xdata才可以访问,使用汇编的时候需要用MOVX指令访问。
xdata:访问内部RAM数据
/******decription ******************
源程序
利用printf函数向串口打印信息
51单片机晶振频率11.0592MHZ
串口通信波特率9600bps
利用系统自带printf函数TI初始值必须置1
***********************************
******* includesfile *************
#include<reg51.h>
#include<intrins.h>
〃用于vsprintf函数原型
#include<stdio.h>#include<stdarg.h>
#defineucharunsignedchar
#defineuintunsignedint
voiddelay_1ms(uintz);
voiduart_init(void);
voidsendbyte(ucharc);
voidsendstring(uchar*string);
voiduart_printf(constchar*fmt,...
可变参数列表函数
voidmain(void)
{
inta=99;
uart_init();
while(1)
uart_printf("10进制%d16进制%x字符格
式%c",a,a,a);
delay_1ms(1000);
)
)
voiddelay_1ms(uintz)
{
uintx,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
)
voiduart_init(void)
{
TMOD=0x20;〃定时计数器1,工作方式2 TH1=0xFD;〃设置波特率9600
TL1=0xFD;
TR1=1;
SCON=0x50;
PCON=0x00;
EA=1;
ES=1;
)voidsendbyte(unsignedcharc)
if(c=='\n')
(
SBUF=0X0D;
while(TI==0);
TI=0;
SBUF=0X0A;
while(TI==0);
TI=0;
}
else
(
SBUF=c;
while(TI==0);
TI=0;
}
}
voidsendstring(unsignedchar*string)
(
while(*string!='\0')//判断是否到字符串的尾端sendbyte(*string);
string++;
)
)
voiduart_printf(constchar*fmt,...)
(
va_listap;
charxdatastring[1024];〃访问内部RAM
va_start(ap,fmt);
vsprintf(string,fmt,ap);
sendstring(string);
va_end(ap);
)
voiduart_interrupt()interrupt4 (
if(RI==1)
(
ES=0;
/*添加处理代码*/
ES=1;。