昨天看了下《嵌入式C编程与atmel avr》后,立刻照着上面做了个程序,没想到一次成功,哈哈。(这本书还是不错,老外写的,可惜现在已经买不到了,我看的是扫描版。)

这个程序主要是演示中断和计数器,目的让LED灯每隔大概半秒闪一次。

#define LED_PORT_DDR        DDRB
#define LED_PORT_OUTPUT     PORTB
#define LED_BIT             0
unsigned int timecount = 0;

ISR(TIM0_OVF_vect) {
	TCNT0 =0;
	if(++timecount == 31){
		LED_PORT_OUTPUT ^= _BV(LED_BIT);//toggle
		timecount = 0; //reset
	}
}

int __attribute__((noreturn)) main(void)
{
uchar   i;

    wdt_enable(WDTO_1S);
    odDebugInit();
    DBG1(0x00, 0, 0);       /* debug output: main starts */
    usbInit();
    usbDeviceDisconnect();  /* enforce re-enumeration, do this while interrupts are disabled! */
    i = 0;
    while(--i){             /* fake USB disconnect for > 250 ms */
        wdt_reset();
        _delay_ms(1);
    }
    usbDeviceConnect();
    LED_PORT_DDR |= _BV(LED_BIT);   /* make the LED bit an output */
    // 1:1024 presc.
    TCCR0B = 0x05;
    TCNT0 =0;
    TIMSK= 1 << TOIE0; //unmark Timer 0 overflow interrup

    sei();
    DBG1(0x01, 0, 0);       /* debug output: main loop starts */
    for(;;){                 /* main event loop */
        DBG1(0x02, 0, 0);   /* debug output: main loop iterates */
        wdt_reset();
        usbPoll();
    }
}

解释如下:

  1. AVR-GCC里面中断用推荐用宏ISR,老的代码用SIGNAL。
  2. attiny45的频率为16.5MHZ,这个频率对于半秒来说太快,所以要用prescaler来降低计数的节拍频率,最小的是原来的频率的1/1024,16.5Mhz / 1024 = 15.625KHZ。上面的设置TCCR0B就是这个用途。这样系统每滴答一次需要1/15.625khz = 64 微秒,计数器为8位,所以滴答256次后就异常了,而这个异常代码是能捕获的,所以能捕获到的最小的计时单位为:64 * 256微秒 = 16 毫秒, 这个离500毫秒还有段距离,所以要用一个全局变量timeout,当timeout递增到31次时,大概就是500毫秒了。这个时候就可以点灯了。
  3. 接下来就要打开中断了,TIMSK这句就是enable Time0的溢出中断。
  4. TCCR0A是用来设置PWM的,要设置prescaler需要用TCCR0B。

数据达到最大值而产生的异常在单片机里面成了一个有实际用途的中断,这种思路真是很奇特。如果一个单片机的中断越多,那么功能也越强大,因为代码能相应更多的事件,而且代码也更清晰。有点类似观察者模式。

接下来做的事情就是测风扇的转速(RPM),思路应该差不多。外行搞单片机还是循序渐进好,首先要有个能够work的东东,不管这个东东有多么小。然后开始滚。