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.
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).
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
{$DEFINE USB_HID} {-$DEFINE USB_CDC} {$DEFINE USB_MSD}
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.
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).
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.
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.
// ------------------------------ // --- 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
var MMC_Chip_Select : sbit at LATC2_bit; Mmc_Chip_Select_Direction : sbit at TRISC2_bit;
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).
InitUsb; // Start USB repeat until ConfiguredUsb; // wait for the completion of the USB enumerationThe initialization procedure ("InitUsb") takes care of the enabling of the necessary interrupts.
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;
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).
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.
repeat until USB_HID_Write(@SendBuffer, NrBytesToSend);Be carefull: this can block your software (it is waiting for HID_Write to succeed).
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; ...
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).
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".
repeat until USB_CDC_Write(@SendBuffer, NrBytesToSend);Be carefull: this can block your software (it is waiting for CDC_Write to succeed).
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; ...