| ホーム | 私の電子工作作品集 |
[ 初公開日:2021年3月15日 ] |
去年の9月ごろの話ですが、アマゾンの中華モールを覗いていたら 「 8 x 8 LED ドットマトリクス・ディスプレイモジュール」が余りにも安価だったため、思わず購入をしてしまいました。
8 x 8 ドットマトリクス LED に、8 桁 LED ディスプレイドライバ IC MAX7219 が付いたモジュールです。 10 数年も以前に "097. ドットマトリクス 8 x 8 LED 表示時計" を製作したのですが、そのときには MAX7219 のような便利な ICがなかった(存在していたかどうかは分かりませんが)ので、 ドットマトリクス LED を6個も駆動させるための配線量が膨大?となり、本当に大変だったことを思い出します。 それに比べて本機では、上述のディスプレイモジュールを使用するので、配線量の膨大な問題は一挙に解決することができます。 したがって、まだ PIC 周りの回路図も未完成のままの状態の頃、"190. I2C IF モジュール + LCD(1602A) 表示時計" で使用した各種のモジュールを入手したこともあって、それらの各種モジュールの機能、使用方法などを確認、評価するために、 本機の製作の方は一時中断をして、先に、LCD(1602A) 表示時計の製作に取り掛かったのでした。 その結果、本機でも LCD(1602A) 表示時計と同じ「リアルタイムクロックモジュール」、「デジタル温度湿度センサーモジュール」を使用することとし、PIC には、私にとっては初めての使用となる 20 ピンの PIC16F690 を使用することにしました。 この PIC16F690 は、"183. ラーメン・タイマー III" の製作時に、14 ピンの PIC16F684 と一緒に購入をしておいたものです。 タイマースイッチ機能等を追加 ( 2021/5/16 更新 ) このページをご覧になった方から、「オン/オフ時間を自由に設定ができるタイマースイッチ機能や、設定した温度になったらオン/オフする温度スイッチ機能が欲しい」 と、ご要望がありました。 しかし、次の回路図を見ると分かるように、 PIC にはそれらの新たな機能を割り付けるための空きポートが1つもありません。 そのような訳で私は、当初はこの機能追加についてはあまり乗り気ではなかったのですが、その後の検討で、次の回路図の項の追加説明で述べる方法で新たな I/O ポートを確保し、機能追加の実現をさせました。 なお、追加をした機能概要の内容については、機能概要と使用法 の項の < 新しい機能 > を参照してください。 機能追加後、新たに見つかった不具合 ( 2021/10/9 追記 ) RTC(リアルタイムクロック)モジュールの設定操作の不具合、およびコロン(:)、ピリオド(.) の表示制御の不具合が見つかりました。 詳細は 新たに見つかった不具合 2 件、不具合の修正プログラムについては 現在の最新バージョン を参照してください。 LED の輝度調節機能を追加、その他を改善 ( 2021/12/7 更新 ) 今回の機能追加は、前回のようにハードウエアの変更を伴うような大きな機能追加ではなく、LED の輝度をスイッチ操作によって調節ができるように、プログラムの変更だけの小規模なものです。 その内容については、機能概要と使用法 の項の < 新しい機能 2 > を参照してください。 また、本機が動作中に "ジー" という異音(雑音)が、ブザーから出たり出なかったりする現象が本機の作製当初からあったのですが、その雑音が出ないように改善をしました。 この件に関しての詳細は 下記 を参照してください。 割り込み処理の不具合を解決 ( 2022/4/30 更新 ) 上述の "タイマースイッチ機能等を追加" して以来、タイマー0割り込みを有効にすると不具合が起こる事象を初めとして、割り込み処理に関していろいろな不具合が起こっていました。 そして、タイマー0割り込みでは、タイマー0割り込みを使用しない 代替処理で目的を達していました。 この度ようやく、これらの割り込みに関しての不具合の原因が分かったため、割り込み全般に関して今まで "?" だったプログラム部分を修正しました。 詳細はプログラムの項の 割り込み処理の不具合を解決、 および 現在の最新バージョン の ソースファイル (Matrix88LED_ClockII.asm)(Ver. 1.14)を参照してください。 |
注.従来の 回路図 の左上部分にある ICSP 廻りを、上回路図のように変更をしました。 なお、プリント基板(2)パターン図 (部品面) ... (改造後) 等には反映をしてありませんので、 製作をする場合には注意をしてください。 ( 2024/7/3 更新 ) |
追加説明 ( 2021/5/16 追加 ) 上述のようにタイマースイッチ機能等の追加をすることにしたのですが、現在、PIC には新たな機能追加をするための空きポートが1つもありません。 通常であれば PIC16F690 よりも多ピンの、例えば 28 ピンの PIC16F873A/876A や PIC16F883/886 などに取り替えて作り直すことになるのですが、私は作り直しをしたくなかったので今回は、使用 PIC には 20 ピンの PIC16F690 のまま変更はしないで、 I/O エキスパンダ IC の PCF8574/74A を追加して新たな I/O ポートを確保することにしました。 この PCF8574/74A は、"190. I2C IF モジュール + LCD(1602A) 表示時計" で使用をした、"I2C インタフェースモジュール" に使われていた IC で、 私は扱い易いように、DIP パッケージ版の PCF8574AP を、アマゾンから入手をして使用しました。 なお、PCF8574 でも PCF8574A でもどちらでも構いませんが、両者ではスレーブアドレスだけが異なっています。 まず、PIC16F690 用のプリント基板(2) の空きスペースに、上図の左下に位置する 赤色 ----- で囲んだ4本の端子を追加します (詳細は プリント基板(2)パターン図 (部品面) ... (改造後)、 (ハンダ面) ... (改造後) を参照)。 その上で、次の機能追加の回路図に示す プリント基板(3)パターン図 (部品面)、(ハンダ面) を新たに作製をして、 両者をコネクタ、ケーブルで接続をします。 ハードウエアの変更としては、このように既設のプリント基板(2) の簡単な改造と、プリント基板(3) を新たに作製をすることによって、機能追加の実現をさせました。 後は新機能のためのプログラム追加ですが、PIC16F690 のプログラムメモリとデータメモリには十分な空きがあったため、機能追加を盛り込むことができました。 下の追加回路において、CH1 と CH2 にはタイマースイッチ機能、 CH3 には温度スイッチ機能を割り当ててプログラミングがしてあります。 また、今回の機能追加を機として、以前から気になっていたハードウエアのちょっとした設計ミスも修正をしておきました。 間違いではないのですが、時と分、分と秒、それぞれの間のコロン(:)、あるいはピリオド(.)用の4つの LED に割り当ててある、RC ポート番号の変更をしました(当然、プログラム変更も必要)。 このハードウエア(とプログラム)の変更で、どのポート番号がどの LED に対応しているかが直感的に分かり、他の人がプログラムを理解する上でも容易になります。 (参考: 変更前の 回路図 ) RC7, RC6, RC5, RC4, RC3, RC2, RC1, RC0 ... (ポート番号) RL, RU, LL, LU, SW5, CLK, LOAD,DIN ... (変更前の信号名) LU, LL, RU, RL, SW5, CLK, LOAD,DIN ... (変更後の信号名)なお、4つの LED の信号名は、LU:左上、LL:左下、RU:右上、RL:右下 を表しています。 上の追加回路では小型リレーを使用していますが、もしも、商用AC電源を制御するような場合には少々役不足なため、もう一段パワーリレーの追加などの考慮が必要です。 |
| 回路図 (Matrix88LED_ClockIIc.CE3) | 回路図 (ExpanderPCF8574A.CE3) | ページトップ |
このドットマトリクス・ディスプレイモジュールには、ドライバ IC MAX7219 に DIP パッケージを使用したものと、SOP パッケージを使用したものの2種類があって、冒頭で述べた "余りにも安価だった" のは、
実は DIP パッケージの方で、SOP パッケージのものは DIP パッケージのものに比べて 1.5 倍ほどします。 前者の DIP パッケージ版は、モジュールを横に並べて使用することができない* ため、本機には不向きです。 後者の SOP パッケージ版は、モジュールのプリント基板と LED が同サイズになっていて、 いくつも横に並べて使用することが可能となっています。 本機では後者を採用しました。
しかし、これらのモジュールですが、上述のように良いところばかりではありません。 中華モールにはいろいろなお宝電子部品(商品)が売られていますが、それぞれの商品に対するテクニカルな説明書というものは、 一切、添付されていません。 商品は売るがそれぞれの使用方法などは、自分で勝手に調べろ。 ということのようです。 これらのモジュールも、MAX7219 についてはネットを検索すると、メーカー "Maxim Integrated" 社のデータシートが見つかりますが、ドットマトリクス LED については、側面に印字がされている "1088AS" の文字列で検索をしても、 有益な情報は得られませんでした。 このドットマトリクス LED の裏面には、8 ピン x 2 列の足が出ているので、上の写真からもモジュールのソケットに差し込むことは分かりますが、さて、どちらの向き( 2 列の内どちらを上、または下)に差し込むのかは上述のように、 一切、説明がないのです。 仕方がないので、自分で調べることにしました。 そしてまず初めに、モジュールのソケットの各ピンには、MAX7219 のどの信号と繋がれているのかを調べてみました。 その結果が下図に示す (1) と (4) で、薄い水色とピンクに塗ったものが、ソケットの各ピンに繋がれている MAX7219 の信号名です。 これはモジュール基板を上写真の位置の状態に置いたときのものを表しています。 そして、DIP パッケージ版、SOP パッケージ版ともに、同位置の各ピンには同信号が繋がれていることを確認しました。 また、下図の (1) と (4) で白抜きになっている数字部分はソケットの各ピン番号で、上写真を 拡大 して見るとよく分かるように、DIP パッケージ版には (1) のようにピン番号が印字されていますが、SOP パッケージ版には何も記されていないので、この (1) の時点では DIP パッケージ版のピン番号が正しいものと仮定をした番号です。 そして次に、ドットマトリクス LED のピン番号についても、DIP パッケージ版に添付されていた LED では、上の写真では分かり辛いと思いますが、裏面にピン番号が記されています。 しかし、SOP パッケージ版に添付されていた LED では、 ピン番号の表記は一切ありません。 どちらも同一名の "1088AS" の文字列が側面に印字がされているのですが、両者では明らかに別物であることが見て取れます。 メーカーが違うのでしょうか。 (これらのマトリクス LED は赤色のものだと勝手に思っていましたが、実際に点灯をさせてみるとオレンジ色に発光するものでした。) そこで、この DIP パッケージ版に添付されていた LED のピン番号が正しいものと仮定をして、実際に各ピンに電圧を加えたときに、どの位置の LED が点灯するかを調べてみた結果が (2) です。 このときは、モジュールのソケットのピン番号 = LED のピン番号 の関係となっています。 なお、(2)、(3)、(5) で XX-XX と表記があるのは、(6) に示すように左側の数字は K: カソ−ドで−電位を、右側の数字は A: アノ−ドで+電位を、それぞれのピン番号に加えたことを表しています。 例えば、(2) の左上角の 9-13 とは、9 ピン = −電位、13 ピン = +電位を、それぞれに加えたときに点灯したことを表しています。 (2) の結果を見てみると、何かおかしいことに気が付くと思いますが、マトリクス LED のX軸とY軸に現れる信号名 Dig x と Seg x がバラバラというか、両軸ともに両信号名(ディジットとセグメント)が混在をしていて、 これではマトリクス LED を制御することは不可能です。 モジュールのソケットのピン番号 = LED のピン番号は間違っているようです。 ―― う〜ん、どうすれば良いのか。
このときのマトリクス LED のX軸とY軸に現れる信号名 Dig x と Seg x は、両軸でそれぞれが分離され、しかも順序良く信号順に並んでいることが分かります。 しかし、(3) ではマトリクス LED の本来のピン番号ではなく、 (1) のモジュールのピンソケット番号に合わせてある、というのはやはり不合理だと思います。 そこで再び、(3) での番号を本来のマトリクス LED のピン番号に戻したものが (5) で、かつ、モジュールのピンソケット番号の方を、本来のマトリクス LED のピン番号に置き換えたものが (4) になっています。
このようにして、ドライバ IC MAX7219 が SOP パッケージのものを使用することにしたのですが、もう一つ、不備な点があります。 それは、上に示した 写真 からも分かるように、 DIP パッケージのものには電解コンデンサが添付されてきますが、SOP パッケージのものには添付されず、それ以前にプリント基板のパターンそのものに取り付けるような考慮がありません。 しかし、MAX7219 データシート(日本語版) の 10 ページには、"桁ドライバのピーク電流に起因する電源リップルを最小限に抑えるために、できる限りデバイスに近い位置で、V+ と GNDの間に 10μFの電解コンデンサと 0.1μFのセラミックコンデンサを接続してください。" と書かれています。 仕方がないので 10μFの電解コンデンサを、モジュール6個とも、上の写真のように自前で追加して取り付けました。 また、このモジュールにはL型のピンヘッダが添付されてきますが、L型ではプリント基板に実装するときには都合が悪いので、ストレート型のピンヘッダに取り替えました。 (詳細は、プリント基板(1)パターン図 (部品面) を参照のこと。) |
| ページトップ |
冒頭で述べたように、本機でも前回公開の "190. I2C IF モジュール + LCD(1602A) 表示時計" で使用をした、「リアルタイムクロックモジュール」、
「デジタル温度湿度センサーモジュール」と同じモジュールを使用しました。 これらのモジュールを使用するに当たって、私が感じた問題点やその他のことなどについては、そちらのページの
使用した各モジュールについて のそれぞれの項、リアルタイムクロックモジュール、
デジタル温度湿度センサーモジュール で詳述をしているので参考にしてください。 写真だけを次に再掲をしておきます。 そして、これらのモジュールを本機で使用するに当たっては、下写真に写っているL型ピンヘッダを、ストレート型のピンヘッダに取り替えて使用をしました。 L型ピンヘッダのままだとプリント基板に取り付けたときに、 基板の平面に対して垂直となって空間的に邪魔になるため、どちらも交換をしました。 また、「リアルタイムクロックモジュール」のバックアップ用の電池については、一次電池の CR2032 を使用するために簡単な改造が必要で、それについても前回公開ページの リアルタイムクロックモジュール で述べているのでそちらを参照してください。
話は変わって、最近私が感じている愚痴話しを少々 ・・・ アマゾンにおける中華モールに出品されているたくさんの電子部品は、それらの機能の高さや種類の豊富なこともさることながら、 我々アマチュアにとってはそれらの価格の安さが一番の気になるところです。 どんなに素晴らしい部品であっても、高価格であってはアマチュアの私には手を出すことができません。 そんな意味からも最近のアマゾンを見ていると、私にとっては魅力が半減をしてしまいました。 私がこれらの中華モールと付き合い出してからまだ1年にも満たないのですが、たくさんの電子部品それぞれの価格が、 一定ではなく常に変動をしている、とは当初からいつも感じていました。 それでも、配送料無料というネットショップが多くあって、中国本土から送られてくるために相当な時間は掛かりますが、それなりの魅力を感じていました。 私の場合には、直ちにこの部品が入用である、というようなことは少なく、 今度いつか機会があったら使ってみよう、てきな購入動機が多いので、したがって、到着までに、2週間、3週間が掛かってもあまり問題ではないのです。 ところが、昨年の年末あたりから、やたらと配送料が有料になってしまったネットショップが増えてきたようです。 そして、例えば上述のデジタル温度湿度センサーモジュール( DHT22(AM2302) )を例に取ってみると、 私が入手をしたときには、昨年の 12 月の初め頃で、ネットショップRから1個 340 円(配送料無料)で、初めて使用する部品であることから 2 個を試し買いしました。 その同じデジタル温度湿度センサーモジュールですが、最近のアマゾンを覗いて見ると、ネットショップRが出荷元で単体(1個売り)での出品が無くなり、販売元は同じネットショップRとなっていますが、 出荷元が Amazon に代わって、何と1個が 691 円(2021/03/15 現在)と、2倍以上になっています。 しかも、出荷元が Amazon ということは、プライム会員でない私にとっては 2,000 円以上の買い物をしなければ無料配送が適用されません。 2倍どころか、実質、3倍以上の大幅な値上がりです。 このように、今ではかなり高価なものとなってしまった DHT22(AM2302)は、今後は残念ですが、気軽に使用するということができなくなってしまいました。 このような価格が続く限り、私はもう DHT22(AM2302)を使用することはないでしょう。 また、今回使用した他のモジュール類では、リアルタイムクロックモジュール( DS3231 )と ドットマトリクス・ディスプレイモジュール( MAX7219 )についても、私が入手をしたときに比べると、 残念ながら現在では2、3割以上は値上がりをしているようです。 |
| ページトップ |
| ||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||
|
| ページトップ |
本項におけるドットマトリクス LED の表示写真のすべては、フィルタ越しに撮ったものを使用しました。 素のままで撮ったものは、発光している LED が滲んだように写ったりして、うまく撮れなかったためです。 なお、フィルタには 100 均で購入をした赤色の透明な "下敷き" を、フィルタ代わりとして使用しました。 |
モード 2 モード 3 温度表示 [ _- xx.x℃ ] 湿度表示 [ _x xx.x% ] |
― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― |
< 新しい機能 > ( 2021/5/16 追加 ) |
|
< 新しい機能 2 > ( 2021/12/7 追加 ) |
|
| ページトップ |
・ MAX7219 使用 8 x 8 ドットマトリクス LED モジュールのプログラミング |
・ 事前に必要な予備知識 |
・ MAX7219 制御の基本的な考え方 |
・ フォントデータの読み出しとマトリクス LED への表示 |
・ リアルタイムクロックモジュールのプログラミング |
・ デジタル温度湿度センサーモジュールのプログラミング |
・ 機能追加: タイマースイッチ機能、温度スイッチ機能 ( 2021/5/16 追加 ) |
・ I/O エキスパンダ IC(PCF8574/74A)のプログラミング |
・ PIC16F690 とページの問題 |
・ PIC16F690 とバンクの問題 |
・ タイマーの設定とタイマー用メモリの構成 |
・ 機能追加で発生した不具合 |
・ 新たに見つかった不具合 2 件 ( 2021/10/9 追記 ) |
・ 割り込み動作の再確認 ( 2021/12/7 追記 ) |
・ 割り込み処理の不具合を解決 ( 2022/4/30 追記 ) |
・ 機能追加: LED の輝度調節機能を追加、その他を改善 ( 2021/12/7 追加 ) |
・ 現在の最新バージョン |
● MAX7219 使用 8 x 8 ドットマトリクス LED モジュールのプログラミング ◎ 事前に必要な予備知識 このモジュールのプログラミングを行うには、使用したドットマトリクス LED モジュールについて の項で述べた、8 x 8 ドットマトリクス LED の各ドット LED の位置と、ドライバ IC MAX7219 の各出力の Digit 番号、Segment 番号との対応を、まず、理解をする必要があります。 本機で使用したモジュールのその対応図を次に再掲しておきます。
また、文字フォントデータを作成する観点からも、データシートのようにセグメントの各ビットの並びを左右逆にして作成するのは少なからず大変なので、次の例のようにフォントデータを作成しておいて、使用時にプログラムで左右を逆変換することにしました。 例えば、"5" のフォントデータを例にとると、次のようにデータの定義をした方が自然で作成をするのにもずっと楽になります。 retlw b'11111100' ;05: 5 retlw b'11000000' retlw b'11000000' retlw b'11111100' retlw b'00000110' retlw b'00000110' retlw b'11000110' retlw b'01111100'そして、各ビットの並びを左右逆に変換をするためには、次のようにプログラムで簡単に行うことができます。 : call font_read ;文字フォントデータの読み出し movwf work1 ;反転前のビット列 : movlw 8 ; movwf lp_cnt1 ;ループカウンタ1(segment カウンタ) frbuf06 rlf work1,F ;C bit を出力 ;;┐この部分が重要 rrf work2,F ;C bit を入力 ;;┘ decfsz lp_cnt1,F ;ループカウンタ1 - 1 = 0 か? goto frbuf06 ;No movf work2,W ;反転後のビット列 movwf INDF ;バッファへ格納 :また、文字フォントデータの定義には、本機で表示をするために必要な最小限の文字量だけを確保することとし、それでも全体としてのデータ量が多くなるのでプログラムメモリの一部に 「文字フォントデータテーブル」の確保をしました。 そして、本機では6桁のドットマトリクス LED に効率良く表示をさせるために、「文字フォントデータテーブル」から一気に6桁分のフォントデータを読み出して (このときに、上述のビット列変換も行ってしまう)、一旦、PIC のデータメモリ内に設けたバッファエリア(1 文字当たり 8 バイト x 6 桁 = 48 バイト)に貯め込みます。 本機で使用の PIC16F690 では下図の右側に示すように、データメモリの内、薄黄色の部分が汎用レジスタ(GPR)でユーザが自由に使用できる RAM メモリです。 バンク 0 にはプログラム内で使用する一般の変数が多くを占めていて、 同バンクには 48 バイトのバッファの確保ができないため、本機ではバンク 1 を使用することにしました。 次の図は、本機における PIC と MAX7219(モジュール)との接続の様子を、ブロック図で表したもの(詳細は 回路図 を参照)ですが、図に示すように、DIN、CLK、LOAD の3本のインタフェース線で PIC と MAX7219 を接続、および各 MAX7219 同士を順にカスケード接続をしています。 CLK、LOAD については、PIC および6個の各 MAX7219 ともすべて同一の信号名同士の接続をし、DIN については、PIC の DIN から1番目("HH" 桁) MAX7219 の DIN と接続をし、次に1番目 MAX7219 の DOUT と2番目("HL" 桁) MAX7219 の DIN と接続をし、以下同様に、DOUT から DIN に、DOUT から DIN に、・・・ というように最後6番目("SL" 桁) MAX7219 の DIN まで接続をします。 |
| プログラムのトップに戻る |
◎ MAX7219 制御の基本的な考え方 以上のことを事前に必要な予備知識として十分に理解をした上で、MAX7219 のプログラミングを始めます。 なお、以下の図1と表1〜 表 10 はいずれも Maxim Integrated 社の MAX7219 データシート(日本語版) から抜粋をしたもので、これらの図表を参考にしてプログラミングを行いました。 次の図1は、上述した3本のインタフェース線で通信を行うときのタイミングを図に表したもので、DOUT を除いて、いずれの信号もすべて PIC から MAX7219 に送られます。 なお、桁レジスタまたは制御レジスタの種類は表1の ADDRESS (D11 〜 D8) で指定をし、DATA (D7 〜 D0) にはそれらのレジスタに設定するデータを指定します。 XXXX (D15 〜 D12) はダミービットのため 1 でも 0 でも構いません。 私の使用例では、輝度レジスタ(Intensity 0xXA)を初期設定時以外に、デモプログラムのときに変更をしたぐらいです。 また、この輝度レジスタ(Intensity 0xXA)は LED の輝度を変更させるものですが、 モジュールに使用のマトリクス LED が高輝度 LED らしく、通常は 最小値の 0 を設定 しておけば十分(以上)に明るく点灯をしています。 なお、リストはプログラム Ver.1.13 で更新をしたものに差し替えをしました。 ( 2021/12/7 更新 ) ;========================================================================== ; 定数の定義と変数のレジスタ割付け ;========================================================================== : maxDIN equ 0 ;MAX7219 DIN 制御 ;┐ maxLOAD equ 1 ;MAX7219 LOAD 制御 ;│PORTC maxCLK equ 2 ;MAX7219 CLK 制御 ;┘ : cblock h'20' ;Bank0 : reg_addr ;MAX7219 のレジスタアドレス reg_data ;レジスタへの書き込みデータ maxtmp ;送信データの一時保存 mbit_cnt ;ループカウンタ mlpcnt1 ;ループカウンタ mlpcnt2 ;ループカウンタ mlpcnt3 ;ループカウンタ nop_cnt ;該当の桁より左側の桁数 segment ;Segment データ変数 digit ;Digit データ変数 addr_high ;文字フォントデータの high アドレス addr_low ;文字フォントデータの low アドレス dspdigit ;表示桁指定フラグ fsr_mem ;フォントデータバッファのアドレス記憶 tbl_type ;フォントデータテーブルの種類 (0/1/2) : endc cblock h'a0' ;Bank1 : ; (フォントデータのバッファエリア) fd_buff ;バッファエリアの先頭 ; (8 * 6 = 48 byte 使用) endc : : : ;========================================================================== ; MAX7219 レジスタの基本制御 ;========================================================================== ; マトリクス LED モジュールの初期化 max_led_init movlw h'0f' ;ディスプレイテストレジスタ movwf reg_addr movlw h'00' ;通常動作 movwf reg_data call max_led_contr movlw h'09' ;デコードモードレジスタ movwf reg_addr movlw h'00' ;デコードなし movwf reg_data call max_led_contr movlw h'0a' ;輝度レジスタ movwf reg_addr movlw h'00' ;1/32 (minimum) movwf reg_data call max_led_contr movlw h'0b' ;スキャン制限レジスタ movwf reg_addr movlw h'07' ;スキャン対象 8桁 movwf reg_data call max_led_contr movlw h'0c' ;シャットダウンレジスタ movwf reg_addr movlw h'01' ;通常動作 movwf reg_data call max_led_contr call digit_reg_clr ;全 Digit レジスタのクリア return ;-------------------------------------------------------------------------- ; 同一データ MAX7219 x 6個分の制御 max_led_contr bcf PORTC,maxLOAD ;maxLOAD="L" movlw 6 ; movwf mlpcnt1 ;ループカウンタにセット maxcont01 movf reg_addr,W ;レジスタアドレス call max_byte_send movf reg_data,W ;レジスタデータ call max_byte_send decfsz mlpcnt1,F ;6個分送信した か? goto maxcont01 ;No bsf PORTC,maxLOAD ;maxLOAD="H" return ;-------------------------------------------------------------------------- ; データ 1 バイト送信 max_byte_send movwf maxtmp ;Wレジスタの内容を一時保存 movlw 8 movwf mbit_cnt ;ループカウンタにセット mbsend01 bcf PORTC,maxDIN ;maxDIN="L" btfsc maxtmp,7 ;送り出しデータの 7ビット目 = 1 か? bsf PORTC,maxDIN ;Yes, maxDIN="H" bsf PORTC,maxCLK ;maxCLK="H" nop bcf PORTC,maxCLK ;maxCLK="L" rlf maxtmp,F ;送り出しデータを 1ビット左にシフト decfsz mbit_cnt,F ;8ビット送信した か? goto mbsend01 ;No return ;-------------------------------------------------------------------------- ; 全 Digit レジスタのクリア digit_reg_clr clrf reg_data ;レジスタデータ movlw 8 ;Digit レジスタ数 movwf reg_addr ;ループカウンタにセット dclr01 call max_led_contr decfsz reg_addr,F ;全 Digit レジスタに送信した か? goto dclr01 ;No return ;-------------------------------------------------------------------------- ; ノーオペレーション no_operation movwf mlpcnt1 ;ループカウンタ1 にセット movf mlpcnt1,W btfsc STATUS,Z ;mlpcnt1 = 0 か? goto nop02 ;Yes nop01 clrw ;no-opレジスタ call max_byte_send clrw call max_byte_send decfsz mlpcnt1,F ;ループカウンタ1 - 1 = 0 か? goto nop01 ;No nop02 return 先にプログラムを示すと次のようになります。 初めに LOAD を "L" にした後、PIC から一番離れた、すなわち "SL" 桁のデータ "F" を一番最初に送り、順次、"SH" 桁のデータ "E"、"ML" 桁のデータ "D"、"MH" 桁のデータ "C"、"HL" 桁のデータ "B"と PIC に近づき、一番最後に、 "HH" 桁のデータ "A" を送ります。 reg_addr: レジスタアドレスはそのときに実際に制御するレジスタ番号を指定します。 そして、全桁分のデータを送った後に LOAD を "H" にすることによって、それぞれのデータがそれぞれの桁の、指定をした桁レジスタまたは制御レジスタに設定がされます。 これが複数の MAX7219 をカスケード接続をして使用するときの、 個々の MAX7219 のレジスタにデータを設定するための基本的な考え方です。 なお、使用しているサブルーチン max_byte_send は、上に示した MAX7219 の基本制御ルーチンの中にあり、各 1 バイト( = 8 ビット)のデータを送信するサブルーチンです。 : bcf PORTC,maxLOAD ;maxLOAD="L" movf reg_addr,W ;レジスタアドレス ..... "SL" 桁用 call max_byte_send movlw "F" ;レジスタデータ call max_byte_send movf reg_addr,W ;レジスタアドレス ..... "SH" 桁用 call max_byte_send movlw "E" ;レジスタデータ call max_byte_send movf reg_addr,W ;レジスタアドレス ..... "ML" 桁用 call max_byte_send movlw "D" ;レジスタデータ call max_byte_send movf reg_addr,W ;レジスタアドレス ..... "MH" 桁用 call max_byte_send movlw "C" ;レジスタデータ call max_byte_send movf reg_addr,W ;レジスタアドレス ..... "HL" 桁用 call max_byte_send movlw "B" ;レジスタデータ call max_byte_send movf reg_addr,W ;レジスタアドレス ..... "HH" 桁用 call max_byte_send movlw "A" ;レジスタデータ call max_byte_send bsf PORTC,maxLOAD ;maxLOAD="H" :次に、上同図において例えば "MH" 桁のデータ "C" だけを設定したいような場合にはどうすれば良いか。 ただし、他の桁は現状のままで変更はしないものとする。 これもプログラムを示すと次のようになります。 ( この解答部分は 2021/12/7 に訂正をしました。) : bcf PORTC,maxLOAD ;maxLOAD="L" movlw 3 call no_operation ;ノーオペレーション movf reg_addr,W ;レジスタアドレス call max_byte_send movlw "C" ;レジスタデータ call max_byte_send movlw 2 call no_operation ;ノーオペレーション bsf PORTC,maxLOAD ;maxLOAD="H" :初めに LOAD を "L" にした後、まず "MH" 桁より右側の桁数分 = 3個の No-Op コード(1個当たり 16 ビットのオール "0")を送ります。 次に該当の "MH" 桁用のデータ "C" を送り、その後 "MH" 桁より左側の桁数分 = 2個の No-Op コードを送ります。 そして、最後に LOAD を "H" にすることによって "MH" 桁のデータ "C" だけが設定をされます。 上の例と同様に reg_addr: レジスタアドレスは、そのときに実際に制御するレジスタ番号を指定します。 |
| プログラムのトップに戻る |
◎ フォントデータの読み出しとマトリクス LED への表示 以上の2例については、カスケード接続をした MAX7219 の各桁の任意レジスタに、任意データを設定する基本的な考え方、方法を述べましたが、次に、本機では実際にどのようにして、 文字フォントデータテーブルからその必要なフォントデータを読み出して、マトリクス LED に表示をさせているかを説明します。 まず初めに、本機で使用をしている 文字フォントデータテーブル の一部(文字 "0" 〜 "9")を次に示します。 データの各横1行がセグメントデータ 8 ビット(1 バイト)で、8 x 8 ドットマトリクス LED の各横1行に対応しています (しかし、上述 したように実際には左右の並びが逆)。 そして、各フォントデータ 8 行(8 バイト)分が上から順に Dig7 〜 Dig0 で、8 x 8 ドットマトリクス LED の上の行から順に各 8 行に対応しています。 なお、これらのフォントデータを MAX7219 に送ると、該当位置の "1" のビットは LED が点灯し "0" のビットは消灯します。 ;========================================================================== ; 文字フォントデータテーブル ;========================================================================== font_data_tbl_0 retlw b'01111100' ;00: 0 retlw b'11000110' retlw b'11000110' retlw b'11000110' retlw b'11000110' retlw b'11000110' retlw b'11000110' retlw b'01111100' retlw b'00011000' ;01: 1 retlw b'00111000' retlw b'00011000' retlw b'00011000' retlw b'00011000' retlw b'00011000' retlw b'00011000' retlw b'00111100' retlw b'01111100' ;02: 2 retlw b'11000110' retlw b'11000110' retlw b'00001100' retlw b'00011000' retlw b'00110000' retlw b'01100000' retlw b'11111110' retlw b'01111100' ;03: 3 retlw b'11000110' retlw b'00001100' retlw b'00111000' retlw b'00001100' retlw b'11000110' retlw b'11000110' retlw b'01111100' retlw b'00011000' ;04: 4 retlw b'00111000' retlw b'01011000' retlw b'11011000' retlw b'11011000' retlw b'11111110' retlw b'00011000' retlw b'00011000' retlw b'11111100' ;05: 5 retlw b'11000000' retlw b'11000000' retlw b'11111100' retlw b'00000110' retlw b'00000110' retlw b'11000110' retlw b'01111100' retlw b'01111100' ;06: 6 retlw b'11000110' retlw b'11000000' retlw b'11111100' retlw b'11000110' retlw b'11000110' retlw b'11000110' retlw b'01111100' retlw b'11111110' ;07: 7 retlw b'11000110' retlw b'00000110' retlw b'00001100' retlw b'00011000' retlw b'00110000' retlw b'00110000' retlw b'00110000' retlw b'01111100' ;08: 8 retlw b'11000110' retlw b'11000110' retlw b'01111100' retlw b'11000110' retlw b'11000110' retlw b'11000110' retlw b'01111100' retlw b'01111100' ;09: 9 retlw b'11000110' retlw b'11000110' retlw b'11000110' retlw b'01111110' retlw b'00000110' retlw b'11000110' retlw b'01111100' 本機では、これらのフォントデータの読み出し方法を、本機の前身である "097. ドットマトリクス 8 x 8 LED 表示時計" から引き継いでいるのですが、 その前身では、LED に表示をする文字種としては 16 種類にも満たなかったため、指定が 4 ビットで十分でした。 ところが本機では、曜日の表示とか、エラーメッセージの表示とかの新たな機能を加えたために、 その文字種が大幅に増加をしてしまい、同じ指定方法では該当の文字の指定をすることができなくなってしまいました。 この 4 ビットで指定するのをもっとビット数を増やせば簡単なのですが、この 4 ビット指定を変更すると他のことにも影響が波及をするため、4 ビット指定のままでテーブルの種類(グループ)を増やすことで対応することにしました。 そのための情報を、変数 tbl_type で追加指定をします。 テーブルの種類(グループ)は現在3つあって 0 / 1 / 2 で指定をします。 ( 注.機能追加でテーブル 3 を追加した ( 2021/5/16 ) ) このサブルーチンを CALL する前にもう一つ指定をしておかなければならないことがあります。 それはフォントデータテーブルから読み出したデータ(1 フォント当たり 8 バイト)を、どこに格納をするかを FSR レジスタ に予め指定をしておきます。 本機では 上述 したように、データメモリ内に設けたバッファエリアを指定します。(下リストの上から数行下にある (例) を参照) このように、3つの指定をしてサブルーチン( font_read_buff )を CALL すると、W レジスタと変数 tbl_type で指定をしたフォントデータ(8 バイト)がテーブルから読み出され、1 バイトごとにビット列並びの逆変換をして、 FSR レジスタで指定をしたバッファエリアに格納がされます。 次に示すリストは、機能追加のために更新をした Ver.1.10 のものに差し替えをしました。 ( 2021/5/16 更新 ) ;-------------------------------------------------------------------------- ; 1文字フォントデータパタンの読み出しとビット列の反転後バッファへ格納 ; ( フォントデータ segment のビット列並びを逆に変換してバッファへ格納 ) ;-------------------------------------------------------------------------- ; 入力: W: 表示文字データ (h'x0'〜h'x9', h'xa'〜h'xf') ; tbl_type: フォントデータテーブルの種類 (0/1/2/3) ; 出力: fd_buff(8 byte): フォントデータ(ビット列並びを逆に変換) ; (例) movlw fd_buff ;フォントデータ格納バッファの先頭アドレス ; movwf FSR ;間接アドレスに設定 font_read_buff andlw h'0f' ;下位4ビットを取り出す movwf addr_low bcf STATUS,C rlf addr_low,F ;8倍する rlf addr_low,F rlf addr_low,F movf tbl_type,W btfss STATUS,Z ;フォントデータテーブルの種類 = 0 か? goto frbuf01 ;No movlw high font_data_tbl_0 movwf addr_high ;文字フォントデータの先頭 high アドレス movlw low font_data_tbl_0 goto frbuf04 frbuf01 movwf work1 ;ワーク decf work1,F btfss STATUS,Z ;フォントデータテーブルの種類 = 1 か? goto frbuf02 ;No movlw high font_data_tbl_1 movwf addr_high ;文字フォントデータの先頭 high アドレス movlw low font_data_tbl_1 goto frbuf04 frbuf02 decf work1,F btfss STATUS,Z ;フォントデータテーブルの種類 = 2 か? goto frbuf03 ;No movlw high font_data_tbl_2 movwf addr_high ;文字フォントデータの先頭 high アドレス movlw low font_data_tbl_2 goto frbuf04 frbuf03 ; decf work1,F ; btfss STATUS,Z ;フォントデータテーブルの種類 = 3 か? ; goto frbuf0x ;No movlw high font_data_tbl_3 movwf addr_high ;文字フォントデータの先頭 high アドレス movlw low font_data_tbl_3 frbuf04 addwf addr_low,F ;文字フォントデータの先頭 low アドレス btfsc STATUS,C ;オーバーフローした場合は incf addr_high,F ;文字フォントデータ high アドレスの更新 movlw 8 ; movwf lp_cnt2 ;ループカウンタ2(digit カウンタ) frbuf05 call font_read ;文字フォントデータの読み出し movwf work1 ;反転前のビット列 movlw high $ ;この場所の high アドレスを movwf PCLATH ;PCLATH に設定 movlw 8 ; movwf lp_cnt1 ;ループカウンタ1(segment カウンタ) frbuf06 rlf work1,F ;C bit を出力 rrf work2,F ;C bit を入力 decfsz lp_cnt1,F ;ループカウンタ1 - 1 = 0 か? goto frbuf06 ;No movf work2,W ;反転後のビット列 movwf INDF ;バッファへ格納 incf FSR,F ;間接アドレス +1 更新 incf addr_low,F ;文字フォントデータ low アドレスの更新 btfsc STATUS,Z ;オーバーフローした場合は incf addr_high,F ;文字フォントデータ high アドレスの更新 decfsz lp_cnt2,F ;ループカウンタ2 - 1 = 0 か? goto frbuf05 ;No return ;-------------------------------------------------------------------------- ; 文字フォントデータの読み出し ;-------------------------------------------------------------------------- ; 入力: addr_high: 文字フォントデータ high アドレス ; addr_low: 文字フォントデータ low アドレス ; 出力: W: 文字フォントデータ font_read movf addr_high,W ;文字フォントデータの high アドレス movwf PCLATH movf addr_low,W ;文字フォントデータの low アドレス movwf PCL フォントデータテーブルから読み出す6桁分の文字指定には、temp_hh、temp_mm、temp_ss の3つの変数で行います。 それぞれの1バイト(8 ビット)の変数を、上位 4 ビットと下位 4 ビットの2つずつに分けて、 2 x 3(変数) = 6 桁で指定をします(この 4 ビットというのが、前のサブルーチン( font_read_buff )のところで説明をした、フォントデータの指定のための 4 ビットです)。 また、temp_hh、temp_mm、temp_ss は汎用の変数で、 実際には時分秒、年月日、温度、湿度、エラーメッセージなどの情報を指定します。 また、変数 dspdigit は次のリストの数行下にコメントがあるように、dspdigit の各ビットがドットマトリクス LED の6桁と、コロン、ピリオドの LED に対応していて、0:無効/1:有効を決めています。 例えば、bit7 = 0 であった場合には "HH" 桁が無効ということで、変数 temp_hh の上位 4 ビットの指定は無視されます。 そして、"HH" 桁のフォントデータの読み出しは行われず、 バッファエリアの "HH" 桁に該当する 8 バイトのエリアすべてには h'00' データ が格納されます。 したがって、このサブルーチン( font_read6_buff )が実行されると、バッファエリア(48 バイト)のすべてに、読み出されたフォントデータ、もしくは h'00' データで満たされることになります。 なお、この変数 dspdigit は本サブルーチン( font_read6_buff )だけではなく、次のサブルーチン( dxled_display )にも使用されています。 ;========================================================================== ; 文字フォントデータの6桁分の読み出し ;========================================================================== ; 入力 dspdigit: (xx:xx:xx) ; bit7: "HH" 桁の指定 ; bit6: "HL" 桁の指定 ; bit5: "MH" 桁の指定 ; bit4: "ML" 桁の指定 ; bit3: "SH" 桁の指定 ; bit2: "SL" 桁の指定 ; bit1: :("Colon") の指定 ; bit0: .("Period")の指定 ; temp_hh: 仮の時 BCD カウンタ ; temp_mm: 仮の分 BCD カウンタ ; temp_ss: 仮の秒 BCD カウンタ ; 出力: fd_buff(8 x 6 = 48 byte): フォントデータ font_read6_buff movlw fd_buff ;フォントデータ格納バッファの先頭アドレス movwf FSR ;間接アドレスの先頭番地 btfss dspdigit,7 ;"HH" 桁 = 1 か? goto fr6buf01 ;No swapf temp_hh,W call font_read_buff ;1文字フォントデータパタンの読み出しとビット列変換 goto fr6buf02 fr6buf01 call zero_clear ;ゼロクリア fr6buf02 btfss dspdigit,6 ;"HL" 桁 = 1 か? goto fr6buf03 ;No movf temp_hh,W call font_read_buff ;1文字フォントデータパタンの読み出しとビット列変換 goto fr6buf04 fr6buf03 call zero_clear ;ゼロクリア fr6buf04 btfss dspdigit,5 ;"MH" 桁 = 1 か? goto fr6buf05 ;No swapf temp_mm,W call font_read_buff ;1文字フォントデータパタンの読み出しとビット列変換 goto fr6buf06 fr6buf05 call zero_clear ;ゼロクリア fr6buf06 btfss dspdigit,4 ;"ML" 桁 = 1 か? goto fr6buf07 ;No movf temp_mm,W call font_read_buff ;1文字フォントデータパタンの読み出しとビット列変換 goto fr6buf08 fr6buf07 call zero_clear ;ゼロクリア fr6buf08 btfss dspdigit,3 ;"SH" 桁 = 1 か? goto fr6buf09 ;No swapf temp_ss,W call font_read_buff ;1文字フォントデータパタンの読み出しとビット列変換 goto fr6buf10 fr6buf09 call zero_clear ;ゼロクリア fr6buf10 btfss dspdigit,2 ;"SL" 桁 = 1 か? goto fr6buf11 ;No movf temp_ss,W call font_read_buff ;1文字フォントデータパタンの読み出しとビット列変換 goto fr6buf12 fr6buf11 call zero_clear ;ゼロクリア fr6buf12 ;; return goto dxdsp00 ;** スタック改善のため: return → goto ** ;-------------------------------------------------------------------------- ; ゼロクリア zero_clear movlw 8 movwf lp_cnt2 ;ループカウンタ zclr01 clrw movwf INDF ;バッファへ格納 incf FSR,F ;間接アドレス +1 更新 decfsz lp_cnt2,F ;ループカウンタ - 1 = 0 か? goto zclr01 ;No return 1番目の目的としては、フォントデータはバッファエリア(48 バイト)にすべてが揃っているので、それらのフォントデータを効率よく MAX7219 に送るように、6桁分の MAX7219 の同一番号の桁(Digit)レジスタに、 それぞれのセグメントデータを送って受け取らせ、この操作を桁レジスタの Digit7 から Digit0 まで8回繰り返しています。 2番目の目的としてのコロン(またはピリオド)用の LED の表示制御には、前のサブルーチン( font_read6_buff )で説明をした 変数 dspdigit の bit1 ("Colon" 制御) と bit0 ("Period" 制御) で行っています。 なお、これらの LED の表示制御は、MAX7219 の制御とは関係がなく、PIC の4つの出力ポートを制御することで直接行っています。 NORMAL モード時の時間表示(12H/24H)、年月日(曜日)表示、温度表示、湿度表示、また SET モード時の時間表示(12H/24H)、年月日表示、またはエラーメッセージの表示時には、それぞれでコロン(またはピリオド)用の LED の表示パタンが異なるため、 変数 dspdigit の他に、time_flg、mod_flg、int_flg、exp_flg などの変数の状態も参照しながら、このサブルーチンの後半部分でそれぞれの場合分けの表示制御を行います。 次に示すリストは、機能追加のために更新をした Ver.1.10 のものに差し替えをしました。 ( 2021/5/16 更新 ) ;========================================================================== ; ドットマトリクス LED 6桁分の表示 ;========================================================================== dxled_display ;; call font_read6_buff ;文字フォントデータの6桁分の読み出し goto font_read6_buff ;**スタック改善のため** dxdsp00 movf mod_cnt,W btfsc STATUS,Z ;モードNo表示時間カウンタ = 0 か? goto dxdsp02 ;Yes ; バッファ "HH" 桁をモードNoに置き換え movlw fd_buff ;フォントデータ格納バッファ "HH" 桁の先頭アドレス movwf FSR ;間接アドレスの先頭番地 movlw 8 ; movwf mlpcnt2 ;ループカウンタにセット dxdsp01 incf mod_flg,W subwf mlpcnt2,W ;Z フラグだけが必要 movlw h'00' ;h'00' btfsc STATUS,Z ;ループカウンタ = mod_flg + 1 か? movlw h'ff' ;Yes movwf INDF ;バッファ "HH" 桁を置き換え incf FSR,F ;間接アドレス +1 更新 decfsz mlpcnt2,F ;ループカウンタ - 1 = 0 か? goto dxdsp01 ;No ; フォントデータを6桁分表示 dxdsp02 movlw fd_buff + 8 * 5 ;フォントデータ格納バッファ"SL" 桁の先頭アドレス movwf FSR ;間接アドレスの先頭番地 movwf fsr_mem ;フォントデータバッファのアドレス記憶 movlw h'08' ;Digit = 7 movwf digit ;Digit の初期値 dxdsp03 bcf PORTC,maxLOAD ;maxLOAD="L" movlw 6 ; movwf mlpcnt1 ;ループカウンタにセット dxdsp04 movf digit,W ;Digit = X call xmax_byte_send ;;レジスタアドレス movf INDF,W ;バッファから読み出し call xmax_byte_send ;;レジスタデータ movlw 8 subwf FSR,F ;間接アドレスを1つ前の桁 decfsz mlpcnt1,F ;6桁分表示した か? goto dxdsp04 ;No bsf PORTC,maxLOAD ;maxLOAD="H" incf fsr_mem,F ;バッファアドレスの記憶更新 movf fsr_mem,W movwf FSR ;間接アドレス +1 更新 decfsz digit,F ;全 Digit 表示した か? goto dxdsp03 ;No ; :("Colon") / .("Period") の LED表示 ; PC7, PC6, PC5, PC4, PC3, PC2, PC1, PC0 ... (ポート番号) ; LU, LL, RU, RL, SW5, CLK, LOAD,DIN ... (信号名) movlw h'07' andwf PORTC,F ;下位 3bit だけをそのまま残す btfss dspdigit,1 ;表示桁指定フラグ.1 ("Colon") = ON か? goto dxdsp06 ;No ; :("Colon") の LED表示 movlw h'c0' ;:("Colon")指定, (LU, LL, xx, xx) btfsc exp_flg,7 ;メモリの内容表示フラグ = ON か? goto dxdsp08 ;Yes movlw h'f0' ;:("Colon")指定, (RL, RU, LL, LU) btfss time_flg,1 ;12時間表示 か? goto dxdsp08 ;No movlw 5 subwf mod_flg,W btfss STATUS,Z ;MODEフラグ = 5(SET モード) か? goto dxdsp05 ;No movlw h'b0' ;am指定, :("Colon")左列上側 + 右列上下 LED点灯, (LU, xx, RU, RL) btfss time_flg,1 ;pm表示 か? goto dxdsp08 ;No movlw h'70' ;pm指定, :("Colon")左列下側 + 右列上下 LED点灯, (xx, LL, RU, RL) goto dxdsp08 dxdsp05 movlw h'f0' ;:("Colon")指定, (RL, RU, LL, LU) btfss int_flg,1 ;12時間表示時の AM/PM 表示点滅タイミング goto dxdsp08 ;No movlw h'70' ;am指定, :("Colon")左列上側 LED点滅, (xx, LL, RU, RL) btfss time_flg,0 ;pm表示 か? goto dxdsp08 ;No movlw h'b0' ;pm指定, :("Colon")左列下側 LED点滅, (LU, xx, RU, RL) goto dxdsp08 ; .("Period") の LED表示 dxdsp06 movlw h'00' ;無表示指定, (xx, xx, xx, xx) btfss dspdigit,0 ;表示桁指定フラグ.0 ("Period") = ON か? goto dxdsp08 ;No. 無表示 movlw 5 subwf mod_flg,W btfsc STATUS,Z ;MODEフラグ = 5(SET モード) か? goto dxdsp07 ;Yes movlw h'40' ;.("Period")指定, (xx, LL, xx, xx) btfsc exp_flg,7 ;メモリの内容表示フラグ = ON か? goto dxdsp08 ;Yes ; movlw h'40' ; 左. ("Period")指定, (xx, LL, xx, xx) ; btfss dspdigit,3 ;表示桁指定フラグ.3 ("SH") = ON か? ; goto dxdsp08 ;No movlw h'10' ; 右 .("Period")指定, (xx, xx, xx, RL) btfss dspdigit,6 ;表示桁指定フラグ.6 ("HL") = ON か? goto dxdsp08 ;No. ... 気温(__ xx.x℃), 湿度(__ xx.x%) dxdsp07 movlw h'50' ; 両..("Period")指定, (xx, LL, xx, RL) ... 年月日(xx.xx.xx), Ver(xx._x.xx) dxdsp08 xorlw h'f0' ;ビット反転 iorwf PORTC,F ; return |
| プログラムのトップに戻る |
● リアルタイムクロックモジュールのプログラミング リアルタイムクロックモジュールには、前作 "190. I2C IF モジュール + LCD(1602A) 表示時計" と同様に "DS3231" を採用しました。 したがって、そのプログラミングについては前作の "リアルタイムクロックモジュールのプログラミング" の項で詳細に述べているので、そちらを参照してください。 ただし、前作の "190. I2C IF モジュール + LCD(1602A) 表示時計" では、 I2C IF モジュールで使用されている IC "PCF8574" が、SCL クロックが 100 kHz の Standard mode でないと動作ができないため、 この RTC(DS3231)モジュールにも "PCF8574" と同じ "I2C サブルーチン( ファイル: I2C_XS_100K.sub )" を使用していました。 しかし、RTC IC "DS3231" は "PCF8574" とは異なって、SCL クロックが 400 kHz の Fast mode を備えているので、 IC "PCF8574" を使用しない本機では、"I2C サブルーチン( ファイル: I2C_XS_100K.sub )" を Fast mode にも対応できるように書き換えて( ファイル: I2C_XS.sub に変更 )使用をしました。
ところが、このように一旦は Fast mode に対応できるように変更をしたのですが、機能追加のために Standard mode でないと動作ができない、I/O エキスパンダ IC の PCF8574/74A を使用することになったために、 "I2C サブルーチン( ファイル: I2C_XS.sub )" も Standard mode で使用をすることになりました。 #define TRISX TRISB ;PORTB で I2C を行う場合、 #define PORTX PORTB ; #define _not_wait ;Fast mode で使用 include D:\Project\PIC\_sub\I2C_XS.subのように、include する側で #define 定義をすればOKです。 なお、3行目の #define 定義がない場合は、従来通りの Standard mode として機能をします。 ;========================================================================== ; I2C (SCLクロック周波数 = 100/400 KHz) サブルーチン(ソフトウエア I2C 版) ;========================================================================== ;#define TRISX TRISC ;例. PORTC で I2C を行う場合は、 ;#define PORTX PORTC ;include する側で #define 定義をすること ;#define _not_wait ;Fast mode で使用する場合に定義をする ;<使用レジスタ> ;i2ctmp ;送受信データの一時保存 ;bit_cnt ;ループカウンタ ;-------------------------------------------------------------------------- ; ウェイト・ルーチン (8 MHz のとき) ;-------------------------------------------------------------------------- #ifndef _not_wait i2c_wait goto $+1 ;2 goto $+1 ;2 goto $+1 ;2 ;; goto $+1 ;2 return ;2 ;Total 10/8MHz*4 = 5 μS #endif ;-------------------------------------------------------------------------- ; スタートコンディション ;-------------------------------------------------------------------------- i2c_start bsf PORTX,SCL ;SCL="H" bsf PORTX,SDA ;SDA="H" #ifndef _not_wait call i2c_wait #endif bcf PORTX,SDA ;SDA="L" #ifndef _not_wait call i2c_wait #endif bcf PORTX,SCL ;SCL="L" return ;-------------------------------------------------------------------------- ; ストップコンディション ;-------------------------------------------------------------------------- i2c_stop bcf PORTX,SDA ;SDA="L" bsf PORTX,SCL ;SCL="H" #ifndef _not_wait call i2c_wait #endif bsf PORTX,SDA ;SDA="H" return ;-------------------------------------------------------------------------- ; データ 1 バイト送信 ;-------------------------------------------------------------------------- ;使用レジスタ: i2ctmp, bit_cnt ;入力: W reg: 送信データ i2c_byte_send movwf i2ctmp ;Wレジスタの内容を一時保存 movlw 8 movwf bit_cnt ;ループカウンタにセット bsend01 bcf PORTX,SDA ;SDA="L" btfsc i2ctmp,7 ;送り出しデータの 7ビット目 = 1 か? bsf PORTX,SDA ;Yes, SDA="H" #ifndef _not_wait call i2c_wait #endif bsf PORTX,SCL ;SCL="H" #ifndef _not_wait call i2c_wait #endif bcf PORTX,SCL ;SCL="L" rlf i2ctmp,F ;送り出しデータを 1ビット左にシフト decfsz bit_cnt,F ;8ビット送信した か? goto bsend01 ;No ; 1 バイト送信後の ACK チェック bsf STATUS,RP0 ;バンク 1 bsf TRISX,SDA ;SDA: 入力ポート bcf STATUS,RP0 ;バンク 0 #ifndef _not_wait call i2c_wait #endif bsf PORTX,SCL ;SCL="H" #ifndef _not_wait call i2c_wait #endif nop ;ACK のチェックをしない bcf PORTX,SCL ;SCL="L" bsf STATUS,RP0 ;バンク 1 bcf TRISX,SDA ;SDA: 出力ポート bcf STATUS,RP0 ;バンク 0 return ;-------------------------------------------------------------------------- ; データ 1 バイト受信 ;-------------------------------------------------------------------------- ;使用レジスタ: i2ctmp, bit_cnt ;出力: W reg: 受信データ i2c_byte_reciv movlw 8 movwf bit_cnt ;ループカウンタにセット bsf STATUS,RP0 ;バンク 1 bsf TRISX,SDA ;SDA: 入力ポート bcf STATUS,RP0 ;バンク 0 brecv01 #ifndef _not_wait call i2c_wait #endif bsf PORTX,SCL ;SCL="H" bcf STATUS,C ;C=0 btfsc PORTX,SDA ;受け取り SDA = "H" か? bsf STATUS,C ;Yes, C=1 rlf i2ctmp,F ;受け取りデータを 1ビット左にシフト #ifndef _not_wait call i2c_wait #endif bcf PORTX,SCL ;SCL="L" decfsz bit_cnt,F ;8ビット受信した か? goto brecv01 ;No bsf STATUS,RP0 ;バンク 1 bcf TRISX,SDA ;SDA: 出力ポート bcf STATUS,RP0 ;バンク 0 movf i2ctmp,W ;W reg = 受信データ return ;-------------------------------------------------------------------------- ; ACK 応答 ;-------------------------------------------------------------------------- i2c_ack bcf PORTX,SDA ;SDA="L" bsf PORTX,SCL ;SCL="H" #ifndef _not_wait call i2c_wait #endif bcf PORTX,SCL ;SCL="L" bsf PORTX,SDA ;SDA="H" return ;-------------------------------------------------------------------------- ; NACK 応答 ;-------------------------------------------------------------------------- i2c_nack bsf PORTX,SDA ;SDA="H" bsf PORTX,SCL ;SCL="H" #ifndef _not_wait call i2c_wait #endif bcf PORTX,SCL ;SCL="L" return ;=============================================================== end ====== |
| プログラムのトップに戻る |
● デジタル温度湿度センサーモジュールのプログラミング デジタル温度湿度センサーモジュールについても、前作 "190. I2C IF モジュール + LCD(1602A) 表示時計" と同様に "DHT22(AM2302)" を採用しました。 したがって、そのプログラミングについても前作の "デジタル温度湿度センサーモジュールのプログラミング" の項で詳細に述べていますので、そちらを参照してください。 |
| プログラムのトップに戻る |
― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ―
● 機能追加: タイマースイッチ機能、温度スイッチ機能 ( 2021/5/16 追加 ) ◎ I/O エキスパンダ IC(PCF8574/74A)のプログラミング 今回、私が新たに作成をした I/O エキスパンダ IC の PCF8574/74A 用としての、基本的なサブルーチンを次に示します。 見ての通り至って簡単なもので、PCF8574/74A の I/O ポート(全 8 ポート)に対して、 ポートごとのビット単位での読み書きはできないので、全ポートをまとめてバイト単位での読み書きを行います。 これらのサブルーチン内で使用をしている i2c_start, i2c_stop, i2c_byte_send, i2c_byte_reciv, i2c_nack の各サブルーチンは、既に上で説明をした "I2C サブルーチン( ファイル: I2C_XS.sub )" 内に収容がされているものです。 ;========================================================================== ; I/O エキスパンダ IC(PCF8574/74A)のポートとのデータ入出力 ;========================================================================== ;ExpAddr equ h'20' ; I/O エキスパンダ IC(PCF8574) のスレーブアドレス ExpAddr equ h'38' ; I/O エキスパンダ IC(PCF8574A)のスレーブアドレス ;<使用外部ルーチン> ;(i2c_start, i2c_stop, i2c_byte_send, i2c_byte_reciv, i2c_nack) ;<使用レジスタ> ;exptmp ;書き込み/読み出しデータ ;-------------------------------------------------------------------------- ; (PCF8574/74A)のポートへデータを書き込み ;-------------------------------------------------------------------------- ExpPCF8574_Write call i2c_start ;スタートコンディション movlw ExpAddr << 1 ;スレーブアドレス call i2c_byte_send ;1 バイト送信 movf exptmp,W ;書き込みデータ call i2c_byte_send ;1 バイト送信 call i2c_stop ;ストップコンディション return ;-------------------------------------------------------------------------- ; (PCF8574/74A)のポートからデータを読み出し ;-------------------------------------------------------------------------- ExpPCF8574_Read call i2c_start ;スタートコンディション movlw (ExpAddr << 1) + 1 ;スレーブアドレス call i2c_byte_send ;1 バイト送信 call i2c_byte_reciv ;1 バイト受信 movwf exptmp ;読み出しデータ call i2c_nack ;NACK call i2c_stop ;ストップコンディション return まず初めに、本機の起動直後に PCF8574/74A の入出力ポートの初期設定として、次のように入力ポートには "1" を、出力ポートには "0" を書き込むと同時に、それらのポート状態を記憶(変数 exp_port)しておきます。 このように、PCF8574/74A では入出力ポートの切り替えは必要ありません。 bsf PORTB,SDA ;SDA="H" bsf PORTB,SCL ;SCL="H" movlw h'0f' movwf exp_port ;初期値 movwf exptmp ;拡張ポートのリレー制御データ call ExpPCF8574_Write ;(PCF8574/74A)の拡張ポートを初期設定そして、以降のプログラムの中で PCF8574/74A の入力ポートに収容の、各スイッチの状態を把握することが必要になった場面で、 call ExpPCF8574_Read ;(PCF8574/74A)の拡張ポートからデータを読み出し movlw h'0f' andwf exptmp,W ;スイッチデータを取り出し xorlw h'0f' ;反転する btfss STATUS,Z ;どれかスイッチ = ON か? goto ..... ;Yes goto ..... ;Noのように、入力ポートからスイッチ状態のデータを読み出し(変数 exptmp)て、適宜それらのポートに応じた処理をすることになります。 なお、本機では PIC に空きポートがないため、 PCF8574/74A から出力される INT 信号は使用していません。 また、出力ポートの制御では、タイマー & 温度の監視プログラムの中で、次のように該当のポート(リレー)だけを ON/OFF しています。 ; CH1 CH2 CH3 movf work1,W ;CH(SW) 番号対応データ(b'00010000' / b'00100000' / b'01000000') btfsc fmmwwwss,5 ;mm bit = x1: ON 出力 か? iorwf exp_port,F ;Yes. リレーを ON xorlw h'ff' btfss fmmwwwss,5 ;mm bit = x0: OFF 出力 か? andwf exp_port,F ;Yes. リレーを OFF movf exp_port,W ;拡張ポートのリレー制御データ movwf exptmp ;書き込みデータ call xExpPCF8574_Write ;;(PCF8574/74A)の拡張ポートへデータを書き込み以上のように、今回の機能追加のために使用をした、I/O エキスパンダ IC(PCF8574/74A)自体の制御プログラミングは至って簡単でした。 しかし、機能概要と使用法 の項の < 新しい機能 > で述べた、タイマースイッチ機能、温度スイッチ機能のユーザー・インターフェースとしての操作の、メモリ設定機能、メモリ読み出し機能、メモリ消去機能などの実現では、 かなり多くの時間を費やしました。 |
| プログラムのトップに戻る |
◎ PIC16F690 とページの問題 PIC16F ファミリーのプログラムメモリは 2K ワードずつのページ単位が、最大でページ 0 〜 3 までの4ページ(2K x 4 = 8K ワード)となるため、この全メモリをアドレス指定ができるように、 プログラムカウンタ PC は 13 ビットで構成されています。 (ただし、PIC16F690 ではページ 0 〜 1 の2ページだけが実装されています。) そして、GOTO, CALL を除く命令で、かつ PCL レジスタに書き込みをしない命令の場合には、プログラムカウンタ PC は1ずつインクリメントされて、プログラムメモリは次々に連続的に参照されて行きますが、 そうではない場合には、プログラムカウンタ PC は次図のように更新されて指定されたアドレスにジャンプをします。 右端の図は、プログラムカウンタ PC とスタックとの関係を表したもので、CALL 命令や割り込みが起こったときに、プログラムカウンタ PC の内容はスタックに PUSH されます。 そして、RETURN, RETLW, または RETFIE 命令でサブルーチンや割り込み処理ルーチンから、再び呼び出し元のプログラムに戻るときには、スタックしておいた内容をプログラムカウンタ PC に POP して復帰をします。 次に示すリストは、アセンブラソースプログラムをアセンブルしたときに作成される、アセンブルリストの一部を切り取ったもので、今回の機能追加を行った前後のプログラムメモリの使用マップと、 プログラムサイズを比較してみたものです。 上述のように PIC16F690 にはプログラムメモリが 4K ワード存在しますが、2K ワードを超えるような大きなプログラムを作成する場合には、前半(ページ 0 = アドレス: 0000 〜 07FF)の 2K ワードと、 後半(ページ 1 = アドレス: 0800 〜 0FFF)の 2K ワードとを、常に意識をしながらそれに対応させたプログラムを作成しなければなりません。
MEMORY USAGE MAP ('X' = Used, '-' = Unused) 0000 : X---XXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0040 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0080 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 00C0 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0100 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0140 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0180 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 01C0 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0200 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0240 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0280 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 02C0 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0300 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0340 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0380 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 03C0 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0400 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0440 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0480 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 04C0 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0500 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0540 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0580 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 05C0 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0600 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0640 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0680 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 06C0 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0700 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0740 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0780 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 07C0 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0800 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0840 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0880 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXX- ---------------- 2000 : -------X-------- ---------------- ---------------- ---------------- 2100 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX X--------------- ---------------- All other memory blocks unused. Program Memory Words Used: 2220 Program Memory Words Free: 1876
MEMORY USAGE MAP ('X' = Used, '-' = Unused) 0000 : X---XXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0040 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0080 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 00C0 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0100 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0140 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0180 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 01C0 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0200 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0240 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0280 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 02C0 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0300 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0340 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0380 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 03C0 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0400 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0440 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0480 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 04C0 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0500 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0540 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0580 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 05C0 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0600 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0640 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0680 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 06C0 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0700 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0740 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0780 : XXXXXXXXXXXXXXXX XX-------------- ---------------- ---------------- 0800 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0840 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0880 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 08C0 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0900 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0940 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0980 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 09C0 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0A00 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0A40 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0A80 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0AC0 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0B00 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0B40 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0B80 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0BC0 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0C00 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0C40 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0C80 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0CC0 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0D00 : XXXXXXXXXXXXXXXX ---------------- ---------------- ---------------- 2000 : -------X-------- ---------------- ---------------- ---------------- 2100 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX X--------------- ---------------- All other memory blocks unused. Program Memory Words Used: 3231 Program Memory Words Free: 865まず初めに、機能追加を行う前(Ver.1.01)のプログラムメモリの使用マップの、ページ 0 とページ 1 の境界辺りを再掲してみると、 : 0700 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0740 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0780 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 07C0 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX ← 07FF を超えたプログラム 0800 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0840 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0880 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXX- ---------------- :のように、境界の 07FF を超えてプログラムメモリが使用されているのが分かります。 そして、この辺りのメモリのプログラム分布を詳細にするために、その辺りのアセンブルリストを見てみると 次のようになっています。 リスト中の最右端に ;; で示したコメント (A), (B), (C), (D) のところに注目をしてください。 : 03075 03076 ;-------------------------------------------------------------------------- 03077 ; 1文字フォントデータパタンの読み出しとビット列の反転後バッファへ格納 03078 ; ( フォントデータ segment のビット列並びを逆に変換してバッファへ格納 ) 03079 ;-------------------------------------------------------------------------- 03080 03081 ; 入力: W: 表示文字データ (h'x0'〜h'x9', h'xa'〜h'xf') 03082 ; tbl_type: フォントデータテーブルの種類 (0/1/2) 03083 03084 ; 出力: fd_buff(8 byte): フォントデータ(ビット列並びを逆に変換) 03085 03086 ; (例) movlw fd_buff ;フォントデータ格納バッファの先頭アドレス 03087 ; movwf FSR ;間接アドレスに設定 03088 0714 03089 font_read_buff 0714 390F 03090 andlw h'0f' ;下位4ビットを取り出す 0715 00D5 03091 movwf addr_low 0716 1003 03092 bcf STATUS,C 0717 0DD5 03093 rlf addr_low,F ;8倍する 0718 0DD5 03094 rlf addr_low,F 0719 0DD5 03095 rlf addr_low,F 03096 071A 0858 03097 movf tbl_type,W 071B 1D03 03098 btfss STATUS,Z ;フォントデータテーブルの種類 = 0 か? 071C 2F21 03099 goto frbuf01 ;No 03100 071D 3007 03101 movlw high font_data_tbl_0 071E 00D4 03102 movwf addr_high ;文字フォントデータの先頭 high アドレス 071F 3047 03103 movlw low font_data_tbl_0 0720 2F2B 03104 goto frbuf03 03105 0721 0358 03106 frbuf01 decf tbl_type,W 0722 1D03 03107 btfss STATUS,Z ;フォントデータテーブルの種類 = 1 か? 0723 2F28 03108 goto frbuf02 ;No 03109 0724 3007 03110 movlw high font_data_tbl_1 0725 00D4 03111 movwf addr_high ;文字フォントデータの先頭 high アドレス 0726 30BF 03112 movlw low font_data_tbl_1 0727 2F2B 03113 goto frbuf03 03114 0728 3008 03115 frbuf02 movlw high font_data_tbl_2 0729 00D4 03116 movwf addr_high ;文字フォントデータの先頭 high アドレス 072A 303F 03117 movlw low font_data_tbl_2 03118 072B 07D5 03119 frbuf03 addwf addr_low,F ;文字フォントデータの先頭 low アドレス 072C 1803 03120 btfsc STATUS,C ;オーバーフローした場合は 072D 0AD4 03121 incf addr_high,F ;文字フォントデータ high アドレスの更新 03122 072E 3008 03123 movlw 8 ; 072F 00BD 03124 movwf lp_cnt2 ;ループカウンタ2(digit カウンタ) 03125 0730 2743 03126 frbuf04 call font_read ;文字フォントデータの読み出し ;; ... (A) 0731 00B9 03127 movwf work1 ;反転前のビット列 03128 0732 3007 03129 movlw high $ ;この場所の high アドレスを ;; ┐ 0733 008A 03130 movwf PCLATH ;PCLATH に設定 ;; ┘ ... (D) 0734 3008 03131 movlw 8 ; 0735 00BC 03132 movwf lp_cnt1 ;ループカウンタ1(segment カウンタ) 03133 0736 0DB9 03134 frbuf05 rlf work1,F ;C bit を出力 0737 0CBA 03135 rrf work2,F ;C bit を入力 0738 0BBC 03136 decfsz lp_cnt1,F ;ループカウンタ1 - 1 = 0 か? 0739 2F36 03137 goto frbuf05 ;No 03138 073A 083A 03139 movf work2,W ;反転後のビット列 073B 0080 03140 movwf INDF ;バッファへ格納 073C 0A84 03141 incf FSR,F ;間接アドレス +1 更新 03142 073D 0AD5 03143 incf addr_low,F ;文字フォントデータ low アドレスの更新 073E 1903 03144 btfsc STATUS,Z ;オーバーフローした場合は 073F 0AD4 03145 incf addr_high,F ;文字フォントデータ high アドレスの更新 0740 0BBD 03146 decfsz lp_cnt2,F ;ループカウンタ2 - 1 = 0 か? 0741 2F30 03147 goto frbuf04 ;No 03148 0742 0008 03149 return 03150 03151 ;-------------------------------------------------------------------------- 03152 ; 文字フォントデータの読み出し 03153 ;-------------------------------------------------------------------------- 03154 03155 ; 入力: addr_high: 文字フォントデータ high アドレス 03156 ; addr_low: 文字フォントデータ low アドレス 03157 ; 出力: W: 文字フォントデータ 03158 0743 03159 font_read 0743 0854 03160 movf addr_high,W ;文字フォントデータの high アドレス ;; ┐ 0744 008A 03161 movwf PCLATH ;; ┘ ... (B) 0745 0855 03162 movf addr_low,W ;文字フォントデータの low アドレス 0746 0082 03163 movwf PCL 03164 03165 ;========================================================================== 03166 ; 文字フォントデータテーブル 03167 ;========================================================================== 03168 0747 03169 font_data_tbl_0 0747 347C 03170 retlw b'01111100' ;00: 0 0748 34C6 03171 retlw b'11000110' 0749 34C6 03172 retlw b'11000110' 074A 34C6 03173 retlw b'11000110' 074B 34C6 03174 retlw b'11000110' 074C 34C6 03175 retlw b'11000110' 074D 34C6 03176 retlw b'11000110' 074E 347C 03177 retlw b'01111100' : (省略) : 07F7 3400 03371 retlw b'00000000' ;07: 07F8 34DC 03372 retlw b'11011100' 07F9 3412 03373 retlw b'00010010' 07FA 3412 03374 retlw b'00010010' 07FB 3492 03375 retlw b'10010010' 07FC 3412 03376 retlw b'00010010' 07FD 3412 03377 retlw b'00010010' 07FE 34DC 03378 retlw b'11011100' 03379 ;; ... (C) 07FF 3400 03380 retlw b'00000000' ;08: "THU" ;; ← 07FF(ページ 0) ┐境界 0800 34FA 03381 retlw b'11111010' ;; ← 0800(ページ 1) ┘ 0801 3422 03382 retlw b'00100010' 0802 3422 03383 retlw b'00100010' 0803 3423 03384 retlw b'00100011' 0804 3422 03385 retlw b'00100010' 0805 3422 03386 retlw b'00100010' 0806 3422 03387 retlw b'00100010' 03388 0807 3400 03389 retlw b'00000000' ;09: 0808 3452 03390 retlw b'01010010' 0809 3452 03391 retlw b'01010010' 080A 3452 03392 retlw b'01010010' 080B 34D2 03393 retlw b'11010010' 080C 3452 03394 retlw b'01010010' 080D 3452 03395 retlw b'01010010' 080E 344C 03396 retlw b'01001100' :このように、PCLATH レジスタの操作に関連して 常に意識をした配慮が必要です。 次に、機能追加を行った後(Ver.1.10)のプログラムメモリの使用マップとサイズの、ページ 0 とページ 1 の境界辺り以降を再掲してみると、 0700 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0740 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0780 : XXXXXXXXXXXXXXXX XX-------------- ---------------- ---------------- ← ページ 0 には 110 ワードほどの空きができた 0800 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0840 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0880 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 08C0 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0900 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0940 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0980 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 09C0 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0A00 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0A40 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0A80 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0AC0 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0B00 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0B40 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0B80 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0BC0 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0C00 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0C40 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0C80 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0CC0 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX 0D00 : XXXXXXXXXXXXXXXX ---------------- ---------------- ---------------- 2000 : -------X-------- ---------------- ---------------- ---------------- 2100 : XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX X--------------- ---------------- All other memory blocks unused. Program Memory Words Used: 3231 Program Memory Words Free: 865のように、ページ 0 には 110 ワードほどの空きができ、ページ 1 ではその使用サイズが大幅に増加をしています。 これは今後プログラムの変更をしたいような場合に、ある程度(変更による増加分)の空きがないと 変更することができなくなる事態が発生する(かもしれない)ために、機能追加を行う前(Ver.1.01)のプログラムの一部を積極的に ページ 0 から ページ 1 に移動をさせた結果と、新たに機能追加をしたプログラム自体も約 1,000 ワードほどを 占めているためです。 また、この ページ 0 から ページ 1 に移動をさせる場合には、単純に移動をさせれば良いというものではなく、両ページ間には常にページ問題が発生するため、プログラムはそれを考慮したものに変更をしなければなりません。 例えば、次のプログラムリストのような配慮が必要です。 元々ページ 0 にあったサブルーチン time_display, date_display, temp_display, humi_display をページ 1 に移動をさせたとします。 そして、移動先のページ 1 側から同サブルーチンを CALL をする場合には、当然ながら何の問題も起こりませんが、逆にページ 0 側から移動をしたそれらのサブルーチンを従来通りそのまま CALL をすると、即、プログラムは暴走につながります。 そこで、それを回避するためにはワンクッションを置くように、新たなサブルーチン xtime_display, xdate_display, xtemp_display, xhumi_display をページ 0 側に作成をして、ページ 0 側からはそれらの新たなサブルーチンを CALL するようにします。 また逆に、ページ 1 側からページ 0 にあるサブルーチン inc_bcd, dec_bcd, bindec16c_cnv を CALL する場合にも同様の配慮が必要です。 inc_bcd : : return dec_bcd : : return bindec16c_cnv : : return : : xtime_display ;; bsf PCLATH,3 ;Page 1 ;; call time_display ; ;; bcf PCLATH,3 ;Page 0 ;; return ;; ;; xdate_display ;; bsf PCLATH,3 ;Page 1 ;; call date_display ; ;; bcf PCLATH,3 ;Page 0 ;; return ;; ページ 0 側からページ 1 にある ;; xtemp_display ;; time_display, date_display, temp_display, humi_display bsf PCLATH,3 ;Page 1 ;; call temp_display ; ;; を CALL する場合に必要な措置 bcf PCLATH,3 ;Page 0 ;; return ;; ;; xhumi_display ;; bsf PCLATH,3 ;Page 1 ;; call humi_display ; ;; bcf PCLATH,3 ;Page 0 ;; return ;; : : ;============================================================================================================= org h'800' ;(Page 1) ;============================================================================================================= xinc_bcd ;; bcf PCLATH,3 ;Page 0 ;; call inc_bcd ; ;; bsf PCLATH,3 ;Page 1 ;; return ;; ;; xdec_bcd ;; ページ 1 側からページ 0 にある bcf PCLATH,3 ;Page 0 ;; call dec_bcd ; ;; inc_bcd, dec_bcd, bindec16c_cnv bsf PCLATH,3 ;Page 1 ;; return ;; を CALL する場合に必要な措置 ;; xbindec16c_cnv ;; bcf PCLATH,3 ;Page 0 ;; call bindec16c_cnv ; ;; bsf PCLATH,3 ;Page 1 ;; return ;; : : time_display : : return date_display : : return temp_display : : return humi_display : : return( 2021/12/7 補記 ) 上述の方法を使用した場合には、新たなサブルーチンを介して本来のサブルーチンを CALL するために、スタックが1つ余分に消費されることになります。 PIC にはスタックが8ワードしか存在しないため、ネストを重ねるようなサブルーチンの場合には、 すぐにスタックがオーバーフローをしてしまうこともあるために、例えば、ページ 0 側からページ 1 側にあるサブルーチンを CALL をする場合には、 : : call xtime_display ; ;; 上述の方法 : :とするよりも : : bsf PCLATH,3 ;Page 1 ;; ページ 0 側からページ 1 にある call time_display ; ;; time_display bcf PCLATH,3 ;Page 0 ;; を CALL する場合 : :のように、ページ 0 のプログラムの中でワンクッションを置かないで、CALL をする目的のサブルーチンの前後でページの切り替えを行って直接 CALL をした方が、スタックの消費の面からは有利になります。 (この方が一般的かもしれません。 ただし、使用頻度が多いサブルーチンの場合には、上述の方が使用プログラムメモリ量の面からは有利。) |
| プログラムのトップに戻る |
◎ PIC16F690 とバンクの問題 前項ではプログラムメモリのページ間の問題を取り上げましたが、PIC16F ファミリーにはデータメモリであるバンクの問題も付き纏います。 プログラムで扱う変数が少ない小さなプログラムでは通常は問題が起こるようなことはありませんが、 本機のように多くのデータメモリを必要とするようなプログラムでは、常に意識をしてプログラムを作成しなければなりません。 本機で使用の PIC16F690 では下図の右側に示すように、データメモリの内、薄黄色の部分が汎用レジスタ(GPR)としてユーザが自由に使用できる RAM メモリで、機能追加を行った後(Ver.1.10)の汎用レジスタ(GPR)の使用状況を表しています。 上図に示すように、PIC16F ファミリーのデータメモリはバンク 0 〜バンク 3 の4つに分かれていますが、その内、PIC16F690 の汎用レジスタ(GPR)としては、バンク 0 〜バンク 2 の3つにしか存在しません。 次のリストはアセンブルリストの一部で、上図の水色で示した各変数やバッファなどを、実際のレジスタに割付けた部分を抜粋したものですが、リスト中の最右端に ;; <<<<< のコメントを付した行と、 最左端の列のレジスタアドレスに注目をして見てください。 : 00464 cblock h'20' ;(バンク 0) ;; <<<<< 00000020 00465 int_flg ;外部(RA2/INT)割り込みフラグ 00466 ;bit0: RTC DS3231 からデータ読み出し要求フラグ 00467 ;bit1: DHT22 からデータ読み出し要求フラグ, (取消変更)* 00468 ;bit1-2: 4 秒カウンタ, (変更)* 00469 ;bit3: DHT22 からデータ読み出し要求フラグ, (変更)* 00000021 00470 bcd_yh ;年(上位2桁) BCD カウンタ 00000022 00471 bcd_yl ;年(下位2桁) BCD カウンタ : (省略) : 0000006A 00561 i2ctmp ;送受信データの一時保存 0000006B 00562 bit_cnt ;ループカウンタ 00563 endc 00564 00565 cblock h'80' - 4 0000007C 00566 w_save ;W レジスタの退避用 0000007D 00567 s_save ;STATUS レジスタの退避用 0000007E 00568 f_save ;FSR レジスタの退避用 0000007F 00569 p_save ;PCLATH レジスタの退避用 00570 endc 00571 00572 cblock h'a0' ;(バンク 1) ;; <<<<< 00573 ; (I2C_RTC.sub で使用) 000000A0 00574 seconds ;00h: Seconds: 00-59 000000A1 00575 minutes ;01h: Minutes: 00-59 : (省略) : 000000B1 00591 temp_m ;11h: MSB of Temp 000000B2 00592 temp_l ;12h: LSB of Temp 00593 00594 ; (フォントデータのバッファエリア) 000000B3 00595 fd_buff ;(8 x 6 = 48) バイトを使用 00596 endc 00597 00598 cblock h'120' ;(バンク 2) ;; <<<<< 00000120 00599 timer_mem ;タイマー用メモリの先頭 00600 ; (3 * 20 = 60 byte 使用) 00601 endc 00602 :余談ですが、ネット上で他の方が作成をしたアセンブラプログラムを見たときに、変数の定義に int_flg equ h'20' bcd_yh equ h'21' bcd_yl equ h'22' : :などと記載された例をよく見かけるのですが、変数が多数ある場合に既に定義をしてしまった後で、もしもその変数の順番を変更したいような場合にはどうするのでしょうか? やはり1行ずつアドレスを書き換えるのでしょうか? 変更をする変数がたくさんある場合には大変な作業になると思いますが ・・・ それともレジスタアドレスの昇順、降順などには拘りませんか? 私は、equ は定数の定義などに使用すべきで、変数の定義には上のアセンブルリストに示した例のように、cblock h'xx' 〜 endc で定義をした方が簡単だし、変数の順番変更などのときにも自由度が大きく作業が楽になると思います。 さて、話を戻して、上のアセンブルリストに示したように定義をした各変数(レジスタ)を、プログラムで使用するときには 直接アドレス指定 と 間接アドレス指定 の2つの方法があります。 次図は、Microchip Technology 社のデータシート(PIC16F685/687/689/690)から、STATUS レジスタの必要な部分だけを抜粋したものです。 上図において STATUS レジスタの青色に塗った左の3つのビットが、アドレス指定に関連するもので、RP0, RP1 が直接アドレス指定、IRP が間接アドレス指定、それぞれのときに参照されるビットです。 これらのビットとバンクが選択されるときの関係を、図に表したものを次に示しておきます。 なお、これらの図を作成するに当たっては、サイト "趣味の電子回路工作" さんの "PIC16F873のSFR説明(2)" にある図を参考にさせていただきました。 まず、直接アドレス指定 のときの例ですが、例えば、プログラムで bcf STATUS,RP1 bcf STATUS,RP0 ;バンク 0と書くと、それ以降のプログラムではすべてバンク 0 の変数(レジスタ)が参照されます。 したがって movwf int_flgとした場合には、W レジスタの内容がバンク 0 内に存在する変数(レジスタ)int_flg に書き込まれて、意図した通りの動作をしますが、 bcf STATUS,RP1 bsf STATUS,RP0 ;バンク 1と書くと、それ以降のプログラムではすべてバンク 1 の変数(レジスタ)が参照されるので、その後で movwf int_flgとしても、W レジスタの内容は変数(レジスタ)int_flg には書き込まれず、意図した通りの動作にはなりません。 しかも、W レジスタの内容はバンク 1 に定義してある変数(レジスタ)seconds に書き込まれてしまうので、 場合によっては大変なことになってしまいます。 なお、STATUS レジスタはその実態が1つしかないのですが、すべてのバンクで共通に指定をすることができる特殊なレジスタで、このようなレジスタは他にも FSR, PCLATH, INTCON など複数個が存在しています。 次に、間接アドレス指定 の例を示します。 次のプログラム例では、上に示した図 PIC16F690 のデータメモリ のバンク 2 内に定義をしてあるタイマー用メモリ(60 バイト)のすべてを、 h'00' クリアをして初期設定をしている例です。 このプログラム例のように、まず、タイマー用メモリ timer_mem が存在するバンクを STATUS レジスタの IRP ビット で指定をし、次に、その timer_mem の先頭アドレスを 間接アドレスレジスタ FSR にセットします。 そして目的とするレジスタの一連の操作( h'00' クリア)が済んだ後には、再び STATUS レジスタの IRP ビットを元の状態に戻しておくと、間違いが少なくなると思います。 ; bsf STATUS,IRP ;バンク 2,3(間接アドレス指定用) movlw low timer_mem ;タイマー用メモリの先頭アドレス movwf FSR movlw 3 * 20 movwf lp_cnt1 ;ループカウンタ varinit02 clrf INDF ;タイマー用メモリの初期設定 incf FSR,F ;間接アドレス +1 更新 decfsz lp_cnt1,F ;ループカウンタ - 1 = 0 か? goto varinit02 ;No bcf STATUS,IRP ;バンク 0,1(間接アドレス指定用)に戻す ;以上述べたように、直接アドレス指定と間接アドレス指定の使い分けは、一般的には、個々に名付けた変数(レジスタ)を個々にプログラムで操作をする場合には、直接アドレス指定 で、 連続してまとまった複数の変数(レジスタ)を連続的に操作をする場合には、間接アドレス指定 で扱うのが基本です。 |
| プログラムのトップに戻る |
◎ タイマーの設定とタイマー用メモリの構成 あらかじめ設定をしておいた時間(時分)になると、スイッチ(リレー)が ON/OFF/SLP 動作をする タイマースイッチ機能、また、温度の設定をしておくとその設定温度に達したときに、同様にスイッチ(リレー)が ON/OFF 動作をする 温度スイッチ機能 の2つを、新しい機能として本機に追加をしました。 これらのスイッチ機能のためのあらかじめ設定をした情報は、上に示した図 PIC16F690 のデータメモリ のバンク 2 内に定義がしてある、タイマー用メモリの中に記憶がされます。 次の図は、このタイマー用メモリを詳細に表したもので、1 ワード(1メモリ単位)を3バイト構成として、本機では 20 メモリ分、すなわち、20 ワード(3 バイト x 20 メモリ = 60 バイト)を用意しています。 そして、1 ワード(1メモリ単位)3バイトの各バイトを、先頭から、タイマー制御バイト、時_BCD カウンタバイト、分_BCD カウンタバイトと 名付けています。 先頭バイトである タイマー制御バイト の各ビットの意味、構成は図に示す通りですが、先頭の f ビット (メモリ有効フラグ) = 1 の場合には、その1メモリ単位(3バイト)に書かれているデータ内容すべてが 有効(使用中)であることを示しています。 逆に、f ビット (メモリ有効フラグ) = 0 の場合には、例え他のビットやバイトにデータ内容が残っていても、その1メモリ単位(3バイト)は無効(空き)であることを示し、 他のデータ内容で上書をしても構わないことを意味しています。 次のバイトの 時_BCD カウンタバイト は、タイマー時間(時分)の内の "時"(または、温度の整数部)を記憶しておくバイトであり、最後のバイトの 分_BCD カウンタバイト は、"分"(または、温度の小数部)を記憶しておくバイトです。 これらのバイト名の BCD とは、Binary Coded Decimal の略であり 2進化 10進数 を意味し、"時分" 4 桁の各桁を 4 ビットずつの 2進数にして記憶をしています。 なお、これらのタイマー用メモリの考え方は、私の他のページ "181. TMS1121 デジタルタイマー・シミュレータ" のプログラムを作成したときに、私独自の考え方の基に創り出したもので、 これらのメモリに対する各種のプログラムのアクセス方法(書き込み、読み出し、内容消去、監視プログラムでの検索など)を、そちらのページで詳述していますので、興味のある方は是非ともそちらをご覧になってみて下さい。 その "181. TMS1121 デジタルタイマー・シミュレータ" のプログラムでは、4個あるリレー(スイッチ)を自由に ON / OFF 制御(設定)する
|
| プログラムのトップに戻る |
◎ 機能追加で発生した不具合 今回の機能追加のプログラム作成の中で、メモリ設定機能の中のほんの一部のところで不具合が発生し、かなり多くの時間ロスがありました。 何と 10 日ほど格闘をし続けたのですが残念ながら今なお解決をしておりません。 今は代替の方法でとりあえず不具合を回避していますが、この代替の方法では私の本意ではなく不満やるせない気持ちです。 具体的に述べると、タイマースイッチや温度スイッチを機能させるためには、事前にそれらの情報を入力設定しておかなければなりません。 本機では メモリ設定機能 を使用してそれぞれ4つの項目を入力することになるのですが、 どのような形式で入力するのかを伝えるために、マトリクス LED には "xx:xx xx" と表示がされます。 しかし、そのままではどの桁位置の項目に入力をすればよいのかが分かりません。 そこで次のように、 "~~" と記した桁位置のところの 数字を点滅表示 をさせて、ここがこれから入力をする項目なのだとユーザに知らせます。 そして、4つの項目ごとに点滅表示をする桁位置が移動をします。 [ xx:xx xx ] → [ xx:xx xx ] → [ xx:xx xx ] → [ xx:xx xx ] ~~ ~~ ~ ~ 今、私が分かっていることは、タイマー0割り込みを許可(T0IE=1)すると、その結果、INTCON(割り込み制御)レジスタの GIE(グローバル割り込みを許可/禁止)ビットがリセット(GIE=0)されて、 すべての割り込みが禁止状態になってしまう(らしい)、ということまでで、なぜこのようなことになってしまうのかが分からないのです。 本機のプログラムでは、起動直後の各種の初期化処理のところでグローバル割り込みを許可(GIE=1)していますが、それ以降は明示的なプログラムでの操作(GIE=0 または GIE=1)は一切しておりません。 したがって、T0IE=1 にすると GIE=0 になってしまう(らしい)ということは、何が原因なのかは分かりませんがプログラムが暴走をして、INTCON レジスタがオーバーライトされて GIE=0 にされてしまう(のではないか?)、 ということしか考えられません。 しかし、見かけ上ではプログラムが暴走をしているようにも見えないのですが ・・・ タイマー0割り込み処理のルーチン内では何を行っているかを一応次に示しておきますが、リストのように大したことを行っている訳ではありません。 変数 exp_flg の 6, 7 ビットを 200 mS ごとに設定をしているだけで、 なぜ、これが暴走につながるのか、が分からないのです。 ;---------------- タイマー0割り込み処理 ---------------------------------- ;(タイマー0を許可したときのみ: 割り込み周期 = 4/8Mz*16*250=2msec) t0_int bcf INTCON,T0IF ;タイマー0割込みフラグをクリア movlw tm0_h_val ;ハードタイマー0カウント値 = 250 を movwf TMR0 ;TMR0 に設定 decfsz tm0_s_cnt,F ;ソフトタイマー0カウンタ - 1 = 0 か? goto int_end ;No movlw 1 << 6 ;対象ビットをセット xorwf exp_flg,F ;反転表示フラグを 200 mS 毎にビット反転 bsf exp_flg,7 ;メモリの内容表示フラグ = ON movlw tm0_s_val ;ソフトタイマー0カウント値 = 100 を movwf tm0_s_cnt ;ソフトタイマー0カウンタに設定 goto int_endしかし、結果として上述したように、タイマー0割り込みを許可(T0IE=1)すると、グローバル割り込みビットがリセット(GIE=0)されて、すべての割り込みが禁止状態になってしまう(らしい)ので、 現在は次のリストのように、該当の (1) 行をコメント行にした代わりに (2)、(3) を追加して、タイマー0割り込みの処理の代替をさせています。 : : movlw tm0_h_val ;ハードタイマー0カウント値を movwf TMR0 ;TMR0 に設定 movlw tm0_s_val ;ソフトタイマー0カウント値を movwf tm0_s_cnt ;ソフトタイマー0カウンタに設定 ;;; bsf INTCON,T0IE ;bit5(T0IE)=1: タイマー0割り込み許可 ;; <<<<< ... (1) expro11 clrf temp_hh ;初期設定 clrf temp_mm ; clrf temp_ss ; clrf tg_point ;対象の位置ポインタ = 0 clrf chg_flg ;データ変更フラグをクリア movlw h'0a' ;℃ btfsc exp_flg,2 ;CH = 3 か? movwf temp_mm ;Yes. "ML" 桁 bsf exp_flg,7 ;メモリの内容表示フラグ = ON ; メモリ設定データの表示 expro12 call exp_monitor ;タイマーと温度の監視 call blinc_blinc ;(タイマー0割り込み処理の代わり) ;; <<<<< ... (2) btfss exp_flg,7 ;メモリの内容表示フラグ = ON か? goto expro18 ;No btfsc exp_flg,6 ;反転表示フラグ = ON (非表示) か? goto expro13 ;Yes : : expro2x clrf exp_flg ; bsf mod_flg,7 ;表示変更フラグ = ON bsf INTCON,GIE ; ;; <<<<< ... (4) expro2y return ;-------------------------------------------------------------------------- ; ブリンク(タイマー0割り込み処理の代わり) ;; <<<<< ... (3) blinc_blinc call wait_1ms ; decfsz tm0_s_cnt,F ;ソフトタイマー0カウンタ - 1 = 0 か? goto blinc01 ;No movlw 1 << 6 ;対象ビットをセット xorwf exp_flg,F ;反転表示フラグを 200 mS 毎にビット反転 bsf exp_flg,7 ;メモリの内容表示フラグ = ON movlw tm0_s_val * 2 ;ソフトタイマー0カウント値 = 200 を movwf tm0_s_cnt ;ソフトタイマー0カウンタに設定 blinc01 returnと、ここまで書いてはきたのですが、上述の代替処理ではどうしても自分に納得がいかず、なおも4、5日ほど格闘を続けてみたのですが、残念ながら大した進展はありません。 ただ、上リストの (1)、(2)、(3) だけの処置では、まだときどきですがブザーが鳴らなくなることがある、ということが分かり (4) 行を追加しました。 これは (1) 行のタイマー0割り込みを許可(T0IE=1)することだけが原因(不具合となる引き金)ではなさそうです。 このように、今のところ何が本来の原因なのかは相変わらず分かっていませんが、(1) 〜 (4) の代替の処置で動作は落ち着いているようです。 そして、この問題はどうやら長期戦になりそうな予感なので、とりあえずは現状のままでこのページの更新公開をすることにしました。 引き続き原因の追及はして行きますが、 この先も苦戦が予想されます。 本機に今回のタイマースイッチ機能等を追加するために要する時間は、このホームページの追加更新も含めて、1か月ほどの期間があれば何とかなるのではないか、と初めはそんな安易な心づもりで取り掛かったのですが、 それは実に甘い考えでした。 進捗は大幅に遅れ、気が付けばいつの間にか5月の連休もとうに過ぎてしまっています。 |
| プログラムのトップに戻る |
◎ 新たに見つかった不具合 2 件 ( 2021/10/9 追記 ) その後、本ページをご覧になって実際に本機を製作された方から申告がありました。 機能概要と使用法 でも述べていますが、本機に初めて電源を入れたときなどには、RTC(リアルタイムクロック)モジュールの初期設定のために、 スイッチ操作によって現在のリアルな年月日、時分秒を設定する必要があるのですが、そのための機能がうまく動作をしない ― というものです。 タイマースイッチ等の機能追加後の私の場合には、RTC(リアルタイムクロック)モジュールの設定は、機能追加前に既に済んでいるために必要とせず、機能追加後にこの RTC モジュール設定機能を再チェックすることを怠っていました。 原因は 月データのテーブル読み出し(month_day2)サブルーチン が不具合を誘発していたため、別コードに書き換えました(Ver. 1.11)。 そして不具合(1 件目)を修正後、しばらくあれこれとスイッチを操作したり、LED の表示状況を監視していると、また、ある事に気が付きました。 コロン(:)、またはピリオド(.) の表示が時々おかしくなることがあるのです。 (実は、こちらの不具合(2 件目)の方が原因の探索が大変でした。) NORMAL モードでの時間表示なのに、左側のコロン(:) は点灯するのですが、右側のコロン(:) は消灯してしまったり、温度、湿度表示なのに、右側のピリオド(.) が消灯して、左側のピリオド(.) が点灯してしまうのです。 初めは気が付かなかったのですが、これはタイマースイッチ等の機能追加のメモリ読み出しのときのコロン(:)、ピリオド(.) の表示パタンと同じであることに途中で気が付きました。 これらのコロン(:)、ピリオド(.) の表示制御を行っているのは、ドットマトリクス LED 6桁分の表示(dxled_display)サブルーチン の後半部分で、変数 exp_flg の 7 ビット目が ON のときに、 この機能追加用のパタン表示をするのです。 しかし、NORMAL モードでは通常 変数 exp_flg が ON になることはあり得ない筈(?)なのですが ・・・ タイマースイッチ等の機能追加でメモリ設定機能、メモリ読み出し機能をスイッチ操作したときに 7 ビット目を ON にして、 機能追加用のコロン(:)、ピリオド(.) の表示制御を行います。 ところが、いろいろと調べている内に、ブザー音が伴うと動作をしてしまう(変数 exp_flg の 7 ビット目が ON)ことが分かりました(これは異常動作です)。 例えば "ピッ" というブザー音を発生させるのには、次のようなコード群が実行されます。 : : call buzzer_pi ;"ピッ" ブザー音出力 : : ; "ピッ" ブザー音出力 buzzer_pi Sound_M f_so, pi_tim movlw high f_so ;出力音階 ; movwf const_high ; movlw low f_so ; マクロ展開 movwf const_low ; movlw pi_tim ;出力時間(ms) ; call buzzer_control ;ブザー音出力コントロール ; return : : ; ブザー音出力コントロール buzzer_control bsf STATUS,RP0 ;バンク 1 bsf PIE1,TMR1IE ;タイマー1割り込みを許可 ;; ..... (1) bcf STATUS,RP0 ;バンク 0 call wait_xxms ;入力 W: 音長(ループカウンタ) bsf STATUS,RP0 ;バンク 1 bcf PIE1,TMR1IE ;タイマー1割り込みを禁止 ;; ..... (2) bcf STATUS,RP0 ;バンク 0 return :本機のプログラムでは、ブザー音を発生させるのにタイマー1割り込みを使用しています。 上リストの (1) でタイマー1割り込みの許可をし (2) で禁止をして、その間を割り込み処理を繰り返しながらブザー音を出力していますが、 本機での実際のタイマー1割り込み処理ルーチンを次に示します。 次のリストでは本機においての全体の割り込み処理ルーチンを示しておきます。 ;========================================================================== ; 割り込み処理 ;========================================================================== interrupt ; レジスタの保存 movwf w_save ;W レジスタの保存 swapf STATUS,W movwf s_save ;STATUS レジスタの保存 movf FSR,W movwf f_save ;FSR レジスタの保存 movf PCLATH,W movwf p_save ;PCLATH レジスタの保存 movlw high $ ;この場所の high アドレスを movwf PCLATH ;PCLATH に設定 bcf STATUS,RP0 ;バンク 0 btfsc INTCON,INTF ;外部(RA2/INT)割り込み か? goto ra2_int ;Yes btfsc INTCON,T0IF ;タイマー0割り込み か? goto t0_int ;Yes btfsc PIR1,TMR1IF ;タイマー1割り込み か? goto t1_int ;Yes ;; ..... (3) goto int_end ;(Illegal interrupt) ;---------------- 外部(RA2/INT)割り込み処理 ------------------------------- ra2_int bcf INTCON,INTF ;外部(RA2/INT)割り込みフラグをクリア bsf int_flg,0 ;bit0: RTC DS3231 から年月日、時分秒の読み出し要求フラグ = ON movlw b'00000010' ;; xorwf int_flg,F ;bit1: DHT22 から温度、湿度の読み出し要求フラグ = ON/OFF, (変更)* addwf int_flg,F ;bit1-2: 4 秒カウンタ ;bit3: DHT22 から温度、湿度の読み出し要求フラグ = ON movf mod_cnt,W btfss STATUS,Z ;モードNo表示時間カウンタ = 0 か? decf mod_cnt,F ;No. モードNo表示時間カウンタ - 1 goto int_end ;---------------- タイマー0割り込み処理 ---------------------------------- ;(タイマー0を許可したときのみ: 割り込み周期 = 4/8Mz*16*250=2msec) t0_int bcf INTCON,T0IF ;タイマー0割込みフラグをクリア movlw tm0_h_val ;ハードタイマー0カウント値 = 250 を movwf TMR0 ;TMR0 に設定 decfsz tm0_s_cnt,F ;ソフトタイマー0カウンタ - 1 = 0 か? goto int_end ;No movlw 1 << 6 ;対象ビットをセット ;; (5) xorwf exp_flg,F ;反転表示フラグを 200 mS 毎にビット反転 ;;(2021/10/08) bugfix: Ver. 1.12 ;; (6) bsf exp_flg,7 ;メモリの内容表示フラグ = ON ;;コロン(:)表示の異常を仮修正のため movlw tm0_s_val ;ソフトタイマー0カウント値 = 100 を movwf tm0_s_cnt ;ソフトタイマー0カウンタに設定 goto int_end ;---------------- タイマー1割り込み処理 ---------------------------------- t1_int bcf PIR1,TMR1IF ;タイマー1割込みフラグをクリア ;; ..... (4) movf const_high,W ;音階用分周定数 high を movwf TMR1H ;TMR1H に設定 movf const_low,W ;音階用分周定数 low を movwf TMR1L ;TMR1L に設定 movlw 1 << _BZ ;対象ビットをセット xorwf PORTB,F ;出力をビット反転 ; goto int_end ; レジスタの復帰 int_end movf p_save,W movwf PCLATH ;PCLATH レジスタの復帰 movf f_save,W movwf FSR ;FSR レジスタの復帰 swapf s_save,W movwf STATUS ;STATUS レジスタの復帰 swapf w_save,F swapf w_save,W ;W レジスタの復帰 retfieそして、全体の割り込み処理ルーチン内では、タイマー1割り込みは (3) で振り分けられ、(4) のラベル t1_int へジャンプをしてきて、タイマー1割り込み処理を実行します。 以上がリストから読み取れる割り込み処理の流れですが、現実には、このときに信じられないことが起こっているのが分かりました。 タイマー1割り込みが実行されると、何故か????? タイマー0割り込みも同時に実行されてしまうらしいのです。 いや、らしいのではなく、現実のようなのです。 その証拠として、上述した 変数 exp_flg の 7 ビット目が ON になってしまうのです。 そして、タイマー0割り込み処理内の (5)、(6) 行をコメントにすると、変数 exp_flg の 7 ビット目が ON になることはなく OFF のままなのです。 したがって、 (5)、(6) 行をコメントにすることによって、上述のようにブザー音が伴っても、コロン(:)、ピリオド(.) の表示制御は正常に行われるようになりました(Ver. 1.12)。 タイマー0割り込み処理ルーチンは、 前回の 機能追加で発生した不具合 で述べたように、現在、実際には使用しておらず、タイマー0割り込みを使用しない代替処理でブリンク表示を行っているため不要な存在です。 ただ、前述したように代替のブリンク処理方法では私個人的に不本意なため、タイマー0割り込みルーチンを残してありました。 このように、現在、タイマー0割り込みがいろいろとワルを起こす原因となっていますが、いつかきっと解決をさせてすっきりしたいと思っています。 しかし、今のところ何故このようなことが起こるのかまったく分かりません。 やはり、長期戦にはなりそうです。 |
| プログラムのトップに戻る |
◎ 割り込み動作の再確認 ( 2021/12/7 追記 ) 上述した 機能追加で発生した不具合 と 新たに見つかった不具合 2 件 で、割り込み動作についての私の認識が少々足らないところもあったので、 ここで割り込み動作の再確認を(自分自身に)しておくとともに、上述の説明ももう少し補っておこうと思います。 まず初めに、Microchip Technology 社の "データシート PIC16F685/687/689/690" に次の図があり、これは割り込み要因の種類の全体像を表したもので、この内、赤色に彩色した信号名が本機に関わる3つの割り込み要因と、 それらの要因に対する割り込みの許可信号との関係を表しています。 上述の ところが、・・・ ブザー音が伴うと動作をしてしまう ・・・ や、タイマー1割り込みが実行されると、・・・ タイマー0割り込みも同時に実行されてしまう ・・・ という疑問にも納得が行きます。 上に示した "割り込み処理ルーチン" のリストでは、 : btfsc INTCON,T0IF ;タイマー0割り込み か? goto t0_int ;Yes btfsc PIR1,TMR1IF ;タイマー1割り込み か? goto t1_int ;Yes :のように、割り込み種類の振り分けが、タイマー1割り込みよりもタイマー0割り込みの方が、先に処理されるように記述がされています。 従って、本来タイマー1割り込みによってこの "割り込み処理ルーチン" にジャンプをしてきたにも拘らず、 例えタイマー0割込みイネーブルビット ( INTE ) = OFF になっていても、この時点でタイマー0割込みフラグ (T0IF) = ON になっているときには、タイマー0割込みの処理が優先して行われてしまう ― ことになります。 ここで、割り込みが発生したときのその処理の流れを、初めから おさらい をしてみると次のようになります。
今回、LED の輝度調節機能の追加 ( Ver. 1.13 ) を行ったのを機として、前回、前々回にも増して、本機のタイマー0割込みに関わる不具合の探索に時間をかけてきたのですが、やはり解決に結びつけることはできませんでした。 現在の最終的な "割り込み処理ルーチン" だけを次に示しておきます。 今でもタイマー0割り込み処理 ( t0_int ) は残してありますが、この処理が実行されることはありません。 ;========================================================================== ; 割り込み処理 ;========================================================================== ; INTCON: bit 7 6 5 4 3 2 1 0 ; GIE, PEIE, T0IE, INTE, RABIE, T0IF, INTF, RABIF interrupt ; レジスタの保存 movwf w_save ;W レジスタの保存 swapf STATUS,W movwf s_save ;STATUS レジスタの保存 movf FSR,W movwf f_save ;FSR レジスタの保存 movf PCLATH,W movwf p_save ;PCLATH レジスタの保存 movlw high $ ;この場所の high アドレスを movwf PCLATH ;PCLATH に設定 clrf STATUS ;バンク 0 ;; <<<<< btfsc INTCON,INTF ;外部(RA2/INT)割り込み か? goto ra2_int ;Yes ;; btfsc INTCON,T0IF ;タイマー0割り込み か? ;; <<<<< ;; goto t0_int ;Yes ;; <<<<< btfsc PIR1,TMR1IF ;タイマー1割り込み か? goto t1_int ;Yes goto int_end ;(Illegal interrupt) ;---------------- 外部(RA2/INT)割り込み処理 ------------------------------- ra2_int bcf INTCON,INTF ;外部(RA2/INT)割り込みフラグをクリア bsf int_flg,0 ;bit0: RTC DS3231 から年月日、時分秒の読み出し要求フラグ = ON movlw b'00000010' ; xorwf int_flg,F ;bit1: DHT22 から温度、湿度の読み出し要求フラグ = ON/OFF, (変更)* addwf int_flg,F ;bit1-2: 4 秒カウンタ ;bit3: DHT22 から温度、湿度の読み出し要求フラグ = ON movf mod_cnt,W btfss STATUS,Z ;モードNo表示時間カウンタ = 0 か? decf mod_cnt,F ;No. モードNo表示時間カウンタ - 1 goto int_end ;---------------- タイマー0割り込み処理 ---------------------------------- ;(タイマー0を許可したときのみ: 割り込み周期 = 4/8Mz*16*250=2msec) t0_int bcf INTCON,T0IF ;タイマー0割り込みフラグをクリア movlw tm0_h_val ;ハードタイマー0カウント値 = (256 - 250) を movwf TMR0 ;TMR0 に設定 decfsz tm0_s_cnt,F ;ソフトタイマー0カウンタ - 1 = 0 か? goto int_end ;No movlw 1 << 6 ;対象ビットをセット xorwf exp_flg,F ;反転表示フラグを 200 mS 毎にビット反転 ;; <<<<< bsf exp_flg,7 ;メモリの内容表示フラグ = ON ;; <<<<< movlw tm0_s_val ;ソフトタイマー0カウント値 = 100 を movwf tm0_s_cnt ;ソフトタイマー0カウンタに設定 goto int_end ;---------------- タイマー1割り込み処理 ---------------------------------- t1_int bcf PIR1,TMR1IF ;タイマー1割り込みフラグをクリア movf const_high,W ;音階用分周定数 high を movwf TMR1H ;TMR1H に設定 movf const_low,W ;音階用分周定数 low を movwf TMR1L ;TMR1L に設定 movlw 1 << _BZ ;対象ビットをセット xorwf PORTB,F ;出力をビット反転 ; goto int_end ;---------------- 割り込み処理の終了 -------------------------------------- ; レジスタの復帰 int_end movf p_save,W movwf PCLATH ;PCLATH レジスタの復帰 movf f_save,W movwf FSR ;FSR レジスタの復帰 swapf s_save,W movwf STATUS ;STATUS レジスタの復帰 swapf w_save,F swapf w_save,W ;W レジスタの復帰 retfie最後に結論として、グローバルイネーブルビット ( GIE ) をクリア ( OFF ) してしまう犯人は一体誰なのか? ― という謎だけが、今尚、残ったままで解決できずに残念です。 |
| プログラムのトップに戻る |
◎ 割り込み処理の不具合を解決 ( 2022/4/30 追記 ) 上(次)の3項目の中で述べている、割り込み処理の不具合に関しての記述は、すべて本項で述べる原因から起因するもので、この度ようやく解決をさせることができました。 上述の "グローバルイネーブルビット ( GIE ) をクリア ( OFF ) してしまう犯人は一体誰なのか?" ― 犯人が分かり、すべての謎が解けました。 犯人を捕まえてみればあまりにも単純なことで、ここで種明かしをするのも恥ずかしい、 と思えるような私のミスでした。 最近公開したばかりの "195. ドットマトリクス LED コントローラー" のプログラムを作っているときに気が付きました。 答えは、既に上述した おさらい の中にあったのです。
org h'0004' ;割り込みベクタ goto interrupt ;; ジャンプ : : : interrupt ; レジスタの保存 movwf w_save ;W レジスタの保存 swapf STATUS,W movwf s_save ;STATUS レジスタの保存 movf FSR,W movwf f_save ;FSR レジスタの保存 movf PCLATH,W movwf p_save ;PCLATH レジスタの保存 movlw high $ ;この場所の high アドレスを ;; <<<<< movwf PCLATH ;PCLATH に設定 ;; <<<<< clrf STATUS ;バンク 0 btfsc INTCON,INTF ;外部(RA2/INT)割り込み か? goto ra2_int ;Yes : : :上リストの ;; <<<<< の部分で、PCLATH レジスタに interrupt が存在(ページ 0)している high アドレスを設定していますが、この時点では既に遅く、上から 2 行目の goto interrupt ;; ジャンプより以前に PCLATH レジスタに設定をする必要があったのです。 本機のプログラムは少々大きく、ページ 0 と ページ 1 のプログラムメモリを使用していますが、ページ 0 のプログラムを実行中に割り込みがあった場合には問題はないのですが、ページ 1 のプログラムを実行中に 割り込みがあった場合に、今回のような不具合が起こります。 なぜなら、PIC16F690 とページの問題 で示した、真ん中の [ GOTO, CALL 命令の場合 ] の図をご覧ください。 今回のように GOTO 命令を実行するときには、 PCLATH レジスタの bit4 と bit3 が、プログラムカウンタ PC の上位 2 ビットに入れられてジャンプを行います。 ところが、ページ 1 のプログラムを実行中には PCLATH レジスタの内容は、ページ 1 の上位バイトが入っています。 その状態で、割り込みによってハード的に h'0004' 番地にジャンプをしてきたその直後に、再び goto interrupt ;; ジャンプの命令を実行しても、プログラマ(自分 = 私)の思惑通りには行きません。 プログラムカウンタ PC の下位 11 ビットには、interrupt が存在しているアドレス(すなわち、ページ 0 内のアドレス)が設定されますが、 プログラムカウンタ PC の上位 2 ビットについては、PCLATH レジスタの、すなわち、ページ 1 の上位バイトの内、bit4 と bit3 が設定されることになり、結果、interrupt 番地にジャンプすることはなく、 ページ 1 のとんでもないところへジャンプをしてしまいます。 この状態を一般的には暴走、と呼びます。 このように、目的とする interrupt 番地にジャンプすることはないのですが、割り込みが起こったことによって、上述の おさらい の
5. > 次に、実行中の命令の次の命令アドレス ( 13 ビット) を、スタックに保存 ( PUSH ) する。
そして、暴走しているプログラムは、やがて、どこかのサブルーチン内に飛び込んで、そのサブルーチンの末尾の RETURN 命令によって、5. で保存 ( PUSH ) してあった命令アドレスを、 プログラムカウンタ PC に復帰 ( POP ) し、プログラムは割り込みによって中断されていた命令から再開されることになります。 こうして、一旦は暴走をしたプログラムは、即に、割り込み前の中断された命令から再開をして、何でもなかったような顔をして続行をするため、結果、"グローバルイネーブルビット ( GIE ) がクリア ( OFF ) された" 事実だけが残ることになり、私も長いこと惑わされ続けてきました。 以上述べたように、そもそも、"割り込み処理ルーチン" を、プログラムメモリアドレス h'0004' 番地から離れた場所に配置をするから、このような不具合が起こる起因ともなるわけで、 h'0004' 番地(以降)に配置をすれば何も問題は起こらないはずです。 私作のプログラムのほとんどはこのように離れた配置になっているので、複数ページを使用したプログラムの場合には、 今後の反省すべき点として受け止めたいと思います。 本機のプログラムでは、上述の GOTO 命令を削除して、"割り込み処理ルーチン" を h'0004' 番地以降に配置替えを行いました。 このように、不具合の原因が分かってしまえば何のことはないのですが、今回の不具合では、その発生から(本来の)修正までに、何と1年近くも掛かってしまいました。 タイマー0割り込みに関しては、タイマー0割り込みを使用しない代替の処理で賄っていたので、本来のタイマー0割り込みによる処理に戻しました。 ただ、これは私の自己満足であって、ユーザサイドからすればどちらの処理でも同じことなんですが ・・・ |
| プログラムのトップに戻る |
● LED の輝度調節機能を追加、その他を改善 ( 2021/12/7 追加 ) 本機の 8 x 8 ドットマトリクス LED 6個の輝度について従来は、プログラミング時の一定値に固定がされていて、変更することはできませんでした。 私が入手をしたモジュールに使用のマトリクス LED が高輝度 LED らしく、 輝度レベルを最小値に設定しておいても十分(以上)に明るく点灯をしていたことから、それで問題はないと思っていました。 しかし、このホームページを見た方が本機を再現する場合に、必ずしも同じマトリクス LED を使用するとは限らないわけで、輝度レベルが自由に変更することができるようにしておいた方が良い、と思い直しました。 本機で使用したIC MAX7219 では 16 段階 で変更することが可能ですが、LEDの安全性を考慮して 0 から上限を 9 までの 10 段階に制限をしてあります。 ただし、このように輝度調節ができるのは6個の 8 x 8 ドットマトリクス LED だけで、時と分、分と秒、それぞれの間のコロン(:)、またはピリオド(.)用の4つの LED については、その制御に MAX7219 を使用していないので、 輝度調節をすることはできません。 LED の輝度調節機能を実現させているのが以下のリストですが、プログラムメモリのページ 0 には数十ワードの空きしかないので、輝度調節機能の本体プログラムはページ 1 に配置をさせました。 なお、調節機能の操作法については、< 新しい機能 2 > を参照してください。 ;-------------------------------------------------------------------------- ; ZERO スイッチの処理 ... メイン・ルーチンから CALL ;-------------------------------------------------------------------------- zero_switch call wait_30ms ;チャッタリング吸収 btfsc PORTA,_swZER ;再度 ZERO スイッチ = ON か? goto zersw04 ;No movlw h'05' subwf bcd_mm,W btfss STATUS,C ;分 BCD カウンタ < h'05' か? goto zersw02 ;Yes movlw h'55' subwf bcd_mm,W btfss STATUS,C ;分 BCD カウンタ >= h'55' か? ;; goto zersw04 ;No. 無視 ; (Ver.1.13 で変更) goto zersw05 ;No ; : ( 省略 ) : zersw04 btfss PORTA,_swZER ;ZERO スイッチ = OFF か? goto $ - 1 ;No call wait_30ms ;チャッタリング吸収 return ;-------------------------------------------------------------------------- ; (Ver.1.13 で追加) zersw05 movlw 20 ;50ms x 20 = 1 秒間の監視 movwf wk_order ;ループカウンタ zersw06 call wait_50ms ;50m 秒 ウエイト btfsc PORTA,_swZER ;ZERO スイッチ = OFF か? goto zersw04 ;Yes decfsz wk_order,F ;ループカウンタ - 1 = 0 か? goto zersw06 ;No bsf PCLATH,3 ;Page 1 call bright_adj ;輝度調節機能 (Ver.1.13 で機能追加) bcf PCLATH,3 ;Page 0 goto zersw04 : : ;============================================================================================================= org h'800' ;(Page 1) ;============================================================================================================= : : ;========================================================================== ; 輝度調節機能の追加 ... (Ver.1.13) で追加 ;========================================================================== ; intensity ;bit7: 輝度更新フラグ, bit3-0: 輝度調節値 (h'00' 〜 h'09') bright_adj bsf intensity,7 ;輝度更新フラグ = ON ; 輝度レジスタの更新 badj01 movlw h'0a' ;輝度レジスタ movwf reg_addr movf intensity,W ;輝度調節値 andlw h'0f' movwf reg_data bcf PCLATH,3 ;Page 0 call max_led_contr ;6個分の制御 bsf PCLATH,3 ;Page 1 ; 輝度調節値の LED 表示 movlw 3 movwf tbl_type ;フォントデータテーブルの種類 = 3 を指定 movlw h'89' ;"Bri." movwf temp_hh movlw h'ab' ;"Lev" movwf temp_mm movlw h'c0' ;"=" movwf temp_ss movf intensity,W ;輝度調節値 andlw h'0f' iorwf temp_ss,F movlw h'fc' ;(xx_xx_xx)表示 movwf dspdigit ;表示桁指定フラグの設定 call dxled_display ;ドットマトリクス LED の表示 ; clrf tbl_type ;フォントデータテーブルの種類 = 0 を指定 bcf intensity,7 ;輝度更新フラグ = OFF ; 各スイッチの入力チェック badj02 btfss PORTA,_swUP ;UP スイッチ = ON か? call z_up_switch ;Yes btfss PORTA,_swDWN ;DOWN スイッチ = ON か? call z_down_switch ;Yes btfsc intensity,7 ;輝度更新フラグ = ON か? goto badj01 ;Yes btfss PORTA,_swZER ;ZERO スイッチ = ON か? goto badj02 ;Yes ; 輝度調節処理の終了 bcf INTCON,INTF ;外部(RA2/INT)割り込みフラグをクリア ;; <<<<< bsf INTCON,INTE ;INTE: 外部(RA2/INT)割り込みを禁止解除 ;; <<<<< bsf INTCON,GIE ; ;; <<<<< bsf mod_flg,7 ;表示変更フラグ = ON return ;-------------------------------------------------------------------------- ; UP スイッチの処理 ... 輝度調節機能から CALL ;-------------------------------------------------------------------------- z_up_switch call xwait_30ms ;チャッタリング吸収 btfsc PORTA,_swUP ;再度 UP スイッチ = ON か? goto zupsw01 ;No incf intensity,F ;+1 更新 movlw h'0a' ;MAX h'0f' を h'09' に制限した subwf intensity,W btfsc STATUS,C ;intensity >= h'0a' か? decf intensity,F ;Yes. MAX 値(= h'09') にする bsf intensity,7 ;輝度更新フラグ = ON zupsw01 btfss PORTA,_swUP ;UP スイッチ = OFF か? goto $ - 1 ;No call xwait_30ms ;チャッタリング吸収 return ;-------------------------------------------------------------------------- ; DOWN スイッチの処理 ... 輝度調節機能から CALL ;-------------------------------------------------------------------------- z_down_switch call xwait_30ms ;チャッタリング吸収 btfsc PORTA,_swDWN ;再度 DOWN スイッチ = ON か? goto zdwsw01 ;No decf intensity,F ;-1 更新 incf intensity,W ; btfsc STATUS,Z ;intensity = h'00' か? clrf intensity ;Yes. MIN 値(= h'00') にする bsf intensity,7 ;輝度更新フラグ = ON zdwsw01 btfss PORTA,_swDWN ;DOWN スイッチ = OFF か? goto $ - 1 ;No call xwait_30ms ;チャッタリング吸収 returnなお、上リスト中の最右端のコメントに ;; <<<<< を付した3行の命令を追加しておかないと、ここでも外部(RA2/INT)割り込み処理が行えなくなる、すなわち時刻の更新が停止をしてしまう不具合が発生しますが、 残念ながらこれも原因は分かりません。*
話は変わりますが、本機が動作中に "ジー" という異音(雑音)が、ブザーから出たり出なかったりする現象 が本機の作製当初からあったのですが、その雑音が出ないように改善をしました。 当初はこの雑音がどこから発っせられているのか、なかなか分かりませんでした。 今回の輝度調節機能で輝度レベルを順に大きく設定変更をして行くにしたがって、ますますその "ジー" 音が大きくなるのです。 そこで分かったのですが、ブザーに繋がっているコネクタを抜いたみた結果、ピタッと音が止まりました。 まさかブザーから出ている音とは思いもよりませんでした。 とてもブザー音とは思えないような音なのです。 たぶん、MAX7219 に内蔵されている多重スキャン回路からの誘導音じゃないか? と思われます。 ブザー音を出力するときには、ブザー音出力コントロール ( buzzer_control ) サブルーチン が CALL されますが、そのときにサブルーチン内にあるウェイト・ルーチン ( wait_xxms ) が実行している間だけを、 タイマー1割り込みが何度も繰り返す(次の命令実行を繰り返す)ことによってブザー音を出力しています。 : movlw 1 << _BZ ;対象ビットをセット xorwf PORTB,F ;出力をビット反転 :そして、ウェイト・ルーチン ( wait_xxms ) のタイミングによって、サブルーチン ( buzzer_control ) が終了した時点で、PIC のブザー出力ピンの信号レベルが "L" で終わったり "H" で終わったりします。 "L" で終わったときには問題はないのですが、"H" で終わってそのままの状態が続いている(ブザー音の出力が終了して無音の)とき に、"H" 信号に誘導音 "ジー" が乗っかってブザー出力ピンから出力されてしまう ― ことになるようです。 そこで、次のリストに示すように、このサブルーチン ( buzzer_control ) が終了する直前で、必ずブザー出力ピンを "L" にしておくように修正をしました。 ; ブザー音出力コントロール buzzer_control bsf STATUS,RP0 ;バンク 1 bsf PIE1,TMR1IE ;タイマー1割り込みを許可 bcf STATUS,RP0 ;バンク 0 call wait_xxms ;入力 W: 音長(ループカウンタ) bsf STATUS,RP0 ;バンク 1 bcf PIE1,TMR1IE ;タイマー1割り込みを禁止 bcf STATUS,RP0 ;バンク 0 ;常時に "ジー" という異音防止のため ; btfss alm_flg,3 ;時報正報音フラグ = ON のとき以外は ; (Ver.1.13) で追加 bcf PORTB,_BZ ;ブザー出力ビットを "L" にしておく ; returnただし、時報正報音 "ポッ〜〜ン" を1秒間出力するときには、本プログラムでは 250 ミリ秒の "ポッ〜〜ン" 音を4回繰り返して1秒間を作り出しているため、その 250 ミリ秒終了ごとに "L" にしてしまうと、 その都度、音が途切れた瞬間雑音として聞こえるために、この時報正報音の出力のときだけは "L" にすることを除外しています。 そして、4回繰り返して1秒間を作り出した後で "L" にしています。 現在、ブザー音出力コントロール ( buzzer_control ) サブルーチンに上述の修正を加えたことによって、マトリクス LED の輝度レベルを 1 〜 9 の範囲内に設定した場合には、 "ジー" 雑音はなくなりましたが、 輝度レベルを最小の 0 に設定した場合には、なぜか微かに "ジー" 音が聞こえます。 このときにはブザーのコネクタを抜いても聞こえることから、ブザー以外の場所(部品?)から出ている音であることは明らかです。 |
| プログラムのトップに戻る |
現在の最新バージョン: Ver. 1.14 ( 2022/4/30 更新 )
ソースファイル (Matrix88LED_ClockII.asm) (Ver. 1.14) | ||
I2C RTC(DS3231)モジュール 制御サブルーチン (I2C_RTC.sub) | ||
I2C (SCLクロック周波数 = 100/400 KHz) サブルーチン (I2C_XS.sub) | ||
デジタル温度湿度センサーモジュール DHT22 制御サブルーチン (DHT22.sub) | ||
HEX ファイル (Matrix88LED_ClockII_74.hex) ... PCF8574 用 | ||
HEX ファイル (Matrix88LED_ClockII_74A.hex) ... PCF8574A 用 | ||
ソースファイル (Matrix88LED_ClockII_113.asm) (参考: 旧 Ver. 1.13) |
||
ソースファイル (Matrix88LED_ClockII_101.asm) (参考: 旧 Ver. 1.01) |
| ページトップ |
使用したプリント基板(1)は、"秋月電子通商" の "[New_Board 1L] 片面ガラス・ユニバーサル基板 140 x 40 mm 752 穴 めっき仕上げ (通販コード P-03250 )" を2枚使用しました。 まず、それぞれの基板を次図(上)のように
Y1、Y2 の ----- 線の位置で切断をし、その後、次図(下)のように切断した Y1、Y2 の断面を合わせ、0.5 mm のスズメッキ線とハンダで繋ぎ合わせて使用をします。
また、繋ぎ合わせた位置から少し左の2か所にΦ3.2 の丸穴を開けておきます。 このプリント基板(1)は、計6か所の丸穴位置でスペーサとともにビス、ナットでケースに固定をさせます。 |
| プリント基板部品配置図 (Matrix88LED_ClockII_1PC0.CE3) |
プリント基板(2)については、やはり同ユニバーサル基板を1枚使用しました。こちらの母基板は切断などの加工はありませんが、----- 線で囲んだ中の2か所にΦ3.2 の丸穴を開けます。
また、下図に示すように他にタクトスイッチ用の小基板も必要で、私は新しい基板から切り出したのではなく、今までに溜めおいた端材を使用しました。 次の左側の図はタイマースイッチ機能等の追加のための改造後のもので、右上に位置する L型の 4P のピンヘッダを追加しました。 また、タクトスイッチ用の小基板の5個のタクトスイッチの左右の並びが、 まるっきり逆になるように改めました。 他に、左端にある2連のピンヘッダ周りの配線も一部の変更をしました。 (詳細は、プリント基板(2)パターン図 (部品面) ... (改造後)、 プリント基板(2)パターン図 (ハンダ面) ... (改造後) を参照のこと) 参考に改造前のものも右側の図に示しておきます。 |
(参考: 改造前のもの) |
| プリント基板部品配置図 (Matrix88LED_ClockII_2PC0b.CE3) |
タイマースイッチ機能等の追加のために、新たにプリント基板(3)を作製しました。 こちらも同ユニバーサル基板を使用して、次図(左上)のように Y の ----- 線の位置で切断加工が必要です。 また、この追加機能のためにも新たなタクトスイッチ用の小基板の追加が必要で、次図(右下)に示しておきます。 |
| プリント基板部品配置図 (Matrix88LED_ClockII_3PC0.CE3) | ページトップ |
なお、以上の図の中で、長方形の中に ○で表したものは通常のピンヘッダ(オス)で、同様に◎で表したものは ピンソケット(メス)を使用することを表しているので、注意をしてください。 |
このディスプレイ基板は、保守性を容易にするために直接基板にハンダ付けするのを避けて、ドットマトリクス・ディスプレイモジュールを、コネクタを使用して基板に搭載をしています。
初め、通常のピンヘッダ(オス)とピンソケット(メス)で構成をさせるつもりでいたのですが、この場合にはそれぞれピンの長さが長いので、プリント基板(1)全体の厚さ(高さ)が大きくなってしまいます。 そこで高さを抑えるために、私は SIP 丸ピンの IC ソケット(メス)と、細ピンのピンヘッダ(オス)を使用してみたのですが、今考えてみると、通常(6 mm 長)のピンヘッダ(オス)とピンソケット(メス)の他に、 "秋月電子" では、もう少し短い(4 mm 長)のピンヘッダ(オス)とピンソケット(メス)も取り扱っているようなので、その方が良かったかもしれません。 |
| プリント基板(1)パターン図 (部品面) (Matrix88LED_ClockII_1PC.CE3) |
[ コロン用小基板の作製 ] プリント基板(1)パターン図 (部品面)、および (ハンダ面) それぞれの、右端にある縦細長の図を拡大して次に示します。 これらは時、分、秒それぞれの間のコロン用の小基板図を表しています。 これらのコロン用小基板を作製するには、まず、写真1に示すような 1 列 13 穴の片面小基板を 2 枚用意します。 (ただし、これらのコロン用小基板は2組必要なので計 4 枚用意します。) これらの小基板についても、私は今までに溜めおいた端材を使用しました。 なお、片面基板を 2 枚ではなく両面基板 1 枚を使用するという方法もありますが、私は個人的にスルーホールのユニバーサル基板の使用が嫌いなので、 ここでは対象外です。 また、小基板の裏面側の上下位置に母基板との接続用のコネクタとして、2P ピンヘッダを 2 個(これも2組必要なので計 4 個)を用意します。 これらのピンヘッダは写真1に示すように、 黒色の樹脂部分がピンの中央位置になるように、あらかじめ加工をしておきます。 そして、このコロン用小基板については、直接母基板にハンダ付けするのが前提です。 |
小基板の組み立て方法は、まず、2 枚の片面小基板それぞの (部品面) が内側に向かい合うように重ね合わせます。 そして、裏面側となる上下位置にそれぞれ 2P ピンヘッダを取り付け(仮ハンダし)ます。
次に、Φ0.4 程度の裸線(抵抗等の足の線くずで可)で、左図の青色の線で示したようにジャンパー配線を、表面側、裏面側ともにします(このとき上下のピンヘッダも本ハンダ)。 最後に表面側となる面に 2 個の LED を取り付けるのですが、各 LED の足には、2P ピンヘッダから各ピンを抜き取った黒色の樹脂部分だけを、スペーサとして取り付けてからハンダ付けをします。 このとき、各 LED の足には高さ調節のために 2 個ずつが必要です。 なお、上下のピンヘッダにも 1 個ずつを追加使用しました。 |
| ページトップ |
| プリント基板(1)パターン図 (ハンダ面) (Matrix88LED_ClockII_1PC1.CE3) | ページトップ |
このメイン基板についても、「リアルタイムクロックモジュール(RTC_DS3231_mod)」、「デジタル温度湿度センサーモジュール(DHT22_mod)」の取り付けには、保守性を容易にするために直接基板にハンダ付けするのを避けて、
コネクタを使用して基板に搭載をしています。 私が購入をした「リアルタイムクロックモジュール」、「デジタル温度湿度センサーモジュール」のどちらにも、コネクタとして初めからL型のピンヘッダが取り付けられていますが、L型で使用をすると取り付けたときに、 プリント基板に対して垂直となって邪魔な位置になるため、ストレート型のピンヘッダに取り替えて、プリント基板の平面と平行になるようにしました。 このとき、「リアルタイムクロックモジュール」については、RTC IC の DS3231 が搭載されている面側には LED も付けられていて、この LED が異常なくらいに眩しく光るので、この面が正面を向かないように(裏側を向くように)、 電池ホルダーが取り付けられている面が正面を向くように、ストレート型ピンヘッダを取り付けました。(下の実装写真を参照のこと) |
ストレート型ピンヘッダに取り替えて使用 |
|||
上の写真で、左上に位置する +5V のピンヘッダの右横にあるリセッタブルヒューズに、間違えて電流容量の大きなものを取り付けてしまいました。 写真ではそのままにしてありますが、 パターン図に示すように RLD60P050X などの 0.5 A 程度のものを取り付けてください。 |
| プリント基板(2)パターン図 (部品面) (Matrix88LED_ClockII_2PC.CE3) |
タクトスイッチ用の小基板を取り付けて、プリント基板(2) としての完成形の写真を次に示します。 プリント基板(2) 母基板とタクトスイッチ用基板との間のスペーサには、本機の(母基板を 10 mm 長のスペーサでケースに取り付けた)場合には、30 mm 長のものが適長* ですが手持ちになかったため、 15mm 長のオス+メス 六角ジュラコンスペーサ(廣杉計器 BS-315W)を2本繋ぎ合わせて使用をしました。 私の手持ちとしては 10 mm と 20 mm 長の中空のスペーサもあるので、長さとしてはその組み合わせでもよいのですが、この場合には 30 mm + 7 〜 8 mm 程度のビスが必要になります。 しかし、そんなに長いビスの持ち合わせもないので、 上述の 15mm 長のオス+メス 2本の組み合わせにしました。 |
タクトスイッチ用基板を取り付け後、真上から見たところ | タクトスイッチ用基板を取り付け後、斜め上から見たところ |
|
| ページトップ |
| プリント基板(2)パターン図 (ハンダ面) (Matrix88LED_ClockII_2PC1.CE3)
| ページトップ |
タイマースイッチ機能等の追加のための改造後のもので、右上に位置する L型の 4P のピンヘッダを追加しました。 ストレート型のピンヘッダは、相棒のコネクタを取り付けたときに、
デジタル温度湿度センサーモジュール(DHT22_mod)とぶつかってしまうので、使用ができません。 また、タクトスイッチ用小基板の5個のタクトスイッチの並びについてですが、改造前のような並びだとスイッチの操作をしていて、表示されているドットマトリクス LED の並びと感覚的に違和感を感じるので、下図や写真のように、 改造前のものとは左右の位置がまるっきり逆になるように改め修正をしました。 コネクタケーブルの配線替えと文字入れの変更が必要になります。 3つ目の変更点は、今回の機能追加とは関係はないのですが、回路図 の項の 追加説明 で述べたように、ハードウエアのちょっとした設計ミス(間違いではないのですが)を修正しました。 プリント基板(2) の左端にあるピンヘッダの、LU, LL, RU, RL 4つのピンと PIC との配線替えを行いました。 最新のプログラム のためには次図のような改造が必要です。 |
| プリント基板(2)パターン図 (部品面) (Matrix88LED_ClockII_2PCb.CE3)
| ページトップ |
| プリント基板(2)パターン図 (ハンダ面) (Matrix88LED_ClockII_2PC1b.CE3)
| ページトップ |
タイマースイッチ機能等の追加をするために、新たに追加作製をしたプリント基板で、 I/O エキスパンダ IC の PCF8574/74A を使用して新たな I/O ポートを 8 つ確保(実際に使用したポートは 7 つ)。
基板の右上に位置する4本の端子(GND、VCC、SDA、SCL)を、メイン基板である プリント基板(2) ... (改造後) とコネクタ、ケーブルで接続をします。 また、この追加機能のために新たなタクトスイッチ用の小基板の追加も必要で、右側に図と写真で示しておきます。 なお、この小基板はプリント基板(2)パターン図で示した小基板とは異なって、 ケースの上箱にスペーサを介して直接取り付けました。 |
| プリント基板(3)パターン図 (部品面) (Matrix88LED_ClockII_3PC.CE3)
| ページトップ |
| プリント基板(3)パターン図 (ハンダ面) (Matrix88LED_ClockII_3PC1.CE3)
| ページトップ |
使用したケースは、100均(セリア)で購入をした "No.1442 クリアシンプルケース ペントレー (山田化学)" という、ポリスチレンケースです。 この箱状のものを2個使用して、
お互いの内側を向かい合わせ、貝合わせのような形にして使用をしました。 そして、2個の箱を結合するのには、次のような 2 mm 厚のアクリル板を、箱の内側の左右側面に縦長に取り付けます。 まず、奥側の箱(下箱)にこのアクリル板を M3 のビスとナットで固定をし、 次に、手前側の箱(上箱)をアクリル板に切ったタップに M3 のビスで固定をします。 したがって、通常時の上箱の取り付け/取り外しには、上箱側のビスで行うようにします。 なお、図中で 赤色 で示した部分は、タイマースイッチ機能等の追加のために必要となった、追加加工の部分を表しています。 ( 2021/5/16 追加 ) |
|
上図内の (上箱正面図) において、数値が2段で表示されているものについては、下段の( )内の数値は単位がインチサイズで表したものとなっています。
| ケース加工図 (Matrix88LED_ClockII_CSb.CE3) | ページトップ |
(主要部品: IC, トランジスタ等) | (データシート) | ||
PICマイコン | .................... | PIC16F690 | |
MAX7219 8X8 ドット マトリックスモジュール | .................... | ( MAX7219 ) | |
RTCモジュール | .................... | ( DS3231 ) | |
温度湿度センサーモジュール | .................... | ( DHT22 ) | |
I/O エキスパンダ | .................... | PCF8574/74A |
| 部品表 | Excel ファイル (Matrix88LED_ClockII_parts.xls) | ページトップ |
PIC16F690 データシート | .......... | https://ww1.microchip.com/downloads/en/DeviceDoc/41262A.pdf |
MAX7219 データシート(日本語版) | .......... | https://akizukidenshi.com/download/ds/maxim/max7219_max7221_j.pdf |
DS3231 データシート | .......... | https://datasheets.maximintegrated.com/en/ds/DS3231.pdf |
AM2302 (DHT22) データシート | .......... | https://akizukidenshi.com/download/ds/aosong/AM2302.pdf |
DHT22 データシート(日本語訳) | .......... | https://pub.idisk-just.com/fview/Ofo5BntkKx3FZmmLkRMOsMRNS439HU6_QbdmJ9kpFT_ ... .pdf |
PCF8574/PCF8574A データシート | .......... | https://www.nxp.com/docs/en/data-sheet/PCF8574_PCF8574A.pdf |
PCF8574/PCF8574A データシート(日本語訳) | .......... | https://drive.google.com/file/d/1LEWwPEu_2RurksVSHlvkz9NAkpNZbkGV/view |
| ページトップ |
これから述べることは、このページ "189. ドットマトリクス 8 x 8 LED 表示時計 II ( MAX7219 版)" の内容とは一切関係はないのですが、
どうせ、このページ更新の進捗が遅れているついでに、この項を付け加えておきます。 私の愚痴話ですから読むのをパスしていただいて結構です。 私がパソコンでインターネットを始めた遠い昔には、ブラウザに Netscape Communications 社の Netscape Navigator を一時期使用していたこともありましたが、しばらくしてからは Microsoft 社の Internet Explorer を使用するようになり、その後もバージョンはどんどん更新をして行きましたが、ずっと Internet Explorer を使用し続けてきました。 ですから Firefox とか Chrome など、他のブラウザは自分で使用したことはもちろんのこと、 表示されているパソコン画面を見た経験すらありません。 また、私がホームページの作成をするようになったのは 14、5 年ほども以前のことで、その頃からエディタにはメモ帳(Notepad)、ブラウザにはもちろん Internet Explorer という組み合わせで最近まで開発をし続けてきました。 したがって、Internet Explorer で表示されるパソコン画面を基準としたホームページの作成をしてきました。 ところが、最近になって Edge がやたらと Internet Explorer の邪魔をするようになったのです。 私が Internet Explorer でいろいろと作業をしていると Edge が、 「 俺に、乗り換えろ。乗り換えろ。乗り換えろ。・・・ 変更をしろ。変更をしろ。変更をしろ。・・・ 」 としつこいぐらいに、うるさく纏いついてくるのです。 私はその都度 「 うるさい !! 」 といいながら、X ボタンをたたいて殺してやるのですが、忘れた頃にまた現れるのです。 そして最近では、Internet Explorer の一部の機能を取り上げて使用ができないようにしてしまいました。 それでもなお Internet Explorer を使用していると、ごく最近に至っては日に一度ぐらいの割で、それまで使用していた Internet Explorer を突然、無反応状態にしてしまうようになったのです。 仕方なく Internet Explorer には一度死んで頂いて、再度立ち上げ直すことになるのですが、Edge の無謀ぶりは日に日に増して行くようです。 私がこうまで Edge を嫌うのにはそれなりの訳があるのです。 どちらも Microsoft 社のブラウザといえども、いろいろと異なっている点が多いのです。 同一のホームページを表示させてみても Internet Explorer と Edge とでは、 微妙に異なっていてまったくの同一表示にはなりません。 現在、私のホームページを Edge などのブラウザでご覧になっている方は、例えばデフォルト設定での文字サイズが大き過ぎるとか、画面に表示がされている文章の行間が空き過ぎで全体のバランスが悪いとか、 いろいろなことを感じている方がいらっしゃると思いますが、上述したように Internet Explorer で表示されるパソコン画面を基準としたホームページの作成をしているためです。 また、Internet Explorer では当たり前に使用してきた機能が、Edge では使用することができない、ということがしばしばあるのです。 例えば、このページのトップのところに "Matrix88LED_ClockII_01.jpg" という画像ファイルが表示されていますが、この画像の "大きさ" が知りたいような場合(ホームページを作成するに当たって、画像サイズを知る必要がしばしばある。)に、 Internet Explorer では画像を右クリックすればメニューが現れるので、その項目の "プロパティ(R)" をクリックすることで、簡単に "610 x 250 ピクセル" という "大きさ" を知ることができます。 これに対して、Edge ではメニューの項目に "プロパティ(R)" がありません。 これはもしかしたら何かの設定でメニュー項目に追加ができるのかもしれませんが、 少なくてもデフォルトでは項目にないので "大きさ" を知ることができないのです。*1 また、このページの回路図の項の最終行で
しかし、これもデフォルトの Edge では "回路図エディタ bsch3v.exe" が起動されることはなく、ファイルの中身がテキストデータになっているためか、私にとってはまったく意味のないデータが Edge の画面に表示されるばかりで、 回路図ファイルの編集を行うことができません。 これも何かの設定次第で "回路図エディタ bsch3v.exe" を起動させることができるようになるのかもしれませんが、まだ Edge に不慣れな私にはその方法が分かりません。*2 このように、Internet Explorer とは異なる点が多い Edge では、いくら Microsoft 社のお勧めでも私にとっては使い辛いばかりで、正直、その気にはなれないでいたのです。 しかし、Microsoft 社の強引な Edge の売り込み、押し付けのために、可哀そうな Internet Explorer はどうやら見捨てられてしまったようで、上述したように、一部の機能を取り上げられたり、時々無反応状態にさせられる Internet Explorer を、 これまでのように今後も使い続けることには私もいよいよ限界となりました。 そして、今回の機能追加のためのホームページの更新作業から、長年連れ添ってきた Internet Explorer とも泣く泣くお別れをして、仕方なく使い辛い Edge と手を組むことになりました。 Edge の数々の妨害行為に屈した形にはなってしまいましたが、不本意ながらこれもやむを得ません。 したがって、今後のブラウザ表示は Edge で表示されるパソコン画面を基準としたホームページの作成に、徐々に切り替えて行かなければなりません。 どのブラウザに対してもほぼ同様な表示をさせるような作成の方法もある ― とは思うのですが、 プロではない今の私には難しい課題です。
|