Subject:
MACRO expanding with gpasm
From:
Frederic Mantegazza ####@####.####
Date:
16 Nov 2001 09:06:28 -0000
Message-Id: <0111161006350F.05384@sunceng>
Hello,
For my first PIC project, I would like to make a rotary encoder interface
based on a 12C509, which will be read via I2C bus.
So, I started with the AN541, which describes the way to implement the I2C
slave protocol, without SSP, using a 16C54 (hope it won't be to hard to
switch to the 12C509).
But I have some troubles with gpasm. It seems that macro expanding doesn't
work for the BIT macro (all others work):
BIT MACRO label,bit,file ;Define a bit label
label EQU file<<8|bit ;(macro)
ENDM ;
Every time using this macro, the following error occurs:
i2c.asm:114:Error [113] Symbol not previously defined (B_C).
i2c.asm:55:Error [116] Value of symbol "label" differs on second pass
pass 1=0, pass 2=768
The line 55 is the line where the BIT macro is defined, and the 114 line
is:
BIT B_C,0,STATUS ;Carry
where the B_C label is defined.
Do you have any idea why this macro doesn't expand correctly ?
--
Frederic
LIST P=16C54, C=80, N=0, R=DEC
;
;******************************************************************************
;
; Program: I2C.ASM
; Revision Date:
; 12-12-95 Compatibility with MPASMWIN 1.30
;
;******************************************************************************
;
CPU EQU 1654
SIM EQU 0 ;Change timing constants for simulator
IF (CPU==1654) || (CPU==1655)
_RESVEC EQU 01FFH ;16c54 start address
ENDIF
IF CPU==1656
_RESVEC EQU 03FFH ;16C56 start address
ENDIF
IF CPU==1657
_RESVEC EQU 07FFH ;16C57 start address
ENDIF
;*** Reset Vector ********************************************************
ORG _RESVEC ;
RESVEC ;
GOTO INIT ;
;*************************************************************************
;**********************************************************************
;* Macros to set/clear/branch/skip on bits
;* These macros define and use synthetic "bit labels"
;* Bit labels contain the address and bit of a location
;*
;**********************************************************************
;* Usage Description
;* ----------------------- --------------------------------------
;* BIT label,bit,file ;Define a bit label
;* SEB label ;set bit using bit label
;* CLB label ;clear bit using bit label
;* SKBS label ;SKIP on bit set
;* SKBC label ;SKIP on bit clear
;* BBS label,address ;BRANCH on bit set
;* BBC label,address ;BRANCH on bit clear
;* CBS label,address ;CALL on bit set
;* CBC label,address ;CALL on bit clear
;*
;**********************************************************************
BIT MACRO label,bit,file ;Define a bit label
label EQU file<<8|bit ;(macro)
ENDM ;
SEB MACRO label ;Set bit
BSF label>>8,label&7 ;(macro)
ENDM ;
CLB MACRO label ;Clear bit
BCF label>>8,label&7 ;(macro)
ENDM ;
SKBS MACRO label ;Skip on bit set
BTFSS label>>8,label&7 ;(macro)
ENDM
SKBC MACRO label ;Skip on bit clear
BTFSC label>>8,label&7 ;(macro)
ENDM
BBS MACRO label,address ;Branch on bit set
BTFSC label>>8,label&7 ;(macro)
GOTO address ;(macro)
ENDM ;
BBC MACRO label,address ;Branch on bit clear
BTFSS label>>8,label&7 ;(macro)
GOTO address ;(macro)
ENDM
CBS MACRO label,address ;Call on bit set
CALL label>>8,label&7 ;(macro)
ENDM ;
CBC MACRO label,address ;Call on bit clear
CALL label>>8,label&7 ;(macro)
ENDM
;For Assembler portability
W EQU 0 ;For file,W
w EQU 0 ;For file,W
F EQU 1 ;For file,F
f EQU 1 ;For file,F
;*******************************************************************
;* REGISTER DECLARATIONS
;*******************************************************************
ORG 0 ;ORG for register declarations
ind RES 1 ;0=pseudo-reg 0 for indirect (FSR)
RTCC RES 1 ;1=real time counter
PC RES 1 ;2=PC
STATUS RES 1 ;3=status reg
;* Status reg bits
BIT B_C,0,STATUS ;Carry
BIT B_DC,1,STATUS ;Half carry
BIT B_Z,2,STATUS ;Zero
BIT B_PD,3,STATUS ;Power down
BIT B_TO,4,STATUS ;Timeout
BIT B_PA0,5,STATUS ;Page select (56/57 only)
BIT B_PA1,6,STATUS ;Page select (56/57 only)
BIT B_PA2,7,STATUS ;GP flag
FSR RES 1 ;4=file select reg 0-4=indirect address
PORTA RES 1 ;5=port A I/O register (4 bits)
PORTB RES 1 ;6=port B I/O register
IF (CPU==1655)||(CPU==1657)
PORTC RES 1 ;7=I/O port C on 16C54/56 only
ENDIF
;registers used by this code
I2CFLG RES 1 ;I2C flag reg
;--i2c flags-------------------------------------------------
BIT B_RD,0,I2CFLG ;Flag: 1=read
BIT B_UA,1,I2CFLG ;Flag: 0=reading unit address
BIT B_SA,2,I2CFLG ;Flag: 1=reading subabbress
BIT B_ID,3,I2CFLG ;Flag: 1=reading id
;------------------------------------------------------------
I2CREG RES 1 ;I2C I/O register
I2CSUBA RES 1 ;Subaddress
I2CBITS RES 1 ;I2C xmit bit counter
;**********************************************************************
;* 8 Pseudo registers accessed by sub-addresses 1-8
;* (address 0 accesses the ID string)
;* these are read-write registers
;**********************************************************************
I2CR0 EQU $ ;Sub-address 8
RES 1 ;8 pseudo registers
I2CR1 EQU $ ;Sub-address 1
RES 1
I2CR2 EQU $ ;Sub-address 2
RES 1
I2CR3 EQU $ ;Sub-address 3
RES 1
I2CR4 EQU $ ;Sub-address 4
RES 1
I2CR5 EQU $ ;Sub-address 5
RES 1
I2CR6 EQU $ ;Sub-address 6
RES 1
I2CR7 EQU $ ;Sub-address 7
RES 1
;Constants used by program
DEVICE_ADDRESS EQU 0D6H ;I2C device address (device_address+1 = read)
;*************************************************************************
;** PORTA DEFINITIONS
;** I2C interface uses PORTA
;** note SDA goes to A0 for code efficiency
;**
;*************************************************************************
TAREAD EQU B'11110111' ;TRISA register for SDA read
TAWRITE EQU B'11110110' ;TRISA register for SDA write
TAINIT EQU TAREAD ;Initial TRISA value
BIT B_SDA,0,PORTA ;I2C SDA (data) This must be bit 0!
BIT B_SCL,1,PORTA ;I2C SCL (clock)
;spare B_???,2,PORTA ;not used
;spare B_???,3,PORTA ;not used
;*************************************************************************
;**
;** Port B definition (Parallel out)
;**
;*************************************************************************
TBINIT EQU B'00000000' ;Port B tris (all output)
PBINIT EQU B'11111111' ;Port B init
;**********************************************************************
;* Macros to contain user POLL loop code.
;* These are implimented as macros to allow ease of modification,
;* especially in real-time applications. The functions could be coded as
;* in-line code or as subroutines depending on ROM/time tradeoffs.
;*
;* USER_MAIN: Decision or code to perform at idle time
;*
;* USER_Q: 'Quick' code for use during transfer - max 8 æs for full
;* IýC Spec. More than 4 æs may result in IýC retries (at
;* full spec speed.
;*
;* USER_MSG: Code to execute at receipt of IýC command.
;*
;**********************************************************************
USER_MAIN MACRO
;*** This would be user code for idle loop
ENDM
USER_Q MACRO
;*** This would be quick user code
ENDM
USER_MSG MACRO
;*** This would be user code to process a message
ENDM
USER_RECV MACRO
;*** This would be user code to process a received byte
;*** example code sends sub-address 0 to port b
BBC B_ID,_NXI_notid ;Channel 0! Bit set if INITIAL address was 0
MOVFW I2CREG ;get received byte
MOVWF PORTB ;and write it on portb
GOTO IN_CONT
_NXI_notid
ENDM
USER_XMIT MACRO
;*** This would be user code to prepare an output byte
;*** example code sends id string to output
BBC B_ID,_NXO_notid ;Channel 0! Bit set if INITIAL address was 0
CALL GETID ;get next byte from ID channel
GOTO OUT_CONT ;and send it
_NXO_notid
ENDM
;*************************************************************************
; START OF CODE
;*************************************************************************
ORG 0
;*************************************************************************
;* Device ID Table (must be at start)
;* TABLE FOR UNIT ID returns next char in W
;**********************************************************************
GETID
MOVFW I2CSUBA ;W=I2CSUBA
ANDLW 07H ;Limit to 8 locations
ADDWF PC,F
;**********************************************************************
;* Device ID text: read starting at sub-address 0
;**********************************************************************
RETLW 'P'
RETLW 'I'
RETLW 'C'
RETLW 'I'
RETLW '2'
RETLW 'C'
RETLW 0
RETLW 0
;**********************************************************************
;* I2C Device routines
;*
;* Enable must be HIGH, else state goes to 0
;* write is to me, read is from me.
;*
;* <============== first byte / subsequant writes =====> <end>
;*
;* SDA --| X-----X-----X-----X-----X-----X-----X-----X---X------| |--
;* |---X-----X-----X-----X-----X-----X-----X-----X---X******|--|
;* (bit) s 7 6 5 4 3 2 1 0 ackout
;* SCL -----| |--| |--| |--| |--| |--| |--| |--| |--| |--| |---
;* |--| |--| |--| |--| |--| |--| |--| |--| |--| |--|
;*
;* STATE: 0 1 2 3 2 3 2 3 2 3 2 3 2 3 2 3 2 4 5 6 2 3 0
;*
;* <============== subsequant reads ===================>
;*
;* SDA X-----X-----X-----X-----X-----X-----X-----X----X------X-----
;* --X-----X-----X-----X-----X-----X-----X-----X----X******X-----
;* (bit)ack 7 6 5 4 3 2 1 0 ackin
;* SCL --| |--| |--| |--| |--| |--| |--| |--| |--| |--| |--
;* |--| |--| |--| |--| |--| |--| |--| |--| |--| |--|
;*
;* STATE: 7 8 7 8 7 8 7 8 7 8 7 8 7 8 7 8 9 A 7 8
;*
;* <============== Final READ =========================>
;*
;* SDA X-----X-----X-----X-----X-----X-----X-----X----X-|**|-------
;* --X-----X-----X-----X-----X-----X-----X-----X----X*|--|
;* (bit)ack 7 6 5 4 3 2 1 0 ackin
;* SCL --| |--| |--| |--| |--| |--| |--| |--| |--| |---------
;* |--| |--| |--| |--| |--| |--| |--| |--| |--|
;*
;* STATE: 7 8 7 8 7 8 7 8 7 8 7 8 7 8 7 8 9 A 0 0
;*
;* STATE B is an ignore bit state for non-addressed bits
;* STATE C indicates last sample had ENA low
;* on rising edge of ENA, DATA LOW = low voltage, DATA&CLOCK low = RESET
;**********************************************************************
;I2C interface uses PORTA
;note SDA must be on PORTA,0 for code efficiency
;*************************************************************************
;** INIT
;** Hardware reset entry point
;**
;*************************************************************************
INIT ;Power-on entry
;*************************************************************************
;** RESET
;** software reset entry point
;**
;*************************************************************************
RESET ;Soft reset
MOVLW TAINIT ;Init ports
TRIS PORTA
MOVLW TBINIT
TRIS PORTB
MOVLW PBINIT
MOVWF PORTB
;******************************************************
; Main wait loop while idle. POLL loop should be called here
;
;******************************************************
I2CWAIT
CLRWDT ;Clear watchdog timer
CLB B_UA ;Init state flags
CLB B_SA ;Init state flags
CLB B_RD ;Init state flags
loop1
CLRWDT ;Clear watchdog timer
USER_MAIN ;Call user code while in idle state
SKBC B_SDA ;Wait for SDA&SCL=H
loop2
SKBS B_SCL ;
GOTO loop1 ; No longer valid to wait for start!
CLRWDT ;Clear watchdog timer
USER_MAIN ;Call user code while in idle state
;** wait for start **
SKBC B_SCL ;Clock has dropped
SKBC B_SDA ;Data dropped... Start!
GOTO loop2
;** START RECEIVED! --- wait for first bit!
loop3
BBS B_SDA,I2CWAIT ;Data raised before clock dropped -- abort
BBS B_SCL,loop3 ;Wait for clock low
NEXTBYTE
CLRWDT ;Clear watchdog timer
MOVLW 1 ;Init receive byte so bit falls off at end!
MOVWF I2CREG
;** Shift bits! -- external poll may be executed during low clock cycle only!
;* ENABLE line is checked for loss of enable ONLY during HIGH CLOCK
;*** CLOCK IS LOW -- DATA MAY CHANGE HERE
;*** We have at least 4 æs before any change can occur
loop4
USER_Q
loop4A
BBC B_SCL,loop4A ;Wait for clock high
;*** CLOCK IS HIGH -- SHIFT BIT - then watch for change
RRF PORTA,W ;Move RA0 into C
RLF I2CREG,F ;Shift in bit
SKPNC ;Skip if not done
GOTO ACK_I2C ;Acknowledge byte
BTFSC I2CREG,0 ;Skip if data bit was 0
GOTO ii_1 ;This bit was set
ii_0
BBC B_SCL,loop4 ;Wait for clock low
SKBS B_SDA ;Data low-high == stop
GOTO ii_0
I2CSTOP
USER_MSG ;process completed message!
GOTO I2CWAIT ;back to main loop
ii_1 BBC B_SDA,I2CWAIT ;Data high-low == start
BBC B_SCL,loop4 ;Wait for clock low
GOTO ii_1
ACK_I2C
BBC B_UA,ACK_UA ;Not addressed - check unit address
BBS B_SA,ACK_SA ;Reading secondary address
;****
;** Do what must be done with new data bytes here (before ACKloop)
;** Don't ack if byte can't be processed!
;****
;--------
USER_RECV
MOVLW 07H ;Register count
ANDWF I2CSUBA,f ;Limit register count
MOVLW I2CR0 ;Pseudo-registers
ADDWF I2CSUBA,W ;Offset from buffer start
INCF I2CSUBA, F ;Next sub-address
MOVWF FSR ;Indirect address
MOVFW I2CREG
MOVWF ind ;Put data into register
IN_CONT ;continue point for intercepted bytes
ACKloop
BBS B_SCL,ACKloop ;Wait for clock low
CLB B_SDA ;Set ACK
MOVLW TAWRITE
TRIS PORTA
CLB B_SDA ;Set ACK (just in case docs are wrong)
ACKloop2
USER_Q
BBC B_SCL,ACKloop2 ;Wait for clock high
ACKloop3
USER_Q
BBS B_SCL,ACKloop3 ;Wait for clock low
MOVLW TAREAD ;End ACK
TRIS PORTA
BBC B_RD,NEXTBYTE ;Skip if read (we were acking address only)
;*********************************************************************
; I2C Readback (I2C read request)
; Application specific code to get bytes to send may be added here.
; This routine gets data from location pointed to by I2CSUBA and
; sends it to I2C. Subsequent reads get sequential addresses. This version
; AND's the register # with 7 to limit to 8 registers (for speed). This
; could be modified to do a comparison to an ablolute number.
;
;*********************************************************************
NEXTOUT
;*** <<< PUT NEXT BYTE INTO I2CREG HERE NOW! >>> ***
USER_XMIT
MOVLW 07H ;Register count
ANDWF I2CSUBA,f ;Limit register count
MOVLW I2CR0 ;Pseudo-registers
ADDWF I2CSUBA,W ;Offset from buffer start
INCF I2CSUBA, F ;Next sub-address
MOVWF FSR ;Indirect address
MOVFW ind ;Get data from register
OUT_CONT
MOVWF I2CREG
;-- add code here to init I2CREG! when B_ID is clear!
MOVLW 8 ;Bit counter
MOVWF I2CBITS
;** OUT bits! -- external poll may be executed during low clock cycle, but
; may also be executed during high cycle if necessary.
;* ENABLE line is checked for loss of enable ONLY during HIGH CLOCK
;*** CLOCK IS LOW -- CHANGE DATA HERE FIRST!
;*** loop 1: data was 1
iiOUT_loop_1
RLF I2CREG,F ;Shift data out, MSB first
SKPNC ;1->0: change
GOTO iiOUT_1 ;Output another 1!
CLB B_SDA ;Output 0
MOVLW TAWRITE
TRIS PORTA
CLB B_SDA ;Set data (just in case docs are wrong)
iiOUT_0
CLRWDT ;Clear watchdog timer
USER_Q
iiOUT_loop_02
BBC B_SCL,iiOUT_loop_02 ;Wait for clock high
USER_Q
iiOUT_loop_03
BBS B_SCL,iiOUT_loop_03 ;Wait for clock low
DECFSZ I2CBITS, F ;Count bits
GOTO iiOUT_loop_0 ;Loop for last bit 0
MOVLW TAREAD ;Done with last bit 0... Set to 1 for ACK
TRIS PORTA
GOTO iiOUT_ack ;Get ACK
iiOUT_loop_0
RLF I2CREG,F ;Shift data out, MSB first
SKPC ;0->1: change
GOTO iiOUT_0 ;Output another 0!
MOVLW TAREAD ;Set to 1
TRIS PORTA
iiOUT_1
CLRWDT ;Clear watchdog timer
USER_Q
iiOUT_loop_12
BBC B_SCL,iiOUT_loop_12 ;Wait for clock high
USER_Q
iiOUT_loop_13
BBS B_SCL,iiOUT_loop_13 ;Wait for clock low
DECFSZ I2CBITS, F ;Count bits
GOTO iiOUT_loop_1 ;Loop for last bit 1
iiOUT_ack ;Get acknowledge
INCF I2CSUBA, F ;Next sub-address
iiOUT_loop_a2
BBC B_SCL,iiOUT_loop_a2 ;Wait for clock high
BBS B_SDA,I2CWAIT ;No ACK --- wait for restart!
;-- prepare next character here!
iiOUT_loop_a3
BBC B_SCL,NEXTOUT ;Wait for clock low - output next char!
BBS B_SDA,iiOUT_loop_a3 ;Watch out for new start condition!
GOTO I2CWAIT ;Stop received!
USER_READ ;user code to process data sent
GOTO I2CWAIT
;**********************************************************************
;* Unit address received - check for valid address
;*
;**********************************************************************
ACK_UA
SEB B_UA ;Flag unit address received
BTFSC I2CREG,0 ;Skip if data coming in
SEB B_RD ;Flag - reading from slave
MOVF I2CREG,W ;Get address
ANDLW 0FEH ;Mask direction flage before compare
XORLW DEVICE_ADDRESS ;Device address
BNZ I2CWAIT ;Not for me! (skip rest of message)
BBS B_RD,ACKloop ;Read - no secondary address
SEB B_SA ;Next is secondary address
GOTO ACKloop ;Yes! ACK address and continue!
;**********************************************************************
;* Secondary address received - stow it!
;* SA = 0 is converted to 128 to facilitate ID read
;**********************************************************************
ACK_SA
CLB B_SA ;Flag second address received
CLB B_ID
MOVFW I2CREG ;Get subaddress
SKPNZ ;Not 0
SEB B_ID ;Flag - id area selected
MOVWF I2CSUBA ;Set subaddress
GOTO ACKloop
END