Siemens Logo
Einleitung
Da ich kürzlich eine Siemens Logo!8 230 RCE (0BA8) erworben habe kam bei mir relativ schnell der Wunsch nach weiteren Anschlussmöglichkeiten auf. Wünschen würde ich mir zum Beispiel eine Möglichkeit digitale Temperatursensoren einzulesen. Des weiteren habe ich kurze Zeit später ein Erweiterungsmodul für die Logo gekauft, beim auspacken fiel mir auf, dass die Abdeckung nicht richtig eingerastet war. Das war die Gelegenheit einen Blick in das Erweiterungsmodul zu werfen...
Hier möchte ich nun meine Entdeckungen Kundtun und später auch noch ergänzen, da mein Ehrgeiz nun geweckt ist.
Update: Es war mir bisher nicht möglich die Logo zu überreden meine Eingänge einzulesen, woran das liegt kann ich mangels weiterer Module leider momentan nicht feststellen. Falls jemand mehrere Module an seiner 0BA8 betreibt würde ich mich freuen wenn er/sie ein paar Protokollaufzeichnungen hier anhängen könnte. Meine Logo wandert nun für das erste zurück in den Verteiler, mit 8 zusätzlichen Ausgängen allerdings. ^^ (siehe unten anhängendes Programm) Das ist zumindest ein Teilerfolg und für das erste gar nicht so schlecht, wie ich finde.
Erste Entdeckungen
Meine Suche nach Bustreibern auf der Erweiterungsplatine blieb erfolglos, ich konnte aber die Pins des Erweiterungsmoduls recht schnell eindeutig zuordnen und allem Anschein nach verwendet Siemens zur Kommunikation zwischen den Modulen hier SPI. Dabei ist die Pinbelegung wie folgt: (Pins von oben nach unten)
Pin | Funktion | Pegel |
---|---|---|
1 | GND | 0V |
2 | VCC | 3V3 |
3 | MISO | 3V3 |
4 | MOSI | 3V3 |
5 | SCK | 3V3 |
6 | GND | 0V |
Bei nächster Gelegenheit werde ich mit dem LogicAnalyser mal auf dem Bus lauschen und die Erkenntnisse hier einstellen.
Ich erhoffe mir davon dass es möglich ist das Protokoll zu erkennen und daraus eigene Module zu bauen.
Kommunikation
Der SPI-Bus läuft mit einer Clockfrequenz von 1MHz.
Es werden im Abstand von 0,25ms Statusnachrichten übertragen, dies scheint eine Art Lifecycle auf dem Bus zu sein.
Diese sehen wie folgt aus:
Pin | Byte 1 | Byte 2 | Byte 3 | Byte 4 |
---|---|---|---|---|
MOSI | 0xA5 | 0xD0 | 0xA5 | 0xD0 |
MISO | 0xD0 | 0xA5 | 0xD0 | 0xA5 |
Des weiteren werden alle 15ms längere Telegramme übertragen, in diesen verbergen sich auch die Statusbits für die IO-Register.
LOGO Programm: Ausgang 6 periodisch ein/aus
Q6 aus Q6 ein
MOSI 0xD1 MISO 0xA5 MOSI 0xD1 MISO 0xA5
MOSI 0x11 MISO 0xD0 MOSI 0x11 MISO 0xD0
MOSI 0x0D MISO 0xA5 MOSI 0x0D MISO 0xA5
MOSI 0x00 MISO 0xD1 MOSI 0x02 MISO 0xD1
MOSI 0x00 MISO 0x10 MOSI 0x00 MISO 0x10
MOSI 0x00 MISO 0x0D MOSI 0x00 MISO 0x0D
MOSI 0x00 MISO 0x00 MOSI 0x00 MISO 0x00
MOSI 0x00 MISO 0x00 MOSI 0x00 MISO 0x00
MOSI 0x00 MISO 0x00 MOSI 0x00 MISO 0x00
MOSI 0x00 MISO 0x00 MOSI 0x00 MISO 0x00
MOSI 0x00 MISO 0x00 MOSI 0x00 MISO 0x00
MOSI 0x3A MISO 0x00 MOSI 0x3A MISO 0x00
MOSI 0x58 MISO 0x00 MOSI 0x5A MISO 0x00
MOSI 0x00 MISO 0x00 MOSI 0x00 MISO 0x00
MOSI 0xA5 MISO 0x3A MOSI 0xA5 MISO 0x3A
MOSI 0xD0 MISO 0x57 MOSI 0xD0 MISO 0x57
MOSI 0xA5 MISO 0x00 MOSI 0xA5 MISO 0x00
LOGO Programm: Ausgang 5 periodisch ein/aus
Q5 aus Q5 ein
MOSI 0xD1 MISO 0xD0 MOSI 0xD1 MISO 0xA5
MOSI 0x11 MISO 0xA5 MOSI 0x11 MISO 0xD0
MOSI 0x0D MISO 0xD0 MOSI 0x0D MISO 0xA5
MOSI 0x00 MISO 0xA5 MOSI 0x01 MISO 0xD1
MOSI 0x00 MISO 0xD1 MOSI 0x00 MISO 0x10
MOSI 0x00 MISO 0x10 MOSI 0x00 MISO 0x0D
MOSI 0x00 MISO 0x0D MOSI 0x00 MISO 0x00
MOSI 0x00 MISO 0x00 MOSI 0x00 MISO 0x00
MOSI 0x00 MISO 0x00 MOSI 0x00 MISO 0x00
MOSI 0x00 MISO 0x00 MOSI 0x00 MISO 0x00
MOSI 0x00 MISO 0x00 MOSI 0x00 MISO 0x00
MOSI 0x3A MISO 0x00 MOSI 0x3A MISO 0x00
MOSI 0x58 MISO 0x00 MOSI 0x59 MISO 0x00
MOSI 0x00 MISO 0x00 MOSI 0x00 MISO 0x00
MOSI 0xA5 MISO 0x00 MOSI 0xA5 MISO 0x3A
MOSI 0xD0 MISO 0x3A MOSI 0xD0 MISO 0x57
MOSI 0xA5 MISO 0x57 MOSI 0xA5 MISO 0x00
MOSI 0xD0 MISO 0x00 MOSI 0xD0 MISO 0xA5
Allgemein fällt auf dass die Übertragung hin und wieder um ein Byte verschoben ist, ich bin mir nicht sicher ob das unkritisch ist oder ein Messfehler. Geloggt wurde mit Saleae Logic v1.1.34Beta mit einer Abtastrate von 12MS/s mit SPI Modus 4 (CPOL=1, CPHA=1) MSB first, 8bit per Transfer. Am Ausgang des Erweiterungsmoduls liegen MOSI und CLK etwas Zeitversetzt an, wie viel genau kann ich im Moment nicht messen da mir eine voll bestückte Prüfleitung fehlt.
Scheinbar ist der Telegrammaufbau wie folgt:
1 0xD1 Startbyte
2 0x11 unbekannt bisher konstant
3 0x0D unbekannt bisher konstant
4 0x02 Ausgaenge 5-12 / Eingaenge 9-16
5 0x01 Ausgaenge 13-20 / Eingaenge 17-24
6 0x00 unbekannt vmtl. analog 2
7 0x00 unbekannt vmtl. analog 2
8 0x00 unbekannt vmtl. analog 2
9 0x00 unbekannt vmtl. analog 1
10 0x00 unbekannt vmtl. analog 1
11 0x00 unbekannt vmtl. analog 1
12 0x3A unbekannt, vmtl. Daten Ende
13 0x5B Checksumme aus Byte 2-12
14 0x00 Ende Update: scheint eher die Nummer des Moduls zu sein am Ausgang des ersten erscheint hier 0x01
Ausserdem werden alle 50ms weitere Statusnachrichten versendet, der Aufbau ist wie folgt:
1.Modul Ausgang
MOSI 0xD1 Startbyte
MOSI 0x1E Nachrichtentyp
MOSI 0x05 Nachrichtentyp
MOSI 0x3A Daten Ende
MOSI 0x5E Checksumme
MOSI 0x00 Ende
1.Modul Eingang
MOSI 0xD1 MISO 0xA5
MOSI 0x1E MISO 0xD0
MOSI 0x05 MISO 0xA5
MOSI 0x3A MISO 0xD1 // Startbyte
MOSI 0x5D MISO 0x1F // Typ / Adresse?
MOSI 0x00 MISO 0x0A // Typ
MOSI 0xA5 MISO 0x01 // Moduladresse ?
MOSI 0xD0 MISO 0x08 // 8IN ??
MOSI 0xA5 MISO 0x08 // 8OUT ??
MOSI 0xD0 MISO 0x00 // ??
MOSI 0xA5 MISO 0x00 // ??
MOSI 0xD0 MISO 0x3A // Daten Ende
MOSI 0xA5 MISO 0x74 // Checksumme
MOSI 0xD1 MISO 0x00 // Ende
Die Antwort ist immer Konstant an diesem Modul, daher dienen diese Nachrichten vermutlich der Identifikation der Module bzw. deren Startadressen.
Erste Tests
Ich habe ein kurzes Testprogramm für einen Atmega8 geschrieben, das setzen der Ausgänge ist problemlos möglich. Die Rückmeldung der Eingänge funkioniert leider nicht. Wie man schnell sieht muss das einmal ordentlich geschrieben werden, zum Testen der Funktion lang der Code aber für das erste.
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdint.h>
#define BUF_SIZE 64
#define OUTPUT_PORT PORTC
#define INPUT_PORT PIND
#define DATA_START 0xD1
#define DATA_END 0x3A
#define DATA_ADDR 0x01
#define DATA_UPDATE 0x11
#define DATA_UPD_1 0x0D
#define DATA_STAT 0x1E
#define DATA_STAT1 0x05
volatile unsigned char in_buf[BUF_SIZE];
volatile unsigned char out_buf[BUF_SIZE];
volatile unsigned char data, inpointer, status;
void init( void );
// x += y mit Überlauf
static inline uint8_t add_usat8 (uint8_t x, uint8_t y)
{
asm ("add %[x], %[y]" "\n\t"
"brcc 0f" "\n\t"
//"ldi %[x], 0xff" "\n\t"
"0:"
: [x] "+d" (x)
: [y] "r" (y));
return x;
}
SIGNAL (SPI_STC_vect) {
in_buf[inpointer] = SPDR;
inpointer++;
if( inpointer >= BUF_SIZE ) inpointer = 0;
}
int main( void ) {
init();
unsigned char statmsg[] = { 0x0A, 0x01, 0x08, 0x08, 0x00, 0x00, 0x3A, 0x74, 0x00 };
inpointer = 0;
unsigned char addressself, start = 0, tempindex = 0, sendstat = 0, message[14];
uint8_t checksumin = 0, checksumout = 0;
sei();
while(1) {
if( tempindex != inpointer ) {
tempindex = inpointer;
unsigned char tempdata = in_buf[inpointer-1];
if( tempdata == DATA_START ) {
start = 1;
checksumin = 0;
checksumout = 0;
}
if( start ){
message[start-1] = tempdata;
if( sendstat ) {
tempdata = statmsg[start-3];
if( start >= 12 ) {
start = 0;
sendstat = 0;
}
}else{
switch( start ) {
case 2: if( tempdata == DATA_UPDATE ) {
checksumin = add_usat8( checksumin, tempdata );
tempdata -= 1;
checksumout = add_usat8( checksumout, tempdata );
} else if( tempdata == DATA_STAT ) {
sendstat = 1;
checksumin = add_usat8( checksumin, tempdata );
tempdata += 1;
checksumout = add_usat8( checksumout, tempdata );
}
break;
case 3: checksumout = add_usat8( checksumout, tempdata );
checksumin = add_usat8( checksumin, tempdata );
break;
case 4: if( ( message[1] == DATA_STAT ) && ( tempdata == DATA_END ) ) start = 0;
checksumin = add_usat8( checksumin, tempdata );
checksumout = add_usat8( checksumout, tempdata );
break;
case 5: checksumin = add_usat8( checksumin, tempdata );
tempdata = INPUT_PORT;
checksumout = add_usat8( checksumout, tempdata );
break;
case 12: checksumin = add_usat8( checksumin, tempdata );
checksumout = add_usat8( checksumout, tempdata );
break;
case 13: if( message[11] == DATA_END ) {
if( tempdata == (uint8_t) checksumin )
OUTPUT_PORT = message[4];
tempdata = checksumout;
}
break;
case 14: addressself = tempdata;
start = 0;
break;
default: if( start > 1 && start < 13 ) {
checksumout = add_usat8( checksumout, tempdata );
checksumin = add_usat8( checksumin, tempdata );
}
break;
}
}
out_buf[inpointer] = tempdata;
if( start ) start++;
}
if( tempdata != in_buf[inpointer-1] ) SPDR = out_buf[inpointer];
}
}
}
void init( void ) {
DDRB = (1<<PB4);
SPCR = 0xCC; //SPI Mode 4 interrupt enable
status = SPSR;
DDRD = 0x00;
PORTD = 0xCC;
DDRC = 0xFF;
PORTC = 0x00;
}