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.
- iced98lx likes this