PICアセンブラーで芸当(?) その1)磁気コンパス/磁界ロガー
連載中の新型レーザープロッターは、しばし新しく必要となったメカ部品の一部を手作り中です。少しずつやるために期間がかかりますので、その間に息抜きとしてPICアセンブラーでの別の話題を書いておきます。
下はPICの同じ2ピンに2つの異なる通信プロトコルを同居させているロジアナ出力です。そのうちI2C通信プロトコルはPICでも標準的なものですが、ここでは勝手なピンが使えるように、それぞれアセンブラーで手作りしています。
どうしてこんな共有ができるのかを詳しく説明したいと思います。
この説明のために、例題を作りました。精密磁気コンパス(HMC5883Lブレークアウトモジュール)で測定したXYZ各軸数値を、7セグメント4桁ディスプレイ(TM1637モジュール)に表示するというもの。
この連載ではまずこれを少し説明し、シリーズの後半で回路の意味とプログラム内容を詳しく説明する予定です。
この回路はブレッドボードですぐできます。
次のようにI2C接続の2線とTM16XXの2線を同居させています。毎度汚い手書きですみません。
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と特定ピンを使う場合にはその考慮は要りません。
ここでは逆にその抵抗値を活用するアイデアで、プロトコルを切り分けるという新案を考えて確認しました。それを後で詳しく説明していくことにして、まずは磁気コンパスが何かを説明します。ここでの測定は次の動画のとおりです。
数値は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による小さな携帯磁界ロガーを作ってみました。
中身はPICとSDカード、センサー、それに電池だけですのでだいぶガラガラです。
その後これにI2C液晶を加えたものも作りました。この携帯用機器での感度設定はレンジを広くするために今回の設定よりも鈍く、次の表でいえば下から3番目に設定してあります。(今回の例題回路では表の一番上の最大感度にしてあります)。下の表はデータシートにあるものです。
どれに設定してもこの磁界センサーはかなり敏感であり、箱を手でランダムにぐるぐる回すとロギング値は次のグラフのように球面となります。
このことを利用すると、付近の動く鉄とその方角が求められるなど考えつき、応用が色々できますがそちらの話はここでは割愛するとします。
冒頭のプロトコルの様子をもう少し詳しく説明すると次のようになっています。
最初はPICから磁気センサーに対して構成などの指定情報を書き込みます。そしてここでは各測定ごとにPICから単発測定であることを送り、センサーHMC5883Lから答をXh,Xl, Zh,Zl,Yh,Ylの順に6バイト受け取ります。つまりそれぞれの軸の測定値は16ビット構成です。
最初に送る信号はこれです。
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バイトを受け取ります。
最初の5バイトに対してはそれぞれPICからAckを返しますが、最後の1バイトではNack(もういらないという合図)を送ります。Ackでもよいのですが、そうすると相手のデバイスはさらに送り続けるか、自分のアドレスカウンターの更新処理をします。
TM16xxを同居させるとここでも干渉をされて、NackをAckに換えてしまいますので、そういう邪魔もこの回路で防いでいます。
次に、PICはこのデータを加工し、TM16xxプロトコルで相手の内部アドレスと各軸の計測データを編集した4文字を7セグメントLED形式で送り出します。次の図で2番目のバイトがコマンド、3番目からがその文字です。
前述のとおり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.