mirror of
https://github.com/No-Chicken/Power-Pico.git
synced 2026-04-03 13:02:36 +08:00
add pd ufp
This commit is contained in:
596
Power_Pico/BSP/PD/FUSB302_UFP.c
Normal file
596
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
Power_Pico/BSP/PD/FUSB302_UFP.h
Normal file
76
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
Power_Pico/BSP/PD/PD_UFP_Protocol.c
Normal file
599
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
Power_Pico/BSP/PD/PD_UFP_Protocol.h
Normal file
150
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
|
||||||
185
Power_Pico/BSP/PD/fusb302_dev.c
Normal file
185
Power_Pico/BSP/PD/fusb302_dev.c
Normal file
@@ -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;
|
||||||
|
}
|
||||||
84
Power_Pico/BSP/PD/fusb302_dev.h
Normal file
84
Power_Pico/BSP/PD/fusb302_dev.h
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
#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;
|
||||||
|
|
||||||
|
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
|
||||||
@@ -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
|
|
||||||
|
|
||||||
16
Power_Pico/User/Tasks/Inc/user_PDUFPTask.h
Normal file
16
Power_Pico/User/Tasks/Inc/user_PDUFPTask.h
Normal file
@@ -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
|
||||||
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
#include "stm32f4xx_it.h"
|
#include "stm32f4xx_it.h"
|
||||||
#include "adc.h"
|
#include "adc.h"
|
||||||
#include "rtc.h"
|
#include "rtc.h"
|
||||||
|
#include "i2c.h"
|
||||||
// user
|
// user
|
||||||
#include "user_TasksInit.h"
|
#include "user_TasksInit.h"
|
||||||
|
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
#include "lcd_init.h"
|
#include "lcd_init.h"
|
||||||
#include "gate.h"
|
#include "gate.h"
|
||||||
#include "data_queue.h"
|
#include "data_queue.h"
|
||||||
|
#include "fusb302_dev.h"
|
||||||
|
|
||||||
// ui
|
// ui
|
||||||
#include "lvgl.h"
|
#include "lvgl.h"
|
||||||
@@ -64,9 +65,6 @@ void HardwareInitTask(void *argument)
|
|||||||
Gate_Port_Init();
|
Gate_Port_Init();
|
||||||
flow_route_selection(HIGH_CUR);
|
flow_route_selection(HIGH_CUR);
|
||||||
|
|
||||||
// FUSB CC dis connect
|
|
||||||
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET);
|
|
||||||
|
|
||||||
// ADC sample start
|
// 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_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*/
|
HAL_TIM_Base_Start(&htim2); /*开启定时器,用溢出时间来触发ADC*/
|
||||||
@@ -80,6 +78,9 @@ void HardwareInitTask(void *argument)
|
|||||||
// key
|
// key
|
||||||
Key_Port_Init();
|
Key_Port_Init();
|
||||||
|
|
||||||
|
// FUSB CC pin connect
|
||||||
|
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_SET);
|
||||||
|
|
||||||
// lcd
|
// lcd
|
||||||
// done in lvgl disp init
|
// done in lvgl disp init
|
||||||
|
|
||||||
@@ -94,7 +95,6 @@ void HardwareInitTask(void *argument)
|
|||||||
|
|
||||||
xTaskResumeAll();
|
xTaskResumeAll();
|
||||||
vTaskDelete(NULL);
|
vTaskDelete(NULL);
|
||||||
osDelay(500);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
//tasks
|
//tasks
|
||||||
#include "user_HardwareInitTask.h"
|
#include "user_HardwareInitTask.h"
|
||||||
#include "user_DataTask.h"
|
#include "user_PDUFPTask.h"
|
||||||
#include "user_KeyTask.h"
|
#include "user_KeyTask.h"
|
||||||
|
|
||||||
/* Private typedef -----------------------------------------------------------*/
|
/* Private typedef -----------------------------------------------------------*/
|
||||||
@@ -37,11 +37,11 @@ const osThreadAttr_t HardwareInitTask_attributes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
//Data process task
|
//Data process task
|
||||||
osThreadId_t DataTaskHandle;
|
osThreadId_t PDUFPTaskHandle;
|
||||||
const osThreadAttr_t DataTask_attributes = {
|
const osThreadAttr_t PDUFPTask_attributes = {
|
||||||
.name = "DataTask",
|
.name = "PDUFPTask",
|
||||||
.stack_size = 128 * 2,
|
.stack_size = 128 * 2,
|
||||||
.priority = (osPriority_t) osPriorityHigh1,
|
.priority = (osPriority_t) osPriorityLow7,
|
||||||
};
|
};
|
||||||
|
|
||||||
//Key task
|
//Key task
|
||||||
@@ -86,7 +86,7 @@ void User_Tasks_Init(void)
|
|||||||
|
|
||||||
/* add threads, ... */
|
/* add threads, ... */
|
||||||
HardwareInitTaskHandle = osThreadNew(HardwareInitTask, NULL, &HardwareInitTask_attributes);
|
HardwareInitTaskHandle = osThreadNew(HardwareInitTask, NULL, &HardwareInitTask_attributes);
|
||||||
// DataTaskHandle = osThreadNew(DataTask, NULL, &DataTask_attributes);
|
// PDUFPTaskHandle = osThreadNew(PDUFPTask, NULL, &PDUFPTask_attributes);
|
||||||
KeyTaskHandle = osThreadNew(KeyTask, NULL, &KeyTask_attributes);
|
KeyTaskHandle = osThreadNew(KeyTask, NULL, &KeyTask_attributes);
|
||||||
LvHandlerTaskHandle = osThreadNew(LvHandlerTask, NULL, &LvHandlerTask_attributes);
|
LvHandlerTaskHandle = osThreadNew(LvHandlerTask, NULL, &LvHandlerTask_attributes);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user