-
Notifications
You must be signed in to change notification settings - Fork 10
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
kbarning
wants to merge
43
commits into
RIOT-OS:main
Choose a base branch
from
kbarning:impl_uart_wrapper
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
43 commits
Select commit
Hold shift + click to select a range
9722813
Added uart
kbarning 5ace203
Fixed callback
kbarning 530e441
Fix code according to @jaschman
kbarning 14f0d08
Add init pins
kbarning 3d7b15b
Fix comments
kbarning 5fb2e12
Fixed signature
kbarning e1dd2c0
Fixed closure type
kbarning ac0d680
Mark init pins as unsafe
kbarning a39a7a7
Fix comments
kbarning 1a0d07c
Change drop
kbarning b62e14e
Introduce Phantom Data Lifetimes
kbarning 788d633
Add generics to rxstart
kbarning 7e2badb
Add mode feature
kbarning eec16de
PWM: Add wrapper around RIOTs PWM-interface
398cf41
Removed wrong libs
kbarning 77290d9
Delete pwm.rs
kbarning 2adc6ac
Removed unused comments
kbarning a3ee384
Update uart.rs
kbarning 6ca86f3
Fixed issues as suggested by chrysn
kbarning 2942733
Added new macro to init uart
kbarning bf83576
Fix comments
kbarning 06ba80e
Added scoped approach
kbarning 2788caa
Add static new
kbarning a545c7b
Added new static impl + fix doc
kbarning e0e99a6
Make get gpio optional
kbarning 820b42c
Remove colission detection for now
kbarning 74e68fd
Add new scoped main
kbarning be20cea
Update src/uart.rs
kbarning 3a9e145
Update src/uart.rs
kbarning d957c9f
Update src/uart.rs
kbarning e45c297
Update src/uart.rs
kbarning 7aa931b
Update src/uart.rs
kbarning 762a639
Make internal construct fn unsafe
kbarning 748fe93
add test
kbarning cd066aa
Fix scope signature and add cfg to unused structs
kbarning 12fda60
Reordering
kbarning 973d70a
Cleanup
kbarning 59f3d78
Update tests/uart/Cargo.toml
kbarning 03595f9
Update src/uart.rs
kbarning 9763bf3
Update src/uart.rs
kbarning 2b63e71
Update tests/uart/Cargo.toml
kbarning 455291e
Add println to examples, remove unused code
kbarning 16a67e9
Fix rebase
kbarning File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,331 @@ | ||
//! Access to [RIOT's UART](https://doc.riot-os.org/group__drivers__periph__uart.html) | ||
//! | ||
//! Author: Kilian Barning <barning@uni-bremen.de> | ||
|
||
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 { | ||
match n { | ||
crate::error::ENODEV => Self::InvalidDevice, | ||
crate::error::ENOTSUP => Self::UnsupportedConfig, | ||
_ => Self::Other, | ||
} | ||
} | ||
} | ||
|
||
#[cfg(riot_module_periph_uart_modecfg)] | ||
#[derive(Debug)] | ||
chrysn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#[non_exhaustive] | ||
pub enum DataBits { | ||
Five, | ||
Six, | ||
Seven, | ||
Eight, | ||
} | ||
|
||
#[cfg(riot_module_periph_uart_modecfg)] | ||
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, | ||
} | ||
} | ||
} | ||
|
||
#[cfg(riot_module_periph_uart_modecfg)] | ||
#[derive(Debug)] | ||
#[non_exhaustive] | ||
pub enum Parity { | ||
None, | ||
Even, | ||
Odd, | ||
Mark, | ||
Space, | ||
} | ||
|
||
#[cfg(riot_module_periph_uart_modecfg)] | ||
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, | ||
} | ||
} | ||
} | ||
|
||
#[cfg(riot_module_periph_uart_modecfg)] | ||
#[derive(Debug)] | ||
#[non_exhaustive] | ||
pub enum StopBits { | ||
One, | ||
Two, | ||
} | ||
|
||
#[cfg(riot_module_periph_uart_modecfg)] | ||
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 { | ||
dev: uart_t, | ||
} | ||
|
||
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<'scope, F>( | ||
index: usize, | ||
baud: u32, | ||
user_callback: &'scope mut F, | ||
) -> Result<Self, UartDeviceError> | ||
where | ||
F: FnMut(u8) + Send + 'scope, | ||
{ | ||
let dev = macro_UART_DEV(index as c_uint); | ||
uart_init( | ||
dev, | ||
baud, | ||
Some(Self::new_data_callback::<'scope, F>), | ||
user_callback as *mut _ as *mut c_void, | ||
) | ||
.negative_to_error() | ||
.map(|_| Self { dev }) | ||
.map_err(UartDeviceError::from_c) | ||
} | ||
|
||
/// Tries to initialize the given `UART`. Returns a Result with rather `Ok<RMain>` where `RMain` is the value returned by the scoped main function | ||
/// or a `Err<UartDeviceStatus>` containing the error | ||
/// | ||
/// This is the scoped version of [`new_with_static_cb()`] 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| { | ||
/// println!("Received {:02x}", new_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<'scope, F, Main, RMain>( | ||
index: usize, | ||
baud: u32, | ||
user_callback: &'scope mut F, | ||
main: Main, | ||
) -> Result<RMain, UartDeviceError> | ||
where | ||
F: FnMut(u8) + Send + 'scope, | ||
Main: FnOnce(&mut Self) -> RMain, | ||
chrysn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
// This possibly relies on Rust code in RIOT to not unwind. | ||
let mut self_ = unsafe { 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<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 }) | ||
.map_err(UartDeviceError::from_c) | ||
} | ||
} | ||
|
||
/// 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| { | ||
/// println!("Received {:02x}", new_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) + 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<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) { | ||
chrysn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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) }) | ||
} | ||
|
||
/// 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<'scope, 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 | ||
} | ||
} | ||
|
||
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(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
[package] | ||
name = "riot-wrappers-test-uart" | ||
version = "0.1.0" | ||
authors = ["Kilian Barning <barning@uni-bremen.de>"] | ||
edition = "2021" | ||
publish = false | ||
|
||
[lib] | ||
crate-type = ["staticlib"] | ||
|
||
[profile.release] | ||
panic = "abort" | ||
|
||
[dependencies] | ||
riot-wrappers = { path = "../..", features = [ "set_panic_handler" ] } | ||
riot-sys = "*" | ||
embedded-hal = "0.2.4" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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:?}")); | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't have a consistent policy in Rust code for RIOT as to how to annotate authorship per module. I'd leave this in for now, especially until we do have something more generic, but as this is not a copyright note, it may be removed in a later version – realistically, I expect it to be moved into more meta data at some point.