Hallo, ohne Messgeräte schwierig zu sagen aber so ist es nunmal: Ich benutze die TinyWireM Lib für den Tiny84 um eine I2C Strecke zu einem ADXL345 zu bauen aber leider wird nur bei einem Power On Kaltstart das DeviceID 1 Mal (!) richtig ausgelesen. Danach wird kein weiteres Byte mehr richtig geschrieben und gelesen und auch nicht bei einem Warmstart mit RESET. Ich habe mit 8 und mit 1 Mhz herumgespielt, die Libs auf den Kopf gestellt. Aber alles ohne Erfolg und in die USI wollte ich nicht einsteigen,, nützt ohne Messgeräte sowieso nichts. Die Libs sind ja recht bekannt und scheinen zu funktionieren. Kennt einer das Problem?
Seit Jahrzehnten bekanntes Problem, schwer zu lösen: In Zeile 42 deines Programms fehlt ein "-".
Dazu braucht es keinen Code hier. Einbinden und Benutzen, fertig. I2C ist ja kein Hexenwerk. SPI geht leider kein Mode 3 mit dem USI, sonst würde ich den nehmen. Der ADXl345 unterstützt nämlich nur Mode 3.
Christian J. schrieb: > Dazu braucht es keinen Code hier. Einbinden und Benutzen, fertig. I2C > ist ja kein Hexenwerk. Du bist echt lustig! Alles ist ganz einfach und kein Hexenwerk, aber Du bekommst es nicht zum Laufen ;-) Die Erfahrungen die ich mit 'fertigen' USI-Routinen gemacht habe, sind enttäuschend. Entweder muß ich mich noch einmal daran setzen, selber brauchbaren Code zu schreiben, oder ich verwende doch lieber AVRs mit TWI in Hardware. Du bist hier ja bekannt, Alles ganz einfach frei Haus geliefert zu bekommen, aber das reale Leben sieht anders aus. Wenn Du nur die Master-Funktion brauchst, nimm eine reine Softwarelösung. Da hat man die Funktion besser im Griff.
Christian J. schrieb: > Ich benutze > die TinyWireM Lib für den Tiny84 um eine I2C Strecke zu einem ADXL345 zu > bauen Warum so aufwendig? Um einen Master zu implementieren ist das Herumgewürge mit der USI doch eher kontraproduktiv, diese Hardware kann am ehesten noch bei einem Slave hilfreich sein mit ihrer Fähigkeit Interrupts zu generieren bei den entscheidenden Ereignissen aber wenn man die USI entgegen ihrer Natur zur Arbeit als I²C Master überreden will braucht man mehr Code und kompliziertenen Code als würde man es mit simplem Bitbanging implementieren. Mein Vorschlag ist also: Implementiere I²C master mit Bitbanging. Bitte beachte hierzu daß die Ausgänge über DDR (und nicht über PORT) gesteuert werden müssen um das Verhalten von open-collector-Ausgängen zu erhalten. Hierzu müsste aber auch reichlich funktionierender Beispielcode im Forum und andernorts zu finden sein.
Christian J. schrieb: > leider wird nur bei einem Power On Kaltstart das DeviceID 1 > Mal (!) richtig ausgelesen. Danach wird kein weiteres Byte mehr richtig > geschrieben und gelesen und auch nicht bei einem Warmstart mit RESET. Klassischer Fall. 1. Der Read wird nicht richtig beendet, vermutlich wird das letzte Byte nicht mit NAK quitiert. Damit bleibt er im Lesemodus und reagiert nicht auf einen STOP oder START. 2.Problem: Es wird ignoriert, daß der µC keinen START erzeugen kann, weil der Bus nicht idle ist oder das ACK beim Senden der Adresse wird nicht ausgewertet, daher wird nicht erkannt, daß der Chip sich garnicht angesprochen fühlt. Anschließend wird der hängende Bus ausgelesen und gesagt, der Chip liefert falsche Werte. Zwei Maßnahmen sind nötig: das Lesen muß in Ordnung gebracht werden und Reset Routine ins I2C Init eingebaut werden Beitrag "I2C hängt sich auf" MfG Klaus
Christian J. schrieb: > SPI geht leider kein Mode 3 mit dem USI, sonst würde ich den nehmen. Der > ADXl345 unterstützt nämlich nur Mode 3. SPI in Software, also ohne Hardware wie USI, ist nicht grad Raketenbau.
Guten Morgen, ich muss etwas widersprechen: Aus dem Datenblatt geht für den ADXL345 hervor: SPI (3- and 4-wire) SPI ist wirklich kein Hexenwerk und benötigt keine Hardwareunterstützung. Christian J. schrieb: > Dazu braucht es keinen Code hier. Einbinden und Benutzen, fertig. I2C > ist ja kein Hexenwerk. > > SPI geht leider kein Mode 3 mit dem USI, sonst würde ich den nehmen. Der > ADXl345 unterstützt nämlich nur Mode 3.
Uwe S. schrieb: > Aus dem Datenblatt geht für den ADXL345 hervor: SPI (3- and 4-wire) > > SPI ist wirklich kein Hexenwerk und benötigt keine > Hardwareunterstützung. Wenn man sich ein wenig durchs Netz liest merkt man, dass das Begrifflichhkeiten oft verwechselt werden: 1) 3 Wire SPI heisst bei einigen, dass CS weggelassen wird und der Slave dauernd "selecteted" ist. Es gibt aber MISO und MOSI, bzw SDO und SDI 2) Bei anderen und auch beim ADXL345 ist es aber so, dass 3 Wire bedeutet, dass es einen SDI/O Pin gibt, der wechselweise Input und Output ist. Und das geht mit der USI nicht, deren Pins haben eine feste Direction. Da ich keine Bitbang Routinen schreiben werde (außer für SPI), sondern auf fertige Lösungen setze um mich dem eigentlichen Problem zu widmen bin ich auf Libs angewiesen. Ich habe (derzeit) keinerlei Messgeräte um ein Protokoll zu überprüfen und würde in einer Black Box herumstochern. Den Beitrag von Klaus muss ich mir nochmal näher anschauen .... allerdings nahm ich an dass "bewährte Libs" das richtig machen und die TinyWire wird oft zitiert.
Klaus schrieb: > 1. Der Read wird nicht richtig beendet, vermutlich wird das letzte Byte > nicht mit NAK quitiert. Damit bleibt er im Lesemodus und reagiert nicht > auf einen STOP oder START. Ich schließe mich dieser Meinung an.
A. K. schrieb: > SPI in Software, also ohne Hardware wie USI, ist nicht grad Raketenbau Aber sie ist langsam bei einem 1Mhz geclockten Chip und ich muss schnell auslesen, wenn der FIFO voll ist. Habe mir 2 Tage und Nächte um die Ohren gehauen den ADXL345 genau zu verstehen und richtig zu programmieren, wobei auch noch der blöde Chip einen Fehler hat, die Z-Achse ist beschädigt (Offset weit außerhalb der Spec, muss sowohl per Hardware als auch noch Software korriiert werden (-600) wurde vom Verkäufer bei ebay auch bestätigt, dass alle Chips defekt sind) und er sie reklamieren will.
Christian J. schrieb: > Da ich keine Bitbang Routinen schreiben werde Warum? Allein in der Zeit die Du damit verbracht hast das Eingangsposting zu formulieren hättest Du schon 8 Bits an einem Pin rausgeschoben, dabei am Taktpin gewackelt und das 9te Bit eingelesen.
Bitbanging ist zu langsam! Das muss mit dem Chip Clock takten. Hier ist mit die Hauptroutine des TinyWireM. Ich habe mich mit der USi nicht befasst, nur quer gelesen aber ist da ein Bug drin, dass der sich aufhängt? So ganz trivial ist das ja nicht, dass man da mal eben durchblickt.
Klaus schrieb: > 1. Der Read wird nicht richtig beendet, vermutlich wird das letzte Byte > nicht mit NAK quitiert. Damit bleibt er im Lesemodus und reagiert nicht > auf einen STOP oder START. > > 2.Problem: Es wird ignoriert, daß der µC keinen START erzeugen kann, > weil der Bus nicht idle ist oder das ACK beim Senden der Adresse wird > nicht ausgewertet, daher wird nicht erkannt, daß der Chip sich garnicht > angesprochen fühlt. Anschließend wird der hängende Bus ausgelesen und > gesagt, der Chip liefert falsche Werte. > > Zwei Maßnahmen sind nötig: das Lesen muß in Ordnung gebracht werden und > Reset Routine ins I2C Init eingebaut werden > Beitrag "I2C hängt sich auf" Ich vermute dass Klaus hier richtig liegt. Nur wo soll ich da suchen? Ohne Bus Analyzer? Mehr als das hier habe ich nicht geschrieben in der Hoffung, dass das richtig umgesetzt wird. / Lies ein einzelnes Byte aus dem Sensor aus uint8_t ReadADXL(uint8_t addr) { uint8_t val; // Sensor adressieren TinyWireM.beginTransmission(DEVICE); TinyWireM.send(addr); TinyWireM.endTransmission(); // Bytes anfordern TinyWireM.beginTransmission(DEVICE); TinyWireM.requestFrom(DEVICE, 1); val = TinyWireM.receive(); TinyWireM.endTransmission(); return (val); }
Christian J. schrieb: > Ich vermute dass Klaus hier richtig liegt. Nur wo soll ich da suchen? > Ohne Bus Analyzer? Indem Du beim Lesen des letzten Bytes ein NAK veranlasst anstelle des sonst üblichen ACK. Nach dieser kleinen Änderung wird es magischerweise funktionieren.
Bernd K. schrieb: > Indem Du beim Lesen des letzten Bytes ein NAK veranlasst anstelle des > sonst üblichen ACK. Und wie geht das? Ich bin da echt zu blond... lese mich nur grad hier mal ein: http://support.saleae.com/hc/en-us/articles/200730905-Learn-I2C-Inter-Integrated-Circuit UNd in der Lib scheint das auch richtig gemacht worden zu sein:
1 | /* Else masterRead cycle*/ |
2 | else |
3 | { |
4 | /* Read a data byte */ |
5 | DDR_USI &= ~(1<<PIN_USI_SDA); // Enable SDA as input. |
6 | *(msg++) = USI_TWI_Master_Transfer( tempUSISR_8bit ); |
7 | |
8 | /* Prepare to generate ACK (or NACK in case of End Of Transmission) */ |
9 | if( msgSize == 1) // If transmission of last byte was performed. |
10 | { |
11 | USIDR = 0xFF; // Load NACK to confirm End Of Transmission. |
12 | } |
13 | else |
14 | { |
15 | USIDR = 0x00; // Load ACK. Set data register bit 7 (output for SDA) low. |
16 | } |
17 | USI_TWI_Master_Transfer( tempUSISR_1bit ); // Generate ACK/NACK. |
18 | } |
19 | }while( --msgSize) ; // Until all data sent/received. |
20 | |
21 | if (!USI_TWI_Master_Stop()) |
22 | { |
23 | return (FALSE); // Send a STOP condition on the TWI bus. |
24 | } |
Christian J. schrieb: > Und wie geht das? Ich bin da echt zu blond... lese mich nur grad hier > mal ein: Siehst Du? Du gewinnst keine Zeit beim Versuch eine fremde Library einzubinden, am Ende musst Du doch die Zeit aufwenden I²C zu verstehen (ist jedoch nicht schwer), das musst Du letzten Endes so oder so machen. Nachdem Du es dann jedoch verstanden haben wirst wird sich die eigene Implementation (in nur einem drittel des Codes ganz ohne USI) quasi von selbst aufdrängen und Du wirst fortan nie wieder Probleme damit haben, bis ans Ender aller Zeiten.
Christian J. schrieb: > UNd in der Lib scheint das auch richtig gemacht worden zu sein: Diese Lib ist 5 mal so umfangreich wie es für ein simples Bitbanging (inclusive Clock stretching und allem Pipapo) erforderlich wäre. Und am Ende benutzt sie doch nur while()-Schleifen um zu warten und keine Interrupts, also null Vorteil durch Hardware. Und vom Lesen obigen Codes bekommt man übrigens Kopfschmerzen. Ich würde diesen Code in die Kategorie Softwaresatire einordnen. Wahrscheinlich wurde er geschrieben um zu demonstrieren wie überaus ungeeignet dieser Ansatz mit USI als Master doch ist.
Bernd K. schrieb: > Siehst Du? Du gewinnst keine Zeit beim Versuch eine fremde Library > einzubinden, am Ende musst Du doch die Zeit aufwenden I²C zu verstehen > (ist jedoch nicht schwer), das musst Du letzten Endes so oder so machen. Bernd, sie mir nicht böse aber an einem Schreibtisch, wo nur ein PC, ein Programmer, ein paar AVR Boards stehen und ein paar Bauteile aber kein einziges Messgerät sieht es düster aus mit "selbst machen". Außerdem haben sich schon tausende darin verewigt I2c Libs zu schreiben. Wie I2C funktioniert ist nicht schwer, zusammen mit der ominösen USI aber ist das ein Wochenwerk bis das läuft und dafür habe ich keine zeit nd keine Mittel, da es ein Herumgestochere wäre zu sehen was abgeht. Notfalls fliegt der Attiny raus und ein 328er kommt rein, auch wenn der mehr Platz braucht. Mit dem geht es ja einwandfrei.
Bernd K. schrieb: > null Vorteil durch Hardware Einfach nur mal ein "grep delay *.cpp" ist schon sehr aufschlussreich.
Georg G. schrieb: > Einfach nur mal ein "grep delay *.cpp" ist schon sehr aufschlussreich. Einzeiler enthalten keine verwertbare Informationen.
Den 328er gibt es in noch gut lötbar als 9x9mm oder eben auch in 4x4mm, jeweils Außenkante des Footprints. Muß es kleiner sein?
Christian J. schrieb: > Georg G. schrieb: > >> Einfach nur mal ein "grep delay *.cpp" ist schon sehr aufschlussreich. > > Einzeiler enthalten keine verwertbare Informationen. Die verwertbare Information in diesem Falle ist daß _delay-Funktionen zum Einsatz kommen um Bitzeiten abzuwarten. Würde das Ding die Hardware autonom arbeiten lassen dann wäre vielleicht ein Interrupt pro Byte nötig und kein einziges Delay. Der obige Code ist letztendlich auch nur ein verkapptes Bitbanging mit Warteschleifen, aber nicht direkt mit simplem Pin-Wackeln sondern schmerzhaft umständlich per Vergewaltigung der USI-Hardware und manuellem Erzeugen des Clocks um von hinten durch die Brust ins Auge das Ausgangsbit in gewünschter Weise wackeln zu lassen, jeder andere hätte es direkt über PIN, PORT und DDR gemacht in einem fünftel des Codes.
Bernd K. schrieb: > Der obige Code ist letztendlich auch nur ein verkapptes Bitbanging mit > Warteschleifen, aber nicht direkt mit simplem Pin-Wackeln sondern > schmerzhaft umständlich per Vergewaltigung der USI-Hardware und > manuellem Erzeugen des Clocks um von hinten durch die Brust ins Auge das > Ausgangsbit in gewünschter Weise wackeln zu lassen, jeder andere hätte > es direkt über PIN, PORT und DDR gemacht in einem fünftel des Codes. Ich habe gesehen. Ich weiss nicht wieso die eine USI erfunden haben, vermutlich um die Chipfläche zu reduzieren und den Aufwand auf Software umzulegen. Dennoch nehme ich an, dass diese USi funktioniert und auch der Lib Code. Grad mal mit einem 2402 Eprom geprüft, das gleiche Theater. Ich weiss jetzt mit I2C auch nicht weiter, keine Ahnung was ich da ändern könnte, von daher werde ich mal SPI umverdrahten auf dem Testboard. Die USI scheint das ja zu beherrschen und Mode 0 und Mode 3 sind so ähnlich, dass sie austauschbar sein müssten. Und wenn das nicht geht dann eben per Software SPI und 8 mhz statt 1Mhz.
Christian J. schrieb: > und ein 328er kommt rein Vorsicht: Der 328er hat einen Bug im I2C Master der noch viel mehr Arbeit und graue Haare macht, auch dort empfiehlt sich also Nichtverwendung desselben und Bitbanging stattdessen. Also nichts gewonnen. Du könntest schon fertig sein mit der Implementation, es sind schon 90 Minuten vergangen. Auch ohne Oszi.
Bernd K. schrieb: > Vorsicht: Der 328er hat einen Bug im I2C Master der noch viel mehr > Arbeit und graue Haare macht, Mit dem 328 habe ich es aber vorher aufgebaut und es spielte einwandfrei. Also treibt der Bug sein Unwesen woanders und Bugs werden meist in einer neuen Revision ausgemerzt wenn sie wesentlich sind. Der ADXL345 war auch total buggy in der Rev A.
Für sehr wenig Geld bekommst du aus China einen Logik Analysator. Und mit passendem Werkzeug ist die Fehlersuche deutlich angenehmer und schneller.
Christian J. schrieb: > // Sensor adressieren > TinyWireM.beginTransmission(DEVICE); > TinyWireM.send(addr); > TinyWireM.endTransmission(); Da geht das Elend schon los. Ich bin ziemlich sicher, daß TinyWireM.beginTransmission(DEVICE) einen Rückgabewert hat, in dem mitgeteilt wird, ob es geklappt hat. Leider unterscheiden die gängigen Libraries nicht zwischen "BUS nicht Idle" und "kein ACK vom Slave", daran könnte man auch ohne Analyzer etwas erkennen. Auch TinyWireM.send(addr) wird einen Rückgabewert haben, wenn nicht, gehören diese Funktionen ins Bitbucket. MfG Klaus
Boah..... ne Soft i2c nervt aber auch... vor allem wenn man den fehler nicht findet, warum da nur FF kommt, wenn man ein Byte lesen will. Da kann ja erstmal nicht falsch dran sein, oder? void WriteADXL(uint8_t addr, uint8_t val) { i2c_start(); i2c_write(DEVICE); i2c_write(addr); i2c_write(val); i2c_stop(); } uint8_t ReadADXL(uint8_t addr) { uint8_t val; // Sensor adressieren ( Zeiger auf Zielbyte) i2c_start(); i2c_write(DEVICE); i2c_write(addr); // Bytes anfordern val = i2c_read(1); i2c_stop(); return (val); }
Und die habe ich mir geklaut und optisch etwas aufbereitet: http://www.avrfreaks.net/forum/program-i2c-atmega16-problem?name=PNphpBB2&file=viewtopic&t=77455
1 | / PORT A |
2 | #define DDR DDRA |
3 | #define PORT PORTA |
4 | #define PIN PINA |
5 | |
6 | // Pins im Port festlegen |
7 | #define SDA 6 // Definition der Port Pins |
8 | #define SCL 4 |
9 | |
10 | // Makros für Pin Wackeln |
11 | #define SCL_set() (PORT |= _BV(SCL)) |
12 | #define SCL_clr() (PORT &= ~_BV(SCL)) |
13 | #define SCL_out() (DDR |= _BV(SCL)) |
14 | |
15 | #define SDA_set() (PORT |= _BV(SDA)) |
16 | #define SDA_clr() (PORT &= ~_BV(SDA)) |
17 | #define SDA_read() (PIN & _BV(SDA)) |
18 | #define SDA_out() (DDR |= _BV(SDA)) |
19 | #define SDA_in() (DDR &= ~_BV(SDA)) |
20 | |
21 | //////////////////////////////////////////////////////////////////////////////////////////// |
22 | void delay(void) |
23 | { |
24 | delayMicroseconds(10); |
25 | } |
26 | //////////////////////////////////////////////////////////////////////////////////////////// |
27 | void i2c_write(unsigned char data) |
28 | { |
29 | char index; |
30 | |
31 | SDA_out(); |
32 | SCL_out(); |
33 | |
34 | SCL_clr(); |
35 | delay(); |
36 | |
37 | for(index=0; index<9; index++) |
38 | { |
39 | if(data & 0x80) |
40 | SDA_set(); |
41 | else |
42 | SDA_clr(); |
43 | |
44 | delay(); |
45 | SCL_set(); |
46 | delay(); |
47 | SCL_clr(); |
48 | delay(); |
49 | data<<=1; |
50 | } |
51 | |
52 | SDA_clr(); |
53 | } |
54 | |
55 | /////////////////////////////////////////////////////////////////////////////////////////// |
56 | // ACK = 0 => letztes Byte, NAK senden |
57 | // ACK = 1 => es geht nooch weiter, ACK senden |
58 | unsigned char i2c_read(unsigned char ACK) |
59 | { |
60 | unsigned char data=0x00; |
61 | char index; |
62 | |
63 | SDA_in(); |
64 | SCL_out(); |
65 | SCL_clr(); |
66 | |
67 | for(index=0; index<8; index++) |
68 | { |
69 | delay(); |
70 | SCL_set(); |
71 | delay(); |
72 | data <<= 1; |
73 | if(SDA_read()) |
74 | data++; |
75 | |
76 | delay(); |
77 | SCL_clr(); |
78 | } |
79 | |
80 | // Folgt noch was oder nicht? |
81 | if(ACK == 0x00) |
82 | SDA_set(); // Ja, fertig |
83 | else |
84 | SDA_clr(); // Nein, es wird noch mehr gelesen |
85 | |
86 | delay(); |
87 | SCL_set(); |
88 | delay(); |
89 | SCL_clr(); |
90 | |
91 | return data; |
92 | } |
93 | |
94 | |
95 | void i2c_start(void) |
96 | { |
97 | SCL_out(); |
98 | SDA_out(); |
99 | |
100 | SDA_set(); |
101 | delay(); |
102 | SCL_set(); |
103 | delay(); |
104 | SDA_clr(); |
105 | delay(); |
106 | SCL_clr(); |
107 | } |
108 | |
109 | void i2c_stop(void) |
110 | { |
111 | SCL_out(); |
112 | SDA_out(); |
113 | |
114 | SDA_clr(); |
115 | delay(); |
116 | SCL_set(); |
117 | delay(); |
118 | SDA_set(); |
119 | delay(); |
120 | } |
Wenn ich das richtig verstanden habe, dann hast Du die Wahl zwischen I2C und Mode 3 SPI. Weshalb du dann allerdings per Bitbanging lieber I2C machst als das viel einfachere SPI, das ist mir ein Rätsel.
A. K. schrieb: > Wenn ich das richtig verstanden habe, dann hast Du die Wahl zwischen I2C > und Mode 3 SPI. Weshalb du dann allerdings per Bitbanging lieber I2C > machst als das viel einfachere SPI, das ist mir ein Rätsel. Das ist einfach Bequemlichkeit, da der Aufbau hier für I2C fertig steht. SPI wäre auch wieder die Wahl zwischen Bitbang und der ominösen und unverständlichen USI. Wenngleich die SPI Routinen winzig sind im Vergleich zu den I2C. Der Tag ist noch lang und draußen wechselt es eh zwischen Sonne, Weltuntergang und Gewitter... Irgendwo fliegt auch noch so ein China Logic Analyzer rum.... gucken ob ich den wiederfinde....
Christian J. schrieb: > Notfalls fliegt der Attiny raus und ein 328er kommt rein, auch wenn der > mehr Platz braucht. Mit dem geht es ja einwandfrei. Falsche Wahl. Ein PIC12F1822 oder ein PIC12F1840 im SO08 oder DIL08 hat SPI und I2C in Hardware, so wie es sich gehört, dazu eine UART, auch in Hardware. Inkl. Noise Filtering bei I2C, was die USI NICHT hat. Nur um Dir mal zu zeigen, was es sonst noch alles gibt. fchk
Christian J. schrieb: > uint8_t ReadADXL(uint8_t addr) > { > uint8_t val; > > // Sensor adressieren ( Zeiger auf Zielbyte) > i2c_start(); > i2c_write(DEVICE); > i2c_write(addr); > > // Bytes anfordern > val = i2c_read(1); > i2c_stop(); > return (val); > > } Gängige I2C Sklaven wollen vor dem "Bytes anfordern" ein (re)start. Und bitte nicht vergessen: Immer das letzte Byte, das gelesen wurde, mit NAK abschließen. Aber das wurde ja weiter oben schon mehrfach erwähnt. Du hast es wohl nur überlesen. Ich empfehle dir, das Datenblatt zu konsultieren. In Rev. D | Page 18 of 40 ist es Figure 41. I2C Device Addressing
:
Bearbeitet durch User
Christian J. schrieb: > #define SCL_set() (PORT |= _BV(SCL)) Falsch. Erstens hab ich gesagt nimm DDR statt PORT und zweitens braucht es nach dem Freigeben von SCL noch ne while-schleife die wartet bis er tatsächlich auf high ist (clock-strectching). Diese Lib die Du da gefunden hats kannst Du getrost in die Tonne treten, wer immer das verbrochen hat hat nicht den geringsten Schimmer. Nicht den geringsten. Daß dieser Code vollkommen untauglich und falsch ist wird übrigens auch von allen Teilnehmern in dem verlinkten Thread ausdrücklich gesagt!
:
Bearbeitet durch User
Aber rücken wir dem Problem mal mit China zu Leibe... an einem 24LC02, vielleicht ist der ADXL345 ja auch schon tot.
Und nun nimm bitte endlich das Datenblatt zur Hand und vergleiche deine Aufzeichnung mit dem Bild 41. Dann sollte dir auffallen, dass nach dem re-Start etwas fehlt. So könnte ein Lesevorgang aussehen: i2c_start(); i2c_write(SLAVE_ADR); i2c_write(adresse); i2c_start(); i2c_write(SLAVE_ADR +1); for (i = 0; i < 15; i++) { i2c_read(ACK); } i2c_read(NAK); i2c_stop(); (die gelesenen Werte werden hier nicht ausgewertet)
:
Bearbeitet durch User
Georg G. schrieb: > Und nun nimm bitte endlich das Datenblatt zur Hand Ich bin dabei, grad genau an dieser Baustelle aber mit einem E2PROm, was ich erst umbauen musste....... also hör auf zu drängeln :-) Bei der zitierten Soft I2C scheint aber das W/R Bit manuell noch gesetzt zu werden müssen. In den Konfort Libs wird das mit eingearbeitet und noch weiter gekapselt. Was fehlt denn in dem Bild? grüne Punkte sind Start, rote Punkte sind Stop.
Georg G. schrieb: > Gängige I2C Sklaven wollen vor dem "Bytes anfordern" ein (re)start. Nicht unbedingt. Manche kennen auch reine Lese-Transaktionen, insbesondere solche ohne Registeradresse nach der I2C-Adresse. Bei vielen I2C Slaves kann man erfahrungsgemäss auch auf die korrekte repeated start condition verzichten und die write transaction mit einer stop condition beenden, um dann eine reine read transaction anzuschliessen. Aber das funktioniert eben nicht bei allen Slaves und da kann man bei bestehendem Code Slave schon man auf die Nase fallen.
Mit dem 24lc32 e2prom geht es jedenfalls, da wird ein Wert ausgelesen i2c_start(); i2c_write(0b10100000); i2c_write(0); i2c_write(addr); // Byte anfordern i2c_start(); i2c_write(0b10100001); val = i2c_read(1); i2c_stop(); return (val);
Fielmann? Im ersten Versuch fehlt das Schreiben der Slave Adresse. Nun hast du es richtig gemacht. Allerdings sagte der von dir gezeigte Quelltext, dass ein i2c_read(1) ein ACK sendet. Aktuell kommt aber ein NAK.
Wahrscheinlich sollte ich mal ne Pause machen..... jedenfalls erzeugt ein Schreiben von 0x75 auf 0x00 nicht das gewünschte Ergebnis. Das R/W Bit habe ich manuell eingefügt. 1010 ist die Adresse des 24lc032, was auf 0x00 geetzt wude an A0-2. Kaffeepause.... //////////////////////////////////////////////// // Schreibe ein Byte auf I2C Bus in eine Adresse // Eingabe: Adresse, Byte void WriteADXL(uint8_t addr, uint8_t val) { i2c_start(); i2c_write(0b10100000); i2c_write(0); i2c_write(addr); i2c_write(val); i2c_stop(); } // Lies ein einzelnes Byte aus dem Sensor aus uint8_t ReadADXL(uint8_t addr) { uint8_t val; // Sensor adressieren ( Zeiger auf Zielbyte) i2c_start(); i2c_write(0b10100000); i2c_write(0); i2c_write(addr); // Byte anfordern i2c_start(); i2c_write(0b10100001); val = i2c_read(1); i2c_stop(); return (val); }
Woher kommt eigentlich der SDA-Glitch ganz vorne? Fasche Reihenfolge beim Pin-Setup?
Christian J. schrieb: > nicht das gewünschte Ergebn Weil du wieder das Datenblatt nicht gelesen hast. Schreiben: i2c_start(); i2c_write(SLAVE_ADR); i2c_write(adresse); for (i = 0; i < 16; i++) { i2c_write(wr_data[i]); } i2c_stop(); Und nach dem i2c_stop musst du warten, bis das Byte wirklich geschrieben wurde, entweder eine feste Zeit oder per "ACK-Polling". Ich geh Kaffee trinken.
Bernd K. schrieb: > Christian J. schrieb: >> #define SCL_set() (PORT |= _BV(SCL)) > > Falsch. > > Erstens hab ich gesagt nimm DDR statt PORT und zweitens braucht es nach > dem Freigeben von SCL noch ne while-schleife die wartet bis er > tatsächlich auf high ist (clock-strectching). Clock stretching braucht man hauptsächlich dann, wenn der Slave ein µC ist. Sonst eher selten. Deinem Kommentar zu der Lib muss ich zustimmen, die ist Unfug. Open Collector/Drain Leitungen steuert man mit der Richtung, nicht dem Wert, wenn der verwendete µC nicht sowieso solche Pins hat (8051) oder entsprechenden Port-Setup (viele ARMs). Und wenn man es schon falsch macht, dann sollte besser aufpassen, dass man keine Glitches durch falsche Reihenfolge der PORT/DDR-Steuerung produziert.
:
Bearbeitet durch User
Bernd K. schrieb: > Diese Lib die Du da gefunden hats kannst Du getrost in die Tonne treten, > wer immer das verbrochen hat hat nicht den geringsten Schimmer. Nicht > den geringsten. Hast du ne bessere? Ich bin jetzt nicht so der Crack was den AVR innen drin angeht.
Christian J. schrieb: > Hast du ne bessere? Ich bin jetzt nicht so der Crack was den AVR innen > drin angeht. Kannst mal damit anfangen, die Makros zu korrigieren. I2C Leitungen steuert man so: Init ab Reset: nix, d.h. PORTx.y = DDRx.y = 0. Out0: DDRx.y auf 1 setzen. Out1: DDRx.y auf 0 setzen. In: vorher DDRx.y auf 0 setzen.
:
Bearbeitet durch User
> #define SCL_set() (PORT |= _BV(SCL))
Das ist echt Blödsinn, weil man den Bus ja eh nur ziehen oder loslassen
kann..... arbeite mich grad da durch..... dauert was.... mit set scheint
er "auf 0 ziehen" zu meinen und mit clear "loslassen".
Christian J. schrieb: > Das ist echt Blödsinn, weil man den Bus ja eh nur ziehen oder loslassen > kann. Genau! Und bei der SCL-Leitung baust Du zusätzlich nach dem Loslassen noch ne while-schleife ein die so lange wartet bis der Pin auch tatsächlich auf high gegangen ist, damit hast Du dann mit nur einer Zeile zusätzlichem Code noch das Clock-Stretching ebenfalls berücksichtigt (denn nichts anderes ist das: der Slave darf jederzeit die SCL nach unten ziehen und der Master muss bei jedem Loslassen der SCL-Leitung so lange warten bis sie tatsächlich auf high gegangen ist.)
Christian J. schrieb: > Das ist echt Blödsinn, weil man den Bus ja eh nur ziehen oder loslassen > kann. Genau! Und bei der SCL-Leitung baust Du zusätzlich nach dem Loslassen noch ne while-schleife ein die so lange wartet bis der Pin auch tatsächlich auf high gegangen ist, damit hast Du dann mit nur einer Zeile zusätzlichem Code noch das Clock-Stretching ebenfalls berücksichtigt (denn nichts anderes ist das: der Slave darf jederzeit die SCL nach unten ziehen und der Master muss bei jedem Loslassen der SCL-Leitung so lange warten bis sie tatsächlich auf high gegangen ist.) Du bist also schon nahe dran, das bekommst Du heute noch hin.
Es klappt...... einfach die Sch.. in die Tonne und woanders mal was runterladen von einer Firmenpage. Nicht viel nachdenken, einfach ändern und benutzen und siehe da, 0x19 reingschrieben mit einem HV Programmer und 0x19 wieder ausgelesen. Die Soft I2c ist sehr schön klein, nur 300 Bytes, das freut. Das ACK überlesen die aber alle, kein stretching. Kann man machen, ja.... So kann man sich auch dden Tag um die Ohren hauen :-) [code] /********************************************************** Software I2C Library for AVR Devices. Copyright 2008-2012 eXtreme Electronics, India www.eXtremeElectronics.co.in **********************************************************/ #include <avr/io.h> #include <util/delay.h> #define SCLPORT PORTA //TAKE PORTD as SCL OUTPUT WRITE #define SCLDDR DDRA //TAKE DDRB as SCL INPUT/OUTPUT configure #define SDAPORT PORTA //TAKE PORTD as SDA OUTPUT WRITE #define SDADDR DDRA //TAKE PORTD as SDA INPUT configure #define SDAPIN PINA //TAKE PORTD TO READ DATA #define SCLPIN PINA //TAKE PORTD TO READ DATA #define SCL PA4 //PORTD.0 PIN AS SCL PIN #define SDA PA6 //PORTD.1 PIN AS SDA PIN #define SOFT_I2C_SDA_LOW SDADDR|=((1<<SDA)) #define SOFT_I2C_SDA_HIGH SDADDR&=(~(1<<SDA)) #define SOFT_I2C_SCL_LOW SCLDDR|=((1<<SCL)) #define SOFT_I2C_SCL_HIGH SCLDDR&=(~(1<<SCL)) #define Q_DEL _delay_loop_2(3) #define H_DEL _delay_loop_2(5) void i2c_init() { SDAPORT&=(1<<SDA); SCLPORT&=(1<<SCL); SOFT_I2C_SDA_HIGH; SOFT_I2C_SCL_HIGH; } void i2c_start() { SOFT_I2C_SCL_HIGH; H_DEL; SOFT_I2C_SDA_LOW; H_DEL; } void i2c_stop() { SOFT_I2C_SDA_LOW; H_DEL; SOFT_I2C_SCL_HIGH; Q_DEL; SOFT_I2C_SDA_HIGH; H_DEL; } uint8_t i2c_write(uint8_t data) { uint8_t i; for(i=0;i<8;i++) { SOFT_I2C_SCL_LOW; Q_DEL; if(data & 0x80) SOFT_I2C_SDA_HIGH; else SOFT_I2C_SDA_LOW; H_DEL; SOFT_I2C_SCL_HIGH; H_DEL; while((SCLPIN & (1<<SCL))==0); data=data<<1; } //The 9th clock (ACK Phase) SOFT_I2C_SCL_LOW; Q_DEL; SOFT_I2C_SDA_HIGH; H_DEL; SOFT_I2C_SCL_HIGH; H_DEL; uint8_t ack=!(SDAPIN & (1<<SDA)); SOFT_I2C_SCL_LOW; H_DEL; return ack; } uint8_t i2c_read(uint8_t ack) { uint8_t data=0x00; uint8_t i; for(i=0;i<8;i++) { SOFT_I2C_SCL_LOW; H_DEL; SOFT_I2C_SCL_HIGH; H_DEL; while((SCLPIN & (1<<SCL))==0); if(SDAPIN &(1<<SDA)) data|=(0x80>>i); } SOFT_I2C_SCL_LOW; Q_DEL; //Soft_I2C_Put_Ack if(ack) { SOFT_I2C_SDA_LOW; } else { SOFT_I2C_SDA_HIGH; } H_DEL; SOFT_I2C_SCL_HIGH; H_DEL; SOFT_I2C_SCL_LOW; H_DEL; return data; } [code]
PS: Die Frage ist vielleicht dämlich aber mir dreht sich eh der Schädel: Wann benutzt man i2c_read(0) und wann i2c_read(1)? Das NAK muss doch den Leesezyklus beenden, gell? @AK: Der Glitch ist auch weg :-) Auf die Terminal Meldung habe ich seit gestern gewartet :-))))) ID:0xE5 ADXL345 ok! Fragt man sich natürlich schon etwas wieso man einen Riesen-USI Trümmer einbauen soll, der fast 1k frisst anstatt einer Soft I2C, die mit einem 1/3 auskommt und genauso gut funktioniert? Schneller läuft die CPU damit auch nicht, fehlen halt nur die ganzen Fehlermeldungen.
Ich bräuchte nochmal eure Hifle, verpacke das jetzt mal in 2 Posts. Hier wäre als erstes das Diagramm der Kommunikation, des ADXL345. Ok, soweit gut.....
Dann hier meine 1-Byte lese Routine (man beachte das ACK) und was dabei herauskommt.... E5 ist richtig und danach kommt ein NAK, also könnte man denken read(0) erzeugt ein NAK. / Lies ein einzelnes Byte aus dem Sensor aus uint8_t ReadADXL(uint8_t addr) { uint8_t val; // Sensor adressieren ( Zeiger auf Zielbyte) i2c_start(); i2c_write(DEVICE_W); i2c_write(addr); // Byte anfordern i2c_start(); i2c_write(DEVICE_R); val = i2c_read(0); i2c_stop(); return (val); }
Da ich aber auch gern einen Burst Read durchführen möchte habe ich diese Routine so geschrieben... die Schleife wurde für Tests mal weggelassen Da steht eine (1) drin und das I2C Bild sieht so aus wie gezeigt, nur das erste Byte ist richtig, danach sind alle 0. Was sie aber nicht sind und nicht sein können. Ein read(1) erzeugt also jetzt ein ACK und ein read(0) ein NAK. Ok NAK = NO ACK, eben keines. Bloss wieso kommen da nur noch Nullen bei raus nach dem ersten Byte? static void Read_Mult_ADXL(int address, int num, uint8_t* bptr) { // Sensor adressieren i2c_init(); i2c_start(); i2c_write(DEVICE_W); i2c_write(address); // Bytes anfordern i2c_start(); i2c_write(DEVICE_R); *bptr++ = i2c_read(1); *bptr++ = i2c_read(1); *bptr++ = i2c_read(1); *bptr++ = i2c_read(1); *bptr++ = i2c_read(1); *bptr = i2c_read(0); i2c_stop(); }
Meine Erfahrung ist, das USI taugt nur als I2C-Slave. I2C-Master oder SPI ist einfacher, kürzer und nervenschonender mit Bit-Banging erledigt. Wenn man die Wahl zwischen I2C und SPI hat, ist SPI eindeutig im Vorteil. Man braucht keine Adresse, ACK, Delays usw., d.h. man ist erheblich schneller und der Code kleiner. Auch das Umschalten des DIO ist in SW ein Klacks. Wenn man von irgendwo her Libs nimmt, hat man 2 Fehlerquellen: 1. die Lib hat Fehler. 2. man benutzt die Lib falsch. Man sollte also vorher wissen, ob die Lib schon von vielen in der gleichen Konfiguration erfolgreich eingesetzt wure.
Danke für die Antwort aber beim Bitbang sind wir ja schon, ich wurde überzeugt, zudem der Code extrem kurz ist. Und die Lib funktioniert ja.... naja, das meiste jedenfalls, sind ja nur 5 Routinen drin, die man kombinieren kann. Zudem der Analyzer ja sofort meckert, wenn das Codemuster falsch ist. Dieses Chinateil ist schon echt nett. Also bleiben die Nullen ungeklärt... die sind ja hardwaremäßßig da wie man sieht. Er hört einfach auf zuu funken nach dem ersten Byte :-(
Ich weiß, ich bin penetrant... DATENBLATT (dieses Mal groß und ganz langsam geschrieben). Mehrere Bytes nacheinander gelesen kommen von aufeinander folgenden Adressen (Auto-Increment). Und was finden wir für die Register 0x01-0x1c? "RESERVED, DO NOT ACCESS!" Noch ein Hinweis: Du bist stolz auf deine Routinen, die nun funktionieren. Dabei hast du aber konsequent ignoriert, wie man die Port Umschaltung machen soll (per DDR). Du fragst hier nach Hilfe. Dann nimm sie auch an. So demotiviert das jeden, zukünftig auf deine Probleme ein zu gehen.
Und was steht da ? Sieht für mich nach DDR aus.. #define SOFT_I2C_SDA_LOW SDADDR|=((1<<SDA)) #define SOFT_I2C_SDA_HIGH SDADDR&=(~(1<<SDA)) #define SOFT_I2C_SCL_LOW SCLDDR|=((1<<SCL)) #define SOFT_I2C_SCL_HIGH SCLDDR&=(~(1<<SCL)) >>Mehrere Bytes nacheinander gelesen kommen von aufeinander folgenden >>Adressen (Auto-Increment). Und was finden wir für die Register >>0x01-0x1c? "RESERVED, DO NOT ACCESS! Gilt übrigens für alle Register, egal welche ich da einstelle. Eines der Rätsel, deren Ursache nur schwer zu finden sein dürften....
@Georg: ich nerv nochmal, da mir das keine Ruhe lässt und aufgeben nicht drin ist. Derzeit an einem Eprom, weil mir das besser bekannt ist. Die 0xOD habe ich in eine Zelle geschrieben. 1101 sind die letzten Bits. Der Source Code steht ja oben. Ich vermute, dass am Ende der i2c_read was nicht stimmt, dass die nur einmal funktioniert und dann nie wieder. Aufgefallen ist mir das an einem Clock Puls der kürzer war als alle anderen. Ich habe zusätzlich in der Leseschleife eine Pause von 20us eingefügt, damit man die Lesezyklen unterscheiden kann. Das Bild oben entsteht, wenn ich die letzten Zeilen auskommentiere, damit SCL wieder auf "Idle" geht nach einem Lesevorgang, sonst .... siehe dazu nächstes Posting. Routine zum Bild: uint8_t i2c_read(uint8_t ack) { uint8_t data=0x00; uint8_t i; for(i=0;i<8;i++) { // SCL clocken SOFT_I2C_SCL_LOW; H_DEL; SOFT_I2C_SCL_HIGH; H_DEL; while((SCLPIN & (1<<SCL)) == 0); if(SDAPIN & (1<<SDA)) data |= (0x80>>i); } SOFT_I2C_SCL_LOW; Q_DEL; //Soft_I2C_Put_Ack if(ack) { SOFT_I2C_SDA_LOW; } else { SOFT_I2C_SDA_HIGH; } H_DEL; SOFT_I2C_SCL_HIGH; H_DEL; //SOFT_I2C_SCL_LOW; //H_DEL; return data; }
Und jetzt die Geschichte, wenn ich SOFT_I2C_SCL_HIGH; H_DEL; SOFT_I2C_SCL_LOW; H_DEL; return data; wieder reinmache. Der Peask da ist 2us lang, kürzer als alle anderen im gesamten Telegramm, die mindestens 3us und 5us lang sein. SCL bleibt gezogen auch in der Pause, der Bus ist blockiert. Seltsam. Leider sind es immer noch 0en, mit beiden Versionen aber da ist doch was faul.... Die Leseroutine für die Aufzeichung
1 | void read_ee(uint8_t adr, uint8_t num, uint8_t* ptr) |
2 | { |
3 | i2c_init(); |
4 | i2c_start(); |
5 | i2c_write(0b10100000); |
6 | i2c_write(0); |
7 | i2c_write(adr); |
8 | |
9 | i2c_start(); |
10 | i2c_write(0b10100001); |
11 | for (int i = 0;i < (num-1);i++) |
12 | { |
13 | *(ptr++) = i2c_read(1); |
14 | delayMicroseconds(20); |
15 | } |
16 | |
17 | *(ptr++) = i2c_read(0); |
18 | i2c_stop(); |
19 | |
20 | } |
Anbei noch die Aufzeichnung des "Selea Logic", einem Billig Analyzer. Vielleicht hat ja jemand so einen und kann sich das dann anschauen.
PS: Nachdem ich mich totgesucht habe hier nun die Auflösung: Die letzte Codezeile musste da noch hin und dann läuft es auch. Mannomann, das war ne schwere Geburt....
1 | uint8_t SoftI2CReadByte(uint8_t ack) |
2 | { |
3 | uint8_t data=0×00; |
4 | uint8_t i; |
5 | |
6 | for(i=0;i<8;i++) |
7 | { |
8 | |
9 | SOFT_I2C_SCL_LOW; |
10 | H_DEL; |
11 | SOFT_I2C_SCL_HIGH; |
12 | H_DEL; |
13 | |
14 | while((SCLPIN & (1<<SCL))==0); |
15 | |
16 | if(SDAPIN &(1<>i); |
17 | |
18 | } |
19 | |
20 | SOFT_I2C_SCL_LOW; |
21 | Q_DEL; //Soft_I2C_Put_Ack |
22 | |
23 | if(ack) |
24 | { |
25 | //H_DEL; |
26 | SOFT_I2C_SDA_LOW; |
27 | } |
28 | else |
29 | { |
30 | SOFT_I2C_SDA_HIGH; |
31 | } |
32 | H_DEL; |
33 | |
34 | SOFT_I2C_SCL_HIGH; |
35 | H_DEL; |
36 | |
37 | SOFT_I2C_SCL_LOW; |
38 | H_DEL; |
39 | |
40 | SOFT_I2C_SDA_HIGH; // was missing!! |
41 | |
42 | return data; |
43 | |
44 | } |
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.