;***** FM_StereoRadioIII.asm **********************************************
;
;	ＦＭステレオラジオIII
;
;	Copyright (C) 2020-2022   by M.Yamamoto. All rights reserved.
;
;	Homepage URL: http://xyama.sakura.ne.jp/hp/FM_StereoRadioIII.html
;
;     ( coding:  Ver. 0.00   2020/10/11  by M.Yamamoto )
;	coding:  Ver. 1.00   2020/10/22  初期バージョン、PIC16F684 に変更
;	update:  Ver. 1.01   2020/10/25  スリープ機能の追加
;	update:  Ver. 1.02   2020/10/28  ST_LEDの表示、受信強度のLCD表示
;	update:  Ver. 1.03   2020/10/28  各フラグの ONタイミングを見直し
;	update:  Ver. 1.04   2020/11/04  SEEK機能での選局を追加
;	update:  Ver. 1.05   2020/11/05  計画変更のためのソース変更
;	update:  Ver. 1.06   2020/11/09  FMスペクトラムのためのRSSIデータ収集機能
;	bugfix:  Ver. 1.07   2021/01/06  テーブルデータアドレスの計算を修正
;	bugfix:  Ver. 1.07b  2021/01/12  テーブルデータアドレスの計算を修正(コメント)
;	update:  Ver. 1.10   2022/01/13  PIC16F1705 にプログラム移植
;	update:  Ver. 1.11   2022/01/13  スタートメッセージの表示省略の機能追加
;	update:  Ver. 1.12   2022/01/23  LCD電源の電圧低下に伴うコントラスト自動補正
;
;******************************************************* PicAsm 0262 ******

;	Device: PIC16F1705	Clock: 8MHz (Internal OSC)

#ifdef comment

 <機能概要と使用法>

・本機 FMステレオラジオでは、FMラジオモジュール（ RDA5807M ）と、LCDモジュール（ AQM0802A-RN-GBW ）を
　使用し、共に  PIC から I2C 接続で制御をしている。 なお、本機ではパワーアンプを省いて小型化を図ったた
　め、ヘッドフォン（イヤホン）で聞くことを想定している。

・さて、本機の電源スイッチをオンにすると、LCD に次の開始メッセージが交互に２秒間ずつ連続２回表示される。

			[ FmStereo ]			[ Ver x.xx ]
			[ RadioIII ]			[  by M.Y  ]

・その後、２秒間無表示の後、PIC のフラッシュメモリにバックアップが取られている、前回のセーブ情報（ラジ
　オ局番号、音量設定値）が読み出され、そのラジオ局番号を基にして、予め番号毎にプリセットがされている情
　報に変換をする。

・そして、LCD の１行目にはラジオ局名、２行目には音量設定値（範囲: 0～15）が表示されるとともに、必要情報
　を FMラジオモジュールに設定をして、選局、および音量を調節して音声を出力する。

・この２行目に表示されている音量設定値は初めの約２秒間で、続いて FMラジオモジュールから得た情報の内の、
　受信強度が約２秒間表示される。 また、ステレオ放送がされているときには、LED （赤）を点灯させる。 
　その後は、受信しているラジオ局の周波数が表示されて、ユーザがスイッチ操作をするか、PIC がスリープ状態
　に移行するまで表示し続ける。 

			[ NHK FM   ]		[ NHK FM   ]		[ NHK FM   ]
			[  Vol:xx  ]		[  Lev:xx  ]		[  82.5MHz ]

・なお、プログラム作成時のデフォルトのフラッシュメモリセーブ情報には、ラジオ局番号 = 名古屋周辺地区では
　3、東京周辺地区では 4、大阪周辺地区は 3 の、共に NHK FM で、音量 = 2（範囲: 0～15）に設定がされている。

・周波数の表示の後は、ユーザのスイッチ操作待ちとなって、各スイッチの ON／OFF 状態を監視するが、１分が経
　過してもスイッチ操作がないときには、乾電池電源のための省電力化として、タイマーによるカウントを停止さ
　せ、 PIC をスリープ状態にするとともに、LCD の電源も OFF にし、LED も消灯させる。 これらの操作は既に選
　局が済んでいる FMラジオモジュールの動作には影響がない（音声は出力され続ける）。

・本機では、FMラジオモジュールを操作するためのタクトスイッチが４個あり、まず、ラジオ局番号（各ラジオ局
　の局名、周波数、FMラジオモジュールへの設定情報を、番号毎にプリセットしてある）を選択するために、TUNING 
　DOWN スイッチと TUNING UP スイッチがある。なお、ラジオ局番号はプログラム内部で使用をしている番号であ
　って、LCD に表示されることはない。

・この番号毎にプリセットしてある情報には、名古屋周辺地区用、東京周辺地区用、大阪周辺地区用の３種類が用
　意されていて、それらをタクトスイッチとは別に、SELECT 1,0 の２個の DIP スイッチの操作によって、簡単に
　変更をすることが可能である。 ただし、この操作は電源スイッチをオンにする前に行っておく必要がある。

		SELECT 1	SELECT 0	（ 選局方法 ）
		　　0   	    0   	名古屋周辺地区用　┐
		　　0   	    1   	東京周辺地区用　　│プリセット情報で選局
		　　1   	    0   	大阪周辺地区用　　┘
		　　1   	    1   	100kHzステップ毎の SEEK機能で自動選局

・上記のプリセット情報で選局する場合には、TUNING DOWN スイッチは、押す毎にラジオ局番号が－１され、0 の
　次は、プログラム設定されている内の、最大値となる。また、TUNING UP スイッチは、押す毎にラジオ局番号が
　＋１され、最大値の次は、0 となる。

・SEEK機能で選局する場合には、TUNING DOWN スイッチを押すと、現在選択されている放送局の周波数から、100kHz
　ステップ毎に減る方向にシークダウンしながら選局を行う。 

・また、TUNING UP スイッチを押すと、逆に、周波数が 100kHzステップ毎に増す方向にシークアップしながら選
　局を行う。 そして、帯域の上限または下限では停止しないでラップをし、シークを続行する。 なお、本機では、
　帯域を 76 ～ 108 MHz に予め設定がしてある。

・次に、音量（範囲: 0～15）を調節するために、VOLUME DOWN スイッチと VOLUME UP スイッチがあり、VOLUME 
　DOWN スイッチは、押す毎に音量が－１され、0 になると、それ以上押しても更新されることはない。

・また、VOLUME UP スイッチは、押す毎に＋１され、最大値 15 になると同様に、それ以上押しても更新されるこ
　とはない。

・このように、４個のタクトスイッチのどれかを操作すると、ラジオ局番号／シーク選局、または音量設定値が更
　新され、それに応じて LCD の表示、または音声出力の音量が変更される。

			[ FM AICHI ]		[ FM AICHI ]		[ FM AICHI ]
			[  Vol:xx  ]		[  Lev:xxx ]		[  80.7MHz ]

・また、４個のタクトスイッチのどれかを操作したときには、現在カウント中のタイマーが、必ずクリアされて新
　たに０からカウントが始まり、LCD の２行目に表示される内容は上述のように、初めの２秒間は音量設定値が、
　次の２秒間は受信強度が表示され、その後は、周波数が表示されるようになる。

			タイマーカウントの開始　・・・　FMラジオモジュールの設定、
							ラジオ局名、音量設定値の表示
			　　　カウントの２秒後　・・・　FMラジオモジュールから情報の読み出し
							受信強度の表示
					４秒後　・・・　周波数の表示
					６秒後　・・・　セーブ情報をバックアップ
					１分後　・・・　スリープ状態に移行

・そして、カウントが０から始まって６秒が経過をすると、現在 FMラジオモジュールに設定されているプリセット
　の番号（ラジオ局番号）と音量設定値を、電源 OFF 後の次回の本機の使用時に備えたセーブ情報として、 PIC 
　のフラッシュメモリに自動的にバックアップが取られる。

・したがって、この２秒、４秒、６秒の各カウント値が経過する以前に、４個のスイッチのどれかを続けて押した
　場合には、押す度に必ず０からの新たなカウントがされるようになる。

・特に、セーブ情報のバックアップについては、スイッチの操作毎に無条件で取るのではなく、このように一定の
　時間監視（６秒間）を行い、続く操作がなくなった時点でバックアップを取るようにしている。すなわち、６秒
　が経過する以前に次の操作があった場合には、前の操作のバックアップは取られず無視される。これは書き込み
　回数に制限があるフラッシュメモリの寿命を延ばすために、無駄な書き込みをしないための処置である。

・なお、SEEK機能で選局する場合のセーブ情報は、ラジオ局番号ではなく、FMラジオモジュールに設定するための
　チャネル情報等と音量設定値が対象となる。

・また、同じく SEEK機能で選局する場合には、FMラジオモジュールを設定した直後には、まだ選局したラジオ局名
　が定まらないため、LCD の１行目の表示には次のように、シークを実行中である旨を表示をする。

			[ * Seek * ]		[ NHK FM   ]		[ NHK FM   ]
			[  Vol:xx  ]		[  Lev:xx  ]		[  82.5MHz ]

・そして、タイマーカウント開始の２秒後のタイミングで、FMラジオモジュールから読み出した情報（チャネル情
　報）と、プリセットで選局するときの登録情報をもとにラジオ局名を特定して、LCD の１行目の表示を書き換え
　る。また、２行目に表示されるタイミングと表示内容は、プリセット情報で選局するときと同様である。

・なお、このときにプリセットで選局するときの登録情報内に、FMラジオモジュールから読み出した情報（チャネ
　ル情報）が登録されてなかったときには、ラジオ局名が特定できないため、１行目は次のように表示をする。

						[ ???????? ]		[ ???????? ]
						[  Lev:xx  ]		[  xx.xMHz ]

・このときの２行目の周波数の表示には、FMラジオモジュールから読み出した情報（チャネル情報）を、計算によ
　って周波数に変換をして表示をする。

・本機の４個のタクトスイッチの機能については上述した通りであるが、他に PIC がスリープ状態になっていると
　きだけには、RA ポート変化割り込みの機能を持たせて、４個のタクトスイッチのどれかを操作したときに、スリ
　ープからウェイクアップをさせるようにしている。

・そして、このウェイクアップをした直後には、再び LCD の電源を ON にして、消してしまっていた表示内容の復
　活、ステレオ放送のときには LED も点灯をさせる。 このときは FMラジオモジュールに対しての設定は既に済ん
　でいるので再設定は行わないが、タイマーのカウントを開始をして、そのタイミングでもって FMラジオモジュー
　ルから情報の再読み出しを行ったり、LCD への各種の情報の表示は上述と同様に行う。

・また、ウェイクアップの直後というのは、４個のタクトスイッチのどれかを操作しても、スリープから目覚めさ
　せることが目的であって、FMラジオモジュールに対して設定の変更は行っていないため、このときには６秒が経
　過してもセーブ情報のバックアップも行わない。

---（Ver. 1.06 で 以下機能を追加）---------------------------------------------------------------------

・FMスペクトラム図を作成するために、各チャネルの周波数に対する RSSIデータを収集する機能を追加した。

・本機の電源スイッチをオンにした後の開始メッセージが終了する直前までに、TUNING_UP スイッチと VOLUME_DOWN 
　スイッチを同時に押していると、RSSIデータを収集する機能を開始する。

・数秒後に押していた手を離すと、まず、LCD の１行目に周波数（76.0MHz）、２行目にその受信強度が表示される。
　本機では 76 ～ 108 MHz (world wide) の帯域をカバーしているので、下限の 76.0MHz から始まる。

			[  76.0MHz ]
			[  Lev:xx  ]

・そして、TUNING_UP スイッチを押す毎に＋ 0.1 MHz ずつ周波数が増加して、その周波数に対する受信強度ととも
　に、LCD の１行目と２行目にそれぞれが表示される。 上限の 108 MHz に達すると、次にはまた初めの 76.0MHz 
　に戻る。

・また、TUNING_DOWN スイッチの場合には、逆に、押す毎に－ 0.1 MHz ずつ周波数が減少して、同様に LCD の１
　行目と２行目にそれぞれが表示される。 下限の 76.0MHz に達すると、次には上限の 108 MHz になる。

・なお、これらの操作の後で VOLUME_DOWN スイッチを押すと、現在表示されている周波数の増減はしないで、再度
　RSSIデータの収集だけを行って、同様に LCD に表示をし直す。

・このように、帯域 76 ～ 108 MHz 内を３個のスイッチの操作で、自由に行き来することが可能であるが、本機で
　はそれぞれのデータを LCD に表示することしかできないので、データ収集のためには、ユーザがこれらのデータ
　をメモする必要がある。（ (108 - 76) / 0.1 + 1 = 321 個あるので たいへん !! ）

・また、一旦、この RSSIデータの収集機能の実行に移行した場合には、この機能から抜け出す方法はないので、電
　源スイッチをオフにするしかない。 上述のスイッチ操作をしないで電源を入れ直せば、再び、通常のラジオ受信
　機として機能をするようになる。

---（Ver. 1.10 (PIC16F1705 に移植) 以降に追加した機能）------------------------------------------------

・１つ目（Ver. 1.11) は、従来は本機の電源スイッチをオンにすると、必ず LCD に開始メッセージが表示される
　ため、ラジオ音声が出力されるまでに約 10 秒間を要したが、スイッチ操作によってこの時間を省略する機能を
　追加した。スイッチ操作がなければ、通常通り次の開始メッセージが表示される。

			[ FmStereo ]			[ Ver x.xx ]
			[ RadioIII ]			[  by M.Y  ]

・４個のタクトスイッチの内、真ん中の２個、すなわち TUNING_DOWN スイッチと VOLUME_UP スイッチを、同時に
　押しながら電源スイッチをオンにすると、開始メッセージの表示が省略されて、直ちに、前回のセーブ情報に基
　づいたラジオ局情報の LCD 表示とともに音声出力がされる。

・２つ目（Ver. 1.12) は、電源電圧低下に伴う LCD 表示のコントラスト低下を自動補正する機能を追加した。

・本機では本体電源に乾電池を使用しているため、時間の経過とともに電圧低下を起こし、LCD の電源電圧も低下
　して一定値を下回ってくると、LCD のコントラストが弱くなって、表示文字が見づらくなるため、この弱まった
　コントラストを自動で補強をするというもの。

・自動補正するタイミングは、電源スイッチをオンにした直後、PIC のスリープ状態からウェイクアップした直後、
　および４個のタクトスイッチのどれかを操作した直後に行う。それぞれ、そのときの LCD モジュールの電源電圧
　を測定し、その電圧値が低下をしてきても LCD コントラストをほぼ一定に保つように、自動的に設定をする。

・これらのとき本機能の追加以前は、LCD には次のように一連の表示がされていたが、

			[ NHK FM   ]		[ NHK FM   ]		[ NHK FM   ]
			[  Vol:xx  ]		[  Lev:xx  ]		[  82.5MHz ]

　これらの表示に先立って、LCD モジュールの電源電圧の測定値と LCD コントラストの設定値が、２秒間追加され
　て次のように表示される。したがって、上の３つの表示は２秒間ずつ遅れて表示されるようになる。

			[ NHK FM   ]
			[ x.xxV xx ]

・これらの表示等のタイミングの関係は、本機能の追加後では次のように変更になったが、表示中に４個のタクトス
　イッチのどれかを操作したときには、現在カウント中のタイマーが、必ずクリアされて新たに０からカウントがし
　直されることには、従来と変わりはない。

		タイマーカウントの開始　・・・　FMラジオモジュールの設定、ラジオ局名の表示
						LCDモジュールの電源電圧値、LCDコントラストの設定値の表示
		　　　カウントの２秒後　・・・　音量設定値の表示
		　　　		４秒後　・・・　FMラジオモジュールから情報の読み出し
						受信強度の表示
				６秒後　・・・　周波数の表示
				８秒後　・・・　セーブ情報をバックアップ
				１分後　・・・　スリープ状態に移行

・したがって、従来の機能概要での秒数の記述に関しても、本機能の追加後においては、上記のように変更になった
　秒数に読み替えて頂きたい。

#endif

;==========================================================================

;	PIC16F1705 のピンと用途

;		(1) Vdd: 電源 +3V DC		(14) Vss: 電源 GND
;		(2) RA5: VOLUME_DOWN SW 入力	(13) RA0: TUNING_UP SW 入力   / DAT
;		(3) RA4: VOLUME_UP SW 入力	(12) RA1: TUNING_DOWN SW 入力 / CLK
;		(4) RA3: 予備入力 / Vpp		(11) RA2: AD 変換 入力
;		(5) RC5: SELECT_1 SW 入力	(10) RC0: I2C SDA 入出力
;		(6) RC4: SELECT_0 SW 入力	 (9) RC1: I2C SCL 出力
;		(7) RC3: ST/MO LED 出力		 (8) RC2: LCD 電源制御 出力

;	*注.  RA3 は入力専用で、出力ポートには使えない。

;==========================================================================

		list		p=pic16f1705		;デバイス情報
		include		p16f1705.inc		;インクルードファイル
		errorlevel	-302			;Eliminate bank warning

;==========================================================================
;		コンフィギュレーションワードの設定
;==========================================================================

	__config _CONFIG1, _FOSC_INTOSC & _WDTE_OFF & _PWRTE_ON & _MCLRE_OFF & _CP_OFF & _BOREN_OFF & _CLKOUTEN_OFF & _IESO_OFF & _FCMEN_OFF
	__config _CONFIG2, _WRT_OFF & _PPS1WAY_OFF & _ZCDDIS_ON & _PLLEN_OFF & _STVREN_OFF & _BORV_HI & _LPBOR_ON & _LVP_ON

; ------------------------------------
; CONFIGURATION WORD 1 (ADDRESS:8007H)		Data Sheet DS40001729C - page 48
; ------------------------------------
;
;     CONFIG1 = 00 1001 1000 0100
;	        || |||| |||| ||||
;	bit2-0  || |||| |||| |+++---- FOSC<2:0>: オシレータ選択ビット
;	        || |||| |||| |          111 = ECH:    外部クロック、高消費電力モード(4-20 MHz):  OSC1/CLKINピンに供給
;	        || |||| |||| |          110 = ECM:    外部クロック、中消費電力モード(0.5-4 MHz): OSC1/CLKINピンに供給
;	        || |||| |||| |          101 = ECL:    外部クロック、低消費電力モード(0-0.5 MHz): OSC1/CLKINピンに供給
;	        || |||| |||| |      >   100 = INTOSC: 内部HFINTOSC CLKINピンはI/Oとして機能
;	        || |||| |||| |          011 = EXTRC:  CLKINピンに外部RC回路を接続
;	        || |||| |||| |          010 = HSOSC:  OSC1とOSC2ピンの間に高速水晶/セラミック振動子を接続
;	        || |||| |||| |          001 = XTOSC:  OSC1とOSC2ピンの間に水晶/セラミック振動子を接続
;	        || |||| |||| |          000 = LPOSC:  OSC1とOSC2ピンの間に低消費電力水晶振動子を接続
;	        || |||| |||| |
;	bit4-3  || |||| |||+ +------- WDTE<1:0>: ウォッチドッグ タイマ イネーブルビット
;	        || |||| |||             11 = ON:     WDTを有効
;	        || |||| |||             10 = NSLEEP: WDTを動作時は有効、スリープ中は無効
;	        || |||| |||             01 = SWDTEN: WDTをWDTCONレジスタのSWDTENビットで制御
;	        || |||| |||         >   00 = OFF:    WDTを無効
;	        || |||| |||           _____
;	bit5    || |||| ||+---------- PWRTE: パワーアップ タイマ イネーブルビット
;	        || |||| ||              1 = OFF: PWRTを無効
;	        || |||| ||          >   0 = ON:  PWRTを有効
;	        || |||| ||
;	bit6    || |||| |+----------- MCLRE: MCLR/VPPピン機能選択ビット
;	        || |||| |              LVPビット=0 の場合
;	        || |||| |               1 = ON:  MCLR/VPPピンの機能はMCLR、弱プルアップを有効
;	        || |||| |           >   0 = OFF: MCLR/VPPピンはデジタル入力、MCLRは無効、弱プルアップはWPUA3ビットで制御
;	        || |||| |             __
;	bit7    || |||| +------------ CP: コード保護ビット
;	        || ||||             >   1 = OFF: プログラムメモリのコード保護を無効
;	        || ||||                 0 = ON:  プログラムメモリのコード保護を有効
;	        || ||||
;	bit8    || |||+-------------- 未実装
;	        || |||
;	bit10-9 || |++--------------- BOREN<1:0>: ブラウンアウト リセット イネーブルビット
;	        || |                    11 = ON:     BORを有効
;	        || |                    10 = NSLEEP: BORを動作時は有効、スリープ中は無効
;	        || |                    01 = SBODEN: BORをBORCONレジスタのSBORENビットで制御
;	        || |                >   00 = OFF:    BORを無効
;	        || |                  ________
;	bit11   || +----------------- CLKOUTEN: クロック出力イネーブルビット
;	        ||                     FOSCビットがLP、XT、HSモードの場合
;	        ||                      このビットを無視しCLKOUT機能を無効、CLKOUTピンはI/Oとして機能
;	        ||                     FOSCビットがその他のモードの場合
;	        ||                  >   1 = OFF: CLKOUT機能を無効、CLKOUTピンはI/Oとして機能
;	        ||                      0 = ON:  CLKOUTピンのCLKOUT機能を有効
;	        ||
;	bit12   |+------------------- IESO: 内部/外部切り換えビット
;	        |                       1 = ON:  内部/外部切り換えモードを有効
;	        |                   >   0 = OFF: 内部/外部切り換えモードを無効
;	        |
;	bit13   +-------------------- FCMEN: フェイルセーフ クロックモニタ イネーブルビット
;	                                1 = ON:  フェイルセーフ クロックモニタと内部/外部切り換えの両方を有効
;	                            >   0 = OFF: フェイルセーフ クロックモニタを無効
;
; ------------------------------------
; CONFIGURATION WORD 2 (ADDRESS:8008H)		Data Sheet DS40001729C - page 50
; ------------------------------------
;
;     CONFIG2 = 11 0000 1111 1011
;	        || |||| |||| ||||
;	bit1-0  || |||| |||| ||++---- WRT<1:0>: フラッシュメモリ自己書き込み保護ビット
;	        || |||| |||| ||     >   11 = OFF:  書き込み保護を無効
;	        || |||| |||| ||         10 = BOOT: 0000h～01FFhを書き込み保護、0200h～1FFFhをPMCON制御で変更可能
;	        || |||| |||| ||         01 = HALF: 0000h～0FFFhを書き込み保護、1000h～1FFFhをPMCON制御で変更可能
;	        || |||| |||| ||         00 = ALL:  0000h～1FFFhを書き込み保護、PMCON制御によるアドレス変更を不可
;	        || |||| |||| ||
;	bit2    || |||| |||| |+------ PPS1WAY: PPSLOCKビット単方向セット イネーブルビット
;	        || |||| |||| |          1 = ON:  PPSLOCKビットは、ロック解除シーケンスを実行後に一度のみセット可、
;	        || |||| |||| |                   一度PPSLOCKがセットされると、その後PPSレジスタは変更不可
;	        || |||| |||| |      >   0 = OFF: ロック解除シーケンスを実行すれば何度でもPPSLOCKビットをセット/クリア可
;	        || |||| |||| |
;	bit6-3  || |||| |+++ +------- 未実装
;	        || |||| |
;	bit7    || |||| +------------ ZCDDIS: ZCDディセーブルビット
;	        || ||||             >   1 = ON:  ZCDを無効、有効にするにはZCDCONのZCDSENビットをセット
;	        || ||||                 0 = OFF: ZCDを常に有効
;	        || ||||
;	bit8    || |||+-------------- PLLEN: PLLイネーブルビット
;	        || |||                  1 = ON:  4xPLLを有効
;	        || |||              >   0 = OFF: 4xPLLを無効
;	        || |||
;	bit9    || ||+--------------- STVREN: スタック オーバーフロー/アンダーフロー リセット イネーブルビット
;	        || ||                   1 = ON:  スタックのオーバーフロー/アンダーフローによってリセットを発生させる
;	        || ||               >   0 = OFF: スタックのオーバーフロー/アンダーフローによってリセットを発生させない
;	        || ||
;	bit10   || |+---------------- BORV: ブラウンアウト リセット電圧選択ビット
;	        || |                    1 = LO: ブラウンアウト リセット電圧(VBOR)のトリップポイントを低に設定
;	        || |                >   0 = HI: ブラウンアウト リセット電圧(VBOR)のトリップポイントを高に設定                   
;	        || |                  _____
;	bit11   || +----------------- LPBOR: 低消費電力BORイネーブルビット
;	        ||                      1 = OFF: 低消費電力ブラウンアウト リセットを無効
;	        ||                  >   0 = ON:  低消費電力ブラウンアウト リセットを有効
;	        ||                    _____
;	bit12   |+------------------- DEBUG: インサーキット デバッガモード ビット
;	        |                   >   1 = OFF: インサーキット デバッガを無効、ICSPCLKとICSPDATは汎用I/Oピンとして機能
;	        |                       0 = ON:  インサーキット デバッガを有効、ICSPCLKとICSPDATはデバッガ専用
;	        |
;	bit13   +-------------------- LVP: 低電圧プログラミング イネーブルビット
;	                            >   1 = ON:  低電圧プログラミングを有効
;	                                0 = OFF: プログラミング時にMCLRに高電圧の印加が必要

;==========================================================================
;		定数の定義と変数のレジスタ割付け
;==========================================================================

#define 	ver_no	"1.12"
#define		chan_h	radio_no

#define 	lcnt1	lpcnt
#define 	lcnt2	buff
#define 	x16_hi	buff1
#define 	x16_lo	buff2
#define 	y16_hi	buff3
#define 	y16_lo	buff4

		radix	dec

_swTUP		equ	0		;TUNING UP   スイッチ	;PORTA
_swTDWN		equ	1		;TUNING DOWN スイッチ
_swVUP		equ	4		;VOLUME UP   スイッチ
_swVDWN		equ	5		;VOLUME DOWN スイッチ

SCL		equ	0		;I2C-LCD SCL		;PORTC
SDA		equ	1		;I2C-LCD SDA
LCD_PW		equ	2		;LCD 電源制御
LED_ST		equ	3		;ST/MO LED 出力
_swSEL0		equ	4		;SELECT 0  スイッチ
_swSEL1		equ	5		;SELECT 1  スイッチ

tm0_h_val	equ	256 - 200	;ハードタイマー０のカウント値 (4 / 8MHz * 200 = 100μS)
xx		equ	0		;補正値
tm0_s_val	equ	10000 - xx	;ソフトタイマー０のカウント値

		cblock	h'20'		;バンク 0
tm0_cnt_h				;ソフトタイマー０の high カウンタ
tm0_cnt_l				;ソフトタイマー０の low  カウンタ
sec_cnt					;秒カウンタ

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
max_no					;ラジオ局番号の最大値
updt_flg				;更新フラグ
;					; bit7: 初期化直後フラグ
;					; bit6: バックアップ抑止フラグ
;					; bit5: スリープ移行フラグ
;					; bit4: バックアップ要求フラグ
;					; bit3: 受信強度表示の終了フラグ
;					; bit2: 音量表示の終了フラグ
;					; bit1: 音量更新フラグ
;					; bit0: ラジオ局番号更新フラグ
updt2_flg				;更新フラグ2
;					; bit1: ウエイクアップフラグ
;					; bit0: 測定電圧とコントラストの表示終了フラグ
freq_high				;表示する周波数アドレスの保存用 high バイト
freq_low				;表示する周波数アドレスの保存用 low バイト
rda_reg_h				;RDA_Reg への書き込み high バイト
rda_reg_l				;RDA_Reg への書き込み low バイト
reg_0a_h				;RDA_Reg 0ah の読み出し high バイト
reg_0a_l				;RDA_Reg 0ah の読み出し low バイト
reg_0b_h				;RDA_Reg 0bh の読み出し high バイト
reg_0b_l				;RDA_Reg 0bh の読み出し low バイト
freq_flg				;CHAN から周波数へ変換指示フラグ
spec_flg				;FMスペクトラムRSSIデータ収集フラグ
contrast				;コントラスト

a16_hi					;16ビット演算引き数Ａ(上位) ／ 結果(上位)
a16_lo					;16ビット演算引き数Ａ(下位) ／ 結果(上位)
b16_hi					;16ビット演算引き数Ｂ(上位)
b16_lo					;16ビット演算引き数Ｂ(下位)
lpcnt			;lcnt1		;ループカウンタ
buff		;(00)	;lcnt2		;変換文字列の格納エリア
buff1		;(01)	;x16_hi		;
buff2		;(02)	;x16_lo		;
buff3		;(03)	;y16_hi		;
buff4		;(04)	;y16_lo		;
work1					;ワーク
work2					;ワーク

wt1cnt					;ウェイト用ループカウンタ
wt2cnt					;ウェイト用ループカウンタ
wt3cnt					;ウェイト用ループカウンタ

; ( I2C_F1S_LCD.sub で使用 )
;wt1cnt
addr_high				;読み出すデータの high アドレス
addr_low				;読み出すデータの low アドレス
lcdtmp					;LCD への出力データの一時保存
lcdtmp2					;
i2ctmp					;
bit_cnt					;ループカウンタ
		endc

		cblock	h'70'		;バンク 0（共通 RAM 領域）
ad_res_hi				;ワーク
ad_res_lo				;ワーク
		endc

		cblock	h'1a0'		;バンク 3
f_lpcnt					;ループカウンタ
save_data_ram				;3 * 4 = 12 バイト確保
		endc

;==========================================================================
;		マクロ命令定義
;==========================================================================

;		I2C LCD メッセージデータ表示マクロ

Disp_I2cLcd	macro	@com,@msg,@x
		movlw	@com
		call	I2cLcd_Cmd	;I2C LCD へコマンドを出力
	#if 	@x == 0
	#else
		movlw	high @msg	;メッセージデータの先頭アドレスの high アドレス
		movwf	addr_high	;
		movlw	low @msg	;メッセージデータの先頭アドレスの low アドレス
		movwf	addr_low	;
	#endif
		call	I2cLcd_Disp	;メッセージデータ１行表示
		endm

;==========================================================================
;		プログラム開始
;==========================================================================

		org	0		;リセットベクタ
		goto	init
		org	4		;割り込みベクタ
		goto	interrupt

;==========================================================================
;		サブルーチンファイルのインクルード
;==========================================================================

#define 	TRISX	TRISC		;PORTC で I2C を行う場合
#define 	PORTX	PORTC		;
#define 	_PIC16F1		;PIC16F1ファミリで使用

;				ドライブ、パス名は、各自の開発環境に合わせるように !!

		include		D:\Project\PIC\_sub\I2C_S2_LCD.sub

;==========================================================================
;		初期化処理
;==========================================================================

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: タイマー０割り込みを禁止
		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	;;;;; ( この関連 = ３ヶ所あり )

#ifdef	_test	;-- 1 / 3 --
		movlw	32		;デフォルト値
		movwf	contrast
		goto	lcd_contrast	;;;;; test: コントラスト手動調整テスト ;;;;;
#endif

		; スタートメッセージの表示を省略（Ver. 1.11 で追加）

		movlw	b'00110011'
		andwf	PORTA,W		
		xorlw	b'00100001'
		btfsc	STATUS,Z	;_swTDWN & _swVUP = ON か？
		goto	init02		;Yes

		; スタートメッセージの表示

		bsf	PORTC,LED_ST	;ST/MO LED 表示チェック = ON、
		movlw	2
		movwf	lpcnt		;メッセージの表示回数
init01
;			[FmStereo]	;スタートメッセージ
;			[RadioIII]
		Disp_I2cLcd	h'80',start_msg,1	;DDRAM のアドレスをセット(１行目先頭)
		Disp_I2cLcd	h'c0',         ,0	;DDRAM のアドレスをセット(２行目先頭)
		call	wait_1sec
		call	wait_1sec

;			[Ver x.xx]
;			[ by M.Y ]
		Disp_I2cLcd	h'80',         ,0	;DDRAM のアドレスをセット(１行目先頭)
		Disp_I2cLcd	h'c0',         ,0	;DDRAM のアドレスをセット(２行目先頭)
		call	wait_1sec
		call	wait_1sec

		decfsz	lpcnt,F		;メッセージの表示回数 = 終了 か？
		goto	init01		;No

		bcf	PORTC,LED_ST	;ST/MO LED 表示チェック = OFF
		movlw	h'01'		;表示クリア
		call	I2cLcd_Cmd	;I2C LCD へコマンドを出力
		call	wait_1sec
		call	wait_1sec

		; 各変数の初期設定

init02		clrf	updt_flg	;更新フラグの初期設定
		bsf	updt_flg,7	;初期化直後フラグ = ON
		clrf	updt2_flg	;更新フラグ2 の初期設定
		clrf	freq_flg	;CHAN から周波数へ変換指示フラグの初期設定

		clrf	spec_flg		;;RSSIデータ収集フラグの初期設定
		movlw	b'00110011'		;;（Ver. 1.06 で追加）
		andwf	PORTA,W			;;（Ver. 1.10 でビット変更）
		xorlw	b'00010010'		;;
		btfsc	STATUS,Z		;;_swTUP & _swVDWN = ON か？
		goto	spectrum		;;Yes

		swapf	PORTC,W		;SELECT 1,0 スイッチを読み取る
		andlw	h'03'
		xorlw	h'03'		;反転して
		movwf	sel_sw		;SELECT 1,0 スイッチ値を保存

		btfss	STATUS,Z	;SELECT 1,0 スイッチ = b'00' か？
		goto	init03		;No
			; sel_sw = b'00'
		movlw	(fm_radio_n_tbl_end - fm_radio_n_tbl) / 20 - 1	;（ 名古屋周辺地区用 ）
		goto	init06

init03		movlw	b'10'
		subwf	sel_sw,W
		btfsc	STATUS,C	;SELECT 1,0 スイッチ < b'10' か？
		goto	init04		;No
			; sel_sw = b'01'
		movlw	(fm_radio_t_tbl_end - fm_radio_t_tbl) / 20 - 1	;（ 東京周辺地区用 ）
		goto	init06

init04		btfss	STATUS,Z	;SELECT 1,0 スイッチ = b'10' か？
		goto	init05		;No
			; sel_sw = b'10'
		movlw	(fm_radio_o_tbl_end - fm_radio_o_tbl) / 20 - 1	;（ 大阪周辺地区用 ）
		goto	init06
			; sel_sw = b'11'
init05		movlw	(fm_radio_o_tbl_end - fm_radio_n_tbl) / 20 - 1	;（ SEEK機能で選局時 ）

init06		movwf	max_no		;ラジオ局番号の最大値を設定

		; セーブ情報（ラジオ局番号、音量）の復帰

		call	flash_mem_read	;フラッシュメモリから RAM バッファへデータ読み出し
		call	ram_buff_read	;RAM バッファから個々の変数へコピー
		goto	main

;--------------------------------------------------------------------------
;		I2C LCD（ AQM0802A の場合 ）表示用 メッセージ等
;--------------------------------------------------------------------------

		; スタートメッセージ

start_msg	dt	"FmStereo",0
		dt	"RadioIII",0
;			 01234567

		dt	"Ver ",ver_no,0
		dt	" by M.Y ",0
;			 01234567

		; 音量メッセージ

volume_msg	dt	" Vol:   ",0
;			 01234567

		; 受信強度メッセージ

level_msg	dt	" Lev:   ",0
;			 01234567

		; シーク操作中メッセージ

seek_msg	dt	"* Seek *",0
;			 01234567

;==========================================================================
;		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[FSR1] か？
		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'		;２行目
		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 へデータを１文字出力

		movlw	3
		subwf	lpcnt,W
		movlw	'.'
		btfsc	STATUS,Z	;lpcnt = 3 か？
		call	I2cLcd_Data	;Yes. I2C LCD へデータを１文字出力

		decfsz	lpcnt,F		;lpcnt = 0 か？
		goto	test01		;No

		movlw	'V'
		call	I2cLcd_Data	;I2C LCD へデータを１文字出力
		movlw	' '
		call	I2cLcd_Data	;I2C LCD へデータを１文字出力
		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 へデータを１文字出力
		movf	buff2,W
		call	I2cLcd_Data	;I2C LCD へデータを１文字出力
		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

;==========================================================================
;		High-Endurance フラッシュメモリ関連アクセス
;==========================================================================

;--------------------------------------------------------------------------
;    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 フラッシュメモリを１行（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

;==========================================================================
;		メイン・ルーチン
;==========================================================================

;updt_flg	;更新フラグ
;		; bit7: 初期化直後フラグ
;		; bit6: バックアップ抑止フラグ		 	 タイマー設定の
;		; bit5: スリープ移行フラグ			... 1 分後に ON
;		; bit4: バックアップ要求フラグ			... 8 秒後に ON
;		; bit3: 受信強度表示の終了フラグ		... 6 秒後に ON
;		; bit2: 音量表示の終了フラグ			... 4 秒後に ON
;		; bit1: 音量更新フラグ
;		; bit0: ラジオ局番号更新フラグ

;updt2_flg	;更新フラグ2
;		; bit1: ウエイクアップフラグ
;		; bit0: 測定電圧とコントラストの表示終了フラグ	... 2 秒後に ON

;--------------------------------------------------------------------------

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

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	;測定電圧値とコントラスト値の表示

		clrf	updt_flg	;更新フラグの初期設定
		bsf	updt_flg,6	;バックアップ抑止フラグ = ON

m02		movf	PORTA,W
		andlw	b'00110011'	;スイッチ入力を
		xorlw	b'00110011'	;反転
		btfss	STATUS,Z	;スイッチ操作 = なし か？
		goto	m02		;No
		goto	m05		;Yes

		; 各スイッチの ON／OFF 状態のチェック

m03		btfss	PORTA,_swTDWN	;TUNING DOWN スイッチ = ON か？
		call	tuning_down	;Yes

		btfss	PORTA,_swTUP	;TUNING UP スイッチ = ON か？
		call	tuning_up	;Yes

		btfsc	updt_flg,0	;ラジオ局番号更新フラグ = ON か？
		goto	m04		;Yes

		btfss	PORTA,_swVDWN	;VOLUME DOWN スイッチ = ON か？
		call	volume_down	;Yes

		btfss	PORTA,_swVUP	;VOLUME UP スイッチ = ON か？
		call	volume_up	;Yes

		btfss	updt_flg,1	;音量更新フラグ = ON か？
		goto	m06		;No

	;;;;;;; ラジオ局番号、音量の更新（スイッチ操作）があったとき ;;;;;;;

m04		call	table_select	;FM ラジオ放送局のテーブル選択 … 局名の表示
		call	fm_tuner_set	;（RDA5807M FM Tuner）の設定
		call	lcd_contrast	;LCD 電源電圧の測定とコントラスト調整
		call	volt_contr_disp	;測定電圧値とコントラスト値の表示

		clrf	updt_flg	;更新フラグの初期設定
		call	wait_30ms	;30m 秒 ウェイト

		; タイマー０の設定

m05		bcf	INTCON,TMR0IE	;タイマー０割り込みを禁止
		movlw	60		;1 分
		movwf	sec_cnt		;秒カウンタ
		movlw	tm0_h_val	;ハードタイマー０カウント値
		movwf	TMR0		;タイマー０ TMR0 の設定
		movlw	high tm0_s_val	;ソフトタイマー０の high カウント値
		movwf	tm0_cnt_h	;ソフトタイマー０の high カウンタの設定
		movlw	low tm0_s_val	;ソフトタイマー０の low  カウント値
		movwf	tm0_cnt_l	;ソフトタイマー０の low  カウンタの設定
		bsf	INTCON,TMR0IE	;タイマー０割り込みを許可
		goto	main

	;;;;;;; ラジオ局番号、音量の更新（スイッチ操作）がなかったとき ;;;;;;;

		; 測定電圧とコントラストの表示終了フラグのチェック

m06		btfss	updt2_flg,0	;測定電圧とコントラストの表示終了フラグ = ON か？
		goto	m07		;No

		call	volume_disp	;(２行目表示) … 音量の表示
		bcf	updt2_flg,0	;測定電圧とコントラストの表示終了フラグ = OFF
		goto	main

		; 音量表示の終了フラグのチェック

m07		btfss	updt_flg,2	;音量表示の終了フラグ = ON か？
		goto	m08		;No

		call	rssi_disp	;(２行目表示) … 受信強度の表示
		bcf	updt_flg,2	;音量表示の終了フラグ = OFF
		goto	main

		; 受信強度表示の終了フラグのチェック

m08		btfss	updt_flg,3	;受信強度表示の終了フラグ = ON か？
		goto	m11		;No

		btfsc	freq_flg,0	;CHAN から周波数へ変換指示フラグ = ON か？
		goto	m09		;Yes

		movf	freq_high,W	;表示する周波数のアドレスを復帰
		movwf	addr_high	;
		movf	freq_low,W	;
		movwf	addr_low	;

		Disp_I2cLcd    h'c0',,0 ;(２行目表示) … 周波数の表示
		goto	m10

m09		call	chan_freq_conv	;CHAN から周波数へ変換と周波数の表示

m10		bcf	updt_flg,3	;受信強度表示の終了フラグ = OFF
		goto	main

		; バックアップ要求フラグのチェック

m11		btfss	updt_flg,4	;バックアップ要求フラグ = ON か？
		goto	m12		;No

		call	ram_buff_write	;個々の変数から RAM バッファへコピー
		call	flash_mem_erase	;フラッシュメモリを１行（32 ワード）消去
		call	flash_mem_write	;RAM バッファからフラッシュメモリへデータ書き込み

		bcf	updt_flg,4	;バックアップ要求フラグ = OFF
		goto	main

		; スリープ移行フラグのチェック

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

;--------------------------------------------------------------------------
;		TUNING DOWN スイッチ = ON の処理
;--------------------------------------------------------------------------

tuning_down
		call	wait_30ms	;30m 秒 ウェイト
		btfsc	PORTA,_swTDWN	;TUNING DOWN スイッチ = ON か？
		goto	tdwn02		;No

		bsf	updt_flg,0	;ラジオ局番号更新フラグ = ON

		movlw	b'11'
		subwf	sel_sw,W
		btfss	STATUS,Z	;SELECT 1,0 スイッチ = b'11' か？
		goto	tdwn01		;No

		bcf	seekup,1	;SEEKUP = 0: Seek down
		goto	tdwn02

tdwn01		decf	radio_no,F	;ラジオ局番号 - 1
		incf	radio_no,W	;
		btfss	STATUS,Z	;W reg = 0 か？
		goto	tdwn02		;No

		movf	max_no,W	;
		movwf	radio_no	;ラジオ局番号 = 最大値

tdwn02		btfss	PORTA,_swTDWN	;TUNING DOWN スイッチ = OFF か？
		goto	$ - 1		;No

		return

;--------------------------------------------------------------------------
;		TUNING UP スイッチ = ON の処理
;--------------------------------------------------------------------------

tuning_up
		call	wait_30ms	;30m 秒 ウェイト
		btfsc	PORTA,_swTUP	;TUNING UP スイッチ = ON か？
		goto	tup02		;No

		bsf	updt_flg,0	;ラジオ局番号更新フラグ = ON

		movlw	b'11'
		subwf	sel_sw,W
		btfss	STATUS,Z	;SELECT 1,0 スイッチ = b'11' か？
		goto	tup01		;No

		bsf	seekup,1	;SEEKUP = 1: Seek up
		goto	tup02

tup01		incf	radio_no,F	;ラジオ局番号 + 1
		movf	radio_no,W
		subwf	max_no,W	;max_no - W(radio_no)
		btfsc	STATUS,C	;radio_no =< max_no か？
		goto	tup02		;Yes

		clrf	radio_no	;ラジオ局番号 = 最小値 = 0

tup02		btfss	PORTA,_swTUP	;TUNING UP スイッチ = OFF か？
		goto	$ - 1		;No

		return

;--------------------------------------------------------------------------
;		VOLUME DOWN スイッチ = ON の処理
;--------------------------------------------------------------------------

volume_down
		call	wait_30ms	;30m 秒 ウェイト
		btfsc	PORTA,_swVDWN	;VOLUME DOWN スイッチ = ON か？
		goto	vdwn01		;No

		bsf	updt_flg,1	;音量更新フラグ = ON
		decf	volume,F	;音量 - 1
		incf	volume,W	;
		btfss	STATUS,Z	;W reg = 0 か？
		goto	vdwn01		;No

		clrf	volume		;音量 = 最小値 = 0

vdwn01		btfss	PORTA,_swVDWN	;VOLUME DOWN スイッチ = OFF か？
		goto	$ - 1		;No

		return

;--------------------------------------------------------------------------
;		VOLUME UP スイッチ = ON の処理
;--------------------------------------------------------------------------

volume_up
		call	wait_30ms	;30m 秒 ウェイト
		btfsc	PORTA,_swVUP	;VOLUME UP スイッチ = ON か？
		goto	vup01		;No

		bsf	updt_flg,1	;音量更新フラグ = ON
		incf	volume,F	;音量 + 1
		movf	volume,W
		sublw	15		;15 - W(volume)
		btfsc	STATUS,C	;volume > 15 か？
		goto	vup01		;No

		movlw	15		;h'0f'
		movwf	volume		;音量 = 最大値

vup01		btfss	PORTA,_swVUP	;VOLUME UP スイッチ = OFF か？
		goto	$ - 1		;No

		return

;--------------------------------------------------------------------------
;		LCD へ音量の表示	;音量の範囲 = 0 ～ 15
;--------------------------------------------------------------------------

volume_disp
;			[ Vol:   ]
;			 01234567
		Disp_I2cLcd    h'c0',volume_msg,1 ;(２行目表示) … 音量の表示

		movlw	10
		subwf	volume,W
		btfsc	STATUS,C	;volume >= 10 か？
		goto	voldsp01	;Yes

		movlw	h'c6'		;DDRAM のアドレスをセット
		call	I2cLcd_Cmd	;I2C LCD へコマンドを出力
		movf	volume,W
		goto	voldsp02

voldsp01	movlw	h'c5'		;DDRAM のアドレスをセット
		call	I2cLcd_Cmd	;I2C LCD へコマンドを出力
		movlw	a'1'		;
		call	I2cLcd_Data	;I2C LCD へデータを１文字出力
		movlw	10
		subwf	volume,W

voldsp02	iorlw	h'30'		;ASCII コードに変換
		call	I2cLcd_Data	;I2C LCD へデータを１文字出力

		return

;==========================================================================
;		（ デバッグ用 ）
;==========================================================================

#ifdef _debug

		; レジスタ内容の表示
test_disp
		movlw	h'80'		;
		call	I2cLcd_Cmd	;I2C LCD へコマンドを出力

		movlw	low reg_0a_h
		movwf	FSR0L		;間接アドレスに設定
		movlw	high reg_0a_h
		movwf	FSR0H
		movlw	4
		movwf	lpcnt		;

tdsp01		moviw	FSR0++
		call	lcd_hex_out
		decfsz	lpcnt,F		;メッセージの表示回数 = 終了 か？
		goto	tdsp01

		return

;--------------------------------------------------------------------------

#define		cnvtmp	buff
#define		hextmp	buff + 1

		; １バイト変換 (BIN → HEX 文字) ２文字 LCD 出力
lcd_hex_out
		movwf	cnvtmp		;変換データの一時保存
		swapf	cnvtmp,W
		andlw	h'0f'
		call	hex_conv	;上位４ビットの変換
		call	I2cLcd_Data	;I2C LCD へデータを１文字出力

		movlw	h'0f'
		andwf	cnvtmp,W
		call	hex_conv	;下位４ビットの変換
		call	I2cLcd_Data	;I2C LCD へデータを１文字出力
		return

		; ４ビット変換 (BIN → HEX 文字)
hex_conv
		movwf	hextmp
		sublw	h'0a'
		btfsc	STATUS,Z	;
		goto	cnv01		;W = h'0a' 

		btfss	STATUS,C	;
		goto	cnv01		;W > h'0a' 

		movlw	h'30'		;0 ～ 9
		goto	cnv02

cnv01		movlw	h'40' - 9	;A ～ F

cnv02		addwf	hextmp,W
		return

#endif

;==========================================================================
;		FM ラジオ放送局のテーブル選択と表示
;==========================================================================

;	入力:	radio_no: ラジオ局番号

table_select
		; ラジオ局番号から該当のテーブルデータアドレスの計算

		movf	sel_sw,W	;SELECT 1,0 スイッチ値
		btfss	STATUS,Z	;SELECT 1,0 スイッチ = b'00' か？
		goto	tblsel01	;No
					;（ 名古屋周辺地区用 ）
		movlw	high fm_radio_n_tbl ;テーブルの先頭アドレスの high アドレス
		movwf	addr_high	;
		movlw	low fm_radio_n_tbl  ;テーブルの先頭アドレスの low アドレス
		movwf	addr_low	;
		goto	tblsel03

tblsel01	movlw	b'10'
		subwf	sel_sw,W
		btfsc	STATUS,C	;SELECT 1,0 スイッチ < b'10' か？
		goto	tblsel02	;No
					;（ 東京周辺地区用 ）
		movlw	high fm_radio_t_tbl ;テーブルの先頭アドレスの high アドレス
		movwf	addr_high	;
		movlw	low fm_radio_t_tbl  ;テーブルの先頭アドレスの low アドレス
		movwf	addr_low	;
		goto	tblsel03
		
tblsel02	btfss	STATUS,Z	;SELECT 1,0 スイッチ = b'10' か？
		goto	tblsel04	;No
					;（ 大阪周辺地区用 ）
		movlw	high fm_radio_o_tbl ;テーブルの先頭アドレスの high アドレス
		movwf	addr_high	;
		movlw	low fm_radio_o_tbl  ;テーブルの先頭アドレスの low アドレス
		movwf	addr_low	;

tblsel03	movf	radio_no,W	;ラジオ局番号
		movwf	work1		;x20 倍する、(1 テーブルデータ = 20 byte)
		bcf	STATUS,C
		rlf	work1,F		;x 2
		rlf	work1,F		;x 4
		movf	work1,W
		movwf	work2
		rlf	work1,F		;x 8
	;;	btfsc	STATUS,C	;オーバーフローした か？
	;;	incf	addr_high,F	;Yes. 読み出すデータの high アドレスの更新
	;;	bcf	STATUS,C	;(Ver. 1.07b で追加)

		rlf	work1,W		;x16
		btfsc	STATUS,C	;オーバーフローした か？			***（注）↓
		incf	addr_high,F	;Yes. 読み出すデータの high アドレスの更新	*** 下の例を参照

		addwf	work2,W		;x16 + x4 = x20
		btfsc	STATUS,C	;オーバーフローした か？			***（注）↓      (Ver. 1.07 で 2行追加)
		incf	addr_high,F	;Yes. 読み出すデータの high アドレスの更新	*** 下の例を参照

		addwf	addr_low,F	;読み出すデータの low アドレスの更新
		btfsc	STATUS,C	;オーバーフローした場合は
		incf	addr_high,F	;読み出すデータの high アドレスの更新

		; LCD へ局名の表示

;			[NHK FM  ]
;			 01234567
		Disp_I2cLcd    h'80',,0 ;(１行目表示) … 局名の表示

		movf	addr_high,W	;表示する周波数のアドレスを保存
		movwf	freq_high	;
		movf	addr_low,W	;
		movwf	freq_low	;

		; テーブルデータ（RDA_Reg 03h への書き込み用データ）の読み出し

		movlw	9		;ポインタを進める
		addwf	addr_low,F
		btfsc	STATUS,C	;オーバーフローした場合は
		incf	addr_high,F	;読み出すデータの high アドレスの更新
		call	tbl_read	;テーブルデータの読み出し
		movwf	rda_reg_h	;RDA_Reg 03h への書き込み high バイト

		incf	addr_low,F	;読み出すデータの low アドレスの更新
		btfsc	STATUS,Z	;オーバーフローした場合は
		incf	addr_high,F	;読み出すデータの high アドレスの更新
		call	tbl_read	;テーブルデータの読み出し
		movwf	rda_reg_l	;RDA_Reg 03h への書き込み low バイト
		goto	tblsel05

		; sel_sw = b'11' （SEEK選局）のとき

;			[* Seek *]
;			 01234567
tblsel04	Disp_I2cLcd    h'80',seek_msg,1 ;(１行目表示) … シーク操作中メッセージの表示

tblsel05	return

;--------------------------------------------------------------------------		***（注）↑

;	12		13		14		15		16		31		32

;	0000.1100	0000.1101	0000.1110	0000.1111	0001.0000	0001.1111	0010.0000

;x 4	0011.0000	0011.0100	0011.1000	0011.1100	0100.0000	0111.1100	1000.0000
;x16	1100.0000	1101.0000	1110.0000       1111.0000     1.0000.0000     1.1111.0000    10.0000.0000
;	---------     -----------     -----------     -----------    ------------    ------------    ------------
;x20	1111.0000     1.0000.0100     1.0001.1000     1.0010.1100     1.0100.0000    10.0110.1100    10.1000.0000
;									       ( radio_no = 0～31まで可 )

;--------------------------------------------------------------------------
;		FM ラジオ放送局 データテーブル 
;--------------------------------------------------------------------------

;	     ( RDA5807M のデータシート Page 10、REG 03H: Channel Select を参照 )

;	     周波数: Frequency (MHz) = SPACE (kHz) x CHAN + 76.0 MHz  … SPACE = 100

;				CHAN = (Frequency (MHz) - 76.0 (MHz)) / SPACE (kHz)
;				     = (Frequency - 76.0) x 10

; 例.  FM AICHI:80.7MHz   →	CHAN = 807 - 760 = 47 = b'0010 1111'         … Binary に変換

;				         CHAN << 6 =   b'00 1011 1100 0000'  … 左に 6 bit シフト

;				                   +             b'01 1000'  … OR をする
;				                   ------------------------
;				                   = b'0000 1011 1101 1000'  … 16 bit に書き換える

;;;;;	各地区で受信可能な FM ラジオ局が、以下の各テーブル内に設定がない場合には、（ radio_no = 0～31まで）追加が可能です。

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

fm_radio_t_tbl
		dt	"NACK5   ",0, " 79.5MHz",0, b'00001000', b'11011000'	; 0 … （ 東京周辺地区用 ）
		dt	"TOKYO FM",0, " 80.0MHz",0, b'00001010', b'00011000'	; 1
		dt	"J-WAVE  ",0, " 81.3MHz",0, b'00001101', b'01011000'	; 2
		dt	"NHK YKH ",0, " 81.9MHz",0, b'00001110', b'11011000'	; 3
		dt	"NHK TKO ",0, " 82.5MHz",0, b'00010000', b'01011000'	; 4
		dt	"YOKOHAMA",0, " 84.7MHz",0, b'00010101', b'11011000'	; 5
		dt	"InterFM ",0, " 89.7MHz",0, b'00100010', b'01011000'	; 6
		dt	"TBS FM  ",0, " 90.5MHz",0, b'00100100', b'01011000'	; 7
		dt	"BUNKA FM",0, " 91.6MHz",0, b'00100111', b'00011000'	; 8
		dt	"NIPPON  ",0, " 93.0MHz",0, b'00101010', b'10011000'	; 9
fm_radio_t_tbl_end

fm_radio_o_tbl
		dt	"CO.CO.LO",0, " 76.5MHz",0, b'00000001', b'01011000'	; 0 … （ 大阪周辺地区用 ）
		dt	"BeHappy!",0, " 78.9MHz",0, b'00000111', b'01011000'	; 1
		dt	"FM OSAKA",0, " 85.1MHz",0, b'00010110', b'11011000'	; 2
		dt	"NHK FM  ",0, " 88.1MHz",0, b'00011110', b'01011000'	; 3
		dt	"MBS     ",0, " 90.6MHz",0, b'00100100', b'10011000'	; 4
		dt	"OBC     ",0, " 91.9MHz",0, b'00100111', b'11011000'	; 5
		dt	"ABC     ",0, " 93.3MHz",0, b'00101011', b'01011000'	; 6
fm_radio_o_tbl_end

;==========================================================================
;		（RDA5807M FM Tuner）の設定
;==========================================================================

fm_tuner_set
		btfss	updt_flg,7	;初期化直後フラグ = ON か？
		goto	ftset03		;No

		; updt_flg.7 = 1 （初期化直後）のとき

		call	i2c_start	;スタートコンディション
		movlw	h'20'		;RDA スレーブアドレス
		call	byte_send	;1 バイト送信

		movlw	b'11010000'	;RDA_Reg 02h、15=DHIZ,14=DMUTE,13=MONO,12=BASS,11=RCLK_N,10=RCLK_D,9=SEEKUP,8=SEEK
		call	byte_send	;1 バイト送信
		movlw	b'00000001'	;RDA_Reg 02h、7=SKMODE,6:4=CLK_MODE[2:0],3=RDS_EN,2=NEW_METHOD,1=SOFT_RESET,0=ENABLE
		call	byte_send	;1 バイト送信

		movlw	b'11'
		subwf	sel_sw,W
		btfsc	STATUS,Z	;SELECT 1,0 スイッチ = b'11' か？
		goto	ftset01		;Yes

		movf	rda_reg_h,W	;RDA_Reg 03h、15:8=CHAN[9:2]
		call	byte_send	;1 バイト送信      ---------
		movf	rda_reg_l,W	;RDA_Reg 03h、7:6=CHAN[1:0],5=DIRECT MODE,4=TUNE,3:2=BAND[1:0],1:0=SPACE[1:0]
		call	byte_send	;1 バイト送信     ---------
		goto	ftset02

ftset01		movf	radio_no,W	;RDA_Reg 03h、15:8=CHAN[9:2], (radio_no = chan_h)
		call	byte_send	;1 バイト送信      ---------
		movf	chan_l,W	;RDA_Reg 03h、7:6=CHAN[1:0],5=DIRECT MODE,4=TUNE,3:2=BAND[1:0],1:0=SPACE[1:0]
		iorlw	h'18'		;4=TUNE = '1', 3:2=BAND[1:0] = '10' = 76-108 MHz (world wide), 1:0=SPACE[1:0] = '00' = 100 kHz
					;チューニング操作が完了すると、TUNE = 0 に設定され、STC = 1 に設定される
		call	byte_send	;1 バイト送信

ftset02		movlw	b'00000000'	;RDA_Reg 04h、15:12=RSVD,11=DE,10=RSVD,9=SOFTMUTE_EN,8=AFCD
		call	byte_send	;1 バイト送信
		movlw	b'00000000'	;RDA_Reg 04h
		call	byte_send	;1 バイト送信

		movlw	b'10001000'	;RDA_Reg 05h、15=INT_MODE,14:12=RSVD,11:8=SEEKTH[3:0]
		call	byte_send	;1 バイト送信
		movf	volume,W
		iorlw	b'10000000'	;RDA_Reg 05h、7:6=?? ,5:4=RSVD,3:0=VOLUME[3:0]
		call	byte_send	;1 バイト送信                      -----------

		call	i2c_stop	;ストップコンディション
		goto	ftset06

ftset03		btfss	updt_flg,0	;ラジオ局番号更新フラグ = ON か？
		goto	ftset05		;No

		; updt_flg.0 = 1 （ラジオ局番号更新）のとき

		movlw	b'11'
		subwf	sel_sw,W
		btfsc	STATUS,Z	;SELECT 1,0 スイッチ = b'11' か？
		goto	ftset04		;Yes

		movlw	h'03'		;
		call	rda_reg_write	;RDA_Reg = h'03' へ書き込み
		goto	ftset06

		; updt_flg.0 = 1 && sel_sw = b'11' （SEEK選局）のとき

		;	9 = SEEKUP:  0 = シークダウン, 1 = シークアップ
		;	8 = SEEK:    0 = ストップシークを無効, 1 = 有効
		;	    シーク動作が完了すると、SEEK = 0 に設定され、STC = 1 に設定される
		;	7 = SKMODE:  0 = 帯域の上限または下限でラップしてシークを続行, 1 = シークを停止

ftset04		movlw	b'11010001'	;RDA_Reg 02h、15=DHIZ,14=DMUTE,13=MONO,12=BASS,11=RCLK_N,10=RCLK_D,9=SEEKUP,8=SEEK
		iorwf	seekup,W	;                                                                  -------- ------
		movwf	rda_reg_h
		movlw	b'00000001'	;RDA_Reg 02h、7=SKMODE,6:4=CLK_MODE[2:0],3=RDS_EN,2=NEW_METHOD,1=SOFT_RESET,0=ENABLE
		movwf	rda_reg_l	;             --------                                                      --------
		movlw	h'02'		;
		call	rda_reg_write	;RDA_Reg = h'02' へ書き込み
		goto	ftset06

		; updt_flg.1 = 1 （音量更新）のとき

ftset05		movlw	b'10001000'	;
		movwf	rda_reg_h
		movf	volume,W
		iorlw	b'10000000'	;
		movwf	rda_reg_l
		movlw	h'05'		;
		call	rda_reg_write	;RDA_Reg = h'05' へ書き込み

ftset06		return

;==========================================================================
;	（RDA5807M FM Tuner）からレジスタ内容の読み出しと表示
;==========================================================================

rssi_disp
		movlw	high reg_0a_h	;読み出しデータの格納アドレス
		movwf	FSR0H
		movlw	low reg_0a_h
		movwf	FSR0L		;間接アドレスに設定
		movlw	h'0a'		;
		call	rda_reg_read	;RDA_Reg = h'0a' から読み出し

		btfss	reg_0a_h,6	;STC ビット = 1（シーク/チューン完了）か？
		goto	rssi_disp	;No

		movlw	h'0b'		;
		call	rda_reg_read	;RDA_Reg = h'0b' から読み出し

		bcf	PORTC,LED_ST	;ST/MO LED 消灯
		btfsc	reg_0a_h,2	;ST（Stereo Indicator）ビット = 1 か？
		bsf	PORTC,LED_ST	;Yes. ST/MO LED 点灯

		rrf	reg_0b_h,W
		andlw	h'7f'		;RSSI[6:0]
		movwf	work1
		call	bindec8cz_cnv	;10進数文字列に変換（ゼロサプレス)

;			[ Lev:   ]
;			 01234567
		Disp_I2cLcd    h'c0',level_msg,1 ;(２行目表示) … 受信強度の表示

		movlw	h'c5'		;DDRAM のアドレスをセット
		call	I2cLcd_Cmd	;I2C LCD へコマンドを出力

		movlw	high (buff+1)	;変換文字列の格納エリア
		movwf	FSR0H
		movlw	low (buff+1)
		movwf	FSR0L		;間接アドレスの設定
		movlw	2		;
		movwf	lpcnt		;ループカウンタに設定

rssi01		moviw	FSR0++
		call	I2cLcd_Data	;I2C LCD へデータを１文字出力
		decfsz	lpcnt,F		;ループカウンタ - 1 = 0 か？
		goto	rssi01		;No

		movlw	b'11'
		subwf	sel_sw,W
		btfsc	STATUS,Z	;SELECT 1,0 スイッチ = b'11' か？
		call	channel_compare	;Yes. 局名の表示

		return

;--------------------------------------------------------------------------
;	SEEK で選局時の局名の表示と 周波数のテーブルアドレスを保存
;--------------------------------------------------------------------------

;		Reg_03h 15:6     CHAN[9:0]	xxxx.xxxx xx00.0000
;		Reg_0Ah  9:0 READCHAN[9:0]	0000.00xx xxxx.xxxx

channel_compare
		movf	reg_0a_h,W	;READCHAN[9:8]
		movwf	work2
		movf	reg_0a_l,W	;READCHAN[7:0]
		movwf	work1		; work2:????.??xx, work1:xxxx.xxxx

		; 10 ビットデータを 右詰め → 左詰め に変換
		rrf	work2,F		;????.???x x 
		rrf	work1,F		;            xxxx.xxxx x
		rrf	work2,F		;x???.???? x 
		rrf	work1,F		;            xxxx.xxxx x
		rrf	work2,F		;xx??.???? ? 
		movlw	h'c0'
		andwf	work2,F		; work1:xxxx.xxxx, work2:xx00.0000

		movlw	high fm_radio_n_tbl ;テーブルの先頭アドレスの high アドレス
		movwf	addr_high	;
		movlw	low fm_radio_n_tbl  ;テーブルの先頭アドレスの low アドレス
		movwf	addr_low	;

		movf	max_no,W	;
		movwf	lpcnt		;ループカウンタ

chan01		movf	addr_high,W	;テーブルの先頭アドレス（局名）を退避
		movwf	freq_high	;
		movf	addr_low,W	;
		movwf	freq_low	;

		movlw	9 * 2		;ポインタを進める
		addwf	addr_low,F
		btfsc	STATUS,C	;オーバーフローした場合は
		incf	addr_high,F	;読み出すデータの high アドレスの更新
		call	tbl_read	;テーブルデータの読み出し

		incf	addr_low,F	;読み出すデータの low アドレスの更新
		btfsc	STATUS,Z	;オーバーフローした場合は
		incf	addr_high,F	;読み出すデータの high アドレスの更新

		subwf	work1,W		;high バイトの比較
		btfss	STATUS,Z	;READCHAN[9:2] = CHAN[9:2] か？
		goto	chan02		;No

		call	tbl_read	;テーブルデータの読み出し
		andlw	h'c0'
		subwf	work2,W		;low バイトの比較
		btfsc	STATUS,Z	;READCHAN[1:0] = CHAN[1:0] か？
		goto	chan04		;Yes

chan02		incf	addr_low,F	;読み出すデータの low アドレスの更新
		btfsc	STATUS,Z	;オーバーフローした場合は
		incf	addr_high,F	;読み出すデータの high アドレスの更新
		decfsz	lpcnt,F		;ループカウンタ - 1 = 0 か？
		goto	chan01		;No

		; テーブル上に登録がなかったとき
		movlw	h'80'		;１行目表示
		call	I2cLcd_Cmd	;I2C LCD へコマンドを出力
		movlw	8
		movwf	lpcnt		;ループカウンタ

chan03		movlw	'?'
		call	I2cLcd_Data	;I2C LCD へデータを１文字出力
		decfsz	lpcnt,F		;ループカウンタ - 1 = 0 か？
		goto	chan03		;No

		bsf	freq_flg,0	;CHAN から周波数へ変換指示フラグ = 1
		goto	chan05

		; テーブル上に登録があったとき
chan04		movf	freq_high,W	;表示する局名のアドレスを復帰
		movwf	addr_high	;
		movf	freq_low,W	;
		movwf	addr_low	;

		Disp_I2cLcd    h'80',,0 ;(１行目表示) … 局名の表示

		movf	addr_high,W	;表示する周波数のアドレスを保存
		movwf	freq_high	;
		movf	addr_low,W	;
		movwf	freq_low	;
		bcf	freq_flg,0	;CHAN から周波数へ変換指示フラグ = 0

		; フラッシュメモリへのセーブ対象情報
chan05		movf	work1,W
		movwf	radio_no	;= chan_h
		movf	work2,W
		iorwf	seekup,W	;SEEKUP を足し込む
		movwf	chan_l		;

		return

;--------------------------------------------------------------------------
;	CHAN から周波数へ変換と 周波数の表示（テーブル上に登録がないとき）
;--------------------------------------------------------------------------

;	周波数: Frequency (MHz) = SPACE (kHz) x CHAN + 76.0 MHz
;		Frequency x 10  =               CHAN + 760 

;		Reg_0Ah  9:0 READCHAN[9:0]	0000.00xx xxxx.xxxx

chan_freq_conv
		btfsc	spec_flg,0	;RSSIデータ収集フラグ = ON か？
		goto	chfrcv01	;Yes

		movf	reg_0a_h,W	;READCHAN[9:8]
		andlw	h'03'
		movwf	a16_hi
		movf	reg_0a_l,W	;READCHAN[7:0]
		movwf	a16_lo
		goto	chfrcv02

chfrcv01	movf	chan_h,W	;CHAN[9:8]
		movwf	a16_hi
		movf	chan_l,W	;CHAN[7:0]
		movwf	a16_lo

chfrcv02	movlw	high 760
		movwf	b16_hi
		movlw	low 760
		movwf	b16_lo
		call	add_16		;READCHAN[9:8] + 760

		call	bindec16cz_cnv	;10進数 5桁の文字列に変換（ゼロサプレス)

		movlw	h'c0'		;２行目表示
		btfsc	spec_flg,0	;RSSIデータ収集フラグ = ON か？
		movlw	h'80'		;Yes. １行目表示
		call	I2cLcd_Cmd	;I2C LCD へコマンドを出力

		movlw	high (buff+1)	;変換文字列の格納エリア
		movwf	FSR0H
		movlw	low (buff+1)
		movwf	FSR0L		;間接アドレスの設定
		movlw	4
		movwf	lpcnt		;ループカウンタ

chfrcv03	decf	lpcnt,W
		movlw	'.'
		btfsc	STATUS,Z	;lpcnt - 1 = 0 か？
		call	I2cLcd_Data	;Yes. I2C LCD へ '.' を１文字出力

		moviw	FSR0++
		call	I2cLcd_Data	;I2C LCD へデータを１文字出力
		decfsz	lpcnt,F		;ループカウンタ - 1 = 0 か？
		goto	chfrcv03	;No

		movlw	high (fm_radio_n_tbl + 14) ;先頭アドレスの high アドレス
		movwf	addr_high	;
		movlw	low (fm_radio_n_tbl + 14)  ;先頭アドレスの low アドレス
		movwf	addr_low	;
		call	I2cLcd_Disp	;メッセージデータ "MHz" 表示

		return

;--------------------------------------------------------------------------
;		（RDA5807M FM Tuner）の 指定 RDA_Reg へ書き込み
;--------------------------------------------------------------------------

;		入力 W_reg:	RDA_Reg 番号

rda_reg_write
		movwf	work1		;RDA_Reg 番号を退避
		movlw	high rda_reg_h	;書き込みデータの格納アドレス
		movwf	FSR0H
		movlw	low rda_reg_h
		movwf	FSR0L		;間接アドレスに設定

		call	i2c_start	;スタートコンディション
		movlw	h'22'		;RDA スレーブアドレス
		call	byte_send	;1 バイト送信

		movf	work1,W		;RDA_Reg 番号
		call	byte_send	;1 バイト送信

		moviw	FSR0++		;書き込みデータの high バイト
		call	byte_send	;1 バイト送信
		moviw	FSR0++		;書き込みデータの low バイト
		call	byte_send	;1 バイト送信

		call	i2c_stop	;ストップコンディション
		return

;--------------------------------------------------------------------------
;		（RDA5807M FM Tuner）の 指定 RDA_Reg から読み出し
;--------------------------------------------------------------------------

;		入力 W_reg:	RDA_Reg 番号

rda_reg_read
		movwf	work1		;RDA_Reg 番号を退避

		call	i2c_start	;スタートコンディション
		movlw	h'22'		;RDA スレーブアドレス
		call	byte_send	;1 バイト送信

		movf	work1,W		;RDA_Reg 番号
		call	byte_send	;1 バイト送信

		call	i2c_start	;再スタートコンディション
		movlw	h'22' + 1	;RDA スレーブアドレス
		call	byte_send	;1 バイト送信

		call	byte_reciv	;データ 1 バイト受信
		call	ack		;ACK
		movwi	FSR0++
		call	byte_reciv	;データ 1 バイト受信
		call	nack		;NACK
		movwi	FSR0++

		call	i2c_stop	;ストップコンディション
		return

;==========================================================================
;	FMスペクトラム作成のための RSSIデータ収集（Ver. 1.06 で追加）
;==========================================================================

;		Frequency x 10  =  CHAN + 760 
;		CHAN		=  Frequency x 10 - 760

spectrum
		movlw	b'00110011'
		xorwf	PORTA,W
		andlw	b'00110011'
		btfss	STATUS,Z	;すべてのスイッチ = OFF か？
		goto	spectrum	;No

		; 各変数の初期設定

		bsf	spec_flg,0	;RSSIデータ収集フラグ = ON

		clrf	chan_h		;CHAN[9:8] 初期値の設定
		clrf	chan_l		;CHAN[7:0]
		clrf	sel_sw		;sel_sw を無効にする

		clrf	rda_reg_h	;CHAN-H: Reg03H[15:8]=CHAN[9:2]
		movlw	h'18'		;TUNE=1, BAND[1：0]=10, SPACE[1：0]=00
		movwf	rda_reg_l	;CHAN-L: Reg03H[ 7:6]=CHAN[1:0]
		movlw	h'02'		;
		movwf	volume		;
		call	fm_tuner_set	;（RDA5807M FM Tuner）の設定
		call	wait_1sec

		; LCD への表示

spect01		call	wait_1sec	;
		call	chan_freq_conv	;(１行目表示) … 周波数の表示
		call	rssi_disp	;(２行目表示) … 受信強度の表示

		; 各スイッチの ON／OFF 状態のチェック

spect02		btfss	PORTA,_swVDWN	;VOLUME DOWN スイッチ = ON か？
		goto	spect01		;Yes. 再表示

		btfss	PORTA,_swTDWN	;TUNING DOWN スイッチ = ON か？
		call	sp_tune_down	;Yes

		btfss	PORTA,_swTUP	;TUNING UP スイッチ = ON か？
		call	sp_tune_up	;Yes

		btfss	updt_flg,0	;更新フラグ = ON か？
		goto	spect02		;No

		; チャネルの変更 （ RDA_Reg = h'03' へ書き込み ）

		movf	chan_h,W	;CHAN[9:8]
		movwf	work2
		movf	chan_l,W	;CHAN[7:0]
		movwf	work1		; work2:0000.00xx, work1:xxxx.xxxx

		; 10 ビットデータを 右詰め → 左詰め に変換
		rrf	work2,F		;?000.000x x 
		rrf	work1,F		;            xxxx.xxxx x
		rrf	work2,F		;x?00.0000 x 
		rrf	work1,F		;            xxxx.xxxx x
		rrf	work2,F		;xx?0.0000 0 
		movlw	h'c0'
		andwf	work2,F		; work1:xxxx.xxxx, work2:xx00.0000

		movf	work1,W		;work1:xxxx.xxxx
		movwf	rda_reg_h	;書き込み high バイト
		movf	work2,W		;work2:xx00.0000
		iorlw	h'18'		;TUNE=1, BAND[1：0]=10, SPACE[1：0]=00
		movwf	rda_reg_l	;書き込み low バイト
		movlw	h'03'		;RDA_Reg 番号
		call	rda_reg_write	;RDA_Reg = h'03' へ書き込み

		bcf	updt_flg,0	;更新フラグ = OFF
		goto	spect01

;--------------------------------------------------------------------------
;		TUNING DOWN スイッチ = ON の処理
;--------------------------------------------------------------------------

;			chan_h    chan_l
;			0000.00xx xxxx.xxxx

sp_tune_down
		call	wait_30ms	;30m 秒 ウェイト
		btfsc	PORTA,_swTDWN	;TUNING DOWN スイッチ = ON か？
		goto	tdwn01		;No

		bsf	updt_flg,0	;更新フラグ = ON

		decf	chan_l,F	;chan_l - 1
		incf	chan_l,W	;
		btfss	STATUS,Z	;W reg = 0 (chan_l < 0) か？
		goto	stdw01		;No

		decf	chan_h,F	;chan_h - 1
		incf	chan_h,W	;
		btfss	STATUS,Z	;W reg = 0 (chan_h < 0) か？
		goto	stdw01		;No

		movlw	high (1080 - 760) ;最大値
		movwf	chan_h
		movlw	low (1080 - 760)
		movwf	chan_l

stdw01		btfss	PORTA,_swTDWN	;TUNING DOWN スイッチ = OFF か？
		goto	$ - 1		;No

		return

;--------------------------------------------------------------------------
;		TUNING UP スイッチ = ON の処理
;--------------------------------------------------------------------------

;			chan_h    chan_l
;			0000.00xx xxxx.xxxx

sp_tune_up
		call	wait_30ms	;30m 秒 ウェイト
		btfsc	PORTA,_swTUP	;TUNING UP スイッチ = ON か？
		goto	stup02		;No

		bsf	updt_flg,0	;更新フラグ = ON

		movlw	high (1080 - 760)
		subwf	chan_h,W
		btfsc	STATUS,C	;chan_h < high (1080 - 760) か？
		goto	stup01		;No

		incf	chan_l,F	;chan_l + 1
		btfss	STATUS,Z	;W reg = 0 (chan_l = 0) か？
		goto	stup02		;No

		incf	chan_h,F	;chan_h + 1
		goto	stup02		;No

stup01		incf	chan_l,F	;chan_l + 1
		movlw	low (1080 - 760 + 1)
		subwf	chan_l,W
		btfss	STATUS,C	;chan_l < low (1080 - 760 + 1)
		goto	stup02		;Yes

		clrf	chan_h		;最小値 = 0
		clrf	chan_l		;

stup02		btfss	PORTA,_swTUP	;TUNING UP スイッチ = OFF か？
		goto	$ - 1		;No

		return

;==========================================================================
;		ウェイト・ルーチン (8 MHz)
;==========================================================================

		; 20μ秒 ウェイトルーチン

wait_20us
		movlw	7		;1
		nop			;1
		goto	wu01		;2
					;Total	40/8MHz*4 = 20 μS

		; 100μ秒 ウェイトルーチン
wait_100us
		movlw	39		;1

wu01		movwf	wt1cnt		;1

wu02		goto	$+1		;2
		decfsz	wt1cnt,F	;1,2
		goto	wu02		;2,0	1+1+(2+1+2)*38+(2+2+0)=196

		return			;2	196+2=198
					;Total	198/8MHz*4 = 99 μS = 100 μS = 0.1 mS

		; 30m 秒 ウェイトルーチン
wait_30ms
		movlw	30
		goto	wait_xxms

		; 100m 秒 ウェイトルーチン
wait_100ms
		movlw	100
		goto	wait_xxms

		; 200m 秒 ウェイトルーチン
wait_200ms
		movlw	200
		goto	wait_xxms

		; 1m 秒 ウェイトルーチン
wait_1ms
		movlw	1

		; xx m 秒（ 1m 秒 x W ）ウェイトルーチン
wait_xxms
		movwf	wt2cnt		;1

w1m01		movlw	221		;1
		movwf	wt1cnt		;1

w1m02		goto	$ + 1		;2
		goto	$ + 1		;2
		goto	$ + 1		;2
		decfsz	wt1cnt,F	;1,2
		goto	w1m02		;2,0	(2+2+2+1+2)*220+(2+2+2+2+0)=1988

		nop			;1
		goto	$ + 1		;2
		goto	$ + 1		;2
		decfsz	wt2cnt,F	;1,2
		goto	w1m01		;2,0	(1+1+1988+1+2+2+1+2)*(W-1)+(1+1+1988+1+2+2+2+0)=1998*W-1

		return			;2	1+(1998*W-1)+2=1998*W+2
					;Total	(1998*W+2)/8MHz*4 = (999*W + 1) μS

		; 1 秒 ウェイトルーチン
wait_1sec
		movlw	10
w1s01		movwf	wt3cnt

w1s02		call	wait_100ms
		decfsz	wt3cnt,F
		goto	w1s02
		return

;==========================================================================
;		割り込み処理
;==========================================================================

interrupt
		; 割り込み処理の振り分け

		movlb	0		;バンク 0
		btfsc	INTCON,TMR0IF	;タイマー０割り込み か？
		goto	int_t0		;Yes

		btfsc	INTCON,IOCIF	;状態変化割り込み か？
		goto	int_ioc		;Yes
		goto	int_end		;(Illegal interrupt)

		; タイマー０割り込み処理（ 100μ秒毎に割り込み ）

int_t0		bcf	INTCON,TMR0IF	;タイマー０割り込みフラグをクリア

		movlw	tm0_h_val	;ハードタイマー０カウント値
		movwf	TMR0		;タイマー０ TMR0 の設定

		movlw	1
		subwf	tm0_cnt_l,F	;ソフトタイマー０の low  カウンタ - 1
		btfsc	STATUS,Z	;ソフトタイマー０の low  カウンタ = 0 か？
		goto	int01		;Yes

		btfsc	STATUS,C	;ソフトタイマー０の low  カウンタ > 0 か？
		goto	int_end		;Yes

		subwf	tm0_cnt_h,F	;ソフトタイマー０の high カウンタ - 1
		goto	int_end	

int01		movf	tm0_cnt_h,F
		btfss	STATUS,Z	;ソフトタイマー０の high カウンタ = 0 か？
		goto	int_end		;No

		; １秒経過毎（ tm0_cnt_h = tm0_cnt_l = 0 ）

		movlw	high tm0_s_val	;ソフトタイマー０の high カウント値
		movwf	tm0_cnt_h	;ソフトタイマー０の high カウンタの設定
		movlw	low tm0_s_val	;ソフトタイマー０の low  カウント値
		movwf	tm0_cnt_l	;ソフトタイマー０の low  カウンタの設定

		movlw	60 - 2 + 1	;２秒経過の監視
		subwf	sec_cnt,W	;
		btfsc	STATUS,Z	;秒カウンタ = 60 - 1 か？
		bsf	updt2_flg,0	;Yes. 測定電圧とコントラストの表示終了フラグ = ON

		movlw	60 - 4 + 1	;４秒経過の監視
		subwf	sec_cnt,W	;
		btfsc	STATUS,Z	;秒カウンタ = 60 - 3 か？
		bsf	updt_flg,2	;Yes. 音量表示の終了フラグ = ON

		movlw	60 - 6 + 1	;６秒経過の監視
		subwf	sec_cnt,W	;
		btfsc	STATUS,Z	;秒カウンタ = 60 - 5 か？
		bsf	updt_flg,3	;Yes. 受信強度表示の終了フラグ = ON

		movlw	60 - 8 + 1	;８秒経過の監視
		subwf	sec_cnt,W	;
		btfss	STATUS,Z	;秒カウンタ = 60 - 7 か？
		goto	int02		;No

		btfss	updt_flg,6	;バックアップ抑止フラグ = ON か？
		bsf	updt_flg,4	;No. バックアップ要求フラグ = ON
		btfsc	updt_flg,6	;バックアップ抑止フラグ = ON か？
		bcf	updt_flg,6	;Yes. バックアップ抑止フラグ = OFF

int02		decfsz	sec_cnt,F	;秒カウンタ - 1 = 0 か？
		goto	int_end		;No

		bsf	updt_flg,5	;スリープ移行フラグ = ON
		bcf	INTCON,TMR0IE	;タイマー０割り込みを禁止
		goto	int_end

		; 状態変化割り込み処理

int_ioc		movlb	7		;バンク 7
		clrf	IOCAF		;状態変化割り込みフラグをクリアすると
					;INTCON レジスタの IOCIF ビットもクリアされる
		movlb	0		;バンク 0
		bcf	INTCON,IOCIE	;RAポート変化割り込みを禁止

int_end		retfie

;==========================================================================
;		各種の演算、データ変換サブルーチン
;==========================================================================

;--------------------------------------------------------------------------
;		16 ＋ 16 ＝ 16 ビットの加算
;--------------------------------------------------------------------------

;	(a16_hi,a16_lo) + (b16_hi,b16_lo) = (a16_hi,a16_lo)

; 使用変数:	a16_hi, a16_lo: 足される数の上,下位	(終了時に答えが返る)
;		b16_hi, b16_lo: 足す数の    上,下位	(終了時に変化しない)

add_16
		movf	b16_lo,W	;下位
		addwf	a16_lo,F
		movf	b16_hi,W	;上位
		addwfc	a16_hi,F

		return

;--------------------------------------------------------------------------
;		16 － 16 ＝ 16 ビットの減算
;--------------------------------------------------------------------------

;	(a16_hi,a16_lo) - (b16_hi,b16_lo) = (a16_hi,a16_lo)

; 使用変数:	a16_hi, a16_lo: 引かれる数の上,下位	(終了時に答えが返る)
;		b16_hi, b16_lo: 引く数の    上,下位	(終了時に変化しない)

sub_16
		movf	b16_lo,W	;下位
		subwf	a16_lo,F
		movf	b16_hi,W	;上位
		subwfb	a16_hi,F

		return

;--------------------------------------------------------------------------
;		16 ÷ 16 ＝ 16 ビットの割り算
;--------------------------------------------------------------------------

;	(a16_hi,a16_lo) ÷ (b16_hi,b16_lo) = (x16_hi,x16_lo) ... (y16_hi,y16_lo) 

; 使用変数:	a16_hi, a16_lo: 割られる数の上,下位	(終了時は０になる)
;		b16_hi, b16_lo: 割る数の    上,下位	(終了時に変化しない)
;		x16_hi, x16_lo: 答えの      上,下位	(答えが返る)
;		y16_hi, y16_lo: 余りの      上,下位	(余りが返る、ワークにも使用)
;		lcnt1,  lcnt2:  ループカウンタ
;		C フラグ:	エラーフラグ		(割る数が０であった場合 C=1)

div_16
		movlw	16
		movwf	lcnt1		;ループカウンタ1
		movf	b16_lo,W	;割る数下位をワークにコピー
		movwf	y16_lo
		movf	b16_hi,W	;割る数上位をワークにコピー
		movwf	y16_hi

d16_01		rlf	y16_lo,F	;左シフトする
		rlf	y16_hi,F
		btfsc	STATUS,C	;キャリフラグを見る
		goto	d16_02		;割る数の上位ビット位置検索

		decfsz	lcnt1,F
		goto	d16_01

		bsf	STATUS,C	;割る数が０である。エラー終了
		goto	d16_09

d16_02		clrf	x16_lo		;答え用変数のクリア
		clrf	x16_hi
		clrf	y16_lo		;ワーク変数のクリア
		clrf	y16_hi

		movlw	16
		movwf	lcnt2		;ループカウンタ2
		movf	lcnt1,W
		subwf	lcnt2,F		;残り、実ループの回数

d16_03		lslf	a16_lo,F
		rlf	a16_hi,F
		rlf	y16_lo,F
		rlf	y16_hi,F
		decfsz	lcnt1,F		;割られる数を初期位置までシフト
		goto	d16_03

d16_04		movf	b16_hi,W	;現位置での減算が可能かチェック
		subwf	y16_hi,W
		btfss	STATUS,C
		goto	d16_06

		movf	y16_hi,W
		subwf	b16_hi,W
		btfss	STATUS,C
		goto	d16_05

		movf	b16_lo,W
		subwf	y16_lo,W
		btfss	STATUS,C
		goto	d16_06

d16_05		movf	b16_lo,W
		subwf	y16_lo,F	;ワークから下位を引く
		btfss	STATUS,C	;キャリフラグが 1（正）なら次をスキップ
		decf	y16_hi,F	;上位 -1

		movf	b16_hi,W
		subwf	y16_hi,F	;ワークから上位を引く
		bsf	STATUS,C	;キャリフラグをセット
		goto	d16_07

d16_06		bcf	STATUS,C	;キャリフラグをクリア

d16_07		rlf	x16_lo,F	;キャリフラグの内容を答えにシフト
		rlf	x16_hi,F
		movf	lcnt2,F
		btfsc	STATUS,Z	;lcnt2 = 0 か？
		goto	d16_08		;Yes, 終了

		decf	lcnt2,F		;ビット位置を１つ下げる（右へ）
		lslf	a16_lo,F	;ワークへ１ビット左シフト
		rlf	a16_hi,F
		rlf	y16_lo,F
		rlf	y16_hi,F
		goto	d16_04

d16_08		bcf	STATUS,C	;正常終了

d16_09		return

;--------------------------------------------------------------------------
;		16 ビット数値を 10進数 5桁の文字列に変換（ゼロサプレス)
;--------------------------------------------------------------------------

; 使用変数:	a16_hi, a16_lo: 変換する 16 ビット数値
;		 	 work2: ワーク
;		         lpcnt: ループカウンタ
;		   buff + 0～4: 結果

bindec16cz_cnv
		movlw	high (buff+4+1)	;変換文字列の格納エリア
		movwf	FSR0H
		movlw	low (buff+4+1)
		movwf	FSR0L		;間接アドレスの設定

		call	bd16c		;(04) 最下位変換
		call	bd16c		;(03)
		call	bd16c		;(02)
		call	bd16c		;(01)
		call	bd16c		;(00) 最上位変換

		movlw	4		;
		movwf	lpcnt		;ループカウンタに設定

bd16cv01	moviw	0[FSR0]
		sublw	h'30'
		btfss	STATUS,Z	;(INDF) = h'30' か？
		goto	bd16cv02	;No

		movlw	' '
		movwi	FSR0++
		decfsz	lpcnt,F		;ループカウンタ - 1 = 0 か？
		goto	bd16cv01	;No

bd16cv02	return

		; ÷10（10で除算）
bd16c
		movlw	16		;16ビット繰り返し
		movwf	lpcnt		;ループカウンタに設定
		clrf	work2

bd16c01		lslf	a16_lo,F
		rlf	a16_hi,F
		rlf	work2,F
		movlw	-10		;-10 = h'f6'
		addwf	work2,W
		btfsc	STATUS,C	;C = 0 か？
		movwf	work2		;No
		btfsc	STATUS,C	;C = 0 か？
		incf	a16_lo,F	;No
		decfsz	lpcnt,F		;ループカウンタ - 1 = 0 か？
		goto	bd16c01		;No

		movf	work2,W		;余り
		addlw	h'30'		;文字コードに変換
		movwi	--FSR0
		return

;--------------------------------------------------------------------------
;		8 ビット数値を 10進数 3桁の文字列に変換（ゼロサプレス)
;--------------------------------------------------------------------------

; 使用変数:	         work1: 変換する 8 ビット数値
;		         work2: ワーク
;		         lpcnt: ループカウンタ
;		   buff + 0～2: 結果

bindec8cz_cnv
		movlw	high (buff+2+1)	;変換文字列の格納エリア
		movwf	FSR0H
		movlw	low (buff+2+1)
		movwf	FSR0L		;間接アドレスの設定

		call	bd8c		;(02) 最下位変換
		call	bd8c		;(01)
		call	bd8c		;(00) 最上位変換

		movlw	2		;
		movwf	lpcnt		;ループカウンタに設定

bd8cv01		moviw	0[FSR0]
		sublw	h'30'
		btfss	STATUS,Z	;(INDF) = h'30' か？
		goto	bd8cv02		;No

		movlw	' '
		movwi	FSR0++
		decfsz	lpcnt,F		;ループカウンタ - 1 = 0 か？
		goto	bd8cv01		;No

bd8cv02		return

		; ÷10（10で除算）
bd8c
		movlw	8		;8ビット繰り返し
		movwf	lpcnt		;シフトカウンタに設定
		clrf	work2		;一時余り格納場所のクリア

bd8c01		lslf	work1,F
		rlf	work2,F

		movlw	-10		;-10 = h'f6'
		addwf	work2,W
		btfsc	STATUS,C	;C = 0 か？
		movwf	work2		;No
		btfsc	STATUS,C	;C = 0 か？
		incf	work1,F		;No
		decfsz	lpcnt,F		;ループカウンタ - 1 = 0 か？
		goto	bd8c01		;No

		movf	work2,W		;余り
		addlw	h'30'		;文字コードに変換
		movwi	--FSR0
		return

;==========================================================================

copyright	de	"Copyright (C) 2020-2022 by M.Yamamoto"

;==========================================================================
;		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'		;音量

;==========================================================================

		end			;プログラムの終了
