Posts mit dem Label Motor Shield werden angezeigt. Alle Posts anzeigen
Posts mit dem Label Motor Shield 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.



Sonntag, 21. Januar 2018

Motor Treiber für den Rover



Sicherlich hat jemand schon etwas geschrieben, dass die Ansteuern des Motor Shields vereinfacht. Dennoch möchte ich genau die Funktionsmöglichkeiten kennenlernen sowie auch den Schaltplan.

Seit Jahren liegt mein Rover unbenutzt in der Kiste und das möchte ich ändern. Aber damit dieser Betrieben werden kann, wird ein Motor Treiber benötigt. In diesem Fall ist es ein Motor Shield für Arduino. 
  • Arduino UNO / Duemilanove
  • Motor Shield
  • Externe Spannungsversorgung mit dem 9V oder einen zwei Zellen Lipo
  • DAGU Rover 5 Chassis 4WD

Antrieb
Der Rover von DAGU hat vier Motoren die unabhängig voneinander betrieben werden können. Allerdings werden die Gummi Ketten eingesetzt womit die Motoren zur einen Seite immer gleichzeitig laufen müssen.
Leider verfügt der verwendete Arduino nicht Ausreichend Pins für die Interrupt Funktion mit denen sich die vier Encoder vom Rover einlesen ließen. Die Umsetzung für das einlesen der Encoders würde besser mit einem Arduino MKR1000 funktionieren, wenn alle vier Encoder eingelesen werden sollen. Alternativ bei Verwendung mit den Gummi Ketten, reichen zwei Encoders aus und könnte mit einem Arduino Mega eingelesen werden.

Motor Treiber und Motor Shield
Eigentlich existiert für den Rover ein passender Motor Treiber, den ich jedoch nicht finden konnte. Daher kommt ein Motor Shield zum Einsatz, der genau alle Motoren Ansteuern kann. Für die Externe Spannungsversorgung wird ein zwei Zellen Lipo Akku verwendet. Auf dem Shield sollte der Jumper für PWR nicht gesteckt sein, solange der Arduino am USB mit dem PC Verbunden ist.
Die Anschlüsse für den Betrieb der zwei Servos werden aktuell für dieses Beispiel nicht verwendet. Somit bleiben Pin 9 und 10 offen.

Steuerung des Motor Shields
Für die Ansteuerung der Ausgänge M1, M2, M3 und M4 auf dem Motor Shield, werden insgesamt acht Pins benötigt. Zwei mehr, wenn man die Servo Ausgänge mit zählt, die jedoch nur weitergeleitete Pins sind.
Der Ausgang M1 besteht aus zwei Ausgängen die in diesem Beispiel immer mit M1_A und M1_B Bezeichnet werden. Daraus schließt sich, dass insgesamt acht Ausgänge geschaltet werden. Alle Ausgänge können jedoch mit einem PWM reguliert werden, aber es stehen nur vier zu Verfügung. Daher steht z.B. für M1 ein PWM Ausgang zur Verfügung. Aber rechnet man dies wieder hoch, kommen wir auf zwölf benötigte Pins.
Auf dem Shield wird ein 74HC595 Shiftregister verwendet, der selbst acht schaltbare Ausgänge hat. Dieser wird vom Arduino mit vier Pins beschaltet. Grundsätzlich reichen drei Pins, um einen Shiftregister anzusteuern. Der Vierte jedoch ermöglicht das explizite Abschalten aller Ausgänge.


Programmcode
Im folgenden Code Beispiel sind die wesentlichen Methoden aufgeführt. Geschaltet werden die Ausgänge M3 und M4 und maximalen PWM Signal Ausgang. Den gesamten Programmcode mit Kommentaren findet ihr wieder auf dem Github Repository.

 #define PIN_595_LATCH 12       // Pin connected to ST_CP of 74HC595  
 #define PIN_595_CLOCK 4        // Pin connected to SH_CP of 74HC595  
 #define PIN_595_DATA 8        // Pin connected to DS of 74HC595  
 #define PIN_595_SHIFT_REG_EN 7    // enable the Shiftregister  
 #define PIN_OUTPUT_M1 11       // pwm pin to control M1 output  
 #define PIN_OUTPUT_M2 3        // pwm pin to control M2 output  
 #define PIN_OUTPUT_M3 6        // pwm pin to control M3 output  
 #define PIN_OUTPUT_M4 5        // pwm pin to control M4 output  
 void setup() {  
  MotorShieldInitialize();  
 }  
 void loop() {  
  int motorsOn = 33; // schaltet M3 A und M4 A ein  
  int speedValue = 255; // Der Wert kann von 0 bis 255 gesetzt werden  
  SetRunMotors(motorsOn, speedValue, speedValue);  
  digitalWrite(PIN_595_SHIFT_REG_EN, LOW);  
  delay(2000);  
  digitalWrite(PIN_595_SHIFT_REG_EN, HIGH);  
  delay(1000);  
 }  
 void MotorShieldInitialize() {  
  pinMode(PIN_OUTPUT_M1, OUTPUT);  
  pinMode(PIN_OUTPUT_M2, OUTPUT);  
  pinMode(PIN_OUTPUT_M3, OUTPUT);  
  pinMode(PIN_OUTPUT_M4, OUTPUT);  
  pinMode(PIN_595_LATCH, OUTPUT);  
  pinMode(PIN_595_CLOCK, OUTPUT);  
  pinMode(PIN_595_DATA, OUTPUT);  
  pinMode(PIN_595_SHIFT_REG_EN, OUTPUT);  
  digitalWrite(PIN_595_LATCH, LOW);  
  digitalWrite(PIN_595_CLOCK, LOW);  
  digitalWrite(PIN_595_DATA, LOW);  
  digitalWrite(PIN_595_SHIFT_REG_EN, LOW);  
 }  
 void SetRunMotors(int motorsOn, int speedValueLeft, int speedValueRight) {  
  SetOutputValue(speedValueLeft, speedValueRight);  
  digitalWrite(PIN_595_LATCH, LOW);  
  shiftOut(PIN_595_DATA, PIN_595_CLOCK, MSBFIRST, motorsOn); //GetShiftValue(m));  
  digitalWrite(PIN_595_LATCH, HIGH);  
 }  
 void SetOutputValue(int speedValueLeft, int speedValueRight) {  
  analogWrite(PIN_OUTPUT_M1, speedValueRight); // right  
  analogWrite(PIN_OUTPUT_M2, speedValueRight);  
  analogWrite(PIN_OUTPUT_M3, speedValueLeft); // left  
  analogWrite(PIN_OUTPUT_M4, speedValueLeft);  
 }  

Steuerung
Das Beispiel stellt wieder eine Grundlage für weiteres. Der Spaß geht natürlich am besten weiter, wenn zur Steuerung des Rovers Sensoren eingesetzt werden oder über Funk die Steuerbefehle übermittelt werden.

Das selbe in .NET
Ein Codebeispiel mit C# und .NET Micro Framework beschreibe ich im Post "Motor Treiber für den Rover (.NETMF)"



Samstag, 18. November 2017

Servo mit dem Xbox Controller steuern


Das ansteuern eines Servos über einen Xbox One Controller ist simpel umzusetzen. Für dieses Beispiel wird folgendes verwendet:

  • MonoGame
  • Xbox Controller
  • Arduino UNO oder vergleichbar
  • Servo
  • Motor Shield
  • Externe Batterie

Nach der Installation von MonoGame sind in Visual Studio mehre Vorlagen verfügbar. Benötigt wird das Template 'MonoGame Windows Project', das im folgenden Bild als erstes in der Liste erscheint.


Programmcode mit MonoGame
Sobald das Projekt angelegt würde, könnt ihr die Game1.cs Datei öffnen. Für die Verbindung zum Arduino wird die Klasse SerialPort verwendet. Dazu sollte vorher bekannt sein, welcher COM Port bei euch der Arduino verwendet. Die Baudrate von 115200 ist die maximale Geschwindigkeit, die zuverlässig funktioniert. Die restlichen Parameter sind die Default Werte der Seriellen Verbindung zum Arduino (siehe Programmcode).
Die überschriebenen Methoden 'Initialization()', 'LoadContent()' und 'Draw()' werden für das Beispiel nicht verwendet und können entfernt werden.
Mit der Update Methode wird der Xbox Controller eingelesen. Der Input Wert vom Stick kommt dann aus dem GamePadState unter '.ThumbSticks.Left.X'. Der Float Wert geht von -1 bis +1 und wird deshalb zunächst um plus eins dass Offset verschoben. Anschießend wird der Float Wert in ein Byte Wert transformiert, dass dann anschließend an den Serial Port geschrieben wird.
Damit die Anwendung nicht zu viele Bytes versendet, wird am Ende 50 Millisekunden gewartet. Ansonsten läuft der Buffer im Arduino über.

GraphicsDeviceManager _graphics;  
 private SerialPort _serialPort;  
 public Game1()  
 {  
   _graphics = new GraphicsDeviceManager(this);  
   // Verwendete COM Port muss ggf. angepasst werden.  
   this._serialPort = new SerialPort("COM5", 115200, Parity.None, 8, StopBits.One);  
   this._serialPort.Open();  
 }  
 protected override void UnloadContent()  
 {  
   this._serialPort.DiscardOutBuffer();  
   this._serialPort.Close();  
 }  
 protected override void Update(GameTime gameTime)  
 {  
   GamePadState state = GamePad.GetState(PlayerIndex.One);  
   byte stickValue = (byte)((state.ThumbSticks.Left.X + 1) * 90);  
   this._serialPort.Write(new byte[] { stickValue }, 0, 1);  
   Task.Delay(50).Wait();  
 }  

Arduino als Empfänger
Der Arduino muss nun die empfangenden Bytes vom PC entgegen nehmen und daraus ein PWM Signal erzeugen das zur Steuerung des Servos verwendet wird. Weiters habe ich im Programmcode beschrieben.

 #include <Servo.h>  
 Servo servo;  
 int servoPin = 9;  
 int receivedValue;  
 void setup() {  
  // Muss die selbe Baudrate haben,   
  // wie in der MonoGame Anwendung.  
  Serial.begin(115200);  
  // die meisten Servos arbeiten mit einem   
  // HIGH Signal von 1ms bis 2ms.  
  servo.attach(servoPin, 1000, 2000);  
 }  
 void loop() {  
  if( Serial.available() > 0){  
   receivedValue = Serial.read();  
   // Den eingelesenen Wert Abschneiden,  
   // falls der maximale Stellwert überschritten wird.  
   if(receivedValue > 180) {  
    receivedValue = 180;  
   }  
   else if(receivedValue < 0) {  
    receivedValue = 0;  
   }  
   // alle weiteren eingegangenen bytes werden verworfen  
   Serial.flush();  
  }  
  // Aktuellen Einstellwert festlegen.  
  servo.write(receivedValue);  
  delay(20);  
 }  

Was nun
Im Grunde ist die Ausführung simpel und das Schreiben dieses Post hat da deutlich mehr Zeit benötig. :D
Aber was kann man nun mit dem Wissen anstellen? Nun, man kann stattdessen einer  Kabelgebundenen Ausführung, auf eine Bluetooth Funkverbindung wechseln. Damit ließe sich dann ein RC Model ansteuern. Mag etwas Umständlich sein, die Fernsteuerung gegen etwas eigenes gebasteltes zu ersetzen, aber darin liegt der Spaß.

Zur Vollständigkeit wieder ein Link zu dem Github Repository und dem gesamten Beispiel Programmcode.

Nachtrag
Leider funktioniert dieses einfache Beispiel nur in dieser Konstellation. Sobald versucht wird, zwei Bytes zu versenden, treten Probleme auf mit der Seriellen Verbindung.
Wie man das behebt, beschreibe ich mit dem Post "Zeichnen auf dem TFT mit dem XBox Controller"





Ameisen Simulation und andere Dinge

Wer lange sich schon mit C# beschäftig, hat sicherlich schon mal was von AntMe gelesen oder gehört. Diese Idee hatte ich aufgegriffen und mi...