# usb_serial

USB Serial

Author | Albert Faber, Copyright © 2009..2021, all rights reserved.
Adapted-by | Rob Jansen
Compiler | 2.5r5

Description

    The library will ease the use of for serial communication over
    the USB interface. By default nothing has to be defined by the user, below
    an example how to use the library

    include usb_serial
    ..
    usb_serial_init()
    ..
    ;-- optionally wait till USB becomes available
    while ( usb_cdc_line_status() ==  0x00 )  loop
    end loop
    ..
    ;-- main loop
    var byte ch
    forever loop
    ;   -- Call the flush routine on a regular base in the main loop
    ;   -- in order to keep the USB communicaiton alive.
    ;   -- This is only needed when USB_INTERRUPT_DRIVEN is not defined, otherwise
    ;   -- the usb_serial_flush() is not needed, see notes.
    ;
        usb_serial_flush()
        if ( usb_serial_read( ch ) )
            usb_serial_data = ch -- echo
         end if
           .....
    end loop

    In addition this library can be used in combination with other JAL
    libraries, like print.jal and format.jal, for example to send a
    string, one can use the following code fragment:

    const byte str[] = "Hello World"
    print_string( usb_serial_data, str )

    customization
    The usb_serial library offers the following custimization prameters,
    the user has the ability to set these const parameter(s) before the
    inclusion of the usb_serial file

    const byte USB_CDC_RX_BUFFER_SIZE = 0x??  -- set receive FIFO size
    const byte USB_CDC_TX_BUFFER_SIZE = 0x??  -- set transmit FIFO size
    const word USB_SERIAL_PRODUCT_ID = 0x???? -- set USB device product ID
    const word USB_SERIAL_VENDOR_ID = 0x????  -- set USB device vendor ID
    const byte USB_STRING0[] = { .. }         -- set USB language string
    const byte USB_STRING1[] = { .. }         -- set USB mfg string
    const byte USB_STRING2[] = { .. }         -- set USB product string

Notes

    There are two options to use the USB driver for serial:
           1) Without interrupts by frequently calling 'usb_serial_flush()'
              frequently in the main loop.
           2) Using interrupts. In that case no calls to 'usb_serial_flush()' are
              needed. This can be activated by defining
                 const USB_INTERRUPT_DRIVEN = TRUE

Dependencies

- usb_drv
- usb_defs
- usb_drv_cdc_class
- usb_drv_core

## Summary

### Global variables/contants

- `const byte USB_EP3_OUT_SIZE = 8`
- `const byte USB_DSC_FN_RPT_CAPABILITIES = 0x05`
- `const byte USB_CONFIGURATION_DESCRIPTOR[ USB_CONFIGURATION_DESCRIPTOR_SIZE ]=`
- `const byte USB_DSC_FN_UNION = 0x06`
- `const byte USB_CS_INTERFACE = 0x24`
- `const byte USB_DSC_FN_CALL_MGT = 0x01`
- `const byte USB_DCS_INTERFACE = 0x04`
- `const byte USB_DEVICE_DESCRIPTOR[USB_DEVICE_DESCRIPTOR_SIZE] = {`
- `const word USB_EP3_IN_ADDR = ( USB_EP3_OUT_ADDR + USB_EP3_OUT_SIZE )`
- `var volatile byte usb_cdc_eptx_buffer[USB_EP3_IN_SIZE] at USB_EP3_IN_ADDR`
- `const byte USB_CDC_RX_BUFFER_SIZE = 0x10`
- `const byte USB_CDC_DATA_INTF = 0x0A`
- `var volatile byte usb_ep2out_buf[ USB_EP2_OUT_SIZE ] at USB_EP2_OUT_ADDR`
- `const byte USB_CDC_DATA_ENDPOINT = 3`
- `const bit USB_EP0 = 1`
- `const byte USB_STRING0[] =`
- `var volatile byte usb_ep3in_buf[ USB_EP3_IN_SIZE ] at USB_EP3_IN_ADDR`
- `const byte USB_STRING1[0x36] =`
- `const byte USB_EP3_IN_SIZE = 8`
- `const byte USB_DSC_FN_COUNTRY_SELECTION = 0x07`
- `const byte USB_CDC_INT_EP_SIZE = 8`
- `const byte USB_EP0_IN_SIZE = 8`
- `const USB_CONFIGURATION_DESCRIPTOR_SIZE = 0x09 + 0x09 + 0x05 + 0x04 + 0x05 +`
- `const word USB_SERIAL_VENDOR_ID = 0x04D8`
- `const byte USB_STRING2[30] =`
- `var volatile byte usb_ep0out_buf[ USB_EP0_OUT_SIZE ] at USB_EP0_OUT_ADDR`
- `const byte USB_CDC_BULK_OUT_EP_SIZE = USB_EP3_OUT_SIZE`
- `var volatile byte usb_ep1in_buf[ USB_EP1_IN_SIZE ] at USB_EP1_IN_ADDR`
- `const word USB_EP2_OUT_ADDR = ( USB_EP1_IN_ADDR + USB_EP1_IN_SIZE )`
- `const word USB_EP3_OUT_ADDR = ( USB_EP2_IN_ADDR + USB_EP2_IN_SIZE )`
- `const byte USB_CDC_COMM_INTF_ID = 0x00`
- `const bit USB_EP1 = 1`
- `var volatile byte usb_ep3out_buf[ USB_EP3_OUT_SIZE ] at USB_EP3_OUT_ADDR`
- `const word USB_CDC_BULK_IN_EP_ADDR = USB_EP3_IN_ADDR`
- `const byte USB_DSC_FN_USB_TERMINAL = 0x09`
- `const word USB_SERIAL_PRODUCT_ID = 0x000A`
- `const byte USB_DSC_FN_TELEPHONE_RINGER = 0x04`
- `const byte USB_EP0_OUT_SIZE = 8`
- `const word USB_EP0_OUT_ADDR = ( USB_EP0_ADDRESS )`
- `const byte USB_EP1_IN_SIZE = 8`
- `const byte USB_CS_ENDPOINT = 0x25`
- `const byte USB_DSC_FN_TEL_OP_MODES = 0x08`
- `var volatile byte usb_ep1out_buf[ USB_EP1_OUT_SIZE ] at USB_EP1_OUT_ADDR`
- `const bit USB_EP2 = 1`
- `const byte USB_EP2_IN_SIZE = 8`
- `const byte USB_CDC_BULK_IN_EP_SIZE = USB_EP3_IN_SIZE`
- `const word USB_EP1_OUT_ADDR = ( USB_EP0_IN_ADDR + USB_EP0_IN_SIZE )`
- `const byte USB_EP1_OUT_SIZE = 8`
- `const bit USB_EP3 = 1`
- `const byte USB_EP2_OUT_SIZE = 8`
- `const word USB_EP2_IN_ADDR = ( USB_EP2_OUT_ADDR + USB_EP2_OUT_SIZE )`
- `const word USB_EP1_IN_ADDR = ( USB_EP1_OUT_ADDR + USB_EP1_OUT_SIZE )`
- `const byte USB_DSC_FN_ACM = 0x02`
- `var volatile byte usb_cdc_eprx_buffer[USB_EP3_OUT_SIZE] at USB_EP3_OUT_ADDR`
- `var volatile byte usb_ep2in_buf[ USB_EP2_IN_SIZE ] at USB_EP2_IN_ADDR`
- `const word USB_EP0_IN_ADDR = ( USB_EP0_OUT_ADDR + USB_EP0_OUT_SIZE )`
- `const word USB_CDC_BULK_OUT_EP_ADDR = USB_EP3_OUT_ADDR`
- `var volatile byte usb_ep0in_buf[ USB_EP0_IN_SIZE ] at USB_EP0_IN_ADDR`
- `const byte USB_DSC_FN_HEADER = 0x00`
- `const byte USB_CDC_NO_PROTOCOL = 0x00`
- `const byte USB_CDC_TX_BUFFER_SIZE = 0x20`
- `const byte USB_DSC_FN_DLM = 0x03`
- `const byte USB_CDC_DATA_INTF_ID = 0x01`

### Procedures

- `usb_serial_data'put(byte in data)`
- `usb_serial_init()`
- `usb_serial_flush()`
- `usb_serial_write(byte in data)`

### Functions

- `usb_serial_data'get() return byte`
- `usb_serial_data_available'get() return bit`
- `usb_serial_port_open'get() return bit`
- `usb_serial_read(byte out data) return bit`

Private

- `_usb_serial_read(byte out data) return bit`

## API details

### Global variables/contants

- `const byte USB_EP3_OUT_SIZE = 8`

  No documentation found

- `const byte USB_DSC_FN_RPT_CAPABILITIES = 0x05`

  No documentation found

- `const byte USB_CONFIGURATION_DESCRIPTOR[ USB_CONFIGURATION_DESCRIPTOR_SIZE ]=`

      Sets the default USB_CONFIGURATION_DESCRIPTOR record. It is not expected
      that the user of the usb_serial library needs to create its own
      record. However, if needed in exeptional cases, the user has the
      ability to define the const byte USB_CONFIGURATION_DESCRIPTOR[]
      before inclusion of the usb_serial library to customize the USB
      conifguration

- `const byte USB_DSC_FN_UNION = 0x06`

  No documentation found

- `const byte USB_CS_INTERFACE = 0x24`

      CDC class Functional Descriptors related constants
      Type Values for the bDscType Field (see  USB specification

- `const byte USB_DSC_FN_CALL_MGT = 0x01`

  No documentation found

- `const byte USB_DCS_INTERFACE = 0x04`

  No documentation found

- `const byte USB_DEVICE_DESCRIPTOR[USB_DEVICE_DESCRIPTOR_SIZE] = {`

      Sets the default USB_DEVICE_DESCRIPTOR record. It is not expected
      that the user of the usb_serial library needs to create its own
      record. However, if needed in exeptional cases, the user has the
      ability to define the
      const byte USB_DEVICE_DESCRIPTOR[USB_DEVICE_DESCRIPTOR_SIZE]
      before inclusion of the usb_serial library

- `const word USB_EP3_IN_ADDR = ( USB_EP3_OUT_ADDR + USB_EP3_OUT_SIZE )`

      RJ: The above buffer to send data to the host is increased.
      This still fits sinds it is at the end of the usb_epxoutbuf.
      Since we need 48 bytes for EP0 to EP2 and 8 bytes for EP3 OUT
      so in total 56 bytes and the usb_epxoutbuf is 80 bytes max,
      keeping the PIC16 in mind which can only have 80 bytes buffers,
      we still have 24 bytes free so USB_EP3_IN_SIZE can be increased
      to 24. This slightly increases the serial transmission the
      speed to the host with for larger transmissions.
      const byte USB_EP3_IN_SIZE = 24

- `var volatile byte usb_cdc_eptx_buffer[USB_EP3_IN_SIZE] at USB_EP3_IN_ADDR`

      define the buffer in dual port of the USB SIE, so data is
      accessible from the JAL code

- `const byte USB_CDC_RX_BUFFER_SIZE = 0x10`

      USB_CDC_RX_BUFFER_SIZE contains the size of the receive FIFO buffer
      a large(r) buffer can speed up the transmission speed at the cost
      of free memory space. The user of the usb_serial library has the
      ability to control size of the FIFO buffer, by defining the
      const byte USB_CDC_RX_BUFFER_SIZE = 0x?? before the inclusion of
      the usb_serial file

- `const byte USB_CDC_DATA_INTF = 0x0A`

  No documentation found

- `var volatile byte usb_ep2out_buf[ USB_EP2_OUT_SIZE ] at USB_EP2_OUT_ADDR`

  No documentation found

- `const byte USB_CDC_DATA_ENDPOINT = 3`

      USB_CDC_DATA_ENDPOINT specifies the USB CDC Data end point
      This usb serial library uses end point 3

- `const bit USB_EP0 = 1`

      Setup the buffers for the USB endpoints, three endpoints are required
      for a CDC class:
      ) The default control enpoint (0)
      ) Interrupt endpoint (1)
      ) The bulk endpoint (3)
      Note: Addresses are based on USB_EP0_ADDRESS and not USB_BDT_ADDRESS.
      The USB_BDT_ADDRESS is the start address of the buffer endpoints.
      Currently the USB driver covers buffer sizes of at most 8 bytes.

- `const byte USB_STRING0[] =`

      USB_STRING0 shall contain the USB strings language ID
      The user has the ability to define the string content of
      USB_STRING0 before inclusion of the usb_serial library to
      override the default content ( 0x0904 ).
      note that the USB shall contain 16 bit unicode characters, little endian
      encoded, and that the first field of USB_STRING0 shall contain the size
      of the entire USB_STRING0 array, the second byte shall be set to
      USB_DT_STRING (=0x03).

- `var volatile byte usb_ep3in_buf[ USB_EP3_IN_SIZE ] at USB_EP3_IN_ADDR`

  No documentation found

- `const byte USB_STRING1[0x36] =`

      USB_STRING1 shall contain the USB device descriptor string
      The user has the ability to define the string content of
      USB_STRING1 before inclusion of the usb_serial library to
      override the default content ("Microchip Technology Inc.").
      note that the USB shall contain 16 bit unicode characters, little endian
      encoded, and that the first field of USB_STRING1 shall contain the size
      of the entire USB_STRING1 array, the second byte shall be set to
      USB_DT_STRING (=0x03).

- `const byte USB_EP3_IN_SIZE = 8`

      Size may be changed, see comment below.

- `const byte USB_DSC_FN_COUNTRY_SELECTION = 0x07`

  No documentation found

- `const byte USB_CDC_INT_EP_SIZE = 8`

      set the size of the interrupt interface

- `const byte USB_EP0_IN_SIZE = 8`

  No documentation found

- `const USB_CONFIGURATION_DESCRIPTOR_SIZE = 0x09 + 0x09 + 0x05 + 0x04 + 0x05 +`

  No documentation found

- `const word USB_SERIAL_VENDOR_ID = 0x04D8`

      USB_SERIAL_VENDOR_ID is set to Microchip if const is not defined by
      the user of the usb_serial library. One can set the USB_SERIAL_VENDOR_ID
      by defining the const before inclusion of the usb_serial library

- `const byte USB_STRING2[30] =`

      USB_STRING2 shall contain the USB product descriptor string
      The user has the ability to define the string content of
      USB_STRING1 before inclusion of the usb_serial library to
      override the default content ("JAL Serial.").
      note that the USB shall contain 16 bit unicode characters, little endian
      encoded, and that the first field of USB_STRING1 shall contain the size
      of the entire USB_STRING2 array, the second byte shall be set to
      USB_DT_STRING (=0x03).

- `var volatile byte usb_ep0out_buf[ USB_EP0_OUT_SIZE ] at USB_EP0_OUT_ADDR`

  No documentation found

- `const byte USB_CDC_BULK_OUT_EP_SIZE = USB_EP3_OUT_SIZE`

      Endpoint buffer sizes for CDC.

- `var volatile byte usb_ep1in_buf[ USB_EP1_IN_SIZE ] at USB_EP1_IN_ADDR`

  No documentation found

- `const word USB_EP2_OUT_ADDR = ( USB_EP1_IN_ADDR + USB_EP1_IN_SIZE )`

  No documentation found

- `const word USB_EP3_OUT_ADDR = ( USB_EP2_IN_ADDR + USB_EP2_IN_SIZE )`

  No documentation found

- `const byte USB_CDC_COMM_INTF_ID = 0x00`

      set the communication interface ID

- `const bit USB_EP1 = 1`

  No documentation found

- `var volatile byte usb_ep3out_buf[ USB_EP3_OUT_SIZE ] at USB_EP3_OUT_ADDR`

  No documentation found

- `const word USB_CDC_BULK_IN_EP_ADDR = USB_EP3_IN_ADDR`

  No documentation found

- `const byte USB_DSC_FN_USB_TERMINAL = 0x09`

  No documentation found

- `const word USB_SERIAL_PRODUCT_ID = 0x000A`

      USB_SERIAL_PRODUCT_ID is set to Microchip CDC if const is not defined
      by the user of the usb_serial library can set the USB_SERIAL_PRODUCT_ID
      by defining the const before inclusion of the usb_serial library

- `const byte USB_DSC_FN_TELEPHONE_RINGER = 0x04`

  No documentation found

- `const byte USB_EP0_OUT_SIZE = 8`

  No documentation found

- `const word USB_EP0_OUT_ADDR = ( USB_EP0_ADDRESS )`

  No documentation found

- `const byte USB_EP1_IN_SIZE = 8`

  No documentation found

- `const byte USB_CS_ENDPOINT = 0x25`

  No documentation found

- `const byte USB_DSC_FN_TEL_OP_MODES = 0x08`

  No documentation found

- `var volatile byte usb_ep1out_buf[ USB_EP1_OUT_SIZE ] at USB_EP1_OUT_ADDR`

  No documentation found

- `const bit USB_EP2 = 1`

  No documentation found

- `const byte USB_EP2_IN_SIZE = 8`

  No documentation found

- `const byte USB_CDC_BULK_IN_EP_SIZE = USB_EP3_IN_SIZE`

  No documentation found

- `const word USB_EP1_OUT_ADDR = ( USB_EP0_IN_ADDR + USB_EP0_IN_SIZE )`

  No documentation found

- `const byte USB_EP1_OUT_SIZE = 8`

  No documentation found

- `const bit USB_EP3 = 1`

  No documentation found

- `const byte USB_EP2_OUT_SIZE = 8`

  No documentation found

- `const word USB_EP2_IN_ADDR = ( USB_EP2_OUT_ADDR + USB_EP2_OUT_SIZE )`

  No documentation found

- `const word USB_EP1_IN_ADDR = ( USB_EP1_OUT_ADDR + USB_EP1_OUT_SIZE )`

  No documentation found

- `const byte USB_DSC_FN_ACM = 0x02`

      ACM - Abstract Control Management

- `var volatile byte usb_cdc_eprx_buffer[USB_EP3_OUT_SIZE] at USB_EP3_OUT_ADDR`

  No documentation found

- `var volatile byte usb_ep2in_buf[ USB_EP2_IN_SIZE ] at USB_EP2_IN_ADDR`

  No documentation found

- `const word USB_EP0_IN_ADDR = ( USB_EP0_OUT_ADDR + USB_EP0_OUT_SIZE )`

  No documentation found

- `const word USB_CDC_BULK_OUT_EP_ADDR = USB_EP3_OUT_ADDR`

      Endpoint buffer locations for CDC.

- `var volatile byte usb_ep0in_buf[ USB_EP0_IN_SIZE ] at USB_EP0_IN_ADDR`

  No documentation found

- `const byte USB_DSC_FN_HEADER = 0x00`

      CDC class Functional Descriptors related constants
      bDscSubType in Functional Descriptors
      more.... see Table 25 in USB CDC Specification 1.1

- `const byte USB_CDC_NO_PROTOCOL = 0x00`

      No class specific protocol required

- `const byte USB_CDC_TX_BUFFER_SIZE = 0x20`

      USB_CDC_TX_BUFFER_SIZE contains the size of the transmit FIFO buffer
      a large(r) buffer can speed up the transmission speed at the cost
      of free memory space. The user of the usb_serial library has the
      ability to control size of the FIFO buffer, by defining the
      const byte USB_CDC_TX_BUFFER_SIZE = 0x?? before the inclusion of
      the usb_serial file

- `const byte USB_DSC_FN_DLM = 0x03`

      DLM - Direct Line Managment

- `const byte USB_CDC_DATA_INTF_ID = 0x01`

      set the interrupt interface ID

### Procedures

- `usb_serial_data'put(byte in data)`

      usb_serial_data'put - write char to the USB port, blocking

      Asynchronuous serial send routine, using the USB Cummunication class
      Sends byte X (8 bit) to the USB host (via virtual COM port)
      Note that characters are dropped when the communcation channel on the
      USB host side is not opened (yet), this is a design choice in order to
      avoid lockup(s)

- `usb_serial_init()`

      usb_serial_init will setup the USB communication, however, this can be
      a time consuming process, therefore this call is non blocking.
      In order to check if the USB device has established contact with the
      HOST USB device, one can call the usb_is_configured() function, see
      usb_is_configured() documentation for additional details.

- `usb_serial_flush()`

      usb_serial_flush shall be called on a regular base (millisecond range)
      in order to keep the USB transmision alive. Therefore it is adviced to
      call the usb_serial_flush procedure in the main loop of your
      application.
      Note: Only needed when USB interrupt is not used otherwise this call
      can be omitted (it does nothing in interrupt mode).

- `usb_serial_write(byte in data)`

      usb_serial_write - write char to the USB port, blocking

      Asynchronuous serial send routine, using the USB Cummunication class
      Sends byte X (8 bit) to the USB host (via virtual COM port)
      Checks (and waits if necessary) if FIFO buffer is full
      Note that characters are dropped when the communcation channel on the
      USB host side is not opened (yet), this is a design choice in order to
      avoid lockup(s)

### Functions

- `usb_serial_data'get() return byte`

      usb_serial_data'get() - read a char (blocking!)

      usb_serial_data'get() waits till a character has been received and
      returns it

- `usb_serial_data_available'get() return bit`

  No documentation found

- `usb_serial_port_open'get() return bit`

      Check if the serial port has been opened by the host.

- `usb_serial_read(byte out data) return bit`

      usb_serial_read - read char if available (non-blocking)

      usb_serial_read returns true if a character has been received,
      otherwise returns false. If function returns true, the data variable
      is valid, and contains the character that has been received. In other
      cases the data variable is not defined
      Note that characters are dropped when the communcation channel on the
      USB host side is not opened (yet), this is a design choice in order to
      avoid lockup(s)

Private

- `_usb_serial_read(byte out data) return bit`

      _usb_serial_read - internal use only!

      _usb_serial_read returns true if a character has been received,
      otherwise returns false. If function returns true, the data variable
      is valid, and contains the character that has been received. In other
      cases the data variable is not defined
      Note that characters are dropped when the communcation channel on the
      USB host side is not opened (yet), this is a design choice in order to
      avoid lockup(s)

## Related samples

Here are the list of samples which use this library:

16f1455 | 16f1455_usb_serial.jal
16f1455 | 16f1455_tutorial_usb_serial.jal
18f14k50 | 18f14k50_usb_serial.jal
18f2450 | 18f2450_usb_serial.jal
18f2550 | 18f2550_usb_serial.jal
18f25k50 | 18f25k50_usb_serial.jal
18f4550 | 18f4550_ir2serial.jal
18f4550 | 18f4550_usb_serial.jal
18f4550 | 18f4550_slip_poll_usb.jal
18f67j50 | 18f67j50_usb_serial.jal
18f67j50 | 18f67j50_slip_poll_usb.jal