From 17995a0263cbbb8c2fdb46ee4a698e41769cf88c Mon Sep 17 00:00:00 2001 From: Kilian Barning Date: Wed, 1 Feb 2023 03:45:13 +0100 Subject: [PATCH 01/41] Added uart --- src/lib.rs | 3 + src/uart.rs | 319 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 322 insertions(+) create mode 100644 src/uart.rs diff --git a/src/lib.rs b/src/lib.rs index 75d43372..a506af8e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -166,3 +166,6 @@ pub mod interrupt; pub mod main; pub mod led; + +#[cfg(riot_module_periph_uart)] +pub mod uart; diff --git a/src/uart.rs b/src/uart.rs new file mode 100644 index 00000000..524059aa --- /dev/null +++ b/src/uart.rs @@ -0,0 +1,319 @@ +#![allow(dead_code)] + +use core::ptr; +use riot_sys::libc::{c_int, c_void}; +use riot_sys::*; + +/// This struct contains the `UART` device and handles all operation regarding it +pub struct UartDevice { + dev: uart_t, +} + +/// This enum representatives the status returned by various `UART`-functions +#[derive(Debug, Eq, PartialEq)] +pub enum UartDeviceStatus { + Success, + InvalidDevice, + UnsupportedConfig, + Other, +} + +impl UartDeviceStatus { + /// Converts the given `c_int` into the matching Enum representation + fn from_c_int(n: c_int) -> Self { + const _ENODEV: c_int = -(ENODEV as c_int); + const _ENOTSUP: c_int = -(ENOTSUP as c_int); + match n { + 0 => Self::Success, + _ENODEV => Self::InvalidDevice, + _ENOTSUP => Self::UnsupportedConfig, + _ => Self::Other, + } + } +} + +impl UartDevice { + /// Tries to initialize the given `UART`. Returns a Result with rather `Ok` if the UART was initialized successfully or a + /// `Err` containing the error + /// + /// # Arguments + /// + /// * `dev` - The uart_t handle to the hardware device + /// * `baud` - The used baud rate + /// * `user_callback` The user defined callback that gets called from the os whenever new data is received from the `UART`. More about it is described below + /// + /// # Safety + /// The unsafe here is necessary because we call a `C`-function which is inherently unsafe. + /// We have to cast the given closure reference into a `void*`. This is called `type-erasure` and + /// it is necessary because `C` has no build in functions for such complex mechanisms. + /// We also give the callback the generic parameter `F` which contains the unique signature + /// of the closure. This is very imported because only with this we can cast it back later + /// (as described below). + /// + /// Also we check if the device is initialized successfully and return a rust-idiomatic + /// Result as described above + /// + /// + /// # Examples + /// ``` + /// let mut received_data = 0u8; + /// let mut uart = UartDevice::new(uart_type_t_STM32_USART, 115200, &mut |data| { + /// received_data = data; + /// }) + /// .unwrap_or_else(|e| panic!("Error initializing UART: {e:?}")); + /// uart.write(b"Hello from UART"); + /// ``` + pub fn new(dev: uart_t, baud: u32, user_callback: &mut F) -> Result + where + F: FnMut(u8), //FnMut(u8) is a closure that can capture the environment. It has one argument of type u8 which will be the received data from the UART + { + unsafe { + match UartDeviceStatus::from_c_int(uart_init( + dev, + baud, + Some(Self::callback::), // Here we pass in our callback + user_callback as *mut _ as *mut c_void, // Here we erase the type of the closure by casting it's reference into a void* + )) { + UartDeviceStatus::Success => Ok(Self { dev }), + status => Err(status), + } + } + } + + /// Tries to initialize the given `UART`. Returns a Result with rather `Ok` if the UART was initialized successfully or a + /// `Err` containing the error. As the name implies, the created `UART` device can ONLY send data + /// + /// # Arguments + /// + /// * `dev` - The uart_t handle to the hardware device + /// * `baud` - The used baud rate + /// + /// # Safety + /// The unsafe here is necessary because we call a `C`-function which is inherently unsafe. + /// In this case it is safe because we check if the device is initialized successfully and + /// return a rust-idiomatic Result as described above + /// + /// # Examples + /// ``` + /// let mut received_data = 0u8; + /// let mut uart = UartDevice::new_without_rx(uart_type_t_STM32_USART, 115200) + /// .unwrap_or_else(|e| panic!("Error initializing UART: {e:?}")); + /// uart.write(b"Hello from UART"); + /// ``` + pub fn new_without_rx(dev: uart_t, baud: u32) -> Result { + unsafe { + match UartDeviceStatus::from_c_int(uart_init(dev, baud, None, ptr::null_mut())) { + UartDeviceStatus::Success => Ok(Self { dev }), + status => Err(status), + } + } + } + + /// Sets the mode according to the given parameters + /// Should the parameters be invalid, the function returns a Err + /// # Arguments + /// * `data_bits` - Number of data bits in a UART frame + /// * `parity` - Parity mode + /// * `stop_bits` - Number of stop bits in a UART frame + /// + /// # Examples + /// ``` + /// let mut received_data = 0u8; + /// let mut uart = UartDevice::new_without_rx(uart_type_t_STM32_USART, 115200) + /// .unwrap_or_else(|e| panic!("Error initializing UART: {e:?}")); + /// uart.set_mode( + /// riot_sys::uart_data_bits_t_UART_DATA_BITS_8, + /// riot_sys::uart_parity_t_UART_PARITY_NONE, + /// uart_stop_bits_t_UART_STOP_BITS_1, + /// ).unwrap_or_else(|e| panic!("Error setting UART mode: {e:?}")); + /// ``` + pub fn set_mode( + &mut self, + data_bits: uart_data_bits_t, + parity: uart_parity_t, + stop_bits: uart_stop_bits_t, + ) -> Result<(), UartDeviceStatus> { + unsafe { + match UartDeviceStatus::from_c_int(uart_mode(self.dev, data_bits, parity, stop_bits)) { + UartDeviceStatus::Success => Ok(()), + status => Err(status), + } + } + } + + /// Transmits the given data via the `UART`-device + /// # Safety + /// The unsafe here is necessary because we call a `C`-function which is inherently unsafe. + /// In this case it is safe because the rust compiler guarantees, that the slice pointing to the + /// data is always valid + /// + /// # Examples + /// ``` + /// let mut uart = UartDevice::new_without_rx(uart_type_t_STM32_USART, 115200) + /// .unwrap_or_else(|e| panic!("Error initializing UART: {e:?}")); + /// uart.write(b"Hello from UART\n"); + pub fn write(&mut self, data: &[u8]) { + unsafe { + uart_write(self.dev, data.as_ptr(), data.len() as size_t); + } + } + + /// The function turns off the power from the `UART-Device` + /// # Safety + /// The unsafe here is necessary because we call a `C`-function which is inherently unsafe. + /// In this case it safe because we pass no parameters to the function + pub fn power_off(&mut self) { + unsafe { uart_poweroff(self.dev) }; + } + + /// The function turns on the power from the `UART-Device` + /// # Safety + /// The unsafe here is necessary because we call a `C`-function which is inherently unsafe. + /// In this case it safe because we pass no parameters to the function + pub fn power_on(&mut self) { + unsafe { uart_poweron(self.dev) }; + } + + /// Disables collision detection check of the given UART device + /// + /// # Safety + /// The unsafe here is necessary because we call a `C`-function which is inherently unsafe. + /// In this case it's safe because we pass no parameters to the function + #[cfg(riot_module_periph_uart_rxstart_irq)] + pub fn collision_detect_disable(&mut self) { + unsafe { uart_collision_detect_disable(self.dev) }; + } + + /// Enables collision detection check of the given UART device + /// + /// # Safety + /// The unsafe here is necessary because we call a `C`-function which is inherently unsafe. + /// In this case it's safe because we pass no parameters to the function + #[cfg(riot_module_periph_uart_rxstart_irq)] + pub fn collision_detect_enable(&mut self) { + unsafe { uart_collision_detect_enable(self.dev) }; + } + + /// Disables collision detection of the given UART device + /// and return true if collision occurred during last transfer + /// + /// # Safety + /// The unsafe here is necessary because we call a `C`-function which is inherently unsafe. + /// In this case it's safe because we pass no parameters to the function + #[cfg(riot_module_periph_uart_rxstart_irq)] + pub fn collision_detected(&mut self) -> bool { + unsafe { uart_collision_detected(self.dev) } + } + + /// Change the pins of the given UART back to plain GPIO functionality. + /// + /// # Safety + /// The unsafe here is necessary because we call a `C`-function which is inherently unsafe. + /// In this case it's safe because we pass no parameters to the function + #[cfg(riot_module_periph_uart_reconfigure)] + pub fn deinit_pins(&mut self) { + unsafe { uart_deinit_pins(self.dev) }; + } + + + /// Get the RX pin of the given UART device + /// + /// # Safety + /// The unsafe here is necessary because we call a `C`-function which is inherently unsafe. + /// In this case it's safe because we pass no parameters to the function + #[cfg(riot_module_periph_uart_reconfigure)] + pub fn get_pin_rx(&mut self) { + unsafe { uart_pin_rx(self.dev) }; + } + + /// Get the TX pin of the given UART device + /// + /// # Safety + /// The unsafe here is necessary because we call a `C`-function which is inherently unsafe. + /// In this case it's safe because we pass no parameters to the function + #[cfg(riot_module_periph_uart_reconfigure)] + pub fn get_pin_tx(&mut self) { + unsafe { uart_pin_tx(self.dev) }; + } + + /// Configure the function that will be called when a start condition is detected. + /// + /// # Arguments + /// * `dev` - The uart_t handle to the hardware device + /// * `user_fxopt` - The user defined callback function called when a start condition is detected + /// + /// # Safety + /// The unsafe here is necessary because we call a `C`-function which is inherently unsafe. + /// In this case it's safe because we pass no parameters to the function + #[cfg(riot_module_periph_uart_rxstart_irq)] + pub fn rxstart_irq_configure(dev: uart_t, user_fxopt: &mut F) + where + F: FnMut(u8), + { + unsafe { + uart_rxstart_irq_configure( + dev, + Self::callback::, + user_fxopt as *mut _ as *mut c_void, + ) + }; + } + + /// Enable the RX start interrupt + /// + /// # Safety + /// The unsafe here is necessary because we call a `C`-function which is inherently unsafe. + /// In this case it's safe because we pass no parameters to the function + #[cfg(riot_module_riot_module_periph_uart_rxstart_irq)] + pub fn rxstart_irq_enable(&mut self) { + unsafe { uart_rxstart_irq_enable(self.dev) }; + } + + /// Disable the RX start interrupt + /// + /// # Safety + /// The unsafe here is necessary because we call a `C`-function which is inherently unsafe. + /// In this case it's safe because we pass no parameters to the function + #[cfg(riot_module_riot_module_periph_uart_rxstart_irq)] + pub fn rxstart_irq_disable(&mut self) { + unsafe { uart_rxstart_irq_disable(self.dev) }; + } + + /// This is the callback that gets called directly from the kernel + /// # Safety + /// It is safe for us to cast the void* back to the user-defined closure, + /// because it's signature type `F` gets passed to us from the callers side. Therefore, the + /// signature and the captured variables are pointing to the exact same addresses + /// from the stack-frame that owns the device + /// + /// # Arguments + /// * `user_callback` - The address pointing to the user defined callback + /// * `data` - The newly received data from the `UART` + unsafe extern "C" fn callback(user_callback: *mut c_void, data: u8) + where + F: FnMut(u8), + { + (*(user_callback as *mut F))(data); //We cast the void* back to the closure and call it + } +} + +impl Drop for UartDevice { + /// The `drop` method resets the uart to 9600 baud and removes the user defined callback + /// # Safety + /// At this moment it is unclear if this implementation is the right way to go. There might + /// be a better solution... + /// Also if the build is in debug mode and the `UART` is reinitialized unsuccessfully, the code panics which + /// is definitely NOT the right behavior at this point! + fn drop(&mut self) { + unsafe { + let status = + UartDeviceStatus::from_c_int(uart_init(self.dev, 9600, None, ptr::null_mut())); + debug_assert_eq!( + status, + UartDeviceStatus::Success, + "Error while deinitializing UART: {status:?}" + ); + self.power_off(); + } + } +} From dd1372af996ebb53187e8d2f75fe212fbda5f62d Mon Sep 17 00:00:00 2001 From: Kilian Barning Date: Wed, 1 Feb 2023 04:48:56 +0100 Subject: [PATCH 02/41] Fixed callback --- src/uart.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/uart.rs b/src/uart.rs index 524059aa..77961d10 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -71,7 +71,7 @@ impl UartDevice { match UartDeviceStatus::from_c_int(uart_init( dev, baud, - Some(Self::callback::), // Here we pass in our callback + Some(Self::new_data_callback::), // Here we pass in our callback user_callback as *mut _ as *mut c_void, // Here we erase the type of the closure by casting it's reference into a void* )) { UartDeviceStatus::Success => Ok(Self { dev }), @@ -253,7 +253,7 @@ impl UartDevice { unsafe { uart_rxstart_irq_configure( dev, - Self::callback::, + Self::rxstart_callback::, user_fxopt as *mut _ as *mut c_void, ) }; @@ -289,12 +289,19 @@ impl UartDevice { /// # Arguments /// * `user_callback` - The address pointing to the user defined callback /// * `data` - The newly received data from the `UART` - unsafe extern "C" fn callback(user_callback: *mut c_void, data: u8) + unsafe extern "C" fn new_data_callback(user_callback: *mut c_void, data: u8) where F: FnMut(u8), { (*(user_callback as *mut F))(data); //We cast the void* back to the closure and call it } + + unsafe extern "C" fn rxstart_callback(user_callback: *mut c_void) + where + F: FnMut(u8), + { + (*(user_callback as *mut F))(); + } } impl Drop for UartDevice { From 5e3c472253ead34ae797b5ce3705593b3877c6db Mon Sep 17 00:00:00 2001 From: Kilian Barning Date: Mon, 6 Feb 2023 02:33:08 +0100 Subject: [PATCH 03/41] Fix code according to @jaschman --- src/lib.rs | 6 +- src/uart.rs | 214 ++++++++++++++++++++++++++-------------------------- 2 files changed, 112 insertions(+), 108 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a506af8e..73e5e49d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -114,6 +114,9 @@ pub mod msg; #[cfg(riot_module_periph_spi)] pub mod spi; +#[cfg(riot_module_periph_uart)] +pub mod uart; + #[cfg(riot_module_periph_adc)] pub mod adc; @@ -166,6 +169,3 @@ pub mod interrupt; pub mod main; pub mod led; - -#[cfg(riot_module_periph_uart)] -pub mod uart; diff --git a/src/uart.rs b/src/uart.rs index 77961d10..16f9bb52 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -1,37 +1,98 @@ #![allow(dead_code)] use core::ptr; -use riot_sys::libc::{c_int, c_void}; +use riot_sys::libc::{c_int, c_uint, c_void}; use riot_sys::*; /// This struct contains the `UART` device and handles all operation regarding it +/// +/// [UART implementation]: https://doc.riot-os.org/group__drivers__periph__uart.html +#[derive(Debug)] pub struct UartDevice { dev: uart_t, } /// This enum representatives the status returned by various `UART`-functions #[derive(Debug, Eq, PartialEq)] +#[non_exhaustive] pub enum UartDeviceStatus { Success, InvalidDevice, UnsupportedConfig, - Other, + Other(i32), } impl UartDeviceStatus { /// Converts the given `c_int` into the matching Enum representation - fn from_c_int(n: c_int) -> Self { + fn from_c(n: c_int) -> Self { const _ENODEV: c_int = -(ENODEV as c_int); const _ENOTSUP: c_int = -(ENOTSUP as c_int); match n { 0 => Self::Success, _ENODEV => Self::InvalidDevice, _ENOTSUP => Self::UnsupportedConfig, - _ => Self::Other, + other => Self::Other(other), } } } +#[derive(Debug, Eq, PartialEq)] +pub enum DataBits { + Five, + Six, + Seven, + Eight, +} + +impl DataBits { + fn to_c(self) -> uart_data_bits_t { + match self { + Self::Five => uart_data_bits_t_UART_DATA_BITS_5, + Self::Six => uart_data_bits_t_UART_DATA_BITS_6, + Self::Seven => uart_data_bits_t_UART_DATA_BITS_7, + Self::Eight => uart_data_bits_t_UART_DATA_BITS_8, + } + } +} + +#[derive(Debug, Eq, PartialEq)] +pub enum Parity { + None, + Even, + Odd, + Mark, + Space, +} + +impl Parity { + fn to_c(self) -> uart_parity_t { + match self { + Self::None => uart_parity_t_UART_PARITY_NONE, + Self::Even => uart_parity_t_UART_PARITY_EVEN, + Self::Odd => uart_parity_t_UART_PARITY_ODD, + Self::Mark => uart_parity_t_UART_PARITY_ODD, + Self::Space => uart_parity_t_UART_PARITY_SPACE, + } + } +} + + +#[derive(Debug, Eq, PartialEq)] +pub enum StopBits { + One, + Two, +} + +impl StopBits { + fn to_c(self) -> uart_stop_bits_t { + match self { + Self::One => uart_stop_bits_t_UART_STOP_BITS_1, + Self::Two => uart_stop_bits_t_UART_STOP_BITS_2, + } + } +} + + impl UartDevice { /// Tries to initialize the given `UART`. Returns a Result with rather `Ok` if the UART was initialized successfully or a /// `Err` containing the error @@ -40,19 +101,7 @@ impl UartDevice { /// /// * `dev` - The uart_t handle to the hardware device /// * `baud` - The used baud rate - /// * `user_callback` The user defined callback that gets called from the os whenever new data is received from the `UART`. More about it is described below - /// - /// # Safety - /// The unsafe here is necessary because we call a `C`-function which is inherently unsafe. - /// We have to cast the given closure reference into a `void*`. This is called `type-erasure` and - /// it is necessary because `C` has no build in functions for such complex mechanisms. - /// We also give the callback the generic parameter `F` which contains the unique signature - /// of the closure. This is very imported because only with this we can cast it back later - /// (as described below). - /// - /// Also we check if the device is initialized successfully and return a rust-idiomatic - /// Result as described above - /// + /// * `user_callback` The user defined callback that gets called from the os whenever new data is received from the `UART` /// /// # Examples /// ``` @@ -65,14 +114,14 @@ impl UartDevice { /// ``` pub fn new(dev: uart_t, baud: u32, user_callback: &mut F) -> Result where - F: FnMut(u8), //FnMut(u8) is a closure that can capture the environment. It has one argument of type u8 which will be the received data from the UART + F: FnMut(u8), { unsafe { - match UartDeviceStatus::from_c_int(uart_init( + match UartDeviceStatus::from_c(uart_init( dev, baud, - Some(Self::new_data_callback::), // Here we pass in our callback - user_callback as *mut _ as *mut c_void, // Here we erase the type of the closure by casting it's reference into a void* + Some(Self::new_data_callback::), + user_callback as *mut _ as *mut c_void, )) { UartDeviceStatus::Success => Ok(Self { dev }), status => Err(status), @@ -88,11 +137,6 @@ impl UartDevice { /// * `dev` - The uart_t handle to the hardware device /// * `baud` - The used baud rate /// - /// # Safety - /// The unsafe here is necessary because we call a `C`-function which is inherently unsafe. - /// In this case it is safe because we check if the device is initialized successfully and - /// return a rust-idiomatic Result as described above - /// /// # Examples /// ``` /// let mut received_data = 0u8; @@ -102,13 +146,14 @@ impl UartDevice { /// ``` pub fn new_without_rx(dev: uart_t, baud: u32) -> Result { unsafe { - match UartDeviceStatus::from_c_int(uart_init(dev, baud, None, ptr::null_mut())) { + match UartDeviceStatus::from_c(uart_init(dev, baud, None, ptr::null_mut())) { UartDeviceStatus::Success => Ok(Self { dev }), status => Err(status), } } } + /// Sets the mode according to the given parameters /// Should the parameters be invalid, the function returns a Err /// # Arguments @@ -120,21 +165,23 @@ impl UartDevice { /// ``` /// let mut received_data = 0u8; /// let mut uart = UartDevice::new_without_rx(uart_type_t_STM32_USART, 115200) - /// .unwrap_or_else(|e| panic!("Error initializing UART: {e:?}")); - /// uart.set_mode( - /// riot_sys::uart_data_bits_t_UART_DATA_BITS_8, - /// riot_sys::uart_parity_t_UART_PARITY_NONE, - /// uart_stop_bits_t_UART_STOP_BITS_1, - /// ).unwrap_or_else(|e| panic!("Error setting UART mode: {e:?}")); + /// .unwrap_or_else(|e| panic!("Error initializing UART: {e:?}")); + /// uart.set_mode(DataBits::Eight, Parity::None, StopBits::One) + /// .unwrap_or_else(|e| panic!("Error setting UART mode: {e:?}")); /// ``` pub fn set_mode( &mut self, - data_bits: uart_data_bits_t, - parity: uart_parity_t, - stop_bits: uart_stop_bits_t, + data_bits: DataBits, + parity: Parity, + stop_bits: StopBits, ) -> Result<(), UartDeviceStatus> { unsafe { - match UartDeviceStatus::from_c_int(uart_mode(self.dev, data_bits, parity, stop_bits)) { + match UartDeviceStatus::from_c(uart_mode( + self.dev, + data_bits.to_c(), + parity.to_c(), + stop_bits.to_c(), + )) { UartDeviceStatus::Success => Ok(()), status => Err(status), } @@ -142,10 +189,6 @@ impl UartDevice { } /// Transmits the given data via the `UART`-device - /// # Safety - /// The unsafe here is necessary because we call a `C`-function which is inherently unsafe. - /// In this case it is safe because the rust compiler guarantees, that the slice pointing to the - /// data is always valid /// /// # Examples /// ``` @@ -159,92 +202,56 @@ impl UartDevice { } /// The function turns off the power from the `UART-Device` - /// # Safety - /// The unsafe here is necessary because we call a `C`-function which is inherently unsafe. - /// In this case it safe because we pass no parameters to the function pub fn power_off(&mut self) { unsafe { uart_poweroff(self.dev) }; } /// The function turns on the power from the `UART-Device` - /// # Safety - /// The unsafe here is necessary because we call a `C`-function which is inherently unsafe. - /// In this case it safe because we pass no parameters to the function pub fn power_on(&mut self) { unsafe { uart_poweron(self.dev) }; } /// Disables collision detection check of the given UART device - /// - /// # Safety - /// The unsafe here is necessary because we call a `C`-function which is inherently unsafe. - /// In this case it's safe because we pass no parameters to the function - #[cfg(riot_module_periph_uart_rxstart_irq)] + #[cfg(riot_module_periph_uart_collision)] pub fn collision_detect_disable(&mut self) { unsafe { uart_collision_detect_disable(self.dev) }; } /// Enables collision detection check of the given UART device - /// - /// # Safety - /// The unsafe here is necessary because we call a `C`-function which is inherently unsafe. - /// In this case it's safe because we pass no parameters to the function - #[cfg(riot_module_periph_uart_rxstart_irq)] + #[cfg(riot_module_periph_uart_collision)] pub fn collision_detect_enable(&mut self) { unsafe { uart_collision_detect_enable(self.dev) }; } - /// Disables collision detection of the given UART device - /// and return true if collision occurred during last transfer - /// - /// # Safety - /// The unsafe here is necessary because we call a `C`-function which is inherently unsafe. - /// In this case it's safe because we pass no parameters to the function + /// Disables collision detection of the given UART device and return true if collision occurred during last transfer #[cfg(riot_module_periph_uart_rxstart_irq)] pub fn collision_detected(&mut self) -> bool { unsafe { uart_collision_detected(self.dev) } } /// Change the pins of the given UART back to plain GPIO functionality. - /// - /// # Safety - /// The unsafe here is necessary because we call a `C`-function which is inherently unsafe. - /// In this case it's safe because we pass no parameters to the function #[cfg(riot_module_periph_uart_reconfigure)] pub fn deinit_pins(&mut self) { unsafe { uart_deinit_pins(self.dev) }; } - /// Get the RX pin of the given UART device - /// - /// # Safety - /// The unsafe here is necessary because we call a `C`-function which is inherently unsafe. - /// In this case it's safe because we pass no parameters to the function #[cfg(riot_module_periph_uart_reconfigure)] pub fn get_pin_rx(&mut self) { unsafe { uart_pin_rx(self.dev) }; } /// Get the TX pin of the given UART device - /// - /// # Safety - /// The unsafe here is necessary because we call a `C`-function which is inherently unsafe. - /// In this case it's safe because we pass no parameters to the function #[cfg(riot_module_periph_uart_reconfigure)] pub fn get_pin_tx(&mut self) { unsafe { uart_pin_tx(self.dev) }; } - /// Configure the function that will be called when a start condition is detected. + /// Configure the function that will be called when a start condition is detected /// /// # Arguments /// * `dev` - The uart_t handle to the hardware device /// * `user_fxopt` - The user defined callback function called when a start condition is detected - /// - /// # Safety - /// The unsafe here is necessary because we call a `C`-function which is inherently unsafe. - /// In this case it's safe because we pass no parameters to the function #[cfg(riot_module_periph_uart_rxstart_irq)] pub fn rxstart_irq_configure(dev: uart_t, user_fxopt: &mut F) where @@ -260,32 +267,19 @@ impl UartDevice { } /// Enable the RX start interrupt - /// - /// # Safety - /// The unsafe here is necessary because we call a `C`-function which is inherently unsafe. - /// In this case it's safe because we pass no parameters to the function #[cfg(riot_module_riot_module_periph_uart_rxstart_irq)] pub fn rxstart_irq_enable(&mut self) { unsafe { uart_rxstart_irq_enable(self.dev) }; } /// Disable the RX start interrupt - /// - /// # Safety - /// The unsafe here is necessary because we call a `C`-function which is inherently unsafe. /// In this case it's safe because we pass no parameters to the function #[cfg(riot_module_riot_module_periph_uart_rxstart_irq)] pub fn rxstart_irq_disable(&mut self) { unsafe { uart_rxstart_irq_disable(self.dev) }; } - /// This is the callback that gets called directly from the kernel - /// # Safety - /// It is safe for us to cast the void* back to the user-defined closure, - /// because it's signature type `F` gets passed to us from the callers side. Therefore, the - /// signature and the captured variables are pointing to the exact same addresses - /// from the stack-frame that owns the device - /// + /// This is the callback that gets called directly from the kernel if new data from the `UART` is received /// # Arguments /// * `user_callback` - The address pointing to the user defined callback /// * `data` - The newly received data from the `UART` @@ -296,9 +290,14 @@ impl UartDevice { (*(user_callback as *mut F))(data); //We cast the void* back to the closure and call it } + /// This is the callback that gets called directly from the kernel when a start condition is detected + /// # Arguments + /// * `user_callback` - The address pointing to the user defined callback + /// * `data` - The newly received data from the `UART` + #[cfg(riot_module_periph_uart_rxstart_irq)] unsafe extern "C" fn rxstart_callback(user_callback: *mut c_void) where - F: FnMut(u8), + F: FnMut(), { (*(user_callback as *mut F))(); } @@ -312,15 +311,20 @@ impl Drop for UartDevice { /// Also if the build is in debug mode and the `UART` is reinitialized unsuccessfully, the code panics which /// is definitely NOT the right behavior at this point! fn drop(&mut self) { - unsafe { - let status = - UartDeviceStatus::from_c_int(uart_init(self.dev, 9600, None, ptr::null_mut())); - debug_assert_eq!( - status, - UartDeviceStatus::Success, - "Error while deinitializing UART: {status:?}" - ); - self.power_off(); + if cfg!(riot_module_periph_uart_reconfigure) { + #[cfg(riot_module_periph_uart_reconfigure)] + deinit_pins(self); //TODO Check if this also removes the irq + } else { + unsafe { + let status = + UartDeviceStatus::from_c(uart_init(self.dev, 9600, None, ptr::null_mut())); + debug_assert_eq!( + status, + UartDeviceStatus::Success, + "Error while deinitializing UART: {status:?}" + ); + self.power_off(); + } } } } From fc644cfdd7ff7dbb6bf6ddf58f1acdd99bd6d1bf Mon Sep 17 00:00:00 2001 From: Kilian Barning Date: Mon, 6 Feb 2023 02:36:34 +0100 Subject: [PATCH 04/41] Add init pins --- src/uart.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/uart.rs b/src/uart.rs index 16f9bb52..646dbf6e 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -235,6 +235,12 @@ impl UartDevice { unsafe { uart_deinit_pins(self.dev) }; } + /// Init the pins of the `UART`. In normal cases, this function will not be used. + #[cfg(riot_module_periph_uart_reconfigure)] + pub fn init_pins(&mut self) { + unsafe { uart_init_pins(self.dev) }; + } + /// Get the RX pin of the given UART device #[cfg(riot_module_periph_uart_reconfigure)] pub fn get_pin_rx(&mut self) { From 88ff4a654c6300215840cf59126efa293cebbd7e Mon Sep 17 00:00:00 2001 From: Joel Aschmann Date: Mon, 6 Feb 2023 13:55:17 +0100 Subject: [PATCH 05/41] DAC: Add wrapper around RIOTs DAC-interface --- src/dac.rs | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 +++ 2 files changed, 63 insertions(+) create mode 100644 src/dac.rs diff --git a/src/dac.rs b/src/dac.rs new file mode 100644 index 00000000..0d81ab18 --- /dev/null +++ b/src/dac.rs @@ -0,0 +1,60 @@ +use riot_sys::dac_t; + +#[derive(Debug)] +pub struct DACLine { + line: dac_t, +} + +#[derive(Debug)] +pub enum DACError { + /// The given dac_t line did not exist + NoLine, + /// An unknown error did occur + Unknown, +} + +impl DACLine { + /// Creates and intializes a new [`DACLine`] from the given + /// unitialized [`dac_t`]. + /// + /// Returns an Error if the given line does not exist + /// on the board. + pub fn new(line: dac_t) -> Result { + let res = unsafe { riot_sys::dac_init(line) } as i32; + + const DAC_OK: i32 = riot_sys::DAC_OK as i32; + const DAC_NOLINE: i32 = riot_sys::DAC_NOLINE as i32; + + match res { + DAC_OK => Ok(DACLine { line }), + DAC_NOLINE => Err(DACError::NoLine), + _ => Err(DACError::Unknown), + } + } + + /// Builds a [`DACLine`] from an already initialized [`dac_t`]. + /// + /// Providing a not initialized [`dac_t`] results in undefined behavior. + pub unsafe fn new_without_init(line: dac_t) -> Self { + DACLine { line } + } + + /// Writes the given value to this [`DACLine`] + /// + /// The `value` is internally scaled to the underlying + /// dac device so that the maximum voltage output + /// is always equivalent to [`u16::MAX`] + pub fn set(&self, value: u16) { + unsafe { riot_sys::dac_set(self.line, value) } + } + + /// Turns the [`DACLine`] on after `DACLine::power_off` + pub fn power_on(&self) { + unsafe { riot_sys::dac_poweron(self.line) } + } + + /// Turns the [`DACLine`] off + pub fn power_off(&self) { + unsafe { riot_sys::dac_poweroff(self.line) } + } +} diff --git a/src/lib.rs b/src/lib.rs index 75d43372..41be66a7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -117,6 +117,9 @@ pub mod spi; #[cfg(riot_module_periph_adc)] pub mod adc; +#[cfg(riot_module_periph_dac)] +pub mod dac; + #[cfg(riot_module_ztimer)] pub mod ztimer; From 79e8e237b845b16c279168a2f58f75141d0f1cc1 Mon Sep 17 00:00:00 2001 From: Kilian Barning Date: Mon, 6 Feb 2023 23:23:31 +0100 Subject: [PATCH 06/41] Fix comments --- src/uart.rs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/uart.rs b/src/uart.rs index 646dbf6e..761845c4 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -1,6 +1,6 @@ #![allow(dead_code)] -use core::ptr; +use core::{mem, ptr}; use riot_sys::libc::{c_int, c_uint, c_void}; use riot_sys::*; @@ -70,7 +70,7 @@ impl Parity { Self::None => uart_parity_t_UART_PARITY_NONE, Self::Even => uart_parity_t_UART_PARITY_EVEN, Self::Odd => uart_parity_t_UART_PARITY_ODD, - Self::Mark => uart_parity_t_UART_PARITY_ODD, + Self::Mark => uart_parity_t_UART_PARITY_MARK, Self::Space => uart_parity_t_UART_PARITY_SPACE, } } @@ -229,13 +229,17 @@ impl UartDevice { unsafe { uart_collision_detected(self.dev) } } - /// Change the pins of the given UART back to plain GPIO functionality. + /// Change the pins of the given UART back to plain GPIO functionality. It also consumes the `UART`, so it cannot + /// be used afterwards #[cfg(riot_module_periph_uart_reconfigure)] - pub fn deinit_pins(&mut self) { + pub fn deinit_pins(self) { unsafe { uart_deinit_pins(self.dev) }; } - /// Init the pins of the `UART`. In normal cases, this function will not be used. + /// After calling uart_init, the pins must be initialized (i.e. uart_init is calling this function internally). + /// In normal cases, this function will not be used. But there are some devices, that use UART bus lines also + /// for other purposes and need the option to dynamically re-configure one or more of the used pins. So + /// they can take control over certain pins and return control back to the UART driver using this function. #[cfg(riot_module_periph_uart_reconfigure)] pub fn init_pins(&mut self) { unsafe { uart_init_pins(self.dev) }; @@ -243,14 +247,14 @@ impl UartDevice { /// Get the RX pin of the given UART device #[cfg(riot_module_periph_uart_reconfigure)] - pub fn get_pin_rx(&mut self) { - unsafe { uart_pin_rx(self.dev) }; + pub fn get_pin_rx(&mut self) -> gpio_t { + unsafe { uart_pin_rx(self.dev) } } /// Get the TX pin of the given UART device #[cfg(riot_module_periph_uart_reconfigure)] - pub fn get_pin_tx(&mut self) { - unsafe { uart_pin_tx(self.dev) }; + pub fn get_pin_tx(&mut self) -> gpio_t { + unsafe { uart_pin_tx(self.dev) } } /// Configure the function that will be called when a start condition is detected @@ -279,7 +283,6 @@ impl UartDevice { } /// Disable the RX start interrupt - /// In this case it's safe because we pass no parameters to the function #[cfg(riot_module_riot_module_periph_uart_rxstart_irq)] pub fn rxstart_irq_disable(&mut self) { unsafe { uart_rxstart_irq_disable(self.dev) }; From fa26004993ff20d059b37c8c534cf39e2a20e490 Mon Sep 17 00:00:00 2001 From: Joel Aschmann Date: Wed, 8 Feb 2023 13:55:36 +0100 Subject: [PATCH 07/41] PWM: Add wrapper around RIOTs PWM-interface --- src/lib.rs | 3 + src/pwm.rs | 186 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 189 insertions(+) create mode 100644 src/pwm.rs diff --git a/src/lib.rs b/src/lib.rs index 41be66a7..c4324b87 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -117,6 +117,9 @@ pub mod spi; #[cfg(riot_module_periph_adc)] pub mod adc; +#[cfg(riot_module_periph_pwm)] +pub mod pwm; + #[cfg(riot_module_periph_dac)] pub mod dac; diff --git a/src/pwm.rs b/src/pwm.rs new file mode 100644 index 00000000..a63036aa --- /dev/null +++ b/src/pwm.rs @@ -0,0 +1,186 @@ +use crate::println; +use embedded_hal::Pwm; +use riot_sys::pwm_t; + +/// PWM modes +/// +/// [`PWMMode::CENTER`] exists in RIOT but is marked as "not supported" +#[derive(Debug)] +pub enum PWMMode { + LEFT, + RIGHT, + CENTER, +} + +impl PWMMode { + /// Converts the rust enum to a c type + pub fn to_c(self) -> riot_sys::pwm_mode_t { + match self { + Self::LEFT => riot_sys::pwm_mode_t_PWM_LEFT, + Self::RIGHT => riot_sys::pwm_mode_t_PWM_RIGHT, + Self::CENTER => riot_sys::pwm_mode_t_PWM_CENTER, + } + } +} + +#[derive(Debug)] +pub struct PWMDevice { + dev: pwm_t, + channel_duty_values: [u16; CHANNELS], //Needed because of embedded_hal implementation + frequency: u32, //Needed because of embedded_hal implementation +} + +pub type Hz = u32; + +impl PWMDevice { + /// Creates and initializes a [`PWMDevice`] with the given frequency in Hz and the resolution of a period/duty cycle + /// + /// If the given frequency and resolution values can not be acchieved on the current device, the + /// resolution will be kept the same while the frequency will be lowered until the device can handle the combination. + /// The actual set frequency can the be obtained with [`PWMDevice::get_frequency`]. + /// + /// Returns the initialized [`PWMDevice`] on success + /// + /// **Note** that the `CHANNELS` Variable is only used to cache the duty values for each of the `CHANNELS` number of + /// pwm channels. This is used by [`embedded_hal::Pwm::get_duty`]. If this is not needed setting `CHANNELS` to `0` saves memory. + pub fn new(dev: pwm_t, mode: PWMMode, frequency: Hz, resolution: u16) -> Result { + let mut pwm_dev = PWMDevice { + dev, + channel_duty_values: [0; CHANNELS], + frequency, + }; + + match pwm_dev.init(mode, frequency, resolution) { + Ok(()) => Ok(pwm_dev), + // Sadly RIOTs interface does not allow for a more elaborate error handling. + // Maybe something like errno is set without the doc telling us, which could be used here. + Err(()) => Err(()), + } + } + + /// Creates a PWM device from an already initialized c type. + /// + /// **Note** that the `CHANNELS` Variable is only used to cache the duty values for each of the `CHANNELS` number of + /// pwm channels. This is used by [`embedded_hal::Pwm::get_duty`]. If this is not needed setting `CHANNELS` to `0` saves memory. + /// + /// ## Important: + /// It is **important** to make sure that the provided **device is already initialized** by using [`riot_sys::pwm_init`](https://rustdoc.etonomy.org/riot_sys/fn.pwm_init.html). + /// Using the returned device otherwise results in **undefined behavior**! + /// + /// Also note, that the given frequency is only to be used by [`embedded_hal::Pwm::get_period`] or [`PWMDevice::get_frequency`]. + /// Just setting this to `x` will only result in those two functions returning `x` but will have no other impact, if + /// for example the given [`pwm_t`] is initialized by a board and the actually set frequency is unknown. + /// + pub unsafe fn new_without_init(dev: pwm_t, frequency: Hz) -> Self { + PWMDevice { + dev, + channel_duty_values: [0; CHANNELS], + frequency, + } + } + + /// Initializes the [`PWMDevice`] with the given frequency in Hz and resolution of a period/duty cycle. + /// + /// If the given frequency and resolution values can not be acchieved on the current device, the + /// resolution will be kept the same while the frequency will be lowered until the device can handle the combination. + /// The resulting frequency is written into the [`PWMDevice`]. + /// + /// Uses a [`Result`] in anticipation of a more usefull error handling in the future. + /// + /// Returns if the initialization was a success + fn init(&mut self, mode: PWMMode, frequency: Hz, resolution: u16) -> Result<(), ()> { + let err = unsafe { riot_sys::pwm_init(self.dev, mode.to_c(), frequency, resolution) }; + + match err { + 0 => Err(()), + freq => { + // Set frequency in the device + self.frequency = freq; + Ok(()) + } + } + } + + /// Returns the number of available channels for this device + pub fn channels(&self) -> u8 { + unsafe { riot_sys::pwm_channels(self.dev).min(CHANNELS as u8) } + } + + /// Stops PWM generation on this device + pub fn power_off(&mut self) { + unsafe { riot_sys::pwm_poweroff(self.dev) } + } + + /// Resumes PWM generation after power_off on this device + pub fn power_on(&mut self) { + unsafe { + riot_sys::pwm_poweron(self.dev); + } + } + + /// Sets the duty-cycle for the given channel + /// + /// value: `0: 0%, u16::MAX: 100%` duty_cycle + pub fn set(&mut self, channel: u8, value: u16) { + unsafe { + riot_sys::pwm_set(self.dev, channel, value); + } + + let channel = channel as usize; + // Ignore if entry does not exists because + // only embedded_hal interface cares about those values + // and the implementation already checks for this + if channel < (CHANNELS) { + self.channel_duty_values[channel] = value; + } + } + + /// Returns the used frequency of this [`PWMDevice`] in Hz + /// + pub fn get_frequency(&self) -> u32 { + self.frequency + } +} + +pub type Seconds = f32; + +impl Pwm for PWMDevice { + type Channel = u8; + type Duty = u16; + type Time = Seconds; + + fn disable(&mut self, _channel: Self::Channel) { + println!("[ERROR] RIOT does not support enabling/disabling single channels") + } + + fn enable(&mut self, _channel: Self::Channel) { + println!("[ERROR] RIOT does not support enabling/disabling single channels") + } + + fn get_period(&self) -> Self::Time { + 1. / self.frequency as f32 + } + + fn get_duty(&self, channel: Self::Channel) -> Self::Duty { + self.channel_duty_values[channel as usize] + } + + fn get_max_duty(&self) -> Self::Duty { + u16::MAX + } + + fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { + if (channel as usize) >= CHANNELS { + panic!("Tried to set duty for non existing channel: {channel}") + } + + self.set(channel, duty); + } + + fn set_period

(&mut self, _period: P) + where + P: Into, + { + println!("RIOT does not support setting the period after initialisation") + } +} From 9be601ce7616330fb61b52cbd4e0d59dd652331b Mon Sep 17 00:00:00 2001 From: Kilian Barning Date: Wed, 8 Feb 2023 16:50:37 +0100 Subject: [PATCH 08/41] Fixed signature --- src/uart.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uart.rs b/src/uart.rs index 761845c4..10e929c0 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -263,7 +263,7 @@ impl UartDevice { /// * `dev` - The uart_t handle to the hardware device /// * `user_fxopt` - The user defined callback function called when a start condition is detected #[cfg(riot_module_periph_uart_rxstart_irq)] - pub fn rxstart_irq_configure(dev: uart_t, user_fxopt: &mut F) + pub fn rxstart_irq_configure(&mut self, user_fxopt: &mut F) where F: FnMut(u8), { From cfbe7c13e6383d1867808a72f0e101e0ab6c20db Mon Sep 17 00:00:00 2001 From: Kilian Barning Date: Wed, 8 Feb 2023 16:52:56 +0100 Subject: [PATCH 09/41] Fixed closure type --- src/uart.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/uart.rs b/src/uart.rs index 10e929c0..4624c94d 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -260,12 +260,11 @@ impl UartDevice { /// Configure the function that will be called when a start condition is detected /// /// # Arguments - /// * `dev` - The uart_t handle to the hardware device /// * `user_fxopt` - The user defined callback function called when a start condition is detected #[cfg(riot_module_periph_uart_rxstart_irq)] pub fn rxstart_irq_configure(&mut self, user_fxopt: &mut F) where - F: FnMut(u8), + F: FnMut(), { unsafe { uart_rxstart_irq_configure( From 9bff3decea0aa23c51cac17a10b3917e0a792a3a Mon Sep 17 00:00:00 2001 From: Kilian Barning Date: Wed, 8 Feb 2023 17:05:58 +0100 Subject: [PATCH 10/41] Mark init pins as unsafe --- src/uart.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/uart.rs b/src/uart.rs index 4624c94d..e998846e 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -229,20 +229,20 @@ impl UartDevice { unsafe { uart_collision_detected(self.dev) } } - /// Change the pins of the given UART back to plain GPIO functionality. It also consumes the `UART`, so it cannot - /// be used afterwards - #[cfg(riot_module_periph_uart_reconfigure)] - pub fn deinit_pins(self) { - unsafe { uart_deinit_pins(self.dev) }; - } - /// After calling uart_init, the pins must be initialized (i.e. uart_init is calling this function internally). /// In normal cases, this function will not be used. But there are some devices, that use UART bus lines also /// for other purposes and need the option to dynamically re-configure one or more of the used pins. So /// they can take control over certain pins and return control back to the UART driver using this function. #[cfg(riot_module_periph_uart_reconfigure)] - pub fn init_pins(&mut self) { - unsafe { uart_init_pins(self.dev) }; + pub unsafe fn init_pins(&mut self) { + uart_init_pins(self.dev); + } + + /// Change the pins of the given UART back to plain GPIO functionality. It also consumes the `UART`, so it cannot + /// be used afterwards + #[cfg(riot_module_periph_uart_reconfigure)] + pub unsafe fn deinit_pins(&mut self) { + uart_deinit_pins(self.dev); } /// Get the RX pin of the given UART device From 793614b2aa3460c11bcb78ebd7e03837e476210e Mon Sep 17 00:00:00 2001 From: Kilian Barning Date: Wed, 8 Feb 2023 17:10:38 +0100 Subject: [PATCH 11/41] Fix comments --- src/uart.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/uart.rs b/src/uart.rs index e998846e..03b77188 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -238,8 +238,7 @@ impl UartDevice { uart_init_pins(self.dev); } - /// Change the pins of the given UART back to plain GPIO functionality. It also consumes the `UART`, so it cannot - /// be used afterwards + /// Change the pins of the given UART back to plain GPIO functionality #[cfg(riot_module_periph_uart_reconfigure)] pub unsafe fn deinit_pins(&mut self) { uart_deinit_pins(self.dev); From c6ef6d50eda0db64b7454b850826bf46d01e9028 Mon Sep 17 00:00:00 2001 From: Kilian Barning Date: Wed, 8 Feb 2023 17:18:43 +0100 Subject: [PATCH 12/41] Change drop --- src/uart.rs | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/src/uart.rs b/src/uart.rs index 03b77188..6b4229e5 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -311,27 +311,11 @@ impl UartDevice { } impl Drop for UartDevice { - /// The `drop` method resets the uart to 9600 baud and removes the user defined callback - /// # Safety - /// At this moment it is unclear if this implementation is the right way to go. There might - /// be a better solution... - /// Also if the build is in debug mode and the `UART` is reinitialized unsuccessfully, the code panics which - /// is definitely NOT the right behavior at this point! + /// The `drop` method resets the `UART` pins back to gpio functionality if possible fn drop(&mut self) { if cfg!(riot_module_periph_uart_reconfigure) { #[cfg(riot_module_periph_uart_reconfigure)] deinit_pins(self); //TODO Check if this also removes the irq - } else { - unsafe { - let status = - UartDeviceStatus::from_c(uart_init(self.dev, 9600, None, ptr::null_mut())); - debug_assert_eq!( - status, - UartDeviceStatus::Success, - "Error while deinitializing UART: {status:?}" - ); - self.power_off(); - } } } } From c7cc6265f8bfa1f2838391df35c213e7762fe586 Mon Sep 17 00:00:00 2001 From: Kilian Barning Date: Thu, 9 Feb 2023 04:01:34 +0100 Subject: [PATCH 13/41] Introduce Phantom Data Lifetimes --- src/uart.rs | 127 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 73 insertions(+), 54 deletions(-) diff --git a/src/uart.rs b/src/uart.rs index 6b4229e5..36867863 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -1,19 +1,16 @@ +//! Access to [RIOT's UART](https://doc.riot-os.org/group__drivers__periph__uart.html) +//! +//! Author: Kilian Barning #![allow(dead_code)] -use core::{mem, ptr}; -use riot_sys::libc::{c_int, c_uint, c_void}; -use riot_sys::*; +use core::marker::PhantomData; +use core::ptr; -/// This struct contains the `UART` device and handles all operation regarding it -/// -/// [UART implementation]: https://doc.riot-os.org/group__drivers__periph__uart.html -#[derive(Debug)] -pub struct UartDevice { - dev: uart_t, -} +use riot_sys::libc::{c_int, c_void}; +use riot_sys::*; /// This enum representatives the status returned by various `UART`-functions -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug)] #[non_exhaustive] pub enum UartDeviceStatus { Success, @@ -36,7 +33,7 @@ impl UartDeviceStatus { } } -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug)] pub enum DataBits { Five, Six, @@ -55,7 +52,7 @@ impl DataBits { } } -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug)] pub enum Parity { None, Even, @@ -77,7 +74,7 @@ impl Parity { } -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug)] pub enum StopBits { One, Two, @@ -92,8 +89,16 @@ impl StopBits { } } +/// This struct contains the `UART` device and handles all operation regarding it +/// +/// [UART implementation]: https://doc.riot-os.org/group__drivers__periph__uart.html +#[derive(Debug)] +pub struct UartDevice<'a> { + dev: uart_t, + _marker: PhantomData<&'a ()>, /// We use this `PhantomData` here to make sure that the lifetime of the borrowed closure is equal to this struct +} -impl UartDevice { +impl<'a> UartDevice<'a> { /// Tries to initialize the given `UART`. Returns a Result with rather `Ok` if the UART was initialized successfully or a /// `Err` containing the error /// @@ -105,14 +110,19 @@ impl UartDevice { /// /// # Examples /// ``` - /// let mut received_data = 0u8; - /// let mut uart = UartDevice::new(uart_type_t_STM32_USART, 115200, &mut |data| { - /// received_data = data; - /// }) - /// .unwrap_or_else(|e| panic!("Error initializing UART: {e:?}")); + /// use riot_wrappers::uart::UartDevice; + /// let mut cb = |new_data| { + /// //do something here with the received data + /// }; + /// let mut uart = UartDevice::new(uart_type_t_STM32_USART, 115200, &mut cb) + /// .unwrap_or_else(|e| panic!("Error initializing UART: {e:?}")); /// uart.write(b"Hello from UART"); /// ``` - pub fn new(dev: uart_t, baud: u32, user_callback: &mut F) -> Result + pub fn new( + dev: uart_t, + baud: u32, + user_callback: &'a mut F, + ) -> Result where F: FnMut(u8), { @@ -123,7 +133,10 @@ impl UartDevice { Some(Self::new_data_callback::), user_callback as *mut _ as *mut c_void, )) { - UartDeviceStatus::Success => Ok(Self { dev }), + UartDeviceStatus::Success => Ok(Self { + dev, + _marker: PhantomData, + }), status => Err(status), } } @@ -139,6 +152,7 @@ impl UartDevice { /// /// # Examples /// ``` + /// use riot_wrappers::uart::UartDevice; /// let mut received_data = 0u8; /// let mut uart = UartDevice::new_without_rx(uart_type_t_STM32_USART, 115200) /// .unwrap_or_else(|e| panic!("Error initializing UART: {e:?}")); @@ -147,7 +161,10 @@ impl UartDevice { pub fn new_without_rx(dev: uart_t, baud: u32) -> Result { unsafe { match UartDeviceStatus::from_c(uart_init(dev, baud, None, ptr::null_mut())) { - UartDeviceStatus::Success => Ok(Self { dev }), + UartDeviceStatus::Success => Ok(Self { + dev, + _marker: PhantomData, + }), status => Err(status), } } @@ -155,7 +172,7 @@ impl UartDevice { /// Sets the mode according to the given parameters - /// Should the parameters be invalid, the function returns a Err + /// Should the parameters be invalid, the function returns a Err /// # Arguments /// * `data_bits` - Number of data bits in a UART frame /// * `parity` - Parity mode @@ -163,6 +180,7 @@ impl UartDevice { /// /// # Examples /// ``` + /// use riot_wrappers::uart::{DataBits, Parity, StopBits, UartDevice}; /// let mut received_data = 0u8; /// let mut uart = UartDevice::new_without_rx(uart_type_t_STM32_USART, 115200) /// .unwrap_or_else(|e| panic!("Error initializing UART: {e:?}")); @@ -192,6 +210,7 @@ impl UartDevice { /// /// # Examples /// ``` + /// use riot_wrappers::uart::UartDevice; /// let mut uart = UartDevice::new_without_rx(uart_type_t_STM32_USART, 115200) /// .unwrap_or_else(|e| panic!("Error initializing UART: {e:?}")); /// uart.write(b"Hello from UART\n"); @@ -201,56 +220,55 @@ impl UartDevice { } } - /// The function turns off the power from the `UART-Device` - pub fn power_off(&mut self) { - unsafe { uart_poweroff(self.dev) }; - } - - /// The function turns on the power from the `UART-Device` + /// Turns on the power from the `UART-Device` pub fn power_on(&mut self) { unsafe { uart_poweron(self.dev) }; } - /// Disables collision detection check of the given UART device - #[cfg(riot_module_periph_uart_collision)] - pub fn collision_detect_disable(&mut self) { - unsafe { uart_collision_detect_disable(self.dev) }; + /// Turns off the power from the `UART-Device` + pub fn power_off(&mut self) { + unsafe { uart_poweroff(self.dev) }; } - /// Enables collision detection check of the given UART device + /// Enables collision detection check #[cfg(riot_module_periph_uart_collision)] pub fn collision_detect_enable(&mut self) { unsafe { uart_collision_detect_enable(self.dev) }; } - /// Disables collision detection of the given UART device and return true if collision occurred during last transfer + /// Disables collision detection check + #[cfg(riot_module_periph_uart_collision)] + pub fn collision_detect_disable(&mut self) { + unsafe { uart_collision_detect_disable(self.dev) }; + } + + /// Disables collision detection and returns if a collision occurred during last transfer #[cfg(riot_module_periph_uart_rxstart_irq)] pub fn collision_detected(&mut self) -> bool { unsafe { uart_collision_detected(self.dev) } } - /// After calling uart_init, the pins must be initialized (i.e. uart_init is calling this function internally). - /// In normal cases, this function will not be used. But there are some devices, that use UART bus lines also - /// for other purposes and need the option to dynamically re-configure one or more of the used pins. So - /// they can take control over certain pins and return control back to the UART driver using this function. + /// This function normally does not need to be called. But in some case, the pins on the `UART` + /// might be shared with some other functionality (like `GPIO`). In this case, it is necessary + /// to give the user the possibility to init the pins again. #[cfg(riot_module_periph_uart_reconfigure)] pub unsafe fn init_pins(&mut self) { uart_init_pins(self.dev); } - /// Change the pins of the given UART back to plain GPIO functionality + /// Change the pins back to plain GPIO functionality #[cfg(riot_module_periph_uart_reconfigure)] pub unsafe fn deinit_pins(&mut self) { uart_deinit_pins(self.dev); } - /// Get the RX pin of the given UART device + /// Get the RX pin #[cfg(riot_module_periph_uart_reconfigure)] pub fn get_pin_rx(&mut self) -> gpio_t { unsafe { uart_pin_rx(self.dev) } } - /// Get the TX pin of the given UART device + /// Get the TX pin #[cfg(riot_module_periph_uart_reconfigure)] pub fn get_pin_tx(&mut self) -> gpio_t { unsafe { uart_pin_tx(self.dev) } @@ -259,29 +277,29 @@ impl UartDevice { /// Configure the function that will be called when a start condition is detected /// /// # Arguments - /// * `user_fxopt` - The user defined callback function called when a start condition is detected + /// * `user_fxopt` - The user defined callback function that gets called when a start condition is detected #[cfg(riot_module_periph_uart_rxstart_irq)] - pub fn rxstart_irq_configure(&mut self, user_fxopt: &mut F) + pub fn rxstart_irq_configure(&mut self, user_fxopt: &'a mut F) where F: FnMut(), { unsafe { uart_rxstart_irq_configure( dev, - Self::rxstart_callback::, + Self::rxstart_callback, user_fxopt as *mut _ as *mut c_void, ) }; } /// Enable the RX start interrupt - #[cfg(riot_module_riot_module_periph_uart_rxstart_irq)] + #[cfg(riot_module_periph_uart_rxstart_irq)] pub fn rxstart_irq_enable(&mut self) { unsafe { uart_rxstart_irq_enable(self.dev) }; } /// Disable the RX start interrupt - #[cfg(riot_module_riot_module_periph_uart_rxstart_irq)] + #[cfg(riot_module_periph_uart_rxstart_irq)] pub fn rxstart_irq_disable(&mut self) { unsafe { uart_rxstart_irq_disable(self.dev) }; } @@ -300,22 +318,23 @@ impl UartDevice { /// This is the callback that gets called directly from the kernel when a start condition is detected /// # Arguments /// * `user_callback` - The address pointing to the user defined callback - /// * `data` - The newly received data from the `UART` #[cfg(riot_module_periph_uart_rxstart_irq)] unsafe extern "C" fn rxstart_callback(user_callback: *mut c_void) where F: FnMut(), { - (*(user_callback as *mut F))(); + (*(user_callback as *mut F))(); //We cast the void* back to the closure and call it } } -impl Drop for UartDevice { - /// The `drop` method resets the `UART` pins back to gpio functionality if possible +impl<'a> Drop for UartDevice<'a> { + /// The `drop` method resets the `UART`, removes the interrupt and tries + /// to reset the `GPIO` pins if possible fn drop(&mut self) { - if cfg!(riot_module_periph_uart_reconfigure) { + unsafe { + uart_init(self.dev, 9600, None, ptr::null_mut()); #[cfg(riot_module_periph_uart_reconfigure)] - deinit_pins(self); //TODO Check if this also removes the irq + self.deinit_pins(); } } } From b1f76f0ad89b6661e32420aa357750485ca0ade8 Mon Sep 17 00:00:00 2001 From: Kilian Barning Date: Thu, 9 Feb 2023 12:43:52 +0100 Subject: [PATCH 14/41] Add generics to rxstart --- src/uart.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uart.rs b/src/uart.rs index 36867863..d7739c6f 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -95,7 +95,7 @@ impl StopBits { #[derive(Debug)] pub struct UartDevice<'a> { dev: uart_t, - _marker: PhantomData<&'a ()>, /// We use this `PhantomData` here to make sure that the lifetime of the borrowed closure is equal to this struct + _marker: PhantomData<&'a ()>, // We use this `PhantomData` here to make sure that the lifetime of the borrowed closure is equal to this struct } impl<'a> UartDevice<'a> { @@ -286,7 +286,7 @@ impl<'a> UartDevice<'a> { unsafe { uart_rxstart_irq_configure( dev, - Self::rxstart_callback, + Self::rxstart_callback::, user_fxopt as *mut _ as *mut c_void, ) }; From c3738ca9b97d3443881703690a967cd686bfd95f Mon Sep 17 00:00:00 2001 From: Kilian Barning Date: Thu, 9 Feb 2023 14:07:27 +0100 Subject: [PATCH 15/41] Add mode feature --- Cargo.toml | 3 +++ src/uart.rs | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 512792fd..40dec64d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -82,3 +82,6 @@ with_msg_v2 = [] # This is primarily for documentation building; enabling it may also help users # of nightly when they see a mess of types. actual_never_type = [] + +# On some plattforms, uart_set_mode isn't implemented yet +uart_set_mode = [] \ No newline at end of file diff --git a/src/uart.rs b/src/uart.rs index d7739c6f..8b64f8eb 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -187,6 +187,7 @@ impl<'a> UartDevice<'a> { /// uart.set_mode(DataBits::Eight, Parity::None, StopBits::One) /// .unwrap_or_else(|e| panic!("Error setting UART mode: {e:?}")); /// ``` + #[cfg(feature = "uart_set_mode")] pub fn set_mode( &mut self, data_bits: DataBits, @@ -243,7 +244,7 @@ impl<'a> UartDevice<'a> { } /// Disables collision detection and returns if a collision occurred during last transfer - #[cfg(riot_module_periph_uart_rxstart_irq)] + #[cfg(riot_module_periph_uart_collision)] pub fn collision_detected(&mut self) -> bool { unsafe { uart_collision_detected(self.dev) } } @@ -275,7 +276,7 @@ impl<'a> UartDevice<'a> { } /// Configure the function that will be called when a start condition is detected - /// + /// This will not enable / disable the generation of the RX start interrupt /// # Arguments /// * `user_fxopt` - The user defined callback function that gets called when a start condition is detected #[cfg(riot_module_periph_uart_rxstart_irq)] From 0b965d2fc349adab720078826cda19f9216f030a Mon Sep 17 00:00:00 2001 From: Kilian Barning Date: Thu, 9 Feb 2023 14:28:18 +0100 Subject: [PATCH 16/41] Added feature --- Cargo.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index fbb350ab..7bd4d97d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -89,4 +89,7 @@ actual_never_type = [] # # This has some effects of its own (making ValueInThread fundamental), and also # enables actual_never_type. -nightly_docs = [ "actual_never_type" ] \ No newline at end of file +nightly_docs = [ "actual_never_type" ] + +# On some plattforms, uart_set_mode isn't implemented yet +uart_set_mode = [] \ No newline at end of file From 11014dda19abf82f90db07410f984aa9da332f6c Mon Sep 17 00:00:00 2001 From: Kilian Barning <50280188+kbarning@users.noreply.github.com> Date: Thu, 9 Feb 2023 15:17:13 +0100 Subject: [PATCH 17/41] Delete dac.rs --- src/dac.rs | 60 ------------------------------------------------------ 1 file changed, 60 deletions(-) delete mode 100644 src/dac.rs diff --git a/src/dac.rs b/src/dac.rs deleted file mode 100644 index 0d81ab18..00000000 --- a/src/dac.rs +++ /dev/null @@ -1,60 +0,0 @@ -use riot_sys::dac_t; - -#[derive(Debug)] -pub struct DACLine { - line: dac_t, -} - -#[derive(Debug)] -pub enum DACError { - /// The given dac_t line did not exist - NoLine, - /// An unknown error did occur - Unknown, -} - -impl DACLine { - /// Creates and intializes a new [`DACLine`] from the given - /// unitialized [`dac_t`]. - /// - /// Returns an Error if the given line does not exist - /// on the board. - pub fn new(line: dac_t) -> Result { - let res = unsafe { riot_sys::dac_init(line) } as i32; - - const DAC_OK: i32 = riot_sys::DAC_OK as i32; - const DAC_NOLINE: i32 = riot_sys::DAC_NOLINE as i32; - - match res { - DAC_OK => Ok(DACLine { line }), - DAC_NOLINE => Err(DACError::NoLine), - _ => Err(DACError::Unknown), - } - } - - /// Builds a [`DACLine`] from an already initialized [`dac_t`]. - /// - /// Providing a not initialized [`dac_t`] results in undefined behavior. - pub unsafe fn new_without_init(line: dac_t) -> Self { - DACLine { line } - } - - /// Writes the given value to this [`DACLine`] - /// - /// The `value` is internally scaled to the underlying - /// dac device so that the maximum voltage output - /// is always equivalent to [`u16::MAX`] - pub fn set(&self, value: u16) { - unsafe { riot_sys::dac_set(self.line, value) } - } - - /// Turns the [`DACLine`] on after `DACLine::power_off` - pub fn power_on(&self) { - unsafe { riot_sys::dac_poweron(self.line) } - } - - /// Turns the [`DACLine`] off - pub fn power_off(&self) { - unsafe { riot_sys::dac_poweroff(self.line) } - } -} From b81ee74e6fcba173f24c01efb118f05cde74b28c Mon Sep 17 00:00:00 2001 From: Kilian Barning <50280188+kbarning@users.noreply.github.com> Date: Thu, 9 Feb 2023 15:18:46 +0100 Subject: [PATCH 18/41] Delete pwm.rs --- src/pwm.rs | 186 ----------------------------------------------------- 1 file changed, 186 deletions(-) delete mode 100644 src/pwm.rs diff --git a/src/pwm.rs b/src/pwm.rs deleted file mode 100644 index a63036aa..00000000 --- a/src/pwm.rs +++ /dev/null @@ -1,186 +0,0 @@ -use crate::println; -use embedded_hal::Pwm; -use riot_sys::pwm_t; - -/// PWM modes -/// -/// [`PWMMode::CENTER`] exists in RIOT but is marked as "not supported" -#[derive(Debug)] -pub enum PWMMode { - LEFT, - RIGHT, - CENTER, -} - -impl PWMMode { - /// Converts the rust enum to a c type - pub fn to_c(self) -> riot_sys::pwm_mode_t { - match self { - Self::LEFT => riot_sys::pwm_mode_t_PWM_LEFT, - Self::RIGHT => riot_sys::pwm_mode_t_PWM_RIGHT, - Self::CENTER => riot_sys::pwm_mode_t_PWM_CENTER, - } - } -} - -#[derive(Debug)] -pub struct PWMDevice { - dev: pwm_t, - channel_duty_values: [u16; CHANNELS], //Needed because of embedded_hal implementation - frequency: u32, //Needed because of embedded_hal implementation -} - -pub type Hz = u32; - -impl PWMDevice { - /// Creates and initializes a [`PWMDevice`] with the given frequency in Hz and the resolution of a period/duty cycle - /// - /// If the given frequency and resolution values can not be acchieved on the current device, the - /// resolution will be kept the same while the frequency will be lowered until the device can handle the combination. - /// The actual set frequency can the be obtained with [`PWMDevice::get_frequency`]. - /// - /// Returns the initialized [`PWMDevice`] on success - /// - /// **Note** that the `CHANNELS` Variable is only used to cache the duty values for each of the `CHANNELS` number of - /// pwm channels. This is used by [`embedded_hal::Pwm::get_duty`]. If this is not needed setting `CHANNELS` to `0` saves memory. - pub fn new(dev: pwm_t, mode: PWMMode, frequency: Hz, resolution: u16) -> Result { - let mut pwm_dev = PWMDevice { - dev, - channel_duty_values: [0; CHANNELS], - frequency, - }; - - match pwm_dev.init(mode, frequency, resolution) { - Ok(()) => Ok(pwm_dev), - // Sadly RIOTs interface does not allow for a more elaborate error handling. - // Maybe something like errno is set without the doc telling us, which could be used here. - Err(()) => Err(()), - } - } - - /// Creates a PWM device from an already initialized c type. - /// - /// **Note** that the `CHANNELS` Variable is only used to cache the duty values for each of the `CHANNELS` number of - /// pwm channels. This is used by [`embedded_hal::Pwm::get_duty`]. If this is not needed setting `CHANNELS` to `0` saves memory. - /// - /// ## Important: - /// It is **important** to make sure that the provided **device is already initialized** by using [`riot_sys::pwm_init`](https://rustdoc.etonomy.org/riot_sys/fn.pwm_init.html). - /// Using the returned device otherwise results in **undefined behavior**! - /// - /// Also note, that the given frequency is only to be used by [`embedded_hal::Pwm::get_period`] or [`PWMDevice::get_frequency`]. - /// Just setting this to `x` will only result in those two functions returning `x` but will have no other impact, if - /// for example the given [`pwm_t`] is initialized by a board and the actually set frequency is unknown. - /// - pub unsafe fn new_without_init(dev: pwm_t, frequency: Hz) -> Self { - PWMDevice { - dev, - channel_duty_values: [0; CHANNELS], - frequency, - } - } - - /// Initializes the [`PWMDevice`] with the given frequency in Hz and resolution of a period/duty cycle. - /// - /// If the given frequency and resolution values can not be acchieved on the current device, the - /// resolution will be kept the same while the frequency will be lowered until the device can handle the combination. - /// The resulting frequency is written into the [`PWMDevice`]. - /// - /// Uses a [`Result`] in anticipation of a more usefull error handling in the future. - /// - /// Returns if the initialization was a success - fn init(&mut self, mode: PWMMode, frequency: Hz, resolution: u16) -> Result<(), ()> { - let err = unsafe { riot_sys::pwm_init(self.dev, mode.to_c(), frequency, resolution) }; - - match err { - 0 => Err(()), - freq => { - // Set frequency in the device - self.frequency = freq; - Ok(()) - } - } - } - - /// Returns the number of available channels for this device - pub fn channels(&self) -> u8 { - unsafe { riot_sys::pwm_channels(self.dev).min(CHANNELS as u8) } - } - - /// Stops PWM generation on this device - pub fn power_off(&mut self) { - unsafe { riot_sys::pwm_poweroff(self.dev) } - } - - /// Resumes PWM generation after power_off on this device - pub fn power_on(&mut self) { - unsafe { - riot_sys::pwm_poweron(self.dev); - } - } - - /// Sets the duty-cycle for the given channel - /// - /// value: `0: 0%, u16::MAX: 100%` duty_cycle - pub fn set(&mut self, channel: u8, value: u16) { - unsafe { - riot_sys::pwm_set(self.dev, channel, value); - } - - let channel = channel as usize; - // Ignore if entry does not exists because - // only embedded_hal interface cares about those values - // and the implementation already checks for this - if channel < (CHANNELS) { - self.channel_duty_values[channel] = value; - } - } - - /// Returns the used frequency of this [`PWMDevice`] in Hz - /// - pub fn get_frequency(&self) -> u32 { - self.frequency - } -} - -pub type Seconds = f32; - -impl Pwm for PWMDevice { - type Channel = u8; - type Duty = u16; - type Time = Seconds; - - fn disable(&mut self, _channel: Self::Channel) { - println!("[ERROR] RIOT does not support enabling/disabling single channels") - } - - fn enable(&mut self, _channel: Self::Channel) { - println!("[ERROR] RIOT does not support enabling/disabling single channels") - } - - fn get_period(&self) -> Self::Time { - 1. / self.frequency as f32 - } - - fn get_duty(&self, channel: Self::Channel) -> Self::Duty { - self.channel_duty_values[channel as usize] - } - - fn get_max_duty(&self) -> Self::Duty { - u16::MAX - } - - fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { - if (channel as usize) >= CHANNELS { - panic!("Tried to set duty for non existing channel: {channel}") - } - - self.set(channel, duty); - } - - fn set_period

(&mut self, _period: P) - where - P: Into, - { - println!("RIOT does not support setting the period after initialisation") - } -} From 4d917bfe9cf84214919aa1b1bab0abbb22104077 Mon Sep 17 00:00:00 2001 From: Kilian Barning Date: Thu, 9 Feb 2023 15:20:07 +0100 Subject: [PATCH 19/41] Removed wrong libs --- src/lib.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e9b252e6..430d88f1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -121,12 +121,6 @@ pub mod uart; #[cfg(riot_module_periph_adc)] pub mod adc; -#[cfg(riot_module_periph_pwm)] -pub mod pwm; - -#[cfg(riot_module_periph_dac)] -pub mod dac; - #[cfg(riot_module_ztimer)] pub mod ztimer; From 6b40cd913c1526a9857c0431c5d4bc0444729c43 Mon Sep 17 00:00:00 2001 From: Kilian Barning <50280188+kbarning@users.noreply.github.com> Date: Fri, 10 Feb 2023 17:01:03 +0100 Subject: [PATCH 20/41] Removed unused comments --- src/uart.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/uart.rs b/src/uart.rs index 8b64f8eb..ec24815b 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -153,7 +153,6 @@ impl<'a> UartDevice<'a> { /// # Examples /// ``` /// use riot_wrappers::uart::UartDevice; - /// let mut received_data = 0u8; /// let mut uart = UartDevice::new_without_rx(uart_type_t_STM32_USART, 115200) /// .unwrap_or_else(|e| panic!("Error initializing UART: {e:?}")); /// uart.write(b"Hello from UART"); From fd5d9b3cd2bc2ce843bc546c0d8ff940397ddd31 Mon Sep 17 00:00:00 2001 From: Kilian Barning <50280188+kbarning@users.noreply.github.com> Date: Fri, 10 Feb 2023 17:02:07 +0100 Subject: [PATCH 21/41] Update uart.rs --- src/uart.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/uart.rs b/src/uart.rs index ec24815b..c68b551a 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -180,7 +180,6 @@ impl<'a> UartDevice<'a> { /// # Examples /// ``` /// use riot_wrappers::uart::{DataBits, Parity, StopBits, UartDevice}; - /// let mut received_data = 0u8; /// let mut uart = UartDevice::new_without_rx(uart_type_t_STM32_USART, 115200) /// .unwrap_or_else(|e| panic!("Error initializing UART: {e:?}")); /// uart.set_mode(DataBits::Eight, Parity::None, StopBits::One) From fd709317469b489c06f87d6daaf1ffcd18dcd632 Mon Sep 17 00:00:00 2001 From: Kilian Barning Date: Wed, 15 Feb 2023 18:15:23 +0100 Subject: [PATCH 22/41] Fixed issues as suggested by chrysn --- Cargo.toml | 5 +-- src/uart.rs | 92 ++++++++++++++++++++++++----------------------------- 2 files changed, 43 insertions(+), 54 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7bd4d97d..fbb350ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -89,7 +89,4 @@ actual_never_type = [] # # This has some effects of its own (making ValueInThread fundamental), and also # enables actual_never_type. -nightly_docs = [ "actual_never_type" ] - -# On some plattforms, uart_set_mode isn't implemented yet -uart_set_mode = [] \ No newline at end of file +nightly_docs = [ "actual_never_type" ] \ No newline at end of file diff --git a/src/uart.rs b/src/uart.rs index c68b551a..2eed41af 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -3,37 +3,36 @@ //! Author: Kilian Barning #![allow(dead_code)] -use core::marker::PhantomData; use core::ptr; -use riot_sys::libc::{c_int, c_void}; +use crate::error::{NegativeErrorExt, NumericError}; +use riot_sys::libc::c_void; use riot_sys::*; /// This enum representatives the status returned by various `UART`-functions #[derive(Debug)] #[non_exhaustive] -pub enum UartDeviceStatus { - Success, +pub enum UartDeviceError { InvalidDevice, UnsupportedConfig, - Other(i32), + Other, } -impl UartDeviceStatus { +impl UartDeviceError { /// Converts the given `c_int` into the matching Enum representation - fn from_c(n: c_int) -> Self { - const _ENODEV: c_int = -(ENODEV as c_int); - const _ENOTSUP: c_int = -(ENOTSUP as c_int); - match n { - 0 => Self::Success, + fn from_c(n: NumericError) -> Self { + const _ENODEV: isize = -(ENODEV as isize); + const _ENOTSUP: isize = -(ENOTSUP as isize); + match n.number { _ENODEV => Self::InvalidDevice, _ENOTSUP => Self::UnsupportedConfig, - other => Self::Other(other), + _ => Self::Other, } } } #[derive(Debug)] +#[non_exhaustive] pub enum DataBits { Five, Six, @@ -53,6 +52,7 @@ impl DataBits { } #[derive(Debug)] +#[non_exhaustive] pub enum Parity { None, Even, @@ -73,8 +73,8 @@ impl Parity { } } - #[derive(Debug)] +#[non_exhaustive] pub enum StopBits { One, Two, @@ -93,12 +93,11 @@ impl StopBits { /// /// [UART implementation]: https://doc.riot-os.org/group__drivers__periph__uart.html #[derive(Debug)] -pub struct UartDevice<'a> { +pub struct UartDevice { dev: uart_t, - _marker: PhantomData<&'a ()>, // We use this `PhantomData` here to make sure that the lifetime of the borrowed closure is equal to this struct } -impl<'a> UartDevice<'a> { +impl UartDevice { /// Tries to initialize the given `UART`. Returns a Result with rather `Ok` if the UART was initialized successfully or a /// `Err` containing the error /// @@ -121,27 +120,25 @@ impl<'a> UartDevice<'a> { pub fn new( dev: uart_t, baud: u32, - user_callback: &'a mut F, - ) -> Result + user_callback: &'static mut F, + ) -> Result where - F: FnMut(u8), + F: FnMut(u8) + Sync + 'static, { unsafe { - match UartDeviceStatus::from_c(uart_init( + uart_init( dev, baud, Some(Self::new_data_callback::), user_callback as *mut _ as *mut c_void, - )) { - UartDeviceStatus::Success => Ok(Self { - dev, - _marker: PhantomData, - }), - status => Err(status), - } + ) } + .negative_to_error() + .map(|_| Self { dev }) + .map_err(UartDeviceError::from_c) } + /// Tries to initialize the given `UART`. Returns a Result with rather `Ok` if the UART was initialized successfully or a /// `Err` containing the error. As the name implies, the created `UART` device can ONLY send data /// @@ -157,19 +154,13 @@ impl<'a> UartDevice<'a> { /// .unwrap_or_else(|e| panic!("Error initializing UART: {e:?}")); /// uart.write(b"Hello from UART"); /// ``` - pub fn new_without_rx(dev: uart_t, baud: u32) -> Result { - unsafe { - match UartDeviceStatus::from_c(uart_init(dev, baud, None, ptr::null_mut())) { - UartDeviceStatus::Success => Ok(Self { - dev, - _marker: PhantomData, - }), - status => Err(status), - } - } + pub fn new_without_rx(dev: uart_t, baud: u32) -> Result { + unsafe { uart_init(dev, baud, None, ptr::null_mut()) } + .negative_to_error() + .map(|_| Self { dev }) + .map_err(UartDeviceError::from_c) } - /// Sets the mode according to the given parameters /// Should the parameters be invalid, the function returns a Err /// # Arguments @@ -185,21 +176,21 @@ impl<'a> UartDevice<'a> { /// uart.set_mode(DataBits::Eight, Parity::None, StopBits::One) /// .unwrap_or_else(|e| panic!("Error setting UART mode: {e:?}")); /// ``` - #[cfg(feature = "uart_set_mode")] + #[cfg(riot_module_periph_uart_modecfg)] pub fn set_mode( &mut self, data_bits: DataBits, parity: Parity, stop_bits: StopBits, - ) -> Result<(), UartDeviceStatus> { + ) -> Result<(), UartDeviceError> { unsafe { - match UartDeviceStatus::from_c(uart_mode( + match UartDeviceError::from_c(uart_mode( self.dev, data_bits.to_c(), parity.to_c(), stop_bits.to_c(), )) { - UartDeviceStatus::Success => Ok(()), + UartDeviceError::Success => Ok(()), status => Err(status), } } @@ -213,6 +204,7 @@ impl<'a> UartDevice<'a> { /// let mut uart = UartDevice::new_without_rx(uart_type_t_STM32_USART, 115200) /// .unwrap_or_else(|e| panic!("Error initializing UART: {e:?}")); /// uart.write(b"Hello from UART\n"); + /// ``` pub fn write(&mut self, data: &[u8]) { unsafe { uart_write(self.dev, data.as_ptr(), data.len() as size_t); @@ -269,8 +261,8 @@ impl<'a> UartDevice<'a> { /// Get the TX pin #[cfg(riot_module_periph_uart_reconfigure)] - pub fn get_pin_tx(&mut self) -> gpio_t { - unsafe { uart_pin_tx(self.dev) } + pub fn get_pin_tx(&mut self) -> GPIO { + GPIO::from_c(unsafe { uart_pin_tx(self.dev) }).unwrap() // Is this always safe? Maybe use unwrap_unchecked for better performance } /// Configure the function that will be called when a start condition is detected @@ -280,7 +272,7 @@ impl<'a> UartDevice<'a> { #[cfg(riot_module_periph_uart_rxstart_irq)] pub fn rxstart_irq_configure(&mut self, user_fxopt: &'a mut F) where - F: FnMut(), + F: FnMut() + Send + 'static, { unsafe { uart_rxstart_irq_configure( @@ -309,9 +301,9 @@ impl<'a> UartDevice<'a> { /// * `data` - The newly received data from the `UART` unsafe extern "C" fn new_data_callback(user_callback: *mut c_void, data: u8) where - F: FnMut(u8), + F: FnMut(u8) + 'static, { - (*(user_callback as *mut F))(data); //We cast the void* back to the closure and call it + (*(user_callback as *mut F))(data); // We cast the void* back to the closure and call it } /// This is the callback that gets called directly from the kernel when a start condition is detected @@ -320,13 +312,13 @@ impl<'a> UartDevice<'a> { #[cfg(riot_module_periph_uart_rxstart_irq)] unsafe extern "C" fn rxstart_callback(user_callback: *mut c_void) where - F: FnMut(), + F: FnMut() + 'static, { - (*(user_callback as *mut F))(); //We cast the void* back to the closure and call it + (*(user_callback as *mut F))(); // We cast the void* back to the closure and call it } } -impl<'a> Drop for UartDevice<'a> { +impl Drop for UartDevice { /// The `drop` method resets the `UART`, removes the interrupt and tries /// to reset the `GPIO` pins if possible fn drop(&mut self) { From 0c1f2dd1f79eb1b1173bdb5848f987778cc045ed Mon Sep 17 00:00:00 2001 From: Kilian Barning Date: Wed, 15 Feb 2023 18:22:02 +0100 Subject: [PATCH 23/41] Add newline to cargo.toml --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index fbb350ab..df8de3fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -89,4 +89,4 @@ actual_never_type = [] # # This has some effects of its own (making ValueInThread fundamental), and also # enables actual_never_type. -nightly_docs = [ "actual_never_type" ] \ No newline at end of file +nightly_docs = [ "actual_never_type" ] From 055fa14800fae40fa104d384ed425fdce87f9e60 Mon Sep 17 00:00:00 2001 From: Kilian Barning Date: Sun, 19 Feb 2023 04:00:02 +0100 Subject: [PATCH 24/41] Added new macro to init uart --- src/uart.rs | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/uart.rs b/src/uart.rs index 2eed41af..62459209 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -6,7 +6,7 @@ use core::ptr; use crate::error::{NegativeErrorExt, NumericError}; -use riot_sys::libc::c_void; +use riot_sys::libc::{c_uint, c_void}; use riot_sys::*; /// This enum representatives the status returned by various `UART`-functions @@ -113,12 +113,12 @@ impl UartDevice { /// let mut cb = |new_data| { /// //do something here with the received data /// }; - /// let mut uart = UartDevice::new(uart_type_t_STM32_USART, 115200, &mut cb) + /// let mut uart = UartDevice::new(0, 115200, &mut cb) /// .unwrap_or_else(|e| panic!("Error initializing UART: {e:?}")); /// uart.write(b"Hello from UART"); /// ``` pub fn new( - dev: uart_t, + index: usize, baud: u32, user_callback: &'static mut F, ) -> Result @@ -126,16 +126,17 @@ impl UartDevice { F: FnMut(u8) + Sync + 'static, { unsafe { + let dev = macro_UART_DEV(index as c_uint); uart_init( dev, baud, Some(Self::new_data_callback::), user_callback as *mut _ as *mut c_void, ) + .negative_to_error() + .map(|_| Self { dev }) + .map_err(UartDeviceError::from_c) } - .negative_to_error() - .map(|_| Self { dev }) - .map_err(UartDeviceError::from_c) } @@ -150,15 +151,18 @@ impl UartDevice { /// # Examples /// ``` /// use riot_wrappers::uart::UartDevice; - /// let mut uart = UartDevice::new_without_rx(uart_type_t_STM32_USART, 115200) + /// let mut uart = UartDevice::new_without_rx(0, 115200) /// .unwrap_or_else(|e| panic!("Error initializing UART: {e:?}")); /// uart.write(b"Hello from UART"); /// ``` - pub fn new_without_rx(dev: uart_t, baud: u32) -> Result { - unsafe { uart_init(dev, baud, None, ptr::null_mut()) } - .negative_to_error() - .map(|_| Self { dev }) - .map_err(UartDeviceError::from_c) + pub fn new_without_rx(index: usize, baud: u32) -> Result { + unsafe { + let dev = macro_UART_DEV(index as c_uint); + uart_init(dev, baud, None, ptr::null_mut()) + .negative_to_error() + .map(|_| Self { dev }) + .map_err(UartDeviceError::from_c) + } } /// Sets the mode according to the given parameters From 66de5f01043f1de618bf8a334fcfc105f3ee643d Mon Sep 17 00:00:00 2001 From: Kilian Barning Date: Sun, 19 Feb 2023 04:01:44 +0100 Subject: [PATCH 25/41] Fix comments --- src/uart.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uart.rs b/src/uart.rs index 62459209..68397b92 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -175,7 +175,7 @@ impl UartDevice { /// # Examples /// ``` /// use riot_wrappers::uart::{DataBits, Parity, StopBits, UartDevice}; - /// let mut uart = UartDevice::new_without_rx(uart_type_t_STM32_USART, 115200) + /// let mut uart = UartDevice::new_without_rx(0, 115200) /// .unwrap_or_else(|e| panic!("Error initializing UART: {e:?}")); /// uart.set_mode(DataBits::Eight, Parity::None, StopBits::One) /// .unwrap_or_else(|e| panic!("Error setting UART mode: {e:?}")); @@ -205,7 +205,7 @@ impl UartDevice { /// # Examples /// ``` /// use riot_wrappers::uart::UartDevice; - /// let mut uart = UartDevice::new_without_rx(uart_type_t_STM32_USART, 115200) + /// let mut uart = UartDevice::new_without_rx(0, 115200) /// .unwrap_or_else(|e| panic!("Error initializing UART: {e:?}")); /// uart.write(b"Hello from UART\n"); /// ``` From 1b87e047a418c949944318b8664ba40528e2e4ad Mon Sep 17 00:00:00 2001 From: Kilian Barning Date: Sun, 19 Feb 2023 04:24:37 +0100 Subject: [PATCH 26/41] Added scoped approach --- src/uart.rs | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/uart.rs b/src/uart.rs index 68397b92..331152b2 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -3,6 +3,7 @@ //! Author: Kilian Barning #![allow(dead_code)] +use core::marker::PhantomData; use core::ptr; use crate::error::{NegativeErrorExt, NumericError}; @@ -93,11 +94,12 @@ impl StopBits { /// /// [UART implementation]: https://doc.riot-os.org/group__drivers__periph__uart.html #[derive(Debug)] -pub struct UartDevice { +pub struct UartDevice<'scope> { dev: uart_t, + _scope: PhantomData<&'scope ()>, } -impl UartDevice { +impl<'scope> UartDevice<'scope> { /// Tries to initialize the given `UART`. Returns a Result with rather `Ok` if the UART was initialized successfully or a /// `Err` containing the error /// @@ -120,10 +122,10 @@ impl UartDevice { pub fn new( index: usize, baud: u32, - user_callback: &'static mut F, + user_callback: &'scope mut F, ) -> Result where - F: FnMut(u8) + Sync + 'static, + F: FnMut(u8) + Sync + 'scope, { unsafe { let dev = macro_UART_DEV(index as c_uint); @@ -134,7 +136,10 @@ impl UartDevice { user_callback as *mut _ as *mut c_void, ) .negative_to_error() - .map(|_| Self { dev }) + .map(|_| Self { + dev, + _scope: PhantomData, + }) .map_err(UartDeviceError::from_c) } } @@ -160,7 +165,10 @@ impl UartDevice { let dev = macro_UART_DEV(index as c_uint); uart_init(dev, baud, None, ptr::null_mut()) .negative_to_error() - .map(|_| Self { dev }) + .map(|_| Self { + dev, + _scope: PhantomData, + }) .map_err(UartDeviceError::from_c) } } @@ -305,7 +313,7 @@ impl UartDevice { /// * `data` - The newly received data from the `UART` unsafe extern "C" fn new_data_callback(user_callback: *mut c_void, data: u8) where - F: FnMut(u8) + 'static, + F: FnMut(u8) + 'scope, { (*(user_callback as *mut F))(data); // We cast the void* back to the closure and call it } @@ -316,13 +324,13 @@ impl UartDevice { #[cfg(riot_module_periph_uart_rxstart_irq)] unsafe extern "C" fn rxstart_callback(user_callback: *mut c_void) where - F: FnMut() + 'static, + F: FnMut() + 'scope, { (*(user_callback as *mut F))(); // We cast the void* back to the closure and call it } } -impl Drop for UartDevice { +impl<'scope> Drop for UartDevice<'scope> { /// The `drop` method resets the `UART`, removes the interrupt and tries /// to reset the `GPIO` pins if possible fn drop(&mut self) { From a98b8e36ae7733d1436ff9511be9d7b9d1af2b30 Mon Sep 17 00:00:00 2001 From: Kilian Barning Date: Sun, 19 Feb 2023 04:39:02 +0100 Subject: [PATCH 27/41] Add static new --- src/uart.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/uart.rs b/src/uart.rs index 331152b2..2361f637 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -144,6 +144,36 @@ impl<'scope> UartDevice<'scope> { } } + /// Tries to initialize the given `UART` with a static callback. Returns a Result with rather `Ok` if the UART + /// was initialized successfully or a `Err` containing the error + /// + /// # Arguments + /// + /// * `dev` - The uart_t handle to the hardware device + /// * `baud` - The used baud rate + /// * `user_callback` The user defined callback that gets called from the os whenever new data is received from the `UART` + /// + /// # Examples + /// ``` + /// use riot_wrappers::uart::UartDevice; + /// static mut CB: fn(u8) = |new_data| { + /// //do something here with the received data + /// }; + /// let mut uart = UartDevice::new_with_static_cb(0, 115200, unsafe { &mut CB }) + /// .unwrap_or_else(|e| panic!("Error initializing UART: {e:?}")); + /// uart.write(b"Hello from UART"); + /// ``` + pub fn new_with_static_cb( + index: usize, + baud: u32, + user_callback: &'static mut F, + ) -> Result + where + F: FnMut(u8) + Sync + 'static, + { + Self::new(index, baud, user_callback) + } + /// Tries to initialize the given `UART`. Returns a Result with rather `Ok` if the UART was initialized successfully or a /// `Err` containing the error. As the name implies, the created `UART` device can ONLY send data From 6de785ca2e71c612fac23b0a7c19ac8260295a75 Mon Sep 17 00:00:00 2001 From: Kilian Barning Date: Sun, 19 Feb 2023 20:12:25 +0100 Subject: [PATCH 28/41] Added new static impl + fix doc --- src/uart.rs | 67 +++++++++++++++++++++++++++-------------------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/src/uart.rs b/src/uart.rs index 2361f637..da773b86 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -105,7 +105,7 @@ impl<'scope> UartDevice<'scope> { /// /// # Arguments /// - /// * `dev` - The uart_t handle to the hardware device + /// * `dev` - The index of the hardware device /// * `baud` - The used baud rate /// * `user_callback` The user defined callback that gets called from the os whenever new data is received from the `UART` /// @@ -144,43 +144,12 @@ impl<'scope> UartDevice<'scope> { } } - /// Tries to initialize the given `UART` with a static callback. Returns a Result with rather `Ok` if the UART - /// was initialized successfully or a `Err` containing the error - /// - /// # Arguments - /// - /// * `dev` - The uart_t handle to the hardware device - /// * `baud` - The used baud rate - /// * `user_callback` The user defined callback that gets called from the os whenever new data is received from the `UART` - /// - /// # Examples - /// ``` - /// use riot_wrappers::uart::UartDevice; - /// static mut CB: fn(u8) = |new_data| { - /// //do something here with the received data - /// }; - /// let mut uart = UartDevice::new_with_static_cb(0, 115200, unsafe { &mut CB }) - /// .unwrap_or_else(|e| panic!("Error initializing UART: {e:?}")); - /// uart.write(b"Hello from UART"); - /// ``` - pub fn new_with_static_cb( - index: usize, - baud: u32, - user_callback: &'static mut F, - ) -> Result - where - F: FnMut(u8) + Sync + 'static, - { - Self::new(index, baud, user_callback) - } - - /// Tries to initialize the given `UART`. Returns a Result with rather `Ok` if the UART was initialized successfully or a /// `Err` containing the error. As the name implies, the created `UART` device can ONLY send data /// /// # Arguments /// - /// * `dev` - The uart_t handle to the hardware device + /// * `dev` - The index of the hardware device /// * `baud` - The used baud rate /// /// # Examples @@ -371,3 +340,35 @@ impl<'scope> Drop for UartDevice<'scope> { } } } + +impl UartDevice<'static> { + /// Tries to initialize the given `UART` with a static callback. Returns a Result with rather `Ok` if the UART + /// was initialized successfully or a `Err` containing the error + /// + /// # Arguments + /// + /// * `dev` - The index of the hardware device + /// * `baud` - The used baud rate + /// * `user_callback` The user defined callback that gets called from the os whenever new data is received from the `UART` + /// + /// # Examples + /// ``` + /// use riot_wrappers::uart::UartDevice; + /// static mut CB: fn(u8) = |new_data| { + /// //do something here with the received data + /// }; + /// let mut uart = UartDevice::new_with_static_cb(0, 115200, unsafe { &mut CB }) + /// .unwrap_or_else(|e| panic!("Error initializing UART: {e:?}")); + /// uart.write(b"Hello from UART"); + /// ``` + pub fn new_with_static_cb( + index: usize, + baud: u32, + user_callback: &'static mut F, + ) -> Result + where + F: FnMut(u8) + Sync + 'static, + { + Self::new(index, baud, user_callback) + } +} From 0fc9d443064be7d28dd4cf056e98bfa44cbaa9d7 Mon Sep 17 00:00:00 2001 From: Kilian Barning Date: Mon, 27 Feb 2023 21:06:08 +0100 Subject: [PATCH 29/41] Make get gpio optional --- src/uart.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/uart.rs b/src/uart.rs index da773b86..0e9db4b9 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -266,14 +266,14 @@ impl<'scope> UartDevice<'scope> { /// Get the RX pin #[cfg(riot_module_periph_uart_reconfigure)] - pub fn get_pin_rx(&mut self) -> gpio_t { - unsafe { uart_pin_rx(self.dev) } + pub fn get_pin_rx(&mut self) -> Option { + crate::gpio::GPIO::from_c(unsafe { uart_pin_rx(self.dev) }) } /// Get the TX pin #[cfg(riot_module_periph_uart_reconfigure)] - pub fn get_pin_tx(&mut self) -> GPIO { - GPIO::from_c(unsafe { uart_pin_tx(self.dev) }).unwrap() // Is this always safe? Maybe use unwrap_unchecked for better performance + pub fn get_pin_tx(&mut self) -> Option { + crate::gpio::GPIO::from_c(unsafe { uart_pin_tx(self.dev) }) } /// Configure the function that will be called when a start condition is detected From 3ff8dd00b4b59300d1b29c0d42a8cfbe2d346158 Mon Sep 17 00:00:00 2001 From: Kilian Barning Date: Mon, 27 Feb 2023 21:16:01 +0100 Subject: [PATCH 30/41] Remove colission detection for now --- src/uart.rs | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/uart.rs b/src/uart.rs index 0e9db4b9..8b2c6be1 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -232,24 +232,6 @@ impl<'scope> UartDevice<'scope> { unsafe { uart_poweroff(self.dev) }; } - /// Enables collision detection check - #[cfg(riot_module_periph_uart_collision)] - pub fn collision_detect_enable(&mut self) { - unsafe { uart_collision_detect_enable(self.dev) }; - } - - /// Disables collision detection check - #[cfg(riot_module_periph_uart_collision)] - pub fn collision_detect_disable(&mut self) { - unsafe { uart_collision_detect_disable(self.dev) }; - } - - /// Disables collision detection and returns if a collision occurred during last transfer - #[cfg(riot_module_periph_uart_collision)] - pub fn collision_detected(&mut self) -> bool { - unsafe { uart_collision_detected(self.dev) } - } - /// This function normally does not need to be called. But in some case, the pins on the `UART` /// might be shared with some other functionality (like `GPIO`). In this case, it is necessary /// to give the user the possibility to init the pins again. From a9231a001d8cc80d2a101ee4655f972a64c67f47 Mon Sep 17 00:00:00 2001 From: Kilian Barning Date: Fri, 17 Mar 2023 19:26:14 +0100 Subject: [PATCH 31/41] Add new scoped main --- src/uart.rs | 66 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 21 deletions(-) diff --git a/src/uart.rs b/src/uart.rs index 8b2c6be1..8797860d 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -100,26 +100,7 @@ pub struct UartDevice<'scope> { } impl<'scope> UartDevice<'scope> { - /// Tries to initialize the given `UART`. Returns a Result with rather `Ok` if the UART was initialized successfully or a - /// `Err` containing the error - /// - /// # Arguments - /// - /// * `dev` - The index of the hardware device - /// * `baud` - The used baud rate - /// * `user_callback` The user defined callback that gets called from the os whenever new data is received from the `UART` - /// - /// # Examples - /// ``` - /// use riot_wrappers::uart::UartDevice; - /// let mut cb = |new_data| { - /// //do something here with the received data - /// }; - /// let mut uart = UartDevice::new(0, 115200, &mut cb) - /// .unwrap_or_else(|e| panic!("Error initializing UART: {e:?}")); - /// uart.write(b"Hello from UART"); - /// ``` - pub fn new( + fn construct_uart( index: usize, baud: u32, user_callback: &'scope mut F, @@ -144,6 +125,49 @@ impl<'scope> UartDevice<'scope> { } } + + /// Tries to initialize the given `UART`. Returns a Result with rather `Ok` if the UART was initialized successfully or a + /// `Err` containing the error + /// + /// This is the scoped version of [`new()`] that can be used if you want to use short-lived callbacks, such as + /// closures or anything containing references. The UartDevice is deconfigured when the internal main function + /// terminates. A common pattern around this kind of scoped functions is that `main` contains the application's + /// main loop, and never terminates (in which case the clean-up code is eliminated during compilation). + /// # Arguments + /// + /// * `dev` - The index of the hardware device + /// * `baud` - The used baud rate + /// * `user_callback` The user defined callback that gets called from the os whenever new data is received from the `UART` + /// * `main` The mainloop that is executed inside the wrapper + /// + /// # Examples + /// ``` + ///use riot_wrappers::uart::UartDevice; + ///let mut cb = |new_data| { + /// //do something here with the received data + ///}; + ///let mut scoped_main = |self_: &mut UartDevice| loop { + /// self_.write(b"Hello from UART") + ///}; + ///let mut uart = UartDevice::new_scoped(0, 115200, &mut cb, scoped_main) + /// .unwrap_or_else(|e| panic!("Error initializing UART: {e:?}")); + /// ``` + pub fn new_scoped( + index: usize, + baud: u32, + user_callback: &'scope mut F, + main: Main, + ) -> Result + where + F: FnMut(u8) + Sync + 'scope, + Main: FnOnce(&mut Self) -> RMain, + { + let mut self_ = Self::construct_uart(index, baud, user_callback)?; + let result = (main)(&mut self_); + drop(self_); + Ok(result) + } + /// Tries to initialize the given `UART`. Returns a Result with rather `Ok` if the UART was initialized successfully or a /// `Err` containing the error. As the name implies, the created `UART` device can ONLY send data /// @@ -351,6 +375,6 @@ impl UartDevice<'static> { where F: FnMut(u8) + Sync + 'static, { - Self::new(index, baud, user_callback) + Self::construct_uart(index, baud, user_callback) } } From c3afaf41315b9a502e35b5dcd2c95b90a6310fc7 Mon Sep 17 00:00:00 2001 From: Kilian Barning <50280188+kbarning@users.noreply.github.com> Date: Tue, 21 Mar 2023 19:53:51 +0100 Subject: [PATCH 32/41] Update src/uart.rs Co-authored-by: chrysn --- src/uart.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/uart.rs b/src/uart.rs index 8797860d..6b1f57e3 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -162,6 +162,7 @@ impl<'scope> UartDevice<'scope> { F: FnMut(u8) + Sync + 'scope, Main: FnOnce(&mut Self) -> RMain, { + // This possibly relies on Rust code in RIOT to not unwind. let mut self_ = Self::construct_uart(index, baud, user_callback)?; let result = (main)(&mut self_); drop(self_); From 7aff5f728610708cb1a6439df29d1327e8b25401 Mon Sep 17 00:00:00 2001 From: Kilian Barning <50280188+kbarning@users.noreply.github.com> Date: Tue, 21 Mar 2023 19:54:21 +0100 Subject: [PATCH 33/41] Update src/uart.rs Co-authored-by: chrysn --- src/uart.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uart.rs b/src/uart.rs index 6b1f57e3..6b729f54 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -106,7 +106,7 @@ impl<'scope> UartDevice<'scope> { user_callback: &'scope mut F, ) -> Result where - F: FnMut(u8) + Sync + 'scope, + F: FnMut(u8) + Send + 'scope, { unsafe { let dev = macro_UART_DEV(index as c_uint); From 5a6ea25ee6849a565bc1a9cd2f386c9f9c76898e Mon Sep 17 00:00:00 2001 From: Kilian Barning <50280188+kbarning@users.noreply.github.com> Date: Tue, 21 Mar 2023 19:54:33 +0100 Subject: [PATCH 34/41] Update src/uart.rs Co-authored-by: chrysn --- src/uart.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uart.rs b/src/uart.rs index 6b729f54..f1dae336 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -159,7 +159,7 @@ impl<'scope> UartDevice<'scope> { main: Main, ) -> Result where - F: FnMut(u8) + Sync + 'scope, + F: FnMut(u8) + Send + 'scope, Main: FnOnce(&mut Self) -> RMain, { // This possibly relies on Rust code in RIOT to not unwind. From cc63b4f7187534168850f56ad98ee76d6d90ffa8 Mon Sep 17 00:00:00 2001 From: Kilian Barning <50280188+kbarning@users.noreply.github.com> Date: Tue, 21 Mar 2023 19:54:53 +0100 Subject: [PATCH 35/41] Update src/uart.rs Co-authored-by: chrysn --- src/uart.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/uart.rs b/src/uart.rs index f1dae336..0691bb63 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -1,7 +1,6 @@ //! Access to [RIOT's UART](https://doc.riot-os.org/group__drivers__periph__uart.html) //! //! Author: Kilian Barning -#![allow(dead_code)] use core::marker::PhantomData; use core::ptr; From 83856a1d9035db9eea7f5dc5762c3a60077d10cb Mon Sep 17 00:00:00 2001 From: Kilian Barning <50280188+kbarning@users.noreply.github.com> Date: Tue, 21 Mar 2023 19:55:06 +0100 Subject: [PATCH 36/41] Update src/uart.rs Co-authored-by: chrysn --- src/uart.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uart.rs b/src/uart.rs index 0691bb63..977e6dff 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -373,7 +373,7 @@ impl UartDevice<'static> { user_callback: &'static mut F, ) -> Result where - F: FnMut(u8) + Sync + 'static, + F: FnMut(u8) + Send + 'static, { Self::construct_uart(index, baud, user_callback) } From b6eada927c77089ff597d62a27b98a85d20725e8 Mon Sep 17 00:00:00 2001 From: Kilian Barning Date: Tue, 21 Mar 2023 20:16:52 +0100 Subject: [PATCH 37/41] Make internal construct fn unsafe --- src/uart.rs | 51 +++++++++++++++++++++++++-------------------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/src/uart.rs b/src/uart.rs index 977e6dff..95b4c6f6 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -99,7 +99,8 @@ pub struct UartDevice<'scope> { } impl<'scope> UartDevice<'scope> { - fn construct_uart( + /// Unsafety: To use this safely, the caller must ensure that the returned Self is destructed before &'scope mut F becomes unavailable. + unsafe fn construct_uart( index: usize, baud: u32, user_callback: &'scope mut F, @@ -107,26 +108,24 @@ impl<'scope> UartDevice<'scope> { where F: FnMut(u8) + Send + 'scope, { - unsafe { - let dev = macro_UART_DEV(index as c_uint); - uart_init( - dev, - baud, - Some(Self::new_data_callback::), - user_callback as *mut _ as *mut c_void, - ) - .negative_to_error() - .map(|_| Self { - dev, - _scope: PhantomData, - }) - .map_err(UartDeviceError::from_c) - } + let dev = macro_UART_DEV(index as c_uint); + uart_init( + dev, + baud, + Some(Self::new_data_callback::), + user_callback as *mut _ as *mut c_void, + ) + .negative_to_error() + .map(|_| Self { + dev, + _scope: PhantomData, + }) + .map_err(UartDeviceError::from_c) } - /// Tries to initialize the given `UART`. Returns a Result with rather `Ok` if the UART was initialized successfully or a - /// `Err` containing the error + /// Tries to initialize the given `UART`. Returns a Result with rather `Ok` where `RMain` is the value returned by the scoped main function + /// or a `Err` containing the error /// /// This is the scoped version of [`new()`] that can be used if you want to use short-lived callbacks, such as /// closures or anything containing references. The UartDevice is deconfigured when the internal main function @@ -141,14 +140,14 @@ impl<'scope> UartDevice<'scope> { /// /// # Examples /// ``` - ///use riot_wrappers::uart::UartDevice; - ///let mut cb = |new_data| { + /// use riot_wrappers::uart::UartDevice; + /// let mut cb = |new_data| { /// //do something here with the received data - ///}; - ///let mut scoped_main = |self_: &mut UartDevice| loop { + /// }; + /// let mut scoped_main = |self_: &mut UartDevice| loop { /// self_.write(b"Hello from UART") - ///}; - ///let mut uart = UartDevice::new_scoped(0, 115200, &mut cb, scoped_main) + /// }; + /// let mut uart = UartDevice::new_scoped(0, 115200, &mut cb, scoped_main) /// .unwrap_or_else(|e| panic!("Error initializing UART: {e:?}")); /// ``` pub fn new_scoped( @@ -162,7 +161,7 @@ impl<'scope> UartDevice<'scope> { Main: FnOnce(&mut Self) -> RMain, { // This possibly relies on Rust code in RIOT to not unwind. - let mut self_ = Self::construct_uart(index, baud, user_callback)?; + let mut self_ = unsafe { Self::construct_uart(index, baud, user_callback) }?; let result = (main)(&mut self_); drop(self_); Ok(result) @@ -375,6 +374,6 @@ impl UartDevice<'static> { where F: FnMut(u8) + Send + 'static, { - Self::construct_uart(index, baud, user_callback) + unsafe { Self::construct_uart(index, baud, user_callback) } } } From bbfa1c329b78daf7995f676439d069d9e08b26c3 Mon Sep 17 00:00:00 2001 From: Kilian Barning Date: Tue, 21 Mar 2023 20:33:48 +0100 Subject: [PATCH 38/41] add test --- tests/uart/Cargo.toml | 20 ++++++++++++++++++++ tests/uart/Makefile | 8 ++++++++ tests/uart/src/lib.rs | 18 ++++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 tests/uart/Cargo.toml create mode 100644 tests/uart/Makefile create mode 100644 tests/uart/src/lib.rs diff --git a/tests/uart/Cargo.toml b/tests/uart/Cargo.toml new file mode 100644 index 00000000..03b4901f --- /dev/null +++ b/tests/uart/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "riot-wrappers-test-uart" +version = "0.1.0" +authors = ["Kilian Barning "] +edition = "2021" +publish = false + +[lib] +crate-type = ["staticlib"] + +[profile.release] +panic = "abort" + +[dependencies] +riot-wrappers = { version = "*", features = [ "set_panic_handler" ] } +riot-sys = "*" +embedded-hal = "0.2.4" + +[patch.crates-io] +riot-sys = { git = "https://github.com/RIOT-OS/rust-riot-sys" } diff --git a/tests/uart/Makefile b/tests/uart/Makefile new file mode 100644 index 00000000..55f95e98 --- /dev/null +++ b/tests/uart/Makefile @@ -0,0 +1,8 @@ +# name of your application +APPLICATION = riot-wrappers-test-uart +APPLICATION_RUST_MODULE = riot-wrappers-test-uart +BASELIBS += $(APPLICATION_RUST_MODULE).module +FEATURES_REQUIRED += rust_target +FEATURES_REQUIRED += periph_uart + +include $(RIOTBASE)/Makefile.include diff --git a/tests/uart/src/lib.rs b/tests/uart/src/lib.rs new file mode 100644 index 00000000..cc6cedcb --- /dev/null +++ b/tests/uart/src/lib.rs @@ -0,0 +1,18 @@ +#![no_std] + +use riot_wrappers::println; +use riot_wrappers::riot_main; +use riot_wrappers::uart; + +riot_main!(main); + +fn main() { + let mut cb = |new_data| { + //do something here with the received data + }; + let mut scoped_main = |self_: &mut UartDevice| loop { + self_.write(b"Hello from UART") + }; + let mut uart = UartDevice::new_scoped(0, 115200, &mut cb, scoped_main) + .unwrap_or_else(|e| panic!("Error initializing UART: {e:?}")); +} From cb8257f4a327d93772e7f58703f7cf449248ce47 Mon Sep 17 00:00:00 2001 From: Kilian Barning Date: Wed, 22 Mar 2023 00:52:14 +0100 Subject: [PATCH 39/41] Fix scope signature and add cfg to unused structs --- src/uart.rs | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/uart.rs b/src/uart.rs index 95b4c6f6..a5d31152 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -31,6 +31,7 @@ impl UartDeviceError { } } +#[cfg(riot_module_periph_uart_modecfg)] #[derive(Debug)] #[non_exhaustive] pub enum DataBits { @@ -40,6 +41,7 @@ pub enum DataBits { Eight, } +#[cfg(riot_module_periph_uart_modecfg)] impl DataBits { fn to_c(self) -> uart_data_bits_t { match self { @@ -51,6 +53,7 @@ impl DataBits { } } +#[cfg(riot_module_periph_uart_modecfg)] #[derive(Debug)] #[non_exhaustive] pub enum Parity { @@ -61,6 +64,7 @@ pub enum Parity { Space, } +#[cfg(riot_module_periph_uart_modecfg)] impl Parity { fn to_c(self) -> uart_parity_t { match self { @@ -73,6 +77,7 @@ impl Parity { } } +#[cfg(riot_module_periph_uart_modecfg)] #[derive(Debug)] #[non_exhaustive] pub enum StopBits { @@ -80,6 +85,7 @@ pub enum StopBits { Two, } +#[cfg(riot_module_periph_uart_modecfg)] impl StopBits { fn to_c(self) -> uart_stop_bits_t { match self { @@ -93,14 +99,13 @@ impl StopBits { /// /// [UART implementation]: https://doc.riot-os.org/group__drivers__periph__uart.html #[derive(Debug)] -pub struct UartDevice<'scope> { +pub struct UartDevice { dev: uart_t, - _scope: PhantomData<&'scope ()>, } -impl<'scope> UartDevice<'scope> { +impl UartDevice { /// Unsafety: To use this safely, the caller must ensure that the returned Self is destructed before &'scope mut F becomes unavailable. - unsafe fn construct_uart( + unsafe fn construct_uart<'scope, F>( index: usize, baud: u32, user_callback: &'scope mut F, @@ -112,14 +117,11 @@ impl<'scope> UartDevice<'scope> { uart_init( dev, baud, - Some(Self::new_data_callback::), + Some(Self::new_data_callback::<'scope, F>), user_callback as *mut _ as *mut c_void, ) .negative_to_error() - .map(|_| Self { - dev, - _scope: PhantomData, - }) + .map(|_| Self { dev }) .map_err(UartDeviceError::from_c) } @@ -150,7 +152,7 @@ impl<'scope> UartDevice<'scope> { /// let mut uart = UartDevice::new_scoped(0, 115200, &mut cb, scoped_main) /// .unwrap_or_else(|e| panic!("Error initializing UART: {e:?}")); /// ``` - pub fn new_scoped( + pub fn new_scoped<'scope, F, Main, RMain>( index: usize, baud: u32, user_callback: &'scope mut F, @@ -187,10 +189,7 @@ impl<'scope> UartDevice<'scope> { let dev = macro_UART_DEV(index as c_uint); uart_init(dev, baud, None, ptr::null_mut()) .negative_to_error() - .map(|_| Self { - dev, - _scope: PhantomData, - }) + .map(|_| Self { dev }) .map_err(UartDeviceError::from_c) } } @@ -315,7 +314,7 @@ impl<'scope> UartDevice<'scope> { /// # Arguments /// * `user_callback` - The address pointing to the user defined callback /// * `data` - The newly received data from the `UART` - unsafe extern "C" fn new_data_callback(user_callback: *mut c_void, data: u8) + unsafe extern "C" fn new_data_callback<'scope, F>(user_callback: *mut c_void, data: u8) where F: FnMut(u8) + 'scope, { @@ -334,7 +333,7 @@ impl<'scope> UartDevice<'scope> { } } -impl<'scope> Drop for UartDevice<'scope> { +impl Drop for UartDevice { /// The `drop` method resets the `UART`, removes the interrupt and tries /// to reset the `GPIO` pins if possible fn drop(&mut self) { @@ -346,7 +345,7 @@ impl<'scope> Drop for UartDevice<'scope> { } } -impl UartDevice<'static> { +impl UartDevice { /// Tries to initialize the given `UART` with a static callback. Returns a Result with rather `Ok` if the UART /// was initialized successfully or a `Err` containing the error /// From e0cd7941b42f2495142f830580dc3caa50b6eb4d Mon Sep 17 00:00:00 2001 From: Kilian Barning Date: Wed, 22 Mar 2023 00:55:20 +0100 Subject: [PATCH 40/41] Reordering --- src/uart.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/uart.rs b/src/uart.rs index a5d31152..91d01188 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -333,18 +333,6 @@ impl UartDevice { } } -impl Drop for UartDevice { - /// The `drop` method resets the `UART`, removes the interrupt and tries - /// to reset the `GPIO` pins if possible - fn drop(&mut self) { - unsafe { - uart_init(self.dev, 9600, None, ptr::null_mut()); - #[cfg(riot_module_periph_uart_reconfigure)] - self.deinit_pins(); - } - } -} - impl UartDevice { /// Tries to initialize the given `UART` with a static callback. Returns a Result with rather `Ok` if the UART /// was initialized successfully or a `Err` containing the error @@ -376,3 +364,15 @@ impl UartDevice { unsafe { Self::construct_uart(index, baud, user_callback) } } } + +impl Drop for UartDevice { + /// The `drop` method resets the `UART`, removes the interrupt and tries + /// to reset the `GPIO` pins if possible + fn drop(&mut self) { + unsafe { + uart_init(self.dev, 9600, None, ptr::null_mut()); + #[cfg(riot_module_periph_uart_reconfigure)] + self.deinit_pins(); + } + } +} From 0b0c4002f75d8954c21f0c0f8d9f6517c6e57d42 Mon Sep 17 00:00:00 2001 From: Kilian Barning Date: Wed, 22 Mar 2023 00:57:54 +0100 Subject: [PATCH 41/41] Cleanup --- src/uart.rs | 63 +++++++++++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 33 deletions(-) diff --git a/src/uart.rs b/src/uart.rs index 91d01188..74f9217f 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -2,7 +2,6 @@ //! //! Author: Kilian Barning -use core::marker::PhantomData; use core::ptr; use crate::error::{NegativeErrorExt, NumericError}; @@ -194,6 +193,36 @@ impl UartDevice { } } + /// Tries to initialize the given `UART` with a static callback. Returns a Result with rather `Ok` if the UART + /// was initialized successfully or a `Err` containing the error + /// + /// # Arguments + /// + /// * `dev` - The index of the hardware device + /// * `baud` - The used baud rate + /// * `user_callback` The user defined callback that gets called from the os whenever new data is received from the `UART` + /// + /// # Examples + /// ``` + /// use riot_wrappers::uart::UartDevice; + /// static mut CB: fn(u8) = |new_data| { + /// //do something here with the received data + /// }; + /// let mut uart = UartDevice::new_with_static_cb(0, 115200, unsafe { &mut CB }) + /// .unwrap_or_else(|e| panic!("Error initializing UART: {e:?}")); + /// uart.write(b"Hello from UART"); + /// ``` + pub fn new_with_static_cb( + index: usize, + baud: u32, + user_callback: &'static mut F, + ) -> Result + where + F: FnMut(u8) + Send + 'static, + { + unsafe { Self::construct_uart(index, baud, user_callback) } + } + /// Sets the mode according to the given parameters /// Should the parameters be invalid, the function returns a Err /// # Arguments @@ -333,38 +362,6 @@ impl UartDevice { } } -impl UartDevice { - /// Tries to initialize the given `UART` with a static callback. Returns a Result with rather `Ok` if the UART - /// was initialized successfully or a `Err` containing the error - /// - /// # Arguments - /// - /// * `dev` - The index of the hardware device - /// * `baud` - The used baud rate - /// * `user_callback` The user defined callback that gets called from the os whenever new data is received from the `UART` - /// - /// # Examples - /// ``` - /// use riot_wrappers::uart::UartDevice; - /// static mut CB: fn(u8) = |new_data| { - /// //do something here with the received data - /// }; - /// let mut uart = UartDevice::new_with_static_cb(0, 115200, unsafe { &mut CB }) - /// .unwrap_or_else(|e| panic!("Error initializing UART: {e:?}")); - /// uart.write(b"Hello from UART"); - /// ``` - pub fn new_with_static_cb( - index: usize, - baud: u32, - user_callback: &'static mut F, - ) -> Result - where - F: FnMut(u8) + Send + 'static, - { - unsafe { Self::construct_uart(index, baud, user_callback) } - } -} - impl Drop for UartDevice { /// The `drop` method resets the `UART`, removes the interrupt and tries /// to reset the `GPIO` pins if possible