? ?(七)、鎖存器
?1、原理
???? ?藍橋杯中數據傳入口都是P0,也就是數碼管段選、位選數據、LED亮滅的數據、蜂鳴器啟動或禁用的數據,外設啟動或者關閉都需要通過P0寫入數據,那么如何這樣共用一個端口會造成沖突嘛,答案是肯定的。所以藍橋杯加入了鎖存器---通過P2高三位(22、21、20)操作。如圖 三十LED鎖存器對應地址為Y4C,湊成高三位數據100,后五位自動補零,P2對應數據1000 0000(用十六進制表示0x80)。Y5C對應數據就是1010 0000(0xa0)…
? 2、代碼示范與解讀
?如 圖 三十一,首先定義一個temp臨時變量,再在P0口傳入數據
P2 & 0x1f就是保留低五位,單獨使高三位全為0,因為高三位才是鎖存器的選擇位。
再單獨改變temp = temp | 0x80,0x80是剛剛推出來的數據,這樣|上,就是單獨修改了高三位,低五位 保持不變。再將數據放入P2中,此時P0中數據就已經傳入了。
最后我們temp = P2 & 0x1f就是清空高三位,P2 = temp關閉鎖存器。注意如果此時不關閉鎖存器,那么一旦有數據傳入P0就會被立即寫入。
對這個代碼進行推廣,只需改變0x80那個位置的數據即可,假設想操作數碼管位選的鎖存器Y6C,可知為1100 0000(即為0xc0),將0x80改成0xc0就可以傳入數碼管位選數據了。
? ? ? ? ? ? ? ? ? ? ? ? 圖 三十 LED原理圖
????????????
? ? ? ? ? ? ? ? ?圖 三十一? 借助鎖存器寫入數據
(八)、LED
?1、原理(圖 三十)
????? ??在P00-P07口傳入數據,對應第一盞燈到第八盞燈,打開鎖存器,就可以實現LED亮滅了。由原理圖可知,為共陽極接法,當P0端口傳入低電平數據時,就點亮LED了。注意51單片機LED亮滅可能不同。
?2、代碼解讀(圖 三十二)
? ? ? ? 定義一個數組,傳入LED亮滅的數據,1為亮表示使能,0為滅表示失能
????? ?定義兩個變量,temp_1與temp_1_old,只有這兩者不相等時操作寄存器(改變)寫入,temp_1存放亮滅的八位數據。
? ? ? ?LED_Buf[0]<<0就是將第一個燈數據左移0位,放在temp_1的最低位,就是操作P00第一個燈。LED_Buf[1]<<1就是把第二個燈數據按二進制左移1位,放在temp_1的Bit2位…
? ? ? ?有改變發生,兩者不同了,寫入數據,記住低電平點亮燈,所以要取反,在打開LED的鎖存器,更新temp_1_old的值。
? ? ? 在主程序中我們可以打開定時器0,寫一個1ms中斷函數,在中斷中掃描這個函數。
? ? ? ? ? ? ? ? ? ?圖 三十二 LED代碼解讀
(九)、繼電器、蜂鳴器、Motor
??1、原理(圖 三十三)
????? 最左邊P01~P07是數據輸入端口,最右邊Relay就是對應繼電器,Motor就是發動機,Buzz對應蜂鳴器,低電平0就是打開這個設備,但是ULN2003設備在中間會對數據進行取反,所以打開Relay數據為 空一位011 1111,對應最左邊數據為 0001 0000,即為0x10。同理打開蜂鳴器即為0x40,打開Motor即為0x20。
? ? ? ? ? ? ? ? ? ? 圖 三十三?外設原理圖
???????????????
?2、代碼解讀(圖 三十四 )
???? 開局必須定義兩個全局變量為蜂鳴器、繼電器、發動機共用,防止不同變量操作對其他設備影響。
? ? ?當Flag為1時,必須單獨變化第四位為1,其余的不變,用 | 操作符來實現,|上0這一位就是保持不變,|上1就是強制為1。當Flag為1時,第四位為0,其余的不變,用 & 符來實現。
? ? temp_2就是臨時數據值,temp_2_Old就是記錄上一個狀態的值,不相同說明狀態發生了改變,將數據傳入P0端,打開鎖存器即可。
? ? ? ? ? ? ? ? 圖 三十四?代碼解讀(繼電器)
?3、代碼推廣(圖 三十五、圖 三十六)
? ?將0x10改為0x40就是單獨打開蜂鳴器代碼,改成0x20就是啟動Motor電機代碼,電機適用于設計PWM波形的。一般來說只考繼電器在特定條件下打開與關閉。
? ? ? ? ? ? ? ? ? 圖 三十五?蜂鳴器推廣代碼
? ? ? ? ? ? ? ? ? ? ? 圖 三十六?Motor推廣代碼
4、提供參考代碼,希望對讀者有幫助
#include <STC15F2K60S2.H>idata unsigned char temp_1 = 0x00;
idata unsigned char temp_old_1 = 0xff;
void LED_Disp(unsigned char *LED_Buf) //傳入LED數據數組,1亮、0滅
{unsigned char temp; temp_1 = 0x00;temp_1 = (LED_Buf[0]<<0) | (LED_Buf[1]<<1) |(LED_Buf[2]<<2) |(LED_Buf[3]<<3) |(LED_Buf[4]<<4) |(LED_Buf[5]<<5) |(LED_Buf[6]<<6) |(LED_Buf[7]<<7); //通過一個數組傳入8個LED燈的數據 //LED_Buf[0]<<0,假設第一個數據為1,數據<<(按位左移)0就是讓Bit1為1//LED_Buf[1]<<1,假設第一個數據為1,數據<<(按位左移)1就是讓Bit2為1if(temp_1 != temp_old_1) //數據改變{P0 = ~temp_1; //一定記得取反temp = P2 & 0x1f;temp = temp | 0x80; //操作Led燈的鎖存器P2 = temp;temp = P2 & 0x1f;P2 = temp;temp_old_1 = temp_1; //更新temp_old_1}
}idata unsigned char temp_2 = 0x00;
idata unsigned char temp_2_old = 0xff;//繼電器操作函數
void Relay_Disp(unsigned char Flag)
{unsigned char temp;if(Flag)temp_2 |= 0x10;elsetemp_2 &= ~0x10;if(temp_2 != temp_2_old){P0 = temp_2;temp = P2 & 0x1f;temp = temp | 0xa0;P2 = temp;temp = P2 & 0x1f;P2 = temp;temp_2_old = temp_2;}
}//蜂鳴器操作函數
void Beep_Disp(unsigned char Flag)
{unsigned char temp;if(Flag)temp_2 |= 0x40;elsetemp_2 &= ~0x40;if(temp_2 != temp_2_old){P0 = temp_2;temp = P2 & 0x1f;temp = temp | 0xa0;P2 = temp;temp = P2 & 0x1f;P2 = temp;temp_2_old = temp_2;}
}//Motor操作函數
void Motor_Disp(unsigned char Flag)
{unsigned char temp;if(Flag)temp_2 |= 0x20;elsetemp_2 &= ~0x20;if(temp_2 != temp_2_old){P0 = temp_2;temp = P2 & 0x1f;temp = temp | 0xa0;P2 = temp;temp = P2 & 0x1f;P2 = temp;temp_2_old = temp_2;}
}
5、提供定時器1代碼與中斷
//定時器一初始化,自己加上EA = 1;ET1 = 1;
void Timer1_Init(void) //1毫秒@12.000MHz
{AUXR &= 0xBF; //定時器時鐘12T模式TMOD &= 0x0F; //設置定時器模式TL1 = 0x18; //設置定時初始值TH1 = 0xFC; //設置定時初始值TF1 = 0; //清除TF1標志TR1 = 1; //定時器1開始計時EA = 1; //打開總中斷ET1 = 1; //打開定時器一中斷允許位
}void Timer1_Routine() interrupt 3
{
LED_Disp(LED_Buf); //LED掃描
}
?
?
?