☛ 功能說明

使用 Arduino 開發板讀取 DS1302 時鐘晶片應用電路的日期時間及 DHT11 溫濕度感測器的溫濕度數值,以按鍵開關切換八位數七段顯示器顯示日期、時間及溫濕度,以 Serial Monitor 視窗顯示日期時間及調整日期時間。 當七段顯示器顯示日期時,按一下按鍵開關,切換至顯示時間,當七段顯示器顯示時間時,按一下按鍵開關,切換至顯示溫濕度,當七段顯示器顯示溫濕度時,按一下按鍵開關,切換至設定模式,七段顯示器不顯示日期、時間及溫濕度,透過 Serial Monitor 監控視窗顯示並設定時間。

☛ 使用材料

Arduino UNO R3 開發板 × 1、MAX7219 八位數七段顯示器模組、DHT11 溫濕度感測器模組、按鍵開關、電阻 10KΩ × 3、DS1302 時鐘晶片 × 1、32.768kHz 晶振 × 1、DS1302 函式庫 、MAX7219 LED 控制函式庫DHT11 第三方函式庫 。

☛ 電路圖及麵包板接線圖

☛ 程式碼

#include <stdio.h>
#include <string.h>
#include <DS1302.h>
#include <DHT.h>
#include <LedControl.h>

uint8_t CE_PIN   = 5;                  // CE(DS1302 pin5) -> Arduino D5
uint8_t IO_PIN   = 6;                  // IO(DS1302 pin6) -> Arduino D6
uint8_t SCLK_PIN = 7;                  // SCLK(DS1302 pin7) -> Arduino D7

LedControl lc=LedControl(12,11,10,1);

char buf[50];                          // 日期變數緩存
char day[10];
String comdata = "";                   // 序列埠數據緩存
int numdata[7] ={0}, j = 0, mark = 0;
char led_Data[8];                      // 時間顯示的資料格式 00-00-00

const int sw=8;                        //按鍵開關連接至數位接腳第 8 腳。
const int debounceDelay=20;            //按鍵開關穩定所需的時間
int val;                               //按鍵開關狀態
int swValue=0;                         //按鍵開關數值
int humidity;                          //濕度變數
int temperature;                       //溫度變數

DS1302 rtc(CE_PIN, IO_PIN, SCLK_PIN);  // 創建 DS1302 物件
DHT dht;                               // 產生一個 dht 物件

void setup()
{
     Serial.begin(9600);              //設定序列埠傳輸速率為9600bps
     pinMode(sw,INPUT_PULLUP);        //設定數位第 8 腳為輸入模式 ( 使用內部上拉電阻 )。
     
     rtc.write_protect(false);
     rtc.halt(false);

     dht.setup(9);                    // DHT11 數據接腳連接數位接腳 9
     
     lc.shutdown(0,false);            // The MAX72XX is in power-saving mode on startup,we have to do a wakeup call
     lc.setIntensity(0,8);            // Set the brightness to a medium values and clear the display
     lc.clearDisplay(0);
}

void loop()
{
     val=digitalRead(sw);              //讀取按鍵狀態。
     if(val==LOW)                      //按鍵開關被按下?
     {
        swValue++;                     //按鍵開關數值加一
        delay(debounceDelay);          //消除按鍵開關的不穩定狀態 ( 機械彈跳 )。
        while(digitalRead(sw)==LOW)    //按鍵開關已放開?
              ;                        //等待放開按鍵開關。
     }
     switch(swValue)
     {
            case 0:                    //七段顯示器顯示日期
                    getDate();
                    break;
            case 1:                    //七段顯示器顯示時間
                    getTime();
                    break;
            case 2:                    //七段顯示器顯示溫濕度
                    delay(dht.getMinimumSamplingPeriod());// 根據溫濕度感測器型號來選取取樣時間
                    getDHT11();
                    break;             
            case 3:                    //進入日期時間設定模式
                    mode_Setup();
                    for(int i=7;i>=0;i--)
                        led_Data[i]='-';
                    print_time();      //序列埠監控視窗顯示日期時間
                    break;             
            case 4: swValue=0; break;
     }
     if(swValue==2)
        displayDHT11();
     else
        display();
     delay(1000);
}

void mode_Setup()                       //設定模式,由 USB 監控視窗設定及顯示日期時間
{
    while (Serial.available() > 0)      // 當序列埠有數據的時候,將數據拼接到變數comdata
    {
           comdata += char(Serial.read());
           delay(2);
           mark = 1;
    }
    // 以逗號分隔分解 comdata 的字串,分解結果變成轉換成數位到numdata[] 陣列
    if(mark == 1)
    {
        Serial.print("You inputed : ");
        Serial.println(comdata);
        for(int i = 0; i < comdata.length() ; i++)
        {
            if(comdata[i] == ',' || comdata[i] == 0x10 || comdata[i] == 0x13)
            {
                j++;
            }
            else
            {
                numdata[j] = numdata[j] * 10 + (comdata[i] - '0');
            }
        }
        // 將轉換好的numdata湊成時間格式,寫入DS1302
        Time t(numdata[0], numdata[1], numdata[2], numdata[3], numdata[4], numdata[5], numdata[6]);
        rtc.time(t);
        mark = 0;j=0;
        comdata = String("");           // 清空 comdata 變數,以便等待下一次輸入
        /* 清空 numdata */
        for(int i = 0; i < 7 ; i++) numdata[i]=0;
    }  
}

void print_time()
{
    Time t = rtc.time();                // 從 DS1302 獲取當前時間
    memset(day, 0, sizeof(day));        // 將星期從數位轉換為名稱
    switch (t.day)
    {
            case 1: strcpy(day, "Sunday"); break;
            case 2: strcpy(day, "Monday"); break;
            case 3: strcpy(day, "Tuesday"); break;
            case 4: strcpy(day, "Wednesday"); break;
            case 5: strcpy(day, "Thursday"); break;
            case 6: strcpy(day, "Friday"); break;
            case 7: strcpy(day, "Saturday"); break;
    }
    // 將日期代碼格式化湊成buf等待輸出
    snprintf(buf, sizeof(buf), "%s %04d-%02d-%02d %02d:%02d:%02d", day, t.yr, t.mon, t.date, t.hr, t.min, t.sec);
    Serial.println(buf);           // 輸出日期到序列埠
}

char transChar(int number)        // 數值轉換成字元函式
{
     char value;
     switch(number)
     {
            case 0: value='0'; break;
            case 1: value='1'; break;
            case 2: value='2'; break;
            case 3: value='3'; break;
            case 4: value='4'; break;
            case 5: value='5'; break;
            case 6: value='6'; break;
            case 7: value='7'; break;
            case 8: value='8'; break;
            case 9: value='9'; break;
     }
     return value;
}

void getDate()                         // 取得 DS1302 時鐘晶片的日期,轉換成顯示的日期格式
{
     Time t = rtc.time();
     led_Data[0]=transChar(t.yr/1000); // 西元年
     led_Data[1]=transChar(t.yr%1000/100);
     led_Data[2]=transChar(t.yr%1000%100/10);
     led_Data[3]=transChar(t.yr%1000%100%10);
     led_Data[4]=transChar(t.mon/10);  // 月
     led_Data[5]=transChar(t.mon%10);
     led_Data[6]=transChar(t.date/10); // 日
     led_Data[7]=transChar(t.date%10);     
}

void getTime()                        // 取得 DS1302 時鐘晶片的時間,轉換成顯示的時間格式
{
     Time t = rtc.time();
     led_Data[0]=transChar(t.hr/10);  // 時
     led_Data[1]=transChar(t.hr%10);
     led_Data[2]='-';
     led_Data[3]=transChar(t.min/10); // 分
     led_Data[4]=transChar(t.min%10);
     led_Data[5]='-';
     led_Data[6]=transChar(t.sec/10); // 秒
     led_Data[7]=transChar(t.sec%10);
}

void getDHT11()                       // 取得 DHT11 溫濕度,轉換成顯示格式
{
    humidity = dht.getHumidity();         // 讀取感測器的濕度值
    temperature = dht.getTemperature();   // 讀取感測器的溫度值

    temperature=temperature*100;
    humidity=humidity*100;

    led_Data[0]=transChar(temperature/1000);  //將溫度值轉換成七段顯示器顯示格式
    led_Data[1]=transChar(temperature%1000/100);
    led_Data[2]=transChar(temperature%1000%100/10);
    led_Data[3]='C';
    led_Data[4]=transChar(humidity/1000);     //將濕度值轉換成七段顯示器顯示格式
    led_Data[5]=transChar(humidity%1000/100);
    led_Data[6]=transChar(humidity%1000%100/10);
    led_Data[7]='H';
}

void display()                        // 顯示時間函式
{
     for(int i=7;i>=0;i--)
         lc.setChar(0,7-i,led_Data[i],false);
}

void displayDHT11()                   // 顯示溫度及濕度
{
     for(int i=7;i>=0;i--)
     {
        if(i==1 || i==5)
           lc.setChar(0,7-i,led_Data[i],true);  //七段顯示器顯示帶小數點數位
        else
           lc.setChar(0,7-i,led_Data[i],false); //七段顯示器顯示不帶小數點數位
     }            
}