STM32USB HID class的一部分 BarCodeScanner(条码枪)不兼容的解决
硬件构成
STM32F479-EVAL 评价板
型号为FFTAA10AP条码枪
现象
最近用CubeMX生成的USB库做条形码枪的USB驱动,用的是HID协议。有的条形码枪,用标准的USB库一次就能成功。
但是有的型号的条码枪貌似和标准USB库流程有所出入,执行后,函数在 USBH_HID_ClassRequest 里的
USBH_HID_SetIdle 函数发生了Stall,以至于无法继续。
USBH_HID_ClassRequest的调查结果(应该是枚举已经完了)
usbh_hid.c
- before
case HID_REQ_SET_IDLE:
classReqStatus = USBH_HID_SetIdle (phost, 0, 0);
/* set Idle */
if (classReqStatus == USBH_OK)
{
HID_Handle->ctl_state = HID_REQ_SET_PROTOCOL;
}
else if(classReqStatus == USBH_NOT_SUPPORTED)
{
HID_Handle->ctl_state = HID_REQ_SET_PROTOCOL;
}
break;
USBH_HID_SetIdle的返回值最初是1(busy),重试三次以后,返回值成了3(USBH_NOT_SUPPORTED),函数最底层发生了STALL。
在网上查了一些资料,有个设备是不需要(不支持?不知道哪个描述更准确)setIdle。因此这一步骤可以省略。为了保持兼容性,我把代码做了一下修改。
※ 当然,我的那个case直接跳过最好,我修改的从逻辑上看完全不通,因为正常的情况是连续三次busy后,会出现Ok的情况,而我的代码则是在第一次busy后直接跳到下一个步骤。
没办法,这个地方需要给领导看,这么写可以从一定程度上保持已有的代码逻辑完整。笑。当然,按照我这个写法修改也是没有错误的。
- after
case HID_REQ_SET_IDLE:
classReqStatus = USBH_HID_SetIdle (phost, 0, 0);
/* set Idle */
if (classReqStatus == USBH_OK)
{
HID_Handle->ctl_state = HID_REQ_SET_PROTOCOL;
}
else if(classReqStatus == USBH_NOT_SUPPORTED)
{
HID_Handle->ctl_state = HID_REQ_SET_PROTOCOL;
}
/*
* SetIdleが三回リトライすると、USBH_URB_STALLが発生
* あるKeyBoarにはsetIdleコマンドが必須ではないため、コマンド実施しなくて次に行ってもOK
* ここで一回実施して成功しても、失敗しても全部次のSET_PROTOCOLに続く(直接削除もOK)
*/
#if 1
else if( classReqStatus == USBH_BUSY)
{
HID_Handle->ctl_state = HID_REQ_SET_PROTOCOL;
}
#endif
break;
经过以上的修改,条码枪就可以进到ready状态栏。如果你的设备还是不可以,那么下面的东西就不需要再看了,我们的错误不一样。
HID调试
简单是了一下,看了log,在这以后的命令仍然出现STALL。经过一顿搜索,定位在进入Idle后的 USBH_HID_GetReport 函数。
usbh_hid.c
- before
static USBH_StatusTypeDef USBH_HID_Process(USBH_HandleTypeDef *phost)
{
USBH_StatusTypeDef status = USBH_OK;
HID_HandleTypeDef *HID_Handle = (HID_HandleTypeDef *) phost->pActiveClass->pData;
switch (HID_Handle->state)
{
case HID_INIT:
HID_Handle->Init(phost);
case HID_IDLE:
if(USBH_HID_GetReport (phost, //这里发生错误
0x01,
0,
HID_Handle->pData,
HID_Handle->length) == USBH_OK)
{
fifo_write(&HID_Handle->fifo, HID_Handle->pData, HID_Handle->length);
HID_Handle->state = HID_SYNC;
}
break;
网上调查的结果是,有一部分设备不支持这个命令,因此可以直接跳过HID_IDLE这个case。修改后如下。
- after
static USBH_StatusTypeDef USBH_HID_Process(USBH_HandleTypeDef *phost)
{
USBH_StatusTypeDef status = USBH_OK;
HID_HandleTypeDef *HID_Handle = (HID_HandleTypeDef *) phost->pActiveClass->pData;
switch (HID_Handle->state)
{
case HID_INIT:
HID_Handle->Init(phost);
/*
* ネットで調べてGet_Reportサポートされないバーコードリーダーがあるため、
* HID_IDLEを抜けて直接HID_SYNCに遷移する。
*/
#if 1
HID_Handle->state = HID_SYNC; // skip HID_IDLE state
break;
#endif
case HID_IDLE:
if(USBH_HID_GetReport (phost,
0x01,
0,
HID_Handle->pData,
HID_Handle->length) == USBH_OK)
也就是在初始化完了后,直接进入SYNC状态。
经过上面的修改,条形码枪应该可以正常的工作量。
不要问原因,不要问理由,因为我也不是很明白。笑
其他内容
还有个现象就是有的条形码枪,在STM32启动前就插入到USB的话,STM32启动后可以正常识别。
但是如果,STM32启动后,再将条形码枪插入USB的话,那么STM32将无法识别USB。
从log上看只有[USB device Attach],然后底层的是一直 busy的状态。
经过反复的实验,考虑到可能是有的USB设备的启动时间比较慢(慢热型,哈哈)STM32的检测过快导致的。
因此,在检测USB设备这块,需要多延迟一会,以确保设备内部确实初始化完成。
修改代码如下
usbh_core.c
USBH_StatusTypeDef USBH_Process(USBH_HandleTypeDef *phost)
{
__IO USBH_StatusTypeDef status = USBH_FAIL;
uint8_t idx = 0;
switch (phost->gState)
{
case HOST_IDLE :
if (phost->device.is_connected)
{
/* Wait for 200から500 ms after connection */
phost->gState = HOST_DEV_WAIT_FOR_ATTACHMENT;
#if 1
USBH_Delay(300); //あるバーコード設備の立ち上がる時間が普通より長いため 200から300へ変更
#endif
USBH_LL_ResetPort(phost);
#if (USBH_USE_OS == 1)
osMessagePut ( phost->os_event, USBH_PORT_EVENT, 0);
#endif
}
break;
经过这几部分的修改,BarCodeReader 或者说 BarCodeScanner 或者说 条形码枪 就可以正确的工作了。
不过这也仅限于我的设备。仅供参考。
USBH_Delay(200); 里面的参数变成500后,以前好用的枪反而不好用了,所以说,这个时间既不能太长也不能太短。