高分解能かつ長周期インターバルタイマ
16ビットを超える処理は32ビットタイマーを使え、って
ことでもあるけれど。実際stm32あたりでも、32ビットまで
実装されているタイマーは限られている。
まあ、lpcみたいにどれもこれも32ビットタイマー、
なんて実装のもあるし。
チップの設計思想なんだろけどね.....
stm32に関しては、沢山タイマーがあっても、そのうち
32ビット実装は1~2個なので、st的にはタイマーの
物理カウンタは16ビットあれば十分でしょ?と言いたいのか。
それ以上の事は、ソフトウエアでどうにでもなるよね?って
メッセージの気もする。。
まあ....まあ、そうなんだけど。
-------
ざっくり、だいたい動く(ときどきおかしい値が出る)んなら
簡単にコーディングできるけど、
ちゃんと動かすには、かなり考えないと出来ないよね....
適当な実装で、ときどきズッコケるタイマー処理コード
に付き合うことになると、流石にメンタル削れたりもするけどね。
現実的に、普通は16ビットの範囲で処理できるように、
プリスケーラーで動作クロックを落として、
タイマーレジスタを素直に設定できる範囲に
持ってきて処理をする。
原理的に時間分解能は下がってしまうが、その辺はさじ加減。
例えば、連続的に1Hz~20KHzとかの矩形波を作ろうとすると、
ソフトウエア処理は十分できても、タイマーの
分解能の限界が先に来て、
1Hzを設定値0xffffまででカバーできるように設定すると、
20KHz付近では設定できる周波数が、非常にとびとびになる。
このあたりで考えると、実質28ビットくらいは欲しい処。
では、と、プリスケーラ―の値を動的に書き換えると、
その瞬間に、一発だけ変な周期の割込みが発生したりとか、
これまた 上記の”まあ、だいたい動く” 一例となる。
これでもいいですよね?とねじ込む技術(キレたり、ゴネたりとか)
の方がお手軽ではあるのだが....
諸々コストを考えると、ねじ込む技術も大いにアリだけど、
ねじ込むばかりだと、段々行き詰っていくのも確か。
------
16ビットタイマーがオーバーフローするのを
ソフトウエアでカウントして、目的のタイミングだけで、
処理を回すようにするとよいのだが。
#define TIMERCONST 200000;
static unsigned long buff = TIMERCONST;
void TIM2_IRQHandler(void) {
if (TIM2->SR & (1 << 1)) {
TIM2->SR = ~(1 << 1);
if (buff == 0){
buff = TIMERCONST;
// ここでインターバル処理など
}
if (buff > 0x10000){
buff -= 0x10000;
// ここでタイマー出力禁止(コンペアマッチで出力をする場合)
}
else {
TIM2->CCR1 += buff;
buff = 0;
// ここでタイマー出力許可(コンペアマッチで出力をする場合)
}
}
}
とまあ、こんな感じで記述すると、16ビットを超える
タイマー定数でのインターバル処理を実現できる。
この記述にも問題があって、buff値が
非常に小さくなると、CCR1の更新が間に合わなくなり、
カウンタ1ループ分、処理が遅れるタイミングが
できてくる。
(例えば初期値0x20001とか)
これはハードウエアの割込み処理応答に起因するので、
このままでは回避できず、処理間隔として上手く動かない
値が存在しているということになる。
簡単にソフトウエアで処理するとすれば、
この付近の値が設定されないように
事前にチェックし、近くて安全な値に変更する、
などが考えられる。
割込み応答や、優先順位の高い処理がどの程度あるか
によって、うまく動かない定数の範囲が変わっていくのだけど
DMAなんかも動かしているようなシステムだと、
どの程度まで余裕を見ればいいかの判断が難しい。
絶対安全、だからと言って、ハードウエアで
動かしているタイマーが数msecもズレる
ようだと、流石に意味をなさない。
長周期で動かしているインターバル処理を
成立させるために、
許容できる応答時間が非常に短いとか、
システムとしてはかなりダメな気がする。
------
なので、もう一工夫して、
TIM2->CCR1 += buff;
のbuffの値が小さくならないような処理を追加する。
void TIM2_IRQHandler(void) {
if (TIM2->SR & (1 << 1)) {
TIM2->SR = ~(1 << 1);
if (buff == 0){
buff = TIMERCONST;
// ここでインターバル処理など
}
if (buff > 0x10000){
if (buff < 0x20000){
buff -= (buff / 2);
TIM2->CCR1 += buff;
}
else {
buff -= 0x10000;
}
// ここでタイマー出力禁止(コンペアマッチで出力をする場合)
}
else {
TIM2->CCR1 += buff;
buff = 0;
// ここでタイマー出力許可(コンペアマッチで出力をする場合)
}
}
}
こんな感じにして、最後の引き算の結果が
小さくなりすぎないようにしてやる。
これで、タイマーが誤作動するような危険なタイミングを回避しつつ
必要な割り込み処理の応答も、シビアではなくなる。
(割込み処理の間隔が、0x10000以上から始めた場合、
0x7fff以下にはならない)
この記述で、めでたく16ビットタイマーで、
16ビット超のイベント間隔と時間分解能を両立できる。
つづく
| 固定リンク | 0
コメント