stm32l4_hal/
serial.rs

1//! Serial
2
3use core::marker::{PhantomData, Unsize};
4use core::ptr;
5use core::sync::atomic::{self, Ordering};
6
7use crate::hal::serial;
8use nb;
9use crate::stm32::{USART1, USART2};
10use void::Void;
11
12use crate::gpio::gpioa::{PA10, PA2, PA3, PA9};
13use crate::gpio::gpiob::{PB6, PB7};
14use crate::gpio::{AF7, Alternate, Input, Floating};
15use crate::rcc::{APB1R1, APB2, Clocks};
16use crate::time::Bps;
17use crate::dma::{dma1, CircBuffer};
18
19/// Interrupt event
20pub enum Event {
21    /// New data has been received
22    Rxne,
23    /// New data can be sent
24    Txe,
25    /// The line has gone idle
26    Idle
27}
28
29/// Serial error
30#[derive(Debug)]
31pub enum Error {
32    /// Framing error
33    Framing,
34    /// Noise error
35    Noise,
36    /// RX buffer overrun
37    Overrun,
38    /// Parity check error
39    Parity,
40    #[doc(hidden)]
41    _Extensible,
42}
43
44pub trait Pins<USART> {
45    const REMAP: u8;
46}
47
48impl Pins<USART1> for (PA9<Alternate<AF7, Input<Floating>>>, PA10<Alternate<AF7, Input<Floating>>>) {
49    const REMAP: u8 = 0;
50}
51
52impl Pins<USART1> for (PB6<Alternate<AF7, Input<Floating>>>, PB7<Alternate<AF7, Input<Floating>>>) {
53    const REMAP: u8 = 1;
54}
55
56impl Pins<USART2> for (PA2<Alternate<AF7, Input<Floating>>>, PA3<Alternate<AF7, Input<Floating>>>) {
57    const REMAP: u8 = 0;
58}
59
60// impl Pins<USART2> for (PD5<Alternate<PushPull>>, PD6<Input<Floating>>) {
61//     const REMAP: u8 = 0;
62// }
63
64
65/// Serial abstraction
66pub struct Serial<USART, PINS> {
67    usart: USART,
68    pins: PINS,
69}
70
71/// Serial receiver
72pub struct Rx<USART> {
73    _usart: PhantomData<USART>,
74}
75
76/// Serial transmitter
77pub struct Tx<USART> {
78    _usart: PhantomData<USART>,
79}
80
81macro_rules! hal {
82    ($(
83        $USARTX:ident: ($usartX:ident, $APB:ident, $usartXen:ident, $usartXrst:ident, $pclkX:ident, tx: ($dmacst:ident, $tx_chan:path), rx: ($dmacsr:ident, $rx_chan:path)),
84    )+) => {
85        $(
86            impl<PINS> Serial<$USARTX, PINS> {
87                /// Configures a USART peripheral to provide serial communication
88                pub fn $usartX(
89                    usart: $USARTX,
90                    pins: PINS,
91                    baud_rate: Bps,
92                    clocks: Clocks,
93                    apb: &mut $APB,
94                ) -> Self
95                where
96                    PINS: Pins<$USARTX>,
97                {
98                    // enable or reset $USARTX
99                    apb.enr().modify(|_, w| w.$usartXen().set_bit());
100                    apb.rstr().modify(|_, w| w.$usartXrst().set_bit());
101                    apb.rstr().modify(|_, w| w.$usartXrst().clear_bit());
102
103                    // TODO implement pin remaping
104
105                    // disable hardware flow control
106                    // usart.cr3.write(|w| w.rtse().clear_bit().ctse().clear_bit());
107                    
108                    usart.cr3.write(|w| w.dmat().set_bit().dmar().set_bit()); // enable DMA transfers
109
110                    let brr = clocks.$pclkX().0 / baud_rate.0;
111                    assert!(brr >= 16, "impossible baud rate");
112                    usart.brr.write(|w| unsafe { w.bits(brr) });
113
114                    // UE: enable USART
115                    // RE: enable receiver
116                    // TE: enable transceiver
117                    usart
118                        .cr1
119                        .write(|w| w.ue().set_bit().re().set_bit().te().set_bit());
120
121                    Serial { usart, pins }
122                }
123
124                /// Starts listening for an interrupt event
125                pub fn listen(&mut self, event: Event) {
126                    match event {
127                        Event::Rxne => {
128                            self.usart.cr1.modify(|_, w| w.rxneie().set_bit())
129                        },
130                        Event::Txe => {
131                            self.usart.cr1.modify(|_, w| w.txeie().set_bit())
132                        },
133                        Event::Idle => {
134                            self.usart.cr1.modify(|_, w| w.idleie().set_bit())
135                        },
136                    }
137                }
138
139                /// Starts listening for an interrupt event
140                pub fn unlisten(&mut self, event: Event) {
141                    match event {
142                        Event::Rxne => {
143                            self.usart.cr1.modify(|_, w| w.rxneie().clear_bit())
144                        },
145                        Event::Txe => {
146                            self.usart.cr1.modify(|_, w| w.txeie().clear_bit())
147                        },
148                        Event::Idle => {
149                            self.usart.cr1.modify(|_, w| w.idleie().clear_bit())
150                        },
151                    }
152                }
153
154                /// Splits the `Serial` abstraction into a transmitter and a receiver half
155                pub fn split(self) -> (Tx<$USARTX>, Rx<$USARTX>) {
156                    (
157                        Tx {
158                            _usart: PhantomData,
159                        },
160                        Rx {
161                            _usart: PhantomData,
162                        },
163                    )
164                }
165
166                /// Releases the USART peripheral and associated pins
167                pub fn free(self) -> ($USARTX, PINS) {
168                    (self.usart, self.pins)
169                }
170            }
171
172            impl serial::Read<u8> for Rx<$USARTX> {
173                type Error = Error;
174
175                fn read(&mut self) -> nb::Result<u8, Error> {
176                    // NOTE(unsafe) atomic read with no side effects
177                    let isr = unsafe { (*$USARTX::ptr()).isr.read() };
178
179                    Err(if isr.pe().bit_is_set() {
180                        nb::Error::Other(Error::Parity)
181                    } else if isr.fe().bit_is_set() {
182                        nb::Error::Other(Error::Framing)
183                    } else if isr.nf().bit_is_set() {
184                        nb::Error::Other(Error::Noise)
185                    } else if isr.ore().bit_is_set() {
186                        nb::Error::Other(Error::Overrun)
187                    } else if isr.rxne().bit_is_set() {
188                        // NOTE(read_volatile) see `write_volatile` below
189                        return Ok(unsafe {
190                            ptr::read_volatile(&(*$USARTX::ptr()).rdr as *const _ as *const _)
191                        });
192                    } else {
193                        nb::Error::WouldBlock
194                    })
195                }
196            }
197
198            impl serial::Write<u8> for Tx<$USARTX> {
199                // NOTE(Void) See section "29.7 USART interrupts"; the only possible errors during
200                // transmission are: clear to send (which is disabled in this case) errors and
201                // framing errors (which only occur in SmartCard mode); neither of these apply to
202                // our hardware configuration
203                type Error = Void;
204
205                fn flush(&mut self) -> nb::Result<(), Void> {
206                    // NOTE(unsafe) atomic read with no side effects
207                    let isr = unsafe { (*$USARTX::ptr()).isr.read() };
208
209                    if isr.tc().bit_is_set() {
210                        Ok(())
211                    } else {
212                        Err(nb::Error::WouldBlock)
213                    }
214                }
215
216                fn write(&mut self, byte: u8) -> nb::Result<(), Void> {
217                    // NOTE(unsafe) atomic read with no side effects
218                    let isr = unsafe { (*$USARTX::ptr()).isr.read() };
219
220                    if isr.txe().bit_is_set() {
221                        // NOTE(unsafe) atomic write to stateless register
222                        // NOTE(write_volatile) 8-bit write that's not possible through the svd2rust API
223                        unsafe {
224                            ptr::write_volatile(&(*$USARTX::ptr()).tdr as *const _ as *mut _, byte)
225                        }
226                        Ok(())
227                    } else {
228                        Err(nb::Error::WouldBlock)
229                    }
230                }
231            }
232
233            impl Rx<$USARTX> {
234                pub fn circ_read<B>(
235                    &self,
236                    mut chan: $rx_chan,
237                    buffer: &'static mut [B; 2],
238                ) -> CircBuffer<B, $rx_chan>
239                where
240                    B: Unsize<[u8]>,
241                {
242                    {
243                        let buffer: &[u8] = &buffer[0];
244                        chan.cmar().write(|w| {
245                            w.ma().bits(buffer.as_ptr() as usize as u32)
246                        });
247                        chan.cndtr().write(|w|{
248                            w.ndt().bits((buffer.len() * 2) as u16)
249                        });
250                        chan.cpar().write(|w| unsafe {
251                            w.pa().bits(&(*$USARTX::ptr()).rdr as *const _ as usize as u32)
252                        });
253
254                        // Tell DMA to request from serial
255                        chan.cselr().write(|w| {
256                            w.$dmacsr().bits(0b0010)
257                        });
258
259                        // TODO can we weaken this compiler barrier?
260                        // NOTE(compiler_fence) operations on `buffer` should not be reordered after
261                        // the next statement, which starts the DMA transfer
262                        atomic::compiler_fence(Ordering::SeqCst);
263
264                        chan.ccr().modify(|_, w| unsafe {
265                            w.mem2mem()
266                                .clear_bit()
267                                // 00: Low, 01: Medium, 10: High, 11: Very high
268                                .pl()
269                                .bits(0b01)
270                                // 00: 8-bits, 01: 16-bits, 10: 32-bits, 11: Reserved
271                                .msize()
272                                .bits(0b00)
273                                // 00: 8-bits, 01: 16-bits, 10: 32-bits, 11: Reserved
274                                .psize()
275                                .bits(0b00)
276                                // incr mem address
277                                .minc()
278                                .set_bit()
279                                .pinc()
280                                .clear_bit()
281                                .circ()
282                                .set_bit()
283                                .dir()
284                                .clear_bit()
285                                .en()
286                                .set_bit()
287                        });
288                    }
289
290                    CircBuffer::new(buffer, chan)
291                }
292
293                /// Checks to see if the usart peripheral has detected an idle line and clears the flag
294                pub fn is_idle(&mut self, clear: bool) -> bool {
295                    let isr = unsafe { &(*$USARTX::ptr()).isr.read() };
296                    let icr = unsafe { &(*$USARTX::ptr()).icr };
297                    
298                    if isr.idle().bit_is_set() {
299                        if clear {
300                            icr.write(|w| {
301                                w.idlecf()
302                                .set_bit()
303                            });
304                        }
305                        true
306                    } else {
307                        false
308                    }
309                }
310            }
311        )+
312    }
313}
314
315hal! {
316    USART1: (usart1, APB2, usart1en, usart1rst, pclk2, tx: (c4s, dma1::C4), rx: (c5s, dma1::C5)),
317    USART2: (usart2, APB1R1, usart2en, usart2rst, pclk1, tx: (c7s, dma1::C7), rx: (c6s, dma1::C6)),
318}