User avatar
metachris
Posts: 96
Joined: Wed Feb 06, 2013 1:52 pm
Location: Vienna, Austria

RPIO.PWM: Precise PWM via DMA for servos and more (1µs res)

Sat Mar 09, 2013 7:47 pm

I'm happy to announce that RPIO now includes PWM via DMA for very precise semi-hardware PWM. RPIO.PWM supports pulses with a maximum resolution of 1µs, all 15 available DMA channels and any number of GPIO's. Since it uses the same on-board PWM module as the sound card it may interfere with sound.

RPIO.PWM is written in C (source) and includes a Python wrapper compatible with Python 2 and 3. You can find the PWM documentation at pythonhosted.org/RPIO.The easiest way to install/update RPIO is with easy_install:

Code: Select all

$ sudo apt-get install python-setuptools
$ sudo easy_install -U RPIO
Here is an example with oscilloscope output (GPIO 15 is the blue channel, GPIO 17 the yellow one):

Code: Select all

# Setup PWM.Servo with the default 20ms subcycle
from RPIO import PWM
servo = PWM.Servo()
Image

Code: Select all

# Set a 4000us (4ms) pulse every 20ms for GPIO 15:
servo.set_servo(15, 4000)
Image

Code: Select all

# Now a 1000us (1ms) pulse for GPIO 17:
servo.set_servo(17, 1000)
Image

Code: Select all

# We can use the low-level PWM methods to add further pulses to a subcycle:
PWM.add_channel_pulse(0, 17, 200, width=100)
Image

Feedback is much appreciated!

Links
Last edited by metachris on Sat Mar 09, 2013 8:20 pm, edited 1 time in total.
pythonhosted.org/RPIO

User avatar
joan
Posts: 14175
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: RPIO.PWM: Precise PWM via DMA for servos and more (1µs r

Sat Mar 09, 2013 8:14 pm

Great work. :D

I was wondering if you know why DMA channel 16 doesn't seem to work. I asked the question before but didn't get a response.

User avatar
metachris
Posts: 96
Joined: Wed Feb 06, 2013 1:52 pm
Location: Vienna, Austria

Re: RPIO.PWM: Precise PWM via DMA for servos and more (1µs r

Sat Mar 09, 2013 8:19 pm

The datasheet (http://www.raspberrypi.org/wp-content/u ... herals.pdf) states on page 39 that
DMA Channel 15 however, is physically removed from the other DMA Channels and so has a different address base of 0x7EE05000.
But there is no register map for it under point 4.2.1.2, so probably it's not usable after all.
Last edited by metachris on Sat Mar 09, 2013 8:26 pm, edited 1 time in total.
pythonhosted.org/RPIO

User avatar
joan
Posts: 14175
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: RPIO.PWM: Precise PWM via DMA for servos and more (1µs r

Sat Mar 09, 2013 8:26 pm

metachris wrote:The 16th DMA channel is probably usable but has a different base address. The datasheet (http://www.raspberrypi.org/wp-content/u ... herals.pdf) states on page 39 that
DMA Channel 15 however, is physically removed from the other DMA Channels and so has a different address base of 0x7EE05000.
I use the proper base address. Still can't get it to work. Perhaps it's been used by the GPU. Anyhow it's a side-issue.

trouch
Posts: 310
Joined: Fri Aug 03, 2012 7:24 pm
Location: France
Contact: Website

Re: RPIO.PWM: Precise PWM via DMA for servos and more (1µs r

Sat Mar 09, 2013 9:19 pm

Very nice work, thanks for sharing !
Will take a look to replace my poor threaded PWM in WebIOPi.

Can I suggest to add a pulse angle function ?
pulse = 1520 + (angle*400)/45
gives a µs pulse for a futaba servo standard :
1520µs neutral
400µs / 45° travel
=> -45° = 1120µs
=> 45° = 1920µs

WebIOPi - Raspberry Pi REST Framework to control your Pi from the web
http://store.raspberrypi.com/projects/webiopi
http://code.google.com/p/webiopi/
http://trouch.com

User avatar
metachris
Posts: 96
Joined: Wed Feb 06, 2013 1:52 pm
Location: Vienna, Austria

Re: RPIO.PWM: Precise PWM via DMA for servos and more (1µs r

Tue Mar 12, 2013 11:47 am

Thanks for the suggestion about the angle method for Futaba servos. I was wondering whether this same angle method is used only by Futaba or a wider adopted standard. If it is only Futaba, I'll need to think of a way to make it clear to users (eg. servo.set_angle(angle=30, servo_type=SERVO_TYPE_FUTABA)). What do you guys think about that?
pythonhosted.org/RPIO

trouch
Posts: 310
Joined: Fri Aug 03, 2012 7:24 pm
Location: France
Contact: Website

Re: RPIO.PWM: Precise PWM via DMA for servos and more (1µs r

Tue Mar 12, 2013 12:20 pm

this is not futaba specific and it's widely used.
we also see 1000/1500/2000µs for -45°/0°/+45° in electronic forums
always specifying the formula used is not a good idea, or assume one by default.

WebIOPi - Raspberry Pi REST Framework to control your Pi from the web
http://store.raspberrypi.com/projects/webiopi
http://code.google.com/p/webiopi/
http://trouch.com

leechpool
Posts: 4
Joined: Sun Jun 15, 2014 12:17 pm

Re: RPIO.PWM: Precise PWM via DMA for servos and more (1µs r

Wed Jul 30, 2014 11:34 pm

Hi,

I'm new to Raspberry Pi and servos etc. I was very pleased to find RPIO.PWM as a way of controlling servos without the excessive jitter I was getting when using RPi.GPIO. I very much appreciate the effort gone into creating RPIO.PWM.

I'm trying to create a web controlled robot with Raspberry Pi camera on a pan tilt mechanism. I'm using a Henge MD260 servo to do the panning because it gives 260 degrees. However, using RPIO.PWM, although it is jitter free, I find the range of movement available with the servo is reduced by about 10% - it can no longer reach the full 260 degrees. I'd assumed that using the same pulse length (translating the duty cycle from RPi.GPIO to microseconds for RPIO.PWM) would achieve exactly the same results but smoother. But this was not the case; the centre and extremes were at different pulse lengths. I experimented by gradually increasing / decreasing the pulse length to find the extremes of movement again with RPIO.PWM but they do not give the same range.

Can anyone shed light on why this is and is there any way I can get the range of movement using PRIO.PWM I had with RPi.GPIO. I would really appreciate some help.

Thanks
:)

servo: http://www.henge-rc.com/showproduct.asp ... 134&sid=38

spec:
Rotating Angle: 260°
Operating voltage range: 4.5V—6.0V
Operating speed: 0.10 sec/60°6V 0.12 sec/60°4.8V
Torque: 2.0kg.cm 6V 1.8kg.cm 4.8V
Distance: 90°(400usec)
Input pulse frequency: 50-200Hz
Pulse width: 1520us (wide band)
Locked rotor current: 600mA
Dead band width: ≤4usec
Bearing material: Metal
Gear material: 4 metal gears and 1 nylon gear
Size: 22.5×11.5×24.6mm
Weight: 12g

User avatar
joan
Posts: 14175
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: RPIO.PWM: Precise PWM via DMA for servos and more (1µs r

Mon Aug 04, 2014 9:55 am

Is it a normal servo, i.e. 1500 micros for middle, 1000 micros for one end and 2000 micros for the other.

I don't understand what
Pulse width: 1520us (wide band)
is trying to say.

leechpool
Posts: 4
Joined: Sun Jun 15, 2014 12:17 pm

Re: RPIO.PWM: Precise PWM via DMA for servos and more (1µs r

Wed Aug 06, 2014 6:01 pm

Hi Joan,
Yes it is a normal servo except that it travels 260 degrees instead of the usual 180 degrees. From memory I found about 1520 microseconds was the centre and 800 to 900 was one extreme with around 1950 being the other. Depending on which library I used to generate the pulse, the exact number changed. This surprised me but I assumed it was something to do with details of the pulse shape (perhaps its not entirely square, perhaps there is over shoot or ringing or something). However, even if pulse shape is "shifting" the response, I don't understand why pushing the pulse width further from the centre value can't compensate and reach the full extreme in each direction. It does give full travel in a clockwise sense; it is in the anticlockwise sense that it falls 20 to 30 degrees short when using PRIO.PMW compared to RPi.GPIO.

the "Pulse width: 1520us (wide band)" also confused me- I assume its referring to the mid point.

If anyone can shed any light on this I would be very grateful.
:D

User avatar
joan
Posts: 14175
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: RPIO.PWM: Precise PWM via DMA for servos and more (1µs r

Wed Aug 06, 2014 7:08 pm

leechpool wrote:Hi Joan,
Yes it is a normal servo except that it travels 260 degrees instead of the usual 180 degrees. From memory I found about 1520 microseconds was the centre and 800 to 900 was one extreme with around 1950 being the other. Depending on which library I used to generate the pulse, the exact number changed. This surprised me but I assumed it was something to do with details of the pulse shape (perhaps its not entirely square, perhaps there is over shoot or ringing or something). However, even if pulse shape is "shifting" the response, I don't understand why pushing the pulse width further from the centre value can't compensate and reach the full extreme in each direction. It does give full travel in a clockwise sense; it is in the anticlockwise sense that it falls 20 to 30 degrees short when using PRIO.PMW compared to RPi.GPIO.

the "Pulse width: 1520us (wide band)" also confused me- I assume its referring to the mid point.

If anyone can shed any light on this I would be very grateful.
:D
The RPIO times should be accurate, i.e. ask for 1500 us and you get 1500 us. The RPi times will probably be +100 us or so if they are timed by Linux delays (unless the software compensates by asking for a shorter delay to allow for the wake-up time).

I don't know why RPIO doesn't allow for the needed range.

You could try servoblaster or my pigpio Python module. I'm sure at least one will give the control you need.

leechpool
Posts: 4
Joined: Sun Jun 15, 2014 12:17 pm

Re: RPIO.PWM: Precise PWM via DMA for servos and more (1µs r

Mon Aug 11, 2014 10:24 pm

Hi Joan,
You could try servoblaster or my pigpio Python module. I'm sure at least one will give the control you need.
Thanks for your suggestion- you were right. I tried pigpio and it worked! It gave the full range of movement (same as RPi.GPIO) and it was smooooth. I'm very pleased. I'd be really interested to know why RPIO.PWM give's a reduced range of travel in my application. I might try looking at the pulses with an oscilloscope from work to see if it sheds any light on the issue.....

Thanks again for the help.
:D

linhartr22
Posts: 1
Joined: Mon Nov 24, 2014 11:53 pm

Re: RPIO.PWM: Precise PWM via DMA for servos and more (1µs r

Tue Nov 25, 2014 12:48 am

I'm having trouble with the RPIO.PWM library. I'm working up to controlling a strip of Common Anode RGB LEDs but for now I'm just trying to figure out the PWM library. I have the LED Anode wired to the Pi (model B Rev 2) +3V (P1-1) and the LED Red, Grn and Blu Cathodes wired through current limiting resistors (330 ohm) to GPIO 17, 27, 21 (P1-11, 13, 15). This should result in the LED being off when the GPIO pin is high.

Here's the code I wrote

Code: Select all

## Import PWM Library.
from RPIO import PWM
## PWM Increment usec. Default = 10 usec.
PWM_INCR = 10
## Init PWM Library.
PWM.setup(PWM_INCR)
## PWM DMA Channel.
LED_DMA = 0
## PWM Cycle usec. Default = 20000 usec.
PWM_CYCLE = 20000
## Init PWM channel.
PWM.init_channel(LED_DMA, PWM_CYCLE + PWM_INCR)
## RefreshLED.
## redPWM, grnPWM and bluPWM percentage (0 (0%) to 1 (100%))
def RefreshLED(redPWM, grnPWM, bluPWM):
  ## LED GPIO Pins.
  LED_RED_GPIO = 17
  LED_GRN_GPIO = 27
  LED_BLU_GPIO = 22
  ## Clear last PWM.
  PWM.clear_channel(LED_DMA)
  ## Refresh LED. Inverted for common anode LED.
  PWM.add_channel_pulse(LED_DMA, LED_RED_GPIO, 0, int((PWM_CYCLE - (PWM_CYCLE * redPWM)) / PWM_INCR)) 
  PWM.add_channel_pulse(LED_DMA, LED_GRN_GPIO, 0, int((PWM_CYCLE - (PWM_CYCLE * grnPWM)) / PWM_INCR)) 
  PWM.add_channel_pulse(LED_DMA, LED_BLU_GPIO, 0, int((PWM_CYCLE - (PWM_CYCLE * bluPWM)) / PWM_INCR)) 

## Test LED
## Import 'time' library. Allows us to use 'sleep'
import time
## LED 100%
RefreshLED(1, 1, 1)
time.sleep(5)
## LED 0%
RefreshLED(0, 0, 0)
time.sleep(5)
## LED 100%
RefreshLED(1, 1, 1)
time.sleep(5)
## LED 0%
RefreshLED(0, 0, 0)
time.sleep(5)
The LED lights up but it doesn't change when I run the program:
[email protected]:/home/pi$ sudo python pwmrgbled.py
Using hardware: PWM
PW increments: 10us
Initializing channel 0...
clear_channel: channel=0
add_channel_pulse: channel=0, gpio=17, start=0, width=0
init_gpio 17
add_channel_pulse: channel=0, gpio=27, start=0, width=0
init_gpio 27
add_channel_pulse: channel=0, gpio=22, start=0, width=0
init_gpio 22
clear_channel: channel=0
add_channel_pulse: channel=0, gpio=17, start=0, width=2000
add_channel_pulse: channel=0, gpio=27, start=0, width=2000
add_channel_pulse: channel=0, gpio=22, start=0, width=2000
clear_channel: channel=0
add_channel_pulse: channel=0, gpio=17, start=0, width=0
add_channel_pulse: channel=0, gpio=27, start=0, width=0
add_channel_pulse: channel=0, gpio=22, start=0, width=0
shutting down dma channel 0
clear_channel: channel=0
[email protected]:/home/pi$
I've done this before with Arduino but this is my first attempt with a Pi so be on the lookout for noob blunders. ::grin::

rogerjames99
Posts: 29
Joined: Fri Sep 28, 2012 2:58 pm

Re: RPIO.PWM: Precise PWM via DMA for servos and more (1µs r

Tue Jan 20, 2015 9:50 pm

Is the module still under active development? I have tried to use use it and come up against an odd problem when using the

If you initialise two DMA channels and then add a pulse to the second channel, the subcyle timing and pulse width on the second channel is double what it should. I have measured this on an oscilloscope.

Here is is some sample code that illustrates the problem.

#!/usr/bin/env python

import RPIO
from RPIO import PWM
import time

PWM.setup()
RPIO.setup(25, RPIO.OUT, initial=RPIO.LOW)
PWM.init_channel(4)
PWM.init_channel(0)
PWM.add_channel_pulse(0, 25, 0, 100)
time.sleep(5000)

Has anyone else observed this? I have a quick look at the code. It looks to me that if you have two dma units pushing data into the pwm fifo and both have WAIT_RESP set on the bus side then the subcycle time is going to be twice as long as you think it should be.

Roger

jasomo
Posts: 19
Joined: Fri Feb 07, 2014 9:47 am

Re: RPIO.PWM: Precise PWM via DMA for servos and more (1µs r

Fri Feb 20, 2015 8:38 pm

Hi all!

Im trying to control my ESC/motor through this RPIO.PWM methods.

Tested the motor_test.py (https://solenerotech1.wordpress.com/motor-py/) working good.
My esc enters the throttle range setting option...

I tried to develop my own c program to achieve the same result, but it doesn work.

The ESC protocol:
- Throttle at upper limit
- Connect ESC
- Wait tones... Throttle at lower limit
- Wait tones...

So my pseudocode:

CODE: SELECT ALL
#define GPIO         17
#define CHANNEL         0
#define SUBCYCLE_TIME   SUBCYCLE_TIME_US_DEFAULT // 20000 setup(PULSE_WIDTH_INCREMENT_GRANULARITY_US_DEFAULT, DELAY_VIA_PWM);
init_channel(CHANNEL, SUBCYCLE_TIME);
add_channel_pulse(CHANNEL, GPIO, 0, 1999);
getchar();
add_channel_pulse(CHANNEL, GPIO, 0, 0);


What am i doing wrong?
How is the best way to send the pulses to the motor? (where to start, the pulse, width...?)

Thanks all!

l0ci
Posts: 1
Joined: Mon Mar 02, 2015 5:02 am

Re: RPIO.PWM: Precise PWM via DMA for servos and more (1µs r

Mon Mar 02, 2015 5:16 am

I just did exactly this yesterday. I'm using these 40A ESCs from HobbyKing: http://www.hobbyking.com/hobbyking/stor ... duct=70234

I found that I had to do it a little differently. My ESCs have already been calibrated and set up by my flight controller board if that makes a difference. I tried the same as you, starting high then dropping low. For a high and low though, you want around 2000 and 1000, not 2000 and 0. I had no luck with that. Instead I tried starting up with a setting of 1000 for a couple of seconds and viola, the ESC would power up and initialize, then I could do whatever I liked with it.

Here's a stripped down snippet of my own motor test in Python if it helps out at all. It's not C, but I hope this helps out.

from RPIO import PWM
ESC1 = 17 # On GPIO 17
servo = PWM.Servo()

servo.set_servo(ESC1, 1000) # Initial setup
time.sleep(5) # During which I get the confirmation beep from my ESCs

# Ramp up a few steps
servo.set_servo(ESC1, 1100)
time.sleep(1)
servo.set_servo(ESC1, 1150)
time.sleep(1)
servo.set_servo(ESC1, 1200)
time.sleep(1)

# Throttle down before shutting down. Have to do it twice for some reason.
servo.set_servo(ESC1, 1000)
time.sleep(0.25)
servo.set_servo(ESC1, 1000)
time.sleep(0.25)

Return to “Interfacing (DSI, CSI, I2C, etc.)”