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

195. ドットマトリクス LED コントローラー

[ 初公開日:2022年4月25日 ]

 先に公開済みの別ページ "189. ドットマトリクス 8 x 8 LED 表示時計 II ( MAX7219 版)" では、時計専用機としてのドットマトリクス LED を使用した製作例を紹介しましたが、遊び心が高じまして、 もう少しドットマトリクス LED とそのドライバ IC MAX7219 とで遊んでみたい、と思う気持ちが強くて本機の製作に至りました。

 本機では、従来の時計の表示機能、年月日(曜日)の表示機能、温度、湿度の表示機能等はもちろん持たせてありますが、他に、電光(電子)掲示板のように、文字が1ドットずつ横方向にスクロールをしながら表示をする機能、 電子オルゴールのメロディーに合わせながら、その曲の歌詞を1文字ずつ表示をする機能、などを実現させています。

■ 回路図 ■


 上回路図中の各 MAX7219 の電源には、それぞれ 10μF の電解コンデンサの記入がありますが、4連ドットマトリクス 8 x 8 LED モジュール の項で述べているように、本機では、この電解コンデンサの追加は無しで製作をしました。

| 回路図 (MatrixLED_Controller.CE3) | ページトップ |

■ 本機で使用した各種モジュールについて ■

 以下に挙げる各種モジュールは、1年以上も前にアマゾンで購入をしてあったもので、私にとって、どれも使用することは今回が初めてなので、本機の製作を通してどのようなものなのかを、私が感じた問題点やその他のことなどを交えて、次に順に述べていきます。 (なお、これらのプログラミングに関しては、プログラム の項を参照のこと。)
● 4連ドットマトリクス 8 x 8 LED モジュール

 このドットマトリクスモジュールは、"189. ドットマトリクス 8 x 8 LED 表示時計 II ( MAX7219 版)" の 使用したドットマトリクス LED モジュールについて で述べた、ドライバ IC MAX7219(SOP パッケージ版)を使用したドットマトリクス 8 x 8 LED モジュールが、1枚のプリント基板上に4個が連なった次の写真のような外観になっています。

 2020 年 11 月頃にアマゾン出店の中華ネットショップで2個購入をしておいたもので、その頃は今と違って価格は安く、しかも配送料も無料でした。 たかだか1年と数か月前のことですが、その頃は一般的にすべてのパーツ類が今と比べて安価で、 しかも配送料無料というショップが多く存在していました。 しかし、その年が明けた頃からパーツ類の価格は高くなり、しかも配送料が有料となったショップがやたらと多くなったように思います。  このドットマトリクスモジュールも、前回述べたようにテクニカルな説明書というものは一切添付されてきませんが、前回使用した単連のモジュールと扱い方は同じですから、前回私が調べたドットマトリクス 8 x 8 LED モジュールの 記述 を参考にしてください。

 また、そちらの記述でも述べましたが、この4連のモジュールでも同様にいえることで、ドライバ IC MAX7219 に SOP パッケージを使用したモジュールには、その電源に電解コンデンサが取り付けられていない、という不備な点があります。

 MAX7219 データシート(日本語版) の 10 ページには、"桁ドライバのピーク電流に起因する電源リップルを最小限に抑えるために、できる限りデバイスに近い位置で、V+ と GND の間に 10μF の電解コンデンサと 0.1μF のセラミックコンデンサを接続してください。" と書かれています。 したがって、このデータシートを忠実に守るためには、4 x 2 = 8 個の MAX7219 に対して、それぞれ 10μF の電解コンデンサを、自前で追加して取り付ける必要があります。

 しかし、上述のアマゾンの中華街を覗いて見ると、今でも現状のままで多くのショップから販売がされていますし、また、ネットを検索してみても、この件に関しての話題はなさそうなので、恐らく問題はないのでしょう。 本機でも現状のままで、電解コンデンサの追加は無しで製作を進めることにしました。

 本機では、この4連ドットマトリクス 8 x 8 LED モジュールを、2個横に直列に並べて使用をしますが、そのときに右側に位置するモジュールの裏側から見て右端に、上写真のようにL型ピンヘッダが取り付けられていますが、 これが製作時には邪魔をするので除去をする必要があります。 そして、左右のモジュール間の配線には、直接ジャンパー線をハンダ付けしました。 また、左側に位置するモジュールについては、メインの プリント基板 と コネクタ接続をするために必要で、L型ピンヘッダは残しておきます。

● リアルタイムクロック( DS1307 )モジュール

 既に公開済みの別ページ "189. ドットマトリクス 8 x 8 LED 表示時計 II ( MAX7219 版)"、"190. I2C IF モジュール + LCD(1602A) 表示時計"、 "193. 7セグメントLED表示時計 II ( MAX7219 版)" では、リアルタイムクロックモジュールに、DS3231 を使用したモジュールを採用してきました。

 しかし、この DS3231 を使用したモジュールの手持ちが少なくなってきたことと、本機では実用機というよりも "お遊び" 的な要素が強いため、DS3231 版と比べると精度が少し低い、と言われている DS1307 版のモジュールを使用することにしました。

 実は、DS1307 版モジュールを DS3231 版よりも先に購入をしてあったのですが、後になって、DS1307 版よりも精度が高い DS3231 版の存在を知ることとなって再購入をし、その精度が高い DS3231 版をずっと使用してきたために、 今まで、この DS1307 版はその出番を失っていたのです。 このモジュールも 2020 年 9 月頃にアマゾンで購入をしたもので、その頃は何と、100 円を大きく下回る2桁の値段でした。

 その外観は次の写真に示すようなもので、表面側(写真左)には RTC IC の DS1307(U2)の他に、EEPROM の AT24C32(U3)が搭載されており、裏面側(写真右)には DS1307 の電源バックアップ用の、 コイン型バッテリーを使用するための電池ホルダーが搭載されています。  次の図は、この DS1307 版のリアルタイムクロックモジュールの回路図で、あるネットショップの商品説明の一部に使われていた回路図を、私自身が見て分かり易くなるように書き直したものです。 なお、----- で囲んだ U1 温度センサIC DS18B20 と、 R7 抵抗 680KΩ はオプションで、このモジュールには実装されていません。

 また、C1 と C2 の2個のコンデンサに、ネットショップの同回路図には共に 100 pF と記入がありましたが、パスコンにそんな小さな値のコンデンサを使用するのはおかしいので、下回路図のように 0.1μF の間違いではないかと思います。 しかし、チップコンデンサには表示がないので実際のところは分かりません。


赤色←の位置

ピンヘッダを取り付け

 上の回路図からも分かるように、バックアップ用のバッテリーには、充電回路(ダイオード 1N4148 と抵抗 200Ω)が付属されていて、充電池(二次電池) LIR2032 を使用することを想定しているため、そのままでは一次電池の CR2032 を使用することはできません。 したがって CR2032 を使用する場合には、ダイオード 1N4148 か抵抗 200Ωのどちらかを取り除いて、充電電流が流れないようにする必要があります。 充電池の LIR2032 は高価なので、私は右上写真のように、R5 抵抗 200Ωを取り外して一次電池の CR2032 を使用することにしました。

 ちなみに、このモジュールは初めての購入ということとすごく安価だったということから、試し買いで5個を購入してありました。 そして、この R5 抵抗についてですが、5個の内、回路図通りの 200Ω(201)が取り付けられていたモジュールは1個だけで、 残りの4個はすべて 1KΩ(102)が取り付けられていました。 チップ抵抗の表記が 201 と 102、天地を逆にして見れば同じように見えますが、DS3231 版モジュールの 回路図 でも 200Ωとなっているので、 1KΩ(102)は間違いだと思われます。

 また、このモジュールを本機で使用するに当たっては、右上写真のような位置に、ストレート型の 7 ピンヘッダを取り付けて使用をしました。 このモジュールにはピンヘッダは付属していないので、自前で用意をする必要があります。 プリント基板 側に取り付けたピンソケット(メス)で受けて接続をします。

● デジタル温度湿度センサー( DHT11 )モジュール

 温度湿度センサーモジュールについても、前項のリアルタイムクロックモジュールとまったく同理由で、本機では DHT22 でなく DHT11 を使用しました。 DHT11 は DHT22 に比べると精度が少し低いのですが、 先に DHT11 を購入してあった後に DHT22 の存在を知って再び購入をしたため、精度が低い DHT11 は、今まで出番を失っていました。

 今再びアマゾンを覗いて見ると、"190. I2C IF モジュール + LCD(1602A) 表示時計" 等で使用した、DHT22 版の デジタル温度湿度センサーモジュール のように、 DHT11 版についても、黒色のプリント基板上に載せたものが主流のようですが、私が購入をしたものは、その外観は次の写真に示すようなものです。  黒色のプリント基板上に載せたものと、上の写真のようにモジュール本体だけのものとの違いは、前者ではモジュール本体にプルアップ用抵抗とデカップリングコンデンサ(パスコン)を追加したことと、 モジュール本体の NC 端子を省いて外部への引き出し端子を3本とした違いだけです。(DHT22 版の デジタル温度湿度センサーモジュール の 回路図 を参照。) したがって、本機の 回路図、および プリント基板パターン図 (部品面) に示すように、プルアップ用抵抗 5.1KΩ とパスコン 0.1μF を追加して使用しました。

| ページトップ |

■ 本機外観の様子 ■

 本機では、ケース加工図 の項で述べているように、本機に見合ったような既製のケースが見つからなかったため、下写真のように L 型アルミアングルをベースとしたシャーシに、各パーツ類を取り付けて全体を構成させました。

本機を正面の斜め上から見たところ 本機を裏面の斜め上から見たところ
本機を上面の真上から見たところ 本機を裏面の真上から見たところ
本機を背面の斜め上から見たところ 左側面の斜め上から見たところ 右側面の斜め上から見たところ

| ページトップ |

■ 機能概要と使用法 ■

 ここで述べる "機能概要と使用法" は、プログラム の各項で述べているそれぞれの "機能の概要" を主軸として、それらに写真等を交えて再編集を行ったものです。

 ドットマトリクス LED の発光色については、部屋を暗くしてデジカメで撮ったのですがご覧のように、LED そのものは白っぽくその周りが赤色で滲んだように写ってしまい、実際に目で見る様子とはまったく異なっています。 いつもどのようにして撮ればいいのか悩みますが、実際の発光色は、この ページトップ に掲載をした写真に近く、もっと濃いオレンジ色で周りの滲んだ赤色はなく、すっきりと見えています。

  • 本機の電源を投入すると、"ピポッ" と開始ブザー音を出力後、ドットマトリクス LED のデモ表示が約 18 秒間行われる。 続いて 1 秒間の空白をおいた後、0.5 秒間ずつ9から0までの数字をカウントダウンしながら、 ドットマトリクス LED 全8桁分の表示テストを計5秒間行う。

  • 次に 1 秒間の空白をおいた後、2つに分けたプログラム名が 計約 12 秒間表示され、続いてプログラムのバージョン番号が 約3秒間表示される。
          プログラム名-1:       [ * Matrix LED * ]
          

          プログラム名-2:       [ * Controller * ]
          

          バージョン番号:       [ Ver x.xx  by M.Y ]
          

  • 次の RTC モジュールの状態チェックが行われるまでに、上述のように、通常はこの間、40 秒近くを要するため、5個のタクトスイッチの内、UP スイッチと DOWN スイッチを、同時に押しながら 電源スイッチをオンにした場合には、 上記のマトリクス LED 表示が一切省略されて、直ちに、次の状態チェックに移行するように機能する。

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

  • ユーザはエラーメッセージの確認をした後、SIGNAL スイッチを除いた4つのスイッチの内のどれかを押して応答をする。

  • すると、リアルタイムクロック( RTC )モジュールが初期設定され、エラーフラグがクリアされるとともに、年月日、時分秒が本機をプログラミングしたときの初期値、 "2022/01/01"、"00:00:00" に設定がされて時計機能が開始する。 そして、プログラムは次のメインルーチンに移行をして、ユーザのスイッチ指示を待つ。

◎ メイン、プログラム処理の振り分け
  • メインルーチンでは、12 種類のプログラム(機能)から1つを選択し、それら個々のプログラムに振り分ける処理(ジャンプ)を行う。 ロータリスイッチ で実行したい プログラムを下表から選択した後SET スイッチ を押すことによって 選択したプログラムにジャンプ を行う。

  • メインルーチンを実行中は、ユーザに実行させたいプログラムを選択するように促すため、スイッチ選択メッセージが表示 され、なおかつ、同メッセージの右端に位置する "99" の部分に、現在選択されている スイッチ選択番号がブリンク表示 される。
          スイッチ選択メッセージ:     [ Select.SW ?_ 99 ]
          

  • このブリンク表示はユーザの注意を引くためのもので、当メインルーチンのプログラムでは、タイマー0割り込みを使用してブリンクのタイミングを取っている。

  • ロータリスイッチのスイッチ選択番号をどのように取得しているかは、まず、回路図 の左上を参照のこと。 ロータリスイッチの 12 の各接点にダイオードマトリクスが組み込まれていることに、既に気付かれていると思うが、 この情報を PIC のポート C(RC4 〜 RC7)で監視し、1 〜 12 までのバイナリ信号として取り込んでいる。

  • そして、この後 SET スイッチが押されると、ロータリスイッチの スイッチ選択番号を基にして ジャンプアドレステーブルが検索され、該当のプログラムアドレスを取得することによって、スイッチ選択番号に設定されたプログラムにジャンプをする。

  • なお、12 種に割り付けた現在のプログラム(機能)状況を次表に示す。 表の右半分は SW5 〜 SW1 までの各スイッチに、プログラムの機能別に与えた名称で、このように、同位置のスイッチでも、実行をするプログラムの機能によって名称が変更するので、 次表のように読み替えが必要となる。

          スイッチ選択番号 プログラムの機能 SW5 SW4 SW3 SW2 SW1
          - メイン、プログラム処理の振り分け - - - - SET
          1 時分秒の表示 SIGNAL ZERO - TMODE EXIT*1
          2 年月日(曜日)の表示 SIGNAL - - - EXIT*1
          3 温度、湿度の表示 SIGNAL - - - EXIT*1
          4 オートモードの表示 SIGNAL (ZERO)*2 - (TMODE)*2 EXIT*1
          5 標準文字フォントのデモ表示 - LEFT DOWN UP EXIT*1/RIGHT
          6 プロポーショナル文字フォントのデモ表示 - LEFT DOWN UP EXIT*1/RIGHT
          7 ひらがな文字フォントのデモ表示 - LEFT DOWN UP EXIT*1/RIGHT
          8 さくらさくら/赤とんぼ 電子オルゴール SIGNAL CHANGE DOWN UP EXIT
          9   未定 - - - - EXIT
          10   未定 - - - - EXIT
          11 LED の輝度調節 - - DOWN UP EXIT
          12 年月日、時分秒の設定 - BACK DOWN UP SET/EXIT

          *1 3 秒以上の "長押し" が必要、 *2 時分秒の表示のときだけ機能する

◎ 1. 時分秒の時計表示
  • メインルーチンから当プログラムにジャンプをしてくると、画面レイアウト図 中の 時分秒表示 を表示テンプレートとして、現在の "時" "分" "秒" が刻々と変化をしながら表示される。 デフォルトの表示は 24 時間表示で、12 時間表示のときに表示がされる AM / PM の位置は空白となる。
          時分秒表示:     [ AM 11:59:59 ]
          

  • この 24 時間表示と 12 時間表示は、UP / TMODE(TIME MODE)スイッチ を押すことによって、時分秒表示(オートモードを含む)のときにはいつでも切り替えることができ、押すごとに両者を切り替える、トグルスイッチになっている。

  • 次に、24 時間表示と 12 時間表示の対応を示すが、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
          
  • また、毎正時プラス・マイナス5分以内のときに、ZERO スイッチ を押すと、現在時刻の "分" と "秒" を "00 分 00 秒" に設定し直して、リアルタイムクロックモジュール RTC に書き込まれ、表示もその "00 分 00 秒" から再開始をする。 ただし、毎正時プラス・マイナス5分以外のときには、ZERO スイッチを押しても無視をされる。

  • この時計プログラムには、毎時の時報音を鳴らす機能も搭載されているが、SIGNAL スイッチ によって、時報音のブザー音出力を、押すごとに有効/無効にトグル設定をする。 このときの設定状態は視覚的な表示がないため、有効になったときには "ピピピッ" 音を、無効になったときには "ブー" 音を、 それぞれ鳴らして知らせる。

  • なお、誤操作を避けるために EXIT スイッチ3 秒以上長押し をすると、再び メインルーチンに戻る ことができる。(ちなみに、誤操作とは自分の意思に反して、誤ってスイッチを押してしまったような場合)

◎ 2. 年月日(曜日)の表示
  • メインルーチンから当プログラムにジャンプをしてくると、画面レイアウト図 中の 年月日表示 を表示テンプレートとして、現在の "年" "月" "日" と "曜日" が表示されるが、 毎深夜の零時に1度だけの更新で画面に動きがないため、当プログラムでは "曜日" をブリンク表示( 1 秒間 "点"、1 秒間 "滅" )させて、画面に動きを持たせている。
          年月日表示:     [ 22.02.06 SUN ]
          

  • 当プログラムが実行中にも時計機能は動作をしていて、毎時になると時報音を鳴らすため、SIGNAL スイッチ によって、時報音のブザー音出力を、押すごとに有効/無効にトグル設定をすることができる。 このときの設定状態は視覚的な表示がないため、有効になったときには "ピピピッ" 音を、無効になったときには "ブー" 音を、 それぞれ鳴らして知らせる。

  • なお、誤操作を避けるために EXIT スイッチ3 秒以上長押し をすると、再び メインルーチンに戻る ことができる。(ちなみに、誤操作とは自分の意思に反して、誤ってスイッチを押してしまったような場合)

◎ 3. 温度、湿度の表示
  • メインルーチンから当プログラムにジャンプをしてくると、画面レイアウト図 中の 温度湿度表示 を表示テンプレートとして、現在の "温度" と "湿度" が表示される。
          温度湿度表示:     [ xx.x℃ xx.x% ]
          

        注.上の写真で、右側の湿度の表示は、正常ではない ・・・ DHT11 でのトラブル を参照。)

  • 本機では、デジタル温度湿度センサーモジュール( DHT11 )と PIC との間を、たった1本だけのデータ線によるシリアル通信によって情報を得ているが、通信中にエラーが起こった場合には、次のようなエラーメッセージがマトリクス LED に表示されるとともに、 "ブッブー" ブザー音が出力される。
          タイムアウトエラー:   [ Error: DHT11 TO ]         チェックサム(CRC)エラー:   [ Error: DHT11 PE ]
          

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

  • 当プログラムが実行中にも時計機能は動作をしていて、毎時になると時報音を鳴らすため、SIGNAL スイッチによって、時報音のブザー音出力を、押すごとに有効/無効にトグル設定をすることができる。 このときの設定状態は視覚的な表示がないため、有効になったときには "ピピピッ" 音を、無効になったときには "ブー" 音を、 それぞれ鳴らして知らせる。

  • なお、誤操作を避けるために EXIT スイッチ3 秒以上長押し をすると、再び メインルーチンに戻る ことができる。(ちなみに、誤操作とは自分の意思に反して、誤ってスイッチを押してしまったような場合)

◎ 4. オートモードの表示
  • オートモードでは、上3項で説明をした、時分秒、年月日、温度湿度の各表示を、毎分ごとの 60 秒を次のように分割をして、それぞれを割り当て表示をする。
           (____: 時間表示、NNNN: 年月日表示、OOOO: 温度,湿度表示)
          
          00        10        20        30        40        50        00
          .    .    .    .    .    .    .    .    .    .    .    .    .
          __________NNNNNNNNNNNNNNN_______________OOOOOOOOOOOOOOO_____
          
  • メインルーチンから当プログラムにジャンプをしてきたときには、そのときの現在時間(秒)によって、3つの内のどの表示をするかが決められる。

  • 当プログラム(実際には3つの内のどれか)が実行中には、時計機能が動作をしてるので、毎時になると時報音を鳴らすため、SIGNAL スイッチによって、時報音のブザー音出力を、押すごとに有効/無効にトグル設定をすることができる。 このときの設定状態は視覚的な表示がないため、有効になったときには "ピピピッ" 音を、無効になったときには "ブー" 音を、 それぞれ鳴らして知らせる。

  • なお、誤操作を避けるために EXIT スイッチ3 秒以上長押し をすると、再び メインルーチンに戻る ことができる。(ちなみに、誤操作とは自分の意思に反して、誤ってスイッチを押してしまったような場合)

◎ 5. 標準文字フォントのデモ表示(電光掲示板風)
  • この項で紹介するプログラムは、標準文字フォントFont5x7s.tbl )の全 250 文字を使用して、電光掲示板のように文字が1ドットずつ横方向に流れながら表示をするプログラムで、 左右どちらの方向にも自由に変更をすることが可能なように、横スクロールの実現をさせている。

  • スクロールをしながら表示している例を次に示すが、動画ではないために様子がうまく伝わらないかもしれない。 電光掲示板を想像して欲しい。

    右端から1ドットずつフォントが現れ始め ・・・ やがて、左端に1ドットずつフォントが消え去って行く

  • スタートは左方向への流れ(スクロール)から始まり、右端からフォントテーブル先頭のコード h'06' 文字(↓)が顔を出し始め、1ドットずつ左方向に移動しながら続いて2番目のコード h'07' 文字(→)が現れ、以降同様に、 文字コードが1ずつインクリメントされた文字が現れながら、次々と1ドットずつ左方向に移動しながら表示されて行く。

  • やがて、最後のコード h'ff' 文字が現れると、その後はないので1ドットずつの空白が続く。 そして、コード h'ff' 文字が左端に消え去ったとき、すなわち、すべてが空白で埋まった後は、次には右方向への流れ(スクロール)に変わって、 今消え去った最後のコード h'ff' 文字が再び左端から現れ始める。

  • そして、1ドットずつ右方向に移動しながら続いて最後から2番目のコード h'fe' 文字(√)が現れ、以降同様に、文字コードが1ずつデクリメントされた文字が現れながら、次々と1ドットずつ右方向に移動しながら表示されて行く。

  • やがて、先頭のコード h'06' 文字(↓)が現れると、その前はないので1ドットずつの空白が続く。 そして、コード h'06' 文字(↓)が右端に消え去ったとき、すなわち、すべてが空白で埋まった後は、次には再び左方向への流れ(スクロール)に変わって、 今消え去った先頭のコード h'06' 文字(↓)が再び右端から現れ始める。

  • これまでの動作中にユーザが何の指示も出さなければ、以上の動作を延々と繰り返すことになるが、この間にユーザが LEFT スイッチ、または EXIT / RIGHT スイッチ を押すことによって、 ユーザの意思で 流れる(スクロール)方向をいつでも自由に変更 することができる。

  • また、UP スイッチ、または DOWN スイッチ を押すと、流れる(スクロール)速度(1ドットずつ移動する時間(mS))を変更 することができる。 本機では 10mS 〜 1,500mS まで 11 段階で変更ができ、 UP スイッチを押すごとに速くなり、DOWN スイッチを押すごとに遅くなる。 デフォルトは 200mS の設定で、この 11 段階の内、変更をしても どの段階になったのか表示機能がないため、上限値、下限値、デフォルト値のときには "ピッ" ブザー音を鳴らして、 音でユーザに知らせている。

  • 最後に、この項のプログラムを終わらせる場合は、EXIT / RIGHT スイッチ3 秒以上 "長押し" する ことによって、プログラムを終了させ、再び メインルーチンに戻る ことができる。 (この場合の "長押し" は、同一スイッチで EXIT / RIGHT の区別をさせるため)

◎ 6. プロポーショナル文字フォントのデモ表示(電光掲示板風)
    使用する文字フォントが プロポーショナル文字フォントFont5x7p.tbl )で、フォントの種類、表示数が異なるだけで、それ以外は "5. 標準文字フォントのデモ表示" と同様なので、同機能を参照のこと。

    プロポーショナル文字フォントのデモ表示例

◎ 7. ひらがな文字フォントのデモ表示(電光掲示板風)
    使用する文字フォントが ひらがな文字フォントFont8x8k.tbl )で、フォントの種類、表示数が異なるだけで、それ以外は "5. 標準文字フォントのデモ表示" と同様なので、同機能を参照のこと。

    ひらがな文字フォントのデモ表示例

◎ 8. さくらさくら/赤とんぼ 電子オルゴール
  • メインルーチンから当プログラムにジャンプをしてくると、選曲が、まず "さくらさくら" から始まり、曲の演奏に先立って、初めにドットマトリクス LED に、曲名のブリンク表示( 0.5 秒間 "点"、0.5 秒間 "滅" )が 5 秒間続く。

  • その後、曲データテーブルに書かれている各 指示ワード に従って、曲の演奏が始まるが、曲の各小節ごとの各音符に合わせて、曲の歌詞が1文字ずつドットマトリクス LED に、次々と表示がされて行く。

    音符位置 "さ" "く" "ら" の3文字が表示された例

  • やがて、1曲目の "さくらさくら" の演奏が終了すると、自動的に、2曲目の "赤とんぼ" の選曲がされ、同様に曲名のブリンク表示と曲の演奏、および歌詞の表示がされる。 そして、2曲目が終了するとまた1曲目に戻り、 この繰り返しがユーザの指示があるまで延々と続く。

  • これらの演奏途中に、CHANGE スイッチ を押すことによって、現在の演奏でない方が選曲 され、上記と同様に曲名のブリンク表示から始まる。 CHANGE スイッチはトグルになっていて、押すごとに交互選曲を行う。

  • また、UP スイッチ、または DOWN スイッチ を押すと、演奏されている 曲のテンポを変更 することができる。 テンポのデフォルトは、曲データテーブルに書かれている 指示ワード のテンポに対して、プラスマイナス 0 であるが、 このプラスマイナスの間を 15 段階で、UP スイッチを押すごとに速くなり、DOWN スイッチを押すごとに遅くなる。

  • なお、この 15 段階の内、変更をしても どの段階になったのか表示機能がないため、変更したテンポが上限値、下限値、デフォルト値のときには "ピッ" ブザー音を鳴らして、 音でユーザに知らせる。(ただし、演奏中なので分かり辛いかも)

  • 演奏途中に一時的に演奏音を止めたいような場合は、SIGNAL スイッチ を押すことによって、演奏音を止める ことができるが、曲の進行はそのまま続くので、1文字ずつの歌詞の表示をはじめとして、 曲の選曲交代、曲名のブリンク表示などはそのまま続けられる。 この SIGNAL スイッチもトグルになっていて、押すごとに演奏音を 止める/出す を交互に繰り返す。

  • なお、EXIT スイッチ を押すことによって、再び メインルーチンに戻る ことができる。

◎ 9 〜 10. ( 未定 )


◎ 11. LED の輝度調節
  • メインルーチンから当プログラムにジャンプをしてくると、メッセージ類の画面レイアウト図 中の LED 輝度調節メッセージが表示 され、 なおかつ、同メッセージの右端に位置する "9" の部分に、現在設定されている 輝度レベル値がブリンク表示 される。
          LED 輝度調節メッセージ:     [ brightness = 9 ]
          

  • このブリンク表示は、ユーザの注意を引くためのもので、メインルーチンの場合と同様に当プログラムでも、タイマー0割り込みを使用してブリンクのタイミングを取っている。 輝度レベルは、本機で使用したIC MAX7219 では 16 段階 で変更することが可能であるが、LED の安全性を考慮してプログラムでは、デフォルト値 0 から上限を 9 までの 10 段階に制限をしてある。

  • UP スイッチ、または DOWN スイッチ を押すことによって、輝度レベルの設定値を変更 することができ、輝度レベル値が更新されるとともに、ブリンク表示されている輝度レベル値も更新される。

  • なお、輝度レベルを調節した後、EXIT スイッチ を押すことによって、再び メインルーチンに戻る ことができる。

◎ 12. 年月日、時分秒の設定
  • メインルーチンから当プログラムにジャンプをしてくると、まず初めに、画面レイアウト図 中の 年月日表示 を表示テンプレートとして、現在の "年" "月" "日" と "曜日" が表示され、 なおかつ、左端に位置する "年" 2桁がブリンク表示 されて、設定変更の対象項目であることを示している。
          年月日表示:     [ 22.01.01 SAT ]  ..... ~~ の位置 "年" がブリンク表示
                       ~~
          

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

  • "年" の設定変更が済んだら、SET スイッチ を押すと、次には "月" 2桁がブリンク表示 され、設定変更の対象項目が次に移動したことを示す。 同様に変更が必要であれば、 UP スイッチ、または DOWN スイッチを押すことによって、適宜希望する値に変更をする。
          年月日表示:     [ 22.04.01 FRI ]  ..... ~~ の位置 "月" がブリンク表示
                          ~~
          

  • 同様に、SET スイッチ を押すと、次には "日" 2桁がブリンク表示 されるので、以降同様にスイッチ操作をして行く。
          年月日表示:     [ 22.04.18 MON ]  ..... ~~ の位置 "日" がブリンク表示
                             ~~
          

  • これまでのスイッチ操作で、"年" "月" "日" のどれかの値を変更すると、それらの設定値に基づいてプログラムが "曜日" を自動的に変更するが、この "曜日" をユーザが変更することはできない。

  • この "日" の設定変更が済んだ後に SET スイッチ を押すと、次には 画面レイアウト図 中の 時分秒表示 を表示テンプレートとして、現在設定されている "時" "分" "秒" に表示が変更され、 なおかつ、"時" 2桁がブリンク表示 される。
          時分秒表示:     [ AM 12:00:00]   ..... ~~ の位置 "時" がブリンク表示
                          ~~
          

  • 以降同様にして "分" "秒" と対象項目の変更をして行くが、これまでに、誤って希望の対象項目を通り過ぎてしまったときには、BACK スイッチ を押すごとに1ずつ前の項目に戻ることができる。

  • この対象項目と SET スイッチ、BACK スイッチの関係をまとめると、次のようになる。
                      BACK   BACK   BACK   BACK   BACK
                      ←─   ←─   ←─   ←─   ←─
                       SET    SET    SET    SET    SET    SET
          メインルーチン ─→ 年 ─→ 月 ─→ 日 ─→ 時 ─→ 分 ─→ 秒 ─→ メインルーチン
                     │                   ↑
                  BACK└───────────────────┘
          
  • なお、この時分秒表示で 12 時間表示の設定がされているとき には、"時" の左側の位置に AM または PM の文字が表示されるが、24 時間表示に設定のとき には、同位置は空白表示となる。 そしてこの 12 時間表示 / 24 時間表示 の設定変更は、1. 時分秒の時計表示 プログラムの実行時のみ可能で、当プログラムで変更することはできない。(デフォルトは 24 時間表示)

  • 最後の対象項目 "秒" がブリンク表示されているときに、SET / EXIT スイッチ を押すと、今まで設定をしてきた "年" 〜 "秒" までの、すべての項目が リアルタイムクロックモジュール RTC に書き込み更新され、 新たな設定値で RTC の時刻のカウントが始まると同時に、プログラムは再び メインルーチンに戻る

| ページトップ |

■ プログラム ■


● 事前に必要な予備知識など

 ◎ PIC16F1 ファミリーのアーキテクチャ

 本機を製作するに当たって、まず、PIC には PIC16F1 ファミリーの PIC16F1709 を使用することとしました。 PIC16F1709 は、前回公開の "194. FMステレオラジオ III" で使用した PIC16F1705 とは、 ピン数が 14 ピンから 20ピンに増加(I/O 数が増加)をしただけで、プログラムメモリ、データメモリの構成等はまったく同様です。

 本機のプログラムを 現在の最新バージョン で収録、公開をしていますが、PIC16F1 ファミリーは従来の PIC16F ファミリーとはアーキテクチャが異なっているため、特に、本機のプログラムのように アセンブラで記述をしたプログラムを理解するためには、その前にまず、PIC16F1 ファミリーのアーキテクチャを理解することが必要です。

 "194. FMステレオラジオ III" のプログラムの項で、両者のアーキテクチャの違い等を説明しているので、もしも PIC16F1 ファミリーについては良く知らない、という方は少なくても次の3項目だけは理解をするようにしてください。 (私も、数か月前まではまったく知りませんでした。)  "194. FMステレオラジオ III" では、本機とは実現をさせる機能内容が異なっているため、使用したアセンブラ命令の種類、およびプログラムメモリ、データメモリの使用状況も、どちらかというと控えめでした。 それに対して本機では、PIC16F1709 の持つリソースを存分に使用し、しかも、使い切ったに近いプログラム内容となりました。

 まず、PIC16F1709 のデータメモリの内の半分(Microchip Technology 社のデータシート(PIC16(L)F1705/9)から抜粋)を次に示しますが、彩色をした部分が本機のプログラムで使用をしている部分で、特に GPR については、バンク 0 からバンク 12 まで PIC16F1709 に実装されているすべてをプログラムで使用しています。

 薄緑色に彩色をしたバンク 0 、バンク 1 は、従来型データメモリとして使用をし、水色のバンク 2 〜バンク 12 については、リニア データメモリ* として本機のプログラムでは扱っています。 (リニア データメモリ*: "194. FMステレオラジオ III" の 間接アドレス指定(補足) の項を参照)


 次にプログラムメモリですが、PIC16F1 ファミリーでは次図に示すように、2K ワードずつのページ単位が、最大でページ 0 〜 15 までの 16 ページ(2K x 16 = 32K ワード) で構成され、従来の PIC16F ファミリー の 4 倍のメモリ空間に拡張されていますが、 本機で使用した PIC16F1709 では、薄緑色に彩色をしたページ 0 〜 3 までの 4 ページ(2K x 4 = 8K ワード)だけが実装されています。


 PIC で大きなプログラムを作成する場合には、データメモリにおけるバンクの問題、プログラムメモリにおけるページの問題を、常に念頭に置きながら作成をする必要があるのですが、PIC16F1 ファミリー においては、 データメモリの直接アドレス指定専用に バンクセレクト レジスタ ( BSR ) が設けられ、そして、この BSR レジスタにリテラル値を設定するための専用の命令、MOVLB(Move literal to BSR)が追加されて比較的容易になっています。

 たとえば、本機ではタクトスイッチの操作の有無を検知するために、プログラムの一部では 状態変化割り込み を使用していて、その割り込みフラグをクリアするために
      		movlb	7			;バンク 7
      		clrf	IOCAF			;状態変化割り込みフラグをクリア
      		movlb	0			;バンク 0
      
のような使い方をします。 上の TABLE 3-4 に示すように、IOCAF レジスタはバンク 7 に存在するため、まず、バンク 7 を指定してから IOCAF レジスタをクリアし、その後、再びバンク 0 に戻しています。

 また、プログラムメモリについても同様に、ページを切り替えるために用意された命令で、PCLATH レジスタにリテラル値を設定するための命令、MOVLP(Move literal to PCLATH)も追加され、その使用例として
      		movlp	high time_display
      		call	time_display		;時分秒を LED モジュールに表示
      		movlp	high $
      
のような使い方をします。 この例は実際の本機のプログラムの一部で、ページ 0 にあるプログラムからページ 2 に存在するサブルーチン time_display を CALL している例で、まず、MOVLP 命令で time_display が存在するプログラムメモリアドレスの、 上位バイトを PCLATH レジスタに設定をしています。

 "194. FMステレオラジオ III" の プログラムメモリの構成とプログラムカウンタ(PC) の項で述べたように、PIC16F1 ファミリー では GOTO, CALL 命令が実行される場合には、 PCLATH レジスタの bit6 - bit3 の 4 ビットが、プログラムカウンタ(PC)の bit14 - bit11 に入れられてジャンプをします。 したがって、この 4 ビットがジャンプ先のもの(この例ではページ 2)でないと、目的とするサブルーチン time_display を CALL することはできません。

 この CALL 命令が実行されるときに、CALL に続く次の MOVLP 命令のアドレスがスタックメモリに PUSH されるため、サブルーチン time_display から戻るときには、サブルーチン末尾の RETURN 命令の実行によって、 PUSH されていたアドレスがプログラムカウンタ(PC)に POP され、CALL命令 の次の MOVLP 命令が実行されることになります。

 そして、この MOVLP 命令では自分(この例ではページ 0)のプログラムメモリアドレスの、上位バイトを再び PCLATH レジスタに設定をして元に戻しています。

 次に、本機のプログラムでは、間接アドレス指定をするための 2 バイトの FSR レジスタペア FSRnH と FSRnL を多用しています。 従来の PIC16F ファミリーでは、間接アドレス指定をする FSR レジスタは 1 バイトで、 対象となるメモリはデータメモリ専用でした。

 これに対して PIC16F1 ファミリーでは、データメモリはもちろんですが、プログラムメモリ内に定義をしたデータにも、間接アドレス指定でアクセスをすることができます。 しかも FSR0HFSR0LFSR1HFSR1LTABLE 3-2 を参照)の2組が用意され、INDF0INDF1 レジスタが これらのレジスタペアに対応した、間接レジスタ となっています。

 これらのレジスタペアを使用した間接アドレス指定の一例を次に示します。 この例では、リニアメモリバッファ( linear_buff )のメモリ内容を、フォントデータ表示バッファ( fd_buff )へ 8 * 8 = 64 バイトのコピーをしていますが、 このように、データ移動等のプログラムも簡単に行うことができてしまいます。
      		movlw	high linear_buff	;リニアメモリバッファの先頭アドレス
      		movwf	FSR1H			;
      		movlw	low linear_buff		;
      		movwf	FSR1L			;FSR1H:FSR1L 間接アドレスに設定
      
      		movlw	high fd_buff		;フォントデータ表示バッファの先頭アドレス
      		movwf	FSR0H			;
      		movlw	low fd_buff		;
      		movwf	FSR0L			;FSR0H:FSR0L 間接アドレスに設定
      
      		movlw	8 * 8			;ディスプレイの横幅ドット数
      		movwf	lp_cnt4			;ループカウンタ4
      
      plybk12		moviw	FSR1++			;リニアメモリバッファの読み出し
      		movwi	FSR0++			;フォントデータ表示バッファへ格納
      		decfsz	lp_cnt4,F		;ループカウンタ4 - 1 = 0 か?
      		goto	plybk12			;No
      
 上のプログラム例ではデータを送る側も受ける側も、どちらもデータメモリを対象としているので問題はないのですが、このときの注意すべき点は、受ける側にプログラムメモリを指定することはできません。 FSR レジスタペアを使用してプログラムメモリに書き込むことはできないのです。 もしも、プログラムメモリに書き込む必要がある場合は、 "194. FMステレオラジオ III" の 高書き込み耐性フラッシュメモリのプログラミング で説明をした方法だけが可能です。

 このように、本機のプログラムでは2組の間接アドレス指定のレジスタペアを多用していますが、欲を言えば、もう1組 FSR レジスタペアが存在したならば、もっとプログラミングが楽になった、と思う場面がしばしばありました。

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


 ◎ ドットマトリクス 8 x 8 LED と MAX7219 制御

 本機を製作するに当たっては、ドットマトリクス 8 x 8 LED とそのドライバ IC MAX7219 が共に主役となるため、やはりそれらのことを理解しなければ使いこなすことは難しいと思います。 先に製作をした "189. ドットマトリクス 8 x 8 LED 表示時計 II ( MAX7219 版)" において、ドットマトリクス 8 x 8 LED については ドライバ IC MAX7219 については、プログラムの項の でそれぞれ詳述をしているので、是非ともこれらを参考にしてみてください。

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


● 本機のプログラム機能の構成とプログラミング

 本機では、最大 12 種類までのプログラム(機能)を実行することができるように構成されています。 メインルーチンではその 12 種類のプログラムを選択し、振り分ける処理を行います。 12 種類のプログラムと限られているのは、それらを選択するスイッチに 12 接点のロータリスイッチを使用したためで、また、このロータリスイッチの使用に関しても深い意味はなく、私の気紛れというか何となく使用してみただけのことです。

 そして、12 種に割り付けた現在のプログラム(機能)状況を次表に示します。 なお、表の右半分は SW5 〜 SW1 までの各スイッチに、プログラムの機能別に与えた名称です。 このように、同位置のスイッチでも、実行をするプログラムの機能によって名称が変更するので、次表のように読み替えが必要となります。
          スイッチ選択番号 プログラムの機能 SW5 SW4 SW3 SW2 SW1
          - メイン、プログラム処理の振り分け - - - - SET
          1 時分秒の表示 SIGNAL ZERO - TMODE EXIT*1
          2 年月日(曜日)の表示 SIGNAL - - - EXIT*1
          3 温度、湿度の表示 SIGNAL - - - EXIT*1
          4 オートモードの表示 SIGNAL (ZERO)*2 - (TMODE)*2 EXIT*1
          5 標準文字フォントのデモ表示 - LEFT DOWN UP EXIT*1/RIGHT
          6 プロポーショナル文字フォントのデモ表示 - LEFT DOWN UP EXIT*1/RIGHT
          7 ひらがな文字フォントのデモ表示 - LEFT DOWN UP EXIT*1/RIGHT
          8 さくらさくら/赤とんぼ 電子オルゴール SIGNAL CHANGE DOWN UP EXIT
          9   未定 - - - - EXIT
          10   未定 - - - - EXIT
          11 LED の輝度調節 - - DOWN UP EXIT
          12 年月日、時分秒の設定 - BACK DOWN UP SET/EXIT
          *1 3 秒以上の "長押し" が必要、 *2 時分秒の表示のときだけ機能する
 1, 2, 3, 4 については、"189. ドットマトリクス 8 x 8 LED 表示時計 II ( MAX7219 版)" で実現をさせている表示機能とほぼ同等です。 ただ、同表示時計ではコロン(:)、ピリオド(.) を表示させるのに、それ専用の LED を設けてありましたが、 本機ではドットマトリクス 8 x 8 LED を汎用的に使用して表示をさせています。

 5, 6, 7 については、電光(電子)掲示板のように文字が横方向に流れながら表示をするプログラムで、それぞれが扱っている文字フォントの種類が異なっています。 本機では「4連ドットマトリクス 8 x 8 LED モジュール」を2個(計8連)横に並べて、 左右どちらの方向にも自由に変更することが可能なように、横スクロールを実現させています。

 ネットでこの「4連ドットマトリクス 8 x 8 LED モジュール」を検索してみると、Arduino や Raspberry Pi によってスクロールをさせている例をよく見かけるのですが、 私が本機の製作に至ったのも、これらのスクロール例に触発された影響が非情に大です。

 8 については、17 年も以前に製作をした "025. 電子オルゴール" を元にして、そのメロディーに合わせながら、その曲の歌詞を1文字ずつ表示をする機能のプログラムです。

 11, 12 については、メンテナンス的な存在のプログラムで、11 は LED の輝度調節を行うもので、輝度レベルを 0 から 9 までの 10 段階で変更設定することが可能です。 また、12 は本機の持つ時計機能の年月日、時分秒の変更設定を行うものです。 これらの機能も、"189. ドットマトリクス 8 x 8 LED 表示時計 II ( MAX7219 版)" に持たせてある機能とほぼ同等です。

 なお、9, 10 については、現在思案中で割り付けがなく、選択をしても何も実行をすることはありません。

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


 ◎ メッセージ類の画面レイアウトとデータ変換および LED 表示

 まず初めに、個々のプログラム(機能)の説明をする前に、本機で使用している各種の文字フォントデータを、如何にしてマトリクス 8 x 8 LED モジュールに表示させるか、についてを説明します。

 上述のように、本機では「4連ドットマトリクス 8 x 8 LED モジュール」を2個(計8連)横に並べて、すなわち、横(8 ドット x 8 連 = 64 ドット)x 縦 8 ドット = 横 64 ドット x 縦 8 ドット = 合計 512 ドット の マトリクス LED スクリーンを作り出し、そのスクリーン上にいろいろなメッセージ類を表示させています。

 横 64 ドットというのは一見広そうに見えますが、実際にメッセージの文字列を表示させてみると、決して広くはなくなおも数ドット以上は欲しく感じられます。

 本機では、この 横 64 ドット x 縦 8 ドットの LED スクリーン を使って、次のレイアウト図に示すいろいろなメッセージ類を表示させていますが、横 64 ドット以内に各メッセージをバランス良く収めるために、 下から3つのメッセージを除いて英数文字の表示には、プロポーショナル文字フォント*1 、また、下から3つのメッセージ、すなわち時分秒、年月日(曜日)、温度、湿度の英数文字の表示には、 標準文字フォント*2 の 各フォントテーブルのデータを使用しました。

 ただし、メッセージ内のコロン(:)、ピリオド(.) 等の記号には、これらのフォントテーブルのデータを使用すると、横幅バランスが悪くなったりするためテーブルデータは使用しないで、直接フォントデータを指定してスクリーンの横幅バランスを取っています。

 上のレイアウト図は横ドット方向に関してのレイアウトであり、縦ドット方向に関してはこの図からは見えませんが、上図では小さな1マスが1バイトに対応していて、縦ドット方向には1バイト( = 8 ビット)の各ビットが対応しています。

 本機で使用したプロポーショナル文字フォント、標準文字フォントなどでは、このレイアウト図には都合が良いように定義された文字フォントとなっていて、フォントの各バイトを上図の1マスごとに当てはめて行けば、全体としての文字列(メッセージ)が 出来上がります。

 たとえば、アルファベット 'M' のフォントは、プロポーショナル文字フォント、標準文字フォントのどちらも
      		dt	b'01111111'
      		dt	b'00000010'
      		dt	b'00001100'
      		dt	b'00000010'
      		dt	b'01111111'
      
のように定義がされていて、この定義では文字が横に寝て見えますが、各バイトの LSB(最下位)ビットを上にして上図の1マスごとに当てはめて行けば、1バイト( = 8 ビット)ごとの各ビットが、縦ドット方向に並び変わります。 (ドットマトリクス 8 x 8 LED をこのように制御することは可能)

 以上はレイアウト図で説明をしてきましたが、実際の場合にもデータメモリ(RAM)上に設けたバッファ( fd_buff )に、各バイトを同様に展開して行きます。

 ところが、本機のようにドットマトリクス 8 x 8 LED を、ドライバ IC MAX7219 で制御する場合には、このような 縦ドット並び ではまったく都合が悪いのです。 MAX7219 では、このように縦ドット方向に制御することはできず、 横ドット方向に制御することしかできません。 しかも、マトリクス 8 x 8 LED 単体ごとに制御をする必要があります。

 したがって、上のフォントデータを、たとえば次のように、縦ビット → 横ビットに変換をして MAX7219 に送り出す必要があるのです。(実際にはもう一過程の変換(各ビットの並びを左右逆に変換)をする必要がある ( "189. ドットマトリクス 8 x 8 LED 表示時計 II ( MAX7219 版)" の 事前に必要な予備知識 を参照))
      		dt	b'10001000'
      		dt	b'11011000'
      		dt	b'10101000'
      		dt	b'10101000'
      		dt	b'10001000'
      		dt	b'10001000'
      		dt	b'10001000'
      		dt	b'00000000'
      
 この目的のために作成をしたのが次に示す サブルーチン( verti_horiz_conv ) で、8 バイトごとの 縦ビット並びデータ横ビット並びデータ に変換をします。 ( 7. ひらがな文字フォントのデモ表示 の項で示した 概念図 の A の変換を参照)
    ( 2022/5/18 更新 )
     次に示すリスト中で、変換フラグ: vhcv_flg の参照しているビットが、現在の最新バージョン に収録をした、実際の "ソースファイル (MatrixLED_Controller.asm)" のものと異なっていました。 プログラムを変更(旧 bit0、新 bit7)した後、ここに示したリストに反映されていなかったので、正しいリストに差し替えを行いました。
      ;--------------------------------------------------------------------------
      ;  LED モジュールn桁分の縦横ビットのデータ変換(ビット列の反転機能付き)
      ;--------------------------------------------------------------------------
      
      ;	変換対象バッファ: 任意 fd_buff (8 x n バイト)
      ;	W レジスタ:	  変換する桁数
      
      ;	汎用変数:	  b16_hi, b16_lo, lp_cnt1, lp_cnt2, lp_cnt3
      ;	変換フラグ:	  vhcv_flg
      ;	ワークバッファ:	  fd_buff + 64 (8 バイト)
      
      ;#define 	m_fsr0h		b16_hi
      ;#define 	m_fsr0l		b16_lo
      
      ; 格納先バッファ                                                                ワークバッファ
      ; fd_buff+0      +8*1     +8*2     +8*3     +8*4     +8*5     +8*6     +8*7     +8*8   +8*8+7
      ;	 |        |        |        |        |        |        |        |        |      |
      ;	+--------+--------+--------+--------+--------+--------+--------+--------+--------+ +
      ;	|        |        |        |        |        |        |        |        |        | |
      ;	|        |        |        |        |        |        |        |        |        | | 8 bit
      ;	|        |        |        |        |        |        |        |        |        | |
      ;	+--------+--------+--------+--------+--------+--------+--------+--------+--------+ +
      ;	                                                                        +--------+
      ;	                                                                          8 byte
      ;--------------------------------------------------------------------------
      
      horiz_verti_conv	;------- 横→縦ビットのデータ変換
      
      		bsf	vhcv_flg,7		;縦横ビット変換フラグ = ON
      		goto	vhcnv00
      
      verti_horiz_conv	;------- 縦→横ビットのデータ変換
      
      		bcf	vhcv_flg,7		;縦横ビット変換フラグ = OFF
      
      vhcnv00		movwf	lp_cnt1			;ループカウンタ1(桁数カウンタ)
      
      vhcnv01		movlw	high (fd_buff + 8 * 8)	;ワークバッファの先頭アドレス
      		movwf	FSR1H			;
      		movlw	low (fd_buff + 8 * 8)	;
      		movwf	FSR1L			;FSR1H:FSR1L 間接アドレスに設定
      
      		movlw	8			;8 バイト
      		movwf	lp_cnt2			;ループカウンタ2(バイトカウンタ)
      
      vhcnv02		movf	m_fsr0h,W		;退避したバッファの先頭アドレス
      		movwf	FSR0H			;
      		movf	m_fsr0l,W		;
      		movwf	FSR0L			;FSR0H:FSR0L 間接アドレスに設定
      
      		movlw	8			;8 ビット
      		movwf	lp_cnt3			;ループカウンタ3(ビットカウンタ)
      
      vhcnv03		btfss	vhcv_flg,7		;縦横ビット変換フラグ = OFF か?
      		rrf	INDF0,F			;Yes. C bit を右(LSB)から出力
      		btfsc	vhcv_flg,7		;縦横ビット変換フラグ = ON か?
      		rlf	INDF0,F			;Yes. C bit を左(MSB)から出力
      
      		rrf	INDF1,F			;C bit を入力
      		addfsr	FSR0,1			;間接アドレス FSR0H:FSR0L + 1
      		decfsz	lp_cnt3,F		;ループカウンタ3(ビットカウンタ)- 1 = 0 か?
      		goto	vhcnv03			;No
      
      		addfsr	FSR1,1			;間接アドレス FSR1H:FSR1L + 1
      		decfsz	lp_cnt2,F		;ループカウンタ2(バイトカウンタ)- 1 = 0 か?
      		goto	vhcnv02			;No
      ;
      		movlw	high (fd_buff + 8 * 8)	;ワークバッファの先頭アドレス
      		movwf	FSR1H			;
      		movlw	low (fd_buff + 8 * 8)	;
      		movwf	FSR1L			;FSR1H:FSR1L 間接アドレスに設定
      
      		movf	m_fsr0h,W		;退避したバッファの先頭アドレス
      		movwf	FSR0H			;
      		movf	m_fsr0l,W		;
      		movwf	FSR0L			;FSR0H:FSR0L 間接アドレスに設定
      
      		movlw	8			;8 バイト
      		movwf	lp_cnt2			;ループカウンタ2(バイトカウンタ)
      
      vhcnv04		moviw	FSR1++			;ワークから
      		movwi	FSR0++			;バッファへ格納
      		decfsz	lp_cnt2,F		;ループカウンタ2(バイトカウンタ)- 1 = 0 か?
      		goto	vhcnv04			;No
      
      		movf	FSR0H,W			;次のフォントデータ格納先バッファの先頭アドレス
      		movwf	m_fsr0h			;
      		movf	FSR0L,W			;
      		movwf	m_fsr0l			;退避
      		decfsz	lp_cnt1,F		;ループカウンタ1(桁数カウンタ)- 1 = 0 か?
      		goto	vhcnv01			;No
      
      		return
      
 実は、上のサブルーチンには入口が2つあり、verti_horiz_conv縦 → 横ビットのデータ変換horiz_verti_conv が逆の 横 → 縦ビットのデータ変換 をするサブルーチンになっています。 両者のサブルーチンでは、ほんの一部分が異なっているだけでほとんどが共通なため、このような双頭の構造にしてあります。

 本機ではプロポーショナル文字フォント、標準文字フォントの他に、8 x 8 ドットのひらがな文字フォントも扱っていて、
      font_a		retlw 	b'00010000'	;あ
      		retlw 	b'01111110'
      		retlw 	b'00010000'
      		retlw 	b'00010010'
      		retlw 	b'00111110'
      		retlw 	b'01010101'
      		retlw 	b'01011001'
      		retlw 	b'00110010'
      
のように、初めから横ビット並びのフォントデータになっているのですが、本機の場合にはこれが災いして都合が悪いのです。 このひらがな文字フォントを複数文字横に並べて表示する場合に、文字間に1ドット以上の空白をおかないと、文字と文字がくっついて判読がしづらくなります。 そこで、文字間に空白を挿入したいのですが、1バイト( = 8 ビット)の枠を超えてしまうので、それができません。

 そのために機能するのが サブルーチン( horiz_verti_conv )で、横ビット並び のフォントデータを、縦ビット並び のフォントデータにわざわざ変換をしています。 縦ビット並び のフォントデータに変換をしてしまえば、プロポーショナル文字フォント、標準文字フォントのように、 文字間に空白バイトをおきながら自由に、データメモリ(RAM)上のバッファに展開をすることができます。 その後改めて、文字間の空白バイトも含めて 8 バイトごとにした 縦ビット並び データを、サブルーチン( verti_horiz_conv )横ビット並び データに変換をします。 ( 7. ひらがな文字フォントのデモ表示 の項で示した 概念図 の @ と A の変換を参照)

 後は、次の サブルーチン( dxled_display, dxled_display_x8 ) によって、マトリクス 8 x 8 LED に表示をさせています。
      ;--------------------------------------------------------------------------
      ;		ドットマトリクス LED 8桁分の表示
      ;--------------------------------------------------------------------------
      
      ;	表示対象バッファ: fd_buff (8 x 8 = 64 バイト)
      
      ;	汎用変数:	  b16_hi, b16_lo, lp_cnt1, digit
      
      ;#define 	m_fsr0h		b16_hi
      ;#define 	m_fsr0l		b16_lo
      ;#define 	dsp_cnt		segment
      
      ;--------------------------------------------------------------------------
      
      dxled_display_x8	;-------
      
      		movlw	high (fd_buff + 8 * 7)	;フォントデータ格納先バッファのアドレス
      		movwf	FSR0H			;
      		movlw	low (fd_buff + 8 * 7)	;
      		movwf	FSR0L			;FSR0H:FSR0L 間接アドレスに設定
      
      		movlw	8			;
      		movwf	dsp_cnt			;LED 8 桁分の表示
      		clrf	nop_cnt
      		call	dxled_display		;ドットマトリクス LED n 桁分の表示
      		return
      
      ;--------------------------------------------------------------------------
      ;		ドットマトリクス LED n桁分の表示
      ;--------------------------------------------------------------------------
      
      dxled_display		;-------
      
      		movlw	h'08'			;Digit = 7
      		movwf	digit			;Digit の初期値
      
      dxdsp01		movf	FSR0H,W			;フォントデータ格納先バッファの先頭アドレス
      		movwf	m_fsr0h			;
      		movf	FSR0L,W			;
      		movwf	m_fsr0l			;退避
      
      		movf	dsp_cnt,W		;
      		movwf	lp_cnt1			;ループカウンタにセット
      		bcf	PORTC,maxLOAD		;maxLOAD="L"
      
      dxdsp02		movf	digit,W			;Digit = X
      		call	max_byte_send		;レジスタアドレス
      		movf	INDF0,W			;バッファから読み出し
      		call	max_byte_send		;レジスタデータ
      						;間接アドレスを1つ前の桁にする
      		addfsr	FSR0,-8			;間接アドレス FSR0H:FSR0L - 8
      		decfsz	lp_cnt1,F		;dsp_cnt 分表示した か?
      		goto	dxdsp02			;No
      
      		movf	nop_cnt,W
      		call	no_operation		;ノーオペレーション
      
      		bsf	PORTC,maxLOAD		;maxLOAD="H"
      		movf	m_fsr0h,W		;退避したバッファの先頭アドレス
      		movwf	FSR0H			;
      		movf	m_fsr0l,W		;
      		movwf	FSR0L			;FSR0H:FSR0L 間接アドレスに設定
      
      		addfsr	FSR0,1			;間接アドレス FSR0H:FSR0L + 1
      		decfsz	digit,F			;全 Digit 表示した か?
      		goto	dxdsp01			;No
      
      		return		
      
 サブルーチン( dxled_display )は、個々のドットマトリクス LED 1桁分の表示をさせる場合、サブルーチン( dxled_display_x8 )は、ドットマトリクス LED 8桁分、すなわち、横 64 ドット x 縦 8 ドットの LED スクリーン全体の表示をさせる場合に使用します。

 なお、上のサブルーチン( dxled_display )内で使用されている、max_byte_send, no_operation の各サブルーチンは、MAX7219 を制御するための 基本制御サブルーチン で、"189. ドットマトリクス 8 x 8 LED 表示時計 II ( MAX7219 版)" の プログラムの項の、MAX7219 制御の基本的な考え方 で示したものと同じものですが、他のサブルーチン( max_led_contr )では、取り扱っているドットマトリクス LED の桁数が異なっているので、 本機のプログラム用に修正したものを次に再掲しておきます。
      ;==========================================================================
      ;		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 8個分の制御
      
      max_led_contr
      		bcf	PORTC,maxLOAD		;maxLOAD="L"
      		movlw	8			;
      		movwf	lp_cnt1			;ループカウンタにセット
      
      maxcont01	movf	reg_addr,W		;レジスタアドレス
      		call	max_byte_send
      		movf	reg_data,W		;レジスタデータ
      		call	max_byte_send
      
      		decfsz	lp_cnt1,F		;8個分送信した か?
      		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
      
      ;--------------------------------------------------------------------------
      
      ;		LED を 1 秒間消灯
      
      led_1s_off
      		movlw	h'0c'			;シャットダウンレジスタ
      		movwf	reg_addr
      		clrf	reg_data		;シャットダウン(LED 消灯)
      		call	max_led_contr
      
      		movlp	high wait_1sec
      		call	wait_1sec		;1 秒 ウェイト
      		movlp	high $
      
      		movlw	h'0c'			;シャットダウンレジスタ
      		movwf	reg_addr
      		movlw	h'01'			;通常動作
      		movwf	reg_data
      		call	max_led_contr
      		return		
      
      ;--------------------------------------------------------------------------
      
      ;		ノーオペレーション
      
      no_operation
      		movwf	lp_cnt1			;ループカウンタ1 にセット
      		movf	lp_cnt1,W		;
      		btfsc	STATUS,Z		;W = 0 か?
      		goto	nop02			;Yes
      
      nop01		clrw				;no-opレジスタ
      		call	max_byte_send		;
      		clrw				;dummy
      		call	max_byte_send		;
      		decfsz	lp_cnt1,F		;ループカウンタ1 - 1 = 0 か?
      		goto	nop01			;No
      
      nop02		return
      
      
 | プログラムのトップに戻る |


 ◎ メイン、プログラム処理の振り分け


    機能の概要

    • メインルーチンでは、本機のプログラム機能の構成とプログラミング で概要を述べたように、12 種類のプログラム(機能)から1つを選択し、それら個々のプログラムに振り分ける処理(ジャンプ)を行う。 ロータリスイッチ で実行したい プログラムを選択した後SET スイッチ を押すことによって 選択したプログラムにジャンプ を行う。

    • メインルーチンを実行中は、ユーザに実行させたいプログラムを選択するように促すため、上述した メッセージ類の画面レイアウト図 中の スイッチ選択メッセージが表示 され、 なおかつ、同メッセージの右端に位置する "99" の部分に、現在選択されている スイッチ選択番号がブリンク表示 される。
          スイッチ選択メッセージ:     [ Select.SW ?_ 99 ]
          
    • このブリンク表示はユーザの注意を引くためのもので、当メインルーチンのプログラムでは、タイマー0割り込みを使用してブリンクのタイミングを取っている。

    • ロータリスイッチのスイッチ選択番号をどのように取得しているかは、まず、回路図 の左上を参照のこと。 ロータリスイッチの 12 の各接点にダイオードマトリクスが組み込まれていることに、既に気付かれていると思うが、 この情報を PIC のポート C(RC4 〜 RC7)で監視し、1 〜 12 までのバイナリ信号として取り込んでいる。

    • そして、この後 SET スイッチが押されると、ロータリスイッチの スイッチ選択番号を元にして ジャンプアドレステーブルが検索され、該当のプログラムアドレスを取得することによって、スイッチ選択番号に設定されたプログラムにジャンプをする。


 次に示すリストが本機のメインルーチンのプログラムで、リスト中のコメント "ロータリスイッチの設定値別処理への振り分け" 以降が、次に実行するプログラムを選択後にジャンプをしている部分です。
      ;==========================================================================
      ;==========================================================================
      ;			メイン・ルーチン
      ;==========================================================================
      ;==========================================================================
      
      		; mode_flg:	年月日,時分秒の設定モードフラグ / メイン処理フラグ
      				; bit7: 設定モードフラグ
      				; bit6: ブリンク反転フラグ (タイマー0割り込みで設定)
      				; bit5: ブリンク変更フラグ (     〃      )
      			     ;>	; bit0: メイン処理フラグ
      
      		; rtsw_sel:	ロータリスイッチ選択フラグ
      				; bit7:   初期メッセージ表示指示フラグ
      				; bit6:   ブリンク反転フラグ (タイマー0割り込みで設定)
      				; bit5:   ブリンク変更フラグ (     〃      )
      				; bit4:   設定値 10 以上フラグ (設定値の表示時のみ使用)
      				; bit3-0: ロータリスイッチ設定値 (1 〜 12)
      
      ;--------------------------------------------------------------------------
      
      main		;-------
      
      		movlw	h'01'			;メイン処理フラグ = ON
       		movwf	mode_flg		;年月日,時分秒の設定モードフラグの初期設定
      		movlw	b'10100000'		;bit7: 初期メッセージ表示指示 = ON
      						;bit6: ブリンク反転フラグ     = OFF
      						;bit5: ブリンク変更フラグ     = ON
      		movwf	rtsw_sel		;初期設定
      
      		movlw	tm0_h_val		;ハードタイマー0カウント値を
      		movwf	TMR0			;TMR0 に設定
      		movlw	tm0_s_val		;ソフトタイマー0カウント値を
      		movwf	tm0_s_cnt		;ソフトタイマー0カウンタに設定
      		bcf	INTCON,TMR0IF		;bit2(TMR0IF)=0: タイマー0割込みフラグをクリア
      		bsf	INTCON,TMR0IE		;bit5(TMR0IE)=1: タイマー0割り込みを許可
      
      		; ロータリスイッチ設定値の LED 表示
      
      main01		movlw	h'f0'
      		andwf	rtsw_sel,F		;フラグだけ残す
      		swapf	PORTC,W			;ロータリスイッチの設定値を読む
      		andlw	h'0f'			;スイッチデータを取り出し
      		xorlw	h'0f'			;反転する
      		iorwf	rtsw_sel,F		;新しい設定値に更新
      
      main02		movlp	high select_msg_display
      		call	select_msg_display	;ロータリスイッチ選択メッセージを表示
      		movlp	high $
      
      		; SET スイッチとロータリスイッチの監視
      
      main03		btfss	PORTA,_swSET		;SET スイッチ = ON か?
      		goto	main05			;Yes
      
      		swapf	PORTC,W			;ロータリスイッチの設定値を読む
      		xorlw	h'0f'			;反転する
      		xorwf	rtsw_sel,W		;現在の rtsw_sel と比較する
      		andlw	h'0f'			;フラグを除く
      		btfsc	STATUS,Z		;設定値 = 更新された か?
      		goto	main04			;No
      
      		call	wait_30ms		;チャッタリング吸収
      		swapf	PORTC,W			;ロータリスイッチの設定値を再度読む
      		xorlw	h'0f'			;反転する
      		xorwf	rtsw_sel,W		;現在の rtsw_sel と比較する
      		andlw	h'0f'			;フラグを除く
      		btfsc	STATUS,Z		;設定値 = 更新された か?
      		goto	main04			;No
      
      		bsf	rtsw_sel,5		;ブリンク変更フラグ rtsw_sel.5 = ON
      		goto	main01			;Yes
      
      main04		movlw	20			;要調整
      		call	wait_xxms
      		btfss	rtsw_sel,5		;ブリンク変更フラグ rtsw_sel.5 = ON か?
      		goto	main03			;No
      		goto	main02			;Yes
      
      main05		call	wait_30ms		;チャッタリング吸収
      		btfsc	PORTA,_swSET		;再度 SET スイッチ = ON か?
      		goto	main03			;No
      
      		btfss	PORTA,_swSET		;SET スイッチ = OFF か?
      		goto	$ - 1			;No
      
      		; ロータリスイッチの設定値別処理への振り分け
      
      		bcf	INTCON,TMR0IE		;bit5(TMR0IE)=1: タイマー0割り込みを禁止
      		clrf	mode_flg		;メイン処理フラグ = OFF
      		movlw	h'0f'
      		andwf	rtsw_sel,F		;フラグを消去 !!
      
      		decf	rtsw_sel,W		;0 始まりにする
      		movwf	FSR1L
      		lslf	FSR1L,F			;2 倍する
      		movlw	low jump_addr_tbl
      		addwf	FSR1L,F			;ジャンプアドレステーブルの先頭 low アドレスを足す
      		clrf	FSR1H
      		movlw	high jump_addr_tbl
      		addwfc	FSR1H,F			;ジャンプアドレステーブルの先頭 high アドレスを足す
      
      		moviw	0[FSR1]			;
      		movwf	PCLATH			;
      		moviw	1[FSR1]			;
      		movwf	PCL			;ジャンプ
      
      ;--------------------------------------------------------------------------
      
      		; 処理別ジャンプアドレステーブル
      jump_addr_tbl
      		dt	high time_disp_process		;rtsw_sel =  1	;(ページ 0)
      		dt	low time_disp_process
      		dt	high date_disp_process		;rtsw_sel =  2	;(ページ 0)
      		dt	low date_disp_process
      		dt	high temp_humi_disp_process	;rtsw_sel =  3	;(ページ 0)
      		dt	low temp_humi_disp_process
      		dt	high auto_disp_process		;rtsw_sel =  4	;(ページ 0)
      		dt	low auto_disp_process
      		dt	high font5x7s_process		;rtsw_sel =  5	;(ページ 1)
      		dt	low font5x7s_process
      		dt	high font5x7p_process		;rtsw_sel =  6	;(ページ 1)
      		dt	low font5x7p_process
      		dt	high font8x8kana_process	;rtsw_sel =  7	;(ページ 1)
      		dt	low font8x8kana_process
      		dt	high sakura_tonbo_process	;rtsw_sel =  8	;(ページ 1)
      		dt	low sakura_tonbo_process
      		dt	high blank_process		;rtsw_sel =  9	;(ページ 1)
      		dt	low blank_process
      		dt	high blank_process		;rtsw_sel = 10	;(ページ 1)
      		dt	low blank_process
      		dt	high bright_adj_process		;rtsw_sel = 11	;(ページ 0)
      		dt	low bright_adj_process
      		dt	high date_time_adj_process	;rtsw_sel = 12	;(ページ 0)
      		dt	low date_time_adj_process
      
 このメインルーチンのプログラムで重要なところは ジャンプアドレステーブル で、本機のプログラムのようにジャンプ先のプログラムがたくさんある場合に、この方法を使用すればジャンプ先のプログラムがどのページに存在していても、 簡単でしかも間違いなくジャンプさせることができます。

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


 ◎ 1. 時分秒の時計表示

 本機では、本機で使用した各種モジュールについて の項の、リアルタイムクロック( DS1307 )モジュール で述べたように、リアルタイムクロックモジュールには、DS1307 版モジュール を使用しましたが、 "189. ドットマトリクス 8 x 8 LED 表示時計 II ( MAX7219 版)" 等で使用した DS3231 版モジュール に比べると、精度はかなり劣っているようです。 本機は "お遊び要素" が大きいため DS1307 版モジュール で良いと判断をしたのですが、 より高精度を望まれる方は DS3231 版モジュール を使用するべきです。


    機能の概要

    • メインルーチンから当プログラムにジャンプをしてくると、上述した メッセージ類の画面レイアウト図 中の 時分秒表示 を表示テンプレートとして、現在の "時" "分" "秒" が刻々と変化をしながら表示される。 デフォルトの表示は 24 時間表示で、12 時間表示のときに表示がされる AM / PM の位置は空白となる。
          時分秒表示:     [ AM 11:59:59 ]
          
    • この 24 時間表示と 12 時間表示は、UP / TMODE(TIME MODE)スイッチ を押すことによって、時分秒表示(オートモードを含む)のときにはいつでも切り替えることができ、押すごとに両者を切り替える、トグルスイッチになっている。

    • 次に、24 時間表示と 12 時間表示の対応を示すが、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
          
    • また、毎正時プラス・マイナス5分以内のときに、ZERO スイッチ を押すと、現在時刻の "分" と "秒" を "00 分 00 秒" に設定し直して、リアルタイムクロックモジュール RTC に書き込まれ、表示もその "00 分 00 秒" から再開始をする。 ただし、毎正時プラス・マイナス5分以外のときには、ZERO スイッチを押しても無視をされる。

    • この時計プログラムには、毎時の時報音を鳴らす機能も搭載されているが、SIGNAL スイッチ によって、時報音のブザー音出力を、押すごとに有効/無効にトグル設定をする。 このときの設定状態は視覚的な表示がないため、有効になったときには "ピピピッ" 音を、無効になったときには "ブー" 音を、 それぞれ鳴らして知らせる。

    • なお、誤操作を避けるために EXIT スイッチ3 秒以上長押し をすると、再び メインルーチンに戻る ことができる。(ちなみに、誤操作とは自分の意思に反して、誤ってスイッチを押してしまったような場合)


 この項の時計表示プログラムでは、年月日(曜日)の表示、温度、湿度の表示、時刻等の設定変更をする 各機能を分離して、専ら 時分秒の時計表示 だけにに専念をしているため、その分プログラムもシンプルになっています。
      ;==========================================================================
      ;		rtsw_sel = 1: 時分秒の表示処理
      ;==========================================================================
      
      		; int_flg:	外部(INT)割り込みフラグ
      				; bit7:   RTC DS1307 からデータ読み出し無効フラグ
      				; bit3:   DHT11 からデータ読み出し要求フラグ
      				; bit2-1: 4 秒カウンタ
      				; bit0:   RTC DS1307 からデータ読み出し要求フラグ
      
       		; disp_flg:	LED 表示フラグ
      				; bit7: 年表示フラグ
      				; bit6: 月表示フラグ
      				; bit5: 日表示フラグ
      			     ;>	; bit4: 時表示フラグ
      			     ;>	; bit3: 分表示フラグ
      			     ;>	; bit2: 秒表示フラグ
      				; bit1: 温度表示フラグ
      				; bit0: 湿度表示フラグ
      
       		; mode_flg:	年月日,時分秒の設定モードフラグ
      				; bit7: 設定モードフラグ
      				; bit6: ブリンク反転フラグ (タイマー0割り込みで設定)
      				; bit5: ブリンク変更フラグ (     〃      )
      				; bit0: メイン処理フラグ
      
       		; time_flg:	時間表示フラグ
      				; bit1: 0:24H/1:12H
      				; bit0: 0:am/1:pm
      
      ;--------------------------------------------------------------------------
      
      time_disp_process	;-------
      
      		clrf	mode_flg		;bit7: 設定モードフラグ = OFF
      		clrf	exit_flg		;エグジットフラグ = OFF
      		movlw	b'00011100'		;時,分,秒 すべて
      		movwf	disp_flg		;LED 表示フラグ = ON
      		bsf	int_flg,0		;RTC DS1307 からデータ読み出し要求フラグ = ON
      
      timdsp01	call	time_part_display	;時分秒を LED モジュールに表示
      		call	time_signal		;時報音出力コントロール	
      		call	alarm_sound		;アラームフラグの監視とアラーム音出力
      
      		; 各スイッチの入力処理
      
      		btfss	PORTA,_swMOD		;TMODE スイッチ = ON か?
      		call	tm_mode_switch		;Yes
      
      		btfss	PORTA,_swZER		;ZERO スイッチ = ON か?
      		call	tm_zero_switch		;Yes
      
      		btfss	PORTC,_swSIG		;SIGNAL スイッチ = ON か?
      		call	tm_signal_switch	;Yes
      
      		btfss	PORTA,_swEXT		;EXIT スイッチ = ON か?
      		call	tm_exit_switch		;Yes
      
      		movf	rtsw_sel,W
      		sublw	4
      		btfss	STATUS,Z		;オートモード rtsw_sel = 4 か?
      		goto	timdsp03		;No
      
      		btfsc	exit_flg,7		;処理打ち切りフラグ = ON か?
      		goto	timdsp02		;Yes
      
      		movlw	h'10'			;
      		subwf	bcd_ss,W		;秒 BCD カウンタ
      		btfsc	STATUS,Z		;bcd_ss = h'10' か?
      		goto	timdsp02		;Yes
      
      		movlw	h'40'			;
      		subwf	bcd_ss,W		;秒 BCD カウンタ
      		btfss	STATUS,Z		;bcd_ss = h'40' か?
      		goto	timdsp01		;No
      
      timdsp02	return
      
      timdsp03	btfss	exit_flg,7		;処理打ち切りフラグ = ON か?
      		goto	timdsp01		;No
      
      		bcf	exit_flg,7		;処理打ち切りフラグ = OFF
      		goto	main			;メイン処理に戻る
      
      ;--------------------------------------------------------------------------
      
      time_part_display	;-------
      
      		btfsc	int_flg,7		;RTC DS1307 からデータ読み出し無効フラグ = ON か?
      		goto	tmpdsp01		;Yes
      
      		btfss	int_flg,0		;RTC DS1307 からデータ読み出し要求フラグ = ON か?
      		goto	tmpdsp02		;No
      
      		Rtc_Read_M  seconds,7		;RTC DS1307 から年月日、時分秒を読み出し
      		call	buffer_to_counter
      		bcf	int_flg,0		;RTC DS1307 からデータ読み出し要求フラグ = OFF
      
      		bsf	disp_flg,2		;bit2: 秒表示フラグ = ON
      
      		movf	bcd_ss,W		;秒 BCD カウンタ
      		btfss	STATUS,Z		;bcd_ss = 0 か?
      		goto	tmpdsp01		;No
      
      		bsf	disp_flg,3		;bit3: 分表示フラグ = ON
      
      		movf	bcd_mm,W		;分 BCD カウンタ
      		btfss	STATUS,Z		;bcd_mm = 0 か?
      		goto	tmpdsp01		;No
      
      		bsf	disp_flg,4		;bit4: 時表示フラグ = ON
      
      tmpdsp01	movlp	high time_display
      		call	time_display		;時分秒を LED モジュールに表示
      		movlp	high $
      		bcf	int_flg,7		;RTC DS1307 からデータ読み出し無効フラグ = OFF
      
      tmpdsp02	return
      
      ;--------------------------------------------------------------------------
      ;		EXIT スイッチの処理 ... 時分秒の表示から CALL
      ;--------------------------------------------------------------------------
      
      tm_exit_switch		;-------
      
      		call	wait_30ms		;チャッタリング吸収
      		btfsc	PORTA,_swEXT		;再度 EXIT スイッチ = ON か?
      		goto	tmexsw02		;No
      
      		movlw	60			;50ms x 60 = 3 秒間の監視
      		movwf	lp_cnt4			;ループカウンタ
      
      tmexsw01	call	wait_50ms		;50m 秒 ウエイト
      		call	time_part_display	;時分秒を LED モジュールに表示
      
      		btfsc	PORTA,_swEXT		;EXIT スイッチ = OFF か?
      		goto	tmexsw02		;Yes
      
      		decfsz	lp_cnt4,F		;ループカウンタ - 1 = 0 か?
      		goto	tmexsw01		;No
      
      		bsf	exit_flg,7		;処理打ち切りフラグ = ON
      
      tmexsw02	btfss	PORTA,_swEXT		;EXIT スイッチ = OFF か?
      		goto	$ - 1			;No
      
      		call	wait_30ms		;チャッタリング吸収
      		return
      
      ;--------------------------------------------------------------------------
      
      			:
      			:
      
      ( この後、TIME MODE, ZERO, SIGNAL の各スイッチの処理サブルーチンが続くが省略 )
      
      			:
      			:
      
 上のリストに示す time_disp_process が、この項の 時計表示プログラム としてのメインとなる、(サブメイン)ルーチンで、メイン、プログラム処理の振り分け で示したリストのメイン・ルーチン( main )からは、 GOTO ジャンプをしてくるため、そのメイン・ルーチン( main )に戻るときにも、同様に GOTO でジャンプをしています。

 ただし、この time_disp_process は、4. オートモードの表示 で示す(サブメイン)ルーチン( auto_disp_process )からも、サブルーチンとして CALL をされるため、そのときには RETURN で(サブメイン)ルーチン( auto_disp_process )に戻ります。

 このように、time_disp_process は、メイン・ルーチン( main )からも、(サブメイン)ルーチン( auto_disp_process )からも呼ばれるため、両者に対応ができるような作りの構造になっています。 これは以降に説明をする、 2. 年月日(曜日)の表示date_disp_process3. 温度、湿度の表示temp_humi_disp_process でも同様です。

 また、各スイッチの中で EXIT スイッチ処理ルーチン( tm_exit_switch )だけを取り上げて、ここに示したのには訳があります。 それは EXIT スイッチは 長押し をするように想定をしていますが、何の考慮もないと、その押している間は "秒" の表示の更新が止まってしまって異様な感じを受けます。

 それを避けるために、EXIT スイッチ処理ルーチンtm_exit_switch )内にも、時分秒を表示するサブルーチン time_part_display を挿入して、 "秒" の更新が止まらないようにしています。 ただし、3 秒の経過後は逆に更新を止めて、 これ以上 長押し する必要はない、とユーザにサインを送っています。

 なお、本項の 機能の概要 の冒頭で、"・・・ 図 中の 時分秒表示 を表示テンプレートとして" という部分がありますが、12. 年月日、時分秒の設定 の "機能の概要" の下に説明があるので参考にしてください。

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


 ◎ 2. 年月日(曜日)の表示


    機能の概要

    • メインルーチンから当プログラムにジャンプをしてくると、上述した メッセージ類の画面レイアウト図 中の 年月日表示 を表示テンプレートとして、現在の "年" "月" "日" と "曜日" が表示されるが、 毎深夜の零時に1度だけの更新で画面に動きがないため、当プログラムでは "曜日" をブリンク表示( 1 秒間 "点"、1 秒間 "滅" )させて、画面に動きを持たせている。(ちなみに、画面レイアウト図中の "年月日" は、図の作成日)
          年月日表示:     [ 22.02.06 SUN ]
          
    • 当プログラムが実行中にも時計機能は動作をしていて、毎時になると時報音を鳴らすため、SIGNAL スイッチ によって、時報音のブザー音出力を、押すごとに有効/無効にトグル設定をすることができる。 このときの設定状態は視覚的な表示がないため、有効になったときには "ピピピッ" 音を、無効になったときには "ブー" 音を、 それぞれ鳴らして知らせる。

    • なお、誤操作を避けるために EXIT スイッチ3 秒以上長押し をすると、再び メインルーチンに戻る ことができる。(ちなみに、誤操作とは自分の意思に反して、誤ってスイッチを押してしまったような場合)


 この項でも前項と同様にそれほど大きくはないので、年月日の表示プログラムを次に示しておきます。
      ;==========================================================================
      ;		rtsw_sel = 2: 年月日(曜日)の表示処理
      ;==========================================================================
      
      		; int_flg:	外部(INT)割り込みフラグ
      				; bit7:   RTC DS1307 からデータ読み出し無効フラグ
      				; bit3:   DHT11 からデータ読み出し要求フラグ
      				; bit2-1: 4 秒カウンタ
      				; bit0:   RTC DS1307 からデータ読み出し要求フラグ
      
       		; disp_flg:	LED 表示フラグ
      			     ;>	; bit7: 年表示フラグ
      			     ;>	; bit6: 月表示フラグ
      			     ;>	; bit5: 日表示フラグ
      				; bit4: 時表示フラグ
      				; bit3: 分表示フラグ
      				; bit2: 秒表示フラグ
      				; bit1: 温度表示フラグ
      				; bit0: 湿度表示フラグ
      
       		; mode_flg:	年月日,時分秒の設定モードフラグ
      				; bit7: 設定モードフラグ
      				; bit6: ブリンク反転フラグ (タイマー0割り込みで設定)
      				; bit5: ブリンク変更フラグ (     〃      )
      				; bit0: メイン処理フラグ
      
      ;--------------------------------------------------------------------------
      
      date_disp_process	;-------
      
      		clrf	mode_flg		;bit7: 設定モードフラグ = OFF
      		clrf	exit_flg		;エグジットフラグ = OFF
      		movlw	b'11100000'		;年,月,日 すべて
      		movwf	disp_flg		;LED 表示フラグ = ON
      		bsf	int_flg,0		;RTC DS1307 からデータ読み出し要求フラグ = ON
      
      datdsp01	call	date_part_display	;年月日を LED モジュールに表示
      		call	time_signal		;時報音出力コントロール	
      		call	alarm_sound		;アラームフラグの監視とアラーム音出力
      
      		; 各スイッチの入力処理
      
      		btfss	PORTC,_swSIG		;SIGNAL スイッチ = ON か?
      		call	dt_signal_switch	;Yes
      
      		btfss	PORTA,_swEXT		;EXIT スイッチ = ON か?
      		call	dt_exit_switch		;Yes
      
      		movf	rtsw_sel,W
      		sublw	4
      		btfss	STATUS,Z		;オートモード rtsw_sel = 4 か?
      		goto	datdsp03		;No
      
      		btfsc	exit_flg,7		;処理打ち切りフラグ = ON か?
      		goto	datdsp02		;Yes
      
      		movlw	h'25'			;
      		subwf	bcd_ss,W		;秒 BCD カウンタ
      		btfss	STATUS,C		;bcd_ss < h'25' か?
      		goto	datdsp01		;Yes
      
      datdsp02	return
      
      datdsp03	btfss	exit_flg,7		;処理打ち切りフラグ = ON か?
      		goto	datdsp01		;No
      
      		bcf	exit_flg,7		;処理打ち切りフラグ = OFF
      		goto	main			;メイン処理に戻る
      
      ;--------------------------------------------------------------------------
      
      date_part_display	;-------
      
      		btfss	int_flg,0		;RTC DS1307 からデータ読み出し要求フラグ = ON か?
      		goto	dtpdsp02		;No
      
      		Rtc_Read_M  seconds,7		;RTC DS1307 から年月日、時分秒を読み出し
      		call	buffer_to_counter
      		bcf	int_flg,0		;RTC DS1307 からデータ読み出し要求フラグ = OFF
      
      		bsf	disp_flg,5		;bit5: 日表示フラグ = ON
      
      		movf	bcd_ss,W		;秒 BCD カウンタ
      		iorwf	bcd_mm,W		;分 BCD カウンタ
      		iorwf	bcd_hh,W		;時 BCD カウンタ
      		btfss	STATUS,Z		;bcd_ss = bcd_mm = bcd_hh = 0 か?
      		goto	dtpdsp01		;No
      
      		movf	bcd_dd,W		;日 BCD カウンタ
      		sublw	1
      		btfss	STATUS,Z		;bcd_dd = 1 か?
      		goto	dtpdsp01		;No
      
      		bsf	disp_flg,6		;bit6: 月表示フラグ = ON
      
      		movf	bcd_nn,W		;月 BCD カウンタ
      		sublw	1
      		btfss	STATUS,Z		;bcd_nn = 1 か?
      		goto	dtpdsp01		;No
      
      		bsf	disp_flg,7		;bit7: 年表示フラグ = ON
      
      dtpdsp01	movlp	high date_display
      		call	date_display		;年月日を LED モジュールに表示
      		movlp	high $
      		bcf	int_flg,7		;RTC DS1307 からデータ読み出し無効フラグ = OFF
      
      dtpdsp02	return
      
 なお、前項の項末で述べたことは、本項においてもすべてが当てはまるので、そのように読み替えてください。

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


 ◎ 3. 温度、湿度の表示

 本機では、本機で使用した各種モジュールについて の項の、デジタル温度湿度センサー( DHT11 )モジュール で述べたように、デジタル温度湿度センサーモジュールには、DHT11 版モジュール を使用しましたが、 "189. ドットマトリクス 8 x 8 LED 表示時計 II ( MAX7219 版)" 等で使用した DHT22 版モジュール に比べると、精度は劣っているようです。 本機は "お遊び要素" が大きいため DHT11 版モジュール で良いと判断をしたのですが、 より高精度を望まれる方には DHT22 版モジュール をお勧めします。


    機能の概要

    • メインルーチンから当プログラムにジャンプをしてくると、上述した メッセージ類の画面レイアウト図 中の 温度湿度表示 を表示テンプレートとして、現在の "温度" と "湿度" が表示される。
          温度湿度表示:     [ xx.x℃ xx.x% ]
          
    • 本機では、デジタル温度湿度センサーモジュール( DHT11 )と PIC との間を、たった1本だけのデータ線によるシリアル通信によって情報を得ているが、通信中にエラーが起こった場合には、次のようなエラーメッセージがマトリクス LED に表示されるとともに、 "ブッブー" ブザー音が出力される。
          タイムアウトエラー:  [ Error: DHT11 TO ]	チェックサム(CRC)エラー:  [ Error: DHT11 PE ]
          
    • 左の例では通信中にタイムアウトエラーが起こった場合で、データ線上の "high" または "low" 電圧の続く時間が、仕様で決められた時間をオーバーした場合に発生する。 また、右の例ではチェックサム(CRC)エラーが起こった場合で、 DHT11 からは1度の通信で、湿度情報が 16 ビット、温度情報が 16 ビット、チェックサムが 8 ビットの、計 40 ビットが送られてくるが、それらの情報でパリティチェックを行った結果、エラーが起こった場合に発生する。

    • 当プログラムが実行中にも時計機能は動作をしていて、毎時になると時報音を鳴らすため、SIGNAL スイッチによって、時報音のブザー音出力を、押すごとに有効/無効にトグル設定をすることができる。 このときの設定状態は視覚的な表示がないため、有効になったときには "ピピピッ" 音を、無効になったときには "ブー" 音を、 それぞれ鳴らして知らせる。

    • なお、誤操作を避けるために EXIT スイッチ3 秒以上長押し をすると、再び メインルーチンに戻る ことができる。(ちなみに、誤操作とは自分の意思に反して、誤ってスイッチを押してしまったような場合)


 この項でも前項、前々項と同様にそれほど大きくはないので、温度、湿度の表示プログラムを次に示しておきます。
      ;==========================================================================
      ;		rtsw_sel = 3: 温度,湿度の表示処理
      ;==========================================================================
      
      		; int_flg:	外部(INT)割り込みフラグ
      				; bit7:   RTC DS1307 からデータ読み出し無効フラグ
      				; bit3:   DHT11 からデータ読み出し要求フラグ
      				; bit2-1: 4 秒カウンタ
      				; bit0:   RTC DS1307 からデータ読み出し要求フラグ
      
       		; disp_flg:	LED 表示フラグ
      				; bit7: 年表示フラグ
      				; bit6: 月表示フラグ
      				; bit5: 日表示フラグ
      				; bit4: 時表示フラグ
      				; bit3: 分表示フラグ
      				; bit2: 秒表示フラグ
      			     ;>	; bit1: 温度表示フラグ
      			     ;>	; bit0: 湿度表示フラグ
      
      ;--------------------------------------------------------------------------
      
      temp_humi_disp_process	;-------
      
      		clrf	exit_flg		;エグジットフラグ = OFF
      		movlw	b'00000011'		;温度,湿度 すべて
      		movwf	disp_flg		;LED 表示フラグ = ON
      		movlw	b'00001001'		;
      		movwf	int_flg			;DHT11 からデータ読み出し要求フラグ = ON
      
      temdsp01	call	temp_humi_part_display	;温度,湿度を LED モジュールに表示
      		call	time_signal		;時報音出力コントロール	
      		call	alarm_sound		;アラームフラグの監視とアラーム音出力
      
      		; 各スイッチの入力処理
      
      		btfss	PORTC,_swSIG		;SIGNAL スイッチ = ON か?
      		call	th_signal_switch	;Yes
      
      		btfss	PORTA,_swEXT		;EXIT スイッチ = ON か?
      		call	th_exit_switch		;Yes
      
      		movf	rtsw_sel,W
      		sublw	4
      		btfss	STATUS,Z		;オートモード rtsw_sel = 4 か?
      		goto	temdsp03		;No
      
      		btfsc	exit_flg,7		;処理打ち切りフラグ = ON か?
      		goto	temdsp02		;Yes
      
      		movlw	h'55'			;
      		subwf	bcd_ss,W		;秒 BCD カウンタ
      		btfss	STATUS,C		;bcd_ss < h'55' か?
      		goto	temdsp01		;Yes
      
      temdsp02	return
      
      temdsp03	btfss	exit_flg,7		;処理打ち切りフラグ = ON か?
      		goto	temdsp01		;No
      
      		bcf	exit_flg,7		;処理打ち切りフラグ = OFF
      		goto	main			;メイン処理に戻る
      
      ;--------------------------------------------------------------------------
      
      temp_humi_part_display	;-------
      
      		btfss	int_flg,0		;RTC DS1307 からデータ読み出し要求フラグ = ON か?
      		goto	thpdsp01		;No
      
      		Rtc_Read_M  seconds,7		;RTC DS1307 から年月日、時分秒を読み出し
      		call	buffer_to_counter
      		bcf	int_flg,0		;RTC DS1307 からデータ読み出し要求フラグ = OFF
      
      thpdsp01	btfss	int_flg,3		;DHT11 からデータ読み出し要求フラグ = ON(4秒周期)か?
      		goto	thpdsp04		;No
      
      thpdsp02	call	dht11_measure		;DHT11 から温度、湿度を読み出し
      		movlw	h'c0'
      		andwf	time_cnt,W
      		btfsc	STATUS,Z		;読み出しエラーがあった か?
      		goto	thpdsp03		;No
      
      		movlp	high dht11_err_display
      		call	dht11_err_display	;読み出しエラーの表示
      		movlp	high $
      		call	buzzer_bubuu		;"ブッブー" ブザー音出力
      		bcf	int_flg,3		;DHT11からデータ読み出し要求フラグ = OFF
      
      		btfss	int_flg,3		;DHT11 からデータ読み出し要求フラグ = ON(4秒周期) か?
      		goto	$ - 1			;No
      		goto	thpdsp02
      
      thpdsp03	bsf	disp_flg,1		;bit1: 温度表示フラグ = ON
      		bsf	disp_flg,0		;bit0: 湿度表示フラグ = ON
      
      		movlp	high temp_humi_display
      		call	temp_humi_display	;温度,湿度を LED モジュールに表示
      		movlp	high $
      		bcf	int_flg,3		;DHT11 からデータ読み出し要求フラグ = OFF
      
      thpdsp04	return
      
 なお、前々項の項末で述べたことは、本項においてもすべてが当てはまるので、そのように読み替えてください。

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


 ◎ 4. オートモードの表示


    機能の概要

    • オートモードでは、上3項で説明をした、時分秒、年月日、温度湿度の表示を、毎分ごとの 60 秒を次のように分割をして、それぞれを割り当て表示をする。
           (____: 時間表示、NNNN: 年月日表示、OOOO: 温度,湿度表示)
          
          00        10        20        30        40        50        00
          .    .    .    .    .    .    .    .    .    .    .    .    .
          __________NNNNNNNNNNNNNNN_______________OOOOOOOOOOOOOOO_____
          
    • メインルーチンから当プログラムにジャンプをしてきたときには、そのときの現在時間(秒)によって、3つの内のどの表示をするかが決められる。

    • 当プログラム(実際には3つの内のどれか)が実行中には、時計機能が動作をしてるので、毎時になると時報音を鳴らすため、SIGNAL スイッチによって、時報音のブザー音出力を、押すごとに有効/無効にトグル設定をすることができる。 このときの設定状態は視覚的な表示がないため、有効になったときには "ピピピッ" 音を、無効になったときには "ブー" 音を、 それぞれ鳴らして知らせる。

    • なお、誤操作を避けるために EXIT スイッチ3 秒以上長押し をすると、再び メインルーチンに戻る ことができる。(ちなみに、誤操作とは自分の意思に反して、誤ってスイッチを押してしまったような場合)


 このオートモード制御をするためのプログラムは、以下に示すように至って簡単なものとなっています。
      ;==========================================================================
      ;		rtsw_sel = 4: オートモードの表示処理
      ;==========================================================================
      
      ;		 (____: 時間表示、NNNN: 年月日表示、OOOO: 温度,湿度表示)
      
      ;		00        10        20        30        40        50        00
      ;		.    .    .    .    .    .    .    .    .    .    .    .    .
      ;		__________NNNNNNNNNNNNNNN_______________OOOOOOOOOOOOOOO_____
      
      ;--------------------------------------------------------------------------
      
      auto_disp_process	;-------
      
      autdsp01	movlw	h'10'			;
      		subwf	bcd_ss,W		;秒 BCD カウンタ
      		btfss	STATUS,C		;bcd_ss < h'10' か?
      		goto	autdsp02		;Yes
      
      		movlw	h'25'			;
      		subwf	bcd_ss,W		;秒 BCD カウンタ
      		btfss	STATUS,C		;bcd_ss < h'25' か?
      		goto	autdsp03		;Yes
      
      		movlw	h'40'			;
      		subwf	bcd_ss,W		;秒 BCD カウンタ
      		btfss	STATUS,C		;bcd_ss < h'40' か?
      		goto	autdsp02		;Yes
      
      		movlw	h'55'			;
      		subwf	bcd_ss,W		;秒 BCD カウンタ
      		btfss	STATUS,C		;bcd_ss < h'55' か?
      		goto	autdsp04		;Yes
      
      autdsp02	call	time_disp_process	;時分秒の表示
      		goto	autdsp05
      
      autdsp03	call	date_disp_process	;年月日の表示
      		goto	autdsp05
      
      autdsp04	call	temp_humi_disp_process 	;温度,湿度の表示
      
      autdsp05	btfss	exit_flg,7		;処理打ち切りフラグ = ON か?
      		goto	autdsp01		;No
      
      		bcf	exit_flg,7		;処理打ち切りフラグ = OFF
      		goto	main			;メイン処理に戻る
      
      
 なお、この項の "4. オートモードの表示" プログラムでは、time_disp_process、date_disp_process、temp_humi_disp_process の各プログラムを、"1. 時分秒の時計表示" の 項末 で述べたように、 サブルーチンとして CALL をしている点に注意をしてください。

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


 ◎ 5. 標準文字フォントのデモ表示(電光掲示板風)

 初めに、本機で使用した "標準文字フォント" は、"秋月電子通商" で購入した "I2C接続小型キャラクタLCDモジュール: AQM0802A-RN-GBW" のデータシートから抜粋した次図を基にして、私が作成した標準文字フォントテーブル: "Font5x7s.tbl" です。 "標準文字フォント" とは私が勝手に付けた名前です。

 次図のように、ユーザが作成登録する文字 h'00' 〜 h'05' の 6 文字を除いて、250 のすべての文字フォントが揃っているので、本機に採用することに決めました。


 次に述べる機能の概要は、この項の 5. 標準文字フォント だけでなく、表示される文字フォントの種類、表示数が異なるだけで、6. プロポーショナル文字フォント7. ひらがな文字フォント の、 各デモ表示プログラムのすべてに当てはまり同様に機能をします。


    機能の概要

    • この項で紹介するプログラムは、上述の標準文字フォントの全 250 文字を使用して、電光掲示板のように文字が1ドットずつ横方向に流れながら表示をするプログラムで、左右どちらの方向にも自由に変更をすることが可能なように、横スクロールの実現をさせている。

    • スタートは左方向への流れ(スクロール)から始まり、右端からフォントテーブル先頭のコード h'06' 文字(↓)が顔を出し始め、1ドットずつ左方向に移動しながら続いて2番目のコード h'07' 文字(→)が現れ、以降同様に、 文字コードが1ずつインクリメントされた文字が現れながら、次々と1ドットずつ左方向に移動しながら表示されて行く。

    • やがて、最後のコード h'ff' 文字が現れると、その後はないので1ドットずつの空白が続く。 そして、コード h'ff' 文字が左端に消え去ったとき、すなわち、すべてが空白で埋まった後は、次には右方向への流れ(スクロール)に変わって、 今消え去った最後のコード h'ff' 文字が再び左端から現れ始める。

    • そして、1ドットずつ右方向に移動しながら続いて最後から2番目のコード h'fe' 文字(√)が現れ、以降同様に、文字コードが1ずつデクリメントされた文字が現れながら、次々と1ドットずつ右方向に移動しながら表示されて行く。

    • やがて、先頭のコード h'06' 文字(↓)が現れると、その前はないので1ドットずつの空白が続く。 そして、コード h'06' 文字(↓)が右端に消え去ったとき、すなわち、すべてが空白で埋まった後は、次には再び左方向への流れ(スクロール)に変わって、 今消え去った先頭のコード h'06' 文字(↓)が再び右端から現れ始める。

    • これまでの動作中にユーザが何の指示も出さなければ、以上の動作を延々と繰り返すことになるが、この間にユーザが LEFT スイッチ、または EXIT / RIGHT スイッチ を押すことによって、 ユーザの意思で 流れる(スクロール)方向をいつでも自由に変更 することができる。

    • また、UP スイッチ、または DOWN スイッチ を押すと、流れる(スクロール)速度(1ドットずつ移動する時間(mS))を変更 することができる。 本機では 10mS 〜 1,500mS まで 11 段階で変更ができ、 UP スイッチを押すごとに速くなり、DOWN スイッチを押すごとに遅くなる。 デフォルトは 200mS の設定で、この 11 段階の内、変更をしても どの段階になったのか表示機能がないため、上限値、下限値、デフォルト値のときには "ピッ" ブザー音を鳴らして、 音でユーザに知らせている。

    • 最後に、この項のプログラムを終わらせる場合は、EXIT / RIGHT スイッチ3 秒以上 "長押し" する ことによって、プログラムを終了させ、再び メインルーチンに戻る ことができる。 (この場合の "長押し" は、同一スイッチで EXIT / RIGHT の区別をさせるため)


 次の図は、この項の 5. 標準文字フォントのデモ表示6. プロポーショナル文字フォントのデモ表示7. ひらがな文字フォントのデモ表示 の、各プログラムを実行させるときに必要となる各メモリを、イメージ的に表したものです。


 黄色部分 は3者の各プログラムで必要となる、各フォントテーブルが格納されているプログラムメモリで、合計 2,580 byte(ワード)にも及ぶ大きなテーブルデータとなっています。 本機で使用した PIC16F1709 では、PIC16F1 ファミリーのアーキテクチャ で述べたように、4 ページ(2K x 4 = 8K ワード)= 2,048 x 4 = 8,192 ワードのプログラムメモリが存在していますが、その内の何と 30 %以上を占め、 これらのテーブルデータのために、プログラムエリアが極端に圧迫されてしまっています。

 薄緑色部分 はデータメモリ上に設けた バッファエリア( fd_buff )で全 64 バイトあり、それらの各バイトが メッセージ類の画面レイアウトとデータ変換および LED 表示 で述べた、画面レイアウト図 の中の小さな1マスに対応をしています。

 しかし、これはフォントデータをバッファエリア( fd_buff )に転送した直後の縦ビット並びデータのときの話で、マトリクス LED を表示させるためには、8 バイトごと(マトリクス LED 1個分)の縦ビット並びデータを横ビット並びデータに変換をした後で、 MAX7219 にそれらのデータを送ります。 そのデータ変換の過程で使用されるのが、薄赤色部分ワークエリア( work )です。

 上述の 画面レイアウト図 によって、それらの各メッセージを表示させていた頃の初期のプログラムでは、プログラムメモリ上のフォントデータを、バッファエリア( fd_buff )に直接転送をしていました。 それはそれでほとんど問題はないのですが、この項の標準文字フォントのデモ表示プログラムを作り出した頃にも、その延長線上で同じ手法で始めたのですが、結論から先に言うと失敗でした。

 上述のように、フォントデータ(文字間の空白バイトも含めて)をバッファエリア( fd_buff )に 64 バイト転送した直後は縦ビット並びデータですが、その後すぐに、横ビット並びデータに変換をしてマトリクス LED を表示させた後は、 バッファエリア( fd_buff )上の 64 バイトデータは不要となり、次に1バイトずらした縦ビット並びのフォントデータを、・・・ を繰り返して行くのですが、この1バイトずらすことと文字間の空白バイトを含める、というところに問題が生じてきて、 ここで説明をするほど容易ではないのです。 頭の中がぐるぐるになって訳が分からなくなってくるのです。

 それでも数日間は考えてみたのですが、やはりうまくは行きませんでした。 そこで発想の転換です。 フォントデータを直接バッファエリア( fd_buff )に転送するから頭の中がぐるぐるになってしまう訳で、PIC16F1709 にはたくさんのデータメモリ(RAM)が存在しています。 それらを有効に使いましょう。

 という訳で、フォントテーブルのすべてのデータを文字間の空白バイトとともに、予め、バッファエリア( fd_buff )とは 別のリニアバッファエリア( linear_buff )へ転送しておいて(このときはすべてが縦ビット並びデータ)、 その別のリニアバッファエリア( linear_buff )から必要に応じて 64 バイトずつを、バッファエリア( fd_buff )に転送する、という方法です。

 この別の リニアバッファエリア( linear_buff )というのが、上図の 水色部分 で、エリアのすべてが連続的に使用できないとプログラムが面倒になるため、BANK2 〜 12 のデータメモリをリニアデータメモリとして使用しています。 (サイズ: BANK2 〜 11 = 80 x 10 = 800 バイト、BANK12 = 48 バイト、したがって 800 + 48 = 848 バイト)

 しかし、上で予めすべてのデータを別のリニアバッファエリア( linear_buff )へ転送、と言いましたが、標準文字フォントの場合にはデータ量が多いので一度にすべて、とは行きません。 必要なサイズ =(1文字当たりのフォントデータ = 5 バイト)x(250 文字)+(文字間の空白バイト = 250 - 1 = 249 バイト)= 1,499 バイト、他に文字コード h'06' 〜 h'ff' のフォントデータの前後に 64 バイトずつの空白バイトも必要です。 したがって、合計 = 1,499 + 64 x 2 = 1,627 バイト にも及びます。

 リニアデータメモリ上のリニアバッファエリア( linear_buff )= 848 バイトには到底入り切りません。 そこで、この項の 5. 標準文字フォントのデモ表示プログラムでは、3分割をしてプログラムを実行させました。 その分割の様子が次の図ですが、図では文字コードで表しています。(2分割ではダブりのデータを含めるとやはり入り切らない)

 分割をしたそれぞれのデータ群を切り替えるときは、1ドットでも狂わないように、シームレスにデータスクロールが行われるようにしなくてはなりません。


 上図をもう少しリアル? に表したものが次の関係図で、これはプログラムに書き込んであるコメントを抜粋したものです。
      
      ;		先頭文字コード                       右限界文字コード                   終了文字コード
      ;		  |                                    |                                           |
      ;	          006   007   008                087   088   089   090                 097   098   099
      ;	 (64+1個) h'06' h'07' h'08'  ..(省略)..  h'57 'h'58' h'59' h'5a'  ..(省略)..  h'61' h'62' h'63' 
      ;	_ ...... _xxxxx_xxxxx_xxxxx_ .......... _xxxxx_xxxxx_xxxxx_xxxxx_ .......... _xxxxx_xxxxx_xxxxx_
      ;	|                                                    |
      ;     先頭=左限界アドレス                                  右限界アドレス
      ;    (h'2000' + 80 * 2)
      ;				x: 1個 1バイト,     _: h'00'コード(文字間の区切り)
      
      
      ;     先頭文字コード              左限界文字コード    右限界文字コード              終了文字コード	
      ;	|                            |                            |                            |
      ;	078   079              088   089   090              171   172   173              182   183
      ;	h'4e' h'4f'  .(省略).  h'58' h'59' h'5a'  .(省略).  h'ab' h'ac' h'ad'  .(省略).  h'b6' h'b7'
      ;	xxxxx_xxxxx_ ........ _xxxxx_xxxxx_xxxxx_ ........ _xxxxx_xxxxx_xxxxx_ ........ _xxxxx_xxxxx_ 
      ;	|                            |                                  |
      ;     先頭アドレス                左限界アドレス              右限界アドレス
      ;    (h'2000' + 80 * 2)
      ;				x: 1個 1バイト,     _: h'00'コード(文字間の区切り)
      
      
      ;     先頭文字コード                  左限界文字コード                       終了文字コード	
      ;	|                                          |                                    |
      ;	162   163   164                171   172   173   174                253   254   255
      ;	h'a2' h'a3' h'a4'  ..(省略)..  h'ab' h'ac' h'ad' h'ae'  ..(省略)..  h'fd' h'fe' h'ff' (1+64個)
      ;	xxxxx_xxxxx_xxxxx_ .......... _xxxxx_xxxxx_xxxxx_xxxxx_ .......... _xxxxx_xxxxx_xxxxx_ ...... _
      ;	|                                          |                                          |
      ;     先頭アドレス                       左限界アドレス                             右限界アドレス
      ;    (h'2000' + 80 * 2)
      ;				x: 1個 1バイト,     _: h'00'コード(文字間の区切り)
      
      
 この図では、左限界アドレス、右限界アドレスが最も重要で、左スクロールのときには右限界アドレスを、右スクロールのときには左限界アドレスを、それぞれ常にプログラムで監視をしています。

 左スクロールのときで 1/3 データ群を実行中 に、右限界アドレスを検出すると、直ちに 2/3 データ群に切り替えます。 左スクロールのときで 2/3 データ群を実行中 に、右限界アドレスを検出すると、直ちに 3/3 データ群に切り替えます。 左スクロールのときで 3/3 データ群を実行中 に、右限界アドレスを検出すると、直ちに 右スクロールに方向転換を行います。

 右スクロールのときで 3/3 データ群を実行中 に、左限界アドレスを検出すると、直ちに 2/3 データ群に切り替えます。 右スクロールのときで 2/3 データ群を実行中 に、左限界アドレスを検出すると、直ちに 1/3 データ群に切り替えます。 右スクロールのときで 1/3 データ群を実行中 に、左限界アドレスを検出すると、直ちに 左スクロールに方向転換を行います。

 以上の分割をするという手法を使えば、たとえもっと多量なフォントデータがあったとしても、同様に扱うことが可能になります。 また、この手法は他のプログラムにも、応用が利くものと思います。

 上の 機能の概要 の中で、左、または右に1ドットずつスクロールしながら表示動作中に、LEFT スイッチ、EXIT / RIGHT スイッチ、UP スイッチ、DOWN スイッチの内の、どれかスイッチが押されたときの それぞれの機能を説明しましたが、この項や次項以降を含めた3者のデモ表示プログラムでは、いつ何時でもスイッチが押されたその瞬間を検知できるように、各スイッチが収容されているポート A(回路図 を参照)の 状態変化割り込み を使用しています。 そして、ドットマトリクス LED 8桁分の表示を済ませた後で、改めて、検知したスイッチそれぞれの機能を実行しています。

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


 ◎ 6. プロポーショナル文字フォントのデモ表示(電光掲示板風)

 初めに、本機で使用した "プロポーショナル文字フォント" は、サイト "基礎からの IoT 入門" の "MAX7219 とドットマトリクス LED による文字のスクロール表示" というページに掲載されていた、リスト中の文字フォント部分を利用させていただきました。 (そのページによると、オリジナルは "Google Code Archive / arudino-maxmatrix-library" の "arudino-maxmatrix-library - Example_Display_Characters.wiki" だということです。)

 リストは Arduino のスケッチなので、本機のプログラムで使用するのに都合が良いように、私が PIC アセンブラ用に書き換えた "Font5x7p.tbl" を使用しています。


    機能の概要

      この項の "機能の概要" は、前項 "5. 標準文字フォントのデモ表示" の 機能の概要 で述べたことと、表示される文字フォントの種類、表示数が異なるだけで、それ以外は同様なので前項を参照してください。


 プロポーショナル文字フォントでは、たとえば、アルファベットの 'A' のフォントは、
      		dt	4, b'01111110', b'00010001', b'00010001', b'01111110', b'00000000'	; A   h'41'
      
のように定義をしてありますが、これでは分かり辛いので1バイトずつに書き直してみると、
      		dt	4 
      		dt	b'01111110'
      		dt	b'00010001'
      		dt	b'00010001'
      		dt	b'01111110'
      		dt	b'00000000'	; A   h'41'
      
のようになります。 何となく 'A' の形が見えてきませんか? 右に 90 度寝てはいますが ...

 プロポーショナル文字フォントでは、文字の横幅が、前項の 標準文字フォント のように 5 ドット一定ではなく、各文字によって異なり本機で使用したフォントは 1 〜 5 ドットで作られています。 上の 'A' の例では有効な横幅は 4 ドットで、それが上の定義文では全 6 バイト中 1 バイト目に 4 と定義をしています。 それに続く 5 バイトがフォントデータですが、この 'A' の場合には 4 バイトが本当に有効となるデータで、 残りの 1 バイトはダミーデータとなっています。

 文字フォントテーブルを作る場合に、各文字の定義サイズを同一のバイト数にしておかないと、各文字のフォントを読み出すことができないため、'A' に限らず横幅が 5 ドットに満たない場合には、残りをダミーとしてサイズを合わせます。 記号 '!' のフォントでは
      		dt	1, b'01011111', b'00000000', b'00000000', b'00000000', b'00000000'	; !   h'21'
      
      		dt	1
      		dt	b'01011111'
      		dt	b'00000000'
      		dt	b'00000000'
      		dt	b'00000000'
      		dt	b'00000000'	; !   h'21'
      
のように定義がされていて、横幅は 1 バイトが有効なデータで、残りの 4 バイトはダミーデータとなっています。

 また、本機のプログラムで使用したプロポーショナル文字フォントは、先頭の文字コードが h'20'(スペース)から始まり h'7e'(~ チルダ)までの、全 95 文字しかフォントの定義がありません。 しかも、上述した有効な横幅ドット(バイト)だけをプログラムで取り出して使用するため、文字間の空白バイトを含めても、次図(再掲)の水色で示した、リニアデータメモリ上に設けたリニアバッファエリア( linear_buff )に、十分収めることができます。 (文字コード h'20'(スペース)の表示は、対象から外しました。)

 したがって、プログラミングも前項の標準文字フォントの場合のように、分割をする必要もないので面倒さがなく非常に楽になりました。


 前項の場合と同様に、プログラムに書き込んであるコメントを抜粋したものを表記してみると、次のようになります。
      
      ;	  (64個)  ! "   #     $    %     &     ' (    .(省略).  x     y    z   {   | }   ~     (64個)
      ;	_ ...... _x_xxx_xxxxx_xxxx_xxxxx_xxxxx_x_xxx_ ........ _xxxxx_xxxx_xxx_xxx_x_xxx_xxxx_ ...... _
      ;	|                                                                                    |
      ;     先頭アドレス                                                                         最終アドレス
      ;    (h'2000' + 80 * 2)		x: 1個 1バイト,  _: h'00'コード(文字間の区切り)
      
      
 この項のプロポーショナル文字フォントのデモ表示プログラムでは、左スクロールのとき に先頭アドレスを検出すると、直ちに 右スクロールに方向転換を行い、また、右スクロールのとき に最終アドレスを検出すると、 直ちに 左スクロールに方向転換を行っています。

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


 ◎ 7. ひらがな文字フォントのデモ表示(電光掲示板風)

 初めに、本機で使用した "ひらがな文字フォント" は、"恵梨沙フォント 1.00 ( elisa100.lzh )" * を解凍して得られる "恵梨沙フォント ELISA100.FNT" の、ほんの一部分だけを使用させていただきました。 ELISA100.FNT は、"第一水準と第二水準を合わせて 6,877 文字(ELISA100.DOC より)" を網羅した、8 x 8 ドットの日本語フォントのバイナリファイルで、そのサイズも 55,016 バイトもある膨大なフォントファイルのため、外付けの EEPROM などに収容しない限り、 そのすべての文字フォントを PIC で利用することはできません。  本機では、この 6,877 文字の内、数字とひらがな文字の 95 文字だけを抽出して、バイナリファイルからテキストファイルに、私が変換した "Font8x8k.tbl" を使用しています。 なお、この項のデモ表示プログラムでは、95 文字すべての表示をしようとすると、文字間の空白バイトも必要なので 上図 の水色で示した、リニアデータメモリ上に設けたリニアバッファエリア( linear_buff )には入り切りません。 そこで、プログラムでは数字の 0 〜 9 の 10 文字を表示の対象から外し、ひらがな文字だけを対象としました。


    機能の概要

      この項の "機能の概要" は、前々項 "5. 標準文字フォントのデモ表示" の 機能の概要 で述べたことと、表示される文字フォントの種類、表示数が異なるだけで、それ以外は同様なので前々項を参照してください。


 上述の メッセージ類の画面レイアウトとデータ変換および LED 表示 の項でも少し触れましたが、ひらがな文字フォントは次に示すように、横ビット並びバイト のフォントデータで定義がしてあります。
      font_a		retlw 	b'00010000'	;あ
      		retlw 	b'01111110'
      		retlw 	b'00010000'
      		retlw 	b'00010010'
      		retlw 	b'00111110'
      		retlw 	b'01010101'
      		retlw 	b'01011001'
      		retlw 	b'00110010'
      
 この 横ビット並びバイト のフォントデータを、上図 の水色で示したリニアデータメモリ上に設けたリニアバッファエリア( linear_buff )に、すべての文字フォントバイト(95 - 10 = 85 文字分)を転送するのですが、 そのときに、フォントバイトだけではなく、文字間の空白バイト、およびそれらの全バイトの前後に 64 バイトずつの空白バイトとともに転送をします。 そして次に、8 ビットずつの 横ビット並びバイト のフォントデータだけを、 順に 縦ビット並びバイト のフォントデータ(下図 (B) のデータ)に変換をして、リニアバッファエリア( linear_buff )上のバイトをすべて 縦ビット並びバイト にします。

 その 8 ビットずつの 横ビット並びバイト から 縦ビット並びバイト に変換をする、概念を表したものが 次図の @ です。


 このように、リニアデータメモリ上に設けたリニアバッファエリア( linear_buff )を、すべて 縦ビット並びバイト で満たしている過程の、実際のプログラムを次に示しておきます。
      ;==========================================================================
      ;	    rtsw_sel = 7:   8x8 かな文字フォントのデモ表示処理
      ;==========================================================================
      
      ;	リニアデータメモリのサイズ: 80 * 12 + 48 = 1,008 バイト(max) ... PIC16F1709 の場合
      ;	linear_buff のサイズ:       1,008 - 80 * 2 = 848 バイト(BANK2〜12を使用)
      
      ;#define 	linear_buff	(h'2000' + 80 * 2)	;バッファ先頭アドレス
      
      ;	  (64個)  あ       い       う       え        ..(省略)..  ゅ       ょ       っ       (1+64個)
      ;	_ ...... _xxxxxxxx_xxxxxxxx_xxxxxxxx_xxxxxxxx_ .......... _xxxxxxxx_xxxxxxxx_xxxxxxxx_ ...... _
      ;	|                                                                                     |
      ;     先頭アドレス                                                                          最終アドレス
      ;    (h'2000' + 80 * 2)		x: 1個 1バイト,  _: h'00'コード(文字間の区切り)
      
      font8x8kana_process	;-------
      
      		; 表示する全フォントデータをリニアデータメモリのバッファへ展開(事前処理)
      
      		movlw	high linear_buff	;フォントデータ格納先リニアメモリバッファの先頭アドレス
      		movwf	FSR0H			;
      		movlw	low linear_buff		;
      		movwf	FSR0L			;FSR0H:FSR0L 間接アドレスに設定
      
      		movlw	8 * 8			;ディスプレイの横幅ドット数
      		movlp	high zz_buff
      		call	zz_buff			;文字データに先行する h'00' コード
      		movlp	high $
      
      		movlw	high font_a		;ひらがな文字フォント(8 x 8)データテーブルの先頭アドレス
      		movwf	FSR1H			;
      		movlw	low font_a		;
      		movwf	FSR1L			;FSR1H:FSR1L 間接アドレスに設定
      
      		movlw	high (font8x8k_data_tbl_end - font_a)
      		movwf	addr_high
      		movlw	low (font8x8k_data_tbl_end - font_a)
      		movwf	addr_low		;ループカウンタ
      
      f8x8k01		movlw	8
      		movwf	lp_cnt5			;ループカウンタ5
      
      f8x8k02		moviw	FSR1++			;ひらがな文字フォント(8 x 8)データテーブルの読み出し
      		movwi	FSR0++			;リニアメモリバッファへ格納
      
      		decfsz	lp_cnt5,F		;ループカウンタ5 - 1 = 0 か?
      		goto	f8x8k02			;No
      
      		clrw				;h'00'コード(文字間の区切り)
      		movwi	FSR0++			;バッファへ格納
      
      		movlw	8			;ループカウンタ -8 更新
      		subwf	addr_low,F		;
      		clrw				;
      		subwfb	addr_high,F		;
      
      		movf	addr_low,W		;
      		iorwf	addr_high,W		;
      		btfss	STATUS,Z		;ループカウンタ addr_low = addr_high = 0 か?
      		goto	f8x8k01			;No
      
      		movf	FSR0H,W			;フォントデータ格納先リニアメモリバッファの最終アドレス
      		movwf	m_fsrxh			;
      		movf	FSR0L,W			;
      		movwf	m_fsrxl			;m_fsrxh:m_fsrxl に退避
      
      		movlw	8 * 8			;ディスプレイの横幅ドット数
      		movlp	high zz_buff
      		call	zz_buff			;文字データに後続する h'00' コード
      		movlp	high $
      
      		; フォントデータの横→縦ビットのデータ変換
      
      		movlw	high (linear_buff + 8 * 8) ;フォントデータ格納先リニアメモリバッファの先頭アドレス
      		movwf	m_fsr0h			   ;
      		movlw	low (linear_buff + 8 * 8)  ;
      		movwf	m_fsr0l			   ;退避
      
      f8x8k03		movlw	1			;1桁分
      		movlp	high horiz_verti_conv
      		call	horiz_verti_conv	;横→縦ビットのデータ変換
      		movlp	high $
      
      		movlw	1			;退避したリニアメモリバッファのアドレス +1 更新
      		addwf	m_fsr0l,F		;h'00'コード(文字間の区切り)の分
      		clrw				;
      		addwfc	m_fsr0h,F		;
      
      		movf	m_fsrxl,W
      		subwf	m_fsr0l,W
      		btfss	STATUS,Z		;m_fsr0l = m_fsrxl か?
      		goto	f8x8k03			;No
      
      		movf	m_fsrxh,W
      		subwf	m_fsr0h,W
      		btfss	STATUS,Z		;m_fsr0l = m_fsrxl か?
      		goto	f8x8k03			;No
      
      		; 以下、プロポーショナル文字フォントのデモ表示と同様なので共用する
      
      		goto	f8x8k04
      
 上のリストの下から 20 行目あたりのサブルーチン( horiz_verti_conv )が、実際に 8 ビットずつの 横ビット並びバイト から 縦ビット並びバイト に変換をするもので、その実体は、既に メッセージ類の画面レイアウトとデータ変換および LED 表示 の項で示した サブルーチン です。 ( horiz_verti_conv と verti_horiz_conv の2つの内、前者)

 上リストのプログラムは、表示する全フォントデータを、リニアデータメモリのバッファへ展開するのが目的の事前処理で、この後、この 縦ビット並びバイト で満たされている、リニアデータメモリ上のリニアバッファエリア( linear_buff )から、 1ドットずつスクロール表示をさせるために、そのスクロールに合わせた位置の 64 バイトの 縦ビット並びバイト を、データメモリ上のバッファエリア( fd_buff )へ転送をして、 その後、バッファエリア( fd_buff )上に移したデータを、文字間の空白バイトも含めて 8 ビットずつに区切って、再び 横ビット並びバイト に変換( verti_horiz_conv )後、MAX7219 に送り出してドットマトリクス LED 8 桁を表示させます。

 この再び 横ビット並びバイト に変換された結果のデータは、上図 (A) の 横ビット並びバイト のデータに戻すのではなく、(C) の 横ビット並びバイト のデータに変換がされることに注意をしてください。 1バイト内の各ビットの並びが左右入れ替わります。 したがって、サブルーチン( verti_horiz_conv )は、同図の B 変換ではなく、A 変換になります。

 なぜこのように変換をするのか? という疑問がおありの方は、"189. ドットマトリクス 8 x 8 LED 表示時計 II ( MAX7219 版)" の、事前に必要な予備知識 を参照していただければ解決すると思います。

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


 ◎ 8. さくらさくら/赤とんぼ 電子オルゴール

 ここで作成をしたプログラムは、別ページで紹介をしている "025. 電子オルゴール" のプログラムを応用したものとなっています。 その "025. 電子オルゴール" のプログラムは、随分懐かしい今から 17 年も以前に作成をしたもので、 私が PIC のプログラムを作るようになってかなり初期の頃のものです。

 今回、内容を全面的に見直し、使用するプログラムメモリのサイズの関係から、曲を "さくらさくら" と "赤とんぼ" の2曲に限定をして、PIC16F1 ファミリー用に作り変えました。 そして、従来のオルゴールと一番異なっているところは、 オルゴールのメロディーに合わせながら、ドットマトリクス LED にその曲の歌詞を1文字ずつ表示する、ディスプレイ機能を追加した点です。


    機能の概要

    • メインルーチンから当プログラムにジャンプをしてくると、選曲が、まず "さくらさくら" から始まり、曲の演奏に先立って、初めにドットマトリクス LED に、曲名のブリンク表示( 0.5 秒間 "点"、0.5 秒間 "滅" )が 5 秒間続く。

    • その後、曲データテーブルに書かれている各 指示ワード に従って、曲の演奏が始まるが、曲の各小節ごとの各音符に合わせて、曲の歌詞が1文字ずつドットマトリクス LED に、次々と表示がされて行く。

    • やがて、1曲目の "さくらさくら" の演奏が終了すると、自動的に、2曲目の "赤とんぼ" の選曲がされ、同様に曲名のブリンク表示と曲の演奏、および歌詞の表示がされる。 そして、2曲目が終了するとまた1曲目に戻り、 この繰り返しがユーザの指示があるまで延々と続く。

    • これらの演奏途中に、CHANGE スイッチ を押すことによって、現在の演奏でない方が選曲 され、上記と同様に曲名のブリンク表示から始まる。 CHANGE スイッチはトグルになっていて、押すごとに交互選曲を行う。

    • また、UP スイッチ、または DOWN スイッチ を押すと、演奏されている 曲のテンポを変更 することができる。 テンポのデフォルトは、曲データテーブルに書かれている 指示ワード のテンポに対して、プラスマイナス 0 であるが、 このプラスマイナスの間を 15 段階で、UP スイッチを押すごとに速くなり、DOWN スイッチを押すごとに遅くなる。

    • なお、この 15 段階の内、変更をしても どの段階になったのか表示機能がないため、変更したテンポが上限値、下限値、デフォルト値のときには "ピッ" ブザー音を鳴らして、 音でユーザに知らせる。(ただし、演奏中なので分かり辛いかも)

    • 演奏途中に一時的に演奏音を止めたいような場合は、SIGNAL スイッチ を押すことによって、演奏音を止める ことができるが、曲の進行はそのまま続くので、1文字ずつの歌詞の表示をはじめとして、 曲の選曲交代、曲名のブリンク表示などはそのまま続けられる。 この SIGNAL スイッチもトグルになっていて、押すごとに演奏音を 止める/出す を交互に繰り返す。

    • なお、EXIT スイッチ を押すことによって、再び メインルーチンに戻る ことができる。


 上述の、5. 標準文字フォント、6. プロポーショナル文字フォント、7. ひらがな文字フォントの、各デモ表示プログラムでは、どれも表示をする文字フォント数が多いために、各プログラムによっての違いはありますが、 リニアバッファエリア( linear_buff )をほとんど目いっぱいの状態で使用していました。

それに対して当プログラムでは文字スクロールのようなことはしないので、歌詞の表示のとき には バッファエリア( fd_buff )のサイズに等しい 64 バイトだけを使用しています。 また、プログラム名の表示のとき には リニアバッファエリア( linear_buff )はまったく使用しないで、バッファエリア( fd_buff )だけで処理をしています。

 当プログラムで使用するのはもちろん、7. ひらがな文字フォントのデモ表示 で述べた "ひらがな文字フォント" ですが、プログラムメモリ上に格納がされているこの ひらがな文字フォントテーブル( font8x8k_data_tbl )、 データメモリ上に設けた バッファエリア( fd_buff )、リニアデータメモリ上に設けた リニアバッファエリア( linear_buff )の、3者の関係がイメージし易いように、既に上掲をしてある次図をここにも再掲をしておきます。


 当プログラムにおいて、曲の演奏や1文字ずつの歌詞の表示など、その進行を決めているのはプログラム中に設定がしてある "曲データテーブル( sakura_sakura, aka_tonbo )" で、実際のテーブル例として sakura_sakura の部分リストを次に掲載します。
      ;		<<< さくらさくら >>> 曲データテーブル
      
      sakura_sakura
      		retlw	Ex3			;曲名の表示
      		retlw	Xx			;テンポ
      		retlw	40
      		retlw	Rep			;繰り返し
      		retlw	2
      
      		retlw	Ex1			;格納先バッファのクリア
      		retlw	Ex2			;歌詞1文字の表示
      		retlw	Ra			;さ
      		retlw	L4
      		retlw	Nn
      		retlw	Lx
      		retlw	Ex2			;歌詞1文字の表示
      		retlw	Ra			;く
      		retlw	L4
      		retlw	Ex2			;歌詞1文字の表示
      		retlw	Si			;ら
      		retlw	L2
      		retlw	Ex2			;歌詞1文字の表示
      		retlw	Ra			;さ
      		retlw	L4
      		retlw	Nn
      		retlw	Lx
      		retlw	Ex2			;歌詞1文字の表示
      		retlw	Ra			;く
      		retlw	L4
      		retlw	Ex2			;歌詞1文字の表示
      		retlw	Si			;ら
      		retlw	L2
      
      		retlw	Ex1			;格納先バッファのクリア
      		retlw	Ex2			;歌詞1文字の表示
      		retlw	Ra			;や
      		retlw	L4
      		retlw	Ex2			;歌詞1文字の表示
      		retlw	Si			;よ
      		retlw	L4
      		retlw	Ex2			;歌詞1文字の表示
      		retlw	Do + Oct1		;い
      		retlw	L4
      		retlw	Ex2			;歌詞1文字の表示
      		retlw	Si			;の
      		retlw	L4
      
      			:
      			:			;(省略)
      			:
      
      		retlw	0			;テーブルの終了
      
 上リストのテーブルを構成するための各 指示ワード は次図のように定めてあり、図の下に位置する 拡張 1 〜 拡張 3 の指示ワードが、従来の指示ワードに新たに追加をしたもので、次のような機能を持っています。 (その他の従来からの指示ワードについては、"025. 電子オルゴール" の 曲データの作り方 の項を参照してください。)
  • 拡張 1 (Ex1 = h'e0') ... 格納先バッファのクリア指示

    歌詞を表示するのに必要な "ひらがな文字フォント" を、フォントテーブル( font8x8k_data_tbl )から転送し格納するための、リニアバッファエリア( linear_buff )の 64 バイトをクリアして、 そのバッファすべてを空白バイト h'00' にします。

  • 拡張 2 (Ex2 = h'e1') ... 歌詞1文字の表示指示

    次図上の リニアバッファエリア( linear_buff )は、2 小節 6 文字分のフォントデータ "さくらさくら" を、フォントテーブル ( font8x8k_data_tbl )から転送し終わったときの様子を表しています。 実際には1度にこの状態にするのではなく、 音符に合わせながら1文字分ずつ リニアバッファエリア( linear_buff )に転送をして行きます。 このとき転送されてくるデータは 横ビット並びバイト のため、フォントテーブルから転送されてくる1文字分( 8 バイト)ごとに変換をして、 常に 縦ビット並びバイト のデータに保っています。

    一方、次図下は、最初の音符位置の "さ" の1文字だけのときの リニアバッファエリア( linear_buff )の 64 バイトを、バッファエリア( fd_buff )へ転送し終わったときの様子を表しています。 (両者の時点がバラバラで説明図がまずかったかもしれませんがご容赦を)

    そしてこの後、同図下の バッファエリア( fd_buff )上の 縦ビット並びバイト のデータ 64 バイトを、先頭から 8 バイトごとに区切ってすべてを 横ビット並びバイト に変換後、MAX7219 に送り込み、そしてドットマトリクス LED 8桁に表示をさせます。


    ドットマトリクス LED を MAX7219 で制御をする場合には、8 x 8 ドット単位、すなわち、ドットマトリクス LED 1個単位で制御をすることしかできません。 したがって、上図下のメモリ状態のときには、"さ" の文字を表示させるのに、 最低でも先頭から 8 x 2 = 16 バイトは 横ビット並びバイト に変換をする必要があります。 ただし、後続の 8 バイトごとの空白バイトについては、縦でも横でも同じなので実際にはどちらでも構わないのですが、 プログラムを単純化するために先頭からすべてを 横ビット並びバイト に変換をしています。

    そして、2つ目の音符位置の "く" の文字が フォントテーブル( font8x8k_data_tbl )から リニアバッファエリア( linear_buff )に転送され、その "く" のフォントが 縦ビット並びバイト に変換されると、 再び、後続の空白バイトも含めて先頭から 64 バイトが、バッファエリア( fd_buff )に転送、上書きされます。 その後は1文字目だけのときと同様に、64 バイトすべてを 横ビット並びバイト に変換後、MAX7219 に送り込み、 そしてドットマトリクス LED 8桁に表示をさせます。

    このように以下同様にして、"ら"、"さ"、"く"、"ら" を、フォントテーブル( font8x8k_data_tbl )→ リニアバッファエリア( linear_buff )→ バッファエリア( fd_buff )→ MAX7219 を繰り返すことによって、 ドットマトリクス LED 8桁には、1文字ずつが追加されて行くように見えますが、実は上述のように1文字の増加ごとに、ドットマトリクス LED 8桁すべてを書き換えているのです。

    以上の説明では、スタートが フォントテーブル( font8x8k_data_tbl )から始まっていますが、それではこのフォントテーブルは何を基にして、読み出す文字を選択しているのでしょうか? 実はこのフォントテーブルの前には2つのテーブルを参照しています。

    その1つ目が、上に掲載 をした "曲データテーブル( sakura_sakura, aka_tonbo )" で、すべての大元となります。 指示ワード(拡張 2 (Ex2 = h'e1') )の場合には、 "歌詞1文字の表示指示" と解釈して、次に "歌詞データテーブル( lyrics_sakura, lyrics_akatonbo )" を参照します。 これが2つ目です。
        		; 歌詞データテーブル
        lyrics_sakura
        		retlw	high font_sa		;さ
        		retlw	low font_sa		;
        		retlw	high font_ku		;く
        		retlw	low font_ku		;
        		retlw	high font_ra		;ら
        		retlw	low font_ra		;
        		retlw	high font_sa		;さ
        		retlw	low font_sa		;
        		retlw	high font_ku		;く
        		retlw	low font_ku		;
        		retlw	high font_ra		;ら
        		retlw	low font_ra		;
        
        		retlw	high font_no		;の
        		retlw	low font_no		;
        		retlw	high font_ya		;や
        		retlw	low font_ya		;
        		retlw	high font_ma		;ま
        		retlw	low font_ma		;
        		retlw	high font_mo		;も
        		retlw	low font_mo		;
        		retlw	high font_sa		;さ
        		retlw	low font_sa		;
        		retlw	high font_to		;と
        		retlw	low font_to		;
        		retlw	high font_cyo		;ー
        		retlw	low font_cyo		;
        		retlw	high font_mo		;も
        		retlw	low font_mo		;
        
        			:
        			:			;(省略)
        			:
        
        		retlw	0			;テーブルの終了
        
    "歌詞データテーブル" には、上のテーブルリストに示すように、歌詞を構成する1文字1文字の、それぞれの "ひらがな文字フォント" 8 バイトが格納されている、プログラムメモリ上の フォントごとの先頭アドレス を指定してあります。 したがって、この先頭アドレスを基にして、読み出す文字の選択をしているのです。

    ちなみに、"ひらがな文字フォント" は次のように定義がされています。
        ;===== Font8x8k.tbl =======================================================
        ;
        ;		ひらがな文字フォント(8 x 8)データテーブル
        ;
        ;==========================================================================
        
        font8x8k_data_tbl
        
        			:
        			:			;(省略)
        			:
        
        font_a		retlw 	b'00010000'	;あ
        		retlw 	b'01111110'
        		retlw 	b'00010000'
        		retlw 	b'00010010'
        		retlw 	b'00111110'
        		retlw 	b'01010101'
        		retlw 	b'01011001'
        		retlw 	b'00110010'
        
        font_i		retlw 	b'01000000'	;い
        		retlw 	b'01000100'
        		retlw 	b'01000010'
        		retlw 	b'01000010'
        		retlw 	b'01000001'
        		retlw 	b'01000001'
        		retlw 	b'01001001'
        		retlw 	b'00110000'
        
        font_u		retlw	b'00111100'	;う
        		retlw 	b'00000000'
        		retlw 	b'01111100'
        		retlw 	b'00000010'
        		retlw 	b'00000010'
        		retlw 	b'00000010'
        		retlw 	b'00000100'
        		retlw 	b'00011000'
        
        font_e		retlw	b'00111000'	;え
        		retlw 	b'00000000'
        		retlw 	b'01111110'
        		retlw 	b'00000100'
        		retlw 	b'00001000'
        		retlw 	b'00011000'
        		retlw 	b'00101000'
        		retlw 	b'01000111'
        
        font_o		retlw	b'00100000'	;お
        		retlw 	b'11111010'
        		retlw 	b'00100001'
        		retlw 	b'00111100'
        		retlw 	b'01100010'
        		retlw 	b'10100001'
        		retlw 	b'10100001'
        		retlw 	b'01100110'
        
        			:
        			:			;(省略)
        			:
        
    プログラムは、"曲データテーブル( sakura_sakura, aka_tonbo )" や "歌詞データテーブル( lyrics_sakura, lyrics_akatonbo )" のデータを、次々に読み進めるのですが、この次々に ... のためにプログラムでは、 進行途中のこれらのテーブルのアドレスを、次はどれか、を常に把握(記憶)しています。

  • 拡張 3 (Ex3 = h'20') ... 曲名の表示指示

    曲名の表示の場合には、リニアバッファエリア( linear_buff )を使用しないで、フォントデータを直接 バッファエリア( fd_buff )に転送をしています。 そのため初めに、バッファエリア( fd_buff )の 64 バイトをクリアして、そのバッファのすべてを空白バイト h'00' にしておきます。

    次に、曲名を構成する "曲名フォントアドレステーブル( title_sakura, title_akatonbo )" を基にして、曲名の1文字分のフォントデータ = 8 バイトを、プログラムメモリ上の "ひらがな文字フォントテーブル( font8x8k_data_tbl )" から、 バッファエリア( fd_buff )へ直接転送します。 このフォントデータは 横ビット並びバイト のため、転送した直後に 8 バイトの 縦ビット並びバイト に変換をします。 そして、その後続に文字間の区切りとして1バイトの空白バイト h'00' を送ります。

    この動作を、"曲名フォントアドレステーブル" の終了を検出するまで繰り返します。 そして、同テーブルが終了したときには、バッファエリア( fd_buff )のすべてが 縦ビット並びバイト になっているため、 これらを バッファエリア( fd_buff )の先頭から 8 バイトずつに区切って、バッファエリア( fd_buff )のすべてを 横ビット並びバイト に再変換をします。
        		; 曲名フォントアドレステーブル
        title_sakura
        		retlw	high font_sa		;さ
        		retlw	low font_sa		;
        		retlw	high font_ku		;く
        		retlw	low font_ku		;
        		retlw	high font_ra		;ら
        		retlw	low font_ra		;
        		retlw	high font_sa		;さ
        		retlw	low font_sa		;
        		retlw	high font_ku		;く
        		retlw	low font_ku		;
        		retlw	high font_ra		;ら
        		retlw	low font_ra		;
        		retlw	0
        
    このときの変換結果のデータは、上述した 7. ひらがな文字フォントのデモ表示 で示した 変換の概念図 の中の、一番右側 (C) の 横ビット並びバイト のデータに変換されることに注意をしてください。 ( 同図 Bの (B) から (A) ではなく、Aの (B) から (C) に変換 )

    これで、バッファエリア( fd_buff )のデータは、すべてが 横ビット並びバイト のデータになり、後は MAX7219 に転送することによって、ドットマトリクス LED に曲名の表示がされます。

    そして、曲名の表示であることを強調するために、0.5 秒間 "点"、0.5 秒間 "滅" のブリンク表示を 5 秒間 続けます。 このブリンク表示には MAX7219 の制御レジスタの内の1つ、シャットダウンレジスタ( Shutdown ) を用いて実現をさせました。

 このように、本機のオルゴールプログラムには、拡張した指示ワード 拡張 1 〜 拡張 3 によって、ドットマトリクス LED に、曲名のブリンク表示や歌詞を1文字ずつ表示をする、ディスプレイ機能 が追加されたました。

 この項の冒頭では、メロディーに合わせながら歌詞を1文字ずつ表示…とか、各音符に合わせて歌詞を1文字ずつ表示…とかの表現をしているので、あたかも "音が先" のように感じられたかもしれませんが、上記した "曲データテーブル" を見れば分かるように、 実は "その逆" で、先に歌詞を1文字表示をしてから次に音 を出しています。

 もしも "音を先" にすると、それぞれの音符の長さの時間(数 10 〜 数 100 ミリ秒)が過ぎるまで、LED の点灯を待たなければならないためで、"その逆" であれば LED は瞬時に点灯させることができるので、その後に音出しをしても何も支障は起きません。

 この項のオルゴールプログラムには、"曲データテーブル" や "歌詞データテーブル" のような、重要で、しかも大きなテーブル類が多く存在していて、思いもよらずプログラムサイズが膨れ上がってしまいました。 また、それらのテーブル類を除いたプログラム本体にも、興味ある部分が多く含まれているのですが、それらをすべて解説するとなると、ますますこのホームページも肥大化をしてしまうので、この項はそろそろ終わりにしておこうと思います。

 後は、この項のプログラムに本当に興味を持たれた方々には、現在の最新バージョン で "ソースファイル ( MatrixLED_Controller.asm )" を開示しているので、ご自分でプログラムの中身をご覧になってみてください。 何か発見があるかもしれません。

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


 ◎ 11. LED の輝度調節

 本機の電源スイッチを ON にすると、MAX7219 が初期設定されて、輝度レベルはデフォルト値 0 に設定がされます。 本機では、"本機で使用した各種モジュールについて" で述べたように、4連ドットマトリクス 8 x 8 LED モジュール を使用しているので、私はデフォルト値 0 のままで十分に明るいと感じていますが、LED に高輝度でないものを使用した場合には、輝度レベルを調節できる機能も必要になってきます。


    機能の概要

    • メインルーチンから当プログラムにジャンプをしてくると、上述した メッセージ類の画面レイアウト図 中の LED 輝度調節メッセージが表示 され、 なおかつ、同メッセージの右端に位置する "9" の部分に、現在設定されている 輝度レベル値がブリンク表示 される。
          LED 輝度調節メッセージ:     [ brightness = 9 ]
          
    • このブリンク表示は、ユーザの注意を引くためのもので、メインルーチンの場合と同様に当プログラムでも、タイマー0割り込みを使用してブリンクのタイミングを取っている。 輝度レベルは、本機で使用したIC MAX7219 では 16 段階 で変更することが可能であるが、LED の安全性を考慮してプログラムでは、デフォルト値 0 から上限を 9 までの 10 段階に制限をしてある。

    • UP スイッチ、または DOWN スイッチ を押すことによって、輝度レベルの設定値を変更 することができ、輝度レベル値が更新されるとともに、ブリンク表示されている輝度レベル値も更新される。

    • なお、輝度レベルを調節した後、EXIT スイッチ を押すことによって、再び メインルーチンに戻る ことができる。


      ;==========================================================================
      ;		rtsw_sel = 11: LED の輝度調節機能の処理
      ;==========================================================================
      
      		; intensity:	輝度調節フラグ
      				; bit7:   初期メッセージ表示指示フラグ
      				; bit6:   ブリンクフラグ     (タイマー0割り込みで設定)
      				; bit5:   ブリンク変更フラグ (     〃      )
      				; bit4:   輝度更新フラグ
      				; bit3-0: 輝度調節値 (0 〜 9)
      
      ;--------------------------------------------------------------------------
      
      bright_adj_process	;-------
      
      		movlw	h'0f'			;輝度調節値だけを取り出す
      		andwf	intensity,W		;
      		iorlw	b'10110000'		;bit7: 初期メッセージ表示指示 = ON
      						;bit6: ブリンク反転フラグ     = OFF
      						;bit5: ブリンク変更フラグ     = (ON)
      						;bit4: 輝度更新フラグ         = (ON)
      		movwf	intensity		;初期設定
      
      		movlw	tm0_h_val		;ハードタイマー0カウント値を
      		movwf	TMR0			;TMR0 に設定
      		movlw	tm0_s_val		;ソフトタイマー0カウント値を
      		movwf	tm0_s_cnt		;ソフトタイマー0カウンタに設定
      		bcf	INTCON,TMR0IF		;bit2(TMR0IF)=0: タイマー0割込みフラグをクリア
      		bsf	INTCON,TMR0IE		;bit5(TMR0IE)=1: タイマー0割り込みを許可
      
      		; 輝度レジスタの更新
      
      briadj01	movlw	h'0a'			;輝度レジスタ
      		movwf	reg_addr
      		movf	intensity,W		;輝度調節値
      		andlw	h'0f'
      		movwf	reg_data
      		movlp	high max_led_contr
      		call	max_led_contr		;MAX7219 x 8個分の制御
      		movlp	high $
      		bcf	intensity,4		;輝度更新フラグ intensity.4 = OFF
      		bsf	intensity,5		;ブリンク変更フラグ intensity.5 = ON
      
      		; 輝度調節値の LED 表示
      
      briadj02	movlp	high brightness_msg_display
      		call	brightness_msg_display	;LED の輝度調節メッセージの表示
      		movlp	high $
      
      		; 各スイッチの入力チェック
      
      briadj03	btfss	PORTA,_swEXT		;EXIT スイッチ = ON か?
      		goto	briadj04		;Yes
      
      		btfss	PORTA,_swUP		;UP スイッチ = ON か?
      		call	br_up_switch		;Yes
      
      		btfss	PORTA,_swDWN		;DOWN スイッチ = ON か?
      		call	br_down_switch		;Yes
      
      		btfsc	intensity,4		;輝度更新フラグ intensity.4 = ON か?
      		goto	briadj01		;Yes
      
      		movlw	20			;要調整
      		call	wait_xxms
      		btfss	intensity,5		;ブリンク変更フラグ intensity.5 = ON か?
      		goto	briadj03		;No
      		goto	briadj02		;Yes
      
      briadj04	call	wait_30ms		;チャッタリング吸収
      		btfsc	PORTA,_swEXT		;再度 EXIT スイッチ = ON か?
      		goto	briadj03		;No
      
      		; 輝度調節処理の終了
      
      		btfss	PORTA,_swEXT		;EXIT スイッチ = OFF か?
      		goto	$ - 1			;No
      
      		call	wait_30ms		;チャッタリング吸収
      		bcf	INTCON,TMR0IE		;bit5(TMR0IE)=1: タイマー0割り込みを禁止
      		movlp	high main
      		goto	main			;メイン処理に戻る
      
      ;--------------------------------------------------------------------------
      ;	      UP スイッチの処理 ... 輝度調節機能から CALL
      ;--------------------------------------------------------------------------
      
      br_up_switch		;-------
      
      		call	wait_30ms		;チャッタリング吸収
      		btfsc	PORTA,_swUP		;再度 UP スイッチ = ON か?
      		goto	brupsw01		;No
      
      		movf	intensity,W
      		andlw	h'0f'
      		sublw	9			;MAX h'0f' を h'09' に制限した
      		btfsc	STATUS,Z		;intensity = 9 か?
      		goto	brupsw01		;Yes
      
      		incf	intensity,F		;+1 更新
      		bsf	intensity,4		;輝度更新フラグ = ON
      
      brupsw01	btfss	PORTA,_swUP		;UP スイッチ = OFF か?
      		goto	$ - 1			;No
      
      		call	wait_30ms		;チャッタリング吸収
      		return
      
      ;--------------------------------------------------------------------------
      ;	     DOWN スイッチの処理 ... 輝度調節機能から CALL
      ;--------------------------------------------------------------------------
      
      br_down_switch		;-------
      
      		call	wait_30ms		;チャッタリング吸収
      		btfsc	PORTA,_swDWN		;再度 DOWN スイッチ = ON か?
      		goto	brdwsw01		;No
      
      		movf	intensity,W
      		andlw	h'0f'
      		btfsc	STATUS,Z		;intensity = 0 か?
      		goto	brdwsw01		;Yes
      
      		decf	intensity,F		;-1 更新
      		bsf	intensity,4		;輝度更新フラグ = ON
      
      brdwsw01	btfss	PORTA,_swDWN		;DOWN スイッチ = OFF か?
      		goto	$ - 1			;No
      
      		call	wait_30ms		;チャッタリング吸収
      		return
      
 | プログラムのトップに戻る |


 ◎ 12. 年月日、時分秒の設定

 本機に初めて電源を入れたときや、バックアップ用のバッテリーを交換した後などには、初期設定ルーチンでリアルタイムクロック( RTC )モジュールの初期化が行われますが、その設定値は、初期設定プログラムでプログラミングされたときの設定値になり、 リアルな現在値とは異なるためにユーザが、スイッチ操作によって現在の年月日、時分秒に設定をし直す必要があります。

 また、本機では精度があまり良くない RTC モジュールを使用していることもあって、時間の経過とともに目立つような狂いを生じてくるために、初期設定のときと同様に、年月日、時分秒の設定をし直す必要も起こります。


    機能の概要

    • メインルーチンから当プログラムにジャンプをしてくると、まず初めに、上述した メッセージ類の画面レイアウト図 中の 年月日表示 を表示テンプレートとして、現在の "年" "月" "日" と "曜日" が表示され、 なおかつ、左端に位置する "年" 2桁がブリンク表示 されて、設定変更の対象項目であることを示している。
          年月日表示:     [ 22.01.01 SAT ]  ..... ~~ の位置 "年" がブリンク表示
                       ~~
          
    • ここで変更が必要であれば、UP スイッチ を押すごとに現在値より1ずつ増加し、また DOWN スイッチ を押すごとに1ずつ減少するので、適宜希望する値に変更をする。 また、このときの増減幅を大きくしたい場合には、 UP スイッチ、または DOWN スイッチを押したままにしていると、1秒当たり 4 〜 5 の増減幅で更新をするようになる。

    • "年" の設定変更が済んだら、SET スイッチ を押すと、次には "月" 2桁がブリンク表示 され、設定変更の対象項目が次に移動したことを示す。 同様に変更が必要であれば、 UP スイッチ、または DOWN スイッチを押すことによって、適宜希望する値に変更をする。
          年月日表示:     [ 22.04.01 FRI ]  ..... ~~ の位置 "月" がブリンク表示
                          ~~
          
    • 同様に、SET スイッチ を押すと、次には "日" 2桁がブリンク表示 されるので、以降同様にスイッチ操作をして行く。
          年月日表示:     [ 22.04.18 MON ]  ..... ~~ の位置 "日" がブリンク表示
                             ~~
          
    • これまでのスイッチ操作で、"年" "月" "日" のどれかの値を変更すると、それらの設定値に基づいてプログラムが "曜日" を自動的に変更するが、この "曜日" をユーザが変更することはできない。

    • この "日" の設定変更が済んだ後に SET スイッチ を押すと、次には メッセージ類の画面レイアウト図 中の 時分秒表示 を表示テンプレートとして、現在設定されている "時" "分" "秒" に表示が変更され、 なおかつ、"時" 2桁がブリンク表示 される。
          時分秒表示:     [ AM 12:00:00 ]  ..... ~~ の位置 "時" がブリンク表示
                          ~~
          
    • 以降同様にして "分" "秒" と対象項目の変更をして行くが、これまでに、誤って希望の対象項目を通り過ぎてしまったときには、BACK スイッチ を押すごとに1ずつ前の項目に戻ることができる。

    • この対象項目と SET スイッチ、BACK スイッチの関係をまとめると、次のようになる。
                      BACK   BACK   BACK   BACK   BACK
                      ←─   ←─   ←─   ←─   ←─
                       SET    SET    SET    SET    SET    SET
          メインルーチン ─→ 年 ─→ 月 ─→ 日 ─→ 時 ─→ 分 ─→ 秒 ─→ メインルーチン
                     │                   ↑
                  BACK└───────────────────┘
          
    • なお、この時分秒表示で 12 時間表示の設定がされているとき には、"時" の左側の位置に AM または PM の文字が表示されるが、24 時間表示に設定のとき には、同位置は空白表示となる。 そしてこの 12 時間表示 / 24 時間表示 の設定変更は、1. 時分秒の時計表示 プログラムの実行時のみ可能で、当プログラムで変更することはできない。(デフォルトは 24 時間表示)

    • 最後の対象項目 "秒" がブリンク表示されているときに、SET / EXIT スイッチ を押すと、今まで設定をしてきた "年" 〜 "秒" までの、すべての項目が リアルタイムクロックモジュール RTC に書き込み更新され、 新たな設定値で RTC の時刻のカウントが始まると同時に、プログラムは再び メインルーチンに戻る


 次図は、上述した メッセージ類の画面レイアウト図 から、下の3つだけを取り出して再掲をしたものです。


 この図をよく見ると、年月日表示 では "年" と "月"、および "月" と "日"( "曜日" も含める)が占める各エリアは、赤色の縦太線で示した境界線を跨ぐことなく配置されているのが分かります。 また、時分秒表示 では "時"( "AM" または "PM" も含める)と "分"、および "分" と "秒" についても同様な配置となっています。 この縦の太線や細線を含めた赤線は、各マトリクス LED(横 8 ドット)ごとの境界線を表しています。

 これらの "年" "月" "日" とか "時" "分" "秒" のフォントデータを、実際の バッファエリア( fd_buff )に配置をするプログラムは、少々大きめなのでここで示すのは避けますが、そのプログラム中のコメントだけを次に示しておきます。

 まず 年月日表示 では、次のように3つのブロックに分けて、それぞれのフォントデータを上図に従って、バッファに配置をしています。
      		; 年 フォントデータのバッファ格納
      
      ;		|                       |                       |
      ;		 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15
      ;		 _ _ x x x x x _ x x x x x _  . .
      
      			:
      			:
      
      		; 月 フォントデータのバッファ格納
      
      ;		|                       |                       |
      ;		 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
      ;		 _ x x x x x _ x x x x x _  . ._
      
      			:
      			:
      
      		; 日 フォントデータのバッファ格納
      
      ;	|                       |                       |                       |                       |
      ;	 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
      ;	 x x x x x _ x x x x x _ _ x x x x x _ x x x x x _ x x x x x _ _
      
      			:
      			:
      
 一方 時分秒表示 でも、次のように3つのブロックに分けて、それぞれのフォントデータを上図に従って、バッファに配置をしています。
      		; 時 フォントデータのバッファ格納
      
      ;	|                       |                       |                       |                       |
      ;	 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
      ;	 _ _ _ x x x x x _ x x x x x _ _ x x x x x _ x x x x x _ _ : : _
      
      			:
      			:
      
      		; 分 フォントデータのバッファ格納
      
      ;		|                       |                       |
      ;		 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
      ;		 _ x x x x x _ x x x x x _ _ : :
      
      			:
      			:
      
      		; 秒 フォントデータのバッファ格納
      
      ;		|                       |                       |
      ;		 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
      ;		 _ _ x x x x x _ x x x x x _ _ _
      
      			:
      			:
      
 これらのコメントで示している各ブロック内であれば、フォントデータが境界線を跨いでいても構いません。 これらのフォントデータには、5. 標準文字フォントのデモ表示 で説明をした、縦ビット並びデータ である 標準文字フォント を使用しています。 したがって、MAX7219 でマトリクス LED に表示をさせるときには、各ブロック内の 8 バイトごとを 横ビット並びデータ に変換をします。

 以上の説明は、この項の "12. 年月日、時分秒の設定" プログラム内だけでなく、1. 時分秒の時計表示 プログラム内で使用をしている サブルーチン( time_display )2. 年月日(曜日)の表示 プログラム内で使用をしている サブルーチン( date_display )が、それぞれのフォントデータを、バッファエリア( fd_buff )内の各ブロックごとに配置後、縦ビット並びから 横ビット並びデータ に変換、 および MAX7219 でマトリクス LED に表示をさせているサブルーチンです。

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


● リアルタイムクロックモジュールのプログラミング

 上述の、1. 時分秒の時計表示 の項の冒頭、および "本機で使用した各種モジュールについて" の リアルタイムクロック( DS1307 )モジュール の項で述べたように、 本機では、リアルタイムクロック( RTC )モジュールに DS1307 版モジュール を使用しています。

 このモジュールは、"190. I2C IF モジュール + LCD(1602A) 表示時計" 等で使用した DS3231 版モジュール に比べると、私が使用してみた結果も、ご多分に漏れずかなり精度が劣っていました。 DS3231 版モジュールを使用した同時計では、2020 年 12 月頃に作製(完成したのは翌年の 1 月頃)をして以来、ずっと電源を入れっぱなしにして1年3〜4か月ほどになりますが、現在、ほんの数秒の遅れがある程度です。

 これは、私が購入した DS3231 版モジュールが、たまたま "大当たり" だったのかもしれませんが、それに比べて、今回使用した DS1307 版モジュールでは、数日で数十秒の進みがあり、"えっ" と驚くほどの差があります。 "お遊び要素" が多い本機ではこれでも "まあ良し" としていますが、これは大昔のゼンマイ式の柱時計のようです。

 それはさておき、これらの RTC モジュールを使用する上で重要なのが次表に示すレジスタ群で、上表が DS3231 のもの、下表が DS1307 のもので、それぞれ Maxim Integrated 社のデータシート DS3231、および DS1307 から抜粋をしました。


[ DS3231 ]


[ DS1307 ]

 次に示す各図も、Maxim Integrated 社のデータシート DS1307 から抜粋をしたもので、実際に RTC モジュールのプログラミングをするときに必要になりますが、これらのシーケンスは DS3231 とまったく同じで異なるところはありません。


 したがって、I2C RTC(DS1307)のプログラミングには、"190. I2C IF モジュール + LCD(1602A) 表示時計" の リアルタイムクロックモジュールのプログラミング の項で示した "I2C RTC(DS3231)モジュール 制御サブルーチン(ソフトウエア I2C 版)" を基に、本機の I2C RTC(DS1307)にも対応ができるようにと、次に示すリストのように、同一のプログラムで DS3231 / DS1307 のどちらにも対応 ができるように書き換えました。

 この制御サブルーチンは、本機 "195. ドットマトリクス LED コントローラー" のプログラムとは、別ファイルの " I2C RTC(DS3231/1307)モジュール 制御サブルーチン(ソフトウエア I2C 版) I2C_RTCII.sub " としてまとめてあるため、 この制御サブルーチンを使用するときには、次のリストの冒頭のコメントに従って、include する側(メインのプログラム側)で #define 定義をする必要があります。 なお、本機のように PIC16F1 ファミリーで使用する場合には、その #define 定義もする必要があります。
      ;==========================================================================
      ; I2C RTC(DS3231/1307)モジュール 制御サブルーチン(ソフトウエア I2C 版)
      ;==========================================================================
      
      ;		DS3231 の場合 ... Standard mode, Fast mode
      ;		DS1307 の場合 ... Standard mode only 
      
      ;--------------------------------------------------------------------------
      
      ;#define 	_DS1307			;DS1307 を使用する場合に 
      					;include する側で #define 定義をすること
      ;#define 	_PIC16F1		;PIC16F1ファミリで使用する場合に 〃
      
      ;<使用外部ルーチン>
      
      ;(i2c_start, i2c_stop, i2c_byte_send, i2c_byte_reciv, i2c_ack, i2c_nack)
      
      ;<使用レジスタ>
      
      ;rtc_addr_high				;読み出すデータの high アドレス
      ;rtc_addr_low				;読み出すデータの low アドレス
      ;rtc_lpcnt				;ループカウンタ
      
      ;(読み書きバッファ)
      ;seconds				;00h: Seconds: 00-59
      ;minutes				;01h: Minutes: 00-59
      ;hours					;02h: Hours:   1-12 + AM/PM, 00-23
      ;day					;03h: Day:     1-7
      ;date					;04h: Date:    01-31
      ;month					;05h: Month/Century: 01-12 + Century
      ;year					;06h: Year:    00-99
      ;(control				;07h: Control)
      
      ;alarm1s				;07h: Alarm 1 Seconds: 00-59
      ;alarm1m				;08h: Alarm 1 Minutes: 00-59
      ;alarm1h				;09h: Alarm 1 Hours:   1-12 + AM/PM, 00-23
      ;alarm1dy				;0Ah: Alarm 1 Day:     1-7
      ;alarm1d				;0Bh: Alarm 1 Date:    1-31
      ;alarm2h				;0Ch: Alarm 2 Hours:   1-12 + AM/PM, 00-23
      ;alarm2d				;0Dh: Alarm 2 Day:     1-7, Alarm 2 Date:    1-31
      ;control				;0Eh: Control
      ;cont_stat				;0Fh: Control/Status
      ;aging					;10h: Aging Offset
      ;temp_m					;11h: MSB of Temp
      ;temp_l					;12h: LSB of Temp
      
      ;==========================================================================
      ;		定数の定義
      ;==========================================================================
      
      RtcAddr		equ	h'68'		;RTC モジュール(DS3231/1307)のスレーブアドレス
      	#ifdef	_DS1307
      RtcEepAddr	equ	h'50'		;RTC モジュール(AT24C32)のスレーブアドレス
      	#else
      RtcEepAddr	equ	h'57'		;RTC モジュール(AT24C32)のスレーブアドレス
      	#endif
      
      ;==========================================================================
      ;		マクロ命令定義
      ;==========================================================================
      
      ;		Rtc_Write_M マクロ
      
      Rtc_Write_M	macro	@bufreg,@n
      	#ifdef	_PIC16F1
      		movlw	high @bufreg	;書き込みバッファのレジスタアドレス
      		movwf	FSR1H		;
      		movlw	low @bufreg	;
      		movwf	FSR1L		;間接アドレスに設定
      	#else
      		movlw	@bufreg		;書き込みバッファのレジスタアドレス
      		movwf	FSR		;間接アドレスに設定
      	#endif
      		movlw	@n
      		movwf	rtc_lpcnt	;ループカウンタ
      		call	I2cRtc_Write	;RTC(DS3231/1307)モジュールのレジスタへ書き込み
      		endm
      
      ;		Rtc_Read_M マクロ
      
      Rtc_Read_M	macro	@bufreg,@n
      	#ifdef	_PIC16F1
      		movlw	high @bufreg	;書き込みバッファのレジスタアドレス
      		movwf	FSR1H		;
      		movlw	low @bufreg	;読み出しバッファのレジスタアドレス
      		movwf	FSR1L		;間接アドレスに設定
      	#else
      		movlw	@bufreg		;読み出しバッファのレジスタアドレス
      		movwf	FSR		;間接アドレスに設定
      	#endif
      		movlw	@n
      		movwf	rtc_lpcnt	;ループカウンタ
      		call	I2cRtc_Read	;RTC(DS3231/1307)モジュールのレジスタから読み出し
      		endm
      
      ;==========================================================================
      ;		RTC(DS3231/1307)モジュールのレジスタへ書き込み
      ;==========================================================================
      
      ;入力:		FSR:         書き込みバッファの(先頭)レジスタアドレス
      ;		FSR1H,FSR1L: PIC16F1ファミリの場合
      ;		rtc_lpcnt:   書き込み数
      
      I2cRtc_Write
      		call	i2c_start	;スタートコンディション
      		movlw	RtcAddr	<< 1	;スレーブアドレス
      		call	i2c_byte_send	;1 バイト送信
      		movlw	seconds
      	#ifdef	_PIC16F1
      		subwf	FSR1L,W		;レジスタアドレス
      	#else
      		subwf	FSR,W		;レジスタアドレス
      	#endif
      		call	i2c_byte_send	;1 バイト送信
      
      rtcwr01
      	#ifdef	_PIC16F1
      		moviw	FSR1++		;書き込みデータ
      	#else
      		movf	INDF,W		;書き込みデータ
      		incf	FSR,F		;間接アドレスの更新
      	#endif
      		call	i2c_byte_send	;1 バイト送信
      		decfsz	rtc_lpcnt,F	;ループカウンタ - 1 = 0 か?
      		goto	rtcwr01		;No
      
      		call	i2c_stop	;ストップコンディション
      		return
      
      ;==========================================================================
      ;		RTC(DS3231/1307)モジュールのレジスタから読み出し
      ;==========================================================================
      
      ;入力:		FSR:         読み出しバッファの(先頭)レジスタアドレス
      ;		FSR1H,FSR1L: PIC16F1ファミリの場合
      ;		rtc_lpcnt:   読み出し数
      
      I2cRtc_Read
      		call	i2c_start	;スタートコンディション
      		movlw	RtcAddr	<< 1	;スレーブアドレス
      		call	i2c_byte_send	;1 バイト送信
      		movlw	seconds
      	#ifdef	_PIC16F1
      		subwf	FSR1L,W		;レジスタアドレス
      	#else
      		subwf	FSR,W		;レジスタアドレス
      	#endif
      		call	i2c_byte_send	;1 バイト送信
      
      		call	i2c_start	;再スタートコンディション
      		movlw	(RtcAddr << 1) + 1 ;スレーブアドレス
      		call	i2c_byte_send	;1 バイト送信
      		goto	rtcrd02
      
      rtcrd01		call	i2c_ack		;ACK
      
      rtcrd02		call	i2c_byte_reciv	;1 バイト受信
      	#ifdef	_PIC16F1
      		movwi	FSR1++		;読み出しデータ
      	#else
      		movwf	INDF		;読み出しデータ
      		incf	FSR,F		;間接アドレスの更新
      	#endif
      		decfsz	rtc_lpcnt,F	;ループカウンタ - 1 = 0 か?
      		goto	rtcrd01		;No
      
      		call	i2c_nack	;NACK
      		call	i2c_stop	;ストップコンディション
      		return
      
      ;==========================================================================
      ;		I2C RTC(DS3231/1307)モジュールの初期設定
      ;==========================================================================
      
      I2cRtc_Init
      		movlw	high ds3231_init_table	;テーブルデータの先頭 high アドレス
      		movwf	rtc_addr_high	;
      		movlw	low ds3231_init_table	;テーブルデータの先頭 low アドレス
      		movwf	rtc_addr_low	;
      		movlw	ds3231_init_table_end - ds3231_init_table
      		movwf	rtc_lpcnt	;ループカウンタ
      
      		call	i2c_start	;スタートコンディション
      		movlw	RtcAddr	<< 1	;スレーブアドレス
      		call	i2c_byte_send	;1 バイト送信
      
      rtcini01	call	rtctbl_read	;テーブルデータの読み出し
      ;		bcf	PCLATH,3	;ページ 0 に戻す
      ;		bcf	PCLATH,4
      		call	i2c_byte_send	;1 バイト送信
      
      		incf	rtc_addr_low,F	;読み出すデータの low アドレスの更新
      		btfsc	STATUS,Z	;オーバーフローした場合は
      		incf	rtc_addr_high,F	;読み出すデータの high アドレスの更新
      		decfsz	rtc_lpcnt,F	;ループカウンタ - 1 = 0 か?
      		goto	rtcini01	;No
      
      		call	i2c_stop	;ストップコンディション
      		return
      
      		; テーブルデータの1文字読み出し
      rtctbl_read
      		movf	rtc_addr_high,W	;読み出すデータの high アドレス
      		movwf	PCLATH
      		movf	rtc_addr_low,W	;読み出すデータの low アドレス
      		movwf	PCL
      
      ;--------------------------------------------------------------------------
      
      ;		I2C RTC(DS3231/1307)モジュールの初期設定用テーブルデータ
      
      ds3231_init_table
      		dt	h'00'		;DS3231/1307 レジスタのスタート(先頭)アドレス
      		dt	h'00'		;00h: Seconds, DS1307の場合 bit7:CH = 0
      		dt	h'00'		;01h: Minutes
      		dt	h'00'		;02h: Hours
      		dt	h'07'		;03h: Day(Week)
      		dt	h'01'		;04h: Date
      		dt	h'01'		;05h: Month/Century
      		dt	h'22'		;06h: Year
      	#ifdef	_DS1307
      		dt	h'10'		;07h: Control, bit4:SQWE = 1, bit1-0:RS1-0 = 00 ... 1Hz
      	#else
      		dt	h'00'		;07h: Alarm 1 Seconds
      		dt	h'00'		;08h: Alarm 1 Minutes
      		dt	h'00'		;09h: Alarm 1 Hours
      		dt	h'00'		;0Ah: Alarm 1 Day
      		dt	h'00'		;0Bh: Alarm 1 Date
      		dt	h'00'		;0Ch: Alarm 2 Hour
      		dt	h'00'		;0Dh: Alarm 2 Day
      		dt	h'00'		;0Eh: Control
      		dt	h'00'		;0Fh: Control/Status
      		dt	h'00'		;10h: Aging Offset
      ;		dt	h'00'		;11h: MSB of Temp
      ;		dt	h'00'		;12h: LSB of Temp
      	#endif
      ds3231_init_table_end
      
      ;=============================================================== end ======
      
 本機に初めて電源を入れたときや、バックアップ用のバッテリーを交換した後などには、メインルーチンで RTC の初期設定 が必要となりますが、DS3231 では、Control/Status ( 0Fh ) レジスタOSF ビット を監視することで実現をさせていました。 これに対して DS1307 では、Seconds ( 00h ) レジスタ に同居をしている CH ビット を監視することで実現をさせています。

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


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

 上述の、3. 温度、湿度の表示 の項の冒頭、および "本機で使用した各種モジュールについて" の デジタル温度湿度センサー( DHT11 )モジュール の項で述べたように、 本機では、 デジタル温度湿度センサーモジュールに DHT11 版モジュール を使用しています。

 次に示す各図は、AOSONG 社のデータシート DHT11 から抜粋をしたもので、実際に デジタル温度湿度センサーモジュールのプログラミングをするときに必要になりますが、これらのタイミング図の中で、 "190. I2C IF モジュール + LCD(1602A) 表示時計" 等で使用した、DHT22 と異なっているところは、初めに PIC から送られる スタート信号 の長さが、DHT22 では 1 mSDHT11 では 18 mS 以上となっている点だけで、 それ以外はどちらも同じで異なるところはありません。

 PIC と DHT22 とのインターフェイスの説明を、同時計の デジタル温度湿度センサーモジュールのプログラミング の項で、詳述をしているので参考にしてください。

スタート信号 応答信号


 したがって、DHT11 版モジュールのプログラミングには、"190. I2C IF モジュール + LCD(1602A) 表示時計" の デジタル温度湿度センサーモジュールのプログラミング の項で示した "デジタル温度湿度センサーモジュール DHT22 制御サブルーチン" を基に、本機の DHT11 版モジュールにも対応ができるようにと、次に示すリストのように、同一のプログラムで DHT22 / DHT11 のどちらにも対応 ができるように書き換えました。

 この制御サブルーチンは、本機 "195. ドットマトリクス LED コントローラー" のプログラムとは、別ファイルの " デジタル温度湿度センサーモジュール DHT22/11 制御サブルーチン DHT22_11.sub " としてまとめてあるため、 この制御サブルーチンを使用するときには、次のリストの冒頭のコメントに従って、include する側(メインのプログラム側)で #define 定義をする必要があります。 なお、本機のように PIC16F1 ファミリーで使用する場合には、その #define 定義もする必要があります。
      ;==========================================================================
      ;	デジタル温度湿度センサーモジュール DHT22/11 制御サブルーチン
      ;==========================================================================
      
      ;#define 	TRISX	TRISC		;例. DHT22/11 で使用する DATA ポートを
      ;#define 	PORTX	PORTC		;include する側で #define 定義をすること
      
      ;#define 	_DHT11			;DHT11 を使用する場合に 〃
      
      ;#define 	_PIC16F1		;PIC16F1ファミリで使用する場合に 〃
      
      ;<使用外部ルーチン>
      
      ;(wait_10us, wait_1ms, wait_20ms)	;wait_1ms ... DHT22, wait_20ms ... DHT11
      
      ;<使用レジスタ>
      
      ;humidity_h				;湿度上位 8 bit
      ;humidity_l				;湿度下位 8 bit
      ;temperature_h				;温度上位 8 bit
      ;temperature_l				;温度下位 8 bit
      ;time_cnt				;bit0-5:時間計測カウンタ, 
      					;bit6:チェックサム(CRC)エラー, bit7:タイムアウトエラー
      ;lp_cnt1				;汎用ループカウンタ
      ;lp_cnt2				;汎用ループカウンタ
      ;work1					;ワーク
      ;work2					;ワーク
      
      ;(FSR)					;PIC16F ファミリの場合
      ;(FSR1H, FSR1L)				;PIC16F1ファミリの場合
      
      ;==========================================================================
      ;	  デジタル温度湿度センサーモジュール DHT22/11 から読み出し
      ;==========================================================================
      
      	#ifdef	_DHT11
      dht11_measure
      	#else
      dht22_measure
      	#endif
      
      	#ifdef	_PIC16F1
      		movlb	1		;バンク 1
      	#else
      		bsf	STATUS,RP0	;バンク 1
      	#endif
      		bcf	TRISX,DHT	;出力ポート
      	#ifdef	_PIC16F1
      		movlb	0		;バンク 0
      	#else
      		bcf	STATUS,RP0	;バンク 0
      	#endif
      
      		bcf	PORTX,DHT	;DHT="L", スタート信号
      	#ifdef	_DHT11
      		call	wait_20ms	;18m 秒以上ウェイト
      	#else
      		call	wait_1ms	;1m 秒ウェイト
      	#endif
      
      	#ifdef	_PIC16F1
      		movlb	1		;バンク 1
      	#else
      		bsf	STATUS,RP0	;バンク 1
      	#endif
      		bsf	TRISX,DHT	;入力ポート, プルアップで DHT="H"
      	#ifdef	_PIC16F1
      		movlb	0		;バンク 0
      	#else
      		bcf	STATUS,RP0	;バンク 0
      	#endif
      
      		call	dht_low		;DHT22/11 からの応答信号 "L" を待つ
      		btfsc	time_cnt,7	;タイムアウトエラー か?
      		goto	dht_m05		;Yes
      
      	#ifdef	_PIC16F1
      		movlw	high humidity_h	;湿度上位 8 bit のアドレス
      		movwf	FSR1H		;
      		movlw	low humidity_h	;
      		movwf	FSR1L		;間接アドレスに設定
      	#else
      		movlw	humidity_h	;湿度上位 8 bit のアドレス
      		movwf	FSR		;間接アドレスに設定
      	#endif
      		clrf	work1		;ワーク, チェックサム計算用
      		movlw	5
      		movwf	lp_cnt1		;バイトカウンタ
      
      dht_m01		clrf	work2		;ワーク, ビットデータ保管用
      		movlw	8
      		movwf	lp_cnt2		;ビットカウンタ
      
      dht_m02		call	dht_low		;DHT22/11 からのビットの同期信号 "L" を待つ
      		btfsc	time_cnt,7	;タイムアウトエラー か?
      		goto	dht_m05		;Yes
      
      		call	dht_high	;DHT22/11 からの "H" 信号の長さを計測
      		btfsc	time_cnt,7	;タイムアウトエラー か?
      		goto	dht_m05		;Yes
      
      		bcf	STATUS,C
      		rlf	work2,F		;左にシフト
      
      		movlw	4		;3 〜 4
      		subwf	time_cnt,W
      		btfsc	STATUS,C	;time_cnt >= 4 か?
      		bsf	work2,0		;Yes. ビット "1"
      
      		decfsz	lp_cnt2,F	;ビットカウンタ lp_cnt2 - 1 = 0 か?
      		goto	dht_m02		;No
      
      		movf	work2,W		;受信データ 1 バイト
      
      		decfsz	lp_cnt1,F	;バイトカウンタ lp_cnt1 - 1 = 0 か?
      		goto	dht_m03		;No
      		goto	dht_m04		;Yes
      
      dht_m03
      	#ifdef	_PIC16F1
      		movwi	FSR1++		;
      	#else
      		movwf	INDF		;受信データ格納
      		incf	FSR,F		;間接アドレスの更新
      	#endif
      		addwf	work1,F		;チェックサムの計算
      		goto	dht_m01		;
      
      dht_m04		subwf	work1,W		;
      		btfss	STATUS,Z	;チェックサムは一致 か?
      		bsf	time_cnt,6	;No. チェックサム(CRC)エラー
      
      dht_m05		return
      
      ;--------------------------------------------------------------------------
      
      		; DHT22/11 からの "L" 信号を待つ "H" → "L" → "H"
      
      dht_low
      		call	dht_high	;DHT22/11 からの "H" 信号の長さを計測
      		btfsc	time_cnt,7	;タイムアウトエラー か?
      		goto	dht_l02		;Yes
      
      		clrf	time_cnt	;初期設定
      
      dht_l01		call	wait_10us
      		incf	time_cnt,F	;
      		btfsc	PORTX,DHT	;DHT="H" か?
      		goto	dht_l02		;Yes
      
      		movlw	10
      		subwf	time_cnt,W
      		btfss	STATUS,C	;time_cnt >= 10 か?
      		goto	dht_l01		;No
      
      		bsf	time_cnt,7	;タイムアウトエラー
      
      dht_l02		return
      
      ;--------------------------------------------------------------------------
      
      		; DHT22/11 からの "H" 信号の長さを計測
      
      dht_high
      		clrf	time_cnt	;初期設定
      
      dht_h01		call	wait_10us
      		incf	time_cnt,F	;
      		btfss	PORTX,DHT	;DHT="L" か?
      		goto	dht_h02		;Yes
      
      		movlw	10
      		subwf	time_cnt,W
      		btfss	STATUS,C	;time_cnt >= 10 か?
      		goto	dht_h01		;No
      
      		bsf	time_cnt,7	;タイムアウトエラー
      
      dht_h02		return
      
      ;=============================================================== end ======
      
 DHT11 のデータシートを見てみると、DHT22 のときと同様に、次の順に各データが計 40 ビット送出されてくる、と書かれていて確かに計 40 ビットが送られてはきますが、この内の、湿度下位 8 bit と温度下位 8 bit、すなわち、湿度、温度ともに小数点以下のデータは、 常にオール "0" データ(測定ができない)になっています。
      湿度上位 8 bit、湿度下位 8 bit、温度上位 8 bit、温度下位 8 bit、チェックサム 8 bit
      
 したがって、送出されてくるデータの正確度を別にしても、その点だけでも DHT11 は DHT22 に比べて劣っていると言えます。

 また、これらのデータを取り扱う上で、両者では大きく異なっている点があります。

 それは DHT22 では、湿度上位 8 bit と湿度下位 8 bit、または 温度上位 8 bit と温度下位 8 bit を、それぞれ 計 16 bit データとして取り扱い、10 進数データに変換後は、上位の "0" を除いた有効数字の内、たとえば3桁の有効数字が得られた場合に、 上位2桁を 整数部、下位1桁を 小数部 とします。

 これに対して DHT11 では、変換をする以前から、それぞれの 湿度上位 8 bit、または 温度上位 8 bit を 整数部 とし、湿度下位 8 bit、または 温度下位 8 bit を 小数部 として取り扱う必要があります。

DHT11 でのトラブル

 このように、DHT11 を使用した温度湿度の表示をプログラミングしたのですが、温度については "まあ正常であろう" と思われる、妥当な範囲の値を表示するのですが、それに対して、湿度については "明らかにおかしい" と思うような値を表示するのです。 1x % とか、2x % とかの表示値で、常識をかなり逸した低い値を示すのです。

 家の同部屋には、DHT22 を使用した "190. I2C IF モジュール + LCD(1602A) 表示時計" も置いてあり、比較をしてみればその異常さが良く分かります。

 こうなると毎度のことながら、一番に疑ってみるのは自分のプログラムになってしまうですが、結論から言って今のところ疑わしいところは見つかっていません。 DHT11 に密接したところでは、上で示した DHT22/11 制御サブルーチン( DHT22_11.sub )ですが、 DHT22 では正常(たぶん)に動作をしています。 DHT11 と兼用に書き換えたので、 "190. I2C IF モジュール + LCD(1602A) 表示時計" プログラムを、( DHT22_11.sub )を使用してアセンブルをし直して確認をしました。

 上述したように 1x % とか、2x % とかの表示値になる、ということから、bit5 のビット落ちを疑ってみたくなりますが、プログラムがパリティエラーにもならないことから、そうではないらしいし、現にセンサーに近づいて息を吹きかけてやると、 32 % 以上の表示値も示します。

 また、データの変換過程を疑って、バイナリデータから 10 進数データに変換をするサブルーチン( bindec8cz_cnv )も見直してみましたが問題はなく、また、16 進数データにも変換をして確認をしてみましたが、バイナリデータの変換過程に間違いはなさそうです。

 次に疑うのは、ハードウェアとしての DHT11 モジュールですが、3個を購入してあったことから、それらをすべてプリント基板上で交換をしてみましたが、たぶんロットが同じらしく3個とも症状に変化はありませんでした。 (工場出荷前の校正が為されていないのでは? と疑いたくなります。)

 別の DHT11 でも試してみたいとも思いますが、今更、精度の劣る DHT11 を新しく購入することもためらわれるし、こうなると、今のところもう他に為す術もなくお手上げ状態です。 したがって、本機での 湿度の表示は、正常ではない ことは分かっていますが、現在はそのままの状態で放置がしてあります。

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


● 現在のプログラムメモリの使用状況と今後の更新予定

 下の表は、本機で使用している PIC16F1709 の、現在(Ver. 1.03)のプログラムメモリの使用状況を、アセンブルリストから拾って一覧表にまとめたものです。

 薄水色の部分が未使用で空きとなっているメモリですが、残り後僅かなことが分かります。 本機のプログラム機能の構成とプログラミング の項で示した表中の、スイッチ選択番号 9 と 10 は "未定" となっていて、 現在プログラムがありませんが、将来的には何か面白いものを作ってみたい、と思っています。

 しかし、下表に示すようなプログラムメモリの空き状況では、残りが少なさ過ぎてかなり心許なく思います。 下表の薄ピンク色のメモリ部分に注目をしてください。 これらのテーブルデータは、"5. 標準文字フォントのデモ表示" の項の イメージ図 の説明で述べたように、合計 2,580 byte(ワード)にも及び、全プログラムメモリ 4 ページ(2K x 4 = 8K ワード)= 2,048 x 4 = 8,192 ワードの内の、何と 30 %以上を占めています。

        ページ 使用メモリ範囲 サイズ プログラム処理
        0 h'0000' 〜 h'01A8' 425 words   割り込み、外部サブルーチン include エリア、初期化処理等 
        h'01A9' 〜 h'01FF' 87 words   メイン、プログラム処理の振り分け
        h'0200' 〜 h'0296' 151 words   1: 時分秒の表示
        h'0297' 〜 h'02E1' 75 words   2: 年月日(曜日)の表示
        h'02E2' 〜 h'032D' 76 words   3: 温度、湿度の表示
        h'032E' 〜 h'0346' 25 words   4: オートモードの表示
        h'0347' 〜 h'038E' 72 words   11: LED の輝度調節機能
        h'038F' 〜 h'0457' 201 words   12: 年月日,時分秒の設定変更
        h'0458' 〜 h'066F' 536 words   インクリ / デクリメント計算等、ブザー音、ウェイト 
        h'0670' 〜 h'0700' 145 words   各種の演算、データ変換
        h'0701' 〜 h'07FF' 255 words   空き
        1 h'0800' 〜 h'0953' 340 words   5: 標準文字フォントのデモ表示
        h'0954' 〜 h'09B9' 102 words   6: プロポーショナル文字フォントのデモ表示
        h'09BA' 〜 h'09F6' 61 words   7: ひらがな文字フォントのデモ表示
        h'09F7' 〜 h'0EAF' 1,209 words   8: さくらさくら/赤とんぼ
        h'0EB0' 〜 h'0EB2' 3 words   9 〜 10: 未定
        h'0EB3' 〜 h'0FFF' 333 words   空き
        2 h'1000' 〜 h'1051' 82 words   MAX7219 レジスタの基本制御
        h'1052' 〜 h'1416' 965 words   各種文字フォント読み出し、バッファ格納、変換、LED 表示 
        h'1417' 〜 h'1502' 236 words   起動時のドットマトリクス LED のデモ表示
        h'1503' 〜 h'17FA' 760 words   ひらがな文字フォントテーブル(font8x8k.tbl)
        h'17FB' 〜 h'17FF' 5 words   空き
        3 h'1800' 〜 h'1CE1' 1,250 words   標準文字フォントテーブル(Font5x7s.tbl)
        h'1CE2' 〜 h'1F1B' 570 words   プロポーショナル文字フォントテーブル(Font5x7p.tbl)
        h'1F1C' 〜 h'1F53' 56 words   起動時のドットマトリクス LED の9〜0表示テスト
        h'1F54' 〜 h'1F7F' 44 words   空き
        h'1F80' 〜 h'1FFF' 128 words   空き(High-Endurance フラッシュメモリ)

 そこで、これらのテーブルデータを将来的には、PIC のプログラムメモリから追い出して空きメモリを確保し、テーブルデータは外付けの EEPROM 等に収容をしようと考えています。 これらの実現がいつ頃になるかは今のところ未定ですが、ぜひ前向きに考えてみたいと思います。

 また、以下に述べることはプログラムメモリとは関係がなく、データメモリの話ですが、今まで述べる機会がなかったのでここで述べておきます。

 本機では、全体のプログラム規模が、上表で示したようにかなり大きくなってしまったために、それにともなって、プログラム内で使用する一般の変数(レジスタ)の数も多くを占め、現在(Ver. 1.03)のプログラムにおいては、 次のアセンブルリストに示すように、バンク 0 では残りは僅か( h'6A' まで使用しているので 5 バイト )となっています。 ただし、アドレス h'70' 以降の共通 RAM 領域(16 バイト)は、とりあえず空けてあります。

 上述したように、本機のプログラムは今後も大きくなりそうなので、それにともなって一般の変数(レジスタ)も増加することが予想されます。 そのために、本機のプログラムでは実質的に使用するレジスタの数を抑える(減らす)ために、次のリストのように、 同一のレジスタを二重にも三重にも、それ以上にも異なる変数に割り当てています。 それが次のリストの #define 定義文ですが、各変数の使用に当たっては、プログラム内で変数同士が、すなわち同一のレジスタで競合をしないように、細心の注意を払っています。

 また、アドレス h'3D' のレジスタ vhcv_flg のように、元々は 名前が表すように、縦と横のビット変換フラグに使用をしていたのですが、bit7 の 1 ビットしか使用をしていないため、後になって、さくらさくら/赤とんぼ 電子オルゴールで 新たな変数が必要になったときに、 やはりレジスタの数を抑える(減らす)ために、両者には何ら関係はないのですが、変数 vhcv_flg に同居をさせて使用をしています。 このような使い方は、他の人がプログラムを理解する上では混乱を招く恐れもあるのですが、仕方がありません。
      
                            00455 ;==========================================================================
                            00456 ;               定数の定義と変数のレジスタ割付け
                            00457 ;==========================================================================
                            00458 
                            00459 #define         ver_no          "1.03"
                            00460 
                            00461 #define         m_fsr0h         b16_hi                  ;FSR レジスタペア (FSRnH, FSRnL) の退避
                            00462 #define         m_fsr0l         b16_lo                  ;
                            00463 #define         m_fsrnh         a16_hi                  ;
                            00464 #define         m_fsrnl         a16_lo                  ;
                            00465 #define         m_fsryh         buff1                   ;
                            00466 #define         m_fsryl         buff2                   ;
                            00467 #define         m_fsrxh         buff3                   ;
                            00468 #define         m_fsrxl         buff4                   ;
                            00469 
                            00470 #define         lcnt1           lpcnt                   ;16 ÷ 16 = 16 ビットの割り算 (div_16)
                            00471 #define         lcnt2           buff                    ;
                            00472 #define         x16_hi          buff1                   ;
                            00473 #define         x16_lo          buff2                   ;
                            00474 #define         y16_hi          buff3                   ;
                            00475 #define         y16_lo          buff4                   ;
                            00476 
                            00477 #define         rep_addr_high   lpcnt                   ;繰り返し曲データの high アドレス
                            00478 #define         rep_addr_low    buff                    ;繰り返し曲データの low アドレス
                            00479 #define         scale           buff1                   ;音階コードの保存
                            00480 #define         length          buff2                   ;音長の保存
                            00481 #define         tempo           buff3                   ;テンポ指定
                            00482 #define         repeat          buff4                   ;繰り返し指定
                            00483 
                            00484 #define         linear_buff     (h'2000' + 80 * 2)      ;バッファ先頭アドレス
                            00485 
                            00486                 radix   dec
      
      				:
      				:
      
                            00558                 cblock  h'20'           ; (( バンク 0 ))
      
      				:
      				:
      
        0000003D            00641 vhcv_flg                                ;縦横ビット変換フラグ
                            00642                                         ; bit7:   0:縦→横/1:横→縦
                            00643                                         ; bit4:   曲選択フラグ 0:さくらさくら/1:赤とんぼ
                            00644                                         ; bit3-0: 曲速度の変更値 (1 〜 15)
                            00645 
        0000003E            00646 lp_cnt1                                 ;汎用ループカウンタ
        0000003F            00647 lp_cnt2                                 ;汎用ループカウンタ
        00000040            00648 lp_cnt3                                 ;汎用ループカウンタ
        00000041            00649 lp_cnt4                                 ;汎用ループカウンタ
        00000042            00650 lp_cnt5                                 ;汎用ループカウンタ
        00000043            00651 a16_hi         ;m_fsrnh                 ;16ビット演算引き数A(上位) / 結果(上位)
        00000044            00652 a16_lo         ;m_fsrnl                 ;16ビット演算引き数A(下位) / 結果(上位)
        00000045            00653 b16_hi         ;m_fsr0h                 ;16ビット演算引き数B(上位)
        00000046            00654 b16_lo         ;m_fsr0l                 ;16ビット演算引き数B(下位)
        00000047            00655 lpcnt          ;lcnt1,  rep_addr_high   ;ループカウンタ
        00000048            00656 buff    ;(00)  ;lcnt2,  rep_addr_low    ;変換文字列の格納エリア
        00000049            00657 buff1   ;(01)  ;x16_hi, m_fsryh, scale  ;
        0000004A            00658 buff2   ;(02)  ;x16_lo, m_fsryl, length ;
        0000004B            00659 buff3   ;(03)  ;y16_hi, m_fsrxh, tempo  ;
        0000004C            00660 buff4   ;(04)  ;y16_lo, m_fsrxl, repeat ;
        0000004D            00661 work1                                   ;ワーク
        0000004E            00662 work2                                   ;ワーク
        0000004F            00663 work3                                   ;ワーク
      
      				:
      				:
      
        00000066            00694 humidity_h                              ;湿度上位 8 bit
        00000067            00695 humidity_l                              ;湿度下位 8 bit
        00000068            00696 temperature_h                           ;温度上位 8 bit
        00000069            00697 temperature_l                           ;温度下位 8 bit
        0000006A            00698 time_cnt                                ;
                            00699                                         ; bit7: タイムアウトエラー
                            00700                                         ; bit6: チェックサム(CRC)エラー
                            00701                                         ; bit5-0: 時間計測カウンタ 
                            00702                 endc
                            00703 
                            00704 ;               cblock  h'70'           ;バンク 0(共通 RAM 領域)
                            00705 ;               endc
      
      				:
      				:
      
 なお、上リストのコメントにある、サブルーチン 16 ÷ 16 = 16 ビットの割り算(div_16)の実体は、現在(Ver. 1.03)のプログラムでは予定変更で使用はしていませんが、プログラム内に残してあるのでいつでも使用が可能です。

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

現在の最新バージョン: Ver. 1.03

| ページトップ |

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

 使用したプリント基板は、"秋月電子通商" の "片面ガラス・ユニバーサル基板 Bタイプ(95 x 72 mm) めっき仕上げ (通販コード P-00518 )" を使用しました。 次図に示すように、まず、横 x の ----- 線の位置で切断をし、次に、縦 Y1、Y2 の ----- 線の位置で切断をします。 大きい方がメイン基板となり、小さい方がスイッチ用基板として使用をします。 それぞれの基板の2か所ずつにΦ3.2 の丸穴を追加して開け、スペーサとともにビス、ナットでケースに固定をさせます。

| プリント基板部品配置図 (MatrixLED_ControllerPC0.CE3) | ページトップ |

■ プリント基板パターン図 (部品面) ■

 下図の中で、長方形の中にで表したものは通常のストレート型のピンヘッダ(オス)で、同様にで表したものは ピンソケット(メス)を使用することを表しているので、注意をしてください。


上図から黒文字とダイオードを除去して表示

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

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


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

■ ロータリスイッチの配線 ■

 下図は ALPS 社のデータシートから抜粋をしたもので、SRRN シリーズ 1 回路 12 接点のロータリスイッチを本機で使用しました。 20 年近く以前に "千石電商" で購入をしたものです。 本スイッチは、一見、端子がバラバラの位置に配置されているようで、慣れないと分かり辛いのですが、 左端図(軸とは反対の裏側から見た図)で 3, 6, 9, 12, 15 番端子が抜けて無いもので、また、形状が他の端子と異なった 18 番端子がコモン端子となっています。


 このロータリスイッチとフラットケーブルをどのように配線をするかを少々悩みましたが、下写真のようにスイッチ回り止めの突起物を右側(上左端図と同位置)の位置にして、まず、フラットケーブルの 1 番線(緑色)をロータリスイッチの 18 番端子のコモン端子と配線をします。 次に、フラットケーブルの 2 番線をロータリスイッチの 1 番端子、フラットケーブルの 3 番線をロータリスイッチの 2 番端子、フラットケーブルの 4 番線をロータリスイッチの 4 番端子、・・・、以下同様に、順にフラットケーブルの 13 番線をロータリスイッチの 17 番端子まで配線をします。 (上述のように、ロータリスイッチの端子は 3, 6, 9, 12, 15 番端子が抜けて連番ではないので注意)


 以上、フラットケーブルの 1 番線 〜 13 番線までの 13 本をすべて配線をしますが、説明の便宜上、フラットケーブルの 1 番線 〜 13 番線としましたが、回路図プリント基板パターン図 (部品面) に示すように、 実際には、G (GND) , 1 〜 12 ですのでこれにも注意をしてください。

| ページトップ |

■ 電源周りの様子 ■

 本機におけるDCジャックや電源スイッチなどの電源周りの様子を、いろいろな方向、角度から写した写真で次に示しておいたので参考にしてください。


 DCジャックの取り付けには、別ページ "173. 6桁ニキシー管時計(改良版)" の DCジャックの取り付け の項で、詳細な説明をしています。 ただし、取り付け用金具のサイズを、本機では(サイズ: 23 x 34 x 1 mm 厚)と一周り小さくしました。

 また、電源スイッチの取り付けについては、上写真のように、パネル取り付け用ではなく、プリント基板に取り付け用のトグルスイッチを使用しました。 右端の裏面から見た写真を参考にしてください。

| ページトップ |

■ ケース加工図 ■

 本機では、主役となる4連ドットマトリクス 8 x 8 LED モジュールを2個横に直列に並べるため、その横幅サイズが 270 mm 近くにもなって大きく、100 均等の既製のケースでは、収納ができるようなサイズのポリスチレン等のケースを見つけることはできませんでした。

 そこで、既製のケースを使用することはきっぱりと諦めて、それに代わるものとしてケースではないのですが、次図に示すような形状で本機をまとめることにしました。

 L 型アルミアングルをベースとしてアルミ板とともにシャーシを作り上げ、それらに各パーツ類を取り付けて全体を構成させています。 したがって、全体をケースで覆っていないため埃などに弱くなるのは仕方がありません。 また、外圧等も直接各パーツ類に加わることになるので、 それらの点でも本機の取り扱いには注意が必要となります。


・上図で指定のない小さな○穴はすべてΦ3 を示す。 ただし、4個のプラスチック足用の○穴はΦ2.6 でそれらを除く。
・シャーシの組み立てには、各種長さの M3 ビス、ナットを使用しているが、上図ではそれらを省略。
・ロータリスイッチの取り付け部分は、ベースとなるアルミ板の高さを嵩上げして、アルミ板下に十分な空間を確保して取り付け。
・( 注1 ) は 0.5 mm 厚の平ワッシャー2枚を使用してアルミ板厚を代替。
・( 注2 ) は 0.5 mm 厚の平ワッシャー2枚を使用してアルミアングル厚を代替、および PW SW 用プリント基板の上面を2枚、下面を1枚の 0.8 mm 厚のポリカーボネート平ワッシャーで挟んで絶縁。
・L 型アルミアングルは 1.2 mm 厚、アルミ板は 1.0 mm 厚のものを使用。
・使用したスペーサは 5 mm 長と 10 mm 長の2種類で、ともに中空の外径Φ6 のもの。 縦位置の L 型アルミアングル4本の両端計8個が 10 mm 長で、他のものはすべて 5 mm 長を使用。

| ケース加工図 (MatrixLED_ControllerCS.CE3) | ページトップ |

■ 使用部品表 ■

(主要部品: IC, トランジスタ等)

(データシート)
PICマイコン .................... PIC16F1709
4連ドットマトリクス 8 x 8 LED モジュール .................... ( MAX7219 )
RTCモジュール .................... ( DS1307 )
デジタル温度湿度センサ .................... DHT11

| 部品表 | Excel ファイル (MatrixLED_Controller_parts.xls) | ページトップ |

■ 参考資料・サイト ■

PIC16F1709 データシート .......... https://akizukidenshi.com/download/ds/microchip/pic16(l)f1705_1709.pdf
MAX7219 データシート(日本語版) .......... https://akizukidenshi.com/download/ds/maxim/max7219_max7221_j.pdf
DS1307 データシート .......... https://datasheets.maximintegrated.com/en/ds/DS1307.pdf
DHT11 データシート .......... http://akizukidenshi.com/download/ds/aosong/DHT11_20180119.pdf
F Fityle MAX7219 5ピンケーブル付きArduinoラズベリー用ドットLEDマトリックスMCU制御LEDディスプレイモジュール
Rasbee DS1307 RTC クロックモジュール I2C 24C32メモリ リアルタイムクロック モジュール
Rasbee DHT11 温度と湿度センサー デジタル出力 DHT-11 DHT22 並行輸入品

| ページトップ |

■ Microsoft 社に物申す … Windows 11 のメモ帳 ■

 今年(2022 年)の 3 月の中頃に、Windows 10 から Windows 11 へと無償アップグレードを行いました。 私はパソコンを PIC のプログラム開発と、ホームページの作成を主な目的として使っており、それらのエディタにはメモ帳(Notepad)を、以前からずっと使い続けてきました。 Microsoft 社のアプリでは、他に、これらの資料作りのために、Excel を初めとして、ペイント、電卓、フォトなどをよく利用しています。

 これらの中でもメモ帳だけは特別で、私にとっては無くてはならない存在となっていますが、この Windows 11 のメモ帳( 11.2112.32.0 )は実に酷い駄作アプリで、Microsoft 社がよくもまあ "こんなものを世に出した" ものだ、と思えるほどの程度の低さで、 アップグレード前の Windows 10 のメモ帳を返して欲しい。

 以前のメモ帳は私にとっては何の不満もなく、PIC アセンブラでソースプログラムを作るときも、ホームページの HTML ファイルを編集するときも、私にとってはいつも信頼のおける良き相棒でした。 私は少し大きめのテキストファイルを取り扱うことが多いのですが、 以前のメモ帳は実に軽快に動いてくれ、私も全面的に信頼をしてきました。

 ところが Windows 11 のメモ帳は、動きは緩慢(何かの設定で変更ができるのでしょうか?)で、垂直のスクロールなどでは、右端のスクロールバーは小さくて使い辛いため、タッチパッド上を人差し指と中指の2本で上下にスライドさせるのですが、そのスクロール動作の重いこと。 以前では、垂直のスクロールで不満を感じたことはありませんでした。

 また、水平のスクロールについては最低で、テキストの横幅サイズが画面の横幅よりも大きい場合に、垂直のスクロールのときと同様に、下端のスクロールバーは小さくて使い辛いため、タッチパッド上を人差し指と中指の2本で左右にスライドさせるのですが、 左方向にスクロールさせようとしたときにはまったく無反応で機能せず、右方向にスクロールさせようとしたときには画面が左右に、小刻みに、数秒の間、揺れ動き続けるのです。 一体これは何なのでしょうか? この揺れ動いている間は、当然ながら編集作業は続けられないため、 中断を強いられることになります。

 このようなことは、以前のメモ帳ではまったく起こらなかったことで、Windows 11 のメモ帳を使うようになって初めて経験をすることです。 これらの水平のスクロールについては、メモ帳の作者 = プログラマーが、プログラムの動作テストをしっかりと行っていれば、 すぐに気が付くはずです。

 上述のように、私は PIC アセンブラによるプログラム作成のため、TAB コードを多用しています。 ところが新しいメモ帳は、この TAB コードをうまく処理することができない。 以前のメモ帳では 8 タブで奇麗にカラムを揃えて表示をしてくれました。 Microsoft さん、製品の出荷前にも ちゃんとプログラムテストを行っていますか?

 Windows 11 へアップグレードを行ってから約1か月が経ちますが、現在、開発中のプログラムとホームページがあるために、 この1か月を、毎日のようにメモ帳を使い続けていますが、この新しいメモ帳にはいつもイライラさせられっぱなしで、精神的にもよくありません。

 また、この1か月の間にこんなことが1度だけありました。 それは、右上の「 X 」ボタンや、ファイルメニュ―の「終了」をクリックしていないにも関わらず、メモ帳が突然、勝手に終了をしてしまいました。 まるで、大昔の Excel のようです。(大昔の Excel にはよく泣かされたものです。)

 そのような大昔の Excel の経験を持つ私は、今でも他人よりは多くコマメにセーブをしていますが、このときには油断をしていたわけではないのですが、10 分間ほどの編集内容が吹っ飛び消え去りました。 こういうことは信頼に関わる問題で、新しいメモ帳を使っていて不安にさえさせられます。

 編集メニュ―の「検索する Ctrl + F」「置換 Ctrl + H」などでは、その条件を入力するためのボックス(?)が表示されますが、これがまた、私にとっては実に邪魔な位置に現れて、しかも、固定されているので移動させることができない。 そのために編集作業が妨げられて、イライラが募るばかり ・・・

 その「置換 Ctrl + H」ですが、新しいメモ帳の機能は最悪で、1件ずつ置換をして行くときはいいのですが、置換をする対象が複数ある場合に "すべて置換" をクリックした途端、あっ、と目を疑うような光景が現れます。 今まで編集してきたテキストが ぐちゃぐちゃ。 その結果を 怖くてとてもセーブすることはできません。 ひょっとしたら、これは表示上だけかもしれないですが、とにかく結果を怖くてセーブすることはできないので、そのままで一旦メモ帳を終了させることに ・・・

 他に、こんなこともあります。 以前のメモ帳では、編集メニュ―の「元に戻す Ctrl + Z」は、直前の変更内容だけを対象として、クリックをする度にトグル動作をしていたはず。 それに対して新しいメモ帳では、どんどん過去に遡って元に戻してしまう。 これはメモ帳の仕様が変わったのでしょうか?

 以前のトグル動作を期待していた私は、そのとき一瞬何が起こったのか分かりませんでした。 えっ、えっ、ていう感じです。 こういった基本的な仕様の変更は戸惑うばかりで、はっきりいって迷惑の何物でもない、と思います。

 以上のように、Windows 11 へアップグレードを行って以来、約1か月間の私の新しいメモ帳の使用経験について、不満ばかり(まだ、他にもある)を書き連ねてきましたが、私が Microsoft 社に願うことは、1日でも早く以前のような健全なメモ帳の姿(機能)に戻して欲しい、 ということです。

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


Copyright (C) 2022-2023 やまもとみのる
初版:2022年4月25日、初公開:2022年4月25日、最終更新:2023年10月26日