Posts mit dem Label DAGU Rover 5 werden angezeigt. Alle Posts anzeigen
Posts mit dem Label DAGU Rover 5 werden angezeigt. Alle Posts anzeigen

Sonntag, 22. Juli 2018

Arduino Motor Driver Shield auf Raspberry Pi


Mit einem Expansion Board für Raspberry Pi können die Shields für Arduino verwendet werden. In diesen Beispiel wird ein Motor Shield verwendet, zu dem ich bereits zwei Blog Einträge geschrieben habe. In dem Beispiel mit dem Netduino ist nur die Programmiersprache gleich, aber das verwendete Framework unterscheidet sich stark von einander. Allein schon das Konfigurieren eines Pins für den Raspberry Pi geht vollständig einen anderen Weg. Offen gestanden bin ich wohl an der Stelle konservativ und würde eher im dot Net Umfeld das .NET Micro Framework bevorzugen.

Benötigte Hardware:

  • Raspberry Pi 2 oder 3
  • Waveshare ARPI600 IO Expansion Board
  • Arduino MotorShield mit L293D

Windows IoT Extensions
Mit dem Anlegen einer neuen Solution als Universal Application oder kurz gesagt UWP, muss zunächst die Reference Windows IoT Extensions hinzugefügt werden. Je nach Ziel Version von Windows IoT kann die aktuelle passende Bibliothek hinzugefügt werden. Mit dieser werden dann Klassen bereit gestellt, mit dem der Zugriff auf die GPIOs möglich ist.



Neue MotorShield Klasse anlegen
Den Aufbau der Klasse ist fast wie aus dem Beispiel mit dem Netduino. Die öffentlichen Methoden sind die gleichen. Nur intern musste das Initialisieren der Hardware Pins anders gelöst werden. (Motor Treiber für Rover (.NETMF))
Für mich verwunderlich war, dass es keine PWM Ansteuerung gab oder ich habe das einfach übersehen. Zumindest nach kurzer Suche kamen keine passenden Ergebnisse, wie man die GPIOs mit einem PWM Signal ansteuert. Daher nur Digitales Gas geben.

Anzusteuernde Pins
Im Netz sind Zahlreiche Bilder, die die Pins beschreiben. Auf Waveshare (https://www.waveshare.com/wiki/ARPI600) sind die Pins ebenfalls beschrieben, allerdings ist das auf der Erweiterung anders gekennzeichnet. Hier war es nicht immer schlüssig, ob der Hardware Pin oder GPIO Pin gemeint ist. Am Ende habe ich festgestellt, dass man die Pin Ansicht von Raspberry Pi benötigt und dann auf dem ARPI600 die Beschriftung ablesen und vergleichen musste, um auf das richtige Mapping zu kommen.


Die folgende Tabelle zeigt, mit welchen Bezeichnungen man zu tun hat und wie diese zu einander passen.

Raspberry Pin
Raspberry GPIO
ARPI600
Arduino
MotorShield
8
UARTTO TX
P_RX
D0

10
UARTTO RX
P_TX
D1

11
17
P0
D2

12
18
P1
D3
M2
13
27
P2
D4
Clock
15
22
P3
D5
M4
16
23
P4
D6
M3
18
24
P5
D7
Enable Shiftregister
22
25
P6
D8
Data
7
4
P7
D9

24
8
CE0
D10

19
10
MOSI
D11
M1
21
9
MISO
D12
Latch
23
11
SCK
D13


Verhältnismäßigkeit
Abgesehen der fehlenden Stufenlosen Regeln der Motoren, ist die Steuerung nicht anders als mit einem Arduino oder Netduino. Um nur den Motor Treiber zu verwenden, ist das verwenden mit einem Raspberry Pi gerade zu übertrieben. Selbst mit dem Raspberry Pi 3 und Windows 10 IoT dauerte der Bootvorgang ca. eine Minute, bis dann endlich der Motor Treiber ausgeführt wird. Das relativiert sich dann jedoch, wenn man weitere Funktionalitäten implementiert.


Sonntag, 11. Februar 2018

Motor Treiber für den Rover (.NETMF)


Was in C geht, geht auch in C#. Einige Zeit ist vergangen als ich das letzte Mal ein Beispiel zu Netduino gepostet habe. Viel hat sich nicht geändert und im Grunde verwendet ich statt .NETMF 4.2 nun .NETMF 4.3. Neu ist der Netduino 3 der in drei Varianten kommt und alle haben drei Go Port Anschlüsse. Zudem kommt dieser mit mehr Flash Speicher, bei den Varianten mit Netzwerk Eigenschaften. Nur der Takt ist weiterhin bei 168MHz, dass jedoch ausreichend ist für die meisten Anwendungen.

Nun zur Hardware
  • Netduino 3 Wifi
  • Motor Shield
  • Externe Spannungsversorgung mit 9V oder einen zwei Zellen Lipo
  • DAGU Rover 5 Chassis 4WD

Antrieb und Motor Shield
Zu meinem vorigen Post mit dem Arduino ändert sich am Antrieb und am Shield nichts. Die Pin Belegung und sowie auch die Betroffenen Signal Ausgänge, sind beim Netduino für diesen Zweck identisch. (Arduino, Motor Treiber für den Rover)

Programmcode
Mit der C# und .NET Micro Framework Variante habe ich weitgehend die Namen der Pin Variablen und auch die Methodennamen direkt übernommen. Ein Punkt ist jedoch anders. Das Ausgegebene Pwm Signal wird beim Initialisieren eingestellt. An der Stelle muss man sich bewusst machen, wie lang die Duration (die Länge des HIGH Level Puls) pro Periode sein darf. Beim Arduino konnte ein Wert von 0 bis 255 zugewiesen werden. Mit der PWM Klasse für den Netduino ist die einzustellende Duration davon abhängig, wie hoch die Periode gesetzt wurde. Für dieses Beispiel kann ein Duration Wert von 0 bis 1000 festgelegt werden.


 public class RoverMotorDriver  
 {  
   private OutputPort _latch;  
   private OutputPort _clock;  
   private OutputPort _data;  
   private OutputPort _regEn;  
   private PWM _outputM1;  
   private PWM _outputM2;  
   private PWM _outputM3;  
   private PWM _outputM4;  
   private const int _outputM1_A = 4;        
   private const int _outputM1_B = 8;        
   private const int _outputM2_A = 2;        
   private const int _outputM2_B = 16;       
   private const int _outputM3_A = 32;        
   private const int _outputM3_B = 128;       
   private const int _outputM4_A = 1;        
   private const int _outputM4_B = 64;        
   public RoverMotorDriver()  
   {  
     this._latch = new OutputPort(Pins.GPIO_PIN_D12, false);  
     this._clock = new OutputPort(Pins.GPIO_PIN_D4, false);  
     this._data = new OutputPort(Pins.GPIO_PIN_D8, false);  
     this._regEn = new OutputPort(Pins.GPIO_PIN_D7, false);  
     this._outputM1 = new PWM(PWMChannels.PWM_PIN_D11, 200d, .1d, false);  
     this._outputM1.Duration = 1;  
     this._outputM1.Start();  
     this._outputM2 = new PWM(PWMChannels.PWM_PIN_D3, 200d, .1d, false);  
     this._outputM2.Duration = 1;  
     this._outputM2.Start();  
     this._outputM3 = new PWM(PWMChannels.PWM_PIN_D6, 200d, .1d, false);  
     this._outputM3.Duration = 1;  
     this._outputM3.Start();  
     this._outputM4 = new PWM(PWMChannels.PWM_PIN_D5, 200d, .1d, false);  
     this._outputM4.Duration = 1;  
     this._outputM4.Start();  
   }  
   public void SetMotorShieldOn(bool setOn)  
   {  
     this._regEn.Write(!setOn);  
   }  
   public void MotorStop()  
   {  
     this.SetRunMotors(0, 0, 0);  
     this.SetMotorShieldOn(false);  
   }  
   public void SetForeward(int speedValueLeft, int speedValueRight)  
   {  
     int motorsOn = _outputM1_B | _outputM2_B | _outputM3_A | _outputM4_A;  
     this.SetRunMotors(motorsOn, speedValueLeft, speedValueRight);  
     this.SetMotorShieldOn(true);  
   }  
   public void SetBackward(int speedValueLeft, int speedValueRight)  
   {  
     int motorsOn = _outputM1_A | _outputM2_A | _outputM3_B | _outputM4_B;  
     this.SetRunMotors(motorsOn, speedValueLeft, speedValueRight);  
     this.SetMotorShieldOn(true);  
   }  
   public void SetTurnLeft(int speedValue)  
   {  
     int motorsOn = _outputM1_B | _outputM2_B | _outputM3_B | _outputM4_B;  
     SetRunMotors(motorsOn, speedValue, speedValue);  
     SetMotorShieldOn(true);  
   }  
   public void SetTurnRight(int speedValue)  
   {  
     int motorsOn = _outputM1_A | _outputM2_A | _outputM3_A | _outputM4_A;  
     SetRunMotors(motorsOn, speedValue, speedValue);  
     SetMotorShieldOn(true);  
   }  
   private void SetRunMotors(int motorsOn, int speedValueLeft, int speedValueRight)  
   {  
     this.SetOutputValue(speedValueLeft, speedValueRight);  
     this.RegisterWrite(motorsOn);  
   }  
   private void SetOutputValue(int speedValueLeft, int speedValueRight)  
   {  
     this._outputM1.Duration = this.GetLimitValue(speedValueLeft);  
     this._outputM2.Duration = this.GetLimitValue(speedValueLeft);  
     this._outputM3.Duration = this.GetLimitValue(speedValueRight);  
     this._outputM4.Duration = this.GetLimitValue(speedValueRight);  
   }  
   private uint GetLimitValue(int durationValue)  
   {  
     if(durationValue > 1000)  
     {  
       durationValue = 1000;  
     }  
     if(durationValue < 0)  
     {  
       durationValue = 0;  
     }  
     return 1000 + (uint)durationValue;  
   }  
   private void RegisterWrite(int motors)  
   {  
     this._latch.Write(false);  
     for (int bit = 0; bit < 8; bit++)  
     {  
       int target = motors & 0x80;  
       motors = motors << 1;  
       this._data.Write(target == 128);  
       this._clock.Write(true);  
       this.SleepWhile(5);  
       this._clock.Write(false);  
     }  
     this._latch.Write(true);  
   }  
   private void SleepWhile(int count)  
   {  
     for (int i = 0; i < count; i++) { }  
   }  
 }  

Unschönes
Die Methode "SleepWhile(int count)" ist eher Unschön, reicht aber hierfür aus, um den ShiftRegister richtig anzusteuern. Bisher traten mit der Einfachen Lösung keine Schwierigkeiten auf.

Fernsteuerung
Mit dem WLAN auf dem Netduino lässt sich schnell auch eine Fernsteuerung dazu bauen. Im nächsten Post werde ich darauf eingehen, wie man mit dem Netduino eine einfache Netzwerk Verbindung herstellt.



Gehäuseentwurf für Signalleuchten (ESP32)

RGB LEDs und LIPO Akkus sind bestellt. Weil ich bereits die Abmessungen habe, kann ich schon mal mit dem Gehäuseentwurf in Blender anfangen....