10
votes

I tried controlling the servo with softPwm using the wiringPi Library but this made the servo stutter. Therefore I want to use the hardware PWM pin on the Raspberry Pi (GPIO18) with the wiringPi library. But I don't understand how to set the frequency to 50 Hz and change the duty cycle to have a pulse width ranging from 0.8 ms to 2.5 ms.

I found the following relationship on the internet (i dont know if it is correct):

pwmFrequency in Hz = 19.2e6 Hz / pwmClock / pwmRange.

i know the clock divisor max value is something around 4000 and the Raspberry Pi PWM clock has a base frequency of 19.2 MHz. so this gives me ~4,8KHz.

i already got these settings which should give me ~50Hz using the following relationship:

//put PWM in mark-space mode, which will give you 
//the traditional (and easily predictable) PWM    
pwmSetMode(PWM_MODE_MS);
//setting ping GPIO 18 as a pwm output
pinMode(18,PWM_OUTPUT);
//Set clock divisor to 4000
pwmSetClock(4000);
pwmSetRange (10) ;

I dont got a oscilloscope to test the output signal to check what setting changes what. this makes it hard to find it out myself.

Long story short: Can anyone tell me how I can achieve a duty cycle with a pulse width of 0,8ms to 2,1ms for controlling a servo using the hardware PWM on the Raspberry Pi.

4

4 Answers

9
votes

I'm a complete newby to Pi and to Servo's. But I got it to work with wiringPi.

It says here that we're looking to create pulse of 1ms to 2ms in length, every 20ms or so. Assuming this 19.2Mhz base clock is indeed correct, setting pwm clock to 400 and pwm range to 1000, should give a pulse at 48Hz or every 20.8 ms. Then setting pwm value to 48 should give you a 1ms long pulse and a pwm value of 96 should give you a 2ms long pulse. But you need to set the chip in pwm-ms mode. (Lots of shoulds here, since I do not have an osciolloscope either)

So to set it up:

  • gpio mode 1 pwm
  • gpio pwm-ms
  • gpio pwmc 400
  • gpio pwmr 1000

And then you can turn the servo from left to right via

  • gpio pwm 1 48
  • gpio pwm 1 96

(Actually, the servo I got worked from 28 up to 118; could be the servo) (The setup sequence seems important; could be a bug)

6
votes
if (wiringPiSetup () == -1) //using wPi pin numbering
 exit (1) ;

pinMode(1, PWM_OUTPUT);
pwmSetMode(PWM_MODE_MS); 
pwmSetClock(384); //clock at 50kHz (20us tick)
pwmSetRange(1000); //range at 1000 ticks (20ms)
pwmWrite(1, 75);  //theretically 50 (1ms) to 100 (2ms) on my servo 30-130 works ok
return 0 ;

Make sure you are using correct gpio pins!

Models A and B have one hardware PWM on pin 18 BCM (1 wPi).

Models A+ and B+ can output second hardware pwm on pins 13 and 19 BCM (23, 24 wPi)

2
votes

How about using RPIO instead? Here's the link to the library: http://pythonhosted.org/RPIO/index.html

Here's the PWM example: http://pythonhosted.org/RPIO/pwm_py.html And you may also use the C source directly: https://github.com/metachris/RPIO/tree/master/source/c_pwm

0
votes

I got wiringPi to do it through software bit-banging. I might have tried RPIO but my particular application requires that the audio output works, and I understand that RPIO's DMA makes audio go away. I might have also tried wiringPi's softPwm or even softServo, but I also require to run a DC motor through PWM, and I don't want to bring the whole system down to 50Hz just for the servo.

This program worked as a demonstration, has no jitter (because it doesn't continuously drive the positioning pulses), and lands on its target each time with a visibly indistinguishable error. Granted the Pi isn't doing much else at the time to interfere with the program's timing (except running an X server through SSH, gedit, terminal session, everything in top, etc.).

// Servo trial 11/15/14 by SLC

#include <wiringPi.h>

#include <stdio.h>
#include <unistd.h>

int main()
{
    wiringPiSetup();
    pinMode( 6, OUTPUT );
    digitalWrite( 6,  HIGH );

    int idx = 0, tries = 0;
    while ( tries++ < 30 )
    {
        int i;
        const int period[] = { 500, 1500, 2500 };

        printf( "Setting period to %i ms\n", period[idx] );
        for ( i = 0; i < 20; ++i )
        {
            // Output going through an inverter (to convert 3.3V to 5V)
            digitalWrite( 6, LOW );
            usleep( period[idx] );
            digitalWrite( 6,  HIGH );
            usleep( 20 * 1000 );
        }
        ++idx;
        idx %= 3;

        sleep( 2 );
    }
}

My servo is a Radio Shack Micro Servo, which appears identical to the other "micro" servos out there. I also found I could ditch the inverter and just drive the signal using the 3.3V GPIO.