tasks: first implementation of protocol for the TUSB320 USB-PD chip. also untested!!!
This commit is contained in:
@@ -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));
|
||||
#[cfg(feature="gps")]
|
||||
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")]
|
||||
|
||||
@@ -12,6 +12,7 @@ pub mod tracing;
|
||||
pub mod storage;
|
||||
pub mod simdata;
|
||||
pub mod gpio_interrupt;
|
||||
pub mod tusb320;
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ pub mod demo;
|
||||
#[cfg(feature="oled")]
|
||||
pub mod oled_render;
|
||||
|
||||
pub mod usb_power;
|
||||
|
||||
// Prediction engines
|
||||
pub mod motion;
|
||||
|
||||
|
||||
25
src/tasks/usb_power.rs
Normal file
25
src/tasks/usb_power.rs
Normal 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
356
src/tusb320.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user