tasks: first implementation of protocol for the TUSB320 USB-PD chip. also untested!!!

This commit is contained in:
2026-03-11 14:01:40 +01:00
parent ccb2680954
commit e09649592b
5 changed files with 387 additions and 0 deletions

View File

@@ -131,6 +131,9 @@ 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::mpu::mpu_task(motion_bus.dyn_sender(), I2cDevice::new(i2c_bus), imu_interrupt));
#[cfg(feature="gps")] #[cfg(feature="gps")]
spawner.must_spawn(renderbug_bike::tasks::gps::gps_task(motion_bus.dyn_sender(), I2cDevice::new(i2c_bus))); spawner.must_spawn(renderbug_bike::tasks::gps::gps_task(motion_bus.dyn_sender(), I2cDevice::new(i2c_bus)));
// TODO: Everything i2c should be turned on by default, I guess. we don't need features for individual sensors, but we can keep the option to disable the whole bus for testing purposes
spawner.must_spawn(renderbug_bike::tasks::usb_power::usb_task(I2cDevice::new(i2c_bus), pd_interrupt));
} }
#[cfg(feature="oled")] #[cfg(feature="oled")]

View File

@@ -12,6 +12,7 @@ pub mod tracing;
pub mod storage; pub mod storage;
pub mod simdata; pub mod simdata;
pub mod gpio_interrupt; pub mod gpio_interrupt;
pub mod tusb320;
extern crate alloc; extern crate alloc;

View File

@@ -13,6 +13,8 @@ pub mod demo;
#[cfg(feature="oled")] #[cfg(feature="oled")]
pub mod oled_render; pub mod oled_render;
pub mod usb_power;
// Prediction engines // Prediction engines
pub mod motion; pub mod motion;

25
src/tasks/usb_power.rs Normal file
View File

@@ -0,0 +1,25 @@
use embassy_embedded_hal::shared_bus::asynch::i2c::I2cDevice;
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
use esp_hal::Async;
use log::*;
use crate::{gpio_interrupt::PinInterrupt, tusb320::TUSB320};
#[embassy_executor::task]
pub async fn usb_task(bus: I2cDevice<'static, NoopRawMutex, esp_hal::i2c::master::I2c<'static, Async>>, interrupt_pin: PinInterrupt<'static>) {
let mut tusb = TUSB320::new(bus);
match tusb.get_device_id().await {
Ok(val) => {
info!("TUSB320 Device ID: {val:?}");
},
Err(_) => {
error!("Failed to read from TUSB320");
}
}
loop {
log::info!("Waiting for USB power interrupt...");
interrupt_pin.wait_for_interrupt().await;
}
}

356
src/tusb320.rs Normal file
View File

@@ -0,0 +1,356 @@
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;
// 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>>
}
#[derive(Clone, Copy, Debug)]
pub enum RegisterAddress {
DeviceID = 0x00,
DeviceID1 = 0x01,
DeviceID2 = 0x02,
DeviceID3 = 0x03,
DeviceID4 = 0x04,
DeviceID5 = 0x05,
DeviceID6 = 0x06,
DeviceID7 = 0x07,
Status1 = 0x08,
Status2 = 0x09,
Status3 = 0x0a,
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
}
impl From<Register> 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,
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,
Register::Debounce => RegisterAddress::Status3,
Register::ModeSelect => RegisterAddress::Status3,
Register::SoftReset => RegisterAddress::Status3,
Register::DisableRDRP => RegisterAddress::DisableRDRP
}
}
}
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 {
None = 0b000,
Audio = 0b100,
ThroughAccessory = 0b101,
DebugAccessory = 0b110,
// All other binary patterns are 'reserved'
Reserved
}
pub enum AttachedState {
Unattached = 0b00,
Source = 0b01,
Sink = 0b10,
Accessory = 0b11
}
pub enum CableOrientation {
Normal = 0,
Flipped = 1
}
pub enum DrpDutyCycle {
/// 30%
Default = 0b00,
/// 40%
Fast = 0b01,
/// 50%
Faster = 0b10,
/// 60%
Fastest = 0b11
}
pub enum Debounce {
/// 133ms (default)
D133 = 0b00,
/// 116ms
D116 = 0b01,
/// 151ms
D151 = 0b10,
/// 168ms
D168 = 0b11
}
pub enum Mode {
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 {
Self { bus }
}
pub async fn get_current_advertisement(&mut self) -> Result<CurrentAdvertisement, I2cDeviceError<esp_hal::i2c::master::Error>> {
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<esp_hal::i2c::master::Error>> {
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<CurrentModeDetect, I2cDeviceError<esp_hal::i2c::master::Error>> {
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<AccessoryConnectionState, I2cDeviceError<esp_hal::i2c::master::Error>> {
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<bool, I2cDeviceError<esp_hal::i2c::master::Error>> {
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<AttachedState, I2cDeviceError<esp_hal::i2c::master::Error>> {
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<CableOrientation, I2cDeviceError<esp_hal::i2c::master::Error>> {
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<bool, I2cDeviceError<esp_hal::i2c::master::Error>> {
let reg: RegisterAddress = Register::InterruptStatus.into();
Ok((self.read_address(reg).await? >> 4) == 1)
}
pub async fn clear_interrupt(&mut self) -> Result<(), I2cDeviceError<esp_hal::i2c::master::Error>> {
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<esp_hal::i2c::master::Error>> {
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<DrpDutyCycle, I2cDeviceError<esp_hal::i2c::master::Error>> {
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<Debounce, I2cDeviceError<esp_hal::i2c::master::Error>> {
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<esp_hal::i2c::master::Error>> {
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<Mode, I2cDeviceError<esp_hal::i2c::master::Error>> {
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<esp_hal::i2c::master::Error>> {
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<esp_hal::i2c::master::Error>> {
let reg: RegisterAddress = Register::SoftReset.into();
let mut val = self.read_address(reg).await?;
val |= 0b1 << 3;
self.write_address(reg, val).await
}
pub async fn set_rdrp_disabled(&mut self, disable: bool) -> Result<(), I2cDeviceError<esp_hal::i2c::master::Error>> {
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 async fn get_rdrp_disabled(&mut self) -> Result<bool, I2cDeviceError<esp_hal::i2c::master::Error>> {
let reg: RegisterAddress = Register::DisableRDRP.into();
let val = self.read_address(reg).await? >> 2;
Ok((val & 0b1) == 1)
}
pub async fn get_device_id(&mut self) -> Result<[u8;8], I2cDeviceError<esp_hal::i2c::master::Error>> {
Ok([
self.read_address(RegisterAddress::DeviceID).await?,
self.read_address(RegisterAddress::DeviceID1).await?,
self.read_address(RegisterAddress::DeviceID2).await?,
self.read_address(RegisterAddress::DeviceID3).await?,
self.read_address(RegisterAddress::DeviceID4).await?,
self.read_address(RegisterAddress::DeviceID5).await?,
self.read_address(RegisterAddress::DeviceID6).await?,
self.read_address(RegisterAddress::DeviceID7).await?
])
}
pub async fn read_address(&mut self, reg: RegisterAddress) -> Result<u8, I2cDeviceError<esp_hal::i2c::master::Error>> {
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?;
Ok(response[0])
}
pub async fn write_address(&mut self, reg: RegisterAddress, value: u8) -> Result<(), I2cDeviceError<esp_hal::i2c::master::Error>> {
// 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?;
Ok(())
}
}