这段代码实现了 USB DAP 协议的核心功能:
- 请求接收:通过 OUT 端点回调(
dap_out_callback)将数据存入环形队列,队列满时暂停接收(USB_RequestIdle),处理完成后恢复。 - 请求处理:主函数(
chry_dap_handle)循环处理队列中的请求,生成响应,并协调队列状态。 - 响应发送:通过 IN 端点回调(
dap_in_callback)将响应发送回主机,队列为空时暂停发送(USB_ResponseIdle),新响应生成后恢复。
通过环形队列和空闲标志的流量控制,确保了 USB 通信的可靠性和效率,避免了数据溢出或丢失。
我将从代码结构、核心逻辑、关键变量和函数协作四个维度,详细解析这段代码的功能和实现逻辑。
一、代码整体结构
这段代码包含 3 个核心函数,共同实现 USB DAP(调试访问端口)协议的请求接收、处理和响应发送功能:
- chry_dap_handle:主处理函数,负责循环处理待处理的 USB 请求,并生成响应。
- dap_out_callback:USB OUT 端点的回调函数(接收数据时触发),负责将接收到的请求存入队列,并控制是否继续接收新请求。
- dap_in_callback:USB IN 端点的回调函数(发送完成时触发),负责将响应队列中的数据发送回主机,并控制是否继续发送新响应。
二、关键变量说明
理解这些变量是解析代码的基础(均为全局或静态变量):
| 变量名 | 类型 | 作用 |
|---|---|---|
USB_RequestCountI | uint32_t | 已接收但未处理的请求总数(输入计数) |
USB_RequestCountO | uint32_t | 已处理完成的请求总数(输出计数) |
USB_RequestIndexI | uint32_t | 下一个待存储新请求的队列索引(写指针) |
USB_RequestIndexO | uint32_t | 下一个待处理请求的队列索引(读指针) |
USB_RequestIdle | uint32_t | 请求队列的空闲标志(1 = 队列满暂停接收,0 = 正常接收) |
DAP_PACKET_COUNT | 常量(宏) | 请求队列的总容量(缓冲区大小,如 8 或 16 个请求) |
DAP_PACKET_SIZE | 常量(宏) | 单个请求的最大字节数(如 64 字节) |
USB_ResponseCountI | uint32_t | 已生成但未发送的响应总数(输入计数) |
USB_ResponseCountO | uint32_t | 已发送完成的响应总数(输出计数) |
USB_ResponseIndexI | uint32_t | 下一个待存储新响应的队列索引(写指针) |
USB_ResponseIndexO | uint32_t | 下一个待发送响应的队列索引(读指针) |
USB_ResponseIdle | uint32_t | 响应队列的空闲标志(1 = 响应队列为空暂停发送,0 = 正常发送) |
USB_RespSize | uint16_t[] | 记录每个响应的实际字节数(避免发送无效数据) |
三、核心函数解析
1. chry_dap_handle:请求处理主循环
函数作用:循环处理请求队列中的未处理请求,生成响应,并协调请求 / 响应队列的流量控制。
关键逻辑拆解:
- 循环处理未完成请求
while (USB_RequestCountI != USB_RequestCountO):只要有未处理的请求(输入计数 ≠ 输出计数),就持续处理。 队列命令预处理(ID_DAP_QueueCommands) 当遇到
ID_DAP_QueueCommands(批量队列命令)时,将其转换为ID_DAP_ExecuteCommands(执行命令),实现批量命令的合并处理。while (USB_Request[n][0] == ID_DAP_QueueCommands) { USB_Request[n][0] = ID_DAP_ExecuteCommands; // 转换为执行命令 n++; // 移动到下一个请求 // 处理环形队列的索引循环 if (n == DAP_PACKET_COUNT) n = 0U; // 防止越界(当队列被填满时退出循环) if (n == USB_RequestIndexI) break; }执行 DAP 命令并生成响应 调用
DAP_ExecuteCommand处理当前请求(USB_Request[USB_RequestIndexO]),生成响应数据(存入USB_Response[USB_ResponseIndexI]),并记录响应长度(USB_RespSize[USB_ResponseIndexI])。USB_RespSize[USB_ResponseIndexI] = (uint16_t)DAP_ExecuteCommand(USB_Request[USB_RequestIndexO], USB_Response[USB_ResponseIndexI]);更新请求队列状态
- 读指针(
USB_RequestIndexO)递增,循环到队列头部(环形队列)。 - 输出计数(
USB_RequestCountO)递增,表示 1 个请求已处理完成。
USB_RequestIndexO++; if (USB_RequestIndexO == DAP_PACKET_COUNT) USB_RequestIndexO = 0U; USB_RequestCountO++;- 读指针(
恢复请求接收(关键流量控制) 如果此前因队列满暂停接收(
USB_RequestIdle == 1),且当前队列有空间(USB_RequestCountI - USB_RequestCountO < DAP_PACKET_COUNT),则清除空闲标志并重启 OUT 端点的读取。if (USB_RequestIdle) { if ((uint16_t)(USB_RequestCountI - USB_RequestCountO) != DAP_PACKET_COUNT) { USB_RequestIdle = 0U; usbd_ep_start_read(DAP_OUT_EP, USB_Request[USB_RequestIndexI], DAP_PACKET_SIZE); } }更新响应队列状态
- 写指针(
USB_ResponseIndexI)递增,循环到队列头部。 - 输入计数(
USB_ResponseCountI)递增,表示 1 个响应已生成。
USB_ResponseIndexI++; if (USB_ResponseIndexI == DAP_PACKET_COUNT) USB_ResponseIndexI = 0U; USB_ResponseCountI++;- 写指针(
触发响应发送(IN 端点流量控制) 如果此前因响应队列为空暂停发送(
USB_ResponseIdle == 1),且当前有未发送的响应(USB_ResponseCountI != USB_ResponseCountO),则清除空闲标志并启动 IN 端点的写入。if (USB_ResponseIdle) { if (USB_ResponseCountI != USB_ResponseCountO) { // 取出下一个待发送的响应 n = USB_ResponseIndexO++; if (USB_ResponseIndexO == DAP_PACKET_COUNT) USB_ResponseIndexO = 0U; USB_ResponseCountO++; USB_ResponseIdle = 0U; usbd_ep_start_write(DAP_IN_EP, USB_Response[n], USB_RespSize[n]); } }
2. dap_out_callback:OUT 端点接收回调
函数作用:当 USB 主机向设备发送数据(通过 OUT 端点)时触发,负责将数据存入请求队列,并控制是否继续接收新数据。
关键逻辑拆解:
处理传输中止命令 如果接收到的请求首字节是
ID_DAP_TransferAbort(中止传输命令),则设置全局标志DAP_TransferAbort = 1U,通知上层停止当前传输。if (USB_Request[USB_RequestIndexI][0] == ID_DAP_TransferAbort) { DAP_TransferAbort = 1U; }正常接收请求 否则,更新请求队列的写指针(
USB_RequestIndexI)和输入计数(USB_RequestCountI),表示新请求已存入队列。else { USB_RequestIndexI++; if (USB_RequestIndexI == DAP_PACKET_COUNT) USB_RequestIndexI = 0U; USB_RequestCountI++; }流量控制:队列满时暂停接收 检查当前队列是否已满(
USB_RequestCountI - USB_RequestCountO == DAP_PACKET_COUNT):- 未满:继续启动 OUT 端点的读取(
usbd_ep_start_read),准备接收下一个请求。 - 已满:设置
USB_RequestIdle = 1U,暂停接收(避免覆盖未处理的请求)。
if ((uint16_t)(USB_RequestCountI - USB_RequestCountO) != DAP_PACKET_COUNT) { usbd_ep_start_read(DAP_OUT_EP, USB_Request[USB_RequestIndexI], DAP_PACKET_SIZE); } else { USB_RequestIdle = 1U; }- 未满:继续启动 OUT 端点的读取(
3. dap_in_callback:IN 端点发送回调
函数作用:当 USB 设备向主机发送数据(通过 IN 端点)完成时触发,负责将下一个响应数据发送到 IN 端点,并控制是否继续发送。
关键逻辑拆解:
有未发送的响应时 如果响应队列中有未发送的响应(
USB_ResponseCountI != USB_ResponseCountO),则取出下一个响应(USB_Response[USB_ResponseIndexO]),启动 IN 端点的写入(usbd_ep_start_write),并更新读指针(USB_ResponseIndexO)和输出计数(USB_ResponseCountO)。if (USB_ResponseCountI != USB_ResponseCountO) { usbd_ep_start_write(DAP_IN_EP, USB_Response[USB_ResponseIndexO], USB_RespSize[USB_ResponseIndexO]); USB_ResponseIndexO++; if (USB_ResponseIndexO == DAP_PACKET_COUNT) USB_ResponseIndexO = 0U; USB_ResponseCountO++; }无未发送的响应时 如果响应队列为空(所有响应已发送),设置
USB_ResponseIdle = 1U,暂停发送(避免发送无效数据)。else { USB_ResponseIdle = 1U; }
四、函数协作与流量控制
整个代码通过环形队列和空闲标志实现请求 / 响应的可靠传输,核心协作逻辑如下:
请求接收流程
- 主机通过 USB OUT 端点发送请求 → 触发
dap_out_callback。 dap_out_callback将请求存入队列(USB_Request),并更新USB_RequestIndexI和USB_RequestCountI。- 如果队列未满(
USB_RequestCountI - USB_RequestCountO < DAP_PACKET_COUNT),继续启动 OUT 端点读取(准备接收下一个请求);否则设置USB_RequestIdle = 1U暂停接收。 chry_dap_handle循环处理队列中的请求(USB_RequestCountI != USB_RequestCountO),处理完成后更新USB_RequestCountO。- 当队列腾出空间(
USB_RequestCountI - USB_RequestCountO < DAP_PACKET_COUNT),chry_dap_handle清除USB_RequestIdle并重启 OUT 端点读取,恢复接收。
响应发送流程
chry_dap_handle处理请求后生成响应(存入USB_Response),并更新USB_ResponseIndexI和USB_ResponseCountI。- 主机通过 USB IN 端点请求响应 → 触发
dap_in_callback(或设备主动发送)。 dap_in_callback检查响应队列:若有未发送的响应(USB_ResponseCountI != USB_ResponseCountO),则启动 IN 端点写入(发送当前响应),并更新USB_ResponseIndexO和USB_ResponseCountO;否则设置USB_ResponseIdle = 1U暂停发送。- 当新的响应生成(
USB_ResponseCountI递增),chry_dap_handle检查到USB_ResponseIdle == 1且有未发送响应时,清除USB_ResponseIdle并触发 IN 端点发送。
五、总结
这段代码实现了 USB DAP 协议的核心功能:
- 请求接收:通过 OUT 端点回调(
dap_out_callback)将数据存入环形队列,队列满时暂停接收(USB_RequestIdle),处理完成后恢复。 - 请求处理:主函数(
chry_dap_handle)循环处理队列中的请求,生成响应,并协调队列状态。 - 响应发送:通过 IN 端点回调(
dap_in_callback)将响应发送回主机,队列为空时暂停发送(USB_ResponseIdle),新响应生成后恢复。
通过环形队列和空闲标志的流量控制,确保了 USB 通信的可靠性和效率,避免了数据溢出或丢失。
来自deepseek分析
void chry_dap_handle(void)
{
uint32_t n;
// Process pending requests
while (USB_RequestCountI != USB_RequestCountO) {//判断请求输入计数和输出计数是否相等
// Handle Queue Commands
n = USB_RequestIndexO;//待处理请求位置索引,保证请求按顺序处理,在处理完数据时+1
//将索引位置之后的批量转换为为可执行命令
while (USB_Request[n][0] == ID_DAP_QueueCommands) {
USB_Request[n][0] = ID_DAP_ExecuteCommands;//将队列命令转换为可执行命令,方便后续处理
n++;
if (n == DAP_PACKET_COUNT) {
n = 0U;
}
if (n == USB_RequestIndexI) {
// flags = osThreadFlagsWait(0x81U, osFlagsWaitAny, osWaitForever);
// if (flags & 0x80U) {
// break;
// }
}
}
//处理DAP可执行命令,USB_ResponseIndexI记录待响应的索引位置
// Execute DAP Command (process request and prepare response)
USB_RespSize[USB_ResponseIndexI] = (uint16_t)DAP_ExecuteCommand(USB_Request[USB_RequestIndexO], USB_Response[USB_ResponseIndexI]);
// Update Request Index and Count
USB_RequestIndexO++;
if (USB_RequestIndexO == DAP_PACKET_COUNT) {
USB_RequestIndexO = 0U;
}
USB_RequestCountO++;
if (USB_RequestIdle) {//USB_RequestIdle未处理的USB_Request达到了最大值DAP_PACKET_COUNT,置1,暂停读取OUT事务
if ((uint16_t)(USB_RequestCountI - USB_RequestCountO) != DAP_PACKET_COUNT) {//判断没溢出,接触警报,重新开启接收
USB_RequestIdle = 0U;
usbd_ep_start_read(DAP_OUT_EP, USB_Request[USB_RequestIndexI], DAP_PACKET_SIZE);//设置准备接收数据地址,长度,并允许回应主机ACK
}
}
// Update Response Index and Count
USB_ResponseIndexI++;
if (USB_ResponseIndexI == DAP_PACKET_COUNT) {
USB_ResponseIndexI = 0U;
}
USB_ResponseCountI++;
if (USB_ResponseIdle) {//USB_ResponseIdle =1表示无数据可发了,所有请求都处理完成了
if (USB_ResponseCountI != USB_ResponseCountO) {//判断有数据可以发了
// Load data from response buffer to be sent back
n = USB_ResponseIndexO++;
if (USB_ResponseIndexO == DAP_PACKET_COUNT) {
USB_ResponseIndexO = 0U;
}
USB_ResponseCountO++;
USB_ResponseIdle = 0U;
usbd_ep_start_write(DAP_IN_EP, USB_Response[n], USB_RespSize[n]);
}
}
}
}void dap_out_callback (uint8_t ep, uint32_t nbytes) { // 先把数据处理了,过程中USB中断一直NAK,然后再开启下一次的数据读取准备,之后再允许USB ACK
if (USB_Request[USB_RequestIndexI][0] == ID_DAP_TransferAbort) {
DAP_TransferAbort = 1U;
} else {
USB_RequestIndexI++;
if (USB_RequestIndexI == DAP_PACKET_COUNT) {
USB_RequestIndexI = 0U;
}
USB_RequestCountI++;
#ifdef CONFIG_CHERRYDAP_USE_OS
// osThreadFlagsSet(DAP_ThreadId, 0x01);
usb_osal_sem_give (dap_sem);
#endif
}
// Start reception of next request packet
if ((uint16_t)(USB_RequestCountI - USB_RequestCountO) != DAP_PACKET_COUNT) {
usbd_ep_start_read (DAP_OUT_EP, USB_Request[USB_RequestIndexI], DAP_PACKET_SIZE);
} else {
USB_RequestIdle = 1U;
}
}void dap_in_callback (uint8_t ep, uint32_t nbytes) {
if (USB_ResponseCountI != USB_ResponseCountO) {
// Load data from response buffer to be sent back
usbd_ep_start_write (DAP_IN_EP, USB_Response[USB_ResponseIndexO], USB_RespSize[USB_ResponseIndexO]);
USB_ResponseIndexO++;
if (USB_ResponseIndexO == DAP_PACKET_COUNT) {
USB_ResponseIndexO = 0U;
}
USB_ResponseCountO++;
} else {
USB_ResponseIdle = 1U;
}
}所属系列
从当前文章继续阅读它所在合集中的前后内容。
本作品集介绍USB基础知识,解析USB通信协议,学习USB工作原理,制作USB设备,编写USB上位机软件。
相关文章
优先推荐同专题、同标签和同作者内容,补足热门文章。