diff --git a/src/bin/main.rs b/src/bin/main.rs index 7dc87b9..d237723 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -98,7 +98,7 @@ async fn main(spawner: Spawner) { wdt.enable(); // Spawn the rendering task as soon as possible so it can start pushing pixels - spawner.must_spawn(renderbug_bike::tasks::render::render(peripherals.SPI2.degrade(), peripherals.DMA_CH2.degrade(), peripherals.GPIO5.degrade(), surfaces, safety_surfaces, display_controls, wdt)); + spawner.must_spawn(renderbug_bike::tasks::render::render(peripherals.SPI2.degrade(), peripherals.DMA_CH2.degrade(), peripherals.GPIO5.degrade(), surfaces, safety_surfaces, display_controls.clone(), wdt)); let imu_interrupt = PinInterrupt::new(Input::new(peripherals.GPIO36.degrade(), InputConfig::default()), Event::RisingEdge); let pd_interrupt = PinInterrupt::new(Input::new(peripherals.GPIO16.degrade(), InputConfig::default()), Event::RisingEdge); @@ -131,7 +131,7 @@ async fn main(spawner: Spawner) { spawner.must_spawn(renderbug_bike::tasks::mpu::mpu_task(motion_bus.dyn_sender(), I2cDevice::new(i2c_bus), imu_interrupt)); spawner.must_spawn(renderbug_bike::tasks::gps::gps_task(motion_bus.dyn_sender(), I2cDevice::new(i2c_bus))); - spawner.must_spawn(renderbug_bike::tasks::usb_power::usb_task(I2cDevice::new(i2c_bus), pd_interrupt)); + spawner.must_spawn(renderbug_bike::tasks::usb_power::usb_task(motion_bus.dyn_sender(), I2cDevice::new(i2c_bus), pd_interrupt, display_controls.clone())); } #[cfg(feature="oled")] diff --git a/src/tasks/usb_power.rs b/src/tasks/usb_power.rs index 6fb0975..c90e8d5 100644 --- a/src/tasks/usb_power.rs +++ b/src/tasks/usb_power.rs @@ -1,25 +1,52 @@ use embassy_embedded_hal::shared_bus::asynch::i2c::I2cDevice; -use embassy_sync::blocking_mutex::raw::NoopRawMutex; +use embassy_sync::{blocking_mutex::raw::NoopRawMutex, channel::DynamicSender}; use esp_hal::Async; +use figments_render::power::Milliwatts; use log::*; -use crate::{gpio_interrupt::PinInterrupt, tusb320::TUSB320}; +use crate::{events::{Measurement, SensorSource, SensorState}, gpio_interrupt::PinInterrupt, graphics::display::DisplayControls, tusb320::{ActiveCableConnected, DEVICE_ID, InterruptStatus, TUSB320}}; #[embassy_executor::task] -pub async fn usb_task(bus: I2cDevice<'static, NoopRawMutex, esp_hal::i2c::master::I2c<'static, Async>>, interrupt_pin: PinInterrupt<'static>) { +pub async fn usb_task(motion: DynamicSender<'static, Measurement>, bus: I2cDevice<'static, NoopRawMutex, esp_hal::i2c::master::I2c<'static, Async>>, interrupt_pin: PinInterrupt<'static>, mut display_control: DisplayControls) { let mut tusb = TUSB320::new(bus); + motion.send(Measurement::SensorHardwareStatus(SensorSource::ExternalPower, SensorState::AcquiringFix)).await; match tusb.get_device_id().await { - Ok(val) => { - info!("TUSB320 Device ID: {val:?}"); + Ok(DEVICE_ID) => { + info!("TUSB320 connected"); + motion.send(Measurement::SensorHardwareStatus(SensorSource::ExternalPower, SensorState::Degraded)).await; }, - Err(_) => { - error!("Failed to read from TUSB320"); + Ok(val) => { + error!("Unexpected TUSB320 Device ID: {val:?}! Power management is disabled!!"); + motion.send(Measurement::SensorHardwareStatus(SensorSource::ExternalPower, SensorState::Offline)).await; + return; + }, + Err(e) => { + error!("Failed to read from TUSB320! Power management is disabled!! {e:?}"); + motion.send(Measurement::SensorHardwareStatus(SensorSource::ExternalPower, SensorState::Offline)).await; + return; } } loop { - log::info!("Waiting for USB power interrupt..."); + match tusb.active_cable_connected().read().await.unwrap() { + ActiveCableConnected::Connected => { + let current_mode = tusb.current_mode_detect().read().await.unwrap(); + let cur_mw = Milliwatts(current_mode.milliwatts()); + info!("USB reports current mode: {current_mode:?}, {cur_mw:?} mW"); + display_control.set_max_power(cur_mw); + motion.send(Measurement::ExternalPower(cur_mw)).await; + motion.send(Measurement::SensorHardwareStatus(SensorSource::ExternalPower, SensorState::Online)).await; + }, + ActiveCableConnected::Disconnected => { + warn!("USB power is disconnected! Setting display power to zero."); + // FIXME: We should also send an event through the system to report that the power is down, and the renderer should stop + display_control.set_max_power(Milliwatts(0)); + motion.send(Measurement::ExternalPower(Milliwatts(0))).await; + motion.send(Measurement::SensorHardwareStatus(SensorSource::ExternalPower, SensorState::Degraded)).await; + } + } interrupt_pin.wait_for_interrupt().await; + tusb.interrupt_status().write(InterruptStatus::Clear).await.ok(); } } \ No newline at end of file diff --git a/src/tusb320.rs b/src/tusb320.rs index 335f07c..93a2e5a 100644 --- a/src/tusb320.rs +++ b/src/tusb320.rs @@ -1,13 +1,6 @@ -use embassy_embedded_hal::shared_bus::{I2cDeviceError, asynch::i2c::I2cDevice}; -use embassy_sync::blocking_mutex::raw::NoopRawMutex; -use esp_hal::Async; -use embedded_hal_async::i2c::I2c; +use core::marker::PhantomData; -// TODO: rewrite this to only use embedded_hal_async traits, then publish as a crate -const USB_ADDR: u8 = 0b1100000; -pub struct TUSB320 { - bus: I2cDevice<'static, NoopRawMutex, esp_hal::i2c::master::I2c<'static, Async>> -} +use embedded_hal_async::i2c::I2c as AsyncI2c; #[derive(Clone, Copy, Debug)] pub enum RegisterAddress { @@ -25,307 +18,253 @@ pub enum RegisterAddress { DisableRDRP = 0x45 } -#[derive(Clone, Copy, Debug)] -pub enum Register { - DeviceID, - DeviceID1, - DeviceID2, - DeviceID3, - DeviceID4, - DeviceID5, - DeviceID6, - DeviceID7, - CurrentAdvertisement, - CurrentDetect, - AccessoryConnected, - ActiveCableConnected, - AttachedState, - CableOrientation, - InterruptStatus, - DrpDutyCycle, - Debounce, - ModeSelect, - SoftReset, - DisableRDRP +pub trait RegisterField { + fn mask() -> u8; + fn shift() -> u8; + fn address() -> RegisterAddress; } -impl From for RegisterAddress { - fn from(reg: Register) -> Self { - match reg { - Register::DeviceID => RegisterAddress::DeviceID, - Register::DeviceID1 => RegisterAddress::DeviceID1, - Register::DeviceID2 => RegisterAddress::DeviceID2, - Register::DeviceID3 => RegisterAddress::DeviceID3, - Register::DeviceID4 => RegisterAddress::DeviceID4, - Register::DeviceID5 => RegisterAddress::DeviceID5, - Register::DeviceID6 => RegisterAddress::DeviceID6, - Register::DeviceID7 => RegisterAddress::DeviceID7, +macro_rules! define_register_field { + ($name:tt address=$address:tt offset=$shift:tt mask=$bitmask:tt $($value:tt = $valuemask:tt),+) => { + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum $name { + $($value = $valuemask),+ + } - Register::CurrentAdvertisement => RegisterAddress::Status1, - Register::CurrentDetect => RegisterAddress::Status1, - Register::AccessoryConnected => RegisterAddress::Status1, - Register::ActiveCableConnected => RegisterAddress::Status1 - , - Register::AttachedState => RegisterAddress::Status2, - Register::CableOrientation => RegisterAddress::Status2, - Register::InterruptStatus => RegisterAddress::Status2, - Register::DrpDutyCycle => RegisterAddress::Status2, + impl From for $name { + fn from(value: u8) -> Self { + match value & 0b11 { + $($valuemask => $name::$value),+, + _ => unreachable!() + } + } + } - Register::Debounce => RegisterAddress::Status3, - Register::ModeSelect => RegisterAddress::Status3, - Register::SoftReset => RegisterAddress::Status3, - Register::DisableRDRP => RegisterAddress::DisableRDRP + impl From<$name> for u8 { + fn from(value: $name) -> Self { + value as u8 + } + } + + impl RegisterField for $name { + fn mask() -> u8 { + $bitmask + } + + fn shift() -> u8 { + $shift + } + + fn address() -> RegisterAddress { + RegisterAddress::$address + } + } + }; +} + + +define_register_field!{ + CurrentAdvertisement + address=Status1 + offset=6 + mask=0b11 + Default = 0b00, + // 1.5A + Medium = 0b01, + // 3A + High = 0b10 +} + +define_register_field!{ + CurrentModeDetect + address=Status1 + offset=4 + mask=0b11 + // 500mA + Default = 0b00, + // 1.5A + Medium = 0b01, + // 500mA + ThroughAccessory = 0b10, + // 3A + High = 0b11 +} + +impl CurrentModeDetect { + pub fn milliwatts(&self) -> u32 { + match self { + CurrentModeDetect::Default => 500, + CurrentModeDetect::Medium => 1500, + CurrentModeDetect::ThroughAccessory => 500, + CurrentModeDetect::High => 3000 } } } -pub enum CurrentAdvertisement { - /// 500mA / 900mA - Default = 0b00, - /// 1.5A - Medium = 0b01, - /// 3A - High = 0b10, - /// Reserved, do not use - Reserved = 0b11 -} - -pub enum CurrentModeDetect { - /// Default value at startup. TODO: Does this mean 500ma, or just 'no connection'? - Default = 0b00, - /// 1.5A - Medium = 0b01, - /// 500ma - ThroughAccessory = 0b10, - /// 3A - High = 0b11, -} - -pub enum AccessoryConnectionState { +define_register_field!{ + AccessoryConnected + address=Status1 + offset=1 + mask=0b111 None = 0b000, Audio = 0b100, ThroughAccessory = 0b101, - DebugAccessory = 0b110, - // All other binary patterns are 'reserved' - Reserved + DebugAccessory = 0b110 + // All other patterns are 'reserved' and should not be possible } -pub enum AttachedState { +define_register_field!{ + ActiveCableConnected + address=Status1 + offset=0 + mask=0b1 + Disconnected = 0, + Connected = 1 +} + +define_register_field!{ + AttachedState + address=Status2 + offset=6 + mask=0b11 Unattached = 0b00, Source = 0b01, Sink = 0b10, Accessory = 0b11 } -pub enum CableOrientation { +define_register_field! { + CableOrientation + address=Status2 + offset=5 + mask=0b1 Normal = 0, Flipped = 1 } -pub enum DrpDutyCycle { - /// 30% +define_register_field! { + InterruptStatus + address=Status2 + offset=4 + mask=0b1 + Clear = 0, + Set = 1 +} + +define_register_field! { + DrpDutyCycle + address=Status2 + offset=1 + mask=0b11 + // 30% Default = 0b00, - /// 40% + // 40% Fast = 0b01, - /// 50% + // 50% Faster = 0b10, - /// 60% + // 60% Fastest = 0b11 } -pub enum Debounce { - /// 133ms (default) +define_register_field! { + Debounce + address=Status3 + offset=6 + mask=0b11 + // 133ms (default) D133 = 0b00, - /// 116ms + // 116ms D116 = 0b01, - /// 151ms + // 151ms D151 = 0b10, - /// 168ms + // 168ms D168 = 0b11 } -pub enum Mode { +define_register_field! { + ModeSelect + address=Status3 + offset=4 + mask=0b11 UsePortPin = 0b00, UFP = 0b01, DFP = 0b10, DRP = 0b11 } -impl TUSB320 { - pub const fn new(bus: I2cDevice<'static, NoopRawMutex, esp_hal::i2c::master::I2c<'static, Async>>) -> Self { +define_register_field! { + DisableRDRP + address=DisableRDRP + offset=2 + mask=0b1 + Enabled = 0, + Disabled = 1 +} + +pub const DEVICE_ID: [u8; 8] = *b"TUSB320A"; + +const USB_ADDR_WRITE: u8 = 0b1100000; +const USB_ADDR_READ: u8 = 0b1100001; +pub struct TUSB320 { + bus: T +} + +impl TUSB320 { + pub const fn new(bus: T) -> Self { Self { bus } } - pub async fn get_current_advertisement(&mut self) -> Result> { - let reg: RegisterAddress = Register::CurrentAdvertisement.into(); - let val = self.read_address(reg).await? >> 6; - match val & 0b11 { - 0b00 => Ok(CurrentAdvertisement::Default), - 0b01 => Ok(CurrentAdvertisement::Medium), - 0b10 => Ok(CurrentAdvertisement::High), - 0b11 => Ok(CurrentAdvertisement::Reserved), - _ => unreachable!() - } - } - - pub async fn set_current_advertisement(&mut self, adv: CurrentAdvertisement) -> Result<(), I2cDeviceError> { - let reg: RegisterAddress = Register::CurrentAdvertisement.into(); - let mut val = self.read_address(reg).await?; - let mask = !(0b11 << 6); - val &= mask | ((adv as u8) << 6); - self.write_address(reg, val).await?; - Ok(()) - } - - pub async fn get_current_mode_detect(&mut self) -> Result> { - let reg: RegisterAddress = Register::CurrentDetect.into(); - let val = self.read_address(reg).await? >> 4; - match val & 0b11 { - 0b00 => Ok(CurrentModeDetect::Default), - 0b01 => Ok(CurrentModeDetect::Medium), - 0b10 => Ok(CurrentModeDetect::ThroughAccessory), - 0b11 => Ok(CurrentModeDetect::High), - _ => unreachable!() - } - } - - pub async fn get_accessory_connection_state(&mut self) -> Result> { - let reg: RegisterAddress = Register::AccessoryConnected.into(); - let val = self.read_address(reg).await? >> 1; - match val & 0b111 { - 0b000 => Ok(AccessoryConnectionState::None), - 0b100 => Ok(AccessoryConnectionState::Audio), - 0b101 => Ok(AccessoryConnectionState::ThroughAccessory), - 0b110 => Ok(AccessoryConnectionState::DebugAccessory), - _ => Ok(AccessoryConnectionState::Reserved) - } - } - - pub async fn get_cable_detected(&mut self) -> Result> { - let reg: RegisterAddress = Register::ActiveCableConnected.into(); - let val = self.read_address(reg).await?; - Ok((val & 0b1) == 1) - } - - pub async fn get_attached_state(&mut self) -> Result> { - let reg: RegisterAddress = Register::AttachedState.into(); - let val = self.read_address(reg).await? >> 6; - match val & 0b11 { - 0b00 => Ok(AttachedState::Unattached), - 0b01 => Ok(AttachedState::Source), - 0b10 => Ok(AttachedState::Sink), - 0b11 => Ok(AttachedState::Accessory), - _ => unreachable!() - } - } - - pub async fn get_cable_orientation(&mut self) -> Result> { - let reg: RegisterAddress = Register::CableOrientation.into(); - let val = self.read_address(reg).await? >> 5; - match val & 0b1 { - 0 => Ok(CableOrientation::Normal), - 1 => Ok(CableOrientation::Flipped), - _ => unreachable!() - } - } - - pub async fn get_interrupt_status(&mut self) -> Result> { - let reg: RegisterAddress = Register::InterruptStatus.into(); - Ok((self.read_address(reg).await? >> 4) == 1) - } - - pub async fn clear_interrupt(&mut self) -> Result<(), I2cDeviceError> { - let reg: RegisterAddress = Register::InterruptStatus.into(); - let mut val = self.read_address(reg).await?; - val &= !(0b1 << 4); - self.write_address(reg, val).await - } - - pub async fn set_drp_duty_cycle(&mut self, duty: DrpDutyCycle) -> Result<(), I2cDeviceError> { - let reg: RegisterAddress = Register::DrpDutyCycle.into(); - let mut val = self.read_address(reg).await?; - let mask = !(0b11 << 1); - val &= mask | ((duty as u8) << 1); - self.write_address(reg, val).await - } - - pub async fn get_drp_duty_cycle(&mut self) -> Result> { - let reg: RegisterAddress = Register::DrpDutyCycle.into(); - let val = self.read_address(reg).await? >> 1; - match val & 0b11 { - 0b00 => Ok(DrpDutyCycle::Default), - 0b01 => Ok(DrpDutyCycle::Fast), - 0b10 => Ok(DrpDutyCycle::Faster), - 0b11 => Ok(DrpDutyCycle::Fastest), - _ => unreachable!() - } - } - - pub async fn get_debounce(&mut self) -> Result> { - let reg: RegisterAddress = Register::Debounce.into(); - let val = self.read_address(reg).await? >> 6; - match val & 0b11 { - 0b00 => Ok(Debounce::D133), - 0b01 => Ok(Debounce::D116), - 0b10 => Ok(Debounce::D151), - 0b11 => Ok(Debounce::D168), - _ => unreachable!() - } - } - - pub async fn set_debounce(&mut self, debounce: Debounce) -> Result<(), I2cDeviceError> { - let reg: RegisterAddress = Register::Debounce.into(); - let mut val = self.read_address(reg).await?; - let mask = !(0b11 << 6); - val &= mask | ((debounce as u8) << 6); - self.write_address(reg, val).await - } - - pub async fn get_mode(&mut self) -> Result> { - let reg: RegisterAddress = Register::ModeSelect.into(); - let val = self.read_address(reg).await? >> 4; - match val & 0b11 { - 0b00 => Ok(Mode::UsePortPin), - 0b01 => Ok(Mode::UFP), - 0b10 => Ok(Mode::DFP), - 0b11 => Ok(Mode::DRP), - _ => unreachable!() - } - } - - pub async fn set_mode(&mut self, mode: Mode) -> Result<(), I2cDeviceError> { - let reg: RegisterAddress = Register::ModeSelect.into(); - let mut val = self.read_address(reg).await?; - let mask = !(0b11 << 4); - val &= mask | ((mode as u8) << 4); - self.write_address(reg, val).await - } - - pub async fn soft_reset(&mut self) -> Result<(), I2cDeviceError> { - let reg: RegisterAddress = Register::SoftReset.into(); - let mut val = self.read_address(reg).await?; + pub async fn soft_reset(&mut self) -> Result<(), T::Error> { + let mut val = self.read_address(RegisterAddress::Status3).await?; val |= 0b1 << 3; - self.write_address(reg, val).await + self.write_address(RegisterAddress::Status3, val).await } - pub async fn set_rdrp_disabled(&mut self, disable: bool) -> Result<(), I2cDeviceError> { - let reg: RegisterAddress = Register::DisableRDRP.into(); - let mut val = self.read_address(reg).await?; - if disable { - val |= 0b1; - } else { - val &= !0b1; - } - self.write_address(reg, val).await + pub fn register<'a, R: RegisterField>(&'a mut self) -> UpdateRegister<'a, T, R> { + UpdateRegister::new(R::address(), self) } - pub async fn get_rdrp_disabled(&mut self) -> Result> { - let reg: RegisterAddress = Register::DisableRDRP.into(); - let val = self.read_address(reg).await? >> 2; - Ok((val & 0b1) == 1) - } + pub fn current_advertisement<'a>(&'a mut self) -> UpdateRegister<'a, T, CurrentAdvertisement> { + self.register() + } - pub async fn get_device_id(&mut self) -> Result<[u8;8], I2cDeviceError> { + pub fn current_mode_detect<'a>(&'a mut self) -> UpdateRegister<'a, T, CurrentModeDetect> { + self.register() + } + + pub fn accessory_connected<'a>(&'a mut self) -> UpdateRegister<'a, T, AccessoryConnected> { + self.register() + } + + pub fn active_cable_connected<'a>(&'a mut self) -> UpdateRegister<'a, T, ActiveCableConnected> { + self.register() + } + + pub fn cable_orientation<'a>(&'a mut self) -> UpdateRegister<'a, T, CableOrientation> { + self.register() + } + + pub fn interrupt_status<'a>(&'a mut self) -> UpdateRegister<'a, T, InterruptStatus> { + self.register() + } + + pub fn drp_duty_cycle<'a>(&'a mut self) -> UpdateRegister<'a, T, DrpDutyCycle> { + self.register() + } + + pub fn debounce<'a>(&'a mut self) -> UpdateRegister<'a, T, Debounce> { + self.register() + } + + pub fn mode_select<'a>(&'a mut self) -> UpdateRegister<'a, T, ModeSelect> { + self.register() + } + + pub fn disable_rdrp<'a>(&'a mut self) -> UpdateRegister<'a, T, DisableRDRP> { + self.register() + } + + pub async fn get_device_id(&mut self) -> Result<[u8;8], T::Error> { Ok([ self.read_address(RegisterAddress::DeviceID).await?, self.read_address(RegisterAddress::DeviceID1).await?, @@ -338,19 +277,54 @@ impl TUSB320 { ]) } - pub async fn read_address(&mut self, reg: RegisterAddress) -> Result> { + async fn read_address(&mut self, reg: RegisterAddress) -> Result { let mut response = [0; 1]; - // 1 means 'read' cycle - let i2c_addr = USB_ADDR | 1; - self.bus.write_read(i2c_addr, &[reg as u8], &mut response).await?; + self.bus.write_read(USB_ADDR_READ, &[reg as u8], &mut response).await?; Ok(response[0]) } - pub async fn write_address(&mut self, reg: RegisterAddress, value: u8) -> Result<(), I2cDeviceError> { - // 0 means 'write' cycle - let i2c_addr = USB_ADDR | 0; - self.bus.write(i2c_addr, &[reg as u8]).await?; - self.bus.write(i2c_addr, &[value]).await?; + async fn write_address(&mut self, reg: RegisterAddress, value: u8) -> Result<(), T::Error> { + // FIXME: I think this needs to use self.bus.transaction() + self.bus.write(USB_ADDR_WRITE, &[reg as u8]).await?; + self.bus.write(USB_ADDR_WRITE, &[value]).await?; Ok(()) } } + +pub struct UpdateRegister<'a, I2C: AsyncI2c, T> { + reg: RegisterAddress, + raw: u8, + dev: &'a mut TUSB320, + _type: PhantomData +} + +impl<'a, I2C: AsyncI2c, T> UpdateRegister<'a, I2C, T> { + const fn new(reg: RegisterAddress, dev: &'a mut TUSB320) -> Self { + Self { reg, raw: 0, dev, _type: PhantomData } + } + + pub async fn refresh(&mut self) -> Result<(), I2C::Error> { + self.raw = self.dev.read_address(self.reg).await?; + Ok(()) + } +} + +impl<'a, I2C: AsyncI2c, T: RegisterField + Into> UpdateRegister<'a, I2C, T> where u8: Into { + pub async fn write(&mut self, value: T) -> Result<(), I2C::Error> { + let mask = T::mask(); + let v: u8 = value.into(); + self.refresh().await?; + self.raw &= !(mask | (v << T::shift())); + self.dev.write_address(self.reg, self.raw).await?; + Ok(()) + } + + pub async fn read(&mut self) -> Result { + self.refresh().await?; + Ok(self.value()) + } + + pub fn value(&mut self) -> T { + (self.raw >> T::shift()).into() + } +} \ No newline at end of file