蜂鳴器
兩種蜂鳴器的介紹
有源蜂鳴器一般是輸入一個電流或電壓即可直接驅動工作,而無源蜂鳴器則需要輸入脈沖信號才可以進行工作。在51單片機開發板上的即為無源蜂鳴器。
蜂鳴器相關電路圖


可以看出,信號是通過P15傳遞到ULN2003D芯片后進而傳遞到芯片的OUT5(即BEEP端口)再傳遞到蜂鳴器中的,其中ULN2003D芯片起着電流放大的作用。
控制代碼
首先我們先獲得控制蜂鳴器的引腳,從電路圖可以看出是P15,所以:
sbit BEEP= P1^5;
因為這是無源蜂鳴器,所以我們需要給它提供脈沖信號輸入才能使它工作。而當BEEP為0時有電流,BEEP為1時無電流,所以我們需要循環改變BEEP的值,主函數代碼如下所示:
int main() {
while (1)
{
BEEP = ~BEEP;
deley(10);
}
}
如果我們希望改變蜂鳴器的音調,只需要改變脈沖信號的頻率即可,也就是while
循環中deley()
的參數。
我們也可以不斷改變deley()
中填入的參數來使蜂鳴器發出奇怪的聲音🤣:
int main() {
u16 time = 10;
u8 cnts = 50;
u8 i;
for(time=10;time<200;time++) {
for(i=0;i<cnts;i++) {
BEEP = ~BEEP;
deley(time);
}
}
}
獨立按鍵
獨立按鍵電路圖

可以看到,這4個獨立按鍵都是一端和單片機的引腳(P3[0..3])相連,而另一端直接接地的。
這些按鍵的效果是,當按鍵沒有按下時,它們對應的端口的輸出是高電平,而當按鍵按下之后,這些端口的輸出則變為低電平了。
因此我們可以使用輪詢的方式查看這些端口的電平情況來檢測按鈕是否被按下,如果按下,則我們可以進行計數等控制其他元件的操作。
按鍵控制一個LED的點亮和熄滅
我們希望當點擊按鍵時,第一個LED點亮,而在此單擊時則熄滅。
按照之前的思路,我們很容易就能寫出對應的控制代碼:
sbit OneLED = P2^0; // 使用OneLED來控制對應的引腳的輸出
sbit k1 = P3^1;
void keypros() {
if (k1 == 0) {
deley(1000); // 消抖
if (k1 == 0) {
OneLED = ~OneLED;
}
while (!k1);
}
}
int main() {
while (1) {
keypros();
}
}
重要的是keypros()
函數中的內容,當我們點擊第一個按鈕時,k1的值會變為0,因此我們進行輪詢的時候就會進入到keypros()
函數的第一個if中。然后進行deley(1000);
是為了進行消抖處理。然后再次查看k1是否為0,若不為0則說明此次按動是非法的(即可能是抖動產生的),而如果為0則進入切換LED狀態的代碼。然后使用while (!k1);
來等待抬手,若沒有這句話,則此次函數調用結束后我們的手還沒有抬起來又再次進入函數了,會導致燈光不斷亮滅變換,和我們希望的不符,所以使用這個循環語句進行等待抬手再次輪詢。
運行效果:
設置控制框架
可以看出,我們的按鍵控制其他硬件的框架已經搭好了,以后我們需要控制其他元件時就只需要將keypros()
函數中的OneLED = ~OneLED;
這個部分切換為其他的控制代碼即可。但是直接切換又會顯得非常的麻煩,因此我們使用C語言的一個好用的東西函數指針來完成這項功能。
其實回調函數這種東西是非常常見的,特別是在JavaScript這個語言中被大量地使用,在JavaScript中函數是一等公民,可以隨便作為參數進行傳遞,而在C語言可以通過函數指針將一個函數作為另一個函數的參數進行傳遞,例如:
typedef void (*CallBack)();
int getRunTime(CallBack function) {
clock_t startTime = clock();
function();
clock_t endTime = clock();
return endTime - startTime;
}
這里定義了一個回調函數指針類型CallBack,其指向的函數無參數無返回值。然后我們可以將一個函數指針作為參數傳遞到getRunTime()
函數中,這樣運行結束后我們即可獲得傳遞進去的函數的運行時間是多少了,例如:
void countFunc() {
for (int i = 0; i < 10000000; ++i) {}
}
int main() {
int runTime = getRunTime(countFunc); <--將函數指針傳遞進去,相當於回調函數
cout << "runTime: " << runTime << "ms" << endl;
}
參考博客:https://www.jianshu.com/p/2862d05f64e8
所以我們使keypros()
函數接收一個函數指針,然后將OneLED = ~OneLED;
替換為傳入的函數:
typedef void (*CallBack)(void);
void keypros(CallBack function) {
if (k1 == 0) {
deley(1000);
if (k1 == 0) {
function();
}
while (!k1);
}
}
這樣,我們寫一個切換LED狀態的函數:
void switchLED() {
OneLED = ~OneLED;
}
然后我們在主函數中將它傳遞到keypros()函數中:
int main() {
while (1) {
keypros(switchLED);
}
}
即我們希望在單擊按鈕后發生什么事件,就將這個事件的函數傳遞到keypros()
中即可,這樣按鈕控制事件的框架就搭好了。
按鈕點擊計數器
既然框架已經搭好,我們繼續編寫事件函數即可。
此時事件為計數器+1並顯示出來。
u8 code smgduan[16] = {
0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07,
0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71
};
u8 addOneDigitCnt = 1;
void addOneDigit() {
P0 = smgduan[addOneDigitCnt];
addOneDigitCnt++;
if (addOneDigitCnt == 16) {
addOneDigitCnt = 0;
}
}
int main() {
P0 = smgduan[0];
while (1) {
keypros(addOneDigit);
}
}
運行效果:
按鈕點擊流水燈
我們點擊按鈕是LED燈向前傳遞一格。
代碼:
void LEDMove() {
LED <<= 1;
if (LED != 0xfe) {
LED += 1;
}
}
int main() {
LED = 0xfe;
while (1) {
keypros(LEDMove);
}
}
運行效果: