免責事項

注意、免責事項について
・高電圧、大電流その他高エネルギを扱う実験を行う際にはくれぐれも安全に配慮し、細心の注意を払って行うようにしてください。
・当ブログの記事を参考にして発生した損害についてFEL研究室はいかなる責任も負えませんでご了承ください。

2018年2月4日日曜日

AVRで正確にタイマー割り込みを使いたい

お久しぶりです。随分と間が空いてしまったような気がします。
日付が変わったところですが、昨日は節分という祝日でした。豆まきをしたり、恵方巻きを食べたりするのが一般的ですが、私はスパゲッティを食べたことを報告しておきます。

AVRでタイマー割り込みを使う時に、普段はメインルーチンを回していて、一定期間ごとにタイマー割り込みが入るような構成になることがよくあると思います。しかし最近、そんな感じのプログラムを組んでいて、タイマー割り込みを使っているのに何故か僅かに割り込みの周期が遅れる現象に嵌ったので、原因と解決方法(?)を書き留めておくことしにしました。

まずATMega8にこんなアセンブラのプログラムを書き込んで走らせてみます。

↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
.include "m8def.inc"

//ATmega8 lfuse = 0xe1 = 0b1110_0001 hfuse = 0b1101_1001
//10MHzのセラロックで使いたいので lfuse = 1100_0011 hfuse = 0b1100_1001
//avrdude -c avrisp -P COM12 -b 19200 -p m8 -U lfuse:w:0b11001111:m -U hfuse:w:0b11001001:m

.EQU fclock = 10.0//[MHz]
.EQU freq = 15.734//[kHz]
//.EQU timervalue = (fclock/freq)*1000.0

//こうしたいがEQUシンボルは4バイトINT型
.EQU Htime = 635//[cycles]

.DEF temp = R16
.DEF temp2 = R17

.ORG $0000
    RJMP RESET ;各種リセット
    RETI//RJMP EXT_INT0 ;外部割り込み要求0
    RETI//RJMP EXT_INT1 ;外部割り込み要求1
    RETI//RJMP TIM2_COMP ;タイマ/カウンタ2比較一致
    RETI//RJMP TIM2_OVF ;タイマ/カウンタ2溢れ
    RETI//RJMP TIM1_CAPT ;タイマ/カウンタ1捕獲発生
    RJMP VideoOut//RJMP TIM1_COMPA ;タイマ/カウンタ1比較A一致
    RETI//RJMP TIM1_COMPB ;タイマ/カウンタ1比較B一致
    RETI//RJMP TIM1_OVF ;タイマ/カウンタ1溢れ
    RETI//RJMP TIM0_OVF ;タイマ/カウンタ0溢れ
    RETI//RJMP SPI_STC ;SPI転送完了
    RETI//RJMP USART_RXC ;USART受信完了
    RETI//RJMP USART_DRE ;USART送信緩衝部空
    RETI//RJMP USART_TXC ;USART送信完了
    RETI//RJMP ADC ;A/D変換完了
    RETI//RJMP EE_RDY ;EEPROM操作可
    RETI//RJMP ANA_COMP ;アナログ比較器出力遷移
    RETI//RJMP TWI ;2線直列インターフェース状態変化
    RETI//RJMP SPM_RDY ;SPM命令操作可

Reset:
    LDI R16,high(RAMEND)
    OUT SPH,R16//スタックポインタ上位設定
    LDI R16,low(RAMEND)
    OUT SPL,R16//スタックポインタ下位設定
//スリープ設定
    LDI temp,(1<<SE)//アイドル動作に設定する
    OUT MCUCR,temp
//タイマ設定
//16bitレジスタに書き込む時は上から
    LDI temp,high(Htime)
    OUT OCR1AH,temp
    LDI temp,low(Htime)
    OUT OCR1AL,temp
//TCCR1Aは初期値のまま
    LDI temp,     (0<<COM1A1|0<<COM1A0|0<<FOC1A|0<<FOC1B|0<<WGM11|0<<WGM10)
//タイマA比較一致有効
    OUT TCCR1A,temp
    LDI temp,(0<<TICIE1|1<<OCIE1A|0<<OCIE1B|0<<TOIE1)
    OUT TIMSK,temp
    LDI temp,

(0<<ICNC1|0<<ICES1|0<<WGM13|1<<WGM12|0<<CS12|0<<CS11|1<<CS10)
    OUT TCCR1B,temp
   
    LDI temp,(1<<PD0)
    OUT DDRD,R16//PORTDの0ビット目を出力に設定
    SEI

Main:
    //SLEEP//待ち時間はスリープにしておく
    RJMP Main

VideoOut:
    SBI PORTD,0//high
    CBI PORTD,0//low
    RETI


↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑

改行が微妙に入ってしまって読みにくいことこの上無いのですがどうしたら良いのでしょうか。
まずReset:で色々な初期設定をして、Main:を無限ループさせて一定時間ごとに入るタイマー割り込みごとにパルスを出すようなプログラムです。SBI、CBI命令はそれぞれ2クロック掛かるので出るパルス幅は2クロック分、クロックが10MHzなので200nsということになります。タイマー割り込みの周期はHtimeというシンボルに入れたサイクル数になるようになっていて、クロックと周波数から自動で計算しようと思ったのですがコメントにもあるようにシンボルは4バイトのint型なので小数点以下の計算ができずだいぶざっくりなサイクル数になってしまうので諦めてサイクル数は手計算しました。Htimeに入れる周期は理想のサイクルから2か3くらい引かないと、タイマー割り込みの固定的な遅延時間で遅れるのでご注意ください、ってデータシートにも書いてると思います。
ラベルでピンと来た人もいらっしゃるかもしれませんが、これはビデオ信号の水平同期処理をタイマー割り込みでやろうとした時のソースの一部です。
とまぁタイマー割り込みで一定周期のパルスが出るだろうと動かしてみると下のような波形が出力されました。
なんとタイマー割り込みが入るタイミングが一定ではなくちょうど200nsずれて入っている時があるようです。これは遅延掃引で62.7us立ち上がりから遅らせている波形なのですが、ズレの部分の明るさがさほど変わらないことからたまーに遅れる(ないし早く割り込んでいる)どころでは無く頻繁に割り込み時間が変化してしまっていることもわかります。私はこれで相当悩んだのですが、結論から言うと原因は割り込みが入った瞬間に実行している命令の影響です。
データシート(https://www.mouser.com/ds/2/268/Atmel-2486-8-bit-AVR-microcontroller-ATmega8_L_dat-1065398.pdf)の16ページを見ると割り込みの応答時間についての記述があります。
"... If an interrupt occurs during execution of a multi-cycle instruction, this instruction is completed before the interrupt is served. ..."
複数サイクル命令の実行中に割り込みが入ると、 命令が実行され終わってから割り込みが入ります。とのことです。
1サイクル命令なら何の問題もないのですが、RJMPやBR**系のジャンプが入る命令は2サイクル掛かりますし、SB**系やLPM等は3サイクルかかるものもあります。つまりこれらを実行中に割り込みが入ると、命令を実行し終えるまで割り込みが入れないので遅延が発生することになります。タイマー割り込みって言うんだったら勝手に正確に割り込みが入ってくれたらいいのにと思ったんですがそんなに甘くは無いみたいです。
上のプログラムは、割り込み待ちの時に回しているループのRJMPが2サイクル命令なのでRJMP命令を実行している時には2サイクル遅れ、RJMPを実行する前なら遅れないので設定数値通りと2サイクル遅れたパルスが出力されているようです。なおタイマー割り込みの周期を1サイクル小さくするか大きくするとこのズレは無くなります。
解決策としては、割り込み待ちの処理の命令を全て1サイクル命令にするというのが一番本質的な解決になるような気がしますがまず無理なので私はスリープを使うことにしています。スリープが許可されている状態でSLEEP命令が実行されるとプログラムはそこでスリープ状態に入り、以後の命令は実行されません。そこに割り込み等が入るとスリープから復帰し、また命令を実行し始めます。スリープから復帰する時には4サイクル掛かるのですが、逆に言えばスリープに入ってさへいれば遅延が4サイクルに固定されるということです。ですので、予めこのサイクルをタイマー設定の数値に計上しておいて、タイマー割り込みが入る前にスリープにさせることで設定値通りの割り込みが実現できるのではないかと思います。

Web拍手反応
>海外からのアクセスすごそう
ほぼ無いですね

> 綺麗に立方体ならんかったやつほんま草
あれステレオの片方だけ作ったところでやばいなって感じて放置してあります。時々鳴らしてみたりするんですがなんとも。

> 今更なんですけどHF SSTCの記事投稿の予定ってあるんですか?
そうですねぇ…。予定と言ってしまえばどうとでもなるのですが、今のところ無いです。書きたい記事ではあるのですがいまいち全体として再現性のあるものになら無さそうで結局写真集みたいになっちゃうかもしれません。

> 電子工作について質問しても良いでしょうか?
先日「矩形波」を「くけいは」と読むと知り、びっくりしました。そこで不安になったのですが、「方形波」ってなんて読むんでしょうか?是非ご教授頂ければと思います。
そんなこと聞かなくてもいいです。

> いっぱいのおっぱいがいる
> マ
 ル
  ク
   ス
> あけましておめでとうございます。今年もFEL研究室をよろしくお願いします。
> なかた
おめでとうございます。マルクスのコメントは黙殺することにします。

> NTでは何を展示するんですか
NT京都2018に出展することにしました。KKTで申し込みしています。出展内容としては超音波関係の何か(未定)を持っていく予定です。また完成したら報告します。間に合わなければIHかテスラ持って行きます()。

それでは、今日はこの辺で
http://clap.webclap.com/clap.php?id=fellabo

0 件のコメント:

コメントを投稿

何かあればお気軽にどうぞ。