Skip to content

Commit ce1d478

Browse files
bors[bot]horazont
andauthored
Merge #385
385: Add support for reconfiguring a Serial port r=burrbull a=horazont Example use case is to implement a [1-Wire Bus "Master"][1]. In that example, the UART is used instead of bit-banging the 1-Wire protocol. However, not all signals can be generated with a single baud rate. In particular, the 1-wire reset strobe is much longer than the 1-wire data strobes, requiring to use a different, slower baud rate to reset devices. [1]: https://www.maximintegrated.com/en/design/technical-documents/tutorials/2/214.html Co-authored-by: Jonas Schäfer <j.wielicki@sotecware.net>
2 parents c62c997 + 65e63b2 commit ce1d478

File tree

3 files changed

+128
-8
lines changed

3 files changed

+128
-8
lines changed

CHANGELOG.md

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

88
## [Unreleased]
99

10+
### Added
11+
12+
- Serial can now be reconfigured, allowing to change e.g. the baud rate after initialisation.
13+
1014
## [v0.8.0] - 2021-12-29
1115

1216
### Breaking changes

examples/serial_reconfigure.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
//! Serial interface reconfiguration test
2+
//!
3+
//! You have to short the TX and RX pins to make this program work
4+
5+
#![deny(unsafe_code)]
6+
#![no_main]
7+
#![no_std]
8+
9+
use panic_halt as _;
10+
11+
use cortex_m::asm;
12+
13+
use nb::block;
14+
15+
use cortex_m_rt::entry;
16+
use stm32f1xx_hal::{
17+
pac,
18+
prelude::*,
19+
serial::{Config, Serial},
20+
};
21+
22+
#[entry]
23+
fn main() -> ! {
24+
// Get access to the device specific peripherals from the peripheral access crate
25+
let p = pac::Peripherals::take().unwrap();
26+
27+
// Take ownership over the raw flash and rcc devices and convert them into the corresponding
28+
// HAL structs
29+
let mut flash = p.FLASH.constrain();
30+
let rcc = p.RCC.constrain();
31+
32+
// Freeze the configuration of all the clocks in the system and store the frozen frequencies in
33+
// `clocks`
34+
let clocks = rcc.cfgr.freeze(&mut flash.acr);
35+
36+
// Prepare the alternate function I/O registers
37+
let mut afio = p.AFIO.constrain();
38+
39+
// Prepare the GPIOB peripheral
40+
let mut gpiob = p.GPIOB.split();
41+
42+
// USART1
43+
// let tx = gpioa.pa9.into_alternate_push_pull(&mut gpioa.crh);
44+
// let rx = gpioa.pa10;
45+
46+
// USART1
47+
// let tx = gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl);
48+
// let rx = gpiob.pb7;
49+
50+
// USART2
51+
// let tx = gpioa.pa2.into_alternate_push_pull(&mut gpioa.crl);
52+
// let rx = gpioa.pa3;
53+
54+
// USART3
55+
// Configure pb10 as a push_pull output, this will be the tx pin
56+
let tx = gpiob.pb10.into_alternate_push_pull(&mut gpiob.crh);
57+
// Take ownership over pb11
58+
let rx = gpiob.pb11;
59+
60+
// Set up the usart device. Taks ownership over the USART register and tx/rx pins. The rest of
61+
// the registers are used to enable and configure the device.
62+
let mut serial = Serial::usart3(
63+
p.USART3,
64+
(tx, rx),
65+
&mut afio.mapr,
66+
Config::default().baudrate(9600.bps()),
67+
clocks,
68+
);
69+
70+
// Loopback test. Write `X` and wait until the write is successful.
71+
let sent = b'X';
72+
block!(serial.write(sent)).ok();
73+
74+
// Read the byte that was just sent. Blocks until the read is complete
75+
let received = block!(serial.read()).unwrap();
76+
77+
// Since we have connected tx and rx, the byte we sent should be the one we received
78+
assert_eq!(received, sent);
79+
80+
// Trigger a breakpoint to allow us to inspect the values
81+
asm::bkpt();
82+
83+
// You can reconfigure the serial port to use a different baud rate at runtime.
84+
// This may block for a while if the transmission is still in progress.
85+
block!(serial.reconfigure(Config::default().baudrate(115_200.bps()), clocks)).unwrap();
86+
87+
// Let's see if it works.'
88+
let sent = b'Y';
89+
block!(serial.write(sent)).ok();
90+
let received = block!(serial.read()).unwrap();
91+
assert_eq!(received, sent);
92+
asm::bkpt();
93+
94+
loop {}
95+
}

src/serial.rs

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,19 @@ where
235235
USART::reset(rcc);
236236

237237
remap();
238+
self.apply_config(config, clocks);
239+
240+
// UE: enable USART
241+
// RE: enable receiver
242+
// TE: enable transceiver
243+
self.usart
244+
.cr1
245+
.modify(|_r, w| w.ue().set_bit().re().set_bit().te().set_bit());
246+
247+
self
248+
}
249+
250+
fn apply_config(&self, config: Config, clocks: Clocks) {
238251
// Configure baud rate
239252
let brr = USART::get_frequency(&clocks).0 / config.baudrate.0;
240253
assert!(brr >= 16, "impossible baud rate");
@@ -267,15 +280,23 @@ where
267280
StopBits::STOP1P5 => 0b11,
268281
};
269282
self.usart.cr2.modify(|_r, w| w.stop().bits(stop_bits));
283+
}
270284

271-
// UE: enable USART
272-
// RE: enable receiver
273-
// TE: enable transceiver
274-
self.usart
275-
.cr1
276-
.modify(|_r, w| w.ue().set_bit().re().set_bit().te().set_bit());
277-
278-
self
285+
/// Reconfigure the USART instance.
286+
///
287+
/// If a transmission is currently in progress, this returns
288+
/// [`nb::Error::WouldBlock`].
289+
pub fn reconfigure(&mut self, config: impl Into<Config>, clocks: Clocks) -> nb::Result<(), ()> {
290+
let sr = self.usart.sr.read();
291+
// if we're currently busy transmitting, we have to wait until that is
292+
// over -- regarding reception, we assume that the caller -- with
293+
// exclusive access to the Serial instance due to &mut self -- knows
294+
// what they're doing.
295+
if sr.tc().bit_is_clear() {
296+
return nb::Result::Err(nb::Error::WouldBlock);
297+
}
298+
self.apply_config(config.into(), clocks);
299+
nb::Result::Ok(())
279300
}
280301

281302
/// Starts listening to the USART by enabling the _Received data

0 commit comments

Comments
 (0)