長周期かつ高時間分解能なインプットキャプチャ
で、16ビットを超えるタイマーイベント、
インプットキャプチャ部門だけど。
これ、まあ。
ペリフェラルの機能として
一つのタイマーのオーバーフローを
別のカウンターの入力クロックに接続、
なんて設定があるので、これを使えば
良さそうなもんだけど。
キャプチャとオーバーフローが
重なったときにそれぞれの
レジスタが、正しい値で
整合しているという保証はない。
内部ロジックで、キャプチャの
ロジックが接続されているわけでなく、
信号をそれぞれのタイマーに
個別に 入力する関係で、
こういうクリティカルパスが
発生する。
まあ、ごくまれにしか発生しないけど、
それゆえ、検出と対策が難しい。
まあ、素直に32ビットタイマ使っとけ、
という案件ではあるのだが....
------
なので、これも、コンペアマッチと
同様にソフトウエアで上位ビットを
あれこれやって、機能を実現してみる。
タイムアウトに使っている
インターバルカウンタの値を、
カウンタの上位データ
(の指標)として使う。
当然、そのまま上位データ
としては使えないので一工夫。
データとして使うのは
あくまでタイマーのキャプチャ
レジスタの値。
------
説明しやすくするために
インプットキャプチャは1MHzで
カウントしていて、タイムアウト用
の別タイマは1msecインターバルで
動いているとしようか。
とあるタイミングでキャプチャされた
値と前回キャプチャされた
値の差がNとなった場合、
この時の真のパルスの間隔は、
N+65536*M(usec)
となる(Mは0~)
値として分かるのはNだけなので、
ここからMを推測する。
ここで、1msecインターバルで
ソフトウエアカウンタを回し、
1msecでのパルス間隔も
(大まかに)計測する。
こっちは、C(msec)としようか。
この2つのデータを合わせ、元のMを計算する。
N + 65536 * M ≒ C * 1000
なので、
M ≒ (C * 1000 - N) / 65536
と変形して、Mの概算値を求める。
計測されたCの値は、正確ではないけれど、
概ね整数値の近くに寄ってくる。
整数計算で四捨五入すれば、Mが求まるので
M = (C*1000 - N + 32768) / 65536
で、Mが確定し、
N + 65536 * M
で目的のインターバル(usec)が求められる。
タイムアウトのインターバルは、
元のクロックやオーバーフローから
一桁くらい離れていれば問題なく
機能するはず。
これで、16ビットタイマーで
長ビットでのインプットキャプチャが
実現できる。
ちょっとややこしいけれど、
この方法を使うようになって、
タイマー周りのクリティカルパスは
大体回避できるようになったと思う。
コード例としては
#define TIMEOUT 1000
unsigned long interval; // 計測した時間間隔
int timeout; // タイムアウトカウンタ
void SysTick_Handler(void){ // 1msecインターバル
if (timeout > 0 ){
timeout--;
if (timeout == 0){
interval = 0xffffffff; // タイムアウト
}
}
}
}
void TIM2_IRQHandler(void) { // ch1でインプットキャプチャ
static unsigned short last;
unsigned short us;
if (TIM2->SR & (1 << 1)) { //インプットキャプチャ
us = TIM2->CCR1;
if (timeout > 0){
interval = ( (unsigned long)(32768L + (TIMEOUT - timeout) * 1000 - ((us - last) & 0xffff) ) & 0xffff0000) + (unsigned long)(us - last);
}
last = us; // 今回の値を保存
timeout = TIMEOUT; // タイムアウト初期化
}
}
これもそうだけど、
こういったプログラム、
何をやろうとしているのか、
コードだけから読み取るのは
かなり難しいね...
| 固定リンク | 0
コメント