Files
motor_ili9341/Core/Src/freertos.c
T
zhy200308 e4d61b19f0 重构 ILI9341+LVGL 驱动:异步 DMA + 硬件旋转,修复残影与白屏
- 硬件 MADCTL 旋转,删除软件转置,LVGL 坐标与面板 1:1,消除移动残影
- 全异步 DMA:flush 启动即返回,完成/出错由 SPI 中断收尾,去掉信号量阻塞
- DMA 缓冲经 .lcd_ram 段强制放入 AXI SRAM,规避 DTCM 不可被 DMA 访问导致的白屏
- LV_COLOR_16_SWAP=1,LVGL 直出面板字节序,省去逐像素交换/拷贝
- 看门狗超时重建 SPI 自恢复,单次异常不再级联长时间白屏
- 定制 scatter(motor_ili9341_lcd.sct) 并接入 uvprojx
- 新增 README_ILI9341.md 说明根因/设计/构建验证
2026-07-08 11:02:42 +08:00

303 lines
9.6 KiB
C

/* USER CODE BEGIN Header */
/**
******************************************************************************
* File Name : freertos.c
* Description : Code for freertos applications
******************************************************************************
* @attention
*
* Copyright (c) 2026 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "uart4.h"
#include "esc.h"
#include "lvgl.h"
#include "ili9341.h"
#include "lv_port_disp.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */
/* USER CODE END Variables */
/* Definitions for defaultTask */
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
.name = "defaultTask",
.stack_size = 1024 * 4,
.priority = (osPriority_t) osPriorityNormal,
};
/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */
/* USER CODE END FunctionPrototypes */
void StartDefaultTask(void *argument);
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */
/* Hook prototypes */
void vApplicationIdleHook(void);
void vApplicationStackOverflowHook(xTaskHandle xTask, signed char *pcTaskName);
void vApplicationMallocFailedHook(void);
/* USER CODE BEGIN 2 */
void vApplicationIdleHook( void )
{
/* vApplicationIdleHook() will only be called if configUSE_IDLE_HOOK is set
to 1 in FreeRTOSConfig.h. It will be called on each iteration of the idle
task. It is essential that code added to this hook function never attempts
to block in any way (for example, call xQueueReceive() with a block time
specified, or call vTaskDelay()). If the application makes use of the
vTaskDelete() API function (as this demo application does) then it is also
important that vApplicationIdleHook() is permitted to return to its calling
function, because it is the responsibility of the idle task to clean up
memory allocated by the kernel to any task that has since been deleted. */
}
/* USER CODE END 2 */
/* USER CODE BEGIN 4 */
void vApplicationStackOverflowHook(xTaskHandle xTask, signed char *pcTaskName)
{
/* Run time stack overflow checking is performed if
configCHECK_FOR_STACK_OVERFLOW is defined to 1 or 2. This hook function is
called if a stack overflow is detected. */
}
/* USER CODE END 4 */
/* USER CODE BEGIN 5 */
void vApplicationMallocFailedHook(void)
{
/* vApplicationMallocFailedHook() will only be called if
configUSE_MALLOC_FAILED_HOOK is set to 1 in FreeRTOSConfig.h. It is a hook
function that will get called if a call to pvPortMalloc() fails.
pvPortMalloc() is called internally by the kernel whenever a task, queue,
timer or semaphore is created. It is also called by various parts of the
demo application. If heap_1.c or heap_2.c are used, then the size of the
heap available to pvPortMalloc() is defined by configTOTAL_HEAP_SIZE in
FreeRTOSConfig.h, and the xPortGetFreeHeapSize() API function can be used
to query the size of free heap space that remains (although it does not
provide information on how the remaining heap might be fragmented). */
}
/* USER CODE END 5 */
/**
* @brief FreeRTOS initialization
* @param None
* @retval None
*/
void MX_FREERTOS_Init(void) {
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* USER CODE BEGIN RTOS_MUTEX */
/* add mutexes, ... */
/* USER CODE END RTOS_MUTEX */
/* USER CODE BEGIN RTOS_SEMAPHORES */
/* add semaphores, ... */
/* USER CODE END RTOS_SEMAPHORES */
/* USER CODE BEGIN RTOS_TIMERS */
/* start timers, add new ones, ... */
/* USER CODE END RTOS_TIMERS */
/* USER CODE BEGIN RTOS_QUEUES */
/* add queues, ... */
/* USER CODE END RTOS_QUEUES */
/* Create the thread(s) */
/* creation of defaultTask */
defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
/* USER CODE BEGIN RTOS_THREADS */
/* add threads, ... */
/* USER CODE END RTOS_THREADS */
/* USER CODE BEGIN RTOS_EVENTS */
/* add events, ... */
/* USER CODE END RTOS_EVENTS */
}
/* USER CODE BEGIN Header_StartDefaultTask */
/**
* @brief Function implementing the defaultTask thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{
/* USER CODE BEGIN StartDefaultTask */
/*
* 之前这里是:
* ILI9341_Init();
* ILI9341_SetRotation(1);
* ILI9341_FillScreen(ILI9341_BLACK);
*
* 现在硬件初始化、清屏都收拢到了 lv_port_disp.c 的 disp_init()
* 里统一做(跟随 lv_init()/lv_port_disp_init() 一起调用)。
* 不再单独调用 ILI9341_SetRotation(1):横屏旋转已经改成在
* disp_flush() 里用软件转置实现,如果这里再把 MADCTL 的 MV 位
* 置 1,会跟软件转置逻辑重复叠加,导致画面完全错乱。
*/
lv_init();
lv_port_disp_init();
//延时等待时钟同步防止异常出现
osDelay(100);
lv_obj_t *scr = lv_scr_act();
/*
* 屏幕背景
*/
lv_obj_set_style_bg_color(scr, lv_color_hex(0x101820), LV_PART_MAIN);
lv_obj_set_style_bg_opa(scr, LV_OPA_COVER, LV_PART_MAIN);
/*
* 标题
*/
lv_obj_t *title = lv_label_create(scr);
lv_label_set_text(title, "STM32H750 LVGL");
lv_obj_set_style_text_color(title, lv_color_hex(0xFFFFFF), LV_PART_MAIN);
lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 12);
/*
* 状态文字
*/
lv_obj_t *status = lv_label_create(scr);
lv_label_set_text(status, "UI + Animation OK");
lv_obj_set_style_text_color(status, lv_color_hex(0x00E5FF), LV_PART_MAIN);
lv_obj_align(status, LV_ALIGN_TOP_MID, 0, 38);
/*
* 进度条背景
*/
lv_obj_t *bar = lv_bar_create(scr);
lv_obj_set_size(bar, 220, 18);
lv_obj_align(bar, LV_ALIGN_TOP_MID, 0, 72);
lv_bar_set_mode(bar, LV_BAR_MODE_NORMAL);
lv_bar_set_range(bar, 0, 100);
lv_bar_set_value(bar, 0, LV_ANIM_OFF);
lv_obj_set_style_bg_color(bar, lv_color_hex(0x303030), LV_PART_MAIN);
lv_obj_set_style_bg_opa(bar, LV_OPA_COVER, LV_PART_MAIN);
lv_obj_set_style_border_width(bar, 1, LV_PART_MAIN);
lv_obj_set_style_border_color(bar, lv_color_hex(0x666666), LV_PART_MAIN);
lv_obj_set_style_pad_all(bar, 0, LV_PART_MAIN);
lv_obj_set_style_bg_color(bar, lv_color_hex(0x00C853), LV_PART_INDICATOR);
lv_obj_set_style_bg_opa(bar, LV_OPA_COVER, LV_PART_INDICATOR);
/*
* 蓝色动画方块
*/
lv_obj_t *box = lv_obj_create(scr);
lv_obj_set_size(box, 40, 40);
lv_obj_set_pos(box, 20, 115);
lv_obj_set_style_bg_color(box, lv_color_hex(0x0091EA), LV_PART_MAIN);
lv_obj_set_style_bg_opa(box, LV_OPA_COVER, LV_PART_MAIN);
lv_obj_set_style_border_width(box, 0, LV_PART_MAIN);
lv_obj_set_style_pad_all(box, 0, LV_PART_MAIN);
/*
* 按钮
*/
lv_obj_t *btn = lv_btn_create(scr);
lv_obj_set_size(btn, 120, 36);
lv_obj_align(btn, LV_ALIGN_BOTTOM_MID, 0, -18);
lv_obj_set_style_bg_color(btn, lv_color_hex(0x263238), LV_PART_MAIN);
lv_obj_set_style_bg_opa(btn, LV_OPA_COVER, LV_PART_MAIN);
lv_obj_set_style_border_width(btn, 1, LV_PART_MAIN);
lv_obj_set_style_border_color(btn, lv_color_hex(0x00E5FF), LV_PART_MAIN);
lv_obj_t *btn_label = lv_label_create(btn);
lv_label_set_text(btn_label, "START");
lv_obj_set_style_text_color(btn_label, lv_color_hex(0xFFFFFF), LV_PART_MAIN);
lv_obj_center(btn_label);
/*
* 方块左右移动动画
*/
lv_anim_t anim;
lv_anim_init(&anim);
lv_anim_set_var(&anim, box);
lv_anim_set_values(&anim, 20, 260);
lv_anim_set_time(&anim, 1200);
lv_anim_set_playback_time(&anim, 1200);
lv_anim_set_repeat_count(&anim, LV_ANIM_REPEAT_INFINITE);
lv_anim_set_exec_cb(&anim, (lv_anim_exec_xcb_t)lv_obj_set_x);
lv_anim_start(&anim);
/*
* 进度条动画,0 到 100 再回到 0
*/
// lv_anim_t bar_anim;
// lv_anim_init(&bar_anim);
// lv_anim_set_var(&bar_anim, bar);
// lv_anim_set_values(&bar_anim, 0, 100);
// lv_anim_set_time(&bar_anim, 3000);
// lv_anim_set_playback_time(&bar_anim, 0);
// lv_anim_set_repeat_count(&bar_anim, LV_ANIM_REPEAT_INFINITE);
// lv_anim_set_exec_cb(&bar_anim, Bar_Anim_CB);
// lv_anim_start(&bar_anim);
for(;;)
{
lv_timer_handler();
/*
* LCD 看门狗:新驱动是全异步的,正常刷新由 SPI 完成/错误中断
* 自动收尾,不阻塞本任务。此处只兜底极端情况——一次 DMA 既没
* 触发完成中断也没触发错误中断(硬件卡死/中断丢失)时,强制
* 中止并重建 SPI,恢复刷新管线,避免长时间白屏/冻结。
*/
ILI9341_ServiceWatchdog(180);
osDelay(5);
}
/* USER CODE END StartDefaultTask */
}
/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */
/* USER CODE END Application */