ftRoboExt: Software

Nach einiger Pause gibt es mal wieder etwas neues zu meinem selbstgebauten Erweiterungsmodul „ftRoboExt“. Hardware und Gehäuse waren ja schon fertig, nun fehlte eine finale Version der Software. Inzwischen ist das ganze Projekt übrigens auf GitHub zu finden.

Das Protokoll für die Kommunikation zwischen Interface und Extension ist recht einfach, es ist im Prinzip ein SPI-Bus mit einem zusätzlichen Bestätigungssignal und einer Adresse. Über drei Adressleitungen wählt das Interface aus, mit welcher Extension es sprechen möchte. Die originalen Extensions lassen sich verketten, dabei wird das Signal aber nicht ganz unverändert durchgeschleift, sondern die Adresse wird von jedem Modul um ein verringert. Auf diese Weise ist jedes Modul adressiert, wenn es Adresse 0 sieht (das erste direkt, das zweite weil es 1-1=0 sieht). Eine sehr intelligente Lösung, denn dadurch wird die Adresse des Moduls einfach durch die Verkabelung bestimmt.

Wenn das Modul die Adresse 0 sieht, zieht es EM-ACK (die Bestätigungsleitung) auf Low. Nun beginnt das Interface 6 Bytes zu senden, die Extension gibt nach jedem Byte einen kurzen High-Puls auf der EM-ACK Leitung aus. Die originale Extension scheint recht langsam zu sein, so braucht sie etwa 700µs um die Adressierung zu bestätigen. Das Interface hat aber scheinbar auch kein Problem damit, wenn man schneller ist. Die 6 Bytes übertragenen Bytes enthalten die Nutzdaten, genaueres bei thkais.

In Software ist die Umsetzung dieses Protokolls recht einfach, vor allem wenn man das SPI-Moduls des ATmega nutzt. Es hat sich als nützlich herausgestellt, das SPI-Modul jedes Mal zu resetten, wenn das Modul adressiert wurde, ansonsten hat sich das System beim Start oft aufgehängt, vermutlich weil schon ein paar fehlgeleitete Flanken auf der Taktleitung waren und das SPI-Modul danach aus dem Takt war. Die eigentliche Kommunikation erfolgt per Interrupt, so dass der Code recht aufgeräumt ist:


void setup() {
SPI.setDataMode(SPI_MODE3);
SPI.attachInterrupt();
}

ISR (SPI_STC_vect) //SPI interrupt
{
//read data from register
bufIn[pos] = ~SPDR;
//put next value in the register
pos++;
SPDR = ~bufOut[pos];

digitalWriteEmAck(HIGH);
delayMicroseconds(2);
digitalWriteEmAck(LOW);
}

void loop() {
//wait for address
while (digitalRead(PIN_ADDR0) == HIGH) {}

//reset SPI module, just in case (system can hang up on startup otherwise)
SPCR &= ~bit(SPE);
SPCR |= bit(SPE);
pos = 0;

//read in data...

//put first byte in register
SPDR = ~bufOut[0];

delayMicroseconds(100); //700µs delay in original module, not sure if needed
digitalWriteEmAck(LOW);

while (digitalRead(PIN_ADDR0) == LOW) {} //wait for master to release address

delayMicroseconds(10);
digitalWriteEmAck(HIGH);

//process in data...
}

Das eigentliche Auslesen der der Eingänge und setzen der Ausgänge habe ich mal gekürzt, der komplette Code ist hier zu finden.

Das funktionierte erstaunlich einfach und stabil, RoboPro erkennt ein neues Extensionmodul und Ein- und Ausgänge funktionieren.

Blieb noch ein Problem: Ich wollte ursprünglich 2 EM auf einem simulieren, wobei das zweite als „virtuelles“ EM Daten in RAM und EEPROM speichern sollte, so dass ich mir die Position der Kisten in meinem Hochregallager merken kann, auch wenn das Programm auf dem Interface neu gestartet wird. Leider hatte ich ja nicht bedacht, das die /SS-Leitung nicht als GPIO zur Verfügung steht, und damit hatte ich nur noch ADDR0 am Mikrocontroller zur Verfügung. Mit einem Bit Adresse kann man aber nur ein EM simulieren… Außer man greift zu einem kleinen Trick 😉 Mit ein bisschen Software lässt sich schließlich fast jedes Hardware-Problem lösen 😛

Die Grundidee ist einfach: Wir wissen, dass das Interface nach dem Ende der Übertragung der Daten für EM1 die nächste Adresse anlegen wird. Meldet sich dann wieder ein EM in dem EM-ACK auf Low gezogen wird, weiß das Interface, dass diese Adresse auch „besetzt“ ist. Sehen können wir das nicht, weil für Adresse 1, also das zweite Modul, das Signal ADDR0 wieder auf High liegt, also der gleiche Zustand wie wenn der Bus nicht benutzt wird (dann liegen alle drei ADDR-Signale auf High). Wir können aber einfach nach dem Ende der Übertragung für EM1 das EM-ACK-Signal auf Low ziehen, und zwar in dem Moment, in dem wir vermuten, dass das Interface die nächste Adresse angelegt hat. Das ganze ist also ein ziemlicher Hack, funktioniert aber erstaunlich stabil. Um genau den richtigen Zeitpunkt zu finden, war mein Arduino als LogicAnalyzer eine sehr große Hilfe. Ich glaube, ich sollte mal darüber nachdenken, einen „richtigen“ zu kaufen…

Streng genommen ist natürlich nicht garantiert, dass das Interface nach Adresse 0 Adresse 1 anlegt, und auch das Timing ist eigentlich Sache des Interfaces. Glücklicherweise ist das Interface hier aber sehr einfach und läuft immer alle Adresse durch, und zwar immer mit dem gleichen Timing. Einen kleinen Haken gibt es noch: Nach Adresse 1 kommt natürlich Adresse 2, die für uns wieder wie Adresse 0 aussieht (für beides ist das letzte Bit 0), so dass sich das „normale“, erste EM wieder angesprochen fühlt. Daher hat das RoboInterface teilweise mehr als zwei Extensions erkannt. Hier half eine kleine Pause: Wenn nach dem anlegen von Adresse 2 eine Zeit lang niemand antwortet, geht das Interface davon aus, dass diese Adresse nicht belegt ist, und bricht die Übertragung ab.

Mit diesem Hack habe ich jetzt also ein zweites Interface zur Verfügung, was ich als Speicher benutzen kann. Das ist aber in der Software noch nicht implementiert. Als nächstes steht jetzt erst Mal der Einbau in mein Hochregallager und damit der Praxistest an…

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.