Arduino インプットキャプチャー割り込みを「ISR_NAKED」で (デューティー比測定)
2020年8月18日:Arduino デューティー計測のためのインプットキャプチャータイミング
これの「ISR_NAKED」による高速化、こんなコードでやってみました。
SBI、CBI命令を先頭と尻尾に持ってこれたので、
割り込みタイミングが鮮明になりました。 ★1
※これまではpush、popに隠れていた。
で、結果ですが↑↓エッジ間が42クロックくらいまでは
ミスなく取り込めるようになりました。 ★2、★3
これまで、64クロックがギリギリだったのを思えば、
ちょいと進歩です。
※タイミングなどはまた今度に
/***** タイマー1インプットキャプチャー割込 *****/
// ICP1の↓↑エッジでキャプチャー
// "ISR_NAKED"指定でpush,pop,reti,sreg処理を無しに
ISR(TIMER1_CAPT_vect, ISR_NAKED)
{
// PB2_H; // (!!!)
// cnt_r = ICR1; // キャプチャー値
// XCG_EDGE1; // 次にそなえて↑↓エッジをトグル
asm volatile(
" sbi 0x05, 2 \n" // PB2 H ★1
" push r18 \n"
" push r19 \n"
" lds r18, 0x0086 \n" // ICR1 ★2
" lds r19, 0x0087 \n"
" sts cnt_r , r18 \n"
" sts cnt_r + 1 , r19 \n"
" in r18, __SREG__ \n"
" push r18 \n"
" lds r19, 0x0081 \n" // TCCR1B ★3
" ldi r18, 0x40 \n"
" eor r18, r19 \n"
" sts 0x0081, r18 \n"
" push r0 \n"
" push r1 \n"
" eor r1, r1 \n"
" push r20 \n"
" push r21 \n"
" push r22 \n"
" push r23 \n"
" push r24 \n"
" push r25 \n"
" push r26 \n"
" push r27 \n"
);
// オーバーフロー有無
if(CK_OVF1){ // 割込処理中にOVF発生したか?
if(cnt_r < 160){ // 10uS(160ck)未満なら未処理のOVFがある
cnt1_ovf += 1; // オーバーフローカウンタ+1
CLR_OVF1; // オーバーフローフラグをクリア
}
}
// カウント差を計算
cnt1_n = (uint32_t)cnt_r + // 32bitでカウント値
((uint32_t)cnt1_ovf << 16);
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
// PB2_L; // (!!!)
// "ISR_NAKED"復元
asm volatile(
" pop r27 \n"
" pop r26 \n"
" pop r25 \n"
" pop r24 \n"
" pop r23 \n"
" pop r22 \n"
" pop r21 \n"
" pop r20 \n"
" pop r1 \n"
" pop r0 \n"
" pop r18 \n"
" out __SREG__, r18 \n"
" pop r19 \n"
" pop r18 \n"
" cbi 0x05, 2 \n" // PB2 L ★1
" reti \n"
);
}
※豆知識
・I/Oポートをビット操作するSBI、CBI命令ではフラグは変化しない。
だもんでsregは変化しない。
・AVRマイコンには8bitレジスターが32個あるんだけれど、
レジスターr0~r15はイミディエート命令が使えないのだ。
LDIやANDI、ORIのような即値が扱えるのはr16~r31の
16個だけ。
※追実験
ICR1レジスタ(インプットキャプチャー)を先に読むより、
↑↓クロックエッジを切り替える処理を先に持ってくる方が
良さそうです。★4
こんな具合。
※「asm volatile(」も「__asm__ volatile(」にしました。
ISR(TIMER1_CAPT_vect, ISR_NAKED)
{
// PB2_H; // (!!!)
// XCG_EDGE1; //★4 次にそなえて↑↓エッジをトグル
// cnt_r = ICR1; // キャプチャー値
__asm__ volatile(
" sbi 0x05, 2 \n" // PB2 H
" push r18 \n"
" push r19 \n"
" in r18, __SREG__ \n"
" push r18 \n"
" lds r19, 0x0081 \n" // TCCR1B ★4
" ldi r18, 0x40 \n"
" eor r18, r19 \n"
" sts 0x0081, r18 \n"
" lds r18, 0x0086 \n" // ICR1
" lds r19, 0x0087 \n"
" sts cnt_r , r18 \n"
" sts cnt_r + 1 , r19 \n"
" push r0 \n"
" push r1 \n"
" eor r1, r1 \n"
" push r20 \n"
" push r21 \n"
これで「32クロック = 2us」のパルスを読めています。
読みが遅れても、直前の↑↓でキャプチャされた値が出て
くるので、うまく行ってる感じかなぁ。
※オーバーフロー割り込みと重なると・・・
やっぱ、カウントミスが出ます。
クロックが短くなり、ミスカウントし出すとデューティが
ほぼ50%の値になり周波数が半分になってしまいます。
短クロックは検出しているんですが、
「倍の周期でH/Lする方形波」
としてとらえているのでしょう。
※ICP1割り込みをOVF1割り込みがじゃまする様子
OVF1が重なってもちゃんと計測できた時。
もうちょいOVF1が遅れると失敗。
↓エッジへの切替が間に合わなくって、H区間の測定
ができなかった例です。
入力パルス周期とCNT1周期(4.096ms=約244Hz)との
重なり具合でOVF1がじゃまをするのです。
頻繁に発生するもんじゃないんで、この波形は
オシロスコープの組み合わせトリガー機能を使って
記録しました。
※ch1信号のHとch3信号のHをANDゲートして
トリガー源に。
「ISR_NAKED」で見えるようになった割り込みへの応答。
これまでは、push命令に隠れてしまって本当の応答が見
えなかったわけで。
ジッターの周波数がざっと8MHz。
中心±16MHzということなんでしょうね。
| 固定リンク
「Arduino」カテゴリの記事
- UNO R4はanalogWrite(n,128)でデュティー50%の方形波が出るぞ(2025.06.03)
- DDS IC「AD9833」の出力にバッファアンプを(2025.05.27)
- DDS IC「AD9833」をArduino UNO R3で制御(2025.05.25)
- DDS IC「AD9833」の周波数レジスタ書き込みで(2025.05.23)
- Arduino UNO R3で周波数を計る(2025.05.16)
「割り込み処理」カテゴリの記事
- 1クロックでも速くしたい 割込を「ISR_NAKED」で(2024.09.30)
- 1クロックでも速くしたい DDS方式の2相パルス発生器(2024.09.27)
- 最適化処理のせいで悩んだぞ 呪文volatile再び(2024.06.06)
- I2C液晶のアクセス、割り込みで処理しないようにすると(2024.04.12)
- I2C液晶のアクセス、割り込み処理で遅れる原因らしきもの(2024.04.07)
コメント