Hallo Ich möchte zunächst den PCA9634 von einem Atmega32 aus ansteuern. Dabei soll die I2C-Routine aber so flexibel wie möglich gehalten werden, damit ich später ohne größeren Aufwand auch andere I2C Chips benutzen kann. Da der Contoller noch jede Menge anderen Kram zu tun hat, möchte ich das ganze per Interrupt machen. Den UART habe ich schon per Ringpuffer interruptbasiert aufgebaut, daher erscheint es mir recht sinnvoll, den I2C-Bus entsprechend zu realisieren, daß der Aufrufer einfach sein Datenpaket in den Puffer legt, der dann von der ISR abgearbeitet wird. Allerdings ist der UART ASCII-basiert, und ich kann einzelne Blöcke sehr schön mit dem Terminatorzeichen (0x00) von einander trennen. I2C ist aber ja Byte-Basiert und ich kann keine Zeichen reservieren. Daher hatte ich jetzt die Idee, jedem Datenpaket ein Byte voranzustellen, in dem die Anzahl der zu diesem Paket gehörenden Daten steht. Eventuell zur Überprüfung ans Ende eine Checksumme oder doch ein bestimmtes Zeichen. Nun möchte ich aber das Rad auch nicht neu erfinden, daher die Fragen, hat jemand sowas in der Art schonmal gemacht und könnte ein C Beispiel posten, bzw. bin ich mit meiner Idee völlig auf dem Holzweg? Viele Grüße Phil
:
Verschoben durch User
Ok um das nochmal auszugraben, ich hab das jetzt so gemacht wie oben beschrieben. Die Ringpuffer-Sache funktioniert auch soweit, allerdings ist mir bei der Ansteuerung der TWI-Schnittstelle eine Sache nicht ganz klar. Laut Datenblatt ist ja im TWCR das TWINT-Bit der Trigger, um die nächste Aktion durchzuführen. TWEN möchte ich eigentlich beim initialisieren einmal setzen und es dann gesetzt lassen. Die restlichen Bits TWSTA, TWSTO etc. wollte ich dann jeweils einzeln setzen und zuletzt das TWINT Bit setzen (um die Interrupt-Flag zu "clearen"). Problem an der Sache: Es funktioniert nicht. Wenn ich allerdings das TWCR explizit mit dem gesamten Bitmuster setze (TWCR = (1<<TWINT)|(0<<TWSTA)|...), dann funktioniert es plötzlich. Warum? Das zweite Problem betrifft die Stop-Condition. Diese löst nach Abschluss ja kein weiteres Interrupt aus. Da der angesprochene Chip aber per Default das soeben angewiesene erst nach Empfang der Stop-Condition ausführt, habe ich jetzt ein Problem, wenn ich mehrere Datenblöcke aus dem Puffer verschicken will. (Also STA | SLA+W | OFFSET1 | VALUE1 | STOP | STA | SLA+W | OFFSET2 |...) Eine Möglichkeit wäre, die Stop-Condition direkt in der ISR anzuweisen, und dort zu warten, bis sie ausgeführt wurde, aber das widerspricht ja ein wenig der Idee einer Interrupt-Routine. Ich möchte auch nicht in der Mainfunction ständig nachschauen, ob der TWI grade nicht mehr läuft, aber noch Datenblöcke vorhanden sind, auch das widerspricht der Idee eines möglichst eigenständigen Hardware-Treibers. Habt Ihr da einen Tip für mich?
Forum: Codesammlung Wenn ihr eigene Programme oder Anleitungen geschrieben habt könnt ihr sie hier posten. Fragen werden gelöscht!
Oh sorry, da bin ich wohl in die falsche Rubrik gerutscht. Soll ich den thread neu aufmachen oder könnte ein Mod ihn verschieben?
Ich habe es wie oben beschrieben gemacht. Ringpuffer mit Anzahl der Folgenden Daten und einfacher Plausibilitäts-Checksumme am Ende. Für die Stop-Condition prüfe ich in der ISR einfach, ob ein weiteres Paket im Puffer liegt (per mitlaufendem Zähler) und sende entweder nur eine Stop-Condition, oder Stop und danach Start sende (Stop ist ja nur ein Flankenwechsel der innerhalb eines Taktes geschieht).
Alles was Master Reciever ist, habe ich noch leer weil ichs im Moment nicht brauche. Geht bestimmt vieles auch eleganter, aber so tuts schonmal.
1 | /*TWI Interrupt Routine*/
|
2 | ISR(TWI_vect) |
3 | {
|
4 | static uint8_t mode; //Betriebsmodus |
5 | static uint8_t ct_data; //Datenzähler |
6 | static uint8_t data; //Interner Datenpuffer |
7 | static uint8_t checksum; //Checksumme |
8 | // static uint8_t input_buffer[128]; //Interner Pufferspeicher
|
9 | |
10 | mode = (TWSR & TW_STATUS_MASK); //Aktueller Modus aus Status Reg. |
11 | |
12 | if (number_i2c_out_frames == 0) //wenn keine Daten im Puffer liegen |
13 | {
|
14 | twi_stop(); //TWI stoppen |
15 | return; //Routine verlassen |
16 | }
|
17 | |
18 | switch (mode) //Abhängig von vorheriger Aktion |
19 | {
|
20 | case TW_ARB_LOST: //Wenn Kontrolle über Bus verloren (Anderer Master, Fehler) |
21 | {
|
22 | twi_error(mode); //Fehlerbehandlung ausführen |
23 | break; //Abfrage verlassen |
24 | }
|
25 | |
26 | case TW_START: //Start Condition gesendet |
27 | {
|
28 | ; //Weiter zu TW_REP_START, gleiche Anweisung! |
29 | }
|
30 | |
31 | case TW_REP_START: //Repeated Start Condition gesendet |
32 | {
|
33 | ct_data = get_from_ringbuffer(&i2c_out_buffer); //1. Element ist Anzahl der folgenden Daten |
34 | checksum = ct_data; //Checksumme beginnen |
35 | |
36 | if (ct_data-- == 0) //Wenn Null, bedeutet das entweder einen Fehler, oder daß der Puffer Leer ist |
37 | { //Decrement, da erstes Element nicht mit gesendet wird |
38 | twi_error(mode); //Fehlerbehandlung ausführen |
39 | return; |
40 | }
|
41 | //Kein Break!!! Weiter zu TW_MT_ADR_ACK
|
42 | }
|
43 | |
44 | case TW_MT_ADR_ACK: //Wenn SLA + W gesendet und bestätigt wurde |
45 | {
|
46 | ; //Kein Break!!! Weiter zu TW_MT_DATA_ACK |
47 | }
|
48 | |
49 | |
50 | case TW_MT_DATA_ACK: //Wenn Daten gesendet und bestätigt wurden |
51 | {
|
52 | data = get_from_ringbuffer(&i2c_out_buffer); //Daten aus Puffer lesen |
53 | checksum += data; //Checksumme aufaddieren |
54 | |
55 | if (ct_data == 0) //Wenn Datenzähler = 0 |
56 | {
|
57 | number_i2c_out_frames--; //Zahl der Frames reduzieren |
58 | if (checksum) twi_error(mode); //Wenn Checksumme ungleich 0 dann Fehlerbehandlung (eigentlich direkt Puffer reset) |
59 | else
|
60 | {
|
61 | twi_stop(); //Stop Condition senden |
62 | if(number_i2c_out_frames) //Wenn Zahl der Vorhandenen Frames nicht null |
63 | {
|
64 | twi_start(); //Neue Übertragung starten |
65 | }
|
66 | }
|
67 | return; //Routine verlassen |
68 | }
|
69 | |
70 | ct_data--; //Datenzähler verringern |
71 | TWDR = data; //Slave Adress + Datenrichtung in Senderegister schreiben |
72 | TWCR = TW_SET_DATA; //Senden |
73 | break; |
74 | }
|
75 | |
76 | case TW_MT_ADR_NACK: //Wenn SLA + W gesendet, aber nicht bestätigt wurde |
77 | {
|
78 | ; //Weiter zu TW_MT_DATA_NACK |
79 | }
|
80 | |
81 | case TW_MT_DATA_NACK: //Wenn Daten gesendet, aber nicht bestätigt wurden |
82 | {
|
83 | twi_error(mode); //Fehlerbehandlung |
84 | break; |
85 | }
|
86 | |
87 | case TW_MR_ADR_ACK: |
88 | {
|
89 | break; |
90 | }
|
91 | |
92 | case TW_MR_ADR_NACK: |
93 | {
|
94 | break; |
95 | }
|
96 | |
97 | case TW_MR_DATA_ACK: |
98 | {
|
99 | break; |
100 | }
|
101 | |
102 | case TW_MR_DATA_NACK: |
103 | {
|
104 | break; |
105 | }
|
106 | |
107 | case TW_NO_STATE: |
108 | {
|
109 | break; |
110 | }
|
111 | |
112 | case TW_BUS_ERROR: |
113 | {
|
114 | break; |
115 | }
|
116 | |
117 | default:
|
118 | {
|
119 | break; |
120 | }
|
121 | |
122 | return; |
123 | }
|
124 | }
|
Das ist mal mein code: es scheint ja auch zu funktionieren aber nur 1x. hast du da nen Tip für mich ? Uart vorher abschalten und nachher wieder anmachen ist noch zusätzlich drinne. Gruß Andreas ISR (TWI_vect ) { UCSR0B &= ~(_BV(RXCIE0)|(1<<RXEN0)|(1<<TXEN0)); if (state_TWI_INT == TWI_STATE_IDLE ) { } //------------------------------------ else if (state_TWI_INT == TWI_STATE_SEND_START_1 ) { state_TWI_INT++; //------------------------------------ // TWI_STATE_SEND_CMD : i2cSendByte(0xD2); state_TWI_INT++;state_TWI_INT++; } //------------------------------------ else if (state_TWI_INT == TWI_STATE_WAIT_OK_CMD ) { state_TWI_INT++; //------------------------------------ // TWI_STATE_SEND_ADR : i2cSendByte(i2c_adr); state_TWI_INT++; } //------------------------------------ else if (state_TWI_INT == TWI_STATE_WAIT_OK_ADR ) { state_TWI_INT++; //------------------------------------ // TWI_STATE_SEND_START_2 : i2cSendStart(); state_TWI_INT++; } //------------------------------------ else if (state_TWI_INT == TWI_STATE_WAIT_OK_START_2 ) { state_TWI_INT++; //------------------------------------ // TWI_STATE_REC_BYTE_1 : i2cReceiveByte(0); state_TWI_INT++; } //------------------------------------ else if (state_TWI_INT == TWI_STATE_WAIT_REC_BYTE_1) { i2c_rec_data = 0 + TWDR ; state_TWI_INT++; } //------------------------------------ else if (state_TWI_INT == TWI_STATE_FINISCH ) { i2cSendStop(); // cbi(TWCR, TWEN); // Disable TWI // sbi(TWCR, TWEN); i2c_adr = 0; i2c_cmd = 0; state_TWI_INT = TWI_STATE_IDLE; } //------------------------------------ else { state_TWI_INT = TWI_STATE_IDLE; } sbi(TWCR,TWINT); UCSR0B = _BV(RXCIE0)|(1<<RXEN0)|(1<<TXEN0); }
Hi Andreas, Kann es sein, daß Du da was verwechselst? USART und TWI sind zwei komplett unterschiedliche Dinge!
ja ich schalte meine uart ab während ich den Interrupt bearbeite damit nix schief geht
Ach so, ok. Also ich hab beide ohne Probleme parallel laufen, die Hardware macht das ja auch selbständig und das Interrupt wird erst ausgelöst, wenn die aktuelle ISR abgearbeitet ist. Was den restlichen Code angeht, wo genau hängts da denn, bzw. bis wohin kommt er?
Im Moment geht gar nix mehr ... ich meld mich wieder sobald ich wieder Interrupts bekomme
es ist gerade das write collition flag das mich im moment stört nachdem ich die Übertragung begonnen habe
Was für einen Chip willste denn ansprechen? Eventuell erwartet der erstmal eine Stop-Condotion meckert bei erneutem Start?
so geht wieder, aber nach die 2te komm geht nicht mehr sprich nach der ersten stopp, mit pollen gehts ja in der gleichen reihenfolge wie im interrupt handler
jetzt habe ich noch eingebaut twi disable -> enable am schluss jedes telgramms und jetzt gehts
Also das TWEN-Bit muss ja auch jedesmal mit gesetzt werden. Ich habe auch die Erfahrung gemacht, daß das einzelne setzen und löschen des Bits im Kontrollregister nicht funktioniert sondern das ganze Register auf einen Rutsch gesetzt werden muss. Warum das so ist verstehe ich aber auch nicht.
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.