2016-08-30 16:48:55 +02:00
/**
* description : Modified LUFA example to get two virtual serial USB devices .
* author : Kai Lauterbach
* date : 08 / 2016
* version : v0 .1
* license : GPLv3
*/
2016-07-28 10:22:37 +02:00
/*
LUFA Library
2016-08-30 16:48:55 +02:00
Copyright ( C ) Dean Camera , 2016.
2016-07-28 10:22:37 +02:00
dean [ at ] fourwalledcubicle [ dot ] com
www . lufa - lib . org
*/
/*
2016-08-30 16:48:55 +02:00
Copyright 2016 Dean Camera ( dean [ at ] fourwalledcubicle [ dot ] com )
2016-07-28 10:22:37 +02:00
Permission to use , copy , modify , distribute , and sell this
software and its documentation for any purpose is hereby granted
without fee , provided that the above copyright notice appear in
all copies and that both that the copyright notice and this
permission notice and warranty disclaimer appear in supporting
documentation , and that the name of the author not be used in
advertising or publicity pertaining to distribution of the
software without specific , written prior permission .
The author disclaims all warranties with regard to this
software , including all implied warranties of merchantability
and fitness . In no event shall the author be liable for any
special , indirect or consequential damages or any damages
whatsoever resulting from loss of use , data or profits , whether
in an action of contract , negligence or other tortious action ,
arising out of or in connection with the use or performance of
this software .
*/
/** \file
*
2016-08-30 16:48:55 +02:00
* 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 .
2016-07-28 10:22:37 +02:00
*/
2016-08-23 09:32:19 +02:00
# include "main.h"
2016-08-30 16:48:55 +02:00
//********************************************************************************//
2016-08-23 09:32:19 +02:00
2016-08-30 16:48:55 +02:00
FIFO_t agent_fifo ;
FIFO_t seq_mod_fifo ;
FIFO_t seq_val_fifo ;
2016-08-23 09:32:19 +02:00
2016-08-30 16:48:55 +02:00
uint8_t seq_delay = 0 ;
2016-08-23 09:32:19 +02:00
2016-08-30 16:48:55 +02:00
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 ;
2016-07-28 10:22:37 +02:00
2016-08-30 16:48:55 +02:00
//********************************************************************************//
/** 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 .
2016-07-28 10:22:37 +02:00
*/
2016-08-30 16:48:55 +02:00
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 ) ,
} ,
} ;
/** 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 .
2016-07-28 10:22:37 +02:00
*/
2016-08-30 16:48:55 +02:00
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 ;
2016-07-28 10:22:37 +02:00
2016-08-30 16:48:55 +02:00
//********************************************************************************//
2016-07-28 10:22:37 +02:00
2016-08-30 16:48:55 +02:00
/** Main program entry point. This routine contains the overall program flow, including initial
* setup of all components and the main program loop .
2016-07-28 10:22:37 +02:00
*/
int main ( void )
{
2016-08-30 16:48:55 +02:00
// USB/LUFA init
2016-08-17 19:47:39 +02:00
SetupHardware ( ) ;
2016-07-28 10:22:37 +02:00
2016-08-30 16:48:55 +02:00
// 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
2016-08-23 09:32:19 +02:00
cc_init ( ) ;
2016-08-17 16:00:51 +02:00
2016-08-30 16:48:55 +02:00
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 ) ;
2016-08-23 19:10:37 +02:00
2016-08-30 16:48:55 +02:00
SET_GLOB_USB_STATUS ( STATUSMASK_USB_NOTREADY ) ;
2016-08-17 19:47:39 +02:00
GlobalInterruptEnable ( ) ;
2016-07-28 10:22:37 +02:00
2016-08-17 19:47:39 +02:00
for ( ; ; )
{
2016-08-30 16:48:55 +02:00
SendVirtualSerialData ( ) ;
ProcessVirtualSerialData ( ) ;
CDC_Device_USBTask ( & VirtualSerial_CDC_Interface ) ;
HID_Device_USBTask ( & Keyboard_HID_Interface ) ;
2016-08-17 16:00:51 +02:00
2016-08-17 19:47:39 +02:00
USB_USBTask ( ) ;
2016-08-23 14:09:50 +02:00
2016-08-30 16:48:55 +02:00
static uint8_t delay = 0 ;
if ( delay > 50 )
{
delay = 0 ;
lm_show ( ) ;
}
delay + + ;
2016-08-17 19:47:39 +02:00
}
2016-07-28 10:22:37 +02:00
}
2016-08-30 16:48:55 +02:00
//********************************************************************************//
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 ( )
2016-07-28 10:22:37 +02:00
{
# if (ARCH == ARCH_AVR8)
2016-08-30 16:48:55 +02:00
// Disable watchdog if enabled by bootloader/fuses
2016-08-17 19:47:39 +02:00
MCUSR & = ~ ( 1 < < WDRF ) ;
wdt_disable ( ) ;
2016-07-28 10:22:37 +02:00
2016-08-30 16:48:55 +02:00
// Disable clock division */
2016-08-17 19:47:39 +02:00
clock_prescale_set ( clock_div_1 ) ;
2016-07-28 10:22:37 +02:00
# elif (ARCH == ARCH_XMEGA)
2016-08-30 16:48:55 +02:00
// Start the PLL to multiply the 2MHz RC oscillator to 32MHz and switch the CPU
// core to run from it
2016-08-17 19:47:39 +02:00
XMEGACLK_StartPLL ( CLOCK_SRC_INT_RC2MHZ , 2000000 , F_CPU ) ;
XMEGACLK_SetCPUClockSource ( CLOCK_SRC_PLL ) ;
2016-07-28 10:22:37 +02:00
2016-08-30 16:48:55 +02:00
// Start the 32MHz internal RC oscillator and start the DFLL to increase it to
// 48MHz using the USB SOF as a reference
2016-08-17 19:47:39 +02:00
XMEGACLK_StartInternalOscillator ( CLOCK_SRC_INT_RC32MHZ ) ;
XMEGACLK_StartDFLL ( CLOCK_SRC_INT_RC32MHZ , DFLL_REF_INT_USBSOF , F_USB ) ;
2016-07-28 10:22:37 +02:00
2016-08-17 19:47:39 +02:00
PMIC . CTRL = PMIC_LOLVLEN_bm | PMIC_MEDLVLEN_bm | PMIC_HILVLEN_bm ;
2016-07-28 10:22:37 +02:00
# endif
2016-08-30 16:48:55 +02:00
// Hardware Initialization */
USB_Init ( ) ;
}
2016-08-23 14:09:50 +02:00
2016-08-30 16:48:55 +02:00
//********************************************************************************//
2016-08-23 14:09:50 +02:00
2016-08-30 16:48:55 +02:00
void ProcessVirtualSerialData ( )
{
2016-08-23 14:09:50 +02:00
2016-08-30 16:48:55 +02:00
// 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 )
2016-08-23 14:09:50 +02:00
{
2016-08-30 16:48:55 +02:00
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 ) ;
}
2016-08-23 14:09:50 +02:00
}
2016-07-28 10:22:37 +02:00
}
2016-08-30 16:48:55 +02:00
//********************************************************************************//
2016-07-28 10:22:37 +02:00
2016-08-30 16:48:55 +02:00
void SendVirtualSerialData ( )
2016-07-28 10:22:37 +02:00
{
2016-08-30 16:48:55 +02:00
char * ReportString = NULL ;
2016-07-28 10:22:37 +02:00
2016-08-30 16:48:55 +02:00
// 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 ) ;
}
2016-07-28 10:22:37 +02:00
2016-08-30 16:48:55 +02:00
if ( ( ReportString ! = NULL ) )
{
// Write the string to the virtual COM port via the created character stream
USB_serialStreamWriteC ( ReportString , 6 ) ;
}
2016-07-28 10:22:37 +02:00
}
2016-08-30 16:48:55 +02:00
//********************************************************************************//
/** 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
2016-07-28 10:22:37 +02:00
*/
2016-08-30 16:48:55 +02:00
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 )
2016-07-28 10:22:37 +02:00
{
2016-08-17 19:47:39 +02:00
2016-08-30 16:48:55 +02:00
// Determine which interface must have its report generated */
if ( HIDInterfaceInfo = = & Keyboard_HID_Interface )
2016-08-17 19:47:39 +02:00
{
2016-08-30 16:48:55 +02:00
USB_KeyboardReport_Data_t * KeyboardReport = ( USB_KeyboardReport_Data_t * ) ReportData ;
2016-08-17 19:47:39 +02:00
2016-08-30 16:48:55 +02:00
// update the key matrix values
km_updateKeyStates ( ) ;
2016-08-17 19:47:39 +02:00
2016-08-30 16:48:55 +02:00
// 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 )
2016-08-17 19:47:39 +02:00
{
2016-08-30 16:48:55 +02:00
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 ] ) ;
}
}
2016-08-17 19:47:39 +02:00
}
2016-08-30 16:48:55 +02:00
}
2016-08-17 19:47:39 +02:00
2016-08-30 16:48:55 +02:00
// 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 ;
}
2016-07-28 10:22:37 +02:00
2016-08-30 16:48:55 +02:00
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 ;
}
2016-07-28 10:22:37 +02:00
2016-08-30 16:48:55 +02:00
// 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 ) ;
2016-07-28 10:22:37 +02:00
2016-08-30 16:48:55 +02:00
// reset the keystroke delay
keystroke_delay_cnt = keystroke_delay ;
2016-07-28 10:22:37 +02:00
2016-08-30 16:48:55 +02:00
// 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
}
}
2016-07-28 10:22:37 +02:00
2016-08-30 16:48:55 +02:00
// Some debug and test code
for ( uint8_t i = 0 ; i < LM_LED_CNT ; i + + )
2016-08-23 09:32:19 +02:00
{
2016-08-30 16:48:55 +02:00
if ( km_getKeyState ( i ) = = KEY_STATE_GO_DOWN )
lm_ledOn ( i ) ;
if ( km_getKeyState ( i ) = = KEY_STATE_GO_UP )
lm_ledOff ( i ) ;
2016-08-23 09:32:19 +02:00
}
2016-08-30 16:48:55 +02:00
* ReportSize = sizeof ( USB_KeyboardReport_Data_t ) ;
2016-07-28 10:22:37 +02:00
2016-08-30 16:48:55 +02:00
}
return false ;
2016-08-23 09:32:19 +02:00
}
2016-08-30 16:48:55 +02:00
//********************************************************************************//
2016-07-28 10:22:37 +02:00
2016-08-30 16:48:55 +02:00
/** 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 )
2016-08-17 19:47:39 +02:00
{
2016-08-30 16:48:55 +02:00
uint8_t * LEDReport = ( uint8_t * ) ReportData ;
2016-07-28 10:22:37 +02:00
2016-08-30 16:48:55 +02:00
/*
// TODO process the Keyboard LED status information
if ( * LEDReport & HID_KEYBOARD_LED_NUMLOCK )
2016-08-28 09:37:09 +02:00
2016-08-30 16:48:55 +02:00
if ( * LEDReport & HID_KEYBOARD_LED_CAPSLOCK )
2016-07-28 10:22:37 +02:00
2016-08-30 16:48:55 +02:00
if ( * LEDReport & HID_KEYBOARD_LED_SCROLLLOCK )
*/
2016-07-28 10:22:37 +02:00
2016-08-17 19:47:39 +02:00
}
2016-07-28 10:22:37 +02:00
}
2016-08-30 16:48:55 +02:00
//********************************************************************************//
/** 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
2016-07-28 10:22:37 +02:00
*/
2016-08-30 16:48:55 +02:00
void EVENT_CDC_Device_ControLineStateChanged ( USB_ClassInfo_CDC_Device_t * const CDCInterfaceInfo )
2016-07-28 10:22:37 +02:00
{
2016-08-30 16:48:55 +02:00
// 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.
2016-07-28 10:22:37 +02:00
2016-08-30 16:48:55 +02:00
bool HostReady = ( CDCInterfaceInfo - > State . ControlLineStates . HostToDevice & CDC_CONTROL_LINE_OUT_DTR ) ! = 0 ;
}
2016-07-28 10:22:37 +02:00
2016-08-30 16:48:55 +02:00
//********************************************************************************//
2016-07-28 10:22:37 +02:00
2016-08-30 16:48:55 +02:00
/** Event handler for the library USB Connection event. */
void EVENT_USB_Device_Connect ( void )
{
SET_GLOB_USB_STATUS ( STATUSMASK_USB_ENUMERATING ) ;
}
2016-07-28 10:22:37 +02:00
2016-08-30 16:48:55 +02:00
/** Event handler for the library USB Disconnection event. */
void EVENT_USB_Device_Disconnect ( void )
{
SET_GLOB_USB_STATUS ( STATUSMASK_USB_NOTREADY ) ;
}
2016-07-28 10:22:37 +02:00
2016-08-30 16:48:55 +02:00
/** Event handler for the library USB Configuration Changed event. */
void EVENT_USB_Device_ConfigurationChanged ( void )
{
bool ConfigSuccess = true ;
2016-08-23 14:09:50 +02:00
2016-08-30 16:48:55 +02:00
ConfigSuccess & = HID_Device_ConfigureEndpoints ( & Keyboard_HID_Interface ) ;
ConfigSuccess & = CDC_Device_ConfigureEndpoints ( & VirtualSerial_CDC_Interface ) ;
2016-07-28 10:22:37 +02:00
2016-08-30 16:48:55 +02:00
USB_Device_EnableSOFEvents ( ) ;
2016-08-23 14:09:50 +02:00
2016-08-30 16:48:55 +02:00
SET_GLOB_USB_STATUS ( ConfigSuccess ? STATUSMASK_USB_READY : STATUSMASK_USB_ERROR ) ;
}
2016-07-28 10:22:37 +02:00
2016-08-30 16:48:55 +02:00
/** Event handler for the library USB Control Request reception event. */
void EVENT_USB_Device_ControlRequest ( void )
{
HID_Device_ProcessControlRequest ( & Keyboard_HID_Interface ) ;
CDC_Device_ProcessControlRequest ( & VirtualSerial_CDC_Interface ) ;
}
2016-07-28 10:22:37 +02:00
2016-08-30 16:48:55 +02:00
/** Event handler for the USB device Start Of Frame event. */
void EVENT_USB_Device_StartOfFrame ( void )
{
HID_Device_MillisecondElapsed ( & Keyboard_HID_Interface ) ;
}
2016-07-28 10:22:37 +02:00
2016-08-30 16:48:55 +02:00
//********************************************************************************//
2016-07-28 10:22:37 +02:00
2016-08-30 16:48:55 +02:00
void USB_serialStreamWriteC ( char * msg , uint16_t len )
{
for ( uint8_t i = 0 ; i < len ; i + + )
{
fputc ( msg [ i ] , & USBSerialStream ) ;
2016-08-17 16:00:51 +02:00
}
2016-07-28 10:22:37 +02:00
}
2016-08-17 16:00:51 +02:00
2016-08-30 16:48:55 +02:00
void USB_serialStreamWrite ( char * msg )
{
fputs ( msg , & USBSerialStream ) ;
}
//********************************************************************************//