[APM32F1]APM32USB双CDC配置_2

42 篇文章 0 订阅
29 篇文章 0 订阅

承接上一篇帖子,正好将双CDC的设备描述符以及其端点和PMA分配好并且初始化,成功的能够被主机识别成两个CDC设备,但是对于第二个CDC的接口函数还没做处理,因此串口不能正常发送接收数据。
因此,此次将对第二个CDC的接口函数进行添加,使其能够正常发送接收数据。

思路
修改的步骤则是先从外层的接口函数(即用户接口函数)开始修改,再对USB内层使用到这些外部接口函数的地方进行修改。

void USB_DeviceInit(void)

{

    /* USB CDC register interface handler */

    USBD_CDC_RegisterItf(&gUsbDeviceFS, &USBD_CDC_INTERFACE_FS);

    /* USB device and class init */

    USBD_Init(&gUsbDeviceFS, USBD_SPEED_FS, &USBD_DESC_FS, &USBD_CDC_CLASS, USB_DevUserHandler);

}

由上代码可以知道,接口函数的句柄是在设备初始化一开始传给了usb结构体。因为这次弄个的是相同class的复合设备,因此不增加相应的接口,而是修改之前的接口函数,使其能够区分两个CDC并做处理。
CDC接口函数如下:分别是初始化函数,非初始化函数,控制函数,发送函数以及接收函数。

/* USB FS CDC interface handler */

USBD_CDC_INTERFACE_T USBD_CDC_INTERFACE_FS =

{

    "CDC Interface FS",

    USBD_FS_CDC_ItfInit,

    USBD_FS_CDC_ItfDeInit,

    USBD_FS_CDC_ItfCtrl,

    USBD_FS_CDC_ItfSend,

    USBD_FS_CDC_ItfSendEnd,

    USBD_FS_CDC_ItfReceive,

};


1.USBD_FS_CDC_ItfInit
初始化函数,就是对CDC的buffer进行初始化,具体函数如下。因此,我们也需要对CDC2的发送接收buf分配相应的数组地址。

USBD_STA_T USBD_FS_CDC_ItfInit(void)

{

    USBD_STA_T usbStatus = USBD_OK;

    USBD_CDC_ConfigRxBuffer(&gUsbDeviceFS, cdcRxBuffer);

    USBD_CDC_ConfigTxBuffer(&gUsbDeviceFS, cdcTxBuffer, 0);

    return usbStatus;

}

具体就是在内部函数(即USBD_CDC_ConfigRxBuffer以及USBD_CDC_ConfigTxBuffer)做修改。


2.USBD_FS_CDC_ItfDeInit
去初始化函数没有对CDC做处理,因此这里也不对其做处理。
 

<blockquote>USBD_STA_T USBD_FS_CDC_ItfDeInit(void)


3.USBD_FS_CDC_ItfCtrl
控制函数也是同理,不对其做处理。
 

USBD_STA_T USBD_FS_CDC_ItfCtrl(uint8_t command, uint8_t *buffer, uint16_t length)

{

    USBD_STA_T usbStatus = USBD_OK;

    return usbStatus;

}


4.USBD_FS_CDC_ItfSend
发送函数具体如下。实现逻辑就是先判断CDC发送口的state是否为空闲,然后将发送的数据的地址传给CDC的发送buf。最后使用USB的发送函数,将CDC发送buf的数据转移到USB的缓冲区中,再执行发送,将缓冲区的数据发送出去。

复制
USBD_STA_T USBD_FS_CDC_ItfSend(uint8_t *buffer, uint16_t length)

{

    USBD_STA_T usbStatus = USBD_OK;

    USBD_CDC_INFO_T *usbDevCDC = (USBD_CDC_INFO_T*)gUsbDeviceFS.devClass[gUsbDeviceFS.classID]->classData;

    if(usbDevCDC->cdcTx.state != USBD_CDC_XFER_IDLE)

    {

        return USBD_BUSY;

    }

    USBD_CDC_ConfigTxBuffer(&gUsbDeviceFS, buffer, length);

    usbStatus = USBD_CDC_TxPacket(&gUsbDeviceFS);

    return usbStatus;

}

因此,需要为CDC2添加相应的处理函数,具体如下。主要是对分配buf地址给CDC函数以及发送函数增加一个cdcnum参数,这个参数用来区分是CDC1发送还是CDC2发送。

复制
USBD_STA_T USBD_FS_CDC_ItfSend(uint8_t *buffer, uint16_t length, uint8_t cdcnum)

{

    USBD_STA_T usbStatus = USBD_OK;

    USBD_CDC_INFO_T *usbDevCDC = (USBD_CDC_INFO_T*)gUsbDeviceFS.devClass[gUsbDeviceFS.classID]->classData;

        if(cdcnum == CDC1)

        {

                if(usbDevCDC->cdcTx.state != USBD_CDC_XFER_IDLE)

                {

                        return USBD_BUSY;

                }

        }else{

                if(usbDevCDC->cdc2Tx.state != USBD_CDC_XFER_IDLE)

                {

                        return USBD_BUSY;

                }

        }

    USBD_CDC_ConfigTxBuffer(&gUsbDeviceFS, buffer, length, cdcnum);

    usbStatus = USBD_CDC_TxPacket(&gUsbDeviceFS, cdcnum);

    return usbStatus;

}

具体内部函数的修改这里不过太多阐述,如果想知道具体修改函数可以查看附件。不过,对于USB设备的发送有一个需要注意的点,就是发送的时候会对CDC的发送状态做判断,在执行发送的时候会把其状态改为USBD_CDC_XFER_BUSY,因此我们需要找到改为空闲的地方,将其新增对CDC2的处理。
通过KEIL的全局搜索工具,我们发送代码在USBD_CDC_DataInHandler函数里面对其设置成空闲状态。部分函数修改如下。

复制
  if((epNum&0x0F) == 0x1)

                {

                        usbDevCDC->cdcTx.state = USBD_CDC_XFER_IDLE;

        }

                else

                {

                        usbDevCDC->cdc2Tx.state = USBD_CDC_XFER_IDLE;

                }

主要对端点信息判断是否为CDC1还是CDC2。


5.USBD_FS_CDC_ItfCtrl
发送结束函数也是不对其做处理,具体函数如下。

USBD_STA_T USBD_FS_CDC_ItfSendEnd(uint8_t epNum, uint8_t *buffer, uint32_t *length)

{

    USBD_STA_T usbStatus = USBD_OK;

    return usbStatus;

}



6.USBD_FS_CDC_ItfCtrl
接收函数具体如下。处理逻辑就是usb接收到相应数据并存放到缓冲区时,会触发中断并调用该接收函数,将缓冲区的数据转移到用户CDC的接收BUF中。

USBD_STA_T USBD_FS_CDC_ItfReceive(uint8_t *buffer, uint32_t *length)

{

    USBD_STA_T usbStatus = USBD_OK;

    uint16_t index;

    USBD_CDC_ConfigRxBuffer(&gUsbDeviceFS, &buffer[0]);

    USBD_CDC_RxPacket(&gUsbDeviceFS);

    gUsbVCP.state = USBD_CDC_VCP_REV_UPDATE;

    gUsbVCP.rxUpdateLen = *length;

    for(index = 0; index < gUsbVCP.rxUpdateLen; index++)

    {

        cdcRxBufferTotal[gUsbVCP.rxTotalLen + index] = buffer[index];

    }

    gUsbVCP.rxTotalLen += gUsbVCP.rxUpdateLen;

    return usbStatus;

}

同理,需要对USBD_CDC_ConfigRxBuffer以及USBD_CDC_RxPacket加入cdcnum的变量,使其能够对CDC做判断处理。同时,需要知道是哪一个CDC接收数据,需要从源头进行修改。对其接口全局搜索找到其调用函数,并对其做修改。具体如下。

复制
static USBD_STA_T USBD_CDC_DataOutHandler(USBD_INFO_T* usbInfo, uint8_t epNum)

{

    USBD_STA_T  usbStatus = USBD_OK;

    USBD_CDC_INFO_T* usbDevCDC = (USBD_CDC_INFO_T*)usbInfo->devClass[usbInfo->classID]->classData;

    if (usbDevCDC == NULL)

    {

        return USBD_FAIL;

    }

        if(epNum == 0x1)

        {

    usbDevCDC->cdcRx.length = USBD_EP_ReadRxDataLenCallback(usbInfo, epNum);

    ((USBD_CDC_INTERFACE_T *)usbInfo->devClassUserData[usbInfo->classID])->ItfReceive(usbDevCDC->cdcRx.buffer, \

                                                                                      &usbDevCDC->cdcRx.length, CDC1);

        }

        else

        {

    usbDevCDC->cdcRx.length = USBD_EP_ReadRxDataLenCallback(usbInfo, epNum);

    ((USBD_CDC_INTERFACE_T *)usbInfo->devClassUserData[usbInfo->classID])->ItfReceive(usbDevCDC->cdcRx.buffer, \

                                                                                      &usbDevCDC->cdcRx.length, CDC2);

        }

    return usbStatus;

}

那么,对于CDC2的接口处理添加就完毕了。最后就是对一些修改了函数参数的定义进行修改。

7.main
当然,还需要对main函数进行修改,不然怎么知道CDC2有没有正常传输数据。这里因为对于CDC2的接收数组没有再做进一步的处理,因此只看CDC2的发送。修改如下:

复制
 i++;

                sprintf(str,"Hello %d\r\n",i);

                USBD_FS_CDC_ItfSend((uint8_t *)str, strlen(str),CDC1);

                i++;

                sprintf(str,"Hello %d\r\n",i);

                USBD_FS_CDC_ItfSend((uint8_t *)str, strlen(str),CDC2);


测试
打开串口工具,可以发现有两个串行设备,COM8以及COM12。
 



然后打开COM8的时候发现无法连接到系统上。 



通过usb分析仪发现打开COM8串口的时候,主机获取CDC请求的时候设备端没有进行反应。
 



因此,怀疑是代码哪一处地方漏掉做处理导致CDC2没办法正常打开串口。
最后发现在USBD_SetupStage函数中,会对接口信息进行判断,在进去类的setup处理函数中。具体如下:

 if (usbInfo->reqSetup.DATA_FIELD.wIndex[0] <= USBD_SUP_INTERFACE_MAX_NUM)

                                {

                                    /* Add multi class support */

                                    classIndex = 0;

                                    if ((classIndex != 0xFF) && (classIndex < USBD_SUP_CLASS_MAX_NUM))

                                    {

                                        usbInfo->classID = classIndex;

                                        

                                        if (usbInfo->devClass[classIndex]->ClassSetup != NULL)

                                        {

                                            usbStatus = usbInfo->devClass[classIndex]->ClassSetup(usbInfo, &usbInfo->reqSetup);

                                        }

因为主机对于CDC2的CDC请求会带接口信息,为2,。因此,需要将USBD_SUP_INTERFACE_MAX_NUM该宏定义改为2。
这样子,COM8(CDC2)就能够成功进行打印输出了。
 



但是发现其打印的数据是个乱码,正常来说应该递增的数据。通过DEBUG调试知道数据是能够正常的传到CDC2的一个发送数组并且正确的迁移到合适的缓冲区空间里面,但是最后打印出来的数据不是缓冲区所存储的数据。
通过查阅资料发现缓冲区它是有一个表头的东西存在,给到USB查找相应端点缓冲区地址进行发送。这个表的大小就根据端点0的地址来计算的。

#define USBD_EP0_OUT_ADDR                       0x00

#define USBD_EP0_OUT_SIZE                       0x18

#define USBD_EP0_IN_ADDR                        0x80

#define USBD_EP0_IN_SIZE                        0x58

通过查看端点0的地址,我们可以知道表的大小只有0x18,只能容纳3个端点。而CDC2使用的发送端点为端点3,刚好超出了表所包含的端点。因此,这里将其改为0x20和0x60。

结果
最终,两路CDC都能正常进行发送数据。

---------------------
作者:宫影空明人不往
链接:https://bbs.21ic.com/icview-3344838-1-1.html
来源:21ic.com
此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值