feat: implement gpio

This commit is contained in:
Naxdy 2024-10-29 18:45:16 +01:00
parent b84d93d0b5
commit c503a195b2
Signed by: Naxdy
GPG key ID: CC15075846BCE91B
4 changed files with 183 additions and 4 deletions

7
Cargo.lock generated
View file

@ -137,6 +137,12 @@ dependencies = [
"minimal-lexical",
]
[[package]]
name = "once_cell"
version = "1.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]]
name = "prettyplease"
version = "0.2.25"
@ -314,5 +320,6 @@ dependencies = [
"bindgen",
"cc",
"glob",
"once_cell",
"thiserror",
]

View file

@ -4,6 +4,7 @@ version = "0.1.0"
edition = "2021"
[dependencies]
once_cell = "1.20.2"
thiserror = "1.0.65"
[build-dependencies]

View file

@ -2,35 +2,193 @@ use std::sync::Mutex;
use thiserror::Error;
use crate::ffi;
use crate::{
ensure_library_setup,
ffi::{self, HIGH, INPUT, LOW, OUTPUT, PUD_DOWN, PUD_OFF, PUD_UP, PWM_OUTPUT},
};
static PINS_HANDED_OUT: Mutex<Vec<i32>> = Mutex::new(Vec::new());
#[derive(Error, Debug)]
pub enum GpioError {
#[error("could not parse value {0} into type")]
ValueParseError(i32),
#[error("pin {0} is in mode {1:?} when it should be in mode {2:?}")]
WrongPinModeError(i32, PinMode, PinMode),
#[error("mode {0:?} is unsupported")]
UnsupportedModeError(PinMode),
}
#[derive(Error, Debug)]
pub enum PinRetrievalError {
#[error("pin {0} has already been retrieved")]
AlreadyRetrieved(i32),
}
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub enum PinMode {
Input,
Output,
PwmOutput,
Uninitialized,
}
impl TryFrom<i32> for PinMode {
type Error = GpioError;
fn try_from(value: i32) -> Result<Self, Self::Error> {
match value as u32 {
INPUT => Ok(PinMode::Input),
OUTPUT => Ok(PinMode::Output),
PWM_OUTPUT => Ok(PinMode::PwmOutput),
_ => Err(GpioError::ValueParseError(value)),
}
}
}
impl From<PinMode> for i32 {
fn from(value: PinMode) -> Self {
match value {
PinMode::Input => INPUT as i32,
PinMode::Output => OUTPUT as i32,
PinMode::PwmOutput => PWM_OUTPUT as i32,
PinMode::Uninitialized => panic!("tried to make use of uninitialized pin"),
}
}
}
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub enum PullUpMode {
Off,
Up,
Down,
}
impl TryFrom<i32> for PullUpMode {
type Error = GpioError;
fn try_from(value: i32) -> Result<Self, Self::Error> {
match value as u32 {
PUD_OFF => Ok(PullUpMode::Off),
PUD_UP => Ok(PullUpMode::Up),
PUD_DOWN => Ok(PullUpMode::Down),
_ => Err(GpioError::ValueParseError(value)),
}
}
}
impl From<PullUpMode> for i32 {
fn from(value: PullUpMode) -> Self {
match value {
PullUpMode::Down => PUD_DOWN as i32,
PullUpMode::Up => PUD_UP as i32,
PullUpMode::Off => PUD_OFF as i32,
}
}
}
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub enum PinState {
Low,
High,
}
impl TryFrom<i32> for PinState {
type Error = GpioError;
fn try_from(value: i32) -> Result<Self, Self::Error> {
match value as u32 {
HIGH => Ok(PinState::High),
LOW => Ok(PinState::Low),
_ => Err(GpioError::ValueParseError(value)),
}
}
}
impl From<PinState> for i32 {
fn from(value: PinState) -> Self {
match value {
PinState::Low => LOW as i32,
PinState::High => HIGH as i32,
}
}
}
pub struct Pin {
pin_id: i32,
pin_mode: PinMode,
}
impl Pin {
pub fn new(pin_id: i32) -> Result<Self, PinRetrievalError> {
ensure_library_setup!();
let can_retrieve = !PINS_HANDED_OUT
.lock()
.expect("failed to obtain pin list lock")
.contains(&pin_id);
if can_retrieve {
Ok(Self { pin_id })
Ok(Self {
pin_id,
pin_mode: PinMode::Uninitialized,
})
} else {
Err(PinRetrievalError::AlreadyRetrieved(pin_id))
}
}
pub fn get_gpio_mode(&mut self) -> i32 {
unsafe { ffi::orangepi_get_gpio_mode(self.pin_id) }
fn ensure_pin_mode(&mut self, mode: PinMode) -> Result<(), GpioError> {
if self.pin_mode != mode {
Err(GpioError::WrongPinModeError(
self.pin_id,
self.pin_mode,
mode,
))
} else {
Ok(())
}
}
pub fn get_gpio_mode(&mut self) -> Result<PinMode, GpioError> {
ensure_library_setup!();
unsafe { ffi::orangepi_get_gpio_mode(self.pin_id).try_into() }
}
pub fn set_gpio_mode(&mut self, mode: PinMode) -> Result<(), GpioError> {
ensure_library_setup!();
if mode == PinMode::PwmOutput || mode == PinMode::Uninitialized {
return Err(GpioError::UnsupportedModeError(mode));
}
unsafe {
ffi::pinMode(self.pin_id, mode.into());
}
Ok(())
}
pub fn digital_read(&mut self) -> Result<PinState, GpioError> {
ensure_library_setup!();
self.ensure_pin_mode(PinMode::Input)?;
unsafe { ffi::orangepi_digitalRead(self.pin_id).try_into() }
}
pub fn digital_write(&mut self, value: PinState) -> Result<bool, GpioError> {
ensure_library_setup!();
self.ensure_pin_mode(PinMode::Output)?;
unsafe { Ok(ffi::orangepi_digitalWrite(self.pin_id, value.into()) == 0) }
}
pub fn pull_up_down_control(&mut self, mode: PullUpMode) {
ensure_library_setup!();
unsafe { ffi::pullUpDnControl(self.pin_id, mode.into()) }
}
}

View file

@ -1,2 +1,15 @@
use once_cell::sync::Lazy;
mod ffi;
pub mod gpio;
#[macro_export]
macro_rules! ensure_library_setup {
() => {
if !*$crate::LIBRARY_SETUP_COMPLETE {
panic!("wiringOP library failed to set up");
}
};
}
static LIBRARY_SETUP_COMPLETE: Lazy<bool> = Lazy::new(|| unsafe { ffi::wiringPiSetupGpio() == 0 });