勝手な電子工作・・

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

PICアセンブラーで芸当(?)その2)ピン共有の種明かし

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

前回書いたとおり、磁気コンパスHMC5883L用のI2C通信ラインSCLとSDAを、TM1637の通信ラインCLKとDIOが共有しています。これで問題なく動いている訳をここで簡単に説明したいと思います。

 

 前回の記事:a-tomi.hatenablog.com

回路図を再度見ます。

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

PICのI2Cモジュール(MSSPで、SPIと共通の回路です)を使う場合、PIC12F1822ではSCLはRA1でSDAはRA2でなければなりません。ここでは、それを使わずに自分ででコーディングすることにより、I2C用のピンが自由に選べるようにしています。同じルーチンは他のPICでもI2C接続用に使えます。

 

ところでなぜアセンブラーなのかと質問がありましたので、以下脱線気味ですがちょっと追加説明します。

PICを内蔵の16MHzクロックで動かす場合、通常の1マシンステップ実行に1/4µ秒、gotoやbtfscなどのジャンプ命令にその倍の1/2μ秒かかるため、正確なタイミングをプログラムできちんと作るのは簡単ではありません。例えば100KHzの通信プロトコルですとクロックラインはHとLを正確に5μ秒単位で往復しないといけません。その中でアプリケーション処理とデータラインの上げ下げなどを必要とします。

PICに標準装備されているMSSPモジュールを使えば、ライン信号のコントロールなどをハードウェアに任せられますが、ピン番号を自由にするためには、通常のデジタルピンとしての扱いをしなければなりません。C言語ではタイミングを作る上で無理が生じるため、機械語を直接作れるアセンブラーで書いているわけです。

ただしI2CデバイスもTM16xxもクロックに合わせて信号を検出するしかけとなっているので、クロック間隔が多少不揃いでも一応動きます。それで構わなければタイミングはC言語でも大丈夫ですが、小さいPICモデルを使う際はメモリーの制約が厳しくなります。アセンブラーですと上手に書けば資源はごく少しで済みます。

MSSPを使わない勝手な自作プログラム(RYO=ロールユアオウンと呼ばれます)の場合、TM16xxxxでもI2CプロトコルでもデータラインのLOWへの引っ張り合いによる過大電流防止のため、若干の抵抗値挿入が必要なことは前回簡単に説明しました。

それが回路図中のRiとRtです。目的が単にそれだけなら100-300Ωで十分ですが、大きな値ですと別の現象が起きます。データラインだけを見ると次のような接続になるからです。

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

それぞれのデバイスのプルアップ抵抗(Ri2cとRtm)は、各ICに入っているのではなく、ブレークアウトモジュール上で配線されています。

以下に、これがどのように影響するのか、そしてこの手品のような共有方法のしかけはどうなっているのかを説明します。

 

Ri2cは次の写真のように4.7KΩ(472)で、VCCとSDAラインの間の抵抗値を測っても同じく4.7KΩです。

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

それに対しTM1637は10KΩ(103)が付けられています。

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

 

ところがVCCとDIOの間の抵抗値を測るとほぼ5KΩです。IC内部の回路が影響しているかと思われます。なのでRtmは5KΩとみなければならないでしょう。(ちなみにVCCとCLKの間を測るとそちらは10KΩですが。)

 

各デバイスのデータラインが次の表の左の状態欄のようになったとき、上の図中のI,P,T各点の電位がどうなるかを次の表で示します。

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

そこに先程のプルアップ抵抗値と、Ri=1.2KΩ、Rt=1.5KΩを付けた場合に、左の状態でのI,P,T各電位は次の様に計算されます。その結果各デバイスからのL信号がどう認識されるかは、下表の右欄のようになります。瞬間的な話なので信号の変形も起きるかもしれませんがここでは無視しています。

この実験では比較的ゆっくりにして、I2Cのクロックは50KHz、TM16xxのクロックは25KHzで行っています。もちろんこのアプリケーションの実用上はそれで問題ないですが。

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


そもそもLやHとみなされる電圧の範囲はデバイスにより少し異なりますが、大まかには次のようになっています。

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

バイスの通信に関する受信ピン(つまりRx)はシュミットトリガ入力が普通なので、しきい値外のL値がくるとヒステリシスにより前の値を維持します。つまり前の状態のHとみなされます。

PICではこの場合手作り通信のため、単にデジタル入力ピンとして扱われますので、普通のCMOS入力と同じく閾値外はどう扱われるかは不定です(MSSPでRxとして扱うときはシュミットトリガ入力です)。とはいえPICに到着する電圧が前の表のように閾値内ですから問題なくLに認識される筈です。

つまり、手品の種を明かせば「隣のデバイスは認識するが、さらに隣のデバイスは認識しないようにしている」つもりのわけです。

ロジックアナライザーで見ている限りはそのように動いており、エラーもおきません。コーディングでスイッチ部をバイパスして多数回テストをしても正常です。念のためにデバイスを別の個体と交換してみても大丈夫です。

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

 上はP点のデータですが、PICからI2CへNack(H)を送っている部分でダメ押しの確認をします。もしうまく動いていなければラインを共有しているTM1637がそこへAck(L)を入れてしまいますが、そうはならずNack(H)として記録されています。

そもそもTM1637は何の文字でも8ビットがくれば無条件にAckだけ出してしまいます。随分単純化されたプロトコルです。上に書いた理屈からいって、隣のPICからのデータは必ず受信しているわけなので、そこへはAckを出しているはずですね。

そこでT点で同じ信号を測ってみますと次のようになっています。

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

やはり、このT点ではロジアナがNackでなくAckと認識しています。ここまでは種明かしの理屈通りのようです。

しかし、この際は念のためにデータラインの電圧がどうなっているのかをオシロで確認してみることにします。

 

確認に都合のよいのは、PICがI2Cで磁気コンパスとやりとり(50KHz)して6バイトを受け取り、すぐ次にTM1637へ文字を送る(25KHz)箇所でしょう。全部のやりとりが混じっていますから。まずP点を見てみます。

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

各箇所の電位(右端のスケール)はほぼ想定通りのところに見えます。ただし、この低速なのに信号が変形しているところも見られます。

次にI点を観察します。

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

左半分は自分とPICのやりとり、右はPICとTM1637のやりとりで、電位は想定通りの3種類を行き来しています。前述のPICからのNack箇所に少し邪魔がはいっているようにもみえます。

さて、最後にT点で確認をします。

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

これをみると、TM1637は隣のPICからだけではなく、なんと一軒先のI2C磁気コンパスからの信号を検知して、いていちいちAckを送っているではありませんか!これはびっくりで予想外。

ということは、TM1637はLのしきい値が想定より高いようです(シュミット・トリガ―入力を使っていないかも)。このAckがI2C側のプロトコルを邪魔していないのは、タイミングも符号も同じだからでしょうね。また、1か所のNackがもしAckに変っても、毎回シングルショットで測定するこの使い方では大丈夫なのですが、この場合Nackの受け側の磁気コンパスには届いていませんから、何も問題ありませんね。

全体を通してデータの授受は全て正常なのですが、完璧にするには抵抗値をさらにいじるべきかとも考えてしまいます。試しにTM側を2KΩにあげてみるとTM1637の受け取るデータに直ちにエラーが発生してしまいますので、そういう実験はここでやめ。

 

結論として、この方法で共有してちゃんと動きますがすみずみまで完璧とはいえません。実験や手品としてはとてもよいと思うのですが、表示だけならまだしも、他のデバイスでの大事なところには採用できませんね。

ですのでこの連載の次回からは、I2C用の2つのピンとTM16xx用の2つのピンを元通り分けた形で記述していくことにします。そもそもアセンブラーでの単純でピンが自由なI2C接続をご紹介するのが本当の目的ですので。

ただし、Clockラインのピンを共有するのは何も問題ないですね!そうすれば1ピン減らせます。そちらにしようかな・・。

最後にピンを元通りに分けたプログラムを載せておきます。TM16xxのピンの移動はPORTtmという箇所でのEquation2か所を直すだけで、他はプログラム自体の変更は何もありません。このようにプログラムを作るとピンの移動だけでなくPORTの移動も容易ですね。


;U190829-I2C&TM16-V01s.asm		    		As of Aug. 31, 2019
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;										;
;  I2C and TM16xx protocol demonstration, using PIC12F1822 - V00  		;
;	        (c)2010-2019 Akira Tominaga, All rights reserved. 		;
;	  Major changes: 							;
;		V00: Initial version (Aug. 29, 2019)				;
;		V01: Finish I2C & TM16xx line-share				;
;	  Funtion								;
;		1. Demonstration of I2C and TM16xx protocols			;
;		2. Geomagnet of X,Y,Z axis are shown  on 4 digit display	;
;		  (1) XYZ values are shown sequentially on push of tact-switch	;
;		  (2) Hexadecimal values are shown, if Long-push after "PUSH"	;
;		  (3) Magnetic unit is 0.73 milli-Gauss*, i.e. 1370LSb/Gauss	;
;			*: mG(milli-Gauss) = 100 nT(nano-Tesla) = 10^(-7)Tesla	;
;		3. Provide PIC Assembler coding source for reuse		;
;	 	  (1) I2C interface source, using HMC5883L Geomagnetic Compass	;
;		  (2) TM163x interface source, for TM1637 Four Digit Display	;
;		  (3) Device-independent coding techniques and Macro examples	;
;	  Input/OUtput 								;
;		1. RA0 TM16xx-DIO (dio line) Output usually / Input temporarily	;
;		2. RA1 TM16xx-CLK (clk line) Output				;
;		3. RA2 LED Output						;
;		3. RA3 Tact Switch Input pulled-up 				;
;		4. RA4 I2C-SDA (data line) Output usually / Input temporarily	;
;		5. RA5 I2C-SCL (clock line) Output 				;
;	  Remarks								;
;		1. Clock = HFINTOSC (	16MHz)					;
;			Hence 1 step = 0.25 micro seconds			;
;		2. Logical PORT name is introduced, for device independence	;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
	list		p=12F1822      ; list directive to define processor
	#include	 ; processor 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'11001000'	; RA0,1,2,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'00001000'	; Weak Pull-up for RA 3
	movwf	WPUA		;  
;
	clrf	BSR		; Bank=0
	InitP			; Initialize ports
	endm
;
InitP	macro			; Initialize ports
	movlw	B'11111011'	;	
	movwf	PORTA
	endm
;		
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	I/O macros			;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; for I2C-Master protocol
I2Csnd	macro	slave,dataa,datalen
	movlw	slave
	movwf	I2Cadr
	movlw	dataa
	movwf	FSR0L
	movlw	datalen
	movwf	DataLen
	call	I2Csndr
	endm	
;
I2Crcv	macro	slave,dataa,datalen
	movlw	slave
	movwf	I2Cadr
	movlw	dataa
	movwf	FSR0L
	movlw	datalen
	movwf	DataLen
	call	I2Crcvr
	endm	
;
I2Cstat macro
	call	I2Cstar
	endm
;
I2Cstop	macro
	call	I2Cstpr
	endm
;
; For TM16xx-Master protocol
TMsendL	macro	tslit		; Send a literal to TM1637
	movlw	tslit
	call	TMsndLr
	endm
;
TMsendB	macro	tsbyte		; Send a Byte specified, to TM1637
	movf	tsbyte,W
	movwf	Sbyte
	call	TMsndBr
	endm
;
TMstart macro			; Start TM16xx lines
	call	TMstrtr
	endm
;
TMstop	macro			; Stop TM16xx lines
	call	TMstopr
	endm
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 	For general purpose	;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
LEDon	macro			; LED on
	goto	$+1
	bsf	PORTA,LED
	goto	$+1
	endm
;
LEDoff	macro			; LED off
	goto	$+1
	bcf	PORTA,LED
	goto	$+1
	endm
;
WaitSw	macro			; Wait for tact Sw pushed
	call	WaitSwr
	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		;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Udebug	macro 	Addr
	movf	Addr,W
	call	Udbgr
	endm
;
Trigger	macro				; DSO external trigger
	goto	$+1
	bsf	PORTC,Trig
	Mic25
	bcf	PORTC,Trig
	goto	$+1
	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				;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	Calculation macros		;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	Calculate Literal x Value and add it to answer (HAh&HAl)
LxAadd	macro	litV,multiA
	movf	multiA,W	; multiplier byte address in W
	movwf	Mctr		;  and set it to Mctr
	movlw	litV		; set Literal value into W
	call	LxAaddr	
	endm
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	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
;	DataA		; Data area : use Xh addr
	Xh
	Xl
	Zh
	Zl
	Yh
	Yl
	Bh		; Editing work area
	Bl		; Editing work area
;
;Areas for calculation LxA (Literal x byteArea)
	Mctr		; Multiplier counter from byteA
	Madded		; Multiplied value from Literal
	HAh		; Hecto Answer h(Decimal 100-9900)
	HAl		; Hecto Answer l(Decimal 0-99)
;
; For I2C protocols
	I2Cadr		; Destination 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		;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Dio	equ	0		; TM16xx Data line = DIO
;Clk	equ	1		; TM16xx Clock line =CLK
LED	equ	2		; RA2 = LED
Sw	equ	3		; RA3 = Tact Switch(pulled-up)
;Dl	equ	4		; I2C Data line = SDA
;Cl	equ	5		; I2C Clock line = SCL
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ***	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	0		; TM16xx Data line = DIO
Clk	equ	1		; TM16xx Clock line = CLK
LATtm	equ	LATA		; Latch for tm port
TRIStm	equ	TRISA		; TRIS for tm
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	Values & Characters	;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
DataA	equ 	Xh		; share Data address
Compass	equ	H'1E'		; HMC5883L I2C address
; Flags byte
NegaV	equ	1		; Value is negative
OflowV	equ	2		; Value overflow
HexD	equ	3		; Hexadecimal display requested
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 	For Debug		;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
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
;
	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
;
	call 	IniComr		; Set-up Compass HMC5883L
	call	IniTMr		; Set-up TM1638 display
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	Main program loop					;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Mainp	bcf	Flags,HexD   	; Clear Hex display request
	call	DspPUSH		; Show PUSH
;	Swith handling & selecting Decimal or Hexadecimal format
;	 ( Short-push for Decimal and Long-push for Hexadecimal)
	btfsc	PORTA,Sw	; Sw pushed ?
	goto	$-1		; No, go back
;
	Mil100	5		; when pushed, wait 0.5 second
	btfss	PORTA,Sw	;  still pushed on (Long Push) ?
	bsf	Flags,HexD	;   Yes, request Hex display
;
	btfss	PORTA,Sw	; Wait until Sw released
	goto	$-1		;   If still pushed, go back
;
	call	RdComr		; Read compass HMC5883L (Single measure.)
	call	DspXYZr		; Display X,Y,Z values on TM1637
	goto	Mainp		; Loop for next measurement
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	Compass HMC5883L set-up routine		;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
IniComr	equ	$
;Conf-regA = B011(#samples=8)110(75Hz@Cont-mode)00(no bias)=0x78
;Conf-regB = B000(±0.88Gauss)00000(must be cleared)=0x00
;Mode-reg = B00000(must be zeros)01(single measurement)=0x01
;
	movlw	DataA		; Point DataA
	movwf	FSR0L
	movlw 	H'00'		; Config byte A addres
	movwf	INDF0
	incf	FSR0L,F
	movlw	H'78'		; Config byte A contents
	movwf	INDF0
	incf	FSR0L,F
	movlw	H'00'		; Config byte B contents
	movwf	INDF0
;
	I2Csnd	Compass,DataA,3	; Send 3 bytes to Compass
	return
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 	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
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	Read compass HMC5883L into X,Z,Y bytes	;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
RdComr	equ	$
;  Use single measurement mode 0x01 to Mode Reg (adr 0x02)
;  Wait>6mS for data updated
;
;	Setting up data
	movlw	DataA
	movwf	FSR0L
	movlw	H'02'		; Mode reg addr of compass
	movwf	INDF0		; 
	incf	FSR0L,F		; 
	movlw	H'01'		; Specify Single measurement mode
	movwf	INDF0
;
	I2Csnd  Compass,DataA,2 ; Send to compass
	Milli	D'8'		; Wait ≧ 6mS
;Read compass
	I2Crcv Compass,DataA,6  ; Read data X,Z,Y
	return
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	Display X,Y,Z with 4 digits x 3 times	;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
DspXYZr equ	$
	movlw	Xh		; Set addr of X
	movwf	FSR1L		; and point it by FSR1L
	call	EdVdspr		; Show X value
;
	movlw	Yh		; Set addr of Y
	movwf	FSR1L		; and point it by FSR1L
	call	EdVdspr		; Show Y value
;
	movlw	Zh		; Set addr of Z
	movwf	FSR1L		; and point it by FSR1L
	call	EdVdspr		; Show Z value
;
	return
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	Edit value to 4digit values for display	;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
EdVdspr	movf	INDF1,W		; Get high byte
	movwf	Bh		; Set it to Bh
	incf	FSR1L,F		; Point low byte
	movf	INDF1,W		; Get low byte
	movwf	Bl		; Set it to Bl
;
	call	Hex2Dr		; Break out to 4 digits
	btfss	Flags,HexD	; If Hexa disp req'd, skip next
	call	Hex2Dec		; Hexadecimals to Decimals
;
	call	Ed2Seg		; Edit them to 7 Segs'
;
	btfsc	Flags,NegaV	; If value is negative
	goto	EdVnega		; show negative sign
EdVco	btfsc	Flags,OflowV	; If value overflow
	goto	EdVof		; show overflow by LED

EdVd	call	TMdspr		; and display them
;
	WaitSw
	LEDoff
	return
;
EdVnega	btfsc	Flags,HexD	; If Hexa disp req'd, 
	goto	EdVco		;  then do nothing
;
	movlw	H'40'		; Minus sign
	movwf	Dig1000		; to Dig1000
	goto	EdVco		; and goto Edv chek overflow
;
EdVof	LEDon
	goto 	EdVd
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	Break-out Hex 2 bytes to 4 Digits	;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Hex2Dr	movf	Bl,W		; Set Bl to W
	andlw	B'00001111' 	; Clear top half of W
	movwf	Dig1		; and set it to Dig1
;
	swapf	Bl,W		; Swap nibbles into W
	andlw	B'00001111' 	; Clear top half of W
	movwf	Dig10		; and set it to Dig10
;
	movf	Bh,W		; Set Bh to W
	andlw	B'00001111' 	; Clear top half of W
	movwf	Dig100		; Set Dig100
;
	swapf	Bh,W		; Swap nibbles into W
	andlw	B'00001111'	; Clear top half of W
	movwf	Dig1000		; Set Dig1000
;
	return
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	Hexadecimal to Decimal conversion	;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Hex2Dec equ	$
	bcf	Flags,NegaV	; Clear negative Value flag
	bcf	Flags,OflowV	; Clear overflow Value flag
;
; Check1:  Value negative or positive ?	
H2Dc1	btfss	Dig1000,3	; Check top bit of Hex V
	goto	H2Dc2		; If positive, goto next check
;
; Negative value process
	bsf	Flags,NegaV	; Show Value is minus
;	cgabge Digits positive by complement of H'FFFF'
	movlw	H'0F'
	xorwf	Dig1000,F
	xorwf	Dig100,F
	xorwf	Dig10,F
	xorwf	Dig1,F
;
; Check2: Value more than 4095 ?
H2Dc2	CalSO Dig1000,1,H2Dp	; If smaller goto processes
	bsf	Flags,OflowV	; Show that Value overflow
	clrf	Dig1000		; and clear Dig1000
;
; Processes: 
; Clear Hecto answer A
H2Dp	clrf	HAh
	clrf	HAl
;
; Perform A = A + Dig100x256 (Add 256xDig100 to Hecto A's)
;  by A=A+Ax128 + Ax128 in order to avoid  byte overflow,
;  as Literal≦255 (and A can be max 99 before 2nd adding)
H2Dp100 LxAadd	D'128',Dig100	; 
	LxAadd	D'128',Dig100	; 
;
; Perform A = A + Dig10x16
H2Dp10	LxAadd	D'16',Dig10	; Add 16xDig10 to HAh&l
;
; Perform A = A+ Dig1
H2Dp1	LxAadd	D'1',Dig1	; Add 1xDig1 to HAh&l
;
; Now, calculation Hexa to Hecto done
; Break out HAh to Dig1000 & Dig100,and HAl to Dig10 & Dig1
	movf	HAh,W		; Set HAh 
	movwf	Dig100		;  to Dig100
;
	clrf	Dig1000		; Clear Dig1000 to begin next
H2Dbhlp	CalSO	Dig100,D'10',H2Dbl	; If Dig100≦10,goto H2Dbl
	movlw	D'10'		; else Carry-over process
	subwf	Dig100,F
	incf	Dig1000,F
	goto	H2Dbhlp
;
H2Dbl	movf	HAl,W		; Set HAl
	movwf	Dig1		;  to Dig1
;
	clrf	Dig10		; Clear Dig10 to begin next	
H2Dbllp	CalSO	Dig1,D'10',H2Dend	; If Dig1<10,goto end
	movlw	D'10'		; else Carry-over process
	subwf	Dig1,F
	incf	Dig10,F
	goto	H2Dbllp
;
H2Dend	return
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	Edit digits to 7 segmeng x 4 		;
;	  for both Decimals and Hexadecimals	;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Ed2Seg	movlw	Dig1000		; Get top address
	movwf	FSR0L		; Set address
	movlw	4		; Number of digits
	movwf	Bytectr		; Set it to Byte counter
;
Ed2Slp	movf	INDF0,W
	call	TMser		; Translate to 7 seg
	movwf	INDF0		; Replace digit by it
	decfsz	Bytectr,F	; Done 4 digits?
	goto	Ed2Snxt		; No, go on
	return			; Yes, return
;
Ed2Snxt	incf	FSR0L,F		; Point next data
	goto	Ed2Slp		; and loop
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	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
	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
	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 			;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 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 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
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 	Displaying characters directly		;
;	 	as an example "PUSH"		;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
DspPUSH	TMstart			;
	TMsendL	H'40'		; Set data
	TMstop
	TMstart			; 
	TMsendL	H'C0'		; Set address zero
	TMsendL	H'73'		; P
	TMsendL	H'3E'		; U
	TMsendL	H'6D'		; S
	TMsendL	H'76'		; H
	TMstop
	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 top bit
	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 
	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			; 
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	Waiting for Sw pushed and released					;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
WaitSwr	goto	$+1		; Timing delay for PORTupadate
	btfsc	PORTA,Sw	; Wait until Sw on
	goto	$-1		; If off, go back
	btfss	PORTA,Sw	; Wait until released
	goto	$-1		; If still on, go back
	return
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	Multiply and  add result to HAh and HAl			;
;		called by LxAadd macro and			;
;	  	LitVsv x Mctr result to be added to HAh&l	;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
LxAaddr	movwf	Madded	 	; Set Literal as multiplied
	CalEO	Mctr,0,LxAend	; If Mctr=0, return
;
LxAlp	movf	Madded,W	; Multiplied into W
	addwf	HAl,F		; add it to HAl 
	CalSO	HAl,D'100',LxAnxt ; If HAl<100 then goto nxt
	movlw	D'100'		; Else, carry-over process
	subwf	HAl,F
	incf	HAh,F
;
LxAnxt	decfsz	Mctr,F		; If still Mctr,
	goto	LxAlp		; then loop
;
LxAend	return			; else 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	PORTA,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			; Blinking 25 times
	Udebug	Abendn			; Show Abend number
	goto	$
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;	End of program						;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	end

 次回にプログラム内容を説明するとして、今回はこのへんで。

 

©2019 Akira Tominaga, All rights reserved.