Старт ARM. Поднимаем USB CDC.

Всем привет! Сегодня будем поднимать USB CDC (VCP) на плате stm32f401c-disco.

USB communications device class (коммуникационный класс устройства) — является составным классом устройства Универсальной последовательной шины. Класс может включать один (или более) интерфейс, такой как интерфейс пользовательского элемента управления, интерфейс передачи данных, аудио или интерфейс запоминающего устройства.

VCP – Виртуальный COM-порт

Базовый класс 02h (Communications and CDC Control)
Этот базовый класс определен для устройств, которые относятся к классу устройств спецификации связи. Эта спецификация определяет используемый набор подкласса и Протокола значений. Значения за пределами определения спецификации защищены. Обратите внимание, что связи класса устройств спецификации требуется несколько значений Кода класса (три), которые будут использоваться в описания устройств, а некоторые, которые будут использоваться в интерфейсе дескрипторов.

Базовый класс 02h
Подкласс xxh
протокол xxh
смысл Communication device class

Базовый класс 0Ah (CDC-Data)
Этот базовый класс определен для устройств, которые относятся к классу устройств спецификации связи. Это спецификация определяет используемый набор подкласса и протокола значений. Значения за пределами определения спецификации защищены. Эти коды класса могут быть использованы только в интерфейсе дескрипторов.

Базовый класс 0Ah
Подкласс xxh
протокол xxh
смысл CDC data device

Дескриптор CDC

/* USB CDC device Configuration Descriptor */
__ALIGN_BEGIN uint8_t USBD_CDC_CfgHSDesc[USB_CDC_CONFIG_DESC_SIZ] __ALIGN_END =
{
  /*Configuration Descriptor*/
  0x09,   /* bLength: Configuration Descriptor size */
  USB_DESC_TYPE_CONFIGURATION,      /* bDescriptorType: Configuration */
  USB_CDC_CONFIG_DESC_SIZ,                /* wTotalLength:no of returned bytes */
  0x00,
  0x02,   /* bNumInterfaces: 2 interface */
  0x01,   /* bConfigurationValue: Configuration value */
  0x00,   /* iConfiguration: Index of string descriptor describing the configuration */
  0xC0,   /* bmAttributes: self powered */
  0x32,   /* MaxPower 0 mA */
  
  /*---------------------------------------------------------------------------*/
  
  /*Interface Descriptor */
  0x09,   /* bLength: Interface Descriptor size */
  USB_DESC_TYPE_INTERFACE,  /* bDescriptorType: Interface */
  /* Interface descriptor type */
  0x00,   /* 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 */
  0x24,   /* bDescriptorType: CS_INTERFACE */
  0x00,   /* bDescriptorSubtype: Header Func Desc */
  0x10,   /* bcdCDC: spec release number */
  0x01,
  
  /*Call Management Functional Descriptor*/
  0x05,   /* bFunctionLength */
  0x24,   /* bDescriptorType: CS_INTERFACE */
  0x01,   /* bDescriptorSubtype: Call Management Func Desc */
  0x00,   /* bmCapabilities: D0+D1 */
  0x01,   /* bDataInterface: 1 */
  
  /*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 */
  0x00,   /* bMasterInterface: Communication class interface */
  0x01,   /* bSlaveInterface0: Data Class Interface */
  
  /*Endpoint 2 Descriptor*/
  0x07,                           /* bLength: Endpoint Descriptor size */
  USB_DESC_TYPE_ENDPOINT,   /* bDescriptorType: Endpoint */
  CDC_CMD_EP,                     /* bEndpointAddress */
  0x03,                           /* bmAttributes: Interrupt */
  LOBYTE(CDC_CMD_PACKET_SIZE),     /* wMaxPacketSize: */
  HIBYTE(CDC_CMD_PACKET_SIZE),
  0x10,                           /* bInterval: */ 
  /*---------------------------------------------------------------------------*/
  
  /*Data class interface descriptor*/
  0x09,   /* bLength: Endpoint Descriptor size */
  USB_DESC_TYPE_INTERFACE,  /* bDescriptorType: */
  0x01,   /* bInterfaceNumber: Number of Interface */
  0x00,   /* bAlternateSetting: Alternate setting */
  0x02,   /* bNumEndpoints: Two endpoints used */
  0x0A,   /* bInterfaceClass: CDC */
  0x00,   /* bInterfaceSubClass: */
  0x00,   /* bInterfaceProtocol: */
  0x00,   /* iInterface: */
  
  /*Endpoint OUT Descriptor*/
  0x07,   /* bLength: Endpoint Descriptor size */
  USB_DESC_TYPE_ENDPOINT,      /* bDescriptorType: Endpoint */
  CDC_OUT_EP,                        /* bEndpointAddress */
  0x02,                              /* bmAttributes: Bulk */
  LOBYTE(CDC_DATA_HS_MAX_PACKET_SIZE),  /* wMaxPacketSize: */
  HIBYTE(CDC_DATA_HS_MAX_PACKET_SIZE),
  0x00,                              /* bInterval: ignore for Bulk transfer */
  
  /*Endpoint IN Descriptor*/
  0x07,   /* bLength: Endpoint Descriptor size */
  USB_DESC_TYPE_ENDPOINT,      /* bDescriptorType: Endpoint */
  CDC_IN_EP,                         /* bEndpointAddress */
  0x02,                              /* bmAttributes: Bulk */
  LOBYTE(CDC_DATA_HS_MAX_PACKET_SIZE),  /* wMaxPacketSize: */
  HIBYTE(CDC_DATA_HS_MAX_PACKET_SIZE),
  0x00                               /* bInterval: ignore for Bulk transfer */
} ;

1. Находим строку HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0); и меняем на HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5);
2. Находим строку #define CDC_DATA_HS_MAX_PACKET_SIZE 512 /* Endpoint IN & OUT Packet size */ и меняем на #define CDC_DATA_HS_MAX_PACKET_SIZE 64 /* Endpoint IN & OUT Packet size */
3. Производим изменения в usbd_cdc_if.c

static struct
{
    uint8_t Buffer[CDC_DATA_HS_OUT_PACKET_SIZE];
    int Position, Size;
    char ReadDone;
} RxBuffer;

char VCPInitialized;

...

static int8_t CDC_Init_FS(void)
{
	USBD_CDC_SetRxBuffer(&hUsbDeviceFS, RxBuffer.Buffer);
	VCPInitialized = 1;
  return (USBD_OK);
}

...

static int8_t CDC_Receive_FS (uint8_t* Buf, uint32_t *Len)
{
	RxBuffer.Position = 0;
  RxBuffer.Size = *Len;
  RxBuffer.ReadDone = 1;
  return (USBD_OK);
}

...

int VCP_read(void *pBuffer, int size)
{
    if (!RxBuffer.ReadDone)
        return 0;

    int remaining = RxBuffer.Size - RxBuffer.Position;
    int todo = MIN(remaining, size);
    if (todo <= 0)
        return 0;

    memcpy(pBuffer, RxBuffer.Buffer + RxBuffer.Position, todo);
    RxBuffer.Position += todo;
    if (RxBuffer.Position >= RxBuffer.Size)
    {
        RxBuffer.ReadDone = 0;
        USBD_CDC_ReceivePacket(&hUsbDeviceFS);
    }

    return todo;
}

int VCP_write(const void *pBuffer, int size)
{
    if (size > CDC_DATA_HS_OUT_PACKET_SIZE)
    {
        int offset;
        for (offset = 0; offset < size; offset++)
        {
            int todo = MIN(CDC_DATA_HS_OUT_PACKET_SIZE,
                           size - offset);
            int done = VCP_write(((char *)pBuffer) + offset, todo);
            if (done != todo)
                return offset + done;
        }

        return size;
    }

    USBD_CDC_HandleTypeDef *pCDC =
            (USBD_CDC_HandleTypeDef *)hUsbDeviceFS.pClassData;
    while(pCDC->TxState) { } //Wait for previous transfer

    USBD_CDC_SetTxBuffer(&hUsbDeviceFS, (uint8_t *)pBuffer, size);
    if (USBD_CDC_TransmitPacket(&hUsbDeviceFS) != USBD_OK)
        return 0;

    while(pCDC->TxState) { } //Wait until transfer is done
    return size;
}

4. Теперь нужно добавить в usbd_cdc_if.h несколько строк кода

int VCP_read(void *pBuffer, int size);
int VCP_write(const void *pBuffer, int size);
extern char VCPInitialized;

5. Устраняем баг для USBD_CDC_TransmitPacket()

if(hcdc->TxState == 0)
{
    hcdc->TxState = 1;

    USBD_LL_Transmit(pdev, CDC_IN_EP, hcdc->TxBuffer,
                     hcdc->TxLength);
    return USBD_OK;
}

6. И добавляем пример в main.c

#include "stm32f4xx_hal.h"
#include "usb_device.h"
#include "usbd_core.h"
#include "usbd_desc.h"
#include "usbd_cdc_if.h"
/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);

int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration----------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* Configure the system clock */
  SystemClock_Config();

  /* System interrupt init*/
  /* Sets the priority grouping field */
  HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_0);
  HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USB_DEVICE_Init();

  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */
	char byte;
  /* USER CODE BEGIN 3 */
  /* Infinite loop */
	while(!VCPInitialized) {}
	
  while (1)
  {
		if (VCP_read(&byte, 1) != 1)
			continue;
    VCP_write("\r\nYou typed ", 12);
    VCP_write(&byte, 1);
    VCP_write("\r\n", 2);

  }
  /* USER CODE END 3 */

}
...

Проект для CDC

И все готова!, подключаем микро USB к плате, второй вывод к host-у и у нас появляется виртуальный com порт.

By!

6 thoughts on “Старт ARM. Поднимаем USB CDC.

  1. When i debbuged this code i found that for really big messages this doesn’t work right. There was some issue with size off offset. IT send multiple time the same think.

    I corrected it with:

    if (size > CDC_DATA_HS_OUT_PACKET_SIZE) {
    int offset;
    for (offset = 0; offset < size; offset += CDC_DATA_HS_OUT_PACKET_SIZE) {
    int todo = MIN(CDC_DATA_HS_OUT_PACKET_SIZE, size - offset);
    int done = VCP_write(((char *) pBuffer) + offset, todo);
    if (done != todo)
    return offset + done;
    }

    return size;
    }

  2. Ну и зачем столько путанного кода. Всё это легко организуется средствами генерируемого STM32Cube проекта. Достаточно добавить несколько строчек в функцию ( CDC_Receive_FS (uint8_t* UserRxBufferFS, uint32_t *Len)) . И это доходчиво описано здесь: http://habrahabr.ru/post/249395/
    Создается впечатление, что вы пытаетесь запутать, выложив обилие непонятной информации и кода.

  3. Есть одна проблема. Не передаются пакеты более 64 байт. Кто-нибудь решил ее?

Comments are closed.