Hallo zusammen,
ich bin auf der Suche nach einen funktionierenden Beispielcode in C, um
mit einem Atmega168 einen Temperatursensor (MCP9804) per Software-I2C
auszulesen. Per Hardware-TWI funktioniert es schon, nur brauche ich den
HW-TWI des Atmega für die Verbindung zu einem anderen Master-uC.
In der Codequelle (Beitrag "I2C-Master [ohne TWI (Softwarelösung) für die ATMEGAs") habe
ich einen Code gefunden, leider funktioniert er bei mir nicht. Laut
diesem Thread (Beitrag "LM75 mit Software TWI auslesen (Atmega 16, C)") ist der Code
anscheindend auch nicht ganz fehlerfrei. Ich habe leider kein Oszi zur
Hand, nur einen AVR-Dragon und eine UART-Verbindung zum debuggen.
Für Assembler gibt es ja so einige Beispiel (Peter Fleury) nur leider
finde ich keine Code für AVR-Controller.
Ich verwende den GCC-Compiler und das AVR-Studio als
Entwicklungsumgebung.
Danke
Kann ich die Peter Fleury Bibliothek
(http://homepage.hispeed.ch/peterfleury/group__pfleury__ic2master.html)
insofern nutzen, dass die main-Funktion in C-Code die Funktionen, die in
Assembler-Sprache geschrieben sind, aufruft ?
Im Anhang hab ich das Zip-File nochmal hinzugefügt.
Ich habe jetzt das Projekt von Peter Fleury übernommen und nun habe nun
folgendes Problem:
...
i2c_start_wait(L_Device_ADDRESS+I2C_WRITE); // set device address
and write mode
i2c_write(L_Sensor_ADDRESS_MSB_BYTE); //
write address = 5
i2c_stop();
i2c_start_wait(L_Device_ADDRESS+I2C_READ); // set device
address and read mode
ret = i2c_readNak();
USART_Transmit(???);
i2c_stop();
...
Ich will das gelesene letzte Byte aus einen Sensor über die
UART-Schnittstelle auf einen Terminalprogramm ausgeben, und nun weiß ich
nicht, was ich anstatt der drei Fragezeichen als Variable einfügen soll.
Gebe ich ret als Variable ein, so bleibt der Wert konstant auf Null.
i2c_readNak hat ja als Ausgabe entweder 0 oder 1, oder ?
Was bedeutdet r24 ? Ist das eine Adresse auf den Wert ?
Beim Hardware TWI gebe ich einfach TWDR als Variable ein, und es wird
der Sensorwert übergeben.
Unten steht nochmal der Kommentar zur i2c_readNak -Funktion.
Kann mir da bitte jemand weiterhelfen ?
;***********************************************************************
**
; read one byte from the I2C device, send ack or nak to device
; (ack=1, send ack, request more data from device
; ack=0, send nak, read is followed by a stop condition)
;
; extern unsigned char i2c_read(unsigned char ack);
; ack = r24, return = r25(=0):r24
; extern unsigned char i2c_readAck(void);
; extern unsigned char i2c_readNak(void);
; return = r25(=0):r24
;***********************************************************************
**
Danke für die schnelle Antwort.
Mein Terminalprogramm kann aber doch alle Formate lesen, warum muss ich
das dann noch wandeln ?
In der "ret"-Variable steht also sicher der ausgelesene Wert ?
Ich habe leider gleich noch eine Frage: Unter i2cmaster.S steht
folgendes:
;-- map the IO register back into the IO address space
#define SDA_DDR (_SFR_IO_ADDR(SDA_PORT) - 1)
#define SCL_DDR (_SFR_IO_ADDR(SCL_PORT) - 1)
#define SDA_OUT _SFR_IO_ADDR(SDA_PORT)
#define SCL_OUT _SFR_IO_ADDR(SCL_PORT)
#define SDA_IN (_SFR_IO_ADDR(SDA_PORT) +1)
#define SCL_IN (_SFR_IO_ADDR(SCL_PORT) +1)
Was bedeudet das ? Unter iom8x.h steht als als Registeradresse des PORTD
(Pin 7 soll SDA und Pin6 soll SCL sein) der Wert _SFR_I08 (0x0B). Hat
das damit zu tun ? Ist das im Datenblatt (ich verwende den Atmega168)
beschrieben ? Unter welchen Stichwort suche ich da am Besten ?
Michael schrieb:> Ist das im Datenblatt (ich verwende den Atmega168)> beschrieben ? Unter welchen Stichwort suche ich da am Besten ?
Kapitel "Register summary"
Die IO-Register des AVR liegen auch alle im Adressspace. Die Makros
berechnen die Adressen für alle benötigten Register.
An den Zeilen brauchst du aber nichts zu ändern, nur an denen davor.
Oliver
Ich hab den Wert in den beiden unteren Zeilen schon jeweils von -2 auf
+1 abgeändert. Ich habe das aus diesem Thread entnommen:
(http://8515.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=100373)
Noch eine Sache verwundert mich ein wenig. In der main-Funktion habe ich
den Ausdruck
if(!(i2c_start(L_Device_ADDRESS +I2C_WRITE)))//Slave bereit
zumschreiben?
{...
}
else
{
i2c_stop();
}
hinzugefügt, um festzustellen, ob die Adresse des devices richtig
übertragen und mit einem ACK bestätigt wurde. Nun ist es leider so, dass
das Programm, selbst wenn ich eine falsche device_Adresse eingebe,
dennoch nicht in den error Zusatnd wechselt. Selbst wenn ich die den
SDA-Leitung vom Controller trenne, geht er nicht in den Fehlermodus.
Woran kann das liegen ?
Michael schrieb:> Ich hab den Wert in den beiden unteren Zeilen schon jeweils von -2 auf> +1 abgeändert. Ich habe das aus diesem Thread entnommen:> (http://8515.avrfreaks.net/index.php?name=PNphpBB2&...)
Na ja, in dem verlinkten Thread geht es um einen ATXmega128A1, du hast
eine Mega168. Vielleicht schaust du tatsächlich mal ins Datenblatt. Denn
ansonsten ist deine Aktion ziemlich sinnfrei.
Oliver
Ich habe die Werte aus den genannten Thread genommen, weil nur mit
dieser "Zahlenkombination" auf meinem UART was ankommt. Leider das
falsche, weil ja wie oben erwähnt diese Abfrageschlaufe ständig als O.K.
abgehadnelt wird, obwohl die device-Adresse falsch ist, oder überhaupt
nix an SDA angelötet ist. Anderseits funktioniert auch bei richtiger
Adressierung des devices bei Verwendung der +2 im Makro der Code nicht,
d.h. das Programm spingt nicht in die Schleife, obwohl er es eigentlich
sollte. Per Hardware-TWI funktioniert alles, am Sensor kann es also auch
nicht liegen.
Im Anahng habe ich mal mein Projekt angefügt.
Nimm den Originalcode. Der funktioniert mit ATMegas, nur für ATXmegas
müssen die Adressenoffsets geändert werden.
Warum auch immer dein Code nicht funktioniert, an den Offsets liegt es
nicht.
Oliver
In Ordnung, dann kann ich schon mal das als Fehlerquelle ausschließen.
Im Moment stellt es sich so dar, dass die start condition vom
Slave-Sensor nicht beantwortet wird. Ich habe jetzt schon den zweiten
Sensor am Controller (der mit Hardware-TWI auch funktioniert hat).
Ich weiß nicht mehr, woran es noch liegen kann. Ich kann schon
irgendeinen unbesetzten PORT-Pin für die Kommunikation nutzen, oder ?
Prinzipiell funktioniert das Programm schon, oder ? Hat das von euch
schon mal jemand ausprobiert ?
Ich kann leider kein Assembler, weswegen ich gehofft habe, eine fertige
Routine zu übernehmen. Die Bibliothek aus diesem link hier
(Beitrag "I2C-Master [ohne TWI (Softwarelösung) für die ATMEGAs") hat bei mir leider auch
nicht funktioniert.
Ich bin auch allgemmein nicht so fit in der C-Programmierung, sodass ist
hier mehr oder weniger auf fertige Funktionen angewisen bin.
Es kann eigentlich nur eine Kleinigkeit sein, aber komm ich nicht drauf.
Michael schrieb:> Ich kann leider kein Assembler, weswegen ich gehofft habe, eine fertige> Routine zu übernehmen.
Das obige ist auch kein Assembler. Ist C - kein feines C, geb ich ja zu
- aber es funktioniert.
im Grunde wird die funktion aus dem Hauptprogramm folgendermaßen
aufgerufen:
1
unsignedintret;
2
3
DS75_Start();
4
DS75_Write(0x9f);// Die Slaveadresse
5
ret=DS75_Read(0);// auslesen 1 für ACK und 0 für NoACK vom Master
6
DS75_Stop();
Klar die Namen der Routinen und die Portzuweisungen müssen / können
natürlich angepasst werden.
Danke für den Beispielcode in C, Rene, aber ich denke ich versuchs doch
noch erstmal mit der Lib vom Peter Fleury.
Ich habe jetzt die compiler optimziation auf 0s (so wie's auch im
makefile angegeben ist) verändert, und noch zwei Pullup-Widerstände mit
4,7k eingelötet, obwohl seitens am Sensor schon Pullup-Widerstände
eingebaut sind. Im Beispielcode habe ich noch eine if-Abfrage eingebaut,
um festzustellen, ob die Start Anweisung am Sensor angekommen ist. Laut
Programm kommt die Start-Anweisung vom Sensor mit einem Ack bestätigt
an. Das Programm hüpft nämlich in die if-Schleife. Das Problem ist nur,
dass es das auch tut, wenn ich eine falsche Adresse für den
Sensor-device eingebe. Als Sensorwert (hier das LSB-Byte von einem
Lichtsensor) wird immer die Zahl 52 (Dez.) übertragen, der sich leider
nicht mit der Lichteinstrahlung auf den Sensor ändert. Bei einem
Temperatursensor und gleichen (an Sensor angepassten Programm) wird auch
diese Zahl 52 übertragen.
Hat hier vielleicht jemand eine Erklärung dafür ?
Danke für den Code,
ich hab ihn gleich mal ausprobiert (an einem Temperatursensor MCP9804)
und leider erscheint als Wert nur ständig 0xFF. Ich habe einen 4Mhz
Quartz am Mastercontroller in Verwendung. Die internen Delay-Zeiten
ändern sich ja dadurch. Kann es daran liegen, warum es nicht
funktioniert ? Am HW-TWI funktioniert es mit dem gleichen Ablauf der
Befehle.
Eine I2C_init Routine gibt's im Code auch nicht. Braucht mann das gar
nicht unbedingt ?
Pin PB0 soll mein SDA, PB0 der SCL Anschluss sein. Im Anhang habe ich
mein Projekt mal angehängt. Egal welche Register ich auch auslesen will,
es wird immer nur die 0XFF übertragen.
An den selben Pins? Hardware-TWI geht, Software nicht?
Da steht folgender Satz in der Doku:
>Adapt the SCL and SDA port and pin definitions and eventually the delay >routine
in the module i2cmaster.S to your target when using the software >I2C
implementation !
Die delay-Routine in i2cmaster.S ist für 4MHz ausgelegt. Wenn dein
Prozessor auch mit 4MHz läuft, sollte es ja passen. Trotzdem kannst du
ja mal ausprobieren, was passiert, wenn du vor dem ret noch ein paar nop
einfügst.
Oliver
Ich habe die beiden "Software" Anschlüsse SDA und SCL auf verschiedene
Portpins gelegt. (PC0/PC1, PD0/PD1) und entsprechend den Assembler-Code
angepasst. Leider mit den gleichen enttäuschenden Ergebnis. Ich hatte
auch erst einen 4Mhz Quarz am Controller und dann einen 8Mhz Quarz mit
entsprechender Codeanpassung laut diesen Thread
(Beitrag "I2C, es tut sich nichts an den Leitungen", ganz unten) ausgetestet.
Was meinst du, Oliver, mit den "nops" vor den "ret" ? An welcher Stelle
des Programmcodes soll ich da noch etwas einfügen ?
Michael schrieb:> Was meinst du, Oliver, mit den "nops" vor den "ret" ? An welcher Stelle> des Programmcodes soll ich da noch etwas einfügen ?
Mit den nops meinte ich das gleiche, was auch in dem von dir verlinkten
Thread steht.
Michael schrieb:> Ich habe die beiden "Software" Anschlüsse SDA und SCL auf verschiedene> Portpins gelegt. (PC0/PC1, PD0/PD1)
Warum 4 Pins?
Was passiert, wenn du Software-TWI auf den Hardware-TWI-Pins laufen
lässt?
Pull-Up-Widerstände (4,7k) hast du selbstverständlich überall dran,
oder?
Wie steht die CKDIV8-Fuse?
Oliver
Ich habe entweder den Slave an PC0/PC1 oder PD0/PD1 angeschlossen. Ich
hab's nur an verschiedenen Portpins ausprobiert.
Die Software TWI habe ich an den Hardware Pins angeschlossen (das
Assembler File entsprechend abgeändert) und leider habe ich das selbe
Ergebnis, der Slave gibt keie Antwort.
Die beiden Sensordatenleitungen SDA7SCL sind schon mit einen 4,7k
Widerstand versehen. Beim Controller noch extra zwei Pullups einzubauen
macht ja dann keinen Sinn mehr, oder ? Wobei ich es auch ausgetestet
habe, und am Ergebnis hat sich auch nichts geändert.
Beim CKDIV8-Fuse ist im AVR-Studio kein Häkchen. Ich hab nun testweise
ein Häkchen dorthin gesetzt und im Terminalprogramm kommt anstatt ein
"A" für Fehler ein ganz anderes Zeichen raus. Es ist aber immernoch der
Fehlerfall der durchlaufen wird.
Tja, dann musst du dich da weiter durchwühlen.
Wenn du kein Speicheroszi hast, auch hier mit nicht weiterkommst:
Beitrag "I2C (TWI) Sniffer mit AVR"
und auch keinen PC mit Soundkarte hast, den du als "Soundkartenoszi"
nutzen könntest, dann häng doch zumindest mal eine LED an SCL, um zu
schauen, ob sich da überhaupt was tut. Nutz den Dragon zum debuggen, und
versuch halt rauszufinden, was da schief geht.
Oliver
Ich habe nun mal LED's mit an die Portpins angelötet, um zu sehen, ob
sich überhaupt was tut. Ich debugge über SPI mit Hilfe des AVR-Dragons.
Leider ziehen die LEDs ein Signal, dass an Port anliegt (PINxx) sofort
nach unten. Die LEDs ziehen aber max. nur 25mA pro Portpin. Es leuchtet
als leider gar nichts. Sobald ich die LEDs wieder weglöte, erkennt man
beim Debuggen, dass das Programm die Portpins schon entsprechend nach
oben oder unten zieht.
Wie kann es denn sein, dass die LEDs den PORT in die Knie zwingen ?
Außerdem ist mir noch aufgefallen, dass das Programm eigentlich schon am
Anfang des Programms bei i2c_init hängen bleibt und nicht mehr die
Funktion verlässt. In Wirklichkeit läuft das Programm ja aber weiter,
weil ansonst ja auch keine Fehlermeldung erscheinen würde.
TWI ist low-active, der Pegel wird nur über die Pull-Ups nach VCC
gezogen, und die haben 4,7k. Da fliesst nicht viel.
Also müssen die LED's vom Pin nach VCC, mit passendem Vorwiderstand. Die
Leuchten dann, wenn der Pin aktiv = low ist.
25mA für eine LED sind aber selbst für normale Anwendungen schon sehr
viel. Ich würde da nicht mehr als ein paar mA fliessen lassen, keine
Ahnung, was so ein LM75 an maximalem Strom verträgt. Es reicht ja, wenn
die LED so gerade erkennbar glimmt.
Michael schrieb:> Außerdem ist mir noch aufgefallen, dass das Programm eigentlich schon am> Anfang des Programms bei i2c_init hängen bleibt und nicht mehr die> Funktion verlässt. In Wirklichkeit läuft das Programm ja aber weiter,> weil ansonst ja auch keine Fehlermeldung erscheinen würde.
???
Was meinst du mit "in Wirklichkeit?" i2c_init bleibt hängen, wenn der
Slave nicht reagiert (oder gar keiner da ist). Da aber im lm75
vermutlich time-outs vorhanden sind, dürfte das im Einzelschrittmodus
nicht funktionieren. Setz mal einen Breakpoint, und lass das Programm
von Anfgang an bis dahin durchlaufen. Wenn es klappt, setze den
breakpoint weiter nach hinten, und starte erneut.
Oliver
O.K., dass debuggen mit den breakpoints probiere ich gleich mal aus.
Ich muss noch sagen, ich will nicht den LM75 ansteuern, sondern einen
Temperatursensor MCP9804 von Microchip bzw. einen Lichtsensor von
Intersil namens ISL29010.
Das ich den Sensor mit 3V und den Mikrocontroller mit 5V betreibe stellt
aber kein Problem dar, oder ?
Michael schrieb:> Das ich den Sensor mit 3V und den Mikrocontroller mit 5V betreibe stellt> aber kein Problem dar, oder ?
Laut Mega-Datenblatt muß der high-Pegel mindestens 0.6*VCC betragen, um
sicher erkannt zu werden, das sind 3V. Wenn du die Pull-ups gegen 3V
schaltest, sollte der Mega das also als high erkennen, was mit
Hardware-TWI ja anscheinend auch funktioniert. Das ist aber einfach
nachzuprüfen.
Schaltest du die pull ups gegen 5V, steht im Datenblatt der Sensoren, ob
die das vertragen. Ich schätze mal, die tun das nicht.
Oliver
Michael schrieb:> Ich kann sie insofern aussschließen, dass die Kommunikation über den> Hardware TWI einwandfrei funktioniert.> Auch mit verschiedenen Sensoren.Michael schrieb:> Es funktioniert jetzt.>> Der Grund war die falsche Adressierung des Sensors. Die Adresse musste> nur um ein Bit linksgeshifted werden.
Ja nee, is klar. Und wieder mal sinnlos Zeit verplempert.
Der Problem mit der Positionierung der 7-bit-Adresse im Byte ist nicht
neu, sogar naheliegend, aber in Peter Fleury's lib in der Hardware- und
in der Softwareversion gleich. Wenn es also mit der Harwareversion
läuft, kann es das nicht gewesen sein. Was genau verstehst du also unter
"laufen"?
Leicht angepisst...
Oliver
Ich habe für den Hardware-TWI die Funktionsbibliothek von Manfred
Langemann verwendet, die jedoch auf der Bib von Peter Fleury beruht.
Hier ist bei der Adressierung kein Linksshift notwendig.
Entschuldige Oliver, dass hätte ich als Fehlerquelle mit etwas
nachdenken und nachschauen schon ausschließen können.