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
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
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
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
Hallo Mario, danke für den Tip, aber leider bringt das nicht-setzen des TWINT Bits nicht den gewünschten Erfolg. Grüße, Patrick
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
Anmerkung: beim puti ist der 2. Parameter die base für die Umrechnung des INTs in einen String ( siehe itoa() ). Grüße
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.
In meinem alten Code habe ich das schonmal probiert, da gab´s keinen besseren Effekt. Bin aber gerade dabei das umzustricken. Grüße
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
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
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
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
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
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 :-)
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
Habe nebenbei mal deinen Code ausprobiert, funktioniert leider auch nicht. Grüße
Ich darf vielleicht auch as beisteuern - ist zwar wiedermal "nur" FastAVR, aber aus C übernommen.
1 | '#################### I2C-Interrupt ######################## |
2 | Interrupt TWI(), Save All |
3 | Select Case TWSR |
4 | 'Slave receive codes |
5 | Case TW_SR_SLA_ACK '0x60 |
6 | GoTo twi_78 |
7 | Case TW_SR_ARB_LOST_SLA_ACK '0x68 |
8 | GoTo twi_78 |
9 | Case TW_SR_GCALL_ACK '0x70 |
10 | GoTo twi_78 |
11 | Case TW_SR_ARB_LOST_GCALL_ACK '0x78 |
12 | twi_78: |
13 | TWI_State = TWI_SLAVE_RX |
14 | TWI_RXD_INDEX = 0 |
15 | 'has receive byte and return ACK |
16 | $Asm |
17 | ldi r16, (1<<TWIE | 1<<TWINT| 1<<TWEA | 1<<TWEN) |
18 | out TWCR, r16 |
19 | $EndAsm |
20 | Case TW_SR_DATA_ACK '0x80 |
21 | GoTo twi_90 |
22 | Case TW_SR_GCALL_DATA_ACK '0x90 |
23 | twi_90: |
24 | |
25 | TWI_RXDATA(TWI_RXD_INDEX)= TWDR 'get previously received data |
26 | |
27 | Incr TWI_RXD_INDEX |
28 | If TWI_RXD_INDEX < TWI_BUF_SIZE And TWI_RXD_INDEX < MAX_RX_TWI_BYTE |
29 | Then 'check rx-buffer state |
30 | 'has receive byte and return ACK |
31 | $Asm |
32 | ldi r16, (1<<TWIE | 1<<TWINT | 1<<TWEA | 1<<TWEN) |
33 | out TWCR, r16 |
34 | $EndAsm |
35 | Else
|
36 | 'has receive byte and return NACK |
37 | TWI_RXD_INDEX = 0 |
38 | $Asm |
39 | ldi r16, (1<<TWIE | 1<<TWINT | 1<<TWEN ) |
40 | out TWCR, r16 |
41 | $EndAsm |
42 | End If |
43 | Case TW_SR_DATA_NACK '0x88 |
44 | GoTo twi_98 |
45 | Case TW_SR_GCALL_DATA_NACK '0x98 |
46 | twi_98: |
47 | 'has receive byte and return NACK |
48 | $Asm |
49 | ldi r16, (1<<TWIE | 1<<TWINT | 1<<TWEN |1<<TWSTO) |
50 | out TWCR, r16 |
51 | $EndAsm |
52 | |
53 | |
54 | Case TW_SR_STOP '0xA0 |
55 | |
56 | |
57 | TWI_RXD_INDEX = 0 |
58 | |
59 | twi_rx_handle() 'neue I2C-Daten liegen vor |
60 | |
61 | $Asm |
62 | ldi r16, (1<<TWIE | 1<<TWINT | 1<<TWEA | 1<<TWEN) |
63 | out TWCR, r16 |
64 | $EndAsm |
65 | |
66 | TWI_STATE = TWI_IDLE |
67 | 'switch to SR mode with SLA ACK |
68 | |
69 | |
70 | |
71 | |
72 | Case TW_ST_SLA_ACK |
73 | GoTo twi_b0 |
74 | Case TW_ST_ARB_LOST_SLA_ACK |
75 | twi_b0: |
76 | |
77 | TWI_STATE = TWI_SLAVE_TX |
78 | TWI_TXD_INDEX = 0 |
79 | GoTo twi_b8 |
80 | Case TW_ST_DATA_ACK |
81 | twi_b8: |
82 | |
83 | TWDR = TWI_TXDATA(TWI_TXD_INDEX) |
84 | |
85 | |
86 | Incr TWI_TXD_INDEX |
87 | If TWI_TXD_INDEX < TWI_BUF_SIZE Then $Asm |
88 | ldi r16, (1<<TWIE | 1<<TWINT | 1<<TWEA | 1<<TWEN) |
89 | out TWCR, r16 |
90 | $EndAsm |
91 | Else
|
92 | $Asm |
93 | ldi r16, (1<<TWIE | 1<<TWINT | 1<<TWEN) |
94 | out TWCR, r16 |
95 | $EndAsm |
96 | End If |
97 | Case TW_ST_DATA_NACK |
98 | GoTo twi_c8 |
99 | Case TW_ST_LAST_DATA |
100 | twi_c8: |
101 | TWI_TXD_INDEX = 0 |
102 | TWI_STATE =TWI_IDLE |
103 | $Asm |
104 | ldi r16, (1<<TWIE | 1<<TWINT | 1<<TWEA | 1<<TWEN) |
105 | out TWCR, r16 |
106 | $EndAsm |
107 | GoTo end_ints |
108 | Case TW_NO_INFO '0xF8 |
109 | GoTo end_ints |
110 | Case TW_BUS_ERROR |
111 | TWI_STATE = TWI_IDLE |
112 | $Asm |
113 | ldi r16, (1<<TWIE | 1<<TWINT | 1<<TWEA | 1<<TWSTO | 1<<TWEN) |
114 | out TWCR, r16 |
115 | $EndAsm |
116 | end_ints: |
117 | |
118 | End Select |
119 | |
120 | |
121 | end_twi: |
122 | 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.
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
Das ist komisch! irgentein Wert sollte drinn stehen, und wenn's nur 0xF8 ist.
1 | 'Slave Receiver Codes |
2 | Const TW_SR_SLA_ACK = &h60 |
3 | Const TW_SR_ARB_LOST_SLA_ACK = &h68 |
4 | Const TW_SR_GCALL_ACK = &h70 |
5 | Const TW_SR_ARB_LOST_GCALL_ACK = &h78 |
6 | Const TW_SR_DATA_ACK = &h80 |
7 | Const TW_SR_GCALL_DATA_ACK = &h90 |
8 | Const TW_SR_DATA_NACK = &h88 |
9 | Const TW_SR_GCALL_DATA_NACK = &h98 |
10 | Const TW_SR_STOP = &hA0 |
11 | 'Slave Transmitter Codes |
12 | Const TW_ST_SLA_ACK = &hA8 |
13 | Const TW_ST_ARB_LOST_SLA_ACK = &hB0 |
14 | Const TWCR_CMD_MASK = &h0F |
15 | Const TWSR_STATUS_MASK = &hF8 |
16 | Const TW_ST_DATA_ACK = &hB8 |
17 | Const TW_ST_DATA_NACK = &hC0 |
18 | Const TW_ST_LAST_DATA = &hC8 |
19 | Const TW_NO_INFO = &hF8 |
20 | Const TW_BUS_ERROR = &h00 |
ich habe die Konstanten nochmal rauskopiert.
habe ich wirklich!
1 | 'Slave Receiver Codes |
2 | Const TW_SR_SLA_ACK = &h60 |
3 | Const TW_SR_ARB_LOST_SLA_ACK = &h68 |
4 | Const TW_SR_GCALL_ACK = &h70 |
5 | Const TW_SR_ARB_LOST_GCALL_ACK = &h78 |
6 | Const TW_SR_DATA_ACK = &h80 |
7 | Const TW_SR_GCALL_DATA_ACK = &h90 |
8 | Const TW_SR_DATA_NACK = &h88 |
9 | Const TW_SR_GCALL_DATA_NACK = &h98 |
10 | Const TW_SR_STOP = &hA0 |
11 | 'Slave Transmitter Codes |
12 | Const TW_ST_SLA_ACK = &hA8 |
13 | Const TW_ST_ARB_LOST_SLA_ACK = &hB0 |
14 | Const TWCR_CMD_MASK = &h0F |
15 | Const TWSR_STATUS_MASK = &hF8 |
16 | Const TW_ST_DATA_ACK = &hB8 |
17 | Const TW_ST_DATA_NACK = &hC0 |
18 | Const TW_ST_LAST_DATA = &hC8 |
19 | Const TW_NO_INFO = &hF8 |
20 | Const TW_BUS_ERROR = &h00 |
sowas...
1 | //Slave Receiver Codes
|
2 | Const TW_SR_SLA_ACK = &h60 |
3 | Const TW_SR_ARB_LOST_SLA_ACK = &h68 |
4 | Const TW_SR_GCALL_ACK = &h70 |
5 | Const TW_SR_ARB_LOST_GCALL_ACK = &h78 |
6 | Const TW_SR_DATA_ACK = &h80 |
7 | Const TW_SR_GCALL_DATA_ACK = &h90 |
8 | Const TW_SR_DATA_NACK = &h88 |
9 | Const TW_SR_GCALL_DATA_NACK = &h98 |
10 | Const TW_SR_STOP = &hA0 |
11 | 'Slave Transmitter Codes |
12 | Const TW_ST_SLA_ACK = &hA8 |
13 | Const TW_ST_ARB_LOST_SLA_ACK = &hB0 |
14 | Const TWCR_CMD_MASK = &h0F |
15 | Const TWSR_STATUS_MASK = &hF8 |
16 | Const TW_ST_DATA_ACK = &hB8 |
17 | Const TW_ST_DATA_NACK = &hC0 |
18 | Const TW_ST_LAST_DATA = &hC8 |
19 | Const TW_NO_INFO = &hF8 |
20 | Const TW_BUS_ERROR = &h00 |
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.
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
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?
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.