From 40439c1800528e9232a5b3fe59a23c6c296828af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=8D=E5=90=83=E6=B2=B9=E7=82=B8=E9=B8=A1?= <1425962791@qq.com> Date: Tue, 14 Oct 2025 16:24:47 +0800 Subject: [PATCH] add pd ufp --- Power_Pico/BSP/PD/FUSB302_UFP.c | 596 +++++++++++++++++ Power_Pico/BSP/PD/FUSB302_UFP.h | 76 +++ Power_Pico/BSP/PD/PD_UFP_Protocol.c | 599 ++++++++++++++++++ Power_Pico/BSP/PD/PD_UFP_Protocol.h | 150 +++++ Power_Pico/BSP/PD/fusb302_dev.c | 185 ++++++ Power_Pico/BSP/PD/fusb302_dev.h | 84 +++ Power_Pico/User/Tasks/Inc/user_DataTask.h | 16 - Power_Pico/User/Tasks/Inc/user_PDUFPTask.h | 16 + Power_Pico/User/Tasks/Src/user_DataTask.c | 34 - .../User/Tasks/Src/user_HardwareInitTask.c | 10 +- Power_Pico/User/Tasks/Src/user_TasksInit.c | 12 +- 11 files changed, 1717 insertions(+), 61 deletions(-) create mode 100644 Power_Pico/BSP/PD/FUSB302_UFP.c create mode 100644 Power_Pico/BSP/PD/FUSB302_UFP.h create mode 100644 Power_Pico/BSP/PD/PD_UFP_Protocol.c create mode 100644 Power_Pico/BSP/PD/PD_UFP_Protocol.h create mode 100644 Power_Pico/BSP/PD/fusb302_dev.c create mode 100644 Power_Pico/BSP/PD/fusb302_dev.h delete mode 100644 Power_Pico/User/Tasks/Inc/user_DataTask.h create mode 100644 Power_Pico/User/Tasks/Inc/user_PDUFPTask.h delete mode 100644 Power_Pico/User/Tasks/Src/user_DataTask.c diff --git a/Power_Pico/BSP/PD/FUSB302_UFP.c b/Power_Pico/BSP/PD/FUSB302_UFP.c new file mode 100644 index 0000000..05e64f4 --- /dev/null +++ b/Power_Pico/BSP/PD/FUSB302_UFP.c @@ -0,0 +1,596 @@ + +/** + * FUSB302_UFP.c + * + * Updated on: Jan 4, 2021 + * Author: Ryan Ma + * + * Minimalist USB PD implement with only UFP(device) functionality + * Requires only stdint.h and string.h + * No use of bit-field for better cross-platform compatibility + * + * FUSB302 can support PD3.0 with limitations and workarounds + * - Do not have enough FIFO for unchunked message, use chunked message instead + * - VBUS sense low threshold at 4V, disable vbus_sense if request PPS below 4V + * + */ + +#include +#include "FUSB302_UFP.h" + +/* Switches0 : 02h */ +#define PU_EN2 (0x01 << 7) +#define PU_EN1 (0x01 << 6) +#define VCONN_CC2 (0x01 << 5) +#define VCONN_CC1 (0x01 << 4) +#define MEAS_CC2 (0x01 << 3) +#define MEAS_CC1 (0x01 << 2) +#define PDWN2 (0x01 << 1) +#define PDWN1 (0x01 << 0) + +/* Switches1 : 03h */ +#define POWERROLE (0x01 << 7) +#define SPECREV1 (0x01 << 6) +#define SPECREV0 (0x01 << 5) +#define DATAROLE (0x01 << 4) +#define AUTO_CRC (0x01 << 2) +#define TXCC2 (0x01 << 1) +#define TXCC1 (0x01 << 0) + +/* Measure : 04h */ +#define MEAS_VBUS (0x01 << 6) + +/* Control0 : 06h */ +#define TX_FLUSH (0x01 << 6) +#define INT_MASK (0x01 << 5) +#define HOST_CUR_MASK (0x03 << 2) +#define HOST_CUR_3A0 (0x03 << 2) +#define HOST_CUR_1A5 (0x02 << 2) +#define HOST_CUR_USB (0x01 << 2) +#define AUTO_PRE (0x01 << 1) +#define TX_START (0x01 << 0) + +/* Control1 : 07h */ +#define ENSOP2DB (0x01 << 6) +#define ENSOP1DB (0x01 << 5) +#define BIST_MODE2 (0x01 << 4) +#define RX_FLUSH (0x01 << 2) +#define ENSOP2 (0x01 << 1) +#define ENSOP1 (0x01 << 0) + +/* Control2 : 08h */ +#define WAKE_EN (0x01 << 3) +#define MODE_MASK (0x03 << 1) +#define MODE_DFP (0x03 << 1) +#define MODE_UFP (0x02 << 1) +#define MODE_DRP (0x01 << 1) +#define TOGGLE (0x01 << 0) + +/* Control3 : 09h */ +#define SEND_HARDRESET (0x01 << 6) +#define BIST_TMODE (0x01 << 5) /* on FUSB302B only */ +#define AUTO_HARDRESET (0x01 << 4) +#define AUTO_SOFTRESET (0x01 << 3) +#define N_RETRIES_MASK (0x03 << 1) +#define N_RETRIES(n) ((n) << 1) +#define AUTO_RETRY (0x01 << 0) + +/* Mask : 0Ah */ +#define M_VBUSOK (0x01 << 7) +#define M_ACTIVITY (0x01 << 6) +#define M_COMP_CHNG (0x01 << 5) +#define M_CRC_CHK (0x01 << 4) +#define M_ALERT (0x01 << 3) +#define M_WAKE (0x01 << 2) +#define M_COLLISION (0x01 << 1) +#define M_BC_LVL (0x01 << 0) + +/* Power : 0Bh */ +#define PWR_INT_OSC (0x01 << 3) /* Enable internal oscillator */ +#define PWR_MEASURE (0x01 << 2) /* Measure block powered */ +#define PWR_RECEIVER (0x01 << 1) /* Receiver powered and current reference for Measure block */ +#define PWR_BANDGAP (0x01 << 0) /* Bandgap and wake circuitry */ + +/* Reset : 0Ch */ +#define PD_RESET (0x01 << 1) +#define SW_RES (0x01 << 0) + +/* Maska : 0Eh */ +#define M_OCP_TEMP (0x01 << 7) +#define M_TOGDONE (0x01 << 6) +#define M_SOFTFAIL (0x01 << 5) +#define M_RETRYFAIL (0x01 << 4) +#define M_HARDSENT (0x01 << 3) +#define M_TXSENT (0x01 << 2) +#define M_SOFTRST (0x01 << 1) +#define M_HARDRST (0x01 << 0) + +/* Maskb : 0Fh */ +#define M_GCRCSENT (0x01 << 0) + +/* Status0a : 3Ch */ +#define SOFTFAIL (0x01 << 5) +#define RETRYFAIL (0x01 << 4) +#define POWER3_2 (0x01 << 2) +#define SOFTRST (0x01 << 1) +#define HARDRST (0x01 << 0) + +/* Status1a : 3Dh */ +#define TOGSS_MASK (0x07 << 3) +#define TOGSS_RUNNING (0x00 << 3) +#define TOGSS_SRC1 (0x01 << 3) +#define TOGSS_SRC2 (0x02 << 3) +#define TOGSS_SNK1 (0x05 << 3) +#define TOGSS_SNK2 (0x06 << 3) +#define TOGSS_AUDIOA (0x07 << 3) +#define RXSOP2DB (0x01 << 2) +#define RXSOP1DB (0x01 << 1) +#define RXSOP (0x01 << 0) + +/* Interrupta : 3Eh */ +#define I_OCP_TEMP (0x01 << 7) +#define I_TOGDONE (0x01 << 6) +#define I_SOFTFAIL (0x01 << 5) +#define I_RETRYFAIL (0x01 << 4) +#define I_HARDSENT (0x01 << 3) +#define I_TXSENT (0x01 << 2) +#define I_SOFTRST (0x01 << 1) +#define I_HARDRST (0x01 << 0) + +/* Interruptb : 3Fh */ +#define I_GCRCSENT (0x01 << 0) + +/* Status0 : 40h */ +#define VBUSOK (0x01 << 7) +#define ACTIVITY (0x01 << 6) +#define COMP (0x01 << 5) +#define CRC_CHK (0x01 << 4) +#define ALERT (0x01 << 3) +#define WAKE (0x01 << 2) +#define BC_LVL_MASK (0x03 << 0) +#define BC_LVL_LT200 (0x00 << 0) +#define BC_LVL_200_660 (0x01 << 0) +#define BC_LVL_660_1230 (0x02 << 0) +#define BC_LVL_GT1230 (0x03 << 0) + +/* Status1 : 41h */ +#define RXSOP2 (0x01 << 7) +#define RXSOP1 (0x01 << 6) +#define RX_EMPTY (0x01 << 5) +#define RX_FULL (0x01 << 4) +#define TX_EMPTY (0x01 << 3) +#define TX_FULL (0x01 << 2) +#define OVRTEMP (0x01 << 1) +#define OCP (0x01 << 0) + +/* Interrupt : 42h */ +#define I_VBUSOK (0x01 << 7) +#define I_ACTIVITY (0x01 << 6) +#define I_COMP_CHNG (0x01 << 5) +#define I_CRC_CHK (0x01 << 4) +#define I_ALERT (0x01 << 3) +#define I_WAKE (0x01 << 2) +#define I_COLLISION (0x01 << 1) +#define I_BC_LVL (0x01 << 0) + +#define ADDRESS_DEVICE_ID 0x01 +#define ADDRESS_SWITCHES0 0x02 +#define ADDRESS_SWITCHES1 0x03 +#define ADDRESS_MEASURE 0x04 +#define ADDRESS_SLICE 0x05 +#define ADDRESS_CONTROL0 0x06 +#define ADDRESS_CONTROL1 0x07 +#define ADDRESS_CONTROL2 0x08 +#define ADDRESS_CONTROL3 0x09 +#define ADDRESS_MASK 0x0A +#define ADDRESS_POWER 0x0B +#define ADDRESS_RESET 0x0C +#define ADDRESS_MASKA 0x0E +#define ADDRESS_MASKB 0x0F +#define ADDRESS_STATUS0A 0x3C +#define ADDRESS_STATUS1A 0x3D +#define ADDRESS_INTERRUPTA 0x3E +#define ADDRESS_INTERRUPTB 0x3F +#define ADDRESS_STATUS0 0x40 +#define ADDRESS_STATUS1 0x41 +#define ADDRESS_INTERRUPT 0x42 +#define ADDRESS_FIFOS 0x43 + +#define REG_DEVICE_ID dev->reg_control[ADDRESS_DEVICE_ID - ADDRESS_DEVICE_ID] +#define REG_SWITCHES0 dev->reg_control[ADDRESS_SWITCHES0 - ADDRESS_DEVICE_ID] +#define REG_SWITCHES1 dev->reg_control[ADDRESS_SWITCHES1 - ADDRESS_DEVICE_ID] +#define REG_MEASURE dev->reg_control[ADDRESS_MEASURE - ADDRESS_DEVICE_ID] +#define REG_SLICE dev->reg_control[ADDRESS_SLICE - ADDRESS_DEVICE_ID] +#define REG_CONTROL0 dev->reg_control[ADDRESS_CONTROL0 - ADDRESS_DEVICE_ID] +#define REG_CONTROL1 dev->reg_control[ADDRESS_CONTROL1 - ADDRESS_DEVICE_ID] +#define REG_CONTROL2 dev->reg_control[ADDRESS_CONTROL2 - ADDRESS_DEVICE_ID] +#define REG_CONTROL3 dev->reg_control[ADDRESS_CONTROL3 - ADDRESS_DEVICE_ID] +#define REG_MASK dev->reg_control[ADDRESS_MASK - ADDRESS_DEVICE_ID] +#define REG_POWER dev->reg_control[ADDRESS_POWER - ADDRESS_DEVICE_ID] +#define REG_RESET dev->reg_control[ADDRESS_RESET - ADDRESS_DEVICE_ID] +#define REG_MASKA dev->reg_control[ADDRESS_MASKA - ADDRESS_DEVICE_ID] +#define REG_MASKB dev->reg_control[ADDRESS_MASKB - ADDRESS_DEVICE_ID] +#define REG_STATUS0A dev->reg_status[ADDRESS_STATUS0A - ADDRESS_STATUS0A] +#define REG_STATUS1A dev->reg_status[ADDRESS_STATUS1A - ADDRESS_STATUS0A] +#define REG_INTERRUPTA dev->reg_status[ADDRESS_INTERRUPTA - ADDRESS_STATUS0A] +#define REG_INTERRUPTB dev->reg_status[ADDRESS_INTERRUPTB - ADDRESS_STATUS0A] +#define REG_STATUS0 dev->reg_status[ADDRESS_STATUS0 - ADDRESS_STATUS0A] +#define REG_STATUS1 dev->reg_status[ADDRESS_STATUS1 - ADDRESS_STATUS0A] +#define REG_INTERRUPT dev->reg_status[ADDRESS_INTERRUPT - ADDRESS_STATUS0A] + +enum FUSB302_transmit_data_tokens_t { + TX_TOKEN_TXON = 0xA1, + TX_TOKEN_SOP1 = 0x12, + TX_TOKEN_SOP2 = 0x13, + TX_TOKEN_SOP3 = 0x1B, + TX_TOKEN_RESET1 = 0x15, + TX_TOKEN_RESET2 = 0x16, + TX_TOKEN_PACKSYM = 0x80, + TX_TOKEN_JAM_CRC = 0xFF, + TX_TOKEN_EOP = 0x14, + TX_TOKEN_TXOFF = 0xFE, +}; + +enum FUSB302_state_t { + FUSB302_STATE_UNATTACHED = 0, + FUSB302_STATE_ATTACHED +}; + +#define FUSB302_ERR_MSG(s) s + +#define REG_READ(addr, data, count) do { \ + if (reg_read(dev, addr, data, count) != FUSB302_SUCCESS) { return FUSB302_ERR_READ_DEVICE; } \ +} while(0) + +#define REG_WRITE(addr, data, count) do { \ + if (reg_write(dev, addr, data, count) != FUSB302_SUCCESS) { return FUSB302_ERR_WRITE_DEVICE; } \ +} while(0) + +static inline FUSB302_ret_t reg_read(FUSB302_dev_t *dev, uint8_t address, uint8_t *data, uint8_t count) +{ + FUSB302_ret_t ret = dev->i2c_read(dev->i2c_address, address, data, count); + if (ret != FUSB302_SUCCESS) { + dev->err_msg = FUSB302_ERR_MSG("Fail to read register"); + } + return ret; +} + +static inline FUSB302_ret_t reg_write(FUSB302_dev_t *dev, uint8_t address, uint8_t *data, uint8_t count) +{ + FUSB302_ret_t ret = dev->i2c_write(dev->i2c_address, address, data, count); + if (ret != FUSB302_SUCCESS) { + dev->err_msg = FUSB302_ERR_MSG("Fail to write register"); + } + return ret; +} + +static FUSB302_ret_t FUSB302_read_cc_lvl(FUSB302_dev_t *dev, uint8_t * cc_value) +{ + /* 00: < 200 mV : vRa + 01: >200 mV, <660 mV : vRd-USB + 10: >660 mV, <1.23 V : vRd-1.5 + 11: >1.23 V : vRd-3.0 */ + uint8_t cc, cc_verify; + REG_READ(ADDRESS_STATUS0, ®_STATUS0, 1); + cc = REG_STATUS0 & BC_LVL_MASK; + for (uint8_t i = 0; i < 5; i++) { + REG_READ(ADDRESS_STATUS0, ®_STATUS0, 1); + cc_verify = REG_STATUS0 & BC_LVL_MASK; + if (cc != cc_verify) { + return FUSB302_BUSY; + } + } + *cc_value = cc; + return FUSB302_SUCCESS; +} + +static FUSB302_ret_t FUSB302_read_incoming_packet(FUSB302_dev_t *dev, FUSB302_event_t * events) +{ + uint8_t len, b[3]; + REG_READ(ADDRESS_FIFOS, b, 3); + dev->rx_header = ((uint16_t)b[2] << 8) | b[1]; + len = (dev->rx_header >> 12) & 0x7; + REG_READ(ADDRESS_FIFOS, dev->rx_buffer, len * 4 + 4); /* add 4 to len to read CRC out */ + + if (events) { + *events |= FUSB302_EVENT_RX_SOP; + } + return FUSB302_SUCCESS; +} + +static FUSB302_ret_t FUSB302_state_unattached(FUSB302_dev_t *dev, FUSB302_event_t * events) +{ + REG_READ(ADDRESS_STATUS0, ®_STATUS0, 1); + if (REG_STATUS0 & VBUSOK) { + /* enable internal oscillator */ + REG_POWER = PWR_BANDGAP | PWR_RECEIVER | PWR_MEASURE | PWR_INT_OSC; + REG_WRITE(ADDRESS_POWER, ®_POWER, 1); + dev->delay_ms(2); + + /* read cc1 */ + REG_SWITCHES0 = PDWN1 | PDWN2 | MEAS_CC1; + REG_SWITCHES1 = SPECREV0; + REG_MEASURE = 49; + REG_WRITE(ADDRESS_SWITCHES0, ®_SWITCHES0, 3); + dev->delay_ms(2); + while (FUSB302_read_cc_lvl(dev, &dev->cc1) != FUSB302_SUCCESS) { + dev->delay_ms(2); + } + + /* read cc2 */ + REG_SWITCHES0 = PDWN1 | PDWN2 | MEAS_CC2; + REG_WRITE(ADDRESS_SWITCHES0, ®_SWITCHES0, 1); + dev->delay_ms(2); + while (FUSB302_read_cc_lvl(dev, &dev->cc2) != FUSB302_SUCCESS) { + dev->delay_ms(2); + } + + /* clear interrupt */ + REG_READ(ADDRESS_INTERRUPTA, ®_INTERRUPTA, 2); + dev->interrupta = 0; + dev->interruptb = 0; + + /* enable tx on cc pin */ + if (dev->cc1 > 0) { + REG_SWITCHES0 = PDWN1 | PDWN2 | MEAS_CC1; + REG_SWITCHES1 = SPECREV0 | AUTO_CRC | TXCC1; + //REG_SWITCHES1 = SPECREV0 | TXCC1; + } else if (dev->cc2 > 0) { + REG_SWITCHES0 = PDWN1 | PDWN2 | MEAS_CC2; + REG_SWITCHES1 = SPECREV0 | AUTO_CRC | TXCC2; + //REG_SWITCHES1 = SPECREV0 | TXCC2; + } else { + REG_SWITCHES0 = PDWN1 | PDWN2; + REG_SWITCHES1 = SPECREV0; + } + REG_WRITE(ADDRESS_SWITCHES0, ®_SWITCHES0, 2); + + /* update state */ + dev->state = FUSB302_STATE_ATTACHED; + if (events) { + *events |= FUSB302_EVENT_ATTACHED; + } + } + return FUSB302_SUCCESS; +} + +static FUSB302_ret_t FUSB302_state_attached(FUSB302_dev_t *dev, FUSB302_event_t * events) +{ + REG_READ(ADDRESS_STATUS0A, ®_STATUS0A, 7); + dev->interrupta |= REG_INTERRUPTA; + dev->interruptb |= REG_INTERRUPTB; + if (dev->vbus_sense && ((REG_STATUS0 & VBUSOK) == 0)) { + /* reset cc pins to pull down */ + REG_SWITCHES0 = PDWN1 | PDWN2; + REG_SWITCHES1 = SPECREV0; + REG_MEASURE = 49; + REG_WRITE(ADDRESS_SWITCHES0, ®_SWITCHES0, 3); + + /* turn off internal oscillator */ + REG_POWER = PWR_BANDGAP | PWR_RECEIVER | PWR_MEASURE; + REG_WRITE(ADDRESS_POWER, ®_POWER, 1); + + /* update state */ + dev->state = FUSB302_STATE_UNATTACHED; + if (events) { + *events |= FUSB302_EVENT_DETACHED; + } + return FUSB302_SUCCESS; + } + if (REG_STATUS0A & HARDRST) { + uint8_t reg_control = PD_RESET; + REG_WRITE(ADDRESS_RESET, ®_control, 1); + return FUSB302_SUCCESS; + } + if (dev->interruptb & I_GCRCSENT) { + dev->interruptb &= ~I_GCRCSENT; + if (events) { + *events |= FUSB302_EVENT_GOOD_CRC_SENT; + } + } + if ((REG_STATUS1 & RX_EMPTY) == 0) { + if (FUSB302_read_incoming_packet(dev, events) != FUSB302_SUCCESS) { + uint8_t rx_flush = REG_CONTROL1 | RX_FLUSH; + reg_write(dev, ADDRESS_CONTROL1, &rx_flush, 1); + } + } + return FUSB302_SUCCESS; +} + +FUSB302_ret_t FUSB302_init(FUSB302_dev_t *dev) +{ + if (dev->i2c_address == 0) { + dev->err_msg = FUSB302_ERR_MSG("Invalid i2c address"); + return FUSB302_ERR_PARAM; + } + if (dev->i2c_read == 0) { + dev->err_msg = FUSB302_ERR_MSG("Invalid i2c_read function"); + return FUSB302_ERR_PARAM; + } + if (dev->i2c_write == 0) { + dev->err_msg = FUSB302_ERR_MSG("Invalid i2c_write function"); + return FUSB302_ERR_PARAM; + } + + if (reg_read(dev, ADDRESS_DEVICE_ID, &dev->reg_control[1], 1) != FUSB302_SUCCESS) { + dev->err_msg = FUSB302_ERR_MSG("Device not found"); + return FUSB302_ERR_READ_DEVICE; + } + + if ((dev->reg_control[1] & 0x80) == 0) { + dev->err_msg = FUSB302_ERR_MSG("Invalid device version"); + return FUSB302_ERR_DEVICE_ID; + } + + dev->state = FUSB302_STATE_UNATTACHED; + dev->rx_header = 0; + memset(dev->rx_buffer, 0, sizeof(dev->rx_buffer)); + + /* restore default settings */ + REG_RESET = SW_RES; + REG_WRITE(ADDRESS_RESET, ®_RESET, 1); + + /* fetch all R/W registers */ + REG_READ(ADDRESS_DEVICE_ID, ®_DEVICE_ID, 15); + + /* configure switchs and comparators */ + REG_SWITCHES0 = PDWN1 | PDWN2; + REG_SWITCHES1 = SPECREV0; + REG_MEASURE = 49; + REG_WRITE(ADDRESS_SWITCHES0, ®_SWITCHES0, 3); + + /* configure auto retries */ + REG_CONTROL3 &= ~N_RETRIES_MASK; + REG_CONTROL3 |= N_RETRIES(3) | AUTO_RETRY; + REG_WRITE(ADDRESS_CONTROL3, ®_CONTROL3, 1); + + /* configure interrupt mask */ + REG_MASK = 0xFF; + REG_MASK &= ~(M_VBUSOK | M_ACTIVITY | M_COLLISION | M_ALERT | M_CRC_CHK); + REG_WRITE(ADDRESS_MASK, ®_MASK, 1); + + /* configure interrupt maska/maskb */ + REG_MASKA = 0xFF; + REG_MASKA &= ~(M_RETRYFAIL | M_HARDSENT | M_TXSENT | M_HARDRST); + REG_WRITE(ADDRESS_MASKA, ®_MASKA, 1); + REG_MASKB = 0xFF; + REG_MASKB &= ~(M_GCRCSENT); + REG_WRITE(ADDRESS_MASKB, ®_MASKB, 1); + + /* enable interrupt */ + REG_CONTROL0 &= ~INT_MASK; + REG_WRITE(ADDRESS_CONTROL0, ®_CONTROL0, 1); + + /* Power on, enable VUSB detection */ + REG_POWER = PWR_BANDGAP | PWR_RECEIVER | PWR_MEASURE; + REG_WRITE(ADDRESS_POWER, ®_POWER, 1); + + dev->vbus_sense = 1; + dev->err_msg = FUSB302_ERR_MSG(""); + return FUSB302_SUCCESS; +} + +FUSB302_ret_t FUSB302_pd_reset(FUSB302_dev_t *dev) +{ + uint8_t reg = PD_RESET; + REG_WRITE(ADDRESS_RESET, ®, 1); + return FUSB302_SUCCESS; +} + +FUSB302_ret_t FUSB302_pdwn_cc(FUSB302_dev_t *dev, uint8_t enable) +{ + REG_SWITCHES0 = enable ? (PDWN1 | PDWN2) : 0; + REG_WRITE(ADDRESS_SWITCHES0, ®_SWITCHES0, 1); + return FUSB302_SUCCESS; +} + +FUSB302_ret_t FUSB302_set_vbus_sense(FUSB302_dev_t *dev, uint8_t enable) +{ + if (dev->vbus_sense != enable) { + if (enable) { + REG_MASK &= ~M_VBUSOK; /* enable VBUSOK interrupt */ + } else { + REG_MASK |= M_VBUSOK; /* disable VBUSOK interrupt */ + } + REG_WRITE(ADDRESS_MASK, ®_MASK, 1); + dev->vbus_sense = enable; + } + return FUSB302_SUCCESS; +} + +FUSB302_ret_t FUSB302_get_ID(FUSB302_dev_t *dev, uint8_t * version_ID, uint8_t * revision_ID) +{ + if (dev && (REG_DEVICE_ID & 0x80)) { + if (version_ID) { + *version_ID = (REG_DEVICE_ID >> 4) & 0x7; + } + if (revision_ID) { + *revision_ID = (REG_DEVICE_ID >> 0) & 0xF; + } + return FUSB302_SUCCESS; + } + return FUSB302_ERR_PARAM; +} + +FUSB302_ret_t FUSB302_get_cc(FUSB302_dev_t *dev, uint8_t *cc1, uint8_t *cc2) +{ + if (cc1) { + *cc1 = dev->cc1; + } + if (cc2) { + *cc2 = dev->cc2; + } + return FUSB302_SUCCESS; +} + +FUSB302_ret_t FUSB302_get_vbus_level(FUSB302_dev_t *dev, uint8_t *vbus) +{ + uint8_t reg_control; + REG_READ(ADDRESS_STATUS0, ®_control, 1); + *vbus = reg_control & VBUSOK ? 1 : 0; + return FUSB302_SUCCESS; +} + +FUSB302_ret_t FUSB302_get_message(FUSB302_dev_t *dev, uint16_t * header, uint32_t * data) +{ + if (header) { + *header = dev->rx_header; + } + if (data) { + uint8_t len = (dev->rx_header >> 12) & 0x7; + memcpy(data, dev->rx_buffer, len * 4); + } + return FUSB302_SUCCESS; +} + +FUSB302_ret_t FUSB302_tx_sop(FUSB302_dev_t *dev, uint16_t header, const uint32_t *data) +{ + uint8_t buf[40]; + uint8_t * pbuf = buf; + uint8_t obj_count = ((header >> 12) & 7); + *pbuf++ = (uint8_t)TX_TOKEN_SOP1; + *pbuf++ = (uint8_t)TX_TOKEN_SOP1; + *pbuf++ = (uint8_t)TX_TOKEN_SOP1; + *pbuf++ = (uint8_t)TX_TOKEN_SOP2; + *pbuf++ = (uint8_t)TX_TOKEN_PACKSYM | ((obj_count << 2) + 2); + *pbuf++ = header & 0xFF; header >>= 8; + *pbuf++ = header & 0xFF; + for (uint8_t i = 0; i < obj_count; i++) { + uint32_t d = *data++; + *pbuf++ = d & 0xFF; d >>= 8; + *pbuf++ = d & 0xFF; d >>= 8; + *pbuf++ = d & 0xFF; d >>= 8; + *pbuf++ = d & 0xFF; + } + *pbuf++ = (uint8_t)TX_TOKEN_JAM_CRC; + *pbuf++ = (uint8_t)TX_TOKEN_EOP; + *pbuf++ = (uint8_t)TX_TOKEN_TXOFF; + *pbuf++ = (uint8_t)TX_TOKEN_TXON; + REG_WRITE(ADDRESS_FIFOS, buf, pbuf - buf); + dev->delay_ms(2); + return FUSB302_SUCCESS; +} + +FUSB302_ret_t FUSB302_tx_hard_reset(FUSB302_dev_t *dev) +{ + uint8_t reg_control = REG_CONTROL3; + reg_control |= SEND_HARDRESET; + REG_WRITE(ADDRESS_CONTROL3, ®_control, 1); + dev->delay_ms(6); + reg_control = PD_RESET; + REG_WRITE(ADDRESS_RESET, ®_control, 1); + return FUSB302_SUCCESS; +} + +FUSB302_ret_t FUSB302_alert(FUSB302_dev_t *dev, FUSB302_event_t * events) +{ + FUSB302_ret_t (* const handler[]) (FUSB302_dev_t *, FUSB302_event_t *) = { + FUSB302_state_unattached, + FUSB302_state_attached + }; + if (dev->state < sizeof(handler) / sizeof(handler[0])) { + return handler[dev->state](dev, events); + } + dev->state = FUSB302_STATE_UNATTACHED; + return FUSB302_SUCCESS; +} diff --git a/Power_Pico/BSP/PD/FUSB302_UFP.h b/Power_Pico/BSP/PD/FUSB302_UFP.h new file mode 100644 index 0000000..77beafb --- /dev/null +++ b/Power_Pico/BSP/PD/FUSB302_UFP.h @@ -0,0 +1,76 @@ + +/** + * FUSB302_UFP.h + * + * Updated on: Jan 4, 2021 + * Author: Ryan Ma + * + * Minimalist USB PD implement with only UFP(device) functionality + * Requires only stdint.h and string.h + * No use of bit-field for better cross-platform compatibility + * + * FUSB302 can support PD3.0 with limitations and workarounds + * - Do not have enough FIFO for unchunked message, use chunked message instead + * - VBUS sense low threshold at 4V, disable vbus_sense if request PPS below 4V + * + */ + +#ifndef FUSB302_UFP_H +#define FUSB302_UFP_H + +#include + +enum { + FUSB302_SUCCESS = 0, + FUSB302_BUSY = (1 << 0), + FUSB302_ERR_PARAM = (1 << 1), + FUSB302_ERR_DEVICE_ID = (1 << 2), + FUSB302_ERR_READ_DEVICE = (1 << 3), + FUSB302_ERR_WRITE_DEVICE = (1 << 4) +}; +typedef uint8_t FUSB302_ret_t; + +#define FUSB302_EVENT_ATTACHED (1 << 0) +#define FUSB302_EVENT_DETACHED (1 << 1) +#define FUSB302_EVENT_RX_SOP (1 << 2) +#define FUSB302_EVENT_GOOD_CRC_SENT (1 << 3) +typedef uint8_t FUSB302_event_t; + +typedef struct { + /* setup by user */ + uint8_t i2c_address; + FUSB302_ret_t (*i2c_read)(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data, uint8_t count); + FUSB302_ret_t (*i2c_write)(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data, uint8_t count); + FUSB302_ret_t (*delay_ms)(uint32_t t); + + /* used by this library */ + const char * err_msg; + uint16_t rx_header; + uint8_t rx_buffer[32]; + uint8_t reg_control[15]; + uint8_t reg_status[7]; + + uint8_t interrupta; + uint8_t interruptb; + uint8_t cc1; + uint8_t cc2; + uint8_t state; + uint8_t vbus_sense; +} FUSB302_dev_t; + +static inline const char * FUSB302_get_last_err_msg(FUSB302_dev_t *dev) { return dev->err_msg; } + +FUSB302_ret_t FUSB302_init (FUSB302_dev_t *dev); +FUSB302_ret_t FUSB302_pd_reset (FUSB302_dev_t *dev); +FUSB302_ret_t FUSB302_pdwn_cc (FUSB302_dev_t *dev, uint8_t enable); +FUSB302_ret_t FUSB302_set_vbus_sense (FUSB302_dev_t *dev, uint8_t enable); +FUSB302_ret_t FUSB302_get_ID (FUSB302_dev_t *dev, uint8_t *version_ID, uint8_t *revision_ID); +FUSB302_ret_t FUSB302_get_cc (FUSB302_dev_t *dev, uint8_t *cc1, uint8_t *cc2); +FUSB302_ret_t FUSB302_get_vbus_level (FUSB302_dev_t *dev, uint8_t *vbus); +FUSB302_ret_t FUSB302_get_message (FUSB302_dev_t *dev, uint16_t *header, uint32_t *data); +FUSB302_ret_t FUSB302_tx_sop (FUSB302_dev_t *dev, uint16_t header, const uint32_t *data); +FUSB302_ret_t FUSB302_tx_hard_reset (FUSB302_dev_t *dev); +FUSB302_ret_t FUSB302_alert (FUSB302_dev_t *dev, FUSB302_event_t *events); + +#endif /* FUSB302_H */ + diff --git a/Power_Pico/BSP/PD/PD_UFP_Protocol.c b/Power_Pico/BSP/PD/PD_UFP_Protocol.c new file mode 100644 index 0000000..aac1e9e --- /dev/null +++ b/Power_Pico/BSP/PD/PD_UFP_Protocol.c @@ -0,0 +1,599 @@ + +/** + * PD_UFP_Protocol.c + * + * Updated on: Jan 25, 2021 + * Author: Ryan Ma + * + * Minimalist USB PD implement with only UFP(device) sink only functionality + * Requires PD PHY to do automatic GoodCRC response on valid SOP messages. + * Requires only stdint.h, stdbool.h and string.h + * No use of bit-field for better cross-platform compatibility + * + * Support PD3.0 PPS + * Do not support extended message. Not necessary for PD trigger and PPS. + * + * Reference: USB_PD_R2_0 V1.3 - 20170112 + * USB_PD_R3_0 V2.0 20190829 + ECNs 2020-12-10 + * - Chapter 6. Protocol Layer + * + */ + +#include +#include "PD_UFP_Protocol.h" + +#define PD_SPECIFICATION_REVISION 0x2 + +#define PD_CONTROL_MSG_TYPE_ACCEPT 0x3 +#define PD_CONTROL_MSG_TYPE_REJECT 0x4 +#define PD_CONTROL_MSG_TYPE_GET_SRC_CAP 0x7 +#define PD_CONTROL_MSG_TYPE_NOT_SUPPORT 0x10 +#define PD_CONTROL_MSG_TYPE_GET_PPS_STATUS 0x14 + +#define PD_DATA_MSG_TYPE_REQUEST 0x2 +#define PD_DATA_MSG_TYPE_SINK_CAP 0x4 +#define PD_DATA_MSG_TYPE_VENDOR_DEFINED 0xF + +#define PD_EXT_MSG_TYPE_SINK_CAP_EXT 0xF + +typedef struct { + uint8_t type; + uint8_t spec_rev; + uint8_t id; + uint8_t num_of_obj; +} PD_msg_header_info_t; + +typedef struct { + uint16_t limit; + uint8_t use_voltage; + uint8_t use_current; +} PD_power_option_setting_t; + +struct PD_msg_state_t { + const char * name; + void (*handler)(PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events); + bool (*responder)(PD_protocol_t * p, uint16_t * header, uint32_t * obj); +}; + +/* Optimize RAM usage on AVR MCU by allocate const in PROGMEM */ +#if defined(__AVR__) +#include +#define SET_MSG_STAGE(d, s) do { static struct PD_msg_state_t m; memcpy_P(&m, s, sizeof(struct PD_msg_state_t)); d = &m; } while (0) +#define SET_MSG_NAME(d, s) do { static char n[16]; strncpy_P(n, s, 15); d = n; } while (0) +#define COPY_PDO(d, s) do { memcpy_P(&d, &s, 4); } while (0) +#else +#define PROGMEM +#define SET_MSG_STAGE(d, s) do { d = s; } while (0) +#define SET_MSG_NAME(d, s) do { d = s; } while (0) +#define COPY_PDO(d, s) do { d = s; } while (0) +#endif + +#define T(name) static const char str_ ## name [] PROGMEM = #name + +static void handler_good_crc (PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events); +static void handler_goto_min (PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events); +static void handler_accept (PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events); +static void handler_reject (PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events); +static void handler_ps_rdy (PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events); +static void handler_source_cap (PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events); +static void handler_BIST (PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events); +static void handler_alert (PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events); +static void handler_vender_def (PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events); +static void handler_PPS_Status (PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events); + +static bool responder_get_sink_cap (PD_protocol_t * p, uint16_t * header, uint32_t * obj); +static bool responder_reject (PD_protocol_t * p, uint16_t * header, uint32_t * obj); +static bool responder_soft_reset (PD_protocol_t * p, uint16_t * header, uint32_t * obj); +static bool responder_source_cap (PD_protocol_t * p, uint16_t * header, uint32_t * obj); +static bool responder_vender_def (PD_protocol_t * p, uint16_t * header, uint32_t * obj); +static bool responder_sink_cap_ext (PD_protocol_t * p, uint16_t * header, uint32_t * obj); +static bool responder_not_support (PD_protocol_t * p, uint16_t * header, uint32_t * obj); + +T(C0); T(GoodCRC); T(GotoMin); T(Accept); T(Reject); T(Ping); T(PS_RDY); T(Get_Src_Cap); +T(Get_Sink_Cap); T(DR_Swap); T(PR_Swap); T(VCONN_Swap); T(Wait); T(Soft_Rst); T(Dat_Rst); T(Dat_Rst_Cpt); +T(NS); T(Get_Src_Ext); T(Get_Stat); T(FR_Swap); T(Get_PPS_Stat); T(Get_CC); T(Get_Sink_Ext); T(C_R); + +static const struct PD_msg_state_t ctrl_msg_list[] PROGMEM = { + {.name = str_C0, .handler = 0, .responder = 0}, + {.name = str_GoodCRC, .handler = handler_good_crc, .responder = 0}, + {.name = str_GotoMin, .handler = handler_goto_min, .responder = 0}, + {.name = str_Accept, .handler = handler_accept, .responder = 0}, + {.name = str_Reject, .handler = handler_reject, .responder = 0}, + {.name = str_Ping, .handler = 0, .responder = 0}, + {.name = str_PS_RDY, .handler = handler_ps_rdy, .responder = 0}, + {.name = str_Get_Src_Cap, .handler = 0, .responder = responder_not_support}, + {.name = str_Get_Sink_Cap, .handler = 0, .responder = responder_get_sink_cap}, + {.name = str_DR_Swap, .handler = 0, .responder = responder_reject}, + {.name = str_PR_Swap, .handler = 0, .responder = responder_not_support}, + {.name = str_VCONN_Swap, .handler = 0, .responder = responder_reject}, + {.name = str_Wait, .handler = 0, .responder = 0}, + {.name = str_Soft_Rst, .handler = 0, .responder = responder_soft_reset}, + {.name = str_Dat_Rst, .handler = 0, .responder = 0}, + {.name = str_Dat_Rst_Cpt, .handler = 0, .responder = 0}, + + {.name = str_NS, .handler = 0, .responder = 0}, + {.name = str_Get_Src_Ext, .handler = 0, .responder = responder_not_support}, + {.name = str_Get_Stat, .handler = 0, .responder = responder_not_support}, + {.name = str_FR_Swap, .handler = 0, .responder = responder_not_support}, + {.name = str_Get_PPS_Stat, .handler = 0, .responder = responder_not_support}, + {.name = str_Get_CC, .handler = 0, .responder = responder_not_support}, + {.name = str_Get_Sink_Ext, .handler = 0, .responder = responder_sink_cap_ext}, + {.name = str_C_R, .handler = 0, .responder = responder_not_support}, +}; + +T(D0); T(Src_Cap); T(Request); T(BIST); T(Sink_Cap); T(Bat_Stat); T(Alert); T(Get_CI); +T(Enter_USB); T(D9); T(D10); T(D11); T(D12); T(D13); T(D14); T(VDM); +T(D_R); + +static const struct PD_msg_state_t data_msg_list[] PROGMEM = { + {.name = str_D0, .handler = 0, .responder = 0}, + {.name = str_Src_Cap, .handler = handler_source_cap, .responder = responder_source_cap}, + {.name = str_Request, .handler = 0, .responder = responder_not_support}, + {.name = str_BIST, .handler = handler_BIST, .responder = 0}, + {.name = str_Sink_Cap, .handler = 0, .responder = responder_not_support}, + {.name = str_Bat_Stat, .handler = 0, .responder = responder_not_support}, + {.name = str_Alert, .handler = handler_alert, .responder = 0}, + {.name = str_Get_CI, .handler = 0, .responder = responder_not_support}, + {.name = str_Enter_USB, .handler = 0, .responder = 0}, + {.name = str_D9, .handler = 0, .responder = 0}, + {.name = str_D10, .handler = 0, .responder = 0}, + {.name = str_D11, .handler = 0, .responder = 0}, + {.name = str_D12, .handler = 0, .responder = 0}, + {.name = str_D13, .handler = 0, .responder = 0}, + {.name = str_D14, .handler = 0, .responder = 0}, + {.name = str_VDM, .handler = handler_vender_def, .responder = responder_vender_def}, + + {.name = str_D_R, .handler = 0, .responder = responder_not_support}, +}; + +T(E0); T(Src_Cap_Ext); T(Status); T(Get_Bat_cap); T(Get_Bat_Stat); T(Bat_Cap); T(Get_Mfg_Info); T(Mfg_Info); +T(Sec_Request); T(Sec_Response); T(FU_request); T(FU_Response); T(PPS_Stat); T(Country_Info); T(Country_Code); T(Sink_Cap_Ext); +T(E_R); + +static const struct PD_msg_state_t ext_msg_list[] PROGMEM = { + {.name = str_E0, .handler = 0, .responder = responder_not_support}, + {.name = str_Src_Cap_Ext, .handler = 0, .responder = 0}, + {.name = str_Status, .handler = 0, .responder = 0}, + {.name = str_Get_Bat_cap, .handler = 0, .responder = responder_not_support}, + {.name = str_Get_Bat_Stat, .handler = 0, .responder = responder_not_support}, + {.name = str_Bat_Cap, .handler = 0, .responder = 0}, + {.name = str_Get_Mfg_Info, .handler = 0, .responder = responder_not_support}, + {.name = str_Mfg_Info, .handler = 0, .responder = 0}, + {.name = str_Sec_Request, .handler = 0, .responder = responder_not_support}, + {.name = str_Sec_Response, .handler = 0, .responder = 0}, + {.name = str_FU_request, .handler = 0, .responder = responder_not_support}, + {.name = str_FU_Response, .handler = 0, .responder = 0}, + {.name = str_PPS_Stat, .handler = handler_PPS_Status, .responder = 0}, + {.name = str_Country_Info, .handler = 0, .responder = 0}, + {.name = str_Country_Code, .handler = 0, .responder = 0}, + {.name = str_Sink_Cap_Ext, .handler = 0, .responder = responder_not_support}, + + {.name = str_E_R, .handler = 0, .responder = responder_not_support}, +}; + +static const PD_power_option_setting_t power_option_setting[8] = { + {.limit = 25, .use_voltage = 1, .use_current = 0}, /* PD_POWER_OPTION_MAX_5V */ + {.limit = 45, .use_voltage = 1, .use_current = 0}, /* PD_POWER_OPTION_MAX_9V */ + {.limit = 60, .use_voltage = 1, .use_current = 0}, /* PD_POWER_OPTION_MAX_12V */ + {.limit = 75, .use_voltage = 1, .use_current = 0}, /* PD_POWER_OPTION_MAX_15V */ + {.limit = 100, .use_voltage = 1, .use_current = 0}, /* PD_POWER_OPTION_MAX_20V */ + {.limit = 100, .use_voltage = 1, .use_current = 0}, /* PD_POWER_OPTION_MAX_VOLTAGE */ + {.limit = 125, .use_voltage = 0, .use_current = 1}, /* PD_POWER_OPTION_MAX_CURRENT */ + {.limit = 12500,.use_voltage = 1, .use_current = 1}, /* PD_POWER_OPTION_MAX_POWER */ +}; + +static uint8_t evaluate_src_cap(PD_protocol_t * p, uint16_t PPS_voltage, uint8_t PPS_current) +{ + const PD_power_option_setting_t * setting; + PD_power_info_t info; + uint8_t option = p->power_option; + uint8_t selected = 0; + + /* If selected option is not available, use first PDO. Reference: 6.4.1 Capabilities Message + The vSafe5V Fixed Supply Object Shall always be the first object. */ + if (option >= sizeof(power_option_setting) / sizeof(power_option_setting[0])) { + return 0; + } + + setting = &power_option_setting[option]; + for (uint8_t n = 0; PD_protocol_get_power_info(p, n, &info); n++) { + if (info.type == PD_PDO_TYPE_AUGMENTED_PDO) { + uint16_t pps_v = PPS_voltage * 2; /* Voltage in 20mV units */ + uint16_t pps_i = PPS_current * 5; /* Current in 50mA units */ + /* PD_power_info_t: Voltage in 50mV units, Current in 10mA units */ + if (info.min_v * 5 <= pps_v && pps_v <= info.max_v * 5 && pps_i <= info.max_i) { + return n; + } + } else { + uint8_t v = setting->use_voltage ? info.max_v >> 2 : 1; + uint8_t i = setting->use_current ? info.max_i >> 2 : 1; + uint16_t power = (uint16_t)v * i; /* reduce 10-bit power info to 8-bit and use 8-bit x 8-bit multiplication */ + if (power <= setting->limit) { + selected = n; + } + } + } + return selected; +} + +static void parse_header(PD_msg_header_info_t * info, uint16_t header) +{ + /* Reference: 6.2.1.1 Message Header */ + info->type = (header >> 0) & 0x1F; /* 4...0 Message Type */ + info->spec_rev = (header >> 6) & 0x3; /* 7...6 Specification Revision */ + info->id = (header >> 9) & 0x7; /* 11...9 MessageID */ + info->num_of_obj = (header >> 12) & 0x7; /* 14...12 Number of Data Objects */ +} + +static uint16_t generate_header(PD_protocol_t * p, uint8_t type, uint8_t obj_count) +{ + /* Reference: 6.2.1.1 Message Header */ + uint16_t h = ((uint16_t)type << 0) | /* 4...0 Message Type */ + ((uint16_t)PD_SPECIFICATION_REVISION << 6) | /* 7...6 Specification Revision */ + ((uint16_t)p->message_id << 9) | /* 11...9 MessageID */ + ((uint16_t)obj_count << 12); /* 14...12 Number of Data Objects */ + p->tx_msg_header = h; + return h; +} + +static uint16_t generate_header_ext(PD_protocol_t * p, uint8_t type, uint8_t data_size, uint32_t * obj) +{ + uint16_t h = generate_header(p, type, (data_size + 5) >> 2); /* set obj_count to fit ext header and data */ + h |= (uint16_t)1 << 15; /* Set extended field */ + /* Reference: 6.2.1.2 Extended Message Headerr */ + obj[0] |= ((uint16_t)data_size << 0) | /* 8...0 ata Size */ + /* Assume short message, set Chunk Number and Request Chunk to 0 */ + ((uint16_t)1 << 15); /* 15 Chunked */ + p->tx_msg_header = h; + return h; +} + +static void handler_good_crc(PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events) +{ + /* Reference: 6.2.1.3 Message ID + MessageIDCounter Shall be initialized to zero at power-on / reset, increment when receive GoodCRC Message */ + uint8_t message_id = p->message_id; + if (++message_id > 7) { + message_id = 0; + } + p->message_id = message_id; +} + +static void handler_goto_min(PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events) +{ + // Not implemented +} + +static void handler_accept(PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events) +{ + if (events) { + *events |= PD_PROTOCOL_EVENT_ACCEPT; + } +} + +static void handler_reject(PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events) +{ + if (events) { + *events |= PD_PROTOCOL_EVENT_PS_RDY; + } +} + +static void handler_ps_rdy(PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events) +{ + if (events) { + *events |= PD_PROTOCOL_EVENT_PS_RDY; + } +} + +static void handler_source_cap(PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events) +{ + PD_msg_header_info_t h; + parse_header(&h, header); + p->power_data_obj_count = h.num_of_obj; + for (uint8_t i = 0; i < h.num_of_obj; i++) { + p->power_data_obj[i] = obj[i]; + } + p->power_data_obj_selected = evaluate_src_cap(p, p->PPS_voltage, p->PPS_current); + if (events) { + *events |= PD_PROTOCOL_EVENT_SRC_CAP; + } +} + +static void handler_BIST(PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events) +{ + // TODO: implement BIST +} + +static void handler_alert(PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events) +{ + // TODO: implement alert +} + +static void handler_vender_def(PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events) +{ + // TODO: implement VDM parsing +} + +static void handler_PPS_Status(PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events) +{ + /* Handle chunked Extended message, Offset 2 byte for Extended Message Header */ + p->PPSSDB[0] = (obj[0] >> 16) & 0xFF; + p->PPSSDB[1] = (obj[0] >> 24) & 0xFF; + p->PPSSDB[2] = (obj[1] >> 0) & 0xFF; + p->PPSSDB[3] = (obj[1] >> 8) & 0xFF; + if (events) { + *events |= PD_PROTOCOL_EVENT_PPS_STATUS; + } +} + +static bool responder_get_sink_cap(PD_protocol_t * p, uint16_t * header, uint32_t * obj) +{ + /* Reference: 6.4.1.2.3 Sink Fixed Supply Power Data Object */ + uint32_t data = ((uint32_t)100 << 0) | /* B9...0 Operational Current in 10mA units */ + ((uint32_t)100 << 10) | /* B19...10 Voltage in 50mV units */ + ((uint32_t)1 << 26) | /* B26 USB Communications Capable */ + ((uint32_t)1 << 28) | /* B28 Higher Capability */ + ((uint32_t)PD_PDO_TYPE_FIXED_SUPPLY << 30); /* B31...30 Fixed supply */ + *obj = data; /* Only implement 5V 1A Fix supply PDO. Source rarely request sink cap */ + *header = generate_header(p, PD_DATA_MSG_TYPE_SINK_CAP, 1); + return true; +} + +static bool responder_sink_cap_ext(PD_protocol_t * p, uint16_t * header, uint32_t * obj) +{ + /* Reference: 6.5.13 Sink_Capabilities_Extended Message + 6.12.3 Applicability of Extended Messages (Normative; Shall be supported) */ + #define SINK_CAP_VID 0 + #define SINK_CAP_PID 0 + #define SINK_CAP_XID 0 /* If the vendor does not have an XID, then it Shall return zero */ + #define SINK_CAP_FW_Version 1 + #define SINK_CAP_HW_Version 1 + #define SINK_CAP_SKEDB_Version 1 + #define SINK_CAP_SINK_MODE 0x3 /* Bit 0: PPS charging supported, Bit 1: VBUS powered */ + #define SINK_CAP_SINK_MIN_PDP 5 /* Minimum PD Power in Watt */ + #define SINK_CAP_SINK_OP_PDP 5 /* Operational PD Power in Watt */ + #define SINK_CAP_SINK_MAX_PDP 100 /* Maximum PD Power in Watt */ + static const uint32_t SKEDB[6] PROGMEM = { /* 2-byte header + 21-byte data, chunked to 6 PDO */ + /* PDO[0], data byte 0...1 */ + /* 16-bit LSB is reserved for Extended Message Header */ + ((uint32_t)SINK_CAP_VID << 16), /* Byte 0...1 VID */ + /* PDO[1], data byte 2...5 */ + ((uint32_t)SINK_CAP_PID << 0) | /* Byte 2...3 PID */ + (((uint32_t)SINK_CAP_XID & 0xFF) << 16), /* Byte 4...5 XID */ + /* PDO[2], data byte 6...9 */ + (((uint32_t)SINK_CAP_XID >> 16) << 0) | /* Byte 6...7 XID */ + ((uint32_t)SINK_CAP_FW_Version << 16) | /* Byte 8 FW Version */ + ((uint32_t)SINK_CAP_HW_Version << 24), /* Byte 9 HW Version */ + /* PDO[3], data byte 10...13 */ + ((uint32_t)SINK_CAP_SKEDB_Version << 0), /* Byte 10 SKEDB Version */ + /* Not set Byte 11 Load Step, Byte 13..12 Sink Load Characteristics */ + /* PDO[4], data byte 14...17 */ + /* Not set Byte 14 Compliance, Byte 15 Touch Temp, Byte 16 Battery Info */ + ((uint32_t)SINK_CAP_SINK_MODE << 24), /* Byte 17 Sink Modes */ + /* PDO[5], data byte 18...20 */ + ((uint32_t)SINK_CAP_SINK_MIN_PDP << 0) | /* Byte 18 Minimum PDP */ + ((uint32_t)SINK_CAP_SINK_OP_PDP << 8) | /* Byte 19 Operational PDP */ + ((uint32_t)SINK_CAP_SINK_MAX_PDP << 16) /* Byte 20 Maximum PDP */ + }; + uint8_t i; + for (i = 0; i < 6; i++) { + COPY_PDO(obj[i], SKEDB[i]); + } + *header = generate_header_ext(p, PD_EXT_MSG_TYPE_SINK_CAP_EXT, 21, obj); + return false; +} + +static bool responder_reject(PD_protocol_t * p, uint16_t * header, uint32_t * obj) +{ + *header = generate_header(p, PD_CONTROL_MSG_TYPE_REJECT, 0); + return true; +} + +static bool responder_not_support(PD_protocol_t * p, uint16_t * header, uint32_t * obj) +{ + *header = generate_header(p, PD_CONTROL_MSG_TYPE_NOT_SUPPORT, 0); + return true; +} + +static bool responder_soft_reset(PD_protocol_t * p, uint16_t * header, uint32_t * obj) +{ + *header = generate_header(p, PD_CONTROL_MSG_TYPE_ACCEPT, 0); + return true; +} + +static bool responder_source_cap(PD_protocol_t * p, uint16_t * header, uint32_t * obj) +{ + PD_power_info_t info; + uint32_t data, pos = p->power_data_obj_selected + 1; + PD_protocol_get_power_info(p, p->power_data_obj_selected, &info); + /* Reference: 6.4.2 Request Message */ + if (info.type == PD_PDO_TYPE_AUGMENTED_PDO) { + /* NOTE: To compatible PD2.0 PHY, do not set Unchunked Extended Messages Supported */ + data = ((uint32_t)p->PPS_current << 0) | /* B6 ...0 Operating Current 50mA units */ + ((uint32_t)p->PPS_voltage << 9) | /* B19...9 Output Voltage in 20mV units */ + ((uint32_t)1 << 25) | /* B25 USB Communication Capable */ + ((uint32_t)pos << 28); /* B30...28 Object position (000b is Reserved and Shall Not be used) */ + } else { + uint32_t req = info.max_i ? info.max_i : info.max_p; + data = ((uint32_t)req << 0) | /* B9 ...0 Max Operating Current 10mA units / Max Operating Power in 250mW units */ + ((uint32_t)req << 10) | /* B19...10 Operating Current 10mA units / Operating Power in 250mW units */ + ((uint32_t)1 << 25) | /* B25 USB Communication Capable */ + ((uint32_t)pos << 28); /* B30...28 Object position (000b is Reserved and Shall Not be used) */ + } + *obj = data; + *header = generate_header(p, PD_DATA_MSG_TYPE_REQUEST, 1); + return true; +} + +static bool responder_vender_def(PD_protocol_t * p, uint16_t * header, uint32_t * obj) +{ + // TODO: implement VDM respond + return false; +} + +void PD_protocol_handle_msg(PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events) +{ + #define EXT_MSG_LIMIT (sizeof(ext_msg_list) / sizeof(ext_msg_list[0]) - 1) + #define DATA_MSG_LIMIT (sizeof(data_msg_list) / sizeof(data_msg_list[0]) - 1) + #define CTRL_MSG_LIMIT (sizeof(ctrl_msg_list) / sizeof(ctrl_msg_list[0]) - 1) + + const struct PD_msg_state_t * state; + PD_msg_header_info_t h; + parse_header(&h, header); + p->rx_msg_header = header; + if ((header >> 15) & 0x1) { + state = &ext_msg_list[h.type > EXT_MSG_LIMIT ? EXT_MSG_LIMIT : h.type]; + } else if (h.num_of_obj) { + state = &data_msg_list[h.type > DATA_MSG_LIMIT ? DATA_MSG_LIMIT : h.type]; + } else { + state =&ctrl_msg_list[h.type > CTRL_MSG_LIMIT ? CTRL_MSG_LIMIT : h.type]; + } + SET_MSG_STAGE(p->msg_state, state); + if (p->msg_state->handler) { + p->msg_state->handler(p, header, obj, events); + } +} + +bool PD_protocol_respond(PD_protocol_t * p, uint16_t * header, uint32_t * obj) +{ + if (p && p->msg_state && p->msg_state->responder && header && obj) { + return p->msg_state->responder(p, (uint16_t *)header, obj); + } + return false; +} + +void PD_protocol_create_get_src_cap(PD_protocol_t * p, uint16_t * header) +{ + *header = generate_header(p, PD_CONTROL_MSG_TYPE_GET_SRC_CAP, 0); +} + +void PD_protocol_create_get_PPS_status(PD_protocol_t *p, uint16_t *header) +{ + *header = generate_header(p, PD_CONTROL_MSG_TYPE_GET_PPS_STATUS, 0); +} + +void PD_protocol_create_request(PD_protocol_t * p, uint16_t * header, uint32_t * obj) +{ + responder_source_cap(p, header, obj); +} + +bool PD_protocol_get_power_info(PD_protocol_t * p, uint8_t index, PD_power_info_t * power_info) +{ + if (p && index < p->power_data_obj_count && power_info) { + uint32_t obj = p->power_data_obj[index]; + power_info->type = obj >> 30; + switch (power_info->type) { + case PD_PDO_TYPE_FIXED_SUPPLY: + /* Reference: 6.4.1.2.3 Source Fixed Supply Power Data Object */ + power_info->min_v = 0; + power_info->max_v = (obj >> 10) & 0x3FF; /* B19...10 Voltage in 50mV units */ + power_info->max_i = (obj >> 0) & 0x3FF; /* B9 ...0 Max Current in 10mA units */ + power_info->max_p = 0; + break; + case PD_PDO_TYPE_BATTERY: + /* Reference: 6.4.1.2.5 Battery Supply Power Data Object */ + power_info->min_v = (obj >> 10) & 0x3FF; /* B19...10 Min Voltage in 50mV units */ + power_info->max_v = (obj >> 20) & 0x3FF; /* B29...20 Max Voltage in 50mV units */ + power_info->max_i = 0; + power_info->max_p = (obj >> 0) & 0x3FF; /* B9 ...0 Max Allowable Power in 250mW units */ + break; + case PD_PDO_TYPE_VARIABLE_SUPPLY: + /* Reference: 6.4.1.2.4 Variable Supply (non-Battery) Power Data Object */ + power_info->min_v = (obj >> 10) & 0x3FF; /* B19...10 Min Voltage in 50mV units */ + power_info->max_v = (obj >> 20) & 0x3FF; /* B29...20 Max Voltage in 50mV units */ + power_info->max_i = (obj >> 0) & 0x3FF; /* B9 ...0 Max Current in 10mA units */ + power_info->max_p = 0; + break; + case PD_PDO_TYPE_AUGMENTED_PDO: + /* Reference: 6.4.1.3.4 Programmable Power Supply Augmented Power Data Object */ + power_info->max_v = ((obj >> 17) & 0xFF) * 2; /* B24...17 Max Voltage in 100mV units */ + power_info->min_v = ((obj >> 8) & 0xFF) * 2; /* B15...8 Min Voltage in 100mV units */ + power_info->max_i = ((obj >> 0) & 0x7F) * 5; /* B6 ...0 Max Current in 50mA units */ + power_info->max_p = 0; + break; + } + return true; + } + return false; +} + +bool PD_protocol_get_msg_info(uint16_t header, PD_msg_info_t * msg_info) +{ + PD_msg_header_info_t h; + parse_header(&h, header); + if (msg_info) { + const char * name; + const struct PD_msg_state_t * state; + uint8_t type = h.type; + SET_MSG_STAGE(state, header & 0x8000 ? &ext_msg_list[type] : + h.num_of_obj ? &data_msg_list[type] : &ctrl_msg_list[type]); + SET_MSG_NAME(name, state->name); + msg_info->name = name; + msg_info->id = h.id; + msg_info->spec_rev = h.spec_rev; + msg_info->num_of_obj = h.num_of_obj; + msg_info->extended = header >> 15; + return true; + } + return false; +} + +bool PD_protocol_get_PPS_status(PD_protocol_t *p, PPS_status_t * PPS_status) +{ + if (p && PPS_status) { + /* Reference: 6.5.10 PPS_Status Message */ + PPS_status->output_voltage = ((uint16_t)p->PPSSDB[1] << 8) | p->PPSSDB[0]; + PPS_status->output_current = p->PPSSDB[2]; + PPS_status->flag_PTF = (p->PPSSDB[3] >> 1) & 0x3; /* Bit 1 ... 2 */ + PPS_status->flag_OMF = (p->PPSSDB[3] >> 3) & 0x1; /* Bit 3 */ + return true; + } + return false; +} + +bool PD_protocol_set_power_option(PD_protocol_t * p, enum PD_power_option_t option) +{ + p->power_option = option; + p->PPS_voltage = 0; + p->PPS_current = 0; + if (p->power_data_obj_count > 0) { + p->power_data_obj_selected = evaluate_src_cap(p, p->PPS_voltage, p->PPS_current); + return true; /* need to re-send request */ + } + return false; +} + +bool PD_protocol_select_power(PD_protocol_t * p, uint8_t index) +{ + if (index < p->power_data_obj_count) { + p->power_data_obj_selected = index; + return true; /* need to re-send request */ + } + return false; +} + +bool PD_protocol_set_PPS(PD_protocol_t * p, uint16_t PPS_voltage, uint8_t PPS_current, bool strict) +{ + if (p->PPS_voltage != PPS_voltage || p->PPS_current != PPS_current) { + uint8_t selected = evaluate_src_cap(p, PPS_voltage, PPS_current); + if (selected || !strict) { + p->PPS_voltage = PPS_voltage; + p->PPS_current = PPS_current; + p->power_data_obj_selected = selected; + return true; /* need to re-send request */ + } + } + return false; +} + +void PD_protocol_reset(PD_protocol_t * p) +{ + p->msg_state = &ctrl_msg_list[0]; + p->message_id = 0; +} + +void PD_protocol_init(PD_protocol_t * p) +{ + memset(p, 0, sizeof(PD_protocol_t)); + p->msg_state = &ctrl_msg_list[0]; +} diff --git a/Power_Pico/BSP/PD/PD_UFP_Protocol.h b/Power_Pico/BSP/PD/PD_UFP_Protocol.h new file mode 100644 index 0000000..973f23a --- /dev/null +++ b/Power_Pico/BSP/PD/PD_UFP_Protocol.h @@ -0,0 +1,150 @@ + +/** + * PD_UFP_Protocol.c + * + * Updated on: Aug 25, 2021 + * Author: Ryan Ma + * + * Minimalist USB PD implement with only UFP(device) sink only functionality + * Requires PD PHY to do automatic GoodCRC response on valid SOP messages. + * Requires only stdint.h, stdbool.h and string.h + * No use of bit-field for better cross-platform compatibility + * + * Support PD3.0 PPS + * Do not support extended message. Not necessary for PD trigger and PPS. + * + * Reference: USB_PD_R2_0 V1.3 - 20170112 + * USB_PD_R3_0 V2.0 20190829 + ECNs 2020-12-10 + * - Chapter 6. Protocol Layer + * + */ + +#ifndef PD_UFP_PROTOCOL_H +#define PD_UFP_PROTOCOL_H + +#include +#include + +/* For use in PD_protocol_get_power_info() */ +#define PD_V(v) ((uint16_t)(v * 20 + 0.01)) +#define PD_A(a) ((uint16_t)(a * 100 + 0.01)) + +/* For use in PD_protocol_set_PPS_option() */ +#define PPS_V(v) ((uint16_t)(v * 50 + 0.01)) +#define PPS_A(a) ((uint8_t)(a * 20 + 0.01)) + +#define PD_PROTOCOL_MAX_NUM_OF_PDO 7 + +#define PD_PROTOCOL_EVENT_SRC_CAP (1 << 0) +#define PD_PROTOCOL_EVENT_PS_RDY (1 << 1) +#define PD_PROTOCOL_EVENT_ACCEPT (1 << 2) +#define PD_PROTOCOL_EVENT_REJECT (1 << 3) +#define PD_PROTOCOL_EVENT_PPS_STATUS (1 << 4) + +typedef uint8_t PD_protocol_event_t; + +enum PD_power_option_t { + PD_POWER_OPTION_MAX_5V = 0, + PD_POWER_OPTION_MAX_9V = 1, + PD_POWER_OPTION_MAX_12V = 2, + PD_POWER_OPTION_MAX_15V = 3, + PD_POWER_OPTION_MAX_20V = 4, + PD_POWER_OPTION_MAX_VOLTAGE = 5, + PD_POWER_OPTION_MAX_CURRENT = 6, + PD_POWER_OPTION_MAX_POWER = 7, +}; + +enum PD_power_data_obj_type_t { /* Power data object type */ + PD_PDO_TYPE_FIXED_SUPPLY = 0, + PD_PDO_TYPE_BATTERY = 1, + PD_PDO_TYPE_VARIABLE_SUPPLY = 2, + PD_PDO_TYPE_AUGMENTED_PDO = 3 /* USB PD 3.0 */ +}; + +enum PPS_PTF_t { + PPS_PTF_NOT_SUPPORT = 0, + PPS_PTF_NORMAL = 1, + PPS_PTF_WARNING = 2, + PPS_PTF_OVER_TEMPERATURE = 3 +}; + +enum PPS_OMF_t { + PPS_OMF_VOLTAGE_MODE = 0, + PPS_OMF_CURRENT_LIMIT_MODE = 1 +}; + +typedef struct { + uint16_t output_voltage; /* Voltage in 20mV units, 0xFFFF if not supported */ + uint8_t output_current; /* Current in 50mV units, 0xFF if not supported */ + enum PPS_PTF_t flag_PTF; + enum PPS_OMF_t flag_OMF; +} PPS_status_t; + +typedef struct { + const char * name; + uint8_t id; + uint8_t spec_rev; + uint8_t num_of_obj; + uint8_t extended; +} PD_msg_info_t; + +typedef struct { + enum PD_power_data_obj_type_t type; + uint16_t min_v; /* Voltage in 50mV units */ + uint16_t max_v; /* Voltage in 50mV units */ + uint16_t max_i; /* Current in 10mA units */ + uint16_t max_p; /* Power in 250mW units */ +} PD_power_info_t; + +struct PD_msg_state_t; +typedef struct { + const struct PD_msg_state_t *msg_state; + uint16_t tx_msg_header; + uint16_t rx_msg_header; + uint8_t message_id; + + uint16_t PPS_voltage; + uint8_t PPS_current; + uint8_t PPSSDB[4]; /* PPS Status Data Block */ + + enum PD_power_option_t power_option; + uint32_t power_data_obj[PD_PROTOCOL_MAX_NUM_OF_PDO]; + uint8_t power_data_obj_count; + uint8_t power_data_obj_selected; +} PD_protocol_t; + +/* Message handler */ +void PD_protocol_handle_msg(PD_protocol_t *p, uint16_t header, uint32_t *obj, PD_protocol_event_t *events); +bool PD_protocol_respond(PD_protocol_t *p, uint16_t *h, uint32_t *obj); + +/* PD Message creation */ +void PD_protocol_create_get_src_cap(PD_protocol_t *p, uint16_t *header); +void PD_protocol_create_get_PPS_status(PD_protocol_t *p, uint16_t *header); +void PD_protocol_create_request(PD_protocol_t *p, uint16_t *header, uint32_t *obj); + +/* Get functions */ +static inline uint8_t PD_protocol_get_selected_power(PD_protocol_t *p) { return p->power_data_obj_selected; } +static inline uint16_t PD_protocol_get_PPS_voltage(PD_protocol_t *p) { return p->PPS_voltage; } /* Voltage in 20mV units */ +static inline uint8_t PD_protocol_get_PPS_current(PD_protocol_t *p) { return p->PPS_current; } /* Current in 50mA units */ + +static inline uint16_t PD_protocol_get_tx_msg_header(PD_protocol_t *p) { return p->tx_msg_header; } +static inline uint16_t PD_protocol_get_rx_msg_header(PD_protocol_t *p) { return p->rx_msg_header; } + +bool PD_protocol_get_msg_info(uint16_t header, PD_msg_info_t * msg_info); + +bool PD_protocol_get_power_info(PD_protocol_t *p, uint8_t index, PD_power_info_t *power_info); +bool PD_protocol_get_PPS_status(PD_protocol_t *p, PPS_status_t * PPS_status); + +/* Set Fixed and Variable power option */ +bool PD_protocol_set_power_option(PD_protocol_t *p, enum PD_power_option_t option); +bool PD_protocol_select_power(PD_protocol_t *p, uint8_t index); + +/* Set PPS Voltage in 20mV units, Current in 50mA units. return true if re-send request is needed + strict=true, If PPS setting is not qualified, return false, nothing is changed. + strict=false, if PPS setting is not qualified, fall back to regular power option */ +bool PD_protocol_set_PPS(PD_protocol_t * p, uint16_t PPS_voltage, uint8_t PPS_current, bool strict); + +void PD_protocol_reset(PD_protocol_t *p); +void PD_protocol_init(PD_protocol_t *p); + +#endif diff --git a/Power_Pico/BSP/PD/fusb302_dev.c b/Power_Pico/BSP/PD/fusb302_dev.c new file mode 100644 index 0000000..6d45a2a --- /dev/null +++ b/Power_Pico/BSP/PD/fusb302_dev.c @@ -0,0 +1,185 @@ +#include "fusb302_dev.h" +#include "i2c.h" +#include "cmsis_os2.h" + +#define clock_ms() xTaskGetTickCount() +#define delay_ms(ms) osDelay(ms) + +#define PD_POLLING 100 +#define TYPEC_SINK_WAIT_CAP 350 +#define REQUEST_TO_PS_READY 580 +#define PPS_REQUEST 5000 + +FUSB302_dev_t fusb302_dev; +PD_protocol_t pd_protocol; +App_PD_t app_pd; + +int res; + +void fusb302_i2c_read(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data, uint8_t count) +{ + HAL_I2C_Mem_Read(&hi2c1, dev_addr, reg_addr, I2C_MEMADD_SIZE_8BIT, data, count, 10); +} + +void fusb302_i2c_write(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data, uint8_t count) +{ + HAL_I2C_Mem_Write(&hi2c1, dev_addr, reg_addr, I2C_MEMADD_SIZE_8BIT, data, count, 10); +} + + +void status_power_ready(status_power_t status, uint16_t voltage, uint16_t current) +{ + app_pd.ready_voltage = voltage; + app_pd.ready_current = current; + app_pd.status_power = status; +} + +void set_default_power(void) +{ + status_power_ready(STATUS_POWER_TYP, PD_V(5), PD_A(1)); +} + +void handle_protocol_event(PD_protocol_event_t events) +{ + if (events & PD_PROTOCOL_EVENT_SRC_CAP) { + app_pd.wait_src_cap = 0; + app_pd.get_src_cap_retry_count = 0; + app_pd.wait_ps_rdy = 1; + app_pd.time_wait_ps_rdy = clock_ms(); + } + if (events & PD_PROTOCOL_EVENT_REJECT) { + if (app_pd.wait_ps_rdy) { + app_pd.wait_ps_rdy = 0; + } + } + if (events & PD_PROTOCOL_EVENT_PS_RDY) { + PD_power_info_t p; + uint8_t i, selected_power = PD_protocol_get_selected_power(&app_pd.protocol); + PD_protocol_get_power_info(&app_pd.protocol, selected_power, &p); + app_pd.wait_ps_rdy = 0; + if (p.type == PD_PDO_TYPE_AUGMENTED_PDO) { + // PPS mode + FUSB302_set_vbus_sense(&fusb302_dev, 0); + if (app_pd.PPS_voltage_next) { + // Two stage startup for PPS voltage < 5V + PD_protocol_set_PPS(&app_pd.protocol, app_pd.PPS_voltage_next, app_pd.PPS_current_next, false); + app_pd.PPS_voltage_next = 0; + app_pd.send_request = 1; + } else { + app_pd.time_PPS_request = clock_ms(); + status_power_ready(STATUS_POWER_PPS, + PD_protocol_get_PPS_voltage(&app_pd.protocol), PD_protocol_get_PPS_current(&app_pd.protocol)); + } + } else { + FUSB302_set_vbus_sense(&fusb302_dev, 1); + status_power_ready(STATUS_POWER_TYP, p.max_v, p.max_i); + } + } +} + +void handle_FUSB302_event(FUSB302_event_t events) +{ + if (events & FUSB302_EVENT_DETACHED) { + PD_protocol_reset(&app_pd.protocol); + app_pd.cc = 0; + return; + } + if (events & FUSB302_EVENT_ATTACHED) { + uint8_t cc1 = 0, cc2 = 0, cc = 0; + FUSB302_get_cc(&fusb302_dev, &cc1, &cc2); + PD_protocol_reset(&app_pd.protocol); + if (cc1 && cc2 == 0) { + cc = cc1; + app_pd.cc = 1; + } else if (cc2 && cc1 == 0) { + cc = cc2; + app_pd.cc = 2; + } + /* TODO: handle no cc detected error */ + if (cc > 1) { + app_pd.wait_src_cap = 1; + } else { + set_default_power(); + } + } + if (events & FUSB302_EVENT_RX_SOP) { + PD_protocol_event_t protocol_event = 0; + uint16_t header; + uint32_t obj[7]; + FUSB302_get_message(&fusb302_dev, &header, obj); + PD_protocol_handle_msg(&app_pd.protocol, header, obj, &protocol_event); + if (protocol_event) { + handle_protocol_event(protocol_event); + } + } + if (events & FUSB302_EVENT_GOOD_CRC_SENT) { + uint16_t header; + uint32_t obj[7]; + delay_ms(4); /* Delay respond in case there are retry messages */ + if (PD_protocol_respond(&app_pd.protocol, &header, obj)) { + FUSB302_tx_sop(&fusb302_dev, header, obj); + } + } +} + +bool fusb302_timer(void) +{ + uint32_t t = clock_ms(); + if (app_pd.wait_src_cap && t - app_pd.time_wait_src_cap > TYPEC_SINK_WAIT_CAP) { + app_pd.time_wait_src_cap = t; + if (app_pd.get_src_cap_retry_count < 3) { + uint16_t header; + app_pd.get_src_cap_retry_count += 1; + /* Try to request soruce capabilities message (will not cause power cycle VBUS) */ + PD_protocol_create_get_src_cap(&app_pd.protocol, &header); + FUSB302_tx_sop(&fusb302_dev, header, 0); + } else { + app_pd.get_src_cap_retry_count = 0; + /* Hard reset will cause the source power cycle VBUS. */ + FUSB302_tx_hard_reset(&fusb302_dev); + PD_protocol_reset(&app_pd.protocol); + } + } + if (app_pd.wait_ps_rdy) { + if (t - app_pd.time_wait_ps_rdy > REQUEST_TO_PS_READY) { + app_pd.wait_ps_rdy = 0; + set_default_power(); + } + } else if (app_pd.send_request || (app_pd.status_power == STATUS_POWER_PPS && t - app_pd.time_PPS_request > PPS_REQUEST)) { + app_pd.wait_ps_rdy = 1; + app_pd.send_request = 0; + app_pd.time_PPS_request = t; + uint16_t header; + uint32_t obj[7]; + /* Send request if option updated or regularly in PPS mode to keep power alive */ + PD_protocol_create_request(&app_pd.protocol, &header, obj); + app_pd.time_wait_ps_rdy = clock_ms(); + FUSB302_tx_sop(&fusb302_dev, header, obj); + } + if (t - app_pd.time_polling > PD_POLLING) { + app_pd.time_polling = t; + return true; + } + return false; +} + +bool is_power_ready(void) { return app_pd.status_power == STATUS_POWER_TYP; } +bool is_PPS_ready(void) { return app_pd.status_power == STATUS_POWER_PPS; } + + +uint8_t fusb302_dev_init(void) { + fusb302_dev.i2c_address = 0x22<<1; // FUSB302 I2C address + fusb302_dev.i2c_read = fusb302_i2c_read; + fusb302_dev.i2c_write = fusb302_i2c_write; + fusb302_dev.delay_ms = osDelay; + if (FUSB302_init(&fusb302_dev) == FUSB302_SUCCESS && FUSB302_get_ID(&fusb302_dev, 0, 0) == FUSB302_SUCCESS) { + PD_protocol_init(&pd_protocol); + return 0; + } + return 1; +} + +uint8_t fusb302_run(void) { + + return 0; +} diff --git a/Power_Pico/BSP/PD/fusb302_dev.h b/Power_Pico/BSP/PD/fusb302_dev.h new file mode 100644 index 0000000..8bf8ede --- /dev/null +++ b/Power_Pico/BSP/PD/fusb302_dev.h @@ -0,0 +1,84 @@ +#ifndef __FUSB302_DEV_H__ +#define __FUSB302_DEV_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "FUSB302_UFP.h" +#include "PD_UFP_Protocol.h" + +typedef uint8_t status_power_t; + +enum { + PD_UFP_VOLTAGE_LED_OFF = 0, + PD_UFP_VOLTAGE_LED_5V = 1, + PD_UFP_VOLTAGE_LED_9V = 2, + PD_UFP_VOLTAGE_LED_12V = 3, + PD_UFP_VOLTAGE_LED_15V = 4, + PD_UFP_VOLTAGE_LED_20V = 5, + PD_UFP_VOLTAGE_LED_AUTO = 6 +}; +typedef uint8_t PD_UFP_VOLTAGE_LED_t; + +enum { + PD_UFP_CURRENT_LED_OFF = 0, + PD_UFP_CURRENT_LED_LE_1V = 1, + PD_UFP_CURRENT_LED_LE_3V = 2, + PD_UFP_CURRENT_LED_GT_3V = 3, + PD_UFP_CURRENT_LED_AUTO = 4 +}; +typedef uint8_t PD_UFP_CURRENT_LED_t; + +enum { + STATUS_POWER_NA = 0, + STATUS_POWER_TYP, + STATUS_POWER_PPS +}; +typedef uint8_t status_power_t; + +typedef struct +{ + uint8_t state; + //uint8_t pd_ic_initial; + PD_protocol_t protocol; + + // Power ready power + uint16_t ready_voltage; + uint16_t ready_current; + + // PPS setup + uint16_t PPS_voltage_next; + uint8_t PPS_current_next; + // Status + uint8_t status_initialized; + uint8_t status_src_cap_received; + status_power_t status_power; + // Timer and counter for PD Policy + uint32_t time_polling; + uint32_t time_wait_src_cap; + uint32_t time_wait_ps_rdy; + uint32_t time_PPS_request; + uint8_t get_src_cap_retry_count; + uint8_t wait_src_cap; + uint8_t wait_ps_rdy; + uint8_t send_request; + uint8_t cc; + char power_list[7][24]; + uint8_t power_list_num; + uint8_t power_index; + float power_v; + float power_i; +}App_PD_t; + +extern FUSB302_dev_t fusb302_dev; +extern PD_protocol_t pd_protocol; + +uint8_t fusb302_dev_init(void); +uint8_t fusb302_test(void); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/Power_Pico/User/Tasks/Inc/user_DataTask.h b/Power_Pico/User/Tasks/Inc/user_DataTask.h deleted file mode 100644 index e658d15..0000000 --- a/Power_Pico/User/Tasks/Inc/user_DataTask.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef __USER_DATATASK_H__ -#define __USER_DATATASK_H__ - -#ifdef __cplusplus -extern "C" { -#endif - -void DataTask(void *argument); - - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/Power_Pico/User/Tasks/Inc/user_PDUFPTask.h b/Power_Pico/User/Tasks/Inc/user_PDUFPTask.h new file mode 100644 index 0000000..31e9864 --- /dev/null +++ b/Power_Pico/User/Tasks/Inc/user_PDUFPTask.h @@ -0,0 +1,16 @@ +#ifndef __USER_PDUFPTASK_H__ +#define __USER_PDUFPTASK_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +void PDUFPTask(void *argument); + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/Power_Pico/User/Tasks/Src/user_DataTask.c b/Power_Pico/User/Tasks/Src/user_DataTask.c deleted file mode 100644 index 60d5522..0000000 --- a/Power_Pico/User/Tasks/Src/user_DataTask.c +++ /dev/null @@ -1,34 +0,0 @@ -/* Private includes -----------------------------------------------------------*/ -//includes -#include "user_TasksInit.h" -#include "user_DataTask.h" -#include "main.h" -#include "adc.h" -#include "gate.h" -#include "usart.h" -/* Private typedef -----------------------------------------------------------*/ - -/* Private define ------------------------------------------------------------*/ - - -/* Private variables ---------------------------------------------------------*/ - -/* Private function prototypes -----------------------------------------------*/ - -/** - * @brief task for data process and so on - * @param argument: Not used - * @retval None - */ -void DataTask(void *argument) -{ - uint8_t keystr = 0; - uint16_t cur_bias = 4096 / 2; - while(1) - { - - // over current and valtage, beep - - osDelay(100); - } -} diff --git a/Power_Pico/User/Tasks/Src/user_HardwareInitTask.c b/Power_Pico/User/Tasks/Src/user_HardwareInitTask.c index de594dc..31f674c 100644 --- a/Power_Pico/User/Tasks/Src/user_HardwareInitTask.c +++ b/Power_Pico/User/Tasks/Src/user_HardwareInitTask.c @@ -7,7 +7,7 @@ #include "stm32f4xx_it.h" #include "adc.h" #include "rtc.h" - +#include "i2c.h" // user #include "user_TasksInit.h" @@ -17,6 +17,7 @@ #include "lcd_init.h" #include "gate.h" #include "data_queue.h" +#include "fusb302_dev.h" // ui #include "lvgl.h" @@ -64,9 +65,6 @@ void HardwareInitTask(void *argument) Gate_Port_Init(); flow_route_selection(HIGH_CUR); - // FUSB CC dis connect - HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET); - // ADC sample start HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adc_packet.data, ADC_TIMES * ADC_CHANELS); /*启动ADC的DMA传输,配合定时器触发ADC转换 12位的ADC对应0-4095 */ HAL_TIM_Base_Start(&htim2); /*开启定时器,用溢出时间来触发ADC*/ @@ -80,6 +78,9 @@ void HardwareInitTask(void *argument) // key Key_Port_Init(); + // FUSB CC pin connect + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_SET); + // lcd // done in lvgl disp init @@ -94,7 +95,6 @@ void HardwareInitTask(void *argument) xTaskResumeAll(); vTaskDelete(NULL); - osDelay(500); } } diff --git a/Power_Pico/User/Tasks/Src/user_TasksInit.c b/Power_Pico/User/Tasks/Src/user_TasksInit.c index c23246c..0af30f9 100644 --- a/Power_Pico/User/Tasks/Src/user_TasksInit.c +++ b/Power_Pico/User/Tasks/Src/user_TasksInit.c @@ -11,7 +11,7 @@ //tasks #include "user_HardwareInitTask.h" -#include "user_DataTask.h" +#include "user_PDUFPTask.h" #include "user_KeyTask.h" /* Private typedef -----------------------------------------------------------*/ @@ -37,11 +37,11 @@ const osThreadAttr_t HardwareInitTask_attributes = { }; //Data process task -osThreadId_t DataTaskHandle; -const osThreadAttr_t DataTask_attributes = { - .name = "DataTask", +osThreadId_t PDUFPTaskHandle; +const osThreadAttr_t PDUFPTask_attributes = { + .name = "PDUFPTask", .stack_size = 128 * 2, - .priority = (osPriority_t) osPriorityHigh1, + .priority = (osPriority_t) osPriorityLow7, }; //Key task @@ -86,7 +86,7 @@ void User_Tasks_Init(void) /* add threads, ... */ HardwareInitTaskHandle = osThreadNew(HardwareInitTask, NULL, &HardwareInitTask_attributes); - // DataTaskHandle = osThreadNew(DataTask, NULL, &DataTask_attributes); + // PDUFPTaskHandle = osThreadNew(PDUFPTask, NULL, &PDUFPTask_attributes); KeyTaskHandle = osThreadNew(KeyTask, NULL, &KeyTask_attributes); LvHandlerTaskHandle = osThreadNew(LvHandlerTask, NULL, &LvHandlerTask_attributes);