Description
Help please to use LVGL with a SSD1306 OLED and STM32 HAL.
MCU board and compiler
STM32F405RGT MCU
Custom board with SPI1 pins
Similar to a Nucleo-F401RE
Compiler: gcc-arm(1)
Build system: GNU Make
Libs: STM32 HAL
LVGL: Release/v7
Editor: STM32CubeMX
SPI connection (MCU to OLED)
MCU pin | SPI function |
---|---|
PA4 (Pin-20) | Chip select (CS) |
PA5 (Pin-21) | SPI1 SCK (D0) |
PA7 (Pin-23) | SPI1 MOSI (D1) |
PB0 (Pin-26) | Data command (DC) |
PB1 (Pin-27) | Reset (RES) |
3V3 | — |
GND | — |
What do you want to achieve?
I want to display ‘Hello World’ to a monochrome OLED SSD1306 using SPI without DMA.
Please tell me how to use HAL_SPI_Transmit below in the flush_cb where it states ‘Put a pixel to the display.’
What have you tried so far?
I implemented flush_cb() according to outdated tutorials not using STM32 nor SPI, so my lvgl integration is not working (nothing is displayed.)
Code to reproduce
Initialisation in main.c
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
lv_init();
lv_port_disp_init();
lv_theme_mono_init(LV_COLOR_BLACK, LV_COLOR_WHITE, 0, NULL, NULL, NULL, NULL);
lv_ex_get_started_1();
/* USER CODE END SysInit */
My flush callback lacking HAL_SPI
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
int32_t x;
int32_t y;
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)*/
color_p++;
}
}
lv_disp_flush_ready(disp_drv);
}
The entire lv_port_disp.c file
/*********************
* INCLUDES
*********************/
#include "lv_port_disp.h"
/*********************
* DEFINES
*********************/
#define DISP_BUF_SIZE (LV_HOR_RES_MAX*LV_VER_RES_MAX/8)
#define BIT_SET(a,b) ((a) |= (1U<<(b)))
#define BIT_CLEAR(a,b) ((a) &= ~(1U<<(b)))
/**********************
* STATIC PROTOTYPES
**********************/
static void disp_init(void);
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p);
static void disp_round(lv_disp_drv_t * disp_drv, lv_area_t * area);
static void disp_setpx(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, lv_color_t color, lv_opa_t opa);
#if LV_USE_GPU
static void gpu_blend(lv_disp_drv_t * disp_drv, lv_color_t * dest, const lv_color_t * src, uint32_t length, lv_opa_t opa);
static void gpu_fill(lv_disp_drv_t * disp_drv, lv_color_t * dest_buf, lv_coord_t dest_width, const lv_area_t * fill_area, lv_color_t color);
#endif
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_port_disp_init(void)
{
/*-------------------------
* Initialize your display
* -----------------------*/
disp_init();
// Set a display buffer of the whole screen
static lv_disp_buf_t disp_buf; // Descriptor of display buffer
static lv_color_t gbuf[DISP_BUF_SIZE]; // Memory used as display buffer
lv_disp_buf_init(&disp_buf, gbuf, NULL, DISP_BUF_SIZE);
/*-----------------------------------
* Register the display in LVGL
*----------------------------------*/
lv_disp_drv_t disp_drv; /*Descriptor of a display driver*/
lv_disp_drv_init(&disp_drv); /*Basic initialization*/
/*Set the resolution of the display*/
disp_drv.hor_res = LV_HOR_RES_MAX;
disp_drv.ver_res = LV_VER_RES_MAX;
/*Callbacks to copy buffer contents to the display*/
disp_drv.flush_cb = disp_flush;
disp_drv.rounder_cb = disp_round;
disp_drv.set_px_cb = disp_setpx;
disp_drv.sw_rotate = 0;
disp_drv.rotated = LV_DISP_ROT_NONE; // LV_DISP_ROT_180
/*Set a display buffer*/
disp_drv.buffer = &gbuf;
/*Finally register the driver*/
lv_disp_drv_register(&disp_drv);
}
/**********************
* STATIC FUNCTIONS
**********************/
/* Initialize your display and the required peripherals. */
static void disp_init(void)
{
/*You code here*/
}
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
int32_t x;
int32_t y;
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)*/
color_p++;
}
}
lv_disp_flush_ready(disp_drv);
}
static void disp_round(lv_disp_drv_t *disp_drv, lv_area_t *area) {
area->y1 = (area->y1 & (~0x7));
area->y2 = (area->y2 & (~0x7)) + 7;
}
static void disp_setpx(lv_disp_drv_t *disp_drv, uint8_t *buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, lv_color_t color, lv_opa_t opa) {
uint16_t byte_index = x + ((y >> 3) * buf_w);
uint8_t bit_index = y & 0x7;
if (color.full == 0) { // == 0 inverts
BIT_SET(buf[byte_index], bit_index);
}
else {
BIT_CLEAR(buf[byte_index], bit_index);
}
}
Screenshot
Device photograph
STM32CubeMX GPIO screenshot
STM32CubeMX SPI1 screenshot
Thanks!
I’m really grateful to get started with LVGL, thanks for the help.