wrap up initial PD support for G4

This commit is contained in:
hathach 2023-06-12 16:42:27 +07:00
parent c28503060c
commit 6941a5af81
No known key found for this signature in database
GPG Key ID: F5D50C6D51D17CBA
4 changed files with 196 additions and 109 deletions

View File

@ -62,7 +62,8 @@ int main(void)
while (1) {
led_blinking_task();
// tuc_task();
// tinyusb typec task
tuc_task();
}
}
@ -73,6 +74,93 @@ void app_main(void)
}
#endif
//--------------------------------------------------------------------+
// TypeC PD callbacks
//--------------------------------------------------------------------+
bool tuc_pd_data_received_cb(uint8_t rhport, pd_header_t const* header, uint8_t const* dobj, uint8_t const* p_end) {
switch (header->msg_type) {
case PD_DATA_SOURCE_CAP: {
printf("PD Source Capabilities\r\n");
// Examine source capability and select a suitable PDO (starting from 1 with safe5v)
uint8_t obj_pos = 1;
for(size_t i=0; i<header->n_data_obj; i++) {
TU_VERIFY(dobj < p_end);
uint32_t const pdo = tu_le32toh(tu_unaligned_read32(dobj));
switch ((pdo >> 30) & 0x03ul) {
case PD_PDO_TYPE_FIXED: {
pd_pdo_fixed_t const* fixed = (pd_pdo_fixed_t const*) &pdo;
printf("[Fixed] %u mV %u mA\r\n", fixed->voltage_50mv*50, fixed->current_max_10ma*10);
break;
}
case PD_PDO_TYPE_BATTERY:
break;
case PD_PDO_TYPE_VARIABLE:
break;
case PD_PDO_TYPE_APDO:
break;
}
dobj += 4;
}
//------------- Response with selected PDO -------------//
// Be careful and make sure your board can withstand the selected PDO voltage other than safe5v e.g 12v or 20v
// Send request with selected PDO position as response to Source Cap
pd_rdo_fixed_variable_t rdo = {
.current_extremum_10ma = 50, // max 500mA
.current_operate_10ma = 30, // 300mA
.reserved = 0,
.epr_mode_capable = 0,
.unchunked_ext_msg_support = 0,
.no_usb_suspend = 0,
.usb_comm_capable = 1,
.capability_mismatch = 0,
.give_back_flag = 0, // exteremum is max
.object_position = obj_pos,
};
tuc_msg_request(rhport, &rdo);
break;
}
default: break;
}
return true;
}
bool tuc_pd_control_received_cb(uint8_t rhport, pd_header_t const* header) {
switch (header->msg_type) {
case PD_CTRL_ACCEPT:
printf("PD Request Accepted\r\n");
// preparing for power transition
break;
case PD_CTRL_REJECT:
printf("PD Request Rejected\r\n");
// try to negotiate further power
break;
case PD_CTRL_PS_READY:
printf("PD Power Ready\r\n");
// Source is ready to supply power
break;
default:
break;
}
return true;
}
//--------------------------------------------------------------------+
// BLINKING TASK
//--------------------------------------------------------------------+

View File

@ -48,8 +48,7 @@ enum {
TCD_EVENT_TX_COMPLETE,
};
typedef struct {
typedef struct TU_ATTR_PACKED {
uint8_t rhport;
uint8_t event_id;
@ -58,10 +57,10 @@ typedef struct {
uint8_t cc_state[2];
} cc_changed;
struct {
uint16_t xferred_bytes;
uint8_t result;
} rx_complete;
struct TU_ATTR_PACKED {
uint16_t result : 2;
uint16_t xferred_bytes : 14;
} xfer_complete;
};
} tcd_event_t;;
@ -114,7 +113,7 @@ void tcd_event_rx_complete(uint8_t rhport, uint16_t xferred_bytes, uint8_t resul
tcd_event_t event = {
.rhport = rhport,
.event_id = TCD_EVENT_RX_COMPLETE,
.rx_complete = {
.xfer_complete = {
.xferred_bytes = xferred_bytes,
.result = result
}
@ -128,7 +127,7 @@ void tcd_event_tx_complete(uint8_t rhport, uint16_t xferred_bytes, uint8_t resul
tcd_event_t event = {
.rhport = rhport,
.event_id = TCD_EVENT_TX_COMPLETE,
.rx_complete = {
.xfer_complete = {
.xferred_bytes = xferred_bytes,
.result = result
}

View File

@ -52,8 +52,12 @@ static bool _utcd_inited = false;
static bool _port_inited[TUP_TYPEC_RHPORTS_NUM];
// Max possible PD size is 262 bytes
static uint8_t _rx_buf[262] TU_ATTR_ALIGNED(4);
static uint8_t _tx_buf[100] TU_ATTR_ALIGNED(4);
static uint8_t _rx_buf[64] TU_ATTR_ALIGNED(4);
static uint8_t _tx_buf[64] TU_ATTR_ALIGNED(4);
bool utcd_msg_send(uint8_t rhport, pd_header_t const* header, void const* data);
bool parse_msg_data(uint8_t rhport, pd_header_t const* header, uint8_t const* dobj, uint8_t const* p_end);
bool parse_msg_control(uint8_t rhport, pd_header_t const* header);
//--------------------------------------------------------------------+
//
@ -79,6 +83,7 @@ bool tuc_init(uint8_t rhport, uint32_t port_type) {
}
TU_LOG_UTCD("UTCD init on port %u\r\n", rhport);
TU_LOG_INT(UTCD_DEBUG, sizeof(tcd_event_t));
TU_ASSERT(tcd_init(rhport, port_type));
tcd_int_enable(rhport);
@ -87,6 +92,65 @@ bool tuc_init(uint8_t rhport, uint32_t port_type) {
return true;
}
void tuc_task_ext(uint32_t timeout_ms, bool in_isr) {
(void) in_isr; // not implemented yet
// Skip if stack is not initialized
if (!_utcd_inited) return;
// Loop until there is no more events in the queue
while (1) {
tcd_event_t event;
if (!osal_queue_receive(_utcd_q, &event, timeout_ms)) return;
switch (event.event_id) {
case TCD_EVENT_CC_CHANGED:
break;
case TCD_EVENT_RX_COMPLETE:
// TODO process message here in ISR, move to thread later
if (event.xfer_complete.result == XFER_RESULT_SUCCESS) {
pd_header_t const* header = (pd_header_t const*) _rx_buf;
if (header->n_data_obj == 0) {
parse_msg_control(event.rhport, header);
}else {
uint8_t const* p_end = _rx_buf + event.xfer_complete.xferred_bytes;
uint8_t const * dobj = _rx_buf + sizeof(pd_header_t);
parse_msg_data(event.rhport, header, dobj, p_end);
}
}
// prepare for next message
tcd_msg_receive(event.rhport, _rx_buf, sizeof(_rx_buf));
break;
case TCD_EVENT_TX_COMPLETE:
break;
default: break;
}
}
}
bool parse_msg_data(uint8_t rhport, pd_header_t const* header, uint8_t const* dobj, uint8_t const* p_end) {
if (tuc_pd_data_received_cb) {
tuc_pd_data_received_cb(rhport, header, dobj, p_end);
}
return true;
}
bool parse_msg_control(uint8_t rhport, pd_header_t const* header) {
if (tuc_pd_control_received_cb) {
tuc_pd_control_received_cb(rhport, header);
}
return true;
}
//--------------------------------------------------------------------+
//
//--------------------------------------------------------------------+
@ -104,94 +168,18 @@ bool utcd_msg_send(uint8_t rhport, pd_header_t const* header, void const* data)
return tcd_msg_send(rhport, _tx_buf, sizeof(pd_header_t) + n_data_obj * 4);
}
bool parse_message(uint8_t rhport, uint8_t const* buf, uint16_t len) {
(void) rhport;
uint8_t const* p_end = buf + len;
pd_header_t const* header = (pd_header_t const*) buf;
uint8_t const * ptr = buf + sizeof(pd_header_t);
bool tuc_msg_request(uint8_t rhport, void const* rdo) {
pd_header_t const header = {
.msg_type = PD_DATA_REQUEST,
.data_role = PD_DATA_ROLE_UFP,
.specs_rev = PD_REV_30,
.power_role = PD_POWER_ROLE_SINK,
.msg_id = 0,
.n_data_obj = 1,
.extended = 0,
};
if (header->n_data_obj == 0) {
// control message
switch (header->msg_type) {
case PD_CTRL_GOOD_CRC:
break;
case PD_CTRL_ACCEPT:
break;
case PD_CTRL_REJECT:
break;
case PD_CTRL_PS_READY:
break;
default: break;
}
} else {
// data message
switch (header->msg_type) {
case PD_DATA_SOURCE_CAP: {
// Examine source capability and select a suitable PDO (starting from 1 with safe5v)
uint8_t obj_pos = 1;
for(size_t i=0; i<header->n_data_obj; i++) {
TU_VERIFY(ptr < p_end);
uint32_t const pdo = tu_le32toh(tu_unaligned_read32(ptr));
switch ((pdo >> 30) & 0x03ul) {
case PD_PDO_TYPE_FIXED: {
pd_pdo_fixed_t const* fixed = (pd_pdo_fixed_t const*) &pdo;
TU_LOG3("[Fixed] %u mV %u mA\r\n", fixed->voltage_50mv*50, fixed->current_max_10ma*10);
break;
}
case PD_PDO_TYPE_BATTERY:
break;
case PD_PDO_TYPE_VARIABLE:
break;
case PD_PDO_TYPE_APDO:
break;
}
ptr += 4;
}
// Send request with selected PDO position as response to Source Cap
pd_rdo_fixed_variable_t rdo = {
.current_extremum_10ma = 50, // max 500mA
.current_operate_10ma = 30, // 300mA
.reserved = 0,
.epr_mode_capable = 0,
.unchunked_ext_msg_support = 0,
.no_usb_suspend = 0,
.usb_comm_capable = 1,
.capability_mismatch = 0,
.give_back_flag = 0, // exteremum is max
.object_position = obj_pos,
};
pd_header_t const req_header = {
.msg_type = PD_DATA_REQUEST,
.data_role = PD_DATA_ROLE_UFP,
.specs_rev = PD_REV_20,
.power_role = PD_POWER_ROLE_SINK,
.msg_id = 0,
.n_data_obj = 1,
.extended = 0,
};
utcd_msg_send(rhport, &req_header, &rdo);
break;
}
default: break;
}
}
return true;
return utcd_msg_send(rhport, &header, rdo);
}
void tcd_event_handler(tcd_event_t const * event, bool in_isr) {
@ -199,25 +187,17 @@ void tcd_event_handler(tcd_event_t const * event, bool in_isr) {
switch(event->event_id) {
case TCD_EVENT_CC_CHANGED:
if (event->cc_changed.cc_state[0] || event->cc_changed.cc_state[1]) {
// Attach
// Attach, start receiving
tcd_msg_receive(event->rhport, _rx_buf, sizeof(_rx_buf));
}else {
// Detach
}
break;
case TCD_EVENT_RX_COMPLETE:
// TODO process message here in ISR, move to thread later
if (event->rx_complete.result == XFER_RESULT_SUCCESS) {
parse_message(event->rhport, _rx_buf, event->rx_complete.xferred_bytes);
}
// start new rx
tcd_msg_receive(event->rhport, _rx_buf, sizeof(_rx_buf));
break;
default: break;
}
osal_queue_send(_utcd_q, event, in_isr);
}
//--------------------------------------------------------------------+

View File

@ -52,6 +52,17 @@ bool tuc_init(uint8_t rhport, uint32_t port_type);
// Check if typec port is initialized
bool tuc_inited(uint8_t rhport);
// Task function should be called in main/rtos loop, extended version of tud_task()
// - timeout_ms: millisecond to wait, zero = no wait, 0xFFFFFFFF = wait forever
// - in_isr: if function is called in ISR
void tuc_task_ext(uint32_t timeout_ms, bool in_isr);
// Task function should be called in main/rtos loop
TU_ATTR_ALWAYS_INLINE static inline
void tuc_task (void) {
tuc_task_ext(UINT32_MAX, false);
}
#ifndef _TUSB_TCD_H_
extern void tcd_int_handler(uint8_t rhport);
#endif
@ -59,10 +70,19 @@ extern void tcd_int_handler(uint8_t rhport);
// Interrupt handler, name alias to TCD
#define tuc_int_handler tcd_int_handler
//--------------------------------------------------------------------+
// Callbacks
//--------------------------------------------------------------------+
TU_ATTR_WEAK bool tuc_pd_data_received_cb(uint8_t rhport, pd_header_t const* header, uint8_t const* dobj, uint8_t const* p_end);
TU_ATTR_WEAK bool tuc_pd_control_received_cb(uint8_t rhport, pd_header_t const* header);
//--------------------------------------------------------------------+
//
//--------------------------------------------------------------------+
bool tuc_msg_request(uint8_t rhport, void const* rdo);
#ifdef __cplusplus
}