add pd ufp

This commit is contained in:
不吃油炸鸡
2025-10-14 16:24:47 +08:00
parent 6015bfda31
commit 40439c1800
11 changed files with 1717 additions and 61 deletions

View 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, &REG_STATUS0, 1);
cc = REG_STATUS0 & BC_LVL_MASK;
for (uint8_t i = 0; i < 5; i++) {
REG_READ(ADDRESS_STATUS0, &REG_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, &REG_STATUS0, 1);
if (REG_STATUS0 & VBUSOK) {
/* enable internal oscillator */
REG_POWER = PWR_BANDGAP | PWR_RECEIVER | PWR_MEASURE | PWR_INT_OSC;
REG_WRITE(ADDRESS_POWER, &REG_POWER, 1);
dev->delay_ms(2);
/* read cc1 */
REG_SWITCHES0 = PDWN1 | PDWN2 | MEAS_CC1;
REG_SWITCHES1 = SPECREV0;
REG_MEASURE = 49;
REG_WRITE(ADDRESS_SWITCHES0, &REG_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, &REG_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, &REG_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, &REG_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, &REG_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, &REG_SWITCHES0, 3);
/* turn off internal oscillator */
REG_POWER = PWR_BANDGAP | PWR_RECEIVER | PWR_MEASURE;
REG_WRITE(ADDRESS_POWER, &REG_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, &reg_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, &REG_RESET, 1);
/* fetch all R/W registers */
REG_READ(ADDRESS_DEVICE_ID, &REG_DEVICE_ID, 15);
/* configure switchs and comparators */
REG_SWITCHES0 = PDWN1 | PDWN2;
REG_SWITCHES1 = SPECREV0;
REG_MEASURE = 49;
REG_WRITE(ADDRESS_SWITCHES0, &REG_SWITCHES0, 3);
/* configure auto retries */
REG_CONTROL3 &= ~N_RETRIES_MASK;
REG_CONTROL3 |= N_RETRIES(3) | AUTO_RETRY;
REG_WRITE(ADDRESS_CONTROL3, &REG_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, &REG_MASK, 1);
/* configure interrupt maska/maskb */
REG_MASKA = 0xFF;
REG_MASKA &= ~(M_RETRYFAIL | M_HARDSENT | M_TXSENT | M_HARDRST);
REG_WRITE(ADDRESS_MASKA, &REG_MASKA, 1);
REG_MASKB = 0xFF;
REG_MASKB &= ~(M_GCRCSENT);
REG_WRITE(ADDRESS_MASKB, &REG_MASKB, 1);
/* enable interrupt */
REG_CONTROL0 &= ~INT_MASK;
REG_WRITE(ADDRESS_CONTROL0, &REG_CONTROL0, 1);
/* Power on, enable VUSB detection */
REG_POWER = PWR_BANDGAP | PWR_RECEIVER | PWR_MEASURE;
REG_WRITE(ADDRESS_POWER, &REG_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, &reg, 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, &REG_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, &REG_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, &reg_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, &reg_control, 1);
dev->delay_ms(6);
reg_control = PD_RESET;
REG_WRITE(ADDRESS_RESET, &reg_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;
}

View 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 */

View 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];
}

View 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

View 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;
}

View 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

View File

@@ -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

View 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

View File

@@ -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);
}
}

View File

@@ -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);
} }
} }

View File

@@ -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);