Netduino home hardware projects downloads community

Jump to content


The Netduino forums have been replaced by new forums at community.wildernesslabs.co. This site has been preserved for archival purposes only and the ability to make new accounts or posts has been turned off.

BrandonW6

Member Since 29 Sep 2012
Offline Last Active Mar 10 2013 04:30 AM
-----

#37047 Many many PWMs

Posted by BrandonW6 on 11 October 2012 - 06:15 PM

Thanks for the info Carb, I may try out one of those boards later, I like the idea of driving it direct from USB.

But in any case, I had already ordered the Adafruit 16 channel 12 bit Pwm driver. I translated the arduino driver files into c# and found it works great using i2c to communicate to the board. Here is the driver code for anyone who needs it:

/*
 *      Adafruit 16 channel 12 bit PWM Servo Driver NETMF
 *      Code Translated by Brandon Watt Oct 2012
 *      
 * This board use I2C to communicate, 2 pins are required to 
 * interface. For Netduino/Arduino Uno, thats SCL -> Analog 5, SDA -> Analog 4
 * 
 * Brandon Watt: Inital release (1.0) Translated arduino code into c#
 * Based on driver files found at "https://github.com/adafruit/Adafruit-PWM-Servo-Driver-Library"
 * 
 */
using System;
using Microsoft.SPOT;
using System.Threading;
using Toolbox.NETMF.Hardware;

namespace Adafruit_16channel
{

    class Adafruit_PWMServoDriver
    {
        private MultiI2C _Device;

        private const byte PCS9685_SUBADR1 = 0x2;
        private const byte PCA9685_SUBADR2 = 0x3;
        private const byte PCA9685_SUBADR3 = 0x4;

        private const byte PCA9685_MODE1 = 0x0;
        private const byte PCA9685_PRESCALE = 0xFE;

        private const byte LED0_ON_L = 0x6;
        private const byte LED0_ON_H = 0x7;
        private const byte LED0_OFF_L = 0x8;
        private const byte LED0_OFF_H = 0x9;

        private const byte ALLLED_ON_L = 0xFA;
        private const byte ALLLED_ON_H = 0xFB;
        private const byte ALLLED_OFF_L = 0xFC;
        private const byte ALLLED_OFF_H = 0xFD;

        private byte _i2caddr;

        public Adafruit_PWMServoDriver(byte addr = 0x40, int ClockRateKhz = 200)
        {
            _i2caddr = addr;
            this._Device = new MultiI2C(_i2caddr, ClockRateKhz);
        }
        public void Begin()
        {
            Reset();
        }
        public void Reset()
        {
            write8(PCA9685_MODE1, 0x0);
        }
        public void setPWMFreq(float freq)
        {
            float prescaleval = 25000000;
            prescaleval /= 4096;
            prescaleval /= freq;
            prescaleval -= 1;
           // Debug.Print("Estimated pre-scale: " + prescaleval);
            byte prescale = (byte)System.Math.Floor(prescaleval + 0.5);
           // Debug.Print("Final Pre-scale: " + prescale);

            byte oldmode = read8(PCA9685_MODE1);
            byte newmode = (byte)((oldmode & 0x7F) | 0x10); // sleep
            write8(PCA9685_MODE1, newmode); // go to sleep
            write8(PCA9685_PRESCALE, prescale); // set the prescaler
            write8(PCA9685_MODE1, oldmode);
            Thread.Sleep(5);
            write8(PCA9685_MODE1, (byte)(oldmode | 0x80));

        }
        public void setPWM(byte num, UInt16 on, UInt16 off)
        {
            write8((byte)(LED0_ON_L + 4 * num), (byte)on);
            write8((byte)(LED0_ON_H + 4 * num), (byte)(on >> 8));
            write8((byte)(LED0_OFF_L + 4 * num), (byte)off);
            write8((byte)(LED0_OFF_H + 4 * num), (byte)(off >> 8));
        }

        private byte read8(byte addr)
        {
            byte[] ReadBuffer = new byte[1];
            this._Device.WriteRead(new byte[] { addr }, ReadBuffer);
            return ReadBuffer[0];
        }
        private void write8(byte addr, byte d)
        {
            this._Device.Write(new byte[] { addr, d });
        }
    }
}

To use this driver you must have the .Net MF toolkit as it uses the MultiI2c Device class

To setup the driver for your servo first call on the instance:
pwm.setPWMFreq(50); // This sets the frequency pre-scale to 50hz

To set the servo or PWM values you call:
pwm.setPWM(0, 0, PulseLength);  // (servo number 0-15, start of pulse, end of pulse)

Because of the prescale, the values for pulse length are lower then you expect. Since they correlate to percentage of entire on/off cycle, using 12 bits gives values between 0 to 4096 so to control a standard servo, the range runs from about 150 to 600. Or you can use the following function that does the calculation to map the values for you.

public static void setServoPulse(byte n, double pulse) 
        {
            double pulselength;
            pulselength = 20000; // 1,000,000 us per second
            pulselength /= 50; // 50 Hz
           
            pulselength /= 4096; // 12 bits of resolution
           
            pulse *= 1000;
            pulse /= pulselength;

            Debug.Print("Pulse " + pulse);
            pwm.setPWM(n, 0, (ushort)pulse);
        }

Figured someone else may want to use this board, and this will save you some time.


#36279 Netduino servo class

Posted by BrandonW6 on 30 September 2012 - 02:00 AM

I updated the sample for the new PWM classes in 4.2.0.1
Changes include PWM constructor code with new parameters and code to set angle.

/*
 * Servo NETMF Driver
 *      Coded by Chris Seto August 2010
 *      <chris@chrisseto.com> 
 *      
 * Use this code for whatveer you want. Modify it, redistribute it, I don't care.
 * I do ask that you please keep this header intact, however.
 * If you modfy the driver, please include your contribution below:
 * 
 * Chris Seto: Inital release (1.0)
 * Chris Seto: Netduino port (1.0 -> Netduino branch)
 * Chris Seto: bool pin state fix (1.1 -> Netduino branch)
 * 
 * 
 * */

using System;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using SecretLabs.NETMF.Hardware;
using SecretLabs.NETMF.Hardware.Netduino;

namespace Servo_API
{
    public class Servo : IDisposable
    {
        /// <summary>
        /// PWM handle
        /// </summary>
        private PWM servo;

        /// <summary>
        /// Timings range
        /// </summary>
        private int[] range = new int[2];

        /// <summary>
        /// Set servo inversion
        /// </summary>
        public bool inverted = false;

        /// <summary>
        /// Create the PWM Channel, set it low and configure timings
        /// </summary>
        /// <param name="pin"></param>
        public Servo(Cpu.PWMChannel channelPin) 
        {
            // Init the PWM pin
            servo = new PWM((Cpu.PWMChannel)channelPin, 20000, 1500,PWM.ScaleFactor.Microseconds, false);

            servo.DutyCycle = 0;
            // Typical settings
            range[0] = 1000;
            range[1] = 2000;
        }

        public void Dispose()
        {
            disengage();
            servo.Dispose();
        }

        /// <summary>
        /// Allow the user to set cutom timings
        /// </summary>
        /// <param name="fullLeft"></param>
        /// <param name="fullRight"></param>
        public void setRange(int fullLeft, int fullRight)
        {
            range[1] = fullLeft;
            range[0] = fullRight;
        }

        /// <summary>
        /// Disengage the servo. 
        /// The servo motor will stop trying to maintain an angle
        /// </summary>
        public void disengage()
        {
            // See what the Netduino team say about this... 
            servo.DutyCycle = 0; //SetDutyCycle(0);
        }

        /// <summary>
        /// Set the servo degree
        /// </summary>
        public double Degree
        {
            set
            {
                /// Range checks
                if (value > 180)
                    value = 180;

                if (value < 0)
                    value = 0;

                // Are we inverted?
                if (inverted)
                    value = 180 - value;

                // Set the pulse
                //servo.SetPulse(20000, (uint)map((long)value, 0, 180, range[0], range[1]));
                servo.Duration = (uint)map((long)value, 0, 180, range[0], range[1]);
            }
        }

        /// <summary>
        /// Used internally to map a value of one scale to another
        /// </summary>
        /// <param name="x"></param>
        /// <param name="in_min"></param>
        /// <param name="in_max"></param>
        /// <param name="out_min"></param>
        /// <param name="out_max"></param>
        /// <returns></returns>
        private long map(long x, long in_min, long in_max, long out_min, long out_max)
        {
            return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
        }
    }
}

using System;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using SecretLabs.NETMF.Hardware;
using SecretLabs.NETMF.Hardware.Netduino;
using Servo_API;

namespace NetduinoApplication5
{
    public class Program
    {
        public static void Main()
        {
            Servo servo = new Servo(PWMChannels.PWM_PIN_D9);
           
            // Change 'While' to 'For' to limit how many time it repeats
            for (int j = 0; j < 3; j++) 
            {
                for (int i = 0; i <= 180; i++)
                {
                    servo.Degree = i;
                    Thread.Sleep(10);
                }

                for (int i = 180; i >= 0; i--)
                {
                    servo.Degree = i;
                    Thread.Sleep(10);
                }
            }

        }

    }
}



home    hardware    projects    downloads    community    where to buy    contact Copyright © 2016 Wilderness Labs Inc.  |  Legal   |   CC BY-SA
This webpage is licensed under a Creative Commons Attribution-ShareAlike License.