个人网站可以做seo吗,取消wordpress还原,班级优化大师是干什么用的,新闻宣传wordpress主题前言在掌握了“数据流”和“控制流”之后#xff0c;我们来到了嵌入式开发中最容易发生“翻车”的地带#xff1a;资源竞争。如果你的代码在任务 A 里正在通过 I2C 发送 10 个字节#xff0c;发到一半#xff0c;任务 B#xff08;高优先级#xff09;强行插入并抢走 I2C…前言在掌握了“数据流”和“控制流”之后我们来到了嵌入式开发中最容易发生“翻车”的地带资源竞争。如果你的代码在任务 A 里正在通过 I2C 发送 10 个字节发到一半任务 B高优先级强行插入并抢走 I2C 的控制权去读取另一个传感器那么 I2C 总线上的波形就会彻底崩掉。1. 为什么你的 I2C/SPI 总线会无故死锁重入冲突外设驱动如 HAL 库内部通常有状态机不能在未完成时被重入调用。优先级反转任务 C低占着串口任务 A高死等串口结果任务 B中跑出来把任务 C 抢占了导致 A 实际上在等 B。效率低下频繁开关中断会导致系统失去实时性。2. 知识点保护资源的三种境界第一重原子操作与关中断最硬核的锁应用修改一个简单的全局标志位或计数器。手段taskENTER_CRITICAL()。它直接关闭所有可屏蔽中断。代价整个世界都停了。如果这里逻辑太长你的系统时钟滴答都会丢失。第二重互斥锁Mutex应用保护硬件外设I2C/SPI/文件系统。手段osMutexAcquire。它支持优先级继承是 RTOS 中保护资源的正规军。第三重守护任务模式Gatekeeper —— 老鸟的首选理念“既然大家都要抢那就谁也别直接碰找个管家管起来”。优点资源操作被完全串行化不存在竞争且方便添加缓存逻辑。3. 实操构建一个串口“守护任务”场景我们不再让每个任务都去调用HAL_UART_Transmit而是建立一个PrintServer任务。3.1 守护任务的实现/* 定义日志消息结构 */ typedef struct { char *str; uint32_t value; } LogEntry_t; osMessageQueueId_t LogQueueHandle; //消息队列ID void StartLogGatekeeperTask(void *argument) { LogEntry_t log; LogQueueHandle osMessageQueueNew(16, sizeof(LogEntry_t), NULL); //创建消息队列 for(;;) { // 阻塞等待只有有人要打印时管家才干活 if (osMessageQueueGet(LogQueueHandle, log, NULL, osWaitForever) osOK) { // 在这里统一操作硬件不存在竞争 printf([LOG] %s: %d\r\n, log.str, log.value); } } }3.2 客户端任务的调用void StartAppTask(void *argument) { LogEntry_t myLog {Temp, 25}; for(;;) { // 投递即走不阻塞自己的逻辑去等串口发送将要打印的Log写入队列 osMessageQueuePut(LogQueueHandle, myLog, 0, 0); osDelay(1000); } }4. 进阶互斥锁保护 SPI 总线如果你必须在多个任务里直接操作外设比如复杂的屏驱请使用互斥锁。osMutexId_t SpiMutexHandle; void Init_Resources(void) { // 互斥锁必须在任务开始前创建 SpiMutexHandle osMutexNew(NULL); } void Access_Spi_Device(void) { // 1. 拿钥匙 if (osMutexAcquire(SpiMutexHandle, osWaitForever) osOK) { // 2. 独占操作硬件 // HAL_SPI_Transmit(hspi1, data, size, 100); // 3. 还钥匙 osMutexRelease(SpiMutexHandle); } }5. 思考读写锁模型有时候我们允许多个任务“读”参数如配置信息但只允许一个任务“写”。实操技巧这种场景可以用计数信号量Counting Semaphore模拟或者使用互斥锁保护一个读计数器。原则写者必须拿到最高权限读者可以并存。6. 总结本章保护共享资源要么用“锁”把它保护起来要么找个“管家”替你守着它。