2010-06-25
sleep
いつもUSBで電源を確保していたのですが、ADCをいじっていたら「連流流れすぎ」とMacに怒られました...危ないですね。たぶん100mAは流れていないハズなんですけども...。
余談はこれくらいにして、ADCの前に今回はsleepをやっておきます。
sleep概要
sleepは文字通りavrをsleepさせて消費電力を抑えたり、あるいは電源スイッチを省略したり、さらにADC変換中にsleepにさせてADCの精度を上げる*1などなど出来ます。sleepからの復帰は割り込みを使い、sleepの種類によって使える割り込みが違います。
sleep mode一覧
| mode | INT割り込み | TWIアドレス一致割り込み | Timer2割り込み | SPM/EEPROM Ready割り込み | ADC割り込み | WDT割り込み | OtherIO (PCINT?) |
|---|---|---|---|---|---|---|---|
| SLEEP_MODE_IDLE | O | O | O | O | O | O | O |
| SLEEP_MODE_ADC | Low Level | O | O | O | O | O | - |
| SLEEP_MODE_PWR_DOWN | Low Level | O | - | - | - | O | - |
他にもSLEEP_MODE_PWR_SAVEとSLEEP_MODE_STANDBYがありますが、個人的に使わないので省略します。SLEEP_MODE_ADCはADCが搭載されたAVRでないとありません。割り込みの違い意外にクロック動作が変わってきてますが、プログラムを書く面から直接関係はしないと判断したのでこれも省略しています。
INT割り込みの「Low Level」というのは状態変化・立ち上がり・立ち下がり割り込みでは復帰できないので注意です。
sleepでは必要なレジスタはありますが、直接レジスタをいじることは無く、関数を呼び出して使います。使う関数も
- set_sleep_mode(mode)
- sleep_mode()
の2種類だけです。set_sleep_modeで使用するスリープのモードを選び、sleep_modeで実際にスリープさせます。これらはavr/sleep.hに書かれているので、これをインクルードする必要はあります。復帰は割り込みを使うので、割り込みハンドラ無いにsleep_mode()を書くのは避けた方が良いです、割り込みフラグピットが落ちないままスリープして次回の割り込みがかからなくなる可能性があるので。
サンプルコード
SLEEP_MODE_IDLE
SLEEP_MODE_IDLEではTimerは停止しないので、動作自体はタイマ0の時のサンプルと変わりません。
/* * ATTiny2313 * タイマを使ってPD6を反転させる * 8MHzで1024分周ならおよそ110ms周期で点滅 */ #include <avr/io.h> #include <avr/interrupt.h> #include <avr/sleep.h> /* 割り込みでスリープから復帰 */ ISR(TIMER0_OVF_vect) { PORTD ^= (1<<PD6); // PD6反転 } int main(void) { /* PD6を出力L設定 */ DDRD = (1<<PD6); PORTD = ~(1<<PD6); TIMSK = (1<<TOIE0); // タイマ0のオーバーフロー割り込み許可 TCCR0B = 0x05; // 1024分周でカウントスタート set_sleep_mode(SLEEP_MODE_IDLE); // アイドルモードに設定 sei(); // 全体の割り込み許可 while(1) sleep_mode(); // 適宜スリープさせる return 0; }
SLEEP_MODE_PWR_DOWN
こちらの場合、タイマ自体が停止するためスリープ中は止まります。
/* * ATTiny2313 * タイマを使ってPD6を反転させる * 8MHzで1024分周ならおよそ110ms周期で点滅 */ #include <avr/io.h> #include <avr/interrupt.h> #include <avr/sleep.h> /* STATEが0でスリープ禁止、1でスリープ許可(とする) */ volatile unsigned char STATE; ISR(TIMER0_OVF_vect) { PORTD ^= (1<<PD6); // PD6反転 } /* 割り込みでスリープから復帰 */ ISR(INT0_vect) { STATE = 0; // スリープ禁止 } int main(void) { STATE = 0; /* PD6を出力L, その他をプルアップ有り入力に設定 */ DDRD = (1<<PD6); PORTD = ~(1<<PD6); GIMSK = (1<<INT0)|(1<<INT1); // INT0, 1の割り込み許可 TIMSK = (1<<TOIE0); // タイマ0のオーバーフロー割り込み許可 TCCR0B = 0x05; // 1024分周でカウントスタート set_sleep_mode(SLEEP_MODE_PWR_DOWN); // アイドルモードに設定 sei(); // 全体の割り込み許可 while(1) { /* スリープ許可されてたら(PD6を消灯してから)スリープ */ if(STATE == 1) { PORTD &= ~(1<<PD6); sleep_mode(); } /* PD5がLならスリープを許可 */ if((~PIND & (1<<PD5)) == (1<<PD5)) STATE = 1; } return 0; }
参考
ATTiny2313データシート http://www.atmel.com/dyn/resources/prod_documents/doc8246.pdf
ススガ
2010/06/25 13:45
あ、この資格あったのわすれてた・・。興味ある・・
