2016-01-18 16:15:23 +01:00
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*
*/
2016-05-05 23:47:50 +02:00
/** library for USB CDC ACM communication (code)
2016-03-24 10:37:42 +01:00
* @ file usb_cdcacm . c
* @ author King Kévin < kingkevin @ cuvoodoo . info >
* @ date 2016
*/
2016-01-18 16:15:23 +01:00
/* standard libraries */
# include <stdint.h> // standard integer types
# include <stdio.h> // standard I/O facilities
# include <stdlib.h> // general utilities
/* STM32 (including CM3) libraries */
# include <libopencm3/stm32/rcc.h> // real-time control clock library
# include <libopencm3/stm32/gpio.h> // general purpose input output library
# include <libopencm3/cm3/nvic.h> // interrupt handler
2016-01-28 21:21:50 +01:00
# include <libopencm3/cm3/scb.h> // reset utilities
2016-01-18 16:15:23 +01:00
# include <libopencmsis/core_cm3.h> // Cortex M3 utilities
# include <libopencm3/usb/usbd.h> // USB library
# include <libopencm3/usb/cdc.h> // USB CDC library
2016-04-14 23:40:38 +02:00
# include <libopencm3/cm3/sync.h> // synchronisation utilities
2016-01-18 16:15:23 +01:00
2016-04-15 11:59:14 +02:00
# include "global.h" // global utilities
2016-01-18 16:15:23 +01:00
# include "usb_cdcacm.h" // USB CDC ACM header and definitions
2016-05-05 23:47:50 +02:00
/** USB CDC ACM device descriptor
2016-03-24 10:37:42 +01:00
* @ note as defined in USB CDC specification section 5
2016-01-28 21:21:50 +01:00
*/
2016-01-18 16:15:23 +01:00
static const struct usb_device_descriptor device_descriptor = {
. bLength = USB_DT_DEVICE_SIZE , // the size of this header in bytes, 18
. bDescriptorType = USB_DT_DEVICE , // a value of 1 indicates that this is a device descriptor
. bcdUSB = 0x0200 , // this device supports USB 2.0
. bDeviceClass = USB_CLASS_CDC , // use the CDC device class
2016-01-28 21:21:50 +01:00
. bDeviceSubClass = 0 , // unused
. bDeviceProtocol = 0 , // unused
2016-01-18 16:15:23 +01:00
. bMaxPacketSize0 = 64 , // packet size for endpoint zero in bytes
. idVendor = 0xc440 , // Vendor ID (CuVo...)
. idProduct = 0x0d00 , // product ID within the Vendor ID space (...odoo)
. bcdDevice = 0x0100 , // version number for the device
. iManufacturer = 1 , // the index of the string in the string table that represents the name of the manufacturer of this device.
. iProduct = 2 , // the index of the string in the string table that represents the name of the product
. iSerialNumber = 3 , // the index of the string in the string table that represents the serial number of this item in string form.
. bNumConfigurations = 1 , // the number of possible configurations this device has
} ;
2016-05-05 23:47:50 +02:00
/** USB CDC ACM data endpoints
2016-03-24 10:37:42 +01:00
* @ note as defined in USB CDC specification section 5
*/
2016-01-18 16:15:23 +01:00
static const struct usb_endpoint_descriptor data_endpoints [ ] = { {
. bLength = USB_DT_ENDPOINT_SIZE , // the size of the endpoint descriptor in bytes
. bDescriptorType = USB_DT_ENDPOINT , // a value of 5 indicates that this describes an endpoint
. bEndpointAddress = 0x01 , // OUT (from host) direction (0<<7), endpoint 1
. bmAttributes = USB_ENDPOINT_ATTR_BULK , // bulk mode
. wMaxPacketSize = 64 , // maximum packet size
. bInterval = 1 , // the frequency, in number of frames, that we're going to be sending data
} , {
. bLength = USB_DT_ENDPOINT_SIZE , // the size of the endpoint descriptor in bytes
. bDescriptorType = USB_DT_ENDPOINT , // a value of 5 indicates that this describes an endpoint
. bEndpointAddress = 0x82 , // IN (to host) direction (1<<7), endpoint 2
. bmAttributes = USB_ENDPOINT_ATTR_BULK , // bulk mode
. wMaxPacketSize = 64 , // maximum packet size
. bInterval = 1 , // the frequency, in number of frames, that we're going to be sending data
} } ;
2016-05-05 23:47:50 +02:00
/** USB CDC ACM communication endpoints
2016-03-24 10:37:42 +01:00
* @ note This notification endpoint isn ' t implemented . According to CDC spec its optional , but its absence causes a NULL pointer dereference in Linux cdc_acm driver
2016-01-18 16:15:23 +01:00
*/
static const struct usb_endpoint_descriptor communication_endpoints [ ] = { {
. bLength = USB_DT_ENDPOINT_SIZE , // the size of the endpoint descriptor in bytes
. bDescriptorType = USB_DT_ENDPOINT , // a value of 5 indicates that this describes an endpoint
. bEndpointAddress = 0x83 , // IN (to host) direction (1<<7), endpoint 3
. bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT , // interrupt mode
. wMaxPacketSize = 16 , // maximum packet size
. bInterval = 255 , // the frequency, in number of frames, that we're going to be sending data
} } ;
2016-05-05 23:47:50 +02:00
/** USB CDC ACM functional descriptor
2016-03-25 11:42:41 +01:00
* @ return
2016-03-24 10:37:42 +01:00
* @ note as defined in USB CDC specification section 5.2 .3
2016-01-28 21:21:50 +01:00
*/
2016-01-18 16:15:23 +01:00
static const struct {
struct usb_cdc_header_descriptor header ;
struct usb_cdc_call_management_descriptor call_mgmt ;
struct usb_cdc_acm_descriptor acm ;
struct usb_cdc_union_descriptor cdc_union ;
} __attribute__ ( ( packed ) ) cdcacm_functional_descriptors = {
. header = {
. bFunctionLength = sizeof ( struct usb_cdc_header_descriptor ) ,
. bDescriptorType = CS_INTERFACE ,
. bDescriptorSubtype = USB_CDC_TYPE_HEADER ,
. bcdCDC = 0x0110 ,
} ,
. call_mgmt = {
2016-01-28 21:21:50 +01:00
. bFunctionLength = sizeof ( struct usb_cdc_call_management_descriptor ) ,
2016-01-18 16:15:23 +01:00
. bDescriptorType = CS_INTERFACE ,
. bDescriptorSubtype = USB_CDC_TYPE_CALL_MANAGEMENT ,
. bmCapabilities = 0 ,
. bDataInterface = 1 ,
} ,
. acm = {
. bFunctionLength = sizeof ( struct usb_cdc_acm_descriptor ) ,
. bDescriptorType = CS_INTERFACE ,
. bDescriptorSubtype = USB_CDC_TYPE_ACM ,
. bmCapabilities = 0 ,
} ,
. cdc_union = {
. bFunctionLength = sizeof ( struct usb_cdc_union_descriptor ) ,
. bDescriptorType = CS_INTERFACE ,
. bDescriptorSubtype = USB_CDC_TYPE_UNION ,
. bControlInterface = 0 ,
. bSubordinateInterface0 = 1 ,
} ,
} ;
2016-05-05 23:47:50 +02:00
/** USB CDC interface descriptor
2016-03-24 10:37:42 +01:00
* @ note as defined in USB CDC specification section 5.1 .3
2016-01-28 21:21:50 +01:00
*/
2016-01-18 16:15:23 +01:00
static const struct usb_interface_descriptor communication_interface [ ] = { {
. bLength = USB_DT_INTERFACE_SIZE ,
. bDescriptorType = USB_DT_INTERFACE ,
. bInterfaceNumber = 0 ,
. bAlternateSetting = 0 ,
. bNumEndpoints = 1 ,
. bInterfaceClass = USB_CLASS_CDC ,
. bInterfaceSubClass = USB_CDC_SUBCLASS_ACM ,
. bInterfaceProtocol = USB_CDC_PROTOCOL_NONE ,
. iInterface = 0 ,
. endpoint = communication_endpoints ,
. extra = & cdcacm_functional_descriptors ,
. extralen = sizeof ( cdcacm_functional_descriptors ) ,
} } ;
2016-05-05 23:47:50 +02:00
/** USB CDC ACM data class interface descriptor
2016-03-24 10:37:42 +01:00
* @ note as defined in USB CDC specification section 5.1 .3
2016-01-28 21:21:50 +01:00
*/
2016-01-18 16:15:23 +01:00
static const struct usb_interface_descriptor data_interface [ ] = { {
. bLength = USB_DT_INTERFACE_SIZE ,
. bDescriptorType = USB_DT_INTERFACE ,
. bInterfaceNumber = 1 ,
. bAlternateSetting = 0 ,
. bNumEndpoints = 2 ,
. bInterfaceClass = USB_CLASS_DATA ,
. bInterfaceSubClass = 0 ,
. bInterfaceProtocol = 0 ,
. iInterface = 0 ,
. endpoint = data_endpoints ,
} } ;
2016-05-05 23:47:50 +02:00
/** USB CDC ACM interface descriptor */
2016-01-18 16:15:23 +01:00
static const struct usb_interface interfaces [ ] = { {
. num_altsetting = 1 ,
. altsetting = communication_interface ,
} , {
. num_altsetting = 1 ,
. altsetting = data_interface ,
} } ;
2016-05-05 23:47:50 +02:00
/** USB CDC ACM configuration descriptor */
2016-01-18 16:15:23 +01:00
static const struct usb_config_descriptor config = {
. bLength = USB_DT_CONFIGURATION_SIZE , // the length of this header in bytes
. bDescriptorType = USB_DT_CONFIGURATION , // a value of 2 indicates that this is a configuration descriptor
. wTotalLength = 0 , // this should hold the total size of the configuration descriptor including all sub interfaces. it is automatically filled in by the USB stack in libopencm3
. bNumInterfaces = 2 , // the number of interfaces in this configuration
. bConfigurationValue = 1 , // the index of this configuration
. iConfiguration = 0 , // a string index describing this configuration (zero means not provided)
2016-01-28 21:21:50 +01:00
. bmAttributes = 0x80 , // self powered (0<<6), supports remote wakeup (0<<5)
2016-01-18 16:15:23 +01:00
. bMaxPower = 0x32 , // the maximum amount of current that this device will draw in 2mA units
// end of header
. interface = interfaces , // pointer to an array of interfaces
} ;
2016-05-05 23:47:50 +02:00
/** USB string table
2016-03-24 10:37:42 +01:00
* @ note starting with index 1
*/
static const char * usb_strings [ ] = {
2016-01-18 16:15:23 +01:00
" CuVoodoo " ,
" CDC-ACM " ,
" STM32F1 " ,
} ;
2016-04-14 23:40:38 +02:00
static uint8_t usbd_control_buffer [ 128 ] = { 0 } ; /**< buffer to be used for control requests */
static usbd_device * usb_device = NULL ; /**< structure holding all the info related to the USB device */
static bool connected = false ; /**< is the USB device is connected to a host */
2016-01-18 16:15:23 +01:00
/* input and output ring buffer, indexes, and available memory */
2016-03-25 11:42:41 +01:00
static uint8_t rx_buffer [ CDCACM_BUFFER ] = { 0 } ; /**< ring buffer for received data */
static volatile uint8_t rx_i = 0 ; /**< current position of read received data */
static volatile uint8_t rx_used = 0 ; /**< how much data has been received and not red */
2016-04-14 23:40:38 +02:00
mutex_t rx_lock = MUTEX_UNLOCKED ; /**< lock to update rx_i or rx_used */
2016-03-25 11:42:41 +01:00
static uint8_t tx_buffer [ CDCACM_BUFFER ] = { 0 } ; /**< ring buffer for data to transmit */
static volatile uint8_t tx_i = 0 ; /**< current position if transmitted data */
static volatile uint8_t tx_used = 0 ; /**< how much data needs to be transmitted */
2016-04-14 23:40:38 +02:00
mutex_t tx_lock = MUTEX_UNLOCKED ; /**< lock to update tx_i or tx_used */
2016-01-18 16:15:23 +01:00
volatile uint8_t cdcacm_received = 0 ; // same as rx_used, but since the user can write this variable we don't rely on it
2016-05-05 23:47:50 +02:00
/** disconnect USB by pulling down D+ to for re-enumerate */
2016-02-18 10:39:08 +01:00
static void usb_disconnect ( void )
{
/* short USB disconnect to force re-enumerate */
# if defined(SYSTEM_BOARD) || defined(BLUE_PILL)
// pull USB D+ low for a short while
rcc_periph_clock_enable ( RCC_GPIOA ) ;
gpio_set_mode ( GPIOA , GPIO_MODE_OUTPUT_2_MHZ , GPIO_CNF_OUTPUT_PUSHPULL , GPIO12 ) ;
gpio_clear ( GPIOA , GPIO12 ) ;
for ( uint32_t i = 0 ; i < 0x2000 ; i + + ) {
__asm__ ( " nop " ) ;
}
# elif defined(MAPLE_MINI)
// disconnect USB D+ using dedicated DISC line/circuit on PB9
rcc_periph_clock_enable ( RCC_GPIOB ) ;
gpio_set_mode ( GPIOB , GPIO_MODE_OUTPUT_2_MHZ , GPIO_CNF_OUTPUT_PUSHPULL , GPIO9 ) ;
gpio_set ( GPIOB , GPIO9 ) ;
for ( uint32_t i = 0 ; i < 0x2000 ; i + + ) {
__asm__ ( " nop " ) ;
}
gpio_clear ( GPIOB , GPIO9 ) ;
# endif
}
2016-05-05 23:47:50 +02:00
/** incoming USB CDC ACM control request
2016-03-25 11:42:41 +01:00
* @ param [ in ] usbd_dev USB device descriptor
* @ param [ in ] req control request information
* @ param [ in ] buf control request data
* @ param [ in ] len control request data length
* @ param [ in ] complete not used
* @ return 0 if succeeded , error else
2016-03-24 10:37:42 +01:00
* @ note resets device when configured with 5 bits
*/
2016-01-18 16:15:23 +01:00
static int cdcacm_control_request ( usbd_device * usbd_dev , struct usb_setup_data * req , uint8_t * * buf , uint16_t * len , void ( * * complete ) ( usbd_device * usbd_dev , struct usb_setup_data * req ) )
{
( void ) complete ;
( void ) buf ;
( void ) usbd_dev ;
switch ( req - > bRequest ) {
2016-04-14 23:40:38 +02:00
case USB_CDC_REQ_SET_CONTROL_LINE_STATE :
connected = req - > wValue ? true : false ; // check if terminal is open
//bool dtr = (req->wValue & (1 << 0)) ? true : false;
//bool rts = (req->wValue & (1 << 1)) ? true : false;
2016-01-28 21:21:50 +01:00
/* this Linux cdc_acm driver requires this to be implemented
* even though it ' s optional in the CDC spec , and we don ' t
* advertise it in the ACM functional descriptor .
*/
2016-04-15 11:59:14 +02:00
uint8_t reply [ 10 ] = { 0 } ;
struct usb_cdc_notification * notif = ( void * ) reply ;
2016-04-14 23:40:38 +02:00
/* we echo signals back to host as notification. */
notif - > bmRequestType = 0xA1 ;
notif - > bNotification = USB_CDC_NOTIFY_SERIAL_STATE ;
notif - > wValue = 0 ;
notif - > wIndex = 0 ;
notif - > wLength = 2 ;
2016-04-15 11:59:14 +02:00
reply [ 8 ] = req - > wValue & 3 ;
reply [ 9 ] = 0 ;
usbd_ep_write_packet ( usbd_dev , 0x83 , reply , LENGTH ( reply ) ) ;
2016-04-14 23:40:38 +02:00
break ;
2016-01-28 21:21:50 +01:00
case USB_CDC_REQ_SET_LINE_CODING :
// ignore if length is wrong
if ( * len < sizeof ( struct usb_cdc_line_coding ) ) {
return 0 ;
}
// get the line coding
struct usb_cdc_line_coding * coding = ( struct usb_cdc_line_coding * ) * buf ;
/* reset device is the data bits is set to 5
* this is used to allowing rebooting the device in DFU mode for reflashing
* to reset the device from the host you can use stty - - file / dev / ttyACM0 115200 raw cs5
*/
if ( coding - > bDataBits = = 5 ) {
2016-02-18 10:39:08 +01:00
usb_disconnect ( ) ; // force re-enumerate after reset
2016-01-28 21:21:50 +01:00
scb_reset_system ( ) ; // reset device
while ( true ) ; // wait for the reset to happen
}
2016-04-14 23:40:38 +02:00
break ;
2016-01-28 21:21:50 +01:00
default :
2016-01-18 16:15:23 +01:00
return 0 ;
}
2016-04-14 23:40:38 +02:00
return 1 ;
2016-01-18 16:15:23 +01:00
}
2016-05-05 23:47:50 +02:00
/** USB CDC ACM data received callback
2016-03-25 11:42:41 +01:00
* @ param [ in ] usbd_dev USB device descriptor
* @ param [ in ] ep endpoint where data came in
*/
2016-01-18 16:15:23 +01:00
static void cdcacm_data_rx_cb ( usbd_device * usbd_dev , uint8_t ep )
{
( void ) ep ;
char usb_data [ 64 ] = { 0 } ; // buffer to read data
uint16_t usb_length = 0 ; // length of incoming data
/* receive data */
usb_length = usbd_ep_read_packet ( usbd_dev , 0x01 , usb_data , sizeof ( usb_data ) ) ;
if ( usb_length ) { // copy received data
2016-04-15 11:59:14 +02:00
for ( uint16_t i = 0 ; i < usb_length & & rx_used < LENGTH ( rx_buffer ) ; i + + ) { // only until buffer is full
rx_buffer [ ( rx_i + rx_used ) % LENGTH ( rx_buffer ) ] = usb_data [ i ] ; // put character in buffer
2016-01-18 16:15:23 +01:00
rx_used + + ; // update used buffer
}
cdcacm_received = rx_used ; // update available data
}
}
2016-05-05 23:47:50 +02:00
/** USB CDC ACM data transmitted callback
2016-03-25 11:42:41 +01:00
* @ param [ in ] usbd_dev USB device descriptor
* @ param [ in ] ep endpoint where data came in
*/
2016-01-28 21:21:50 +01:00
static void cdcacm_data_tx_cb ( usbd_device * usbd_dev , uint8_t ep )
{
( void ) ep ;
( void ) usbd_dev ;
2016-04-14 23:40:38 +02:00
if ( ! usbd_dev | | ! connected | | ! tx_used ) { // verify if we can send and there is something to send
return ;
}
if ( mutex_trylock ( & tx_lock ) ) { // try to get lock
uint8_t usb_length = ( tx_used > 64 ? 64 : tx_used ) ; // length of data to be transmitted (respect max packet size)
2016-04-15 11:59:14 +02:00
usb_length = ( usb_length > ( LENGTH ( tx_buffer ) - tx_i ) ? LENGTH ( tx_buffer ) - tx_i : usb_length ) ; // since here we use the source array not as ring buffer, only go up to the end
2016-04-14 23:40:38 +02:00
while ( usb_length ! = usbd_ep_write_packet ( usb_device , 0x82 , ( void * ) ( & tx_buffer [ tx_i ] ) , usb_length ) ) ; // ensure data is written into transmit buffer
2016-04-15 11:59:14 +02:00
tx_i = ( tx_i + usb_length ) % LENGTH ( tx_buffer ) ; // update location on buffer
2016-04-14 23:40:38 +02:00
tx_used - = usb_length ; // update used size
mutex_unlock ( & tx_lock ) ; // release lock
} else {
usbd_ep_write_packet ( usb_device , 0x82 , NULL , 0 ) ; // trigger empty tx for a later callback
2016-01-28 21:21:50 +01:00
}
2016-04-14 23:40:38 +02:00
usbd_poll ( usb_device ) ; // ensure the data gets sent
2016-01-28 21:21:50 +01:00
}
2016-05-05 23:47:50 +02:00
/** set USB CDC ACM configuration
2016-03-25 11:42:41 +01:00
* @ param [ in ] usbd_dev USB device descriptor
* @ param [ in ] wValue not used
*/
2016-01-18 16:15:23 +01:00
static void cdcacm_set_config ( usbd_device * usbd_dev , uint16_t wValue )
{
( void ) wValue ;
usbd_ep_setup ( usbd_dev , 0x01 , USB_ENDPOINT_ATTR_BULK , 64 , cdcacm_data_rx_cb ) ;
2016-01-28 21:21:50 +01:00
usbd_ep_setup ( usbd_dev , 0x82 , USB_ENDPOINT_ATTR_BULK , 64 , cdcacm_data_tx_cb ) ;
2016-01-18 16:15:23 +01:00
usbd_ep_setup ( usbd_dev , 0x83 , USB_ENDPOINT_ATTR_INTERRUPT , 16 , NULL ) ;
usbd_register_control_callback ( usbd_dev , USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE , USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT , cdcacm_control_request ) ;
}
2016-02-18 10:39:08 +01:00
2016-01-18 16:15:23 +01:00
void cdcacm_setup ( void )
{
2016-04-14 23:40:38 +02:00
connected = false ; // start with USB not connected
2016-04-12 09:25:04 +02:00
usb_disconnect ( ) ; // force re-enumerate (useful after a restart or if there is a bootloader using another USB profile)
2016-02-18 10:39:08 +01:00
2016-01-18 16:15:23 +01:00
/* initialize USB */
usb_device = usbd_init ( & st_usbfs_v1_usb_driver , & device_descriptor , & config , usb_strings , 3 , usbd_control_buffer , sizeof ( usbd_control_buffer ) ) ;
usbd_register_set_config_callback ( usb_device , cdcacm_set_config ) ;
/* enable interrupts (to not have to poll all the time) */
2016-04-14 23:40:38 +02:00
nvic_enable_irq ( NVIC_USB_LP_CAN_RX0_IRQ ) ; // without this USB isn't detected by the host
2016-01-18 16:15:23 +01:00
/* reset buffer states */
rx_i = 0 ;
rx_used = 0 ;
2016-04-14 23:40:38 +02:00
mutex_unlock ( & rx_lock ) ;
2016-01-18 16:15:23 +01:00
cdcacm_received = 0 ;
2016-04-14 23:40:38 +02:00
tx_i = 0 ;
tx_used = 0 ;
mutex_unlock ( & tx_lock ) ;
2016-01-18 16:15:23 +01:00
}
char cdcacm_getchar ( void )
{
while ( ! rx_used ) { // idle until data is available
2016-02-18 10:39:08 +01:00
__WFI ( ) ; // sleep until interrupt (not sure if it's a good idea here)
2016-01-18 16:15:23 +01:00
}
char to_return = rx_buffer [ rx_i ] ; // get the next available character
2016-04-15 11:59:14 +02:00
rx_i = ( rx_i + 1 ) % LENGTH ( rx_buffer ) ; // update used buffer
2016-01-18 16:15:23 +01:00
rx_used - - ; // update used buffer
cdcacm_received = rx_used ; // update available data
return to_return ;
}
void cdcacm_putchar ( char c )
{
2016-04-14 23:40:38 +02:00
if ( ! usb_device | | ! connected ) {
return ;
}
mutex_lock ( & tx_lock ) ; // get lock to prevent race condition
2016-04-15 11:59:14 +02:00
if ( tx_used < LENGTH ( tx_buffer ) ) { // buffer not full
tx_buffer [ ( tx_i + tx_used ) % LENGTH ( tx_buffer ) ] = c ; // put character in buffer
2016-01-28 21:21:50 +01:00
tx_used + + ; // update used buffer
2016-04-14 23:40:38 +02:00
} else { // buffer full (might be that no terminal is connected to this serial)
2016-04-15 11:59:14 +02:00
tx_i = ( tx_i + 1 ) % LENGTH ( tx_buffer ) ; // shift start
tx_buffer [ ( tx_i + tx_used ) % LENGTH ( tx_buffer ) ] = c ; // overwrite old data
2016-01-28 21:21:50 +01:00
}
2016-04-14 23:40:38 +02:00
mutex_unlock ( & tx_lock ) ; // release lock
2016-04-17 12:11:12 +02:00
if ( tx_used = = 1 ) { // to buffer is not empty anymore
usbd_ep_write_packet ( usb_device , 0x82 , NULL , 0 ) ; // trigger tx callback
}
2016-01-18 16:15:23 +01:00
}
2016-05-05 23:47:50 +02:00
/** USB interrupt service routine called when data is received */
2016-01-18 16:15:23 +01:00
void usb_lp_can_rx0_isr ( void ) {
2016-04-14 23:40:38 +02:00
usbd_poll ( usb_device ) ;
2016-01-18 16:15:23 +01:00
}