arduino-esp32:LVGL项目(二)初始化以及控制模块

又隔了一段时间了,上次发还是7月3号,正好两个月了🙃。
这篇简单过一下初始化的部分和控制模块。
想要全部代码的话问我就行。

初始化

代码如下:

void setup()
{
    /* 调试串口 */
    Serial.begin( 115200 );
    /* 无线串口 */
    Serial2.begin(460800);
    /* 背光PWM调节引脚 */
    ledcAttachPin(BL_PIN, 8);
    ledcSetup(8, 100, 8);
    ledcWrite(8, 128);
    /* 关闭可能的wifi连接 */
    WiFi.disconnect(false, true);
    WiFi.mode(WIFI_OFF);
    /* 初始化LVGL */
    lv_init();

#if LV_USE_LOG != 0
    lv_log_register_print_cb( my_print ); /* register print function for debugging */
#endif

    tft.begin();          /* TFT init */
    tft.setRotation( 1 ); /* Landscape orientation, flipped */

    /*Set the touchscreen calibration data,
     the actual data for your display can be aquired using
     the Generic -> Touch_calibrate example from the TFT_eSPI library*/
    uint16_t calData[5] = { 140, 3773, 282, 3550, 7 };
    tft.setTouch( calData );

    lv_disp_draw_buf_init( &draw_buf, buf1, NULL, screenWidth * 10 );

    /*Initialize the display*/
    static lv_disp_drv_t disp_drv;
    lv_disp_drv_init( &disp_drv );
    /*Change the following line to your display resolution*/
    disp_drv.hor_res = screenWidth;
    disp_drv.ver_res = screenHeight;
    disp_drv.flush_cb = my_disp_flush;
    disp_drv.draw_buf = &draw_buf;
    lv_disp_drv_register( &disp_drv );

    /*Initialize the (dummy) input device driver*/
    static lv_indev_drv_t indev_drv;
    lv_indev_drv_init( &indev_drv );
    indev_drv.type = LV_INDEV_TYPE_POINTER;
    indev_drv.read_cb = my_touchpad_read;
    lv_indev_drv_register( &indev_drv );
    /* Create simple label */
#if 0
    lv_obj_t *label = lv_label_create( lv_scr_act() );
    lv_label_set_text( label, LVGL_Arduino.c_str() );
    lv_obj_align( label, LV_ALIGN_CENTER, 0, 0 );

#else
    main_page();
#endif    
    Serial.println( "Setup done" );
}

arduino的初始化从setup()函数开始,除了对引入的LVGL进行初始化以外,还需要对一些用到的外设进行配置。
首先是对两个uart做初始化,一个用于调试,一个用于无线通信。调试的波特率设的挺正常的,无线通信的为了速度快点,设到了NRF模块能接受的最高波特率460800,8N1配置下传1个字节大概19.5us。
然后对背光IO初始化,这里使用了22引脚。

#define BL_PIN 22

LEDC是ESP32的一个LED PWM控制器,我们这里就是用来控制背光亮度的。
在这里插入图片描述
用起来很简单,首先将通道8绑定到上面定义的22引脚上,接下来对通道8的操作就会作用到22引脚了。然后配置一下通道8的频率和分辨率,频率可以简单的理解为该通道一秒会给出多少次驱动脉冲,分辨率对我们来说,就是一个范围。例如我这里代码设置的是8,那么控制这个通道的亮度范围就是0-(2^8-1),也就是0-255。例如我们想控制这个通道对应的引脚的灯灭,就通过ledcWrite往通道里面写0,控制这个通道对应的引脚的灯最亮,就通过ledcWrite往通道里面写255。这里初始化为128,也就是亮度为50%。

    /* 背光PWM调节引脚 */
    ledcAttachPin(BL_PIN, 8);
    ledcSetup(8, 100, 8);
    ledcWrite(8, 128);

在初始化完成之后,就会调用main_page()函数来对三个主界面进行初始化。
main_page()代码如下:

void main_page(void)
{
    /* 初始化tab栏样式 */
    lv_style_init(&style_tab_view);
    lv_style_set_bg_opa(&style_tab_view, LV_OPA_COVER);
    lv_style_set_bg_color(&style_tab_view, lv_palette_lighten(LV_PALETTE_LIGHT_BLUE, 4));
    /* 设置颜色梯度,实际效果不佳 */
//    lv_style_set_bg_grad_color(&style_tab_view, lv_color_white());
//    lv_style_set_bg_grad_dir(&style_tab_view, LV_GRAD_DIR_HOR);
    /* 设置tab滚动文字栏样式 */
    lv_style_init(&style_tab_view_text);
    lv_style_set_text_color(&style_tab_view_text, lv_palette_main(LV_PALETTE_RED));
    /* 创建tab */
    tv = lv_tabview_create(lv_scr_act(), LV_DIR_TOP, 50);
    /* 在tab左侧创建固定文字区 */
    lv_obj_t * tab_btn = lv_tabview_get_tab_btns(tv);
	lv_obj_set_style_text_font(tab_btn, &myfont_16, 0);
    lv_obj_add_style(tab_btn, &style_tab_view, 0);
    lv_obj_set_style_pad_left(tab_btn, LV_HOR_RES / 2, 0);
    lv_obj_t * logo = lv_img_create(tab_btn);
    lv_obj_set_style_radius(logo, LV_RADIUS_CIRCLE, 0);
    LV_IMG_DECLARE(NCHU);
    lv_img_set_src(logo, &NCHU);
    lv_obj_align(logo, LV_ALIGN_LEFT_MID, -LV_HOR_RES / 2 + 25, 0);

    lv_obj_t * label = lv_label_create(tab_btn);
    lv_label_set_text(label, "LVGL V8");
    lv_obj_align_to(label, logo, LV_ALIGN_OUT_RIGHT_TOP, 10, 0);

    label = lv_label_create(tab_btn);
    lv_label_set_text(label, "ESP32");
    lv_obj_align_to(label, logo, LV_ALIGN_OUT_RIGHT_BOTTOM, 10 ,0);

    mode_label = lv_label_create(tab_btn);
    lv_obj_add_style(mode_label, &style_tab_view_text, 0);
    lv_label_set_long_mode(mode_label, LV_LABEL_LONG_SCROLL_CIRCULAR);
    lv_obj_set_width(mode_label, 75);
    lv_label_set_text(mode_label, "模式待选择...");
    lv_obj_align_to(mode_label, logo, LV_ALIGN_OUT_RIGHT_MID, 85, 0);

    lv_obj_t * t1 = lv_tabview_add_tab(tv, "主界面");
    lv_obj_t * t2 = lv_tabview_add_tab(tv, "分析");
    lv_obj_t * t3 = lv_tabview_add_tab(tv, "配置");

    main_create(t1);
    analytics_create(t2);
    config_create(t3);

    static const char * btns[] ={"手动", "自动", ""};
    lv_obj_t * mode_box = lv_msgbox_create(NULL, "模式选择", "请选择小车运行模式:)", btns, false);
    lv_obj_set_style_text_font(mode_box, &myfont_16, 0);
    lv_obj_add_event_cb(mode_box, mb_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
    lv_obj_center(mode_box);
    
}

其实大部分是参考的官方demo。首先创建tab栏。顶部的是固定不变的,就是下图上面这一行。
在这里插入图片描述
样式是LVGL里最重要的模块之一。首先对一个样式初始化,然后设置样式的背景颜色。这里在设置背景颜色之前必须要调用
lv_style_set_bg_opa函数将背景的透明度设置一下。因为默认的透明度是0,也就是完全透明,不设置一下这个就完全看不见了自己配的颜色了。

    /* 初始化tab栏样式 */
    lv_style_init(&style_tab_view);
    lv_style_set_bg_opa(&style_tab_view, LV_OPA_COVER);
    lv_style_set_bg_color(&style_tab_view, lv_palette_lighten(LV_PALETTE_LIGHT_BLUE, 4));

在创建了对应的控件后,就可以通过lv_obj_add_style函数将初始化好的样式添加到对应的控件上,这样就可以对控件实现各种各样的效果。

    /* 创建tab */
    tv = lv_tabview_create(lv_scr_act(), LV_DIR_TOP, 50);
    /* 在tab左侧创建固定文字区 */
    lv_obj_t * tab_btn = lv_tabview_get_tab_btns(tv);
	lv_obj_set_style_text_font(tab_btn, &myfont_16, 0);
    lv_obj_add_style(tab_btn, &style_tab_view, 0);

这里创建了一个图片控件,将本地的一个图像文件导入进来,也就是上图中左上角的那个我们学校的校徽。这个也是官网提供的图片转换工具,后面单独讲。

    lv_obj_t * logo = lv_img_create(tab_btn);
    lv_obj_set_style_radius(logo, LV_RADIUS_CIRCLE, 0);
    LV_IMG_DECLARE(NCHU);
    lv_img_set_src(logo, &NCHU);

其他就是创建一些文本控件显示信息,还有设置它们的位置。然后通过lv_tabview_add_tab函数给tab控件添加三个子tab窗口,这样就可以实现不同窗口的功能分区了。

    lv_obj_t * t1 = lv_tabview_add_tab(tv, "主界面");
    lv_obj_t * t2 = lv_tabview_add_tab(tv, "分析");
    lv_obj_t * t3 = lv_tabview_add_tab(tv, "配置");

通过三个单独的函数对这三个界面进行初始渲染。

    main_create(t1);
    analytics_create(t2);
    config_create(t3);

三个界面完成后就创建一个弹出框magbox,用于限制用户必须选择功能。

    static const char * btns[] ={"手动", "自动", ""};
    lv_obj_t * mode_box = lv_msgbox_create(NULL, "模式选择", "请选择小车运行模式:)", btns, false);
    lv_obj_set_style_text_font(mode_box, &myfont_16, 0);
    lv_obj_add_event_cb(mode_box, mb_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
    lv_obj_center(mode_box);

也就是这个框。可以看到这里是没有取消按钮的,所以在没有选择其中一个按钮之前,用户没办法对这个系统做任何操作。lv_msgbox_create函数的最后一个形参可以用来选择是否添加取消按钮,选择false就不会添加取消按钮。
在这里插入图片描述

控制模块

整个界面的代码如下:

static void main_create(lv_obj_t * parent)
{
    lv_obj_set_style_bg_opa(parent, LV_OPA_COVER, 0);
    lv_obj_set_style_bg_color(parent, lv_palette_lighten(LV_PALETTE_LIGHT_BLUE, 4), 0);
    lv_obj_set_style_bg_grad_color(parent, lv_palette_lighten(LV_PALETTE_DEEP_PURPLE, 4), 0);
    lv_obj_set_style_bg_grad_dir(parent, LV_GRAD_DIR_VER, 0);
    lv_obj_set_style_bg_grad_stop(parent, 160, 0);
    /* 按键动画 */
    static const lv_style_prop_t props[] = {LV_STYLE_BG_COLOR, LV_STYLE_BORDER_COLOR, LV_STYLE_BORDER_WIDTH, (lv_style_prop_t)0};
    static lv_style_transition_dsc_t trans_def;
    lv_style_transition_dsc_init(&trans_def, props, lv_anim_path_linear, 100, 200, NULL);
    static lv_style_transition_dsc_t trans_pr;
    lv_style_transition_dsc_init(&trans_pr, props, lv_anim_path_linear, 500, 0, NULL);
    /* 控制按钮样式 */
    lv_style_init(&style_control_btn);
    lv_style_set_border_color(&style_control_btn, lv_color_white());
    lv_style_set_border_width(&style_control_btn, 2);
    lv_style_set_shadow_width(&style_control_btn, 10);
    lv_style_set_shadow_ofs_x(&style_control_btn, 4);
    lv_style_set_shadow_ofs_y(&style_control_btn, 4);
    lv_style_set_shadow_opa(&style_control_btn, LV_OPA_60);
    lv_style_set_radius(&style_control_btn, LV_RADIUS_CIRCLE);
    lv_style_set_transition(&style_control_btn, &trans_def);
    /* 按键按下后样式 */
    lv_style_init(&style_control_btn_pr);
    lv_style_set_bg_color(&style_control_btn_pr, lv_palette_main(LV_PALETTE_RED));
    lv_style_set_border_width(&style_control_btn_pr, 6);
    lv_style_set_border_color(&style_control_btn_pr, lv_palette_darken(LV_PALETTE_RED, 3));
    lv_style_set_transition(&style_control_btn_pr, &trans_pr);
    /* 图像panel样式 */
    lv_style_init(&style_image_panel);
    lv_style_set_border_width(&style_image_panel, 2);
    lv_style_set_border_opa(&style_image_panel, LV_OPA_COVER);
    lv_style_set_border_color(&style_image_panel, lv_palette_main(LV_PALETTE_GREY));
    lv_style_set_radius(&style_image_panel, 10);
    lv_style_set_bg_opa(&style_image_panel, LV_OPA_0);
    /* 图像外框样式 */
    lv_style_init(&style_outline);
    lv_style_set_bg_opa(&style_outline, LV_OPA_80);
    lv_style_set_bg_color(&style_outline, lv_color_white());
    /* 控制小车按钮panel */
    lv_obj_t * control_panel = lv_obj_create(parent);
    lv_obj_set_width(control_panel, LV_SIZE_CONTENT);
    lv_obj_set_height(control_panel, LV_PCT(100));
    lv_obj_set_style_radius(control_panel, 10, 0);
    lv_obj_set_style_border_color(control_panel, lv_palette_main(LV_PALETTE_GREY), 0);
    lv_obj_set_style_bg_opa(control_panel, LV_OPA_0, 0);
    /* 控制器上方的label标识 */
    lv_obj_t * controler_label = lv_label_create(control_panel);
    lv_obj_set_style_text_font(controler_label, &myfont_16, 0);
    lv_label_set_text(controler_label, "控制器");
    /* 前进按钮 */
    lv_obj_t* btn_forward = lv_btn_create(control_panel);
    lv_obj_set_height(btn_forward, LV_SIZE_CONTENT);
    lv_obj_set_size(btn_forward, control_btn_rad, control_btn_rad);
    lv_obj_add_style(btn_forward, &style_control_btn, 0);
    lv_obj_add_style(btn_forward, &style_control_btn_pr, LV_STATE_PRESSED);
    lv_obj_add_event_cb(btn_forward, control_btn_event_cb, LV_EVENT_ALL, &user_data1);

    lv_obj_t * control_label = lv_label_create(btn_forward);
    lv_label_set_text(control_label, LV_SYMBOL_UP);
    lv_obj_center(control_label);
    /* 后退按钮 */
    lv_obj_t* btn_back = lv_btn_create(control_panel);
    lv_obj_set_height(btn_back, LV_SIZE_CONTENT);
    lv_obj_set_size(btn_back, control_btn_rad, control_btn_rad);
    lv_obj_add_style(btn_back, &style_control_btn, 0);
    lv_obj_add_style(btn_back, &style_control_btn_pr, LV_STATE_PRESSED);
    lv_obj_add_event_cb(btn_back, control_btn_event_cb, LV_EVENT_ALL, &user_data2);
    
    control_label = lv_label_create(btn_back);
    lv_label_set_text(control_label, LV_SYMBOL_DOWN);
    lv_obj_center(control_label);
    /* 左转按钮 */
    lv_obj_t* btn_left = lv_btn_create(control_panel);
    lv_obj_set_height(btn_left, LV_SIZE_CONTENT);
    lv_obj_set_size(btn_left, control_btn_rad, control_btn_rad);
    lv_obj_add_style(btn_left, &style_control_btn, 0);
    lv_obj_add_style(btn_left, &style_control_btn_pr, LV_STATE_PRESSED);
    lv_obj_add_event_cb(btn_left, control_btn_event_cb, LV_EVENT_ALL, &user_data3);

    control_label = lv_label_create(btn_left);
    lv_label_set_text(control_label, LV_SYMBOL_LEFT);
    lv_obj_center(control_label);    
    /* 右转按钮 */
    lv_obj_t* btn_right = lv_btn_create(control_panel);
    lv_obj_set_height(btn_right, LV_SIZE_CONTENT);
    lv_obj_set_size(btn_right, control_btn_rad, control_btn_rad);
    lv_obj_add_style(btn_right, &style_control_btn, 0);
    lv_obj_add_style(btn_right, &style_control_btn_pr, LV_STATE_PRESSED);
    lv_obj_add_event_cb(btn_right, control_btn_event_cb, LV_EVENT_ALL, &user_data4);

    control_label = lv_label_create(btn_right);
    lv_label_set_text(control_label, LV_SYMBOL_RIGHT);
    lv_obj_center(control_label);
    /* 图像显示panel */
    lv_obj_t * image_panel = lv_obj_create(parent);
    lv_obj_remove_style_all(image_panel);
    lv_obj_set_width(image_panel, LV_PCT(100));
    lv_obj_set_height(image_panel, LV_PCT(100));
    lv_obj_add_style(image_panel, &style_image_panel, 0);
    /* 图像的外框 */
    lv_obj_t * iamge_outline = lv_obj_create(image_panel);
    lv_obj_remove_style_all(iamge_outline);
    lv_obj_set_size(iamge_outline, 188, 120);
    lv_obj_add_style(iamge_outline, &style_outline, 0);
    /* 图像的内框 */
    track_line_left = lv_line_create(iamge_outline);
    lv_obj_set_style_line_color(track_line_left, lv_palette_main(LV_PALETTE_RED), 0);

    track_line_right = lv_line_create(iamge_outline);
    lv_obj_set_style_line_color(track_line_right, lv_palette_main(LV_PALETTE_RED), 0);
    /* 图像上方的label标识 */
    lv_obj_t * iamge_label = lv_label_create(image_panel);
    lv_obj_set_style_text_font(iamge_label, &myfont_16, 0);
    lv_label_set_text(iamge_label, "图像");
    /* 整体布局 */
    static lv_coord_t col_dsc[] = { LV_GRID_CONTENT, LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST };
    static lv_coord_t row_dsc[] = { LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST };
    /* image框布局 */
    static lv_coord_t grid_1_col_dsc[] = { 11, LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST };
    static lv_coord_t grid_1_row_dsc[] = { 30, LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST };
    /* 控制框布局 */
    static lv_coord_t grid_2_col_dsc[] = { 48, 48, 48, LV_GRID_TEMPLATE_LAST };
    static lv_coord_t grid_2_row_dsc[] = { 10, 48, 48, 48, LV_GRID_TEMPLATE_LAST };

    lv_obj_set_grid_dsc_array(parent, col_dsc, row_dsc);
    lv_obj_set_grid_cell(control_panel, LV_GRID_ALIGN_START, 0, 1, LV_GRID_ALIGN_START, 0, 1);
    lv_obj_set_grid_dsc_array(control_panel, grid_2_col_dsc, grid_2_row_dsc);
    lv_obj_set_grid_cell(btn_forward, LV_GRID_ALIGN_START, 1, 1, LV_GRID_ALIGN_START, 1, 1);
    lv_obj_set_grid_cell(btn_left, LV_GRID_ALIGN_START, 0, 1, LV_GRID_ALIGN_START, 2, 1);
    lv_obj_set_grid_cell(btn_right, LV_GRID_ALIGN_START, 2, 1, LV_GRID_ALIGN_START, 2, 1);
    lv_obj_set_grid_cell(btn_back, LV_GRID_ALIGN_START, 1, 1, LV_GRID_ALIGN_START, 3, 1);    

    lv_obj_set_grid_cell(image_panel, LV_GRID_ALIGN_STRETCH, 1, 1, LV_GRID_ALIGN_STRETCH, 0, 1);
    lv_obj_set_grid_dsc_array(image_panel, grid_1_col_dsc, grid_1_row_dsc);
    lv_obj_set_grid_cell(iamge_label, LV_GRID_ALIGN_START, 1, 1, LV_GRID_ALIGN_END, 0, 1);
    lv_obj_set_grid_cell(iamge_outline, LV_GRID_ALIGN_CENTER, 1, 1, LV_GRID_ALIGN_CENTER, 1, 1);

}

可以通过这一段代码来实现颜色的渐变,首先设置背景颜色,然后通过lv_obj_set_style_bg_grad_color函数来设置想要渐变成什么颜色,lv_obj_set_style_bg_grad_dir函数可以设置渐变的方向是竖直还是横向,lv_obj_set_style_bg_grad_stop可以设置两种颜色的分割线的坐标。

    lv_obj_set_style_bg_opa(parent, LV_OPA_COVER, 0);
    lv_obj_set_style_bg_color(parent, lv_palette_lighten(LV_PALETTE_LIGHT_BLUE, 4), 0);
    lv_obj_set_style_bg_grad_color(parent, lv_palette_lighten(LV_PALETTE_DEEP_PURPLE, 4), 0);
    lv_obj_set_style_bg_grad_dir(parent, LV_GRAD_DIR_VER, 0);
    lv_obj_set_style_bg_grad_stop(parent, 160, 0);

可以通过这一段代码来设置动画,动画也是LVGL的一大特色。lv_style_transition_dsc_init第四个参数代表动画所经历的时间,第五个参数代表动画在转变之前的延时时间。

    /* 按键动画 */
    static const lv_style_prop_t props[] = {LV_STYLE_BG_COLOR, LV_STYLE_BORDER_COLOR, LV_STYLE_BORDER_WIDTH, (lv_style_prop_t)0};
    static lv_style_transition_dsc_t trans_def;
    lv_style_transition_dsc_init(&trans_def, props, lv_anim_path_linear, 100, 200, NULL);
    static lv_style_transition_dsc_t trans_pr;
    lv_style_transition_dsc_init(&trans_pr, props, lv_anim_path_linear, 500, 0, NULL);

通过lv_style_set_transition将动画的句柄配置进对应的样式中,在按下之后就可以呈现出所配置的动画效果。比如这里配置了默认的样式,然后又配置了按下后的样式,那么在按下后,LVGL就会根据上文代码中的lv_style_transition_dsc_init的第四个和第五个参数来对默认样式到按下后样式进行转换。

    /* 按键按下后样式 */
    lv_style_init(&style_control_btn_pr);
    lv_style_set_bg_color(&style_control_btn_pr, lv_palette_main(LV_PALETTE_RED));
    lv_style_set_border_width(&style_control_btn_pr, 6);
    lv_style_set_border_color(&style_control_btn_pr, lv_palette_darken(LV_PALETTE_RED, 3));
    lv_style_set_transition(&style_control_btn_pr, &trans_pr);

每个按钮初始化之后绑定同一个回调函数,通过传入的参数来判别按钮类型,然后在回调函数中进行处理。

    /* 前进按钮 */
    lv_obj_t* btn_forward = lv_btn_create(control_panel);
    lv_obj_set_height(btn_forward, LV_SIZE_CONTENT);
    lv_obj_set_size(btn_forward, control_btn_rad, control_btn_rad);
    lv_obj_add_style(btn_forward, &style_control_btn, 0);
    lv_obj_add_style(btn_forward, &style_control_btn_pr, LV_STATE_PRESSED);
    lv_obj_add_event_cb(btn_forward, control_btn_event_cb, LV_EVENT_ALL, &user_data1);

    lv_obj_t * control_label = lv_label_create(btn_forward);
    lv_label_set_text(control_label, LV_SYMBOL_UP);
    lv_obj_center(control_label);
    /* 后退按钮 */
    lv_obj_t* btn_back = lv_btn_create(control_panel);
    lv_obj_set_height(btn_back, LV_SIZE_CONTENT);
    lv_obj_set_size(btn_back, control_btn_rad, control_btn_rad);
    lv_obj_add_style(btn_back, &style_control_btn, 0);
    lv_obj_add_style(btn_back, &style_control_btn_pr, LV_STATE_PRESSED);
    lv_obj_add_event_cb(btn_back, control_btn_event_cb, LV_EVENT_ALL, &user_data2);
    
    control_label = lv_label_create(btn_back);
    lv_label_set_text(control_label, LV_SYMBOL_DOWN);
    lv_obj_center(control_label);
    /* 左转按钮 */
    lv_obj_t* btn_left = lv_btn_create(control_panel);
    lv_obj_set_height(btn_left, LV_SIZE_CONTENT);
    lv_obj_set_size(btn_left, control_btn_rad, control_btn_rad);
    lv_obj_add_style(btn_left, &style_control_btn, 0);
    lv_obj_add_style(btn_left, &style_control_btn_pr, LV_STATE_PRESSED);
    lv_obj_add_event_cb(btn_left, control_btn_event_cb, LV_EVENT_ALL, &user_data3);

    control_label = lv_label_create(btn_left);
    lv_label_set_text(control_label, LV_SYMBOL_LEFT);
    lv_obj_center(control_label);    
    /* 右转按钮 */
    lv_obj_t* btn_right = lv_btn_create(control_panel);
    lv_obj_set_height(btn_right, LV_SIZE_CONTENT);
    lv_obj_set_size(btn_right, control_btn_rad, control_btn_rad);
    lv_obj_add_style(btn_right, &style_control_btn, 0);
    lv_obj_add_style(btn_right, &style_control_btn_pr, LV_STATE_PRESSED);
    lv_obj_add_event_cb(btn_right, control_btn_event_cb, LV_EVENT_ALL, &user_data4);

    control_label = lv_label_create(btn_right);
    lv_label_set_text(control_label, LV_SYMBOL_RIGHT);
    lv_obj_center(control_label);

最后是一个布局配置。怎么把上面所有的控件放在我们想要的位置上呢?如果只通过相对位置或绝对位置来一个一个的配置,也可以,但是这样的话当控件比较多时,要修改某个控件的时候,就需要对所有其他的也进行修改,非常麻烦、所以这里我们使用一种常见的LVGL也拥有的布局格式:网格布局。
如其名,网格布局就是把一块区域分割成网格,然后把控件填充进对应的网格里面,实现该区域控件的总体管理。
首先设置整体的布局,控制模块所在的tab页面有两个子区域:控制模块和显示模块。按照下面的代码把这一整块tab页面分成了一个左右两列的区域。

当lv_coord_t修饰的变量或常量作为布局类型时,必须要在布局分割结束的最后加上LV_GRID_TEMPLATE_LAST。

    /* 整体布局 */
    static lv_coord_t col_dsc[] = { LV_GRID_CONTENT, LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST };
    static lv_coord_t row_dsc[] = { LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST };

这样分完之后的结果就是这样:
在这里插入图片描述
之后针对控制框进行网格分配。下面的代码会把这个子界面分成一个四行三列的网格。前面的数字代表着这一行或这一列占据的长度或宽度。

    /* 控制框布局 */
    static lv_coord_t grid_2_col_dsc[] = { 48, 48, 48, LV_GRID_TEMPLATE_LAST };
    static lv_coord_t grid_2_row_dsc[] = { 10, 48, 48, 48, LV_GRID_TEMPLATE_LAST };

这样分完之后,效果如下图:
在这里插入图片描述
接下来使用这段代码就可以把对应的控件填充进对应的网格里了。首先需要使用lv_obj_set_grid_dsc_array来配置接下来的lv_obj_set_grid_cell函数的作用域,也就是调用了lv_obj_set_grid_dsc_array函数之后,接下来的网格配置操作lv_obj_set_grid_cell就会基于lv_obj_set_grid_dsc_array中的父网格。也就是上图中左边那一块四行三列的网格。所以配置网格前一定要调用一次lv_obj_set_grid_dsc_array来选择父网格。
lv_obj_set_grid_cell第一个形参是控件句柄,第二个形参是当前控件在列方向上,即竖直方向的对齐方式。第三个参数代表当前控件所放置在这个网格里面的多少列,第四个参数代表该控件占据多少列网格位置。第五第六第七个参数和前面一样,但是代表的是在水平方面的三个指标。

    lv_obj_set_grid_dsc_array(control_panel, grid_2_col_dsc, grid_2_row_dsc);
    lv_obj_set_grid_cell(btn_forward, LV_GRID_ALIGN_START, 1, 1, LV_GRID_ALIGN_START, 1, 1);
    lv_obj_set_grid_cell(btn_left, LV_GRID_ALIGN_START, 0, 1, LV_GRID_ALIGN_START, 2, 1);
    lv_obj_set_grid_cell(btn_right, LV_GRID_ALIGN_START, 2, 1, LV_GRID_ALIGN_START, 2, 1);
    lv_obj_set_grid_cell(btn_back, LV_GRID_ALIGN_START, 1, 1, LV_GRID_ALIGN_START, 3, 1);

例如这里的四个按钮,根据上面的代码所放置在的位置和占位如下图。首先btn_forward控件,看lv_obj_set_grid_cell函数的第三个和第六个形参,可以找到防止的位置,即(1,1);btn_left位置为(0,2);btn_right位置为(2,2);btn_back位置为(1,3)。他们占对应方向的占位网格长度都是1。.

在这里插入图片描述
至于占位网格长度,举个例子,如果上述代码的btn_forward控件改为下面的代码,即列上的占位长度为2,那么最后的效果会像下图这样。

lv_obj_set_grid_cell(btn_forward, LV_GRID_ALIGN_START, 1, 2, LV_GRID_ALIGN_START, 1, 1);

在这里插入图片描述
总结一下就是,lv_obj_set_grid_cell(obj, align_l, l, ls, align_r, r, rs)函数会将obj控件安放在父网格布局下的对应位置,位置信息计算如下:在竖向上,开始位置为参数l,占据长度为l * ls,对齐方式为align_l;在横向上,开始位置为参数r,占据长度为r * rs,对齐方式为align_r;最后的控件所占据的网格范围就是从(l,r)到(l * ls,r * rs)。

小结

大概的流程就是这样,写了几个我认为比较重要的点,其他的代码还是挺好理解的。这样至少以后要用的话不会忘记这几个函数是干嘛的🙃。
效果可以去arduino-esp32:LVGL项目(一)整体框架这里看。

  • 4
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 18
    评论
### 回答1: Arduino-ESP32ModbusRTU是一个基于Arduino平台和ESP32芯片的Modbus RTU通信协议库。Modbus RTU是一种常用的串行通信协议,用于在不同设备之间实现通信和数据交换。ESP32是一款强大的Wi-Fi和蓝牙芯片,具有丰富的资源和功能。 利用Arduino-ESP32ModbusRTU库,我们可以轻松地在ESP32上实现Modbus RTU通信功能。通过这个库,我们可以将ESP32作为Modbus RTU的主机或从机,并与其他Modbus RTU设备进行通信。在主机模式下,ESP32可以向其他设备发送Modbus RTU命令,并接收其响应。在从机模式下,ESP32可以接收其他设备发送的Modbus RTU命令,并作出相应的响应。 使用Arduino-ESP32ModbusRTU库,我们可以使用简单的代码实现Modbus RTU通信功能。我们可以指定串口用于与其他设备进行通信,并设置自己的Modbus地址。我们可以使用库提供的函数来读取和写入寄存器或线圈,以及进行其他Modbus操作。 Arduino-ESP32ModbusRTU库还支持异步通信功能,可以在不阻塞主线程的情况下进行Modbus通信操作。这使得我们可以同时处理其他任务,而不会影响Modbus通信的性能和稳定性。 总之,Arduino-ESP32ModbusRTU是一个方便实用的库,可以帮助我们在ESP32上实现Modbus RTU通信功能。无论是作为主机还是从机,通过这个库,我们可以轻松地与其他Modbus RTU设备进行数据交换和通信。 ### 回答2: arduino-esp32modbusrtu是一种基于Arduino开发平台和ESP32控制器的Modbus RTU通信库。Modbus是一种通信协议,常用于工业自动化领域中的设备间通信。 arduino-esp32modbusrtu库为ESP32提供了实现Modbus RTU通信的功能,可以让ESP32作为Modbus RTU主机或从机来与其他设备进行通信。通过该库,用户可以轻松地对Modbus RTU数据进行读取和写入。 使用arduino-esp32modbusrtu库,用户首先需要在Arduino开发平台上导入该库,并在代码中包含相应的头文件。然后,需要设置串口参数,并创建一个Modbus RTU对象。用户可以根据需要选择将ESP32配置为主机或从机,并指定Modbus设备的地址。 在主机模式下,用户可以使用ModbusRTUMaster类的方法来发送读取或写入请求,并获取设备的响应。用户可以指定读取或写入的寄存器地址以及读取的数量或写入的值。在从机模式下,用户可以使用ModbusRTUSlave类的方法来处理主机的请求,并返回相应的数据。 arduino-esp32modbusrtu库提供了许多示例代码和详细的文档,帮助用户快速上手,并实现其Modbus RTU通信需求。使用该库,用户可以自定义设置通信参数、处理各种Modbus功能码,并与其他Modbus RTU设备进行稳定可靠的通信。 总之,arduino-esp32modbusrtu是一款强大而灵活的Modbus RTU通信库,为基于ESP32项目提供了方便快捷的Modbus功能支持。 ### 回答3: Arduino-ESP32ModbusRTU是一种基于ESP32控制器的Modbus RTU通信协议库。该库允许使用Arduino编程语言和开发工具与Modbus RTU设备进行通信。 Modbus RTU是一种在串行通信介质上实现的开放式通信协议。使用Modbus RTU协议,可以实现数据在不同设备之间的传输和控制。通常,Modbus RTU是在RS485物理层上实现的,允许多个设备共享通信线路。 Arduino-ESP32ModbusRTU库通过处理Modbus RTU帧格式,实现了Master和Slave两种角色的操作。作为Master,ESP32可以通过发送请求到Slave设备来读取或写入数据。而作为Slave,ESP32可以接收Master设备发送的请求,并根据请求进行数据读取或写入。 使用Arduino-ESP32ModbusRTU库,我们可以通过Arduino编程语言轻松地实现Modbus RTU通信。我们可以设定串行通信参数(如波特率、数据位、停止位等),并使用预定义的函数来读取或写入Modbus寄存器中的数据。此外,该库还支持不同种类的Modbus寄存器,如输入寄存器、保持寄存器、线圈和离散输入寄存器。 总结而言,Arduino-ESP32ModbusRTU库为我们提供了一种简单而高效的方式来实现ESP32与其他Modbus RTU设备之间的通信。无论是作为Master还是Slave,ESP32都可以通过这个库与Modbus RTU设备进行数据的读取和写入。这个库的使用使得我们可以很方便地将ESP32应用于各种Modbus RTU通信场景中,如工业自动化、设备监控等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值