2025-02-27 7:08 AM - last edited on 2025-02-27 8:58 AM by mƎALLEm
Hello,
On one hand, I built an app that uses I2C, 2 UARTS, ADCs, captures PWM, provides PWM. All of them rely heavily on the use of DMAs. This app gives good results without any apparent issue.
On the other hand, I built a test app on the same target to drive a display using LVGL 9.2.2. It seems to work too.
So far, so good... Now I put both together: the result produces an hard fault with STKALIGN and UNALIGN_TRP bits set.
I suspect an overflow somewhere.
As the app is pretty big, I started to strip the code as much as possible while keeping the hardfault. Stripping code allowed the app to be compiled without optimization hoping this will help resolving.
This leads to a single module that is provided below.
Some elements:
- app is pre-configured using CubeMx.
- in the provided code, nearly any change will make the hard fault disappear (at least for minutes, I don't have the oppportunity to let the app run for hours for each change I did).
- pwm capture and pwm outputs are not enabled in the code. But if I remove one of them from the ioc configuration, the hard fault disappears.
After several days of struggling, I'm requesting some help on how I could solve or investigate deeper the failure.
Here's the code for the display module and how it is called, a zip for the whole minimal project if other pieces of information are needed, also screenshot of hard fault and cubeMx.
Thanks
Julien
#include "display.h"
#include <string.h>
#include <strings.h>
#include <stdint.h>
#include <stddef.h>
#include "lvgl.h"
#include <assert.h>
#include "usart.h"
#include "consts.h"
#define WIDTH 128
#define HEIGHT 64
#define DISP_BUF_SIZE ((WIDTH * HEIGHT / 8))
static lv_obj_t* m_default_scr;
static lv_color_t m_buffer_lvgl[DISP_BUF_SIZE + 8] = { 0 }; // +8 reserved for lvgl
static lv_display_t* m_display = NULL;
static lv_obj_t* lblAltitude = NULL;
volatile static bool m_modification_allowed = true;
static char m_text[50];
static char m_uart_buffer[DISP_BUF_SIZE] = {0};
static void send_buffer_to_display(uint8_t* buffer, uint32_t len);
static void prepare_lvgl_screen(void);
static void my_flush_cb(lv_display_t* display, const lv_area_t* area, uint8_t* px_map);
static void my_rounder_cb(lv_event_t* e);
//----------------------------------------------------------------------------------------------------------------
void display__init()
{
prepare_lvgl_screen();
}
void display__process(void)
{
if (m_modification_allowed)
{
static uint8_t i = 0;
i++;
if (i % 2 == 0)
strcpy(m_text, "0");
else
strcpy(m_text, "1");
lv_label_set_text_static(lblAltitude, m_text);
}
lv_timer_handler();
}
static void send_buffer_to_display(uint8_t* buffer, uint32_t len)
{
assert(len <= sizeof(m_uart_buffer));
memcpy(m_uart_buffer, buffer, sizeof(m_uart_buffer));
HAL_HalfDuplex_EnableTransmitter(&UART_GSU);
HAL_UART_Transmit_DMA(&UART_GSU, (uint8_t*)m_uart_buffer, len);
}
//--------------------------------- uart end transfer callback ------------------------------------------------
void HAL_UART_TxCpltCallback(UART_HandleTypeDef* huart)
{
if (huart == &UART_GSU)
{
lv_display_flush_ready(m_display); // tells LVGL transfer is ended. It can render a frame again if it wants to.
m_modification_allowed = true;
}
}
//--------------------------------- LVGL stuff ------------------------------------------------
static void my_flush_cb(lv_display_t* display, const lv_area_t* area, uint8_t* px_map)
{
m_modification_allowed = false;
send_buffer_to_display(px_map + 8, DISP_BUF_SIZE);
}
// Never called by LVGL
static void my_rounder_cb(lv_event_t* e)
{
lv_area_t* area = lv_event_get_param(e);
/* Round the height to the nearest multiple of 8 */
area->y1 = (area->y1 & ~0x7);
area->y2 = (area->y2 | 0x7);
}
static void prepare_lvgl_screen(void)
{
lv_init();
m_display = lv_display_create(WIDTH, HEIGHT);
lv_display_set_color_format(m_display, LV_COLOR_FORMAT_I1);
lv_display_set_buffers(m_display, m_buffer_lvgl, NULL, sizeof(m_buffer_lvgl), LV_DISPLAY_RENDER_MODE_FULL);
lv_display_set_flush_cb(m_display, my_flush_cb);
lv_tick_set_cb(HAL_GetTick);
lv_display_add_event_cb(m_display, my_rounder_cb, LV_EVENT_INVALIDATE_AREA, m_display);
m_default_scr = lv_disp_get_scr_act(NULL);
//--- lblAltitude
lblAltitude = lv_label_create(m_default_scr);
assert(lblAltitude);
lv_label_set_text_static(lblAltitude, m_text);
lv_obj_set_width(lblAltitude, 100);
lv_obj_set_x(lblAltitude, 0);
lv_obj_set_y(lblAltitude, 00);
lv_scr_load(m_default_scr);
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_TIM2_Init();
MX_ADC1_Init();
MX_TIM4_Init();
MX_TIM3_Init();
MX_TIM14_Init();
MX_TIM15_Init();
MX_USART5_UART_Init();
MX_TIM1_Init();
// Disable IWDG if core is halted
__HAL_DBGMCU_FREEZE_IWDG();
display__init();
while (1)
{
display__process();
}
}