Attiny85引脚错误下基于引脚变化中断+Timer1解码DCC信号故障排查
Attiny85 DCC解码故障排查与修复方案
核心问题1:Timer1预分频配置完全错误,导致计时精度不足
你的Timer1设置了预分频1024(TCCR1 = (1 << CS12)),在8MHz时钟下,单个计数周期为 1024/8000000 = 128μs。而DCC的1位时长仅116μs,比单个计数周期还短,timerVal只能是0或1,完全无法区分116μs和200μs的位间隔,这是解码失败的根本原因。
修复方案:
修改Timer1预分频为64,单个计数周期为64/8000000 = 8μs,刚好能精准测量DCC位间隔:
void setupTimer1() { TCCR1 = 0; // Reset control register TCNT1 = 0; // Reset counter // 预分频64:CS11=1 + CS10=1 TCCR1 = (1 << CS11) | (1 << CS10); }
同时调整位判断的阈值为计数tick数:
const uint8_t ONE_BIT_TICKS = 14; // 116μs / 8μs ≈14.5 const uint8_t ZERO_BIT_TICKS = 25; // 200μs / 8μs =25 // ISR中的判断逻辑修改为: if( timerVal > (ONE_BIT_TICKS - 2) && timerVal < (ONE_BIT_TICKS + 2) ) { addBitToBuffer(1); // 先入缓冲区,主循环处理 } else if( timerVal > (ZERO_BIT_TICKS - 3) && timerVal < (ZERO_BIT_TICKS + 3) ) { addBitToBuffer(0); }
核心问题2:引脚变化中断标志位未清除,导致重复触发
Attiny85的引脚变化中断触发后,GIFR寄存器的PCIF位会置1,必须软件写1清除,否则会持续触发中断,导致ISR反复执行、丢比特或误判。
修复方案:
在ISR开头添加标志位清除代码:
ISR(PCINT0_vect) { // 清除引脚变化中断标志 GIFR |= (1 << PCIF); static uint8_t lastPinState = 1; // ... 剩余代码 }
核心问题3:ISR耗时过长,导致丢中断
你在ISR中直接调用dccBitHandler,该函数包含预amble计数、数据包解析等大量逻辑,执行时间远超DCC位间隔(116μs),会导致下一个中断到来时无法响应,直接丢比特。
修复方案:
ISR只做最小化操作:计时、位判断,将有效位存入环形缓冲区,主循环中再处理解码逻辑:
// 环形缓冲区定义 #define BIT_BUFFER_SIZE 32 volatile uint8_t bitBuffer[BIT_BUFFER_SIZE]; volatile uint8_t bitBufferHead = 0; volatile uint8_t bitBufferTail = 0; // 向缓冲区添加位 static void addBitToBuffer(uint8_t bit) { uint8_t nextHead = (bitBufferHead + 1) % BIT_BUFFER_SIZE; if(nextHead != bitBufferTail) { bitBuffer[bitBufferHead] = bit; bitBufferHead = nextHead; } } // ISR简化为: ISR(PCINT0_vect) { GIFR |= (1 << PCIF); static uint8_t lastPinState = 1; uint8_t pinState = (PINB & (1 << PB4)) ? 1 : 0; if (lastPinState == 1 && pinState == 0) { uint8_t timerVal = TCNT1; TCNT1 = 0; uint8_t bit = 2; // 无效位标记 if( timerVal > (ONE_BIT_TICKS - 2) && timerVal < (ONE_BIT_TICKS + 2) ) { bit = 1; } else if( timerVal > (ZERO_BIT_TICKS - 3) && timerVal < (ZERO_BIT_TICKS + 3) ) { bit = 0; } if(bit != 2) { addBitToBuffer(bit); } } lastPinState = pinState; } // 主循环处理缓冲区 void loop() { while(bitBufferHead != bitBufferTail) { uint8_t bit = bitBuffer[bitBufferTail]; bitBufferTail = (bitBufferTail + 1) % BIT_BUFFER_SIZE; dccBitHandler(bit); } updateDcc(); }
其他优化点
- 配置PB4输入上拉:避免噪声干扰导致误触发中断
void setup() { // PB4设为输入,启用内部上拉 DDRB &= ~(1 << PB4); PORTB |= (1 << PB4); setupPinChangeInterrupt(); setupTimer1(); // ... 其他初始化 }
- 处理Timer1溢出:防止信号中断时计数溢出导致的错误
void setupTimer1() { TCCR1 = 0; TCNT1 = 0; TCCR1 = (1 << CS11) | (1 << CS10); // 启用Timer1溢出中断 TIMSK |= (1 << TOIE1); } volatile bool timerOverflow = false; ISR(TIMER1_OVF_vect) { timerOverflow = true; } // ISR中添加溢出判断 if (lastPinState == 1 && pinState == 0) { if(timerOverflow) { timerOverflow = false; TCNT1 = 0; lastPinState = pinState; return; } uint8_t timerVal = TCNT1; TCNT1 = 0; timerOverflow = false; // ... 位判断逻辑 }
内容的提问来源于stack exchange,提问作者bas knippels
相关产品推荐
相关产品推荐

