Skip to content

Commit 47285df

Browse files
AndreySmirnov81burrbull
authored andcommitted
Add 9-bits word length mode for serial
1 parent 7a5910e commit 47285df

File tree

4 files changed

+174
-7
lines changed

4 files changed

+174
-7
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
99

1010
### Added
1111

12+
- 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.
13+
- `blocking::serial::Write` for `Tx` and `Serial`. `core::fmt::Write` for `Serial`
1214
- `Instance` for Timer's, rtic-monotonic fugit impl
1315
- Serial can now be reconfigured, allowing to change e.g. the baud rate after initialisation.
1416

@@ -58,6 +60,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
5860
- Fix SPI3 alternate function remapping
5961
- Do not enable UART DMA flags unconditionally
6062
- Fix flash erase verification always failing
63+
- Fix invalid 8-bit access to USART registers.
6164

6265
### Changed
6366

examples/serial_9bits.rs

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
//! Testing 9 bits USART word length mode.
2+
//!
3+
//! This example demonstrates the use of the MSB bit (bit 8) to mark the beginning of a packet.
4+
//! The first byte of the packet contains the address of the slave device.
5+
//! The second byte of the packet contains the length of the message.
6+
//! The remaining bytes of the packet contain the message itself.
7+
8+
//#![deny(unsafe_code)]
9+
#![no_main]
10+
#![no_std]
11+
12+
use cortex_m_rt::entry;
13+
use nb::block;
14+
use panic_halt as _;
15+
use stm32f1xx_hal::{
16+
pac,
17+
prelude::*,
18+
serial::{Config, Rx3_16, Serial, Tx3_16},
19+
};
20+
21+
// The address of the slave device.
22+
const SLAVE_ADDR: u8 = 123;
23+
24+
// Maximum possible message length.
25+
const MSG_MAX_LEN: usize = u8::MAX as usize;
26+
27+
// Receives a message addressed to the slave device. Returns the size of the received message.
28+
fn receive_msg(serial_rx: &mut Rx3_16, buf: &mut [u8; MSG_MAX_LEN]) -> usize {
29+
enum RxPhase {
30+
Start,
31+
Length,
32+
Data { len: usize, idx: usize },
33+
}
34+
35+
let mut rx_phase = RxPhase::Start;
36+
37+
loop {
38+
// Read the word that was just sent. Blocks until the read is complete.
39+
let received = block!(serial_rx.read()).unwrap();
40+
41+
// If the beginning of the packet.
42+
if (received & 0x100) != 0 {
43+
rx_phase = if received as u8 == SLAVE_ADDR {
44+
RxPhase::Length
45+
} else {
46+
RxPhase::Start
47+
}
48+
} else {
49+
match rx_phase {
50+
RxPhase::Start => {}
51+
52+
RxPhase::Length => {
53+
if received == 0 {
54+
return 0;
55+
}
56+
rx_phase = RxPhase::Data {
57+
len: received as usize,
58+
idx: 0,
59+
};
60+
}
61+
62+
RxPhase::Data { len, ref mut idx } => {
63+
buf[*idx] = received as u8;
64+
*idx += 1;
65+
if *idx == len {
66+
return len;
67+
}
68+
}
69+
}
70+
}
71+
}
72+
}
73+
74+
// Send message.
75+
fn send_msg(mut serial_tx: Tx3_16, msg: &[u8]) -> Tx3_16 {
76+
// Send address.
77+
block!(serial_tx.write(SLAVE_ADDR as u16 | 0x100)).ok();
78+
79+
// Switching from u16 to u8 data.
80+
let mut serial_tx = serial_tx.with_u8_data();
81+
82+
// Send message len.
83+
assert!(msg.len() <= MSG_MAX_LEN);
84+
block!(serial_tx.write(msg.len() as u8)).ok();
85+
86+
// Send message.
87+
for &b in msg {
88+
block!(serial_tx.write(b)).ok();
89+
}
90+
91+
// Switching back from u8 to u16 data.
92+
serial_tx.with_u16_data()
93+
}
94+
95+
#[entry]
96+
fn main() -> ! {
97+
// Get access to the device specific peripherals from the peripheral access crate.
98+
let p = pac::Peripherals::take().unwrap();
99+
100+
// Take ownership over the raw flash and rcc devices and convert them into the corresponding
101+
// HAL structs.
102+
let mut flash = p.FLASH.constrain();
103+
let rcc = p.RCC.constrain();
104+
105+
// Freeze the configuration of all the clocks in the system and store the frozen frequencies in
106+
// `clocks`.
107+
let clocks = rcc.cfgr.freeze(&mut flash.acr);
108+
109+
// Prepare the alternate function I/O registers.
110+
let mut afio = p.AFIO.constrain();
111+
112+
// Prepare the GPIOB peripheral.
113+
let mut gpiob = p.GPIOB.split();
114+
115+
let tx_pin = gpiob.pb10.into_alternate_push_pull(&mut gpiob.crh);
116+
let rx_pin = gpiob.pb11;
117+
118+
// Set up the usart device. Taks ownership over the USART register and tx/rx pins. The rest of
119+
// the registers are used to enable and configure the device.
120+
let serial = Serial::usart3(
121+
p.USART3,
122+
(tx_pin, rx_pin),
123+
&mut afio.mapr,
124+
Config::default().baudrate(9600.bps()).wordlength_9(),
125+
clocks,
126+
)
127+
// Switching the 'Word' type parameter for the 'Read' and 'Write' traits from u8 to u16.
128+
.with_u16_data();
129+
130+
// Split the serial struct into a transmitting and a receiving part.
131+
let (mut serial_tx, mut serial_rx) = serial.split();
132+
133+
let mut buf = [0u8; MSG_MAX_LEN];
134+
135+
// loopback
136+
loop {
137+
// Receive message from master device.
138+
let received_msg_len = receive_msg(&mut serial_rx, &mut buf);
139+
// Send the received message back.
140+
serial_tx = send_msg(serial_tx, &buf[..received_msg_len]);
141+
}
142+
}

examples/serial_config.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ fn main() -> ! {
6565
serial::Config::default()
6666
.baudrate(9600.bps())
6767
.stopbits(serial::StopBits::STOP2)
68+
.wordlength_9()
6869
.parity_odd(),
6970
clocks,
7071
);

src/serial.rs

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
//! p.USART1,
4040
//! (pin_tx, pin_rx),
4141
//! &mut afio.mapr,
42-
//! Config::default().baudrate(9_600.bps()),
42+
//! Config::default().baudrate(9_600.bps()).wordlength_9(),
4343
//! clocks,
4444
//! );
4545
//!
@@ -78,7 +78,6 @@
7878
7979
use core::marker::PhantomData;
8080
use core::ops::Deref;
81-
use core::ptr;
8281
use core::sync::atomic::{self, Ordering};
8382

8483
use crate::pac::{RCC, USART1, USART2, USART3};
@@ -609,6 +608,30 @@ where
609608
}
610609
}
611610

611+
impl<USARTX> Rx<USARTX, u8> {
612+
pub fn with_u16_data(self) -> Rx<USARTX, u16> {
613+
Rx::new()
614+
}
615+
}
616+
617+
impl<USARTX> Rx<USARTX, u16> {
618+
pub fn with_u8_data(self) -> Rx<USARTX, u8> {
619+
Rx::new()
620+
}
621+
}
622+
623+
impl<USARTX> Tx<USARTX, u8> {
624+
pub fn with_u16_data(self) -> Tx<USARTX, u16> {
625+
Tx::new()
626+
}
627+
}
628+
629+
impl<USARTX> Tx<USARTX, u16> {
630+
pub fn with_u8_data(self) -> Tx<USARTX, u8> {
631+
Tx::new()
632+
}
633+
}
634+
612635
impl<USART, PINS, WORD> crate::hal::serial::Read<WORD> for Serial<USART, PINS, WORD>
613636
where
614637
USART: Instance,
@@ -666,17 +689,15 @@ where
666689
// do a read from the sr register followed by a read from the dr
667690
// register
668691
// NOTE(read_volatile) see `write_volatile` below
669-
unsafe {
670-
ptr::read_volatile(&usart.sr as *const _ as *const _);
671-
ptr::read_volatile(&usart.dr as *const _ as *const _);
672-
}
692+
let _ = usart.sr.read();
693+
let _ = usart.dr.read();
673694
Err(nb::Error::Other(err))
674695
} else {
675696
// Check if a byte is available
676697
if sr.rxne().bit_is_set() {
677698
// Read the received byte
678699
// NOTE(read_volatile) see `write_volatile` below
679-
Ok(unsafe { ptr::read_volatile(&usart.dr as *const _ as *const _) })
700+
usart.dr.read().dr().bits()
680701
} else {
681702
Err(nb::Error::WouldBlock)
682703
}

0 commit comments

Comments
 (0)