前言
2023年Zephyr开发者大会(ZDS)于6月27日至30日在捷克布拉格隆重举行。与以往两次不同,本次ZDS由Zephyr项目规划和管理,并作为首届嵌入式开源峰会(EOSS)的一部分进行。在这个令人期待已久的盛会中,全球Zephyr开发者们共同探讨了Zephyr实时操作系统(RTOS)的最新技术与发展趋势。作为一款开源、灵活和可扩展的嵌入式实时操作系统,Zephyr项目在2014年由英特尔发起,2016年作为Linux基金会项目正式面向公众启动,得到了全球范围内的广泛关注和采用。
ZDS 2023共70余个技术报告,涵盖了使用指导与展示、新功能与技术、架构修改与操作系统、多核异构与虚拟化、模拟器、测试、工业流程与代码管理、安全性、应用案例、工具与调试等丰富多样的内容。湖大嵌入式实验室的小伙伴们将对本次大会的所有技术报告进行逐一收集、整理与分享,尽最大努力为Zephyr开发者提供ZDS 2023技术报告的开发经验、实践成果以及解决方案的参考。
今天分享第40篇技术报告,由毕方琦整理,题目为:
“USB Type-C and You:将产品概念转化为实施的必需品”
作者简介
Sam Hurst, Google,Github ID: sambhurst
Diana Zigterman, Google, Github ID: DianaZig
端口作用回顾
|
|
|
|
下行端口(DFP):
1.充当USB主机或集线器下游端口
2.控制备用模式进入
|
来源:
1.为电子标记电缆提供Vconn
2.与电子标记电缆通信
|
|
|
|
更多详情,请参阅去年的USB-C Power Delivery Overview
规格要求
Accessing the Specifications
1. USB Type-C规格
2. USB送电规格
哪些USB Type-C状态适用?
1. 您想连接什么?
2. 附件模式?
3. 尝试状态(Try states)?
我是否属于“双重角色数据”?
1. 如果您的端口既能作为主机又能作为设备,则肯定是“是”。
2. 如果你还需要使用PD DR_Swap消息
因为您想使用非默认角色组合(sink/DFP)
或者因为您具有双重角色能力,首选数据角色
3. DR交换的注意事项:对方可能会回复 "拒绝"。
我是否必须提供Vconn电压?
1. 电源端口可参考USB Type-C规范
2. 如果需要探测电缆,汇接端口主要需要Vconn信号源
注意:即使关闭了Vconn,您的端口在Vconn交换时,您的端口仍被视为“Vconn源”。
我需要Power Delivery支持吗?
1. 如果您对默认的USB Type-C角色满意,并且不需要超过15W的供电或吸电能力,那么可能不需要。
2. 但是,如果您需要以下功能,则需要Power Delivery支持:
更多的功率
非默认的角色组合
备用模式
电源按钮传输
必须支持哪些PD信息?
参考PD规范中的“信息适用性”部分

USB Type-C下游设备应用
创建USB Type-C设备的三个步骤
USB Type-C设备树节点
Zephyr的USB Type-C端口视角

设备树USB Type-C下游端口描述


Type-C端口控制器(TCPC)驱动程序

VBUS驱动

应用程序特定的数据结构
检查是否支持电源角色
#define PORT1_NODE DT_NODELABEL(port1)
#define PORT1_POWER_ROLE DT_ENUM_IDX(DT_NODELABEL(port1), power_role)
#if (PORT1_POWER_ROLE != TC_ROLE_SINK)
#error "Unsupported board: Only Sink device supported"
#endif
port1: usbc-port@1 {
compatible = "usb-c-connector";
reg = <1>;
tcpc = <&ucpd1>;
vbus = <&vbus1>;
power-role = "sink";
sink-pdos = <PDO_FIXED(5000, 100, 0)>;
};
应用程序数据结构(提取下游PDO)
static struct port1_data_t {
uint32_t snk_caps[DT_PROP_LEN(DT_NODELABEL(port1), sink_pdos)];
int snk_cap_cnt;
} port1_data = {
.snk_caps = {DT_FOREACH_PROP_ELEM(DT_NODELABEL(port1), sink_pdos, SINK_PDO)},
.snk_cap_cnt = DT_PROP_LEN(DT_NODELABEL(port1), sink_pdos),
};
port1: usbc-port@1 {
compatible = "usb-c-connector";
reg = <1>;
tcpc = <&ucpd1>;
vbus = <&vbus1>;
power-role = "sink";
sink-pdos = <PDO_FIXED(5000, 100, 0)>;
};
应用程序数据结构(附加成员)
static struct port1_data_t {
...
uint32_t src_caps[PDO_MAX_DATA_OBJECTS];
int src_cap_cnt;
atomic_t ps_ready;
} port1_data = {
...
.src_caps = {0},
.src_cap_cnt = 0,
.ps_ready = 0
};
策略回调函数
最小下游策略回调函数
int (*policy_cb_get_snk_cap_t)(const struct device *dev, uint32_t **pdos, int *num_pdos);
void (*policy_cb_set_src_cap_t)(const struct device *dev, const uint32_t *pdos, const int num_pdos);
uint32_t (*policy_cb_get_rdo_t)(const struct device *dev);
bool (*policy_cb_check_t)(const struct device *dev, const enum usbc_policy_check_t policy_check);
void (*policy_cb_notify_t)(const struct device *dev, const enum usbc_policy_notify_t policy_notify);
电源传输协商(第一步)

policy_cb_set_src_cap_t
Zephyr USB Type-C PD Subsystem使用此回调函数将收到的Source capabilities发送给应用程序。对于这个应用程序,以下内容将被设置:
port1_data.src_caps
port1_data.src_cap_cnt
static struct port1_data_t {
...
uint32_t src_caps[PDO_MAX_DATA_OBJECTS];
int src_cap_cnt;
atomic_t ps_ready;
} port1_data = {
...
.src_caps = {0},
.src_cap_cnt = 0,
.ps_ready = 0
}
示例的源能力(Sample Source Capabilities)可能包括:
PDO1: 5V(ALWAYS 5V)
PDO2: 12
PDO3: 20
电源传输协商(第二步)

policy_cb_get_rdo_t
在存储了源PDO之后,应用程序必须选择其中一个并构建一个RDO(Requested Data Object)返回给协议栈。Zephyr使用此回调函数来获取该RDO。
union pd_rdo rdo;
/* Maximum operating current 100mA */
rdo.fixed.min_or_max_operating_current = PD_CONVERT_MA_TO_FIXED_PDO_CURRENT(100);
/* Operating current 100mA */
rdo.fixed.operating_current = PD_CONVERT_MA_TO_FIXED_PDO_CURRENT(100);
/* Object position 1 (5V PDO) */
rdo.fixed.object_pos = 1;
return rdo.raw_value;
电源传输协商(第三步)

policy_cb_notify_t
随着Zephyr USB Type-C子系统的执行,它会通知应用程序某些变化的发生。这就是此回调函数的用途。在接下来的情况下,应用程序会被通知已接收到一个Accept消息。
case MSG_ACCEPT_RECEIVED:
LOG_INF("Source accepted the request");
break;
case TRANSITION_PS:
atomic_set_bit(&dpm_data->ps_ready, 0);
break;
case NOT_PD_CONNECTED:
break;
case POWER_CHANGE_0A0:
LOG_INF("PWR 0A");
break;
case POWER_CHANGE_DEF:
LOG_INF("PWR DEF");
break;
电源传输协商(第四步)

policy_cb_notify_t
随着Zephyr USB Type-C子系统的执行,它会通知应用程序某些变化的发生。这就是此回调函数的用途。在接下来的情况下,应用程序会被通知已接收到一个PS_RDY消息。
case MSG_ACCEPT_RECEIVED:
LOG_INF("Source accepted the request");
break;
case TRANSITION_PS:
atomic_set_bit(&dpm_data->ps_ready, 0);
break;
case NOT_PD_CONNECTED:
break;
case POWER_CHANGE_0A0:LOG_INF("PWR 0A");
break;
case POWER_CHANGE_DEF:
LOG_INF("PWR DEF");
break;
获取下游能力

policy_cb_get_snk_cap_t
Zephyr USB Type-C PD堆栈使用此回调函数来获取下游PDO以及它们的数量。对于这个应用程序,将返回以下内容:
port1_data.snk_caps
port1_data.snk_cap_cnt
static struct port1_data_t {
uint32_t snk_caps[DT_PROP_LEN(DT_NODELABEL(port1), sink_pdos)];
int snk_cap_cnt;
} port1_data = {
.snk_caps = {DT_FOREACH_PROP_ELEM(DT_NODELABEL(port1), sink_pdos, SINK_PDO)},
.snk_cap_cnt = DT_PROP_LEN(DT_NODELABEL(port1), sink_pdos),
};
电源角色切换为源

policy_cb_check_t
随着Zephyr USB Type-C堆栈的执行,它需要在执行某些操作之前与应用程序进行确认。这就是此回调函数的用途。
switch (policy_check) {
case CHECK_POWER_ROLE_SWAP:
/* Reject power role swaps */
return false;
case CHECK_DATA_ROLE_SWAP_TO_DFP:
/* Reject data role swap to DFP */
return false;
case CHECK_DATA_ROLE_SWAP_TO_UFP:
/* Accept data role swap to UFP */
return true;
case CHECK_SNK_AT_DEFAULT_LEVEL:
/* The device is at the default power level */
return true;
default:
/* Reject all other policy checks */
return false;
}
注册以下API的回调函数
usbc_set_policy_cb_set_src_cap
usbc_set_policy_cb_get_rdo
usbc_set_policy_cb_get_snk_cap
usbc_set_policy_cb_notify
usbc_set_policy_cb_check
注册应用程序的数据结构
usbc_set_dpm_data
注册后,应用程序的数据结构可以通过以下内容:
usbc_get_dpm_data
启动Zephyr USB Type-C堆栈
usbc_start
Zephyr USB Type-C示例
Sample Sink: zephyr/samples/subsys/sink
Sample Source: zephyr/samples/subsys/source
Zephyr USB Type-C开发板
