WouoUI/WouoUI/WouoUI.ino
音游玩的人 508d337fec
v 1.0
2023-04-02 13:00:08 +08:00

465 lines
11 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
此程序用于实现稚晖君MonoUI风格的超丝滑菜单使用0.96寸OLED显示EC11旋转编码器控制。
UI示例是从我的另一个项目简化而来只保留了列表部分简化了添加和修改列表的操作如需更多功能请参考原项目https://github.com/RQNG/Rapid-trigger-minipad
当前版本 v 1.0
示例实现了以下功能:
* 休眠模式扫描,同时可旋转旋钮
* 休眠模式下点按旋钮停止扫描,进入主菜单
* 主菜单下,旋转旋钮滚动列表,点按选择列表
* 列表长度无限制
* 动画速度可调1 - 无穷大数字越小动画速度越快1没有动画不支持小于1的数值
推荐把返回选项放在开头,个人认为体验比较好。
项目参考:
* 旋钮功能https://zhuanlan.zhihu.com/p/453130384
* UIhttps://www.bilibili.com/video/BV1HA411S7pv/ ; https://www.bilibili.com/video/BV1xd4y1C7BE/
本项目使用Apache 2.0开源协议,如需商用或借鉴,请阅读此协议。
欢迎关注我的B站账号一个只分享osu!相关内容,很无聊的帐号。
用户名音游玩的人B站主页https://space.bilibili.com/9182439?spm_id_from=..0.0
*/
/************************************* 旋钮相关 *************************************/
#define AIO PB12
#define BIO PB13
#define SW PB14
#define DEBOUNCE 50
uint8_t btn_id = 0;
uint8_t btn_flag = 0;
bool btn_val = false;
bool btn_val_last = false;
bool btn_pressed = false;
bool CW_1 = false;
bool CW_2 = false;
void btn_inter()
{
bool alv = digitalRead(AIO);
bool blv = digitalRead(BIO);
if (btn_flag == 0 && alv == LOW)
{
CW_1 = blv;
btn_flag = 1;
}
if (btn_flag && alv)
{
CW_2 = !blv;
if (CW_1 && CW_2)
{
btn_id = 0;
btn_pressed = true;
}
if (CW_1 == false && CW_2 == false)
{
btn_id = 1;
btn_pressed = true;
}
btn_flag = 0;
}
}
void btn_init()
{
pinMode(AIO, INPUT);
pinMode(BIO, INPUT);
pinMode(SW, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(AIO), btn_inter, CHANGE);
}
void btn_scan()
{
btn_val = digitalRead(SW);
if (btn_val != btn_val_last)
{
delay(DEBOUNCE);
btn_val_last = btn_val;
if (btn_val == LOW)
{
btn_pressed = true;
btn_id = 2;
}
}
}
/************************************* 屏幕和UI *************************************/
#include <U8g2lib.h>
#include <Wire.h>
#define SCL PB6
#define SDA PB7
#define RST U8X8_PIN_NONE
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, SCL, SDA, RST);
//状态
enum
{
S_NONE,
S_DISAPPEAR,
};
//菜单结构体
typedef struct MENU
{
char *select;
} SELECT_LIST;
//UI变量
uint8_t ui_index; //目录变量
uint8_t ui_state; //状态变量
uint8_t *buf_ptr; //指向buf首地址的指针
uint16_t buf_len; //buf的长度
bool sleep_flag = true; //休眠标志
uint8_t disappear_step = 1; //消失步数
/********************************** 需要修改的东西 ***********************************/
//UI参数
#define SPEED 6 //动画速度越小越快1没有动画
#define PAGES 4 //页面数量,列表类页面的总数量
#define ui_select 0
#define x 1
#define y 2
#define y_trg 3
#define box_width 4
#define box_width_trg 5
#define box_y 6
#define box_y_trg 7
#define num 8
#define line_y 9
#define line_y_trg 10
int16_t ui_param[11][PAGES];
//目录
enum
{
M_SLEEP,
M_MAIN,
M_NUMB,
M_ALPH,
};
//主菜单内容
SELECT_LIST m_main[]
{
{"<< Sleep"},
{"- Number"},
{"- 1"},
{"- 11"},
{"- 111"},
{"- 1111"},
{"- 11111"},
{"- 111111"},
{"- 1111111"},
{"- 11111111"},
{"- 111111111"},
{"- 1111111111"},
{"- 1"},
{"- 1111111111"},
{"- 1"},
{"- 1111111111"},
{"- 1"},
{"- 1111111111"},
{"- 1"},
{"- 1111111111"},
{"- 1"},
{"- 1111111111"},
{"- 1"},
{"- 1111111111"},
{"- 1"},
};
//数字菜单内容
SELECT_LIST m_numb[]
{
{"<< Main"},
{"- 0"},
{"- 1"},
{"- 2"},
{"- 3"},
{"- 4"},
{"- 5"},
{"- 6"},
{"- 7"},
{"- 8"},
{"- 9"},
};
//睡眠页面处理函数
void sleep_proc()
{
//在这里执行功能
while (sleep_flag)
{
//需要扫描的功能
Serial.println("Scan");
//旋钮扫描
btn_scan();
if (btn_pressed)
{
btn_pressed = false;
switch (btn_id)
{
case 0:
//睡眠时顺时针旋转功能
Serial.println("Clockwise");
break;
case 1:
//睡眠时逆时针旋转功能
Serial.println("Anticlockwise");
break;
case 2:
ui_index = M_MAIN;
ui_state = S_NONE;
sleep_flag = false;
u8g2.setPowerSave(0);
break;
default: break;
}
}
}
}
//主菜单处理函数
void main_proc()
{
if (btn_pressed)
{
btn_pressed = false;
switch (btn_id)
{
case 0:
case 1:
rotate_switch(M_MAIN);
break;
case 2:
switch (ui_param[ui_select][M_MAIN])
{
case 0:
ui_index = M_SLEEP;
ui_state = S_NONE;
u8g2.setPowerSave(1);
sleep_flag = true;
break;
case 1:
ui_index = M_NUMB;
ui_state = S_DISAPPEAR;
break;
default: break;
}
default: break;
}
ui_param[box_width_trg][M_MAIN] = u8g2.getStrWidth(m_main[ui_param[ui_select][M_MAIN]].select) + ui_param[x][M_MAIN] * 2;
}
menu_ui_show(m_main, M_MAIN);
}
//数字菜单处理函数
void numb_proc()
{
if (btn_pressed)
{
btn_pressed = false;
switch (btn_id)
{
case 0:
case 1:
rotate_switch(M_NUMB);
break;
case 2:
switch (ui_param[ui_select][M_NUMB])
{
case 0:
ui_index = M_MAIN;
ui_state = S_DISAPPEAR;
break;
default: break;
}
default: break;
}
ui_param[box_width_trg][M_NUMB] = u8g2.getStrWidth(m_numb[ui_param[ui_select][M_NUMB]].select) + ui_param[x][M_NUMB] * 2;
}
menu_ui_show(m_numb, M_NUMB);
}
//总的UI进程
void ui_proc()
{
switch (ui_state)
{
case S_NONE:
u8g2.clearBuffer();
switch (ui_index)
{
//目录里所有页面都要放在这里
case M_SLEEP:
sleep_proc();
break;
case M_MAIN:
main_proc();
break;
case M_NUMB:
numb_proc();
break;
default: break;
}
break;
case S_DISAPPEAR:
disappear();
break;
default: break;
}
u8g2.sendBuffer();
}
//ui初始化
void ui_init()
{
for(int i = 0 ; i < PAGES ; i++)
{
ui_param[ui_select][i] = 0;
ui_param[x][i] = 4;
ui_param[y][i] = 0;
ui_param[y_trg][i] = 0;
}
ui_index = M_SLEEP;
ui_state = S_NONE;
//主菜单
ui_param[box_width][M_MAIN] = ui_param[box_width_trg][M_MAIN] = u8g2.getStrWidth(m_main[ui_param[ui_select][M_MAIN]].select) + ui_param[x][M_MAIN] * 2;
ui_param[num][M_MAIN] = sizeof(m_main) / sizeof(SELECT_LIST);
//数字菜单
ui_param[box_width][M_NUMB] = ui_param[box_width_trg][M_NUMB] = u8g2.getStrWidth(m_numb[ui_param[ui_select][M_NUMB]].select) + ui_param[x][M_NUMB] * 2;
ui_param[num][M_NUMB] = sizeof(m_numb) / sizeof(SELECT_LIST);
}
/************************************* 动画函数 *************************************/
//移动函数
void move(int16_t *a, int16_t *a_trg)
{
if (*a < *a_trg) *a += ceil(fabs((float)(*a_trg - *a) / SPEED));
else if (*a > *a_trg) *a -= ceil(fabs((float)(*a_trg - *a) / SPEED));
}
//消失函数
void disappear()
{
switch (disappear_step)
{
case 1: for (uint16_t i = 0; i < buf_len; ++i) if (i % 2 == 0) buf_ptr[i] = buf_ptr[i] & 0x55; break;
case 2: for (uint16_t i = 0; i < buf_len; ++i) if (i % 2 != 0) buf_ptr[i] = buf_ptr[i] & 0xAA; break;
case 3: for (uint16_t i = 0; i < buf_len; ++i) if (i % 2 == 0) buf_ptr[i] = buf_ptr[i] & 0x00; break;
case 4: for (uint16_t i = 0; i < buf_len; ++i) if (i % 2 != 0) buf_ptr[i] = buf_ptr[i] & 0x00; break;
default: ui_state = S_NONE; disappear_step = 0; break;
}
disappear_step++;
}
/************************************* 显示函数 *************************************/
//列表类页面通用显示函数
void menu_ui_show(struct MENU arr[], int16_t n)
{
for(uint8_t i = 0 ; i < ui_param[num][n] ; ++i) u8g2.drawStr(ui_param[x][n], 16 * i + ui_param[y][n] + 12, arr[i].select);
ui_param[line_y_trg][n] = ceil((ui_param[ui_select][n]) * ((float)64 / (ui_param[num][n] - 1)));
ui_param[box_width_trg][n] = u8g2.getStrWidth(arr[ui_param[ui_select][n]].select) + 8;
move(&ui_param[y][n], &ui_param[y_trg][n]);
move(&ui_param[box_y][n], &ui_param[box_y_trg][n]);
move(&ui_param[box_width][n], &ui_param[box_width_trg][n]);
move(&ui_param[line_y][n], &ui_param[line_y_trg][n]);
u8g2.drawBox(125, 0, 3, ui_param[line_y][n]);
u8g2.setDrawColor(2);
u8g2.drawRBox(0, ui_param[box_y][n], ui_param[box_width][n], 16, 1);
u8g2.setDrawColor(1);
}
/************************************* 处理函数 *************************************/
//列表类页面旋转时判断通用函数
void rotate_switch(int16_t n)
{
switch (btn_id)
{
case 0:
if (ui_param[ui_select][n] < 1) break;
ui_param[ui_select][n] -= 1;
if (ui_param[ui_select][n] < -(ui_param[y][n] / 16)) ui_param[y_trg][n] += 16;
else ui_param[box_y_trg][n] -= 16;
break;
case 1:
if ((ui_param[ui_select][n] + 2) > ui_param[num][n]) break;
ui_param[ui_select][n] += 1;
if ((ui_param[ui_select][n] + 1) > (4 - ui_param[y][n] / 16)) ui_param[y_trg][n] -= 16;
else ui_param[box_y_trg][n] += 16;
break;
default: break;
}
}
/************************************ 初始化函数 ************************************/
//OLED初始化函数
void oled_init()
{
u8g2.setBusClock(800000);
u8g2.begin();
u8g2.setFont(u8g2_font_wqy12_t_chinese1);
buf_ptr = u8g2.getBufferPtr();
buf_len = 8 * u8g2.getBufferTileHeight() * u8g2.getBufferTileWidth();
}
/************************************ 主循环函数 ************************************/
void setup()
{
oled_init();
ui_init();
btn_init();
Serial.begin(115200);
}
void loop()
{
btn_scan();
ui_proc();
}