| | ホーム | 私の電子工作作品集 |
| [ 初公開日:2025年8月6日 ] |
|
時計関連の作品を製作する場合に、最近の私は、リアルタイムクロック(RTC)として、DS3231 を使用したモジュールを採用することが多いのですが、"195. ドットマトリクス LED コントローラー" の
本機で使用した各種モジュールについて の項で述べたように、DS3231 版と比べると精度が少し低い、と言われている DS1307 版のモジュールも数個を所有しており、
その使い道(使用先)に何か良いアイデアはないものかと、以前から常に思い巡らせていました。 また、先に公開した "201. 有機 EL ディスプレイ(OLED) 表示 万年カレンダー II"、"202. 有機 EL ディスプレイ(OLED) 表示 万年カレンダー III" で使用した、128 x 64 ドットの OLED モジュールの他、128 x 32 ドットという半分サイズの OLED モジュールも同時期に購入がしてありましたが、中途半端なサイズなこともあってなかなか出番がありません。 もう一つ、当初、始めて使用する PIC16F1 ファミリでは 14 ピン PIC に拘って、PIC16F1705 と PIC16F18325 の2種類の PIC を購入してあったのですが、"194. FMステレオラジオ III" の製作では PIC16F1705 を採用したこともあって、 それ以来、14 ピンでは PIC16F1705 ばかりを使用してきて、PIC16F18325 は未使用のままでした。 そこで、このように使用頻度が少なかった、あるいはまったく使用してこなかったこれら3者を、本機 "キッチン・タイマー II" で一気に融合させてみよう、という試みです。
本機では、DS1307 版 RTC モジュールの時計情報(年、月、日、時、分、秒)は一切使用しません。 モジュールから1秒ごとに出力される SQ 信号だけを PIC が受けて、タイマーのクロック源としています。 本機で設定できるタイマーの設定値は最大 99 分 59 秒までの範囲 としているため、クロック源としては十分な精度として使用ができます。 本機のプログラムを更新 ( 2025/10/20 更新 ) 以前から PIC16F1 ファミリでプログラムを作成する場合に、PORTx レジスタと LATx レジスタの取り扱いについて、理解不足で不適切な点がありましたが、この度、その点についてのプログラムの更新を行いました。 詳細は プログラム の項の PORTx レジスタと LATx レジスタの取り扱い をご覧になってください。 |
![]() |
||
| *注. 入力ポート RA0, RA1, RA4, RA5, RC4, RC5 は、内部プルアップ機能を ON にして使用。 また、SLEEP 中には それらのポート に状態変化割り込み機能を使用。 | ||
| 上に示した回路図はもちろん本機 "キッチン・タイマー II" の回路図ですが、これは前作の "202. 有機 EL ディスプレイ(OLED) 表示 万年カレンダー III" の 回路図 で示したものと比べて、 使用した PIC や OLED、RTC の各モジュールに違いはありますが、全体の回路構成はほとんど同じで違いはありません。 |
| 回路図 (KitchenTimerII.CE3) | ページトップ |
|
本機においても上回路図に示すように、3つのモジュール(黄色に塗りつぶしたもの)を使用しています。 これらのモジュールについて、私が感じた問題点やその他の注意点などを交えて、次に順に挙げていきます。
● 有機 EL ディスプレイ(OLED)( SSD1306 )モジュール 冒頭でも述べたように、"201. 有機 EL ディスプレイ(OLED) 表示 万年カレンダー II"、"202. 有機 EL ディスプレイ(OLED) 表示 万年カレンダー III" では、左下の写真に示す 128 x 64 ドットの OLED モジュールを使用しましたが、本機では右下の写真に示すような 128 x 32 ドットという半分サイズの OLED モジュールを使用 しました。 これらのモジュールは、私がアマゾンの中華モールなるものの存在を知ってから初期の 2020 年 8 月ごろに、他の複数種類の異なった OLED モジュールとともに購入したもので、もう5年も以前のことになります。 どちらの OLED モジュールにも SSD1306 という同じ制御チップが使われていて、インタフェースには I2C 通信と SPI 通信の2種類のものが存在しますが、どちらも I2C 通信の方を私は選択購入をしてありました。
外観は上写真のようなものですが、実際のサイズはかなり小さなもので、左のモジュールは 0.96 インチ、右のモジュールは 0.91 インチと商品説明には書かれています。 実際に文字等を表示をさせてみても小さ過ぎる感が強く、実用的にはもっと大きな表示器を選択した方が 良いと思いますが、今回は右側のモジュールを使用しました。 また、このモジュールについても、Arduino や Raspberry Pi 用には各種のライブラリが存在しているようですが、本機のように PIC(特にアセンブラ言語)を使用した場合には、既成のライブラリの存在はまったく期待することはできません。 したがって、プログラム面においてはすべてを自分自身で作成(万年カレンダー II で自作のライブラリを作成済み)することになります。 ● リアルタイムクロック( DS1307 )モジュール リアルタイムクロック(RTC)モジュールに関しても冒頭でも述べたように、最近の私が時計関連の作品を製作する場合には、そのときの私の手持ちの都合で次の写真に示すような、2種類の DS3231 版の RTC モジュールを採用してきました。 DS3231 版の RTC モジュールであれば、私が使用した経験上、どちらの RTC モジュールを採用しても、時計としてはかなりの精度を期待することができます。(ただし、"DS3231 For PI" を使用する場合には、SQW 信号を外部に取り出すための改造が必要。)
これらの DS3231 版の RTC モジュールの他にも、次の写真に示すような DS1307 版の RTC モジュール を、数個を私は所有していますが両者を比較した場合に、一般的に後者の RTC モジュールは精度が少し低い ― と言われている通り、 私の一度だけの使用経験* ですがその誤差は酷いもので、時計としての作品には採用すべきではありません。 私がこれらそれぞれの RTC モジュールの特徴など違いを良く知らなかった 2020 年 9 月ごろに、アマゾン内のあるショップで、DS1307 版があまりにも安価(@ 83 円)だったので、5個(配送料 69 円)を購入してありました。 したがって1個は既に使用済みですが、 今後の時計としての作品には使用ができない(使用したくない)、DS1307 版の RTC モジュールが4個も在庫があるわけで、何か他に使い道(使用先)はないものかと、日頃から常に思い巡らせていました。
私のホームページ上には、"キッチン・タイマー" や "ラーメン・タイマー" の類のコンテンツを、現在、数件を公開していますが、それらはどれもまずまずの人気を得ているようです。 中でも "153. キッチン(カウントダウン)・タイマー" は結構な需要があるようですが、公開をしてから既に 12 年も経とうとしており、途中で一度、プログラムの更新を計ってはいますが、数ある私の PIC 作品の中でも比較的初期に近いころの作品でもあるため、自分自身がプログラミング的に今一に感じるところもあります。 そこで ― というわけでもないのですが、本ページの 冒頭 で述べたように、DS1307 版 RTC モジュール、128 x 32 ドット OLED モジュール、PIC16F18325 の3者を組み合わせた、"キッチン・タイマー" の製作をしてみようと思った次第で、 ハードウエア構成も、"153. キッチン(カウントダウン)・タイマー" とは、まったく異なったものとなりました。 本機では時計とは異なるため、RTC モジュールからの延々と続く時計情報(年、月、日、時、分、秒)は必要がなく、それらを使用することは一切ありません。 モジュールから1秒ごとに出力される SQ 信号だけを、 PIC の外部(INT)割り込みで受けて、タイマーとしてのクロック源とします。 したがって、時計とした場合には精度が低い時計情報は、時間が経てば経つほどその誤差の累計は大きくなって行きますが、1秒ごとの SQ 信号だけを利用する本機のようなタイマーの場合には、最大でも 100 分ほどの短時間のカウンタのために、 精度が低い DS1307 版 RTC モジュールでも、その影響を受けることはほとんどありません。 また、時計情報(年、月、日、時、分、秒)をまったく使用しない本機の使用では、電源を断にしたときに備えての バックアップ用の電池も必要がありません。 このモジュールを本機で使用するに当たっては、右上写真のような位置に、ストレート型の 7 ピンヘッダを取り付けて使用しました。 このモジュールにはピンヘッダは付属していないので、自前で用意をする必要があります。 プリント基板 側に取り付けたピンソケット(メス)で受けて接続をします。 ● TP4056 リチウムイオン電池充電モジュール 本機を使用するための電源としては、最近の私の製作物ではお馴染みとなった 18650 リチウムイオン電池を、やはり本機でも採用することにしました。 そして、18650 リチウムイオン電池を使用する場合に、私は生セルタイプを使用することが多くなるため、 電池を充電することはもちろんのこと、使用時に過放電(2.4V)、過電流から保護するように、安全面からも "TP4056 リチウムイオン電池充電モジュール" なるものが必須となります。 "TP4056 リチウムイオン電池充電モジュール" として、私は以前には、Micro USB タイプ のモジュールを使用していましたが、最近になって、タイプ C の USB コネクタを使用したモジュールを入手する機会を得たので、 本機では後者のモジュールを使用しました。 前者で使用している USB コネクタは使用時の外力には弱く、容易にモジュール基板から剥がれてしまうことも予測されますが、後者のモジュールでは、USB コネクタがスルーホールでがっちりと取り付けられているため、 使用時に破損させてしまう心配がありません。
タイプ C の USB コネクタを使用したモジュールは、アマゾン等を覗いてみると、Micro USB タイプのモジュールに比べて、かなり高価で販売されているショップを多く見かけますが、私は Temu で安価なショップを見つけたので購入をしてみました。 ちなみに、2024 年 6 月現在において 15 個セットで 445 円でした。 1個当たり 30 円弱ですが、アマゾン等で販売している他のショップでは、これほどの安価な充電モジュールを見かけたことはありません。 願わくは、不良品が混在していないように!と祈るばかりです。 なお、"TP4056 リチウムイオン電池充電モジュール" の機能等については、"197. 18650 リチウムイオン電池充電器" の TP4056 リチウムイオン電池充電モジュール で詳述しているので、そちらを参照してください。 また、上写真のように両者を見比べてみると、購入ショップが異なるためかメーカーも恐らく異なっているらしく、両者では微妙に使用部品の配置や回路構成なども、若干ですが異なっている様子が窺えます。 | ||||||||||||||||||||||||||||
| ページトップ |
以下は外観6面写真です。
|
| ページトップ |
![]() | 機能概要のトップに戻る |
CANCEL LEFT DOWN UP RIGHT START ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ │●│ │●│ │●│ │●│ │●│ │●│ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ / MAINTE / NEXT / PREV. / SELECT NEXT スイッチ ..... 選択位置のポインタを + 1 PREV. スイッチ ..... 選択位置のポインタを - 1 SELECT スイッチ ..... 選択位置のカウンタ機能へ移行 UP スイッチ 長押し ..... UP カウンタ機能へ移行(3秒以上の長押し) DOWN スイッチ 長押し ..... DOWN カウンタ機能へ移行(3秒以上の長押し) DOWN スイッチ 長押し + UP スイッチ ..... Down + Up カウンタ機能へ移行(3秒以上の長押し) MAINTE スイッチ 長押し ..... メンテナンス機能に移行(3秒以上の長押し)
| 機能概要のトップに戻る |
CANCEL LEFT DOWN UP RIGHT START ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ │●│ │●│ │●│ │●│ │●│ │●│ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ / MAINTE / NEXT / PREV. / SELECT RIGHT スイッチ ..... ブリンク位置のポインタを + 1 LEFT スイッチ ..... ブリンク位置のポインタを - 1 UP スイッチ ..... ブリンク位置の値を + 1 DOWN スイッチ ..... ブリンク位置の値を - 1 START スイッチ ..... 各カウンタの動作を開始 CANCEL スイッチ ..... 「3種類のカウンタ」画面の表示を終了し、 「カウンタ種類の選択」画面へ戻る
| 機能概要のトップに戻る |
| 機能概要のトップに戻る |
CANCEL LEFT DOWN UP RIGHT START ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ │●│ │●│ │●│ │●│ │●│ │●│ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ / MAINTE / NEXT / PREV. / SELECT NEXT スイッチ ..... OLED コントラスト / スリープまでの移行時間 の選択 PREV.スイッチ ..... OLED コントラスト / スリープまでの移行時間 の選択 UP スイッチ ..... ブリンク位置の値を + 1 UP スイッチ 長押し ..... ブリンク位置の値を + 1 早送り UP スイッチ + DOWN スイッチ ..... ブリンク位置の値をデフォルト値に設定 DOWN スイッチ ..... ブリンク位置の値を - 1 DOWN スイッチ 長押し ..... ブリンク位置の値を - 1 早送り DOWN スイッチ + UP スイッチ ..... ブリンク位置の値をデフォルト値に設定 SELECT スイッチ ..... メンテナンス機能を終了し、メイン(カウンタ種類の選択)に戻る CANCEL スイッチ ..... メンテナンス機能を中断し、メイン(カウンタ種類の選択)に戻る
| 機能概要のトップに戻る | ページトップ |
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
本機では、 PIC には 14 ピンの PIC16F18325、有機 EL ディスプレイ(OLED)モジュールには 128 x 32 ドットサイズ、リアルタイムクロック(RTC)モジュールには DS1307 版、というラインナップで構成をしているのが特徴的と言えます。 このページの冒頭でも述べたように、私にとって、PIC16F1 ファミリの中でも PIC16F18325 は初めて使用する機種(型番)であり、私が今まで使用をしてきた PIC16F1705、 PIC16F1709、PIC16F1827 などとは、ずいぶんと使用感?が異なっていました。 PIC の基本となるシステムクロックの指定(選択)方法とか、タイマー0の使用方法とかが、今まで私が使用してきた PIC16F1 ファミリ とはまったく異なっています。 本来であれば、これらについての具体的な解説を本項でするのが良いとは思うのですが、 残念なことに、私自身がそれほど十分に理解ができた ― とは言えないのが現状なので、今は解説をするのは控えさせていただきます。 いつの日か、それができる日が来るかもしれませんが ・・・
リアルタイムクロック( DS1307 )モジュールのプログラミング では、DS1307 版モジュール を使用する上での基本を解説していますが、本機ではモジュールの初期設定をしているだけで、時計情報(年、月、日、時、分、秒)は必要ないので、 一切それを使用することはありません。 モジュールの初期設定時に、Seconds Register (00h) の CH(Clock Halt) bit を 0 にクリアして発振器を有効にし、Control Register (07h) の各 bit で 1Hz の矩形波を出力するように設定しています。 このとき、時計情報も一緒に初期設定されますが、本機では無効な動作となります。 A / D コンバータ モジュールのプログラミング では、PIC の内部モジュールを使用して、本機で使用している電源 18650 リチウムイオン電池の、現在電圧値を表示します。 この機能に関しては、ハード、ソフトウエアともに、 "202. 有機 EL ディスプレイ(OLED) 表示 万年カレンダー III" のものと、まったく同じものを使用しました。 ● 本機で使用した PIC16F18325 のメモリの使用状況 最近の私作の規模が大きいプログラムでは恒例となった、次に挙げるように、データメモリやプログラムメモリの使用状況についてを、ここに示しておきます。 本機のプログラム全体を理解する上で、データメモリの内の特殊機能レジスタ (SFR) や汎用レジスタ(GPR) には、プログラムでどのレジスタにアクセスをしているかとか、4ページあるプログラムメモリの使用状況がどのようになっているか、 などの情報が事前に分かっていると、他の人はもちろんのこと、作者の自分自身でも数か月以上が経ってしまったときには、非常に役に立つものです。
次の TABLE 4-2 は、Microchip Technology Inc. 社の PIC16F18325 データシートから抜粋したもの、その右横に示す PIC16F18325 DATA MEMORY MAP (BANKS 0-7, 17-18) は私が作成した MEMORY MAP で、 プログラムで一度でもアクセスをしたレジスタについて、それらがよく分かるように彩色を施しました。 実は、前作の "202. 有機 EL ディスプレイ(OLED) 表示 万年カレンダー III" の プログラム の項で示した、 PIC16F1705 データシート内の TABLE 3-3 に相当する MEMORY MAP が、PIC16F18325 データシート内には存在しないため、PIC16F18325 データシートの "TABLE 4-4: SPECIAL FUNCTION REGISTER SUMMARY BANKS0-31" を基にして私が作り出しました。 このように、特殊機能レジスタ (SFR) の一覧が一目で見渡せるのは、個々の (SFR) を把握するのに非常に便利となります。 この MEMORY MAP で、黄色や青色、ピンク色に塗ったレジスタが 特殊機能レジスタ (SFR) で、これらの内で青色のものは、EEPROM メモリにアクセスをするときに必要になるレジスタ群、ピンク色のものは、 ADC モジュールのプログラミングに必要となるレジスタ群です。 また、薄緑色のレジスタ群は特殊機能レジスタ (SFR) ではなく、一般の変数など、ユーザが自由に使用できる 汎用レジスタ(GPR) を表しています。
なお、本機のデバッグ中にはこれらのレジスタの他にも、BANK 31 に存在している スタックポインタ STKPTR レジスタ を、PIC16F1 ファミリではユーザがアクセスすることが可能で、内容を確認する必要に迫られてディスプレイ(OLED) 画面に表示をさせました。 | プログラムのトップに戻る |
次表は、本機(プログラムバージョン: Ver. 1.04 現在)における PIC のプログラムメモリの使用状況を、各処理別に分けたメモリ分布(割り付け)をページごとに表したもので、ご覧のようにページ 0 以外のメモリでは、まだまだ十分な空きが残っています。
| プログラムのトップに戻る | 本機では、横 128 x 縦 32 ドット の OLED モジュールを使用し、下図に示すような画面レイアウトに従って各種の情報を表示させています。 まず、メインとなる DOWN カウンタ、または UP カウンタの内容を、画面の左上 1/4 の部分に LARGE 数字文字で表示をさせています。 ・ DOWN カウンタの画面レイアウト 次に、画面の右上 1/4 部分の1行目にカウンタの種類の略名(Dw Counter)を表示し、2行目には本機で使用している電源 18650 リチウムイオン電池の、現在電圧値を表示させます。 カウンタ機能には直接関係はありませんが、電池の充電時期の判断に使用します。
・ UP カウンタの画面レイアウト 同様に、画面の右上 1/4 部分の1行目にカウンタの種類の略名(Up Counter)を表示し、2行目に電源 18650 リチウムイオン電池の、現在電圧値を表示させるのも同じです。
・ DOWN + UP カウンタの画面レイアウト 画面の右上 1/4 部分の1行目にカウンタの種類の略名(+Up)を表示し、その右横にはメインの DOWN カウンタと並行して、同時カウントをする UP カウンタの内容を表示させます。 2行目に表示させるのは、上記2つのカウンタの場合とまったく同様です。
そして、すべてのカウンタ画面において、画面の上下 1/2 の下部分には DOWN カウンタ、または UP カウンタの内容 "分秒" の内 "秒" が1秒ごとに更新して行く様子を、バーグラフで表示させています。 3行目すべてを使用して刻々と変化する "秒" の更新だけを表します。 最下行4行目の数字は 10 秒ごとのガイドの表記です。 | プログラムのトップに戻る | 次に示す緑色リストのサブルーチン群は、元々、"201. 有機 EL ディスプレイ(OLED) 表示 万年カレンダー II" の、プログラムを作成するために必要となる SSD1306 用コマンドに限ってを、SSD1306 データシートの 9 COMMAND TABLE (P 28/59) の中からいくつか選択して、私がサブルーチン集としてまとめ上げたものです。 このときに使用した OLED モジュールは、横 128 x 縦 64 ドットのモジュールでしたが、本機で使用する OLED モジュールは、横 128 x 縦 32 ドットのため、そのままでは使用することができません。 そこで、どちらのモジュールでも共通に使用することができるように、一部のサブルーチンを書き換えたものが次に紹介するサブルーチン集です。 このサブルーチン集を簡単に使用するためには、まず、サブルーチン集を外部ファイル(ファイル名:OLED_SSD1306.sub)としておき、メインのプログラム側で ; ドライブ、パス名は、各自の開発環境に合わせるように !! include C:\Data\Project\PIC\_sub\OLED_SSD1306.sub ;Ver.1.04 以上が必要として include すれば、横 128 x 縦 64 ドットのモジュール用として機能しますが、次の緑色リストの冒頭コメントに書いてあるように #define _128x32 ;128x32 OLED を使用する場合 ; ドライブ、パス名は、各自の開発環境に合わせるように !! include C:\Data\Project\PIC\_sub\OLED_SSD1306.sub ;Ver.1.05 以上が必要として include するだけで、横 128 x 縦 32 ドットのモジュール用として機能します。 また、このサブルーチン集を動作させるためには、他にも、次リストの冒頭部分のコメントで述べているように、I2C インタフェース用の外部ルーチン (I2C_XS.sub) や、外部テーブル (Font5x7.tbl) 等が必要になりますが、 それらは 現在の最新プログラムバージョン に収録をしてあるので、そちらを参照してみてください。 ;========================================================================== ; OLED モジュール ( SSD1306 ) 制御サブルーチン Ver. 1.05 ;========================================================================== ;#define _128x32 ;128x32 OLED を使用する場合に ;include する側で #define 定義をすること ;<使用外部ルーチン> ;(i2c_start, i2c_stop, i2c_byte_send, i2c_ack, i2c_nack) ;<使用レジスタ> ;pagex ;表示開始ページ ;end_pagex ;表示終了ページ ;column ;表示開始カラム ;end_column ;表示終了カラム ;pattern ;水平ビット位置,垂直パターン,ワーク ;echr_flg ;表示文字の機能拡張フラグ ; ;oled_lpcnt ;ループカウンタ ;<使用テーブル> ;font5x7_data_tbl(外部テーブル: Font5x7.tbl) ;font10x14_data_tbl ;========================================================================== ; 定数の定義 ;========================================================================== SSD1306_Addr equ h'3c' ;OLED モジュール ( SSD1306 ) のスレーブアドレス ;========================================================================== ; マクロ命令定義 ;========================================================================== ; OLED_FontPos_M マクロ ;フォントの表示開始位置指定 OLED_FontPos_M macro @col,@row movlw @col ; movwf column ;表示カラム movlw @row ; movwf pagex ;表示ページ call SSD1306_FontPos ;表示開始位置指定 endm ; OLED_DispStr_M マクロ ;キャラクタ (横:6 x 縦:8) 文字列の表示 OLED_DispStr_M macro @str_adr,@x #if @x != 0 movlw high @str_adr ;文字列の先頭 high アドレス movwf FSR0H ; movlw low @str_adr ;文字列の先頭 low アドレス movwf FSR0L ; #endif call SSD1306_DispStr ;文字列データ1行表示 endm ; OLED_DispStr2_M マクロ ;キャラクタ (横:12 x 縦:16) 文字列の表示 OLED_DispStr2_M macro @str_adr,@x #if @x != 0 movlw high @str_adr ;文字列の先頭 high アドレス movwf FSR0H ; movlw low @str_adr ;文字列の先頭 low アドレス movwf FSR0L ; #endif call SSD1306_DispStr2 ;文字列データ1行表示 endm ; OLED_Line_H_M マクロ ;水平線表示 OLED_Line_H_M macro @col_st,@col_sp,@row,@ptn movlw @col_st ; movwf column ;表示開始カラム movlw @col_sp ; movwf end_column ;表示終了カラム movlw @row ; movwf pagex ;表示ページ movlw @ptn ; movwf pattern ;ビット位置 call SSD1306_Line_H ;モジュールの水平線表示 endm ; OLED_Line_V_M マクロ ;垂直線表示 OLED_Line_V_M macro @col,@row_st,@row_sp,@ptn movlw @col ; movwf column ;表示カラム movlw @row_st ; movwf pagex ;表示開始ページ movlw @row_sp ; movwf end_pagex ;表示終了ページ movlw @ptn ; movwf pattern ;垂直パターン call SSD1306_Line_V ;モジュールの垂直線表示 endm ;========================================================================== ; フォントの表示開始位置指定 ;========================================================================== ;入力レジスタ: pagex: 表示ページ ; column: 表示カラム SSD1306_FontPos call i2c_start ;スタートコンディション movlw SSD1306_Addr << 1 ;スレーブアドレス call i2c_byte_send ;1 バイト送信 movlw h'00' ;Control byte Co=0, D/C#=0 call i2c_byte_send ;1 バイト送信 movlw h'20' ;Set Memory Addressing Mode call i2c_byte_send ;1 バイト送信 movlw h'02' ;Page Addressing Mode call i2c_byte_send ;1 バイト送信 movf pagex,W addlw h'b0' ;Set Page Start Addrss for Page Addressing Mode call i2c_byte_send ;1 バイト送信(ページ開始アドレスの設定) movf column,W andlw h'0f' ;Set Lower Column Start Address for Page Addressing Mode call i2c_byte_send ;1 バイト送信(カラム開始アドレスの下位ニブル) swapf column,W andlw h'0f' addlw h'10' ;Set Higher Column Start Address for Page Addressing Mode call i2c_byte_send ;1 バイト送信(カラム開始アドレスの上位ニブル) call i2c_stop ;ストップコンディション return ;========================================================================== ; キャラクタ (横:6 x 縦:8) 文字の表示 ;========================================================================== ;入力レジスタ: W reg: 表示する文字コード (h'20'〜h'df') ; echr_flg: 表示文字の機能拡張フラグ ;(Ver.1.01にて追加) ; bit7: ビット反転表示指示 ; bit6: 1 bit 左(下)ずらし指示 ;使用レジスタ: oled_lpcnt, pattern, FSR1H:FSR1L ;使用テーブル: font5x7_data_tbl(外部テーブル: Font5x7.tbl) SSD1306_DispChr movwf FSR1L ;表示する文字コード clrf FSR1H movlw h'20' ;h'20'がテーブルの先頭位置 subwf FSR1L,F movf FSR1L,W ;W に一時退避 lslf FSR1L,F ;x2 rlf FSR1H,F lslf FSR1L,F ;x4 rlf FSR1H,F addwf FSR1L,F ;x5 ... 退避した W を足す clrw addwfc FSR1H,F ;C フラグを足す movlw low font5x7_data_tbl ;フォントテーブルデータの先頭 low アドレス addwf FSR1L,F ; movlw high font5x7_data_tbl ;フォントテーブルデータの先頭 high アドレス addwfc FSR1H,F ; ; call i2c_start ;スタートコンディション movlw SSD1306_Addr << 1 ;スレーブアドレス call i2c_byte_send ;1 バイト送信 movlw h'40' ;Control byte Co=0, D/C#=1 call i2c_byte_send ;1 バイト送信 movlw 5 ; movwf oled_lpcnt ;ループカウンタ dspchr1 call oledtbl_read ;テーブルデータの読み出し movwf pattern ;ワークに一時退避 movlp high $ ;この場所の high アドレス btfss echr_flg,7 ;ビット反転表示指示 か? goto dspchr2 ;No movlw h'7f' xorwf pattern,F ;ビット反転 dspchr2 btfsc echr_flg,6 ;1 bit 左(下)ずらし指示 か? lslf pattern,F ;Yes movf pattern,W ;ワーク call i2c_byte_send ;1 バイト送信 addfsr FSR1,1 ;間接アドレス FSR1H:FSR1L + 1 decfsz oled_lpcnt,F ;ループカウンタ - 1 = 0 か? goto dspchr1 ;No clrw ;文字間 h'00' 1 バイト追加 btfsc echr_flg,7 ;ビット反転表示指示 か? movlw h'7f' ;Yes. ビット反転 btfss echr_flg,6 ;1 bit 左(下)ずらし指示 か? goto dspchr3 ;No movwf pattern ;ワーク lslf pattern,W ; dspchr3 call i2c_byte_send ;1 バイト送信 call i2c_stop ;ストップコンディション return ;========================================================================== ; キャラクタ (横:6 x 縦:8) 文字列の表示 ;========================================================================== ;入力レジスタ: FSR0H:FSR0L: 文字列の先頭アドレス ;<使用例> movlw high title_msg ;文字列の先頭 high アドレス ; movwf FSR0H ; ; movlw low title_msg ;文字列の先頭 low アドレス ; movwf FSR0L ; ; call SSD1306_DispStr ;文字列データ1行表示 ; : ;title_msg dt "Perpetual Calendar",0 SSD1306_DispStr dspstr1 call str_read ;文字列データの読み出し movlp high $ ;この場所の high アドレス addfsr FSR0,1 ;間接アドレス FSR0H:FSR0L + 1 addlw 0 btfsc STATUS,Z ;文字列の終了(h'00') か? goto dspstr2 ;Yes call SSD1306_DispChr ;OLED へデータを1文字出力 goto dspstr1 dspstr2 return ; テーブルデータの1文字読み出し str_read movf FSR0H,W ;読み出すデータの high アドレス movwf PCLATH movf FSR0L,W ;読み出すデータの low アドレス movwf PCL ;========================================================================== ; キャラクタ (横:12 x 縦:16) 文字の表示 ;========================================================================== ;入力レジスタ: W: 表示する文字コード (h'2f'(/), h'30'(0) 〜 h'39'(9), h'3a'(:)) ; echr_flg: 表示文字の機能拡張フラグ ;(Ver.1.01にて追加) ; bit7: ビット反転表示指示 ;使用レジスタ: oled_lpcnt, pattern, FSR1H:FSR1L ;使用テーブル: font10x14_data_tbl SSD1306_DispChr2 movwf FSR1L ;表示する文字コード clrf FSR1H movlw h'2f' ;h'2f'がテーブルの先頭位置 subwf FSR1L,F lslf FSR1L,F ;x2 rlf FSR1H,F lslf FSR1L,F ;x4 rlf FSR1H,F movf FSR1L,W ;W に一時退避 lslf FSR1L,F ;x8 rlf FSR1H,F lslf FSR1L,F ;x16 rlf FSR1H,F addwf FSR1L,F ;x20 ... 退避した W を足す clrw addwfc FSR1H,F ;C フラグを足す movlw low font10x14_data_tbl ;フォントテーブルデータの先頭 low アドレス addwf FSR1L,F ; movlw high font10x14_data_tbl ;フォントテーブルデータの先頭 high アドレス addwfc FSR1H,F ; SSD1306_DispChr2s call dsp2chr0 ;文字の上位半分の表示 ; incf pagex,F ;文字の下位半分の表示位置 call SSD1306_FontPos ;フォントの表示開始位置指定 dsp2chr0 call i2c_start ;スタートコンディション movlw SSD1306_Addr << 1 ;スレーブアドレス call i2c_byte_send ;1 バイト送信 movlw h'40' ;Control byte Co=0, D/C#=1 call i2c_byte_send ;1 バイト送信 clrw ;文字間 h'00' 1 バイト追加 btfsc echr_flg,7 ;ビット反転表示指示 か? movlw h'ff' ;Yes. ビット反転 call i2c_byte_send ;1 バイト送信 movlw 10 ; movwf oled_lpcnt ;ループカウンタ dsp2chr1 call oledtbl_read ;テーブルデータの読み出し movwf pattern ;ワークに一時退避 movlp high $ ;この場所の high アドレス btfss echr_flg,7 ;ビット反転表示指示 か? goto dsp2chr2 ;No movlw h'ff' xorwf pattern,F ;ビット反転 dsp2chr2 movf pattern,W ;ワーク call i2c_byte_send ;1 バイト送信 addfsr FSR1,1 ;間接アドレス FSR1H:FSR1L + 1 decfsz oled_lpcnt,F ;ループカウンタ - 1 = 0 か? goto dsp2chr1 ;No clrw ;文字間 h'00' 1 バイト追加 btfsc echr_flg,7 ;ビット反転表示指示 か? movlw h'ff' ;Yes. ビット反転 call i2c_byte_send ;1 バイト送信 call i2c_stop ;ストップコンディション return ;========================================================================== ; キャラクタ (横:12 x 縦:16) 文字列の表示 ;========================================================================== ;入力レジスタ: FSR0H:FSR0L: 文字列の先頭アドレス ;<使用例> movlw high date_msg ;文字列の先頭 high アドレス ; movwf FSR0H ; ; movlw low date_msg ;文字列の先頭 low アドレス ; movwf FSR0L ; ; call SSD1306_DispStr2 ;文字列データ1行表示 ; : ;date_msg dt "2024/12/02",0 SSD1306_DispStr2 dsp2str1 call str_read ;文字列データの読み出し movlp high $ ;この場所の high アドレス addfsr FSR0,1 ;間接アドレス FSR0H:FSR0L + 1 addlw 0 btfsc STATUS,Z ;文字列の終了(h'00') か? goto dsp2str2 ;Yes call SSD1306_DispChr2 ;OLED へデータを1文字出力 decf pagex,F ;文字の上位半分の表示位置に戻す movlw 12 addwf column,F ;カラム位置の更新 call SSD1306_FontPos ;次文字の表示位置 goto dsp2str1 dsp2str2 return ;========================================================================== ; OLED ( SSD1306 ) モジュールの水平線表示 ;========================================================================== ;入力レジスタ: pagex: 表示ページ ; column: 表示開始カラム ; end_column: 表示終了カラム ; pattern: ビット位置: h'01', h'02', h'04', h'08', h'10', h'20', h'40', h'80' ;使用レジスタ: oled_lpcnt: ループカウンタ SSD1306_Line_H call SSD1306_FontPos ;表示開始位置の指定 movf column,W ;表示開始カラム subwf end_column,W ;表示終了カラム addlw 1 movwf oled_lpcnt ;ループカウンタ ; call i2c_start ;スタートコンディション movlw SSD1306_Addr << 1 ;スレーブアドレス call i2c_byte_send ;1 バイト送信 movlw h'40' ;Control byte Co=0, D/C#=1 call i2c_byte_send ;1 バイト送信 oled_lh1 movf pattern,W ; call i2c_byte_send ;1 バイト送信 decfsz oled_lpcnt,F ;ループカウンタ - 1 = 0 か? goto oled_lh1 ;No call i2c_stop ;ストップコンディション return ;========================================================================== ; OLED ( SSD1306 ) モジュールの垂直線表示 ;========================================================================== ;入力レジスタ: pagex: 表示開始ページ ; end_pagex: 表示終了ページ ; column: 表示カラム ; pattern: 垂直パターン: h'00' 〜 h'ff' ;使用レジスタ: oled_lpcnt: ループカウンタ SSD1306_Line_V call i2c_start ;スタートコンディション movlw SSD1306_Addr << 1 ;スレーブアドレス call i2c_byte_send ;1 バイト送信 movlw h'00' ;Control byte Co=0, D/C#=0 call i2c_byte_send ;1 バイト送信 movlw h'20' ;Set Memory Addressing Mode call i2c_byte_send ;1 バイト送信 movlw h'01' ;Vertical Addressing Mode call i2c_byte_send ;1 バイト送信 movlw h'21' ;Set Column Address カラムアドレスの設定 call i2c_byte_send ;1 バイト送信 movf column,W ;Column start address, range: 0-127 call i2c_byte_send ;1 バイト送信 movf column,W ;Column end address, range: 0-127 call i2c_byte_send ;1 バイト送信 movlw h'22' ;Set Page Address ページアドレスの設定 call i2c_byte_send ;1 バイト送信 movf pagex,W ;Page start Address, range: 0-7 call i2c_byte_send ;1 バイト送信 movf end_pagex,W ;Page end Address, range: 0-7 call i2c_byte_send ;1 バイト送信 call i2c_stop ;ストップコンディション ; movf pagex,W ;表示開始ページ subwf end_pagex,W ;表示終了ページ addlw 1 movwf oled_lpcnt ;ループカウンタ call i2c_start ;スタートコンディション movlw SSD1306_Addr << 1 ;スレーブアドレス call i2c_byte_send ;1 バイト送信 movlw h'40' ;Control byte Co=0, D/C#=1 call i2c_byte_send ;1 バイト送信 oled_lv1 movf pattern,W ; call i2c_byte_send ;1 バイト送信 decfsz oled_lpcnt,F ;ループカウンタ - 1 = 0 か? goto oled_lv1 ;No call i2c_stop ;ストップコンディション return ;========================================================================== ; OLED ( SSD1306 ) モジュールの画面消去 ;========================================================================== ;使用レジスタ: oled_lpcnt, FSR1H:FSR1L SSD1306_Clear movlw high ssd1306_clear_table ;テーブルデータの先頭 high アドレス movwf FSR1H ; movlw low ssd1306_clear_table ;テーブルデータの先頭 low アドレス movwf FSR1L ; movlw ssd1306_clear_table_end - ssd1306_clear_table movwf oled_lpcnt ;ループカウンタ call oledini00 ;(SSD1306_Init 内) ; call i2c_start ;スタートコンディション movlw SSD1306_Addr << 1 ;スレーブアドレス call i2c_byte_send ;1 バイト送信 movlw h'40' ;Control byte Co=0, D/C#=1 call i2c_byte_send ;1 バイト送信 #ifdef _128x32 movlw high (128 * 4) ;128culomn * 4page #else movlw high (128 * 8) ;128culomn * 8page #endif movwf FSR1H #ifdef _128x32 movlw low (128 * 4) #else movlw low (128 * 8) #endif movwf FSR1L ;ループカウンタ oledclr01 clrw ;h'00' call i2c_byte_send ;1 バイト送信 addfsr FSR1,-1 ;ループカウンタ - 1 movf FSR1H,W ; iorwf FSR1L,W ; btfss STATUS,Z ;FSR1H = FSR1L = h'00' か? goto oledclr01 ;No call i2c_stop ;ストップコンディション return ;-------------------------------------------------------------------------- ; OLED ( SSD1306 ) の画面消去用コマンドデータ・テーブル ssd1306_clear_table dt SSD1306_Addr << 1 ;スレーブアドレス dt h'00' ;Control byte Co=0, D/C#=0 dt h'20' ;Set Memory Addressing Mode dt h'00' ;Horizontal Addressing Mode dt h'21' ;Set Column Address カラムアドレスの設定 dt 0 ;Column start address, range: 0-127 dt 127 ;Column end address, range: 0-127 dt h'22' ;Set Page Address ページアドレスの設定 dt 0 ;Page start Address, range: 0-7 #ifdef _128x32 dt 3 ;Page end Address, range: 0-3 #else dt 7 ;Page end Address, range: 0-7 #endif ssd1306_clear_table_end ;========================================================================== ; OLED ( SSD1306 ) モジュールの初期設定 ;========================================================================== ;使用レジスタ: oled_lpcnt, FSR1H:FSR1L SSD1306_Init movlw high ssd1306_init_table ;テーブルデータの先頭 high アドレス movwf FSR1H ; movlw low ssd1306_init_table ;テーブルデータの先頭 low アドレス movwf FSR1L ; movlw ssd1306_init_table_end - ssd1306_init_table movwf oled_lpcnt ;ループカウンタ oledini00 call i2c_start ;スタートコンディション oledini01 call oledtbl_read ;テーブルデータの読み出し call i2c_byte_send ;1 バイト送信 addfsr FSR1,1 ;間接アドレス FSR1H:FSR1L + 1 decfsz oled_lpcnt,F ;ループカウンタ - 1 = 0 か? goto oledini01 ;No call i2c_stop ;ストップコンディション return ; テーブルデータの1文字読み出し oledtbl_read movf FSR1H,W ;読み出すデータの high アドレス movwf PCLATH movf FSR1L,W ;読み出すデータの low アドレス movwf PCL ;-------------------------------------------------------------------------- ; OLED ( SSD1306 ) の初期設定用コマンドデータ・テーブル ssd1306_init_table dt SSD1306_Addr << 1 ;スレーブアドレス dt h'00' ;Control byte Co=0, D/C#=0 dt h'a8' ;Set Multiplex Ratio, 画面の解像度 #ifdef _128x32 dt h'1f' ;h'00'- h'3f' ;32 dot #else dt h'3f' ;h'00'- h'3f' ;64 dot #endif dt h'd3' ;Set Display Offset, ディスプレイオフセット dt h'00' ;h'00'- h'3f' ; dt h'40' ;h'40'- h'7f' ;Set Display Start Line dt h'a1' ;h'a0'/ h'a1' ;Set Segment Re-map, 左右反転 dt h'c8' ;h'c0'/ h'c8' ;Set COM Output Scan Direction, 上下反転 dt h'da' ;Set COM Pins Hardware Configuration, COMピンの構成 #ifdef _128x32 dt h'02' ;h'02'- h'32' ;Sequential COM pin (128x32 のとき) #else dt h'12' ;h'02'- h'32' ;Alternative COM pin (128x64 のとき) #endif dt h'81' ;Set Contrast Control dt h'7f' ;h'00'- h'ff' ;コントラスト dt h'a4' ;h'a4'/ h'a5' ;Disable Entire Display On dt h'a6' ;h'a6'/ h'a7' ;Set Normal Display, 白黒反転 dt h'd5' ;Set Osc Frequency dt h'80' ;h'00'- h'ff' ; dt h'8d' ;Enable Charge Pump Regulator dt h'14' ;h'04'/ h'14' ; dt h'af' ;h'ae'/ h'af' ;Display On, h'ae':OFF / h'af':ON ssd1306_init_table_end ;========================================================================== ; OLED ( SSD1306 ) の Display ON/OFF 制御 ;========================================================================== ;入力レジスタ: W reg: コマンドデータ h'af':ON / h'ae':OFF ;使用レジスタ: pattern: ワーク SSD1306_DispOnOff movwf pattern ;ワークに一時退避 call i2c_start ;スタートコンディション movlw SSD1306_Addr << 1 ;スレーブアドレス call i2c_byte_send ;1 バイト送信 movlw h'80' ;Control byte Co=1, D/C#=0 call i2c_byte_send ;1 バイト送信 movf pattern,W ;コマンドデータを復帰 call i2c_byte_send ;1 バイト送信 call i2c_stop ;ストップコンディション return ;========================================================================== ; OLED ( SSD1306 ) の コントラスト制御 ;========================================================================== ;入力レジスタ: W reg: コマンドデータ h'00'- h'ff' ;使用レジスタ: pattern: ワーク SSD1306_Contrast movwf pattern ;ワークに一時退避 call i2c_start ;スタートコンディション movlw SSD1306_Addr << 1 ;スレーブアドレス call i2c_byte_send ;1 バイト送信 movlw h'00' ;Control byte Co=0, D/C#=0 call i2c_byte_send ;1 バイト送信 movlw h'81' ;Set Contrast Control call i2c_byte_send ;1 バイト送信 movf pattern,W ;コマンドデータを復帰 call i2c_byte_send ;1 バイト送信 call i2c_stop ;ストップコンディション return ;=============================================================== end ====== なお、改めて言うまでもないことですが注意事項としては、横 128 x 縦 32 ドットのモジュール用として使用する場合には、変数 pagex、end_pagex の指定範囲は 0 〜 3 までで、4 〜 7 を指定してはいけません。 | プログラムのトップに戻る | 本機では、「DOWN カウンタ」画面 と「DOWN + UP カウンタ」画面 では、DOWN カウンタとしての初期値(タイマー時間)が必要なため、その時間(分秒)を予め設定しなければなりませんが、これらの両カウンタ画面の初期画面には、 通常、前回使用された設定値が記憶されていてそれが表示されます。 そして、設定値が変更された場合には、それを新たな設定値としてカウンタ種類とともに、次回に引き継ぐように、PIC 内の EEPROM メモリに記憶がされます。 また、本機がスリープするまでの移行時間(分)と、OLED ディスプレイ画面のコントラストを決める設定値は、本機の「メンテナンス機能」画面で変更することが可能ですが、変更した場合には、それを新たな設定値として次回にも引き継げるように、 同様に PIC 内の EEPROM メモリに記憶がされます。 これらの設定値は、本機では、データメモリの BANK 0 内に7個の変数として散在していますが、これらの変数を EEPROM メモリに書き込む(または、読み出す)場合には、直接両者間でやり取りをするのではなく、間にデータメモリの BANK 1 内に設けたバッファ save_data_buff を介して、読み書きを行うようにしています。
;========================================================================== ; 定数の定義と変数のレジスタ割付け ;========================================================================== : : cblock h'20' ;バンク 0 cnt_dw_10m ;10M(十分)DOWN カウンタ cnt_dw_01m ; 1M(分)DOWN カウンタ cnt_dw_10s ;10S(十秒)DOWN カウンタ cnt_dw_01s ; 1S(秒)DOWN カウンタ : : count_type ;カウンタ種類 : : sleep_time ;スリープまでの移行時間(分) contrast ;OLED コントラスト : : endc cblock h'a0' ;バンク 1 : : save_data_buff ;引継ぎ情報(バックアップデータ)の読み書きバッファ endc ;========================================================================== ; EEPROM エリア ;========================================================================== org h'f000' : : : ; 以下はデフォルト値 save_data de 3 ;スリープまでの移行時間(分) de 127 ;h'7f' ;OLED コントラスト de 1 ;カウンタ種類 de 1 ;10M(十分)DOWN カウンタ de 5 ; 1M(分)DOWN カウンタ de 0 ;10S(十秒)DOWN カウンタ de 0 ; 1S(秒)DOWN カウンタ save_data_end EEPROM メモリにアクセスするために必要な関連レジスタは、先の MEMORY MAP で青色に塗ったレジスタ群ですが、それらはバンク 17 に集中していて、ここら辺りは私が 199. 多機能携帯デジタル時計 で使用した PIC16F1827 とは、レジスタ名も収容バンクもまったく異なっています。 一番の相違点は、上述したように PIC16F18325 では NVMADRH レジスタに h'70' を設定する点で、PIC16F1827 では NVMADRH レジスタに相当する EEADRH レジスタは、EEPROM メモリにアクセスする場合にはまったく使用しないことです。 ;========================================================================== ; EEPROM メモリ関連アクセス ;========================================================================== ;-------------------------------------------------------------------------- ; バックアップデータを EEPROM から読み出し ;-------------------------------------------------------------------------- read_eeprom movlw high save_data_buff ;引継ぎ情報(バックアップデータ)の先頭アドレス movwf FSR1H ; movlw low save_data_buff ; movwf FSR1L ;間接アドレスに設定 movlw save_data_end - save_data ;読み出しバイト数 movwf lpcnt1 ;ループカウンタ1 movlb 17 ;バンク 17 movlw h'70' movwf NVMADRH ; movlw low save_data movwf NVMADRL ;EEPROM の記録アドレスを設定 bsf NVMCON1,NVMREGS ;データ EEPROM メモリにアクセス rdeep01 movlb 17 ;バンク 17 bsf NVMCON1,RD ;読み出し制御 = ON movf NVMDATL,W ;データの読み出し movwi FSR1++ ;RAM バッファへ書き込み incf NVMADRL,F ;記録アドレスの更新 movlb 0 ;バンク 0 decfsz lpcnt1,F ;ループカウンタ1 - 1 = 0 か? goto rdeep01 ;No call ram_buff_read ;RAM バッファから個々の変数へコピー return ;-------------------------------------------------------------------------- ; RAM バッファから個々の変数へコピー ram_buff_read movlw high save_data_buff ;引継ぎ情報(バックアップデータ)の先頭アドレス movwf FSR1H ; movlw low save_data_buff ; movwf FSR1L ;間接アドレスに設定 moviw FSR1++ ; movwf sleep_time ;スリープまでの移行時間(分) moviw FSR1++ ; movwf contrast ;OLED コントラスト moviw FSR1++ ; movwf count_type ;カウンタ種類 dwn_cnt_read moviw FSR1++ ; movwf cnt_dw_10m ;10M(十分)DOWN カウンタ moviw FSR1++ ; movwf cnt_dw_01m ; 1M(分)DOWN カウンタ moviw FSR1++ ; movwf cnt_dw_10s ;10S(十秒)DOWN カウンタ moviw FSR1++ ; movwf cnt_dw_01s ; 1S(秒)DOWN カウンタ return ;-------------------------------------------------------------------------- ; バックアップデータを EEPROM へ書き込み ;-------------------------------------------------------------------------- write_eeprom call ram_buff_write ;個々の変数から RAM バッファへコピー movlw high save_data_buff ;引継ぎ情報(バックアップデータ)の先頭アドレス movwf FSR1H ; movlw low save_data_buff ; movwf FSR1L ;間接アドレスに設定 movlw save_data_end - save_data ;書き込みバイト数 movwf lpcnt1 ;ループカウンタ1 movlb 17 ;バンク 17 movlw h'70' movwf NVMADRH ; movlw low save_data movwf NVMADRL ;EEPROM の記録アドレスを設定 bsf NVMCON1,NVMREGS ;データ EEPROM メモリにアクセス wreep01 movlb 17 ;バンク 17 moviw FSR1++ ;RAM バッファの読み出し movwf NVMDATL ;書き込みデータを設定 bsf NVMCON1,WREN ;プログラム/消去サイクルを許可 bcf INTCON,GIE ;割り込みを禁止 movlw h'55' movwf NVMCON2 ;h'55' を NVMCON2 に書き込む movlw h'aa' movwf NVMCON2 ;h'aa' を NVMCON2 に書き込む bsf NVMCON1,WR ;書き込み開始 bsf INTCON,GIE ;割り込みを許可 bcf NVMCON1,WREN ;プログラム/消去サイクルを禁止 btfsc NVMCON1,WR ;書き込み 終了か? goto $ - 1 ;No incf NVMADRL,F ;記録アドレスの更新 movlb 0 ;バンク 0 decfsz lpcnt1,F ;ループカウンタ1 - 1 = 0 か? goto wreep01 ;No return ;-------------------------------------------------------------------------- ; 個々の変数から RAM バッファへコピー ram_buff_write movlw high save_data_buff ;引継ぎ情報(バックアップデータ)の先頭アドレス movwf FSR1H ; movlw low save_data_buff ; movwf FSR1L ;間接アドレスに設定 movf sleep_time,W ;スリープまでの移行時間(分) movwi FSR1++ ; movf contrast,W ;OLED コントラスト movwi FSR1++ ; movf count_type,W ;カウンタ種類 movwi FSR1++ ; movf cnt_dw_10m,W ;10M(十分)DOWN カウンタ movwi FSR1++ ; movf cnt_dw_01m,W ; 1M(分)DOWN カウンタ movwi FSR1++ ; movf cnt_dw_10s,W ;10S(十秒)DOWN カウンタ movwi FSR1++ ; movf cnt_dw_01s,W ; 1S(秒)DOWN カウンタ movwi FSR1++ ; return ;==========================================================================
| プログラムのトップに戻る | 一般的に、PIC に限らずマイコンと外部の入出力機器(身近なところで言えば、スイッチ類などの入力機器とか LED を代表とする出力機器)との間で、データをやり取りするためには入出力ポートを介して行います。 私が自分のオリジナル作品に PIC16F1 ファミリを採用し出したのは、約4年ほど前に公開をした "194. FMステレオラジオ III" が始めてで、それ以来、公開をしたものだけを数えると次の表に示すように6作品に及びます。
また、初めて PIC 作品を製作したのは 20 年ほども以前のことで、PIC16F84A を使用したLチカ "003. LEDフラッシャー"(2015/01/03 公開)でした。 この PIC16F84A を代表とする PIC16F ファミリでは、 TRISx レジスタによって入力と出力のポート設定を行った後は、入力ポートも出力ポートも PORTx レジスタによってデータの入出力を行います。 このように、始めて私が PIC のプログラムを作成した頃から、出力ポートに設定した PORTx レジスタを、BSF 命令や BCF 命令を使用して操作をする場合には、注意をしてそれらの命令を使用しないと思わぬ結果が現れる場合がある ― ということも、 (私が PIC のアセンブラ命令の勉強をしていた頃、いろいろな先輩方のサイトを拝見して)知識としては知っていました。 それから約 20 年ほどが経ち、上述のように4年ほど前からは PIC16F ファミリだけでなく、PIC16F1 ファミリのプログラムも扱うようになって、その当初から、出力ポートに LATx レジスタが加わったことは、もちろん分かってはいたのですが、 PORTx レジスタと LATx レジスタの具体的な使い分けが、イマイチ理解できないでいました。 ここら辺りのことを PIC16F1 ファミリのデータシートで調べてみても、リード・モディファイ・ライト(Read-Modify-Write)というキーワードが出てきて、そのこと自体は分かっても、「だから、どうなんだ?」 イマイチ、データシートに書いてあることが良く理解できないままでした。 そんなわけもあって、今まで上の表に挙げた6作品すべてにおいて、LATx レジスタは使用しないで PIC16F ファミリとまったく同様に、入力ポートも出力ポートも PORTx レジスタだけを使用したプログラムを作成してきました。 そして、当然のように PORTx レジスタに対して BSF 命令や BCF 命令を使用していましたが、現在まで目立った不具合は経験していません。(たぶん?) 初めて作成した PIC16F1 ファミリでの、PORTx レジスタだけを使用したプログラム "194. FMステレオラジオ III" では、BSF 命令や BCF 命令による不具合に遭遇しなかった*1 ために、そのままズルズルと今日まで来てしまいました。
ネットで検索をしてみると、この問題については何件もヒットしますが、中でも "PICで遊ぶ電子工作" さんの "PORT レジスタと LAT レジスタ" という記事が、? な私を最も納得させてくれました。 この記事では実際に PIC18F1320 を使用した実験回路を使っての説明になっていますが、PIC16F1 ファミリであっても同様です。 ありがとうございました。 結論として、PIC16F1 ファミリ(PIC16F ファミリ以外の LATx レジスタが存在する PIC すべて)においては、入力ポートには PORTx レジスタ を、出力ポートには LATx レジスタ に対してアクセスするのが基本となります。 もちろん、LATx レジスタに対しては何の気兼ねをすることもなく、BSF 命令や BCF 命令を使用することができます。 これに対して PIC16F ファミリでは、LATx レジスタが存在しないため、出力ポートの場合にも PORTx レジスタにアクセスすることになるのですが、システムクロックが高速な場合 や、出力ポートに容量性の負荷(コンデンサ)が接続 されているような場合に、 例えば、同一ポート内の他ビット同士(例. PORTC の RC0 ビットと RC1 ビットという様に)に対して、BSF 命令や BCF 命令などのビット命令を連続的に使用すると、上述 のように 自分が 期待する結果とは異なって思わぬ結果が現れてしまう 現象が起こります。
さて、本機 "203. 有機 EL ディスプレイ(OLED) 表示 キッチン・タイマー II" のプログラムにおいて、本項(PORTx レジスタと LATx レジスタの取り扱い)で述べているように、両レジスタの取り扱いを改めたプログラムに 更新*2(次に示す "現在の最新プログラムバージョン: Ver. 1.05" )をしました。
更新をした I2C (SCLクロック周波数 = 100/400 KHz) サブルーチン(ソフトウエア I2C 版)(I2C_XS_a.sub) は、同一ファイルを PIC16F ファミリでも PIC16F1 ファミリでも、どちらにも対応ができるように if 文(#ifdef)で切り替える構造になっているので、 見づらいかもしれません。
| プログラムのトップに戻る |
| ページトップ | | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
使用したメインとなる下図 左側のプリント基板 は、"秋月電子" の "片面ガラス・ユニバーサル基板 Bタイプ(95×72mm) めっき仕上げ(販売コード 100518)" ですが、後述の ケース加工図 で示したケース内に収めるためには、
プリント基板の切断等の加工が必要です。 左図に示すように、まず基板下部の ----- X 線の位置で切断をします。 そして、この切断によって失われた基板下部の取り付け用穴の代理となる、新たなΦ3.2 の穴を図のように2か所開けます。 次に、基板右端に位置するスイッチ用基板の取り付けのために用いる、ジュラコンスペーサ用のΦ3.2 の穴を2か所開けます。
次に 右側のプリント基板 は、"秋月電子" の "片面ガラス・ユニバーサル基板 Cタイプ(72×47.5mm) めっき仕上げ(販売コード 100517)" で、(A) は左側メイン基板の右端に取り付けるスイッチ用基板になるもので、 他にコネクタ用の小基板3枚 (B) (C) (D) も切り出します。 | プリント基板部品配置図 (KitchenTimerIIPC0.CE3) | (KitchenTimerII2PC0.CE3) | ページトップ | |
メイン基板に多くのパーツを搭載するにあたっては、それぞれに対して以降に述べるような注意事項があるので、基板を組み立てる上での参考にしてください。
なお、図の中で、細長い長方形の中に ○で表したものはピンヘッダ(オス)を、同様に◎で表したものはピンソケット(メス)を使用することを表しているので、注意をしてください。
・DS1307 RTC モジュール プリント基板の左上の少し下に位置する四角状のものが DS1307 RTC モジュールで、本機では リアルタイムクロック( D1307 )モジュール の項で述べたように、このモジュールにはピンヘッダが取り付けられていないので、ストレート型の 7 ピンヘッダを自前で用意し取り付ける必要があります。 なお本機の使用では、電源を断にしたときに備えてのバックアップ用の電池は必要がありません。 ・TP4056 充電モジュール プリント基板の左下に位置する "TP4056 充電モジュール" の取り付けについては、まず、縦横4か所の各青色線で示す位置の、メイン基板のハンダ面側から 0.4 〜 0.5 mm の錫メッキ線等を通し、モジュール基板上面側で各青色線の対となるもう片方の穴へグルっとUターンをして、 再び、メイン基板のハンダ面側に通した後はんだ付けをします。 このとき、モジュール基板側は 2.54 mm ピッチではないため注意が必要ですが、メイン基板側の穴位置は、前項 プリント基板の加工と部品配置 で示した縦横4か所の各青色線で示したランド穴位置を選択すれば問題はないでしょう。 次に、モジュール基板左側の斜2か所の青色線で示すモジュール基板側の穴に対応するメイン基板側の位置には、ランド穴が開いていないのでこの位置にΦ1 程度の穴を開ける必要があり * ます。 穴を開けた後、前の縦横4か所のときと同要領で斜2か所についても、錫メッキ線等でモジュール基板の固定をします。 ただし、前の縦横4か所についてはその後、メイン基板のハンダ面側で回路の配線が必要になりますが、この斜2か所については回路の配線はありません。
・OLED モジュール プリント基板の上部中央には、OLED モジュールが位置します。 メイン基板側にはあらかじめ 4P のピンソケット(メス)を取り付けておき、OLED モジュール側のピンヘッダと嵌合させます。 このとき、ピンソケットには分割ピンソケットを使用するとその加工が楽になります。 ・トグルスイッチ プリント基板下部中央に位置する POWER スイッチには、3P(1回路)のトグルスイッチで良いのですが、3P でレバーが短いタイプが私の持ち合わせになく、6P(2回路)のものがあったので私は代わりにそれを使用しました。 したがって、3P でレバーが短いタイプを入手した場合には、基板パターン図を 3P(1回路)用に変更をしてください。 本機では ケース加工図 で示したケース(内寸高さ 24 mm)を使用したため、レバーが通常のタイプは不可で、短いタイプでないとケース内に収めることができません。 本機で 18650 リチウムイオン電池を充電する場合には、TP4056 充電モジュールの負荷接続端子を開放しておく必要があるため、POWER スイッチを OFF にする必要があります。 ・圧電スピーカー トグルスイッチの右上、すなわち PIC16F18325 の下位置に示す円状のものが圧電スピーカーで、本機では音量が多くなるように少々直径の大きなものを使用しました。 図に示すように、円の左右に取り付け用の穴(M2.6 用)を2個開ける必要があります。 ・タクトスイッチ用基板との接続コネクタ プリント基板の右端縦長の位置に、タクトスイッチ用基板を取り付けますが、この基板の取り付けには、後述の ケースへのプリント基板の取り付け で説明しているように、私の場合には手持ちの関係で、 13 mm 長のオネジ+メネジの六角ジュラコンスペーサ(廣杉計器 BS-313W)を使用しました。 そして、電気的な接続には、下の プリント基板(2)パターン図 (部品面) (ハンダ面) の項で示した、タクトスイッチ用基板のケーブルコネクタ( 7P ピンソケット(メス))と、メイン基板上に設けた 7P L型ピンヘッダとを、常時接続をさせておきます。 なお、メイン基板側のピンヘッダは、パターン図や写真のように L型でないと接続ができません。 ・その他 プリント基板左下の 3P のピンヘッダ B+ と B- 端子には、本機の電源である 18650 リチウムイオン電池を、電池ホルダからの配線( 3P ピンソケット(メス))を介して接続させます。 電気的には 2P で良いのですが、 2P 状態よりも 3P の方が、しっかりとコネクタ接続ができるために 3P としました。 そのため、真ん中のピンには配線はしていません。 | プリント基板(1)パターン図 (部品面) (KitchenTimerIIPC.CE3) | 左図から黒色文字を除いた図 (KitchenTimerIIPC2.CE3) | ページトップ | |
前項 プリント基板(1)パターン図 (部品面) でも述べたように、下パターン図や写真は、トグルスイッチに 3P(1回路)ではなく 6P(2回路)を使用したものなので、3P(1回路)トグルスイッチを使用した場合には、
スイッチ周りのパターンを適宜変更をしてください。
| プリント基板(1)パターン図 (ハンダ面) (KitchenTimerIIPC1.CE3) | ページトップ | |
| 本機では、タクトスイッチをプリント基板(2) に取り付けるときには、スイッチの底面を基板にぴったり押し付けて取り付けることが必要です。 (詳細は、後述の ケースへのプリント基板の取り付け の項を参照してください。) |
![]() |
![]() |
| プリント基板(2)パターン図 (部品面) (KitchenTimerII2PC.CE3) | ページトップ |
![]() |
![]() |
| プリント基板(2)パターン図 (ハンダ面) (KitchenTimerII2PC1.CE3) | ページトップ |
本機のケースも、前作( 万年カレンダー II 、 万年カレンダー III )で使用したものとまったく同じもので、"秋月電子" の "ABS樹脂ケース(蝶番式・中)112−TS
ニシムラ (販売コード: 100277)" を使用しました。 外側(内側)寸法が 117 (112) x 84 (80) x 28 (24) mm で、可動式のフタが付いています。
上図の(上面図)及び(左側面図)において、各丸穴の位置寸法を記入* はしてありますが、参考程度に見ておいてください。 実際に穴開けを行う場合には、プリント基板を仮に取り付けた後、ケースが透明で中が透けて見えているので、 現物合わせで丸穴の中心位置を千枚通し等で印を付けて位置決めを行った方が、穴開けがうまくいくと思います。 ただし、(下面図)におけるプリント基板の取り付け用の穴位置は、極力、正確な位置に穴開けを行うように心掛けてください。
上図の(下面図)はケースの裏面側から見た図で、上部の大きな点線の四角部分(72 x 89 mm)がメイン基板の取り付け位置です。 私がプリント基板をケース等に取り付ける場合、通常、両者の間には 5 mm 長のスペーサを挟んで 取り付けることが多いのですが、本機のメイン基板には 5 mm 長のスペーサを使用することができません。 上の プリント基板パターン図 (部品面) の項で、使用した トグルスイッチ について述べたように、本機ではレバーが短いタイプを使用していますが、メイン基板を上図のケース内に納めるためには 5 mm 長のスペーサでは長過ぎて、 上蓋が閉まらなくなってしまいます。 そこで本機では M3 のナットをスペーサ代わり、としました。 ウィルコ で購入したナットで、 "鉄(ニッケルめっき) 六角ナット・1種 FNT-03N" の厚さ 2.4 mm のものです。 ケースの裏面側(外側)の4か所から、それぞれ M3 x 10 サイズのビス(+なべ小ねじ)を通し、ケース内側でスペーサ代わりの M3 ナット → メイン基板 → ポリカーボネート平ワッシャー → M3 ナット、の順に取り付けます。 ポリカーボネート平ワッシャーは、プリント基板表面の傷つき防止とねじ緩み防止を兼ねて挿入をしてあります。(ねじ類の型番等については、使用部品表 を参照のこと) ・電池ホルダーの取り付け 同様に上図の(下面図)で、下部の横に細長い点線の四角部分に、18650 リチウムイオン電池用の電池ホルダーを取り付けます。 私が使用した電池ホルダー、およびその取り付けるときの注意点、方法などについては、 別ページ "198. MP3プレーヤー I" の 電池ホルダーをケースに取り付け を参考にしてください。 | ケース加工図 (KitchenTimerIICS.CE3) | ページトップ | |
|
下図は本機ケースの右側面側から上部半分ほどを見た図で、2枚のプリント基板やタクトスイッチの取り付けの様子がよく分かるように、右側面を切り取った断面図で表しています。 ケースへのメイン基板の取り付けに当たって、最も重要なのはその上下(高さ)位置で、重複しますが上述した メイン基板の取り付け のように、本機ではトグルスイッチ搭載のために M3 のナットをスペーサ代わりとして使用しています。 そして、前項で述べたケースを使用する限り、この状態が最もベストな上下(高さ)位置で変更することはできません。 また、同図に示すように本機では、スイッチ用プリント基板に取り付けたタクトスイッチのトップが、ケース上面から適度な長さ(X)を飛び出すようにしなければなりません。 そのために、タクトスイッチには手持ちにあった通常のものより少々長い、 スイッチ底面からトップまでの長さが 9.5mm のものを使用し、スイッチ用基板をメイン基板に取り付けるのに、これも手持ちの関係で右写真のような、M3 x 13 mm 長のオネジ+メネジの六角ジュラコンスペーサ(廣杉計器 BS-313W)を使用しました。
余談ですが、タクトスイッチをプリント基板に取り付ける場合に、私は通常、スイッチを基板穴に軽く押し込んだときに、足ピンがくの字に曲がったあたりで進入が止まった位置、すなわち、スイッチ底面が少し基板上面から浮いた位置でハンダ付けをすることが多いのですが、 今回は上図のように、スイッチ底面が基板上面に付くまでスイッチを押し込んで使用しました。 これは使用したタクトスイッチとスペーサの各長さによって X 値を調節するためで、ちなみに図の組み合わせでは X ≒ 2 mm ほどになりました。 | 基板の取り付け図 (pcb_attach.CE3) | ページトップ | |
| (主要部品: IC, トランジスタ等) | (データシート) | |||
| PICマイコン | .................... | PIC16F18325 | ||
| 三端子レギュレータ | .................... | S-812C33AY-B-G | ||
| 有機 EL ディスプレイ(OLED) | .................... | SSD1306 | ||
| RTC モジュール | .................... | DS1307 | ||
| 圧電ブザー | .................... | SPT08 | ||
| 部品表 | Excel ファイル (KitchenTimerII_parts.xls) | ページトップ |
| PIC16F18325 データシート | .......... | https://akizukidenshi.com/goodsaffix/pic16(l)f18325_18345.pdf | ||
| SSD1306 データシート | .......... | https://akizukidenshi.com/goodsaffix/ssd1306.pdf | ||
| DS1307 データシート | .......... | https://datasheets.maximintegrated.com/en/ds/DS1307.pdf | ||
| はじめての PIC-2 / PIC16F18325 | .......... | http://machoto2.g2.xrea.com/page/P16F18325/P18325_00.htm | ||
| 195. ドットマトリクス LED コントローラー | .......... | https://xyama.sakura.ne.jp/hp/MatrixLED_Controller.html | ||
| 202. 有機 EL ディスプレイ(OLED) 表示 万年カレンダー III | .......... | https://xyama.sakura.ne.jp/hp/PerpetualCalendarIII.html |