0から1MHzの範囲で変化し、0.25Hzの分解能を持つ方形波の周波数を測定する必要があります。
私はまだどのコントローラーを決定していませんが、それはおそらく20ピンAttinyの1つでしょう。
通常、より低い周波数の信号を測定する方法は、タイマーキャプチャモードで設定された2つのタイマーを使用して、外部信号の立ち上がりエッジで割り込みを行うように設定し、別のタイマーを毎秒割り込みするように設定することにより、前のタイマーは1秒後のレジスタ値をカウントします信号の周波数に等しくなります。
ただし、この方法は明らかに0.25Hzの解像度で0〜1MHzの範囲の信号をキャプチャするには機能しません。これには22ビットカウンターが必要です(AFAIK 8ビットマイクロには8/16ビットカウンターしかない)。
私が持っていたアイデアの1つは、信号をマイクロに適用する前に分割することでしたが、信号を61で除算する必要があるため、これは実際的ではなく、周波数は61秒ごとにしか更新できず、数秒ごとにしたい場合があります。
頻度を4秒ごとに更新できる別の方法はありますか?
更新:
最も簡単な解決策は、外部割り込みまたはタイマーキャプチャを使用して、信号の立ち上がりエッジで割り込みを行いisr
、変数の型をインクリメントすることですlong int
。4秒ごとに変数を読み取ります(0.25Hzまでの周波数を測定できるようにするため)。
アップデート2:
JustJeffが指摘したように、8ビットMCUは1MHz信号に追いつくことができないため、すべての立ち上がりエッジで割り込みを中断し、long int
...
私はtimororrが提案した方法を選びました。実装に取り掛かったら、投稿して結果を共有します。ご提案いただきありがとうございます。
経過報告:
ここで紹介するアイデアのいくつかをテストし始めました。まず、vicatcuのコードを試しました。頻度の計算後にTCNT1がクリアされないという明らかな問題がありました-大したことではありません...
次に、コードをデバッグするときに、約2〜7回の頻度で計算されたタイマー1(外部イベントをカウントするように構成されたタイマー)のオーバーフローカウントが2だけ短いことに気付きました。これをタイマー0 ISRのレイテンシに置き、ifステートメントブロックをISRからメイン(以下のスニペットを参照)に移動して、ISRにフラグを設定することにしました。いくつかのデバッグは、最初の測定は問題ないが、その後の読み取りごとに、タイマー1のオーバーフローカウントが2を超えることを示しました。これは説明できません。
int main()
{
while(1)
{
if(global_task_timer_ms > 0 && (T0_overflow == 1))
{
global_task_timer_ms--;
T0_overflow = 0;
}
.....
}
}
次に私はティムロスの提案を実装しようとすることを決めました。Atmega16の唯一の16ビットタイマーが外部信号の立ち上がりエッジをキャプチャするために使用されているため、必要な間隔(各timer_isr割り込みの間に約15ms)を生成するには、2つの8ビットタイマーをカスケードする必要があります。
オーバーヘッドのほとんどがタイマーにシフトされ、CPUが処理する短い残りが1つしかないため、このソリューションが機能し、はるかに効率的だと思いました。しかし、私が期待していたほど正確ではなかったため、測定値は約70Hzだけ前後にシフトしましたが、高周波数では問題にはなりませんが、低周波数では許容できません。私は問題の分析に多くの時間を費やしませんでしたが、2つの16ビットタイマーを備えた非常に遅い8051コントローラーにtimrorrsの提案と同様の構成を実装しており、結果は非常に正確だったため、タイマーのカスケード構成はそれほど正確ではないようです。
これでvicatcuの提案に戻りましたが、周波数計算をTimer 0 isr (下のスニペットを参照)に移動しました。このコードにより、一貫して適度に正確な測定が行われました。少しの校正で、精度は約+/- 10Hzになります。
ISR(TIMER0_OVF_vect)
{
TCNT0 = TIMER0_PRELOAD; //Reload timer for 1KHz overflow rate
if(task_timer_ms > 0)
{
task_timer_ms--;
}
else
{
frequency_hz = 1.0 * TCNT1;
TCNT1 = 0;
frequency_hz += global_num_overflows * 65536.0;
global_num_overflows = 0;
frequency_hz /= (TASK_PERIOD_MS / 1000.0);
task_timer_ms = TASK_PERIOD_MS;
}
}
誰かが他に提案している場合は、私にオープンにしますが、範囲を使用する必要はありません... 0.25%の解像度を取得することを意図しなくなったため、現在の精度レベルにはあまり意味がありません。 。