Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Equivalent of analogWrite() in avr-hal #194

Closed
luukvankooten opened this issue Jul 14, 2021 · 10 comments
Closed

Equivalent of analogWrite() in avr-hal #194

luukvankooten opened this issue Jul 14, 2021 · 10 comments
Labels
hal-api API design for the different components of avr-hal question Further information is requested

Comments

@luukvankooten
Copy link

With the Arduino HAL, it is possible to write values between 0 and 255 with the function analogWrite(). How to do this with this library?

@Rahix
Copy link
Owner

Rahix commented Jul 15, 2021

Hi,

first of all, it is important to note that analogWrite() does not actually produce an analog signal, but instead uses PWM under the hood. PWM signals are generated by the MCUs timer peripherals and then "routed" to one of the supported pins.

Now, we had support for this at some point, but as we recently reworked pretty much all of avr-hal (see #130), this is unfortunately still missing from the new version. This means that you'll have to set up the timer for PWM manually.

As an example, here is code to output a PWM signal on pin D9 of an Arduino Uno. The frequency of the signal is 1 kHz and we're varying the duty-cycle from 0% to 100%. (Disclaimer: I can't test this code right now, but I'm pretty sure this is correct)

#![no_std]
#![no_main]

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

#[arduino_hal::entry]
fn main() -> ! {
    let dp = arduino_uno::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();

    // This is important because otherwise the signal will not actually be
    // visible on the pin.
    pins.d9.into_output();

    let tc1 = dp.TC1;
    tc1.tccr1a.write(|w| w.wgm1().bits(0b01).com1a().match_clear());
    tc1.tccr1b.write(|w| w.wgm1().bits(0b01).cs1().prescale_64());

    loop {
        for duty in 0u8..=255u8 {
            ufmt::uwriteln!(&mut serial, "Duty: {}", duty).void_unwrap();
            tc1.ocr1a.write(|w| unsafe { w.bits(duty as u16) });
            arduino_hal::delay_ms(20);
        }
    }
}

You can of course do the same thing for all the other PWM-capable pins, but the timer and registers to use will be different. The datasheet for the MCU contains all this information, but if you're unsure you can of course always ask me as well.

@Rahix Rahix added hal-api API design for the different components of avr-hal question Further information is requested labels Jul 15, 2021
@Rahix

This comment has been minimized.

@Rahix Rahix changed the title Question: is postible to write a spefic value to analog port? Equivalent of analogWrite() in avr-hal Jul 15, 2021
@stappersg

This comment has been minimized.

@Rahix

This comment has been minimized.

@stappersg

This comment has been minimized.

@luukvankooten
Copy link
Author

After long debugging hours (I'm new to this with rust, data sheets and microcontrollers). The line containing pins.d9.into_output(); must be under the lines tc1.tccr1a and tc1.tccr1b. I think it has something to do with registration, not sure.

@MarkuBu
Copy link

MarkuBu commented Jul 18, 2021

analogWrite and similar functions are higher abstraction functions. In my opinion it doesn't make sense to add them in a HAL for a controller.

If you have a controller that has an actual analog output like a DAC you need different functions.

These kind of functions only make sense in a higher abstraction layer that is independent of the actual hardware

@Rahix
Copy link
Owner

Rahix commented Jul 18, 2021

@MarkuBu, while I agree that an analogWrite() function makes no sense here, I do think that we need to add an abstraction for the PWM outputs which can serve as a functionally equivalent replacement. Just like the one we had in old avr-hal, see https://github.com/Rahix/avr-hal/blob/ce52c90d3950cd81090627e3ad8494a15e4c8f66/boards/arduino-uno/examples/uno-pwm.rs.

@miam-miam
Copy link

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. 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
Copy link
Owner

Rahix commented Dec 3, 2021

@miam-miam100, please open a new issue for this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
hal-api API design for the different components of avr-hal question Further information is requested
Projects
None yet
Development

No branches or pull requests

5 participants