USB New Architecture Library Details
1. General
This set of libraries is intended for the PIC18F2455/2550/4455 and 4550 PIC's or PICs with the same USB SIE.
1.1 The "Libraries"
Following units are part of the NA library set (all to be found in "USB_Library_NA.zip"):
- The "Core" (common) part:
- USB_Library_Core_Constants_NA.mpas, which defines all common USB constants needed by all following units.
- USB_Library_Core_NA.mpas, which is the code part that is used by (common to) all following units.
- The "USB Class specific" parts:
- USB_HID_Library_NA.mpas, the library specific for HID applications (Human Interface Devices - keyboard, mouse, etc -).
- USB_CDC_Library_NA.mpas, the library specific for CDC applications (PIC looks like serial port to the PC).
- USB_MSD_Library_NA.mpas, the library specific for MSD applications (PIC looks like external drive to the PC).
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 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:
- Add USB_Library_Core_Constants_NA.mpas and USB_Library_Core_NA.mpas to your project,
- Add USB_HID_Library_NA.mpas and/or USB_CDC_Library_NA.mpas and/or USB_MSD_Library_NA.mpas
to your project (at least 1 of them),
- Copy USB_ProjectItems.mpas from one of the project examples (preferably from USB_HID_CDC_MSD_Demo_NA.zip) and add it to your project,
- Adapt the Usb_ProjectItems.mpas unit to the needs and specifications of your project (see section 2.3),
- Adapt your project to initialise and work with the USB NA libraries (see section 2.4.1).
2. Details
a. USB_Library_Core_NA.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.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.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): boolean;
// 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.mpas
None. The application is not involved in the MSD process: the user does not see the datatransport from/to the SDMMC card.
a. USB_Library_Core_NA.mpas
- "Full speed" USB communications.
- "Interrupt" data transfers for endpoint 0.
- Endpoint 0 (default pipe) packet size: 64.
- No ping pong mode(s) used.
b. USB_HID_Library_NA.mpas
- "Interrupt" data transfers.
- The maximum send and receive buffersizes are 64 bytes.
- No HID "Feature" data.
- The HID "data" pipe is USB endpoint 1.
c. USB_CDC_Library_NA.mpas
- "Bulk" data transfers.
- The maximum send and receive sizes are 64 bytes
- The CDC "bulk" pipe is USB endpoint 3.
- The CDC class specific commands are supported (at least some of them).
- The CDC "notification" interface is foreseen (endpoint 2), but no notifications are sent to the host.
d. USB_MSD_Library_NA.mpas
- "Bulk" data transfers.
- The send and receive databuffersizes are 64 bytes
- The MSD "data" pipe is USB endpoint 4.
- No user access to the MSD process.
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.
- Define which USB interfaces will belong to your product (e.g. HID + MSD):
{$DEFINE USB_HID}
{-$DEFINE USB_CDC}
{$DEFINE USB_MSD}
- Fill in the "UsbManufacturer", "UsbProduct" and "UsbSerialNo" strings. Adapt their size if needed.
UsbManufacturer : string[18] = 'HID-CDC-MSD Vendor';
// Sets the name of the manufacturer of the USB device you are creating.
UsbProduct : string[19] = 'HID-CDC-MSD Product';
// Sets the name of the USB product you are creating.
UsbSerialNo : string[4] = '1.01';
// Sets the serial number of the product you are creating.
- Fill in the "VENDOR_ID", "PRODUCT_ID", and "DEVICE_REL_NR". Make sure the "PRODUCT_ID" differs for every usb product you make.
VENDOR_ID : word = $1234;
// Identifies the vendor or manufacturer of the USB device created.
// This number is (normally) defined by the USB organisation.
PRODUCT_ID : word = $2000;
// Identifies the product, defined by the manufacturer (You).
DEVICE_REL_NR : word = $0101;
// Identifies the device release number (in BCD!), defined by the manufacturer (You).
2.3.2 Specific for HID devices
Specific items for HID devices that have to be present in the "USB_ProjectItems" unit:
- The definitions of the HID reportsizes ("HID_INPUT_REPORT_BYTES" and "HID_OUTPUT_REPORT_BYTES").
These sizes have all to be > 0. Adapt them if necessary.
HID_INPUT_REPORT_BYTES = 10;
// Defines the number of bytes for the USB HID "Input" (from PIC to host!) data transfer.
// Always this number of bytes is sent to the host (fixed packet size), no matter the
// number of bytes requested to send when calling "USB_HID_Write".
// This number should be equal to or greater than the size of the user defined sendbuffer
// in the main program.
HID_OUTPUT_REPORT_BYTES = 10;
// Defines the number of bytes for the USB HID "Output" (from host to PIC!) data transfer.
// Always this number of bytes is received from the host (fixed packet size), no matter the
// number of bytes requested to receive when calling "USB_HID_READ".
// This number should be equal to or greater than the size of the user defined receivebuffer
// in the main program.
- The definition of the HID Endpoint polling interval (in millisecs), adapt it if necessary:
USB_POLLING_INTERVAL = 10;
// polling of the usb buffer status every n millisecs by the host,
// low value (must be > 0) is frequent polling,
// high value (max 255) is infrequent polling.
- The HID report descriptor, adapt it if necessary:
// ------------------------------
// --- HID Report descriptor ----
// ------------------------------
const HID_REPORT_DESCR_LEN: word = 34;
const HIDReportDescriptor: array[HID_REPORT_DESCR_LEN] of byte = (
0x06, 0xA0, 0xFF, // USAGE_PAGE (Vendor Defined)
0x09, 0x01, // USAGE ID (Vendor Usage 1)
// Collection
0xA1, 0x01, // COLLECTION (Application)
// The Input report
0x09, 0x03, // USAGE ID - Vendor defined
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0x00, 0xFF, // LOGICAL_MAXIMUM (255)
0x75, 0x08, // REPORT_SIZE (8)
0x95, HID_INPUT_REPORT_BYTES, // REPORT_COUNT
0x81, 0x02, // INPUT (Data,Var,Abs)
// The Output report
0x09, 0x04, // USAGE ID - Vendor defined
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0x00, 0xFF, // LOGICAL_MAXIMUM (255)
0x75, 0x08, // REPORT_SIZE (8 bits)
0x95, HID_OUTPUT_REPORT_BYTES, // REPORT_COUNT
0x91, 0x02, // OUTPUT (Data,Var,Abs)
// End Collection
0xC0 // END_COLLECTION
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:
- The definition of the SD/MMC card chip select, adapt it if necessary:
var MMC_Chip_Select : sbit at LATC2_bit;
Mmc_Chip_Select_Direction : sbit at TRISC2_bit;
2.4 Your project
This section describes how to use the class specific NA libraries in your project code.
Overview:
- Add USB_Library_Core_Constants_NA.mpas and USB_Library_Core_NA.mpas to your project,
- Add USB_HID_Library_NA.mpas and/or USB_CDC_Library_NA.mpas and/or USB_MSD_Library_NA.mpas
to your project (at least 1 of them),
- Copy USB_ProjectItems.mpas from one of the project examples (preferably from USB_HID_CDC_MSD_Demo_NA.zip) and add it to your project,
- Adapt the Usb_ProjectItems.mpas unit to the needs and specifications of your project (see section 2.3),
- Adapt your project to initialise and work with the USB NA libraries (see section 2.4.1).
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 interrupt;
begin
USB_Interrupt; // USB_CDC processing procedure
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.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.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
No further code needed to use the NA MSD class specific library (USB_MSD_Library_NA.mpas).
3. The software
The libraries themselves:
USB_Library_NA.zip
The Example Projects (including the "Usb_projectItems.mpas" file) (all 20 Mhz crystal used):
Specific for CDC devices:
-
The PC side software:
TestCDC.zip , TestCDC executable + sources (Delphi).
usbser.sys and usbser.inf. The .inf file resides in "C:\Windows\inf", the .sys file resides in "C:\Windows\Windows32\drivers".
- Do not forget to adapt the usbser.inf file if you use another VID or PID for your USB product than in the example files.
The entry in the usbser.inf file in the Windows directory has to look like:
%DESCRIPTION%=DriverInstall, USB\VID_1234&PID_2000&MI_00, where "xx" in "&MI_xx" is the number of the CDC interface.
- Every time you install (plug in) a CDC device with a new VID/PID combination, windows will give it a comportnumber that was not used before, so the used number always increases. If you want you can change it to a more acceptable number using the windows 'device manager'. Portnumbers already taken can be found in the registry: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Ports.
-------------------------------------------