51單片機串口通信中需要發送數據,而一般都會使用printf這個外部函數,printf函數在<stdio.h>這個頭文件中,所以要使用這個函數必須要有stdio.h這個頭文件。printf函數不需要我們去定義其內部實現,可以直接使用,當然也可以自己去構造一個相似功能的函數。
在stdio.h中可以看到printf函數的第一個參數為一個字符指針其用法與c語言中的的是一樣的,而printf函數的內部實現是依靠putchar這個函數來實現的,putchar這個函數在c51的庫文件下有定義
源碼如下
- #include <reg51.h>
- #define XON 0x11 /*串口流控制符 啟動*/
- #define XOFF 0x13 /*串口流控制符 中斷*/
- /*
- * putchar (full version): expands '\n' into CR LF and handles /*完整版 每次發送數據都要檢查sbuf是否有中斷信號 */
- * XON/XOFF (Ctrl+S/Ctrl+Q) protocol /* XON啟動 XOFF中斷 通信協議*/
- */
- char putchar (char c) {
- if (c == '\n') { /*判斷是否是換行符的原因,是因為字符串的標準格式是末尾為\r(回車符)\n(換行符)這兩個字符*/
- if (RI) { /*判斷接收標識符是否為1,若為1則說明SBUF接受到了信息*/
- if (SBUF == XOFF) { /*判斷SBUF中的信息是否為中斷信號 是則執行以下程序*/
- do {
- RI = 0; /*將接收標識符置1 可以繼續接收信息*/
- while (!RI); /*判斷是否接收到了信息,是則往下循環*/
- }
- while (SBUF != XON); /*判斷接收的信息是否為啟動信息,是則退出循環,不是繼續循環*/
- RI = 0; /*將接受標識符置1 可以繼續接收信息*/
- }
- }</div><div>/*只要c是換行符,最終都要執行這里 判斷發送標識符是否為1,只有為1才往下執行,這點非常重要調用printf函數時,必須將TI置1*/</div><div>while (!TI);</div><div> TI = 0; /*將TI置0 準備發送數據*/
- SBUF = 0x0d; /* output CR */ /* 發送回車符*/
- }
- if (RI) { /*下面的if函數又是判斷SBUF中是否接收了中斷信號與上面的一樣*/
- if (SBUF == XOFF) {
- do {
- RI = 0;
- while (!RI);
- }
- while (SBUF != XON);
- RI = 0;
- <div> }</div><div>while (!TI); /*判斷發送標識符是否為1*/
- TI = 0; /*將TI置0 準備發送數據*/
- return (SBUF = c); /*發送字符c*/
- }
- #if 0 // comment out versions below
- /*
- * putchar (basic version): expands '\n' into CR LF /*精簡版*/
- */
- char putchar (char c) {
- if (c == '\n') { /*還是判斷字符c是不是換行符*/
- while (!TI); /*判斷TI是否置1 為1向下執行*/
- TI = 0;
- SBUF = 0x0d; /* output CR */ /* c是換行符先發送回車符*/
- }
- while (!TI); /*又是判斷TI是否為1 為1向下執行*/
- TI = 0; /*將TI置0 準備發送數據*/
- return (SBUF = c); /*發送字符c*/
- }
- /*
- * putchar (mini version): outputs charcter only /*迷離版 少了判斷字符c是否為換行符的步驟 */
- */
- char putchar (char c) {
- while (!TI); /*判斷TI是否置1 為1向下執行*/
- TI = 0; /*將TI置0 準備發送數據*/
- return (SBUF = c);/*發送字符c*/
- }
- #endif
復制代碼 對于上面putchar函數的實現過程以及細節我已經注釋的非常清楚了,但上面的串口流控制符可能有些人有疑惑,我這里解釋一下。
XON/XOFF 這兩個字符就是串口流控制字符,所謂的流就是數據流,當兩個串口之間進行通信時,鑒于兩個設備的硬件不一樣,一個設備發送的信息讓另一個設備處理不過來時,接收方設備就向發送方發送一個XOFF(0X13)字符,發送方接收到這個字符以后就會中斷發送,直到接收方處理完數據后又向發送方發送一個XON(0x11)字符,當發送方接收到這個字符時又開始向接收方發送數據。
接下來我們可以用printf函數來發送數據了,但發送數據前要將TI置1
- #include<reg52.h>
- #include<stdio.h>
- #include<stdlib.h>
- #include<intrins.h>
- void af(int a){
- int b,c;
- for(b=0;b<a;b++)
- for(c=0;c<110;c++);
- }
- void main(){
- TMOD=0X20;
- TH1=0Xfd;
- TL1=0xfd;
- SCON=0X50;
- SM0=0;
- SM1=1;
- TR1=1;
- EA=1;
- ES=0;
- REN=1;
- TI=1; /*初始化將TI置1*/
- while(1){
- af(1000); /*延遲1秒*/
- printf("ghjkl;lkjhgffghjklkjhgfghjk"); /*調用printf函數*/
- TI=1; /*又將TI置1 方便下車調用printf函數*/
- }
- }
復制代碼 以上就是printf函數的使用方法,如果覺得這個函數不好使用,可以對putchar函數的源代碼進行改動,構造出自己的輸出函數這里就不做介紹了。
發送數據可以使用printf函數但如果我們想接收字符串時就需要自己構造函數,下面的接收字符串函數僅供參考
- #include<reg52.h>
- #include<stdio.h>
- char a=0; /*定義全局字符變量a并且賦值為'0' a用于接收字符函數判斷串口中斷是否產生 */
- char c[30]="0"; /*定義全局字符數值c初始為'0' c用于存放接收的字符串 */
- int b=0; /*定義全局整型變量b 初始b為0 b用于字符串的賦值串口每中斷一次b加1 */
- void yc(int a){ /*構造延遲函數yc*/
- int b,c;
- for(b=0;b<a;b++)
- for(c=0;c<110;c++);
- }
- int scan(){ /*構造接收字符串函數scan*、/
- int af; /*定義一個整型變量af 用于返回值*/
- int ef=0; /*定義一個整型變量ef 賦值為0 用于判斷*/
- if(a==1){ /*通過判斷a的值來判斷串口中斷是否產生*/
- af=b; /*將全局變量b的值賦值給變量af*/
- yc(1); /*延遲1秒*/
- while(b!=af){ /*判斷b與af是否相等 不等進入循環 否則退出循環 */
- af=b; /*又將b的值賦值給af*/
- yc(1); /*延遲1秒*/
- }
- a=0; /*將a復原為0以便下次處理*/
- c[b]='\0'; /*將第b個地址位賦值為空字符*/
- b=0; /*將b清零*/
- ef=1; /*ef賦值1 以便返回*/
- }
- return ef;
- }
- void main(){
- TMOD=0X20;
- TH1=0Xfd;
- TL1=0Xfd;
- SM0=0;
- SM1=1;
- REN=1;
- TR1=1;
- EA=1;
- ES=1;
- while(1){
- while(!scan()); /*接收到字符串就退出循環*/
- TI=1; /*TI置1 下面調用printf函數*/
- ES=0; /*串口中斷置不置0其實沒有多大的影響*/
- printf(c); /*用printf函數將接收到的字符串發送出去*/
- ES=1 ;
- }
- }
- void zd()interrupt 4
- {
- RI=0; /*RI置0 以便下次中斷*/
- a=1; /*有中斷a就為1*/
- c[b]=SBUF; /*將每次接受的字符存入c中*/
- b++; /*中斷產生則b加1*/
- }
復制代碼
出自 51黑電子論壇
|