« ひさびさの「腰痛」 | トップページ | チャックイエーガーさん »

2020年12月 8日 (火)

タイマー割り込みを使ったLEDのダイナミック点灯処理

ラジオペンチさんが
チャーリープレクシングでクリスマスツリー用LEDイルミネーションを作る(ダイナミック点灯プログラムの作成)

という記事をまとめられてますんで、タイマー割り込みを
使った多桁LEDのダイナミックスキャンについての処理例を
示しておきます。

 ※参考 (ArduinoではありませんがATmega328Pです)
2020年9月26日:手組みするときは片面基板で:1kHz PWM発生回路


Arduino-UNOで試せるように、書いてみました。
  LEDはつないでません。 オシロで観察を。
  ATmega328Pのレジスタ、直書きですんで、
  データシートを参照に。

タイマー割り込みによるLEDの点灯とブランキング処理、
アウトプットコンペア割り込みを試してみてください。
  ※タイマー0はシステムで使ってるんで、タイマー2で。

まず、タイマー2をいったん止めてCTCモードに再設定。
1/64分周で1カウント=250kHz=4usに。

OCR2Aの値がTOPになるんで、「250-1」をセットして
これで1msで割り込み。
このタイミングでLEDをオフ。

OCR2Bの値はAの40us後に割り込みということで「10-1」を。
このタイミングでLEDをオン。
960us後に次のA割り込みが。

このコンペアA、コンペアB両方の割り込みを有効に。
タイミングを見るためOC2A、OC2B両方をトグル出力を有効に。

1ms割り込み内では1000を数えて1秒フラグを造ってメインloop
でLEDを点滅。

PB4を観察すれば、点灯とブランキング処理のタイミングが
見えます。

A11_20201208170501

オシロ波形、上から
 PB4 40usオフして960usオン。 これでLEDを点灯・消灯。
 PB3 OC2A コンペアAのタイミングでトグル
 PD3 OC2B コンペアBのタイミングでトグル
 PB5 LED点滅 1秒ごとに

LEDのダイナミックスキャン、こんな手順で。
・コンペアA割り込みで消灯処理。(ブランキング)
  ついでに1msタイマー処理。

・コンペアB割り込みで次桁の点灯を。
  そして次の点灯桁の準備。

点灯・消灯は割り込みが勝手にやってくれますんで、
メインloopはなにしようが勝手。

/*****  タイマー2 アウトプットコンペア割り込みテスト *****/
// 1kHzで割り込み
volatile byte f_1sec; // 1秒フラグ
// D12 PB4ポート 960uS点灯 40us消灯
ISR(TIMER2_COMPA_vect) { // タイマー2 A割り込み
static word t1ms = 0;
PORTB &= ~(1 << PB4); // D12 PB4 off (40us)
t1ms++;
if(t1ms >= 1000){ // 1秒経過
t1ms = 0;
f_1sec = 1; // main処理に知らせる
}
}
ISR(TIMER2_COMPB_vect) { // タイマー2 B割り込み
PORTB |= (1 << PB4); // D12 PB4 on (960us)
}
/***** SETUP *****/
void setup() {
pinMode(13, OUTPUT); // PB5:LEDポート loopで点滅
pinMode(12, OUTPUT); // PB4:1ms割り込みで点滅
pinMode(11, OUTPUT); // PB3:OC2A出力
pinMode( 3, OUTPUT); // PD3:OC2B出力
cli(); // 割込禁止
TCCR2B = 0x00; // タイマー2いったん停止
TCCR2A = 0b01010010; // あらためてモード設定
// |||| ++--- WGM: CTCモード OCR2Aでクリア
// ||++------- COM2B D3 PD3 トグル出力
// ++--------- COM2A D11 PB3 トグル出力
TCCR2B = 0b00000100; // クロック設定
// |+++--- CS:1/64 : 250kHz
// +------ WGM02
OCR2A = 250 - 1; // 250カウントで1kHzを
OCR2B = 10 - 1; // 10カウント(40us)で割り込み
TIMSK2 = 0b00000110;
// ||+---- TOIE2 オーバーフロー割り込みは無し
// |+----- OCIE2A コンペアマッチA割り込み有効
// +------ OCIE2B コンペアマッチB割り込み有効
sei(); // 割込許可
}

void loop() { // D13ポートにパルス
if(f_1sec){
f_1sec = 0; // 1秒サイクル
PINB |= (1 << PB5); // D13 PB5 LED on/off トグル操作
}
}


OCR2Bをいじれば、明るさを変えることができます。
125-1ならデューティ50%に。
大きくすればどんどん暗く。
OCR2Aより大きくなれば全消灯。

  ※OC2A、OC2Bのトグル出力はタイミング確認の
   ためのものですんで、必要ありませんので。

ちょっとtips。
loopのところのPINBのbit5に対する「1」書き込み、
これでPORTBのbit5がトグルしてくれます。
AVRマイコン独自の機能です。


※追記 オフ時間を4種類で変えてみました。
A割り込み内で、次のオンタイミング、OCR2Bを操作
します。


/***** タイマー2 アウトプットコンペア割り込みテスト *****/
volatile byte scan; // スキャン位置 0~3の4つ
byte oc2b[]={ // OCR2B設定値
20-1, 40-1, 80-1, 100-1, }; // on位置
volatile byte f_1cyc; // 1サイクルでon
// 1kHzで割り込み
// D12 PB4ポート 960uS点灯 40us消灯
ISR(TIMER2_COMPA_vect) { // タイマー2 A割り込み
PORTB &= ~(1 << PB4); // D12 PB4 off
OCR2B = oc2b[scan]; // 次onタイミングをセット
scan++; // 次スキャン位置
if(scan >= 4){ // 0~3
scan = 0; // 4で0に戻す
f_1cyc = 1;
}
}
ISR(TIMER2_COMPB_vect) { // タイマー2 B割り込み
PORTB |= (1 << PB4); // D12 PB4 on
}
/***** SETUP *****/
void setup() {
pinMode(13, OUTPUT); // PB5:LEDポート loopで点滅
pinMode(12, OUTPUT); // PB4:1ms割り込みで点滅
pinMode(11, OUTPUT); // PB3:OC2A出力
pinMode( 3, OUTPUT); // PD3:OC2B出力
cli(); // 割込禁止
TCCR2B = 0x00; // タイマー2いったん停止
TCCR2A = 0b01010010; // あらためてモード設定
//      |||| ++--- WGM: CTCモード OCR2Aでクリア
//      ||++------- COM2B D3 PD3 トグル出力
//     ++--------- COM2A D11 PB3 トグル出力
TCCR2B = 0b00000100; // クロック設定
//       |+++--- CS:1/64 : 250kHz
//       +------ WGM02
OCR2A = 250 - 1; // 250カウントで1kHzを
OCR2B = 10 - 1; // 10カウント(40us)で割り込み
TIMSK2 = 0b00000110;
//        ||+---- TOIE2 オーバーフロー割り込みは無し
//        |+----- OCIE2A コンペアマッチA割り込み有効
//        +------ OCIE2B コンペアマッチB割り込み有効
sei(); // 割込許可
}
/***** LOOP *****/
void loop() { // D13ポートにパルス
PINB |= (1 << PB5); // D13 PB5 LED on/off トグル操作
#if 0
if(f_1cyc){
f_1cyc = 0;
PINB |= (1 << PB5); // D13 PB5 LED on/off トグル操作
}
#endif
}

こんな波形になります。
A000_20201209113501  

そして、LOOP内で連続パルスを出すと、割り込み処理時間が推測
できます。
パルスが止まっている時間が割込処理中の時間。
A001_20201209110401

OC2Aパルスはハード的に出ます。
そこからD12がLになるまでが、割り込みの応答時間。
レジスタをスタックに待避したりで大忙し。

そして・・・割り込み応答を観察するためLOOPにパルスを
出してると、たまに別の処理が走っているのに出会うことが
あります。
今回はこんなの。
A003_20201209112401

OC2A、OC2B割り込みは出力波形と同期していますが、
無関係のところでLOOP内のパルス出力が抜けます。
それがバックグランドで走っているタイマー0の
オーバーフロー割り込みのタイミングです。
周期は1.024ms。
ですので1ms周期のタイマー2とは同期しません。
抜け部分がどこに来るかは不明。
もしタイマー2割り込みと競合すると、どちらか早い
ほうが優先され、負けた方はその時間だけ処理が遅れ
ます。
新たなOCR2Bの設定が、その割り込み前にできないと
変化して欲しい時間が1ms周期遅れてしまいます。
  ※割り込み起動は抜けないのでオフが継続という
   ことではない。

2016年02月19日:Arduinoのタイマー処理
2020年1月28日:Arduinoから「タイマー0」を取り上げる(ユーザーが使う)
ラジオペンチ:Arduinoのmillis()関数が返す値は不連続な場合がある

クリチカルな処理の時は、これがじゃまになるかも。


|

« ひさびさの「腰痛」 | トップページ | チャックイエーガーさん »

Arduino」カテゴリの記事

割り込み処理」カテゴリの記事

コメント

ラジオペンチです、アドバイスありがとうございます

ターマー2のA割り込みとB割り込みを使ってLEDを点灯/消灯させるタイミングを作る、ということですね。タイマー2をこのレベルまで使ったことが無いので細かい所まで理解できていないのですが、確認しながらやってみます。

投稿: ラジオペンチ | 2020年12月 9日 (水) 07時14分

完成形へ!
http://radiopench.blog96.fc2.com/blog-entry-1061.html
・2020-12-14:チャーリープレクシングでクリスマスツリー用LEDイルミネーションを作る(ダイナミック点灯プログラムの改良)

投稿: 居酒屋ガレージ店主(JH3DBO) | 2020年12月16日 (水) 10時24分

コメントを書く



(ウェブ上には掲載しません)




« ひさびさの「腰痛」 | トップページ | チャックイエーガーさん »