DEV Community

Cover image for Self-Aligning Satellite Dish in Rust: Servo Example
Ian Ndeda
Ian Ndeda

Posted on

Self-Aligning Satellite Dish in Rust: Servo Example

In this part, we'll use the configured PWM from the previous part to sweep a servo motor through 180°.

Create a new project file pwm.rs under examples, copy the contents of the last part into it and empty the loop section.

Table of contents

Requirements

  • 1 x Raspberry Pico board
  • 1 x USB Cable type 2.0
  • 9 x M-M jumper wires
  • 2 x Mini Breadboards
  • 1 x SG90 Servo Motor

Implementation

Connections Diagram

The electrical connections of the modules will be as shown below.

pwm-example-setup

Sweep

Duty cycle in the RP2040 is achieved by writing a value to the PWM's counter compare register. This value is compared to the value we earlier fixed as the TOP wrap around value (50,000 in our case). The duty cycle can be thus compared: Duty Cycle=CCTOP+1Duty\ Cycle = {CC \over TOP + 1} .

We saw in the last part that the SG90 operates within a duty cycle range of 5 - 10% of a 50Hz PWM signal. With a TOP value of 50,000 this translates to a CC value range of 2500 - 5000.

However, this value seems to vary across suppliers. After testing I found my particular servo's range is 3 - 13%, translating to CC value of 1500 - 6500.

Applying these to a servo sweep of 0° to 180° and back.

Add the below example application code under loop.

// Servo sweep trhough 180
for ccv in 1500..=6500 {
    pwm2.cc().modify(|_, w| unsafe { w.a().bits(ccv) });
    delay.delay_ms(1);

    if ccv == 6500 {
    delay.delay_ms(500);
    for ccv in (1500..=6500).rev() {
        pwm2.cc().modify(|_, w| unsafe { w.a().bits(ccv) });
        delay.delay_ms(1);
    }
}

delay.delay_ms(500);
Enter fullscreen mode Exit fullscreen mode

❗ You may have to tweak the above values for your servo.

At this point it we shall have to add the Delay function to our program. We begin by introducing its handle.

let mut delay = Delay::new(cp.SYST, 125_000_000);
Enter fullscreen mode Exit fullscreen mode

Delay is acquired from the cortex-m crate:

use cortex_m::delay::Delay
Enter fullscreen mode Exit fullscreen mode

❗ We shall comment away the configure_compass function call at the setup section of our code to allow the program to run without the compass being physically attached. Otherwise, the blocking writes of I2C we included earlier in the series will stop the code coming after from running.

Flashing the code thus far into the Pico should sweep the servo from 0° to 180° then 180° back to 0° every half a second.

servo-example-results

We can refactor the above above application code into a function sweep as below.

pub fn sweep(pwm: &pwm2, mut degrees: u16) {
    const BASE: u16 = 1500;// here pwm at position 0 degrees

    degrees = BASE + (degrees * 24);// ~28: movt by 1 degree 
    unsafe {
        pwm.cc().modify(|_, w| w.a().bits(degrees) );// move by one degree * no of degrees
    }
}
Enter fullscreen mode Exit fullscreen mode

Import the pwm channel.

use rp2040_pac::pwm::ch::CH;
Enter fullscreen mode Exit fullscreen mode

Our application code will now be:

// Servo sweep through 180
for deg in 0..=180 {
    sweep(pwm2, deg);
    delay.delay_ms(10);

    if deg == 180 {
    delay.delay_ms(500);
    for deg in (0..=180).rev() {
        sweep(pwm2, deg);
        delay.delay_ms(10);
    }
}

delay.delay_ms(500);
Enter fullscreen mode Exit fullscreen mode

Flashing this into the Pico should have the same sweep results as before.

Final Code

The final code up to this point can be found here.

In the next part we apply the PWM in our overall project

Top comments (0)