Hallo, ich möchte mit dem ATtiny841 als I2C-Slave benutzen. Dazu habe ich schon intensiv bei Google und Co. gesucht, bin aber nicht fündig geworde. Einige I2C-Slave Beispiele konnte finden, die sind aber für den ATtiny841 nicht geeignet. Deshalb meine Frage: Hat jemand ein simples Beispiel, Aplication Note oder Lib für den ATtiny841/441 in C/GCC, um diesen als I2C-Slave zu betreiben?
Das Datenblatt hatte ich bereits gelesen, hilft mir aber nicht weiter, da dort kein Beispiel enthalten ist. Die trockene Beschreibung der Register und dessen Flags hat mir leider kein Licht aufgehen lassen, wie ich das in C realisieren kann. Ich habe konkret nach einen Beispiel in C gefragt, weil genau das mir weiter helfen würde. Ich Beschreibe mal das was ich mit den Tiny machen möchten etwas detaillierter. Der Tiny soll als Slave an einen I2C-Bus hängen. Wenn er vom Master mit seiner 7-Bit Adresse ein Befehls Byte empfängt, antwortet er mit 4 Bytes. Ist eigendlich nichts kompliziertes, aber wenn man es zum ersten mal macht, ist es erstmal doch ein wenig schwierig.
Da gibts irgendwo ein i2c via USI Master/Slave Document für die Tinys ;) Gibts hier im forum auch hunderte Beispiele.
http://www.atmel.com/Images/doc2565.pdf Man sollte immer erstmal auf der Seite zum Controller unter "Documents" schauen. Da stehen viele allgemein gültige, aber auch spezielle auf den Controller zugeschnittene Dokumente. http://www.atmel.com/devices/ATTINY841.aspx?tab=documents
:
Bearbeitet durch User
Ja, dem ATtiny841 hat Atmel wieder einen völlig anderen I2C-Controller verpaßt. Wär ja sonst langweilig. Wenns keine App-Note für den gibt, siehts schlecht aus.
Die ATtiny841/441 unterscheiden sich beim I2C/TWI-Bus von den anderen Tinys, die Register unterscheiden sich aber erheblich und nicht nur von der Bezeichnung her. Die 841/441 haben wohl ausschließlich einen Hardware I2C-Slave Bus. Ich habe das besagte Beispiel (AVR311 http://www.atmel.com/images/AVR311.zip) natürlich auch schon angesehen, es läßt sich aber nicht so auf die ATtiny841/441 übertragen. Ich habe dieses Beispiel sogar schon versucht mit Hilfe des Datenblatts zu migrieren. Das scheitert aber daran das die 841/441 von der Steuerung her anders sind.
Peter Dannegger schrieb: > Ja, dem ATtiny841 hat Atmel wieder einen völlig anderen I2C-Controller > verpaßt. Wär ja sonst langweilig. > > Wenns keine App-Note für den gibt, siehts schlecht aus. Auf der Seite gibt es einiges mit I2C. Hatte nur das mit dem Slave gepostet, weil die Frage danach kam.
Die hier schon probiert? http://www.jtronics.de/avr-projekte/library-i2c-twi-slave-usi.html Die ging bei mir auf Anhieb, allerdings mit nem anderen Tiny als Deinem, aber vielleicht kannst Du sie ja passend machen, denn wenn der ATtiny841 die selbe Art von USI-Schnittstelle hat wie die ganzen anderen unterstützten Controller (was ich vermute) wäre das der optimale Ausgangspunkt.
Ja, funktoniert leider nicht steht aber auch Zitat: Unterstützt werden bisher folgende Controller: ATtiny2313, ATtiny25, ATtiny45, ATtiny85, ATtiny26, ATtiny261, ATtiny461, ATtiny861, ATmega165, ATmega325, ATmega3250, ATmega645, ATmega6450, ATmega329, ATmega3290, ATmega169,ATtiny24, ATtiny44, ATtiny88 Bei einen ATMega hab ich diese Lib auch schon benutzt, hat auch ohne probleme geklappt. Die tiny 441/881 sind wohl nicht kompatibel und werden auch nicht explizit unterstützt.
Sehe gerade daß der 841 eine "richtige" I²C Hardware eingebaut hat, genauso wie die Megas. Dann müsste es doch relativ einfach sein das notfalls zu Fuß hinzubekommen, evtl auch mit Inspiration der entsprechenden libs für irgendeinen Mega?
Ich habe jetzt mal mit Hilfe einiger Libs und Datenblatt diese Funktionsweise hergeleitet. Leider ist irgendwo noch der Wurm trin. Sieht vieleicht jemand, wo der Fehler sein könnte?
1 | #include <avr/io.h> |
2 | #include <avr/interrupt.h> |
3 | |
4 | #define I2C_SLAVE_ADDRESS 0x20 |
5 | #define I2C_DATA_INTERRUPT 0x80 |
6 | #define I2C_BUS_COLLISION 0x08 |
7 | #define I2C_ADDRESS_STOP_MATCH 0x40 |
8 | |
9 | volatile uint8_t command; |
10 | volatile uint8_t tx_buf[4]; |
11 | volatile uint8_t tx_buf_index = 0; |
12 | |
13 | |
14 | int main(void) |
15 | { |
16 | TWSA = I2C_SLAVE_ADDRESS; |
17 | TWSD = 0xFF; |
18 | |
19 | TWSCRA = (1 << TWEN) // Two-Wire Interface Enable |
20 | | (1 << TWSHE) // TWI SDA Hold Time Enable |
21 | | (1 << TWASIE) // TWI Address/Stop Interrupt Enable |
22 | | (1 << TWSIE) // TWI Stop Interrupt Enable |
23 | | (1 << TWDIE); // TWI Data Interrupt Enable |
24 | |
25 | sei(); |
26 | while(1) |
27 | { |
28 | if(command != 0x00) |
29 | { |
30 | switch(command) |
31 | { |
32 | case 0x01: |
33 | // Test Daten |
34 | tx_buf[0] = 0x01; |
35 | tx_buf[1] = 0x02; |
36 | tx_buf[2] = 0x03; |
37 | tx_buf[3] = 0x04; |
38 | tx_buf_index = 0; |
39 | break; |
40 | } |
41 | |
42 | command = 0x00; |
43 | } |
44 | } |
45 | } |
46 | |
47 | ISR( TWI_SLAVE_vect ) |
48 | { |
49 | uint8_t status = TWSSRA & 0xC0; |
50 | |
51 | if (status & I2C_DATA_INTERRUPT) // Daten wurden vom Master empfangen oder werden angefordert |
52 | { |
53 | if (TWSSRA & (1 << TWDIR)) // Master fordert Daten vom Slave an |
54 | { |
55 | if(tx_buf_index >= sizeof(tx_buf)) |
56 | { |
57 | tx_buf_index=0; |
58 | } |
59 | |
60 | TWSD = tx_buf[tx_buf_index]; |
61 | tx_buf_index++; |
62 | |
63 | TWSCRB = (uint8_t) ((1<<TWCMD1)|(1<<TWCMD0)); |
64 | } |
65 | else // Master sendet Daten zum Slave |
66 | { |
67 | TWSCRB |= (uint8_t) ((1<<TWCMD1)|(1<<TWCMD0)); |
68 | command = TWSD; |
69 | } |
70 | } |
71 | else if (status & TWI_ADDRESS_STOP_MATCH) |
72 | { |
73 | if (TWSSRA & TWI_BUS_COLLISION) |
74 | { |
75 | TWSCRB = (uint8_t) (1<<TWCMD1); |
76 | } |
77 | else |
78 | { |
79 | if (TWSSRA & (1<<TWAS)) |
80 | { |
81 | // ACK |
82 | TWSCRB = (uint8_t) ((1<<TWCMD1)|(1<<TWCMD0)); |
83 | } |
84 | else |
85 | { |
86 | // Stop Condition |
87 | TWSCRB = (uint8_t) (1<<TWCMD1); |
88 | } |
89 | } |
90 | } |
91 | } |
Kannst Du beschreiben, was nicht geht? Bis wo funktioniert es noch?
Ich habe einen Master Mikcrocontroller der das Byte 0x01 senden und vier Bytes läd. Ich erhalte nicht 0x01 0x02 0x03 0x04 sondern 0xFF 0xFF 0xFF 0xFF. Da an diesen Master noch ein weiterer I2C-Sensor Angeschlossen ist, der auch funktioniert. Denke ich das der Master korrekt arbeitet. Ich habe leider kein Oszi, sonst hätte ich mir die Pegel angesehen.
Oha, du brauchst wenigstens (oder besser noch) ein Logic Analyzer, sonst wird das schwierig. Wie wäre es damit: Beitrag "10 Euro Logikanalyzer" Hilfreich wäre es noch wenn Du die genaue Abfolge, die Du per Master sendest noch einmal aufschreiben könntest. Mit "Senden" und "Laden" kann man als Außenstehender nicht viel anfangen. Also z.B. so I2C-Start, Adr. 01, I2C-Restart, usw...
Zepp schrieb: > Ich habe einen Master Mikcrocontroller der das Byte 0x01 senden und vier > Bytes läd. Ich erhalte nicht 0x01 0x02 0x03 0x04 sondern 0xFF 0xFF 0xFF > 0xFF. > Da an diesen Master noch ein weiterer I2C-Sensor Angeschlossen ist, der > auch funktioniert. Denke ich das der Master korrekt arbeitet. > Ich habe leider kein Oszi, sonst hätte ich mir die Pegel angesehen. Ich würde mir an deiner Stelle mal an den Tiny das Oszi des kleinen Mannes ankabeln: ein paar LED. Denn im Tiny stocherst du im Nebel und das muss sich ändern. Du willst zb wissen, ob der Interrupt überhaupt ausgelöst wird. Eine LED kann dir das verraten. Du willst wissen, ob du einen Dateninterrupt kriegst. Wieder: Eine LED kann dir das verraten. Ja, ich weiß. Es ist mühsam. Aber wenn man nichts anderes zum Debuggen hat, dann muss das eben reichen.
Der Master hat folgende Funktion die ich so aufrufe:
1 | uint32_t result = Tiny841_Read(0x01); |
Das Ergebnis zeige ich mir dann noch auf den LCD an.
1 | uint32_t Tiny841_Read(uint8_t command) |
2 | { |
3 | int32_t returnValue; |
4 | |
5 | i2c_Start(); |
6 | i2c_put(I2C_SLAVE_ADDRESS); // I2C-Adresse und 0 für Write |
7 | i2c_put(command); // Befehlscode |
8 | i2c_Stop(); |
9 | |
10 | i2c_Start(); |
11 | i2c_put(I2C_SLAVE_ADDRESS | 1); // I2C-Adresse und 1 für Read |
12 | returnValue = i2c_get_ack()<<24; |
13 | returnValue |= i2c_get_ack()<<16; |
14 | returnValue |= i2c_get_ack()<<8; |
15 | returnValue |= i2c_get(); |
16 | i2c_Stop(); |
17 | |
18 | return returnValue; |
19 | } |
Da hat Karl Heinz absolut recht. Hast Du vielleicht sonstige Debug-Möglichkeiten, z.B. AVR Dragon mit Single-Wire Debug? Oder ist das zum 841 noch nicht kompatibel?
Karl Heinz schrieb: > Ich würde mir an deiner Stelle mal an den Tiny das Oszi des kleinen > Mannes ankabeln: ein paar LED. > > Denn im Tiny stocherst du im Nebel und das muss sich ändern. > Du willst zb wissen, ob der Interrupt überhaupt ausgelöst wird. Eine LED > kann dir das verraten. Du willst wissen, ob du einen Dateninterrupt > kriegst. Wieder: Eine LED kann dir das verraten. > > Ja, ich weiß. Es ist mühsam. Aber wenn man nichts anderes zum Debuggen > hat, dann muss das eben reichen. Gute Idee, aber leider sind schon alle Pins für die eigendliche Aufgabe Sensoren belegt. Der Tiny hat ja nur wenige Pins.
Harald schrieb: > Da hat Karl Heinz absolut recht. Hast Du vielleicht sonstige > Debug-Möglichkeiten, z.B. AVR Dragon mit Single-Wire Debug? Oder ist das > zum 841 noch nicht kompatibel? Zur Zeit habe ich nur den AVR ISP MKII, ich lege mir aber bald einen Dragon zu. Ich werde mir mal von einen Bekannten ein Oszi leihen gehen.
Harald schrieb: > Oha, du brauchst wenigstens (oder besser noch) ein Logic Analyzer, sonst > wird das schwierig. Wie wäre es damit: > > Beitrag "10 Euro Logikanalyzer" > > Hilfreich wäre es noch wenn Du die genaue Abfolge, die Du per Master > sendest noch einmal aufschreiben könntest. Mit "Senden" und "Laden" kann > man als Außenstehender nicht viel anfangen. Also z.B. so > > I2C-Start, Adr. 01, I2C-Restart, usw... Der Logikanalyzer sieht für den Preis wirklich sehr gut aus.
Mach das mal mit der LED berichte, ob Du überhaupt in den Interrupt kommst. Und hast Du mal mit einem anderen Teilnehmer getestet, ob Dein Master überhaupt korrekt arbeitet?
Harald schrieb: > Mach das mal mit der LED berichte, ob Du überhaupt in den Interrupt > kommst. Und hast Du mal mit einem anderen Teilnehmer getestet, ob Dein > Master überhaupt korrekt arbeitet? Der Master arbeitet, ich habe da noch einen Temperatur Sensor und der liefert korrekte Werte. Ich werde mir ein Ozi organisieren und irgenwie eine LED tran basteln.
Zepp schrieb: > Gute Idee, aber leider sind schon alle Pins für die eigendliche Aufgabe > Sensoren belegt. Der Tiny hat ja nur wenige Pins. Tja. Du hast 2 Möglichkeiten. Entweder du verschaffst dir eine Möglichkeit, wie du dem Tiny bei der Arbeit zusehen kannst um rauszufinden was da abgeht. Oder du stellst dich auf den Standpunkt "dich trifft keine Schuld, du tust nichts und es geht eben nicht". Nichts gegen ein Oszi. Aber was wirst du am Bus sehen? das der Tiny keinen Acknowledge bringt. Das kann ich dir auch so sagen. Dazu brauchst du kein Oszi.
:
Bearbeitet durch User
Karl Heinz schrieb: > Zepp schrieb: > >> Gute Idee, aber leider sind schon alle Pins für die eigendliche Aufgabe >> Sensoren belegt. Der Tiny hat ja nur wenige Pins. > > Tja. > Du hast 2 Möglichkeiten. > > Entweder du verschaffst dir eine Möglichkeit, wie du dem Tiny bei der > Arbeit zusehen kannst um rauszufinden was da abgeht. > Oder du stellst dich auf den Standpunkt "dich trifft keine Schuld, du > tust nichts und es geht eben nicht". > > Nichts gegen ein Oszi. Aber was wirst du am Bus sehen? das der Tiny > keinen Acknowledge bringt. Das kann ich dir auch so sagen. Dazu brauchst > du kein Oszi. Mein Problem dabei war halt, das ist schon alles fertig auf einer Platine aufgelötet. Ich werde jetzt einen der Sensoren wieder heruter löten, um an dessen Pads ein Paar LEDs tran machen können. Ich war natürlich erstmal ein wenig gehemmt, da wieder Bauteile heruter zu löten. Aber da hab ich wohl keine andere Wahl.
Ich habe es jetzt geschafft zwei LEDs anzubringen. In der Interrupt Funktion sollte jetzt LED1 beim empfangen des Befehls Byte eingeschaltet werden und LED2 beim senden der 4 Antwort Bytes. Mein Master hat ein LCD-Bildschirm und Tatsatur, deshalb kann ich per Tastendruck steuern wann der Master eine Anfrage zum Slave sendet. Das Ergebnis ist LED2 leuchtet und LED1 nicht. d.h das Befehls Byte (im Code ##1##) wurde nicht erkannt, aber warum?
1 | #include <avr/io.h> |
2 | #include <avr/interrupt.h> |
3 | |
4 | #define I2C_SLAVE_ADDRESS 0x20 |
5 | #define I2C_DATA_INTERRUPT 0x80 |
6 | #define I2C_BUS_COLLISION 0x08 |
7 | #define I2C_ADDRESS_STOP_MATCH 0x40 |
8 | |
9 | volatile uint8_t command; |
10 | volatile uint8_t tx_buf[4]; |
11 | volatile uint8_t tx_buf_index = 0; |
12 | |
13 | volatile uint32_t debug_counter = 0; |
14 | #define DEBUG0_ON() (PORTA &= ~(1 << 0)) |
15 | #define DEBUG0_OFF() (PORTA |= (1 << 0)) |
16 | #define DEBUG1_ON() (PORTA &= ~(1 << 1)) |
17 | #define DEBUG1_OFF() (PORTA |= (1 << 1)) |
18 | |
19 | |
20 | int main(void) |
21 | { |
22 | TWSA = I2C_SLAVE_ADDRESS; |
23 | TWSD = 0xFF; |
24 | |
25 | TWSCRA = (1 << TWEN) // Two-Wire Interface Enable |
26 | | (1 << TWSHE) // TWI SDA Hold Time Enable |
27 | | (1 << TWASIE) // TWI Address/Stop Interrupt Enable |
28 | | (1 << TWSIE) // TWI Stop Interrupt Enable |
29 | | (1 << TWDIE); // TWI Data Interrupt Enable |
30 | |
31 | // DEBUG - Definiert die Pins als Ausgänge |
32 | DDRA |= (1 << 0) | (1 << 1); |
33 | DEBUG0_OFF(); |
34 | DEBUG1_OFF(); |
35 | |
36 | sei(); |
37 | while(1) |
38 | { |
39 | // Schaltet die LED nach kurzer Zeit wieder ab |
40 | if(debug_counter++ >= 200000) |
41 | { |
42 | DEBUG0_OFF(); |
43 | DEBUG1_OFF(); |
44 | debug_counter = 0; |
45 | } |
46 | |
47 | if(command != 0x00) |
48 | { |
49 | switch(command) |
50 | { |
51 | case 0x01: |
52 | // Test Daten |
53 | tx_buf[0] = 0x01; |
54 | tx_buf[1] = 0x02; |
55 | tx_buf[2] = 0x03; |
56 | tx_buf[3] = 0x04; |
57 | tx_buf_index = 0; |
58 | break; |
59 | } |
60 | |
61 | command = 0x00; |
62 | } |
63 | } |
64 | } |
65 | |
66 | ISR( TWI_SLAVE_vect ) |
67 | { |
68 | uint8_t status = TWSSRA & 0xC0; |
69 | |
70 | if (status & I2C_DATA_INTERRUPT) // Daten wurden vom Master empfangen oder werden angefordert |
71 | { |
72 | if (TWSSRA & (1 << TWDIR)) // Master fordert Daten vom Slave an |
73 | { |
74 | if(tx_buf_index >= sizeof(tx_buf)) |
75 | { |
76 | tx_buf_index=0; |
77 | } |
78 | |
79 | TWSD = tx_buf[tx_buf_index]; |
80 | tx_buf_index++; |
81 | |
82 | TWSCRB = (uint8_t) ((1<<TWCMD1)|(1<<TWCMD0)); |
83 | |
84 | //************************* |
85 | DEBUG1_ON(); |
86 | debug_counter = 0; |
87 | //************************* |
88 | } |
89 | else // Master sendet Daten zum Slave |
90 | { |
91 | // ##1## |
92 | |
93 | TWSCRB |= (uint8_t) ((1<<TWCMD1)|(1<<TWCMD0)); |
94 | command = TWSD; |
95 | |
96 | //************************* |
97 | DEBUG0_ON(); |
98 | debug_counter = 0; |
99 | //************************* |
100 | } |
101 | } |
102 | else if (status & I2C_ADDRESS_STOP_MATCH) |
103 | { |
104 | if (TWSSRA & I2C_BUS_COLLISION) |
105 | { |
106 | TWSCRB = (uint8_t) (1<<TWCMD1); |
107 | } |
108 | else |
109 | { |
110 | if (TWSSRA & (1<<TWAS)) |
111 | { |
112 | // ACK |
113 | TWSCRB = (uint8_t) ((1<<TWCMD1)|(1<<TWCMD0)); |
114 | } |
115 | else |
116 | { |
117 | // Stop Condition |
118 | TWSCRB = (uint8_t) (1<<TWCMD1); |
119 | } |
120 | } |
121 | } |
122 | } |
Das Problem habe ich jetzt gelöst, der Master war nur ein bischen zu schnell. Ich habe beim Master, die Übertragunggeschwindigkeit ein bischen reduziert und daraufhin klappt jetzt die Kommunikation zwischen Master und Slave. Ich danke allen, für die Hilfe und Tipps. Das könnte sogar als Muster Beispiel für den ATtiny441/841 taugen, ist aufs nötigste beschrängt und deshalb auch gut überschaubar.
Hallo Zepp, Auch Dir ein frohes neues Jahr! Danke für den Code, für dieses neue I2C Modul ist die Anzahl an verfügbarem Code ja noch recht dünn. Hast Du die Frequenz gesenkt oder die Zeit zwischen den Bytes erhöht? Was hattest Du vorher und was nachher? Falls die Frequenz viel zu hoch war könnten ja ein rein elektrisches Problem die Ursache gewesen sein.
Hallo Harald, ich habe die Zeit zwischen den Bytes von 2µs auf 20µs erhöht.
Sollte der Slave den Master nicht eigentlich selbst auf das gerade eben Nötige herunterbremsen können? Bei der lib für die anderen ATTiny funktioniert das bei mir, da kann ich 400kHz takten und dann schön das Wirken des Clock-Stretching auf dem Oszilloskop beobachten.
:
Bearbeitet durch User
Bernd K. schrieb: > Sollte der Slave den Master nicht eigentlich selbst auf das gerade > eben > Nötige herunterbremsen können? Bei der lib für die anderen ATTiny > funktioniert das bei mir, da kann ich 400kHz takten und dann schön das > Wirken des Clock-Stretching auf dem Oszilloskop beobachten. Mein Master hat eine Software I2C Schnittstelle, der Slave hingegen eine Hardware I2C-Schnittstelle. Ich gehe davon aus das dieses Beispiel noch Verbesserungspotenzial hat, es ist aber auf jeden Fall schon mal eine funktionierende Grundlage. Im Datenblatt ist z.B. beschieben, dass man via Flag zwischen Ack/Nack umschalten kann. Register TWSCRB (TWI Slave Control Register B) Flag TWAA (TWI Acknowledge Action). Ich habe dieses Flag nicht benutzt, da bei mir keine Probleme daraus entstanden sind. Wenn man aber zwei Hardware I2C-Schnittstellen hat, könnte dieses möglicherweise erforderlich sein. Wobei die Stop-Condition das problem mit dem fehlenden Nack eigendlich beheben sollte.
Was mich noch etwas stutzig macht ist die Lösung mit der reinen Zeitverzögerung zwischen den Bytes. Ob nun 2µs oder 20µs sollte normalerweise keine Rolle spielen, so schnell ist der Controller ja allemal. Genauere Analyse geht wahrscheinlich nur per Scope bzw. LA. In den nächsten Wochen brauche ich ebenfalls eine I2C Implementation auf einem ATTiny 841. Ich werde berichten.
Harald A. schrieb: > Was mich noch etwas stutzig macht ist die Lösung mit der reinen > Zeitverzögerung zwischen den Bytes. Ob nun 2µs oder 20µs sollte > normalerweise keine Rolle spielen, so schnell ist der Controller ja > allemal. Genauere Analyse geht wahrscheinlich nur per Scope bzw. LA. > In den nächsten Wochen brauche ich ebenfalls eine I2C Implementation auf > einem ATTiny 841. Ich werde berichten. Das könnte auch irgendwo am Software I2C des Masters liegen. Mit einen Hardware I2C-Master hätte man da sicher keine probleme. Das Bauteil ist bei mir in SMD und es wurde leider nicht daran gedacht ein Paar Pads einzufügen, damit man mal am I2C Bus mit nen Oszi schauen kann. Ich hatte ein Oszi da, aber habe es dann leider doch nicht benutz, weil es zu knifflich war mit den SMD Bauteilen. Jetzt habe ich das Oszi schon wieder abgegeben. Wegen der Geschwindigkeit, beide Micocontroller (Msster/Slave) haben einen 14 Mhz Quwarz. Hiermal mal ein Auszug vom Master, und was ich da geändert habe, ist nur der Wert in der Funktion I2CM_Delay(). Vorher habe ich 0xFFFFFFFF empfangen und nach der Änderung den erwarteten Testwert.
1 | void i2c_delay(void) |
2 | { |
3 | // Vorher 2 |
4 | _delay_us(20); |
5 | } |
6 | |
7 | void i2c_Start(void) |
8 | { |
9 | i2c_delay(); |
10 | SDA_HI(); // SDA High |
11 | i2c_delay(); |
12 | i2c_delay(); |
13 | SCL_HI(); // SCL High |
14 | i2c_delay(); |
15 | i2c_delay(); |
16 | SDA_LO(); // SDA Low |
17 | i2c_delay(); |
18 | i2c_delay(); |
19 | SCL_LO(); // SLC Low |
20 | } |
21 | |
22 | uint8_t i2c_get_ack(void) |
23 | { |
24 | uint8_t i; |
25 | uint8_t returnValue = 0; |
26 | |
27 | i2c_delay(); |
28 | SDA_HI(); |
29 | |
30 | for(i=0; i<8; i++) |
31 | { |
32 | i2c_delay(); |
33 | i2c_delay(); |
34 | returnValue <<= 1; |
35 | SCL_HI(); |
36 | i2c_delay(); |
37 | i2c_delay(); |
38 | if(SDAPIN & (1<<SDA)) |
39 | returnValue |= 1; |
40 | SCL_LO(); |
41 | } |
42 | |
43 | i2c_delay(); |
44 | |
45 | // Ack |
46 | SDA_LO(); |
47 | |
48 | i2c_delay(); |
49 | SCL_HI(); |
50 | i2c_delay(); |
51 | i2c_delay(); |
52 | SCL_LO(); |
53 | |
54 | return returnValue; |
55 | } |
56 | |
57 | uint8_t i2c_put(uint8_t b) |
58 | { |
59 | uint8_t i; |
60 | uint8_t ack; |
61 | |
62 | for (i=0;i<8;i++) |
63 | { |
64 | i2c_delay(); |
65 | if ( b & 0x80 ) |
66 | { |
67 | SDA_HI(); |
68 | } |
69 | else |
70 | { |
71 | SDA_LO(); |
72 | } |
73 | |
74 | i2c_delay(); |
75 | SCL_HI(); |
76 | i2c_delay(); |
77 | i2c_delay(); |
78 | SCL_LO(); |
79 | b <<= 1; |
80 | } |
81 | |
82 | i2c_delay(); |
83 | SDA_HI(); |
84 | i2c_delay(); |
85 | SCL_HI(); |
86 | |
87 | i2c_delay(); |
88 | ack = SDAPIN & (1<<SDA); |
89 | i2c_delay(); |
90 | |
91 | SCL_LO(); |
92 | i2c_delay(); |
93 | SDA_LO(); |
94 | |
95 | return (ack == 0); |
96 | } |
Zepp schrieb: > Mein Master hat eine Software I2C Schnittstelle Wenn Du den Quellcode des Masters hast, würde ich doch besser das Rücklesen der SCL-Leitung einbauen. Die darf dann natürlich nicht mehr als push/pull konfiguriert sein, sondern als open-drain (wie ja SDA auch).
Peter Dannegger schrieb: > Zepp schrieb: >> Mein Master hat eine Software I2C Schnittstelle > > Wenn Du den Quellcode des Masters hast, würde ich doch besser das > Rücklesen der SCL-Leitung einbauen. > Die darf dann natürlich nicht mehr als push/pull konfiguriert sein, > sondern als open-drain (wie ja SDA auch). Wie sieht denn Dein SCL_HI() Makro aus? Hoffentlich nicht
1 | DDRx &= ~SCL_BIT; |
sondern stattdessen so:
1 | DDRx &= ~SCL_BIT; |
2 | while (!(PINx & SCL_BIT)); // wait for slave to release SCL (clock stretching!) |
[Edit] Sorry, ging an falschen Adressaten, Frage bleibt aber bestehen.
:
Bearbeitet durch User
Bernd K. schrieb: > Wie sieht denn Dein SCL_HI() Makro aus? > > Hoffentlich nichtDDRx &= ~SCL_BIT; > > sondern stattdessen so:DDRx &= ~SCL_BIT; > while (!(PINx & SCL_BIT)); // wait for slave to release SCL (clock > stretching!) > > [Edit] Sorry, ging an falschen Adressaten, Frage bleibt aber bestehen. Hallo Bernd K., der Master ist nicht von mir, ich habe den Quellcode dazu so bekommen. Den werde ich noch überarbeiten müssen. Der aktuelle Master Code scheint auf die bestehnden Slave und deren Geschwindigkeit angepasst zu sein, das ist natürlich kein richtiges I2C. Die Kommunikationsprobleme, sind wohl genau darauf zurück zu führen.
1 | #define SDA_LO() SDADDR |= (1<<SDA) |
2 | #define SDA_HI() SDADDR &= ~(1<<SDA) |
3 | |
4 | #define SCL_LO() SCLDDR |= (1<<SCL) |
5 | #define SCL_HI() SCLDDR &= ~(1<<SCL) |
Zepp schrieb: >
1 | > #define SCL_HI() SCLDDR &= ~(1<<SCL) |
2 | > |
Das ist falsch! Wer immer Dir diesen Code gegeben hat hat selbst nicht begriffen wie I2C funktioniert. Änder das mal zu:
1 | #define SCL_HI() SCLDDR &= ~(1<<SCL); while(!(SCLPIN & (1<<SCL))) |
Obiges basiert auf der Annahme dass konsequenterweise SCLPIN das input-Register für diesen Pin ist wenn der Autor dieses Codes halbwegs konsequent war in seiner Namensgebung, notfalls entsprechend anpassen. Wenn der Master die SCL-Leitung loslässt dann MUSS er warten bis sie auch tatsächlich auf high geht, denn ein Slave kann jederzeit selbst diese Leitung auf low ziehen um den Master zu bremsen, jederzeit und so lange er will. Der Master muss an diesem Punkt innehalten und darf auf gar keinen Fall weitermachen bevor die Leitung auf high geht.
:
Bearbeitet durch User
@ Bernd K. Danke für den Hinweis, jetzt funktioniert der Master auch mit 2µs.
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.