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

如何验证AVR ATmega328P的TIMER2溢出频率?实测异常求助

ATmega328P Timer2溢出频率验证问题解决

问题背景

已将ATmega328P的Timer2配置为普通模式,预期溢出频率10000Hz,通过溢出中断翻转PB5引脚验证频率。初始示波器测得频率约159Hz,调整后DSO显示4.9407kHz(对应溢出频率9.881kHz),与预期10kHz不符。

原代码

#define F_CPU 16000000
#define TIMER2_PRESCALER 8  // RV8A12, added 2022-11-08
#define TIMER2_CLOCK (F_CPU / TIMER2_PRESCALER)

uint8_t Timer2_count = 0;
unsigned int sample_freq;

ISR(TIMER2_OVF_vect)
{
    TCNT2 = Timer2_count;
    PORTB ^= (1<<PORTB5);
}


void timer2_init()
{
    TCCR2A = 0x00;  // Normal mode
    TCNT2 = Timer2_count;
    TIMSK2 = 0x01;
    TCCR2B = 0x02;  // Set prescaler 8 or divide by 8
}

void timer2_stop()
{
    TCCR2B = 0x00;
}


void setup()
{
    // Put your setup code here, to run once:

    sei();
    Serial.begin(115200);
    Serial.print("\nSTARTING...");
    delay(5000);
    DDRB |= (1<<PORTB5);

    sample_freq = 10000;
    Timer2_count = 256 - (TIMER2_CLOCK / sample_freq);

    timer2_init();
}

void loop()
{
    // Put your main code here, to run repeatedly:
}

问题分析

1. 初始159Hz异常原因

大概率是预分频器配置错误或Timer2_count赋值顺序问题:

  • 若预分频器误设为1024(TCCR2B=0x07),溢出频率为16MHz/1024/256≈61Hz,翻转引脚后为30Hz,与159Hz不符;更可能是前期代码中先调用timer2_init再计算Timer2_count,导致TCNT2被设为0,溢出频率为16MHz/8/256≈7812Hz,翻转后3906Hz,仍不符。需排查代码执行顺序和寄存器配置。

2. 9.88kHz偏差原因

原代码中ISR的软件操作存在延迟:

  • 理论计算:TIMER2_CLOCK=16MHz/8=2MHz,2MHz/10kHz=200,Timer2_count=256-200=56,溢出频率应为10kHz。但ISR中先重载TCNT2再翻转引脚,这两个操作会占用数个时钟周期,导致实际溢出周期变长,频率略低于预期,翻转后的引脚频率(溢出频率的一半)因此接近4.94kHz。

正确验证方法

方法一:用CTC模式减少ISR开销(精度最高)

改用CTC(比较匹配清零定时器)模式,硬件自动清零TCNT2,无需在ISR中重载寄存器,彻底消除软件延迟:

#define F_CPU 16000000
#define TIMER2_PRESCALER 8
#define TIMER2_CLOCK (F_CPU / TIMER2_PRESCALER)

unsigned int sample_freq = 10000;

ISR(TIMER2_COMPA_vect)
{
    PORTB ^= (1<<PORTB5); // 仅保留翻转操作,无寄存器重载
}

void timer2_init()
{
    TCCR2A = (1<<WGM21); // 启用CTC模式
    OCR2A = (TIMER2_CLOCK / sample_freq) - 1; // CTC模式下,比较值为周期数-1
    TIMSK2 = (1<<OCIE2A); // 使能比较匹配A中断
    TCCR2B = (1<<CS21); // 设置预分频器为8
}

void setup()
{
    sei();
    DDRB |= (1<<PORTB5);
    timer2_init();
}

void loop()
{
}

此时溢出频率理论值为10kHz,引脚翻转频率为5kHz,精度大幅提升。

方法二:软件统计中断次数(排除示波器误差)

通过串口统计1秒内的溢出中断次数,直接计算实际频率,避免引脚翻转的软件延迟和示波器测量误差:

#define F_CPU 16000000
#define TIMER2_PRESCALER 8
#define TIMER2_CLOCK (F_CPU / TIMER2_PRESCALER)

uint8_t Timer2_count = 0;
volatile uint32_t overflow_count = 0;
unsigned int sample_freq = 10000;

ISR(TIMER2_OVF_vect)
{
    TCNT2 = Timer2_count;
    overflow_count++;
}

void timer2_init()
{
    TCCR2A = 0x00;
    TCNT2 = Timer2_count;
    TIMSK2 = 0x01;
    TCCR2B = 0x02;
}

void setup()
{
    sei();
    Serial.begin(115200);
    DDRB |= (1<<PORTB5);
    Timer2_count = 256 - (TIMER2_CLOCK / sample_freq);
    timer2_init();
}

void loop()
{
    static unsigned long last_time = 0;
    if (millis() - last_time >= 1000)
    {
        cli(); // 关中断避免统计时被打断
        uint32_t count = overflow_count;
        overflow_count = 0;
        sei(); // 恢复中断
        Serial.print("实际溢出频率:");
        Serial.println(count);
        last_time = millis();
    }
}

串口输出的数值即为1秒内的溢出次数,直接对应溢出频率。

方法三:检查时钟精度

若上述方法仍有偏差,需验证ATmega328P的系统时钟是否为精确16MHz。可通过测量已知频率的引脚(如串口波特率对应的时钟)确认时钟准确性。

总结

  • 优先使用CTC模式提升频率精度,消除ISR软件延迟;
  • 软件统计中断次数可直接获取溢出频率,不受引脚翻转操作影响;
  • 初始异常频率需重点排查预分频器配置和变量赋值顺序。

内容的提问来源于stack exchange,提问作者vishnu m c

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 21:44:59