Code:
$nocompile
'********* Initialise USI for TWI Slave mode ****************************
'---------------------------------------------------------------
'Subroutine: Usi_twi_slave_initialise
'Purpose: Initialise USI for TWI Slave mode
' set I2C Address to TWI_ownAddress
'Result:
'---------------------------------------------------------------
Usi_twi_slave_initialise:
Twi_slaveaddress = Twi_ownaddress / 2
SBI PORT_USI, PIN_SCL 'PORT_USI |= (1<<PORT_USI_SCL) // Set SCL high
SBI PORT_USI, PIN_SDA 'PORT_USI |= (1<<PORT_USI_SDA) // Set SDA high
SBI DDR_USI, PIN_SCL 'DDR_USI |= (1<<PORT_USI_SCL) // Set SCL as output
CBI DDR_USI, PIN_SDA 'DDR_USI &= ~(1<<PORT_USI_SDA) // Set SDA as input
Usicr = Usicr_start_mode 'USICR = Usicr_start_mode // Enable Start Condition Interrupt. Disable Overflow Interrupt
Usisr = Usisr_isr_start 'USISR = Usisr_isr_start // Clear all flags and reset overflow counter
Usi_twi_overflow_state = Usi_start_condition_mode 'USI_TWI_Overflow_State = Usi_start_condition_mode
Return
'********* ISR USI_START (Interrupt Service Routine )****************************
'---------------------------------------------------------------
'Subroutine: _isr_USI_START
'Purpose: Usi start condition ISR
' Detects the USI_TWI Start Condition and intialises the USI
' for reception of the "TWI Address" packet.
'Note: Start Condition Interrupt Flag will _only_ be cleared by writing
' a logical one to the USISIF bit.
' A start condition interrupt will wakeup the processor from all sleep modes.
' Corrected the STOP CONDITION BUG in AVR312 => while ((PIN_USI & (1<<PORT_USI_SCL)) & !(tmpUSISR & (1<<USIPF)))
'Stack use: 2 byte registers + 2 byte address
'---------------------------------------------------------------
_isr_usi_start:
push r24
in r24, SREG
push r24
'Usi_start_flag ist 1 wenn eine start condition empfangen wurde
LDI r24, 1
sts {Usi_start_flag}, r24 'detect _isr_USI_START in main loop
'Die State Maschine wird auf CheckAdress gesetzt damit in der_isr_usi_ovf die Adresse geprüft wird
LDI r24, Usi_check_address '//Set default starting conditions for new TWI package
sts {Usi_twi_overflow_state}, r24 'USI_TWI_Overflow_State = Usi_check_address
CBI Ddr_usi,Pin_sda 'DDR_USI &= ~(1<<PORT_USI_SDA) // Set SDA as input
_isr_usi_loop: 'while (PIN_USI & (1<<PORT_USI_SCL))
IN R24, Pin_usi '//wait until SCL is LOW, avoid counting the first level change
sbrs r24, Pin_sda ' if (PIN_USI & (1<<PORT_USI_SDA))...
rjmp _isr_usi_no_stop
'wenn ein Stop empfangen wird, USI zurücksetzen und ISR verlassen
ldi r24, Usicr_start_mode '//... a Stop condition arises and ...
!out USICR, r24 'Usicr =Usicr_start_mode
rjmp _isr_usi_end '//... then leave the interrupt to prevent waiting forever.
'ansonsten wird USI auf start gesetzt -> Das empfangene Adress Byte wird in das USIDR geschoben
_isr_usi_no_stop:
sbrc r24, Pin_scl
rjmp _isr_usi_loop
ldi r24, Usicr_isr_start 'USICR = Usicr_isr_start
!Out USICR , R24
_isr_usi_end:
ldi r24, Usisr_isr_start 'USISR = Usisr_isr_start
!Out USISR , R24
pop r24
!Out SREG , R24
pop r24
Return 'RETI
'********* ISR USI_OVF (Interrupt Service Routine )****************************
'---------------------------------------------------------------
'Subroutine: _isr_USI_OVF
'Purpose: USI counter overflow ISR
' Handels all the comunication.
' Is disabled only when waiting for new Start Condition.
'Stack use: 5 byte registers + 2 byte address
'---------------------------------------------------------------
_isr_usi_ovf:
push r1
in r1, SREG
push r1
eor r1, r1 'R1 = 0 !
push r24
push r30
push r31
'switch (USI_TWI_Overflow_State)
lds r24, {Usi_twi_overflow_state}
'// ---------- Address mode ----------
'// Check address and send ACK (and next USI_SLAVE_SEND_DATA) if OK, else reset USI.
'case USI_SLAVE_CHECK_ADDRESS:
'prüfen, ob state machine auf CheckAdress steht
Isr_ovf_slave_check_address: 'r24 = Usi_twi_overflow_state
cpi r24, Usi_check_address 'case Usi_check_address ?
brNe Isr_ovf_check_rep_from_send_data 'ansonsten nächsten status prüfen
'if ((USIDR == 0) || (( USIDR>>1 ) == Twi_slaveaddress)) 'check also (TWI-ADDRESS==0)
in r24, USIDR
!and r24, r24
breq Isr_ovf_get_valid_address 'we get a read/write address
in r24, USIDR 'Die Soll- und ist-Adresse werden in r30 bzw r24 geladen...
lsr r24
lds r30, {Twi_slaveaddress}
cp r24, r30 '...dort verglichen, wenn sie ungleich sind -> reset usi, exit isr ; ansonsten gehts weiter
brne Isr_ovf_set_usi_start_cond_mode 'get a invalid address -> SET_USI_TO_TWI_START_CONDITION_MODE() + BREAK
Isr_ovf_get_valid_address:
'if ( USIDR & &H01 )
sbis USIDR, 0
rjmp Isr_ovf_get_write_address
'//we get a read address
ldi r24, Usi_send_data
sts {Usi_twi_overflow_state}, r24 'USI_TWI_Overflow_State = USI_SLAVE_SEND_DATA
rjmp Isr_ovf_set_usi_to_send_ack 'SET_USI_TO_SEND_ACK() + BREAK
'//we get a write address
Isr_ovf_get_write_address:
ldi r24, Usi_request_data
sts {Usi_twi_overflow_state}, r24 'USI_TWI_Overflow_State = USI_SLAVE_REQUEST_DATA
RJMP Isr_ovf_set_usi_to_send_ack 'SET_USI_TO_SEND_ACK() + BREAK
'// ----- Master write data mode ------
'// Check reply and goto USI_SLAVE_SEND_DATA if OK, else reset USI.
'case USI_SLAVE_CHECK_REPLY_FROM_SEND_DATA:
Isr_ovf_check_rep_from_send_data: 'r24 = Usi_twi_overflow_state
cpi r24, Usi_check_reply_from_send_data 'case Usi_check_reply_from_send_data ?
brNe Isr_ovf_slave_send_data 'ansonsten nächsten status prüfen
'if ( USIDR ) // If NACK, the master does not want more data.
in r24, USIDR
!and r24, r24
breq Isr_ovf_req_rep_from_send_data_1 'jmp slave_send_data if Master send a ACK (send next byte)
'SET_USI_TO_TWI_START_CONDITION_MODE()
Isr_ovf_set_usi_start_cond_mode:
ldi r24, Usicr_start_mode
!out USICR, r24 'USICR = Usicr_start_mode
ldi r24, Usisr_send_or_read_data
!out USISR, r24 'USISR = Usisr_send_or_read_data
LDI r24, Usi_start_condition_mode
sts {Usi_twi_overflow_state}, r24 'USI_TWI_Overflow_State = Usi_start_condition_mode
rjmp ISR_OVF_end 'break
'case USI_SLAVE_SEND_DATA
Isr_ovf_slave_send_data: 'r24 = Usi_twi_overflow_state
cpi r24, Usi_send_data 'case Usi_send_data ?
brNe Isr_ovf_req_rep_from_send_data 'no -> jmp to next case
Isr_ovf_req_rep_from_send_data_1:
'zu sendendes Datenbyte muss in twi_tx abgelegt werden
push r1
push r24
in r24, SREG
push r24
push r30
push r31
Goto I2c_master_needs_byte
After_master_needs_byte:
pop r31
pop r30
pop r24
!out SREG, r24
pop r24
pop r1
'// Get data from Buffer
'clr r24
Loadadr Twi_tx , Z 'R31:R30
ldi r31, &H00 'paranoia
adc r31, r1 'add carry
ld r24, Z
!out USIDR, r24 'USIDR = TWI_Tx
ldi r24, Usi_request_reply_from_send_data
sts {Usi_twi_overflow_state}, r24 'USI_TWI_Overflow_State = USI_SLAVE_REQUEST_REPLY_FROM_SEND_DATA
'SET_USI_TO_SEND_DATA()'
SBI DDR_USI, PIN_SDA 'DDR_USI |= (1<<PORT_USI_SDA) // Set SDA as output
ldi r24, Usisr_send_or_read_data
!out USISR, r24 'USISR=Usisr_send_or_read_data
rjmp ISR_OVF_end 'break
'// Set Usi To Sample Reply From Master. Next Usi_slave_check_reply_from_send_data
'case USI_SLAVE_REQUEST_REPLY_FROM_SEND_DATA:
Isr_ovf_req_rep_from_send_data: 'r24 = Usi_twi_overflow_state
cpi r24, Usi_request_reply_from_send_data 'case Usi_request_reply_from_send_data ?
brNe Isr_ovf_slave_request_data 'no -> jmp to next case
ldi r24, Usi_check_reply_from_send_data
sts {Usi_twi_overflow_state}, r24 'Usi_twi_overflow_state = Usi_slave_check_reply_from_send_data
'SET_USI_TO_READ_ACK()'
CBI DDR_USI, PIN_SDA 'DDR_USI &= ~(1<<PORT_USI_SDA) // Set SDA as input
!out USIDR, r1 'USIDR = 0
ldi r24, Usisr_send_or_read_ack
!out USISR, r24 'USISR = Usisr_send_or_read_ack
rjmp ISR_OVF_end 'break
'// ----- Master read data mode ------
'// Set USI to sample data from master. Next USI_SLAVE_GET_DATA_AND_SEND_ACK.
'case USI_SLAVE_REQUEST_DATA:
Isr_ovf_slave_request_data: 'r24 = Usi_twi_overflow_state
cpi r24, Usi_request_data 'case Usi_request_data ?
brNe Isr_ovf_get_data_send_ack 'no -> jmp to next case
ldi r24, Usi_get_data_and_send_ack
sts {Usi_twi_overflow_state}, r24 'USI_TWI_Overflow_State = USI_SLAVE_GET_DATA_AND_SEND_ACK'
'SET_USI_TO_READ_DATA()'
CBI DDR_USI, PIN_SDA 'DDR_USI &= ~(1<<PORT_USI_SDA) // Set SDA as input
ldi r24, Usisr_send_or_read_data
!out USISR, r24 'USISR=Usisr_send_or_read_data
rjmp ISR_OVF_end 'break
'// Copy data from USIDR and send ACK. Next USI_SLAVE_REQUEST_DATA
'case USI_SLAVE_GET_DATA_AND_SEND_ACK:
Isr_ovf_get_data_send_ack: 'r24 = Usi_twi_overflow_state
cpi r24, Usi_get_data_and_send_ack 'case Usi_get_data_and_send_ack ?
BRNE Isr_ovf_end 'no -> jmp END
'// Put data into Buffer
Loadadr Twi_rx , Z 'R31:R30
ldi r31, &H00 'paranoia
adc r31, r1 'add carry flag
IN r24, USIDR
st Z, r24 'TWI_Rx = USIDR
'clr r24
ldi r24, Usi_request_data
sts {Usi_twi_overflow_state}, r24 'USI_TWI_Overflow_State = USI_SLAVE_REQUEST_DATA'
'empfangenes Datenbyte liegt in Twi_rx
push r1
push r24
in r24, SREG
push r24
push r30
push r31
Goto I2c_master_has_byte
After_master_has_byte:
pop r31
pop r30
pop r24
!out SREG, r24
pop r24
pop r1
'SET_USI_TO_SEND_ACK()
Isr_ovf_set_usi_to_send_ack: 'jmp from case USI_SLAVE_CHECK_ADDRESS
!out USIDR, r1 'USIDR = 0 //Prepare ACK
SBI DDR_USI, PIN_SDA 'DDR_USI |= (1<<PORT_USI_SDA) // Set SDA as output
ldi r24, Usisr_send_or_read_ack
!out USISR, r24 'USISR = Usisr_send_or_read_ack
Isr_ovf_end:
pop r31
pop r30
pop r24
pop r1
!out SREG, r1
pop r1
reti
Return
Lesezeichen