Autor |
Atmega 8 einfaches TWI ( I²C) Programm Suche nach: atmega (404) |
|
|
|
|
BID = 677813
gagamicha Gelegenheitsposter
Beiträge: 58
|
|
Hallo !,
ich benötige mal ein wenig Unterstützung!
Ich muss per TWI zwei Atmega8 mittels myAVR board verbinden.
Habe schon diverse Programme ausprobiert allerdings bekomme ich das TWI nicht zum laufen!
Mein Programm soll folgendes machen:
Wenn auf dem Master ein Eingang kommt, soll auf dem Slave eine LED leuchten und umgekehrt!
Hat da evtl jmd ein Programm in Assembler oder C greifbar?? |
|
BID = 677840
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
|
Zitat :
| Wenn auf dem Master ein Eingang kommt, |
Herrliche Formulierung
Zum Thema:
Für diese Anwendung ist TWI eigentlich nicht gedacht.
Es ist damit zweifelsohne möglich, aber nur mit Tricks:
Beide µCs gehen in den Slavemodus, dem jeweils anderen ist die Slaveadresse des anderen bekannt.
Tritt ein Ereignis ein, sendet er ein einfaches Datengramm an den jeweils anderen µCs. Dazu muss er allerdings in den Mastermodus umschalten, denn Slaves können keine Verbindung initiieren.
Im Falle nahezu gleicher Ereignisse (mit welcher Frequenz kommen die denn?) wird einer der Master "gewinnen".
Alternativ nimmt man einen der beiden wahllos als Master und pollt den anderen, also fragt ihn periodisch ab, ob was passiert ist, oder nicht.
Dazu ist die Frage nach der Frequenz (siehe oben) wichtig, und wie groß die Verzögerung zwischen Ereignis und Reaktion sein darf. Wenn es nahezu Echtzeit sein soll, ist das Käse.
Im Datenblatt stehen wunderbare Codebeispiele, ich kann später auch noch mal gucken, habe glaub ich was in C und ASM da.
_________________
|
|
BID = 677875
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
So, ich habe das was gefunden.
Die wirklich kleine Bibliothek wurde auf einem Mega48 eingesetzt, sollte also auf nem Mega8 ohne Änderung laufen.
Schau in den Header, dort sind alle Deklarationen sowie sehr kurze Erklärungen. Für diese habe ich die Doxygen-Syntax benutzt. Einige Editoren heben das auch hervor.
In twi.c ist die eigentliche Implementation. Ich hänge dir die Bibliothek vorkompiliert sowie als Quelltext an.
Es ist ein Tar-Archiv, kann man auch unter Windows öffnen.
Empfohlener Kompiler-Aufruf:
avr-gcc -mmcu=atmega8 -DF_CPU=<Frequenz> -g -Wall -Os -c -o twi.o twi.c
Du musst dann später bloß gegen den Objektcode, der main(void) enthält, linken, und du kannst die Bibliothek nutzen
Den Header hab ich nochmals extra hochgeladen, um sich einen schnellen Überblick zu verschaffen.
Außerdem rate ich dringend zur Datenblattlektüre, denn dann kannst du auch verstehen, was ich da mache. Kommentare habe ich mir großteils gesperrt. Ist ja nicht viel .
_________________
|
BID = 677977
gagamicha Gelegenheitsposter
Beiträge: 58
|
Okay danke schonmal für deine Bemühungen.
Es geht einfach um ein Beispiel für die Technikerschule.
Im Prinzip muss ich einfach ne taste betätigen und fertig!
Ich komme mehr aus der SPS Welt und kenne mich mit y controllern noch nicht wirklich gut aus.
Dumme Frage zu dem code:
kann ich im AVR Studio damit arbeiten ?
Letzt Endlich möchte ich zb das PORTB,7 mein Eingang und PORTD,7 mein Ausgang ist. Auf beiden Atmegas.
Ich kann ja dann zb das ganze Eingangsbyte von PORTB rüber zu dem anderen ATMEGA auf PORTD transferiern oder?
|
BID = 678096
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
Zitat :
| kann ich im AVR Studio damit arbeiten ? |
Ja.
Dazu fügst du twi.c deinem Projekt hinzu und bindest im Hauptprogramm (dort, wie int main(void) ist) den Header twi.h ein.
Die Bibliothek libtwi.a kannst du löschen.
Zitat :
| Letzt Endlich möchte ich zb das PORTB,7 mein Eingang und PORTD,7 mein Ausgang ist. Auf beiden Atmegas.
Ich kann ja dann zb das ganze Eingangsbyte von PORTB rüber zu dem anderen ATMEGA auf PORTD transferiern oder? |
Ja. Port einlesen, per I²C verschicken und zurück in den Slavemodus wechseln.Im Prinzip ist egal, was du genau verschickst, Hauptsache, man kann aus den Daten einwandfrei erkennen, ob die Taste gedrückt wurde.
_________________
|
BID = 678439
gagamicha Gelegenheitsposter
Beiträge: 58
|
Danke für deine Bemühungen aber irgendwie komme ich nicht weiter.
Habe im AVR Studio ein neues Projekt erstellt!
aber was muss ich genau machen um dem ersten Controller auf Master und den zweiten auf Slave zu schalten?? kappiere das nicht so ganz!
|
BID = 678443
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
Also, twi.c beiden Projekten hinzufügen.
Nach dem normalen Initialisieren gehen beide AVRs in den Slavemodus (i2c_switch_slave). Dazu musst du ihnen eine Adresse > 0 verpassen (siehe Header twi.h).
Außerdem solltest du bei beiden die Interrupts aktivieren und einen Handler für TWI einrichten. Der wird aufgerufen, sobald der AVR als Slave mit seiner Adresse adressiert wurde und ein Byte empfangen hat.
Soll ein AVR dem anderen ein Byte (oder mehr) schicken, dann muss er in den Mastermodus geschaltet werden (i2c_switch_master). Dann wird eine Verbindung initiiert (i2c_start()), der andere AVR mit seiner Adresse plus WRITE-Bit adressiert und bei dessen ACK wird ihm mit i2c_transmit() ein Datum geschickt.
Letztendlich wird mit i2c_stop() die Verbindung abgebaut und der sendende AVR sollte anschließend wieder in den Slavemodus wechseln, da er ja auch adressiert werden könnte.
Ich habe den Quelltext extra kommentiert, mithilfe des Datenblatts sollte man alles 100% verstehen können.
Offtopic :
| AVRs sind leider etwas klein, und man kann keine 100% binärkompatiblen Bibliotheken erstellen. Denn dann könnte man schön mit structs arbeiten, und für alle AVRs eine libtwi.a bereitstellen. In der Initialisierung übergibt man dann in guter C-Manier einfach eine Struktur, in der alle wichtigen TWI-Parameter eingestellt werden. Wobei das hier auch gehen könnte, aber man will ja möglichst kleinen und schnellen und nicht unbedingt generischen Kode haben |
_________________
|
BID = 678445
gagamicha Gelegenheitsposter
Beiträge: 58
|
Also unter Header files liegt jetzt die TWI.h
unter source files hab ich die TWI.C
Jetzt brauche ich ein zweites source file für das eigentliche Programm??? oder wie muss ich das verstehen !?
Und wo gebe ich ihm die slave adresse setze ich die direkt fest im TWI.h oder kann ich die übergeben aus dem C file???
|
BID = 678522
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
Hallo Micha,
Zitat :
|
Jetzt brauche ich ein zweites source file für das eigentliche Programm??? oder wie muss ich das verstehen !?
|
Das verstehst du richtig:
Der Teil, der sich um die I²C-Hardware kümmert ist in twi.c implementiert.
Die Softwareschnittstelle für I²C wird durch den Header bekanntgegeben, das ist in diesem Fall twi.h.
Dort steht:
/** Slavemodus
@brief richte AVR als TWI-Slave ein
@param address eigene Adresse im TWI-Bus
*/
extern void
i2c_switch_slave(uint8_t const address);
Was eindeutig ist:
In deinem Programm kannst du in den Slavemodus umschalten mit
i2c_switch_slave(120);
Der AVR bekommt dann im I²C-Verbund diese Adresse zugewiesen.
In der Implementation dieser Funktion steht unter anderem:
TWCR = 1<<TWEN|1<<TWIE|1<<TWEA;
Das bedeutet, dass der Controller ein Interrupt auslöst, wenn er adressiert wurde. Das musst du im Programm abfangen, sonst wird __bad_interrupt aufgerufen, was soviel wie Softwarereset bedeutet.
Also machst du jetzt folgendes:
- Füge deinem Programm eine neue C-Datei zu, benenne sie mit main.c
- in main.c steht folgender Code:
/* Anfang main.c */
#include "twi.h"
#include <avr/io.h>
#include <avr/interrupt.h>
#include <inttypes.h>
/* eigene Adresse im Bus
* beachte, dass das LSB 0 sein muss! Adresse+1 ist
* für den Lesemodus bestimmt
*/
#define ADRESSE 54
volatile uint i2c_flag;
int main(void)
{
/* TWI-Slave aktivieren */
(void) i2c_switch_slave(ADRESSE<<1);
asm volatile ( "sei \n\t" ); /* Interrupts global aktivieren */
/* Endlosschleife, z.B. Abfragen des Tasters, globaler Flaggen oder so */
for(;;)
{
if( twi_flag )
{
/* I²C: Daten empfangen
* mach was
*/
twi_flag = 0;
}
}
/* Interrupthandler für TWI */
ISR(TWI_vect)
{
/* setze Flagge */
twi_flag = 1;
}
/* Ende main.c */
Wenn du senden willst, dann musst du die I²C-Hardware in den Mastermodus versetzen; siehe twi.h:
/** Mastermodus
@brief richte AVR als TWI-Master ein
@param scl_frequency berechne scl_frequency mit calc_SCL_freq()
*/
extern void
i2c_switch_master(uint8_t const scl_frequency);
#define i2c_init_master(scl_freq) \
i2c_switch_master(calc_SCL_freq(scl_freq))
Im Programm kann das z.B. so aussehen:
(void) i2c_init_master(100000);
oder
(void) i2c_init_master( 100E3 ); /* schöner, weil man keine Nullen zählen muss */
oder
(void) i2c_switch_master(calc_SCL_freq(100000));
Edit:
Wichtig ist, dass twi.c und twi.h in einem Ordner zusammen mit main.c liegen!
Sonst bekommst du viele, unschöne undefined references
_________________
[ Diese Nachricht wurde geändert von: DonComi am 20 Mär 2010 0:35 ]
[ Diese Nachricht wurde geändert von: DonComi am 20 Mär 2010 0:40 ]
|
BID = 678691
gagamicha Gelegenheitsposter
Beiträge: 58
|
Danke für die mühe die ich dir mache aber ich bekomms immer noch nicht hin:
Build failed with 10 errors and 2 warnings...
ich häng mal mein projekt an:
wie sage ich in C wo er das empfangen hin speichern soll bzw wie sage ich ihm was er senden soll!?!? kenne bisher leider nur assembler hab in c kein plan was microkontroller betreffen!
|
BID = 678715
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
Ich habe mir die Datei nicht heruntergeladen.
Schau in den Header rein! Dort steht doch alles.
Senden kanst du ein Byte so:
uint8_t byte ;
...
i2c_transmit(byte);
und empfangen so:
uint8_t byte;
...
byte = i2c_receive(ACK); /* Empfang quittieren */
...
byte = i2c_receive(NACK); /* Empfang nicht quittieren */
Hier ein Kodeschnipsel, um einen LM75 auszulesen:
/* Start */
register uint8_t bytel, byteh;
i2c_switch_master(calc_SCL_freq(100000));
i2c_start();
i2c_transmit(ADRESSE + READ);
byteh = i2c_receive(ACK);
bytel = i2c_receive(NACK);
i2c_stop();
/* Ende */
Wie du das Protokoll aufbaust, musst du selbstverständlich selbst wissen. Meine Software stellt nur eine definierte Schnittstelle zur TWI-Hardware zur Verfügung.
Du solltest also schon wissen, wie I²C-Datagramme aufgebaut werden bzw. wie man sie aufbauen könnte.
_________________
|
BID = 679010
gagamicha Gelegenheitsposter
Beiträge: 58
|
Danke nochmal für deine Ausführungen.
Aber ich komme immer noch nicht weiter bekomme keine kommunikation hin,
ich denke mal das Problem wird sein das ich I²C überhaupt nicht kenne.
Ich dachte es wäre einfacher ein Byte über den Bus zu senden.
|
BID = 679059
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
Hallo Micha,
nicht einfach denken, nachschauen, wie der Bus funktioniert!
Ich habe ja oben reichlich Beispiele gegeben, der Header dokumentiert das Interface auch relativ gut.
Also, nochmal:
1. wie sieht deine Hardware denn jetzt aus?
Schau dir an, wie ein I²C-Bus verdrahtet wird.
2. beide AVRs bekommen eine Slaveadresse, beide müssen logischerweise ungleich sein...
3. wenn bei einem AVR ein Ereignis stattfindet, wechselt er in den Mastermodus,
startet mit i2c_start() eine Bus-Transaktion, sendet die Slaveadresse plus das Schreibbit. Der Slave antwortet mit ACK. Wird ACK empfangen, schickst du mit i2c_transmit() dein eigentliches Datum (eben den Schaltzustand) rüber, der Slave sollte mit NACK (nicht) bestätigen. Daraufhin wird die Transaktion mittels i2c_stop() beendet, der sendende AVR geht wieder in den lauschenden Slavemodus über.
Beide Teile sind hier eigentlich gleich, bis auf die eigene Adresse und die des Gegenstücks.
Probleme wird es so keine geben, solange nicht zusätziche Hardware hinzukommt.
P.S.: bevor es gar nichts wird, weil du offensichtlich keine Eigeninitiative zeigst, verbinde die AVRs über deren UARTS.
Dazu werden Tx und Rx über Kreuz verbunden.
_________________
|
BID = 679666
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
|
BID = 679922
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
Also, meine main.c-Datei war nur so ein Handgelenk-Beispiel, dass du nicht 1:1 kopieren und einfügen solltest. Es ging um das Prinzip.
Aber hier mal eine 100% lauffähige Version, die zumindest korrekt übersetzt wird.
Nur wird der AVR nur damit noch nichts anfangen können, da muss noch etwas mehr rein.
/* Anfang main.c */
#include "twi.h"
#include <avr/io.h>
#include <avr/interrupt.h>
#include <inttypes.h>
/* eigene Adresse im Bus
* beachte, dass das LSB 0 sein muss! Adresse+1 ist
* für den Lesemodus bestimmt
*/
#define ADRESSE 54
volatile uint8_t i2c_flag;
int main(void)
{
/* TWI-Slave aktivieren */
(void) i2c_switch_slave(ADRESSE<<1);
asm volatile ( "sei \n\t" ); /* Interrupts global aktivieren */
/* Endlosschleife, z.B. Abfragen des Tasters, globaler Flaggen oder so */
for(;;)
{
if( i2c_flag )
{
/* I²c: Daten empfangen
* mach was
*/
i2c_flag = 0;
}
}
}
/* Interrupthandler für TWI */
ISR(TWI_vect)
{
/* setze Flagge */
i2c_flag = 1;
}
/* Ende main.c */
Ein einfaches Senden kann so aussehen (nachdem eine Taste gedrückt wurde):
#define EVENT_KEY_PRESSED 0x1
i2c_start();
i2c_transmit(ANDERER_AVR + 1); /* hier die Adresse des anderen */
i2c_transmit(EVENT_KEY_PRESSED);
i2c_stop();
Achso, für sichere Kommunikationen sollten immer die Statuskodes der TWI-Hardware nach jedem TWI-Ereignis ausgewertet werden. Im Normalfall z.B. muss der Programmfluss anders weiterlaufen, wenn ein Slave ein Datenbyte mit NACK (nicht) bestätigt.
Ich habe irgendwo eine Bibliothek, die das macht, und die auch etwas sicherer ist (dafür knapp größer).
Offtopic :
| und in der keine überflüssigen Semikolons vorhanden sind so wie oben. |
_________________
|