始于1950年的數(shù)字革命將所有現(xiàn)有的機(jī)械和模擬電子結(jié)構(gòu)轉(zhuǎn)變?yōu)閿?shù)字計(jì)算機(jī)。由于數(shù)字電子產(chǎn)品的增長(zhǎng)呈指數(shù)級(jí)增長(zhǎng),今天一個(gè)人幾乎不可能抗拒使用任何電子設(shè)備。從叫醒你的鬧鐘和為你提供早餐的烤面包機(jī)開(kāi)始,一切都是數(shù)字電子產(chǎn)品的貢獻(xiàn)??紤]到所有這些,對(duì)我們自己的東西進(jìn)行編程真的很令人興奮,這些東西可以完成簡(jiǎn)單而有用的任務(wù),比如我們將在這個(gè)項(xiàng)目中使用PIC 微控制器構(gòu)建的鬧鐘。
該鬧鐘將有一個(gè) 16x2 LCD 顯示屏,將顯示當(dāng)前時(shí)間和設(shè)置時(shí)間。我們將在需要時(shí)使用幾個(gè)按鈕來(lái)設(shè)置鬧鐘時(shí)間。使用DS3231RTC模塊可以跟蹤當(dāng)前時(shí)間,我們將使用IIC通信從RTC模塊獲取這些值。我們已經(jīng)了解了 RTC 模塊以及如何將其與 PIC 接口。因此,建議通讀該教程,我們將跳過(guò)該教程中涵蓋的大部分信息。
所需材料:
面包板 – 2Nos
PIC16F877A
20兆赫晶體
33pf 電容器 – 2 常開(kāi)
DS3231 RTC 模塊
16*2液晶顯示模塊
10K 鍋
10k 和 1K 電阻
按鈕 – 5 否
蜂鳴器
連接線(xiàn)
先決條件:
本項(xiàng)目要求您了解一些有關(guān)PIC微控制器以及如何對(duì)其進(jìn)行編程的基礎(chǔ)知識(shí)。我們將在這個(gè)項(xiàng)目中使用GPIO,LCD顯示器和RTC模塊。因此,最好事先學(xué)習(xí)如何使用這些模塊。
電路圖:
這個(gè)基于PIC的鬧鐘項(xiàng)目的電路圖如下所示,該項(xiàng)目是使用proteus軟件創(chuàng)建的。該項(xiàng)目還將用于進(jìn)一步的仿真。
五個(gè)按鈕將作為輸入,用于將鬧鐘設(shè)置為所需時(shí)間。因此,所有按鈕的一端接地,另一端連接到PORTB引腳,這些引腳上將使用內(nèi)部上拉電阻,以避免引腳浮動(dòng)。蜂鳴器將充當(dāng)輸出,并在警報(bào)被觸發(fā)并連接到 PORT S 引腳時(shí)給我們發(fā)出嗶嗶聲。當(dāng)前時(shí)間始終由DS3231RTC模塊跟蹤,PIC通過(guò)I2C總線(xiàn)從該模塊接收數(shù)據(jù),因此RTC模塊的SCL和SDA引腳連接到PIC控制器的SCL和SDA引腳。LCD顯示器連接到PIC的PORTD,用于顯示當(dāng)前時(shí)間和設(shè)定時(shí)間。在此處了解有關(guān)DS3231 RTC模塊與PIC配合使用的更多信息。
整個(gè)電路可以構(gòu)建在面包板上。由于有幾十根電線(xiàn)要連接,所以請(qǐng)耐心等待并確保連接正確。完成連接后,我的硬件設(shè)置如下所示
我使用面包板模塊和 12V 適配器為模塊供電。這是我的+5V電源電壓源。此外,我還必須使用兩個(gè)面包板來(lái)保持電路清潔。如果您希望制作更強(qiáng)大的項(xiàng)目,您還可以將整個(gè)電路焊接到性能板上。
鬧鐘編程:
此鬧鐘項(xiàng)目的完整PIC程序可以在此頁(yè)面底部找到。該項(xiàng)目還需要三個(gè)庫(kù)來(lái)使用LCD,I2C和RTC和PIC。
在進(jìn)入主程序之前,我們必須定義使用更有意義的名稱(chēng)使用的引腳。這樣,在編程過(guò)程中就很容易使用它們。我們程序中定義的引腳如下所示
//Define the LCD pins
#define RS RD2 //Reset pin of LCD
#define EN RD3 //Enable pin of LCD
#define D4 RD4 //Data bit 0 of LCD
#define D5 RD5 //Data bit 1 of LCD
#define D6 RD6 //Data bit 2 of LCD
#define D7 RD7 //Data bit 3 of LCD
//Define Buttons
#define MB RB1 //The middle button
#define LB RB0 //Left button
#define RB RB2 //Right button
#define UB RB3 //Upper Button
#define BB RB4 //Bottom button
//Define Buzz
#define BUZZ RD1 //Buzzer is connected to RD1
在 main 函數(shù)中,我們首先聲明輸入和輸出引腳。在我們的項(xiàng)目中,PORTB 用于按鈕,這是一個(gè)輸入設(shè)備,因此我們將它們的引腳設(shè)置為輸入,PORTD 用于 LCD 和蜂鳴器,因此我們將它們的引腳設(shè)置為輸出。此外,引腳不應(yīng)懸空,I/O引腳應(yīng)始終連接到地或+5V電壓。在我們的例子中,對(duì)于按鈕,當(dāng)按鈕未按下時(shí),引腳不會(huì)連接到任何東西,因此我們使用內(nèi)部上拉電阻器,該電阻器在不使用時(shí)將引腳設(shè)置為高電平。這是使用控制寄存器完成的,如下所示
TRISD = 0x00; //Make Port D pins as outptu for LCD interfacing
TRISB = 0xFF; //Switchs are declared as input pins
OPTION_REG = 0b00000000; //Enable pull up Resistor on port B for switchs
BUZZ = 0; //Turn of buzzer
由于我們有與主程序鏈接的LCD和I2C頭文件,因此我們可以通過(guò)調(diào)用一個(gè)簡(jiǎn)單的函數(shù)來(lái)開(kāi)始LCD初始化。I2C初始化也可以這樣做。在這里,我們以 100kHz 開(kāi)始 I2C 通信,因?yàn)?RTC 模塊以 100kHz 工作。
Lcd_Start(); // Initialize LCD module
I2C_Initialize(100); //Initialize I2C Master with 100KHz clock
下面的函數(shù)用于在RTC模塊上設(shè)置時(shí)間和日期,一旦設(shè)置了時(shí)間和日期,請(qǐng)刪除此行。否則,每次啟動(dòng)程序時(shí),都會(huì)一次又一次地設(shè)置時(shí)間和日期
//Remove the below line once time and date is set for the first time.
Set_Time_Date(); //set time and date on the RTC module
為了指示程序正在啟動(dòng),我們顯示一個(gè)小的介紹屏幕,其中顯示項(xiàng)目名稱(chēng)和網(wǎng)站名稱(chēng),如下所示
//Give an intro message on the LCD
Lcd_Clear();
Lcd_Set_Cursor(1,1);
Lcd_Print_String("Alarm Clock");
Lcd_Set_Cursor(2,1);
Lcd_Print_String(" -Circuit Digest");
__delay_ms(1500);
接下來(lái)在 while 循環(huán)中,我們需要從 RTC 模塊讀取當(dāng)前時(shí)間和日期,這可以通過(guò)調(diào)用以下函數(shù)來(lái)完成。
Update_Current_Date_Time(); //Read the current date and time from RTC module
調(diào)用上述函數(shù)將使用當(dāng)前值更新變量 sec、min 和 hour。為了在LCD屏幕上顯示它們,我們必須使用以下代碼將它們拆分為單個(gè)字符。
//Split the into char to display on lcd
char sec_0 = sec % 10;
char sec_1 = (sec / 10);
char min_0 = min % 10;
char min_1 = min / 10;
char hour_0 = hour % 10;
char hour_1 = hour / 10;
接下來(lái),我們更新LCD屏幕上的值。當(dāng)前時(shí)間將顯示在第一行,必須觸發(fā)警報(bào)的設(shè)置時(shí)間顯示在第二行。執(zhí)行相同操作的代碼如下所示。
//Display the Current Time on the LCD screen
Lcd_Clear();
Lcd_Set_Cursor(1, 1);
Lcd_Print_String("TIME: ");
Lcd_Print_Char(hour_1 + '0');
Lcd_Print_Char(hour_0 + '0');
Lcd_Print_Char(':');
Lcd_Print_Char(min_1 + '0');
Lcd_Print_Char(min_0 + '0');
Lcd_Print_Char(':');
Lcd_Print_Char(sec_1 + '0');
Lcd_Print_Char(sec_0 + '0');
//Display the Date on the LCD screen
Lcd_Set_Cursor(2, 1);
Lcd_Print_String("Alarm: ");
Lcd_Print_Char(alarm_val[0] + '0');
Lcd_Print_Char(alarm_val[1] + '0');
Lcd_Print_Char(':');
Lcd_Print_Char(alarm_val[2] + '0');
Lcd_Print_Char(alarm_val[3] + '0');
現(xiàn)在,我們已經(jīng)在LCD上顯示了時(shí)間和設(shè)置時(shí)間,我們必須檢查用戶(hù)是否正在嘗試設(shè)置鬧鐘時(shí)間。為此,用戶(hù)必須按下中間按鈕,因此我們將檢查是否按下中間按鈕并切換變量以進(jìn)入警報(bào)設(shè)置模式。將再次按下相同的按鈕以確認(rèn)已設(shè)置值,在這種情況下,我們必須退出警報(bào)設(shè)置模式。因此,我們使用下面的代碼行來(lái)更改變量set_alarm的狀態(tài)。
//Use middle button to check if alarm has to be set
if (MB == 0 && set_alarm == 0) { //If middle button is pressed and alarm is not turned on
while (!MB); //Wait till button is released
set_alarm = 1; //start setting alarm value
}
if (MB == 0 && set_alarm == 1) { //If middle button is pressed and alarm is not turned off
while (!MB); //Wait till button is released
set_alarm = 0; //stop setting alarm value
}
如果用戶(hù)按下了中間按鈕,則表示他正在嘗試設(shè)置鬧鐘時(shí)間。在這種情況下,程序使用上面的代碼進(jìn)入警報(bào)設(shè)置模式。在警報(bào)設(shè)置模式下,如果用戶(hù)按下左或右按鈕,則意味著我們必須向左或向右移動(dòng)光標(biāo)。為此,我們只需遞增遞減光標(biāo)必須放置的位置值
if (LB == 0) { //If left button is pressed
while (!LB); //Wait till button is released
pos--; //Then move the cursor to left
}
if (RB == 0) { //If right button is pressed
while (!RB); //Wait till button is released
pos++; //Move cursor to right
}
在將按鈕與微控制器或微處理器一起使用時(shí),有一個(gè)常見(jiàn)問(wèn)題需要解決。此問(wèn)題稱(chēng)為開(kāi)關(guān)彈跳。也就是說(shuō),當(dāng)按下按鈕時(shí),它可能會(huì)給MCU / MPU發(fā)出嘈雜的脈沖,這可能會(huì)偽造MCU的多個(gè)條目。這個(gè)問(wèn)題可以通過(guò)在開(kāi)關(guān)上增加一個(gè)電容或在檢測(cè)到按鈕按下后立即使用延遲功能來(lái)解決。這種類(lèi)型的解決方案稱(chēng)為去抖動(dòng)。在這里,我們使用了一個(gè)while循環(huán)來(lái)將程序固定到位,直到按鈕被釋放。這不是最好的去抖動(dòng)解決方案,但對(duì)我們來(lái)說(shuō),它會(huì)很好地工作。
while (!RB);
與左右按鈕類(lèi)似,我們也有上下按鈕,可用于增加或減少鬧鐘時(shí)間的值。執(zhí)行相同操作的代碼如下所示。請(qǐng)注意,設(shè)置的警報(bào)時(shí)間的每個(gè)字符都由數(shù)組的索引值尋址。這是我們可以輕松訪問(wèn)必須更改其值的所需字符。
if (UB == 0) { //If upper button is pressed
while (!UB); //Wait till button is released
alarm_val[(pos - 8)]++; //Increase that particular char value
}
if (BB == 0) { //If lower button is pressed
while (!UB); //Wait till button is released
alarm_val[(pos - 8)]--; //Decrease that particular char value
}
設(shè)置鬧鐘時(shí)間后,用戶(hù)將再次短按中間按鈕。然后我們可以開(kāi)始將當(dāng)前時(shí)間與設(shè)置的時(shí)間進(jìn)行比較。通過(guò)檢查當(dāng)前時(shí)間的每個(gè)字符是否等于設(shè)置時(shí)間的字符來(lái)進(jìn)行比較。如果值相等,那么我們通過(guò)設(shè)置 trigger_alarm 變量來(lái)觸發(fā)警報(bào),否則我們只是比較直到它相等。
//IF alarm is set Check if the set value is equal to current value
if (set_alarm == 0 && alarm_val[0] == hour_1 && alarm_val[1] == hour_0 && alarm_val[2] == min_1 && alarm_val[3] == min_0)
trigger_alarm = 1; //Turn on trigger if value match
如果設(shè)置了警報(bào),我們必須發(fā)出蜂鳴器以提醒用戶(hù)警報(bào)。這可以通過(guò)簡(jiǎn)單地定期切換蜂鳴器來(lái)完成,如下所示。
if (trigger_alarm) { //If alarm is triggered
//Beep the buzzer
BUZZ = 1;
__delay_ms(500);
BUZZ = 0;
__delay_ms(500);
}
模擬:
該程序也可以使用變形蟲(chóng)軟件進(jìn)行模擬。只需重新創(chuàng)建上面顯示的電路,并將十六進(jìn)制文件加載到 PIC 中即可。
模擬期間拍攝的屏幕截圖如下所示
當(dāng)您嘗試向項(xiàng)目添加新功能時(shí),模擬變得非常有用。您 還 可以 使用 I2C 調(diào)試 器 模 塊 來(lái) 檢查 哪些 數(shù)據(jù) 通過(guò) I2C 總 線(xiàn) 進(jìn)出。您可以嘗試按下按鈕并設(shè)置鬧鐘時(shí)間。當(dāng)設(shè)定的時(shí)間等于當(dāng)前時(shí)間時(shí),蜂鳴器將變高。
使用PIC16F877A的數(shù)字鬧鐘工作:
在試驗(yàn)板上構(gòu)建電路,從下載鏈接獲取代碼并使用MplabX和XC8編譯器進(jìn)行編譯。
編譯后,使用PicKit3編程器將程序上傳到您的硬件。電路圖中還顯示了將拾取器編程器連接到PIC IC的連接。程序上傳后,您應(yīng)該會(huì)看到介紹屏幕,然后顯示時(shí)間,然后您可以使用按鈕設(shè)置鬧鐘時(shí)間。通電時(shí)的硬件設(shè)置如下所示。
當(dāng)鬧鐘時(shí)間與當(dāng)前時(shí)間匹配時(shí),蜂鳴器將開(kāi)始發(fā)出蜂鳴聲以提醒用戶(hù)。完整的工作可以在下面的視頻中找到。該項(xiàng)目有很多選擇可以構(gòu)建。RTC 模塊可以跟蹤任何時(shí)間和日期,因此您可以在所需的任何時(shí)間/日期執(zhí)行計(jì)劃任務(wù)。您還可以連接風(fēng)扇或燈等交流電器,并在需要時(shí)安排其打開(kāi)或關(guān)閉。
/*
* Program: Alarm Clock Using PIC
* Author: B.Aswinth Raj
* More info: www.circuitdigest.com
* Created on 11 May, 2018, 3:02 PM
*/
#pragma config FOSC = HS // Oscillator Selection bits (HS oscillator)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON // Power-up Timer Enable bit (PWRT enabled)
#pragma config BOREN = ON // Brown-out Reset Enable bit (BOR enabled)
#pragma config LVP = OFF // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off)
#pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)
#define _XTAL_FREQ 20000000 //We are running on 20MHz crystal
//Define the LCD pins
#define RS RD2 //Reset pin of LCD
#define EN RD3 //Enable pin of LCD
#define D4 RD4 //Data bit 0 of LCD
#define D5 RD5 //Data bit 1 of LCD
#define D6 RD6 //Data bit 2 of LCD
#define D7 RD7 //Data bit 3 of LCD
//Define Buttons
#define MB RB1 //The middle button
#define LB RB0 //Left button
#define RB RB2 //Right button
#define UB RB3 //Upper Button
#define BB RB4 //Bottom button
//Define Buzz
#define BUZZ RD1 //Buzzer is connected to RD1
/*Set the current value of date and time below*/
int sec = 00;
int min = 55;
int hour = 10;
int date = 06;
int month = 05;
int year = 18;
/*Time and Date Set*/
/*Vars for Alarm clock*/
char set_alarm = 0;
char trigger_alarm = 0;
char pos = 8;
char jump = 0;
char alarm_h0 = 0;
char alarm_h1 = 0;
char alarm_m0 = 0;
char alarm_m1 = 0;
int alarm_val[4] = {0, 0, 0, 0};
/*End of var declaration*/
#include
#include "lcd.h" //Header for using LCD module
#include "PIC16F877a_I2C.h" // Header for using I2C protocal
#include "PIC16F877a_DS3231.h" //Header for using DS3231 RTC module
int main() {
TRISD = 0x00; //Make Port D pins as outptu for LCD interfacing
TRISB = 0xFF; //Switchs are declared as input pins
OPTION_REG = 0b00000000; //Enable pull up Resistor on port B for switchs
BUZZ = 0; //Turn of buzzer
Lcd_Start(); // Initialize LCD module
I2C_Initialize(100); //Initialize I2C Master with 100KHz clock
//Remove the below line once time and date is set for the first time.
Set_Time_Date(); //set time and date on the RTC module
//Give an intro message on the LCD
Lcd_Clear();
Lcd_Set_Cursor(1,1);
Lcd_Print_String("Alarm Clock");
Lcd_Set_Cursor(2,1);
Lcd_Print_String(" -Circuit Digest");
__delay_ms(1500);
while (1) {
Update_Current_Date_Time(); //Read the current date and time from RTC module
//Split the into char to display on lcd
char sec_0 = sec % 10;
char sec_1 = (sec / 10);
char min_0 = min % 10;
char min_1 = min / 10;
char hour_0 = hour % 10;
char hour_1 = hour / 10;
//Display the Current Time on the LCD screen
Lcd_Clear();
Lcd_Set_Cursor(1, 1);
Lcd_Print_String("TIME: ");
Lcd_Print_Char(hour_1 + '0');
Lcd_Print_Char(hour_0 + '0');
Lcd_Print_Char(':');
Lcd_Print_Char(min_1 + '0');
Lcd_Print_Char(min_0 + '0');
Lcd_Print_Char(':');
Lcd_Print_Char(sec_1 + '0');
Lcd_Print_Char(sec_0 + '0');
//Display the Date on the LCD screen
Lcd_Set_Cursor(2, 1);
Lcd_Print_String("Alarm: ");
Lcd_Print_Char(alarm_val[0] + '0');
Lcd_Print_Char(alarm_val[1] + '0');
Lcd_Print_Char(':');
Lcd_Print_Char(alarm_val[2] + '0');
Lcd_Print_Char(alarm_val[3] + '0');
__delay_ms(50);
//Use middle button to check if alarm has to be set
if (MB == 0 && set_alarm == 0) { //If middle button is pressed and alarm is not turned on
while (!MB); //Wait till button is released
set_alarm = 1; //start setting alarm value
}
if (MB == 0 && set_alarm == 1) { //If middle button is pressed and alarm is not turned off
while (!MB); //Wait till button is released
set_alarm = 0; //stop setting alarm value
}
//If alarm has to be navigate through each digit
if (set_alarm == 1) {
if (LB == 0) { //If left button is pressed
while (!LB); //Wait till button is released
pos--; //Then move the cursor to left
}
if (RB == 0) { //If right button is pressed
while (!RB); //Wait till button is released
pos++; //Move cursor to right
}
if (pos >= 10) //eliminate ":" symbol if cursor reaches there
{
jump = 1; //Jump over the ":" symbol
} else
jump = 0; //get back to normal movement
if (UB == 0) { //If upper button is pressed
while (!UB); //Wait till button is released
alarm_val[(pos - 8)]++; //Increase that particular char value
}
if (BB == 0) { //If lower button is pressed
while (!UB); //Wait till button is released
alarm_val[(pos - 8)]--; //Decrease that particular char value
}
Lcd_Set_Cursor(2, pos + jump);
Lcd_Print_Char(95); //Display "_" to indicate cursor position
}
//IF alarm is set Check if the set value is equal to current value
if (set_alarm == 0 && alarm_val[0] == hour_1 && alarm_val[1] == hour_0 && alarm_val[2] == min_1 && alarm_val[3] == min_0)
trigger_alarm = 1; //Turn on trigger if value match
if (trigger_alarm) { //If alarm is triggered
//Beep the buzzer
BUZZ = 1;
__delay_ms(500);
BUZZ = 0;
__delay_ms(500);
}
__delay_ms(200);//Update interval
}
return 0;
}
-
微控制器
+關(guān)注
關(guān)注
48文章
7555瀏覽量
151431 -
PIC16F877A
+關(guān)注
關(guān)注
2文章
43瀏覽量
21819 -
數(shù)字鬧鐘
+關(guān)注
關(guān)注
0文章
6瀏覽量
5452
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論