Forum: Mikrocontroller und Digitale Elektronik CANopen Implementierung "nur" für Sensordaten


von Hendrik S. (shotar)


Lesenswert?

Hallo,

um Analogwerte einer DMS-Messbrücke auszuwerten bekomme ich bald diesen 
Can-Bus A/D Wandler:
http://www.microcontrol.net/produkte/io-module/trs/1-x-druck-in/

Die Kommunikation läuft über eine Art Raspberry Pi (ARM A7 Prozessor mit 
Linux OS und integriertem CAN Port). Ich möchte allersings "nur" den 
Analogwert zyklisch (50 Hz) auslesen. Ich habe bisher keinerlei 
Erfahrung mit CANopen und möchte für dieses Projekt die Implementierung 
sehr einfach halten. Ich benötige die speziellen Heartbeat und 
Statusabfragen nicht die mit CANopen liefern könnte.

In der EDS Datei vom Hersteller finde ich folgende Einträge:
;--- 7100h: AI Field Value 16Bit ------------------------------------
[7100]
SubNumber=2
ParameterName=AI Field Value 16Bit
ObjectType=0x08

[7100sub0]
ParameterName=Number of entries
ObjectType=0x7
DataType=0x5
AccessType=ro
DefaultValue=0x1
PDOMapping=0

[7100sub1]
ParameterName=Channel 1
ObjectType=0x7
DataType=0x6
AccessType=ro
DefaultValue=0x00
PDOMapping=1


;--- 7130h: AI Process Value 16Bit ----------------------------------
[7130]
SubNumber=2
ParameterName=AI Process Value 16Bit
ObjectType=0x08

[7130sub0]
ParameterName=Number of entries
ObjectType=0x7
DataType=0x5
AccessType=ro
DefaultValue=0x1
PDOMapping=0

[7130sub1]
ParameterName=Channel 1
ObjectType=0x7
DataType=0x3
AccessType=ro
DefaultValue=0x00
PDOMapping=1





In meiner bisherigen Suche habe ich öfter gelesen, dass man die Daten 
einfacher über SDO auslesen kann. Warum?
http://stackoverflow.com/questions/1232624/how-to-program-a-simple-canopen-layer

Ich habe jetzt schon einige Handbücher und Foreneinträge gelesen, steige 
aber mit den PDO und SDO von CANopen nicht so richtig durch. Wie läuft 
so eine Kommunikation denn genau ab?

Wenn ich das richtig verstanden habe, dann sieht ein CANopen Telegramm 
so aus:

Identifier - DLC - DATA (byte0 - byte7)

wobei der Identifier mit den ersten 4 Bit den Service (zB 1100 für SDO 
lesen) und den restlichen 7 Bit die Node-ID festlegt. Aber wo genau 
finde ich denn den Analogwert wieder?

Ich stelle mir mein Programm im Moment so vor, dass ich eine fest 
definiertes Anfrage-Telegramm über CAN sende und im Anschluss die 
Antwort mit dem Analogwert erhalte. Ich nutze dabei SocketCAN unter 
Linux.

: Bearbeitet durch User
von Hendrik S. (shotar)


Lesenswert?

Als "Vorlage" könnte ich mir dieses Projekt vorstellen:
https://www.raspberrypi.org/forums/viewtopic.php?p=444938

Hier wäre der Code-Teil für das CAN-Telegramm:

     struct can_frame frame_led;
     frame_led.can_id = 0x300 + NodeId;  // use all led 8 bit resolution 
TPDO output
     frame_led.can_dlc = 3;  // only 3 bytes  Red,Green and blue
     frame_led.data[0]=Red;
     frame_led.data[1]=Green;
     frame_led.data[2]=Blue;
     write(sfd,&frame_led,sizeof(struct can_frame));

von Volker S. (vloki)


Lesenswert?

Deine Vorlage scheint aber für eine PDO zu sein.
Bei den PDOs bestimmt nur die ID den Inhalt und alle 8 möglichen 
Daten-Bytes können auch Daten enthalten.

Bei CANopen sind die IDs in feste Bereiche eingeteilt:
1
typedef enum{//:quint16{
2
    COBID_NMT       = 0x000,
3
    COBID_SYNC      = 0x080,
4
    COBID_TIMESTAMP = 0x100,
5
    COBID_EMERGENCY = 0x080,
6
                                    //Prozessdatenobjekte
7
    COBID_RXPDO1    = 0x180,        //RX Empfangs-PDO
8
    COBID_TXPDO1    = 0x200,        //TX Sende-PDO
9
    COBID_RXPDO2    = 0x280,
10
    COBID_TXPDO2    = 0x300,
11
    COBID_RXPDO3    = 0x380,
12
    COBID_TXPDO3    = 0x400,   // 00 04
13
    COBID_RXPDO4    = 0x480,   // 80 04
14
    COBID_TXPDO4    = 0x500,
15
16
    COBID_RXSDO     = 0x580,        //Servicedatenobjekte
17
    COBID_TXSDO     = 0x600,
18
    COBID_NMT_LNG   = 0x700         //NMT Network-Management(-Nachricht)
19
                                    //LNG Life-Node-Guarding
20
}CANOPEN_COBID;
Eine ID wird aus einer Basis und einer Bus ID gebildet.
Die Basis für SDO Anfragen von deinem Master wäre 0x600 + die ID des 
Sensors. Bei 3 Sensoren z.B. 0x601, 0x602, 0x603

Ein SDO Transfer zur Abfrage des Sensorwertes würde aus einem Request 
CO_REQ_SDO_RD und einer Response CO_RES_SDO_RD_x bestehen.

Bei SDOs sind die ersten 4 Bytes der Daten gar keine Daten, sondern 
Identifyer. Das erste wäre zum Beispiel der REQUEST, der Beschreibt, was 
für einen Zugriff gemacht werden soll. (Lesen, schreiben und wie viel 
Bytes)
Dann kommen zwei OBJECT-Bytes und ein SUBINDEX die praktisch die Adresse 
des Parameters (z.B. DMS Daten) angeben.


Einer von mir verwendete Nachrichten-Struktur sieht so aus:
1
typedef struct{
2
    CANOPEN_COBID   cobID;
3
    CANOPEN_RTF_DLC rtf_dlc;
4
    union{
5
        struct{
6
            CANOPEN_REQ     request;
7
            quint16         object;
8
            quint8          subindex;
9
            quint32         data_sdo;
10
        };
11
        struct{
12
            quint64         data_pdo;
13
        };
14
    };
15
}CANOPEN_MESSAGE

Für den Request gibt es zum Beispiel folgende Möglichkeiten:
1
typedef enum{//:quint8{
2
    CO_REQ_START_NODE   = 0x01,     //SDO Request, Starte Node
3
    CO_REQ_STOP_NODE    = 0x02,     //SDO Request, Stoppe Node
4
    CO_REQ_SDO_WR_4     = 0x23,     //SDO Request, 4 Byte schreiben
5
    CO_REQ_SDO_WR_2     = 0x2B,     //SDO Request, 2 Byte schreiben
6
    CO_REQ_SDO_WR_1     = 0x2F,     //SDO Request, 1 Byte schreiben
7
    CO_RES_PDO_RD       = 0x37,
8
    CO_REQ_SDO_RD       = 0x40,     //SDO Request, Leseanforderung
9
    CO_RES_SDO_RD_4     = 0x43,     //SDO Response auf Leseanforderung, 4 
10
    CO_RES_SDO_RD_2     = 0x4B,     //SDO Response auf Leseanforderung, 2 
11
    CO_RES_SDO_RD_1     = 0x4F,     //SDO Response auf Leseanforderung, 1 
12
    CO_RES_SDO_WR       = 0x60,     //SDO Response, Schreiben erfolgreich
13
    CO_RES_SDO_ABT      = 0x80,     //SDO Response, Fehler
14
    CO_REQ_RESET_NODE   = 0x81      //SDO Request, Neustart der FW, }CANOPEN_REQ;


Für den Sensor sähe das ungefähr folgendermaßen aus
1
Message.COB_ID = 0x600 + SENSOR_ID; // (0x600 für SDO)
2
Message.rtf_dlc = 4;   // request, object, subindex
3
Message.request = CO_REQ_SDO_RD;
4
Message.object = 0x7130;
5
Message.subindex = ? (keine Ahnung, 0?)
bei der Antwort wäre die
ID -> 0x580 + SENSOR_ID
dlc -> 6 // request, object, subindex, + 2? Bytes für 16bit Daten
request -> CO_RES_SDO_RD_2
object -> 0x7130
subindex -> 0
data_sdo -> der Messwert

von Hendrik S. (shotar)


Angehängte Dateien:

Lesenswert?

Vielen Dank. Aber worin besteht denn der Unterschied die Daten per SDO 
oder PDU abzufragen?

Ich habe nun aus dem Raspberry und deinem Code etwas zusammengebastelt:
1
#include <stdio.h>
2
#include <stdlib.h>
3
#include <unistd.h>
4
#include <string.h>
5
#include <ctype.h>
6
#include <stdint.h>
7
8
#include <sys/types.h>
9
#include <sys/socket.h>
10
#include <sys/ioctl.h>
11
#include <sys/uio.h>
12
#include <net/if.h>
13
14
#include <linux/can.h>
15
#include <linux/can/raw.h>
16
17
#define node_id 127;
18
19
typedef enum{
20
    COBID_NMT       = 0x000,
21
    COBID_SYNC      = 0x080,
22
    COBID_TIMESTAMP = 0x100,
23
    COBID_EMERGENCY = 0x080,
24
                                    //Prozessdatenobjekte
25
    COBID_RXPDO1    = 0x180,        //RX Empfangs-PDO
26
    COBID_TXPDO1    = 0x200,        //TX Sende-PDO
27
    COBID_RXPDO2    = 0x280,
28
    COBID_TXPDO2    = 0x300,
29
30
    COBID_RXSDO     = 0x580,        //Servicedatenobjekte
31
    COBID_TXSDO     = 0x600,
32
    COBID_NMT_LNG   = 0x700         
33
}CANOPEN_COBID;
34
35
typedef enum{
36
  CMD_TX = 0x22,
37
  CMD_TX_4 = 0x23,
38
  CMD_TX_3 = 0x27,
39
  CMD_TX_2 = 0x2B,
40
  CMD_TX_1 = 0x2F,
41
  CMD_RX = 0x40,
42
  CMD_RX_1 = 0x40,
43
  CMD_RX_2 = 0x40,
44
  CMD_RX_3 = 0x40,
45
  CMD_RX_4 = 0x40
46
}CANOPEN_CMD;
47
48
struct CANopen_message{
49
  CANOPEN_COBID id;
50
  uint8_t dlc;
51
  CANOPEN_CMD cmd;
52
  uint16_t index;
53
  uint8_t subindex;
54
  char data[4];
55
};
56
57
int main (int argc, char * argv[])
58
{
59
  struct sockaddr_can addr;
60
    struct ifreq ifr;
61
    int sfd, ai_val;
62
63
    // open part SocketCAN
64
    sfd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
65
    if(sfd<0){
66
        perror("socket : ");
67
        return -1;
68
    }
69
70
    strcpy(ifr.ifr_name, "can0"); // "can0" is the name of the CAN network interface
71
    ioctl(sfd, SIOCGIFINDEX, &ifr);
72
73
    addr.can_family = AF_CAN;
74
    addr.can_ifindex = ifr.ifr_ifindex;
75
76
    // Bind the socket
77
    if(bind(sfd, (struct sockaddr *)&addr, sizeof(addr))<0){
78
         perror("bind: ");
79
         close(sfd);
80
         return -1;
81
    }
82
83
    // send SDO message for AI value
84
    struct CANopen_message message;
85
    message.id = COBID_TXSDO + node_id;      // SDO-Rx + Node ID
86
    message.dlc = 4;         // length
87
    message.cmd =  CMD_RX;   // read, length unknown
88
    message.index = 0x7130;  // index for AI Process Value 16Bit
89
    message.subindex = 0x01; // Channel 1
90
    write(sfd,&message,sizeof(struct CANopen_message));
91
92
    // receive SDO message
93
    read(sfd,&message,sizeof(struct CANopen_message));
94
    ai_val = (int) (message.data[0] + message.data[1]);
95
    printf("Messwert: %d\n", ai_val);
96
    close(sfd);
97
    return 0;
98
}

Ich kann das Programm leider noch nicht testen, da ich den Sensor erst 
in 2 Wochen bekomme. Da ich auch in C-Programmierung noch recht 
unerfahren bin, bin ich für jegliches Feedback dankbar :)

: Bearbeitet durch User
von Volker S. (vloki)


Lesenswert?

Die PDOs werden automatisch in gewissen Zeitabständen geschickt.
(nachdem man das so konfiguriert hat)

Es gibt keine Requests, Index/Subindex. Die Daten beginnen sofort.
Anhand der PDO Nr. weiß man um welche Daten es sich handelt.

Wenn der DMS Messwert auf PDO1 gemappt ist und der Sensor die Busadresse 
1 hat, dann bekommt man in regelmäßigen Zeitabständen automatisch die 
Nachricht

id -> 0x181
dlc -> 2
daten -> 16bit Messwert

von Steffen R. (steffen_rose)


Lesenswert?

Hendrik S. schrieb:
> Aber worin besteht denn der Unterschied die Daten per SDO
> oder PDU abzufragen?

SDO - Service Data Object :
- Ist ein bestätigter Dienst. Du erhälst eine Bestätigung, ob der Wert 
übernommen wurde, gültig ist oder der Wert überhaupt existiert.
- Kann für beliebige Einträge im Objektverzeichnis entsprechend der 
Zugriffsrechte genutzt werden.
- Es gehen auch Daten größer 8 Byte.
- Es wird ein Gerät direkt angesprochen.
- Wird häufig zur Konfiguration genutzt.

PDO - Process Data Objekt :
- Wird für Echtzeitdaten genutzt.
- Es kommt auf das Mapping der PDOs ab, welche Daten übertragen werden. 
Diese sind nicht beliebig.
- max. 8 Byte
- mehrere Objekte (Werte) gleichzeitig möglich, solange die 8 Byte nicht 
überschritten werden.
- Es gibt verschiedene Events, die das Senden/Empfangen der PDOs steuern 
können.
- PDOs müssen konfiguriert werden
- PDOs gehen nur im NMT Operational State
- PDOs ist Broadcast Dienst, d.h. es kann mehrere Empfänger geben.

(Man verzeihe mir, das einiges etwas lax fomuliert ist.)

von Steffen R. (steffen_rose)


Lesenswert?

http://www.can-cia.org/standardization/specifications/#

Der Standard CiA 301 beschreibt die Kommunikation. Du must dich 
registrieren, ansonsten ist der Download aber kostenfrei.

Der Standard ist weniger zum Lernen geeignet. Aber wenn es um Details 
geht gut zum Nachschlagen. Wer CANopen macht, sollte ihn liegen haben.

von Hendrik S. (shotar)


Lesenswert?

wäre ja auch nicht schlecht. So eine Einstellung macht man am besten 
über einen CANopen Manager am PC? Ich habe mir den Kickdrive Zero dazu 
installiert und die EDS Datei eingelesen.

von Steffen R. (steffen_rose)


Lesenswert?

Hendrik S. schrieb:
> So eine Einstellung macht man

Wenn der Sensor das Speichern der Konfiguration erlaubt, kann man diese 
am PC erstellen, runterschaffen und speichern. Manchmal kann man die 
Geräte auch Selbststartend konfigurieren, d.h. sie gehen eigenständig 
nach NMT Operational.

Hierfür muß man in die Gerätebeschreibung sehen oder den Hersteller 
befragen.

Wenn man sich auskennt, kann man es auch einfach ausprobieren. Als 
Anfänger ist man aber häufig am zweifeln, ob man alles richtig gemacht 
hat.

von Hendrik S. (shotar)


Lesenswert?

Steffen R. schrieb:
> Wenn der Sensor das Speichern der Konfiguration erlaubt, kann man diese
> am PC erstellen, runterschaffen und speichern. Manchmal kann man die
> Geräte auch Selbststartend konfigurieren, d.h. sie gehen eigenständig
> nach NMT Operational.

Ja,  die "NMT Startup" Konfiguration gibt es für die MicroControl 
A/D-Wandler. Dazu gibt es auch den "PDO Event Timer", der wohl für 
meinen Zweck richtig wäre.

Zur Konfiguration kann ich mich dann einfach mit einem USB/CAN-Adapter 
mit dem Gerät verbinden und die entsprechenden Parameter anpassen. Ich 
habe als Freeware CANopen Programm bis jetzt nur den Kickdrive Zero
http://www.kickdrive.de/en/index.html?products_kickdrive_zero.htm
gefunden. Gibt es da auch etwas für Linux?

: Bearbeitet durch User
von Volker S. (vloki)


Lesenswert?

Hast du denn schon einen USB/CAN Adapter?
Du kannst doch die Konfiguration direkt von deiner "Art Raspberry" aus 
durchführen. Dazu müssen nur die entsprechenden Objekte (über SDO) 
richtig beschrieben werden.
Die Kommunikation musst du doch eh programmieren. Die ersten Schritte 
kannst du ja über die SDOs machen. Wenn du das drauf hast, dann ist die 
Konfiguration kein Problem mehr...

von Steffen R. (steffen_rose)


Lesenswert?

Hendrik S. schrieb:
> Zur Konfiguration kann ich mich dann einfach mit einem USB/CAN-Adapter
> mit dem Gerät verbinden

Die API ist nicht standardisiert. Insofern müßtest Du schauen, welchen 
CAN Adapter du nutzen möchtest.

Unter Linux ist die socketCAN API weit verbreitet und Teil des Kernels.

von Hendrik S. (shotar)


Lesenswert?

Volker S. schrieb:
> Hast du denn schon einen USB/CAN Adapter?
ich habe mir gestern den USBtin von Thomas Fischl bestellt:
http://www.fischl.de/usbtin/

Volker S. schrieb:
> Du kannst doch die Konfiguration direkt von deiner "Art Raspberry" aus
> durchführen. Dazu müssen nur die entsprechenden Objekte (über SDO)
> richtig beschrieben werden.
> Die Kommunikation musst du doch eh programmieren. Die ersten Schritte
> kannst du ja über die SDOs machen. Wenn du das drauf hast, dann ist die
> Konfiguration kein Problem mehr...
Naja, wenn ich den Sensor vorher am PC so einstelle, dass er zyklisch 
die Daten per PDO sendet, brauch ich ja auf dem Raspberry nur noch den 
entsprechenden Teil der CAN Message auswerten. Wäre vielleicht etwas 
"eleganter".

Steffen R. schrieb:
> Unter Linux ist die socketCAN API weit verbreitet und Teil des Kernels.
Ja die socketCAN unter Linux kenn ich, aber das ist ja kein CANopen 
Manager mit dem ich ESD Dateien direkt einlesen kann. Aber mit dem 
Windows Tool von Kickdrive werde ich wohl auch zurecht kommen.

: Bearbeitet durch User
von Volker S. (vloki)


Lesenswert?

Hendrik S. schrieb:
> Volker S. schrieb:
>> Hast du denn schon einen USB/CAN Adapter?
> ich habe mir gestern den USBtin von Thomas Fischl bestellt:
> http://www.fischl.de/usbtin/

Na dann hast du ja auch PC Software, welche du verwenden kannst...

von Steffen R. (steffen_rose)


Lesenswert?

Hendrik S. schrieb:
> Ja die socketCAN unter Linux kenn ich, aber das ist ja kein CANopen
> Manager mit dem ich ESD Dateien direkt einlesen kann.

Da aber nicht jedes Tool alle Interfaces unterstützt, ist die 
Entscheidung für eine API (socketCAN, can4linux usw.) relevant.

Freie CANopen Master Tools kenne ich nicht. Und ein freier CANopen 
Master Stack hilft Dir ja nicht weiter.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.