Skip to content

Commit ee524b5

Browse files
committed
PWM
1 parent 54e5e39 commit ee524b5

File tree

3 files changed

+134
-0
lines changed

3 files changed

+134
-0
lines changed

src/SUMMARY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@
1010
- [Interrupts](./interrupt_intro.md)
1111
- [GPIO Interrupt](./gpio_interrupt.md)
1212
- [Exercise: toggle LED with interrupt](./led_with_interrupt.md)
13+
- [PWM](./pwm.md)
14+
- [Exercise: LED breathing effect](./pwm_led_breathing.md)

src/pwm.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# PWM
2+
3+
In this chapter, we will learn how to digitally control the brightness of the LED using [Pulse-width modulation][1].
4+
We will continue using the same `hello_world` project from the previous workshops.
5+
6+
`esp-hal` crate provides a PWM driver in [`mcpwm`][2] module. Let's start by importing the necessary modules and types:
7+
8+
```rust
9+
use esp_hal::mcpwm::operator::PwmPinConfig;
10+
use esp_hal::mcpwm::timer::PwmWorkingMode;
11+
use esp_hal::mcpwm::{McPwm, PeripheralClockConfig};
12+
use esp_hal::time::Rate;
13+
```
14+
15+
Next, let set up the PWM peripheral:
16+
17+
```rust
18+
let clock_cfg = PeripheralClockConfig::with_frequency(Rate::from_mhz(40)).unwrap();
19+
let mut mcpwm = McPwm::new(peripherals.MCPWM0, clock_cfg);
20+
let timer_clock_cfg = clock_cfg
21+
.timer_clock_with_frequency(255, PwmWorkingMode::Increase, Rate::from_khz(20))
22+
.expect("could not determine parameters for the requested frequency");
23+
24+
mcpwm.operator0.set_timer(&mcpwm.timer0);
25+
mcpwm.timer0.start(timer_clock_cfg);
26+
```
27+
28+
> 💡 Note the use of `expect`. `expect` performs the same function as `unwrap`, but allows us to write a helpful
29+
> error message. In general, use of `unwrap` is discouraged outside of "example" code. Ideally, the errors should be
30+
> handled gracefully or at least, provide an explanation of what has happened.
31+
32+
We'll change the LED pin to GPIO16, so we can have active-high configuration:
33+
34+
```rust
35+
let led = Output::new(
36+
peripherals.GPIO16,
37+
esp_hal::gpio::Level::Low,
38+
OutputConfig::default(),
39+
);
40+
```
41+
42+
Then, we will transfer GPIO to the PWM instance:
43+
44+
```rust
45+
let mut led = mcpwm.operator0.with_pin_a(led, PwmPinConfig::UP_ACTIVE_HIGH);
46+
```
47+
48+
> 💡 Note how we're re-using the name `led`. This is called rebinding and allows us to write cleaner code.
49+
50+
Finally, let's modify our application to cycle through different brightness levels in a loop:
51+
52+
```rust
53+
let mut sequence = [32, 128, 255].iter().cycle();
54+
loop {
55+
led.set_timestamp(*sequence.next().unwrap());
56+
delay.delay_millis(1000);
57+
}
58+
```
59+
60+
[1]: https://en.wikipedia.org/wiki/Pulse-width_modulation
61+
[2]: https://docs.espressif.com/projects/rust/esp-hal/1.0.0-rc.0/esp32c6/esp_hal/mcpwm/index.html

src/pwm_led_breathing.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Exercise: LED breathing effect
2+
3+
We learnt how to control the LED brightness and make the brightness ramp-up.
4+
For this exercise, you have to implement a commonly-used breathing effect.
5+
6+
The sequence is roughly as follows:
7+
8+
1. LED brightness should increase gradually until it reaches the maximum.
9+
1. LED brightness should decrease gradually until it reaches zero.
10+
1. LED stays off for a couple of seconds.
11+
1. The sequence repeats.
12+
13+
<details>
14+
<summary><strong>Solution</strong></summary>
15+
16+
The requirements of this exercise a suited perfectly for a [state machine][1]
17+
and Rust's `enum`s work great for state machines.
18+
19+
Let's define our enum. We can achieve our goals by using two variants (states):
20+
one for increasing brightness and one for decreasing.
21+
The enum variants will hold the actual brightness value.
22+
23+
```rust
24+
enum LedState {
25+
Up(u16),
26+
Down(u16),
27+
}
28+
```
29+
30+
Let's create the instance of our `LedState`. Declare this variable just above the main loop:
31+
32+
```rust
33+
let mut led_state = LedState::Up(0);
34+
```
35+
36+
We will start at zero brightness and increasing direction.
37+
38+
Finally, let's implement our state transitions:
39+
40+
```rust
41+
loop {
42+
match led_state {
43+
// We have reached maximum, change state to ramp down.
44+
LedState::Up(255) => {
45+
led.set_timestamp(255);
46+
led_state = LedState::Down(255);
47+
}
48+
// Ramping up: increment the brightness.
49+
LedState::Up(brightness) => {
50+
led.set_timestamp(brightness);
51+
led_state = LedState::Up(brightness + 1);
52+
}
53+
// We have reached minimum, wait two seconds then change state to ramp up.
54+
LedState::Down(0) => {
55+
led.set_timestamp(0);
56+
delay.delay_millis(2000);
57+
led_state = LedState::Up(0);
58+
}
59+
// Ramping down: decrement the brightness.
60+
LedState::Down(brightness) => {
61+
led.set_timestamp(brightness);
62+
led_state = LedState::Down(brightness - 1);
63+
}
64+
}
65+
delay.delay_millis(10);
66+
}
67+
```
68+
69+
</details>
70+
71+
[1]: https://en.wikipedia.org/wiki/Finite-state_machine

0 commit comments

Comments
 (0)