Funpack2-3 基于ESP32-E的lvgl图形库和应用
本片文章分享我在Funpack第二季第三期活动中使用ESP32-E开发板进行lvgl图形库和应用开发的流程及效果展示
标签
嵌入式系统
Funpack2-3
ESP32-E
反正都一样
更新2023-01-04
514

FrWUzvIqBZM7ns-DpnkmEQG3Vo57

项目介绍

这里是我参加Funpack第二季第三期活动的任务总结报告,我所完成的是任务二,lvgl图形库和应用,我将使用开发板不断读取加速度传感器的数据,并通过LVGL图形库显示的屏幕上。

 

主要硬件介绍

FireBeetle ESP32-E是一款基于ESP-WROOM-32E双核芯片的主控板,它专为IoT设计。它支持WIFI和蓝牙双模通信并具有体积小巧、超低功耗、板载充电电路、接口易用等特性。可灵活的用于家庭物联网改装、工业物联网改装、可穿戴设备等等。

Fi4OCnuQVej6aIVSFIQA0O37gqRv

显示屏,虽说开发板上有一个GDI显示接口,但我并没有使用,用的是中景园家的一款2.4寸LCD屏幕,驱动芯片为ILI9341,屏幕触摸芯片为tp2046。详细屏幕资料,大家可以到淘宝上进行搜索

MPU6050,该传感器也是DFrobot家的,非常经典的一款。在本次任务中,就是不断读取它的数据后,并显示。

 

主要软件介绍

                                    FpfxJoJ7Rdvx4pSjQBk3eexvP_hD

在软件开发上,虽然官方建议的是arduino的方式,但我还是熟悉使用ESP-IDF的方式进行,能够操作较底层,且速度比较快。

首先就是LVGL的移植了,网上有很多教程,大家可以去搜索使用,需要注意的是,LVGl有多个版本,目前的8.3版本是最新的,对esp32适配较好,当然7.几的版本也是可以使用,也是使用较广泛的一个版本。

Fm41FEpirE7JyqR08I1lB29Z2L_F

两个箭头分别是显示跟外部输入的移植文件,这两个完成后,还需要主函数中创建一个定时器,定时执行lvgl的心跳lv_tick_inc,该函数是为了是lvgl知道过去了多少的时间

  static void lv_tick_task(void *arg)
{
	(void)arg;
	lv_tick_inc(10);
}
   const esp_timer_create_args_t periodic_timer_args = {
        .callback = &lv_tick_task,
        .name = "periodic_gui"};
    esp_timer_handle_t periodic_timer;
    ESP_ERROR_CHECK(esp_timer_create(&periodic_timer_args, &periodic_timer));
    ESP_ERROR_CHECK(esp_timer_start_periodic(periodic_timer, 10 * 1000));

之后就可以像画画一样,添加要显示的组件了,按钮,标签,进度条等

    lv_obj_t* label01 = lv_label_create(lv_scr_act());
    lv_label_set_text(label01, "xyz accel value");
    lv_obj_align(label01, LV_ALIGN_TOP_MID, 0,10);

    lv_obj_t* bar_x = lv_bar_create(lv_scr_act());
    lv_obj_t* bar_y = lv_bar_create(lv_scr_act());
    lv_obj_t* bar_z = lv_bar_create(lv_scr_act());

    lv_obj_align(bar_x, LV_ALIGN_TOP_MID,-40,40);
    lv_obj_align(bar_y, LV_ALIGN_TOP_MID,-40,80);
    lv_obj_align(bar_z, LV_ALIGN_TOP_MID,-40,120);
    //////////////////////////////////////////////////////
    lv_obj_t* label_x = lv_label_create(lv_scr_act());
    lv_label_set_text(label_x, "x");
    lv_obj_align_to(label_x, bar_x, LV_ALIGN_OUT_BOTTOM_MID, 0, 5);

    lv_obj_t* label_y = lv_label_create(lv_scr_act());
    lv_label_set_text(label_y, "y");
    lv_obj_align_to(label_y, bar_y, LV_ALIGN_OUT_BOTTOM_MID, 0, 5);

    lv_obj_t* label_z = lv_label_create(lv_scr_act());
    lv_label_set_text(label_z, "z");
    lv_obj_align_to(label_z, bar_z, LV_ALIGN_OUT_BOTTOM_MID, 0, 5);
    ///////////////////////////////////////////////////////
    lv_obj_t* label_x_value = lv_label_create(lv_scr_act());
    lv_label_set_text(label_x_value, "0");
    lv_obj_align_to(label_x_value, bar_x, LV_ALIGN_OUT_RIGHT_MID, -45, 0);

    lv_obj_t* label_y_value = lv_label_create(lv_scr_act());
    lv_label_set_text(label_y_value, "0");
    lv_obj_align_to(label_y_value, bar_y, LV_ALIGN_OUT_RIGHT_MID, -45, 0);

    lv_obj_t* label_z_value = lv_label_create(lv_scr_act());
    lv_label_set_text(label_z_value, "0");
    lv_obj_align_to(label_z_value, bar_z, LV_ALIGN_OUT_RIGHT_MID, -45, 0);

    /* 设置大小 */
    // 可以不设置,使用默认大小
    lv_obj_set_size(bar_x, 150, 15);
    lv_obj_set_size(bar_y, 150,15);
    lv_obj_set_size(bar_z, 150, 15);

    lv_bar_set_mode(bar_x, LV_BAR_MODE_SYMMETRICAL);
    lv_bar_set_mode(bar_y, LV_BAR_MODE_SYMMETRICAL);
    lv_bar_set_mode(bar_z, LV_BAR_MODE_SYMMETRICAL);

    lv_bar_set_range(bar_x, -98, 98);
    lv_bar_set_range(bar_y, -98, 98);
    lv_bar_set_range(bar_z, -98, 98);


    lv_obj_t* btn = lv_btn_create(lv_scr_act());
    lv_obj_align(btn, LV_ALIGN_CENTER, 0, 60);
    lv_obj_t* btn_label = lv_label_create(btn);
    lv_obj_add_event_cb(btn, button_clicked_cb, LV_EVENT_CLICKED, label01);
    lv_label_set_text(btn_label, "toggle");
    lv_obj_center(btn_label);

MPU6050初始化配置

void mpu6050_init(){
    uint8_t data;
    int i2c_master_port = I2C_MASTER_NUM;

    i2c_config_t conf = {
        .mode = I2C_MODE_MASTER,
        .sda_io_num = I2C_MASTER_SDA_IO,
        .scl_io_num = I2C_MASTER_SCL_IO,
        .sda_pullup_en = GPIO_PULLUP_ENABLE,
        .scl_pullup_en = GPIO_PULLUP_ENABLE,
        .master.clk_speed = I2C_MASTER_FREQ_HZ,
    };

    i2c_param_config(i2c_master_port, &conf);

    i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);

    ESP_ERROR_CHECK(mpu6050_register_read(MPU9250_WHO_AM_I_REG_ADDR, &data, 1));

    printf("WHO_AM_I = 0x%X\n", data);
    //传感器复位
    mpu6050_register_write_byte(MPU6050_RA_PWR_MGMT_1,0x80);
    vTaskDelay(pdMS_TO_TICKS(100));
    //配置时钟
    mpu6050_register_write_byte(MPU6050_RA_PWR_MGMT_1,0x01);
    //陀螺仪范围250
    mpu6050_register_write_byte(MPU6050_RA_GYRO_CONFIG,0x00);
    _gyroScale = 250.0f/32768.0f;
    //加速度范围2g
    mpu6050_register_write_byte(MPU6050_RA_ACCEL_CONFIG,0x00);
    _accelScale = 2.0f/32768.0f;
    //配置6轴都工作
    mpu6050_register_write_byte(MPU6050_RA_PWR_MGMT_2,0x00);
    //关闭所有中断
    mpu6050_register_write_byte(MPU6050_RA_INT_ENABLE,0x00);
    //设置MPU6050的内部采样频率以及低通滤波器
    mpu6050_register_write_byte(MPU6050_RA_SMPLRT_DIV,0x00);
    mpu6050_register_write_byte(MPU6050_RA_CONFIG,0x04);
}

使用的是I2C通信,并对时钟,范围,采样频率,滤波器的寄存器进行相应配置即可。

void GValue2Angle(float *G_data, float *Angle)
{
    float Gdata[3];

    Gdata[0] = G_data[0];
    Gdata[1] = G_data[1];
    Gdata[2] = G_data[2];

    Angle[0] = atan(Gdata[0] / sqrt(Gdata[1]*Gdata[1] + Gdata[2]*Gdata[2]))*180/PI;

    Angle[1] = atan(Gdata[1] / sqrt(Gdata[0]*Gdata[0]+Gdata[2]*Gdata[2]))*180/PI;

    Angle[2] = atan( Gdata[2] / sqrt(Gdata[0]*Gdata[0] +Gdata[1]*Gdata[1]))*180/PI;
}
void mpu6050_rawdata(float *acc,float *gyr,float *ang){
    uint8_t data[14];
    int16_t _accCounts[3] = {0};
    int16_t _gyroCounts[3] = {0};
    int16_t _tcounts = 0;
    mpu6050_register_read(MPU6050_RA_ACCEL_XOUT_H, data, 14);
	_accCounts[0] = (((int16_t)data[0]) << 8) | data[1];
	_accCounts[1] = (((int16_t)data[2]) << 8) | data[3];
	_accCounts[2] = (((int16_t)data[4]) << 8) | data[5];
	_tcounts = (((int16_t)data[6]) << 8) | data[7];
	_gyroCounts[0] = (((int16_t)data[8]) << 8) | data[9];
	_gyroCounts[1] = (((int16_t)data[10]) << 8) | data[11];
	_gyroCounts[2] = (((int16_t)data[12]) << 8) | data[13];
	
	acc[0]=_accCounts[0]*9.8*_accelScale;
	acc[1]=_accCounts[1]*9.8*_accelScale;
	acc[2]=_accCounts[2]*9.8*_accelScale;

    GValue2Angle(acc,ang);
}

上面就是对加速度传感器读取原始数据,和对原始数据进行计算得出姿态角的函数了

    while(1)
    {
        lv_task_handler();
        mpu6050_rawdata(acc,gyr,angle);
        if(!state){
            lv_label_set_text_fmt(label_x_value,"%.1fm/s^2",acc[0]>0?(acc[0]>9.8?9.8:acc[0]):(acc[0]<-9.8?-9.8:acc[0]));
            lv_label_set_text_fmt(label_y_value,"%.1fm/s^2",acc[1]>0?(acc[1]>9.8?9.8:acc[1]):(acc[1]<-9.8?-9.8:acc[1]));
            lv_label_set_text_fmt(label_z_value,"%.1fm/s^2",acc[2]>0?(acc[2]>9.8?9.8:acc[2]):(acc[2]<-9.8?-9.8:acc[2]));
            lv_bar_set_value(bar_x, acc[0]*10, LV_ANIM_ON);
            lv_bar_set_value(bar_y, acc[1]*10, LV_ANIM_ON);
            lv_bar_set_value(bar_z, acc[2]*10, LV_ANIM_ON);            
        }else{
            lv_label_set_text_fmt(label_x_value,"%.f°",angle[0]);
            lv_label_set_text_fmt(label_y_value,"%.f°",angle[1]);
            lv_label_set_text_fmt(label_z_value,"%.f°",angle[2]);
            lv_bar_set_value(bar_x, angle[0], LV_ANIM_ON);
            lv_bar_set_value(bar_y, angle[1], LV_ANIM_ON);
            lv_bar_set_value(bar_z, angle[2], LV_ANIM_ON);   
        }
        if(first){
            if(!state){//加速度数值
                lv_obj_align(bar_x, LV_ALIGN_TOP_MID,-40,40);
                lv_obj_align(bar_y, LV_ALIGN_TOP_MID,-40,80);
                lv_obj_align(bar_z, LV_ALIGN_TOP_MID,-40,120);
                lv_bar_set_range(bar_x, -98, 98);
                lv_bar_set_range(bar_y, -98, 98);
                lv_bar_set_range(bar_z, -98, 98);
            }else{//角度数值
                lv_obj_align(bar_x, LV_ALIGN_TOP_MID,-10,40);
                lv_obj_align(bar_y, LV_ALIGN_TOP_MID,-10,80);
                lv_obj_align(bar_z, LV_ALIGN_TOP_MID,-10,120);
                lv_bar_set_range(bar_x, -90, 90);
                lv_bar_set_range(bar_y, -90, 90);
                lv_bar_set_range(bar_z, -90, 90);
            }
            lv_obj_align_to(label_x, bar_x, LV_ALIGN_OUT_BOTTOM_MID, 0, 5);
            lv_obj_align_to(label_y, bar_y, LV_ALIGN_OUT_BOTTOM_MID, 0, 5);
            lv_obj_align_to(label_z, bar_z, LV_ALIGN_OUT_BOTTOM_MID, 0, 5);
            
            lv_obj_align_to(label_x_value, bar_x, LV_ALIGN_OUT_RIGHT_MID, 10, 0);
            lv_obj_align_to(label_y_value, bar_y, LV_ALIGN_OUT_RIGHT_MID, 10, 0);
            lv_obj_align_to(label_z_value, bar_z, LV_ALIGN_OUT_RIGHT_MID, 10, 0);
            first=0;
        }
        //printf(":%.2f %.2f %.2f\n",acc[0],acc[1],acc[2]);
        //printf(":%.2f %.2f %.2f\n",angle[0],angle[1],angle[2]);
        vTaskDelay(pdMS_TO_TICKS(100));
    }

而在主循环中,我们不断读取数据,不断显示新的数据内容即可

总结

在本次活动中,学习了如何一直LVGL图形库,并做出自己的图形界面。在过程中遇到的问题,通过百度搜索都能找到适合的答案,使自我得到了提升感谢硬禾学堂平台。

附件下载
lvgl_demo.c
其余的见网盘:链接:https://pan.baidu.com/s/1xDZ8vLyNmpgsEHn8_S8DvQ?pwd=hlqq 提取码:hlqq
团队介绍
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号