用示波器“看”arduino (4) ----Timer0

Arduino 默认使用了 Timer0 【参考1】,在 \arduino-1.6.3\hardware\arduino\avr\cores\arduino\wiring.c 有

#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ISR(TIM0_OVF_vect)
#else
ISR(TIMER0_OVF_vect)
#endif
{
	// copy these to local variables so they can be stored in registers
	// (volatile variables must be read from memory on every access)
	unsigned long m = timer0_millis;
	unsigned char f = timer0_fract;

	m += MILLIS_INC;
	f += FRACT_INC;
	if (f >= FRACT_MAX) {
		f -= FRACT_MAX;
		m += 1;
	}

	timer0_fract = f;
	timer0_millis = m;
	timer0_overflow_count++;
}

 

简单的说,设置Timer0 1ms触发一次,然后其中的计数器会自动加1。同样的文件中还能看到millis( ) micros() 和delay() 函数都用到这个计数值。所以有如下结论:
1. 如果修改了Timer0的计时频率,这些函数都会受到影响
2. 如果你关闭了Timer0,那么这些函数都会失效
3. 如果你是编写中断服务,进入你的服务程序之后,关闭了中断,那么不可以使用这些函数

我们尝试修改这个文件,插入翻转pin的代码,可以看到,示波器上一直显示电平有变化

#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ISR(TIM0_OVF_vect)
#else
ISR(TIMER0_OVF_vect)
#endif
{
	// copy these to local variables so they can be stored in registers
	// (volatile variables must be read from memory on every access)
	unsigned long m = timer0_millis;
	unsigned char f = timer0_fract;

	m += MILLIS_INC;
	f += FRACT_INC;
	if (f >= FRACT_MAX) {
		f -= FRACT_MAX;
		m += 1;
	}

	timer0_fract = f;
	timer0_millis = m;
	timer0_overflow_count++;
	//LABZ_Debug_Start
	digitalWrite(9,LOW);
	digitalWrite(9,HIGH);
	digitalWrite(9,LOW);	
	//LABZ_Debug_End
}

 

验证的代码非常简单:

void setup() {
  // put your setup code here, to run once:
  pinMode(9,OUTPUT);
}
void loop() {
 }

 

运行结果

image001

放大一些看,间隔在1ms左右,就是Timer0触发一次的时间

image003

同样的程序我们在主程序中使用noInterrupts关掉中断【参考2】,显示出来始终为低电平,因为Timer0被关掉了,ISR中的代码永远不会跑到。

void setup() {
  // put your setup code here, to run once:
  pinMode(9,OUTPUT);
  noInterrupts();
}

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

}

 

运行结果,始终为低电平

image005

某些时候我们只需要关闭Timer0的中断,可以用屏蔽溢出中断的方法,设置TOIE0为0即可。

#define OutPin 8
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

void setup() {
  // put your setup code here, to run once:
  pinMode(OutPin,OUTPUT);
  digitalWrite(OutPin,LOW);
  cbi (TIMSK0, TOIE0);
}

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

 

运行结果和上图没差别,一直未低

此外还可以通过关闭Timer0的source的方法【参考3】来关闭Timer0.具体只要设置 CS00==CS01==CS02==0 即可。

#define OutPin 9

void setup() {
  // put your setup code here, to run once:
  pinMode(OutPin,OUTPUT);
  digitalWrite(OutPin,LOW);

}

void loop() {
  delay(1000);
  bitWrite(TCCR0B, CS00, 0);
  bitWrite(TCCR0B, CS01, 0);
  bitWrite(TCCR0B, CS02, 0);  
  delayMicroseconds(10000);
  digitalWrite(OutPin,HIGH);   
}

 

进入 loop函数之后 delay 1s 持续有中断产生,之后关闭了中断就出现一直为低的情况,接下来是10ms的delay,最后再拉高(这样只是为了让波形清晰可辩)。

image007

放大一点可以看到,中间有间隔是10539ms.

image001

特别注意:上述代码中,如果我将delayMicroseconds换成 Delay,那么看到的波形将会是一直为低。读者可以思考一下原因。

参考:
1. http://letsmakerobots.com/node/28278 Arduino 101: Timers and Interrupts

2.\arduino-1.6.3\hardware\arduino\avr\cores\arduino\Arduino.h
#define interrupts() sei()
#define noInterrupts() cli()

3. https://arduinodiy.wordpress.com/2012/02/28/timer-interrupts/
Clock Select bit description
CS12 CS11 CS10 Description
0 0 0 No clock source (Timer/Counter stopped)
0 0 1 clki/o/1 (No prescaling)
0 1 0 clki/o/8 (From Prescaler)
0 1 1 clki/o/64 (From Prescaler)
1 0 0 clki/o/256 (From Prescaler)
1 0 1 clki/o/1024 (From Prescaler)
1 1 0 External clock source on T1 pin. Clock on falling edge
1 1 1 External clock source on T1 pin. Clock on rising edge

发表评论

电子邮件地址不会被公开。 必填项已用*标注