Rahix / avr-hal

embedded-hal abstractions for AVR microcontrollers
Apache License 2.0
1.31k stars 221 forks source link

Equivalent of analogWrite() / PWM output on Arduino Uno pins 5 and 6 #230

Closed miam-miam closed 2 years ago

miam-miam commented 2 years ago

Hi I am trying to get the PWM working on an Arduino Uno on pins 5 and 6 to emulate the analogWrite() function but it doesn't work at all. I looked at #194 but the pins 5 and 6 of the Arduino Uno use a different frequency and timer. Would it be possible for someone to help me?

#![no_std]
#![no_main]

use arduino_hal::prelude::*;
use panic_halt as _;

#[arduino_hal::entry]
fn main() -> ! {
    let dp = arduino_hal::Peripherals::take().unwrap();
    let pins = arduino_hal::pins!(dp);
    let mut serial = arduino_hal::default_serial!(dp, pins, 57600);

    ufmt::uwriteln!(&mut serial, "Hello from Arduino!").void_unwrap();

    // Using timer 0 as pin 5+6 on Arduino Uno use it instead of timer 1.
    let tc0 = dp.TC0;
    tc0.tccr0a
        .write(|w| w.wgm0().bits(0b01).com0a().match_clear());
    tc0.tccr0b
        .write(|w| w.wgm02().bit(true).cs0().prescale_64());

    // Setting output after as this is what luukvankooten suggested
    pins.d5.into_output();
    pins.d6.into_output();

    loop {
        for duty in 0u8..=255u8 {
            ufmt::uwriteln!(&mut serial, "Duty: {}", duty).void_unwrap();
            tc0.ocr0a.write(|w| unsafe { w.bits(duty) });
            tc0.ocr0b.write(|w| unsafe { w.bits(duty) });
            arduino_hal::delay_ms(20);
        }
    }
}
Rahix commented 2 years ago

You're right, pins d5 and d6 on the uno (which are PD5 and PD6 of the ATmega328P) use TC0. There is some code on the old branch which had support for PWM ouput. For reference, here it is:

https://github.com/Rahix/avr-hal/blob/ce52c90d3950cd81090627e3ad8494a15e4c8f66/chips/atmega328p-hal/src/pwm.rs#L31-L76

This was tested, so it should not be too hard to derive what you need from it. Looking at your code, a difference I see is that you set the wgm differently. You'll also probably want to enable PWM output on both pins (in this case ocr0a/com0a is for d6 and ocr0b/com0b is for d5).

    let tc0 = dp.TC0;
    tc0.tccr0a
        .write(|w| w.wgm0().pwm_fast().com0a().match_clear().com0b().match_clear());
    tc0.tccr0b
        .write(|w| w.cs0().prescale_64());

I cannot run any tests of this right now, unfortunately. Let me know if it does not work.

miam-miam commented 2 years ago

Works perfectly thanks!