CAN-Kommunikation mit Arduino: Unterschied zwischen den Versionen

Aus HSHL Mechatronik
Zur Navigation springen Zur Suche springen
 
(133 dazwischenliegende Versionen von 3 Benutzern werden nicht angezeigt)
Zeile 1: Zeile 1:
[[Kategorie:Arduino]]
[[Kategorie:Arduino Shield]]
[[Kategorie:CAN]]


==Einleitung / Aufgabenstellung==
In diesem Beitrag wird erläutert, wie mit einem Arduino Daten per CAN-Protokoll übertragen werden können. Dazu wird ein CAN-Shield der Firma Sparkfun eingesetzt. Alle verwendeten Programme bzw. Libraries werden an entsprechender Stelle hochgeladen. Zur Überprüfung der Kommunikation wird ein "CAN-Case" der Firma Vector eingesetzt.


Ziel dieses, im Rahmen des SDE-Praktikums "Systemintegration", ist es einen Arduino in Simulink so zu programmieren, dass mit diesem Sensoren ausgelesen, sowie Aktuatoren angesteuert werden können, wobei alle Informationen über CAN gesendet bzw. empfangen werden sollen. Die dabei gewonnenen Erkenntnisse sollen die Grundlage für ein Carolo-Cup Fahrzeug sein, welches komplett auf Mikrocontrollerbasis aufgebaut wird.
=Einleitung / Aufgabenstellung=
In diesem Beitrag wird erläutert, wie mit einem Arduino, Daten per CAN-Protokoll übertragen werden können. Dazu wird entsprechende Hardware in Form eines CAN-Shieldes eingesetzt.
Die notwendigen Programmteile werden zunächst erklärt und dann in besonders markierten Kästen dargestellt. Zur Überprüfung der Kommunikation wird ein "CAN-Case" der Firma Vector eingesetzt.


==Testen der Hardware in einem CAN-Netzwerkes==
Bei dem Shield handelt es sich um Hardware der Firma "Sparkfun". Dieses arbeitet mit dem Mikrocontroller MCP2515, für welchen bereits fertige Libraries zum Ansteuern zur Verfügung stehen. Weitere technische Daten, sowie Datenblätter und Schaltpläne sind auf der Seite von Sparkfun zu finden: [https://www.sparkfun.com/products/10039 Sparkfun-Homepage]
Um die Funktion der zur verfügung gestellten Hardware zu testen, wird ein kleines CAN-Netzwerk mit zwei Teilnehmern aufgebaut. Ein Teilnehmer ist dabei der Arduino mit einem CAN-Shield der Firma Sparkfun. Der andere Teilnehmer ist ein CAN-Case der Firma Vector. Es soll gewährleistet werden, dass beide Teilnehmer senden und empfangen können. Um dies zu wird ein kleines Programm erstellt, welches eine CAN-Botschaft empfängt und zurückschickt. Das Programm bedient sich dieser [http://code.google.com/p/skpang/downloads/detail?name=Canbus_v4.zip&can=2&q= Library], welcher auf der Sparkfun Seite von diversen Nutzern hochgeladen wurde. Die Library lässt durch einfaches Kopieren in den Arduino Unterordner "Libraries" (z.B. ...\arduino-1.0.3\libraries) einbinden. Der Ablauf des Programmes wird am Ende dieses Kapitels erläutert.


Erste Versuche mit gegebener Hard- und Software blieben erfolglos, nach diversen Tests stellte sich heraus das die Verbindung über das vorgekrimpte Kabel nicht funktioniert. Der Grund dafür ist, dass das Pinning des Sub-D Stecker nicht der üblichen Vector-Norm entspricht. Sollen vorgefertigte Kabel verwendet werden, ist ein Adapter Notwendig. Üblicherweise liegt der CAN-High (im folgenden als CAN-H bezeichnet) an Pin 7 des Sub-D Steckers und CAN-Low (im folgenden als CAN-L bezeichnet) an Pin 2. Auf dem Sparkfun Shield liegt CAN-H auf Pin 1 und CAN-L auf Pin 3. Die folgende Tabelle zeigt die Zuordnung des Adapters:
Ziel dieses, im Rahmen des SDE-Praktikums "Systemintegration", durchgeführten Projektes ist es, Sensoren verschiedener Art mit einem Arduino einzulesen und die Daten per CAN weiterzusenden. Die dabei gewonnenen Erkenntnisse sollen die Grundlage für ein Carolo-Cup Fahrzeug sein, welches komplett auf Mikrocontrollerbasis aufgebaut wird.
 
Autor: [[Benutzer:Jens Henze|Jens Henze]] ([[Benutzer Diskussion:Jens Henze|Diskussion]]) 14:13, 6. Feb. 2014 (CET)
 
=Inbetriebnahme der Hardware=
Um die Funktion der zur Verfügung gestellten Hardware zu testen, wird ein CAN-Netzwerk mit zwei Teilnehmern aufgebaut. Einer der Teilnehmer ist ein Arduino, ausgestattet mit einem CAN-Shield der Firma Sparkfun. Der Andere ist ein CAN-Case der Firma Vector. Es soll gewährleistet werden, dass beide Teilnehmer senden und empfangen können. Um dies zu testen werden kleine Testprogramme erstellt. Das Programm für den Arduino bedient sich dieser [http://code.google.com/p/skpang/downloads/detail?name=Canbus_v4.zip&can=2&q= Library], welcher auf der Sparkfun Seite von diversen Nutzern hochgeladen wurde. Die Library lässt durch einfaches Kopieren in den Arduino Unterordner "Libraries" (z.B. ...\arduino-1.0.3\libraries) einbinden. Um mit dem CAN-Case senden und empfangen zu können, wird ein CAPL Programm erstellt. Beide Programme werden in einem separaten Kapitel an späterer Stelle erläutert.
 
Erste Versuche mit gegebener Hard- und Software blieben erfolglos. Nach diversen Tests stellte sich heraus, dass die Verbindung über das vorgekrimpte Kabel nicht funktioniert. Der Grund dafür ist, dass das Pinning des Sub-D Stecker nicht der üblichen Norm der Firma Vector entspricht. Sollen hier vorgefertigte Kabel verwendet werden, ist ein Adapter Notwendig, der das Pinning entsprechend umlegt.  
Üblicherweise liegt der CAN-High (im Folgenden als CAN-H bezeichnet) an Pin 7 des Sub-D Steckers und CAN-Low (im Folgenden als CAN-L bezeichnet) an Pin 2. Auf dem Sparkfun Shield liegt CAN-H auf Pin 3 und CAN-L auf Pin 5. Die folgende Tabelle zeigt die Zuordnung des Adapters:
{| class="wikitable"
{| class="wikitable"
!Bezeichnung
!Bezeichnung
Zeile 16: Zeile 26:
| CAN-H
| CAN-H
| Pin 7
| Pin 7
| Pin 1
| Pin 3
|-
|-
| CAN-L
| CAN-L
| Pin 2
| Pin 2
| Pin 3
| Pin 5
|-
|-
|}
|}
Das Arduino-Shield stellt an anderer Stelle CAN-H und CAN-L zur verfügung, über diese Kontakte könnte über einadrige Leitungen einer direkte Verbindung zu dem CAN-Case hergestellt werden. Dabei ist zu beachten das ein Aufstecken des CAN-Shieldes nur bei dem Arduino UNO möglich ist. Möchte man einen Arduino Mega 2560 verwenden, müssen die Pins des Shieldes mit Leitungen zu bestimmten Pins des Arduino Megas geleitet werden. Die Pin-Zuordnung dieser Pins zeigt folgende Tabelle:
--[[Benutzer:Ulrich Schneider|Ulrich Schneider]] ([[Benutzer Diskussion:Ulrich Schneider|Diskussion]]) 17:47, 22. Jan. 2014 (CET)
SCH: Laut Schematic sind es CANH Pin3 und CANL Pin5. Was ist richtig?
[https://www.sparkfun.com/datasheets/DevTools/Arduino/canbus_shield-v12.pdf Schematic]
 
Alternativ stellt das Arduino-Shield an anderer Stelle CAN-H und CAN-L zur Verfügung, über diese Kontakte kann mit zwei einadrigen Leitungen einer direkte Verbindung zu dem CAN-Case hergestellt werden.
 
Ein einfaches Aufstecken des CAN-Shieldes ist nur bei dem Arduino UNO möglich. Soll ein Arduino Mega 2560 vewendet werden, müssen die Pins des Shieldes mit Leitungen zu bestimmten Pins des Arduino Mega geleitet werden. Der Grund dafür ist, dass Arduino und Shield über eine sogenannte SPI Schnittstelle kommunizieren. SPI steht für "Serial Peripheral Interface" und ist ein serieller Datenbus mit einer Master-Slave Architektur. Die Pins, die die Schnittstelle zum SPI zur Verfügung stellen, liegen beim Arduino Mega 2560 anders als beim UNO. Die Pin-Zuordnung dieser Pins zeigt folgende Tabelle:
{| class="wikitable"
{| class="wikitable"
!CAN-Shield
!CAN-Shield
Zeile 70: Zeile 86:
|-
|-
|}
|}
alle anderen Pins sind zum Betrieb des Shields nicht notwendig.
Alle anderen Pins sind zum Betrieb des Shields nicht notwendig.
 
Bei dem elektronischen Aufbau dieses Versuches ist zu beachten, dass die Enden der CAN-Leitungen mit einem 120 Ohm Widerstand zwischen CAN-H und CAN-L abgeschlossen werden müssen. Fehlen diese Widerstände kommt es zu Reflektionen der Signale an den Leitungsenden, wodurch im schlimmsten Fall ein Kommunizieren auf der Leitung unmöglich wird. Das folgende Bild zeigt Schematisch den Aufbau:
 
[[Datei:CAN-Netzwerk_aufbau.jpg|500px|center]]
Autor: [[Benutzer:Jens Henze|Jens Henze]] ([[Benutzer Diskussion:Jens Henze|Diskussion]]) 14:13, 6. Feb. 2014 (CET)
 
=Einlesen von Sensoren=
In diesem Abschnitt wird erläutert, wie mit einem Arduino lineare Sensoren und PWM-Signale eingelesen werden können.


Bei dem elektronischen Aufbau dieses Versuches ist zu beachten das die Enden der CAN-Leitungen mit einem 120 Ohm Widerstand zwischen CAN-H und CAN-L abgeschlossen werden müssen. Wird dies nicht gemacht kommt es zu Reflektionen der Signale an den Leitungenden wodurch im schlimmsten Fall ein Kommunizieren auf der Leitung unmöglich wird. Das folgende Bild zeigt Schematisch den Aufbau:
==Einlesen linearer Sensoren==
Zum Einlesen von analogen Werten, ist der Arduino mit Analog/Digitalwandlern ausgestattet. Als Referenzspannung zum Messen dient dabei die von dem Arduino zur Verfügung gestellte 5V Spannungsversorgung. Die Wandler können Werte von 0-1023 vom Typ "Integer" (positive Ganzzahlen) zurückgeben. Damit ergibt sich eine Auflösung von 5V/1023=0,0049V=4,9mV pro Schritt. Im Programm können die analogen Eingänge mit dem Befehl "analogRead()" angesprochen werden. Die folgende, mit dem Tool "Fritzing" erstellte, Abbildung zeigt einen Versuchsaufbau zur Simulation eines Sensors. Dabei dient das Potentiometer als Sensor. Die LED wird zu Anzeigezwecken verbaut. Diese kann genau so gut durch einen Motor mit entsprechendem Treiber ersetzt werden (dies wird an späterer Stelle gezeigt).  


==Einlesen von Sensoren==
[[Datei:Aufbau_Sensor_Aktuator_Schaltplan.jpg|400px|center]]


==Ansteuerung von Aktuatoren==
==Einlesen eines PWM-Signals==
Eingabegeräte, Sensoren und Aktuatoren arbeiten oftmals mit PWM-Signalen. Um diese mit einfachen Mitteln mit dem Arduino einlesen zu können, können Interrupts benutzt werden. Der Arduino kann an seinen Interrupts zwischen steigenden und fallenden Flanken unterscheiden. Dies kann genutzt werden, um eine Zeitmessung während der High Zeit eines PWM Signals durchzuführen. Mithilfe der bekannten Frequenz kann so das High-Low Verhältnis des Signals bestimmt werden. Ist die Frequenz unbekannt kann Sie durch die Zeitmessung zwischen zwei High bzw. Low Flanken bestimmt werden. Als Anschauungsbeispiel soll hier das PWM-Signal einer Fernsteuerung erfasst werden. Die folgende Abbildung zeigt eine Oszilloskop-Aufnahme dieser Fernsteuerung. In diesem Fall ist das Lenk- und Geschwindigkeitssignal zu erkennen. "Thigh" in der Abbildung zeigt die High-Zeit des Signals. "Tfreq" zeigt die Zeit, die gemessen werden muss, falls die Frequenz bestimmt werden soll. Mit dieser Periodendauer lässt sich nach der Formel f=1/Tfreq die Frequenz bestimmen. Das Verhältnis Thigh/Tfreq ist dann das High-Low Verhältnis des PWM-Signals.
[[Datei:PWM.png|600px|center]]
Die nächste Abbildung zeigt das PWM-Signal der Gashebelstellung der Fernsteuerung, wenn diese sich auf Neutral und Maximalstellung Rückwärts bzw. Vorwärts befindet. Es ist zu sehen, dass das Signal bei Neutralstellung eine High-Zeit von 1,5ms, bei Rückwärts 1ms und bei Vorwärts 2ms beträgt.
[[Datei:PWM_brems_gas.png|600px|center]]
Um die Auf- und Absteigenden Flanken mit dem Arduino gesondert erkennen zu können, wird das zu messende Signal auf zwei Interrupt Pins gelegt. Die folgende Abbildung zeigt die verwendbaren Pins beim Arduino-Mega.
[[Datei:Interrupts.png|600px|center]]
Im Folgenden wird ein einfaches Programm gezeigt, welches mithilfe der Pins die High-Zeit des PWM-Signals bestimmt. In diesem Fall werden Auf- und Absteigende Flanken separat detektiert. Dies würde sich auch mit einem Pin realisieren lassen, da hier allerdings zwei PWM Signale bestimmt und gleich angeordnet werden sollen, ist diese Info von großer Bedeutung. Zunächst werden die notwendigen, globalen Variablen als Volatile und long definiert um einen Zeitwert in Mikrosekunden speichern zu können (der Grund für Volatile wird später erläutert):
 
<code>
  volatile unsigned long Anfang_PWM = 0; //Variable zur ermittlung der Impulslaenge der Beschleunigung
  volatile unsigned long Mitte_PWM = 0;
  volatile unsigned long PWM_Lenk = 0;  //Variable zur ermittlung der Impulslaenge des Lenkwinkels
  volatile unsigned long PWM_Geschw = 0;
</code>
 
Danach werden die Interrupts in der "Setup" Funktion festgelegt. Dazu wird für jeden Interrupt die Funktion "attachInterrupt" aufgerufen. Als erster Wert wird dabei Interrupt Nummer übergeben. Die Zuordnung welche Nummer zu welchem Pin gehört kann der vorherigen Abbildung entnommen werden. Als nächstes wird die Funktion angegeben die in dem Interruptfall ausgeführt werden soll. Als letzter Punkt wird angegeben ob auf eine steigende, fallende oder wechselnde Flanke reagiert werden soll (Steigend=RISING, Fallend=FALLING, Wechselnd=CHANGING).
 
<code>
  void setup()
  {
  attachInterrupt(2, PWM_down_Geschw, FALLING); //Interrupt zur identifikation einer fallenden Flanke
  attachInterrupt(4, PWM_up_Lenk, RISING); //Interrupt zur identifikation einer steigenden Flanke
  attachInterrupt(5, PWM_down_Lenk, FALLING); //Interrupt zur identifikation einer fallenden Flanke Lenkung
  }
</code>
Nun werden die entsprechenden Funktionen erstellt. Diese besitzen weder Übergabe- noch Rückgabewert. Wenn Werte gespeichert werden sollen, muss dies über globale Variablen geschehen. Dabei ist zu beachten, dass diese als "Volatile" definiert worden sind. Sonst kann es passieren, dass der Compiler die Variablen "wegoptimiert", da sie nicht im Hauptprogramm auffindbar sind. Den Funktionen ist zu entehmen, dass beim Signalbeginn (in diesem Fall das Lenksignal) die Prozessorlaufzeit gespeichert wird. Fällt die Flanke ab wird die Zeitdifferenz zwischen dem Abfallenden Lenksignal und dem Signalstart gemessen. Da dieser Punkt zeitgleich mit der Aufsteigenden Flanke des Beschleunigungssignals auftritt, kann der Zeitpunkt als Startsignal für das Beschleunigungs-PWM-Signal genutzt werden (so kann ein Interrupt-Pin gespart werden). Mit der letzten Funktion wird die abfallende Flanke des Beschleunigungssignals gemessen, wodurch wieder eine Zeitdifferenz ermittelt werden kann. Somit ist nun die High-Zeit beider PWM-Signale bestimmt.
<code>
 
//Interruptroutinen
 
  void PWM_up_Lenk()
  {
  cli();
  Anfang_PWM=micros();
  sei();
  }
 
  void PWM_down_Lenk()
  {
  cli();
  Mitte_PWM=micros();
  PWM_Lenk=Mitte_PWM-Anfang_PWM;
  sei();
  }
 
  void PWM_down_Geschw()
  {
  cli();
  PWM_Geschw=micros()-Mitte_PWM;
  sei();
  }
 
</code>
Autor: [[Benutzer:Jens Henze|Jens Henze]] ([[Benutzer Diskussion:Jens Henze|Diskussion]]) 14:13, 6. Feb. 2014 (CET)
 
=Ansteuerung von Aktuatoren=
Um einen Aktuator mit dem Arduino ansteuern zu können, ist eine Zusatzschaltung (auch Treiberschaltung genannt) notwendig. Grund dafür ist, dass der Arduino an seinen digitalen Ausgängen nur begrenzt belastet werden kann. Dieses Problem kann z.B. mit Leistungstransistoren, Mosfets oder einem Relais gelöst werden. Da die meisten Relais einen höheren Strom benötigen, als der Arduino liefern kann und die Spule eine große Störquelle darstellt, wird an dieser Stelle eine Lösung mit einem Mosfet realisiert. Analog dazu kann genau so gut ein Leistungstransistor eingesetzt werden.
 
Die folgende Schaltung zeigt die Realisierung einer Motoransteuerung mithilfe eines Mikrocontrollers. Da ein Mikrocontrollerausgang in der Regel nur geringe Ströme liefern kann, ist eine Treiberschaltung mit einem Transistor notwendig. Wechselt in dieser Schaltung der Pin des Mikrocontrollers (dargestellt durch die Spannungsquelle "Digitaler_Pin") auf 5V liegen an den Widerständen R1 und R2 jeweils 2,5 V an. Da der Widerstand R1 parallel zu der Basis-Emitter Strecke des Transistors liegt, ist die notwendige Spannung des Transistors zum Durchschalten erreicht. Diese liegt in der Regel bei mind. 0,7V. Das Durchschalten des Transistors verursacht eine Spannung mit Höhe der Spannungsquelle "Uversorgung" an dem Widerstand R3. Dieser wiederum liegt parallel zur Gate-Source Strecke des eingesetzten P-Kanal Mosfets, wodurch dieser durchschaltet. Nun ist ein Stromfluss über den Mosfet zum Motor (dargestellt durch den Widerstand Rm und der Spule Lm) möglich.
 
[[Datei:Digit_Aktuator_Anschaltung.png|500px|center]]
 
Autor: [[Benutzer:Jens Henze|Jens Henze]] ([[Benutzer Diskussion:Jens Henze|Diskussion]]) 14:14, 6. Feb. 2014 (CET)
 
=Kommunizieren per CAN=
Es folgt eine Beschreibung zum Aufbau einer CAN-Kommunikation zwischen dem Arduino und dem CAN-Case. Zum Schluss wird eine Möglichkeit beschrieben eine Kommunikation auf der Basis von Matlab-Simulink zu erstellen.
 
==Konfiguration des CAN-Case==
Um mit dem CAN-Case zu kommunizieren wird hier die Software "CANalayzer" eingesetzt. Als ersten Schritt muss der Messaufbau in der Software eingestellt werden. Dieser kann unter dem Reiter "Configuration" am unteren Teil des Fensters von CANalayzer gefunden werden. Hier wir zunächst der CAN-Kanal und die Baudrate eingestellt. Die Einstellmöglichkeiten können durch einen Doppelklick auf das Kartensymbol oder unter Konfiguration/Netzwerk-Hardware-Konfiguration erreicht werden. Sind diese Einstellungen getroffen, kann der CAN-Bus mitgeloggt werden. Alle Botschaften auf dem CAN-Bus können unter dem Reiter "Trace" im unteren Bereich des Fensters angeschaut werden.
 
Soll das CAN-Case ebenfalls Botschaften senden, müssen in dem Messaufbau Generatorblöcke eingefügt werden. Diese können zyklisch oder auf Knopfdruck Botschaften senden. Alternativ können diese Blöcke mit der C-ähnlichen Programmiersprache "CAPL" programmiert werden. Dies ermöglicht z.B. das Reagieren auf bestimmte Botschaften.


==Umsetzung der Aufgabe in C==
==Umsetzung der Aufgabe in C==
Wie zuvor beschrieben dient als Grundlage die Bibliothek "mcp2515.h". Diese bietet für den auf dem Shield eingesetzten Microcontroller zugeschnittene Funktionen. Dafür müssen die entsprechenden Dateien wie oben beschrieben in den Arduino Ordner kopiert werden. Im Quelltext kann die Datei dann mit #include <mcp2515.h> eingebunden werden.
In der Library wird eine Struktur mit dem Namen "tCAN" zum erstellen von CAN-Botschaften definiert. Diese Klasse hat die Attribute: ID, rtr, length und Data. Diese Werte sind mindestens nötig um eine Nachricht nach dem Standard des CAN Protokolls zu versenden. Wichtig dabei ist, dass beim CAN-Bus die 0 Dominant ist und 1en überschreibt.
Die ID dient zur Identifizierung von CAN Botschaften, gleichzeitig können dadurch Nachrichten priorisiert werden. Bei der CAN Kommunikation findet regelmäßig eine Arbitrierungsphase statt. Das bedeutet es wird ermittelt, welche CAN Botschaft die höchste Priorität hat und somit als erstes übertragen wird. Dies wird so realisiert, dass alle Bus- Teilnehmer gleichzeit versuchen eine Botschaft zu senden. Die am höchsten priorisierte Botschaft ist die, dessen ID die meisten 0en enthält. Wird eine Botschaft eines Teilnehmers von einer 0 eines anderen Teilnehmers überschreiben, registriert der entsprechende Teilnehmer das und bricht den Sendevorgang ab.
Mit dem rtr Bit wird angegeben ob es sich bei der CAN Botschaft um einen sogenannten "Remote Frame" handelt. Mit einem Remoteframe können Daten von einem anderen Busteilnehmer angefordert werden. In dem Fall der Übertragung von Sensorwerten, muss dieses Bit also auf 0 gesetzt werden.
Das length Bit gibt an, wie lang das Datenfeld der CAN Botschaft ist. Die hier eingesetzte Library benötigt diese Angabe als Ganzzahl, welche die Anzahl der Bytes angibt. Der Arduino kann an seinen analogen Eingängen Werte von 0 - 1023 einlesen. Um dies per CAN zu übertragen, benötigt man hier also 2 Bytes (2Bytes=FFFF=65535).
In dem Datenfeld werden die eigentlich zu übertragenden Daten angegeben.
Die entsprechende Sendenfunktion sieht so aus:
  void CAN_send(int ID, int length,unsigned char *Data)  //Funktion zum senden einer CAN-Botschaft
  {
    message.id = ID;                            //Zuweisen der ID in die Strukturvariable
    message.header.rtr = 0;                    //Zuweisen des rtr in den Header der Strukturvariable
    message.header.length = length;            //Zuweisen der lenth in den Header der Strukturvariable
 
    for( int i = 0; i < length; i++ )          //kopieren des Datenarrays in das Datenarrays der Struktur
      {
      message.data[i] = Data[i];
      }
 
    mcp2515_send_message(&message);            //Funktion aus Library die das senden der fertigen Botschaft übernimmt
  }
Daten werden mit dem Befehl
    ret = mcp2515_get_message(&msg_in);      //Emfpange Botschaft (Funktion aus Library)
empfangen und befinden sich dann im Buffer. Um die Daten aus der Botschaft zu entnehmen, muss auf das Datenfeld der zuvor erzeugten Strukturvariable "msg_in" zugegriffen werden. Im Quellcode sieht dies wie folgt aus.
  Aktuator_an=msg_in.data[0];            //Lese Botschaft aus (überträgt in diesem Fall 1 oder 0
Quelle:Vector, Bussysteme und Bordnetze Vorlesung 10
==Sensorwerte von Arduino empfangen und Antwort senden==
Nachdem es nach der Beschreibung aus dem vorherigen Kapitel nun möglich ist, Daten mit dem Arduino zu senden und empfangen, sollen diese Daten nun von einer anderen Station verarbeitet werden. Hierzu wird ein Can-Case der Firma Vektor verwendet. Dieses wird mithilfe eines "CAPL" Programmes so programmiert, dass dieses einen Wert von dem Arduino empfängt und eine Antwort als 0 oder 1 zurücksendet. Dies könnte z.B. dazu dienen, einen Aktuator einzuschalten.
Das folgende Programm soll auf die Beispielnachricht 0x7B reagieren. Die Zahl ist dabei hexadezimal angegeben und ist nur als Beispiel gewählt. Dies wird durch den Befehl "on message 0x7B{...}" angegeben.
  on message 0x7B
  {
  (...)
  }
Zunächst werden dann alle notwendigen Variablen angegeben. In CAPL werden Can-Botschaften wie Struktur-Variablen behandelt. Hier wird dazu mithilfe von "message 1 msg" eine Nachricht mit dem Namen msg und der Nummer 1 erstellt. Im weiteren Verlauf wird das erste Datenbyte mit 0 gefüllt, und das DLC (Data length Byte) auf 1 gesetzt.
    message 1 msg;
    msg.byte(0) = 0;
    msg.DLC =1;
Anschließend werden drei Variablen vom Typ "Integer" erzeugt, um Daten vom Arduino empfangen zu können. In diesem Fall soll die vom Arduino gesendete Botschaft 2 Bytes lang sein, da ein Wert aus einem Analog-Digital-Wandler gesendet werden soll, dessen Maximallänge 1023 entspricht. Die Variable "Ges" soll dann den Wert enthalten, wie dieser vom Arduino gesendet wurde.
   
    int wert1;
    int wert2;
    int Ges;
Mithilfe von this.byte wird das entsprechende Datenbyte der am Anfang festgelegten Botschaft ausgelesen. Diese werden hier in die entsprechenden Variablen geschrieben. In der anschließenden Rechnung werden die Datenbytes wieder in einen Wert von 0-1023 umgerechnet.
 
    wert1=this.byte(0);
    wert2=this.byte(1);
    Ges=wert1*256+wert2;
Die folgende Auswertung legt nun einen Schwellwert von 1000 fest. Wenn also ein Wert vom Arduino empfangen wurde, der größter als 1000 ist, wird das Byte von "msg" auf 1 gesetzt.
    if(Ges>1000)
    {
      msg.byte(0) = 1; 
    }
Mit dem Befehl "output(msg)" wird diese Botschaft dann gesendet.
    output(msg);
Eine Beispielanwendung für solch einen Programmablauf könnte eine Distanzmessung mit einem Infrarotsensor sein, wobei die Verarbeitung zentral verlaufen soll. Dabei könnte der Infrarotsensor mit einem Mikrocontroller ausgewertet und auf desssen Signal ein Motor gestoppt werden.


==Umsetzung der Aufgabe in Simulink==
==Umsetzung der Aufgabe in Simulink==
Eine Anleitung zur Entwicklung von Arduino Treibern in Simulink befindet sich unter folgendem [http://www.mathworks.de/matlabcentral/fileexchange/39354-device-drivers Link]
Aus Zeitgründen konnte die Aufgabe leider nicht mehr komplett in Simulink gelöst werden. Es folgt daher in diesem Abschnitt nur der erste Schritt dazu. Eine Anleitung zur Entwicklung von Arduino Treibern in Simulink befindet sich unter folgendem [http://www.mathworks.de/matlabcentral/fileexchange/39354-device-drivers Link].
 
Laut dieser Anleitung sollte es möglich sein, mithilfe des S-Function Builder aus der Matlab-Simulink Library, auf dem Arduino lauffähige Programme zu erstellen. Theoretisch können die Arduino Programme Stückweise, eins zu eins an die entsprechende Stelle in dem S-Function-Block kopiert werden. Die Eingänge müssen dabei allerdings im S-Function-Block unter "Data Properties" voreingestellt werden. Abbildung 1 zeigt die Definition der Eingänge, wie sie für das zuvor erstellte C-Programm notwendig wären.
 
[[Datei:S-Function Input.png|500px|center]]
 
In der Simulink-Ansicht sieht dies dann wie folgt aus:
 
[[Datei:S-Function_Oberfläche.png|500px|center]]
 
So wie in einem standard C Programm werden nun unter dem Reiter Libraries in dem Textfeld "Includes" alle zu includierenden Datien angegeben. Für den Fall dieses C-Programmes sind es die Libraries "Arduino.h" und "mcp2515.h". Weiterhin werden an dieser Stelle alle globalen Variablen definiert. Für das C-Programm ist dies die Definition der Can-Botschaft "message" Die folgende Abbildung zeigt diese Einstellungen.
 
[[Datei:S-Function_Libraries.png|500px|center]]
 
Unter dem Reiter "Outputs" wird das eigentliche Hauptprogramm aufgeführt. Also alles was in Arduino Programmen unter "void loop(){(...]}" steht. Die in der folgenden Abbildung gezeigte Variable xD[0] ist eine interne Matlab Variable. Da diese hier auf 1 abgefragt wird, wird dieser Programmteil als Endlosschleife ausgeführt. Dazu im nächsten Abschnitt mehr.
 
[[Datei:S-Function_Output.png|500px|center]]
 
Analog zum letzten Abschnitt werden unter dem Reiter "Discrete Update" alle Programmteile eingefügt, welche im Arduino Programm unter "void setup(){(...)}" stehen. Die folgende Abbildung zeigt, dass die zuvor erwähnte Variable xD[0] hier auf 0 abgefragt wird. Dies ist nur im Einschaltmoment der Fall, da am Ende dieses Programmabschnittes die Variable auf 1 gesetzt wird. So wird erreicht, dass dieser Teil nur einmalig und der Programmteil von "Outputs" dauerhaft ausgeführt wird.
 
[[Datei:S-Function_DiscreteUpdate.png|500px|center]]
 
Wird nun auf den Button Build des S-Function Menüs geklickt, wird der gesamte Block compiliert. Dazu muss allerdings ein C-Compiler für Matlab installiert sein. Dieser kann mithilfe von mex -setup im Matlab Command Fenster initialisiert werden.
Wurde die S-Function compiliert sind in dem Arbeitsverzeichnis mehrere Dateien zu finden, darunter auch eine Datei namens "sfcn_exout_slsp_wrapper.c". Schaut man sich diese Datei an, ist die Struktur des "klassischen" Arduino Programmes wieder zu erkennen. Hier kann also verglichen werden ob das Programm auch so compiliert wurde, wie es soll.
Die Übertragung der S-Function findet wie mit den Arduino-Standard-Programmen über "Run on target Hardware" statt.
 
Autor: [[Benutzer:Jens Henze|Jens Henze]] ([[Benutzer Diskussion:Jens Henze|Diskussion]]) 14:13, 6. Feb. 2014 (CET)
 
=Quellen=
 
Projektbericht von Jens Henze zum Sommersemester 2013
 
http://www.mathworks.de/matlabcentral/fileexchange/39354-device-drivers
 
https://www.sparkfun.com/products/10039
 
http://www.arduino.cc
 
Vorlesung 10 Bussysteme Boardnetze und Diagnose Autor: Axel Thümmler
 
 
Autor: [[Benutzer:Jens Henze|Jens Henze]] ([[Benutzer Diskussion:Jens Henze|Diskussion]]) 14:11, 6. Feb. 2014 (CET)
<!-- Gehört in die Diskussion
== Verbesserungsvorschläge zum Artikel ==
--[[Benutzer:Ulrich Schneider|Ulrich Schneider]] ([[Benutzer Diskussion:Ulrich Schneider|Diskussion]]) 13:36, 4. Feb. 2014 (CET)
* Welches Shield wurde eingesetzt? Link auf Datenblätter fehlern
-->
 
----
→ zurück zum Hauptartikel: [[Praktikum_SDE|Praktikum SDE]]

Aktuelle Version vom 12. Februar 2019, 16:01 Uhr


Einleitung / Aufgabenstellung

In diesem Beitrag wird erläutert, wie mit einem Arduino, Daten per CAN-Protokoll übertragen werden können. Dazu wird entsprechende Hardware in Form eines CAN-Shieldes eingesetzt. Die notwendigen Programmteile werden zunächst erklärt und dann in besonders markierten Kästen dargestellt. Zur Überprüfung der Kommunikation wird ein "CAN-Case" der Firma Vector eingesetzt.

Bei dem Shield handelt es sich um Hardware der Firma "Sparkfun". Dieses arbeitet mit dem Mikrocontroller MCP2515, für welchen bereits fertige Libraries zum Ansteuern zur Verfügung stehen. Weitere technische Daten, sowie Datenblätter und Schaltpläne sind auf der Seite von Sparkfun zu finden: Sparkfun-Homepage

Ziel dieses, im Rahmen des SDE-Praktikums "Systemintegration", durchgeführten Projektes ist es, Sensoren verschiedener Art mit einem Arduino einzulesen und die Daten per CAN weiterzusenden. Die dabei gewonnenen Erkenntnisse sollen die Grundlage für ein Carolo-Cup Fahrzeug sein, welches komplett auf Mikrocontrollerbasis aufgebaut wird.

Autor: Jens Henze (Diskussion) 14:13, 6. Feb. 2014 (CET)

Inbetriebnahme der Hardware

Um die Funktion der zur Verfügung gestellten Hardware zu testen, wird ein CAN-Netzwerk mit zwei Teilnehmern aufgebaut. Einer der Teilnehmer ist ein Arduino, ausgestattet mit einem CAN-Shield der Firma Sparkfun. Der Andere ist ein CAN-Case der Firma Vector. Es soll gewährleistet werden, dass beide Teilnehmer senden und empfangen können. Um dies zu testen werden kleine Testprogramme erstellt. Das Programm für den Arduino bedient sich dieser Library, welcher auf der Sparkfun Seite von diversen Nutzern hochgeladen wurde. Die Library lässt durch einfaches Kopieren in den Arduino Unterordner "Libraries" (z.B. ...\arduino-1.0.3\libraries) einbinden. Um mit dem CAN-Case senden und empfangen zu können, wird ein CAPL Programm erstellt. Beide Programme werden in einem separaten Kapitel an späterer Stelle erläutert.

Erste Versuche mit gegebener Hard- und Software blieben erfolglos. Nach diversen Tests stellte sich heraus, dass die Verbindung über das vorgekrimpte Kabel nicht funktioniert. Der Grund dafür ist, dass das Pinning des Sub-D Stecker nicht der üblichen Norm der Firma Vector entspricht. Sollen hier vorgefertigte Kabel verwendet werden, ist ein Adapter Notwendig, der das Pinning entsprechend umlegt. Üblicherweise liegt der CAN-High (im Folgenden als CAN-H bezeichnet) an Pin 7 des Sub-D Steckers und CAN-Low (im Folgenden als CAN-L bezeichnet) an Pin 2. Auf dem Sparkfun Shield liegt CAN-H auf Pin 3 und CAN-L auf Pin 5. Die folgende Tabelle zeigt die Zuordnung des Adapters:

Bezeichnung Vector-Pinning Sparkfun-Pinning
CAN-H Pin 7 Pin 3
CAN-L Pin 2 Pin 5

--Ulrich Schneider (Diskussion) 17:47, 22. Jan. 2014 (CET) SCH: Laut Schematic sind es CANH Pin3 und CANL Pin5. Was ist richtig? Schematic

Alternativ stellt das Arduino-Shield an anderer Stelle CAN-H und CAN-L zur Verfügung, über diese Kontakte kann mit zwei einadrigen Leitungen einer direkte Verbindung zu dem CAN-Case hergestellt werden.

Ein einfaches Aufstecken des CAN-Shieldes ist nur bei dem Arduino UNO möglich. Soll ein Arduino Mega 2560 vewendet werden, müssen die Pins des Shieldes mit Leitungen zu bestimmten Pins des Arduino Mega geleitet werden. Der Grund dafür ist, dass Arduino und Shield über eine sogenannte SPI Schnittstelle kommunizieren. SPI steht für "Serial Peripheral Interface" und ist ein serieller Datenbus mit einer Master-Slave Architektur. Die Pins, die die Schnittstelle zum SPI zur Verfügung stellen, liegen beim Arduino Mega 2560 anders als beim UNO. Die Pin-Zuordnung dieser Pins zeigt folgende Tabelle:

CAN-Shield Arduino-Mega Arduino-UNO
VIN VIN VIN
GND GND GND
GND GND GND
+5V +5V +5V
+3,3V +3,3V +3,3V
RST RST RST
D10 D50 D10
D11 D51 D11
D12 D52 D12
D13 D53 D13

Alle anderen Pins sind zum Betrieb des Shields nicht notwendig.

Bei dem elektronischen Aufbau dieses Versuches ist zu beachten, dass die Enden der CAN-Leitungen mit einem 120 Ohm Widerstand zwischen CAN-H und CAN-L abgeschlossen werden müssen. Fehlen diese Widerstände kommt es zu Reflektionen der Signale an den Leitungsenden, wodurch im schlimmsten Fall ein Kommunizieren auf der Leitung unmöglich wird. Das folgende Bild zeigt Schematisch den Aufbau:

Autor: Jens Henze (Diskussion) 14:13, 6. Feb. 2014 (CET)

Einlesen von Sensoren

In diesem Abschnitt wird erläutert, wie mit einem Arduino lineare Sensoren und PWM-Signale eingelesen werden können.

Einlesen linearer Sensoren

Zum Einlesen von analogen Werten, ist der Arduino mit Analog/Digitalwandlern ausgestattet. Als Referenzspannung zum Messen dient dabei die von dem Arduino zur Verfügung gestellte 5V Spannungsversorgung. Die Wandler können Werte von 0-1023 vom Typ "Integer" (positive Ganzzahlen) zurückgeben. Damit ergibt sich eine Auflösung von 5V/1023=0,0049V=4,9mV pro Schritt. Im Programm können die analogen Eingänge mit dem Befehl "analogRead()" angesprochen werden. Die folgende, mit dem Tool "Fritzing" erstellte, Abbildung zeigt einen Versuchsaufbau zur Simulation eines Sensors. Dabei dient das Potentiometer als Sensor. Die LED wird zu Anzeigezwecken verbaut. Diese kann genau so gut durch einen Motor mit entsprechendem Treiber ersetzt werden (dies wird an späterer Stelle gezeigt).

Einlesen eines PWM-Signals

Eingabegeräte, Sensoren und Aktuatoren arbeiten oftmals mit PWM-Signalen. Um diese mit einfachen Mitteln mit dem Arduino einlesen zu können, können Interrupts benutzt werden. Der Arduino kann an seinen Interrupts zwischen steigenden und fallenden Flanken unterscheiden. Dies kann genutzt werden, um eine Zeitmessung während der High Zeit eines PWM Signals durchzuführen. Mithilfe der bekannten Frequenz kann so das High-Low Verhältnis des Signals bestimmt werden. Ist die Frequenz unbekannt kann Sie durch die Zeitmessung zwischen zwei High bzw. Low Flanken bestimmt werden. Als Anschauungsbeispiel soll hier das PWM-Signal einer Fernsteuerung erfasst werden. Die folgende Abbildung zeigt eine Oszilloskop-Aufnahme dieser Fernsteuerung. In diesem Fall ist das Lenk- und Geschwindigkeitssignal zu erkennen. "Thigh" in der Abbildung zeigt die High-Zeit des Signals. "Tfreq" zeigt die Zeit, die gemessen werden muss, falls die Frequenz bestimmt werden soll. Mit dieser Periodendauer lässt sich nach der Formel f=1/Tfreq die Frequenz bestimmen. Das Verhältnis Thigh/Tfreq ist dann das High-Low Verhältnis des PWM-Signals.

Die nächste Abbildung zeigt das PWM-Signal der Gashebelstellung der Fernsteuerung, wenn diese sich auf Neutral und Maximalstellung Rückwärts bzw. Vorwärts befindet. Es ist zu sehen, dass das Signal bei Neutralstellung eine High-Zeit von 1,5ms, bei Rückwärts 1ms und bei Vorwärts 2ms beträgt.

Um die Auf- und Absteigenden Flanken mit dem Arduino gesondert erkennen zu können, wird das zu messende Signal auf zwei Interrupt Pins gelegt. Die folgende Abbildung zeigt die verwendbaren Pins beim Arduino-Mega.

Im Folgenden wird ein einfaches Programm gezeigt, welches mithilfe der Pins die High-Zeit des PWM-Signals bestimmt. In diesem Fall werden Auf- und Absteigende Flanken separat detektiert. Dies würde sich auch mit einem Pin realisieren lassen, da hier allerdings zwei PWM Signale bestimmt und gleich angeordnet werden sollen, ist diese Info von großer Bedeutung. Zunächst werden die notwendigen, globalen Variablen als Volatile und long definiert um einen Zeitwert in Mikrosekunden speichern zu können (der Grund für Volatile wird später erläutert):

  volatile unsigned long Anfang_PWM = 0; //Variable zur ermittlung der Impulslaenge der Beschleunigung
  volatile unsigned long Mitte_PWM = 0;
  volatile unsigned long PWM_Lenk = 0;   //Variable zur ermittlung der Impulslaenge des Lenkwinkels
  volatile unsigned long PWM_Geschw = 0;

Danach werden die Interrupts in der "Setup" Funktion festgelegt. Dazu wird für jeden Interrupt die Funktion "attachInterrupt" aufgerufen. Als erster Wert wird dabei Interrupt Nummer übergeben. Die Zuordnung welche Nummer zu welchem Pin gehört kann der vorherigen Abbildung entnommen werden. Als nächstes wird die Funktion angegeben die in dem Interruptfall ausgeführt werden soll. Als letzter Punkt wird angegeben ob auf eine steigende, fallende oder wechselnde Flanke reagiert werden soll (Steigend=RISING, Fallend=FALLING, Wechselnd=CHANGING).

  void setup()
  {
  attachInterrupt(2, PWM_down_Geschw, FALLING);	//Interrupt zur identifikation einer fallenden Flanke 
  attachInterrupt(4, PWM_up_Lenk, RISING);		//Interrupt zur identifikation einer steigenden Flanke 
  attachInterrupt(5, PWM_down_Lenk, FALLING);		//Interrupt zur identifikation einer fallenden Flanke Lenkung
  }

Nun werden die entsprechenden Funktionen erstellt. Diese besitzen weder Übergabe- noch Rückgabewert. Wenn Werte gespeichert werden sollen, muss dies über globale Variablen geschehen. Dabei ist zu beachten, dass diese als "Volatile" definiert worden sind. Sonst kann es passieren, dass der Compiler die Variablen "wegoptimiert", da sie nicht im Hauptprogramm auffindbar sind. Den Funktionen ist zu entehmen, dass beim Signalbeginn (in diesem Fall das Lenksignal) die Prozessorlaufzeit gespeichert wird. Fällt die Flanke ab wird die Zeitdifferenz zwischen dem Abfallenden Lenksignal und dem Signalstart gemessen. Da dieser Punkt zeitgleich mit der Aufsteigenden Flanke des Beschleunigungssignals auftritt, kann der Zeitpunkt als Startsignal für das Beschleunigungs-PWM-Signal genutzt werden (so kann ein Interrupt-Pin gespart werden). Mit der letzten Funktion wird die abfallende Flanke des Beschleunigungssignals gemessen, wodurch wieder eine Zeitdifferenz ermittelt werden kann. Somit ist nun die High-Zeit beider PWM-Signale bestimmt.

//Interruptroutinen

  void PWM_up_Lenk()
  {
  cli();
  Anfang_PWM=micros();
  sei();
  }
  void PWM_down_Lenk()
  {
  cli();
  Mitte_PWM=micros();
  PWM_Lenk=Mitte_PWM-Anfang_PWM;
  sei();
  }
  void PWM_down_Geschw()
  {
  cli();
  PWM_Geschw=micros()-Mitte_PWM;
  sei();
  }

Autor: Jens Henze (Diskussion) 14:13, 6. Feb. 2014 (CET)

Ansteuerung von Aktuatoren

Um einen Aktuator mit dem Arduino ansteuern zu können, ist eine Zusatzschaltung (auch Treiberschaltung genannt) notwendig. Grund dafür ist, dass der Arduino an seinen digitalen Ausgängen nur begrenzt belastet werden kann. Dieses Problem kann z.B. mit Leistungstransistoren, Mosfets oder einem Relais gelöst werden. Da die meisten Relais einen höheren Strom benötigen, als der Arduino liefern kann und die Spule eine große Störquelle darstellt, wird an dieser Stelle eine Lösung mit einem Mosfet realisiert. Analog dazu kann genau so gut ein Leistungstransistor eingesetzt werden.

Die folgende Schaltung zeigt die Realisierung einer Motoransteuerung mithilfe eines Mikrocontrollers. Da ein Mikrocontrollerausgang in der Regel nur geringe Ströme liefern kann, ist eine Treiberschaltung mit einem Transistor notwendig. Wechselt in dieser Schaltung der Pin des Mikrocontrollers (dargestellt durch die Spannungsquelle "Digitaler_Pin") auf 5V liegen an den Widerständen R1 und R2 jeweils 2,5 V an. Da der Widerstand R1 parallel zu der Basis-Emitter Strecke des Transistors liegt, ist die notwendige Spannung des Transistors zum Durchschalten erreicht. Diese liegt in der Regel bei mind. 0,7V. Das Durchschalten des Transistors verursacht eine Spannung mit Höhe der Spannungsquelle "Uversorgung" an dem Widerstand R3. Dieser wiederum liegt parallel zur Gate-Source Strecke des eingesetzten P-Kanal Mosfets, wodurch dieser durchschaltet. Nun ist ein Stromfluss über den Mosfet zum Motor (dargestellt durch den Widerstand Rm und der Spule Lm) möglich.

Autor: Jens Henze (Diskussion) 14:14, 6. Feb. 2014 (CET)

Kommunizieren per CAN

Es folgt eine Beschreibung zum Aufbau einer CAN-Kommunikation zwischen dem Arduino und dem CAN-Case. Zum Schluss wird eine Möglichkeit beschrieben eine Kommunikation auf der Basis von Matlab-Simulink zu erstellen.

Konfiguration des CAN-Case

Um mit dem CAN-Case zu kommunizieren wird hier die Software "CANalayzer" eingesetzt. Als ersten Schritt muss der Messaufbau in der Software eingestellt werden. Dieser kann unter dem Reiter "Configuration" am unteren Teil des Fensters von CANalayzer gefunden werden. Hier wir zunächst der CAN-Kanal und die Baudrate eingestellt. Die Einstellmöglichkeiten können durch einen Doppelklick auf das Kartensymbol oder unter Konfiguration/Netzwerk-Hardware-Konfiguration erreicht werden. Sind diese Einstellungen getroffen, kann der CAN-Bus mitgeloggt werden. Alle Botschaften auf dem CAN-Bus können unter dem Reiter "Trace" im unteren Bereich des Fensters angeschaut werden.

Soll das CAN-Case ebenfalls Botschaften senden, müssen in dem Messaufbau Generatorblöcke eingefügt werden. Diese können zyklisch oder auf Knopfdruck Botschaften senden. Alternativ können diese Blöcke mit der C-ähnlichen Programmiersprache "CAPL" programmiert werden. Dies ermöglicht z.B. das Reagieren auf bestimmte Botschaften.

Umsetzung der Aufgabe in C

Wie zuvor beschrieben dient als Grundlage die Bibliothek "mcp2515.h". Diese bietet für den auf dem Shield eingesetzten Microcontroller zugeschnittene Funktionen. Dafür müssen die entsprechenden Dateien wie oben beschrieben in den Arduino Ordner kopiert werden. Im Quelltext kann die Datei dann mit #include <mcp2515.h> eingebunden werden.

In der Library wird eine Struktur mit dem Namen "tCAN" zum erstellen von CAN-Botschaften definiert. Diese Klasse hat die Attribute: ID, rtr, length und Data. Diese Werte sind mindestens nötig um eine Nachricht nach dem Standard des CAN Protokolls zu versenden. Wichtig dabei ist, dass beim CAN-Bus die 0 Dominant ist und 1en überschreibt.

Die ID dient zur Identifizierung von CAN Botschaften, gleichzeitig können dadurch Nachrichten priorisiert werden. Bei der CAN Kommunikation findet regelmäßig eine Arbitrierungsphase statt. Das bedeutet es wird ermittelt, welche CAN Botschaft die höchste Priorität hat und somit als erstes übertragen wird. Dies wird so realisiert, dass alle Bus- Teilnehmer gleichzeit versuchen eine Botschaft zu senden. Die am höchsten priorisierte Botschaft ist die, dessen ID die meisten 0en enthält. Wird eine Botschaft eines Teilnehmers von einer 0 eines anderen Teilnehmers überschreiben, registriert der entsprechende Teilnehmer das und bricht den Sendevorgang ab.

Mit dem rtr Bit wird angegeben ob es sich bei der CAN Botschaft um einen sogenannten "Remote Frame" handelt. Mit einem Remoteframe können Daten von einem anderen Busteilnehmer angefordert werden. In dem Fall der Übertragung von Sensorwerten, muss dieses Bit also auf 0 gesetzt werden.

Das length Bit gibt an, wie lang das Datenfeld der CAN Botschaft ist. Die hier eingesetzte Library benötigt diese Angabe als Ganzzahl, welche die Anzahl der Bytes angibt. Der Arduino kann an seinen analogen Eingängen Werte von 0 - 1023 einlesen. Um dies per CAN zu übertragen, benötigt man hier also 2 Bytes (2Bytes=FFFF=65535).

In dem Datenfeld werden die eigentlich zu übertragenden Daten angegeben.

Die entsprechende Sendenfunktion sieht so aus:


 void CAN_send(int ID, int length,unsigned char *Data)  //Funktion zum senden einer CAN-Botschaft
 {
   message.id = ID;                            //Zuweisen der ID in die Strukturvariable
   message.header.rtr = 0;                     //Zuweisen des rtr in den Header der Strukturvariable
   message.header.length = length;             //Zuweisen der lenth in den Header der Strukturvariable
 
   for( int i = 0; i < length; i++ )           //kopieren des Datenarrays in das Datenarrays der Struktur
     {
      message.data[i] = Data[i]; 
     }
 
   mcp2515_send_message(&message);             //Funktion aus Library die das senden der fertigen Botschaft übernimmt
 }

Daten werden mit dem Befehl

   ret = mcp2515_get_message(&msg_in);      //Emfpange Botschaft (Funktion aus Library)

empfangen und befinden sich dann im Buffer. Um die Daten aus der Botschaft zu entnehmen, muss auf das Datenfeld der zuvor erzeugten Strukturvariable "msg_in" zugegriffen werden. Im Quellcode sieht dies wie folgt aus.

 Aktuator_an=msg_in.data[0];            //Lese Botschaft aus (überträgt in diesem Fall 1 oder 0

Quelle:Vector, Bussysteme und Bordnetze Vorlesung 10

Sensorwerte von Arduino empfangen und Antwort senden

Nachdem es nach der Beschreibung aus dem vorherigen Kapitel nun möglich ist, Daten mit dem Arduino zu senden und empfangen, sollen diese Daten nun von einer anderen Station verarbeitet werden. Hierzu wird ein Can-Case der Firma Vektor verwendet. Dieses wird mithilfe eines "CAPL" Programmes so programmiert, dass dieses einen Wert von dem Arduino empfängt und eine Antwort als 0 oder 1 zurücksendet. Dies könnte z.B. dazu dienen, einen Aktuator einzuschalten. Das folgende Programm soll auf die Beispielnachricht 0x7B reagieren. Die Zahl ist dabei hexadezimal angegeben und ist nur als Beispiel gewählt. Dies wird durch den Befehl "on message 0x7B{...}" angegeben.

  on message 0x7B
  {
  (...)
  }

Zunächst werden dann alle notwendigen Variablen angegeben. In CAPL werden Can-Botschaften wie Struktur-Variablen behandelt. Hier wird dazu mithilfe von "message 1 msg" eine Nachricht mit dem Namen msg und der Nummer 1 erstellt. Im weiteren Verlauf wird das erste Datenbyte mit 0 gefüllt, und das DLC (Data length Byte) auf 1 gesetzt.

   message 1 msg;
   msg.byte(0) = 0;
   msg.DLC =1;

Anschließend werden drei Variablen vom Typ "Integer" erzeugt, um Daten vom Arduino empfangen zu können. In diesem Fall soll die vom Arduino gesendete Botschaft 2 Bytes lang sein, da ein Wert aus einem Analog-Digital-Wandler gesendet werden soll, dessen Maximallänge 1023 entspricht. Die Variable "Ges" soll dann den Wert enthalten, wie dieser vom Arduino gesendet wurde.

   int wert1;
   int wert2;
   int Ges;

Mithilfe von this.byte wird das entsprechende Datenbyte der am Anfang festgelegten Botschaft ausgelesen. Diese werden hier in die entsprechenden Variablen geschrieben. In der anschließenden Rechnung werden die Datenbytes wieder in einen Wert von 0-1023 umgerechnet.

   wert1=this.byte(0);
   wert2=this.byte(1);
   Ges=wert1*256+wert2;

Die folgende Auswertung legt nun einen Schwellwert von 1000 fest. Wenn also ein Wert vom Arduino empfangen wurde, der größter als 1000 ist, wird das Byte von "msg" auf 1 gesetzt.

   if(Ges>1000)
   {
      msg.byte(0) = 1;   
   }

Mit dem Befehl "output(msg)" wird diese Botschaft dann gesendet.

   output(msg);

Eine Beispielanwendung für solch einen Programmablauf könnte eine Distanzmessung mit einem Infrarotsensor sein, wobei die Verarbeitung zentral verlaufen soll. Dabei könnte der Infrarotsensor mit einem Mikrocontroller ausgewertet und auf desssen Signal ein Motor gestoppt werden.

Umsetzung der Aufgabe in Simulink

Aus Zeitgründen konnte die Aufgabe leider nicht mehr komplett in Simulink gelöst werden. Es folgt daher in diesem Abschnitt nur der erste Schritt dazu. Eine Anleitung zur Entwicklung von Arduino Treibern in Simulink befindet sich unter folgendem Link.

Laut dieser Anleitung sollte es möglich sein, mithilfe des S-Function Builder aus der Matlab-Simulink Library, auf dem Arduino lauffähige Programme zu erstellen. Theoretisch können die Arduino Programme Stückweise, eins zu eins an die entsprechende Stelle in dem S-Function-Block kopiert werden. Die Eingänge müssen dabei allerdings im S-Function-Block unter "Data Properties" voreingestellt werden. Abbildung 1 zeigt die Definition der Eingänge, wie sie für das zuvor erstellte C-Programm notwendig wären.

In der Simulink-Ansicht sieht dies dann wie folgt aus:

So wie in einem standard C Programm werden nun unter dem Reiter Libraries in dem Textfeld "Includes" alle zu includierenden Datien angegeben. Für den Fall dieses C-Programmes sind es die Libraries "Arduino.h" und "mcp2515.h". Weiterhin werden an dieser Stelle alle globalen Variablen definiert. Für das C-Programm ist dies die Definition der Can-Botschaft "message" Die folgende Abbildung zeigt diese Einstellungen.

Unter dem Reiter "Outputs" wird das eigentliche Hauptprogramm aufgeführt. Also alles was in Arduino Programmen unter "void loop(){(...]}" steht. Die in der folgenden Abbildung gezeigte Variable xD[0] ist eine interne Matlab Variable. Da diese hier auf 1 abgefragt wird, wird dieser Programmteil als Endlosschleife ausgeführt. Dazu im nächsten Abschnitt mehr.

Analog zum letzten Abschnitt werden unter dem Reiter "Discrete Update" alle Programmteile eingefügt, welche im Arduino Programm unter "void setup(){(...)}" stehen. Die folgende Abbildung zeigt, dass die zuvor erwähnte Variable xD[0] hier auf 0 abgefragt wird. Dies ist nur im Einschaltmoment der Fall, da am Ende dieses Programmabschnittes die Variable auf 1 gesetzt wird. So wird erreicht, dass dieser Teil nur einmalig und der Programmteil von "Outputs" dauerhaft ausgeführt wird.

Wird nun auf den Button Build des S-Function Menüs geklickt, wird der gesamte Block compiliert. Dazu muss allerdings ein C-Compiler für Matlab installiert sein. Dieser kann mithilfe von mex -setup im Matlab Command Fenster initialisiert werden. Wurde die S-Function compiliert sind in dem Arbeitsverzeichnis mehrere Dateien zu finden, darunter auch eine Datei namens "sfcn_exout_slsp_wrapper.c". Schaut man sich diese Datei an, ist die Struktur des "klassischen" Arduino Programmes wieder zu erkennen. Hier kann also verglichen werden ob das Programm auch so compiliert wurde, wie es soll. Die Übertragung der S-Function findet wie mit den Arduino-Standard-Programmen über "Run on target Hardware" statt.

Autor: Jens Henze (Diskussion) 14:13, 6. Feb. 2014 (CET)

Quellen

Projektbericht von Jens Henze zum Sommersemester 2013

http://www.mathworks.de/matlabcentral/fileexchange/39354-device-drivers

https://www.sparkfun.com/products/10039

http://www.arduino.cc

Vorlesung 10 Bussysteme Boardnetze und Diagnose Autor: Axel Thümmler


Autor: Jens Henze (Diskussion) 14:11, 6. Feb. 2014 (CET)


→ zurück zum Hauptartikel: Praktikum SDE