勝手な電子工作・・

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

PICアセンブラーで芸当(?) その1)磁気コンパス/磁界ロガー

連載中の新型レーザープロッターは、しばし新しく必要となったメカ部品の一部を手作り中です。少しずつやるために期間がかかりますので、その間に息抜きとしてPICアセンブラーでの別の話題を書いておきます。

下はPICの同じ2ピンに2つの異なる通信プロトコルを同居させているロジアナ出力です。そのうちI2C通信プロトコルはPICでも標準的なものですが、ここでは勝手なピンが使えるように、それぞれアセンブラーで手作りしています。

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

I2CとTM16xxプロトコルの同居

どうしてこんな共有ができるのかを詳しく説明したいと思います。

この説明のために、例題を作りました。精密磁気コンパス(HMC5883Lブレークアウトモジュール)で測定したXYZ各軸数値を、7セグメント4桁ディスプレイ(TM1637モジュール)に表示するというもの。

この連載ではまずこれを少し説明し、シリーズの後半で回路の意味とプログラム内容を詳しく説明する予定です。

この回路はブレッドボードですぐできます。

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

次のようにI2C接続の2線とTM16XXの2線を同居させています。毎度汚い手書きですみません。

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

I2CとTM16xxはどちらも2線式プロトコルですが、TM16xxではスレーブ・アドレスをもちません。TM16xxプロトコルでは、最初に送るのがコマンドとデータをわける1バイトで、上位2ビットの配列が3種に固定しているため、いかにも簡単に識別できそうに思えますが、残念ながらそれは不可能です。

bit送出順序に違いがあり、I2Cはビッグエンディアン(上位ビットから)、TM16xxはリトルエンディアン(下位ビットから)という困った違いがあります。そのためTM16xxでは最初に送出されるビットはデータ下位ビットとなり変化する値なのです。(なおバイト順序はどちらもビッグエンディアン(上位バイトから)です。)

したがって、2つのプロトコルをビット符号の論理だけで振り分けすることはできませんから、意図と違うデバイスが返事をしたりします。しかしそこをあきらめずに、ワイルドアイデアで勝手に実現してしまおうというのがこの連載です。

上の回路図でクロックライン(I2CではSCL、TM16xxではCLK)は、マスターからスレーブへの一方通行なのでとくに工夫は要りませんが、データライン(I2CではTM16xxではDIOとよぶ)は両方通行であり入出力の切り替えが要ります。

ご承知の方も多いと思いますが、どちらのプロトコルも基本的にH側(+側)にプルアップされていて、それをL側に引っ張ることでデータを伝えます。そのため、両方通行のデータラインでは双方が上か下に引っ張る競合が生じ得るため、普通は両方ともオープンコレクタ―回路として接続をします。そうしない場合(つまり直接アウトプットを出力する場合)は、片方がマイナスに引っ張り片方がプラスに引っ張るというまずい現象が起きて、過電流でデバイスマイコンを壊す恐れがあります。

そこで、直接アウトプットを出力する場合には、ピンの電流の限界値(PICならMax.25mA)を超えないよう制限抵抗を入れる必要があります。上の回路図でいうとRiとRtがそれです。PICからみると5Vを25mAに抑えたい場合は5V/25mA=200Ω、3Vの場合は120Ωとはなるのですが、相手のデバイスの制限は普通はもっと厳しいため、300Ωを入れておくのが妥当かと思います(瞬時なので200Ωでもよいかも。大きすぎると別の問題がでます)。プロトコルの混在ではなく単独でピンを使う場合にも直接出力の場合に必要な考慮点です。そうしないと高価なデバイスを原因不明で壊してしまう可能性がありますから。もちろんI2C通信でPIC内蔵のMSSPと特定ピンを使う場合にはその考慮は要りません。

ここでは逆にその抵抗値を活用するアイデアで、プロトコルを切り分けるという新案を考えて確認しました。それを後で詳しく説明していくことにして、まずは磁気コンパスが何かを説明します。ここでの測定は次の動画のとおりです。

 

youtu.be

数値はX,Y,Z軸の磁束密度を順次表示しています。ここでは単位は0.73mG(ミリガウス)としています。mGは100nT(ナノ・テスラ)で、東京付近の磁界の強さは45000nT前後、つまり450mG前後です。

付近に磁石や鉄などの邪魔者がなければ(X^2+Y^2+Z^2)^(1/2)がその場所の磁界の強さ(磁束のベクトルに正確に沿った値)です。この2乗和の平方根はどこを向けても一定の値になります。それが450mG/0.73=616あたりというわけです。もちろん地球の環境変化で少し変化しています。

敏感なセンサーなので、ブレッドボード内部の鉄の影響を受けるため、直接ブレッドボードに刺しませんが、近くにあるだけでも少し影響があります。

ここでの動画の数値例、X=349,Y=108,Z=-460をとって計算すると

(X^2+Y^2+Z^2)^(1/2)=584となりますから、先ほど推定した値616にたいへん近いことがわかります。ブレッドボードから遠ざければもっと近い値になるでしょう。この動画の例では2回目の表示にはヘキサデシマル(16進、Hexadecimal)で表示させています。”PULL"表示の際にスイッチを長押しすることで、その回はヘキサ表示するようにプログラムしてあります。

(X^2+Y^2+Z^2)^(1/2)が一定ということは、立体座標では球面となります。5年ほど前に同じセンサーでPICによる小さな携帯磁界ロガーを作ってみました。

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

中身はPICとSDカード、センサー、それに電池だけですのでだいぶガラガラです。

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

その後これにI2C液晶を加えたものも作りました。この携帯用機器での感度設定はレンジを広くするために今回の設定よりも鈍く、次の表でいえば下から3番目に設定してあります。(今回の例題回路では表の一番上の最大感度にしてあります)。下の表はデータシートにあるものです。

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

どれに設定してもこの磁界センサーはかなり敏感であり、箱を手でランダムにぐるぐる回すとロギング値は次のグラフのように球面となります。

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

このことを利用すると、付近の動く鉄とその方角が求められるなど考えつき、応用が色々できますがそちらの話はここでは割愛するとします。

 

冒頭のプロトコルの様子をもう少し詳しく説明すると次のようになっています。

最初はPICから磁気センサーに対して構成などの指定情報を書き込みます。そしてここでは各測定ごとにPICから単発測定であることを送り、センサーHMC5883Lから答をXh,Xl, Zh,Zl,Yh,Ylの順に6バイト受け取ります。つまりそれぞれの軸の測定値は16ビット構成です。

最初に送る信号はこれです。

f:id:a-tomi:20190830154142p:plain

SCLをH→L、続いてSDAをH→LとするのがI2Cプロトコルのスタート信号です。

続いて3バイトを送信して最後にSCL→H、SDA→Hとするのがストップ信号です。PIC各バイト送信ごとに、SDAを入力に切り替え、センサーからAck信号(L)の答をもらいます。最初に送っているのはスレーブアドレス(0x3C)です。(ちなみにArduinoではこれを1ビット右へずらして0x1Eとして扱います。)最後尾に書き込みを表すビット0を追加して3Cとなるものです。
ところがもし同じラインにTM16xxが同居していると、それがバイトごとに無条件で多少長いAckを戻すため、次のバイトの最初の信号が邪魔されて値が狂ってしまうことがありますので、それをうまく避ける工夫が要ります。ここではこの単純な回路だけで防いでいますがそのあたりは次回説明します。

次にPICから「読む」ためのコマンドを送って、デバイスから6バイトを受け取ります。

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

最初の5バイトに対してはそれぞれPICからAckを返しますが、最後の1バイトではNack(もういらないという合図)を送ります。Ackでもよいのですが、そうすると相手のデバイスはさらに送り続けるか、自分のアドレスカウンターの更新処理をします。

TM16xxを同居させるとここでも干渉をされて、NackをAckに換えてしまいますので、そういう邪魔もこの回路で防いでいます。

次に、PICはこのデータを加工し、TM16xxプロトコルで相手の内部アドレスと各軸の計測データを編集した4文字を7セグメントLED形式で送り出します。次の図で2番目のバイトがコマンド、3番目からがその文字です。

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

 前述のとおりTM16XXプロトコルではビットは逆順(リトルエンディアン)です。

プロトコルの同居による干渉を回路でどう防いでいるのかを次の回に書きたいと思います。また、PICプログラムについては後の回で詳しく説明するつもりですが、プログラム自体を次に一応つけておきます。ふだんはマクロなどはIncludeファイルとして外へ出して、ソースの行数を減らすことが多いですが、全てがわかるように全部をべた書きしておきます。一見多いですが、この小さなPICのリソースの3分の1程しか使わず十分な余裕があります。

 

;U190829-I2C&TM16xxCommonLines-V00s.asm		   	As of Aug. 29, 2019
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;										;
;  I2C and TM16xx wire-sharing demonstration, using PIC12F1822 - V00  		;
;	        (c)2010-2019 Akira Tominaga, All rights reserved. 		;
;	  Major changes: 							;
;		V00: Initial version (Aug. 29, 2019)				;
;	  Funtion								;
;		1. Demonstration of I2C and TM16xx protocols sharing same wires	;
;		   (Insert 1Kohm resistor to each device's data line to PIC.)	;
;		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 N/C							;
;		2. RA1 N/C							;
;		3. RA2 LED Output						;
;		3. RA3 Tact Switch Input pulled-up 				;
;		4. RA4 I2C-SDA (data line) Output usually / Input temporarily	;
;		       TM16xx-DIO (dio line) Output usually / Input temporarily	;
;		5. RA5 I2C-SCL (clock line) Output 				;
;		       TM16xx-CLK (clk 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	 ; p12F1822.incをタブに挟んで入れてください 
;                上記はProcessor specific variables __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 ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 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 ;Dio equ 4 ; TM16xx Data line = DIO ;Clk equ 5 ; TM16xx Clock line =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 4 ; TM16xx Data line = DIO Clk equ 5 ; 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 ; Change 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

 

 PIC12F1822はプログラムメモリーが2Kワードしかない8ピンマイコンですが、上記プログラム全体でそのうち656ワード(3割強)のみを使い、限られたデータメモリー128バイトも4分の1の32バイト(25%)しか使わず余裕十分です。

ではプログラムの説明は次回以降にして、今回はこのへんで。

 

©2019 Akira Tominaga, All rights reserved.