我们遇到的问题是在使用cdc_acm_template.c进行测试时,热插拔后出现的问题。具体表现为:
热插拔后,PC主机端可以识别设备,可以打开->关闭->打开串口,但是当PC主机端主动发送数据时,就提示串口号故障,然后尝试自动修复(关闭再打开串口)。而与此同时,从MCU的USB设备端(usbd)主动发送数据到PC主机端,主机却能正常接收。另外,DTR控制信号(属于控制传输)也能正常从主机发送到设备。
热插拔前,双向通信都正常;热插拔后,只有设备到主机的通信正常,主机到设备的数据传输出现故障。
根据描述,问题可能出现在以下几个方面:
1. **USB设备配置**:热插拔后,设备重新枚举,但可能存在配置不正确的地方,特别是端点配置。
2. **端点状态**:热插拔后,主机到设备的端点(通常是OUT端点)可能没有正确配置或激活。
3. **主机驱动问题**:主机端的CDC驱动程序在热插拔后可能没有正确处理OUT端点。
4. **设备固件处理**:设备固件在热插拔后可能没有正确重置端点或重新初始化。
在cdc_acm_template.c中,我们需要关注以下几个部分:
- **配置描述符**:确保端点的地址和属性设置正确。
- **端点最大包长度**:必须与主机端期望的一致(CDC通常使用64字节)。
- **重新枚举的处理**:设备在热插拔后需要重新枚举,固件中应该正确处理复位事件和设置配置事件。
根据问题描述,热插拔后设备能够被识别,说明枚举过程基本成功,而且控制传输(如DTR)正常,说明控制端点是正常的。问题出在主机发送数据(即OUT传输)时。
在CDC ACM中,主机到设备的数据传输使用BULK OUT端点,设备到主机使用BULK IN端点。
因此,我们需要检查:
1. 在热插拔后,当主机发送数据时,设备是否收到了OUT端点的数据?如果没有,可能是主机没有发送,也可能是设备没有正确接收。
但是问题描述中,主机在发送数据时出现故障提示,所以更可能是主机端驱动在准备发送时遇到了问题。
然而,我们注意到设备端发送数据到主机是正常的,说明IN端点工作正常。
所以,我们可以将问题定位在OUT端点上。
可能的原因:
- 热插拔后,主机驱动试图使用一个无效的端点句柄或管道。
- 设备在重新枚举后,OUT端点的状态没有被正确重置(比如NAK状态,导致主机无法发送)。
- 在设备重新连接后,主机驱动在打开串口时没有正确配置串口参数(如波特率、数据位等),但是描述中提到可以打开关闭打开串口,而且控制传输正常,所以这个可能性较小。
在设备固件中,我们需要确保:
- 在USB复位事件中,重新初始化端点,特别是OUT端点,使其准备好接收数据(即置为有效状态,等待主机发送)。
- 检查端点描述符是否正确。
在cdc_acm_template.c中,通常会有以下复位处理:
在`USBD_CDC_ACM_Reset`函数中,应该重新配置端点,包括设置OUT端点为接收状态。
例如,在STM32的USB设备库中,复位回调函数中会重新打开端点:
```c
static int8_t CDC_DeInit(void)
{
/* ... */
}
static int8_t CDC_Reset(void)
{
/* Open EP IN */
USBD_LL_OpenEP(&hUsbDeviceFS, CDC_IN_EP, USBD_EP_TYPE_BULK, CDC_DATA_HS_MAX_PACKET_SIZE);
/* Open EP OUT */
USBD_LL_OpenEP(&hUsbDeviceFS, CDC_OUT_EP, USBD_EP_TYPE_BULK, CDC_DATA_HS_MAX_PACKET_SIZE);
/* ... */
}
```
但是,请注意:在热插拔后,设备会经历一次复位,然后重新枚举。因此,确保复位函数中正确打开端点非常重要。
另外,检查一下在枚举完成后,主机在设置配置(SET_CONFIGURATION)后,设备是否准备好OUT端点。通常,在设置配置后,设备应该准备好OUT端点以接收数据。
在模板中,可能有`CDC_Setup`函数处理设置配置请求。在设置配置请求中,应该打开端点。
如果复位函数中已经打开了端点,那么设置配置中可能不需要再次打开。但具体要看实现。
另一种可能是,主机在热插拔后,尝试发送数据时使用了之前的管道(pipe)配置,而新枚举的设备使用了不同的配置(比如端点地址相同,但上下文已经改变),导致主机发送失败。
但是,根据CDC ACM规范,端点地址在重新枚举后应该是一样的(固件中定义不变),所以主机应该能够正确使用。
建议的调试步骤:
1. **检查复位回调**:确保在USB复位时,端点被正确关闭然后重新打开。在复位回调中,应该关闭所有非0端点(除了控制端点0),然后在后续的枚举过程中重新打开。有些实现是在设置配置时打开端点的。
2. **检查设置配置回调**:当主机发送设置配置请求(SetConfiguration)时,设备应该打开端点。
3. **检查主机端**:使用USB分析仪或软件(如WireShark+USBPcap)捕获USB数据包,观察热插拔后枚举过程是否正常,以及当主机发送数据时发生了什么错误(比如主机是否发送了OUT令牌包,设备是否响应NAK或其他)。
4. **设备端调试**:在设备端,检查当主机发送数据时,是否进入了OUT端点回调函数。如果没有,说明主机根本就没有发送数据,或者端点没有正确配置。
5. **检查端点最大包长度**:确保在描述符中定义的OUT端点最大包长度与主机期望的一致(一般是64字节)。如果描述符中定义的长度与主机驱动期望的不一致,可能会导致问题。
6. **主机驱动日志**:在主机端,查看设备管理器中串口设备的属性,确认无错误;同时可以查看系统日志,看是否有驱动程序错误。
由于问题描述中热插拔前正常,热插拔后出现问题,重点应放在设备在复位和重新枚举后的状态恢复上。
在cdc_acm_template.c中,通常有一个结构体用于管理CDC状态,比如:
```c
typedef struct {
uint8_t data[CDC_DATA_HS_MAX_PACKET_SIZE];
uint32_t len;
uint8_t ep_in;
uint8_t ep_out;
uint8_t ctrl_line_state;
} USBD_CDC_HandleTypeDef;
```
在重新枚举后,这个结构体是否被正确初始化?特别是`len`和缓冲区,以及控制线状态。
在收到复位事件时,应该重置这个结构体。
另外,在打开OUT端点时,应该准备好接收数据,即提交一个OUT请求(对于某些USB库,需要调用函数来接收数据)。例如,在STM32的USB设备库中,通常在初始化OUT端点后立即调用`USBD_LL_PrepareReceive`。
例如:
```c
USBD_LL_PrepareReceive(pdev, CDC_OUT_EP, hcdc->data, CDC_DATA_HS_MAX_PACKET_SIZE);
```
在复位回调或设置配置完成后,应该提交这个请求。这样,当主机发送数据时,设备才能接收。
如果热插拔后没有提交OUT端点的接收请求,那么设备将不会响应主机的OUT事务(即不会提供缓冲区),主机就会认为设备没有准备好,进而导致传输失败。
所以,请检查在热插拔后(即复位和重新枚举后),设备是否重新提交了OUT端点的接收请求。
总结:
可能的原因是热插拔后,设备没有重新为OUT端点提交接收请求,导致主机发送数据时设备没有响应(NAK),主机驱动在多次尝试后认为设备故障,从而报告串口故障并尝试重新打开。
请检查设备固件中,在复位事件处理或设置配置事件处理中,是否调用了准备接收数据的函数(如`USBD_LL_PrepareReceive`)。
在cdc_acm_template.c中,查找`CDC_Reset`或`CDC_Init`函数,看是否有提交OUT端点的接收请求。
如果没有,则需要在枚举完成后(比如在设置配置完成后)提交。
在`CDC_Setup`函数中,处理设置配置请求(通常请求号为`USB_REQ_SET_CONFIGURATION`)时,可以提交OUT端点的接收请求。
或者,在复位处理中,除了打开端点,还需要提交接收请求吗?注意:复位后端点处于关闭状态,需要先打开端点,然后才能提交接收请求。但是复位处理中打开端点后,紧接着的枚举过程会设置配置,在设置配置后提交接收请求更为合理。
因此,建议在设置配置处理中提交OUT端点的接收请求。
示例:
在`CDC_Setup`函数中:
```c
if (req->bRequest == USB_REQ_SET_CONFIGURATION) {
// 打开端点(如果之前没打开)
// 然后,准备接收
USBD_LL_PrepareReceive(pdev, CDC_OUT_EP, hcdc->data, CDC_DATA_HS_MAX_PACKET_SIZE);
}
```
或者,在`CDC_Init`函数中(如果有这个回调函数的话,它会在设置配置后被调用)进行接收请求的提交。
请根据具体的模板实现进行调整。
如果问题仍然存在,请检查端点描述符是否正确,以及端点地址是否一致。
希望这些分析能帮助解决问题。
根据您的描述,问题出现在热插拔后主机到设备(PC到MCU)的数据传输失败,而设备到主机(MCU到PC)的传输正常。结合cdc_acm_template.c的实现和CDC-ACM协议,以下是可能的原因和解决方案:
问题根本原因
热插拔后OUT端点未正确激活
CDC-ACM通信包含两个关键端点:
- IN端点 (设备->主机):热插拔后仍能工作,说明IN端点配置正确。
- OUT端点 (主机->设备):热插拔后失效,表明OUT端点在重新枚举后未被正确初始化或激活。
主机驱动状态不一致
热插拔可能导致主机CDC驱动缓存了旧端点状态,发送数据时使用了无效的管道(pipe)句柄。
设备端未重新提交OUT端点请求
设备固件需在枚举完成后主动提交OUT端点接收请求(如USBD_LL_PrepareReceive)。热插拔后若未重新提交,主机数据无法被接收。
解决方案
1. 检查端点初始化代码
在USB复位(Reset)或配置设置(SetConfig)回调中,确保OUT端点被显式打开并提交接收请求:
// 在 CDC_Init() 或 CDC_Reset() 函数中添加:
static int8_t CDC_Init(void) {
// ... 其他初始化代码
/* 打开OUT端点并准备接收数据 */
USBD_LL_OpenEP(pdev, CDC_OUT_EP, USBD_EP_TYPE_BULK, CDC_DATA_MAX_PACKET_SIZE);
USBD_LL_PrepareReceive(pdev, CDC_OUT_EP, hcdc->RxBuffer, CDC_DATA_MAX_PACKET_SIZE);
return USBD_OK;
}
2. 验证控制信号处理
确保dtr信号处理正确,特别是热插拔后控制请求(SET_CONTROL_LINE_STATE)的响应:
static int8_t CDC_Control(...) {
case SET_CONTROL_LINE_STATE:
hcdc->ctrl_line = req->wValue;
// 必须返回正确响应
USBD_CtlSendData(pdev, NULL, 0);
break;
}
3. 端点描述符检查
确认配置描述符中端点的方向和类型正确:
/* 配置描述符片段 */
0x07, // 端点描述符长度
USB_DESC_TYPE_ENDPOINT,
CDC_OUT_EP, // OUT端点地址 (0x01)
0x02, // 批量传输类型 (BULK)
0x0040, // 最大包大小 (64字节)
0x00, // 轮询间隔
4. 主机端清理操作
热插拔后执行以下步骤清除主机驱动状态:
- 卸载残留驱动:
# Windows (管理员权限)
devcon remove USBVID_xxxx&PID_xxxx
- 重置USB控制器:
- 设备管理器 → USB控制器 → 卸载设备 → 扫描硬件更改。
调试建议
设备端日志
通过调试器检查热插拔后:
USBD_Reset和CDC_Init是否被调用。
USBD_LL_PrepareReceive是否执行且无错误。
USB协议分析
使用Wireshark+USBPcap捕获USB流量,验证:
- 主机是否发送了OUT数据包。
- 设备是否返回ACK/NAK。
简化测试代码
在CDC_ReceiveCallback中添加日志,确认是否收到主机数据:
static int8_t CDC_ReceiveCallback(...) {
log("Received %d bytes", Len);
// 重新提交接收请求
USBD_LL_PrepareReceive(pdev, CDC_OUT_EP, hcdc->RxBuffer, CDC_DATA_MAX_PACKET_SIZE);
}
关键代码修正示例
在cdc_acm_template.c中确保以下关键点:
// 步骤1: 复位时重置状态
void CDC_Reset(void) {
// 关闭旧端点 (如果已打开)
USBD_LL_CloseEP(pdev, CDC_OUT_EP);
// 打开并初始化OUT端点
USBD_LL_OpenEP(pdev, CDC_OUT_EP, USBD_EP_TYPE_BULK, CDC_DATA_MAX_PACKET_SIZE);
}
// 步骤2: 配置完成后提交接收请求
static int8_t CDC_Init(void) {
USBD_LL_PrepareReceive(pdev, CDC_OUT_EP, hcdc->RxBuffer, CDC_DATA_MAX_PACKET_SIZE);
return USBD_OK;
}
// 步骤3: 每次接收完成后重新提交请求
static int8_t CDC_ReceiveCallback(...) {
// 处理数据...
USBD_LL_PrepareReceive(pdev, CDC_OUT_EP, hcdc->RxBuffer, CDC_DATA_MAX_PACKET_SIZE);
}
通过以上修正,热插拔后OUT端点将正确接收主机数据,解决单向通信问题。
我们遇到的问题是在使用cdc_acm_template.c进行测试时,热插拔后出现的问题。具体表现为:
热插拔后,PC主机端可以识别设备,可以打开->关闭->打开串口,但是当PC主机端主动发送数据时,就提示串口号故障,然后尝试自动修复(关闭再打开串口)。而与此同时,从MCU的USB设备端(usbd)主动发送数据到PC主机端,主机却能正常接收。另外,DTR控制信号(属于控制传输)也能正常从主机发送到设备。
热插拔前,双向通信都正常;热插拔后,只有设备到主机的通信正常,主机到设备的数据传输出现故障。
根据描述,问题可能出现在以下几个方面:
1. **USB设备配置**:热插拔后,设备重新枚举,但可能存在配置不正确的地方,特别是端点配置。
2. **端点状态**:热插拔后,主机到设备的端点(通常是OUT端点)可能没有正确配置或激活。
3. **主机驱动问题**:主机端的CDC驱动程序在热插拔后可能没有正确处理OUT端点。
4. **设备固件处理**:设备固件在热插拔后可能没有正确重置端点或重新初始化。
在cdc_acm_template.c中,我们需要关注以下几个部分:
- **配置描述符**:确保端点的地址和属性设置正确。
- **端点最大包长度**:必须与主机端期望的一致(CDC通常使用64字节)。
- **重新枚举的处理**:设备在热插拔后需要重新枚举,固件中应该正确处理复位事件和设置配置事件。
根据问题描述,热插拔后设备能够被识别,说明枚举过程基本成功,而且控制传输(如DTR)正常,说明控制端点是正常的。问题出在主机发送数据(即OUT传输)时。
在CDC ACM中,主机到设备的数据传输使用BULK OUT端点,设备到主机使用BULK IN端点。
因此,我们需要检查:
1. 在热插拔后,当主机发送数据时,设备是否收到了OUT端点的数据?如果没有,可能是主机没有发送,也可能是设备没有正确接收。
但是问题描述中,主机在发送数据时出现故障提示,所以更可能是主机端驱动在准备发送时遇到了问题。
然而,我们注意到设备端发送数据到主机是正常的,说明IN端点工作正常。
所以,我们可以将问题定位在OUT端点上。
可能的原因:
- 热插拔后,主机驱动试图使用一个无效的端点句柄或管道。
- 设备在重新枚举后,OUT端点的状态没有被正确重置(比如NAK状态,导致主机无法发送)。
- 在设备重新连接后,主机驱动在打开串口时没有正确配置串口参数(如波特率、数据位等),但是描述中提到可以打开关闭打开串口,而且控制传输正常,所以这个可能性较小。
在设备固件中,我们需要确保:
- 在USB复位事件中,重新初始化端点,特别是OUT端点,使其准备好接收数据(即置为有效状态,等待主机发送)。
- 检查端点描述符是否正确。
在cdc_acm_template.c中,通常会有以下复位处理:
在`USBD_CDC_ACM_Reset`函数中,应该重新配置端点,包括设置OUT端点为接收状态。
例如,在STM32的USB设备库中,复位回调函数中会重新打开端点:
```c
static int8_t CDC_DeInit(void)
{
/* ... */
}
static int8_t CDC_Reset(void)
{
/* Open EP IN */
USBD_LL_OpenEP(&hUsbDeviceFS, CDC_IN_EP, USBD_EP_TYPE_BULK, CDC_DATA_HS_MAX_PACKET_SIZE);
/* Open EP OUT */
USBD_LL_OpenEP(&hUsbDeviceFS, CDC_OUT_EP, USBD_EP_TYPE_BULK, CDC_DATA_HS_MAX_PACKET_SIZE);
/* ... */
}
```
但是,请注意:在热插拔后,设备会经历一次复位,然后重新枚举。因此,确保复位函数中正确打开端点非常重要。
另外,检查一下在枚举完成后,主机在设置配置(SET_CONFIGURATION)后,设备是否准备好OUT端点。通常,在设置配置后,设备应该准备好OUT端点以接收数据。
在模板中,可能有`CDC_Setup`函数处理设置配置请求。在设置配置请求中,应该打开端点。
如果复位函数中已经打开了端点,那么设置配置中可能不需要再次打开。但具体要看实现。
另一种可能是,主机在热插拔后,尝试发送数据时使用了之前的管道(pipe)配置,而新枚举的设备使用了不同的配置(比如端点地址相同,但上下文已经改变),导致主机发送失败。
但是,根据CDC ACM规范,端点地址在重新枚举后应该是一样的(固件中定义不变),所以主机应该能够正确使用。
建议的调试步骤:
1. **检查复位回调**:确保在USB复位时,端点被正确关闭然后重新打开。在复位回调中,应该关闭所有非0端点(除了控制端点0),然后在后续的枚举过程中重新打开。有些实现是在设置配置时打开端点的。
2. **检查设置配置回调**:当主机发送设置配置请求(SetConfiguration)时,设备应该打开端点。
3. **检查主机端**:使用USB分析仪或软件(如WireShark+USBPcap)捕获USB数据包,观察热插拔后枚举过程是否正常,以及当主机发送数据时发生了什么错误(比如主机是否发送了OUT令牌包,设备是否响应NAK或其他)。
4. **设备端调试**:在设备端,检查当主机发送数据时,是否进入了OUT端点回调函数。如果没有,说明主机根本就没有发送数据,或者端点没有正确配置。
5. **检查端点最大包长度**:确保在描述符中定义的OUT端点最大包长度与主机期望的一致(一般是64字节)。如果描述符中定义的长度与主机驱动期望的不一致,可能会导致问题。
6. **主机驱动日志**:在主机端,查看设备管理器中串口设备的属性,确认无错误;同时可以查看系统日志,看是否有驱动程序错误。
由于问题描述中热插拔前正常,热插拔后出现问题,重点应放在设备在复位和重新枚举后的状态恢复上。
在cdc_acm_template.c中,通常有一个结构体用于管理CDC状态,比如:
```c
typedef struct {
uint8_t data[CDC_DATA_HS_MAX_PACKET_SIZE];
uint32_t len;
uint8_t ep_in;
uint8_t ep_out;
uint8_t ctrl_line_state;
} USBD_CDC_HandleTypeDef;
```
在重新枚举后,这个结构体是否被正确初始化?特别是`len`和缓冲区,以及控制线状态。
在收到复位事件时,应该重置这个结构体。
另外,在打开OUT端点时,应该准备好接收数据,即提交一个OUT请求(对于某些USB库,需要调用函数来接收数据)。例如,在STM32的USB设备库中,通常在初始化OUT端点后立即调用`USBD_LL_PrepareReceive`。
例如:
```c
USBD_LL_PrepareReceive(pdev, CDC_OUT_EP, hcdc->data, CDC_DATA_HS_MAX_PACKET_SIZE);
```
在复位回调或设置配置完成后,应该提交这个请求。这样,当主机发送数据时,设备才能接收。
如果热插拔后没有提交OUT端点的接收请求,那么设备将不会响应主机的OUT事务(即不会提供缓冲区),主机就会认为设备没有准备好,进而导致传输失败。
所以,请检查在热插拔后(即复位和重新枚举后),设备是否重新提交了OUT端点的接收请求。
总结:
可能的原因是热插拔后,设备没有重新为OUT端点提交接收请求,导致主机发送数据时设备没有响应(NAK),主机驱动在多次尝试后认为设备故障,从而报告串口故障并尝试重新打开。
请检查设备固件中,在复位事件处理或设置配置事件处理中,是否调用了准备接收数据的函数(如`USBD_LL_PrepareReceive`)。
在cdc_acm_template.c中,查找`CDC_Reset`或`CDC_Init`函数,看是否有提交OUT端点的接收请求。
如果没有,则需要在枚举完成后(比如在设置配置完成后)提交。
在`CDC_Setup`函数中,处理设置配置请求(通常请求号为`USB_REQ_SET_CONFIGURATION`)时,可以提交OUT端点的接收请求。
或者,在复位处理中,除了打开端点,还需要提交接收请求吗?注意:复位后端点处于关闭状态,需要先打开端点,然后才能提交接收请求。但是复位处理中打开端点后,紧接着的枚举过程会设置配置,在设置配置后提交接收请求更为合理。
因此,建议在设置配置处理中提交OUT端点的接收请求。
示例:
在`CDC_Setup`函数中:
```c
if (req->bRequest == USB_REQ_SET_CONFIGURATION) {
// 打开端点(如果之前没打开)
// 然后,准备接收
USBD_LL_PrepareReceive(pdev, CDC_OUT_EP, hcdc->data, CDC_DATA_HS_MAX_PACKET_SIZE);
}
```
或者,在`CDC_Init`函数中(如果有这个回调函数的话,它会在设置配置后被调用)进行接收请求的提交。
请根据具体的模板实现进行调整。
如果问题仍然存在,请检查端点描述符是否正确,以及端点地址是否一致。
希望这些分析能帮助解决问题。
根据您的描述,问题出现在热插拔后主机到设备(PC到MCU)的数据传输失败,而设备到主机(MCU到PC)的传输正常。结合cdc_acm_template.c的实现和CDC-ACM协议,以下是可能的原因和解决方案:
问题根本原因
热插拔后OUT端点未正确激活
CDC-ACM通信包含两个关键端点:
- IN端点 (设备->主机):热插拔后仍能工作,说明IN端点配置正确。
- OUT端点 (主机->设备):热插拔后失效,表明OUT端点在重新枚举后未被正确初始化或激活。
主机驱动状态不一致
热插拔可能导致主机CDC驱动缓存了旧端点状态,发送数据时使用了无效的管道(pipe)句柄。
设备端未重新提交OUT端点请求
设备固件需在枚举完成后主动提交OUT端点接收请求(如USBD_LL_PrepareReceive)。热插拔后若未重新提交,主机数据无法被接收。
解决方案
1. 检查端点初始化代码
在USB复位(Reset)或配置设置(SetConfig)回调中,确保OUT端点被显式打开并提交接收请求:
// 在 CDC_Init() 或 CDC_Reset() 函数中添加:
static int8_t CDC_Init(void) {
// ... 其他初始化代码
/* 打开OUT端点并准备接收数据 */
USBD_LL_OpenEP(pdev, CDC_OUT_EP, USBD_EP_TYPE_BULK, CDC_DATA_MAX_PACKET_SIZE);
USBD_LL_PrepareReceive(pdev, CDC_OUT_EP, hcdc->RxBuffer, CDC_DATA_MAX_PACKET_SIZE);
return USBD_OK;
}
2. 验证控制信号处理
确保dtr信号处理正确,特别是热插拔后控制请求(SET_CONTROL_LINE_STATE)的响应:
static int8_t CDC_Control(...) {
case SET_CONTROL_LINE_STATE:
hcdc->ctrl_line = req->wValue;
// 必须返回正确响应
USBD_CtlSendData(pdev, NULL, 0);
break;
}
3. 端点描述符检查
确认配置描述符中端点的方向和类型正确:
/* 配置描述符片段 */
0x07, // 端点描述符长度
USB_DESC_TYPE_ENDPOINT,
CDC_OUT_EP, // OUT端点地址 (0x01)
0x02, // 批量传输类型 (BULK)
0x0040, // 最大包大小 (64字节)
0x00, // 轮询间隔
4. 主机端清理操作
热插拔后执行以下步骤清除主机驱动状态:
- 卸载残留驱动:
# Windows (管理员权限)
devcon remove USBVID_xxxx&PID_xxxx
- 重置USB控制器:
- 设备管理器 → USB控制器 → 卸载设备 → 扫描硬件更改。
调试建议
设备端日志
通过调试器检查热插拔后:
USBD_Reset和CDC_Init是否被调用。
USBD_LL_PrepareReceive是否执行且无错误。
USB协议分析
使用Wireshark+USBPcap捕获USB流量,验证:
- 主机是否发送了OUT数据包。
- 设备是否返回ACK/NAK。
简化测试代码
在CDC_ReceiveCallback中添加日志,确认是否收到主机数据:
static int8_t CDC_ReceiveCallback(...) {
log("Received %d bytes", Len);
// 重新提交接收请求
USBD_LL_PrepareReceive(pdev, CDC_OUT_EP, hcdc->RxBuffer, CDC_DATA_MAX_PACKET_SIZE);
}
关键代码修正示例
在cdc_acm_template.c中确保以下关键点:
// 步骤1: 复位时重置状态
void CDC_Reset(void) {
// 关闭旧端点 (如果已打开)
USBD_LL_CloseEP(pdev, CDC_OUT_EP);
// 打开并初始化OUT端点
USBD_LL_OpenEP(pdev, CDC_OUT_EP, USBD_EP_TYPE_BULK, CDC_DATA_MAX_PACKET_SIZE);
}
// 步骤2: 配置完成后提交接收请求
static int8_t CDC_Init(void) {
USBD_LL_PrepareReceive(pdev, CDC_OUT_EP, hcdc->RxBuffer, CDC_DATA_MAX_PACKET_SIZE);
return USBD_OK;
}
// 步骤3: 每次接收完成后重新提交请求
static int8_t CDC_ReceiveCallback(...) {
// 处理数据...
USBD_LL_PrepareReceive(pdev, CDC_OUT_EP, hcdc->RxBuffer, CDC_DATA_MAX_PACKET_SIZE);
}
通过以上修正,热插拔后OUT端点将正确接收主机数据,解决单向通信问题。
举报