Noise on OLED (SSD1306)

Important: posts that do not use this template will be ignored or closed.

Description

I am trying to use LittlevGL on OLED(SSD1306) using Nordic’s nRF52840.
I am not getting the desired output. It is noise which seems random but is the same every time you power on the OLED.

What MCU/Processor/Board and compiler are you using?

MCU - nRF52840

What do you experience?

Screen is showing random noise. Actually the pattern made by this noise is always same.
I am guessing there is some problem with the generated buffer.

I have ported LittlevGL using guide and set callbacks for disp_flush, set_px_cb, rounder_cb. Using these guides.


https://docs.littlevgl.com/en/html/porting/index.html

What do you expect?

Any idea how can I debug ? Or is there some problem with my code.

Code to reproduce

main.c

lv_init();

    lv_port_disp_init();

    lv_obj_t * scr = lv_disp_get_scr_act(NULL);
    lv_theme_t * th = lv_theme_mono_init(0, NULL);
    /* Set the mono system theme */
    lv_theme_set_current(th);
    
    /*Create a Label on the currently active screen*/
    lv_obj_t *label1;
    label1 =  lv_label_create(scr, NULL);
    lv_label_set_text(label1, "Hello World");
    lv_obj_set_pos(label1,30, 30);// position, position);

    NRF_LOG_INFO("Towards end of main"); NRF_LOG_FLUSH();

    while (1) {
        lv_task_handler();
        lv_tick_inc(1);
        nrf_delay_ms(1);
    }

disp_flush function


    static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
    {
        /*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/

        int16_t x;
        int16_t y;
        uint8_t *buf = (uint8_t*) color_p;
        for(y = area->y1; y <= area->y2; y++) {
            for(x = area->x1; x <= area->x2; x++) {
                /* Put a pixel to the display. For example: */
                /* put_px(x, y, *color_p)*/
                Adafruit_GFX_drawPixel(x, y, *buf);
                buf++;
            }
        }

        NRF_LOG_INFO("Written to screen"); NRF_LOG_FLUSH();

        /* IMPORTANT!!!
        * Inform the graphics library that you are ready with the flushing*/
        lv_disp_flush_ready(disp_drv);
    }

A screenshot would be useful.

I was debugging further and saw this.

I tried this code without lvgl library and this is the output.

My Callback function

void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
    /*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/

    int16_t x;
    int16_t y;
    uint8_t *buf = (uint8_t*) color_p;
    //copyToBuffer(buf);
    NRF_LOG_INFO("X1 Value %d | X2 Value %d | Y1 Value %d | Y2 Value %d", area->x1, area->x2, area->y1, area->y2); NRF_LOG_FLUSH();
    for(y = area->y1; y <= area->y2; y++) {
        for(x = area->x1; x <= area->x2; x++) {
            SSD1306_drawPixel(x, y, *buf);
            buf++;
        }
    }
    
    SSD1306_display();

    NRF_LOG_INFO("Written to screen"); NRF_LOG_FLUSH();

    /* IMPORTANT!!!
     * Inform the graphics library that you are ready with the flushing*/
    lv_disp_flush_ready(disp_drv);
}

This means that something is wrong with your display driver, not LittlevGL.

What color depth are you using? You should use lv_color_toX(*color_p) (where X is the display’s color depth) instead of casting color_p to a uint8_t directly.

For example, if you want a 16-bit RGB color value, you should call lv_color_to16 and use the value it returns instead of casting lv_color_t * to uint16_t *.

I am using 1 as colour depth.

The function SSD1306_drawPixel() correctly draws pixel at given coordinates. What could be wrong with my display driver ?

Your disp_flush function is not handling the colors from LittlevGL correctly.

Here’s an example for another display controller that uses the lv_color_to1 function, rather than casting (which is not portable):

UPDATE [SOLVED]

I was using a callback function set_px_cb which was taken from this blog.

uint16_t byte_index = x + (( y>>3 ) * buf_w);
  uint8_t  bit_index  = y & 0x7;
  // == 0 inverts, so we get blue on black
  if ( color.full == 0 ) {
    BIT_SET( buf[ byte_index ] , bit_index );
  }
  else {
    BIT_CLEAR( buf[ byte_index ] , bit_index );
  }

Removing this function and printing each pixel from buffer using this callback function solved my problem.

    int16_t x;
    int16_t y;
    for(y = area->y1; y <= area->y2; y++) {
        for(x = area->x1; x <= area->x2; x++) {
            SSD1306_drawPixel(x, y, lv_color_to1(*color_p));
            color_p++;
        }
    }
    
    SSD1306_display();

    /* IMPORTANT!!!
     * Inform the graphics library that you are ready with the flushing*/
    lv_disp_flush_ready(disp_drv);

How to invert buffer ?

I think one of the uses of this function was there to invert pixels. If this function is interfering with printing of pixels then how can I correct this function to invert my display ?

XORing the color with 1 should work: lv_color_to1(*color_p) ^ 1

1 XOR 1 = 0
0 XOR 1 = 1

Thank You. That works very well.

Another problem I am facing is that content is being displayed in segments.

3a9iyl

I analysed the function calls and found that the disp_flush function is called around 8 times with different area segments segments.

For eg the first call to disp_flush is with area segments X1 = 0, X2=64 and Y1=0, Y2=8
the second call to disp_flush is with area segments X1 = 0, X2=64 and Y1=8, Y2=16
and so on…

**How can I increase speed of display process ? **

You can increase your display buffer size. That will reduce the number of calls to disp_flush.

To increase the speed further you’ll have to optimize the loop. For example, on other SSD display controllers, you can avoid setting the X and Y window before every pixel and just set it once. That has proved to be a huge speedup in my experience.