发新帖我要提问
123
返回列表
打印

【转】STM32 USB 问题汇总

[复制链接]
楼主: sunmeat
手机看帖
扫描二维码
随时随地手机跟帖
41
sunmeat|  楼主 | 2015-2-26 19:28 | 只看该作者 回帖奖励 |倒序浏览
同样每个数据入过程也可能有若干次DATA_IN传输,每次需要向主机传输数据时,USB库都会调用一次回调函数CopyRoutine,参数length是本次传输所要发送的数据字节数目,CopyRoutine必须返回一个缓冲区指针,这个缓冲区中必须包含要求的数据字节,USB库将把用户缓冲区的数据拷贝到USB缓冲区并择机发送出去。

使用特权

评论回复
42
sunmeat|  楼主 | 2015-2-26 19:28 | 只看该作者
当以length=0调用CopyRoutine时,CopyRoutine需要返回用户缓冲区的长度,因为CopyRoutine的返回类型是一个指针,所以需要通过类型的强制转换返回缓冲区长度。这个功能是为了处理用户缓冲区的长度与主机SETUP数据请求长度不符的情况,而不至于造成用户缓冲区的溢出。

使用特权

评论回复
43
sunmeat|  楼主 | 2015-2-26 19:28 | 只看该作者
介绍完上述若干概念和回调函数,再看SET_REPORT的实现就很容易了。

SET_REPORT是一个数据出过程,因此需要实现一个Class_Data_Setup回调函数,示例如下:
RESULT HID_Data_Setup(u8 RequestNo)
{
    u8 *(*CopyRoutine)(u16 length);
    CopyRoutine = NULL;
    if (pInformation->USBbmRequestType == CLASS_REQUEST|INTERFACE_RECIPIENT
            && RequestNo == SET_REPORT)
        CopyRoutine = My_Data_Request;

    if (CopyRoutine == NULL)
        return USB_UNSUPPORT;

    pInformation->Ctrl_Info.CopyData = CopyRoutine;
    pInformation->Ctrl_Info.Usb_wOffset = 0;
    pInformation->Usb_wLength = (*CopyRoutine)(0);

    return USB_SUCCESS;
} // End of HID_Data_Setup()

u8 My_Buffer[10];
u8 *My_Data_Request(u16 length)
{
    if (length == 0)
        return (u8*)10;    // 假定你的REPORT长度和Buffer长度为10

    return My_Buffer;
}

使用特权

评论回复
44
sunmeat|  楼主 | 2015-2-26 19:29 | 只看该作者
上面介绍的CopyRoutine用于把多次传输的数据包合并到一个完整的缓冲区中,因此只有到STATUS阶段才能够指导一次SETUP传输是否结束,所以用户程序需要在回调函数Process_Status_IN中处理从SET_REPORT接收到的数据。因为所有的回调函数都是USB中断处理的一部分,所以更好的办法是在Process_Status_IN中设置一个标记,然后在用户主程序中判断这个标记并做处理。

使用特权

评论回复
45
sunmeat|  楼主 | 2015-2-26 19:29 | 只看该作者
注意,STM32的USB库设计成以回调函数处理用户命令请求,包含类命令请求,是为了能够清晰地区分库程序和用户程序,使这两者不会混在一起,这样的好处是非常明显的,当USB库需要更新升级时,只需替换掉相应的程序模块,而不必修改用户已经完成的程序。

以上的介绍都可以在STM32 USB库的说明手册中找到。

使用特权

评论回复
46
sunmeat|  楼主 | 2015-2-26 20:11 | 只看该作者
上述示意代码是以My_Buffer长度为10字节为例,而USB库的默认包长度为16字节,因此My_Data_Request并没有多包的处理。

关于多包的缓冲区处理的示意代码可以是这样的:
u8 *My_Data_Request(u16 length)
{
    if (length == 0)
        return (u8*)100;    // 假定你的REPORT长度和Buffer长度为100

    return &My_Buffer[pInformation->Ctrl_Info.Usb_wOffset];
}

使用特权

评论回复
47
sunmeat|  楼主 | 2015-2-26 20:26 | 只看该作者
上述示意代码是以My_Buffer长度为10字节为例,而USB库的默认包长度为16字节,因此My_Data_Request并没有多包的处理。

关于多包的缓冲区处理的示意代码可以是这样的:
u8 *My_Data_Request(u16 length)
{
    if (length == 0)
        return (u8*)100;    // 假定你的REPORT长度和Buffer长度为100

    return &My_Buffer[pInformation->Ctrl_Info.Usb_wOffset];
}

使用特权

评论回复
48
sunmeat|  楼主 | 2015-2-26 20:26 | 只看该作者
这里有一个库中使用的变量pInformation->Ctrl_Info.Usb_wOffset,这个变量回在传输每个数据包时候由库中的程序按数据包长度增加,如最大包长为16字节时,第一次调用My_Data_Request时Usb_wOffset=0,第二次调用My_Data_Request时Usb_wOffset=16,第三次调用My_Data_Request时Usb_wOffset=32,依此类推。这样就可以使用Usb_wOffset作为My_Buffer的下标从My_Data_Request返回。

使用特权

评论回复
49
sunmeat|  楼主 | 2015-2-26 20:30 | 只看该作者
对于提问“如何传递length?在上面没有看到这个参数的传递过程”的回答:

参数length是用于检测缓冲区长度是否足够,如果你有足够长的缓冲区,可以不必检测,上述示例中使用了一个固定的缓冲区,所以不必使用参数length检测缓冲区长度

使用特权

评论回复
50
我是前行的狮子| | 2015-5-19 09:53 | 只看该作者
if (GetENDPOINT(ENDP3) & EP_DTOG_TX)//先判断本次接收到的数据是放在哪块缓冲区的 ??
请问一下大家,为什么这句话是判断放在哪个缓冲区的函数啊

使用特权

评论回复
51
sunmeat|  楼主 | 2015-5-20 09:02 | 只看该作者
我是前行的狮子 发表于 2015-5-19 09:53
if (GetENDPOINT(ENDP3) & EP_DTOG_TX)//先判断本次接收到的数据是放在哪块缓冲区的 ??
请问一下大家, ...

你看库中寄存器的定义就知道了。

使用特权

评论回复
52
Mrjiang88178| | 2016-12-14 09:56 | 只看该作者
好贴,mark      

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则