USB·

DAPLink与USB主机、目标设备之间的数据交互异步处理流程

Hugh

Hugh

247 0

这段代码实现了 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_RequestCountIuint32_t已接收但未处理的请求总数(输入计数)
USB_RequestCountOuint32_t已处理完成的请求总数(输出计数)
USB_RequestIndexIuint32_t下一个待存储新请求的队列索引(写指针)
USB_RequestIndexOuint32_t下一个待处理请求的队列索引(读指针)
USB_RequestIdleuint32_t请求队列的空闲标志(1 = 队列满暂停接收,0 = 正常接收)
DAP_PACKET_COUNT常量(宏)请求队列的总容量(缓冲区大小,如 8 或 16 个请求)
DAP_PACKET_SIZE常量(宏)单个请求的最大字节数(如 64 字节)
USB_ResponseCountIuint32_t已生成但未发送的响应总数(输入计数)
USB_ResponseCountOuint32_t已发送完成的响应总数(输出计数)
USB_ResponseIndexIuint32_t下一个待存储新响应的队列索引(写指针)
USB_ResponseIndexOuint32_t下一个待发送响应的队列索引(读指针)
USB_ResponseIdleuint32_t响应队列的空闲标志(1 = 响应队列为空暂停发送,0 = 正常发送)
USB_RespSizeuint16_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;
    }

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

四、函数协作与流量控制

整个代码通过环形队列空闲标志实现请求 / 响应的可靠传输,核心协作逻辑如下:

请求接收流程

  1. 主机通过 USB OUT 端点发送请求 → 触发 dap_out_callback
  2. dap_out_callback 将请求存入队列(USB_Request),并更新 USB_RequestIndexIUSB_RequestCountI
  3. 如果队列未满(USB_RequestCountI - USB_RequestCountO < DAP_PACKET_COUNT),继续启动 OUT 端点读取(准备接收下一个请求);否则设置 USB_RequestIdle = 1U 暂停接收。
  4. chry_dap_handle 循环处理队列中的请求(USB_RequestCountI != USB_RequestCountO),处理完成后更新 USB_RequestCountO
  5. 当队列腾出空间(USB_RequestCountI - USB_RequestCountO < DAP_PACKET_COUNT),chry_dap_handle 清除 USB_RequestIdle 并重启 OUT 端点读取,恢复接收。

响应发送流程

  1. chry_dap_handle 处理请求后生成响应(存入 USB_Response),并更新 USB_ResponseIndexIUSB_ResponseCountI
  2. 主机通过 USB IN 端点请求响应 → 触发 dap_in_callback(或设备主动发送)。
  3. dap_in_callback 检查响应队列:若有未发送的响应(USB_ResponseCountI != USB_ResponseCountO),则启动 IN 端点写入(发送当前响应),并更新 USB_ResponseIndexOUSB_ResponseCountO;否则设置 USB_ResponseIdle = 1U 暂停发送。
  4. 当新的响应生成(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 第 8 / 9 篇
查看合集

本作品集介绍USB基础知识,解析USB通信协议,学习USB工作原理,制作USB设备,编写USB上位机软件。

相关文章

优先推荐同专题、同标签和同作者内容,补足热门文章。

评论 0

登录 后参与评论

评论

成为第一个评论的人