#include <U8g2lib.h>
#include <Wire.h>
#include <math.h>
#define buttun_1 12 // 按钮1 的引脚号 D6
#define buttun_2 13 // 按钮2 的引脚号 D7
// 0.96寸的OLED显示器的驱动使用硬件I2C
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/U8X8_PIN_NONE, /* clock=*/SCL, /* data=*/SDA);
typedef struct
{
byte val; // 当前读取的电平
byte last_val; // 上一次的电平
byte change; // 标记改变
byte shore_press; // 按下时间
byte long_press_flag; // 长按标志 用来屏蔽松开的时候检测到
} buttonEvent;
typedef struct
{
byte id; // 按键号
byte press; // 是否按下
byte update_flag; // 是否最新
byte long_press; // 是否长按
} KEY_MSG;
short menu_x, menu_x_trg; // 菜单x 和菜单的x目标
short menu_y, menu_y_trg; // 菜单y 和菜单的y目标
short pic_x, pic_x_trg; // 图片页面x 和图片的x目标
short pic_y, pic_y_trg; // 图片页面y 和图片的y目标
short setting_x = 0, setting_x_trg = 0; // 设置界面x 和设置界面x目标
short setting_y = 18, setting_y_trg = 18; // 设置界面y 和设置界面y目标
short frame_len = 0, frame_len_trg = 0; // 框框的宽度
short frame_y = 0, frame_y_trg = 0; // 框框的y
enum
{
// E_NONE, // 运行状态机 无
E_MENU, // 时间界面
E_NZ, // 闹钟界面
E_NZSZ, // 闹钟设置界面
E_UI_MAX, // 界面最大值
} E_UI;
enum
{
E_SELECT_SETTING, // 菜单界面的选择->设置
E_SELECT_PIC, // 菜单界面的选择->图片
};
char ui_select = 0; // 当前UI的选项
char ui_index = 0; // 当前UI在哪个界面
u8 ui_state = 0; // UI状态机
int key_long_press_tick = 0; // 按键长按计数
buttonEvent button[2] = {0}; // 按键对象
KEY_MSG key_msg = {0}; // 按键消息
/*------ 获取按钮按下的状态-----------*/
bool getButtonState(byte bt) // 根据数字返回按钮按下的状态
{
if (bt == 0) // 输入0 返回按钮1的状态
{
return digitalRead(buttun_1);
}
else
{
return digitalRead(buttun_2); // 输入1 返回按钮2的状态
}
}
void ButtonInit(void) // 按键初始化
{
for (int i = 0; i < 2; i++)
{
button[i].val = button[i].last_val = getButtonState(i); // 将按键初始化成上电时候的状态
}
}
void key_scan(void)
{
for (int i = 0; i < 2; i++)
{
button[i].val = getButtonState(i); // 获取当前的按键状态
if (button[i].val != button[i].last_val) // 如果按键发生改变
{
button[i].last_val = button[i].val; // 更新状态
button[i].change = 1; // 记录改变
}
}
}
void key_proc(void)
{
for (int i = 0; i < 2; i++)
{
if (button[i].change == 1) // 如果按键改变了 可以认为有按键按下/松开了
{
button[i].change = 0; // 清除标志
if (button[i].val == 0) // 如果按下
{
key_long_press_tick = 10; // 开始长按倒计时
}
else // 如果松开
{
if (button[i].long_press_flag) // 如果已经有长按标记
{
button[i].long_press_flag = 0; // 清除长按标志
button[i].shore_press = 0; // 清掉按下时间计数,避免以短按操作
}
else
{
button[i].shore_press = 1; // 如果没长按标记 则认为是短按 计数
}
}
}
}
}
void key_down_cb(void) // 此函数是按键松开后有计数 认为按键短按
{
for (int i = 0; i < 2; i++)
{
if (button[i].shore_press) // 如果有按键计数
{
button[i].shore_press--; // 计数减一
if (!button[i].shore_press) // 如果为0
{
key_msg.id = i; // 发送短按msg
key_msg.press = 1;
key_msg.update_flag = 1;
key_msg.long_press = 0;
}
}
}
}
void key_press_cb(void) // 按键回调 1ms调用一次
{
if (key_long_press_tick != 0) // 如果按下
{
key_long_press_tick--; //--
if (key_long_press_tick == 0) // 如果计时时间结束
{
for (int i = 0; i < 2; i++)
{
if (button[i].val == 0) // 遍历按键 如果还有按下的 则证明按键为长按,发送长按消息
{
key_msg.id = i;
key_msg.press = 1;
key_msg.update_flag = 1;
key_msg.long_press = 1;
button[i].long_press_flag = 1;
}
}
}
}
key_down_cb(); //否则执行短按函数
}
void system_tick(void) // 系统1ms时钟
{
static unsigned long tick = 0;
if (tick != millis())
{
tick = millis();
key_scan();
key_press_cb();
}
}
short fade_out_val = 0; // 渐隐位移的起始值0: 0到8
short fade_in_val = 8; // 渐显位移的起始值8: 8到0
/***---------------渐隐效果-----------------***/
u8 fade_out(void)
{
int len = 8 * u8g2.getBufferTileHeight() * u8g2.getBufferTileWidth(); // 获取缓冲区的范围
u8 *p = u8g2.getBufferPtr(); // 获取缓冲区的地址
u8 return_flag = 0; // 返回值标志
for (int i = 0; i < len; i++)
{
p[i] = p[i] & (rand() % 0xff) >> fade_out_val;
}
fade_out_val += 1;
// Serial.println(fade_out_val);
if (fade_out_val > 8)
{
return_flag = 1;
fade_out_val = 0;
}
return return_flag ? 0 : 1;
}
/***===================================================***/
/***---------------渐显效果-----------------***/
u8 fade_in(void)
{
int len = 8 * u8g2.getBufferTileHeight() * u8g2.getBufferTileWidth();
u8 *p = u8g2.getBufferPtr();
u8 return_flag = 0;
for (int i = 0; i < len; i++)
{
p[i] = p[i] & (rand() % 0xff) >> fade_in_val;
}
// Serial.println(fade_in_val);
fade_in_val -= 1;
if (fade_in_val < 0)
{
return_flag = 1;
fade_in_val = 8;
}
return return_flag ? 0 : 1;
}
/***===================================================***/
u8 fade_out_flag = 1; // 渐隐标志
u8 fade_in_flag = 0; // 渐显标志
u8 stop_flag = 0; // 渐变效果停止标志
u8 AB_flag = 0; // A向B渐变的标志
u8 BA_flag = 0; // B向A渐变的标志
// 第一页面A
void A(void)
{
u8g2.drawStr(pic_x + 20, pic_y + 18, "AAA");
u8g2.drawStr(pic_x + 20, pic_y + 38, "BBB");
u8g2.drawStr(pic_x + 20 + 128, pic_y + 18, "nzsz");
u8g2.drawRFrame(frame_len + 19, frame_y + 2, 35, 18, 3); // 在选定位置绘制方框
}
// short frame_len, frame_len_trg; // 框框的宽度
// short frame_y, frame_y_trg; // 框框的y
// 第二页面B
void B(void)
{
u8g2.drawStr(menu_x + 20 + 128, menu_y + 18, "BBB");
}
/***---------------A向B渐变-----------------***/
void show_A_to_B(void)
{
u8g2.clearBuffer(); // 清除内部缓冲区
if (fade_out_flag)
{
A(); // 第1个页面
if (fade_out() == 0)
{
fade_in_flag = 1;
fade_out_flag = 0;
}
}
else if (fade_in_flag)
{
XianShi_TianQi(0);
if (fade_in() == 0)
{
fade_in_flag = 0;
stop_flag = 1;
}
}
if (stop_flag == 1)
{
AB_flag = 0;
XianShi_TianQi(0); // 第1个页面
stop_flag = 0;
}
// u8g2.sendBuffer();
}
/***---------------B向A渐变-----------------***/
void show_B_to_A(void)
{
u8g2.clearBuffer(); // 清除内部缓冲区
if (fade_out_flag)
{
XianShi_TianQi(0); // 第1个页面
if (fade_out() == 0)
{
fade_in_flag = 1;
fade_out_flag = 0;
}
}
else if (fade_in_flag)
{
A();
if (fade_in() == 0)
{
fade_in_flag = 0;
stop_flag = 1;
}
}
if (stop_flag == 1)
{
BA_flag = 0;
A(); // 第1个页面
stop_flag = 0;
}
// u8g2.sendBuffer();
}
/***--------------运行渐变函数-----------------***/
void run_JianBian(void)
{
// Serial.print("AB_flag = ");
// Serial.println(AB_flag);
if (AB_flag)
{
show_A_to_B();
// Serial.print("AB_flag = ");
// Serial.println(AB_flag);
}
if (BA_flag)
{
show_B_to_A();
// Serial.print("BA_flag = ");
// Serial.println(BA_flag);
}
}
// bool YuDong = 0; // 判断是否在移动中
/***------------移动效果(从当前坐标向目标坐标靠近)------***/
int ui_run(short *a, short *a_trg, byte temp) // 两个指针变量分别指向代入坐标的地址,加减坐标的步长
{
if (*a < *a_trg) // 取出两个地址中的数据(坐标值)进行比较,如果当前坐标小于目标坐标
{
temp = (*a_trg - *a) > temp ? temp : 1; // 如果目标坐标-当前坐标小于步长,步长赋值为1,避免出现负数
*a += temp; // 加大当前坐标值,向目标坐标靠近
}
else if (*a > *a_trg)
{
temp = (*a_trg - *a) < temp ? temp : 1;
*a -= temp;
}
else
{
// YuDong = 0;
return 0; // 如果两坐标相等,返回0
}
return 1; // 不相等,返回1
}
/***===================================================***/
void XianShi_TianQi(short offset_y) // 显示天气页面
{
u8g2.setCursor(menu_x + 0, offset_y + 20); // 设置显示坐标
u8g2.print("TIME");
u8g2.setCursor(menu_x + 128, offset_y + 20); // 设置显示坐标
u8g2.print("天气实况界面TQ");
u8g2.setCursor(menu_x + 256, offset_y + 20); // 设置显示坐标
u8g2.print("天气预报界面YB");
}
void menu_proc(KEY_MSG *msg) // 菜单处理函数
{
int list_len = 2; // 菜单的元素个数
u8g2.clearBuffer(); // 清除内部缓冲区
if (msg->update_flag && msg->press) // 如果有按键按下
{
msg->update_flag = 0; // 清楚按键标志
if (msg->long_press == 0) // 如果是短按
{
if (ui_state == E_MENU) // 如果是时间界面
{
if (msg->id) // 判断按键id
{
if (ui_select < list_len)
{
ui_select++; // 增加
}
}
else
{
if (ui_select > 0)
{
ui_select--; // 减少
}
}
menu_x_trg = ui_select * -128; // 修改菜单的x目标值 实现菜单滚动
}
else if (ui_state == E_NZ)
{
if (msg->id) // 判断按键id
{
if (ui_select < list_len)
{
ui_select++; // 增加
}
}
else
{
if (ui_select > 0)
{
ui_select--; // 减少
}
}
frame_y_trg = ui_select * +20; // 修改菜单的x目标值 实现菜单滚动
}
}
else if (msg->long_press == 1)
{
Serial.println("ChangAn---------------------------------------");
if (msg->id == 1) // 判断按键id
{
Serial.print("ui_state = ");
Serial.println(ui_state);
if (ui_state == E_MENU)
{
ui_state = E_NZ;
Serial.print("ui_state+ = ");
Serial.println(ui_state);
if (!AB_flag && !BA_flag)
{
Serial.println("B to A");
BA_flag = 1;
fade_out_flag = 1;
}
}
else if (ui_state == E_NZ)
{
ui_state = E_NZSZ;
Serial.print("E_NZ to NZSZ ");
frame_len_trg = frame_len - 128; // 修改菜单的x目标值 实现菜单滚动
pic_x_trg = pic_x - 128; // 修改菜单的x目标值 实现菜单滚动
}
}
else // 按键2:渐变方向为 A到B
{
Serial.print("ui_state = ");
Serial.println(ui_state);
if (ui_state == E_NZ)
{
ui_state = E_MENU;
Serial.print("ui_state = ");
Serial.println(ui_state);
if (!BA_flag && !AB_flag)
{ // 限制同一时间只能执行1次渐变效果
Serial.println("A to B");
AB_flag = 1;
fade_out_flag = 1;
}
}
else if (ui_state == E_NZSZ)
{
ui_state = E_NZ;
Serial.print("E_NZSZ to NZ ");
frame_len_trg = frame_len + 128; // 修改菜单的x目标值 实现菜单滚动
pic_x_trg = pic_x + 128; // 修改菜单的x目标值 实现菜单滚动
}
}
}
}
switch (ui_state) // 菜单界面UI状态机
{
case E_MENU: // 菜单状态
{
// Serial.println("E_MENU");
ui_run(&menu_x, &menu_x_trg, 8); // 只运算x轴
XianShi_TianQi(0); // 显示
break;
}
case E_NZ: // 菜单状态
{
// Serial.print("E_NZ = ");
// Serial.println(ui_state);
// YuDong = 1;
// run_JianBian();
ui_run(&frame_y, &frame_y_trg, 5); // 只运算x轴
ui_run(&frame_len, &frame_len_trg, 8); // 只运算x轴
ui_run(&pic_x, &pic_x_trg, 8); // 只运算x轴
A();
break;
}
case E_NZSZ: // 菜单状态
{
ui_run(&frame_len, &frame_len_trg, 8); // 只运算x轴
ui_run(&pic_x, &pic_x_trg, 8); // 只运算x轴
A(); // 显示
break;
}
default: // 默认 到位或者none 显示菜单ui
// run_JianBian();
// Serial.println("XianShi");
XianShi_TianQi(menu_y);
break;
}
// Serial.println("JianBian");
// run_JianBian();
// u8g2.sendBuffer(); // 将缓冲区的数据显示到OLED上
run_JianBian();
}
void setup(void)
{
pinMode(buttun_1, INPUT_PULLUP);
pinMode(buttun_2, INPUT_PULLUP);
ButtonInit();
Serial.begin(115200);
u8g2.begin();
u8g2.enableUTF8Print(); // 中文显示需要启用 UTF8
// u8g2.setFont(u8g2_font_wqy13_t_gb2312a); // 设置中文字体
u8g2.setFont(u8g2_font_t0_22_mf); // 设置字体
// frame_len = frame_len_trg = list[ui_select].len * 13; // 初始化方框的长宽
ui_state = E_MENU; // 设置UI默认进入菜单
}
void loop(void)
{
key_proc(); // 按键扫描
system_tick(); // 系统tick
// u8g2.clearBuffer(); // 清除内部缓冲区
menu_proc(&key_msg);
// run_JianBian();
u8g2.sendBuffer(); // 将缓冲区的数据显示到OLED上
// ui_proc(&key_msg);
}