SAM9G45之USB学习笔记

秒速五厘米 2022-06-02 09:12 234阅读 0赞

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

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

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

SouthEast

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

SouthEast 1

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

SouthEast 2

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

三、Bus Hound配置问题

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

SouthEast 3

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

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

解决方法:

1、修改P_ID或者V_ID的值;

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

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

SouthEast 4

《———————————————————————————————————————————————————-》

官方USB协议帧学习笔记:

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

SouthEast 5

图1 协议栈的结构

SouthEast 6

图2 USB的状态机

SouthEast 7

图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()

  1. //------------------------------------------------------------------------------
  2. /// Handles a completed transfer on the given endpoint, invoking the
  3. /// configured callback if any.
  4. /// \param bEndpoint Number of the endpoint for which the transfer has completed.
  5. /// \param bStatus Status code returned by the transfer operation
  6. //------------------------------------------------------------------------------
  7. static void UDPHS_EndOfTransfer( unsigned char bEndpoint, char bStatus )
  8. {
  9. Endpoint *pEndpoint = &(endpoints[bEndpoint]);
  10. Transfer *pTransfer = &(pEndpoint->transfer);
  11. // Check that endpoint was sending or receiving data
  12. if( (pEndpoint->state == UDP_ENDPOINT_RECEIVING)
  13. || (pEndpoint->state == UDP_ENDPOINT_IDLE)
  14. || (pEndpoint->state == UDP_ENDPOINT_SENDING) ) {
  15. TRACE_DEBUG_WP("UDPHS_EndOfTransfer ");
  16. if(pEndpoint->state == UDP_ENDPOINT_SENDING) {
  17. pEndpoint->sendZLP = 0;
  18. }
  19. // Endpoint returns in Idle state
  20. //if(pEndpoint->state == UDP_ENDPOINT_SENDING){
  21. pEndpoint->state = UDP_ENDPOINT_IDLE;
  22. //}
  23. // Invoke callback is present
  24. if (pTransfer->fCallback != 0) {
  25. ((TransferCallback) pTransfer->fCallback)
  26. (pTransfer->pArgument,
  27. bStatus,
  28. pTransfer->transferred,
  29. pTransfer->remaining + pTransfer->buffered);
  30. }
  31. else {
  32. TRACE_DEBUG_WP("No callBack\n\r");
  33. }
  34. }
  35. }

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

  1. void CCID_SmartCardRequest_V1( uint8_t chCcidNum )
  2. {
  3. unsigned char bStatus;
  4. if(chCcidNum >= CCID_INTERFACE_NUM){
  5. TRACE_ERROR("CCID_SmartCardRequest chCcidNum >= CCID_INTERFACE_NUM\n\r");
  6. return;
  7. }
  8. //do{
  9. bStatus = CCID_Read_V1((void*)&ccidDriver.tCcidStruct[chCcidNum].sCcidCommand,
  10. sizeof(S_ccid_bulk_out_header),
  11. (TransferCallback)&CCIDCommandDispatcher_V1,
  12. (void*)chCcidNum );//直接把值变为地址传输
  13. //}while (bStatus != USBD_STATUS_SUCCESS);
  14. }

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

  1. //------------------------------------------------------------------------------
  2. /// Reads data from the Data OUT endpoint
  3. /// \param pBuffer Buffer to store the received data
  4. /// \param dLength data buffer length
  5. /// \param fCallback Optional callback function
  6. /// \param pArgument Optional parameter for the callback function
  7. /// \return USBD_STATUS_LOCKED or USBD_STATUS_SUCCESS
  8. //------------------------------------------------------------------------------
  9. /*
  10. unsigned char CCID_Read(void *pBuffer,
  11. unsigned int dLength,
  12. TransferCallback fCallback,
  13. void *pArgument)
  14. {
  15. return USBD_Read(CCID_EPT_DATA_OUT, pBuffer, dLength, fCallback, pArgument);
  16. }*/
  17. unsigned char CCID_Read_V1( void *pBuffer,
  18. unsigned int dLength,
  19. TransferCallback fCallback,
  20. void *pArgument)
  21. {
  22. uint8_t chEndpointOut = 0;
  23. if(((uint8_t)pArgument) >= CCID_INTERFACE_NUM){
  24. TRACE_ERROR("CCID_Read chCcidNum >= CCID_INTERFACE_NUM\n\r");
  25. return USBD_STATUS_INVALID_PARAMETER;
  26. }
  27. switch((uint8_t)pArgument){
  28. #if (CCID_INTERFACE_NUM>0)
  29. case 0:
  30. chEndpointOut = CCID_INTERFACE0_EPT_DATA_OUT;
  31. break;
  32. #endif
  33. #if (CCID_INTERFACE_NUM>1)
  34. case 1:
  35. chEndpointOut = CCID_INTERFACE1_EPT_DATA_OUT;
  36. break;
  37. #endif
  38. #if (CCID_INTERFACE_NUM>2)
  39. case 2:
  40. chEndpointOut = CCID_INTERFACE2_EPT_DATA_OUT;
  41. break;
  42. #endif
  43. #if (CCID_INTERFACE_NUM>3)
  44. case 3:
  45. chEndpointOut = CCID_INTERFACE3_EPT_DATA_OUT;
  46. break;
  47. #endif
  48. }
  49. return USBD_Read(chEndpointOut, pBuffer, dLength, fCallback, pArgument);
  50. }

继续看:

  1. //------------------------------------------------------------------------------
  2. /// Reads incoming data on an USB endpoint (OUT)
  3. /// \param bEndpoint Index of endpoint
  4. /// \param *pData Data to be readen
  5. /// \param dLength Data length to be receive
  6. /// \param fCallback Callback to be call after the success command
  7. /// \param *pArgument Callback argument
  8. /// \return USBD_STATUS_LOCKED or USBD_STATUS_SUCCESS
  9. //------------------------------------------------------------------------------
  10. char USBD_Read( unsigned char bEndpoint,
  11. void *pData,
  12. unsigned int dLength,
  13. TransferCallback fCallback,
  14. void *pArgument )
  15. {
  16. Endpoint *pEndpoint = &(endpoints[bEndpoint]);
  17. Transfer *pTransfer = &(pEndpoint->transfer);
  18. // Return if the endpoint is not in IDLE state
  19. if (pEndpoint->state != UDP_ENDPOINT_IDLE) {
  20. return USBD_STATUS_LOCKED;
  21. }
  22. TRACE_DEBUG_WP("Read%d(%d) ", bEndpoint, dLength);
  23. // Endpoint enters Receiving state
  24. pEndpoint->state = UDP_ENDPOINT_RECEIVING;
  25. // Set the transfer descriptor
  26. pTransfer->pData = pData;
  27. pTransfer->remaining = dLength;
  28. pTransfer->buffered = 0;
  29. pTransfer->transferred = 0;
  30. pTransfer->fCallback = fCallback;
  31. pTransfer->pArgument = pArgument;
  32. #ifdef DMA
  33. // Test if endpoint type control
  34. if(AT91C_UDPHS_EPT_TYPE_CTL_EPT == (AT91C_UDPHS_EPT_TYPE&(AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCFG))) {
  35. #endif
  36. // Control endpoint
  37. // Enable endpoint IT
  38. AT91C_BASE_UDPHS->UDPHS_IEN |= (1 << SHIFT_INTERUPT << bEndpoint);
  39. AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCTLENB = AT91C_UDPHS_RX_BK_RDY;
  40. #ifdef DMA
  41. }
  42. else {
  43. TRACE_DEBUG_WP("Read%d(%d) ", bEndpoint, dLength);
  44. // Others endpoints (not control)
  45. if( pTransfer->remaining > DMA_MAX_FIFO_SIZE ) {
  46. // Transfer the max
  47. pTransfer->buffered = DMA_MAX_FIFO_SIZE;
  48. }
  49. else {
  50. // Transfer the good size
  51. pTransfer->buffered = pTransfer->remaining;
  52. }
  53. AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMAADDRESS = (unsigned int)(pTransfer->pData);
  54. // Clear unwanted interrupts
  55. AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMASTATUS;
  56. // Enable DMA endpoint interrupt
  57. AT91C_BASE_UDPHS->UDPHS_IEN |= (1 << SHIFT_DMA << bEndpoint);
  58. TRACE_DEBUG_WP("\n\r_RR:%d ", pTransfer->remaining );
  59. TRACE_DEBUG_WP("B:%d ", pTransfer->buffered );
  60. TRACE_DEBUG_WP("T:%d ", pTransfer->transferred );
  61. // DMA config
  62. AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMACONTROL = 0; // raz
  63. AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMACONTROL =
  64. ( ((pTransfer->buffered << 16) & AT91C_UDPHS_BUFF_COUNT)
  65. | AT91C_UDPHS_END_TR_EN
  66. | AT91C_UDPHS_END_TR_IT
  67. | AT91C_UDPHS_END_B_EN
  68. | AT91C_UDPHS_END_BUFFIT
  69. | AT91C_UDPHS_CHANN_ENB );
  70. }
  71. #endif
  72. return USBD_STATUS_SUCCESS;
  73. }

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

看下端点的结构体:

  1. //------------------------------------------------------------------------------
  2. /// Describes the state of an endpoint of the UDP controller.
  3. //------------------------------------------------------------------------------
  4. typedef struct
  5. {
  6. /// Current endpoint state.
  7. volatile unsigned char state;
  8. /// Current reception bank (0 or 1).
  9. unsigned char bank;
  10. /// Maximum packet size for the endpoint.
  11. unsigned short size;
  12. /// Describes an ongoing transfer (if current state is either
  13. /// <UDP_ENDPOINT_SENDING> or <UDP_ENDPOINT_RECEIVING>)
  14. Transfer transfer;
  15. /// Special case for send a ZLP
  16. unsigned char sendZLP;
  17. } Endpoint;
  18. /// Describes an ongoing transfer on a UDP endpoint.
  19. typedef struct
  20. {
  21. /// Pointer to a data buffer used for emission/reception.
  22. char *pData;
  23. /// Number of bytes which have been written into the UDP internal FIFO
  24. /// buffers.
  25. volatile int buffered;
  26. /// Number of bytes which have been sent/received.
  27. volatile int transferred;
  28. /// Number of bytes which have not been buffered/transferred yet.
  29. volatile int remaining;
  30. /// Optional callback to invoke when the transfer completes.
  31. volatile TransferCallback fCallback;
  32. /// Optional argument to the callback function.
  33. void *pArgument;
  34. } 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 条评论,234人围观)

还没有评论,来说两句吧...

相关阅读

    相关 mysql45学习笔记

    写个博客记录我的学习过程,我也会相对容易坚持下去,不至于半途而废 强烈建议自己去看这个课,我这里只记录知识点,方便自己回顾和复习,看我这不行的,人家的都有例子和引导,让你理解

    相关 SAM9G45USB学习笔记

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

    相关 SAM9G45死机问题

    最近有个项目,用的SAM9G45平台,遇到一个问题,就是运行一段时间‘’死机‘’问题,现象就是下发协议没有反应。这个问题解决耗费了好长时间,现记录如下,希望能够帮助需要的人吧。