当前位置:首页 > EDA > 电子设计自动化
[导读]裸机里高优先级中断往环形缓冲写、主循环读,常见丢数据不是缓冲太小一种原因:指针非原子被拆、满时静默覆盖、单字节中断太频繁被更高优先级任务堵住,都会丢。下面给三套可直接落地的方案。


裸机里高优先级中断往环形缓冲写、主循环读,常见丢数据不是缓冲太小一种原因:指针非原子被拆、满时静默覆盖、单字节中断太频繁被更高优先级任务堵住,都会丢。下面给三套可直接落地的方案。

一、单写单读SPSC:2幂缓冲+位与,尽量无锁

中断只做生产者、主循环只做消费者时,把缓冲区大小设为2的幂,用位与代替取模,head/tail声明volatile,32位指针在Cortex-M上单写单读可做到几乎无锁。

#define RB_SIZE 256u

#define RB_MASK (RB_SIZE-1u)

volatile uint8_t rb_buf[RB_SIZE];

volatile uint16_t rb_head; // 仅ISR写

volatile uint16_t rb_tail; // 仅主循环读改


void rb_isr_put(uint8_t d){

   uint16_t next = (rb_head+1u)&RB_MASK;

   if(next!=rb_tail){               // 有空位才写

       rb_buf[rb_head]=d;

       rb_head=next;

   }else{

       rb_overflow_cnt++;           // 满:记溢出,不覆盖

   }

}

int rb_get(uint8_t *d){

   if(rb_tail==rb_head) return -1;  // 空

   *d=rb_buf[rb_tail];

   rb_tail=(rb_tail+1u)&RB_MASK;

   return 0;

}

主循环只调rb_get,不碰rb_head写逻辑;ISR只更新rb_head。SPSC下避免同时读写同一指针变量,可显著降低竞态。

二、多路/高优先级竞争:最小临界区+显式满策略

若CAN、UART多个中断都写同一缓冲,或主循环还要算可用空间,必须短关中断。只保护“判满—写—更指针”三行,绝不把解析放进去。满策略要明确:实时遥测丢最旧保最新,日志/命令丢新保完整。

uint8_t rb_put_safe(uint8_t d,int drop_old){

   uint8_t ok=0;

   __disable_irq();

   uint16_t next=(rb_head+1u)&RB_MASK;

   if(next!=rb_tail){

       rb_buf[rb_head]=d; rb_head=next; ok=1;

   }else if(drop_old){

       rb_tail=(rb_tail+1u)&RB_MASK;   // 丢最旧

       rb_buf[rb_head]=d; rb_head=next; ok=1;

   }

   __enable_irq();

   if(!ok) rb_overflow_cnt++;

   return ok;

}

Cortex-M可用BASEPRI只屏蔽低优先级,保留真正高优先级中断;全局关中断只在极短区间用,避免把ADC、控制定时器饿死。

三、高速外设:DMA循环+IDLE/半满搬入二级环形

115200以上、多字节突发时,单字节RXNE中断会被高优先级任务挤压出ORE。改DMA循环写到内存大缓冲,用IDLE断帧或半传输/传输完成中断把数据搬进软件环形,CPU只在“一批”到达时介入。

uint8_t dma_buf[512];

// DMA Circular: UART->dma_buf;IDLE中断里算新写指针

void uart_idle_isr(void){

   uint16_t left=DMA_GetCurrDataCounter(DMA1_Chx);

   uint16_t w=512-left;            // DMA当前写位置

   while(rb_head!=w){              // 搬入二级环形

       rb_isr_put(dma_buf[rb_dma_rd]);

       rb_dma_rd=(rb_dma_rd+1u)&511u;

   }

   clear_idle_flag();

}

不定长用IDLE,定长大块用半满/全满双段搬;H7/F7开D-Cache要把dma_buf放Non-cacheable,避免CPU读到旧值。 DMA只解决“外设到内存”搬运,协议解析仍走环形缓冲,主循环按帧取。

选型小结

低速率、单中断源:方案一位与环形最轻;多中断或要算空闲空间:方案二短临界区;高速串口/CAN/ADC持续流:方案三DMA+环形。三者共同原则——head/tail用volatile、缓冲区2幂、满必须有策略、ISR只写不解析。按这个改,高优先级中断丢包可从偶发百分之几降到可观测、可统计、可回退。

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
关闭