从零拆解 embedmq:一个纯 C 实现的嵌入式线程间事件总线
引言嵌入式项目里,线程间通信是绕不开的问题。传感器线程读到数据,UI 线程要更新显示,网络线程要上报——它们之间怎么传消息? 最直接的做法是让模块互相持有对方的指针或队列句柄,但代价是强耦合:改一个模块,另一个也要跟着改。 embedmq 是我写的一个零依赖 C11 库,把线程间消息分发压缩成三个函数:create、register、post。本文从源码层面拆解它的每一个设计决策。 一、传统做法的痛点裸机:flag 泛滥12345678910volatile bool g_uart_ready = false;volatile bool g_sensor_ready = false;void main(void) { while (1) { if (g_uart_ready) { process_uart(); g_uart_ready = false; } if (g_sensor_ready) { update_display(); g_sensor_ready = fa...
嵌入式开发者视角的 Google C++ Style Guide 实战解读
一、这东西到底有什么用?2013 年 Google 把内部 C++ 规范扔上了 GitHub。现在 38000+ Star,Chromium 在用,LLVM 在参考,国内大厂的规范里也多多少少能看到它的影子。 但它从一开始就没打算当”温和的建议”。它禁异常、禁 RTTI、禁 C 风格转型、禁全局变量、禁静态存储期对象。每一条单独拎出来都能在技术群里吵一个下午。 这些规则背后有一个简单的事实:这份规范是为 100M+ 行代码、上万工程师、维护几十年的代码库写的。这个场景跟嵌入式出奇地像——二进制要小、控制流要稳、出问题不能靠抛异常甩锅。 我不是来翻译官方文档的。下面从写了几十万行嵌入式 C/C++ 的经验出发,拆哪些能直接用、哪些得改改、哪些 Google 自己也没那么认真。 二、核心哲学:为什么偏要优化给”读者”看?Google Style 的第一句话就能劝退不少人: Optimize for the reader, not the writer. 说白了:写的时候多花 5 秒,让别人(以及三个月后的你自己)读的时候省 5 分钟。 这在嵌入式项目里意味着什么?拿...
FreeRTOS 学习笔记(三):信号量与互斥锁
信号量和队列是亲戚——信号量本质上就是个不许传数据的队列。它不关心消息内容,只关心”有没有”。 二值信号量(Binary Semaphore)。 就像一个只能放一个令牌的盒子。任务调用 xSemaphoreTake() 拿走令牌,盒子空了;另一个任务(或 ISR)调用 xSemaphoreGive() 放回令牌,任务被唤醒。 12345678910SemaphoreHandle_t xSemaphoreCreateBinary(void);// 消费者if (xSemaphoreTake(sem, portMAX_DELAY) == pdTRUE) { // 拿到令牌,干活}// 生产者(任务或 ISR)xSemaphoreGive(sem); // 任务里用xSemaphoreGiveFromISR(sem, &woken); // ISR 里用 最常见的场景:ISR 通知任务”数据准备好了”。 12345678910111213141516SemaphoreHandle_t g_data_ready;// UAR...
FreeRTOS 学习笔记(二):队列
任务之间怎么传数据?最简单的办法是全局变量。但全局变量没有”阻塞等待”能力——消费者不知道数据什么时候准备好,只能轮询。 队列解决了这个问题:生产者往里面放,消费者从里面取。如果队列空了,消费者可以选择阻塞等待。 它本质上是个先进先出的缓冲区,但多了任务间同步的能力。 创建队列: 1234QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, // 最多存几条消息 UBaseType_t uxItemSize // 每条消息多大); 注意:uxItemSize 是每条消息的大小,不是总大小。队列的实际内存 = uxQueueLength * uxItemSize,这块内存由 FreeRTOS 从堆上分配。 如果不想用堆,可以用 xQueueCreateStatic(),自己提供 uint8_t 缓冲区。 发送: 12345BaseType_t xQueueSend( QueueHandle_t xQueue, const void * pvItemToQueue, ...
FreeRTOS 学习笔记(一):任务管理
一个 FreeRTOS 任务就是一个永不返回的 C 函数: 123456void MyTask(void *pvParameters) { while (1) { // 干点什么 vTaskDelay(pdMS_TO_TICKS(100)); }} 任务在五种状态之间切换: 123456789101112131415161718192021 xTaskCreate() │ ▼┌──────────── 就绪态 ◄────────────────┐│ │ ││ 调度器选中 时间片到/被抢占│ │ ││ ▼ ││ 运行态 ───────────────...


