91视频免费?看_蜜芽MY188精品TV在线观看_国产免费无遮挡在线观看视频_深夜国产_亚洲精品欧洲精品_欧美黑人粗暴多交

  • 回復(fù)
  • 收藏
  • 點贊
  • 分享
  • 發(fā)新帖

從業(yè)將近十年!手把手教你單片機程序框架(連載)

      大家好,我叫吳堅鴻,從事單片機項目開發(fā)已經(jīng)有快十年了,目前跟小伙伴們在深圳開了個方案公司,專門承接項目開發(fā)。這些年我做的項目不計其數(shù),現(xiàn)在借電子信息網(wǎng)這個平臺把我認(rèn)為最有價值的東西分享給大家,我這個技術(shù)貼每個星期更新一兩篇,直到我江郎才盡為止。

       

      

朱兆祺51單片機學(xué)習(xí)板

      

全部回復(fù)(312)
正序查看
倒序查看
2014-03-05 20:53

第二節(jié):delay()延時實現(xiàn)LED燈的閃爍。

開場白:    

       上一節(jié)鴻哥列出了初學(xué)者七大誤區(qū),到底什么才是初學(xué)者關(guān)注的核心?那就是裸機奔跑的程序結(jié)構(gòu)。一個好的程序結(jié)構(gòu),本身就是一個微型的多任務(wù)操作系統(tǒng)。鴻哥教給大家的就是如何編寫這個簡單的操作系統(tǒng)。在main函數(shù)循環(huán)中用switch語句實現(xiàn)多任務(wù)并行處理的任務(wù)切換,再外加一個定時器中斷,這兩者的結(jié)合就是鴻哥多年來所有實戰(zhàn)項目的核心。鴻哥的程序結(jié)構(gòu)看似簡單,實際上就是那么簡單。大家不用著急,本篇連載文章現(xiàn)在才正式開始,這一節(jié)我要教會大家兩個知識點:

       第一點:鴻哥首次提出的“三區(qū)一線”理論。此理論把程序代碼分成三個區(qū),一個延時分割線。

       第二點:delay()延時的用途。

(1)硬件平臺:基于朱兆祺51單片機學(xué)習(xí)板。

(2)實現(xiàn)功能:讓一個LED閃爍。

(3)源代碼講解如下:

#include "REG52.H"

void initial_myself();    
void initial_peripheral();

void delay_short(unsigned int uiDelayshort);
void delay_long(unsigned int uiDelaylong);
void led_flicker();

/* 注釋一:
* 吳堅鴻個人的命名風(fēng)格:凡是輸出后綴都是_dr,凡是輸入后綴都是_sr。
* dr代表drive驅(qū)動,sr代表sensor感應(yīng)器
*/
sbit led_dr=P3^5;  

void main()  //學(xué)習(xí)要點:深刻理解鴻哥首次提出的三區(qū)一線理論
  {
/* 注釋二:
* initial_myself()函數(shù)屬于鴻哥三區(qū)一線理論的第一區(qū),
* 專門用來初始化單片機自己的寄存器以及個別外圍要求響應(yīng)速度快的輸出設(shè)備,
* 防止剛上電之后,由于輸出IO口電平狀態(tài)不確定而導(dǎo)致外圍設(shè)備誤動作,
* 比如繼電器的誤動作等等。 
*/
   initial_myself();

/* 注釋三:
* 此處的delay_long()延時函數(shù)屬于第一區(qū)與第二區(qū)的分割線,
* 延時時間一般是0.3秒到2秒之間,等待外圍芯片和模塊上電穩(wěn)定。
* 比如液晶模塊,AT24C02存儲芯片,DS1302時鐘芯片,
* 這類芯片有個特點,一般都是跟單片機進行串口或并口通訊的,
* 并且不要求上電立即處理的。
*/
   delay_long(100);

/* 注釋四:
* initial_peripheral()函數(shù)屬于鴻哥三區(qū)一線理論的第二區(qū),
* 專門用來初始化不要求上電立即處理的外圍芯片和模塊.
* 比如液晶模塊,AT24C02存儲芯片,DS1302時鐘芯片。
* 本程序基于朱兆祺51單片機學(xué)習(xí)板。
*/
   initial_peripheral();

/* 注釋五:
* while(1){}主函數(shù)循環(huán)區(qū)屬于鴻哥三區(qū)一線理論的第三區(qū),
* 專門用來編寫被循環(huán)掃描到的非中斷應(yīng)用程序
*/
   while(1)
   {
      led_flicker();   //LED閃爍應(yīng)用程序
   }

}

void led_flicker() //LED閃爍應(yīng)用程序
{
  led_dr=1;  //LED亮
  delay_short(50000);  //延時50000個空指令的時間

/* 注釋六:
* delay_long(100)延時50000個空指令的時間,因為內(nèi)嵌了一個500次的for循環(huán)
*/
  led_dr=0;  //LED滅
  delay_long(100);    //延時50000個空指令的時間  
}


/* 注釋七:
* delay_short(unsigned int uiDelayShort)是小延時函數(shù),
* 專門用在時序驅(qū)動的小延時,一般uiDelayShort的數(shù)值取10左右,
* 最大一般也不超過100.本例為了解釋此函數(shù)的特點,取值范圍超過100。
* 此函數(shù)的特點是時間的細分度高,延時時間不宜過長。uiDelayShort數(shù)值
* 的大小就代表里面執(zhí)行了多少條空指令的時間。數(shù)值越大,延時越長。
* 時間精度不要刻意去計算,感覺差不多就行。
*/
void delay_short(unsigned int uiDelayShort) 
{
   unsigned int i;  
   for(i=0;i
	

 

總結(jié)陳詞:

      鴻哥首次提出的“三區(qū)一線”理論概況了各種項目程序的基本分區(qū)。我后續(xù)的程序就按此分區(qū)編寫。Delay()函數(shù)的長延時適用在上電初始化。Delay()函數(shù)的短延時適用在驅(qū)動時序的脈沖延時,此時的時間不能太長,本例中暫時沒有列出這方面的例子,在后面的章節(jié)中會提到。在本例源代碼中,在led_flicker()閃爍應(yīng)用程序里用到的兩個延時delay,它們的延時時間都太長了,在實戰(zhàn)項目中肯定不能用這種延時,因為消耗的時間太長了,其它任務(wù)根本沒有機會執(zhí)行。那怎么辦呢?我們應(yīng)該如何改善?欲知詳情,請聽下回分解-----累計主循環(huán)次數(shù)使LED燈閃爍。

(未完待續(xù),下節(jié)更精彩,不要走開哦)

0
回復(fù)
2014-03-04 23:22

第一節(jié):吳堅鴻談初學(xué)單片機的誤區(qū)。

(1)很難記住繁雜的寄存器?寄存器不用死記硬背,鴻哥我行走江湖多年,連一個寄存器都記不住。

 

需要配置寄存器的時候,直接在網(wǎng)上或者書本上參考別人現(xiàn)成的配置程序是上策,查找芯片數(shù)據(jù)手冊是中策,死記硬背寄存器是最最下策。

 

(2)很難記住繁雜的匯編語言指令?除非是在校學(xué)生要應(yīng)付考試或者少數(shù)工作中繞不開匯編,否則學(xué)匯編就是浪費時間。鴻哥我行走江湖多年,從來就沒有用匯編幫客戶做過一個項目。

 

(3)C語言很難學(xué)?你不用學(xué)指針,你不用學(xué)帶形參的函數(shù),你不用學(xué)結(jié)構(gòu)體,你不用學(xué)宏定義,你不用學(xué)文件操作,你也不用死記繁瑣的數(shù)據(jù)類型。你只要會:      

 

5條指令語句switch語句,if else語句,while語句,for語句,=賦值語句。     

 7個運算符+,-,*,/,|,&,!。      

 

4個邏輯關(guān)系符||,&&,!=,==.      

 

3個數(shù)據(jù)類型unsigned char, unsigned int, unsigned long。     

 

3個進制相互轉(zhuǎn)化,二進制,十六進制,十進制。      

 

1個void函數(shù)。      

            

1個一維數(shù)組code(或const) unsigned char array[]。     

 

 那么世界上任何一種邏輯功能的單片機軟件你都能做出來。     

 

      鴻哥我當(dāng)年剛畢業(yè)出來工作的時候才知道可以用C語言開發(fā)單片機,一開始只用if語句就把項目做出來了,沒有用指針,沒有用帶形參的函數(shù)等復(fù)雜的功能。再到后來才慢慢開始用C語言其他的高級功能,但是我發(fā)現(xiàn)C語言其他的高級功能,本質(zhì)上都是用我前面列舉出來的最基本功能集合而成,只是書寫更加簡單方便了一點,編譯后的機器碼都大同小異。

所以不會指針等高級功能你不用自卑,恰恰相反,當(dāng)你會最簡單的幾個語句,就把這些高級功能的程序都做出來了,你才發(fā)現(xiàn)你對底層了解得更加透切,再學(xué)那些高級功能輕而易舉。當(dāng)你裸機跑的程序都能夠協(xié)調(diào)得很好的時候,你才發(fā)現(xiàn)所謂高深的操作系統(tǒng)也不過如此,只要給你時間和金錢你也可以寫個操作系統(tǒng)來玩玩。

 

(4)很難記住精確時間的計算公式?經(jīng)常看到時間公式等于晶振,時鐘周期,執(zhí)行指令次數(shù)他們之間的乘除關(guān)系式。鴻哥我認(rèn)為這些都是浮云,不用糾結(jié)也不用去記,大概了解一下就可以了。不管你對公式掌握得有多精確,你都不可能做出非常精確的時間。想用單片機做一個非常精確的時間這種想法一開始就是錯的,不可能的。真想做一個比較精確的時間,應(yīng)該用外圍時鐘芯片或者FPGA和CPLD,而不是單片機。

(5)很難記住繁雜的各種通信協(xié)議?什么IIC,SPI,232串口通訊,CAN,USB等等。這些都是浮云,你不用記那么多,你只要理解兩種通訊方式就夠了,那就是串行通訊方式和并行通訊方式。不管世界上有多少種通訊協(xié)議,物理世界上只有這兩種通訊方式,其他各種名稱的通訊協(xié)議都基于此兩種方式演變而來。

(6)很難寫短小精悍的程序?初學(xué)者不要糾結(jié)于此。做項目開發(fā),程序容量不是刻意追求的目標(biāo),程序多一點少一點沒關(guān)系,現(xiàn)在大容量的單片機品種非常多,容量不會是寸土寸金的事情,我們更加要關(guān)注程序的運行效率,可讀性和可修改性。

      既然鴻哥列出了那么多誤區(qū),那么什么才是初學(xué)者關(guān)注的核心?預(yù)知詳情,請聽下回分解----delay()延時實現(xiàn)LED燈的閃爍。(未完待續(xù),下節(jié)更精彩,不要走開哦)

0
回復(fù)
2014-03-04 23:26
@jianhong_wu
第一節(jié):吳堅鴻談初學(xué)單片機的誤區(qū)。(1)很難記住繁雜的寄存器?寄存器不用死記硬背,鴻哥我行走江湖多年,連一個寄存器都記不住。 需要配置寄存器的時候,直接在網(wǎng)上或者書本上參考別人現(xiàn)成的配置程序是上策,查找芯片數(shù)據(jù)手冊是中策,死記硬背寄存器是最最下策。 (2)很難記住繁雜的匯編語言指令?除非是在校學(xué)生要應(yīng)付考試或者少數(shù)工作中繞不開匯編,否則學(xué)匯編就是浪費時間。鴻哥我行走江湖多年,從來就沒有用匯編幫客戶做過一個項目。 (3)C語言很難學(xué)?你不用學(xué)指針,你不用學(xué)帶形參的函數(shù),你不用學(xué)結(jié)構(gòu)體,你不用學(xué)宏定義,你不用學(xué)文件操作,你也不用死記繁瑣的數(shù)據(jù)類型。你只要會:     5條指令語句switch語句,ifelse語句,while語句,for語句,=賦值語句。     7個運算符+,-,*,/,|,&,!。     4個邏輯關(guān)系符||,&&,!=,==.     3個數(shù)據(jù)類型unsignedchar,unsignedint,unsignedlong。      3個進制相互轉(zhuǎn)化,二進制,十六進制,十進制。     1個void函數(shù)。                  1個一維數(shù)組code(或const)unsignedchararray[]。      那么世界上任何一種邏輯功能的單片機軟件你都能做出來。        鴻哥我當(dāng)年剛畢業(yè)出來工作的時候才知道可以用C語言開發(fā)單片機,一開始只用if語句就把項目做出來了,沒有用指針,沒有用帶形參的函數(shù)等復(fù)雜的功能。再到后來才慢慢開始用C語言其他的高級功能,但是我發(fā)現(xiàn)C語言其他的高級功能,本質(zhì)上都是用我前面列舉出來的最基本功能集合而成,只是書寫更加簡單方便了一點,編譯后的機器碼都大同小異。所以不會指針等高級功能你不用自卑,恰恰相反,當(dāng)你會最簡單的幾個語句,就把這些高級功能的程序都做出來了,你才發(fā)現(xiàn)你對底層了解得更加透切,再學(xué)那些高級功能輕而易舉。當(dāng)你裸機跑的程序都能夠協(xié)調(diào)得很好的時候,你才發(fā)現(xiàn)所謂高深的操作系統(tǒng)也不過如此,只要給你時間和金錢你也可以寫個操作系統(tǒng)來玩玩。 (4)很難記住精確時間的計算公式?經(jīng)常看到時間公式等于晶振,時鐘周期,執(zhí)行指令次數(shù)他們之間的乘除關(guān)系式。鴻哥我認(rèn)為這些都是浮云,不用糾結(jié)也不用去記,大概了解一下就可以了。不管你對公式掌握得有多精確,你都不可能做出非常精確的時間。想用單片機做一個非常精確的時間這種想法一開始就是錯的,不可能的。真想做一個比較精確的時間,應(yīng)該用外圍時鐘芯片或者FPGA和CPLD,而不是單片機。(5)很難記住繁雜的各種通信協(xié)議?什么IIC,SPI,232串口通訊,CAN,USB等等。這些都是浮云,你不用記那么多,你只要理解兩種通訊方式就夠了,那就是串行通訊方式和并行通訊方式。不管世界上有多少種通訊協(xié)議,物理世界上只有這兩種通訊方式,其他各種名稱的通訊協(xié)議都基于此兩種方式演變而來。(6)很難寫短小精悍的程序?初學(xué)者不要糾結(jié)于此。做項目開發(fā),程序容量不是刻意追求的目標(biāo),程序多一點少一點沒關(guān)系,現(xiàn)在大容量的單片機品種非常多,容量不會是寸土寸金的事情,我們更加要關(guān)注程序的運行效率,可讀性和可修改性。   既然鴻哥列出了那么多誤區(qū),那么什么才是初學(xué)者關(guān)注的核心?預(yù)知詳情,請聽下回分解----delay()延時實現(xiàn)LED燈的閃爍。(未完待續(xù),下節(jié)更精彩,不要走開哦)

1

0
回復(fù)
roc19850
LV.5
5
2014-03-05 13:47
@jianhong_wu
1
樓主:怎么我看TXT附件打開為亂碼啊?
0
回復(fù)
2014-03-05 14:18
@roc19850
樓主:怎么我看TXT附件打開為亂碼啊?
論壇系統(tǒng)的問題。我直接發(fā)代碼也會亂在一起,沒有段落,所有的段落首位都會連在一起。望管理員能解決這個問題。還有一種方法,你點擊右鍵“目標(biāo)另存為”下載了之后再打開看,就不會亂。
0
回復(fù)
2014-03-05 15:05

代碼與word 粘貼,可以選擇以上按鈕操作下~~

0
回復(fù)
2014-03-05 16:05
頂起!
0
回復(fù)
huangyi59
LV.4
9
2014-03-05 16:07
@jianhong_wu
第一節(jié):吳堅鴻談初學(xué)單片機的誤區(qū)。(1)很難記住繁雜的寄存器?寄存器不用死記硬背,鴻哥我行走江湖多年,連一個寄存器都記不住。 需要配置寄存器的時候,直接在網(wǎng)上或者書本上參考別人現(xiàn)成的配置程序是上策,查找芯片數(shù)據(jù)手冊是中策,死記硬背寄存器是最最下策。 (2)很難記住繁雜的匯編語言指令?除非是在校學(xué)生要應(yīng)付考試或者少數(shù)工作中繞不開匯編,否則學(xué)匯編就是浪費時間。鴻哥我行走江湖多年,從來就沒有用匯編幫客戶做過一個項目。 (3)C語言很難學(xué)?你不用學(xué)指針,你不用學(xué)帶形參的函數(shù),你不用學(xué)結(jié)構(gòu)體,你不用學(xué)宏定義,你不用學(xué)文件操作,你也不用死記繁瑣的數(shù)據(jù)類型。你只要會:     5條指令語句switch語句,ifelse語句,while語句,for語句,=賦值語句。     7個運算符+,-,*,/,|,&,!。     4個邏輯關(guān)系符||,&&,!=,==.     3個數(shù)據(jù)類型unsignedchar,unsignedint,unsignedlong。      3個進制相互轉(zhuǎn)化,二進制,十六進制,十進制。     1個void函數(shù)。                  1個一維數(shù)組code(或const)unsignedchararray[]。      那么世界上任何一種邏輯功能的單片機軟件你都能做出來。        鴻哥我當(dāng)年剛畢業(yè)出來工作的時候才知道可以用C語言開發(fā)單片機,一開始只用if語句就把項目做出來了,沒有用指針,沒有用帶形參的函數(shù)等復(fù)雜的功能。再到后來才慢慢開始用C語言其他的高級功能,但是我發(fā)現(xiàn)C語言其他的高級功能,本質(zhì)上都是用我前面列舉出來的最基本功能集合而成,只是書寫更加簡單方便了一點,編譯后的機器碼都大同小異。所以不會指針等高級功能你不用自卑,恰恰相反,當(dāng)你會最簡單的幾個語句,就把這些高級功能的程序都做出來了,你才發(fā)現(xiàn)你對底層了解得更加透切,再學(xué)那些高級功能輕而易舉。當(dāng)你裸機跑的程序都能夠協(xié)調(diào)得很好的時候,你才發(fā)現(xiàn)所謂高深的操作系統(tǒng)也不過如此,只要給你時間和金錢你也可以寫個操作系統(tǒng)來玩玩。 (4)很難記住精確時間的計算公式?經(jīng)常看到時間公式等于晶振,時鐘周期,執(zhí)行指令次數(shù)他們之間的乘除關(guān)系式。鴻哥我認(rèn)為這些都是浮云,不用糾結(jié)也不用去記,大概了解一下就可以了。不管你對公式掌握得有多精確,你都不可能做出非常精確的時間。想用單片機做一個非常精確的時間這種想法一開始就是錯的,不可能的。真想做一個比較精確的時間,應(yīng)該用外圍時鐘芯片或者FPGA和CPLD,而不是單片機。(5)很難記住繁雜的各種通信協(xié)議?什么IIC,SPI,232串口通訊,CAN,USB等等。這些都是浮云,你不用記那么多,你只要理解兩種通訊方式就夠了,那就是串行通訊方式和并行通訊方式。不管世界上有多少種通訊協(xié)議,物理世界上只有這兩種通訊方式,其他各種名稱的通訊協(xié)議都基于此兩種方式演變而來。(6)很難寫短小精悍的程序?初學(xué)者不要糾結(jié)于此。做項目開發(fā),程序容量不是刻意追求的目標(biāo),程序多一點少一點沒關(guān)系,現(xiàn)在大容量的單片機品種非常多,容量不會是寸土寸金的事情,我們更加要關(guān)注程序的運行效率,可讀性和可修改性。   既然鴻哥列出了那么多誤區(qū),那么什么才是初學(xué)者關(guān)注的核心?預(yù)知詳情,請聽下回分解----delay()延時實現(xiàn)LED燈的閃爍。(未完待續(xù),下節(jié)更精彩,不要走開哦)
不錯,值得學(xué)習(xí)。占個坐先。
0
回復(fù)
tangchao123
LV.5
10
2014-03-05 17:25
@jianhong_wu
論壇系統(tǒng)的問題。我直接發(fā)代碼也會亂在一起,沒有段落,所有的段落首位都會連在一起。望管理員能解決這個問題。還有一種方法,你點擊右鍵“目標(biāo)另存為”下載了之后再打開看,就不會亂。
頂起!!!!!
0
回復(fù)
shenx123
LV.10
11
2014-03-05 17:28
@jianhong_wu
第一節(jié):吳堅鴻談初學(xué)單片機的誤區(qū)。(1)很難記住繁雜的寄存器?寄存器不用死記硬背,鴻哥我行走江湖多年,連一個寄存器都記不住。 需要配置寄存器的時候,直接在網(wǎng)上或者書本上參考別人現(xiàn)成的配置程序是上策,查找芯片數(shù)據(jù)手冊是中策,死記硬背寄存器是最最下策。 (2)很難記住繁雜的匯編語言指令?除非是在校學(xué)生要應(yīng)付考試或者少數(shù)工作中繞不開匯編,否則學(xué)匯編就是浪費時間。鴻哥我行走江湖多年,從來就沒有用匯編幫客戶做過一個項目。 (3)C語言很難學(xué)?你不用學(xué)指針,你不用學(xué)帶形參的函數(shù),你不用學(xué)結(jié)構(gòu)體,你不用學(xué)宏定義,你不用學(xué)文件操作,你也不用死記繁瑣的數(shù)據(jù)類型。你只要會:     5條指令語句switch語句,ifelse語句,while語句,for語句,=賦值語句。     7個運算符+,-,*,/,|,&,!。     4個邏輯關(guān)系符||,&&,!=,==.     3個數(shù)據(jù)類型unsignedchar,unsignedint,unsignedlong。      3個進制相互轉(zhuǎn)化,二進制,十六進制,十進制。     1個void函數(shù)。                  1個一維數(shù)組code(或const)unsignedchararray[]。      那么世界上任何一種邏輯功能的單片機軟件你都能做出來。        鴻哥我當(dāng)年剛畢業(yè)出來工作的時候才知道可以用C語言開發(fā)單片機,一開始只用if語句就把項目做出來了,沒有用指針,沒有用帶形參的函數(shù)等復(fù)雜的功能。再到后來才慢慢開始用C語言其他的高級功能,但是我發(fā)現(xiàn)C語言其他的高級功能,本質(zhì)上都是用我前面列舉出來的最基本功能集合而成,只是書寫更加簡單方便了一點,編譯后的機器碼都大同小異。所以不會指針等高級功能你不用自卑,恰恰相反,當(dāng)你會最簡單的幾個語句,就把這些高級功能的程序都做出來了,你才發(fā)現(xiàn)你對底層了解得更加透切,再學(xué)那些高級功能輕而易舉。當(dāng)你裸機跑的程序都能夠協(xié)調(diào)得很好的時候,你才發(fā)現(xiàn)所謂高深的操作系統(tǒng)也不過如此,只要給你時間和金錢你也可以寫個操作系統(tǒng)來玩玩。 (4)很難記住精確時間的計算公式?經(jīng)常看到時間公式等于晶振,時鐘周期,執(zhí)行指令次數(shù)他們之間的乘除關(guān)系式。鴻哥我認(rèn)為這些都是浮云,不用糾結(jié)也不用去記,大概了解一下就可以了。不管你對公式掌握得有多精確,你都不可能做出非常精確的時間。想用單片機做一個非常精確的時間這種想法一開始就是錯的,不可能的。真想做一個比較精確的時間,應(yīng)該用外圍時鐘芯片或者FPGA和CPLD,而不是單片機。(5)很難記住繁雜的各種通信協(xié)議?什么IIC,SPI,232串口通訊,CAN,USB等等。這些都是浮云,你不用記那么多,你只要理解兩種通訊方式就夠了,那就是串行通訊方式和并行通訊方式。不管世界上有多少種通訊協(xié)議,物理世界上只有這兩種通訊方式,其他各種名稱的通訊協(xié)議都基于此兩種方式演變而來。(6)很難寫短小精悍的程序?初學(xué)者不要糾結(jié)于此。做項目開發(fā),程序容量不是刻意追求的目標(biāo),程序多一點少一點沒關(guān)系,現(xiàn)在大容量的單片機品種非常多,容量不會是寸土寸金的事情,我們更加要關(guān)注程序的運行效率,可讀性和可修改性。   既然鴻哥列出了那么多誤區(qū),那么什么才是初學(xué)者關(guān)注的核心?預(yù)知詳情,請聽下回分解----delay()延時實現(xiàn)LED燈的閃爍。(未完待續(xù),下節(jié)更精彩,不要走開哦)

第一節(jié)的前面沒基礎(chǔ)的嗎? 比如看什么書,之類

從入門開始行嗎

0
回復(fù)
jianhong_wu
LV.4
12
2014-03-05 20:59
@電源網(wǎng)-儷儷
[圖片]代碼與word粘貼,可以選擇以上按鈕操作下~~
我試過了,都不行。你們用的論壇系統(tǒng)不是discuz的模板吧,這個問題不解決,真的很打擊發(fā)帖的積極性。
0
回復(fù)
jianhong_wu
LV.4
13
2014-03-05 21:07

第三節(jié):累計主循環(huán)次數(shù)使LED燈閃爍。

開場白:

       上一節(jié)鴻哥提到delay()延時函數(shù)消耗的時間太長了,其它任務(wù)根本沒有機會執(zhí)行,我們該怎么改善?本節(jié)教大家利用累計主循環(huán)次數(shù)的方法來解決這個問題。這一節(jié)要教會大家兩個知識點:

       第一點:利用累計主循環(huán)次數(shù)的方法實現(xiàn)時間延時。

       第二點:switch核心語句之初體驗。 鴻哥所有的實戰(zhàn)項目都是基于switch語句實現(xiàn)多任務(wù)并行處理。

(1)硬件平臺:基于朱兆祺51單片機學(xué)習(xí)板。

(2)實現(xiàn)功能:讓一個LED閃爍。

(3)源代碼講解如下:

#include "REG52.H"


/* 注釋一:
* const_time_level是統(tǒng)計循環(huán)次數(shù)的設(shè)定上限,數(shù)值越大,LED延時的時間越久
*/
#define const_time_level 10000  

void initial_myself();    
void initial_peripheral();
void delay_long(unsigned int uiDelaylong);
void led_flicker();

sbit led_dr=P3^5;  

/* 注釋二:
* 吳堅鴻個人的命名風(fēng)格:凡是switch語句里面的步驟變量后綴都是Step.
* 前綴帶uc,ui,ul分別表示此變量是unsigned char,unsigned int,unsigned long.
*/
unsigned char ucLedStep=0; //步驟變量
unsigned int  uiTimeCnt=0; //統(tǒng)計循環(huán)次數(shù)的延時計數(shù)器
void main() 
  {
   initial_myself();  
   delay_long(100);   
   initial_peripheral(); 
   while(1)   
   {
      led_flicker();   
   }

}

void led_flicker() ////第三區(qū) LED閃爍應(yīng)用程序
{
  
  switch(ucLedStep)
  {
     case 0:
/* 注釋三:
* uiTimeCnt累加循環(huán)次數(shù),只有當(dāng)它的次數(shù)大于或等于設(shè)定上限const_time_level時,
* 才會去改變LED燈的狀態(tài),否則CPU退出led_flicker()任務(wù),繼續(xù)快速掃描其他的任務(wù),
* 這樣的程序結(jié)構(gòu)就可以達到多任務(wù)并行處理的目的。
* 本程序基于朱兆祺51單片機學(xué)習(xí)板
*/
          uiTimeCnt++;  //累加循環(huán)次數(shù),
                  if(uiTimeCnt>=const_time_level) //時間到
                  {
                     uiTimeCnt=0; //時間計數(shù)器清零
             led_dr=1;    //讓LED亮
                         ucLedStep=1; //切換到下一個步驟
                  }
              break;
     case 1:
          uiTimeCnt++;  //累加循環(huán)次數(shù),
                  if(uiTimeCnt>=const_time_level) //時間到
                  {
                     uiTimeCnt=0; //時間計數(shù)器清零
             led_dr=0;    //讓LED滅
                         ucLedStep=0; //返回到上一個步驟
                  }
              break;
  
  }

}


void delay_long(unsigned int uiDelayLong)
{
   unsigned int i;
   unsigned int j;
   for(i=0;i

		

總結(jié)陳詞:   

       在實際項目中,用累計主循環(huán)次數(shù)實現(xiàn)時間延時是一個不錯的選擇。這種方法能勝任多任務(wù)處理的程序框架,但是它本身也有一個小小的不足。隨著主函數(shù)里任務(wù)量的增加,我們?yōu)榱吮WC延時時間的準(zhǔn)確性,要不斷修正設(shè)定上限const_time_level 。我們該怎么解決這個問題呢?欲知詳情,請聽下回分解-----累計定時中斷次數(shù)使LED燈閃爍。

(未完待續(xù),下節(jié)更精彩,不要走開哦)

0
回復(fù)
jianhong_wu
LV.4
14
2014-03-05 21:14
@shenx123
第一節(jié)的前面沒基礎(chǔ)的嗎?比如看什么書,之類從入門開始行嗎
我已經(jīng)從很基本的開始講起的,主要是教大家怎么做項目的程序框架和思路,其他更加基礎(chǔ)的東西要靠你們自學(xué),比如C語言的語法,十六進制的轉(zhuǎn)換等等。
0
回復(fù)
jianhong_wu
LV.4
15
2014-03-05 21:18

第四節(jié):累計定時中斷次數(shù)使LED燈閃爍。

開場白:

      上一節(jié)提到在累計主循環(huán)次數(shù)來實現(xiàn)計時,隨著主函數(shù)里任務(wù)量的增加,為了保證延時時間的準(zhǔn)確性,要不斷修正設(shè)定上限閥值const_time_level 。我們該怎么解決這個問題呢?本節(jié)教大家利用累計定時中斷次數(shù)的方法來解決這個問題。這一節(jié)要教會大家四個知識點:

      第一點:利用累計定時中斷次數(shù)的方法實現(xiàn)時間延時。

      第二點:展現(xiàn)鴻哥最完整的實戰(zhàn)程序框架。在主函數(shù)循環(huán)里用switch語句實現(xiàn)狀態(tài)機的切換,在定時中斷里累計中斷次數(shù),這兩個的結(jié)合就是我寫代碼最本質(zhì)的框架思想。 

      第三點:提醒大家C語言中的int ,long變量是由幾個字節(jié)構(gòu)成的數(shù)據(jù),凡是在main函數(shù)和中斷函數(shù)里有可能同時改變的變量,這個變量應(yīng)該在主函數(shù)中被更改之前,先關(guān)閉相應(yīng)的中斷,更改完了此變量,再打開中斷,否則會留下不宜察覺的漏洞。當(dāng)然在大部分的項目中可以不用這么操作,但是在一些要求非常高的項目中,有一些核心變量必須這么做。

       第四點:定時中斷的初始值該怎么設(shè)置。不用嚴(yán)格按公式來計算時間,一般取個經(jīng)驗值是最大初始值減去1000就可以了。具體內(nèi)容,請看源代碼講解。

(1)硬件平臺:基于朱兆祺51單片機學(xué)習(xí)板。

(2)實現(xiàn)功能:讓一個LED閃爍。

(3)源代碼講解如下:

 

#include "REG52.H"

#define const_time_level 200  

void initial_myself();    
void initial_peripheral();
void delay_long(unsigned int uiDelaylong);
void led_flicker();
void T0_time();  //定時中斷函數(shù)

sbit led_dr=P3^5;  

unsigned char ucLedStep=0; //步驟變量
unsigned int  uiTimeCnt=0; //統(tǒng)計定時中斷次數(shù)的延時計數(shù)器


void main() 
  {
   initial_myself();  
   delay_long(100);   
   initial_peripheral(); 
   while(1)   
   {
      led_flicker();   
   }

}

void led_flicker() ////第三區(qū) LED閃爍應(yīng)用程序
{
  
  switch(ucLedStep)
  {
     case 0:
/* 注釋一:
* uiTimeCnt累加定時中斷的次數(shù),每一次定時中斷它都會在中斷函數(shù)里自加一。
* 只有當(dāng)它的次數(shù)大于或等于設(shè)定上限const_time_level時,
* 才會去改變LED燈的狀態(tài),否則CPU退出led_flicker()任務(wù),繼續(xù)快速掃描其他的任務(wù),
* 這樣的程序結(jié)構(gòu)就可以達到多任務(wù)并行處理的目的。這就是鴻哥在所有開發(fā)項目中的核心框架。
*/
                  if(uiTimeCnt>=const_time_level) //時間到
                  {

/* 注釋二:
* ET0=0;uiTimeCnt=0;ET0=1;----在清零uiTimeCnt之前,為什么要先禁止定時中斷?
* 因為uiTimeCnt是unsigned int類型,本質(zhì)上是由兩個字節(jié)組成。
* 在C語言中uiTimeCnt=0看似一條指令,實際上經(jīng)過編譯之后它不只一條匯編指令。
* 由于定時中斷函數(shù)里也對這個變量進行累加操作,如果不禁止定時中斷,
* 那么uiTimeCnt這個變量在main()函數(shù)中還沒被完全清零的時候,如果這個時候
* 突然來一個定時中斷,并且在中斷里又更改了此變量,這種情況在某些要求高的
* 項目上會是一個不容易察覺的漏洞,為項目帶來隱患。當(dāng)然,大部分的普通項目,
* 都可以不用那么嚴(yán)格,可以不用禁止定時中斷。在這里只是提醒各位初學(xué)者有這種情況。
*/
             ET0=0;  //禁止定時中斷
                     uiTimeCnt=0; //時間計數(shù)器清零
             ET0=1; //開啟定時中斷
             led_dr=1;    //讓LED亮
                         ucLedStep=1; //切換到下一個步驟
                  }
              break;
     case 1:
                  if(uiTimeCnt>=const_time_level) //時間到
                  {
             ET0=0;  //禁止定時中斷
                     uiTimeCnt=0; //時間計數(shù)器清零
             ET0=1;   //開啟定時中斷
             led_dr=0;    //讓LED滅
                         ucLedStep=0; //返回到上一個步驟
                  }
              break;
  
  }

}


/* 注釋三:
* C51的中斷函數(shù)格式如下:
* void 函數(shù)名() interrupt 中斷號
* {
*    中斷程序內(nèi)容
* }
* 函數(shù)名可以隨便取,只要不是編譯器已經(jīng)征用的關(guān)鍵字。
* 這里最關(guān)鍵的是中斷號,不同的中斷號代表不同類型的中斷。
* 定時中斷的中斷號是 1.至于其它中斷的中斷號,大家可以查找
* 相關(guān)書籍和資料。大家進入中斷時,必須先清除中斷標(biāo)志,并且
* 關(guān)閉中斷,然后再寫代碼,最后出來時,記得重裝初始值,并且
* 打開中斷。
*/
void T0_time() interrupt 1
{
  TF0=0;  //清除中斷標(biāo)志
  TR0=0; //關(guān)中斷

  if(uiTimeCnt<0xffff)  //設(shè)定這個條件,防止uiTimeCnt超范圍。
  {
      uiTimeCnt++;  //累加定時中斷的次數(shù),
  }

TH0=0xf8;   //重裝初始值(65535-2000)=63535=0xf82f
TL0=0x2f;
TR0=1;  //開中斷
}


void delay_long(unsigned int uiDelayLong)
{
   unsigned int i;
   unsigned int j;
   for(i=0;i

總結(jié)陳詞:

     本節(jié)程序麻雀雖小五臟俱全。在本節(jié)中已經(jīng)展示了我最完整的實戰(zhàn)程序框架。本節(jié)程序只有一個LED燈閃爍的單任務(wù),如果要多增加一個任務(wù)來并行處理,該怎么辦?欲知詳情,請聽下回分解-----蜂鳴器的驅(qū)動程序。

(未完待續(xù),下節(jié)更精彩,不要走開哦)

0
回復(fù)
pangjihao
LV.10
16
2014-03-06 08:36
@jianhong_wu
第四節(jié):累計定時中斷次數(shù)使LED燈閃爍。開場白:   上一節(jié)提到在累計主循環(huán)次數(shù)來實現(xiàn)計時,隨著主函數(shù)里任務(wù)量的增加,為了保證延時時間的準(zhǔn)確性,要不斷修正設(shè)定上限閥值const_time_level。我們該怎么解決這個問題呢?本節(jié)教大家利用累計定時中斷次數(shù)的方法來解決這個問題。這一節(jié)要教會大家四個知識點:   第一點:利用累計定時中斷次數(shù)的方法實現(xiàn)時間延時。   第二點:展現(xiàn)鴻哥最完整的實戰(zhàn)程序框架。在主函數(shù)循環(huán)里用switch語句實現(xiàn)狀態(tài)機的切換,在定時中斷里累計中斷次數(shù),這兩個的結(jié)合就是我寫代碼最本質(zhì)的框架思想。     第三點:提醒大家C語言中的int,long變量是由幾個字節(jié)構(gòu)成的數(shù)據(jù),凡是在main函數(shù)和中斷函數(shù)里有可能同時改變的變量,這個變量應(yīng)該在主函數(shù)中被更改之前,先關(guān)閉相應(yīng)的中斷,更改完了此變量,再打開中斷,否則會留下不宜察覺的漏洞。當(dāng)然在大部分的項目中可以不用這么操作,但是在一些要求非常高的項目中,有一些核心變量必須這么做。    第四點:定時中斷的初始值該怎么設(shè)置。不用嚴(yán)格按公式來計算時間,一般取個經(jīng)驗值是最大初始值減去1000就可以了。具體內(nèi)容,請看源代碼講解。(1)硬件平臺:基于朱兆祺51單片機學(xué)習(xí)板。(2)實現(xiàn)功能:讓一個LED閃爍。(3)源代碼講解如下: #include"REG52.H"#defineconst_time_level200voidinitial_myself();voidinitial_peripheral();voiddelay_long(unsignedintuiDelaylong);voidled_flicker();voidT0_time();//定時中斷函數(shù)sbitled_dr=P3^5;unsignedcharucLedStep=0;//步驟變量unsignedintuiTimeCnt=0;//統(tǒng)計定時中斷次數(shù)的延時計數(shù)器voidmain(){initial_myself();delay_long(100);initial_peripheral();while(1){led_flicker();}}voidled_flicker()////第三區(qū)LED閃爍應(yīng)用程序{switch(ucLedStep){case0:/*注釋一:*uiTimeCnt累加定時中斷的次數(shù),每一次定時中斷它都會在中斷函數(shù)里自加一。*只有當(dāng)它的次數(shù)大于或等于設(shè)定上限const_time_level時,*才會去改變LED燈的狀態(tài),否則CPU退出led_flicker()任務(wù),繼續(xù)快速掃描其他的任務(wù),*這樣的程序結(jié)構(gòu)就可以達到多任務(wù)并行處理的目的。這就是鴻哥在所有開發(fā)項目中的核心框架。*/if(uiTimeCnt>=const_time_level)//時間到{/*注釋二:*ET0=0;uiTimeCnt=0;ET0=1;----在清零uiTimeCnt之前,為什么要先禁止定時中斷?*因為uiTimeCnt是unsignedint類型,本質(zhì)上是由兩個字節(jié)組成。*在C語言中uiTimeCnt=0看似一條指令,實際上經(jīng)過編譯之后它不只一條匯編指令。*由于定時中斷函數(shù)里也對這個變量進行累加操作,如果不禁止定時中斷,*那么uiTimeCnt這個變量在main()函數(shù)中還沒被完全清零的時候,如果這個時候*突然來一個定時中斷,并且在中斷里又更改了此變量,這種情況在某些要求高的*項目上會是一個不容易察覺的漏洞,為項目帶來隱患。當(dāng)然,大部分的普通項目,*都可以不用那么嚴(yán)格,可以不用禁止定時中斷。在這里只是提醒各位初學(xué)者有這種情況。*/ET0=0;//禁止定時中斷uiTimeCnt=0;//時間計數(shù)器清零ET0=1;//開啟定時中斷l(xiāng)ed_dr=1;//讓LED亮ucLedStep=1;//切換到下一個步驟}break;case1:if(uiTimeCnt>=const_time_level)//時間到{ET0=0;//禁止定時中斷uiTimeCnt=0;//時間計數(shù)器清零ET0=1;//開啟定時中斷l(xiāng)ed_dr=0;//讓LED滅ucLedStep=0;//返回到上一個步驟}break;}}/*注釋三:*C51的中斷函數(shù)格式如下:*void函數(shù)名()interrupt中斷號*{*中斷程序內(nèi)容*}*函數(shù)名可以隨便取,只要不是編譯器已經(jīng)征用的關(guān)鍵字。*這里最關(guān)鍵的是中斷號,不同的中斷號代表不同類型的中斷。*定時中斷的中斷號是1.至于其它中斷的中斷號,大家可以查找*相關(guān)書籍和資料。大家進入中斷時,必須先清除中斷標(biāo)志,并且*關(guān)閉中斷,然后再寫代碼,最后出來時,記得重裝初始值,并且*打開中斷。*/voidT0_time()interrupt1{TF0=0;//清除中斷標(biāo)志TR0=0;//關(guān)中斷if(uiTimeCnt
向老師學(xué)習(xí)!
0
回復(fù)
開kai
LV.2
17
2014-03-06 10:37
@jianhong_wu
第四節(jié):累計定時中斷次數(shù)使LED燈閃爍。開場白:   上一節(jié)提到在累計主循環(huán)次數(shù)來實現(xiàn)計時,隨著主函數(shù)里任務(wù)量的增加,為了保證延時時間的準(zhǔn)確性,要不斷修正設(shè)定上限閥值const_time_level。我們該怎么解決這個問題呢?本節(jié)教大家利用累計定時中斷次數(shù)的方法來解決這個問題。這一節(jié)要教會大家四個知識點:   第一點:利用累計定時中斷次數(shù)的方法實現(xiàn)時間延時。   第二點:展現(xiàn)鴻哥最完整的實戰(zhàn)程序框架。在主函數(shù)循環(huán)里用switch語句實現(xiàn)狀態(tài)機的切換,在定時中斷里累計中斷次數(shù),這兩個的結(jié)合就是我寫代碼最本質(zhì)的框架思想。     第三點:提醒大家C語言中的int,long變量是由幾個字節(jié)構(gòu)成的數(shù)據(jù),凡是在main函數(shù)和中斷函數(shù)里有可能同時改變的變量,這個變量應(yīng)該在主函數(shù)中被更改之前,先關(guān)閉相應(yīng)的中斷,更改完了此變量,再打開中斷,否則會留下不宜察覺的漏洞。當(dāng)然在大部分的項目中可以不用這么操作,但是在一些要求非常高的項目中,有一些核心變量必須這么做。    第四點:定時中斷的初始值該怎么設(shè)置。不用嚴(yán)格按公式來計算時間,一般取個經(jīng)驗值是最大初始值減去1000就可以了。具體內(nèi)容,請看源代碼講解。(1)硬件平臺:基于朱兆祺51單片機學(xué)習(xí)板。(2)實現(xiàn)功能:讓一個LED閃爍。(3)源代碼講解如下: #include"REG52.H"#defineconst_time_level200voidinitial_myself();voidinitial_peripheral();voiddelay_long(unsignedintuiDelaylong);voidled_flicker();voidT0_time();//定時中斷函數(shù)sbitled_dr=P3^5;unsignedcharucLedStep=0;//步驟變量unsignedintuiTimeCnt=0;//統(tǒng)計定時中斷次數(shù)的延時計數(shù)器voidmain(){initial_myself();delay_long(100);initial_peripheral();while(1){led_flicker();}}voidled_flicker()////第三區(qū)LED閃爍應(yīng)用程序{switch(ucLedStep){case0:/*注釋一:*uiTimeCnt累加定時中斷的次數(shù),每一次定時中斷它都會在中斷函數(shù)里自加一。*只有當(dāng)它的次數(shù)大于或等于設(shè)定上限const_time_level時,*才會去改變LED燈的狀態(tài),否則CPU退出led_flicker()任務(wù),繼續(xù)快速掃描其他的任務(wù),*這樣的程序結(jié)構(gòu)就可以達到多任務(wù)并行處理的目的。這就是鴻哥在所有開發(fā)項目中的核心框架。*/if(uiTimeCnt>=const_time_level)//時間到{/*注釋二:*ET0=0;uiTimeCnt=0;ET0=1;----在清零uiTimeCnt之前,為什么要先禁止定時中斷?*因為uiTimeCnt是unsignedint類型,本質(zhì)上是由兩個字節(jié)組成。*在C語言中uiTimeCnt=0看似一條指令,實際上經(jīng)過編譯之后它不只一條匯編指令。*由于定時中斷函數(shù)里也對這個變量進行累加操作,如果不禁止定時中斷,*那么uiTimeCnt這個變量在main()函數(shù)中還沒被完全清零的時候,如果這個時候*突然來一個定時中斷,并且在中斷里又更改了此變量,這種情況在某些要求高的*項目上會是一個不容易察覺的漏洞,為項目帶來隱患。當(dāng)然,大部分的普通項目,*都可以不用那么嚴(yán)格,可以不用禁止定時中斷。在這里只是提醒各位初學(xué)者有這種情況。*/ET0=0;//禁止定時中斷uiTimeCnt=0;//時間計數(shù)器清零ET0=1;//開啟定時中斷l(xiāng)ed_dr=1;//讓LED亮ucLedStep=1;//切換到下一個步驟}break;case1:if(uiTimeCnt>=const_time_level)//時間到{ET0=0;//禁止定時中斷uiTimeCnt=0;//時間計數(shù)器清零ET0=1;//開啟定時中斷l(xiāng)ed_dr=0;//讓LED滅ucLedStep=0;//返回到上一個步驟}break;}}/*注釋三:*C51的中斷函數(shù)格式如下:*void函數(shù)名()interrupt中斷號*{*中斷程序內(nèi)容*}*函數(shù)名可以隨便取,只要不是編譯器已經(jīng)征用的關(guān)鍵字。*這里最關(guān)鍵的是中斷號,不同的中斷號代表不同類型的中斷。*定時中斷的中斷號是1.至于其它中斷的中斷號,大家可以查找*相關(guān)書籍和資料。大家進入中斷時,必須先清除中斷標(biāo)志,并且*關(guān)閉中斷,然后再寫代碼,最后出來時,記得重裝初始值,并且*打開中斷。*/voidT0_time()interrupt1{TF0=0;//清除中斷標(biāo)志TR0=0;//關(guān)中斷if(uiTimeCnt
學(xué)習(xí).我頂
0
回復(fù)
wheelzhou
LV.9
18
2014-03-06 11:12
@開kai
學(xué)習(xí).我頂
學(xué)習(xí)ing,復(fù)制, 保存了
0
回復(fù)
hzlqz
LV.2
19
2014-03-06 12:55
@jianhong_wu
第四節(jié):累計定時中斷次數(shù)使LED燈閃爍。開場白:   上一節(jié)提到在累計主循環(huán)次數(shù)來實現(xiàn)計時,隨著主函數(shù)里任務(wù)量的增加,為了保證延時時間的準(zhǔn)確性,要不斷修正設(shè)定上限閥值const_time_level。我們該怎么解決這個問題呢?本節(jié)教大家利用累計定時中斷次數(shù)的方法來解決這個問題。這一節(jié)要教會大家四個知識點:   第一點:利用累計定時中斷次數(shù)的方法實現(xiàn)時間延時。   第二點:展現(xiàn)鴻哥最完整的實戰(zhàn)程序框架。在主函數(shù)循環(huán)里用switch語句實現(xiàn)狀態(tài)機的切換,在定時中斷里累計中斷次數(shù),這兩個的結(jié)合就是我寫代碼最本質(zhì)的框架思想。     第三點:提醒大家C語言中的int,long變量是由幾個字節(jié)構(gòu)成的數(shù)據(jù),凡是在main函數(shù)和中斷函數(shù)里有可能同時改變的變量,這個變量應(yīng)該在主函數(shù)中被更改之前,先關(guān)閉相應(yīng)的中斷,更改完了此變量,再打開中斷,否則會留下不宜察覺的漏洞。當(dāng)然在大部分的項目中可以不用這么操作,但是在一些要求非常高的項目中,有一些核心變量必須這么做。    第四點:定時中斷的初始值該怎么設(shè)置。不用嚴(yán)格按公式來計算時間,一般取個經(jīng)驗值是最大初始值減去1000就可以了。具體內(nèi)容,請看源代碼講解。(1)硬件平臺:基于朱兆祺51單片機學(xué)習(xí)板。(2)實現(xiàn)功能:讓一個LED閃爍。(3)源代碼講解如下: #include"REG52.H"#defineconst_time_level200voidinitial_myself();voidinitial_peripheral();voiddelay_long(unsignedintuiDelaylong);voidled_flicker();voidT0_time();//定時中斷函數(shù)sbitled_dr=P3^5;unsignedcharucLedStep=0;//步驟變量unsignedintuiTimeCnt=0;//統(tǒng)計定時中斷次數(shù)的延時計數(shù)器voidmain(){initial_myself();delay_long(100);initial_peripheral();while(1){led_flicker();}}voidled_flicker()////第三區(qū)LED閃爍應(yīng)用程序{switch(ucLedStep){case0:/*注釋一:*uiTimeCnt累加定時中斷的次數(shù),每一次定時中斷它都會在中斷函數(shù)里自加一。*只有當(dāng)它的次數(shù)大于或等于設(shè)定上限const_time_level時,*才會去改變LED燈的狀態(tài),否則CPU退出led_flicker()任務(wù),繼續(xù)快速掃描其他的任務(wù),*這樣的程序結(jié)構(gòu)就可以達到多任務(wù)并行處理的目的。這就是鴻哥在所有開發(fā)項目中的核心框架。*/if(uiTimeCnt>=const_time_level)//時間到{/*注釋二:*ET0=0;uiTimeCnt=0;ET0=1;----在清零uiTimeCnt之前,為什么要先禁止定時中斷?*因為uiTimeCnt是unsignedint類型,本質(zhì)上是由兩個字節(jié)組成。*在C語言中uiTimeCnt=0看似一條指令,實際上經(jīng)過編譯之后它不只一條匯編指令。*由于定時中斷函數(shù)里也對這個變量進行累加操作,如果不禁止定時中斷,*那么uiTimeCnt這個變量在main()函數(shù)中還沒被完全清零的時候,如果這個時候*突然來一個定時中斷,并且在中斷里又更改了此變量,這種情況在某些要求高的*項目上會是一個不容易察覺的漏洞,為項目帶來隱患。當(dāng)然,大部分的普通項目,*都可以不用那么嚴(yán)格,可以不用禁止定時中斷。在這里只是提醒各位初學(xué)者有這種情況。*/ET0=0;//禁止定時中斷uiTimeCnt=0;//時間計數(shù)器清零ET0=1;//開啟定時中斷l(xiāng)ed_dr=1;//讓LED亮ucLedStep=1;//切換到下一個步驟}break;case1:if(uiTimeCnt>=const_time_level)//時間到{ET0=0;//禁止定時中斷uiTimeCnt=0;//時間計數(shù)器清零ET0=1;//開啟定時中斷l(xiāng)ed_dr=0;//讓LED滅ucLedStep=0;//返回到上一個步驟}break;}}/*注釋三:*C51的中斷函數(shù)格式如下:*void函數(shù)名()interrupt中斷號*{*中斷程序內(nèi)容*}*函數(shù)名可以隨便取,只要不是編譯器已經(jīng)征用的關(guān)鍵字。*這里最關(guān)鍵的是中斷號,不同的中斷號代表不同類型的中斷。*定時中斷的中斷號是1.至于其它中斷的中斷號,大家可以查找*相關(guān)書籍和資料。大家進入中斷時,必須先清除中斷標(biāo)志,并且*關(guān)閉中斷,然后再寫代碼,最后出來時,記得重裝初始值,并且*打開中斷。*/voidT0_time()interrupt1{TF0=0;//清除中斷標(biāo)志TR0=0;//關(guān)中斷if(uiTimeCnt
很好,終于找到老師了。
0
回復(fù)
liuqiwei85
LV.6
20
2014-03-06 13:35
@jianhong_wu
第一節(jié):吳堅鴻談初學(xué)單片機的誤區(qū)。(1)很難記住繁雜的寄存器?寄存器不用死記硬背,鴻哥我行走江湖多年,連一個寄存器都記不住。 需要配置寄存器的時候,直接在網(wǎng)上或者書本上參考別人現(xiàn)成的配置程序是上策,查找芯片數(shù)據(jù)手冊是中策,死記硬背寄存器是最最下策。 (2)很難記住繁雜的匯編語言指令?除非是在校學(xué)生要應(yīng)付考試或者少數(shù)工作中繞不開匯編,否則學(xué)匯編就是浪費時間。鴻哥我行走江湖多年,從來就沒有用匯編幫客戶做過一個項目。 (3)C語言很難學(xué)?你不用學(xué)指針,你不用學(xué)帶形參的函數(shù),你不用學(xué)結(jié)構(gòu)體,你不用學(xué)宏定義,你不用學(xué)文件操作,你也不用死記繁瑣的數(shù)據(jù)類型。你只要會:     5條指令語句switch語句,ifelse語句,while語句,for語句,=賦值語句。     7個運算符+,-,*,/,|,&,!。     4個邏輯關(guān)系符||,&&,!=,==.     3個數(shù)據(jù)類型unsignedchar,unsignedint,unsignedlong。      3個進制相互轉(zhuǎn)化,二進制,十六進制,十進制。     1個void函數(shù)。                  1個一維數(shù)組code(或const)unsignedchararray[]。      那么世界上任何一種邏輯功能的單片機軟件你都能做出來。        鴻哥我當(dāng)年剛畢業(yè)出來工作的時候才知道可以用C語言開發(fā)單片機,一開始只用if語句就把項目做出來了,沒有用指針,沒有用帶形參的函數(shù)等復(fù)雜的功能。再到后來才慢慢開始用C語言其他的高級功能,但是我發(fā)現(xiàn)C語言其他的高級功能,本質(zhì)上都是用我前面列舉出來的最基本功能集合而成,只是書寫更加簡單方便了一點,編譯后的機器碼都大同小異。所以不會指針等高級功能你不用自卑,恰恰相反,當(dāng)你會最簡單的幾個語句,就把這些高級功能的程序都做出來了,你才發(fā)現(xiàn)你對底層了解得更加透切,再學(xué)那些高級功能輕而易舉。當(dāng)你裸機跑的程序都能夠協(xié)調(diào)得很好的時候,你才發(fā)現(xiàn)所謂高深的操作系統(tǒng)也不過如此,只要給你時間和金錢你也可以寫個操作系統(tǒng)來玩玩。 (4)很難記住精確時間的計算公式?經(jīng)常看到時間公式等于晶振,時鐘周期,執(zhí)行指令次數(shù)他們之間的乘除關(guān)系式。鴻哥我認(rèn)為這些都是浮云,不用糾結(jié)也不用去記,大概了解一下就可以了。不管你對公式掌握得有多精確,你都不可能做出非常精確的時間。想用單片機做一個非常精確的時間這種想法一開始就是錯的,不可能的。真想做一個比較精確的時間,應(yīng)該用外圍時鐘芯片或者FPGA和CPLD,而不是單片機。(5)很難記住繁雜的各種通信協(xié)議?什么IIC,SPI,232串口通訊,CAN,USB等等。這些都是浮云,你不用記那么多,你只要理解兩種通訊方式就夠了,那就是串行通訊方式和并行通訊方式。不管世界上有多少種通訊協(xié)議,物理世界上只有這兩種通訊方式,其他各種名稱的通訊協(xié)議都基于此兩種方式演變而來。(6)很難寫短小精悍的程序?初學(xué)者不要糾結(jié)于此。做項目開發(fā),程序容量不是刻意追求的目標(biāo),程序多一點少一點沒關(guān)系,現(xiàn)在大容量的單片機品種非常多,容量不會是寸土寸金的事情,我們更加要關(guān)注程序的運行效率,可讀性和可修改性。   既然鴻哥列出了那么多誤區(qū),那么什么才是初學(xué)者關(guān)注的核心?預(yù)知詳情,請聽下回分解----delay()延時實現(xiàn)LED燈的閃爍。(未完待續(xù),下節(jié)更精彩,不要走開哦)

呵呵,從FSY一路追到這里,走到哪里都是焦點,崇拜中

0
回復(fù)
jianhong_wu
LV.4
21
2014-03-06 14:45

第五節(jié):蜂鳴器的驅(qū)動程序。

開場白:

      上一節(jié)講了利用累計定時中斷次數(shù)實現(xiàn)LED燈閃爍,這個例子同時也第一次展示了我最完整的實戰(zhàn)程序框架:用switch語句實現(xiàn)狀態(tài)機,外加定時中斷。這個框架看似簡單,實際上就是那么簡單。我做的所有開發(fā)項目都是基于這個簡單框架,但是非常好用。上一節(jié)只有一個單任務(wù)的LED燈在閃爍,這節(jié)開始,我們多增加一個蜂鳴器報警的任務(wù),要教會大家四個知識點:

     第一點:蜂鳴器的驅(qū)動程序框架編寫。

     第二點:多任務(wù)處理的程序框架。

     第三點:如何控制蜂鳴器聲音的長叫和短叫。

     第四點:如何知道1秒鐘需要多少個定時中斷,也就是如何按比例修正時間精度。具體內(nèi)容,請看源代碼講解。

(1)硬件平臺:基于朱兆祺51單片機學(xué)習(xí)板。

(2)實現(xiàn)功能:同時跑兩個任務(wù),第一個任務(wù)讓一個LED燈1秒鐘閃爍一次。第二個任務(wù)讓蜂鳴器在前面3秒發(fā)生一次短叫報警,在后面6秒發(fā)生一次長叫報警,反復(fù)循環(huán)。

(3)源代碼講解如下:

#include "REG52.H"

/* 注釋一:
* 如何知道1秒鐘需要多少個定時中斷?
* 這個需要編寫一段小程序測試,得到測試的結(jié)果后再按比例修正。
* 步驟:
* 第一步:在程序代碼上先寫入1秒鐘大概需要200個定時中斷。
* 第二步:基于以上1秒鐘的基準(zhǔn),編寫一個60秒的簡單測試程序(如果編寫超過
* 60秒的時間,這個精度還會更高)。比如,編寫一個用蜂鳴器的聲音來識別計時的
* 起始和終止的測試程序。
* 第三步:把程序燒錄進單片機后,上電開始測試,手上同步打開手機里的秒表。
*         如果單片機僅僅跑了27秒。
* 第四步:那么最終得出1秒鐘需要的定時中斷次數(shù)是:const_time_1s=(200*60)/27=444
*/
#define const_time_05s 222   //0.5秒鐘的時間需要的定時中斷次數(shù)
#define const_time_1s 444   //1秒鐘的時間需要的定時中斷次數(shù)
#define const_time_3s 1332   //3秒鐘的時間需要的定時中斷次數(shù)
#define const_time_6s 2664   //6秒鐘的時間需要的定時中斷次數(shù)

#define const_voice_short  40   //蜂鳴器短叫的持續(xù)時間
#define const_voice_long   200  //蜂鳴器長叫的持續(xù)時間

void initial_myself();    
void initial_peripheral();
void delay_long(unsigned int uiDelaylong);
void led_flicker();
void alarm_run();   
void T0_time();  //定時中斷函數(shù)

sbit beep_dr=P2^7; //蜂鳴器的驅(qū)動IO口
sbit led_dr=P3^5;  //LED燈的驅(qū)動IO口

unsigned char ucLedStep=0; //LED燈的步驟變量
unsigned int  uiTimeLedCnt=0; //LED燈統(tǒng)計定時中斷次數(shù)的延時計數(shù)器

unsigned char ucAlarmStep=0; //報警的步驟變量
unsigned int  uiTimeAlarmCnt=0; //報警統(tǒng)計定時中斷次數(shù)的延時計數(shù)器

unsigned int  uiVoiceCnt=0;  //蜂鳴器鳴叫的持續(xù)時間計數(shù)器

void main() 
  {
   initial_myself();  
   delay_long(100);   
   initial_peripheral(); 
   while(1)  
   { 
      led_flicker();  //第一個任務(wù)LED燈閃爍
          alarm_run();    //第二個任務(wù)報警器定時報警
   }

}

void led_flicker() //第三區(qū) LED閃爍應(yīng)用程序
{
  
  switch(ucLedStep)
  {
     case 0:

           if(uiTimeLedCnt>=const_time_05s) //時間到
           {
             uiTimeLedCnt=0; //時間計數(shù)器清零
             led_dr=1;    //讓LED亮
             ucLedStep=1; //切換到下一個步驟
           }
           break;
     case 1:
           if(uiTimeLedCnt>=const_time_05s) //時間到
           {
              uiTimeLedCnt=0; //時間計數(shù)器清零
              led_dr=0;    //讓LED滅
              ucLedStep=0; //返回到上一個步驟
           }
           break;
  }

}

void alarm_run() //第三區(qū) 報警器的應(yīng)用程序
{
  
  switch(ucAlarmStep)
  {
     case 0:

           if(uiTimeAlarmCnt>=const_time_3s) //時間到
           {
             uiTimeAlarmCnt=0; //時間計數(shù)器清零
/* 注釋二:
* 只要變量uiVoiceCnt不為0,蜂鳴器就會在定時中斷函數(shù)里啟動鳴叫,并且自減uiVoiceCnt
* 直到uiVoiceCnt為0時才停止鳴叫。因此控制uiVoiceCnt變量的大小就是控制聲音的長短。
*/
             uiVoiceCnt=const_voice_short;  //蜂鳴器短叫
             ucAlarmStep=1; //切換到下一個步驟
           }
           break;
     case 1:
           if(uiTimeAlarmCnt>=const_time_6s) //時間到
           {
              uiTimeAlarmCnt=0; //時間計數(shù)器清零
              uiVoiceCnt=const_voice_long;  //蜂鳴器長叫
              ucAlarmStep=0; //返回到上一個步驟
           }
           break;
  }

}

void T0_time() interrupt 1
{
  TF0=0;  //清除中斷標(biāo)志
  TR0=0; //關(guān)中斷

  if(uiTimeLedCnt<0xffff)  //設(shè)定這個條件,防止uiTimeLedCnt超范圍。
  {
      uiTimeLedCnt++;  //LED燈的時間計數(shù)器,累加定時中斷的次數(shù),
  }

  if(uiTimeAlarmCnt<0xffff)  //設(shè)定這個條件,防止uiTimeAlarmCnt超范圍。
  {
      uiTimeAlarmCnt++;  //報警的時間計數(shù)器,累加定時中斷的次數(shù),
  }


/* 注釋三:
* 為什么不把驅(qū)動蜂鳴器這段代碼放到main函數(shù)的循環(huán)里去?
* 因為放在定時中斷里,能保證蜂鳴器的聲音長度是一致的,
* 如果放在main循環(huán)里,聲音的長度就有可能受到某些必須
* 一氣呵成的任務(wù)干擾,得不到及時響應(yīng),影響聲音長度的一致性。
*/


  if(uiVoiceCnt!=0)
  {
     uiVoiceCnt--; //每次進入定時中斷都自減1,直到等于零為止。才停止鳴叫
         beep_dr=0;  //蜂鳴器是PNP三極管控制,低電平就開始鳴叫。
  }
  else
  {
     ; //此處多加一個空指令,想維持跟if括號語句的數(shù)量對稱,都是兩條指令。不加也可以。
           beep_dr=1;  //蜂鳴器是PNP三極管控制,高電平就停止鳴叫。
  }


  TH0=0xf8;   //重裝初始值(65535-2000)=63535=0xf82f
  TL0=0x2f;
  TR0=1;  //開中斷
}


void delay_long(unsigned int uiDelayLong)
{
   unsigned int i;
   unsigned int j;
   for(i=0;i

 

總結(jié)陳詞:

     本節(jié)程序已經(jīng)展示了一個多任務(wù)處理的基本思路,假如要實現(xiàn)一個獨立按鍵檢測,能不能也按照這種思路來處理呢?欲知詳情,請聽下回分解-----在主函數(shù)中利用累計主循環(huán)次數(shù)來實現(xiàn)獨立按鍵的檢測。

 

(未完待續(xù),下節(jié)更精彩,不要走開哦)

 

0
回復(fù)
jianhong_wu
LV.4
22
2014-03-06 14:48
@liuqiwei85
呵呵,從FSY一路追到這里,走到哪里都是焦點,崇拜中
謝謝,聽你這么說我很開心。希望我分享的東西能對你有幫助。
0
回復(fù)
jianhong_wu
LV.4
23
2014-03-06 14:54

第六節(jié):在主函數(shù)中利用累計主循環(huán)次數(shù)來實現(xiàn)獨立按鍵的檢測。

開場白:

     上一節(jié)講了多任務(wù)中蜂鳴器驅(qū)動程序的框架,這節(jié)繼續(xù)利用多任務(wù)處理的方式,在主函數(shù)中利用累計主循環(huán)次數(shù)來實現(xiàn)獨立按鍵的檢測。要教會大家四個知識點:

     第一點:獨立按鍵的驅(qū)動程序框架。

     第二點:用累計主循環(huán)次數(shù)來實現(xiàn)去抖動的延時。

     第三點:靈活運用防止按鍵不松手后一直觸發(fā)的按鍵自鎖標(biāo)志。

     第四點:在按鍵去抖動延時計時中,添加一個抗干擾的軟件監(jiān)控判斷。一旦發(fā)現(xiàn)瞬間雜波干擾,馬上把延時計數(shù)器清零。這種方法是我在復(fù)雜的工控項目中總結(jié)出來的。以后凡是用到開關(guān)感應(yīng)器的地方,都可以用類似的方法實現(xiàn)軟件上的抗干擾處理。具體內(nèi)容,請看源代碼講解。

(1)硬件平臺:基于朱兆祺51單片機學(xué)習(xí)板。用矩陣鍵盤中的S1和S5號鍵作為獨立按鍵,記得把輸出線P0.4一直輸出低電平,模擬獨立按鍵的觸發(fā)地GND。

(2)實現(xiàn)功能:有兩個獨立按鍵,每按一個獨立按鍵,蜂鳴器發(fā)出“滴”的一聲后就停。

(3)源代碼講解如下: 

#include "REG52.H"

#define const_voice_short  40   //蜂鳴器短叫的持續(xù)時間


/* 注釋一:
* 調(diào)整抖動時間閥值的大小,可以更改按鍵的觸發(fā)靈敏度。
* 去抖動的時間本質(zhì)上等于累計主循環(huán)次數(shù)的時間。
*/
#define const_key_time1  500    //按鍵去抖動延時的時間
#define const_key_time2  500    //按鍵去抖動延時的時間

void initial_myself();    
void initial_peripheral();
void delay_long(unsigned int uiDelaylong);
void T0_time();  //定時中斷函數(shù)
void key_service(); //按鍵服務(wù)的應(yīng)用程序
void key_scan(); //按鍵掃描函數(shù)

sbit key_sr1=P0^0; //對應(yīng)朱兆祺學(xué)習(xí)板的S1鍵
sbit key_sr2=P0^1; //對應(yīng)朱兆祺學(xué)習(xí)板的S5鍵
sbit key_gnd_dr=P0^4; //模擬獨立按鍵的地GND,因此必須一直輸出低電平

sbit beep_dr=P2^7; //蜂鳴器的驅(qū)動IO口

unsigned char ucKeySec=0;   //被觸發(fā)的按鍵編號

unsigned int  uiKeyTimeCnt1=0; //按鍵去抖動延時計數(shù)器
unsigned char ucKeyLock1=0; //按鍵觸發(fā)后自鎖的變量標(biāo)志

unsigned int  uiKeyTimeCnt2=0; //按鍵去抖動延時計數(shù)器
unsigned char ucKeyLock2=0; //按鍵觸發(fā)后自鎖的變量標(biāo)志

unsigned int  uiVoiceCnt=0;  //蜂鳴器鳴叫的持續(xù)時間計數(shù)器

void main() 
  {
   initial_myself();  
   delay_long(100);   
   initial_peripheral(); 
   while(1)  
   { 
       key_scan(); //按鍵掃描函數(shù)
       key_service(); //按鍵服務(wù)的應(yīng)用程序
   }

}

void key_scan()//按鍵掃描函數(shù)
{  
/* 注釋二:
* 獨立按鍵掃描的詳細過程:
* 第一步:平時沒有按鍵被觸發(fā)時,按鍵的自鎖標(biāo)志和去抖動延時計數(shù)器一直被清零。
* 第二步:一旦有按鍵被按下,去抖動延時計數(shù)器開始累加,在還沒累加到
*         閥值const_key_time1時,如果在這期間由于受外界干擾或者按鍵抖動,而使
*         IO口突然瞬間觸發(fā)成高電平,這個時候馬上又把延時計數(shù)器uiKeyTimeCnt1
*         清零了,這個過程非常巧妙,非常有效地去除瞬間的雜波干擾。這是我實戰(zhàn)中摸索出來的。
*         以后凡是用到開關(guān)感應(yīng)器的時候,都可以用類似這樣的方法去干擾。
* 第三步:如果按鍵按下的時間超過了閥值const_key_time1,則觸發(fā)按鍵,把編號ucKeySec賦值。
*         同時,馬上把自鎖標(biāo)志ucKeyLock1置位,防止按住按鍵不松手后一直觸發(fā)。
* 第四步:等按鍵松開后,自鎖標(biāo)志ucKeyLock1及時清零,為下一次自鎖做準(zhǔn)備。
* 第五步:以上整個過程,就是識別按鍵IO口下降沿觸發(fā)的過程。
*/
  if(key_sr1==1)//IO是高電平,說明按鍵沒有被按下,這時要及時清零一些標(biāo)志位
  {
     ucKeyLock1=0; //按鍵自鎖標(biāo)志清零
         uiKeyTimeCnt1=0;//按鍵去抖動延時計數(shù)器清零,此行非常巧妙,是我實戰(zhàn)中摸索出來的。      
  }
  else if(ucKeyLock1==0)//有按鍵按下,且是第一次被按下
  {
     ++uiKeyTimeCnt1;  //延時計數(shù)器
     if(uiKeyTimeCnt1>const_key_time1)
     {
        uiKeyTimeCnt1=0; 
        ucKeyLock1=1;  //自鎖按鍵置位,避免一直觸發(fā)
        ucKeySec=1;    //觸發(fā)1號鍵
     }
  }

  if(key_sr2==1)
  {
     ucKeyLock2=0; 
         uiKeyTimeCnt2=0;
  }
  else if(ucKeyLock2==0)
  {
     ++uiKeyTimeCnt2; 
     if(uiKeyTimeCnt2>const_key_time2)
     {
        uiKeyTimeCnt2=0;
        ucKeyLock2=1; 
        ucKeySec=2;     //觸發(fā)2號鍵
     }
  }

}


void key_service() //第三區(qū) 按鍵服務(wù)的應(yīng)用程序
{
  switch(ucKeySec) //按鍵服務(wù)狀態(tài)切換
  {
    case 1:// 1號鍵 對應(yīng)朱兆祺學(xué)習(xí)板的S1鍵

              uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。
              ucKeySec=0;  //響應(yīng)按鍵服務(wù)處理程序后,按鍵編號清零,避免一致觸發(fā)
          break;        
    case 2:// 2號鍵 對應(yīng)朱兆祺學(xué)習(xí)板的S5鍵

              uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。
              ucKeySec=0;  //響應(yīng)按鍵服務(wù)處理程序后,按鍵編號清零,避免一致觸發(fā)
          break;                    
  }                
}



void T0_time() interrupt 1
{
  TF0=0;  //清除中斷標(biāo)志
  TR0=0; //關(guān)中斷

  if(uiVoiceCnt!=0)
  {
     uiVoiceCnt--; //每次進入定時中斷都自減1,直到等于零為止。才停止鳴叫
         beep_dr=0;  //蜂鳴器是PNP三極管控制,低電平就開始鳴叫。
  }
  else
  {
     ; //此處多加一個空指令,想維持跟if括號語句的數(shù)量對稱,都是兩條指令。不加也可以。
           beep_dr=1;  //蜂鳴器是PNP三極管控制,高電平就停止鳴叫。
  }


  TH0=0xf8;   //重裝初始值(65535-2000)=63535=0xf82f
  TL0=0x2f;
  TR0=1;  //開中斷
}


void delay_long(unsigned int uiDelayLong)
{
   unsigned int i;
   unsigned int j;
   for(i=0;i
 

總結(jié)陳詞:   

      本節(jié)程序已經(jīng)展示了在主函數(shù)中,利用累計主循環(huán)次數(shù)來實現(xiàn)獨立按鍵的檢測。這種方法我經(jīng)常在實戰(zhàn)用應(yīng)用,但是它也有一個小小的不足,隨著在主函數(shù)循環(huán)中任務(wù)量的增加,為了保證去抖動延時的時間一致性,要適當(dāng)調(diào)整一下去抖動的閥值const_key_time1。如何解決這個問題呢?欲知詳情,請聽下回分解-----在主函數(shù)中利用累計定時中斷的次數(shù)來實現(xiàn)獨立按鍵的檢測。

 

(未完待續(xù),下節(jié)更精彩,不要走開哦)

 

0
回復(fù)
jianhong_wu
LV.4
24
2014-03-06 15:02

第七節(jié):在主函數(shù)中利用累計定時中斷的次數(shù)來實現(xiàn)獨立按鍵的檢測。

開場白:

      上一節(jié)講了在主函數(shù)中利用累計主循環(huán)次數(shù)來實現(xiàn)獨立按鍵的檢測,但是它也有一個小小的不足,隨著在主函數(shù)中任務(wù)量的增加,為了保證去抖動延時的時間一致性,要適當(dāng)調(diào)整一下去抖動的時間閥值const_key_time1。如何解決這個問題呢?這一節(jié)教大家在主函數(shù)中利用累計定時中斷的次數(shù)來實現(xiàn)獨立按鍵的檢測,可以有效地避免這個問題。要教會大家一個知識點:如何在上一節(jié)的基礎(chǔ)上,略作修改,就可以在主函數(shù)中,利用累計定時中斷的次數(shù)來實現(xiàn)去抖動的延時。

      具體內(nèi)容,請看源代碼講解。

(1)硬件平臺:基于朱兆祺51單片機學(xué)習(xí)板。用矩陣鍵盤中的S1和S5號鍵作為獨立按鍵,記得把輸出線P0.4一直輸出低電平,模擬獨立按鍵的觸發(fā)地GND。

(2)實現(xiàn)功能:有兩個獨立按鍵,每按一個獨立按鍵,蜂鳴器發(fā)出“滴”的一聲后就停。

(3)源代碼講解如下:

#include "REG52.H"

#define const_voice_short  40   //蜂鳴器短叫的持續(xù)時間


/* 注釋一:
* 調(diào)整抖動時間閥值的大小,可以更改按鍵的觸發(fā)靈敏度。
* 去抖動的時間本質(zhì)上等于累計定時中斷次數(shù)的時間。
*/
#define const_key_time1  30    //按鍵去抖動延時的時間
#define const_key_time2  30    //按鍵去抖動延時的時間

void initial_myself();    
void initial_peripheral();
void delay_long(unsigned int uiDelaylong);
void T0_time();  //定時中斷函數(shù)
void key_service(); //按鍵服務(wù)的應(yīng)用程序
void key_scan(); //按鍵掃描函數(shù)

sbit key_sr1=P0^0; //對應(yīng)朱兆祺學(xué)習(xí)板的S1鍵
sbit key_sr2=P0^1; //對應(yīng)朱兆祺學(xué)習(xí)板的S5鍵
sbit key_gnd_dr=P0^4; //模擬獨立按鍵的地GND,因此必須一直輸出低電平

sbit beep_dr=P2^7; //蜂鳴器的驅(qū)動IO口

unsigned char ucKeySec=0;   //被觸發(fā)的按鍵編號

unsigned char ucKeyStartFlag1=0; //啟動定時中斷計數(shù)的開關(guān)
unsigned int  uiKeyTimeCnt1=0; //按鍵去抖動延時計數(shù)器
unsigned char ucKeyLock1=0; //按鍵觸發(fā)后自鎖的變量標(biāo)志

unsigned char ucKeyStartFlag2=0; //啟動定時中斷計數(shù)的開關(guān)
unsigned int  uiKeyTimeCnt2=0; //按鍵去抖動延時計數(shù)器
unsigned char ucKeyLock2=0; //按鍵觸發(fā)后自鎖的變量標(biāo)志

unsigned int  uiVoiceCnt=0;  //蜂鳴器鳴叫的持續(xù)時間計數(shù)器

void main() 
  {
   initial_myself();  
   delay_long(100);   
   initial_peripheral(); 
   while(1)  
   { 
       key_scan(); //按鍵掃描函數(shù)
       key_service(); //按鍵服務(wù)的應(yīng)用程序
   }

}

void key_scan()//按鍵掃描函數(shù)
{  
/* 注釋二:
* 獨立按鍵掃描的詳細過程:
* 第一步:平時沒有按鍵被觸發(fā)時,按鍵的自鎖標(biāo)志,計時器開關(guān)和去抖動延時計數(shù)器一直被清零。
* 第二步:一旦有按鍵被按下,啟動計時器,去抖動延時計數(shù)器開始在定時中斷函數(shù)里累加,在還沒累加到
*         閥值const_key_time1時,如果在這期間由于受外界干擾或者按鍵抖動,而使
*         IO口突然瞬間觸發(fā)成高電平,這個時候馬上停止計時,并且把延時計數(shù)器uiKeyTimeCnt1
*         清零了,這個過程非常巧妙,非常有效地去除瞬間的雜波干擾。這是我實戰(zhàn)中摸索出來的。
*         以后凡是用到開關(guān)感應(yīng)器的時候,都可以用類似這樣的方法去干擾。
* 第三步:如果按鍵按下的時間超過了閥值const_key_time1,則觸發(fā)按鍵,把編號ucKeySec賦值。
*         同時,馬上把自鎖標(biāo)志ucKeyLock1置位,防止按住按鍵不松手后一直觸發(fā)。
* 第四步:等按鍵松開后,自鎖標(biāo)志ucKeyLock1及時清零,為下一次自鎖做準(zhǔn)備。
* 第五步:以上整個過程,就是識別按鍵IO口下降沿觸發(fā)的過程。
*/
  if(key_sr1==1)//IO是高電平,說明按鍵沒有被按下,這時要及時清零一些標(biāo)志位
  {
     ucKeyLock1=0; //按鍵自鎖標(biāo)志清零
         ucKeyStartFlag1=0; //停止計數(shù)器
         uiKeyTimeCnt1=0;//按鍵去抖動延時計數(shù)器清零,此行非常巧妙,是我實戰(zhàn)中摸索出來的。      
  }
  else if(ucKeyLock1==0)//有按鍵按下,且是第一次被按下
  {
         ucKeyStartFlag1=1; //啟動計數(shù)器
     if(uiKeyTimeCnt1>const_key_time1)
     {
                 ucKeyStartFlag1=0; //停止計數(shù)器
        uiKeyTimeCnt1=0; 
        ucKeyLock1=1;  //自鎖按鍵置位,避免一直觸發(fā)
        ucKeySec=1;    //觸發(fā)1號鍵
     }
  }

  if(key_sr2==1)
  {
     ucKeyLock2=0; 
         ucKeyStartFlag2=0; //停止計數(shù)器
         uiKeyTimeCnt2=0;
  }
  else if(ucKeyLock2==0)
  {
         ucKeyStartFlag2=1; //啟動計數(shù)器
     if(uiKeyTimeCnt2>const_key_time2)
     {
            ucKeyStartFlag2=0; //停止計數(shù)器
        uiKeyTimeCnt2=0;
        ucKeyLock2=1; 
        ucKeySec=2;     //觸發(fā)2號鍵
     }
  }

}


void key_service() //第三區(qū) 按鍵服務(wù)的應(yīng)用程序
{
  switch(ucKeySec) //按鍵服務(wù)狀態(tài)切換
  {
    case 1:// 1號鍵 對應(yīng)朱兆祺學(xué)習(xí)板的S1鍵

              uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。
              ucKeySec=0;  //響應(yīng)按鍵服務(wù)處理程序后,按鍵編號清零,避免一致觸發(fā)
          break;        
    case 2:// 2號鍵 對應(yīng)朱兆祺學(xué)習(xí)板的S5鍵

              uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。
              ucKeySec=0;  //響應(yīng)按鍵服務(wù)處理程序后,按鍵編號清零,避免一致觸發(fā)
          break;                    
  }                
}



void T0_time() interrupt 1
{
  TF0=0;  //清除中斷標(biāo)志
  TR0=0; //關(guān)中斷


  if(ucKeyStartFlag1==1)//啟動計數(shù)器
  {
     if(uiKeyTimeCnt1<0xffff)  //防止計數(shù)器超范圍
         {
            uiKeyTimeCnt1++;
         }
  }

   if(ucKeyStartFlag2==1)//啟動計數(shù)器
  {
     if(uiKeyTimeCnt2<0xffff) //防止計數(shù)器超范圍
         {
            uiKeyTimeCnt2++;
         }
  }

  if(uiVoiceCnt!=0)
  {
     uiVoiceCnt--; //每次進入定時中斷都自減1,直到等于零為止。才停止鳴叫
         beep_dr=0;  //蜂鳴器是PNP三極管控制,低電平就開始鳴叫。
  }
  else
  {
     ; //此處多加一個空指令,想維持跟if括號語句的數(shù)量對稱,都是兩條指令。不加也可以。
           beep_dr=1;  //蜂鳴器是PNP三極管控制,高電平就停止鳴叫。
  }


  TH0=0xf8;   //重裝初始值(65535-2000)=63535=0xf82f
  TL0=0x2f;
  TR0=1;  //開中斷
}


void delay_long(unsigned int uiDelayLong)
{
   unsigned int i;
   unsigned int j;
   for(i=0;i

 

總結(jié)陳詞:

      本節(jié)程序已經(jīng)展示了在主函數(shù)中,利用累計定時中斷次數(shù)來實現(xiàn)獨立按鍵的檢測。這種方法我也經(jīng)常在實戰(zhàn)用應(yīng)用,但是如果在某些項目中,需要在主函數(shù)里間歇性地執(zhí)行一些一氣呵成的耗時任務(wù),這種方法就不是很實用,因為當(dāng)主函數(shù)正在處理一氣呵成的耗時任務(wù)時,這個時候如果有按鍵按下來,就有可能沒有及時被響應(yīng)到而遺漏了。那有什么方法可以解決這類項目中遇到的問題?欲知詳情,請聽下回分解-----在定時中斷函數(shù)里執(zhí)行獨立按鍵的掃描程序。

 

(未完待續(xù),下節(jié)更精彩,不要走開哦)

 

1
回復(fù)
jianhong_wu
LV.4
25
2014-03-06 15:07

第八節(jié):在定時中斷函數(shù)里執(zhí)行獨立按鍵的掃描程序。

開場白:

      上一節(jié)講了在主函數(shù)中利用累計定時中斷的次數(shù)來實現(xiàn)獨立按鍵的檢測,但是如果在某些項目中,需要在主函數(shù)里間歇性地執(zhí)行一些一氣呵成的耗時任務(wù),當(dāng)主函數(shù)正在處理一氣呵成的耗時任務(wù)時(前提是沒有關(guān)閉定時器中斷),這個時候如果有按鍵按下來,就有可能沒有及時被響應(yīng)到而遺漏了。在定時中斷函數(shù)里處理獨立按鍵的掃描程序,可以避免這個問題。要教會大家一個知識點:如何在上一節(jié)的基礎(chǔ)上,略作修改,就可以在定時中斷函數(shù)里處理獨立按鍵的掃描程序。具體內(nèi)容,請看源代碼講解。

(1)硬件平臺:基于朱兆祺51單片機學(xué)習(xí)板。用矩陣鍵盤中的S1和S5號鍵作為獨立按鍵,記得把輸出線P0.4一直輸出低電平,模擬獨立按鍵的觸發(fā)地GND。

(2)實現(xiàn)功能:有兩個獨立按鍵,每按一個獨立按鍵,蜂鳴器發(fā)出“滴”的一聲后就停。

(3)源代碼講解如下:

#include "REG52.H"

#define const_voice_short  40   //蜂鳴器短叫的持續(xù)時間


/* 注釋一:
* 調(diào)整抖動時間閥值的大小,可以更改按鍵的觸發(fā)靈敏度。
* 去抖動的時間本質(zhì)上等于累計定時中斷次數(shù)的時間。
*/
#define const_key_time1  20    //按鍵去抖動延時的時間
#define const_key_time2  20    //按鍵去抖動延時的時間

void initial_myself();    
void initial_peripheral();
void delay_long(unsigned int uiDelaylong);
void T0_time();  //定時中斷函數(shù)
void key_service(); //按鍵服務(wù)的應(yīng)用程序
void key_scan(); //按鍵掃描函數(shù) 放在定時中斷里

sbit key_sr1=P0^0; //對應(yīng)朱兆祺學(xué)習(xí)板的S1鍵
sbit key_sr2=P0^1; //對應(yīng)朱兆祺學(xué)習(xí)板的S5鍵
sbit key_gnd_dr=P0^4; //模擬獨立按鍵的地GND,因此必須一直輸出低電平

sbit beep_dr=P2^7; //蜂鳴器的驅(qū)動IO口

unsigned char ucKeySec=0;   //被觸發(fā)的按鍵編號

unsigned int  uiKeyTimeCnt1=0; //按鍵去抖動延時計數(shù)器
unsigned char ucKeyLock1=0; //按鍵觸發(fā)后自鎖的變量標(biāo)志


unsigned int  uiKeyTimeCnt2=0; //按鍵去抖動延時計數(shù)器
unsigned char ucKeyLock2=0; //按鍵觸發(fā)后自鎖的變量標(biāo)志

unsigned int  uiVoiceCnt=0;  //蜂鳴器鳴叫的持續(xù)時間計數(shù)器

void main() 
  {
   initial_myself();  
   delay_long(100);   
   initial_peripheral(); 
   while(1)  
   { 
       key_service(); //按鍵服務(wù)的應(yīng)用程序
   }

}

void key_scan()//按鍵掃描函數(shù) 放在定時中斷里
{  
/* 注釋二:
* 獨立按鍵掃描的詳細過程:
* 第一步:平時沒有按鍵被觸發(fā)時,按鍵的自鎖標(biāo)志,去抖動延時計數(shù)器一直被清零。
* 第二步:一旦有按鍵被按下,去抖動延時計數(shù)器開始在定時中斷函數(shù)里累加,在還沒累加到
*         閥值const_key_time1時,如果在這期間由于受外界干擾或者按鍵抖動,而使
*         IO口突然瞬間觸發(fā)成高電平,這個時候馬上把延時計數(shù)器uiKeyTimeCnt1
*         清零了,這個過程非常巧妙,非常有效地去除瞬間的雜波干擾。這是我實戰(zhàn)中摸索出來的。
*         以后凡是用到開關(guān)感應(yīng)器的時候,都可以用類似這樣的方法去干擾。
* 第三步:如果按鍵按下的時間超過了閥值const_key_time1,則觸發(fā)按鍵,把編號ucKeySec賦值。
*         同時,馬上把自鎖標(biāo)志ucKeyLock1置位,防止按住按鍵不松手后一直觸發(fā)。
* 第四步:等按鍵松開后,自鎖標(biāo)志ucKeyLock1及時清零,為下一次自鎖做準(zhǔn)備。
* 第五步:以上整個過程,就是識別按鍵IO口下降沿觸發(fā)的過程。
*/
  if(key_sr1==1)//IO是高電平,說明按鍵沒有被按下,這時要及時清零一些標(biāo)志位
  {
     ucKeyLock1=0; //按鍵自鎖標(biāo)志清零
         uiKeyTimeCnt1=0;//按鍵去抖動延時計數(shù)器清零,此行非常巧妙,是我實戰(zhàn)中摸索出來的。      
  }
  else if(ucKeyLock1==0)//有按鍵按下,且是第一次被按下
  {
     uiKeyTimeCnt1++; //累加定時中斷次數(shù)
     if(uiKeyTimeCnt1>const_key_time1)
     {
        uiKeyTimeCnt1=0; 
        ucKeyLock1=1;  //自鎖按鍵置位,避免一直觸發(fā)
        ucKeySec=1;    //觸發(fā)1號鍵
     }
  }

  if(key_sr2==1)
  {
     ucKeyLock2=0; 
         uiKeyTimeCnt2=0;
  }
  else if(ucKeyLock2==0)
  {
     uiKeyTimeCnt2++; //累加定時中斷次數(shù)
     if(uiKeyTimeCnt2>const_key_time2)
     {
        uiKeyTimeCnt2=0;
        ucKeyLock2=1; 
        ucKeySec=2;     //觸發(fā)2號鍵
     }
  }

}


void key_service() //第三區(qū) 按鍵服務(wù)的應(yīng)用程序
{
  switch(ucKeySec) //按鍵服務(wù)狀態(tài)切換
  {
    case 1:// 1號鍵 對應(yīng)朱兆祺學(xué)習(xí)板的S1鍵

              uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。
              ucKeySec=0;  //響應(yīng)按鍵服務(wù)處理程序后,按鍵編號清零,避免一致觸發(fā)
          break;        
    case 2:// 2號鍵 對應(yīng)朱兆祺學(xué)習(xí)板的S5鍵

              uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。
              ucKeySec=0;  //響應(yīng)按鍵服務(wù)處理程序后,按鍵編號清零,避免一致觸發(fā)
          break;                    
  }                
}



void T0_time() interrupt 1
{
  TF0=0;  //清除中斷標(biāo)志
  TR0=0; //關(guān)中斷

  key_scan(); //按鍵掃描函數(shù)

  if(uiVoiceCnt!=0)
  {
     uiVoiceCnt--; //每次進入定時中斷都自減1,直到等于零為止。才停止鳴叫
         beep_dr=0;  //蜂鳴器是PNP三極管控制,低電平就開始鳴叫。
  }
  else
  {
     ; //此處多加一個空指令,想維持跟if括號語句的數(shù)量對稱,都是兩條指令。不加也可以。
           beep_dr=1;  //蜂鳴器是PNP三極管控制,高電平就停止鳴叫。
  }


  TH0=0xf8;   //重裝初始值(65535-2000)=63535=0xf82f
  TL0=0x2f;
  TR0=1;  //開中斷
}


void delay_long(unsigned int uiDelayLong)
{
   unsigned int i;
   unsigned int j;
   for(i=0;i

 

總結(jié)陳詞:

     本節(jié)程序已經(jīng)展示了在定時中斷函數(shù)里執(zhí)行獨立按鍵的掃描程序。這節(jié)和前面兩節(jié)所講的掃描方式,我都在項目上用過,具體跟項目的側(cè)重點不同來選擇不同的方式,我本人用得最多的就是當(dāng)前這種方式。假如要獨立按鍵實現(xiàn)類似鼠標(biāo)的雙擊功能,我們改怎么寫程序?欲知詳情,請聽下回分解-----獨立按鍵的雙擊按鍵觸發(fā)。

 

(未完待續(xù),下節(jié)更精彩,不要走開哦)

 

0
回復(fù)
jianhong_wu
LV.4
26
2014-03-06 15:12

第九節(jié):獨立按鍵的雙擊按鍵觸發(fā)。

開場白:

      上一節(jié)講了在定時中斷函數(shù)里處理獨立按鍵的掃描程序,這種結(jié)構(gòu)的程序我用在了很多項目上。這一節(jié)教大家如何實現(xiàn)按鍵雙擊觸發(fā)的功能,這種功能類似鼠標(biāo)的雙擊。要教會大家一個知識點:如何在上一節(jié)的基礎(chǔ)上,略作修改,就可以實現(xiàn)按鍵的雙擊功能。

       具體內(nèi)容,請看源代碼講解。

(1)硬件平臺:基于朱兆祺51單片機學(xué)習(xí)板。用矩陣鍵盤中的S1和S5號鍵作為獨立按鍵,記得把輸出線P0.4一直輸出低電平,模擬獨立按鍵的觸發(fā)地GND。

(2)實現(xiàn)功能:有兩個獨立按鍵,每雙擊一個獨立按鍵,蜂鳴器發(fā)出“滴”的一聲后就停。

(3)源代碼講解如下:

#include "REG52.H"

#define const_voice_short  40   //蜂鳴器短叫的持續(xù)時間


/* 注釋一:
* 調(diào)整抖動時間閥值的大小,可以更改按鍵的觸發(fā)靈敏度。
* 去抖動的時間本質(zhì)上等于累計定時中斷次數(shù)的時間。
*/
#define const_key_time1  20    //按鍵去抖動延時的時間
#define const_key_time2  20    //按鍵去抖動延時的時間

/* 注釋二:
* 有效時間差,是指連續(xù)兩次按鍵觸發(fā)的最大有效間隔時間。
* 如果雙擊的兩個按鍵按下的時間間隔太長,則視為無效雙擊。
*/
#define const_interval_time1  200     //連續(xù)兩次按鍵之間的有效時間差
#define const_interval_time2  200     //連續(xù)兩次按鍵之間的有效時間差

void initial_myself();    
void initial_peripheral();
void delay_long(unsigned int uiDelaylong);
void T0_time();  //定時中斷函數(shù)
void key_service(); //按鍵服務(wù)的應(yīng)用程序
void key_scan(); //按鍵掃描函數(shù) 放在定時中斷里

sbit key_sr1=P0^0; //對應(yīng)朱兆祺學(xué)習(xí)板的S1鍵
sbit key_sr2=P0^1; //對應(yīng)朱兆祺學(xué)習(xí)板的S5鍵
sbit key_gnd_dr=P0^4; //模擬獨立按鍵的地GND,因此必須一直輸出低電平

sbit beep_dr=P2^7; //蜂鳴器的驅(qū)動IO口

unsigned char ucKeySec=0;   //被觸發(fā)的按鍵編號

unsigned int  uiKeyTimeCnt1=0; //按鍵去抖動延時計數(shù)器
unsigned char ucKeyLock1=0; //按鍵觸發(fā)后自鎖的變量標(biāo)志
unsigned char ucKeyTouchCnt1=0; //按鍵按下的次數(shù)記錄
unsigned int  uiKeyIntervalCnt1=0; //按鍵間隔的時間計數(shù)器

unsigned int  uiKeyTimeCnt2=0; //按鍵去抖動延時計數(shù)器
unsigned char ucKeyLock2=0; //按鍵觸發(fā)后自鎖的變量標(biāo)志
unsigned char ucKeyTouchCnt2=0; //按鍵按下的次數(shù)記錄
unsigned int  uiKeyIntervalCnt2=0; //按鍵間隔的時間計數(shù)器

unsigned int  uiVoiceCnt=0;  //蜂鳴器鳴叫的持續(xù)時間計數(shù)器

void main() 
  {
   initial_myself();  
   delay_long(100);   
   initial_peripheral(); 
   while(1)  
   { 
       key_service(); //按鍵服務(wù)的應(yīng)用程序
   }

}

void key_scan()//按鍵掃描函數(shù) 放在定時中斷里
{  
/* 注釋三:
* 獨立雙擊按鍵掃描的詳細過程:
* 第一步:平時沒有按鍵被觸發(fā)時,按鍵的自鎖標(biāo)志,去抖動延時計數(shù)器一直被清零。
*         如果之前已經(jīng)有按鍵觸發(fā)過一次,那么啟動時間間隔計數(shù)器uiKeyIntervalCnt1,
*         在這個允許的時間差范圍內(nèi),如果一直沒有第二次按鍵觸發(fā),則把累加按鍵觸發(fā)的
*         次數(shù)ucKeyTouchCnt1也清零。
* 第二步:一旦有按鍵被按下,去抖動延時計數(shù)器開始在定時中斷函數(shù)里累加,在還沒累加到
*         閥值const_key_time1時,如果在這期間由于受外界干擾或者按鍵抖動,而使
*         IO口突然瞬間觸發(fā)成高電平,這個時候馬上把延時計數(shù)器uiKeyTimeCnt1
*         清零了,這個過程非常巧妙,非常有效地去除瞬間的雜波干擾。這是我實戰(zhàn)中摸索出來的。
*         以后凡是用到開關(guān)感應(yīng)器的時候,都可以用類似這樣的方法去干擾。
* 第三步:如果按鍵按下的時間超過了閥值const_key_time1,馬上把自鎖標(biāo)志ucKeyLock1置位,
*         防止按住按鍵不松手后一直觸發(fā)。與此同時,累加一次按鍵次數(shù),如果按鍵次數(shù)累加有兩次以上,
*         則認(rèn)為觸發(fā)雙擊按鍵,并把編號ucKeySec賦值。 
* 第四步:等按鍵松開后,自鎖標(biāo)志ucKeyLock1及時清零,為下一次自鎖做準(zhǔn)備。并且累加間隔時間,
*         防止兩次按鍵的間隔時間太長。
* 第五步:以上整個過程,就是識別按鍵IO口下降沿觸發(fā)的過程。
*/
  if(key_sr1==1)//IO是高電平,說明按鍵沒有被按下,這時要及時清零一些標(biāo)志位
  {
         ucKeyLock1=0; //按鍵自鎖標(biāo)志清零
         uiKeyTimeCnt1=0;//按鍵去抖動延時計數(shù)器清零,此行非常巧妙,是我實戰(zhàn)中摸索出來的。      
                 if(ucKeyTouchCnt1>0) //之前已經(jīng)有按鍵觸發(fā)過一次,再來一次就構(gòu)成雙擊
                 {
                     uiKeyIntervalCnt1++; //按鍵間隔的時間計數(shù)器累加
                         if(uiKeyIntervalCnt1>const_interval_time1) //超過最大允許的間隔時間
                         {
                            uiKeyIntervalCnt1=0; //時間計數(shù)器清零
                            ucKeyTouchCnt1=0; //清零按鍵的按下的次數(shù)
                         }
                 }
  }
  else if(ucKeyLock1==0)//有按鍵按下,且是第一次被按下
  {
     uiKeyTimeCnt1++; //累加定時中斷次數(shù)
     if(uiKeyTimeCnt1>const_key_time1)
     {
        uiKeyTimeCnt1=0; 
        ucKeyLock1=1;  //自鎖按鍵置位,避免一直觸發(fā)
                uiKeyIntervalCnt1=0; //按鍵有效間隔的時間計數(shù)器清零

                ucKeyTouchCnt1++;
                if(ucKeyTouchCnt1>1)  //連續(xù)被按了兩次以上
                {
                    ucKeyTouchCnt1=0;  //統(tǒng)計按鍵次數(shù)清零
                    ucKeySec=1;    //觸發(fā)1號鍵
                }

     }
  }




  if(key_sr2==1)
  {
         ucKeyLock2=0; 
         uiKeyTimeCnt2=0;
                  if(ucKeyTouchCnt2>0)
                 {
                     uiKeyIntervalCnt2++; //按鍵間隔的時間計數(shù)器累加
                         if(uiKeyIntervalCnt2>const_interval_time2) //超過最大允許的間隔時間
                         {
                            uiKeyIntervalCnt2=0; //時間計數(shù)器清零
                            ucKeyTouchCnt2=0; //清零按鍵的按下的次數(shù)
                         }
                 }
  }
  else if(ucKeyLock2==0)
  {
     uiKeyTimeCnt2++; //累加定時中斷次數(shù)
     if(uiKeyTimeCnt2>const_key_time2)
     {
        uiKeyTimeCnt2=0;
        ucKeyLock2=1; 
                uiKeyIntervalCnt2=0; //按鍵有效間隔的時間計數(shù)器清零

                ucKeyTouchCnt2++;
                if(ucKeyTouchCnt2>1)  //連續(xù)被按了兩次以上
                {
                    ucKeyTouchCnt2=0;  //統(tǒng)計按鍵次數(shù)清零
                    ucKeySec=2;    //觸發(fā)2號鍵
                }
     }
  }

}


void key_service() //第三區(qū) 按鍵服務(wù)的應(yīng)用程序
{
  switch(ucKeySec) //按鍵服務(wù)狀態(tài)切換
  {
    case 1:// 1號鍵 雙擊  對應(yīng)朱兆祺學(xué)習(xí)板的S1鍵

              uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。
              ucKeySec=0;  //響應(yīng)按鍵服務(wù)處理程序后,按鍵編號清零,避免一致觸發(fā)
          break;        
    case 2:// 2號鍵 雙擊  對應(yīng)朱兆祺學(xué)習(xí)板的S5鍵

              uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。
              ucKeySec=0;  //響應(yīng)按鍵服務(wù)處理程序后,按鍵編號清零,避免一致觸發(fā)
          break;                    
  }                
}



void T0_time() interrupt 1
{
  TF0=0;  //清除中斷標(biāo)志
  TR0=0; //關(guān)中斷

  key_scan(); //按鍵掃描函數(shù)

  if(uiVoiceCnt!=0)
  {
     uiVoiceCnt--; //每次進入定時中斷都自減1,直到等于零為止。才停止鳴叫
         beep_dr=0;  //蜂鳴器是PNP三極管控制,低電平就開始鳴叫。
  }
  else
  {
     ; //此處多加一個空指令,想維持跟if括號語句的數(shù)量對稱,都是兩條指令。不加也可以。
           beep_dr=1;  //蜂鳴器是PNP三極管控制,高電平就停止鳴叫。
  }


  TH0=0xf8;   //重裝初始值(65535-2000)=63535=0xf82f
  TL0=0x2f;
  TR0=1;  //開中斷
}


void delay_long(unsigned int uiDelayLong)
{
   unsigned int i;
   unsigned int j;
   for(i=0;i

 

總結(jié)陳詞:

      假如要兩個獨立按鍵實現(xiàn)組合按鍵的功能,我們該怎么寫程序?欲知詳情,請聽下回分解-----獨立按鍵的組合按鍵觸發(fā)。

 

(未完待續(xù),下節(jié)更精彩,不要走開哦)

 

0
回復(fù)
jianhong_wu
LV.4
27
2014-03-06 15:17

第十節(jié):兩個獨立按鍵的組合按鍵觸發(fā)。

開場白:

     上一節(jié)講了按鍵雙擊觸發(fā)功能的程序,這一節(jié)講類似電腦鍵盤組合按鍵觸發(fā)的功能,要教會大家一個知識點:如何在上一節(jié)的基礎(chǔ)上,略作修改,就可以實現(xiàn)兩個獨立按鍵的組合按鍵觸發(fā)功能。具體內(nèi)容,請看源代碼講解。

(1)硬件平臺:基于朱兆祺51單片機學(xué)習(xí)板。用矩陣鍵盤中的S1和S5號鍵作為獨立按鍵,記得把輸出線P0.4一直輸出低電平,模擬獨立按鍵的觸發(fā)地GND。

(2)實現(xiàn)功能:有兩個獨立按鍵,當(dāng)把兩個獨立按鍵都按下后,蜂鳴器發(fā)出“滴”的一聲后就停。直到松開任一個按鍵后,才能重新進行下一次的組合按鍵觸發(fā)。

(3)源代碼講解如下:

#include "REG52.H"

#define const_voice_short  40   //蜂鳴器短叫的持續(xù)時間


/* 注釋一:
* 調(diào)整抖動時間閥值的大小,可以更改按鍵的觸發(fā)靈敏度。
* 去抖動的時間本質(zhì)上等于累計定時中斷次數(shù)的時間。
*/
#define const_key_time12  20    //按鍵去抖動延時的時間


void initial_myself();    
void initial_peripheral();
void delay_long(unsigned int uiDelaylong);
void T0_time();  //定時中斷函數(shù)
void key_service(); //按鍵服務(wù)的應(yīng)用程序
void key_scan(); //按鍵掃描函數(shù) 放在定時中斷里

sbit key_sr1=P0^0; //對應(yīng)朱兆祺學(xué)習(xí)板的S1鍵
sbit key_sr2=P0^1; //對應(yīng)朱兆祺學(xué)習(xí)板的S5鍵
sbit key_gnd_dr=P0^4; //模擬獨立按鍵的地GND,因此必須一直輸出低電平

sbit beep_dr=P2^7; //蜂鳴器的驅(qū)動IO口

unsigned char ucKeySec=0;   //被觸發(fā)的按鍵編號

unsigned int  uiKeyTimeCnt12=0; //按鍵去抖動延時計數(shù)器
unsigned char ucKeyLock12=0; //按鍵觸發(fā)后自鎖的變量標(biāo)志


unsigned int  uiVoiceCnt=0;  //蜂鳴器鳴叫的持續(xù)時間計數(shù)器

void main() 
  {
   initial_myself();  
   delay_long(100);   
   initial_peripheral(); 
   while(1)  
   { 
       key_service(); //按鍵服務(wù)的應(yīng)用程序
   }

}

void key_scan()//按鍵掃描函數(shù) 放在定時中斷里
{  
/* 注釋二:
* 獨立組合按鍵掃描的詳細過程:
* 第一步:平時只要兩個按鍵中有一個沒有被按下時,按鍵的自鎖標(biāo)志,去抖動延時計數(shù)器一直被清零。
* 第二步:一旦兩個按鍵都被按下,去抖動延時計數(shù)器開始在定時中斷函數(shù)里累加,在還沒累加到
*         閥值const_key_time12時,如果在這期間由于受外界干擾或者按鍵抖動,而使
*         IO口突然瞬間觸發(fā)成高電平,這個時候馬上把延時計數(shù)器uiKeyTimeCnt12
*         清零了,這個過程非常巧妙,非常有效地去除瞬間的雜波干擾。這是我實戰(zhàn)中摸索出來的。
*         以后凡是用到開關(guān)感應(yīng)器的時候,都可以用類似這樣的方法去干擾。
* 第三步:如果按鍵按下的時間超過了閥值const_key_time12,馬上把自鎖標(biāo)志ucKeyLock12置位,
*         防止按住按鍵不松手后一直觸發(fā)。并把編號ucKeySec賦值。 組合按鍵觸發(fā)
* 第四步:等按鍵松開后,自鎖標(biāo)志ucKeyLock12及時清零,為下一次自鎖做準(zhǔn)備。
* 第五步:以上整個過程,就是識別按鍵IO口下降沿觸發(fā)的過程。
*/
  if(key_sr1==1||key_sr2==1)//IO是高電平,說明兩個按鍵沒有全部被按下,這時要及時清零一些標(biāo)志位
  {
         ucKeyLock12=0; //按鍵自鎖標(biāo)志清零
         uiKeyTimeCnt12=0;//按鍵去抖動延時計數(shù)器清零,此行非常巧妙,是我實戰(zhàn)中摸索出來的。      
  }
  else if(ucKeyLock12==0)//有按鍵按下,且是第一次被按下
  {
     uiKeyTimeCnt12++; //累加定時中斷次數(shù)
     if(uiKeyTimeCnt12>const_key_time12)
     {
        uiKeyTimeCnt12=0; 
        ucKeyLock12=1;  //自鎖按鍵置位,避免一直觸發(fā)
        ucKeySec=1;    //觸發(fā)1號鍵
              
     }
  }




}


void key_service() //第三區(qū) 按鍵服務(wù)的應(yīng)用程序
{
  switch(ucKeySec) //按鍵服務(wù)狀態(tài)切換
  {
    case 1:// 1號鍵 組合按鍵  對應(yīng)朱兆祺學(xué)習(xí)板的S1鍵和S5鍵

              uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。
              ucKeySec=0;  //響應(yīng)按鍵服務(wù)處理程序后,按鍵編號清零,避免一致觸發(fā)
          break;        
              
  }                
}



void T0_time() interrupt 1
{
  TF0=0;  //清除中斷標(biāo)志
  TR0=0; //關(guān)中斷

  key_scan(); //按鍵掃描函數(shù)

  if(uiVoiceCnt!=0)
  {
     uiVoiceCnt--; //每次進入定時中斷都自減1,直到等于零為止。才停止鳴叫
         beep_dr=0;  //蜂鳴器是PNP三極管控制,低電平就開始鳴叫。
  }
  else
  {
     ; //此處多加一個空指令,想維持跟if括號語句的數(shù)量對稱,都是兩條指令。不加也可以。
           beep_dr=1;  //蜂鳴器是PNP三極管控制,高電平就停止鳴叫。
  }


  TH0=0xf8;   //重裝初始值(65535-2000)=63535=0xf82f
  TL0=0x2f;
  TR0=1;  //開中斷
}


void delay_long(unsigned int uiDelayLong)
{
   unsigned int i;
   unsigned int j;
   for(i=0;i

 

總結(jié)陳詞:

      以前尋呼機流行的時候,尋呼機往往只有一個設(shè)置按鍵,它要求用一個按鍵來設(shè)置不同的參數(shù),這個時候就要用到同一個按鍵來實現(xiàn)短按和長按的區(qū)別觸發(fā)功能。要現(xiàn)實這種功能,我們該怎么寫程序?欲知詳情,請聽下回分解-----同一個按鍵短按與長按的區(qū)別觸發(fā)。

 

(未完待續(xù),下節(jié)更精彩,不要走開哦)

 

0
回復(fù)
yzl1128
LV.2
28
2014-03-06 16:34
@jianhong_wu
第十節(jié):兩個獨立按鍵的組合按鍵觸發(fā)。開場白:    上一節(jié)講了按鍵雙擊觸發(fā)功能的程序,這一節(jié)講類似電腦鍵盤組合按鍵觸發(fā)的功能,要教會大家一個知識點:如何在上一節(jié)的基礎(chǔ)上,略作修改,就可以實現(xiàn)兩個獨立按鍵的組合按鍵觸發(fā)功能。具體內(nèi)容,請看源代碼講解。(1)硬件平臺:基于朱兆祺51單片機學(xué)習(xí)板。用矩陣鍵盤中的S1和S5號鍵作為獨立按鍵,記得把輸出線P0.4一直輸出低電平,模擬獨立按鍵的觸發(fā)地GND。(2)實現(xiàn)功能:有兩個獨立按鍵,當(dāng)把兩個獨立按鍵都按下后,蜂鳴器發(fā)出“滴”的一聲后就停。直到松開任一個按鍵后,才能重新進行下一次的組合按鍵觸發(fā)。(3)源代碼講解如下:#include"REG52.H"#defineconst_voice_short40//蜂鳴器短叫的持續(xù)時間/*注釋一:*調(diào)整抖動時間閥值的大小,可以更改按鍵的觸發(fā)靈敏度。*去抖動的時間本質(zhì)上等于累計定時中斷次數(shù)的時間。*/#defineconst_key_time1220//按鍵去抖動延時的時間voidinitial_myself();voidinitial_peripheral();voiddelay_long(unsignedintuiDelaylong);voidT0_time();//定時中斷函數(shù)voidkey_service();//按鍵服務(wù)的應(yīng)用程序voidkey_scan();//按鍵掃描函數(shù)放在定時中斷里sbitkey_sr1=P0^0;//對應(yīng)朱兆祺學(xué)習(xí)板的S1鍵sbitkey_sr2=P0^1;//對應(yīng)朱兆祺學(xué)習(xí)板的S5鍵sbitkey_gnd_dr=P0^4;//模擬獨立按鍵的地GND,因此必須一直輸出低電平sbitbeep_dr=P2^7;//蜂鳴器的驅(qū)動IO口unsignedcharucKeySec=0;//被觸發(fā)的按鍵編號unsignedintuiKeyTimeCnt12=0;//按鍵去抖動延時計數(shù)器unsignedcharucKeyLock12=0;//按鍵觸發(fā)后自鎖的變量標(biāo)志unsignedintuiVoiceCnt=0;//蜂鳴器鳴叫的持續(xù)時間計數(shù)器voidmain(){initial_myself();delay_long(100);initial_peripheral();while(1){key_service();//按鍵服務(wù)的應(yīng)用程序}}voidkey_scan()//按鍵掃描函數(shù)放在定時中斷里{/*注釋二:*獨立組合按鍵掃描的詳細過程:*第一步:平時只要兩個按鍵中有一個沒有被按下時,按鍵的自鎖標(biāo)志,去抖動延時計數(shù)器一直被清零。*第二步:一旦兩個按鍵都被按下,去抖動延時計數(shù)器開始在定時中斷函數(shù)里累加,在還沒累加到*閥值const_key_time12時,如果在這期間由于受外界干擾或者按鍵抖動,而使*IO口突然瞬間觸發(fā)成高電平,這個時候馬上把延時計數(shù)器uiKeyTimeCnt12*清零了,這個過程非常巧妙,非常有效地去除瞬間的雜波干擾。這是我實戰(zhàn)中摸索出來的。*以后凡是用到開關(guān)感應(yīng)器的時候,都可以用類似這樣的方法去干擾。*第三步:如果按鍵按下的時間超過了閥值const_key_time12,馬上把自鎖標(biāo)志ucKeyLock12置位,*防止按住按鍵不松手后一直觸發(fā)。并把編號ucKeySec賦值。組合按鍵觸發(fā)*第四步:等按鍵松開后,自鎖標(biāo)志ucKeyLock12及時清零,為下一次自鎖做準(zhǔn)備。*第五步:以上整個過程,就是識別按鍵IO口下降沿觸發(fā)的過程。*/if(key_sr1==1||key_sr2==1)//IO是高電平,說明兩個按鍵沒有全部被按下,這時要及時清零一些標(biāo)志位{ucKeyLock12=0;//按鍵自鎖標(biāo)志清零uiKeyTimeCnt12=0;//按鍵去抖動延時計數(shù)器清零,此行非常巧妙,是我實戰(zhàn)中摸索出來的。}elseif(ucKeyLock12==0)//有按鍵按下,且是第一次被按下{uiKeyTimeCnt12++;//累加定時中斷次數(shù)if(uiKeyTimeCnt12>const_key_time12){uiKeyTimeCnt12=0;ucKeyLock12=1;//自鎖按鍵置位,避免一直觸發(fā)ucKeySec=1;//觸發(fā)1號鍵}}}voidkey_service()//第三區(qū)按鍵服務(wù)的應(yīng)用程序{switch(ucKeySec)//按鍵服務(wù)狀態(tài)切換{case1://1號鍵組合按鍵對應(yīng)朱兆祺學(xué)習(xí)板的S1鍵和S5鍵uiVoiceCnt=const_voice_short;//按鍵聲音觸發(fā),滴一聲就停。ucKeySec=0;//響應(yīng)按鍵服務(wù)處理程序后,按鍵編號清零,避免一致觸發(fā)break;}}voidT0_time()interrupt1{TF0=0;//清除中斷標(biāo)志TR0=0;//關(guān)中斷key_scan();//按鍵掃描函數(shù)if(uiVoiceCnt!=0){uiVoiceCnt--;//每次進入定時中斷都自減1,直到等于零為止。才停止鳴叫beep_dr=0;//蜂鳴器是PNP三極管控制,低電平就開始鳴叫。}else{;//此處多加一個空指令,想維持跟if括號語句的數(shù)量對稱,都是兩條指令。不加也可以。beep_dr=1;//蜂鳴器是PNP三極管控制,高電平就停止鳴叫。}TH0=0xf8;//重裝初始值(65535-2000)=63535=0xf82fTL0=0x2f;TR0=1;//開中斷}voiddelay_long(unsignedintuiDelayLong){unsignedinti;unsignedintj;for(i=0;i
不錯,值得學(xué)習(xí).
0
回復(fù)
maluhan
LV.1
29
2014-03-06 17:09
@yzl1128
不錯,值得學(xué)習(xí).
相當(dāng)不錯,樓主辛苦
0
回復(fù)
tanb006
LV.10
30
2014-03-06 17:17

如此精彩,不得不頂.

0
回復(fù)
luxha
LV.6
31
2014-03-06 17:30
@tanb006
如此精彩,不得不頂.
謝謝!好經(jīng)典!很久沒看到這么好的文章了!
0
回復(fù)
發(fā)
主站蜘蛛池模板: 清镇市| 南澳县| 乌兰浩特市| 枣庄市| 徐汇区| 承德市| 罗江县| 融水| 德阳市| 古浪县| 桦甸市| 九寨沟县| 赤城县| 海林市| 平果县| 盈江县| 历史| 玛曲县| 蒙自县| 益阳市| 池州市| 宁远县| 兴化市| 土默特左旗| 东乌珠穆沁旗| 荃湾区| 五原县| 宝坻区| 连江县| 乌苏市| 临猗县| 东平县| 鲁山县| 庆云县| 方城县| 静海县| 农安县| 日照市| 来凤县| 陵水| 同心县|