Hi community!
I've been working on a Microbit v2 project that is a metronome - device that emits sounds periodically. Sound is emitted using PWM and it can be directed to build-in speaker pin (P0_01) or a free GPIO pin (example: P0_00) that I use with a jack plug.
I tried to implement a dynamic switching of the output with the button and encountered some problems related to the typestate design of the HAL lib and ownership. Here's simplified version of the code:
// Speaker is protected with mutex, because its frequency is changed in the interrupts
static SPEAKER: Mutex<RefCell<Option<pwm::Pwm<pac::PWM0>>>> = Mutex::new(RefCell::new(None));
#[entry]
fn main() -> ! {
if let Some(mut board) = Board::take() {
let button_a = board.buttons.button_a.into_pullup_input();
let button_b = board.buttons.button_b.into_pullup_input();
let mut jack_pin = board.pins.p0_02.into_push_pull_output(gpio::Level::Low);
let mut speaker_pin = board.speaker_pin.into_push_pull_output(gpio::Level::Low);
let mut out_pin: Option<Pin<Output<PushPull>>> = Some(jack_pin.degrade());
let speaker = pwm::Pwm::new(board.PWM0);
speaker
.set_output_pin(pwm::Channel::C0, speaker_pin.degrade())
.set_prescaler(pwm::Prescaler::Div16)
.set_period(Hertz(440u32))
.set_counter_mode(pwm::CounterMode::UpAndDown)
.set_max_duty(32767)
.enable();
speaker
.set_seq_refresh(pwm::Seq::Seq0, 0)
.set_seq_end_delay(pwm::Seq::Seq0, 0);
cortex_m::interrupt::free(move |cs| {
*SPEAKER.borrow(cs).borrow_mut() = Some(speaker);
unsafe {
pac::NVIC::unmask(pac::Interrupt::RTC0);
}
pac::NVIC::unpend(pac::Interrupt::RTC0);
});
loop {
// When both buttons are pressed, switch sound output
if let (Ok(true), Ok(true)) = (button_a.is_low(), button_b.is_low()) {
cortex_m::interrupt::free(move |cs| {
if let Some(speaker) = SPEAKER.borrow(cs).borrow_mut().as_ref() {
if let Some(new_pin) = out_pin {
if let Some(old_pin) = speaker.swap_output_pin(pwm::Channel::C0, new_pin.into()) {
out_pin = Some(old_pin)
}
}
}
});
}
// Rest of the code handling metronome functions
}
}
}
This code has multiple issues, but the one I'm unable to solve is the move of the pin value.
error[E0382]: use of moved value: `out_pin`
--> src/main.rs:102:43
|
72 | let mut out_pin: Option<Pin<Output<PushPull>>> = Some(jack_pin.degrade());
| ----------- move occurs because `out_pin` has type `Option<microbit::nrf52833_hal::gpio::Pin<Output<PushPull>>>`, which does not implement the `Copy` trait
...
102 | cortex_m::interrupt::free(move |cs| {
| ^^^^^^^^^
| |
| value moved into closure here, in previous iteration of loop
| value used here after move
103 | if let Some(speaker) = SPEAKER.borrow(cs).borrow_mut().as_ref() {
104 | if let Some(new_pin) = out_pin {
| ------- use occurs due to use in closure
I'm aware that the pin is moved into the closure, but I don't have better idea how swap the pin in the speaker. The speaker must be in interrupt-free block closure, because it can be accessed any time, due to interrupt. I also tried wrapping the pin in a mutex, but this doesn't help much.
What's the correct way of dealing with dynamic switch of output pin in PWM? Is there any API that could help me?
Full code is in GitHub - sireliah/metronome: Pet metronome project designed to work on nRF52 chip for the reference.