;***** FM_StereoRadio.asm *************************************************
;
;	ＦＭステレオラジオ
;
;	Copyright (C) 2020   by M.Yamamoto. All rights reserved.
;
;     ( coding:  Ver. 0.00   2020/09/28  by M.Yamamoto )
;	coding:  Ver. 1.00   2020/10/03  初期バージョン
;	update:  Ver. 1.01   2020/10/04  コメントの強化、他
;	update:  Ver. 1.02   2020/10/07  LCDに音量表示を追加
;	update:  Ver. 1.03   2020/11/14  ラジオ局番号の最大値を拡張
;	bugfix:  Ver. x.xx
;
;******************************************************* PicAsm 0253 ******

;	Device: PIC12F635	Clock: 8MHz (Internal OSC)

#ifdef comment

 <機能概要と使用法>

・本機 FMステレオラジオでは、FMラジオモジュール（ RDA5807M ）と、LCDモジュール（ AQM0802A-RN-GBW ）を使
　用し、共に PIC から I2C 接続で制御をしている。また、他にデジタルアンプモジュール（ PAM8403 ）も使用を
　しているが、これはプログラムには関係がない。

・さて、本機の電源スイッチをオンにすると、LCD に次の開始メッセージが交互に２秒間ずつ連続２回表示される。

			[ FmStereo ]			[ Ver x.xx ]
			[   Radio  ]			[  by M.Y  ]

・その後、２秒間無表示の後、PIC の EEPROM にバックアップが取られている、前回のセーブ情報（ラジオ局番号、
　音量）が読み出されて、LCD にはラジオ局名と、その局の周波数が表示されるとともに、必要情報を FMラジオモ
　ジュールに設定をして、選局、および音量調節をして、スピーカーから音声を出力する。

			[ NHK FM   ]
			[  82.5MHz ]

・なお、プログラム作成時のデフォルトのセーブ情報は、ラジオ局番号 = 3（名古屋地区では 3: NHK FM）、音量 
　= 1（範囲: 0～15）となっている。

・その後はユーザのスイッチの操作待ちとなって、各スイッチの ON／OFF 状態を監視する。

・その監視するスイッチは４つあり、まず、ラジオ局番号（各ラジオ局の局名、周波数、FMラジオモジュールへの
　設定情報を、番号毎にプリセットしてある）を選択するために、TUNING DOWN スイッチと TUNING UP スイッチが
　ある。なお、ラジオ局番号はプログラム内部で使用をしている番号であって、LCD に表示されることはない。

・TUNING DOWN スイッチは、押す毎にラジオ局番号が－１され、0 の次は、プログラム設定されている内の、最大
　値となる。また、TUNING UP スイッチは、押す毎にラジオ局番号が＋１され、最大値の次は、0 となる。

・次に、音量（範囲: 0～15）を調節するために、VOLUME DOWN スイッチと VOLUME UP スイッチがあり、VOLUME 
　DOWN スイッチは、押す毎に音量が－１され、0 になると、それ以上押しても更新されることはない。

・また、VOLUME UP スイッチは、押す毎に＋１され、最大値になると同様に、それ以上押しても更新されることは
　ない。

・このように、４つのスイッチのどれかを操作すると、ラジオ局番号、または音量が更新され、それに応じて LCD 
　の表示、またはスピーカーの音量が変更される。

			[ FM AICHI ]
			[  80.7MHz ]

・ここで、音量（範囲: 0～15）の設定値を LCD に表示するように機能を追加（2020/10/7）* した。
　以上の４つのスイッチのどれかを操作すると、LCD の２行目に周波数の表示に先立って、１秒間だけ次のように
　音量の設定値が表示される。１秒が経過する以前に４つのスイッチのどれかを続けて押した場合には、押す毎に
　１秒が新たにカウントされて、最終的に押した時点から１秒が経過後に、周波数が表示されるようになる。

			[ Vol=xx ]	..... ２行目に音量（範囲: 0～15）が表示される。

　（ 機能を追加（2020/10/7）* ： 本機を使用していて音量を変更した場合に、実際にどのように変更がされたか
　　 を目で確認できないというのは、意外と不便さを感じるもので、そのために音量の表示機能を追加した。）

・このとき、次回の本機の使用時に備えて、セーブ情報（ラジオ局番号、音量）として、自動的に PIC の EEPROM 
　にバックアップが取られる。

・ただし、スイッチの操作毎に無条件でバックアップを取るのではなく、操作毎に一定の時間監視（５秒間）を行い、
　続く操作がなくなった時点でバックアップを取るようにしている。すなわち、５秒が経過する以前に次の操作が
　あった場合には、前の操作のバックアップは取られず無視される。これは書き込み回数に制限がある、EEPROM の
　寿命を延ばすための処置である。

#endif

;==========================================================================

;	PIC12F635 のピンと用途

;		(1) Vdd: 電源 +5V DC		(8) Vss: 電源 GND
;		(2) GP5: TUNING_DOWN SW 入力	(7) GP0: I2C SDA 出力
;		(3) GP4: TUNING_UP SW 入力　	(6) GP1: I2C SCL 出力
;		(4) GP3: VOLUME_DOWN SW 入力	(5) GP2: VOLUME_UP SW 入力

;	      GP3 は入力専用で、出力ポートには使えない。

;==========================================================================

		list		P=PIC12F635	;デバイス情報
		include        "p12f635.inc"	;インクルードファイル
		errorlevel	-302		;Eliminate bank warning

		__config	_CPD_OFF & _CP_OFF & _BOD_ON & _MCLRE_OFF & _PWRTE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT

;==========================================================================
;		定数の定義と変数のレジスタ割付け
;==========================================================================

#define 	ver_no	"1.03"

		radix	dec

SDA		equ	0		;I2C-LCD SDA
SCL		equ	1		;I2C-LCD SCL
_swVUP		equ	2		;VOLUME UP   スイッチ
_swVDWN		equ	3		;VOLUME DOWN スイッチ
_swTUP		equ	4		;TUNING UP   スイッチ
_swTDWN		equ	5		;TUNING DOWN スイッチ

tm0_h_val	equ	256 - 200	;ハードタイマー０のカウント値 (4 / 8MHz * 200 = 100μS)
xx		equ	0		;補正値
tm0_s_val	equ	10000 - xx	;ソフトタイマー０のカウント値

		cblock	h'40'
tm0_cnt_h				;ソフトタイマー０の high カウンタ
tm0_cnt_l				;ソフトタイマー０の low  カウンタ
sec_cnt					;秒カウンタ

radio_no				;ラジオ局番号	┐EEPROM セーブ情報
volume					;音量		┘
max_no					;ラジオ局番号の最大値
updt_flg				;更新フラグ
;					; bit2: 音量表示の終了フラグ
;					; bit1: バックアップ要求フラグ
;					; bit0: ラジオ局番号、音量更新フラグ
freq_high				;RDA_Reg 03h への書き込み用 high バイト
freq_low				;RDA_Reg 03h への書き込み用 low バイト
work1					;ワーク
work2					;ワーク

lpcnt					;ワーク
wt1cnt					;ウェイト用ループカウンタ
wt2cnt					;ウェイト用ループカウンタ
wt3cnt					;ウェイト用ループカウンタ

; ( I2C_S_LCD.sub で使用 )
;wt1cnt
addr_high				;読み出すデータの high アドレス
addr_low				;読み出すデータの low アドレス
lcdtmp					;LCD への出力データの一時保存
lcdtmp2					;
i2ctmp					;
bit_cnt					;ループカウンタ
		endc

		cblock	h'70'
w_save					;W レジスタの退避用
s_save					;STATUS レジスタの退避用
		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

		org	h'08'

;==========================================================================
;		サブルーチンファイルのインクルード
;==========================================================================

;#define	TRISA	TRISIO
;#define	PORTA	GPIO

		include		D:\Project\PIC\_sub\I2C_S_LCD.sub

;==========================================================================
;		初期化処理
;==========================================================================

init
		clrf	STATUS		;STATUS クリア
	 	clrf	INTCON		;INTCON クリア

		; GPIO をデジタルI/O に設定

		clrf	GPIO
		movlw	h'07'		;Comparator mode Off
		movwf	CMCON0		;GP2-0 はデジタルI/O

		bsf	STATUS,RP0	;バンク 1
		movlw	h'3c'		;GP5-GP2: 入力, GP1-GP0: 出力
		movwf	TRISIO

		movlw	h'34'		;GP5,GP4,GP2: プルアップ
		movwf	WDA		;選択
		movwf	WPUDA		;有効

		; 内部クロックを 8 MHz に設定

		movlw	h'70'		;CLOCK=8MHz
		movwf	OSCCON

		; タイマー０割り込みの設定

		movlw	h'08'		;プルアップ有効、プリスケーラ: 使用しない
		movwf	OPTION_REG

		bcf	STATUS,RP0	;バンク 0
		clrf	TMR0		;TMR0 クリア
		bcf	INTCON,T0IE	;T0IE: タイマー０割り込みを禁止
		bsf	INTCON,GIE	;GIE: グローバル割り込み許可

		; スタートメッセージの表示

		bsf	GPIO,SDA	;SDA="H"
		bsf	GPIO,SCL	;SCL="H"
		call	wait_200ms	;
		call	I2cLcd_Init	;I2C LCD (AQM0802A) の初期設定
		movlw	h'0c'		;表示オン／オフコントロール
		call	I2cLcd_Cmd	;D=1: 表示オン, C=0: カーソル非表示, B=0: 非ブリンク

		movlw	2
		movwf	lpcnt		;メッセージの表示回数
init01
;			[FmStereo]	;スタートメッセージ
;			[  Radio ]
		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

		movlw	h'01'		;表示クリア
		call	I2cLcd_Cmd	;LCD へコマンドを出力
		call	wait_1sec
		call	wait_1sec

		; セーブ情報（ラジオ局番号、音量）の復帰

		call	eeprom_read	;EEPROM からデータ読み出し
		call	table_select	;FM ラジオ放送局の選局

		; 各変数の初期設定

		clrf	updt_flg	;更新フラグの初期設定
		bsf	updt_flg,2	;音量表示の終了フラグ = ON

		movlw	(fm_radio_tbl_end - fm_radio_tbl) / 20 - 1
		movwf	max_no		;ラジオ局番号の最大値を設定
	
		goto	main

;--------------------------------------------------------------------------
;		I2C LCD（ AQM0802A の場合 ）表示用 メッセージ等
;--------------------------------------------------------------------------

		; スタートメッセージ

start_msg	dt	"FmStereo",0
		dt	"  Radio ",0
;			 01234567

		dt	"Ver ",ver_no,0
		dt	" by M.Y ",0
;			 01234567

		; 音量メッセージ

volume_msg	dt	" Vol=   ",0
;			 01234567

;--------------------------------------------------------------------------
;		EEPROM からデータ読み出し
;--------------------------------------------------------------------------

eeprom_read
		bsf	STATUS,RP0	;バンク 1
		movlw	save_data - eep_start
		movwf	EEADR		;EEPROM の記録アドレスを設定

		bcf	STATUS,RP0	;バンク 0
		movlw	radio_no	;ラジオ局番号のアドレスを
		movwf	FSR		;間接アドレスに設定
		movlw	2
		movwf	lpcnt		;ループカウンタ

eeprd01		bsf	STATUS,RP0	;バンク 1
		bsf	EECON1,RD	;読み出し制御 = ON
		movf	EEDATA,W	;データの読み出し
		incf	EEADR,F		;記録アドレスの更新

		bcf	STATUS,RP0	;バンク 0
		movwf	INDF		;データ読み出し
		incf	FSR,F		;間接アドレスの更新
		decfsz	lpcnt,F		;ループカウンタ - 1 = 0 か？
		goto	eeprd01		;No

		return

;--------------------------------------------------------------------------
;		EEPROM へデータ書き込み
;--------------------------------------------------------------------------

eeprom_write
		bsf	STATUS,RP0	;バンク1
		movlw	save_data - eep_start
		movwf	EEADR		;EEPROM の記録アドレスを設定

		bcf	STATUS,RP0	;バンク0
		movlw	radio_no	;ラジオ局番号のアドレスを
		movwf	FSR		;間接アドレスに設定
		movlw	2
		movwf	lpcnt		;ループカウンタ

eepwr01		movf	INDF,W		;書き込みデータを読み出し

		bsf	STATUS,RP0	;バンク1
		movwf	EEDATA		;書き込みデータを設定
		bsf	EECON1,WREN	;書き込みイネーブル = ON
		movlw	h'55'
		movwf	EECON2		;h'55' を EECON2 に書き込む
		movlw	h'aa'
		movwf	EECON2		;h'aa' を EECON2 に書き込む
		bsf	EECON1,WR	;書き込み開始
		btfsc	EECON1,WR	;書き込み 終了か?
		goto	$-1		;No

		incf	EEADR,F		;記録アドレスの更新

		bcf	STATUS,RP0	;バンク 0
		incf	FSR,F		;間接アドレスの更新
		decfsz	lpcnt,F		;ループカウンタ - 1 = 0 か？
		goto	eepwr01		;No

		return

;==========================================================================
;		メイン・ルーチン
;==========================================================================

;		選局:	TUNING DOWN スイッチ
;			TUNING UP スイッチ
;		音量:	VOLUME DOWN スイッチ
;			VOLUME UP スイッチ

main
		; 各スイッチの ON／OFF 状態のチェック

		btfss	GPIO,_swTDWN	;TUNING DOWN スイッチ = ON か？
		call	tuning_down	;Yes

		btfss	GPIO,_swTUP	;TUNING UP スイッチ = ON か？
		call	tuning_up	;Yes

		btfss	GPIO,_swVDWN	;VOLUME DOWN スイッチ = ON か？
		call	volume_down	;Yes

		btfss	GPIO,_swVUP	;VOLUME UP スイッチ = ON か？
		call	volume_up	;Yes

		; ラジオ局番号、音量の更新があったとき

		btfss	updt_flg,0	;ラジオ局番号、音量更新フラグ = ON か？
		goto	m01		;No

		call	table_select	;FM ラジオ放送局の選局
		bcf	updt_flg,0	;ラジオ局番号、音量更新フラグ = OFF
		call	wait_30ms	;30m 秒 ウェイト

		bcf	INTCON,T0IE	;タイマー０割り込みを禁止
		movlw	5
		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,T0IE	;タイマー０割り込みを許可

		; 音量表示の終了フラグ、バックアップ要求フラグのチェック

m01		btfss	updt_flg,2	;音量表示の終了フラグ = ON か？
		goto	m02		;No

		movf	work1,W		;表示する周波数のアドレスを復帰
		movwf	addr_high	;
		movf	work2,W		;
		movwf	addr_low	;
		bcf	updt_flg,2	;音量表示の終了フラグ = OFF

		Disp_I2cLcd    h'c0',,0 ;(２行目表示) … 周波数の表示

m02		btfss	updt_flg,1	;バックアップ要求フラグ = ON か？
		goto	main		;No

		call	eeprom_write	;EEPROM へデータ書き込み
		bcf	updt_flg,1	;バックアップ要求フラグ = OFF
		goto	main

;--------------------------------------------------------------------------
;		TUNING DOWN スイッチ = ON の処理
;--------------------------------------------------------------------------

tuning_down
		call	wait_30ms	;30m 秒 ウェイト
		btfsc	GPIO,_swTDWN	;TUNING DOWN スイッチ = ON か？
		goto	tdwn01		;No

		bsf	updt_flg,0	;更新フラグ = ON
		decf	radio_no,F	;Yes. ラジオ局番号 - 1
		incf	radio_no,W	;
		btfss	STATUS,Z	;W reg = 0 か？
		goto	tdwn01		;No

		movf	max_no,W	;
		movwf	radio_no	;ラジオ局番号 = 最大値

tdwn01		btfss	GPIO,_swTDWN	;TUNING DOWN スイッチ = OFF か？
		goto	$ - 1		;No

		return

;--------------------------------------------------------------------------
;		TUNING UP スイッチ = ON の処理
;--------------------------------------------------------------------------

tuning_up
		call	wait_30ms	;30m 秒 ウェイト
		btfsc	GPIO,_swTUP	;TUNING DOWN スイッチ = ON か？
		goto	tup01		;No

		bsf	updt_flg,0	;更新フラグ = ON
		incf	radio_no,F	;Yes. ラジオ局番号 + 1
		movf	radio_no,W
		subwf	max_no,W	;max_no - W(radio_no)
		btfsc	STATUS,C	;radio_no =< max_no か？
		goto	tup01		;Yes

		clrf	radio_no	;ラジオ局番号 = 最小値 = 0

tup01		btfss	GPIO,_swTUP	;TUNING DOWN スイッチ = OFF か？
		goto	$ - 1		;No

		return

;--------------------------------------------------------------------------
;		VOLUME DOWN スイッチ = ON の処理
;--------------------------------------------------------------------------

volume_down
		call	wait_30ms	;30m 秒 ウェイト
		btfsc	GPIO,_swVDWN	;VOLUME DOWN スイッチ = ON か？
		goto	vdwn01		;No

		bsf	updt_flg,0	;更新フラグ = ON
		decf	volume,F	;Yes. 音量 - 1
		incf	volume,W	;
		btfss	STATUS,Z	;W reg = 0 か？
		goto	vdwn01		;No

		clrf	volume		;音量 = 最小値 = 0

vdwn01		btfss	GPIO,_swVDWN	;VOLUME DOWN スイッチ = OFF か？
		goto	$ - 1		;No

		return

;--------------------------------------------------------------------------
;		VOLUME UP スイッチ = ON の処理
;--------------------------------------------------------------------------

volume_up
		call	wait_30ms	;30m 秒 ウェイト
		btfsc	GPIO,_swVUP	;VOLUME UP スイッチ = ON か？
		goto	vup01		;No

		bsf	updt_flg,0	;更新フラグ = ON
		incf	volume,F	;Yes. 音量 + 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	GPIO,_swVUP	;VOLUME UP スイッチ = OFF か？
		goto	$ - 1		;No

		return

;==========================================================================
;		FM ラジオ放送局の選局
;==========================================================================

;	入力:	radio_no: ラジオ局番号

table_select
		; ラジオ局番号から該当のテーブルデータアドレスの計算

		movlw	high fm_radio_tbl ;テーブルの先頭アドレスの high アドレス
		movwf	addr_high	;
		movlw	low fm_radio_tbl  ;テーブルの先頭アドレスの low アドレス
		movwf	addr_low	;

		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
		rlf	work1,W		;x16
		btfsc	STATUS,C	;オーバーフローした か？			***（注）↓      (Ver. 1.03 で 2行追加)
		incf	addr_high,F	;Yes. 読み出すデータの high アドレスの更新	*** 下の例を参照
		addwf	work2,W		;x16 + x4 = x20

		addwf	addr_low,F	;読み出すデータの low アドレスの更新
		btfsc	STATUS,C	;オーバーフローした場合は
		incf	addr_high,F	;読み出すデータの high アドレスの更新

		; LCD へ局名の表示

		Disp_I2cLcd    h'80',,0 ;(１行目表示) … 局名の表示

		movf	addr_high,W	;表示する周波数のアドレスを保存
		movwf	work1		;
		movf	addr_low,W	;
		movwf	work2		;

		; テーブルデータ（RDA_Reg 03h への書き込み用データ）の読み出し

		movlw	9		;ポインタを進める
		addwf	addr_low,F
		btfsc	STATUS,C	;オーバーフローした場合は
		incf	addr_high,F	;読み出すデータの high アドレスの更新
		call	tbl_read	;テーブルデータの読み出し
		movwf	freq_high	;RDA_Reg 03h への書き込み用 high バイト

		incf	addr_low,F	;読み出すデータの low アドレスの更新
		btfsc	STATUS,Z	;オーバーフローした場合は
		incf	addr_high,F	;読み出すデータの high アドレスの更新
		call	tbl_read	;テーブルデータの読み出し
		movwf	freq_low	;RDA_Reg 03h への書き込み用 low バイト

		; LCD へ音量の表示

;			[ Vol=   ]
;			 01234567
		Disp_I2cLcd    h'c0',volume_msg,1 ;(２行目表示) … 音量の表示

		movlw	10
		subwf	volume,W
		btfsc	STATUS,C	;volume >= 10 か？
		goto	tblsel01	;Yes

		movlw	h'c6'		;DDRAM のアドレスをセット
		call	I2cLcd_Cmd	;I2C LCD へコマンドを出力
		movf	volume,W
		goto	tblsel02

tblsel01	movlw	h'c5'		;DDRAM のアドレスをセット
		call	I2cLcd_Cmd	;I2C LCD へコマンドを出力
		movlw	a'1'		;
		call	I2cLcd_Data	;LCD へデータを１文字出力
		movlw	10
		subwf	volume,W

tblsel02	iorlw	h'30'		;ASCII コードに変換
		call	I2cLcd_Data	;LCD へデータを１文字出力

		; （RDA5807M FM Tuner）に周波数等を設定

		call	fm_tuner_set	;（RDA5807M FM Tuner）の設定
		return

;--------------------------------------------------------------------------		***（注）↑      (Ver. 1.03 で追加)

;	12		13		15		16		31		32

;	0000.1100	0000.1101	0000.1111	0001.0000	0001.1111	0010.0000

;x 4	0011.0000	0011.0100	0011.1100	0100.0000	0111.1100	1000.0000
;x16	1100.0000	1101.0000	1111.0000     1.0000.0000     1.1111.0000    10.0000.0000
;	---------     -----------     -----------     -----------    ------------    ------------
;x20	1111.0000     1.0000.0100     1.0010.1100     1.0100.0000    10.0110.1100    1
;							       ( radio_no = ～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 = ～31まで）追加が可能です。

fm_radio_tbl

#define 	nagoya			;┐
;#define 	tokyo			;│どれか１つを選択
;#define 	osaka			;┘

;		                                                         00: SPACE = 100 kHz
;			 局名           周波数       ┌─  CHAN ──┐  ┌┐	;ラジオ局番号
#ifdef nagoya
		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
#endif
;			                                              └┘
;			                                               10: BAND = 76-108 MHz

#ifdef tokyo
		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
#endif

#ifdef osaka
		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
#endif

fm_radio_tbl_end

;--------------------------------------------------------------------------
;		（RDA5807M FM Tuner）の設定
;--------------------------------------------------------------------------

fm_tuner_set
		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,7=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 バイト送信

		movf	freq_high,W	;RDA_Reg 03h、15:8=CHAN[9:2]
		call	byte_send	;1 バイト送信      ---------
		movf	freq_low,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 バイト送信     ---------

		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	;ストップコンディション
		return

;==========================================================================
;		ウェイト・ルーチン (8 MHz)
;==========================================================================

		; 100μ秒 ウェイトルーチン
wait_100us
		movlw	39		;1
		movwf	wt1cnt		;1
wu01		goto	$+1		;2
		decfsz	wt1cnt,F	;1,2
		goto	wu01		;2,0	1+1+(2+1+2)*38+(2+2+0)=196
;		goto	$+1		;2
		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
		; レジスタの保存

		movwf	w_save		;W レジスタの保存
		movf	STATUS,W
		movwf	s_save		;STATUS レジスタの保存

		bcf	STATUS,RP0	;バンク 0
		btfss	INTCON,T0IF	;タイマー０割り込み か？
		goto	int_end		;No. (Illegal interrupt)

		; タイマー０割り込み処理（ 100μ秒毎に割り込み ）

int_t0		bcf	INTCON,T0IF	;タイマー０割込みフラグをクリア

		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

		movlw	high tm0_s_val	;ソフトタイマー０の high カウント値
		movwf	tm0_cnt_h	;ソフトタイマー０の high カウンタの設定
		movlw	low tm0_s_val	;ソフトタイマー０の low  カウント値
		movwf	tm0_cnt_l	;ソフトタイマー０の low  カウンタの設定

		movlw	5		;１秒経過の監視
		subwf	sec_cnt,W	;
		btfsc	STATUS,Z	;秒カウンタ = 5 か？
		bsf	updt_flg,2	;Yes. 音量表示の終了フラグ = ON

		decfsz	sec_cnt,F	;秒カウンタ - 1 = 0 か？
		goto	int_end		;No

		bsf	updt_flg,1	;バックアップ要求フラグ = ON
		bcf	INTCON,T0IE	;タイマー０割り込みを禁止

		; レジスタの復帰

int_end		movf	s_save,W
		movwf	STATUS		;STATUS レジスタの復帰
		swapf	w_save,F
		swapf	w_save,W	;W レジスタの復帰

		retfie

;==========================================================================
;		EEPROM エリア
;==========================================================================

		org	h'2100'

eep_start	de	"Copyright (C) 2020 by M.Yamamoto"

		de	h'ff'

		; セーブ情報（ラジオ局番号、音量）の格納位置

save_data				;以下の設定値は初回の起動時のみ

		de	3		;ラジオ局番号（default=3:NHK FM） … （ 名古屋地区、大阪地区 ）
		de	h'01'		;音量

;==========================================================================

		end			;プログラムの終了
