How to pass just color_p->full values to SPI write function?

Description

Great library, thank you. I tried searching for an answer here but couldnt’ find one.

How do I pass just the full values from the lv_color_t * color_p buffer pointer that are passed to the flush function?

I want to pass all the RGB values to my SPI transfer function at once rather than use for loop through the width and height and send one pixel at a time.

I’ve seen functions on the forum that just pass the color_p into DMA, but how is the RGB bytes extracted if this is a struct (lv_color_t)?

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

NRF52840 with Nordic SDK v15.0

What do you want to achieve?

Pass just RGB bytes from LVGL buffer into SPI write function.

What have you tried so far?

Code to reproduce

This is what I’ve tried (in theory)

Flush function

void st7789_flush(lv_disp_drv_t * disp, const lv_area_t * area, lv_color_t * color_p)
{
    set_addr_window(area->x1, area->y1, area->x2, area->y2);
    
    NRF_LOG_INFO("In display flush with area x1: %d, x2: %d, y1: %d, y2: %d", area->x1, area->x2, area->y1, area->y2);

   spi_write(&color_p->full, sizeof(color_p));

    lv_disp_flush_ready(disp);         /* Indicate you are ready with the flushing*/
}

lv_color_t struct

typedef union {
    struct {
#if LV_COLOR_16_SWAP == 0
        uint16_t blue : 5;
        uint16_t green : 6;
        uint16_t red : 5;
#else
        uint16_t green_h : 3;
        uint16_t red : 5;
        uint16_t blue : 5;
        uint16_t green_l : 3;
#endif
    } ch;
    uint16_t full;
} lv_color16_t;

SPI write function

static inline void spi_write(const uint8_t * data, size_t size)
{
    //   APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, data, size, NULL, 0));
    nrfx_err_t err_code;
    nrfx_spim_xfer_desc_t xfer_desc = NRFX_SPIM_XFER_TX(data, size);
    uint32_t flags = NRFX_SPIM_FLAG_NO_XFER_EVT_HANDLER;
    err_code = nrfx_spim_xfer(&spi_st7735_instance, &xfer_desc, flags);
    APP_ERROR_CHECK(err_code);
      
    }

Screenshot and/or video

N/A

It works because lv_color_t is a union of a structure and a uint16_t/uint32_t/uint8_t. That means that they are stored as the latter type in memory.

Aha, that makes sense now. Here’s a StackOverflow about what I didn’t know here.

Since the SPI bus will be writing 1 byte values at a time, and since I’m using RGB 565 mode (2byte, 16 bit), do you have any suggestions on how to split the byte in spi_write() without again manually looping over the passed in array pointer?

I guess I’m inching towards using DMA, but that’s a bit intimidating at this point.

I realize this probably won’t work, consider it psuedo-code.

static inline void spi_write(const uint16_t * data, size_t size)
{
    // How to avoid looping over this in order to split 2-byte to 1-byte transfers???
    nrfx_err_t err_code;
    uint8_t tx_buff[2];
    tx_buff[0] = data >> 8;
    tx_buff[1] = data & 0xFF;   

    nrfx_spim_xfer_desc_t xfer_desc = NRFX_SPIM_XFER_TX(tx_buff, 2);
    uint32_t flags = NRFX_SPIM_FLAG_NO_XFER_EVT_HANDLER;
    err_code = nrfx_spim_xfer(&spi_st7735_instance, &xfer_desc, flags);
    APP_ERROR_CHECK(err_code);
      
    }

You should be able to just treat the lv_color_t array as an array of bytes (you would have to calculate the size in bytes and not pixels). You may need to change the LV_COLOR_16_SWAP option if you end up with reds and blues being swapped.

Thanks for the help, @embeddedt!

Here’s what I settled with for future lurkers. Of course, DMA would be ideal, but I’ll deal with this later and I learned something here!

static inline void spi_write_rgb(lv_color_t * color_p, size_t size){
    nrfx_err_t err_code;

    nrfx_spim_xfer_desc_t xfer_desc = NRFX_SPIM_XFER_TX(color_p, size );
    uint32_t flags = NRFX_SPIM_FLAG_NO_XFER_EVT_HANDLER;
    err_code = nrfx_spim_xfer(&spi_st7735_instance, &xfer_desc, flags);
    APP_ERROR_CHECK(err_code);
}

void st7789_flush(lv_disp_drv_t * disp, const lv_area_t * area, lv_color_t * color_p)
{
    // NRF_LOG_INFO("In display flush with area x1: %d, x2: %d, y1: %d, y2: %d", area->x1, area->x2, area->y1, area->y2);

    set_addr_window(area->x1, area->y1, area->x2, area->y2);
    uint8_t width = area->x2 - area->x1 + 1;
    uint8_t height = area->y2 - area->y1 + 1;
    uint16_t length_to_transfer = width * height * 2;
    nrf_gpio_pin_set(ST7789_DC_PIN);
    spi_write_rgb(color_p,length_to_transfer);
    lv_disp_flush_ready(disp);         /* Indicate you are ready with the flushing*/
}