ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

嵌入式系统程序框架:从组成到实现

嵌入式系统程序框架:从组成到实现 文章目录1. 什么是框架2. 嵌入式系统组成2.1 传统组成main 与中断2.2 本套框架采用的 RTX52.2.1 RTX5 安全认证2.2.2 支持的内核2.2.3 授权方式3. 嵌入式系统实现3.1 核心实现思路3.2 示例中断发送信号线程接收并处理3.3 工程目录结构3.4 任务优先级划分建议3.5 常见问题与排查3.5.1 线程优先级反转3.5.2 中断中调用阻塞函数3.5.3 信号丢失3.5.4 栈溢出3.5.5 调试建议4. 总结1. 什么是框架程序框架其实就类似一个文件大纲或者模板。因为写程序就和类似于写文章如果没有大纲或者模板那么你写起来就会比较费劲。一个好的框架能事半功倍节约时间减少错误。2. 嵌入式系统组成嵌入式控制系统基本都是由“main:while(1)”和“中断服务”组成。2.1 传统组成main 与中断main 任务指对时间响应要求不高或者说是那种周期性执行的任务中断任务指对时间响应要求高必须立刻处理的任务。2.2 本套框架采用的 RTX5本套框架将采用 RTX5CMSIS-RTOS2 实现这是 ARM 公司研发的实时操作系统效率高使用便捷。RTX5 可以实现零中断延时也就是跟裸机情况是一样的。2.2.1 RTX5 安全认证RTX5 的汽车级、工业级、医疗和铁路安全认证已经通过ISO 26262 (ASIL D) 汽车级最高安全认证IEC 61508 (SIL 3) 工业级认证IEC 62304 (Class C) 医疗认证EN 50128 (SIL 4) 运输/铁路安全认证2.2.2 支持的内核Cortex-M0/M0Cortex-M3Cortex-M4Cortex-M72.2.3 授权方式RTX4 和 RTX5 都已经是开源免费的采用 Apache-2.0 授权可随意商用无需付费。综合来看裸机编程、RTX5 与 FreeRTOS 的核心差异如下对比项裸机编程RTX5FreeRTOS实时性最高无操作系统调度开销中断响应由开发者直接控制高可实现零中断延时任务切换确定性好较好支持抢占式调度但中断临界区可能带来一定延迟内存占用极小仅包含业务代码本身小内核针对 Cortex-M 裁剪RAM/ROM 开销低中等内核精简但随任务数量和功能增加栈与堆占用上升安全认证无内置安全认证需自行完成功能安全设计已通过 ISO 26262 (ASIL D)、IEC 61508 (SIL 3)、IEC 62304 (Class C)、EN 50128 (SIL 4) 等认证官方内核通常需额外安全评估SafeRTOS 等衍生版本可提供认证支持开源协议不涉及 RTOS 开源协议Apache-2.0开源免费可商用MIT开源免费可商用3. 嵌入式系统实现3.1 核心实现思路中断发送信号线程接收信号并处理利用软定时器节约硬件资源采用注册回调函数的方式实现功能与业务分层。3.2 示例中断发送信号线程接收并处理下面以按键中断触发业务处理为例演示 RTX5 中「中断发送信号、线程接收信号并处理」的典型流程#includecmsis_os2.h/* 按键信号标志 */#defineKEY_PRESS_FLAG0x01U/* 主线程 ID用于在中断服务函数中向该线程发送信号 */osThreadId_tmain_thread_id;/* * 按键中断服务函数 * 在中断中只做必要的硬件状态清理然后发送信号给主线程。 * RTX5 的 osThreadFlagsSet 可以在 ISR 中安全调用不会阻塞中断。 */voidEXTI0_IRQHandler(void){/* 1. 清除外部中断挂起位避免重复进入中断 */if(EXTI-PREXTI_PR_PR0){EXTI-PREXTI_PR_PR0;}/* 2. 向主线程发送按键信号 */osThreadFlagsSet(main_thread_id,KEY_PRESS_FLAG);}/* * 主线程接收中断信号并执行实际业务处理 */voidapp_main_thread(void*argument){(void)argument;for(;;){/* * 等待按键信号 * - 等待目标标志 KEY_PRESS_FLAG * - osFlagsWaitAny任一标志置位即返回 * - osWaitForever没有收到信号时挂起线程让出 CPU */uint32_tflagsosThreadFlagsWait(KEY_PRESS_FLAG,osFlagsWaitAny,osWaitForever);/* 确认是否收到目标信号 */if((flagsKEY_PRESS_FLAG)!0U){/* 执行按键业务处理例如翻转 LED、上报状态等 */led_toggle();}}}/* 系统初始化时创建主线程并保存线程 ID */voidapp_init(void){main_thread_idosThreadNew(app_main_thread,NULL,NULL);}3.3 工程目录结构├─app │ ├─app_key │ ├─app_adc │ ├─app_led │ ├─app_power │ ├─app_temp_control │ └─app_soft_voltameter ├─lib │ ├─x_strtok │ ├─str_hex │ └─crc16 ├─bsp │ ├─cx32l003 │ └─nrf52 ├─os │ ├─rtx │ └─rtx5 ├─sys │ ├─cx32f0 │ └─nrf52 ├─drivers │ ├─include │ └─g_sensor │ └─adxl34x ├─project │ ├─bt_ant_code_table │ ├─bt_speaker │ ├─bike_lamp │ │ ├─head_tail_lamp │ │ │ ├─cx32_RX1500 │ │ │ │ └─business │ │ └─public_code │ │ ├─cx32_bootload │ │ │ └─boot │ │ ├─biz_ldr │ │ ├─biz_power │ │ ├─biz_temp │ │ ├─biz_uart │ │ ├─check_uid │ │ └─comm_uart ├─chip │ ├─CX32L003_SDK │ └─nRF5_SDK_17.0.2_d674dde ├─tool │ ├─xBin2Dfu │ ├─xAudioTool │ │ ├─.vscode │ │ ├─res │ │ ├─src │ │ └─ui │ ├─LiSunTool ├─platform │ ├─log │ │ ├─cx32f0 │ │ └─nrf52 │ └─at_comm │ └─cx32f0 ├─protocol_stack └─protocol3.4 任务优先级划分建议在 RTX5 中线程优先级通过 CMSIS-RTOS2 的osPriority配置数值越大优先级越高硬件中断则始终先于所有线程执行。结合本框架“中断发信号、线程收信号并处理、软定时器节约硬件资源”的思路建议按如下原则分配中断服务函数只做清中断标志、读取硬件状态、发送信号等短操作不要执行耗时业务。硬件中断自然拥有最高实时性是系统响应的第一入口。主线程负责接收中断信号并执行按键、通信、控制等业务处理建议配置为osPriorityAboveNormal或osPriorityHigh保证事件得到及时响应但要避免长时间独占 CPU。软定时器任务用于周期采集、周期上报、超时检测等非紧急工作建议配置为osPriorityNormal或osPriorityBelowNormal优先级低于主线程避免抢占关键业务。一个简单的优先级配置示例如下任务类型优先级建议说明按键中断EXTI0_IRQHandler硬件中断最高由硬件保证仅清标志并发送KEY_PRESS_FLAG主线程app_main_thread线程osPriorityAboveNormal接收信号处理按键业务软定时器周期任务软定时器回调osPriorityNormal周期采样或超时检测执行应简短实际开发中应结合系统负载调整优先级优先层级不宜划分过细高优先级线程要避免死循环或长时间等待否则可能导致低优先级任务饿死。3.5 常见问题与排查RTX5 使用过程中开发者常遇到以下几类典型问题下面逐一分析原因并给出解决方案。3.5.1 线程优先级反转现象低优先级线程持有共享资源高优先级线程等待该资源时被阻塞而中等优先级线程趁机抢占 CPU导致高优先级线程迟迟得不到执行。原因RTX5 默认不启用优先级继承机制当多个线程竞争同一互斥量Mutex时可能发生优先级反转。解决方案使用osMutexNew创建互斥量时通过osMutexAttr_t的attr_bits设置osMutexPrioInherit启用优先级继承让持有互斥量的低优先级线程临时提升优先级缩短高优先级线程的等待时间。尽量缩短临界区代码避免在持锁期间执行耗时操作。若业务允许优先使用信号量或事件标志等无阻塞机制替代互斥量。3.5.2 中断中调用阻塞函数现象程序在中断服务函数中调用osDelay、osMutexAcquire等阻塞 API导致系统卡死或行为异常。原因RTX5 的中断服务函数运行在中断上下文不允许发生线程切换或阻塞等待调用阻塞 API 会破坏内核调度状态。解决方案中断中只允许调用 ISR 安全 API如osThreadFlagsSet、osSemaphoreRelease、osMessageQueuePut等。需要处理耗时业务时采用「中断发信号、线程收信号并处理」的模式把业务逻辑放到线程中执行。可在编译期开启 RTX5 的配置宏RTX_EVALUATE_ISR或使用osKernelGetState判断当前是否处于中断上下文便于调试定位。3.5.3 信号丢失现象高频中断触发时线程偶尔收不到某些信号业务处理出现遗漏。原因osThreadFlagsSet发送的是「事件标志」而非「计数信号量」同一标志位重复置位不会累积若线程处理速度跟不上中断频率中间多次触发会被合并为一次。解决方案若需要精确计数改用osSemaphoreRelease/osSemaphoreAcquire信号量会累积释放次数。若业务允许合并处理如按键去抖、状态刷新事件标志即可满足需求无需改动。提高接收线程优先级或把高频事件先缓存到队列osMessageQueuePut再批量处理。3.5.4 栈溢出现象程序运行一段时间后随机崩溃、进入 HardFault或线程行为异常。原因线程栈分配过小函数调用层级深或局部变量占用过大导致栈溢出覆盖了相邻内存区域。解决方案合理评估线程栈大小主线程建议分配 1024 字节以上复杂业务可适当加大。开启 RTX5 的栈检测功能在RTX_Config.h中使能RTX_STACK_CHECK并在osThreadNew时传入osThreadDetached属性溢出时会触发osRtxErrorNotify回调。在osRtxErrorNotify回调中打印出错线程 ID 和栈使用情况便于定位是哪个线程溢出。使用调试器查看线程栈高水位标记High-Water Mark据此调整栈大小。3.5.5 调试建议使用 Keil MDK 的 RTX5 事件查看器Event Recorder可实时观察线程切换、信号量、互斥量等内核事件快速定位调度问题。在关键路径上添加osKernelGetTickCount打印测量任务响应时间验证实时性是否达标。开启RTX_EVALUATE_ISR宏在调试阶段尽早暴露「中断中调用非法 API」的问题。发布前关闭调试打印和栈检测避免影响实时性和增加 ROM 占用。4. 总结本框架基于 RTX5具有零中断延时、低内存占用并通过多项功能安全认证兼顾实时性与可靠性。相比裸机编程它引入线程与信号机制把硬件中断快速转为业务处理提升代码结构和可维护性相比 FreeRTOS在安全认证和 Cortex-M 裁剪效率上更有优势。适用于对实时性、安全性和工程化有要求的嵌入式项目。使用时需合理划分任务优先级避免长时间占用线程并注意中断服务函数应保持简短。
返回列表