Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UART: Add wrapper around RIOT's UART-interface #39

Open
wants to merge 47 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
17995a0
Added uart
kbarning Feb 1, 2023
dd1372a
Fixed callback
kbarning Feb 1, 2023
9dad81a
Merge branch 'implement_uart' into '6-uart-wrapper'
kbarning Feb 1, 2023
5e3c472
Fix code according to @jaschman
kbarning Feb 6, 2023
fc644cf
Add init pins
kbarning Feb 6, 2023
88ff4a6
DAC: Add wrapper around RIOTs DAC-interface
Feb 6, 2023
c6fbd33
Merge branch '3-dac-pdi-wrapper' into 'main'
Feb 6, 2023
79e8e23
Fix comments
kbarning Feb 6, 2023
78d412a
Merge branch 'upstream-merge-080223' into 'main'
Flole998 Feb 7, 2023
fa26004
PWM: Add wrapper around RIOTs PWM-interface
Feb 8, 2023
4aa6841
Merge branch '2-pwm-peripheral-driver-interface-wrapper' into 'main'
Feb 8, 2023
9be601c
Fixed signature
kbarning Feb 8, 2023
cfbe7c1
Fixed closure type
kbarning Feb 8, 2023
9bff3de
Mark init pins as unsafe
kbarning Feb 8, 2023
793614b
Fix comments
kbarning Feb 8, 2023
c6ef6d5
Change drop
kbarning Feb 8, 2023
c7cc626
Introduce Phantom Data Lifetimes
kbarning Feb 9, 2023
b1f76f0
Add generics to rxstart
kbarning Feb 9, 2023
c3738ca
Add mode feature
kbarning Feb 9, 2023
581729d
Merge branch 'main' into '6-uart-wrapper'
kbarning Feb 9, 2023
0b965d2
Added feature
kbarning Feb 9, 2023
11014dd
Delete dac.rs
kbarning Feb 9, 2023
b81ee74
Delete pwm.rs
kbarning Feb 9, 2023
4d917bf
Removed wrong libs
kbarning Feb 9, 2023
2896752
Merge remote-tracking branch 'origin/impl_uart_wrapper' into impl_uar…
kbarning Feb 9, 2023
6b40cd9
Removed unused comments
kbarning Feb 10, 2023
fd5d9b3
Update uart.rs
kbarning Feb 10, 2023
fd70931
Fixed issues as suggested by chrysn
kbarning Feb 15, 2023
0c1f2dd
Add newline to cargo.toml
kbarning Feb 15, 2023
055fa14
Added new macro to init uart
kbarning Feb 19, 2023
66de5f0
Fix comments
kbarning Feb 19, 2023
1b87e04
Added scoped approach
kbarning Feb 19, 2023
a98b8e3
Add static new
kbarning Feb 19, 2023
6de785c
Added new static impl + fix doc
kbarning Feb 19, 2023
0fc9d44
Make get gpio optional
kbarning Feb 27, 2023
3ff8dd0
Remove colission detection for now
kbarning Feb 27, 2023
a9231a0
Add new scoped main
kbarning Mar 17, 2023
c3afaf4
Update src/uart.rs
kbarning Mar 21, 2023
7aff5f7
Update src/uart.rs
kbarning Mar 21, 2023
5a6ea25
Update src/uart.rs
kbarning Mar 21, 2023
cc63b4f
Update src/uart.rs
kbarning Mar 21, 2023
83856a1
Update src/uart.rs
kbarning Mar 21, 2023
b6eada9
Make internal construct fn unsafe
kbarning Mar 21, 2023
bbfa1c3
add test
kbarning Mar 21, 2023
cb8257f
Fix scope signature and add cfg to unused structs
kbarning Mar 21, 2023
e0cd794
Reordering
kbarning Mar 21, 2023
0b0c400
Cleanup
kbarning Mar 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,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;

Expand Down
380 changes: 380 additions & 0 deletions src/uart.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,380 @@
//! Access to [RIOT's UART](https://doc.riot-os.org/group__drivers__periph__uart.html)
//!
//! Author: Kilian Barning <[email protected]>
#![allow(dead_code)]
kbarning marked this conversation as resolved.
Show resolved Hide resolved

use core::marker::PhantomData;
use core::ptr;

use crate::error::{NegativeErrorExt, NumericError};
use riot_sys::libc::{c_uint, c_void};
use riot_sys::*;

/// This enum representatives the status returned by various `UART`-functions
#[derive(Debug)]
#[non_exhaustive]
pub enum UartDeviceError {
InvalidDevice,
UnsupportedConfig,
Other,
}

impl UartDeviceError {
/// Converts the given `c_int` into the matching Enum representation
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,
_ => Self::Other,
}
}
}

#[derive(Debug)]
chrysn marked this conversation as resolved.
Show resolved Hide resolved
#[non_exhaustive]
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)]
#[non_exhaustive]
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_MARK,
Self::Space => uart_parity_t_UART_PARITY_SPACE,
}
}
}

#[derive(Debug)]
#[non_exhaustive]
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,
}
}
}

/// 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<'scope> {
dev: uart_t,
_scope: PhantomData<&'scope ()>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which purpose does this serve? Off my head, it doesn't change any properties of UartDevice, so I don't see why it's needed. (The UartDevice can easily have a 'scope without referring to it in its properties.)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But if I remove the PhantomData, the compiler tells me that the parameter is unused and fails to compile. So should I put the 'scope generic parameter on the new functions?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think that'd be the right way to go. Only the construct_uart and new_scoped would need one -- new_without_rx and new_with_static_cb would not need a generic (although the latter will still need to demand F: ... + 'static as it does now, and may need to call construct_uart::<'static>(index. ...) but just try it out).

That's also based in the type's properties: No callback (which is all for which we'd need the lifetime) is part of the type, the type is just the UART that's usable for writing and power management. The callback just gets set by a few of the constructors, but it's not something the live type interacts with.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

}

impl<'scope> UartDevice<'scope> {
fn construct_uart<F>(
chrysn marked this conversation as resolved.
Show resolved Hide resolved
index: usize,
baud: u32,
user_callback: &'scope mut F,
) -> Result<Self, UartDeviceError>
where
F: FnMut(u8) + Sync + 'scope,
kbarning marked this conversation as resolved.
Show resolved Hide resolved
{
unsafe {
let dev = macro_UART_DEV(index as c_uint);
uart_init(
dev,
baud,
Some(Self::new_data_callback::<F>),
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<Self>` if the UART was initialized successfully or a
chrysn marked this conversation as resolved.
Show resolved Hide resolved
/// `Err<UartDeviceStatus>` 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;
chrysn marked this conversation as resolved.
Show resolved Hide resolved
///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<F, Main, RMain>(
index: usize,
baud: u32,
user_callback: &'scope mut F,
main: Main,
) -> Result<RMain, UartDeviceError>
where
F: FnMut(u8) + Sync + 'scope,
kbarning marked this conversation as resolved.
Show resolved Hide resolved
Main: FnOnce(&mut Self) -> RMain,
{
let mut self_ = Self::construct_uart(index, baud, user_callback)?;
kbarning marked this conversation as resolved.
Show resolved Hide resolved
let result = (main)(&mut self_);
drop(self_);
Ok(result)
}

/// Tries to initialize the given `UART`. Returns a Result with rather `Ok<Self>` if the UART was initialized successfully or a
/// `Err<UartDeviceStatus>` containing the error. As the name implies, the created `UART` device can <b>ONLY</b> send data
///
/// # Arguments
///
/// * `dev` - The index of the hardware device
/// * `baud` - The used baud rate
///
/// # Examples
/// ```
/// use riot_wrappers::uart::UartDevice;
/// 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(index: usize, baud: u32) -> Result<Self, UartDeviceError> {
unsafe {
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_err(UartDeviceError::from_c)
}
}

/// Sets the mode according to the given parameters
/// Should the parameters be invalid, the function returns a Err<UartDeviceStatus::UnsupportedConfig>
/// # 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
/// ```
/// use riot_wrappers::uart::{DataBits, Parity, StopBits, UartDevice};
/// 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:?}"));
/// ```
#[cfg(riot_module_periph_uart_modecfg)]
pub fn set_mode(
&mut self,
data_bits: DataBits,
parity: Parity,
stop_bits: StopBits,
) -> Result<(), UartDeviceError> {
unsafe {
match UartDeviceError::from_c(uart_mode(
self.dev,
data_bits.to_c(),
parity.to_c(),
stop_bits.to_c(),
)) {
UartDeviceError::Success => Ok(()),
status => Err(status),
}
}
}

/// Transmits the given data via the `UART`-device
///
/// # Examples
/// ```
/// use riot_wrappers::uart::UartDevice;
/// 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");
/// ```
pub fn write(&mut self, data: &[u8]) {
unsafe {
uart_write(self.dev, data.as_ptr(), data.len() as size_t);
}
}

/// Turns on the power from the `UART-Device`
pub fn power_on(&mut self) {
unsafe { uart_poweron(self.dev) };
}

/// Turns off the power from the `UART-Device`
pub fn power_off(&mut self) {
unsafe { uart_poweroff(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.
#[cfg(riot_module_periph_uart_reconfigure)]
pub unsafe fn init_pins(&mut self) {
uart_init_pins(self.dev);
}

/// 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
#[cfg(riot_module_periph_uart_reconfigure)]
pub fn get_pin_rx(&mut self) -> Option<crate::gpio::GPIO> {
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) -> Option<crate::gpio::GPIO> {
crate::gpio::GPIO::from_c(unsafe { uart_pin_tx(self.dev) })
}

/// 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)]
pub fn rxstart_irq_configure<F>(&mut self, user_fxopt: &'a mut F)
where
F: FnMut() + Send + 'static,
{
unsafe {
uart_rxstart_irq_configure(
dev,
Self::rxstart_callback::<F>,
user_fxopt as *mut _ as *mut c_void,
)
};
}

/// Enable the RX start interrupt
#[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_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 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`
unsafe extern "C" fn new_data_callback<F>(user_callback: *mut c_void, data: u8)
where
F: FnMut(u8) + 'scope,
{
(*(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
#[cfg(riot_module_periph_uart_rxstart_irq)]
unsafe extern "C" fn rxstart_callback<F>(user_callback: *mut c_void)
where
F: FnMut() + 'scope,
{
(*(user_callback as *mut F))(); // We cast the void* back to the closure and call it
}
}

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) {
unsafe {
uart_init(self.dev, 9600, None, ptr::null_mut());
#[cfg(riot_module_periph_uart_reconfigure)]
self.deinit_pins();
}
}
}

impl UartDevice<'static> {
/// Tries to initialize the given `UART` with a static callback. Returns a Result with rather `Ok<Self>` if the UART
/// was initialized successfully or a `Err<UartDeviceStatus>` 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<F>(
index: usize,
baud: u32,
user_callback: &'static mut F,
) -> Result<Self, UartDeviceError>
where
F: FnMut(u8) + Sync + 'static,
kbarning marked this conversation as resolved.
Show resolved Hide resolved
{
Self::construct_uart(index, baud, user_callback)
}
}