找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 11378|回复: 5
收起左侧

51单片机printf函数的说明及使用以及串口接收字符处理

  [复制链接]
ID:527981 发表于 2019-5-25 17:52 | 显示全部楼层 |阅读模式
51单片机串口通信中需要发送数据,而一般都会使用printf这个外部函数,printf函数在<stdio.h>这个头文件中,所以要使用这个函数必须要有stdio.h这个头文件。printf函数不需要我们去定义其内部实现,可以直接使用,当然也可以自己去构造一个相似功能的函数。
头文件.PNG
在stdio.h中可以看到printf函数的第一个参数为一个字符指针其用法与c语言中的的是一样的,而printf函数的内部实现是依靠putchar这个函数来实现的,putchar这个函数在c51的库文件下有定义
putchar.PNG
源码如下
  1. #include <reg51.h>

  2. #define XON  0x11             /*串口流控制符  启动*/
  3. #define XOFF 0x13            /*串口流控制符  中断*/


  4. /*
  5. * putchar (full version):  expands '\n' into CR LF and handles     /*完整版 每次发送数据都要检查sbuf是否有中断信号 */
  6. *                          XON/XOFF (Ctrl+S/Ctrl+Q) protocol             /* XON启动 XOFF中断      通信协议*/                                                         
  7. */
  8. char putchar (char c)  {

  9.   if (c == '\n')  {          /*判断是否是换行符的原因,是因为字符串的标准格式是末尾为\r(回车符)\n(换行符)这两个字符*/
  10.     if (RI)  {                          /*判断接收标识符是否为1,若为1则说明SBUF接受到了信息*/
  11.       if (SBUF == XOFF)  {       /*判断SBUF中的信息是否为中断信号  是则执行以下程序*/
  12.         do  {
  13.           RI = 0;                          /*将接收标识符置1 可以继续接收信息*/
  14.           while (!RI);                    /*判断是否接收到了信息,是则往下循环*/
  15.         }
  16.         while (SBUF != XON);     /*判断接收的信息是否为启动信息,是则退出循环,不是继续循环*/
  17.         RI = 0;                          /*将接受标识符置1 可以继续接收信息*/
  18.       }
  19.   }</div><div>/*只要c是换行符,最终都要执行这里   判断发送标识符是否为1,只有为1才往下执行,这点非常重要调用printf函数时,必须将TI置1*/</div><div>while (!TI);</div><div>  TI = 0;                                         /*将TI置0 准备发送数据*/
  20.   SBUF = 0x0d;                         /* output CR  */          /*  发送回车符*/
  21.   }
  22.   if (RI)  {                           /*下面的if函数又是判断SBUF中是否接收了中断信号与上面的一样*/
  23.     if (SBUF == XOFF)  {
  24.       do  {
  25.         RI = 0;
  26.         while (!RI);
  27.       }
  28.       while (SBUF != XON);
  29.       RI = 0;
  30. <div>    }</div><div>while (!TI);      /*判断发送标识符是否为1*/
  31. TI = 0; /*将TI置0 准备发送数据*/
  32.   return (SBUF = c);                                /*发送字符c*/
  33. }



  34. #if 0         // comment out versions below

  35. /*
  36. * putchar (basic version): expands '\n' into CR LF                  /*精简版*/
  37. */
  38. char putchar (char c)  {              
  39.   if (c == '\n')  {             /*还是判断字符c是不是换行符*/
  40.     while (!TI);                  /*判断TI是否置1     为1向下执行*/
  41.     TI = 0;
  42.     SBUF = 0x0d;                         /* output CR  */       /* c是换行符先发送回车符*/
  43.   }
  44.   while (!TI);                                /*又是判断TI是否为1   为1向下执行*/
  45.   TI = 0;                                     /*将TI置0 准备发送数据*/
  46.   return (SBUF = c);                    /*发送字符c*/
  47. }


  48. /*
  49. * putchar (mini version): outputs charcter only                    /*迷离版   少了判断字符c是否为换行符的步骤 */
  50. */
  51. char putchar (char c)  {
  52.   while (!TI);                                           /*判断TI是否置1     为1向下执行*/                     
  53.   TI = 0;                                                  /*将TI置0 准备发送数据*/
  54.   return (SBUF = c);/*发送字符c*/
  55. }                                                         

  56. #endif
复制代码
对于上面putchar函数的实现过程以及细节我已经注释的非常清楚了,但上面的串口流控制符可能有些人有疑惑,我这里解释一下。
XON/XOFF  这两个字符就是串口流控制字符,所谓的流就是数据流,当两个串口之间进行通信时,鉴于两个设备的硬件不一样,一个设备发送的信息让另一个设备处理不过来时,接收方设备就向发送方发送一个XOFF(0X13)字符,发送方接收到这个字符以后就会中断发送,直到接收方处理完数据后又向发送方发送一个XON(0x11)字符,当发送方接收到这个字符时又开始向接收方发送数据。

接下来我们可以用printf函数来发送数据了,但发送数据前要将TI置1
  1. #include<reg52.h>
  2. #include<stdio.h>
  3. #include<stdlib.h>
  4. #include<intrins.h>
  5. void af(int a){
  6. int b,c;
  7. for(b=0;b<a;b++)
  8. for(c=0;c<110;c++);
  9. }
  10. void main(){
  11. TMOD=0X20;
  12. TH1=0Xfd;
  13. TL1=0xfd;
  14. SCON=0X50;
  15. SM0=0;
  16. SM1=1;
  17. TR1=1;
  18. EA=1;
  19. ES=0;
  20. REN=1;
  21. TI=1;                /*初始化将TI置1*/

  22. while(1){
  23. af(1000);          /*延迟1秒*/

  24. printf("ghjkl;lkjhgffghjklkjhgfghjk");       /*调用printf函数*/
  25.           TI=1;                                                 /*又将TI置1 方便下车调用printf函数*/
  26. }
  27. }
复制代码
以上就是printf函数的使用方法,如果觉得这个函数不好使用,可以对putchar函数的源代码进行改动,构造出自己的输出函数这里就不做介绍了。


发送数据可以使用printf函数但如果我们想接收字符串时就需要自己构造函数,下面的接收字符串函数仅供参考
  1. #include<reg52.h>
  2. #include<stdio.h>
  3. char a=0;                            /*定义全局字符变量a并且赋值为'0'    a用于接收字符函数判断串口中断是否产生  */
  4. char c[30]="0";                    /*定义全局字符数值c初始为'0'            c用于存放接收的字符串               */
  5. int b=0;                              /*定义全局整型变量b  初始b为0     b用于字符串的赋值串口每中断一次b加1 */
  6. void yc(int a){                     /*构造延迟函数yc*/
  7. int b,c;
  8. for(b=0;b<a;b++)
  9. for(c=0;c<110;c++);
  10. }
  11. int  scan(){                         /*构造接收字符串函数scan*、/
  12. int af;                                 /*定义一个整型变量af 用于返回值*/
  13. int ef=0;                            /*定义一个整型变量ef 赋值为0  用于判断*/
  14. if(a==1){                            /*通过判断a的值来判断串口中断是否产生*/
  15. af=b;                                  /*将全局变量b的值赋值给变量af*/
  16. yc(1);                                  /*延迟1秒*/
  17. while(b!=af){                       /*判断b与af是否相等     不等进入循环     否则退出循环 */
  18. af=b;                                  /*又将b的值赋值给af*/
  19.   yc(1);  /*延迟1秒*/
  20. }
  21. a=0;                                     /*将a复原为0以便下次处理*/
  22. c[b]='\0';                              /*将第b个地址位赋值为空字符*/
  23. b=0;                                     /*将b清零*/
  24. ef=1;                                    /*ef赋值1 以便返回*/
  25. }
  26. return ef;
  27. }
  28. void main(){
  29. TMOD=0X20;
  30. TH1=0Xfd;
  31. TL1=0Xfd;
  32. SM0=0;
  33. SM1=1;
  34. REN=1;
  35. TR1=1;
  36. EA=1;
  37. ES=1;
  38. while(1){
  39. while(!scan());             /*接收到字符串就退出循环*/
  40. TI=1;                           /*TI置1 下面调用printf函数*/
  41. ES=0;                          /*串口中断置不置0其实没有多大的影响*/
  42. printf(c);                    /*用printf函数将接收到的字符串发送出去*/
  43. ES=1  ;     
  44. }

  45. }

  46. void zd()interrupt 4
  47. {
  48. RI=0;                  /*RI置0 以便下次中断*/
  49. a=1;                   /*有中断a就为1*/
  50. c[b]=SBUF;       /*将每次接受的字符存入c中*/
  51. b++;               /*中断产生则b加1*/

  52. }
复制代码

出自 51黑电子论坛
捕获3.PNG
捕获2.PNG
捕获1.PNG
捕获.PNG
捕获3.PNG
捕获2.PNG
捕获2.PNG
捕获1.PNG
捕获.PNG

评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

回复

使用道具 举报

ID:653902 发表于 2019-12-15 18:51 | 显示全部楼层
看了您对putchar函数的中文注释,感觉非常有用。
有个小问题请教,在17和21行,程序是写的RI=0,注释中为将RI置1,有点不理解,不是应该注释为将RI清零吗?谢谢
回复

使用道具 举报

ID:535176 发表于 2020-4-9 08:00 | 显示全部楼层
我用两个串口这个putchar函数名怎么起啊
回复

使用道具 举报

ID:527981 发表于 2020-12-31 22:53 | 显示全部楼层
rotga 发表于 2019-12-15 18:51
看了您对putchar函数的中文注释,感觉非常有用。
有个小问题请教,在17和21行,程序是写的RI=0,注释中为 ...

确实是你说的那样,我写的有问题
回复

使用道具 举报

ID:527981 发表于 2020-12-31 23:11 | 显示全部楼层
冷月枫 发表于 2020-4-9 08:00
我用两个串口这个putchar函数名怎么起啊

这个是库函数里面的,你可以按照它的结构来自己写相似功能的函数
回复

使用道具 举报

ID:390416 发表于 2021-1-1 10:02 | 显示全部楼层
那么问题来了,函数里面有while(TI)或者while(RI)这样的死等。要是以1200波特率通信,那就不好玩了。
看《人人学会单片机》学串口多字节高效率发送代码。直接中断里面发送。不需要执行while 死等循环。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表