use super::{GpioChanges, GpioDirection, GpioValues, LogicLevel};
use crate::settings::{Gp0Mode, Gp1Mode, Gp2Mode, Gp3Mode, GpSettings};
use crate::{Error, MCP2221};
#[derive(Debug)]
pub struct GpPin<'a> {
driver: &'a MCP2221,
pin_number: PinNumber,
}
impl<'a> GpPin<'a> {
pub fn configure_as_digital_input(self) -> Result<Input<'a>, Error> {
let (_, mut gp_settings) = self.driver.sram_read_settings()?;
self.pin_number
.configure_as_gpio(&mut gp_settings, GpioDirection::Input);
self.driver.sram_write_gp_settings(gp_settings)?;
Ok(Input(self))
}
pub fn configure_as_digital_output(self) -> Result<Output<'a>, Error> {
let (_, mut gp_settings) = self.driver.sram_read_settings()?;
self.pin_number
.configure_as_gpio(&mut gp_settings, GpioDirection::Output);
self.driver.sram_write_gp_settings(gp_settings)?;
Ok(Output(self))
}
}
impl<'a> TryFrom<GpPin<'a>> for Input<'a> {
type Error = Error;
fn try_from(pin: GpPin<'a>) -> Result<Self, Self::Error> {
pin.configure_as_digital_input()
}
}
impl<'a> TryFrom<GpPin<'a>> for Output<'a> {
type Error = Error;
fn try_from(pin: GpPin<'a>) -> Result<Self, Self::Error> {
pin.configure_as_digital_output()
}
}
impl<'a> From<Input<'a>> for GpPin<'a> {
fn from(value: Input<'a>) -> Self {
value.destroy()
}
}
impl<'a> From<Output<'a>> for GpPin<'a> {
fn from(value: Output<'a>) -> Self {
value.destroy()
}
}
#[derive(Debug)]
pub struct Input<'a>(GpPin<'a>);
impl<'a> Input<'a> {
fn pin_settings(&self) -> Result<(GpioDirection, LogicLevel), Error> {
let gpio_values = self.0.driver.gpio_read()?;
self.0
.pin_number
.get_value(&gpio_values)
.filter(|(dir, _)| dir.is_input())
.ok_or(Error::PinModeChanged)
}
pub fn get_level(&self) -> Result<LogicLevel, Error> {
self.pin_settings().map(|(_, level)| level)
}
pub fn destroy(self) -> GpPin<'a> {
self.0
}
pub fn try_into_output(self) -> Result<Output<'a>, Error> {
self.pin_settings()?;
let mut changes = GpioChanges::new();
self.0
.pin_number
.change_direction(&mut changes, GpioDirection::Output);
self.0.driver.gpio_write(&changes)?;
Ok(Output(self.0))
}
}
impl embedded_hal::digital::Error for Error {
fn kind(&self) -> embedded_hal::digital::ErrorKind {
embedded_hal::digital::ErrorKind::Other
}
}
impl embedded_hal::digital::ErrorType for Input<'_> {
type Error = Error;
}
impl embedded_hal::digital::InputPin for Input<'_> {
fn is_high(&mut self) -> Result<bool, Self::Error> {
self.get_level().map(LogicLevel::is_high)
}
fn is_low(&mut self) -> Result<bool, Self::Error> {
self.get_level().map(LogicLevel::is_low)
}
}
#[derive(Debug)]
pub struct Output<'a>(GpPin<'a>);
impl<'a> Output<'a> {
fn pin_settings(&self) -> Result<(GpioDirection, LogicLevel), Error> {
let gpio_values = self.0.driver.gpio_read()?;
self.0
.pin_number
.get_value(&gpio_values)
.filter(|(dir, _)| dir.is_output())
.ok_or(Error::PinModeChanged)
}
pub fn set_level(&self, level: LogicLevel) -> Result<(), Error> {
self.pin_settings()?;
let mut changes = GpioChanges::new();
self.0.pin_number.change_level(&mut changes, level);
self.0.driver.gpio_write(&changes)
}
pub fn get_output_level(&self) -> Result<LogicLevel, Error> {
self.pin_settings().map(|(_, level)| level)
}
pub fn destroy(self) -> GpPin<'a> {
self.0
}
pub fn try_into_input(self) -> Result<Input<'a>, Error> {
self.pin_settings()?;
let mut changes = GpioChanges::new();
self.0
.pin_number
.change_direction(&mut changes, GpioDirection::Input);
self.0.driver.gpio_write(&changes)?;
Ok(Input(self.0))
}
}
impl embedded_hal::digital::ErrorType for Output<'_> {
type Error = Error;
}
impl embedded_hal::digital::OutputPin for Output<'_> {
fn set_low(&mut self) -> Result<(), Self::Error> {
self.set_level(LogicLevel::Low)
}
fn set_high(&mut self) -> Result<(), Self::Error> {
self.set_level(LogicLevel::High)
}
}
impl embedded_hal::digital::StatefulOutputPin for Output<'_> {
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
self.get_output_level().map(LogicLevel::is_high)
}
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
self.get_output_level().map(LogicLevel::is_low)
}
}
impl<'a> TryFrom<Output<'a>> for Input<'a> {
type Error = Error;
fn try_from(value: Output<'a>) -> Result<Self, Self::Error> {
value.try_into_input()
}
}
impl<'a> TryFrom<Input<'a>> for Output<'a> {
type Error = Error;
fn try_from(value: Input<'a>) -> Result<Self, Self::Error> {
value.try_into_output()
}
}
#[derive(Debug)]
pub struct Pins<'a> {
pub gp0: GpPin<'a>,
pub gp1: GpPin<'a>,
pub gp2: GpPin<'a>,
pub gp3: GpPin<'a>,
}
impl<'a> Pins<'a> {
pub(crate) fn new(driver: &'a MCP2221) -> Self {
Self {
gp0: GpPin {
pin_number: PinNumber::Gp0,
driver,
},
gp1: GpPin {
pin_number: PinNumber::Gp1,
driver,
},
gp2: GpPin {
pin_number: PinNumber::Gp2,
driver,
},
gp3: GpPin {
pin_number: PinNumber::Gp3,
driver,
},
}
}
}
#[derive(Debug, Clone, Copy)]
enum PinNumber {
Gp0,
Gp1,
Gp2,
Gp3,
}
impl PinNumber {
fn configure_as_gpio(&self, gp_settings: &mut GpSettings, direction: GpioDirection) {
match self {
PinNumber::Gp0 => {
gp_settings.gp0_mode = Gp0Mode::Gpio;
gp_settings.gp0_direction = direction;
}
PinNumber::Gp1 => {
gp_settings.gp1_mode = Gp1Mode::Gpio;
gp_settings.gp1_direction = direction;
}
PinNumber::Gp2 => {
gp_settings.gp2_mode = Gp2Mode::Gpio;
gp_settings.gp2_direction = direction;
}
PinNumber::Gp3 => {
gp_settings.gp3_mode = Gp3Mode::Gpio;
gp_settings.gp3_direction = direction;
}
}
}
fn get_value(&self, gpio_values: &GpioValues) -> Option<(GpioDirection, LogicLevel)> {
match self {
PinNumber::Gp0 => gpio_values.gp0,
PinNumber::Gp1 => gpio_values.gp1,
PinNumber::Gp2 => gpio_values.gp2,
PinNumber::Gp3 => gpio_values.gp3,
}
}
fn change_level(&self, changes: &mut GpioChanges, level: LogicLevel) {
match self {
PinNumber::Gp0 => changes.with_gp0_level(level),
PinNumber::Gp1 => changes.with_gp1_level(level),
PinNumber::Gp2 => changes.with_gp2_level(level),
PinNumber::Gp3 => changes.with_gp3_level(level),
};
}
fn change_direction(&self, changes: &mut GpioChanges, direction: GpioDirection) {
match self {
PinNumber::Gp0 => changes.with_gp0_direction(direction),
PinNumber::Gp1 => changes.with_gp1_direction(direction),
PinNumber::Gp2 => changes.with_gp2_direction(direction),
PinNumber::Gp3 => changes.with_gp3_direction(direction),
};
}
}