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

194. FMステレオラジオ III

[ 初公開日:2022年1月27日 ]

 "FMステレオラジオ II" を製作して以来、次の数点において私自身が不満と物足らなさを、ずっと抱き続けていたためそれらを解消するために、この "FMステレオラジオ III" を新たに製作をし直しました。 当初は、ハードウェアを "FMステレオラジオ II" のプリント基板をそのまま使用して、PIC の交換と配線替えだけの変更を考えていたのですが、やはり電源スイッチについての不満も大きいので、 プリント基板の製作やケースの加工を新たにし直すことしました。
  • "FMステレオラジオ II" の回路図下の 注3. で述べたように、「当初計画していた機能を持たせることが、PIC16F684 ではできないことが分かった」ことを、どうしても実現させたい。
  • 同様に 使用した電源スイッチについて で述べたように、"FMステレオラジオ II" で使用した電源スイッチには、大いに不満を感じている。 できれば変更をしたい。
  • 次の 回路図 に示すように、最近の私の PIC を使用した製作物には、ICSP 端子を設けるようにしている。 ("FMステレオラジオ II" では設けていなかった。)
  • また上1つ目の実現のためには、PIC16F1 ファミリーを使用すれば良いことが分かっているので、私が長い間先延ばしにしていた新たなアーキテクチャと、追加されたアセンブラ命令を理解するために、 本機へのプログラム移植と新機能のためのプログラム追加を通して、遅まきながら挑戦をしてみたい。
 また、このホームページについても当初は、"FMステレオラジオ II" のページに同居をさせるつもりでいましたが、同ページは既に大きく膨み過ぎているため、同ページと重複するような内容はなるべく避けて、 やはり、この "FMステレオラジオ III" 用のページを新たに作成しました。

■ 回路図 ■

*注1. PIC の RA0、RA1、RA4、RA5、RC4、RC5 は内部プルアップ機能を ON にして使用。
*注2. LED(高輝度 赤)は ステレオ放送のときに点灯させるもので、この LED は正面から見ることが多くて眩しいので、輝度を抑えるために電流制限抵抗は通常より大きな抵抗を使用。

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

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

ケース正面の斜め上から見たところ ケース上側面の斜め上から見たところ
ケース左側面の斜め上から見たところ ケース右側面の斜め上から見たところ
ケース正面を真上から見たところ ケース裏面を真上から見たところ
蓋をあけて内部の様子を見た様子( 拡大 電源スイッチを ON 後、選局をした様子

| ページトップ |

■ 機能概要と使用法 ■

 この項では、"FMステレオラジオ II" から "FMステレオラジオ III" に、ハードウェアの変更とプログラムの移植をした後で、新たに追加をした機能についてだけを説明します。 その他の機能については、"FMステレオラジオ II" の機能を引継いで同様のため、そちらの 機能概要と使用法 を参照してください。

 なお、前者の 機能概要と使用法 で "EEPROM" についての記述は、後者では "高書き込み耐性プログラム フラッシュメモリ (PFM)" と読み替えてください。 両者では使用した PIC が異なっているためハードウェア構造も異なっていますが、 どちらも不揮発性メモリで行っている目的は同様で変わりません。

○ スイッチ操作による開始メッセージの表示を省略
  • 従来は本機の電源スイッチをオンにすると、必ず LCD に次の開始メッセージが表示されるため、ラジオ音声が出力されるまでに約 10 秒間を要したが、スイッチ操作によってこの時間を省略する機能を追加した。 スイッチ操作がなければ、通常通り次の開始メッセージが表示される。
      			[ FmStereo ]			[ Ver x.xx ]
      			[ RadioIII ]			[  by M.Y  ]
      

  • 4個のタクトスイッチの内、真ん中の2個、すなわち TUNING_DOWN スイッチと VOLUME_UP スイッチを、同時に押しながら 電源スイッチをオンにすると、開始メッセージの表示が省略されて、直ちに、前回のセーブ情報に基づいたラジオ局情報等の LCD 表示とともに音声出力がされる。
○ 電源の電圧低下に伴う LCD 表示のコントラスト低下を自動補正
  • 本機では本体電源に乾電池を使用しているため、時間の経過とともに電圧低下を起こし、LCD の電源電圧も低下して一定値を下回ってくると、LCD のコントラストが弱くなって、表示文字が見づらくなるため、この弱まったコントラストを自動で補強をする というもの。

  • 自動補正するタイミングは、電源スイッチをオンにした直後、PIC のスリープ状態からウェイクアップした直後、および4個のタクトスイッチのどれかを操作した直後に行う。 それぞれ、そのときの LCD モジュールの電源電圧を測定し、 その電圧値が低下をしてきても LCD コントラストをほぼ一定に保つように、自動的に設定をする。

  • これらのとき本機能の追加以前は、LCD には次のように一連の表示がされていたが、
      			[ NHK FM   ]			[ NHK FM   ]			[ NHK FM   ]
      			[  Vol:xx  ]			[  Lev:xx  ]			[  82.5MHz ]
      

    これらの表示に先立って、LCD モジュールの電源電圧*の測定値と LCD コントラストの設定値が、2秒間追加されて次のように表示される。 したがって、上の3つの表示は2秒間ずつ遅れて表示されるようになる。 ( *LCD モジュールの電源電圧 ≒ 本体の電源電圧 )
      			[ NHK FM   ]
      			[ x.xxV xx ]
      

  • これらの表示等のタイミングの関係は、本機能の追加後では次のように変更になったが、表示中に4個のタクトスイッチのどれかを操作したときには、現在カウント中のタイマーが、必ずクリアされて新たに0からカウントがし直されることには、 従来と変わりはない。
      		タイマーカウントの開始 ・・・ FMラジオモジュールの設定、ラジオ局名の表示
      						LCDモジュールの電源電圧値、LCDコントラストの設定値の表示
      		   カウントの2秒後 ・・・ 音量設定値の表示
      		   		4秒後 ・・・ FMラジオモジュールから情報の読み出し
      						受信強度の表示
      				6秒後 ・・・ 周波数の表示
      				8秒後 ・・・ セーブ情報をバックアップ
      				1分後 ・・・ スリープ状態に移行
      
  • したがって、従来の機能概要(FMステレオラジオ II で説明)の秒数の記述に関しても、本機能の追加後においては、上記のように変更になった秒数に読み替えて頂きたい。

| ページトップ |

■ プログラム ■

 前作 "FMステレオラジオ II" で使用していた PIC16F684 を、本機 "FMステレオラジオ III" では、同じ 14 ピンの PIC16F1705 に変更をしたことから、プログラムを移植するに当たっては少なからず大変でした。 というのも、前者、すなわち従来の PIC16F ファミリーと、後者の PIC16F1 ファミリーのものとではアーキテクチャが異なっているため、前者用のプログラムをそのままでは、もちろん、単純に後者で使用することはできません。

 私にとって PIC16F1 ファミリーを使用するのが、遅まきながら今回が初めてである、ということを冒頭でも少しばかり触れましたが、本機 "FMステレオラジオ III" の製作を通して、プログラムを作成するという観点で、 私が新たに学んだことやトラブった経験などをここにまとめておきます。  PIC16F1 ファミリーでは、プログラムメモリ、データメモリともに、それらの構成が大きく変更(拡張)された、ということが最大の特徴となっています。

● プログラムメモリの構成とプログラムカウンタ(PC)

 初めに、プログラムメモリの構成とプログラムカウンタ PC との関係を図に表し、従来の PIC16F ファミリーと新しい PIC16F1 ファミリーとを、比較してみると分かり易くなると思います。

 まず、次図に示すのは従来の PIC16F ファミリー のプログラムメモリで、2K ワードずつのページ単位が、最大でページ 0 〜 3 までの 4 ページ(2K x 4 = 8K ワード)で構成 されることから、この全メモリをアドレス指定ができるように、 プログラムカウンタ PC は 13 ビット で構成されています。 この最大 4 ページのプログラムメモリの内、"FMステレオラジオ II" で使用した PIC16F684 では、薄緑色に彩色をしたページ 0 だけ(2K ワード)が実装されています。
 これに対して PIC16F1 ファミリー では次図に示すように、2K ワードずつのページ単位が、最大でページ 0 〜 15 までの 16 ページ(2K x 16 = 32K ワード) の 4 倍のメモリ空間に拡張され、その全域のアクセスが可能なように、 プログラムカウンタ PC は 15 ビット で構成されています。 そして同様に、本機 "FMステレオラジオ III" で使用した PIC16F1705 では、薄緑色に彩色をしたページ 0 〜 3 までの 4 ページ(2K x 4 = 8K ワード)が実装されています。
 ここで申し遅れましたが、どちらの場合にも1ワードのプログラムメモリのビット幅、すなわち1命令は 14 ビットで構成されていて、この点は変更はありません。

 しかし、上述のようにプログラムカウンタ PC は、13 ビットから 15 ビットに拡張されたため、それに伴って、スタックメモリのビット幅も 15 ビットに拡張され、しかも、従来は 8 レベルしか存在していなかったのが、 倍の 16 レベルに拡張されました。 最近、時々オーバーフローをさせることが増えてきた私にとっては、とても心強く感じられます。
 また、PIC16F1 ファミリーでは上図に示すように、プログラムカウンタ PC の値を変更するための3つの命令が追加されており、いずれもジャンプ先のアドレスがページをまたがっていてもOKで、問題は起こりません。 しかし、本機 "FMステレオラジオ III" では、これらの命令は現在のところ使用していません。

 他に、ページを切り替えるために用意された命令で、PCLATH レジスタにリテラル値を設定するための、MOVLP(Move literal to PCLATH)があり、この命令だけの1命令1サイクルでページを切り替えることができます。 本機のプログラムはページ 0 だけで収まっているため、この命令も使用していません。

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


● データメモリの構成とアドレス指定

 次に、データメモリの構成とアドレス指定との関係を図に表し、ここでも従来の PIC16F ファミリーと新しい PIC16F1 ファミリーとを、比較してみます。

 まず、次図に示すのは従来の PIC16F ファミリー のデータメモリで、128 バイトずつのバンク単位が、最大でバンク 0 〜 3 までの 4 バンク(128 x 4 = 512 バイト)で構成 されており、大きく 特殊機能レジスタ (SFR)汎用レジスタ(GPR) に分けられます。 これらのデータメモリの内、"FMステレオラジオ II" で使用した PIC16F684 では、黄色と薄緑色に彩色をした部分だけしか実装されていません。 なお、バンク 1 の GPR の内、--- より下に示した 16 バイトの部分は実際には存在せず、バンク 0 の同位置 16 バイトと共通となっていて、すべてのバンクからアクセスが可能なメモリです。

 また、これらの各メモリ(= RAM =レジスタ=変数)をプログラムで使用するときには、次の関係図のように、直接アドレス指定と間接アドレス指定の2つの方法があります。

 直接アドレス指定 の場合には、STATUS レジスタの RP1 ビットと RP0 ビットの 2 ビットでバンク指定を行い、命令のレジスタ指定部分の 7 ビットで各バンク内の1レジスタを直接指定するのに対して、 間接アドレス指定 の場合には、STATUS レジスタの IRP ビットと FSR レジスタの最上位ビットの 2 ビットでバンク指定を行い、また FSR レジスタの残りの 7 ビットで各バンク内の1レジスタを間接的に指定を行う方法です。
 一方、PIC16F1 ファミリー のデータメモリは次図に示すように、128 バイトずつのバンク単位が、最大でバンク 0 〜 31 までの 32 バンク(128 x 32 = 4,096 バイト) の 8 倍のメモリ空間に、大幅に拡張がされています。 本機 "FMステレオラジオ III" で使用した PIC16F1705 では、黄色と薄緑色に彩色をした部分が実装されています。
    ( 2022/5/25 追記 )
     同じ PIC16F1 ファミリーの中でも、例えば PIC16F18857/77 のような比較的新しいデバイスでは、バンク 0 〜 63 までの 64 バンク(128 x 64 = 8,192 バイト) のメモリ空間に拡張がされています。 そして、そのほとんどは GPR を増やすためと思われ、バンク 0 〜 50 までの GPR で 80 x 51 = 4,080 バイトの、次項で述べている リニア データメモリ 領域を格保しています。 (ちなみに PIC16F18857/77 では、プログラムメモリも ページ 0 〜 15 までの、16 ページ(2K x 16 = 32K ワード)がフル搭載されています。)
 そして、各バンクの最下位メモリ(図上では最上位)に位置するコアレジスタは、すべてのバンクからアクセスができる共通メモリで、そこに含まれるレジスタの種類を TABLE 3-2 に示します。 また、バンク 31 に位置するシャドウレジスタの種類を TABLE 3-8 に示します。 なお、図中の数字は各領域のレジスタが占めるバイト数を表しています。 (これらの TABLE は、Microchip Technology 社のデータシート(PIC16(L)F1705/9)から抜粋して、私が彩色を施したもの)
 PIC16F1 ファミリーにおいても個別のレジスタを識別するのに、直接アドレス指定と間接アドレス指定の2つの方法がありますが、上図に示すように 32 バンクもあって、従来のような STATUS レジスタによる 2 ビット(RP1, RP0)では、 バンク指定ができないために廃止をして、直接アドレス指定専用に新たな バンクセレクト レジスタ ( BSR ) (TABLE 3-2 を参照)が設けられました。 そして、この BSR レジスタにリテラル値を設定するための専用の命令、 MOVLB(Move literal to BSR) も追加されています。

 また、間接アドレス指定をする場合にも、最大 4,096 バイトもあるデータメモリを識別するには、従来のように STATUS レジスタの IRP ビットと FSR レジスタだけでは、データメモリ全域のアドレス指定をすることができないため、 2 バイトの FSR レジスタペア FSRnH と FSRnL が設けられています。 しかも FSR0HFSR0LFSR1HFSR1L (TABLE 3-2 を参照)の2組が用意され、 INDF0INDF1 レジスタが これらのレジスタペアに対応した、間接レジスタ となっています。

 そして、これらのレジスタで間接アクセスを容易にするために、間接レジスタ(INDFn)から W レジスタへデータを移動する命令 MOVIW(Move INDFn to W)、逆に W レジスタから間接レジスタ(INDFn)へデータを移動する命令、 MOVWI(Move W to INDFn)などが新たに追加されています。 これらの命令を使用した例を 下のリスト に示してありますが、記述方法には他にもいろいろなバリエーションが用意されており、 Cコンパイラに最適化された命令になっています。

 なお、これらの間接アドレス指定レジスタはデータメモリの指定だけでなく、PIC16F1 ファミリーでは プログラムメモリの指定にも使用 されます。 下の例 EXAMPLE 3-2 はデータシートから抜粋したものですが、ラベルがプログラムメモリ内の位置を指し示している場合、High ディレクティブにより FSRnH の bit<7> がセットされる、とのことです。


 従来は、割り込み時の必要なレジスタの内容保存(退避)とそれらの復元は、ユーザの責任においてプログラムで行っていましたが、新 PIC16F1 ファミリーでは、割り込みが発生すると重要なレジスタの内容がシャドウレジスタに自動保存 されます(上表 TABLE 3-8 を参照)。 そして、割り込みルーチンから抜け出すための RETFIE 命令が実行されると、シャドウレジスタに保存(退避)されていた内容が、再び元のレジスタに復元されます。

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


● 間接アドレス指定(補足)

 PIC16F1 ファミリーの FSRnH と FSRnL のレジスタペアで、アクセスされるアドレス空間は 0000h 〜 FFFFh で、64K(65,536)バイトの空間を指定することができます。 アドレスが 下位の 32K(32,768)バイトデータメモリ(RAM)空間上位の 32K(32,768)バイトプログラムメモリ(ROM)空間 となっています。 プログラムメモリ(ROM)空間は、下位バイト(14 ビットの内、下位 8 ビット)をアクセスして、定数データとして取り扱います。

 下位の 32K(32,768)バイトの内、最下位の 4Kバイト(4,096)は 従来型データメモリ 領域(0000h 〜 0FFFh:下図 FIGURE 3-8 で薄緑色の彩色部分)で、このアドレスは、すべての SFR / GPR / 共通レジスタの絶対アドレスに該当します。 連続的にアクセスは可能ですが、バンクごとに SFR の領域が入ってしまいます。

 これに対して、2000h から始まる 2,480 バイトの リニア データメモリ 領域(2000h 〜 29AFh:下図 FIGURE 3-8 で水色の彩色部分)は仮想領域で、全バンクにある GPR(96 バイトの内、共通に使われる 16 バイトを除く) 80 バイトずつをすべてつないで、1つの連続した RAM 領域として仮想的に構成したもので、連続した空間としてのアクセスができるようにしました。 これで、C言語などで大きな配列や構造体を扱うことができるようになります。


 既に上述したように、全アドレス空間の内、上位の 32K(32,768)バイトが プログラム フラッシュメモリ 領域(8000h 〜 FFFFh:上図 FIGURE 3-8 で黄色の彩色部分)で、定数データのアクセスを容易にするために、 プログラム フラッシュメモリ全体が同空間に割り当てられています。

 FSRnH レジスタの MSB(最上位)ビットがセットされている場合、レジスタペアの下位 15 ビットがプログラムメモリのアドレスとなり、INDFn レジスタでアクセスされます。 ただし、INDFn レジスタでアクセスできる場所は、各メモリ位置の下位バイト(14 ビットの内、下位 8 ビット)のみです。

 また、この項で述べている FSR / INDF インターフェイスでは、プログラム フラッシュメモリにデータを書き込むことはできません。 プログラム フラッシュメモリに書き込むには、 次項の 高書き込み耐性フラッシュメモリのプログラミング を参照してください。

 以上述べてきた、3つのメモリ領域にアクセスするときの、FSRnH と FSRnL レジスタペアの各ビットが、どのようにアドレス指定に関わっているかを図に表したものが、次の FIGURE 3-9:従来型データメモリ、FIGURE 3-10:リニア データメモリ、 FIGURE 3-11:プログラム フラッシュメモリです。


 なお、この項で使用した FIGURE 3-8 〜 FIGURE 3-11 は、Microchip Technology 社のデータシート(PIC16(L)F1705/9)から抜粋して、私が彩色を施したものです。

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


● 高書き込み耐性フラッシュメモリのプログラミング

 従来、データの保存に使用されていた不揮発性メモリの EEPROM が、本機 "FMステレオラジオ III" で使用した PIC16F1705 には存在していません。 同じ PIC16F1 ファミリーでも種類によっては、従来通り存在するものもあるようですが、 PIC16F1705 では EEPROM に代わるものとして、高書き込み耐性プログラム フラッシュメモリ (PFM) が、実装されている全プログラムメモリの内、最終ページ 3 の アドレス 1F80h 〜 1FFFh に 128 バイト 分が備えられています。

 今回、本機にプログラムを移植するに当たって、EEPROM に代わってこの高書き込み耐性フラッシュメモリに変更をしなければならず、データシートの例を参考にしながらプログラミングを行ったのですが、その扱いは従来の EEPROM に比べてかなり面倒でした。

 EEPROM の場合には、単純に(といっても、それなりの手順はありますが)必要なバイトだけを、読み書きを行えば良かったのですが、フラッシュメモリに(書き換えるために)書き込む場合には、その前に予め書き込むメモリに対して消去処理をしておかなければならない、 というのが EEPROM の場合と比べて最も異なっている点です。

 しかも、旧データを新データに書き換える場合の事前の消去処理は、たとえ1バイトの書き換えであっても、その対象となる1バイトのメモリだけを消去することができず、そのメモリが所属する行全体を消去しなければなりません。 フラッシュメモリは行単位で構成されていて、1行は一定数のメモリワードで構成されているため、この1行がユーザプログラムで消去できる最小サイズとなっています。 この最小サイズは PIC の種類によって異なっていて、本機の PIC16F1705 では 32 ワードとなっています。

 ここで、バイトと言ったりワードと言ったりして紛らわしいので、整理をしておきます。 プログラムの命令(1ワード = 14 ビット)を格納するための、プログラムメモリ全体がフラッシュメモリで構成されているのですが、 その内の高書き込み耐性フラッシュメモリ(アドレス 1F80h 〜 1FFFh の 128 バイト分)に関しては、下位バイトしか存在しなくて(上位バイトがない)、すなわち、1ワード = 8 ビット = 1 バイトとなっています。

 次に示した リストの末尾 に、フラッシュメモリに格納する本機のセーブ情報の構成(3 x 4 = 12 バイト)を定義した部分がありますが、この内データ更新の対象となるのは、本機の場合には常に 3 バイトだけですが、 上述のように1行(32 バイト)単位で消去をするため、書き換えの対象以外のメモリ部分も消去されてしまいます。

 したがって、本来の書き換えの対象(3 バイト)以外のメモリ部分も含めて、すなわち、セーブ情報(3 x 4 = 12 バイト)全体を書き換えなければなりません。 そのために本機では、電源スイッチが ON にされたプログラムの起動直後に、 フラッシュメモリに格納されているセーブ情報(12 バイト)全体を読み出して、データメモリ上にも同内容のコピーを保存しています。

 そして、セーブ情報の書き換えが発生したときには、まず、データメモリ上に保存をしてあるセーブ情報(3 x 4 = 12 バイト)の内、対象となる 3 バイトを更新し、次に、データメモリ上のセーブ情報(12 バイト)全体を対象として、 フラッシュメモリの書き換え、すなわち、対象となるフラッシュメモリの行(32 バイト)を消去の後、セーブ情報(12 バイト)の再書き込みを行っています。

 また、このフラッシュメモリに複数バイト(ワード)を書き込む場合にも2段階の手順が必要で、本機の場合には 12 バイトのデータを書き込むわけですが、まず 1 〜 11 バイト目までは 書き込みラッチに書き込む ように指示(PMCON1 レジスタの LWLO ビットをセット) をし、最後の 12 バイト目のときに フラッシュメモリに書き込む ように指示(PMCON1 レジスタの LWLO ビットをクリア) をして、それぞれの書き込みデータを送るようにします。 その結果、書き込みラッチ上のデータも含めてすべての 12 バイトが、フラッシュメモリに書き込まれます。

 本機においてのフラッシュメモリに関する処理プログラムの、まずはメイン側での抜粋リストを次に示します。
      
      		; セーブ情報(ラジオ局番号、音量)の復帰
      
      		call	flash_mem_read	;フラッシュメモリから RAM バッファへデータ読み出し
      		call	ram_buff_read	;RAM バッファから個々の変数へコピー
      		goto	main
      
      			:
      			:
      
      		; バックアップ要求フラグのチェック
      
      m11		btfss	updt_flg,4	;バックアップ要求フラグ = ON か?
      		goto	m12		;No
      
      		call	ram_buff_write	;個々の変数から RAM バッファへコピー
      		call	flash_mem_erase	;フラッシュメモリを1行(32 ワード)消去
      		call	flash_mem_write	;RAM バッファからフラッシュメモリへデータ書き込み
      
      		bcf	updt_flg,4	;バックアップ要求フラグ = OFF
      		goto	main
      
      			:
      			:
      
 そして、上のメイン側のリスト中で CALL しているそれぞれのサブルーチンとその関連を、次に示します。
      ;==========================================================================
      ;		定数の定義と変数のレジスタ割付け
      ;==========================================================================
      
      			:
      			:
      
      		cblock	h'20'		;バンク 0
      
      			:
      			:
      sel_sw					;SELECT 1,0 スイッチ値の保存用
      radio_no ;=chan_h(sel_sw=b'11'のとき)	;ラジオ局番号	┐
      chan_l	 ; chan_l(sel_sw=b'11'のとき)	;dummy		│フラッシュメモリ セーブ情報
      volume					;音量		┘
      seekup					;RDA_Reg 02h.9 = SEEKUP
      			:
      			:
      		endc
      
      		cblock	h'1a0'		;バンク 3
      f_lpcnt					;ループカウンタ
      save_data_ram				;3 * 4 = 12 バイト確保
      		endc
      			:
      			:
      
      ;--------------------------------------------------------------------------
      ;    High-Endurance フラッシュメモリ から RAM バッファへ データ読み出し
      ;--------------------------------------------------------------------------
      
      flash_mem_read
      		movlb	3		;バンク 3
      		movlw	low save_data_ram
      		movwf	FSR1L		;間接アドレスに設定
      		movlw	high save_data_ram
      		movwf	FSR1H
      		movlw	3 * 4
      		movwf	f_lpcnt		;ループカウンタ
      
      		movlw	low save_data
      		movwf	PMADRL
      		movlw	high save_data
      		movwf	PMADRH		;フラッシュメモリの記録アドレスを設定
      
      fmemrd01	bcf	PMCON1,CFGS	;フラッシュメモリにアクセス
      		bsf	PMCON1,RD	;読み出し制御 = ON、ハードウェアでクリア
      		nop
      		nop
      		movf	PMDATL,W	;下位データの読み出し
      		movwi	FSR1++		;
      	;	movf	PMDATH,W	;上位データの読み出し
      	;	movwi	FSR1++		;
      		incf	PMADRL,F	;記録アドレスの更新
      		decfsz	f_lpcnt,F	;ループカウンタ - 1 = 0 か?
      		goto	fmemrd01	;No
      
      		movlb	0		;バンク 0
      		return
      
      ;--------------------------------------------------------------------------
      
      		; RAM バッファから個々の変数へコピー
      ram_buff_read
      		movf	sel_sw,W	;SELECT 1,0 スイッチ値
      		addwf	sel_sw,W
      		addwf	sel_sw,W	;x 3
      		addlw	low save_data_ram
      		movwf	FSR1L
      		clrf	FSR1H
      		movlw	high save_data_ram
      		addwfc	FSR1H,F
      
      		moviw	0[FSR1]
      		movwf	radio_no	;ラジオ局番号	┐
      		moviw	1[FSR1]
      		movwf	chan_l		;dummy		│フラッシュメモリ セーブ情報
      		moviw	2[FSR1]
      		movwf	volume		;音量		┘
      
      		movlw	b'11'
      		subwf	sel_sw,W
      		btfss	STATUS,Z	;SELECT 1,0 スイッチ = b'11' か?
      		goto	bufrd01		;No
      					;chan_l = xx00.00x0
      		movlw	h'02'		;         CC  .  S 
      		andwf	chan_l,W
      		movwf	seekup		;SEEKUP を取り出す
      		movlw	h'c0'
      		andwf	chan_l,F	;CHAN[1:0] を取り出す
      
      bufrd01		return
      
      ;--------------------------------------------------------------------------
      ;	    High-Endurance フラッシュメモリを1行(32 ワード)消去
      ;--------------------------------------------------------------------------
      
      flash_mem_erase
      		bcf	INTCON,GIE	;割り込みを禁止
      		movlb	3		;バンク 3
      		movlw	low save_data
      		movwf	PMADRL
      		movlw	high save_data
      		movwf	PMADRH
      
      		bcf	PMCON1,CFGS	;フラッシュメモリにアクセス
      		bsf	PMCON1,FREE	;消去操作を指定、ハードウェアでクリア
      		bsf	PMCON1,WREN	;書き込みを許可
      
      		movlw	h'55'
      		movwf	PMCON2		;h'55' を PMCON2 に書き込む
      		movlw	h'aa'
      		movwf	PMCON2		;h'aa' を PMCON2 に書き込む
      		bsf	PMCON1,WR	;書き込み制御 = ON、ハードウェアでクリア
      		nop
      		nop
      		bcf	PMCON1,WREN	;書き込みを禁止
      		movlb	0		;バンク 0
      		bsf	INTCON,GIE	;割り込みを許可
      		return
      
      ;--------------------------------------------------------------------------
      ;    RAM バッファから High-Endurance フラッシュメモリへ データ書き込み
      ;--------------------------------------------------------------------------
      
      flash_mem_write
      		bcf	INTCON,GIE	;割り込みを禁止
      		movlb	3		;バンク 3
      		movlw	low save_data_ram
      		movwf	FSR1L		;間接アドレスに設定
      		movlw	high save_data_ram
      		movwf	FSR1H		;
      		movlw	3 * 4
      		movwf	f_lpcnt		;ループカウンタ
      
      		movlw	low save_data
      		movwf	PMADRL		;
      		movlw	high save_data
      		movwf	PMADRH		;
      
      		bcf	PMCON1,CFGS	;フラッシュメモリにアクセス
      		bsf	PMCON1,WREN	;書き込みを許可
      		bsf	PMCON1,LWLO	;書き込みラッチへ書き込み指示
      
      fmemwr01	moviw	FSR1++		;最初のデータバイトを下位にロード
      		movwf	PMDATL		;
      	;	moviw	FSR1++		;2番目のデータバイトを上位にロード
      	;	movwf	PMDATH		;
      		decfsz	f_lpcnt,F	;ループカウンタ - 1 = 0 か?
      		goto	fmemwr02	;No
      		goto	fmemwr03	;Yes
      
      fmemwr02	movlw	h'55'
      		movwf	PMCON2		;h'55' を PMCON2 に書き込む
      		movlw	h'aa'
      		movwf	PMCON2		;h'aa' を PMCON2 に書き込む
      		bsf	PMCON1,WR	;書き込み制御 = ON、ハードウェアでクリア
      		nop
      		nop
      		incf	PMADRL,F	;記録アドレスの更新
      		goto	fmemwr01
      
      fmemwr03	bcf	PMCON1,LWLO	;フラッシュメモリへ書き込み指示
      		movlw	h'55'
      		movwf	PMCON2		;h'55' を PMCON2 に書き込む
      		movlw	h'aa'
      		movwf	PMCON2		;h'aa' を PMCON2 に書き込む
      		bsf	PMCON1,WR	;書き込み制御 = ON、ハードウェアでクリア
      		nop
      		nop
      		bcf	PMCON1,WREN	;書き込みを禁止
      		movlb	0		;バンク 0
      		bsf	INTCON,GIE	;割り込みを許可
      		return
      
      ;--------------------------------------------------------------------------
      
      		; 個々の変数から RAM バッファへコピー
      ram_buff_write
      		movf	sel_sw,W	;SELECT 1,0 スイッチ値
      		addwf	sel_sw,W
      		addwf	sel_sw,W	;x 3
      		addlw	low save_data_ram
      		movwf	FSR1L
      		clrf	FSR1H
      		movlw	high save_data_ram
      		addwfc	FSR1H,F
      
      		movf	radio_no,W	;ラジオ局番号	┐
      		movwi	0[FSR1]
      		movf	chan_l,W	;dummy		│フラッシュメモリ セーブ情報
      		movwi	1[FSR1]
      		movf	volume,W	;音量		┘
      		movwi	2[FSR1]
      		return
      
      			:
      			:
      
      ;==========================================================================
      ;		High-Endurance フラッシュメモリ
      ;==========================================================================
      
      		org	h'1f80'
      
      		; セーブ情報(ラジオ局番号、音量)の格納位置
      
      save_data				;以下の設定値は初回の起動時のみ
      
      		de	3		;ラジオ局番号(default=3:NHK FM) … ( 名古屋周辺地区 )
      		de	h'00'		;dummy
      		de	h'02'		;音量
      
      		de	4		;ラジオ局番号(default=4:NHK TKO)… ( 東京周辺地区 )
      		de	h'00'		;dummy
      		de	h'02'		;音量
      
      		de	3		;ラジオ局番号(default=3:NHK FM) … ( 大阪周辺地区 )
      		de	h'00'		;dummy
      		de	h'02'		;音量
      
      		de	h'00'		;CHAN-H: Reg03H[15:8]=CHAN[9:2]   … ( SEEK機能で選局時 )
      		de	h'02'		;CHAN-L: Reg03H[ 7:6]=CHAN[1:0] + Reg02H[9]=SEEKUP
      		de	h'02'		;音量
      
      
 次の表 TABLE 3-3 は、PIC16F1705 の具体的なデータメモリの一部分で、本機のプログラムを作成する上で必要な部分を、データシートから抜粋して彩色をしたものです。

 上リストで示した、フラッシュメモリの読み書き等を行うために必要となる各種の SFR レジスタ( PMxxxx )は、バンク 3 に集中をしています。 したがって、フラッシュメモリに関して使用する変数 ( f_lpcnt ) とバッファ ( save_data_ram ) も、 バンク切り替えを行わなくても済むように、同じ バンク 3 の GPR 内に定義 をしました。 ただし、バッファ ( save_data_ram ) については間接アドレス指定で使用をするため、どのバンクにあっても構わないのですが。


 なお、上表において 水色 に彩色したレジスタが、フラッシュメモリの読み書き等を行うために必要な SFR レジスタですが、ピンク色 に彩色したレジスタは、次項で説明をする ADC モジュールのプログラミングに必要となる SFR レジスタです。 他にも 黄色 に彩色した SFR レジスタを、本機のプログラム内では使用しています。

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


 ◎ プログラム移植をした直後の ちょっとしたトラブル

 ソースプログラムを書き換えて移植を行った(もちろんアセンブルを済ませた)直後に、初めてプログラムを起動させたときには、上に示した リストの末尾 のように、予め 高書き込み耐性(High-Endurance)フラッシュメモリ に設定されている、デフォルトのセーブ情報が使用されて、ラジオ局の選局や、音量の設定がされる ― 筈なんですが ・・・

 上のリストの末尾では、名古屋周辺地区の場合、ラジオ局番号 = 3、音量 = 2 が デフォルト、として設定がされています。 そして、名古屋周辺地区用の FM ラジオ放送局 データテーブル ( fm_radio_n_tbl ) を次に示しますが、
      ;--------------------------------------------------------------------------
      ;		FM ラジオ放送局 データテーブル 
      ;--------------------------------------------------------------------------
      
      			:
      			:
      fm_radio_n_tbl
      ;		                                                         00: SPACE = 100 kHz
      ;			 局名           周波数       ┌─  CHAN ──┐  ┌┐	;ラジオ局番号
      
      		dt	"MID-FM  ",0, " 76.1MHz",0, b'00000000', b'01011000'	; 0 … ( 名古屋周辺地区用 )
      		dt	"ZIP-FM  ",0, " 77.8MHz",0, b'00000100', b'10011000'	; 1
      		dt	"FM AICHI",0, " 80.7MHz",0, b'00001011', b'11011000'	; 2
      		dt	"NHK FM  ",0, " 82.5MHz",0, b'00010000', b'01011000'	; 3
      		dt	"TOKAI   ",0, " 92.9MHz",0, b'00101010', b'01011000'	; 4
      		dt	"CBC     ",0, " 93.7MHz",0, b'00101100', b'01011000'	; 5
      
      ;			                                              └┘
      ;			                                               10: BAND = 76-108 MHz
      fm_radio_n_tbl_end
      			:
      			:
      
ラジオ局番号 = 3 は "NHK FM" として設定がされています。 ところが、初めてプログラムを起動させたときに、ラジオ局番号 = 0 の "MID-FM" が選局されるのです。 また、そのときのデフォルトの音量 = 2 の筈が 0 に設定されてしまうのです。???

 しかし、起動後に4個のスイッチをそれぞれ操作をしてみて、選局変更や音量変更の機能には異状がないことを確認しました。 また、これらのスイッチ操作の後で電源スイッチを入れ直してみても、セーブ情報のバックアップが正常に取られることも、 確認がとれました。 要するにアセンブル直後のプログラムを起動させた場合だけ、デフォルトのセーブ情報がおかしいのです。 このアセンブル直後のプログラム起動 〜 スイッチ操作後のバックアップ確認も、数回繰り返してみたので間違いはありません。

 こんな現象が起こるのは何故 ???  こういったトラブルが起きたときには、第一番に疑うのはやはり自分が書いたプログラムで、上記リスト中の 高書き込み耐性フラッシュメモリから RAM バッファへ データ読み出しサブルーチン ( flash_mem_read ) にバグがあるのではないか、とかいろいろなことを考えたりします。

 実は、上に示した リストの末尾 のセーブ情報のテーブル ( save_data ) は修正後のもので、このトラブルが起きていたときには次のように書いてありました。
      ;==========================================================================
      ;		High-Endurance フラッシュメモリ
      ;==========================================================================
      
      		org	h'1f80'
      
      		; セーブ情報(ラジオ局番号、音量)の格納位置
      
      save_data				;以下の設定値は初回の起動時のみ
      
      		db	3		;ラジオ局番号(default=3:NHK FM) … ( 名古屋周辺地区 )
      		db	h'00'		;dummy
      		db	h'02'		;音量
      
      		db	4		;ラジオ局番号(default=4:NHK TKO)… ( 東京周辺地区 )
      		db	h'00'		;dummy
      		db	h'02'		;音量
      
      		db	3		;ラジオ局番号(default=3:NHK FM) … ( 大阪周辺地区 )
      		db	h'00'		;dummy
      		db	h'02'		;音量
      
      		db	h'00'		;CHAN-H: Reg03H[15:8]=CHAN[9:2]   … ( SEEK機能で選局時 )
      		db	h'02'		;CHAN-L: Reg03H[ 7:6]=CHAN[1:0] + Reg02H[9]=SEEKUP
      		db	h'02'		;音量
      
 この修正前のリストと修正後のリストとで、違いが分かりますか? プログラムを移植する前のソースリストでは、もちろん、DE ディレクティブ(疑似命令)で書いてありました。 それを移植するときに、わざわざ DB ディレクティブに 書き換えたのですが、これが大きな誤りでした。 DE ディレクティブは EEPROM 内にデータを定義をするもの、と勝手に思い込んでいて、移植後は EEPROM ではないし、でもバイトデータを確保するのだから DB ディレクティブだろう、 と思って書き換えたのですが ・・・

 この2つのディレクティブの違いは、後になってアセンブルリストを見て分かりました。 まず修正前( DB )のアセンブルリストを示します。
      < 修正前リスト >    02767                 org     h'1f80'
                            02768 
             ↓↓           02769                 ; セーブ情報(ラジオ局番号、音量)の格納位置
                            02770 
      1F80                  02771 save_data                               ;以下の設定値は初回の起動時のみ
                            02772 
      1F80   0300           02773                 db      3               ;ラジオ局番号(default=3:NHK FM) … ( 名古屋周辺地区 )
      1F81   0000           02774                 db      h'00'           ;dummy
      1F82   0200           02775                 db      h'02'           ;音量
                            02776 
      1F83   0400           02777                 db      4               ;ラジオ局番号(default=4:NHK TKO)… ( 東京周辺地区 )
      1F84   0000           02778                 db      h'00'           ;dummy
      1F85   0200           02779                 db      h'02'           ;音量
                            02780 
      1F86   0300           02781                 db      3               ;ラジオ局番号(default=3:NHK FM) … ( 大阪周辺地区 )
      1F87   0000           02782                 db      h'00'           ;dummy
      1F88   0200           02783                 db      h'02'           ;音量
                            02784 
      1F89   0000           02785                 db      h'00'           ;CHAN-H: Reg03H[15:8]=CHAN[9:2]   … ( SEEK機能で選局時 )
      1F8A   0200           02786                 db      h'02'           ;CHAN-L: Reg03H[ 7:6]=CHAN[1:0] + Reg02H[9]=SEEKUP
      1F8B   0200           02787                 db      h'02'           ;音量
      
 次に修正後( DE )のアセンブルリストを示します。 両者の左上に印した↓↓の部分に注目をして見てください。 ↓↓の部分はアセンブラによって翻訳された機械語です。
      < 修正後リスト >    02767                 org     h'1f80'
                            02768 
             ↓↓           02769                 ; セーブ情報(ラジオ局番号、音量)の格納位置
                            02770 
      1F80                  02771 save_data                               ;以下の設定値は初回の起動時のみ
                            02772 
      1F80   0003           02773                 de      3               ;ラジオ局番号(default=3:NHK FM) … ( 名古屋周辺地区 )
      1F81   0000           02774                 de      h'00'           ;dummy
      1F82   0002           02775                 de      h'02'           ;音量
                            02776 
      1F83   0004           02777                 de      4               ;ラジオ局番号(default=4:NHK TKO)… ( 東京周辺地区 )
      1F84   0000           02778                 de      h'00'           ;dummy
      1F85   0002           02779                 de      h'02'           ;音量
                            02780 
      1F86   0003           02781                 de      3               ;ラジオ局番号(default=3:NHK FM) … ( 大阪周辺地区 )
      1F87   0000           02782                 de      h'00'           ;dummy
      1F88   0002           02783                 de      h'02'           ;音量
                            02784 
      1F89   0000           02785                 de      h'00'           ;CHAN-H: Reg03H[15:8]=CHAN[9:2]   … ( SEEK機能で選局時 )
      1F8A   0002           02786                 de      h'02'           ;CHAN-L: Reg03H[ 7:6]=CHAN[1:0] + Reg02H[9]=SEEKUP
      1F8B   0002           02787                 de      h'02'           ;音量
      
 両者の機械語では、上位バイトと下位バイトが逆に翻訳されています。

 高書き込み耐性フラッシュメモリ から RAM バッファへ データ読み出しサブルーチン ( flash_mem_read ) では、
      		movf	PMDATL,W	;下位データの読み出し
      		movwi	FSR1++		;
      	;	movf	PMDATH,W	;上位データの読み出し
      	;	movwi	FSR1++		;
      
 RAM バッファから 高書き込み耐性フラッシュメモリへ データ書き込みサブルーチン ( flash_mem_write ) でも、
      fmemwr01	moviw	FSR1++		;最初のデータバイトを下位にロード
      		movwf	PMDATL		;
      	;	moviw	FSR1++		;2番目のデータバイトを上位にロード
      	;	movwf	PMDATH		;
      
のように、どちらも下位バイトのみを扱っています。 したがって、アセンブル直後のプログラムを起動させたときには、修正前のリストではテーブル全体が下位バイト = h'00' となっているため、上述のように、ラジオ局番号 = 0、音量 = 0 と読み出されることになります。 ちなみに、セーブ情報をプログラムで書き込むときには、下位バイトに書き込んでいるため、その後の読み出しにはまったく問題が起こらないことになります。

 このように、高書き込み耐性フラッシュメモリ内にデータを定義をするときは、DB ディレクティブは間違いで、EEPROM のときと同様に、DE ディレクティブが正解です。

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


● A / D コンバータ モジュールのプログラミング

 そもそも私が PIC16F684 を PIC16F1705 に変更をして、本機 "FMステレオラジオ III" に作り直した理由は、この A / D コンバータ(ADC)モジュールの違いにあります。 次図左側の PIC16F684 の ADC モジュールには、固定参照電圧(FVR)が存在しない ため、それを選択する余地がありません。 そのために前作 "FMステレオラジオ II" では、次に述べる機能実現を諦めざるを得ませんでした。 (これらの図は、Microchip Technology 社のデータシート(PIC16F684)、および(PIC16(L)F1705/9)から抜粋したもの)

PIC16F684 の ADC モジュール PIC16F1705 の ADC モジュール

 固定参照電圧(FVR)を使用した ADC モジュールで、私が何をしたいかというと、本機の表示器に使用している LCD モジュールの、電源に加わる電圧の測定がしたいのです。 本機では本体電源に乾電池を使用しているため、 この LCD 電圧が低下して一定値を下回ってくると、LCD のコントラストが弱くなって、表示文字が見づらくなってしまいます。

 ADC モジュールを使用して電圧の測定をする場合に(本機のようにあまりシビアな測定値を必要としない場合)、その基準となる電圧に普通は電源電圧等を用いることが多いのですが、それでも本機のように乾電池を使用している場合には、 時間の経過とともに電圧低下を起こすために、電源電圧を基準電圧とすることはできません。 しかし、PIC16F1705 の場合には 固定参照電圧(FVR)の使用ができる ので、電圧測定という目的を達成することができます。

 また、本機に使用している "AQM0802A" という LCD モジュールは、I2C インターフェイスを持つモジュール( 回路図 を参照)で、コマンドでコントラストを変更することができます。 そこで、上述の LCD モジュールの電源に加わる電圧を測定しながら、その電圧値に応じて弱まったコントラストを自動で補強 をしてやろう、というものです。

 実は、このアイデアは私が発想したものではなく、別ページ 187. FMステレオラジオ の冒頭で述べた、サイト "PIC電子工作" の "59円チューナモジュールを使ったFMラジオ" から得たもので、私作の "FMステレオラジオ" にもぜひ取り入れたい、と思った次第です。 その実現方法はそちらとは少し異なっていますが、目的は同じです。

 前書きが少々長くなりましたが、そのための私が実際にプログラムしたリストを、次から示します。

 まず初めに、次に示すリストは、本機のプログラムで必要となる SFR レジスタ群の初期設定をしている部分で、特に、ADC モジュールを使用するために必要な、固定参照電圧制御レジスタ ( FVRCON )、ADC 制御レジスタ 0 ( ADCON0 )、ADC 制御レジスタ 1 ( ADCON1 ) については、今後も別プログラムで同様に ADC モジュールを使用するときには、各ビットの意味がこのリストを見ればすぐ分かるようにと、データシートから転記して詳細にコメントを記しておきました。
      			:
      			:
      
      		cblock	h'70'		;バンク 0(共通 RAM 領域)
      ad_res_hi				;
      ad_res_lo				;
      		endc
      			:
      			:
      
      ;==========================================================================
      ;		初期化処理
      ;==========================================================================
      
      init
      		clrf	STATUS		;STATUS クリア
      	 	clrf	INTCON		;INTCON クリア
      
      		; 内部クロックを 8 MHz に設定
      
      		movlb	1		;バンク 1
      		movlw	b'01110000'	;CLOCK=8MHz
      		movwf	OSCCON
      
      		; PORTA,PORTC ポートの各機能設定
      
      		movlb	0		;バンク 0
      		clrf	PORTA		;
      		clrf	PORTC		;
      
      		movlb	2		;バンク 2
      		clrf	LATA		;
      		clrf	LATC		;
      
      		movlb	3		;バンク 3
      		movlw	b'00000100'
      		movwf	ANSELA		;RA2 のみアナログ入力ポート
      		clrf	ANSELC		;すべて デジタル I/O ポート
      
      		movlb	1		;バンク 1
      		movlw	b'00111111'	;RA5-RA0:入力
      		movwf	TRISA		;ポートA の I/0 設定
      		movlw	b'00110000'	;RC5-RC4:入力, RC3-RC0:出力
      		movwf	TRISC		;ポートC の I/0 設定
      
      		movlb	4		;バンク 4
      		movlw	b'00110011'	;RA5-RA4,RA1-RA0
      		movwf	WPUA		;プルアップ有効
      		movlw	b'00110000'	;RC5-RC4
      		movwf	WPUC		;プルアップ有効
      
      		movlb	7		;バンク 7
      		movlw	b'00110011'	;RA5-RA4,RA1-RA0
      		movwf	IOCAN		;ポートA の立ち下がりエッジ状態変化割り込み
      
      		; 固定参照電圧の設定
      
      		movlb	2		;バンク 2
      		movlw	b'10000010'
      ;			  ||||||||
      ;		bit1-0	  ||||||++----- ADFVR<1:0>: ADC FVRバッファゲイン選択ビット
      ;			  ||||||          11 = ADC FVRバッファゲインは4倍、出力VADFVR = 4x VFVR ... 固定参照電圧出力は
      ;			  ||||||      >   10 = ADC FVRバッファゲインは2倍、出力VADFVR = 2x VFVR ... VDDを超える事はできない
      ;			  ||||||          01 = ADC FVRバッファゲインは1倍、出力VADFVR = 1x VFVR
      ;			  ||||||          00 = ADC FVRバッファはOFF
      ;			  ||||||
      ;		bit3-2	  ||||++------- CDAFVR<1:0>: コンパレータFVRバッファゲイン選択ビット
      ;			  ||||            11 = コンパレータFVRバッファゲインは4倍、出力VCDAFVR = 4x VFVR
      ;			  ||||            10 = コンパレータFVRバッファゲインは2倍、出力VCDAFVR = 2x VFVR
      ;			  ||||            01 = コンパレータFVRバッファゲインは1倍、出力VCDAFVR = 1x VFVR
      ;			  ||||        >   00 = コンパレータFVRバッファはOFF
      ;			  ||||
      ;		bit4	  |||+--------- TSRNG: 温度インジケータ レンジ選択ビット
      ;			  |||             1 = VOUT = VDD - 4VT (Highレンジ)
      ;			  |||         >   0 = VOUT = VDD - 2VT (Lowレンジ)
      ;			  |||
      ;		bit5	  ||+---------- TSEN: 温度インジケータ イネーブルビット
      ;			  ||              1 = 温度インジケータを有効
      ;			  ||          >   0 = 温度インジケータを無効
      ;			  ||
      ;		bit6	  |+----------- FVRRDY: 固定参照電圧レディ フラグビット
      ;			  |               1 = 固定参照電圧出力は使える状態
      ;			  |               0 = 固定参照電圧出力の準備が整っていない(または無効)
      ;			  |
      ;		bit7	  +------------ FVREN: 固定参照電圧イネーブルビット
      ;			              >   1 = 固定参照電圧を有効
      ;			                  0 = 固定参照電圧を無効
      
      		movwf	FVRCON		;FVRCON: 固定参照電圧制御レジスタ
      
      		; ADC モジュールの設定
      
      		movlb	1		;バンク 1
      		movlw	b'00001000'
      ;			  ||||||||
      ;		bit0	  |||||||+----- ADON: ADCイネーブルビット
      ;			  |||||||         1 = ADCを有効
      ;			  |||||||     >   0 = ADCを無効、消費電流をゼロ
      ;			  |||||||          ____
      ;		bit1	  ||||||+------ GO/DONE: A/D変換ステータスビット
      ;			  ||||||          1 = A/D変換サイクルを実行中、このビットをセットするとA/D変換サイクルが開始
      ;			  ||||||              このビットは、A/D変換が完了するとハードウェアによって自動的にクリア
      ;			  ||||||      >   0 = A/D変換サイクルが完了(進行中でない)
      ;			  ||||||
      ;		bit6-2	  |+++++------- CHS<4:0>: アナログ チャンネル選択ビット
      ;			  |                 :
      ;			  |                 :
      ;			  |               00011 = AN3
      ;			  |           >   00010 = AN2
      ;			  |               00001 = AN1
      ;			  |               00000 = AN0
      ;			  |
      ;		bit7	  +------------ 未実装
      
      		movwf	ADCON0		;ADCON0: ADC制御レジスタ0
      
      		movlw	b'10010011'
      ;			  ||||||||
      ;		bit1-0	  ||||||++----- ADPREF<1:0>: ADC正側参照電圧コンフィグレーション ビット
      ;			  ||||||      >   11 = VREF+を内部の固定参照電圧(FVR)モジュールに接続
      ;			  ||||||          10 = VREF+を外部VREF+ピンに接続
      ;			  ||||||          01 = 予約済み
      ;			  ||||||          00 = VREF+をVDDに接続
      ;			  ||||||
      ;		bit2	  |||||+------- ADNREF: ADC負側参照電圧コンフィグレーション ビット
      ;			  |||||           1 = VREF-を外部VREF-ピンに接続
      ;			  |||||       >   0 = VREF-をVSSに接続
      ;			  |||||
      ;		bit3	  ||||+-------- 未実装
      ;			  ||||
      ;		bit6-4	  |+++--------- ADCS<2:0>: ADC変換クロック選択ビット
      ;			  |               111 = FRC(内部のRCオシレータから供給されるクロック)
      ;			  |               110 = FOSC/64
      ;			  |               101 = FOSC/16
      ;			  |               100 = FOSC/4
      ;			  |               011 = FRC(内部のRCオシレータから供給されるクロック)
      ;			  |               010 = FOSC/32
      ;			  |           >   001 = FOSC/8
      ;			  |               000 = FOSC/2
      ;			  |
      ;		bit7	  +------------ ADFM: A/D変換結果フォーマット選択ビット
      ;			              >   1 = 右詰め、変換結果を書き込むとADRESHの上位6ビットが「0」に設定
      ;			                  0 = 左詰め、変換結果を書き込むとADRESLの下位6ビットが「0」に設定
      
      		movwf	ADCON1		;ADCON1: ADC制御レジスタ1
      
      		; OPTION_REG の設定
      
      		movlw	h'08'		;プルアップ有効、プリスケーラ: 使用しない
      		movwf	OPTION_REG
      
      		; 割り込みの設定
      
      		movlb	0		;バンク 0
      		clrf	TMR0		;TMR0 クリア
      		bcf	INTCON,TMR0IE	;TMR0IE: タイマー0割り込みを禁止
      		bcf	INTCON,IOCIE	;IOCIE:  状態変化割り込みを禁止
      		bsf	INTCON,GIE	;GIE:    グローバル割り込みを許可
      
      		; I2C LCD (AQM0802A) の初期設定
      
      		bcf	PORTC,LCD_PW	;LCD 電源制御 ON 
      		bsf	PORTC,SDA	;SDA="H"
      		bsf	PORTC,SCL	;SCL="H"
      		call	wait_200ms	;
      		call	I2cLcd_Init	;I2C LCD (AQM0802A) の初期設定
      		movlw	h'0c'		;表示オン/オフコントロール
      		call	I2cLcd_Cmd	;D=1: 表示オン, C=0: カーソル非表示, B=0: 非ブリンク
      		call	lcd_contrast	;LCD 電源電圧の測定とコントラスト調整
      
      ;;#define _test	;;;;; ( この関連 = 3ヶ所あり )
      
      #ifdef	_test	;-- 1 / 3 --
      		movlw	32		;デフォルト値
      		movwf	contrast
      		goto	lcd_contrast	;;;;; test: コントラスト手動調整テスト ;;;;;
      #endif
      			:
      			:
      
 次にこの項の本命である、LCD モジュールの電源電圧の測定をしているところから、その測定電圧値の低下に応じて、LCD コントラストをほぼ一定に保つようなコントラスト値を求めて、LCD モジュールに設定をしている部分を示します。 リスト中には豊富なコメントを挿入しておきましたから、何をしているかが分かっていただけるかと思います。

 リストの緑色の部分がそれで、黒色の部分は コントラスト テーブル ( contrast_table ) を作成するために、事前に手動操作によって、電圧値に応じたコントラスト値を求めるために作成をしたプログラム部分で、 完成をしたプログラムリストではアセンブル対象から外すようにしてあります。 参考程度に見ておいてください。
      			:
      			:
      
      ;==========================================================================
      ;		LCD 電源電圧の測定とコントラスト調整
      ;==========================================================================
      
      lcd_contrast
      
      ;	     >> LCD 電源電圧を測定する
      
      		movlb	1		;バンク 1
      		bsf	ADCON0,ADON	;ADC 有効 = 電源 ON
      		movlb	0		;バンク 0
      		call	wait_20us	;アクイジション時間 (5μS以上)
      		movlb	1		;バンク 1
      		bsf	ADCON0,ADGO	;A/D 変換開始
      		btfsc	ADCON0,ADGO	;変換完了(ハードでクリア) か?
      		goto	$ - 1		;No
      
      		bcf	PIR1,ADIF	;ADC 割り込みフラグをクリア
      		bcf	ADCON0,ADON	;ADC 無効 = 電源 OFF
      
      ;	     >> 結果 = ADRESH:ADRESL (右詰め) に測定電圧値
      
      	;;	抵抗で分圧した分(1/3)を補正 ... x 1.5 倍する		;;
      	;;								;;
      	;;	lsrf	ADRESH,W	;右にシフト(x 0.5 倍する)	;;
      	;;	movwf	ad_res_hi					;; 分圧比を
      	;;	rrf	ADRESL,W	;C ビットを含めて右にシフト	;; 下に変更
      	;;								;;(ハードを
      	;;	addwf	ADRESL,W	;x 1.5 倍する			;; 変更した)
      	;;	movwf	ad_res_lo					;;
      	;;	movf	ADRESH,W					;;
      	;;	addwfc	ad_res_hi,F	;C ビットを含めて加算		;;
      
      ;	     >> 抵抗で分圧した分(1/2)を補正 ... x 2 倍する
      
      		lslf	ADRESL,W	;左にシフト(x 2 倍する)
      		movwf	ad_res_lo
      		rlf	ADRESH,W	;C ビットを含めて左にシフト
      		movwf	ad_res_hi
      		movlb	0		;バンク 0
      
      ;	     >> 結果 = ad_res_hi:ad_res_lo ... x 2 倍に補正したバイナリデータ
      ;
      ;	  2.048 (V) : X (V) = 1024 : ad_res_hi:ad_res_lo
      ;
      ;	              X (V) = 2.048 * ad_res_hi:ad_res_lo / 1024
      ;	                    = 0.002 * ad_res_hi:ad_res_lo
      ;        	            = ad_res_hi:ad_res_lo / 500
      ;
      ;	        X (V) * 500 = ad_res_hi:ad_res_lo
      ;
      ;	     >> 0.01 V 単位にするため x 100 倍にする ... 5 で割る
      ;
      ;	        X (V) * 100 = ad_res_hi:ad_res_lo / 5
      
      		movf	ad_res_hi,W	;割られる数
      		movwf	a16_hi
      		movf	ad_res_lo,W
      		movwf	a16_lo
      		clrf	b16_hi		;割る数
      		movlw	5
      		movwf	b16_lo
      		call	div_16		;16 ビットの割り算
      
      ;	(a16_hi,a16_lo) ÷ (b16_hi,b16_lo) = (x16_hi,x16_lo) ... (y16_hi,y16_lo) 
      ;
      ;	     >> 結果 = 商 (x16_hi,x16_lo) ... 余り (y16_hi,y16_lo) は無視
      
      #ifdef	_test	;-- 2 / 3 --
      		goto	adjust_test
      #endif
      
      ;		(参考) "秋月電子" のデータシート AQM0802A には
      ;		 VDD = 3V の場合、C5=1, C4=0, C3=0, C2=0, C1=0, C0=0 程度、とある
      
      ;	     >> 商 (x16_hi,x16_lo) を 8 ビットデータにするため ... - 120 する
      ;		( 8 ビットデータになれば、いくつを引いても可 )
      
      		movf	x16_hi,W	;引かれる数
      		movwf	a16_hi
      		movwf		ad_res_hi	;high 値を一時保存
      		movf	x16_lo,W
      		movwf	a16_lo
      		movwf		ad_res_lo	;low 値を一時保存
      		clrf	b16_hi		;引く数
      		movlw	120
      		movwf	b16_lo
      		call	sub_16		;16 ビットの減算
      
      ;	(a16_hi,a16_lo) - (b16_hi,b16_lo) = (a16_hi,a16_lo)
      ;
      ;	     >> 結果 = 答えは、a16_lo
      ;		               ------
      ;	     >> a16_lo と次のテーブルデータとを比較する
      
      		movlw	low contrast_table
      		movwf	FSR0L		;間接アドレスに設定
      		movlw	high contrast_table
      		movwf	FSR0H		;
      		movlw	(contrast_table_end - contrast_table) / 2
      		movwf	lpcnt		;ループカウンタ = テーブル(データ)行数
      
      lcdcon01	moviw	0[FSR0]		;
      		subwf	a16_lo,W	;比較: a16_lo >= 0[FSR0] か?
      		btfsc	STATUS,C	;	
      		goto	lcdcon02	;Yes
      
      		addfsr	FSR0,2		;間接アドレス FSR0H:FSR0L + 2
      		decfsz	lpcnt,F		;ループカウンタ - 1 = 0 か?
      		goto	lcdcon01	;No
      
      		movlw	60		;テーブル外: 多分、PIC が動作不能となっている?
      		goto	$ + 2
      
      ;	     >> contrast値を読み出す
      
      lcdcon02	moviw	1[FSR0]		;contrast値
      
      		movwf	contrast	;
      		call	contr_command	;コントラストの設定
      		return
      
      ;	LCD 電源の電圧低下変動を 3.20 V 〜 2.20 V として、テーブルを作成
      ;	(次の contrast値は、私の主観で決めたもので、絶対的なものではない)
      
      		; コントラスト テーブル
      contrast_table
      ;	       - 120 した値, contrast値, 元値,  実電圧
      		dt	200, 28		; 320	3.20 V
      		dt	195, 29		; 315	3.15 V
      		dt	190, 30		; 310	3.10 V
      		dt	185, 31		; 305	3.05 V
      		dt	180, 32		; 300	3.00 V
      		dt	175, 33		; 295	2.95 V
      		dt	170, 35		; 290	2.90 V
      		dt	165, 37		; 285	2.85 V
      		dt	160, 39		; 280	2.80 V
      		dt	155, 40		; 275	2.75 V
      		dt	150, 42		; 270	2.70 V
      		dt	145, 43		; 265	2.65 V
      		dt	140, 44		; 260	2.60 V
      		dt	135, 45		; 255	2.55 V
      		dt	130, 46		; 250	2.50 V
      		dt	125, 48		; 245	2.45 V
      		dt	120, 50		; 240	2.40 V
      		dt	115, 52		; 235	2.35 V
      		dt	110, 53		; 230	2.30 V
      		dt	105, 55		; 225	2.25 V
      		dt	100, 57		; 220	2.20 V
      contrast_table_end
      
      ;--------------------------------------------------------------------------
      ;		LCD コントラストの設定コマンド
      ;--------------------------------------------------------------------------
      
      contr_command
      		movlw	h'39'		;Function set, IS=1: 拡張コマンド
      		call	I2cLcd_Cmd	;
      
      		movf	contrast,W
      		andlw	h'0f'		;下位 4 ビット
      		iorlw	h'70'		;Contrast set
      		call	I2cLcd_Cmd	;
      
      		swapf	contrast,W
      		andlw	h'03'		;上位 2 ビット
      		iorlw	h'54'		;Power/ICON/Contrast Control
      		call	I2cLcd_Cmd	;
      
      		movlw	h'38'		;Function set, IS=0: 拡張コマンド解除
      		call	I2cLcd_Cmd	;
      		return
      
      ;--------------------------------------------------------------------------
      ;		測定電圧値の表示とコントラスト値の表示
      ;--------------------------------------------------------------------------
      
      volt_contr_disp				;LCD表示: [x.xxV xx]
      
      		; 測定電圧値の表示
      		movlw	h'c0'		;2行目
      		call	I2cLcd_Cmd	;I2C LCD へコマンド出力
      
      		movf	ad_res_hi,W
      		movwf	a16_hi
      		movf	ad_res_lo,W
      		movwf	a16_lo
      		call	bindec16cz_cnv	;16 ビット数値を 10進数 5桁の文字列に変換
      
      		movf	buff2,W
      		call	I2cLcd_Data	;I2C LCD へデータ出力
      		movlw	'.'
      		call	I2cLcd_Data	;I2C LCD へデータ出力
      		movf	buff3,W
      		call	I2cLcd_Data	;I2C LCD へデータ出力
      		movf	buff4,W
      		call	I2cLcd_Data	;I2C LCD へデータ出力
      		movlw	'V'
      		call	I2cLcd_Data	;I2C LCD へデータ出力
      		movlw	' '
      		call	I2cLcd_Data	;I2C LCD へデータ出力
      
      		; コントラスト値の表示
      		movf	contrast,W
      		movwf	work1
      		call	bindec8cz_cnv	;8 ビット数値を 10進数 3桁の文字列に変換
      
      		movf	buff1,W
      		call	I2cLcd_Data	;I2C LCD へデータ出力
      		movf	buff2,W
      		call	I2cLcd_Data	;I2C LCD へデータ出力
      		return
      
      #ifdef	_test	;-- 3 / 3 --
      ;--------------------------------------------------------------------------
      ;		コントラスト手動調整テスト	LCD表示: [x.xxV xx]
      ;--------------------------------------------------------------------------
      
      adjust_test
      		; 測定電圧値の表示
      
      		movlw	h'80'		;
      		call	I2cLcd_Cmd	;I2C LCD へコマンドを出力
      
      		movf	x16_hi,W
      		movwf	a16_hi
      		movf	x16_lo,W
      		movwf	a16_lo
      		call	bindec16cz_cnv	;16 ビット数値を 10進数 5桁の文字列に変換(ゼロサプレス)
      
      		movlw	low (buff+2)
      		movwf	FSR0L		;間接アドレスに設定
      		movlw	high (buff+2)
      		movwf	FSR0H		;
      		movlw	3
      		movwf	lpcnt		;ループカウンタ
      
      test01		moviw	FSR0++		;
      		call	I2cLcd_Data	;I2C LCD へデータを1文字出力
      
      		movlw	3
      		subwf	lpcnt,W
      		movlw	'.'
      		btfsc	STATUS,Z	;lpcnt = 3 か?
      		call	I2cLcd_Data	;Yes. I2C LCD へデータを1文字出力
      
      		decfsz	lpcnt,F		;lpcnt = 0 か?
      		goto	test01		;No
      
      		movlw	'V'
      		call	I2cLcd_Data	;I2C LCD へデータを1文字出力
      		movlw	' '
      		call	I2cLcd_Data	;I2C LCD へデータを1文字出力
      		call	contr_disp	;コントラスト値の表示
      
      test02		btfss	PORTA,_swTUP	;TUNING UP スイッチ = ON か?
      		call	contr_up	;Yes
      
      		btfss	PORTA,_swTDWN	;TUNING DOWN スイッチ = ON か?
      		call	contr_dwn	;Yes
      
      		btfsc	PORTA,_swVDWN	;VOLUME DOWN スイッチ = ON か?
      		goto	test02		;No
      
      		call	wait_1sec	;1 秒 ウェイト
      		goto	lcd_contrast
      
      ;--------------------------------------------------------------------------
      
      		; コントラスト値の表示
      contr_disp
      		movlw	h'86'		;
      		call	I2cLcd_Cmd	;I2C LCD へコマンドを出力
      
      		movf	contrast,W
      		movwf	work1
      		call	bindec8cz_cnv	;8 ビット数値を 10進数 3桁の文字列に変換(ゼロサプレス)
      
      		movf	buff1,W
      		call	I2cLcd_Data	;I2C LCD へデータを1文字出力
      		movf	buff2,W
      		call	I2cLcd_Data	;I2C LCD へデータを1文字出力
      		return
      
      ;--------------------------------------------------------------------------
      
      		; TUNING (CONTRAST) UP スイッチ = ON の処理
      contr_up
      		call	wait_30ms	;30m 秒 ウェイト
      		btfsc	PORTA,_swTUP	;TUNING UP スイッチ = ON か?
      		goto	ctup01		;No
      
      		incf	contrast,F	;contrast + 1
      		movlw	64
      		subwf	contrast,W	;contrast - 64
      		btfsc	STATUS,C	;contrast < 64 か?
      		decf	contrast,F	;No. contrast = contrast - 1 = 63
      		call	contr_command	;コントラスト設定
      		call	contr_disp	;コントラスト値の表示
      
      ctup01		btfss	PORTA,_swTUP	;TUNING UP スイッチ = OFF か?
      		goto	$ - 1		;No
      
      		return
      
      ;--------------------------------------------------------------------------
      
      		; TUNING (CONTRAST) DOWN スイッチ = ON の処理
      contr_dwn
      		call	wait_30ms	;30m 秒 ウェイト
      		btfsc	PORTA,_swTDWN	;TUNING DOWN スイッチ = ON か?
      		goto	ctdwn01		;No
      
      		decf	contrast,F	;contrast - 1
      		incf	contrast,W
      		btfsc	STATUS,Z	;W = 0 か?
      		incf	contrast,F	;Yes. contrast = contrast + 1 = 0
      		call	contr_command	;コントラスト設定
      		call	contr_disp	;コントラスト値の表示
      
      ctdwn01		btfss	PORTA,_swTDWN	;TUNING DOWN スイッチ = OFF か?
      		goto	$ - 1		;No
      
      		return
      #endif
      
 なお、上リスト中で使用している演算サブルーチン ( div_16, sub_16 )、データ変換サブルーチン ( bindec16cz_cnv, bindec8cz_cnv ) は、長くなるのでこのページ上では示しませんが、下の 現在の最新バージョン にある、本機のソースプログラムファイル ( FM_StereoRadioIII.asm ) の中に収録されているので、そちらを参照してください。

 上リスト中の始め部分に、抵抗で分圧した分を補正している個所がありますが、LCD モジュールの電源電圧の測定をするための分圧用抵抗に、当初、考慮不足から 1 : 2( 2 / 3 )の比率の組み合わせ抵抗を取り付けたのですが、これではまずい、 と途中で気が付いて 1 : 1( 1 / 2 )の比率抵抗に変更をした名残を残しておきました。

 実際に本機の LCD モジュールの電源電圧を測定するためには、次に挙げるような様々な条件を満たすように、ハードウェア、ソフトウェアともに考慮をして、ADC モジュールを使用しなければなりません。
  • 本機の電源には、単3乾電池 x 2本を使用しているので、公称電圧は 1.5 V x 2 = 3.0 V となる。
  • しかし、乾電池が新しい内はもっと高く 3.0 V 以上になるが、時間経過とともに電圧低下を起こす。
  • LCD モジュール電源の電圧低下変動を 3.20 V 〜 2.20 V として、これらの範囲の電圧を測定したい。
  • 本機の ADC モジュールでは、基準電圧として電圧変動のある Vdd の使用はできないため、固定参照電圧(FVR)を使用する。
  • 固定参照電圧(FVR)は、1.024 V、2.048 V、4.096 V の3種類があるが、本機では Vdd を超える 4.096 V の指定はできない。
  • Vref- と Vref+ の電圧差が 1.8 V 以上ないと精度が保証されないため、Vref- = Vss とした場合、Vref+ に 1.024 V の使用はできない。
  • また、Vref- = Vss とした場合、Vref+ を超えるような電圧の測定をすることはできない。
  • ADC モジュールの入力条件として、入力源の出力インピーダンスは 10 kΩ 以下と規定されている。
  • したがって、消費電流を抑えるために分圧用抵抗として、高抵抗を用いることはできない。
 以上のような条件(制約)から本機では、Vref- = Vss (GND)、Vref+ = 固定参照電圧(FVR)の 2.048 V を、使用することになります。 したがって、被測定電圧は基準電圧の Vref+ = 2.048 V が上限で、それを上回るような電圧の測定はできません。

 上に挙げたように、本機では上限として 3.20 V までを測定したいのですが、そのままでは測定ができないために分圧抵抗を使用して、上限を 2.048 V までに抑えることになります。 当初、乾電池 x 2本 = 3.0 V と単純に考えて、 1 : 2 の比率の分圧抵抗で 2 を測定すれば良い、としたのですが、実際には新しい乾電池は 3.0 V 以上のために、1 : 2 の比率では正確な測定ができません。

 それに気が付いたのはハードウェアが完成した後で(おそまつ)、そんな経緯があって 1 : 1 の比率の分圧抵抗に変更をした次第です。 1 : 1 ということは 1 / 2 の電圧しか測定をしていないため、実際の電圧は実測値を 2 倍にする必要があります。 そこら辺りの必要な部分を、上リストから抜き出して再掲をしてみると、
      ;	     >> LCD 電源電圧を測定する
      
      		movlb	1		;バンク 1
      		bsf	ADCON0,ADON	;ADC 有効 = 電源 ON
      		movlb	0		;バンク 0
      		call	wait_20us	;アクイジション時間 (5μS以上)
      		movlb	1		;バンク 1
      		bsf	ADCON0,ADGO	;A/D 変換開始
      		btfsc	ADCON0,ADGO	;変換完了(ハードでクリア) か?
      		goto	$ - 1		;No
      
      		bcf	PIR1,ADIF	;ADC 割り込みフラグをクリア
      		bcf	ADCON0,ADON	;ADC 無効 = 電源 OFF
      
      ;	     >> 結果 = ADRESH:ADRESL (右詰め) に測定電圧値
      
      	;;	抵抗で分圧した分(1/3)を補正 ... x 1.5 倍する		;;
      	;;								;;
      	;;	lsrf	ADRESH,W	;右にシフト(x 0.5 倍する)	;;
      	;;	movwf	ad_res_hi					;; 分圧比を
      	;;	rrf	ADRESL,W	;C ビットを含めて右にシフト	;; 下に変更
      	;;								;;(ハードを
      	;;	addwf	ADRESL,W	;x 1.5 倍する			;; 変更した)
      	;;	movwf	ad_res_lo					;;
      	;;	movf	ADRESH,W					;;
      	;;	addwfc	ad_res_hi,F	;C ビットを含めて加算		;;
      
      ;	     >> 抵抗で分圧した分(1/2)を補正 ... x 2 倍する
      
      		lslf	ADRESL,W	;左にシフト(x 2 倍する)
      		movwf	ad_res_lo
      		rlf	ADRESH,W	;C ビットを含めて左にシフト
      		movwf	ad_res_hi
      		movlb	0		;バンク 0
      
      ;	     >> 結果 = ad_res_hi:ad_res_lo ... x 2 倍に補正したバイナリデータ
      ;
      ;	  2.048 (V) : X (V) = 1024 : ad_res_hi:ad_res_lo
      ;
      ;	              X (V) = 2.048 * ad_res_hi:ad_res_lo / 1024
      ;	                    = 0.002 * ad_res_hi:ad_res_lo
      ;        	            = ad_res_hi:ad_res_lo / 500
      ;
      ;	        X (V) * 500 = ad_res_hi:ad_res_lo
      ;
      ;	     >> 0.01 V 単位にするため x 100 倍にする ... 5 で割る
      ;
      ;	        X (V) * 100 = ad_res_hi:ad_res_lo / 5
      
      		movf	ad_res_hi,W	;割られる数
      		movwf	a16_hi
      		movf	ad_res_lo,W
      		movwf	a16_lo
      		clrf	b16_hi		;割る数
      		movlw	5
      		movwf	b16_lo
      		call	div_16		;16 ビットの割り算
      
      ;	(a16_hi,a16_lo) ÷ (b16_hi,b16_lo) = (x16_hi,x16_lo) ... (y16_hi,y16_lo) 
      ;
      ;	     >> 結果 = 商 (x16_hi,x16_lo) ... 余り (y16_hi,y16_lo) は無視
      
のようになります。 そして、本機で使用した PIC16F1705 のADC モジュールは分解能が 10 ビットのため、測定値の最小値は Vref-、最大値は Vref+ となって、この間が 1,024(2 の 10 乗)等分されます。 すなわち、本機では 2.048 V / 1024 = 0.002 V の分解能となります。

 したがって、リスト中のコメントにもあるように、実際には、測定後 2 倍に補正をしたデータは、実電圧値の 500 倍となっています。 そして、本機のプログラムで必要とする電圧を 0.01 V 単位としたいため、500 倍データから 100 倍データに変換をするように、5 で割って 100 倍データを求めています。

 以上が前半部分ですが、その後の後半部分も同様に次に再掲をしてみます。

 上で求めた 100 倍データは2バイトで、最終的なテーブル検索をするのには面倒なために、再度、1バイトデータに変換をしています。 このとき、100 倍データは後ほど電圧値の LCD 表示にも使用をするため、 100 倍データが壊されてしまう前にバックアップを取りながら、次のように - 120 をして1バイトデータに変換をしていますが、1バイトデータになればいいので、その範囲であればいくつを引いてもかまいません。

 そして、いよいよテーブル検索をしてコントラスト値を導き出します。 あらかじめ、- 120 をした1バイトデータと、それに対応したコントラスト値を、コントラスト テーブル ( contrast_table ) として作成をしておき、 先ほど求めた1バイトデータをキーとして、テーブル検索を行います。 その結果、テーブルから読み出したコントラスト値を、LCD モジュールに I2C インターフェイスでコマンドとして送り込み、 測定電圧値に応じて LCD モジュールのコントラストを一定に保つように設定をします。
      ;		(参考) "秋月電子" のデータシート AQM0802A には
      ;		 VDD = 3V の場合、C5=1, C4=0, C3=0, C2=0, C1=0, C0=0 程度、とある
      
      ;	     >> 商 (x16_hi,x16_lo) を 8 ビットデータにするため ... - 120 する
      ;		( 8 ビットデータになれば、いくつを引いても可 )
      
      		movf	x16_hi,W	;引かれる数
      		movwf	a16_hi
      		movwf		ad_res_hi	;high 値を一時保存
      		movf	x16_lo,W
      		movwf	a16_lo
      		movwf		ad_res_lo	;low 値を一時保存
      		clrf	b16_hi		;引く数
      		movlw	120
      		movwf	b16_lo
      		call	sub_16		;16 ビットの減算
      
      ;	(a16_hi,a16_lo) - (b16_hi,b16_lo) = (a16_hi,a16_lo)
      ;
      ;	     >> 結果 = 答えは、a16_lo
      ;		               ------
      ;	     >> a16_lo と次のテーブルデータとを比較する
      
      		movlw	low contrast_table
      		movwf	FSR0L		;間接アドレスに設定
      		movlw	high contrast_table
      		movwf	FSR0H		;
      		movlw	(contrast_table_end - contrast_table) / 2
      		movwf	lpcnt		;ループカウンタ = テーブル(データ)行数
      
      lcdcon01	moviw	0[FSR0]		;
      		subwf	a16_lo,W	;比較: a16_lo >= 0[FSR0] か?
      		btfsc	STATUS,C	;	
      		goto	lcdcon02	;Yes
      
      		addfsr	FSR0,2		;間接アドレス FSR0H:FSR0L + 2
      		decfsz	lpcnt,F		;ループカウンタ - 1 = 0 か?
      		goto	lcdcon01	;No
      
      		movlw	60		;テーブル外: 多分、PIC が動作不能となっている?
      		goto	$ + 2
      
      ;	     >> contrast値を読み出す
      
      lcdcon02	moviw	1[FSR0]		;contrast値
      
      		movwf	contrast	;
      		call	contr_command	;コントラストの設定
      		return
      
      ;	LCD 電源の電圧低下変動を 3.20 V 〜 2.20 V として、テーブルを作成
      ;	(次の contrast値は、私の主観で決めたもので、絶対的なものではない)
      
      		; コントラスト テーブル
      contrast_table
      ;	       - 120 した値, contrast値, 元値,  実電圧
      		dt	200, 28		; 320	3.20 V
      		dt	195, 29		; 315	3.15 V
      		dt	190, 30		; 310	3.10 V
      		dt	185, 31		; 305	3.05 V
      		dt	180, 32		; 300	3.00 V
      		dt	175, 33		; 295	2.95 V
      		dt	170, 35		; 290	2.90 V
      		dt	165, 37		; 285	2.85 V
      		dt	160, 39		; 280	2.80 V
      		dt	155, 40		; 275	2.75 V
      		dt	150, 42		; 270	2.70 V
      		dt	145, 43		; 265	2.65 V
      		dt	140, 44		; 260	2.60 V
      		dt	135, 45		; 255	2.55 V
      		dt	130, 46		; 250	2.50 V
      		dt	125, 48		; 245	2.45 V
      		dt	120, 50		; 240	2.40 V
      		dt	115, 52		; 235	2.35 V
      		dt	110, 53		; 230	2.30 V
      		dt	105, 55		; 225	2.25 V
      		dt	100, 57		; 220	2.20 V
      contrast_table_end
      
      ;--------------------------------------------------------------------------
      ;		LCD コントラストの設定コマンド
      ;--------------------------------------------------------------------------
      
      contr_command
      		movlw	h'39'		;Function set, IS=1: 拡張コマンド
      		call	I2cLcd_Cmd	;
      
      		movf	contrast,W
      		andlw	h'0f'		;下位 4 ビット
      		iorlw	h'70'		;Contrast set
      		call	I2cLcd_Cmd	;
      
      		swapf	contrast,W
      		andlw	h'03'		;上位 2 ビット
      		iorlw	h'54'		;Power/ICON/Contrast Control
      		call	I2cLcd_Cmd	;
      
      		movlw	h'38'		;Function set, IS=0: 拡張コマンド解除
      		call	I2cLcd_Cmd	;
      		return
      
 このようにプログラムメモリ(データメモリだけでなく)のアクセスにも、PIC16F1 ファミリーでしかできない、間接アドレス指定用の FSRn レジスタペア、および間接レジスタ INDFn を使用した新しい命令とともに、 プログラミングをしてみました。

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


 ◎ 機能を追加後に起こった メイン・ルーチンでのトラブル

 上述のように、電源の電圧低下に伴う LCD コントラストの低下を自動補正する機能を追加後に、メイン・ルーチンでは1分が経過をしても、PIC がスリープをしなくなってしまいました。 PIC16F1705 にプログラム移植を行った直後の Ver. 1.10 のプログラムでは、1分が経過後には間違いなくスリープをすることを確認ができています。

 機能を追加後にメイン・ルーチンで変更をした点は、
      			:
      			:
      m01		call	table_select	;FM ラジオ放送局のテーブル選択 … 局名の表示
      		btfsc	updt_flg,7	;初期化直後フラグ = ON か?
      		call	fm_tuner_set	;Yes.(RDA5807M FM Tuner)の設定
      		call	lcd_contrast	;LCD 電源電圧の測定とコントラスト調整		;; 追加
      		call	volt_contr_disp	;測定電圧値とコントラスト値の表示		;; 追加
      
      			:
      			:
      
      m04		call	table_select	;FM ラジオ放送局のテーブル選択 … 局名の表示
      		call	fm_tuner_set	;(RDA5807M FM Tuner)の設定
      		call	lcd_contrast	;LCD 電源電圧の測定とコントラスト調整		;; 追加
      		call	volt_contr_disp	;測定電圧値とコントラスト値の表示		;; 追加
      			:
      			:
      
のように 上述 で作成をした、電圧の測定とコントラストを設定するサブルーチンと、測定電圧値とコントラスト値を表示するサブルーチンを、2か所に追加したことと、
    < 変更前 >	タイマーカウントの開始 ・・・ FMラジオモジュールの設定、ラジオ局名の表示
    						音量設定値の表示
    		   カウントの2秒後 ・・・ FMラジオモジュールから情報の読み出し
    						受信強度の表示
    				4秒後 ・・・ 周波数の表示
    				6秒後 ・・・ セーブ情報をバックアップ
    				1分後 ・・・ スリープ状態に移行
    
    < 変更後 >	タイマーカウントの開始 ・・・ FMラジオモジュールの設定、ラジオ局名の表示
    						LCDモジュールの電源電圧値、LCDコントラストの設定値の表示
    		   カウントの2秒後 ・・・ 音量設定値の表示
    		   		4秒後 ・・・ FMラジオモジュールから情報の読み出し
    						受信強度の表示
    				6秒後 ・・・ 周波数の表示
    				8秒後 ・・・ セーブ情報をバックアップ
    				1分後 ・・・ スリープ状態に移行
    
のように機能の追加に伴い従来の表示に先立って、一番最初に LCD モジュールの電源電圧値、LCD コントラストの設定値を2秒間表示するために、以降、LCD に表示等をするそのタイミングが、2秒間ずつ遅れて実行されるように変更になった点が 異なっています。

 これらのタイミングはすべて、タイマー0割り込みを使用して基本となる1秒を作り出し、それをカウントしながらそれぞれの秒数に応じて、LCD に表示等をするようにしています。 したがって、カウント途中で実行される内容と全体の量は、 機能を追加する以前とは異なって増加してますが、最終的に 60 秒、すなわち1分が経過した後に PIC をスリープ状態にする、という目的を果たしたいことには違いはありません。

 そのスリープ状態にする、というプログラム部分を次に示しますが、リスト中のスリープ移行フラグ ( updt_flg の bit 5 ) は、タイマー0割り込みルーチン内で 60 秒をカウントしたときにセット(ON)をしています。 すなわち、スリープ移行フラグ = ON であれば、次のリストのように必ず SLEEP 命令を実行する ― 筈なんですが ・・・
      		; スリープ移行フラグのチェック
      
      m12		btfss	updt_flg,5	;スリープ移行フラグ = ON か?
      		goto	main		;No
      
      		bsf	PORTC,LCD_PW	;LCD 電源制御 OFF
      		bcf	PORTC,LED_ST	;ST/MO LED 消灯
      
      		movlb	7		;バンク 7
      		clrf	IOCAF		;状態変化割り込みフラグをクリア
      		movlb	0		;バンク 0
      		bsf	INTCON,IOCIE	;状態変化割り込みを許可
      		sleep			;おやすみなさい
      		;
      		;			;wake up
      		goto	main
      
 ところが、1分を経過しても PIC がスリープにならない、ということはこの SLEEP 命令が実行されない、ということになります。 そんなバカな ・・・

 PIC16F684 のプログラムを PIC16F1705 に移植するときには、変更になったレジスタ名やフラグ名等には気を使いますが、論理的なことはあまり深くは考えずにほとんど機械的に行いました。 したがって、もう1か所 次のようにスリープ移行フラグ ( updt_flg の bit 5 ) で分岐判断を行っていることに、深く考えが及んでいませんでした。
      main
      		btfsc	updt_flg,7	;初期化直後フラグ = ON か?
      		goto	m01		;Yes
      
      		; ウェイクアップ直後かどうかのチェック
      
      		btfss	updt_flg,5	;スリープ移行フラグ = ON か?
      		goto	m03		;No
      
      		bcf	PORTC,LCD_PW	;LCD 電源制御 ON
      		call	I2cLcd_Init	;I2C LCD (AQM0802A) の初期設定
      			:
      			:
      		clrf	updt_flg	;更新フラグの初期設定
      			:
      			:
      
 上の2つの部分リストは、ともにメイン・ルーチンの移植直後の一部で、大きな誤りを含んでいます。 これらのリストでは、1分が経過するとスリープ移行フラグ = ON になり、まず、上リスト部分で LCD 電源 = OFF にした後で PIC がスリープ状態に移行をします。 そして、やがてユーザのスイッチ操作によって PIC がウェイクアップをすると、下リストの main に戻って再びスリープ移行フラグ = ON の分岐判断で、LCD 電源 = ON にした後で 平常の動作、 すなわち、LCD 表示などを行います。

 これらの動作は、たまたまプログラマ(私)の希望通りの動作になっただけで、実は少しばかり考慮に欠けています。 その考慮不足が今回の機能追加後のプログラムに現れてしまいました。 機能追加後では、スリープ移行フラグ = ON になると、 先に下リストの分岐判断の方が実行されて LCD 電源 = ON の制御の方へ進み、その後、同フラグ = OFF にリセットをしているため、上リストで SLEEP 命令が実行されることは永久にありません。

 上リストはメイン・ルーチンの最後部分、下リストは同ルーチンの最初部分を抜粋したもので、PIC がスリープ状態でない平常時には、この間をぐるぐるとループ動作をしています。 そして、タイマー0割り込みルーチン内でスリープ移行フラグ = ON にセットした後で、同ルーチンからの復帰時のタイミング(本当は割り込み時のタイミング)によって、2つの部分リストの内、どちらの分岐判断が先に行われるかが 決められてしまいます。

 PIC16F684 のプログラム、および PIC16F1705 に移植直後のプログラムでは、上述のように偶然私の希望通りになっていただけで、機能追加後のプログラムでは、上述のような LCD への追加表示等を行ったために、 機能追加以前のプログラムとはループ動作におけるタイミングがずれて、希望通りにはならない結果となったわけです。

 そこで次リストのように、新たにウェイクアップフラグ ( updt2_flg の bit 1 ) を追加して、問題となる2か所のそれぞれの分岐判断のフラグを分け、どのタイミングによっても確実に希望通りの動作をするように、 プログラムの修正を行いました。
      main
      		btfsc	updt_flg,7	;初期化直後フラグ = ON か?
      		goto	m01		;Yes
      
      		; ウェイクアップ直後かどうかのチェック
      
      		btfss	updt2_flg,1	;ウエイクアップフラグ = ON か?				;;<<<<<
      		goto	m03		;No
      
      		bcf	PORTC,LCD_PW	;LCD 電源制御 ON
      		call	I2cLcd_Init	;I2C LCD (AQM0802A) の初期設定
      		movlw	h'0c'		;表示オン/オフコントロール
      		call	I2cLcd_Cmd	;D=1: 表示オン, C=0: カーソル非表示, B=0: 非ブリンク
      		bcf	updt2_flg,1	;ウエイクアップフラグ = OFF				;;<<<<<
      
      			:
      
      		clrf	updt_flg	;更新フラグの初期設定					;;<<<<<
      
      			:
      			:
      			:
      
      		; スリープ移行フラグのチェック
      
      m12		btfss	updt_flg,5	;スリープ移行フラグ = ON か?				;;<<<<<
      		goto	main		;No
      
      		bsf	PORTC,LCD_PW	;LCD 電源制御 OFF
      		bcf	PORTC,LED_ST	;ST/MO LED 消灯
      
      		movlb	7		;バンク 7
      		clrf	IOCAF		;状態変化割り込みフラグをクリア
      		movlb	0		;バンク 0
      		bsf	INTCON,IOCIE	;状態変化割り込みを許可
      		sleep			;おやすみなさい
      		;
      		;			;wake up
      		bsf	updt2_flg,1	;ウエイクアップフラグ = ON				;;<<<<<
      		goto	main
      
 このように、トラブルの原因が分かってしまえば何のことはないのですが、いつものことながらその原因にたどり着くまでが大変です。 今回も以上のような原因解明に至るまでに、半日ほどを費やしてしまいました。 しかし、正直なところこの過程が私にとっては楽しいのであって、何の躓きもなくすんなりとプログラムが完成をしてしまっては、楽しさも半減というもの(負け惜しみも入っている?)です。

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


 ◎ [ コントラスト テーブルの設定値を変更したい方へ ]

 上述した コントラスト テーブル の作成方法についてですが、上リストで触れた 黒色のリスト部分(再掲したリストを除く)の先頭の ;; を外して、 #define 文を有効にしてアセンブル対象とすると、黒色のリスト部分もアセンブルされて、このプログラムを実行させると、スイッチ操作によってコントラスト値を変更することができるようになります。
      ;;#define _test	;;;;; ( この関連 = 3ヶ所あり )
      
 したがって、電源には乾電池ではなく、2.00 V 〜 3.30 V 程度の範囲で電圧設定ができるような可変型の直流電源を接続し、例えば上テーブルに示したように 0.05 V 毎の増減で電圧値を変更させながら、その電圧値に対するコントラスト値を スイッチ操作によって変更させてみて、最適と思われるコントラスト値を収集していきます。

 このようにして収集するコントラスト値は、多分に各個人の主観が入るのでこれがベスト、という値を求める(決める)ことは難しいかもしれませんが、"秋月電子" の LCD モジュールのデータシート AQM0802A には、 VDD = 3V の場合に 32 程度と記されているので、それを1つの参考にしてみてください。

 このプログラムを実行させたときのスイッチ操作ですが、まず 電源スイッチを ON にする と、現在の LCD モジュールに加わっている電源電圧とコントラスト値(デフォルト = 32)が、LCD の1行目に [x.xxV xx] の形式で表示されます。 そして、コントラスト値の数字が小さいほど薄く、大きいほど濃く LCD 表示がされるので、タクトスイッチの操作でコントラスト値を変更させていきます。

 TUNING UP スイッチを ON するごと に、コントラスト値は 現在値 +1 された値に更新 され、TUNING DOWN スイッチを ON するごと に、逆に 現在値 −1 された値に更新 されて、 これらの更新値が直ちに LCD コントラストに反映されるとともに、LCD に表示されているコントラスト値も更新されます。

 このように、2つのスイッチによって1つの電圧値に対するコントラスト値の収集が終わったら、電源電圧を次の電圧値に変更した後で、VOLUME DOWN スイッチを ON にする ことで、LCD に表示されている LCD モジュールの電源電圧値も更新され、次のコントラスト値の収集に移ります。

 私の場合には、別ページで紹介している "011. 定電圧安定化電源 (3 WAY)" のフリーレンジを使用して、上リストに示すように、電源の電圧を 3.20 V 〜 2.20 V の範囲で 0.05 V ごとに電圧を減じながら、 その電圧値に対するコントラスト値を収集しました。

 最後になりますが、このプログラムを実行させた場合には、スイッチ操作によってコントラスト値を収集するための機能専用となり、FMステレオラジオの機能は一切無視されるので、そのように心得ておいてください。 また、上述のような可変型の電源を使用する場合には、FMラジオモジュールの電源電圧の上限値 = MAX 3.3 V ですから、それを超えることがないように、十分に注意をして作業を行ってください。 念のため。

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


● MPLAB X IDE と PICkit3

 私が PIC でプログラムを作成するようになった 2004 年頃から、本機のプログラムを作成以前までは、ずっと MPLAB IDE を使用し続けてきました。 最近では最終バージョンの MPLAB IDE v8.92 を使用していましたが、 いよいよ MPLAB IDE とはお別れのときが来ました。 もう以前から分かってはいましたが、MPLAB IDE では本機 "FMステレオラジオ III" で使用した、PIC16F1705 のプログラムをアセンブルすることができないのです。

 そこで今更感が拭えないですが、MPLAB IDE の後継である MPLAB X IDE を使用することになります。 1年ほど以前から PC にインストールだけはしてあったのですが、上述のように、今まで実際に使用したことはありませんでした。

 しかし、今回初めて使用してみて、インストールしてあったのは MPLAB X IDE v.5.40 のため、やはりアセンブルすることができませんでした。 ネット情報によると v.5.40 以上にはアセンブラ "MPASM" が付属していない、というのです。 アセンブラ "MPASM" を使用したいのであれば、もう1世代前の v.5.35 をインストールすればアセンブラ "MPASM" が付属してくる、というのです。 さすがに私もそこまでは知りませんでした。

 早速 MPLAB X IDE v.5.35 にインストールをし直しました。 これでアセンブラの件については取り敢えず落着することができました。

 次に、HEX ファイルを PIC に書き込まなければなりませんが、PIC でプログラムを作成し出した頃は、別ページ "002. PICライター(プログラマー)" で紹介をした "秋月電子" の「PICプログラマー」 を使用していましたが、2008 年頃にマイクロチップ純正の "PICkit2" を購入し現在に至っています。

 しかし、PIC の書き込みに関しても、PICkit2 では新しいデバイスにはサポートがなく書き込むことができない、ということは分かっていました。 ですから、将来的には絶対に必要になると思って、これも今更ながらですが1年ほど以前のこと、 既に PICkit4 が世に出ているときですから、純正の PICkit3 の入手は無理なために、互換品の PICkit3 をアマゾンで購入してありました。 MPLAB X IDE と同様に、今まで使用したことはありませんでしたが、 PIC16F1705 には問題なく書き込むことができています。

 とはいうものの、まったく問題がなかったという訳でなく、書き込みソフトウェアと ICSP 回路の考え方で、少々すったもんだをしてしまいました。 これらについてはまだ自分自身で良く理解、整理ができていない点もあるため、 今回はこれ以上は触れませんが、MPLAB X IPE v.5.35 を使用して ICSP 接続で書き込みを行った、ことだけを付け加えておきます。 この件に関してはまた改めて述べるときがあるかもしれません。

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


現在の最新バージョン: Ver. 1.12  ( 2022/1/27 更新 )

ソースファイル (FM_StereoRadioIII.asm)
 I2C LCD 制御サブルーチン (I2C_S2_LCD.sub)  Ver. 1.04
HEX ファイル (FM_StereoRadioIII.hex)

| ページトップ |

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

 本項と次項で述べるいろいろな注意事項は、本機の前身である "188. FMステレオラジオ II" のものと重複をする事項も多いのですが、 プリント基板を作製する上で重要なので、それらについても再び述べることにします。

 使用したプリント基板は、"秋月電子" の "片面ガラス・ユニバーサル基板 Bタイプ(95×72mm)めっき仕上げ(通販コード P-00518)" ですが、ケース加工図 で示したポリスチレンケース内に収納するためには、 下図に示すようにプリント基板の切断等の加工が必要です。 まず、横 ----- X 線の位置で切断をし、次に、縦 ----- Y1、Y2 線の2ヶ所の位置で切断をします。 本機のプリント基板では、前作のものと比べて横幅は同じですが、縦の長さが1穴分多くとってあるので注意が必要です。

 そして次に、左横の位置に図のような ----- 線のコの字型になるようにくり抜きます。 この位置には基板取付用の BOX型Φ3.5ステレオジャックを取り付けたいのですが、使用したケースの左横の形状が特殊?で単純な垂直ではないことと、 ケース内側の底板と天板との空間サイズが小さいために、プリント基板の部品面に取り付けたのでは、BOX型ジャックのプラグ挿入口をケース外に取り出すことが不可能です。 そこで、サブ基板を使用してメイン基板のハンダ面側に位置するように取り付けます。 (前作の BOX型ジャックの取り付け の項を参照)

 最後に、基板の四隅にケースに取り付け用の穴Φ3.2 を4個開けます。 残りの小さな穴5個は、LCDモジュールの取り付け台のための木片固定用、サブ基板の固定用、およびアンテナ線の取り出し用で、すべてΦ2 を開けます。



切断、穴あけしたプリント基板 (部品面)


同プリント基板 (ハンダ面)

プリント基板加工時の追加情報

 上で、本機のプリント基板では縦の長さが1穴分多くとってある、と述べましたが、それがケースに収納するときに災いをしました。 前機では、ケースの縦の長さにかなり余裕があったので大丈夫と思ったのですが、 本機では、1穴分多いプリント基板をそのままでは、電池ケースとともに収納することができませんでした。

 私の場合には、既にプリント基板を完成させた後のことですから、縦の長さを少しでも短く(ケースに収められるように)するために、仕方なくやすりで削る羽目になりました。 完成後のプリント基板をやすりで削るということは、 当然ながら削り粉が出たりするわけですから、大変な作業になります。

 したがって、各部品をプリント基板に取り付ける前に、やすりで削っておく必要があります。 もしくは、前機のように1穴分少なくとって基板を切断する、かです。 後者の場合にはパターン設計の変更も必要になるでしょう。

 私としては仕方がないので前者の方法をお勧めしますが、基板の上辺と下辺を合わせて少なくても1mm は削る必要があります。 そのときの注意として、基板の上辺をケースの上の内側面にぴったりと合わせるように削ります。 ケースの内側面は一直線にはなっていなく、左右の両端付近では内側に膨らんでいるのでその膨らみを吸収するように、次項の写真のように、現物合わせで基板の上辺左右の端付近を削るようにします。

 基板の下辺についても現物合わせで、電池ケースの外側面と馴染むように削りますが、1mm 程度は必要です。 この電池ケースの外側面も一直線ではなく左右の両端付近では外側に少し膨らんでいるので、それに合わせます。 面倒な作業になりますが、以上のように基板サイズの修正を事前に行ってください。

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

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

 本機で使用する LCDモジュールは、基板との接続用の足ピンのピッチが 1.5 mm のため、通常の 2.54 mm ユニバーサル基板には、そのままでは搭載することができません。 そのために、専用のピッチ変換基板を使用する場合は良いのですが、 本機のように専用のピッチ変換基板を使用しない場合には、一工夫が必要です。 そこら辺りのことを "184. ストップウオッチ II" の "LCDのプリント基板への取り付け" で詳細に述べていますので、そちらを参照してください。

 また、FMラジオモジュールについても、外部との各接続用端子間のピッチが 2 mm のため、同様にそのままでは搭載することができません。 こちらについては、前々作 "187. FMステレオラジオ" の "FMラジオ、デジタルアンプの各モジュールの加工" で、ピッチ変換の方法を詳細に述べていますので、そちらを参照してください。 ただし、本機ではサブ基板の使用を省いて、直接プリント基板上でピッチ変換を行いました。 要領は同じです。

左図では黒色の文字列のためにハンダ面のパタンが分かり辛いので右図で補完

 BOX型ジャック用サブ基板については、前作 "188. FMステレオラジオ II" の BOX型ジャックの取り付け を参照してください。 4個のタクトスイッチについては、毎回述べているように本機でも、 キートップ長が通常のものより長いもので、実測 9.5mm のものを使用しています。 左下の2個の青色の四角いものは以前に "秋月電子" で購入した、ニッセイ電機のメタライズドフィルムコンデンサで、立てて取り付けるとケース内部の空間に収まらないため、 寝かせて取り付けました。

 2個のコネクタの 2P ピンヘッダ(オス)については、"秋月電子" の背の低いタイプのロープロファイルピンヘッダ(全長:7.7 mm)を使用しました。 そして、相棒のコネクタ(メス)には、 空間の高さが小さくてピンソケットの使用ができないため、"TJC8ピンターミナル(メス)" を曲げて使用しました。 このターミナルについては、"182. ラーメン・タイマー II 2題" の "ケースへのプリント基板の取り付け" を参照してください。 ICSP 端子用の 6P ピンヘッダ(オス)については、通常の長さのものです。

 SELECT スイッチに使用した 2P DIP スイッチ(フジソク製、型番は分からない)ですが、左右の向きを前機とは逆に取り付けました。 このように取り付けると写真のようにスイッチに印字の1が逆立ちをしてしまいますが、 本機の場合には、スイッチ OFF = 0 は右側に、スイッチ ON = 1 は左側になるようにした方が自然だと思います。

 最後になりましたが、前機で大いに不満を感じていた電源スイッチを変更しました。 本機では上写真のような 6P トグルスイッチ(フジソク製、型番: ATE2D )を使用して、プリント基板上に配置をしました。 このスイッチは以前に Yahoo!オークションで入手したもので、50 mA と許容電流が小さいのですが、6P の2回路用なので並列にして倍の電流まで使用ができるようにしています。 しかし本機では、そんなに大きな電流は流れないので十分な余裕があります。

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

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

 右写真の上部中央位置に抵抗が付けられていて、その周りのパターンも左パターン図 (ハンダ面) とは異なっていますが、正しいのはパターン図の方(抵抗はいらない)ですので注意をしてください。 回路図、および プリント基板パターン図 (部品面) を見ていただければ、正解はパターン図であることが分かると思います。

 これらの抵抗は、LCD モジュールの電源電圧の測定をするための分圧用抵抗ですが、当初、考慮不足から回路図とは異なった分圧比の抵抗を取り付けてしまったためで、本プリント基板では、部品面に LCD モジュールを搭載した後からでは、 その下に位置する抵抗の交換をしたりの修正が、部品面ではできないために写真のようになっています。

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

■ ケース加工図 ■

 使用したケースは "FMステレオラジオ II" と同様に、100均(セリア)で購入した "トラベルケース L 1P (No.1432) 山田化学" というポリスチレンケースです。

 このケースは特殊な形状をしているため、下図に寸法を記入してありますが、これらの値は参考までに見ておいてください。 実際にケース加工を行う場合は、その方法を前作の "FMステレオラジオ II" の ケース加工図 の項で述べているので、そちらを参照してください。

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

■ 使用部品表 ■

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

(データシート)
PICマイコン .................... PIC16F1705
トランジスタ .................... 2SA1015
LCDモジュール .................... AQM0802A-RN-GBW

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

■ 参考資料・サイト ■

PIC16F1705 データシート .......... https://akizukidenshi.com/download/ds/microchip/PIC16F1705.pdf
PIC16F1765 日本語 データシート .......... https://akizukidenshi.com/download/ds/microchip/PIC16F1764_PIC16LF1769_j.pdf
RDA5807M データシート .......... http://www.datasheet.jp/pdf/808923/RDA5807M.html
AQM0802A-RN-GBW データシート .......... https://akizukidenshi.com/download/ds/xiamen/AQM0802.pdf
Ren He FMラジオモジュール RDA5807M RRD-102V2.0 ステレオ DC 2.7-3.6V 5個 収納ボックス付き

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


Copyright (C) 2021-2023 やまもとみのる
初版:2021年12月7日、初公開:2022年1月27日、最終更新:2023年10月23日