Составное USB устройство на STM32F4

После реализации USB audio, захотелось прикрутить к проекту еще и ком-порт. Естественно, через USB CDC. Тема в интернете освещена многократно, но, в основном, по форумам производителей МК. Ближе всего подходит к моему случаю аппнота NXP AN11115 (http://www.nxp.com/documents/application_note/AN11115.zip) в которой разбирается составное устройство UAC+CDC.

Виртуальный ком-порт можно реализовать множеством способов. Например, Prolific в своих микросхемах PL2303 объявляет устройство как Vendor-specific class, один интерфейс с двумя конечными точками. То есть, сама по себе эта микросхема никакого отношения к CDC не имеет, вся необходимая логика реализуется в драйвере. Драйвер копирует данные в/из виртуального порта в конечные точки USB устройства путем программирования соответствующих URB (USB request block — запрос на передачу по USB).
Я выбрал другой путь — использование драйвера ком-порта операционной системы. Пример USB CDC есть от ST вместе с USB-стеком и драйвером COM-порта на сайте.
Объединение двух устройств сводится к редактированию дескриптора конфигурации

static uint8_t usbd_audio_CfgDesc[AUDIO_CONFIG_DESC_SIZE] =
{
/* Configuration 1 */
USB_CONFIGUARTION_DESC_SIZE,          /* bLength */
USB_CONFIGURATION_DESCRIPTOR_TYPE,    /* bDescriptorType */
LOBYTE(AUDIO_CONFIG_DESC_SIZE),       /* wTotalLength  118 bytes*/
HIBYTE(AUDIO_CONFIG_DESC_SIZE),
0x04,                                 /* bNumInterfaces */
0x01,                                 /* bConfigurationValue */
0x00,                                 /* iConfiguration */
0x80,                                 /* bmAttributes  BUS Powred*/
0xF0,                                 /* bMaxPower = 500 mA*/
/* 09 byte*/

/* UAC IAD */
INTERFACE_ASSOC_DESC_SIZE,	// bLength: Interface Descriptor size
USB_INTERFACE_ASSOC_DESCRIPTOR_TYPE,	// bDescriptorType: IAD
0x00,	// bFirstInterface
0x02,	// bInterfaceCount
USB_DEVICE_CLASS_AUDIO,	// bFunctionClass: Audio
0x00,	// bFunctionSubClass
0x00,	// bFunctionProtocol
0x00,	// Interface string index

/* USB Speaker Standard interface descriptor */
AUDIO_INTERFACE_DESC_SIZE,            /* bLength */
USB_INTERFACE_DESCRIPTOR_TYPE,        /* bDescriptorType */
0x00,                                 /* bInterfaceNumber */
0x00,                                 /* bAlternateSetting */
0x00,                                 /* bNumEndpoints */
USB_DEVICE_CLASS_AUDIO,               /* bInterfaceClass */
AUDIO_SUBCLASS_AUDIOCONTROL,          /* bInterfaceSubClass */
AUDIO_PROTOCOL_UNDEFINED,             /* bInterfaceProtocol */
0x00,                                 /* iInterface */
/* 09 byte*/

/* USB Speaker Class-specific AC Interface Descriptor */
AUDIO_INTERFACE_DESC_SIZE,            /* bLength */
AUDIO_INTERFACE_DESCRIPTOR_TYPE,      /* bDescriptorType */
AUDIO_CONTROL_HEADER,                 /* bDescriptorSubtype */
0x00,          /* 1.00 */             /* bcdADC */
0x01,
AUDIO_INTERFACE_DESC_SIZE+AUDIO_INPUT_TERMINAL_DESC_SIZE+AUDIO_FEATURE_UNIT_DESC_SZ+AUDIO_OUTPUT_TERMINAL_DESC_SIZE,   /* wTotalLength = 40*/
0x00,
0x01,                                 /* bInCollection */
0x01,                                 /* baInterfaceNr */
/* 09 byte*/

/* USB Speaker Input Terminal Descriptor - host sends audio here*/
AUDIO_INPUT_TERMINAL_DESC_SIZE,       /* bLength */
AUDIO_INTERFACE_DESCRIPTOR_TYPE,      /* bDescriptorType */
AUDIO_CONTROL_INPUT_TERMINAL,         /* bDescriptorSubtype */
0x01,                                 /* bTerminalID */
0x01,                                 /* wTerminalType AUDIO_TERMINAL_USB_STREAMING   0x0101 */
0x01,
0x00,                                 /* bAssocTerminal */
0x02,                                 /* bNrChannels */
0x03,                                 /* wChannelConfig 0x0003  FL,FR */
0x00,
0x00,                                 /* iChannelNames */
0x00,                                 /* iTerminal */
/* 12 byte*/

/* USB Speaker Audio Feature Unit Descriptor - audio is processed here */
AUDIO_FEATURE_UNIT_DESC_SZ,           /* bLength */
AUDIO_INTERFACE_DESCRIPTOR_TYPE,      /* bDescriptorType */
AUDIO_CONTROL_FEATURE_UNIT,           /* bDescriptorSubtype */
AUDIO_OUT_STREAMING_CTRL,             /* bUnitID */
0x01,                                 /* bSourceID */
0x01,                                 /* bControlSize */
AUDIO_CONTROL_MUTE,                   /* bmaControls(0) */
0x00,                                 /* bmaControls(1) */
0x00,									/* bmaControls(2) */
0x00,                                 /* iTerminal */
/* 10 byte*/

/*USB Speaker Output Terminal Descriptor */
AUDIO_OUTPUT_TERMINAL_DESC_SIZE,      /* bLength */
AUDIO_INTERFACE_DESCRIPTOR_TYPE,      /* bDescriptorType */
AUDIO_CONTROL_OUTPUT_TERMINAL,        /* bDescriptorSubtype */
0x03,                                 /* bTerminalID */
0x01,                                 /* wTerminalType  0x0301*/
0x03,
0x00,                                 /* bAssocTerminal */
0x02,                                 /* bSourceID */
0x00,                                 /* iTerminal */
/* 09 byte*/

/* USB Speaker Standard AS Interface Descriptor - Audio Streaming Zero Bandwith */
/* Interface 1, Alternate Setting 0                                             */
AUDIO_INTERFACE_DESC_SIZE,  			/* bLength */
USB_INTERFACE_DESCRIPTOR_TYPE,        /* bDescriptorType */
0x01,                                 /* bInterfaceNumber */
0x00,                                 /* bAlternateSetting */
0x00,                                 /* bNumEndpoints */
USB_DEVICE_CLASS_AUDIO,               /* bInterfaceClass */
AUDIO_SUBCLASS_AUDIOSTREAMING,        /* bInterfaceSubClass */
AUDIO_PROTOCOL_UNDEFINED,             /* bInterfaceProtocol */
0x00,                                 /* iInterface */
/* 09 byte*/

/* USB Speaker Standard AS Interface Descriptor - Audio Streaming Operational */
/* Interface 1, Alternate Setting 1                                           */
AUDIO_INTERFACE_DESC_SIZE,  			/* bLength */
USB_INTERFACE_DESCRIPTOR_TYPE,        /* bDescriptorType */
0x01,                                 /* bInterfaceNumber */
0x01,                                 /* bAlternateSetting */
0x02,                                 /* bNumEndpoints - Audio Out and Feedback enpoint*/
USB_DEVICE_CLASS_AUDIO,               /* bInterfaceClass */
AUDIO_SUBCLASS_AUDIOSTREAMING,        /* bInterfaceSubClass */
AUDIO_PROTOCOL_UNDEFINED,             /* bInterfaceProtocol */
0x00,                                 /* iInterface */
/* 09 byte*/

/* USB Speaker Audio Streaming Interface Descriptor */
AUDIO_STREAMING_INTERFACE_DESC_SIZE,  /* bLength */
AUDIO_INTERFACE_DESCRIPTOR_TYPE,      /* bDescriptorType */
AUDIO_STREAMING_GENERAL,              /* bDescriptorSubtype */
0x01,                                 /* bTerminalLink */
0x04,                                 /* bDelay */
0x01,                                 /* wFormatTag AUDIO_FORMAT_PCM  0x0001*/
0x00,
/* 07 byte*/

/* USB Speaker Audio Type III Format Interface Descriptor */
AUDIO_FORMAT_TYPE_I_DESC_SZ,          /* bLength */
AUDIO_INTERFACE_DESCRIPTOR_TYPE,      /* bDescriptorType */
AUDIO_STREAMING_FORMAT_TYPE,          /* bDescriptorSubtype */
AUDIO_FORMAT_TYPE_I,                  /* bFormatType */
0x02,                                 /* bNrChannels */
0x02,                                 /* bSubFrameSize :  2 Bytes per frame (16bits) */
16,                                   /* bBitResolution (16-bits per sample) */
0x01,                                 /* bSamFreqType only one frequency supported */
SAMPLE_FREQ(USBD_AUDIO_FREQ),         /* Audio sampling frequency coded on 3 bytes */
/* 11 byte*/

/* Endpoint 1 - Standard Descriptor */
AUDIO_STANDARD_ENDPOINT_DESC_SIZE,    /* bLength */
USB_ENDPOINT_DESCRIPTOR_TYPE,         /* bDescriptorType */
AUDIO_OUT_EP,                         /* bEndpointAddress 1 out endpoint*/
0x05,							      /* bmAttributes */
AUDIO_OUT_PACKET+16,0,    			  /* wMaxPacketSize in Bytes ((Freq(Samples)+1)*2(Stereo)*2(HalfWord)) */
0x01,                                 /* bInterval */
0x01,                                 /* bRefresh */
AUDIO_IN_EP,                          /* bSynchAddress */
/* 09 byte*/

/* Endpoint - Audio Streaming (Class-specific) Descriptor*/
AUDIO_STREAMING_ENDPOINT_DESC_SIZE,   /* bLength */
AUDIO_ENDPOINT_DESCRIPTOR_TYPE,       /* bDescriptorType */
AUDIO_ENDPOINT_GENERAL,               /* bDescriptor */
0x00,                                 /* bmAttributes */
0x00,                                 /* bLockDelayUnits */
0x32,                                 /* wLockDelay */
0x00,
/* 07 byte*/

/* Endpoint 2 - Standard Descriptor */
AUDIO_STANDARD_ENDPOINT_DESC_SIZE,    /* bLength */
USB_ENDPOINT_DESCRIPTOR_TYPE,         /* bDescriptorType */
AUDIO_IN_EP,                         /* bEndpointAddress 2 in endpoint*/
0x11,        						   /* bmAttributes */
3,0,   						    /* wMaxPacketSize in Bytes 4 */
1,								/* bInterval 1ms*/
SOF_RATE,							/* bRefresh 64ms*/
0x00,                             /* bSynchAddress */
/* 09 byte*/

/* CDC IAD */
0x08,	// bLength: Interface Descriptor size
0x0B,	// bDescriptorType: IAD
0x02,	// bFirstInterface
0x02,	// bInterfaceCount
0x02,	// bFunctionClass: CDC
0x02,	// bFunctionSubClass
0x01,	// bFunctionProtocol
0x02,	// iFunction

/*Interface Descriptor*/
0x09,   /* bLength: Interface Descriptor size */
USB_INTERFACE_DESCRIPTOR_TYPE,  /* bDescriptorType: Interface */  /* Interface descriptor type */
0x02,   /* bInterfaceNumber: Number of Interface */
0x00,   /* bAlternateSetting: Alternate setting */
0x01,   /* bNumEndpoints: One endpoints used */
0x02,   /* bInterfaceClass: Communication Interface Class */
0x02,   /* bInterfaceSubClass: Abstract Control Model */
0x01,   /* bInterfaceProtocol: Common AT commands */
0x00,   /* iInterface: */

/*Header Functional Descriptor*/
0x05,   /* bLength: Endpoint Descriptor size */
CDC_INTERFACE_DESCRIPTOR_TYPE,   /* bDescriptorType: CS_INTERFACE */
0x00,   /* bDescriptorSubtype: Header Func Desc */
0x10,   /* bcdCDC: spec release number */
0x01,

/*Call Managment Functional Descriptor*/
0x05,   /* bFunctionLength */
0x24,   /* bDescriptorType: CS_INTERFACE */
0x01,   /* bDescriptorSubtype: Call Management Func Desc */
0x00,   /* bmCapabilities: D0+D1 */
0x03,   /* bDataInterface: 3 */

/*ACM Functional Descriptor*/
0x04,   /* bFunctionLength */
0x24,   /* bDescriptorType: CS_INTERFACE */
0x02,   /* bDescriptorSubtype: Abstract Control Management desc */
0x02,   /* bmCapabilities */

/*Union Functional Descriptor*/
0x05,   /* bFunctionLength */
0x24,   /* bDescriptorType: CS_INTERFACE */
0x06,   /* bDescriptorSubtype: Union func desc */
0x02,   /* bMasterInterface: Communication class interface */
0x03,   /* bSlaveInterface0: Data Class Interface */

/*Endpoint 3 Descriptor*/
0x07,  									 /* bLength: Endpoint Descriptor size */
USB_ENDPOINT_DESCRIPTOR_TYPE, 			/* bDescriptorType: Endpoint */
CDC_STATE_IN_EP,   						/* bEndpointAddress: (IN3) */
USB_ENDPOINT_TYPE_INTERRUPT,   			/* bmAttributes: Interrupt */
VIRTUAL_COM_PORT_INT_SIZE,      			/* wMaxPacketSize: */
0x00,
0xFF,   									/* bInterval: */

/*Data class interface descriptor*/
0x09,   /* bLength: Endpoint Descriptor size */
USB_INTERFACE_DESCRIPTOR_TYPE,  /* bDescriptorType: */
0x03,   /* bInterfaceNumber: Number of Interface */
0x00,   /* bAlternateSetting: Alternate setting */
0x02,   /* bNumEndpoints: Two endpoints used */
0x0A,   /* bInterfaceClass: CDC */
0x00,   /* bInterfaceSubClass: */
0x00,   /* bInterfaceProtocol: */
0x00,   /* iInterface: */

/*Endpoint 2 Descriptor*/
0x07,   									/* bLength: Endpoint Descriptor size */
USB_ENDPOINT_DESCRIPTOR_TYPE,   			/* bDescriptorType: Endpoint */
CDC_OUT_DATA_EP,   						/* bEndpointAddress: (OUT2) */
USB_ENDPOINT_TYPE_BULK,   				/* bmAttributes: Bulk */
VIRTUAL_COM_PORT_DATA_SIZE,             	/* wMaxPacketSize: */
0x00,
0x00,   									/* bInterval: ignore for Bulk transfer */

/*Endpoint 2 Descriptor*/
0x07,   /* bLength: Endpoint Descriptor size */
USB_ENDPOINT_DESCRIPTOR_TYPE,   			/* bDescriptorType: Endpoint */
CDC_IN_DATA_EP,   						/* bEndpointAddress: (IN2) */
USB_ENDPOINT_TYPE_BULK,   				/* bmAttributes: Bulk */
VIRTUAL_COM_PORT_DATA_SIZE,             	/* wMaxPacketSize: */
0x00,
0x00    									/* bInterval */
};

Вносим в инициализацию также открытие конечных точек CDC:

static uint8_t  usbd_audio_Init (void  *pdev, 
                                 uint8_t cfgidx)
{  
  /* Open EP OUT */
  DCD_EP_Open(pdev,
              AUDIO_OUT_EP,
              AUDIO_OUT_PACKET+16,
              USB_OTG_EP_ISOC);

  /* Open EP IN */
  DCD_EP_Open(pdev,
		  	  AUDIO_IN_EP,
              3,
              USB_OTG_EP_ISOC);
  DCD_EP_Flush(pdev,AUDIO_IN_EP);

  DCD_EP_Open(pdev,
		  	  CDC_STATE_IN_EP,
		  	  VIRTUAL_COM_PORT_INT_SIZE,
              USB_OTG_EP_INT);
  DCD_EP_Open(pdev,
              CDC_IN_DATA_EP,
              VIRTUAL_COM_PORT_DATA_SIZE,
              USB_OTG_EP_BULK);
  DCD_EP_Open(pdev,
		  	  CDC_OUT_DATA_EP,
		  	  VIRTUAL_COM_PORT_DATA_SIZE,
              USB_OTG_EP_BULK);

  /* Initialize the Audio output Hardware layer */
  if (AUDIO_OUT_fops.Init(USBD_AUDIO_FREQ, DEFAULT_VOLUME, 0) != USBD_OK)
  {
    return USBD_FAIL;
  }
  flag=1;
  return USBD_OK;
}

Деинициализация банальна и закрывает все конечные точки.
Разбор SETUP-пакетов становится немного сложнее. Нужно учесть, что теперь приходят class-specific запросы для разных классов.

/**
  * @brief  usbd_audio_Setup
  *         Handles the Audio control request parsing.
  * @param  pdev: instance
  * @param  req: usb requests
  * @retval status
  */
static uint8_t  usbd_audio_Setup (void  *pdev, 
                                  USB_SETUP_REQ *req)
{
  uint16_t len;
  uint8_t  *pbuf;
  uint32_t tmp;
  
  switch (req->bmRequest & USB_REQ_TYPE_MASK)
  {
    /* AUDIO Class Requests -------------------------------*/
  case USB_REQ_TYPE_CLASS :    
    switch (req->bRequest)
    {
    case AUDIO_REQ_GET_CUR:
      AUDIO_Req_GetCurrent(pdev, req);
      break;
      
    case AUDIO_REQ_SET_CUR:
      AUDIO_Req_SetCurrent(pdev, req);   
      break;

    case CDC_REQ_SET_LINE_CODING:
    	/* Set the value of the current command to be processed */
    	cdcCmd = req->bRequest;
    	cdcLen = req->wLength;
    	/* Prepare the reception of the buffer over EP0
    	 Next step: the received data will be managed in usbd_cdc_EP0_TxSent()
    	 function. */
    	USBD_CtlPrepareRx (pdev,CmdBuff, req->wLength);
    	break;

    case CDC_REQ_GET_LINE_CODING:
        /* Get the data to be sent to Host from interface layer */
        APP_FOPS.pIf_Ctrl(req->bRequest, CmdBuff, req->wLength);
        /* Send the data to the host */
        USBD_CtlSendData (pdev,
                          CmdBuff,
                          req->wLength);
    	break;

    case CDC_REQ_SET_CONTROL_LINE_STATE:
    	return USBD_OK;

    default:
      USBD_CtlError (pdev, req);
      return USBD_FAIL;
    }
    break;
    
    /* Standard Requests -------------------------------*/
  case USB_REQ_TYPE_STANDARD:
    switch (req->bRequest)
    {
    case USB_REQ_GET_DESCRIPTOR: 
      if( (req->wValue >> 8) == AUDIO_DESCRIPTOR_TYPE)
      {
#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED
        pbuf = usbd_audio_Desc;   
#else
        pbuf = usbd_audio_CfgDesc + 18;
#endif 
        len = MIN(USB_AUDIO_DESC_SIZ , req->wLength);
      }
      
      USBD_CtlSendData (pdev, 
                        pbuf,
                        len);
      break;
      
    case USB_REQ_GET_INTERFACE :
      if ((uint8_t)(req->wIndex)==1) //Audio streaming interface
    	USBD_CtlSendData (pdev,
                        (uint8_t *)&usbd_audio_AltSet,
                        1);
      else if ((uint8_t)(req->wIndex)==3) { //CDC data interface
    	  USBD_CtlSendData (pdev,(uint8_t *)&usbd_cdc_AltSet,1);
	};
      break;
      
    case USB_REQ_SET_INTERFACE :

    	if ((uint8_t)(req->wValue) < AUDIO_TOTAL_IF_NUM && (uint8_t)(req->wIndex)==1) //Alt Setting for audio
      {
        usbd_audio_AltSet = (uint8_t)(req->wValue);
        if (usbd_audio_AltSet == 1)
        {SOF_num=0; };
        flag=0;
        DCD_EP_Flush(pdev,AUDIO_IN_EP);
      }
      else if ((uint8_t)(req->wIndex)!=1) //Alt Setting for CDC
    	      {
    			usbd_cdc_AltSet = (uint8_t)(req->wValue);
    	      }
      else
      {
        /* Call the error management function (command will be nacked */
        USBD_CtlError (pdev, req);
      }
      break;
    }
  }
  return USBD_OK;
}

Поскольку запросы «на запись» состоят из двух частей (запрос и данные), дорабатываем и разбор данных запроса

static uint8_t  usbd_audio_EP0_RxReady (void  *pdev)
{ 
  uint32_t temp;
	/* Check if an AudioControl request has been issued */
  if (AudioCtlCmd == AUDIO_REQ_SET_CUR)
  {/* In this driver, to simplify code, only SET_CUR request is managed */
    /* Check for which addressed unit the AudioControl request has been issued */
	if (AudioCtlUnit == AUDIO_OUT_STREAMING_CTRL)
    {/* In this driver, to simplify code, only one unit is manage */
      /* Call the audio interface mute function */
      AUDIO_OUT_fops.MuteCtl(AudioCtl[0]);
/* Reset the AudioCtlCmd variable to prevent re-entering this function */
      AudioCtlCmd = 0;
      AudioCtlLen = 0;
    }
  }

  if (cdcCmd != NO_CMD)
  {
    /* Process the data */
    APP_FOPS.pIf_Ctrl(cdcCmd, CmdBuff, cdcLen);

    /* Reset the command variable to default value */
    cdcCmd = NO_CMD;
  }
  return USBD_OK;
}

Дорабатываем обработчики чтения и записи в конечные точки данных

static uint8_t  usbd_audio_DataIn (void *pdev, uint8_t epnum)
{
	if (epnum == (AUDIO_IN_EP&0x7f))
	{
		flag=0;
		SOF_num=0;
	};
	if (epnum==(CDC_IN_DATA_EP&0x7f))
	{  if (USB_Tx_State == 1)
	  {
	    if (APP_Rx_length == 0)
	    {
	      USB_Tx_State = 0;
	    }
	    else
	    {
	      if (APP_Rx_length > CDC_DATA_IN_PACKET_SIZE){
	        USB_Tx_ptr = APP_Rx_ptr_out;
	        USB_Tx_length = CDC_DATA_IN_PACKET_SIZE;

	        APP_Rx_ptr_out += CDC_DATA_IN_PACKET_SIZE;
	        APP_Rx_length -= CDC_DATA_IN_PACKET_SIZE;
	      }
	      else
	      {
	        USB_Tx_ptr = APP_Rx_ptr_out;
	        USB_Tx_length = APP_Rx_length;

	        APP_Rx_ptr_out += APP_Rx_length;
	        APP_Rx_length = 0;
	      }

	      /* Prepare the available data buffer to be sent on IN endpoint */
	      DCD_EP_Tx (pdev,CDC_IN_DATA_EP,(uint8_t*)&APP_Rx_Buffer[USB_Tx_ptr],USB_Tx_length);
	    }
	  }
	};

	return USBD_OK;
}

/**
  * @brief  usbd_audio_DataOut
  *         Handles the Audio Out data stage.
  * @param  pdev: instance
  * @param  epnum: endpoint number
  * @retval status
  */


static uint8_t  usbd_audio_DataOut (void *pdev, uint8_t epnum)
{     
	  uint32_t curr_length,curr_pos,rest;

	if (epnum == AUDIO_OUT_EP)
  {
	  curr_length=USBD_GetRxCount (pdev,epnum);
	  curr_pos=(IsocOutWrPtr-IsocOutBuff);
	  rest=TOTAL_OUT_BUF_SIZE-curr_pos;
	  //monitor sample rate conversion
	  if (curr_length<AUDIO_OUT_PACKET) {STM_EVAL_LEDToggle(LED3);};
	  if (curr_length>AUDIO_OUT_PACKET) {STM_EVAL_LEDToggle(LED5);};

	  if (rest<curr_length)
	  {
	  if (rest>0)
	  {memcpy((uint8_t*)IsocOutWrPtr,tmpbuf,rest);
	  IsocOutWrPtr = IsocOutBuff;
	  curr_length-=rest;
	  };
	  if ((curr_length)>0)
	  {memcpy((uint8_t*)IsocOutWrPtr,(uint8_t *)(&tmpbuf[0]+rest),curr_length);
	  IsocOutWrPtr+=curr_length;};
	  }
	  else
	  {
	  if (curr_length>0)
	  {memcpy((uint8_t*)IsocOutWrPtr,tmpbuf,curr_length);
	  // Increment the Buffer pointer
	  IsocOutWrPtr += curr_length;};
  	  }
	  //roll it back when all buffers are full
	  if (IsocOutWrPtr >= (IsocOutBuff + (TOTAL_OUT_BUF_SIZE)))
		  IsocOutWrPtr = IsocOutBuff;
    /* Toggle the frame index */
    ((USB_OTG_CORE_HANDLE*)pdev)->dev.out_ep[epnum].even_odd_frame =
      (((USB_OTG_CORE_HANDLE*)pdev)->dev.out_ep[epnum].even_odd_frame)? 0:1;
	   DCD_EP_PrepareRx(pdev,
	                     AUDIO_OUT_EP,
	                     (uint8_t*)tmpbuf,
	                     AUDIO_OUT_PACKET+16);
    /* Trigger the start of streaming only when half buffer is full */
    if ((PlayFlag == 0) && (IsocOutWrPtr >= (IsocOutBuff + TOTAL_OUT_BUF_SIZE/2)))
    {
      /* Enable start of Streaming */
      PlayFlag = 1;
      AUDIO_OUT_fops.AudioCmd((uint8_t*)(IsocOutRdPtr),  /* Samples buffer pointer */
                          AUDIO_OUT_PACKET,          /* Number of samples in Bytes */
                          AUDIO_CMD_PLAY);           /* Command to be processed */
    }
  };
	if (epnum==CDC_OUT_DATA_EP)
	{  curr_length=USBD_GetRxCount (pdev,epnum);
	  /* USB data will be immediately processed, this allow next USB traffic being
	     NAKed till the end of the application Xfer */
	  APP_FOPS.pIf_DataRx(USB_Rx_Buffer, curr_length);

	  /* Prepare Out endpoint to receive next packet */
	  DCD_EP_PrepareRx(pdev,CDC_OUT_DATA_EP,(uint8_t*)(USB_Rx_Buffer),VIRTUAL_COM_PORT_DATA_SIZE);
	};

  return USBD_OK;
}

Обработчик начала кадра тоже чуть-чуть меняется (добавляем код по «разбору завалов» для CDC)

static uint8_t  usbd_audio_SOF (void *pdev)
{     uint8_t res;
static uint16_t n;
USB_OTG_DSTS_TypeDef  FS_DSTS;
static uint32_t FrameCount = 0;
  /* Check if there are available data in stream buffer.
    In this function, a single variable (PlayFlag) is used to avoid software delays.
    The play operation must be executed as soon as possible after the SOF detection. */
if (usbd_audio_AltSet==1)
{
	shift=0;
	gap=(IsocOutWrPtr-IsocOutRdPtr);
	tmpxx=(DMA1_Stream7->NDTR)%96;
	if (tmpxx==0) tmpxx+=96;
	if (gap<0) gap+=(TOTAL_OUT_BUF_SIZE);
	shift=-(gap+tmpxx*2-(TOTAL_OUT_BUF_SIZE/2))>>3;
	accum+=(TIM2->CCR1);
	if (shift!=0) accum+=shift;
	SOF_num++;
	if (SOF_num==(1<<SOF_RATE))
		{if (SOF_RATE>6)
			{feedback_data+=accum>>(SOF_RATE-6);}
		else
			{feedback_data+=accum<<(6-SOF_RATE);};
		feedback_data>>=1;
		SOF_num=0;
		accum=0;
		//flag=0;
	}

	if ((!flag))
		{
			FS_DSTS.d32 = USB_OTG_READ_REG32(&(((USB_OTG_CORE_HANDLE*)pdev)->regs.DREGS->DSTS));
			if (((FS_DSTS.b.soffn)&0x1) == dpid)
				{//feedback_data=722534;
					DCD_EP_Tx (pdev, AUDIO_IN_EP, (uint8_t *) &feedback_data, 3);
					flag=1;
				};
			};

  }

if (FrameCount++ == CDC_IN_FRAME_INTERVAL)
{
  /* Check the data to be sent through IN pipe */
  Handle_USBAsynchXfer(pdev);
  /* Reset the frame counter */
  FrameCount = 0;
}

return USBD_OK;
}

Обработчики незавершенных транзакций IN, OUT не трогаем. Также копируем все функции работы с VCP (virtual COM port) из usb_cdc_vcp.
ОЧЕНЬ ВАЖНО!!! Поскольку задействуются все конечные точки IN (0x81 — обратная связь UAC, 0x82 — данные CDC, 0x83 — запрос статуса CDC — реально она не используется), нужно определить TXFIFO под них!!!1111

//usb_conf.h
/****************** USB OTG FS CONFIGURATION **********************************/
#ifdef USB_OTG_FS_CORE
 #define RX_FIFO_FS_SIZE						   208
 #define TX0_FIFO_FS_SIZE                          32
 #define TX1_FIFO_FS_SIZE                          16
 #define TX2_FIFO_FS_SIZE                          16
 #define TX3_FIFO_FS_SIZE                          48
#endif
  • +10
  • 02 декабря 2015, 14:04
  • romanetz
  • 1
Файлы в топике: 4.zip

Комментарии (5)

RSS свернуть / развернуть
Вопрос чуть в сторону. Или я что-то накосячил, или ST, но если сгенерировать Кубом проект с USBCDC, то устройство нормально инициализируется на Винде 7 только один раз после резета. А с отладкой проекта с USB вообще всё печально — программный сброс гарантировано блокирует USB пока вручную не передёрнуть. Винда 7. Как вы с этим боретесь и отлаживаете?
0
USB это соединение реального времени, поэтому вся отладка — только через STMstudio. Поскольку интерфейс SWD асинхронный от исполнения программы, в нужных точках копирую рабочие переменные во временные и их уже просматриваю.
0
А как быть с нормальной работой? Возможно ли переподключение устройства без резета МК?
0
Да, все обработчики этих операций (очистку буферов и прочее) нужно прописать в usbd_usr.c
0
с отладкой все вроде понятно, это везде беда такая. а вот пердергивание решается ключиком в разрыв подтяжки д+. Вообще это крайне полезная штука, которая является признаком хорошего тона (а в селфпаверед решениях без нее совсем никак)
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.