アプリ版:「スタンプのみでお礼する」機能のリリースについて

PIC16F84Aでスピードメーターを作ろうと思います。

条件は
・タイヤ1回転で4回のパルス。
・1パルスあたり50cm(タイヤ外周2m)

測定方法は
・各パルスの間隔をタイマーで計測
・タイマーは0.009秒のタイマー(Aと名付ける)

・パルスを検出するとタイマーAを繰り返し、Wレジスタに1ずつ加算。
・次にパルスを検出したらWレジスタの値(Aを繰り返した回数)に応じて速度をダイナミック点灯表示させるプログラムを呼び出す。

速度の表示方法(ダイナミック点灯)
・パルス回数>200なら時速0kmと表示
・パルス回数=200なら時速1km、パルス回数=199・・時速2km、パルス回数=1なら時速200kmと表示
各パルスの間隔をAを何回繰り返したかによって測定し、それに応じて7セグに3桁表示するものとする。

接続
・RB0~RB6・・・7セグLED
・RA0~RA2・・・7セグLED(カソード)

Wレジスタの値に応じて速度を表示するプログラムを呼び出すまではプログラム書けそうなのですが、
「ダイナミック点灯のプログラムの実行」と「パルスを検出し、タイマーAの回数をカウント」という2つの動作を同時にさせなければなりませんよね?
16F84Aでこのようなことは可能なのでしょうか?
だとしたらどのような考え方でプログラミングすれば良いでしょうか?ど素人なので分かりやすい説明をお願いします。

A 回答 (10件)

> ダイレクトカウントだと加速時に表示が0.1.2.3.4.5と連続せずに0.3.5.6.8.みたいな感じになるのでは?


このイメージがよくわかりませんが、このようには、ならないと思います。
なめらかな表示かどうかは、値の更新速度の問題だと思います。

よくイメージがつかめないようですが、具体的な例が必要ですか ? 次に例を示します。
ダイレクト・カウントの方式での工夫を、前回の回答の 4. のようにするとします。つまり、0.1 秒ごとにカウントして、1.8 秒の加算を表示するとします。

1. 自転車は、最初、止まっているものとします。
2. それから動き始めて、パルスが入ったとします。これを、時間の原点とします (t = 0)
3. 1.8 秒後に、さらにもうひとつパルスが入ったとします (t = 1.8)
4. 0.9 秒後に、次のパルスが入ったとします (t = 2.7)

ダイレクト・カウントのとき、最初は 0 で、t = 0 - 0.1 の間で、表示が 1 になります。t = 2.7 - 2.8 の間で、表示が 2 になります。
レシプロカル・カウントのとき、最初は 0 で、t = 1.8 で、表示が 1 になります。t = 2.7 で、表示が 2 になります。

レシプロカル・カウントの利点は、低速のとき、パルスの 1 周期分を測定するだけで、分解能を上げることができるということです。
たとえば、現在は 1 km/h の表示分解能だと思いますが、仮にこれを 1000 倍にしようとする (たとえば 1.001 km/h とか) と、タイムベースが速く、時間のカウンタのビット数が十分大きく、計算精度が確保できるなら、パルスの 1 周期で実現できます (ダイレクト・カウントでは、1000 倍の分解能を得ようとすれば、1000 倍の時間が必要となります)
しかし、最低、パルスの 1 周期を測ることが必要です。あたりまえですが、次のパルスが入ってこないうちに、結果を出すことはできません。
この結果、止まっているときはいつまでたっても次のパルスがこなく、値の更新もできないままタイマがオーバーフローしますので、何らかの回避策は必要でしょう。
また、速度が速いとき、つまり、パルスの 1 周期が短いときは、十分な分解能を得ることは困難です (速度が速いところでは、とびとびの値しか表示できない可能性があるということです)

表示が現在のように固定小数点の方式なら、ダイレクト・カウントでは、低速から高速まで、表示の分解能とデータの分解能の関係は同じになると思います。
ただし、レシプロカル・カウントの方式で、高速域で必要な分解能が得られるまでタイムベースを速くして、低速域でもオーバーフローしないようにカウンタのビット数を大きくして、計算を行えば、可能かもしれません。これができれば、18 km/h で、パルス間隔は 0.1 秒となるので、これぐらいよりも速度の速い領域では、表示値の応答が十分速くなるでしょう。もっとも、ちょっとたいへんかなという気もします。
    • good
    • 0

> 例えば時速1キロで走行する場合はパルス同士の間隔が1.8秒になりますので1.8秒間隔でパルスの数を数えれば速度を計測できる


そうですね。これは、スケールファクタを適切に選ばれた例ですね。得られたデータがそのまま表示するデータになりますので、プログラムが簡単になると思います。

> これでは速度の計測結果の更新に1.8秒かかる、つまり1.8秒単位で速度の表示が切り替わるということでしょうか
この場合は、そうなりますね。50 cm ごとにパルスが入って 1 km/h の分解能を得るという条件で、得られたデータをそのまま表示するのであれば、そうなります。

> 速度の変化にリニアに反応するようにしたい
この意味がちょっとわかりません。得られるデータは速度のデータなので、実際の速度と得られるデータの関係は、いつでも線形 (リニア) だと思うのですが ?

> 例えば0.1秒ごとに更新なんて事も出来るのでしょうか
工夫次第だと思います。
表示内容の更新間隔としては、0.1 秒ほど速くなくても十分ではないかという気がします。いずれにしろ、もっと速い更新のために、事情がわからないところもありますので参考になるかどうか、思いつくままに、例として、書いてみます。

1. 表示分解能を落とす。偶数値しか表示しないとか。たぶんこれはあまり気に入らないでしょうが、可能性として、あげておきます。
2. 一回転につき、多くのパルスが (等間隔で) 得られるようにする。根本的な解決方法ですが、事情によっては、難しいかもしれません。
3. 0.2 秒ごとにデータを受け取って、データとデータの間は計算で補間して、0.1 秒ごとに値を更新する。
4. 0.1 秒ごとにデータを取り込んで、最近の 18 回のデータを加算して、表示する。これだと、0.1 秒ごとに値を更新できます。ただし、表示する速度は、最近の 1.8 秒間の平均速度になります。これは、ちょっと長すぎるような気がしますので、ほかの方法とあわせて考えたほうがよいかもしれません。

たとえば、速度がステップ状に、突然変わったときに、どれほど速く追随して表示するかですが、0.1 秒で、最終値の表示に達する必要はないと思います。表示内容の更新速度とは、別に考えたほうがよいかもしれません。次のような理由です。
・ 人間の理解できる速度は、それほど速くないと思います (具体的な数値はわかりません)
・ 自転車の、通常の加速度の限界があると思います。どのくらいか、具体的な数値はわかりません。
・ 速度が A -> B へ、ある程度の時間で変わったとき、A と B の表示は正確でないといけないと思いますが、目で見る目的であれば、その間の表示はそれほど正確でなくてもよいのではないでしょうか。それまで一定であった A の速度が変わったときに、表示がそれなりに速く反応すれば、十分かもしれません。

上記のいくつかを組み合わせるとか、そのほかの方法を考えるとか、工夫が必要なところだと思います。

追記ですが、前回の回答の「呼び出す側のルーチンと変換するルーチンが、同じ 256 バイトの境界内におさまっていて ...」は、256 ワード、「アドレスは 256 バイトの境界をまたがなければどこでもかまいません」も、256 ワードのまちがいです。訂正いたします。プログラムメモリの 256 ワード分です。PC (プログラムカウンタ) の 8 ビットめ以上が変わらない範囲、という意味です。

この回答への補足

ありがとうございます。

> 速度の変化にリニアに反応するようにしたい
この意味がちょっとわかりません。得られるデータは速度のデータなので、実際の速度と得られるデータの関係は、いつでも線形 (リニア) だと思うのですが ?

ダイレクトカウントだと時速1kmの場合パルス間隔が1.8秒ですよね?
すると最新の計測結果が分かるのが1.8秒毎ということになります。
1.8秒毎に最新の計測結果が出るということは例えば自動車が1秒毎に1kmづつ加速すると、
スタートして1.8秒間はパルスが来ないので表示は「0」となりますよね?1.8秒経過してパルスが1回入ると時速1キロとなりますがこのときは自動車は時速1.8kmに達しています。
ほんのわずかな遅れですが、出来ればこの遅れが少ないほうが良いかな?と思ったのです。
おそらくダイレクトカウントだと加速時に表示が0.1.2.3.4.5と連続せずに0.3.5.6.8.みたいな感じになるのでは?と心配をしているのです。
正直言って実際に作って使用してみないとそれが自分にとって納得できる範囲かどうかが分からないのです。
ちょっと表現方法が悪かったですね。すみません。

パルス毎に速度を計測するレシプロカルの方がよりきめ細かい(なめらかな)表示が出来るのかな?と思ったのです。
もしそうならレシプロカルカウントで実現させたいです。
よろしくお願いします。

補足日時:2006/01/15 16:38
    • good
    • 0

フラグの説明は、わかりにくかったしょうか。

だいたいのイメージはつかんでいただけると思ったのですが。すみません。詳しく説明します。

これまでの経緯に沿って、具体的に郵便屋さんにあてはめると、パルスの割り込みが郵便屋さんです。
フラグとは、16F84A の例でいえば、STATUS レジスタの Z, DC, C のようなもので、INTCON レジスタの T0IF, INTF, RBIF のようなものです。通常、0 か 1 (または 0 以外) の論理値です。まさに「合図をする」ということです。
パルスの割り込みで使うフラグとデータの領域を、あらかじめメモリのどこかに作っておきます。
パルスの割り込みが入ると、そのときの時間データを、作っておいたデータの領域にコピーして、作っておいたフラグを 1 にします。これで、処理すべきデータがあるということを、家の中の人に通知するわけです。
メインルーチンが、家の中にいる人のことです。フラグを見て、処理すべきデータがあることを知れば、そのデータを取ってきて、表示すべきデータに変換します。

メインルーチンでは、3 回、変換、または計算を行わなければならないと思います。
1. パルス割り込みから渡された「時間 / 距離」のデータを「距離 / 時間」の、スケーリングを含めた、表示すべき速度の値になるように、計算、または、変換します。これは割り算か、テーブル参照の変換になるの
ではないでしょうか。
2. 速度の値を、バイナリから 3 桁の BCD に変換します。
各桁は、0 - 9 の間の値でなければなりません。一桁は、1 バイトでなくてもかまいませんが、一桁を 1 バイトに割り当てておいたほうが、混乱が少ないでしょう。
3. BCD の各桁はそのまま表示できませんので、これを、表示できる、セグメント用のパターンのデータに変換します。

上記の 2 と 3 は、いちどに行ってもかまいませんが、わかりやすいように、ここでは分けておきます。
> http://www25.cds.ne.jp/~kamiken/electro/sakuhin/ …
にあるような LS247 や、HC4511 は、上記の 3 の変換を、ハードウェアで行っているということです。
> これらは2進数の信号を10進数の7セグ表示に変換してくれる物ですよね
そのとおりです。ハードウェアで行う利点は、使う I/O ポートの数を少しだけ少なくでき、処理も高速にできます。しかし、コストも基板面積も大きくなります。今回は、I/O ポートが足りないわけではなく、ソフトウェアで十分、処理できるので、ソフトウェアで行うほうがよいと思います。
> http://www.fureai-ch.ne.jp/~tsumura/pdfFile/7seg …
については、セグメント用のデータのデコードが含まれているわけではなく (8 桁と多くなるので、桁のデコードは含まれているようです) 表示機能のみですので、あまり今回のものには向いていないでしょう。
また、上記のふたつとも、ダイナミック点灯をさせるには、ソフトウェアが必要なようです。

> セグメントデータに変換するのはどのようにプログラムするのでしょうか
上記 3 の変換は、テーブル参照の変換で行えばよいと思います。
テーブル変換がわかりますか ? 具体的な例が必要ですか ? それでは、次に簡単な例を示します。
前提条件として、BCD の 3 桁のデータが bcd_data[3] にあるとします。これを処理し、変換後のデータを disp_data[3] に入れるとします。また、作業用の一時的な変数として、temp_data, loop_count, save_pch などを使うものとします。
セグメントのデータは、a が bit 0, b が bit 1, c が bit 2 ... などとします。また、負論理で定義するものとします (0 で点灯、1 で消灯)
以下に、Microchip のアセンブラの例で示しますが、アセンブルして、正しく動作するとは限りません。
まちがいが含まれているかもしれませんが、参考にしてください。
なお、以下のコードで、呼び出す側のルーチンと変換するルーチンが、同じ 256 バイトの境界内におさまっていて、PCLATH がそのエリアにすでに設定されていれば、PCLATH を設定する必要はありません。
(呼び出す側のルーチン)
 ...   (ここまでにいろいろあるとします)

_DATA  UDATA     ; 必要ならアドレスを指定します
temp_data  res 1
loop_count res 1
save_pch  res 1
bcd_data  res 3      ; それぞれの桁のデータ、0 - 9 の間の範囲のデータ
disp_data  res 3      ; 出力するセグメントのパターン

_TEXT CODE
 ...    (ここまでに、データのバンク、PCLATH などは、適切に設定されているとします)
 movf  PCLATH, W    ; たぶん 0 になっているはず
 movwf  save_pch     ; 現在の値を保存
 movlw  HIGH seg_tbl
 movwf  PCLATH
 movlw  3
 movwf  loop_count    ; 3 桁分の変換をする
conv_loop:
  movlw  bcd_data - 1
  addwf  loop_count, W
  movwf  FSR
  movf  INDF, W
  call   seg_tbl
  movwf  temp_data    ; セグメントの点灯データを一時的に保存
  movlw  disp_data - 1
  addwf  loop_count, W
  movwf  FSR
  movf  temp_data, W
  movwf  INDF
  decfsz  loop_count, f
  goto  conv_loop
 movf  save_pch, W
 movwf  PCLATH       ; PCLATH の値を戻す、たぶん 0
 ...

(変換するルーチン、W レジスタにデータを入れてコールすれば、W レジスタに変換後のデータを返します)
SEG_TABLE CODE h'300'  ; アドレスは 256 バイトの境界をまたがなければどこでもかまいません
seg_tbl:
 addwf  PCL, f
 retlw  h'40'       ; 0
 retlw  h'79'       ; 1
 retlw  h'24'       ; 2
 retlw  h'30'       ; 3
 ...   (以降、9 まで列挙します)

ひとつの例としては、こんな感じです (上記の例には、全角の空白が含まれています) あとは、タイマの割り込みルーチン内で、I/O ポートの PORTA に出力した桁に対応する disp_data を、そのまま PORTB に出力します。
ですから
> 計測結果に合わせて200通りのセグメントデータに変換する
ことではありません。10 種類の数字を、10 通りのセグメントデータに変換するということです。

> n=1なら「199」を表示するダイナミック点灯のルーチンへ
> n=2なら「198」を表示するダイナミック点灯のルーチンへ
というのは、上記の 1 の変換そのものだと思います。
ただし、実現する方法として、ひとつづつ比較して条件分岐するという方法は、無理だと思います。
理由は、いちいち条件分岐のコードを書いていられない (プログラムが無意味に複雑になるため) というのと、だいいち、メモリにおさまらないでしょう。納得できませんか ? それでは、例を示します。
PIC はアセンブラの 1 命令が 1 ワードです。たとえば、ひとつの値を比較するためのサンプルは、次のようになるでしょう。

movlw  1       ; たとえば n = 1 を比較する
subwf  (n のデータ), W
btfss  STATUS, Z
goto  (次の比較へ)
call   (199 のルーチンへ)
goto  (この一連の比較が終わったところへ)
(ここから次の比較)

さらに飛んでいったルーチン内で、なにもしなくて、すぐリターンしただけでも、この一回の比較で 7 ワード必要です。これを 200 回繰り返すと 1400 ワード必要です。実際には、リターンするだけのルーチンは役に立たないので、さらに必要でしょう。ところが、16F84A は、1k ワードしかありません。

したがって、1 の変換の、得られたデータから速度への変換は、計算によるか、あらかじめ計算しておいてテーブルにするかだと思います。また、ひとつづつ比較するというのは、テーブルによる変換の形に近いと思いとます。
(ちなみに、n = 1 のとき 199 だとするなら、n = 2 なら、求める数値は 100 か 99 あたりになると思います)

ここまで読んで、たいへんなことだとお思いですか ? 実際には、そんなことはありません。ひとつづつ考えていけば、なんとかなります。「分割して統治せよ」という言葉もあります。

ところで、レシプロカル・カウンタの方式は、望ましくないと思うようになってきました。
理由は、表示が 3 桁で、小数点の表示も使わないことから、速度の遅いところで、分解能が必要ではないというこです。速度の速いところでは、表示の分解能に合った計測の分解能が必要になってくるでしょう。
ダイレクト・カウンタの方式にすれば、得られるデータは、単位時間あたりの距離ということになります。つまり、速度です。計測の分解能も、表示の分解能に見合っています。スケールファクタを適切に選べば、1 の変換も必要なくなるかもしれません。
また、表示内容の更新速度を一定にできますし、なにより、ソフトウェアが簡単になると思います。
もういちど、考えてみてください。なお、ダイレクト・カウントにするときは、郵便屋さんもまた変わってきます。

この回答への補足

ありがとうございます。
フラグの解釈が違っていたみたいですね・・・。
なんとなくイメージは掴めたのですが・・・。
メインルーチンの方が「家の中の人」なんですね。

BCD変換にテーブル変換ですか・・・頭の中がぐちゃぐちゃですね。じっくり読み返して何とか理解できるように努力してみます。

それからダイレクトカウントですが、これは一定の時間に速度センサーのパルスが何回入ったかをカウントする方式ですよね?
例えば時速1キロで走行する場合はパルス同士の間隔が1.8秒になりますので1.8秒間隔でパルスの数を数えれば速度を計測できると思うのですが、これでは速度の計測結果の更新に1.8秒かかる、つまり1.8秒単位で速度の表示が切り替わるということでしょうか?
なるべく速度の変化にリニアに反応するようにしたいのですが、例えば0.1秒ごとに更新なんて事も出来るのでしょうか?

補足日時:2006/01/13 22:56
    • good
    • 0

> 「この表示内容更新要求のフラグを立てる」の意味がさっぱり分かりません


アメリカの映画か何かで、かまぼこが大きくなったような形の郵便受けを見たことがありますか。その郵便受けには、横に小さな旗のようなものがついています。
郵便屋さんが配達すると、その旗を立てていってくれます。
一方、家の中にいる人は、ときどき外を見て、もし郵便受けの旗が立っていれば、郵便物を取りにいきます。そのときに旗を元の位置に戻しておきます。旗が立っていなければ、外に郵便物を取りに行く必要はありません。
取ってきた郵便物は、家の中に持って入ってから、開封して、読み始めます。それから、泣くとか、笑うとか、返事を書くとか、いろいろします。
郵便屋さんは、個々の郵便物に対して、泣いたり笑ったりしません。ただ、郵便受けの中に入れて、旗を立てていくだけです。
こんな説明で、わかりますか ?

> n=1なら「199」を表示する ...
この処理は「前回からのパルスの時間を、表示するための数値に変換 」することそのものです。
> ... を表示するダイナミック点灯のルーチン
のなかで、「数値を、表示するためのセグメントデータに変換」するのではないかと思います。そうしなければ、表示できませんので。
つまり、同じことです。また、似たようなルーチンを 200 個も並べたら、メモリに入り切らないかもしれません。
ダイナミック点灯の場合は、他のルーチンから、プログラムで呼び出すことは、おすすめできません。理由は、一定の時間ごとに各桁を表示し続ける必要があるからです。3 桁一度に表示するわけではなく、入力パルスがあろうがなかろうが、表示する内容が変わろうが、同じであろうが、常に一定の時間で、桁を変えて、表示を更新し続けなければなりません。
もし、表示する対象がダイナミック点灯の LED ではなく、キャラクタ内蔵の LCD とか、スタティック点灯の LED であれば、インタバルタイマの割り込みを使うのではなく、メインルーチンから呼び出すだけで、十分だと思います。

追記ですが、レシプロカル・カウンタの方式であれば、スピードが極端に遅いときとか、止まっているときは、時間をカウントしている部分が、オーバーフローするおそれがあります。オーバーフローする前に、表示の内容を更新して、時間のカウントをクリアするほうがいいと思います。

この回答への補足

ありがとうございます。
フラグに関してですが、
教えて頂いた例では
郵便物が届き郵便受けの旗が上がるというのはTMR0のオーバーフローによる割り込みが発生し、計測結果を表示しなさいという合図をするという事で、家の人が手紙を郵便受けから取り出して読んだりする部分が表示部分のプログラムに当たるのでしょうか?

表示のプログラム部分ですが、
http://www25.cds.ne.jp/~kamiken/electro/sakuhin/ …
このHPにあるようなデコードIC(74HC4511など)を使用するのでしょうか?
それか
http://www.fureai-ch.ne.jp/~tsumura/pdfFile/7seg …
このようなキットを利用するとか・・・。
これらは2進数の信号を10進数の7セグ表示に変換してくれる物ですよね?
これで勝手にスタティック点灯(または勝手にダイナミック点灯)しててなおかつ次のデータ更新までその表示を維持してくれればプログラム部分の負担が大きく減らせそうなのですが・・・そんな便利なものは無いでしょうか?

便利なICなどを使ってプログラムの簡素化を少しでも図れるならそのほうが作りやすくなりそうですし。


>のなかで、「数値を、表示するためのセグメントデータに変換」するのではないかと思います。そうしなければ、表示できませんので。
つまり、同じことです。また、似たようなルーチンを 200 個も並べたら、メモリに入り切らないかもしれません。

計測結果に合わせて200通りのセグメントデータに変換するという事ですよね?
セグメントデータに変換するのはどのようにプログラムするのでしょうか?

補足日時:2006/01/09 21:11
    • good
    • 0

> この程度理解出来てればOKでしょうか?


おおむね OK だと思います。
ただし TMR0 は 8 ビットなので、255 までしかありません。そのあとは、オーバーフローして、0 に戻ります。ステップ数ということでいえば、0 も含みますので、256 です。また「カウント数はプリスケーラの ... 設定可能」とあるのは、そのうちの任意の値を設定できるわけでないことに注意してください。
F レジスタという名称はよくわかりませんが (ファイル・レジスタのこと ?) TMR0 レジスタにはちがいないので、たぶんそのとおりでしょう。
「クロック入力毎にTMR0のFレジスタの値に+1される」については、外部クロックを選択していて、プリスケーラを使っていなければ、そのとおりです。
また、TMR0 に値を書き込むと、プリスケーラはクリアされます。プリスケーラの値は読み出すことができませんし、TMR0 に値を書き込むのは、初期設定時を除いてどうかと思いますが、そんなに気にしないのであれば OK です。

> でも・・・スピードメーターを作る場合にこれをどう利用するか??が問題ですよね。
全体の構成は、もっとも重要なところで、楽しいところでもあり、苦しいところでもあるので、仕事でもなければ、他人が口を出すのはどうかと思います。
しかし、すでに口を挟んでしまっているようなので、もう少し口を挟ませてもらうことにします。

> 表示部分のプログラムは割り込みが発生したときに ...
> ... 呼び出すようにすれば良いですよね?
表示部分は、インタバルタイマの割り込みが発生したときに、単に表示するためのデータを出力するだけでいいと思います。
データの内容を気にする必要はありませんし、他のルーチンから呼び出す必要もありません。

お望みのものは、早い話が、周波数カウンタのようなものだと思います。
周波数カウンタには、大別して、ダイレクト・カウンタとレシプロカル・カウンタのふたつの方法があります (わからなければ、調べてください) 一定時間内で入力パルスの個数を数えるのか、入力パルスの間隔の時間を数えるのかのちがいです。最初の質問内容からは、レシプロカル・カウンタの動作に近いと思うのですが、いかがですか。以下は、そのつもりで書きます。
入力パルスは、割り込みで処理をすればいいと思います。入力パルスが入ったなら、前回のパルスからの時間をメモリのどこかに保存して、表示内容更新要求のフラグを立てるだけでいいと思います。ちなみに、表示内容更新要求のフラグというのは、そういうのがすでにあるわけではありません。メモリのどこかに作ります。前回からの時間というのは、別の、インタバルタイマの割り込みでインクリメントしていけばいいと思います。入力パルスの割り込みでは、それをメモリの別の領域にコピーして、前回からの時間をカウントしているメモリをクリアして、表示内容更新要求のフラグを立てればいいと思います。仮りに、もっと分解能が必要なら、TMR0 を読み出して演算するとか、そもそものインタバルタイマの間隔を考え直すとか、してください。入力パルスにチャタリングが入る可能性があるのなら、チャタリングを除去する工夫も必要です。
一方、メインルーチンでは、表示内容更新要求のフラグが立っていれば、そのフラグをクリアして、前回からのパルスの時間を、表示するための数値に変換 (BCD 変換も含む) し、さらに、数値を、表示するためのセグメントデータに変換して、メモリのどこかに、表示するためのデータとして、書き込むだけです。メインルーチンで、表示するためのルーチンを呼び出す必要はありません。放っておけば、そのうちに勝手に表示してくれます。
また、割り込みルーチン内の処理は、必要最小限にするほうがいいと思います。
こんなイメージですが、どうでしょうか。
なお、ダイレクト・カウンタとしての動作なら問題ないかと思いますが、レシプロカル・カウンタとしての動作なら、止まっているときはパルスが入らないので、最後のパルスが入ったときの値が更新されない可能性があります。たとえば、止まっているのに 1 km/h などと表示されるとか。強制的に、少なくともある時間内で表示内容を更新するような機能を加えたほうがいいかもしれません。
また、レシプロカル・カウンタのときは、逆数を計算する必要がありますが、PIC のように乗除算命令のない CPU で、アセンブラで割算をするのは面倒です。これは、何とか考えてください。さもなければ、割算をしないでテーブル変換にすることもできますし、いっそのこと、ダイレクト・カウンタにする手もあります。
いずれにしろ、インタバルタイマの割り込み間隔を、計算ができるだけ楽になるように選択してください。

この回答への補足

ありがとうございます。
>レシプロカル・カウンタの動作に近いと思うのですが、いかがですか。
そうですね、パルス同士の間隔を計測するのでおっしゃるとおりです。

>入力パルスは、割り込みで処理をすればいいと思います。
割り込みでパルスを受け付けるようにして割り込み発生で前回のパルスからの時間をメモリに入れ、表示内容更新要求のフラグを立てて割り込みから復帰するとう事ですね?
しかし、「この表示内容更新要求のフラグを立てる」の意味がさっぱり分かりません。
表示内容を更新してくださいと要求するプログラムを書くということですか?
それは具体的にはどのようにするのでしょうか?

あと、表示部分のプログラムですが、素人なので分かりやすさを優先したいと思います。
なので「000」~「199」までの表示は
例えば

タイマAのカウント結果をnとすると

n=1なら「199」を表示するダイナミック点灯のルーチンへ
n=2なら「198」を表示するダイナミック点灯のルーチンへ

みたいな感じでずら~っと200パターン並べてnの値に応じてプログラムでどのルーチンを実行するかを選択するという方法はどうかな?と思います。

プログラム的にはかなり不細工な書き方になると思いますが、この方が素人には理解しやすいと思うのです。
表示部分に関してこのような書き方はいかがでしょうか?素人の私には到底↓のようなデータの書き込みは無理そうなので・・・。
「前回からのパルスの時間を、表示するための数値に変換 (BCD 変換も含む) し、さらに、数値を、表示するためのセグメントデータに変換して、メモリのどこかに、表示するためのデータとして、書き込むだけです」

やっぱりまだまだ苦しみそうですね・・・(笑)。

補足日時:2006/01/08 22:30
    • good
    • 0

> 要するにこれですよね?


> 一定時間ごとに勝手に割り込みルーチンに飛んで処理をするということですよね?
そうです。

> ざっと目を通してみたのですがちょっと難しいですね。
確かに、他人の書いたものは難しく思えますね。でも、ご自分でやってみると、案外、簡単だと思います。案ずるより産むが易し。ただし、産みの苦しみという言葉もあります。
なお、重箱の隅をつつくようで恐縮ですが、参考 URL のように、割り込みルーチンの中で TMR0 の再設定をする必要はありません。FF -> 0 へと変化したのが原因で割り込みルーチンのなかへ飛んできているのですから、TMR0 はすでに 0 になって、さらにカウントアップしている途中だからです。むしろ、再設定しないほうがよいでしょう。再設定することによって現在の値を失うのは、多くの場合、望ましくないと思います。
さらに、隅をほじくるようなことをいえば、TMR0 のオーバーフローだけでは、望む時間が得られないときには、TMR0 に再設定することも有効です。この場合でも、単に TMR0 に一定値を書き込むのではなく、現在の TMR0 の値に一定値を加算するような形にしたほうがよいと思います。
これらは考え方の問題であり、実際には、たいしてちがいは現れてこないとも思えますが。
もっとも、TMR0 に再設定する必要が生じたなら、素直に TMR1 や TMR2 のコンペアを利用するほうがよいでしょう。しかし、16F84A には TMR0 しかありません。このへんが、16F84A の機能の制限という理由です。

> これならタイマーのカウント(速度の計測)とLEDの表示という2つの処理を同時に行えるということですね?
そのとおりですが、ひとつのタイマで、両方の処理を行えるといったほうが適切かもしれません。

> 長期戦になるとおもいますが
そのほうが望ましいでしょうね。他人の意見や、Web ページを見て、わかったような気になっているだけよりも、ご自分で、必要なら試してみたりして、納得されるほうが、よほど役に立つと思います。

この回答への補足

ありがとうございます。素人ながら頑張ってどうにか読んでみました。

・TMR0とは8ビットのタイマーカウンタ(カウントアップ)で最大256までのクロックをカウント可能
・入力されるクロック信号はCPUのクロックの1/4またはTOCKI端子に接続した外部からの信号のどちらかを選択可能
・カウント数はプリスケーラの3つのビットの設定により最大65535まで設定可能
・クロック入力毎にTMR0のFレジスタの値に+1される
・Fレジスタの値が十進数の0になると(オーバーフロー)割り込みを発生させる
・Fレジスタの値は任意の値を書けるのでオーバーフローするまでのカウント数を好きな値にできる。

あとはオプションレジスタでクロック信号の選択やプリスケーラの使用不使用、プリスケーラの値を設定、INTCONレジスタで割り込みに関する設定をする。

この程度理解出来てればOKでしょうか?
でも・・・スピードメーターを作る場合にこれをどう利用するか??が問題ですよね。
今度はそちらに頭をひねらねば・・・。
表示部分のプログラムは割り込みが発生したときに0km~200kmまで用意されたダイナミック点灯用のプログラム(サブルーチン)を計測結果に応じて呼び出すようにすれば良いですよね?
メインプログラム中でスピードパルスを数えて割り込みで表示用のサブルーチンを呼び出す・・・が良いかな?とは思っているのですが。
そうすればメインで常にカウントをしながら定期的にその結果を表示できる・・・(甘いかな?)
すみません。もう少しで楽になりそうなのでもうちょっとだけお付き合いください。
ありがとうございます。

補足日時:2006/01/08 16:47
    • good
    • 0

> 表示部分の割り込みというのはBの割り込みを利用するということでしょうか


そうです。

> 「インタバルタイマの割り込み」というのが分からないのです
> 何を条件に割り込みを発生させて表示用プログラムへ行く
使用目的による名まえで、PIC のデータシートには、この名まえで記載されていないかもしれません。
具体的には、TMR0 のオーバーフローの割り込みです。これを時間の間隔を管理するためのタイマとして使用するということです。
この割り込みを許可していれば、一定の時間ごとに、勝手に割り込みルーチンに飛んでいきます。プログラムから呼び出すわけではないので、フロ-チャートで示される以外の動作です。

以前の回答を読み返してみて、「これ以外の方法は、考えられません」というのは、断定的すぎたかもしれませんね。すみません。
ダイナミック点灯のためには、点灯している時間、消灯している時間を、一定にできればいいわけです。
必要な時間の間隔を得るためには、ソフトウェアで待ちのループを作る、割り込みを使う、などいろいろ方法があります。
ソフトウェアで待ちのループを作るというのは、一般的には嫌われます。理由は、待っている間は他の処理ができないのでマイコンの資源を有効に活用できない、待っている間に他の割り込みなどが入ると時間間隔が管理できない、プログラムを変更するごとに気にしておく必要がある、などです。しかし、これらが問題にならないのであれば、一定の時間の間隔を得るために、割り込みを使わないで実現できます。

> あと、PICをほんの少し動かせる・・・・ですが
うーん、どうでしょうね。PIC のアーキテクチャは特殊な部分があるので、どの程度、理解しているか、という意味でいったのですが。
最近のデバイスも 16F84A も、そんなに変わりはありません。追加された周辺機能を使わなければ、16F84A と同じように動かせます。もっとも、適切に初期設定をする必要はありますが。

あと、最初の質問について、回答を補足しておきます。
値を保存しておくのに、W レジスタではなく、メモリに保存しておくほうがいいでしょう。
W レジスタは、ワーキング・レジスタであり、他のマイコンでいう、アキュームレータのようなものです。あくまでも一時的に使用するレジスタです。

疑問点がありましたら、補足で追加してください。
また、ほかの回答者のかたのご意見もお待ちしています。

この回答への補足

ありがとうございます。
インタバルタイマについて調べたらこんなHPがありました。
http://homepage1.nifty.com/rikiya/software/313.T …

要するにこれですよね?
ざっと目を通してみたのですがちょっと難しいですね。なんとなくぼんやりとはイメージ出来始めてはいるのですが・・・。
一定時間ごとに勝手に割り込みルーチンに飛んで処理をするということですよね?
これならタイマーのカウント(速度の計測)とLEDの表示という2つの処理を同時に行えるということですね?
じっくり目を通して疑問があればまた補足いたしますのでよろしくお願いします(長期戦になるとおもいますが)。

補足日時:2006/01/05 23:09
    • good
    • 0

> 各桁の数字を高速で順番に切り替えて残像を利用して各桁が同時点灯しているように見せかける


これは、そのとおりです。
LED の明るさは、表示時間に依存しますので、各桁が点灯している時間は、同じでなければなりません。そうでなければ、桁によって明るさがちがってくるでしょう。また、各桁の点滅する周期を目の残像時間よりも、長くすると、表示のちらつきが感じられるようになります。
つまり、ダイナミック点灯においては「一定」の時間で、表示する桁を変えていくことが必要です。
インタバルタイマの割り込みによる方法が一般的です。
メインルーチンで他に何もする必要がなければ、#2 の方の示された参考 URL のようにソフトウェアのループで時間を計っても、ほぼ「一定」の時間で、表示する桁を変えることは可能と思われます。

> 表示中はパルスを受け付けない
パルスは、割り込みで受け付けるような設計ですか ?
パルスの割り込みが受け付けられないようなことは、ないと思います。
LED 用の割り込みは、表示する桁を切り換えて、新規の桁の表示を更新するだけです。あとは I/O ポートがその桁のドライブと表示内容をラッチしているので、何もしなくても、次に表示を更新するまで、その内容は表示され続けます。
LED 用の割り込みからは、すぐに抜けることができる (時間がかからない) と思いますが、割り込み処理中 (表示の更新中) に他の割り込みが入った場合でも、その割り込みは待たされます。
通常、割り込み処理中は、割り込み禁止になっています。だから、LED 用も割り込み処理中に、パルスからの割り込みが入った場合、その割り込みは待たされ、LED 用の割り込み処理が終わった直後に、パルスからの割り込みが起動します。
というよりも、そのようにプログラムを作らなければなりません。特に PIC では、割り込み処理中に、他の割り込みを受け付けるように (多重割り込み) しては、いけません。
また、割り込み中は、必要最小限の処理にするような配慮も必要です。

> こんな感じですか
そんな感じです。

> 16F84Aをほんの少しプログラムできる程度で最近のデバイスも動かせるものでしょうか
「ほんの少し」というのが微妙ですが、できるでしょう。
逆にいえば、最近のデバイスを動かせないのであれば、16F84A も、ろくに動かせていなかった、ということでしょう。
ミッドレンジであれば、アーキテクチャは 16F84A と同じです。いやらしいバンク切り換えが必要なところまでもが同じです。周辺機能については、データシートを見て理解でき、周辺機能を動かすためのハードウェアの一般的な知識があるなら、問題ないでしょう。
ソフトウェアの全体の設計、デバイスのアーキテクチャ、ハードウェアの理解が重要と思います。

疑問点がありましたら、補足で追加してください。

この回答への補足

すみません。本当に素人で・・・。
割り込みについて調べてみたんですが、割り込みには
A「外部からの信号入力によって発生する割り込み」
B「プログラム内部で一定の条件を満たした場合に発生する割り込み」
の2種類があるんですね。
今回の表示部分の割り込みというのはBの割り込みを利用するということでしょうか?

「インタバルタイマの割り込み」というのが分からないのです。

何を条件に割り込みを発生させて表示用プログラムへ行くのでしょうか?
フローチャートのように時間の流れを追って説明をいただけると助かります。(すでにそうしていただいているのかも知れませんが・・・すみません)

あと、PICをほんの少し動かせる・・・・ですが、
ピンからの出力とタイマでLEDを点灯、点滅させたり、スイッチでプログラムを変更したり(割り込みではなくて入力待ち)、といった程度です。

補足日時:2006/01/04 09:14
    • good
    • 0

参考URLの回路とプログラムソースはかなり参考になると思います。


プログラムソースを見る限りダイナミック点灯のためにタイマ割り込みは用いられていません。

参考URL:http://www.try-net.or.jp/~el_dream/clock/seven_a …

この回答への補足

ありがとうございます。
なるほど、時計もスピードメーターも同じような処理でできるということですね。
ただ、プログラムの理解がまだ十分出来ていないので私にはちょっと難しいです。
後でまたじっくり読み返してみます。

補足日時:2006/01/04 09:11
    • good
    • 0

ダイナミック点灯をさせるためには、インタバルタイマの割り込みを使います。

これ以外の方法は、考えられません。
LED のそれぞれの桁に表示させたい値を、メモリに書いておきます。タイマの割り込みで、表示をいったん消して、表示する桁を切り換えてから、メモリから読み出してきた次の桁の値を表示します。
ですから、タイマ割り込みルーチン以外のところでは、表示のことを気にする必要はありません。表示したい値をメモリに書いてさえおけば、そのうち割り込みルーチンで表示してくれます。
各セグメントに対するデコードは、メモリに書き込むときにデコードするか、表示するときにデコードするかは、都合のいいように決めます。
ダイナミック点灯なので、それぞれの桁は、点滅を繰り返しますが、点滅の周波数を、最低でも 60 Hz 以上にしないと、ちらつきが発生します。今回は 3 桁なので、タイマの割り込みは 180 Hz 以上 (5.5 msec 以下) にするべきでしょう。元のクロックも、タイマ割り込みにあわせて、プリスケーラと組み合わせて、適当な周波数を決めます。

さて、質問者さんの疑問は、16F84A にはタイマが 1 個しかないのに、そして、その 1 個はすでに使い道を決めているのに、どうやってやるんだ ? ということでしょうか。
9 msec のタイマを予定されているようですが、LED 用のタイマ割り込みと共用できます。たとえば 4.5 msec の周期で割り込みをかけるとして、ソフトウェアで、ポストスケーラを作れば、9 msec のタイマとして利用することができます。つまり、割り込みルーチン内で回数を数えておいて、2 回タイマ割り込みが入ったら、本来の「タイマ A」としての動作をさせるというぐあいです。
おそらくタイヤからのパルスも、割り込みで処理する必要があるでしょうが、割り込み要因を見て、分岐させてください。

ところで、16F84A などという機能の貧弱なものを、いまどき、使う必要があるのでしょうか。特にタイマ関係は制限が大きいと感じています。
PIC の最近のデバイスを使ったほうが、ずっと楽だと思うのですが。

この回答への補足

ありがとうございます。
ダイナミック点灯でのインタバルタイマの割り込みですか?素人なのでよく分からないのですが、ダイナミック点灯って各桁の数字を高速で順番に切り替えて残像を利用して各桁が同時点灯しているように見せかける方法ですよね?素人考えでは各桁を高速切り替え点灯させるラベルを設けておいてパルス入力の割り込みでそのラベルに飛ぶようにすれば良いかな?と思ったんですが、そのようにすれば表示中はパルスを受け付けないんですよね?だからインタバルタイマの割り込みが必要だということですか?
「さて、質問者さんの・・・」以下はなんとなくイメージとしては掴めました。
こんな感じですか?

4.5msecのタイマ・・・(1)

LED用タイマ割り込み

4.5msecのタイマ

LED用タイマ割り込み
&タイマAのカウント

(1)へ

ダイナミック点灯でのタイマ割り込みの使い方をもう少し噛み砕いて教えていただけると理解できそうなのでお願いいたします。
それと、デバイスですが、16F84Aをほんの少しプログラムできる程度で最近のデバイスも動かせるものでしょうか?興味はあるんですが、知識が乏しいので・・・・。

補足日時:2006/01/02 08:15
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!