ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

FreeRTOS v202107.00 移植实战:从内核裁剪到 TLS/LVGL 集成

FreeRTOS v202107.00 移植实战:从内核裁剪到 TLS/LVGL 集成 简介本资源是FreeRTOS官方2021年7月发布的稳定版本v202107.00完整源码包面向嵌入式开发工程师、物联网系统开发者及高校电子/计算机专业学生用于构建高可靠性实时控制应用。包内含2000个文件主体为5045个C源文件与6576个头文件.h构成完整的内核、端口层、中间件及多平台移植适配代码另有大量Makefile、CMakeLists、项目工程文件如uvprojx、sln、ewp和配置文档.md、.txt、.yml全面支持ARM、RISC-V、x86等主流架构的快速集成与调试。资源大小82.29MB结构规范、模块清晰涵盖任务调度、队列通信、信号量同步、内存管理及安全增强组件预览可见sp_arm32.c、sp_arm64.c等关键端口实现及ssl.c等扩展协议支持。目前已有91人下载学习可直接用于教学实验、产品原型开发或RTOS底层机制深度研读。1. FreeRTOS v202107.00 是一个稳定可裁剪的实时内核专为资源受限的嵌入式设备设计不是通用操作系统FreeRTOS v202107.00.zip 这个文件名指向的是 FreeRTOS 官方在 2021 年 7 月发布的第 00 版本即主发布版对应 Git 标签V10.4.3。它不是某个定制发行版或第三方打包而是官方源码包的标准命名格式FreeRTOS年月.序号.zip。这个版本在 STM32F103C8T6、STM32H743VIT6 等主流 Cortex-M 系列 MCU 上已通过长期验证被广泛用于工业控制、智能传感和低功耗物联网终端。它不内置 SSL/TLS 协议栈也不直接支持 LVGL 图形库——这些必须由开发者按需集成所谓“freertos移植lvgl”“freertos中检查线程中内存使用大小的接口”等需求本质是围绕该内核构建完整固件时的典型工程动作。如果你正在 Keil 或 IAR 环境下调试.\obj\freertos.hex: error: q0147e: failed to create directory .\obj\freertos说明编译路径配置错误而非内核本身问题而no required ssl certificate was sent或exception in invoking authentication handler [ssl: certificate_verify_failed]这类报错一定发生在你额外引入的 TLS 库如 mbedTLS与 FreeRTOS 协同运行阶段与 v202107.00 内核无关。理解这一点是避免把移植失败归咎于 FreeRTOS 本身的起点。2. 从 FreeRTOSv202107.00.zip 解压到 Keil/IAR 工程可编译需完成四层结构映射FreeRTOS v202107.00.zip 解压后呈现标准的三层目录结构FreeRTOS/内核源码、FreeRTOS-Plus/可选扩展组件、Demo/大量板级例程。但实际工程中不能直接拖入全部文件——必须按目标芯片架构、开发环境和内存模型进行精准裁剪。以 STM32F103C8T6Cortex-M3ARM32在 Keil MDK-ARM v5.37 下为例关键操作不是“复制所有 .c 文件”而是建立四层映射关系内核层 → 端口层 → 板级支持层 → 应用层。2.1 映射内核层只保留核心调度与同步原语FreeRTOS 内核源码位于FreeRTOS/Source/目录下共 13 个 .c 文件。但绝大多数项目只需其中 6 个// Keil 工程中必须包含的 6 个核心文件按依赖顺序 FreeRTOS/Source/tasks.c // 任务管理、调度器入口 FreeRTOS/Source/queue.c // 队列、信号量、互斥量实现 FreeRTOS/Source/timers.c // 软件定时器可选但建议保留 FreeRTOS/Source/list.c // 双向链表基础结构被 tasks/queue 共用 FreeRTOS/Source/portable/MemMang/heap_4.c // 动态内存分配推荐 heap_4防碎片 FreeRTOS/Source/portable/GCC/ARM_CM3/port.c // Cortex-M3 端口层Keil 使用 GCC 兼容模式提示port.c文件名中的ARM_CM3表明它专为 Cortex-M3 设计与sp_arm32热搜词直接对应若目标是 Cortex-A53如树莓派 CM4则应选ARM_CA53子目录下的port.c对应sp_arm64场景。不要混用端口文件否则 SVC 异常或 PendSV 失效将导致freertos挂在svc0。2.2 映射端口层确认异常向量与系统节拍配置端口层决定 FreeRTOS 如何与 CPU 底层交互。对 Cortex-M3port.c中最关键的两个函数是xPortSysTickHandler()和xPortPendSVHandler()。它们必须正确挂接到芯片的 SysTick 和 PendSV 异常向量表中。在 Keil 中这通过startup_stm32f10x_md.s启动文件完成; startup_stm32f10x_md.s 片段Keil 默认启动文件 EXPORT SysTick_Handler EXPORT PendSV_Handler ; ... 其他向量定义 SysTick_Handler PROC IMPORT xPortSysTickHandler B xPortSysTickHandler ENDP PendSV_Handler PROC IMPORT xPortPendSVHandler B xPortPendSVHandler ENDP同时FreeRTOSConfig.h中必须设置匹配的系统节拍频率// FreeRTOSConfig.h 关键参数单位Hz #define configTICK_RATE_HZ ( ( TickType_t ) 1000 ) // 每秒 1000 次节拍1ms 间隔 #define configCPU_CLOCK_HZ ( ( unsigned long ) 72000000 ) // STM32F103 主频 72MHz #define configSYSTICK_CLOCK_HZ ( configCPU_CLOCK_HZ / 8 ) // SysTick 使用 AHB/8 9MHz注意configSYSTICK_CLOCK_HZ必须等于 SysTick 实际输入时钟。STM32F103 的 SysTick 默认使用 AHB 总线时钟72MHz分频后输入常见分频系数为 8故为 9MHz。若此处设错xTaskGetTickCount()返回值将严重失准进而导致freertos堆栈溢出检测逻辑误判或freertos创建任务延迟异常。2.3 映射板级支持层从 Demo 目录提取最小硬件适配Demo/目录下有上百个子目录如Demo/CORTEX_M3_STM32F103_GCC/。不要直接复制整个 Demo 工程——它包含大量调试代码和外设驱动。应只提取三个关键文件文件路径作用是否必需Demo/CORTEX_M3_STM32F103_GCC/STM32F103_Init.c初始化 RCC、GPIO、SysTick 等基础外设✅ 必需尤其 SysTick 初始化Demo/CORTEX_M3_STM32F103_GCC/FreeRTOSConfig.h经过该平台验证的配置模板✅ 推荐比默认模板更贴合 STM32Demo/CORTEX_M3_STM32F103_GCC/main.c展示xTaskCreate()和vTaskStartScheduler()调用顺序✅ 必需学习freertos学习篇一的起点将这三个文件拷贝到你的工程目录后必须修改main.c中的main()函数签名Keil 要求int main(void)而 Demo 中可能是void main(void)否则链接时报undefined symbol __main。2.4 映射应用层验证最小可运行任务完成上述映射后编写最简应用验证调度器是否启动#include FreeRTOS.h #include task.h static void prvLEDTask( void *pvParameters ) { const TickType_t xDelay pdMS_TO_TICKS(500); // 500ms for( ;; ) { HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0); // 假设 PB0 接 LED vTaskDelay( xDelay ); } } int main(void) { HAL_Init(); SystemClock_Config(); // 配置 72MHz 主频 MX_GPIO_Init(); // 初始化 GPIO含 PB0 xTaskCreate( prvLEDTask, LED, configMINIMAL_STACK_SIZE, NULL, 1, NULL ); vTaskStartScheduler(); // 启动调度器 —— 此后永不返回 while(1); // 调度器未启动才会执行到这里 }编译前在 Keil 的Options for Target → C/C → Define中添加宏ARM_MATH_CM3,STM32F10X_MD根据你使用的 STM32 型号调整。提示若编译报错.\obj\freertos.hex: error: q0147e: failed to create directory .\obj\freertos说明 Keil 输出路径.\\obj\\freertos不存在或权限不足。右键工程 →Options → Output将Output Directory改为绝对路径如D:\MyProject\Objects\并确保该目录可写。这是freertos移植教程中高频出现的环境配置陷阱。3. 在 FreeRTOS v202107.00 上安全集成 SSL/TLS必须绕过内核限制并重写底层 I/OFreeRTOS v202107.00 本身不提供网络协议栈更不包含 SSL/TLS 实现。所谓freertos ssl、freertos怎样生成 ssl 访问 key,crt 文件、freertos ssl错误等需求实质是将第三方 TLS 库如 mbedTLS与 FreeRTOS 协同运行。但 mbedTLS 默认依赖 POSIX socket 和阻塞式read()/write()而 FreeRTOS 无文件描述符概念且网络驱动如 LwIP通常工作在中断队列模式。因此必须重写 mbedTLS 的底层 I/O 接口并严格管理其内存与线程上下文。3.1 选择 mbedTLS 并配置非阻塞 I/O 回调FreeRTOS v202107.00 与 mbedTLS v2.28.32021 年稳定版兼容性最佳。下载 mbedTLS 源码后禁用所有依赖 libc 的模块仅启用核心加密与 TLS// mbedtls_config.h 关键裁剪移除所有 #define MBEDTLS_XXX_C 中的 _C 后缀 #define MBEDTLS_AES_C #define MBEDTLS_CTR_DRBG_C #define MBEDTLS_ENTROPY_C #define MBEDTLS_SHA256_C #define MBEDTLS_X509_CRT_PARSE_C #define MBEDTLS_SSL_TLS_C #define MBEDTLS_SSL_CLI_C #define MBEDTLS_NET_C // 保留但需重写其内部函数注意MBEDTLS_NET_C必须开启因为它是mbedtls_ssl_set_bio()的载体但其内部net_connect()、net_send()等函数必须被替换。否则会链接失败或触发ssl连接错误。3.2 重写 mbedTLS 的 BIO 接口对接 FreeRTOS 队列mbedTLS 通过mbedtls_ssl_set_bio()注册收发回调。在 FreeRTOS 中应使用xQueueSend()和xQueueReceive()替代阻塞 socket// 全局定义在 tls_wrapper.c 中 static QueueHandle_t xTlsTxQueue NULL; static QueueHandle_t xTlsRxQueue NULL; // 发送回调将数据推入发送队列 static int tls_send( void *ctx, const unsigned char *buf, size_t len ) { BaseType_t xHigherPriorityTaskWoken pdFALSE; for( size_t i 0; i len; i ) { xQueueSendFromISR( xTlsTxQueue, buf[i], xHigherPriorityTaskWoken ); } portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); return (int)len; } // 接收回调从接收队列取数据非阻塞 static int tls_recv( void *ctx, unsigned char *buf, size_t len ) { size_t received 0; while( received len xQueueReceive( xTlsRxQueue, buf[received], 0 ) pdTRUE ) { received; } return (received 0) ? MBEDTLS_ERR_SSL_WANT_READ : (int)received; } // 初始化 TLS 上下文时绑定 mbedtls_ssl_init( ssl_ctx ); mbedtls_ssl_config_init( ssl_conf ); mbedtls_ssl_config_defaults( ssl_conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT ); mbedtls_ssl_set_bio( ssl_ctx, NULL, tls_send, tls_recv, NULL );3.3 管理 TLS 内存与线程避免堆栈溢出与证书验证失败mbedTLS 在握手过程中会动态申请大量内存尤其处理 X.509 证书链时而 FreeRTOS 的heap_4.c默认堆空间仅几 KB。若不扩容将触发freertos堆栈溢出检测机制或静默失败// FreeRTOSConfig.h 中扩大总堆单位字节 #define configTOTAL_HEAP_SIZE ( ( size_t ) ( 64 * 1024 ) ) // 从默认 16KB 提至 64KB // 为 TLS 任务单独分配大堆栈避免主线程溢出 #define TLS_TASK_STACK_SIZE ( 4096 ) // 至少 4KB远超 configMINIMAL_STACK_SIZE128 字 xTaskCreate( tls_client_task, TLS, TLS_TASK_STACK_SIZE, NULL, 3, NULL );证书验证失败no required ssl certificate was sent通常因以下三者之一原因检查方法修复方式服务器证书链不完整用openssl s_client -connect example.com:443 -showcerts抓取全链将-----BEGIN CERTIFICATE-----到-----END CERTIFICATE-----的全部内容含中间 CA拼接进ca_certs.pem时间未校准mbedtls_x509_crt_parse()返回-0x2700在tls_client_task开头调用mbedtls_platform_set_time()设置 UTC 时间戳证书域名不匹配mbedtls_ssl_get_verify_result()返回BADCERT_CN_MISMATCH确保mbedtls_ssl_set_hostname(ssl_ctx, api.example.com)中的 hostname 与证书 CN 或 SAN 完全一致提示freertos面试题汇总中高频出现的“如何检查线程内存使用”在此场景至关重要。应在tls_client_task中周期调用UBaseType_t uxHighWaterMark uxTaskGetStackHighWaterMark(NULL); printf(TLS task stack high water: %d bytes\n, uxHighWaterMark);若该值持续低于 200 字节说明栈即将溢出必须增大TLS_TASK_STACK_SIZE。4. 移植 LVGL 到 FreeRTOS v202107.00图形刷新与任务调度的协同机制freertos移植lvgl不是简单添加源文件而是解决 LVGL 的“刷新时机”与 FreeRTOS “任务调度粒度”之间的根本矛盾。LVGL 默认采用轮询式刷新lv_timer_handler()每 5ms 调用一次但在 FreeRTOS 中若将其放在高优先级任务中持续运行会饿死其他任务若放在低优先级任务中则 UI 响应迟钝。v202107.00 的解决方案是利用xTimerCreate()创建软件定时器并在定时器回调中触发 LVGL 刷新从而解耦调度与渲染。4.1 构建 LVGL 与 FreeRTOS 的事件桥接层LVGL 5.x/6.x 均支持lv_tick_inc()和lv_timer_handler()两个核心接口。前者通知 LVGL 经过多少毫秒后者执行实际渲染。在 FreeRTOS 中应让 SysTick 中断每 1ms 调用lv_tick_inc(1)再用独立定时器每 5ms 触发lv_timer_handler()// lv_port_freertos.c static TimerHandle_t xLvglTimer NULL; // SysTick 中断服务程序已在 port.c 中注册 void SysTick_Handler(void) { if (xTaskGetSchedulerState() ! taskSCHEDULER_NOT_STARTED) { xPortSysTickHandler(); } lv_tick_inc(1); // LVGL 时间基准必须在中断中调用 } // LVGL 定时器回调在任务上下文中执行 static void prvLvglTimerCallback(TimerHandle_t xTimer) { lv_timer_handler(); // 执行 LVGL 渲染、动画、事件处理 } // 初始化 LVGL 时创建定时器 void lv_port_init(void) { lv_init(); // 创建 5ms 周期定时器精度由 FreeRTOS tick 决定 xLvglTimer xTimerCreate( LVGL, // 名称 pdMS_TO_TICKS(5), // 周期5ms pdTRUE, // 自动重载 (void *)0, // 参数未使用 prvLvglTimerCallback // 回调函数 ); if (xLvglTimer ! NULL) { xTimerStart(xLvglTimer, 0); // 启动定时器 } }4.2 配置 LVGL 的内存与线程安全策略LVGL 默认使用malloc()必须重定向到 FreeRTOS 的pvPortMalloc()// lv_conf.h 中启用内存钩子 #define LV_MEM_CUSTOM 1 #if LV_MEM_CUSTOM 1 #define LV_MEM_ADD_MEM_AVAILABLE() /* 无需额外操作 */ #define LV_MEM_ALLOC(size) pvPortMalloc(size) #define LV_MEM_FREE(ptr) vPortFree(ptr) #endifLVGL 的对象创建如lv_btn_create()和事件回调如按钮点击可能跨任务调用必须加锁// lv_port_freertos.c 中实现 LVGL 的互斥锁 static SemaphoreHandle_t xLvglMutex NULL; void lv_port_lock(void) { if (xLvglMutex ! NULL) { xSemaphoreTake(xLvglMutex, portMAX_DELAY); } } void lv_port_unlock(void) { if (xLvglMutex ! NULL) { xSemaphoreGive(xLvglMutex); } } // 初始化时创建互斥量 void lv_port_init(void) { lv_init(); xLvglMutex xSemaphoreCreateMutex(); if (xLvglMutex NULL) { /* 错误处理 */ } // 告诉 LVGL 使用自定义锁 lv_mutex_create(lvgl_mutex); lv_mutex_set_lock_cb(lv_port_lock); lv_mutex_set_unlock_cb(lv_port_unlock); }4.3 验证 LVGL 移植用 FreeRTOS 任务驱动动画创建一个独立任务每 2 秒改变 LVGL 按钮背景色验证任务与图形系统协同static lv_obj_t *btn1 NULL; static void prvAnimationTask(void *pvParameters) { const lv_color_t colors[] {LV_COLOR_RED, LV_COLOR_GREEN, LV_COLOR_BLUE}; uint8_t color_idx 0; for(;;) { lv_obj_set_style_bg_color(btn1, colors[color_idx], LV_PART_MAIN); color_idx (color_idx 1) % 3; vTaskDelay(pdMS_TO_TICKS(2000)); } } // 在 main() 中初始化后创建该任务 btn1 lv_btn_create(lv_scr_act()); lv_obj_set_size(btn1, 120, 50); lv_obj_center(btn1); xTaskCreate(prvAnimationTask, LVGL_ANIM, 2048, NULL, 2, NULL);提示若 UI 卡顿或动画跳帧首先检查pdMS_TO_TICKS(5)是否被configTICK_RATE_HZ限制。例如当configTICK_RATE_HZ10010ms tick时5ms 定时器无法精确触发此时应将 LVGL 定时器周期改为pdMS_TO_TICKS(10)并在prvLvglTimerCallback中连续调用两次lv_timer_handler()以维持吞吐量。这是freertos项目实战中处理freertos 数控类高实时 UI 的关键技巧。本文还有配套的精品资源点击获取
返回列表