| ホーム |     私の電子工作作品集

189. ドットマトリクス 8 x 8 LED 表示時計 II ( MAX7219 版)

[ 初公開日: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個も駆動させるための配線量が膨大?となり、本当に大変だったことを思い出します。 それに比べて本機では、上述のディスプレイモジュールを使用するので、配線量の膨大な問題は一挙に解決することができます。  この "189. ドットマトリクス 8 x 8 LED 表示時計 II ( MAX7219 版)" は、実は前回公開をした、"190. I2C IF モジュール + LCD(1602A) 表示時計" よりも先に製作を始めていたのですが、 クロックに使用するオシレータ・モジュールや温度センサ、PIC に、何を使用するかをまだ決めかねていて、上述のドットマトリクス 8 x 8 LED を使用したディスプレイ基板だけが、ほとんど完成間近までになっていました。

 したがって、まだ 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)を参照してください。

■ 回路図 ■


追加説明 ( 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_ClockIIb.CE3) | 回路図 (ExpanderPCF8574A.CE3) | ページトップ |

■ 使用したドットマトリクス LED モジュールについて ■

 このドットマトリクス・ディスプレイモジュールには、ドライバ IC MAX7219 に DIP パッケージを使用したものと、SOP パッケージを使用したものの2種類があって、冒頭で述べた "余りにも安価だった" のは、 実は DIP パッケージの方で、SOP パッケージのものは DIP パッケージのものに比べて 1.5 倍ほどします。

 前者の DIP パッケージ版は、モジュールを横に並べて使用することができない* ため、本機には不向きです。 後者の SOP パッケージ版は、モジュールのプリント基板と LED が同サイズになっていて、 いくつも横に並べて使用することが可能となっています。 本機では後者を採用しました。
      * 横に並べて使用することができない: ただし、モジュールの位置を 90 度左に回転をさせて縦長の形にして横に並べ、かつ、文字フォントデータもソフトウエアで 90 度左に回転をすれば、 使用が可能である。
 また、前者の DIP パッケージ版は DIY ということで、すべてのパーツを自分でハンダ付けをする必要がありますが、後者の SOP パッケージ版は MAX7219 と CR は初めからハンダ付けがしてあるので、 LED 用のソケットとピンヘッダだけを自分でハンダ付けをします。 いずれも、MAX7219 と LED 用のソケット間の配線は、プリント基板上で済まされているので面倒さがありません。

左: DIP パッケージ MAX7219 を使用のモジュール   右: SOP パッケージ MAX7219 を使用のモジュール 左: DIP 版に添付の LED   右: 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 のピン番号は間違っているようです。 ―― う〜ん、どうすれば良いのか。
    (1) ディスプレイモジュールのピンソケット番号
    (誤り)
    (2) マトリクス LED の点灯時のピン番号
     ( 1088 AS と印字面を下にしたとき )
    (3) マトリクス LED の点灯時のピン番号
     ( 1088 AS と印字面を上にしたとき )
 モジュールのソケットに差し込む方向は、マトリクス LED のピン列のどちらを上にするかの二者択一ですから、今度はマトリクス LED の上下の位置を逆にして、(2) のときと同様に、 各ピンに電圧を加えたときの結果が (3) です。 ただし、(3) 内に書き込んだマトリクス LED のピン番号は、本来(DIP パッケージ版に添付されていた LED)のマトリクス LED のピン番号ではなく、 (1) のモジュールのピンソケット番号に置き換えてあります。

 このときのマトリクス LED のX軸とY軸に現れる信号名 Dig x と Seg x は、両軸でそれぞれが分離され、しかも順序良く信号順に並んでいることが分かります。

 しかし、(3) ではマトリクス LED の本来のピン番号ではなく、 (1) のモジュールのピンソケット番号に合わせてある、というのはやはり不合理だと思います。 そこで再び、(3) での番号を本来のマトリクス LED のピン番号に戻したものが (5) で、かつ、モジュールのピンソケット番号の方を、本来のマトリクス LED のピン番号に置き換えたものが (4) になっています。
    (4) ディスプレイモジュールのピンソケット番号
    (正しい)
    (5) マトリクス LED の点灯時のピン番号
     ( 1088 AS と印字面を上にしたとき )
    (6) 点灯時のピン番号

    K: カソ−ド
    A: アノ−ド
 したがって、(4) と (5) が正解で、モジュールのピンソケット番号は、モジュール基板の印字には関係なく (4) のように考え、マトリクス LED のピン番号は、本来の固有のピン番号を使用することが正解です。 ただし、本機で採用した SOP パッケージ版に添付されていた LED には、上述もしましたが、ピン番号の表記は一切ないので注意が必要ですが、"1088AS" の文字列が印字されている側面を上側にして、 モジュールのソケットに差し込めば問題はありません。 上の 回路図 には、この (5)、すなわち (4) の並びのピン番号で記入がしてあります。

 このようにして、ドライバ IC MAX7219 が SOP パッケージのものを使用することにしたのですが、もう一つ、不備な点があります。 それは、上に示した 写真 からも分かるように、 DIP パッケージのものには電解コンデンサが添付されてきますが、SOP パッケージのものには添付されず、それ以前にプリント基板のパターンそのものに取り付けるような考慮がありません。

 しかし、MAX7219 データシート(日本語版) の 10 ページには、"桁ドライバのピーク電流に起因する電源リップルを最小限に抑えるために、できる限りデバイスに近い位置で、V+ と GNDの間に 10μFの電解コンデンサと 0.1μFのセラミックコンデンサを接続してください。" と書かれています。
          電解コンデンサを取り付けた様子1 電解コンデンサを取り付けた様子2 電解コンデンサを取り付けた様子3
 仕方がないので 10μFの電解コンデンサを、モジュール6個とも、上の写真のように自前で追加して取り付けました。

 また、このモジュールにはL型のピンヘッダが添付されてきますが、L型ではプリント基板に実装するときには都合が悪いので、ストレート型のピンヘッダに取り替えました。 (詳細は、プリント基板(1)パターン図 (部品面) を参照のこと。)

| ページトップ |

■ その他の使用モジュールについて ■

 冒頭で述べたように、本機でも前回公開の "190. I2C IF モジュール + LCD(1602A) 表示時計" で使用をした、「リアルタイムクロックモジュール」、 「デジタル温度湿度センサーモジュール」と同じモジュールを使用しました。 これらのモジュールを使用するに当たって、私が感じた問題点やその他のことなどについては、そちらのページの 使用した各モジュールについて のそれぞれの項、リアルタイムクロックモジュールデジタル温度湿度センサーモジュール で詳述をしているので参考にしてください。 写真だけを次に再掲をしておきます。

 そして、これらのモジュールを本機で使用するに当たっては、下写真に写っているL型ピンヘッダを、ストレート型のピンヘッダに取り替えて使用をしました。 L型ピンヘッダのままだとプリント基板に取り付けたときに、 基板の平面に対して垂直となって空間的に邪魔になるため、どちらも交換をしました。

 また、「リアルタイムクロックモジュール」のバックアップ用の電池については、一次電池の CR2032 を使用するために簡単な改造が必要で、それについても前回公開ページの リアルタイムクロックモジュール で述べているのでそちらを参照してください。

リアルタイムクロックモジュール( DS3231 ) デジタル温度湿度センサーモジュール( DHT22(AM2302) )

 話は変わって、最近私が感じている愚痴話しを少々 ・・・ アマゾンにおける中華モールに出品されているたくさんの電子部品は、それらの機能の高さや種類の豊富なこともさることながら、 我々アマチュアにとってはそれらの価格の安さが一番の気になるところです。 どんなに素晴らしい部品であっても、高価格であってはアマチュアの私には手を出すことができません。

 そんな意味からも最近のアマゾンを見ていると、私にとっては魅力が半減をしてしまいました。 私がこれらの中華モールと付き合い出してからまだ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割以上は値上がりをしているようです。

| ページトップ |

■ ケース外観と内部の様子 ■

ケース正面の斜め上から見たところ

上側面
左側面 正面 右側面
下側面
後面

ケース蓋を取って内部の様子を見たところ

時間の表示をしている例 ( 拡大 )、 実際の発光色はこのような黄色ではなく赤っぽいオレンジ色です。

(サイズの比較) 上: 初代 ドットマトリクス 8 x 8 LED 表示時計、 下: 本機 ドットマトリクス 8 x 8 LED 表示時計 II ( MAX7219 版)

機能追加後の正面 ( 2021/5/16 追加 )
機能追加後の後面 ( 2021/5/16 追加 )

| ページトップ |

■ 機能概要と使用法 ■

 本項におけるドットマトリクス LED の表示写真のすべては、フィルタ越しに撮ったものを使用しました。 素のままで撮ったものは、発光している LED が滲んだように写ったりして、うまく撮れなかったためです。 なお、フィルタには 100 均で購入をした赤色の透明な "下敷き" を、フィルタ代わりとして使用しました。
  • 本機の電源を投入すると、"ピポッ" と開始ブザー音を出力後、ドットマトリクス LED の点灯チェックを兼ねたデモ表示が数秒間行われ、続いて 1 秒間の空白をおいた後、0.5 秒間ずつ9から0までの数字をカウントダウンしながら、 ドットマトリクス LED 全6桁分の表示テストを計5秒間行う。

  • 次に 1 秒間の空白をおいた後、プログラムのバージョン番号がマトリクス LED に約3秒間表示される。
    		バージョン番号		[ Ver. x.xx ]
    

  • その後1秒間の空白をおいた後、RTC(リアルタイムクロック)モジュールの状態がチェックされ、初めて電源を入れたときや、バックアップ用バッテリーが異常なとき等に、次のエラーメッセージがマトリクス LED に表示されるとともに、 "ブッブー" ブザー音が出力される。
    		エラーメッセージ	[ Err. DS3231 ]
    

  • ユーザはバックアップ用バッテリーの状態等を確認してその対処を行った後、SIGNAL スイッチを除いた4つのスイッチの内のどれかを押して応答をする。

  • すると、RTC(リアルタイムクロック)モジュールが初期設定され、エラーフラグがクリアされるとともに、年月日、時分秒が本機をプログラミングしたときの初期値、 "2021/01/01"、"00:00:00" に設定がされて時計機能が開始される。 この状態を NORMAL モード という。

  • しかし、これらは現在値とは異なるため、スイッチ操作による手順で現在の年月日、時分秒に設定をし直す必要がある。
    		時間表示		[ xx:xx:xx ]
    
    初期設定後、01 分 01 秒が経過

  • その手順の説明の前に、本機における表示モードを先に説明をしておく。 (このときのモードの意味は上述の NORMAL モードと対比するものではなく、単に表示する内容の種類を表している)。 本機には上述の時間表示の他に、 年月日(曜日)表示、温度表示、湿度表示の機能を持っている。
    		モード 0          	  			モード 1
    		時間表示 [ xx:xx:xx ]				年月日表示 [ xx.xx.xx ]
    

  • 		モード 2          				モード 3 
    		温度表示 [ _- xx.x℃ ]				湿度表示 [ _x xx.x% ]		
    

  • なお、上述のモード 0 〜 モード 3 の他にモード 4 もあって、これらは MODE / SET スイッチ を押すごとにモード番号が増加をし、マトリクス LED に表示される内容が切り替わる。 そして モード 4 の次は再び モード 0 に戻る。
    		┌→ 時間表示 → 年月日表示 → 温度表示 → 湿度表示 → オートモード ─┐
    		│ (モード 0)  (モード 1)  (モード 2) (モード 3)  (モード 4)  │
    		│                                  │
    		└──────────────────────────────────┘
    
  • オートモード(モード 4) を選択したときには、モード 0 〜 モード 3 の個々の内容表示を、次図に示すように自動的に 10 秒ごとに内容を切り替えて、1分ごとにすべての表示を繰り返す。
          		毎 05 〜 14 秒の間:	年月日表示
          		毎 25 〜 34 秒の間:	温度表示
          		毎 45 〜 54 秒の間:	湿度表示
    		上記以外の間:		時間表示
    
    		 (___: 時間表示、NNN: 年月日表示、OOO: 温度表示、SSS: 湿度表示)
    
    		00        10        20        30        40        50        00
    		.    .    .    .    .    .    .    .    .    .    .    .    .
    		_____NNNNNNNNNN__________OOOOOOOOOO__________SSSSSSSSSS_____
    
    
  • このとき、MODE スイッチを何度も続けて押していると、現在のモードが何なのか分かり辛くなるときがあるため、マトリクス LED の1桁目(最左端桁)でそのときのモード種類を表示する。

  • すなわち、MODE スイッチを押した直後に、1桁目の横1列の8個の LED が次のように数秒間だけ表示される。 その後1桁目の LED は通常の表示に戻る。
    (例ではオートモード(モード 4) を表す。 ●は点灯、○は消灯)
    		(例)	○○○○○○○○
    			○○○○○○○○
    			○○○○○○○○
    			●●●●●●●● ← オートモード
    			○○○○○○○○ ← 湿度表示
    			○○○○○○○○ ← 温度表示
    			○○○○○○○○ ← 年月日表示
    			○○○○○○○○ ← 時間表示
    
  • また、年月日表示(モード 1) を選択したときには、年月日の表示とそのときの曜日の表示が、1秒ごとに切り替わってマトリクス LED に交互に表示がされる。
    		年月日表示 [ xx.xx.xx ]			曜日表示 [ _( xxx )_ ]
    

  • 本機では12/24時間の両表示機能を持ち、通常の NORMAL モード中であれば UP スイッチ を押すことによっていつでも切り替えることができる。 すなわち、時間表示(モード 0) 中だけでなく、 年月日表示(モード 1) 〜 オートモード(モード 4) 中であっても切り替えられる。 ただし、時間表示(モード 0) でなければ切り替わったことを目で確認することはできない。
    		時間表示 [ xx:xx:xx ]
    
    24H 表示 12H 表示

  • そして、12時間表示の場合には、左列の :(コロン)の上側 LEDで AM を、また下側 LEDで PM を、1秒間隔で点滅を繰り返すことで、それぞれを表現する。
    						  ┌─ AM
    						  ↓
    		12時間表示の場合		xx:xx:xx
    						  ↑
    						  └─ PM
    
  • 次に、12時間表示と24時間表示の対応を示す。 なお、12時間表示の場合だけ 先頭の1桁目(左端)は、ゼロサプレスを行なって表示をする。
    		(24H)  00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23
    		(12H)  12  1  2  3  4  5  6  7  8  9 10 11 12  1  2  3  4  5  6  7  8  9 10 11
    		       am am am am am am am am am am am am pm pm pm pm pm pm pm pm pm pm pm pm
    
  • さて、ここで話を戻して、年月日、時分秒の設定を変更する手順としては、現在、時間表示(モード 0) 〜 オートモード(モード 4) のどの内容が表示されている場合においても可能で、まず、表示内容(モード)を切り替えるときのように "チョン押し" ではなく、同じ MODE / SET スイッチ を "長押し" することによって、年月日、時分秒を自由に変更することができる、SET モード に移行をする。

  • すなわち、MODE / SET スイッチ を3秒以上 "長押し" をしていると、マトリクス LED に次のメッセージが表示されて、NORMAL モードから SET モード に移行をしたことを知らせる。
    		SET モード に移行	[ ** SET ** ]
    

  • ユーザはそれを確認してスイッチから指を離すと、次には必ず年月日の表示パタンで "年" だけが表示される。 この表示されている部分がこれから変更することが可能な対象項目であることを表している。
    		年月日表示("年"だけ表示)[ xx.__.__ ]
    

  • ここで変更が必要であれば、UP スイッチ を押すごとに現在値より1ずつ増加し、また DOWN スイッチ を押すごとに1ずつ減少するので、適宜希望する値に変更をする。 また、このときの増減幅を大きくしたい場合には、 UP スイッチ、または DOWN スイッチを押したままにしていると、1秒当たり 4 〜 5 の増減幅で更新をするようになる。

  • "年" の変更が済んだら、再び MODE / SET スイッチ を押すと、次には年月日の表示パタンで "月" だけが表示され、変更の対象項目が次に移動したことを示す。 同様に変更が必要であれば、 UP スイッチ、または DOWN スイッチを押すことによって、適宜希望する値に変更をする。
    		年月日表示("月"だけ表示)[ __.xx.__ ]
    

  • 同様に、SET スイッチ を押すごとに "年"、"月"、"日"、"時"、"分"、"秒" と変更の対象項目が移動するので、該当の項目を選択後 UP または DOWN スイッチ によって値を変更設定する。 最後の "秒" 設定後にもう一度 SET スイッチを押すと、SET モードから抜け出して再び NORMAL モード に移行をし、必ず時間表示(モード 0) に戻る。

  • この SET スイッチによって対象項目を選択中に、誤って希望する項目を通り過ぎてしまったような場合には、BACK スイッチ を押すごとに、ひとつずつ前の項目に戻ることができる。 ZERO / BACK スイッチは、"年"、"月"、"日"、"時"、"分"、"秒" の設定中にはこのような機能を有する。
    		               BACK   BACK   BACK   BACK   BACK
    		               ←─   ←─   ←─   ←─   ←─
    		           SET    SET    SET    SET    SET    SET    SET
    		5つのどれかのモード ─→ 年 ─→ 月 ─→ 日 ─→ 時 ─→ 分 ─→ 秒 ─→ 時間表示モード(モード 0) 
    		             │                   ↑
    		           BACK└───────────────────┘
    
  • このように、各対象項目が選択されたときの表示パタンをまとめると、次のように表示される。
    	   (xx 選択項目、 __ 消灯桁)
    
    		xx.__.__  "年"が選択,		__.xx.__  "月"が選択,		__.__.xx  "日"が選択
    		xx:__:__  "時"が選択,		__:xx:__  "分"が選択,		__:__:xx  "秒"が選択
    
    表示例
    "年"が選択 "月"が選択 "日"が選択
    "時"が選択 "分"が選択 "秒"が選択

  • なお、NORMAL モードのときに12時間表示に設定がされている場合には、SET モードに移行後もその表示が反映されるため、SET スイッチによって項目の "時"、"分"、"秒" を選択時には、左列の :(コロン)の上側 LED点灯 + 下側 LED消灯で AM を、上側 LED消灯 + 下側 LED点灯で PM を、それぞれ表現をする。 また、この場合には先頭の1桁目(左端)のゼロサプレスも行われる。

  • ZERO/BACK スイッチは、SET モード中で "年"、"月"、"日"、"時"、"分"、"秒" の設定のときには、上述のように BACK スイッチとして機能をするが、それ以外の NORMAL モード中には、毎正時プラス・マイナス5分以内に押されると 00 分 00 秒に設定するための ZERO スイッチ として機能をする。

  • 年月日カレンダー機能について、うるう年の計算は 2001年 〜 2399年までの範囲で対応をしている。 ただし、年の表示は下2桁のみになる。

  • SIGNAL スイッチ は、毎時の時報音出力のブザー音を、押すごとに有効/無効に交互に設定をする。 このときの設定状態は視覚的な表示がないため、有効になったときには "ピピピッ" 音を、無効になったときには "ブー" 音を、 それぞれ鳴らして知らせる。 なお、電源を投入した直後は有効状態に設定がされている。 また、有効に設定時には、時間表示(モード 0) 以外のモードにおいても、毎時の時報音は有効(鳴る)なので注意が必要である。

  • 本機では年月日(曜日)、時分秒の他に、温度、湿度を表示するモードがあることは上述の通りであるが、年月日(曜日)、時分秒の情報は、上述した RTC(リアルタイムクロック)モジュール( DS3231 )と PIC とを、 I2C シリアル通信によって情報を得ているのに対して、温度、湿度情報は、デジタル温度湿度センサーモジュール( DHT22(AM2302))と PIC との間を、たった1本だけのデータ線によるシリアル通信によって情報を得ている。

  • そして、前者の場合には1秒間隔で通信を行うが、後者の場合には本機では4秒間隔(センサーの仕様により2秒以上の間隔が必要)で通信を行っている。 また、後者のデジタル温度湿度センサーモジュール( DHT22(AM2302))との通信においては、 通信中にエラーが起こった場合には、次のようなエラーメッセージがマトリクス LED に表示されるとともに、"ブッブー" ブザー音が出力される。
    		タイムアウトエラー				チェックサム(CRC)エラー
    		[ Err. DHT22 TO ]				[ Err. DHT22 PE ]
    

  • 左の例では通信中にタイムアウトエラーが起こった場合で、データ線上の "high" または "low" 電圧の続く時間が、仕様で決められた時間をオーバーした場合に発生する。 また、右の例ではチェックサム(CRC)エラーが起こった場合で、 DHT22(AM2302)からは1度の通信で、湿度情報が 16 ビット、温度情報が 16 ビット、チェックサムが 8 ビットの、計 40 ビットが送られてくるが、それらの情報でパリティチェックを行った結果、エラーが起こった場合に発生する。

  • (補足)本機に電源を入れてから1度目の温度湿度センサーモジュール( DHT22(AM2302))との通信には、どういう訳か?時々チェックサム(CRC)エラーが起きるようである。 しかし、その4秒後以降には正常な通信が行われて、 エラーメッセージは消滅してしまうので、あまり問題はないものと思う。
― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ―
< 新しい機能 >  ( 2021/5/16 追加 )
  • あらかじめ設定をしておいた時間(時分)になると、スイッチ(リレー)が ON/OFF/SLP 動作をする タイマースイッチ機能、また、温度の設定をしておくとその設定温度に達したときに、同様にスイッチ(リレー)が ON/OFF 動作をする 温度スイッチ機能 の2つを、新しい機能として本機に追加をした。

  • 前者のタイマースイッチには CH 1CH 2 の2系統を用意し、後者の温度スイッチには CH 3 の1系統を用意している。 そして、これらの時間(時分)や温度の設定情報を記憶しておくためのメモリは、 本機では3系統分を共通で使用し、合計 20 メモリまでを可能とする。

  • また、これらのタイマースイッチ機能、温度スイッチ機能のユーザー・インターフェースとしての操作には、あらかじめそれらの情報をメモリに設定をしておくための メモリ設定機能、 メモリに設定されている情報をいつでも読み出して確認ができる メモリ読み出し機能、不要になったメモリ情報を消去するための メモリ消去機能 がある。

  • これらの操作には、新たに設けた4つのスイッチと、従来からある SIGNAL スイッチを除いた4つのスイッチとの組み合わせで行う。

  • まず、メモリ設定機能 の操作は、本機が NORMAL モードのときに、EXP スイッチ を押しながら CH 1CH 3 スイッチ のどれかを押す ことによって始める。 CH 1 〜 CH 3 スイッチは上述のように、どの系統にスイッチ機能を設定するかを選択するものである。

  • すると、選択をした CH x スイッチによって、マトリクス LED には次の2種類の内のどちらかが表示される。 すなわち、CH 1、CH 2 スイッチの場合には左側の、CH 3 スイッチの場合には右側の形式で表示がされ、なおかつ、 先頭の2桁が点滅表示 をしてこれから設定する対象項目の位置(~~)を表している。
    		時分設定表示 [ 00:00 00 ]			温度設定表示 [ 00.0℃ 00 ]
    		         ~~        			         ~~
    
    		 └────┘この2桁が点滅			 └────┘この2桁が点滅
    
  • CH 1、CH 2 スイッチの場合には、設定をする "時"、"分" の内の "時" の位2桁 であり、CH 3 スイッチの場合には、設定をする 温度値の整数部2桁 が設定の対象項目となる。 なお、すべての設定項目の初期値は "00"、または "0" が表示される。

  • それぞれの値を設定するには、UP スイッチ を押すごとに現在値より1ずつ増加し、また DOWN スイッチ を押すごとに1ずつ減少するので、適宜希望値に設定をする。 また、このときの増減幅を大きくしたい場合には、 UP スイッチ、または DOWN スイッチを押したままにしていると、1秒当たり 4 〜 5 の増減幅で更新をするようになる。

  • "時" または、温度値の整数部の設定が済んだら、MODE / SET スイッチ を押すと、次には "分" の位2桁 または、温度値の 小数部1桁 が点滅表示され、設定の対象項目が次に移動したことを示す。 同様に、UP スイッチ、または DOWN スイッチを押すことによって、適宜希望値に設定をする。
    		時分設定表示 [ xx:00 00 ]			温度設定表示 [ xx.0℃ 00 ]
    		            ~~        			            ~
    
  • なお、上述までで設定値の範囲は、"時" は "00" 〜 "23"、"分" は "00" 〜 "59"、温度値の整数部は "00" 〜 "99"、小数部は "0" 〜 "9" であり、それ以外の値を設定することはできない (プログラムが制限をかけている)。 このように、"時" の設定には常に 24 時間表示で行うこと。

  • "分" または、温度値の小数部の設定が済んだら、再度 MODE / SET スイッチ を押すことによって、点滅表示が次の設定の対象項目 "曜日" 1桁 に移動する。
    		時分設定表示 [ xx:xx 00 ]			温度設定表示 [ xx.x℃ 00 ]
    		               ~        			                ~
    
  • "曜日" の設定方法は CH 1 〜 CH 3 すべて共通で、UP スイッチ、または DOWN スイッチによって、次の対応表のように数値によって指定をする。 なお、このように温度スイッチの設定にも "曜日" の条件を加えてある。

          曜日 毎日
          設定値 0 1 2 3 4 5 6 7

  • 同様に、再々度 MODE / SET スイッチ を押すと、4つ目(最後)の設定項目の右端1桁に点滅表示が移動する。 CH 1 〜 CH 3 の各 リレーの動作種類 をどのように制御するかを設定するもので、 UP スイッチ、または DOWN スイッチによって、次の対応表のように数値によって指定をする。
    		時分設定表示 [ xx:xx x0 ]			温度設定表示 [ xx.x℃ x0 ]
    		                ~        		                 ~
    
          CH 1、CH 2 CH 3
          動作種類 OFF ON SLP OFF ON
          設定値 0 1 3 0 1

  • 上表のように、CH 3 の温度スイッチにはスリープ(SLP)動作を設定することはできない。 また、CH 1、CH 2 のタイマースイッチにおいて設定値 "2" は、 プログラムの内部で使用するものでユーザが設定することはできない(ON 動作をしたリレーが1時間後に OFF 動作をさせるときの情報に使用)。

  • また、UP スイッチ、または DOWN スイッチによって、上表2つのそれぞれの設定値以外を指定しようとしても、プログラムがそれぞれで制限をかけているので不可能である。

  • なお、これまでの MODE / SET スイッチによって対象項目を選択中に、誤って希望する項目を通り過ぎてしまったような場合には、BACK スイッチ を押すごとに、ひとつずつ前の項目に戻ることができる。 また、これらの項目を設定途中に何らかの理由によって中止をしたいような場合には、EXP スイッチ を押すとそれまでに設定をしてきた項目が破棄され、メモリ設定機能を終了して、NORMAL モードに戻る。

  • 最後の リレーの動作種類 を設定後、すなわち、右端1桁が点滅表示をしているときに、もう一度 MODE / SET スイッチ を押すと、今まで入力をしてきたタイマースイッチ、または温度スイッチの設定情報を記憶するために、 プログラムは PIC 内部の RAM メモリに書き込み動作を行う。

  • タイマースイッチ、または温度スイッチの設定情報が正常に書き込まれると、"ピピッ" ブザー音を鳴らしてユーザにそれを知らせ、再び新たなメモリ設定のために、すべての設定項目を初期値の "00"、または "0" に戻して、 先頭の2桁が点滅表示をするので、続けて次の設定情報の入力を行うことができる。

  • ただし、この場合には CH x は前回のものが引き継がれるので、もしも他の CH x に変更をしたい場合には EXP スイッチ だけを押して、一旦、それまでの CH x での メモリ設定機能を終了 させて NORMAL モードに戻し、 新たに EXP スイッチ を押しながら CH 1CH 3 スイッチ のどれかを押して、CH x の選択をし直す必要がある。

  • また、プログラムが PIC 内部の RAM メモリに書き込み動作を行うときに、既に 20 メモリまでが書き込まれて満タン状態の場合には、次のエラーメッセージがマトリクス LED に表示されるとともに、 "ブッブー" ブザー音が出力される。
    		エラーメッセージ	[ Err. MEM    ]
    

  • このエラーメッセージが表示された場合には、いつまでもその表示状態が続くので EXP スイッチ を押して メモリ設定機能を終了 させると、NORMAL モードに戻る。

  • 次に、メモリ読み出し機能 の操作は、本機が NORMAL モードのときに、CH 1CH 3 スイッチ のどれかを押す ことによって始める。 本機では、CH x 別にメモリ読み出しを行うようにプログラミングがされているため、 メモリ内容の確認を希望する CH x スイッチを押す。

  • すると、直ちに RAM メモリ内が検索されて CH x の設定情報が見つかった場合は、押した CH x スイッチによって、次の2種類の内のどちらかの形式でマトリクス LED に表示がされる。 メモリ設定機能のときと同様に、CH 1、CH 2 スイッチの場合には左側の、CH 3 スイッチの場合には右側の形式で表示がされる。 ただし、メモリ設定ではないため点滅表示が行われることはない。
    		時分設定表示 [ xx:xx xx ]			温度設定表示 [ xx.x℃ xx ]
    
    例. 15 時 00 分、7:土曜、1:ON 例. 27.0 ℃、0:毎日、1:ON

  • このとき 20 メモリの内の先頭から検索が行われ、まず、最初に見つかったメモリ内容が上記の形式で表示がされる。 その後、続けて CH x スイッチを押すごとに、2番目に見つかったメモリ内容、3番目に見つかったメモリ内容、・・・ の順にマトリクス LED に表示がされる。

  • やがて最終の 20 メモリを過ぎると、一旦、次のように空白表示を行い、なおも CH x スイッチが押されると、また先頭からの検索に戻って以上の表示が繰り返される。
    		時分設定表示 [ __:__ __ ]			温度設定表示 [ __.__ __ ]
    

  • このメモリ読み出し機能では、現在検索中の CH x(例えば CH 1)スイッチのときに、異なった CH x(例えば CH 3)スイッチを押した場合には、検索対象が(CH 3 に)変更されて、新たに 20 メモリの内の先頭から検索がされるようになる。

  • このようにメモリ内容の確認中に、現在、マトリクス LED に表示がされている内容が不要となった場合には、そのときに DOWN スイッチ を押すことによって、20 メモリの内のその情報だけを 個別に消去 することができる。

  • メモリ内容が消去されると、次の左のメッセージが約 3 秒間マトリクス LED に表示されるとともに、"ピピッ" ブザー音が出力される。 なお、上述のように最終の 20 メモリを過ぎて空白表示がされているときに DOWN スイッチ を押すと、 右のメッセージが約 3 秒間表示されるとともに、"ブー" ブザー音が出力される。
    		CLR メッセージ	[    CLR    ]			NOT メッセージ	[    NOT    ]
    

  • 上述の操作で、現在表示中のメモリ内容が消去されてメッセージが表示された約 3 秒後、消去したメモリ内容の次の順に記憶がされている同じ CH x の内容が読み出されて、新たにマトリクス LED に表示がされる。

  • このように メモリ読み出し機能 では、後からの DOWN スイッチの併用によって 個別のメモリ消去機能 も有している。

  • また、このメモリ読み出し機能を終了させるためには、前のメモリ設定機能と同様に EXP スイッチ を押すことによって、再び NORMAL モードに戻る。

  • 上述の例では、個別に1件ずつメモリ内容を確認しながら消去をする方法であるが、これとは別に CH 別に一気に消去 をしてしまう方法もある。

  • 本機が NORMAL モードのときに、DOWN スイッチ を押しながら CH 1CH 3 スイッチ のどれかを押す と、その指定された CH x のメモリ内容のすべてが一気に消去され、次の左のメッセージが約 3 秒間マトリクス LED に表示されるとともに、"ピピッ" ブザー音が出力される。 なお、このとき指定された該当の CH x のメモリ内容が1つもなかった場合には、右のメッセージが約 3 秒間表示されるとともに、"ブー" ブザー音が出力される。
    		CLR メッセージ	[ CHx CLR    ]			NOT メッセージ	[ CHx NOT    ]
    

  • そして、メモリ設定機能やメモリ読み出し機能とは異なって、メッセージの表示時間約 3 秒が過ぎると、再び NORMAL モードに戻る(EXP スイッチの操作は不要)。

  • 上述のメモリ設定機能で一旦メモリに記憶設定された情報は、本機の電源が断になって消滅をした場合や、ユーザがメモリ消去機能で故意に消去をしない限り、いつまでもメモリ内に存在し続けて、タイマースイッチや温度スイッチの プログラムでの監視対象となる。

  • タイマースイッチ の情報としては、"時"、"分"、"曜日"、リレーの動作種類、CH 番号がメモリに記憶されているため、本機の時計機能が毎分 "00" 秒になると、該当の情報がメモリ内に設定されていないかどうかを、 監視プログラムが検索をする。

  • また、温度スイッチ の情報としては、温度値の整数部、小数部、"曜日"、リレーの動作種類、CH 番号がメモリに記憶されていて、本機では 4 秒ごとに温度情報をセンサから読み出して更新をするため、その都度監視プログラムが同様に検索をする。 ただし、この温度スイッチの場合には、現在、設定情報がメモリ内に存在しているかどうかだけを常にプログラムが把握をしていて、存在している場合に限ってメモリ検索を行う。

  • このようにメモリ検索をした結果、該当の設定情報がメモリ内に存在した場合には、それぞれの CH 番号のスイッチ(リレー)が、指定をされた動作種類( ON/OFF/SLP )に応じた動作をするとともに、 SIGNAL スイッチ によってアラーム音の出力が有効に設定されている場合には、スイッチが ON 動作のときには "ピポッピポッ" ブザー音を、また OFF 動作のときには "ブーー" ブザー音を併せて出力をする。

  • なお、以上述べてきたメモリ設定、メモリ読み出し、メモリ消去、それぞれの操作時に伴う "ピピッ" ブザー音、"ブッブー" ブザー音、"ブー" ブザー音は、SIGNAL スイッチ によるアラーム音の有効/無効にかかわらず出力されるので、 注意が必要である。

< 新しい機能 2 >  ( 2021/12/7 追加 )
  • 改めて言うまでもなく本機には、8 x 8 ドットマトリクス LED が6個使用されているが、それらの LED の輝度について従来は、プログラミング時の一定値に固定がされていて、変更することはできなかった。 それを簡単なスイッチ操作によって自由に調節ができるように、機能追加を行った。

  • 本機が NORMAL モードのときに、毎正時プラス・マイナス5分以内に ZERO/BACK スイッチが押されると、00 分 00 秒に設定するための ZERO スイッチ機能が優先するが、それ以外の、毎正時プラス・マイナス5分以外のときにZERO/BACK スイッチ を押しながら UP スイッチ、または DOWN スイッチ を押す ことによって、LED の輝度調節として機能 をするようになる。

  • まず、このように毎正時プラス・マイナス5分以外のときに、ZERO/BACK スイッチ を1秒間以上押している と、次のように、現在の輝度レベルの設定値がマトリクス LED に表示される。
    		輝度レベルの表示例	[ Bri. Lev = 0 ]  ...  Brightness
    

  • そして次に、ZERO/BACK スイッチを押したままで UP スイッチ、または DOWN スイッチを押す と、輝度レベルの設定値が更新されるとともに、マトリクス LED の表示も更新される。

  • 本機では、輝度調節のレベルを 0 から 9 までの 10 段階 * を、変更設定することができる。 すなわち、ZERO/BACK スイッチを押しながら、UP スイッチを押すごとに現在値より1ずつ増加し、 また DOWN スイッチを押すごとに1ずつ減少する。 ただし、このときにそれぞれの限度値になってから尚も、UP スイッチ、または DOWN スイッチを押しても、それぞれの限度値を超えることはない。 (デフォルト値 = 0)

      * 本機で使用したIC MAX7219 では 16 段階で変更することが可能であるが、LEDの安全性を考慮して上限を 9 までに制限をした。

  • ただし、このように輝度調節ができるのは6個の 8 x 8 ドットマトリクス LED だけで、時と分、分と秒、それぞれの間のコロン(:)、またはピリオド(.)用の4つの LED については対象外で、輝度調節をすることはできない。

| ページトップ |

■ プログラム ■


● MAX7219 使用 8 x 8 ドットマトリクス LED モジュールのプログラミング

 ◎ 事前に必要な予備知識

 このモジュールのプログラミングを行うには、使用したドットマトリクス LED モジュールについて の項で述べた、8 x 8 ドットマトリクス LED の各ドット LED の位置と、ドライバ IC MAX7219 の各出力の Digit 番号、Segment 番号との対応を、まず、理解をする必要があります。 本機で使用したモジュールのその対応図を次に再掲しておきます。
    (4) ディスプレイモジュールのピンソケット番号
    (正しい)
    (5) マトリクス LED の点灯時のピン番号
     ( 1088 AS と印字面を上にしたとき )
 ところが、実際にプログラムを作成する段階で分かったのですが、Maxim Integrated 社の MAX7219 データシート(日本語版) を見ると、次の図表がありました。
 私が作成をした上の (5) 図とデータシートのセグメントの並びとを比べてみると、各ビットが左右まったく逆になっているのが分かります。 したがって、モジュールごと上下を逆にしてしまえばセグメントの並びは一致をしますが、 今度は (5) 図のデジット(桁)の並びが上下逆になってしまいます。 また、この時点ではハードウエアとしての プリント基板(1) が既に完成をしていたこともあって、(5) 図のままでプログラムの作成を進めることにしました。

 また、文字フォントデータを作成する観点からも、データシートのようにセグメントの各ビットの並びを左右逆にして作成するのは少なからず大変なので、次の例のようにフォントデータを作成しておいて、使用時にプログラムで左右を逆変換することにしました。 例えば、"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 に送られます。
 PIC からは DIN に次の表1のフォーマットで、常に 16 ビットのシリアルデータとして MAX7219 に送られます。 そして、それらのビットを送る前には、まず LOAD を "Low" にします。 DIN に送られてきた各ビットは CLK の立上りエッジごとに MAX7219 のシフトレジスタにシフトインされ、最後(16 番目 = D0)のビットを送出後に LOAD を "High" にすることで(その立上りエッジで)、桁レジスタまたは制御レジスタのいずれかに取り込まれます。

 なお、桁レジスタまたは制御レジスタの種類は表1の ADDRESS (D11 〜 D8) で指定をし、DATA (D7 〜 D0) にはそれらのレジスタに設定するデータを指定します。 XXXX (D15 〜 D12) はダミービットのため 1 でも 0 でも構いません。
 また MAX7219 の桁レジスタまたは制御レジスタには次の表2に示す種類がありますが、桁レジスタと No-Op レジスタを除いた制御レジスタについては、MAX7219 の初期設定時に一度設定をすれば、 後は設定をし直す必要はほとんどないと思います。

 私の使用例では、輝度レジスタ(Intensity 0xXA)を初期設定時以外に、デモプログラムのときに変更をしたぐらいです。 また、この輝度レジスタ(Intensity 0xXA)は LED の輝度を変更させるものですが、 モジュールに使用のマトリクス LED が高輝度 LED らしく、通常は 最小値の 0 を設定 しておけば十分(以上)に明るく点灯をしています。
 以下の表は、いずれも各制御レジスタに設定をするデータについての細目を定めたもので、制御レジスタにデータを設定するときに必要に応じて参照をします。
 さて、ここで私が本機においてプログラミングをした、MAX7219 の基本制御ルーチンを示しておきます。
 なお、リストはプログラム 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
      
 上で PIC と MAX7219(モジュール)との 接続ブロック図 に示したように、本機では6個の MAX7219 をカスケード接続をしていますが、これらの個々の MAX7219 にデータを送り込むのには、 如何にすれば良いのでしょうか。 その方法について次に説明をします。 例えば次の図のように、個々の MAX7219 に赤字で示したデータを送るような場合にはどうすれば良いか。


 先にプログラムを示すと次のようになります。

 初めに 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'
      
 そして、この文字フォントデータテーブルから該当の文字フォントを読み出すのには、次の サブルーチン( font_read_buff ) のように行っています。 まず、テーブルからどのフォントデータを読み出すかの 指定をしてやらなければなりません。 その指定を W レジスタ で行い、具体的には "0" のフォントデータであれば 0 を、 "1" のフォントデータであれば 1 を、W レジスタで指定をします。

 本機では、これらのフォントデータの読み出し方法を、本機の前身である "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桁表示のデジタル時計のため、ドットマトリクス LED を6個装備しています。 したがって、文字フォントデータの読み出しやマトリクス LED への表示も、6桁分をまとめて取り扱った方が 何かと効率が良くなります。 次の サブルーチン( font_read6_buff ) は、文字フォントデータテーブルから6桁分を読み出してバッファエリアへ格納をするためのものです。

 フォントデータテーブルから読み出す6桁分の文字指定には、temp_hhtemp_mmtemp_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
      
 次の サブルーチン( dxled_display ) は、前のサブルーチン( font_read6_buff )で、バッファエリア(48 バイト)に読み出した6桁分の文字フォントデータを、次には MAX7219 に送ってマトリクス LED に表示をさせるのを1番目の目的としています。 また、本機ではデジタル時計のために、"時" と "分"、"分" と "秒" のそれぞれの間に区切り記号としてのコロン(またはピリオド)用の LED も装備をしていますが、 それらの LED の制御も2番目の目的としています。

 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_flgmod_flgint_flgexp_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 に変更 )使用をしました。
      ( 2021/5/16 追記 )
      ところが、このように一旦は Fast mode に対応できるように変更をしたのですが、機能追加のために Standard mode でないと動作ができない、I/O エキスパンダ IC の PCF8574/74A を使用することになったために、 "I2C サブルーチン( ファイル: I2C_XS.sub )" も Standard mode で使用をすることになりました。

 次に、書き換え変更後の "I2C サブルーチン( ファイル: I2C_XS.sub )" の内容を次に示しておきます(緑色のリスト)。 このサブルーチンの使用方法としては、
      #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 ======
      
 なお、本機で採用をした PIC16F690 にはハードウエアとしての I2C 機能を備えているので、当初はハードウエア版の I2C サブルーチンを作成するつもりでいたのですが、本機全体としてのプログラミングの進捗が、 つまらないところで何度も躓いたりしてかなり遅れてしまったため、安易にソフトウエア I2C 版で間に合わせてしまいました。 したがって、将来的にはハードウエア版に置き換えるかもしれません。

 | プログラムのトップに戻る |

● デジタル温度湿度センサーモジュールのプログラミング

 デジタル温度湿度センサーモジュールについても、前作 "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
      
 次に示す図は NXP Semiconductors 社のデータシート(PCF8574; PCF8574A)から抜粋をしたもので、これらの図を参考にして上のサブルーチンを作成しました。  本機における I/O エキスパンダ IC の PCF8574/74A を使用した 機能追加の回路図 は上の回路図の項に示してありますが、そこでの I/O ポート(全 8 ポート)の 使用用途をまとめると次の表のようになります。 P0 〜 P3 を入力、P4 〜 P6 を出力として使用をし、P7 は空きで使用していません。
 このように割り当てた PCF8574/74A の入出力ポートを、I2C インタフェースを使用して具体的にはどのように制御をしているのかを、次から説明をします。 ハードウエアとしての2本の I2C インタフェース線は、 本機では「リアルタイムクロックモジュール( DS3231 )」で使用をしていて、既に存在をしているのでそれに接続をします。

 まず初めに、本機の起動直後に 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 ワードとを、常に意識をしながらそれに対応させたプログラムを作成しなければなりません。
     * 機能追加を行う前(Ver.1.01)のプログラムメモリの使用マップとサイズ * (参考: Matrix88LED_ClockII_101.lst から抜粋)
      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
      
     * 機能追加を行った後(Ver.1.10)のプログラムメモリの使用マップとサイズ * (参考: Matrix88LED_ClockII_110.lst から抜粋)
      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 制御(設定)する
    (1) ダイレクト・コントロール (直接制御) ..... "時間には関係なく、直ちに何番のスイッチをどのようにするか" を設定するもの
    (2) インターバル・タイマーの設定 (間隔プログラム) ..... "何番のスイッチを現在の時刻から何時間何分後にどのようにするか" を設定するもの
    (3) タイマーの設定 (定時プログラム) ..... "何番のスイッチを何曜日の何時何分にどのようにするか" を設定するもの
の3つの方法を実現させています。 本機においても同様にしたかったのですが、やはり、入力するためのスイッチの数が足らない、ということもあってスマートな入力方法が見つからないため、 現状では一番の目的であった、 (3) タイマーの設定(定時プログラム) だけの実現にとどまっています。

 | プログラムのトップに戻る |

 ◎ 機能追加で発生した不具合

 今回の機能追加のプログラム作成の中で、メモリ設定機能の中のほんの一部のところで不具合が発生し、かなり多くの時間ロスがありました。 何と 10 日ほど格闘をし続けたのですが残念ながら今なお解決をしておりません。 今は代替の方法でとりあえず不具合を回避していますが、この代替の方法では私の本意ではなく不満やるせない気持ちです。

 具体的に述べると、タイマースイッチや温度スイッチを機能させるためには、事前にそれらの情報を入力設定しておかなければなりません。 本機では メモリ設定機能 を使用してそれぞれ4つの項目を入力することになるのですが、 どのような形式で入力するのかを伝えるために、マトリクス LED には "xx:xx xx" と表示がされます。

 しかし、そのままではどの桁位置の項目に入力をすればよいのかが分かりません。 そこで次のように、 "~~" と記した桁位置のところの 数字を点滅表示 をさせて、ここがこれから入力をする項目なのだとユーザに知らせます。 そして、4つの項目ごとに点滅表示をする桁位置が移動をします。
        	[ xx:xx xx ]	 →	[ xx:xx xx ]	 →	[ xx:xx xx ]	 →	[ xx:xx xx ]
        	  ~~			     ~~  	  	        ~		         ~
        
 この点滅表示をさせるためのタイミング情報を、タイマー0割り込み を使用して作り出す(筈な)のですが、これがうまく行かないのです。 本機では他の割り込み機能として、 「リアルタイムクロックモジュール( DS3231 )」からの割り込みを 外部(RA2/INT)割り込み で、また、時報音やアラーム音など各種のブザー音の発生に タイマー1割り込み を使用していますが、 タイマー0割り込み を許可すると、それら他の割り込み処理までが機能をしなくなってしまうのです。

 今、私が分かっていることは、タイマー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つの割り込み要因と、 それらの要因に対する割り込みの許可信号との関係を表しています。
 また、同データシート内の INTCON レジスタの項を改めて見てみると、次のように Note: が書かれています。 英語表記のデータシートのためにいつもあまり注意をしないでスルーをしていたのですが、 ここには大変重要なことが書かれていました。
 英語はあまり得意ではないので、"Google 翻訳" の力を借りて日本語に翻訳をしてみると、次のように書かれています。
 右側の日本語文の中で赤色の線を引いた部分が重要で、「割り込みフラグは、イネーブルビットに関係なく設定される。」 当たり前といえば当たり前なんですが、この一文で改めて気づかされ再認識をしました。

 上述の ところが、・・・ ブザー音が伴うと動作をしてしまう ・・・ や、タイマー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割込みの処理が優先して行われてしまう ― ことになります。

 ここで、割り込みが発生したときのその処理の流れを、初めから おさらい をしてみると次のようになります。
  1. 例えば、本機では「リアルタイムクロックモジュール( DS3231 )」からの割り込みを、外部(RA2/INT)割り込み で処理をするために、その割込みフラグ ( INTF ) に対応したイネーブルビット ( INTE ) と グローバルイネーブルビット ( GIE ) を予めセット ( ON ) しておきます。
  2. そして、PIC16F690 の外部(RA2/INT)割り込みピン (17) に、DS3231 からの割り込み信号 ( SQW ) が入力されると、PIC では次の動作を行います。

  3. > 外部(RA2/INT)割り込みフラグ ( INTF ) をセット ( ON ) して割り込みが発生する。 ( 上図 で INTF, INTE, GIE の3つの信号が ON )
  4. > すると PIC では以後の他の割込みの発生を禁止するために、グローバルイネーブルビット ( GIE ) をクリア ( OFF ) する。
  5. > 次に、実行中の命令の次の命令アドレス ( 13 ビット) を、スタックに保存 ( PUSH ) する。
  6. > そしてプログラムの流れは、強制的にプログラムカウンタ ( PC ) にハードウェアで h'0004' がセットされるので、実行中の命令の動作が終わり次第4番地にジャンプする。 (それまでに実行して来たプログラムは中断される。)

  7. 本機のプログラムでは、この後再び、4番地からアドレス interrupt 番地にジャンプして、"割り込み処理ルーチン" を実行します。
  8. "割り込み処理ルーチン" 内では、"外部(RA2/INT)割り込み処理" を実行(この処理プログラム内で割込みフラグ ( INTF ) をクリア ( OFF ) しておく)した後、"割り込み処理ルーチン" の最後の命令 RETFIE を実行して、 割り込みから抜け出すことになるのですが、その RETFIE 命令を実行したときの PIC の行う動作 は次のようになります。

  9. > 先の割り込み時に 5. でスタックに保存しておいたアドレス ( 13 ビット) を、プログラムカウンタ ( PC ) に復帰 ( POP ) する。
  10. > と同時に、次の割込みを許可するために、グローバルイネーブルビット ( GIE ) を再びセット ( ON ) する。

  11. そして、プログラムは割り込みによって中断されていた(プログラムカウンタ ( PC ) が示す)命令から再開され、通常の処理を続けることになります。 他の割り込み要因が発生したときにも、流れは同様です。
 したがって、上述した 今、私が分かっていることは、・・・ 以降5、6行で述べている疑問には、上の おさらい4. で述べた PIC が GIE = 0 にしていることが、1つの解答になるのかもしれません。 しかし、割り込みから抜け出るときには 同 10. で述べているように、RETFIE 命令が再び GIE = 1 にしていることから、GIE = 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 コントローラー" のプログラムを作っているときに気が付きました。 答えは、既に上述した おさらい の中にあったのです。
    7. 本機のプログラムでは、この後再び、4番地からアドレス interrupt 番地にジャンプして、"割り込み処理ルーチン" を実行します。
 この "4番地からアドレス interrupt 番地にジャンプ" が犯人でした。 いや、ちゃんとした手続きさえ踏んでいれば犯人にはならなかったのですが、いや、いや、"割り込み処理ルーチン" 内では踏んでいるつもりでいたのですが、 (この場合には)その時点では既に遅かったのです。
      		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 番地にジャンプすることはないのですが、割り込みが起こったことによって、上述の おさらい
    4. > すると PIC では以後の他の割込みの発生を禁止するために、グローバルイネーブルビット ( GIE ) をクリア ( OFF ) する。
    5. > 次に、実行中の命令の次の命令アドレス ( 13 ビット) を、スタックに保存 ( PUSH ) する。
は実行されます。 しかし、上述のように interrupt 番地にある "割り込み処理ルーチン" が実行されないために、末尾の RETFIE 命令も実行されることはなく、したがって
    10. > と同時に、次の割込みを許可するために、グローバルイネーブルビット ( GIE ) を再びセット ( ON ) する。
ことはなく、グローバルイネーブルビット ( GIE ) は、4. でクリア ( OFF ) されたままの状態になります。

 そして、暴走しているプログラムは、やがて、どこかのサブルーチン内に飛び込んで、そのサブルーチンの末尾の 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 更新 )

| ページトップ |

■ プリント基板の加工と部品配置 ■

 使用したプリント基板(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)パターン図 (部品面) ■

 このディスプレイ基板は、保守性を容易にするために直接基板にハンダ付けするのを避けて、ドットマトリクス・ディスプレイモジュールを、コネクタを使用して基板に搭載をしています。 初め、通常のピンヘッダ(オス)とピンソケット(メス)で構成をさせるつもりでいたのですが、この場合にはそれぞれピンの長さが長いので、プリント基板(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に示すように、 黒色の樹脂部分がピンの中央位置になるように、あらかじめ加工をしておきます。 そして、このコロン用小基板については、直接母基板にハンダ付けするのが前提です。


下写真のように赤色 LED を使用していますが、マトリクス LED がオレンジ色のため、色を合わせた方が良いと思います。
また、この写真には写っていませんが、LED には光拡散キャップ Φ3 白(OS-CAP-3MK-1)を被せて使用しました。


組み立て前の片面小基板 2 枚で1組分


組み立て完成


一緒に写っているナットは、被写体を
上向かせるために使用をしたもので、
この小基板の作製には必要はない
表面側 裏面側 写真1 写真2 写真3

 小基板の組み立て方法は、まず、2 枚の片面小基板それぞの (部品面) が内側に向かい合うように重ね合わせます。 そして、裏面側となる上下位置にそれぞれ 2P ピンヘッダを取り付け(仮ハンダし)ます。 次に、Φ0.4 程度の裸線(抵抗等の足の線くずで可)で、左図の青色の線で示したようにジャンパー配線を、表面側、裏面側ともにします(このとき上下のピンヘッダも本ハンダ)。

 最後に表面側となる面に 2 個の LED を取り付けるのですが、各 LED の足には、2P ピンヘッダから各ピンを抜き取った黒色の樹脂部分だけを、スペーサとして取り付けてからハンダ付けをします。 このとき、各 LED の足には高さ調節のために 2 個ずつが必要です。 なお、上下のピンヘッダにも 1 個ずつを追加使用しました。

| ページトップ |

■ プリント基板(1)パターン図 (ハンダ面) ■


| プリント基板(1)パターン図 (ハンダ面) (Matrix88LED_ClockII_1PC1.CE3) | ページトップ |

■ プリント基板(2)パターン図 (部品面) ■ (参考: 改造前のもの)

 このメイン基板についても、「リアルタイムクロックモジュール(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本の組み合わせにしました。

タクトスイッチ用基板を取り付け後、真上から見たところ タクトスイッチ用基板を取り付け後、斜め上から見たところ
    * 30 mm 長のものが適長 : 母基板に取り付けてある「リアルタイムクロックモジュール」との適度な空間を保つことと、最終的には、タクトスイッチのトップ位置がケースの上箱表面よりも数 mm 飛び出させるように調節をするため。 また、各タクトスイッチにはキートップ長が通常のものより長い 10 mm ほどのものを使用し、タクトスイッチ用基板に取り付けるときにも、高さ調節のために、 スイッチを基板に押し付けてスイッチの底が基板に付くように取り付けます。 このようにして取り付けたスペーサやタクトスイッチの状態で、本機の場合にはケースの上箱表面よりも 2 mm ほどが飛び出し、スイッチの操作性も丁度良い感じです。

| ページトップ |

■ プリント基板(2)パターン図 (ハンダ面) ■ (参考: 改造前のもの)


| プリント基板(2)パターン図 (ハンダ面) (Matrix88LED_ClockII_2PC1.CE3) | ページトップ |

■ プリント基板(2)パターン図 (部品面) ... (改造後) ■ ( 2021/5/16 追加 )

 タイマースイッチ機能等の追加のための改造後のもので、右上に位置する L型の 4P のピンヘッダを追加しました。 ストレート型のピンヘッダは、相棒のコネクタを取り付けたときに、 デジタル温度湿度センサーモジュール(DHT22_mod)とぶつかってしまうので、使用ができません。

 また、タクトスイッチ用小基板の5個のタクトスイッチの並びについてですが、改造前のような並びだとスイッチの操作をしていて、表示されているドットマトリクス LED の並びと感覚的に違和感を感じるので、下図や写真のように、 改造前のものとは左右の位置がまるっきり逆になるように改め修正をしました。 コネクタケーブルの配線替えと文字入れの変更が必要になります。

 3つ目の変更点は、今回の機能追加とは関係はないのですが、回路図 の項の 追加説明 で述べたように、ハードウエアのちょっとした設計ミス(間違いではないのですが)を修正しました。 プリント基板(2) の左端にあるピンヘッダの、LU, LL, RU, RL 4つのピンと PIC との配線替えを行いました。 最新のプログラム のためには次図のような改造が必要です。


| プリント基板(2)パターン図 (部品面) (Matrix88LED_ClockII_2PCb.CE3) | ページトップ |

■ プリント基板(2)パターン図 (ハンダ面) ... (改造後) ■ ( 2021/5/16 追加 )


| プリント基板(2)パターン図 (ハンダ面) (Matrix88LED_ClockII_2PC1b.CE3) | ページトップ |

■ プリント基板(3)パターン図 (部品面) ■ ( 2021/5/16 追加 )

 タイマースイッチ機能等の追加をするために、新たに追加作製をしたプリント基板で、 I/O エキスパンダ IC の PCF8574/74A を使用して新たな I/O ポートを 8 つ確保(実際に使用したポートは 7 つ)。 基板の右上に位置する4本の端子(GND、VCC、SDA、SCL)を、メイン基板である プリント基板(2) ... (改造後) とコネクタ、ケーブルで接続をします。

 また、この追加機能のために新たなタクトスイッチ用の小基板の追加も必要で、右側に図と写真で示しておきます。 なお、この小基板はプリント基板(2)パターン図で示した小基板とは異なって、 ケースの上箱にスペーサを介して直接取り付けました。


| プリント基板(3)パターン図 (部品面) (Matrix88LED_ClockII_3PC.CE3) | ページトップ |

■ プリント基板(3)パターン図 (ハンダ面) ■ ( 2021/5/16 追加 )


| プリント基板(3)パターン図 (ハンダ面) (Matrix88LED_ClockII_3PC1.CE3) | ページトップ |

■ ケース加工図 ■

 使用したケースは、100均(セリア)で購入をした "No.1442 クリアシンプルケース ペントレー (山田化学)" という、ポリスチレンケースです。 この箱状のものを2個使用して、 お互いの内側を向かい合わせ、貝合わせのような形にして使用をしました。

 そして、2個の箱を結合するのには、次のような 2 mm 厚のアクリル板を、箱の内側の左右側面に縦長に取り付けます。 まず、奥側の箱(下箱)にこのアクリル板を M3 のビスとナットで固定をし、 次に、手前側の箱(上箱)をアクリル板に切ったタップに M3 のビスで固定をします。 したがって、通常時の上箱の取り付け/取り外しには、上箱側のビスで行うようにします。
 (下箱正面図) の上下位置にたくさんのΦ3.5 の丸穴が開けてあるのは、このケースの使用時には蓋 (上箱正面図) をするために箱内が密閉状態になるのを防いで、箱外の空気が自由に流出入できるように開けました。 本機では温度湿度センサーモジュールを使用しているため、その機能が妨げられないようにするのが目的です。

 なお、図中で 赤色 で示した部分は、タイマースイッチ機能等の追加のために必要となった、追加加工の部分を表しています。 ( 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
Rasbee MAX7219 8X8 ドット マトリックス 部品 ディスプレイ ドット マトリックス ディスプレイ モジュール Arduinoと互換 MCU制御駆動 マトリックス
Rasbee 5個 DS3231 時計モジュール 国内配送 AT24C32 高精度 リアル時間時計モジュール IIC RTCモジュール リアルタイムクロック DC 3.3-5.5V DS1307 ...
Rasbee DHT22 デジタル温度センサーモジュール AM2302 接続線付き Arduino DIY用 [並行輸入品]
5pcs / lot PCF8574P PCF8574AP DIP16 PCF8574 DIP新規およびオリジナルIC在庫あり

| ページトップ |

■ Microsoft 社の Internet Explorer と Edge ■ ( 2021/5/16 追加 )

 これから述べることは、このページ "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

 また、このページの回路図の項の最終行で
      | 回路図 (Matrix88LED_ClockII.CE3) | 回路図 (ExpanderPCF8574A.CE3) | ページトップ |
というところがありますが、実は皆さんが今ご覧になっているホームページの "公開版" では、リンクをコメントにして外してありますが、これらの回路図に変更が生じたような場合に、 私専用の "開発版" ではリンクを有効にしてあるのでそれをクリックすれば、Internet Explorer では直ちにアプリ "回路図エディタ bsch3v.exe" が起動して、これらの回路図ファイルの編集ができるようになっています。

 しかし、これもデフォルトの Edge では "回路図エディタ bsch3v.exe" が起動されることはなく、ファイルの中身がテキストデータになっているためか、私にとってはまったく意味のないデータが Edge の画面に表示されるばかりで、 回路図ファイルの編集を行うことができません。 これも何かの設定次第で "回路図エディタ bsch3v.exe" を起動させることができるようになるのかもしれませんが、まだ Edge に不慣れな私にはその方法が分かりません。*2

 このように、Internet Explorer とは異なる点が多い Edge では、いくら Microsoft 社のお勧めでも私にとっては使い辛いばかりで、正直、その気にはなれないでいたのです。

 しかし、Microsoft 社の強引な Edge の売り込み、押し付けのために、可哀そうな Internet Explorer はどうやら見捨てられてしまったようで、上述したように、一部の機能を取り上げられたり、時々無反応状態にさせられる Internet Explorer を、 これまでのように今後も使い続けることには私もいよいよ限界となりました。

 そして、今回の機能追加のためのホームページの更新作業から、長年連れ添ってきた Internet Explorer とも泣く泣くお別れをして、仕方なく使い辛い Edge と手を組むことになりました。 Edge の数々の妨害行為に屈した形にはなってしまいましたが、不本意ながらこれもやむを得ません。

 したがって、今後のブラウザ表示は Edge で表示されるパソコン画面を基準としたホームページの作成に、徐々に切り替えて行かなければなりません。 どのブラウザに対してもほぼ同様な表示をさせるような作成の方法もある ― とは思うのですが、 プロではない今の私には難しい課題です。

*1, *2 その後、次のことが分かりました。 ツールバーの … をクリックして、設定 → 外観 → ツールバーのカスタマイズで、「Internet Explorer モード ボタン」を ON に設定すると、ツールバーにボタンが表示されるようになるので、 そのボタンをクリックすることで Edge から Internet Explorer の表示画面に切り替わり、その画面からであれば、これまでの Internet Explorer と同様に *1, *2 の操作もできるようになりました。 ( 2021/12/7 追記 )

| ページトップ | ホーム |


Copyright (C) 2020-2023 やまもとみのる
初版:2020年11月18日、初公開:2021年3月15日、最終更新:2023年11月11日