Skip to content

Commit

Permalink
generic: usart: Add Baudrate type for flexible baud selection
Browse files Browse the repository at this point in the history
Currently, the USART/Serial driver has a hardcoded algorithm for
selecting the UBRR value that best fits a certain baudrate.  This,
however, has a number of problems:

 - Downstream code has no possibility to specify exact values if needed.
 - Board-specific workarounds cannot be transparently applied.

To resolve these issues, add a new `Baudrate` type which represents
a baudrate selection.  It can easily be created from an integer using

    115200.into_baudrate()

(provided by `BaudrateExt`).  But it also allows custom selection of
exact values using the `Baudrate::with_exact()` constructor.  For
board-specific workarounds, the `.into_baudrate()` method is provided by
a different extension trait (e.g.  `BaudrateArduinoExt`).

Ref: #80
  • Loading branch information
explicite authored Nov 7, 2020
1 parent 9f9d697 commit 4c0094a
Show file tree
Hide file tree
Showing 34 changed files with 159 additions and 41 deletions.
111 changes: 104 additions & 7 deletions avr-hal-generic/src/serial.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,105 @@
//! Serial Implementations
use core::cmp::Ordering;

// Clock is needed because the calculations needs to take core clock into account
#[derive(Debug, Clone, Copy)]
pub struct Baudrate<CLOCK> {
pub ubrr: u16,
pub u2x: bool,
pub _clock: ::core::marker::PhantomData<CLOCK>,
}

impl<CLOCK: crate::clock::Clock> PartialEq for Baudrate<CLOCK> {
fn eq(&self, other: &Self) -> bool {
self.compare_value() == other.compare_value()
}
}

impl<CLOCK: crate::clock::Clock> Eq for Baudrate<CLOCK> {}

impl<CLOCK: crate::clock::Clock> PartialOrd for Baudrate<CLOCK> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.compare_value().cmp(&other.compare_value()))
}
}

impl<CLOCK: crate::clock::Clock> Ord for Baudrate<CLOCK> {
fn cmp(&self, other: &Self) -> Ordering {
other.compare_value().cmp(&self.compare_value())
}
}

impl<CLOCK: crate::clock::Clock> From<u32> for Baudrate<CLOCK> {
fn from(baud: u32) -> Self {
Baudrate::new(baud)
}
}

impl<CLOCK: crate::clock::Clock> Baudrate<CLOCK> {
pub fn new(baud: u32) -> Baudrate<CLOCK> {
let mut ubrr = (CLOCK::FREQ / 4 / baud - 1) / 2;
let mut u2x = true;
debug_assert!(ubrr <= u16::MAX as u32);
if ubrr > 4095 {
u2x = false;
ubrr = (CLOCK::FREQ / 8 / baud - 1) / 2;
}

Baudrate {
ubrr: ubrr as u16,
u2x: u2x,
_clock: ::core::marker::PhantomData,
}
}

pub fn with_exact(u2x: bool, ubrr: u16) -> Baudrate<CLOCK> {
Baudrate {
ubrr, u2x, _clock: ::core::marker::PhantomData,
}
}

fn compare_value(&self) -> u32 {
if self.u2x {
return 8 * (self.ubrr as u32 + 1);
} else {
return 16 * (self.ubrr as u32 + 1);
};
}
}

pub trait BaudrateExt {
fn into_baudrate<CLOCK: crate::clock::Clock>(self) -> Baudrate<CLOCK>;
}

impl BaudrateExt for u32 {
fn into_baudrate<CLOCK: crate::clock::Clock>(self) -> Baudrate<CLOCK> {
Baudrate::new(self)
}
}

pub trait BaudrateArduinoExt {
fn into_baudrate<CLOCK: crate::clock::Clock>(self) -> Baudrate<CLOCK>;
}

impl BaudrateArduinoExt for u32 {
fn into_baudrate<CLOCK: crate::clock::Clock>(self) -> Baudrate<CLOCK> {
let br = Baudrate::new(self);

// hardcoded exception for 57600 for compatibility with the bootloader
// shipped with the Duemilanove and previous boards and the firmware
// on the 8U2 on the Uno and Mega 2560.
//
// https://github.com/arduino/ArduinoCore-avr/blob/3055c1efa3c6980c864f661e6c8cc5d5ac773af4/cores/arduino/HardwareSerial.cpp#L123-L132
if CLOCK::FREQ == 16_000_000 && br.ubrr == 34 && br.u2x {
// (CLOCK::FREQ / 8 / 57600 - 1) / 2 == 16
Baudrate::with_exact(false, 16)
} else {
br
}
}
}

/// Implement serial traits for a USART peripheral
#[macro_export]
macro_rules! impl_usart {
Expand Down Expand Up @@ -40,7 +140,7 @@ macro_rules! impl_usart {
p: $USART,
rx: $rxmod::$RX<$crate::port::mode::Input<RX_MODE>>,
tx: $txmod::$TX<$crate::port::mode::Output>,
baud: u32,
baud: Baudrate<CLOCK>,
) -> $Usart<CLOCK, RX_MODE> {
let mut usart = $Usart {
p,
Expand All @@ -52,12 +152,9 @@ macro_rules! impl_usart {
usart
}

fn initialize(&mut self, baud: u32) {
// Value for baudrate register must be calculated based on clock frequency.
let brr = CLOCK::FREQ / (16 * baud) - 1;
self.p.[<ubrr $n>].write(|w| unsafe { w.bits(brr as u16) });

self.p.[<ucsr $n a>].reset();
fn initialize(&mut self, baud: Baudrate<CLOCK>) {
self.p.[<ubrr $n>].write(|w| unsafe { w.bits(baud.ubrr) });
self.p.[<ucsr $n a>].write(|w| w.[<u2x $n>]().bit(baud.u2x));

// Enable receiver and transmitter but leave interrupts disabled.
self.p.[<ucsr $n b>].write(|w| w
Expand Down
2 changes: 1 addition & 1 deletion boards/arduino-diecimila/examples/diecimila-panic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ fn main() -> ! {
dp.USART0,
pins.d0,
pins.d1.into_output(&mut pins.ddr),
57600,
57600.into_baudrate(),
);

ufmt::uwriteln!(&mut serial, "Hello from Arduino!\r").void_unwrap();
Expand Down
5 changes: 4 additions & 1 deletion boards/arduino-diecimila/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ mod pins;
pub use crate::pac::Peripherals;
pub use crate::pins::*;
pub use crate::hal::adc;
pub use crate::hal::prelude;
pub mod prelude {
pub use crate::hal::prelude::*;
pub use crate::hal::usart::BaudrateArduinoExt as _;
}
pub use crate::hal::pwm;
pub use crate::hal::spi;

Expand Down
2 changes: 1 addition & 1 deletion boards/arduino-leonardo/examples/leonardo-adc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ fn main() -> ! {
dp.USART1,
pins.d0,
pins.d1.into_output(&mut pins.ddr),
57600,
57600.into_baudrate(),
);

ufmt::uwriteln!(&mut serial, "Reading analog inputs ...\r").void_unwrap();
Expand Down
2 changes: 1 addition & 1 deletion boards/arduino-leonardo/examples/leonardo-i2cdetect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ fn main() -> ! {
dp.USART1,
pins.d0,
pins.d1.into_output(&mut pins.ddr),
57600,
57600.into_baudrate(),
);
let mut i2c = arduino_leonardo::I2c::new(
dp.TWI,
Expand Down
2 changes: 1 addition & 1 deletion boards/arduino-leonardo/examples/leonardo-panic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ fn main() -> ! {
dp.USART1,
pins.d0,
pins.d1.into_output(&mut pins.ddr),
57600,
57600.into_baudrate(),
);

ufmt::uwriteln!(&mut serial, "Hello from Arduino!\r").void_unwrap();
Expand Down
2 changes: 1 addition & 1 deletion boards/arduino-leonardo/examples/leonardo-serial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ fn main() -> ! {
dp.USART1,
pins.d0,
pins.d1.into_output(&mut pins.ddr),
57600,
57600.into_baudrate(),
);

ufmt::uwriteln!(&mut serial, "Hello from Arduino!\r").void_unwrap();
Expand Down
2 changes: 1 addition & 1 deletion boards/arduino-leonardo/examples/leonardo-spi-feedback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ fn main() -> ! {
dp.USART1,
pins.d0,
pins.d1.into_output(&mut pins.ddr),
57600,
57600.into_baudrate(),
);

pins.led_rx.into_output(&mut pins.ddr); // SS must be set to output mode.
Expand Down
8 changes: 6 additions & 2 deletions boards/arduino-leonardo/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,11 @@ pub use crate::pac::Peripherals;
mod pins;
pub use crate::pins::*;

pub use crate::hal::prelude;
pub mod prelude {
pub use crate::hal::prelude::*;
pub use crate::hal::usart::BaudrateExt as _;
}
pub use crate::hal::usart;

/// Busy-Delay
///
Expand Down Expand Up @@ -187,7 +191,7 @@ pub mod pwm {
/// dp.USART1,
/// pins.d0,
/// pins.d1.into_output(&mut pins.ddr),
/// 57600,
/// 57600.into_baudrate(),
/// );
///
/// ufmt::uwriteln!(&mut serial, "Hello from Arduino!\r").void_unwrap();
Expand Down
2 changes: 1 addition & 1 deletion boards/arduino-mega2560/examples/mega2560-adc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ fn main() -> ! {
dp.USART0,
pins.d0,
pins.d1.into_output(&mut pins.ddr),
57600,
57600.into_baudrate(),
);

ufmt::uwriteln!(&mut serial, "Reading analog inputs ...\r").void_unwrap();
Expand Down
2 changes: 1 addition & 1 deletion boards/arduino-mega2560/examples/mega2560-i2cdetect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ fn main() -> ! {
dp.USART0,
porte.pe0,
porte.pe1.into_output(&mut porte.ddr),
57600,
57600.into_baudrate(),
);
let mut i2c = arduino_mega2560::I2c::new(
dp.TWI,
Expand Down
2 changes: 1 addition & 1 deletion boards/arduino-mega2560/examples/mega2560-panic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ fn main() -> ! {
dp.USART0,
porte.pe0,
porte.pe1.into_output(&mut porte.ddr),
57600,
57600.into_baudrate(),
);

ufmt::uwriteln!(&mut serial, "Hello from MEGA2560!\r").void_unwrap();
Expand Down
2 changes: 1 addition & 1 deletion boards/arduino-mega2560/examples/mega2560-serial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ fn main() -> ! {
dp.USART0,
porte.pe0,
porte.pe1.into_output(&mut porte.ddr),
57600,
57600.into_baudrate(),
);

ufmt::uwriteln!(&mut serial, "Hello from Arduino!\r").void_unwrap();
Expand Down
2 changes: 1 addition & 1 deletion boards/arduino-mega2560/examples/mega2560-spi-feedback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ fn main() -> ! {
dp.USART0,
pins.d0,
pins.d1.into_output(&mut pins.ddr),
57600,
57600.into_baudrate(),
);

pins.d53.into_output(&mut pins.ddr); // SS must be set to output mode.
Expand Down
5 changes: 4 additions & 1 deletion boards/arduino-mega2560/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ pub use crate::pac::Peripherals;
mod pins;
pub use crate::pins::*;

pub use crate::hal::prelude;
pub mod prelude {
pub use crate::hal::prelude::*;
pub use crate::hal::usart::BaudrateExt as _;
}

pub use crate::hal::spi;
pub use crate::hal::adc;
Expand Down
2 changes: 1 addition & 1 deletion boards/arduino-uno/examples/uno-adc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ fn main() -> ! {
let mut pins = arduino_uno::Pins::new(dp.PORTB, dp.PORTC, dp.PORTD);

let mut serial =
arduino_uno::Serial::new(dp.USART0, pins.d0, pins.d1.into_output(&mut pins.ddr), 9600);
arduino_uno::Serial::new(dp.USART0, pins.d0, pins.d1.into_output(&mut pins.ddr), 9600.into_baudrate());

let mut adc = adc::Adc::new(dp.ADC, Default::default());

Expand Down
2 changes: 1 addition & 1 deletion boards/arduino-uno/examples/uno-hc-sr04.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ fn main() -> ! {
dp.USART0,
pins.d0,
pins.d1.into_output(&mut pins.ddr),
57600,
57600.into_baudrate(),
);

let timer1 = dp.TC1;
Expand Down
2 changes: 1 addition & 1 deletion boards/arduino-uno/examples/uno-i2cdetect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ fn main() -> ! {
dp.USART0,
pins.d0,
pins.d1.into_output(&mut pins.ddr),
57600,
57600.into_baudrate(),
);
let mut i2c = arduino_uno::I2c::new(
dp.TWI,
Expand Down
2 changes: 1 addition & 1 deletion boards/arduino-uno/examples/uno-panic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ fn main() -> ! {
dp.USART0,
pins.d0,
pins.d1.into_output(&mut pins.ddr),
57600,
57600.into_baudrate(),
);

ufmt::uwriteln!(&mut serial, "Hello from Arduino!\r").void_unwrap();
Expand Down
2 changes: 1 addition & 1 deletion boards/arduino-uno/examples/uno-serial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ fn main() -> ! {
dp.USART0,
pins.d0,
pins.d1.into_output(&mut pins.ddr),
57600,
57600.into_baudrate(),
);

ufmt::uwriteln!(&mut serial, "Hello from Arduino!\r").void_unwrap();
Expand Down
2 changes: 1 addition & 1 deletion boards/arduino-uno/examples/uno-spi-feedback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ fn main() -> ! {
dp.USART0,
pins.d0,
pins.d1.into_output(&mut pins.ddr),
57600,
57600.into_baudrate(),
);

pins.d10.into_output(&mut pins.ddr); // SS must be set to output mode.
Expand Down
7 changes: 5 additions & 2 deletions boards/arduino-uno/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@ pub use crate::pac::Peripherals;
mod pins;
pub use crate::pins::*;

pub use crate::hal::prelude;
pub mod prelude {
pub use crate::hal::prelude::*;
pub use crate::hal::usart::BaudrateArduinoExt as _;
}

/// Busy-Delay
///
Expand Down Expand Up @@ -192,7 +195,7 @@ pub mod pwm {
/// dp.USART0,
/// pins.d0,
/// pins.d1.into_output(&mut pins.ddr),
/// 57600,
/// 57600.into_baudrate(),
/// );
///
/// ufmt::uwriteln!(&mut serial, "Hello from Arduino!\r").void_unwrap();
Expand Down
2 changes: 1 addition & 1 deletion boards/bigavr6/examples/bigavr6-i2cdetect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ fn main() -> ! {
dp.USART0,
porte.pe0,
porte.pe1.into_output(&mut porte.ddr),
57600,
57600.into_baudrate(),
);
let mut i2c = bigavr6::I2c::new(
dp.TWI,
Expand Down
2 changes: 1 addition & 1 deletion boards/bigavr6/examples/bigavr6-panic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ fn main() -> ! {
dp.USART0,
porte.pe0,
porte.pe1.into_output(&mut porte.ddr),
57600,
57600.into_baudrate(),
);

ufmt::uwriteln!(&mut serial, "Hello from BIGAVR6!\r").void_unwrap();
Expand Down
2 changes: 1 addition & 1 deletion boards/bigavr6/examples/bigavr6-serial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ fn main() -> ! {
dp.USART0,
porte.pe0,
porte.pe1.into_output(&mut porte.ddr),
57600,
57600.into_baudrate(),
);

// The following would also work, but needs +600% more bytes
Expand Down
5 changes: 4 additions & 1 deletion boards/bigavr6/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ pub use crate::hal::pac;
pub use crate::hal::entry;

pub use crate::pac::Peripherals;
pub use crate::hal::prelude;
pub mod prelude {
pub use crate::hal::prelude::*;
pub use crate::hal::usart::BaudrateExt as _;
}

pub type Delay = crate::hal::delay::Delay<hal::clock::MHz16>;
pub type Serial<IMODE> = crate::hal::usart::Usart0<hal::clock::MHz16, IMODE>;
Expand Down
2 changes: 1 addition & 1 deletion boards/sparkfun-pro-micro/examples/pro-micro-adc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ fn main() -> ! {
dp.USART1,
pins.d0,
pins.d1.into_output(&mut pins.ddr),
57600,
57600.into_baudrate(),
);

ufmt::uwriteln!(&mut serial, "Reading analog inputs ...\r").void_unwrap();
Expand Down
Loading

0 comments on commit 4c0094a

Please sign in to comment.