CANopen Implementierung "nur" für Sensordaten

OP #4289447
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.
OP #4289467
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));
#4289570
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
OP #4289833
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 :)
Angehängte Dateien:
#4289867
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
#4289892
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.)
#4289944
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.
OP #4290730
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?
#4290805
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...
OP #4291171
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.
#4291290
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.

Antwort schreiben

Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt. Bitte hier nur auf die ursprüngliche Frage antworten, für neue Fragen einen neuen Beitrag erstellen.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.