Re: RTC-clock under GEOS

From: John (john_at_ucc.gu.uwa.edu.au)
Date: 1999-10-11 11:38:51

;I2C communication routines by John West.

;This code will deal with any number of slave devices on the bus, but only
;one master.

;The hardware is very simple - two output bits and one input.  The diode
;gives you a pretend open-collector output.  Both lines require a pullup
;resistor (not shown).
;You can do with one output line and a bidirectional I/O line and no diode
;if you're really enthusiastic.  I couldn't be bothered.
;You'll have to supply your own routines to read and write these lines - the
;places to put code are marked with !!!

--------+
        |
SCL_OUT |----------------> SCL to the RTC
        |
SDA_IN  |--------+-------> SDA to the RTC
        |        |
SDA_OUT |---|<---+
        |
--------+

;------------------------------------------------------------------------------

	.module	i2c

	.include	"hardware.h"
	.include	"globals.h"

	.area	_DATA

i2c_err::	.blkb	1
i2c_bufptr::	.blkw	1
i2c_tmp:	.blkb	2

	.area	_CODE

;Idle state is after a stop condition, with SDA and SCL both high.
;Active state is after a start condition, with SDA high and SCL low.

i2c_init::
;Init the I2C bus

			jsr	stop
			jsr	stop
			jsr	stop
			rts

i2c_read::
;Read bytes from an I2C device.  Device address in A, subaddress in Y,
;number of bytes in X, address of buffer in i2c_bufptr.

			sei
			jsr	stop
			lda	#0
			sta	i2c_err
			pha
			and	#$fe
			jsr	start
			jsr	sendbyte
			jsr	readack
			tya
			jsr	sendbyte
			jsr	readack
			jsr	start
			pla
			ora	#1
			jsr	sendbyte
			jsr	readack
			ldy	#0
	1$:		jsr	readbyte
			sta	(i2c_bufptr),y
			inc	i2c_bufptr
			dex
			beq	2$
			jsr	sendack
			jmp	1$
	2$:		jsr	stop
			cli
			rts

i2c_write::
;Write a byte to an I2C device.  Device address in A, subaddress in Y,
;data to be written in X.  One attempt.

			sei
			call	i2c_init
			and	#$fe
			jsr	start
			jsr	sendbyte
			jsr	readack
			tya
			jsr	sendbyte
			jsr	readack
			txa
			jsr	sendbyte
			jsr	readack
			jsr	stop
			cli
			rts

readbyte:
;Read a byte into A.  Assumes the bus is in the active state.

			stx	i2c_tmp
			ldx	#8
	1$:		jsr	readbit
			dex
			bne	1$
			ldx	i2c_tmp
			rts

readbit:
;Shift A left one bit and read a bit into A[0]

			asl	a
			jsr	sclset
	;!!! Read the SDA line and jump to 1$ if it is 0.
			ora	#1
	1$:		jsr	sclres
			rts

sendbyte:
;Send the byte in A.  Assumes the bus is in the active state.

			sta	i2c_tmp
			stx	i2c_tmp+1
			ldx	#8
	1$:		jsr	sendbit
			dex
			bne	1$
			lda	i2c_tmp
			ldx	i2c_tmp+1
			rts

sendbit:
;Send the bit in A[7].  Returns with A shifted one bit left (ready for
;sending the next bit).  Assumes the bus is in the active state.

			lsr	a
			bcc	1$
			jsr	sdaset
			jr	2$
	1$:		jsr	sdares
	2$:		jsr	sclset
			jsr	sclres
			jsr	sdaset
			rts

readack:
;Read an ACK (if the slave has sent it).  Sets I2C_ERR if the slave
;doesn't ACK.

			sta	i2c_tmp
			jsr	readbit
			and	#1
			beq	1$
			lda	#1
			sta	i2c_err
	1$:		lda	i2c_tmp
			rts


sendack:
;Send an ACK.  Assumes the bus is in the active state.

			jsr	sdares
			jsr	sclset
			jsr	sclres
			jsr	sdaset
			rts

start:
;Send an I2C start condition.  Can be called from idle or active state.

			jsr	sclset
			jsr	sdares
			jsr	sclres
			rts

stop:
;Send an I2C stop condition.  Assumes the bus is in the active state, and
;puts it in the idle state.

			jsr	sdares
			jsr	sclset
			jsr	sdaset
			rts

sdaset:
;!!! Puts a '1' on the SDA line

sdares:
;!!! Puts a '0' on the SDA line

sclset:	
;!!! Puts a '1' on the SCL line

sclres:
;!!! Puts a '0' on the SCL line


;Month, dayofmonth, and so on all start at 0. (so January is month 0, the
;first day of the month is day 0).
;Year4 is the bottom two bits of the year (the number of years since the
;last leap year).  This is going to fail in 2100, but I plan to be dead by
;then.  The year calculation can only give 28 years after 1996 anyway (but
;the calendar repeats in a 28 year cycle, so it doesn't matter).
;1996 is used as the base year because that's when I originally wrote the
;code.

;The RTC's values are all in BCD.  Its date values are 1-based, but my date
;code expects 0-based (I convert as I read them).

;Here's the RTC initialization routine: it starts the clock running and
;sets up a 2Hz interrupt.

rtc_init::
			lda	#rtc_addr
			ldy	#rtc_status
			ldx	#%00000100
			jsr	i2c_write
			ldy	#rtc_alarm
			ldx	#%11001001
			jsr	i2c_write
			ldy	#rtc_timer
			ldx	#0x50
			jsr	i2c_write
			rts

;And to acknowledge the interrupt...
rtc_ack_irq::
			lda	#rtc_addr
			ldy	#rtc_status
			ldx	#%00000100
			jsr	i2c_write
			ret

;The evil evil date calculations:

;calculate day of year from dayofmonth, month, and year4.
;Jan 1 is day 0.
;dayofyear = dayofmonth + 28 * month + magic[month]
;magic[]=[0,3,3,6,8,11,13,16,19,21,24,26] if year4!=0
;magic[]=[0,3,4,7,9,12,14,17,20,22,25,27] if year4=0
;magic[] is the accumulated number of days beyond 28 in each month.

;Calculate the day of week of Jan 1 this year from dayofyear 
;and dayofweek.
;result in A.  Sunday is day 0.
;dayofweekJan1 = (371 + dayofweek - dayofyear) % 7
;371 is added to make sure no negative numbers appear.  It is a multiple of
;7, so it can't affect the result.  (my 32 bit arithmetic routines are all
;unsigned)

;calculate years since 1996 from dayofweekJan1 and year4.
;1996 is year 0.
;years since 1996 = year4 + 4(((dayofweekJan1 + magic[year4]) * 3) % 7)
;magic[]=[6,4,3,2]
;magic[] is real magic.  The whole calculation is magic.  I have no
;idea how it works.

;calculate days since 1996 from date_year and date_dayofyear.
;dayssince1996 = 365*yearssince1996 + dayofyear + (yearssince1996 + 3) / 4
;Jan 1 1996 is day 0.
-
This message was sent through the cbm-hackers mailing list.
To unsubscribe: echo unsubscribe | mail cbm-hackers-request@dot.tcm.hut.fi.

Archive generated by hypermail 2.1.1.