由數碼管動態(tài)顯示淺談單片機程序
傳統的數碼管顯示程序為:
本文引用地址:http://butianyuan.cn/article/201611/323870.htm#define DUAN P1
#define WEI P0
void delayms(uchar x)
{
uchar y=120;
while(x--)
while(y--);
}
WEI=0; //消影 共陽,共陰為 WEI=0xff
DUAN=code[value_duan1]; //送段選數據
WEI=value_wei1; //送位選數據,確定第幾個數碼管點亮
Delayms(5); //延時5ms使其顯示穩(wěn)定
WEI=0; //消影 共陽,共陰為 WEI=0xff
DUAN=code[value_duan2]; //送段選數據
WEI= value_wei2; //送位選數據,確定第幾個數碼管點亮
Delayms(5); //延時5ms使其顯示穩(wěn)定
當延時后依次再送下一個數據,再延時······
這里我想再次說一下關于延時的問題。一般教科書或者說目前絕大多數能看到的數碼管處理程序資料大多都是按照上面的方式處理的。我想問一下,這里延時5ms的意義何在?可否不延時?答案是可以,但顯示結果就是最后一個被點亮的數碼管會比較亮,其余的都比較暗。至于原因很簡單,點亮最后一個數碼管后,單片機CPU還要跑其他程序,然后再次跑到數碼管顯示處理函數時再依次點亮第一第二個數碼管。顯然這樣最后一個被點亮的數碼管點亮的時間遠遠比其他的數碼管時間要長,自然這一個特別亮,其余的很暗淡。然而加上5ms延時的話呢?由于單片機速度還算比較快(一般一條指令1us),5ms相當于5000條指令。5000條指令什么概念呢?怎么說呢,若程序里不用“delay”這樣的空指令的話,一般一個大型項目就差不多了。一般而言,比如AD測溫、adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=17&is_app=0&jk=85d214d39ac24f75&k=%B5%E7%D7%D3%CA%B1%D6%D3&k0=%B5%E7%D7%D3%CA%B1%D6%D3&kdi0=0&luki=8&n=10&p=baidu&q=98059059_cpr&rb=0&rs=1&seller_id=1&sid=754fc29ad314d285&ssp2=1&stid=0&t=tpclicked3_hc&tu=u1831118&u=http%3A%2F%2Fwww%2E51hei%2Ecom%2Fmcu%2F2120%2Ehtml&urlid=0" id="16_nwl" mpid="16" target="_blank">電子時鐘等這樣小項目實際有用的指令絕對不會達到好幾千的樣子。所以說5ms對于單片機來說是個相當長的時間。
再回到延時5ms后顯示較為穩(wěn)定的問題上。因為點亮最后一個數碼管延時5ms后單片機在跑其他的指令時相對要不了多久(前提是其余地方沒有“delay”空指令),所以最后一個數碼管比其他數碼管多亮的時間就不太明顯了,這樣自然顯示也會比較均勻一些。但是事實上,最后一個被點亮的數碼管還是稍微比別的數碼管亮一些。注意上面所說的是單片機跑其他指令用的時間不長的情況。如果,程序比較大,模塊很多,單片機要處理的事情很多呢?比方說一個float型數據的除法(不知道讀者有沒有在單片機上試過)所耗費的指令數可能你都想象不到(尤其是對于內部不含硬件除法器單片機,其除法指令轉換成其他的運算)。我當時做AD測電阻時用的sonix芯片(沒有除法器),開始程序里面一個float型除法,整個程序當時有1.5k的樣子,數碼管顯示,老是一個亮,其余的暗淡,而且很不穩(wěn)定。后來查找原因,就是因為那條float型除法的問題,竟然占了大幾ms的時間。當刪除那條指令后,整個程序只有700多字節(jié),也就是說就這么一條指令就直接讓程序大小翻倍了!查看編譯器翻譯成的匯編指令占了相當大一部分,而且還有很多CALL指令。所以說,8位單片機 確實不適合做除法以及float運算。
扯遠了,回到數碼管問題。綜上面所述,delayms(5)的方式是不可靠的。而且最為關鍵的時delay指令是毫無意義的,就是讓單片機啥也不做,在那里死等。試想一下,單片機還要處理其他事情,光在這里死等豈不是太浪費了嗎?就好比人一樣我可以在吃飯的同時聽音樂,而不是先吃飯,飯吃完了再專門聽音樂。單片機也是一樣,要的是效率,而不是在那里死等。
那么這種方式不好,該怎么辦呢?要不要延時?延時肯定是需要的,不然顯示不均勻,但不是這種方式。正確的方式是定時器定時5ms,5ms到后刷新一次數碼管,這樣一來單片機不會在這里死等,二來數碼管顯示時間絕對均勻。對于定時器,一般的單片機至少有一個,而且它作為單片機的獨立模塊,根本不影響cpu工作。以3個數碼管為例,其程序代碼如下
#define DUAN P1 //宏定義段選
#define WEI P0 //宏定義位選
void timer(); //定時器處理函數,用定時器定時5ms
{
if(定時標志位置一)
{
定時標志清零;
If(T_5ms) //若T_5ms大于0,每5ms減1
T_5ms--;
}
}
if(T_5ms==2)
{
WEI=0; //消影 共陽,共陰為 WEI=0xff
DUAN=code[value_duan1];
WEI= value_wei1;
}
if(T_5ms==1)
{
WEI=0;
DUAN=code[value_duan1];
WEI= value_wei2;
}
if(T_5ms==0)
{
T_5ms=3; //計時寄存器重新賦值
WEI=0;
DUAN=code[value1];
WEI=value2;
}
這樣CPU不用在這里死等,每次程序跑到這里時,只需做個判斷就好了,5ms到后就進去點亮數碼管,否則就不進去,顯示效果絕對均勻。同理,諸如鍵盤掃描程序,延時消抖,都可以采用這種方式,而不用delay。
也許讀者可能發(fā)現了問題,對,還是有點問題。假若程序在其他地方耗費時間大于5ms的話,那么這里的定時5ms就失去了作用。確實是這樣的。實際上對于好程序來說,主循環(huán)絕對控制在1ms一下(CPU跑1Mhz,即1條指令1us)。也許你會問,有些模塊不可能1ms以內就完成啊。是的,有些時候某些模塊確實需要很久才能完成。這就涉及到程序分時分段處理的問題。好比人做事情一樣,我這件事今天做不完,但可以明天再做,而且同時今天我也要吃飯、睡覺做其他的事情,而不是說這件事沒做完,別的什么也不干了。單片機也是如此。不管在大的程序,都是分時分段處理的,不然CPU會崩潰的,CPU不可能同時把所有的都一起處理了,而是這段時間處理一點,下段時間再處理一點。這樣在總的時間上是一樣的,CPU完成的事情卻翻倍了。說道這里程序主循環(huán)控制在1ms絕對不成問題了。在試想一下,在程序里面到處delay是不是很可怕?比如按鍵,消抖可以delay 10ms,如果是長按鍵呢?難道要delay 3s或更長?所以說不管從功能上還是程序結構上,delay是絕對不可取的。對于delay,幾個us(相當于幾個nop指令)還是允許的。
說到這里,程序分時分段思想已經很明白了。雖然說一個大型項目,包括很多模塊,比如按鍵、AD采樣、數碼管顯示(或lcd顯示)、PWM輸出、UART、IIC通訊等等,但是并不是說每個模塊都在同一時間完成。比如按鍵按一下大概幾百毫秒的樣子,長按好幾秒的樣子,主程序不是一直在這里等,而是一遍又一遍的循環(huán)掃描,當掃描的按鍵鍵值變化而且連續(xù)在100ms以內沒有變化,那么確認此次為短按鍵按下。每次在掃描鍵盤時大概耗費幾十us,然后接著以同樣的方式掃描其他模塊。這樣主循環(huán)把所有的模塊都掃一遍頂多也就幾百us的樣子。這樣說來,一次按鍵按下事件,程序已經把它分成了大概幾百次來完成,即為分時處理。而不是以delay的方式死等這一次事件完成。其他模塊都遵循這個道理。
告別delay,主循環(huán)while(1)周期做到小于1ms,那么就告別了學校教育從而進入實際應用!為什么學校所教的不是這種思想呢?因為在學校和實踐嚴重脫節(jié),沒有考慮過真正的項目。所學的都是一些獨立子程序模塊。由于子程序結構簡單,程序較小,delay無所謂。而把各個模塊都加在一起,可能就會出毛病。實際情況要考慮的還多的很,比如功耗、成本等等。在學校只管能搞出結果就不錯了,哪管這個芯片多少錢,這個電容多少錢?對于真正的項目應用,實現功能只算很小很小的一部分。由于學校所學和實際脫節(jié),所以這也是當今大學生難找工作的原因之一,這也是當今中國教育的缺陷!當然成為單片機高手,還有很多路要走,了解這些只是一個門檻而已。
評論