SAM9G45之USB学习笔记

最近一直在学习SAM9G45的USB功能,资料大部分都是从网上找的,还有就是官方给的库,只是把自己调试过程中遇到的问题记录下来,因为没有调试完,所以会持续更新。

问题一(其实也不是问题),SAM9G45只有7个端点(包括端点0),我开始调试的时候以为不带0,哎,郁闷。

问题二(待求证),不过从我目前的调试来看,结论是对的。SAM9G45的端点不是全双工了,一个端点不能同时配置为IN和OUT,但是手册又有自相矛盾的地方。

从表38-1来看它的每个端点都支持ctrl传输模式,根据USB协议规定,这个传输模式要求端点必须是全双工的,因为ctrl的端点必须支持messsge pipe类型。

从表38-2也能看出,CTRL模式是双向的;

从表38-3的注意1来看,它说control transfer必须是1bank,从38-1看,bank1只能是端点0;从这里来看其它端点是不能支持ctrl模式的。和我的实验结论一致,也可能是我不会设置端点地址,如果哪位网友正好知道,请给我留言,谢谢。

三、Bus Hound配置问题

这个东西用的不熟悉,抓包发现只能抓取32的字节的IN指令包,以为是设备问题,最后发现是busHound设置问题,郁闷。

四、这个是目前遇到最郁闷的问题

刚开始的Demo是hid_transfer,我在这个基础上改的CCID,并且开始只是用一个设备+一个interface调试的,很顺利,很快就识别出来了。应用需求,需要两个interface,改过之后怎么也识别不了第二个设备。Bus Hound上面,也没有第二个设备的报文。郁闷,以为是配置问题,结果把配置复制带interface0上面,正常。郁闷了,所有的操作函数都是一套,通过接口传参数,来确定是操作那个设备,难道个别的函数有错误,参数没有改过来?于是把所有的函数全部对了一遍,没有错误啊?这是怎么回事。最后找到原因了,原来是windows系统的驱动问题。如果一个设备的V_ID和P_ID不修改,之前已经插入过电脑,并且正确设别并安装驱动的话,如果再次插入(即使配置付改变了)驱动不会从新安装,这个时候设备出来的还是原来的设备。

解决方法:

1、修改P_ID或者V_ID的值;

2、或者在注册表里面删掉对应的配置信息;

我修改了P_ID值,正确识别了两个设备:

《-------------------------------------------------------------------------------------------------------》

官方USB协议帧学习笔记:

先上传几张图片,回头再整理:

图1   协议栈的结构

图2  USB的状态机

图3  USB的协议栈回调函数架构

首先要知道USB的驱动状态:

USB device states
/// The device is currently suspended.
#define USBD_STATE_SUSPENDED            0
/// USB cable is plugged into the device.
#define USBD_STATE_ATTACHED             1
/// Host is providing +5V through the USB cable.
#define USBD_STATE_POWERED              2
/// Device has been reset.
#define USBD_STATE_DEFAULT              3
/// The device has been given an address on the bus.
#define USBD_STATE_ADDRESS              4
/// A valid configuration has been selected.
#define USBD_STATE_CONFIGURED           5

deviceState(状态变量)  

其次USB的API返回值

USB device API return values
/// Indicates the operation was successful.
#define USBD_STATUS_SUCCESS             0
/// Endpoint/device is already busy.
#define USBD_STATUS_LOCKED              1
/// Operation has been aborted.
#define USBD_STATUS_ABORTED             2
/// Operation has been aborted because the device has been reset.
#define USBD_STATUS_RESET               3
/// Operation failed because parameter error
#define USBD_STATUS_INVALID_PARAMETER   4
/// Operation failed because HW not supported
#define USBD_STATUS_HW_NOT_SUPPORTED    5

端点的状态:

Endpoint states
/// Endpoint states: Endpoint is disabled
#define UDP_ENDPOINT_DISABLED       0
/// Endpoint states: Endpoint is halted (i.e. STALLs every request)
#define UDP_ENDPOINT_HALTED         1
/// Endpoint states: Endpoint is idle (i.e. ready for transmission)
#define UDP_ENDPOINT_IDLE           2
/// Endpoint states: Endpoint is sending data
#define UDP_ENDPOINT_SENDING        3
/// Endpoint states: Endpoint is receiving data
#define UDP_ENDPOINT_RECEIVING      4

1、CCIDDriver_Initialize()

CCIDDriver_Initialize()->USBD_Init()->USBDCallbacks_Initialized()->IRQ_ConfigureIT(AT91C_ID_UDPHS, 0, UDPD_IrqHandler);//, UDPD_IrqHandler);

UDPD_IrqHandler()配置为USB的中断函数;

UDPD_IrqHandler()调用UDPHS_EndpointHandler()和UDPHS_DmaHandler()

其中:

UDPHS_EndpointHandler()->USBDCallbacks_RequestReceived()->CCID_RequestHandler()->CCID

处理和USBDDriver_RequestHandler()

UDPHS_EndpointHandler()和UDPHS_DmaHandler()都会调用UDPHS_EndOfTransfer()

来看下UDPHS_EndOfTransfer()

//------------------------------------------------------------------------------
/// Handles a completed transfer on the given endpoint, invoking the
/// configured callback if any.
/// \param bEndpoint Number of the endpoint for which the transfer has completed.
/// \param bStatus   Status code returned by the transfer operation
//------------------------------------------------------------------------------
static void UDPHS_EndOfTransfer( unsigned char bEndpoint, char bStatus )
{
    Endpoint *pEndpoint = &(endpoints[bEndpoint]);
    Transfer *pTransfer = &(pEndpoint->transfer);

    // Check that endpoint was sending or receiving data
    if( (pEndpoint->state == UDP_ENDPOINT_RECEIVING)
     || (pEndpoint->state == UDP_ENDPOINT_IDLE)
     || (pEndpoint->state == UDP_ENDPOINT_SENDING) ) {

        TRACE_DEBUG_WP("UDPHS_EndOfTransfer ");
        if(pEndpoint->state == UDP_ENDPOINT_SENDING) {
            pEndpoint->sendZLP = 0;
        }
        // Endpoint returns in Idle state
        //if(pEndpoint->state == UDP_ENDPOINT_SENDING){
            pEndpoint->state = UDP_ENDPOINT_IDLE;
        //}

        // Invoke callback is present
        if (pTransfer->fCallback != 0) {

            ((TransferCallback) pTransfer->fCallback)
                (pTransfer->pArgument,
                 bStatus,
                 pTransfer->transferred,
                 pTransfer->remaining + pTransfer->buffered);
        }
        else {
            TRACE_DEBUG_WP("No callBack\n\r");
        }
    }
}


当接收完成或者发送完成,就会调用一个callback,这个callback是哪里来的呢?继续看

void CCID_SmartCardRequest_V1( uint8_t   chCcidNum )
{
    unsigned char bStatus;
    if(chCcidNum >= CCID_INTERFACE_NUM){
        TRACE_ERROR("CCID_SmartCardRequest  chCcidNum >= CCID_INTERFACE_NUM\n\r");
        return;
    }
    //do{

        bStatus = CCID_Read_V1((void*)&ccidDriver.tCcidStruct[chCcidNum].sCcidCommand,
                             sizeof(S_ccid_bulk_out_header),
                             (TransferCallback)&CCIDCommandDispatcher_V1,
                             (void*)chCcidNum );//直接把值变为地址传输
    //}while (bStatus != USBD_STATUS_SUCCESS);

}

这个是API层的CCID调用函数,

//------------------------------------------------------------------------------
/// Reads data from the Data OUT endpoint
/// \param pBuffer   Buffer to store the received data
/// \param dLength   data buffer length
/// \param fCallback Optional callback function
/// \param pArgument Optional parameter for the callback function
/// \return USBD_STATUS_LOCKED or USBD_STATUS_SUCCESS
//------------------------------------------------------------------------------
/*
unsigned char CCID_Read(void *pBuffer,
                        unsigned int dLength,
                        TransferCallback fCallback,
                        void *pArgument)
{
    return USBD_Read(CCID_EPT_DATA_OUT, pBuffer, dLength, fCallback, pArgument);
}*/


unsigned char CCID_Read_V1( void *pBuffer,
                            unsigned int dLength,
                            TransferCallback fCallback,
                            void *pArgument)
{
    uint8_t chEndpointOut = 0;

    if(((uint8_t)pArgument) >= CCID_INTERFACE_NUM){
        TRACE_ERROR("CCID_Read  chCcidNum >= CCID_INTERFACE_NUM\n\r");
        return USBD_STATUS_INVALID_PARAMETER;
    }

    switch((uint8_t)pArgument){
#if (CCID_INTERFACE_NUM>0)
    case 0:
        chEndpointOut = CCID_INTERFACE0_EPT_DATA_OUT;
        break;
#endif
#if (CCID_INTERFACE_NUM>1)
    case 1:
        chEndpointOut = CCID_INTERFACE1_EPT_DATA_OUT;
        break;
#endif
#if (CCID_INTERFACE_NUM>2)
    case 2:
        chEndpointOut = CCID_INTERFACE2_EPT_DATA_OUT;
        break;
#endif
#if (CCID_INTERFACE_NUM>3)
    case 3:
        chEndpointOut = CCID_INTERFACE3_EPT_DATA_OUT;
        break;
#endif
    }

    return USBD_Read(chEndpointOut, pBuffer, dLength, fCallback, pArgument);
}


继续看:

//------------------------------------------------------------------------------
/// Reads incoming data on an USB endpoint (OUT)
/// \param bEndpoint Index of endpoint
/// \param *pData  Data to be readen
/// \param dLength Data length to be receive
/// \param fCallback  Callback to be call after the success command
/// \param *pArgument Callback argument
/// \return USBD_STATUS_LOCKED or USBD_STATUS_SUCCESS
//------------------------------------------------------------------------------
char USBD_Read( unsigned char    bEndpoint,
                void             *pData,
                unsigned int     dLength,
                TransferCallback fCallback,
                void             *pArgument )
{
    Endpoint *pEndpoint = &(endpoints[bEndpoint]);
    Transfer *pTransfer = &(pEndpoint->transfer);
  
    // Return if the endpoint is not in IDLE state
    if (pEndpoint->state != UDP_ENDPOINT_IDLE) {

        return USBD_STATUS_LOCKED;
    }

    TRACE_DEBUG_WP("Read%d(%d) ", bEndpoint, dLength);

    // Endpoint enters Receiving state
    pEndpoint->state = UDP_ENDPOINT_RECEIVING;

    // Set the transfer descriptor
    pTransfer->pData = pData;
    pTransfer->remaining = dLength;
    pTransfer->buffered = 0;
    pTransfer->transferred = 0;
    pTransfer->fCallback = fCallback;
    pTransfer->pArgument = pArgument;

#ifdef DMA
    // Test if endpoint type control
    if(AT91C_UDPHS_EPT_TYPE_CTL_EPT == (AT91C_UDPHS_EPT_TYPE&(AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCFG))) {
#endif
        // Control endpoint
        // Enable endpoint IT
        AT91C_BASE_UDPHS->UDPHS_IEN |= (1 << SHIFT_INTERUPT << bEndpoint);
        AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCTLENB = AT91C_UDPHS_RX_BK_RDY;
#ifdef DMA
    }
    else {

        TRACE_DEBUG_WP("Read%d(%d) ", bEndpoint, dLength);

        // Others endpoints (not control)
        if( pTransfer->remaining > DMA_MAX_FIFO_SIZE ) {

            // Transfer the max
            pTransfer->buffered = DMA_MAX_FIFO_SIZE;    
        }
        else {
            // Transfer the good size
            pTransfer->buffered = pTransfer->remaining;
        }

        AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMAADDRESS = (unsigned int)(pTransfer->pData);

        // Clear unwanted interrupts
        AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMASTATUS;

        // Enable DMA endpoint interrupt
        AT91C_BASE_UDPHS->UDPHS_IEN |= (1 << SHIFT_DMA << bEndpoint);

        TRACE_DEBUG_WP("\n\r_RR:%d ", pTransfer->remaining );
        TRACE_DEBUG_WP("B:%d ", pTransfer->buffered );
        TRACE_DEBUG_WP("T:%d ", pTransfer->transferred );

        // DMA config
        AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMACONTROL = 0; // raz
        AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMACONTROL =
                                 ( ((pTransfer->buffered << 16) & AT91C_UDPHS_BUFF_COUNT)
                                   | AT91C_UDPHS_END_TR_EN
                                   | AT91C_UDPHS_END_TR_IT
                                   | AT91C_UDPHS_END_B_EN
                                   | AT91C_UDPHS_END_BUFFIT
                                   | AT91C_UDPHS_CHANN_ENB );
    }
#endif

    return USBD_STATUS_SUCCESS;
}

到这看明白了吧,是上层的CCID_SmartCardRequest_V1()通过usb_read()设置的
 

看下端点的结构体:

//------------------------------------------------------------------------------
/// Describes the state of an endpoint of the UDP controller.
//------------------------------------------------------------------------------
typedef struct
{
    /// Current endpoint state.
    volatile unsigned char  state;
    /// Current reception bank (0 or 1).
    unsigned char  bank;
    /// Maximum packet size for the endpoint.
    unsigned short size;
    /// Describes an ongoing transfer (if current state is either
    ///  <UDP_ENDPOINT_SENDING> or <UDP_ENDPOINT_RECEIVING>)
    Transfer       transfer;
    /// Special case for send a ZLP
    unsigned char  sendZLP;
} Endpoint;

/// Describes an ongoing transfer on a UDP endpoint.
typedef struct
{
    /// Pointer to a data buffer used for emission/reception.
    char             *pData;
    /// Number of bytes which have been written into the UDP internal FIFO
    /// buffers.
    volatile int     buffered;
    /// Number of bytes which have been sent/received.
    volatile int     transferred;
    /// Number of bytes which have not been buffered/transferred yet.
    volatile int     remaining;
    /// Optional callback to invoke when the transfer completes.
    volatile TransferCallback fCallback;
    /// Optional argument to the callback function.
    void             *pArgument;
} Transfer;

至此,我们把自己每个端点的处理函数CCIDCommandDispatcher_V1()绑定到了相应的端点上。

问题:

1、到目前为止,有一点我不是很理解,为什么每次收到后,需要对相应的DMA重新初始化,相应的端点也同样做了初始化,我查看了HID、CDC历程,都是这么处理的。因此在CCIDCommandDispatcher_V1()里面我们需要调用usb_read()做相应初始化,或者利用查询方式,在应用层初始化。
 

<--------------------------------------------------------2021.08.05------------------------------------------------------>

一、今天测试MCU的CDC功能,在其它产品上在跑了,没问题了。但是应用到我的产品上,发现老是复位。和没有问题的产品区别:

1)它是USB数据到UART1,我是USB数据到UART3;

2)它是连接的其它UART模块,我是连接的NB;

通过屏蔽代码,发现是看门狗复位。

通过屏蔽代码,NB上电不使能,没有问题。

NB上电会通过串口往外发信息,导致MCU“死机”,看门狗复位。

在分析原因,上电USB会初始化,和上位机交互,如果这个时候应用层通过USB发送数据,会导致USB出问题。解决方案是等待USB初始化完成,才允许应用层通过USB发送数据。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值