/* ****************************************************************************** * @file usb_cdc.c * @author FreqChip Firmware Team * @version V1.0.0 * @date 2021 * @brief This file provides the high layer firmware functions to manage the * USB CDC Class.(Communication Device Class) ****************************************************************************** * @attention * * Copyright (c) 2021 FreqChip. * All rights reserved. * ****************************************************************************** * How to use, for example: * * int main(void) * { * NVIC_ClearPendingIRQ(USBMCU_IRQn); * NVIC_SetPriority(USBMCU_IRQn, 0); * NVIC_EnableIRQ(USBMCU_IRQn); * * usb_device_init(); * usb_cdc_init(); * * // Wait for other initialization of the MCU * while(1) * { * usb_cdc_serialReceive(); * } * } ****************************************************************************** */ #include "usb_cdc.h" extern UART_HandleTypeDef Uart_CDC_handle; #define CDC_MAX_PACK (64) /* Fixed 64 bytes */ #define CDC_MAX_IN_PACK (1024) /* IN Endpoint max pack buffer */ volatile bool COM_Activate = false; volatile uint32_t Tx_Count = 0; volatile uint32_t Tx_Total = 0; uint8_t RxBuffer[CDC_MAX_PACK]; uint8_t TxBuffer[CDC_MAX_IN_PACK]; USBD_CDC_LineCodingTypeDef LineCoding; /* USB Standard Device Descriptor */ const uint8_t USB_CDC_DeviceDesc[] = { 0x12, /* bLength */ 0x01, /* bDescriptorType */ 0x00, /* bcdUSB */ 0x02, 0x02, /* bDeviceClass */ 0x02, /* bDeviceSubClass */ 0x00, /* bDeviceProtocol */ 0x40, /* bMaxPacketSize */ 0xAA, /* idVendor */ 0xFF, /* idVendor */ 0x55, /* idProduct */ 0x55, /* idProduct */ 0x00, /* bcdDevice rel. 2.00 */ 0x20, 0x01, /* Index of manufacturer string */ 0x02, /* Index of product string */ 0x03, /* Index of serial number string */ 0x01, /* bNumConfigurations */ }; /* USB Standard Configuration Descriptor */ const uint8_t USB_CDC_ConfigurationDesc[] = { /* Configuration Descriptor */ 0x09, /* bLength */ 0x02, /* bDescriptorType */ 0x43, /* wTotalLength */ 0x00, 0x02, /* bNumInterfaces */ 0x01, /* bConfigurationValue */ 0x00, /* iConfiguration */ 0xC0, /* bmAttributes */ 0x32, /* bMaxPower */ /* CDC Interface Descriptor */ 0x09, /* bLength */ 0x04, /* bDescriptorType */ 0x00, /* bInterfaceNumber */ 0x00, /* bAlternateSetting */ 0x01, /* bNumEndpoints */ 0x02, /* bInterfaceClass */ 0x02, /* bInterfaceSubClass */ 0x01, /* bInterfaceProtocol */ 0x00, /* iConfiguration */ /* 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 */ 0x05, /* bDescriptorType */ 0x82, /* bEndpointAddress */ 0x03, /* bmAttributes: Interrupt */ 0x08, /* wMaxPacketSize: */ 0x00, 0x10, /* bInterval */ /* Data class interface descriptor */ 0x09, /* bLength */ 0x04, /* bDescriptorType */ 0x01, /* bInterfaceNumber */ 0x00, /* bAlternateSetting */ 0x02, /* bNumEndpoints */ 0x0A, /* bInterfaceClass */ 0x00, /* bInterfaceSubClass */ 0x00, /* bInterfaceProtocol */ 0x00, /* iConfiguration */ /* Endpoint OUT Descriptor */ 0x07, /* bLength */ 0x05, /* bDescriptorType */ 0x01, /* bEndpointAddress */ 0x02, /* bmAttributes: Bulk */ 0x40, /* wMaxPacketSize */ 0x00, 0x00, /* bInterval */ /* Endpoint IN Descriptor */ 0x07, /* bLength */ 0x05, /* bDescriptorType */ 0x81, /* bEndpointAddress */ 0x02, /* bmAttributes: Bulk */ 0x40, /* wMaxPacketSize */ 0x00, 0x00, /* bInterval */ }; /* USB Standard Manufacture Descriptor */ const uint8_t USB_CDC_ManufactureDesc[] = { 0x12, /* bLength */ 0x03, /* bDescriptorType */ 'F', 0x00, /* BString */ 'R', 0x00, 'E', 0x00, 'Q', 0x00, 'C', 0x00, 'H', 0x00, 'I', 0x00, 'P', 0x00, }; /* USB Standard Configuration Descriptor */ const uint8_t USB_CDC_ProductDesc[] = { 0x1A, /* bLength */ 0x03, /* bDescriptorType */ 'F', 0x00, /* BString */ 'R', 0x00, 'E', 0x00, 'Q', 0x00, 'C', 0x00, 'H', 0x00, 'I', 0x00, 'P', 0x00, '-', 0x00, 'C', 0x00, 'O', 0x00, 'M', 0x00, }; /* USB Standard Configuration Descriptor */ const uint8_t USB_CDC_SerialNumberDesc[] = { 0x1E, /* bLength */ 0x03, /* bDescriptorType */ '2', 0x00, /* BString */ '0', 0x00, '2', 0x00, '1', 0x00, '-', 0x00, '1', 0x00, '0', 0x00, '0', 0x00, '1', 0x00, '-', 0x00, 'A', 0x00, '1', 0x00, '3', 0x00, '5', 0x00, }; /* USB Standard Configuration Descriptor */ const uint8_t USB_CDC_LanuageIDDesc[] = { 0x04, /* bLength */ 0x03, /* bDescriptorType */ 0x09, /* BString */ 0x04, }; /********************************************************************* * @fn usb_cdc_serialReceive * * @brief serial port receives data * * @param None. * @return None. */ void usb_cdc_serialReceive(void) { uint32_t SendLength; while (!__UART_IS_RxFIFO_EMPTY(Uart_CDC_handle.UARTx)) { TxBuffer[Tx_Total++] = __UART_READ_FIFO(Uart_CDC_handle.UARTx); if (Tx_Total >= CDC_MAX_IN_PACK) { Tx_Total = 0; } } /* have Data to be sent */ SendLength = Tx_Total >= Tx_Count ? Tx_Total - Tx_Count : CDC_MAX_IN_PACK - Tx_Count; if (SendLength) { usb_selecet_endpoint(ENDPOINT_1); if (usb_Endpoints_GET_TxPktRdy() == false) { if (SendLength > CDC_MAX_PACK) { usb_write_fifo(ENDPOINT_1, &TxBuffer[Tx_Count], CDC_MAX_PACK); Tx_Count += CDC_MAX_PACK; } else { usb_write_fifo(ENDPOINT_1, &TxBuffer[Tx_Count], SendLength); Tx_Count += SendLength; } if (Tx_Count >= CDC_MAX_IN_PACK) { Tx_Count = 0; } usb_Endpoints_SET_TxPktRdy(); } } } /********************************************************************* * @fn usb_cdc_serialSend * * @brief Sending data over a serial port * * @param None. * @return None. */ static void usb_cdc_serialSend(uint8_t *Buffer, uint8_t Size) { while (Size--) { /* Tx empty */ while(__UART_IS_TxFIFO_FULL(Uart_CDC_handle.UARTx)); /* Send data */ __UART_WRITE_FIFO(Uart_CDC_handle.UARTx, *Buffer++); } } /* CDC Line coding */ uint8_t CDC_LineCoding[7]; /********************************************************************* * @fn usb_cdc_SetLineCoding * * @brief receive Host data and Set Line Coding * * @param None. * @return None. */ static void usb_cdc_SetLineCoding(void) { USBD_CDC_LineCodingTypeDef Temp_LineCoding; uint8_t lu8_RxCount; uint8_t lu8_UartLine; lu8_RxCount = usb_Endpoint0_get_RxCount(); usb_read_fifo(ENDPOINT_0, CDC_LineCoding, lu8_RxCount); usb_Endpoint0_FlushFIFO(); Endpoint_0_DataOut_Handler = NULL; Temp_LineCoding.dwDTERate = CDC_LineCoding[0]; Temp_LineCoding.dwDTERate |= CDC_LineCoding[1] << 8; Temp_LineCoding.dwDTERate |= CDC_LineCoding[2] << 16; Temp_LineCoding.dwDTERate |= CDC_LineCoding[3] << 24; Temp_LineCoding.bCharFormat = CDC_LineCoding[4]; Temp_LineCoding.bParityType = CDC_LineCoding[5]; Temp_LineCoding.bDataBits = CDC_LineCoding[6]; /* Modify the uart configuration */ if (LineCoding.dwDTERate != Temp_LineCoding.dwDTERate) { LineCoding.dwDTERate = Temp_LineCoding.dwDTERate; Uart_CDC_handle.Init.BaudRate = LineCoding.dwDTERate; uart_config_baudRate(&Uart_CDC_handle); } if (LineCoding.bCharFormat != Temp_LineCoding.bCharFormat || LineCoding.bParityType != Temp_LineCoding.bParityType || LineCoding.bDataBits != Temp_LineCoding.bDataBits) { LineCoding.bCharFormat = Temp_LineCoding.bCharFormat; LineCoding.bParityType = Temp_LineCoding.bParityType; LineCoding.bDataBits = Temp_LineCoding.bDataBits; switch (LineCoding.bDataBits) { case 8: lu8_UartLine = 0x03; break; case 7: lu8_UartLine = 0x02; break; case 6: lu8_UartLine = 0x01; break; case 5: lu8_UartLine = 0x00; break; default: break; } switch (LineCoding.bCharFormat) { case 0: lu8_UartLine |= 0x03; break; case 1: case 2: lu8_UartLine |= 1 << 2; break; default: break; } switch (LineCoding.bParityType) { case 1: lu8_UartLine |= (1 << 3); break; case 2: lu8_UartLine |= (1 << 3) | (1 << 4); break; default: break; } __UART_SET_LINE_CTRL(Uart_CDC_handle.UARTx, lu8_UartLine); } } /********************************************************************* * @fn usb_cdc_ClassRequest_Handler * * @brief CDC Class Request Handler * * @param None. * @return None. */ static void usb_cdc_ClassRequest_Handler(usb_StandardRequest_t* pStandardRequest, usb_ReturnData_t* pReturnData) { switch (pStandardRequest->bRequest) { case CDC_SET_LINE_CODING: { /* Host data out */ Endpoint_0_DataOut_Handler = usb_cdc_SetLineCoding; }break; case CDC_GET_LINE_CODING: { CDC_LineCoding[0] = (LineCoding.dwDTERate >> 0) & 0xFF; CDC_LineCoding[1] = (LineCoding.dwDTERate >> 8) & 0xFF; CDC_LineCoding[2] = (LineCoding.dwDTERate >> 16) & 0xFF; CDC_LineCoding[3] = (LineCoding.dwDTERate >> 24) & 0xFF; CDC_LineCoding[4] = LineCoding.bCharFormat; CDC_LineCoding[5] = LineCoding.bParityType; CDC_LineCoding[6] = LineCoding.bDataBits; pReturnData->DataBuffer = CDC_LineCoding; pReturnData->DataLength = sizeof(CDC_LineCoding); }break; case CDC_SET_CONTROL_LINE_STATE: { /* COM acitvate or inacitvate */ if (pStandardRequest->wValue[0]) { COM_Activate = true; } else { COM_Activate = false; } /* clear count */ Tx_Count = 0; Tx_Total = 0; /* Flush FIFO */ usb_selecet_endpoint(ENDPOINT_1); usb_Endpoints_FlushRxFIFO(); usb_Endpoints_FlushTxFIFO(); usb_selecet_endpoint(ENDPOINT_0); }break; default: break; } } /********************************************************************* * @fn usb_cdc_OtherEndpoints_Handler * * @brief CDC other Endpoints handler * * @param None. * @return None. */ static void usb_cdc_OtherEndpoints_Handler(uint8_t RxStatus, uint8_t TxStatus) { uint8_t lu8_RxCount; /* DATA OUT */ if (RxStatus & ENDPOINT_1_MASK) { usb_selecet_endpoint(ENDPOINT_1); lu8_RxCount = usb_Endpoints_get_RxCount(); usb_read_fifo(ENDPOINT_1, RxBuffer, lu8_RxCount); usb_cdc_serialSend(RxBuffer, lu8_RxCount); usb_Endpoints_FlushRxFIFO(); } } /********************************************************************* * @fn usb_cdc_init * * @brief CDC device parameter initialization * * @param None. * @return None. */ void usb_cdc_init(void) { /* Initialize the relevant pointer */ usbdev_get_dev_desc((uint8_t *)USB_CDC_DeviceDesc); usbdev_get_config_desc((uint8_t *)USB_CDC_ConfigurationDesc); usbdev_get_string_Manufacture((uint8_t *)USB_CDC_ManufactureDesc); usbdev_get_string_Product((uint8_t *)USB_CDC_ProductDesc); usbdev_get_string_SerialNumber((uint8_t *)USB_CDC_SerialNumberDesc); usbdev_get_string_LanuageID((uint8_t *)USB_CDC_LanuageIDDesc); Endpoint_0_ClassRequest_Handler = usb_cdc_ClassRequest_Handler; Endpoints_Handler = usb_cdc_OtherEndpoints_Handler; USB_Reset_Handler = usb_cdc_init; /* config data endpoint fifo */ usb_selecet_endpoint(ENDPOINT_1); usb_endpoint_Txfifo_config(0x08, 3); usb_endpoint_Rxfifo_config(0x10, 3); usb_TxMaxP_set(8); usb_RxMaxP_set(8); usb_selecet_endpoint(ENDPOINT_2); usb_endpoint_Txfifo_config(0x28, 1); usb_RxMaxP_set(1); /* Endpoint_1 Rx interrupt enable */ usb_RxInt_Enable(ENDPOINT_1); LineCoding.dwDTERate = 115200; LineCoding.bCharFormat = 0x00; LineCoding.bParityType = 0x00; LineCoding.bDataBits = 0x08; }