【学习笔记】单片机-MCU

适合在中断里处理的操作

1. 硬件原始数据接收、缓存写入

仅简单字节拷贝,不解析、不换算、不判断业务阈值。 示例:

  • UART 中断:把接收字节写入环形缓冲区
  • ADC DMA 完成中断:拷贝原始采样值到缓存数组
  • SPI/I2C 中断:读取寄存器原始数值存入 buffer
  • 射频接收中断:拷贝无线原始报文到缓存

限制:只做简单内存赋值 / 拷贝,不做滤波、换算、协议解析。

2. 事件同步、通知上层

ISR 只发送通知,业务交给任务处理,分两种场景:

裸机

置全局事件标记位

复制代码
void adc_isr(void)
{
    adc_buf[adc_idx++] = ADC_DATAR;
    g_flag_adc_done = 1; // 仅置标记,主循环处理
}

FreeRTOS RTOS

使用FromISR系列接口,唤醒阻塞任务

  • 二值信号量:xSemaphoreGiveFromISR
  • 队列发送:xQueueSendFromISR
  • 事件组置位:xEventGroupSetBitsFromISR
  • 给事件驱动框架投递极简事件标记

3. 硬件必须即时响应的底层硬件操作

硬件时序要求严格,晚操作就会丢失数据、外设异常,只能放 ISR:

  1. 清除外设中断标志位(绝大部分外设必须 ISR 内清 flag,否则重复进中断)
  2. 快速切换 IO 电平、PWM 紧急关输出、故障硬件保护(过流立即关 MOS 管)
  3. 简单寄存器配置:切换采样通道、清空 FIFO、复位外设接收指针
  4. 编码器脉冲计数:简单cnt++/cnt--单字节 / 32bit 自增减(原子操作)

4. 极简、无分支、单周期数学运算

仅一行简单加减,无 if/else 判断、无循环:

  • 脉冲计数 g_encoder_val++
  • 采样索引自增 idx = (idx+1) % BUF_LEN

5. 临界资源极简保护(短临界区)

仅几行代码,临时关中断保护环形缓冲区写入,绝对不能长临界区

优先级反转

1)什么是优先级反转(Priority Inversion)

系统三个任务,优先级:高 H > 中 M > 低 L,共享同一个互斥锁保护的资源:

  1. 低优先级 L 先拿到锁,操作共享缓存;
  2. 高优先级 H 就绪,也要这把锁,拿不到进入阻塞;
  3. 中等优先级 M 就绪,M 优先级高于原始 L,直接抢占 L;
  4. M 持续占用 CPU,L 一直无法运行、无法释放锁; 结果:最高优先级 H,被中等优先级 M 无限阻塞,调度优先级完全颠倒,这就是优先级反转。 危害:高实时控制任务延迟、看门狗复位、设备失控。

2)优先级继承(Priority Inheritance,PI)定义

【只保护持有锁的低优先级任务不被中间优先级任务打断,避免高优先级任务无限等待。】

仅 Mutex 互斥锁自带的内核机制(二值 / 计数信号量没有):

高优先级任务 H 阻塞在低优先级任务 L 持有的互斥锁上时,RTOS 内核自动、临时把持有锁的低优先级任务 L 的优先级,提升到和等待它的最高优先级任务 H 一致;

等 L 释放互斥锁后,自动恢复 L 原本的低优先级。

简单记:低优先级任务临时 "继承" 等待它的高优先级任务的优先级 ,故名优先级继承

临界区域

Cortex-M裸机环境下临界区保护的三种实现~-CSDN博客

单片机裸机环境下临界区保护_裸机嵌入式系统的共享资源临界区保护-CSDN博客

**临界区域(Critical Section)** 是指访问共享资源(如变量、数据结构、文件、硬件设备等)的一段代码。为了保证数据的一致性和程序的正确性,必须确保在任意时刻,最多只有一个执行线程(或进程)能够进入其临界区域。这种互斥访问的机制,就是临界区域机制。

裸机程序-嵌套问题-使用变量flag标记

嵌套会导致"中断"提前打开,无法签到对嵌套的"后续部分"的保护;

Cortex-M 使用PRIMASK寄存器防止嵌套

RTOS 环境下临界区保护

8. 临界段的保护 --- FreeRTOS内核实现与应用开发实战指南---基于STM32 文档 (embedfire.com)

PendSV

PendSV(可挂起系统调用)是 Cortex‑M 专属低优先级异常 ,专门用于任务上下文切换 ; 它优先级低于普通硬件中断,保证:硬件中断能优先打断上下文切换,中断处理完再切任务。

场景 1:主动触发 ------ 任务主动放弃 CPU(最常见)

  • vTaskDelay() / osDelay() 延时阻塞;
  • xQueueReceive()xSemaphoreTake() 无资源进入阻塞;
  • 低优先级任务主动 taskYIELD() 出让 CPU;

场景 2:抢占触发 ------ 高优先级任务就绪,抢占当前低优先级任务

  1. 串口、ADC、定时器 ISR 调用 xSemaphoreGiveFromISR / xQueueSendFromISR
  2. 高优先级任务变为就绪态;
  3. 中断退出前,RTOS 判断存在更高优先级就绪任务,置位 PendSV;
  4. 硬件中断执行完毕退出后,立即进入 PendSV 执行任务切换。

关键设计:不在中断内部直接切栈,推迟到 PendSV 里切换,简化中断处理、保证中断响应速度

场景 3:时间片用完后,SysTick 内部置位 PendSV,发起任务切换。