99久久全国免费观看_国产一区二区三区四区五区VM_久久www人成免费看片中文_国产高清在线a视频大全_深夜福利www_日韩一级成人av

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

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

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

       

      

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

      

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

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

開場白:    

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

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

       第二點(diǎn):delay()延時(shí)的用途。

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

(2)實(shí)現(xiàn)功能:讓一個(gè)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();

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

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

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

/* 注釋四:
* initial_peripheral()函數(shù)屬于鴻哥三區(qū)一線理論的第二區(qū),
* 專門用來初始化不要求上電立即處理的外圍芯片和模塊.
* 比如液晶模塊,AT24C02存儲芯片,DS1302時(shí)鐘芯片。
* 本程序基于朱兆祺51單片機(jī)學(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);  //延時(shí)50000個(gè)空指令的時(shí)間

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


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

 

總結(jié)陳詞:

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

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

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

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

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

 

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

 

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

 

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

 

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

 7個(gè)運(yùn)算符+,-,*,/,|,&,!。      

 

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

 

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

 

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

 

1個(gè)void函數(shù)。      

            

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

 

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

 

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

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

 

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

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

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

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

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

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

開場白:

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

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

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

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

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

(3)源代碼講解如下:

#include "REG52.H"


/* 注釋一:
* const_time_level是統(tǒng)計(jì)循環(huán)次數(shù)的設(shè)定上限,數(shù)值越大,LED延時(shí)的時(shí)間越久
*/
#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;  

/* 注釋二:
* 吳堅(jiān)鴻個(gè)人的命名風(fēng)格:凡是switch語句里面的步驟變量后綴都是Step.
* 前綴帶uc,ui,ul分別表示此變量是unsigned char,unsigned int,unsigned long.
*/
unsigned char ucLedStep=0; //步驟變量
unsigned int  uiTimeCnt=0; //統(tǒng)計(jì)循環(huán)次數(shù)的延時(shí)計(jì)數(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時(shí),
* 才會去改變LED燈的狀態(tài),否則CPU退出led_flicker()任務(wù),繼續(xù)快速掃描其他的任務(wù),
* 這樣的程序結(jié)構(gòu)就可以達(dá)到多任務(wù)并行處理的目的。
* 本程序基于朱兆祺51單片機(jī)學(xué)習(xí)板
*/
          uiTimeCnt++;  //累加循環(huán)次數(shù),
                  if(uiTimeCnt>=const_time_level) //時(shí)間到
                  {
                     uiTimeCnt=0; //時(shí)間計(jì)數(shù)器清零
             led_dr=1;    //讓LED亮
                         ucLedStep=1; //切換到下一個(gè)步驟
                  }
              break;
     case 1:
          uiTimeCnt++;  //累加循環(huán)次數(shù),
                  if(uiTimeCnt>=const_time_level) //時(shí)間到
                  {
                     uiTimeCnt=0; //時(shí)間計(jì)數(shù)器清零
             led_dr=0;    //讓LED滅
                         ucLedStep=0; //返回到上一個(gè)步驟
                  }
              break;
  
  }

}


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

		

總結(jié)陳詞:   

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

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

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

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

開場白:

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

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

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

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

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

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

(2)實(shí)現(xiàn)功能:讓一個(gè)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í)中斷函數(shù)

sbit led_dr=P3^5;  

unsigned char ucLedStep=0; //步驟變量
unsigned int  uiTimeCnt=0; //統(tǒng)計(jì)定時(shí)中斷次數(shù)的延時(shí)計(jì)數(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ù),每一次定時(shí)中斷它都會在中斷函數(shù)里自加一。
* 只有當(dāng)它的次數(shù)大于或等于設(shè)定上限const_time_level時(shí),
* 才會去改變LED燈的狀態(tài),否則CPU退出led_flicker()任務(wù),繼續(xù)快速掃描其他的任務(wù),
* 這樣的程序結(jié)構(gòu)就可以達(dá)到多任務(wù)并行處理的目的。這就是鴻哥在所有開發(fā)項(xiàng)目中的核心框架。
*/
                  if(uiTimeCnt>=const_time_level) //時(shí)間到
                  {

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

}


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

  if(uiTimeCnt<0xffff)  //設(shè)定這個(gè)條件,防止uiTimeCnt超范圍。
  {
      uiTimeCnt++;  //累加定時(shí)中斷的次數(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)展示了我最完整的實(shí)戰(zhàn)程序框架。本節(jié)程序只有一個(gè)LED燈閃爍的單任務(wù),如果要多增加一個(gè)任務(wù)來并行處理,該怎么辦?欲知詳情,請聽下回分解-----蜂鳴器的驅(qū)動程序。

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

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

呵呵,從FSY一路追到這里,走到哪里都是焦點(diǎn),崇拜中

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

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

開場白:

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

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

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

     第三點(diǎn):如何控制蜂鳴器聲音的長叫和短叫。

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

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

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

(3)源代碼講解如下:

#include "REG52.H"

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

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

void initial_myself();    
void initial_peripheral();
void delay_long(unsigned int uiDelaylong);
void led_flicker();
void alarm_run();   
void T0_time();  //定時(shí)中斷函數(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)計(jì)定時(shí)中斷次數(shù)的延時(shí)計(jì)數(shù)器

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

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

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

}

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

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

}

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

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

}

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

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

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


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


  if(uiVoiceCnt!=0)
  {
     uiVoiceCnt--; //每次進(jìn)入定時(shí)中斷都自減1,直到等于零為止。才停止鳴叫
         beep_dr=0;  //蜂鳴器是PNP三極管控制,低電平就開始鳴叫。
  }
  else
  {
     ; //此處多加一個(gè)空指令,想維持跟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)展示了一個(gè)多任務(wù)處理的基本思路,假如要實(shí)現(xiàn)一個(gè)獨(dú)立按鍵檢測,能不能也按照這種思路來處理呢?欲知詳情,請聽下回分解-----在主函數(shù)中利用累計(jì)主循環(huán)次數(shù)來實(shí)現(xiàn)獨(dú)立按鍵的檢測。

 

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

 

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

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

開場白:

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

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

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

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

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

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

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

(3)源代碼講解如下: 

#include "REG52.H"

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


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

void initial_myself();    
void initial_peripheral();
void delay_long(unsigned int uiDelaylong);
void T0_time();  //定時(shí)中斷函數(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; //模擬獨(dú)立按鍵的地GND,因此必須一直輸出低電平

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

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

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

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

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

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

}

void key_scan()//按鍵掃描函數(shù)
{  
/* 注釋二:
* 獨(dú)立按鍵掃描的詳細(xì)過程:
* 第一步:平時(shí)沒有按鍵被觸發(fā)時(shí),按鍵的自鎖標(biāo)志和去抖動延時(shí)計(jì)數(shù)器一直被清零。
* 第二步:一旦有按鍵被按下,去抖動延時(shí)計(jì)數(shù)器開始累加,在還沒累加到
*         閥值const_key_time1時(shí),如果在這期間由于受外界干擾或者按鍵抖動,而使
*         IO口突然瞬間觸發(fā)成高電平,這個(gè)時(shí)候馬上又把延時(shí)計(jì)數(shù)器uiKeyTimeCnt1
*         清零了,這個(gè)過程非常巧妙,非常有效地去除瞬間的雜波干擾。這是我實(shí)戰(zhàn)中摸索出來的。
*         以后凡是用到開關(guān)感應(yīng)器的時(shí)候,都可以用類似這樣的方法去干擾。
* 第三步:如果按鍵按下的時(shí)間超過了閥值const_key_time1,則觸發(fā)按鍵,把編號ucKeySec賦值。
*         同時(shí),馬上把自鎖標(biāo)志ucKeyLock1置位,防止按住按鍵不松手后一直觸發(fā)。
* 第四步:等按鍵松開后,自鎖標(biāo)志ucKeyLock1及時(shí)清零,為下一次自鎖做準(zhǔn)備。
* 第五步:以上整個(gè)過程,就是識別按鍵IO口下降沿觸發(fā)的過程。
*/
  if(key_sr1==1)//IO是高電平,說明按鍵沒有被按下,這時(shí)要及時(shí)清零一些標(biāo)志位
  {
     ucKeyLock1=0; //按鍵自鎖標(biāo)志清零
         uiKeyTimeCnt1=0;//按鍵去抖動延時(shí)計(jì)數(shù)器清零,此行非常巧妙,是我實(shí)戰(zhàn)中摸索出來的。      
  }
  else if(ucKeyLock1==0)//有按鍵按下,且是第一次被按下
  {
     ++uiKeyTimeCnt1;  //延時(shí)計(jì)數(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--; //每次進(jìn)入定時(shí)中斷都自減1,直到等于零為止。才停止鳴叫
         beep_dr=0;  //蜂鳴器是PNP三極管控制,低電平就開始鳴叫。
  }
  else
  {
     ; //此處多加一個(gè)空指令,想維持跟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ù)中,利用累計(jì)主循環(huán)次數(shù)來實(shí)現(xiàn)獨(dú)立按鍵的檢測。這種方法我經(jīng)常在實(shí)戰(zhàn)用應(yīng)用,但是它也有一個(gè)小小的不足,隨著在主函數(shù)循環(huán)中任務(wù)量的增加,為了保證去抖動延時(shí)的時(shí)間一致性,要適當(dāng)調(diào)整一下去抖動的閥值const_key_time1。如何解決這個(gè)問題呢?欲知詳情,請聽下回分解-----在主函數(shù)中利用累計(jì)定時(shí)中斷的次數(shù)來實(shí)現(xiàn)獨(dú)立按鍵的檢測。

 

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

 

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

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

開場白:

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

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

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

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

(3)源代碼講解如下:

#include "REG52.H"

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


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

void initial_myself();    
void initial_peripheral();
void delay_long(unsigned int uiDelaylong);
void T0_time();  //定時(shí)中斷函數(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; //模擬獨(dú)立按鍵的地GND,因此必須一直輸出低電平

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

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

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

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

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

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

}

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

  if(key_sr2==1)
  {
     ucKeyLock2=0; 
         ucKeyStartFlag2=0; //停止計(jì)數(shù)器
         uiKeyTimeCnt2=0;
  }
  else if(ucKeyLock2==0)
  {
         ucKeyStartFlag2=1; //啟動計(jì)數(shù)器
     if(uiKeyTimeCnt2>const_key_time2)
     {
            ucKeyStartFlag2=0; //停止計(jì)數(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)//啟動計(jì)數(shù)器
  {
     if(uiKeyTimeCnt1<0xffff)  //防止計(jì)數(shù)器超范圍
         {
            uiKeyTimeCnt1++;
         }
  }

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

  if(uiVoiceCnt!=0)
  {
     uiVoiceCnt--; //每次進(jìn)入定時(shí)中斷都自減1,直到等于零為止。才停止鳴叫
         beep_dr=0;  //蜂鳴器是PNP三極管控制,低電平就開始鳴叫。
  }
  else
  {
     ; //此處多加一個(gè)空指令,想維持跟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ù)中,利用累計(jì)定時(shí)中斷次數(shù)來實(shí)現(xiàn)獨(dú)立按鍵的檢測。這種方法我也經(jīng)常在實(shí)戰(zhàn)用應(yīng)用,但是如果在某些項(xiàng)目中,需要在主函數(shù)里間歇性地執(zhí)行一些一氣呵成的耗時(shí)任務(wù),這種方法就不是很實(shí)用,因?yàn)楫?dāng)主函數(shù)正在處理一氣呵成的耗時(shí)任務(wù)時(shí),這個(gè)時(shí)候如果有按鍵按下來,就有可能沒有及時(shí)被響應(yīng)到而遺漏了。那有什么方法可以解決這類項(xiàng)目中遇到的問題?欲知詳情,請聽下回分解-----在定時(shí)中斷函數(shù)里執(zhí)行獨(dú)立按鍵的掃描程序。

 

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

 

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

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

開場白:

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

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

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

(3)源代碼講解如下:

#include "REG52.H"

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


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

void initial_myself();    
void initial_peripheral();
void delay_long(unsigned int uiDelaylong);
void T0_time();  //定時(shí)中斷函數(shù)
void key_service(); //按鍵服務(wù)的應(yīng)用程序
void key_scan(); //按鍵掃描函數(shù) 放在定時(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; //模擬獨(dú)立按鍵的地GND,因此必須一直輸出低電平

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

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

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


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

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

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

}

void key_scan()//按鍵掃描函數(shù) 放在定時(shí)中斷里
{  
/* 注釋二:
* 獨(dú)立按鍵掃描的詳細(xì)過程:
* 第一步:平時(shí)沒有按鍵被觸發(fā)時(shí),按鍵的自鎖標(biāo)志,去抖動延時(shí)計(jì)數(shù)器一直被清零。
* 第二步:一旦有按鍵被按下,去抖動延時(shí)計(jì)數(shù)器開始在定時(shí)中斷函數(shù)里累加,在還沒累加到
*         閥值const_key_time1時(shí),如果在這期間由于受外界干擾或者按鍵抖動,而使
*         IO口突然瞬間觸發(fā)成高電平,這個(gè)時(shí)候馬上把延時(shí)計(jì)數(shù)器uiKeyTimeCnt1
*         清零了,這個(gè)過程非常巧妙,非常有效地去除瞬間的雜波干擾。這是我實(shí)戰(zhàn)中摸索出來的。
*         以后凡是用到開關(guān)感應(yīng)器的時(shí)候,都可以用類似這樣的方法去干擾。
* 第三步:如果按鍵按下的時(shí)間超過了閥值const_key_time1,則觸發(fā)按鍵,把編號ucKeySec賦值。
*         同時(shí),馬上把自鎖標(biāo)志ucKeyLock1置位,防止按住按鍵不松手后一直觸發(fā)。
* 第四步:等按鍵松開后,自鎖標(biāo)志ucKeyLock1及時(shí)清零,為下一次自鎖做準(zhǔn)備。
* 第五步:以上整個(gè)過程,就是識別按鍵IO口下降沿觸發(fā)的過程。
*/
  if(key_sr1==1)//IO是高電平,說明按鍵沒有被按下,這時(shí)要及時(shí)清零一些標(biāo)志位
  {
     ucKeyLock1=0; //按鍵自鎖標(biāo)志清零
         uiKeyTimeCnt1=0;//按鍵去抖動延時(shí)計(jì)數(shù)器清零,此行非常巧妙,是我實(shí)戰(zhàn)中摸索出來的。      
  }
  else if(ucKeyLock1==0)//有按鍵按下,且是第一次被按下
  {
     uiKeyTimeCnt1++; //累加定時(shí)中斷次數(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í)中斷次數(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--; //每次進(jìn)入定時(shí)中斷都自減1,直到等于零為止。才停止鳴叫
         beep_dr=0;  //蜂鳴器是PNP三極管控制,低電平就開始鳴叫。
  }
  else
  {
     ; //此處多加一個(gè)空指令,想維持跟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ù)里執(zhí)行獨(dú)立按鍵的掃描程序。這節(jié)和前面兩節(jié)所講的掃描方式,我都在項(xiàng)目上用過,具體跟項(xiàng)目的側(cè)重點(diǎn)不同來選擇不同的方式,我本人用得最多的就是當(dāng)前這種方式。假如要獨(dú)立按鍵實(shí)現(xiàn)類似鼠標(biāo)的雙擊功能,我們改怎么寫程序?欲知詳情,請聽下回分解-----獨(dú)立按鍵的雙擊按鍵觸發(fā)。

 

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

 

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

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

開場白:

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

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

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

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

(3)源代碼講解如下:

#include "REG52.H"

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


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

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

void initial_myself();    
void initial_peripheral();
void delay_long(unsigned int uiDelaylong);
void T0_time();  //定時(shí)中斷函數(shù)
void key_service(); //按鍵服務(wù)的應(yīng)用程序
void key_scan(); //按鍵掃描函數(shù) 放在定時(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; //模擬獨(dú)立按鍵的地GND,因此必須一直輸出低電平

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

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

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

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

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

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

}

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

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

     }
  }




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

                ucKeyTouchCnt2++;
                if(ucKeyTouchCnt2>1)  //連續(xù)被按了兩次以上
                {
                    ucKeyTouchCnt2=0;  //統(tǒng)計(jì)按鍵次數(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--; //每次進(jìn)入定時(shí)中斷都自減1,直到等于零為止。才停止鳴叫
         beep_dr=0;  //蜂鳴器是PNP三極管控制,低電平就開始鳴叫。
  }
  else
  {
     ; //此處多加一個(gè)空指令,想維持跟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é)陳詞:

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

 

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

 

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

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

開場白:

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

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

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

(3)源代碼講解如下:

#include "REG52.H"

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


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


void initial_myself();    
void initial_peripheral();
void delay_long(unsigned int uiDelaylong);
void T0_time();  //定時(shí)中斷函數(shù)
void key_service(); //按鍵服務(wù)的應(yīng)用程序
void key_scan(); //按鍵掃描函數(shù) 放在定時(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; //模擬獨(dú)立按鍵的地GND,因此必須一直輸出低電平

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

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

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


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

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

}

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

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

 

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

 

0
回復(fù)
yzl1128
LV.2
28
2014-03-06 16:34
@jianhong_wu
第十節(jié):兩個(gè)獨(dú)立按鍵的組合按鍵觸發(fā)。開場白:    上一節(jié)講了按鍵雙擊觸發(fā)功能的程序,這一節(jié)講類似電腦鍵盤組合按鍵觸發(fā)的功能,要教會大家一個(gè)知識點(diǎn):如何在上一節(jié)的基礎(chǔ)上,略作修改,就可以實(shí)現(xiàn)兩個(gè)獨(dú)立按鍵的組合按鍵觸發(fā)功能。具體內(nèi)容,請看源代碼講解。(1)硬件平臺:基于朱兆祺51單片機(jī)學(xué)習(xí)板。用矩陣鍵盤中的S1和S5號鍵作為獨(dú)立按鍵,記得把輸出線P0.4一直輸出低電平,模擬獨(dú)立按鍵的觸發(fā)地GND。(2)實(shí)現(xiàn)功能:有兩個(gè)獨(dú)立按鍵,當(dāng)把兩個(gè)獨(dú)立按鍵都按下后,蜂鳴器發(fā)出“滴”的一聲后就停。直到松開任一個(gè)按鍵后,才能重新進(jìn)行下一次的組合按鍵觸發(fā)。(3)源代碼講解如下:#include"REG52.H"#defineconst_voice_short40//蜂鳴器短叫的持續(xù)時(shí)間/*注釋一:*調(diào)整抖動時(shí)間閥值的大小,可以更改按鍵的觸發(fā)靈敏度。*去抖動的時(shí)間本質(zhì)上等于累計(jì)定時(shí)中斷次數(shù)的時(shí)間。*/#defineconst_key_time1220//按鍵去抖動延時(shí)的時(shí)間voidinitial_myself();voidinitial_peripheral();voiddelay_long(unsignedintuiDelaylong);voidT0_time();//定時(shí)中斷函數(shù)voidkey_service();//按鍵服務(wù)的應(yīng)用程序voidkey_scan();//按鍵掃描函數(shù)放在定時(shí)中斷里sbitkey_sr1=P0^0;//對應(yīng)朱兆祺學(xué)習(xí)板的S1鍵sbitkey_sr2=P0^1;//對應(yīng)朱兆祺學(xué)習(xí)板的S5鍵sbitkey_gnd_dr=P0^4;//模擬獨(dú)立按鍵的地GND,因此必須一直輸出低電平sbitbeep_dr=P2^7;//蜂鳴器的驅(qū)動IO口unsignedcharucKeySec=0;//被觸發(fā)的按鍵編號unsignedintuiKeyTimeCnt12=0;//按鍵去抖動延時(shí)計(jì)數(shù)器unsignedcharucKeyLock12=0;//按鍵觸發(fā)后自鎖的變量標(biāo)志unsignedintuiVoiceCnt=0;//蜂鳴器鳴叫的持續(xù)時(shí)間計(jì)數(shù)器voidmain(){initial_myself();delay_long(100);initial_peripheral();while(1){key_service();//按鍵服務(wù)的應(yīng)用程序}}voidkey_scan()//按鍵掃描函數(shù)放在定時(shí)中斷里{/*注釋二:*獨(dú)立組合按鍵掃描的詳細(xì)過程:*第一步:平時(shí)只要兩個(gè)按鍵中有一個(gè)沒有被按下時(shí),按鍵的自鎖標(biāo)志,去抖動延時(shí)計(jì)數(shù)器一直被清零。*第二步:一旦兩個(gè)按鍵都被按下,去抖動延時(shí)計(jì)數(shù)器開始在定時(shí)中斷函數(shù)里累加,在還沒累加到*閥值const_key_time12時(shí),如果在這期間由于受外界干擾或者按鍵抖動,而使*IO口突然瞬間觸發(fā)成高電平,這個(gè)時(shí)候馬上把延時(shí)計(jì)數(shù)器uiKeyTimeCnt12*清零了,這個(gè)過程非常巧妙,非常有效地去除瞬間的雜波干擾。這是我實(shí)戰(zhàn)中摸索出來的。*以后凡是用到開關(guān)感應(yīng)器的時(shí)候,都可以用類似這樣的方法去干擾。*第三步:如果按鍵按下的時(shí)間超過了閥值const_key_time12,馬上把自鎖標(biāo)志ucKeyLock12置位,*防止按住按鍵不松手后一直觸發(fā)。并把編號ucKeySec賦值。組合按鍵觸發(fā)*第四步:等按鍵松開后,自鎖標(biāo)志ucKeyLock12及時(shí)清零,為下一次自鎖做準(zhǔn)備。*第五步:以上整個(gè)過程,就是識別按鍵IO口下降沿觸發(fā)的過程。*/if(key_sr1==1||key_sr2==1)//IO是高電平,說明兩個(gè)按鍵沒有全部被按下,這時(shí)要及時(shí)清零一些標(biāo)志位{ucKeyLock12=0;//按鍵自鎖標(biāo)志清零uiKeyTimeCnt12=0;//按鍵去抖動延時(shí)計(jì)數(shù)器清零,此行非常巧妙,是我實(shí)戰(zhàn)中摸索出來的。}elseif(ucKeyLock12==0)//有按鍵按下,且是第一次被按下{uiKeyTimeCnt12++;//累加定時(shí)中斷次數(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--;//每次進(jìn)入定時(shí)中斷都自減1,直到等于零為止。才停止鳴叫beep_dr=0;//蜂鳴器是PNP三極管控制,低電平就開始鳴叫。}else{;//此處多加一個(gè)空指令,想維持跟if括號語句的數(shù)量對稱,都是兩條指令。不加也可以。beep_dr=1;//蜂鳴器是PNP三極管控制,高電平就停止鳴叫。}TH0=0xf8;//重裝初始值(65535-2000)=63535=0xf82fTL0=0x2f;TR0=1;//開中斷}voiddelay_long(unsignedintuiDelayLong){unsignedinti;unsignedintj;for(i=0;i
不錯(cuò),值得學(xué)習(xí).
0
回復(fù)
maluhan
LV.1
29
2014-03-06 17:09
@yzl1128
不錯(cuò),值得學(xué)習(xí).
相當(dāng)不錯(cuò),樓主辛苦
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ā)
主站蜘蛛池模板: 91sex国产海角社区 | 欧美人与动牲交免费观看网 | 国产色片免费色网视频 | 亚洲富人天堂视频 | 国产精久久 | 成年人视频在线观看免费 | 亚洲精品乱码久久久久久不卡 | 大地资源在线观看免费高清一 | 国产精品人妻无码一区二区三区 | 美男被强行糟蹋np各种play | jizzjizz丝袜老师 | 麻豆人妻少妇精品无码专区 | 国产欧美亚洲精品 | 国产综合精品一区二区 | 性欧美乱妇com喷浆水多 | 欧美日韩无 | 岛国a视频在线观看免费18在线看 | av网址免费看 | 国产AV无码精品国产精品 | 精品国产一区二区三区久久狼5月 | 一本色道无码道在线观 | 成人国产精品久久久按摩 | www.夜夜 | 亚洲精品无码久久久久去Q 国产精选一级毛片 | 日韩中文字幕亚洲一区二区va在线 | 一级二级三级毛片 | 亚洲最色视频 | 久久久WWW成人免费毛片 | 国产精品影视在线 | 久久97超碰 | 中文字幕一区二区日韩精品绯色 | 天天做天天爱夜夜爽女人爽 | 99综合在线| 无码av一区在线观看免费 | 久久大香伊蕉在人线免费 | 69一区二区 | 成人av中文解说水果派在线观看 | 天天操夜操视频 | 国产高清一区在线观看 | 超碰在线观看免费 | 91九色丨porny丨交换 |