http://www.perlmonks.org?node_id=1194568

I've received quite a few pieces of great feedback from a variety of people since posting about writing my Perl/Raspberry Pi tutorial, and a lot of good has come from that feedback already.

One person who pointed out one minor mistake of mine with follow up with some other questions, asked about how to run a servo without needing a controller board. I realized that I hadn't exposed a couple of functions in the core WiringPi::API distribution that allowed a user to configure the PWM frequency, which is required as the Pi default doesn't play nice with typical servos.

The default PWM base frequency on a Pi is 19.2MHz, which is then divided by the clock signal (default: 32) and the PWM range (0-1023). So to get the default operating frequency:

# base range clck operational freq 19.2e6 / 1024 / 32 == 586Hz

To get this down to 50Hz required for a typical servo, I bumped up the range to 2000 (nice round number), and then just bounced around with the clock signal divider until I hit 50:

19.2e6 / 2000 / 192 == 50Hz

To be honest, I found the formula online, but then read through the datasheet for the Pi, and went on my way to not just copy and paste, but figure out exactly what frequency meant, what the divisors meant and then felt comfortable knowing exactly how PWM works ;)

So, for full left, the servo requires a pulse of 50Hz for ~1ms (PWM 50), centre is ~1.5ms (PWM 150) and full right is ~2.5ms (PWM 250). My servo required me to tweak these numbers a tiny bit to get the full 180 degree motion.

Anyway, to some code. I've commented the code as to what's happening and when, but an overall is that when started, the servo will go full-left, wait a sec, then swing from left-to-right, then back right-to-left until a SIGINT (CTRL-C) is caught, at which time, it puts the servo back to left position, then puts the pin back to INPUT mode so that if a different software is run after, the pin won't still be in PWM mode.

Unfortunately, at this time, we still require sudo for PWM functionality. It's being looked at. It's the *only* thing left that requires root.

use warnings; use strict; use RPi::WiringPi; use RPi::WiringPi::Constant qw(:all); die "need root!\n" if $> !=0; use constant { LEFT => 60, RIGHT => 255, CENTRE => 150, PIN => 18, DIVISOR => 192, RANGE => 2000, DELAY => 0.001, }; # set up a signal handler for CTRL-C my $run = 1; $SIG{INT} = sub { $run = 0; }; # create the Pi object my $pi = RPi::WiringPi->new; # create a signal pin, set mode to PWM output my $s = $pi->pin(PIN); $s->mode(PWM_OUT); # configure PWM to 50Hz for the servo $pi->pwm_mode(PWM_MODE_MS); $pi->pwm_clock(DIVISOR); $pi->pwm_range(RANGE); # set the servo to left max $s->pwm(LEFT); sleep 1; while ($run){ for (LEFT .. RIGHT){ # sweep all the way left to right $s->pwm($_); select(undef, undef, undef, DELAY); } sleep 1; for (reverse LEFT .. RIGHT){ # sweep all the way right to left $s->pwm($_); select(undef, undef, undef, DELAY); } sleep 1; } # set the pin back to INPUT $s->pwm(LEFT); $s->mode(INPUT);

It won't be until later today after I get some extra tests written and update a couple of other items that are lingering, but it is available on Github for now.

Note that the Pi may struggle to power the servo and it may cause a low-voltage situation, so it's best you power your 5v servo from an alternate source (I just happen to have a few powered-up Arduino's nearby all with 5v pins accessible). Also note that even though the +/- of the servo is 5v, you can safely connect the signal pin on it to the 3.3v GPIO on the Pi as on the servo, the pin is input only (ie. it doesn't feed back to the Pi).

Replies are listed 'Best First'.
Re: Using a controllerless servo on the Raspberry Pi with Perl
by marto (Bishop) on Jul 09, 2017 at 07:42 UTC

    Nice work.

    "Note that the Pi may struggle to power the servo and it may cause a low-voltage situation, so it's best you power your 5v servo from an alternate source (I just happen to have a few powered-up Arduino's nearby all with 5v pins accessible). Also note that even though the +/- of the servo is 5v, you can safely connect the signal pin on it to the 3.3v GPIO on the Pi as on the servo, the pin is input only (ie. it doesn't feed back to the Pi)"

    I think it'd be a good idea to epmhasize this early on, in addidtion to causing the pi to freeze/bounce (hitting lower than 4.7v IIRC), powering an inductive load from the pi directly can cause a voltage spike to flow back on the 5v rail, I dare say many a pi has been fried this way :) Use of decoupling capacitors can negate such problems. Safety first and all that. Keep up the good work.

      powering an inductive load from the pi directly can cause a voltage spike to flow back on the 5v rail
      Use of decoupling capacitors can negate such problems

      You are mixing up two things there.

      Decoupling capacitors are required to keep the supply voltage stable when lots of transistors inside the controller switch simultanously and parasitic capacitors inside the controller are forced to change their charge. The PCB tracks to the supply pins behave like a resistor or inductor, so even a very good, nearly ideal power supply simply can not keep the voltage at the supply pins constant. The decoupling capacitors can, because their track to the supply pins is extremely short. As a welcome side-effect, the decoupling capacitors also short high frequency noise to ground.

      Inductive loads switched to DC suppiles create a more or less large voltage spike with reversed polarity. This voltage is usually large enough to kill nearby semiconductors, typically the switching transistor. To get rid of the voltage, connect a fast diode in parallel to the inductive load, but with reversed polarity. This is commonly called a flyback diode. While the load is powered, the diode is passive. Because the voltage spike has reversed polarity, the diode becomes conductive and shorts out the voltage from the load, converting the energy to heat in the diode and the load's wire. The remaining circuit sees a reverse voltage of about 0.7 V across the load that does not harm usual Si semiconductors.

      Another thing: Yes, the I/O pins of AVR microcontrollers have a significant capability to sink and source current. The ATmega328 (datasheet), commonly found on Arduino boards, can sink and source at least 20 mA @ 5 V, 10 mA @ 3 V, abs. max. 40 mA (see datasheet page 365 and following). But the total current must not exceed 100 mA per port, and it must not exceed 200 mA total. Other AVR processors have very similar ratings, and I would expect similar performance from PIC chips. But for anything but a simple LED or a TTL/CMOS compatible input, I would use a driver transistor. I/O pins also have a pair of diodes (see page 97), but those diodes are just clamping diodes that prevent killing the controller by ESD. They are not designed to discharge an inductive load.

      Oh, and don't expect other, younger microcontrollers to have similar driving capabilities. Let's look at the SAM4S series (datasheet), an ARM Cortex M4 with a lot of peripherals, more I/Os, more flash, more RAM, and faster than AVRs. The I/O pins can sink and source max. 2 mA (see page 1143), a few pins can sink and source 4 mA, and only two pins can sink and source 30 mA. Abs. max. total current is 100 mA for 64 pin devices, 150 mA for 100 pin devices. All of that at an I/O voltage of 3.3 V typ. Other ARM-based microcontrollers have similar limits. They can directly drive a low-current LED, or CMOS compatible logic inputs, but for everything else, you need a driver transistor.

      Alexander

      --
      Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

      AVR and PIC IO pins are designed to handle loads like PWM controlled servos. Still, bypass capacitors should be used.

      Also, until recently, replacing an AVR or PIC was less expensive. Still is, except that the Pi Zero is inexpensive enough to use instead of an AVR or PIC in many applications. And, it gets Perl closer to hardware it's controlling.

      Update/addendum:

      The Pi is US$25 to US$35 and the micro in it not replaceable except by an expert, so if you damage the micro, you buy another Pi. while there are surface mount AVRs and PICs, you can still get ones you can plug into a socket, so are US$2 to US$5 to replace for many "hobby" projects. So, using an AVR or PIC as a "smart" I/O handler (under the control of a Pi or other, similar device) makes a lot of sense.

      The Pi Zero is US$5, so replacing it is almost as inexpensive as replacing an AVR/PIC. So, for some uses, the Pi Zero could be used with out an AVR or PIC as an intermediary.

      (Though, because the I/O pins of AVRs and PICs are more robust than those of the Pi's micro, there are still times when adding an AVR or PIC along side the Pi Zero makes sense.)

        Hi ron, the pi isn't AVR or PIC, and the GPIO pins have nothing to protect the board. Can you tell more about what you mean here?
Re: Using a controllerless servo on the Raspberry Pi with Perl
by GrandFather (Sage) on Jul 12, 2017 at 21:36 UTC

    Your DELAY constant is 1 ms, but you say the pulse rate for the PWM servo is 50 Hz which is 20 ms, so assuming your control for loop has trivial overhead you are messing with the control pulse width 20 times faster than the pulse is generated. What don't I understand here? Could this be related to "My servo required me to tweak these numbers a tiny bit"?

    Premature optimization is the root of all job security