« Arduino UNOのPWM出力を(ちょっと精密に)確かめる | トップページ | Arduino インプットキャプチャー割り込みを「ISR_NAKED」で (デューティー比測定) »

2020年8月18日 (火)

Arduino デューティー計測のためのインプットキャプチャータイミング

2020年8月16日:Arduino UNOのPWM出力を(ちょっと精密に)確かめる

2020年8月16日:Arduino UNOのPWM出力を(ちょっと精密に)確かめる
の中の、デューティー計測回路。
これはタイマー1のインプットキャプチャー機能(日本語訳だと捕獲入力)
を使って実現しています。
ICP1入力のパルスエッジ↑↓を捉えて、↑パルスを検出した時は
「L区間のタイマー1カウント値」、↓パルスを検出した時は「H区間の
カウント値」として、内部クロック16MHzを数えます。

16bitカウンタですので65535が最大。
周波数約244Hz以下のパルスになるとこれがオーバーフロ-します。
そこで、タイマー1のオーバーフロー割り込みを使って、32bit値の
上位桁をカウントアップ。
これで、10Hzや1Hzなどの低周波パルスでもそのデューティが測定でき
ます。

問題は、デューティーが「0%」や「100%」に近づいた時。
0%や100%ならLかHに固定しちゃうんで、「パルス無し」なんですが、
例えば「analogWrite」のval値を1や254にしたような時に、
「16MHz/64 = 4us」のHあるいはLのパルスが出てきます。
これの検出がギリギリなんです。

その様子。
上が入力パルス。(このくらいだと十分間に合う)
真ん中がPB2に出力したICP1割り込みの実行時間。
下がPB3に出しているオーバーフロー割り込みの処理時間。

Dt001

ポートを観察して見えるICP1割り込みの実行時間は6usほど。
しかし・・・パルスの↑↓エッジからPB2が現れるまで、2~3us
の時間がかかっています。
また、オーバーフロー割り込みが↑↓エッジの直前に入ると、
ICP割り込みの起動が遅れている様子が見えます。


この短パルス、もっと短くするとこうなってしまいます。
Dt004

↑↓エッジの切替処理が間に合わなくなり、デューティーの
測定ができなくなってしまいます。

この遅れの原因が「C言語」特有の「レジスタの待避処理」。
割り込み処理が始まると、割り込みで使うレジスタを全部
プッシュします。
この手順が遅れの原因。

「C」で書いている割り込み処理はこんなの。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/***** タイマー1インプットキャプチャー割込 *****/
// ICP1の↓↑エッジでキャプチャー
ISR(TIMER1_CAPT_vect)
{
uint32_t cnt_d;     // 差の計算用
word cnt_r;       // ICR1読み出し
  PB2_H;         // (!!!)
  cnt_r = ICR1;      // キャプチャー値 ★1
  XCG_EDGE1;       // 次にそなえて↑↓エッジをトグル ★1
// オーバーフロー有無
  if(CK_OVF1){      // 割込処理中にOVF発生したか? ★2
    if(cnt_r < 160){  // 10uS(160ck)未満なら未処理のOVFがある
      cnt1_ovf += 1; // オーバーフローカウンタ+1
      CLR_OVF1;    // オーバーフローフラグをクリア
    }
  }
// カウント差を計算
  cnt1_n = (uint32_t)cnt_r +
      ((uint32_t)cnt1_ovf << 16); // 32bitでカウント値
  cnt_d = cnt1_n - cnt1_x;   // 前回値との差分
  cnt1_x = cnt1_n;        // 次回用に残す
// ↑↓エッジをチェック
  if(CK_EDGE1){      // ↑になった H区間
    cnt1_caph = cnt_d;
  }
  else{          // ↓になった L区間
    cnt1_capl = cnt_d;
  }
  f_plsin = 1;      // パルス有フラグをon
  f_plsin2 = 1;      // min,maxチェック用
// 次の↑↓パルスが来てるかどうかのチェック
  if(CK_ICF1)   f_plsovl = 1;    // パルスがオーバーラップ
  PB2_L;         // (!!!)
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

★1の処理をできるだけ前に持ってきたいのですが、展開された
機械語はこうなってしまいます。

=============================
356 <__vector_10>:
356: 1f 92    push  r1
358: 0f 92    push  r0  
35a: 0f b6    in r0, 0x3f ;sreg
35c: 0f 92    push  r0
35e: 11 24    eor r1, r1   ;△
360: 2f 93    push  r18  ;△
362: 3f 93    push  r19  ;△
364: 4f 93    push  r20  ;△
366: 5f 93    push  r21  ;△
368: 6f 93    push  r22  ;△
36a: 7f 93    push  r23  ;△
36c: 8f 93    push  r24  ;△
36e: 9f 93    push  r25  ;△
370: af 93    push  r26  ;△
372: bf 93    push  r27  ;△
374: 2a 9a    sbi 0x05, 2   ;PB2
376: 20 91 86 00 lds r18, 0x0086 ;cnt_r = ICR1 ★1
37a: 30 91 87 00 lds r19, 0x0087
37e: 90 91 81 00 lds r25, 0x0081
382: 80 e4    ldi r24, 0x40  ;XCG_EDGE1 ★1
384: 89 27    eor r24, r25
386: 80 93 81 00 sts 0x0081, r24
38a: b0 9b    sbis  0x16, 0 ;if(CK_OVF1) ★2
38c: 0e c0    rjmp  .+28    ; 0x3aa
38e: 20 3a    cpi r18, 0xA0  ;if(cnt_r < 160)
 ※長いので以下省略
=============================

★1の処理をできるだけ前に持ってきたいのです。
そうすれば、短いパルスもちゃんと処理できるようになります。

10個ある△の「push命令」。
命令実行の手順としては、これを★1より後に下げても同じ。
先にpushしてるr0とr1でもって処理できます。

アセンブラで書けば自由になるんですが、「Arduino」の環境だと
如何とも・・・
   ↑
 ここらがhelp!でっす。

※豆知識
AVRマイコンの割り込み優先順序(同時に割り込み要因が起動し
た時、どっちが先に処理されるか)、割り込みベクトルの若い方
(先頭に近い方)が優先されます。
タイマーではオーバーフローより、コンペアマッチと
インプットキャプチャ-が優先されます。

リストの★、もう一つが★2。
これがオーバーフローの処理。
ICP割り込み処理の前にオーバーフローが発生し、その割り込み
処理が遅れても32bitのカウント値は正しく読めます。

※参考図
D21
しかし、ICP割り込みとOVF割り込みが同時、あるいは微妙な
タイミングならどうかというのを考えたのがこの図です。
D22

ICP割り込みで、
・OVF割り込みフラグが無ければクリチカルな問題は無し。

・処理されてないOVF割り込みフラグが残っていても、
 その時のキャプチャーカウント値が十分に大きければ
 クリチカルな問題じゃ無い。

・OVF割り込みフラグがあって、キャプチャーカウント値
 が小さい時は、微妙なタイミングが発生してる。
 その時は、ICP割り込み内でもオーバーフローの処理が
 必要。

こんな処理がICP割り込み内に必要です。
でもこれは↑↓エッジ検出から遅れても問題なし。

短パルスを捉えるため、ICP割り込みでまず優先しなくちゃ
ならないのはキャプチャーカウンタの読み出しと↑↓エッジ
検出の逆転処理です。


※Arduino環境で機械語命令をどう書く、というのが課題・・・

※調べてたらこんなのが・・・
 これで、解決できそうな。
    ISR(TIMER1_CAPT_vect, ISR_NAKED)

ISR_NAKEDを入れると、割り込みルーチンのpush、pop、reti、
それにsregのセーブとリストアのルーチンが全部無くなります。

ISR_NAKEDを入れる前と入れた後を比較して、抜けてる
処理を手で(インラインアセンブラで)入れ込めば
解決(★1処理をちょっとでも速く)できるかっと。

こりゃ面白い!

|

« Arduino UNOのPWM出力を(ちょっと精密に)確かめる | トップページ | Arduino インプットキャプチャー割り込みを「ISR_NAKED」で (デューティー比測定) »

Help me! (助けて!)」カテゴリの記事

Arduino」カテゴリの記事

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

コメント

「ISR_NAKED」、解説はあれこれ出てきますが、
「具体例」が少ないようでっす。

ちゃんと動かすまで、ちょい待って。

投稿: 居酒屋ガレージ店主(JH3DBO) | 2020年8月18日 (火) 20時28分

コメントを書く



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




« Arduino UNOのPWM出力を(ちょっと精密に)確かめる | トップページ | Arduino インプットキャプチャー割り込みを「ISR_NAKED」で (デューティー比測定) »