mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik I2C Slave Reciever


Autor: Ppp Mmm (sanic)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich habe folgendes Problem:

Derzeit programmiere ich an einem Hardware I2C-Slave der von einer
C-Control 2 ausgelesen werden soll.
Folgender Ablauf ist momentan eingestellt:
 1. C-Control schickt START Condition mit der Adresse des ICs
 2. C-Control gibt ACK oder NACK zurück.

Wenn ich nun das C-Control Programm starte tut der Slave nichts ( kein
Sprung in die ISR ). Wenn ich allerdings SDA und SCL vertausche kommen
wirre Werte beim Slave an ( 0xFF ). Die C-Control gibt in beiden Fällen
NACK zurück.

Meine Frage:
Ist der Slave-Code vom Prinzip richtig oder liegt´s wohl eher an der
Schaltung ?

Danke im vorraus!

Grüße,
Patrick

Autor: Mario Richter (mario001) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kinder Kinder,

könnten wir uns vielleicht mal um wichtige Dinge kümmern, wie zum
Beispiel darum, warum Patricks Code nicht funktioniert ?

Ich experimentiere auch grade mit der TWI rum (allerdings als Master),
deswegen versuch ich mal meine Ideen anzubringen:

Du setzt das Bit TWINT im TWCR-Register; laut Datenblatt (vom ATmega16)
musst du im Slave Receive-Mode dieses Bit zurücksetzen (siehe Seite
190ff.). In das TWAR-Register kommt die Adresse, auf die der Slave
antworten soll (welchen Wert hat 'addr' in deinem Code ?? Haste da
evtl. die Initialisierung vergessen ?).

Über das TWEA-Bit kannst du steuern, ob empfangene Pakete mit der
richtigen Adresse bestätigt werden sollen (TWEA=1) oder nicht
(TWEA=0).

Jau, probier das mal alles aus bzw. überprüfe es, und dann schaun wir
mal weiter, ob sich evtl. schon mehr tut.

Grüße, Mario

Autor: Ppp Mmm (sanic)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke Mario für deinen Einsatz.
Ich hoffe wir können jetzt geregelte Bahnen gehen ;-)

Also:

"addr" wird bei dem init_i2c() befehl übergeben, siehe:
  init_i2c(62); // Starte Slave mit Adresse "62"

TWINT:
  Zitat aus dem Datenblatt: "The TWINT Flag must be cleared by
software by writing a logic one to it."

TWEA:
  Ist so konfiguriert dass der ATMega antwortet wenn er angeprochen
wird (1)

Grüße

Autor: Mario Richter (mario001) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke an Andreas für die "Reinigung" dieses Threads :-)

Sorry, dass mit der Adressübergabe hatte ich echt übersehen; hab wohl
meine Brille ned geputzt ...

Du hast schon Recht, nach TWINT muss man ne 1 schreiben, um das Flag zu
löschen. Was aber, wenn das Flag bereits vorher gelöscht war ? Das geht
aus dem Datenblatt nicht ganz klar hervor. Ich hab es so interpretiert,
dass man das Flag nur dann auf diese Weise löschen muss, wenn es vorher
von der Hardware gesetzt wurde. Ich beziehe mich auch auf den bereits
erwähnten Abschnitt über den Slave Receive-Mode, wo ja drinsteht,
welche Werte man schreiben muss, und da steht für TWINT auch ne 0 !

Probier's doch mal aus, was passiert, wenn du TWINT bei der
Initialisierung NICHT setzt.

Grüße, Mario

Autor: Ppp Mmm (sanic)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Mario,

danke für den Tip, aber leider bringt das nicht-setzen des TWINT Bits
nicht den gewünschten Erfolg.

Grüße,
Patrick

Autor: Ppp Mmm (sanic)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mittlerweile habe ich den Application Note Code von Atmel soweit
umgebaut, dass ich ein ACK zurückbekomme.

Allerdings ist der Datenempfang immer noch nicht drin.
Die ISR mit dem ganzen Error Handling habe ich auskommentiert und
folgendes geschrieben:

SIGNAL(SIG_2WIRE_SERIAL){
  PORTB=0xFF;
  static unsigned char TWI_bufPtr;
  uart_puts("SR: ",0);
  uart_puti(TWSR,2);
  uart_puts("\r\n",0);

  uart_puts("DR: ",0);
  uart_puti(TWDR,10);
  uart_puts("\r\n",1);

  TWI_Start_Transceiver( );
}

PORTB wid auf 0xFF geschaltet und über den UART wird auch was
ausgegeben. Allerdings ist das Statusregister auf 0 und das
Datenregistert auf 129 ?!
mit TWI_Start_Transceiver() wird folgendes gemacht:

void TWI_Start_Transceiver( void )
{
  while ( TWI_Transceiver_Busy() );             // Wait until TWI is
ready for next transmission.
  TWI_statusReg.all = 0;
  TWI_state         = TWI_NO_STATE ;
  TWCR = (1<<TWEN)|                             // TWI Interface
enabled.
         (1<<TWIE)|(1<<TWINT)|                  // Enable TWI Interupt
and clear the flag.
         (1<<TWEA)|(0<<TWSTA)|(0<<TWSTO)|       // Prepare to ACK next
time the Slave is addressed.
         (0<<TWWC);                             //
}

Grüße

Autor: Ppp Mmm (sanic)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Anmerkung: beim puti ist der 2. Parameter die base für die Umrechnung
des INTs in einen String ( siehe itoa() ).

Grüße

Autor: Rahul Der trollige (rahul)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
wie lange braucht die "Uart_put.." zum arbeiten? Kann es sein, dass
die länger braucht, als deine Übertragung braucht, sprich: es gibt
einen Überlauf?
Andere Funktionen von ISR aus aufrufen ist sehr unschön.
Nimm deine Daten und schreibe sie in einen Puffer, der dann in der Main
an die Uart-Funktionen übergeben wird.

Autor: Ppp Mmm (sanic)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
In meinem alten Code habe ich das schonmal probiert, da gab´s keinen
besseren Effekt.
Bin aber gerade dabei das umzustricken.

Grüße

Autor: Ppp Mmm (sanic)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hab das Ganze mal so umfunktioniert:

SIGNAL(SIG_2WIRE_SERIAL){
  PORTB=0xFF;
  static unsigned char TWI_bufPtr;
  twsr_back=TWSR;
  switch (TWSR)
  { ........... // TWI Application Note von Atmel

Wenn ich nun in einer Vorschleife immer folgendes mache:

if(twsr_back!=0){
    uart_puti(twsr_back,2);
    uart_puts("\n\r",1);
}

Passiert nichts.
In die ISR springt der Controller aber nach wie vor. Ein ACK wird auch
zurückgegeben.

Grüße

Autor: alex (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hi Mania

habe auch schon mal ne Slave programmiert. Schau dir mal den Code im
Anhang an. Habe da nen paar Unterroutienen. In dem Hauptprogramm wird
dann abgefragt, ob der Controller adrressiert worden ist.

MFG

Alex

Autor: Ppp Mmm (sanic)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Alex,

danke für deinen Code, werde ich später mal testen.
Aber eine kurze Frage:
Warum setzt du das Bitrate Register ?
Das sollte doch bei einem reinen Slave überflüssig sein oder ?
Der Slave reagiert ja auf den SCL Puls vom Master.

Grüße

Autor: alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi Patrik,

ich habe das Bitrate Register gesetzt, da der Prozessor bei meinem
Projekt sowohl als Slave als auch Master arbeitet. Wenn der Prozessor
als reiner Slave arbeiten soll, brauchst du das natürlich nicht.

Mann könnte das auch mit I2C-Interrupt programmieren, dann muss man im
hauptprogramm das TWI-Flag nicht ständig abfragen.....

Ich hoffe du kannst mit dem Quelltext was anfangen.

mfg

Alex

Autor: Ppp Mmm (sanic)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ok, das erklärt natürlich einiges.
Wenn du aber im Modus "// I2C-Slave-Mode Daten lesen" bist brauchste
doch am Ende auch kein i2c.stop zu senden oder ?
Das macht im Regelfall auch der Master.

Grüße

Autor: alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nein, wenn der Prozessor in Slave Modus ist, braucht der Slave keine
Stopbedingung zu senden, du musst aber in dem Statusrgister des
Prozessors gewisse Bits setzten damit der Prozessor wieder auf
bereitschaft geht.
du könntest dir dafür noch ne extra Routine schreiben, mit der Stop
routine gehts auch :-)

Autor: Ppp Mmm (sanic)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wunderbar, danke!
Ich werde die Anlage jetzt mal an den Logic Analyzer klemmen und testen
ob da alles mit rechten Dingen vor geht.

Grüße

Autor: Ppp Mmm (sanic)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Habe nebenbei mal deinen Code ausprobiert,
funktioniert leider auch nicht.

Grüße

Autor: AxelR. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich darf vielleicht auch as beisteuern - ist zwar wiedermal "nur"
FastAVR, aber aus C übernommen.
'#################### I2C-Interrupt ########################
Interrupt TWI(), Save All
Select Case TWSR
            'Slave receive codes
Case TW_SR_SLA_ACK        '0x60 
GoTo twi_78                         
Case TW_SR_ARB_LOST_SLA_ACK       '0x68
GoTo twi_78                           
Case TW_SR_GCALL_ACK        '0x70
GoTo twi_78                           
Case TW_SR_ARB_LOST_GCALL_ACK      '0x78
twi_78:
  TWI_State = TWI_SLAVE_RX
  TWI_RXD_INDEX = 0
            'has receive byte and return ACK
  $Asm
  ldi r16, (1<<TWIE | 1<<TWINT| 1<<TWEA | 1<<TWEN)
  out TWCR, r16
  $EndAsm
Case TW_SR_DATA_ACK        '0x80
GoTo twi_90
Case TW_SR_GCALL_DATA_ACK      '0x90
twi_90:
  
  TWI_RXDATA(TWI_RXD_INDEX)= TWDR    'get previously received data

  Incr TWI_RXD_INDEX
  If TWI_RXD_INDEX < TWI_BUF_SIZE And TWI_RXD_INDEX < MAX_RX_TWI_BYTE
Then  'check rx-buffer state
            'has receive byte and return ACK
    $Asm
    ldi r16, (1<<TWIE | 1<<TWINT | 1<<TWEA | 1<<TWEN)
    out TWCR, r16
    $EndAsm
  Else
            'has receive byte and return NACK
    TWI_RXD_INDEX = 0
    $Asm
    ldi r16, (1<<TWIE | 1<<TWINT | 1<<TWEN )
    out TWCR, r16
    $EndAsm
  End If
Case TW_SR_DATA_NACK        '0x88
GoTo twi_98                                   
Case TW_SR_GCALL_DATA_NACK      '0x98
twi_98:    
            'has receive byte and return NACK
  $Asm
  ldi r16, (1<<TWIE | 1<<TWINT | 1<<TWEN |1<<TWSTO)
  out TWCR, r16
  $EndAsm
                      

Case TW_SR_STOP          '0xA0
  

  TWI_RXD_INDEX = 0
  
  twi_rx_handle()        'neue I2C-Daten liegen vor
  
  $Asm
  ldi r16, (1<<TWIE | 1<<TWINT | 1<<TWEA | 1<<TWEN)
  out TWCR, r16
  $EndAsm
  
  TWI_STATE = TWI_IDLE
            'switch to SR mode with SLA ACK
                    
  
  

Case TW_ST_SLA_ACK
  GoTo twi_b0
Case TW_ST_ARB_LOST_SLA_ACK
twi_b0:  
    
  TWI_STATE = TWI_SLAVE_TX
  TWI_TXD_INDEX = 0
  GoTo twi_b8
Case TW_ST_DATA_ACK
twi_b8:
      
  TWDR = TWI_TXDATA(TWI_TXD_INDEX)


  Incr TWI_TXD_INDEX
  If TWI_TXD_INDEX < TWI_BUF_SIZE  Then          $Asm
    ldi r16, (1<<TWIE | 1<<TWINT | 1<<TWEA | 1<<TWEN)
    out TWCR, r16
    $EndAsm
  Else
    $Asm
    ldi r16, (1<<TWIE | 1<<TWINT | 1<<TWEN)
    out TWCR, r16
    $EndAsm
  End If
Case TW_ST_DATA_NACK
GoTo twi_c8
Case TW_ST_LAST_DATA
twi_c8:  
    TWI_TXD_INDEX =  0
  TWI_STATE =TWI_IDLE
  $Asm
  ldi r16, (1<<TWIE | 1<<TWINT | 1<<TWEA | 1<<TWEN)
  out TWCR, r16
  $EndAsm
GoTo end_ints
Case TW_NO_INFO      '0xF8
GoTo end_ints
Case TW_BUS_ERROR
    TWI_STATE = TWI_IDLE
  $Asm
  ldi r16, (1<<TWIE | 1<<TWINT | 1<<TWEA | 1<<TWSTO | 1<<TWEN)
  out TWCR, r16
  $EndAsm      
end_ints:

End Select


end_twi:
End Interrupt


hier kannst Du sehr schön sehen, bei welchem Wert im TWSR du die
einzelnen Bits im TWCR setzen musst. die einzelnen Bedingungen sind
oben im Program als Konstanten gesetzt (habe ich jetzt nicht
rüberkopiert, stehen ja dahinter.
Gruß
axelR.

Autor: Ppp Mmm (sanic)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn in meinem TWSR mal was stehen würde .... ;-)

Autor: Ppp Mmm (sanic)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Auch nach längerem hin&her steht immer noch nichts in meinem TWSR.
Der IC wird korrekt angesprochen , springt in die ISR, aber es ist
nichts im TWSR.
Was kann da los sein ?

Grüße

Autor: AxelR. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das ist komisch!
irgentein Wert sollte drinn stehen, und wenn's nur 0xF8 ist.
'Slave Receiver Codes
Const TW_SR_SLA_ACK        = &h60
Const TW_SR_ARB_LOST_SLA_ACK  = &h68
Const TW_SR_GCALL_ACK      = &h70
Const TW_SR_ARB_LOST_GCALL_ACK  = &h78
Const TW_SR_DATA_ACK      = &h80
Const TW_SR_GCALL_DATA_ACK    = &h90
Const TW_SR_DATA_NACK      = &h88
Const TW_SR_GCALL_DATA_NACK    = &h98
Const TW_SR_STOP        = &hA0
'Slave Transmitter Codes
Const TW_ST_SLA_ACK        = &hA8
Const TW_ST_ARB_LOST_SLA_ACK  = &hB0
Const TWCR_CMD_MASK        = &h0F
Const TWSR_STATUS_MASK      = &hF8
Const TW_ST_DATA_ACK      = &hB8
Const TW_ST_DATA_NACK      = &hC0
Const TW_ST_LAST_DATA      = &hC8
Const TW_NO_INFO        = &hF8
Const TW_BUS_ERROR        = &h00

ich habe die Konstanten nochmal rauskopiert.

Autor: AxelR. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
habe ich wirklich!

'Slave Receiver Codes
Const TW_SR_SLA_ACK        = &h60
Const TW_SR_ARB_LOST_SLA_ACK  = &h68
Const TW_SR_GCALL_ACK      = &h70
Const TW_SR_ARB_LOST_GCALL_ACK  = &h78
Const TW_SR_DATA_ACK      = &h80
Const TW_SR_GCALL_DATA_ACK    = &h90
Const TW_SR_DATA_NACK      = &h88
Const TW_SR_GCALL_DATA_NACK    = &h98
Const TW_SR_STOP        = &hA0
'Slave Transmitter Codes
Const TW_ST_SLA_ACK        = &hA8
Const TW_ST_ARB_LOST_SLA_ACK  = &hB0
Const TWCR_CMD_MASK        = &h0F
Const TWSR_STATUS_MASK      = &hF8
Const TW_ST_DATA_ACK      = &hB8
Const TW_ST_DATA_NACK      = &hC0
Const TW_ST_LAST_DATA      = &hC8
Const TW_NO_INFO        = &hF8
Const TW_BUS_ERROR        = &h00


Autor: AxelR. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
sowas...
//Slave Receiver Codes
Const TW_SR_SLA_ACK        = &h60
Const TW_SR_ARB_LOST_SLA_ACK  = &h68
Const TW_SR_GCALL_ACK      = &h70
Const TW_SR_ARB_LOST_GCALL_ACK  = &h78
Const TW_SR_DATA_ACK      = &h80
Const TW_SR_GCALL_DATA_ACK    = &h90
Const TW_SR_DATA_NACK      = &h88
Const TW_SR_GCALL_DATA_NACK    = &h98
Const TW_SR_STOP        = &hA0
'Slave Transmitter Codes
Const TW_ST_SLA_ACK        = &hA8
Const TW_ST_ARB_LOST_SLA_ACK  = &hB0
Const TWCR_CMD_MASK        = &h0F
Const TWSR_STATUS_MASK      = &hF8
Const TW_ST_DATA_ACK      = &hB8
Const TW_ST_DATA_NACK      = &hC0
Const TW_ST_LAST_DATA      = &hC8
Const TW_NO_INFO        = &hF8
Const TW_BUS_ERROR        = &h00


Autor: Ppp Mmm (sanic)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Konstanten habe ich auch in dem Sourcecode von der Application Note
super dokumentiert und eingebunden.
Wenn ich mir aber in der ISR das TWSR sichere und danach ausgeben lasse
kommt 0 raus.
Die Start/Stop Condition sieht auf dem Bus aber gut aus.
Controller habe ich momentan 2 Stück ( beides ATMega8 ) an nem 3,6864
MHz Quarz die ich beide ausprobiere.
UART etc. funktioniert wunderbar.

Autor: Ppp Mmm (sanic)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So,

Problem(e) gelöst!
Es waren mehrere Faktoren die eine Rolle gespielt haben:
1. Ich habe versucht nur die ISR laufen zu lassen ohne das neue
Aufrufen von TWI_start_receiver().
In der ISR von Atmel wird nach der Bearbeitung des TWI Prozesses TWEN
auf 0 gesetzt. Daher habe ich ein low auf meine SDA Leitung bekommen
worauf die CC2 dachte: ACK!

2. TWAR:
Die Adressierung mittels TWAR funktioniert nicht ganz wie beim
Datenblatt beschrieben. Normalerweise muss ja die Adresse ab dem 1. Bit
geschrieben werden. Das funktioniert allerdings nicht .... Die Adresse
schreibe ich jetzt ab dem 0. Bit und schon kann der Slave korrekt
adressiert werden.

Grüße

Autor: Thomas (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallöchen!

versuche verzweifelt einen TWI Slave Receiver in WinAVR -C auf
Interrupt-Basis zu programmieren. Leider funzt des nicht so wirklich.
Also wenn ich den Master einschalte, springt er in die ISR rein. Mehr
passiert nicht. Das TWSR hat immer 0 als Wert.

Sieht hier jemand einen Fehler im Programm?

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.