avr-gcc で工作(その6)
avr-gcc で工作(その6)
もののついでにさらに色気を出して、LEDがジワっと点灯、ジワっと消灯を繰り返すプログラムを書いてみました。今回ターゲットとしている工作にはおそらく採用しない*1のですが、先日 (id:Chuck:20040910#p1) 買ったフルカラーLEDに適用してみようかなと。
PWMでやっているわけですが、timer2 でジワっと明るさ定数 _pwm の更新を行い、timer0 でその値を PWMでポートに出力しています。timer0 は クロック8MHz を prescale 1 で、256カウントでオーバーフローして割り込みに飛んできます*2。それを64を1周期として出力しています*3。
ジワっと明るさ定数 _pwm の更新はもっとずっとゆっくりです。8MHzを1024でprescaleして256カウントでオーバーフローして割り込みに飛んでは来るものの、さらに cnt (初期値 _COUNT) を数えないと値更新にたどり着きません。この値は当初直線的に変化させていたのですが、今回繋いだLEDで今ひとつに見えた*4ので、2次関数的に変化させるようにしてみました。が、もうひとこえ欲しいところです。
いかん、寄り道していないで、本題に戻らねば…
以下ソースコード
/* PORTA LED fade in/out */ #include#include #include #include #define _COUNTER 2 enum { UP, DOWN }; volatile uint8_t cnt = _COUNTER; volatile uint8_t _direction = UP; volatile uint16_t _pwm = 0; volatile uint8_t _pwm_work = 0; volatile uint8_t _pwm_cnt = 0; /* timer0 for PWM, timer2 for fade in/out */ SIGNAL(SIG_OVERFLOW0) { if (++_pwm_cnt > 64) _pwm_cnt = 0; if (_pwm_cnt < (uint8_t)(_pwm>>5)) sbi(PORTA, PA0); else cbi(PORTA, PA0); } INTERRUPT(SIG_OVERFLOW2) { if (!(--cnt)) { cnt = _COUNTER; switch (_direction) { case UP: _pwm += _pwm_work++; if (_pwm_work > 64) _direction = DOWN; break; case DOWN: _pwm -= --_pwm_work; if (!(_pwm_work)) _direction = UP; break; } } } void ioinit (void) { /* tmr0 is prescaled by 1, tmr2 by 1024 */ TCCR0 = _BV (CS00); TCCR2 = _BV (CS22) | _BV (CS21) | _BV (CS20); /* enable PA0 as output */ DDRA = _BV (PA0); timer_enable_int (_BV (TOIE0) | _BV (TOIE2)); /* enable interrupts */ sei (); } int main(void) { ioinit(); for (;;) ; return 0; }