Files
wiringop-rs/src/gpio.rs

257 lines
6.1 KiB
Rust

use std::sync::Mutex;
use thiserror::Error;
use crate::{
ensure_library_setup,
ffi::{
self, HIGH, INPUT, LOW, OUTPUT, PUD_DOWN, PUD_OFF, PUD_UP, PWM_CLK_DIV_12K,
PWM_CLK_DIV_24K, PWM_CLK_DIV_360, PWM_CLK_DIV_36K, PWM_CLK_DIV_480, PWM_CLK_DIV_48K,
PWM_CLK_DIV_72K, 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),
#[error("pin {0} has already been retrieved")]
PinAlreadyRetrieved(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 | PWM_CLK_DIV_12K | PWM_CLK_DIV_24K | PWM_CLK_DIV_360 | PWM_CLK_DIV_36K
| PWM_CLK_DIV_480 | PWM_CLK_DIV_48K | PWM_CLK_DIV_72K => 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, GpioError> {
ensure_library_setup!();
let can_retrieve = {
let mut vec = PINS_HANDED_OUT.lock().expect("should obtain pins lock");
if vec.contains(&pin_id) {
false
} else {
vec.push(pin_id);
true
}
};
if can_retrieve {
let pin_mode = unsafe { ffi::getAlt(pin_id).try_into()? };
Ok(Self { pin_id, pin_mode })
} else {
Err(GpioError::PinAlreadyRetrieved(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 pwm_write(&mut self, value: i32) -> Result<(), GpioError> {
ensure_library_setup!();
self.ensure_pin_mode(PinMode::PwmOutput)?;
unsafe {
ffi::pwmWrite(self.pin_id, value);
}
Ok(())
}
pub fn pwm_set_tone(&mut self, freq: i32) -> Result<(), GpioError> {
ensure_library_setup!();
self.ensure_pin_mode(PinMode::PwmOutput)?;
unsafe {
ffi::pwmToneWrite(self.pin_id, freq);
}
Ok(())
}
pub fn pwm_set_range(&mut self, range: u32) -> Result<(), GpioError> {
ensure_library_setup!();
self.ensure_pin_mode(PinMode::PwmOutput)?;
unsafe {
ffi::pwmSetRange(self.pin_id, range);
}
Ok(())
}
pub fn pwm_set_clock(&mut self, divisor: i32) -> Result<(), GpioError> {
ensure_library_setup!();
self.ensure_pin_mode(PinMode::PwmOutput)?;
unsafe {
ffi::pwmSetClock(self.pin_id, divisor);
}
Ok(())
}
pub fn get_gpio_mode(&mut self) -> Result<PinMode, GpioError> {
ensure_library_setup!();
self.pin_mode = unsafe { ffi::getAlt(self.pin_id).try_into()? };
Ok(self.pin_mode)
}
pub fn set_gpio_mode(&mut self, mode: PinMode) -> Result<(), GpioError> {
ensure_library_setup!();
if mode == PinMode::Uninitialized {
return Err(GpioError::UnsupportedModeError(mode));
}
unsafe {
ffi::pinMode(self.pin_id, mode.into());
}
self.pin_mode = mode;
Ok(())
}
pub fn digital_read(&mut self) -> Result<PinState, GpioError> {
ensure_library_setup!();
self.ensure_pin_mode(PinMode::Input)?;
unsafe { ffi::digitalRead(self.pin_id).try_into() }
}
pub fn digital_write(&mut self, value: PinState) -> Result<(), GpioError> {
ensure_library_setup!();
self.ensure_pin_mode(PinMode::Output)?;
unsafe {
ffi::digitalWrite(self.pin_id, value.into());
}
Ok(())
}
pub fn pull_up_down_control(&mut self, mode: PullUpMode) {
ensure_library_setup!();
unsafe { ffi::pullUpDnControl(self.pin_id, mode.into()) }
}
}