hallo!
wollte mal nachfragen ob du es geschaft hast den tiny auch als slave zu betreiben?
gruss bluesmash
Hallo,
der erste Schritt ist getan, der Tiny macht jetzt schon als USI-TWI-Master und sendet einem PCF8574 Daten die ich über UART an den Tiny sende.
Das Programm ist in Bascom gebaut, das Problem ist nun, da der Tiny nur 2KB Flash hat, ist es schon zu 59% belegt.![]()
Mit Schuld ist da auch die ganze UART-String-Print-usw-Umwandlung, aber trotzdem ists relativ eng.
Man kann also nicht mehr viel anderes damit machen, zumindest unter Bascom![]()
€dit: Ich hab das Beispielprogramm mal ins Wiki gestellt, dann findet es sich leichter:
https://www.roboternetz.de/wissen/in...-Kommunikation
Das ist im Prinzip eine Übersetzung des C-Beispiels zur AN310 von Atmel:
http://www.atmel.com/dyn/products/ap...?family_id=607
€dit-PS:
Das Beispiel dürfte auch auf allen anderen AVRsen laufen die USI haben, weil die Bitpositionen der Register immer gleich sind, und die Namen vom Bascom sowieso angepasst übersetzt werden.
Nur ausreichend Speicher ist nötig![]()
hallo!
wollte mal nachfragen ob du es geschaft hast den tiny auch als slave zu betreiben?
gruss bluesmash
Zum Slave bin ich noch nicht gekommen,
hab die Woche für eine anderen Thread
https://www.roboternetz.de/phpBB2/viewtopic.php?t=25466
schnell eine Platine mit DS1621 gebaut und probiert, erst mit Software-I2C, dann mit USI (siehe Wiki)
Ich hatte in letzter Zeit nicht genug Zeit am Stück, damit ich das lesen der Doku und das testen eines Programms auf einmal geschafft hätte
Bin aber noch dran, es muss ja noch irgendwie versucht werden, das ganze etwas zu optimieren, damit man ausser dem USI noch was anderes mit dem AVR machen kann![]()
Hallo,
hab nun auch ein paar Tage frei, deshalb darf mein Tiny dran glauben
Wer sich mal versuchen will, ist die AppNote 312 als Bascom-Version.
Verändert werden sollte erstmal nix, ausser evtl. der Slaveadresse, und dem Soundausgang
Ansonsten kann für die Funktion nicht garantiert werden
Der Testaufbau schaut so aus:Code:' USI-I2C Testprogramm ' Slave @ $40 ' ' mit Interrupt und ohne Timer $regfile = "attiny2313.dat" $crystal = 16000000 $baud = 9600 $hwstack = 40 '$framesize = 16 $swstack = 16 ' Unterprogramme für die USI-Kommunikation Declare Sub Usi_twi_slave_initialise() Declare Sub Usi_twi_transmit_byte(zeichen As Byte) Declare Function Usi_twi_receive_byte() As Byte On Uci_start Usi_start_condition_isr , Nosave On Uci_ovflw Usi_counter_overflow_isr , Nosave ' einige Aliases anlegen Pout_usi_scl Alias Portb.7 Pin_usi_scl Alias Pinb.7 Ddr_usi_scl Alias Ddrb.7 Pout_usi_sda Alias Portb.5 Pin_usi_sda Alias Pinb.5 Ddr_usi_sda Alias Ddrb.5 Dim Usi_twi_errorstate As Byte ' eigener Fehlerstatus ' Array der Daten die übertragen werden Dim Twi_txbuf(16) As Byte Dim Twi_rxbuf(16) As Byte ' Zeiger auf Buffer Dim Twi_rxhead As Byte Dim Twi_rxtail As Byte Dim Twi_txhead As Byte Dim Twi_txtail As Byte Dim Temp_usi As Byte Dim Temp_usi_isr As Byte ' Byte das in den ISRs verwendet wird Dim Usi_twi_data_in_receive_buffer As Bit ' Flag ob Buffer Daten enthält Dim Usi_twi_overflow_state As Byte Const Twi_slaveaddress = &H40 ' Möglichkeiten für Usi_twi_overflow_state Const Usi_sl_check_address = 0 Const Usi_sl_send_data = 1 Const Usi_sl_req_reply_from_send_data = 2 Const Usi_sl_chk_reply_from_send_data = 3 Const Usi_sl_req_data = 4 Const Usi_sl_get_data_and_send_ack = 5 ' Ausgänge für LEDs Config Portb.6 = Output Config Portb.2 = Output Config Portb.1 = Output Config Portb.0 = Output Config Portd.6 = Output Config Portd.4 = Output Enable Interrupts ' IRQs global erlauben Call Usi_twi_slave_initialise Waitms 500 ' Sicherheitspause nach Reset Sound Portd.5 , 300 , 450 ' BEEP Print Print "USI Slavetest" ' Hauptprogramm Do Gosub Check_if_data_in_receive_buf If Usi_twi_data_in_receive_buffer = 1 Then Temp_usi = Usi_twi_receive_byte() ' Wert an LEDs anzeigen Portb.6 = Temp_usi.5 Portb.2 = Temp_usi.4 Portb.1 = Temp_usi.3 Portb.0 = Temp_usi.2 Portd.6 = Temp_usi.1 Portd.4 = Temp_usi.0 Temp_usi = Not Temp_usi Print Temp_usi 'Usi_twi_transmit_byte(Temp_usi) Else ' Print "nix" Waitms 100 End If 'Print Twi_rxhead ; 'Print " "; 'Print Twi_rxtail ; 'Print " "; 'Print Twi_txhead ; 'Print " "; 'Print Twi_txtail Loop End ' Unterprogramme ' Initialise USI for TWI Slave mode. Sub Usi_twi_slave_initialise() Gosub Flush_twi_buffers ' Flushes the TWI buffers ' Direction Out Ddr_usi_scl = 1 Ddr_usi_sda = 0 ' SDA Input ' Release SCL & SDA Pout_usi_scl = 1 Pout_usi_sda = 1 ' Preload dataregister with "released level" data. Usidr = &HFF ' Enable Start Condition Interrupt. Disable Overflow Interrupt. ' Set USI in Two-wire mode. No USI Counter overflow prior ' To First Start Condition(potentail Failure) ' Shift Register Clock Source = External , Positive Edge Usicr = &B10101000 ' Clear flags, and reset counter. Usisr = &B11110000 End Sub ' Puts data in the transmission buffer, Waits if buffer is full. Sub Usi_twi_transmit_byte(zeichen As Byte) Local Tmphead As Byte ' Calculate Buffer Index. Tmphead = Twi_txhead + 1 Tmphead = Tmphead And &H0F ' Wait for free space in buffer. While Tmphead = Twi_txtail Wend Twi_txbuf(tmphead + 1) = Zeichen ' Store Data In Buffer. Twi_txhead = Tmphead ' Store new index. End Sub ' Returns a byte from the receive buffer. Waits if buffer is empty Function Usi_twi_receive_byte() As Byte Local Tmptail As Byte ' warten bis etwas im Buffer steht While Twi_rxhead = Twi_rxtail Wend ' Tmptail =(twi_rxtail + 1 ) & Twi_rx_buffer_mask; ' Calculate buffer index Tmptail = Twi_rxtail + 1 Tmptail = Tmptail And &H0F Twi_rxtail = Tmptail ' Store new index Usi_twi_receive_byte = Twi_rxbuf(tmptail + 1) ' Return data from the buffer. End Function ' ISR für Startsequenz erkannt Usi_start_condition_isr: $asm PUSH R16 PUSH R20 PUSH R24 PUSH R25 PUSH R26 IN R24,&H3F PUSH R24 $end Asm ' Code Usi_twi_overflow_state = Usi_sl_check_address Ddr_usi_sda = 0 ' Enable SDA as input. ' Wait for SCL to go low. ' If a Stop condition arises then leave the interrupt to prevent waiting forever. While Pin_usi_scl = 1 And Usisr.5 = 0 ' USIPF Wend ' Enable Overflow and Start Condition Interrupt. (Keep StartCondInt to detect RESTART) ' Set USI in Two-wire mode. ' Shift Register Clock Source = External, positive edge Usicr = &B11111000 ' Clear flags, and reset counter. Usisr = &B11110000 $asm POP R24 Out &H3F , R24 POP R26 POP R25 POP R24 POP R20 POP R16 $end Asm Return ' ISR für Counteroverflow Usi_counter_overflow_isr: $asm PUSH R10 PUSH R16 PUSH R20 PUSH R24 PUSH R26 IN R24,&H3F PUSH R24 $end Asm ' Code 'Print Usi_twi_overflow_state Select Case Usi_twi_overflow_state ' -- Address mode -- ' Check address and send ACK (and next USI_SL_SEND_DATA) if OK, else reset USI. Case Usi_sl_check_address : Temp_usi_isr = Usidr And &HFE ' 0. Bit ausblenden falls RW gleich 1 If Temp_usi_isr = Twi_slaveaddress Then ' Read oder Write ? 1 - Master will was von uns haben If Usidr.0 = 1 Then Usi_twi_overflow_state = Usi_sl_send_data Else Usi_twi_overflow_state = Usi_sl_req_data End If Gosub Set_usi_to_send_ack Else Gosub Set_usi_to_twi_start_cond_mode 'Print "nicht ich" End If ' -- Master write data mode -- ' Check reply and goto USI_SL_SEND_DATA if OK, else reset USI. Case Usi_sl_chk_reply_from_send_data : ' If NACK, the master does not want more data. If Usidr = 0 Then Gosub Set_usi_to_twi_start_cond_mode Goto Fertig_usi_counter_overflow_isr Else ' From here we just drop straight into USI_SL_SEND_DATA if the master sent an ACK If Twi_txhead <> Twi_txtail Then ' Twi_txtail = (twi_txtail + 1 ) And Twi_tx_buffer_mask Temp_usi_isr = Twi_txtail + 1 Temp_usi_isr = Temp_usi_isr And &H0F Twi_txtail = Temp_usi_isr Usidr = Twi_txbuf(temp_usi_isr + 1) Else ' If the buffer is empty then: Gosub Set_usi_to_twi_start_cond_mode Goto Fertig_usi_counter_overflow_isr End If Usi_twi_overflow_state = Usi_sl_req_reply_from_send_data Gosub Set_usi_to_send_data End If ' Copy data from buffer to USIDR and set USI to shift byte. Next USI_SL_REQ_REPLY_FROM_SEND_DATA Case Usi_sl_send_data : If Twi_txhead <> Twi_txtail Then ' Twi_txtail = (twi_txtail + 1 ) And Twi_tx_buffer_mask Temp_usi_isr = Twi_txtail + 1 Temp_usi_isr = Temp_usi_isr And &H0F Twi_txtail = Temp_usi_isr Usidr = Twi_txbuf(temp_usi_isr + 1) Else ' If the buffer is empty then: Gosub Set_usi_to_twi_start_cond_mode Goto Fertig_usi_counter_overflow_isr End If Usi_twi_overflow_state = Usi_sl_req_reply_from_send_data Gosub Set_usi_to_send_data ' Set USI to sample reply from master. Next USI_SL_CHK_REPLY_FROM_SEND_DATA Case Usi_sl_req_reply_from_send_data : Usi_twi_overflow_state = Usi_sl_chk_reply_from_send_data Gosub Set_usi_to_read_ack ' -- Master read data mode -- ' Set USI to sample data from master. Next USI_SL_GET_DATA_AND_SEND_ACK. Case Usi_sl_req_data : Usi_twi_overflow_state = Usi_sl_get_data_and_send_ack Gosub Set_usi_to_read_data ' Copy data from USIDR and send ACK. Next USI_SL_REQ_DATA Case Usi_sl_get_data_and_send_ack : ' Put data into Buffer ' Twi_rxhead = (twi_rxhead + 1 ) & Twi_rx_buffer_mask Temp_usi_isr = Twi_rxhead + 1 Temp_usi_isr = Temp_usi_isr And &H0F Twi_rxhead = Temp_usi_isr Twi_rxbuf(temp_usi_isr + 1) = Usidr Usi_twi_overflow_state = Usi_sl_req_data Gosub Set_usi_to_send_ack End Select Fertig_usi_counter_overflow_isr: $asm POP R24 Out &H3F , R24 POP R26 POP R24 POP R20 POP R16 POP R10 $end Asm Return ' Einstellungen für Read und Write ' Set_usi_to_twi_start_cond_mode: ' Enable Start Condition Interrupt. Disable Overflow Interrupt ' Set USI in Two-wire mode. No USI Counter overflow hold. ' Shift Register Clock Source = External, positive edge Usicr = &B10101000 'Clear all flags, except Start Cond Usisr = &B01110000 Return ' Set_usi_to_send_ack: Usidr = 0 ' Prepare ACK Ddr_usi_sda = 1 ' Set Sda As Output ' Clear all flags, except Start Cond ' Set Usi Counter To Shift 1 Bit Usisr = &B01111110 Return ' Set_usi_to_read_ack: Ddr_usi_sda = 0 ' Set Sda As Intput Usidr = 0 ' Prepare ACK ' Clear all flags, except Start Cond ' set USI counter to shift 1 bit Usisr = &B01111110 Return ' Set_usi_to_send_data: Ddr_usi_sda = 1 ' Set Sda As Output ' Clear all flags, except Start Cond ' set USI to shift out 8 bits Usisr = &B01110000 Return ' Set_usi_to_read_data: Ddr_usi_sda = 0 ' Set Sda As Intput ' Clear all flags, except Start Cond ' set USI to shift out 8 bits Usisr = &B01110000 Return ' Check_if_data_in_receive_buf: If Twi_rxtail <> Twi_rxhead Then Usi_twi_data_in_receive_buffer = 1 Else Usi_twi_data_in_receive_buffer = 0 End If Return ' hauwech Flush_twi_buffers: Twi_rxtail = 0 Twi_rxhead = 0 Twi_txtail = 0 Twi_txhead = 0 Return
Die LEDs links auf dem RN-Control, sind von oben die ersten beiden für den I2C-Bus, der Rest ist für die Anzeige der Daten, die vom RN-Mega8 gesendet werden.
Der Mega8 liest die Temperatur vom DS1621, und gibt den Wert an einen PCF8574 weiter, der Tiny2313 spielt eben diesen vor, und gibt den Wert an den 6 LEDs aus.
Das Muster ist dasselbe wie auf dem RN-M8 LEDs unten.
Testprogramm auf dem RN-M8 ist dieses vom Wiki:
https://www.roboternetz.de/wissen/in...it_und_Receive
€dit:
Ich hab die If-Abfrage (Zeile 236-237) umgebaut, da der Slave nicht auf ein Master-Read reagiert hat, so geht das jetzt auch. (Falls was im Puffer steht)
wenn ich jetzt deinen post richtig interpretiere hast du es geschaft den tiny als I2C Hardware slave zu betreiben?? oder ist dies der testaufbau den du gerade testest?
gruss bluesmash
Das Programm funktioniert schon so wie es oben steht,
es ist aber natürlich trotzdem ein Testaufbau, denn wirklich brauchen tue ich das ja nicht
Ich weiss halt jetzt was sich Atmel da gedacht hat bei diesem USI, und wie man einem AVR dazu bringt etwas damit zu machen.
Man könnte das jetzt noch probieren zu optimieren, denn das Progamm ist ziemlich gross, und der Speicher (SRAM) ist bis auf 8 Byte belegt !
Da lässt sich sonst nicht mehr viel machen mit so einem AVR.
Es wäre evtl. noch einen Versuch wert, das ganze ohne ISR hinzubekommen. Auch das hier mit den Puffern ist nicht so ganz Zeitnah, denn wenn ein Master was will, müssen die Daten die zurückgesendet werden erst erzeugt werden, und nicht schon im Puffer stehen !
Ich würde aber sagen, dass das USI besser geeignet ist ein Slave zu sein, als einen Master nachzubilden.
Jetzt könnte ich allerdings noch das mit dem USI-SPI-Modus probieren ...![]()
ist ja super das du es gschaft hast! ich werde es in den nächsten tagen auch mal testen... ich habe ja auch noch ein bisschen zeitaber für einen kleinen rgb led slave controller sollte es ja reichen..
da würde ich auch nur die empfansroutine brauchen...
gruss bluesmash
Ich wärme den alten Thread mal wieder auf:
Der C-Code in der AppNote AVR312 ist eine Grotte, man kommt aber bei Interrupt gesteuertem USI-Slave an der AVR312 nicht vorbei.Zitat von linux_80
Da ich bei einem Projekt einen Attiny25 verwenden möchte, habe ich den Bascom-Code mit ASM-Unterstützung auf 478Byte (23% von 2KByte) zusammengeschossen. Der 128Byte-SRAM ist nur halb voll.
... so bleibt genügend Platz für eigene Erweiterungen.
Erweiterung:
Im Code ist noch eine auskommentierte alternative Main_Loop eingefügt, die I2C-Bus-TimeOuts abfängt, wie "Slave will senden, zieht SDA auf LOW aber Master schickt keinen Takt".
Wenn keine USI-Kommunikation anliegt bzw. nach TimeOut, wird der Attiny in den Power-Down (1µA) Modus versetzt.
Weiterhin habe ich den Ringpuffer aus AVR312 gestrichen, da er für meine Zwecke (Nachbildung PCF85xx) nicht optimal ist.
Nach einer I2C-Startkondition wird:
- das erste empfangene Daten-Byte in Twi_rxbuf(1) geschrieben bzw.
- das erste zu sendende Daten-Byte aus Twi_txbuf(1) entnommen
Die AVR312-Atmel-Kommentare und den zugehörigen C-Code habe ich im Quelltext weitestgehend stehen gelassen
Code:'/***************************************************************************** ' ' Atmel AppNote : AVR312 - Using the USI module as a TWI slave ' ' Supported devices : All device with USI module can be used. ' The example is written for ATtiny25, ATtiny26, ATtiny2313, ATmega169 ' ' Description : Example showing how to use the USI_TWI drivers; ' Loops back received data (incremented). ' ' Changes for BASCOM: I removed for general purposes the AVR312-ringbuffer in Twi_rxbuf() and Twi_txbuf() ' V1.11.9.0 the first new byte after a I2C-Start-Condition is ' saved in Twi_rxbuf(1) or ' read from Twi_txbuf(1) ' '****************************************************************************/ $regfile = "ATtiny25.DAT" 'Controllertyp $hwstack = 32 'Stack $framesize = 20 'don't use to much num<>string conversion!! 'if you DECLARE FUNCTION / SUB increase $swstack !!! $swstack = 0 'in this sample code no LOCAL variable inside a SUB or function!! $crystal = 8000000 'crystal frequency '********* parameters set by user (usi port, address and buffer size) **************************** Const Twi_rx_buffer_size = 8 '1,2,4,8,16,32 bytes are allowed buffer sizes Const Twi_tx_buffer_size = 8 '1,2,4,8,16,32 bytes are allowed buffer sizes Const Twi_ownaddress = &H10 'only for compatibility AVR312, the real write adress is 2*Twi_ownaddress !! Const Ddr_usi = Ddrb Const Port_usi = Portb Const Pin_usi = Pinb Const Pin_sda = 0 Const Pin_scl = 2 Const Mcucr_power_down = &B0011_0000 'alternate main_loop set power-down mode in register MCUCR '********* Initialise USI **************************** 'USICR => USISIE|USIOIE|USIWM1|USIWM0 USICS1|USICS0|USICLK|USITC (USICR = &H0d) Const Usicr_start_mode = &B1010_1000 'Set USI in Two-wire mode. No USI Counter overflow prior 'to first Start Condition (potentail failure), Shift Register Clock Source = External, positive edge Const Usicr_isr_start = &B1111_1000 'like above with Counter Overflow ISR 'USISR => USISIF|USIOIF|USIPF|USIDC USICNT4|USICNT2|USICNT1|USICNT0 (USISR = &H0e) Const Usisr_isr_start = &B1111_0000 'set USI to shift 8 bits and clear "Start Condition Interrupt Flag" Const Usisr_send_or_read_data = &B0111_0000 'set USI to shift 8 bits Const Usisr_send_or_read_ack = &B0111_1110 'set USI to shift 1 bits Dim Twi_slaveaddress As Byte Const Twi_rx_buffer_mask = Twi_rx_buffer_size - 1 'filter mask Dim Twi_rxbuf(twi_rx_buffer_size) As Byte 'read buffer (array) Dim Twi_rxhead As Byte Const Twi_tx_buffer_mask = Twi_tx_buffer_size - 1 'filter mask Dim Twi_txbuf(twi_tx_buffer_size) As Byte 'send buffer (array) Dim Twi_txhead As Byte Dim Usi_twi_overflow_state As Byte 'state machine Const Usi_start_condition_mode = &H00 Const Usi_check_address = &H01 Const Usi_send_data = &H02 Const Usi_request_reply_from_send_data = &H03 Const Usi_check_reply_from_send_data = &H04 Const Usi_request_data = &H05 Const Usi_get_data_and_send_ack = &H06 '********* Initialise ISR **************************** On Usi_start _isr_usi_start Nosave 'Interrupt NOSAVE_ISR! Enable Usi_start On Usi_ovf _isr_usi_ovf Nosave 'Interrupt NOSAVE_ISR! Enable Usi_ovf '********* Debug **************************** Config Portb.3 = Output 'Ein freier Pin wird als LED-Ausgang konfiguriert ' cbi portb,3 'control led off ' SBI portb,3 'control led on '********* Main Loop Variables **************************** Dim I As Byte Dim Timeout As Byte 'for extended main loop, see below Dim Usi_start_flag As Byte 'for extended main loop, see below '********* Main Loop **************************** Gosub Usi_twi_slave_initialise Enable Interrupts Do If Twi_rxhead > 0 Then 'new bytes arrives in the input buffer 'While Usisr.usipf = 0 'wait for USIPF (Stop Condition Flag) then the last byte arrived Main_01: SBIS Usisr,usipf 'Wend 'USIPF cleared in next call of _isr_USI_START (SBI Usisr, usipf) rjmp Main_01 '************* here place your code // attention: Twi_txbuf(0) -> ERROR ***************** '//this is my sample user code, you can remove it 'For I = 1 To Twi_rxhead 'check your constants: Twi_tx_buffer_size > Twi_rx_buffer_size !! lds r24, {Twi_txhead} TST r24, r24 BREQ Main_03 'error handler: jmp if Twi_txhead=0 Loadadr Twi_rxbuf(1) , X Loadadr Twi_txbuf(1) , Y Main_02: 'Twi_txbuf(i) = Twi_rxbuf(i) + 1 LD r25, X+ 'post increment inc r25 'r25 = Twi_rxbuf(i) + 1 st Y+, r25 'post increment 'Next DEC r24 BRNE Main_02 Main_03: '************* end user code *************************************************************** '//now reset the read pointer Twi_rxhead = 0 End If Loop '********* Alternate Main Loop with Power-Down of AVR and I2C-Bus-Reset after TimeOut **************************** ' Gosub Usi_twi_slave_initialise ' Enable Interrupts ' Do ' If Usi_start_flag = 1 Then 'Usi_start_flag set in _isr_USI_START ' Usi_start_flag = 0 ' Timeout = 0 ' While Usisr.usipf = 0 And Timeout < 200 'wait on USIPF (Stop Condition Flag) with timeout ' Waitus 100 'TiimeOut=100µs*200=20ms ' Incr Timeout ' Wend 'USIPF cleared in next call of _isr_USI_START (SBI Usisr, usipf) ' If Twi_rxhead > 0 Then ' 'Input Daten auswerten, hier nur Input+1 auf Output kopieren ' For I = 1 To Twi_rxhead 'Beachte: Index Twi_rxhead kann auch 0 sein, d.h. Twi_txbuf(0) -> ERROR ' Twi_txbuf(i) = Twi_rxbuf(i) + 1 ' Next ' End If ' 'Reset TWI to SET_USI_TO_TWI_START_CONDITION_MODE() and set SDA as Input ' CBI DDR_USI, PIN_SDA 'Set SDA as input ' Usicr = Usicr_start_mode ' Usisr = Usisr_isr_start ' End If ' 'set power-down mode ' Enable Interrupts 'Paranoia ' LDI r18, Mcucr_power_down 'set powerdown ' !Out MCUCR, r18 ' sleep ' Loop '********* 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: 'Flushes the TWI buffers Twi_rxhead = 0 Twi_txhead = 0 Twi_slaveaddress = Twi_ownaddress 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 LDI r24, 1 sts {Usi_start_flag}, r24 'detect _isr_USI_START in main loop 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 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. _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: 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 'no -> jmp to next case '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 lsr r24 lds r30, {Twi_slaveaddress} cp r24, r30 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 '//reset pointer sts {Twi_txhead}, r1 'Twi_txhead=0 rjmp Isr_ovf_set_usi_to_send_ack 'SET_USI_TO_SEND_ACK() + BREAK 'else //we get a read 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 '//reset pointer sts {Twi_rxhead}, r1 'Twi_rxhead=0 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 'no -> jmp to next case '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: '// Get data from Buffer lds r24, {Twi_txhead} '// Pointer Twi_txhead Loadadr Twi_txbuf(1) , Z 'R31:R30 ldi r31, &H00 'paranoia add r30, r24 'add index adc r31, r1 'add carry ld r24, Z !out USIDR, r24 'USIDR = TWI_TxBuf[Twi_txhead] '//incr and mask pointer lds r24, {Twi_txhead} subi r24, &HFF ' incr Twi_txhead andi r24, Twi_tx_buffer_mask ' mask pointer sts {Twi_txhead}, r24 'Twi_txhead = ( Twi_txhead + 1 ) & TWI_TX_BUFFER_MASK 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_rxbuf(1) , Z 'R31:R30 ldi r31, &H00 'paranoia lds r24, {Twi_rxhead} add r30, r24 'add Twi_rxhead to address Twi_rxbuf(1) adc r31, r1 'add carry flag -> Z=TWI_RxBuf[Twi_rxhead] IN r24, USIDR st Z, r24 'TWI_RxBuf[Twi_rxhead] = USIDR '//incr and mask pointer lds r24, {Twi_rxhead} subi r24, &HFF 'INCR Twi_rxhead andi r24, Twi_rx_buffer_mask sts {Twi_rxhead}, r24 'Twi_rxhead = ( Twi_rxhead + 1 ) & TWI_RX_BUFFER_MASK ldi r24, Usi_request_data sts {Usi_twi_overflow_state}, r24 'USI_TWI_Overflow_State = USI_SLAVE_REQUEST_DATA' '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
Hallo Thomas
ich habe vor kurzem Deinen Beitrag hier gefunden da ich genau das gesucht hatte , die I2C Verbindung zwischen AVR Kontrollern, war auch das erste Programm was auf Anhieb funktionierte.
Habe aber trotzdem ein Problem damit:
Daten werden in Twi_rxbuf() geschrieben und aus Twi_txbuf() gelesen,
wenn ich aber vorrangig nur Daten lesen will muß ich erstmal ein Byte mit I2Csend (egal was) senden um nacher mit i2creceive ein Byte (welches im Tiny25 aufbereitet wurde und in Twi_txbuf() geschrieben wurde) zu holen.
Die komunikation funktioniert, aber warum muß ich erst was senden?
Da ich mich erst seit kurzem mit AVR Kontrollern beschäftige und die I2C Verbindung zu Slave Mikrokontrollern auch nicht ganz einfach ist habe ich noch Probleme Dein Programm soweit nachzuvollziehen daß ich die Probleme selbst beheben kann.
Ich hoffe Du "hörst" mich noch, der Thread ist ja schon ziemlich alt
Hallo,
das Problem bei der Interruptbasierten Versendung ist, dass dies asynchron geschieht. DH. der Slave weiss nicht wann er was Senden soll wenn der Master was anfordert.
Einfach was in den Ausgangspuffer schreiben ist auch nicht unbedingt die Lösung, weil man nicht weiss ob es überhaupt abgeholt wird.
Du kannst aber mal rumprobieren, in der Main-Loop gibts eig. nur eine If-Abfrage, in die nur reingesprungen wird, wenn etwas Empfangen wurde.
Den Ausgangspuffer nur so gross, wie die Anzahl Bytes die auf einmal zum Master übertragen werden sollen. Damit es keine "alten" Daten gibt. Dann Regelmässig diese mit den entsprehenden Daten füllen, aber nur überschreiben, wenn der Master nicht grad am lesen des Puffers ist.
HTH
Lesezeichen