1.JPG (30.68 KB, 下載次數: 336)
下載附件
2013-9-28 14:54 上傳
通信按照傳統的理解就是信息的傳輸與交換。對于單片機來說,通信則與傳感器、存儲芯片、外圍控制芯片等技術緊密結合,成為整個單片機系統的“神經中樞”。沒有通信,單片機所實現的功能僅僅局限于單片機本身,就無法通過其他設備獲得有用信息,也無法將自己產生的信息告訴其它設備。如果單片機通信沒處理好的話,它和外圍器件的合作程度就受到限制,最終整個系統也無法完成強大的功能,由此可見單片機通信技術的重要性。 UART(Universal Asynchronous Receiver/Transmitter,即通用異步收發器)串行通信是單片機最常用的一種通信技術,通常用于單片機和電腦之間以及單片機和單片機之間的通信。 11.1 串行通信的初步認識通信按照基本類型可以分為并行通信和串行通信。并行通信時數據的各個位同時傳送,可以實現字節為單位通信,但是因為通信線多占用資源多,成本高。比如我們前邊用到的P0 = 0xfe;一次給P0的8個IO口分別賦值,同時進行信號輸出,類似于有8個車道同時可以過去8輛車一樣,這種形式就是并行的,我們習慣上還稱P0、P1、P2和P3為51單片機的4組并行總線。 而串行通信,就如同一條車道,一次只能一輛車過去,如果一個0xfe這樣一個字節的數據要傳輸過去的話,假如低位在前高位在后,那發送方式就是0-1-1-1-1-1-1-1-1,一位一位的發送出去的,要發送8次才能發送完一個字節。 在我們的STC89C52上,有兩個引腳,是專門用來做UART串口通信的,一個是P3.0一個是P3.1,還分別有另外的名字叫做RXD和TXD,這兩個引腳是專門用來進行UART通信的,如果我們兩個單片機進行UART串口通信的話,那基本的演示圖如圖11-1所示。
2.JPG (14.8 KB, 下載次數: 337)
下載附件
2013-9-28 14:30 上傳
圖11-1 單片機之間UART通信示意圖 圖中,GND表示單片機系統電源的參考地,TXD是串行發送引腳,RXD是串行接收引腳。兩個單片機之間要通信,首先電源基準得一樣,所以我們要把兩個單片機的GND相互連起來,然后單片機1的TXD引腳接到單片機2的RXD引腳上,即此路為單片機1發送而單片機2接收的通道,單片機1的RXD引腳接到單片機2的TXD引腳上,即此路為單片機2發送而單片機2接收的通道。這個示意圖就體現了兩個單片機各自收發信息的過程。 當單片機1想給單片機2發送數據時,比如發送一個0xE4這個數據,用二進制形式表示就是0b11100100,在UART通信過程中,是低位先發,高位后發的原則,那么就讓TXD首先拉低電平,持續一段時間,發送一位0,然后繼續拉低,再持續一段時間,又發送了一位0,然后拉高電平,持續一段時間,發了一位1......一直到把8位二進制數字0b11100100全部發送完畢。這里就牽扯到了一個問題,就是持續的這“一段時間”到底是多久?從這里引入我們通信中的另外重要概念——波特率,也叫做比特率。 波特率就是發送一位二進制數據的速率,習慣上用baud表示,即我們發送一位數據的持續時間=1/baud。在通信之前,單片機1和單片機2首先都要明確的約定好他們之間的通信波特率,必須保持一致,收發雙方才能正常實現通信,這一點大家一定要記清楚。 約定好速度后,我們還要考慮第二個問題,數據什么時候是起始,什么時候是結束呢?不管是提前接收還是延遲接收,數據都會接收錯誤。在UART串行通信的時候,一個字節是8位,規定當沒有通信信號發生時,通信線路保持高電平,當要發送數據之前,先發一位0表示起始位,然后發送8位數據位,數據位是先低后高的順序,數據位發完后再發一位1表示停止位。這樣本來要發送一個字節8位數據,而實際上我們一共發送了10位,多出來的兩位其中一位起始位,一位停止位。而接收方呢,原本一直保持的高電平,一旦檢測到來了一位低電平,那就知道了要開始準備接收數據了,接收到8位數據位后,然后檢測到停止位,再準備下一個數據的接收了。我們圖示看一下,如圖11-2所示。
3.JPG (27.66 KB, 下載次數: 325)
下載附件
2013-9-28 14:30 上傳
圖11-2 串口數據發送示意圖 像我們的圖11-2串口數據發送示意圖,實際上是一個時域示意圖,就是信號隨著時間變化的對應關系。比如在單片機的發送引腳上,左邊的是先發生的,右邊的是后發生的,數據位的切換時間就是波特率分之一秒,如果能夠理解時域的概念,后邊很多通信的時序圖就很容易理解了。 11.2 串行RS232通信接口在我們的臺式電腦上,有一個9針的串行接口,這個串行接口叫做RS232接口,它和UART通信有關聯,但是由于現在筆記本電腦都不帶這種9針串口了,所以和單片機通信越來越趨向于使用USB虛擬的串口和單片機通信,因此這一節的內容作為了解內容,大家知道有這么回事就行。 我們先來認識一下這個標準串口,串口分為9針的和9孔的,習慣上我們也稱之為公頭和母頭,如圖11-3所示。
4.JPG (57.46 KB, 下載次數: 329)
下載附件
2013-9-28 14:30 上傳
圖11-3 RS232通信接口 RS232接口一共有9個引腳,分別定義是:1、載波檢測(DCD);2、接收數據(RXD);3、發送數據(TXD);4、數據終端準備好(DTR);5、信號地線(SG);6、數據準備好(DSR);7、請求發送(RTS);8、清除發送(CTS);9、振鈴提示(RI)。我們要讓這個串口和我們單片機進行通信,我們只需要關心其中的2腳(RXD),3腳(TXD)和5腳(GND)。 雖然這三個腳的名字和我們單片機上的串口名字一樣,但是卻不能直接和單片機對連直接通信,這是為什么呢?隨著我們了解的內容越來越多,我們得慢慢知道,不是所有的電路都是5V代表高電平而0V代表低電平的。對于RS232標準來說,它是個反邏輯,也叫做負邏輯。為何叫負邏輯?它的TXD和RXD的電壓,-3V到-15V代表是1,3-15V之間的電壓代表是0。低電平代表的是1,而高電平代表的是0,所以稱之為負邏輯。因此電腦的9針232串口是不能和單片機直接連接的,需要用一個轉換芯片MAX232來完成,如圖11-4所示。
5.JPG (98.73 KB, 下載次數: 297)
下載附件
2013-9-28 14:30 上傳
圖11-4 MAX232轉接圖 這個芯片就可以實現把標準RS232串口電平轉換成我們單片機能夠識別和承受的UART 0V/5V電平標準。從這里大家似乎慢慢有點明白了,其實RS232串口和UART串口,他們的協議類型是一樣,只是電平不同而已,而MAX232這個芯片起到的就是中間人的作用,他把UART電平轉換成RS232電平,也把RS232電平轉換成UART電平,從而實現標準RS232接口和單片機UART之間的通信連接。 11.3 USB轉串口通信隨著技術的發展,工業上還有RS232串口通信的大量使用,但是商業技術的應用上,已經慢慢的使用USB轉UART技術取代了RS232串口,絕大多數筆記本電腦已經沒有串口這個東西了,那我們要實現單片機和電腦之間的通信該如何辦呢? 我們只需要在我們電路上添加一個USB轉串口芯片,就可以成功實現USB通信協議和標準UART串行通信協議的轉換,在我們的開發板上,我們使用的是CH340T這個芯片,如圖11-5所示。
6.JPG (75.64 KB, 下載次數: 348)
下載附件
2013-9-28 14:30 上傳
圖11-5 USB轉串口電路 左側J2是一組跳線的組合,大家可以在我們板子左下角的跳線位置找到,我們是把3腳和5腳、4腳和6腳通過跳線帽短接到一起。右側的CH340T這個電路很簡單,把電源電路,晶振電路接好后,6腳和7腳的DP和DM分別接USB口的2個數據引腳上去,3腳和4腳通過跳線接到了我們單片機的TXD和RXD上去。 CH340T的電路里3腳位置加了個4148的二極管,是一個小技巧。因為我們的STC89C52RC這個單片機下載程序需要冷啟動,就是先點下載后上電,上電瞬間單片機會先檢測需要不需要下載程序。雖然單片機的VCC是由開關來控制,但是由于CH340T的3腳是輸出引腳,如果沒有此二極管,開關后級單片機在斷電的情況下,CH340T的3腳和單片機的P3.0(即RXD)引腳連在一起,有電流會通過這個引腳流入后級電路并且給后級的電容充電,造成后級有一定幅度的電壓,這個電壓值雖然只有兩三伏左右,但是可能會影響到我們的冷啟動。加了二極管后,一方面不影響通信,另外一個方面還可以消除這種問題。這個地方可以暫時作為了解,大家如果自己做這塊電路,可以參考一下。 11.4 IO口模擬UART串口通信為了讓大家充分理解UART串口通信的原理,我們先用P3.0和P3.1這兩個當做IO口來進行模擬實際串口通信的過程,原理搞懂后,我們再使用寄存器配置實現串口通信過程。 對于 UART串口波特率,常用的值是300、600、1200、2400、4800、9600、14400、19200、28800、38400、57600、115200、128000、256000等速率。IO口模擬UART串行通信程序是一個簡單的演示程序,我們使用串口調試助手下發一個數據,數據加1后,再自動返回。串口調試助手,在我們進行全板子測試視頻的時候,大家已經見過,這里我們直接使用STC-ISP軟件自帶的串口調試助手,可到www.zg4o1577.cn去下載此程序,先把串口調試助手使用給大家說一下,如圖11-6所示。第一步要選擇串口助手菜單,第二步選擇十六進制顯示,第三步選擇十六進制發送,第四步選擇COM口,這個COM口要和自己電腦設備管理器里的那個COM口一致,波特率是我們程序設定好的選擇,我們程序中讓一個數據位持續時間是1/9600秒,那這個地方選擇波特率就是選9600,校驗位選N,數據位8,停止位1。
7.JPG (178.6 KB, 下載次數: 316)
下載附件
2013-9-28 14:31 上傳
圖11-6 串口調試助手示意圖 串口調試助手的實質就是我們利用電腦上的UART通信接口,通過這個UART接口發送數據給我們的單片機,也可以把我們的單片機發送的數據接收到這個調試助手界面上。 因為初次接觸通信方面的技術,所以我對這個程序進行一下解釋,大家可以邊看我的解釋邊看程序,把底層原理先徹底弄懂。 變量定義部分就不用說了,直接看main主函數。首先是對通信的波特率的設定,在這里我們配置的波特率是9600,那么串口調試助手也得是9600。配置波特率的時候,我們用的是定時器0的模式2。模式2中,不再是TH0代表高8位,TL0代表低8位了,而只有TL0在進行計數了。當TL0溢出后,不僅僅會讓TF0變1,而且還會將TH0中的內容重新自動裝到TL0中。這樣有一個好處,我們可以把我們想要的定時器初值提前存在TH0中,當TL0溢出后,TH0自動把初值就重新送入TL0了,全自動的,不需要程序上再給TL0重新賦值了,配置方式很簡單,大家可以自己看下程序并且計算一下初值。 波特率設置好以后,打開中斷,然后等待接收串口調試助手下發的數據。接收數據的時候,首先要進行低電平檢測 while (PIN_RXD),若沒有低電平則說明沒有數據,一旦檢測到低電平,就進入啟動接收函數StartRXD()。接收函數最開始啟動半個波特率周期,初學可能這里不是很明白。大家回頭看一下我們的圖11-2里邊的串口數據示意圖,信號在數據位電平變化的時候去讀,因為時序上的誤差以及信號穩定性的問題很容易讀錯數據,所以我們希望在信號最穩定的時候去讀數據。除了信號變化的那個沿的位置外,其他位置都很穩定,那么我們現在就約定在信號中間位置去讀取電平狀態,這樣能夠保證我們信號讀的是對的。 一旦讀到了起始信號,我們就把當前狀態設定成接受狀態,并且打開定時器中斷,第一次是半個周期進入中斷后,對起始位進行二次判斷一下,確認一下起始位是低電平,而不是一個干擾信號。以后每經過9600分之一秒進入一次中斷,并且把這個引腳的狀態讀到RxdBuf里邊。等待接收完畢之后,我們再把這個RxdBuf加1,再通過TXD引腳發送出去,同樣需要先發一位起始位,然后發8個數據位,再發結束位,發送完畢后,程序運行到while (PIN_RXD),等待第二輪信號接收的開始。 #include <reg52.h> sbit PIN_RXD = P3^0; //接收引腳定義 sbit PIN_TXD = P3^1; //發送引腳定義 bit RxdOrTxd = 0; //指示當前狀態為接收還是發送 bit RxdEnd = 0; //接收結束標志 bit TxdEnd = 0; //發送結束標志 unsigned char RxdBuf = 0; //接收緩沖器 unsigned char TxdBuf = 0; //發送緩沖器 void ConfigUART(unsigned int baud); void StartTXD(unsigned char dat); void StartRXD(); void main () { ConfigUART(9600); //配置波特率為9600 EA = 1; //開總中斷
while(1) { while (PIN_RXD); //等待接收引腳出現低電平,即起始位 StartRXD(); //啟動接收 while (!RxdEnd); //等待接收完成 StartTXD(RxdBuf+1); //接收到的數據+1后,發送回去 while (!TxdEnd); //等待發送完成 } } void ConfigUART(unsigned int baud) //串口配置函數,baud為波特率 { TMOD &= 0xF0; //清零T0的控制位 TMOD |= 0x02; //配置T0為模式2 TH0 = 256 - (11059200/12) / baud; //計算T0重載值 } void StartRXD() //啟動串行接收 { TL0 = 256 - ((256-TH0) >> 1); //接收啟動時的T0定時為半個波特率周期 ET0 = 1; //使能T0中斷 TR0 = 1; //啟動T0 RxdEnd = 0; //清零接收結束標志 RxdOrTxd = 0; //設置當前狀態為接收 } void StartTXD(unsigned char dat) //啟動串行發送,dat為待發送字節數據 { TxdBuf = dat; //待發送數據保存到發送緩沖器 TL0 = TH0; //T0計數初值為重載值 ET0 = 1; //使能T0中斷 TR0 = 1; //啟動T0 PIN_TXD = 0; //發送起始位 TxdEnd = 0; //清零發送結束標志 RxdOrTxd = 1; //設置當前狀態為發送 } void InterruptTimer0() interrupt 1 //T0中斷服務函數,處理串行發送和接收 { static unsigned char cnt = 0; //bit計數器,記錄當前正在處理的位 if (RxdOrTxd) //串行發送處理 { cnt++; if (cnt <= 8) //低位在先依次發送8bit數據位 { PIN_TXD = TxdBuf & 0x01; TxdBuf >>= 1; } else if (cnt == 9) //發送停止位 { PIN_TXD = 1; } else //發送結束 { cnt = 0; //復位bit計數器 TR0 = 0; //關閉T0 TxdEnd = 1; //置發送結束標志 } } else //串行接收處理 { if (cnt == 0) //處理起始位 { if (!PIN_RXD) //起始位為0時,清零接收緩沖器,準備接收數據位 { RxdBuf = 0; cnt++; } else //起始位不為0時,中止接收 { TR0 = 0; //關閉T0 } } else if (cnt <= 8) //處理8位數據位 { RxdBuf >>= 1; //低位在先,所以將之前接收的位向右移 if (PIN_RXD) //接收腳為1時,緩沖器最高位置1;為0時不處理即仍保持移位后的0 { RxdBuf |= 0x80; } cnt++; } else //停止位處理 { cnt = 0; //復位bit計數器 TR0 = 0; //關閉T0 if (PIN_RXD) //停止位為1時,方能認為數據有效 { RxdEnd = 1; //置接收結束標志 } } } } 同學們通過學習我們的程序,也慢慢感受到了,程序的延時部分已經不再使用簡單的delay來完成了,我們要通過我們的程序編寫積累,慢慢提高自己靈活運用定時器的能力。一個小小的定時器,可以幫我們完成很多很多工作。 11.5 UART串口通信的基本應用11.5.1 通信的三種基本類型我們常用的通信通常可以分為單工、半雙工、全雙工通信。 單工就是指只允許一方向另外一方傳送信息,而另一方不能回傳信息。比如我們的電視遙控器,我們的收音機廣播等,都是單工通信技術。 半雙工是指數據可以在雙方之間相互傳播,但是同一時刻只能其中一方發給另外一方,比如我們的對講機就是典型的半雙工。 全雙工通信就發送數據的同時也能夠接受數據,兩者同步進行,就如同我們的電話一樣,我們說話的同時也可以聽到對方的聲音。 11.5.2 UART模塊介紹IO口模擬串口通信,大家了解了串口通信的實質,但是我們的單片機程序卻需要不停的檢測掃描單片機IO口收到的數據,大量占用了CPU資源。這時候就會有聰明人想了,其實我們不是很關心通信的過程,我們只需要一個通信的結果,最終得到接收到的數據就行了。這樣我們可以在單片機內部做一個硬件模塊,讓他自動接收數據,接收完了,通知我們一下就可以了,我們的51單片機內部就存在這樣一個UART模塊,要正確使用它,當然還得先把對應的特殊功能寄存器配置好。 51單片機的UART串行口的結構由串行口控制寄存器SCON、發送和接收電路三部分構成,先來了解一下串口控制寄存器SCON。 表11-1 SCON--串行控制寄存器的位分配(地址:98H) 可位尋址;復位值:0x00;復位源:任何復位 位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 符號 | SM0 | SM1 | SM2 | REN | TB8 | RB8 | TI | RI |
表11-2 SCON--串行控制寄存器的位描述 | | | | | 這兩位共同決定了串口通信的模式0到模式3共4種模式。我們最常用的就是模式1,也就是SM0=0,SM1=1,下邊我們重點就講模式1,其他模式從略。 | | | | | | | | 使能串行接收。由軟件置位使能接收,軟件清零則禁止接收 | | | | | | 模式2和3中接收第9位數據(很少用),模式1用來接收停止位 | | | 發送中斷標志位,模式1下,在數據位最后一位發送結束,開始發送停止位時由硬件自動置1,必須通過軟件清零。也就是說,再發送前我們清零TI,發送數據,數據發送到停止位時,TI硬件置1,方便我們CPU查詢發送完畢狀態。 | | | 接收中斷標志位,當接收電路接收到停止位的中間位置時,RI由硬件置1。也就是說,接收數據之前我們必須清零RI,接受數據到停止位的中間位置時,RI硬件置1,方便我們CPU查詢到接收狀態。 |
前邊學了那么多寄存器的配置,相信SCON這個地方,對于大多數同學來說已經不是難點了,應該能看懂并且可以自己配置了。對于串口的四種模式,模式1是最常用的,就是我們前邊提到的1位起始位,8位數據位和1位結束位。因為我們的教程不同于教科書,只要有的功能都一一介紹,我們只介紹實用的技術,所以其他3種模式,真正遇到需要使用的時候大家再去查資料就行。 在我們使用IO口模擬串口通信的時候,我們串口的波特率是使用定時器0的中斷體現出來的。在實際串口模塊中,有一個專門的波特率發生器用來控制發送數據的速度和讀取接收數據的速度。對于STC89C52RC單片機來講,這個波特率發生器只能由定時器1或定時器2產生,而不能由定時器0產生,這和我們模擬的通信是完全不同的概念。 如果用定時器2,需要配置額外的寄存器,默認是使用定時器1的,我們本章內容主要是使用定時器1作為波特率發生器來講解,方式1下的波特率發生器必須使用定時器1的模式2,也就是自動重裝載模式,定時器的初值具體的計算公式是: TH1 = TL1 = 256 - 晶振值/12 /2/16 /波特率 和波特率有關的還有一個寄存器,是一個電源管理寄存器PCON,他的最高位可以把波特率提高一倍,也就是如果寫PCON |=0x80以后,計算公式就成了 TH1 = TL1 = 256 - 晶振值/12 /16 /波特率 數字的含義這里解釋一下,256是8位數據的溢出值,也就是TL1的溢出值,11059200就是我們板子上單片機的晶振,12是說1個機器周期是12個時鐘周期,值得關注的是這個16,重點說明。我們在IO口模擬串口通信接收數據的時候,我們采集的是這一位數據的中間位置,而實際上串口模塊比我們模擬的要復雜和精確一些。他采取的方式是把一位信號采集16次,其中第7、8、9次取出來,這三次中其中兩次如果是高電平,那么就認定這一位數據是1,如果兩次是低電平,那么就認定這一位是0,這樣一旦受到意外干擾讀錯一次數據,也依然可以保證最終數據的正確性。 了解了串口采集模式,在這里要給大家留一個思考題。“晶振值/12/2/16/波特率”這個地方計算的時候,出現不能除盡,或者出現小數怎么辦,允許出現多大的偏差?把這部分理解了,也就理解了我們的晶振為何使用11.0592M了。 串口通信的發送和接收電路,我們主要了解一下他們在物理上有2個名字相同的SBUF寄存器,他們的地址也都是99H,但是一個用來做發送緩沖,一個用來做接收緩沖。意思就是說,有2個房間,兩個房間的門牌號是一樣的,其中一個只出人不進人,另外一個只進人不出人,這樣的話,我們就可以實現UART的全雙工通信,相互之間不會產生干擾。但是在邏輯上呢,我們每次只操作SBUF,單片機會自動根據對它執行的是“讀”還是“寫”操作來選擇是接收SBUF還是發送SBUF,后邊通過程序,我們就會徹底了解這個問題。 11.5.3 UART串口程序一般情況下,我們編寫串口通信程序的基本步驟如下所示: 1、配置串口為模式1。 2、配置定時器T1為模式2,即自動重裝模式。 3、確定波特率大小,計算定時器TH1和TL1的初值,如果有需要可以使用PCON進行波特率加倍。 4、打開定時器控制寄存器TR1,讓定時器跑起來。 這個地方還要特別注意一下,就是在使用T1做波特率發生器的時候,千萬不要再使能T1的中斷了。 我們先來看一下由IO口模擬串口通信直接改為使用硬件UART模塊時程序代碼,看看程序是不是簡單了很多,因為大部分的工作硬件模塊都替我們做了。程序功能和IO口模擬的是完全一樣的。 #include <reg52.h> void ConfigUART(unsigned int baud); void main () { ConfigUART(9600); //配置波特率為9600
while(1) { while (!RI); //等待接收完成 RI = 0; //清零接收中斷標志位 SBUF = SBUF + 1; //接收到的數據+1后,發送回去; //等號左邊的SBUF實際上就是發送SBUF,因為對它的操作是“寫”; //等號右邊的是接收SBUF,因為對它的操作是“讀”。 while (!TI); //等待發送完成 TI = 0; //清零發送中斷標志位 } } void ConfigUART(unsigned int baud) //串口配置函數,baud為波特率 { SCON = 0x50; //配置串口為模式1 TMOD &= 0x0F; //清零T1的控制位 TMOD |= 0x20; //配置T1為模式2 TH1 = 256 - (11059200/12/32) / baud; //計算T1重載值 TL1 = TH1; //初值等于重載值 ET1 = 0; //禁止T1中斷 TR1 = 1; //啟動T1 } 當然了,這個程序還是在主循環里等待接收中斷標志位和發送中斷標志位的方法來編寫的,而實際工程開發中,當然就不能這么干了,所以就用到了串口中斷,來看一下程序。 #include <reg52.h> void ConfigUART(unsigned int baud); void main () { ConfigUART(9600); //配置波特率為9600
while(1); } void ConfigUART(unsigned int baud) //串口配置函數,baud為波特率 { SCON = 0x50; //配置串口為模式1 TMOD &= 0x0F; //清零T1的控制位 TMOD |= 0x20; //配置T1為模式2 TH1 = 256 - (11059200/12/32) / baud; //計算T1重載值 TL1 = TH1; //初值等于重載值 ET1 = 0; //禁止T1中斷 TR1 = 1; //啟動T1 ES = 1; //打開串口中斷 EA = 1; //打開總中斷 } void InterruptUART() interrupt 4 { if (RI) //接收到字節 { RI = 0; //手動清零接收中斷標志位 SBUF = SBUF + 1;//接收數據+1發回去,左邊為發送SBUF,右邊為接收SBUF。 } if (TI) //字節發送完畢 { TI = 0; //手動清零發送中斷標志位 } } 大家可以試驗一下試試,看看是不是和前邊用IO口模擬通信實現的效果一致,而主循環卻完全空出來了,我們就可以隨意添加其它功能代碼進去。 11.6 字符和數據之間的轉換我們學串口通信的應用主要是實現單片機和電腦之間的信息互發,可以用電腦控制單片機的一些信息,可以把單片機的一些信息狀況發給電腦上的軟件。下面我們就做一個簡單的例程,實現單片機串口調試助手發送的數據,在我們開發板上的數碼管上顯示出來。 #include <reg52.h> sbit ADDR3 = P1^3; //LED選擇地址線3 sbit ENLED = P1^4; //LED總使能引腳 unsigned char code LedChar[] = { //數碼管顯示字符轉換表 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E }; unsigned char LedBuff[6] = { //數碼管 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; unsigned char T0RH = 0; //T0重載值的高字節 unsigned char T0RL = 0; //T0重載值的低字節 unsigned char RxdByte = 0; //串口接收到的字節 void ConfigTimer0(unsigned int ms); void ConfigUART(unsigned int baud); void main () { P0 = 0xFF; //P0口初始化 ADDR3 = 1; //選擇數碼管 ENLED = 0; //LED總使能 EA = 1; //開總中斷 ConfigTimer0(1); //配置T0定時1ms ConfigUART(9600); //配置波特率為9600
while(1) { //將接收字節在數碼管上以十六進制形式顯示出來 LedBuff[0] = LedChar[RxdByte & 0x0F]; LedBuff[1] = LedChar[RxdByte >> 4]; } } void ConfigTimer0(unsigned int ms) //T0配置函數 { unsigned long tmp;
tmp = 11059200 / 12; //定時器計數頻率 tmp = (tmp * ms) / 1000; //計算所需的計數值 tmp = 65536 - tmp; //計算定時器重載值 tmp = tmp + 31; //修正中斷響應延時造成的誤差
T0RH = (unsigned char)(tmp >> 8); //定時器重載值拆分為高低字節 T0RL = (unsigned char)tmp; TMOD &= 0xF0; //清零T0的控制位 TMOD |= 0x01; //配置T0為模式1 TH0 = T0RH; //加載T0重載值 TL0 = T0RL; ET0 = 1; //使能T0中斷 TR0 = 1; //啟動T0 } void ConfigUART(unsigned int baud) //串口配置函數,baud為波特率 { SCON = 0x50; //配置串口為模式1 TMOD &= 0x0F; //清零T1的控制位 TMOD |= 0x20; //配置T1為模式2 TH1 = 256 - (11059200/12/32) / baud; //計算T1重載值 TL1 = TH1; //初值等于重載值 ET1 = 0; //禁止T1中斷 ES = 1; //使能串口中斷 TR1 = 1; //啟動T1 } void LedScan() //LED顯示掃描函數 { static unsigned char index = 0;
P0 = 0xFF; //關閉所有段選位,顯示消隱 P1 = (P1 & 0xF8) | index; //位選索引值賦值到P1口低3位 P0 = LedBuff[index]; //相應顯示緩沖區的值賦值到P0口 if (index < 5) //位選索引0-5循環,因有6個數碼管 index++; else index = 0; } void InterruptTimer0() interrupt 1 //T0中斷服務函數 { TH0 = T0RH; //定時器重新加載重載值 TL0 = T0RL; LedScan(); //LED掃描顯示 } void InterruptUART() interrupt 4 { if (RI) //接收到字節 { RI = 0; //手動清零接收中斷標志位 RxdByte = SBUF; //接收到的數據保存到接收字節變量中 SBUF = RxdByte; //接收到的數據又直接發回,這叫回顯-"echo",以提示用戶輸入的信息是否已正確接收 } if (TI) //字節發送完畢 { TI = 0; //手動清零發送中斷標志位 } } 大家在做這個實驗的時候,有個小問題要注意一下。因為我們STC89C52RC下載程序是使用了UART串口下載,下載完程序后,程序運行起來了,可是下載軟件最后還會通過串口發送一些額外的數據,所以程序剛下載進去不是顯示00,而可能是其他數據。大家只要把開關關閉,重新打開一次就好了。 細心的同學可能會發現,在串口調試助手發送選項和接收選項處,還有個“字符格式發送”和“字符格式顯示”,這是什么意思呢? 先拋開我們使用的漢字不談,那么我們常用的字符就包含了0~9的數字、A~Z/a~z的字母、還有各種標點符號等。那么在單片機系統里面我們怎么來表示它們呢?ASCII碼(American Standard Code for Information Interchange,即美國信息互換標準代碼)可以完成這個使命:我們知道,在單片機中一個字節的數據可以有0~255共256個值,我們取其中的0~127共128個值賦予了它另外一層涵義,即讓它們分別來代表一個常用字符,其具體的對應關系如下表。 表11-3 ASCII表 這樣我們就在常用字符和字節數據之間建立了一一對應的關系,那么現在一個字節就既可以代表一個整數又可以代表一個字符了,但它本質上只是一個字節的數據,而我們賦予了它不同的涵義,什么時候賦予它那種涵義就看編程者的意圖了。ASCII碼在單片機系統中應用非常廣泛,我們后續的課程也會經常使用到它,下面我們來對它做一個直觀的認識,同學們一定要深刻理解其本質。 對照上述表格,我們就可以實現字符和數字之間的轉換了,比如還是這個程序,我們發送的時候改成字符格式發送,接收還是用十六進制接收,這樣接收和數碼管好做一下對比。 我們用字符格式發送一個小寫的a,返回一個十六進制的0x61,數碼管上顯示的也是61,ASCII碼表里字符a對應十進制是97,等于十六進制的0x61;我們再用字符格式發送一個數字1,返回一個十六進制的0x31,數碼管上顯示的也是31,ASCII表里字符1對應的十進制是49,等于十六進制的0x31。這下大家就該清楚了:所謂的十六進制發送和十六進制接收,都是按字節數據的真實值進行的;而字符格式發送和字符格式接收,是按ASCII碼表中字符形式進行的,但它實際上最終傳輸的還是一個字節數據。這個表格,當然不需要大家去記住,理解它,用的時候過來查就行了。 通信的學習,不像前邊控制部分那么直觀了,通信部分我們的程序只能獲得一個結果,而其過程我們卻無法直接看到,所以慢慢的可能大家就會知道有示波器和邏輯分析儀這類測量儀器。如果學校實驗室或者公司里有示波器或者邏輯分析儀這類儀器,可以拿過來抓一下串口波形,直觀的了解一下。如果暫時還沒有這些儀器,先知道這么回事,有條件再說。因為工具類的東西有的比較昂貴,有條件可以盡量使用學校或者公司的。在這里我用一款簡易的邏輯分析儀把串口通信的波形抓出來給大家看一下,大家了解一下即可,如圖11-7所示。
8.JPG (44.15 KB, 下載次數: 350)
下載附件
2013-9-28 14:31 上傳
9.JPG (9.21 KB, 下載次數: 282)
下載附件
2013-9-28 14:31 上傳
圖11-7 邏輯分析儀串口數據示意圖 分析儀和示波器的作用,就是把通信過程的波形抓出來進行分析。先大概說一下波形的意思。波形左邊是低位,右邊是高位,上邊這個波形是電腦發送給單片機的,下邊這個波形是單片機回發給電腦的。以上邊的波形為例,左邊第一位是起始位0,從低位到高位依次是10001100,順序倒一下,就是數據0x31,也就是ASCII碼表里的‘1’。大家可以注意到分析儀在每個數據位都給標了一個白色的點,表示是數據,起始位和無數據的時候都沒有這個白點。時間標T1和T2的差值在右邊顯示出來是0.102ms,大概是9600分之一,稍微有點偏差,在容許范圍內即可。通過圖11-7,我們可以清晰的了解了串口通信的收發的詳細過程。 那我們這里再來了解一下,如果我們使用串口調試助手,用字符格式直接發送一個“12”,我們在我們的數碼管上應該顯示什么呢?串口調試助手應該返回什么呢?經過試驗發現,我們數碼管顯示的是32,而串口調試助手返回十六進制顯示的是31、32兩個數據,如圖11-8所示。
10.JPG (48.28 KB, 下載次數: 330)
下載附件
2013-9-28 14:33 上傳
圖11-8 串口調試助手數據顯示 我們用邏輯分析儀把這個數據抓出來看一下,如圖11-9所示。
11.JPG (35.58 KB, 下載次數: 311)
下載附件
2013-9-28 14:33 上傳
圖11-9 邏輯分析儀抓取數據 對于ASCII碼表來說,數字本身是字符而非數據,所以如果發送“12”的話,實際上是是分別發送了“1”和“2”兩個字符,單片機呢,先收到第一個字符“1”,在數碼管上會顯示出31這個對應數字,但是瞬間馬上就又收到了“2”這個字符,數碼管瞬間從31變成了32,而我們視覺上呢,根本是沒有辦法發現這種快速變化的,所以我們感覺數碼管直接顯示的是32。 11.7 作業1、能夠理解UART串口通信的基本原理和通信過程。 2、通過IO口模擬UART串口通信把通信的底層操作原理弄明白。 3、學會通過配置寄存器,實現串口通信的基本操作過程。 4、了解字符和數據之間的轉換依據和方法。 5、完成通過串口控制流水燈流動和停止的程序。 6、完成通過串口實現蜂鳴器響的程序。
|