//Twine USB Serial Communication Library //! Assuming you installed TivaWare in the default directory, a //! driver information (INF) file for use with Windows XP, Windows Vista and //! Windows7 can be found in C:/ti/TivaWare-for-C-Series/windows_drivers. //! For Windows 2000, the required INF file is in //! C:/ti/TivaWare-for-C-Series/windows_drivers/win2K. #include #include #include #include "inc/hw_ints.h" #include "inc/hw_memmap.h" #include "inc/hw_types.h" #include "inc/hw_uart.h" #include "driverlib/debug.h" #include "driverlib/gpio.h" #include "driverlib/interrupt.h" #include "driverlib/sysctl.h" #include "driverlib/systick.h" #include "driverlib/timer.h" #include "driverlib/uart.h" #include "driverlib/usb.h" #include "driverlib/rom.h" #include "driverlib/rom_map.h" #include "usblib/usblib.h" #include "usblib/usbcdc.h" #include "usblib/usb-ids.h" #include "usblib/device/usbdevice.h" #include "usblib/device/usbdcdc.h" #include #include "utils/ustdlib.h" #include "usb_serial_structs.h" #include "usb_serial_buffer.h" #include "usb_serial_adapter.h" static SerialBuffer inBuffer; static int expected_message_size; static int current_message_size; //***************************************************************************** // // This function is called whenever serial data is received from the UART. // It is passed the accumulated error flags from each character received in // this interrupt and determines from them whether or not an interrupt // notification to the host is required. // // If a notification is required and the control interrupt endpoint is idle, // we send the notification immediately. If the endpoint is not idle, we // accumulate the errors in a global variable which will be checked on // completion of the previous notification and used to send a second one // if necessary. // //***************************************************************************** void CheckForSerialStateChange(const tUSBDCDCDevice *psDevice, uint32_t ui32Errors) { uint16_t ui16SerialState; // // Clear our USB serial state. Since we are faking the handshakes, always // set the TXCARRIER (DSR) and RXCARRIER (DCD) bits. // ui16SerialState = USB_CDC_SERIAL_STATE_TXCARRIER | USB_CDC_SERIAL_STATE_RXCARRIER; // // Are any error bits set? // if(ui32Errors) { // // At least one error is being notified so translate from our hardware // error bits into the correct state markers for the USB notification. // if(ui32Errors & UART_DR_OE) { ui16SerialState |= USB_CDC_SERIAL_STATE_OVERRUN; } if(ui32Errors & UART_DR_PE) { ui16SerialState |= USB_CDC_SERIAL_STATE_PARITY; } if(ui32Errors & UART_DR_FE) { ui16SerialState |= USB_CDC_SERIAL_STATE_FRAMING; } if(ui32Errors & UART_DR_BE) { ui16SerialState |= USB_CDC_SERIAL_STATE_BREAK; } // // Call the CDC driver to notify the state change. // USBDCDCSerialStateChange((void *)psDevice, ui16SerialState); } } //***************************************************************************** // // Set the state of the RS232 RTS and DTR signals. // //***************************************************************************** void SetControlLineState(uint16_t ui16State) { // // TODO: If configured with GPIOs controlling the handshake lines, // set them appropriately depending upon the flags passed in the wValue // field of the request structure passed. // } //***************************************************************************** // // Get the communication parameters in use on the UART. // //***************************************************************************** void GetLineCoding(tLineCoding *psLineCoding) { psLineCoding->ui32Rate = 9600; psLineCoding->ui8Databits = 8; psLineCoding->ui8Parity = USB_CDC_PARITY_NONE; psLineCoding->ui8Stop = USB_CDC_STOP_BITS_1; } //***************************************************************************** // // Handles CDC driver notifications related to control and setup of the device. // // \param pvCBData is the client-supplied callback pointer for this channel. // \param ulEvent identifies the event we are being notified about. // \param ulMsgValue is an event-specific value. // \param pvMsgData is an event-specific pointer. // // This function is called by the CDC driver to perform control-related // operations on behalf of the USB host. These functions include setting // and querying the serial communication parameters, setting handshake line // states and sending break conditions. // // \return The return value is event-specific. // //***************************************************************************** uint32_t ControlHandler(void *pvCBData, uint32_t ui32Event, uint32_t ui32MsgValue, void *pvMsgData) { // // Which event are we being asked to process? // switch(ui32Event) { // // We are connected to a host and communication is now possible. // case USB_EVENT_CONNECTED: { // // Now connected and ready for normal operation. // HWREGBITW(&g_ui32Flags, FLAG_USB_CONFIGURED) = 1; // // Flush our buffers. // USBBufferFlush(&g_sTxBuffer); USBBufferFlush(&g_sRxBuffer); //TODO: Notify connection! // // Set the command status update flag. // HWREGBITW(&g_ui32Flags, FLAG_STATUS_UPDATE) = 1; break; } // // The host has disconnected. // case USB_EVENT_DISCONNECTED: { // // No longer connected. // HWREGBITW(&g_ui32Flags, FLAG_USB_CONFIGURED) = 0; //TODO: Notify disconnection! // // Set the command status update flag. // HWREGBITW(&g_ui32Flags, FLAG_STATUS_UPDATE) = 1; break; } // // Return the current serial communication parameters. // case USBD_CDC_EVENT_GET_LINE_CODING: { GetLineCoding(pvMsgData); break; } // // Set the current serial communication parameters. // case USBD_CDC_EVENT_SET_LINE_CODING: { GetLineCoding(pvMsgData); break; } // // Set the current serial communication parameters. // case USBD_CDC_EVENT_SET_CONTROL_LINE_STATE: { SetControlLineState((uint16_t)ui32MsgValue); break; } // // Send a break condition on the serial line. // case USBD_CDC_EVENT_SEND_BREAK: { break; } // // Clear the break condition on the serial line. // case USBD_CDC_EVENT_CLEAR_BREAK: { break; } // // Ignore SUSPEND and RESUME for now. // case USB_EVENT_SUSPEND: case USB_EVENT_RESUME: { break; } // // We don't expect to receive any other events. Ignore any that show // up in a release build or hang in a debug build. // default: { #ifdef DEBUG while(1); #else break; #endif } } return(0); } //***************************************************************************** // // Handles CDC driver notifications related to the transmit channel (data to // the USB host). // // \param pvCBData is the client-supplied callback pointer for this channel. // \param ui32Event identifies the event we are being notified about. // \param ui32MsgValue is an event-specific value. // \param pvMsgData is an event-specific pointer. // // This function is called by the CDC driver to notify us of any events // related to operation of the transmit data channel (the IN channel carrying // data to the USB host). // // \return The return value is event-specific. // //***************************************************************************** uint32_t TxHandler(void *pvCBData, uint32_t ui32Event, uint32_t ui32MsgValue, void *pvMsgData) { // // Which event have we been sent? // switch(ui32Event) { case USB_EVENT_TX_COMPLETE: { // // Since we are using the USBBuffer, we don't need to do anything // here. // break; } // // We don't expect to receive any other events. Ignore any that show // up in a release build or hang in a debug build. // default: { #ifdef DEBUG while(1); #else break; #endif } } return(0); } //***************************************************************************** // // Handles CDC driver notifications related to the receive channel (data from // the USB host). // // \param pvCBData is the client-supplied callback data value for this channel. // \param ui32Event identifies the event we are being notified about. // \param ui32MsgValue is an event-specific value. // \param pvMsgData is an event-specific pointer. // // This function is called by the CDC driver to notify us of any events // related to operation of the receive data channel (the OUT channel carrying // data from the USB host). // // \return The return value is event-specific. // //***************************************************************************** uint32_t RxHandler(void *pvCBData, uint32_t ui32Event, uint32_t ui32MsgValue,void *pvMsgData) { // // Which event are we being sent? // switch(ui32Event) { // // A new packet has been received. // case USB_EVENT_RX_AVAILABLE: { // // Feed some characters into the UART TX FIFO and enable the // interrupt so we are told when there is more space. // handleRx(); break; } // // We are being asked how much unprocessed data we have still to // process. We return 0 if the UART is currently idle or 1 if it is // in the process of transmitting something. The actual number of // bytes in the UART FIFO is not important here, merely whether or // not everything previously sent to us has been transmitted. // case USB_EVENT_DATA_REMAINING: { // // Get the number of bytes in the buffer and add 1 if some data // still has to clear the transmitter. return(0); } // // We are being asked to provide a buffer into which the next packet // can be read. We do not support this mode of receiving data so let // the driver know by returning 0. The CDC driver should not be sending // this message but this is included just for illustration and // completeness. // case USB_EVENT_REQUEST_BUFFER: { return(0); } // // We don't expect to receive any other events. Ignore any that show // up in a release build or hang in a debug build. // default: #ifdef DEBUG while(1); #else break; #endif } return(0); } void handleRx(void) { uint32_t ui32Read; uint8_t ui8Char; uint8_t size[4]; int size_bar = 0; if (expected_message_size == 0) { do { ui32Read = USBBufferRead((tUSBBuffer *)&g_sRxBuffer, &ui8Char, 1); if(ui32Read) { size[size_bar++] = ui8Char; } } while(size_bar < 4); expected_message_size = *(int *)size; } do { ui32Read = USBBufferRead((tUSBBuffer *)&g_sRxBuffer, &ui8Char, 1); // Did we get a character? if(ui32Read) { insertArray(&inBuffer, ui8Char); current_message_size++; } if (current_message_size == expected_message_size) { g_RxCount += current_message_size; expected_message_size = 0; current_message_size = 0; break; } } while(ui32Read); } //Execute this function on UART0 Interrupt; void InitUSB(void) { CheckForSerialStateChange(&g_sCDCDevice, 0); } //Initialize USB. void StartUSB(uint32_t ui32SysClock) { uint32_t ui32PLLRate, ui32RxCount; ui32RxCount = 0; g_RxCount = 0; initArray(&inBuffer, 1); // Save the PLL rate used by this application. SysCtlVCOGet(SYSCTL_XTAL_25MHZ, &ui32PLLRate); // Not configured initially. g_ui32Flags = 0; // Initialize the transmit and receive buffers. USBBufferInit(&g_sTxBuffer); USBBufferInit(&g_sRxBuffer); // Set the USB stack mode to Device mode with VBUS monitoring. USBStackModeSet(0, eUSBModeDevice, 0); // Tell the USB library the CPU clock and the PLL frequency. This is a // new requirement for TM4C129 devices. USBDCDFeatureSet(0, USBLIB_FEATURE_CPUCLK, &ui32SysClock); USBDCDFeatureSet(0, USBLIB_FEATURE_USBPLL, &ui32PLLRate); // Pass our device information to the USB library and place the device // on the bus. USBDCDCInit(0, (tUSBDCDCDevice *)&g_sCDCDevice); while (1) { if(ui32RxCount != g_RxCount) { ui32RxCount = g_RxCount; if (callback != NULL) { callback(inBuffer.buffer,inBuffer.used); } freeArray(&inBuffer); initArray(&inBuffer, 1); } } } //Send a sequence of chars to PC. uint32_t SendChars(char* buffer,size_t length) { uint8_t size[4]; size[3] = (length>>24) & 0xFF; size[2] = (length>>16) & 0xFF; size[1] = (length>>8) & 0xFF; size[0] = length & 0xFF; USBBufferWrite((tUSBBuffer *)&g_sTxBuffer, size, 4); return USBBufferWrite((tUSBBuffer *)&g_sTxBuffer, (uint8_t*)buffer, length); } //Register for serial data receive callback. void RegisterReceiveCallback(void (*callback_ptr)(char* buffer, size_t length)) { callback = callback_ptr; }