diff options
| -rw-r--r-- | embassy-nxp/Cargo.toml | 3 | ||||
| -rw-r--r-- | embassy-nxp/src/chips/lpc55.rs | 9 | ||||
| -rw-r--r-- | embassy-nxp/src/lib.rs | 20 | ||||
| -rw-r--r-- | embassy-nxp/src/usart.rs | 6 | ||||
| -rw-r--r-- | embassy-nxp/src/usart/lpc55.rs | 885 | ||||
| -rw-r--r-- | examples/lpc55s69/src/bin/usart_blocking.rs | 40 |
6 files changed, 962 insertions, 1 deletions
diff --git a/embassy-nxp/Cargo.toml b/embassy-nxp/Cargo.toml index 9fa48c4b9..cdfaee64d 100644 --- a/embassy-nxp/Cargo.toml +++ b/embassy-nxp/Cargo.toml | |||
| @@ -24,7 +24,8 @@ log = { version = "0.4.27", optional = true } | |||
| 24 | embassy-time = { version = "0.4.0", path = "../embassy-time", optional = true } | 24 | embassy-time = { version = "0.4.0", path = "../embassy-time", optional = true } |
| 25 | embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", optional = true } | 25 | embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", optional = true } |
| 26 | embassy-time-queue-utils = { version = "0.2", path = "../embassy-time-queue-utils", optional = true } | 26 | embassy-time-queue-utils = { version = "0.2", path = "../embassy-time-queue-utils", optional = true } |
| 27 | 27 | embedded-io = "0.6.1" | |
| 28 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } | ||
| 28 | ## Chip dependencies | 29 | ## Chip dependencies |
| 29 | lpc55-pac = { version = "0.5.0", optional = true } | 30 | lpc55-pac = { version = "0.5.0", optional = true } |
| 30 | nxp-pac = { version = "0.1.0", optional = true, git = "https://github.com/i509VCB/nxp-pac", rev = "be4dd0936c20d5897364a381b1d95a99514c1e7e" } | 31 | nxp-pac = { version = "0.1.0", optional = true, git = "https://github.com/i509VCB/nxp-pac", rev = "be4dd0936c20d5897364a381b1d95a99514c1e7e" } |
diff --git a/embassy-nxp/src/chips/lpc55.rs b/embassy-nxp/src/chips/lpc55.rs index c95218af0..e168ced00 100644 --- a/embassy-nxp/src/chips/lpc55.rs +++ b/embassy-nxp/src/chips/lpc55.rs | |||
| @@ -67,4 +67,13 @@ embassy_hal_internal::peripherals! { | |||
| 67 | PIO1_29, | 67 | PIO1_29, |
| 68 | PIO1_30, | 68 | PIO1_30, |
| 69 | PIO1_31, | 69 | PIO1_31, |
| 70 | |||
| 71 | USART0, | ||
| 72 | USART1, | ||
| 73 | USART2, | ||
| 74 | USART3, | ||
| 75 | USART4, | ||
| 76 | USART5, | ||
| 77 | USART6, | ||
| 78 | USART7 | ||
| 70 | } | 79 | } |
diff --git a/embassy-nxp/src/lib.rs b/embassy-nxp/src/lib.rs index b2e910f7e..3fcb14b7e 100644 --- a/embassy-nxp/src/lib.rs +++ b/embassy-nxp/src/lib.rs | |||
| @@ -6,6 +6,8 @@ pub(crate) mod fmt; | |||
| 6 | pub mod gpio; | 6 | pub mod gpio; |
| 7 | #[cfg(feature = "lpc55")] | 7 | #[cfg(feature = "lpc55")] |
| 8 | pub mod pint; | 8 | pub mod pint; |
| 9 | #[cfg(feature = "lpc55")] | ||
| 10 | pub mod usart; | ||
| 9 | 11 | ||
| 10 | #[cfg(feature = "_time_driver")] | 12 | #[cfg(feature = "_time_driver")] |
| 11 | #[cfg_attr(feature = "time-driver-pit", path = "time_driver/pit.rs")] | 13 | #[cfg_attr(feature = "time-driver-pit", path = "time_driver/pit.rs")] |
| @@ -115,3 +117,21 @@ impl Iterator for BitIter { | |||
| 115 | } | 117 | } |
| 116 | } | 118 | } |
| 117 | } | 119 | } |
| 120 | |||
| 121 | trait SealedMode {} | ||
| 122 | |||
| 123 | /// UART mode. | ||
| 124 | #[allow(private_bounds)] | ||
| 125 | pub trait Mode: SealedMode {} | ||
| 126 | |||
| 127 | macro_rules! impl_mode { | ||
| 128 | ($name:ident) => { | ||
| 129 | impl SealedMode for $name {} | ||
| 130 | impl Mode for $name {} | ||
| 131 | }; | ||
| 132 | } | ||
| 133 | |||
| 134 | /// Blocking mode. | ||
| 135 | pub struct Blocking; | ||
| 136 | |||
| 137 | impl_mode!(Blocking); | ||
diff --git a/embassy-nxp/src/usart.rs b/embassy-nxp/src/usart.rs new file mode 100644 index 000000000..009c251e2 --- /dev/null +++ b/embassy-nxp/src/usart.rs | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | //! Universal Synchronous/Asynchronous Receiver/Transmitter (USART) driver. | ||
| 2 | #![macro_use] | ||
| 3 | |||
| 4 | #[cfg_attr(feature = "lpc55", path = "./usart/lpc55.rs")] | ||
| 5 | mod inner; | ||
| 6 | pub use inner::*; | ||
diff --git a/embassy-nxp/src/usart/lpc55.rs b/embassy-nxp/src/usart/lpc55.rs new file mode 100644 index 000000000..3f7456a2e --- /dev/null +++ b/embassy-nxp/src/usart/lpc55.rs | |||
| @@ -0,0 +1,885 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | |||
| 3 | use embassy_hal_internal::{Peri, PeripheralType}; | ||
| 4 | use embedded_io::{self, ErrorKind}; | ||
| 5 | pub use sealed::SealedInstance; | ||
| 6 | |||
| 7 | use crate::gpio::AnyPin; | ||
| 8 | use crate::{Blocking, Mode}; | ||
| 9 | |||
| 10 | /// Serial error | ||
| 11 | #[derive(Debug, Eq, PartialEq, Copy, Clone)] | ||
| 12 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 13 | #[non_exhaustive] | ||
| 14 | pub enum Error { | ||
| 15 | /// Triggered when the FIFO (or shift-register) is overflowed. | ||
| 16 | Overrun, | ||
| 17 | /// Triggered when there is a parity mismatch between what's received and | ||
| 18 | /// our settings. | ||
| 19 | Parity, | ||
| 20 | /// Triggered when the received character didn't have a valid stop bit. | ||
| 21 | Framing, | ||
| 22 | /// Triggered when the receiver detects noise | ||
| 23 | Noise, | ||
| 24 | /// Triggered when the receiver gets a break | ||
| 25 | Break, | ||
| 26 | } | ||
| 27 | |||
| 28 | impl embedded_io::Error for Error { | ||
| 29 | fn kind(&self) -> ErrorKind { | ||
| 30 | match self { | ||
| 31 | Error::Overrun => ErrorKind::Other, | ||
| 32 | Error::Parity => ErrorKind::InvalidData, | ||
| 33 | Error::Framing => ErrorKind::InvalidData, | ||
| 34 | Error::Noise => ErrorKind::Other, | ||
| 35 | Error::Break => ErrorKind::Interrupted, | ||
| 36 | } | ||
| 37 | } | ||
| 38 | } | ||
| 39 | /// Word length. | ||
| 40 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] | ||
| 41 | pub enum DataBits { | ||
| 42 | /// 7 bits data length. | ||
| 43 | DataBits7, | ||
| 44 | /// 8 bits data length. | ||
| 45 | DataBits8, | ||
| 46 | /// 9 bits data length. The 9th bit is commonly used for addressing in multidrop mode. | ||
| 47 | DataBits9, | ||
| 48 | } | ||
| 49 | |||
| 50 | impl DataBits { | ||
| 51 | fn bits(&self) -> u8 { | ||
| 52 | match self { | ||
| 53 | Self::DataBits7 => 0b00, | ||
| 54 | Self::DataBits8 => 0b01, | ||
| 55 | Self::DataBits9 => 0b10, | ||
| 56 | } | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | /// Parity bit. | ||
| 61 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] | ||
| 62 | pub enum Parity { | ||
| 63 | /// No parity. | ||
| 64 | ParityNone, | ||
| 65 | /// Even parity. | ||
| 66 | ParityEven, | ||
| 67 | /// Odd parity. | ||
| 68 | ParityOdd, | ||
| 69 | } | ||
| 70 | |||
| 71 | impl Parity { | ||
| 72 | fn bits(&self) -> u8 { | ||
| 73 | match self { | ||
| 74 | Self::ParityNone => 0b00, | ||
| 75 | Self::ParityEven => 0b10, | ||
| 76 | Self::ParityOdd => 0b11, | ||
| 77 | } | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | /// Stop bits. | ||
| 82 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] | ||
| 83 | pub enum StopBits { | ||
| 84 | /// 1 stop bit. | ||
| 85 | Stop1, | ||
| 86 | /// 2 stop bits. This setting should only be used for asynchronous communication. | ||
| 87 | Stop2, | ||
| 88 | } | ||
| 89 | |||
| 90 | impl StopBits { | ||
| 91 | fn bits(&self) -> bool { | ||
| 92 | return match self { | ||
| 93 | Self::Stop1 => false, | ||
| 94 | Self::Stop2 => true, | ||
| 95 | }; | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | /// UART config. | ||
| 100 | #[non_exhaustive] | ||
| 101 | #[derive(Clone, Debug)] | ||
| 102 | pub struct Config { | ||
| 103 | /// Baud rate. | ||
| 104 | pub baudrate: u32, | ||
| 105 | /// Word length. | ||
| 106 | pub data_bits: DataBits, | ||
| 107 | /// Stop bits. | ||
| 108 | pub stop_bits: StopBits, | ||
| 109 | /// Parity bit. | ||
| 110 | pub parity: Parity, | ||
| 111 | /// Invert the tx pin output | ||
| 112 | pub invert_tx: bool, | ||
| 113 | /// Invert the rx pin input | ||
| 114 | pub invert_rx: bool, | ||
| 115 | } | ||
| 116 | |||
| 117 | impl Default for Config { | ||
| 118 | fn default() -> Self { | ||
| 119 | Self { | ||
| 120 | baudrate: 9600, | ||
| 121 | data_bits: DataBits::DataBits8, | ||
| 122 | stop_bits: StopBits::Stop1, | ||
| 123 | parity: Parity::ParityNone, | ||
| 124 | invert_rx: false, | ||
| 125 | invert_tx: false, | ||
| 126 | } | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | /// # Type parameters | ||
| 131 | /// 'd: the lifetime marker ensuring correct borrow checking for peripherals used at compile time | ||
| 132 | /// T: the peripheral instance type allowing usage of peripheral specific registers | ||
| 133 | /// M: the operating mode of USART peripheral | ||
| 134 | pub struct Usart<'d, T: Instance, M: Mode> { | ||
| 135 | tx: UsartTx<'d, T, M>, | ||
| 136 | rx: UsartRx<'d, T, M>, | ||
| 137 | } | ||
| 138 | |||
| 139 | pub struct UsartTx<'d, T: Instance, M: Mode> { | ||
| 140 | phantom: PhantomData<(&'d (), T, M)>, | ||
| 141 | } | ||
| 142 | |||
| 143 | pub struct UsartRx<'d, T: Instance, M: Mode> { | ||
| 144 | phantom: PhantomData<(&'d (), T, M)>, | ||
| 145 | } | ||
| 146 | |||
| 147 | impl<'d, T: Instance, M: Mode> UsartTx<'d, T, M> { | ||
| 148 | pub fn new(_usart: Peri<'d, T>, tx: Peri<'d, impl TxPin<T>>, config: Config) -> Self { | ||
| 149 | Usart::<T, M>::init(Some(tx.into()), None, config); | ||
| 150 | Self::new_inner() | ||
| 151 | } | ||
| 152 | |||
| 153 | #[inline] | ||
| 154 | fn new_inner() -> Self { | ||
| 155 | Self { phantom: PhantomData } | ||
| 156 | } | ||
| 157 | |||
| 158 | pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { | ||
| 159 | T::blocking_write(buffer) | ||
| 160 | } | ||
| 161 | |||
| 162 | pub fn blocking_flush(&mut self) -> Result<(), Error> { | ||
| 163 | T::blocking_flush() | ||
| 164 | } | ||
| 165 | |||
| 166 | pub fn tx_busy(&self) -> bool { | ||
| 167 | T::tx_busy() | ||
| 168 | } | ||
| 169 | } | ||
| 170 | |||
| 171 | impl<'d, T: Instance> UsartTx<'d, T, Blocking> { | ||
| 172 | pub fn new_blocking(_usart: Peri<'d, T>, tx: Peri<'d, impl TxPin<T>>, config: Config) -> Self { | ||
| 173 | Usart::<T, Blocking>::init(Some(tx.into()), None, config); | ||
| 174 | Self::new_inner() | ||
| 175 | } | ||
| 176 | } | ||
| 177 | |||
| 178 | impl<'d, T: Instance, M: Mode> UsartRx<'d, T, M> { | ||
| 179 | pub fn new(_usart: Peri<'d, T>, rx: Peri<'d, impl RxPin<T>>, config: Config) -> Self { | ||
| 180 | Usart::<T, M>::init(None, Some(rx.into()), config); | ||
| 181 | Self::new_inner() | ||
| 182 | } | ||
| 183 | |||
| 184 | #[inline] | ||
| 185 | fn new_inner() -> Self { | ||
| 186 | Self { phantom: PhantomData } | ||
| 187 | } | ||
| 188 | |||
| 189 | pub fn blocking_read(&mut self, mut buffer: &mut [u8]) -> Result<(), Error> { | ||
| 190 | while !buffer.is_empty() { | ||
| 191 | match Self::drain_fifo(self, buffer) { | ||
| 192 | Ok(0) => continue, // Wait for more data | ||
| 193 | Ok(n) => buffer = &mut buffer[n..], | ||
| 194 | Err((_, err)) => return Err(err), | ||
| 195 | } | ||
| 196 | } | ||
| 197 | Ok(()) | ||
| 198 | } | ||
| 199 | |||
| 200 | /// Returns: | ||
| 201 | /// - Ok(n) -> read n bytes | ||
| 202 | /// - Err(n, Error) -> read n-1 bytes, but encountered an error while reading nth byte | ||
| 203 | fn drain_fifo(&mut self, buffer: &mut [u8]) -> Result<usize, (usize, Error)> { | ||
| 204 | T::drain_fifo(buffer) | ||
| 205 | } | ||
| 206 | } | ||
| 207 | |||
| 208 | impl<'d, T: Instance> UsartRx<'d, T, Blocking> { | ||
| 209 | pub fn new_blocking(_usart: Peri<'d, T>, rx: Peri<'d, impl RxPin<T>>, config: Config) -> Self { | ||
| 210 | Usart::<T, Blocking>::init(None, Some(rx.into()), config); | ||
| 211 | Self::new_inner() | ||
| 212 | } | ||
| 213 | } | ||
| 214 | |||
| 215 | impl<'d, T: Instance> Usart<'d, T, Blocking> { | ||
| 216 | pub fn new_blocking( | ||
| 217 | usart: Peri<'d, T>, | ||
| 218 | tx: Peri<'d, impl TxPin<T>>, | ||
| 219 | rx: Peri<'d, impl RxPin<T>>, | ||
| 220 | config: Config, | ||
| 221 | ) -> Self { | ||
| 222 | Self::new_inner(usart, tx.into(), rx.into(), config) | ||
| 223 | } | ||
| 224 | } | ||
| 225 | |||
| 226 | impl<'d, T: Instance, M: Mode> Usart<'d, T, M> { | ||
| 227 | fn new_inner(_usart: Peri<'d, T>, mut tx: Peri<'d, AnyPin>, mut rx: Peri<'d, AnyPin>, config: Config) -> Self { | ||
| 228 | Self::init(Some(tx.reborrow()), Some(rx.reborrow()), config); | ||
| 229 | Self { | ||
| 230 | tx: UsartTx::new_inner(), | ||
| 231 | rx: UsartRx::new_inner(), | ||
| 232 | } | ||
| 233 | } | ||
| 234 | |||
| 235 | fn init(_tx: Option<Peri<'_, AnyPin>>, _rx: Option<Peri<'_, AnyPin>>, config: Config) { | ||
| 236 | T::enable_clock(); | ||
| 237 | T::reset_flexcomm(); | ||
| 238 | let source_clock: u32 = T::select_clock(config.baudrate); | ||
| 239 | T::configure_flexcomm(); | ||
| 240 | T::tx_pin_config(); | ||
| 241 | T::rx_pin_config(); | ||
| 242 | Self::set_baudrate(source_clock, config.baudrate); | ||
| 243 | T::configure_usart(config); | ||
| 244 | T::disable_dma(); | ||
| 245 | T::enable_usart(); | ||
| 246 | } | ||
| 247 | |||
| 248 | fn set_baudrate(source_clock: u32, baudrate: u32) { | ||
| 249 | // There are two types of dividers: integer divider (baud rate generator register and oversample selection value) | ||
| 250 | // and fractional divider (fractional rate divider). | ||
| 251 | // For oversampling, the default is industry standard 16x oversampling, i.e. OSRVAL = 15 | ||
| 252 | |||
| 253 | // The formulas are: | ||
| 254 | |||
| 255 | // FLCK = (clock selected via FCCLKSEL) / (1 + MULT / DIV) | ||
| 256 | // DIV is always 256, then: | ||
| 257 | // FLCK = (clock selected via FCCLKSEL) / (1 + MULT / 256) | ||
| 258 | |||
| 259 | // Baud rate = [FCLK / (OSRVAL+1)] / (BRGVAL + 1) => | ||
| 260 | // Baud rate = [FCLK / 16] / (BRGVAL + 1) | ||
| 261 | |||
| 262 | // There are 2 unknowns: MULT and BRGVAL. | ||
| 263 | // MULT is responsible for fractional division | ||
| 264 | // BRGVAL is responsible for integer division | ||
| 265 | |||
| 266 | // The Fractional Rate Generator can be used to obtain more precise baud rates when the | ||
| 267 | // function clock is not a good multiple of standard (or otherwise desirable) baud rates. | ||
| 268 | // The FRG is typically set up to produce an integer multiple of the highest required baud | ||
| 269 | // rate, or a very close approximation. The BRG is then used to obtain the actual baud rate | ||
| 270 | // needed. | ||
| 271 | |||
| 272 | // Firstly, BRGVAL is calculated to get the raw clock which is a rough approximation that has to be adjusted | ||
| 273 | // so that the desired baud rate is obtained. | ||
| 274 | // Secondly, MULT is calculated to ultimately 'chisel' the clock to get the baud rate. | ||
| 275 | // The deduced formulas are written below. | ||
| 276 | |||
| 277 | let brg_value = (source_clock / (16 * baudrate)).min(255); | ||
| 278 | let raw_clock = source_clock / (16 * brg_value); | ||
| 279 | let mult_value = ((raw_clock * 256 / baudrate) - 256).min(255); | ||
| 280 | T::set_baudrate(mult_value as u8, brg_value as u8); | ||
| 281 | } | ||
| 282 | } | ||
| 283 | |||
| 284 | impl<'d, T: Instance, M: Mode> Usart<'d, T, M> { | ||
| 285 | /// Transmit the provided buffer blocking execution until done. | ||
| 286 | pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { | ||
| 287 | self.tx.blocking_write(buffer) | ||
| 288 | } | ||
| 289 | |||
| 290 | /// Flush USART TX blocking execution until done. | ||
| 291 | pub fn blocking_flush(&mut self) -> Result<(), Error> { | ||
| 292 | self.tx.blocking_flush() | ||
| 293 | } | ||
| 294 | |||
| 295 | /// Read from USART RX blocking execution until done. | ||
| 296 | pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { | ||
| 297 | self.rx.blocking_read(buffer) | ||
| 298 | } | ||
| 299 | |||
| 300 | /// Check if UART is busy transmitting. | ||
| 301 | pub fn tx_busy(&self) -> bool { | ||
| 302 | self.tx.tx_busy() | ||
| 303 | } | ||
| 304 | |||
| 305 | /// Split the Usart into a transmitter and receiver, which is particularly | ||
| 306 | /// useful when having two tasks correlating to transmitting and receiving. | ||
| 307 | pub fn split(self) -> (UsartTx<'d, T, M>, UsartRx<'d, T, M>) { | ||
| 308 | (self.tx, self.rx) | ||
| 309 | } | ||
| 310 | |||
| 311 | /// Split the Usart into a transmitter and receiver by mutable reference, | ||
| 312 | /// which is particularly useful when having two tasks correlating to | ||
| 313 | /// transmitting and receiving. | ||
| 314 | pub fn split_ref(&mut self) -> (&mut UsartTx<'d, T, M>, &mut UsartRx<'d, T, M>) { | ||
| 315 | (&mut self.tx, &mut self.rx) | ||
| 316 | } | ||
| 317 | } | ||
| 318 | |||
| 319 | impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for UsartTx<'d, T, M> { | ||
| 320 | type Error = Error; | ||
| 321 | |||
| 322 | fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { | ||
| 323 | self.blocking_write(buffer) | ||
| 324 | } | ||
| 325 | |||
| 326 | fn bflush(&mut self) -> Result<(), Self::Error> { | ||
| 327 | self.blocking_flush() | ||
| 328 | } | ||
| 329 | } | ||
| 330 | |||
| 331 | impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for Usart<'d, T, M> { | ||
| 332 | type Error = Error; | ||
| 333 | |||
| 334 | fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { | ||
| 335 | self.blocking_write(buffer) | ||
| 336 | } | ||
| 337 | |||
| 338 | fn bflush(&mut self) -> Result<(), Self::Error> { | ||
| 339 | self.blocking_flush() | ||
| 340 | } | ||
| 341 | } | ||
| 342 | |||
| 343 | impl<'d, T: Instance> embedded_io::ErrorType for UsartTx<'d, T, Blocking> { | ||
| 344 | type Error = Error; | ||
| 345 | } | ||
| 346 | |||
| 347 | impl<'d, T: Instance> embedded_io::Write for UsartTx<'d, T, Blocking> { | ||
| 348 | fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> { | ||
| 349 | self.blocking_write(buf).map(|_| buf.len()) | ||
| 350 | } | ||
| 351 | |||
| 352 | fn flush(&mut self) -> Result<(), Self::Error> { | ||
| 353 | self.blocking_flush() | ||
| 354 | } | ||
| 355 | } | ||
| 356 | |||
| 357 | impl<'d, T: Instance> embedded_io::ErrorType for UsartRx<'d, T, Blocking> { | ||
| 358 | type Error = Error; | ||
| 359 | } | ||
| 360 | |||
| 361 | impl<'d, T: Instance> embedded_io::Read for UsartRx<'d, T, Blocking> { | ||
| 362 | fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> { | ||
| 363 | self.blocking_read(buf).map(|_| buf.len()) | ||
| 364 | } | ||
| 365 | } | ||
| 366 | |||
| 367 | impl<'d, T: Instance> embedded_io::ErrorType for Usart<'d, T, Blocking> { | ||
| 368 | type Error = Error; | ||
| 369 | } | ||
| 370 | |||
| 371 | impl<'d, T: Instance> embedded_io::Write for Usart<'d, T, Blocking> { | ||
| 372 | fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> { | ||
| 373 | self.blocking_write(buf).map(|_| buf.len()) | ||
| 374 | } | ||
| 375 | |||
| 376 | fn flush(&mut self) -> Result<(), Self::Error> { | ||
| 377 | self.blocking_flush() | ||
| 378 | } | ||
| 379 | } | ||
| 380 | |||
| 381 | impl<'d, T: Instance> embedded_io::Read for Usart<'d, T, Blocking> { | ||
| 382 | fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> { | ||
| 383 | self.blocking_read(buf).map(|_| buf.len()) | ||
| 384 | } | ||
| 385 | } | ||
| 386 | |||
| 387 | type UsartRegBlock = crate::pac::usart0::RegisterBlock; | ||
| 388 | |||
| 389 | mod sealed { | ||
| 390 | use crate::usart::inner::UsartRegBlock; | ||
| 391 | use crate::usart::{Config, Error}; | ||
| 392 | pub trait SealedInstance { | ||
| 393 | fn usart_reg() -> &'static UsartRegBlock; | ||
| 394 | fn enable_clock(); | ||
| 395 | fn select_clock(baudrate: u32) -> u32; | ||
| 396 | fn configure_flexcomm(); | ||
| 397 | fn set_baudrate(mult_value: u8, brg_value: u8); | ||
| 398 | fn reset_flexcomm(); | ||
| 399 | fn tx_pin_config(); | ||
| 400 | fn rx_pin_config(); | ||
| 401 | |||
| 402 | fn configure_usart(config: Config) { | ||
| 403 | // See section 34.6.1 | ||
| 404 | Self::usart_reg().cfg.modify(|_, w| { | ||
| 405 | // LIN break mode enable | ||
| 406 | w.linmode() | ||
| 407 | // Disabled. Break detect and generate is configured for normal operation. | ||
| 408 | .disabled() | ||
| 409 | //CTS Enable. Determines whether CTS is used for flow control. CTS can be from the | ||
| 410 | //input pin, or from the USART’s own RTS if loopback mode is enabled. | ||
| 411 | .ctsen() | ||
| 412 | // No flow control. The transmitter does not receive any automatic flow control signal. | ||
| 413 | .disabled() | ||
| 414 | // Selects synchronous or asynchronous operation. | ||
| 415 | .syncen() | ||
| 416 | .asynchronous_mode() | ||
| 417 | // Selects the clock polarity and sampling edge of received data in synchronous mode. | ||
| 418 | .clkpol() | ||
| 419 | .rising_edge() | ||
| 420 | // Synchronous mode Master select. | ||
| 421 | .syncmst() | ||
| 422 | // When synchronous mode is enabled, the USART is a master. | ||
| 423 | .master() | ||
| 424 | // Selects data loopback mode | ||
| 425 | .loop_() | ||
| 426 | // Normal operation | ||
| 427 | .normal() | ||
| 428 | // Output Enable Turnaround time enable for RS-485 operation. | ||
| 429 | .oeta() | ||
| 430 | // Disabled. If selected by OESEL, the Output Enable signal deasserted at the end of | ||
| 431 | // the last stop bit of a transmission. | ||
| 432 | .disabled() | ||
| 433 | // Output enable select. | ||
| 434 | .oesel() | ||
| 435 | // Standard. The RTS signal is used as the standard flow control function. | ||
| 436 | .standard() | ||
| 437 | // Automatic address matching enable. | ||
| 438 | .autoaddr() | ||
| 439 | // Disabled. When addressing is enabled by ADDRDET, address matching is done by | ||
| 440 | // software. This provides the possibility of versatile addressing (e.g. respond to more | ||
| 441 | // than one address) | ||
| 442 | .disabled() | ||
| 443 | // Output enable polarity. | ||
| 444 | .oepol() | ||
| 445 | // Low. If selected by OESEL, the output enable is active low. | ||
| 446 | .low() | ||
| 447 | }); | ||
| 448 | |||
| 449 | Self::usart_reg().cfg.modify(|_, w| unsafe { | ||
| 450 | w.datalen() | ||
| 451 | .bits(config.data_bits.bits()) | ||
| 452 | .paritysel() | ||
| 453 | .bits(config.parity.bits()) | ||
| 454 | .stoplen() | ||
| 455 | .bit(config.stop_bits.bits()) | ||
| 456 | .rxpol() | ||
| 457 | .bit(config.invert_rx) | ||
| 458 | .txpol() | ||
| 459 | .bit(config.invert_tx) | ||
| 460 | }); | ||
| 461 | } | ||
| 462 | fn disable_dma() { | ||
| 463 | Self::usart_reg() | ||
| 464 | .fifocfg | ||
| 465 | .modify(|_, w| w.dmatx().disabled().dmarx().disabled()); | ||
| 466 | } | ||
| 467 | fn enable_usart() { | ||
| 468 | Self::usart_reg() | ||
| 469 | .fifocfg | ||
| 470 | .modify(|_, w| w.enabletx().enabled().enablerx().enabled()); | ||
| 471 | Self::usart_reg().cfg.modify(|_, w| w.enable().enabled()); | ||
| 472 | while Self::usart_reg().fifostat.read().rxnotempty().bit_is_set() { | ||
| 473 | let _ = Self::usart_reg().fiford.read().bits(); | ||
| 474 | } | ||
| 475 | } | ||
| 476 | fn blocking_write(buffer: &[u8]) -> Result<(), Error> { | ||
| 477 | for &b in buffer { | ||
| 478 | while Self::usart_reg().fifostat.read().txnotfull().bit_is_clear() {} | ||
| 479 | Self::usart_reg() | ||
| 480 | .fifowr | ||
| 481 | .modify(|_, w| unsafe { w.txdata().bits(b as u16) }); | ||
| 482 | } | ||
| 483 | Ok(()) | ||
| 484 | } | ||
| 485 | fn blocking_flush() -> Result<(), Error> { | ||
| 486 | while Self::usart_reg().fifostat.read().txempty().bit_is_clear() {} | ||
| 487 | Ok(()) | ||
| 488 | } | ||
| 489 | fn tx_busy() -> bool { | ||
| 490 | Self::usart_reg().fifostat.read().txempty().bit_is_clear() | ||
| 491 | } | ||
| 492 | fn drain_fifo(buffer: &mut [u8]) -> Result<usize, (usize, Error)> { | ||
| 493 | for (i, b) in buffer.iter_mut().enumerate() { | ||
| 494 | while Self::usart_reg().fifostat.read().rxnotempty().bit_is_clear() {} | ||
| 495 | |||
| 496 | if Self::usart_reg().fifostat.read().rxerr().bit_is_set() { | ||
| 497 | return Err((i, Error::Overrun)); | ||
| 498 | } else if Self::usart_reg().fifordnopop.read().parityerr().bit_is_set() { | ||
| 499 | return Err((i, Error::Parity)); | ||
| 500 | } else if Self::usart_reg().fifordnopop.read().framerr().bit_is_set() { | ||
| 501 | return Err((i, Error::Framing)); | ||
| 502 | } else if Self::usart_reg().fifordnopop.read().rxnoise().bit_is_set() { | ||
| 503 | return Err((i, Error::Noise)); | ||
| 504 | } else if Self::usart_reg().intstat.read().deltarxbrk().bit_is_set() { | ||
| 505 | return Err((i, Error::Break)); | ||
| 506 | } | ||
| 507 | let dr = Self::usart_reg().fiford.read().bits() as u8; | ||
| 508 | *b = dr; | ||
| 509 | } | ||
| 510 | Ok(buffer.len()) | ||
| 511 | } | ||
| 512 | } | ||
| 513 | } | ||
| 514 | |||
| 515 | /// UART instance. | ||
| 516 | #[allow(private_bounds)] | ||
| 517 | pub trait Instance: sealed::SealedInstance + PeripheralType {} | ||
| 518 | |||
| 519 | #[macro_export] | ||
| 520 | macro_rules! impl_instance { | ||
| 521 | ( | ||
| 522 | $inst:ident, | ||
| 523 | usart_peripheral: $USARTX:ident, | ||
| 524 | usart_crate: $usartX:ident, | ||
| 525 | |||
| 526 | flexcomm: { | ||
| 527 | field: $FLEXCOMM_FIELD:ident, | ||
| 528 | clock_field: $FLEXCOMM_CLK_FIELD:ident | ||
| 529 | }, | ||
| 530 | |||
| 531 | reset: { | ||
| 532 | bit: $RESET_BIT:ident | ||
| 533 | }, | ||
| 534 | |||
| 535 | clock: { | ||
| 536 | sel_field: $CLKSEL_FIELD:ident, | ||
| 537 | frg_field: $FRG_FIELD:ident | ||
| 538 | }, | ||
| 539 | |||
| 540 | pins: { | ||
| 541 | tx: $TX_IOCON:ident => $TX_FUNC:expr, | ||
| 542 | rx: $RX_IOCON:ident => $RX_FUNC:expr | ||
| 543 | } | ||
| 544 | |||
| 545 | ) => { | ||
| 546 | impl $crate::usart::SealedInstance for $crate::peripherals::$inst { | ||
| 547 | fn usart_reg() -> &'static UsartRegBlock { | ||
| 548 | unsafe { &*$crate::pac::$USARTX::ptr() } | ||
| 549 | } | ||
| 550 | |||
| 551 | fn enable_clock() { | ||
| 552 | critical_section::with(|_cs| { | ||
| 553 | if syscon_reg().ahbclkctrl0.read().iocon().is_disable() { | ||
| 554 | syscon_reg().ahbclkctrl0.modify(|_, w| w.iocon().enable()); | ||
| 555 | } | ||
| 556 | if syscon_reg().ahbclkctrl1.read().$FLEXCOMM_CLK_FIELD().is_disable() { | ||
| 557 | syscon_reg() | ||
| 558 | .ahbclkctrl1 | ||
| 559 | .modify(|_, w| w.$FLEXCOMM_CLK_FIELD().enable()); | ||
| 560 | } | ||
| 561 | }); | ||
| 562 | } | ||
| 563 | |||
| 564 | fn configure_flexcomm() { | ||
| 565 | let flexcomm = unsafe { &*$crate::pac::$FLEXCOMM_FIELD::ptr() }; | ||
| 566 | flexcomm.pselid.modify(|_, w| w.persel().usart()); | ||
| 567 | } | ||
| 568 | |||
| 569 | fn reset_flexcomm() { | ||
| 570 | syscon_reg().presetctrl1.modify(|_, w| w.$RESET_BIT().set_bit()); | ||
| 571 | syscon_reg().presetctrl1.modify(|_, w| w.$RESET_BIT().clear_bit()); | ||
| 572 | } | ||
| 573 | |||
| 574 | fn select_clock(baudrate: u32) -> u32 { | ||
| 575 | // Adaptive clock choice based on baud rate | ||
| 576 | // To get the desired baud rate, it is essential to choose the clock bigger than baud rate so that it can be 'chiseled' | ||
| 577 | // There are two types of dividers: integer divider (baud rate generator register and oversample selection value) | ||
| 578 | // and fractional divider (fractional rate divider). | ||
| 579 | |||
| 580 | // By default, oversampling rate is 16 which is an industry standard. | ||
| 581 | // That means 16 clocks are used to deliver the byte to recipient. | ||
| 582 | // In this way the probability of getting correct bytes instead of noise directly increases as oversampling increases as well. | ||
| 583 | |||
| 584 | // Minimum and maximum values were computed taking these formulas into account: | ||
| 585 | // For minimum value, MULT = 0, BRGVAL = 0 | ||
| 586 | // For maximum value, MULT = 255, BRGVAL = 255 | ||
| 587 | // Flexcomm Interface function clock = (clock selected via FCCLKSEL) / (1 + MULT / DIV) | ||
| 588 | // By default, OSRVAL = 15 (see above) | ||
| 589 | // Baud rate = [FCLK / (OSRVAL+1)] / (BRGVAL + 1) | ||
| 590 | return match baudrate { | ||
| 591 | 750_001..=6000000 => { | ||
| 592 | syscon_reg().$CLKSEL_FIELD().write(|w| w.sel().enum_0x3()); // 96 MHz | ||
| 593 | 96_000_000 | ||
| 594 | } | ||
| 595 | 1501..=750_000 => { | ||
| 596 | syscon_reg().$CLKSEL_FIELD().write(|w| w.sel().enum_0x2()); // 12 MHz | ||
| 597 | 12_000_000 | ||
| 598 | } | ||
| 599 | 121..=1500 => { | ||
| 600 | syscon_reg().$CLKSEL_FIELD().write(|w| w.sel().enum_0x4()); // 1 MHz | ||
| 601 | 1_000_000 | ||
| 602 | } | ||
| 603 | _ => { | ||
| 604 | panic!("{} baudrate is not permitted in this mode", baudrate); | ||
| 605 | } | ||
| 606 | }; | ||
| 607 | } | ||
| 608 | |||
| 609 | fn set_baudrate(mult_value: u8, brg_value: u8) { | ||
| 610 | // FCLK = (clock selected via FCCLKSEL) / (1+ MULT / DIV) | ||
| 611 | // Remark: To use the fractional baud rate generator, 0xFF must be wirtten to the DIV value | ||
| 612 | // to yield a denominator vale of 256. All other values are not supported | ||
| 613 | syscon_reg() | ||
| 614 | .$FRG_FIELD() | ||
| 615 | .modify(|_, w| unsafe { w.div().bits(0xFF).mult().bits(mult_value as u8) }); | ||
| 616 | |||
| 617 | // Baud rate = [FCLK / (OSRVAL+1)] / (BRGVAL + 1) | ||
| 618 | // By default, oversampling is 16x, i.e. OSRVAL = 15 | ||
| 619 | |||
| 620 | // Typical industry standard USARTs use a 16x oversample clock to transmit and receive | ||
| 621 | // asynchronous data. This is the number of BRG clocks used for one data bit. The | ||
| 622 | // Oversample Select Register (OSR) allows this USART to use a 16x down to a 5x | ||
| 623 | // oversample clock. There is no oversampling in synchronous modes. | ||
| 624 | Self::usart_reg() | ||
| 625 | .brg | ||
| 626 | .modify(|_, w| unsafe { w.brgval().bits((brg_value - 1) as u16) }); | ||
| 627 | } | ||
| 628 | |||
| 629 | fn tx_pin_config() { | ||
| 630 | iocon_reg().$TX_IOCON.modify(|_, w| unsafe { | ||
| 631 | w.func() | ||
| 632 | .bits($TX_FUNC) | ||
| 633 | .digimode() | ||
| 634 | .digital() | ||
| 635 | .slew() | ||
| 636 | .standard() | ||
| 637 | .mode() | ||
| 638 | .inactive() | ||
| 639 | .invert() | ||
| 640 | .disabled() | ||
| 641 | .od() | ||
| 642 | .normal() | ||
| 643 | }); | ||
| 644 | } | ||
| 645 | |||
| 646 | fn rx_pin_config() { | ||
| 647 | iocon_reg().$RX_IOCON.modify(|_, w| unsafe { | ||
| 648 | w.func() | ||
| 649 | .bits($RX_FUNC) | ||
| 650 | .digimode() | ||
| 651 | .digital() | ||
| 652 | .slew() | ||
| 653 | .standard() | ||
| 654 | .mode() | ||
| 655 | .inactive() | ||
| 656 | .invert() | ||
| 657 | .disabled() | ||
| 658 | .od() | ||
| 659 | .normal() | ||
| 660 | }); | ||
| 661 | } | ||
| 662 | } | ||
| 663 | |||
| 664 | impl $crate::usart::Instance for $crate::peripherals::$inst {} | ||
| 665 | }; | ||
| 666 | } | ||
| 667 | |||
| 668 | impl_instance!(USART0, usart_peripheral: USART0, usart_crate: usart0, | ||
| 669 | flexcomm: { | ||
| 670 | field: FLEXCOMM0, | ||
| 671 | clock_field: fc0 | ||
| 672 | }, | ||
| 673 | |||
| 674 | reset: { | ||
| 675 | bit: fc0_rst | ||
| 676 | }, | ||
| 677 | |||
| 678 | clock: { | ||
| 679 | sel_field: fcclksel0, | ||
| 680 | frg_field: flexfrg0ctrl | ||
| 681 | }, | ||
| 682 | |||
| 683 | pins: { | ||
| 684 | tx: pio1_6 => 1, | ||
| 685 | rx: pio1_5 => 1 | ||
| 686 | } | ||
| 687 | ); | ||
| 688 | |||
| 689 | impl_instance!(USART1, usart_peripheral: USART1, usart_crate: usart1, | ||
| 690 | flexcomm: { | ||
| 691 | field: FLEXCOMM1, | ||
| 692 | clock_field: fc1 | ||
| 693 | }, | ||
| 694 | |||
| 695 | reset: { | ||
| 696 | bit: fc1_rst | ||
| 697 | }, | ||
| 698 | |||
| 699 | clock: { | ||
| 700 | sel_field: fcclksel1, | ||
| 701 | frg_field: flexfrg1ctrl | ||
| 702 | }, | ||
| 703 | |||
| 704 | pins: { | ||
| 705 | tx: pio1_11 => 2, | ||
| 706 | rx: pio1_10 => 2 | ||
| 707 | } | ||
| 708 | ); | ||
| 709 | |||
| 710 | impl_instance!(USART2, usart_peripheral: USART2, usart_crate: usart2, | ||
| 711 | flexcomm: { | ||
| 712 | field: FLEXCOMM2, | ||
| 713 | clock_field: fc2 | ||
| 714 | }, | ||
| 715 | |||
| 716 | reset: { | ||
| 717 | bit: fc2_rst | ||
| 718 | }, | ||
| 719 | |||
| 720 | clock: { | ||
| 721 | sel_field: fcclksel2, | ||
| 722 | frg_field: flexfrg2ctrl | ||
| 723 | }, | ||
| 724 | |||
| 725 | pins: { | ||
| 726 | tx: pio0_27 => 1, | ||
| 727 | rx: pio1_24 => 1 | ||
| 728 | } | ||
| 729 | ); | ||
| 730 | |||
| 731 | impl_instance!(USART3, usart_peripheral: USART3, usart_crate: usart3, | ||
| 732 | flexcomm: { | ||
| 733 | field: FLEXCOMM3, | ||
| 734 | clock_field: fc3 | ||
| 735 | }, | ||
| 736 | |||
| 737 | reset: { | ||
| 738 | bit: fc3_rst | ||
| 739 | }, | ||
| 740 | |||
| 741 | clock: { | ||
| 742 | sel_field: fcclksel3, | ||
| 743 | frg_field: flexfrg3ctrl | ||
| 744 | }, | ||
| 745 | |||
| 746 | pins: { | ||
| 747 | tx: pio0_2 => 1, | ||
| 748 | rx: pio0_3 => 1 | ||
| 749 | } | ||
| 750 | ); | ||
| 751 | |||
| 752 | impl_instance!(USART4, usart_peripheral: USART4, usart_crate: usart4, | ||
| 753 | flexcomm: { | ||
| 754 | field: FLEXCOMM4, | ||
| 755 | clock_field: fc4 | ||
| 756 | }, | ||
| 757 | |||
| 758 | reset: { | ||
| 759 | bit: fc4_rst | ||
| 760 | }, | ||
| 761 | |||
| 762 | clock: { | ||
| 763 | sel_field: fcclksel4, | ||
| 764 | frg_field: flexfrg4ctrl | ||
| 765 | }, | ||
| 766 | |||
| 767 | pins: { | ||
| 768 | tx: pio0_16 => 1, | ||
| 769 | rx: pio0_5 => 2 | ||
| 770 | } | ||
| 771 | ); | ||
| 772 | |||
| 773 | impl_instance!(USART5, usart_peripheral: USART5, usart_crate: usart5, | ||
| 774 | flexcomm: { | ||
| 775 | field: FLEXCOMM5, | ||
| 776 | clock_field: fc5 | ||
| 777 | }, | ||
| 778 | |||
| 779 | reset: { | ||
| 780 | bit: fc5_rst | ||
| 781 | }, | ||
| 782 | |||
| 783 | clock: { | ||
| 784 | sel_field: fcclksel5, | ||
| 785 | frg_field: flexfrg5ctrl | ||
| 786 | }, | ||
| 787 | |||
| 788 | pins: { | ||
| 789 | tx: pio0_9 => 3, | ||
| 790 | rx: pio0_8 => 3 | ||
| 791 | } | ||
| 792 | ); | ||
| 793 | |||
| 794 | impl_instance!(USART6, usart_peripheral: USART6, usart_crate: usart6, | ||
| 795 | flexcomm: { | ||
| 796 | field: FLEXCOMM6, | ||
| 797 | clock_field: fc6 | ||
| 798 | }, | ||
| 799 | |||
| 800 | reset: { | ||
| 801 | bit: fc6_rst | ||
| 802 | }, | ||
| 803 | |||
| 804 | clock: { | ||
| 805 | sel_field: fcclksel6, | ||
| 806 | frg_field: flexfrg6ctrl | ||
| 807 | }, | ||
| 808 | |||
| 809 | pins: { | ||
| 810 | tx: pio1_16 => 2, | ||
| 811 | rx: pio1_13 => 2 | ||
| 812 | } | ||
| 813 | ); | ||
| 814 | |||
| 815 | impl_instance!(USART7, usart_peripheral: USART7, usart_crate: usart7, | ||
| 816 | flexcomm: { | ||
| 817 | field: FLEXCOMM7, | ||
| 818 | clock_field: fc7 | ||
| 819 | }, | ||
| 820 | |||
| 821 | reset: { | ||
| 822 | bit: fc7_rst | ||
| 823 | }, | ||
| 824 | |||
| 825 | clock: { | ||
| 826 | sel_field: fcclksel7, | ||
| 827 | frg_field: flexfrg7ctrl | ||
| 828 | }, | ||
| 829 | |||
| 830 | pins: { | ||
| 831 | tx: pio0_19 => 7, | ||
| 832 | rx: pio0_20 => 7 | ||
| 833 | } | ||
| 834 | ); | ||
| 835 | |||
| 836 | /// Trait for TX pins. | ||
| 837 | pub trait TxPin<T: Instance>: crate::gpio::Pin {} | ||
| 838 | /// Trait for RX pins. | ||
| 839 | pub trait RxPin<T: Instance>: crate::gpio::Pin {} | ||
| 840 | |||
| 841 | // TODO: Add RTS, CTS and CLK pin traits | ||
| 842 | |||
| 843 | macro_rules! impl_pin { | ||
| 844 | ($pin:ident, $instance:ident, Tx) => { | ||
| 845 | impl TxPin<crate::peripherals::$instance> for crate::peripherals::$pin {} | ||
| 846 | }; | ||
| 847 | ($pin:ident, $instance:ident, Rx) => { | ||
| 848 | impl RxPin<crate::peripherals::$instance> for crate::peripherals::$pin {} | ||
| 849 | }; | ||
| 850 | } | ||
| 851 | |||
| 852 | impl_pin!(PIO1_5, USART0, Rx); | ||
| 853 | impl_pin!(PIO1_6, USART0, Tx); | ||
| 854 | impl_pin!(PIO1_10, USART1, Rx); | ||
| 855 | impl_pin!(PIO1_11, USART1, Tx); | ||
| 856 | impl_pin!(PIO0_27, USART2, Tx); | ||
| 857 | impl_pin!(PIO1_24, USART2, Rx); | ||
| 858 | impl_pin!(PIO0_2, USART3, Tx); | ||
| 859 | impl_pin!(PIO0_3, USART3, Rx); | ||
| 860 | impl_pin!(PIO0_16, USART4, Tx); | ||
| 861 | impl_pin!(PIO0_5, USART4, Rx); | ||
| 862 | impl_pin!(PIO0_8, USART5, Rx); | ||
| 863 | impl_pin!(PIO0_9, USART5, Tx); | ||
| 864 | impl_pin!(PIO1_16, USART6, Tx); | ||
| 865 | impl_pin!(PIO1_13, USART6, Rx); | ||
| 866 | impl_pin!(PIO0_20, USART7, Rx); | ||
| 867 | impl_pin!(PIO0_19, USART7, Tx); | ||
| 868 | |||
| 869 | /// Get the SYSCON register block. | ||
| 870 | /// | ||
| 871 | /// # Safety | ||
| 872 | /// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO | ||
| 873 | /// registers are not accessed concurrently by multiple threads. | ||
| 874 | pub(crate) fn syscon_reg() -> &'static crate::pac::syscon::RegisterBlock { | ||
| 875 | unsafe { &*crate::pac::SYSCON::ptr() } | ||
| 876 | } | ||
| 877 | |||
| 878 | /// Get the IOCON register block. | ||
| 879 | /// | ||
| 880 | /// # Safety | ||
| 881 | /// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO | ||
| 882 | /// registers are not accessed concurrently by multiple threads. | ||
| 883 | pub(crate) fn iocon_reg() -> &'static crate::pac::iocon::RegisterBlock { | ||
| 884 | unsafe { &*crate::pac::IOCON::ptr() } | ||
| 885 | } | ||
diff --git a/examples/lpc55s69/src/bin/usart_blocking.rs b/examples/lpc55s69/src/bin/usart_blocking.rs new file mode 100644 index 000000000..a38ec0c5b --- /dev/null +++ b/examples/lpc55s69/src/bin/usart_blocking.rs | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use core::str::from_utf8_mut; | ||
| 5 | |||
| 6 | use defmt::*; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_nxp::usart::{Config, Usart}; | ||
| 9 | use embassy_time::Timer; | ||
| 10 | use {defmt_rtt as _, panic_halt as _}; | ||
| 11 | |||
| 12 | #[embassy_executor::main] | ||
| 13 | async fn main(_spawner: Spawner) { | ||
| 14 | let p = embassy_nxp::init(Default::default()); | ||
| 15 | let mut usart = Usart::new_blocking(p.USART2, p.PIO0_27, p.PIO1_24, Config::default()); | ||
| 16 | let tx_buf = b"Hello, Ferris!"; | ||
| 17 | let mut rx_buf = [0u8; 14]; | ||
| 18 | |||
| 19 | loop { | ||
| 20 | info!("Write a message"); | ||
| 21 | usart.blocking_write(tx_buf).unwrap(); | ||
| 22 | usart.blocking_flush().unwrap(); | ||
| 23 | |||
| 24 | Timer::after_millis(500).await; | ||
| 25 | |||
| 26 | info!("Read a message"); | ||
| 27 | usart.blocking_read(&mut rx_buf).unwrap(); | ||
| 28 | |||
| 29 | match from_utf8_mut(&mut rx_buf) { | ||
| 30 | Ok(str) => { | ||
| 31 | info!("The message is: {}", str); | ||
| 32 | } | ||
| 33 | Err(_) => { | ||
| 34 | error!("Error in converting to UTF8"); | ||
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 38 | Timer::after_millis(500).await; | ||
| 39 | } | ||
| 40 | } | ||
