勝手な電子工作・・

勝手なオリジナル電子工作に関する記事を書きます

PICアセンブラーで芸当 (?) その3) 超簡素なRTCライター

手品ではなく役立つRTC(リアルタイム・クロック)ライターを作ります。アセンブラーでこそできる離れ業、、と作者本人も思います^^ 

f:id:a-tomi:20190908220554j:plain

RTC、表示器、ボタン3個を僅か8ピンのPICで処理し、プログラム・メモリー、データ・メモリーともに4分の1ほどしか使いません。
日曜の今日は台風が来そうなので出歩かず、午後から半日かけてこれをじっくり作ってみました。全く同じ機能のものをArduinoでも作りましたので、両方のプログラムを後ろの方につけておきます。

前回のプログラムの中身の説明の予定でしたが、今回は追加のアプリケーションです。説明にはむしろこちらのほうが分かり易いかもしれません。説明は次回以降ににするとして、ここでは簡単に紹介します。

まず回路です。いつものとおり、汚い手書きですみませんが。

f:id:a-tomi:20190908220946j:plain

I2C接続とTM1637ではクロックは問題なくピン共有します。前回説明のとおりで、これなら全く問題ありませんね。

RTC(リアルタイム・クロック)は、非常に正確なDS3231のブレークアウトモジュールを使います。1年で数秒しか狂いません。筆者の作ったものでは4年間で僅か5秒しか狂わないのに驚いています。5年ほど前にネットで1個60円以下でたくさん買った部品です。しかもCR2032電池付きで。

f:id:a-tomi:20190908221619j:plain

f:id:a-tomi:20190908221639j:plain

この場合、I2Cアドレスはジャンパーで7種類変更できるようになっていますね。現在ではこの4分の1程度に小型化し小型電池を使うRTCモジュールのほうが多くでまわっています。確かに大きな容量のCR2032電池を使う必要はないほど省エネです。これからLEDをカットすればなおさら。小さい方は結構使いにくいのですが、値段は150円位します。

下の方に掲げるプログラムをご覧いただくと分かるのですが、普段は時計表示をしており、ボタンを操作するとRTCへカレンダーや時刻が書き込めるようにしてあります。

f:id:a-tomi:20190908222902j:plain

ブレッドボードの余計な配線は、ロジックアナライザーがセットしやすいようにしたものですが、今回はほぼ一発で動き、とくに動員の必要がありませんでした。

I2Cデバイスプロトコルがデータシートだけではわかりにくいときは、同じものをArduinoで作ってロジアナでプロトコルを確認しながら作れば、なんでも間違いなく作れるわけです。

次の写真は、これと同じ機能と操作をArduino-Uno互換機でこれといっしょに試作したもの。こちらのプログラムはさすがにすぐできます。このスケッチを下のほうにつけておきます。

f:id:a-tomi:20190908223425j:plain

このプロトコルロジックアナライザーで先に確認をしたので、PICプログラムは全く迷いなくできました。

f:id:a-tomi:20190908224059j:plain

 次にアセンブラーのPICプログラム、その下にArduinoのスケッチをつけておきます。アセンブラーの解説はまた次の機会にしますが、ソースをご覧いただくとお分かりいただけることが多いかと思います。

ここではマクロなど不使用のものもつけたままですし、PICのデバッグ用の独自ルーチンもつけたままです、今回は使いませんでしたが使うと便利です。

それでも十分に小さく収まっており、プログラムメモリーもデータメモリーも約4分の3はまだ空いています。

まずPICアセンブラーのソースです。

;U190908-RTC-Writer-V00-02.asm			     		As of Sep. 8, 2019
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;										;
; RTC Writer with PIC12F1822 V00-02 						;
;	        (C)2015-2019 Akira Tominaga, All rights reserved.		;
;	  Major revisions							;
;		00-02	Replace LCD device  with TM1637-4digit-display		;
;										;
;	  Function								;
;		1.Show time on TM1637 4 digit display				;
;		2.Update DS3231 calendar and time when Set switch pushed	;
;		  Mode switch to change calendar/time unit			;
;		  Time switch to change value of the unit within its range	;
;										;
;	  Input/output       							;
;		RA0 Set switch (swSet) input pulled-up				;
;		RA1 Time switch (swTime) input pulled-up			;
;		    Used as LED output when debugging				;
;		RA2 DIO for TM1637 outuput usually and input occasionally	;
;		RA3 Mode switch (swMode) input pulled-up			;
;		RA4 SDA for I2C-RTC  output  usually and input occasionally	;
;		RA5 SCL for I2C-RTC  output and CLK for TM1637 output		;
;										;
;	  Remarks								;
;		1. Clock = HFINTOSC (	16MHz)					;
;			Hence 1 step = 0.25 micro seconds			;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
	list		p=12F1822      ; list directive to define processor
	#include	 ; p12F1822.inc specific variable definitions
    __CONFIG _CONFIG1, _FOSC_INTOSC & _WDTE_OFF & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _CPD_OFF & _BOREN_OFF & _CLKOUTEN_OFF & _IESO_OFF & _FCMEN_OFF
    __CONFIG _CONFIG2, _WRT_OFF & _PLLEN_OFF & _STVREN_OFF & _BORV_LO & _LVP_OFF
;	page
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 	Macro definitions					;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 	Device dependent Macros			;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
DEVset	macro	
	BANKSEL	OSCCON		; Bank=1
	movlw	B'01111010'	; 16MHz and internal oscillator
	movwf	OSCCON
;
;	BANKSEL	INTCON		; Interrupt Con (in all banks hence comment)
	clrf	INTCON		; Disable all interrupts
;
; PORTA initialization
	BANKSEL	PORTA		; Bank=0
	clrf	PORTA
;	BANKSEL	LATA		; Bank=2
;	clrf	LATA
	BANKSEL	ANSELA		; Bank=3
	clrf	ANSELA		; No use of ADC
	BANKSEL	ADCON0		; Bank=1
	clrf	ADCON0		; No use of ADC
;
	BANKSEL	TRISA		; Bank=1
	movlw	B'11001011'	; RA2,4 and 5 are output
	movwf	TRISA
;
	BANKSEL	OPTION_REG	; Bank=1
	bcf	OPTION_REG,7	; Enable weak pull-up
	BANKSEL	WPUA		; Bank=4
	movlw	B'00001011'	; Weak Pull-up for RA 0,1,and 3
	movwf	WPUA		;  
;
	clrf	BSR		; Bank=0
	InitP			; Initialize ports
	endm
;
InitP	macro			; Initialize ports
	movlw	B'11111111'	; All inputs pulled-up and outputs H
	movwf	PORTA
	endm
;		
; for I2C-Master protocol
; Sending data to I2C slave
I2Csnd	macro	slave,dataa,datalen
	movlw	slave
	movwf	I2Cadr
	movlw	dataa
	movwf	FSR0L
	movlw	datalen
	movwf	DataLen
	call	I2Csndr
	endm	
;
; Receiving data from I2C slave
I2Crcv	macro	slave,dataa,datalen
	movlw	slave
	movwf	I2Cadr
	movlw	dataa
	movwf	FSR0L
	movlw	datalen
	movwf	DataLen
	call	I2Crcvr
	endm	
;
; I2C start signal
I2Cstat macro
	call	I2Cstar
	endm
;
; I2C stop signal
I2Cstop	macro
	call	I2Cstpr
	endm
;
; For TM16xx-Master protocol
; Sending Literal to TM16xx
TMsendL	macro	tslit		; Send a literal to TM1637
	movlw	tslit
	call	TMsndLr
	endm
;
; Sending a Byte to TM16xx
TMsendB	macro	tsbyte		; Send a Byte specified, to TM1637
	movf	tsbyte,W
	movwf	Sbyte
	call	TMsndBr
	endm
;
; TM16xx start signal
TMstart macro			; Start TM16xx lines
	call	TMstrtr
	endm
;
; TM16xx stop signal
TMstop	macro			; Stop TM16xx lines
	call	TMstopr
	endm
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	API and I/O macros			;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;for TM1637 application interfaces
;display 4 literal characters directly to TM1637
dsp4chr	macro 	d1000,d100,d10,d1
	movlw	d1000
	movwf	Dig1000
	movlw	d100
	movwf	Dig100
	movlw	d10
	movwf	Dig10
	movlw	d1
	movwf	Dig1
	call	TMdspr
	endm
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	Time cosuming macros		;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Mic	macro			;Consume 1 μS only
	goto	$+1
	goto	$+1
	endm
;
Mic2	macro	mic2p		;Consume 2 μS x n
	movlw	mic2p
	call	Mic2r
	endm
;
Mic2p5	macro 	mic25p		; Consume 2.5μS x n
	movlw	mic25p
	call	Mic25r
	endm
;
Mic5	macro	mic5p		; Consume 5μS x n
	movlw	mic5p
	call	Mic25r
	movlw	mic5p
	call	Mic25r
	endm
;
Mic50	macro	mic50p		; Consume 50μS x n
	movlw	mic50p
	call	Mic50r
	endm
;
Milli	macro	millip		; Consume mS x n
	movlw	millip
	call 	Millir
	endm
;
Mil100	macro	mil100p		; Consume 100 mS x n
	movlw	mil100p
	call 	Mil100r
	endm
; 
Secs	macro	secsp		; Consume Second x n
	movlw	secsp
	call	Secsr
	endm
;
Mins	macro	minsp		; Consume Minute x n
	movlw	minsp
	call	Minsr
	endm
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	Debug and Abend macros		;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
LEDon	macro			; LED on macro for debugging
	BANKSEL	TRISdbg		; Select TRIS bank
	btfsc	TRISdbg,LED	; Is LED pin input?
	bsf	Flags,LEDpin	; Yes,show LED pin was input
	bcf	TRISdbg,LED	; Set LED output mode, in any case
	clrf	BSR		; Point Bank 0
	nop			; Timing for PORTdbg changed
	bsf	PORTdbg,LED	; LED on
	goto	$+1		; Timing for PORTdbg changed
	endm
;
LEDoff	macro			; LED off macro for debugging
	goto	$+1		; Timing in case PORTdbg changed
	bcf	PORTdbg,LED	; LED off
	btfss	Flags,LEDpin	; Was LEDpin input before debug?
	goto	$+7		; No, skip the followings
	BANKSEL	TRISdbg		; Select TRIS register
	bsf	TRISdbg,LED	; Set LED input mode again
	nop			; nop for timing
	BANKSEL	WPUdbg		; Bank for WPUx
	bsf	WPUdbg,LED	; Weak Pull-up for LED port
	clrf	BSR		; Point Bank 0
	nop			; Timing for PORTdbg changed
	endm 
;
Udebug	macro 	Addr
	movf	Addr,W
	call	Udbgr
	endm
;
Trigon	macro			; DSO trigger on for debugging
	BANKSEL	TRISdbg		; Select TRIS bank
	btfsc	TRISdbg,Trig	; Is Trig pin input?
	bsf	Flags,Trigpin	; Yes, show Trig pin was input
	bcf	TRISdbg,Trig	; Set Trig output mode, in any case
	clrf	BSR		; Point Bank 0
	nop			; Timing for PORTdbg changed
	bsf	PORTdbg,LED	; Trig on
	goto	$+1		; Timing for PORTdbg changed
	endm
;
Trigoff macro			; DSO trigger off for debugging
	goto	$+1		; Timing in case PORTdbg changed
	bcf	PORTdbg,Trig	; Trig off
	btfss	Flags,Trigpin	; Was Trigpin input before debug?
	goto	$+7		; No, skip the followings
	BANKSEL	TRISdbg		; Select TRIS register
	bsf	TRISdbg,Trig	; Set Trig input mode again
	nop			; nop for timing
	BANKSEL	WPUdbg		; Bank for WPUx
	bsf	WPUdbg,Trig	; Weak Pull-up for Trig port
	clrf	BSR		; Point Bank 0
	nop			; Timing for PORTdbg changed
	endm
;
Uabend	macro	abn		; User Abnormal-end number
	movlw	abn
	goto	Uabendr
	endm
;
Ublink	macro	bno		; Blink LED for specified times
	movlw	bno
	call	Ublinkr
	endm
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	Logic macros				;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	Comparison macros		;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	Compare A w/ B and branch to Smaller, Equal, or Larger(next statm't line) 
CabSEL	macro	Aadr,Badr,Smladr,Eqladr
	movf	Badr,W		; W=B
	subwf	Aadr,W		; W=A-B
	btfss	STATUS,C	; A≧B? Yes, skip next
	goto	Smladr		; 	If A<B goto Smaladr
	btfsc	STATUS,Z	; A=B? No skip next
	goto	Eqladr		;	If A=B goto Eqladr
;				; 	If A>B then next (here)
	endm
;
;	Compare A w/ B and branch to Smaller or Others (Equal or Larger) 
CabSO	macro	Aadr,Badr,Smladr
	movf	Badr,W		; W=B
	subwf	Aadr,W		; W=A-B
	btfss	STATUS,C	; A≧B? Yes, skip next
	goto	Smladr		; 	If A<B goto Smaladr
;				; 	If A≧B then next (here)
	endm
;
;	Compare A w/ B and branch to Equal, or Other(next statm't line) 
CabEO	macro	Aadr,Badr,Eqladr
	movf	Badr,W		; W=B
	subwf	Aadr,W		; W=A-B
	btfsc	STATUS,Z	; A=B? 
	goto	Eqladr		;	If A=B goto Eqljmp
;				; 	else next (here)
	endm
;
;	Compare A w/ literal and branch to Smaller, Equal, or Larger(next statm't) 
CalSEL	macro	Aadr,Blit,Smladr,Eqladr
	movlw	Blit		; W=B
	subwf	Aadr,W		; W=A-B
	btfss	STATUS,C	; A≧B? Yes, skip next
	goto	Smladr		; 	If A<B goto Smaladr
	btfsc	STATUS,Z	; A=B? No skip next
	goto	Eqladr		;	If A=B goto Eqladr
;				; 	If A>B then next (here)
	endm
;
;	Compare A w/ literal and branch to Smaller, or Others (Equal or Larger) 
CalSO	macro	Aadr,Blit,Smladr
	movlw	Blit		; W=B
	subwf	Aadr,W		; W=A-B
	btfss	STATUS,C	; A≧B? Yes, skip next
	goto	Smladr		; 	If A<B goto Smaladr
;				; 	If A≧B then next (here)
	endm
;
;	Compare A w/ literal and branch to Equal, or Others 
CalEO	macro	Aadr,Blit,Eqladr
	movlw	Blit		; W=B
	subwf	Aadr,W		; W=A-B
	btfsc	STATUS,Z	; A=B? No skip next
	goto	Eqladr		;	If A=B goto Eqladr
;				; 	else next (here)
	endm
;
;	Compare A w/ literal and branch to Large, or Others
CalLO	macro	Aadr,Blit,Ladr
	movlw	Blit		; W=B
	subwf	Aadr,W		; W=A-B
	btfss	STATUS,C	; A≧B? Yes, skip next
	goto	$+3		; 	If A<B goto Others
	btfss	STATUS,Z	; A=B? Yes skip next
	goto	Ladr		;	If A>B goto Laddr
;				; 	If A≦B then next (here)
	endm
;
;	Compare A with Literal and branch to Un-equal, or Others
CalUO	macro	Aadr,Blit,Uneqladr
	movlw	Blit		; W=B literal
	subwf	Aadr,W		; W=A-B	
	btfss	STATUS,Z	; A=B?
	goto	Uneqladr	; No, goto unequal
;				;	If yes then next
	endm
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 	Files and Equations					;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	Files				;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	cblock	H'20'
;
; For Application support
	ModeN		; Mode to point BCD mem addr of RTC
	T7Chr		; 7 segment character to show ModeN
	RvMax		; Max number of RTC data in ModeN
	RvMin		; Minimum num of RTC data in ModeN
	Byteh		; Work area for high byte
	Bytel		; Work area for low byte
;
; For RTC
	RTCmem		; RTC memory address
	BCDsec		; RTC memory 0 = Second BCD	00-59
	BCDmin		; RTC memory 1 = Min BCD	00-59
	BCDhr		; RTC memory 2 = Hour BCD	00-23
	BCDdow		; RTC memory 3 = Day of Weak 	01-07
	BCDday		; RTC memory 4 = Day BCD	01-31
	BCDmon		; RTC memory 5 = Month BCD 	01-12
	BCDyr		; RTC memory 6 = Year yy BCD 	00-99
;
; For I2C protocols
	I2Cadr		; Destinated slave I2C address
	DataLen		; Number of bytes to be sent/received
	Sbyte		; One byte to be sent/received
	Bitctr		; Loop counter for bits in a byte 
;
; For TM1637 4 digit display
;	Sbyte		; Byte to send, shared with I2C
	Dig1000		; Digit 1000
	Dig100		; Digit 100
	Dig10		; Digit 10
	Dig1		; Digit 1
	Bytectr		; Loop counter for TM1637-4digits
;
; Areas for time consuming subroutines
; Do not change the sequences from Mic5c to Minsc
;	if co-used with calculation parameters
	Mic25c
	Mic50c
	Millic
	Mil100c
	Secsc
	Minsc
;
; Areas for debugging routines
	DmpA 		; Display byte area 
	Dmp8C 		; Bit counter for loop (Initial Dmp8V =8)
	BlinkC 		; Counter for blinking (set for Debugb or Abendb)
	Abendn		; Abend number
	Ucpc		; User Check point chr to trace 
	Flags
;
	endc		
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 	Equations			;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 	For PORTA		;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;swSet	equ	0		; PORTsw Set switch
;swTime	equ	1		; PORTsw Time switch
;LED	equ	1		; PORTdbg LED  for debugging
;Trig	equ	1		; PORTdbg Trig for debugging
;Dio	equ	2		; PORTtm TM16xx DIO
;swMode	equ	3		; PORTsw Mode Switch
;Dl	equ	4		; PORTI2C SDA
;Cl	equ	5		; PORTI2C SCL
;Clk	equ	5		; PORTtm TM16xx CLK
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ***	Logical PORTI2C		; Change this when port changed
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PORTI2C equ	PORTA		; 
Dl	equ	4		; I2C Data line = SDA
Cl	equ	5		; I2C Clock line = SCL
LATI2C	equ	LATA		; LATch for I2C port
TRISI2C equ	TRISA		; TRIS reg for I2C
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ***	Logical PORTtm		; Change this when port changed
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PORTtm	equ	PORTA		; 
Dio	equ	2		; TM16xx Data line = DIO
Clk	equ	5		; TM16xx Clock line = CLK
LATtm	equ	LATA		; Latch for tm port
TRIStm	equ	TRISA		; TRIS for tm
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ***	Logical PORTsw		; Change this when port changed
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PORTsw	equ	PORTA
swSet	equ	0		; Set switch input pulled-up
swTime	equ	1		; Time switch input pulled-up
swMode	equ	3		; Mode switch input ulled-up
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ***	Logical PORTdbg		; Change this when port changed
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PORTdbg	equ	PORTA
TRISdbg	equ	TRISA
WPUdbg	equ	WPUA
LED	equ	1		; LED when debugging
Trig	equ	1		; Trigger for DSO when debugging
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	Values & Characters	;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Equations for RTC DS3231 
DS3231	equ	H'D0'		; RTC DS3231 address (to write)
RTCdata equ	BCDsec		; RTC memory data top
ModeMax	equ	6		; RTC data pointer ModeN max
ModeMin	equ	0		; RTC data pointer ModeN minimum
;
secMax	equ	H'59'
secMin	equ	0
;
minMax	equ	H'59'
minMin	equ	0
;
hrMax	equ	H'23'
hrMin	equ	0
;
dowMax	equ	H'07'
dowMin	equ	H'01'
;
dayMax	equ	H'31'
dayMin	equ	H'01'
;
monMax	equ	H'12'
monMin	equ	H'01'
;
Modeyr	equ	6
yrMax	equ	H'99'
yrMin	equ	0
;
; 7 segment LED characters for TM1637
Ch7Y	equ	H'6E'
Ch7Eq	equ	H'48'
Ch7M	equ	H'37'
Ch7d	equ	H'5E'
Ch7w	equ	H'1C'
Ch7h	equ	H'74'
Ch7m	equ	H'54'
Ch7S 	equ	H'6D'
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 	For Debug		;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PORTdbg	equ	PORTA		;
Trig	equ	1		; DSO trigger pulse port for test use
;
Dmp8V	equ	D'8'		; Debug display bit counter initial value
Debugb	equ	D'8'		; number of blinkings to show Debugging
Abendb	equ	D'25'		; number of blinkings to notify Abend
; Flags byte
LEDsv	equ	0		; LED save bit in Flags byte
LEDpin	equ	1		; LED pin was input when not debugging
Trigpin	equ	2		; Trigger pin was input before debug use
;
	page
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	Initializing						;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	org	0
	goto	Startp		; Go to start entry
	org	4		; This is Interrupt entry
	retfie			; 
;
Startp	DEVset			; Define ports
	clrf	FSR0H		; Clear FSR0H forever
	clrf	FSR1H		; Clear FSR1H forever
	clrf	Flags		; Clear all bits of Flags
;
	Milli	D'50'		; Wait devices stabilized
	call	IniTMr		; Set-up TM1638 display
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	Main program loop					;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Mainp	btfss	PORTsw,swSet	; Set switch ?
	call	RTCupdr
;
	call	RTCrdr		; Read clock
	call	Dsptime		; Display time
;	
	Milli	D'20'		; +20 mS for one Main loop
	goto	Mainp		; Contilue infinite loop
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	RTC updating routine					;
;		Display value for current ModeN			;
;		If swMode pushed, display next unit to update	;
;		If swTime pushed, increase value and display it	;
;		If swSet pushed,  update RTC (RTCwtr)		;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
RTCupdr	btfss	PORTsw,swSet	; swSet still on?
	goto	$-1		; then wait for off
 	movlw 	Modeyr		; set initial mode year
	movwf	ModeN
;
RTCudm	movf	ModeN,W		; Large loop on ModeN
	brw
	goto	Mdsecr		; case ModeN=0 
	goto	Mdminr		; case ModeN=1 
	goto	Mdhrr		; case ModeN=2 
	goto	Mddowr		; case ModeN=3 
	goto 	Mddayr		; case ModeN=4 
	goto	Mdmonr		; case ModeN=5 
	goto	Mdyrr		; case ModeN=6 
;
; Macro used within  this section
Mdsetup macro	T7c,Rmax,Rmin
	movlw	T7c
	movwf	T7Chr
	movlw	Rmax
	movwf	RvMax
	movlw	Rmin
	movwf	RvMin
	goto	Mdnext
	endm
;
; set up environment for update and display
Mdsecr	Mdsetup	Ch7S,secMax,secMin
Mdminr	Mdsetup	Ch7m,minMax,minMin
Mdhrr	Mdsetup	Ch7h,hrMax,hrMin
Mddowr	Mdsetup	Ch7w,dowMax,dowMin
Mddayr	Mdsetup	Ch7d,dayMax,dayMin
Mdmonr	Mdsetup	Ch7M,monMax,monMin
Mdyrr	Mdsetup	Ch7Y,yrMax,yrMin
;
Mdnext 	call	Rdspr		; Display X=nn
	btfss	PORTsw,swTime	; Is Time pushed ?
	goto	Rinc		;  Yes, goto  Rinc
	btfss	PORTsw,swMode	; Is Mode pushed ?
	goto	Rnxtm		;  Yes, goto Rnxtm
	btfss	PORTsw,swSet	; Is Set pushed ?
	goto	Rwrite		;  Yes, goto Rwrite
	goto	Mdnext		; If no button, small loop for button
;
; 	Time pushed here
Rinc	call	Rincr		; Increase value in that mode
	Mil100	2		; 0.2 sec for increasing value
	goto	Mdnext		; return to loop for button
;
;	Mode pushed here
Rnxtm	btfss	PORTsw,swMode	; Mode button still pushed
	goto	$-1		; then wait for release
	CalEO	ModeN,0,Msmax	; If current ModeN=0 goto Msmax
	decf	ModeN,F		; set next mode
Rnxtme	goto	RTCudm		; and return to large loop for mode
;
Msmax	movlw	Modeyr		; get maximum mode
	movwf	ModeN		; and set it
	goto	RTCudm		; and return to large loop for mode
;	
Rwrite	btfss	PORTsw,swSet	; Set button still on?
	goto	$-1		; then wait for release
	call	RTCwtr		; and write to RTC
;
	return			; exit from RTC updating routine
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	Display value pointed by ModeN	;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Rdspr	movf	T7Chr,W
	movwf	Dig1000
	movlw	Ch7Eq
	movwf	Dig100
;
	movlw	BCDsec		; Point BCD area top
	movwf	FSR1L		; set it to FSR1L
	movf	ModeN,W		; Get Mode number
	addwf	FSR1L,F		; Point current BCD area	
	swapf	INDF1,W		; get high nibble to low in W
	call	TMser		; clear high nibble & edit
	movwf	Dig10		; set it to Dig10
	movf	INDF1,W		; get low nibble to low
	call	TMser		; clear high nibble to low
	movwf	Dig1		; set it to Dig1
;
	call	TMdspr
	return
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	Increase value pointed by  ModeN;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Rincr	movlw	BCDsec		; Point BCD area top
	movwf	FSR1L		; set it to pointer
	movf	ModeN,W		; get ModeN to add
	addwf	FSR1L,F		; Point current BCD area
;
	CabSO	INDF1,RvMax,Rigo ;If smaller than max, goto Rigo
	movf	RvMin,W		; Else, get minimum value
	movwf	INDF1		; and set it to INDF1 (BCD area)
	return
;
Rigo	movf	INDF1,W		; Get BCD value
	andlw	H'0F'		; clear left nibble
	movwf	Bytel		; and set to Bytel
	swapf	INDF1,W		; Get BCD nibbles swapped
	andlw	H'0F'		; clear left nibble
	movwf	Byteh		; and set to Byteh
;
	incf	Bytel,F		; Bytel+1
	CalSO	Bytel,D'10',Rie ; If Bytel≦9,goto Rie
	movlw	D'16'		; Else increase digit 10
	addwf	INDF1,F		;
	movlw	H'F0'		; and clear digit 1
	andwf	INDF1,F
	return

Rie	incf	INDF1,F		; Increase BCD value
	return			; and return
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	Displaying Time routine					;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Dsptime movf	BCDmin,W ; get minute dig 1
	call	TMser	; edit bit 0-3 value to 7 seg char
	movwf	Dig1	; set it to Digit 1
;	
	swapf	BCDmin,W ; get minutes dig 10
	call	TMser	; edit bit 0-3 value to 7 seg char
	movwf	Dig10	; set it to Digit 10
;
	movf	BCDhr,W ; get hour dig 1
	call	TMser	; edit bit 0-3 value to 7 seg char
	movwf	Dig100	; set it to Digit 100
	bsf	Dig100,7 ; set colon after Digit 100
;
	swapf	BCDhr,W ; get hour dig 10
	call	TMser	; edit bit 0-3 value to 7 seg char
	movwf	Dig1000	; set it to Digit 1000
;
	call	TMdspr	; show them to TM1637
	return
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	RTC reading routine	;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
RTCrdr	clrf	RTCmem		; set RTC memory addr 0
	I2Csnd DS3231,RTCmem,1 ; send it to DS3231
	Milli	1		; wait for readiness (unnecessary?)
	I2Crcv DS3231,RTCdata,7	; receive 7 byte data from DS3231
	return
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	RTC writing routine	;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
RTCwtr	clrf	RTCmem		; Set memory address zero
	I2Csnd	DS3231,RTCmem,8 ; Send data, includign memory addr in top
	return
;	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	I2C Send byte-string				;
;         when called,					;
;		I2C device adr in I2Cadr byte		;
;		Data length in DataLen			;
;		Data Addr in FSR0L (INDF0)		;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
I2Csndr	movf	I2Cadr,W
	movwf	Sbyte
;	rlf	Sbyte,F		; shift leftt (if adr for Arduino)
	bcf	Sbyte,0		; indicate write
;
	I2Cstat			; Start I2C
	call	Sendr

Sdata	movf	INDF0,W		; Get a sending character
	movwf	Sbyte		; Set it to Sbyte
	call	Sendr		; Send it to the device
;
	incf	FSR0L,F		; point next char
	decfsz	DataLen,F	; Have all chars sent ?
	goto	Sdata		; No, loop
;
; Stop signal and return
	I2Cstop
	return	
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 	I2C Receive byte-string				;
;         when called,					;
;		I2C device adr in I2Cadr byte		;
;		Data length in DataLen			;
;		Data Addr in FSR0L (INDF0)		;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
I2Crcvr	equ	$
	I2Cstat			; Start I2C
	movf	I2Cadr,W	; Get I2C dev addr
	movwf	Sbyte		; Set it into Sbyte
;	rlf	Sbyte,F		; Shift left (if adr for Arduino)
	bsf	Sbyte,0		; indicate read
	call	Sendr		; Send slave addr to read
;
I2Crbyt	equ	$
	BANKSEL	TRISI2C		; 
	bsf	TRISI2C,Dl	; Set Dline input mode
	clrf	BSR		; Point Bank 0
;
	movlw	8		; Bits in a reciving byte
	movwf	Bitctr		; into loop counter 
I2Crblp	Mic5	1		; Timing before raising clock
	bsf	PORTI2C,Cl	; Set clock high
	Mic2p5	1		; Timing after raising clock
;
	btfsc	PORTI2C,Dl
	goto	Rbith
;	goto	Rbitl
;
Rbitl	bcf	Sbyte,0		; Clear bit 0
	goto	Rbitnxt		; and goto next
;
Rbith	bsf	Sbyte,0		; Set bit 0
Rbitnxt	Mic2p5	1		; Timing after checking
	bcf	PORTI2C,Cl	; Set clock low
;	Mic2p5	1		; Timing after clock falling
	decfsz	Bitctr,F	; Still bits?
	goto	Rbcont		; Yes, continue
	goto	Rbend		; No, end of one byte
;
Rbcont	rlf	Sbyte,F		; Shit left
	goto	I2Crblp		; and goto loop
;
Rbend	movf	Sbyte,W		; Get received byte
	movwf	INDF0		; Set it to INDF0
	incf	FSR0L,F		; increase index
	decfsz	DataLen,F	; DataLen-1
	goto	Sackr		; Send Ack and continue
	goto	Snackr		; Send Nack and stop
;
;Set Dl output mode and send Ack to conitinue
Sackr	equ	$
	BANKSEL	LATI2C		; Data line high when output mode
	bsf	LATI2C,Dl	; Lat-Dl on
	BANKSEL	TRISI2C		; 
	bcf	TRISI2C,Dl	; Set Dl output mode
	clrf	BSR		; Point Bank 0
;
	bcf	PORTI2C,Dl	; Send Ack
	Mic2p5	1		; Timing after clock falling
	bsf	PORTI2C,Cl	; Show Ack
	Mic2p5	2
	bcf	PORTI2C,Cl	; Set clock low
	Mic2p5	1
	bsf	PORTI2C,Dl	; Return Dline to high
	goto	I2Crbyt		; Next byte process
;
;Set Dl output mode and send Nack
Snackr	equ	$
	BANKSEL	LATI2C		; Data line high when output mode
	bsf	LATI2C,Dl	; Lat-Dl on
	BANKSEL	TRISI2C		; 
	bcf	TRISI2C,Dl	; Set Dl output mode
	clrf	BSR		; Point Bank 0
;
	bsf	PORTI2C,Dl	; Send Nack
	Mic2p5	1		; Timing after clock falling
	bsf	PORTI2C,Cl	; Show Nack
	Mic2p5	2
	bcf	PORTI2C,Cl	; Set clock low
	Mic2p5	2
	I2Cstop			; and stop I2C once
	Mic5	D'10'		; for 50 micro sec
;
	I2Cstat			; then start I2C again
	movf	I2Cadr,W	; Get I2C dev addr
	movwf	Sbyte		; Set it into Sbyte
	rlf	Sbyte,F		; Shift left
	bcf	Sbyte,0		; indicate write
	call	Sendr		; Send it
;
	I2Cstop
	return
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	I2C Start signal routine 			;
;	   	when dataline mode is output		;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
I2Cstar	bcf	PORTI2C,Dl	; set SDA low
	Mic2p5	1
	bcf	PORTI2C,Cl	; set SCL low
	Mic2p5	3
	return
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	I2C Stop signal routine 			;
;	   	when dataline mode is output		;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
I2Cstpr	bcf	PORTI2C,Cl	; set SCL low
	Mic2p5	1
	bcf	PORTI2C,Dl	; set SDA low
	Mic2p5	4
	bsf	PORTI2C,Cl	; Clock line rasing
	Mic2p5	2
	bsf	PORTI2C,Dl	; Data line rasing
	Mic2p5	1
	return	
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	I2C One-byte sending routine 			;
;	   	with data in Sbyte			;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Sendr	movlw	8		; Set bit count in a byte
	movwf	Bitctr		; into loop counter 
;
Sloop	btfsc	Sbyte,7		; Check top bit of Sbyte
	goto	Slpdh		; 
	bcf	PORTI2C,Dl	; If bit low then Data L
	goto	Slpnxt
Slpdh	bsf	PORTI2C,Dl	; If bit high then Data H
;
Slpnxt	Mic2p5	1
	bsf	PORTI2C,Cl	; Show bit
	Mic2p5	2
	bcf	PORTI2C,Cl	;
	Mic2p5	1
;
	rlf	Sbyte,F		; Shift left 
	decfsz	Bitctr,F        ; All bits done?
	goto	Sloop		; No, loop within a byte
;	
;Receive Ack 		
; set data line input mode
	BANKSEL	TRISI2C		; 
	bsf	TRISI2C,Dl	; Set Dline input mode
	clrf	BSR		; Point Bank 0
;
	Mic2p5	1		; wait for Ack timing
	bsf	PORTI2C,Cl	; Clock H for Acq confirmation
	Mic2p5	2		; 
	bcf	PORTI2C,Cl	; Cl L for Dl release by slave
	Mic2p5	2
;
; Set Data line output
	BANKSEL	LATI2C		; Data line high when output mode
	bsf	LATI2C,Dl	; Lat-Dl on
	BANKSEL	TRISI2C		; 
	bcf	TRISI2C,Dl	; Set Dl output mode
	clrf	BSR		; Point Bank 0
	return
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	TM1637 support routines 			;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 	TM1637 display brightness setting	;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
IniTMr	equ	$
; TM brightness initial setting	;
;   H'88'+ duty ratio:
;       H'00'=1/16, H'01'=2/16, H'02'=4/16, H'03'=10/16
;	H'04'=11/16, H'05'=12/16, H'06'=13/16, H'07'=14/16
	movlw	H'88'+H'02'	; Initial brightness value
	movwf	Sbyte		; Set to Sbyte
	TMstart
	TMsendB Sbyte
	TMstop
	return
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; TM 1637 Char to 7 segment editing routine	;
;	replaceubg Char in W with 7 seg in W	;
;	  7 seg format is B'xgfe-dcba'		;
;	  if colon required,IOR H'80' to Dig100	;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TMser	andlw	H'0F'		;Prevent wild branch
	brw
Case0	retlw	H'3F'
Case1	retlw	H'06'
Case2	retlw	H'5B'
Case3	retlw	H'4F'
Case4	retlw	H'66'
Case5	retlw	H'6D'
Case6	retlw	H'7D'
Case7	retlw	H'07'
Case8	retlw	H'7F'
Case9	retlw	H'6F'
CaseA	retlw	H'77'		; A
CaseB	retlw	H'7C'		; b
CaseC	retlw	H'39'		; C
CaseD	retlw	H'5E'		; d
CaseE	retlw	H'79'		; E
CaseF	retlw	H'71'		; F
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	TM1637 4digits displaying routine	;
;	  	using Dig1000 to Dig1		;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TMdspr	movlw	Dig1000		; Point Byte0 address
	movwf	FSR0L		; set it to use INDF0
	movlw	4		; Set four digits
	movwf	Bytectr		; to Byte counter
;
	TMstart	
	TMsendL	H'40'		; 
	TMstop 			; 
;
	TMstart
	TMsendL	H'C0'		; Set addr zero of TM
;
TMdspl	movf	INDF0,W		; Get byte contents
	movwf	Sbyte		; Set them to Sbyte
	TMsendB	Sbyte		; 
	incf	FSR0L,F		; point next byte
	decfsz	Bytectr,F	; Check if all done
	goto	TMdspl		; No. loop
;
	TMstop		; When all sent, Stop
	return
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	TM1637 Start sending routine 		;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TMstrtr	nop
	BANKSEL	TRIStm
	bsf	LATtm,Dio	; Dio high beforhand
	bcf	TRIStm,Dio	; Set Dio output
	clrf	BSR		; return to Bank0
;
	bsf	PORTtm,Dio	; Dio high
	Mic5	1
	bsf	PORTtm,Clk	; Clk high
	Mic5	1
	bcf	PORTtm,Dio	; Dio low
	Mic5	1
	bcf	PORTtm,Clk	; Clk low
	Mic5	1	
	return
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	TM1637 Stop sending routine 		;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TMstopr	nop
	BANKSEL	TRIStm
	bcf	LATtm,Dio	; Dio low beforehand
	bcf	TRIStm,Dio	; Set Dio output
	clrf	BSR		; Return to Bank0
;
	Mic5	1
	bsf	PORTtm,Clk	; Clk high
	Mic5	1
	bsf	PORTtm,Dio	; Data high
	Mic50	1
	return
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	TM1637 Byte sending routine 		;
;		called by TMsendB with Sbyte or	;
;		TMsendL	with value in W reg	;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TMsndLr	movwf	Sbyte		; Set sending byte
;
TMsndBr	movlw	8		; Set bit count
	movwf	Bitctr		; into loop counter
;
Tloop	btfsc	Sbyte,0		; Check LSb for little-endian-
	goto	Tlpdh		; 
	bcf	PORTtm,Dio	; If bit low then Data L
	goto	Tlpnxt
;
Tlpdh	bsf	PORTtm,Dio	; If bit high then Data H
Tlpnxt	Mic5	1		; 
	bsf	PORTtm,Clk 	; Clock high
	Mic2p5	4		; 10 micro sec
	bcf	PORTtm,Clk	; Clock low	
	Mic5	1
	rrf	Sbyte,F		; Shift right for little-endian 
	decfsz	Bitctr,F
	goto	Tloop	
;	
;	Receiving Ack 		;
	BANKSEL	TRIStm
	bsf	TRIStm,Dio	; Set Dio input mode
	clrf	BSR		; return to Bank0
	Mic2p5	4		; Wait for Ack=low
	bsf	PORTtm,Clk 	; Clk high for Ack confirmation
	Mic2p5	4		; 10 micro sec
	bcf	PORTtm,Clk	; Clk L for Data release to H by Slave
	Mic2p5	4		; Wait for Dio set H 
;
	BANKSEL	TRIStm
	bsf	LATtm,Dio	; Dio high beforhand
	bcf	TRIStm,Dio	; Set Dio output
	clrf	BSR		; return to Bank0
	Mic50	1		; Enough time for TM1637 after Ack
	return			; 
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	Timing subrooutines for general purposes		;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 	Make 2.0 micro S x n	(Mic2)	;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Mic2r	movwf	Mic25c		; + Wset + call = 1 micro sec 
;
Mic2l	decfsz	Mic25c,F	; If exhausted, 1 micro S hereafter
	goto	Mic2li		; else go out (2nd time 1.75 mic sec)
	return		
;
Mic2li	goto	$+1		;            (2nd time 2.25 mic sec)
	nop			;	     (2nd time 2.5 mic sec)
	goto	Mic2l		; go back    (2nd time 3 micro sec)
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 	Make 2.5 micro S x n (Mic2p5)	;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Mic25r	movwf	Mic25c		; + Wset + call = 1 micro sec 
	nop			; 1.25 micro sec
;
Mic25l	nop			; 1.5 micro sec (2nd time 4 mic sec)
	decfsz	Mic25c,F	; If exhausted, 1 micro S hereafter
	goto	Mic25li		; else go out (2nd time 2.25 mic sec)
	return		
;
Mic25li	Mic			;	      (2nd time 3.25 mic sec)
	goto	Mic25l		; go back    (2nd time 3.75 micro sec)
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	50 Microseconds	x n	Mic50  	;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
Mic50r	movwf	Mic50c	 	; set how many 50 microsec (1 micro sec to here)
	nop			; 1.25 micro sec up to here
;
Mic50l	Mic2p5	D'19'		; + 47.5 = 48.75 mic sec (2nd time 98.75 mic sec)
	nop			; + 0.25 = 49 micro sec (2nd time 99 mic sec)
; 
	decfsz	Mic50c,F	; If exhausted then 1 mic S hereafter
	goto	Mic50li		; else go out (2nd time 49.75 mic sec)
	return
;
;
Mic50li	Mic			; 	  (2nd time 50.75 mic sec)
	goto	Mic50l		; go back (2nd time 51.25 mic sec)
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	Milliseconds x n	(Milli)	;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
Millir	movwf	Millic 		; set how many 1 mil sec (1 mic S up to here)
	nop			; 1.25 micro sec
;
Millil	Mic50	D'19'		; + 50 mic x 19 = 951.25 mic S (2nd, 1951.25) 
	Mic2p5	D'19'		; + 47.5 mic = 998.75 micro S  (2nd, 1998.75)
	nop			; +0.25 mic = 999 micro sec    (2nd, 1999)
;
	decfsz	Millic,F	; If  exhausted then 1 micro sec hereafter
	goto	Millili		; else go out (2nd, 999.75 mic S)
	return
;
Millili	Mic			; 		(2nd time 1000.75 mic S)
	goto	Millil		; go back (2nd time 1001.25 mic S)
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	100 Milliseconds x n	(Mil100);
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Mil100r	movwf	Mil100c		;set how many 100 ms(1 micr sec up to here)
	nop			; 1.25 micro sec
;
Milhl	Milli	D'99'		;+1ms x 99 = 99001.25 micS (2nd,199001.25mic)
	Mic50	D'19'		; + 950 mic = 99951.25 micS(2nd.199951.25mic)
	Mic2p5	D'19'		; + 47.5 mic = 99998.75micS(2nd,199998.75mic)
	nop			; + 0.25 mic = 99999 mic S (2nd,199999 micS)
;
	decfsz	Mil100c,F	; If exhausted then 1 micro sec hereafter
	goto	Milhli		; else go out (2nd time, 99999.75 mic S)
	return
;
Milhli	Mic			;  	    	(2nd time, 100000.75 mic S)
	goto	Milhl		; 		(2nd time, 100001.25 mic S)
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	Seconds x n	 (Secs)		;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
Secsr	movwf	Secsc 		; set how many sec ( 1 mic sec up to here)
	nop			; 1.25 micro sec
;
Secsl	Mil100	D'9'		; 
	Milli	D'99'		; + 999 milli sec = 999001.25 micro sec
;
	Mic50	D'19'		; + 950 mic = 999951.25 micro sec
	Mic2p5	D'19'		; + 47.5 mic = 999998.75 micro sec
	nop			; + 0.25 mic = 999999 micro sec
;
	decfsz	Secsc,F		; If exhausted then 1 micro sec hereafter
	goto	Secsli		; else, go out 
	return
;
Secsli	Mic
	goto	Secsl		; (Second time, Sec + 1.25 micro sec)
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	Minutes	x n	 (Mins)		;
;	 Overhead ignored, that is only	;
;	 751.25 Mic S even when 100 Min	;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Minsr	movwf	Minsc		;set how many minutes from parameter
;
Minsl	Secs	D'60'		; 1 Seconds x 60
	decfsz	Minsc,F
	goto	Minsl
	return
;
	space
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; User debug subroutine		;
;	Show bit 7 to 0 	;
;	of specified byte	;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Udbgr	clrf	BSR		; Set Bank=0
	movwf	DmpA		; move data to Dmpa
;
	btfss	PORTdbg,LED	; Check if LED=on (on=High)
	goto	UdbLoff		;  if LED=off(Low), skip saving process
	bsf	Flags,LEDsv	; Save LEDon status
	LEDoff			; and off LED
	Mil100	D'10'		; wait for a second in case LED has been on
;
UdbLoff	movlw	Dmp8V		; set counter 8
	movwf	Dmp8C		; to Dmp8C
;
Udblp	Ublink	Debugb		; Blink for Debug = 8 times
	btfsc	DmpA,7		; check top bit 7
	goto	UdbOn		; if on then to UdbOn
	goto	UdbOff		; if off then to UdbOff
;
UdbOn	LEDon
	Mil100	D'30'		;
	goto	Udbeck
;
UdbOff	LEDoff
	Mil100	D'30'		;	
;	goto	Udbeck
;
Udbeck	decfsz	Dmp8C,F
	goto	Udbnext
	goto	Udbend
;
Udbnext	rlf	DmpA,F
	goto	Udblp
;
Udbend	Ublink	Debugb		; end blinking and 
	Mil100	D'100'		; blank for 10 seconds to write down
;
	btfss	Flags,LEDsv	; Check if LED was on
	goto	Udbret		;  no, goto return
	LEDon			;  if it was on, then on again
	bcf	Flags,LEDsv	; Clear LED save flag
;
Udbret	return
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Blinking to show debug or abend	;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Ublinkr	movwf	BlinkC
Ublinkl	LEDon			; LED on
	Milli	D'30'		; for 30ms
	LEDoff			; LED off
	Milli	D'200'		; for 200ms
;
	decfsz	BlinkC,F
	goto	Ublinkl
	return
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 	Abend routine			;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Uabendr	clrf	BSR
	movwf	Abendn			; Set Uabend number
;
	Ublink	Abendb			; Blink 25 times
	Udebug	Abendn			; Show Abend number
	goto	$
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	End of program						;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	end

 次にArduinoのスケッチです。

 

/**********************************************************
    RTC writer with TM1637 V00   Sept.8, 2019
    (c) 2015-2019 Akira Tominaga, All rights reserved.
    1.Function
     1)Display RTC Time on TM1637
     2)Update RTC when Set button pushed
    2.Major modifications
     00-00 Initial Version  Sept.8, 2019                *
    3.Remarks                                             *
**********************************************************/
#include  // TM1637Display.h
#include  // Wire.h

// Digital pins assignment
#define Md 16                   // Mode button (L=on) D16=A2
#define Tm 15                   // Time button (L=on) D15=A1
#define St 14                   // Set button (L=on)  D14=A0
#define CLK 7                   // TM1637 CLK signal
#define DIO 6                   // TM1637 DIO signal

TM1637Display display(CLK, DIO);
uint8_t Data[] = { 0x3f, 0xbf, 0x3f, 0x3f };

byte  vR[8];            // values in RTC registers
int vI[8] = { 0, 0, 0, 0, 0, 0, 0, 0}; // integers for RTC data
#define RTC_addr 0x68
#define mdI 0           // Initial mode
#define mds 1           // ss
#define mdm 2           // mm
#define mdh 3           // hh
#define mdW 4           // WW=Day of Week
#define mdD 5           // DD
#define mdM 6           // MM
#define mdY 7           // YY
#define sS 0
#define mM 1
#define hH 2

char  strYMDHMS[20];    // editing area for calendar and clock

void setup()
{
  Serial.begin(9600);
  Wire.begin();
  pinMode(Md, INPUT_PULLUP);
  pinMode(Tm, INPUT_PULLUP);
  pinMode(St, INPUT_PULLUP);

  display.setBrightness(9);  // values are valid from 8 to 11
  for (int k = 0; k < 4; k++) {
    Data[k] = 0x7F;          // all segments on
  }
  display.setSegments(Data);
  delay(5);    // this is unneccessary
}

void loop()
{
  rRTC();      // read RTC device
  // Edit and display time to TM1637
  cR2I();      // convert RTC-format to Integers
  sprintf(strYMDHMS, "20%02d/%02d/%02d %02d:%02d:%02d", vI[mdY], vI[mdM], vI[mdD], vI[mdh], vI[mdm], vI[mds]);
  Data[0] = display.encodeDigit(strYMDHMS[11]);
  Data[1] = display.encodeDigit(strYMDHMS[12]);
  Data[1] = Data[1] | 0x80;
  Data[2] = display.encodeDigit(strYMDHMS[14]);
  Data[3] = display.encodeDigit(strYMDHMS[15]);
  display.setSegments(Data);
  
  // when Set switch pushed, update RTC
  if (digitalRead(St) == LOW) {
    rtcSet();
  }
  delay(5);    // delay in loop (unnecessary)
}

/**************************************
      User defined functions
* *********************************** */

// read Real-Time-Clock
void rRTC(void) {
  Wire.beginTransmission(RTC_addr);
  Wire.write(0x00);
  Wire.endTransmission();
  Wire.requestFrom(RTC_addr, 7);
  while (Wire.available() < 7) {} // wait for data ready
  for (int i = 1; i < 8; i++) {
    vR[i] = Wire.read();
  }
  Wire.endTransmission();
}

// convert RTC-format to Integers
void cR2I(void) {
  vI[mds] = ((vR[mds] & B01110000) / 16) * 10 + (vR[mds] & B00001111);
  vI[mdm] = ((vR[mdm] & B01110000) / 16) * 10 + (vR[mdm] & B00001111);
  vI[mdh] = ((vR[mdh] & B00100000) / 32) * 20 + ((vR[mdh] & B00010000) / 16) * 10 + (vR[mdh] & B00001111);
  vI[mdW] = vR[mdW];
  vI[mdD] = ((vR[mdD] & B01110000) / 16) * 10 + (vR[mdD] & B00001111);
  vI[mdM] = ((vR[mdM] & B00010000) / 16) * 10 + (vR[mdM] & B00001111);
  vI[mdY] = ((vR[mdY] & B11110000) / 16) * 10 + (vR[mdY] & B00001111);
}

// set RTC when set button pushed
void rtcSet(void) {
  while (digitalRead(St) == LOW) { // wait for button released
  }
  byte mC[8] = {0x00, 0x6d, 0x54, 0x76, 0x3e, 0x5e, 0x37, 0x6e};
  // 7 seg modeChr 0, 's', 'm', 'h', 'W', 'D', 'M', 'Y'
  char Su[3];
  int maxvI[8] = { 0, 59, 59, 23, 7, 31, 12, 99};
  int minvI[8] = {0, 0, 0, 0, 0, 1, 1, 0};
  while (digitalRead(Md) == LOW) {}; // wait until Md released
  int mD = mdY ;          // initial mode=mdY
  rRTC();                 // read RTC device
  cR2I();                 // convert RTC-format to Integers

  // loop waiting for Set button
  while (digitalRead(St) == HIGH) {
    Data[0] = mC[mD];
    Data[1] = 0x48;       // "= "
    sprintf(Su, "%02d", vI[mD]);
    Data[2] = display.encodeDigit(Su[0]);
    Data[3] = display.encodeDigit(Su[1]);
    display.setSegments(Data);

    // Mode button loop within Set button loop
    while (digitalRead(Md) == HIGH) { // do until Md pushed
      if (digitalRead(Tm) == LOW) {   // if Time button pushed
        vI[mD] = vI[mD] + 1;          // increase value
        if (vI[mD] == maxvI[mD] + 1) {  // if exceeded max
          vI[mD] = minvI[mD];         // then set minimum & go
        }
        sprintf(Su, "%02d", vI[mD]);
        Data[2] = display.encodeDigit(Su[0]);
        Data[3] = display.encodeDigit(Su[1]);
        display.setSegments(Data);
        delay(250);

        // if Set switch on, go out of Time loop
        if (digitalRead(St) == LOW) break;

      } // repeat TM loop

      // if Set witch on, go out of Mode loop
      if (digitalRead(St) == LOW) break;
      // else repeat Mode button loop
    }

    // out of Mode loop within Set loop
    while (digitalRead(Md) == LOW) {} // until end of pushing
    if (digitalRead(St) == LOW) break; // if Set pushed, go out
    mD = mD - 1;
    if (mD == mdI) {
      mD = mdY;
    }

  }  // out of Set loop
  while (digitalRead(St) == LOW) {} // until end of Set-pushing
  // write RTC
  vR[mds] = (vI[mds] / 10) * 16 + vI[mds] % 10;
  vR[mdm] = (vI[mdm] / 10) * 16 + vI[mdm] % 10;
  vR[mdh] = (vI[mdh] / 20) * 32 + ((vI[mdh] % 20) / 10) * 16 + vI[mdh] % 10;
  vR[mdW] = vI[mdW];
  vR[mdD] = (vI[mdD] / 10) * 16 + vI[mdD] % 10;
  vR[mdM] = (vI[mdM] / 10) * 16 + vI[mdM] % 10;
  vR[mdY] = (vI[mdY] / 10) * 16 + vI[mdY] % 10;
  Wire.beginTransmission(RTC_addr);
  Wire.write(0x00);
  for (int i = 1; i < 9; i++) {
    Wire.write(vR[i]);
  }
  Wire.endTransmission();
}

今回はこのへんで。なにせ台風間近のため、さっさと寝ることにします。明日は交通機関も心配。

 

©2019 Akira Tominaga, All rights reserved.