mirror of
https://github.com/No-Chicken/Power-Pico.git
synced 2026-04-03 13:02:36 +08:00
移动文件夹
This commit is contained in:
596
software/Power_Pico/BSP/PD/FUSB302_UFP.c
Normal file
596
software/Power_Pico/BSP/PD/FUSB302_UFP.c
Normal file
@@ -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 <string.h>
|
||||
#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;
|
||||
}
|
||||
76
software/Power_Pico/BSP/PD/FUSB302_UFP.h
Normal file
76
software/Power_Pico/BSP/PD/FUSB302_UFP.h
Normal file
@@ -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 <stdint.h>
|
||||
|
||||
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 */
|
||||
|
||||
599
software/Power_Pico/BSP/PD/PD_UFP_Protocol.c
Normal file
599
software/Power_Pico/BSP/PD/PD_UFP_Protocol.c
Normal file
@@ -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 <string.h>
|
||||
#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 <avr/pgmspace.h>
|
||||
#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];
|
||||
}
|
||||
150
software/Power_Pico/BSP/PD/PD_UFP_Protocol.h
Normal file
150
software/Power_Pico/BSP/PD/PD_UFP_Protocol.h
Normal file
@@ -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 <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* 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
|
||||
196
software/Power_Pico/BSP/PD/fusb302_dev.c
Normal file
196
software/Power_Pico/BSP/PD/fusb302_dev.c
Normal file
@@ -0,0 +1,196 @@
|
||||
#include "fusb302_dev.h"
|
||||
#include "i2c.h"
|
||||
#include "cmsis_os2.h"
|
||||
|
||||
#define FUSB302_USE_OS 1
|
||||
|
||||
#if FUSB302_USE_OS == 1
|
||||
#define clock_ms() osKernelGetTickCount()
|
||||
#define delay_ms(ms) osDelay(ms)
|
||||
#else
|
||||
#define clock_ms() HAL_GetTick()
|
||||
#define delay_ms(ms) HAL_Delay(ms)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#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;
|
||||
App_PD_t app_pd;
|
||||
|
||||
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.0), PD_A(1.0));
|
||||
}
|
||||
|
||||
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();
|
||||
app_pd.state = PD_STATE_GET_CAPS; // 更新状态:已收到能力,可以请求了
|
||||
}
|
||||
if (events & PD_PROTOCOL_EVENT_REJECT) {
|
||||
if (app_pd.wait_ps_rdy) {
|
||||
app_pd.wait_ps_rdy = 0;
|
||||
app_pd.state = PD_STATE_FAILED; // 更新状态:请求被拒绝
|
||||
}
|
||||
}
|
||||
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));
|
||||
app_pd.state = PD_STATE_SUCCESS_PPS; // 更新状态:成功协商到PPS
|
||||
}
|
||||
} else {
|
||||
FUSB302_set_vbus_sense(&fusb302_dev, 1);
|
||||
status_power_ready(STATUS_POWER_TYP, p.max_v, p.max_i);
|
||||
app_pd.state = PD_STATE_SUCCESS_FIXED; // 更新状态:协商到了一个固定电压
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.state = PD_STATE_NEGOTIATING; // 更新状态:正在协商中
|
||||
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; }
|
||||
void send_power_request(void) { app_pd.send_request = 1; }
|
||||
|
||||
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;
|
||||
#if FUSB302_USE_OS == 1
|
||||
fusb302_dev.delay_ms = osDelay;
|
||||
#else
|
||||
fusb302_dev.delay_ms = HAL_Delay;
|
||||
#endif
|
||||
if (FUSB302_init(&fusb302_dev) == FUSB302_SUCCESS && FUSB302_get_ID(&fusb302_dev, 0, 0) == FUSB302_SUCCESS) {
|
||||
PD_protocol_init(&app_pd.protocol);
|
||||
app_pd.state = PD_STATE_IDLE; // 初始状态
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
104
software/Power_Pico/BSP/PD/fusb302_dev.h
Normal file
104
software/Power_Pico/BSP/PD/fusb302_dev.h
Normal file
@@ -0,0 +1,104 @@
|
||||
#ifndef __FUSB302_DEV_H__
|
||||
#define __FUSB302_DEV_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#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;
|
||||
|
||||
enum {
|
||||
PD_SYS_STATE_INIT = 0,
|
||||
PD_SYS_STATE_READY,
|
||||
PD_SYS_STATE_ERROR
|
||||
};
|
||||
typedef uint8_t PD_user_state_t;
|
||||
|
||||
typedef enum {
|
||||
PD_STATE_IDLE, // 空闲状态
|
||||
PD_STATE_NEGOTIATING, // 正在协商中 (已发送请求,等待PS_RDY)
|
||||
PD_STATE_SUCCESS_PPS, // 成功协商到PPS模式
|
||||
PD_STATE_SUCCESS_FIXED, // 成功协商到固定电压
|
||||
PD_STATE_FAILED, // 协商失败 (收到Reject或超时)
|
||||
PD_STATE_GET_CAPS // 刚刚收到能力列表,准备发起第一次请求
|
||||
} App_PD_State_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
||||
App_PD_State_t state;
|
||||
|
||||
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 App_PD_t app_pd;
|
||||
|
||||
uint8_t fusb302_dev_init(void);
|
||||
void handle_FUSB302_event(FUSB302_event_t events);
|
||||
bool is_power_ready(void);
|
||||
bool is_PPS_ready(void);
|
||||
void send_power_request(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
Reference in New Issue
Block a user