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.