直接操作定时器timer1输出方波遇到的问题-Arduino中文社区 - Powered by Discuz!

Arduino中文社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 3611|回复: 1

直接操作定时器timer1输出方波遇到的问题

[复制链接]
发表于 2019-3-11 09:23 | 显示全部楼层 |阅读模式
材料:Mega2560
目标:D11引脚输出400KHz的方波(查mega2560 sch知,11引脚与timer1相关)
参考:
1、"G哥撸Arduino之..."系列
2、https://www.arduino.cn/thread-21311-1-1.html
3、原汁原味的http://maxembedded.com/2011/07/avr-timers-ctc-mode/
4、数据手册


方法一:
pdf手册上P159有Compare Ouput Mode的说明,直接使11引脚输出方波,不预分频
...
const byte TESTPIN=11;
void timer1_init()
{
    cli();
    TCCR1A =0;
    TCCR1B =0;
    // set up timer with prescaler = 1 and CTC mode,和第一种方法一样,不预分频
    TCCR1B |= (1 << WGM12)|(1 << CS10);
     // set up timer OC1A pin in toggle mode
    TCCR1A |= (1 << COM1A0);  
    // initialize counter
    TCNT1 = 0;
    // initialize compare value
    OCR1A = 18;
    sei();//不是必须的,只是习惯
}
void setup()
{
    pinMode(TESTPIN, OUTPUT);
    timer1_init();
}

示波器结果:freq=416.7khz, Prd=2.400us
结论:和理论值有偏差,但基本正确


方法二,使用比较中断服务例程输出方波
void timer1_init()
{
    cli();
    TCCR1A =0;
    TCCR1B =0;
    // set up timer with prescaler = 1 and CTC mode
    TCCR1B |= (1 << WGM12)|(1 << CS10);
    //改成调用中断模式
    TIMSK1 = (1<<OCIE1A);
    // initialize counter
    TCNT1 = 0;
    // 初值不变,还是18
    OCR1A = 18;
    sei();
}


void setup()
{
    pinMode(TESTPIN, OUTPUT);
    // 初始化timer1
    timer1_init();
}
//中断服务
ISR(TIMER1_COMPA_vect)
{
    digitalWrite(TESTPIN, !digitalRead(TESTPIN));
}

结果:示波器显示freq=26.04khz prd=38.40um
结论:不是预期结果
探讨:
定时器CTC模式的公式在手册p150,
(1)将OCR1A初值改为1(理论上应该输出4MHZ方波),再测,结果竟然和初值18时,完全一样freq=26.04khz prd=38.40um
(2)将OCR1A初值改为63(理论上应该输出125kHZ方波),再测,结果竟然和初值18时,完全一样freq=26.04khz prd=38.40um
(3)将OCR1A初值改为1023(理论上应该输出7.8125kHZ方波),再测,结果有变化,示波器显示freq=7.576khz, prd=132.0us
(4)将OCR1A初值改为3000理论上应该输出2.66577kHZ方波,再测,结果有变化,示波器显示freq=2.660khz, prd=376.0us


问题:问什么比较中断不能输出期望结果?而且通过观察探讨(3)(4)的结果与理论基本一致。
求大神指导

发表于 2019-3-11 14:47 | 显示全部楼层
建议看下 AVR 底层结构,一次比较中断从底层到 ISR 的调用大致是:

中断请求FLAG -> CPU硬件等待(2~3T)-> 中断向量表, RJMP指令(3T)-> 底层中断处理函数, PUSH保存寄存器(10T+)-> ISR

在 ISR 被调用之前,硬件已经过了是几个时钟周期了。
况且你用了 digitalWrite/Read 接口,这个需要硬件查表,将逻辑 D 端口号转换为 AVR-GPIO 地址、位号,然后再置高/低,几十时钟周期就又过去了。

上述分析,在 ISR 版本的发波程序中,随便算算就需要几十个时钟周期(1T=1/16 us),还发个什么高频波形。

P.S. 你试试看,这个都快不到哪里去(基本就是 Write+Read+RJMP 的时间):
  1. while(1)
  2. {
  3.    digitalWrite(TESTPIN, !digitalRead(TESTPIN));
  4. }
复制代码
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|Archiver|手机版|Arduino中文社区

GMT+8, 2024-11-28 11:44 , Processed in 0.076908 second(s), 15 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表