diff --git a/WouoUI/WouoUI.ino b/WouoUI/WouoUI.ino new file mode 100644 index 0000000..4d3e72a --- /dev/null +++ b/WouoUI/WouoUI.ino @@ -0,0 +1,1894 @@ +/* + 此程序用于实现 wooting 键盘中的 Rapid trigger 功能。 + + 主功能移植并优化自 Github 上的一个开源项目,添加了按键的独立参数和两端的死区,优化了初始化方式,增加控制器,模仿稚晖君 MonoUI 的超丝滑菜单。 + + 当前版本:v2.1 + + * 重构代码,只保存路径和选择框所在位置,分页面类型定义变量。 + * 增加图标和列表两种主界面风格。 + * 支持本地查看所有配置,在关于本机页面。 + * 支持128*64,128*32两种主流OLED屏幕分辨率,只需要替换驱动即可。 + * 增加列表展开动画效果。 + * 增加选择框展开和跳转动画效果。 + * 增强动画速度的可调精度。 + * 增加循环模式。 + * 增加旋钮方向反转选项。 + + 控制器实现了以下功能: + + * 丝滑菜单,模仿UltraLink的MonoUI,实现主界面动画,列表动画,聚焦动画等,可分别调节动画速度。 + * 旋钮功能,在休眠界面旋转可调节音量或亮度,点按后进入主菜单,在菜单界面旋转滚动菜单,点按选择菜单。 + * 三种模式,支持不同极性的磁轴和传感器的组合,还有禁用模式。 + * 参数设置,支持设置几乎所有参数,包括:触发值,释放值,上死区,下死区,键值,模式,初始化时放大抖动的倍数,每个按键都可以独立设置,也可以一起设置。 + * 断电保存,回到休眠模式即保存当前设置,断电后不会丢失。 + * 测试引脚,支持查看每一个引脚的电压变化情况,用于测试传感器和引脚是否正常。 + + 测试用开发板为STM32 F103C8T6,以下是使用说明: + + 硬件: + * EC11:A -> PB13 ; B -> PB12 ; S -> PB14 ; 电源3.3v + * OLED:四线,0.96 SSD1306 128*64,SDA -> PB7 ; SCL -> PB6 ; 电源3.3v + * 板子上的跳线帽设置:BOOT0 -> 1 ; BOOT1 -> 0 + * 串口模块与板子连线:GND -> GND ; 3V3 -> 3.3V ; RX -> PA9 ; TX -> PA10,其余引脚空着。 + * 串口与电脑连接,用数据线连接板子上的母口和电脑。 + * 点击IDE左上角向右箭头的按钮,等待编译和上传完成。 + * 上传完成后如要测试手感,无需断开串口模块,但需要拔插数据线,电脑才能识别到是USB设备。 + * 当BOOT在第一条所述设置时,上传完成后,第一次拔插数据线时电脑会认到USB设备,从第二次开始就认不到了。 + * 手感调试合适后修改跳线帽的设置为:BOOT0 -> 0 ; BOOT1 -> 0。设置后就能收起串口模块了。 + + 软件: + + * 文件 -> 首选项 -> 其他开发板管理器地址 -> 添加一行:http://dan.drown.org/stm32duino/package_STM32duino_index.json + * 工具 -> 开发板 -> 开发板管理器 -> 搜索stm32F1,下载STM32F1xx开头的库 + * 工具 -> 开发板 -> stm32F1... -> Generic STM32F103C series + * 工具 -> 端口:“COMX” -> COMX(X为设备对应端口号) + * 工具 -> Upload method -> Serial + * 进入目录C:\Users{username}\AppData\Local\Arduino15\packages\stm32duino\hardware\STM32F1{版本名称}\libraries\USBComposite下,打开usb_hid.c文件,搜索bInterval,将该值修改为0x01,即轮询间隔修改为1ms + + 注意: + + * 上传程序前,先确认使用的轴的总键程是否为4mm,如果不是,需要修改 key 数组变量中代表 travel 一行的相关参数,单位0.1mm,即4mm的值为40。 + * 拔插USB后,正常情况下,屏幕会变黑,板子上的蓝灯会点亮一秒钟,点亮时不要动按键,这是在初始化。 + * 初始化后,每个按键都需要按下一次后才能正常使用,按下时要按到底。 + + 遇到困难的话参考以下资料: + + 设置参考: + + * https://github.com/rogerclarkmelbourne/Arduino_STM32 + * https://baijiahao.baidu.com/s?id=1727012582049990775&wfr=spider&for=pc + * https://blog.csdn.net/oChiTu1/article/details/108166233?depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-2-108166233-blog-119909968.pc_relevant_3mothn_strategy_and_data_recovery + + 项目参考: + + * Rapid trigger功能:https://github.com/chent7/hall-2k-keypad-handmade + * STM32触盘:https://mp.weixin.qq.com/s/o-8_3SQS2AGT_WTyTLYQnA + * UI:https://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 +*/ + +/************************************* 屏幕和UI *************************************/ + +#include +#include + +#define SCL PB6 +#define SDA PB7 +#define RST U8X8_PIN_NONE + +U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, SCL, SDA, RST); // 分辨率:128*64 驱动:SSD1306 接口:IIC(硬件) +//U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2(U8G2_R0, SCL, SDA, RST); // 分辨率:128*32 驱动:SSD1306 接口:IIC(硬件) + +/************************************* USB 相关 *************************************/ + +#include + +USBHID HID; + +const uint8_t reportDescription[] = +{ + HID_CONSUMER_REPORT_DESCRIPTOR(), + HID_KEYBOARD_REPORT_DESCRIPTOR() +}; + +HIDConsumer Consumer(HID); +HIDKeyboard Keyboard(HID); + +void hid_init() +{ + HID.begin(reportDescription, sizeof(reportDescription)); + while (!USBComposite); +} + +/************************************* 按键相关 *************************************/ + +// 按键参数 +#define KEYS 10 +#define LIGHT PC13 +#define TRIGGER 0 +#define RELEASE 1 +#define TOP 2 +#define BOTTOM 3 +#define TIMES 4 +#define CODES 5 +#define MODES 6 +#define PINS 7 +#define TRAVEL 8 + +// 按键变量 +uint8_t key [9][KEYS + 1] = +{ + // key0 key1 key2 key3 key4 key5 key6 key7 key8 key9 all + { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }, // trigger + { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }, // release + { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }, // top + { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }, // bottom + { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 }, // times + { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k' }, // codes + { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, // modes + { PA0, PA1, PA2, PA3, PA4, PA5, PA6, PA7, PB0, PB1, PB1 }, // pins + { 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40 }, // travel +}; + +/************************************* 按键扫描 *************************************/ + +// 按键参数结构体 +struct +{ + int min; + int max; + int trigger; + int release; + int value; + int reference; + int unit; + int cushion; + bool init; + bool pressed; +} keyx[KEYS]; + +/************************************* 不分模式 *************************************/ + +// 初始化 +void key_init() +{ + // 打开初始化指示灯 + pinMode(LIGHT, OUTPUT); + digitalWrite(LIGHT, LOW); + + // 读模拟量,最大值和最小值先暂定为当前值 + for (uint8_t n = 0; n < KEYS; ++n) + { + pinMode(key[PINS][n],INPUT_ANALOG); + keyx[n].value = analogRead(key[PINS][n]); + keyx[n].min = keyx[n].value; + keyx[n].max = keyx[n].value; + } + + // 并行扫描所有传感器,记录每个传感器的初始抖动范围,每1ms扫描一次,扫描1s + for (int i = 0; i < 1000; ++i) + { + for (uint8_t n = 0; n < KEYS; ++n) + { + keyx[n].value = analogRead(key[PINS][n]); + if (keyx[n].value < keyx[n].min) + { + keyx[n].min = keyx[n].value; + } + else if (keyx[n].value > keyx[n].max) + { + keyx[n].max = keyx[n].value; + } + } + delay(1); + } + + for (uint8_t n = 0; n < KEYS; ++n) + { + keyx[n].cushion = (keyx[n].max - keyx[n].min) * key[TIMES][n]; // 计算缓冲值 + + // 初始化一些参数 + keyx[n].init = false; // 按键还没有被首次按下 + keyx[n].reference = keyx[n].value; // 初始化参考值为当前值 + keyx[n].trigger = key[TRIGGER][n] * keyx[n].cushion; // 初始化触发值,避免插上的瞬间有输入 + keyx[n].release = key[RELEASE][n] * keyx[n].cushion; // 初始化释放值,避免插上的瞬间有输入 + + if (key[MODES][n] == 1) + { + keyx[n].min = keyx[n].value; // 初始化最小值,暂定为当前值 + keyx[n].max = keyx[n].value + keyx[n].cushion; // 初始化最大值,暂定为当前值 + 缓冲值 + } + else if (key[MODES][n] == 2) + { + keyx[n].min = keyx[n].value - keyx[n].cushion; // 初始化最小值,暂定为当前值 + keyx[n].max = keyx[n].value; // 初始化最大值,暂定为当前值 + 缓冲值 + } + } + + // 关闭初始化指示灯 + digitalWrite(LIGHT, HIGH); +} + +// 转换距离,将以毫米为单位的值转换成模拟量,假设磁铁外磁场强度均匀变化,则距离与模拟量线性相关 +void convertDistance(uint8_t n) +{ + keyx[n].unit = (keyx[n].max - keyx[n].min) / key[TRAVEL][n]; + keyx[n].trigger = keyx[n].unit * key[TRIGGER][n]; + keyx[n].release = keyx[n].unit * key[RELEASE][n]; +} + +/************************************* 区分模式 *************************************/ + +/************************************* 模式 1 *************************************/ + +// 按下按键,发送后改变按键状态,按键首次被按下 +void pressKey_1(uint8_t n) +{ + if (keyx[n].init) + { + Keyboard.press(key[CODES][n]); + } + else if (keyx[n].value > keyx[n].min + keyx[n].cushion) + { + keyx[n].init = true; + } + keyx[n].pressed = true; +} + +// 释放按键,发送后改变按键状态,按键首次被按下 +void releaseKey_1(uint8_t n) +{ + if (keyx[n].init) + { + Keyboard.release(key[CODES][n]); + } + else if (keyx[n].value > keyx[n].min + keyx[n].cushion) + { + keyx[n].init = true; + } + keyx[n].pressed = false; +} + +// 平衡按键,按下时探索更大的值,释放时探索更小的值,以免在使用时极值漂移使抖动能误触发 +void balanceKey_1(uint8_t n) +{ + if (!keyx[n].pressed && (keyx[n].value < keyx[n].min)) + { + keyx[n].min = keyx[n].value; + convertDistance(n); + } + else if (keyx[n].pressed && (keyx[n].value > keyx[n].max)) + { + keyx[n].max = keyx[n].value; + convertDistance(n); + } +} + +// 按键移动时判断状态,同时动态改变参考值 +void processKey_1(uint8_t n) +{ + // 不在死区移动时的判断 + if ((keyx[n].value > keyx[n].min + keyx[n].unit * key[TOP][n]) && (keyx[n].value < keyx[n].max - keyx[n].unit * key[BOTTOM][n])) + { + if (!keyx[n].pressed) + { + if (keyx[n].value > keyx[n].reference + keyx[n].trigger) + { + pressKey_1(n); + } + else if (keyx[n].value < keyx[n].reference) + { + keyx[n].reference = keyx[n].value; + } + } + else + { + if (keyx[n].value <= keyx[n].reference - keyx[n].release) + { + releaseKey_1(n); + } + else if (keyx[n].value > keyx[n].reference) + { + keyx[n].reference = keyx[n].value; + } + } + } + + // 在死区移动时的判断 + else if ((keyx[n].value < keyx[n].min + keyx[n].unit * key[TOP][n]) && keyx[n].pressed) + { + releaseKey_1(n); + } + else if ((keyx[n].value > keyx[n].max - keyx[n].unit * key[BOTTOM][n]) && !keyx[n].pressed) + { + pressKey_1(n); + } +} + +/************************************* 模式 2 *************************************/ + +// 按下按键,发送后改变按键状态,按键首次被按下 +void pressKey_2(uint8_t n) +{ + if (keyx[n].init) + { + Keyboard.press(key[CODES][n]); + } + else if (keyx[n].value < keyx[n].max - keyx[n].cushion) + { + keyx[n].init = true; + } + keyx[n].pressed = true; +} + +// 释放按键,发送后改变按键状态,按键首次被按下 +void releaseKey_2(uint8_t n) +{ + if (keyx[n].init) + { + Keyboard.release(key[CODES][n]); + } + else if (keyx[n].value < keyx[n].min - keyx[n].cushion) + { + keyx[n].init = true; + } + keyx[n].pressed = false; +} + +// 平衡按键,按下时探索更小的值,释放时探索更大的值,以免在使用时极值漂移使抖动能误触发 +void balanceKey_2(uint8_t n) +{ + if (keyx[n].pressed && (keyx[n].value < keyx[n].min)) + { + keyx[n].min = keyx[n].value; + convertDistance(n); + } + else if (!keyx[n].pressed && (keyx[n].value > keyx[n].max)) + { + keyx[n].max = keyx[n].value; + convertDistance(n); + } +} + +// 按键移动时判断状态,同时动态改变参考值 +void processKey_2(uint8_t n) +{ + // 不在死区移动时的判断 + if ((keyx[n].value > keyx[n].min + keyx[n].unit * key[BOTTOM][n]) && (keyx[n].value < keyx[n].max - keyx[n].unit * key[TOP][n])) + { + if (!keyx[n].pressed) + { + if (keyx[n].value < keyx[n].reference - keyx[n].trigger) + { + pressKey_2(n); + } + else if (keyx[n].value > keyx[n].reference) + { + keyx[n].reference = keyx[n].value; + } + } + else + { + if (keyx[n].value >= keyx[n].reference + keyx[n].release) + { + releaseKey_2(n); + } + else if (keyx[n].value < keyx[n].reference) + { + keyx[n].reference = keyx[n].value; + } + } + } + + // 在死区移动时的判断 + else if ((keyx[n].value < keyx[n].min + keyx[n].unit * key[BOTTOM][n]) && !keyx[n].pressed) + { + pressKey_2(n); + } + else if ((keyx[n].value > keyx[n].max - keyx[n].unit * key[TOP][n]) && keyx[n].pressed) + { + releaseKey_2(n); + } +} + +// 扫描一次按键 +void runKey(uint8_t n) +{ + if (key[MODES][n] == 1) + { + keyx[n].value = analogRead(key[PINS][n]); + balanceKey_1(n); + processKey_1(n); + } + else if (key[MODES][n] == 2) + { + keyx[n].value = analogRead(key[PINS][n]); + balanceKey_2(n); + processKey_2(n); + } +} + +/************************************* 定义页面 *************************************/ + +//总目录 +enum +{ + M_SLEEP, + M_MAIN, + M_EDITOR, + M_EDIT, + M_EDITING, + M_CODE, + M_ALPH, + M_NUMB, + M_FUNC, + M_MORE, + M_MODE, + M_MEASURE, + M_VOLTAGE, + M_SETTINGS, + M_CONTROLLER, + M_KNOB, + M_ABOUT, +}; + +//状态 +enum +{ + S_NONE, + S_DISAPPEAR, +}; + +//菜单结构体 +typedef struct MENU +{ + char *m_select; +} M_SELECT; + +/************************************* 定义内容 *************************************/ + +/************************************* 文字内容 *************************************/ + +M_SELECT main_menu[] +{ + {"Sleep"}, + {"Editor"}, + {"Measure"}, + {"Settings"}, +}; + +M_SELECT editor_menu[] +{ + {"<< Main Menu"}, + {"- Key 0"}, + {"- Key 1"}, + {"- Key 2"}, + {"- Key 3"}, + {"- Key 4"}, + {"- Key 5"}, + {"- Key 6"}, + {"- Key 7"}, + {"- Key 8"}, + {"- Key 9"}, + {"- All"}, +}; + +M_SELECT edit_menu[] +{ + {"<< Select Keys"}, + {"- Trigger"}, + {"- Release"}, + {"- Top"}, + {"- Bottom"}, + {"- Times"}, + {"- Codes"}, + {"- Modes"}, +}; + +M_SELECT code_menu[] +{ + {"<< Editor Menu"}, + {"- Alphabet"}, + {"- Number"}, + {"- Function"}, + {"- More"}, +}; + +M_SELECT alph_menu[] +{ + {"<< Code Category"}, + {"- a"}, + {"- b"}, + {"- c"}, + {"- d"}, + {"- e"}, + {"- f"}, + {"- g"}, + {"- h"}, + {"- i"}, + {"- j"}, + {"- k"}, + {"- l"}, + {"- m"}, + {"- n"}, + {"- o"}, + {"- p"}, + {"- q"}, + {"- r"}, + {"- s"}, + {"- t"}, + {"- u"}, + {"- v"}, + {"- w"}, + {"- x"}, + {"- y"}, + {"- z"}, +}; + +M_SELECT numb_menu[] +{ + {"<< Code Category"}, + {"- 0"}, + {"- 1"}, + {"- 2"}, + {"- 3"}, + {"- 4"}, + {"- 5"}, + {"- 6"}, + {"- 7"}, + {"- 8"}, + {"- 9"}, +}; + +M_SELECT func_menu[] +{ + {"<< Code Category"}, + {"- F1"}, + {"- F2"}, + {"- F3"}, + {"- F4"}, + {"- F5"}, + {"- F6"}, + {"- F7"}, + {"- F8"}, + {"- F9"}, + {"- F10"}, + {"- F11"}, + {"- F12"}, +}; + +M_SELECT more_menu[] +{ + {"<< Code Category"}, + {"- LEFT CTRL"}, + {"- RIGHT CTRL"}, + {"- LEFT SHIFT"}, + {"- RIGHT SHIFT"}, + {"- LEFT ALT"}, + {"- RIGHT ALT"}, + {"- LEFT GUI"}, + {"- RIGHT GUI"}, + {"- UP ARROW"}, + {"- DOWN ARROW"}, + {"- LEFT ARROW"}, + {"- RIGHT ARROW"}, + {"- BACKSPACE"}, + {"- TAB"}, + {"- RETURN"}, + {"- ESC"}, + {"- INSERT"}, + {"- DELETE"}, + {"- PAGE UP"}, + {"- PAGE DOWN"}, + {"- HOME"}, + {"- END"}, + {"- CAPS LOCK"}, +}; + +M_SELECT mode_menu[] +{ + {"<< Editor Menu"}, + {"- Mode 1"}, + {"- Mode 2"}, + {"- Disable"}, +}; + +M_SELECT measure_menu[] +{ + {"<< Main Menu"}, + {"- Key 0 : PA 0"}, + {"- Key 1 : PA 1"}, + {"- Key 2 : PA 2"}, + {"- Key 3 : PA 3"}, + {"- Key 4 : PA 4"}, + {"- Key 5 : PA 5"}, + {"- Key 6 : PA 6"}, + {"- Key 7 : PA 7"}, + {"- Key 8 : PB 0"}, + {"- Key 9 : PB 1"}, +}; + +M_SELECT settings_menu[] +{ + {"<< Main Menu"}, + {"- Knob Function"}, + {"- OLED Brightness"}, + {"- Main Ans Speed"}, + {"- List Ans Speed"}, + {"- Volt Ans Speed"}, + {"- Fade Ans Speed"}, + {"- Loop Mode"}, + {"- Switch Style"}, + {"- Switch Knob"}, + {"- [ About ]"}, +}; + +M_SELECT knob_menu[] +{ + {"<< Settings Menu"}, + {"- Volume"}, + {"- Brightness"}, + {"- Disable"}, +}; + +/************************************* 图片内容 *************************************/ + +//主菜单图片 +PROGMEM const uint8_t main_icon_pic[][120] +{ + { + 0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xF1,0x3F, + 0xFF,0xFF,0xC3,0x3F,0xFF,0xFF,0x87,0x3F,0xFF,0xFF,0x07,0x3F,0xFF,0xFF,0x0F,0x3E, + 0xFF,0xFF,0x0F,0x3E,0xFF,0xFF,0x0F,0x3C,0xFF,0xFF,0x0F,0x3C,0xFF,0xFF,0x0F,0x38, + 0xFF,0xFF,0x0F,0x38,0xFF,0xFF,0x0F,0x38,0xFF,0xFF,0x07,0x38,0xFF,0xFF,0x07,0x38, + 0xFF,0xFF,0x03,0x38,0xF7,0xFF,0x01,0x38,0xE7,0xFF,0x00,0x3C,0x87,0x3F,0x00,0x3C, + 0x0F,0x00,0x00,0x3E,0x0F,0x00,0x00,0x3E,0x1F,0x00,0x00,0x3F,0x3F,0x00,0x80,0x3F, + 0x7F,0x00,0xC0,0x3F,0xFF,0x01,0xF0,0x3F,0xFF,0x07,0xFC,0x3F,0xFF,0xFF,0xFF,0x3F, + 0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xFF,0x3F + + }, + { + 0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xFF,0x3F,0xFF,0xF9,0xE7,0x3F, + 0xFF,0xF9,0xE7,0x3F,0xFF,0xF9,0xE7,0x3F,0xFF,0xF0,0xE7,0x3F,0x7F,0xE0,0xE7,0x3F, + 0x7F,0xE0,0xC3,0x3F,0x7F,0xE0,0xC3,0x3F,0x7F,0xE0,0xC3,0x3F,0x7F,0xE0,0xE7,0x3F, + 0xFF,0xF0,0xE7,0x3F,0xFF,0xF9,0xE7,0x3F,0xFF,0xF9,0xE7,0x3F,0xFF,0xF9,0xE7,0x3F, + 0xFF,0xF9,0xE7,0x3F,0xFF,0xF9,0xC3,0x3F,0xFF,0xF9,0x81,0x3F,0xFF,0xF0,0x81,0x3F, + 0xFF,0xF0,0x81,0x3F,0xFF,0xF0,0x81,0x3F,0xFF,0xF9,0x81,0x3F,0xFF,0xF9,0xC3,0x3F, + 0xFF,0xF9,0xE7,0x3F,0xFF,0xF9,0xE7,0x3F,0xFF,0xF9,0xE7,0x3F,0xFF,0xFF,0xFF,0x3F, + 0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xFF,0x3F + }, + { + 0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xDF,0x3F, + 0xFF,0xFF,0x8F,0x3F,0xFF,0xFF,0x07,0x3F,0xFF,0xFF,0x03,0x3E,0xFF,0xFF,0x01,0x3C, + 0xFF,0xFF,0x00,0x38,0xFF,0x7F,0x40,0x3C,0xFF,0x3F,0x80,0x3E,0xFF,0x1F,0x00,0x3F, + 0xFF,0x0F,0x88,0x3F,0xFF,0x07,0xD0,0x3F,0xFF,0x03,0xE0,0x3F,0xFF,0x01,0xF1,0x3F, + 0xFF,0x00,0xFA,0x3F,0x7F,0x00,0xFC,0x3F,0x3F,0x20,0xFE,0x3F,0x1F,0x40,0xFF,0x3F, + 0x0F,0x80,0xFF,0x3F,0x07,0xC4,0xFF,0x3F,0x0F,0xE8,0xFF,0x3F,0x1F,0xF0,0xFF,0x3F, + 0x3F,0xF8,0xFF,0x3F,0x7F,0xFC,0xFF,0x3F,0xFF,0xFE,0xFF,0x3F,0xFF,0xFF,0xFF,0x3F, + 0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xFF,0x3F + }, + { + 0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xFF,0x3F, + 0xFF,0x1F,0xFE,0x3F,0xFF,0x1F,0xFE,0x3F,0xFF,0x0C,0xCC,0x3F,0x7F,0x00,0x80,0x3F, + 0x3F,0x00,0x00,0x3F,0x3F,0xE0,0x01,0x3F,0x7F,0xF8,0x87,0x3F,0x7F,0xFC,0x8F,0x3F, + 0x3F,0xFC,0x0F,0x3F,0x0F,0x3E,0x1F,0x3C,0x0F,0x1E,0x1E,0x3C,0x0F,0x1E,0x1E,0x3C, + 0x0F,0x3E,0x1F,0x3C,0x3F,0xFC,0x0F,0x3F,0x7F,0xFC,0x8F,0x3F,0x7F,0xF8,0x87,0x3F, + 0x3F,0xE0,0x01,0x3F,0x3F,0x00,0x00,0x3F,0x7F,0x00,0x80,0x3F,0xFF,0x0C,0xCC,0x3F, + 0xFF,0x1F,0xFE,0x3F,0xFF,0x1F,0xFE,0x3F,0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xFF,0x3F, + 0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xFF,0x3F + }, +}; + +/************************************* 页面变量 *************************************/ + +//OLED变量 +uint8_t *buf_ptr; +uint16_t buf_len; +uint8_t display_width; +uint8_t display_height; + +//控制器变量 +struct +{ + uint8_t select = 0; + uint8_t value[9] = { 100, 25, 25, 25, 15, 1, 0, 0, 0 }; //OLED亮度,主页面动画速度,列表动画速度,电压页面动画速度,消失动画速度,旋钮功能,循环列表,主菜单风格,旋钮方向 +} controller; + +//UI变量 +#define UI_DEPTH 20 +struct +{ + bool init; + uint8_t num; + uint8_t select[UI_DEPTH]; + uint8_t select_last; + uint8_t layer = 0; + uint8_t index = M_SLEEP; + uint8_t state = S_NONE; + int16_t indicator_y; + int16_t indicator_y_trg; + bool sleep = true; + uint8_t disappear = 1; +} ui; + +//主菜单变量 +#define ICON_INDICATOR_WIDTH 0 //在128*32分辨率时,图标下方有两个像素的空隙,可以在中心图标下方显示指示器,这是指示器的宽度 +#define ICON_HEIGHT 30 +#define ICON_WIDTH 30 +#define ICON_SPACE 36 +#define TITLE_HIGH 14 +#define TITLE_SPACE 36 +#define TITLE_Y 77 +#define TITLE_Y_TRG 58 +#define TITLE_INDICATOR_WIDTH 7 +#define TITLE_INDICATOR_HEIGHT 27 +struct +{ + int16_t icon; + int16_t icon_trg; + int16_t indicator_x = 0; + int16_t indicator_x_trg = TITLE_INDICATOR_WIDTH; + int16_t title_y; + int16_t title_y_trg; + bool select_flag = false; +} tile; + +//进入主菜单时的初始化 +void main_param_init() +{ + switch (controller.value[7]) + { + case 0: + ui.init = true; + ui.indicator_y = 0; + tile.icon = 0; + tile.icon_trg = - ui.select[ui.layer] * ICON_SPACE; + tile.title_y = 0; + tile.title_y_trg = - ui.select[ui.layer] * TITLE_SPACE; + break; + + case 1: + ui.init = false; + ui.select_last = ui.select[ui.layer] + 1; + tile.icon = 0; + tile.icon_trg = ICON_SPACE; + tile.title_y = TITLE_Y; + tile.title_y_trg = TITLE_Y_TRG; + break; + } +} + +//列表变量 +#define LIST_BAR_WIDTH 7 +struct +{ + int16_t y; + int16_t y_trg; + int16_t box_x; + int16_t box_x_trg; + int16_t box_y; + int16_t box_y_trg[UI_DEPTH]; +} list; + +//进入更深层级时的初始化 +void list_param_init_in() +{ + //每次进入页面都有动画 + ui.layer++; + ui.init = 0; + ui.indicator_y = 0; + list.y = 0; + list.y_trg = 16; + list.box_x = 0; +} + +//进入更浅层级时的初始化 +void list_param_init_out() +{ + //离开当前页时重置当前页,确保下次来时从头开始 + ui.select[ui.layer] = 0; + list.box_y_trg[ui.layer] = 0; + + //每次进入页面都有动画 + ui.layer--; + ui.init = 0; + ui.indicator_y = 0; + list.y = 0; + list.y_trg = 16; + list.box_x = 0; +} + +//电压测量页面 +struct +{ + uint8_t left_space = 28; + uint8_t right_space = 28; + uint8_t up_space; + uint8_t down_space; + int16_t left; + int16_t left_trg; + int16_t right; + int16_t right_trg; + int16_t up; + int16_t up_trg; + int16_t down; + int16_t down_trg; +} focus; + +//进入电压测量界面的初始化 +void voltage_param_init() +{ + //每次进入页面都有动画 + focus.up_space = focus.down_space = (display_height - 28) / 2; + focus.left = 0; + focus.left_trg = focus.left_space; + focus.right = display_width - 1; + focus.right_trg = display_width - focus.right_space - 1; + focus.up = 0; + focus.up_trg = focus.up_space; + focus.down = display_height - 1; + focus.down_trg = display_height - focus.down_space - 1; +} + +//关于本机页面 +struct +{ + int16_t y; + int16_t y_trg; +} about; + +//进入关于本机界面的初始化 +void about_param_init() +{ + //每次进入页面都从头开始 + about.y = 0; + about.y_trg = 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 = controller.value[8]; + btn_pressed = true; + } + if (CW_1 == false && CW_2 == false) + { + btn_id = !controller.value[8]; + 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; + } + } +} + +/************************************* 断电保存 *************************************/ + +#include + +uint8_t init_check[11] = { 'a', 'b', 'c', 'd', 'e', 'f','g', 'h', 'i', 'j', 'k' }; +bool eeprom_change = false; +int address; + +//EEPROM写数据,回到主菜单时执行一遍 +void eeprom_write_all_data() +{ + address = 0; for (uint8_t i = 0; i < 11; ++i) EEPROM.write(address + i, init_check[i]); + address += 11; for (uint8_t i = 0; i < 11; ++i) EEPROM.write(address + i, key[0][i]); + address += 11; for (uint8_t i = 0; i < 11; ++i) EEPROM.write(address + i, key[1][i]); + address += 11; for (uint8_t i = 0; i < 11; ++i) EEPROM.write(address + i, key[2][i]); + address += 11; for (uint8_t i = 0; i < 11; ++i) EEPROM.write(address + i, key[3][i]); + address += 11; for (uint8_t i = 0; i < 11; ++i) EEPROM.write(address + i, key[4][i]); + address += 11; for (uint8_t i = 0; i < 11; ++i) EEPROM.write(address + i, key[5][i]); + address += 11; for (uint8_t i = 0; i < 11; ++i) EEPROM.write(address + i, key[6][i]); + address += 11; for (uint8_t i = 0; i < 9; ++i) EEPROM.write(address + i, controller.value[i]); +} + +//EEPROM读数据,开机初始化时执行一遍 +void eeprom_read_all_data() +{ + address = 11; for (uint8_t i = 0; i < 11; ++i) key[0][i] = EEPROM.read(address + i); + address += 11; for (uint8_t i = 0; i < 11; ++i) key[1][i] = EEPROM.read(address + i); + address += 11; for (uint8_t i = 0; i < 11; ++i) key[2][i] = EEPROM.read(address + i); + address += 11; for (uint8_t i = 0; i < 11; ++i) key[3][i] = EEPROM.read(address + i); + address += 11; for (uint8_t i = 0; i < 11; ++i) key[4][i] = EEPROM.read(address + i); + address += 11; for (uint8_t i = 0; i < 11; ++i) key[5][i] = EEPROM.read(address + i); + address += 11; for (uint8_t i = 0; i < 11; ++i) key[6][i] = EEPROM.read(address + i); + address += 11; for (uint8_t i = 0; i < 9; ++i) controller.value[i] = EEPROM.read(address + i); +} + +//开机检查是否已经修改过,没修改过则跳过读配置步骤,用默认设置 +void eeprom_init() +{ + uint8_t check = 0; + address = 0; for (uint8_t i = 0; i < 11; ++i) if (EEPROM.read(address + i) != init_check[i]) check ++; + if (check <= 1) eeprom_read_all_data(); +} + +/************************************* 动画函数 *************************************/ + +//移动函数 +void move(int16_t *a, int16_t *a_trg, uint8_t n) +{ + if (*a < *a_trg) *a += ceil(fabs((float)(*a_trg - *a) / (controller.value[n] / 10.0))); + else if (*a > *a_trg) *a -= ceil(fabs((float)(*a_trg - *a) / (controller.value[n] / 10.0))); +} + +//消失函数 +void disappear() +{ + delay(controller.value[4]); + switch (ui.disappear) + { + 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; ui.disappear = 0; break; + } + ui.disappear++; +} + +/************************************* 显示函数 *************************************/ + +/********************************** 列表风格主菜单 ***********************************/ + +//主菜单列表风格显示函数 +void main_show_list() +{ + u8g2.setFont(u8g2_font_crox4hb_tr); + ui.indicator_y_trg = ceil((ui.select[ui.layer]) * ((float)display_height / (ui.num - 1))); + move(&tile.icon, &tile.icon_trg, 1); + move(&tile.title_y, &tile.title_y_trg, 1); + move(&ui.indicator_y, &ui.indicator_y_trg, 1); + u8g2.drawBox(display_width - TITLE_INDICATOR_WIDTH, 0, TITLE_INDICATOR_WIDTH, ui.indicator_y); + for(uint8_t i = 0 ; i < ui.num ; ++ i) + { + u8g2.drawXBMP(0, tile.icon + i * ICON_SPACE + 1 + (display_height - ICON_HEIGHT) / 2, ICON_WIDTH, ICON_HEIGHT, main_icon_pic[i]); + u8g2.drawStr(((display_width - u8g2.getStrWidth(main_menu[i].m_select)) / 2) + ceil((float)(ICON_WIDTH - TITLE_INDICATOR_WIDTH) / 2), tile.title_y + (i + 1) * TITLE_SPACE - TITLE_HIGH + (display_height - ICON_HEIGHT) / 2, main_menu[i].m_select); + } +} + +/************************************* 图标主菜单 *************************************/ + +//主菜单图标风格显示函数 +void main_show_icon() +{ + u8g2.setFont(u8g2_font_helvB18_tf); + move(&tile.icon, &tile.icon_trg, 1); + if (ui.select[ui.layer] != ui.select_last) + { + move(&tile.indicator_x, &tile.indicator_x_trg, 1); + move(&tile.title_y, &tile.title_y_trg, 1); + } + if (tile.select_flag) + { + tile.indicator_x = TITLE_INDICATOR_WIDTH; + tile.title_y = TITLE_Y_TRG; + } + if (tile.icon == tile.icon_trg && tile.indicator_x == tile.indicator_x_trg && tile.title_y == tile.title_y_trg) ui.select_last = ui.select[ui.layer]; + switch (display_height) + { + case 32: + u8g2.drawBox((display_width - ICON_INDICATOR_WIDTH) / 2, 31, ICON_INDICATOR_WIDTH, 1); + break; + + case 64: + u8g2.drawBox(0, display_height - TITLE_INDICATOR_HEIGHT - 1, tile.indicator_x, TITLE_INDICATOR_HEIGHT); + u8g2.drawStr(((display_width - u8g2.getStrWidth(main_menu[ui.select[ui.layer]].m_select)) / 2) + TITLE_INDICATOR_WIDTH, tile.title_y, main_menu[ui.select[ui.layer]].m_select); + break; + } + if (!ui.init) + { + for(uint8_t i = 0 ; i < ui.num ; ++ i) u8g2.drawXBMP((display_width - ICON_WIDTH) / 2 + (i - ui.select[ui.layer]) * tile.icon, 0, ICON_WIDTH, ICON_HEIGHT, main_icon_pic[i]); + if (tile.icon == tile.icon_trg) + { + ui.init = true; + tile.icon = tile.icon_trg = - ui.select[ui.layer] * ICON_SPACE; + } + } + else for(uint8_t i = 0 ; i < ui.num ; ++ i) u8g2.drawXBMP((display_width - ICON_WIDTH) / 2 + tile.icon + i * ICON_SPACE, 0, ICON_WIDTH, ICON_HEIGHT, main_icon_pic[i]); +} + +/*************************************************************************************/ + +//列表类页面通用显示函数 +void menu_ui_show(struct MENU arr[]) +{ + u8g2.setFont(u8g2_font_wqy12_t_chinese1); + ui.indicator_y_trg = ceil((ui.select[ui.layer]) * ((float)display_height / (ui.num - 1))); + list.box_x_trg = u8g2.getStrWidth(arr[ui.select[ui.layer]].m_select) + 8; + move(&list.y, &list.y_trg, 2); + move(&list.box_x, &list.box_x_trg, 2); + move(&list.box_y, &list.box_y_trg[ui.layer], 2); + move(&ui.indicator_y, &ui.indicator_y_trg, 2); + if (!ui.init) + { + for (uint8_t i = 0 ; i < ui.num ; ++ i) u8g2.drawStr(4, 12 + (i - ui.select[ui.layer]) * list.y + list.box_y_trg[ui.layer], arr[i].m_select); + if (list.y == list.y_trg) + { + ui.init = true; + list.y = list.y_trg = - 16 * ui.select[ui.layer] + list.box_y_trg[ui.layer]; + } + } + else for (uint8_t i = 0 ; i < ui.num ; ++ i) u8g2.drawStr(4, 16 * i + list.y + 12, arr[i].m_select); + u8g2.drawBox(display_width - LIST_BAR_WIDTH, 0, LIST_BAR_WIDTH, ui.indicator_y); + u8g2.setDrawColor(2); + u8g2.drawRBox(0, list.box_y, list.box_x, 16, 1); + u8g2.setDrawColor(1); +} + +//正在编辑界面显示函数 +void editing_show() +{ + uint8_t editing_up_space = (display_height - 32) / 2; + u8g2.setDrawColor(0); + u8g2.drawRBox(12, editing_up_space, 104, 32, 8); + u8g2.setDrawColor(1); + u8g2.drawRFrame(12, editing_up_space, 104, 32, 8); + u8g2.drawRFrame(18, editing_up_space + 19, 92, 8, 3); + float width = (float)(key[ui.select[ui.layer] - 1][ui.select[ui.layer - 1] - 1]) / (float)(key[TRAVEL][ui.select[ui.layer - 1] - 1]) * 86; + if (width) u8g2.drawRBox(20, editing_up_space + 21, width + 2, 4, 1); + u8g2.setCursor(20, editing_up_space + 14); + switch (ui.select[ui.layer]) + { + case 1: u8g2.print("Trigger"); break; + case 2: u8g2.print("Release"); break; + case 3: u8g2.print("Top"); break; + case 4: u8g2.print("Bottom"); break; + case 5: u8g2.print("Times"); break; + } + u8g2.setCursor(96, editing_up_space + 14); + u8g2.print(key[ui.select[ui.layer] - 1][ui.select[ui.layer - 1] - 1]); +} + +//控制器正在编辑界面显示函数 +void controller_show() +{ + uint8_t max_value; + uint8_t cursor_pos; + switch (controller.select) + { + case 0: max_value = 255; cursor_pos = 90; break; + case 1: max_value = 90; cursor_pos = 96; break; + case 2: max_value = 90; cursor_pos = 96; break; + case 3: max_value = 90; cursor_pos = 96; break; + case 4: max_value = 50; cursor_pos = 96; break; + } + uint8_t editing_up_space = (display_height - 32) / 2; + u8g2.setDrawColor(0); + u8g2.drawRBox(12, editing_up_space, 104, 32, 8); + u8g2.setDrawColor(1); + u8g2.drawRFrame(12, editing_up_space, 104, 32, 8); + u8g2.drawRFrame(18, editing_up_space + 19, 92, 8, 3); + float width = (float)controller.value[controller.select] / (float)max_value * 85; + if (width) u8g2.drawRBox(20, editing_up_space + 21, width + 3, 4, 1); + u8g2.setCursor(20, editing_up_space + 14); + switch (ui.select[ui.layer]) + { + case 2: u8g2.print("Brightness"); break; + case 3: u8g2.print("Main Speed"); break; + case 4: u8g2.print("List Speed"); break; + case 5: u8g2.print("Volt Speed"); break; + case 6: u8g2.print("Fade Speed"); break; + } + u8g2.setCursor(cursor_pos, editing_up_space + 14); + u8g2.print(controller.value[controller.select]); +} + +//电压测量页面显示函数 +void voltage_show() +{ + move(&focus.left, &focus.left_trg, 3); + move(&focus.right, &focus.right_trg, 3); + move(&focus.up, &focus.up_trg, 3); + move(&focus.down, &focus.down_trg, 3); + u8g2.setDrawColor(0); + u8g2.setFont(u8g2_font_crox4hb_tr); + u8g2.drawBox(0, 0, display_width, display_height); + u8g2.setDrawColor(1); + u8g2.drawRBox(focus.left, focus.up, focus.right - focus.left, focus.down - focus.up, 8); + float pin_val = analogRead(key[PINS][ui.select[ui.layer - 1] - 1]); + float pin_vol = (pin_val / 4096) * 3.3; + u8g2.setDrawColor(0); + u8g2.setCursor(36, focus.up_space + 21); + u8g2.print(pin_vol); + u8g2.print(" v"); + u8g2.setDrawColor(1); +} + +//关于本机界面显示函数 +void about_show() +{ + move(&about.y, &about.y_trg, 2); + u8g2.setFont(u8g2_font_t0_11_tf); + u8g2.setCursor(0, 10 - about.y); + u8g2.print("K"); + u8g2.setCursor(10 + 16 * 0, 10 - about.y); + u8g2.print("TR"); + u8g2.setCursor(10 + 16 * 1, 10 - about.y); + u8g2.print("RL"); + u8g2.setCursor(10 + 16 * 2, 10 - about.y); + u8g2.print("TP"); + u8g2.setCursor(10 + 16 * 3, 10 - about.y); + u8g2.print("BT"); + u8g2.setCursor(10 + 16 * 4, 10 - about.y); + u8g2.print("TM"); + u8g2.setCursor(10 + 16 * 5, 10 - about.y); + u8g2.print("MD"); + u8g2.setCursor(10 + 16 * 6, 10 - about.y); + u8g2.print("CD"); + for (uint8_t i = 0; i < 7; ++i) u8g2.drawBox(8 + 16 * i, 0 - about.y, 1, 130); + for (uint8_t j = 0; j < 10; ++j) + { + u8g2.setCursor(0, 22 + 12 * j - about.y); + u8g2.print(j); + for (uint8_t i = 0; i < 7; ++i) + { + u8g2.setCursor(10 + 16 * i, 22 + 12 * j - about.y); + if (i == 5) u8g2.print(key[6][j]); + else if (i == 6) u8g2.print(key[5][j]); + else u8g2.print(key[i][j]); + } + } +} + +/************************************* 处理函数 *************************************/ + +//列表类页面旋转时判断通用函数 +void rotate_switch() +{ + switch (btn_id) + { + case 0: + if (ui.select[ui.layer] == 0) + { + if (controller.value[6]) + { + ui.select[ui.layer] = ui.num - 1; + list.y_trg = - 16 * (ui.num - display_height / 16); + list.box_y_trg[ui.layer] = (display_height / 16 - 1) * 16; + break; + } + else break; + } + if (ui.init) + { + ui.select[ui.layer] -= 1; + if (ui.select[ui.layer] < -(list.y / 16)) list.y_trg += 16; + else list.box_y_trg[ui.layer] -= 16; + break; + } + + case 1: + if (ui.select[ui.layer] == (ui.num - 1)) + { + if (controller.value[6]) + { + ui.select[ui.layer] = 0; + list.y_trg = 0; + list.box_y_trg[ui.layer] = 0; + break; + } + else break; + } + if (ui.init) + { + ui.select[ui.layer] += 1; + if ((ui.select[ui.layer] + 1) > (display_height / 16 - list.y / 16)) list.y_trg -= 16; + else list.box_y_trg[ui.layer] += 16; + break; + } + break; + } +} + +//睡眠页面处理函数 +void sleep_proc() +{ + while (ui.sleep) + { + //按键扫描 + for (int n = 0; n < KEYS; n++) runKey(n); + + //旋钮扫描 + btn_scan(); + if (btn_pressed) + { + btn_pressed = false; + + switch (btn_id) + { + case 0: //顺时针旋转 + switch (controller.value[5]) + { + case 1: Consumer.press(HIDConsumer::VOLUME_UP); Consumer.release(); break; + case 2: Consumer.press(HIDConsumer::BRIGHTNESS_UP); Consumer.release(); break; + } + break; + + case 1: //逆时针旋转 + switch (controller.value[5]) + { + case 1: Consumer.press(HIDConsumer::VOLUME_DOWN); Consumer.release(); break; + case 2: Consumer.press(HIDConsumer::BRIGHTNESS_DOWN); Consumer.release(); break; + } + break; + + case 2: + ui.index = M_MAIN; + ui.state = S_NONE; + ui.sleep = false; + u8g2.setPowerSave(0); + main_param_init(); + break; + } + } + } +} + +void main_proc(void) +{ + ui.num = sizeof(main_menu) / sizeof(M_SELECT); + switch (controller.value[7]) + { + case 0: main_show_list(); break; + case 1: main_show_icon(); break; + } + if(btn_pressed) + { + btn_pressed = false; + if (controller.value[7]) + { + tile.indicator_x = 0; + tile.title_y = TITLE_Y; + } + switch(btn_id) + { + case 0: + if (ui.init) + { + if (ui.select[ui.layer] > 0) + { + ui.select_last = ui.select[ui.layer]; + ui.select[ui.layer] -= 1; + tile.icon_trg += ICON_SPACE; + tile.select_flag = false; + if (!controller.value[7]) tile.title_y_trg += TITLE_SPACE; + } + else + { + if (controller.value[6]) + { + ui.select[ui.layer] = ui.num - 1; + tile.icon_trg = - ICON_SPACE * (ui.num - 1); + if (!controller.value[7]) tile.title_y_trg = - TITLE_SPACE * (ui.num - 1); + break; + } + else tile.select_flag = true; + } + } + break; + + case 1: + if (ui.init) + { + if (ui.select[ui.layer] < (ui.num - 1)) + { + ui.select_last = ui.select[ui.layer]; + ui.select[ui.layer] += 1; + tile.icon_trg -= ICON_SPACE; + tile.select_flag = false; + if (!controller.value[7]) tile.title_y_trg -= TITLE_SPACE; + } + else + { + if (controller.value[6]) + { + ui.select[ui.layer] = 0; + tile.icon_trg = 0; + if (!controller.value[7]) tile.title_y_trg = 0; + break; + } + else tile.select_flag = true; + } + } + break; + + case 2: + switch (ui.select[ui.layer]) + { + case 0: + ui.index = M_SLEEP; + ui.state = S_NONE; + u8g2.setPowerSave(1); + ui.sleep = true; + key_init(); + if (eeprom_change == true) + { + eeprom_write_all_data(); + eeprom_change = false; + } + break; + + case 1: ui.index = M_EDITOR; ui.state = S_DISAPPEAR; list_param_init_in(); break; + case 2: ui.index = M_MEASURE; ui.state = S_DISAPPEAR; list_param_init_in(); break; + case 3: ui.index = M_SETTINGS; ui.state = S_DISAPPEAR; list_param_init_in(); break; + } + break; + } + } +} + +void editor_proc() +{ + ui.num = sizeof(editor_menu) / sizeof(M_SELECT); + menu_ui_show(editor_menu); + list.box_x_trg = u8g2.getStrWidth(editor_menu[ui.select[ui.layer]].m_select) + 8; + if (btn_pressed) + { + btn_pressed = false; + switch (btn_id) + { + case 0: case 1: rotate_switch(); break; + case 2: + switch (ui.select[ui.layer]) + { + case 0: ui.index = M_MAIN; ui.state = S_DISAPPEAR; list_param_init_out(); main_param_init(); break; + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: ui.index = M_EDIT; ui.state = S_DISAPPEAR; list_param_init_in(); break; + } + break; + } + } +} + +void edit_proc() +{ + ui.num = sizeof(edit_menu) / sizeof(M_SELECT); + menu_ui_show(edit_menu); + list.box_x_trg = u8g2.getStrWidth(edit_menu[ui.select[ui.layer]].m_select) + 8; + if (btn_pressed) + { + btn_pressed = false; + switch (btn_id) + { + case 0: case 1: rotate_switch(); break; + case 2: + switch (ui.select[ui.layer]) + { + case 0: ui.index = M_EDITOR; ui.state = S_DISAPPEAR; list_param_init_out(); break; + case 1: + case 2: + case 3: + case 4: + case 5: ui.index = M_EDITING; ui.state = S_NONE; break; + case 6: ui.index = M_CODE; ui.state = S_DISAPPEAR; list_param_init_in(); break; + case 7: ui.index = M_MODE; ui.state = S_DISAPPEAR; list_param_init_in(); break; + } + break; + } + } +} + +//正在编辑页面处理函数 +void editing_proc() +{ + if (btn_pressed) + { + btn_pressed = false; + switch (btn_id) + { + case 0: + if (key[ui.select[ui.layer] - 1][ui.select[ui.layer - 1] - 1] < key[TRAVEL][ui.select[ui.layer - 1] - 1]) key[ui.select[ui.layer] - 1][ui.select[ui.layer - 1] - 1] += 1; + eeprom_change = true; + break; + + case 1: + if (key[ui.select[ui.layer] - 1][ui.select[ui.layer - 1] - 1] > 0) key[ui.select[ui.layer] - 1][ui.select[ui.layer - 1] - 1] -= 1; + eeprom_change = true; + break; + + case 2: + if (ui.select[ui.layer - 1] == 11) for (uint8_t i = 0 ; i < KEYS ; ++i) key[ui.select[ui.layer] - 1][i] = key[ui.select[ui.layer] - 1][ui.select[ui.layer - 1] - 1]; + ui.index = M_EDIT; + ui.state = S_NONE; + break; + } + } + menu_ui_show(edit_menu); + for (uint16_t i = 0; i < buf_len; ++i) buf_ptr[i] = buf_ptr[i] & (i % 2 == 0 ? 0x55 : 0xAA); + editing_show(); +} + +void code_proc() +{ + ui.num = sizeof(code_menu) / sizeof(M_SELECT); + menu_ui_show(code_menu); + list.box_x_trg = u8g2.getStrWidth(code_menu[ui.select[ui.layer]].m_select) + 8; + if (btn_pressed) + { + btn_pressed = false; + switch (btn_id) + { + case 0: case 1: rotate_switch(); break; + case 2: + switch (ui.select[ui.layer]) + { + case 0: if (ui.select[ui.layer - 2] == 11) for (uint8_t i = 0 ; i < KEYS ; ++i) key[CODES][i] = key[CODES][ui.select[ui.layer - 2] - 1]; + ui.index = M_EDIT; ui.state = S_DISAPPEAR; list_param_init_out(); break; + case 1: ui.index = M_ALPH; ui.state = S_DISAPPEAR; list_param_init_in(); break; + case 2: ui.index = M_NUMB; ui.state = S_DISAPPEAR; list_param_init_in(); break; + case 3: ui.index = M_FUNC; ui.state = S_DISAPPEAR; list_param_init_in(); break; + case 4: ui.index = M_MORE; ui.state = S_DISAPPEAR; list_param_init_in(); break; + } + break; + } + } +} + +void alph_proc() +{ + ui.num = sizeof(alph_menu) / sizeof(M_SELECT); + menu_ui_show(alph_menu); + list.box_x_trg = u8g2.getStrWidth(alph_menu[ui.select[ui.layer]].m_select) + 8; + if (btn_pressed) + { + btn_pressed = false; + switch (btn_id) + { + case 0: case 1: rotate_switch(); break; + case 2: + switch (ui.select[ui.layer]) + { + case 1: key[CODES][ui.select[ui.layer - 3] - 1] = 'a'; eeprom_change = true; break; + case 2: key[CODES][ui.select[ui.layer - 3] - 1] = 'b'; eeprom_change = true; break; + case 3: key[CODES][ui.select[ui.layer - 3] - 1] = 'c'; eeprom_change = true; break; + case 4: key[CODES][ui.select[ui.layer - 3] - 1] = 'd'; eeprom_change = true; break; + case 5: key[CODES][ui.select[ui.layer - 3] - 1] = 'e'; eeprom_change = true; break; + case 6: key[CODES][ui.select[ui.layer - 3] - 1] = 'f'; eeprom_change = true; break; + case 7: key[CODES][ui.select[ui.layer - 3] - 1] = 'g'; eeprom_change = true; break; + case 8: key[CODES][ui.select[ui.layer - 3] - 1] = 'h'; eeprom_change = true; break; + case 9: key[CODES][ui.select[ui.layer - 3] - 1] = 'i'; eeprom_change = true; break; + case 10: key[CODES][ui.select[ui.layer - 3] - 1] = 'j'; eeprom_change = true; break; + case 11: key[CODES][ui.select[ui.layer - 3] - 1] = 'k'; eeprom_change = true; break; + case 12: key[CODES][ui.select[ui.layer - 3] - 1] = 'l'; eeprom_change = true; break; + case 13: key[CODES][ui.select[ui.layer - 3] - 1] = 'm'; eeprom_change = true; break; + case 14: key[CODES][ui.select[ui.layer - 3] - 1] = 'n'; eeprom_change = true; break; + case 15: key[CODES][ui.select[ui.layer - 3] - 1] = 'o'; eeprom_change = true; break; + case 16: key[CODES][ui.select[ui.layer - 3] - 1] = 'p'; eeprom_change = true; break; + case 17: key[CODES][ui.select[ui.layer - 3] - 1] = 'q'; eeprom_change = true; break; + case 18: key[CODES][ui.select[ui.layer - 3] - 1] = 'r'; eeprom_change = true; break; + case 19: key[CODES][ui.select[ui.layer - 3] - 1] = 's'; eeprom_change = true; break; + case 20: key[CODES][ui.select[ui.layer - 3] - 1] = 't'; eeprom_change = true; break; + case 21: key[CODES][ui.select[ui.layer - 3] - 1] = 'u'; eeprom_change = true; break; + case 22: key[CODES][ui.select[ui.layer - 3] - 1] = 'v'; eeprom_change = true; break; + case 23: key[CODES][ui.select[ui.layer - 3] - 1] = 'w'; eeprom_change = true; break; + case 24: key[CODES][ui.select[ui.layer - 3] - 1] = 'x'; eeprom_change = true; break; + case 25: key[CODES][ui.select[ui.layer - 3] - 1] = 'y'; eeprom_change = true; break; + case 26: key[CODES][ui.select[ui.layer - 3] - 1] = 'z'; eeprom_change = true; break; + } + ui.index = M_CODE; + ui.state = S_DISAPPEAR; + list_param_init_out(); + break; + } + } +} + +void numb_proc() +{ + ui.num = sizeof(numb_menu) / sizeof(M_SELECT); + menu_ui_show(numb_menu); + list.box_x_trg = u8g2.getStrWidth(numb_menu[ui.select[ui.layer]].m_select) + 8; + if (btn_pressed) + { + btn_pressed = false; + switch (btn_id) + { + case 0: case 1: rotate_switch(); break; + case 2: + switch (ui.select[ui.layer]) + { + case 1: key[CODES][ui.select[ui.layer - 3] - 1] = 0; eeprom_change = true; break; + case 2: key[CODES][ui.select[ui.layer - 3] - 1] = 1; eeprom_change = true; break; + case 3: key[CODES][ui.select[ui.layer - 3] - 1] = 2; eeprom_change = true; break; + case 4: key[CODES][ui.select[ui.layer - 3] - 1] = 3; eeprom_change = true; break; + case 5: key[CODES][ui.select[ui.layer - 3] - 1] = 4; eeprom_change = true; break; + case 6: key[CODES][ui.select[ui.layer - 3] - 1] = 5; eeprom_change = true; break; + case 7: key[CODES][ui.select[ui.layer - 3] - 1] = 6; eeprom_change = true; break; + case 8: key[CODES][ui.select[ui.layer - 3] - 1] = 7; eeprom_change = true; break; + case 9: key[CODES][ui.select[ui.layer - 3] - 1] = 8; eeprom_change = true; break; + case 10: key[CODES][ui.select[ui.layer - 3] - 1] = 9; eeprom_change = true; break; + } + ui.index = M_CODE; + ui.state = S_DISAPPEAR; + list_param_init_out(); + break; + } + } +} + +void func_proc() +{ + ui.num = sizeof(func_menu) / sizeof(M_SELECT); + menu_ui_show(func_menu); + list.box_x_trg = u8g2.getStrWidth(func_menu[ui.select[ui.layer]].m_select) + 8; + if (btn_pressed) + { + btn_pressed = false; + switch (btn_id) + { + case 0: case 1: rotate_switch(); break; + case 2: + switch (ui.select[ui.layer]) + { + case 1: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_F1; eeprom_change = true; break; + case 2: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_F2; eeprom_change = true; break; + case 3: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_F3; eeprom_change = true; break; + case 4: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_F4; eeprom_change = true; break; + case 5: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_F5; eeprom_change = true; break; + case 6: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_F6; eeprom_change = true; break; + case 7: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_F7; eeprom_change = true; break; + case 8: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_F8; eeprom_change = true; break; + case 9: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_F9; eeprom_change = true; break; + case 10: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_F10; eeprom_change = true; break; + case 11: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_F11; eeprom_change = true; break; + case 12: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_F12; eeprom_change = true; break; + } + ui.index = M_CODE; + ui.state = S_DISAPPEAR; + list_param_init_out(); + break; + } + } +} + +void more_proc() +{ + ui.num = sizeof(more_menu) / sizeof(M_SELECT); + menu_ui_show(more_menu); + list.box_x_trg = u8g2.getStrWidth(more_menu[ui.select[ui.layer]].m_select) + 8; + if (btn_pressed) + { + btn_pressed = false; + switch (btn_id) + { + case 0: case 1: rotate_switch(); break; + case 2: + switch (ui.select[ui.layer]) + { + case 1: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_LEFT_CTRL; eeprom_change = true; break; + case 2: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_RIGHT_CTRL; eeprom_change = true; break; + case 3: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_LEFT_SHIFT; eeprom_change = true; break; + case 4: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_RIGHT_SHIFT; eeprom_change = true; break; + case 5: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_LEFT_ALT; eeprom_change = true; break; + case 6: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_RIGHT_ALT; eeprom_change = true; break; + case 7: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_LEFT_GUI; eeprom_change = true; break; + case 8: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_RIGHT_GUI; eeprom_change = true; break; + case 9: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_UP_ARROW; eeprom_change = true; break; + case 10: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_DOWN_ARROW; eeprom_change = true; break; + case 11: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_LEFT_ARROW; eeprom_change = true; break; + case 12: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_RIGHT_ARROW; eeprom_change = true; break; + case 13: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_BACKSPACE; eeprom_change = true; break; + case 14: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_TAB; eeprom_change = true; break; + case 15: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_RETURN; eeprom_change = true; break; + case 16: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_ESC; eeprom_change = true; break; + case 17: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_INSERT; eeprom_change = true; break; + case 18: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_DELETE; eeprom_change = true; break; + case 19: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_PAGE_UP; eeprom_change = true; break; + case 21: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_PAGE_DOWN; eeprom_change = true; break; + case 22: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_HOME; eeprom_change = true; break; + case 23: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_END; eeprom_change = true; break; + case 24: key[CODES][ui.select[ui.layer - 3] - 1] = KEY_CAPS_LOCK; eeprom_change = true; break; + } + ui.index = M_CODE; + ui.state = S_DISAPPEAR; + list_param_init_out(); + break; + } + } +} + +void mode_proc() +{ + ui.num = sizeof(mode_menu) / sizeof(M_SELECT); + menu_ui_show(mode_menu); + list.box_x_trg = u8g2.getStrWidth(mode_menu[ui.select[ui.layer]].m_select) + 8; + if (btn_pressed) + { + btn_pressed = false; + switch (btn_id) + { + case 0: case 1: rotate_switch(); break; + case 2: + switch (ui.select[ui.layer]) + { + case 1: key[MODES][ui.select[ui.layer - 2] - 1] = 1; eeprom_change = true; break; + case 2: key[MODES][ui.select[ui.layer - 2] - 1] = 2; eeprom_change = true; break; + case 3: key[MODES][ui.select[ui.layer - 2] - 1] = 0; eeprom_change = true; break; + } + if (ui.select[ui.layer - 2] == 11) for (uint8_t i = 0 ; i < KEYS ; ++i) key[MODES][i] = key[MODES][ui.select[ui.layer - 2] - 1]; + ui.index = M_EDIT; + ui.state = S_DISAPPEAR; + list_param_init_out(); + break; + } + } +} + +void measure_proc() +{ + ui.num = sizeof(measure_menu) / sizeof(M_SELECT); + menu_ui_show(measure_menu); + list.box_x_trg = u8g2.getStrWidth(measure_menu[ui.select[ui.layer]].m_select) + 8; + if (btn_pressed) + { + btn_pressed = false; + switch (btn_id) + { + case 0: case 1: rotate_switch(); break; + case 2: + switch (ui.select[ui.layer]) + { + case 0: ui.index = M_MAIN; ui.state = S_DISAPPEAR; list_param_init_out(); main_param_init(); break; + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: ui.index = M_VOLTAGE; ui.state = S_DISAPPEAR; list_param_init_in(); voltage_param_init(); break; + } + break; + } + } +} + +void settings_proc() +{ + ui.num = sizeof(settings_menu) / sizeof(M_SELECT); + menu_ui_show(settings_menu); + list.box_x_trg = u8g2.getStrWidth(settings_menu[ui.select[ui.layer]].m_select) + 8; + if (btn_pressed) + { + btn_pressed = false; + switch (btn_id) + { + case 0: case 1: rotate_switch(); break; + case 2: + switch (ui.select[ui.layer]) + { + case 0: ui.index = M_MAIN; ui.state = S_DISAPPEAR; list_param_init_out(); main_param_init(); break; + case 1: ui.index = M_KNOB; ui.state = S_DISAPPEAR; list_param_init_in(); break; + case 2: ui.index = M_CONTROLLER; ui.state = S_NONE; controller.select = 0; break; + case 3: ui.index = M_CONTROLLER; ui.state = S_NONE; controller.select = 1; break; + case 4: ui.index = M_CONTROLLER; ui.state = S_NONE; controller.select = 2; break; + case 5: ui.index = M_CONTROLLER; ui.state = S_NONE; controller.select = 3; break; + case 6: ui.index = M_CONTROLLER; ui.state = S_NONE; controller.select = 4; break; + case 7: ui.index = M_SETTINGS; ui.state = S_DISAPPEAR; eeprom_change = true; controller.value[6] = !controller.value[6]; break; + case 8: ui.index = M_SETTINGS; ui.state = S_DISAPPEAR; eeprom_change = true; controller.value[7] = !controller.value[7]; break; + case 9: ui.index = M_SETTINGS; ui.state = S_DISAPPEAR; eeprom_change = true; controller.value[8] = !controller.value[8]; break; + case 10: ui.index = M_ABOUT; ui.state = S_DISAPPEAR; list_param_init_in(); about_param_init(); break; + } + break; + } + } +} + +void knob_proc() +{ + ui.num = sizeof(knob_menu) / sizeof(M_SELECT); + menu_ui_show(knob_menu); + list.box_x_trg = u8g2.getStrWidth(knob_menu[ui.select[ui.layer]].m_select) + 8; + if (btn_pressed) + { + btn_pressed = false; + switch (btn_id) + { + case 0: case 1: rotate_switch(); break; + case 2: + switch (ui.select[ui.layer]) + { + case 1: controller.value[5] = 1; eeprom_change = true; break; + case 2: controller.value[5] = 2; eeprom_change = true; break; + case 3: controller.value[5] = 0; eeprom_change = true; break; + } + ui.index = M_SETTINGS; + ui.state = S_DISAPPEAR; + list_param_init_out(); + break; + } + } +} + +//正在编辑页面处理函数 +void controller_proc() +{ + uint8_t max_value; + uint8_t min_value; + uint8_t step_value; + switch (controller.select) + { + case 0: max_value = 255; min_value = 0; step_value = 5; break; + case 1: max_value = 90; min_value = 10; step_value = 1; break; + case 2: max_value = 90; min_value = 10; step_value = 1; break; + case 3: max_value = 90; min_value = 10; step_value = 1; break; + case 4: max_value = 50; min_value = 0; step_value = 5; break; + } + if (btn_pressed) + { + btn_pressed = false; + switch (btn_id) + { + case 0: if (controller.value[controller.select] < max_value) controller.value[controller.select] += step_value; eeprom_change = true; break; + case 1: if (controller.value[controller.select] > min_value) controller.value[controller.select] -= step_value; eeprom_change = true; break; + case 2: ui.index = M_SETTINGS; ui.state = S_NONE; break; + } + if (controller.select == 0) u8g2.setContrast(controller.value[0]); + } + menu_ui_show(settings_menu); + for (uint16_t i = 0; i < buf_len; ++i) buf_ptr[i] = buf_ptr[i] & (i % 2 == 0 ? 0x55 : 0xAA); + controller_show(); +} + +void voltage_proc() +{ + voltage_show(); + if (btn_pressed) + { + btn_pressed = false; + if (btn_id == 2) + { + ui.index = M_MEASURE; + ui.state = S_DISAPPEAR; + list_param_init_out(); + } + } +} + +void about_proc(void) +{ + about_show(); + if(btn_pressed) + { + btn_pressed = false; + switch(btn_id) + { + case 0: if (about.y_trg > 0) about.y_trg -= 12; break; + case 1: if (about.y_trg < 130 - display_height)about.y_trg += 12; break; + case 2: + ui.index = M_SETTINGS; + ui.state = S_DISAPPEAR; + list_param_init_out(); + break; + } + } +} + +//总的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_EDITOR: editor_proc(); break; + case M_EDIT: edit_proc(); break; + case M_EDITING: editing_proc(); break; + case M_CODE: code_proc(); break; + case M_ALPH: alph_proc(); break; + case M_NUMB: numb_proc(); break; + case M_FUNC: func_proc(); break; + case M_MORE: more_proc(); break; + case M_MODE: mode_proc(); break; + case M_MEASURE: measure_proc(); break; + case M_SETTINGS: settings_proc(); break; + case M_CONTROLLER: controller_proc(); break; + case M_KNOB: knob_proc(); break; + case M_VOLTAGE: voltage_proc(); break; + case M_ABOUT: about_proc(); break; + } + break; + case S_DISAPPEAR: disappear(); break; + } + u8g2.sendBuffer(); +} + +//OLED初始化函数 +void oled_init() +{ + u8g2.setBusClock(1000000); + u8g2.begin(); + u8g2.setFont(u8g2_font_helvB18_tf); + u8g2.setContrast(controller.value[0]); + buf_ptr = u8g2.getBufferPtr(); + buf_len = 8 * u8g2.getBufferTileHeight() * u8g2.getBufferTileWidth(); + display_width = u8g2.getDisplayWidth(); + display_height = u8g2.getDisplayHeight(); +} + +void setup() +{ + eeprom_init(); + oled_init(); + btn_init(); + hid_init(); + key_init(); +} + +void loop() +{ + btn_scan(); + ui_proc(); +} + +