当前位置:   article > 正文

U8g2图形库使用技巧记录(4)_u8g2库

u8g2库

        ~最近在优化平衡车的UI界面~

        寻寻觅觅间,偶然在B站上看到一个很优雅的UI框架WouoUI,底层的oled驱动操作使用的是U8G2方式,于是花了点时间把它移植了过来(丙正正),跑在小平衡车(小黑)上效果真的很哇塞,以一路foc电机+一个按钮作为输入;

~以下是UI的视频效果:

~以下是移植的源码,希望对想移植的后来者有所帮助:

  1. /************************************* 屏幕驱动 *************************************/
  2. //分辨率128*64,可以使用硬件IIC接口
  3. #include "flex_ui.h"
  4. #include "u8g2_ssd1306.h"
  5. #include <driver/gpio.h>
  6. #include <driver/spi_master.h>
  7. #include <esp_log.h>
  8. #include <freertos/FreeRTOS.h>
  9. #include <freertos/task.h>
  10. #include "nvs_flash.h"
  11. #include "nvs.h"
  12. #include "sbus.h"
  13. #include "param.h"
  14. #include "ms5611.h"
  15. #include "tle5012b.h"
  16. #include "drv_adc.h"
  17. #include "drv_motor.h"
  18. #include "stabilizer.h"
  19. #include <stdio.h>
  20. #include <string.h>
  21. #include <u8g2.h>
  22. #include "wiring.h"
  23. #include "lv_math.h"
  24. static const char *TAG = "flex_ui";
  25. /************************************* 定义页面 *************************************/
  26. //总目录,缩进表示页面层级
  27. enum
  28. {
  29. M_WINDOW,
  30. M_SLEEP,
  31. M_MAIN,
  32. M_EDITOR,
  33. M_KNOB,
  34. M_KRF,
  35. M_KPF,
  36. M_VOLT,
  37. M_SETTING,
  38. M_ABOUT,
  39. };
  40. //状态,初始化标签
  41. enum
  42. {
  43. S_FADE, //转场动画
  44. S_WINDOW, //弹窗初始化
  45. S_LAYER_IN, //层级初始化
  46. S_LAYER_OUT, //层级初始化
  47. S_NONE, //直接选择页面
  48. };
  49. //菜单结构体
  50. typedef struct MENU
  51. {
  52. char *m_select;
  53. } M_SELECT;
  54. /************************************* 定义内容 *************************************/
  55. /************************************* 文字内容 *************************************/
  56. M_SELECT main_menu[]
  57. {
  58. {"Sleep"},
  59. {"Editor"},
  60. {"Volt"},
  61. {"Setting"},
  62. };
  63. M_SELECT editor_menu[]
  64. {
  65. {"[ Editor ]"},
  66. {"- Function 0"},
  67. {"- Function 1"},
  68. {"- Function 2"},
  69. {"- Function 3"},
  70. {"- Function 4"},
  71. {"- Function 5"},
  72. {"- Function 6"},
  73. {"- Function 7"},
  74. {"- Function 8"},
  75. {"- Function 9"},
  76. {"- Knob"},
  77. };
  78. M_SELECT knob_menu[]
  79. {
  80. {"[ Knob ]"},
  81. {"# Rotate Func"},
  82. {"$ Press Func"},
  83. };
  84. M_SELECT krf_menu[]
  85. {
  86. {"[ Rotate Function ]"},
  87. {"--------------------------"},
  88. {"= Disable"},
  89. {"--------------------------"},
  90. {"= Volume"},
  91. {"= Brightness"},
  92. {"--------------------------"},
  93. };
  94. M_SELECT kpf_menu[]
  95. {
  96. {"[ Press Function ]"},
  97. {"--------------------------"},
  98. {"= Disable"},
  99. {"--------------------------"},
  100. {"= A"},
  101. {"= B"},
  102. {"= C"},
  103. {"= D"},
  104. {"= E"},
  105. {"= F"},
  106. {"= G"},
  107. {"= H"},
  108. {"= I"},
  109. {"= J"},
  110. {"= K"},
  111. {"= L"},
  112. {"= M"},
  113. {"= N"},
  114. {"= O"},
  115. {"= P"},
  116. {"= Q"},
  117. {"= R"},
  118. {"= S"},
  119. {"= T"},
  120. {"= U"},
  121. {"= V"},
  122. {"= W"},
  123. {"= X"},
  124. {"= Y"},
  125. {"= Z"},
  126. {"--------------------------"},
  127. {"= 0"},
  128. {"= 1"},
  129. {"= 2"},
  130. {"= 3"},
  131. {"= 4"},
  132. {"= 5"},
  133. {"= 6"},
  134. {"= 7"},
  135. {"= 8"},
  136. {"= 9"},
  137. {"--------------------------"},
  138. {"= Esc"},
  139. {"= F1"},
  140. {"= F2"},
  141. {"= F3"},
  142. {"= F4"},
  143. {"= F5"},
  144. {"= F6"},
  145. {"= F7"},
  146. {"= F8"},
  147. {"= F9"},
  148. {"= F10"},
  149. {"= F11"},
  150. {"= F12"},
  151. {"--------------------------"},
  152. {"= Left Ctrl"},
  153. {"= Left Shift"},
  154. {"= Left Alt"},
  155. {"= Left Win"},
  156. {"= Right Ctrl"},
  157. {"= Right Shift"},
  158. {"= Right Alt"},
  159. {"= Right Win"},
  160. {"--------------------------"},
  161. {"= Caps Lock"},
  162. {"= Backspace"},
  163. {"= Return"},
  164. {"= Insert"},
  165. {"= Delete"},
  166. {"= Tab"},
  167. {"--------------------------"},
  168. {"= Home"},
  169. {"= End"},
  170. {"= Page Up"},
  171. {"= Page Down"},
  172. {"--------------------------"},
  173. {"= Up Arrow"},
  174. {"= Down Arrow"},
  175. {"= Left Arrow"},
  176. {"= Right Arrow"},
  177. {"--------------------------"},
  178. };
  179. M_SELECT volt_menu[]
  180. {
  181. {"A0"},
  182. {"A1"},
  183. {"A2"},
  184. {"A3"},
  185. {"A4"},
  186. {"A5"},
  187. {"A6"},
  188. {"A7"},
  189. {"B0"},
  190. {"B1"},
  191. };
  192. M_SELECT setting_menu[]
  193. {
  194. {"[ Setting ]"},
  195. {"~ Disp Bri"},
  196. {"~ Tile Ani"},
  197. {"~ List Ani"},
  198. {"~ Win Ani"},
  199. {"~ Spot Ani"},
  200. {"~ Tag Ani"},
  201. {"~ Fade Ani"},
  202. {"~ Btn SPT"},
  203. {"~ Btn LPT"},
  204. {"+ T Ufd Fm Scr"},
  205. {"+ L Ufd Fm Scr"},
  206. {"+ T Loop Mode"},
  207. {"+ L Loop Mode"},
  208. {"+ Win Bokeh Bg"},
  209. {"+ Knob Rot Dir"},
  210. {"+ Dark Mode"},
  211. {"- [ About ]"},
  212. };
  213. M_SELECT about_menu[]
  214. {
  215. {"[ FlexUI ]"},
  216. {"- Version: v1.0"},
  217. {"- Board: ESP32 PICO"},
  218. {"- Ram: 340k"},
  219. {"- Flash: 4MB"},
  220. {"- Freq: 240Mhz"},
  221. {"- Creator: Leon"},
  222. {"- Billi UID: 20230705"},
  223. };
  224. /************************************* 图片内容 *************************************/
  225. const uint8_t main_icon_pic[][120]
  226. {
  227. {
  228. 0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xF1,0x3F,
  229. 0xFF,0xFF,0xC3,0x3F,0xFF,0xFF,0x87,0x3F,0xFF,0xFF,0x07,0x3F,0xFF,0xFF,0x0F,0x3E,
  230. 0xFF,0xFF,0x0F,0x3E,0xFF,0xFF,0x0F,0x3C,0xFF,0xFF,0x0F,0x3C,0xFF,0xFF,0x0F,0x38,
  231. 0xFF,0xFF,0x0F,0x38,0xFF,0xFF,0x0F,0x38,0xFF,0xFF,0x07,0x38,0xFF,0xFF,0x07,0x38,
  232. 0xFF,0xFF,0x03,0x38,0xF7,0xFF,0x01,0x38,0xE7,0xFF,0x00,0x3C,0x87,0x3F,0x00,0x3C,
  233. 0x0F,0x00,0x00,0x3E,0x0F,0x00,0x00,0x3E,0x1F,0x00,0x00,0x3F,0x3F,0x00,0x80,0x3F,
  234. 0x7F,0x00,0xC0,0x3F,0xFF,0x01,0xF0,0x3F,0xFF,0x07,0xFC,0x3F,0xFF,0xFF,0xFF,0x3F,
  235. 0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xFF,0x3F
  236. },
  237. {
  238. 0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xFF,0x3F,0xFF,0xF9,0xE7,0x3F,
  239. 0xFF,0xF9,0xE7,0x3F,0xFF,0xF9,0xE7,0x3F,0xFF,0xF0,0xE7,0x3F,0x7F,0xE0,0xE7,0x3F,
  240. 0x7F,0xE0,0xC3,0x3F,0x7F,0xE0,0xC3,0x3F,0x7F,0xE0,0xC3,0x3F,0x7F,0xE0,0xE7,0x3F,
  241. 0xFF,0xF0,0xE7,0x3F,0xFF,0xF9,0xE7,0x3F,0xFF,0xF9,0xE7,0x3F,0xFF,0xF9,0xE7,0x3F,
  242. 0xFF,0xF9,0xE7,0x3F,0xFF,0xF9,0xC3,0x3F,0xFF,0xF9,0x81,0x3F,0xFF,0xF0,0x81,0x3F,
  243. 0xFF,0xF0,0x81,0x3F,0xFF,0xF0,0x81,0x3F,0xFF,0xF9,0x81,0x3F,0xFF,0xF9,0xC3,0x3F,
  244. 0xFF,0xF9,0xE7,0x3F,0xFF,0xF9,0xE7,0x3F,0xFF,0xF9,0xE7,0x3F,0xFF,0xFF,0xFF,0x3F,
  245. 0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xFF,0x3F
  246. },
  247. {
  248. 0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xFF,0x3F,0xEF,0xFF,0xFF,0x3F,0xC7,0xFF,0xFF,0x3F,
  249. 0xC7,0xF3,0xFF,0x3F,0x83,0xC0,0xFF,0x3F,0xEF,0xCC,0xFF,0x3F,0x6F,0x9E,0xFF,0x3F,
  250. 0x6F,0x9E,0xFF,0x3F,0x2F,0x3F,0xFF,0x3F,0x2F,0x3F,0xFF,0x3F,0x8F,0x7F,0xFE,0x3F,
  251. 0x8F,0x7F,0xFE,0x39,0x8F,0x7F,0xFE,0x39,0xCF,0xFF,0xFC,0x3C,0xCF,0xFF,0xFC,0x3C,
  252. 0xEF,0xFF,0xFC,0x3C,0xEF,0xFF,0x79,0x3E,0xEF,0xFF,0x79,0x3E,0xEF,0xFF,0x33,0x3F,
  253. 0xEF,0xFF,0x33,0x3F,0xEF,0xFF,0x87,0x3F,0xEF,0xFF,0xCF,0x3F,0xEF,0xFF,0x7F,0x3E,
  254. 0xEF,0xFF,0x7F,0x38,0x0F,0x00,0x00,0x30,0xFF,0xFF,0x7F,0x38,0xFF,0xFF,0x7F,0x3E,
  255. 0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xFF,0x3F,
  256. },
  257. {
  258. 0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xFF,0x3F,
  259. 0xFF,0x1F,0xFE,0x3F,0xFF,0x1F,0xFE,0x3F,0xFF,0x0C,0xCC,0x3F,0x7F,0x00,0x80,0x3F,
  260. 0x3F,0x00,0x00,0x3F,0x3F,0xE0,0x01,0x3F,0x7F,0xF8,0x87,0x3F,0x7F,0xFC,0x8F,0x3F,
  261. 0x3F,0xFC,0x0F,0x3F,0x0F,0x3E,0x1F,0x3C,0x0F,0x1E,0x1E,0x3C,0x0F,0x1E,0x1E,0x3C,
  262. 0x0F,0x3E,0x1F,0x3C,0x3F,0xFC,0x0F,0x3F,0x7F,0xFC,0x8F,0x3F,0x7F,0xF8,0x87,0x3F,
  263. 0x3F,0xE0,0x01,0x3F,0x3F,0x00,0x00,0x3F,0x7F,0x00,0x80,0x3F,0xFF,0x0C,0xCC,0x3F,
  264. 0xFF,0x1F,0xFE,0x3F,0xFF,0x1F,0xFE,0x3F,0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xFF,0x3F,
  265. 0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xFF,0x3F
  266. },
  267. };
  268. /************************************* 页面变量 *************************************/
  269. //OLED变量
  270. #define DISP_H 64 //屏幕高度
  271. #define DISP_W 128 //屏幕宽度
  272. uint8_t *buf_ptr; //指向屏幕缓冲的指针
  273. uint16_t buf_len; //缓冲长度
  274. //UI变量
  275. #define UI_DEPTH 20 //最深层级数
  276. #define UI_MNUMB 100 //菜单数量
  277. #define UI_PARAM 16 //参数数量
  278. enum
  279. {
  280. DISP_BRI, //屏幕亮度
  281. TILE_ANI, //磁贴动画速度
  282. LIST_ANI, //列表动画速度
  283. WIN_ANI, //弹窗动画速度
  284. SPOT_ANI, //聚光动画速度
  285. TAG_ANI, //标签动画速度
  286. FADE_ANI, //消失动画速度
  287. BTN_SPT, //按键短按时长
  288. BTN_LPT, //按键长按时长
  289. TILE_UFD, //磁贴图标从头展开开关
  290. LIST_UFD, //菜单列表从头展开开关
  291. TILE_LOOP, //磁贴图标循环模式开关
  292. LIST_LOOP, //菜单列表循环模式开关
  293. WIN_BOK, //弹窗背景虚化开关
  294. KNOB_DIR, //旋钮方向切换开关
  295. DARK_MODE, //黑暗模式开关
  296. };
  297. struct
  298. {
  299. bool init;
  300. uint8_t num[UI_MNUMB];
  301. uint8_t select[UI_DEPTH];
  302. uint8_t layer;
  303. uint8_t index = M_MAIN;
  304. uint8_t state = S_LAYER_IN;
  305. bool sleep = true;
  306. uint8_t fade = 1;
  307. uint8_t param[UI_PARAM];
  308. } ui;
  309. //磁贴变量
  310. //所有磁贴页面都使用同一套参数
  311. #define TILE_B_FONT u8g2_font_helvB18_tr //磁贴大标题字体
  312. #define TILE_B_TITLE_H 18 //磁贴大标题字体高度
  313. #define TILE_ICON_H 30 //磁贴图标高度
  314. #define TILE_ICON_W 30 //磁贴图标宽度
  315. #define TILE_ICON_S 36 //磁贴图标间距
  316. #define TILE_INDI_H 27 //磁贴大标题指示器高度
  317. #define TILE_INDI_W 7 //磁贴大标题指示器宽度
  318. #define TILE_INDI_S 36 //磁贴大标题指示器上边距
  319. struct
  320. {
  321. float title_y_calc = TILE_INDI_S + (TILE_INDI_H - TILE_B_TITLE_H) / 2 + TILE_B_TITLE_H * 2;
  322. float title_y_trg_calc = TILE_INDI_S + (TILE_INDI_H - TILE_B_TITLE_H) / 2 + TILE_B_TITLE_H;
  323. int16_t temp;
  324. bool select_flag;
  325. float icon_x;
  326. float icon_x_trg;
  327. float icon_y;
  328. float icon_y_trg;
  329. float indi_x;
  330. float indi_x_trg;
  331. float title_y;
  332. float title_y_trg;
  333. } tile;
  334. //列表变量
  335. //默认参数
  336. #define LIST_FONT u8g2_font_HelvetiPixel_tr //列表字体
  337. #define LIST_TEXT_H 8 //列表每行文字字体的高度
  338. #define LIST_LINE_H 16 //列表单行高度
  339. #define LIST_TEXT_S 4 //列表每行文字的上边距,左边距和右边距,下边距由它和字体高度和行高度决定
  340. #define LIST_BAR_W 5 //列表进度条宽度,需要是奇数,因为正中间有1像素宽度的线
  341. #define LIST_BOX_R 0.5 //列表选择框圆角
  342. /*
  343. //超窄行高度测试
  344. #define LIST_FONT u8g2_font_4x6_tr //列表字体
  345. #define LIST_TEXT_H 5 //列表每行文字字体的高度
  346. #define LIST_LINE_H 7 //列表单行高度
  347. #define LIST_TEXT_S 1 //列表每行文字的上边距,左边距和右边距,下边距由它和字体高度和行高度决定
  348. #define LIST_BAR_W 7 //列表进度条宽度,需要是奇数,因为正中间有1像素宽度的线
  349. #define LIST_BOX_R 0.5 //列表选择框圆角
  350. */
  351. struct
  352. {
  353. uint8_t line_n = DISP_H / LIST_LINE_H;
  354. int16_t temp;
  355. bool loop;
  356. float y;
  357. float y_trg;
  358. float box_x;
  359. float box_x_trg;
  360. float box_y;
  361. float box_y_trg[UI_DEPTH];
  362. float bar_y;
  363. float bar_y_trg;
  364. } list;
  365. //曲线相关
  366. #define WAVE_SAMPLE 20 //采集倍数
  367. #define WAVE_W 94 //波形宽度
  368. #define WAVE_L 24 //波形左边距
  369. #define WAVE_U 0 //波形上边距
  370. #define WAVE_MAX 27 //最大值
  371. #define WAVE_MIN 4 //最小值
  372. #define WAVE_BOX_H 32 //波形边框高度
  373. #define WAVE_BOX_W 94 //波形边框宽度
  374. #define WAVE_BOX_L_S 24 //波形边框左边距
  375. //列表和文字背景框相关
  376. #define VOLT_FONT u8g2_font_helvB18_tr //电压数字字体
  377. #define VOLT_TEXT_BG_L_S 24 //文字背景框左边距
  378. #define VOLT_TEXT_BG_W 94 //文字背景框宽度
  379. #define VOLT_TEXT_BG_H 29 //文字背景框高度
  380. struct
  381. {
  382. int ch0_adc[WAVE_SAMPLE * WAVE_W];
  383. int ch0_wave[WAVE_W];
  384. int val;
  385. float text_bg_r;
  386. float text_bg_r_trg;
  387. } volt;
  388. //选择框变量
  389. //默认参数
  390. #define CHECK_BOX_L_S 95 //选择框在每行的左边距
  391. #define CHECK_BOX_U_S 2 //选择框在每行的上边距
  392. #define CHECK_BOX_F_W 12 //选择框外框宽度
  393. #define CHECK_BOX_F_H 12 //选择框外框高度
  394. #define CHECK_BOX_D_S 2 //选择框里面的点距离外框的边距
  395. /*
  396. //超窄行高度测试
  397. #define CHECK_BOX_L_S 99 //选择框在每行的左边距
  398. #define CHECK_BOX_U_S 0 //选择框在每行的上边距
  399. #define CHECK_BOX_F_W 5 //选择框外框宽度
  400. #define CHECK_BOX_F_H 5 //选择框外框高度
  401. #define CHECK_BOX_D_S 1 //选择框里面的点距离外框的边距
  402. */
  403. struct
  404. {
  405. uint8_t *v;
  406. uint8_t *m;
  407. uint8_t *s;
  408. uint8_t *s_p;
  409. } check_box;
  410. //弹窗变量
  411. #define WIN_FONT u8g2_font_HelvetiPixel_tr //弹窗字体
  412. #define WIN_H 32 //弹窗高度
  413. #define WIN_W 102 //弹窗宽度
  414. #define WIN_BAR_W 92 //弹窗进度条宽度
  415. #define WIN_BAR_H 7 //弹窗进度条高度
  416. #define WIN_Y - WIN_H - 2 //弹窗竖直方向出场起始位置
  417. #define WIN_Y_TRG - WIN_H - 2 //弹窗竖直方向退场终止位置
  418. struct
  419. {
  420. //uint8_t
  421. uint8_t *value;
  422. uint8_t max;
  423. uint8_t min;
  424. uint8_t step;
  425. MENU *bg;
  426. uint8_t index;
  427. char title[20];
  428. uint8_t select;
  429. uint8_t l = (DISP_W - WIN_W) / 2;
  430. uint8_t u = (DISP_H - WIN_H) / 2;
  431. float bar;
  432. float bar_trg;
  433. float y;
  434. float y_trg;
  435. } win;
  436. //聚光灯变量
  437. struct
  438. {
  439. float l;
  440. float l_trg;
  441. float r;
  442. float r_trg;
  443. float u;
  444. float u_trg;
  445. float d;
  446. float d_trg;
  447. } spot;
  448. /********************************** 自定义功能变量 **********************************/
  449. //旋钮功能变量
  450. #define KNOB_PARAM 4
  451. #define KNOB_DISABLE 0
  452. #define KNOB_ROT_VOL 1
  453. #define KNOB_ROT_BRI 2
  454. enum
  455. {
  456. KNOB_ROT, //睡眠下旋转旋钮的功能,0 禁用,1 音量,2 亮度
  457. KNOB_COD, //睡眠下短按旋钮输入的字符码,0 禁用
  458. KNOB_ROT_P, //旋转旋钮功能在单选框中选择的位置
  459. KNOB_COD_P, //字符码在单选框中选择的位置
  460. };
  461. struct
  462. {
  463. uint8_t param[KNOB_PARAM] = { KNOB_DISABLE, KNOB_DISABLE, 2, 2 }; //禁用在列表的第2个选项,第0个是标题,第1个是分界线
  464. } knob;
  465. /************************************* 断电保存 *************************************/
  466. //#include <EEPROM.h>
  467. //EEPROM变量
  468. #define EEPROM_CHECK 11
  469. struct
  470. {
  471. bool init;
  472. bool change;
  473. int address;
  474. uint8_t check;
  475. uint8_t check_param[EEPROM_CHECK] = { 'a', 'b', 'c', 'd', 'e', 'f','g', 'h', 'i', 'j', 'k' };
  476. } eeprom;
  477. //EEPROM写数据,回到睡眠时执行一遍
  478. void eeprom_write_all_data()
  479. {
  480. #if 0
  481. eeprom.address = 0;
  482. for (uint8_t i = 0; i < EEPROM_CHECK; ++i) EEPROM.write(eeprom.address + i, eeprom.check_param[i]); eeprom.address += EEPROM_CHECK;
  483. for (uint8_t i = 0; i < UI_PARAM; ++i) EEPROM.write(eeprom.address + i, ui.param[i]); eeprom.address += UI_PARAM;
  484. for (uint8_t i = 0; i < KNOB_PARAM; ++i) EEPROM.write(eeprom.address + i, knob.param[i]); eeprom.address += KNOB_PARAM;
  485. #else
  486. nvs_handle handle;
  487. int32_t err;
  488. // Open
  489. err = nvs_open(STORAGE_NAMESPACE, NVS_READWRITE, &handle);
  490. if (err != ESP_OK) goto err;
  491. err = nvs_set_blob(handle, "ui_param_ck", eeprom.check_param, EEPROM_CHECK);
  492. if (err != ESP_OK) goto err;
  493. err = nvs_set_blob(handle, "ui_param", ui.param, UI_PARAM);
  494. if (err != ESP_OK) goto err;
  495. err = nvs_set_blob(handle, "knob_param", knob.param, KNOB_PARAM);
  496. if (err != ESP_OK) goto err;
  497. ESP_LOGI(TAG, "param write all finish!");
  498. err = nvs_commit(handle);
  499. if (err != ESP_OK) goto err;
  500. err:
  501. // Close
  502. nvs_close(handle);
  503. #endif
  504. }
  505. //EEPROM读数据,开机初始化时执行一遍
  506. void eeprom_read_all_data()
  507. {
  508. #if 0
  509. eeprom.address = EEPROM_CHECK;
  510. for (uint8_t i = 0; i < UI_PARAM; ++i) ui.param[i] = EEPROM.read(eeprom.address + i); eeprom.address += UI_PARAM;
  511. for (uint8_t i = 0; i < KNOB_PARAM; ++i) knob.param[i] = EEPROM.read(eeprom.address + i); eeprom.address += KNOB_PARAM;
  512. #else
  513. nvs_handle handle;
  514. size_t length;
  515. int32_t err;
  516. // Open
  517. err = nvs_open(STORAGE_NAMESPACE, NVS_READWRITE, &handle);
  518. if (err != ESP_OK) goto err;
  519. err = nvs_get_blob(handle, "ui_param", NULL, &length);
  520. err = nvs_get_blob(handle, "ui_param", ui.param, &length);
  521. if (err != ESP_OK) goto err;
  522. err = nvs_get_blob(handle, "knob_param", NULL, &length);
  523. err = nvs_get_blob(handle, "knob_param", knob.param, &length);
  524. if (err != ESP_OK) goto err;
  525. ESP_LOGI(TAG, "param read all finish!");
  526. err:
  527. // Close
  528. nvs_close(handle);
  529. #endif
  530. }
  531. //开机检查是否已经修改过,没修改过则跳过读配置步骤,用默认设置
  532. void eeprom_init()
  533. {
  534. #if 0
  535. eeprom.check = 0;
  536. eeprom.address = 0; for (uint8_t i = 0; i < EEPROM_CHECK; ++i) if (EEPROM.read(eeprom.address + i) != eeprom.check_param[i]) eeprom.check ++;
  537. if (eeprom.check <= 1) eeprom_read_all_data(); //允许一位误码
  538. else ui_param_init();
  539. #else
  540. nvs_handle handle;
  541. size_t length;
  542. int32_t err;
  543. uint8_t check_param[EEPROM_CHECK] = {0};
  544. // Open
  545. err = nvs_open(STORAGE_NAMESPACE, NVS_READWRITE, &handle);
  546. if (err != ESP_OK) goto err;
  547. err = nvs_get_blob(handle, "ui_param_ck", NULL, &length);
  548. err = nvs_get_blob(handle, "ui_param_ck", check_param, &length);
  549. if (err != ESP_OK) {
  550. ui_param_init();
  551. goto err;
  552. }
  553. eeprom.check = 0;
  554. eeprom.address = 0; for (uint8_t i = 0; i < EEPROM_CHECK; ++i) if (check_param[i] != eeprom.check_param[i]) eeprom.check ++;
  555. if (eeprom.check <= 1) eeprom_read_all_data(); //允许一位误码
  556. else ui_param_init();
  557. ESP_LOGI(TAG, "ui param check pass!");
  558. err:
  559. // Close
  560. nvs_close(handle);
  561. #endif
  562. }
  563. /************************************* 旋钮相关 *************************************/
  564. //可按下旋钮引脚
  565. #define AIO PB12
  566. #define BIO PB13
  567. //#define SW PB14
  568. #define SW 34
  569. //按键ID
  570. #define BTN_ID_CC 0 //逆时针旋转
  571. #define BTN_ID_CW 1 //顺时针旋转
  572. #define BTN_ID_SP 2 //短按
  573. #define BTN_ID_LP 3 //长按
  574. //按键变量
  575. #define BTN_PARAM_TIMES 2 //由于uint8_t最大值可能不够,但它存储起来方便,这里放大两倍使用
  576. struct
  577. {
  578. uint8_t id;
  579. bool flag;
  580. bool pressed;
  581. bool CW_1;
  582. bool CW_2;
  583. bool val;
  584. bool val_last;
  585. bool alv;
  586. bool blv;
  587. long count;
  588. } volatile btn;
  589. void knob_inter()
  590. {
  591. #if 0
  592. btn.alv = digitalRead(AIO);
  593. btn.blv = digitalRead(BIO);
  594. if (!btn.flag && btn.alv == LOW)
  595. {
  596. btn.CW_1 = btn.blv;
  597. btn.flag = true;
  598. }
  599. if (btn.flag && btn.alv)
  600. {
  601. btn.CW_2 = !btn.blv;
  602. if (btn.CW_1 && btn.CW_2)
  603. {
  604. btn.id = ui.param[KNOB_DIR];
  605. btn.pressed = true;
  606. }
  607. if (btn.CW_1 == false && btn.CW_2 == false)
  608. {
  609. btn.id = !ui.param[KNOB_DIR];
  610. btn.pressed = true;
  611. }
  612. btn.flag = false;
  613. }
  614. #else
  615. if (btn.pressed == false) {
  616. if (motor_a.pos_spd_ltd.v2 > 60) {
  617. btn.id = !ui.param[KNOB_DIR];
  618. btn.pressed = true;
  619. } else if (motor_a.pos_spd_ltd.v2 < -60) {
  620. btn.id = ui.param[KNOB_DIR];
  621. btn.pressed = true;
  622. } else {
  623. btn.pressed = false;
  624. }
  625. }
  626. #endif
  627. }
  628. void btn_scan()
  629. {
  630. btn.val = digitalRead(SW);
  631. if (btn.val != btn.val_last)
  632. {
  633. btn.val_last = btn.val;
  634. delay(ui.param[BTN_SPT] * BTN_PARAM_TIMES);
  635. btn.val = digitalRead(SW);
  636. if (btn.val == LOW)
  637. {
  638. btn.pressed = true;
  639. btn.count = 0;
  640. while (!digitalRead(SW))
  641. {
  642. btn.count++;
  643. delay(1);
  644. }
  645. if (btn.count < ui.param[BTN_LPT] * BTN_PARAM_TIMES) btn.id = BTN_ID_SP;
  646. else btn.id = BTN_ID_LP;
  647. ESP_LOGI(TAG, "btn_scan:%d/%d=%d", btn.count, (ui.param[BTN_LPT] * BTN_PARAM_TIMES), btn.id);
  648. }
  649. }
  650. }
  651. void btn_init()
  652. {
  653. #if 0
  654. pinMode(AIO, INPUT);
  655. pinMode(BIO, INPUT);
  656. pinMode(SW, INPUT_PULLUP);
  657. attachInterrupt(digitalPinToInterrupt(AIO), knob_inter, CHANGE);
  658. #else
  659. #endif
  660. }
  661. /************************************ 初始化函数 ***********************************/
  662. /********************************* 初始化数据处理函数 *******************************/
  663. //显示数值的初始化
  664. void check_box_v_init(uint8_t *param)
  665. {
  666. check_box.v = param;
  667. }
  668. //多选框的初始化
  669. void check_box_m_init(uint8_t *param)
  670. {
  671. check_box.m = param;
  672. }
  673. //单选框时的初始化
  674. void check_box_s_init(uint8_t *param, uint8_t *param_p)
  675. {
  676. check_box.s = param;
  677. check_box.s_p = param_p;
  678. }
  679. //多选框处理函数
  680. void check_box_m_select(uint8_t param)
  681. {
  682. check_box.m[param] = !check_box.m[param];
  683. eeprom.change = true;
  684. }
  685. //单选框处理函数
  686. void check_box_s_select(uint8_t val, uint8_t pos)
  687. {
  688. *check_box.s = val;
  689. *check_box.s_p = pos;
  690. eeprom.change = true;
  691. }
  692. //弹窗数值初始化
  693. void window_value_init(char title[], uint8_t select, uint8_t *value, uint8_t max, uint8_t min, uint8_t step, MENU *bg, uint8_t index)
  694. {
  695. strcpy(win.title, title);
  696. win.select = select;
  697. win.value = value;
  698. win.max = max;
  699. win.min = min;
  700. win.step = step;
  701. win.bg = bg;
  702. win.index = index;
  703. ui.index = M_WINDOW;
  704. ui.state = S_WINDOW;
  705. }
  706. /*********************************** UI 初始化函数 *********************************/
  707. //在初始化EEPROM时,选择性初始化的默认设置
  708. void ui_param_init()
  709. {
  710. ui.param[DISP_BRI] = 255; //屏幕亮度
  711. ui.param[TILE_ANI] = 30; //磁贴动画速度
  712. ui.param[LIST_ANI] = 60; //列表动画速度
  713. ui.param[WIN_ANI] = 25; //弹窗动画速度
  714. ui.param[SPOT_ANI] = 50; //聚光动画速度
  715. ui.param[TAG_ANI] = 60; //标签动画速度
  716. ui.param[FADE_ANI] = 30; //消失动画速度
  717. ui.param[BTN_SPT] = 25; //按键短按时长
  718. ui.param[BTN_LPT] = 150; //按键长按时长
  719. ui.param[TILE_UFD] = 1; //磁贴图标从头展开开关
  720. ui.param[LIST_UFD] = 1; //菜单列表从头展开开关
  721. ui.param[TILE_LOOP] = 0; //磁贴图标循环模式开关
  722. ui.param[LIST_LOOP] = 0; //菜单列表循环模式开关
  723. ui.param[WIN_BOK] = 0; //弹窗背景虚化开关
  724. ui.param[KNOB_DIR] = 0; //旋钮方向切换开关
  725. ui.param[DARK_MODE] = 1; //黑暗模式开关
  726. }
  727. //列表类页面列表行数初始化,必须初始化的参数
  728. void ui_init()
  729. {
  730. ui.num[M_MAIN] = sizeof( main_menu ) / sizeof(M_SELECT);
  731. ui.num[M_EDITOR] = sizeof( editor_menu ) / sizeof(M_SELECT);
  732. ui.num[M_KNOB] = sizeof( knob_menu ) / sizeof(M_SELECT);
  733. ui.num[M_KRF] = sizeof( krf_menu ) / sizeof(M_SELECT);
  734. ui.num[M_KPF] = sizeof( kpf_menu ) / sizeof(M_SELECT);
  735. ui.num[M_VOLT] = sizeof( volt_menu ) / sizeof(M_SELECT);
  736. ui.num[M_SETTING] = sizeof( setting_menu ) / sizeof(M_SELECT);
  737. ui.num[M_ABOUT] = sizeof( about_menu ) / sizeof(M_SELECT);
  738. }
  739. /********************************* 分页面初始化函数 ********************************/
  740. //进入磁贴类时的初始化
  741. void tile_param_init()
  742. {
  743. ui.init = false;
  744. tile.icon_x = 0;
  745. tile.icon_x_trg = TILE_ICON_S;
  746. tile.icon_y = -TILE_ICON_H;
  747. tile.icon_y_trg = 0;
  748. tile.indi_x = 0;
  749. tile.indi_x_trg = TILE_INDI_W;
  750. tile.title_y = tile.title_y_calc;
  751. tile.title_y_trg = tile.title_y_trg_calc;
  752. }
  753. //进入睡眠时的初始化
  754. void sleep_param_init()
  755. {
  756. //#ifdef ARDUINO
  757. #if 0
  758. u8g2.setDrawColor(0);
  759. u8g2.drawBox(0, 0, DISP_W, DISP_H);
  760. u8g2.setPowerSave(1);
  761. #else
  762. u8g2_SetDrawColor(&u8g2, 0);
  763. u8g2_DrawBox(&u8g2, 0, 0, DISP_W, DISP_H);
  764. //u8g2_SetPowerSave(&u8g2, 1);
  765. #endif
  766. ui.state = S_NONE;
  767. ui.sleep = true;
  768. if (eeprom.change)
  769. {
  770. eeprom_write_all_data();
  771. eeprom.change = false;
  772. }
  773. }
  774. //旋钮设置页初始化
  775. void knob_param_init() { check_box_v_init(knob.param); }
  776. //旋钮旋转页初始化
  777. void krf_param_init() { check_box_s_init(&knob.param[KNOB_ROT], &knob.param[KNOB_ROT_P]); }
  778. //旋钮点按页初始化
  779. void kpf_param_init() { check_box_s_init(&knob.param[KNOB_COD], &knob.param[KNOB_COD_P]); }
  780. //电压测量页初始化
  781. void volt_param_init()
  782. {
  783. volt.text_bg_r = 0;
  784. volt.text_bg_r_trg = VOLT_TEXT_BG_W;
  785. }
  786. //设置页初始化
  787. void setting_param_init()
  788. {
  789. check_box_v_init(ui.param);
  790. check_box_m_init(ui.param);
  791. }
  792. /********************************** 通用初始化函数 *********************************/
  793. /*
  794. 页面层级管理逻辑是,把所有页面都先当作列表类初始化,不是列表类按需求再初始化对应函数
  795. 这样做会浪费一些资源,但跳转页面时只需要考虑页面层级,逻辑上更清晰,减少出错
  796. */
  797. //弹窗动画初始化
  798. void window_param_init()
  799. {
  800. win.bar = 0;
  801. win.y = WIN_Y;
  802. win.y_trg = win.u;
  803. ui.state = S_NONE;
  804. }
  805. //进入更深层级时的初始化
  806. void layer_init_in()
  807. {
  808. ui.layer ++;
  809. ui.init = 0;
  810. list.y = 0;
  811. list.y_trg = LIST_LINE_H;
  812. list.box_x = 0;
  813. list.box_y = 0;
  814. list.bar_y = 0;
  815. ui.state = S_FADE;
  816. switch (ui.index)
  817. {
  818. case M_MAIN: tile_param_init(); break; //睡眠进入主菜单,动画初始化
  819. case M_KNOB: knob_param_init(); break; //旋钮设置页,行末尾文字初始化
  820. case M_KRF: krf_param_init(); break; //旋钮旋转页,单选框初始化
  821. case M_KPF: kpf_param_init(); break; //旋钮点按页,单选框初始化
  822. case M_VOLT: volt_param_init(); break; //主菜单进入电压测量页,动画初始化
  823. case M_SETTING: setting_param_init(); break; //主菜单进入设置页,单选框初始化
  824. }
  825. }
  826. //进入更浅层级时的初始化
  827. void layer_init_out()
  828. {
  829. ui.select[ui.layer] = 0;
  830. list.box_y_trg[ui.layer] = 0;
  831. ui.layer --;
  832. ui.init = 0;
  833. list.y = 0;
  834. list.y_trg = LIST_LINE_H;
  835. list.bar_y = 0;
  836. ui.state = S_FADE;
  837. switch (ui.index)
  838. {
  839. case M_SLEEP: sleep_param_init(); break; //主菜单进入睡眠页,检查是否需要写EEPROM
  840. case M_MAIN: tile_param_init(); break; //不管什么页面进入主菜单时,动画初始化
  841. }
  842. }
  843. /************************************* 动画函数 *************************************/
  844. //动画函数
  845. void animation(float *a, float *a_trg, uint8_t n)
  846. {
  847. if (fabs(*a - *a_trg) < 0.15) *a = *a_trg;
  848. if (*a != *a_trg) *a += (*a_trg - *a) / (ui.param[n] / 10.0);
  849. }
  850. //消失函数
  851. void fade()
  852. {
  853. delay(ui.param[FADE_ANI]);
  854. if (ui.param[DARK_MODE])
  855. {
  856. switch (ui.fade)
  857. {
  858. case 1: for (uint16_t i = 0; i < buf_len; ++i) if (i % 2 != 0) buf_ptr[i] = buf_ptr[i] & 0xAA; break;
  859. case 2: for (uint16_t i = 0; i < buf_len; ++i) if (i % 2 != 0) buf_ptr[i] = buf_ptr[i] & 0x00; break;
  860. case 3: for (uint16_t i = 0; i < buf_len; ++i) if (i % 2 == 0) buf_ptr[i] = buf_ptr[i] & 0x55; break;
  861. case 4: for (uint16_t i = 0; i < buf_len; ++i) if (i % 2 == 0) buf_ptr[i] = buf_ptr[i] & 0x00; break;
  862. default: ui.state = S_NONE; ui.fade = 0; break;
  863. }
  864. }
  865. else
  866. {
  867. switch (ui.fade)
  868. {
  869. case 1: for (uint16_t i = 0; i < buf_len; ++i) if (i % 2 != 0) buf_ptr[i] = buf_ptr[i] | 0xAA; break;
  870. case 2: for (uint16_t i = 0; i < buf_len; ++i) if (i % 2 != 0) buf_ptr[i] = buf_ptr[i] | 0x00; break;
  871. case 3: for (uint16_t i = 0; i < buf_len; ++i) if (i % 2 == 0) buf_ptr[i] = buf_ptr[i] | 0x55; break;
  872. case 4: for (uint16_t i = 0; i < buf_len; ++i) if (i % 2 == 0) buf_ptr[i] = buf_ptr[i] | 0x00; break;
  873. default: ui.state = S_NONE; ui.fade = 0; break;
  874. }
  875. }
  876. ui.fade++;
  877. }
  878. /************************************* 显示函数 *************************************/
  879. //磁贴类页面通用显示函数
  880. void tile_show(struct MENU arr_1[], const uint8_t icon_pic[][120])
  881. {
  882. //计算动画过渡值
  883. animation(&tile.icon_x, &tile.icon_x_trg, TILE_ANI);
  884. animation(&tile.icon_y, &tile.icon_y_trg, TILE_ANI);
  885. animation(&tile.indi_x, &tile.indi_x_trg, TILE_ANI);
  886. animation(&tile.title_y, &tile.title_y_trg, TILE_ANI);
  887. //设置大标题的颜色,0透显,1实显,2反色,这里用实显
  888. //u8g2.setDrawColor(1);
  889. u8g2_SetDrawColor(&u8g2, 1);
  890. //绘制大标题
  891. //u8g2.setFont(TILE_B_FONT);
  892. //u8g2.drawStr(((DISP_W - TILE_INDI_W) - u8g2.getStrWidth(arr_1[ui.select[ui.layer]].m_select)) / 2 + TILE_INDI_W, tile.title_y, arr_1[ui.select[ui.layer]].m_select);
  893. u8g2_SetFont(&u8g2, TILE_B_FONT);
  894. u8g2_DrawStr(&u8g2, ((DISP_W - TILE_INDI_W) - u8g2_GetStrWidth(&u8g2, arr_1[ui.select[ui.layer]].m_select)) / 2 + TILE_INDI_W, tile.title_y, arr_1[ui.select[ui.layer]].m_select);
  895. //绘制大标题指示器
  896. //u8g2.drawBox(0, TILE_ICON_S, tile.indi_x, TILE_INDI_H);
  897. u8g2_DrawBox(&u8g2, 0, TILE_ICON_S, tile.indi_x, TILE_INDI_H);
  898. //绘制图标
  899. if (!ui.init)
  900. {
  901. for (uint8_t i = 0; i < ui.num[ui.index]; ++i)
  902. {
  903. if (ui.param[TILE_UFD]) tile.temp = (DISP_W - TILE_ICON_W) / 2 + i * tile.icon_x - TILE_ICON_S * ui.select[ui.layer];
  904. else tile.temp = (DISP_W - TILE_ICON_W) / 2 + (i - ui.select[ui.layer]) * tile.icon_x;
  905. //u8g2.drawXBMP(tile.temp, (int16_t)tile.icon_y, TILE_ICON_W, TILE_ICON_H, icon_pic[i]);
  906. u8g2_DrawXBMP(&u8g2, tile.temp, (int16_t)tile.icon_y, TILE_ICON_W, TILE_ICON_H, icon_pic[i]);
  907. }
  908. if (tile.icon_x == tile.icon_x_trg)
  909. {
  910. ui.init = true;
  911. tile.icon_x = tile.icon_x_trg = - ui.select[ui.layer] * TILE_ICON_S;
  912. }
  913. }
  914. else for (uint8_t i = 0; i < ui.num[ui.index]; ++i)
  915. //u8g2.drawXBMP((DISP_W - TILE_ICON_W) / 2 + (int16_t)tile.icon_x + i * TILE_ICON_S, 0, TILE_ICON_W, TILE_ICON_H, icon_pic[i]);
  916. u8g2_DrawXBMP(&u8g2, (DISP_W - TILE_ICON_W) / 2 + (int16_t)tile.icon_x + i * TILE_ICON_S, 0, TILE_ICON_W, TILE_ICON_H, icon_pic[i]);
  917. //反转屏幕内元素颜色,白天模式遮罩
  918. //u8g2.setDrawColor(2);
  919. u8g2_SetDrawColor(&u8g2, 2);
  920. if (!ui.param[DARK_MODE])
  921. //u8g2.drawBox(0, 0, DISP_W, DISP_H);
  922. u8g2_DrawBox(&u8g2, 0, 0, DISP_W, DISP_H);
  923. }
  924. static u8g2_uint_t u8g2_cursor_x = 0;
  925. static u8g2_uint_t u8g2_cursor_y = 0;
  926. static void u8g2_setCursor(u8g2_uint_t x, u8g2_uint_t y) {
  927. u8g2_cursor_x = x;
  928. u8g2_cursor_y = y;
  929. }
  930. static u8g2_uint_t u8g2_print(const char *str) {
  931. u8g2_uint_t delta = u8g2_DrawStr(&u8g2, u8g2_cursor_x, u8g2_cursor_y, str);
  932. u8g2_cursor_x += delta;
  933. if (u8g2_cursor_x > DISP_W) u8g2_cursor_x=0;
  934. return delta;
  935. }
  936. #define DEC 10
  937. #define HEX 16
  938. #define OCT 8
  939. #define BIN 2
  940. static u8g2_uint_t printNumber(unsigned long n, uint8_t base)
  941. {
  942. char buf[8 * sizeof(n) + 1]; // Assumes 8-bit chars plus zero byte.
  943. char *str = &buf[sizeof(buf) - 1];
  944. *str = '\0';
  945. // prevent crash if called with base == 1
  946. if(base < 2) {
  947. base = 10;
  948. }
  949. do {
  950. char c = n % base;
  951. n /= base;
  952. *--str = c < 10 ? c + '0' : c + 'A' - 10;
  953. } while (n);
  954. return u8g2_print(str);
  955. }
  956. static u8g2_uint_t printNumber(long n, uint8_t base)
  957. {
  958. int t = 0;
  959. if (base == 10 && n < 0) {
  960. t = u8g2_print("-");
  961. n = -n;
  962. }
  963. return printNumber(static_cast<unsigned long>(n), base);
  964. }
  965. static u8g2_uint_t u8g2_print(char value) {
  966. return printNumber((long)value, DEC);
  967. }
  968. static u8g2_uint_t u8g2_print(unsigned char value) {
  969. return printNumber((unsigned long)value, DEC);
  970. }
  971. static u8g2_uint_t u8g2_print(int value) {
  972. return printNumber((long)value, DEC);
  973. }
  974. static u8g2_uint_t u8g2_print(unsigned int value) {
  975. return printNumber((unsigned long)value, DEC);
  976. }
  977. static u8g2_uint_t u8g2_print(long value) {
  978. return printNumber(value, DEC);
  979. }
  980. static u8g2_uint_t u8g2_print(unsigned long value) {
  981. return printNumber(value, DEC);
  982. }
  983. static u8g2_uint_t printFloat(double number, uint8_t digits) {
  984. u8g2_uint_t n = 0;
  985. if(isnan(number)) {
  986. return u8g2_print("nan");
  987. }
  988. if(isinf(number)) {
  989. return u8g2_print("inf");
  990. }
  991. if(number > 4294967040.0) {
  992. return u8g2_print("ovf"); // constant determined empirically
  993. }
  994. if(number < -4294967040.0) {
  995. return u8g2_print("ovf"); // constant determined empirically
  996. }
  997. // Handle negative numbers
  998. if(number < 0.0) {
  999. n += u8g2_print("-");
  1000. number = -number;
  1001. }
  1002. // Round correctly so that print(1.999, 2) prints as "2.00"
  1003. double rounding = 0.5;
  1004. for(uint8_t i = 0; i < digits; ++i) {
  1005. rounding /= 10.0;
  1006. }
  1007. number += rounding;
  1008. // Extract the integer part of the number and print it
  1009. unsigned long int_part = (unsigned long) number;
  1010. double remainder = number - (double) int_part;
  1011. n += u8g2_print(int_part);
  1012. // Print the decimal point, but only if there are digits beyond
  1013. if(digits > 0) {
  1014. n += u8g2_print(".");
  1015. }
  1016. // Extract digits from the remainder one at a time
  1017. while(digits-- > 0) {
  1018. remainder *= 10.0;
  1019. int toPrint = int(remainder);
  1020. n += u8g2_print(toPrint);
  1021. remainder -= toPrint;
  1022. }
  1023. return n;
  1024. }
  1025. static u8g2_uint_t u8g2_print(double value) {
  1026. return printFloat(value, 3);
  1027. }
  1028. /*************** 根据列表每行开头符号,判断每行尾部是否绘制以及绘制什么内容 *************/
  1029. //列表显示数值
  1030. void list_draw_value(int n) {
  1031. //u8g2.print(check_box.v[n - 1]);
  1032. u8g2_print(check_box.v[n - 1]);
  1033. }
  1034. //绘制外框
  1035. void list_draw_check_box_frame() {
  1036. //u8g2.drawRFrame(CHECK_BOX_L_S, list.temp + CHECK_BOX_U_S, CHECK_BOX_F_W, CHECK_BOX_F_H, 1);
  1037. u8g2_DrawRFrame(&u8g2, CHECK_BOX_L_S, list.temp + CHECK_BOX_U_S, CHECK_BOX_F_W, CHECK_BOX_F_H, 1);
  1038. }
  1039. //绘制框里面的点
  1040. void list_draw_check_box_dot() {
  1041. //u8g2.drawBox(CHECK_BOX_L_S + CHECK_BOX_D_S + 1, list.temp + CHECK_BOX_U_S + CHECK_BOX_D_S + 1, CHECK_BOX_F_W - (CHECK_BOX_D_S + 1) * 2, CHECK_BOX_F_H - (CHECK_BOX_D_S + 1) * 2);
  1042. u8g2_DrawBox(&u8g2, CHECK_BOX_L_S + CHECK_BOX_D_S + 1, list.temp + CHECK_BOX_U_S + CHECK_BOX_D_S + 1, CHECK_BOX_F_W - (CHECK_BOX_D_S + 1) * 2, CHECK_BOX_F_H - (CHECK_BOX_D_S + 1) * 2);
  1043. }
  1044. //列表显示旋钮功能
  1045. void list_draw_krf(int n)
  1046. {
  1047. switch (check_box.v[n - 1])
  1048. {
  1049. case 0: u8g2_print("OFF"); break;
  1050. case 1: u8g2_print("VOL"); break;
  1051. case 2: u8g2_print("BRI"); break;
  1052. }
  1053. }
  1054. //列表显示按键键值
  1055. void list_draw_kpf(int n)
  1056. {
  1057. if (check_box.v[n - 1] == 0) u8g2_print("OFF");
  1058. else if (check_box.v[n - 1] <= 90) u8g2_print((char)check_box.v[n - 1]);
  1059. else u8g2_print("?");
  1060. }
  1061. //判断列表尾部内容
  1062. void list_draw_text_and_check_box(struct MENU arr[], int i)
  1063. {
  1064. //u8g2.drawStr(LIST_TEXT_S, list.temp + LIST_TEXT_H + LIST_TEXT_S, arr[i].m_select);
  1065. //u8g2.setCursor(CHECK_BOX_L_S, list.temp + LIST_TEXT_H + LIST_TEXT_S);
  1066. u8g2_DrawStr(&u8g2, LIST_TEXT_S, list.temp + LIST_TEXT_H + LIST_TEXT_S, arr[i].m_select);
  1067. u8g2_setCursor(CHECK_BOX_L_S, list.temp + LIST_TEXT_H + LIST_TEXT_S);
  1068. switch (arr[i].m_select[0])
  1069. {
  1070. case '~': list_draw_value(i); break;
  1071. case '+': list_draw_check_box_frame(); if (check_box.m[i - 1] == 1) list_draw_check_box_dot(); break;
  1072. case '=': list_draw_check_box_frame(); if (*check_box.s_p == i) list_draw_check_box_dot(); break;
  1073. case '#': list_draw_krf(i); break;
  1074. case '$': list_draw_kpf(i); break;
  1075. }
  1076. }
  1077. /******************************** 列表显示函数 **************************************/
  1078. //列表类页面通用显示函数
  1079. void list_show(struct MENU arr[], uint8_t ui_index)
  1080. {
  1081. //更新动画目标值
  1082. //u8g2.setFont(LIST_FONT);
  1083. u8g2_SetFont(&u8g2, LIST_FONT);
  1084. //list.box_x_trg = u8g2.getStrWidth(arr[ui.select[ui.layer]].m_select) + LIST_TEXT_S * 2;
  1085. list.box_x_trg = u8g2_GetStrWidth(&u8g2, arr[ui.select[ui.layer]].m_select) + LIST_TEXT_S * 2;
  1086. list.bar_y_trg = ceil((ui.select[ui.layer]) * ((float)DISP_H / (ui.num[ui_index] - 1)));
  1087. //计算动画过渡值
  1088. animation(&list.y, &list.y_trg, LIST_ANI);
  1089. animation(&list.box_x, &list.box_x_trg, LIST_ANI);
  1090. animation(&list.box_y, &list.box_y_trg[ui.layer], LIST_ANI);
  1091. animation(&list.bar_y, &list.bar_y_trg, LIST_ANI);
  1092. //检查循环动画是否结束
  1093. if (list.loop && list.box_y == list.box_y_trg[ui.layer]) list.loop = false;
  1094. //设置文字和进度条颜色,0透显,1实显,2反色,这里都用实显
  1095. //u8g2.setDrawColor(1);
  1096. u8g2_SetDrawColor(&u8g2, 1);
  1097. //绘制进度条
  1098. #if 0
  1099. u8g2.drawHLine(DISP_W - LIST_BAR_W, 0, LIST_BAR_W);
  1100. u8g2.drawHLine(DISP_W - LIST_BAR_W, DISP_H - 1, LIST_BAR_W);
  1101. u8g2.drawVLine(DISP_W - ceil((float)LIST_BAR_W / 2), 0, DISP_H);
  1102. u8g2.drawBox(DISP_W - LIST_BAR_W, 0, LIST_BAR_W, list.bar_y);
  1103. #else
  1104. u8g2_DrawHLine(&u8g2, DISP_W - LIST_BAR_W, 0, LIST_BAR_W);
  1105. u8g2_DrawHLine(&u8g2, DISP_W - LIST_BAR_W, DISP_H - 1, LIST_BAR_W);
  1106. u8g2_DrawVLine(&u8g2, DISP_W - ceil((float)LIST_BAR_W / 2), 0, DISP_H);
  1107. u8g2_DrawBox(&u8g2, DISP_W - LIST_BAR_W, 0, LIST_BAR_W, list.bar_y);
  1108. #endif
  1109. //绘制列表文字
  1110. if (!ui.init)
  1111. {
  1112. for (int i = 0; i < ui.num[ui_index]; ++ i)
  1113. {
  1114. if (ui.param[LIST_UFD]) list.temp = i * list.y - LIST_LINE_H * ui.select[ui.layer] + list.box_y_trg[ui.layer];
  1115. else list.temp = (i - ui.select[ui.layer]) * list.y + list.box_y_trg[ui.layer];
  1116. list_draw_text_and_check_box(arr, i);
  1117. }
  1118. if (list.y == list.y_trg)
  1119. {
  1120. ui.init = true;
  1121. list.y = list.y_trg = - LIST_LINE_H * ui.select[ui.layer] + list.box_y_trg[ui.layer];
  1122. }
  1123. }
  1124. else for (int i = 0; i < ui.num[ui_index]; ++ i)
  1125. {
  1126. list.temp = LIST_LINE_H * i + list.y;
  1127. list_draw_text_and_check_box(arr, i);
  1128. }
  1129. //绘制文字选择框,0透显,1实显,2反色,这里用反色
  1130. //u8g2.setDrawColor(2);
  1131. //u8g2.drawRBox(0, list.box_y, list.box_x, LIST_LINE_H, LIST_BOX_R);
  1132. u8g2_SetDrawColor(&u8g2, 2);
  1133. u8g2_DrawRBox(&u8g2, 0, list.box_y, list.box_x, LIST_LINE_H, LIST_BOX_R);
  1134. //反转屏幕内元素颜色,白天模式遮罩,在这里屏蔽有列表参与的页面,使遮罩作用在那个页面上
  1135. if (!ui.param[DARK_MODE])
  1136. {
  1137. //u8g2.drawBox(0, 0, DISP_W, DISP_H);
  1138. u8g2_DrawBox(&u8g2, 0, 0, DISP_W, DISP_H);
  1139. switch(ui.index)
  1140. {
  1141. case M_WINDOW:
  1142. case M_VOLT:
  1143. //u8g2.drawBox(0, 0, DISP_W, DISP_H);
  1144. u8g2_DrawBox(&u8g2, 0, 0, DISP_W, DISP_H);
  1145. }
  1146. }
  1147. }
  1148. //电压页面显示函数
  1149. void volt_show()
  1150. {
  1151. //使用列表类显示选项
  1152. list_show(volt_menu, M_VOLT);
  1153. //计算动画过渡值
  1154. animation(&volt.text_bg_r, &volt.text_bg_r_trg, TAG_ANI);
  1155. //设置曲线颜色,0透显,1实显,2反色,这里用实显
  1156. //u8g2.setDrawColor(1);
  1157. u8g2_SetDrawColor(&u8g2, 1);
  1158. //绘制电压曲线和外框
  1159. volt.val = 0;
  1160. //u8g2.drawFrame(WAVE_BOX_L_S, 0, WAVE_BOX_W, WAVE_BOX_H);
  1161. //u8g2.drawFrame(WAVE_BOX_L_S + 1, 1, WAVE_BOX_W - 2, WAVE_BOX_H - 2);
  1162. u8g2_DrawFrame(&u8g2, WAVE_BOX_L_S, 0, WAVE_BOX_W, WAVE_BOX_H);
  1163. u8g2_DrawFrame(&u8g2, WAVE_BOX_L_S + 1, 1, WAVE_BOX_W - 2, WAVE_BOX_H - 2);
  1164. if (list.box_y == list.box_y_trg[ui.layer] && list.y == list.y_trg)
  1165. {
  1166. for (int i = 0; i < WAVE_SAMPLE * WAVE_W; i++) volt.ch0_adc[i] = volt.val = (int)(motor_b.pos_spd_ltd.v2);//(int)(adc_data.voltage_mv);//analogRead(analog_pin[ui.select[ui.layer]]);
  1167. for (int i = 1; i < WAVE_W - 1; i++)
  1168. {
  1169. //volt.ch0_wave[i] = lv_map(volt.ch0_adc[int(5 * i)], 0, 4095, WAVE_MAX, WAVE_MIN);
  1170. //u8g2.drawLine(WAVE_L + i - 1, WAVE_U + volt.ch0_wave[i - 1], WAVE_L + i, WAVE_U + volt.ch0_wave[i]);
  1171. volt.ch0_wave[i] = lv_map(volt.ch0_adc[int(5 * i)], -5000, 5000, WAVE_MIN, WAVE_MAX);
  1172. u8g2_DrawLine(&u8g2, WAVE_L + i - 1, WAVE_U + volt.ch0_wave[i - 1], WAVE_L + i, WAVE_U + volt.ch0_wave[i]);
  1173. }
  1174. }
  1175. #if 0
  1176. //绘制电压值
  1177. u8g2.setFontDirection(0);
  1178. u8g2.setFont(VOLT_FONT);
  1179. u8g2.setCursor(39, DISP_H - 6);
  1180. u8g2.print(volt.val / 4096.0 * 3.3);
  1181. u8g2.print("V");
  1182. //绘制列表选择框和电压文字背景
  1183. u8g2.setDrawColor(2);
  1184. u8g2.drawBox(VOLT_TEXT_BG_L_S, DISP_H - VOLT_TEXT_BG_H, volt.text_bg_r, VOLT_TEXT_BG_H);
  1185. //反转屏幕内元素颜色,白天模式遮罩
  1186. if (!ui.param[DARK_MODE]) u8g2.drawBox(0, 0, DISP_W, DISP_H);
  1187. #else
  1188. //绘制电压值
  1189. u8g2_SetFontDirection(&u8g2, 0);
  1190. u8g2_SetFont(&u8g2, VOLT_FONT);
  1191. u8g2_setCursor(32, DISP_H - 6);
  1192. //u8g2_print(volt.val / 4096.0 * 3.3);
  1193. //u8g2_print(volt.val/1000.0);
  1194. //u8g2_print(motor_b.pos_spd_ltd.v2/1000.0);
  1195. u8g2_print(volt.val);
  1196. //u8g2_SetFont(&u8g2, LIST_FONT);
  1197. u8g2_print("V");
  1198. //绘制列表选择框和电压文字背景
  1199. u8g2_SetDrawColor(&u8g2, 2);
  1200. u8g2_DrawBox(&u8g2, VOLT_TEXT_BG_L_S, DISP_H - VOLT_TEXT_BG_H, volt.text_bg_r, VOLT_TEXT_BG_H);
  1201. //反转屏幕内元素颜色,白天模式遮罩
  1202. if (!ui.param[DARK_MODE]) u8g2_DrawBox(&u8g2, 0, 0, DISP_W, DISP_H);
  1203. #endif
  1204. }
  1205. //弹窗通用显示函数
  1206. void window_show()
  1207. {
  1208. //绘制背景列表,根据开关判断背景是否要虚化
  1209. list_show(win.bg, win.index);
  1210. if (ui.param[WIN_BOK]) for (uint16_t i = 0; i < buf_len; ++i) buf_ptr[i] = buf_ptr[i] & (i % 2 == 0 ? 0x55 : 0xAA);
  1211. //更新动画目标值
  1212. //u8g2.setFont(WIN_FONT);
  1213. u8g2_SetFont(&u8g2, WIN_FONT);
  1214. win.bar_trg = (float)(*win.value - win.min) / (float)(win.max - win.min) * (WIN_BAR_W - 4);
  1215. //计算动画过渡值
  1216. animation(&win.bar, &win.bar_trg, WIN_ANI);
  1217. animation(&win.y, &win.y_trg, WIN_ANI);
  1218. #if 0
  1219. //绘制窗口
  1220. u8g2.setDrawColor(0); u8g2.drawRBox(win.l, (int16_t)win.y, WIN_W, WIN_H, 2); //绘制外框背景
  1221. u8g2.setDrawColor(1); u8g2.drawRFrame(win.l, (int16_t)win.y, WIN_W, WIN_H, 2); //绘制外框描边
  1222. u8g2.drawRFrame(win.l + 5, (int16_t)win.y + 20, WIN_BAR_W, WIN_BAR_H, 1); //绘制进度条外框
  1223. u8g2.drawBox(win.l + 7, (int16_t)win.y + 22, win.bar, WIN_BAR_H - 4); //绘制进度条
  1224. u8g2.setCursor(win.l + 5, (int16_t)win.y + 14); u8g2.print(win.title); //绘制标题
  1225. u8g2.setCursor(win.l + 78, (int16_t)win.y + 14); u8g2.print(*win.value); //绘制当前值
  1226. //需要在窗口修改参数时立即见效的函数
  1227. if (!strcmp(win.title, "Disp Bri")) u8g2.setContrast(ui.param[DISP_BRI]);
  1228. //反转屏幕内元素颜色,白天模式遮罩
  1229. u8g2.setDrawColor(2);
  1230. if (!ui.param[DARK_MODE]) u8g2.drawBox(0, 0, DISP_W, DISP_H);
  1231. #else
  1232. //绘制窗口
  1233. u8g2_SetDrawColor(&u8g2, 0); u8g2_DrawRBox(&u8g2, win.l, (int16_t)win.y, WIN_W, WIN_H, 2); //绘制外框背景
  1234. u8g2_SetDrawColor(&u8g2, 1); u8g2_DrawRFrame(&u8g2, win.l, (int16_t)win.y, WIN_W, WIN_H, 2); //绘制外框描边
  1235. u8g2_DrawRFrame(&u8g2, win.l + 5, (int16_t)win.y + 20, WIN_BAR_W, WIN_BAR_H, 1); //绘制进度条外框
  1236. u8g2_DrawBox(&u8g2, win.l + 7, (int16_t)win.y + 22, win.bar, WIN_BAR_H - 4); //绘制进度条
  1237. u8g2_setCursor(win.l + 5, (int16_t)win.y + 14); u8g2_print(win.title); //绘制标题
  1238. u8g2_setCursor(win.l + 78, (int16_t)win.y + 14); u8g2_print(*win.value); //绘制当前值
  1239. //需要在窗口修改参数时立即见效的函数
  1240. //if (!strcmp(win.title, "Disp Bri")) u8g2_SetContrast(&u8g2, ui.param[DISP_BRI]);
  1241. //反转屏幕内元素颜色,白天模式遮罩
  1242. u8g2_SetDrawColor(&u8g2, 2);
  1243. if (!ui.param[DARK_MODE]) u8g2_DrawBox(&u8g2, 0, 0, DISP_W, DISP_H);
  1244. #endif
  1245. }
  1246. /************************************* 处理函数 *************************************/
  1247. /*********************************** 通用处理函数 ***********************************/
  1248. //磁贴类页面旋转时判断通用函数
  1249. void tile_rotate_switch()
  1250. {
  1251. switch (btn.id)
  1252. {
  1253. case BTN_ID_CC:
  1254. if (ui.init)
  1255. {
  1256. if (ui.select[ui.layer] > 0)
  1257. {
  1258. ui.select[ui.layer] -= 1;
  1259. tile.icon_x_trg += TILE_ICON_S;
  1260. tile.select_flag = false;
  1261. }
  1262. else
  1263. {
  1264. if (ui.param[TILE_LOOP])
  1265. {
  1266. ui.select[ui.layer] = ui.num[ui.index] - 1;
  1267. tile.icon_x_trg = - TILE_ICON_S * (ui.num[ui.index] - 1);
  1268. break;
  1269. }
  1270. else tile.select_flag = true;
  1271. }
  1272. }
  1273. break;
  1274. case BTN_ID_CW:
  1275. if (ui.init)
  1276. {
  1277. if (ui.select[ui.layer] < (ui.num[ui.index] - 1))
  1278. {
  1279. ui.select[ui.layer] += 1;
  1280. tile.icon_x_trg -= TILE_ICON_S;
  1281. tile.select_flag = false;
  1282. }
  1283. else
  1284. {
  1285. if (ui.param[TILE_LOOP])
  1286. {
  1287. ui.select[ui.layer] = 0;
  1288. tile.icon_x_trg = 0;
  1289. break;
  1290. }
  1291. else tile.select_flag = true;
  1292. }
  1293. }
  1294. break;
  1295. }
  1296. }
  1297. //列表类页面旋转时判断通用函数
  1298. void list_rotate_switch()
  1299. {
  1300. if (!list.loop)
  1301. {
  1302. switch (btn.id)
  1303. {
  1304. case BTN_ID_CC:
  1305. if (ui.select[ui.layer] == 0)
  1306. {
  1307. if (ui.param[LIST_LOOP] && ui.init)
  1308. {
  1309. list.loop = true;
  1310. ui.select[ui.layer] = ui.num[ui.index] - 1;
  1311. if (ui.num[ui.index] > list.line_n)
  1312. {
  1313. list.box_y_trg[ui.layer] = DISP_H - LIST_LINE_H;
  1314. list.y_trg = DISP_H - ui.num[ui.index] * LIST_LINE_H;
  1315. }
  1316. else list.box_y_trg[ui.layer] = (ui.num[ui.index] - 1) * LIST_LINE_H;
  1317. break;
  1318. }
  1319. else break;
  1320. }
  1321. if (ui.init)
  1322. {
  1323. ui.select[ui.layer] -= 1;
  1324. if (ui.select[ui.layer] < - (list.y_trg / LIST_LINE_H))
  1325. {
  1326. if (!(DISP_H % LIST_LINE_H)) list.y_trg += LIST_LINE_H;
  1327. else
  1328. {
  1329. if (list.box_y_trg[ui.layer] == DISP_H - LIST_LINE_H * list.line_n)
  1330. {
  1331. list.y_trg += (list.line_n + 1) * LIST_LINE_H - DISP_H;
  1332. list.box_y_trg[ui.layer] = 0;
  1333. }
  1334. else if (list.box_y_trg[ui.layer] == LIST_LINE_H)
  1335. {
  1336. list.box_y_trg[ui.layer] = 0;
  1337. }
  1338. else list.y_trg += LIST_LINE_H;
  1339. }
  1340. }
  1341. else list.box_y_trg[ui.layer] -= LIST_LINE_H;
  1342. break;
  1343. }
  1344. case BTN_ID_CW:
  1345. if (ui.select[ui.layer] == (ui.num[ui.index] - 1))
  1346. {
  1347. if (ui.param[LIST_LOOP] && ui.init)
  1348. {
  1349. list.loop = true;
  1350. ui.select[ui.layer] = 0;
  1351. list.y_trg = 0;
  1352. list.box_y_trg[ui.layer] = 0;
  1353. break;
  1354. }
  1355. else break;
  1356. }
  1357. if (ui.init)
  1358. {
  1359. ui.select[ui.layer] += 1;
  1360. if ((ui.select[ui.layer] + 1) > (list.line_n - list.y_trg / LIST_LINE_H))
  1361. {
  1362. if (!(DISP_H % LIST_LINE_H)) list.y_trg -= LIST_LINE_H;
  1363. else
  1364. {
  1365. if (list.box_y_trg[ui.layer] == LIST_LINE_H * (list.line_n - 1))
  1366. {
  1367. list.y_trg -= (list.line_n + 1) * LIST_LINE_H - DISP_H;
  1368. list.box_y_trg[ui.layer] = DISP_H - LIST_LINE_H;
  1369. }
  1370. else if (list.box_y_trg[ui.layer] == DISP_H - LIST_LINE_H * 2)
  1371. {
  1372. list.box_y_trg[ui.layer] = DISP_H - LIST_LINE_H;
  1373. }
  1374. else list.y_trg -= LIST_LINE_H;
  1375. }
  1376. }
  1377. else list.box_y_trg[ui.layer] += LIST_LINE_H;
  1378. break;
  1379. }
  1380. break;
  1381. }
  1382. }
  1383. }
  1384. //弹窗通用处理函数
  1385. void window_proc()
  1386. {
  1387. window_show();
  1388. if (win.y == WIN_Y_TRG) ui.index = win.index;
  1389. if (btn.pressed && win.y == win.y_trg && win.y != WIN_Y_TRG)
  1390. {
  1391. btn.pressed = false;
  1392. switch (btn.id)
  1393. {
  1394. case BTN_ID_CC: if (*win.value < win.max) *win.value += win.step; eeprom.change = true; break;
  1395. case BTN_ID_CW: if (*win.value > win.min) *win.value -= win.step; eeprom.change = true; break;
  1396. case BTN_ID_SP: case BTN_ID_LP: win.y_trg = WIN_Y_TRG; break;
  1397. }
  1398. }
  1399. }
  1400. /********************************** 分页面处理函数 **********************************/
  1401. //睡眠页面处理函数
  1402. void sleep_proc()
  1403. {
  1404. while (ui.sleep)
  1405. {
  1406. //睡眠时循环执行的函数
  1407. //睡眠时需要扫描旋钮才能退出睡眠
  1408. btn_scan();
  1409. delay(1);
  1410. //当旋钮有动作时
  1411. if (btn.pressed) {
  1412. btn.pressed = false;
  1413. switch (btn.id) {
  1414. #if 0
  1415. //睡眠时顺时针旋转执行的函数
  1416. case BTN_ID_CW:
  1417. switch (knob.param[KNOB_ROT])
  1418. {
  1419. case KNOB_ROT_VOL: Consumer.press(HIDConsumer::VOLUME_UP); Consumer.release(); break;
  1420. case KNOB_ROT_BRI: Consumer.press(HIDConsumer::BRIGHTNESS_UP); Consumer.release(); break;
  1421. }
  1422. break;
  1423. //睡眠时逆时针旋转执行的函数
  1424. case BTN_ID_CC:
  1425. switch (knob.param[KNOB_ROT])
  1426. {
  1427. case KNOB_ROT_VOL: Consumer.press(HIDConsumer::VOLUME_DOWN); Consumer.release(); break;
  1428. case KNOB_ROT_BRI: Consumer.press(HIDConsumer::BRIGHTNESS_DOWN); Consumer.release(); break;
  1429. }
  1430. break;
  1431. //睡眠时短按执行的函数
  1432. case BTN_ID_SP: Keyboard.press(knob.param[KNOB_COD]); Keyboard.release(knob.param[KNOB_COD]); break;
  1433. #endif
  1434. //睡眠时长按执行的函数
  1435. //case BTN_ID_LP: ui.index = M_MAIN; ui.state = S_LAYER_IN; u8g2.setPowerSave(0); ui.sleep = false; break;
  1436. case BTN_ID_LP: ui.index = M_MAIN; ui.state = S_LAYER_IN; /*u8g2_SetPowerSave(&u8g2, 0);*/ ui.sleep = false; break;
  1437. }
  1438. }
  1439. }
  1440. }
  1441. //主菜单处理函数,磁贴类模板
  1442. void main_proc()
  1443. {
  1444. tile_show(main_menu, main_icon_pic);
  1445. if (btn.pressed) { btn.pressed = false; switch (btn.id) { case BTN_ID_CW: case BTN_ID_CC: tile_rotate_switch(); break; case BTN_ID_SP: switch (ui.select[ui.layer]) {
  1446. case 0: ui.index = M_SLEEP; ui.state = S_LAYER_OUT; break;
  1447. case 1: ui.index = M_EDITOR; ui.state = S_LAYER_IN; break;
  1448. case 2: ui.index = M_VOLT; ui.state = S_LAYER_IN; break;
  1449. case 3: ui.index = M_SETTING; ui.state = S_LAYER_IN; break;
  1450. }
  1451. }
  1452. if (!tile.select_flag && ui.init) { tile.indi_x = 0; tile.title_y = tile.title_y_calc; }
  1453. }
  1454. }
  1455. //编辑器菜单处理函数
  1456. void editor_proc()
  1457. {
  1458. list_show(editor_menu, M_EDITOR);
  1459. if (btn.pressed) { btn.pressed = false; switch (btn.id) { case BTN_ID_CW: case BTN_ID_CC: list_rotate_switch(); break; case BTN_ID_LP: ui.select[ui.layer] = 0; case BTN_ID_SP: switch (ui.select[ui.layer]) {
  1460. case 0: ui.index = M_MAIN; ui.state = S_LAYER_OUT; break;
  1461. case 11: ui.index = M_KNOB; ui.state = S_LAYER_IN; break;
  1462. }
  1463. }
  1464. }
  1465. }
  1466. //旋钮编辑菜单处理函数
  1467. void knob_proc()
  1468. {
  1469. list_show(knob_menu, M_KNOB);
  1470. if (btn.pressed) { btn.pressed = false; switch (btn.id) { case BTN_ID_CW: case BTN_ID_CC: list_rotate_switch(); break; case BTN_ID_LP: ui.select[ui.layer] = 0; case BTN_ID_SP: switch (ui.select[ui.layer]) {
  1471. case 0: ui.index = M_EDITOR; ui.state = S_LAYER_OUT; break;
  1472. case 1: ui.index = M_KRF; ui.state = S_LAYER_IN; check_box_s_init(&knob.param[KNOB_ROT], &knob.param[KNOB_ROT_P]); break;
  1473. case 2: ui.index = M_KPF; ui.state = S_LAYER_IN; check_box_s_init(&knob.param[KNOB_COD], &knob.param[KNOB_COD_P]); break;
  1474. }
  1475. }
  1476. }
  1477. }
  1478. //旋钮旋转功能菜单处理函数
  1479. void krf_proc()
  1480. {
  1481. list_show(krf_menu, M_KRF);
  1482. if (btn.pressed) { btn.pressed = false; switch (btn.id) { case BTN_ID_CW: case BTN_ID_CC: list_rotate_switch(); break; case BTN_ID_LP: ui.select[ui.layer] = 0; case BTN_ID_SP: switch (ui.select[ui.layer]) {
  1483. case 0: ui.index = M_KNOB; ui.state = S_LAYER_OUT; break;
  1484. case 1: break;
  1485. case 2: check_box_s_select(KNOB_DISABLE, ui.select[ui.layer]); break;
  1486. case 3: break;
  1487. case 4: check_box_s_select(KNOB_ROT_VOL, ui.select[ui.layer]); break;
  1488. case 5: check_box_s_select(KNOB_ROT_BRI, ui.select[ui.layer]); break;
  1489. case 6: break;
  1490. }
  1491. }
  1492. }
  1493. }
  1494. #define KEY_LEFT_CTRL 0x80
  1495. #define KEY_LEFT_SHIFT 0x81
  1496. #define KEY_LEFT_ALT 0x82
  1497. #define KEY_LEFT_GUI 0x83
  1498. #define KEY_RIGHT_CTRL 0x84
  1499. #define KEY_RIGHT_SHIFT 0x85
  1500. #define KEY_RIGHT_ALT 0x86
  1501. #define KEY_RIGHT_GUI 0x87
  1502. #define KEY_UP_ARROW 0xDA
  1503. #define KEY_DOWN_ARROW 0xD9
  1504. #define KEY_LEFT_ARROW 0xD8
  1505. #define KEY_RIGHT_ARROW 0xD7
  1506. #define KEY_BACKSPACE 0xB2
  1507. #define KEY_TAB 0xB3
  1508. #define KEY_RETURN 0xB0
  1509. #define KEY_ESC 0xB1
  1510. #define KEY_INSERT 0xD1
  1511. #define KEY_DELETE 0xD4
  1512. #define KEY_PAGE_UP 0xD3
  1513. #define KEY_PAGE_DOWN 0xD6
  1514. #define KEY_HOME 0xD2
  1515. #define KEY_END 0xD5
  1516. #define KEY_CAPS_LOCK 0xC1
  1517. #define KEY_F1 0xC2
  1518. #define KEY_F2 0xC3
  1519. #define KEY_F3 0xC4
  1520. #define KEY_F4 0xC5
  1521. #define KEY_F5 0xC6
  1522. #define KEY_F6 0xC7
  1523. #define KEY_F7 0xC8
  1524. #define KEY_F8 0xC9
  1525. #define KEY_F9 0xCA
  1526. #define KEY_F10 0xCB
  1527. #define KEY_F11 0xCC
  1528. #define KEY_F12 0xCD
  1529. #define KEY_F13 0xF0
  1530. #define KEY_F14 0xF1
  1531. #define KEY_F15 0xF2
  1532. #define KEY_F16 0xF3
  1533. #define KEY_F17 0xF4
  1534. #define KEY_F18 0xF5
  1535. #define KEY_F19 0xF6
  1536. #define KEY_F20 0xF7
  1537. #define KEY_F21 0xF8
  1538. #define KEY_F22 0xF9
  1539. #define KEY_F23 0xFA
  1540. #define KEY_F24 0xFB
  1541. //旋钮点按功能菜单处理函数
  1542. void kpf_proc()
  1543. {
  1544. list_show(kpf_menu, M_KPF);
  1545. if (btn.pressed) { btn.pressed = false; switch (btn.id) { case BTN_ID_CW: case BTN_ID_CC: list_rotate_switch(); break; case BTN_ID_LP: ui.select[ui.layer] = 0; case BTN_ID_SP: switch (ui.select[ui.layer]) {
  1546. case 0: ui.index = M_KNOB; ui.state = S_LAYER_OUT; break;
  1547. case 1: break;
  1548. case 2: check_box_s_select(KNOB_DISABLE, ui.select[ui.layer]); break;
  1549. case 3: break;
  1550. case 4: check_box_s_select('A', ui.select[ui.layer]); break;
  1551. case 5: check_box_s_select('B', ui.select[ui.layer]); break;
  1552. case 6: check_box_s_select('C', ui.select[ui.layer]); break;
  1553. case 7: check_box_s_select('D', ui.select[ui.layer]); break;
  1554. case 8: check_box_s_select('E', ui.select[ui.layer]); break;
  1555. case 9: check_box_s_select('F', ui.select[ui.layer]); break;
  1556. case 10: check_box_s_select('G', ui.select[ui.layer]); break;
  1557. case 11: check_box_s_select('H', ui.select[ui.layer]); break;
  1558. case 12: check_box_s_select('I', ui.select[ui.layer]); break;
  1559. case 13: check_box_s_select('J', ui.select[ui.layer]); break;
  1560. case 14: check_box_s_select('K', ui.select[ui.layer]); break;
  1561. case 15: check_box_s_select('L', ui.select[ui.layer]); break;
  1562. case 16: check_box_s_select('M', ui.select[ui.layer]); break;
  1563. case 17: check_box_s_select('N', ui.select[ui.layer]); break;
  1564. case 18: check_box_s_select('O', ui.select[ui.layer]); break;
  1565. case 19: check_box_s_select('P', ui.select[ui.layer]); break;
  1566. case 20: check_box_s_select('Q', ui.select[ui.layer]); break;
  1567. case 21: check_box_s_select('R', ui.select[ui.layer]); break;
  1568. case 22: check_box_s_select('S', ui.select[ui.layer]); break;
  1569. case 23: check_box_s_select('T', ui.select[ui.layer]); break;
  1570. case 24: check_box_s_select('U', ui.select[ui.layer]); break;
  1571. case 25: check_box_s_select('V', ui.select[ui.layer]); break;
  1572. case 26: check_box_s_select('W', ui.select[ui.layer]); break;
  1573. case 27: check_box_s_select('X', ui.select[ui.layer]); break;
  1574. case 28: check_box_s_select('Y', ui.select[ui.layer]); break;
  1575. case 29: check_box_s_select('Z', ui.select[ui.layer]); break;
  1576. case 30: break;
  1577. case 31: check_box_s_select('0', ui.select[ui.layer]); break;
  1578. case 32: check_box_s_select('1', ui.select[ui.layer]); break;
  1579. case 33: check_box_s_select('2', ui.select[ui.layer]); break;
  1580. case 34: check_box_s_select('3', ui.select[ui.layer]); break;
  1581. case 35: check_box_s_select('4', ui.select[ui.layer]); break;
  1582. case 36: check_box_s_select('5', ui.select[ui.layer]); break;
  1583. case 37: check_box_s_select('6', ui.select[ui.layer]); break;
  1584. case 38: check_box_s_select('7', ui.select[ui.layer]); break;
  1585. case 39: check_box_s_select('8', ui.select[ui.layer]); break;
  1586. case 40: check_box_s_select('9', ui.select[ui.layer]); break;
  1587. case 41: break;
  1588. case 42: check_box_s_select( KEY_ESC, ui.select[ui.layer]); break;
  1589. case 43: check_box_s_select( KEY_F1, ui.select[ui.layer]); break;
  1590. case 44: check_box_s_select( KEY_F2, ui.select[ui.layer]); break;
  1591. case 45: check_box_s_select( KEY_F3, ui.select[ui.layer]); break;
  1592. case 46: check_box_s_select( KEY_F4, ui.select[ui.layer]); break;
  1593. case 47: check_box_s_select( KEY_F5, ui.select[ui.layer]); break;
  1594. case 48: check_box_s_select( KEY_F6, ui.select[ui.layer]); break;
  1595. case 49: check_box_s_select( KEY_F7, ui.select[ui.layer]); break;
  1596. case 50: check_box_s_select( KEY_F8, ui.select[ui.layer]); break;
  1597. case 51: check_box_s_select( KEY_F9, ui.select[ui.layer]); break;
  1598. case 52: check_box_s_select( KEY_F10, ui.select[ui.layer]); break;
  1599. case 53: check_box_s_select( KEY_F11, ui.select[ui.layer]); break;
  1600. case 54: check_box_s_select( KEY_F12, ui.select[ui.layer]); break;
  1601. case 55: break;
  1602. case 56: check_box_s_select( KEY_LEFT_CTRL, ui.select[ui.layer]); break;
  1603. case 57: check_box_s_select( KEY_LEFT_SHIFT, ui.select[ui.layer]); break;
  1604. case 58: check_box_s_select( KEY_LEFT_ALT, ui.select[ui.layer]); break;
  1605. case 59: check_box_s_select( KEY_LEFT_GUI, ui.select[ui.layer]); break;
  1606. case 60: check_box_s_select( KEY_RIGHT_CTRL, ui.select[ui.layer]); break;
  1607. case 61: check_box_s_select( KEY_RIGHT_SHIFT, ui.select[ui.layer]); break;
  1608. case 62: check_box_s_select( KEY_RIGHT_ALT, ui.select[ui.layer]); break;
  1609. case 63: check_box_s_select( KEY_RIGHT_GUI, ui.select[ui.layer]); break;
  1610. case 64: break;
  1611. case 65: check_box_s_select( KEY_CAPS_LOCK, ui.select[ui.layer]); break;
  1612. case 66: check_box_s_select( KEY_BACKSPACE, ui.select[ui.layer]); break;
  1613. case 67: check_box_s_select( KEY_RETURN, ui.select[ui.layer]); break;
  1614. case 68: check_box_s_select( KEY_INSERT, ui.select[ui.layer]); break;
  1615. case 69: check_box_s_select( KEY_DELETE, ui.select[ui.layer]); break;
  1616. case 70: check_box_s_select( KEY_TAB, ui.select[ui.layer]); break;
  1617. case 71: break;
  1618. case 72: check_box_s_select( KEY_HOME, ui.select[ui.layer]); break;
  1619. case 73: check_box_s_select( KEY_END, ui.select[ui.layer]); break;
  1620. case 74: check_box_s_select( KEY_PAGE_UP, ui.select[ui.layer]); break;
  1621. case 75: check_box_s_select( KEY_PAGE_DOWN, ui.select[ui.layer]); break;
  1622. case 76: break;
  1623. case 77: check_box_s_select( KEY_UP_ARROW, ui.select[ui.layer]); break;
  1624. case 78: check_box_s_select( KEY_DOWN_ARROW, ui.select[ui.layer]); break;
  1625. case 79: check_box_s_select( KEY_LEFT_ARROW, ui.select[ui.layer]); break;
  1626. case 80: check_box_s_select( KEY_RIGHT_ARROW, ui.select[ui.layer]); break;
  1627. case 81: break;
  1628. }
  1629. }
  1630. }
  1631. }
  1632. //电压测量页处理函数
  1633. void volt_proc()
  1634. {
  1635. volt_show();
  1636. if (btn.pressed) { btn.pressed = false; switch (btn.id) { case BTN_ID_CW: case BTN_ID_CC: list_rotate_switch(); break;
  1637. case BTN_ID_SP: case BTN_ID_LP: ui.index = M_MAIN; ui.state = S_LAYER_OUT; break;
  1638. }
  1639. }
  1640. }
  1641. //设置菜单处理函数,多选框列表类模板,弹窗模板
  1642. void setting_proc()
  1643. {
  1644. list_show(setting_menu, M_SETTING);
  1645. if (btn.pressed) { btn.pressed = false; switch (btn.id) { case BTN_ID_CW: case BTN_ID_CC: list_rotate_switch(); break; case BTN_ID_LP: ui.select[ui.layer] = 0; case BTN_ID_SP: switch (ui.select[ui.layer]) {
  1646. //返回更浅层级,长按被当作选择这一项,也是执行这一行
  1647. case 0: ui.index = M_MAIN; ui.state = S_LAYER_OUT; break;
  1648. //弹出窗口,参数初始化:标题,参数名,参数值,最大值,最小值,步长,背景列表名,背景列表标签
  1649. case 1: window_value_init("Disp Bri", DISP_BRI, &ui.param[DISP_BRI], 255, 0, 5, setting_menu, M_SETTING); break;
  1650. case 2: window_value_init("Tile Ani", TILE_ANI, &ui.param[TILE_ANI], 100, 10, 1, setting_menu, M_SETTING); break;
  1651. case 3: window_value_init("List Ani", LIST_ANI, &ui.param[LIST_ANI], 100, 10, 1, setting_menu, M_SETTING); break;
  1652. case 4: window_value_init("Win Ani", WIN_ANI, &ui.param[WIN_ANI], 100, 10, 1, setting_menu, M_SETTING); break;
  1653. case 5: window_value_init("Spot Ani", SPOT_ANI, &ui.param[SPOT_ANI], 100, 10, 1, setting_menu, M_SETTING); break;
  1654. case 6: window_value_init("Tag Ani", TAG_ANI, &ui.param[TAG_ANI], 100, 10, 1, setting_menu, M_SETTING); break;
  1655. case 7: window_value_init("Fade Ani", FADE_ANI, &ui.param[FADE_ANI], 255, 0, 1, setting_menu, M_SETTING); break;
  1656. case 8: window_value_init("Btn SPT", BTN_SPT, &ui.param[BTN_SPT], 255, 0, 1, setting_menu, M_SETTING); break;
  1657. case 9: window_value_init("Btn LPT", BTN_LPT, &ui.param[BTN_LPT], 255, 0, 1, setting_menu, M_SETTING); break;
  1658. //多选框
  1659. case 10: check_box_m_select( TILE_UFD ); break;
  1660. case 11: check_box_m_select( LIST_UFD ); break;
  1661. case 12: check_box_m_select( TILE_LOOP ); break;
  1662. case 13: check_box_m_select( LIST_LOOP ); break;
  1663. case 14: check_box_m_select( WIN_BOK ); break;
  1664. case 15: check_box_m_select( KNOB_DIR ); break;
  1665. case 16: check_box_m_select( DARK_MODE ); break;
  1666. //关于本机
  1667. case 17: ui.index = M_ABOUT; ui.state = S_LAYER_IN; break;
  1668. }
  1669. }
  1670. }
  1671. }
  1672. //关于本机页
  1673. void about_proc()
  1674. {
  1675. list_show(about_menu, M_ABOUT);
  1676. if (btn.pressed) { btn.pressed = false; switch (btn.id) { case BTN_ID_CW: case BTN_ID_CC: list_rotate_switch(); break; case BTN_ID_LP: ui.select[ui.layer] = 0; case BTN_ID_SP: switch (ui.select[ui.layer]) {
  1677. case 0: ui.index = M_SETTING; ui.state = S_LAYER_OUT; break;
  1678. }
  1679. }
  1680. }
  1681. }
  1682. //总的UI进程
  1683. void ui_proc()
  1684. {
  1685. //u8g2.sendBuffer();
  1686. //u8g2_SendBuffer(&u8g2);
  1687. //u8g2_FirstPage(&u8g2);
  1688. switch (ui.state)
  1689. {
  1690. case S_FADE: fade(); break; //转场动画
  1691. case S_WINDOW: window_param_init(); break; //弹窗初始化
  1692. case S_LAYER_IN: layer_init_in(); break; //层级初始化
  1693. case S_LAYER_OUT: layer_init_out(); break; //层级初始化
  1694. case S_NONE: u8g2_ClearBuffer(&u8g2);//u8g2.clearBuffer();
  1695. switch (ui.index) //直接选择页面
  1696. {
  1697. case M_WINDOW: window_proc(); break;
  1698. case M_SLEEP: sleep_proc(); break;
  1699. case M_MAIN: main_proc(); break;
  1700. case M_EDITOR: editor_proc(); break;
  1701. case M_KNOB: knob_proc(); break;
  1702. case M_KRF: krf_proc(); break;
  1703. case M_KPF: kpf_proc(); break;
  1704. case M_VOLT: volt_proc(); break;
  1705. case M_SETTING: setting_proc(); break;
  1706. case M_ABOUT: about_proc(); break;
  1707. }
  1708. }
  1709. u8g2_NextPage(&u8g2);
  1710. }
  1711. //OLED初始化函数
  1712. void oled_init()
  1713. {
  1714. //u8g2.setBusClock(1000000); //硬件IIC接口使用
  1715. //u8g2.begin();
  1716. //u8g2.setContrast(ui.param[DISP_BRI]);
  1717. u8g2_SetContrast(&u8g2, ui.param[DISP_BRI]);
  1718. //buf_ptr = u8g2.getBufferPtr();
  1719. //buf_len = 8 * u8g2.getBufferTileHeight() * u8g2.getBufferTileWidth();
  1720. buf_ptr = u8g2_GetBufferPtr(&u8g2);
  1721. buf_len = 8 * u8g2_GetBufferTileHeight(&u8g2) * u8g2_GetBufferTileWidth(&u8g2);
  1722. }
  1723. void flex_ui_setup()
  1724. {
  1725. eeprom_init();
  1726. ui_init();
  1727. oled_init();
  1728. btn_init();
  1729. ESP_LOGI(TAG, "flex ui setup!");
  1730. }
  1731. void flex_ui_loop()
  1732. {
  1733. btn_scan();
  1734. knob_inter();
  1735. ui_proc();
  1736. }

~小结:

         这次主要记录了WouoUI框架在esp32 idf环境下的移植,WouoUI的框架原本是在arduino下面实现的,最终用丙正正实现了移植,移植过程主要难点在于arduino框架u8g2类各种方法的丙语言实现替换,其中又属print重载函数的实现最麻烦,还好最终一一克服,将这优雅的UI呈现在小黑上面;

以下是一些和本文相关的文章链接和U8g2的wiki链接:

一、~~呆萌的瓦力平衡机器人~~链接:

        1.基于ESP32双无刷FOC电机的瓦力平衡机器人(1)

        2.基于ESP32双无刷FOC电机的瓦力平衡机器人(2)

二、 ~~U8g2图形库使用技巧记录~~链接:

        1.U8g2图形库使用技巧记录(1)

        2.U8g2图形库使用技巧记录(2)

        3.U8g2图形库使用技巧记录(3)

三、U8g2 wiki链接:

        1.U8g2_wiki

        2.WouoUI框架,可以直接搜索;

后续的计划:

       目前UI的大体框架已经定型,后续会基于该框架实现小车参数的设置和增加一些动效页面的;          

                      大家如果也感兴趣,可以来这交流学习(这里提供了丰富的esp32资料):

                             

                                                        燥起来吧!!!!!! 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/459628
推荐阅读
相关标签
  

闽ICP备14008679号