TOP工作

TT250R用 デジタルタコメーターの製作 (4.ソフト製作(試作1号完成))
■PICの選定



このタコメータ、最終的には回転数の数値表示とバーグラフ表示を考えている。

・回転数表示
7セグメントデコーダを使うとデータの出力に4bit、ダイナミック点灯制御で1桁あたり1bitなので、4桁表示だと4+4=8bit必要。

・バーグラフ表示
500rpmで1セグメントとすると、MAX9000rpmで18桁?

それにパルス入力の1bitを加えると、単純計算でI/Oは8+18+1=27必要になる。となると40pinのでかいPICが必要になるけど、バイクに乗せるにはちょいとでかい気がする・・。
バーグラフ表示の方法は少し検討することにして、とりあえず28pinの16F886で進めてみることにした。
(まぁ、手持ちの中から適当な大きさでかつ安い石をテキトーに選んだだけでもある、、)


さて。正直この製作で自分にとって一番の難関はソフトウェア。
どうやってパルスを拾って回転数に変換するかはぼんやりとしか頭に浮かんでこないけど、とりあえず色々テストプログラムを組みながら考えて行く。

開発環境はmikroElektronikaという会社の出しているmikroBASICを使ってみる。(Ver.7.0.0.2)

PICにSWとLCDを下記のように接続した回路をブレッドボードで組む。SWは外部割込みピンに接続。

(クリックで拡大)


測定したい対象信号が最大でも250Hz@15000rpmと比較的低速(周期4msec)なので、20MHz動作のPICなら下記のような方法でパルスの間隔を測れないだろうか?

INTピン変化割り込み発生

タイマ1カウント開始。

再びINTピン変化割り込み発生

タイマ1カウント値の確認

これで、タイマ1の1カウントあたりの時間がわかっていれば、パルスの周期を求めることができるのでは無いかと考えてみた。とりあえず、INTピン変化割り込みの方法、タイマ0の使い方とも良く判らないのでそれぞれで練習してみることにする。


■INT割り込みってどうやる?



PIC16F886の外部割込みピンは21番ピン(RB0と共用)。
割り込みの許可とINT割り込みの設定はINTCONレジスタで設定する。
mikroBASICでは、割り込み処理は

sub procedure interrupt
 動作命令
end sub


というプロシージャを作って記述するようだ。

で、とりあえずLCDの表示が薄くなる(高速で何度も書き換えるのでチラついているのだろう)等問題は有るもの、とりあえずスイッチを押すごとに数字をカウントアップするソフトは書けた。

正直公開するのは恥ずかしいけど、ソースは下記の通り。なお、全角文字(スペース含む)が含まれているので、コピペ時(する人居るのか??)は注意。

'**********************************************
'Tachometer test program (2009-08-30) by Magokoro
'This program count pulse of INT pin and show it on LCD.
'**********************************************

program Tachometer

↓変数の宣言
dim text as char[16]
dim PUSH as byte

↓割り込み時処理のサブルーチン。INTピンの立上りエッジで割り込み、変数PUSHを+1する。
sub procedure interrupt
  INTCON = %00010000 'Interrupt is disable
  PUSH = PUSH + 1
  INTCON = %10010000 'Interrupt is enable
end sub

main:

↓BポートのA/DコンバータをOFFにする。(デフォルトではI/Oとして使えない)
  ANSEL = 0 'A/D Converter disable
  ANSELH = 0 'A/D Converter disable

↓LCDのイニシャライズ
  Lcd_Config(PORTC,7,6,5,4,PORTB,5,6,7) '(data_port,DB7,DB6,DB5,DB4,ctrl_port,RS,RW,E)
  Lcd_Cmd(Lcd_CURSOR_OFF) ' Turn off cursor

↓INT割り込み(外部割込み)の許可
  INTCON = %10010000 'INT pin interrupt enable
  OPTION_REG = %11111111 'Interrupt on rising edge of INT pin(Default)

↓LCDの1行目にPUSH TIMES;を表示
  PUSH = 0
  Lcd_Out(1, 1, "PUSH TIMES:")

↓無限ループでLCDに変数PUSHの値を表示し続け、割り込み処理を待つ。
LCD制御の関数実行中に割り込みがかかるとLCDの表示がおかしくなるので、その間は割り込み禁止にしている。

  while TRUE 'Endless loop
    INTCON.7 = 0
    ByteToStr(PUSH, text)
    Lcd_out(2, 1, text)
    INTCON.7 = 1
    delay_ms(10)
  wend
end.
'*************************************************
' END OF PROGRAM
'*************************************************



左の黄色いプッシュSWを押すたびに数字がカウントアップされる。
(チャタリング対策をしていないので時々2〜3カウントされたりする。)


■TIMER1で時間をカウント



とりあえずINTピン入力立上りによる割り込みの方法はなんとなくわかったので、その間隔をタイマ1でカウントしてみる事にする。タイマ1の動作方法はT1CONレジスタで設定する。

基準クロックに動作クロック(20MHz)を使用し、プリスケーラ(何クロックで1カウントするか決める)を1:8とすると、1カウントあたりの時間は次で求められる。
  
T = 1 /( ((20*10^6) / 4) / 8 )
 =1.6μsec
(これで÷4が入っているのは、基準クロック=PICの動作クロック÷4という仕様なため。)

タイマ1は16ビットカウンタなので、カウント値は0〜65535。
 ・1000rpm = 16.7Hz = 周期60000μsec = 37500カウント
 ・15000rpm = 250.0Hz = 周期4000μsec = 2500カウント
と、とりあえず目標とする周波数に対して充分な余裕がある。

というわけで、カウント値から周波数を逆算してLCDに表示するプログラムを書いたのが下記。


'**********************************************
'Tachometer test program (2009-08-31) by Magokoro
'This program measure frequency of pulse
'for INT pin and show it on LCD.
'**********************************************
program Tachometer

dim text as char[16] 'LCD Output text
↓タイマ1は16ビットカウンタなのでword型の変数で問題ないが、その後の演算で65535(16bit)を超えてしまうのであらかじめlongwordで定義する。
dim TMR1VALUE as longword 'Timer1 count value (32bit)
dim CONVERT as word
dim INPUT_FLAG as byte

↓割り込み処理
sub procedure interrupt
 INTCON.7 = 0 'Interrupt is disable

 if INPUT_FLAG = 0
 then
  T1CON = %00110001 'Timer1 start (prescaler 1:8)
  INPUT_FLAG = 1

 else
  T1CON = %00000000 'Timer1 stop
↓16bitカウンタの上位8bitを読み込み、ビットを8ビットシフトした後下位8bitを読み込む。
  TMR1VALUE = TMR1H
  TMR1VALUE = TMR1VALUE << 8
  TMR1VALUE = TMR1VALUE + TMR1L

  WordToStr(TMR1VALUE, text)
  Lcd_out(1, 1, text)

↓本当は周波数=1/周期Tなので、単純にカウント値×1.6μsecの逆数を取ればよい。しかしプログラム上で小数点のある値を取り扱いたくなかったので、あらかじめ欲しい有効桁数まで10のn乗を掛け、後でそれを割る方法にした。邪道かなぁ。
  TMR1VALUE = TMR1VALUE * 16
  TMR1VALUE = 100000000 / TMR1VALUE

↓このあたりはデバック用の不要コード
  CONVERT = TMR1VALUE
  WordToStr(CONVERT, text)
  Lcd_out(2, 1, text)

  TMR1VALUE = TMR1VALUE * 6

  CONVERT = TMR1VALUE
  WordToStr(CONVERT, text)
  Lcd_out(2, 8, text)

  INPUT_FLAG = 0
  TMR1H = 0
  TMR1L = 0
 end if

 INTCON.1 = 0 'INT Interrupt flag clear
 INTCON.7 = 0 'Interrupt is enable
end sub

main:
 ANSEL = 0 'A/D Converter disable
 ANSELH = 0 'A/D Converter disable
 INPUT_FLAG = 0
 Lcd_Config(PORTC,7,6,5,4,PORTB,5,6,7) '(data_port,DB7,DB6,DB5,DB4,ctrl_port,RS,RW,E)

 TRISB.0 = 1 'PORTB.0 input
 OPTION_REG = %11111111 'Interrupt on rising edge of INT pin.
 PIE1 = %00000000 'Timer1 Interrupt Disable
 INTCON = 0 'Resister clear
 INTCON.4 = 1 'INT pin interrupt enable
 INTCON.7 = 1 'Interrupt enable

↓無限ループ。ここで割り込みがかかるのを待つ。
 While TRUE
 wend

end.
'*************************************************
' END OF PROGRAM
'*************************************************


回路図は下記。(クリックで拡大)


この回路図中のC3(7414とPICのINT端子間に入っているパスコン)はプリント基板では不要かもしれない。

実は最初、どうにも正常に作動しない(およそ2倍の値が表示される)ので色々調べると、パルスの立ち上がりでしか割り込みがかからないはずなのに立上り/立下り両方で割り込みがかかっていることが判明。ブレッドボードで実装している関係で、リンギングが起きてそれを検出してしまったようで、パスコンを1つ追加して波形を鈍らせたらあっさり解決。およそ1週間をこれでつぶした。
知っている人にとっては当たり前なのだろうけど、やっぱりブレッドボードはこういうところを気をつけないといけないみたいだ。

で、動作確認。(Youtubeにアップ。いつまで残っているのかは不明)
http://www.youtube.com/watch?v=Zc_FvgiHPy8
とりあえず、回転数をLCDに表示するところまでは完成した模様。


■プログラムの修正



現状のプログラムは割り込みがかかる度にLCDの数値を書き直しているのだが、
 ・回転数に応じて表示の更新間隔が短くなる
 ・ちらつく(当然・・・・)
という問題があるので、割り込み処理はタイマのカウント値取得のみに書き換え、LCDの表示をmainへ移動させた。あとはLCDに表示している数字を7セグLEDに置き換え、バーグラフをつければ当初の目的は達成な感じ。

今のWebサーバの都合上、TXT、ZIP、PDF等は置けないので、ソースファイルは下にドバッと貼り付けておく。
もしEAGLEデータ、mikroBASICソースファイル等そのものが欲しい人はメールででも連絡してもらえれば対応します。(いつまでPCに残っているかは判らないけど。。。)

<3.波形整形  5.LED表示への変更>


■資料


LCD表示版のソースファイルは下記。
貼り付け時に半角、タブ等全て無視されていているため、読みづらいのはご勘弁を。
'**********************************************
'Tachometer test program (2009-09-17) by Magokoro
'This program measure frequency of pulse
'for INT pin and show it on LCD. Ver.0.2
'**********************************************
program Tachometer1

dim text as char[20] 'LCD Output text
dim TMR1VALUE as word 'Timer1 count value (16bit)
dim BUFFER as longword 'For culcuration (32bit)
dim CONVERT as word
dim INPUT_FLAG as byte

'***********************************************
' INTERRUPT PROCEDURE
'***********************************************
sub procedure interrupt
INTCON.7 = 0 'Interrupt is disable

if INPUT_FLAG = 0
then
T1CON = %00110001 'Timer1 start (prescaler 1:8)
INPUT_FLAG = 1

else
T1CON = %00000000 'Timer1 stop
TMR1VALUE = TMR1H
TMR1VALUE = TMR1VALUE << 8
TMR1VALUE = TMR1VALUE + TMR1L
BUFFER = TMR1VALUE

INPUT_FLAG = 0
TMR1H = 0
TMR1L = 0
end if

INTCON.1 = 0 'INT Interrupt flag clear
INTCON.7 = 1 'Interrupt is enable
end sub

'***********************************************
' MAIN
'***********************************************
main:
ANSEL = 0 'A/D Converter disable
ANSELH = 0 'A/D Converter disable
INPUT_FLAG = 0
Lcd_Config(PORTC,7,6,5,4,PORTB,5,6,7) '(data_port,DB7,DB6,DB5,DB4,ctrl_port,RS,RW,E)

OPTION_REG = %11111111 'Interrupt on rising edge of INT pin.
PIE1 = %00000000 'Timer1 Interrupt Disable
INTCON.4 = 1 'INT pin interrupt enable
INTCON.7 = 1 'Interrupt enable

While TRUE 'Endless loop
BUFFER = BUFFER * 16
BUFFER = 100000000 / BUFFER
BUFFER = BUFFER * 6
CONVERT = BUFFER 'Type convert: word <- longword

'When 1500rpm F=(1500/60)=25Hz
'T=(1/F)=(1/25)=0.04sec=40000usec
'1Count=1.6usec Then (40000/1.6)=25000Count

'Example at 1500rpm(25Hz) as below:
'25000*16 = 400000
'100000000/400000 = 250 (Therefore count unit will be 0.1Hz)
'250*6 = 1500(rpm)

'<NOTE> longword type can operate 0 to 4294967295(32bit)

WordToStr(CONVERT, text)

INTCON.7 = 0 'Interrupt is disable
Lcd_out(1, 1, text)
INTCON.7 = 1 'Interrupt is enable

delay_ms(500)
wend
end.
'***********************************************
' END OF PROGRAM
'***********************************************


おことわり


※1 記事を書きながら製作・勉強しています。誤っている箇所やアドバイスありましたら教えてください。質問も分かる範囲でお答えします。一緒に勉強しましょう。

TOP工作


SEO [PR] 爆速!無料ブログ 無料ホームページ開設 無料ライブ放送