直接操作定时器timer1输出方波遇到的问题
材料: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)的结果与理论基本一致。
求大神指导
建议看下 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 的时间):
while(1)
{
digitalWrite(TESTPIN, !digitalRead(TESTPIN));
}
页:
[1]