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

Add more configuration options to UART #63

Merged
merged 2 commits into from
May 28, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions examples/serial-dma-circ.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use cortex_m::{asm, singleton};
use stm32f1xx_hal::{
prelude::*,
pac,
serial::Serial,
serial::{Config, Serial},
dma::Half,
};
use cortex_m_rt::entry;
Expand Down Expand Up @@ -51,7 +51,7 @@ fn main() -> ! {
p.USART1,
(tx, rx),
&mut afio.mapr,
9_600.bps(),
Config::default().baudrate(9_600.bps()),
clocks,
&mut rcc.apb2,
);
Expand Down
4 changes: 2 additions & 2 deletions examples/serial-dma-peek.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use cortex_m::{asm, singleton};
use stm32f1xx_hal::{
prelude::*,
pac,
serial::Serial,
serial::{Config, Serial},
};
use cortex_m_rt::entry;

Expand Down Expand Up @@ -50,7 +50,7 @@ fn main() -> ! {
p.USART1,
(tx, rx),
&mut afio.mapr,
115_200.bps(),
Config::default(),
clocks,
&mut rcc.apb2,
);
Expand Down
4 changes: 2 additions & 2 deletions examples/serial-dma-rx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use cortex_m::{asm, singleton};
use stm32f1xx_hal::{
prelude::*,
pac,
serial::Serial,
serial::{Config, Serial},
};
use cortex_m_rt::entry;

Expand Down Expand Up @@ -50,7 +50,7 @@ fn main() -> ! {
p.USART1,
(tx, rx),
&mut afio.mapr,
9_600.bps(),
Config::default().baudrate(9_600.bps()),
clocks,
&mut rcc.apb2,
);
Expand Down
4 changes: 2 additions & 2 deletions examples/serial-dma-tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use cortex_m::asm;
use stm32f1xx_hal::{
prelude::*,
pac,
serial::Serial,
serial::{Config, Serial},
};
use cortex_m_rt::entry;

Expand Down Expand Up @@ -50,7 +50,7 @@ fn main() -> ! {
p.USART1,
(tx, rx),
&mut afio.mapr,
9_600.bps(),
Config::default().baudrate(9600.bps()),
clocks,
&mut rcc.apb2,
);
Expand Down
4 changes: 2 additions & 2 deletions examples/serial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use nb::block;
use stm32f1xx_hal::{
prelude::*,
pac,
serial::Serial,
serial::{Config, Serial},
};
use cortex_m_rt::entry;

Expand Down Expand Up @@ -63,7 +63,7 @@ fn main() -> ! {
p.USART3,
(tx, rx),
&mut afio.mapr,
9_600.bps(),
Config::default().baudrate(9600.bps()),
clocks,
&mut rcc.apb1,
);
Expand Down
89 changes: 89 additions & 0 deletions examples/serial_config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
//! Serial interface loopback test
//!
//! You have to short the TX and RX pins to make this program work

#![deny(unsafe_code)]
#![no_main]
#![no_std]

use panic_halt as _;

use cortex_m::asm;

use nb::block;

use stm32f1xx_hal::{
prelude::*,
pac,
serial::{self, Serial},
timer::Timer,
};
use cortex_m_rt::entry;

#[entry]
fn main() -> ! {
// Get access to the device specific peripherals from the peripheral access crate
let p = pac::Peripherals::take().unwrap();

// Take ownership over the raw flash and rcc devices and convert them into the corresponding
// HAL structs
let mut flash = p.FLASH.constrain();
let mut rcc = p.RCC.constrain();

// Freeze the configuration of all the clocks in the system and store the frozen frequencies in
// `clocks`
let clocks = rcc.cfgr.freeze(&mut flash.acr);

// Prepare the alternate function I/O registers
let mut afio = p.AFIO.constrain(&mut rcc.apb2);

// Prepare the GPIOB peripheral
let mut gpiob = p.GPIOB.split(&mut rcc.apb2);

// USART1
// let tx = gpioa.pa9.into_alternate_push_pull(&mut gpioa.crh);
// let rx = gpioa.pa10;

// USART1
// let tx = gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl);
// let rx = gpiob.pb7;

// USART2
// let tx = gpioa.pa2.into_alternate_push_pull(&mut gpioa.crl);
// let rx = gpioa.pa3;

// USART3
// Configure pb10 as a push_pull output, this will be the tx pin
let tx = gpiob.pb10.into_alternate_push_pull(&mut gpiob.crh);
// Take ownership over pb11
let rx = gpiob.pb11;

// Set up the usart device. Taks ownership over the USART register and tx/rx pins. The rest of
// the registers are used to enable and configure the device.
let serial = Serial::usart3(
p.USART3,
(tx, rx),
&mut afio.mapr,
serial::Config::default()
.baudrate(9600.bps())
.stopbits(serial::StopBits::STOP2)
.parity_odd(),
clocks,
&mut rcc.apb1,
);

// Split the serial struct into a receiving and a transmitting part
let (mut tx, mut rx) = serial.split();

let mut timer = Timer::tim2(p.TIM2, 1000.hz(), clocks, &mut rcc.apb1);

let sent = b'U';
block!(tx.write(sent)).ok();
// block!(timer.wait());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the purpose of these commented out lines?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also if we're not going to use the timer, why initialise it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, I'll remove the comments and timer

// timer.start(1000.hz());
// block!(timer.wait());
block!(tx.write(sent)).ok();


loop {}
}
98 changes: 94 additions & 4 deletions src/serial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ use crate::gpio::gpioa::{PA10, PA2, PA3, PA9};
use crate::gpio::gpiob::{PB10, PB11, PB6, PB7};
use crate::gpio::{Alternate, Floating, Input, PushPull};
use crate::rcc::{Clocks, APB1, APB2};
use crate::time::Bps;
use crate::time::{U32Ext, Bps};

/// Interrupt event
pub enum Event {
Expand Down Expand Up @@ -107,6 +107,67 @@ impl Pins<USART3> for (PB10<Alternate<PushPull>>, PB11<Input<Floating>>) {
// const REMAP: u8 = 0b11;
// }

pub enum Parity {
ParityNone,
ParityEven,
ParityOdd,
}

pub enum StopBits {
#[doc = "1 stop bit"]
STOP1,
#[doc = "0.5 stop bits"]
STOP0P5,
#[doc = "2 stop bits"]
STOP2,
#[doc = "1.5 stop bits"]
STOP1P5,
}

pub struct Config {
pub baudrate: Bps,
pub parity: Parity,
pub stopbits: StopBits,
}

impl Config {
pub fn baudrate(mut self, baudrate: Bps) -> Self {
self.baudrate = baudrate;
self
}

pub fn parity_none(mut self) -> Self {
self.parity = Parity::ParityNone;
self
}

pub fn parity_even(mut self) -> Self {
self.parity = Parity::ParityEven;
self
}

pub fn parity_odd(mut self) -> Self {
self.parity = Parity::ParityOdd;
self
}

pub fn stopbits(mut self, stopbits: StopBits) -> Self {
self.stopbits = stopbits;
self
}
}

impl Default for Config {
fn default() -> Config {
let baudrate = 19_200_u32.bps();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

19200 baud is kind of an odd choice, I haven't seen that in common use since the age of the acoustic coupler. Maybe either use 9600 or 115200 baud instead which are the two common default baud rates used nowadays (with a slight preference for the latter).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're absolutely right, I have no idea what made me chose 19200. 115200 makes sense to me.

Config {
baudrate,
parity: Parity::ParityNone,
stopbits: StopBits::STOP1,
}
}
}

/// Serial abstraction
pub struct Serial<USART, PINS> {
usart: USART,
Expand Down Expand Up @@ -162,7 +223,7 @@ macro_rules! hal {
usart: $USARTX,
pins: PINS,
mapr: &mut MAPR,
baud_rate: Bps,
config: Config,
clocks: Clocks,
apb: &mut $APB,
) -> Self
Expand All @@ -183,16 +244,45 @@ macro_rules! hal {
// enable DMA transfers
usart.cr3.write(|w| w.dmat().set_bit().dmar().set_bit());

let brr = clocks.$pclk().0 / baud_rate.0;
// Configure baud rate
let brr = clocks.$pclk().0 / config.baudrate.0;
assert!(brr >= 16, "impossible baud rate");
usart.brr.write(|w| unsafe { w.bits(brr) });

// Configure parity and word length
// Unlike most uart devices, the "word length" of this usart device refers to
// the size of the data plus the parity bit. I.e. "word length"=8, parity=even
// results in 7 bits of data. Therefore, in order to get 8 bits and one parity
// bit, we need to set the "word" length to 9 when using parity bits.
let (word_length, parity_control_enable, parity) = match config.parity {
Parity::ParityNone => (false, false, false),
Parity::ParityEven => (true, true, false),
Parity::ParityOdd => (true, true, true),
};
usart.cr1.modify(|_r, w| {
w
.m().bit(word_length)
.ps().bit(parity)
.pce().bit(parity_control_enable)
});

// Configure stop bits
let stop_bits = match config.stopbits {
StopBits::STOP1 => 0b00,
StopBits::STOP0P5 => 0b01,
StopBits::STOP2 => 0b10,
StopBits::STOP1P5 => 0b11,
};
usart.cr2.modify(|_r, w| {
w.stop().bits(stop_bits)
});

// UE: enable USART
// RE: enable receiver
// TE: enable transceiver
usart
.cr1
.write(|w| w.ue().set_bit().re().set_bit().te().set_bit());
.modify(|_r, w| w.ue().set_bit().re().set_bit().te().set_bit());

Serial { usart, pins }
}
Expand Down