From 7089e67e1fecdadb1dc502ce2e119829b057d871 Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 12 May 2024 13:48:16 +0100 Subject: [PATCH 1/7] usb attach and detach events and pmic api Power micropython API including being able to turn the badge off. USB host and device attach and detach event detection. PD comms disabled or not yet implemented. Makes change to i2c to allow usage from c. Also some fixes for fusb python module. --- drivers/micropython.cmake | 4 + drivers/tildagon_i2c/tildagon_i2c.c | 39 +- drivers/tildagon_i2c/tildagon_i2c.h | 14 + drivers/tildagon_power/bq25895/bq25895.c | 173 ++++++ drivers/tildagon_power/bq25895/bq25895.h | 79 +++ drivers/tildagon_power/fusb302b/fusb302b.c | 392 ++++++++++++ drivers/tildagon_power/fusb302b/fusb302b.h | 158 +++++ drivers/tildagon_power/fusb302b/fusb302b_pd.c | 211 +++++++ drivers/tildagon_power/fusb302b/fusb302b_pd.h | 192 ++++++ drivers/tildagon_power/mp_power.c | 234 ++++++++ drivers/tildagon_power/tildagon_power.c | 565 ++++++++++++++++++ drivers/tildagon_power/tildagon_power.cmake | 22 + drivers/tildagon_power/tildagon_power.h | 47 ++ modules/fusb302b.py | 63 +- tildagon/board_init.c | 8 +- 15 files changed, 2155 insertions(+), 46 deletions(-) create mode 100644 drivers/tildagon_power/bq25895/bq25895.c create mode 100644 drivers/tildagon_power/bq25895/bq25895.h create mode 100644 drivers/tildagon_power/fusb302b/fusb302b.c create mode 100644 drivers/tildagon_power/fusb302b/fusb302b.h create mode 100644 drivers/tildagon_power/fusb302b/fusb302b_pd.c create mode 100644 drivers/tildagon_power/fusb302b/fusb302b_pd.h create mode 100644 drivers/tildagon_power/mp_power.c create mode 100644 drivers/tildagon_power/tildagon_power.c create mode 100644 drivers/tildagon_power/tildagon_power.cmake create mode 100644 drivers/tildagon_power/tildagon_power.h diff --git a/drivers/micropython.cmake b/drivers/micropython.cmake index 4e5a1b1..66318be 100644 --- a/drivers/micropython.cmake +++ b/drivers/micropython.cmake @@ -8,3 +8,7 @@ include(${CMAKE_CURRENT_LIST_DIR}/tildagon_usb/tildagon_usb.cmake) # Add TCA9548A I2C MUX and micropython machine.I2C bindings include(${CMAKE_CURRENT_LIST_DIR}/tildagon_i2c/tildagon_i2c.cmake) + +# Add PMIC and usb PD and micropython power bindings +include(${CMAKE_CURRENT_LIST_DIR}/tildagon_power/tildagon_power.cmake) + diff --git a/drivers/tildagon_i2c/tildagon_i2c.c b/drivers/tildagon_i2c/tildagon_i2c.c index bcec9a4..25cd2d6 100644 --- a/drivers/tildagon_i2c/tildagon_i2c.c +++ b/drivers/tildagon_i2c/tildagon_i2c.c @@ -36,7 +36,6 @@ #include "driver/i2c.h" #include "hal/i2c_ll.h" -#include "tca9548a.h" #include "tildagon_i2c.h" @@ -48,16 +47,23 @@ #define MP_I2C_MUX_PORT_MAX (7) -typedef struct _tildagon_mux_i2c_obj_t { - mp_obj_base_t base; - const tca9548a_i2c_mux_t *mux; - tca9548a_i2c_port_t port; -} tildagon_mux_i2c_obj_t; static tildagon_mux_i2c_obj_t tildagon_mux_i2c_obj[8]; static tca9548a_i2c_mux_t tildagon_i2c_mux; +tildagon_mux_i2c_obj_t* tildagon_get_mux_obj( uint8_t port ) +{ + if ( tildagon_mux_i2c_obj[port].base.type == NULL ) + { + // Created for the first time + tildagon_mux_i2c_obj[port].base.type = &machine_i2c_type; + tildagon_mux_i2c_obj[port].mux = tildagon_get_i2c_mux(); + tildagon_mux_i2c_obj[port].port = port; + } + return &tildagon_mux_i2c_obj[port]; +} + const tca9548a_i2c_mux_t *tildagon_get_i2c_mux() { return &tildagon_i2c_mux; } @@ -81,16 +87,11 @@ void tildagon_i2c_init() { i2c_driver_install(TILDAGON_HOST_I2C_PORT, I2C_MODE_MASTER, 0, 0, 0); } -int tildagon_mux_i2c_transfer(mp_obj_base_t *self_in, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags) { - tildagon_mux_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in); - if (addr == self->mux->addr) { - return -MP_ENODEV; - } - - - i2c_cmd_handle_t cmd = i2c_cmd_link_create(); +int tildagon_mux_i2c_transaction(tildagon_mux_i2c_obj_t *self, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags) { + int data_len = 0; + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); if (flags & MP_MACHINE_I2C_FLAG_WRITE1) { i2c_master_start(cmd); @@ -134,6 +135,16 @@ int tildagon_mux_i2c_transfer(mp_obj_base_t *self_in, uint16_t addr, size_t n, m return data_len; } +int tildagon_mux_i2c_transfer(mp_obj_base_t *self_in, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags) { + tildagon_mux_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in); + + if (addr == self->mux->addr) { + return -MP_ENODEV; + } + + return tildagon_mux_i2c_transaction( self, addr, n, bufs, flags); +} + /******************************************************************************/ // MicroPython bindings for machine API diff --git a/drivers/tildagon_i2c/tildagon_i2c.h b/drivers/tildagon_i2c/tildagon_i2c.h index 98b281c..ff0d2d3 100644 --- a/drivers/tildagon_i2c/tildagon_i2c.h +++ b/drivers/tildagon_i2c/tildagon_i2c.h @@ -1,6 +1,11 @@ #ifndef _MICROPY_PY_TILDAGON_I2C #define _MICROPY_PY_TILDAGON_I2C + +#include "tca9548a.h" +#include "driver/i2c.h" +#include "extmod/modmachine.h" + #if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 #define I2C_SCLK_FREQ XTAL_CLK_FREQ #elif CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 @@ -15,9 +20,18 @@ #define TILDAGON_HOST_I2C_PORT (0) #define TILDAGON_HOST_I2C_TIMEOUT (50000) +typedef struct _tildagon_mux_i2c_obj_t { + mp_obj_base_t base; + const tca9548a_i2c_mux_t *mux; + tca9548a_i2c_port_t port; +} tildagon_mux_i2c_obj_t; + const tca9548a_i2c_mux_t *tildagon_get_i2c_mux(); +tildagon_mux_i2c_obj_t *tildagon_get_mux_obj( uint8_t port ); + void tildagon_i2c_init(); +int tildagon_mux_i2c_transaction(tildagon_mux_i2c_obj_t *self_in, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags); #endif // _MICROPY_PY_TILDAGON_I2C \ No newline at end of file diff --git a/drivers/tildagon_power/bq25895/bq25895.c b/drivers/tildagon_power/bq25895/bq25895.c new file mode 100644 index 0000000..88155db --- /dev/null +++ b/drivers/tildagon_power/bq25895/bq25895.c @@ -0,0 +1,173 @@ +/* + BQ25895 power management ic driver in c + +The driver supports basic read write access with some helpers and setup + +*/ + +#include "bq25895.h" + +typedef struct +{ + uint8_t regaddr; + uint8_t mask; + uint8_t position; +} bq_register_t; + +typedef struct +{ + uint8_t regaddr; + uint8_t mask; + uint8_t position; + float scaling; + float offset; +} scaled_register_t; + +#define ADDRESS 0x6A +#define READ ( MP_MACHINE_I2C_FLAG_WRITE1 | MP_MACHINE_I2C_FLAG_READ | MP_MACHINE_I2C_FLAG_STOP ) +#define WRITE MP_MACHINE_I2C_FLAG_STOP + +static const scaled_register_t input_Ilim = { 0x00U, 0x3FU, 0U, 50.0F, 100.0F }; +static const bq_register_t Ilim_pin = { 0x00U, 0x40U, 6U }; +static const bq_register_t enable_HiZ = { 0x00U, 0x80U, 7U }; +static const bq_register_t otg_boost = { 0x03U, 0x20U, 5U }; +static const bq_register_t batfet_disable = { 0x09U, 0x20U, 5U }; +static const bq_register_t register_reset = { 0x14U, 0x80U, 7U }; + +static void write_bits( bq_state_t* state, bq_register_t reg, uint8_t value ); +static void write_scaled( bq_state_t* state, scaled_register_t scaledregister, float value ); + +/** + * @brief initialise the bq25895 + * @details reset then setup 500mA Iin limit, boost disabled, charging enabled, + * ADC at 1Hz, disable unused features and disable watchdog to reduce interruptions + * @param state pmic object + */ +void bq_init( bq_state_t* state ) +{ + write_bits( state, register_reset, 1 ); + uint8_t write_buffer[3] = { 0x02, 0x60, 0x1A }; + mp_machine_i2c_buf_t buffer = { .len = 3, .buf = write_buffer }; + tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 1, &buffer, WRITE ); + write_buffer[0] = 0x07; + write_buffer[1] = 0x8C; + buffer.len = 2; + tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 1, &buffer, WRITE ); +} + +/** + * @brief Put the converter into High impedance mode on the input to prevent + * current draw or take it out of HiZ mode + * @param state pmic object + * @param enable uint8_t if > 0 enable the high impedance mode, if 0 disable + * the high impedance mode + */ +void bq_enable_HiZ_input( bq_state_t* state,uint8_t enable ) +{ + if (enable > 1) + { + enable = 1; + } + write_bits( state, enable_HiZ, enable ); +} + +/** + * @brief Control the boost output + * @param state pmic object + * @param enable uint8_t if > 0 enable boost converter + * if 0 disable the boost converter + */ +void bq_enable_boost( bq_state_t* state, uint8_t enable ) +{ + if (enable > 1) + { + enable = 1; + } + write_bits( state, otg_boost, enable ); +} + +/** + * @brief Disconnect the battery from the IC + * @param state pmic object + */ +void bq_disconnect_battery( bq_state_t* state ) +{ + write_bits( state, batfet_disable, 1 ); +} + +/** + * @brief Set the Input current limit + * @param state pmic object + * @param limit float Limit in mA, range 100-3250mA resolution 50mA + */ +void bq_set_input_current_limit( bq_state_t* state, float limit ) +{ + write_scaled( state, input_Ilim, limit ); + if (limit > 1500.0F) + { + write_bits( state, Ilim_pin, 1 ); + } + else + { + write_bits( state, Ilim_pin, 0 ); + } +} + +/** + * @brief update the status + * @param state pmic object +*/ +void bq_update_state( bq_state_t* state ) +{ + uint8_t address = 0x0B; + uint8_t read_buffer[8]; + mp_machine_i2c_buf_t buffer[2] = { { .len = 1, .buf = &address }, + { .len = 8, .buf = read_buffer } }; + tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 2, buffer, READ ); + state->status = read_buffer[0]; + state->fault = read_buffer[1]; + state->vbat = ((float)( read_buffer[3] & 0x7F) * 0.02F ) + 2.304F; + state->vsys = ((float)( read_buffer[4] & 0x7F) * 0.02F ) + 2.304F; + state->vbus = ((float)( read_buffer[6] & 0x7F) * 0.10F ) + 2.600F; + state->ichrg = ((float)( read_buffer[7] & 0x7F) * 0.05F ); +} + +/** + * @brief read modify write a single register entry + * @param state pmic object + * @param reg bq_register_t details of the register to read + * @param value uint to convert to a register entry + */ +inline void write_bits( bq_state_t* state, bq_register_t reg, uint8_t value ) +{ + uint8_t regVal = 0; + mp_machine_i2c_buf_t buffer[2] = { { .len = 1, .buf = ®.regaddr }, + { .len = 1, .buf = ®Val } }; + tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 2, buffer, READ ); + regVal = regVal & (~reg.mask); + regVal = regVal | ( value << reg.position ); + uint8_t write_buffer[2] = { reg.regaddr, regVal }; + buffer[0].len = 2; + buffer[0].buf = write_buffer; + tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 1, buffer, WRITE ); + } + +/** + * @brief read modify write a value from a register after applying scaling and offset. + * @param state pmic object + * @param scaledregister scaled_register_t details of the register to read + * @param value float to convert to a register entry + */ +inline void write_scaled( bq_state_t* state, scaled_register_t scaledregister, float value ) +{ + uint8_t regVal = 0; + mp_machine_i2c_buf_t buffer[2] = { { .len = 1, .buf = &scaledregister.regaddr }, + { .len = 1, .buf = ®Val } }; + tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 2, buffer, READ ); + regVal = regVal & (~scaledregister.mask); + regVal |= ( (int)(( value - scaledregister.offset ) / scaledregister.scaling ) << scaledregister.position ) & scaledregister.mask; + uint8_t write_buffer[2] = { scaledregister.regaddr, regVal }; + ((mp_machine_i2c_buf_t*)&buffer[0])->len = 2; + ((mp_machine_i2c_buf_t*)&buffer[0])->buf = write_buffer; + tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 1, buffer, WRITE ); +} diff --git a/drivers/tildagon_power/bq25895/bq25895.h b/drivers/tildagon_power/bq25895/bq25895.h new file mode 100644 index 0000000..7a31850 --- /dev/null +++ b/drivers/tildagon_power/bq25895/bq25895.h @@ -0,0 +1,79 @@ + +#ifndef BQ25895_H +#define BQ25895_H + +#include +#include "tildagon_i2c.h" + +typedef enum +{ + BQ_NOTCHARGING = 0x00, + BQ_PRECHARGING = 0x08, + BQ_FASTCHARGING = 0x10, + BQ_TERMINATED = 0x18 +} bq_charge_status_t; + +typedef enum +{ + BQ_FAULT_NONE = 0x00, + BQ_FAULT_INPUT = 0x04, + BQ_FAULT_THERMAL = 0x08, + BQ_FAULT_TIMER = 0x0C, +} bq_charge_fault_t; + +#define BQ_CHARGE_STAT_MASK (uint8_t)0x18U +#define BQ_FAULT_BOOST_MASK (uint8_t)0x08U +#define BQ_FAULT_CHARGE_MASK (uint8_t)0x30U +#define BQ_FAULT_BATTERY_MASK (uint8_t)0x40U + +typedef struct +{ + float vbus; + float vsys; + float vbat; + float ichrg; + uint8_t fault; + uint8_t status; + tildagon_mux_i2c_obj_t* mux_port; +} bq_state_t; + +/** + * @brief initialise the bq25895 + * @details reset then setup 500mA Iin limit, boost disabled, charging enabled, + * ADC at 1Hz, disable unused features and disable watchdog to reduce interruptions + * @param state pmic object + */ +extern void bq_init( bq_state_t* state ); +/** + * @brief Put the converter into High impedance mode on the input to prevent + * current draw or take it out of HiZ mode + * @param state pmic object + * @param enable uint8_t if > 0 enable the high impedance mode, if 0 disable + * the high impedance mode + */ +extern void bq_enable_HiZ_input( bq_state_t* state, uint8_t enable ); +/** + * @brief Control the boost output + * @param state pmic object + * @param enable uint8_t if > 0 enable boost converter + * if 0 disable the boost converter + */ +extern void bq_enable_boost( bq_state_t* state, uint8_t enable ); +/** + * @brief Disconnect the battery from the IC + * @param state pmic object + */ +extern void bq_disconnect_battery( bq_state_t* state ); +/** + * @brief Set the Input current limit + * @param state pmic object + * @param limit float Limit in mA, range 100-3250mA resolution 50mA + */ +extern void bq_set_input_current_limit( bq_state_t* state, float limit ); +/** + * @brief update the status + * @param state pmic object +*/ +extern void bq_update_state( bq_state_t* state ); + +#endif /* BQ25895_H */ diff --git a/drivers/tildagon_power/fusb302b/fusb302b.c b/drivers/tildagon_power/fusb302b/fusb302b.c new file mode 100644 index 0000000..94a94ff --- /dev/null +++ b/drivers/tildagon_power/fusb302b/fusb302b.c @@ -0,0 +1,392 @@ +/* +FUSB302 Programmable USB Type-C Controller w/PD driver in MicroPython. + +The driver supports basic read write access with some helpers + +*/ +#include "fusb302b.h" +#include "fusb302b_pd.h" + +typedef struct +{ + uint8_t regaddr; + uint8_t mask; + uint8_t position; +} fusb_register_t; + + +#define ADDRESS 0x22 +#define READ ( MP_MACHINE_I2C_FLAG_WRITE1 | MP_MACHINE_I2C_FLAG_READ | MP_MACHINE_I2C_FLAG_STOP ) +#define WRITE MP_MACHINE_I2C_FLAG_STOP +static const fusb_register_t select_cc = { 0x02U, 0x0CU, 2U }; +static const fusb_register_t enable_bmc = { 0x03U, 0x03U, 0U }; +static const fusb_register_t auto_crc = { 0x03U, 0x04U, 2U }; +static const fusb_register_t tx_flush = { 0x06U, 0x40U, 6U }; +static const fusb_register_t rx_flush = { 0x07U, 0x04U, 2U }; +static const fusb_register_t mask_bc_level = { 0x0AU, 0x01U, 0U }; +static const fusb_register_t mask_comparator_change = { 0x0AU, 0x20U, 5U }; +static const fusb_register_t enable_oscillator = { 0x0BU, 0x01U, 0U }; +static const fusb_register_t register_reset = { 0x0CU, 0x01U, 0U }; +static const fusb_register_t pd_reset = { 0x0CU, 0x02U, 1U }; +static const fusb_register_t mask_hardreset_int = { 0x0EU, 0x01U, 0U }; +static const fusb_register_t mask_softreset_int = { 0x0EU, 0x02U, 1U }; +static const fusb_register_t mask_tx_sent_int = { 0x0EU, 0x04U, 2U }; +static const fusb_register_t mask_retry_fail_int = { 0x0EU, 0x10U, 4U }; +static const fusb_register_t mask_toggle_done_int = { 0x0EU, 0x40U, 6U }; +static const fusb_register_t mask_good_crc_sent_int = { 0x0FU, 0x01U, 0U }; +static const fusb_register_t rx_fifo_empty = { 0x41U, 0x20U, 5U }; + +static void power_up( fusb_state_t* state ); +static uint8_t read_bits( fusb_state_t* state, fusb_register_t reg ); +static void write_bits( fusb_state_t* state, fusb_register_t reg, uint8_t value ); + +/** + * @brief Initialise the fusb302 to a device + * @details reset the device then set comparator threshold to 2.226V, enable Vbus measurment, + * flush buffers, enable interrupts, 3 retries and Vbus, BC level and good crc interrupts, enable toggle + * @param state the port object + */ +void fusb_setup_device( fusb_state_t* state ) +{ + write_bits( state, register_reset, 1 ); + power_up(state); + uint8_t write_buffer[13] = { 0x04, 0x3E, 0x60, 0x44, 0x04, 0xA5, 0x07, 0x7F, 0x07, 0x00, 0x0F, 0x3F, 0x00 }; + mp_machine_i2c_buf_t buffer = { .len = 13, .buf = write_buffer }; + tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 1, &buffer, WRITE ); + state->host = 0U; + state->input_current_limit = 500U; + state->cc_select = 0U; +} + +/** + * @brief Initialise the fusb302 to a host + * @details reset then setup pull ups, data roles, cc measurement level, enable toggle + * and toggle interrupt, 1.5A current limit, 3 auto retries and flush buffers + * @param state the port object + */ +void fusb_setup_host( fusb_state_t* state ) +{ + write_bits( state, register_reset, 1 ); + power_up(state); + uint8_t write_buffer[15] = { 0x02, 0xC0, 0xB0, 0x25, 0x60, 0x48, 0x04, 0xA7, 0x07, 0xFF, 0x07, 0x00, 0x0F, 0xB0, 0x01 }; + mp_machine_i2c_buf_t buffer = { .len = 15, .buf = write_buffer }; + tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 1, &buffer, WRITE ); + state->cc_select = 0U; + state->host = 1U; +} + +/** + * @brief set the measurement to a cc line + * @param state the port object + * @param cc_select the pin to measure on + */ +void fusb_set_cc( fusb_state_t* state, uint8_t cc_select ) +{ + write_bits( state, select_cc, cc_select ); +} + +/** + * @brief disble toggle + * @param state the port object + */ +void fusb_stop_toggle( fusb_state_t* state ) +{ + uint8_t write_buffer[2] = { 0x08, 0x00 }; + mp_machine_i2c_buf_t buffer = { .len = 2, .buf = write_buffer }; + tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 1, &buffer, WRITE ); +} + +/** + * @brief status read from the device + * @param state the port object + */ +void fusb_get_status( fusb_state_t* state ) +{ + uint8_t read_buffer[2]; + uint8_t address = 0x40; + mp_machine_i2c_buf_t buffer[2] = { { .len = 1, .buf = &address }, + { .len = 2, .buf = read_buffer } }; + tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 2, buffer, READ ); + state->status = read_buffer[0] | ( (uint16_t)read_buffer[1] << 8 ); +} + +/** + * @brief statusa read from the device + * @param state the port object + */ +void fusb_get_statusa( fusb_state_t* state ) +{ + uint8_t read_buffer[2]; + uint8_t address = 0x3C; + mp_machine_i2c_buf_t buffer[2] = { { .len = 1, .buf = &address }, + { .len = 2, .buf = read_buffer } }; + tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 2, buffer, READ ); + state->statusa = read_buffer[0] | ( (uint16_t)read_buffer[1] << 8 ); +} + +/** + * @brief read interrupt a and b registers + * @param state the port object + */ +uint16_t fusb_get_interruptab( fusb_state_t* state ) +{ + uint8_t read_buffer[2]; + uint8_t address = 0x3E; + mp_machine_i2c_buf_t buffer[2] = { { .len = 1, .buf = &address }, + { .len = 2, .buf = read_buffer } }; + tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 2, buffer, READ ); + return read_buffer[0] | ( (uint16_t)read_buffer[1] << 8 ); +} + +/** + * @brief read interrupt register + * @param state the port object + */ +uint8_t fusb_get_interrupt( fusb_state_t* state ) +{ + uint8_t read_buffer; + uint8_t address = 0x42; + mp_machine_i2c_buf_t buffer[2] = { { .len = 1, .buf = &address }, + { .len = 1, .buf = &read_buffer } }; + tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 2, buffer, READ ); + return read_buffer; +} + +/** + * @brief setup the PD to send the good CRC packet automatically + * @param state the port object + */ +void fusb_auto_good_crc ( fusb_state_t* state ) +{ + write_bits( state, auto_crc, 1 ); +} + +/** + * @brief flush both the rx and tx buffers + * @param state the port object + */ +void fusb_flush_buffers( fusb_state_t* state ) +{ + write_bits( state, tx_flush, 1 ); + write_bits( state, rx_flush, 1 ); +} + +/** + * @brief mask toggle interrupt + * @param state the port object + * @param value of mask + */ +void fusb_mask_interrupt_toggle( fusb_state_t* state, uint8_t value ) +{ + write_bits( state, mask_toggle_done_int, value ); +} + +/** + * @brief mask comparator interrupt + * @param state the port object + * @param value of mask + */ +void fusb_mask_interrupt_comp( fusb_state_t* state, uint8_t value ) +{ + write_bits( state, mask_comparator_change, value ); +} + +/** + * @brief mask BC level interrupt + * @param state the port object + * @param value of mask + */ +void fusb_mask_interrupt_bclevel( fusb_state_t* state, uint8_t value ) +{ + write_bits( state, mask_bc_level, value ); +} + +/** + * @brief mask PD good CRC interrupt + * @param state the port object + * @param value of mask + */ +void fusb_mask_interrupt_gdcrc( fusb_state_t* state, uint8_t value ) +{ + write_bits( state, mask_good_crc_sent_int, value ); +} + +/** + * @brief mask PD soft reset interrupt + * @param state the port object + * @param value of mask + */ +void fusb_mask_interrupt_softreset( fusb_state_t* state, uint8_t value ) +{ + write_bits( state, mask_softreset_int, value ); +} + +/** + * @brief mask PD hard reset interrupt + * @param state the port object + * @param value of mask + */ +void fusb_mask_interrupt_hardreset( fusb_state_t* state, uint8_t value ) +{ + write_bits( state, mask_hardreset_int, value ); +} + +/** + * @brief mask PD retry failure interrupt + * @param state the port object + * @param value of mask + */ +void fusb_mask_interrupt_retryfail( fusb_state_t* state, uint8_t value ) +{ + write_bits( state, mask_retry_fail_int, value ); +} + +/** + * @brief mask PD tx sent got a good crc interrupt + * @param state the port object + * @param value of mask + */ +void fusb_mask_interrupt_txsent( fusb_state_t* state, uint8_t value ) +{ + write_bits( state, mask_tx_sent_int, value ); +} + +/** + * @brief enable or disable comms based on cc_select + * @param state the port object + */ +void fusb_setup_pd( fusb_state_t* state ) +{ + if ( state->cc_select ) + { + uint8_t write_buffer[2] = { 0x08, 0x04 }; + mp_machine_i2c_buf_t buffer = { .len = 2, .buf = write_buffer }; + tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 1, &buffer, WRITE ); + write_buffer[0] = mask_good_crc_sent_int.regaddr; + write_buffer[1] = 0x00; + tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 1, &buffer, WRITE ); + + write_bits( state, rx_flush, 1 ); + uint8_t data = 0x20; + if ( state->cc_select == 1 ) + { + data |= 0x01; + } + else + { + data |= 0x02; + } + if ( state->host == 0 ) + { + data |= 0x84; + } + else + { + data |= 0x04; + } + write_buffer[0] = enable_bmc.regaddr; + write_buffer[1] = data; + tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 1, &buffer, WRITE ); + write_bits( state, tx_flush, 1 ); + write_bits( state, rx_flush, 1 ); + write_buffer[0] = 0x0B; + write_buffer[1] = 0x0F; + tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 1, &buffer, WRITE ); + write_buffer[0] = pd_reset.regaddr; + write_buffer[1] = 0x02; + tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 1, &buffer, WRITE ); + } + else + { + write_bits( state, auto_crc, 0 ); + write_bits( state, mask_good_crc_sent_int, 1 ); + write_bits( state, enable_bmc, 0 ); + } +} + +/** + * @brief are bytes available + * @param state the port object + */ +uint8_t fusb_rx_empty( fusb_state_t* state ) +{ + return read_bits( state, rx_fifo_empty ); +} + +/** + * @brief get the contents of the rx fifo + * @param state the port object + * @param rx_buffer buffer to return the data of size length + * @param length amount of data + */ +void fusb_get_fifo(fusb_state_t* state, uint8_t* rx_buffer, uint8_t length ) +{ + uint8_t address = 0x43; + mp_machine_i2c_buf_t buffer[2] = { { .len = 1, .buf = &address }, + { .len = length, .buf = rx_buffer } }; + tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 2, buffer, READ ); +} + +/** + * @brief send a message to the tx fifo of up to PD_MAX_TX_MSG_SIZE bytes + * @param state the port object + * @param message buffer to send, must be prefixed with TX_FIFO_ADDRESS. + * @param length amount of data + */ +void fusb_send( fusb_state_t* state, uint8_t* message, uint8_t length ) +{ + mp_machine_i2c_buf_t buffer = { .len = length, .buf = message }; + tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 1, &buffer, WRITE ); +} + +/** + * @brief Turn on parts of the fusb hardware + * @param state the port object + */ +void power_up( fusb_state_t* state ) +{ + /* + bit 0: Bandgap and wake circuit. + bit 1: Receiver powered and current references for Measure block + bit 2: Measure block powered. + bit 3: Enable internal oscillator. + */ + uint8_t write_buffer[2] = { enable_oscillator.regaddr, 0x07 }; + mp_machine_i2c_buf_t buffer = { .len = 2, .buf = write_buffer }; + tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 1, &buffer, WRITE ); +} + +/** + * @brief Returns a value from the selected bits of a register + * @param state the port object + * @param reg fusb_register_t details of the register to read + * @return the value of the register + */ +uint8_t read_bits( fusb_state_t* state, fusb_register_t reg ) +{ + uint8_t regVal = 0; + mp_machine_i2c_buf_t buffer[2] = { { .len = 1, .buf = ®.regaddr }, + { .len = 1, .buf = ®Val } }; + tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 2, buffer, READ ); + return ( ( regVal & reg.mask) >> reg.position ) ; +} + +/** + * @brief read modify write a single register entry + * @param state the port object + * @param reg fusb_register_t details of the register to read + * @param value uint to convert to a register entry + */ +void write_bits( fusb_state_t* state, fusb_register_t reg, uint8_t value ) +{ + uint8_t regVal = 0; + mp_machine_i2c_buf_t buffer[2] = { { .len = 1, .buf = ®.regaddr }, + { .len = 1, .buf = ®Val } }; + tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 2, buffer, READ ); + regVal = regVal & (~reg.mask); + regVal = regVal | ( value << reg.position ); + uint8_t write_buffer[2] = { reg.regaddr, regVal }; + buffer[0].len = 2; + buffer[0].buf = write_buffer; + tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 1, buffer, WRITE ); +} diff --git a/drivers/tildagon_power/fusb302b/fusb302b.h b/drivers/tildagon_power/fusb302b/fusb302b.h new file mode 100644 index 0000000..995cc17 --- /dev/null +++ b/drivers/tildagon_power/fusb302b/fusb302b.h @@ -0,0 +1,158 @@ + +#ifndef FUSB302B_H +#define FUSB302B_H + +#include +#include "tildagon_i2c.h" + +#define FUSB_TOGGLE_I_MASK (uint16_t)0x0040U +#define FUSB_GD_CRC_I_MASK (uint16_t)0x0100U +#define FUSB_BC_LVL_I_MASK (uint8_t)0x01U +#define FUSB_CMPCHG_I_MASK (uint8_t)0x20U +#define FUSB_VBUSOK_I_MASK (uint8_t)0x80U + +#define FUSB_STATUSA_TOGGLE_MASK (uint16_t)0x3800U +#define FUSB_STATUSA_TOGGLE_SHIFT (int8_t)10 +#define FUSB_STATUS_COMP_MASK (uint16_t)0x0020U +#define FUSB_STATUS_BCLVL_MASK (uint16_t)0x0003U +#define FUSB_STATUS_VBUSOK_MASK (uint16_t)0x0080U + +typedef struct +{ + tildagon_mux_i2c_obj_t* mux_port; + uint8_t cc_select; + uint16_t input_current_limit; + uint16_t status; + uint16_t statusa; + uint8_t host; +} fusb_state_t; + +/** + * @brief Initialise the fusb302 to a device + * @details reset the device then set comparator threshold to 2.226V, enable Vbus measurment, + * flush buffers, enable interrupts, 3 retries and Vbus, BC level and good crc interrupts, enable toggle + * @param state the port object + */ +extern void fusb_setup_device( fusb_state_t* state ); +/** + * @brief Initialise the fusb302 to a host + * @details reset then setup pull ups, data roles, cc measurement level, enable toggle + * and toggle interrupt, 1.5A current limit, 3 auto retries and flush buffers + * @param state the port object + */ +extern void fusb_setup_host( fusb_state_t* state ); +/** + * @brief set the measurement to a cc line + * @param state the port object + * @param cc_select the pin to measure on + */ +extern void fusb_set_cc( fusb_state_t* state, uint8_t cc_select ); +/** + * @brief disble toggle + * @param state the port object + */ +extern void fusb_stop_toggle( fusb_state_t* state ); +/** + * @brief status read from the device + * @param state the port object + */ +extern void fusb_get_status( fusb_state_t* state ); +/** + * @brief statusa read from the device + * @param state the port object + */ +extern void fusb_get_statusa( fusb_state_t* state ); +/** + * @brief read interrupt a and b registers + * @param state the port object + */ +extern uint16_t fusb_get_interruptab( fusb_state_t* state ); +/** + * @brief read interrupt register + * @param state the port object + */ +extern uint8_t fusb_get_interrupt( fusb_state_t* state ); +/** + * @brief setup the PD to send the good CRC packet automatically + * @param state the port object + */ +extern void fusb_auto_good_crc ( fusb_state_t* state ); +/** + * @brief flush both the rx and tx buffers + * @param state the port object + */ +extern void fusb_flush_buffers( fusb_state_t* state ); +/** + * @brief mask toggle interrupt + * @param state the port object + * @param value of mask + */ +extern void fusb_mask_interrupt_toggle( fusb_state_t* state, uint8_t value ); +/** + * @brief mask comparator interrupt + * @param state the port object + * @param value of mask + */ +extern void fusb_mask_interrupt_comp( fusb_state_t* state, uint8_t value ); +/** + * @brief mask BC level interrupt + * @param state the port object + * @param value of mask + */ +extern void fusb_mask_interrupt_bclevel( fusb_state_t* state, uint8_t value ); +/** + * @brief mask PD good CRC interrupt + * @param state the port object + * @param value of mask + */ +extern void fusb_mask_interrupt_gdcrc( fusb_state_t* state, uint8_t value ); +/** + * @brief mask PD soft reset interrupt + * @param state the port object + * @param value of mask + */ +extern void fusb_mask_interrupt_softreset( fusb_state_t* state, uint8_t value ); +/** + * @brief mask PD hard reset interrupt + * @param state the port object + * @param value of mask + */ +extern void fusb_mask_interrupt_hardreset( fusb_state_t* state, uint8_t value ); +/** + * @brief mask PD retry failure interrupt + * @param state the port object + * @param value of mask + */ +extern void fusb_mask_interrupt_retryfail( fusb_state_t* state, uint8_t value ); +/** + * @brief mask PD tx sent got a good crc interrupt + * @param state the port object + * @param value of mask + */ +extern void fusb_mask_interrupt_txsent( fusb_state_t* state, uint8_t value ); +/** + * @brief enable or disable comms based on cc_select + * @param state the port object + */ +extern void fusb_setup_pd( fusb_state_t* state ); +/** + * @brief are bytes available + * @param state the port object + */ +extern uint8_t fusb_rx_empty( fusb_state_t* state ); +/** + * @brief get the contents of the rx fifo + * @param state the port object + * @param rx_buffer buffer to return the data of size length + * @param length amount of data + */ +extern void fusb_get_fifo(fusb_state_t* state, uint8_t* rx_buffer, uint8_t length ); +/** + * @brief send a message to the tx fifo of up to PD_MAX_TX_MSG_SIZE bytes + * @param state the port object + * @param message buffer to send + * @param length amount of data + */ +extern void fusb_send( fusb_state_t* state, uint8_t* message, uint8_t length ); + +#endif /* FUSB302B_H */ diff --git a/drivers/tildagon_power/fusb302b/fusb302b_pd.c b/drivers/tildagon_power/fusb302b/fusb302b_pd.c new file mode 100644 index 0000000..d7e1581 --- /dev/null +++ b/drivers/tildagon_power/fusb302b/fusb302b_pd.c @@ -0,0 +1,211 @@ + +#include "fusb302b_pd.h" + +#define TX_FIFO_ADDRESS 0x43U +/* FIFO tokens */ +#define TX_ON 0xA1U +#define TX_SOP1 0x12U +#define TX_SOP2 0x13U +#define TX_SOP3 0x1BU +#define TX_RESET1 0x15U +#define TX_RESET2 0x16U +#define TX_PACKSYM 0x80U +#define TX_JAM_CRC 0xFFU +#define TX_EOP 0x14U +#define TX_OFF 0xFEU +#define RX_SOP 0xE0U +#define RX_SOP1 0xC0U +#define RX_SOP2 0xA0U +#define RX_SOP1DB 0x80U +#define RX_SOP2DB 0x60U + +/** + * @brief parse and decode the receive buffer + * @param state the comms state onject + * @param fusb the object for the fusb to use + */ +void fusbpd_decode( pd_state_t* state, fusb_state_t* fusb ) +{ + uint8_t rx_buffer[32]; + /* parse rx buffer */ + while ( !fusb_rx_empty( fusb ) ) + { + fusb_get_fifo( fusb, rx_buffer, 1 ); + if ( rx_buffer[0] == RX_SOP ) + { + fusb_get_fifo( fusb, state->last_rx_header.raw, 2 ); + + if ( state->last_rx_header.sop.number_objects == 0 ) + { + /* handle control messages */ + state->last_rx_control_msg_type = state->last_rx_header.sop.message_type; + } + else + { + uint8_t buffer_index = 0U; + if ( state->last_rx_header.sop.message_type == PD_DATA_SOURCE_CAPABILITIES ) + { + for ( uint8_t i = 8U; i; i-- ) + { + state->pdos[i].raw32 = 0U; + } + fusb_get_fifo( fusb, rx_buffer, ( state->last_rx_header.sop.number_objects * 4) + 4 ); + for ( uint8_t i = 0U; i < state->last_rx_header.sop.number_objects; i++ ) + { + state->pdos[i].raw32 = *((uint32_t*)&rx_buffer[ buffer_index ]); + buffer_index += 4U; + } + state->number_of_pdos = state->last_rx_header.sop.number_objects; + } + else if ( state->last_rx_header.sop.message_type == PD_DATA_VENDOR_DEFINED ) + { + + fusb_get_fifo( fusb, rx_buffer, ( state->last_rx_header.sop.number_objects * 4) + 4 ); + buffer_index += 2U; + uint16_t vendor_id = rx_buffer[ buffer_index ] + ( rx_buffer[ buffer_index + 1 ] << 8 ); + buffer_index += 2U; + if ( vendor_id == PD_VENDOR_ID ) + { + *((uint32_t*)&state->rx_badge_id[0]) = *((uint32_t*)&rx_buffer[buffer_index]); + *((uint32_t*)&state->rx_badge_id[4]) = *((uint32_t*)&rx_buffer[buffer_index + 4]); + buffer_index += 8U; + } + else + { + //todo save this to pass to micropython? + } + } + else + { + } + state->last_rx_data_msg_type = state->last_rx_header.sop.message_type; + } + } + } +} + +/** + * @brief select the highest voltage pdo up to 14V + * @param state the comms state onject + * @return the index of the pdo object + */ +uint8_t fusbpd_select_pdo( pd_state_t* state ) +{ + uint16_t highest_voltage = 5000U; + uint8_t index = 0U; + for ( uint8_t i = 0U; i < state->last_rx_header.sop.number_objects; i++ ) + { + const uint16_t voltage = state->pdos[i].fixed.voltage * 50; + if ( + ( state->pdos[i].fixed.pdo_type == PD_FIXED_SUPPLY ) + && ( voltage > highest_voltage ) + && ( voltage < 14000 ) + ) + { + highest_voltage = voltage; + index = i; + } + } + return index; +} + +/** + * @brief creat a request power message + * @param state the comms state onject + * @param num the index of the pdo list sent from the source + * @param current the current required to run the device + * @param max_current the maximum current required by the device + */ +void fusbpd_request_power( pd_state_t* state, uint8_t num, uint16_t current, uint16_t max_current ) +{ + state->tx_buffer[0] = TX_FIFO_ADDRESS; + state->tx_buffer[1] = TX_SOP1; + state->tx_buffer[2] = TX_SOP1; + state->tx_buffer[3] = TX_SOP1; + state->tx_buffer[4] = TX_SOP2; + state->tx_buffer[5] = TX_PACKSYM | 0x06; + state->tx_buffer[6] = ( 0x01 << 6 ) /* PD 2.0 */ | 0x02 /* request */; + state->tx_buffer[7] = ( 0x01 << 4 ) /* object count */ | ( ( state->msg_id & 0x07 ) << 1 ); + state->msg_id++; + + /* packing max current into fields */ + uint16_t max_current_b = max_current / 10; + state->tx_buffer[8] = max_current_b & 0xFF; + state->tx_buffer[9] = max_current_b >> 8; + + /* packing current into fields */ + uint16_t current_b = current / 10; + uint8_t current_l = current_b & 0x3f; + state->tx_buffer[9] |= current_l << 2; + state->tx_buffer[10] = current_b >> 6; + + state->tx_buffer[11] = ( (num+1) << 4 )/* object position */ | 0x01 /* no suspend */; + state->tx_buffer[12] = TX_JAM_CRC; + state->tx_buffer[13] = TX_EOP; + state->tx_buffer[14] = TX_OFF; + state->tx_buffer[15] = TX_ON; + state->message_length = 16; +} + +/** + * @brief create a request source capabilities message + * @param state the comms state onject + */ +void fusbpd_request_capability( pd_state_t* state ) +{ + state->tx_buffer[0] = TX_FIFO_ADDRESS; + state->tx_buffer[1] = TX_SOP1; + state->tx_buffer[2] = TX_SOP1; + state->tx_buffer[3] = TX_SOP1; + state->tx_buffer[4] = TX_SOP2; + state->tx_buffer[5] = TX_PACKSYM | 0x02; + state->tx_buffer[6] = 0x47U; + state->tx_buffer[7] = 0x00U | ( ( state->msg_id & 0x07 ) << 1 ) ; + state->msg_id++; + state->tx_buffer[8] = TX_JAM_CRC; + state->tx_buffer[9] = TX_EOP; + state->tx_buffer[10] = TX_OFF; + state->tx_buffer[11] = TX_ON; + state->message_length = 12; +} + +/** + * @brief create a vendor specific pdo message with the esp32 unique id + * @param state the comms state onject + */ +void fusbpd_vendor_specific( pd_state_t* state ) +{ + state->tx_buffer[0] = TX_FIFO_ADDRESS; + state->tx_buffer[1] = TX_SOP1; + state->tx_buffer[2] = TX_SOP1; + state->tx_buffer[3] = TX_SOP1; + state->tx_buffer[4] = TX_SOP2; + state->tx_buffer[5] = TX_PACKSYM | 0x0E; + pd_header_union_t header = { 0U }; + header.sop.number_objects = 0x02U; + header.sop.message_id = state->msg_id; + header.sop.message_type = 0x0FU; + header.sop.revision = 0x01U; + header.sop.data_role = state->data_role; + header.sop.power_role = state->power_role; + state->tx_buffer[6] = header.raw[0]; + state->tx_buffer[7] = header.raw[1]; + state->msg_id++; + state->tx_buffer[8] = 0x00U; + state->tx_buffer[9] = 0x00U; + state->tx_buffer[10] = PD_VENDOR_ID >> 8; + state->tx_buffer[11] = PD_VENDOR_ID & 0XFF; + state->tx_buffer[12] = state->badge_id[0]; + state->tx_buffer[13] = state->badge_id[1]; + state->tx_buffer[14] = state->badge_id[2]; + state->tx_buffer[15] = state->badge_id[3]; + state->tx_buffer[16] = state->badge_id[4]; + state->tx_buffer[17] = state->badge_id[5]; + state->tx_buffer[18] = state->badge_id[6]; + state->tx_buffer[19] = state->badge_id[7]; + state->tx_buffer[20] = TX_JAM_CRC; + state->tx_buffer[21] = TX_EOP; + state->tx_buffer[22] = TX_OFF; + state->tx_buffer[23] = TX_ON; + state->message_length = 24; +} diff --git a/drivers/tildagon_power/fusb302b/fusb302b_pd.h b/drivers/tildagon_power/fusb302b/fusb302b_pd.h new file mode 100644 index 0000000..c53c159 --- /dev/null +++ b/drivers/tildagon_power/fusb302b/fusb302b_pd.h @@ -0,0 +1,192 @@ + +#ifndef FUSB302B_PD_H +#define FUSB302B_PD_H + +#include +#include "fusb302b.h" + +/* + PD data structures +*/ +typedef enum +{ + PD_CONTROL_DO_NOT_USE = 0x00U, + PD_CONTROL_GOODCRC = 0x01U, + PD_CONTROL_GOTOMIN = 0x02U, + PD_CONTROL_ACCEPT = 0x03U, + PD_CONTROL_REJECT = 0x04U, + PD_CONTROL_PING = 0x05U, + PD_CONTROL_PS_RDY = 0x06U, + PD_CONTROL_GET_SOURCE_CAP = 0x07U, + PD_CONTROL_GET_SINK_CAP = 0x08U, + PD_CONTROL_DATA_ROLE_SWAP = 0x09U, + PD_CONTROL_POWER_ROLE_SWAP = 0x0AU, + PD_CONTROL_VCONN_SWAP = 0x0BU, + PD_CONTROL_WAIT = 0x0CU, + PD_CONTROL_SOFT_RESET = 0x0DU, + PD_CONTROL_DATA_RESET = 0x0EU, + PD_CONTROL_DATA_RESET_COMPLETE = 0x0FU, + PD_CONTROL_NOT_SUPPORTED = 0x10U, + PD_CONTROL_GET_SRC_CAP_EXTENDED = 0x11U, + PD_CONTROL_GET_STATUS = 0x12U, + PD_CONTROL_FR_SWAP = 0x13U, + PD_CONTROL_GET_PPS_STATUS = 0x14U, + PD_CONTROL_COUNTRY_CODE = 0x15U, +} pd_control_message_type_t; + +typedef enum +{ + PD_DATA_DO_NOT_USE = 0x00U, + PD_DATA_SOURCE_CAPABILITIES = 0x01U, + PD_DATA_REQUEST = 0x02U, + PD_DATA_BIST = 0x03U, + PD_DATA_SINK_CAPABILITIES = 0x04U, + PD_DATA_VENDOR_DEFINED = 0x0FU, +} pd_data_message_types_t; + +/* SOP header */ +typedef struct +{ + uint16_t message_type : 5; + uint16_t data_role : 1; + uint16_t revision : 2; + uint16_t power_role : 1; + uint16_t message_id : 3; + uint16_t number_objects : 3; + uint16_t extended : 1; +} pd_sop_header_t; + +/* SOP' and SOP" header */ +typedef struct +{ + uint16_t message_type : 5; + uint16_t reserved : 1; + uint16_t revision : 2; + uint16_t cable_plug : 1; + uint16_t message_id : 3; + uint16_t number_objects : 3; + uint16_t extended : 1; +} pd_sop_prime_header_t; + +typedef union +{ + uint8_t raw[2]; + pd_sop_header_t sop; + pd_sop_prime_header_t sop_prime; +} pd_header_union_t; + +/* power data objects */ + +typedef struct +{ + uint32_t max_current : 10; /* Maximum Current in 10mA units */ + uint32_t voltage : 10; /* Voltage in 50mV units */ + uint32_t peak_current : 2; /* Peak Current capability */ + uint32_t reserved : 3; /* Reserved – Shall be set to zero. */ + uint32_t dual_role : 1; /* Dual-Role Data */ + uint32_t usb_cooms : 1; /* USB Communications Capable */ + uint32_t Unconstrained : 1; /* Unconstrained Power */ + uint32_t suspend : 1; /* USB Suspend Supported */ + uint32_t drp : 1; /* Dual-Role Power */ + uint32_t pdo_type : 2; /* Fixed supply == 0 */ +} pd_fixed_pdo_t; + +typedef struct +{ + uint32_t max_power : 10; /* Maximum Allowable Power in 250mW units */ + uint32_t min_volt : 10; /* Maximum Voltage in 50mV units */ + uint32_t max_volt : 10; /* Minimum Voltage in 50mV units */ + uint32_t pdo_type : 2; /* Battery == 1 */ +} pd_battery_pdo_t; + +typedef struct +{ + uint32_t max_current : 10; /* Maximum Current in 10mA units */ + uint32_t min_voltage : 10; /* Minimum Voltage in 50mV units */ + uint32_t max_voltage : 10; /* Maximum Voltage in 50mV units */ + uint32_t pdo_type : 2; /* Variable Supply (non-Battery) == 2 */ +} pd_variable_pdo_t; + +typedef union +{ + uint8_t raw[4]; + uint32_t raw32; + pd_fixed_pdo_t fixed; + pd_battery_pdo_t battery; + pd_variable_pdo_t variable; +} pd_source_pdo_union_t; + +typedef struct +{ + uint32_t min_current : 10; + uint32_t current : 10; + uint32_t reserved : 2; + uint32_t epr_cap : 1; + uint32_t uem_suspend : 1; + uint32_t no_suspend : 1; + uint32_t usb_comms : 1; + uint32_t capability_mismatch : 1; + uint32_t give_back : 1; + uint32_t position : 1; +} pd_request_pdo_t; + +typedef union +{ + uint32_t raw; + pd_request_pdo_t bits; +} pd_request_pdo_union_t; + +#define PD_FIXED_SUPPLY 0U +#define PD_BATTERY 1U +#define PD_VARIABLE_SUPPLY 2U +#define PD_MAX_TX_MSG_SIZE 50 +#define PD_VENDOR_ID 0xCDCD + +typedef struct +{ + uint8_t tx_buffer[PD_MAX_TX_MSG_SIZE]; + uint8_t message_length; + uint8_t msg_id; + uint8_t power_role; + uint8_t data_role; + pd_header_union_t last_rx_header; + pd_source_pdo_union_t pdos[8]; + uint8_t number_of_pdos; + pd_control_message_type_t last_rx_control_msg_type; + pd_data_message_types_t last_rx_data_msg_type; + uint8_t badge_id[8]; + uint8_t rx_badge_id[8]; +} pd_state_t; + +/** + * @brief parse and decode the receive buffer + * @param state the comms state onject + * @param fusb the object for the fusb to use + */ +extern void fusbpd_decode( pd_state_t* state, fusb_state_t* fusb ); +/** + * @brief select the highest voltage pdo up to 14V + * @param state the comms state onject + * @return the index of the pdo object + */ +extern uint8_t fusbpd_select_pdo( pd_state_t* state ); +/** + * @brief creat a request power message + * @param state the comms state onject + * @param num the index of the pdo list sent from the source + * @param current the current required to run the device + * @param max_current the maximum current required by the device + */ +extern void fusbpd_request_power( pd_state_t* state, uint8_t num, uint16_t current, uint16_t max_current ); +/** + * @brief create a request source capabilities message + * @param state the comms state onject + */ +extern void fusbpd_request_capability( pd_state_t* state ); +/** + * @brief create a vendor specific pdo message with the esp32 unique id + * @param state the comms state onject + */ +extern void fusbpd_vendor_specific( pd_state_t* state ); + +#endif /* FUSB302B_PD_H */ diff --git a/drivers/tildagon_power/mp_power.c b/drivers/tildagon_power/mp_power.c new file mode 100644 index 0000000..fbbd31c --- /dev/null +++ b/drivers/tildagon_power/mp_power.c @@ -0,0 +1,234 @@ +#include "py/obj.h" +#include "py/runtime.h" +#include "py/builtin.h" +#include +#include "tildagon_power.h" + +static mp_obj_t power_enable5V( mp_obj_t a_obj ) +{ + bool enable = (bool)mp_obj_get_int(a_obj); + tildagon_power_enable_5v( enable ); + return MP_OBJ_NEW_SMALL_INT(1); +} + +static MP_DEFINE_CONST_FUN_OBJ_1( enable5V_obj, power_enable5V); + +static mp_obj_t power_Off( void ) +{ + tildagon_power_off(); + return MP_OBJ_NEW_SMALL_INT(1); +} + +static MP_DEFINE_CONST_FUN_OBJ_0( Off_obj, power_Off); + +static mp_obj_t power_Vbus( void ) +{ + bq_update_state( &pmic ); + return mp_obj_new_float(pmic.vbus); +} + +static MP_DEFINE_CONST_FUN_OBJ_0( Vbus_obj, power_Vbus); + +static mp_obj_t power_Vsys( void ) +{ + bq_update_state( &pmic ); + return mp_obj_new_float(pmic.vsys); +} + +static MP_DEFINE_CONST_FUN_OBJ_0( Vsys_obj, power_Vsys); + +static mp_obj_t power_Vbat( void ) +{ + bq_update_state( &pmic ); + return mp_obj_new_float(pmic.vbat); +} + +static MP_DEFINE_CONST_FUN_OBJ_0( Vbat_obj, power_Vbat); + +static mp_obj_t power_Icharge( void ) +{ + bq_update_state( &pmic ); + return mp_obj_new_float(pmic.ichrg); +} + +static MP_DEFINE_CONST_FUN_OBJ_0( Icharge_obj, power_Icharge); + +static mp_obj_t power_BatteryLevel( void ) +{ + bq_update_state( &pmic ); + return mp_obj_new_float( ( ( pmic.vbat-VBATMIN ) / ( VBATMAX- VBATMIN ) ) * 100.0F ); +} + +static MP_DEFINE_CONST_FUN_OBJ_0( BatteryLevel_obj, power_BatteryLevel); + +static mp_obj_t power_BatteryChargeState( void ) +{ + mp_obj_t charge_state = mp_obj_new_str( "Not Charging", 12 ); + bq_update_state( &pmic ); + switch ( pmic.status & BQ_CHARGE_STAT_MASK ) + { + case BQ_NOTCHARGING: + { + charge_state = mp_obj_new_str( "Not Charging", 12 ); + break; + } + case BQ_PRECHARGING: + { + charge_state = mp_obj_new_str( "Pre-Charging", 12 ); + break; + } + case BQ_FASTCHARGING: + { + charge_state = mp_obj_new_str( "Fast Charging", 13 ); + break; + } + case BQ_TERMINATED: + { + charge_state = mp_obj_new_str( "Terminated", 10 ); + break; + } + } + return charge_state; +} + +static MP_DEFINE_CONST_FUN_OBJ_0( BatteryChargeState_obj, power_BatteryChargeState); + +static mp_obj_t power_SupplyCapabilities( void ) +{ + mp_obj_t capabilities = mp_obj_new_list(0, NULL); + if ( usb_in.pd.number_of_pdos > 0 ) + { + for ( uint8_t i = 0; i < usb_in.pd.number_of_pdos; i++ ) + { + mp_obj_t tuple[3]; + switch ( usb_in.pd.pdos[i].fixed.pdo_type ) + { + case 0: /* fixed supply */ + { + tuple[0] = mp_obj_new_str( "fixed", 5 ); + tuple[1] = mp_obj_new_int( usb_in.pd.pdos[i].fixed.max_current * 10 ); + tuple[2] = mp_obj_new_int( ( usb_in.pd.pdos[i].fixed.voltage * 50 ) / 1000 ); + mp_obj_t capability = mp_obj_new_tuple(3, tuple); + mp_obj_list_append(capabilities, capability); + break; + } + case 1: /* battery */ + { + tuple[0] = mp_obj_new_str( "battery", 7 ); + tuple[1] = mp_obj_new_int( ( usb_in.pd.pdos[i].battery.min_volt * 50 ) / 1000 ); + tuple[2] = mp_obj_new_int( ( usb_in.pd.pdos[i].battery.max_volt * 50 ) / 1000 ); + mp_obj_t capability = mp_obj_new_tuple(3, tuple); + mp_obj_list_append(capabilities, capability); + break; + } + case 2: /* non battery variable supply */ + { + tuple[0] = mp_obj_new_str( "variable", 8 ); + tuple[1] = mp_obj_new_int( usb_in.pd.pdos[i].variable.max_current * 10 ); + tuple[2] = mp_obj_new_int( ( usb_in.pd.pdos[i].variable.max_voltage * 50 ) / 1000 ); + mp_obj_t capability = mp_obj_new_tuple(3, tuple); + mp_obj_list_append(capabilities, capability); + break; + } + default: + { + /* don't add anything to the list */ + } + } + } + } + else + { + /* do something for non pd supplies */ + + if ( usb_in.fusb.status & FUSB_STATUS_VBUSOK_MASK ) + { + bq_update_state( &pmic ); + mp_obj_t tuple[3]; + tuple[0] = mp_obj_new_str( "non-pd", 6 ); + tuple[1] = mp_obj_new_int( usb_in.fusb.input_current_limit ); + tuple[2] = mp_obj_new_int( (int)pmic.vbus ); + mp_obj_t capability = mp_obj_new_tuple(3, tuple); + mp_obj_list_append(capabilities, capability); + } + else + { + mp_obj_t tuple[3]; + tuple[0] = mp_obj_new_str( "disconnected", 12 ); + tuple[1] = mp_obj_new_str( "0", 1 ); + tuple[2] = mp_obj_new_str( "0", 1 ); + mp_obj_t capability = mp_obj_new_tuple(3, tuple); + mp_obj_list_append(capabilities, capability); + } + } + return capabilities; +} + +static MP_DEFINE_CONST_FUN_OBJ_0( SupplyCapabilities_obj, power_SupplyCapabilities); + +static mp_obj_t power_Fault( void ) +{ + mp_obj_t faults = mp_obj_new_dict( 3 ); + if ( pmic.fault & BQ_FAULT_BATTERY_MASK ) + { + mp_obj_dict_store( faults, mp_obj_new_str( "Battery", 7 ), mp_obj_new_str( "Battery Over Voltage", 20 ) ); + } + else + { + mp_obj_dict_store( faults, mp_obj_new_str( "Battery", 7 ), mp_obj_new_str( "Normal", 6 ) ); + } + if ( pmic.fault & BQ_FAULT_BOOST_MASK ) + { + mp_obj_dict_store( faults, mp_obj_new_str( "Boost", 5 ), mp_obj_new_str( "Overloaded or low battery", 25 ) ); + } + else + { + mp_obj_dict_store( faults, mp_obj_new_str( "Boost", 5 ), mp_obj_new_str( "Normal", 6 ) ); + } + switch ( pmic.fault & BQ_FAULT_CHARGE_MASK ) + { + case BQ_FAULT_INPUT: + { + mp_obj_dict_store( faults, mp_obj_new_str( "Charge", 6 ), mp_obj_new_str( "Input Fault", 11 ) ); + break; + } + case BQ_FAULT_TIMER: + { + mp_obj_dict_store( faults, mp_obj_new_str( "Charge", 6 ), mp_obj_new_str( "Safety timer expired", 20 ) ); + break; + } + case BQ_FAULT_THERMAL: /* thermal shutdown ignored */ + case BQ_FAULT_NONE: + default: + { + mp_obj_dict_store( faults, mp_obj_new_str( "Charge", 6 ), mp_obj_new_str( "Normal", 6 ) ); + break; + } + } + return faults; +} + +static MP_DEFINE_CONST_FUN_OBJ_0( Fault_obj, power_Fault); + +static const mp_rom_map_elem_t power_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_power) }, + { MP_ROM_QSTR(MP_QSTR_enable5V), MP_ROM_PTR(&enable5V_obj) }, + { MP_ROM_QSTR(MP_QSTR_Off), MP_ROM_PTR(&Off_obj) }, + { MP_ROM_QSTR(MP_QSTR_Vin), MP_ROM_PTR(&Vbus_obj) }, + { MP_ROM_QSTR(MP_QSTR_Vsys), MP_ROM_PTR(&Vsys_obj) }, + { MP_ROM_QSTR(MP_QSTR_Vbat), MP_ROM_PTR(&Vbat_obj) }, + { MP_ROM_QSTR(MP_QSTR_Icharge), MP_ROM_PTR(&Icharge_obj) }, + { MP_ROM_QSTR(MP_QSTR_BatteryLevel), MP_ROM_PTR(&BatteryLevel_obj) }, + { MP_ROM_QSTR(MP_QSTR_BatteryChargeState), MP_ROM_PTR(&BatteryChargeState_obj) }, + { MP_ROM_QSTR(MP_QSTR_SupplyCapabilities), MP_ROM_PTR(&SupplyCapabilities_obj) }, + { MP_ROM_QSTR(MP_QSTR_Fault), MP_ROM_PTR(&Fault_obj) }, +}; + +static MP_DEFINE_CONST_DICT(power_module_globals, power_module_globals_table); + +const mp_obj_module_t power_user_cmodule = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&power_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_power, power_user_cmodule); diff --git a/drivers/tildagon_power/tildagon_power.c b/drivers/tildagon_power/tildagon_power.c new file mode 100644 index 0000000..4bac961 --- /dev/null +++ b/drivers/tildagon_power/tildagon_power.c @@ -0,0 +1,565 @@ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/idf_additions.h" +#include "esp_task.h" +#include "esp_random.h" +#include "driver/i2c_master.h" +#include "modmachine.h" + +//#include "py/runtime.h"// debug + +#include "tildagon_power.h" + + +typedef enum +{ + DISABLED, + UNATTACHED, + ATTACHED +} attach_machine_state_t; + +typedef enum +{ + NOT_STARTED = 0x00, + WAITING = 0x01, + POWER_REQUESTED = 0x02, + PSU_READY_RECEIVED = 0x03, + BADGE_TO_BADGE = 0x04, + LANYARD = 0x05, + REQUEST_RETRY_FAIL = 0x06, + VENDOR_SENT = 0x07, + VENDOR_RETRY_FAIL = 0x08, +} pd_machine_state_t; + +typedef enum +{ + NO_EVENT, + INTERRUPT_EVENT, + HOST_TOGGLE, + HOST_ATTACH, + HOST_DETACH, + HOST_GOODCRCSENT, + HOST_MAX_EVENT, + DEVICE_TOGGLE, + DEVICE_ATTACH, + DEVICE_DETACH, + DEVICE_GOODCRCSENT, + DEVICE_BC_LEVEL, +} event_t; + +typedef void (*funptr_t)( event_t ); + +void host_disabled_handler( event_t event ); +void host_unattached_handler( event_t event ); +void host_attached_handler ( event_t event ); +void device_disabled_handler ( event_t event ); +void device_unattached_handler( event_t event ); +void device_attached_handler( event_t event ); +void device_pd_machine ( event_t event ); +void generate_events( void ); +void determine_input_current_limit ( usb_state_t* state ); +void clean_in( void ); +void clean_out( void ); + +funptr_t host_attach_machine[3] = +{ + host_disabled_handler, + host_unattached_handler, + host_attached_handler +}; + +funptr_t device_attach_machine[3] = +{ + device_disabled_handler, + device_unattached_handler, + device_attached_handler +}; + +bq_state_t pmic = { 0 }; +usb_state_t usb_in = { 0 }; +usb_state_t usb_out = { 0 }; + +static attach_machine_state_t host_attach_state = DISABLED; +static attach_machine_state_t device_attach_state = DISABLED; +static pd_machine_state_t host_pd_state = NOT_STARTED; +static pd_machine_state_t device_pd_state = NOT_STARTED; +static TaskHandle_t tildagon_power_task_handle = NULL; +static QueueHandle_t event_queue; + +/** + * @brief fast rate task to handle the interrupt generated events + */ +void tildagon_power_fast_task(void *param __attribute__((__unused__))) +{ + + event_queue = xQueueCreate( 10, sizeof(event_t) ); + + usb_in.fusb.mux_port = tildagon_get_mux_obj( 7 ); + usb_out.fusb.mux_port = tildagon_get_mux_obj( 0 ); + pmic.mux_port = tildagon_get_mux_obj( 7 ); + // turn off 5V switch before setting up PMIC as the reset will enable the boost. + //todo add when port expander interface available + bq_init( &pmic ); + + /* setup lanyard and badge to badge numbers */ + esp_fill_random( usb_in.pd.badge_id, 8 ); + for ( uint8_t i = 0; i < 8; i++) + { + usb_out.pd.badge_id[i] = usb_in.pd.badge_id[i]; + } + + /* initialise isr */ + // ToDo: move to allow sharing of sys_int isr + machine_pins_init(); + gpio_set_intr_type(GPIO_NUM_10, GPIO_INTR_NEGEDGE ); + gpio_isr_handler_add( GPIO_NUM_10, tildagon_power_interrupt_event, NULL ); + + fusb_setup_device( &usb_in.fusb ); + fusb_setup_host( &usb_out.fusb ); + + /* determine the current state */ + fusb_get_status( &usb_in.fusb ); + fusb_get_statusa( &usb_in.fusb ); + if ( usb_in.fusb.statusa & FUSB_STATUSA_TOGGLE_MASK ) + { + //const event_t event = DEVICE_TOGGLE; + //xQueueSendToBack(event_queue, (void*)&event , (TickType_t)0 ); + device_attach_state = UNATTACHED; + usb_in.fusb.cc_select = ( usb_in.fusb.statusa >> FUSB_STATUSA_TOGGLE_SHIFT ) & 0x03; + fusb_mask_interrupt_bclevel( &usb_in.fusb, 0 ); + fusb_set_cc( &usb_in.fusb, usb_in.fusb.cc_select ); + } + + fusb_get_status( &usb_out.fusb ); + fusb_get_statusa( &usb_out.fusb ); + if ( usb_out.fusb.statusa & FUSB_STATUSA_TOGGLE_MASK ) + { + const event_t event = HOST_TOGGLE; + xQueueSendToBack(event_queue, (void*)&event , (TickType_t)0 ); + } + + bq_update_state( &pmic ); + /* + The interrupt adds an event to the queue to trigger a read over I2C + as the freertos has a thread safe accessor to the queue + */ + if ( event_queue != NULL ) + { + while ( 1 ) + { + event_t event = NO_EVENT; + if ( xQueueReceive(event_queue, &event, portMAX_DELAY) ) + { + if ( ( event == NO_EVENT ) ) + { + /* this may happen if portMAX_DELAY isn't configured to block forever */ + } + else if ( ( event == INTERRUPT_EVENT ) ) + { + generate_events(); + } + else if ( event < HOST_MAX_EVENT ) + { + (*host_attach_machine[host_attach_state])( event ); + } + else + { + (*device_attach_machine[device_attach_state])( event ); + } + } + } + } +} + +/** + * @brief initialise the badge power management task + */ +void tildagon_power_init( void ) +{ + xTaskCreatePinnedToCore(tildagon_power_fast_task, "power_task", 2048, NULL, tskIDLE_PRIORITY+3, &tildagon_power_task_handle, 0); +} + +/** + * @brief add an interrupt event to the queue. intended to be called from an ISR + */ +void tildagon_power_interrupt_event( void* param ) +{ + BaseType_t TaskWoken; + TaskWoken = pdFALSE; + const event_t event = INTERRUPT_EVENT; + xQueueSendToBackFromISR( event_queue, (void*)&event, &TaskWoken ); +} + +/** + * @brief disconnect the battery to allow turn off on usb is disconnect + */ +void tildagon_power_off( void ) +{ + bq_disconnect_battery( &pmic ); +} + +/** + * @brief turn the 5V supply on or off + * @details check if we need Hi-Z. only required when 500mA input current limit + * from usb A-C cable or when usb in is not attached ( default current limit is 500mA) + * or when there is a 1500mA input current limit until we have established that we're not in lanyard mode + */ +void tildagon_power_enable_5v( bool enable ) +{ + if ( enable ) + { + if ( + ( usb_in.fusb.input_current_limit == 500U ) + || ( ( usb_in.fusb.input_current_limit == 1500U ) && ( host_pd_state != VENDOR_RETRY_FAIL ) ) + ) + { + bq_enable_HiZ_input( &pmic, 1 ); + } + /* switch on 5V */ + bq_enable_boost( &pmic, 1 ); + /* close 5V switch */ + //todo add this after hi-Z control has been tested + } + else + { + /* open switch */ + //todo add when port expander interface available + /* switch off 5V */ + bq_enable_boost( &pmic, 0 ); + /* enable Hi-Z on PMIC */ + bq_enable_HiZ_input( &pmic, 0 ); + } +} + +/** + * @brief handler for host events possible when nothing is attached + */ +void host_disabled_handler( event_t event ) +{ + if( event == HOST_TOGGLE ) + { + host_attach_state = UNATTACHED; + usb_out.fusb.cc_select = ( usb_out.fusb.statusa >> FUSB_STATUSA_TOGGLE_SHIFT ) & 0x03; + fusb_mask_interrupt_toggle( &usb_in.fusb, 1 ); + fusb_stop_toggle(&usb_out.fusb ); + fusb_mask_interrupt_bclevel( &usb_out.fusb, 0 ); + fusb_mask_interrupt_comp( &usb_out.fusb, 0 ); + fusb_set_cc( &usb_out.fusb, usb_out.fusb.cc_select ); + //const mp_print_t *print = &mp_plat_print; // debug + //mp_printf(print, "HOST_TOGGLE" ); // debug + } +} + +/** + * @brief handler for host events to detect attach + */ +void host_unattached_handler( event_t event ) +{ + if( event == HOST_ATTACH ) + { + host_attach_state = ATTACHED; + fusb_mask_interrupt_bclevel( &usb_out.fusb, 1 ); + tildagon_power_enable_5v(true); + //todo enable host pd + //fusb_setup_pd(&usb_out.fusb ); + //fusb_mask_interrupt_retryfail( &usb_out.fusb, 0 ); + //fusb_mask_interrupt_txsent( &usb_out.fusb, 0 ); + //fusbpd_vendor_specific( &usb_out.pd ); + //fusb_send ( &usb_out.fusb, usb_out.pd.tx_buffer, usb_out.pd.message_length ); + //host_pd_state = VENDOR_SENT; + + //const mp_print_t *print = &mp_plat_print; // debug + //mp_printf(print, "HOST_ATTACH" ); // debug + } +} + +/** + * @brief handler for events while attached + */ +void host_attached_handler( event_t event ) +{ + if( event == HOST_DETACH ) + { + host_attach_state = DISABLED; + clean_out(); + //const mp_print_t *print = &mp_plat_print; // debug + //mp_printf(print, "HOST_DETACH" ); // debug + } + else + { + /* pass to host pd state machine or handle here? */ + + } +} + + +/** + * @brief handler for device events possible when nothing is attached + */ +void device_disabled_handler( event_t event ) +{ + if( event == DEVICE_TOGGLE ) + { + device_attach_state = UNATTACHED; + fusb_mask_interrupt_toggle( &usb_in.fusb, 1 ); + fusb_get_statusa( &usb_in.fusb ); + /* use toggle status to get which CC line to use for PD */ + fusb_mask_interrupt_bclevel( &usb_in.fusb, 0 ); + usb_in.fusb.cc_select = ( usb_in.fusb.statusa >> FUSB_STATUSA_TOGGLE_SHIFT ) & 0x03; + fusb_set_cc( &usb_in.fusb, usb_in.fusb.cc_select ); + if ( usb_in.fusb.status & FUSB_STATUS_VBUSOK_MASK ) + { + const event_t event = DEVICE_ATTACH; + xQueueSendToBack(event_queue, (void*)&event , (TickType_t)0 ); + } + //const mp_print_t *print = &mp_plat_print; // debug + //mp_printf(print, "DEVICE_TOGGLE" ); // debug + } +} + +/** + * @brief handler for device events to detect attach + */ +void device_unattached_handler( event_t event ) +{ + if ( event == DEVICE_ATTACH ) + { + device_attach_state = ATTACHED; + const mp_print_t *print = &mp_plat_print; // debug + mp_printf(print, "DEVICE_ATTACH" ); // debug + } + else if ( event == DEVICE_BC_LEVEL ) + { + determine_input_current_limit( &usb_in ); + if ( ( usb_in.fusb.input_current_limit >= 1500 ) && ( device_pd_state == NOT_STARTED ) ) + { + fusb_setup_pd( &usb_in.fusb ); + device_pd_state = WAITING; + } + fusb_mask_interrupt_bclevel( &usb_in.fusb, 1 ); + //const mp_print_t *print = &mp_plat_print; // debug + //mp_printf(print, "DEVICE_BC_LEVEL" ); // debug + } +} + +/** + * @brief handler for device events while attached + */ +void device_attached_handler( event_t event ) +{ + if ( event == DEVICE_DETACH) + { + device_attach_state = DISABLED; + clean_in(); + //const mp_print_t *print = &mp_plat_print; // debug + //mp_printf(print, "DEVICE_DETACH" ); // debug + } + else if ( event == DEVICE_BC_LEVEL ) + { + determine_input_current_limit( &usb_in ); + if ( ( usb_in.fusb.input_current_limit >= 1500 ) && ( device_pd_state == NOT_STARTED ) ) + { + //todo enable once fusb is replaced and tvs diodes fitted + //fusb_setup_pd( &usb_in.fusb ); + //device_pd_state = WAITING; + } + fusb_mask_interrupt_bclevel( &usb_in.fusb, 1 ); + //const mp_print_t *print = &mp_plat_print; // debug + //mp_printf(print, "DEVICE_BC_LEVEL" ); // debug + } + else + { + if ( device_pd_state > NOT_STARTED ) + { + device_pd_machine( event ); + } + } +} + +/** + * @brief state machine for the device pd comms + */ +void device_pd_machine ( event_t event ) +{ + switch ( device_pd_state ) + { + case WAITING: + { + if ( event == DEVICE_GOODCRCSENT ) + { + fusbpd_decode( &usb_in.pd, &usb_in.fusb ); + if ( usb_in.pd.last_rx_data_msg_type == PD_DATA_SOURCE_CAPABILITIES ) + { + uint8_t index = fusbpd_select_pdo( &usb_in.pd ); + fusbpd_request_power( &usb_in.pd, index, usb_in.pd.pdos[index].fixed.max_current * 10, usb_in.pd.pdos[index].fixed.max_current * 10 ); + fusb_send( &usb_in.fusb, usb_in.pd.tx_buffer, usb_in.pd.message_length ); + usb_in.pd.last_rx_data_msg_type = PD_DATA_DO_NOT_USE; + device_pd_state = POWER_REQUESTED; + } + else if( usb_in.pd.last_rx_data_msg_type == PD_DATA_VENDOR_DEFINED ) + { + /* if vendor pdo received decide on badge to badge or lanyard mode and callback? */ + } + } + break; + } + case POWER_REQUESTED: + { + if ( event == DEVICE_GOODCRCSENT ) + { + fusb_auto_good_crc( &usb_in.fusb ); + fusbpd_decode( &usb_in.pd, &usb_in.fusb ); + /* if psu ready move state */ + if ( usb_in.pd.last_rx_control_msg_type == PD_CONTROL_PS_RDY ) + { + device_pd_state = PSU_READY_RECEIVED; + } + } + break; + } + case PSU_READY_RECEIVED: + case BADGE_TO_BADGE: + case LANYARD: + default: + { + /* stay in this state until detach */ + } + } +} + + +/** + * @brief Determines if the interrupt was a USB event and which one. + */ +void generate_events( void ) +{ + if ( gpio_get_level( GPIO_NUM_10 ) == 0 ) + { + uint16_t interruptab = fusb_get_interruptab( &usb_in.fusb ); + uint8_t interrupt = fusb_get_interrupt( &usb_in.fusb ); + fusb_get_status( &usb_in.fusb ); + fusb_get_statusa( &usb_in.fusb ); + if( interruptab & FUSB_TOGGLE_I_MASK ) + { + const event_t event = DEVICE_TOGGLE; + xQueueSendToBack(event_queue, (void*)&event , (TickType_t)0 ); + } + if( ( interrupt & FUSB_VBUSOK_I_MASK ) && ( usb_in.fusb.status & FUSB_STATUS_VBUSOK_MASK ) ) + { + const event_t event = DEVICE_ATTACH; + xQueueSendToBack(event_queue, (void*)&event , (TickType_t)0 ); + } + else if ( ( interrupt & FUSB_VBUSOK_I_MASK ) && ( ( usb_in.fusb.status & FUSB_STATUS_VBUSOK_MASK ) == 0 ) ) + { + const event_t event = DEVICE_DETACH; + xQueueSendToBack(event_queue, (void*)&event , (TickType_t)0 ); + } + if ( device_pd_state > NOT_STARTED ) + { + if ( interruptab & FUSB_GD_CRC_I_MASK ) + { + const event_t event = DEVICE_GOODCRCSENT; + xQueueSendToBack(event_queue, (void*)&event , (TickType_t)0 ); + } + } + else if ( interrupt & FUSB_BC_LVL_I_MASK ) + { + const event_t event = DEVICE_BC_LEVEL; + xQueueSendToBack(event_queue, (void*)&event , (TickType_t)0 ); + } + } + + if ( gpio_get_level( GPIO_NUM_10 ) == 0 ) + { + uint16_t interruptab = fusb_get_interruptab( &usb_out.fusb ); + uint8_t interrupt = fusb_get_interrupt( &usb_out.fusb ); + fusb_get_status( &usb_out.fusb ); + fusb_get_statusa( &usb_out.fusb ); + + if ( interruptab & FUSB_TOGGLE_I_MASK ) + { + const event_t event = HOST_TOGGLE; + xQueueSendToBack(event_queue, (void*)&event, (TickType_t)0 ); + } + if ( ( ( ( usb_out.fusb.status & FUSB_STATUS_COMP_MASK ) == 0U ) && ( interrupt & FUSB_CMPCHG_I_MASK ) ) + || ( ( interrupt & FUSB_BC_LVL_I_MASK ) && ( ( usb_out.fusb.status & FUSB_STATUS_BCLVL_MASK ) < 3 ) ) ) + { + const event_t event = HOST_ATTACH; + xQueueSendToBack(event_queue, (void*)&event, (TickType_t)0 ); + } + else if ( ( usb_out.fusb.status & FUSB_STATUS_COMP_MASK ) && ( interrupt & FUSB_CMPCHG_I_MASK ) ) + { + const event_t event = HOST_DETACH; + xQueueSendToBack(event_queue, (void*)&event , (TickType_t)0 ); + } + if( host_pd_state > NOT_STARTED ) + { + if ( interruptab & FUSB_GD_CRC_I_MASK ) + { + const event_t event = HOST_GOODCRCSENT; + xQueueSendToBack(event_queue, (void*)&event , (TickType_t)0 ); + } + } + } +} + +/** + * @brief figure out the input current limit of the supply + */ +void determine_input_current_limit ( usb_state_t* state ) +{ + state->fusb.input_current_limit = 500; + uint16_t bc_level = state->fusb.status & FUSB_STATUS_BCLVL_MASK; + if ( bc_level > 0 ) + { + if ( bc_level == 2 ) + { + state->fusb.input_current_limit = 1500U; + } + else if ( ( bc_level == 3 ) && ( ( state->fusb.status & FUSB_STATUS_COMP_MASK ) == 0 ) ) + { + state->fusb.input_current_limit = 3000U; + } + } + bq_set_input_current_limit( &pmic, (float) state->fusb.input_current_limit ); +} + +/** + * @brief reset the device port to the initial state + */ +void clean_in( void ) +{ + usb_in.fusb.input_current_limit = 500; + bq_set_input_current_limit( &pmic, (float) usb_in.fusb.input_current_limit ); + device_pd_state = NOT_STARTED; + usb_in.fusb.cc_select = 0U; + fusb_setup_device( &usb_in.fusb ); + usb_in.pd.last_rx_control_msg_type = 0U; + usb_in.pd.last_rx_data_msg_type = 0U; + usb_in.pd.number_of_pdos = 0U; + *((uint16_t*)&usb_in.pd.last_rx_header.raw[0]) = 0U; + usb_in.pd.msg_id = 0U; + *((uint32_t*)&usb_in.pd.rx_badge_id[0]) = 0U; + *((uint32_t*)&usb_in.pd.rx_badge_id[4]) = 0U; +} + +/** + * @brief reset the host port to the initial state + */ +void clean_out( void ) +{ + tildagon_power_enable_5v(false); + host_pd_state = NOT_STARTED; + usb_in.fusb.cc_select = 0; + fusb_setup_host( &usb_out.fusb ); + usb_out.pd.last_rx_control_msg_type = 0U; + usb_out.pd.last_rx_data_msg_type = 0U; + usb_out.pd.number_of_pdos = 0U; + + *((uint16_t*)&usb_out.pd.last_rx_header.raw[0]) = 0U; + *((uint32_t*)&usb_out.pd.rx_badge_id[0]) = 0U; + *((uint32_t*)&usb_out.pd.rx_badge_id[4]) = 0U; +} + diff --git a/drivers/tildagon_power/tildagon_power.cmake b/drivers/tildagon_power/tildagon_power.cmake new file mode 100644 index 0000000..f1b4943 --- /dev/null +++ b/drivers/tildagon_power/tildagon_power.cmake @@ -0,0 +1,22 @@ +# Create an INTERFACE library for our C module. +add_library(usermod_tildagon_power INTERFACE) + + +# Add our source files to the lib +target_sources(usermod_tildagon_power INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/tildagon_power.c + ${CMAKE_CURRENT_LIST_DIR}/bq25895/bq25895.c + ${CMAKE_CURRENT_LIST_DIR}/fusb302b/fusb302b.c + ${CMAKE_CURRENT_LIST_DIR}/fusb302b/fusb302b_pd.c + ${CMAKE_CURRENT_LIST_DIR}/mp_power.c +) + +# Add the current directory as an include directory. +target_include_directories(usermod_tildagon_power INTERFACE + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/bq25895 + ${CMAKE_CURRENT_LIST_DIR}/fusb302b +) + +# Link our INTERFACE library to the usermod target. +target_link_libraries(usermod INTERFACE usermod_tildagon_power) \ No newline at end of file diff --git a/drivers/tildagon_power/tildagon_power.h b/drivers/tildagon_power/tildagon_power.h new file mode 100644 index 0000000..ceaa05e --- /dev/null +++ b/drivers/tildagon_power/tildagon_power.h @@ -0,0 +1,47 @@ + +#ifndef TILDAGON_POWER_H +#define TILDAGON_POWER_H + + +#include +#include +#include "bq25895/bq25895.h" +#include "fusb302b/fusb302b_pd.h" +#include "fusb302b/fusb302b.h" + + +#define VBATMAX 4.104F +#define VBATMIN 2.500F + +typedef struct +{ + fusb_state_t fusb; + pd_state_t pd; +} usb_state_t; + +extern bq_state_t pmic; +extern usb_state_t usb_in; +extern usb_state_t usb_out; + +/** + * @brief initialise the badge power management task + */ +extern void tildagon_power_init( void ); +/** + * @brief add an interrupt event to the queue. intended to be called from an ISR + */ +extern void tildagon_power_interrupt_event ( void* param ); +/** + * @brief disconnect the battery to allow turn off on usb is disconnect + */ +extern void tildagon_power_off( void ); +/** + * @brief turn the 5V supply on or off + * @details check if we need Hi-Z. only required when 500mA input current limit + * from usb A-C cable or when usb in is not attached ( default current limit is 500mA) + * or when there is a 1500mA input current limit until we have established that we're not in lanyard mode + */ +extern void tildagon_power_enable_5v( bool enable ); + + +#endif diff --git a/modules/fusb302b.py b/modules/fusb302b.py index 2b7761e..05f5149 100644 --- a/modules/fusb302b.py +++ b/modules/fusb302b.py @@ -109,17 +109,17 @@ def __init__(self, i2c_instance): # reg 0E mask a mask_hardreset_int = Register(0x0E, 0x01, 0) mask_softreset_int = Register(0x0E, 0x02, 1) - mask_tx_snet_int = Register(0x0E, 0x04, 2) + mask_tx_sent_int = Register(0x0E, 0x04, 2) mask_hard_sent_int = Register(0x0E, 0x08, 3) - mask_retry_fail_int = Register(0x0E, 0x01, 4) - mask_soft_fail_int = Register(0x0E, 0x02, 5) - mask_toggle_done_int = Register(0x0E, 0x04, 6) - mask_ocp_temp_int = Register(0x0E, 0x08, 7) + mask_retry_fail_int = Register(0x0E, 0x10, 4) + mask_soft_fail_int = Register(0x0E, 0x20, 5) + mask_toggle_done_int = Register(0x0E, 0x40, 6) + mask_ocp_temp_int = Register(0x0E, 0x80, 7) # reg 0F mask b mask_good_crc_sent_int = Register(0x0F, 0x01, 0) # reg 10 Control4 toggle_unattahce_exit = Register(0x10, 0x01, 0) - # reg 3C status a read only + # reg 3C status 0a read only hard_reset_order = Register(0x3C, 0x01, 0) soft_reset_order = Register(0x3C, 0x02, 1) power_state = Register(0x3C, 0x0C, 2) @@ -144,7 +144,7 @@ def __init__(self, i2c_instance): # reg 40 status0 bc_level = Register(0x40, 0x03, 0) wake = Register(0x40, 0x04, 2) - alert = Register(0x40, 0x80, 3) + alert = Register(0x40, 0x08, 3) crc_check = Register(0x40, 0x10, 4) comparator = Register(0x40, 0x20, 5) activity = Register(0x40, 0x40, 6) @@ -323,7 +323,7 @@ def power_up(self): self.ADDRESS, self.enable_oscillator.register, bytes([0x0F]) ) - def get_Status0(self): + def get_status0(self): """ Returns the decoded status0 read from the device Do not use from an ISR @@ -434,7 +434,7 @@ def get_status1a(self): ("RXSOP", 0), ("RXSOP1DB", 0), ("RXSOP2DB", 0), - # ( 'TOGSS', 0 ) + ("TOGSS", 0 ), ] ) status["RXSOP"] = (read & self.rx_sop.mask) >> self.ocp.position @@ -444,7 +444,9 @@ def get_status1a(self): status["RXSOP2DB"] = ( read & self.rx_sop_double_debug.mask ) >> self.rx_sop_double_debug.position - # status['TOGSS'] = ( read & self.toggle_status.mask ) >> self.toggle_status.position + status["TOGSS"] = ( + read & self.toggle_status.mask + ) >> self.toggle_status.position return status def get_interrupts(self): @@ -460,9 +462,9 @@ def get_interrupts(self): Interruptb = self.i2c.readfrom_mem( self.ADDRESS, self.good_crc_sent_int.register, 1 )[0] - Interrupt = self.i2c.readfrom_mem(self.ADDRESS, self.bc_level_int.register, 1)[ - 0 - ] + Interrupt = self.i2c.readfrom_mem( + self.ADDRESS, self.bc_level_int.register, 1 + )[0] current_interrupts = dict( [ ("I_HARDRST", 0), @@ -471,6 +473,7 @@ def get_interrupts(self): ("I_HARDSENT", 0), ("I_RETRYFAIL", 0), ("I_SOFTFAIL", 0), + ("I_TOGDONE", 0 ), ("I_OCP_TEMP", 0), ("I_GCRCSENT", 0), ("I_BC_LVL", 0), @@ -504,6 +507,9 @@ def get_interrupts(self): current_interrupts["I_OCP_TEMP"] = ( Interrupta & self.ocp_temp_int.mask ) >> self.ocp_temp_int.position + current_interrupts['I_TOGDONE '] = ( + Interrupta & self.toggle_done_int.mask + ) >> self.toggle_done_int.position current_interrupts["I_GCRCSENT"] = ( Interruptb & self.good_crc_sent_int.mask ) >> self.good_crc_sent_int.position @@ -549,14 +555,14 @@ def determine_input_current_limit(self): self.write_bits(self.measure_cc, 1) self.cc_select = 1 sleep(0.001) - status = self.get_Status0() + status = self.get_status0() if status["BC_LVL"] == 0: # Ra connected to this CC, change to other CC connection self.cc_select = 2 self.write_bits(self.measure_cc, 2) sleep(0.001) # re-read status - status = self.get_Status0() + status = self.get_status0() # determine current level. self.input_current_limit = 500 if status["BC_LVL"] > 0: @@ -622,12 +628,12 @@ def setup_device(self): self.i2c.writeto_mem(self.ADDRESS, 0x07, bytes([0x04])) # set bits for good crc and auto response last as we need to respond within a timeout. - self.i2c.writeto_mem(self.ADDRESS, 0x03, bytes([0xA4])) + self.i2c.writeto_mem(self.ADDRESS, 0x03, bytes([0x24])) self.host = False def setup_host(self): """ - Initialise the fusb302 to a device + Initialise the fusb302 to a host """ # put device into a known state self.reset() @@ -635,7 +641,7 @@ def setup_host(self): # set power and data roles self.i2c.writeto_mem(self.ADDRESS, 0x03, bytes([0xB0])) # setup pull ups and measure on cc1 - self.i2c.writeto_mem(self.ADDRESS, 0x02, bytes([0xC4])) + self.i2c.writeto_mem(self.ADDRESS, 0x02, bytes([0xC0])) self.cc_select = 1 # set measurement level self.i2c.writeto_mem(self.ADDRESS, 0x04, bytes([0x25])) @@ -689,7 +695,7 @@ def setup_pd(self): def get_rxb(self, length=80): # read the FIFO contents - return self.i2c.readfrom_mem(0x22, 0x43, length) + return self.i2c.readfrom_mem(self.ADDRESS, 0x43, length) def request_pdo(self, num, current, max_current, msg_id=0): sop_seq = [ @@ -729,9 +735,9 @@ def request_pdo(self, num, current, max_current, msg_id=0): sop_seq[4] |= pdo_len - self.i2c.writeto_mem(0x22, 0x43, bytes(sop_seq)) - self.i2c.writeto_mem(0x22, 0x43, bytes(pdo)) - self.i2c.writeto_mem(0x22, 0x43, bytes(eop_seq)) + self.i2c.writeto_mem(self.ADDRESS, self.rxtx_fifo.register, bytes(sop_seq)) + self.i2c.writeto_mem(self.ADDRESS, self.rxtx_fifo.register, bytes(pdo)) + self.i2c.writeto_mem(self.ADDRESS, self.rxtx_fifo.register, bytes(eop_seq)) pdo_types = ["fixed", "batt", "var", "pps"] pps_types = ["spr", "epr", "res", "res"] @@ -739,17 +745,14 @@ def request_pdo(self, num, current, max_current, msg_id=0): def read_pdos(self): pdo_list = [] header = self.get_rxb(1)[0] - print(header) if header == 0xE0: b1, b0 = self.get_rxb(2) pdo_count = (b0 >> 4) & 0b111 read_len = pdo_count * 4 pdos = self.get_rxb(read_len) - print(pdos.hex()) _ = self.get_rxb(4) # crc for pdo_i in range(pdo_count): pdo_bytes = pdos[(pdo_i * 4) :][:4] - print(pdo_bytes.hex()) parsed_pdo = self.parse_pdo(pdo_bytes) pdo_list.append(parsed_pdo) return pdo_list @@ -795,7 +798,7 @@ def parse_pdo(self, pdo): limited, ) - def request_capability(self): + def request_capability(self, msg_id = 0): """ ask for the power supply options """ @@ -804,22 +807,22 @@ def request_capability(self): self.TX_SOP1, self.TX_SOP1, self.TX_SOP2, - self.TX_PACKSYM, + self.TX_PACKSYM | 0x02, 0x47, - 0xE0, + ( 0x00 | ( ( msg_id & 0x07 ) << 1 ) ), self.TX_JAM_CRC, self.TX_EOP, self.TX_OFF, self.TX_ON, ] - self.i2c.writeto_mem(0x22, 0x43, bytes(seq)) + self.i2c.writeto_mem(self.ADDRESS, self.rxtx_fifo.register, bytes(seq)) def soft_reset(self): """ reset the protocol layer on other port """ - self.i2c.writeto_mem(0x22, 0x43, bytes(self.TX_RESET1)) + self.i2c.writeto_mem(self.ADDRESS, self.rxtx_fifo.register, bytes(self.TX_RESET1)) if __name__ == "__main__": diff --git a/tildagon/board_init.c b/tildagon/board_init.c index 4306a95..8e57e4c 100644 --- a/tildagon/board_init.c +++ b/tildagon/board_init.c @@ -1,4 +1,6 @@ +#include "tildagon_power.h" + // This is the default startup handler for ESP32, does VFS and stuff void boardctrl_startup(void); @@ -13,8 +15,10 @@ void tildagon_i2c_init(void); void tildagon_startup(void) { // call the micropy default startup - does VFS init on ESP32 boardctrl_startup(); - + + tildagon_i2c_init(); + tildagon_power_init(); + tildagon_usb_init(); - tildagon_i2c_init(); } From af97d31e389406a518ad855de10d55b98e77a612 Mon Sep 17 00:00:00 2001 From: Chris Date: Tue, 21 May 2024 20:24:00 +0100 Subject: [PATCH 2/7] glowing lanyard and host 5v and pd transmission contains hacks for isr and 5v switch, these need replacing before integrating --- drivers/tildagon_power/bq25895/bq25895.c | 31 ++- drivers/tildagon_power/fusb302b/fusb302b.h | 3 +- drivers/tildagon_power/fusb302b/fusb302b_pd.c | 2 +- drivers/tildagon_power/mp_power.c | 2 +- drivers/tildagon_power/tildagon_power.c | 189 ++++++++++-------- drivers/tildagon_power/tildagon_power.h | 3 - modules/bq25895.py | 3 +- 7 files changed, 138 insertions(+), 95 deletions(-) diff --git a/drivers/tildagon_power/bq25895/bq25895.c b/drivers/tildagon_power/bq25895/bq25895.c index 88155db..7016625 100644 --- a/drivers/tildagon_power/bq25895/bq25895.c +++ b/drivers/tildagon_power/bq25895/bq25895.c @@ -119,16 +119,37 @@ void bq_set_input_current_limit( bq_state_t* state, float limit ) */ void bq_update_state( bq_state_t* state ) { - uint8_t address = 0x0B; + uint8_t address = 0x0BU; uint8_t read_buffer[8]; mp_machine_i2c_buf_t buffer[2] = { { .len = 1, .buf = &address }, { .len = 8, .buf = read_buffer } }; tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 2, buffer, READ ); state->status = read_buffer[0]; state->fault = read_buffer[1]; - state->vbat = ((float)( read_buffer[3] & 0x7F) * 0.02F ) + 2.304F; - state->vsys = ((float)( read_buffer[4] & 0x7F) * 0.02F ) + 2.304F; - state->vbus = ((float)( read_buffer[6] & 0x7F) * 0.10F ) + 2.600F; + if ( ( read_buffer[3] & 0x7F ) == 0U ) + { + state->vbat = 0.0F; + } + else + { + state->vbat = ((float)( read_buffer[3] & 0x7F) * 0.02F ) + 2.304F; + } + if ( ( read_buffer[4] & 0x7F ) == 0U ) + { + state->vsys = 0.0F; + } + else + { + state->vsys = ((float)( read_buffer[4] & 0x7F) * 0.02F ) + 2.304F; + } + if ( ( read_buffer[6] & 0x7F) == 0U ) + { + state->vbus = 0.0F; + } + else + { + state->vbus = ((float)( read_buffer[6] & 0x7F) * 0.10F ) + 2.600F; + } state->ichrg = ((float)( read_buffer[7] & 0x7F) * 0.05F ); } @@ -140,7 +161,7 @@ void bq_update_state( bq_state_t* state ) */ inline void write_bits( bq_state_t* state, bq_register_t reg, uint8_t value ) { - uint8_t regVal = 0; + uint8_t regVal = 0U; mp_machine_i2c_buf_t buffer[2] = { { .len = 1, .buf = ®.regaddr }, { .len = 1, .buf = ®Val } }; tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 2, buffer, READ ); diff --git a/drivers/tildagon_power/fusb302b/fusb302b.h b/drivers/tildagon_power/fusb302b/fusb302b.h index 995cc17..e9e150c 100644 --- a/drivers/tildagon_power/fusb302b/fusb302b.h +++ b/drivers/tildagon_power/fusb302b/fusb302b.h @@ -5,6 +5,7 @@ #include #include "tildagon_i2c.h" +#define FUSB_TXSENT_I_MASK (uint16_t)0x0004U #define FUSB_TOGGLE_I_MASK (uint16_t)0x0040U #define FUSB_GD_CRC_I_MASK (uint16_t)0x0100U #define FUSB_BC_LVL_I_MASK (uint8_t)0x01U @@ -12,7 +13,7 @@ #define FUSB_VBUSOK_I_MASK (uint8_t)0x80U #define FUSB_STATUSA_TOGGLE_MASK (uint16_t)0x3800U -#define FUSB_STATUSA_TOGGLE_SHIFT (int8_t)10 +#define FUSB_STATUSA_TOGGLE_SHIFT (int8_t)11 #define FUSB_STATUS_COMP_MASK (uint16_t)0x0020U #define FUSB_STATUS_BCLVL_MASK (uint16_t)0x0003U #define FUSB_STATUS_VBUSOK_MASK (uint16_t)0x0080U diff --git a/drivers/tildagon_power/fusb302b/fusb302b_pd.c b/drivers/tildagon_power/fusb302b/fusb302b_pd.c index d7e1581..1118c11 100644 --- a/drivers/tildagon_power/fusb302b/fusb302b_pd.c +++ b/drivers/tildagon_power/fusb302b/fusb302b_pd.c @@ -182,7 +182,7 @@ void fusbpd_vendor_specific( pd_state_t* state ) state->tx_buffer[4] = TX_SOP2; state->tx_buffer[5] = TX_PACKSYM | 0x0E; pd_header_union_t header = { 0U }; - header.sop.number_objects = 0x02U; + header.sop.number_objects = 0x03U; header.sop.message_id = state->msg_id; header.sop.message_type = 0x0FU; header.sop.revision = 0x01U; diff --git a/drivers/tildagon_power/mp_power.c b/drivers/tildagon_power/mp_power.c index fbbd31c..b071a2c 100644 --- a/drivers/tildagon_power/mp_power.c +++ b/drivers/tildagon_power/mp_power.c @@ -212,7 +212,7 @@ static MP_DEFINE_CONST_FUN_OBJ_0( Fault_obj, power_Fault); static const mp_rom_map_elem_t power_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_power) }, - { MP_ROM_QSTR(MP_QSTR_enable5V), MP_ROM_PTR(&enable5V_obj) }, + { MP_ROM_QSTR(MP_QSTR_Enable5V), MP_ROM_PTR(&enable5V_obj) }, { MP_ROM_QSTR(MP_QSTR_Off), MP_ROM_PTR(&Off_obj) }, { MP_ROM_QSTR(MP_QSTR_Vin), MP_ROM_PTR(&Vbus_obj) }, { MP_ROM_QSTR(MP_QSTR_Vsys), MP_ROM_PTR(&Vsys_obj) }, diff --git a/drivers/tildagon_power/tildagon_power.c b/drivers/tildagon_power/tildagon_power.c index 4bac961..6b7acd8 100644 --- a/drivers/tildagon_power/tildagon_power.c +++ b/drivers/tildagon_power/tildagon_power.c @@ -8,16 +8,14 @@ #include "driver/i2c_master.h" #include "modmachine.h" -//#include "py/runtime.h"// debug - #include "tildagon_power.h" - typedef enum { DISABLED, UNATTACHED, - ATTACHED + ATTACHED, + MAX_STATES } attach_machine_state_t; typedef enum @@ -41,11 +39,13 @@ typedef enum HOST_ATTACH, HOST_DETACH, HOST_GOODCRCSENT, + HOST_TX_SENT, HOST_MAX_EVENT, DEVICE_TOGGLE, DEVICE_ATTACH, DEVICE_DETACH, DEVICE_GOODCRCSENT, + DEVICE_TX_SENT, DEVICE_BC_LEVEL, } event_t; @@ -63,18 +63,18 @@ void determine_input_current_limit ( usb_state_t* state ); void clean_in( void ); void clean_out( void ); -funptr_t host_attach_machine[3] = +funptr_t host_attach_machine[MAX_STATES] = { host_disabled_handler, host_unattached_handler, - host_attached_handler + host_attached_handler, }; -funptr_t device_attach_machine[3] = +funptr_t device_attach_machine[MAX_STATES] = { device_disabled_handler, device_unattached_handler, - device_attached_handler + device_attached_handler, }; bq_state_t pmic = { 0 }; @@ -87,49 +87,62 @@ static pd_machine_state_t host_pd_state = NOT_STARTED; static pd_machine_state_t device_pd_state = NOT_STARTED; static TaskHandle_t tildagon_power_task_handle = NULL; static QueueHandle_t event_queue; +bool lanyard_mode = false; +//todo integrate with port expanders +#include "tildagon_i2c.h" +#define READ ( MP_MACHINE_I2C_FLAG_WRITE1 | MP_MACHINE_I2C_FLAG_READ | MP_MACHINE_I2C_FLAG_STOP ) +#define WRITE MP_MACHINE_I2C_FLAG_STOP +void dev_5v_sw( bool enable ) +{ + uint8_t state = 0x00; + uint8_t regVal = 0; + uint8_t regAddr = 0x02; + if ( enable ) + { + state = 0x10; + } + mp_machine_i2c_buf_t buffer[2] = { { .len = 1, .buf = ®Addr }, + { .len = 1, .buf = ®Val } }; + tildagon_mux_i2c_transaction( usb_in.fusb.mux_port, 0x5A, 2, buffer, READ ); + regVal = regVal & 0xEF; + regVal = regVal | state; + uint8_t write_buffer[2] = { 0x02, regVal }; + buffer[0].len = 2; + buffer[0].buf = write_buffer; + tildagon_mux_i2c_transaction( usb_in.fusb.mux_port, 0x5A, 1, buffer, WRITE ); +} + /** * @brief fast rate task to handle the interrupt generated events */ void tildagon_power_fast_task(void *param __attribute__((__unused__))) { - - event_queue = xQueueCreate( 10, sizeof(event_t) ); - + event_queue = xQueueCreate( 10, sizeof(event_t) ); usb_in.fusb.mux_port = tildagon_get_mux_obj( 7 ); usb_out.fusb.mux_port = tildagon_get_mux_obj( 0 ); pmic.mux_port = tildagon_get_mux_obj( 7 ); // turn off 5V switch before setting up PMIC as the reset will enable the boost. - //todo add when port expander interface available + dev_5v_sw(false); + //todo replace when port expander interface available bq_init( &pmic ); - /* setup lanyard and badge to badge numbers */ - esp_fill_random( usb_in.pd.badge_id, 8 ); - for ( uint8_t i = 0; i < 8; i++) - { - usb_out.pd.badge_id[i] = usb_in.pd.badge_id[i]; - } - /* initialise isr */ - // ToDo: move to allow sharing of sys_int isr + //todo move to allow sharing of sys_int isr machine_pins_init(); gpio_set_intr_type(GPIO_NUM_10, GPIO_INTR_NEGEDGE ); gpio_isr_handler_add( GPIO_NUM_10, tildagon_power_interrupt_event, NULL ); - fusb_setup_device( &usb_in.fusb ); - fusb_setup_host( &usb_out.fusb ); + clean_in(); + clean_out(); /* determine the current state */ fusb_get_status( &usb_in.fusb ); fusb_get_statusa( &usb_in.fusb ); if ( usb_in.fusb.statusa & FUSB_STATUSA_TOGGLE_MASK ) { - //const event_t event = DEVICE_TOGGLE; - //xQueueSendToBack(event_queue, (void*)&event , (TickType_t)0 ); - device_attach_state = UNATTACHED; - usb_in.fusb.cc_select = ( usb_in.fusb.statusa >> FUSB_STATUSA_TOGGLE_SHIFT ) & 0x03; - fusb_mask_interrupt_bclevel( &usb_in.fusb, 0 ); - fusb_set_cc( &usb_in.fusb, usb_in.fusb.cc_select ); + const event_t event = DEVICE_TOGGLE; + xQueueSendToBack(event_queue, (void*)&event , (TickType_t)0 ); } fusb_get_status( &usb_out.fusb ); @@ -164,10 +177,14 @@ void tildagon_power_fast_task(void *param __attribute__((__unused__))) { (*host_attach_machine[host_attach_state])( event ); } - else + else if ( !lanyard_mode ) { (*device_attach_machine[device_attach_state])( event ); } + else + { + /* throw away */ + } } } } @@ -197,38 +214,27 @@ void tildagon_power_interrupt_event( void* param ) */ void tildagon_power_off( void ) { + //todo replace 5v off. + dev_5v_sw(false); bq_disconnect_battery( &pmic ); } /** * @brief turn the 5V supply on or off - * @details check if we need Hi-Z. only required when 500mA input current limit - * from usb A-C cable or when usb in is not attached ( default current limit is 500mA) - * or when there is a 1500mA input current limit until we have established that we're not in lanyard mode */ void tildagon_power_enable_5v( bool enable ) { + //todo replace when port expander interface available if ( enable ) { - if ( - ( usb_in.fusb.input_current_limit == 500U ) - || ( ( usb_in.fusb.input_current_limit == 1500U ) && ( host_pd_state != VENDOR_RETRY_FAIL ) ) - ) - { - bq_enable_HiZ_input( &pmic, 1 ); - } - /* switch on 5V */ bq_enable_boost( &pmic, 1 ); - /* close 5V switch */ - //todo add this after hi-Z control has been tested + dev_5v_sw(true); } else { - /* open switch */ - //todo add when port expander interface available - /* switch off 5V */ + /* open switch then disable 5V */ + dev_5v_sw(false); bq_enable_boost( &pmic, 0 ); - /* enable Hi-Z on PMIC */ bq_enable_HiZ_input( &pmic, 0 ); } } @@ -242,13 +248,11 @@ void host_disabled_handler( event_t event ) { host_attach_state = UNATTACHED; usb_out.fusb.cc_select = ( usb_out.fusb.statusa >> FUSB_STATUSA_TOGGLE_SHIFT ) & 0x03; - fusb_mask_interrupt_toggle( &usb_in.fusb, 1 ); + fusb_mask_interrupt_toggle( &usb_out.fusb, 1 ); fusb_stop_toggle(&usb_out.fusb ); fusb_mask_interrupt_bclevel( &usb_out.fusb, 0 ); fusb_mask_interrupt_comp( &usb_out.fusb, 0 ); fusb_set_cc( &usb_out.fusb, usb_out.fusb.cc_select ); - //const mp_print_t *print = &mp_plat_print; // debug - //mp_printf(print, "HOST_TOGGLE" ); // debug } } @@ -261,17 +265,13 @@ void host_unattached_handler( event_t event ) { host_attach_state = ATTACHED; fusb_mask_interrupt_bclevel( &usb_out.fusb, 1 ); - tildagon_power_enable_5v(true); - //todo enable host pd - //fusb_setup_pd(&usb_out.fusb ); - //fusb_mask_interrupt_retryfail( &usb_out.fusb, 0 ); - //fusb_mask_interrupt_txsent( &usb_out.fusb, 0 ); - //fusbpd_vendor_specific( &usb_out.pd ); - //fusb_send ( &usb_out.fusb, usb_out.pd.tx_buffer, usb_out.pd.message_length ); - //host_pd_state = VENDOR_SENT; - - //const mp_print_t *print = &mp_plat_print; // debug - //mp_printf(print, "HOST_ATTACH" ); // debug + tildagon_power_enable_5v(true); + fusb_setup_pd(&usb_out.fusb ); + fusb_mask_interrupt_retryfail( &usb_out.fusb, 0 ); + fusb_mask_interrupt_txsent( &usb_out.fusb, 0 ); + fusbpd_vendor_specific( &usb_out.pd ); + fusb_send ( &usb_out.fusb, usb_out.pd.tx_buffer, usb_out.pd.message_length ); + host_pd_state = VENDOR_SENT; } } @@ -284,13 +284,14 @@ void host_attached_handler( event_t event ) { host_attach_state = DISABLED; clean_out(); - //const mp_print_t *print = &mp_plat_print; // debug - //mp_printf(print, "HOST_DETACH" ); // debug } else { - /* pass to host pd state machine or handle here? */ - + //todo host pd state machine for badge to badge + if ( host_pd_state > NOT_STARTED ) + { + //host_pd_machine( event ); + } } } @@ -304,7 +305,6 @@ void device_disabled_handler( event_t event ) { device_attach_state = UNATTACHED; fusb_mask_interrupt_toggle( &usb_in.fusb, 1 ); - fusb_get_statusa( &usb_in.fusb ); /* use toggle status to get which CC line to use for PD */ fusb_mask_interrupt_bclevel( &usb_in.fusb, 0 ); usb_in.fusb.cc_select = ( usb_in.fusb.statusa >> FUSB_STATUSA_TOGGLE_SHIFT ) & 0x03; @@ -314,8 +314,6 @@ void device_disabled_handler( event_t event ) const event_t event = DEVICE_ATTACH; xQueueSendToBack(event_queue, (void*)&event , (TickType_t)0 ); } - //const mp_print_t *print = &mp_plat_print; // debug - //mp_printf(print, "DEVICE_TOGGLE" ); // debug } } @@ -327,20 +325,22 @@ void device_unattached_handler( event_t event ) if ( event == DEVICE_ATTACH ) { device_attach_state = ATTACHED; - const mp_print_t *print = &mp_plat_print; // debug - mp_printf(print, "DEVICE_ATTACH" ); // debug } else if ( event == DEVICE_BC_LEVEL ) { determine_input_current_limit( &usb_in ); if ( ( usb_in.fusb.input_current_limit >= 1500 ) && ( device_pd_state == NOT_STARTED ) ) { - fusb_setup_pd( &usb_in.fusb ); + //todo enable device pd + //fusb_setup_pd( &usb_in.fusb ); device_pd_state = WAITING; } fusb_mask_interrupt_bclevel( &usb_in.fusb, 1 ); - //const mp_print_t *print = &mp_plat_print; // debug - //mp_printf(print, "DEVICE_BC_LEVEL" ); // debug + } + else if ( event == DEVICE_DETACH) + { + device_attach_state = DISABLED; + clean_in(); } } @@ -353,21 +353,17 @@ void device_attached_handler( event_t event ) { device_attach_state = DISABLED; clean_in(); - //const mp_print_t *print = &mp_plat_print; // debug - //mp_printf(print, "DEVICE_DETACH" ); // debug } else if ( event == DEVICE_BC_LEVEL ) { determine_input_current_limit( &usb_in ); if ( ( usb_in.fusb.input_current_limit >= 1500 ) && ( device_pd_state == NOT_STARTED ) ) { - //todo enable once fusb is replaced and tvs diodes fitted + //todo enable device pd //fusb_setup_pd( &usb_in.fusb ); - //device_pd_state = WAITING; + device_pd_state = WAITING; } fusb_mask_interrupt_bclevel( &usb_in.fusb, 1 ); - //const mp_print_t *print = &mp_plat_print; // debug - //mp_printf(print, "DEVICE_BC_LEVEL" ); // debug } else { @@ -378,6 +374,7 @@ void device_attached_handler( event_t event ) } } + /** * @brief state machine for the device pd comms */ @@ -400,7 +397,7 @@ void device_pd_machine ( event_t event ) } else if( usb_in.pd.last_rx_data_msg_type == PD_DATA_VENDOR_DEFINED ) { - /* if vendor pdo received decide on badge to badge or lanyard mode and callback? */ + /* if vendor pdo received decide on badge to badge and callback? */ } } break; @@ -421,7 +418,6 @@ void device_pd_machine ( event_t event ) } case PSU_READY_RECEIVED: case BADGE_TO_BADGE: - case LANYARD: default: { /* stay in this state until detach */ @@ -434,7 +430,13 @@ void device_pd_machine ( event_t event ) * @brief Determines if the interrupt was a USB event and which one. */ void generate_events( void ) -{ +{ + bq_update_state( &pmic ); + if ( ( pmic.vbus > 2.6 ) && ( pmic.vbus < 4.3 ) ) + { + bq_enable_HiZ_input( &pmic, 1 ); + lanyard_mode = true; + } if ( gpio_get_level( GPIO_NUM_10 ) == 0 ) { uint16_t interruptab = fusb_get_interruptab( &usb_in.fusb ); @@ -463,6 +465,10 @@ void generate_events( void ) const event_t event = DEVICE_GOODCRCSENT; xQueueSendToBack(event_queue, (void*)&event , (TickType_t)0 ); } + if ( interruptab & FUSB_TXSENT_I_MASK ) + { + usb_in.pd.msg_id++; + } } else if ( interrupt & FUSB_BC_LVL_I_MASK ) { @@ -479,7 +485,7 @@ void generate_events( void ) fusb_get_statusa( &usb_out.fusb ); if ( interruptab & FUSB_TOGGLE_I_MASK ) - { + { const event_t event = HOST_TOGGLE; xQueueSendToBack(event_queue, (void*)&event, (TickType_t)0 ); } @@ -501,6 +507,10 @@ void generate_events( void ) const event_t event = HOST_GOODCRCSENT; xQueueSendToBack(event_queue, (void*)&event , (TickType_t)0 ); } + if ( interruptab & FUSB_TXSENT_I_MASK ) + { + usb_out.pd.msg_id++; + } } } } @@ -557,9 +567,24 @@ void clean_out( void ) usb_out.pd.last_rx_control_msg_type = 0U; usb_out.pd.last_rx_data_msg_type = 0U; usb_out.pd.number_of_pdos = 0U; + usb_out.pd.msg_id = 0U; *((uint16_t*)&usb_out.pd.last_rx_header.raw[0]) = 0U; *((uint32_t*)&usb_out.pd.rx_badge_id[0]) = 0U; *((uint32_t*)&usb_out.pd.rx_badge_id[4]) = 0U; + + /* setup lanyard and badge to badge numbers */ + esp_fill_random( usb_in.pd.badge_id, 8 ); + for ( uint8_t i = 0; i < 8; i++) + { + usb_out.pd.badge_id[i] = usb_in.pd.badge_id[i]; + } + + if ( lanyard_mode ) + { + lanyard_mode = false; + const event_t event = DEVICE_DETACH; + xQueueSendToBack(event_queue, (void*)&event , (TickType_t)0 ); + } } diff --git a/drivers/tildagon_power/tildagon_power.h b/drivers/tildagon_power/tildagon_power.h index ceaa05e..a86ec93 100644 --- a/drivers/tildagon_power/tildagon_power.h +++ b/drivers/tildagon_power/tildagon_power.h @@ -37,9 +37,6 @@ extern void tildagon_power_interrupt_event ( void* param ); extern void tildagon_power_off( void ); /** * @brief turn the 5V supply on or off - * @details check if we need Hi-Z. only required when 500mA input current limit - * from usb A-C cable or when usb in is not attached ( default current limit is 500mA) - * or when there is a 1500mA input current limit until we have established that we're not in lanyard mode */ extern void tildagon_power_enable_5v( bool enable ); diff --git a/modules/bq25895.py b/modules/bq25895.py index e8168a4..28a6ddd 100644 --- a/modules/bq25895.py +++ b/modules/bq25895.py @@ -488,10 +488,9 @@ def init(self): from machine import Pin, I2C from utime import sleep_ms - i2c = I2C(0, scl=Pin(46), sda=Pin(45), freq=400000) + i2c = I2C(7) pin_reset_i2c = Pin(9, Pin.OUT) pin_reset_i2c.on() - i2c.writeto(0x77, bytes([(0x1 << 7)])) pmic = bq25895(i2c) pmic.init() # start 1Hz ADC sampling From 985b86e8726f9f0185372ce07b9e035a9dfad07d Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 27 May 2024 18:04:30 +0100 Subject: [PATCH 3/7] push power events to event bus use callbacks to push events onto event bus --- drivers/tildagon_power/mp_power_event.c | 103 ++++++++++++++++++++ drivers/tildagon_power/mp_power_event.h | 22 +++++ drivers/tildagon_power/tildagon_power.c | 31 ++++-- drivers/tildagon_power/tildagon_power.cmake | 1 + modules/fusb302b.py | 3 +- modules/main.py | 2 + modules/power_handler.py | 38 ++++++++ modules/system/power/events.py | 29 ++++++ 8 files changed, 220 insertions(+), 9 deletions(-) create mode 100644 drivers/tildagon_power/mp_power_event.c create mode 100644 drivers/tildagon_power/mp_power_event.h create mode 100644 modules/power_handler.py create mode 100644 modules/system/power/events.py diff --git a/drivers/tildagon_power/mp_power_event.c b/drivers/tildagon_power/mp_power_event.c new file mode 100644 index 0000000..f35a353 --- /dev/null +++ b/drivers/tildagon_power/mp_power_event.c @@ -0,0 +1,103 @@ + +#include "py/runtime.h"// debug +#include "py/obj.h" +#include "py/objmodule.h" +#include "py/builtin.h" +#include "mp_power_event.h" + +static mp_obj_t callbacks[MP_POWER_EVENT_MAX] = { NULL }; + +void push_event( mp_power_event_t event ) +{ + if ( callbacks[event] != NULL ) + { + mp_sched_schedule(callbacks[event], mp_const_none); + } +} + +static mp_obj_t mp_power_charge_event_set_cb(mp_obj_t cb) +{ + callbacks[MP_POWER_EVENT_CHARGE] = cb; + return mp_const_none; +} + +static MP_DEFINE_CONST_FUN_OBJ_1(mp_power_charge_event_set_cb_obj, mp_power_charge_event_set_cb); + +static mp_obj_t mp_power_fault_event_set_cb(mp_obj_t cb) +{ + callbacks[MP_POWER_EVENT_FAULT] = cb; + return mp_const_none; +} + +static MP_DEFINE_CONST_FUN_OBJ_1(mp_power_fault_event_set_cb_obj, mp_power_fault_event_set_cb); + +static mp_obj_t mp_power_host_attach_set_cb(mp_obj_t cb) +{ + callbacks[MP_POWER_EVENT_HOST_ATTACH] = cb; + return mp_const_none; +} + +static MP_DEFINE_CONST_FUN_OBJ_1(mp_power_event_host_attach_set_cb_obj, mp_power_host_attach_set_cb); + +static mp_obj_t mp_power_host_detach_set_cb(mp_obj_t cb) +{ + callbacks[MP_POWER_EVENT_HOST_DETACH] = cb; + return mp_const_none; +} + +static MP_DEFINE_CONST_FUN_OBJ_1(mp_power_event_host_detach_set_cb_obj, mp_power_host_detach_set_cb); + +static mp_obj_t mp_power_device_attach_set_cb(mp_obj_t cb) +{ + callbacks[MP_POWER_EVENT_DEVICE_ATTACH] = cb; + return mp_const_none; +} + +static MP_DEFINE_CONST_FUN_OBJ_1(mp_power_event_device_attach_set_cb_obj, mp_power_device_attach_set_cb); + +static mp_obj_t mp_power_device_detach_set_cb(mp_obj_t cb) +{ + callbacks[MP_POWER_EVENT_DEVICE_DETACH] = cb; + return mp_const_none; +} + +static MP_DEFINE_CONST_FUN_OBJ_1(mp_power_event_device_detach_set_cb_obj, mp_power_device_detach_set_cb); + +static mp_obj_t mp_power_lanyard_attach_set_cb(mp_obj_t cb) +{ + callbacks[MP_POWER_EVENT_LANYARD_ATTACH] = cb; + return mp_const_none; +} + +static MP_DEFINE_CONST_FUN_OBJ_1(mp_power_event_lanyard_attach_set_cb_obj, mp_power_lanyard_attach_set_cb); + +static mp_obj_t mp_power_lanyard_detach_set_cb(mp_obj_t cb) +{ + callbacks[MP_POWER_EVENT_LANYARD_DETACH] = cb; + return mp_const_none; +} + +static MP_DEFINE_CONST_FUN_OBJ_1(mp_power_event_lanyard_detach_set_cb_obj, mp_power_lanyard_detach_set_cb); + + +static const mp_rom_map_elem_t power_event_globals_table[] = +{ + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_power_event) }, + { MP_ROM_QSTR(MP_QSTR_set_charge_cb), MP_ROM_PTR(&mp_power_charge_event_set_cb_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_fault_cb), MP_ROM_PTR(&mp_power_fault_event_set_cb_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_host_attach_cb), MP_ROM_PTR(&mp_power_event_host_attach_set_cb_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_host_detach_cb), MP_ROM_PTR(&mp_power_event_host_detach_set_cb_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_device_attach_cb), MP_ROM_PTR(&mp_power_event_device_attach_set_cb_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_device_detach_cb), MP_ROM_PTR(&mp_power_event_device_detach_set_cb_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_lanyard_attach_cb), MP_ROM_PTR(&mp_power_event_lanyard_attach_set_cb_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_lanyard_detach_cb), MP_ROM_PTR(&mp_power_event_lanyard_detach_set_cb_obj) }, +}; + +static MP_DEFINE_CONST_DICT(power_event_globals, power_event_globals_table); + +const mp_obj_module_t mp_power_events_cmodule = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&power_event_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_power_event, mp_power_events_cmodule); diff --git a/drivers/tildagon_power/mp_power_event.h b/drivers/tildagon_power/mp_power_event.h new file mode 100644 index 0000000..b7bfd16 --- /dev/null +++ b/drivers/tildagon_power/mp_power_event.h @@ -0,0 +1,22 @@ + +#ifndef MP_POWER_EVENT_H +#define MP_POWER_EVENT_H + +#include "stdint.h" + +typedef enum +{ + MP_POWER_EVENT_CHARGE, + MP_POWER_EVENT_FAULT, + MP_POWER_EVENT_HOST_ATTACH, + MP_POWER_EVENT_HOST_DETACH, + MP_POWER_EVENT_DEVICE_ATTACH, + MP_POWER_EVENT_DEVICE_DETACH, + MP_POWER_EVENT_LANYARD_ATTACH, + MP_POWER_EVENT_LANYARD_DETACH, + MP_POWER_EVENT_MAX +} mp_power_event_t; + +extern void push_event( mp_power_event_t event ); + +#endif diff --git a/drivers/tildagon_power/tildagon_power.c b/drivers/tildagon_power/tildagon_power.c index 6b7acd8..14f17f4 100644 --- a/drivers/tildagon_power/tildagon_power.c +++ b/drivers/tildagon_power/tildagon_power.c @@ -9,6 +9,7 @@ #include "modmachine.h" #include "tildagon_power.h" +#include "mp_power_event.h" typedef enum { @@ -63,6 +64,7 @@ void determine_input_current_limit ( usb_state_t* state ); void clean_in( void ); void clean_out( void ); + funptr_t host_attach_machine[MAX_STATES] = { host_disabled_handler, @@ -272,6 +274,7 @@ void host_unattached_handler( event_t event ) fusbpd_vendor_specific( &usb_out.pd ); fusb_send ( &usb_out.fusb, usb_out.pd.tx_buffer, usb_out.pd.message_length ); host_pd_state = VENDOR_SENT; + push_event( MP_POWER_EVENT_HOST_ATTACH ); } } @@ -284,6 +287,7 @@ void host_attached_handler( event_t event ) { host_attach_state = DISABLED; clean_out(); + push_event( MP_POWER_EVENT_HOST_DETACH ); } else { @@ -325,6 +329,7 @@ void device_unattached_handler( event_t event ) if ( event == DEVICE_ATTACH ) { device_attach_state = ATTACHED; + push_event( MP_POWER_EVENT_DEVICE_ATTACH ); } else if ( event == DEVICE_BC_LEVEL ) { @@ -337,11 +342,6 @@ void device_unattached_handler( event_t event ) } fusb_mask_interrupt_bclevel( &usb_in.fusb, 1 ); } - else if ( event == DEVICE_DETACH) - { - device_attach_state = DISABLED; - clean_in(); - } } /** @@ -353,6 +353,7 @@ void device_attached_handler( event_t event ) { device_attach_state = DISABLED; clean_in(); + push_event( MP_POWER_EVENT_DEVICE_DETACH ); } else if ( event == DEVICE_BC_LEVEL ) { @@ -431,11 +432,27 @@ void device_pd_machine ( event_t event ) */ void generate_events( void ) { + uint8_t prev_status = pmic.status; + uint8_t prev_faut = pmic.fault; + bq_update_state( &pmic ); if ( ( pmic.vbus > 2.6 ) && ( pmic.vbus < 4.3 ) ) { bq_enable_HiZ_input( &pmic, 1 ); lanyard_mode = true; + push_event(MP_POWER_EVENT_LANYARD_ATTACH); + } + if ( prev_status == pmic.status ) + { + uint8_t status_change = prev_status ^ pmic.status; + if ( status_change & BQ_CHARGE_STAT_MASK ) + { + push_event( MP_POWER_EVENT_CHARGE ); + } + } + if ( ( prev_faut ^ pmic.fault ) & 0x71 ) + { + push_event( MP_POWER_EVENT_FAULT ); } if ( gpio_get_level( GPIO_NUM_10 ) == 0 ) { @@ -583,8 +600,8 @@ void clean_out( void ) if ( lanyard_mode ) { lanyard_mode = false; - const event_t event = DEVICE_DETACH; - xQueueSendToBack(event_queue, (void*)&event , (TickType_t)0 ); + clean_in(); + push_event(MP_POWER_EVENT_LANYARD_DETACH); } } diff --git a/drivers/tildagon_power/tildagon_power.cmake b/drivers/tildagon_power/tildagon_power.cmake index f1b4943..6a93a8a 100644 --- a/drivers/tildagon_power/tildagon_power.cmake +++ b/drivers/tildagon_power/tildagon_power.cmake @@ -9,6 +9,7 @@ target_sources(usermod_tildagon_power INTERFACE ${CMAKE_CURRENT_LIST_DIR}/fusb302b/fusb302b.c ${CMAKE_CURRENT_LIST_DIR}/fusb302b/fusb302b_pd.c ${CMAKE_CURRENT_LIST_DIR}/mp_power.c + ${CMAKE_CURRENT_LIST_DIR}/mp_power_event.c ) # Add the current directory as an include directory. diff --git a/modules/fusb302b.py b/modules/fusb302b.py index 05f5149..110cc6b 100644 --- a/modules/fusb302b.py +++ b/modules/fusb302b.py @@ -828,10 +828,9 @@ def soft_reset(self): if __name__ == "__main__": from machine import Pin, I2C - i2c = I2C(0, scl=Pin(46), sda=Pin(45), freq=400000) + i2c = I2C(0) pin_reset_i2c = Pin(9, Pin.OUT) pin_reset_i2c.on() - i2c.writeto(0x77, bytes([(0x1 << 7)])) usb_in = fusb302(i2c) usb_in.setup_device() usb_in.determine_input_current_limit() diff --git a/modules/main.py b/modules/main.py index 8ac719a..0f78aaf 100644 --- a/modules/main.py +++ b/modules/main.py @@ -9,6 +9,8 @@ from frontboards.twentyfour import TwentyTwentyFour +import power_handler + # Start front-board interface scheduler.start_app(TwentyTwentyFour()) diff --git a/modules/power_handler.py b/modules/power_handler.py new file mode 100644 index 0000000..d6b2ce9 --- /dev/null +++ b/modules/power_handler.py @@ -0,0 +1,38 @@ + +from system.eventbus import eventbus +from system.power import events +import power_event + +class PowerEventHandler: + def ChargeEventHandler(self): + eventbus.emit(events.RequestChargeEvent(events.PowerEvent("Charge Cycle change"))) + + def FaultEventHandler(self): + eventbus.emit(events.RequestChargeFaultEvent(events.PowerEvent("Charge Fault"))) + + def HostAttachHandler(self): + eventbus.emit(events.RequestHostAttachEvent(events.PowerEvent("Host attatched"))) + + def HostDetachHandler(self): + eventbus.emit(events.RequestHostDetachEvent(events.PowerEvent("Host Detatched"))) + + def DeviceAttachHandler(self): + eventbus.emit(events.RequestDeviceAttachEvent(events.PowerEvent("Device attatched"))) + + def DeviceDetachHandler(self): + eventbus.emit(events.RequestDeviceDetachEvent(events.PowerEvent("Device detatched"))) + + def LanyardAttachHandler(self): + eventbus.emit(events.RequestLanyardAttachEvent(events.PowerEvent("Lanyard attatched"))) + + def LanyardDetachHandler(self): + eventbus.emit(events.RequestLanyardDetachEvent(events.PowerEvent("Lanyard Detatched"))) + +power_event.set_charge_cb( PowerEventHandler.ChargeEventHandler ) +power_event.set_device_attach_cb( PowerEventHandler.DeviceAttachHandler ) +power_event.set_device_detach_cb( PowerEventHandler.DeviceDetachHandler ) +power_event.set_fault_cb( PowerEventHandler.FaultEventHandler ) +power_event.set_host_attach_cb( PowerEventHandler.HostAttachHandler ) +power_event.set_host_detach_cb( PowerEventHandler.HostDetachHandler ) +power_event.set_lanyard_attach_cb( PowerEventHandler.LanyardAttachHandler ) +power_event.set_lanyard_detach_cb( PowerEventHandler.LanyardDetachHandler ) diff --git a/modules/system/power/events.py b/modules/system/power/events.py new file mode 100644 index 0000000..1ae54e8 --- /dev/null +++ b/modules/system/power/events.py @@ -0,0 +1,29 @@ + + + +class PowerEvent: + def __init__(self, EventName): + self.__str__ = EventName + +class RequestChargeEvent(PowerEvent): ... + +class RequestBatFaultEvent(PowerEvent): ... + +class RequestBoostFaultEvent(PowerEvent): ... + +class RequestChargeFaultEvent(PowerEvent): ... + +class RequestTimeoutFaultEvent(PowerEvent): ... + +class RequestHostAttachEvent(PowerEvent): ... + +class RequestHostDetachEvent(PowerEvent): ... + +class RequestDeviceAttachEvent(PowerEvent): ... + +class RequestDeviceDetachEvent(PowerEvent): ... + +class RequestLanyardAttachEvent(PowerEvent): ... + +class RequestLanyardDetachEvent(PowerEvent): ... + From 7cead6f3ff5cf9123310d97180e64ec9a9123ead Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 27 May 2024 21:24:14 +0100 Subject: [PATCH 4/7] move power event handlers --- modules/power_handler.py | 38 ------------------- modules/system/power/power_handler.py | 53 +++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 38 deletions(-) delete mode 100644 modules/power_handler.py create mode 100644 modules/system/power/power_handler.py diff --git a/modules/power_handler.py b/modules/power_handler.py deleted file mode 100644 index d6b2ce9..0000000 --- a/modules/power_handler.py +++ /dev/null @@ -1,38 +0,0 @@ - -from system.eventbus import eventbus -from system.power import events -import power_event - -class PowerEventHandler: - def ChargeEventHandler(self): - eventbus.emit(events.RequestChargeEvent(events.PowerEvent("Charge Cycle change"))) - - def FaultEventHandler(self): - eventbus.emit(events.RequestChargeFaultEvent(events.PowerEvent("Charge Fault"))) - - def HostAttachHandler(self): - eventbus.emit(events.RequestHostAttachEvent(events.PowerEvent("Host attatched"))) - - def HostDetachHandler(self): - eventbus.emit(events.RequestHostDetachEvent(events.PowerEvent("Host Detatched"))) - - def DeviceAttachHandler(self): - eventbus.emit(events.RequestDeviceAttachEvent(events.PowerEvent("Device attatched"))) - - def DeviceDetachHandler(self): - eventbus.emit(events.RequestDeviceDetachEvent(events.PowerEvent("Device detatched"))) - - def LanyardAttachHandler(self): - eventbus.emit(events.RequestLanyardAttachEvent(events.PowerEvent("Lanyard attatched"))) - - def LanyardDetachHandler(self): - eventbus.emit(events.RequestLanyardDetachEvent(events.PowerEvent("Lanyard Detatched"))) - -power_event.set_charge_cb( PowerEventHandler.ChargeEventHandler ) -power_event.set_device_attach_cb( PowerEventHandler.DeviceAttachHandler ) -power_event.set_device_detach_cb( PowerEventHandler.DeviceDetachHandler ) -power_event.set_fault_cb( PowerEventHandler.FaultEventHandler ) -power_event.set_host_attach_cb( PowerEventHandler.HostAttachHandler ) -power_event.set_host_detach_cb( PowerEventHandler.HostDetachHandler ) -power_event.set_lanyard_attach_cb( PowerEventHandler.LanyardAttachHandler ) -power_event.set_lanyard_detach_cb( PowerEventHandler.LanyardDetachHandler ) diff --git a/modules/system/power/power_handler.py b/modules/system/power/power_handler.py new file mode 100644 index 0000000..d7c02fe --- /dev/null +++ b/modules/system/power/power_handler.py @@ -0,0 +1,53 @@ +from system.eventbus import eventbus +from system.power import events +import power_event + + +class PowerEventHandler: + def ChargeEventHandler(self): + eventbus.emit( + events.RequestChargeEvent(events.PowerEvent("Charge Cycle change")) + ) + + def FaultEventHandler(self): + eventbus.emit(events.RequestChargeFaultEvent(events.PowerEvent("Charge Fault"))) + + def HostAttachHandler(self): + eventbus.emit( + events.RequestHostAttachEvent(events.PowerEvent("Host attatched")) + ) + + def HostDetachHandler(self): + eventbus.emit( + events.RequestHostDetachEvent(events.PowerEvent("Host Detatched")) + ) + + def DeviceAttachHandler(self): + eventbus.emit( + events.RequestDeviceAttachEvent(events.PowerEvent("Device attatched")) + ) + + def DeviceDetachHandler(self): + eventbus.emit( + events.RequestDeviceDetachEvent(events.PowerEvent("Device detatched")) + ) + + def LanyardAttachHandler(self): + eventbus.emit( + events.RequestLanyardAttachEvent(events.PowerEvent("Lanyard attatched")) + ) + + def LanyardDetachHandler(self): + eventbus.emit( + events.RequestLanyardDetachEvent(events.PowerEvent("Lanyard Detatched")) + ) + + +power_event.set_charge_cb(PowerEventHandler.ChargeEventHandler) +power_event.set_device_attach_cb(PowerEventHandler.DeviceAttachHandler) +power_event.set_device_detach_cb(PowerEventHandler.DeviceDetachHandler) +power_event.set_fault_cb(PowerEventHandler.FaultEventHandler) +power_event.set_host_attach_cb(PowerEventHandler.HostAttachHandler) +power_event.set_host_detach_cb(PowerEventHandler.HostDetachHandler) +power_event.set_lanyard_attach_cb(PowerEventHandler.LanyardAttachHandler) +power_event.set_lanyard_detach_cb(PowerEventHandler.LanyardDetachHandler) From 755a9a1e988ee0b39816ee028b9dcacaa022ccf9 Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 27 May 2024 21:25:26 +0100 Subject: [PATCH 5/7] rename power handler --- modules/system/power/{power_handler.py => handler.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename modules/system/power/{power_handler.py => handler.py} (100%) diff --git a/modules/system/power/power_handler.py b/modules/system/power/handler.py similarity index 100% rename from modules/system/power/power_handler.py rename to modules/system/power/handler.py From a81de6a21de6ae21376123c823a780f0cf9ffc88 Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 27 May 2024 21:49:16 +0100 Subject: [PATCH 6/7] refactor refactor after setting up ruff --- modules/main.py | 5 +++-- modules/system/power/handler.py | 23 ++++++++++++----------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/modules/main.py b/modules/main.py index 0f78aaf..d0c9958 100644 --- a/modules/main.py +++ b/modules/main.py @@ -6,10 +6,10 @@ from system.patterndisplay.app import PatternDisplay from system.notification.app import NotificationService from system.launcher.app import Launcher +from system.power.handler import PowerEventHandler from frontboards.twentyfour import TwentyTwentyFour -import power_handler # Start front-board interface scheduler.start_app(TwentyTwentyFour()) @@ -26,7 +26,8 @@ # Start notification handler scheduler.start_app(NotificationService(), always_on_top=True) +PowerEventHandler.RegisterDefaultCallbacks(PowerEventHandler) + Partition.mark_app_valid_cancel_rollback() scheduler.run_forever() - diff --git a/modules/system/power/handler.py b/modules/system/power/handler.py index d7c02fe..d93871c 100644 --- a/modules/system/power/handler.py +++ b/modules/system/power/handler.py @@ -1,9 +1,20 @@ from system.eventbus import eventbus from system.power import events -import power_event + +import power_event as pe class PowerEventHandler: + def RegisterDefaultCallbacks(self): + pe.set_charge_cb(self.ChargeEventHandler) + pe.set_device_attach_cb(self.DeviceAttachHandler) + pe.set_device_detach_cb(self.DeviceDetachHandler) + pe.set_fault_cb(self.FaultEventHandler) + pe.set_host_attach_cb(self.HostAttachHandler) + pe.set_host_detach_cb(self.HostDetachHandler) + pe.set_lanyard_attach_cb(self.LanyardAttachHandler) + pe.set_lanyard_detach_cb(self.LanyardDetachHandler) + def ChargeEventHandler(self): eventbus.emit( events.RequestChargeEvent(events.PowerEvent("Charge Cycle change")) @@ -41,13 +52,3 @@ def LanyardDetachHandler(self): eventbus.emit( events.RequestLanyardDetachEvent(events.PowerEvent("Lanyard Detatched")) ) - - -power_event.set_charge_cb(PowerEventHandler.ChargeEventHandler) -power_event.set_device_attach_cb(PowerEventHandler.DeviceAttachHandler) -power_event.set_device_detach_cb(PowerEventHandler.DeviceDetachHandler) -power_event.set_fault_cb(PowerEventHandler.FaultEventHandler) -power_event.set_host_attach_cb(PowerEventHandler.HostAttachHandler) -power_event.set_host_detach_cb(PowerEventHandler.HostDetachHandler) -power_event.set_lanyard_attach_cb(PowerEventHandler.LanyardAttachHandler) -power_event.set_lanyard_detach_cb(PowerEventHandler.LanyardDetachHandler) From b477999b387ac9ade625125174b70f392bf39d15 Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 29 May 2024 19:57:36 +0100 Subject: [PATCH 7/7] keep reading interrupt until it clears --- drivers/tildagon_power/tildagon_power.c | 125 ++++++++++++------------ 1 file changed, 64 insertions(+), 61 deletions(-) diff --git a/drivers/tildagon_power/tildagon_power.c b/drivers/tildagon_power/tildagon_power.c index 14f17f4..c5c0cf2 100644 --- a/drivers/tildagon_power/tildagon_power.c +++ b/drivers/tildagon_power/tildagon_power.c @@ -454,79 +454,82 @@ void generate_events( void ) { push_event( MP_POWER_EVENT_FAULT ); } - if ( gpio_get_level( GPIO_NUM_10 ) == 0 ) + while ( gpio_get_level( GPIO_NUM_10 ) == 0 ) { - uint16_t interruptab = fusb_get_interruptab( &usb_in.fusb ); - uint8_t interrupt = fusb_get_interrupt( &usb_in.fusb ); - fusb_get_status( &usb_in.fusb ); - fusb_get_statusa( &usb_in.fusb ); - if( interruptab & FUSB_TOGGLE_I_MASK ) + if ( gpio_get_level( GPIO_NUM_10 ) == 0 ) { - const event_t event = DEVICE_TOGGLE; - xQueueSendToBack(event_queue, (void*)&event , (TickType_t)0 ); - } - if( ( interrupt & FUSB_VBUSOK_I_MASK ) && ( usb_in.fusb.status & FUSB_STATUS_VBUSOK_MASK ) ) - { - const event_t event = DEVICE_ATTACH; - xQueueSendToBack(event_queue, (void*)&event , (TickType_t)0 ); - } - else if ( ( interrupt & FUSB_VBUSOK_I_MASK ) && ( ( usb_in.fusb.status & FUSB_STATUS_VBUSOK_MASK ) == 0 ) ) - { - const event_t event = DEVICE_DETACH; - xQueueSendToBack(event_queue, (void*)&event , (TickType_t)0 ); - } - if ( device_pd_state > NOT_STARTED ) - { - if ( interruptab & FUSB_GD_CRC_I_MASK ) + uint16_t interruptab = fusb_get_interruptab( &usb_in.fusb ); + uint8_t interrupt = fusb_get_interrupt( &usb_in.fusb ); + fusb_get_status( &usb_in.fusb ); + fusb_get_statusa( &usb_in.fusb ); + if( interruptab & FUSB_TOGGLE_I_MASK ) { - const event_t event = DEVICE_GOODCRCSENT; + const event_t event = DEVICE_TOGGLE; xQueueSendToBack(event_queue, (void*)&event , (TickType_t)0 ); } - if ( interruptab & FUSB_TXSENT_I_MASK ) + if( ( interrupt & FUSB_VBUSOK_I_MASK ) && ( usb_in.fusb.status & FUSB_STATUS_VBUSOK_MASK ) ) { - usb_in.pd.msg_id++; + const event_t event = DEVICE_ATTACH; + xQueueSendToBack(event_queue, (void*)&event , (TickType_t)0 ); + } + else if ( ( interrupt & FUSB_VBUSOK_I_MASK ) && ( ( usb_in.fusb.status & FUSB_STATUS_VBUSOK_MASK ) == 0 ) ) + { + const event_t event = DEVICE_DETACH; + xQueueSendToBack(event_queue, (void*)&event , (TickType_t)0 ); + } + if ( device_pd_state > NOT_STARTED ) + { + if ( interruptab & FUSB_GD_CRC_I_MASK ) + { + const event_t event = DEVICE_GOODCRCSENT; + xQueueSendToBack(event_queue, (void*)&event , (TickType_t)0 ); + } + if ( interruptab & FUSB_TXSENT_I_MASK ) + { + usb_in.pd.msg_id++; + } + } + else if ( interrupt & FUSB_BC_LVL_I_MASK ) + { + const event_t event = DEVICE_BC_LEVEL; + xQueueSendToBack(event_queue, (void*)&event , (TickType_t)0 ); } - } - else if ( interrupt & FUSB_BC_LVL_I_MASK ) - { - const event_t event = DEVICE_BC_LEVEL; - xQueueSendToBack(event_queue, (void*)&event , (TickType_t)0 ); } - } - if ( gpio_get_level( GPIO_NUM_10 ) == 0 ) - { - uint16_t interruptab = fusb_get_interruptab( &usb_out.fusb ); - uint8_t interrupt = fusb_get_interrupt( &usb_out.fusb ); - fusb_get_status( &usb_out.fusb ); - fusb_get_statusa( &usb_out.fusb ); - - if ( interruptab & FUSB_TOGGLE_I_MASK ) - { - const event_t event = HOST_TOGGLE; - xQueueSendToBack(event_queue, (void*)&event, (TickType_t)0 ); - } - if ( ( ( ( usb_out.fusb.status & FUSB_STATUS_COMP_MASK ) == 0U ) && ( interrupt & FUSB_CMPCHG_I_MASK ) ) - || ( ( interrupt & FUSB_BC_LVL_I_MASK ) && ( ( usb_out.fusb.status & FUSB_STATUS_BCLVL_MASK ) < 3 ) ) ) - { - const event_t event = HOST_ATTACH; - xQueueSendToBack(event_queue, (void*)&event, (TickType_t)0 ); - } - else if ( ( usb_out.fusb.status & FUSB_STATUS_COMP_MASK ) && ( interrupt & FUSB_CMPCHG_I_MASK ) ) - { - const event_t event = HOST_DETACH; - xQueueSendToBack(event_queue, (void*)&event , (TickType_t)0 ); - } - if( host_pd_state > NOT_STARTED ) + if ( gpio_get_level( GPIO_NUM_10 ) == 0 ) { - if ( interruptab & FUSB_GD_CRC_I_MASK ) + uint16_t interruptab = fusb_get_interruptab( &usb_out.fusb ); + uint8_t interrupt = fusb_get_interrupt( &usb_out.fusb ); + fusb_get_status( &usb_out.fusb ); + fusb_get_statusa( &usb_out.fusb ); + + if ( interruptab & FUSB_TOGGLE_I_MASK ) + { + const event_t event = HOST_TOGGLE; + xQueueSendToBack(event_queue, (void*)&event, (TickType_t)0 ); + } + if ( ( ( ( usb_out.fusb.status & FUSB_STATUS_COMP_MASK ) == 0U ) && ( interrupt & FUSB_CMPCHG_I_MASK ) ) + || ( ( interrupt & FUSB_BC_LVL_I_MASK ) && ( ( usb_out.fusb.status & FUSB_STATUS_BCLVL_MASK ) < 3 ) ) ) { - const event_t event = HOST_GOODCRCSENT; - xQueueSendToBack(event_queue, (void*)&event , (TickType_t)0 ); - } - if ( interruptab & FUSB_TXSENT_I_MASK ) + const event_t event = HOST_ATTACH; + xQueueSendToBack(event_queue, (void*)&event, (TickType_t)0 ); + } + else if ( ( usb_out.fusb.status & FUSB_STATUS_COMP_MASK ) && ( interrupt & FUSB_CMPCHG_I_MASK ) ) { - usb_out.pd.msg_id++; + const event_t event = HOST_DETACH; + xQueueSendToBack(event_queue, (void*)&event , (TickType_t)0 ); + } + if( host_pd_state > NOT_STARTED ) + { + if ( interruptab & FUSB_GD_CRC_I_MASK ) + { + const event_t event = HOST_GOODCRCSENT; + xQueueSendToBack(event_queue, (void*)&event , (TickType_t)0 ); + } + if ( interruptab & FUSB_TXSENT_I_MASK ) + { + usb_out.pd.msg_id++; + } } } }