$TITLE (Demo program for PCD8584 I2C-routines)
$PAGELENGTH(40)
;Program displays on the LCD display the time (with PCF8583).
;Dots on LCD display blink every second.
;On the LED display the values of the successive analog
;input channels are shown.
;Program reads analog channels of PCF8591P.
;Channel number and channel value are displayed successively.
;Values are displayed on LCD and LED display on I2C demo board.
;
PUBLIC SLAVE_ADR,I2C_CLOCK,PCD8584
EXTRN CODE(I2C_INIT,INT0_SRV,START)
EXTRN BIT(I2C_END,DIR)
EXTRN DATA(BASE,NR_BYTES,IIC_CNT,SLAVE)
;
;
;Define used segments
USER SEGMENT CODE ;Segment for user program
RAMTAB SEGMENT DATA ;Segment for table in internal RAM
RAMVAR SEGMENT DATA ;Segment for variables
;
RSEG RAMVAR
STACK: DS 20 ;Stack area (20 bytes)
PREVIOUS: DS 1 ;Store for previous seconds
CHANNEL:DS 1 ;Channel number to be sampled
AN_VAL: DS 1 ;Analog value sampled channel
CONVAL: DS 3 ;Converted BCD value sampled channel
;
CSEG AT 00H
LJMP MAIN ;Reset vector
;
CSEG AT 03H ;INT0/
LJMP INT0_SRV ;Vector I2C-interrupt
;
;
RSEG USER
;Define I2C clock, own slave address and address for main processor
SLAVE_ADR EQU 55H ;Own slaveaddress is 55h
I2C_CLOCK EQU 00011100B ;12.00MHz/90kHz
PCD8584 EQU 0000H ;Address of PCD8584. This must be an EVEN number!!
;Define addresses of I2C peripherals
PCF8583R EQU 10100011B ;Address PCF8583 with Read active
PCF8583W EQU 10100010B ;Address PCF8583 with Write active
PCF8591R EQU 10011111B ;Address PCF8591 with Read active
PCF8591W EQU 10011110B ;Address PCF8591 with Write active
PCF8577W EQU 01110100B ;Address PCF8577 with Write active
SAA1064W EQU 01110110B ;Address SAA1064 with Write active
;
MAIN: MOV SP,#STACK-1 ;Define stack pointer
;Initialise 80C31 interruptregisters for I2C interrupt (INT0/)
SETB EX0 ;Enable interrupt INT0/
SETB EA ;Set global enable
SETB PX0 ;Priority level is '1'
SETB IT0 ;INT0/ on falling edge
;Initialise PCD8584
CALL I2C_INIT
;
MOV CHANNEL,#00 ;Set AD-channel
;
;Time must be read from PCD8583.
;First write word address and control register of PCD8583.
SETB DIR ;DIR='transmission'
MOV BASE,#TABLE ;Start address I2C data
MOV NR_BYTES,#02H ;Send 2 bytes
MOV SLAVE,#PCF8583W
CLR A
MOV TABLE,A ;Data to be sent (word address).
MOV TABLE+1,A ; " (control byte)
CALL START ;Start transmission.
FIN_1: JNB I2C_END,FIN_1 ;Wait till transmission finished
;Send word address before reading time
REPEAT: SETB DIR ;'transmission
MOV BASE,#TABLE ;I2C data
MOV SLAVE,#PCF8583W
MOV A,#01
MOV NR_BYTES,A ;Send 1 byte
MOV TABLE,A ;Data to be sent is '1'
CALL START ;Start I2C transmission
FIN_2: JNB I2C_END,FIN_2 ;Wait till transmission finished
;
;Time can now be read from PCD8583. Data read is
;hundredths of sec's, sec's, min's and hr's
CLR DIR ;DIR='receive'
MOV BASE,#TABLE ;I2C table
MOV NR_BYTES,#04; 4 bytes to receive
MOV SLAVE,#PCF8583R
CALL START ;Start I2C reception
FIN_3: JNB I2C_END,FIN_3 ;Wait till finished
;
;Transfer data to R2...R5
MOV R0,#TABLE ;Set pointers
MOV R1,#02H ;Pointer R2
TRANSFER:MOV A,@R0
MOV @R1,A
INC R0
INC R1
DJNZ NR_BYTES,TRANSFER
MOV A,R5 ;Mask of hour counter
ANL A,#3FH
MOV R5,A
;
;Data must now be displayed on LCD display.
;First minutes and hours (in R4 and R5) must be
;converted from BCD to LCD segment data.The segment data
;will be transferred to TABLE. R0 is pointer to table
MOV R0,#TABLE
MOV @R0,#00H ;Control word for PCF8577
INC R0
CALL CONV
;
;Switch on dp between hours and minutes
ORL TABLE+3,#01H
;If lsb of seconds is '0' then switch on dp.
MOV A,R3 ;Get seconds
RRC A ;lsb in carry
JC PROCEED
ORL TABLE+1,#01H;switch on dp
;
;Now the time (hours,minutes) can be displayed on the LCD
PROCEED:
SETB DIR ;Direction 'transmit'
MOV BASE,#TABLE
MOV NR_BYTES,#05H
MOV SLAVE,#PCF8577W
CALL START ;Start transmission
;
FIN_4: JNB I2C_END,FIN_4
JMP ADCON ;Proceed with AD-conversion part
;
;*****************************************************************
;Routines used by clock part of demo
;
;CONV converts hour and minute data to LCD data and stores
;it in TABLE.
CONV: MOV DPTR,#LCD_TAB ;Base for LCD segment table
MOV A,R5 ;Hours to accu
SWAP A ;Swap nibbles
CALL LCD_DATA ;Convert 10's hours to LCD data in table
MOV A,R5 ;Get hours
CALL LCD_DATA
MOV A,R4 ;Get minutes
SWAP A
CALL LCD_DATA ;Convert 10's minutes
MOV A,R4
CALL LCD_DATA ;Convert minutes
RET
;
;LCD_DATA gets data from segment table and stores it in TABLE
LCD_DATA:ANL A,#0FH ;Mask off LS-nibble
MOVC A,@A+DPTR ;Get LCD segment data
MOV @R0,A ;Save data in table
INC R0
RET
;
;LCD_TAB is conversion table for LCD
LCD_TAB:
DB 0FCH,60H,0DAH; '0','1','2'
DB 0F2H,66H,0B6H; '3','4','5'
DB 3EH,0E0H,0FEH; '6','7','8'
DB 0E6H ; '9'
;
;*******************************************************************
;
;
;These part of the program reads an analog input-channel.
;Displaying is done on the LED-display
;On odd-seconds the channel number will be displayed.
;On even-seconds the analog value of this channel is displayed
;Then the next channel is displayed.
;
ADCON: MOV A,R3 ;Get seconds
RRC A ;lsb to carry
JNC NEW_MEAS ;Even seconds; do a measurement on the current channel
;
;Display and/or update channel
RLC A ;Restore accu
CJNE A,PREVIOUS,NEW_CH ;If new seconds, update channel number
JMP DISP_CH
NEW_CH: INC CHANNEL
MOV A,CHANNEL ;If channel=4 then channel:=0
CJNE A,#04,DISP_CH
MOV CHANNEL,#00
DISP_CH:MOV PREVIOUS,R3 ;Update previous seconds
MOV A,CHANNEL ;Get segment value of channel
MOV DPTR,#LED_TAB
MOVC A,@A+DPTR
;
MOV R0,#TABLE ;Fill table with I2C data
MOV @R0,#00 ;SAA1064 instruction byte
INC R0
MOV @R0,#77H ;SAA1064 control byte
INC R0
MOV @R0,A ;Channel number
CLR A
INC R0
MOV @R0,A ;Second digit
INC R0
MOV @R0,A ;Third digit
INC R0
MOV @R0,A ;Fourth byte
;
SETB DIR ;I2C transmission of channel number
MOV BASE,#TABLE
MOV NR_BYTES,#06H
MOV SLAVE,#SAA1064W
CALL START
;
FIN_5: JNB I2C_END,FIN_5
JMP REPEAT ; Repeat clock and AD cycle again
;
;
;Measure and display the value of an AD-channel
NEW_MEAS: CALL AD_VAL ;Do measurement
;Wait till values are available
FIN_6: JNB I2C_END,FIN_6
;Relevant byte in TABLE+1. Transfer to AN_VAL
MOV R0,#TABLE+1
MOV AN_VAL,@R0
MOV A,AN_VAL ;Channel value in accu for conversion
;AN_VAL is converted to BCD value of the measured voltage.
;Input value for CONVERT in accu
;Address for MSByte in R1
MOV R1,#CONVAL
CALL CONVERT
;Convert 3 bytes of CONVAL to LED-segments
MOV DPTR,#LED_TAB ;Base of segment table
MOV R0,#CONVAL
CALL SEG_LOOP
;Display value of channel to LED display
CALL LED_DISP
FIN_8: JNB I2C_END,FIN_8 ;Wait till I2C transmission is
JMP REPEAT ;Repeat clock and AD cycle
;
;****************************************************************
;Routines used for AD converter.
;
;AIN reads an analog values from channel denoted by CHANNEL.
;Send controlbyte:
AD_VAL: SETB DIR ;I2C transmission
MOV R0,#TABLE ;Define control word
MOV @R0,CHANNEL
MOV BASE,#TABLE ;Set base at table
MOV NR_BYTES,#01H ;Number of bytes to be send
MOV SLAVE,#PCF8591W ;Slave address PCF8591
CALL START ;Start transmission of controlword
FIN_7: JNB I2C_END,FIN_7 ;Wait until tranmission is finished
;Read 2 data bytes from AD-converter
;First data byte is from previous conversion and not
;relevant
CLR DIR ;I2C reception
MOV BASE,#TABLE ;Bytes must be stored in TABLE
MOV NR_BYTES,#02H; Receive 3 bytes
MOV SLAVE,#PCF8591R ;Slave address PCF8591
CALL START
RET
;
;LED_DISP displays the data of 3 bytes from address CONVAL
LED_DISP:
ORL CONVAL,#80H ;Set decimal point
MOV R0,#TABLE
MOV R1,#CONVAL
MOV @R0,#00 ;SAA1064 instruction byte
INC R0
MOV @R0,#01110111B ;SAA1064 control byte
INC R0
MOV @R0,#00 ;First LED digit
INC R0
CALL GETBY ;Second digit
CALL GETBY ;Third digit
CALL GETBY ;Fourth digit
SETB DIR ;I2C transmission
MOV BASE,#TABLE
MOV NR_BYTES,#06
MOV SLAVE,#01110110B
CALL START ;Start I2C transmission
RET
;
;CONVERT calculates the voltage of the analog value.
;Analog value must be in accu
;BCD result (3 bytes) is stored from address stored in R1
;Calculation: AN_VAL*(5/256)
CONVERT:MOV B,#05
MUL AB
;b2..b0 of reg. B : 2E+2..2E0
;b7..b0 of accu : 2E-1..2E-8
MOV @R1,B ;Store MSB (10E0-units)
INC R1
MOV @R1,#00 ;Calculate 10E-1 units (10E-1 is 19h)
TEN_CH: CJNE A,#19H+03H,V1 ;Check if accu <= 0.11
JMP TENS ;accu=0.11; update tens
V1: JC NX_CON ;accu<0.11; update hundreds
TENS: CLR C ;Calculate new value
SUBB A,#19H
INC @R1 ;Update BCD byte
JMP TEN_CH
;Correction may be neccessary. With 8 bits '0.1' is in fact 0.0976.
;A digit of '0A' may appear. Correct this by decrementing the digit.
;The intermediate result result must be corrected with 10*(0.1-0.0976)
;This is 06H
NX_CON: CJNE @R1,#0AH,PROC_CON ; If digit is '0A' then correct
DEC @R1
ADD A,#19H
PROC_CON:INC R1
MOV @R1,#00 ;Calculate 10E-2 units
HUND: CJNE A,#03H,V2 ;Check if accu <= 10E-2
JMP HUNS ;accu=10E-2; update hundreds
V2: JC FINISH ;accu<10E-2; conversion finished
HUNS: CLR C ;Calculate new value
SUBB A,#03H
INC @R1 ;Update BCD byte
JMP HUND
FINISH: CJNE @R1,#0AH,FIN ;Check if result is '0A'. Then correct.
DEC @R1
FIN: RET
;
;CALLBY tranfers byte from @R1 to @R0
GETBY: MOV A,@R1
MOV @R0,A
INC R0
INC R1
RET
;
;SEG_LOOP converts 3 values to segment values.
;R0 contains address of source and destination
;DPTR contains base of table
SEG_LOOP: MOV R1,#03 ;Loop counter
INLOOP: MOV A,@R0 ;Get value to be displayed
MOVC A,@A+DPTR ;Get segment value from table
MOV @R0,A ;Store segment data
INC R0
DJNZ R1,INLOOP
RET
;
;
;LED_TAB is conversion table for BCD to LED segments
LED_TAB:
DB 7DH,48H,3EH ; '0','1','2'
DB 6EH,4BH,67H ; '3','4','5'
DB 73H,4CH,7FH ; '6','7','8'
DB 4FH ; '9'
;
;************************************************************
;
RSEG RAMTAB
TABLE: DS 10
;
END