1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
//! This module allows for serial communication using the STM32 USART module.

// todo: see if you can use this macro-less approach elsewhere. Currently used
// todo here and in I2c

// todo: Synchronous mode.
// todo: Auto baud

use crate::{
    pac::{self, RCC},
    rcc_en_reset,
    traits::ClockCfg,
};

use core::ops::Deref;

use cfg_if::cfg_if;

// todo: Make a single interrupt function that takes this macro?
// todo: Prescaler (USART_PRESC) register on v3 (L5, G, H etc)

#[derive(Clone, Copy)]
#[repr(u8)]
/// The number of stop bits. (USART_CR2, STOP)
pub enum StopBits {
    S1 = 0b00,
    S0_5 = 0b01,
    S2 = 0b10,
    S1_5 = 0b11,
}

#[derive(Clone, Copy)]
#[repr(u8)]
/// The length of word to transmit and receive. (USART_CR1, M)
pub enum WordLen {
    W8 = 0b00,
    W9 = 0b01,
    W7 = 0b10,
}

impl WordLen {
    /// We use this function due to the M field being split into 2 separate bits.
    /// Returns M1 val, M0 val
    /// todo: Make sure this isn't backwards.
    fn bits(&self) -> (u8, u8) {
        match self {
            Self::W8 => (0, 0),
            Self::W9 => (0, 1),
            Self::W7 => (1, 0),
        }
    }
}

#[derive(Clone, Copy)]
/// Used for setting the appropriate APB.
pub enum UsartDevice {
    One,
    Two,
    #[cfg(not(any(feature = "l4x1", feature = "g0")))]
    Three,
    // Four,  todo
    // Five,
}

#[derive(Clone, Copy)]
#[repr(u8)]
/// Used for setting the appropriate APB.
pub enum OverSampling {
    O16 = 0,
    O8 = 1,
}

#[derive(Clone, Copy)]
/// The type of USART interrupt to configure. Reference the USART_ISR register.
pub enum UsartInterrupt {
    CharDetect(u8),
    Cts,
    EndOfBlock,
    Idle,
    FramingError,
    LineBreak,
    Overrun,
    ParityError,
    ReadNotEmpty,
    ReceiverTimeout,
    Tcbgt,
    TransmissionComplete,
    TransmitEmpty,
}

/// Represents the USART peripheral, for serial communications.
pub struct Usart<U> {
    regs: U,
    baud: u32,
    word_len: WordLen,
    stop_bits: StopBits,
    oversampling: OverSampling,
}

impl<U> Usart<U>
where
    U: Deref<Target = pac::usart1::RegisterBlock>,
{
    pub fn new<C: ClockCfg>(
        regs: U,
        device: UsartDevice,
        baud: u32,
        word_len: WordLen,
        stop_bits: StopBits,
        clocks: &C,
        rcc: &mut RCC,
    ) -> Self {
        // todo: Let user customize oversampling?
        let oversampling = OverSampling::O16;

        // todo: Hard set to usart 1 to get started
        match device {
            UsartDevice::One => {
                rcc_en_reset!(apb2, usart1, rcc);
            }
            UsartDevice::Two => {
                rcc_en_reset!(apb1, usart2, rcc);
            }
            #[cfg(not(any(feature = "l4x1", feature = "g0")))]
            UsartDevice::Three => {
                rcc_en_reset!(apb1, usart3, rcc);
            } // UsartDevice::Four => {
              //     rcc_en_reset!(apb1, uart4, rcc);
              // }
              // UsartDevice::Five => {
              //     rcc_en_reset!(apb1, usart5, rcc);
              // }
        }

        // This should already be disabled on power up, but disable here just in case:
        regs.cr1.modify(|_, w| w.ue().clear_bit());
        while regs.cr1.read().ue().bit_is_set() {}

        regs.cr1
            .modify(|_, w| w.over8().bit(oversampling as u8 != 0));

        // Set up transmission. See L44 RM, section 38.5.2: "Character Transmission Procedures".
        // 1. Program the M bits in USART_CR1 to define the word length.
        // todo: Make sure you aren't doing this backwards.

        cfg_if! {
            if #[cfg(feature = "f3")] {
                regs.cr1.modify(|_, w| w.m().bit(word_len as u8 != 0));
            } else {
                let word_len_bits = word_len.bits();
                regs.cr1.modify(|_, w| w.m1().bit(word_len_bits.0 != 0));
                regs.cr1.modify(|_, w| w.m0().bit(word_len_bits.1 != 0));
            }
        }

        // 2. Select the desired baud rate using the USART_BRR register.

        // To set BAUD rate, see L4 RM section 38.5.4: "USART baud rate generation".
        let fclk = match device {
            UsartDevice::One => clocks.apb2(),
            _ => clocks.apb1(),
        };

        // Oversampling by 16:
        let usart_div = match oversampling {
            OverSampling::O16 => fclk / baud,
            OverSampling::O8 => 2 * fclk / baud,
        };

        // USARTDIV is an unsigned fixed point number that is coded on the USART_BRR register.
        // • When OVER8 = 0, BRR = USARTDIV.
        // • When OVER8 = 1
        // – BRR[2:0] = USARTDIV[3:0] shifted 1 bit to the right.
        // – BRR[3] must be kept cleared.
        // – BRR[15:4] = USARTDIV[15:4]
        // todo: BRR needs to be modified per the above if on oversampling 8.

        regs.brr.write(|w| unsafe { w.bits(usart_div as u32) });
        // 3. Program the number of stop bits in USART_CR2.
        regs.cr2
            .modify(|_, w| unsafe { w.stop().bits(stop_bits as u8) });
        // 4. Enable the USART by writing the UE bit in USART_CR1 register to 1.
        regs.cr1.modify(|_, w| w.ue().set_bit());
        // 5. Select DMA enable (DMAT) in USART_CR3 if multibuffer communication is to take
        // place. Configure the DMA register as explained in multibuffer communication.
        // todo?
        // 6. Set the TE bit in USART_CR1 to send an idle frame as first transmission.
        // 6. Set the RE bit USART_CR1. This enables the receiver which begins searching for a
        // start bit.
        regs.cr1.modify(|_, w| {
            w.te().set_bit();
            w.re().set_bit()
        });

        Self {
            regs,
            baud,
            word_len,
            stop_bits,
            oversampling,
        }
    }

    /// Transmit data, as a sequence of u8.. See L44 RM, section 38.5.2: "Character transmission procedure"
    pub fn write(&mut self, data: &[u8]) {
        // 7. Write the data to send in the USART_TDR register (this clears the TXE bit). Repeat this
        // for each data to be transmitted in case of single buffer.
        // todo: Do we need to manually wait until txe = 1?
        for word in data {
            while self.regs.isr.read().txe().bit_is_clear() {}
            // todo: how does this work with a 9 bit words? Presumably you'd need to make `data`
            // todo take `&u16`.
            self.regs
                .tdr
                .modify(|_, w| unsafe { w.tdr().bits(*word as u16) });
        }
        // 8. After writing the last data into the USART_TDR register, wait until TC=1. This indicates
        // that the transmission of the last frame is complete. This is required for instance when
        // the USART is disabled or enters the Halt mode to avoid corrupting the last
        // transmission
        while self.regs.isr.read().tc().bit_is_clear() {}
    }

    /// Receive data into a u8 buffer. See L44 RM, section 38.5.3: "Character reception procedure"
    pub fn read(&mut self, buf: &mut [u8]) {
        for i in 0..buf.len() {
            // Wait for the next bit
            while self.regs.isr.read().rxne().bit_is_clear() {}
            buf[i] = self.regs.rdr.read().rdr().bits() as u8;
        }

        // When a character is received:
        // • The RXNE bit is set to indicate that the content of the shift register is transferred to the
        // RDR. In other words, data has been received and can be read (as well as its
        // associated error flags).
        // • An interrupt is generated if the RXNEIE bit is set.
        // • The error flags can be set if a frame error, noise or an overrun error has been detected
        // during reception. PE flag can also be set with RXNE.
        // • In multibuffer, RXNE is set after every byte received and is cleared by the DMA read of
        // the Receive data Register.
        // • In single buffer mode, clearing the RXNE bit is performed by a software read to the
        // USART_RDR register. The RXNE flag can also be cleared by writing 1 to the RXFRQ
        // in the USART_RQR register. The RXNE bit must be cleared before the end of the
        // reception of the next character to avoid an overrun error
    }

    // /// Flush the transmit buffer.
    // pub fn flush(&mut self) {
    //
    // }

    /// Enable a specific type of interrupt.
    pub fn enable_interrupt(&mut self, interrupt_type: UsartInterrupt) {
        // Disable the UART to allow writing the `add` and `addm7` bits
        self.regs.cr1.modify(|_, w| w.ue().clear_bit());
        while self.regs.cr1.read().ue().bit_is_set() {}

        match interrupt_type {
            UsartInterrupt::CharDetect(char) => {
                // Enable character-detecting UART interrupt
                self.regs.cr1.modify(|_, w| w.cmie().set_bit());

                // Allow an 8-bit address to be set in `add`.
                self.regs.cr2.modify(|_, w| {
                    w.addm7().set_bit();
                    // Set the character to detect
                    cfg_if! {
                        if #[cfg(any(feature = "f3", feature = "l4", feature = "h7"))] {
                            w.add().bits(char)
                        } else { unsafe { // todo: Is this right, or backwards?
                            w.add0_3().bits(char & 0b1111);
                            w.add4_7().bits(char & (0b1111 << 4))
                        }}
                    }
                });
            }
            UsartInterrupt::Cts => {
                self.regs.cr3.modify(|_, w| w.ctsie().set_bit());
            }
            UsartInterrupt::EndOfBlock => {
                self.regs.cr1.modify(|_, w| w.eobie().set_bit());
            }
            UsartInterrupt::Idle => {
                self.regs.cr1.modify(|_, w| w.idleie().set_bit());
            }
            UsartInterrupt::FramingError => {
                // self.regs.cr1.modify(|_, w| w.eie().set_bit()); // todo
            }
            UsartInterrupt::LineBreak => {
                self.regs.cr2.modify(|_, w| w.lbdie().set_bit());
            }
            UsartInterrupt::Overrun => {
                // self.regs.cr1.modify(|_, w| w.eie().set_bit()); // todo
            }
            UsartInterrupt::ParityError => {
                self.regs.cr1.modify(|_, w| w.peie().set_bit());
            }
            UsartInterrupt::ReadNotEmpty => {
                self.regs.cr1.modify(|_, w| w.rxneie().set_bit());
            }
            UsartInterrupt::ReceiverTimeout => {
                self.regs.cr1.modify(|_, w| w.rtoie().set_bit());
            }
            UsartInterrupt::Tcbgt => {
                // self.regs.cr3.modify(|_, w| w.tcbgtie().set_bit()); // todo?
            }
            UsartInterrupt::TransmissionComplete => {
                self.regs.cr1.modify(|_, w| w.tcie().set_bit());
            }
            UsartInterrupt::TransmitEmpty => {
                self.regs.cr1.modify(|_, w| w.txeie().set_bit());
            }
        }

        self.regs.cr1.modify(|_, w| w.ue().set_bit());
    }

    /// Clears the interrupt pending flag for a specific type of interrupt.
    pub fn clear_interrupt(&mut self, interrupt_type: UsartInterrupt) {
        match interrupt_type {
            UsartInterrupt::CharDetect(_) => self.regs.icr.write(|w| w.cmcf().set_bit()),
            UsartInterrupt::Cts => self.regs.icr.write(|w| w.ctscf().set_bit()),
            UsartInterrupt::EndOfBlock => self.regs.icr.write(|w| w.eobcf().set_bit()),
            UsartInterrupt::Idle => self.regs.icr.write(|w| w.idlecf().set_bit()),
            UsartInterrupt::FramingError => self.regs.icr.write(|w| w.fecf().set_bit()),
            UsartInterrupt::LineBreak => self.regs.icr.write(|w| w.lbdcf().set_bit()),
            UsartInterrupt::Overrun => self.regs.icr.write(|w| w.orecf().set_bit()),
            UsartInterrupt::ParityError => self.regs.icr.write(|w| w.pecf().set_bit()),
            UsartInterrupt::ReadNotEmpty => self.regs.rqr.write(|w| w.rxfrq().set_bit()),
            UsartInterrupt::ReceiverTimeout => self.regs.icr.write(|w| w.rtocf().set_bit()),
            UsartInterrupt::Tcbgt => {
                // self.regs.icr.write(|w| w.tcbgtcf().set_bit()) // todo ?
            }
            UsartInterrupt::TransmissionComplete => self.regs.icr.write(|w| w.tccf().set_bit()),
            UsartInterrupt::TransmitEmpty => self.regs.rqr.write(|w| w.txfrq().set_bit()),
        }
    }
}

// todo: Implement EH traits.