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
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));
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
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
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
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.)
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.
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.
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.
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
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...
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.
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
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...
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.