Skip to content

Commit

Permalink
Add 9-bits word length mode for serial
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreySmirnov81 authored and burrbull committed Aug 11, 2021
1 parent 0c44231 commit 333d30e
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 7 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Use bit-banding for Peripheral enable/reset.
Don't require APBs in initializers.
- Rename `gpio::Edge::{RISING, FALLING, RISING_FALLING}` to `Rising`, `Falling`, `RisingFalling`, respectively
- Added the ability to specify the word size (8 or 9 bits) for `Serial` (USART). When using parity, the parity bit is included in the number of bits of the word.

### Added

- `blocking::serial::Write` for `Tx` and `Serial`. `core::fmt::Write` for `Serial`
- `From<Bps>` for `serial::Config`
- `From<Into<Hertz>>` for `i2c::Mode`
- `exti_rtic` example
Expand All @@ -38,6 +40,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Fix i2c interactions after errors
- Fix SPI3 alternate function remapping
- Do not enable UART DMA flags unconditionally
- Fix invalid 8-bit access to USART registers.

### Changed

Expand Down
142 changes: 142 additions & 0 deletions examples/serial_9bits.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
//! Testing 9 bits USART word length mode.
//!
//! This example demonstrates the use of the MSB bit (bit 8) to mark the beginning of a packet.
//! The first byte of the packet contains the address of the slave device.
//! The second byte of the packet contains the length of the message.
//! The remaining bytes of the packet contain the message itself.
//#![deny(unsafe_code)]
#![no_main]
#![no_std]

use cortex_m_rt::entry;
use nb::block;
use panic_halt as _;
use stm32f1xx_hal::{
pac,
prelude::*,
serial::{Config, Rx3_16, Serial, Tx3_16},
};

// The address of the slave device.
const SLAVE_ADDR: u8 = 123;

// Maximum possible message length.
const MSG_MAX_LEN: usize = u8::MAX as usize;

// Receives a message addressed to the slave device. Returns the size of the received message.
fn receive_msg(serial_rx: &mut Rx3_16, buf: &mut [u8; MSG_MAX_LEN]) -> usize {
enum RxPhase {
Start,
Length,
Data { len: usize, idx: usize },
}

let mut rx_phase = RxPhase::Start;

loop {
// Read the word that was just sent. Blocks until the read is complete.
let received = block!(serial_rx.read()).unwrap();

// If the beginning of the packet.
if (received & 0x100) != 0 {
rx_phase = if received as u8 == SLAVE_ADDR {
RxPhase::Length
} else {
RxPhase::Start
}
} else {
match rx_phase {
RxPhase::Start => {}

RxPhase::Length => {
if received == 0 {
return 0;
}
rx_phase = RxPhase::Data {
len: received as usize,
idx: 0,
};
}

RxPhase::Data { len, ref mut idx } => {
buf[*idx] = received as u8;
*idx += 1;
if *idx == len {
return len;
}
}
}
}
}
}

// Send message.
fn send_msg(mut serial_tx: Tx3_16, msg: &[u8]) -> Tx3_16 {
// Send address.
block!(serial_tx.write(SLAVE_ADDR as u16 | 0x100)).ok();

// Switching from u16 to u8 data.
let mut serial_tx = serial_tx.with_u8_data();

// Send message len.
assert!(msg.len() <= MSG_MAX_LEN);
block!(serial_tx.write(msg.len() as u8)).ok();

// Send message.
for &b in msg {
block!(serial_tx.write(b)).ok();
}

// Switching back from u8 to u16 data.
serial_tx.with_u16_data()
}

#[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 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();

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

let tx_pin = gpiob.pb10.into_alternate_push_pull(&mut gpiob.crh);
let rx_pin = 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_pin, rx_pin),
&mut afio.mapr,
Config::default().baudrate(9600.bps()).wordlength_9(),
clocks,
)
// Switching the 'Word' type parameter for the 'Read' and 'Write' traits from u8 to u16.
.with_u16_data();

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

let mut buf = [0u8; MSG_MAX_LEN];

// loopback
loop {
// Receive message from master device.
let received_msg_len = receive_msg(&mut serial_rx, &mut buf);
// Send the received message back.
serial_tx = send_msg(serial_tx, &buf[..received_msg_len]);
}
}
1 change: 1 addition & 0 deletions examples/serial_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ fn main() -> ! {
serial::Config::default()
.baudrate(9600.bps())
.stopbits(serial::StopBits::STOP2)
.wordlength_9()
.parity_odd(),
clocks,
);
Expand Down
35 changes: 28 additions & 7 deletions src/serial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
//! p.USART1,
//! (pin_tx, pin_rx),
//! &mut afio.mapr,
//! Config::default().baudrate(9_600.bps()),
//! Config::default().baudrate(9_600.bps()).wordlength_9(),
//! clocks,
//! );
//!
Expand Down Expand Up @@ -78,7 +78,6 @@
use core::marker::PhantomData;
use core::ops::Deref;
use core::ptr;
use core::sync::atomic::{self, Ordering};

use crate::pac::{RCC, USART1, USART2, USART3};
Expand Down Expand Up @@ -584,6 +583,30 @@ where
}
}

impl<USARTX> Rx<USARTX, u8> {
pub fn with_u16_data(self) -> Rx<USARTX, u16> {
Rx::new()
}
}

impl<USARTX> Rx<USARTX, u16> {
pub fn with_u8_data(self) -> Rx<USARTX, u8> {
Rx::new()
}
}

impl<USARTX> Tx<USARTX, u8> {
pub fn with_u16_data(self) -> Tx<USARTX, u16> {
Tx::new()
}
}

impl<USARTX> Tx<USARTX, u16> {
pub fn with_u8_data(self) -> Tx<USARTX, u8> {
Tx::new()
}
}

impl<USART, PINS, WORD> crate::hal::serial::Read<WORD> for Serial<USART, PINS, WORD>
where
USART: Instance,
Expand Down Expand Up @@ -641,17 +664,15 @@ where
// do a read from the sr register followed by a read from the dr
// register
// NOTE(read_volatile) see `write_volatile` below
unsafe {
ptr::read_volatile(&usart.sr as *const _ as *const _);
ptr::read_volatile(&usart.dr as *const _ as *const _);
}
let _ = usart.sr.read();
let _ = usart.dr.read();
Err(nb::Error::Other(err))
} else {
// Check if a byte is available
if sr.rxne().bit_is_set() {
// Read the received byte
// NOTE(read_volatile) see `write_volatile` below
Ok(unsafe { ptr::read_volatile(&usart.dr as *const _ as *const _) })
return Ok(usart.dr.read().dr().bits());
} else {
Err(nb::Error::WouldBlock)
}
Expand Down

0 comments on commit 333d30e

Please sign in to comment.