Posts mit dem Label Servo werden angezeigt. Alle Posts anzeigen
Posts mit dem Label Servo werden angezeigt. Alle Posts anzeigen

Mittwoch, 23. September 2026

Gedruckte Bewegliche Erweiterung für die Arduino UNO Q Plattform

 

Nach drei Korrekturen am 3D Model, habe habe ich ein Gerüst, dass funktionieren könnte. Als nächstes kommt der Boden, der mit dem Schritt Motor Verbunden wird, damit sich die Plattform drehen kann. Ob da noch zusätzlich ein Getriebe benötigt wird, muss ich allerdings noch prüfen.

Wenn das soweit passt, muss ich mich über die Stromversorgung kümmern, denn diese kann nur im Unterenbereich sein platziert werden. Die Herausforderung ist jedoch, eine Leitung so auf der Drehplattform festzulegen, dass diese sich nicht hängen bleibt und genügend Strom übertragen kann. Klar, die Plattform kann keine 360° Drehung durchführen mit der Versorgungsleitung, aber das muss mein Ziel auch nicht.

Sonntag, 23. September 2018

PCA9685 PWM Driver Modul mit dem Netduino


Nach etwas längerer Zeit ist eine Thema mit dem Netduino wieder dran. Diesmal ist das PWM Treiber Modul dran, den ich schon etwas länger hier liegen habe und es ist eigentlich zunächst für den Raspberry Pi gedacht, aber da fehlt mir noch eine neue Platine um einen eigenen Motor Treiber zu bauen.

Benötigt wird:
  • Netduino (sollte mit allen Versionen funktionieren)
  • PCA9685 16 Channel PWM Driver (Meins ist nicht von Adafruit)
  • Mindestens ein Servo zum Testen
  • Externe Spannunsquelle mit Maximal 6V


Neues Projekt
Für das Projekt werden die Referenzen Microsoft.SPOT.Hardware und Microsoft.SPOT.Native die für die I²C und Debug Ausgaben verwendet werden. Neben der bestehenden Program.cs Klasse wird eine eigene Klasse für den PWM Treiber hinzugefügt, die dann den Inhalt hat des hier folgenden gezeigten Code. Weitere Kommentare sind im Github Repository zu sehen.

public class Pca9685 : I2CDevice
{
    private readonly byte PCA9685_MODE1 = 0x00;
    private readonly byte PCA9685_PRESCALE = 0xFE;

    private readonly byte LED0_ON_L = 0x06;
    private readonly byte LED0_ON_H = 0x07;

    private readonly byte LED0_OFF_L = 0x08;
    private readonly byte LED0_OFF_H = 0x09;

    private readonly byte ALL_LED_ON_L = 0xFA;
    private readonly byte ALL_LED_ON_H = 0xFB;
       
    private int _period;
       
    public Pca9685(int period) : base(new Configuration(0x40, 100))
    {
        this._period = period * 1000;
    }

    public void Reset()
    {
        this.Write(new byte[]{ PCA9685_MODE1, 0x00 });
        Thread.Sleep(10);
    }

    internal void Start()
    {
        int hz = 1000000 / this._period;

        this.Reset();
        this.SetPwmFrequency(hz);
    }
        
    public void SetPwmFrequency(float frequency)
    {
        frequency *= 0.9f;
        float prescaleval = 25000000;
        prescaleval /= 4096;
        prescaleval /= frequency;
        prescaleval -= 1;

        byte prescale = (byte)(prescaleval + 0.5);

        byte[] buffer = new byte[1] { PCA9685_MODE1 };
           
        if (this.Read(buffer) != 1)
        {
            throw new Exception("can not read mode1");
        }

        byte oldMode = buffer[0];

        byte newMode = (byte)(((byte)oldMode & (byte)0x7F) | (byte)0x10);

        // go to sleep
        this.Write(new byte[] { PCA9685_MODE1, newMode });
        this.Write(new byte[] { PCA9685_PRESCALE, prescale });
        this.Write(new byte[] { PCA9685_MODE1, oldMode });
        Thread.Sleep(5);
        this.Write(new byte[] { PCA9685_MODE1, (byte)(oldMode | 0x80) });

        byte[] bufferRead = new byte[] { PCA9685_MODE1 };
        this.Read(bufferRead);
        Debug.Print("Mode now: " + bufferRead[0].ToString());
    }
       
    internal void SetPwm(byte outputNumber, int on, int off)
    {
        Debug.Print("Output: " + outputNumber.ToString() + ", ON: " + on.ToString() + ", OFF: " + off.ToString());

        byte targetOutput_ON_L = (byte)(LED0_ON_L + 4 * outputNumber);
        byte targetOutput_ON_H = (byte)(LED0_ON_H + 4 * outputNumber);
        byte targetOutput_OFF_L = (byte)(LED0_OFF_L + 4 * outputNumber);
        byte targetOutput_OFF_H = (byte)(LED0_OFF_H + 4 * outputNumber);

        this.Write(new byte[] { targetOutput_ON_L, (byte)on });
        this.Write(new byte[] { targetOutput_ON_H, (byte)(on >> 8) });
        this.Write(new byte[] { targetOutput_OFF_L, (byte)(off) });
        this.Write(new byte[] { targetOutput_OFF_H, (byte)(off >> 8) });
    }

    private int Write(byte[] buffer)
    {
        I2CTransaction[] trans = new I2CTransaction[]
        {
            CreateWriteTransaction(buffer)
        };

        return Execute(trans, 1000);
    }
       
    private int Read(byte[] buffer)
    {
        I2CTransaction[] trans = new I2CTransaction[]
        {
            CreateReadTransaction(buffer)
        };

        return Execute(trans, 1000);
    }
}

Inhalt zur Klasse
Mit der Fertigstellung der Klasse, bzw. ist diese jetzt soweit ausgeführt, dass die einzelnen PWM Ausgänge des Moduls über die I²C gesteuert werden können. Sieht man in das Datenblatt, dann ist zusehen, dass das Modul noch ein paar mehr Sachen kann. Zum einen bin ich hier nicht auf das Kaskadieren des Moduls mit mehreren nicht eingegangen. Dazu komme ich auf ein anderes mal, wenn ich mehrere Module davon besitze.
Der Programmcode selbst habe ich ursprünglich aus öffentlichen C++ Programmcode entnommen und habe deshalb auch die selben Namen für Methoden verwendet wie aus dem Adafruit Programmcode.

Vielleicht verdreht
An der Stelle für SetPwm bin ich mir noch nicht sicher ob die Ansteuerung so richtig ist. Zugegeben, ich bin noch nicht mit dem Datenblatt durch. Da ich selbst weis, wie ein PWM Signal auszusehen hat, sollte auch die Ansteuerung meiner Vorstellung entsprechen. Doch wenn ich den High Puls festlege, dann ist dieser länger als der Low Puls Länge Inherhalb der Periode. Das sollte eigentlich umgekehrt sein.

Verkabeln
Mit vier Leitungen ist das Modul vom Netduino steuerbar. Damit aber auch die Servos später versorgt sind, muss in der Mitte ebenfalls eine Spannungsquelle angeschlossen werden.


Probleme
Falls ihr die Stromversorgung in der Mitte vom Modul mit dem 5V Ausgang vom Netduino versorgt, kann es sein, dass es nicht funktioniert oder instabil reagiert. Bedeutet, kann funktionieren, muss aber nicht. Besser ist immer eine seperate Versorgung anzuschließen. Allein je nach größe des verwendeten Servos und dessen Belastung, könnte die Stromaufnahme zu groß sein (Leider konnte ich keine Information finden, wieviel Strom maximal am 5V Pin abgenommen werden kann vom Netduino).

Die Wesentlichen Technischen Infos:
PWM Signal kann in 4096 Stufen gesteuert werden (12Bit)
PWM Signal kann zwischen 24Hz bis maximal 1526Hz eingestellt werden.
Die 16 Anschlüsse für die Stromversorgung der Servos, können maximal betrieben werden in Abhängigkeit der Angeschlossenen Spannungsversorgung.
PWM Output selbst kann maximal bis 25mA verwendet werden für LEDs
Spannungsversorgung für die Steuerung über I²C darf von 2,3V bis 5,5V liegen.
16 PWM Ausgänge
Kann über einen Externen Clock betrieben werden, der maximal bis 50MHz sein darf.
Intern ist ein 25MHz Ozillator verbaut.
Kann für LEDs oder Servos verwendet werden.


Quellenangaben:
Github Quellcode in C++
Datenblatt:



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"





Dienstag, 14. Oktober 2014

Intervallaufnahme mit der Kamera ohne Interne Funktion

IntervallAufnahme01

Die Funktion für eine Intervallaufnahme ist simpel, daher ist es für mich unverständlich, warum nicht zumindest die Systemkameras von der Nikon 1 Serie in ihren Modellen diese Option anbieten. Nun gut, dies war kein Kaufkriterium, wäre aber schön gewesen. Ich selbst verwende die Nikon 1 J3 (die J1 hat die Intervallaufnahme), allerdings sollte mein Beispiel auch für andere Kameras funktionieren. Grundsätzlich geht es darum einen Servo auf den Auslöser zu drücken. Für diesen Zweck habe ich aus Aluminium Eckschienen eine Vorrichtung gebaut.

image

Wie schon am Bild zu erkennen ist, sollten zum Schutz die Alu Eckschienen nach der Verarbeitung abgerundet und ggf. mit Filzaufklebern beklebt werden.

Die Materialliste:

  • Alu Eckschienen ca. 20cm
  • Filzaufkleber
  • ein paar 3mm Schrauben und entsprechende Muttern
  • Mini Servo mit ca. 10 Ncm
  • Eine passende Schraube zur Kamera dass mit dem Stativ auch verschraubt werden kann. (siehe folgendes Bild)
image

Zum Ansteuern wird der Netduino mit einem passenden Shield zur Ansteuerung für einen Servo verwendet. Die geringe Belastung kann das Board verkraften. Bei Normalen und größeren Servos wird eine Externe Stromversorgung notwendig.

image

Der Aufwand des Programmcodes gestaltet sich übersichtlich. Zusehen ist, dass noch ein Analoger Wert eingelesen wird, worauf ich im nächsten Absatz zu sprechen komme.

public class Program
{
    // Für die Einstellung des Druckpunktes.
    private static SecretLabs.NETMF.Hardware.AnalogInput _Input;

    // Ansteuerung des Servos für den Auslöser.
    private static PWM _Servo;

    // Led Status wiedergeben.
    private static OutputPort _Status;

   // Einen Zähler.
    private static int _IntervallCounter = 0;

   // Nach wie viel Sekunden soll ausgelöst werden.
   private static int _IntervallEnd = 15;//30;

    public static void Main()
    {
        _Input = new SecretLabs.NETMF.Hardware.AnalogInput(Pins.GPIO_PIN_A0);
        _Servo = new PWM(Pins.GPIO_PIN_D5);
        _Status = new OutputPort(Pins.ONBOARD_LED, false);
           
        // 20ms für den Analog Servo.
        uint periode = 20000;

        // Mittelstellung bei 1,5ms
        _Servo.SetPulse(periode, 1500);

       while (true)
        {
           if (_IntervallCounter >= _IntervallEnd)
            {
                for (int i = 0; i < 3; i++)
                {
                    _Status.Write(true);
                    Thread.Sleep(100);
                    _Status.Write(false);
                    Thread.Sleep(100);
                }

                // Hinweis: bei Netduino 1 wird ein Wert gelesen von 0 bis 1023.
                //          Der Netduino 2 hingegen liest bis 4095.
                //uint duration = 1000 + (uint)_Input.Read();         // Ein kommentieren um den Servo
                //Debug.Print("Duration: " + duration.ToString());  // einzustellen am Pin A1.

                _Servo.SetPulse(periode, 1710); // 1710 ist eine Einstellung, an meiner Kamera
                Thread.Sleep(400);

               // Zähler Zurücksetzten.
                _IntervallCounter = 0;
            }
            else
            {
                // Aktivität zeigen
                _Status.Write(true);
                Thread.Sleep(100);
                _Status.Write(false);
               Thread.Sleep(900);

                _IntervallCounter++;
            }

           // Zurückstellen.
            _Servo.SetPulse(periode, 1500);
        }
    }
}

Das gleiche nochmal für den Arduino.

#include <Servo.h>
// Servo Kontrolle anlegen
Servo controlServo;
// Einstellung wie weit sich der Servo drehen darf
int pinAnalogIn = A0;
// Servo ansteuern
int pinServo = 5;
// LED Status
int pinLed = 13;
// Intervall Zähler
int intervall = 0;
// IntervallLänge in Sekunden
int intervallLength = 15; // 30;

void setup(){

  // Dient zu Einstellgunszwecke
  Serial.begin(9600);
  // Pins zuweisen
  pinMode(pinAnalogIn, INPUT);
  controlServo.attach(pinServo);
  pinMode(pinLed, OUTPUT);
}

void loop()
{
  if(intervall >= intervallLength)
  {
    for(int i = 0; i < 3; i++)
    {
      digitalWrite(pinLed, HIGH);
      delay(100);
      digitalWrite(pinLed, LOW);
      delay(100);
    }
   
   // Einkommentieren um die Reichweite des Servos einzustellen,
    // mit einem Potitometer am Pin A1.

    int result = 500; //analogRead(pinAnalogIn);
   
    controlServo.write(map(result, 0, 1023, 0, 179));
    delay(400);
   
    // Zähler zurück setzen
    intervall = 0;
  }
  else
  {
   digitalWrite(pinLed, HIGH);
   delay(100);
    digitalWrite(pinLed, LOW);
    delay(900);
    intervall++;
  }
 
  // Zurück stellen.
  controlServo.write(90);
}

Jetzt fehlt noch ein fester Wert für den Druckpunkt. Der Potentiometer wird an Pin A1 angeschlossen, die Spannungsversorgung muss beim Netduino mit 3,3V angelegt werden. Das Auslesen des Stellwertes erfolgt beim Netduino über Debug.Print() und wird über die Ausgabe angezeigt. Beim Arduino wird der Stellwert über dem Serial Monitor wiedergegeben. Je nach Belastung kann die Versorgung über USB instabil werden. Das zeigt sich dann, wenn der Servo beim Drücken des Auslösers leicht zittert. Später, mit einem 9V Akku oder Netzteil, sollten diese Schwierigkeiten nicht mehr auftauchen. Ansonsten muss der Servo einen eigenen Stromkreis erhalten, um zu verhindern, dass die Versorgung des Mikrocontrollers zu sehr schwankt und so zu einem Neustart des Programms führt.

image image
image image

Zu den Einstellungen der Kamera. Um ein schnelles Auslösen der Kamera zu ermöglichen, sollte auf den Kreativ Modus gewechselt werden. Die Blende, die Verschlusszeit und der Fokus sind am besten manuell festzulegen. Spätestens bei Nachtaufnahmen kommt man an diesen Einstellungen nicht vorbei. Achja, das Dateiformat JPG ist für die Ausgabe zu empfehlen. Mit dem NEF bzw. RAW ist das Bearbeiten im Video Schnittprogramm kaum machbar.

BeiNachtMitSternenhimmel_06

Tipp zur Nachtaufnahme: acht bis fünfzehn Sekunden für die Verschlusszeit bei maximal offener Blende reichen aus. Zu lange Belichtung führt dazu, dass die Sterne Streifen bilden, also praktisch gesehen wird die Erdrotation sichtbar. Die idealen Nächte mit Sternenhimmel sind in der Zeit um Neumond. Die besten Bedingungen sind Orte, die so dunkel sind, dass beim Hinabsehen die eigenen Füße nicht mehr zu sehen sind. Zwinkerndes Smiley

Nun noch ein paar Aufnahmen, die ich mit meiner Apparatur erstellt habe.

Zu guter Letzt auch noch der Quellcode zum Herunterladen.

Dienstag, 29. Oktober 2013

PWM / PPM Frequenz an Brushless Motoren mit dem Netduino

NetduinoServo01_bearbeitet-1

Im Modellbau ist es üblich, dass die Frequenz für PWM bei 50Hz zum Ansteuern von analogen Servos und analogen Motorenreglern verwendet wird. Wer allerdings einen digitalen Servo oder eine digitale Motorensteuerung hat, kann hier eine weit höhere Frequenz verwenden. Besonders bei Modellhelikoptern oder auch bei den Quadrocoptern wird dies gefordert. Der Grund liegt hier klar auf der Hand, dass die Periodendauer wesentlich kürzer wird und somit pro Sekunde mehrmals ein neuer Steuerwert festgelegt werden kann. Bei 50Hz liegt die Periodendauer bei 20 Millisekunden, wovon 1 bis 2 Millisekunden das eigentliche Steuersignal (Duration) abbilden und in den restlichen 18 Millisekunden nichts weiter kommt. Daher habe ich mir gedacht, wie sieht eigentlich dann das Signal aus, wenn es auf 200Hz gesetzt wird.

image
Beispiel Darstellung mit 50Hz PWM Signal mit einer Periodendauer von 20ms.
image
Beispiel Darstellung mit 200Hz PWM Signal mit einer Periodendauer von 5ms

Schnell stellt sich heraus, dass sich grundsätzlich die Periodendauer und die LOW Pegellänge verändert, wie in den Bildern zu sehen ist. Die Duration bleibt unverändert und wird auch nicht skaliert, was das Programmieren des Signals einfach gestaltet. Im nächsten Programm Beispiel wird der erste PWM Port mit 20ms und der zweite Port mit 5ms Periodendauer initialisiert.

// PWM Signal mit 20 Millisekunden Periode und 1 Millisekunde Duration
PWM pwm1 = new PWM(PWMChannels.PWM_PIN_D5, 20000, 1000,
    PWM.ScaleFactor.Microseconds, false);
// PWM Signal mit 5 Millisekunden Periode und 1 Millisekunde Duration
PWM pwm2 = new PWM(PWMChannels.PWM_PIN_D6, 5000, 1000,
   PWM.ScaleFactor.Microseconds, false);

// PWM Ausgänge starten
pwm1.Start();
pwm2.Start();

// Analogen Eingang einrichten zum verstellen der Duration für die PWM Ausgänge
AnalogInput ai = new AnalogInput(Cpu.AnalogChannel.ANALOG_0);

double addDuration = 0;

while (true)
{
   // Abfrage des Analogen Einganges
    addDuration = ai.Read();

    // Neuen Duration Wert zuweisen zwischen 1000µs bis 2000µs
    pwm1.Duration = 1000 + (uint)(addDuration * 1000);
    pwm2.Duration = 1000 + (uint)(addDuration * 1000);

}

Dieser Inhalt wird in der ‘Main()’ Methode ausgeführt.

Mit dem analogen Eingang wird die Duration neu festgelegt, was mit einem Potentiometer bestimmt werden kann.

Netduino_zwei_Servos_Steckplatine
Hier sollten möglichst schwache Servos verwenden werden, um den Netduino nicht sonderlich zu belasten andernfalls ist eine externe Versorgung notwendig.

Für diesen Test funktionierten auch die analog Mini Servos, wenn auch nicht ideal (Leider keine passenden Digital Servos). Dreht man nun an dem Potentiometer, so kann man erkennen, dass der Servo am Port mit 200Hz schneller reagiert und der andere mit 50Hz nachzieht.

PWM Steuerung mit verschiedenen Frequenzen
Highspeed Aufnahme mit zwei Servos. Der mit dem Kreuzarm, wird mit 200Hz betrieben. Der mit den einzelnen Arm, wird mit 50Hz angesteuert.

Zudem benötigt die Klasse beim Zuweisen einer neuen Duration etwas Zeit. Grund hierfür ist, dass beim Zuweisen gewartet wird (siehe dazu ggf. Interrupt). Das bedeutet, wenn im Programmcode die Duration einen neuen Wert erhält, so kann es einige Millisekunden dauern, bis es an der Stelle weiter geht. Deutlich wird dies beim Zeitstoppen. Die folgenden Ergebnisse zeigen die Dauer eines Durchlaufens vom Einlesen des Analogen Wertes und anschließend das Zuweisen der beiden PWM Ports.

image image
Netduino Plus 2 Netduino Plus 1

Eigentlich sollte hier die Iterationsdauer fast immer gleich sein, aber sie springt teilweise von 2100 Mikrosekunden bis über 4000 Mikrosekunden bei dem Netduino Plus 1. Der Netduino Plus 2 ist da schon wesentlich konstanter und ist im Schnitt eine halbe Millisekunde schneller, was allein der höheren Taktrate zu verdanken ist.

Zusammengefasst ist klar, dass eine höhere Steuerfrequenz weniger Zeit benötigt, und die zu steuernde Einheit in kürzerer Periodendauer ein neues Steuersignal erhält. Die Frequenzen weit über 50Hz sind aber gut für digital Servos und ESC Regler (Brushless Regler) geeignet. Wie hoch die maximale Steuerfrequenz sein darf ist von der Abtastung des Reglers abhängig.

Verwendete Inhalte:

  • Netduino Plus und Netduino Plus 2
  • Analog Servo; 4,8V-6V  (oder besser ein Digital Servo)
  • Potentiometer
  • Kabel

Die Beispiel Solution mit .NET Micro Framework 4.2 zum Downloaden.

Mittwoch, 1. Mai 2013

PWM Signal Einlesen mit dem Netduino

PwmSignal_Netduino2
Der Empfänger am Netduino

Es gibt zwei Lösungen, wie man ein PWM Signal einlesen kann. Über eine RC Schaltung mit der “AnalogInput” Klasse oder vollständig programmatisch. Wenn keine Bauteile wie Widerstand und Kondensator vorhanden ist, dann erübrigt sich das letztere. Beim Arduino kennt man bereits aus der Library die Methode ‘pulseIn()’. Leider wurde das für den Netduino noch nicht umgesetzt. Alternativ bleibt die Möglichkeit, die sich ändernden Pulszustände zeitlich zu messen und das geht hervorragend mit der “InterruptPort” Klasse.

/// <summary>
/// Dieses Klasse ermöglicht das genaue Einlesen des eingehenden Pulssignals
/// </summary>
private static InterruptPort _IntPort = new InterruptPort(
    Pins.GPIO_PIN_D0,
    true,
    Port.ResistorMode.Disabled,
    Port.InterruptMode.InterruptEdgeBoth);

/// <summary>
/// Wird verwendet um den Startzeit des Pulssignals zu merken
/// </summary>
private static long _HighTicks = 0;
/// <summary>
/// Kann verwendet werden um einen Servo anzusteuern
/// </summary>
private static PWM _PwmOutput = new PWM(Cpu.PWMChannel.PWM_0, 20000, 1500, PWM.ScaleFactor.Microseconds, false);

public static void Main()
{
    // PWM Ausgang starten
    _PwmOutput.Start();
    // Event zuweisen
    _IntPort.OnInterrupt += new NativeEventHandler(_IntPort_OnInterrupt);

    while (true)
    {
        Thread.Sleep(1000);
    }
}
/// <summary>
/// Wird ausgeführt, wenn sich der Zustand am Eingang geändert hat.
/// </summary>
private static void _IntPort_OnInterrupt(uint data1, uint data2, DateTime time)
{
   if (data2 == 1)
    {
        // Startzeit des Pulssignals merken
        _HighTicks = time.Ticks;
    }
    else
    {
        // Mit Ende des High Zustands wird die aktuelle
        // Zeit mit der begin Zeit abgerechnet und erhält hier
        // die Pulslänge des High Signals

        double result = time.Ticks - _HighTicks;

        // Werte werden zwischen 10.000 bis 20.000 ausgegeben
        Debug.Print(result.ToString());

       if (result < 20000)
        {
            // Das Pulssignal darf nur zwischen 1000 bis 2000 sein
            _PwmOutput.Duration = (uint)(result / 10);
        }
    }
}

Zum einlesen eines PWM Signals

Über die PWM Klasse gebe ich das eingelesene Signal wieder aus, um z.B. einen Servo oder Regler anzusteuern. Sinn ergibt dies jedoch, wenn der Wert noch verarbeitet wird, bevor es weiter gegeben wird.

Solution zum Downloaden: ExampleReadPwm

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...