问题描述
STM32开发USB设备,一般是将其模拟为CDC设备,也就是虚拟串口设备Vitual Port Com,如果在使用的过程中设备掉线了,通讯也就中断了。
解决方案
如何检测自身与电脑的连接是否正常呢?
方案1 检测VBUS
可以通过VBUS检测电压,当检测不到VBUS电压时,会触发中断,通过检测这个中断就可以判断设备是否掉线了。这个是硬件检测,系统开销比较小。
方案2 分压电路
也可以搭一个分压电路,另用一个引脚检测分压电路的下降沿。
但是方案1和方案2都是检测5V电压的,如果USB的差分线短路导致设备连接失败的话,是检测不了的。
方案3 SOF检测
STM32G431是不支持VBUS检测的,这时我们可以通过帧首包SOF(start of frame)来实现。对于USB的差分信号线故障造成的断连,也可以检测出来。安全性是最好的,但是系统开销会增大。
USB主设备会定时发送SOF包,全速设备是1ms,高速设备是125us,持续不断的发送信号出来。同时STM32的USB支持包,会检测SOF,并输出中断。我们只需要响应这个中断,并记录收到SOF包的时间即可。
在任何时刻都可以查询这个时间,如果一旦超时,就可以认为USB设备掉线,这时可以重启USB模块,重新进行连接。
使能USB模块,并开启SOF
中间件的设置
开启中间件的支持,并选择CDC设备
增加堆栈的长度
这一步非常重要,否则会在设备管理器中出现感叹号,一般0x400和0x800就够了,我习惯设大一点,放心。
代码
在usb_conf.c文件中,需要做修改,我增加的代码,都有中文注释
void HAL_PCD_SetupStageCallback(PCD_HandleTypeDef *hpcd)
{
/* USER CODE BEGIN HAL_PCD_SetupStageCallback_PreTreatment */
/* USER CODE END HAL_PCD_SetupStageCallback_PreTreatment */
USBD_LL_SetupStage((USBD_HandleTypeDef*)hpcd->pData, (uint8_t *)hpcd->Setup);
/* USER CODE BEGIN HAL_PCD_SetupStageCallback_PostTreatment */
LED1(1); //点亮LED
usb_enum_ok_flag = 1; //枚举成功的标志
/* USER CODE END HAL_PCD_SetupStageCallback_PostTreatment */
}
void HAL_PCD_SOFCallback(PCD_HandleTypeDef *hpcd)
{
/* USER CODE BEGIN HAL_PCD_SOFCallback_PreTreatment */
/* USER CODE END HAL_PCD_SOFCallback_PreTreatment */
USBD_LL_SOF((USBD_HandleTypeDef*)hpcd->pData);
/* USER CODE BEGIN HAL_PCD_SOFCallback_PostTreatment */
//会每1ms进一次这个中断,增加系统开销
last_sof_time = HAL_GetTick(); //记录最后一次收到SOF的时间
/* USER CODE END HAL_PCD_SOFCallback_PostTreatment */
}
void Check_USB_Connection()
{
u32 offset;
offset = HAL_GetTick();
if((offset - last_sof_time) > 100) // 正常应为1或0,以100ms作为超时时间
{
// USB连接可能已断开
LED1(0);
LL_usb_reload(); //重启USB模块
}
}
//重新初始化
void LL_usb_reload(void)
{
__HAL_RCC_USB_FORCE_RESET();
delay_us(1);
__HAL_RCC_USB_RELEASE_RESET();
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_12,GPIO_PIN_RESET);
HAL_Delay(300);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_12,GPIO_PIN_SET);
HAL_Delay(300);
MX_USB_Device_Init();
HAL_Delay(300);
}
最后,在主程序中运行
Check_USB_Connection();
运行现象
正常工作时,LED1会亮。
设备拔掉,或人为模拟USB差分线故障,比如短路USB的2根信号线时时,LED会灭。
这时程序会不停的尝试重启USB模块,并连接主机。如果成功后,LED会点亮。