PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Dicker Fehler in der RP6I2CmasterTWI.h der RP6Lib + Bugfix



RolfD
12.02.2011, 11:43
Ich bin dabei die TWI-Funktion für Master/Salve und Multimaster umzuschreiben und dabei ist mir ein dicker Fehler in den RP6Libs aufgefallen. Er führt zu einem instabilen TWI System.

Es betrifft in der Datei RP6I2CmasterTWI.h die Zeile:

#define I2CTWI_isBusy() ((TWCR & (1<<TWIE)))

Dort wird durch Abfrage des TWIE Bit im TWCR geprüft ob die TWI Hardware frei ist bevor ein weiterer TWI-Befehl abgesetzt wird. Das TWIE Bit ist aber laut Datenblatt "nur" das TWI Interrupt Enable Bit. Die Abfrage ist recht unsinnig. Der Entwickler hat - weil er so laufend die Hardware überrannte - an allen möglichen und unmöglichen Stellen des Code Delays eingefügt. Durch die statischen Delays wird das Risiko vermidert, die Hardware zu überrennen aber es macht große Timingprobleme auf dem TWI Bus weshalb dieser instabil, langsam und manchmal arg unwillig ist.

Das ganze ist einfach zu fixen in dem man oben die Zeile durch folgende ersetzt:

#define I2CTWI_isBusy() ((TWCR & (1<<TWINT)))

Hier wird nun korrekt das TWINT Bit geprüft. Das Datenblatt sagt dazu:
"TWINT: TWI Interrupt Flag ; This bit is set by hardware when the TWI has finished its current job and expects application software response."

Weiterhin kann man dann auch alle Aufrufe von I2CTWI_delay in der RP6I2CmasterTWI.c entfernen. Als Folge läuft die TWI Hardware deutlich stabiler und schneller. Wer viel auf dem TWI Bus zu tun hat wird das schätzen wissen. Im Treiber sind noch mehr Bugs aber der erscheint mir wichtig genug um vorab eine Meldung zu machen.

Mehr dann vielleicht wenn meine Lib bzw. die Änderungen fertig sind.

EDIT: Also das mit dem "Delays entfernen" klappt nicht so ganz wie gedacht, bei der Aussage zum TWIE/TWINT bleibe ich aber.
LG RolfD

Fabian E.
12.02.2011, 16:34
Das sieht doch nicht schlecht aus.
Jetzt weiß ich auch wo machmal die unerklärlichen I2C-Fehler herkamen, die sich nur mit einem Dealy beheben ließen...

Wirst du deine Lib hier veröffentlichen?
Hätte ein nicht grade kleines Interesse an einer Multimaster Lib.

RolfD
12.02.2011, 17:13
Wenn ich sie so hinbekomme wie ich mir das vorstelle werde ich sie natürlich hier veröffentlichen. Es schaut gut aus aber sie ist noch nicht fertig. Interupts und Callbacks zu debuggen ist viel frickelei bis alles richtig läuft. Es sei schon mal verraten, das die Lib 99% Befehlscompatibel zum alten System ist, man kann also einfach die .c und .h Dateien des TWI System mit den neuen ersetzen, neu compilieren und man hat alle zusätzlichen Möglichkeiten und Fixes.
Ja an Dich denke ich auch bei der Lib. :) Aber bau du erst mal an Remotrol weiter :)
Ich melde mich wenn ich soweit bin.
LG Rolf

Fabian E.
13.02.2011, 21:26
Vielleicht könnte SlyD mal etwas dazu sagen.
Mir erscheint diese Abfrage in der Tat auch ziemlich sinnlos, aber vielleicht hat er sich ja in irgendeiner Art etwas dabei gedacht und es ist kein Bug.
Nicht, dass hier etwas gefixt wird, was eigentlich nie falsch war.

Ansonsten freue ich mich schon sehr auf die neuen Möglichkeiten und kann mich auch gerne als Beta-Tester anbieten ;)

RolfD
14.02.2011, 07:57
Neues vom TWI Mastermode :)

Also zunächst bitte mein Edit im 1. Post beachten:
"Das mit dem "Delays entfernen" klappt nicht so ganz wie gedacht, bei der Aussage zum TWIE/TWINT bleibe ich aber."

Ohne Delays sind sind Befehlsfolgen wie:
I2CTWI_transmit3Bytes(I2C_RP6_BASE_ADR, 0, CMD_SET_ACS_POWER, ACS_PWR_MED);
I2CTWI_transmit3Bytes(I2C_RP6_BASE_ADR, 0, CMD_SET_WDT, true);
I2CTWI_transmit3Bytes(I2C_RP6_BASE_ADR, 0, CMD_SET_WDT_RQ, true);
problematisch da sie ohne Delay hintereinander weg auf das TWI geschrieben werden können wärend es noch aktiv ist (was nicht passieren darf!). Schüzen tut da ohne Delay nur das TWINT (ehemals das funktionslose TWIE) bit, was jedoch nach jedem übertragenem Zeichen "frei" meldet. Damit kann eine angefangene Befehlsfolge vom nächsten Transmit Aufruf nach Abschluß eines Bytes (aus der angefangene Befehlsfolge) platt geschrieben werden. Das passiert in der ungefixten Version scheinbar ab und zu auch wenn die Delays ansich zu kurz oder aus irgendeinem Grund die Übertragung zu lange für's Delay ist. (Z.B. wenn 2 Master Busarbitierung durchführen müssen)
Man kann sich an 3 Fingern abzählen das es da schon mal knallt wenn sich der Master sozusagen selbst ins Wort fällt.

Man hatte in den Request Funktionen schon mal versucht das Problem zu erschlagen in dem man ein Hilfsflag lastTransOK verwendet, in den Transmissionfunktionen fehlt das allerdings.

Zudem sind die Delays auf der Base und M32 wegen unterschiedlicher CPU-Clocks unterschiedlich lang. Ansich kein Problem aber die M32 wartet eben nur halb so lange bis es ein weiteres Zeichen sendet.

In meiner Version knallt es hier ohne Delays grade laufend... weshalb mir das auch aufgefallen ist. Das als Zwischenbericht meiner fummeleien hier - es erklärt vielleicht einige Probleme mit dem TWI.

@Fabian E.
Ja ich bin auch gespannt was SlyD sagt. Ich bin noch am debuggen aber du bekommst die Lib als erster zum testen.

LG Rolf

Jaecko
14.02.2011, 08:05
Hm, könnte das ein Grund sein, warum man von einem Slave oft nur ständig 0x00 statt brauchbare Werte liest?
Würde ja dann einiges erklären...

Fabian E.
14.02.2011, 08:30
Wie kompliziert wäre es denn das Flag fertig zu stellen?
Hat man Zugriff darauf, wann der Befehl eindeutig übertragen wurde?
Lösungen die auf Warten basieren sind mir nämlich ziemlich unsympathisch...

Habe gerade selbst mal in den Source reingeguckt, das passiert ja tatsächlich ziemlich oft...
Und 150 Cycles sind auch nicht grade wenig, wenn mans etwas öfter aufruft...

Soweit ich das sehe wird in der ISR aber doch immer schön lastTransOK gesetzt?
Und in den Sende-Methoden auch...

RolfD
14.02.2011, 08:42
Da bin ich grade dabei Fabian...

Es ist nicht sonderlich kompliziert denke ich.. und das Kriterium ist schlicht und einfach die Stop condition. In der ISR und in den Request Funktionen ist das vorgesehen aber eben nicht in den Transmisson Funktionen.

Keine Bange... ich krieg das schon hin :) Musst nur noch ein bischen warten *breitrinz
LG Rolf

Fabian E.
14.02.2011, 08:45
xD Da bin fest von überzeugt ;)
Ich habe demnächst übrigens ein neues Design bei Remotrol für dich =P
Ist zwar sicher noch nicht perfekt dein Geschmack, aber es wird dir (vielleicht) besser gefallen. ;)

SlyD
14.02.2011, 13:23
Hallo,

zunächst vorweg allgemein zur Erläuterung:
Es kann durchaus sein, dass einige Dinge alles andere als optimal implementiert sind - war auch nicht das Ziel und dazu war auch keine Zeit.
Es gab damals (2007) eine Deadline und 10 Baustellen (bei Hard- und Software)
gleichzeitig.
Nachdem es funktionsfähig war, kamen dann schon wieder andere Dinge...

Steht in der Baselib auch dabei
"This Code works OK, but it is not optimal! There is a lot potential for tuning!"
Und das gilt für die gesamte Lib!


Ziel war eine bessere Software Ausgangsbasis für den Anwender
als beim ASURO und beim alten CCRP5 Vorgänger.
Nicht mehr und nicht weniger. Dem bisherigen Feedback der Mehrzahl der Anwender nach zu urteilen ist das auch gut gelungen - von kleinen Details mal abgesehen.

Naja, wenn sofort alles perfekt wäre, bliebe für fortgeschrittene Anwender
wie euch :) auch nix interessantes mehr zu tun.



Zum Thema:
Ja das mit den Delays habe ich aus genau dem Grund eingebaut wie RolfD
oben schon rausgefunden hat. Natürlich nur eine "Notlösung" hat aber
für das was ich getestet hatte erstmal funktioniert.
Ich weiss nicht mehr genau, da es schon sehr lange her ist, aber ich meine
ich hätte es auch mit dem TWINT Bit getestet hat aber nicht
zufriedenstellend funktioniert. Hatte dann auch keine Zeit mir
was besseres auszudenken also ists einfach so ein unschönes Delay geworden.

Könnt ihr also gerne versuchen besser zu lösen :)


MfG,
SlyD

Fabian E.
14.02.2011, 13:43
Hallo,

zunächst vorweg allgemein zur Erläuterung:
Es kann durchaus sein, dass einige Dinge alles andere als optimal implementiert sind - war auch nicht das Ziel und dazu war auch keine Zeit.
Es gab damals (2007) eine Deadline und 10 Baustellen (bei Hard- und Software)
gleichzeitig.
Nachdem es funktionsfähig war, kamen dann schon wieder andere Dinge...

Steht in der Baselib auch dabei
"This Code works OK, but it is not optimal! There is a lot potential for tuning!"
Und das gilt für die gesamte Lib!


Ziel war eine bessere Software Ausgangsbasis für den Anwender
als beim ASURO und beim alten CCRP5 Vorgänger.
Nicht mehr und nicht weniger. Dem bisherigen Feedback der Mehrzahl der Anwender nach zu urteilen ist das auch gut gelungen - von kleinen Details mal abgesehen.

Naja, wenn sofort alles perfekt wäre, bliebe für fortgeschrittene Anwender
wie euch :) auch nix interessantes mehr zu tun.



Zum Thema:
Ja das mit den Delays habe ich aus genau dem Grund eingebaut wie RolfD
oben schon rausgefunden hat. Natürlich nur eine "Notlösung" hat aber
für das was ich getestet hatte erstmal funktioniert.
Ich weiss nicht mehr genau, da es schon sehr lange her ist, aber ich meine
ich hätte es auch mit dem TWINT Bit getestet hat aber nicht
zufriedenstellend funktioniert. Hatte dann auch keine Zeit mir
was besseres auszudenken also ists einfach so ein unschönes Delay geworden.

Könnt ihr also gerne versuchen besser zu lösen :)


MfG,
SlyD

Ja, da hast du vollkommen Recht! Beim RP6 ist ganze wesentlich besser gelöst, als noch beim ASURO.
Mir ging es ja auch nur darum, zu erfahren, ob wir da in allen Punkten Recht haben, nicht darum deine Software schlecht zu machen ;)

Wie du gesagt hast, funktioniert das ganze soweit super, die kleineren Probleme die ich hatte konnte ich immer umschiffen.
Auch die Anfänger kommen nach ein paar dutzend Fragen, die auch alle im Handbuch erläutert sind, damit zurecht.

Du hast sicherlich viel Zeit in die Library gesteckt und ich finde das Ergebnis kann sich durchaus sehen lassen! =)

Vielleicht könntest du ja (wenn RolfD das möchte) darüber nachdenken, die Multimaster-Version mit aufzunehmen...

RolfD
14.02.2011, 14:42
@SlyD
Du brauchst dich nicht zu entschuldigen, ich möchte nur die "Macken" finden und entfernen. Das es Macken sind weis ich, und wie sie entstanden sind kann ich mir so ca. denken. :) Unter Zeitdruck proggen ist übel. Wie man sie behebt... da arbeite ich dran und am Schluß sollte eine Lib raus kommen die allen nutzt. Schließlich steht die Software under GPL.

Übrigends habe ich noch keine Software im Bereich AVR gesehen, die eine continuierliche interruptgesteuerte Verarbeitung am TWI zulässt, selbst die AMTEL Beispiele enden meist mit "Disable Interrupt..." was irgendwie wenig produktiv ist. Das was wir mit der RP6Lib haben ist bereits gut ... oder zumindest eine gute Idee. Sie muss nur noch rund laufen.

Ich bastel mal weiter... ich bräuchte dringend einen zweiten USB Adapter damit ich nicht dauernd umstecken muss.. *seufz
LG Rolf

EDIT: Ich habe mal ein wenig rumgeforscht und da ist mir die Arduino-Plattform incl. Source (GPL) begegnet. Sollte es mal ein RP7 geben... ... ... irgendwann... ... in ferner Zukunft... dann wäre es schön wenn man sich beim entwickeln der Libs etwas mehr daran orientieren würde... die haben nämlich z.B. eine 1.A funktionierende TWI Schnittstelle! Nur kriegt man die leider nicht mal eben so an das RP6Lib System portiert. *seufz

RolfD
16.02.2011, 18:08
Jetzt antworte ich mir schon auf meine eigenen Posts... tsts :)

Und dann das..

"Deine Nachricht darf nicht mehr als 20000 Zeichen enthalten."

:)

Aber es gibt ein guten Grund... für die Betatester hab ich was zu tun.
Ich betone BETATESTER... der Code ist noch nicht reif, das man ihn auf die Menschheit und insbesondere arglose RP6 Nutzer loslassen kann :)

Es folgen eine twi_target.h, eine include Datei um ein paar Dinge zu vereinfachen, hauptsächlich Präprozessordirektiven die es erlauben den Quelltext abhängig vom Zielsystem zu bauen. Sie muss also entsprechend geändert werden, da sie aber im Projektverzeichnis liegt und jedes Projekt eine eigene haben darf, spart man sich bissel arbeit. Dort gibts auch ein debug Flag welches die Ausgabe der Zustände am i2c Bis und Datenbytes ausgibt. Bei vielen Daten streikt allerdings das Konsolenterminal des Loaders...

Dann eine RP6I2CTWI.h, in der Hauptsache ein Verbund aus den beiden alten includes + paar Änderungen.

Dann die RP6I2CTWI.c als eingentliche Lib. Die alten Libs werden nicht includiert.

Meine Testumgebung ist ein RP6 mit einem modifizierten Slave Programm + einer i2c pcf8583 sowie einem aufgestecktem M32. Die M32 spielt mit dem Programm Example_08_I2CMaster Master, die PCF8583 spielt Slave für das Baseboard, man kann aber auch ein anderes i2c Device nehmen... lcd, ein SRF oder sonst was... Ich lasse die Base ein wenig in den Registern und RAM der Uhr spielen. Die Base als Slave (für die M32) ist also zugleich auch Master für das PCF8583. Ich katte noch keine Zeit mir ein richtiges Stress und Benchmarks system aufzubauen, dazu fehlt mir vor allem ein zweiter USB Adapter und ohne den ist debuggen wirklich im trüben fischen...

Alles in allem läuft das so auch so wie gedacht. Allerdings treten noch Probleme (wahrscheinlich Timing) auf und daher sage ich ganz deutlich: Das ist alles wirklich noch beta! Allerdings gibts in der Lib auch keine nennenswerten statischen Delays.

Die Änderungen im einzelnen zu beschreiben und zu begründen würde ein Buch füllen, ihr werdet euch also ggf. den Code anschauen müssen. Vielleicht spuckt "diff" was sinnvolles aus... keine Ahnung. Er ist zu 99% komapibel mit der alten Lib, es gibt allerding kleine Änderungen.

__I2CTWI_initMaster gibts nicht mehr und ein paar Variabelen die scheinbar gedacht waren um den ISR zu monitoren sind statisch gesetzt (waus auch zu problemen führen kann, da such ich noch, hat aber keine Priorität grade... Der Mastermode ist nur Master (und wenn man das in twi_target.h auch einträgt spart man einiges an Platz), der Slave Mode kann beides, Slave und Master. Sonst ist das meiste ähnlich zu beachten wie in den ursprünglichen Libs. Meinem Gefühl nach ist die Lib deutlich stabiler als die alte. Das würde ich aber gern auch von euch erfahren.

Bin für Fragen, Vorschläge, Verbesserungen usw. immer offen. Ich hoffe auf Feedback.

EDIT1: Im makefile nicht vergessen:
#SRC += $(RP6_LIB_PATH)/RP6common/RP6I2CslaveTWI.c
#SRC += $(RP6_LIB_PATH)/RP6common/RP6I2CmasterTWI.c
SRC += $(RP6_LIB_PATH)/RP6common/RP6I2CTWI.c

EDIT2:
Es spricht nichts dagegen, z.B. mit eigenen i2c Funktionen auf dem i2c Bus zu arbeiten, allerdings sollte man dann für die Zeit Interrupts unterbinden und die i2c Register (TWSR) überwachen. In jedem Fall muss man nach einem übertragenem Datenpaket eine Stop Condition setzen. Viele Slaves vertragen zwar den repeated Start ... also Start,Daten,Start,Daten,Start,Daten, usw., allerdings wird der Bus so nie frei und andere Master bekommen ggf. keine Gelegenheit zum senden. Wichtig für mehrere Master auf einem Bus ist also Start,Daten,Stop,Start,Daten,Stop usw... Es wäre auch ein Repeated Start durch Start,Daten,Start,Daten,Stop denkbar.. wenn man verhindern will das andere Master einem in die Devices schreiben wärend man noch im Zugriff ist. Aber ein Stop am Ende ist quasi Pflicht. Sinnvoller Weise müsste man ein RepStart nach einer gewissen Zeit unterbinden z.B. auch um nicht anfällig für Signalstörungen auf dem Bus zu sein.

Schreibt man eigene Funktionen und will die ISR nutzen, sollte man vor einem Start im TWCR mit bitset(TWI_statusReg,STR_isr); die ISR "reservieren" und nach einem Stop mit bitclr(TWI_statusReg,STR_isr); freigeben. bitset und bitclr sind gängige Macros:
#define bitset(var, bit) ((var) |= (1 << (bit)))
#define bitclr(var, bit) ((var) &= (unsigned)~(1 << (bit)))
Repstarts kann die Lib als Master derzeit noch nicht... bzw konnte sie glaube ich auch noch nie, bin ich mir jetzt nicht sicher. Steht aber auf meiner Todo. Sinnvoll wäre dies z.B bei Funktionen die in einem Rutsch ein Pointer setzen und dann lesen - wie in RAMs/EEPROMs üblich.
LG Rolf

Fabian E.
18.02.2011, 16:57
Na das sieht ja schon mal gut aus. Ich hoffe heute oder am Wochenende noch zum Testen zu kommen.
Ich habe jetzt noch nicht in die Dateien reingeguckt, aber prinzipiell wäre es bestimmt nicht schlecht, wenn du ein Beispielprojekt posten würdest.
Dann könnten vielleicht noch mehr Leute etwas testen.

Fabi

Fabian E.
18.02.2011, 18:00
Also dann erst mal meine ersten Eindrücke dazu:
Szenario: Base läuft mit unverändertem Slave-Programm,
M32 mit angepasstem Multimaster Remotrol,
M128 nicht angeschlossen (hab ich mich bezüglich Multimaster noch nicht drum gekümmert) Konvertierung verlief problemlos, keine Fehler beim compilieren Normale Funktionstests klappen soweit schon gut, habe dort keine Fehler festgestellt.
(LEDs ansteuern, Motoren steuern, Werte auslesen, etc...) Mein eigener Stresstest für die Verbindung PC->RP6 klappt auch problemlos (der benutzt im Endeffekt auch die RP6SetLeds), die Perfomance ist etwa die selbe I2C Selbstests klappen Jetzt habe ich den I2CLED Test mal etwas modifiziert:
-mSleep(100) nach jedem Durchgang entfernt-->kein Problem,
-mSleep(50) zwischen Setzen der RP6 LEDs und Auslesen selbiger von der M32 --> ca 500 Fehler bei 2400 Versuchen.
Bei diesem Versuchsuafbau wird überhaupt nicht mehr gewartet, die einzigen Pausen ergeben sich durch das Schreiben auf UART.
Ich denke aber fast, dass diese Pause dort rein muss und du da auch nichts gegen machen kannst. Liegt wohl eher am Prinzip des Ganzen.
Ich bilde mir ein, dass bei meinen Tests per Hand wesentlich mehr Steuerbefehle auf der Strecke geblieben sind (Verhältnis 5:1), das ist aber keine qualifizierte Aussage, sondern nur ein Eindruck.
Kann sein, dass das Zufall war.

Läuft also bis her recht gut ;)

Als nächstes werde ich dann mal die Base anpassen und schauen, was die M128 dazu sagt.

Habe ich das eigentlich richtig verstanden, dass der Slave-Modus mehr kann als der Master-Modus? ;)
Das solltest du umbennen, vielleicht in Master und MasterSlave. Ist sonst etwas verwirrend.

Dirk
18.02.2011, 19:58
Ich habe auch schon je ein Testprog für die Base und M32 geschrieben und werde am Wochenende ausgiebig testen.

@RolfD:
Noch eine kleine Anregung/Frage:
Wenn man die RP6I2CTWI Lib in der "normalen" RP6-Umgebung ins Verzeichnis /RP6lib/RP6common tut, müßte man dahin auch die twi_target.h tun.
Das bedeutet beim Kompilieren von Programmen für die Base und M32, dass man immer die twi_target.h neu anpassen muss. Das ist ziemlich lästig und fehlerträchtig, wenn man beim Proggen zwischen M32 und Base wechselt.
Vielleicht kann man das noch anders regeln (z.B. je eine twi_target.h in das Programmverzeichnis der einzelnen Programme o.ä.).
Ist 'ne Kleinigkeit, wäre aber wirklich praktisch ... und die neue Lib wäre dann besser im bisherigen Konzept einsetzbar.

So, dann werde ich mal testen ... :-b

Gruß Dirk

RolfD
18.02.2011, 21:03
Hallo,
erst mal nur eine kurze Antwort.
Danke fürs testen an euch.
Dann @Dirk, die twi_target.h kommt ins PROJEKTverzeichnis und nicht in die Common. Es ist genau so gedacht wie du es auch anregen wolltest.

Also es sind definitiv noch nicht alle Probleme behoben aber ich bin zuversichtlich. Ich werde am WE weiter daran proggen, ich fummel mir grade ein i2c Display an die Base damit ich dort Ausgaben besser überwachen kann wärend ich mit dem USB Dongle an der M32 hänge.

@Fabian, ich bin auf weitere Tests gespannt. Zum Thema Master/Slave...
Ja der Slavemode kann _auch_ Master Funktionen. Der Master nur Master. Wenn Du ein i2c Display hast, kannst du jetzt z.B. das Display in der ersten Zeile vom Master beschreiben lassen, in der 2.ten Zeile vom "Slave". (vorausgesetzt beide setzen den Cursor richtig :) )
Radbruch hatte hier https://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=41805 schon mal gezeigt wie das von der Base aus gehen kann, die M32 kann das bis auf die Ausgabe auf den Portbaustein ja quasi schon.
LG Rolf

Dirk
19.02.2011, 16:39
So, ich habe mal mit diesen Programmen getestet:

RP6 Base (Slave):
/*
* ************************************************** **************************
* RP6 ROBOT SYSTEM - ROBOT BASE TESTS
* ************************************************** **************************
* Example: I2C Slave - I2C Receive Test
* Author(s): D. Ottensmeyer
* ************************************************** **************************
* Description:
*
* This Example receives data via I2C as slave.
*
*
* ################################################## ##########################
* The Robot does NOT move in this example! You can simply put it on a table
* next to your PC and you should connect it to the PC via the USB Interface!
* ################################################## ##########################
* ************************************************** **************************
*/

/************************************************** ***************************/
// Includes:

#include "RP6RobotBaseLib.h" // The RP6 Robot Base Library.
// Always needs to be included!
#include "RP6I2CTWI.h" // Include the new I2C-Bus Library

/************************************************** ***************************/
// Defines:

// The Slave Address on the I2C Bus can be specified here:
#define RP6BASE_I2C_SLAVE_ADR 10

#define CMD_SHOW_DATA 99 // Command to display the data

#define TEXT_OUTPUT // Show text

/************************************************** ***************************/
// Variables:

uint16_t cnt = 0;
uint16_t error_cnt = 0;
uint8_t controlbyte;
uint8_t highbyte = 0;
uint8_t lowbyte = 0;

/************************************************** ***************************/
// Main function - The program starts here:

int main(void)
{

initRobotBase();

setLEDs(0b111111);
mSleep(500);
setLEDs(0b000000);

I2CTWI_initSlave(RP6BASE_I2C_SLAVE_ADR);

while (true)
{
if (I2CTWI_writeRegisters[0] && !I2CTWI_writeBusy) {
controlbyte = I2CTWI_writeRegisters[0]; // Read control byte
I2CTWI_writeRegisters[0] = 0; // and reset it (!!!)
lowbyte = I2CTWI_writeRegisters[1]; // Read lowbyte
highbyte = I2CTWI_writeRegisters[2]; // Read highbyte
if (controlbyte == CMD_SHOW_DATA) {
cnt = ((highbyte << 8) | lowbyte); // Restore 16-bit value
#ifdef TEXT_OUTPUT
writeString_P("Received: "); // and show it
writeInteger(cnt, DEC);
#endif
if (!cnt) error_cnt = 0; // Reset error counter
else error_cnt++; // Error counter + 1
if (cnt != error_cnt) {
writeString_P(" ERROR!\n");
error_cnt = cnt;
}
#ifdef TEXT_OUTPUT
else writeChar('\n');
#endif
controlbyte = 0;
}
}
}
return 0;
}

/************************************************** ****************************
* Additional info
* ************************************************** **************************
* Changelog:
* - v. 1.0 (initial release) 19.02.2011 by D. Ottensmeyer
*
* ************************************************** **************************
*/

/************************************************** ***************************/

RP6 M32 (Master):
/*
* ************************************************** **************************
* RP6 ROBOT SYSTEM - RP6 CONTROL M32 EXAMPLES
* ************************************************** **************************
* Example: I2C Master - I2C Send Test
* Author(s): D. Ottensmeyer
* ************************************************** **************************
* Description:
*
* This Example sends data via I2C as master.
*
*
* ################################################## ##########################
* The Robot does NOT move in this example! You can simply put it on a table
* next to your PC and you should connect it to the PC via the USB Interface!
* ################################################## ##########################
* ************************************************** **************************
*/

/************************************************** ***************************/
// Includes:

#include "RP6ControlLib.h" // The RP6 Control Library.
// Always needs to be included!
#include "RP6I2CTWI.h" // Include the new I2C-Bus Library

/************************************************** ***************************/
// Defines:

// The Slave Address on the I2C Bus can be specified here:
#define RP6BASE_I2C_SLAVE_ADR 10

#define CMD_SHOW_DATA 99 // Command to display the data

#define TEXT_OUTPUT // Show text

/************************************************** ***************************/
// I2C Event handlers:

/**
* This function gets called automatically if there was an I2C Error like
* the slave sent a "not acknowledge" (NACK, error codes e.g. 0x20 or 0x30).
* The most common mistakes are:
* - using the wrong address for the slave
* - slave not active or not connected to the I2C-Bus
* - too fast requests for a slower slave
* Be sure to check this if you get I2C errors!
*/
void I2C_transmissionError(uint8_t errorState)
{
writeString_P("\nI2C ERROR --> TWI STATE IS: 0x");
writeInteger(errorState, HEX);
writeChar('\n');
}

/************************************************** ***************************/
// Variables:

uint16_t cnt = 0;
uint8_t buffer[4];

/************************************************** ***************************/
// Main - The program starts here:

int main(void)
{
initRP6Control();

initLCD();

I2CTWI_initMaster(100); // Initialize the TWI Module for Master operation
// with 100kHz SCL Frequency

// Register the event handlers:
I2CTWI_setTransmissionErrorHandler(I2C_transmissio nError);

setLEDs(0b1111);
mSleep(500);
setLEDs(0b0000);

while(true)
{
if (!I2CTWI_isBusy()) {
cnt++; // TEST: COUNTER!!!
if (cnt > 32767) cnt = 0;
buffer[0] = 0;
buffer[1] = CMD_SHOW_DATA;
buffer[2] = cnt;
buffer[3] = (cnt >> 8);
I2CTWI_transmitBytes(RP6BASE_I2C_SLAVE_ADR, buffer, 4);
#ifdef TEXT_OUTPUT
setCursorPosLCD(0, 0);
writeStringLCD_P("I2C has sent:"); // What was sent?
setCursorPosLCD(1, 0);
writeIntegerLCD(cnt, DEC);
#endif
}
}
return 0;
}

/************************************************** ****************************
* Additional info
* ************************************************** **************************
* Changelog:
* - v. 1.0 (initial release) 19.02.2011 by D. Ottensmeyer
*
* ************************************************** **************************
*/

/************************************************** ***************************/

Ergebnis:
Der Zähler zählt immer 3 Stellen hoch, danach wird eine Stelle ausgelassen. Da pro Zähler 4 Bytes übertragen werden, flutschen wohl immer ca. 12 Bytes, bevor eins verloren geht.

Gruß Dirk

... ich bleibe dran ...

RolfD
19.02.2011, 21:12
hm.... hm....
also erst mal danke für das "testsystem", das ist ein schöner Test und genau das was ich als Ansporn brauche um hier weiter zu basteln. :) Und ggf. auch ein Hinweis woran es liegen kann. Ich hab beide Sources compiliert und kann dein Ergebnis bestätigen, allerdings weis ich noch nicht wieso das so ist - es ist jedenfalls nicht gut das es so ist!

Mir drängt sich der Verdacht auf, das es mit anderen Interrupts im System zu tun hat, z.B. dem Timerinterrupt. Oder die Messung aller Sensoren... Das würde zumindest die relative Regelmässigkeit der Errors erklären. Die TWI-isr versucht ja alles anzunehmen was kommt aber wenn jemand extern mit cli/sei den Interrupt abdreht, kriegt die ISR das ja nicht mit, folglich schlagen Bytes auf, die in der isr nicht verarbeitet werden und schon meldet dein Programm ein Fehler oder die TWI rising Buserros oder so'n quark.
Würde man das jetzt perfektionieren wollen, würde man wie im Ethernet bzw. ISO-OSI Modell eine logische Netzwerkschicht mit Fehlererkennung einbauen.. sprich alle Daten mit CRC-Prüfsumme versehen. Dann kann der Client (Base) ein retransmit anfordern. Nur das ist beim TWI mit Kanonen auf Spatzen geschossen und für die Datenübertragung zu I2C Slaves auch garnicht machbar (und bei einer Fehlerquote von 33% eh witzlos). Das Problem betrifft vor allem den Slavemodus, mit dem Sender (Master) hat das nur so viel zu tun da er viel sendet, aber weit weniger als der TWI Bus zulässt. Es ist auch nicht auf die Geschwindigkeit allgemein zu schieben, die bewegt sich im Bereich wo auch jedes normale UART läuft. Als Master führt man den SCL-Takt, wird ein Timerinterrupt ausgeführt geht nichts verloren, der Slave merkt nur das der Master "ruckelt" beim arbeiten. Als Slave muss er aber immer und zu jeder Zeit alle Datenbytes annehmen können oder ein NACK senden worauf hin der Master normal die Übertragung abbricht. Im Fall von verlorenen Bytes kann der Slave aber nicht mal NACKen... Ist also auch keine Lösung.

Die resultierende Frage ist also.... : Wie bringt man dem Rest der Lib bei, die Füße still zu halten bzw. sich mit der TWI zu arrangieren so lange eine Übertragung am TWI läuft?

Zweite Frage: betrifft das nur den Slave oder ist auch der Master Receiver Mode betroffen (lesen von großen Datenmengen aus einem EPROM als Master z.B.) Ich vermute stark, es ist nur der Slave betroffen.
@Dirk, kannst Du dein Programm mal umschreiben so das der Master (M32) möglichst große Datenmengen beim Slave (Base) liest und verifiziert. Das wäre eine wichtige Frage das zu klären. Danke schon mal.

*kopfkratz
SlyD hast Du eine Idee bzw. kannst Du den Gedankengang bestätigen?
Ich grübel mal weiter. SO ist das TWI jedenfalls kein wirklicher Gewinn für den RP6 mit Zusatzboards. Aber ich bin sicher, das geht noch besser....

Ich schau mir das jetzt erst mal genauer an... ich habe übrigends mal
//#define TEXT_OUTPUT // Show text
im Slave ausgeschaltet und prompt habe ich auf ca. 3500 übertragene Werte nur noch ca. 20 Fehler... also unter 1% ... und nicht mehr jedes dritte Datum im Eimer. Ein Hinweis das auch die (Echtzeit)UART-Ausgabe hier nicht ganz unbeteiligt an der Fehlerdichte ist.
Ich hab nur leider keine wirkliche Idee, wie man das in den Griff bekommt... Slyds Worte oben bekommen grade einen ganz neuen Sinn für mich...

"This Code works OK, but it is not optimal! There is a lot potential for tuning!"

Man muss aber fairer Weise dazu sagen, das Amtel für Multiprozessorkommunikation den UART vorgesehen hat... und nicht das TWI, der UART hat sogar ein besonderen Modus dafür. Man könnte sich auch anders behelfen, z.B. indem man ein pcf8583.h mit immerhin 240 Byte RAM im System vorsieht auf den beide CPU's als Master zugreifen und dort Daten austauschen. Nur dafür ist ab Werk weder der RP6 noch die Software ausgelegt. Wenn man zuverlässige Ergebnisse erwartet, sollte man z.Z auf den Slavemode wohl eher verzichten - oder/und fehlertolerant proggen und tatsächlich sowas wie ne CRC vorsehen. Kommt halt auf den Zeck an.

*grins

Aber für uns Robotbauer wäre es immerhin schon mal interssant wenn der Multimastermode richtig funktioniert und die Daten zuverlässig ankommen wie sie abgeschickt wurden... das war bisher nicht immer der Fall und da lohnt es sich die Lib noch zu verbessern. Den Slavemode... tja... mal sehen aber ich denke das schaut nicht gut aus... vielleicht ists aber auch nur ein kleiner fieser Bug... mal sehen.

EDIT:
Für die Betatester eine kleine Änderung am Include File, bitte

#define I2CTWI_isBusy() ((TWCR & (1<<TWINT)))
durch
#define I2CTWI_isBusy() (bit_is_set(TWI_statusReg,STR_isr))

ersetzen. Hintergrund ist, das TWINT Bit spielt für die Übertragung eines kompetten "Satzbaus" im TWI (Start Daten Stop) keine Rolle, eine Aussage darüber ob die TWI frei ist gibt hingegen das STR_isr in TWI_statusReg (abhängig von der Stop condition) , folglich muss das geändert werden. Die Lowlevelfunktionen warten eh auf das STR_isr aber um festzustellen ob ein Task Rechenzeit braucht - wie in dem Master testprogramm - sollte es natürlich die richtigen Bedingungen abfragen.

Gruß RolfD

SlyD
20.02.2011, 11:08
Hallo,

tja ist schon lange her das ich was dran gemacht habe aber mal kurz überlegen... ohne den neuen I2C Lib Code angeschaut zu haben - also keine Garantie auf garnix ;)


Der Master sollte eigentlich kein Problem sein.

Es ist natürlich logisch, dass wenn der Slave lange an irgendwelchen anderen Sachen rumrechnet in dieser Zeit keine Daten aus den I2C "Registern" (dem Array) abgeholt werden können. Auch UART Ausgaben kosten Rechenzeit, da die ja relativ langsam mit 38400 Baud laufen... mach mal ein mSleep(100) in den Slave Code das dürfte dann noch etwas drastischer werden.
Das Abholen und Interpretieren der Daten wird ja nicht in der ISR erledigt, das muss noch immer im Hauptprogramm erfolgen.

Die Daten kommen vermutlich sogar korrekt in den Registern (in demI2CTWI_writeRegisters[] Array) an, aber das Programm holt die nicht schnell genug ab und werden dann schon wieder vom nächsten Datenpaket überschrieben.
Vermute ich jedenfalls mal.

Kannst ja einfach mal nen Zähler in die I2C ISR reinpacken und hochzählen wieviele Datenpakete tatsächlich IN DER ISR empfangen und in das Array geschrieben wurden.
Den dann im Hauptprogramm neben einem zweiten Zähler für die Zahl der dort ausgewerteten Daten ausgeben lassen.


In Dirks Programm oben wird übrigens noch gar nichts von den ganzen aufwändigen Sachen in der Lib ausser der Timer ISR ausgeführt - in den ganzen task_xxxxxx Funktionen wird die eigentliche Arbeit erledigt in der Timer ISR wird nur ein bisschen was mit RC5 Empfang gemacht und einige Flags für Software Timer gesetzt.
Zum Testen kannst Du die natürlich deaktivieren (s. initRP6...() funktionen).


> ISO-OSI Modell ... mit Fehlererkennung

;-)
Ich denke es geht auch ein wenig einfacher, man muss ja nur die Rechenzeit / Pausen überbrücken in der das aktuelle "Datenpaket" (bei I2C werden eigentlich immer nur einzelne Register gesetzt oder gelesen) nicht vom Anwendungsprogramm aus den Registern abgeholt werden kann weils mit anderen Dingen beschäftigt ist.
Echte Übertragungsfehler sollten beim I2C Bus eher nicht passieren.

Als kleiner Denkanstoß - wenn denn tatsächlich viele Daten schnell hintereinander an den Slave geschickt werden müssen: man könnte es ggf. ähnlich wie beim UART Empfang über einen kleinen Ring Buffer lösen in den im Slave Modus die empfangenen Bytes geschrieben werden.
Muss hier natürlich etwas anders aufgebaut werden da es ja zusammenhängende Datenpakete und nicht nur einzelne Zeichen sind...


MfG,
SlyD

PS:
Viele I2C Slaves brauchen eine ganze Weile um Befehle zu verarbeiten.
Das ist eigentlich normal das man da nicht beliebig schnell hintereinander Befehle absetzen kann.
Man kann aber mit einem Befehl viele Register (z.B. 32 Stück) in einer einzigen Übertragung setzen - probiert das doch bitte auch mal aus.
Vorher natürlich I2CTWI_SLAVE_WRITE_REGISTERS entsprechend erhöhen.
Hab ich damals sicherlich auch getestet, weiss ich aber nicht mehr.

RolfD
20.02.2011, 11:17
@Slyd die Idee mit dem "zwar received aber noch nicht verarbeitet" werde ich genauer prüfen, zur Zeit hab ich den Eindruck das die Zeichen wirklich verloren gehen - das lässt sich aber genau rausfinden. Danke für den Tip.

@Betatesters, ein kleines update in RP6I2CTWI.h. ich hatte es schon oben editiert aber hier noch mal:
Bitte
#define I2CTWI_isBusy() ((TWCR & (1<<TWINT)))
durch
#define I2CTWI_isBusy() (bit_is_set(TWI_statusReg,STR_isr))
ersetzen.
Hintergrund ist, das TWINT Bit spielt für die Übertragung eines kompetten "Satzbaus" im TWI (Start Daten Stop) keine Rolle, eine Aussage darüber ob die TWI frei ist gibt hingegen das STR_isr in TWI_statusReg (abhängig von der Stop condition) , folglich muss das geändert werden. Die (blockierenden) Lowlevelfunktionen warten eh auf das STR_isr aber um festzustellen ob ein Task Rechenzeit braucht - wie in dem Master testprogramm - sollte es natürlich die richtigen Bedingungen abfragen.
Das TWINT spielt in der ganzen Übertragung übrigends garkeine Rolle mehr da wir nicht mehr das TWI als Registerset pollen sondern wirklich nur auf Interrupts reagieren und in den Lowlevelfunktionen die TWI Übertragung als "Satz" sehen. Damit werden u.a. unzulässig fehlende Stops unterbunden. Ein deutliches Zeichen das es vorran geht. Im Slavemode kann ein eintreffendes Byte die ISR übrigends auch blockieren so das verhindert wird, das ein Mastertask aktiv wird wärend Daten reinkommen... da arbeite ich aber grade dran. Das wird auch das counten/vergleichen in Dirks Slave Programm erleichtern bzw. die dort aufgeworfenen Fragen klären helfen.
LG RolfD

Fabian E.
20.02.2011, 13:42
So, ich habe dann jetzt mal auch die Base auf die neue Library umgestellt und das hat sehr viele Fehler produziert.

Es waren einige Änderungen am Code nötig (Umbennungen) Mein Verbidnungsselbsttest zeigt das gleiche, wie das manuelle Setzen der RP6 LEDs:
etwa 80% der Befehle kommen nicht richtig an. Das ist etwas seltsam...
Die Befehle kommen irgendwie dann beim nächsten Befehl erst an...
Beispiel: Ich setzte beim RP6 LED1 --> nichts passiert. Ich setzte danach LED2 --> LED1 ist dann an und LED2 aus.
So geht das immer weiter...
Mit dem normalen Baseprogramm ohne deine Lib geht alles normal. Mein I2C Stresstest gibt mit der abgeänderten Base von 2400 Versuchen 2400 Fehler aus.
Auch in diesem Fall werden die LED zwar gesetzt, aber scheinbar zu spät. Das mit dem Ringbuffer auf dem Slave hätte ich jetzt spontan auch vorgeschlagen...
Wie groß der sein muss wäre dann zwar etwas Trial&Error, aber eigentlich müsste es damit besser klappen.

RolfD
20.02.2011, 14:46
Hallo Fabian,
die Umbenennungen dürften nur I2CTWI_readRegisters und I2CTWI_readableRegisters betreffen, und dazu kann ich wenig. Wenn Du andere Umbenennungen hattest, sag mir bitte welche. Siehe auch:

* fix: I2CTWI_readRegisters -> I2CTWI_readableRegisters / double use of name

Ich hatte nur die Möglichkeit, das Array von I2CTWI_readRegisters im Slavecode in I2CTWI_readableRegisters umzutaufen oder ich hätte der Mastercode Funktion I2CTWI_readRegisters einen anderen Namen geben müssen... ich hab mich für erstes entschieden... müsst ihr nun mit leben :)
Natürlich muss man im eigenen Slave code auch die Registers umbenennen aber das macht der PN mit 3 Tasten ... quasi... allerdings sollte das besser dokumentiert sein, geb ich zu. Ich hatte es aber weiter oben auch schon mal gesagt...

Das mit dem Ringbuffer... nun die Register im Slavemode sind quasi ein Ringbuffer nur das der Pointer eben nicht im Kreis läuft sondern bei Verbindungsbegin üblicher Weise gesetzt wird (wie der Adresspointer im EEPROM). Wird über die Register hinaus gelesen, schickt der Slave bisher ein Nack zum Master. Allerdings hab ich das noch nicht 100% debugt ob das aufs Byte genau stimmt. Ich kann natürlich auch bei Ende der Register den Zähler einfach zurück auf Anfang stellen was einem Ringbuffer gleich komt. Die Frage ist nur: Nutzt das was? Das überlesen/überschreiben hinter die Registergrenzen ist in jedem Fall ein klarer Verstoß gegen Konventionen und vor allem ein Fehler in der Nutzung des Treibers.. nicht des Treibers selbst, daher bisher NACK. Der alte Treiber hat gnadenlos Zugriffe hinter die Registergrenzen zugelassen... was der z.B. mit dem Stackheap dann anstellt... das möcht ich garnicht mehr so genau wissen :) Der Master sollte allerdings auch mitkriegen wenn die Registergrenze erreicht ist und entsprechend reagieren (Fehlermeldung). Siehe auch:

* fix: Register/Buffer overruns

Das Problem mit dem Stresstest kann ich nicht beurteilen so lange ich den Quellcode dazu nicht habe. Würd mich aber wie Dirks Programm interssieren.

Zum Problem mit den verzögerten Übertragungen, ich werde die Lib die Tage umfangreich testen, ich halte es auch hier für möglich das der Fehler nicht im TWI Treiber zu suchen ist. Aber auch hier muss ich sagen: Abwarten... ich werde mich um alles kümmern was hier an Problemen aufschlägt.

Bevor Du an der Remotrol Software Anpassungen machst, sollten wir also zunächst dein Stesstest ans rennen kriegen, das ist ggf. auch übersichtlicher. Wenn Dein Stresstest jedes mal systematisch gegen die Registergrenze klatscht, wäre es z.B. kein Wunder wenn der 2400 Fehler ausgibt. Vielleicht gebe ich das NACK aber auch ein Byte zu früh. Zeig mir am besten mal die Quellen für den Stresstest.
LG Rolf

Fabian E.
20.02.2011, 15:48
Genau die Umbenennungen meinte ich, war auch nicht als Kritik oder so gedacht, sondern nur für andere Leute die das hier lesen sollten.

Ringbuffer...:So wie ich das jetzt verstanden habe, geht es doch darum, dass die Daten ankommen aber eben nicht verarbeitet werden, weil schon wieder ÜBERSCHRIEBEN...
Wenn man aber einfach alle ankommenden Daten irgendwie in einen Ringbuffer speichert geht zumindest nichts verloren.
Wie man diese Daten dann wieder sinnvoll auswertet, ist etwas anderes.

Du sagst was vom umbennenen der Register? Wie genau meinst du das?

Quellcode zum Stresstest kommt nachher noch.

Was meinst du mit, dass "der der Fehler nicht im TWI Treiber zu suchen ist"? Wo denn sonst? Mit der alten Lib klappt das perfekt ;)

Und Remotrol ändere ich natürlich nicht, ich habe mir einen Testbuild dafür gemacht.

Fabian E.
20.02.2011, 16:05
So hier Code des Tests, der sehr viele Fehler bringt:

void TestI2CLEDs(void)
{
TestStart("I2CLED Test"); //Unwichtig, dient nur der Kommunikation mit Remotrol
uint8_t testResult = 1;

uint8_t runningLight = 1;
uint16_t cnt;
uint16_t errorcount = 0;
for(cnt = 0; cnt < 2400; cnt++) //"400 Durchläufe
{
writeIntegerLength(cnt,DEC,3); //Aktuelle Werte ausgeben
writeChar(':');
writeIntegerLength(runningLight,DEC,3);
writeChar(',');
writeChar(' ');

setRP6LEDs(runningLight); //RP6 LEDs über I2C setzen
//mSleep(50); //Hierran scheitert es prinzipiell, es wäre aber sehr schön, wenn man solche Delays nicht brauchen würde
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 29); //Aktuelle LEDs des RP6 wieder auslesen
uint8_t result = I2CTWI_readByte(I2C_RP6_BASE_ADR);
if(result != runningLight)
{
writeString_P("\nTWI TEST FEHLER!\n");
writeString_P("Value: ");
writeInteger(runningLight,DEC);
writeString_P(" - Result: ");
writeInteger(result,DEC);
writeString_P("\r\n");
errorcount++;
testResult = 0;
}
runningLight <<= 1;
if(runningLight > 32)
runningLight = 1;

if((cnt+1) % 6 == 0) writeChar('\n');

if(cnt % 20 == 0)//Unwichtig, dient nur der Kommunikation mit Remotrol
{
SerialHeartBeat();
}

//mSleep(100);
}
if(!testResult)
{
writeString_P("Anzahl Fehler: ");
writeInteger(errorcount, DEC);
writeString_P("\r\n");
}
setRP6LEDs(0);

TestEnd(testResult);//Unwichtig, dient nur der Kommunikation mit Remotrol
}

Das Problem an sich ist klar... Er überrent halt dauerhaft die Hardware.
Es wäre aber prinzipiell sehr schön, wenn man die statischen Delays hier weglassen könnte.
Dann wäre die Lib perfekt ;)

RolfD
20.02.2011, 16:07
Genau die Umbenennungen meinte ich, war auch nicht als Kritik oder so gedacht, sondern nur für andere Leute die das hier lesen sollten.

Ne als Kritik fasse ich das auch nicht auf, es gab ja einen guten Grund das umzubennen.... ich mach sowas nicht aus Langeweile :)



Ringbuffer...:So wie ich das jetzt verstanden habe, geht es doch darum, dass die Daten ankommen aber eben nicht verarbeitet werden, weil schon wieder ÜBERSCHRIEBEN...


Das hatte SlyD gemutmaßt.. ja.. Tatsache ist aber, das die Empfangenen Daten noch in der ISR in das Registerarray ... also quasi der "Ringbuffer" geschrieben werden... weshalb ich auf seinen Post auch sagte: "zur Zeit hab ich den Eindruck das die Zeichen wirklich verloren gehen - das lässt sich aber genau rausfinden."
Es können _eigentlich_ keine Zeichen in der ISR verloren gehen - es sei denn, es verhindert was die Ausführung der ISR. Wenn die ISR aber geblockt wird nutzen auch 100 buffer nix weil bereits ein Buffer genutzt wird... die TWI Hardware müsste ein Hardwarebuffer haben.. wie ein UART... is aber nich :)

Es wäre aber auch möglich, das der Kontrolltask ein empfangenes Zeichen schlicht und einfach nicht mitbekommt .. das Programm von Dirk muss ich mir darauf hin noch mal ansehen. Es wäre zumindest eine Möglichkeit die SlyD auch ansprach.



Du sagst was vom umbennenen der Register? Wie genau meinst du das?

Siehe oben ->
* fix: I2CTWI_readRegisters -> I2CTWI_readableRegisters / double use of name
Mit "Register" meine ich hier die Slaveregister der Base Software. Sollte jetzt klar sein.



Quellcode zum Stresstest kommt nachher noch.

Super, danke Dir.



Was meinst du mit, dass "der der Fehler nicht im TWI Treiber zu suchen ist"? Wo denn sonst? Mit der alten Lib klappt das perfekt ;)
Und Remotrol ändere ich natürlich nicht, ich habe mir einen Testbuild dafür gemacht.

Ich formuliere mal vorsichtig... vielleicht ist Dir ein eigener Fehler mit der ALTEN Lib nur nicht aufgefallen? *grins ... perfekt ist relativ... nein.. noch ist es zu früh um andere Software zu debuggen, lass uns erst mal klar definierte Bedingungen wie Testprogramme usw. zum laufen bringen. :)

LG RolfD

Fabian E.
20.02.2011, 16:29
Was meinst du mit, dass "der der Fehler nicht im TWI Treiber zu suchen ist"? Wo denn sonst? Mit der alten Lib klappt das perfekt ;)
Und Remotrol ändere ich natürlich nicht, ich habe mir einen Testbuild dafür gemacht.

Ich formuliere mal vorsichtig... vielleicht ist Dir ein eigener Fehler mit der ALTEN Lib nur nicht aufgefallen? *grins ... perfekt ist relativ... nein.. noch ist es zu früh um andere Software zu debuggen, lass uns erst mal klar definierte Bedingungen wie Testprogramme usw. zum laufen bringen. :)

Das kann natürlich sein, aber wenn ich einfach nur das alte Hexfile-flashe klappt alles so wie ich das will. Da da trotzdem Fehler sein können, die ich einfach nicht sehe ist klar =)

Aber der Fehler mit der neuen Lib ist so offensichtlich, das hängt auf jedenfall irgendwie zusammen ;)
Ich tausche ja immer nur die Base aus. Entweder die mehr oder weniger originale I2C-Slave aus den Beispielen oder eben genau das nur mit der neuen Lib.

Achja, so zur allgemeinen Information: Die M128 unterstützt einfach mal schnell auch kein Multimaster. Soll aber wohl relativ leicht umzustellen sein, laut dem Entwickler.

SlyD
20.02.2011, 16:45
@Fabian:
Jo korrekt mit dem Ringbuffer könnte man das Überschreiben durch den Master vermeiden...
@RolfD: ... der Schreibpointer darf natürlich NICHT vom Master kontrolliert werden sondern muss vom Slave verwaltet werden.

Ein Ringbuffer hat einen Schreib- und einen Lesepointer. Der Schreibpointer wird ausschließlich in der ISR gesetzt, der Lesepointer ausschließlich in der Anwendung (in Library Funktionen).

Um den Code einfach zu gestalten könnte man z.B. einen Ringbuffer mit einem 2D Array anlegen (oder 1D Array und jeweils entsprechend die Indizes um +n Bytes erhöhen).
Z.B. 16 Einträge für Schreibzugriffe vom Master mit jeweils max. 8 Bytes - das reicht für die üblichen Anwendungen wohl aus.


> Registerarray ... also quasi der "Ringbuffer" geschrieben werden...

Nene das hat gar nix mit einem Ringbuffer zu tun - s. Funktionsweise oben bzw. schau Dir den UART Code an.

Der ganze Inhalt des Arrays ist bei beginn JEDES schreibzugriffes sofort ungültig wenn man es für die Übertragung von zusammenhängenden Kommandos(!) verwendet.
Man kann natürlich für bestimmte Funktionen einfach fest sagen Array[23] ist für die LEDs, Array[42] ist für Geschwindigkeit links o.ä. dann braucht man keine Befehle wie beim derzeitigen Slaveprogramm und kanns direkt setzen.
Geht übrigens auch beides in Kombination mit dem bisherigen Slave Code.
Ganz geschickt könnte mans kombinieren indem man sagt die ersten 8 Bytes werden mit dem Ringbuffer für Kommandos gepuffert und die anderen kann man direkt wie normale Register beschreiben - ohne Buffer.


Nochmal anders gesagt falls das hier noch ein Missverständnis sein sollte:
Bislang ist in der Lib ja nur ein Haufen "Register" vorgesehen. Wie bei normalen I2C Slaves auch. Das die zufällig in einem gemeinsamen Array liegen hat nur den Grund, dass es so effizienter ist das in der ISR zu beschreiben. Wenn man den Haufen Register als Buffer auffassen möchte hat der eben nur die Tiefe 1 - kein Ring kein gar nix.

Daher sind die Pausen nach Befehlen notwendig damit der Slave Zeit hat drauf zu reagieren. Die kann man natürlich auch sinnvoll für andere Dinge verwenden nicht nur mSleep... wird z.B. bei den Beispielen mit den Ultraschallsensoren gemacht die brauchen ja z.B. typ. 65ms für ein Ping Kommando.


MfG,
SlyD

RolfD
20.02.2011, 17:14
@Fabian Ok was du meinst ist also die Sequenz:

I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 29); //Aktuelle LEDs des RP6 wieder auslesen
uint8_t result = I2CTWI_readByte(I2C_RP6_BASE_ADR);

Also Pointer im Registersatz auf das 28'te byte (Arrays beginnen bei 0) setzen und nächtes Byte (29) lesen der LEDs? Das knallt? hm...
Mal abgesehen davon, das man das 1te Byte auf Position 0 so nie adressieren kann sollte das eigentlich gehen. TWI definiert:
#define I2CTWI_SLAVE_WRITE_REGISTERS 16
#define I2CTWI_SLAVE_READ_REGISTERS 48
... also genug Platz um nicht an die Registergrenze zu klatschen... hm hm..

Kannst du mal deine setRP6LEDs funktion noch posten? Dann bau ich mir das mal hier nach.... Danke.

@SlyD
Ich glaube wir Missverstehen uns...


@RolfD: ... der Schreibpointer darf natürlich NICHT vom Master kontrolliert werden sondern muss vom Slave verwaltet werden.

Der Schreibpointer wie auch der Lesepointer MUSS vom Master kontrolliert werden damit der Slave weis welches Register gemeint ist. Da die ISR bereits die Daten direkt in die Register schreibt, nutzt ein extra "RingBuffer" (dessen Pointer wie in einer UART natürlich allein der Slave zu verwalten hätte) nichts. Mir ist der Unterschied zwischen einer Pipe und einem Array schon klar...

Du verwendest als Beispiel selbst in "case TWI_SRX_ADR_DATA_ACK:"

current_register = TWDR;

und damit setzt du den Schreib/Lesezeiger im Registerset beim folgenden Access. Das mit den Buffern habe ich garnicht verändert sondern benutze da zu 100% dein Code :) Der Code von RP6Base_I2CSlave.c wie auch vom alten TWI benutzt soweit ich das sehen kann auch keine unabhängigen Buffer oder Pointer für Schreiben und Lesen. Das war vielleicht mal geplant aber umgesetzt ist es in der alte TWI steuerung nicht. Vielleicht kommt das mal noch auf meine Todo Liste, mal sehen.

Würde ich (bzw. Du damals) die Daten nicht sofort in die Slaveregister schreiben, wäre ein Ringbuffer natürlich das Mittel der Wahl... keine Frage, und dann stimmt natürlich alles was Du gesagt hast. Wir reden aber beim TWI immer noch über Registeraccess und NICHT über UART/ISO-OSI :)

Allerdings sehe ich grade, das ich evtl. die Initialiierung von current_register = 0; in case TWI_STX_DATA_NACK: verschlampt habe... wobei als nächstes ein START kommen muss und da wird sie dann initialisiert... *seufz .. ich muss da noch mal durch...

LG Rolf

SlyD
20.02.2011, 18:25
Der Schreibpointer wie auch der Lesepointer MUSS vom Master kontrolliert werden damit der Slave weis welches Register gemeint ist.


Lötzinn :P
Schau Dir erstmal an wie ein Ringbuffer funktioniert (google).

Der Master adressiert zwar welche Register er schreiben will, das hat mit dem Ringbuffer aber NIX zu tun. Die "Register" sind nur die Daten die in den Ringbuffer reinkommen. Ein Eintrag = Zustand ALLER Register. Daher macht das auch nur für einen kleinen Teil aller Register sinn - z.b. die ersten 8 der Master muss dann immer bei Register 0 anfangen damit das richtig initialisiert ist und keine alten Werte enthält - wird ja in den Beispielen sowieso schon so ähnlich gemacht.
Wird die Übertragung beendet, ist das ein neuer Eintrag im Buffer, der Slave zählt dann den Schreibpointer hoch.
Schreibt der Master mehr Bytes als 8 oder adressiert er direkt die Bytes >7 dann wird nix gebuffert und direkt in die entsprechenden Register geschrieben.

Der Buffer wird also selbstverständlich vom Slave gesteuert - der Schreibpointer wird halt in der ISR gesetzt wenn neue Daten ankommen hat aber nix damit zu tun welches Register der Master schreibt.
Adressiert der Master z.B. Byte 0 bis 7 wird der Schreibpointer vom Slave Code in der ISR um 1 erhöt (neuer Eintrag).
daten[schreibpointer_vom_ringbuffer][Registernummer_vom_Master]

daten[0][0] = Eintrag 0 im Buffer, Register Nummer 0
daten[0][1] = Eintrag 0 im Buffer, Register Nummer 1
daten[0][2] = Eintrag 0 im Buffer, Register Nummer 2
...
daten[1][0] = Eintrag 1 im Buffer, Register Nummer 0
...

Die "Register" sind die Daten im Buffer das könnte aber auch was ganz anderes sein.


Der LESEpointer muss sowieso vom Slave gesteuert werden - nur der weiss ja wo er gerade mit der Abarbeitung der ganzen angekommenen Daten ist.

Klar soweit?


> Du verwendest als Beispiel selbst in ...

grrrrrr ;) ich sage doch nicht was der fast 4 Jahre alte Code macht ich sage wie man es richtig machen MÜSSTE sollte hätte könnte wenn man einen Ringbuffer haben hätte wollen...
Ein Ringbuffer ist weder in Deinem noch in meinem Code drin ;)

MfG,
SlyD

EDIT:
Ich glaub Du hast Deinen Beitrag grad nochmal editiert während ich das hier geschrieben hab? Naja egal.

RolfD
20.02.2011, 18:42
Der Schreibpointer wie auch der Lesepointer MUSS vom Master kontrolliert werden damit der Slave weis welches Register gemeint ist.


Lötzinn :P


Noch mal...
Würde ich (bzw. Du damals) die Daten nicht sofort in die Slaveregister schreiben, wäre ein Ringbuffer natürlich das Mittel der Wahl... keine Frage, und dann stimmt natürlich alles was Du gesagt hast. Wir reden aber beim TWI immer noch über Registeraccess und NICHT über UART/ISO-OSI



Schau Dir erstmal an wie ein Ringbuffer funktioniert (google).


Du das weis ich aber ich bleibe dabei das wir uns missverstehen :) Und ich hoffe die Aussage da klärt es nun. Ich gebe dir vollkommen recht wenn man keine Register sondern eine Pipe verwenden würde... aber schau dir vielleicht einfach den geänderten Code mal an... ich ändere nichts am Prinzip was Du mal vorgegeben hast, ich mache ihn nur gängig. Alle Daten landen sofort im RAM, und zwar in den Slave Registern. Ein Ringbuffer ist unnötig und hilft nicht, das Receiverpüroblem zu lösen. Es wäre nur weitere RAM verbraucht.. für nix. Ob ich ein N-Dimensionales Array als Ringbuffer verwende oder die Daten direkt ins RAM auf die Registerplätze schreibe, ist Pott wie Deckel. Für die ISR ebenso wie für verlorene Zeichen.

I2CTWI_writeRegisters[current_register++] = TWDR;

sagt doch alles, oder?
Mit der Aussage bezüglich Read/Write Register oben lag ich übrigends daneben wie ich jetzt sehe... tut aber nichts zur Sache.

LG RolfD

SlyD
20.02.2011, 18:52
OK gut - wie ich schon geschrieben hatte hattest du glaube ich deinen Beitrag da oben auch nochmal editiert das hatte ich nicht gesehen - oder meine Augen lassen nach ;)

Dennoch: Der Ringbuffer ist notwendig wenn Du tatsächlich wie in dem Testcode von Dirk nahezu komplett ohne Pause Daten in Richtung Slave ballerst ;)
Sonst kann da natürlich mal was überschrieben werden bevor der Slave überhaupt mitbekommt das sich da was geändert hatte (läuft ja asynchron zum Hauptprogramm).

Mit der bisherigen Methode ist eine kleine Pause nach jeder Übertragung notwendig sonst kann das in die Hose gehen auch wenn der Rest vom Code 100% korrekt arbeitet.
Man kann in der Zeit natürlich irgendwas anderes machen - auch auf dem I2C Bus aber mit anderen Slaves.

So nun aber genug geschrieben für heute :)

MfG,
SlyD

RolfD
20.02.2011, 19:09
OK gut - wie ich schon geschrieben hatte hattest du glaube ich deinen Beitrag da oben auch nochmal editiert das hatte ich nicht gesehen - oder meine Augen lassen nach ;)

*grins ... sollich dir meine Lesebrille leihen :)



Dennoch: Der Ringbuffer ist notwendig wenn Du tatsächlich wie in dem Testcode von Dirk nahezu komplett ohne Pause Daten in Richtung Slave ballerst ;)

Sonst kann da natürlich mal was überschrieben werden bevor der Slave überhaupt mitbekommt das sich da was geändert hatte (läuft ja asynchron zum Hauptprogramm).



Genau das ist das hüpfende Komma.... und das wäre zu prüfen. Wenn es so ist... ok. Wenn es doch echte verlorene Zeichen sind, gehts weiter mit der Fehlersuche... :) Aber der Hinweis ist wichtig, man muss dem Slave natürlich Zeit lassen um Daten zu verarbeiten... man kann den nicht mit Daten bombardieren und erwarten das er immer sofort reagiert.

(@Fabian, da ist auch dein Delay mit 50 ms aus dem Beispiel zwischen schreiben der LEDs und lesen von Zustand zu kurz gesetzt - das TWI ist einfach zu schnell dafür. Du stresst hier die Base und nicht das TWI. Für die Base würde sich ein Busy Flag im Statusregister anbieten was anzeigt ob die Base schon aktiv war.. das der Master dann abfragt und erst liest wenn das Bit getoggelt ist. Macht mit mehreren Mastern z.B. bei eienr M128 aber auch Probleme, ist als wenn, dann nur ein Notpflaster. Ihr müsst euch glaub ich erst an das schnellere TWI gewöhnen :) Übrigends kann man das was ich der M32 beibringe auch mit der M128 machen... "kann das nicht" gibts nicht! Ich hab ne M128 hier liegen.. also irgendwann.... )

Das kann so wirklich nur eine Pipe mit Ringbuffer. Ich halte es für realistisch, sich auf 3 bis 5 Updates/sec eines kompletten Registersatzes zu beschränken, aber die umgebaute Lib zeigt mit Dirks Programm zumindest was nun auf dem TWI möglich wäre. :) Da ist 1% Fehlerquote ohne Terminalausgabe schon ganz gut. :) Vielleicht kann man da tatsächlich einen Ringbuffer einbauen aber zuerst muss alles andere laufen. Ein Ringbuffer für Registeraccess ist mässig sinnvoll (man müsste ein Ringbuffer in der Breite der Register bauen, also 16 byte x z.B. 8 Byte Tiefe, was min. 128 Byte fürs Schreibregister an kostbarem RAM verballert - und auch 8 byte Buffer kriegt man bei 100Kbit/sec schnell platt geschrieben.) und direkte Kommunikation via Pipe zwischen Prozessoren eher ne Aufgabe für linux oder so :). Aber vielleicht findet sich mal jemand der sowas umsetzt wenn die Grundlagen funktionieren.
Die generic Adresse 00 würde sich für so Spielchen anbieten. Eine Pipe wäre dann sozusagen ein Broadcast und das Protokoll muss sicherstellen das der richtige Prozessor sich angesprochen fühlt. Von mir aus dann auch mit crc und so Schnickschnack :) Dann noch 8 M32er auf die Base und man hat den kleinsten Clusterbot der Welt :)



So nun aber genug geschrieben für heute :)


Dir ein schönen Feierabend :) Mein RP6 ist aufgeladen, kann weiter gehen mit testen :)
LG Rolf

RolfD
20.02.2011, 23:23
@Dirk, ich kann dich beruhigen.. bzw. uns :)
Ich habe dein Testprogramm nun so umgebaut, das es exakt 1000 Pakete sendet... und auf der Base nachgeschaut wieviel IRQs reinkommen bzw. wie oft die ISR aufgerufen wird. Bei 1000 sind es exakt 6000 Aufrufe - je 4 für 4 Datenbytes und 1 für start, 1 für stop ... macht 6000. nicht ein mal mehr oder weniger. :) Die Sorge, das der Prozessor also was nicht mitbekommt war zum Glück unbegründet, die Errors haben nur mit dem Laufzeitverhalten der ansyncronen Datenübertragung zu tun. Das relativiert auch Aussagen zum Slavemode oben... er ist wohl stabil. Allerdings gibts doch einiges zu beachten was das Timing zwischen Base und M32 angeht - wie unsere Diskussion heute zeigte. Ich bin froh das sich das so aufgelöst hat. Aber weiter gehts, die Kuh ist noch nicht vom Eis.
LG Rolf

Das noch als Anhang:

// if (I2CTWI_writeRegisters[0] && !I2CTWI_writeBusy) {
//I2CTWI_writeBusy ist eine Statusvariable der ISR die so nicht mehr nötig ist
//und unterstützt wird. Statt dessen bitte wie im Master I2CTWI_isBusy() nutzen.
//ret=0 isr frei, 1= isr belegt
if (I2CTWI_writeRegisters[0] && !I2CTWI_isBusy()) {


EDIT:
Nach reiflicher Überlegung und überschlafen des Problems mit Dirks Programm überlege ich nun, ob ich das verhalten der TWI dahingehend ändern soll, das nach einem Schreibvorgang auf den Slave das TWI NACKs senden soll bis der Slavecode quasi signalisiert das die Daten verarbeitet wurden. Das Entspricht auch dem Verhalten von EEPROMS und ähnlichen Bausteinen und würde das überschreiben unverarbeiteter Register im Slave nachhaltig verhindern. Jedoch muss der Slave dann zusätzlich über ein bitset eines Flags das Empfangen/Verarbeiten quitieren. Ggf muss auch der Mastercode verändert werden, so das er ein retransmit versucht bis keine NACKs mehr kommen (was ich aber komplett in der Lib regeln kann) Das bedeutet natürlich weitere kleine Änderungen an vorhandenen Programmen im Slavemode, macht aber die Synconisation zwischen Master und Slave absolut sicher, laufzeitunkritisch und conform zu anderen Bauteilen. Es würde aber auch die Prüfung im Slave erleichtern, ob er was zu tun hat - er braucht nur eben dieses Flag zu befragen.
Meinungen?

EDIT: Je länger ich drüber nachdenke, um so mehr frag ich mich warum ich das noch nicht umgesetzt hab... :)

EDIT: Oh Ohhh.... @Fabian, deine 2400 Fehler erklären sich grade noch aus einem Punkt, der mir beim mergen der Dateien unterlaufen ist. Es ist nicht nur dein timing... Ich denke, heute Abend gibts ne neue Beta Version zum testen für alle. Dann aber schon mit der Funktion aus edit 1. Dazu sag ich aber dann noch mehr.

EDIT: hat jemand vielleicht ne schließende Klammer übrig, ich hab eine in der ISR verbummelt *seufz
Noch'n EDIT: ich hab sie... www.prettyprinter.de sei dank :)

Und der letzte EDIT für heute....
Leider wird das wohl nichts mit der Beta für heute Abend. Das System reagiert auf NACKs nicht richtig (harmlos ausgedrückt...) , weder in der ISR noch im Mastercode (Fehlerbehandlung, Retransmit usw.) Das wird dauern bis ich das laufen hab. Sorry.

Fabian E.
21.02.2011, 18:41
Mach dir keinen Stress ;)
Also das Verhalten was ich erwarten würde. bzw. am Besten finde, sieht so aus:
Ich kann vom Master einen Slave so schnell ich will mit Daten beschießen ohne dass irgendwas schief geht.
Das dann natürlich irgendwo gewartet werden muss versteht sich von selbst.
Das Warten sollte allerdings die Lib übernehmen und außerdem sollte das Warten so kurz wie möglich sein.

Das entspricht ja quasi dem, was du in deinem ersten Edit geschrieben hast.
Wenn du das so hinbekommst, dann wäre die Lib schneller und stabiler als die alte.
Und das sie Multimaster kann wäre dann der ultimative Bonus ;)

RolfD
22.02.2011, 12:20
Irgendwie muss die i2c Hardware der Mega32 CPUs ne Macke haben...
Ich zeig euch mal nen Protokoll, vielleicht hat ja einer eine Idee dazu:
Es geht um den Buserror da... wir sind Receiver Slave.

die Syntax des Protokls ist ca.
Datenregister x Statusregister x STATUSREGISTERALSTEXT meineANTWORT (Grund) Flags (bit2=SlaveBusy bit1=IsrBusy bit0=ohne Bedeutung)
Die Flags spielen eigentlich keine Rolle, erklären aber warum die ISR nackt (was auch vom Prinzip her so richtig ist). Die Datenverarbeitung ist asyncron, im Prinzip nackt die isr bis das Programm SlaveBusy auf 0 setzt.
Start der Datenübertragung

got DATA a and STATUS 60 mean / SRX_ADR_ACK ACK (Start) Flags 1
got DATA 0 and STATUS 80 mean <- SRX_ADR|data)_ACK -> ACK (Adress) Flags 11
got DATA 63 and STATUS 80 mean <- SRX_(adr|DATA)_ACK -> ACK (Data) Flags 11
got DATA 1 and STATUS 80 mean <- SRX_(adr|DATA)_ACK -> ACK (Data) Flags 111
got DATA 0 and STATUS 80 mean <- SRX_(adr|DATA)_ACK -> ACK (Data) Flags 111
got DATA a and STATUS 60 mean / SRX_STOP_RESTART (eot) Flags 101

Datenübertragung abgeschlossen, nächstes Datagram (wird abgebrochen)

got DATA a and STATUS 60 mean / SRX_ADR_ACK ACK (Start) Flags 101
got DATA 0 and STATUS 80 mean / SRX_(ADR|DATA)_ACK NACK (Slave busy) Flags 111

Die isr nackt weil der Client die Daten noch nicht bearbeitet hat
Clent sagt aber nun "Received: 1"

got DATA 63 and STATUS 88 mean / SRX_ADR_DATA_NACK ACK (eof) Flags 1

Wir antworten noch auf das Nack, der Versuch ist abgebrochen. Der Master versucht ein Neustart. Wir sahen bis hier ein sauber ünbertragenes Byte und einen abgebrochenen Versuch, beides gut abgewickelt.
Also weiter...

got DATA a and STATUS 60 mean / SRX_ADR_ACK ACK (Start) Flags 1
got DATA 0 and STATUS 80 mean <- SRX_ADR|data)_ACK -> ACK (Adress) Flags 11
got DATA 63 and STATUS 80 mean <- SRX_(adr|DATA)_ACK -> ACK (Data) Flags 11
got DATA 2 and STATUS 80 mean <- SRX_(adr|DATA)_ACK -> ACK (Data) Flags 111
got DATA 0 and STATUS 80 mean <- SRX_(adr|DATA)_ACK -> ACK (Data) Flags 111
got DATA a and STATUS 60 mean / SRX_STOP_RESTART (eot) Flags 101

Wieder ein Datagram übertragen...

got DATA a and STATUS 60 mean / SRX_ADR_ACK ACK (Start) Flags 101
got DATA 0 and STATUS 80 mean / SRX_(ADR|DATA)_ACK NACK (Slave busy) Flags 111

isr nackt weil Daten unbearbeitet
Clent sagt aber nun Received: 2

erwartet wird ein SRX_ADR_DATA_NACK vom Master wie in den ersten beiden Durchläufen damit die ISR ein ACK senden kann, bekommen tue ich das:

got DATA 1 and STATUS 0 mean BUSERROR Flags 1

In der Folge verschluckt sich das System weil der Master den Buserror nicht mitbekommen hat, ist hier aber nicht mehr relevant...

Warum bekomme ich erst im 3 Datensatz nach immerhin 6 übertragenen Byte Daten einen Buserror auf einen Ablauf, der genau so vorher 2 mal geklappt hat? Der Master macht in beiden duchläufen exakt das gleiche, der Slave auch. Was ist am 3. bzw. ende vom 2 anders? Andere Master sind nicht am Bus. Das Ganze bei langsamen 100khz.
Achja, das Ganze ist aus Dirks Programm, senden von 3 Byte auf Register 0,1 und 2

Die Vorgeschriebene Reaktion auf SRX_ADR_DATA_NACK (0x88) laut Datenblatt ist [Switched to the not addressed Slave mode; own SLA will be recognized; GCA will be recognized if TWGCE = “1”] -> (1<<TWINT)|(1<<TWEA) ... also ACK

Buserrors findet man im allgemeinen nur bei ungültigen Start/Stop Folgen, es kann sein das der Master das letzte Datenbyte mit einem NACK senden muss als Vorbereitung auf Datenende und Stop, das muss ich noch rausfinden aber es erklärt nicht warum es mal geht und mal nicht!

Ideen, Ratschläge,Meinungen?

PS: SlyD hat es sich übrigends einfach gemacht ... er ACKt einfach alles was reinkommt *grins

LG Rolf


EDIT:
So... das Problem ist behoben... und in Dirks Programm gehen keine Daten mehr flöten... keine Buserrors... und das Ganze mit ca. 4000 Zeichen/Sek. UND Textausgabe... Leider bleibt ab und zu der Transport komplett hängen, wo ich noch nicht raus hab warum. Vielleicht hab ich mir irgendwo nen deadlock gebaut. Und ich hab jetzt noch nicht alle anderen Funktionen geprüft. Und noch was... wenn ich gewusst hätte wieviel besch.... schlechte Doku es zu dem Thema gibt, hätte ich NIEMALSNICHT damit angefangen... aber es wird langsam.

SlyD
23.02.2011, 13:51
So... das Problem ist behoben... und in Dirks Programm gehen
keine Daten mehr flöten... keine Buserrors... und das Ganze mit ca. 4000 Zeichen/Sek. UND Textausgabe...


:)

Tipp: In die ISR könntest Du zumindest LED Ausgaben reinmachen um zu schauen an welcher Stelle er sich aufhängt.




schlechte Doku es zu dem Thema gibt


Sei froh DAS es überhaupt Doku gibt :P
Ich kenne Bauteile wo nur die RegisterNAMEN im Datenblatt stehen und nix weiter erläutert wird was die genau machen - und der Hersteller ist da auch keine große Hilfe ;)


MfG,
SlyD

RolfD
24.02.2011, 01:13
@SlyD oh.. auch nicht schlecht... da bin ich mit dem Prozessordatenblatt und den vielen halb implementierten Sources die im Web rumgeistern (incl. Atmels Sourcecode) ja noch gut dran :) Im Web zu googlen lohnt sich für mich nicht mehr.. da finde ich inzwischen meine eigenen Beiträge hier :) Interssanter Weise finden sich die besten Infos immer noch hier im RN-Wissen.

Hallo,
damit euch nicht langweilig wird, hab ich für die Betatester was.
Die Geschichte ist noch nicht stabil aber zumindest sollten sich Master und Slave unterhalten können. (bei mir tuen sie das :) )

Zunächst das modifizierte Testprogramm von Dirk:
Die Base/Slave, ich poste nur die main


int main(void)
{
initRobotBase();
setLEDs(0b111111);
mSleep(500);
setLEDs(0b000000);
I2CTWI_initSlave(RP6BASE_I2C_SLAVE_ADR);

while (true)
{
/* asks TWI for waiting Jobs, textblock may be a "task_slave" in rp6 c notation?
**
*/ if (I2CTWI_needAction()) {
/*
** yessssss ... doit!
*/
// get data from slave receiver mode register
controlbyte = I2CTWI_writeRegisters[0]; // Read control byte
I2CTWI_writeRegisters[0] = 0; // and reset it (!!!)
lowbyte = I2CTWI_writeRegisters[1]; // Read lowbyte
highbyte = I2CTWI_writeRegisters[2]; // Read highbyte

// copy data to slave sender mode register
I2CTWI_readableRegisters[0] = controlbyte; //copy controlbyte
I2CTWI_readableRegisters[1] = lowbyte; // copy lowbyte
I2CTWI_readableRegisters[2] = highbyte; // copy highbyte

// this is controlled by I2CTWI_needAction()
// if (controlbyte == CMD_SHOW_DATA) { //
cnt = ((highbyte << 8) | lowbyte); // Restore 16-bit value
#ifdef TEXT_OUTPUT
writeString_P("Received: "); // and show it
writeInteger(cnt, DEC);
#endif
if (!cnt) error_cnt = 0; // Reset error counter
else error_cnt++; // Error counter + 1
if (cnt != error_cnt) {
writeString_P(" ERROR!\n");
error_cnt = cnt;
}
#ifdef TEXT_OUTPUT
else writeChar('\n');
#endif
controlbyte = 0;
// }

/* signals Job as "done" and free the receiver for next Data
**
*/ I2CTWI_doneAction();
/*
** done!
*/
}
/*
** blink like fireworks, attract girls by danci'n circles or lying around an drink ... gear oil
*/
}
return 0;
}

Dann der Master auf der M32



int main(void)
{
initRP6Control();

initLCD();

I2CTWI_initSlave(12); // Initialize the TWI Module for Master operation
// with 100kHz SCL Frequency

// Register the event handlers:
I2CTWI_setTransmissionErrorHandler(I2C_transmissio nError);

setLEDs(0b1111);
mSleep(500);
setLEDs(0b0000);

while(true)
{
if (!I2CTWI_isBusy()) {
cnt++; // TEST: COUNTER!!!
if (cnt > 255) cnt = 0;
buffer[0] = 0;
buffer[1] = CMD_SHOW_DATA;
buffer[2] = cnt;
buffer[3] = (cnt >> 8);
I2CTWI_transmitBytes(RP6BASE_I2C_SLAVE_ADR, buffer, 4);

mSleep(1000); //the slave works...

buffer[0] = 0; //reset buffer
buffer[1] = 0;
buffer[2] = 0;
buffer[3] = 0;

I2CTWI_readRegisters(RP6BASE_I2C_SLAVE_ADR, 0, buffer, 4);

if (cnt == ((buffer[2] << 8) | buffer[1])) {
#ifdef TEXT_OUTPUT
setCursorPosLCD(0, 0);
writeStringLCD_P("I2C has moved:"); // What was sent?
setCursorPosLCD(1, 0);
writeIntegerLCD(cnt, DEC);
#endif
} else {
setCursorPosLCD(0, 0);
writeStringLCD_P("Error!"); // What was sent?
}
//if (cnt==3) while(true); //countertrap
}
}
return 0;
}

Es hat sich in soweit etwas verändert, das der Master die Daten auf den Slave schreibt, dieser kopiert die Daten vom Schreib- ins Leseregister und prüft, eine Zeit lang später (hier 1 Sek. weil ich am debuggen bin) liest der Master die Daten zurück um sie mit dem Original zu vergleichen. Sinnvoll wäre hier ein Statusbyte einzuplanen das der Master abfragen kann. Dafür müsste der Master aber ins Leseregister schreiben da der slave nicht mitbekommt ob der Master liest. Ließe sich aber irgendwie anders einbauen. Ich vermute aber das man in Dirks Programm locker auf 1ms runter gehen kann, der Slave copiert ja nur bissel in der Gegend rum bis die Daten bereit sind. Hier wie in der Schreibfunktion zu nacken macht aber keinen Sinn da es den Master lesend blockiert. Das ist mit einem Statusbyte besser gelöst

Es sind also alle 4 Modi mit Slave Receive/Transmit und Master Receive/Transmit beteiligt. Die ISR sperrt nach einem schreibenden Durchgang auf den Slave weitere Schreibversuche durch NACKen. Erst wenn der Slave fertig ist schaltet er die ISR frei. I2CTWI_needAction() und insbesondere I2CTWI_doneAction() erledigen das.

Eine Abfrage von Statusbytes in den Registern erledigt sich damit, allerdings muss damit jede Schreibaktion vom Slave quitiert werden sonst wartet sich der Master einen Wolf.

Die aktuelle twi_target.h sieht bei mir so aus (hier von der Base):


/**/

#ifndef TARGET_H //Include once
#define TARGET_H


/* this define Hardware to selecting the right includes
** use one of them, never both
*/
#define M32
//#define BASE

/* this define TWI Modus to stip obsolete Slave code in Mastermode
** use one of them, never both
*/
//#define TWIMASTER
#define TWISLAVE

/*
** use LOWLEVEL I2C Functions without interupts too
*/
//#define TWILOW

/* this TWI define Speedsetup at 100 or 400 KHz Standart
** use one of them
*/
#define TWISPEED 100

/*
** use Generic calls, not supported yet
*/
//#define GENADR


/*
** if set, Base can use I2CLCD like LCD on M32
*/
//#define I2C_LCD




/*
** internal use, hide warnings
*/
#define UNUSED(x) ((void)(x)) //help optimizing
#define DEBUG

#include "RP6I2CTWI.h"

#ifdef BASE
#include <RP6RobotBase.h>
#ifdef DEBUG
#include <RP6RobotBaseLib.h>
#endif
#ifdef I2C_LCD
#include <RP6RobotBaseLib.h>
#include <lcd.h>
#define writeLCDCommand writeI2CLCDCommand
#define initLCD initI2CLCD
#define clearLCD clearI2CLCD
#define clearPosLCD clearPosI2CLCD
#define writeCharLCD writeCharI2CLCD
#define writeStringLCD_P writeStringI2CLCD_P
#define writeStringLengthLCD writeStringLengthI2CLCD
#define writeStringLCD writeStringI2CLCD
#define writeNStringLCD_P writeNStringI2CLCD_P
#define _showScreenLCD_P _showScreenI2CLCD_P
#define writeIntegerLCD writeIntegerI2CLCD
#define writeIntegerLengthLCD writeIntegerLengthI2CLCD
#define setCursorPosLCD setCursorPosI2CLCD
#define setlightLCD setlightI2CLCD
#endif
#else
#ifdef M32
#include <RP6Control.h>
#ifdef DEBUG
#include <RP6ControlLib.h>
#endif
#ifdef I2C_LCD
#include <RP6ControlLib.h>
#include <lcd.h>
#endif
#endif
#endif
#endif

Ihr werdet nicht alles brauchen, allerdings sind die Optionen im oberen Bereich schon interessant da sie ggf. Code einsparen helfen. Achtung, DEBUG ist Aktiv... unten stricke ich mir u.a. das i2c-lcd für die Base als normals M32 Display zurecht.... macht sich gut wenn man die gleichen Programme auf der Base UND der M32 testen will.

In der TWI hat sich viel getan, u.a. habe ich einige Variablen abgeschaltet, die versuchten die ISR zu monitoren. Sowas z.B.

/* don't use, obsolte, try I2CTWI_isBusy() instead
volatile uint8_t I2CTWI_genCallCMD;
volatile uint8_t I2CTWI_dataWasRead = 1;
volatile uint8_t I2CTWI_dataReadFromReg = 1;
volatile uint8_t I2CTWI_readBusy = 0;
volatile uint8_t I2CTWI_writeBusy = 0;
*/

Die Funktionalität (war eigentlich keine) ist mit den Action Befehlen und isBusy besser gelöst, der C Preprozessor hilft hier beim anpassen älterer Projekte ungemein ohne das man viel umschreiben muss. Allerdings funktioniert das jetzt so nicht mehr so einfach mit dem Slave Programm für die Base. Aber zum testen reicht Dirks Programm und Fabian wird den Slave sicher anpassen können. Sonst mach ich das irgendwann. Ich sehe jedenfalls nicht ein, Code zu pflegen der obsolet ist, nur weil er da ist. So viel Ram und Flash hat der RP6 auch nicht...
Evtl. mache ich das NACKen nach schreiben auf den Slave optional, derzeit kann man sich gut mit dem bedingungslosen Befehl I2CTWI_doneAction(); in der äusseren While Loop helfen wenn der Slave möglichst nicht nacken soll - allerdings werden dann halt die Register überschrieben. Die gencall Geschichte ist z.Z. komplett abgeschaltet, da konnte bisher eh nur ein Byte mit empfangen werden. Da hab ich noch was in der Todo stehen :) für irgendwann mal...

Das ganze System hat immer noch viele Macken, es ist noch nicht auf Multimaster umgestellt aber man kann aus dem Slavemode herraus zumindest Masterfunktionen ansprechen. Ein paar Macken sind leider systembedingt - z.B. werden zusammenhängende Befehlsketten wie Setzen einer Registeradresse und anschließendes Register lesen in 2 Zyklen ausgeführt was nicht nötig wäre, was aber z.B. dazu führt, das die ISR für kurze Zeiten "frei" ist obwohl sie eigentlich mitten in einer Transaktion steckt. Ein folgender Befehl kann hier die noch arbeitende ISR stören. Das scheint auch für Buserrors zu sorgen. Sowas ist unschön! Ob ich das beheben kann, weis ich noch nicht. Es stört mich auch, das in einer ansich Interruotgesteuerten Umgebung blockende Befehle stecken, das mindert den Sinn einer ISR doch gewaltig. Vermutlich stimmen auch einige Reaktionen auf die TWI-State Engine noch nicht, was wohl auch zu Buserrors führt, da bin ich aber dran... alles in allem also noch sehr Beta. Aber wieder mal ein Stück weiter hoffe ich.

Mein nächstes Ziel ist zum einen die Geschichte stabiler zu machen, aus irgendeinem Grund bleibt der Transport hängen, zum anderen möchte ich die Programme von Dirk zusammen legen, zu einem "Masterslave" so das sich Master und Slave des jeweiligen Boards abwechseln und gegenseitig Daten zuwerfen und prüfen. Also bidirektionaler/intermitierender Master/Slave Betrieb auf Base und M32.
Mir erschließt sich noch nicht, welch tieferer Sinn sich dahinter verbirgt, das man z.B. mit Readregisters nur Speicherplätze ab 1 im Buffer lesen, aber mit anderen Funktionen ab 0 beschreiben kann. Sowas werde ich wohl auch ändern wenns keine großen Einsprüche gibt.

Benutzt man die Geschichte nur im Mastermode mit nur einem Prozessor (per twi_target einstellen), kann man den Slavecode komplett einsparen, benutzt man auch den Slave, hat man beide Modi zur Auswahl. Die default Werte für
#define I2CTWI_SLAVE_WRITE_REGISTERS 16
#define I2CTWI_SLAVE_READ_REGISTERS 48
sind etwas zu hoch gewählt, hier würden im normalen Slaveprogramm auch 8/32 reichen wenn man ein paar Byte sparen will. Ich habs aber mal so gelassen.

EDIT:
Und wieder eine kleine Änderung in der RP6I2CTWI.c
Um die Buffer gehören Präprozessorabfragen zum TWISLAVE, keine funktionelle Änderung aber es spart Platz im Mastermode. In der nächsten Fassung ist das mit drin. In der .h war es schon eingetragen.

#ifdef TWISLAVE
uint8_t I2CTWI_readableRegisters[I2CTWI_SLAVE_READ_REGISTERS];
volatile uint8_t I2CTWI_writeRegisters[I2CTWI_SLAVE_WRITE_REGISTERS];
#endif

Dann noch was zum NACKen und Buffergrenzen überfahren, es wäre z.Z. wohl denkbar, das der Master hinter die Buffergrenze liest was der Slave nacken müsste worauf hin der Master abbrechen soll. Ob das klappt weis ich nicht, muss ich noch testen. Ich überlege sowieso, alternativ 1 R+W oder 2 R/W Registersets per twi_target.h anzubieten, das macht den Slave universeller. Und demnächst wirds zusätzlich ein Satz ISR-lose Funktionen geben, ähnlich der von Peter Fleury oder der AVR-LIBC aber angepasst an die Abläufe und Vorgaben der aktuellen TWI Steuerung.

EDIT: Hm.. ich glaube da stimmt was noch nicht... der Master hängt sich auf laut LEDs.. aber morgen wirds wieder ne Beta Version geben denk ich mal. NACK als Option ist drin, die Lowlevelfunktionen sind schon drin, jede Menge Code cleanup über #ifdef...

LG Rolf

Dirk
24.02.2011, 19:31
@Rolf:
Du bleibst ja energisch am Ball. Komplement! =D>

Kurze Frage als Beta-Tester:
Warum wird der M32 Master mit I2CTWI_initSlave(12) initialisiert?
Muss ich ihn in der twi_target.h auch als Slave (#define TWISLAVE) definieren, weil als Master das I2CTWI_initSlave(12) zum Fehler führt?

Kurze Anregung als "normaler User":
Du bist ja da an einem dynamischen Prozeß dran, d.h. versuchst, in mehreren Schritten möglichst gute Lösungen zu finden. Dabei verändert sich auch die "Makrosprache" und der Funktionsumfang, der dem RP6 zur Nutzung des I2C-Bus mitgegeben wurde.
Wenn die entstehende Lib (ich bin da guter Hoffnung!) problemlos für alle zu nutzen sein soll, gibt es bei der Entwicklung eines neuen TWI-"Treibers" aus meiner Sicht auf der Makroebene nur 3 Möglichkeiten:
1. Man nutzt NUR die vorgegebenen Befehle und Makros, so dass vorhandene Programme mit der neuen Version kompiliert werden können.
2. Man ergänzt alle vorgegebenen Befehle und Makros durch neue Funktionen und Makros. Damit ist ebenfalls Abwärtskompatibilität möglich, es können dann aber neue Funktionalitäten zusätzlich genutzt werden.
3. Man verzichtet auf Kompatibilität und beschreibt einen neuen "Befehlssatz" für die TWI-Funktion.
Mir persönlich liegt die Option 2. am Herzen, weil ich faul bin, und meine Programme nicht ändern möchte. Trotzdem würde ich gern von "Fortschritten" durch eine neue Lib profitieren (egoistisch wie ich bin ...).

Also: Bitte nicht als Kritik, sondern nur als Anregung verstehen. :-k

Ich teste weiter ...

Gruß Dirk

RolfD
24.02.2011, 20:55
@Rolf:
Du bleibst ja energisch am Ball. Komplement! =D>


Naja ich will das es funktioniert... ich kauf mir keine Zusatzboards wenn die am TWI nur so vor sich hin stottern... alles Eigennutz :)



Kurze Frage als Beta-Tester:
Warum wird der M32 Master mit I2CTWI_initSlave(12) initialisiert?
Muss ich ihn in der twi_target.h auch als Slave (#define TWISLAVE) definieren, weil als Master das I2CTWI_initSlave(12) zum Fehler führt?


Nein.. ja.. das auch.. aber es hat andere Gründe. Technisch gesehen gibts zwischen der Master und Slave Init ganze 3 Unterschiede. Der Master kann ohne eigene Adresse leben funktioniert aber auch mit dieser, der Slave ohne Geschwindigkeitsangabe und funktioniert mit dieser und letztlich macht der Master im Init ein NACK und der Slave ein ACK.
Dazu braucht es keine 2 separaten Funktionen, es reicht wenn diese Daten da sind, zur Not eben Defaultwerte. Daher habe ich in der aktuellen Codefassung nicht mal mehr ein Unterschied sondern gleich alles in eine Funktion gesetzt.


void I2CTWI_init(uint8_t dummy)
{
UNUSED(dummy); // reserved, set up speed and OWN address in twi_target.h

TWI_operation = I2CTWI_NO_OPERATION; //Setup defaults
bitset(TWI_statusReg,STR_lastTransOK);
bitclr(TWI_statusReg,STR_needAction); // we start clean
bitclr(TWI_statusReg,STR_isr); // don't talk if isr is busy
bitset(TWI_statusReg,STR_NACKonAction);//we NACK, Master must wait to rewrite dirty Regs
cli();
#ifdef TWISLAVE
TWAR = TWIADDR; // Set own TWI slave address.
#endif
TWDR = 0xFF; // Default content = SDA released.
TWSR = 0x00; // DO NOT USE PRESCALER! Otherwise you need to mask the
TWBR = I2CTWI_SPEED(TWISPEED); // default Speed @ KHz in master mode, 100 and 400 are well known
#ifdef TWISLAVE
TWACK();
#else
TWNACK();
#endif
sei();
}


So schaut die Init in der kommenden Version aus...

Wer möchte, kann sich dann ein:
#define I2CTWI_initMaster I2CTWI_init
und
#define I2CTWI_initSlave I2CTWI_init
in die twi_target setzen und man muss nicht mal groß was an alten Programmen umschreiben. Ich glaube das setz ich sogar so ins .h File.
Weitere Krücken wären ebenfalls denkbar. Zu irgendwas muss so'n Compiler ja taugen...
#define I2CTWI_delay nop // microwaiting
der is auch schön :)

Noch mal zum Verständnis, es gibt keinen wirklichen Unterschied zwischen Master und Slave AUSSER das der Slave Code enthält, den der Master allein so nicht braucht und der Master Code enthält, den der Slave allein so nicht braucht. Das würde immer zu einer Codegröße von Master+Slave führen und man müsste sich um Code kümmern den man vielleicht nie nutzt. Das versuche ich mit der twi_target.h etwas einzugrenzen wenn man z.B. den Slave nicht braucht weil man nur ein RP6 ohne Zusatz und vielleicht ein SRF-Sensor hat. Mit der Lib wird man aber auch von 2 CPUs gemeinsam auf Targets wie i2c-LCDs zugreifen können und da müssen dann beide Programmteile zusammen wirken wenn die Prozessoren noch untereinander quatschen sollen. Ich betone "zusammen" ... also nicht Master oder Slave. Der alleinige Mastermode ist quasi nur eine reine Ersparnis um ein paar codebytes + etwas ram für Register aber oft sinnvoll wenn man eben kein Slave braucht. Es ist aber nicht als sich "gegenseitig ausschließender Modus" zu verstehen... Hier in der Lib ist vorgesehen: Slave+Master und Master sowie eine Lowlevel Master Geschichte die aber davon eigentlich unbetroffen und z.Z. noch unveröffentlicht ist.

Das ist das Gleiche wie bei der Multimastergeschichte.... jede ATMEL-cpu mit i2c Bus hat eine eingebaute Busarbitierung und daher sind sie alle MM-Fähig. Allerdings muss die Software die Situation erkennen - tut sie es nicht, ist sie eben nicht MM-fähig... die Hardware.. ist es aber, und die sorgt z.B. auch dafür wenn sich die Software nicht kümmert, das es auf dem Bus eigentlich nicht zu Schäden/Datenverlust kommt - im Gegensatz zu selbstgeschriebenen Softwarelösungen an irgendwelchen Portpins. Wenn man aber jegliches Handshake abschaltet, ist klar das Daten verloren gehen. Es käme auch keiner auf die Idee ein UART mit wilden Baudraten zu betreiben bzw. da weiss jeder das sowas in die Buxe geht. Wir nutzen mit dem alten Treiber quasi nur max. 30% dessen was möglich wäre - und selbst das nur wackelig. Das ist der Punkt. Was ich hier mache hat absolut nix mit "Magic" zu tun... das TWI in der MEGA32 ist mit 400KHz stabil vorgesehen, es gibt nichts was daran hindert das auch zu nutzen... ausser suboptimale Software. Das versuch ich mit meinen bescheidenen Mitteln zu beheben. Wirklich ärgerlich ist nur, das selbst die Codebeispiele von ATMEL selbst unvollständig sind. Scheinbar will man bei ATMEL die Lust aufs unbekannte wecken indem man nur ja nicht konkret wird. Und die TWI-State Engine von ATMEL wird bei Philips/NPX nun mal nicht genauer beschrieben... Das Problem ist nicht i2c, das Problem ist TWI. (Oder ich hab noch nicht die richtigen Stellen im Mega32-Datenblatt gefunden... will ich nicht ausschließen.) Sorry für's ausschweifen :)

Der Grund warum man in der twi_target einstellen soll was man möchte, ist schlicht und einfach das ich daran erkennen kann welcher Code Teil nicht gebraucht wird - und der wird dann entfernt. Entfernt wurde dabei eben auch der überflüssige Initaufruf für den Master da ja in der target Slave angegeben war. Also bei mir.. bei euch war es noch TWIMASTER was natürlich zu fehlendem Code führt. Sorry dafür. Hätte ich besser kommunizieren sollen.



Kurze Anregung als "normaler User":

Du bist ja da an einem dynamischen Prozeß dran, d.h. versuchst, in mehreren Schritten möglichst gute Lösungen zu finden. Dabei verändert sich auch die "Makrosprache" und der Funktionsumfang, der dem RP6 zur Nutzung des I2C-Bus mitgegeben wurde.
Wenn die entstehende Lib (ich bin da guter Hoffnung!) problemlos für alle zu nutzen sein soll, gibt es bei der Entwicklung eines neuen TWI-"Treibers" aus meiner Sicht auf der Makroebene nur 3 Möglichkeiten:
1. Man nutzt NUR die vorgegebenen Befehle und Makros, so dass vorhandene Programme mit der neuen Version kompiliert werden können.
2. Man ergänzt alle vorgegebenen Befehle und Makros durch neue Funktionen und Makros. Damit ist ebenfalls Abwärtskompatibilität möglich, es können dann aber neue Funktionalitäten zusätzlich genutzt werden.
3. Man verzichtet auf Kompatibilität und beschreibt einen neuen "Befehlssatz" für die TWI-Funktion.

Absolut richtig.
Weg 1 funktioniert nicht.
Weg 2 ist der goldene Mittelweg aber leider nicht immer möglich.
Weg 3 hört sich theoretisch gut an aber auch ich koche nur mit Wasser und habe nicht vor das Rad neu zu erfinden.



Mir persönlich liegt die Option 2. am Herzen, weil ich faul bin, und meine Programme nicht ändern möchte. Trotzdem würde ich gern von "Fortschritten" durch eine neue Lib profitieren (egoistisch wie ich bin ...).


Das sehe ich zu 100% wie Du. Siehe oben die defines. Letztlich wird es eine Mischung aus allen 3 Wegen wobei ich Weg 2 anvisiere. Es ist auch immer eine Frage der Abwägung von Aufwand und Nutzen. Ich sitze zwar jeden Tag lange hier dran aber es mus eben auch zum Prozessor und zum Rest der Libs passen.
Ich versuche jedenfalls die Änderungen klein zu halten und ändere nur da wo es wirklich Sinn macht oder Notwendig ist. Grundsätzliche Änderungen wären nur möglich wenn man die ganze RP6 Lib auf den Kopf stellt und dazu braucht man a. mehrere Leute um das in einem zeitlich sinnvollen Rahmen zu schaffen und es besteht b. glaube ich kaum eine echte Notwendigkeit da es nur ein (schönes!) Spielzeug und Experimentierplattform ist. Wenn auch eine sehr Interssante. Aber so lange mir die ESA kein Gehalt zahlt, tue ich auch nicht so als sei das hier ein Marsrover... :)



Also: Bitte nicht als Kritik, sondern nur als Anregung verstehen. :-k
Ich teste weiter ...


Das ist schön.
Nein ich verstehe das nicht als Kritik, nur meine Zeiten als Codewarrior sind schon etwas länger her und ich hab den RP6 erst seid 3 Wochen. AVR-Prozesoren sind mir auch fremd, ich komme eigentlich von 68000er CPUs (asm) die heute kaum einer noch kennt... und Linux von da her ists ne gewaltige Umstellung. Aber keine Sorge, ich werd euch keinen Linux Kernel schreiben :) auch wenns mich in den Fingern juckt :) Ich versuche euch an den Veränderungen Teil haben zu lassen und zu begründen und ihr habt natürlich die Möglichkeit darauf zu reagieren. Eine richtige Docu dazu fehlt ja auch noch aber das meiste ist im Programm zu erkennen.

Vielleicht erklärt das auch die RTOS Frage von mir an anderer Stelle des Forums etwas besser. :) Ich bin es gewohnt, in den Source zu schauen und nehme nicht alles als gegeben hin. (Was ich auch nicht als Kritik verstanden haben möchte) Na jedenfalls hab ich Hoffnung das es noch was wird mit dem TWI hier :) Es geht ja jetzt schon mehr als mit dem alten Treiber ohne das es riesengroß wird.
Ich bin übrigends dabei einen weiteren Fehler einzukreisen, welcher sich hier in meinem Hardwareaufbau eingeschlichen hat - der evtl. auch die Buserrors BEI MIR verursacht. Wäre möglich das ihr weniger bis keine habt... Hängt evtl. mit der Nutzung von E_INT1 zusammen. Der Lötkolben ist schon warm.... Ansonsten läuft die Lib schon ganz gut hab ich das Gefühl.

PS: mich wundert schon etwas, das sich noch niemand rangetraut hat um dem RP6 bzw. die Libs zu verbessern, schließlich sagt SlyD selbst das nicht alles optimal ist. Die TWI ist da nur ein, aber bestimmt nicht das einzige Beispiel. Und ich hab jetzt nicht den Eindruck, das sich SlyD guten Ideen und Umsetzungen gegenüber sperren würde. Ich rege mich jedenfalls mehr über ein störanfälliges TWI auf, als über laute Zahnräder ...

LG Rolf

Fabian E.
24.02.2011, 22:43
Schön, dass ihr heir so schöne Fortschritte macht!
So wie ich das jetzt verstanden habe, warte ich mit dem Testen noch bis du deine neue Version draußen hast? Scheinst ja bei dir noch einige Änderungen zu haben.

Aber auch von mir schonmal ein Kompliment ;) Ohne guten Debugger würde ich mich an sowas nicht rantrauen ;)

RolfD
24.02.2011, 23:40
@Fabian
ja warte besser bis morgen Nachmittag. Aber so viel wird sich dann erst mal nicht mehr ändern.

Zum Thema debuggen... da sag'ste was... ich hab noch gelernt Fehler zu suchen indem man mit nem Schraubendreher auf nen Bauteil kloppt... :) Aber nen 2.ter USB Adapter wär schon klasse damit ich nicht mehr ständig umstecken muss. Sonst arbeite ich mit dem gcc... bis ich mich in eine IDE eingefunden hab... puh... aber da freue ich mich schon auf die M128 Unit. :)
LG Rolf

Fabian E.
24.02.2011, 23:47
Ja, da klappt das tatsächlich ganz gut. Zumindest wenn du die UART Schnittstelle nicht debuggen willst... das ist nämlich nicht so ganz einfach ;-)

RolfD
25.02.2011, 00:09
*breitgrins....

RolfD
25.02.2011, 11:08
So.. der Fehler mit hängen bleiben lang tatsächlich an E_INT1.
Kurze Beschreibung dazu: Ich habe eine pcf8583 Uhr am i2c hängen dessen INT Leitung am E_INT1 der Base liegt. Dies ist ansich ein harmloser ADC-Port. Ich möchte deren Alarmfunktion später mal nutzen.
Die Uhr gibt wenn das Register 0 mit 0 beschrieben ist, pro Sek ein Impuls aus. Ich hatte dem Baustein das die Tage abgewöhnt aber irgend ein rumflitzendes Byte auf dem i2c bus hat es zurück gesetzt. Die Base juckt das nicht weiter, für sie ist das nur ein normaler Port an dem jemand klopft. Die Leitung ist aber auch über den XBUS an den INT1 der M32 geführt was ein echter Interrupt ist - und ich nicht bedacht hatte. Dem zu Folge löste die M32 pro Sek ein irq aus wo der Master drauf läuft. Die INT Leitung hat eine höhere Priorität als TWI - daher wurde die TWI immer wieder unterbrochen was zu Timingproblemen und letztlich zum Abbruch des Datenflusses führte.

Für euch interssant ist eigentlich die Info, das wenn man den E_INT1 z.B. als Ausgang nutzt - warum auch immer - ihr damit die M32 quasi ständig in eine ISR schickt. Ich finde das sogar sehr gut durchdacht vom Entwickler... nur darf man das halt nicht vergessen wenns um die Leitung geht. Die E_INT1 ist als Steuerleitung Tabu wenn man eine M32 oder vermutlich auch M128 nutzt. Eigentlich logisch aber man denkt eben nicht immer dran.

Nun zum TWI. Ich hab euch meine aktuellen Files zum Projekt bzw. Dirks modifiziertes Testprogramm zusammen mit der TWI in ein Zip gepackt. Damit können jetzt auch mehr Leute mal versuchen wie es klappt. Achtet bitte auf Pfade und Abhänigkeiten im makefile und kommentiert die defines zum lcd und das lcd.h aus wenn ihr kein i2c lcd mit pcf Baustein habt. Benötigt zum testen mit Dirks Programm werden die Base, eine M32 sowie ein M32Display.

I2CTWI_isBusy() (bit_is_set(TWI_statusReg,STR_isr))
#define I2CTWI_needAction() (bit_is_set(TWI_statusReg,STR_needAction) && bit_is_clear(TWI_statusReg,STR_isr))
#define I2CTWI_waitAction() (bitset(TWI_statusReg,STR_needAction))
#define I2CTWI_doneAction() (bitclr(TWI_statusReg,STR_needAction))
#define I2CTWI_NACKonAction() bitset(TWI_statusReg,STR_NACKonAction)
#define I2CTWI_ACKonAction() bitclr(TWI_statusReg,STR_NACKonAction);bitclr(TWI_ statusReg,STR_needAction)

Das hier sind Befehle, die für die Ablaufsteuerung relevant sind. Sie fragen oder setzen alle Flags ab die die ISR steuert.

I2CTWI_isBusy() ist gesetzt wenn die ISR ein Datagram überträgt. Egal ob als Master oder Slave, vom Start bis zum Stop.
Achtung, die Befehle
I2CTWI_requestRegisterFromDevice
und
I2CTWI_readRegisters
bestehen je aus 2 Einzelbefehlen in denen die ISR zwischendurch "Frei" meldet was zur Ausführung nachfolgend wartender TWI Befehle führen kann. Dafür muss ich noch eine Lösung stricken. Alle anderen TWI-Befehle sind Einzelbefehle, da sollte das Problem nicht auftauchen.

I2CTWI_needAction() prüft ob die Slave Register beschrieben wurden was mit einem gesetzten Bit angezeigt wird. Wenn ja, sollte der Slave als Zeichen das er die Daten verarbeitet hat immer I2CTWI_doneAction() ausführen was das Bit zurück setzt. Der Master hat dazwischen keine Schreibmöglichkeit und wird geblockt (was der natürlich auch über I2CTWI_isBusy() merken kann). Will man den Master ohne vorherige Schreiboperation zwingen zu warten (was eigentlich nur für mich zum testen relevant ist) nutzt man I2CTWI_waitAction() was das schreiben der Register simuliert. Soll der Master immer schreiben können (Verhalten wie bisher, führt zum überschreiben von Registern) nutzt man I2CTWI_ACKonAction(), mit I2CTWI_NACKonAction() blockt der Slave wieder das überschreiben.
Das Blocken geschiet durch senden von NACK wäred der Master ein Datenbyte auf ein bereits beschriebenes Registerset schreiben will, was wiederum mit einen neuen Anlauf der Übertragung mündet bis er erfolgreich war. Quasi eine Endlosschleife. Das Beschreiben des Adressbytes müsste aber noch gehen so lange der Master keine Daten mitsendet. Zur Not könnte der Master also auch noch darüber dem Slave was mitteilen. Morsen sozusagen... Vielleicht eine Möglichkeit für weitere Master zu signalisieren, wenn sie Daten haben und der Slave gefälligst die Register frei zu geben hat.... aber das ist dann schon leicht abgedreht und scheitert noch an fehlender Busarbitierung :). Derzeit ist der Bus belegt wenn der Slave nackt und ein 2.ter Master müsste den ersten von der Leitung schubsen um reden zu dürfen, wobei sich der erste vermutlich dann gehörig verschluckt. Bildlich gesprochen...

I2CTWI_waitAction() werde ich vermutlich wieder entfernen da es für euch kaum Sinn macht. I2CTWI_(N)ACKonAction() wird bleiben.

Die Variablen

I2CTWI_genCallCMD
I2CTWI_dataWasRead
I2CTWI_dataReadFromReg
I2CTWI_readBusy
I2CTWI_writeBusy

sind obsolet, I2CTWI_genCallCMD tut es eh nicht weil gencalls derzeit garnicht gehen, die anderen 4 Vars machen quasi das gleiche wie I2CTWI_isBusy() und I2CTWI_needAction() mit der Ausnahme das sie nun auch zu gebrauchen sind und den Ablauf am TWI beeinflussen.

Konfiguriert wir die Anwendung mit twi_target.h, sie ist selbsterklärend, jedes Projekt hat sein eigenes File. Es gelten die üblichen Vorgaben für TWI.

Flags wie DEBUG, XDEBUG können kommentiert wreden (//) dabei bedeutet:
TWISLAVE=Slave & Master
TWIMASTER=Master
TWILOW=(Slave & Master | Master) & TWILOW
Ich glaube, es geht nichts kaputt wenn man TWISLAVE und TWIMASTER setzt aber es ist doppelt gemoppelt. TWILOW sind Bascom ähnliche Funktionen die - wenn genutzt - das Handling auf dem TWI erlauben ohne das die ISR dazwischen funkt. Dazu müssen die Funktionen ein paar Dinge berücksichtigen, insbesondere das Ausschalten des TWI Interrupts und das Einschalten wenn man fertig ist. Gedacht ist das zum debuggen eigener Devices/Hardware, man kann sich natürlich auch kompexe Funktionen daraus basteln die mit der ISR koexistieren. Damit ist jedoch kaum Slave Betrieb möglich oder man müsste wie blöde TWI-Register pollen, daher nur einfache Masterfunktion.

Im Base Programm sitzt noch ein vagabundierender extINT Befehl, der kann/muss raus... und einige Kommentare wo ich am basteln war.. kann auch raus.

Ich bin mir noch nicht sicher wie die TWI auf Busarbitation reagiert, grundsätzlich können und dürfen am Bus mehrere Master laufen. Evtl. stürtzt aber die aktuelle TWI ab bzw. baut Mist. Aber es geht nichts kaputt.
Wer möchte, kann also Dirks Programm so umbauen das auf beiden Prozessoren Master und Slave laufen und die sich gegenseitig Daten zuwerfen und abfragen.... schlimmstenfalls bleiben die Programme nur nach einigen Bytes hängen. Nur hätte ich den Source dann bitte gern am Montag da ich dann auch die Arbitation in Angriff nehmen kann. :) Sonst muss ich mir das erst selbst schreiben.

Das sind mal grob alle wichtigen Infos zusammen gepackt.
Sonst schaut auch mal in die .h und .c files ... die beissen nicht...
Der compilerlauf mit gcc klappt hier, wenn was nicht klappt, bitte zuerst in der twi_target prüfen. Dirks Programm testet alle 4 Teilfunktionen der TWI, also MR/MT/SR/ST und sollte damit auch in eigenen Projeken funktionieren. Alles weitere findet sich auch im Thread hier.
Ich hab am WE keine Zeit und werde mich erst Montag wieder intensiv darum kümmern, ihr habt also die Möglichkeit damit zu spielen bevor ich wieder alles auf den Kopf stell. :) Es werden noch weitere Veränderungen und Betatests kommen, insbesondere Fabian sollte mit der Lösung und Remotrol aber schon arbeiten können. Sonst fragt einfach hier, ich werde sicher mal reinschauen am WE.

LG Rolf

Fabian E.
25.02.2011, 11:21
Cool, dann habe ich ja was zu tun. =)
Das ist mit dem E_INT1 ist ja genau für den Interrupt gedacht, der RP6 muss ja quasi Interupts an die M32 schicken können.
Aber wenn man das vergisst, kann das natürlich ganz schön verwirrend sein. =)

Ich hatte da aber auch schon Probleme mit, zwar nicht bei der M32 sondern bei der M128... Da ist ein Interuptpin hinüber... Zum Glück kann man das da ja auf einen anderen umstecken.

Dirk
25.02.2011, 16:42
@Rolf:


... ich komme eigentlich von 68000er CPUs (asm) die heute kaum einer noch kennt...
Doch, ich schon. Meine ersten ASM-Versuche waren mit dem 6502, da habe ich auch noch an "Treibern" (z.B. Druckerspooler, ich glaube für den 6510) gearbeitet, dann aber in ASM nur noch bis zum 8086 mithalten können.


... mich wundert schon etwas, das sich noch niemand rangetraut hat um dem RP6 bzw. die Libs zu verbessern, schließlich sagt SlyD selbst das nicht alles optimal ist.
Naja, da gab es z.B. von mir schon über die Jahre Libs und Verbesserungen vorhandener Libs.
Kurze unvollständige Liste: M32-Lib-Version 1.3beta, M32-EEPROM-Zugriff, M32/Base/M128-Clock-Lib und DCF-Lib, M32/Base-Servo-Lib, M32/M128-TRX433-Lib, M32/Base-Uart_2-Lib


... aber da freue ich mich schon auf die M128 Unit.
Ja, wenn du die in die Finger kriegst, geht es hier wohl erst richtig ab. \:D/


Die E_INT1 ist als Steuerleitung Tabu wenn man eine M32 oder vermutlich auch M128 nutzt.
Ja, das ist so und gilt auch für die M128: Dort ist E_INT1 der Base (und INT0 (PD2) der M32) mit INT5 (PE5) verbunden, also auch mit einem interruptfähigen Eingang.

Gruß Dirk

Dirk
26.02.2011, 15:53
@Rolf:
So, ich habe jetzt die neue Version getestet und die Bytes herumsausen lassen.
Ergebnis: Es gibt keine Probleme.

Ich habe dann im Base-Programm noch die task_RP6System() aufgenommen, um eine realistische Umgebung zu haben.
Ergebnis: Keine Probleme.

Dann habe ich noch im M32-Programm die task_RP6M32System() aufgenommen, die es aber nur in meiner Version 1.3beta der RP6ControlLib gibt.
Ergebnis: Auch keine Probleme.

Fragen eines kleinen Testers:
1. Die Funktionen I2CTWI_needAction() und I2CTWI_doneAction() umschließen ja den ganzen Komplex der I2C-Kommunikation. Im Grunde will ja der Nutzer des I2C-Busses ja nur Bytes senden oder empfangen, genau wie bei der Uart-Lib. Ihm ist es wohl eher egal, ob TWI eine "action" braucht oder ob die zuende ist. Daher meine (vorsichtige) Frage, ob man das auf der Befehlsebene wirklich braucht? Müßte nicht der Treiber selbst entscheiden, ob er eine action (bzw. Busaktivitäten) durchführen muss oder wann er damit fertig ist? Wie gesagt: Vielleicht verstehe ich auch nicht die Intention. Eigentlich brauche ich auf der Befehlsebene nur Schreib-/Lesebefehle und evtl. ein oder 2 Flags, ob der Bus wieder frei ist. Sonst will ich als Nutzer eigentlich nicht unbedingt wissen, was sich im Busprotokoll tut.

2.
Wer möchte, kann also Dirks Programm so umbauen das auf beiden Prozessoren Master und Slave laufen und die sich gegenseitig Daten zuwerfen und abfragen...
Oh,- da bräuchte ich von dir Anregungen, wie das zu initialisieren wäre ... Dann mache ich da gern was draus ...

Gruß Dirk

RolfD
26.02.2011, 18:52
6502 ? nettes Teil... auch einer der mit ner Brotkiste angefangen hat? :) Mein erster war ein ZX81 :) Hach ja die alten Zeiten ... :)

Aber zur TWI. Ich war unzufrieden, das der IRQ mir die Daten auf dem i2c zerbröselte - auf Grund der Sache mit der Uhr - und hab eine Lösung gefunden. Einfach aber effektiv. Die ISR wird zukünftig (steuerbar über twi_target.h) im Eingang ein cli(); ausführen und im Ausgang ein sei(); , das verhindert nach nun 24h lang laufenden Dauertests absolut zuverlässig die Unterbrechung und Abbruch trotz sekündlichen Uhrenticks. Wird in der nächsten Release drin sein.

Dann freut es mich erst mal sehr, das Du reproduzierbar stabile Funktion berichten kannst. Zu Deiner Frage, nun wenn Du dich nicht um das "Protokoll" kümmern willst, kannst Du mit I2CTWI_ACKonAction() kurz nach der Initialisierung das so einstellen, das der Master nicht behindert wird.

I2CTWI_init()
I2CTWI_ACKonAction();
...
Programmlauf
...

Wenn aber dein Programm auf der Base sowas wie ein Handshake braucht, (Fabians Remotrol braucht das z.B. zwingend) oder verhindert werden muss das der Master Daten überschreibt (weil Befehlssequenzen übertragen werden, kommst Du um eine Verarbeitung der Daten/Befehle und letztlich auch einem Handshake.. nicht rum. Da bietet sich die default Einstellung bzw. I2CTWI_NACKonAction(); und dann endsprechendes bearbeiten mit I2CTWI_needAction() und I2CTWI_doneAction() an.

Es kommt also ein wenig auf deine Anforderungen an das Programm an, es gibt keine ja/nein Antwort dazu. Man kann überlegen, die Defaulteinstellung auf I2CTWI_ACKonAction zu setzen was dem alten Verhalten entspricht, ohne ein Handshake ist allerdings eine gesicherte Datenübertragung nicht möglich. Vergleich es mit den RTS/CTS Leitungen und dem Software Protokoll XON/XOFF, es würde auch keiner eine stabile UART ohne die Leitungen betreiben können und wenn doch, so muss zumindest das Softwareprotokoll XON/XOFF vorhanden sein. I2C hat kein RTS/CTS bzw. XON/XOFF, also braucht es da was anderes, die ACK/NACK Signale sind quasi eine Mischumg aus beidem und ohne die klappt so gut wie nix wirklich sicher auf dem Bus - der alte Treiber welcher alles ACKte, ist bester Beleg dazu. Natürlich muss man sich als Programm... nein.. man muss nicht, man kann... sich drum kümmern... und sicher stellen das die Daten korrekt verarbeitet werden. Aber es gibt auch Anwendungen wo das nicht so wichtig ist. Das entscheidest letztlich Du als Programmierer bzw. die Aufgabenstellung.

Man kann grundsätzlich das Ganze in ein Task packen und mit einem
If I2CTWI_needAction() task_do_i2c();
erledigen, wo dann halt in task_do_i2c alles verarbeitet wird. Incl. einem I2CTWI_doneAction(); am Taskende. Die Libs und Examples liefern dazu reichlich Beispiele. Das hatte ich in deinem Programm auch schon im Kommentar irgendwo angemerkt. Ich werde dazu am Montag ein kleines Beispiel bauen, denke aber das ihr da jetzt schon mit klar kommt.

Eigentlich reicht für das genannte Programm also:

while (true)
{
die_üblichen_System_Tasks();
If I2CTWI_needAction() task_do_i2c(); //Receiver bedienen (Daten kopieren wie im Slavebeispiel)
Master_task();//Selber aktiv werden, so wie in dem Masterbeispiel
}

Das ist echt nicht kompliziert, Du hast bereits alles geschrieben da was wir da brauchen...
LG Rolf

PS: Ich hab noch was für Dich Dirk, extra eben noch geknipst :) Du wirst sie erkennen, für die anderen: Eine 6502 CPU von MOS aus dem Jahr 1983 aus meiner Grabbelkiste. Hier noch bissel Text dazu. http://de.wikipedia.org/wiki/MOS_Technology_6502
Die 6509 war ein direkter Verwandter und wurde im CBM610 z.B. verbaut. http://www.zock.com/8-Bit/D_CBM610.HTML Ich hatte auch mal einen. Und nicht zu vergessen die allseits berühmte 6510 CPU aus dem C64 und die 6508 aus dem NES :) Eine Z80 CPU aus der Generation hab ich auch noch, und hüte sie wie Dagobert Duck sein ersten Taler :)

EDIT: Ich hab über die Handshake Geschichte nachgedacht und stelle fest, das es da noch einiges zu verbessern gibt. Z.B. kann der Masterzugriff bisher nicht beeinflussen was bei einem NACK passiert, bzw. ist einem hängendem Slave quasi ausgeliefert. Das ist unschön und wird demnächst behoben. Beide Seiten werden also steuern können, ob und wie sie mit NACKS umgehen. Dazu wird die ISR ein weiters Flag bekommen, das mit 1 oder 2 Funktionen/Macros steuerbar ist.

Dirk
27.02.2011, 18:28
@Rolf:

Hier 'mal meine Testprogramme, die (erwartungsgemäß) nicht funktionieren. Das "erwartungsgemäß" hat damit zu tun, dass ich nicht wirklich verstanden hatte (SORRY!), wie auf beiden Systemen Master- und Slavefunktionen zu initialisieren sind.
Ich bin sicher, du kannst da rasch weiter helfen.

Meine Idee war, in einer Richtung einen Aufwärtszähler laufen zu lassen, in die andere Richtung einen Abwärtszähler. Abwechselnd würden dann beide Systeme den Master machen. Auf dem LCD der M32 wäre die eine Zeile für die (eigene) Masterfunktion, die andere für die Gegenrichtung, für die Base würden die Textausgaben über Uart mit "M-" für die eine und "S-" für die andere Richtung ausgegeben. Vielleicht habe ich mir das Ganze auch zu naiv so vorgestellt.
In der twi_target.h habe ich beide Systeme als TWISLAVE definiert (richtig?).
Als TWI-Adresse für die M32 nehme ich 12 dezimal (NICHT hex!). An der Base habe ich kein I2C-LCD.

Testprogramm 3 (RP6Control M32):

/*
* ************************************************** **************************
* RP6 ROBOT SYSTEM - RP6 CONTROL M32 EXAMPLES
* ************************************************** **************************
* Example: I2C Master/Slave - I2C Send/Receive Test 3
* Author(s): Dirk
* ************************************************** **************************
* Description:
*
* This Example sends/receives data via I2C.
*
*
* ################################################## ##########################
* The Robot does NOT move in this example! You can simply put it on a table
* next to your PC and you should connect it to the PC via the USB Interface!
* ################################################## ##########################
* ************************************************** **************************
*/

/************************************************** ***************************/
// Includes:

#include "twi_target.h" // Include the new I2C-Bus Library

#include "RP6ControlLib.h" // The RP6 Control Library.
// Always needs

to be included!

/************************************************** ***************************/
// Defines:

// The Slave Address of the RP6Base on the I2C Bus can be specified here:
#define RP6BASE_I2C_SLAVE_ADR 10

#define CMD_SHOW_DATA 99 // Command to display the data

#define TEXT_OUTPUT // Show text

/************************************************** ***************************/
// I2C Event handlers:

/**
* This function gets called automatically if there was an I2C Error like
* the slave sent a "not acknowledge" (NACK, error codes e.g. 0x20 or 0x30).
* The most common mistakes are:
* - using the wrong address for the slave
* - slave not active or not connected to the I2C-Bus
* - too fast requests for a slower slave
* Be sure to check this if you get I2C errors!
*/
void I2C_transmissionError(uint8_t errorState)
{
writeString_P("\nI2C ERROR --> TWI STATE IS: 0x");
writeInteger(errorState, HEX);
writeChar('\n');
}

/************************************************** ***************************/
// Variables:

uint8_t buffer[4];
uint16_t error_cnt = 0;
uint8_t controlbyte;
uint8_t highbyte = 0;
uint8_t lowbyte = 0;

/************************************************** ***************************/
// Functions:

void task_Slave(void)
{
static uint16_t cnt;
if I2CTWI_needAction() {
controlbyte = I2CTWI_writeRegisters[0]; // Read

control byte
I2CTWI_writeRegisters[0] = 0;

// and reset it (!!!)
lowbyte = I2CTWI_writeRegisters[1];

// Read lowbyte
highbyte = I2CTWI_writeRegisters[2]; // Read

highbyte

I2CTWI_readableRegisters[0] = controlbyte; // copy

controlbyte
I2CTWI_readableRegisters[1] = lowbyte; // copy

lowbyte
I2CTWI_readableRegisters[2] = highbyte; // copy

highbyte

cnt = ((highbyte << 8) | lowbyte);

// Restore 16-bit value
#ifdef TEXT_OUTPUT
setCursorPosLCD(1, 0);
writeStringLCD_P("S-Rcved ");

// What was received?
writeIntegerLCD(cnt, DEC);
writeStringLCD_P(" ");
#endif
if (!cnt) error_cnt = 0;

// Reset error counter
else error_cnt++;

// Error counter + 1
if (cnt != error_cnt) {
setCursorPosLCD(1, 0);
writeStringLCD_P("S-ERROR!");
error_cnt = cnt;
}
else {
setCursorPosLCD(1, 0);
writeStringLCD_P(" ");
}
controlbyte = 0;
I2CTWI_doneAction();
}
}

void task_Master(void)
{
static uint16_t cnt = 32768;
if (!I2CTWI_isBusy()) {
cnt--;

// TEST: COUNTER!!!
if (cnt > 32767) cnt = 32767;
buffer[0] = 0;
buffer[1] = CMD_SHOW_DATA;
buffer[2] = cnt;
buffer[3] = (cnt >> 8);
I2CTWI_transmitBytes(RP6BASE_I2C_SLAVE_ADR, buffer, 4);

buffer[0] = 0;

// reset buffer
buffer[1] = 0;
buffer[2] = 0;
buffer[3] = 0;

I2CTWI_readRegisters(RP6BASE_I2C_SLAVE_ADR, 0, buffer, 4);

if (cnt == ((buffer[2] << 8) | buffer[1])) {
#ifdef TEXT_OUTPUT
setCursorPosLCD(0, 0);
writeStringLCD_P("M-Moved "); // What

was sent?
writeIntegerLCD(cnt, DEC);
writeStringLCD_P(" ");
#endif
}
else {
setCursorPosLCD(0, 0);
writeStringLCD_P("M-ERROR! "); // What

was sent?
}
// if (cnt==3) while(true);

// countertrap
}
}

/************************************************** ***************************/
// Main - The program starts here:

int main(void)
{
initRP6Control();

initLCD();

// I2CTWI_init(100); // Initialize the TWI Module for Master

operation
// with 100kHz SCL

Frequency

// Register the event handlers:
I2CTWI_setTransmissionErrorHandler(I2C_transmissio nError);

setLEDs(0b1111);
mSleep(500);
setLEDs(0b0000);

while(true)
{
task_Slave();
task_Master();
if (RP6LIB_VERSION == 15) {

// Only for
task_RP6M32System();

// Lib V1.3beta!!!
}
else mSleep(5);

// Other Libs: 5ms
}
return 0;
}

/************************************************** ****************************
* Additional info
* ************************************************** **************************
* Changelog:
* - v. 1.0 (initial release) 27.02.2011 by Dirk
*
* ************************************************** **************************
*/

/************************************************** ***************************/



Testprogramm 3 (RP6Base):

/*
* ************************************************** **************************
* RP6 ROBOT SYSTEM - ROBOT BASE TESTS
* ************************************************** **************************
* Example: I2C MaSTER/Slave - I2C Send/Receive Test 3
* Author(s): Dirk
* ************************************************** **************************
* Description:
*
* This Example sends/receives data via I2C.
*
*
* ################################################## ##########################
* The Robot does NOT move in this example! You can simply put it on a table
* next to your PC and you should connect it to the PC via the USB Interface!
* ################################################## ##########################
* ************************************************** **************************
*/

/************************************************** ***************************/
// Includes:

#include "twi_target.h" // Include the new I2C-Bus Library

//#include "PCF8583.h"

//#include "RP6RobotBaseLib.h" // The RP6 Robot Base Library.
// Always needs

to be included!
/************************************************** ***************************/
// Defines:

// The Slave Address of the Control M32 on the I2C Bus can be specified here:
#define RP6CONTROL_I2C_SLAVE_ADR 12

#define CMD_SHOW_DATA 99 // Command to display the data

#define TEXT_OUTPUT // Show text

/************************************************** ***************************/
// Variables:

uint8_t buffer[4];
uint16_t error_cnt = 0;
uint8_t controlbyte;
uint8_t highbyte = 0;
uint8_t lowbyte = 0;

/************************************************** ***************************/
// Functions:

void task_Slave(void)
{
static uint16_t cnt;
if I2CTWI_needAction() {
controlbyte = I2CTWI_writeRegisters[0]; // Read

control byte
I2CTWI_writeRegisters[0] = 0;

// and reset it (!!!)
lowbyte = I2CTWI_writeRegisters[1];

// Read lowbyte
highbyte = I2CTWI_writeRegisters[2]; // Read

highbyte

I2CTWI_readableRegisters[0] = controlbyte; // copy

controlbyte
I2CTWI_readableRegisters[1] = lowbyte; // copy

lowbyte
I2CTWI_readableRegisters[2] = highbyte; // copy

highbyte

cnt = ((highbyte << 8) | lowbyte);

// Restore 16-bit value
#ifdef TEXT_OUTPUT
writeString_P("S-Received: ");

// and show it
writeInteger(cnt, DEC);
writeChar('\n');
#endif
if (!cnt) error_cnt = 0;

// Reset error counter
else error_cnt--;

// Error counter - 1
if (cnt != error_cnt) {
writeString_P("S-ERROR!\n");
error_cnt = cnt;
}
controlbyte = 0;
I2CTWI_doneAction();
}
}

void task_Master(void)
{
static uint16_t cnt = 0;
if (!I2CTWI_isBusy()) {
cnt++;

// TEST: COUNTER!!!
if (cnt > 32767) cnt = 0;
buffer[0] = 0;
buffer[1] = CMD_SHOW_DATA;
buffer[2] = cnt;
buffer[3] = (cnt >> 8);
I2CTWI_transmitBytes(RP6CONTROL_I2C_SLAVE_ADR, buffer, 4);

buffer[0] = 0;

// reset buffer
buffer[1] = 0;
buffer[2] = 0;
buffer[3] = 0;

I2CTWI_readRegisters(RP6CONTROL_I2C_SLAVE_ADR, 0, buffer, 4);

if (cnt == ((buffer[2] << 8) | buffer[1])) {
#ifdef TEXT_OUTPUT
writeString_P("M-Moved: ");
writeInteger(cnt, DEC);
writeChar('\n');
#endif
}
else {
writeString_P("M-ERROR!\n");
}
// if (cnt==3) while(true);

// countertrap
}
}

/************************************************** ***************************/
// Main function - The program starts here:

int main(void)
{
extIntOFF();

initRobotBase();
// initLCD();

setLEDs(0b111111);
mSleep(500);
setLEDs(0b000000);

// I2CTWI_init(RP6BASE_I2C_SLAVE_ADR);

// clearI2CLCD();
// setCursorPosLCD(0,0);
// writeStringLCD("I2C Init "); writeIntegerLCD(1,DEC);
//---------------------------------------

/*
writeString("START ----------------------------------\n");



uint8_t results[15];

I2CTWI_transmitByte(PCF8583_ADDR_WRITE, 0);

I2CTWI_readBytes(PCF8583_ADDR_READ, results, 7);

*/

//---------------------------------------
//while (true);



while (true)
{
task_Slave();
task_Master();
task_RP6System();
}
return 0;
}

/************************************************** ****************************
* Additional info
* ************************************************** **************************
* Changelog:
* - v. 1.0 (initial release) 27.02.2011 by Dirk
*
* ************************************************** **************************
*/

/************************************************** ***************************/

Vielleicht kannst du ja daraus was machen. Ich brauche auf jeden Fall noch ein paar Anregungen.

Gruß Dirk

P.S.: Oh, ja, ich habe auch noch eine 6502 und eine 6510 liegen. Ich gehe mal in den Keller,- suchen ...

RolfD
27.02.2011, 18:54
Hallo Dirk, ich schau gleich rein, hab jetzt wieder Zeit. Ja so in etwa dachte ich mir das. Mal genauer hinsehen wo es da klemmt...

Aber vorweg...
Initialisiert werden hier beide Seiten im Programm mit I2CTWI_initSlave(wert_egal); zumindest in der Version die du jetzt noch hast. Die Werte für Speed und Adresse müssten aus dem jeweiligen twi_target.h kommen, je 100khz und base dez.10, m32 dez.12. für die EIGENE Adresse, den jeweils anderen 10->12 und 12->10 muss man im Programm als Ziel in den Funktionen mitgeben. Das reicht eigentlich. Ach ja.. in beiden twi_targets muss natürlich TWISLAVE aktiv sein weil beide Seiten Registersets bereit stellen sollen. Und die beiden Tandems dürfen sich natürlich nicht gegenseitig in die Register schreiben oder sonst wie ins Gehege kommen.. also z.B. einen Master/Slave Verbund auf der Base in die Register 0(1) bis 3(4) packen wie bisher, den anderen neuen Master/Slave Verbund mit Slave auf der M32 in die Register z.b. ab 8(9)... (die Klammern jedes mal für den Master weil der ja erst ab reg, 1 liest.)
Eigentlich kann man auf beiden Seiten die regs 0-3 nutzen aber damit man sich nicht verhaspelt würd ich das erst mal logisch in 2 Bereiche trennen, später wenns klappt und verstanden ist müsste man es auch runter setzen können. <- Die Aussage is glaube ich Quark, es is recht eindeutig was wo hin geht.. sorry..

In folgenden Versionen wird es kein differenziertes Master/Slave Init mehr geben, und ist nur aus Kompatibilität (wie gepostet) noch vorhanden, wird aber von meinem .h File auf eine gemeinsame Init Funktion per Macro umgebogen.

Damit sollten erst mal die Unklarheiten beseitigt sein, warum das Programm klemmt, da schau ich nun und Poste es wohl als Edit.

EDIT:
Ja zum Init sagte ich schon, in beiden:
// die Funktion holt sich die Addresse aus twi_target.h
I2CTWI_initSlave(42); // parameter egal
Aber da klemmt noch mehr... erst mal gucken ob die Einzeln gehen... also in der Base
// task_Master();
und im M32
// task_Slave();
das ist ja die alte laufende Konstellation...

Der extIntOFF(); Befehl ist auch noch in der Base.. wir sprachen drüber wie ungeschickt es wäre den, E_INT1 als Ausgang zu steuern :) Hatte ich extra noch gesagt das der raus muss :) Aber auch das ist noch nicht der Fehler...

LG Rolf

RolfD
28.02.2011, 03:47
Habt ihr schon mal davon gehört, das die Pullups duchbrennen???

Ich hab seid stunden Probleme mit dem i2c und such mir hier nen Wolf, nachdem ich mein Oszi angeschlossen hab sah ich etwas seltsame Pegel, darauf hin hab ich R32 und R33 durchgemessen und siehe da.. R32 ist hochohmig, bei R33 finde ich 4,62 KOhm. Von den Pins SDA und SCL sollte ich laut Schaltplan einen Widerstand von 4k7 gegen Vdd messen können... sowie zwischen den Widerstandsanschlüssen gegen vdd 0 Ohm und andere Seite zu scl/sda auch 0 Ohm... alles so wie es sein soll.. aber der R32 ist und bleibt futsch...eine kalte Lötstelle schließe ich optisch aus.

Ich bin etwas ... naja ... platt !
Das Problem tritt erst seid dem 24h i2c-Dauertest auf, ich habe in der Zeit nichts am RP6 gemacht. Ich hoffe der Prozessor hat nichts abbekommen, den Widerstand kann ich ersetzen...

LG Rolf

SlyD
28.02.2011, 13:04
Hallo,

das ist höchst seltsam. überlasten/durchbrennen kann so ein 4K7 Widerstand in der Schaltung eigentlich nicht - selbst dann nicht wenn man den dauerhaft direkt an den Akku klemmen würde.

Das einzige was ich mir vorstellen könnte, wäre ein Materialfehler im Widerstand, beim Lötprozess dann weiter gestresst und dann irgendwann gebrochen.

Sind aber zum Glück alles sehr große 0805er Bauteile, daher problemlos auszutauschen.
Wenns doch Probleme geben sollte kannst Du mich gerne per Mail kontaktieren.

MfG,
SlyD

RolfD
28.02.2011, 14:15
Also ich kann euch beruhigen, die komischen Flanken lagen an dem PCF Uhrenmodul was ich vor einiger Zeit eingebaut hatte. Ich habe eben mit etwas mehr System und Sorgfalt (und Schlaf, ich war gestern Nacht zu k.o.) alles noch mal durchgemessen und den R32 vorsichtshalber ersetzt, und das verlötete Uhrenmodul abgetrennt, es ist alles wieder so wie es sein soll. Der R32 kann eigentlich nicht durchgebrannt sein wie SlyD schon sagte, dem stimme ich auch voll zu, der R hatte aber wohl trotzdem nen Schlag weg. Kann passieren. Das Uhrenmodul ist aber definitiv nicht in Ordnung obwohl ich da zig mal die Zeit mit auslesen konnte. Ich werde nun ohne das Uhrenmoul weiter testen und bedanke mich vor allem für SlyDs Angebot zu helfen. Da nun alles wieder ok ist, kann ich auch mit dem Treiber weiter machen. Jetzt hab ich nur dummer Weise kein unabhänigen Slave mehr, den ich auslesen kann. Mal sehen... vielleicht kann ich den pcf8574 am i2c Display verwenden..., der tut es.
LG Rolf

EDIT:
@All
@ Dirk, deine Programme die Masterfunktion nutzen... müssen natürlich auch task_I2CTWI(); aufrufen. Sonst bleibt der Transport hängen... aber ich bin schon wieder so viel am umstellen, wartet lieber auf die nächste Beta.

Dirk
01.03.2011, 16:14
Dirk, deine Programme die Masterfunktion nutzen... müssen natürlich auch task_I2CTWI(); aufrufen. Sonst bleibt der Transport hängen... aber ich bin schon wieder so viel am umstellen, wartet lieber auf die nächste Beta.
Ok, hätte ich merken können.

Das mit deinem Pullup an SDA ist ja echt merkwürdig! Naja, hast du ja gut gelöst!

Also: Ich warte geduldig auf die nächste Version ... :Ostern :Weihnacht

Gruß Dirk

RolfD
02.03.2011, 15:09
Ich hab mir scheinbar doch was am SDA Pin der CPU zerstört.
Hier eine kurze Diganoseanleitung, alles natürlich OHNE angebaute Teile am I2C Bus mit dem Oszi und einem Multimeter geprüft!

SDA verhält sich so als wenn kein Pullup (R32) da wäre, der ist aber gewechselt und mehrfach getetet. Darauf hin habe ich die Ports SCL und SDA als Ausgang geschaltet und volle Rechteckflanken gemessen, schaute also gut aus. Als Ausgang ist das ok. Hier der Code dazu.

while (true) {
DDRC |= (SCL | SDA);
PORTC |= SCL;
PORTC |= SDA;
mSleep(100);
PORTC &= ~SCL;
PORTC &= ~SDA;
mSleep(100);
}

Dann SDA und SCL als Eingang geschaltet und den Prozessor die Werte ansagen lassen.


PORTC &= ~SCL;
PORTC &= ~SDA;
DDRC &= ~SDA;
DDRC &= ~SCL;
while (true) {
writeString_P("SCL / SDA -> ");
writeInteger(PINC & SCL , BIN);writeString_P(" / ");
writeInteger(PINC & SDA , BIN);writeChar('\n');
mSleep(500);
}

Ergebnis war, das beide Pins zunächst logisch 1 hatten, der SDA aber manchmal auf 0 kippte. Mit einer Brücke konnte ich beide Pins gegen GND auf 0 ziehen, was ok ist, SCL sprang danach sofort auf 1. SDA kippelte zwischen 0 und 1. Gemessen habe ich am SDA dabei 1,4v statt der erwarteten 4v wie am SCL. Das erklärt das kippeln, ein Verhalten wie bei fehlendem R32 nur das dann 0,7 statt 1,4v zu messen sind.

Also noch mal R32 ausgebaut, gemessen, anderen besorgt, eingebaut... immer das gleiche. SlyD gab mir dann den Tip, mal einen Diodentest zu machen.


Was misst Du mit dem Multimeter im Diodentest (gegen GND und VDD bei beiden I2C Pins in allen Kombinationen also Rot/Schwarzes Kabel auch tauschen)? Das sollte bei beiden Pins identisch sein. (0,7V 0,6V 1,6V 1,7V sowas in der Richtung je nachdem wie mans polt)


Der Diodentest zeigt eine Auffälligkeit.
Mit - am SDA/SCL gegen gnd gemessen 0,5v, gegen vdd 1,5v
Mit + am SCL gegen gnd gemessen 1,7v, gegen vdd 0,6v
Mit + am SDA gegen gnd gemessen 1,2-1,0v, gegen vdd 0,4-0,3v und schwankt ein wenig

Meine Folgerung daraus ist, das der Port SDA als Eingang in der CPU einen Schaden hat. Ich glaube nicht an einen Fehler durch den Dauertest oder beschädigung durch Software, vermutlich eher statische Entladung oder der defekten Slave. Es wäre aber auch denkbar das der Slave dabei mit zu Bruch ging (was bedeuten kann, das die M32 auch im Eimer ist). Werde ich gleich noch per Diodentest durchmessen.

Da ich mir nicht zutraue, die CPU selbst zu wechseln, werde ich den RP6 wohl zur reparatur einschicken müssen und mit dem TWI Treiber länger pausieren.

Ich hänge meine letzte aktuelle TWI-Version noch an, sie müsste so ziemlich laufen, testen kann ich sie leider nicht mehr. Wichtigste Änderung ist die Umstellung aller Masterfunktionen auf asyncronen Betrieb da es immer wieder zu Problemen kam, wenn man syncrone und asyncrone Funktionen mischte. Man kann die Befehle aber quasi syncronisieren wenn man nach dem TWI-Befehl sofort task_I2CTWI aufruft, ein folgender TWI-Befehl würde das auch tuen. Es ist also nur für den ersten/letzten in einer Kette von Befehlen sowie bei I2CTWI_requestRegisterFromDevice und I2CTWI_readRegisters relevant, welche aus Mehrfachinstruktionen für die TWI bestehen - sowie für die Fehlerprüfung die auch dort abgewickelt wird. Anders ausgedrückt, es wird immer erst ein Befehl ausgeführt wenn ein weiterer abgeschickt wird oder mit task_I2CTWI nachgeholfen wird. Das ist in zeitkritischen Situationen wie Abfragen eiens SRF zu bedenken. Es erlaubt aber die einstufige Entkoppelung einer TWI Aktion wenn man selbst zeitkritische Dinge zu erledigen hat, die nichts mit dem TWI zu tun haben. Jede Medaille hat eben 2 Seiten. Die Idee dafür stammt übrigends aus SlyDs TWI Version wo es dann manchmal (für den User zuweilen schlecht nachvollziehbar) zu verzögerten TWI-Reaktionen kam, ich habe das nun auf alle Funktionen umgesetzt, damit das Verhalten eindeutig ist.
Wer oft syncrone Funktionen braucht, kann sich mit einem Macro Set ala #define SyncTWIBEFEHL TWIBEFEHL;task_I2CTWI(); für jeden Befehl ähnlich der LCD-Macros in twi_target.h oder einige Macros in RP6I2CTWI.h das Ausführen direkt hinten dran klatschen. Allerdings spart das (noch) nicht den Aufruf von task_I2CTWI(); in der Idle-Loop ein. Ich hatte vor, das später per twi_target.h einstellbar zu machen ob generell sync oder async gearbeitet werden soll.

LG Rolf

Dirk
02.03.2011, 16:16
Ich hab mir scheinbar doch was am SDA Pin der CPU zerstört.
Das ist ja echt Pech!

Aber:
Am I2C-Bus sind die Pins der Teilnehmer direkt miteinander verbunden und über einen Pullup an VDD. Hat man also 3 Teilnehmer am Bus, sind schon 3 Portpins direkt verbunden. Sind dann z.B. 2 davon gleichzeitig AUSGÄNGE (sollte nicht passieren!) und davon einer logisch high und einer low, dann kann das auch das Ende eines Portpins sein. Da braucht es nicht einmal EMV Einflüsse.

Schutz:
Wäre in der Entwicklungsphase eines I2C-Treibers nur mit Reihenwiderständen (z.B. 220 Ohm) an jedem Portpin möglich. Das erlaubt dann aber zunächst keine hohen Taktfrequenzen.

Gruß Dirk

RolfD
07.03.2011, 00:18
Hier ist aber ein Beitrag von mir verschwunden... ^^

Naja egal.
Ich habe nach einiger Sucherei was gefunden, was im Zusammenhang mit dem Hinweis von Dirk und den Ports auf den Grund der Beschädigung meines Boards und der Schwierigkeiten im MM-Betrieb hinweisen könnte. Ich war zwar der Meinung das Atmel das gefixt hat...
http://www.robotroom.com/Atmel-AVR-TWI-I2C-Multi-Master-Problem.html
Dort wird beschrieben, das die TWI Hardware bei einem Stop den Bus nicht überwacht und fälschlich für frei halten kann - was zu den besagten Zuständen wie von Dirk beschrieben führen kann. Ich hatte in meinem fehlenden Beitrag argumentiert, warum das nicht passieren darf/kann aber scheinbar haben die CPUs da evtl. eine Macke. Dort wird auch ein Workarround beschrieben, den ich dann testen werde. Ich habe noch einen anderen Verdacht, und zwar das man nach einem Stop dem TWI erst wieder sagen muss das er auf dem Bus lauschen soll. Ähnlich wie bei der Slaveinitialisierung. Dafür habe ich aber bisher nicht 1 Zeile Code im Netz oder in Dokus gefunden, das muss ich erst ausmessen. Es wäre aber quasi logisch zumal ein Stop auch kein irq auslöst - also prädestiniert ist für ein Folgebefehl.
LG Rolf

SlyD
07.03.2011, 10:56
mit dem Hinweis von Dirk und den Ports auf den Grund der Beschädigung meines BoardsEigentlich kann das nur passieren wenn Du einen Port aktiv auf 1 setzt. I2C ist ja Open Drain.
Nur weil der Master mal nicht am Bus lauscht schaltet der nicht einfach so auf aktiv high. Der darf den Pin nur zwischen aktiv low und high Z wechseln den Rest übernimmt der externe Pullup.

Mehrere Ausgänge gleichzeitig machen so kein Problem - wenn alle nur aktiv auf GND ziehen können.

RolfD
07.03.2011, 13:38
@SlyD
Hm.. hm... auch wahr. Ok, da auf den Schaden zu schließen funktioniert nicht. Sehe ich ein.
Für das i2c Protokoll ist es jedoch von Bedeutung wenn der Master den Bus nicht verfolgt und nach einem Stop "wild" ein Start bzw Daten sendet. Im "schlimmsten" Fall müsste er arbitieren und/oder ein Buserror kriegen...davon hatte ich beides reichlich beim testen. Das kann ich damit evtl. (hoffentlich) reduzieren. Ich werde mich hüten, die Leitungen aktiv zu steuern ... mir ist schon nicht wohl bei der Umsetzung wie in dem Bugreport die Leitungen abzufragen. Geht zwar aber irgendwas werden sich die Entwickler bei Atmel dabei gedacht haben... die Frage ist nur was. Da bin ich mit dem (N)ACK nach STOP wohl näher dran.... hoff ich.

Andere Frage, wie kriege ich zuverlässig raus, ob noch weitere Devices was abbekommen haben? Reicht es, die Diodenstrecken von SDA und SCL zu messen? Mein M32 Modul und das i2c LCD device sehen nach dieser Messung gesund aus aber ich will mir mein Board nicht gleich wieder zerlegen weil ich was nicht beachtet hab.

EDIT: Mir ist grade aufgefallen, das ich bei einer meiner letzten Umräumaktionen im code eine wichtige Abfrage verschusselt habe.
Und zwar fehlt in task_I2CTWI um das ganze Switch Konstrukt die Zeile:
if bit_is_clear(TWI_statusReg,STR_isr) {
und unten dann }
Ohne die Abfrage überrennen sich natürlich die Befehle wieder... was zu Allem führt.. nur nicht zu einer geordneten Datenübertragung. Sorry. Wirklich lauffähig ist der Code aber auch dann noch nicht. Ich wieder einige Problemstellen gefunden.
Es hat sich auch sonst wieder einiges getan im Source. Es gibt jetzt einen speziellen 400KHz Mode für den 100KHz Bus, eine Fehlerbehandlung mit Zähler und Retry, ein Timeout bei Fehlern, syncrone/asyncrone Ausführung ... ich hoffe mein Board ist schon unterwegs da ich so vieles nicht wirklich testen kann. Hab zwischenzeitlich WinAVR ausprobiert aber der SIM ist nicht besonders geeignet um ISRs zu debuggen, sonst aber ganz nett.

EDIT: Ein netter Mensch hier aus dem Forum ist bereit mir sein RP6 zu verkaufen, demnächst hab ich also 2 Stück... und kann besser vergleichen. Mal sehen was zuerst eintrifft. Das Ersatzboard oder der Zukauf. Und dann gehts endlich weiter...

EDIT: Da mein Board noch nicht hier ist, suche ich diverse Schaltungen und Knowhow zum Thema AVR und I2C. Nun ist mir aufgefallen, das manche die Pullups bis 470 Ohm - also auf 10% dessen runter setzen was aktuell im RP6 anliegt - allerdings dann mit einer LED im Zweig gegen VDD. Namentlich z.b. die ATmega8 ASM Treiber im www.mikrocontroller.net (http://www.mikrocontroller.net) für Master/Slave Betrieb mit Schaltplan. Dies bedeuet natürlich ordentlich Laststrom in der Ausgangsstufe des AVR, zieht die Leitung aber bei high weiter nach VDD. Sie baumelt ja bei mir normal derzeit so um 2 V, mit der LED zum testen nun bei 3V -3,5V im Highzustand.

Ich hab mal ein Filmchen davon gemacht.
http://home.arcor.de/rolf.diesing/daten/P1100529.MP4 (fliegt die Tage wieder raus... )

Die CPU befindet sich zu dieser Zeit GARANTIERT in einer While (true) Schleife, es finden keine Datenbewegungen statt und wenn die CPU SDA auf low schaltet also aktiv Daten schiebt, leuchtet die LED erwartungsgemäß kräftig. Die gelbe 5mm LED flackert hier schwach obwohl der AVR nichts auf den Ports macht. Mein Oszi zeigt ebenfalls oben die grade laufende SCL Leitung bei 5v und unten die flackernde SDA Leitung (Oszi im Dual chopper Mode, 1V/cm 20ms/cm). Und das Ganze mit 470 Ohm Pullup an der LED. Mit der Schaltung kriege ich übrigends fast zuverlässig Daten auf ein I2C LCD mit PCF8574 geschrieben da ich nun halbwechs definierte Logikpegel habe. Die Störungen/Flackern tritt jedoch auch bei der Datenübertragung auf und einlesen kann der Port damit immer noch nicht richtig. Ohne die LED und nur mit R32 flackert der SDA Port sonst irgendwo bei 1,2V und 2,2V ... ich hätte aber ehrlich gesagt nicht mal damit gerechnet, das es mit der LED klappt, denn da muss ja irgendwo in der CPU ein angeschlagener Transistor sein und der kriegt nun locker 10mA ab.

EDIT: So... mein 2ter RP6 steht auf dem Tisch, heute war der Paketbote da, es kann weiter gehen. Das Ersatzboard aus Holland ist leider noch nicht da. Die M32 ist auch wieder angeflanscht und die Hardware ist damit jetzt erst mal wieder soweit ok das ich Base und Master zugleich testen kann. Erste Messungen an SDA/SCL ergeben erwartete Werte und ordentliche Pegel. Ich werde zusehen, das ich Dirks letztes Programm ans laufen bekomme und bald eine neue beta rausbringe. Ich habe durch den 2.ten Bot nun auch 2 USB-Adapter. Man kann zwar 2 Robotloader starten aber wenn man Verbindung zum ersten USB Adapter aufbaut, verweigert der Loader beim 2. dann mit Hinweis das die Schnittstelle "in use" sei. Base und M32 gleichzeitig debugen geht also immer noch nicht aber ich brauche zumindest nicht mehr umzustecken. Wäre schön wenn man den Loader (ich hab 1.5h) da noch anpassen kann.

EDIT: Mein Ersatzboard von AREXX ist da :) Das ist die Gute Nachricht... die schlechte - man hat bei AREXX das Board in einen wattierten Umschlag gepackt und die Post hatte nix besseres zu tun als da massenweise Stempel mit "Priority mail" draufzukloppen.... 8-9 Stück... beidseitig.... und das ganze für eine Strecke von unglaublichen 231 Km und min. 1 Woche unterwegs... Ich muss leider erst testen ob das Board das überlebt hat ... mit grade biegen von Teilen isses da leider nicht mehr getan. Der Schalter, der sich schon durch die Briefhülle gedrückt hatte ist jedenfalls hin... aber da kann ich den aus dem alten Bord umlöten. Oh man... nur Ärger!

EDIT: Das I2C treibt mich noch in den Wahnsinn...

RolfD
23.03.2011, 17:31
Sooo... wieder mal neues vom TWI.
Folgendes... der Treiber ist BETA und noch nicht fertig. Ich kann ihn noch nicht zur allgemeinen Nutzung frei geben. Er funktioniert aber schon mal soweit, das man es sehen kann und ich denke, zum betatesten langts auch schon mal.
Basis zum testen ist weiterhin Dirks Programm, was ich leicht modifiziert habe. Beide Programme (Base/M32) sind beigefügt, incl. der dazugehörenden TWI_TARGET.H
Es sind einige Optionen hinzu gekommen in twi_target, ansich sind sie aber dort jeweils kurz erklärt.
Was passiert da jetzt genau?
Nun auf beiden CPUs läuft jeweils ein masterfähiger Slave. Die beiden werfen sich Daten zu, die sie umkopieren so das die wieder abgeholt werden können. Die Ergebnisse werden vom Master verglichen und wenn Fehler auftauchen, werden diese erkannt und ausgegeben.
Starten tuen die jeweiligem Mastertasks z.Z. nur auf verlangen, das heisst beim drücken der Bumpertasten bzw. Key 1 auf der M32. Das habe ich deshalb gemacht, weil die sich z.Z. noch gegenseitig überrennen würden, wenn aber nur je einer sendet, klappts. Kann man einfach testen indem man auf beiden Boards die Taster drückt. Ziel wird sein, das beide auch dauerhaft senden können. Dafür ist das System jedoch noch nicht robust genug. Daher auch Beta. Derzeit dürften es die beiden Funktionen I2CTWI_request* nicht tuen.... also nicht wundern. Auch ein Grund für Beta.

Es gibt neben dem normalen Debug nun ein Debug für eine State Engine, diese überwacht ob ein kompletter Transport erfolgte (incl. Stop) sowie eine allgemeine Ausgabe für Bufferüberläufe.

Es gibt da noch ein paar ungereimtheiten im Timing, ich hoffe aber das beseitigen zu können. U.a. gehen ab und zu Daten verloren bzw. ist der Slave der Meinung, das Daten reinkamen obwohl die Register noch nicht beschrieben wurden. Je mehr Befehle in der ISR ausgeführt werden - und sich damit das Clocksignal streckt - um so seltener werden die Fehler. Es macht dabei z.B. einen Unterschied ob man die Stateengine einschaltet oder nicht. Das sind vielleicht 20 Maschinenbefehle mehr als sonst... also keine wirklich langen Zyklen. Mir ist aber nicht klar warum das so ist. Es gehen aber bis auf dieses kleine Problem keine Daten flöten, der Master macht bis zu 10 neue Anläufe mit je 5ms Pause dazwischen (alles einstellbar), der Slave nackt wenn er Busy ist und so kommen normal auch alle Daten zielsicher an.
Viel Spass beim testen
LG Rolf

EDIT: Einen längeren Test über mehrmalig je 1-2 Std. überstanden mein reparierter RP6 und der 2.te damit auch schon. Also keine Sorge. Übrigends sind seid der Reparatur komischer Weise ALLE Buserrors weg.

Edit: Weitere Änderung, ich hatte nicht bedacht, das bei Dirty Regs der Host (Slave) zwar ein weiteres Schreiben mit I2CTWI_doneAction(); frei geben muss, der Client (Master) aber die Regs bisher auslesen kann wärend der Slave sie ändert bzw. bearbeitet - was vor allem auf der schnelleren M32 zu Fehlern führt. Daher muss man die case TWI_STX_DATA_ACK gegen Folgende austauschen. Dies verhindert dann ein Auslesen wenn der Client noch nicht so weit ist - auch wenn kein Registerpointer geschrieben wird.


case TWI_STX_DATA_ACK: // 0xB8 TW_ST_DATA_ACK Data byte in TWDR has been transmitted; ACK has been received
if (TWI_statusReg.needAction && TWI_statusReg.NACKonAction) { //if Registers dirty, now send nacks
#ifdef DEBUG
logmyaction(TWI_STATUS,"/ STX_(ADR|DATA)_ACK NACK (Slave busy)");
#endif
TWI_statusReg.SendNACK=1; //dead end, master must retry, I2CTWI_doneAction/TWINIT will reset this
} else {
if (current_register < I2CTWI_SLAVE_READ_REGISTERS) {
TWDR = I2CTWI_readableRegisters[current_register++];
TWI_statusReg.SendACK=1;
#ifdef DEBUG
logmyaction(TWDR,"<- STX_DATA_ACK -> ACK (data)");
#endif
} else {
#ifdef DEBUG_MISC
writeString_P("Register read boundary violation\n");
TWI_statusReg.SendNACK=1;
#else
TWDR = 0;
TWI_statusReg.SendACK=1; // THIS should not happen... count your Regs please...
#endif
}
}
break;Dieser Fix ist für eine Anwendung wie in dem RP6-Slave weniger wichtig, in Dirks Programm jedoch, wo sich beide CPU's mit Daten bewerfen und requesten spielt es eine gewichtige Rolle.

Damit macht nun auch ein weiterer Befehl in der RP6I2CTWI.h Sinn.
#define I2CTWI_doAction() TWI_statusReg.needAction=1
was quasi den Slave jederzeit in die Lage versetzt, dem Master anzuzeigen das er grade keine Daten kriegt, was man aber durch
I2CTWI_doneAction(); auch wieder freigeben sollte. Dabei sind die Retrys und der Timer in twi_target.h zu berücksichtigen.
Der Slave sollte also nur max so lange die Regs blockieren wie sich aus den beiden Zahlen Millisekunden + Reserve ergeben.
Leider liefern die asyncronen Funktionen ja kein echten Errorcode zurück, sonst könnte man das besser abfangen.

Kleine Änderung für TWI_MRX_ADR_NACK, bisher wurde hier ein Error initiiert, dies sollte durch ein Restart ersetzt werden. Zum einen entscheidet der Retrycode ob und was ein Error ist, zum anderen ist ein NACK auf Grund von "Device Bussy" ja kein Grund, den ganzen Transport abzuwürgen. Die case TWI_MRX_ADR_NACK wird daher ersetzt mit:


case TWI_MRX_ADR_NACK: // 0x48 TW_MR_SLA_NACK SLA+R has been transmitted and NACK received
#ifdef DEBUG
logmyaction(TWDR,"<- MRX_DATA_NACK RESTART (busy)");
#endif
TWI_STATUS_state = TWI_STATUS;
TWI_statusReg.SendStop=1;
TWI_old_operation = I2CTWI_NEED_RESTART;
break;
Das System verhält sich damit bei einem NACK im Master Receive (MRX) Mode genau so wie im Master Send (MTX) Mode und versucht es erneut bis die Retry Defaults aus twi_target.h erreicht sind.

EDIT:
Ich habe ein weiters Problem gefixt, der Slave war zu früh der Meinung, das der Transport fertig ist wesshalb teilweise Daten nur halb ankamen bzw. zu früh verarbeitet wurden. Die Änderungen im Code sind zu verstreut als das ich sie hier posten kann, es wird also demnächst wieder eine Beta geben. Ich suche z.Z. aber noch ein Fehler, der dann zu Stande kommt wenn beide CPUs quasi zeitgleich senden. Das ist ein Laufzeitproblem und hat wohl noch nichts mit Arbitierung zu tun.

RolfD
21.04.2011, 09:52
Es gibt mal wieder eine neue Beta.
Einige Fehler sind gegenüber der vorherigen Version behoben. Achtung, die TWI Einstellungen stehen z.Z. auf debug. Die Version läuft recht stabil und macht nur Probleme wenn beide Seiten gleichzeitig senden, da bleibt der Slave code dann wohl irgendwie hängen ohne das es zur arbitation käme.

In dem Code stecken mehrere "Verriegelungen" damit z.B. der Master nicht das Senden anfängt so lange der Slavecode noch empfängt. Ich hab das nun schon drei mal umgeschrieben aber da ich mich an viele Vorgaben des alten Treibers halte, gelingt es mir bisher nicht, gewisse Probleme wasserdicht zu lösen.
Die Version ist also wie die vorherigen eine Beta bzw. "Zwischenstand".

Um den Master zu aktivieren muss man hier T1 auf der M32 oder die Bumper auf der Base auslösen, ansonsten befinden sich die Boards im slave mode.

LG Rolf

EDIT: Für Leute die mal testen wollen ohne das Projekt zu bauen leg ich mal noch die hex-Files bei. Wie gesagt... Debug aktiv -> viel Textausgabe auf Console... Benötigt werden die Base, eine M32 und ein Display an der M32

Fabian E.
10.07.2011, 17:02
Und, wie sieht es so aus? Hast du nochmal nach den Problemen beim wechselseitigen Senden geforscht?
Wolltest du nicht auch eine Version für die M128 machen? :)

RolfD
10.07.2011, 18:04
Nein... ja.. geforscht schon.. aber es nicht wasserdicht gelöst. Ich habe auch den Eindruck das viel zu wenig Interesse besteht als das ich da allein Tage über Tage an Zeit reinhänge. Auf die letzte Testversion gabs auch kein Feedback... also was solls.
Für die M128 könnte man das als Maschinencodemodul evtl. auch machen, die Software hat ja nur eine mässig implementierte Master-I2C mit polling...bedingt durch das Interpreter-C. Da hab ich aber noch keine weiteren Forschungen unternommen.
LG

SlyD
13.07.2011, 17:46
Hi Rolf,

ich hatte bislang leider leider noch keine Zeit das genauer unter die Lupe zu nehmen,
aber mal dazu:


> da ich mich an viele Vorgaben des alten Treibers halte,
> gelingt es mir bisher nicht, gewisse Probleme wasserdicht zu lösen.


Wenn Dir die API nicht gefällt, änder das ruhig ab. Die ganze RP6lib ist gerade deshalb Opensource damit man bei Bedarf was dran ändern kann wenn es nicht passt (in großen Opensource Projekten (z.B. im Linux Kernel) ändert sich auch dauernd irgendwo irgendwas an den APIs das ist völlig normal)


Die geringe Resonanz dürfte auch daher kommen, das für die meisten standard Anwendungen die bisherige Lib stabil genug ist.
(also die möglichen Fehlerfälle nicht weiter auffallen)


MfG,
SlyD

radbruch
13.07.2011, 17:50
Die geringe Resonanz dürfte auch daher kommen, das für die meisten standard Anwendungen die bisherige Lib stabil genug ist.
Das sehe ich genauso. Da ich von I2C nicht viel Ahnung habe warte ich als "Abstauber" auf eine bessere Version von euch :)