入力されるパルスの周波数を測る
パルス幅を計測するのをやっておきます。
赤外線通信、リモコン信号の解析などなど、やってくるパルス幅を計測する必要は時々あります。
3664FでもタイマーWを使えば割と簡単にパルスの幅は計測できます。
パルス幅を測ったので周波数も同じように測れます。
一定時間に 何パルス来たか のパルスを検知して数えます。
周波数の単位Hz は 1秒間に何個の波が来たかの単位なので、一定時間幅でのパルス数の測定です。
一定時間内でのパルス数を数える方法はいくつもあるかと思います、
その計測方法は
タイマーで一定時間を決め、その間に何パルス来たかを測る ことになります。
そのためには
- タイマー自体の機能でパルス取り込みする
- タイマーの一定時間の間に IOピンの入力などでパルスを数える
の方法が出来ます。
ここでは1. の方法で タイマーWのインプットキャプチャ を使います。
タイマーで外部からのパルスを取り込むのを出来るだけ簡潔にハード機能でやろうとすると、 3664Fでは
タイマーVでは 外部入力設定は カウントアップ開始機能のトリガしかない。
タイマーWでは外部入力を 外部クロック入力として、内部カウンタに代えてカウンタをカウントアップする機能なので、一定時間のパルス数計測にはうまく利用できません。
タイマーWのインプットキャプチャを使います。
いろいろな方法が考えられますが、このハード機能を使うことが一番簡単にできるでしょう。
インプットキャプチャは、パルスの幅を計測する でも使いました。
一定時間を作る
タイマーWのオーバーフローを使って正確な一定時間を作ります。
パルス数を数える
インプットキャプチャ機能は
”外部からのインプットキャプチャ信号を検出すると、TCNTの値が格納され、その時割り込み要求の発生も可能”
という機能です
この機能を使い
TCNT→GRA などの転送はされていても利用せず、
カウンタで一定時間間隔を測り、その間、インプットキャプチャ信号での割り込みでパルス数をカウントする方法に使います。
回路図
パルスの入力ピンは インプットキャプチャD(P84) に入力するだけです。
プログラム
TmW_RPM_fc_OVF
/*
タイマーWのインプットキャプチャで 周波数カウター
オーバーフローで0.025s間隔を作り それを40回のカウントで1秒
その間の入力パルス数をインプットキャプチャ機能でカウントする
インプットキャプチャは立ち上がりをカウント
入力ピンは インプットキャプチャD(P84)
*/
#include<3664.h>
#include "myfunc.h"
short cnt,twcnt;
void int_timerw (void){
//オーバーフロー割り込みの場合 0.025s毎
if(TW.TSRW.BIT.OVF == 1){
TW.TCNT = 15535;
if(twcnt == 40){ //×40 → 1秒間
LCD_locate(1,2);
LCD_dataout(cnt); //カウント値表示
cnt=0; //カウント初期化
twcnt=0;
}else{
twcnt++;
TW.TSRW.BIT.OVF =0;// 検知フラグを戻して再開
}
}
//インプットキャプチャD割り込みの場合
if(TW.TSRW.BIT.IMFD == 1){
cnt++;
TW.TSRW.BIT.IMFD = 0; // 検知フラグを戻して再開
}
}
int main(void){
DI;
//TmrW設定
TW.TCRW.BYTE=0x30; // カウンタクリアなし、内部クロックの1/8( 2MHz ) FTIOBの初期値は0
TW.TIERW.BYTE=0x88; //1000 1000 オーバーフロー割り込み & インプットキャプチャ割り込みD
TW.TIOR1.BYTE=0x40; //0100 0000 D:インプットキャプチャ、立ち上がりエッジ
TW.TCNT=15535; // TCNの初期化
TW.TMRW.BIT.CTS=1; // TCNTカウンタスタート
cnt=0; //カウント初期化
twcnt=0;
LCD_init( 16 ); //LCD初期化
EI;
while(1);
}
1秒の間のパルスを数える
方針は
タイマーWのインプットキャプチャ割り込みでパルス入力を取り込みます。
- まず0.025秒の時間間隔を作ります。これは TCNTのオーバーフローで測ります
- その0.025秒間×40 =1s に 何個のパルス数が入ったかをインプットキャプチャ割り込みで数えます。
パルス入力はインプットキャプチャD(P84)を使います。
0.025秒 を作る
オーバーフロー割り込みで作ります。
タイマーWは16ビットのタイマーなので 16ビット満タンで 65535 までです。(タイマーVは8ビット)
クロックの分周はタイマーWでは最大遅らせて φ/8 。
つまり 分周 φ/8 設定 で 1クロック 16/8= 2MHz (1÷2000,000 秒=0.0000005秒)
これがカウンタの 一進み=1キザミ。
オーバーフローまでに測れる最大は 0.0000005s×65535 = 0.032767秒
タイマーWで正確に作れる最大間隔の単位は最長これになります。
中途半端なので0.025秒でオーバーフロー設定にします。これでオーバーフローで 0.025秒の間隔を作ります。
オーバーフロー40回で 0.025×40=1秒 の間隔にします。
0.025秒でオーバーフロー設定 は カウンタ値で 50000
タイマーのオーバーフローは カウンタTCNTが 満タン=65535 を超えたときカウンタが0に戻るのがオーバーフローなので
オーバーフローするときのカウンタ値 65535 は設定では変えられません。
スタート値を設定して カウント50000を数えます。
65535-50000=15535 スタート値をこれにすると タイマがあと50000カウント(0.025秒)するとオーバーフローとなります。
40回のオーバーフローで 1秒の間隔が作れます。その間隔でカウントした表示をだしています。
インプットキャプチャでカウント
40回のオーバーフロー → 1秒の間隔 の間に インプットキャプチャ割り込みでパルスを数え表示する内容です。
インプットキャプチャの立ち上がりでの割り込み設定にしています。
これでパルス波が入力される立ち上がりごとに割り込みがかかり、それをcntにカウントします。
インプットキャプチャ設定は パルスの幅を測る のような設定です。
誤差の調整
誤差の調整
//オーバーフロー割り込みの場合 0.025s毎
if(TW.TSRW.BIT.OVF == 1){
TW.TCNT = 15535;
if(twcnt == 40){ //×40 → 1秒間
LCD_locate(1,2);
LCD_dataout(cnt); //カウント値表示
cnt=0; //カウント初期化
twcnt=0;
}else{
twcnt++;
TW.TSRW.BIT.OVF =0;// 検知フラグを戻して再開
}
//TW.TSRW.BIT.OVF =0;// 検知フラグを戻して再開
}
以前ためしたように
オーバーフロー割り込み処理部分
if(twcnt == 40){
・
・
}else{
・
TW.TSRW.BIT.OVF =0;// 検知フラグを戻して再開
}
//TW.TSRW.BIT.OVF =0;// 検知フラグを戻して再開
コメントアウトしてある //TW.TSRW.BIT.OVF =0;// 検知フラグを戻して再開
の場所でフラグクリアをするのが通常ですが、 これでは、今までのように誤差がでます。
偶然できたのがモトですが、
else の中で これをクリアにすると 間隔カウンタ if(twcnt == 40){ の時には クリアされないので、
オーバーフロー割り込み処理、
if(TW.TSRW.BIT.OVF == 1){
を終えた直後に、再度また同じ割り込み処理を繰り返すことになります、その2度目の時は
else部分
else{
twcnt++;
TW.TSRW.BIT.OVF =0;// 検知フラグを戻して再開 } if(twcnt == 40){
}
を実行するので、間隔時間を待たず 間隔カウンタ twcnt++; を1つ進める結果になっています。
これで、測定時間間隔が 1単位0.025s 縮まるので、その分 短い間隔を測定するようなことになりますが、
これの方がだいぶん正確になり、補正になります。
PCに転送表示
PCにラジコンサーボの多軸 同様、転送表示するもの
ハイパーターミナルの設定などは同じ
実行
今回は、周波数発生器(FG:ファンクションジェネレター)で出力するパルス波を入れてみると、だいぶ誤差がでそうで、FGの出力と同じ数字が表示されるわけないと思っていたのですが、意外にも正確でした。
FGとの表示の違いは
周波数
~999(3桁) Hzまでは 同じ
1kHz ~ 10kHz では +3Hz程度
意外に正確でした。簡易周波数カウンタになりました。
*通常の方法
//TW.TSRW.BIT.OVF =0;// 検知フラグを戻して再開
にして
時間を間隔カウンタ twcnt で短くし
twcnt == 40 → 39
とすると
周波数
~999(3桁) Hzまでは 2Hz程度
1kHz ~ 10kHz では +20Hz程度
と上の補正より、誤差が大きくなった結果です。
- twtter
- google+
- hatena