Embedded Linux (awtk Linux FB) dual screen display

1, Foreword

Recently, we tried to adapt dual screen display on Embedded Linux, that is, two external screens are connected to synchronously display GUI interface. The difficulty is mainly to copy images from Flush to two LCD devices. This paper makes a record.

Note: Based on AWTK, this paper adapts dual screen display for arm linux platform.

AWTK is a GUI engine library developed for embedded systems. GitHub address: https://github.com/zlgopen/awtk.

AWTK Linux FB is AWTK's migration to arm linux platform. GitHub address: https://github.com/zlgopen/awtk-linux-fb.

2, Principle of dual screen display

Awtk Linux FB LCD based on Framebuffer supports two screen displays, which is relatively simple. Its principle is to copy the images in the offline video memory to the online video memory of the two displays during Flush. It should be noted that this will consume performance and reduce the frame rate.

After understanding the above principles, it can be understood that whether it is dual screen display, three screen display or even multi screen display, it can be realized through this simple software copy method, but the GUI refresh efficiency will be seriously reduced. Generally, this multi screen display is completed through hardware, and the software implementation only provides a really helpless solution.

Note: offline is the offline video memory used by AWTK for drawing; Online is the online video memory used for LCD screen display, that is, the real system video memory.

3, How to realize dual screen display

3.1 confirm the driver files of the two screens

In the embedded Linux platform, two screens usually correspond to two driver files, such as / dev/fb0 and / dev/fb1. It should be noted that the resolution and color format of the two LCD devices must be consistent when directly copying images in Flush. When they are inconsistent, you can also do internal processing before copying, but it will further reduce efficiency, so it is not recommended.

3.2 open LCD driver file during initialization

LCD is required during AWTK initialization_ T object, which is used to associate the hardware device LCD and realize the display function. If dual screen display is required, you need to open two LCD driver files and initialize relevant information. The code is as follows:

/* awtk-linux-fb/awtk-port/lcd_linux/lcd_linux_fb.c */
/* Turn on and initialize both LCD devices */
static bool_t lcd_linux_fb_open(fb_info_t* fb, fb_info_t* fb_ex, const char* filename, const char* filename_ex) {
  return_value_if_fail(fb !=  NULL && filename != NULL, FALSE);
  if (fb_open(fb, filename) == 0) {
    if (fb_ex != NULL && filename_ex != NULL){
      if (fb_open(fb_ex, filename_ex) != 0) {
        fb_close(fb);
        return FALSE;
      }
    }
    return TRUE;
  }
  return FALSE;
}

/* Create lcd_t object, two LCD driver file names are required here */
lcd_t* lcd_linux_fb_create_ex(const char* filename, const char* filename_ex) {
  lcd_t* lcd = NULL;
  fb_info_t* fb = &s_fb;
  fb_info_t* fb_ex = &s_fb_ex;
  return_value_if_fail(filename != NULL && filename_ex != NULL, NULL);

  if (lcd_linux_fb_open(fb, fb_ex, filename, filename_ex)) {
    lcd = lcd_linux_create(fb, fb_ex);
  }

  atexit(on_app_exit);
  signal(SIGINT, on_signal_int);

  return lcd;
}

Remarks: FB_ See awtk Linux FB / awtk port / LCD for the implementation of the open function_ linux/fb_ info.h.

3.3 Heavy Duty lcd_t object's flush function

Usually, two display screens correspond to two video memories respectively, while the default flush function of AWTK is only copied to the video memory of one display screen. Since the GUI needs to be copied to two video memories here, the LCD needs to be overloaded_ T object. The code is as follows:

/* awtk-linux-fb/awtk-port/lcd_linux/lcd_linux_fb.c */
/* Create an LCT that refreshes the screen based on Flush mode_ T object  */
static lcd_t* lcd_linux_create_flushable(fb_info_t* fb) {
  lcd_t* lcd = NULL;
  int w = fb_width(fb);
  int h = fb_height(fb);
  int line_length = fb_line_length(fb);

  uint8_t* online_fb = (uint8_t*)(fb->fbmem0);
  uint8_t* offline_fb = (uint8_t*)(fb->fbmem_offline);
  
  /* Create LCD according to bpp and color format of LCD device_ T object */
  lcd = lcd_mem_bgr565_create_double_fb(w, h, online_fb, offline_fb);

  if (lcd != NULL) {
    lcd->flush = lcd_mem_linux_flush;  /* Overload flush function*/
    lcd_mem_set_line_length(lcd, line_length);
  }

  return lcd;
}

3.4 realize double screen image copy

Heavy Duty LCD_ After the flush function of t object, you only need to reload the LCD_ mem_ linux_ In the flush function, copy the offline image to the online memory of the two screens. The code is as follows:

/* awtk-linux-fb/awtk-port/lcd_linux/lcd_linux_fb.c */
/* Copy image */
static ret_t lcd_mem_linux_flush_ex(lcd_t* lcd, uint8_t* buff) {
  uint32_t rect_nr = 0;
  bitmap_t offline_fb;
  bitmap_t online_fb;
  bitmap_t online_fb_ex;
  fb_info_t* fb = &s_fb;
  fb_info_t* fb_ex = &s_fb_ex;
  uint8_t* buff_ex = fb_ex->fbmem0;
  const dirty_rects_t* dirty_rects;
  lcd_mem_t* mem = (lcd_mem_t*)lcd;
  system_info_t* info = system_info();
  lcd_orientation_t o = info->lcd_orientation;
  graphic_buffer_t *gb  = graphic_buffer_create_with_data(NULL, fb_width(fb_ex), fb_height(fb_ex), mem->format);
  return_value_if_fail(lcd != NULL && buff != NULL, RET_BAD_PARAMS);
  
  /* Bind the bitmap corresponding to offline and two online */
  lcd_linux_init_drawing_fb(mem, &offline_fb);
  lcd_linux_init_online_fb(mem, &online_fb, mem->online_gb, buff, fb_width(fb), fb_height(fb), fb_line_length(fb));
  lcd_linux_init_online_fb(mem, &online_fb_ex, gb, buff_ex, fb_width(fb_ex), fb_height(fb_ex), fb_line_length(fb_ex));

  dirty_rects = lcd_get_dirty_rects(lcd);  /* Get list of dirty rectangles */
  if (dirty_rects != NULL && dirty_rects->nr > 0 && gb != NULL) {
    uint32_t i = 0;
    rect_nr = dirty_rects->disable_multiple ? 1 : dirty_rects->nr;
    for (i = 0; i < rect_nr; i++) {
      const rect_t* dr = rect_nr > 1 ? (const rect_t*)dirty_rects->rects + i : (const rect_t*)&(dirty_rects->max);
      /* Copy the image. If the screen rotates, rotate it first and then copy it */
      if (o == LCD_ORIENTATION_0) {
        image_copy(&online_fb, &offline_fb, dr, dr->x, dr->y);
        image_copy(&online_fb_ex, &offline_fb, dr, dr->x, dr->y);
      } else {
        image_rotate(&online_fb, &offline_fb, dr, o);
        image_rotate(&online_fb_ex, &offline_fb, dr, o);
      }
    }
  }

  if (gb != NULL) {
    graphic_buffer_destroy(gb);
  }
  return RET_OK;
}

/* Overloaded flush function */
static ret_t lcd_mem_linux_flush(lcd_t* lcd) {
  fb_info_t* fb = &s_fb;
  fb_info_t* fb_ex = &s_fb_ex;

/* Wait for vertical synchronization */
#if __FB_WAIT_VSYNC
  fb_sync(fb);
#endif

  if (fb_width(fb) == fb_width(fb_ex) && fb_height(fb) == fb_height(fb_ex) && fb_bpp(fb) == fb_bpp(fb_ex)){
    lcd_mem_linux_flush_ex(lcd, fb->fbmem0);
  }

  return RET_OK;
}

Tags: C C++ Linux Embedded system stm32

Posted on Sun, 05 Sep 2021 20:31:26 -0400 by qo2o