Pingtouge RVB2601 development board evaluation -- Introduction to OLED and LVGL transplantation

Author: Xi Yue

1, OLED introduction

Our screen adopts 128 * 64 resolution, and the driver ic should be SSD1306. This just supports 129 * 64, but it is a monochrome screen with spi interface.


Initialize first io Mouth:
 csi_gpio_pin_t pin_clk;
 csi_gpio_pin_t pin_mosi;
 csi_gpio_pin_t pin_cs;
 csi_gpio_pin_t pin_miso;
 static void oled_pinmux_init()
     csi_pin_set_mux(PA28, PIN_FUNC_GPIO); //clk
     csi_pin_set_mux(PA29, PIN_FUNC_GPIO); //mosi
     csi_pin_set_mux(PA27, PIN_FUNC_GPIO); //cs
     csi_pin_set_mux(PA30, PIN_FUNC_GPIO); //miso
 static void oled_gpio_init()
     csi_gpio_pin_init(&pin_clk, PA28);
     csi_gpio_pin_dir(&pin_clk, GPIO_DIRECTION_OUTPUT);     csi_gpio_pin_init(&pin_mosi, PA29);
     csi_gpio_pin_dir(&pin_mosi, GPIO_DIRECTION_OUTPUT);
     csi_gpio_pin_init(&pin_cs, PA27);
     csi_gpio_pin_dir(&pin_cs, GPIO_DIRECTION_OUTPUT);
     csi_gpio_pin_init(&pin_miso, PA30); //dc
     csi_gpio_pin_dir(&pin_miso, GPIO_DIRECTION_OUTPUT);
 Then write commands, data functions
 void Write_Command(unsigned char Data)
     unsigned char i;
     for (i = 0; i < 8; i++) {
         lcd_sdin((Data & 0x80) >> 7);
         Data = Data << 1;
 void Write_Data(unsigned char Data)
     unsigned char i;
     for (i = 0; i < 8; i++) {
         lcd_sdin((Data & 0x80) >> 7);
         Data = Data << 1;
 For this monochrome screen, we can directly open a buffer:
 uint8_t g_oled_ram[8][128];
 To draw a dot is to modify the contents of the cache:
 void oled_draw_point(uint8_t r, uint8_t c, uint8_t t)
     if (t) {
         SET_BIT(g_oled_ram[r / 8][c], ((r % 8)));
     } else {
         CLR_BIT(g_oled_ram[r / 8][c], (r % 8));
 Finally, call the refresh function to modify an entire screen:
 void oled_reflesh()
     unsigned char i, j;
     for (i = 0; i < 8; i++) {
         for (j = 0; j < 128; j++) {
 Screen initialization:
 static void oled_initialize()
     Set_Command_Lock(0x12);           // Unlock Driver IC (0x12/0x16)
     Set_Display_On_Off(0xAE);         // Display Off (0xAE/0xAF)
     Set_Display_Clock(0xA0);          // Set Clock as 116 Frames/Sec
     Set_Multiplex_Ratio(0x3F);        // 1/64 Duty (0x0F~0x3F)
     Set_Display_Offset(0x00);         // Shift Mapping RAM Counter (0x00~0x3F)
     Set_Start_Line(0x00);             // Set Mapping RAM Display Start Line (0x00~0x3F)
     Set_Low_Power(0x04);              // Set Normal Power Mode (0x04/0x05)
     Set_Addressing_Mode(0x02);        // Set Page Addressing Mode (0x00/0x01/0x02)
     Set_Segment_Remap(0xA1);          // Set SEG/Column Mapping (0xA0/0xA1)
     Set_Common_Remap(0xC8);           // Set COM/Row Scan Direction (0xC0/0xC8)
     Set_Common_Config(0x12);          // Set Alternative Configuration (0x02/0x12)
     Set_Contrast_Control(Brightness); // Set SEG Output Current
     Set_Precharge_Period(0x82);       // Set Pre-Charge as 8 Clocks & Discharge as 2 Clocks
     Set_VCOMH(0x34);                  // Set VCOM Deselect Level
     Set_Entire_Display(0xA4);         // Disable Entire Display On (0xA4/0xA5)
     Set_Inverse_Display(0xA6);        // Disable Inverse Display On (0xA6/0xA7)
     Fill_RAM(0x00); // Clear Screen
     Set_Display_On_Off(0xAF); // Display On (0xAE/0xAF)

If you want to display a picture, change the buffer with the help of the mold software:


2, Transplantation of LVGL

littlevgl is a small open source embedded GUI Library (lvgl for short). It has beautiful interface, low resource consumption, high portability and supports responsive layout. The whole library is developed in pure c language, which is easy to transplant.

  • It has very rich built-in controls, such as buttons, charts, lists, sliders, images, etc
  • Advanced graphics effects: animation, anti aliasing, transparency, smooth scrolling
  • Support a variety of input devices, such as touchpad, mouse, keyboard, encoder, etc
  • Support multilingual UTF-8} encoding
  • Support multiple and multiple display devices, such as synchronous display on multiple color screens or monochrome screens
  • Fully customized graphic elements
  • The hardware is independent of any microcontroller or display
  • It can be reduced to the minimum memory (64 kB Flash, 16 kB RAM)
  • Support for operating system, external storage and} GPU (not required)
  • Only a single frame buffer device can render advanced visual effects
  • Write in C + + for maximum compatibility (compatible with C + +)
  • Support PC emulator
  • To accelerate GUI design, provide tutorials, cases and topics, and support responsive layout

This is the temperature test display interface based on LVGL I wrote before. It's a little ugly.




Src is some source files:



Two important API s:

One is the transaction processing function:


One is LVGL heartbeat:


Both need periodic Commission adjustment.

porting is an important migration related file:


The first is the display interface:

//lvgl display interface initialization
 void lv_port_disp_init(void)
     static lv_disp_buf_t disp_buf;
     //Display buffer initialization
     lv_disp_buf_init(&disp_buf, color_buf, NULL, COLOR_BUF_SIZE); 
     //Display drive default initialization   
     lv_disp_drv_t disp_drv;                         
     //Set the display size of the screen. I'm here to support multiple screens of punctual atoms and adopt the method of dynamic acquisition
     //If you are using it for an actual project, you don't need to set it. The default value is Lv_ LV in conf.h_ HOR_ RES_ Max and LV_VER_RES_MAX macro defined value
     disp_drv.hor_res = lcddev.width;
     disp_drv.ver_res = lcddev.height;
     //Register display driver callback
     disp_drv.flush_cb = disp_flush;
     //Register display buffer
     disp_drv.buffer = &disp_buf;
     //Optionally, GPU needs to be implemented only when GPU is used_ Blend and gpu_fill interface
     //GPU is required to mix two color arrays using transparency_ Blend interface
     disp_drv.gpu_blend = gpu_blend;
     //GPU is required to fill a memory array with one color_ Fill interface
     disp_drv.gpu_fill = gpu_fill;
     //Register the display driver into lvgl
 //Write the contents of the display buffer of the specified area to the screen. You can use DMA or other hardware accelerators to complete this operation in the background
 //But after that, you have to call lv_disp_flush_ready()
 static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
     //Writes the contents of the display buffer of the specified area to the screen
     //Finally, you have to call to notify lvgl library that you have finished the flushing copy


It mainly displays the buffer and the adaptation of dot functions.

2 and 3 are touch and file related operations:

//lvgl input device initialization
 void lv_port_indev_init(void)
     lv_indev_drv_t indev_drv;
     //lvgl supports many kinds of input devices, but we usually use the touch screen, or Touchpad
     indev_drv.type = LV_INDEV_TYPE_POINTER;
     indev_drv.read_cb = touchpad_read;
 //It will be called periodically by lvgl, and the cycle value is through Lv_ LV in conf.h_ INDEV_ DEF_ READ_ Defined by the period macro
 //This value should not be set too large, otherwise it will feel insensitive to touch. The default value is 30ms
 static bool touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
     static uint16_t last_x = 0;
     static uint16_t last_y = 0;
     if(tp_dev.sta&TP_PRES_DOWN)//Touch pressed
     last_x = tp_dev.x[0];
     last_y = tp_dev.y[0];
     data->point.x = last_x;
     data->point.y = last_y;
     data->state = LV_INDEV_STATE_PR;
     }else{//The touch is released
     data->point.x = last_x;
     data->point.y = last_y;
     data->state = LV_INDEV_STATE_REL;
     //Return false for unbuffered data
     return false;

In fact, it is just to assign the coordinate value to its internal data structure and modify the state, but our board has not been touched. Unfortunately, many functions are useless.

Finally, we need to match the actual OLED interface function just now. Here we use double cache with fast speed:

 static void oled_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p)
 static lv_disp_buf_t disp_buf1;
 static lv_color_t    buf1[64 * 128];
 static lv_color_t    buf2[64 * 128];
 void oled_init()
     lv_disp_buf_init(&disp_buf1, buf1, buf2, 64 * 128);
     lv_disp_drv_t disp_drv;
 disp_drv.buffer   = &disp_buf1;
     disp_drv.flush_cb = oled_flush;
     disp_drv.rotated  = 0;
 Finally, create an example GUI Task:
 static void gui_label_create(void)
     lv_obj_t *p = lv_label_create(lv_scr_act(), NULL);
     lv_label_set_long_mode(p, LV_LABEL_LONG_BREAK);
     lv_label_set_align(p, LV_LABEL_ALIGN_CENTER);
     lv_obj_set_pos(p, 0, 4);
     lv_obj_set_size(p, 128, 60);
     lv_label_set_text(p, "THEAD RISC-V\nGUI TEST\nEEWORLD NB!!");
 static void gui_lvgl_task(void *arg)
     while (1) {
         /* Periodically call the lv_task handler.
          * It could be done in a timer interrupt or an OS task too.*/

So you can:

This article is derived from: Pingtouge chip open community

Welcome to the official account: ID:OCC_THEAD, to see more practical articles.

Keywords: risc-v

Added by hammerloaf on Thu, 13 Jan 2022 04:19:58 +0200