USB New Architecture Library Details

Table of Contents
1. General
  1.1. The "Libraries"
  1.2 "USB_ProjectItems"
  1.3 Your project
2. Details
  2.1 The library interfaces
  2.2 The libraries technical details
  2.3 The "USB_ProjectItems.mpas" unit
    2.3.1. Common
    2.3.2 Specific for HID devices
    2.3.3 Specific for CDC devices
    2.3.4 Specific for MSD devices
  2.4 Your project
    2.4.1 Common
    2.4.2 Specific for HID devices
    2.4.3 Specific for CDC devices
    2.4.4 Specific for MSD devices
3. The software


1. General

This set of libraries is intended for the PIC24FJ64GB002 or PIC24 devices with the same USB SIE.

1.1 The "Libraries"

Following units are part of the NA library set:

1.2 "USB_ProjectItems"

Each application needs also a unit called USB_ProjectItems.mpas wherin all project dependant items are defined. So, for each project a separate version of USB_ProjectItems.mpas should exist, residing in the project's directory. This unit is also a part of the USB NA P24 library set, because the libraries can not do without it.
Examples of different USB_ProjectItems inits can be found in the project examples.

1.3 Your project

This section describes globally the activities to be done to use the library set in your project:

2. Details

2.1 The library interfaces

a. USB_Library_Core_NA_P24.mpas
procedure USB_Interrupt;
// USB interrupt procedure, does all the USB processing

procedure InitUsb;
// Initializes USB and starts the uSB enumeration process.

function ConfiguredUsb: boolean;
// Returns true if the USB enumeration process was completed and successful.

function ActiveUsb: boolean;
// Returns true if "ConfiguredUsb" and the USB bus is active

procedure DeInitUsb;
// Stops and disables USB.

procedure SoftDetachUsb;
// Disconnects USB from the "host" and connects it again,
// restarting the USB enumeration process.


b. USB_HID_Library_NA_P24.mpas
function USB_HID_Read(DstPtr: ^byte; MaxLen: byte): boolean;
// Returns true if some data has arrived, false means: no data arrived.
// The arguments are: //
//   DstPtr:  the address of the user defined receive buffer.
//   MaxLen:  the actual number of received bytes to be copied into the user defined receive buffer
//            (can be less than or equal to the size of the user defined receive buffer).

function USB_HID_Write(SrcPtr: ^byte; ByteCount: byte): boolean;
// Returns success as true, false means: try later again (USB sendbuffer was still busy).
// The arguments are: //
//   SrcPtr:  the address of the user defined send buffer.
//   ByteCount:  the actual number of bytes to be sent
//              (can be less than or equal to the size of the user defined send buffer).


c. USB_CDC_Library_NA_P24.mpas
type TLineCoding =
     record
       BaudRate   : dword;
       CharFormat : byte;
       ParityType : byte;
       DataBits   : byte;
     end;

function USB_CDC_Bytes_Received: byte;
// returns the nr of bytes that are received in the internal USB-CDC receive buffer (and not read yet by "USB_CDC_Read")

function USB_CDC_Read(DstPtr: ^byte; MaxLen: byte): byte;
// returns the number of bytes actually copied from the internal USB_CDC receive buffer into the "DstPtr" buffer
// The arguments are: //
//   DstPtr:  the address of the user defined receive buffer.
//   MaxLen:  the actual number of received bytes to be copied into the user defined receive buffer
//            (can be less than or equal to the size of the user defined receive buffer).

function USB_CDC_Write_Ready: boolean;
// returns "true" if the system is ready for a new "USB_CDC_Write" (USB sendbuffer empty)

function USB_CDC_Write(SrcPtr: ^byte; ByteCount: byte): boolean;
// Copies "ByteCount" bytes from the "SrcPtr" buffer into the internal USB-CDC send buffer if it was not empty
// Returns success as true, false means: try later again (USB sendbuffer was still busy).
// The arguments are: //
//   SrcPtr:  the address of the user defined send buffer.
//   ByteCount:  the actual number of bytes to be sent
//              (can be less than or equal to the size of the user defined send buffer).

procedure USB_CDC_Get_Line_Coding(var LineCoding: TLineCoding);
// Gets the linecoding sent by the host (PC) to the CDC device (wanted value by the host)
// type TLineCoding =
//     record
//       BaudRate   : dword; (in bits/second)
//       CharFormat : byte;  (0 = 1 Stop bit, 1 = 1.5 Stop bits, 2 = 2 Stop bits)
//       ParityType : byte;  (0 = None, 1 = Odd, 2 = Even, 3 = Mark, 4 = Space)
//       DataBits   : byte;  (5, 6, 7, 8 or 16)
//     end;

procedure USB_CDC_Set_Line_Coding(var LineCoding: TLineCoding);
// Set the linecoding to the host (PC) from the CDC device (actual value in the device)
// TLineCoding contents: see above

function USB_CDC_Get_Control_Line_State: word;
// Gets the control line state sent by the host (PC) to the CDC device (value signalled by the host)
// TControlLineState:
//   bit0: RS-232 signal "DTR". 0 = Not Present,  1 = Present
//   bit1: RS-232 signal "RTS". 0 = Deactivate carrier, 1 = Activate carrier
//   bit2..bit15: not used


d. USB_MSD_Library_NA_P24.mpas

None. The application is not involved in the MSD process: the user does not see the datatransport from/to the SDMMC card.

2.2 The libraries technical details

a. USB_Library_Core_NA_P24.mpas
b. USB_HID_Library_NA_P24.mpas
c. USB_CDC_Library_NA_P24.mpas
d. USB_MSD_Library_NA_P24.mpas


2.3 The "USB_ProjectItems.mpas" unit

This section descibes how this file (project specific) must be adapted to the needs and specifications of your project. The explanation assumed you copied the Usb_projectItems file from this project (the most complete version).

Things to do:

2.3.1. Common

These are items that mainly define which USB device classes will be present in your product.

2.3.2 Specific for HID devices

Specific items for HID devices that have to be present in the "USB_ProjectItems" unit:

2.3.3 Specific for CDC devices

Specific items for CDC devices that have to be present in the "USB_ProjectItems" unit:

2.3.4 Specific for MSD devices

Specific items for MSD devices that have to be present in the "USB_ProjectItems" unit:

2.4 Your project

This section describes how to use the class specific NA libraries in your project code.
Overview:

2.4.1 Common

This section describes what the application (your project) should do/have to make the NA Usb libraries work.
2.4.1.1 The Interrupt procedure
In the interrupt procedure of the application the USB inperrupt procedure should be called. This procedure does all USB processing based on interrupts received from the PIC's SIE (the USB engine):
procedure USB1Interrupt(); iv IVT_ADDR_USB1INTERRUPT;
begin
  USB_Interrupt;
end;
Important: There is no need to "force" extra interrupts, e.g. via a timer, the "USB_Interrupt" procedure will only do something when actually an inperrupt came from the SIE (the PIC's USB engine).
2.4.1.2 USB Initialisation
Usb initialisation should be done once before other USB activities take place:
  InitUsb;                    // Start USB
  repeat until ConfiguredUsb; // wait for the completion of the USB enumeration
The initialization procedure ("InitUsb") takes care of the enabling of the necessary interrupts.
2.4.1.3 Checking if USB is still active
After initialisation, one should check if the USB connection is (still) there before trying to do some USB activity:
  while true do  // main program loop
  begin
    ...
    ...
    if ActiveUsb then // <--------------- here the test is done for an active USB link
    begin
      // do some usb related activities here
      ...
    end;
  end;	
The same mechanism can also be used to signal an active USB link on e.g. a led:
   If ActiveUsb
   then PortA.0 := 1  // led connected to port A.0 (via a series resistor)
   else PortA.0 := 0;

2.4.2 Specific for HID devices

Here is described how to use the NA HID class specific library (USB_HID_Library_NA_P24.mpas).
Declaration of HID receive and sendbuffers
The "report length"s (an USB-HID term) is defined with the file "USB_ProjectItems.mpas". It defines (in bytes) the size of the data packet to receive ("HID_OUTPUT_REPORT_BYTES") and to send ("HID_INPUT_REPORT_BYTES").

The most common is that the user's receive and sendbuffers in the application have the same size as the corresponding "report length"s. To achieve this, the buffers can be declared as follows:
var 
    ReceiveBuffer: array[HID_OUTPUT_REPORT_BYTES] of byte;
    SendBuffer:    array[HID_INPUT_REPORT_BYTES]  of byte;
Important: In all USB documentation the words "IN" and "OUT" are frequently used to indicate the direction of data transfer. Those directions are always "USB host" (e.g. PC) related. So, "IN" means data from the PIC to the host (data OUT from PIC point of view), "OUT" means data from the host to the PIC (data IN from PIC point of view).
Receiving HID data (PC --> PIC)
Receiving HID data is done like this:
  if USB_HID_Read(@ReceiveBuffer, NrBytesToReceive) then
  begin
    // process received data here
  end;
As you can see, this routine takes two parameters: the address of the user's receivebuffer and the number of bytes "to receive". The latter is in fact the number of bytes to be copied from the "internal USB_HID receivebuffer" into the user's receivebuffer. The function gives back a boolean value: "true" means that a data packet was received in the internal USB_HID buffer, and that "NrBytesToReceive" bytes were copied from it to the user's receivebuffer; "false" means that there was nothing received. Make sure that "NrBytesToReceive" is smaller than or equal to the size of the user's receivebuffer.

Remark: the number of bytes received in the "internal USB_HID receivebuffer is always "HID_OUTPUT_REPORT_BYTES", the packet size is a constant value (a USB_HID attribute), "NrBytesToReceive" only defines how many bytes are copied from it. This is the reason why "USB_HID_Read" returns true or false and not the "number of bytes received".
Sending HID data (PIC --> PC)
Sending data is done like this:
  repeat until USB_HID_Write(@SendBuffer, NrBytesToSend); 
Be carefull: this can block your software (it is waiting for HID_Write to succeed). To avoid this (and thus not block your software) use following code:
  if USB_HID_Write(@SendBuffer, NrBytesToSend) then
  begin
    { writing was successful }
  end else
  begin
    { writing was not successfull, try again later }
  end;

As you can see, two parameters are required by this function: the address of the sendbuffer and the number of bytes to be sent. The second parameter only defines how many bytes from the user's sendBuffer must be copied into the "internal" HID-USB sendbuffer (which is of size "HID_INPUT_REPORT_BYTES"). The number of bytes that actually will be send is always "HID_INPUT_REPORT_BYTES", regardless of the value of the 2nd parameter. Make sure the "NrBytesToSend" is smaller than or equal to "HID_INPUT_REPORT_BYTES".

The function "USB_HID_Write" returns "true" if success (data was accepted and will be sent), "false" if no success (data is not accepted to be sent -- the previous send operation was not finished yet, the "internal USB_HID sendbuffer is still occupied --).

Another remark: "USB_HID_Write" does not wait for the actual transmission from the PIC to the PC to happen. It only "prepares" things: it copies data from the user's "SendBuffer" into the "internal" HID-USB sendbuffer and tells it to send. After this the SIE (the PIC's USB engine) takes care of the actual send action.

A simple "echo back" example:
var HID_ReceiveBuffer: array[10] of byte;
    HID_SendBuffer   : array[10] of byte;
    I                : byte;
	
    ...
	
    if ActiveUsb then
    begin
      ...
	  // read data from the USB-HID interface and echo it
      if USB_HID_Read(@HID_ReceiveBuffer, SizeOf(HID_ReceiveBuffer)) then     // some HID USB data came in
      begin
        for I := 0 to SizeOf(HID_SendBuffer) - 1
        do HID_SendBuffer[i] := HID_ReceiveBuffer[i];                         // simply echo
        repeat until USB_HID_Write(@HID_SendBuffer, SizeOf(HID_SendBuffer));  // send
      end;
      ...
    end;
    ...  

2.4.3 Specific for CDC devices

Here is described how to use the NA CDC class specific library (USB_CDC_Library_NA_P24.mpas).
Declaration of the user's CDC Receive and SendBuffers
This is done as follows:
var 
    ReceiveBuffer: array[64] of byte;
    SendBuffer:    array[20] of byte;
The maximum value for both buffersizes is 64 (that is the size of the īnternal USB_CDC buffers).
It is preferred that the receivebuffer is 64 bytes, otherwise parts or usb data could be lost.
Receiving CDC data (PC --> PIC)
Receiving data is done like this:
  NrofBytesReceived := Read USB_CDC_Read(@ReceiveBuffer, NrBytesToReceive);
  if NrofBytesReceived > 0 then
  begin
    // process received data here
  end;
As you can see, this routine takes two parameters: the address of the user's receivebuffer and the number of bytes "to receive".
The latter is in fact the maximum number of bytes to be copied from the "internal USB_CDC receivebuffer" into the user's receivebuffer (again, it is preferred that the receivebuffer's size is 64 bytes to prevent loss of usb data).
The function returns the actual bytes that are received and copied into the user's receivebuffer.
Make sure that "NrBytesToReceive" is smaller than or equal to the size of the user's receivebuffer.

Sending CDC data (PIC --> PC)
Sending data is done like this:
  repeat until USB_CDC_Write(@SendBuffer, NrBytesToSend); 
Be carefull: this can block your software (it is waiting for CDC_Write to succeed). To test if "USB_CDC_Write" will be successfull (and thus not block your software) use "USB_CDC_Write_Ready" before "USB_CDC_Write":
  if USB_CDC_Write_Ready then
  begin
    USB_CDC_Write(@SendBuffer, NrBytesToSend);
  end;

As you can see, two parameters are required by this function: the address of the sendbuffer and the number of bytes to be sent. Make sure the "NrBytesToSend" is smaller than or equal to the size of the user's sendbuffer.

The function "USB_CDC_Write" returns "true" if success (data was accepted and will be sent), "false" if no success (data is not accepted to be sent -- the previous send operation was not finished yet, the "internal USB_CDC sendbuffer is still occupied --).

Remark: "USB_CDC_Write" does not wait for the actual transmission from the PIC to the PC to happen. It only "prepares" things: it copies data from the user's "SendBuffer" into the "internal" CDC-USB sendbuffer and tells it to send. After this the SIE (the PIC's USB engine) takes care of the actual send action.

A simple "echo back" example:
var ReceiveBuffer  : array[64] of byte;    
    SendBuffer     : array[64] of byte;
    I              : byte;
	
    ...
		
    if ActiveUsb then
    begin
      ...
      // read data from the USB-CDC interface and echo it
      NrRead := USB_CDC_Read(@ReceiveBuffer, SizeOf(ReceiveBuffer));    
      if NrRead > 0 then                                                   // some CDC USB data came in
      begin
        ...                    
        if NrRead > SizeOf(SendBuffer) then NrRead :=  SizeOf(SendBuffer); // to be sure
        for I := 0 to NrRead - 1 do SendBuffer[i] := ReceiveBuffer[i];     // simply echo data towards the host (PC)
        repeat until USB_CDC_Write(@SendBuffer, NrRead);                   // send          
        ...
      end;
    end;
    ...  

2.4.4 Specific for MSD devices


3. The software


The libraries:
USB NA Library files.

Example projects:

Specific for CDC devices:


-------------------------------------------