分析lvgl的代码启动过程,对比esp32,stm32,linux

lvgl是gui层负责绘制gui并根据输入设备的事件来响应重绘 ,然后把绘制的缓冲区发送给显示驱动去实际显示。

以下代码参考lvgl arduino官方例程,gui guider模拟器例程,,零知 stm32 fsmc lvgl例程

第0步 时钟

时钟是lvgl绘制gui的节拍器。获取时钟   在这个文件里  ..\lvgl\src\hal\lv_hal_tick.c

在arduino 中时钟是从 millis()这个函数获得的

#define LV_TICK_CUSTOM 1
#if LV_TICK_CUSTOM
    #define LV_TICK_CUSTOM_INCLUDE "Arduino.h"         /*Header for the system time function*/
    #define LV_TICK_CUSTOM_SYS_TIME_EXPR (millis())    /*Expression evaluating to current system time in ms*/
#endif   /*LV_TICK_CUSTOM*/

 

stm32使用硬件timer   

HardwareTimer *MyTim;
    /*Initialize the graphics library's tick*/
    MyTim = new HardwareTimer(TIM2);
    MyTim->setMode(2, TIMER_OUTPUT_COMPARE);  // In our case, channekFalling is configured but not really used. Nevertheless it would be possible to attach a callback to channel compare match.
    MyTim->setOverflow(1000/LVGL_TICK_PERIOD, HERTZ_FORMAT); // period in Hz
    MyTim->attachInterrupt(lv_tick_handler);
    MyTim->resume();



static void lv_tick_handler(HardwareTimer*)
{
    
    lv_tick_inc(LVGL_TICK_PERIOD);
}

linux等使用 mingw标准库   <stddef.h>  里的宏定义  sys_time

#if !LV_TICK_CUSTOM
/**
 * You have to call this function periodically
 * @param tick_period the call period of this function in milliseconds
 */
LV_ATTRIBUTE_TICK_INC void lv_tick_inc(uint32_t tick_period)
{
    tick_irq_flag = 0;
    sys_time += tick_period;
}
#endif

第一步 核心初始化

lv核心初始化 完成各种结构体与函数的初始化   

  lv_init();

第二步  硬件抽象层

硬件虚拟层需要 完成  display, input devices, tick     也就是与驱动的对接,与输入设备的对接   完成tick (节拍)?

1 先看display 

先实例化底层显示驱动    

 

SDL驱动      monitor_init();

TFT espi驱动      tft.begin();          /* TFT init TFT初始化*/ 

  tft.setRotation( 1 ); /* Landscape orientation, flipped 设置方向*/

 

创造缓冲区

    /*Create a display buffer  SDL样式的*/  
    static lv_disp_draw_buf_t disp_buf1;
    static lv_color_t buf1_1[480 * 10];
    lv_disp_draw_buf_init(&disp_buf1, buf1_1, NULL, 480 * 10);
lv_disp_buf_init(&disp_buf, buf, NULL, LV_HOR_RES_MAX * 10);

完成display到实际底层驱动的填充函数

 

/* Display flushing 显示填充  espi样式  */
void my_disp_flush( lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p )
{
    uint32_t w = ( area->x2 - area->x1 + 1 );
    uint32_t h = ( area->y2 - area->y1 + 1 );

    tft.startWrite();
    tft.setAddrWindow( area->x1, area->y1, w, h );
    tft.pushColors( ( uint16_t * )&color_p->full, w * h, true );
    tft.endWrite();

    lv_disp_flush_ready( disp );
}

直接写GRAM样式

void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{

    u16 height,width;
    u16 i,j;
    width=area->x2 - area->x1+1;             //得到填充的宽度
    height=area->y2 - area->y1+1;            //高度
    for(i=0;i<height;i++)
    {
        LCD_SetCursor(area->x1,area->y1+i);       //设置光标位置
        LCD_WriteRAM_Prepare();     //开始写入GRAM
        for(j=0;j<width;j++)
        {
            LCD_TYPE->LCD_RAM=color_p->full;//写入数据
            color_p++;
        }
    }
    
    lv_disp_flush_ready(disp); /* tell lvgl that flushing is done */
}

 

 

SDL样式

monitor_flush

 

 

创造display

    /*Initialize the display初始化display*/
    lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);
    disp_drv.hor_res = LV_HOR_RES_MAX;
    disp_drv.ver_res = LV_VER_RES_MAX;
    disp_drv.flush_cb = my_disp_flush;//  这里就用到了填充函数
    disp_drv.buffer = &disp_buf;
    lv_disp_drv_register(&disp_drv);

 

 

2 再看输入层

先初始化硬件接口 

TFT-esp 这样  触摸屏

    uint16_t calData[5] = { 187, 3596, 387, 3256, 5 };
    tft.setTouch( calData ); // espi如果设置了touch cs 自动完成触摸的初始化

 独立的xpt2046驱动 触摸屏

XPT2046_Touchscreen ts(CS_PIN, irq_pin);

 模拟器的鼠标键盘

mouse_init();//在模拟器里也完成了触摸回调函数

其他的滚轮键盘编码器也同样原理

对接触摸驱动 完成触摸回调函数

espi

/*Read the touchpad*/
/*读取触摸板*/
void my_touchpad_read( lv_indev_drv_t * indev_driver, lv_indev_data_t * data )
{
    uint16_t touchX, touchY;

    bool touched = tft.getTouch( &touchX, &touchY, 600 );

    if( !touched )
    {
        data->state = LV_INDEV_STATE_REL;
    }
    else
    {
        data->state = LV_INDEV_STATE_PR;

        /*Set the coordinates*/
        data->point.x = touchX;
        data->point.y = touchY;

        Serial.print( "Data x " );
        Serial.println( touchX );

        Serial.print( "Data y " );
        Serial.println( touchY );
    }
}

 

xpt2046

bool my_touchpad_read(lv_indev_drv_t * indev, lv_indev_data_t * data)
{
    static lv_coord_t last_x = 0;
    static lv_coord_t last_y = 0;

    /*Save the state and save the pressed coordinate*/
    data->state = ts.touched() ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL;
    if(data->state == LV_INDEV_STATE_PR){
        TS_Point p = ts.getPoint();
        
        //convert to lcd position
        last_x = LV_HOR_RES-(p.x *LV_HOR_RES)/4095;       /*TODO save the current X coordinate*/
        last_y = LV_VER_RES-(p.y *LV_VER_RES)/4095;       /*TODO save the current Y coordinate*/
        
        Serial.print("touched:");
        Serial.print(last_x);Serial.print(",");Serial.println(last_y);
    } 

    /*Set the coordinates (if released use the last pressed coordinates)*/
    data->point.x = last_x;
    data->point.y = last_y;

    return false; /*Return `false` because we are not buffering and no more data to read*/
}

完成lvgl输入驱动与硬件驱动的对接

触摸样式

    /*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 );

模拟器样式 使用鼠标

   /* Add the mouse as input device
     * Use the 'mouse' driver which reads the PC's mouse*/
    mouse_init();
    lv_indev_drv_t indev_drv;
    lv_indev_drv_init(&indev_drv);          /*Basic initialization*/
    indev_drv.type = LV_INDEV_TYPE_POINTER;
    indev_drv.read_cb = mouse_read;         /*This function will be called periodically (by the library) to get the mouse position and state*/
    lv_indev_t * mouse_indev = lv_indev_drv_register(&indev_drv);

 

3 初始化节拍

arduino 自动完成此步骤

模拟器

    /* Tick init.
     * You have to call 'lv_tick_inc()' in periodically to inform LittelvGL about how much time were elapsed
     * Create an SDL thread to do this*/
    SDL_CreateThread(tick_thread, "tick", NULL);

stm32

 

    /*Initialize the graphics library's tick*/
    MyTim = new HardwareTimer(TIM2);
    MyTim->setMode(2, TIMER_OUTPUT_COMPARE);  // In our case, channekFalling is configured but not really used. Nevertheless it would be possible to attach a callback to channel compare match.
    MyTim->setOverflow(1000/LVGL_TICK_PERIOD, HERTZ_FORMAT); // period in Hz
    MyTim->attachInterrupt(lv_tick_handler);
    MyTim->resume();

说实话我没怎么看懂

 

第三步  开始绘制gui

硬件抽象层完成初始化   就可以开始绘制ui

第一步是初始化sceen

从sceen里创建各种widget    给widget添加事件回调  然后在无线循环里调用lv_timer_handler(),它会按LVGL_TICK_PERIOD 设置的事件重复的响应输入事件来重绘ui

 

这里简单绘制个label,注意label默认不带事件。除非用函数强制开启

    /*Create a Label on the currently active screen在当前屏幕上生成一个label*/
  static  lv_obj_t * label1= lv_label_create(lv_scr_act());// lv_sce_act()可以获得当前屏幕   使用静态指针是避免setup函数结束后指针被释放找不到对象自己
/*Modify the Label's text设置文本*/
    lv_label_set_text(label1, "Hello world!");
    
    lv_obj_set_pos(label1, 0, 2);

    /* Align the Label to the center
     * NULL means align on parent (which is the screen now)
     * 0, 0 at the end means an x, y offset after alignment*/
// 

 

添加一个带事件的按钮 

 这里我一开始尝试用模拟器,但是guiguider跟lvgl是是纯c语言的  不支持 c++的String,但是c想实现字符串的动态增长可是无比蛋疼的  这时候arduino的C++特性就很好用


 static void btn_event_cb(lv_event_t * e)//回调函数都是静态的 
{
    lv_event_code_t code = lv_event_get_code(e);
        if(code == LV_EVENT_CLICKED) {
           static String temp = lv_label_get_text(label);//临时变量也是静态的
           temp = temp + "!";

            lv_label_set_text(label1,  temp.c_str());
           
        }

}

 

    lv_obj_t * btn = lv_btn_create(lv_scr_act());     /*Add a button the current screen*/
    lv_obj_set_pos(btn, 50, 50);                            /*Set its position*/
    lv_obj_set_size(btn, 120, 50);                          /*Set its size*/
    lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_ALL, NULL);           /*Assign a callback to the button*/

 

posted @ 2022-08-20 17:09  kyo413  阅读(1721)  评论(0编辑  收藏  举报