- 3D-Druck Einstieg und Tipps         
Seite 2 von 3 ErsteErste 123 LetzteLetzte
Ergebnis 11 bis 20 von 23

Thema: USI interface an tiny 2313

  1. #11
    Erfahrener Benutzer Robotik Einstein
    Registriert seit
    22.05.2005
    Ort
    12°29´ O, 48°38´ N
    Beiträge
    2.731
    Anzeige

    LiFePo4 Akku selber bauen - Video
    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

  2. #12
    Erfahrener Benutzer Roboter Genie
    Registriert seit
    16.04.2005
    Ort
    Aarau
    Alter
    41
    Beiträge
    982
    hallo!

    wollte mal nachfragen ob du es geschaft hast den tiny auch als slave zu betreiben?

    gruss bluesmash

  3. #13
    Erfahrener Benutzer Robotik Einstein
    Registriert seit
    22.05.2005
    Ort
    12°29´ O, 48°38´ N
    Beiträge
    2.731
    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

  4. #14
    Erfahrener Benutzer Robotik Einstein
    Registriert seit
    22.05.2005
    Ort
    12°29´ O, 48°38´ N
    Beiträge
    2.731
    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


    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
    Der Testaufbau schaut so aus:
    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)

  5. #15
    Erfahrener Benutzer Roboter Genie
    Registriert seit
    16.04.2005
    Ort
    Aarau
    Alter
    41
    Beiträge
    982
    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

  6. #16
    Erfahrener Benutzer Robotik Einstein
    Registriert seit
    22.05.2005
    Ort
    12°29´ O, 48°38´ N
    Beiträge
    2.731
    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 ...

  7. #17
    Erfahrener Benutzer Roboter Genie
    Registriert seit
    16.04.2005
    Ort
    Aarau
    Alter
    41
    Beiträge
    982
    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 zeit aber für einen kleinen rgb led slave controller sollte es ja reichen.. da würde ich auch nur die empfansroutine brauchen...

    gruss bluesmash

  8. #18
    Erfahrener Benutzer Fleißiges Mitglied
    Registriert seit
    27.03.2004
    Beiträge
    185
    Ich wärme den alten Thread mal wieder auf:

    Zitat Zitat von linux_80
    Wer sich mal versuchen will, hier ist die AppNote 312 als Bascom-Version.
    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.
    Der C-Code in der AppNote AVR312 ist eine Grotte, man kommt aber bei Interrupt gesteuertem USI-Slave an der AVR312 nicht vorbei.
    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

  9. #19
    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

  10. #20
    Erfahrener Benutzer Robotik Einstein
    Registriert seit
    22.05.2005
    Ort
    12°29´ O, 48°38´ N
    Beiträge
    2.731
    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

Seite 2 von 3 ErsteErste 123 LetzteLetzte

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •  

12V Akku bauen