PICの初期設定
CCS-Cのおまじない
FUSEのセットです。内部クロックはH4で4逓倍させます。(10MHZX4=40MHz)だからといって、20MHZ取り付けて80MHZでは動きません。内部最大は40MHZですので・・・
//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/__/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_
//
// SPEED COUNTER
/
//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/__/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_
#include <18f8720.h>
#fuses H4,NOWDT,NOPROTECT,PUT,BROWNOUT,NOLVP
#device ADC=10//A/D変換10ビットモード
#USE DELAY(CLOCK=40MHZ, CRYSTAL=10MHZ)
#use RS232(BAUD=230400, XMIT=PIN_C6,RCV=PIN_C7)
//割り込み優先設定//
#PRIORITY CCP4,CCP5,CCP3,CCP2,CCP1,TIMER1
SPEEDパルスとは
タイヤ一回転で4、8、16、20、25パルス(通常は4パルス)0−5Vがパルスとして出力されます。JIS規格では駆動軸637RPMの時60KM/Hを指示するとなっています。
つまり、60km/hは毎分637回転ですので
60kmでのパルスを4パルス式で考えると:637X4=2548PULSE 2548/60=42.46Hzの周期でパルスが出力されます。
各速度では((X * 1000 * 100 * 10 mm) / 392.5mm) / (60 * 60) = Y plus/sec
10km/h = 7.1 plus/sec
20km/h = 14.2 plus/sec
30km/h = 21.2 plus/sec
40km/h = 28.3 plus/sec
50km/h = 35.4 plus/sec
60km/h
70km/h = 49.5 plus/sec
80km/h = 56.6 plus/sec
90km/h = 63.7 pulse/sec
100km/h = 70.8 plus/sec
走行距離は
毎分637回転で1時間走行出来る速度が60km/hですので
637x60x4=152880pulse
152880pulseで60kmとなるので
60x1000/152880=0.3924646781・・・・・・・・
4パルス式では1パルスで約0.3924646781m進んだことになります
ここで速度計測の分解能問題が出てきます。例えば10KM/Hの時パルス数をカウントして速度を計算する場合1秒間に7パルスカウントされるまでは速度が計算されません(分解能1秒)。1パルスの時間を計測して速度を算出するにはタイマーがオーバーフローしてしまいます。
1秒と考えないで、0.5秒に約3パルスであれば約10KMと判断できますが、正確ではありません。(分解能は0.5秒になります)
1パルスは立ち上がりと立下りの二つで考えた場合は10kmで7パルスとなりますが、立ち上がり、立下りをそれぞれカウントすれば1秒間に14パルスと考えることも出来ます。(パルス生成のSSG歯が正確に加工されているならばですが)
この問題を解決するための方法ですが低速での速度はある程度犠牲にし、パルスカウントとパルス時間測定併用法が一番実用的です。
低速ではパルスカウントによる速度計算、中速以上はパルス時間で計算します。パルスカウントは0.5秒間隔が最も実用的で、0.25秒では速度のバラツキが大きくなります。内部クロック40MHzでは実証試験において22kmを境にして、速度計測方法を切り替えるのが、最も自然に感じました。
※タイマーのビット数を拡張(オーバーフロー回数をカウントして計算)して時間計算する方法もありますが、低速域バラツキが大きくて筆者はやめました。
回路
ccp2 ccp3にてパルスをキャプチャ。シュミットトリガで波形を反転してますので極性は反転で考えて下さい回路は以下のようにしたとして説明します。
カウントエラー対策とノイズ対策
車両からのパルスをオシロスコープで観察すると、波形がまともな矩形波ではなく、さらにノイズが混入する場合があります。ハードで対策するには限界があるし、コストがかかってしまうので、ソフト処理でなんとかならないかなと、2年も(笑)試行錯誤してしまいました。実証試験からわかったことは、※タイマーカウンタ割込みによる表示をしなくても、100HZ以上の信号がたまに抜けることがある。
※ノイズが入力されるとミスカウントをしてしまう。
※インジェクション、ATソレノイド等の誘導負荷があるため、ノイズが避けられない
※#device HIGH_INTS=TRUEを宣言して高位割込みを使用するとプログラムメモリ大となる
※各計測パルスの割込み周期が偶発的に一致する。
※移動平均を使用しないほうがよい。
回転数−>高回転で波形が低くなる
インジェクション−>デューティー制御のためあまり使用しないほうがよいかな
※ABS動作、トラクションコントール、冬場のコールドスタート時にノイズが入ってくる場合が ある<BR>
※ABS動作の時の速度波形をパルス抜けとしないようにする必要がある。
※凹凸路面走行での波形処理を考慮する必要がある
※急ブレーキの減速比率を考慮する必要がある。(パルス抜けと処理されないように)
※シフトアップ、シフトダウン時の増減率を考慮する必要がある(パルス抜けと処理されない
ように)<BR>
※速度と回転数はPICの内部クロックを引き上げると低速・低回転でオーバーフローする
別の方法あるんですが、BootLoaderからめるとフリーズするので・・・・<BR>
※CCP UPエッジトリガとDPWNエッジトリガどちらか一方のみで使用できるのは速度と回転数
※インジェクシヨンはCCP UPエッジトリガとDPWNエッジトリガを使用する必要がある。
(デューティー制御のため)
※タイマーカウンタ割込みによる回転数計測は不安定
※エアコン動作するとノイズが混入する場合がある。
※パーキング停止状態でノイズ入ったら、ノイズと判断する(ATの状態監視)
※パルスとりこぼしは全てオリジナルパルス波形時間の2倍前後となる
※パルス時間を比較するよりも、表示対象に変換してから比較したほうがよい
インジェクション:パルス時間小=負荷小とは限らない(開弁率の関係より)
※回転数を基準として考える必要がある
で、以下のように対策します。
図ー1模擬入力波形(カウントエラー対策未実施)

上のグラフから、速度波形が処理遅れによってカウントミスしていることがわかります。基準に対して、異常値を判断する必要があるため、タイマーカウンタ割込みによる速度を基準値として使用します。
タイマーカウンタ割込みによる基準速度波形は下のようになります。
図ー2基準波形

タイマーカウンタ割込みによる速度と比較した波形(ccp割込み速度/タイマーカウンタ割込みによる速度=1.21〜0.845の範囲となる)は下のようになります。(60KM付近の急峻な山はノイズと思われる)
波形を比較すると、タイマーカウンタ割込みによる速度は0.05S毎のカウントのため、一種のローパスフィルターのような働きをしており、大幅に変動することがないことがわかります
図ー3波形比較

ノイズ対策、カウントエラー対策実施後の波形は以下のようになります。
図ー4対策実施波形

※40KM以下はタイマーオーバーフロー対策のため、タイマーカウンタ割込みによる速度を使用しているので階段状になってます。
図ー5 凹凸雪面走行波形

パルス抜けの対策方法



プログラムソース(ccs-c用)
mainルーティン設定はこのようにします。//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_main_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_
main()
{
setup_ccp1(CCP_CAPTURE_RE); //CCP1動作条件設定 立上りはRE 立下りはFE
cp1edge=0;//fall=1 rise=0
setup_ccp2(CCP_CAPTURE_RE);
setup_ccp3(CCP_CAPTURE_FE);
setup_ccp4(CCP_CAPTURE_FE); //CCP2動作条件設定 立下り
setup_ccp5(CCP_CAPTURE_RE); //CCP2動作条件設定 立下り
setup_timer_0(RTCC_INTERNAL | RTCC_DIV_8);
setup_timer_1(T1_INTERNAL | T1_DIV_BY_8);
setup_timer_3(T3_INTERNAL | T3_DIV_BY_8|T3_CCP1_TO_5);
set_timer1(3035);
enable_interrupts(INT_TIMER1); //タイマ1割込み許可
enable_interrupts(INT_TIMER0); //タイマ0割込み許可
enable_interrupts(INT_CCP1); //CCP2側のみ割込み許可
enable_interrupts(INT_CCP2); //CCP2側のみ割込み許可
enable_interrupts(INT_CCP3); //CCP2側のみ割込み許可
rpm_rise=1;
enable_interrupts(INT_CCP4); //CCP2側のみ割込み許可
enable_interrupts(INT_CCP5); //CCP2側のみ割込み許可
enable_interrupts(GLOBAL); //グローバル割込み許可
・
・
・
・
・
}
割込みにて計測しますのでソースはこのようになります。
//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_割り込みtimer1_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_
//0.05s毎に割込み
#int_timer1
void isr1()
{
set_timer1(3035);
counter ++;//回数カウンタ+1
//基準速度(低速におけるタイマーオーバーフロー対応):
if(counter>19)//1s
{
speed0= number2*0.707714083510262*2*(4/(float)e_pulse) ;
}
if(counter==10)//0.5s
{
speed0= number2*0.707714083510262*2*2*(4/(float)e_pulse) ;
}
//変数speedが表示する値となる
//22km以下ではパルスの周期がタイマーオーバーフローとなるため0.5s毎のパルスカウントを行い速度に変換
//22km以上ではパルスの立ち上がり〜立下りまでの時間より速度に変換する。
//パルス抜けは実験より70km以上から発生する場合があるため35km以上から判定処理を行う。判断には
//パルスの立ち上がりのみから算出した速度を使用する。
if(speed0<22)
{
speed=speed0;
}
else if(speed0>=22)
{
speed=vew_speed;
}
if (counter >19) //10回目か?
{
counter = 0; //回数カウンタクリア
number2=0;
}
}
//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_割り込みccp2/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_
#int_ccp2
void ccp2_isr()
{
lowspeed_rise=CCP_2;
if (lowspeed_rise >lowspeed_fall)
lowspeed_pulse = (lowspeed_rise - lowspeed_fall); //パルス幅=時間差
else
lowspeed_pulse =(0xffff - lowspeed_fall) + lowspeed_rise;
lowspeed_dpulse=(float)lowspeed_pulse;
dpulse3=(float)lowspeed_pulse;
vew_speed=(1000000/(dpulse3*0.025*4*8))*0.0003925*60*60*(4/(float)e_pulse)*0.5;
if(fall_f==0)//fall missing
{
number2++;
speederr=speederr+1;
if(speed0>0)
}
//---------------------------停止時のノイズカウントアップ防止:
if(speed0==0)
{
vew_speed=0;
}
//----------------------------------------------------------------------------
fall_f=0;
}
//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_割り込みccp3_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_
#int_ccp3 // HIGH
void ccp3_isr()
{
fall_f=1;//FALL開始フラグ//
lowspeed_fall=CCP_3;//ccp2割込み側にて速度計算に使用
number2++;//TIMER1による基準速度計算用//
//パルス立ち下がりのみの速度
sintrup_n1=sintrup_n2;
sintrup_n2=CCP_3;
if (sintrup_n2 > sintrup_n1)
pulse2 = (sintrup_n2 - sintrup_n1); //パルス幅=時間差
else
pulse2 =(0xffff - sintrup_n1) +sintrup_n2;
dpulse2=(float)pulse2;
//pic内部40MHzの場合0.025x4=0.1μでタイマー1は
// setup_timer_3(T3_INTERNAL | T3_DIV_BY_8|T3_CCP1_TO_5)のプリスケーラ8としていることから
//実時間は0.1x8となる
//よってパルス立下りのみより算出した(波形反転しているので実際は波形の立ち上がり)基準速度は以下のようになる
kijyun_speed=(1000000/(dpulse2*0.025*4*8))*0.0003925*60*60*(4/(float)e_pulse);
//speed0はタイマー1にて0.5s毎定期的に計算する。
speed_hantei=kijyun_speed/speed0;//走行テストでは1.21〜0.845の範囲が正常//
//35km以上より異常処理を行う
//NOIZE 0.5秒でspped0カウントしているので0.5sで30%は異常とする//
//kijyun_speed/2はパルス立下りのみより算出しているため
if(speed0>35&&speed_hantei<3)
{
//--------------------------ノイズ・パルス抜け処理
if((kijyun_speed/2)/dstart_speed>0.9&&(kijyun_speed/2)/dstart_speed<1.1)
{
if(speed_hantei>1.3)//基準30%以上のためエラー処理
{
dpulse2= old_pulse;
speederr=speederr+1;
mul_f=2;
}
}
else if((kijyun_speed*2)/dstart_speed>0.9&&(kijyun_speed*2)/dstart_speed<1.1)
{
if(speed_hantei<0.7)
{
dpulse2= dpulse2/2;
speederr=speederr+1;
mul_f=1;
}
}
else
{
dpulse2= dpulse2;
old_pulse=dpulse2;
mul_f=0;
}
//------------------------------------異常値比較に使用する速度の算出
dstart_speed=(1000000/(dpulse2*0.025*4*8))*0.0003925*60*60*(4/(float)e_pulse);
}
else
{
dstart_speed=speed0;
kijyun_speed=35;
mul_f=0;
}
//初期計算によるエラー防止フラグ
if(dstart_speed>0)
fast_read=1;
//---------------------------停止時のノイズカウントアップ防止:
if(speed0==0)
{
dstart_speed=0;
}
//--------------------------走行距離の計算
if(dstart_speed>0&&mul_f<2)
{
odo_m=odo_m+(0.0003925*(4/(float)e_pulse))*odo_hosei;
nenpi_odo=nenpi_odo+(0.0003925*(4/(float)e_pulse))*odo_hosei;//進んだ距離km
odo04=odo04+(0.3925*(4/(float)e_pulse));//進んだ距離km
if(mul_f==1)
{
odo_m=odo_m+(0.0003925*(4/(float)e_pulse))*odo_hosei*(float)(mul_f);
nenpi_odo=nenpi_odo+(0.0003925*(4/(float)e_pulse))*odo_hosei*(float)(mul_f);
odo04=odo04+(0.3925*(4/(float)e_pulse))*(float)(mul_f);//進んだ距離km
mul_f=0;
}
}
}
以上の手法は超低速での計算が難しく、鬼門となるところではないでしょうか。次に説明する手法はタイマー0を使用して超低速のみ計測する手法です。
まず、タイマー0を5ms毎に割込みさせます。(10msでは精度がでません)
※内部40MHz動作の場合です
変数は
float lowspeed;
long spd_pcount;
と設定します。
またmain()で0に初期化します
lowspeed=0;
spd_count=0;
#int_timer0
void isr0()
{
set_timer0(59285);//5ms
rpm_pcount++;
spd_pcount++;
if(number2!=0)
{
counter04 ++;
}
}
スピードパルスの間隔をタイマー0割込みによる5ms毎のカウンター値より計算します。分解能は5msですが、超低速1kmまで計測が可能になります。
#int_ccp2
void ccp2_isr()
{
・
・
・
//※22km以上はタイマー0カウント数が少なくなるので不正確になります。
if(spd_pcount==0)
spd_pcount=1;//0除算エラー防止 最大計測速度191km
lowspeed=(((0.3925*60/(spd_pcount*0.005))*60)/1000)*0.5;
//0.005s=5ms
//0.5はパルスの山と谷があるため半分にする。÷2より掛け算のほうが実行速度が速いため。
・
・
・
}
#int_ccp3
void ccp3_isr()
{
・
・
・
・
spd_pcount=0;
・
・
・
}
注意点として、1km以下の速度ではカウンタspd_pcountがオーバーフローしてしまうので、400カウントで0に初期化します。
#int_timer1
void isr1()
{
・
・
・
・
if(spd_pcount>400)
{
lowspeed=0;
spd_pcount=0;
}
cstart_speed=lowspeed;//speed0;
s_start_speed=lowspeed;//speed0;
start_speed=lowspeed;//speed0;
end_speed=lowspeed;//speed0;
else if(speed0>=22)
{
cstart_speed=vew_speed;
s_start_speed=vew_speed;
start_speed=vew_speed;
end_speed=vew_speed;
}
・
・
・
・
make32を使用してタイマーオーバーフロー分を入れて速度1パルス分の時間計算もできるのですが、内部処理による時間遅れ等により結構速度がバラツキます。見た目はこちらの手法が安定して表示できます。
ノイズを注入してみる
ノイズを注入してみると誤計測しますのでソフトで処理してみます。下の写真がノイズを注入した波形です。
単発よりも複数入れて検証してみました。波形の谷に2発ほど高周波?の波形を入れてみました。

高周波のノイズが入るパターンを仮に定義すると
・極たまに1発入ってくる
・連続して周期的に入ってくる
アマチュア的考えですので、こんなもんかと対策してみました。
極たまに入ってくる1発もしくは連続高周波ノイズの除去(周期が低いとカットされません。)は以下のソースで問題ないみたいです。
※前述の変数speed0を判断に使用するとノイズの度に割込みカウントされるため、変数vew_speedがオーバーフローする場合があり 正確に判断ができません。
#int_timer1
void isr1()
{
・
・
・
・
if(AN_H7<=1&&spd_filter==1)////パーキング入ると0//
{
speed0=0;
lowspeed=0;
}
if(spd_timecount>=200)
{
lowspeed=0;
spd_pcount=200;
spd_timecount=0;
}
cstart_speed=lowspeed;//speed0;
s_start_speed=lowspeed;//speed0;
start_speed=lowspeed;//speed0;
end_speed=lowspeed;//speed0;
old_speed0=lowspeed;
if(lowspeed>=25)
{
//高周波ノイズフィルタ//
if(vew_speed<300&&vew_speed>=0)
{
cstart_speed=vew_speed;
s_start_speed=vew_speed;
start_speed=vew_speed;
end_speed=vew_speed;
old_speed=vew_speed;
// end_speed=end_speed*(4/e_pulse);
}
else
{
cstart_speed=old_speed;
s_start_speed=old_speed;
start_speed=old_speed;
end_speed=old_speed;
}
}
・
・
・
・