|
|
@@ -0,0 +1,378 @@
|
|
|
+/*------------------------------------------------------------------------------
|
|
|
+ * MDK Middleware - Component ::USB:Device:CDC
|
|
|
+ * Copyright (c) 2004-2021 Arm Limited (or its affiliates). All rights reserved.
|
|
|
+ *------------------------------------------------------------------------------
|
|
|
+ * Name: USBD_User_CDC_ACM_UART_0.c
|
|
|
+ * Purpose: USB Device Communication Device Class (CDC)
|
|
|
+ * Abstract Control Model (ACM) USB <-> UART Bridge User module
|
|
|
+ * Rev.: V1.0.8
|
|
|
+ *----------------------------------------------------------------------------*/
|
|
|
+/**
|
|
|
+ * \addtogroup usbd_cdcFunctions
|
|
|
+ *
|
|
|
+ * USBD_User_CDC_ACM_UART_0.c implements the application specific
|
|
|
+ * functionality of the CDC ACM class and is used to demonstrate a USB <-> UART
|
|
|
+ * bridge. All data received on USB is transmitted on UART and all data
|
|
|
+ * received on UART is transmitted on USB.
|
|
|
+ *
|
|
|
+ * Details of operation:
|
|
|
+ * UART -> USB:
|
|
|
+ * Initial reception on UART is started after the USB Host sets line coding
|
|
|
+ * with SetLineCoding command. Having received a full UART buffer, any
|
|
|
+ * new reception is restarted on the same buffer. Any data received on
|
|
|
+ * the UART is sent over USB using the CDC0_ACM_UART_to_USB_Thread thread.
|
|
|
+ * USB -> UART:
|
|
|
+ * While the UART transmit is not busy, data transmission on the UART is
|
|
|
+ * started in the USBD_CDC0_ACM_DataReceived callback as soon as data is
|
|
|
+ * received on the USB. Further data received on USB is transmitted on
|
|
|
+ * UART in the UART callback routine until there is no more data available.
|
|
|
+ * In this case, the next UART transmit is restarted from the
|
|
|
+ * USBD_CDC0_ACM_DataReceived callback as soon as new data is received
|
|
|
+ * on the USB.
|
|
|
+ *
|
|
|
+ * The following constants in this module affect the module functionality:
|
|
|
+ *
|
|
|
+ * - UART_PORT: specifies UART Port
|
|
|
+ * default value: 0 (=UART0)
|
|
|
+ * - UART_BUFFER_SIZE: specifies UART data Buffer Size
|
|
|
+ * default value: 512
|
|
|
+ *
|
|
|
+ * Notes:
|
|
|
+ * If the USB is slower than the UART, data can get lost. This may happen
|
|
|
+ * when USB is pausing during data reception because of the USB Host being
|
|
|
+ * too loaded with other tasks and not polling the Bulk IN Endpoint often
|
|
|
+ * enough (up to 2 seconds of gap in polling Bulk IN Endpoint may occur).
|
|
|
+ * This problem can be solved by using a large enough UART buffer to
|
|
|
+ * compensate up to a few seconds of received UART data or by using UART
|
|
|
+ * flow control.
|
|
|
+ * If the device that receives the UART data (usually a PC) is too loaded
|
|
|
+ * with other tasks it can also loose UART data. This problem can only be
|
|
|
+ * solved by using UART flow control.
|
|
|
+ *
|
|
|
+ * This file has to be adapted in case of UART flow control usage.
|
|
|
+ */
|
|
|
+
|
|
|
+
|
|
|
+//! [code_USBD_User_CDC_ACM]
|
|
|
+#include <stdio.h>
|
|
|
+#include <string.h>
|
|
|
+
|
|
|
+#include "rl_usb.h"
|
|
|
+
|
|
|
+#include "Driver_USART.h"
|
|
|
+
|
|
|
+#include "DAP_config.h"
|
|
|
+#include "DAP.h"
|
|
|
+
|
|
|
+// UART Configuration ----------------------------------------------------------
|
|
|
+
|
|
|
+#define UART_BUFFER_SIZE (512) // UART Buffer Size
|
|
|
+
|
|
|
+//------------------------------------------------------------------------------
|
|
|
+
|
|
|
+#define _UART_Driver_(n) Driver_USART##n
|
|
|
+#define UART_Driver_(n) _UART_Driver_(n)
|
|
|
+extern ARM_DRIVER_USART UART_Driver_(DAP_UART_DRIVER);
|
|
|
+#define ptrUART (&UART_Driver_(DAP_UART_DRIVER))
|
|
|
+
|
|
|
+// Local Variables
|
|
|
+static uint8_t uart_rx_buf[UART_BUFFER_SIZE];
|
|
|
+static uint8_t uart_tx_buf[UART_BUFFER_SIZE];
|
|
|
+
|
|
|
+static volatile int32_t uart_rx_cnt = 0;
|
|
|
+static volatile int32_t usb_tx_cnt = 0;
|
|
|
+
|
|
|
+static void *cdc_acm_bridge_tid = 0U;
|
|
|
+static CDC_LINE_CODING cdc_acm_line_coding = { 0U, 0U, 0U, 0U };
|
|
|
+
|
|
|
+static uint8_t cdc_acm_active = 1U;
|
|
|
+static osMutexId_t cdc_acm_mutex_id = NULL;
|
|
|
+
|
|
|
+// Acquire mutex
|
|
|
+__STATIC_INLINE void CDC_ACM_Lock (void) {
|
|
|
+ if (cdc_acm_mutex_id == NULL) {
|
|
|
+ cdc_acm_mutex_id = osMutexNew(NULL);
|
|
|
+ }
|
|
|
+ osMutexAcquire(cdc_acm_mutex_id, osWaitForever);
|
|
|
+}
|
|
|
+
|
|
|
+// Release mutex
|
|
|
+__STATIC_INLINE void CDC_ACM_Unlock (void) {
|
|
|
+ osMutexRelease(cdc_acm_mutex_id);
|
|
|
+}
|
|
|
+
|
|
|
+// Change communication settings.
|
|
|
+// \param[in] line_coding pointer to CDC_LINE_CODING structure.
|
|
|
+// \return true set line coding request processed.
|
|
|
+// \return false set line coding request not supported or not processed.
|
|
|
+static bool CDC_ACM_SetLineCoding (const CDC_LINE_CODING *line_coding) {
|
|
|
+ uint32_t data_bits = 0U, parity = 0U, stop_bits = 0U;
|
|
|
+ int32_t status;
|
|
|
+
|
|
|
+ (void)ptrUART->Control (ARM_USART_ABORT_SEND, 0U);
|
|
|
+ (void)ptrUART->Control (ARM_USART_ABORT_RECEIVE, 0U);
|
|
|
+ (void)ptrUART->Control (ARM_USART_CONTROL_TX, 0U);
|
|
|
+ (void)ptrUART->Control (ARM_USART_CONTROL_RX, 0U);
|
|
|
+
|
|
|
+ switch (line_coding->bCharFormat) {
|
|
|
+ case 0: // 1 Stop bit
|
|
|
+ stop_bits = ARM_USART_STOP_BITS_1;
|
|
|
+ break;
|
|
|
+ case 1: // 1.5 Stop bits
|
|
|
+ stop_bits = ARM_USART_STOP_BITS_1_5;
|
|
|
+ break;
|
|
|
+ case 2: // 2 Stop bits
|
|
|
+ stop_bits = ARM_USART_STOP_BITS_2;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (line_coding->bParityType) {
|
|
|
+ case 0: // None
|
|
|
+ parity = ARM_USART_PARITY_NONE;
|
|
|
+ break;
|
|
|
+ case 1: // Odd
|
|
|
+ parity = ARM_USART_PARITY_ODD;
|
|
|
+ break;
|
|
|
+ case 2: // Even
|
|
|
+ parity = ARM_USART_PARITY_EVEN;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (line_coding->bDataBits) {
|
|
|
+ case 5:
|
|
|
+ data_bits = ARM_USART_DATA_BITS_5;
|
|
|
+ break;
|
|
|
+ case 6:
|
|
|
+ data_bits = ARM_USART_DATA_BITS_6;
|
|
|
+ break;
|
|
|
+ case 7:
|
|
|
+ data_bits = ARM_USART_DATA_BITS_7;
|
|
|
+ break;
|
|
|
+ case 8:
|
|
|
+ data_bits = ARM_USART_DATA_BITS_8;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ status = ptrUART->Control(ARM_USART_MODE_ASYNCHRONOUS |
|
|
|
+ data_bits |
|
|
|
+ parity |
|
|
|
+ stop_bits ,
|
|
|
+ line_coding->dwDTERate );
|
|
|
+
|
|
|
+ if (status != ARM_DRIVER_OK) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Store requested settings to local variable
|
|
|
+ memcpy(&cdc_acm_line_coding, line_coding, sizeof(cdc_acm_line_coding));
|
|
|
+
|
|
|
+ uart_rx_cnt = 0;
|
|
|
+ usb_tx_cnt = 0;
|
|
|
+
|
|
|
+ (void)ptrUART->Control (ARM_USART_CONTROL_TX, 1U);
|
|
|
+ (void)ptrUART->Control (ARM_USART_CONTROL_RX, 1U);
|
|
|
+
|
|
|
+ (void)ptrUART->Receive (uart_rx_buf, UART_BUFFER_SIZE);
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+// Activate or Deactivate USBD COM PORT
|
|
|
+// \param[in] cmd 0=deactivate, 1=activate
|
|
|
+void USB_COM_PORT_Activate (uint32_t cmd) {
|
|
|
+ switch (cmd) {
|
|
|
+ case 0:
|
|
|
+ cdc_acm_active = 0U;
|
|
|
+ USBD_CDC0_ACM_Uninitialize();
|
|
|
+ break;
|
|
|
+ case 1:
|
|
|
+ USBD_CDC0_ACM_Initialize();
|
|
|
+ CDC_ACM_Lock();
|
|
|
+ CDC_ACM_SetLineCoding(&cdc_acm_line_coding);
|
|
|
+ cdc_acm_active = 1U;
|
|
|
+ CDC_ACM_Unlock();
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// Called when UART has transmitted or received requested number of bytes.
|
|
|
+// \param[in] event UART event
|
|
|
+// - ARM_USART_EVENT_SEND_COMPLETE: all requested data was sent
|
|
|
+// - ARM_USART_EVENT_RECEIVE_COMPLETE: all requested data was received
|
|
|
+static void UART_Callback (uint32_t event) {
|
|
|
+ int32_t cnt;
|
|
|
+
|
|
|
+ if (cdc_acm_active == 0U) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (event & ARM_USART_EVENT_SEND_COMPLETE) {
|
|
|
+ // USB -> UART
|
|
|
+ cnt = USBD_CDC_ACM_ReadData(0U, uart_tx_buf, UART_BUFFER_SIZE);
|
|
|
+ if (cnt > 0) {
|
|
|
+ (void)ptrUART->Send(uart_tx_buf, (uint32_t)(cnt));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (event & ARM_USART_EVENT_RECEIVE_COMPLETE) {
|
|
|
+ // UART data received, restart new reception
|
|
|
+ uart_rx_cnt += UART_BUFFER_SIZE;
|
|
|
+ (void)ptrUART->Receive(uart_rx_buf, UART_BUFFER_SIZE);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// Thread: Sends data received on UART to USB
|
|
|
+// \param[in] arg not used.
|
|
|
+__NO_RETURN static void CDC0_ACM_UART_to_USB_Thread (void *arg) {
|
|
|
+ int32_t cnt, cnt_to_wrap;
|
|
|
+
|
|
|
+ (void)(arg);
|
|
|
+
|
|
|
+ for (;;) {
|
|
|
+ // UART - > USB
|
|
|
+ if (ptrUART->GetStatus().rx_busy != 0U) {
|
|
|
+ cnt = uart_rx_cnt;
|
|
|
+ cnt += (int32_t)ptrUART->GetRxCount();
|
|
|
+ cnt -= usb_tx_cnt;
|
|
|
+ if (cnt >= UART_BUFFER_SIZE) {
|
|
|
+ // Dump data received on UART if USB is not consuming fast enough
|
|
|
+ usb_tx_cnt += cnt;
|
|
|
+ cnt = 0U;
|
|
|
+ }
|
|
|
+ if (cnt > 0) {
|
|
|
+ cnt_to_wrap = (int32_t)(UART_BUFFER_SIZE - ((uint32_t)usb_tx_cnt & (UART_BUFFER_SIZE - 1)));
|
|
|
+ if (cnt > cnt_to_wrap) {
|
|
|
+ cnt = cnt_to_wrap;
|
|
|
+ }
|
|
|
+ cnt = USBD_CDC_ACM_WriteData(0U, (uart_rx_buf + ((uint32_t)usb_tx_cnt & (UART_BUFFER_SIZE - 1))), cnt);
|
|
|
+ if (cnt > 0) {
|
|
|
+ usb_tx_cnt += cnt;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ (void)osDelay(10U);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static osRtxThread_t cdc0_acm_uart_to_usb_thread_cb_mem __SECTION(.bss.os.thread.cb);
|
|
|
+static uint64_t cdc0_acm_uart_to_usb_thread_stack_mem[512U / 8U] __SECTION(.bss.os.thread.cdc.stack);
|
|
|
+static const osThreadAttr_t cdc0_acm_uart_to_usb_thread_attr = {
|
|
|
+ "CDC0_ACM_UART_to_USB_Thread",
|
|
|
+ 0U,
|
|
|
+ &cdc0_acm_uart_to_usb_thread_cb_mem,
|
|
|
+ sizeof(osRtxThread_t),
|
|
|
+ &cdc0_acm_uart_to_usb_thread_stack_mem[0],
|
|
|
+ sizeof(cdc0_acm_uart_to_usb_thread_stack_mem),
|
|
|
+ osPriorityNormal,
|
|
|
+ 0U,
|
|
|
+ 0U
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+// CDC ACM Callbacks -----------------------------------------------------------
|
|
|
+
|
|
|
+// Called when new data was received from the USB Host.
|
|
|
+// \param[in] len number of bytes available to read.
|
|
|
+void USBD_CDC0_ACM_DataReceived (uint32_t len) {
|
|
|
+ int32_t cnt;
|
|
|
+
|
|
|
+ (void)(len);
|
|
|
+
|
|
|
+ if (cdc_acm_active == 0U) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ptrUART->GetStatus().tx_busy == 0U) {
|
|
|
+ // Start USB -> UART
|
|
|
+ cnt = USBD_CDC_ACM_ReadData(0U, uart_tx_buf, UART_BUFFER_SIZE);
|
|
|
+ if (cnt > 0) {
|
|
|
+ (void)ptrUART->Send(uart_tx_buf, (uint32_t)(cnt));
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// Called during USBD_Initialize to initialize the USB CDC class instance (ACM).
|
|
|
+void USBD_CDC0_ACM_Initialize (void) {
|
|
|
+ (void)ptrUART->Initialize (UART_Callback);
|
|
|
+ (void)ptrUART->PowerControl (ARM_POWER_FULL);
|
|
|
+
|
|
|
+ cdc_acm_bridge_tid = osThreadNew (CDC0_ACM_UART_to_USB_Thread, NULL, &cdc0_acm_uart_to_usb_thread_attr);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+// Called during USBD_Uninitialize to de-initialize the USB CDC class instance (ACM).
|
|
|
+void USBD_CDC0_ACM_Uninitialize (void) {
|
|
|
+ if (osThreadTerminate (cdc_acm_bridge_tid) == osOK) {
|
|
|
+ cdc_acm_bridge_tid = NULL;
|
|
|
+ }
|
|
|
+ (void)ptrUART->Control (ARM_USART_ABORT_RECEIVE, 0U);
|
|
|
+ (void)ptrUART->PowerControl (ARM_POWER_OFF);
|
|
|
+ (void)ptrUART->Uninitialize ();
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+// Called upon USB Bus Reset Event.
|
|
|
+void USBD_CDC0_ACM_Reset (void) {
|
|
|
+ if (cdc_acm_active == 0U ) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ (void)ptrUART->Control (ARM_USART_ABORT_SEND, 0U);
|
|
|
+ (void)ptrUART->Control (ARM_USART_ABORT_RECEIVE, 0U);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+// Called upon USB Host request to change communication settings.
|
|
|
+// \param[in] line_coding pointer to CDC_LINE_CODING structure.
|
|
|
+// \return true set line coding request processed.
|
|
|
+// \return false set line coding request not supported or not processed.
|
|
|
+bool USBD_CDC0_ACM_SetLineCoding (const CDC_LINE_CODING *line_coding) {
|
|
|
+ bool ret = false;
|
|
|
+
|
|
|
+ CDC_ACM_Lock();
|
|
|
+ if (cdc_acm_active == 0U) {
|
|
|
+ // Store requested settings to local variable
|
|
|
+ memcpy(&cdc_acm_line_coding, line_coding, sizeof(cdc_acm_line_coding));
|
|
|
+ ret = true;
|
|
|
+ } else {
|
|
|
+ ret = CDC_ACM_SetLineCoding(line_coding);
|
|
|
+ }
|
|
|
+ CDC_ACM_Unlock();
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+// Called upon USB Host request to retrieve communication settings.
|
|
|
+// \param[out] line_coding pointer to CDC_LINE_CODING structure.
|
|
|
+// \return true get line coding request processed.
|
|
|
+// \return false get line coding request not supported or not processed.
|
|
|
+bool USBD_CDC0_ACM_GetLineCoding (CDC_LINE_CODING *line_coding) {
|
|
|
+
|
|
|
+ // Load settings from ones stored on USBD_CDC0_ACM_SetLineCoding callback
|
|
|
+ *line_coding = cdc_acm_line_coding;
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+// Called upon USB Host request to set control line states.
|
|
|
+// \param [in] state control line settings bitmap.
|
|
|
+// - bit 0: DTR state
|
|
|
+// - bit 1: RTS state
|
|
|
+// \return true set control line state request processed.
|
|
|
+// \return false set control line state request not supported or not processed.
|
|
|
+bool USBD_CDC0_ACM_SetControlLineState (uint16_t state) {
|
|
|
+ // Add code for set control line state
|
|
|
+
|
|
|
+ (void)(state);
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+//! [code_USBD_User_CDC_ACM]
|