| ホーム | 私の電子工作作品集 |
[ 初公開日: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 そのものは白っぽくその周りが赤色で滲んだように写ってしまい、実際に目で見る様子とはまったく異なっています。 いつもどのようにして撮ればいいのか悩みますが、実際の発光色は、この ページトップ に掲載をした写真に近く、もっと濃いオレンジ色で周りの滲んだ赤色はなく、すっきりと見えています。
◎ 11. LED の輝度調節
|
| ページトップ |
● 事前に必要な予備知識など ◎ 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 ファミリーでは、データメモリはもちろんですが、プログラムメモリ内に定義をしたデータにも、間接アドレス指定でアクセスをすることができます。 しかも FSR0H と FSR0L 、FSR1H と FSR1L (TABLE 3-2 を参照)の2組が用意され、INDF0、INDF1 レジスタが これらのレジスタペアに対応した、間接レジスタ となっています。 これらのレジスタペアを使用した間接アドレス指定の一例を次に示します。 この例では、リニアメモリバッファ( 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 までの各スイッチに、プログラムの機能別に与えた名称です。 このように、同位置のスイッチでも、実行をするプログラムの機能によって名称が変更するので、次表のように読み替えが必要となります。
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マスごとに当てはめて行けば、全体としての文字列(メッセージ)が 出来上がります。 たとえば、アルファベット '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 の変換を参照)
次に示すリスト中で、変換フラグ: 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 本機ではプロポーショナル文字フォント、標準文字フォントの他に、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 )内で使用されている、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 ◎ メイン、プログラム処理の振り分け
;========================================================================== ;========================================================================== ; メイン・ルーチン ;========================================================================== ;========================================================================== ; 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 版モジュール を使用するべきです。
;========================================================================== ; 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_process、3. 温度、湿度の表示 の temp_humi_disp_process でも同様です。 また、各スイッチの中で EXIT スイッチ処理ルーチン( tm_exit_switch )だけを取り上げて、ここに示したのには訳があります。 それは EXIT スイッチは 長押し をするように想定をしていますが、何の考慮もないと、その押している間は "秒" の表示の更新が止まってしまって異様な感じを受けます。 それを避けるために、EXIT スイッチ処理ルーチン( tm_exit_switch )内にも、時分秒を表示するサブルーチン time_part_display を挿入して、 "秒" の更新が止まらないようにしています。 ただし、3 秒の経過後は逆に更新を止めて、 これ以上 長押し する必要はない、とユーザにサインを送っています。 なお、本項の 機能の概要 の冒頭で、"・・・ 図 中の 時分秒表示 を表示テンプレートとして" という部分がありますが、12. 年月日、時分秒の設定 の "機能の概要" の下に説明があるので参考にしてください。 | プログラムのトップに戻る | ◎ 2. 年月日(曜日)の表示
;========================================================================== ; 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 版モジュール をお勧めします。
;========================================================================== ; 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. オートモードの表示
;========================================================================== ; 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. ひらがな文字フォント の、 各デモ表示プログラムのすべてに当てはまり同様に機能をします。
次の図は、この項の 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" を使用しています。
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 で利用することはできません。
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文字ずつ表示する、ディスプレイ機能を追加した点です。
それに対して当プログラムでは文字スクロールのようなことはしないので、歌詞の表示のとき には バッファエリア( 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. 電子オルゴール" の 曲データの作り方 の項を参照してください。)
ちなみに、"ひらがな文字フォント" は次のように定義がされています。 ;===== 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 )" のデータを、次々に読み進めるのですが、この次々に ... のためにプログラムでは、 進行途中のこれらのテーブルのアドレスを、次はどれか、を常に把握(記憶)しています。 曲名の表示の場合には、リニアバッファエリア( 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 に高輝度でないものを使用した場合には、輝度レベルを調節できる機能も必要になってきます。
;========================================================================== ; 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 モジュールを使用していることもあって、時間の経過とともに目立つような狂いを生じてくるために、初期設定のときと同様に、年月日、時分秒の設定をし直す必要も起こります。
これらの "年" "月" "日" とか "時" "分" "秒" のフォントデータを、実際の バッファエリア( 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 から抜粋をしました。
次に示す各図も、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 ====== | プログラムのトップに戻る | ● デジタル温度湿度センサーモジュールのプログラミング 上述の、3. 温度、湿度の表示 の項の冒頭、および "本機で使用した各種モジュールについて" の デジタル温度湿度センサー( DHT11 )モジュール の項で述べたように、 本機では、 デジタル温度湿度センサーモジュールに DHT11 版モジュール を使用しています。 次に示す各図は、AOSONG 社のデータシート DHT11 から抜粋をしたもので、実際に デジタル温度湿度センサーモジュールのプログラミングをするときに必要になりますが、これらのタイミング図の中で、 "190. I2C IF モジュール + LCD(1602A) 表示時計" 等で使用した、DHT22 と異なっているところは、初めに PIC から送られる スタート信号 の長さが、DHT22 では 1 mS、DHT11 では 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 ====== 湿度上位 8 bit、湿度下位 8 bit、温度上位 8 bit、温度下位 8 bit、チェックサム 8 bit また、これらのデータを取り扱う上で、両者では大きく異なっている点があります。 それは 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 %以上を占めています。
そこで、これらのテーブルデータを将来的には、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 厚)と一周り小さくしました。 また、電源スイッチの取り付けについては、上写真のように、パネル取り付け用ではなく、プリント基板に取り付け用のトグルスイッチを使用しました。 右端の裏面から見た写真を参考にしてください。 |
| ページトップ |
| ケース加工図 (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 並行輸入品 |
| ページトップ |
今年(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日でも早く以前のような健全なメモ帳の姿(機能)に戻して欲しい、 ということです。 |