亚洲春色中文字幕久久久-三上亚,一吻二脱三床四吻胸,国产真实伦对白视频全集,在线毛片观看,精品成品入口黄网,国产毛aⅴ片久久久,亚洲AV色香蕉一区二区三区老师,萧皇后A级艳片,色情日本视频更新,99久久亚洲精品日本无码

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 1205|回復: 1
打印 上一主題 下一主題
收起左側

從單片機基礎到程序框架 資料下載

[復制鏈接]
跳轉到指定樓層
樓主
ID:140489 發表于 2025-10-17 09:48 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
吳大拿 從單片機基礎到程序框架 資料下載

“非阻塞”,在處理消抖的時候,必須用到延時,如果此時用阻塞的delay延時就會影響其它任務的運行效率,因此,用非阻塞的定時延時更加有優越性。
“清零式濾波”,在消抖的時候,有兩種境界,第一種境界是判斷兩次電平的狀態,中間插入“固定的時間”延時,這種方法前后一共判斷了兩次,第一次是識別到低電平就進入延時的狀態,第二次是延時后再確認一次是否繼續是低電平的狀態,這種方法的不足是,“固定的時間”全憑經驗值,但是不同的按鍵它們的抖動時間長度是不同的,除此之外,前后才判斷了兩次,在軟件的抗干擾能力上也弱了很多,“密碼等級”不夠高。第二種境界就是“清零式濾波”,“清零式濾波”非常巧妙,抗擾能力超強,它能自動過濾不同按鍵的“抖動時間”,然后再進入一個“穩定時間”的“N次識別判斷”,更加巧妙的是,在“抖動時間”和“穩定時間”兩者時間內,只要發現一次是高電平的干擾,就馬上自動清零計時器,重新開始計時。“穩定時間”一般取20ms到30ms之間,而“抖動時間”是隱藏的,在代碼上并沒有直接描寫出來,但是卻無形地融入了代碼之中,只有慢慢體會才能發現它的存在。
具體的代碼如下,實現的功能是按一次K1或者K2按鍵,就觸發一次蜂鳴器鳴叫。
#include "REG52.H"  
#define KEY_VOICE_TIME   50 //按鍵觸發后發出的聲音長度  
#define KEY_FILTER_TIME  25  //按鍵濾波的“穩定時間”25ms
void T0_time();
void SystemInitial(void) ;
void Delay(unsigned long u32DelayTime) ;
void PeripheralInitial(void) ;
void BeepOpen(void);   
void BeepClose(void);
void VoiceScan(void);
void KeyScan(void);    //按鍵識別的驅動函數,放在定時中斷里
void KeyTask(void);    //按鍵任務函數,放在主函數內
sbit P3_4=P3^4;  
sbit KEY_INPUT1=P2^2;  //K1按鍵識別的輸入口。
sbit KEY_INPUT2=P2^1;  //K2按鍵識別的輸入口。
volatile unsigned char vGu8BeepTimerFlag=0;  
volatile unsigned int vGu16BeepTimerCnt=0;  
volatile unsigned char vGu8KeySec=0;  //按鍵的觸發序號,全局變量意味著是其它函數的接口。
void main()
{
SystemInitial();            
Delay(10000);               
PeripheralInitial();      
    while(1)  
{  
   KeyTask();    //按鍵任務函數
    }
}
void T0_time() interrupt 1     
{
VoiceScan();  
KeyScan();    //按鍵識別的驅動函數
TH0=0xfc;   
TL0=0x66;   
}
void SystemInitial(void)
{
TMOD=0x01;  
TH0=0xfc;   
TL0=0x66;   
EA=1;      
ET0=1;      
TR0=1;      
}
void Delay(unsigned long u32DelayTime)
{
    for(;u32DelayTime>0;u32DelayTime--);
}
void PeripheralInitial(void)
{

}
void BeepOpen(void)
{
P3_4=0;  
}
void BeepClose(void)
{
P3_4=1;  
}
void VoiceScan(void)
{
   static unsigned char Su8Lock=0;  
if(1==vGu8BeepTimerFlag&&vGu16BeepTimerCnt>0)
   {
    if(0==Su8Lock)
    {
     Su8Lock=1;  
BeepOpen();
     }
    else  
{     
         vGu16BeepTimerCnt--;  
     if(0==vGu16BeepTimerCnt)
     {
      Su8Lock=0;     
BeepClose();  
     }
}
   }  
}
/* 注釋一:
* 獨立按鍵掃描的詳細過程,以按鍵K1為例,如下:
* 第一步:平時沒有按鍵被觸發時,按鍵的自鎖標志,去抖動延時計數器一直被清零。
* 第二步:一旦有按鍵被按下,去抖動延時計數器開始在定時中斷函數里累加,在還沒累加到
*         閥值KEY_FILTER_TIME時,如果在這期間由于受外界干擾或者按鍵抖動,而使
*         IO口突然瞬間觸發成高電平,這個時候馬上把延時計數器Su16KeyCnt1清零了,這個過程
*         非常巧妙,非常有效地去除瞬間的雜波干擾。以后凡是用到開關感應器的時候,
*         都可以用類似這樣的方法去干擾。
* 第三步:如果按鍵按下的時間達到閥值KEY_FILTER_TIME時,則觸發按鍵,把編號vGu8KeySec賦值。
*         同時,馬上把自鎖標志Su8KeyLock1置1,防止按住按鍵不松手后一直觸發。
* 第四步:等按鍵松開后,自鎖標志Su8KeyLock1及時清零(解鎖),為下一次自鎖做準備。
* 第五步:以上整個過程,就是識別按鍵IO口下降沿觸發的過程。
*/
void KeyScan(void)  //此函數放在定時中斷里每1ms掃描一次
{
   static unsigned char Su8KeyLock1; //1號按鍵的自鎖
   static unsigned int  Su16KeyCnt1; //1號按鍵的計時器
   static unsigned char Su8KeyLock2; //2號按鍵的自鎖
   static unsigned int  Su16KeyCnt2; //2號按鍵的計時器

   //1號按鍵
   if(0!=KEY_INPUT1)//IO是高電平,說明按鍵沒有被按下,這時要及時清零一些標志位
   {
      Su8KeyLock1=0; //按鍵解鎖
      Su16KeyCnt1=0; //按鍵去抖動延時計數器清零,此行非常巧妙,是全場的亮點。      
   }
   else if(0==Su8KeyLock1)//有按鍵按下,且是第一次被按下。這行很多初學者有疑問,請看專題分析。
   {
      Su16KeyCnt1++; //累加定時中斷次數
      if(Su16KeyCnt1>=KEY_FILTER_TIME) //濾波的“穩定時間”KEY_FILTER_TIME,長度是25ms。
      {
         Su8KeyLock1=1;  //按鍵的自鎖,避免一直觸發
         vGu8KeySec=1;    //觸發1號鍵
      }
   }
   //2號按鍵
   if(0!=KEY_INPUT2)
   {
      Su8KeyLock2=0;
      Su16KeyCnt2=0;      
   }
   else if(0==Su8KeyLock2)
   {
      Su16KeyCnt2++;
      if(Su16KeyCnt2>=KEY_FILTER_TIME)
      {
         Su8KeyLock2=1;  
         vGu8KeySec=2;    //觸發2號鍵
      }
   }
}
void KeyTask(void)    //按鍵任務函數,放在主函數內
{
if(0==vGu8KeySec)
{
return; //按鍵的觸發序號是0意味著無按鍵觸發,直接退出當前函數,不執行此函數下面的代碼
}
switch(vGu8KeySec) //根據不同的按鍵觸發序號執行對應的代碼
{
   case 1:     //1號按鍵
        vGu8BeepTimerFlag=0;  
vGu16BeepTimerCnt=KEY_VOICE_TIME;  //觸發按鍵后,發出固定長度的聲音
        vGu8BeepTimerFlag=1;  
vGu8KeySec=0;  //響應按鍵服務處理程序后,按鍵編號必須清零,避免一直觸發
break;
   case 2:     //2號按鍵
        vGu8BeepTimerFlag=0;  
vGu16BeepTimerCnt=KEY_VOICE_TIME;  //觸發按鍵后,發出固定長度的聲音
        vGu8BeepTimerFlag=1;  
vGu8KeySec=0;  //響應按鍵服務處理程序后,按鍵編號必須清零,避免一直觸發
break;
}
}
92.2   專題分析:else if(0==Su8KeyLock1)。
疑問:
   if(0!=KEY_INPUT1)
   {
      Su8KeyLock1=0;
      Su16KeyCnt1=0;      
   }
   else if(0==Su8KeyLock1)//有按鍵按下,且是第一次被按下。為什么?為什么?為什么?
   {
      Su16KeyCnt1++;
      if(Su16KeyCnt1>KEY_FILTER_TIME)
      {
         Su8KeyLock1=1;  
         vGu8KeySec=1;   
      }
   }
解答:
首先,我們要明白C語言的語法中,
if(條件1)
{
}
else if(條件2)
{
}
以上語句是一對組合語句,不能分開來看。當(條件1)成立的時候,它是絕對不會判斷(條件2)的。當(條件1)不成立的時候,才會判斷(條件2)。
回到剛才的問題,當程序執行到(條件2) else if(0==Su8KeyLock1)的時候,就已經默認了(條件1) if(0!=KEY_INPUT1)不成立,這個條件不成立,就意味著0==KEY_INPUT1,也就是有按鍵被按下,因此,這里的else if(0==Su8KeyLock1)等效于else if(0==Su8KeyLock1&&0==KEY_INPUT1),而Su8KeyLock1是一個自鎖標志位,一旦按鍵被觸發后,這個標志位會變1,防止按鍵按住不松手的時候不斷觸發按鍵。這樣,按鍵只能按一次觸發一次,松開手后再按一次,又觸發一次。
92.3   專題分析:if(0!=KEY_INPUT1)。
疑問:為什么不用if(1==KEY_INPUT1)而用if(0!=KEY_INPUT1)?
解答:其實兩者在功能上是完全等效的,在這里都可以用。之所以本教程優先選用后者if(0!=KEY_INPUT1),是因為考慮到了代碼在不同單片機平臺上的可移植性和兼容性。很多32位的單片機提供的是庫函數,庫函數返回的按鍵狀態是一個字節變量來表示,當被按下的時候是0,但是,當沒有按下的時候并不一定等于1,而是一個“非0”的數值。

92.4   專題分析:把KeyScan函數放在定時器中斷里。
疑問:為什么把KeyScan函數放在定時器中斷里?
解答:中斷函數里放的函數或者代碼越少越好,但是KeyScan函數是特殊的函數,是涉及到IO口輸入信號的濾波,濾波就涉及到時間的及時性與均勻性,放在定時中斷函數里更加能保證時間的一致性。比如,蜂鳴器驅動,動態數碼管驅動,按鍵掃描驅動,我個人都習慣放在定時中斷函數里。
92.5   專題分析:if(0==vGu8KeySec)return。
疑問:if(0==vGu8KeySec)return是不是多此一舉?
解答:在KeyTask函數這里,if(0==vGu8KeySec)return這行代碼刪掉,對程序功能是沒有影響的,這里之所以多插入這行判斷語句,是因為,當按鍵多達幾十個的時候,避免主函數每次進入KeyTask函數,都挨個掃描判斷switch的狀態進行多次判斷,如果增加了這行if(0==vGu8KeySec)return代碼,就可以直接退出省事,在理論上感覺更加運行高效。其實,不同單片機不同的C編譯器可能對switch語句的翻譯不一樣,因此,這里的是不是更加高效我不敢保證。但是可以保證的是,加了這行代碼也沒有其它副作用。

從單片機基礎到程序框架_pdf教程_word教程_原理圖_PCB文件_四件套.zip

9.12 MB, 下載次數: 0, 下載積分: 黑幣 -5

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏3 分享淘帖 頂 踩
回復

使用道具 舉報

沙發
ID:81978 發表于 2026-4-26 14:49 | 只看該作者
#在這里快速回復#學習了,很好的資料,頂一下
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術交流QQ群281945664

Powered by 單片機教程網

快速回復 返回頂部 返回列表