Arduino提高篇24—搖桿調節時鐘時間

對於電子時鐘而言,雖然我們可以在程序中對RTC芯片寫入初始時間數據,但按鍵調時功能也是必不可少的,它可以讓我們更加靈活的進行時間調節校準。本篇我們使用搖桿按鍵來調節時間數據,採用多級菜單的思路進行設計,關於多級菜單的介紹可以參考之前的文章Arduino提高篇15—搖桿操作OLED多級菜單

1. 菜單顯示結構

本實驗中顯示菜單結構如下圖所示:

菜單結構

其中menu00是首頁正常的時間日期顯示界面,然後通過搖桿按鍵切換頁面,進行日期調節和時間調節選擇,然後對應跳轉年月日和時分秒的調節頁面。

根據結構圖,結合多級菜單思路,很容易得到搖桿按鍵與界面對應的相關數據。

//定義按鍵操作數據
KEY_TABLE table[9] =
{
  {0, 0, 0, 0, 1, (*menu00)},
  {1, 1, 2, 0, 3, (*menu11)},
  {2, 1, 2, 0, 6, (*menu12)},
  {3, 3, 4, 1, 3, (*menu21)},
  {4, 3, 5, 1, 4, (*menu22)},
  {5, 4, 5, 1, 5, (*menu23)},
  {6, 6, 7, 2, 6, (*menu24)},
  {7, 6, 8, 2, 7, (*menu25)},
  {8, 7, 8, 2, 8, (*menu26)},
};

2. 實驗材料

  • Uno R3開發板
  • 配套USB數據線
  • 公對母杜邦線
  • 麪包板及配套連接線
  • OLED顯示屏
  • DS1302模塊
  • 雙軸按鍵搖桿模塊

3. 實驗步驟

1. 根據原理圖搭建電路圖。

DS1302模塊的VCC和GND連接Uno開發板的3.3V和GND。DS1302模塊的CLK、DAT、RST對應連接Uno開發板的4、3、2引腳。OLED的VCC和GND分別連接開發板的3.3V和GND,OLED的SDA、SCL分別連接開發板的A4、A5引腳。雙軸按鍵搖桿模塊的VCC、GND分別連接開發板的5V、GND,模塊的X軸輸出、Y軸輸出分別連接開發板的模擬引腳A0、A1。

實驗原理圖如下圖所示:

實驗原理圖

實物連接圖如下圖所示:

實物連接圖

2. 新建sketch,拷貝如下代碼替換自動生成的代碼並進行保存。

#include <DS1302.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

Adafruit_SSD1306 oled(128, 64, &Wire, -1);
DS1302 rtc(2, 3, 4); //對應DS1302的RST,DAT,CLK

//搖桿相關變量
#define pinX  A0
#define pinY  A1

int valueX = 0;
int valueY = 0;
unsigned char keyValue = 0;

unsigned char menu = 0;

int sec_temp;
Time set_temp(2020, 4, 25, 21, 50, 50, 7);//用於存儲正在修改的時間數據

//多級菜單相關變量
//定義按鍵結構體
typedef struct
{
  unsigned char index;
  unsigned char up;
  unsigned char down;
  unsigned char left;
  unsigned char right;
  void (*operation)(void);
} KEY_TABLE;

unsigned char funIndex = 0;
void (*current)(void);
void menu00(void);
void menu11(void);
void menu12(void);
void menu21(void);
void menu22(void);
void menu23(void);
void menu24(void);
void menu25(void);
void menu26(void);

//定義按鍵操作數據
KEY_TABLE table[9] =
{
  {0, 0, 0, 0, 1, (*menu00)},
  {1, 1, 2, 0, 3, (*menu11)},
  {2, 1, 2, 0, 6, (*menu12)},
  {3, 3, 4, 1, 3, (*menu21)},
  {4, 3, 5, 1, 4, (*menu22)},
  {5, 4, 5, 1, 5, (*menu23)},
  {6, 6, 7, 2, 6, (*menu24)},
  {7, 6, 8, 2, 7, (*menu25)},
  {8, 7, 8, 2, 8, (*menu26)},
};

void menu00(void)
{
  if (menu == 1)//從調時界面退出後將調節後的時間數據寫入DS1302
  {
    initRTCTime(set_temp);
    menu = 0;
  }
  updatTime();
}

void menu11(void)
{
  menu = 1;//進入調時界面

  oled.clearDisplay();//清屏
  oled.setCursor(15, 2);//設置顯示位置
  oled.println("-Set Dat-");
  oled.setCursor(2, 25);//設置顯示位置
  oled.println("->1.Dat");
  oled.setCursor(2, 50);//設置顯示位置
  oled.println("  2.Tim");
  oled.display(); // 開顯示
}

void menu12(void)
{
  oled.clearDisplay();//清屏
  oled.setCursor(15, 2);//設置顯示位置
  oled.println("-Set Tim-");
  oled.setCursor(2, 25);//設置顯示位置
  oled.println("  1.Dat");
  oled.setCursor(2, 50);//設置顯示位置
  oled.println("->2.Tim");
  oled.display(); // 開顯示
}

void menu21(void)
{
  set_temp.yr++;
  oled.clearDisplay();//清屏
  oled.setCursor(2, 2);//設置顯示位置
  oled.print("->Yer:");
  oled.println(set_temp.yr);
  oled.setCursor(2, 25);//設置顯示位置
  oled.print("  Mon: ");
  oled.println(set_temp.mon);
  oled.setCursor(2, 48);//設置顯示位置
  oled.print("  Day: ");
  oled.println(set_temp.date);
  oled.display(); // 開顯示

  if (set_temp.yr >= 2030)
  {
    set_temp.yr = 2019;
  }
}

void menu22(void)
{
  set_temp.mon++;
  oled.clearDisplay();//清屏
  oled.setCursor(2, 2);//設置顯示位置
  oled.print("  Yer:");
  oled.println(set_temp.yr);
  oled.setCursor(2, 25);//設置顯示位置
  oled.print("->Mon: ");
  oled.println(set_temp.mon);
  oled.setCursor(2, 48);//設置顯示位置
  oled.print("  Day: ");
  oled.println(set_temp.date);
  oled.display(); // 開顯示

  if (set_temp.mon >= 12)
  {
    set_temp.mon = 0;
  }
}

void menu23(void)
{
  set_temp.date++;
  oled.clearDisplay();//清屏
  oled.setCursor(2, 2);//設置顯示位置
  oled.print("  Yer:");
  oled.println(set_temp.yr);
  oled.setCursor(2, 25);//設置顯示位置
  oled.print("  Mon: ");
  oled.println(set_temp.mon);
  oled.setCursor(2, 48);//設置顯示位置
  oled.print("->Day: ");
  oled.println(set_temp.date);
  oled.display(); // 開顯示

  if (set_temp.date >= 31)
  {
    set_temp.date = 0;
  }
}

void menu24(void)
{
  set_temp.hr++;
  oled.clearDisplay();//清屏
  oled.setCursor(2, 2);//設置顯示位置
  oled.print("->Hor: ");
  oled.println(set_temp.hr);
  oled.setCursor(2, 25);//設置顯示位置
  oled.print("  Min: ");
  oled.println(set_temp.min);
  oled.setCursor(2, 48);//設置顯示位置
  oled.print("  Sec: ");
  oled.println(set_temp.sec);
  oled.display(); // 開顯示

  if (set_temp.hr >= 24)
  {
    set_temp.hr = 0;
  }
}

void menu25(void)
{
  set_temp.min++;
  oled.clearDisplay();//清屏
  oled.setCursor(2, 2);//設置顯示位置
  oled.print("  Hor: ");
  oled.println(set_temp.hr);
  oled.setCursor(2, 25);//設置顯示位置
  oled.print("->Min: ");
  oled.println(set_temp.min);
  oled.setCursor(2, 48);//設置顯示位置
  oled.print("  Sec: ");
  oled.println(set_temp.sec);
  oled.display(); // 開顯示

  if (set_temp.min >= 60)
  {
    set_temp.min = 0;
  }
}

void menu26(void)
{
  set_temp.sec++;
  oled.clearDisplay();//清屏
  oled.setCursor(2, 2);//設置顯示位置
  oled.print("  Hor: ");
  oled.println(set_temp.hr);
  oled.setCursor(2, 25);//設置顯示位置
  oled.print("  Min: ");
  oled.println(set_temp.min);
  oled.setCursor(2, 48);//設置顯示位置
  oled.print("->Sec: ");
  oled.println(set_temp.sec);
  oled.display(); // 開顯示

  if (set_temp.sec >= 60)
  {
    set_temp.sec = 0;
  }
}

//按鍵掃描函數
unsigned char keyScan(void)
{
  static unsigned char keyUp = 1;

  valueX = analogRead(pinX);
  valueY = analogRead(pinY);

  if (keyUp && ((valueX <= 10) || (valueX >= 1010) || (valueY <= 10) || (valueY >= 1010)))
  {
    delay(10);
    keyUp = 0;
    if (valueX <= 10)return 1;
    else if (valueX >= 1010)return 2;
    else if (valueY <= 10)return 3;
    else if (valueY >= 1010)return 4;
  } else if ((valueX > 10) && (valueX < 1010) && (valueY > 10) && (valueY < 1010))keyUp = 1;
  return 0;
}

void initRTCTime(Time t)//初始化RTC時鐘
{
  rtc.writeProtect(false); //關閉寫保護
  rtc.halt(false); //清除時鐘停止標誌
  rtc.time(t);//向DS1302設置時間數據
}

void show_time(Time tim)
{
  char date[20];
  char timer[20];
  snprintf(date, sizeof(date), "%04d-%02d-%02d",
           tim.yr, tim.mon, tim.date);
  snprintf(timer, sizeof(timer), "%02d:%02d:%02d",
           tim.hr, tim.min, tim.sec);

  oled.clearDisplay();//清屏
  oled.setCursor(15, 2);//設置顯示位置
  oled.println("--CLOCK--");
  oled.setCursor(4, 25);//設置顯示位置
  oled.println(date);
  oled.setCursor(18, 50);//設置顯示位置
  oled.println(timer);
  oled.display(); // 開顯示
}

void updatTime()//更新時間數據
{
  Time tim = rtc.time(); //從DS1302獲取時間數據
  set_temp = tim;//獲取時間數據已備調節
  if (tim.sec != sec_temp) { //一秒刷新一次
    show_time(tim);
  }
  sec_temp = tim.sec;
}

void setup() {
  oled.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  oled.setTextColor(WHITE);//開像素點發光
  oled.clearDisplay();//清屏
  oled.setTextSize(2); //設置字體大小
}

void loop() {
  keyValue = keyScan();

  if (keyValue != 0) //每發生一次有效按鍵就根據按鍵功能獲取對應函數並執行
  {
    switch (keyValue)//獲取按鍵對應序號
    {
      case 1: funIndex = table[funIndex].right; break;
      case 2: funIndex = table[funIndex].left; break;
      case 3: funIndex = table[funIndex].down; break;
      case 4: funIndex = table[funIndex].up; break;
    }
    current = table[funIndex].operation;//根據需要獲取對應需要執行的函數
    (*current)();//執行獲取到的函數
  }

  if (menu == 0)//只有在首頁才進行時間刷新
  {
    updatTime();
  }
}

3. 連接開發板,設置好對應端口號和開發板類型,進行程序下載。

程序下載

4. 實驗現象

搖桿操作調節時間,效果如下:

實驗現象


關注公衆號「TonyCode」,更多精彩內容分享。
回覆「1024」獲取1000G學習資料。
個人博客
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章