STC89C52通過串口控制流水燈亮滅
1.只是控制LED的亮滅,不返回數(shù)值
本文引用地址:http://m.butianyuan.cn/article/201611/318684.htm2.控制LED的亮滅,并返回數(shù)值
看了幾講的視頻,都是在講串口的方式1。其他的還沒接觸,這里也只用串口的方式1實現(xiàn)這兩個功能。串口里面需要計算的地方是根據(jù)所要使用的波特率求定時器的初始值。定時器使用的是方式2,可以自動裝初始值,避免賦值語句裝初始值時出現(xiàn)誤差。
以9800bps,定時器使用方式2,串口使用方式1,晶振波特率為 11.0592MHZ,求TH1跟TL1的值。
方式1的波特率 = (2^SMOD/32)xT1溢出率。單片機復位后,電源管理寄存器PCON全部清零,SMOD作為其中一位自然也清零。
波特率已經(jīng)知道了。這樣就剩下T1溢出率了。
假設(shè)初值為X,則定時器每次計256-X個數(shù)溢出一次(定時器為8位,最大為255 。256時發(fā)生溢出)。每計一個數(shù)的時間為一個機器周期,機器周期 = T時鐘周期 X 12 。于是溢出的時間為 = 個數(shù)X 每個時間 = ( 256 - X) * 12/Fosc . 那么基礎(chǔ)率就是溢出時間的倒數(shù)。
于是結(jié)合公式“方式1的波特率 = (2^SMOD/32)xT1溢出率”,式子可以總結(jié)為:
9600 = 2^0 /32 * Fosc / (256 - X)*12 帶入全部已知數(shù)據(jù)得到 9600 = 2^0 /32 * 11059200/ (256 - X)*12 =====》》》》 求得的X為: 253 .
在此基礎(chǔ)上,如果把SMOD 設(shè)為1 ,則 求得波特率為 :
波特率 = (2^1/32) * 11059200 / (256 - 253 ) = 2 * [ 1/32 * 11059200 / (256 - 253)] = 2 * 9600 = 19200 。即變?yōu)樵瓉淼膬杀丁?/p>
如果把晶振換成12MHZ再求初值,求得的X為: 252.744792…… 無窮小數(shù)。這樣就會產(chǎn)生誤差。以前一直感覺整數(shù)的晶振挺好,現(xiàn)在才知道為什么會有11.0592MHZ這種晶振的存在了,。
這樣計算得到了初值,下面貼代碼。
只是控制LED的亮滅,不返回數(shù)值
實現(xiàn)這個又分為查詢和中斷兩種方法。
A。先用查詢。感覺叫判斷更好些,因為是用if判斷來實現(xiàn)的
#includeB 中斷法void main(){ //設(shè)置參數(shù)TMOD = 0x20; //設(shè)定定時器1的工作方式為方式2TH1 = 0xfd;TL1 = 0xfd; //裝載TH1、TL1TR1 = 1; //啟動定時器1REN = 1; //允許串行接收位SM0 = 0;SM1 = 1; //設(shè)定串口工作方式為方式1/** EA = 1; //全局中斷允許位* ES = 1; //串口中斷允許位* 此處使用的是查詢法判斷接收中斷標志位,所以即便不開啟中斷允許位,也可以**/while(1){ //查詢法檢測RIif(RI == 1)//RI為接收中斷標志位。硬件置為1,必須軟件清0{P1 = SBUF;RI = 0;} }}
#includevoid main(){ //設(shè)置參數(shù)TMOD = 0x20; //設(shè)定定時器1的工作方式為方式2TH1 = 0xfd;TL1 = 0xfd; //裝載TH1、TL1TR1 = 1; //啟動定時器1REN = 1; //允許串行接收位SM0 = 0;SM1 = 1; //設(shè)定串口工作方式為方式1EA = 1; //全局中斷允許位ES = 1; //串口中斷允許位while(1) ; //等待中斷的發(fā)生}//中斷檢測RIvoid ser() interrupt 4{P1 = SBUF;RI = 0;}
這兩個除了代碼,感覺就是是否開啟中斷允許了。因為RI置為1是硬件自動執(zhí)行的。即便是不開啟中斷允許位,照樣可以用if進行判斷。
上面這兩個是單方向的,再來個雙向的。
/**通過串口給下位機發(fā)送數(shù)據(jù),并使之顯示在P1口的流水燈上。*同時單片機返回接收到的數(shù)據(jù),顯示在串口助手上*/#includeunsigned char flag;void main(){ //設(shè)置參數(shù)TMOD = 0x20; //設(shè)定定時器1的工作方式為方式2TH1 = 0xfd;TL1 = 0xfd; //裝載TH1、TL1TR1 = 1; //啟動定時器1SM0 = 0;SM1 = 1; //設(shè)定串口工作方式為方式1REN = 1; //允許串行接收位EA = 1; //全局中斷允許位ES = 1; //串口中斷允許位while(1){ /* 剛開始單片機緩沖寄存器為空,無數(shù)據(jù)可以顯示* 先從串口接收數(shù)據(jù),再返回該數(shù)據(jù)* 在中斷中接收數(shù)據(jù),同時將flag標志位置為1.說明接收到了數(shù)據(jù)* 若接收到數(shù)據(jù)(flag == 1),說明接收到了;否則說明未接收到數(shù)據(jù),不顯示。繼續(xù)判斷flag數(shù)值*/if(flag == 1) { //發(fā)送數(shù)據(jù)ES = 0; //關(guān)閉串口中斷,發(fā)送數(shù)據(jù)SBUF = P1; //數(shù)據(jù)寫入SBUF寄存器while(!TI); //等待TI = 0;ES = 1; flag = 0;} }}void ser() interrupt 4{//接收數(shù)據(jù)P1 = SBUF;flag = 1;RI = 0;}
主函數(shù)里面那個flag = 0 。 一定不能少了。否則只要一小會兒的功能,串口助手就卡了。。。。
這個例子里還有兩條語句比較關(guān)鍵:
P1 = SBUF;//把SBUF寄存器中的數(shù)值賦給P1
SBUF = P1;//把P1的數(shù)值寫入到SBUF
SBUF是這么寫的:SBUF 串行數(shù)據(jù)緩沖寄存器,一個發(fā)送緩沖寄存器,一個接收緩沖寄存器。兩個公用一個地址99H,但在物理上是兩個獨立的寄存器。那么如何區(qū)分是發(fā)送還是接收呢?就用語句來區(qū)分了。
控制流水燈的話,需要發(fā)送十六進制格式的。
比如發(fā)送FB(1111,1011)。在我的開發(fā)板上是L2燈亮。如果發(fā)送字符,就不太好控制了。如果用2中的例程,以字符方式發(fā)送“fb”,單片機返回串口助手并用十六進制顯示為“62” .這個,嗯,目前不會算 :P
沒啥值得紀念的圖片,還是幾個流水燈。不過此時的流水燈,非彼時的流水燈?,F(xiàn)在的流水燈,可是我從電腦上就能控制開發(fā)板上的了:D
只是不知道下次自己寫個上位機是什么時候了,
評論