Siemens Logo

Aus der Mikrocontroller.net Artikelsammlung, mit Beiträgen verschiedener Autoren (siehe Versionsgeschichte)
Wechseln zu: Navigation, Suche

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;
}