Skip to content

Commit

Permalink
pwm
Browse files Browse the repository at this point in the history
  • Loading branch information
ImplFerris committed Oct 28, 2024
1 parent fd628bc commit b652185
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 67 deletions.
1 change: 1 addition & 0 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- [Dim LED with RP HAL](./blinky/rp-hal.md)
- [Basic concepts](./blinky/no-std-main.md)
- [PWM](./blinky/pwm.md)
- [PWM in RP2350](./blinky/pwm-rp2350.md)
- [Watchdog](./blinky/watchdog.md)
- [Using External LED](./blinky/external-led.md)
- [Ultrasonic](./ultrasonic/index.md)
Expand Down
Binary file added src/blinky/assets/frequency-1hz.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/blinky/assets/frequency-50hz.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/blinky/assets/frequency-period.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/blinky/assets/led-pwm.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
68 changes: 68 additions & 0 deletions src/blinky/pwm-rp2350.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# PWM Peripheral in RP2350
The PWM peripheral is responsible for generating PWM signals. The Pico 2 features 12 PWM generators, known as slices, with each slice having two channels(A/B). This configuration results in a total of 24 PWM output channels available for use.

Refer the 1073th page of the [RP2350](https://datasheets.raspberrypi.com/rp2350/rp2350-datasheet.pdf) Datasheet for more information.

**Mapping of PWM channels to GPIO Pins**:

You can find the table on page 1073 of the [RP2350](https://datasheets.raspberrypi.com/rp2350/rp2350-datasheet.pdf) Datasheet for more information. You have to refer this table when using a specific GPIO, as each GPIO corresponds to a particular slice. For instance, using GP25 (for the LED) means you are working with output 4B, which is the B output of the fourth slice.

<img style="display: block; margin: auto;" alt="pico2" src="../images/gpio-map-pwm-channels.png"/>

Initialize the PWM slices by creating an instance using the PAC's PWM peripheral and reset control.
```rust
let mut pwm_slices = hal::pwm::Slices::new(pac.PWM, &mut pac.RESETS);
```

Retrieve a mutable reference to PWM4 from the initialized PWM slices for further configuration.
```rust
let pwm = &mut pwm_slices.pwm4;
```

Configure PWM4 to operate in phase-correct mode for smoother output transitions. (You can refer the [secrets arudion PWM](https://docs.arduino.cc/tutorials/generic/secrets-of-arduino-pwm/) if you want to know what is phase-correct)
```rust
pwm.set_ph_correct();
```

Get a mutable reference to channel B of PWM4 and direct its output to GPIO pin 25.
```rust
let channel = &mut pwm.channel_b;
channel.output_to(pins.gpio25);
```

## Fading Effect

For LED brightness, PWM works by rapidly turning the LED on and off. If this happens fast enough, our eyes perceive a steady light, and the brightness increases with a higher duty cycle.


In the previous example code, we use PWM to fade an LED.

### Fading Up
The code below gradually increases the LED brightness by adjusting the duty cycle from 0 to 25,000, with a small delay between each step:
```rust
for i in LOW..=HIGH {
delay.delay_us(8);
let _ = channel.set_duty_cycle(i);
}
```
The delay ensures the LED brightens gradually. Without it, the brightness would change too quickly for the eye to notice, making the LED appear to jump from dim to bright. The delay allows for a smooth, noticeable "fading up" effect.

Dont' believe me! Adjust the delay to 0 and observe. You can increase the delay (eg: 25) and observe the fading effect.

Note: `set_duty_cycle` function under the hood writes the given value into CC register(Count compare value).

### Fading Down
The following code decreases the LED brightness by reducing the duty cycle from 25,000 to 0.
```rust
// Here rev is to reverse the iteration. so it goes from 25_000 to 0
for i in (LOW..=HIGH).rev() {
delay.delay_us(8);
let _ = channel.set_duty_cycle(i);
}
```

### Pause
After fading up and down, the program pauses for 500 milliseconds before repeating the cycle, allowing the LED to rest briefly.

Play around by adjusting the `delay` and observe. You can even comment out one of the for loop and observe the effect.

103 changes: 36 additions & 67 deletions src/blinky/pwm.md
Original file line number Diff line number Diff line change
@@ -1,83 +1,52 @@

## What is PWM?
PWM stands for **Pulse Width Modulation**. It is a technique used to control the amount of power delivered to a device by adjusting the width of the pulses in a signal.

In PWM, a digital signal switches between **on** and **off** states. The **duty cycle** of the signal determines how long it stays on compared to how long it stays off.

- **Duty Cycle**:
The percentage of time the signal is on during one cycle.
<img style="display: block; margin: auto;" alt="pico2" src="../images/pwm-duty-cycle.png" />
<span style="text-align: center;display: block; margin: auto; font-size: 12px;">Image Credit: Wikipedia</span>
- For example:
- 100% duty cycle means the signal is always on.
- 50% duty cycle means the signal is on half the time and off half the time.
- 0% duty cycle means the signal is always off.

Refer the 1073th page of the [RP2350](https://datasheets.raspberrypi.com/rp2350/rp2350-datasheet.pdf) Datasheet for more information.

## PWM Peripheral in RP2350
The PWM peripheral is responsible for generating PWM signals. The Pico 2 features 12 PWM generators, known as slices, with each slice having two channels(A/B). This configuration results in a total of 24 PWM output channels available for use.
# PWM

**Mapping of PWM channels to GPIO Pins**:
In this section, what is PWM and why we need it.

You can find the table on page 1073 of the [RP2350](https://datasheets.raspberrypi.com/rp2350/rp2350-datasheet.pdf) Datasheet for more information. You have to refer this table when using a specific GPIO, as each GPIO corresponds to a particular slice. For instance, using GP25 (for the LED) means you are working with output 4B, which is the B output of the fourth slice.
## Digital vs Analog
In a digital circuit, signals are either high (such as 5V or 3.3V) or low (0V), with no in-between values. These two distinct states make digital signals ideal for computers and digital devices, as they’re easy to store, read, and transmit without losing accuracy.

<img style="display: block; margin: auto;" alt="pico2" src="../images/gpio-map-pwm-channels.png"/>
Analog signals, however, can vary continuously within a range, allowing for any value between a High and Low voltage. This smooth variation is valuable for applications requiring fine control, such as adjusting audio volume or light brightness.

Initialize the PWM slices by creating an instance using the PAC's PWM peripheral and reset control.
```rust
let mut pwm_slices = hal::pwm::Slices::new(pac.PWM, &mut pac.RESETS);
```
Devices like servo motors and LEDs(for dimming effect) often need gradual, precise control over voltage, which analog signals provide through their continuous range.

Retrieve a mutable reference to PWM4 from the initialized PWM slices for further configuration.
```rust
let pwm = &mut pwm_slices.pwm4;
```
Microcontrollers use PWM to bridge this gap.

Configure PWM4 to operate in phase-correct mode for smoother output transitions. (You can refer the [secrets arudion PWM](https://docs.arduino.cc/tutorials/generic/secrets-of-arduino-pwm/) if you want to know what is phase-correct)
```rust
pwm.set_ph_correct();
```

Get a mutable reference to channel B of PWM4 and direct its output to GPIO pin 25.
```rust
let channel = &mut pwm.channel_b;
channel.output_to(pins.gpio25);
```

## Fading Effect

For LED brightness, PWM works by rapidly turning the LED on and off. If this happens fast enough, our eyes perceive a steady light, and the brightness increases with a higher duty cycle.
## What is PWM?
PWM stands for **Pulse Width Modulation**, creates an analog-like signal by rapidly pulsing a digital signal on and off. The average output voltage, controlled by adjusting the pulse's high duration or "duty cycle," can simulate a continuous analog level.

<img style="display: block; margin: auto;" alt="pico2" src="./assets/led-pwm.jpg" />


In the previous example code, we use PWM to fade an LED.
The **duty cycle** of the signal determines how long it stays on compared to how long it stays off.

### Fading Up
The code below gradually increases the LED brightness by adjusting the duty cycle from 0 to 25,000, with a small delay between each step:
```rust
for i in LOW..=HIGH {
delay.delay_us(8);
let _ = channel.set_duty_cycle(i);
}
```
The delay ensures the LED brightens gradually. Without it, the brightness would change too quickly for the eye to notice, making the LED appear to jump from dim to bright. The delay allows for a smooth, noticeable "fading up" effect.
- **Duty Cycle**:
The percentage of time the signal is on during one cycle.
- For example:
- 100% duty cycle means the signal is always on.
- 50% duty cycle means the signal is on half the time and off half the time.
- 0% duty cycle means the signal is always off.
<img style="display: block; margin: auto;" alt="pico2" src="../images/pwm-duty-cycle.png" />
<span style="text-align: center;display: block; margin: auto; font-size: 12px;">Image Credit: Wikipedia</span>

Dont' believe me! Adjust the delay to 0 and observe. You can increase the delay (eg: 25) and observe the fading effect.
## Period and Frequency
Period is the total time for one on-off cycle to complete.

Note: `set_duty_cycle` function under the hood writes the given value into CC register(Count compare value).
The frequency of a PWM signal is the number of cycles it completes in one second, measured in Hertz (Hz). Frequency is the inverse of the period:

### Fading Down
The following code decreases the LED brightness by reducing the duty cycle from 25,000 to 0.
```rust
// Here rev is to reverse the iteration. so it goes from 25_000 to 0
for i in (LOW..=HIGH).rev() {
delay.delay_us(8);
let _ = channel.set_duty_cycle(i);
}
```
<img alt="pico2" src="./assets/frequency-period.png"/>
<br/>
So if the period is 1 second, then the frequency will be 1HZ.
<br/>
<img alt="pico2" src="./assets/frequency-1hz.png"/>
<br/>
For example, if the period is 20ms(0.02s), the frequency will be 50Hz.
<img alt="pico2" src="./assets/frequency-50hz.png"/>
<br/>

### Pause
After fading up and down, the program pauses for 500 milliseconds before repeating the cycle, allowing the LED to rest briefly.
**Calculating Cycle count from Frequency per second**

Play around by adjusting the `delay` and observe. You can even comment out one of the for loop and observe the effect.
The Formula to calculate cycle count:
`Cycle Count=Frequency (Hz)×Total Time (seconds)`

If a PWM signal has a frequency of 50Hz, it means it completes 50 cycles in one second.

0 comments on commit b652185

Please sign in to comment.