;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.