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

The problem of setting PWM OUT to PWM IN


  • Please log in to reply
3 replies to this topic

#1 XQuirrel

XQuirrel

    New Member

  • Members
  • Pip
  • 5 posts
  • LocationLondon

Posted 25 March 2012 - 06:47 PM

Hi,

I'm, an ArduCopter Radio Control boffin playing around with NetDuino, trying to write a library to take PWM IN from my Futaba RC Receiver, so that I can then use it to control whatever thingy I might want to control. The following code works and should be quite educational for peeps trying to make inroads into PWM.

I verified that the PWM OUT was working on NetDuino merely by plugging the negative lead on a Futaba Servo into the NetDuino's GND pin, the positive lead on the Futaba Servo into the NetDuino's +5V pin, and the Servo's PWM lead into Pin 9.

The Code to set it up was:

PWM servo = new PWM(Pins.GPIO_PIN_D9);

and I put the following in a loop that incrementally increased from a duration of 1000 milliseconds, to 2000 milliseconds, and then back to 1000, where it repeated the loop infinitum:

servo.SetPulse(20000, duration);

For an excellent tutorial on PWM doctrine, see here.

There's also an excellent background article by the same author, on how RC servos work,here.

Anyway, the servo naturally started at 90 degrees, moved slowly to 180 degrees, and then back, only to continue the process add infinitum until I pulled the plug.

This was the expected behaviour, and it should work for most RC servos.

Writing a PWN IN library should be as easy as feeding a PWM OUT signal from, say Pin 9, back in to, say, Pin 0, and setting up an interrupt, measuring the duration between the rise of the signal and the next fall of the same incoming PWM signal (it is a square wave), then accommodating the matter of the PWM frequency: if you look at the previous link, you'll see that PWM is all about the width of the positive part of the square wave, as a percentage of the period of the square wave.

Here's my Proof of Concept Code, it works, and I'll probably extend another Sunday. There are a couple of obvious improvements that need to be made, the first is that it should measure the period of the incoming signal (which coming from Netduino we already know is 10kHz, something which I accommodate in the code by later dividing by 10 ... hence, the following code will only work if the PWM frequency is in actual fact 10KHz ... but a universal solution should accommodate any PWM frequency, hence the code would be better changed to instead of dividing by 10, dividing by a factor of the measured PWM frequency. The other obvious change is minor but nevertheless, good form ~ I notice in the output that the 'measured' input peaks above 2000 (ie 2005), hence the measurement should be constrained to the expected range, in this case 1000 to 2000). But that's it, I think it is quite a good start, for me anyway!

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using SecretLabs.NETMF.Hardware;
using SecretLabs.NETMF.Hardware.NetduinoPlus;

namespace NetduinoPlusPWM
{
public class Program
{
/// <summary>
/// A counter that merely allows one interrupt cycle to pass so that there is a value in
/// pwm1ticksPrevious when calculations are done.
/// </summary>
static int pwm1Counter;
/// <summary>
/// A state that must be true for delta calculations to be done. 'Armed' merely means that
/// there is a value in pwm1ticksPrevious
/// </summary>
static Boolean pwm1Armed = false;
/// <summary>
/// The DateTime.Now expressed as a tick (long). Represents the latest time that the pwm1 interrupt fired.
/// </summary>
static long pwm1ticksCurrent;
/// <summary>
/// Represents the previous time that the pwm1 interrupt fired.
/// </summary>
static long pwm1ticksPrevious;
/// <summary>
/// The time difference between the PWM1 interrupt last firing and now firing (long)
/// </summary>
static long pwm1ticksDelta;
/// <summary>
/// The pwm1ticksDelta expressed as milliseconds.
/// </summary>
static long pwm1Duration;


public static void Main()
{
// Create a PWM OUT on Pin 9.
PWM servo = new PWM(Pins.GPIO_PIN_D9);

// Create a PWM IN on Pin 0: Note, the output from 9 is merely wired straight back into the PWM IN on Pin 0 to get this :))

InterruptPort pwm1 = new InterruptPort(Pins.GPIO_PIN_D0, false, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeBoth);

// Set up the interrupt callback function.
pwm1.OnInterrupt += new NativeEventHandler(pwm1_OnInterrupt);


while (true)
{

// These two for loops merely increment in steps of 10 upwards from 1000 to 2000, and then reverses down to 1000, ad infinitum

for (uint duration = 1000; duration < 2000; duration += 10)
{
// The PWM pulse width is set here, for example, if it is 1500 milliseconds, duration = 1500
servo.SetPulse(20000, duration);
// This output to the IDE Output Window merely compares the PWM Output with the measured PWM Input.
Debug.Print(duration + " :: " + pwm1Duration.ToString());
// Put a delay in to prevent too much printing to the Output Window
Thread.Sleep(50);

}

for (uint duration = 2000; duration > 1000; duration -= 10)
{
// The PWM pulse width is set here, for example, if it is 1500 milliseconds, duration = 1500
servo.SetPulse(20000, duration);
// This output to the IDE Output Window merely compares the PWM Output with the measured PWM Input.
Debug.Print(duration + " :: " + pwm1Duration.ToString());
// Put a delay in to prevent too much printing to the Output Window
Thread.Sleep(50);

}
}
}

static void pwm1_OnInterrupt(uint data1, uint data2, DateTime time)
{
// This is the time that the interrupt occured ~ i.e. when the signal on Pin 0 went high or low ~
//i.e. the InterruptPort was set as Port.InterruptMode.InterruptEdgeBoth
pwm1ticksCurrent = time.Ticks;
// The delay to arming the logic sequence is just so that there is a value in pwm1ticksPrevious when the logic is executed.
if (pwm1Armed)
{
pwm1ticksDelta = pwm1ticksCurrent - pwm1ticksPrevious;
// Accommodate PWM period of 10KHz
pwm1Duration = (pwm1ticksDelta / 10);
// Now that the PWM duration has been calculated, set the pwm1ticksCurrent value into the pwm1ticksPrevious register.
pwm1ticksPrevious = pwm1ticksCurrent;
}
else
{
// Delay a cycle before arming so that there is a value in pwm1ticksPrevious
if (pwm1Counter > 1)
{
pwm1Armed = true;
Debug.Print("PWM Armed");
}
pwm1Counter++;
pwm1ticksPrevious = pwm1ticksCurrent;
}


}

}
}

SAMPLE OUTPUT IS AS FOLLOWS (BOTH NUMBERS ON THE SAME LINE SHOULD BE APPROXIMATELY EQUAL).


The following PWM INPUT (RHS) is close enough to the PWM OUTPUT (LHS)

PWM_OUT :: PWM_IN
2000 :: 1984
1990 :: 2005
1980 :: 1984
1970 :: 1984
1960 :: 1962
1950 :: 1962
1940 :: 1941
1930 :: 1941
1920 :: 1920
1910 :: 1920
1900 :: 1898
1890 :: 1898
1880 :: 1877
1870 :: 1877
1860 :: 1856
1850 :: 1856
1840 :: 1834
1830 :: 1834
1820 :: 1813
1810 :: 1813
1800 :: 1792
1790 :: 1792
1780 :: 1792
1770 :: 1792
1760 :: 1770
1750 :: 1770
1740 :: 1749
1730 :: 1749
1720 :: 1728
1710 :: 1728
1700 :: 1706
1690 :: 1706
1680 :: 1685
1670 :: 1685
1660 :: 1664
1650 :: 1664
1640 :: 1642
1630 :: 1642
1620 :: 1621
1610 :: 1621
1600 :: 1600
1590 :: 1600
1580 :: 1578
1570 :: 1578
1560 :: 1557
1550 :: 1578
1540 :: 1536
1530 :: 1536
1520 :: 1514
1510 :: 1514
1500 :: 1493
1490 :: 1514
1480 :: 1472
1470 :: 1472
1460 :: 1472
1450 :: 1472
1440 :: 1450
1430 :: 1450
1420 :: 1429
1410 :: 1429
1400 :: 1408
1390 :: 1408
1380 :: 1386
1370 :: 1386
1360 :: 1365
1350 :: 1365
1340 :: 1344
1330 :: 1344
1320 :: 1322
1310 :: 1322
1300 :: 1301
1290 :: 1301
1280 :: 1280
1270 :: 1280
1260 :: 1258
1250 :: 1258
1240 :: 1237
1230 :: 1237
1220 :: 1216
1210 :: 1216
1200 :: 1194
1190 :: 1194
1180 :: 1173
1170 :: 1173
1160 :: 1152
1150 :: 1152
1140 :: 1152
1130 :: 1152
1120 :: 1130
1110 :: 1130
1100 :: 1109
1090 :: 1109
1080 :: 1088
1070 :: 1088
1060 :: 1066
1050 :: 1066
1040 :: 1045
1030 :: 1045
1020 :: 1024
1010 :: 1024
1000 :: 1002
1010 :: 1002
1020 :: 1002
1030 :: 1024
1040 :: 1024
1050 :: 1045
1060 :: 1045
1070 :: 1066
1080 :: 1066
1090 :: 1088
1100 :: 1088
1110 :: 1109
1120 :: 1109
1130 :: 1130
1140 :: 1130
1150 :: 1152
1160 :: 1152
1170 :: 1152
1180 :: 1152
1190 :: 1173
1200 :: 1173
1210 :: 1194
1220 :: 1194
1230 :: 1216
1240 :: 1216
1250 :: 1237
1260 :: 1237
1270 :: 1258
1280 :: 1258
1290 :: 1280
1300 :: 1280
1310 :: 1301
1320 :: 1301
1330 :: 1322
1340 :: 1322
1350 :: 1344
1360 :: 1344
1370 :: 1365
1380 :: 1365
1390 :: 1386
1400 :: 1386
1410 :: 1408
1420 :: 1408
1430 :: 1429
1440 :: 1429
1450 :: 1450
1460 :: 1450
1470 :: 1472
1480 :: 1472
1490 :: 1472
1500 :: 1472
1510 :: 1493
1520 :: 1493
1530 :: 1536
1540 :: 1514
1550 :: 1536
1560 :: 1536
1570 :: 1557
1580 :: 1557
1590 :: 1600
1600 :: 1578
1610 :: 1600
1620 :: 1600
1630 :: 1621
1640 :: 1621
1650 :: 1642
1660 :: 1642
1670 :: 1664
1680 :: 1664
1690 :: 1685
1700 :: 1685
1710 :: 1706
1720 :: 1706
1730 :: 1728
1740 :: 1728
1750 :: 1749
1760 :: 1749
1770 :: 1770
1780 :: 1770
1790 :: 1792
1800 :: 1792
1810 :: 1792
1820 :: 1792
1830 :: 1813
1840 :: 1813
1850 :: 1834
1860 :: 1834
1870 :: 1856
1880 :: 1856
1890 :: 1877
1900 :: 1877
1910 :: 1898
1920 :: 1898
1930 :: 1920
1940 :: 1920
1950 :: 1941
1960 :: 1941
1970 :: 1962
1980 :: 1962
1990 :: 1984
2000 :: 1984

Not too bad for a Sunday afternoon's tinkering, and also thanks to CW2 for his prompting below, also to Pete (Bainesbunch) for his similar prompt in the Netduino formum.

#2 CW2

CW2

    Advanced Member

  • Members
  • PipPipPip
  • 1592 posts
  • LocationCzech Republic

Posted 25 March 2012 - 07:08 PM

the theory, of course, is that if you change the PWM duration, you change the PWM OUTPUT frequency, hence measuring PWM IN should be as easy as measuring the time difference between the falling edge of one pulse, and the falling edge of the next one.

Nope, in the current implementation PWM runs at fixed frequency (10 kHz), it is the duty cycle what is changed - so you'd have to measure pulse length between opposite edges (rising - falling).

#3 XQuirrel

XQuirrel

    New Member

  • Members
  • Pip
  • 5 posts
  • LocationLondon

Posted 25 March 2012 - 07:42 PM

Nope, in the current implementation PWM runs at fixed frequency (10 kHz), it is the duty cycle what is changed - so you'd have to measure pulse length between opposite edges (rising - falling).


Thanks, CW2, I've now achieved that with Port.InterruptMode.InterruptEdgeBoth (and modified my post accordingly) and the test output now stacks up (which I have also edited to show the proper output) ... but having said that, I'm still to properly get my head around the implications of duty cycle! But it is nevertheless very good to see that PWM OUT is approximately equal to the same signal taken back in as input and then measured via interrupts!

Additional Edit: Thanks to your prompting, I've found an excellent PWM tutorial, and an excellent Servo tutorial by the same author, and once again modified my post accordingly, as well as adding those two links to it. Thanks again!

#4 NeonMika / Markus VV.

NeonMika / Markus VV.

    Advanced Member

  • Members
  • PipPipPip
  • 209 posts
  • LocationUpper Austria

Posted 26 March 2012 - 03:17 PM

Thanks for the links, I also want to learn some more about PWM. Will read them when I come home. Probably you would like to put them into the wiki, so others could see them (and also learn more about PWM)? =) Greets, Markus

NeonMika.Webserver
> Control your N+ and write webservice methods easyily
> Receive data from you N+ (in XML or JSON)
> Browse the SD on your N+ directly in the browser and d
own - and upload files

 

If you need help with NeonMika.Webserver, please just leave a note in the thread and/or contact me via Skype :)

 

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Mistakes teach you important lessons. Every time you make one, you are one step closer to your goal. ----
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------





0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users

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.