/*--------------------------------------------------------------------------1--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------本程序所引用库--------------------------------------------------------------------*/
#include <Wire.h> // I2C通信协议库
#include <Adafruit_GFX.h> // 用于在屏幕上绘制图形元素,是一个通用显示库
#include <Adafruit_SSD1306.h> // 基于GFX库,用于控制SSD1306系列OLED显示屏
/*--------------------------------------------------------------------------2--------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------声明定义-----------------------------------------------------------------------*/
/*****************************< 常量声明 >*****************************/
// OLED宽高度
#define OLED_WIDTH 128 // OLED屏幕的宽度
#define OLED_HEIGHT 64 // OLED屏幕的高度
#define ENCODER_STEP 20 // 每转的步数,即编码器的分辨率
/*****************************< 引脚定义 >*****************************/
const int Encoder_Pin_DT = 3; // 编码器输出引脚1
const int Encoder_Pin_CLK = 4; // 编码器输出引脚2
const int Encoder_Pin_SW = 2; // 按键
/*****************************< 变量定义 >*****************************/
bool CTD_FG = false; // 是否开始countdown倒计时
unsigned long CTD_Time; // 用于存储倒计时时间
unsigned long Pre_Millis;
volatile long Step_Count = 0; // 储存步数(挥发性变量)
bool SET_MODE = false; // 是否进入设置模式
int STAGE = 0; // 0: 调节小时,1: 调节分钟,2: 调节秒
int Hours = 0; // 初始化为0小时
int Minutes = 0; // 初始化为25分钟
int Seconds = 30; // 初始化为0秒
bool blink = false; // 用于设置模式的闪烁效果
unsigned long blinkMillis = 0;
bool Set_FG = 1;
/*****************************< 对象声明 >*****************************/
Adafruit_SSD1306 display = Adafruit_SSD1306(OLED_WIDTH, OLED_HEIGHT, &Wire); // 声明一个控制OLED显示屏的对象
/*--------------------------------------------------------------------------3--------------------------------------------------------------------------*/
/*-------------------------------------------------------------------setup()程序初始化------------------------------------------------------------------*/
void setup() {
/******************************< 启动串口通讯 >******************************/
Serial.begin(9600); // 初始化串口通信,并设置波特率为9600
/******************************< 设置引脚模式 >******************************/
pinMode(Encoder_Pin_DT, INPUT_PULLUP); // 设置引脚为内部电阻输入模式
pinMode(Encoder_Pin_CLK, INPUT_PULLUP); // 设置引脚为内部电阻输入模式
pinMode(Encoder_Pin_SW, INPUT_PULLUP); // 设置引脚为内部电阻输入模式
/********************************< 设置中断 >********************************/
attachInterrupt(digitalPinToInterrupt(Encoder_Pin_DT), update_Encoder, CHANGE);
/******************************< OLED初始化设置 >****************************/
init_OLED();
// 初始化倒计时时间
CTD_Time = (Hours * 3600L) + (Minutes * 60L) + Seconds;
}
/*--------------------------------------------------------------------------4--------------------------------------------------------------------------*/
/*-------------------------------------------------------------------loop()基础循环体-------------------------------------------------------------------*/
void loop() {
bool state = debounce(Encoder_Pin_SW, 40);
// 长按
if (keep_HIGH_Until_Time(state, 1000)) { // 如果长按到一定值
SET_MODE = 0; // 回到倒计时模式
if (CTD_FG) { // 如果倒计时已经开始,长按复位倒计时
CTD_FG = false;
CTD_Time = (Hours * 3600L) + (Minutes * 60L) + Seconds; // 复位倒计时时间
} else { // 否则,启动倒计时
CTD_FG = true;
Pre_Millis = millis(); // 记录倒计时开始时的时间
}
}
if (just_HIGH(state)) Set_FG = 1;
if (get_Duration(state, HIGH) > 500) Set_FG = 0;
// 单击
if (Set_FG) {
if (just_LOW(state)) {
SET_MODE = 1; // 进入调节模式
if (STAGE < 3) {
STAGE++; // 0 1 2 3
} else {
STAGE = 0; // 复位,回到倒计时模式
SET_MODE = 0;
CTD_Time = (Hours * 3600L) + (Minutes * 60L) + Seconds; // 更新倒计时时间
}
}
}
// 倒计时模式
if (!SET_MODE) {
if (CTD_FG) { // 如果倒计时已经开始,执行倒计时逻辑
if (millis() - Pre_Millis >= 1000) { // 每秒减少倒计时
Pre_Millis = millis();
if (CTD_Time > 0) {
CTD_Time--; // 每秒减少倒计时时间
} else {
CTD_FG = false; // 倒计时结束
CTD_Time = (Hours * 3600L) + (Minutes * 60L) + Seconds; // 倒计时结束后复位
}
}
}
// 显示倒计时
display_Countdown(CTD_Time);
}
// 调节模式
else {
if (millis() - blinkMillis >= 500) { // 每500毫秒闪烁
blink = !blink;
blinkMillis = millis();
}
display_Setting(STAGE, Hours, Minutes, Seconds, blink);
}
}
/*--------------------------------------------------------------------------5--------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------函数定义-----------------------------------------------------------------------*/
void init_OLED() {
Wire.begin(); // 初始化 I2C 总线,开始 I2C 通信
display.begin(SSD1306_SWITCHCAPVCC, 0x3C, false, false); // OLED初始化
display.setTextColor(SSD1306_WHITE); // 设置文字颜色
display.setTextSize(2); // 设置文本的大小
display.setTextWrap(true); // 启用文本自动换行功能
display.display(); // 显示屏幕
display.clearDisplay(); // 清屏
display.display(); // 显示屏幕
}
// 显示设置模式的闪烁效果
void display_Setting(int stage, int hours, int minutes, int seconds, bool blink) {
display.clearDisplay();
display.setCursor(2, 16);
display.setTextSize(3);
// 显示小时或空白
if (stage == 1 && blink) {
display.print(" :");
} else {
if (hours < 10) display.print("0");
display.print(hours);
display.print(":");
}
// 显示分钟或空白
if (stage == 2 && blink) {
display.print(" ");
} else {
if (minutes < 10) display.print("0");
display.print(minutes);
}
// 设置秒钟的位置
display.setCursor(92, 23);
display.setTextSize(2);
display.print(":");
// 显示秒钟或空白
if (stage == 3 && blink) {
display.print(" ");
} else {
if (seconds < 10) display.print("0");
display.print(seconds);
}
display.display();
}
// 显示倒计时
void display_Countdown(unsigned long countdownTime) {
display.clearDisplay();
display.setTextSize(2);
display.setCursor(0, 20);
int h = countdownTime / 3600;
int m = (countdownTime % 3600) / 60;
int s = countdownTime % 60;
display.setCursor(2, 16);
display.setTextSize(3);
if (h < 10) display.print("0");
display.print(h);
display.print(":");
if (m < 10) display.print("0");
display.print(m);
display.setCursor(92, 23);
display.setTextSize(2);
display.print(":");
if (s < 10) display.print("0");
display.print(s);
display.display();
}
void update_Encoder() {
static bool Pre_Encoder_State = 0; // 定义一个储存之前引脚状态的变量
bool Cur_Encoder_State = digitalRead(Encoder_Pin_DT); // 先获取DT引脚的值
if ((Cur_Encoder_State == HIGH) && (Pre_Encoder_State == LOW)) { // 检查这个值是否刚刚变为HIGH(上升沿)
if (digitalRead(Encoder_Pin_CLK) == LOW) {
Step_Count= 1;
} else {
Step_Count= -1;
}
if (STAGE == 1) { // 调节小时
Hours = constrain(Hours + Step_Count, 0, 24); // 限制小时范围在0-24
Serial.println(Hours);
} else if (STAGE == 2) { // 调节分钟
Minutes = constrain(Minutes + Step_Count, 0, 59); // 限制分钟范围在0-59
} else if (STAGE == 3) { // 调节秒
Seconds = constrain(Seconds + Step_Count, 0, 59); // 限制秒钟范围在0-59
}
}
Pre_Encoder_State = Cur_Encoder_State; // 重置之前的引脚状态
}
bool debounce(int Pin, unsigned long Debounce_Delay) {
static bool Last_Reading;
static unsigned long Last_Debounce_Time;
static bool Button_State = HIGH;
bool Reading = digitalRead(Pin);
if (Reading != Last_Reading) {
Last_Debounce_Time = millis();
}
if ((millis() - Last_Debounce_Time) > Debounce_Delay) {
if (Reading != Button_State) {
Button_State = Reading;
}
}
Last_Reading = Reading;
return !Button_State;
}
bool just_HIGH(bool Input_State) {
static bool Just_HIGH_Fg = true;
static bool Before_LOW_Fg;
if (Input_State == HIGH) {
if (Just_HIGH_Fg == true && Before_LOW_Fg == true) {
Just_HIGH_Fg = false;
Before_LOW_Fg = false;
return 1;
}
} else // 如果是低电平
{
Before_LOW_Fg = true;
Just_HIGH_Fg = true; // 打开刚好进入高电平的标志,等待进入高电平
}
return 0;
}
bool just_LOW(bool Input_State) {
static bool Just_LOW_Fg = true;
static bool Before_HIGH_Fg;
if (Input_State == LOW) // 如果是低电平
{
if (Just_LOW_Fg == true && Before_HIGH_Fg == true) {
Just_LOW_Fg = false;
Before_HIGH_Fg = false;
return 1;
}
} else // 如果是高电平
{
Before_HIGH_Fg = true;
Just_LOW_Fg = true;
}
return 0;
}
unsigned long get_Duration(bool Input_State, bool Set_State) {
static unsigned long Duration;
static bool Duration_State;
if (Input_State != Duration_State) {
Duration_State = !Duration_State; // 同步_HIGH_State为HIGH
Duration = millis(); // 开始计时
}
return (Duration_State == Set_State) ? (millis() - Duration) : 0; // 返回对应状态的时间
}
bool keep_HIGH_Until_Time(bool Input_State, unsigned long Target_Time) {
static bool HIGH_Target_Flag;
static bool HIGH_Last_Target_Flag;
unsigned long Current_Time = get_Duration(Input_State, HIGH);
if (Current_Time > Target_Time) {
HIGH_Target_Flag = 1;
} else {
HIGH_Target_Flag = 0;
}
if (HIGH_Target_Flag == 1 && HIGH_Last_Target_Flag == 0) {
HIGH_Last_Target_Flag = HIGH_Target_Flag;
return 1;
}
HIGH_Last_Target_Flag = HIGH_Target_Flag;
return 0;
}