use crate::usb4604_reg::*;
use crate::{Error, Usb4604};
use strum::{AsRefStr, EnumIter};
pub struct PushPullOutput {
flex: Flex,
}
pub struct Input {
flex: Flex,
}
pub struct OpenDrainOutput {
flex: Flex,
}
pub struct Flex {
usb4604: Usb4604,
pio: Pio,
mode: Mode,
is_out_en: bool,
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Mode {
OutputPushPull,
OutputOpenDrain,
Input,
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Pull {
None,
Up,
Down,
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Level {
Low,
High,
}
#[derive(Clone, Copy, EnumIter, AsRefStr, PartialEq, Debug)]
pub enum Pio {
Pio0,
Pio1,
Pio3,
Pio8,
Pio9,
Pio10,
Pio19,
Pio20,
}
impl Flex {
pub(crate) fn init_get_mode(usb4604: Usb4604, pio: Pio) -> Result<Flex, Error> {
let is_out = match pio {
Pio::Pio0 => usb4604.read_reg::<Gpio0_7Dir>()?.gpio0_out_en(),
Pio::Pio1 => usb4604.read_reg::<Gpio0_7Dir>()?.gpio1_out_en(),
Pio::Pio3 => usb4604.read_reg::<Gpio0_7Dir>()?.gpio3_out_en(),
Pio::Pio8 => usb4604.read_reg::<Gpio8_10Dir>()?.gpio8_out_en(),
Pio::Pio9 => usb4604.read_reg::<Gpio8_10Dir>()?.gpio9_out_en(),
Pio::Pio10 => usb4604.read_reg::<Gpio8_10Dir>()?.gpio10_out_en(),
Pio::Pio19 => usb4604.read_reg::<Gpio17_20Dir>()?.gpio19_out_en(),
Pio::Pio20 => usb4604.read_reg::<Gpio17_20Dir>()?.gpio20_out_en(),
};
let mode = if is_out {
Mode::OutputPushPull
} else {
Mode::Input
};
Ok(Flex {
usb4604,
pio,
mode,
is_out_en: is_out,
})
}
pub(crate) fn init_ignore_mode(usb4604: Usb4604, pio: Pio) -> Flex {
Flex {
usb4604,
pio,
mode: Mode::Input,
is_out_en: false,
}
}
pub fn set_as_output(&mut self, initial: Level) -> Result<(), Error> {
self.set_level(initial)?;
self.set_mode(Mode::OutputPushPull)?;
self.set_pull(Pull::None)?;
self.mode = Mode::OutputPushPull;
Ok(())
}
pub fn into_output(mut self, initial: Level) -> Result<PushPullOutput, Error> {
self.set_as_output(initial)?;
Ok(PushPullOutput { flex: self })
}
pub fn set_as_input(&mut self, pull: Pull) -> Result<(), Error> {
self.set_mode(Mode::Input)?;
self.set_pull(pull)?;
self.mode = Mode::Input;
Ok(())
}
pub fn into_input(mut self, pull: Pull) -> Result<Input, Error> {
self.set_as_input(pull)?;
Ok(Input { flex: self })
}
pub fn set_as_open_drain(&mut self, pull: Pull) -> Result<(), Error> {
self.set_mode(Mode::Input)?;
self.set_pull(pull)?;
self.mode = Mode::OutputOpenDrain;
Ok(())
}
pub fn into_open_drain_output(mut self, pull: Pull) -> Result<OpenDrainOutput, Error> {
self.set_as_open_drain(pull)?;
Ok(OpenDrainOutput { flex: self })
}
fn set_mode(&mut self, mode: Mode) -> Result<(), Error> {
let out_en = matches!(mode, Mode::OutputPushPull);
self.is_out_en = out_en;
match self.pio {
Pio::Pio0 => self
.usb4604
.modify_reg::<Gpio0_7Dir, _>(|r| r.set_gpio0_out_en(out_en)),
Pio::Pio1 => self
.usb4604
.modify_reg::<Gpio0_7Dir, _>(|r| r.set_gpio1_out_en(out_en)),
Pio::Pio3 => self
.usb4604
.modify_reg::<Gpio0_7Dir, _>(|r| r.set_gpio3_out_en(out_en)),
Pio::Pio8 => self
.usb4604
.modify_reg::<Gpio8_10Dir, _>(|r| r.set_gpio8_out_en(out_en)),
Pio::Pio9 => self
.usb4604
.modify_reg::<Gpio8_10Dir, _>(|r| r.set_gpio9_out_en(out_en)),
Pio::Pio10 => self
.usb4604
.modify_reg::<Gpio8_10Dir, _>(|r| r.set_gpio10_out_en(out_en)),
Pio::Pio19 => self
.usb4604
.modify_reg::<Gpio17_20Dir, _>(|r| r.set_gpio19_out_en(out_en)),
Pio::Pio20 => self
.usb4604
.modify_reg::<Gpio17_20Dir, _>(|r| r.set_gpio20_out_en(out_en)),
}?;
Ok(())
}
pub fn mode(&self) -> Mode {
self.mode
}
pub fn pio(&self) -> Pio {
self.pio
}
pub fn set_level(&mut self, level: Level) -> Result<(), Error> {
if self.mode == Mode::OutputOpenDrain {
if level == Level::High {
self.set_mode(Mode::Input)?;
return Ok(());
} else {
self.set_mode(Mode::OutputPushPull)?;
}
}
let is_high = matches!(level, Level::High);
match self.pio {
Pio::Pio0 => self
.usb4604
.modify_reg::<Gpio0_7Output, _>(|r| r.set_gpio0_out(is_high)),
Pio::Pio1 => self
.usb4604
.modify_reg::<Gpio0_7Output, _>(|r| r.set_gpio1_out(is_high)),
Pio::Pio3 => self
.usb4604
.modify_reg::<Gpio0_7Output, _>(|r| r.set_gpio3_out(is_high)),
Pio::Pio8 => self
.usb4604
.modify_reg::<Gpio8_10Output, _>(|r| r.set_gpio8_out(is_high)),
Pio::Pio9 => self
.usb4604
.modify_reg::<Gpio8_10Output, _>(|r| r.set_gpio9_out(is_high)),
Pio::Pio10 => self
.usb4604
.modify_reg::<Gpio8_10Output, _>(|r| r.set_gpio10_out(is_high)),
Pio::Pio19 => self
.usb4604
.modify_reg::<Gpio17_20Output, _>(|r| r.set_gpio19_out(is_high)),
Pio::Pio20 => self
.usb4604
.modify_reg::<Gpio17_20Output, _>(|r| r.set_gpio20_out(is_high)),
}?;
Ok(())
}
pub fn set_high(&mut self) -> Result<(), Error> {
self.set_level(Level::High)
}
pub fn set_low(&mut self) -> Result<(), Error> {
self.set_level(Level::Low)
}
pub fn toggle(&mut self) -> Result<(), Error> {
if self.get_output_level()? == Level::Low {
self.set_level(Level::High)
} else {
self.set_level(Level::Low)
}
}
pub fn get_output_level(&self) -> Result<Level, Error> {
let is_high = match self.pio {
Pio::Pio0 => self.usb4604.read_reg::<Gpio0_7Output>()?.gpio0_out(),
Pio::Pio1 => self.usb4604.read_reg::<Gpio0_7Output>()?.gpio1_out(),
Pio::Pio3 => self.usb4604.read_reg::<Gpio0_7Output>()?.gpio3_out(),
Pio::Pio8 => self.usb4604.read_reg::<Gpio8_10Output>()?.gpio8_out(),
Pio::Pio9 => self.usb4604.read_reg::<Gpio8_10Output>()?.gpio9_out(),
Pio::Pio10 => self.usb4604.read_reg::<Gpio8_10Output>()?.gpio10_out(),
Pio::Pio19 => self.usb4604.read_reg::<Gpio17_20Output>()?.gpio19_out(),
Pio::Pio20 => self.usb4604.read_reg::<Gpio17_20Output>()?.gpio20_out(),
};
let level = if is_high { Level::High } else { Level::Low };
Ok(level)
}
pub fn is_set_high(&self) -> Result<bool, Error> {
Ok(self.get_output_level()? == Level::High)
}
pub fn is_set_low(&self) -> Result<bool, Error> {
Ok(self.get_output_level()? == Level::Low)
}
pub fn get_input_level(&self) -> Result<Level, Error> {
let is_high = match self.pio {
Pio::Pio0 => self.usb4604.read_reg::<Gpio0_7Input>()?.gpio0_in(),
Pio::Pio1 => self.usb4604.read_reg::<Gpio0_7Input>()?.gpio1_in(),
Pio::Pio3 => self.usb4604.read_reg::<Gpio0_7Input>()?.gpio3_in(),
Pio::Pio8 => self.usb4604.read_reg::<Gpio8_10Input>()?.gpio8_in(),
Pio::Pio9 => self.usb4604.read_reg::<Gpio8_10Input>()?.gpio9_in(),
Pio::Pio10 => self.usb4604.read_reg::<Gpio8_10Input>()?.gpio10_in(),
Pio::Pio19 => self.usb4604.read_reg::<Gpio17_20Input>()?.gpio19_in(),
Pio::Pio20 => self.usb4604.read_reg::<Gpio17_20Input>()?.gpio20_in(),
};
let level = if is_high { Level::High } else { Level::Low };
Ok(level)
}
pub fn is_high(&self) -> Result<bool, Error> {
Ok(self.get_input_level()? == Level::High)
}
pub fn is_low(&self) -> Result<bool, Error> {
Ok(self.get_input_level()? == Level::High)
}
pub fn set_pull(&mut self, pull: Pull) -> Result<(), Error> {
let (pull_up, pull_down) = match pull {
Pull::None => (false, false),
Pull::Up => (true, false),
Pull::Down => (false, true),
};
match self.pio {
Pio::Pio0 => {
self.usb4604
.modify_reg::<Gpio0_7PullUp, _>(|r| r.set_gpio0_pu(pull_up))?;
self.usb4604
.modify_reg::<Gpio0_7PullDown, _>(|r| r.set_gpio0_pd(pull_down))?;
}
Pio::Pio1 => {
self.usb4604
.modify_reg::<Gpio0_7PullUp, _>(|r| r.set_gpio1_pu(pull_up))?;
self.usb4604
.modify_reg::<Gpio0_7PullDown, _>(|r| r.set_gpio1_pd(pull_down))?;
}
Pio::Pio3 => {
self.usb4604
.modify_reg::<Gpio0_7PullUp, _>(|r| r.set_gpio3_pu(pull_up))?;
self.usb4604
.modify_reg::<Gpio0_7PullDown, _>(|r| r.set_gpio3_pd(pull_down))?;
}
Pio::Pio8 => {
self.usb4604
.modify_reg::<Gpio8_10PullUp, _>(|r| r.set_gpio8_pu(pull_up))?;
self.usb4604
.modify_reg::<Gpio8_10PullDown, _>(|r| r.set_gpio8_pd(pull_down))?;
}
Pio::Pio9 => {
self.usb4604
.modify_reg::<Gpio8_10PullUp, _>(|r| r.set_gpio9_pu(pull_up))?;
self.usb4604
.modify_reg::<Gpio8_10PullDown, _>(|r| r.set_gpio9_pd(pull_down))?;
}
Pio::Pio10 => {
self.usb4604
.modify_reg::<Gpio8_10PullUp, _>(|r| r.set_gpio10_pu(pull_up))?;
self.usb4604
.modify_reg::<Gpio8_10PullDown, _>(|r| r.set_gpio10_pd(pull_down))?;
}
Pio::Pio19 => {
self.usb4604
.modify_reg::<Gpio17_20PullUp, _>(|r| r.set_gpio19_pu(pull_up))?;
self.usb4604
.modify_reg::<Gpio17_20PullDown, _>(|r| r.set_gpio19_pd(pull_down))?;
}
Pio::Pio20 => {
self.usb4604
.modify_reg::<Gpio17_20PullUp, _>(|r| r.set_gpio20_pu(pull_up))?;
self.usb4604
.modify_reg::<Gpio17_20PullDown, _>(|r| r.set_gpio20_pd(pull_down))?;
}
}
Ok(())
}
}
impl PushPullOutput {
pub fn set_high(&mut self) -> Result<(), Error> {
self.flex.set_high()
}
pub fn set_low(&mut self) -> Result<(), Error> {
self.flex.set_low()
}
pub fn toggle(&mut self) -> Result<(), Error> {
self.flex.toggle()
}
pub fn set_level(&mut self, level: Level) -> Result<(), Error> {
self.flex.set_level(level)
}
pub fn level(&self) -> Result<Level, Error> {
self.flex.get_output_level()
}
pub fn pio(&self) -> Pio {
self.flex.pio
}
}
impl Input {
pub fn is_high(&self) -> Result<bool, Error> {
self.flex.is_high()
}
pub fn is_low(&self) -> Result<bool, Error> {
self.flex.is_low()
}
pub fn pio(&self) -> Pio {
self.flex.pio
}
pub fn set_pull(&mut self, pull: Pull) -> Result<(), Error> {
self.flex.set_pull(pull)
}
}
impl OpenDrainOutput {
pub fn set_high_z(&mut self) -> Result<(), Error> {
self.flex.set_high()
}
pub fn set_low(&mut self) -> Result<(), Error> {
self.flex.set_low()
}
pub fn toggle(&mut self) -> Result<(), Error> {
self.flex.toggle()
}
pub fn set_level(&mut self, level: Level) -> Result<(), Error> {
self.flex.set_level(level)
}
pub fn level(&self) -> Result<Level, Error> {
if self.flex.is_out_en {
Ok(Level::Low)
} else {
self.flex.get_input_level()
}
}
pub fn pio(&self) -> Pio {
self.flex.pio
}
}