51单片机串口通信使用类似printf函数的两种办法

来源:互联网 发布:网络布线验收标准 编辑:程序博客网 时间:2024/05/17 14:27

<span style="font-family: Arial; font-size: 14px; line-height: 26px; background-color: rgb(255, 255, 255);"></span><pre name="code" class="cpp"><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; font-family: Arial; font-size: 14px; line-height: 26px;"></p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; font-family: Arial; font-size: 14px; line-height: 26px;">今天就简单来说说怎么在51单片机中使用"printf"函数:包括使用自带的printf函数和自己动手写一个printf函数,再贴代码之前先介绍几个相关的知识,主要用于自己写printf函数。</p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; font-family: Arial; font-size: 14px; line-height: 26px;">①C语言函数:vsprintf,</p>

其原型为int vsprintf(char *string, char *format, va_list param);,作用为将param按格式format写入字符串string中,因此他可以用于将任何格式数据转化为字符串数据,比如把整数97转化为ASCII码的97的程序就像下面这样vsprintf(string,"%d",97),当然还有一点需要注意:在keil里面使用sprintf需要包含stdio.h这个头文件(当然这里也可以使用sprintf,作用完全差不多,只需要稍加修改即可,不多介绍)。说到这里,再来说说另一个函数itoa吧,其实他比vsprintf更简单,其原型为char *itoa(int value, char *string, int radix),作用为将value按radix进制写入到string字符串中,使用时需要包含头文件stdlib.h,可是在keil里面却无法使用,头文件里面不包含itoa,即使把VC里面的复制进来貌似还是不行,呵呵,目前不知道原因。

②可变参数函数

具体来说就是stdarg.h里面的这几个函数va_start,va_arg,va_list,va_end,这几个参数用于开辟一段内存区域,可以配合vsprintf使用,但是对内存使用较大,需要单片机具备一定的RAM,否则程序就算能编译通过也是无法运行的,用法见这里http://blog.csdn.net/googlemi/article/details/8988567

③内部拓展RAM

定义:集成在单片机内部的数据存储器,在物理上是内部,但逻辑上是外部,访问时需要使用MOVX或者xdata访问,具体可以看STC8051手册,如下所示





:内部RAM(256byte)包括三部分:低128byte,高128byte和特殊功能寄存器区,特别注意和51不同,他的特殊功能寄存器和高128byte地址重叠,但是物理上是分开的,所有内部可用的RAM有256byte,所以到此为止可以得到内部RAM大小总共有:256byte+1024byte=1280byte

说了这么多,下面不多说,直接上程序

《一》直接使用系统自带的printf函数:特别注意需要置位TI=1,否则是无法发送的,程序如下:

//此程序主要用于uart发送(proteus终端不能显示汉字,但串口助手可以),输入换行符\n换行#include<reg52.h>//stdio.h,string.h用于printf函数原型#include<stdio.h>void delay(unsigned int z);void uart_init(void);//串行口初始化int main(void){int a=99;char *string="abde";uart_init();while(1){printf("%d  %x  %c  %s  %p\n",a,a,(char)a,string,string);delay(1000);}return 0;}void uart_init(void){TMOD=0x20;//即0010 0000,定时器/计数器1,工作方式2TH1=0xfd;//设置波特率为9600TL1=0xfd;TR1=1;//启动定时器/计数器1SCON=0x50; //0101 0000.串口工作方式1,允许串行控制PCON=0x00;//设置SMOD=0IE=0x90; //CPU允许中断,串行允许中断TI=1;//直接使用printf必须加入此句才能实现发送}void delay(unsigned int z){unsigned int x,y;for(x=z;x>0;x--)for(y=110;y>0;y--);}

《二》自己动手写一个类似printf的函数:uart_printf

但是这种情况占用RAM较多,因为需要开辟大数组,所以需要使用拓展型的51单片机,普通的AT89C51和STC89C52之类的会造成内存不够用、堆栈溢出等等问题,所以以下程序都是基于STC12C5A60S2的,因为它含有内部拓展的1024byte的RAM,可以用来存储大数组

//此程序主要用于uart发送(proteus不能仿真,但实际是可以运行的),输入换行符\n换行#include<reg52.h>//stdio.h,stdarg.h用于vsprintf函数原型#include<stdio.h>#include<stdarg.h>void delay(unsigned int z);void uart_init(void);//串行口初始化void sendbyte(unsigned char c);void sendstring(unsigned char *string);void uart_printf(const char *fmt,...);int main(void){int a=99;uart_init();while(1){uart_printf("10进制:%d  16进制:%x  字符格式:%c\n",a,a,a);delay(1000);}return 0;}void uart_init(void){TMOD=0x20;//即0010 0000,定时器/计数器1,工作方式2TH1=0xf3;//设置波特率为2400TL1=0xf3;TR1=1;//启动定时器/计数器1SCON=0x50; //0101 0000.串口工作方式1,允许串行控制PCON=0x00;//设置SMOD=0IE=0x00; //由于是查询方式,故需要禁止中断,CPU不允许中断,串行不允许中断}void delay(unsigned int z){unsigned int x,y;for(x=z;x>0;x--)for(y=110;y>0;y--);}void sendbyte(unsigned char c){if(c=='\n')//如果遇到\n就换行{//发送CR(carriage return)SBUF=0x0D; while(!TI);//等待发送完成TI=0;//发送 LF(NL line feed,new line)SBUF=0x0A; while(!TI);//等待发送完成TI=0;}else{SBUF=c;while(!TI);//等待发送完成TI=0;}}void sendstring(unsigned char *string)//此处*string相当于数组{while(*string!='\0')//判断是否到字符串末尾{sendbyte(*string);string++;}}void uart_printf(const char *fmt,...){va_list ap;char xdata string[1024];//访问内部拓展RAM,非访问外部RAM,不能超过内部拓展RAM大小(此处为1024)va_start(ap,fmt);vsprintf(string,fmt,ap);//此处也可以使用sprintf函数,用法差不多,稍加修改即可,此处略去sendstring(string);va_end(ap);}

最后简单总结 :其中第一种方法用于中断方式工作会有问题,因为其底层是调用putchar函数实现的,工作方式为中断,但是比较简单,可以通过proteus仿真;第二种方法更完善,不过程序比较复杂,不能使用proteus仿真,但实际是可以工作的。


0 0
原创粉丝点击