TWI-Master für ATMEGA in C (ohne TWI-Start/Stop, ACK/NACK)
Ok, der Betreff soll provozieren.
Natürlich ist ein Betrieb des TWI-Busses ohne TWI-Start/Stop-Condition
nicht möglich. Und auch das ACK/NACK muss ausgewertet werden.
Die Frage ist aber, ob der Anwender diesen Verwaltungskram wirklich
selbst regeln muss.
Der Einsatz des TWI-Busses könnte benutzerfreundlicher sein als es in
manchen Bibliotheken der Fall ist.
Selbst die Arduino-Fraktion quält die geneigte Anwenderschaft mit
- wire.beginn();
- wire.beginTransmission(0x40);
- wire:send('A');
- wire.send('B');
- wire:endTransmission();
Dabei kann man den Beispielcode aus der AVR315 von ATMEL nehmen, einige
kleine Ergänzungen vornehmen - und die Arbeit läuft fast von selbst.
Als Beispiel:
Um ein "0x55" an einen PCF8574 mit der TWI-Adresse "0x40" zu senden:
twi_buffer[0] = 0x40;
twi_buffer[1] = 0x55;
TWI_MA_write(twi_buffer, 2);
Um den Status des PCF8574 anschließend zur Kontrolle wieder auszulesen:
TWI_MA_read(twi_buffer, 2);
In twi_buffer[1] steht dann der Status der PortPins des PCF8574.
Ein anderes Beispiel:
Um 16 Byte ab der 16-Bit Adresse "eeprom_adr" aus einem Eeprom mit der
TWI-Adresse 162 auslesen:
twi_buffer[0] = 162;
twi_buffer[1] = (uint8_t) (eeprom_adr >> 8);
twi_buffer[2] = (uint8_t) (eeprom_adr);
TWI_MA_write(twi_buffer, 3);
TWI_MA_read(twi_buffer, 17);
Die Daten stehen nun in twi_buffer[1] .. [17].
Wie man sieht:
Das umständliche Hantieren mit TWI-Start, TWI-Stop, das Abfragen bzw.
Setzen von ACK's und NACK's, das Setzen von Flags und von Read/Write
Bits ist nicht wirklich notwendig - zumindest nicht durch den Anwender.
Er muss lediglich beachten:
- Einmalige zu Beginn des Programmablaufes muss das TWI-Modul
initialisiert werden.
- Die zu sendenden/empfangenen Daten werden in einem Array übergeben.
- Das erste Byte des Arrays (Index 0) muss die Adresse des Slave
beinhalten!
- Die zu sendenden/empfangenen Daten stehen im Array immer beginnend
an Index 1!
- Bei der Anzahl der zu sendenden/empfangenden Daten muss die Adresse
mitgezählt werden!
Dass die TWI-Adresse an Position 0 innerhalb des Arrays steht, das sieht
im ersten Moment befremdlich aus.
Hat aber den enormen Vorteil, dass die TWI-Adresse den Aufruf eines
Read/Write überlebt und mehrfach nacheinander verwendet werden kann:
Häufig schreibt man eine Adresse auf einen Slave und liest anschließend
Daten aus - die TWI-Adresse in Byte[0] muss dabei nicht neu ins Array
geschrieben werden.
Achtung, die 7-Bit TWI-Adresse wird links ausgerichtet erwartet!
Das Read/Write-Bit (das Bit.0) kann völlig ignoriert werden, das
TWI-Modul wählt die erforderlichen Einstellungen ohne Zutun von aussen!
Ein weiterer Vorteil dieser "Bibliothek":
Das TWI-Modul arbeitet interruptgesteuert.
Sobald die zu sendenden Daten an das Modul übergeben sind, kann das
Hauptprogramm weiterarbeiten, das Versenden der Daten erfolgt im
Hintergrund.
Zu beachten ist, dass die Interrupts freigegeben sein müssen!
Ein verschmerzbarer Nachteil dieser Lösung:
Innerhalb des TWI-Moduls muss zur Pufferung der Daten ein zusätzliches,
lokales Array zur Verfügung stehen, das so groß ist wie die maximal zu
versendende Nachricht + 1 (für die TWI-Adresse).
mfg
Michael S.
Hallo,
schoene Sache das!
Ein kleiner Vorschlag.
Waere es nicht einleuchtender, twi_buffer[0] direkt mit einer Funktion
als TWI_Adress== TWI_MA_buf[0] zuzuweisen und dabei direkt passend zu
schieben.
1
TWI_MA_SetAdress(uint8_tadr){
2
TWI_MA_buf[0]=adr<<1;}
Den TWI-Puffer ab 1 wird ohnehin so umkopiert.
TWI_MA_write(twi_buffer, 2); statt ,3
TWI_MA_read(twi_buffer, 16);statt ,17 ist einfach weniger
fehlertraechtig, wenn man 16 Byte abholen will.
Das genau passiert doch auch in Deinen read und write Prozeduren durch
zwei leichte Aenderungen:
1
voidTWI_MA_write(uint8_t*msg,uint8_tmsgSize){
2
uint8_ti;
3
4
while(TWI_BUSY);// Wait until TWI is ready for next transmission.
5
6
->TWI_msgSize=msgSize+1;// Number of data to transmit.
7
->TWI_MA_buf[0]&=0xFE;// Store slave address with Write-Bit setting.
8
// Die Daten umkopieren nach TWI_MA_buf[]
9
for(i=1;i<msgSize;i++)TWI_MA_buf[i]=msg[i];
10
11
TWI_success=FALSE;
12
TWI_state=TWI_NO_STATE;
13
TWCR=(1<<TWEN)|// TWI Interface enabled.
14
(1<<TWIE)|(1<<TWINT)|// Enable TWI Interupt and clear the flag.
15
(0<<TWEA)|(1<<TWSTA)|(0<<TWSTO)|// Initiate a START condition.
TWI-Slave für ATMEGA in C
Zur Vervollständigung folgt hier noch die Beschreibung zum Pendant des
TWI-Masters, dem TWI-Slave.
Dieses Programm ist auf der Grundlage der AppNote AVR311 entstanden.
Das TWI-Slave-Modul arbeitet vollständig interruptgesteuert im
Hintergrund - unabhängig und unsichtbar für die Hauptanwendung.
Es verfügt über 2 unabhängige Puffer - jeweils für Daten, die der Master
sich abholen kann und für Daten, die der Master an den Slave sendet.
Wenn der Master Daten an den Slave sendet, dann beendet er die
Transaktion mit einer Stop-Condition.
Wird diese vom Slave erkannt, dann schaltet er den Empfang ab (das Flag
TWIE wird gelöscht).
Will main() wissen, ob Daten empfangen wurden, dann muss es eine Abfrage
losschicken.
if (TWI_SLA_Rx_Cnt()) ....
Ist das Flag TWIE gelöscht, dann wird die Anzahl der empfangenen Bytes
zurückgegeben.
Main() muss nun die Daten aus dem Empfangspuffer abholen, danach wird
automatisch wieder die Empfangsbereitschaft des TWI-Moduls hergestellt.
Der schematische Programmablauf sieht so aus:
TWI_SLA_Init();
asm volatile("sei");
while(1)
{
if ((i=TWI_SLA_Rx_Cnt()))
{
TWI_SLA_Get_Data(twi_buffer);
/*
Die empfangenen Daten stehen nun in twi_buffer
es sind genau (i-1) Nutzbytes
twi_buffer[0] ist die verwendete TWI-Slave-Adresse
*/
}
/*
tu sonst noch was
*/
}
Wie beim TWI-Master liefert das erste Byte des Empfangsarrays die
TWI-Adresse.
In diesem Falle die TWI-Adresse, unter der der Slave angesprochen wurde
(Die ATMegas können so konfiguriert werden, dass sie auf mehreren
Adressen lauschen).
Wenn der Master vom Slave Daten abholen will, müssen diese vorher auf
dem Slave bereitgestellt werden.
Dazu werden die Daten in ein Array verpackt und auf das TWI-Modul
kopiert:
TWI_SLA_Put_Data(twi_buffer, anzahl_der_bytes);
Alternativ kann der Pointer auf den Sendepuffer des TWI-Moduls auf eine
beliebige Speicherstelle verbogen werden (sinnigerweise das erste
Element eines Arrays).
TWI_SLA_Set_TxPtr(&test_buffer[0]);
Holt der Master nun Daten ab, dann werden die aus dem Array
test_buffer[] geliefert.
Diese Methode erspart das Umkopieren und reduziert den Speicherbedarf
(weil der Tx_Buffer im TWI_Slave-Modul reduziert werden kann).
Ausserdem kann man auf diesem Wege (zum Debuggen) Speicherinhalte des
Slave auslesen - gelegentlich ist das ganz hilfreich.
mfg
Michael S.
Hallo Horst,
Dein Einwand ist richtig.
Aber über das Problem habe ich natürlich auch nachgedacht.
Am Ende habe ich mich entschieden, diese Eigenart bei der
Hardware-TWI-Schnittstelle beizubehalten
(sie hat ja auch Einzug in den TWI-Slave gehalten).
Ich will aber gerne noch einmal prüfen, ob meine Entscheidung
tatsächlich richtig war.
Bei der Software-TWI habe ich es übrigens so gelöst, wie von dir
vorgeschlagen.
Da Du im Besitz des Source-Codes bist, kannst Du das Programm leicht auf
Deine Denkweise umstricken.
Genauso das habe ich ja mit dem Programm-Code der AppNotes auch gemacht.
mfg
Michael S.
Hallo Horst,
über Deinen Hinweis habe ich noch einmal in Ruhe nachgedacht.
Angenommen, wir folgen Deinem Vorschlag.
Dann wird zwar die Parameterübergabe logisch, aber beim
Beschreiben/Auslesen der Daten gibt's dafür ein neues Problem:
twi_buffer[0] = TWI_ADR;
twi_buffer[1] = data_A;
twi_buffer[2] = data_B;
TWI_MA_write(twi_buffer, 2); // klingt ist das logisch nach
// der Variablenzuweisung davor ?
TWI_MA_read(twi_buffer, 2);
data_A = twi_buffer[1]; // ist das logisch ?
data_B = tei_buffer[2];
Also auch hier sind wieder reichlich Fettnäpfchen verteilt, durch die
man waten kann.
Deswegen habe ich mich entschieden, die Parameter genau zur
Datenstruktur passend zu wählen.
Das ist dann zumindest in der Sache logisch - und nachvollziehbar,
sofern man die Datenstruktur versteht.
Es gäbe allerdings noch einen anderen Weg, den Funktionsaufruf
plausibler zu machen.
Nämlich die TWI-Adr als zusätzlichen Parameter zu übergeben:
TWI_MA_write(twi_adr, twi_buffer, bytes);
TWI_MA_read(twi_adr, twi_buffer, bytes);
Dafür erkauft man sich diese Nachteile:
- die zusätzliche Parameterübergabe kostet einige Nanosekunden und
einige Bytes,
- die TWI_Adresse geht verloren und muss bei jedem Funktionsaufruf
neu gesetzt werden - das wäre unschön.
Da wir mit einem Microcontroller arbeiten, muss manchmal die
Gratwanderung zwischen Ökonomie vor Bequemlichkeit gewagt werden.
Meine persönliche Entscheidung war hier die zugunsten der Ökonomie.
Michael S.
Hallo,
ich kamm nur auf diese Idee, weil Du die Daten ohnehin umkopierst, um
sie per Interrupt überhaupt unmanipuliert senden zu können.
Es wird vom Hauptprogramm nie direkt auf TWI_MA_buf zugegriffen, wenn
man read oder write benutzt.
// Die Daten umkopieren nach TWI_MA_buf
Oh, falscher Fehler, ich habe mit main.c garnicht angesehen...
Du willst also den zweiten Puffer sparen
( OT , kopierst aber trotzdem? Gute Güte mir zerrinnen die Nanosekunden.
OK, ich tippe langsamer. ;-) )
main.c
1
while(1)
2
{
3
twi_buffer[0]=0x40;// Standardadresse PCF8574
4
twi_buffer[1]=i++;
5
TWI_MA_write(twi_buffer,2);
6
_delay_ms(100);
7
}
Ohne Delay ausreichender Größe kann das schiefgehen.
Vielleicht lieber so in der Art:
Horst Hahn schrieb:> while(1)> {> twi_buffer[0] = 0x40; // Standardadresse PCF8574> twi_buffer[1] = i++;> TWI_MA_write(twi_buffer, 2);> while ( TWI_BUSY )> {> _delay_ms(1);> }> }
Die Warteschleife ist überflüssig, denn in TWI_MA_read() und in
TWI_MA_write() steht am Anfang:
while ( TWI_BUSY );
Hier wird gewartet, bis die letzte Transaktion abgeschlossen ist.
Muss ja auch, ansonsten würde der Datenbereich ja überschrieben.
mfg
Michael S.
Hallo,
Der Datenbereich wird doch überschrieben.
Vielleicht wird es besser sichtbar, wenn ich das delay weglasse.
Es ist twi_buffer[1] = 0 zu Beginn.
>twi_buffer[0] = 0x40; // Standardadresse PCF8574>> // Im Sinne des Erfinders nur einmal>> vor der Schleife>while(1)> {> twi_buffer[1] = i++;> TWI_MA_write(twi_buffer, 2);> }
dann wäre beim ersten Durchhlauf twi_buffer[1] = 1, wenn
TWI_MA_write(twi_buffer, 2) aufgerufen wird.
TWI beginnt mit der Ausgabe per Interupt, sozusagen nonblocking.
Die Schleife saust weiter und ändert twi_buffer[1] in 2, bevor noch die
Adresse auf dem I2C Bus ausgeben ist.
Das bedeutet, die 1 ist nicht auf TWI-Bus gesendet worden wäre.
Es ist richtig, das jetzt TWI_MA_write geduldig auf den Abschluss der
zuvor begonnenen Übertragung wartet, aber es hat nicht mitbekommen, das
sich twi_buffer[1] inzwischen geändert hat.
Bei längeren Übertragungen mit einem I2C-Eeprom wäre das fatal.
Deshalb empfand ich den Anschein, twi_buffer praktisch gekapselt, also
ohne äußeren Zugriff zu verwenden, indem man msg erzeugt und dann in
write erst kopiert, als den richtigen Ansatz.
Es ist vielleicht eine Betrachtung, die in der Praxis gar nicht
vorkommt, weil man seine Sende und Empfangsdaten erst ändert, wenn die
Übertragung auch fertig ist, aber dann hätte man sich das eingebaute
kopieren von msg sparen können und dies nur bei Bedarf selbst getan.
Hallo michael!!
Ich habe nach dem TWI_Master Routinen von AVR315 geschaut als ich auf
deine vereinfachte version gestoßen bin. sehr hilfreich was du da
gemacht hast.
Aber ich hätte eine kleine frage dazu.
Ich benuzte ein Atmega8. Ich möchte Die daten (so 300 Bytes) die ich
vorher ins eeprom des Atmega8 gesichert habe über TWI weiterleiten.
kann ich dafür dieselbe main funktion wie du benutzen? so in der art:
Hallo Patrick,
wenn ich Dein Problem richtig verstanden habe, dann möchtest Du 300 Byte
(ob aus dem Eeprom oder woher sonst ist gleichgültig) via TWI an einen
Slave senden.
Wenn Du das in einer einzigen Sendung realisieren willst, dann müssten
auf allen beteiligten Geräten die Puffer entsprechend groß dimensioniert
sein!
Du musst dann die 301 Byte in das Sendearray packen und an das TWI-Modul
übertragen.
Benötigt also schon 602 Byte auf dem Master ( weil das TWI-Modul die
Daten ja zwischenspeichert ).
Machbar sollte das ein, aber dieser Weg ist wenig (Speicher-) effizient.
Ich würde die Daten in kleine Häppchen aufteilen und nacheinander
versenden.
Jedem Paket vorangestellt wird eine Packet-Nr.
Der Empfänger sortiert die Daten dann an das vorgesehene Ziel.
Aber viele Wege führen nach Rom ...
mfg
Michael S.
Hallo Horst,
das Problem, das Du beschreibst, kann (besser: sollte) nicht sein.
Denn die Routine TWI_MA_Read() schreibt zunächst die TWI-Adresse des
Slave auf den Bus, wartet dann in einem while( TWI_BUSY ); bis die
Übertragung abgeschlossen (TWIE gelöscht) und die STOP-Condition auf den
Bus geschrieben ist (!(TWSTO)) und beginnt erst dann mit dem Lesen der
Daten.
Genau dieselbe Abfrage steht auch vor einem TWI_MA_Write().
Möglicherweise beobachtest Du Seiteneffekte?
Vielleicht siehst Du die "1" gar nicht, weil sie bereits von der "2"
überschrieben wurde ?
Sieh Dir mal die Daten mit einem Logicanalyser an, der offenbart den
wahren Verlauf der Kommunikation.
mfg
Michael S.
Hallo Horst,
um zu klären, ob womöglich doch noch irgendwo ein Denkfehler steckt,
habe ich eine Schleife getestet:
for(i =0; i<5;i++)
{
twi_buffer[0] = 0x40;
twi_buffer[1] = i;
TWI_MA_Write( twi_buffer, 2);
}
while(1);
Das Signal/die Kommunikation kann man im Screedump nachvollziehen.
Was meinst Du dazu ?
mfg
Michael S.
USI-Slave für ATTINY in C
Nur die "größeren" Controller verfügen über eine
Hardware-TWI-Schnittstelle.
Aber auch auf den ATtiny's lässt sich eine TWI-Schnittstelle einrichten.
Anstelle der TWI-Hardware wird hier das USI-Modul eingesetzt.
Die nachfolgenden Routinen für den USI-Slave funktionieren aus Sicht des
Anwenders nach der gleichen Methode wie der TWI-Slave.
Das USI-Slave-Modul arbeitet wieder vollständig interruptgesteuert im
Hintergrund - unabhängig und unsichtbar für die Hauptanwendung.
Es verfügt über 2 unabhängige Puffer - jeweils für Daten, die der Master
sich abholen kann und für Daten, die der Master an den Slave sendet.
Wenn der Master Daten an den Slave sendet, dann beendet er die
Transaktion mit einer Stop-Condition.
Diese vom USI-Slave als Zeichen für den Abschluss einer Übertragung
genutzt.
(Im Gegensatz zum TWI-Slave bleibt der USI-Slave z.Z. weiterhin
empfangsbereit.
Folgt eine neue Übertragung, bevor die Daten abgeholt sind, dann wird
der Rx_Puffer überschrieben.)
Will main() wissen, ob Daten eingegangen sind, dann muss es eine Abfrage
losschicken.
if (USI_SLA_Rx_Cnt()) ....
Diese Funktion liefert die Anzahl der empfangenen Bytes (+1) zurück
- oder NULL, wenn keine neuen Daten vorliegen.
Sofern Daten eingegangen sind, muss main() sie aus dem Empfangspuffer
abholen.
Der schematische Programmablauf sieht so aus:
USI_SLA_Init();
asm volatile("sei");
while(1)
{
if ((i=USI_SLA_Rx_Cnt()))
{
USI_SLA_Get_Data(twi_buffer);
/*
Die empfangenen Daten stehen nun in twi_buffer,
es sind genau (i-1) Nutzbytes
twi_buffer[0] ist die verwendete USI-Slave-Adresse
*/
}
/*
tu sonst noch was
*/
}
Wie bei den TWI-Routinen liefert das erste Byte des Empfangsarrays die
TWI-Adresse.
(Die Interrupt-Routine könnte man mit geringem Aufwand so ändern, dass
auch der USI-Slave auf mehrere Adressen reagiert.)
Wenn der Master vom Slave Daten abholen will - oder soll, müssen diese
vorher auf dem Slave bereitgestellt werden.
Dazu werden die Daten in ein Array verpackt (ohne dass eine TWI-Adresse
vorangestellt wird !) und auf das TWI-Modul kopiert :
USI_SLA_Put_Data(twi_buffer, anzahl_der_bytes);
Die Alterative (Pointer auf einen beliebigen Speicherbereich setzen),
wie sie beim TWI-Slave realisiert ist, habe ich hier erst einmal
entfallen lassen.
Die Funktionen sind aus dem Beispielcode zur AppNote AVR 312 entwickelt.
Jetzt fehlt nur noch der USI_Master ....
mfg
Michael S.
USI-Master für ATTINY in C (ohne TWI-Start/Stop, ACK/NACK)
Wenngleich eher selten benötigt, hier zum Abschluss das letzte Teil im
Puzzle: der USI-Master.
Das Handling ist aus Sicht des Anwenders identisch mit dem des
TWI-Masters.
Als Beispiel:
Um ein "0x55" an einen PCF8574 mit der TWI-Adresse "0x40" zu senden:
twi_buffer[0] = 0x40;
twi_buffer[1] = 0x55;
USI_MA_write(twi_buffer, 2);
Um den Status des PCF8574 anschließend zur Kontrolle wieder auszulesen:
USI_MA_read(twi_buffer, 2);
In twi_buffer[1] steht dann der Status der PortPins des PCF8574.
Ein anderes Beispiel:
Um 16 Byte ab der 16-Bit Adresse "eeprom_adr" aus einem Eeprom mit der
TWI-Adresse 162 auslesen:
twi_buffer[0] = 162;
twi_buffer[1] = (uint8_t) (eeprom_adr >> 8);
twi_buffer[2] = (uint8_t) (eeprom_adr);
USI_MA_write(twi_buffer, 3);
USI_MA_read(twi_buffer, 17);
Die Daten stehen nun in twi_buffer[1] .. [17].
Zu beachten ist wieder:
- Einmalige zu Beginn des Programmablaufes muss das USI-Modul
initialisiert werden.
- Die zu sendenden/empfangenen Daten werden in einem Array übergeben.
- Das erste Byte des Arrays (Index 0) muss die Adresse des Slave
beinhalten!
- Die zu sendenden/empfangenen Daten stehen im Array immer beginnend
an Index 1!
- Bei der Anzahl der zu sendenden/empfangenden Daten muss die Adresse
mitgezählt werden!
Dass die TWI-Adresse an Position 0 innerhalb des Arrays steht, das sieht
im ersten Moment befremdlich aus.
Hat aber den enormen Vorteil, dass die TWI-Adresse den Aufruf eines
Read/Write überlebt und mehrfach nacheinander verwendet werden kann:
Häufig schreibt man eine Adresse auf einen Slave und liest anschließend
Daten aus - die TWI-Adresse in Byte[0] muss dabei nicht neu ins Array
geschrieben werden.
Achtung, die 7-Bit TWI-Adresse wird links ausgerichtet erwartet!
Das Read/Write-Bit (das Bit.0) kann völlig ignoriert werden, das
TWI-Modul wählt die erforderlichen Einstellungen ohne Zutun von selbst!
Programmintern gibt es wesentliche Unterschiede zwischen der
Arbeitsweise des TWI-Masters und der des USI-Masters:
Der USI-Master arbeitet nicht interruptgesteuert und die zu sendenden
Daten werden nicht ins USI-Modul kopiert.
Das Programm schiebt eigenhändig Bit für Bit auf die SDA-Leitung,
erzeugt das Clocksignal auf SCL, setzt START- und STOP Conditions und
prüft die ACKs und NACKs der Gegenstelle.
Aber all das wollen wir im Detail eigentlich alles garnicht wissen.
Der Programmcode ist mit nur geringfügigen Ergänzungen aus dem
Beispielcode zur AppNote AVR312 von ATMEL übernommen.
Mit dem USI-Master ist das Quartett endlich komplett:
Es gibt den Master und den Slave jeweils auf Basis von Hardware-TWI für
die ATMEGAs bzw. Software-USI für die ATTINYs - mit einheitlicher
Bedienung und ohne die Notwendigkeit, eigenhändig Start- Stop-
Bedingungen setzen oder ACKs und NACKs prüfen oder setzen zu müssen.
Die Bedienung beschränkt sich (nach der Initialisierung) auf folgende
Funktionen:
Bei einem Master:
- MA_Write(Daten_Array, Anzahl_Bytes) Daten auf den Slave schreiben
- MA_Read(Daten_Array, Anzahl_Bytes) Daten vom Slave lesen
Bei einem Slave:
- SLA_Rx_Cnt() Prüfen, ob Daten empfangen wurden
- SLA_Get_Data(Daten_Array) Wenn ja, dann die Daten abholen
Daten für den Master bereitstellen
- SLA_Put_Data(Daten_Array, Anzahl_Bytes)
TWI kann ja so einfach sein ...
mfg
Michael S.
Hallo Stephan!
> wenn ich Dein Problem richtig verstanden habe, dann möchtest Du 300 Byte> (ob aus dem Eeprom oder woher sonst ist gleichgültig) via TWI an einen> Slave senden.
Ja genau das ist mein problem
> Du musst dann die 301 Byte in das Sendearray packen und an das TWI-Modul> übertragen.
meinst du so in der art?
.// kann natürlich auch in eine schleife zusammengefasst werden
18
.
19
twi_buffer[301]=data[299];
20
21
22
TWI_MA_write(twi_buffer,301);
23
_delay_ms(100);
24
}
25
}
> Benötigt also schon 602 Byte auf dem Master ( weil das TWI-Modul die> Daten ja zwischenspeichert ).
Ich glaube das Atmega8 hätte genug speicher oder ?
Danke
Mfg
Hallo Patrick,
grundsätzlich kann das so funktionieren.
Ein kleines (behebbares) Problem besteht aber dennoch.
Die Indizes sind nur als uint8_t definiert.
Da Du aber 300 Byte übertragen willst, reicht das nicht aus.
Und - wie schon gesagt - die Methode ist auch reichlich ineffizient (aus
Sicht der Speichernutzung).
Folgender Weg benötigt nur 16 Byte für die Puffer:
twi_buffer[0] = twi_adr;
for( i = 0; i < 20; i++)
{
for(j = 1; j < 16; j++)
{
twi_buffer[j] == eeprom_data[i + j];
}
TWI_write(twi_buffer[j], 16);
}
Hier werden 20 * 15 Byte = 300 Byte nacheinander verschickt.
Der empfangende Slave muss die Daten nur wieder richtig ans Ziel
sortieren.
mfg
Michael S.
Hallo,
ich beziehe mich auf meinen nonsens von oben.
Ich hatte im Text meine (falsche) Sichtweise erklaert.
Ich dachte, twi_buffer waere identisch mit TWI_MA_Buf, um Speicherplatz
zu sparen.
Dem ist aber zum Glueck nicht so.
Hallo Michael!
Erstmal danke für dein ansatz.
Aber ich glaube bei diesem Code werden jedesmal dieselben Datenpakets +
ein neues weitergeleitet.
oder irre ich mich?
Mfg
Patrick N. schrieb:
> oder irre ich mich?
Nein, Du hast mich ertappt.
Aber so könnte es möglicherweise funktionieren.
for( i = 0; i < 20; i++)
{
for(j = 1; j < 16; j++)
{
twi_buffer[j] == eeprom_data[(i * 15) + j];
}
TWI_write(twi_buffer[j], 16);
}
mfg
Michael S.
Hallo Horst!!
> er meinte wohl, dass man 20x 15 Byte verschickt.> Dazu muss eeprom_data ein Feld von 0..301 sein.
So war es auch geplannt! ich habe ein Atmega8 mit einer EEPROM von 512
Byte. Und eigentlich ist mein ziel die temperatur von einem sensor zu
lesen, die daten in digital Werten umzuwalndeln (mit einer frequenz von
1 Hz), und sie zuerst ins EEPROM spreichern bevor ich sie über TWI
weiterleite.
Ich hätte gleich die 300Bytes weitergeleitet, aber ich werde eure rat
folgen und die Daten in datenpateke zerteilen.
Danke schön.
Mfg
Hallo!
@ Horst
> TWI_write(twi_buffer[0], 16);
Dadurch wird jedesmal nur die Slave addresse geschickt oder??
@ Michael
> TWI_write(twi_buffer[j], 16);
Bei dir wird die Slave adresse nicht geschickt oder ?
Ich hoffe ich gehe euch nicht zu sehr auf dem sack mit meinen blöden
fragen.
Mfg
Patrick N. schrieb:
> Ich hoffe ich gehe euch nicht zu sehr auf dem sack mit meinen blöden> fragen.
Nein, Du hast ja recht.
Da sind uns die üblichen Flüchtigkeitsfehler unterlaufen, wenn man mal
eben so zwischen Tür und Angel prinzipiell etwas darstellen will ....
>> TWI_write(twi_buffer[0], 16);> Dadurch wird jedesmal nur die Slave addresse geschickt oder??
Nein - der Compiler meldet sich vermutlich.
Der erste Parameter ist ein Pointer auf ein Array.
Deswegen muss er lauten:
- &twi_buffer[0]
oder
- twi_buffer
> @ Michael>> TWI_write(twi_buffer[j], 16);> Bei dir wird die Slave adresse nicht geschickt oder
Der gleiche Fehler wie oben.
Aber in beiden Fällen würden 15 Datenbytes und 1 Adresse verschickt.
mfg
Michael S.
Hallo allerseits,
bei meinem Versuch, TWI-Master und TWI-Slave einem TWI-Multi-Master zu
verheiraten, sind mir einige sinnfreie Stellen im Programmcode des
TWI-Master und des TWI-Slave aufgefallen.
Die sind nun entfallen.
TWI-Slave und USI-Slave konnten bislang keinen General Call verarbeiten,
auch das ist behoben.
Der USI-Slave hat nun auch einen Pointer erhalten, mit dessen Hilfe
unterschiedliche Variablenbereiche aus dem Slave ausgelesen werden
können.
Zu(nicht)guterletzt waren auch einige Bezeichnungen bei Funktionen und
Variablen inkonsistent.
Die Beschreibung für alle Module füge ich als
- TWI_USI_master_slave_120303.pdf bei,
der Programmcode steckt in
- TWI_USI_master_slave_c_120303.zip.
Was ich bislang nicht erwähnt habe:
TWI-Master/Slave sind auf dem ATMega48 getestet, USI-Master/Slave auf
einem ATTiny25.
mfg
Michael S.
Hallo,
im Manual zum ATMEGA 48/88/168 steht folgende Formel:
SCL frequency = CPU Clock frequency / (16 + 2* (TWBR) * Prescaler Value)
Bei einem Prescaler Value von 1 komme ich durch Umformen auf:
TWDR = (CPU Clock frequency / (SCL frequency) * 2)) - 8
Konkret für FCPU von 4MHz und TWI-Clock von 100.000Khz
TWBR = 4.000.000 / (100.000 * 2) - 8 = 20 - 8 = 12
Würde deine Annahme stimmen
#define TWI_TWBR ((F_CPU / (8 * TWI_CLOCK)) - 2)
dann sähe die Rechnung so aus:
TWBR = (4.000.000 / (8 * 100.000)) - 2 = 5 - 2 = 3
In älteren Manuals stand der Hinweis, dass der Wert TWBR nicht kleiner
als 10 sein sollte.
Alles klar ?
mfg
Michael S.
>im Manual zum ATMEGA 48/88/168 steht folgende Formel:
alles Klar daher das Misverstandnis!!
Ich habe die Rechnung mit einem ATMEGA8 gemacht:
Da sieht die Formel ein bisschen aber entscheidend anders aus und zwar
so:
SCL_frequency = CPU Clock frequency / ( 16 + 2(TWBR)* 4^TWPS )
Mit TWPS = 1 kommt man auf meine Lösung.
>In älteren Manuals stand der Hinweis, dass der Wert TWBR nicht kleiner>als 10 sein sollte.
Das gilt wahrscheinlich nicht für ATMEGA8.... hoffe ich zumindest.
Trotzdem danke für deine Antwort