;
; 学習リモコンソースプログラム Ver1.0
;
; for PIC16F84@10MHz
;
; その他    赤外線受光モジュール:CRVP1738
;           EEPROM:24LC64
;
; またまた例のごとく本ソースは秋月のPA.EXE拡張インストラクションを使用して
; おりますのであしからず。  2000.8 (c) RUU
;
; 注意
; 本リモコンで記録・送信できる赤外線リモコンの種類は、リモコンの家電標準方式を採用
; している機種とSONYの一部機器です。実際に動作を確認したコマンドは、松下製TV・照明
; ・VTR・CATVターミナルの一部、およびaiwa製TVの一部、SONY製VTRの一部、パイオニア製
; ミニコンポ・ＬＤの一部のみです。また、上記の機器でも正しく学習や操作できないコマ
; ンドが存在する場合があります。
; これは私の調査・解析不足(あるいはバグ・・・)によるところですが、本プログラムおよ
; びハードウェアの使用によって損害が発生したとしても私は一切の責任を負わないものと
; します。
;

            .include    16F84.h
            .osc        hs
            .wdt        off
            .pwrt       on
            .protect    off

trisa       equ 5
trisb       equ 6

SCLIO       equ trisa.2         ; I2C SCL I/O bit = trisa.2
SDAIO       equ trisa.3         ; I2C SDA I/O bit = trisa.3

IR_OUT      equ     ra.0        ; 赤外線出力ピン
PWR         equ     ra.1        ; 周辺モジュール用電源
SCL         equ     ra.2        ; 24LC64 SCL
SDA         equ     ra.3        ; 24LC64 SDA
IR_IN       equ     ra.4        ; 赤外線入力(CRVP1738)
L_STATUS    equ     rb.3        ; ステータス表示LED


SAVE_ADR    equ     0ch         ; SAVE/LOADメモリアドレス
SAVE_LEN    equ     25          ; LOAD/SAVEバイト数
BUF_ADR     equ     15h         ; バッファアドレス
BUF_LEN     equ     16          ; バッファ長
D_HI        equ     4           ; 'Hi'信号時間許容差
TM0MAX      equ     193         ; タイマしきい値(タイムアウト=5秒)


            org     0ch

;; データメモリ(SAVE/LOAD対象分)
modeflg     ds  1               ; ビット転送方式.0/リザルトコード.7/status.6 他
bit_len     ds  1               ; データビット総数
start_hi    ds  1               ; 以下、リモコンデータ各Hi/Lo長カウンタ
start_lo    ds  1
min_hi      ds  1
max_hi      ds  1
min_lo      ds  1
max_lo      ds  1
stop_hi     ds  1
data_buf    ds  16              ; リモコンデータ本体    (adr=15hから16bytes)

;; データメモリ(その他)
cnt         ds  1               ; カウンタ(汎用)
timecnt     ds  1               ; 信号長カウンタ
work_hi     ds  1               ; カウント待避用/ワーク(汎用)
work_lo     ds  1               ; 〃
threshold   ds  1               ; 1/0判定用しきい値
param       ds  1               ; サブルーチンインタフェース
work        ds  1               ; いろいろつかう
work2       ds  1               ; 〃
bit_cnt     ds  1               ; リモコンデータbitカウンタ
hi_1        ds  1               ; 以下、ビット送信インタフェース
hi_0        ds  1
lo_1        ds  1
lo_0        ds  1
hi_time     ds  1
lo_time     ds  1

;; 24LC64アクセス用
byte_buf    ds  1               ; read/write用バッファ
cnt_bit     ds  1               ; 送受ビットカウンタ
rw_dat      ds  1               ; 送受信バイトデータ
i2c_stat    ds  1               ; status .. bit0 = acknowledge (0:ACK 1:NAK)
adr_h       ds  1               ; Address High byte
adr_l       ds  1               ; Address Low byte
adrmax      ds  1               ; メモリ最大adr上位1バイト(1Fh=24xx64, 7Fh=24xx256)
ctrl_byte   ds  1               ; control code + chip select + r/w


keycode     ds  1               ; キー押下発生コード
crnt_page   ds  1               ; メモリ現在ページ

tm0cnt      ds  1               ; タイマ割り込み回数

wsave       ds  1               ; Wレジスタ待避
stsave      ds  1               ; STATUS待避

            org     0
            goto    main
            org     4
            goto    intproc

;;;;;;;;;;;; main ;;;;;;;;;;;;
main
            ;; 初期化
            clr     crnt_page                   ; カレントページクリア
            mov     adrmax,#1Fh                 ; タイプ:24LC64
            mov     ctrl_byte,#10100000b        ; コントロールバイト

init
            ;; I/Oポート設定
            mov     !ra,#00011000b              ; IR_IN,SDAをINに、IR_OUT,PWR,SCLをOUTに
            mov     !rb,#11110000b              ; KeyXを入力,KeyYを出力に
            clr     ra
            clr     rb

            ;; TMR0設定
            setb    rp0
            mov     option,#00000111b           ; ポートBプルアップ、プリスケーラ=1:256
            clrb    rp0

            ;; 割り込み設定
            mov     intcon,#00001000b           ; ポートB割り込み許可,タイマ0およびグローバル割り込み禁止
            clr     tmr0                        ; タイマ0割り込み=約26ms周期

            ;; スリープ
            mov     modeflg,#01000000b          ; スリープフラグON,エラーフラグクリア
            clrb    PWR                         ; メモリ,受信モジュール パワーOFF
            sleep                               ; システムクロック停止、省電力モードで待機
            nop

            ;; wakeup (キー入力でプログラム再開)
            mov     intcon,#10100000b           ; グローバル割り込み,タイマ0割り込み許可,ポートB割り込み禁止
            setb    PWR                         ; メモリ,受信モジュール パワーON
            mov     cnt,#200                    ; 周辺回路安定待ち
            call    wait100
tm_rst_loop
            clr     tm0cnt

mainloop
            cje     tm0cnt,#TM0MAX,init         ; タイムオーバー
            call    get_keycode                 ; キーコード取得
            call    disp_page                   ; ページLED点灯
            cje     keycode,#0FFh,mainloop      ; キー入力なし then goto mainloop

            ;; キー処理
            cjne    keycode,#0F0h,key_page
            ;; メモリクリアJP-ON (rb6=rb7=GND ; EEPROMクリア処理)
            mov     rb,#00001000b               ; STATUS LEDだけON
            call    ee_clr
            call    ready_status
            goto    init
key_page
            cjne    keycode,#0Fh,key_e
            ;; #15 (ページキー)
            jb      modeflg.6,un_slp            ; 再開直後?
            call    pageup                      ; else ページインクリメント
            goto    tm_rst_loop
un_slp
            clr     modeflg
            call    wait05s
            goto    tm_rst_loop
key_e
            clrb    modeflg.6
            cjne    keycode,#0Eh,key_send
            ;; #14 (メモリキー)
            call    buf_clr
            call    ready_status                ; ready 表示
            call    analyze                     ; リモコンデータ解析
            jb      modeflg.7,error             ; エラー表示
            call    set_timing                  ; パラメータ設定
            call    ready_status                ; ready 表示
            jb      modeflg.0,get_s_type
get_n_type
            call    get_data_n                  ; 標準モードデータ取得
            goto    get_n_s_e
get_s_type
            call    get_data_s                  ; SONYモードデータ取得
get_n_s_e
            jb      modeflg.7,error             ; エラー表示
            call    ready_status                ; ready 表示
            clr     tm0cnt
;           setb    gie
key_loop
            cje     tm0cnt,#TM0MAX,init         ; タイムオーバー
            call    get_keycode                 ; キーコード取得
            call    disp_page                   ; ページLED点灯
            cjae    keycode,#0F0h,key_loop      ; キー入力なし then goto key_loop
            clr     tm0cnt
            cje     keycode,#0Eh,init           ; メモリキーの場合sleep
            cjne    keycode,#0Fh,key_save
            call    pageup                      ; ページキー
            goto    key_loop
key_save
            clrb    gie
            call    save_data                   ; key#0～13 --> データセーブ
            call    ready_status                ; ready 表示
            setb    gie
            goto    tm_rst_loop

key_send
            clrb    gie
            ;; #0-13 (送信キー)
            call    load_data                   ; key1-13 --> データロード
            cje     bit_len,#0,error            ; データなし?
            call    send_data                   ; データ送信
            jnb     modeflg.0,send_nxt
            mov     cnt,#200                    ; SONYモードは2回送信
            call    wait100
            call    send_data
send_nxt
            mov     cnt,#200                    ; 適当なインターバル
            call    wait100
            mov     cnt,#200
            call    wait100
            setb    gie
            goto    tm_rst_loop

error                                           ; エラー
            call    err_status
            goto    init


;;;;;;;;;;;; save_data ;;;;;;;;;;;;;;;;
save_data
            call    set_mem_addr                ; SAVE_ADRからSAVE_LENバイトのレジスタ内容を
            mov     fsr,#SAVE_ADR               ; EEPROMの指定アドレス以降に書き込む
            mov     cnt,#SAVE_LEN
save_loop
            mov     byte_buf,indirect
            call    byte_write
            call    adr_inc
            inc     fsr
            djnz    cnt,save_loop
            ret

;;;;;;;;;;;; load_data ;;;;;;;;;;;;;;;;
load_data
            call    set_mem_addr                ; EEPROMの指定アドレスからSAVE_LENバイトの内容
            mov     fsr,#SAVE_ADR               ; をSAVE_ADR以降のレジスタにロードする
            mov     cnt,#SAVE_LEN
load_loop
            call    random_read
            mov     indirect,byte_buf
            call    adr_inc
            inc     fsr
            djnz    cnt,load_loop
            ret


;;;;;;;;;;;; set_mem_addr ;;;;;;;;;;;;;;;;
set_mem_addr                                    ; PAGEとキー番号をEEPROMアドレスにマッピングする
            mov     adr_h,crnt_page
            rl      adr_h
            and     adr_h,#0FEh                 ; adr_h <- page * 2
            mov     adr_l,keycode
            swap    adr_l
            rl      adr_l
            and     adr_l,#0E0h                 ; adr_l <- keycode<<5
            cjbe    keycode,#7,set_mem_end
            inc     adr_h
set_mem_end
            ret

;;;;;;;;;;;; ee_clr ;;;;;;;;;;;;;;;;
ee_clr                                          ; メモリをヌルクリアする
            clr     adr_h
            clr     adr_l
            clr     byte_buf
ee_loop
            call    byte_write
            call    adr_inc
            cjne    adr_l,#0,ee_loop
            cjne    adr_h,#0,ee_loop
            ret


;;;;;;;;;;;; ready_status ;;;;;;;;;;;;;;;;
ready_status                                    ; Ready = 短く2回点滅
            mov     timecnt,#2
turn_loop1
            clrb    L_STATUS
            mov     work2,#15
            call    loop05s
            setb    L_STATUS
            mov     work2,#15
            call    loop05s
            decsz   timecnt
            goto    turn_loop1
            ret

;;;;;;;;;;;; err_status ;;;;;;;;;;;;;;;;
err_status                                      ; Error = ゆっくり3回点滅
            mov     timecnt,#3
turn_loop2
            clrb    L_STATUS
            call    wait05s
            setb    L_STATUS
            call    wait05s
            decsz   timecnt
            goto    turn_loop2
            ret

;;;;;;;;;;; pageup ;;;;;;;;;;;;;;;
pageup
            inc     crnt_page
            cjne    crnt_page,#7,end_pageup
            clr     crnt_page
end_pageup
            call    disp_page
            call    wait05s
            ret

;;;;;;;;;;;; intproc ;;;;;;;;;;;;;;
intproc                                         ; 割り込み処理
            mov     wsave,w                     ; Wレジスタ待避
            mov     stsave,status               ; STATUS待避
            jnb     t0if,int_end
            cje     tm0cnt,#TM0MAX,int_end      ; tm0割り込み毎にtm0cntカウントアップ
            inc     tm0cnt                      ; 最大193(=5sec)
int_end
            clrb    t0if                        ; 割り込み要因クリア
            clrb    rbif
            clr     tmr0                        ; tmr0リセット
            mov     status,stsave               ; STATUS戻す
            mov     w,wsave                     ; W戻す
            retfie                              ; 復帰

;;;;;;;;;;;; disp_page ;;;;;;;;;;;;;;
disp_page
            mov     work,crnt_page
            inc     work
            setb    work.3
            mov     rb,work
            ret

;;;;;; get_keycode  押下キー番号を得る(keycode=0xFFは押下なし,=0xF0はメモリリセット) ;;;;;;;;;
get_keycode                                 ; キーはrb.0～3とrb.4～7のマトリクススイッチ
            clr     rb
            mov     keycode,rb
            swap    keycode
            not     keycode
            and     keycode,#0Fh
            cje     keycode,#0,key_void     ; key入力なし
            mov     !rb,#00001111b          ; KeyYを入力,KeyXを出力に
            nop                             ; KeyY入力確定までwait
            nop
            nop
            mov     work,rb
            mov     !rb,#11110000b          ; KeyXを入力,KeyYを出力に
            not     work
            and     work,#0Fh
            cje     work,#0,key_void

            cje     keycode,#1,key0
            cje     keycode,#2,key4
            cje     keycode,#4,key8
            mov     keycode,#0ch
            goto    keynxt
key0
            clr     keycode
            goto    keynxt
key4
            mov     keycode,#4h
            goto    keynxt
key8
            mov     keycode,#8h
keynxt
            cje     work,#1,key_end
            cje     work,#2,key_1
            cje     work,#4,key_2
            add     keycode,#3
            goto    key_end
key_1
            add     keycode,#1
            goto    key_end
key_2
            add     keycode,#2
            goto    key_end
key_void
            cje     keycode,#0ch,key_rst
            mov     keycode,#0FFh           ; いずれのキーも押されていない場合
            goto    key_end
key_rst
            mov     keycode,#0F0h           ; rb6と7同時にGND接続(リセットJP)の場合
key_end
            ret

;;;;;;;;;;;; set_timing ;;;;;;;;;;;;
set_timing
            mov     param,start_hi              ; カウント値を40/38kキャリア用に変換
            call    set_sub
            mov     start_hi,param
            mov     param,start_lo
            call    set_sub
            mov     start_lo,param
            mov     param,max_hi
            call    set_sub
            mov     max_hi,param
            mov     param,max_lo
            call    set_sub
            mov     max_lo,param
            mov     param,min_hi
            call    set_sub
            mov     min_hi,param
            mov     param,min_lo
            call    set_sub
            mov     min_lo,param
            mov     param,stop_hi
            call    set_sub
            mov     stop_hi,param
            ret

;;;;;;;;;;;; set_sub ;;;;;;;;;;;;
set_sub
            jnb     modeflg.0,set_38
            call    set40k
            goto    set_sub_end
set_38
            call    set38k
set_sub_end
            ret

;;;;;;;;;;;; set40k ;;;;;;;;;;;;
set40k                                          ; 40kHzキャリアのリモコンでは
            mov     cnt,param                   ; 送信のためにカウント値を1.053倍にする
cv40loop
            cjb     cnt,#20,set40end
            inc     param
            sub     cnt,#20
            goto    cv40loop
set40end
            ret

;;;;;;;;;;;; set38k ;;;;;;;;;;;;
set38k                                          ; 元々38kHz用にカウントしているので
            nop                                 ; 変換の必要なし
            ret



;;;;;;;;;;;; buf_clr ;;;;;;;;;;;;
buf_clr                                         ; データバッファをクリアする
            mov     cnt,#BUF_LEN
            mov     fsr,#BUF_ADR
b_clr_loop
            clr     indirect
            inc     fsr
            djnz    cnt,b_clr_loop
            ret

;;;;;;;;;;;; analyze ;;;;;;;;;;;;
analyze                                         ; 赤外線受信データのフォーマット解析を行う(Pass1)
            mov     min_hi,#255
            mov     min_lo,#255
            clr     max_hi
            clr     max_lo
            clr     modeflg
            clr     tm0cnt
;           clrb    gie                         ; 以降、データ受信完了まで割り込み禁止

wait_ir
            cje     tm0cnt,#TM0MAX,cnt_err      ; タイムオーバー
            jb      IR_IN,wait_ir               ; 赤外線入力あり?
            clrb    gie                         ; 以降、データ受信完了まで割り込み禁止
            call    count_hi                    ; start hiまたは最初のHi信号長
            cje     timecnt,#0ffh,cnt_err
            mov     start_hi,timecnt
            call    count_lo                    ; start loまたは最初のlo信号長
            cje     timecnt,#0ffh,cnt_err
            mov     start_lo,timecnt

check_hi
            call    count_hi                    ; dataのHiレベル信号長
            cje     timecnt,#0ffh,cnt_err
            mov     work_hi,timecnt
check_lo
            call    count_lo                    ; dataのLoレベル信号長
            cje     timecnt,#0ffh,cnt_end
            mov     work_lo,timecnt

cmp_hi                                          ; High最大/最小を取得
            cja     work_hi,max_hi,set_max_hi
            cja     min_hi,work_hi,set_min_hi
            goto    cmp_lo
set_max_hi
            mov     max_hi,work_hi
            goto    cmp_lo
set_min_hi
            mov     min_hi,work_hi
cmp_lo                                          ; Low最大/最小を取得
            cja     work_lo,max_lo,set_max_lo
            cja     min_lo,work_lo,set_min_lo
            goto    check_hi
set_max_lo
            mov     max_lo,work_lo
            goto    check_hi
set_min_lo
            mov     min_lo,work_lo
            goto    check_hi

cnt_end
            mov     stop_hi,work_hi             ; stop hiまたは最後のhi信号長


            mov     threshold,max_hi            ; データフォーマットの判定
            sub     threshold,min_hi
            cjb     threshold,#D_HI,mode_std    ; Hi(max,min)時間にD_HI以上の差があれば
            setb    modeflg.0                   ; SONY方式とみなす
            rr      threshold                   ; threshold <- 1or0判定の'Hi'しきい値
            and     threshold,#7Fh
            add     threshold,min_hi
            goto    analyze_end
mode_std
            clrb    modeflg.0                   ; 標準方式
            mov     threshold,max_lo            ; threshold <- 1or0判定の'Lo'しきい値
            sub     threshold,min_lo
            rr      threshold
            and     threshold,#7Fh
            add     threshold,min_lo
            goto    analyze_end

cnt_err
            setb    modeflg.7                   ; エラーコード
analyze_end
            setb    gie                         ; 割り込み可
            ret

;;;;;;;;;;;; get_data_n ;;;;;;;;;;;;
get_data_n                                      ; 赤外線リモコンデータの取得(Pass2/Normal)
            clr     tm0cnt
n_get_data_l
            cje     tm0cnt,#TM0MAX,n_get_err        ; タイムオーバー
            jb      IR_IN,n_get_data_l          ; startbit読み飛ばし
            clrb    gie                         ; 以降、データ受信完了まで割り込み禁止
            clr     bit_len                     ; bit_len = データbit総数 (クリアする)
            clr     work
            mov     bit_cnt,#8
            mov     fsr,#BUF_ADR
            call    count_hi
            call    count_lo

n_get_loop
            call    count_hi
            call    count_lo                    ; 標準:Lo長さで0/1決定
            cje     timecnt,#0ffh,n_recv_end    ; 標準:受信終了?

            rl      work
            cjb     timecnt,threshold,n_bit_0
n_bit_1
            setb    work.0
            goto    n_get01
n_bit_0
            clrb    work.0
n_get01
            dec     bit_cnt
            inc     bit_len
            cja     bit_len,#BUF_LEN*8,n_get_err    ; データバッファオーバーフロー
            cjne    bit_cnt,#0,n_get_loop
            mov     bit_cnt,#8
            mov     indirect,work               ; 8bit(1byte)毎にバッファへ書き込み
            inc     fsr
            clr     work
            goto    n_get_loop

n_recv_end
            cje     bit_cnt,#8,n_get_end        ; バイト単位なら終了
n_recv_sft
            rl      work
            clrb    work.0
            djnz    bit_cnt,n_recv_sft
            mov     indirect,work
            goto    n_get_end
n_get_err
            setb    modeflg.7
n_get_end
            setb    gie                         ; 割り込み可
            ret

;;;;;;;;;;;; get_data_s ;;;;;;;;;;;;
get_data_s                                      ; 赤外線リモコンデータの取得(Pass2/SONY)
            clr     tm0cnt
s_get_data_l
            cje     tm0cnt,#TM0MAX,s_get_err        ; タイムオーバー
            jb      IR_IN,s_get_data_l          ; startbit読み飛ばし
            clrb    gie                         ; 以降、データ受信完了まで割り込み禁止
            clr     bit_len                     ; bit_len = データbit総数 (クリアする)
            clr     work
            mov     bit_cnt,#8
            mov     fsr,#BUF_ADR
            call    count_hi
            call    count_lo

s_get_loop
            call    count_hi                    ; SONY:Hi長さで0/1決定
            rl      work
            cjb     timecnt,threshold,s_bit_0
s_bit_1
            setb    work.0
            goto    s_get01
s_bit_0
            clrb    work.0
s_get01
            dec     bit_cnt
            inc     bit_len
            cja     bit_len,#BUF_LEN*8,s_get_err    ; データバッファオーバーフロー
            cjne    bit_cnt,#0,s_get02
            mov     bit_cnt,#8
            mov     indirect,work               ; 8bit(1byte)毎にバッファへ書き込み
            inc     fsr
            clr     work
s_get02
            call    count_lo
            cje     timecnt,#0ffh,s_recv_end    ; SONY:受信終了
            goto    s_get_loop
s_recv_end
            cje     bit_cnt,#8,s_get_end        ; バイト単位なら終了
s_recv_sft
            rl      work
            clrb    work.0
            djnz    bit_cnt,s_recv_sft
            mov     indirect,work
            goto    s_get_end
s_get_err
            setb    modeflg.7
s_get_end
            setb    gie                         ; 割り込み可
            ret


;;;;;;;;;;;; send_data ;;;;;;;;;;;;
send_data                                       ; データバッファ内容を赤外線LEDで送信する
            mov     work2,bit_len
            mov     fsr,#BUF_ADR
            mov     bit_cnt,#8
            mov     work,indirect

            jnb     modeflg.0,set_normal
            mov     hi_1,max_hi
            mov     hi_0,min_hi
            mov     lo_1,max_lo
            mov     lo_0,max_lo
            goto    send_main
set_normal
;           mov     hi_1,max_hi
;           mov     hi_0,max_hi
            mov     hi_1,min_hi         ; こっち(min)の方がよさそう
            mov     hi_0,min_hi
            mov     lo_1,max_lo
            mov     lo_0,min_lo
            add     lo_0,#2             ; ちょっと補正
send_main
            mov     hi_time,start_hi
            mov     lo_time,start_lo
            call    send_bit

send_loop
            jb      work.7,send_1
send_0
            mov     hi_time,hi_0
            mov     lo_time,lo_0
            call    send_bit
            goto    byte_e_chk
send_1
            mov     hi_time,hi_1
            mov     lo_time,lo_1
            call    send_bit
byte_e_chk
            djnz    bit_cnt,send_sft
            inc     fsr
            mov     work,indirect
            mov     bit_cnt,#8
            goto    send_e_chk
send_sft
            rl      work
send_e_chk
            djnz    work2,send_loop
            jb      modeflg.0,send_end          ; SONY:stopbitなし

            mov     hi_time,stop_hi
            mov     lo_time,stop_hi
            call    send_bit
send_end
            ret

;;;;;;;;;;;; send_bit ;;;;;;;;;;;;
send_bit                                        ; 1bit分のデータを送信する
send_b_hi                                       ; 'Hi'信号
            setb    IR_OUT
            call    wait_carrier
            clrb    IR_OUT
            call    wait_carrier
            setb    IR_OUT
            call    wait_carrier
            clrb    IR_OUT
            call    wait_carrier
            djnz    hi_time,send_b_hi
send_b_lo                                       ; 'Lo'
            call    wait_carrier
            call    wait_carrier
            call    wait_carrier
            call    wait_carrier
            nop
            nop
            nop
            nop
            djnz    lo_time,send_b_lo
            ret

;;;;;;;;;;;; wait_carrier ;;;;;;;;;;;;
wait_carrier                                    ; 赤外線キャリアの半サイクル時間を消費
        btfsc   modeflg.0
        goto    c40k
        mov     cnt,#7
        nop
        nop
        goto    loop_carr
c40k
        mov     cnt,#7
        nop
loop_carr
        decsz   cnt
        goto    loop_carr
        ret


;;;;;;;;;;;; count_hi ;;;;;;;;;;;;
count_hi                                        ; 'Hi'信号時間をカウントする
            clr     timecnt
h_cnt_loop
            jb      IR_IN,h_cnt_end
            call    wait526                     ; 2cycle(@38kHz)wait
            inc     timecnt
            cjne    timecnt,#0ffh,h_cnt_loop    ; 最大255(=13.4ms)
h_cnt_end
            ret

;;;;;;;;;;;; count_lo ;;;;;;;;;;;;
count_lo                                        ; 'Lo'信号時間をカウントする
            clr     timecnt
l_cnt_loop
            jnb     IR_IN,l_cnt_end
            call    wait526                     ; 2cycle(@38kHz)wait
            inc     timecnt
            cjne    timecnt,#0ffh,l_cnt_loop    ; 最大255(=13.4ms)
l_cnt_end
            ret


;;;;;;;;;;;; wait526 ;;;;;;;;;;;;
wait526                                         ; 52.6μsを消費(38kHzの2cycle分)
            mov     cnt,#42
loop526
            decsz   cnt
            goto    loop526
            ret

;;;;;;;;;;;; wait100;;;;;;;;;;;;
wait100                                         ; 100μs * cnt 時間を消費
loop_n
            mov     work,#81
loop100
            decsz   work
            goto    loop100
            decsz   cnt
            goto    loop_n
            ret


;;;;;;;;;;;; wait05s ;;;;;;;;;;;
wait05s                                         ; 0.5秒くらい消費
            mov     work2,#50
loop05s
            mov     cnt,#100
            call    wait100
            decsz   work2
            goto    loop05s
            ret

; i2c_start
; Start Conditionを出力する

i2c_start
        setb    rp0                 ; Bank1
        clrb    SDAIO               ; SDAを出力にする
        clrb    rp0                 ; Bank0

        clrb    SCL                 ; SCL=L
        nop                         ; setup time wait
        nop
        setb    SDA                 ; SDA=H
        nop
        nop
        setb    SCL                 ; SCL=H
        nop                         ; (TSU:STA)
        nop
        clrb    SDA                 ; SCL=HでSDAがH->L : start condition
        nop                         ; (THD:STA)
        nop
        clrb    SCL                 ; SCL=L

        setb    rp0                 ; Bank1
        setb    SDAIO               ; SDAを開放(入力)にする
        clrb    rp0                 ; Bank0
        ret


; i2c_stop
; Stop Conditionを出力する

i2c_stop
        setb    rp0                 ; Bank1
        clrb    SDAIO               ; SDAを出力にする
        clrb    rp0                 ; Bank0

        clrb    SCL                 ; SCL=L
        nop                         ; setup time wait
        nop
        clrb    SDA                 ; SDA=L
        nop
        nop
        setb    SCL                 ; SCL=H
        nop                         ; (TSU:STO)
        nop
        setb    SDA                 ; SCL=HでSDAがL->H : stop condition
        nop
        nop
        clrb    SCL                 ; SCL=L

        setb    rp0                 ; Bank1
        setb    SDAIO               ; SDAを開放(入力)にする
        clrb    rp0                 ; Bank0
        ret


; i2c_write (マスタ・トランスミッタ)
; 1バイトのデータを出力する
; 注）送信データrw_datはi2c_writeのcallで破壊される
; 結果(ACK/NAK)はi2c_stat.0に格納される

i2c_write
        setb    rp0                 ; Bank1
        clrb    SDAIO               ; SDAを出力にする
        clrb    rp0                 ; Bank0
        mov     cnt_bit,#8              ; カウンタセット
send_1bit
        jb      rw_dat.7,bit_wh     ; dataの7bit目から送信
        clrb    SDA                 ; bit=0
        goto    send_one
bit_wh
        setb    SDA                 ; bit=1
send_one
        nop                         ; TSU:DAT
        setb    SCL                 ; SCL=H
        nop                         ; (THIGH > 600ns)
        rl      rw_dat              ; bitシフト
        clrb    SCL                 ; SCL=L
        djnz    cnt_bit,send_1bit   ; 7-0bit繰り返し
        ;; -- ACK --
        setb    rp0                 ; Bank1
        setb    SDAIO               ; SDAを開放(入力)にする
        clrb    rp0                 ; Bank0
        setb    SCL                 ; SCL=H
        nop
        jnb     SDA,w_ack
        setb    i2c_stat.0          ; NO ACK
        goto    write_end
w_ack
        clrb    i2c_stat.0          ; ACK
write_end
        clrb    SCL
        ret

; i2c_read (マスタ・レシーバ)
; 1バイトのデータを読み込む
; 読み込み後にレシーバから送信するACK/NAKはi2c_stat.0で指定する
; 受信データはrw_datに格納される
i2c_read
        mov     cnt_bit,#8              ; カウンタセット
read_bit
        setb    SCL                 ; SCL=H
        nop
        jb      SDA,bit_rh          ; 1bit受信
        clrb    rw_dat.0            ; bit=0
        goto    scl_rl
bit_rh
        setb    rw_dat.0            ; bit=1
scl_rl
        clrb    SCL                 ; SCL=L
        decsz   cnt_bit
        goto    read_nxt            ; 7-0bit繰り返し
        goto    read_end
read_nxt
        rl      rw_dat                  ; bitシフト
        goto    read_bit
read_end
        ;; -- ACK / NO ACK --
        jnb     i2c_stat.0,r_ack
        setb    SDA                 ; NO ACK
        goto    send_ack
r_ack
        clrb    SDA                 ; ACK
send_ack
        setb    rp0                 ; Bank1
        clrb    SDAIO               ; SDAを出力にする
        clrb    rp0                 ; Bank0

        setb    SCL                 ; SCL=H
        nop                         ; (THIGH)
        nop
        clrb    SCL
        setb    rp0                 ; Bank1
        setb    SDAIO               ; SDAを開放(入力)にする
        clrb    rp0                 ; Bank0
        ret


;; ******** 24LCxxアクセスサブルーチン ********


; send_ctrl
; START および Control Byte 送信 + ACK待ち

send_ctrl
        call    i2c_start
        mov     rw_dat,ctrl_byte
        call    i2c_write
        jb      i2c_stat.0,send_ctrl    ; if NO ACK then retry
        ret

; adr_inc
; アドレスインクリメント

adr_inc
        cje     adr_l,#0FFh,inc_hi
        inc     adr_l
        goto    inc_end
inc_hi
        clr     adr_l
        cje     adr_h,adrmax,inc_roll
        inc     adr_h
        goto    inc_end
inc_roll
        clr     adr_h
inc_end
        ret

; byte_write
; 1バイトをメモリに書き込む (I:ctrl_byte,adr_h,adr_l,byte_buf)

byte_write
        clrb    ctrl_byte.0         ; r/w = 0 (write)
        call    send_ctrl           ; START and control byte
        mov     rw_dat,adr_h        ; address high
        call    i2c_write
        mov     rw_dat,adr_l        ; address low
        call    i2c_write
        mov     rw_dat,byte_buf     ; data
        call    i2c_write
        call    i2c_stop
        ret

; random_read
; 1バイトをメモリから読み出す(I:ctrl_byte,adr_h,adr_l  O:byte_buf)

random_read
        clrb    ctrl_byte.0         ; r/w = 0 (write)
        call    send_ctrl           ; START and control byte
        mov     rw_dat,adr_h        ; address high
        call    i2c_write
        mov     rw_dat,adr_l        ; address low
        call    i2c_write
        setb    ctrl_byte.0         ; r/w = 1 (read)
        call    send_ctrl           ; START and control byte
        setb    i2c_stat.0          ; NAK
        call    i2c_read            ; get byte and send NAK
        mov     byte_buf,rw_dat     ; get data
        call    i2c_stop
        ret

;;;;;;;;;;;;;;;;;;; おしまい ;;;;;;;;;;;;;;;;;;;;;;

