| ホーム | 私の電子工作作品集 |
[ 初公開日:2022年1月27日 ] |
"FMステレオラジオ II" を製作して以来、次の数点において私自身が不満と物足らなさを、ずっと抱き続けていたためそれらを解消するために、この "FMステレオラジオ III" を新たに製作をし直しました。
当初は、ハードウェアを "FMステレオラジオ II" のプリント基板をそのまま使用して、PIC の交換と配線替えだけの変更を考えていたのですが、やはり電源スイッチについての不満も大きいので、
プリント基板の製作やケースの加工を新たにし直すことしました。
|
*注1. PIC の RA0、RA1、RA4、RA5、RC4、RC5 は内部プルアップ機能を ON にして使用。 | |
*注2. LED(高輝度 赤)は ステレオ放送のときに点灯させるもので、この LED は正面から見ることが多くて眩しいので、輝度を抑えるために電流制限抵抗は通常より大きな抵抗を使用。 |
| 回路図 (FM_StereoRadioIII.CE3) | ページトップ |
|
| ページトップ |
この項では、"FMステレオラジオ II" から "FMステレオラジオ III" に、ハードウェアの変更とプログラムの移植をした後で、新たに追加をした機能についてだけを説明します。
その他の機能については、"FMステレオラジオ II" の機能を引継いで同様のため、そちらの 機能概要と使用法 を参照してください。 なお、前者の 機能概要と使用法 で "EEPROM" についての記述は、後者では "高書き込み耐性プログラム フラッシュメモリ (PFM)" と読み替えてください。 両者では使用した PIC が異なっているためハードウェア構造も異なっていますが、 どちらも不揮発性メモリで行っている目的は同様で変わりません。 ○ スイッチ操作による開始メッセージの表示を省略
これらの表示に先立って、LCD モジュールの電源電圧*の測定値と LCD コントラストの設定値が、2秒間追加されて次のように表示される。 したがって、上の3つの表示は2秒間ずつ遅れて表示されるようになる。 ( *LCD モジュールの電源電圧 ≒ 本体の電源電圧 ) [ NHK FM ] [ x.xxV xx ] タイマーカウントの開始 ・・・ FMラジオモジュールの設定、ラジオ局名の表示 LCDモジュールの電源電圧値、LCDコントラストの設定値の表示 カウントの2秒後 ・・・ 音量設定値の表示 4秒後 ・・・ FMラジオモジュールから情報の読み出し 受信強度の表示 6秒後 ・・・ 周波数の表示 8秒後 ・・・ セーブ情報をバックアップ 1分後 ・・・ スリープ状態に移行 |
| ページトップ |
前作 "FMステレオラジオ II" で使用していた PIC16F684 を、本機 "FMステレオラジオ III" では、同じ 14 ピンの PIC16F1705 に変更をしたことから、プログラムを移植するに当たっては少なからず大変でした。
というのも、前者、すなわち従来の PIC16F ファミリーと、後者の PIC16F1 ファミリーのものとではアーキテクチャが異なっているため、前者用のプログラムをそのままでは、もちろん、単純に後者で使用することはできません。 私にとって PIC16F1 ファミリーを使用するのが、遅まきながら今回が初めてである、ということを冒頭でも少しばかり触れましたが、本機 "FMステレオラジオ III" の製作を通して、プログラムを作成するという観点で、 私が新たに学んだことやトラブった経験などをここにまとめておきます。 ● プログラムメモリの構成とプログラムカウンタ(PC) 初めに、プログラムメモリの構成とプログラムカウンタ PC との関係を図に表し、従来の PIC16F ファミリーと新しい PIC16F1 ファミリーとを、比較してみると分かり易くなると思います。 まず、次図に示すのは従来の PIC16F ファミリー のプログラムメモリで、2K ワードずつのページ単位が、最大でページ 0 〜 3 までの 4 ページ(2K x 4 = 8K ワード)で構成 されることから、この全メモリをアドレス指定ができるように、 プログラムカウンタ PC は 13 ビット で構成されています。 この最大 4 ページのプログラムメモリの内、"FMステレオラジオ II" で使用した PIC16F684 では、薄緑色に彩色をしたページ 0 だけ(2K ワード)が実装されています。 しかし、上述のようにプログラムカウンタ PC は、13 ビットから 15 ビットに拡張されたため、それに伴って、スタックメモリのビット幅も 15 ビットに拡張され、しかも、従来は 8 レベルしか存在していなかったのが、 倍の 16 レベルに拡張されました。 最近、時々オーバーフローをさせることが増えてきた私にとっては、とても心強く感じられます。 他に、ページを切り替えるために用意された命令で、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 ファミリーの中でも、例えば 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 ワード)がフル搭載されています。) また、間接アドレス指定をする場合にも、最大 4,096 バイトもあるデータメモリを識別するには、従来のように STATUS レジスタの IRP ビットと FSR レジスタだけでは、データメモリ全域のアドレス指定をすることができないため、 2 バイトの FSR レジスタペア FSRnH と FSRnL が設けられています。 しかも FSR0H と FSR0L 、 FSR1H と FSR1L (TABLE 3-2 を参照)の2組が用意され、 INDF0、INDF1 レジスタが これらのレジスタペアに対応した、間接レジスタ となっています。 そして、これらのレジスタで間接アクセスを容易にするために、間接レジスタ(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' ;音量 上リストで示した、フラッシュメモリの読み書き等を行うために必要となる各種の 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)から抜粋したもの)
固定参照電圧(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.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 接続で書き込みを行った、ことだけを付け加えておきます。 この件に関してはまた改めて述べるときがあるかもしれません。 | プログラムのトップに戻る |
|
| ページトップ |
本項と次項で述べるいろいろな注意事項は、本機の前身である "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個 収納ボックス付き |