This commit is contained in:
音游玩的人 2023-04-02 13:00:08 +08:00 committed by GitHub
parent 355f2e002b
commit 508d337fec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

464
WouoUI/WouoUI.ino Normal file
View File

@ -0,0 +1,464 @@
/*
MonoUI风格的超丝滑菜单使0.96OLED显示EC11旋转编码器控制
UI示例是从我的另一个项目简化而来https://github.com/RQNG/Rapid-trigger-minipad
v 1.0
*
*
*
*
* 1 - 11
* 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();
}