257 lines
6.1 KiB
Rust
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()) }
|
|
}
|
|
}
|