You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Arduino ATmega328P-PU(16MHz)定时器脉冲时序异常问题咨询

解决Arduino ATmega328P定时器CTC模式下引脚高电平占比异常的问题

看起来你的问题核心是定时器中断的触发逻辑和引脚状态设置的顺序搞反了,导致高电平只占了周期的1/12左右。咱们一步步拆解问题,找到根源并修正:

问题分析

你提到定时器工作在CTC模式,计数到28触发ISRB,到340触发ISRA后循环。从时间占比来看,1/12的周期刚好对应0到28的计数区间(341个总计数周期 ÷12≈28.4),这说明当前你的引脚高电平是从周期开始(计数0)到28就被拉低,而不是你期望的从28到340保持高电平。这种情况大概率是ISR里的引脚状态设置顺序反了。

另外先确认一个关键细节:ATmega328P的Timer0是8位定时器,最大计数到255,而你用到了340这个值,所以你应该是用的16位Timer1,这个前提很重要,配置时不能搞错定时器寄存器。

解决方案步骤

1. 正确配置Timer1的CTC模式

首先确保定时器的基础配置没有问题,以下是标准的CTC模式初始化代码:

void setup() {
  pinMode(4, OUTPUT);
  
  // 初始化Timer1为CTC模式(TOP值由OCR1A设定)
  TCCR1A = 0; // 清零寄存器,WGM11/WGM10设为0
  TCCR1B = (1 << WGM12); // 开启CTC模式(WGM13=0, WGM12=1)
  
  OCR1A = 340; // 设置周期TOP值,计数到340触发ISRA
  OCR1B = 28;  // 设置比较阈值,计数到28触发ISRB
  
  // 启用比较匹配A和B的中断
  TIMSK1 |= (1 << OCIE1A) | (1 << OCIE1B);
  
  // 设置时钟不分频(使用16MHz系统时钟)
  TCCR1B |= (1 << CS10);
  
  sei(); // 开启全局中断,必须执行这一步才能触发ISR
}

2. 修正ISR的逻辑顺序

核心问题出在ISR里的引脚状态设置时机。你需要:

  • 在周期结束的ISRA(计数到340)里,先拉低引脚,同时读取下一个sequence的位,标记下一个周期是否需要拉高;
  • 在ISRB(计数到28)里,根据标记将引脚拉高(如果sequence位为1)。

以下是修正后的ISR代码(假设你有全局变量存储sequence和当前遍历位置):

// 全局变量:定义你的序列,以及当前遍历的位置
const uint8_t sequence[] = {0b11001100, 0b10101010}; // 替换成你的实际序列
volatile uint8_t current_byte = 0;
volatile int8_t current_bit = 7; // 从最高位开始遍历,int8_t方便处理溢出
volatile bool should_set_high = false;

// 计数到28时触发:设置引脚为高(如果需要)
ISR(TIMER1_COMPB_vect) {
  if (should_set_high) {
    digitalWrite(4, HIGH);
  }
}

// 计数到340时触发:周期结束,准备下一个周期
ISR(TIMER1_COMPA_vect) {
  // 先拉低引脚,重置周期初始状态
  digitalWrite(4, LOW);
  
  // 读取当前sequence的位
  uint8_t current_value = (sequence[current_byte] >> current_bit) & 1;
  should_set_high = (current_value == 1);
  
  // 遍历下一位,处理循环
  current_bit--;
  if (current_bit < 0) {
    current_bit = 7;
    current_byte++;
    if (current_byte >= sizeof(sequence) / sizeof(sequence[0])) {
      current_byte = 0; // 循环回到序列开头
    }
  }
}

3. 验证逻辑

这样修改后,当sequence的位为1时,引脚会在计数到28时拉高,直到周期结束(340)被拉低,高电平持续的计数区间是28到340,共312个时钟周期,占总周期(341个时钟)的91.5%,符合你的预期;如果位为0,引脚会保持低电平整个周期。

额外注意事项

  • 确保全局变量用volatile修饰,因为ISR会修改这些变量,防止编译器优化导致错误;
  • 如果需要调整高电平的起始位置,只需要修改OCR1B的值即可;
  • 如果你用的是其他定时器(比如Timer2),寄存器名称会不同,但CTC模式的逻辑是一致的,注意对应修改。

内容的提问来源于stack exchange,提问作者Bob

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:00:19