From d9546a3120ead8e81beecedfbdf934beddb36a86 Mon Sep 17 00:00:00 2001 From: klaute Date: Tue, 30 Aug 2016 16:48:55 +0200 Subject: [PATCH] A lot of untested modifications. --- firmware/main.c | 664 ++++++++++++++++++++++---------------- tools/_serial.py | 36 +++ tools/logClusterBootup.sh | 23 ++ tools/muxctrl.py | 314 +++++++++++------- 4 files changed, 652 insertions(+), 385 deletions(-) create mode 100755 tools/_serial.py create mode 100755 tools/logClusterBootup.sh diff --git a/firmware/main.c b/firmware/main.c index b4b113b..9eff66d 100755 --- a/firmware/main.c +++ b/firmware/main.c @@ -1,13 +1,21 @@ +/** + * description: Modified LUFA example to get two virtual serial USB devices. + * author: Kai Lauterbach + * date: 08/2016 + * version: v0.1 + * license: GPLv3 + */ + /* LUFA Library - Copyright (C) Dean Camera, 2015. + Copyright (C) Dean Camera, 2016. dean [at] fourwalledcubicle [dot] com www.lufa-lib.org */ /* - Copyright 2015 Dean Camera (dean [at] fourwalledcubicle [dot] com) + Copyright 2016 Dean Camera (dean [at] fourwalledcubicle [dot] com) Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted @@ -30,357 +38,467 @@ /** \file * - * Main source file for the iUSB2SerialMux demo. This file contains the main tasks of the demo and - * is responsible for the initial application hardware configuration. + * Main source file for the project - based on the Mouse and VirtualSerial lufa demo. + * This file contains the main tasks of the demo and is responsible for the initial + * application hardware configuration. */ #include "main.h" -/**************************************************************************************/ +//********************************************************************************// -uint32_t EEMEM eep_baudrate; +FIFO_t agent_fifo; +FIFO_t seq_mod_fifo; +FIFO_t seq_val_fifo; -uint32_t baudrate = 115200; // replacement for the UART_BAUDRATE definition +uint8_t seq_delay = 0; -/**************************************************************************************/ +volatile uint16_t time_measure_cnt = 0; +volatile uint16_t keystroke_delay_time_measure_cnt_old = 0; +volatile uint16_t seq_delay_time_measure_cnt_old = 0; -/** Contains the current baud rate and other settings of the first virtual serial port. While this demo does not use - * the physical USART and thus does not use these settings, they must still be retained and returned to the host - * upon request or the host will assume the device is non-functional. - * - * These values are set by the host via a class-specific request, however they are not required to be used accurately. - * It is possible to completely ignore these value or use other settings as the host is completely unaware of the physical - * serial link characteristics and instead sends and receives data in endpoint streams. +//********************************************************************************// + +/** Buffer to hold the previously generated Keyboard HID report, for comparison purposes inside the HID class driver. */ +static uint8_t PrevKeyboardHIDReportBuffer[sizeof(USB_KeyboardReport_Data_t)]; + +//********************************************************************************// + +/** LUFA HID Class driver interface configuration and state information. This structure is + * passed to all HID Class driver functions, so that multiple instances of the same class + * within a device can be differentiated from one another. This is for the keyboard HID + * interface within the device. */ -static CDC_LineEncoding_t LineEncoding1 = { .BaudRateBPS = 0, - .CharFormat = CDC_LINEENCODING_OneStopBit, - .ParityType = CDC_PARITY_None, - .DataBits = 8 }; +USB_ClassInfo_HID_Device_t Keyboard_HID_Interface = + { + .Config = + { + .InterfaceNumber = INTERFACE_ID_Keyboard, + .ReportINEndpoint = + { + .Address = KEYBOARD_IN_EPADDR, + .Size = KEYBOARD_EPSIZE, + .Banks = 1, + }, + .PrevReportINBuffer = PrevKeyboardHIDReportBuffer, + .PrevReportINBufferSize = sizeof(PrevKeyboardHIDReportBuffer), + }, + }; -/** Contains the current baud rate and other settings of the second virtual serial port. While this demo does not use - * the physical USART and thus does not use these settings, they must still be retained and returned to the host - * upon request or the host will assume the device is non-functional. - * - * These values are set by the host via a class-specific request, however they are not required to be used accurately. - * It is possible to completely ignore these value or use other settings as the host is completely unaware of the physical - * serial link characteristics and instead sends and receives data in endpoint streams. +/** LUFA CDC Class driver interface configuration and state information. This structure is + * passed to all CDC Class driver functions, so that multiple instances of the same class + * within a device can be differentiated from one another. */ -static CDC_LineEncoding_t LineEncoding2 = { .BaudRateBPS = 0, - .CharFormat = CDC_LINEENCODING_OneStopBit, - .ParityType = CDC_PARITY_None, - .DataBits = 8 }; +USB_ClassInfo_CDC_Device_t VirtualSerial_CDC_Interface = + { + .Config = + { + .ControlInterfaceNumber = INTERFACE_ID_CDC_CCI, + .DataINEndpoint = + { + .Address = CDC_TX_EPADDR, + .Size = CDC_TXRX_EPSIZE, + .Banks = 1, + }, + .DataOUTEndpoint = + { + .Address = CDC_RX_EPADDR, + .Size = CDC_TXRX_EPSIZE, + .Banks = 1, + }, + .NotificationEndpoint = + { + .Address = CDC_NOTIFICATION_EPADDR, + .Size = CDC_NOTIFICATION_EPSIZE, + .Banks = 1, + }, + }, + }; +/** Standard file stream for the CDC interface when set up, so that the virtual + * CDC COM port can be used like any regular character stream in the C APIs. + */ +static FILE USBSerialStream; -/** Main program entry point. This routine configures the hardware required by the application, then - * enters a loop to run the application tasks in sequence. +//********************************************************************************// + +/** Main program entry point. This routine contains the overall program flow, including initial + * setup of all components and the main program loop. */ int main(void) { + // USB/LUFA init SetupHardware(); - // initialize the command interpreter + // init the timer for time measurements + timer_init(); + + // LED matrix init + lm_init(); + + // KEY matrix init + km_init(); + + // read the key configuration table from EEPROM + ch_readConfig(); + + // init the serial communication command controller cc_init(); - SET_ERR_MASK(ERRMASK_USB_NOTREADY); + FIFO_init( agent_fifo); + FIFO_init(seq_mod_fifo); + FIFO_init(seq_val_fifo); + // Create a regular character stream for the interface so that it can be used with the stdio.h functions + CDC_Device_CreateStream(&VirtualSerial_CDC_Interface, &USBSerialStream); + + SET_GLOB_USB_STATUS(STATUSMASK_USB_NOTREADY); GlobalInterruptEnable(); for (;;) { - CDC1_Task(); - CDC2_Task(); + SendVirtualSerialData(); + + ProcessVirtualSerialData(); + + CDC_Device_USBTask(&VirtualSerial_CDC_Interface); + + HID_Device_USBTask(&Keyboard_HID_Interface); USB_USBTask(); - //uart_putc('1'); + static uint8_t delay = 0; + if (delay > 50) + { + delay = 0; + lm_show(); + } + delay++; + } } -/** Configures the board hardware and chip peripherals for the demo's functionality. */ -void SetupHardware(void) +//********************************************************************************// + +void timer_init() +{ + // configure TIMER0 to count using a specified frequency the TCNT0 variable to generate + // a overflow interrupt + TCCR0A = 0x02; // WGM1 = 1; WGM0 = 0 => CTC-mode + TCCR0B = 0x03; // prescaler = 64; WGM2 = 0 => 16MHz * 256 / 64 = ~ 1mz per Overflow Interrupt + TIMSK0 = 0x01; // Overflow Interrupt Enable + + sei(); +} + +ISR(TCC0_OVF_vect) +{ + if (time_measure_cnt >= 65534) + { + // reset all the counter values to prevent unpredictable behaviour + time_measure_cnt = 0; + seq_delay_time_measure_cnt_old = 0; + keystroke_delay_time_measure_cnt_old = 0; + } + time_measure_cnt++; +} + +//********************************************************************************// + +/** Configures the board hardware and chip peripherals for the demo's functionality. + */ +void SetupHardware() { #if (ARCH == ARCH_AVR8) - /* Disable watchdog if enabled by bootloader/fuses */ + // Disable watchdog if enabled by bootloader/fuses MCUSR &= ~(1 << WDRF); wdt_disable(); - /* Disable clock division */ + // Disable clock division */ clock_prescale_set(clock_div_1); #elif (ARCH == ARCH_XMEGA) - /* Start the PLL to multiply the 2MHz RC oscillator to 32MHz and switch the CPU core to run from it */ + // Start the PLL to multiply the 2MHz RC oscillator to 32MHz and switch the CPU + // core to run from it XMEGACLK_StartPLL(CLOCK_SRC_INT_RC2MHZ, 2000000, F_CPU); XMEGACLK_SetCPUClockSource(CLOCK_SRC_PLL); - /* Start the 32MHz internal RC oscillator and start the DFLL to increase it to 48MHz using the USB SOF as a reference */ + // Start the 32MHz internal RC oscillator and start the DFLL to increase it to + // 48MHz using the USB SOF as a reference XMEGACLK_StartInternalOscillator(CLOCK_SRC_INT_RC32MHZ); XMEGACLK_StartDFLL(CLOCK_SRC_INT_RC32MHZ, DFLL_REF_INT_USBSOF, F_USB); PMIC.CTRL = PMIC_LOLVLEN_bm | PMIC_MEDLVLEN_bm | PMIC_HILVLEN_bm; #endif - // set the three MUX control lines to output - DDRB |= (1 << PIN4) | (1 << PIN5) | (1 << PIN6); - // set every pin to low - EN_MUX_LINE0; - - DDRB |= (1 << PIN0) | (1 << PIN1); - - LED0_OFF; - LED1_OFF; - - /* UART Hardware Initialization */ - eeprom_busy_wait(); - baudrate = eeprom_read_dword(&eep_baudrate); - - if (baudrate == 0xffffffff) - { - eeprom_busy_wait(); - baudrate = 115200; - eeprom_write_dword(&eep_baudrate, baudrate); - } - - uart_init( UART_BAUD_SELECT(baudrate, F_CPU) ); - sei(); - - /* USB Hardware Initialization */ + // Hardware Initialization */ USB_Init(); } -/** Event handler for the USB_Connect event. This indicates that the device is enumerating via the status LEDs and - * starts the library USB task to begin the enumeration and USB management process. +//********************************************************************************// + +void ProcessVirtualSerialData() +{ + + // process the read data from Host here, if there is data to read + int16_t ReceivedBytes = CDC_Device_BytesReceived(&VirtualSerial_CDC_Interface); + if (ReceivedBytes > 0) + { + for (uint16_t i = 0; i < ReceivedBytes; i++) + { + int16_t ReceivedByte = CDC_Device_ReceiveByte(&VirtualSerial_CDC_Interface); + // call the command controller - command processor + cc_processData((uint8_t)ReceivedByte); + } + } + +} + +//********************************************************************************// + +void SendVirtualSerialData() +{ + char* ReportString = NULL; + + // send the next id from the agent FIFO + if (FIFO_available(agent_fifo)) + { + uint8_t id = FIFO_pop(agent_fifo); + ReportString = " "; + sprintf(ReportString, "%c%c%c%c%c%c", MSG_SOM1, MSG_SOM2, + MSG_TYPE_AGENTID, id, + MSG_EOM1, MSG_EOM2); + } + + if ((ReportString != NULL)) + { + // Write the string to the virtual COM port via the created character stream + USB_serialStreamWriteC(ReportString, 6); + } +} + +//********************************************************************************// + +/** HID class driver callback function for the creation of HID reports to the host. + * + * \param[in] HIDInterfaceInfo Pointer to the HID class interface configuration structure being referenced + * \param[in,out] ReportID Report ID requested by the host if non-zero, otherwise callback should set to the generated report ID + * \param[in] ReportType Type of the report to create, either HID_REPORT_ITEM_In or HID_REPORT_ITEM_Feature + * \param[out] ReportData Pointer to a buffer where the created report should be stored + * \param[out] ReportSize Number of bytes written in the report (or zero if no report is to be sent) + * + * \return Boolean \c true to force the sending of the report, \c false to let the library determine if it needs to be sent */ +bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo, + uint8_t* const ReportID, + const uint8_t ReportType, + void* ReportData, + uint16_t* const ReportSize) +{ + + // Determine which interface must have its report generated */ + if (HIDInterfaceInfo == &Keyboard_HID_Interface) + { + USB_KeyboardReport_Data_t* KeyboardReport = (USB_KeyboardReport_Data_t*)ReportData; + + // update the key matrix values + km_updateKeyStates(); + + // prepare the new sequence related to the pressed key + for (uint8_t k = 0; k < LM_LED_CNT; k++) + { + if (km_getKeyState(k) == KEY_STATE_GO_DOWN) + { + FIFO_push(agent_fifo, k); + + for (uint8_t s = 0; s < EEP_KEY_CNT; s++) + { + // ignore a mod/value combination of 0xff 0xff + // just add the sequence to a "sequence execution FIFO" in case that it + // is not an agent call + // prevent multiple calls + + // TODO manage multikey (simultanous pressed) shortcuts here + + if (key_config[k][s][0] == 0xff && key_config[k][s][1] == 0xff) + { + s = EEP_KEY_CNT; // abort the loop, no more data found + } else { + FIFO_push(seq_mod_fifo, key_config[k][s][0]); + FIFO_push(seq_val_fifo, key_config[k][s][1]); + } + } + } + } + + // TODO maybe we shall do ab bit more precise time measurement + if (seq_delay > 0 && time_measure_cnt > seq_delay_time_measure_cnt_old) + { + seq_delay--; + seq_delay_time_measure_cnt_old = time_measure_cnt; + } + + if (keystroke_delay_cnt > 0 && time_measure_cnt > keystroke_delay_time_measure_cnt_old) + { + // TODO test if NN seconds are gone + keystroke_delay_cnt--; + keystroke_delay_time_measure_cnt_old = time_measure_cnt; + } + + // execute the sequence execution FIFO content + if (FIFO_available(seq_mod_fifo) && FIFO_available(seq_val_fifo) && + seq_delay == 0 && keystroke_delay_cnt == 0) + { + uint8_t mod = FIFO_pop(seq_mod_fifo); + uint8_t val = FIFO_pop(seq_val_fifo); + + // reset the keystroke delay + keystroke_delay_cnt = keystroke_delay; + + // process the data + if ((mod & KEY_MOD_DELAY) != 0) + { + // start the delay + seq_delay = val; + } else { + // TODO be aware of the os_type variable + if ((mod & KEY_MOD_FN) != 0) + KeyboardReport->Modifier += HID_KEYBOARD_MODIFIER_RIGHTSHIFT; // TODO fix the modifier + if ((mod & KEY_MOD_SHIFT) != 0) + KeyboardReport->Modifier += HID_KEYBOARD_MODIFIER_LEFTSHIFT; + if ((mod & KEY_MOD_CTRL) != 0) + KeyboardReport->Modifier += HID_KEYBOARD_MODIFIER_LEFTCTRL; + if ((mod & KEY_MOD_ALT) != 0) + KeyboardReport->Modifier += HID_KEYBOARD_MODIFIER_LEFTALT; + if ((mod & KEY_MOD_ALTGR) != 0) + KeyboardReport->Modifier += HID_KEYBOARD_MODIFIER_RIGHTALT; + if ((mod & KEY_MOD_SUPER) != 0) + KeyboardReport->Modifier += HID_KEYBOARD_MODIFIER_LEFTGUI; + + // possible values: http://www.fourwalledcubicle.com/files/LUFA/Doc/120219/html/group___group___u_s_b_class_h_i_d_common.html + KeyboardReport->KeyCode[0] = val; // TODO one key at a time, up to 6 is supported: http://www.fourwalledcubicle.com/files/LUFA/Doc/120219/html/struct_u_s_b___keyboard_report___data__t.html#a1c24d97011685d58ab05e2f65d7b2c1b + } + } + + // Some debug and test code + for (uint8_t i = 0; i < LM_LED_CNT; i++) + { + if (km_getKeyState(i) == KEY_STATE_GO_DOWN) + lm_ledOn(i); + if (km_getKeyState(i) == KEY_STATE_GO_UP) + lm_ledOff(i); + } + + *ReportSize = sizeof(USB_KeyboardReport_Data_t); + + } + return false; +} + +//********************************************************************************// + +/** HID class driver callback function for the processing of HID reports from the host. + * + * \param[in] HIDInterfaceInfo Pointer to the HID class interface configuration structure being referenced + * \param[in] ReportID Report ID of the received report from the host + * \param[in] ReportType The type of report that the host has sent, either HID_REPORT_ITEM_Out or HID_REPORT_ITEM_Feature + * \param[in] ReportData Pointer to a buffer where the received report has been stored + * \param[in] ReportSize Size in bytes of the received HID report + */ +void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo, + const uint8_t ReportID, + const uint8_t ReportType, + const void* ReportData, + const uint16_t ReportSize) +{ + if (HIDInterfaceInfo == &Keyboard_HID_Interface) + { + uint8_t* LEDReport = (uint8_t*)ReportData; + + /* + // TODO process the Keyboard LED status information + if (*LEDReport & HID_KEYBOARD_LED_NUMLOCK) + + if (*LEDReport & HID_KEYBOARD_LED_CAPSLOCK) + + if (*LEDReport & HID_KEYBOARD_LED_SCROLLLOCK) + */ + + } +} + +//********************************************************************************// + +/** CDC class driver callback function the processing of changes to the virtual + * control lines sent from the host.. + * + * \param[in] CDCInterfaceInfo Pointer to the CDC class interface configuration structure being referenced + */ +void EVENT_CDC_Device_ControLineStateChanged(USB_ClassInfo_CDC_Device_t *const CDCInterfaceInfo) +{ + // You can get changes to the virtual CDC lines in this callback; a common + // use-case is to use the Data Terminal Ready (DTR) flag to enable and + // disable CDC communications in your application when set to avoid the + // application blocking while waiting for a host to become ready and read + // in the pending data from the USB endpoints. + + bool HostReady = (CDCInterfaceInfo->State.ControlLineStates.HostToDevice & CDC_CONTROL_LINE_OUT_DTR) != 0; +} + +//********************************************************************************// + +/** Event handler for the library USB Connection event. */ void EVENT_USB_Device_Connect(void) { - /* Indicate USB enumerating */ - SET_ERR_MASK(ERRMASK_USB_ENUMERATING); + SET_GLOB_USB_STATUS(STATUSMASK_USB_ENUMERATING); } -/** Event handler for the USB_Disconnect event. This indicates that the device is no longer connected to a host via - * the status LEDs and stops the USB management and CDC management tasks. - */ +/** Event handler for the library USB Disconnection event. */ void EVENT_USB_Device_Disconnect(void) { - /* Indicate USB not ready */ - SET_ERR_MASK(ERRMASK_USB_NOTREADY); + SET_GLOB_USB_STATUS(STATUSMASK_USB_NOTREADY); } -/** Event handler for the USB_ConfigurationChanged event. This is fired when the host set the current configuration - * of the USB device after enumeration - the device endpoints are configured and the CDC management tasks are started. - */ +/** Event handler for the library USB Configuration Changed event. */ void EVENT_USB_Device_ConfigurationChanged(void) { bool ConfigSuccess = true; - /* Setup first CDC Interface's Endpoints */ - ConfigSuccess &= Endpoint_ConfigureEndpoint(CDC1_TX_EPADDR, EP_TYPE_BULK, CDC_TXRX_EPSIZE, 1); - ConfigSuccess &= Endpoint_ConfigureEndpoint(CDC1_RX_EPADDR, EP_TYPE_BULK, CDC_TXRX_EPSIZE, 1); - ConfigSuccess &= Endpoint_ConfigureEndpoint(CDC1_NOTIFICATION_EPADDR, EP_TYPE_INTERRUPT, CDC_NOTIFICATION_EPSIZE, 1); + ConfigSuccess &= HID_Device_ConfigureEndpoints(&Keyboard_HID_Interface); + ConfigSuccess &= CDC_Device_ConfigureEndpoints(&VirtualSerial_CDC_Interface); - /* Setup second CDC Interface's Endpoints */ - ConfigSuccess &= Endpoint_ConfigureEndpoint(CDC2_TX_EPADDR, EP_TYPE_BULK, CDC_TXRX_EPSIZE, 1); - ConfigSuccess &= Endpoint_ConfigureEndpoint(CDC2_RX_EPADDR, EP_TYPE_BULK, CDC_TXRX_EPSIZE, 1); - ConfigSuccess &= Endpoint_ConfigureEndpoint(CDC2_NOTIFICATION_EPADDR, EP_TYPE_INTERRUPT, CDC_NOTIFICATION_EPSIZE, 1); + USB_Device_EnableSOFEvents(); - /* Reset line encoding baud rates so that the host knows to send new values */ - LineEncoding1.BaudRateBPS = 0; - LineEncoding2.BaudRateBPS = 0; - - /* Indicate endpoint configuration success or failure */ - SET_ERR_MASK(ConfigSuccess ? ERRMASK_USB_READY : ERRMASK_USB_ERROR); + SET_GLOB_USB_STATUS(ConfigSuccess ? STATUSMASK_USB_READY : STATUSMASK_USB_ERROR); } -/** Event handler for the USB_ControlRequest event. This is used to catch and process control requests sent to - * the device from the USB host before passing along unhandled control requests to the library for processing - * internally. - */ +/** Event handler for the library USB Control Request reception event. */ void EVENT_USB_Device_ControlRequest(void) { - /* Determine which interface's Line Coding data is being set from the wIndex parameter */ - void* LineEncodingData = (USB_ControlRequest.wIndex == 0) ? &LineEncoding1 : &LineEncoding2; - - /* Process CDC specific control requests */ - switch (USB_ControlRequest.bRequest) - { - case CDC_REQ_GetLineEncoding: - if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE)) - { - Endpoint_ClearSETUP(); - - /* Write the line coding data to the control endpoint */ - Endpoint_Write_Control_Stream_LE(LineEncodingData, sizeof(CDC_LineEncoding_t)); - Endpoint_ClearOUT(); - } - - break; - case CDC_REQ_SetLineEncoding: - if (USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE)) - { - Endpoint_ClearSETUP(); - - /* Read the line coding data in from the host into the global struct */ - Endpoint_Read_Control_Stream_LE(LineEncodingData, sizeof(CDC_LineEncoding_t)); - Endpoint_ClearIN(); - } - - break; - case CDC_REQ_SetControlLineState: - if (USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE)) - { - Endpoint_ClearSETUP(); - Endpoint_ClearStatusStage(); - } - - break; - } + HID_Device_ProcessControlRequest(&Keyboard_HID_Interface); + CDC_Device_ProcessControlRequest(&VirtualSerial_CDC_Interface); } -/** Function to manage CDC data transmission and reception to and from the host for the first CDC interface, - * which sends answers or response data to the host. - */ -void CDC1_Task(void) +/** Event handler for the USB device Start Of Frame event. */ +void EVENT_USB_Device_StartOfFrame(void) { - /* Device must be connected and configured for the task to run */ - if (USB_DeviceState != DEVICE_STATE_Configured) - return; - - //=========================================================================== - /* Select the Serial Rx Endpoint */ - Endpoint_SelectEndpoint(CDC1_RX_EPADDR); - - if (Endpoint_IsOUTReceived()) - { - /* Create a temp buffer big enough to hold the incoming endpoint packet */ - uint8_t Buffer[Endpoint_BytesInEndpoint()]; - - /* Remember how large the incoming packet is */ - uint16_t DataLength = Endpoint_BytesInEndpoint(); - - /* Read in the incoming packet into the buffer */ - Endpoint_Read_Stream_LE(&Buffer, DataLength, NULL); - - /* Finalize the stream transfer to send the last packet */ - Endpoint_ClearOUT(); - - for (uint16_t i = 0; i < DataLength; i++) - { - // process the received data and descide to do an action - LED0_ON; - cc_processData(Buffer[i]); - LED0_OFF; - } - - } - + HID_Device_MillisecondElapsed(&Keyboard_HID_Interface); } -void USB_serialStreamWriteC(char *data, uint16_t len) +//********************************************************************************// + +void USB_serialStreamWriteC(char* msg, uint16_t len) { - //=========================================================================== - /* Determine if data/answeres should be sent to the host - * the previous RX section should be clarify that behaviour. - */ - - /* Flag management - Only allow one string to be sent per action */ - if (data != NULL && len > 0 && LineEncoding1.BaudRateBPS) + for (uint8_t i = 0; i < len; i++) { - - LED1_ON; - - /* Select the Serial Tx Endpoint */ - Endpoint_SelectEndpoint(CDC1_TX_EPADDR); - - /* Write the String to the Endpoint */ - Endpoint_Write_Stream_LE(data, len, NULL); - - /* Finalize the stream transfer to send the last packet */ - Endpoint_ClearIN(); - - /* Wait until the endpoint is ready for another packet */ - Endpoint_WaitUntilReady(); - - /* Send an empty packet to ensure that the host does not buffer data sent to it */ - Endpoint_ClearIN(); - - LED1_OFF; + fputc(msg[i], &USBSerialStream); } - } -/** Function to manage CDC data transmission and reception to and from the host for the second CDC interface, - * which sends all data received from a node (mux) during USART to the host. - */ -void CDC2_Task(void) +void USB_serialStreamWrite(char* msg) { - /* Device must be connected and configured for the task to run */ - if (USB_DeviceState != DEVICE_STATE_Configured) - return; - - //=========================================================================== - /* Select the Serial Rx Endpoint */ - Endpoint_SelectEndpoint(CDC2_RX_EPADDR); - - /* Check to see if any data has been received */ - if (Endpoint_IsOUTReceived()) - { - /* Create a temp buffer big enough to hold the incoming endpoint packet */ - uint8_t Buffer[Endpoint_BytesInEndpoint()]; - - /* Remember how large the incoming packet is */ - uint16_t DataLength = Endpoint_BytesInEndpoint(); - - /* Read in the incoming packet into the buffer */ - Endpoint_Read_Stream_LE(&Buffer, DataLength, NULL); - - /* Finalize the stream transfer to send the last packet */ - Endpoint_ClearOUT(); - - // TODO at this point send the data to the USART - // Send USART &Buffer - for (uint16_t i = 0; i < DataLength; i++) - { - uart_putc(Buffer[i]); - } - } - - //return; - - //=========================================================================== - uint8_t outBuffer[OUTPUT_BUFFER_SIZE]; - // TODO read the USART data and send them to the host - // Fill &Buffer with USART data or send the USART input buffer direct - uint16_t cnt = 0; - int c = uart_getc(); - while (!(c & UART_NO_DATA) && cnt < OUTPUT_BUFFER_SIZE) - { - //LED0_ON; - outBuffer[cnt] = c; - c = uart_getc(); - cnt++; - } - - /* - cnt = 1; - outBuffer[0] = '2'; - */ - - // send the data which was received from the uart connection - if (cnt > 0) - { - /* Select the Serial Tx Endpoint */ - Endpoint_SelectEndpoint(CDC2_TX_EPADDR); - - /* Write the received data to the endpoint */ - Endpoint_Write_Stream_LE(&outBuffer, cnt, NULL); - - /* Finalize the stream transfer to send the last packet */ - Endpoint_ClearIN(); - - /* Wait until the endpoint is ready for the next packet */ - Endpoint_WaitUntilReady(); - - /* Send an empty packet to prevent host buffering */ - Endpoint_ClearIN(); - } + fputs(msg, &USBSerialStream); } +//********************************************************************************// + diff --git a/tools/_serial.py b/tools/_serial.py new file mode 100755 index 0000000..c9dec4c --- /dev/null +++ b/tools/_serial.py @@ -0,0 +1,36 @@ + +# a simple class to manage serial communication testing without to install the pySerial package + +class Serial: + + def __init__(self, device, baudrate, timeout): + self.device = device + self.baudrate = baudrate + self.timeout = timeout + self.msg_ok = [ 0x3c, 0x3e, 0x01, 0x0d, 0x0a ] + self.msg_nok = [ 0x3c, 0x3e, 0x02, 0x0d, 0x0a ] + self.msg_br = [ 0x3c, 0x3e, 0x03, 0x00, 0x01, 0xc2, 0x00, 0x0d, 0x0a ] + self.msg_ml = [ 0x3c, 0x3e, 0x04, 0x00, 0x0d, 0x0a ] + + def read(self, num=0): + if num == 0: + return bytearray(['0']) + else: + ret = [] + tmp = self.msg_ok + tmp += self.msg_nok + tmp += self.msg_br + tmp += self.msg_ml + for i in range(0, num): + if i < len(tmp): + ret.append(tmp[i]) + + return bytearray(ret) + + def write(self, data): + for d in data: + print "0x%02x " % (d) + pass + + def close(self): + print "Serial connection close" diff --git a/tools/logClusterBootup.sh b/tools/logClusterBootup.sh new file mode 100755 index 0000000..f6d49fd --- /dev/null +++ b/tools/logClusterBootup.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# reset the mux device to default values +pypy ./muxctrl.py -g -m -l 0 -s 115200 + +# TODO start logging of the received data + +# read from line 0 to 7 +for i in `seq 0 7`; +do + echo "channel #"$i + + pypy ./muxctrl.py -l $i + + sleep 30 +done + +sleep 3 + +# TODO stop logging of the received data + +pypy ./muxctrl.py -g -m -l 0 -s 115200 + diff --git a/tools/muxctrl.py b/tools/muxctrl.py index 7776d1c..8226b4d 100755 --- a/tools/muxctrl.py +++ b/tools/muxctrl.py @@ -10,11 +10,11 @@ import copy ############################################################################### parser = argparse.ArgumentParser(description='USB2SerialMux control helper tool.') -parser.add_argument("-g", "--getbaudrate", default=False, help="", action='store_true') -parser.add_argument("-s", "--setbaudrate", type=int, help="Something like 9600 or 115200.") -parser.add_argument("-m", "--getmuxline", default=False, help="", action='store_true') -parser.add_argument("-l", "--setmuxline", type=int, help="Something like 0 to 7.") -parser.add_argument("-r", "--resettobtldr", default=False, help="Reset the device to the LUFA bootloader.", action='store_true') +parser.add_argument("-g", "--getBaudrate", default=False, help="Get the baudrate for the USART connection.", action='store_true') +parser.add_argument("-s", "--setBaudrate", type=int, help="Something like 9600 or 115200.") +parser.add_argument("-m", "--getMuxLine", default=False, help="Get the current multiplexer control line state.", action='store_true') +parser.add_argument("-l", "--setMuxLine", type=int, help="Something like 0 to 7.") +parser.add_argument("-r", "--resetToBtldr", default=False, help="Reset the device to the LUFA bootloader.", action='store_true') ############################################################################### @@ -28,23 +28,27 @@ MSG_TYPE_ANSWER_NOK = 0x02 MSG_TYPE_BAUDRATE = 0x03 MSG_TYPE_MUXLINE = 0x04 +MSG_TYPE_ANSWER_OK_DATA_TO_RECV = 0 +MSG_TYPE_ANSWER_NOK_DATA_TO_RECV = 0 +MSG_TYPE_BAUDRATE_DATA_TO_RECV = 4 +MSG_TYPE_MUXLINE_DATA_TO_RECV = 1 + CC_CMD_SET_BAUDRATE = 0x0A CC_CMD_GET_BAUDRATE = 0x14 CC_CMD_SET_MUX_LINE = 0x1E CC_CMD_GET_MUX_LINE = 0x28 CC_CMD_START_BTLDR = 0x32 -CC_CMD_GET_BAUDRATE_DATA_TO_RECV 4 -CC_CMD_SET_BAUDRATE_DATA_TO_RECV 0 -CC_CMD_GET_MUX_LINE_DATA_TO_RECV 1 -CC_CMD_SET_MUX_LINE_DATA_TO_RECV 0 -CC_CMD_START_BTLDR_DATA_TO_RECV 0 +############################################################################### + +TIMEOUT_CNT_MAX = 50 +MAIN_LOOP_DELAY_S = 0.05 +THREAD_LOOP_DELAY_S = 0.01 ############################################################################### -TIMEOUT_CNT_MAX = 1000 -MAIN_LOOP_DELAY_S = 0.05 -THREAD_LOOP_DELAY_S = 0.01 +MUX_MIN_VAL = 0 +MUX_MAX_VAL = 7 ############################################################################### @@ -62,30 +66,31 @@ CC_STATE_WAIT_SOD2 = 0x02 CC_STATE_READ_DATA = 0x03 CC_STATE_WAIT_EOD1 = 0x04 CC_STATE_WAIT_EOD2 = 0x05 +CC_STATE_GET_TYPE = 0x06 cc_state = CC_STATE_WAIT_SOD1 -cc_cmd_list = [ CC_CMD_SET_BAUDRATE, - CC_CMD_GET_BAUDRATE, - CC_CMD_SET_MUX_LINE, - CC_CMD_GET_MUX_LINE, - CC_CMD_START_BTLDR, ] +cc_state_list = [ CC_STATE_WAIT_SOD1, + CC_STATE_WAIT_SOD2, + CC_STATE_READ_DATA, + CC_STATE_WAIT_EOD1, + CC_STATE_WAIT_EOD2 ] -cc_state_fn = { CC_STATE_WAIT_SOD1 : cc_state_fn_wait_for_sod1, - CC_STATE_WAIT_SOD2 : cc_state_fn_wait_for_sod2, - CC_STATE_WAIT_EOD1 : cc_state_fn_wait_for_eod1, - CC_STATE_WAIT_EOD2 : cc_state_fn_wait_for_eod2, - CC_STATE_GET_TYPE : cc_state_fn_get_type, - CC_STATE_READ_DATA : cc_state_fn_read_data, } +cc_state_fn = {} -cc_cmd_data_to_read = { CC_CMD_SET_BAUDRATE : CC_CMD_SET_BAUDRATE_DATA_TO_RECV, - CC_CMD_GET_BAUDRATE : CC_CMD_GET_BAUDRATE_DATA_TO_RECV, - CC_CMD_SET_MUX_LINE : CC_CMD_SET_MUX_LINE_DATA_TO_RECV, - CC_CMD_GET_MUX_LINE : CC_CMD_GET_MUX_LINE_DATA_TO_RECV, - CC_CMD_START_BTLDR : CC_CMD_START_BTLDR_DATA_TO_RECV, } +msg_type_list = [ MSG_TYPE_ANSWER_OK, + MSG_TYPE_ANSWER_NOK, + MSG_TYPE_BAUDRATE, + MSG_TYPE_MUXLINE, ] + +msg_type_data_to_read = { MSG_TYPE_ANSWER_OK : MSG_TYPE_ANSWER_OK_DATA_TO_RECV, + MSG_TYPE_ANSWER_NOK : MSG_TYPE_ANSWER_NOK_DATA_TO_RECV, + MSG_TYPE_BAUDRATE : MSG_TYPE_BAUDRATE_DATA_TO_RECV, + MSG_TYPE_MUXLINE : MSG_TYPE_MUXLINE_DATA_TO_RECV, } + +msg_type = 0 cc_data_read = 0 -cc_type = 0 cc_data_buffer = [] # yes a separate counter to manage the order of the received messages @@ -94,95 +99,163 @@ cc_received_messages = [] ############################################################################### -thread_obj = None -thread_lock = None +thread_obj = None +thread_lock = None +thread_started = False +thread_stop = False ############################################################################### +def cc_init(): + + global cc_state_fn + global cc_state + + cc_state = CC_STATE_WAIT_SOD1 + + cc_state_fn = { CC_STATE_WAIT_SOD1 : cc_state_fn_wait_for_sod1, + CC_STATE_WAIT_SOD2 : cc_state_fn_wait_for_sod2, + CC_STATE_WAIT_EOD1 : cc_state_fn_wait_for_eod1, + CC_STATE_WAIT_EOD2 : cc_state_fn_wait_for_eod2, + CC_STATE_GET_TYPE : cc_state_fn_get_type, + CC_STATE_READ_DATA : cc_state_fn_read_data, } + ########## function to call by the thread -def processReceivedData(): +def cc_dataReceiverThread(): + + global ser + global cc_state + global cc_state_fn + global thread_started + global thread_stop thread_started = True - for thread_stop == False: + while thread_stop == False: # 1. read byte from serial port into incoming - incoming = [] - incoming = ser.read(64) + incoming = [] + incoming = ser.read(64) # 2. process the received data for c in incoming: + + # call the cc_state specific function to process the currently received byte cc_state_fn[cc_state](c) + if cc_state not in cc_state_list: + cc_state = CC_STATE_WAIT_SOD1 + time.sleep(THREAD_LOOP_DELAY_S) + thread_stop = True thread_started = False ########## -def startReceiverThread(): +def cc_startReceiverThread(): + + global thread_obj + global thread_lock + global thread_stop + if thread_started == False: thread_lock = threading.Lock() - thread_obj = threading.Thread(target=processReceivedData) + thread_obj = threading.Thread(target=cc_dataReceiverThread) thread_obj.start() thread_stop = False ########## -def stopReceiverThread(): - thread_stop = True - thread_obj.join() # wait for the thread to finish +def cc_stopReceiverThread(): + + global thread_obj + global thread_started + global thread_stop + + if thread_started == True: + thread_stop = True + thread_obj.join() # wait for the thread to finish ##### CC_STATE_WAIT_SOD1 def cc_state_fn_wait_for_sod1(c): + + global cc_data_read + global msg_type + global cc_data_buffer + global cc_state + cc_data_read = 0 - cc_type = 0 + msg_type = 0 cc_data_buffer = [] if c == MSG_SOD1: cc_state = CC_STATE_WAIT_SOD2 - else + else: cc_state = CC_STATE_WAIT_SOD1 ##### CC_STATE_WAIT_SOD2 def cc_state_fn_wait_for_sod2(c): + + global cc_state + if c == MSG_SOD2: cc_state = CC_STATE_GET_TYPE - else + else: cc_state = CC_STATE_WAIT_SOD1 ##### CC_STATE_GET_TYPE def cc_state_fn_get_type(c): - if c in cc_cmd_list: - cc_type = c - if cc_cmd_data_to_read[cc_type] > 0: + + global msg_type + global cc_state + + if c in msg_type_list: + msg_type = c + if msg_type_data_to_read[msg_type] > 0: cc_state = CC_STATE_READ_DATA else: cc_state = CC_STATE_WAIT_EOD1 - else + else: cc_state = CC_STATE_WAIT_SOD1 ##### CC_STATE_READ_DATA def cc_state_fn_read_data(c): - if cc_data_read <= cc_state_data_to_read[cc_state] - 1: - cc_data_buffer[cc_data_read] = c + + global cc_data_buffer + global cc_data_read + global cc_state + + if cc_data_read <= msg_type_data_to_read[msg_type] - 1: + cc_data_buffer.append(c) cc_data_read = cc_data_read + 1 - if cc_data_read == cc_state_data_to_read[cc_state]: + if cc_data_read == msg_type_data_to_read[msg_type]: cc_state = CC_STATE_WAIT_EOD1 ##### CC_STATE_WAIT_EOD1 def cc_state_fn_wait_for_eod1(c): + + global cc_state + if c == MSG_EOD1: cc_state = CC_STATE_WAIT_EOD2 - else + else: cc_state = CC_STATE_WAIT_SOD1 ##### CC_STATE_WAIT_EOD2 def cc_state_fn_wait_for_eod2(c): + + global thread_lock + global cc_message_cnt + global cc_state + if c == MSG_EOD2: # TODO process or save the data is_message_read = False thread_lock.acquire() - cc_received_messages.append([ cc_message_cnt, cc_type, is_message_read, copy.deepcopy(cc_data_buffer) ]) + cc_received_messages.append([ cc_message_cnt, + msg_type, + is_message_read, + copy.deepcopy(cc_data_buffer) ]) thread_lock.release() cc_message_cnt = cc_message_cnt + 1 @@ -190,60 +263,71 @@ def cc_state_fn_wait_for_eod2(c): ############################################################################### -def getBaudrate(): - print "send: get baudrate " % CC_CMD_GET_BAUDRATE +def send_getBaudrate(): + + print "send: get baudrate (0x%02x)" % (CC_CMD_GET_BAUDRATE) sendSerialData([CC_CMD_GET_BAUDRATE]) -def setBaudrate(b): - print "send: set baudrate " % CC_CMD_SET_BAUDRATE - sendSerialData([CC_CMD_SET_BAUDRATE, - (baudrate & 0xff000000) >> 24, - (baudrate & 0xff0000) >> 16, - (baudrate & 0xff00) >> 8, - (baudrate & 0xff)]) +def send_setBaudrate(b): + + print "send: set baudrate (0x%02x)" % (CC_CMD_SET_BAUDRATE) + sendSerialData([ CC_CMD_SET_BAUDRATE, + (b & 0xff000000) >> 24, + (b & 0xff0000) >> 16, + (b & 0xff00) >> 8, + (b & 0xff) ]) ############################################################################### -def getMuxLine(): - print "send: get mux line " % CC_CMD_GET_MUX_LINE +def send_getMuxLine(): + + print "send: get mux line (0x%02x)" % (CC_CMD_GET_MUX_LINE) sendSerialData([CC_CMD_GET_MUX_LINE]) -def setMuxLine(l): - print "send: set mux line " % CC_CMD_SET_MUX_LINE - sendSerialData([CC_CMD_SET_MUX_LINE, muxLine]) +def send_setMuxLine(l): + + print "send: set mux line (0x%02x)" % (CC_CMD_SET_MUX_LINE) + sendSerialData([CC_CMD_SET_MUX_LINE, l]) ############################################################################### -def getStatus(): +def send_resetToBtldr(): - getBaudrate() - getMuxLine() - -############################################################################### - -def resetToBtldr(): - print "send: reset to bootloader message " % CC_CMD_START_BTLDR + print "send: reset to bootloader message (0x%02x)" % (CC_CMD_START_BTLDR) sendSerialData([CC_CMD_START_BTLDR]) ############################################################################### ##### def openSerialDevice(d): + + global ser + + # Why 115200? Because the host defines the baudrate for the USB serial connection. ser = serial.Serial(d, 115200, timeout=0) ##### def closeSerialDevice(): + + global ser + ser.close() ##### def sendSerialData(data): - for c in data: - ser.write(c) + + global ser + + ser.write([ MSG_SOD1, MSG_SOD2 ]) + ser.write(bytearray(data)) + ser.write([ MSG_EOD1, MSG_EOD2 ]) ############################################################################### if __name__ == "__main__": + cc_init() + # parse the commandline arguments args = parser.parse_args() @@ -253,67 +337,70 @@ if __name__ == "__main__": # 1. open serial device or abort openSerialDevice("/dev/ttyACM0") - # 2. start thread to poll processReceivedData() - startReceiverThread() + # 2. start thread to poll cc_dataReceiverThread() + cc_startReceiverThread() - # 3. get status - getStatus() - - # 4. get and process the commandline arguments/parameter - if args.resettobtldr == True: - resetToBtldr() + # 3. get and process the commandline arguments/parameter + if args.resetToBtldr == True: + send_resetToBtldr() else: - if args.getbaudrate == True: - getBaudrate() + if args.setBaudrate != None: + baudrate = args.setBaudrate + send_setBaudrate(baudrate) dataSend = dataSend + 1 - if args.getmuxline == True: - getMuxLine() + if args.setMuxLine != None: + muxLine = args.setMuxLine + + # keep the mux line in range + if muxLine < MUX_MIN_VAL: + muxLine = MUX_MIN_VAL + if muxLine > MUX_MAX_VAL: + muxLine = MUX_MAX_VAL + + send_setMuxLine(muxLine) dataSend = dataSend + 1 - if args.setbaudrate != None: - baudrate = map(int, args.setbaudrate) - setBaudrate(baudrate) + if args.getBaudrate == True: + send_getBaudrate() dataSend = dataSend + 1 - if args.setmuxline != None: - muxLine = map(int, args.setmuxline) - if muxLine < 0: - muxLine = 0 - if muxLine > 7: - muxLine = 7 - setMuxLine(muxLine) + if args.getMuxLine == True: + send_getMuxLine() dataSend = dataSend + 1 - # 5. start main loop - for dataSend > 0 and timeout < TIMEOUT_CNT_MAX: + # 4. start main loop + while dataSend > 0 and timeout < TIMEOUT_CNT_MAX: thread_lock.acquire() tmp_messages = copy.deepcopy(cc_received_messages) thread_lock.release() - # 5.1 wait for the response(s) + # 4.1 test for the response(s) for e in tmp_messages: if e[2] == False: # test for unread message # process it and set the data to read if e[1] == MSG_TYPE_ANSWER_OK: print "recv: OK" + elif e[1] == MSG_TYPE_ANSWER_NOK: print "recv: NOT OK" + elif e[1] == MSG_TYPE_BAUDRATE: - baudrate = e[3][0] << 24 - baudrate = baudrate + e[3][1] << 16 - baudrate = baudrate + e[3][2] << 8 - baudrate = baudrate + e[3][3] - print "recv: baudrate = " % baudrate + baudrate = e[3][0] << 24 + baudrate += e[3][1] << 16 + baudrate += e[3][2] << 8 + baudrate += e[3][3] + print "recv: baudrate = %d" % (baudrate) + elif e[1] == MSG_TYPE_MUXLINE: muxLine = e[3][0] - print "recv: muxLine = " % muxLine + print "recv: muxLine = %d" % (muxLine) else: - print "err: unknown type " % e[1] + print "err: unknown type 0x%02x" % (e[1]) break thread_lock.acquire() @@ -329,10 +416,13 @@ if __name__ == "__main__": time.sleep(MAIN_LOOP_DELAY_S) timeout = timeout + 1 - # 6. stop data processing thread - stopReceiverThread() + if timeout >= TIMEOUT_CNT_MAX: + print "Timeout happened" - # 7. close serial device + # 5. stop data processing thread + cc_stopReceiverThread() + + # 6. close serial device closeSerialDevice() exit(0)