« 「東成おもちゃ病院」、8月6日はお休みします。 | トップページ | 勝目 純也 著 『日本海軍潜水艦戦記』 »

2022年7月26日 (火)

割り込みと絡むクリチカルな制御 変数にはVolatileを!

魔法の言葉「Volatile」、これ大事です。
先日の Atmega328P タイマー/カウンタ1の高速PWM動作
このスケッチ、「IR_FRQ_SCAN3.ino」。
  ※たくさん空いている出力ポートにパルスを出して、
   動きをオシロでチェックできるようにしています。

大まかな処理の流れは、

・5msごとに、LED駆動周波数であるPWM周波数と
 デューティ比を計算して、変数に保存。
・タイマー1割り込み内(PWM周波数と同じ)でその値を
 タイマー1レジスタに書き込み。

これを繰り返し、22kHz~54kHzの周波数で赤外線LEDを
光らせています。

スケッチから関連部分をプックアップ。

【関連する変数】
メインルーチンでセットして割り込みでハードウェアを制御

volatile byte f_frqset; // 周波数設定フラグ
volatile word led_div; // LED駆動 分周比 ICR1に設定
volatile word led_duty; // 駆動デューティ PWM設定値 OCR1Aに設定

【関連する処理:loop内】

// 5msサイクルloop
while(1){
if(f_5ms){ // 5ms経過
f_5ms = 0;
// LED PWM周波数とデューティ
PD4_H; // (!!!)
if(led_frq == FRQ_LO){ // LO周波数のとき
PD2_H; // (!!!) パルスH/L
nop(); nop(); nop(); nop();
PD2_L; // (!!!)
}
a = (word)(160000L / (long)led_frq); // 分周比 (1)
b = (2 * a) / 3; // デューティ 1/3で (2)
cli(); // いったん割り込み禁止にして (3)
led_div = a; // LED駆動周分周比
led_duty = b; // デューティ比
f_frqset = 1; // 設定on タイマー1割り込みで処理
sei(); // 割込再開
// 周波数スキャン D/A PWM出力 220~540を0~320にして1/2
OCR2A = 255 - ((led_frq - FRQ_LO) / 2); // PWM 0~
PD4_L; // (!!!)
}

【割り込み処理】

ISR(TIMER1_OVF_vect) // タイマー1割り込み TOP値で
{
static byte cyc = 0; // LEDonサイクル
PD7_H; // (!!!)
switch(f_frqset){ // 実行区分
case 1: // PWM 設定指令
PD3_H; // (!!!)
ICR1 = led_div - 1; // LED周波数分周比 ★
OCR1A = led_duty - 1; // LED駆動デューティ 1/3 ★
cyc = 8; // 8サイクルだけLEDをon
f_frqset = 2;
PD3_L; // (!!!)
break;
case 2: // サイクル数をチェックしてオフに
if(cyc) cyc--;
if(cyc == 0){ // ダウンカウントしてゼロになった
PD3_H; // (!!!)
if(INP_SENS){ // センサー入力 H/L ?
SENS_H; // ポート出力でH保持
f_senshl = 1;
}
else{
SENS_L; // ポート出力でL保持
f_senshl = 0;
}
OCR1A = 0xFFFF; // LED駆動オフに
f_sensok = 1; // センサー状態確定
f_frqset = 0; // 次の起動設定を待つ
PD3_L; // (!!!)
}
break;
}
PD7_L; // (!!!)
}

(1) 5msごとに分周比=PWMの周波数とデューティ比を計算。
(2) 結果をいったん a と b に入れておいて。
(3) 割り込み禁止状態で変数に保存。

この部分の処理が、変数に付けた「volatileの有無」で変わって
しまうのです。

まずvolatileを付けたとき。 オシロでタイミングを見ます。
Vl09

PWMを処理しているタイマー1割り込み、この場合は
一定の周期で繰り返しています。

ところがvolatileを外すと・・・

Vl07

ch1波形、約65uほどのHレベル区間が「(1)(2)(3)」を
処理している時間です。
  ※ch2の1ms割り込みが5ms周期を知らせている。

ch3のタイマー1割り込み、新周期と新デューティ値を
変数に書いているのが「★★★」のタイミングです。
どういうわけか、割り込みが待たされてしまい、
処理の開始が遅れています。

これ、volatileを取ってしまったため、コンパイラが
  「いちいち仮変数の a と b に入れんでもエエやん」
  「直接 led_div と led_dutyに書いたろ」
  「ちょっとは早なるで
  「いちおう書き込み前に cli() はしとこ」
  「書き終わったら sei() ね」
と判断したせいです。

コンパイラの「ちょっとでも速よう実行できるほうがエエやろ」
というおせっかい。 (最適化の指示)
ただ、計算の前から割り込み禁止としたんで、時間のかかる除算
割り込み禁止の中に入ってしまい、処理が長くなってタイマー1割り込み
が待たされてしまいました。
  ※(2)の除算が割り込み禁止の中に入ってました。
   (1)は禁止前の実行でした。
割り込みが待たされるということは・・・
ICR1でPWM周波数を決めている方法では、ちょっと怖いこと
(PWM処理の一周抜け)が起こるかもしれません。

※関連
2018年10月11日:魔法の言葉「volatile」
2016年02月19日:Arduinoのタイマー処理

※追記
コンパイルされたソースファイルで比較してみると。

★volatile有

7b4: 20 91 21 01 lds r18, 0x0121 ▲led_frq
7b8: 30 91 22 01 lds r19, 0x0122
7bc: 40 e0 ldi r20, 0x00 ; 0
7be: 50 e0 ldi r21, 0x00 ; 0
7c0: c5 01 movw r24, r10
7c2: b4 01 movw r22, r8
7c4: 0e 94 94 07 call 0xf28 ; 0xf28 <__divmodsi4>
7c8: 29 01 movw r4, r18
7ca: 3a 01 movw r6, r20
7cc: c9 01 movw r24, r18
7ce: 88 0f add r24, r24
7d0: 99 1f adc r25, r25
7d2: b6 01 movw r22, r12
7d4: 0e 94 80 07 call 0xf00 ; 0xf00 <__udivmodhi4>
7d8: f8 94 cli
7da: 50 92 20 01 sts 0x0120, r5 ▲led_div
7de: 40 92 1f 01 sts 0x011F, r4
7e2: 70 93 1e 01 sts 0x011E, r23 ▲led_duty
7e6: 60 93 1d 01 sts 0x011D, r22
7ea: 30 92 1c 01 sts 0x011C, r3 ▲f_frq_set
7ee: 78 94 sei
★volatile無

7b2: 20 91 21 01 lds r18, 0x0121 ▲led_frq
7b6: 30 91 22 01 lds r19, 0x0122
7ba: 40 e0 ldi r20, 0x00 ; 0
7bc: 50 e0 ldi r21, 0x00 ; 0
7be: c5 01 movw r24, r10
7c0: b4 01 movw r22, r8
7c2: 0e 94 92 07 call 0xf24 ; 0xf24 <__divmodsi4>
7c6: f8 94 cli
7c8: 30 93 20 01 sts 0x0120, r19 ▲led_div
7cc: 20 93 1f 01 sts 0x011F, r18
7d0: c9 01 movw r24, r18
7d2: 88 0f add r24, r24
7d4: 99 1f adc r25, r25
7d6: b6 01 movw r22, r12
7d8: 0e 94 7e 07 call 0xefc ; 0xefc <__udivmodhi4> ←★
7dc: 70 93 1e 01 sts 0x011E, r23 ▲led_duty
7e0: 60 93 1d 01 sts 0x011D, r22
7e4: 70 92 1c 01 sts 0x011C, r7 ▲f_frq_set
7e8: 78 94 sei

「★volatile無」だと、「cli()割り込み禁止~sei()割り込み許可」内に
割り算が入っています。
「★volatile有」だと割り算は外に。
これが割り込み処理の遅れにつながっています。

|

« 「東成おもちゃ病院」、8月6日はお休みします。 | トップページ | 勝目 純也 著 『日本海軍潜水艦戦記』 »

Arduino」カテゴリの記事

重箱の隅」カテゴリの記事

AVRマイコン」カテゴリの記事

コメント

コメントを書く



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




« 「東成おもちゃ病院」、8月6日はお休みします。 | トップページ | 勝目 純也 著 『日本海軍潜水艦戦記』 »