玩命加载中 . . .

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」,更多精彩内容分享。

扫码关注


文章作者: Tony
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Tony !