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

More fine grained control of serial baudrate #80

Closed
Rahix opened this issue Oct 4, 2020 · 2 comments
Closed

More fine grained control of serial baudrate #80

Rahix opened this issue Oct 4, 2020 · 2 comments
Assignees
Labels
hal-api API design for the different components of avr-hal help wanted Extra attention is needed

Comments

@Rahix
Copy link
Owner

Rahix commented Oct 4, 2020

Currently, the code only allows specifying a baudrate which the HAL-driver will try to match as closely as possible. The "generic" algorithm used for this will not always produce the most optimal settings, however. This means, for example, that 115200 baud on Arduino Uno is completely broken.

It would be nice to also allow setting exact baudrate parameters or/and providing alternate algorithms. There are quite a few ways to implement this, here are a few ideas:

  1. A new Baudrate type with methods for the different modes:

    // Clock is needed because the calculations needs to take core clock into account
    pub struct Baudrate<CLOCK> {
        pub ubrr: u16,
        pub u2x: bool,
        pub _clock: PhantomData<CLOCK>,
    }
    
    impl<CLOCK: crate::clock::Clock> Baudrate<CLOCK> {
        pub fn new(baud: u32) -> Baudrate<CLOCK> {
            // current implementation
        }
    
        pub fn with_exact(u2x: bool, ubrr: u16) -> Baudrate<CLOCK> {
            Baudrate { ubrr, u2x, _clock: PhantomData }
        }
    }

    The Usart constructor now takes Baudrate<CLOCK> instead of u32.

  2. Using an Into<Baudrate<CLOCK>> bound in the Usart constructor. We also implement From<u32> for Baudrate<CLOCK>. This would allow backwards compatibility with current code (but I'm not sure if that is a good thing ...).

@Rahix Rahix added help wanted Extra attention is needed hal-api API design for the different components of avr-hal labels Oct 4, 2020
@explicite
Copy link
Contributor

I will try to start this. Probably I will have lot of questions...

  1. This is the generic algorithm mentioned by you let brr = CLOCK::FREQ / (16 * baud) - 1;?
  2. Baundrate should be defined as distinct file under generic src like Clock?

@Rahix
Copy link
Owner Author

Rahix commented Oct 11, 2020

This is the generic algorithm mentioned by you let brr = CLOCK::FREQ / (16 * baud) - 1;?

Yes, it's this code:

// 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) });

In comparison, Arduino upstream uses this code:

  // Try u2x mode first
  uint16_t baud_setting = (F_CPU / 4 / baud - 1) / 2;
  *_ucsra = 1 << U2X0;

  // 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. Also, The baud_setting cannot
  // be > 4095, so switch back to non-u2x mode if the baud rate is too
  // low.
  if (((F_CPU == 16000000UL) && (baud == 57600)) || (baud_setting >4095))
  {
    *_ucsra = 0;
    baud_setting = (F_CPU / 8 / baud - 1) / 2;
  }

  // assign the baud_setting, a.k.a. ubrr (USART Baud Rate Register)
  *_ubrrh = baud_setting >> 8;
  *_ubrrl = baud_setting;

Baundrate should be defined as distinct file under generic src like Clock?

No, I'd prefer putting that in avr-hal-generic/src/serial.rs as it is very closely tied to it.

Probably I will have lot of questions...

Sure, please don't hesitate asking if anything is unclear!

Rahix pushed a commit that referenced this issue Nov 7, 2020
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
@Rahix Rahix closed this as completed Nov 7, 2020
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 help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

2 participants