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

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

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

~以下是UI的视频效果:

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

/************************************* 屏幕驱动 *************************************/
//分辨率128*64,可以使用硬件IIC接口
#include "flex_ui.h"
#include "u8g2_ssd1306.h"#include 
#include 
#include 
#include 
#include 
#include "nvs_flash.h"
#include "nvs.h"#include "sbus.h"
#include "param.h"
#include "ms5611.h"
#include "tle5012b.h"
#include "drv_adc.h"
#include "drv_motor.h"
#include "stabilizer.h"
#include 
#include 
#include #include "wiring.h"
#include "lv_math.h"static const char *TAG = "flex_ui";/************************************* 定义页面 *************************************///总目录,缩进表示页面层级
enum 
{M_WINDOW,M_SLEEP,M_MAIN, M_EDITOR,M_KNOB,M_KRF,M_KPF,M_VOLT,M_SETTING,M_ABOUT,
};//状态,初始化标签
enum
{S_FADE,       //转场动画S_WINDOW,     //弹窗初始化S_LAYER_IN,   //层级初始化S_LAYER_OUT,  //层级初始化S_NONE,       //直接选择页面
};//菜单结构体
typedef struct MENU
{char *m_select;
} M_SELECT;/************************************* 定义内容 *************************************//************************************* 文字内容 *************************************/M_SELECT main_menu[]
{{"Sleep"},{"Editor"},{"Volt"},{"Setting"},
};M_SELECT editor_menu[]
{{"[ Editor ]"},{"- Function 0"},{"- Function 1"},{"- Function 2"},{"- Function 3"},{"- Function 4"},{"- Function 5"},{"- Function 6"},{"- Function 7"},{"- Function 8"},{"- Function 9"},{"- Knob"},
};M_SELECT knob_menu[]
{{"[ Knob ]"},{"# Rotate Func"},{"$ Press Func"},
};M_SELECT krf_menu[]
{{"[ Rotate Function ]"},{"--------------------------"},{"= Disable"},{"--------------------------"},{"= Volume"},{"= Brightness"},{"--------------------------"},
};M_SELECT kpf_menu[]
{{"[ Press Function ]"},{"--------------------------"},{"= Disable"},{"--------------------------"},{"= 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"},{"--------------------------"},{"= 0"},{"= 1"},{"= 2"},{"= 3"},{"= 4"},{"= 5"},{"= 6"},{"= 7"},{"= 8"},{"= 9"},{"--------------------------"},{"= Esc"},{"= F1"},{"= F2"},{"= F3"},{"= F4"},{"= F5"},{"= F6"},{"= F7"},{"= F8"},{"= F9"},{"= F10"},{"= F11"},{"= F12"},{"--------------------------"},{"= Left Ctrl"},{"= Left Shift"},{"= Left Alt"},{"= Left Win"},{"= Right Ctrl"},{"= Right Shift"},{"= Right Alt"},{"= Right Win"},{"--------------------------"},{"= Caps Lock"},{"= Backspace"},{"= Return"},{"= Insert"},{"= Delete"},{"= Tab"},{"--------------------------"},{"= Home"},{"= End"},{"= Page Up"},{"= Page Down"},{"--------------------------"},{"= Up Arrow"},{"= Down Arrow"},{"= Left Arrow"},{"= Right Arrow"},{"--------------------------"},
};M_SELECT volt_menu[]
{{"A0"},{"A1"},{"A2"},{"A3"},{"A4"},{"A5"},{"A6"},{"A7"},{"B0"},{"B1"},
};M_SELECT setting_menu[]
{{"[ Setting ]"},{"~ Disp Bri"},{"~ Tile Ani"},{"~ List Ani"},{"~ Win Ani"},{"~ Spot Ani"},{"~ Tag Ani"},{"~ Fade Ani"},{"~ Btn SPT"},{"~ Btn LPT"},{"+ T Ufd Fm Scr"},{"+ L Ufd Fm Scr"},{"+ T Loop Mode"},{"+ L Loop Mode"},{"+ Win Bokeh Bg"},{"+ Knob Rot Dir"},{"+ Dark Mode"},{"- [ About ]"},
};M_SELECT about_menu[]
{{"[ FlexUI ]"},{"- Version: v1.0"},{"- Board: ESP32 PICO"},{"- Ram: 340k"},{"- Flash: 4MB"},{"- Freq: 240Mhz"},{"- Creator: Leon"},{"- Billi UID: 20230705"},  
};/************************************* 图片内容 *************************************/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,0xEF,0xFF,0xFF,0x3F,0xC7,0xFF,0xFF,0x3F,0xC7,0xF3,0xFF,0x3F,0x83,0xC0,0xFF,0x3F,0xEF,0xCC,0xFF,0x3F,0x6F,0x9E,0xFF,0x3F,0x6F,0x9E,0xFF,0x3F,0x2F,0x3F,0xFF,0x3F,0x2F,0x3F,0xFF,0x3F,0x8F,0x7F,0xFE,0x3F,0x8F,0x7F,0xFE,0x39,0x8F,0x7F,0xFE,0x39,0xCF,0xFF,0xFC,0x3C,0xCF,0xFF,0xFC,0x3C,0xEF,0xFF,0xFC,0x3C,0xEF,0xFF,0x79,0x3E,0xEF,0xFF,0x79,0x3E,0xEF,0xFF,0x33,0x3F,0xEF,0xFF,0x33,0x3F,0xEF,0xFF,0x87,0x3F,0xEF,0xFF,0xCF,0x3F,0xEF,0xFF,0x7F,0x3E,0xEF,0xFF,0x7F,0x38,0x0F,0x00,0x00,0x30,0xFF,0xFF,0x7F,0x38,0xFF,0xFF,0x7F,0x3E,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变量
#define   DISP_H              64    //屏幕高度
#define   DISP_W              128   //屏幕宽度
uint8_t   *buf_ptr;                 //指向屏幕缓冲的指针
uint16_t  buf_len;                  //缓冲长度//UI变量
#define   UI_DEPTH            20    //最深层级数
#define   UI_MNUMB            100   //菜单数量
#define   UI_PARAM            16    //参数数量
enum 
{DISP_BRI,     //屏幕亮度TILE_ANI,     //磁贴动画速度LIST_ANI,     //列表动画速度WIN_ANI,      //弹窗动画速度SPOT_ANI,     //聚光动画速度TAG_ANI,      //标签动画速度FADE_ANI,     //消失动画速度BTN_SPT,      //按键短按时长BTN_LPT,      //按键长按时长TILE_UFD,     //磁贴图标从头展开开关LIST_UFD,     //菜单列表从头展开开关TILE_LOOP,    //磁贴图标循环模式开关LIST_LOOP,    //菜单列表循环模式开关WIN_BOK,      //弹窗背景虚化开关KNOB_DIR,     //旋钮方向切换开关DARK_MODE,    //黑暗模式开关
};
struct 
{bool      init;uint8_t   num[UI_MNUMB];uint8_t   select[UI_DEPTH];uint8_t   layer;uint8_t   index = M_MAIN;uint8_t   state = S_LAYER_IN;bool      sleep = true;uint8_t   fade = 1;uint8_t   param[UI_PARAM];
} ui;//磁贴变量
//所有磁贴页面都使用同一套参数
#define   TILE_B_FONT         u8g2_font_helvB18_tr        //磁贴大标题字体
#define   TILE_B_TITLE_H      18                          //磁贴大标题字体高度
#define   TILE_ICON_H         30                          //磁贴图标高度
#define   TILE_ICON_W         30                          //磁贴图标宽度
#define   TILE_ICON_S         36                          //磁贴图标间距
#define   TILE_INDI_H         27                          //磁贴大标题指示器高度
#define   TILE_INDI_W         7                           //磁贴大标题指示器宽度
#define   TILE_INDI_S         36                          //磁贴大标题指示器上边距
struct 
{float   title_y_calc = TILE_INDI_S + (TILE_INDI_H - TILE_B_TITLE_H) / 2 + TILE_B_TITLE_H * 2;float   title_y_trg_calc = TILE_INDI_S + (TILE_INDI_H - TILE_B_TITLE_H) / 2 + TILE_B_TITLE_H;int16_t temp;bool    select_flag;float   icon_x;float   icon_x_trg;float   icon_y;float   icon_y_trg;float   indi_x; float   indi_x_trg;float   title_y;float   title_y_trg;
} tile;//列表变量
//默认参数#define   LIST_FONT           u8g2_font_HelvetiPixel_tr   //列表字体
#define   LIST_TEXT_H         8                           //列表每行文字字体的高度
#define   LIST_LINE_H         16                          //列表单行高度
#define   LIST_TEXT_S         4                           //列表每行文字的上边距,左边距和右边距,下边距由它和字体高度和行高度决定
#define   LIST_BAR_W          5                           //列表进度条宽度,需要是奇数,因为正中间有1像素宽度的线
#define   LIST_BOX_R          0.5                         //列表选择框圆角/*
//超窄行高度测试
#define   LIST_FONT           u8g2_font_4x6_tr            //列表字体
#define   LIST_TEXT_H         5                           //列表每行文字字体的高度
#define   LIST_LINE_H         7                           //列表单行高度
#define   LIST_TEXT_S         1                           //列表每行文字的上边距,左边距和右边距,下边距由它和字体高度和行高度决定
#define   LIST_BAR_W          7                           //列表进度条宽度,需要是奇数,因为正中间有1像素宽度的线
#define   LIST_BOX_R          0.5                         //列表选择框圆角
*/
struct
{uint8_t line_n = DISP_H / LIST_LINE_H;int16_t temp;bool    loop;float   y;float   y_trg;float   box_x;float   box_x_trg;float   box_y;float   box_y_trg[UI_DEPTH];float   bar_y;float   bar_y_trg;
} list;//曲线相关
#define   WAVE_SAMPLE         20                          //采集倍数
#define   WAVE_W              94                          //波形宽度
#define   WAVE_L              24                          //波形左边距
#define   WAVE_U              0                           //波形上边距
#define   WAVE_MAX            27                          //最大值
#define   WAVE_MIN            4                           //最小值
#define   WAVE_BOX_H          32                          //波形边框高度
#define   WAVE_BOX_W          94                          //波形边框宽度
#define   WAVE_BOX_L_S        24                          //波形边框左边距
//列表和文字背景框相关
#define   VOLT_FONT           u8g2_font_helvB18_tr        //电压数字字体
#define   VOLT_TEXT_BG_L_S    24                          //文字背景框左边距
#define   VOLT_TEXT_BG_W      94                          //文字背景框宽度
#define   VOLT_TEXT_BG_H      29                          //文字背景框高度
struct
{int     ch0_adc[WAVE_SAMPLE * WAVE_W];int     ch0_wave[WAVE_W];int     val;float   text_bg_r; float   text_bg_r_trg; 
} volt;//选择框变量//默认参数
#define   CHECK_BOX_L_S       95                          //选择框在每行的左边距
#define   CHECK_BOX_U_S       2                           //选择框在每行的上边距
#define   CHECK_BOX_F_W       12                          //选择框外框宽度
#define   CHECK_BOX_F_H       12                          //选择框外框高度
#define   CHECK_BOX_D_S       2                           //选择框里面的点距离外框的边距/*
//超窄行高度测试
#define   CHECK_BOX_L_S       99                          //选择框在每行的左边距
#define   CHECK_BOX_U_S       0                           //选择框在每行的上边距
#define   CHECK_BOX_F_W       5                           //选择框外框宽度
#define   CHECK_BOX_F_H       5                           //选择框外框高度
#define   CHECK_BOX_D_S       1                           //选择框里面的点距离外框的边距
*/
struct
{uint8_t *v;uint8_t *m;uint8_t *s;uint8_t *s_p;
} check_box;//弹窗变量
#define   WIN_FONT            u8g2_font_HelvetiPixel_tr   //弹窗字体
#define   WIN_H               32                          //弹窗高度
#define   WIN_W               102                         //弹窗宽度
#define   WIN_BAR_W           92                          //弹窗进度条宽度
#define   WIN_BAR_H           7                           //弹窗进度条高度
#define   WIN_Y               - WIN_H - 2                 //弹窗竖直方向出场起始位置
#define   WIN_Y_TRG           - WIN_H - 2                 //弹窗竖直方向退场终止位置
struct
{//uint8_tuint8_t   *value;uint8_t   max;uint8_t   min;uint8_t   step;MENU      *bg;uint8_t   index;char      title[20];uint8_t   select;uint8_t   l = (DISP_W - WIN_W) / 2;uint8_t   u = (DISP_H - WIN_H) / 2;float     bar;float     bar_trg;float     y;float     y_trg;
} win;//聚光灯变量
struct
{float   l; float   l_trg; float   r; float   r_trg; float   u; float   u_trg; float   d; float   d_trg; 
} spot;/********************************** 自定义功能变量 **********************************///旋钮功能变量
#define   KNOB_PARAM          4
#define   KNOB_DISABLE        0
#define   KNOB_ROT_VOL        1
#define   KNOB_ROT_BRI        2
enum 
{KNOB_ROT,       //睡眠下旋转旋钮的功能,0 禁用,1 音量,2 亮度KNOB_COD,       //睡眠下短按旋钮输入的字符码,0 禁用KNOB_ROT_P,     //旋转旋钮功能在单选框中选择的位置KNOB_COD_P,     //字符码在单选框中选择的位置
};
struct 
{uint8_t param[KNOB_PARAM] = { KNOB_DISABLE, KNOB_DISABLE, 2, 2 }; //禁用在列表的第2个选项,第0个是标题,第1个是分界线
} knob;/************************************* 断电保存 *************************************///#include //EEPROM变量
#define   EEPROM_CHECK        11
struct
{bool    init;bool    change;int     address;uint8_t check;uint8_t check_param[EEPROM_CHECK] = { 'a', 'b', 'c', 'd', 'e', 'f','g', 'h', 'i', 'j', 'k' }; 
} eeprom;//EEPROM写数据,回到睡眠时执行一遍
void eeprom_write_all_data()
{
#if 0eeprom.address = 0;for (uint8_t i = 0; i < EEPROM_CHECK; ++i)    EEPROM.write(eeprom.address + i, eeprom.check_param[i]);  eeprom.address += EEPROM_CHECK;for (uint8_t i = 0; i < UI_PARAM; ++i)        EEPROM.write(eeprom.address + i, ui.param[i]);            eeprom.address += UI_PARAM;for (uint8_t i = 0; i < KNOB_PARAM; ++i)      EEPROM.write(eeprom.address + i, knob.param[i]);          eeprom.address += KNOB_PARAM;
#elsenvs_handle handle;int32_t err;// Openerr = nvs_open(STORAGE_NAMESPACE, NVS_READWRITE, &handle);if (err != ESP_OK) goto err;err = nvs_set_blob(handle, "ui_param_ck", eeprom.check_param, EEPROM_CHECK);if (err != ESP_OK) goto err;err = nvs_set_blob(handle, "ui_param",    ui.param,   		  UI_PARAM);if (err != ESP_OK) goto err;err = nvs_set_blob(handle, "knob_param",  knob.param, 		  KNOB_PARAM);if (err != ESP_OK) goto err;ESP_LOGI(TAG, "param write all finish!");err = nvs_commit(handle);if (err != ESP_OK) goto err;err:// Closenvs_close(handle);
#endif
}//EEPROM读数据,开机初始化时执行一遍
void eeprom_read_all_data()
{
#if 0eeprom.address = EEPROM_CHECK;   for (uint8_t i = 0; i < UI_PARAM; ++i)        ui.param[i]   = EEPROM.read(eeprom.address + i);          eeprom.address += UI_PARAM;for (uint8_t i = 0; i < KNOB_PARAM; ++i)      knob.param[i] = EEPROM.read(eeprom.address + i);          eeprom.address += KNOB_PARAM;
#elsenvs_handle handle;size_t length;int32_t err;// Openerr = nvs_open(STORAGE_NAMESPACE, NVS_READWRITE, &handle);if (err != ESP_OK) goto err;err = nvs_get_blob(handle, "ui_param",   NULL, &length);err = nvs_get_blob(handle, "ui_param",   ui.param, &length);if (err != ESP_OK) goto err;err = nvs_get_blob(handle, "knob_param", NULL, &length);err = nvs_get_blob(handle, "knob_param", knob.param, &length);if (err != ESP_OK) goto err;ESP_LOGI(TAG, "param read all finish!");err:// Closenvs_close(handle);#endif
}//开机检查是否已经修改过,没修改过则跳过读配置步骤,用默认设置
void eeprom_init()
{
#if 0eeprom.check = 0;eeprom.address = 0; for (uint8_t i = 0; i < EEPROM_CHECK; ++i)  if (EEPROM.read(eeprom.address + i) != eeprom.check_param[i])  eeprom.check ++;if (eeprom.check <= 1) eeprom_read_all_data();  //允许一位误码else ui_param_init();
#elsenvs_handle handle;size_t length;int32_t err;uint8_t check_param[EEPROM_CHECK] = {0};// Openerr = nvs_open(STORAGE_NAMESPACE, NVS_READWRITE, &handle);if (err != ESP_OK) goto err;err = nvs_get_blob(handle, "ui_param_ck", NULL, &length);err = nvs_get_blob(handle, "ui_param_ck", check_param, &length);if (err != ESP_OK) {ui_param_init();goto err;}eeprom.check = 0;eeprom.address = 0; for (uint8_t i = 0; i < EEPROM_CHECK; ++i)	if (check_param[i] != eeprom.check_param[i])  eeprom.check ++;if (eeprom.check <= 1) eeprom_read_all_data();	//允许一位误码else ui_param_init();ESP_LOGI(TAG, "ui param check pass!");err:// Closenvs_close(handle);
#endif
}/************************************* 旋钮相关 *************************************///可按下旋钮引脚
#define   AIO   PB12
#define   BIO   PB13
//#define   SW    PB14
#define   SW    34//按键ID
#define   BTN_ID_CC           0   //逆时针旋转
#define   BTN_ID_CW           1   //顺时针旋转
#define   BTN_ID_SP           2   //短按
#define   BTN_ID_LP           3   //长按//按键变量
#define   BTN_PARAM_TIMES     2   //由于uint8_t最大值可能不够,但它存储起来方便,这里放大两倍使用
struct
{uint8_t   id;bool      flag;bool      pressed;bool      CW_1;bool      CW_2;bool      val;bool      val_last;  bool      alv;  bool      blv;long      count;
} volatile btn;void knob_inter() 
{
#if 0btn.alv = digitalRead(AIO);btn.blv = digitalRead(BIO);if (!btn.flag && btn.alv == LOW) {btn.CW_1 = btn.blv;btn.flag = true;}if (btn.flag && btn.alv) {btn.CW_2 = !btn.blv;if (btn.CW_1 && btn.CW_2){btn.id = ui.param[KNOB_DIR];btn.pressed = true;}if (btn.CW_1 == false && btn.CW_2 == false) {btn.id = !ui.param[KNOB_DIR];btn.pressed = true;}btn.flag = false;}
#elseif (btn.pressed == false) {if (motor_a.pos_spd_ltd.v2 > 60) {btn.id = !ui.param[KNOB_DIR];btn.pressed = true;} else if (motor_a.pos_spd_ltd.v2 < -60) {btn.id = ui.param[KNOB_DIR];btn.pressed = true;} else {btn.pressed = false;}}
#endif
}void btn_scan() 
{btn.val = digitalRead(SW);if (btn.val != btn.val_last){btn.val_last = btn.val;delay(ui.param[BTN_SPT] * BTN_PARAM_TIMES);btn.val = digitalRead(SW);if (btn.val == LOW){btn.pressed = true;btn.count = 0;while (!digitalRead(SW)){btn.count++;delay(1);}if (btn.count < ui.param[BTN_LPT] * BTN_PARAM_TIMES)  btn.id = BTN_ID_SP;else  btn.id = BTN_ID_LP;ESP_LOGI(TAG, "btn_scan:%d/%d=%d", btn.count, (ui.param[BTN_LPT] * BTN_PARAM_TIMES), btn.id);}}
}void btn_init() 
{
#if 0pinMode(AIO, INPUT);pinMode(BIO, INPUT);pinMode(SW, INPUT_PULLUP);attachInterrupt(digitalPinToInterrupt(AIO), knob_inter, CHANGE);
#else#endif
}/************************************ 初始化函数 ***********************************//********************************* 初始化数据处理函数 *******************************///显示数值的初始化
void check_box_v_init(uint8_t *param)
{check_box.v = param;
}//多选框的初始化
void check_box_m_init(uint8_t *param)
{check_box.m = param;
}//单选框时的初始化
void check_box_s_init(uint8_t *param, uint8_t *param_p)
{check_box.s = param;check_box.s_p = param_p;
}//多选框处理函数
void check_box_m_select(uint8_t param)
{check_box.m[param] = !check_box.m[param];eeprom.change = true;
}//单选框处理函数
void check_box_s_select(uint8_t val, uint8_t pos)
{*check_box.s = val;*check_box.s_p = pos;eeprom.change = true;
}//弹窗数值初始化
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)
{strcpy(win.title, title);win.select = select;win.value = value;win.max = max;win.min = min;win.step = step;win.bg = bg;win.index = index;  ui.index = M_WINDOW;ui.state = S_WINDOW;
}/*********************************** UI 初始化函数 *********************************///在初始化EEPROM时,选择性初始化的默认设置
void ui_param_init()
{ui.param[DISP_BRI]  = 255;      //屏幕亮度ui.param[TILE_ANI]  = 30;       //磁贴动画速度ui.param[LIST_ANI]  = 60;       //列表动画速度ui.param[WIN_ANI]   = 25;       //弹窗动画速度ui.param[SPOT_ANI]  = 50;       //聚光动画速度ui.param[TAG_ANI]   = 60;       //标签动画速度ui.param[FADE_ANI]  = 30;       //消失动画速度ui.param[BTN_SPT]   = 25;       //按键短按时长ui.param[BTN_LPT]   = 150;      //按键长按时长ui.param[TILE_UFD]  = 1;        //磁贴图标从头展开开关ui.param[LIST_UFD]  = 1;        //菜单列表从头展开开关ui.param[TILE_LOOP] = 0;        //磁贴图标循环模式开关ui.param[LIST_LOOP] = 0;        //菜单列表循环模式开关ui.param[WIN_BOK]   = 0;        //弹窗背景虚化开关ui.param[KNOB_DIR]  = 0;        //旋钮方向切换开关   ui.param[DARK_MODE] = 1;        //黑暗模式开关   
}//列表类页面列表行数初始化,必须初始化的参数
void ui_init()
{ui.num[M_MAIN]      = sizeof( main_menu     )   / sizeof(M_SELECT);ui.num[M_EDITOR]    = sizeof( editor_menu   )   / sizeof(M_SELECT);ui.num[M_KNOB]      = sizeof( knob_menu     )   / sizeof(M_SELECT);ui.num[M_KRF]       = sizeof( krf_menu      )   / sizeof(M_SELECT);ui.num[M_KPF]       = sizeof( kpf_menu      )   / sizeof(M_SELECT);ui.num[M_VOLT]      = sizeof( volt_menu     )   / sizeof(M_SELECT);ui.num[M_SETTING]   = sizeof( setting_menu  )   / sizeof(M_SELECT);ui.num[M_ABOUT]     = sizeof( about_menu    )   / sizeof(M_SELECT);   
}/********************************* 分页面初始化函数 ********************************///进入磁贴类时的初始化
void tile_param_init()
{ui.init = false;tile.icon_x = 0;tile.icon_x_trg = TILE_ICON_S;tile.icon_y = -TILE_ICON_H;tile.icon_y_trg = 0;tile.indi_x = 0;tile.indi_x_trg = TILE_INDI_W;tile.title_y = tile.title_y_calc;tile.title_y_trg = tile.title_y_trg_calc;
}//进入睡眠时的初始化
void sleep_param_init()
{
//#ifdef ARDUINO
#if 0u8g2.setDrawColor(0);u8g2.drawBox(0, 0, DISP_W, DISP_H);u8g2.setPowerSave(1);
#elseu8g2_SetDrawColor(&u8g2, 0);u8g2_DrawBox(&u8g2, 0, 0, DISP_W, DISP_H);//u8g2_SetPowerSave(&u8g2, 1);
#endifui.state = S_NONE;  ui.sleep = true;if (eeprom.change){eeprom_write_all_data();eeprom.change = false;}
}//旋钮设置页初始化
void knob_param_init() { check_box_v_init(knob.param); }//旋钮旋转页初始化
void krf_param_init() { check_box_s_init(&knob.param[KNOB_ROT], &knob.param[KNOB_ROT_P]); }//旋钮点按页初始化
void kpf_param_init() { check_box_s_init(&knob.param[KNOB_COD], &knob.param[KNOB_COD_P]); }//电压测量页初始化
void volt_param_init()
{volt.text_bg_r = 0;volt.text_bg_r_trg = VOLT_TEXT_BG_W; 
}//设置页初始化
void setting_param_init()
{check_box_v_init(ui.param);check_box_m_init(ui.param);
}/********************************** 通用初始化函数 *********************************//*页面层级管理逻辑是,把所有页面都先当作列表类初始化,不是列表类按需求再初始化对应函数这样做会浪费一些资源,但跳转页面时只需要考虑页面层级,逻辑上更清晰,减少出错
*///弹窗动画初始化
void window_param_init()
{win.bar = 0;win.y = WIN_Y;win.y_trg = win.u;ui.state = S_NONE;
}//进入更深层级时的初始化
void layer_init_in()
{ui.layer ++;ui.init = 0;list.y = 0;list.y_trg = LIST_LINE_H;list.box_x = 0;list.box_y = 0;list.bar_y = 0;ui.state = S_FADE;switch (ui.index){case M_MAIN:    tile_param_init();    break;  //睡眠进入主菜单,动画初始化   case M_KNOB:    knob_param_init();    break;  //旋钮设置页,行末尾文字初始化case M_KRF:     krf_param_init();     break;  //旋钮旋转页,单选框初始化  case M_KPF:     kpf_param_init();     break;  //旋钮点按页,单选框初始化  case M_VOLT:    volt_param_init();    break;  //主菜单进入电压测量页,动画初始化case M_SETTING: setting_param_init(); break;  //主菜单进入设置页,单选框初始化}
}//进入更浅层级时的初始化
void layer_init_out()
{ui.select[ui.layer] = 0;list.box_y_trg[ui.layer] = 0;ui.layer --;ui.init = 0;list.y = 0;list.y_trg = LIST_LINE_H;list.bar_y = 0;ui.state = S_FADE;switch (ui.index){case M_SLEEP: sleep_param_init(); break;    //主菜单进入睡眠页,检查是否需要写EEPROMcase M_MAIN:  tile_param_init();  break;    //不管什么页面进入主菜单时,动画初始化}
}/************************************* 动画函数 *************************************///动画函数
void animation(float *a, float *a_trg, uint8_t n)
{if (fabs(*a - *a_trg) < 0.15) *a = *a_trg;if (*a != *a_trg) *a += (*a_trg - *a) / (ui.param[n] / 10.0);
}//消失函数
void fade()
{delay(ui.param[FADE_ANI]);if (ui.param[DARK_MODE]){switch (ui.fade){case 1: for (uint16_t i = 0; i < buf_len; ++i)  if (i % 2 != 0) buf_ptr[i] = buf_ptr[i] & 0xAA; break;case 2: for (uint16_t i = 0; i < buf_len; ++i)  if (i % 2 != 0) buf_ptr[i] = buf_ptr[i] & 0x00; break;case 3: for (uint16_t i = 0; i < buf_len; ++i)  if (i % 2 == 0) buf_ptr[i] = buf_ptr[i] & 0x55; 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.fade = 0; break;}}else{switch (ui.fade){case 1: for (uint16_t i = 0; i < buf_len; ++i)  if (i % 2 != 0) buf_ptr[i] = buf_ptr[i] | 0xAA; break;case 2: for (uint16_t i = 0; i < buf_len; ++i)  if (i % 2 != 0) buf_ptr[i] = buf_ptr[i] | 0x00; break;case 3: for (uint16_t i = 0; i < buf_len; ++i)  if (i % 2 == 0) buf_ptr[i] = buf_ptr[i] | 0x55; 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.fade = 0; break;}    }ui.fade++;
}/************************************* 显示函数 *************************************///磁贴类页面通用显示函数
void tile_show(struct MENU arr_1[], const uint8_t icon_pic[][120])
{//计算动画过渡值animation(&tile.icon_x, &tile.icon_x_trg, TILE_ANI);animation(&tile.icon_y, &tile.icon_y_trg, TILE_ANI);animation(&tile.indi_x, &tile.indi_x_trg, TILE_ANI);animation(&tile.title_y, &tile.title_y_trg, TILE_ANI);//设置大标题的颜色,0透显,1实显,2反色,这里用实显//u8g2.setDrawColor(1);u8g2_SetDrawColor(&u8g2, 1);//绘制大标题//u8g2.setFont(TILE_B_FONT); //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);u8g2_SetFont(&u8g2, TILE_B_FONT);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);//绘制大标题指示器//u8g2.drawBox(0, TILE_ICON_S, tile.indi_x, TILE_INDI_H);u8g2_DrawBox(&u8g2, 0, TILE_ICON_S, tile.indi_x, TILE_INDI_H); //绘制图标if (!ui.init){for (uint8_t i = 0; i < ui.num[ui.index]; ++i)  {if (ui.param[TILE_UFD]) tile.temp = (DISP_W - TILE_ICON_W) / 2 + i * tile.icon_x - TILE_ICON_S * ui.select[ui.layer];else tile.temp = (DISP_W - TILE_ICON_W) / 2 + (i - ui.select[ui.layer]) * tile.icon_x;//u8g2.drawXBMP(tile.temp, (int16_t)tile.icon_y, TILE_ICON_W, TILE_ICON_H, icon_pic[i]); u8g2_DrawXBMP(&u8g2, tile.temp, (int16_t)tile.icon_y, TILE_ICON_W, TILE_ICON_H, icon_pic[i]); }if (tile.icon_x == tile.icon_x_trg) {ui.init = true;tile.icon_x = tile.icon_x_trg = - ui.select[ui.layer] * TILE_ICON_S;}}else for (uint8_t i = 0; i < ui.num[ui.index]; ++i) //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]);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]);//反转屏幕内元素颜色,白天模式遮罩//u8g2.setDrawColor(2);u8g2_SetDrawColor(&u8g2, 2);if (!ui.param[DARK_MODE]) //u8g2.drawBox(0, 0, DISP_W, DISP_H);u8g2_DrawBox(&u8g2, 0, 0, DISP_W, DISP_H);
}static u8g2_uint_t u8g2_cursor_x = 0;
static u8g2_uint_t u8g2_cursor_y = 0;static void u8g2_setCursor(u8g2_uint_t x, u8g2_uint_t y) {u8g2_cursor_x = x;u8g2_cursor_y = y;
}static u8g2_uint_t u8g2_print(const char *str) { u8g2_uint_t delta = u8g2_DrawStr(&u8g2, u8g2_cursor_x, u8g2_cursor_y, str);u8g2_cursor_x += delta;if (u8g2_cursor_x > DISP_W) u8g2_cursor_x=0;return delta;
}#define DEC 10
#define HEX 16
#define OCT 8
#define BIN 2
static u8g2_uint_t printNumber(unsigned long n, uint8_t base)
{char buf[8 * sizeof(n) + 1]; // Assumes 8-bit chars plus zero byte.char *str = &buf[sizeof(buf) - 1];*str = '\0';// prevent crash if called with base == 1if(base < 2) {base = 10;}do {char c = n % base;n /= base;*--str = c < 10 ? c + '0' : c + 'A' - 10;} while (n);return u8g2_print(str);
}static u8g2_uint_t printNumber(long n, uint8_t base)
{int t = 0;if (base == 10 && n < 0) {t = u8g2_print("-");n = -n;}return printNumber(static_cast(n), base);
}static u8g2_uint_t u8g2_print(char value) { return printNumber((long)value, DEC);
}static u8g2_uint_t u8g2_print(unsigned char value) { return printNumber((unsigned long)value, DEC);
}static u8g2_uint_t u8g2_print(int value) { return printNumber((long)value, DEC);
}static u8g2_uint_t u8g2_print(unsigned int value) { return printNumber((unsigned long)value, DEC);
}static u8g2_uint_t u8g2_print(long value) { return printNumber(value, DEC);
}static u8g2_uint_t u8g2_print(unsigned long value) { return printNumber(value, DEC);
}static u8g2_uint_t printFloat(double number, uint8_t digits) {u8g2_uint_t n = 0;if(isnan(number)) {return u8g2_print("nan");}if(isinf(number)) {return u8g2_print("inf");}if(number > 4294967040.0) {return u8g2_print("ovf");    // constant determined empirically}if(number < -4294967040.0) {return u8g2_print("ovf");    // constant determined empirically}// Handle negative numbersif(number < 0.0) {n += u8g2_print("-");number = -number;}// Round correctly so that print(1.999, 2) prints as "2.00"double rounding = 0.5;for(uint8_t i = 0; i < digits; ++i) {rounding /= 10.0;}number += rounding;// Extract the integer part of the number and print itunsigned long int_part = (unsigned long) number;double remainder = number - (double) int_part;n += u8g2_print(int_part);// Print the decimal point, but only if there are digits beyondif(digits > 0) {n += u8g2_print(".");}// Extract digits from the remainder one at a timewhile(digits-- > 0) {remainder *= 10.0;int toPrint = int(remainder);n += u8g2_print(toPrint);remainder -= toPrint;}return n;
}static u8g2_uint_t u8g2_print(double value) { return printFloat(value, 3);
}/*************** 根据列表每行开头符号,判断每行尾部是否绘制以及绘制什么内容 *************/
//列表显示数值
void list_draw_value(int n) { //u8g2.print(check_box.v[n - 1]); u8g2_print(check_box.v[n - 1]); 
}//绘制外框
void list_draw_check_box_frame() { //u8g2.drawRFrame(CHECK_BOX_L_S, list.temp + CHECK_BOX_U_S, CHECK_BOX_F_W, CHECK_BOX_F_H, 1);u8g2_DrawRFrame(&u8g2, CHECK_BOX_L_S, list.temp + CHECK_BOX_U_S, CHECK_BOX_F_W, CHECK_BOX_F_H, 1);
}//绘制框里面的点
void list_draw_check_box_dot() { //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); 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); 
}//列表显示旋钮功能
void list_draw_krf(int n) 
{ switch (check_box.v[n - 1]){case 0: u8g2_print("OFF"); break;case 1: u8g2_print("VOL"); break;case 2: u8g2_print("BRI"); break;}
}//列表显示按键键值
void list_draw_kpf(int n) 
{ if (check_box.v[n - 1] == 0) u8g2_print("OFF");else if (check_box.v[n - 1] <= 90) u8g2_print((char)check_box.v[n - 1]);else u8g2_print("?");
}//判断列表尾部内容
void list_draw_text_and_check_box(struct MENU arr[], int i)
{//u8g2.drawStr(LIST_TEXT_S, list.temp + LIST_TEXT_H + LIST_TEXT_S, arr[i].m_select);//u8g2.setCursor(CHECK_BOX_L_S, list.temp + LIST_TEXT_H + LIST_TEXT_S);u8g2_DrawStr(&u8g2, LIST_TEXT_S, list.temp + LIST_TEXT_H + LIST_TEXT_S, arr[i].m_select);u8g2_setCursor(CHECK_BOX_L_S, list.temp + LIST_TEXT_H + LIST_TEXT_S);switch (arr[i].m_select[0]){case '~': list_draw_value(i); break;case '+': list_draw_check_box_frame(); if (check_box.m[i - 1] == 1)  list_draw_check_box_dot(); break;case '=': list_draw_check_box_frame(); if (*check_box.s_p == i)      list_draw_check_box_dot(); break;case '#': list_draw_krf(i);   break;case '$': list_draw_kpf(i);   break;}
}/******************************** 列表显示函数 **************************************///列表类页面通用显示函数
void list_show(struct MENU arr[], uint8_t ui_index)
{//更新动画目标值//u8g2.setFont(LIST_FONT);u8g2_SetFont(&u8g2, LIST_FONT);//list.box_x_trg = u8g2.getStrWidth(arr[ui.select[ui.layer]].m_select) + LIST_TEXT_S * 2;list.box_x_trg = u8g2_GetStrWidth(&u8g2, arr[ui.select[ui.layer]].m_select) + LIST_TEXT_S * 2; list.bar_y_trg = ceil((ui.select[ui.layer]) * ((float)DISP_H / (ui.num[ui_index] - 1)));//计算动画过渡值animation(&list.y, &list.y_trg, LIST_ANI);animation(&list.box_x, &list.box_x_trg, LIST_ANI);animation(&list.box_y, &list.box_y_trg[ui.layer], LIST_ANI);animation(&list.bar_y, &list.bar_y_trg, LIST_ANI);//检查循环动画是否结束if (list.loop && list.box_y == list.box_y_trg[ui.layer]) list.loop = false;//设置文字和进度条颜色,0透显,1实显,2反色,这里都用实显//u8g2.setDrawColor(1);u8g2_SetDrawColor(&u8g2, 1);//绘制进度条#if 0u8g2.drawHLine(DISP_W - LIST_BAR_W, 0, LIST_BAR_W);u8g2.drawHLine(DISP_W - LIST_BAR_W, DISP_H - 1, LIST_BAR_W);u8g2.drawVLine(DISP_W - ceil((float)LIST_BAR_W / 2), 0, DISP_H);u8g2.drawBox(DISP_W - LIST_BAR_W, 0, LIST_BAR_W, list.bar_y);#elseu8g2_DrawHLine(&u8g2, DISP_W - LIST_BAR_W, 0, LIST_BAR_W);u8g2_DrawHLine(&u8g2, DISP_W - LIST_BAR_W, DISP_H - 1, LIST_BAR_W);u8g2_DrawVLine(&u8g2, DISP_W - ceil((float)LIST_BAR_W / 2), 0, DISP_H);u8g2_DrawBox(&u8g2, DISP_W - LIST_BAR_W, 0, LIST_BAR_W, list.bar_y);#endif//绘制列表文字  if (!ui.init){for (int i = 0; i < ui.num[ui_index]; ++ i){if (ui.param[LIST_UFD]) list.temp = i * list.y - LIST_LINE_H * ui.select[ui.layer] + list.box_y_trg[ui.layer];else list.temp = (i - ui.select[ui.layer]) * list.y + list.box_y_trg[ui.layer];list_draw_text_and_check_box(arr, i);}if (list.y == list.y_trg) {ui.init = true;list.y = list.y_trg = - LIST_LINE_H * ui.select[ui.layer] + list.box_y_trg[ui.layer];}}else for (int i = 0; i < ui.num[ui_index]; ++ i){list.temp = LIST_LINE_H * i + list.y;list_draw_text_and_check_box(arr, i);}//绘制文字选择框,0透显,1实显,2反色,这里用反色//u8g2.setDrawColor(2);//u8g2.drawRBox(0, list.box_y, list.box_x, LIST_LINE_H, LIST_BOX_R);u8g2_SetDrawColor(&u8g2, 2);u8g2_DrawRBox(&u8g2, 0, list.box_y, list.box_x, LIST_LINE_H, LIST_BOX_R);//反转屏幕内元素颜色,白天模式遮罩,在这里屏蔽有列表参与的页面,使遮罩作用在那个页面上if (!ui.param[DARK_MODE]){//u8g2.drawBox(0, 0, DISP_W, DISP_H);u8g2_DrawBox(&u8g2, 0, 0, DISP_W, DISP_H);switch(ui.index){case M_WINDOW: case M_VOLT://u8g2.drawBox(0, 0, DISP_W, DISP_H);  u8g2_DrawBox(&u8g2, 0, 0, DISP_W, DISP_H);  }}
}//电压页面显示函数
void volt_show()
{//使用列表类显示选项list_show(volt_menu, M_VOLT); //计算动画过渡值  animation(&volt.text_bg_r, &volt.text_bg_r_trg, TAG_ANI);//设置曲线颜色,0透显,1实显,2反色,这里用实显//u8g2.setDrawColor(1); u8g2_SetDrawColor(&u8g2, 1);//绘制电压曲线和外框volt.val = 0;//u8g2.drawFrame(WAVE_BOX_L_S, 0, WAVE_BOX_W, WAVE_BOX_H);//u8g2.drawFrame(WAVE_BOX_L_S + 1, 1, WAVE_BOX_W - 2, WAVE_BOX_H - 2);u8g2_DrawFrame(&u8g2, WAVE_BOX_L_S, 0, WAVE_BOX_W, WAVE_BOX_H);u8g2_DrawFrame(&u8g2, WAVE_BOX_L_S + 1, 1, WAVE_BOX_W - 2, WAVE_BOX_H - 2);if (list.box_y == list.box_y_trg[ui.layer] && list.y == list.y_trg){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]]);for (int i = 1; i < WAVE_W - 1; i++){ //volt.ch0_wave[i] = lv_map(volt.ch0_adc[int(5 * i)], 0, 4095, WAVE_MAX, WAVE_MIN);   //u8g2.drawLine(WAVE_L + i - 1, WAVE_U + volt.ch0_wave[i - 1], WAVE_L + i, WAVE_U + volt.ch0_wave[i]);volt.ch0_wave[i] = lv_map(volt.ch0_adc[int(5 * i)], -5000, 5000, WAVE_MIN, WAVE_MAX); u8g2_DrawLine(&u8g2, WAVE_L + i - 1, WAVE_U + volt.ch0_wave[i - 1], WAVE_L + i, WAVE_U + volt.ch0_wave[i]);}}#if 0//绘制电压值u8g2.setFontDirection(0);u8g2.setFont(VOLT_FONT); u8g2.setCursor(39, DISP_H - 6);u8g2.print(volt.val / 4096.0 * 3.3);u8g2.print("V");//绘制列表选择框和电压文字背景u8g2.setDrawColor(2);u8g2.drawBox(VOLT_TEXT_BG_L_S, DISP_H - VOLT_TEXT_BG_H, volt.text_bg_r, VOLT_TEXT_BG_H);//反转屏幕内元素颜色,白天模式遮罩if (!ui.param[DARK_MODE]) u8g2.drawBox(0, 0, DISP_W, DISP_H);
#else//绘制电压值u8g2_SetFontDirection(&u8g2, 0);u8g2_SetFont(&u8g2, VOLT_FONT); u8g2_setCursor(32, DISP_H - 6);//u8g2_print(volt.val / 4096.0 * 3.3);//u8g2_print(volt.val/1000.0);//u8g2_print(motor_b.pos_spd_ltd.v2/1000.0);u8g2_print(volt.val);//u8g2_SetFont(&u8g2, LIST_FONT); u8g2_print("V");//绘制列表选择框和电压文字背景u8g2_SetDrawColor(&u8g2, 2);u8g2_DrawBox(&u8g2, VOLT_TEXT_BG_L_S, DISP_H - VOLT_TEXT_BG_H, volt.text_bg_r, VOLT_TEXT_BG_H);//反转屏幕内元素颜色,白天模式遮罩if (!ui.param[DARK_MODE]) u8g2_DrawBox(&u8g2, 0, 0, DISP_W, DISP_H);
#endif
}//弹窗通用显示函数
void window_show()
{//绘制背景列表,根据开关判断背景是否要虚化list_show(win.bg, win.index);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);//更新动画目标值//u8g2.setFont(WIN_FONT);u8g2_SetFont(&u8g2, WIN_FONT);win.bar_trg = (float)(*win.value - win.min) / (float)(win.max - win.min) * (WIN_BAR_W - 4);//计算动画过渡值animation(&win.bar, &win.bar_trg, WIN_ANI);animation(&win.y, &win.y_trg, WIN_ANI);#if 0//绘制窗口u8g2.setDrawColor(0); u8g2.drawRBox(win.l, (int16_t)win.y, WIN_W, WIN_H, 2);    //绘制外框背景u8g2.setDrawColor(1); u8g2.drawRFrame(win.l, (int16_t)win.y, WIN_W, WIN_H, 2);  //绘制外框描边u8g2.drawRFrame(win.l + 5, (int16_t)win.y + 20, WIN_BAR_W, WIN_BAR_H, 1);       //绘制进度条外框u8g2.drawBox(win.l + 7, (int16_t)win.y + 22, win.bar, WIN_BAR_H - 4);           //绘制进度条u8g2.setCursor(win.l + 5, (int16_t)win.y + 14); u8g2.print(win.title);          //绘制标题u8g2.setCursor(win.l + 78, (int16_t)win.y + 14); u8g2.print(*win.value);        //绘制当前值//需要在窗口修改参数时立即见效的函数if (!strcmp(win.title, "Disp Bri")) u8g2.setContrast(ui.param[DISP_BRI]);//反转屏幕内元素颜色,白天模式遮罩u8g2.setDrawColor(2);if (!ui.param[DARK_MODE]) u8g2.drawBox(0, 0, DISP_W, DISP_H);
#else//绘制窗口u8g2_SetDrawColor(&u8g2, 0); u8g2_DrawRBox(&u8g2, win.l, (int16_t)win.y, WIN_W, WIN_H, 2);	//绘制外框背景u8g2_SetDrawColor(&u8g2, 1); u8g2_DrawRFrame(&u8g2, win.l, (int16_t)win.y, WIN_W, WIN_H, 2);	//绘制外框描边u8g2_DrawRFrame(&u8g2, win.l + 5, (int16_t)win.y + 20, WIN_BAR_W, WIN_BAR_H, 1);		//绘制进度条外框u8g2_DrawBox(&u8g2, win.l + 7, (int16_t)win.y + 22, win.bar, WIN_BAR_H - 4);			//绘制进度条u8g2_setCursor(win.l + 5, (int16_t)win.y + 14); u8g2_print(win.title);			//绘制标题u8g2_setCursor(win.l + 78, (int16_t)win.y + 14); u8g2_print(*win.value);		//绘制当前值//需要在窗口修改参数时立即见效的函数//if (!strcmp(win.title, "Disp Bri")) u8g2_SetContrast(&u8g2, ui.param[DISP_BRI]);//反转屏幕内元素颜色,白天模式遮罩u8g2_SetDrawColor(&u8g2, 2);if (!ui.param[DARK_MODE]) u8g2_DrawBox(&u8g2, 0, 0, DISP_W, DISP_H);#endif
}/************************************* 处理函数 *************************************//*********************************** 通用处理函数 ***********************************///磁贴类页面旋转时判断通用函数
void tile_rotate_switch()
{switch (btn.id){ case BTN_ID_CC:if (ui.init){if (ui.select[ui.layer] > 0){ui.select[ui.layer] -= 1;tile.icon_x_trg += TILE_ICON_S;tile.select_flag = false;}else {if (ui.param[TILE_LOOP]){ui.select[ui.layer] = ui.num[ui.index] - 1;tile.icon_x_trg = - TILE_ICON_S * (ui.num[ui.index] - 1);break;}else tile.select_flag = true;}}break;case BTN_ID_CW:if (ui.init){if (ui.select[ui.layer] < (ui.num[ui.index] - 1)) {ui.select[ui.layer] += 1;tile.icon_x_trg -= TILE_ICON_S;tile.select_flag = false;}else {if (ui.param[TILE_LOOP]){ui.select[ui.layer] = 0;tile.icon_x_trg = 0;break;}else tile.select_flag = true;}}break;}
}//列表类页面旋转时判断通用函数
void list_rotate_switch()
{if (!list.loop){switch (btn.id){case BTN_ID_CC:if (ui.select[ui.layer] == 0){if (ui.param[LIST_LOOP] && ui.init){list.loop = true;ui.select[ui.layer] = ui.num[ui.index] - 1;if (ui.num[ui.index] > list.line_n) {list.box_y_trg[ui.layer] = DISP_H - LIST_LINE_H;list.y_trg = DISP_H - ui.num[ui.index] * LIST_LINE_H;}else list.box_y_trg[ui.layer] = (ui.num[ui.index] - 1) * LIST_LINE_H;break;}else break;}if (ui.init){ui.select[ui.layer] -= 1;if (ui.select[ui.layer] < - (list.y_trg / LIST_LINE_H)) {if (!(DISP_H % LIST_LINE_H)) list.y_trg += LIST_LINE_H;else{if (list.box_y_trg[ui.layer] == DISP_H - LIST_LINE_H * list.line_n){list.y_trg += (list.line_n + 1) * LIST_LINE_H - DISP_H;list.box_y_trg[ui.layer] = 0;}else if (list.box_y_trg[ui.layer] == LIST_LINE_H){list.box_y_trg[ui.layer] = 0;}else list.y_trg += LIST_LINE_H;}}else list.box_y_trg[ui.layer] -= LIST_LINE_H;break;}case BTN_ID_CW:if (ui.select[ui.layer] == (ui.num[ui.index] - 1)){if (ui.param[LIST_LOOP] && ui.init){list.loop = true;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) > (list.line_n - list.y_trg / LIST_LINE_H)){if (!(DISP_H % LIST_LINE_H)) list.y_trg -= LIST_LINE_H;else{if (list.box_y_trg[ui.layer] == LIST_LINE_H * (list.line_n - 1)){list.y_trg -= (list.line_n + 1) * LIST_LINE_H - DISP_H;list.box_y_trg[ui.layer] = DISP_H - LIST_LINE_H;}else if (list.box_y_trg[ui.layer] == DISP_H - LIST_LINE_H * 2){list.box_y_trg[ui.layer] = DISP_H - LIST_LINE_H;}else list.y_trg -= LIST_LINE_H;}}else list.box_y_trg[ui.layer] += LIST_LINE_H;break;}break;}}
}//弹窗通用处理函数
void window_proc()
{window_show();if (win.y == WIN_Y_TRG) ui.index = win.index;if (btn.pressed && win.y == win.y_trg && win.y != WIN_Y_TRG){btn.pressed = false;switch (btn.id){case BTN_ID_CC: if (*win.value < win.max)  *win.value += win.step;  eeprom.change = true;  break;case BTN_ID_CW: if (*win.value > win.min)  *win.value -= win.step;  eeprom.change = true;  break;  case BTN_ID_SP: case BTN_ID_LP: win.y_trg = WIN_Y_TRG; break;}}
}/********************************** 分页面处理函数 **********************************///睡眠页面处理函数
void sleep_proc()
{while (ui.sleep){//睡眠时循环执行的函数//睡眠时需要扫描旋钮才能退出睡眠btn_scan();delay(1);//当旋钮有动作时if (btn.pressed) { btn.pressed = false; switch (btn.id) {    
#if 0//睡眠时顺时针旋转执行的函数case BTN_ID_CW:switch (knob.param[KNOB_ROT]){case KNOB_ROT_VOL: Consumer.press(HIDConsumer::VOLUME_UP);       Consumer.release(); break;case KNOB_ROT_BRI: Consumer.press(HIDConsumer::BRIGHTNESS_UP);   Consumer.release(); break;}break;//睡眠时逆时针旋转执行的函数case BTN_ID_CC:switch (knob.param[KNOB_ROT]){case KNOB_ROT_VOL: Consumer.press(HIDConsumer::VOLUME_DOWN);     Consumer.release(); break;case KNOB_ROT_BRI: Consumer.press(HIDConsumer::BRIGHTNESS_DOWN); Consumer.release(); break;}break;//睡眠时短按执行的函数case BTN_ID_SP: Keyboard.press(knob.param[KNOB_COD]);  Keyboard.release(knob.param[KNOB_COD]); break;   
#endif        //睡眠时长按执行的函数//case BTN_ID_LP: ui.index = M_MAIN;  ui.state = S_LAYER_IN; u8g2.setPowerSave(0); ui.sleep = false; break;case BTN_ID_LP: ui.index = M_MAIN;  ui.state = S_LAYER_IN; /*u8g2_SetPowerSave(&u8g2, 0);*/ ui.sleep = false; break;}}}
}//主菜单处理函数,磁贴类模板
void main_proc()
{tile_show(main_menu, main_icon_pic);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]) {case 0: ui.index = M_SLEEP;   ui.state = S_LAYER_OUT; break;case 1: ui.index = M_EDITOR;  ui.state = S_LAYER_IN;  break;case 2: ui.index = M_VOLT;    ui.state = S_LAYER_IN;  break;case 3: ui.index = M_SETTING; ui.state = S_LAYER_IN;  break;}}if (!tile.select_flag && ui.init) { tile.indi_x = 0; tile.title_y = tile.title_y_calc; }}
}//编辑器菜单处理函数
void editor_proc()
{list_show(editor_menu, M_EDITOR); 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]) {case 0:   ui.index = M_MAIN;  ui.state = S_LAYER_OUT; break;case 11:  ui.index = M_KNOB;  ui.state = S_LAYER_IN;  break;}}}
}//旋钮编辑菜单处理函数
void knob_proc()
{list_show(knob_menu, M_KNOB);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]) {case 0: ui.index = M_EDITOR;  ui.state = S_LAYER_OUT; break;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;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;}}}
}//旋钮旋转功能菜单处理函数
void krf_proc()
{list_show(krf_menu, M_KRF);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]) {case 0: ui.index = M_KNOB;  ui.state = S_LAYER_OUT; break;case 1: break;case 2: check_box_s_select(KNOB_DISABLE, ui.select[ui.layer]); break;case 3: break;case 4: check_box_s_select(KNOB_ROT_VOL, ui.select[ui.layer]); break;case 5: check_box_s_select(KNOB_ROT_BRI, ui.select[ui.layer]); break;case 6: break;}}}
}#define KEY_LEFT_CTRL   0x80
#define KEY_LEFT_SHIFT  0x81
#define KEY_LEFT_ALT    0x82
#define KEY_LEFT_GUI    0x83
#define KEY_RIGHT_CTRL  0x84
#define KEY_RIGHT_SHIFT 0x85
#define KEY_RIGHT_ALT   0x86
#define KEY_RIGHT_GUI   0x87#define KEY_UP_ARROW    0xDA
#define KEY_DOWN_ARROW  0xD9
#define KEY_LEFT_ARROW  0xD8
#define KEY_RIGHT_ARROW 0xD7
#define KEY_BACKSPACE   0xB2
#define KEY_TAB         0xB3
#define KEY_RETURN      0xB0
#define KEY_ESC         0xB1
#define KEY_INSERT      0xD1
#define KEY_DELETE      0xD4
#define KEY_PAGE_UP     0xD3
#define KEY_PAGE_DOWN   0xD6
#define KEY_HOME        0xD2
#define KEY_END         0xD5
#define KEY_CAPS_LOCK   0xC1
#define KEY_F1          0xC2
#define KEY_F2          0xC3
#define KEY_F3          0xC4
#define KEY_F4          0xC5
#define KEY_F5          0xC6
#define KEY_F6          0xC7
#define KEY_F7          0xC8
#define KEY_F8          0xC9
#define KEY_F9          0xCA
#define KEY_F10         0xCB
#define KEY_F11         0xCC
#define KEY_F12         0xCD
#define KEY_F13         0xF0
#define KEY_F14         0xF1
#define KEY_F15         0xF2
#define KEY_F16         0xF3
#define KEY_F17         0xF4
#define KEY_F18         0xF5
#define KEY_F19         0xF6
#define KEY_F20         0xF7
#define KEY_F21         0xF8
#define KEY_F22         0xF9
#define KEY_F23         0xFA
#define KEY_F24         0xFB//旋钮点按功能菜单处理函数
void kpf_proc()
{list_show(kpf_menu, M_KPF);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]) {case 0:   ui.index = M_KNOB;  ui.state = S_LAYER_OUT; break;case 1:   break;case 2:   check_box_s_select(KNOB_DISABLE, ui.select[ui.layer]); break;case 3:   break;case 4:   check_box_s_select('A', ui.select[ui.layer]); break;case 5:   check_box_s_select('B', ui.select[ui.layer]); break;case 6:   check_box_s_select('C', ui.select[ui.layer]); break;case 7:   check_box_s_select('D', ui.select[ui.layer]); break;case 8:   check_box_s_select('E', ui.select[ui.layer]); break;case 9:   check_box_s_select('F', ui.select[ui.layer]); break;case 10:  check_box_s_select('G', ui.select[ui.layer]); break;case 11:  check_box_s_select('H', ui.select[ui.layer]); break;case 12:  check_box_s_select('I', ui.select[ui.layer]); break;case 13:  check_box_s_select('J', ui.select[ui.layer]); break;case 14:  check_box_s_select('K', ui.select[ui.layer]); break;case 15:  check_box_s_select('L', ui.select[ui.layer]); break;case 16:  check_box_s_select('M', ui.select[ui.layer]); break;case 17:  check_box_s_select('N', ui.select[ui.layer]); break;case 18:  check_box_s_select('O', ui.select[ui.layer]); break;case 19:  check_box_s_select('P', ui.select[ui.layer]); break;case 20:  check_box_s_select('Q', ui.select[ui.layer]); break;case 21:  check_box_s_select('R', ui.select[ui.layer]); break;case 22:  check_box_s_select('S', ui.select[ui.layer]); break;case 23:  check_box_s_select('T', ui.select[ui.layer]); break;case 24:  check_box_s_select('U', ui.select[ui.layer]); break;case 25:  check_box_s_select('V', ui.select[ui.layer]); break;case 26:  check_box_s_select('W', ui.select[ui.layer]); break;case 27:  check_box_s_select('X', ui.select[ui.layer]); break;case 28:  check_box_s_select('Y', ui.select[ui.layer]); break;case 29:  check_box_s_select('Z', ui.select[ui.layer]); break;case 30:  break;case 31:  check_box_s_select('0', ui.select[ui.layer]); break;case 32:  check_box_s_select('1', ui.select[ui.layer]); break;case 33:  check_box_s_select('2', ui.select[ui.layer]); break;case 34:  check_box_s_select('3', ui.select[ui.layer]); break;case 35:  check_box_s_select('4', ui.select[ui.layer]); break;case 36:  check_box_s_select('5', ui.select[ui.layer]); break;case 37:  check_box_s_select('6', ui.select[ui.layer]); break;case 38:  check_box_s_select('7', ui.select[ui.layer]); break;case 39:  check_box_s_select('8', ui.select[ui.layer]); break;case 40:  check_box_s_select('9', ui.select[ui.layer]); break;case 41:  break;case 42:  check_box_s_select( KEY_ESC, ui.select[ui.layer]); break;case 43:  check_box_s_select( KEY_F1,  ui.select[ui.layer]); break;case 44:  check_box_s_select( KEY_F2,  ui.select[ui.layer]); break;case 45:  check_box_s_select( KEY_F3,  ui.select[ui.layer]); break;case 46:  check_box_s_select( KEY_F4,  ui.select[ui.layer]); break;case 47:  check_box_s_select( KEY_F5,  ui.select[ui.layer]); break;case 48:  check_box_s_select( KEY_F6,  ui.select[ui.layer]); break;case 49:  check_box_s_select( KEY_F7,  ui.select[ui.layer]); break;case 50:  check_box_s_select( KEY_F8,  ui.select[ui.layer]); break;case 51:  check_box_s_select( KEY_F9,  ui.select[ui.layer]); break;case 52:  check_box_s_select( KEY_F10, ui.select[ui.layer]); break;case 53:  check_box_s_select( KEY_F11, ui.select[ui.layer]); break;case 54:  check_box_s_select( KEY_F12, ui.select[ui.layer]); break;case 55:  break;case 56:  check_box_s_select( KEY_LEFT_CTRL,   ui.select[ui.layer]); break;case 57:  check_box_s_select( KEY_LEFT_SHIFT,  ui.select[ui.layer]); break;case 58:  check_box_s_select( KEY_LEFT_ALT,    ui.select[ui.layer]); break;case 59:  check_box_s_select( KEY_LEFT_GUI,    ui.select[ui.layer]); break;case 60:  check_box_s_select( KEY_RIGHT_CTRL,  ui.select[ui.layer]); break;case 61:  check_box_s_select( KEY_RIGHT_SHIFT, ui.select[ui.layer]); break;case 62:  check_box_s_select( KEY_RIGHT_ALT,   ui.select[ui.layer]); break;case 63:  check_box_s_select( KEY_RIGHT_GUI,   ui.select[ui.layer]); break;case 64:  break;case 65:  check_box_s_select( KEY_CAPS_LOCK,   ui.select[ui.layer]); break;case 66:  check_box_s_select( KEY_BACKSPACE,   ui.select[ui.layer]); break;case 67:  check_box_s_select( KEY_RETURN,      ui.select[ui.layer]); break;case 68:  check_box_s_select( KEY_INSERT,      ui.select[ui.layer]); break;case 69:  check_box_s_select( KEY_DELETE,      ui.select[ui.layer]); break;case 70:  check_box_s_select( KEY_TAB,         ui.select[ui.layer]); break;case 71:  break;case 72:  check_box_s_select( KEY_HOME,        ui.select[ui.layer]); break;case 73:  check_box_s_select( KEY_END,         ui.select[ui.layer]); break;case 74:  check_box_s_select( KEY_PAGE_UP,     ui.select[ui.layer]); break;case 75:  check_box_s_select( KEY_PAGE_DOWN,   ui.select[ui.layer]); break;case 76:  break;case 77:  check_box_s_select( KEY_UP_ARROW,    ui.select[ui.layer]); break;case 78:  check_box_s_select( KEY_DOWN_ARROW,  ui.select[ui.layer]); break;case 79:  check_box_s_select( KEY_LEFT_ARROW,  ui.select[ui.layer]); break;case 80:  check_box_s_select( KEY_RIGHT_ARROW, ui.select[ui.layer]); break;case 81:  break;}}}
}//电压测量页处理函数
void volt_proc()
{volt_show();if (btn.pressed) { btn.pressed = false; switch (btn.id) { case BTN_ID_CW: case BTN_ID_CC: list_rotate_switch(); break;case BTN_ID_SP: case BTN_ID_LP: ui.index = M_MAIN;  ui.state = S_LAYER_OUT; break;}}
}//设置菜单处理函数,多选框列表类模板,弹窗模板
void setting_proc()
{list_show(setting_menu, M_SETTING);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]) {//返回更浅层级,长按被当作选择这一项,也是执行这一行case 0:   ui.index = M_MAIN;  ui.state = S_LAYER_OUT; break;//弹出窗口,参数初始化:标题,参数名,参数值,最大值,最小值,步长,背景列表名,背景列表标签case 1:   window_value_init("Disp Bri", DISP_BRI, &ui.param[DISP_BRI],  255,  0,  5, setting_menu, M_SETTING);  break;case 2:   window_value_init("Tile Ani", TILE_ANI, &ui.param[TILE_ANI],  100, 10,  1, setting_menu, M_SETTING);  break;case 3:   window_value_init("List Ani", LIST_ANI, &ui.param[LIST_ANI],  100, 10,  1, setting_menu, M_SETTING);  break;case 4:   window_value_init("Win Ani",  WIN_ANI,  &ui.param[WIN_ANI],   100, 10,  1, setting_menu, M_SETTING);  break;case 5:   window_value_init("Spot Ani", SPOT_ANI, &ui.param[SPOT_ANI],  100, 10,  1, setting_menu, M_SETTING);  break;case 6:   window_value_init("Tag Ani",  TAG_ANI,  &ui.param[TAG_ANI],   100, 10,  1, setting_menu, M_SETTING);  break;case 7:   window_value_init("Fade Ani", FADE_ANI, &ui.param[FADE_ANI],  255,  0,  1, setting_menu, M_SETTING);  break;case 8:   window_value_init("Btn SPT",  BTN_SPT,  &ui.param[BTN_SPT],   255,  0,  1, setting_menu, M_SETTING);  break;case 9:   window_value_init("Btn LPT",  BTN_LPT,  &ui.param[BTN_LPT],   255,  0,  1, setting_menu, M_SETTING);  break;//多选框case 10:  check_box_m_select( TILE_UFD  );  break;case 11:  check_box_m_select( LIST_UFD  );  break;case 12:  check_box_m_select( TILE_LOOP );  break;case 13:  check_box_m_select( LIST_LOOP );  break;case 14:  check_box_m_select( WIN_BOK   );  break;case 15:  check_box_m_select( KNOB_DIR  );  break;case 16:  check_box_m_select( DARK_MODE );  break;//关于本机case 17:  ui.index = M_ABOUT; ui.state = S_LAYER_IN; break;}}}
}//关于本机页
void about_proc()
{list_show(about_menu, M_ABOUT);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]) {case 0:   ui.index = M_SETTING;  ui.state = S_LAYER_OUT; break;}}}
}//总的UI进程
void ui_proc()
{//u8g2.sendBuffer();//u8g2_SendBuffer(&u8g2);//u8g2_FirstPage(&u8g2);switch (ui.state){case S_FADE:          fade();                   break;  //转场动画case S_WINDOW:        window_param_init();      break;  //弹窗初始化case S_LAYER_IN:      layer_init_in();          break;  //层级初始化case S_LAYER_OUT:     layer_init_out();         break;  //层级初始化case S_NONE: u8g2_ClearBuffer(&u8g2);//u8g2.clearBuffer(); switch (ui.index)      //直接选择页面{case M_WINDOW:      window_proc();            break;case M_SLEEP:       sleep_proc();             break;case M_MAIN:        main_proc();              break;case M_EDITOR:      editor_proc();            break;case M_KNOB:        knob_proc();              break;case M_KRF:         krf_proc();               break;case M_KPF:         kpf_proc();               break;case M_VOLT:        volt_proc();              break;case M_SETTING:     setting_proc();           break;case M_ABOUT:       about_proc();             break;}}u8g2_NextPage(&u8g2);
}//OLED初始化函数
void oled_init()
{//u8g2.setBusClock(1000000);  //硬件IIC接口使用//u8g2.begin();//u8g2.setContrast(ui.param[DISP_BRI]);u8g2_SetContrast(&u8g2, ui.param[DISP_BRI]);//buf_ptr = u8g2.getBufferPtr();//buf_len = 8 * u8g2.getBufferTileHeight() * u8g2.getBufferTileWidth();buf_ptr = u8g2_GetBufferPtr(&u8g2);buf_len = 8 * u8g2_GetBufferTileHeight(&u8g2) * u8g2_GetBufferTileWidth(&u8g2);
}void flex_ui_setup() 
{eeprom_init();ui_init();oled_init();btn_init();ESP_LOGI(TAG, "flex ui setup!");
}void flex_ui_loop() 
{btn_scan();knob_inter();ui_proc();
}

~小结:

         这次主要记录了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资料):

                             

                                                        燥起来吧!!!!!! 


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部