Netcruzer Library API  V2.03
 All Data Structures Files Functions Variables Typedefs Enumerations Macros Groups Pages
device_HID_demo_no_nzusb/main.c

===== Description =====

USB demo not using the system to do it all. This means all USB defines, includes and other code must be done in our program, it is not done by the system. This demo monitors the USB port for any Debug messages.
- If "hi" is received, it replies with "Hello"
- If "hello" is received, it replies with "G'Day Mate!"
Use the "Netcruzer USB Terminal" app to send and receive USB Debug messages. It can be downloaded here: https://netcruzer.com/usbterminal/

===== Required Hardware =====

The project requires a SBC66 Netcruzer board with an USB Port (not USB Host).

===== Building Project =====

This project is located in the "src/demos/usb/device_HID_demo_no_nzusb" folder of the Netcruzer Download. To compile for Netcruzer Board, open this project in MPLAB X, and select the "Project Configuration" for desired board. For example "SBC66ZL_R1" for the SBC66ZL Revision 1 board. For details click here A common error is "The system cannot find the path specified". This generally means you don't have the required XC16 compiler version installed. Go to "Project Properties", and select your installed XC16 compiler in the "Project Configuration" section.

===== Programming Board =====

After compiling (build), the board can be programmed via the USB Bootloader or a PIC Programmer. USB Programming is simplified when using the SBC board together with a Prototype Board.

===== File History =====

2013-07-22, David H. (DH):

#define THIS_IS_MAIN_FILE //Uniquely identifies this as the file with the main application entry function main()
// Includes /////////////////////////////////////
#include "HardwareProfile.h" //Required for all Netcruzer projects
#include "USB\usb.h"
#if defined(USB_IS_CDC)
#include "USB\usb_function_cdc.h"
#elif defined(USB_IS_HID)
#include "USB\usb_function_hid.h"
#endif
#include "nz_usbHidDefs.h"
// PRIVATE PROTOTYPES ///////////////////////////
void blinkUSBStatus(void);
void usbCBSendResume(void);
void usbInit(void);
void ProcessIO(void);
void LEDTask(void);
//VARIABLES /////////////////////////////////////
PacketToFromPC PacketFromPC; //64 byte buffer for receiving packets on EP1 OUT from the PC
PacketToFromPC PacketToPC; //64 byte buffer for sending packets on EP1 IN to the PC
PacketToFromPC PacketFromPCBuffer;
USB_HANDLE USBOutHandle = 0;
USB_HANDLE USBInHandle = 0;
BYTE usbState;
WORD sysLedMask; //Used for LED flashing pattern. Has 1 set bit, and it rotated left every 50ms.
WORD sysLedPattern; //Used for blinking system LED
int main(void)
{
WORD tmrFlashLed = 0; //Timer for flashing system LED
nzSysInitDefault(); //Default initialization. All ports inputs. All analog features disabled. Tick 1ms
DIR_SYSLED = OUTPUT_PIN; //Set System LED port as outputs
sysLedMask = 0x0001;
sysLedPattern = 0x0f0f; //Blink
usbInit();
#if defined(USB_INTERRUPT)
USBDeviceAttach();
#endif
while(1)
{
nzSysTaskDefault(); //Main netcruzer task, call in main loop.
//Enter every 100ms
if (tick16TestTmr(tmrFlashLed)) {
tick16UpdateTmrMS(tmrFlashLed, 100); //Update timer to expire in 100ms again
LEDTask();
}
#if defined(USB_POLLING)
// Check bus status and service USB interrupts.
USBDeviceTasks(); // Interrupt or polling method. If using polling, must call
// this function periodically. This function will take care
// of processing and responding to SETUP transactions
// (such as during the enumeration process when you first
// plug in). USB hosts require that USB devices should accept
// and process SETUP packets in a timely fashion. Therefore,
// when using polling, this function should be called
// regularly (such as once every 1.8ms or faster** [see
// inline code comments in usb_device.c for explanation when
// "or faster" applies]) In most cases, the USBDeviceTasks()
// function does not take very long to execute (ex: <100
// instruction cycles) before it returns.
#endif
// Application-specific tasks.
// Application related code may be added here, or in the ProcessIO() function.
ProcessIO();
}//end while
}//end main
void usbInit(void)
{
// The USB specifications require that USB peripheral devices must never source
// current onto the Vbus pin. Additionally, USB peripherals should not source
// current on D+ or D- when the host/hub is not actively powering the Vbus line.
// When designing a self powered (as opposed to bus powered) USB peripheral
// device, the firmware should make sure not to turn on the USB module and D+
// or D- pull up resistor unless Vbus is actively powered. Therefore, the
// firmware needs some means to detect when Vbus is being powered by the host.
// A 5V tolerant I/O pin can be connected to Vbus (through a resistor), and
// can be used to detect when Vbus is high (host actively powering), or low
// (host is shut down or otherwise not supplying power). The USB firmware
// can then periodically poll this I/O pin to know when it is okay to turn on
// the USB module/D+/D- pull up resistor. When designing a purely bus powered
// peripheral device, it is not possible to source current on D+ or D- when the
// host is not actively providing power on Vbus. Therefore, implementing this
// bus sense feature is optional. This firmware can be made to use this bus
// sense feature by making sure "USE_USB_BUS_SENSE_IO" has been defined in the
// HardwareProfile.h file.
#if defined(USE_USB_BUS_SENSE_IO)
tris_usb_bus_sense = INPUT_PIN; // See HardwareProfile.h
#endif
// If the host PC sends a GetStatus (device) request, the firmware must respond
// and let the host know if the USB peripheral device is currently bus powered
// or self powered. See chapter 9 in the official USB specifications for details
// regarding this request. If the peripheral device is capable of being both
// self and bus powered, it should not return a hard coded value for this request.
// Instead, firmware should check if it is currently self or bus powered, and
// respond accordingly. If the hardware has been configured like demonstrated
// on the PICDEM FS USB Demo Board, an I/O pin can be polled to determine the
// currently selected power source. On the PICDEM FS USB Demo Board, "RA2"
// is used for this purpose. If using this feature, make sure "USE_SELF_POWER_SENSE_IO"
// has been defined in HardwareProfile.h, and that an appropriate I/O pin has been mapped
// to it in HardwareProfile.h.
#if defined(USE_SELF_POWER_SENSE_IO)
tris_self_power = INPUT_PIN; // See HardwareProfile.h
#endif
//initialize the variable holding the handle for the last transmission
USBOutHandle = 0;
USBInHandle = 0;
USBDeviceInit(); //usb_device.c. Initializes USB module SFRs and firmware variables to known states.
usbState = IDLE_STATE;
}//end
void LEDTask(void) {
sysLedMask = sysLedMask << 1;
if (sysLedMask == 0) {
sysLedMask = 0x0001;
}
if ( sysLedPattern & sysLedMask) {
LAT_SYSLED = 1; // Turn the LED on
}
else {
LAT_SYSLED = 0; // Turn the LED off
}
}
void ProcessIO(void) {
//Blink the LEDs according to the USB device status
blinkUSBStatus();
// User Application USB tasks
if((USBDeviceState < CONFIGURED_STATE) || USBIsDeviceSuspended()) return;
//Check if anything was received via USB
//Are we done sending the last response. We need to be seeing that we might have to send
//a reply to host depending on what we receive
if(!HIDTxHandleBusy(USBInHandle))
{
if(!HIDRxHandleBusy(USBOutHandle)) //Did we receive a command?
{
switch(PacketFromPC.Command)
{
//We received a "Device Info" command. Reply with our board info - identifies the current hardware
case CMDUSB_DEVICE_INFO:
PacketToPC.Command = PacketFromPC.Command; //Build reply message
PacketToPC.DeviceInfo.BoardID = USBHID_BOARD_ID_MAIN;
PacketToPC.DeviceInfo.BoardRev = USBHID_BOARD_REV;
USBInHandle = HIDTxPacket(HID_EP,(BYTE*)&PacketToPC.Contents[0],64);
break;
//We received a debug message. Is a NULL terminated string.
case CMDUSB_DEBUG_MESSAGE:
//Check if we received "hi". If so, reply with "Hello" on a new line
if ( (PacketFromPC.Size==3) && (PacketFromPC.Data[0]=='h') && (PacketFromPC.Data[1]=='i')) {
PacketToPC.Command = PacketFromPC.Command; //Reply with Debug Message
PacketToPC.Size = 7;
PacketToPC.Data[0] = '\n'; //Start on new line
PacketToPC.Data[1] = 'H';
PacketToPC.Data[2] = 'e';
PacketToPC.Data[3] = 'l';
PacketToPC.Data[4] = 'l';
PacketToPC.Data[5] = 'o';
PacketToPC.Data[6] = 0; //NULL terminate string
USBInHandle = HIDTxPacket(HID_EP,(BYTE*)&PacketToPC.Contents[0],64);
}
//Check if we received "hello". If so, reply with "G'Day Mate!" on a new line
//Use strcmp() and strcpy() functions compare and copy strings. Is MUCH slower than method used
//above for "hi" example!
else if (strcmp("hello", (const char*)PacketFromPC.Data) == 0) {
const char* replyStr = "\nG'Day Mate!"; //Reply string
PacketToPC.Command = CMDUSB_DEBUG_MESSAGE; //Reply with Debug Message
PacketToPC.Size = strlen(replyStr)+1; //String length, including NULL terminating character!
strcpy((char*)PacketToPC.Data, replyStr);
USBInHandle = HIDTxPacket(HID_EP,(BYTE*)&PacketToPC.Contents[0],64);
}
break;
}
//Re-arm the OUT endpoint for the next packet
USBOutHandle = HIDRxPacket(HID_EP,(BYTE*)&PacketFromPC,64);
}
}
}
void blinkUSBStatus(void)
{
//USB Suspended
if(USBIsDeviceSuspended())
{
//sysLedPattern = 0b0001111100010101; //1 long, 3 short flash
}
else
{
//If USB is ready and plugged in, 1 long and 2 short flases
//Else, 1 long and 1 short flash
if(USBDeviceState == CONFIGURED_STATE)
{
sysLedPattern = 0b0000111110000101; //1 long, 2 short flash
}
else
{
sysLedPattern = 0b0000011111000001; //1 long, 1 short flash
}
}
}//end blinkUSBStatus
// ******************************************************************************************************
// ************** USB Callback Functions ****************************************************************
// ******************************************************************************************************
// The USB firmware stack will call the callback functions USBCBxxx() in response to certain USB related
// events. For example, if the host PC is powering down, it will stop sending out Start of Frame (SOF)
// packets to your device. In response to this, all USB devices are supposed to decrease their power
// consumption from the USB Vbus to <2.5mA each. The USB module detects this condition (which according
// to the USB specifications is 3+ms of no bus activity/SOF packets) and then calls the USBCBSuspend()
// function. You should modify these callback functions to take appropriate actions for each of these
// conditions. For example, in the USBCBSuspend(), you may wish to add code that will decrease power
// consumption from Vbus to <2.5mA (such as by clock switching, turning off LEDs, putting the
// microcontroller to sleep, etc.). Then, in the USBCBWakeFromSuspend() function, you may then wish to
// add code that undoes the power saving things done in the USBCBSuspend() function.
// The usbCBSendResume() function is special, in that the USB stack will not automatically call this
// function. This function is meant to be called from the application firmware instead. See the
// additional comments near the function.
void USBCBSuspend(void)
{
//Example power saving code. Insert appropriate code here for the desired
//application behavior. If the microcontroller will be put to sleep, a
//process similar to that shown below may be used:
//ConfigureIOPinsForLowPower();
//SaveStateOfAllInterruptEnableBits();
//DisableAllInterruptEnableBits();
//EnableOnlyTheInterruptsWhichWillBeUsedToWakeTheMicro(); //should enable at least USBActivityIF as a wake source
//Sleep();
//RestoreStateOfAllPreviouslySavedInterruptEnableBits(); //Preferrably, this should be done in the USBCBWakeFromSuspend() function instead.
//RestoreIOPinsToNormal(); //Preferrably, this should be done in the USBCBWakeFromSuspend() function instead.
//IMPORTANT NOTE: Do not clear the USBActivityIF (ACTVIF) bit here. This bit is
//cleared inside the usb_device.c file. Clearing USBActivityIF here will cause
//things to not work as intended.
#if defined(__C30__)
//USBSleepOnSuspend(); //Need to include usb_hal_pic24.c if this function is enabled.
#endif
}
void USBCBWakeFromSuspend(void)
{
// If clock switching or other power savings measures were taken when
// executing the USBCBSuspend() function, now would be a good time to
// switch back to normal full power run mode conditions. The host allows
// a few milliseconds of wakeup time, after which the device must be
// fully back to normal, and capable of receiving and processing USB
// packets. In order to do this, the USB module must receive proper
// clocking (IE: 48MHz clock must be available to SIE for full speed USB
// operation).
}
void USBCB_SOF_Handler(void)
{
// No need to clear UIRbits.SOFIF to 0 here.
// Callback caller is already doing that.
}
void USBCBErrorHandler(void) {
// No need to clear UEIR to 0 here.
// Callback caller is already doing that.
// Typically, user firmware does not need to do anything special
// if a USB error occurs. For example, if the host sends an OUT
// packet to your device, but the packet gets corrupted (ex:
// because of a bad connection, or the user unplugs the
// USB cable during the transmission) this will typically set
// one or more USB error interrupt flags. Nothing specific
// needs to be done however, since the SIE will automatically
// send a "NAK" packet to the host. In response to this, the
// host will normally retry to send the packet again, and no
// data loss occurs. The system will typically recover
// automatically, without the need for application firmware
// intervention.
// Nevertheless, this callback function is provided, such as
// for debugging purposes.
}
void USBCBCheckOtherReq(void) {
#if defined(USB_IS_CDC)
USBCheckCDCRequest();
#elif defined(USB_IS_HID)
USBCheckHIDRequest();
#endif
}//end
void USBCBStdSetDscHandler(void) {
// Must claim session ownership if supporting this request
}//end
void USBCBInitEP(void) {
#if defined(USB_IS_CDC)
CDCInitEP();
#elif defined(USB_IS_HID)
//enable the HID endpoint
USBEnableEndpoint(HID_EP,USB_IN_ENABLED|USB_OUT_ENABLED|USB_HANDSHAKE_ENABLED|USB_DISALLOW_SETUP);
//Arm the OUT endpoint for the first packet
//USBOutHandle = HIDRxPacket(HID_EP,(BYTE*)&PacketFromPCBuffer,64);
USBOutHandle = HIDRxPacket(HID_EP,(BYTE*)&PacketFromPC,64);
#endif
}
void usbCBSendResume(void) {
static WORD delay_count;
//First verify that the host has armed us to perform remote wakeup.
//It does this by sending a SET_FEATURE request to enable remote wakeup,
//usually just before the host goes to standby mode (note: it will only
//send this SET_FEATURE request if the configuration descriptor declares
//the device as remote wakeup capable, AND, if the feature is enabled
//on the host (ex: on Windows based hosts, in the device manager
//properties page for the USB device, power management tab, the
//"Allow this device to bring the computer out of standby." checkbox
//should be checked).
if (USBGetRemoteWakeupStatus() == TRUE) {
//Verify that the USB bus is in fact suspended, before we send
//remote wakeup signalling.
if (USBIsBusSuspended() == TRUE) {
USBMaskInterrupts();
//Clock switch to settings consistent with normal USB operation.
USBCBWakeFromSuspend();
USBSuspendControl = 0;
USBBusIsSuspended = FALSE; //So we don't execute this code again,
//until a new suspend condition is detected.
//Section 7.1.7.7 of the USB 2.0 specifications indicates a USB
//device must continuously see 5ms+ of idle on the bus, before it sends
//remote wakeup signalling. One way to be certain that this parameter
//gets met, is to add a 2ms+ blocking delay here (2ms plus at
//least 3ms from bus idle to USBIsBusSuspended() == TRUE, yeilds
//5ms+ total delay since start of idle).
delay_count = 3600U;
do {
delay_count--;
} while (delay_count);
//Now drive the resume K-state signalling onto the USB bus.
USBResumeControl = 1; // Start RESUME signaling
delay_count = 1800U; // Set RESUME line for 1-13 ms
do {
delay_count--;
} while (delay_count);
USBResumeControl = 0; //Finished driving resume signalling
USBUnmaskInterrupts();
}
}
}
#if defined(ENABLE_EP0_DATA_RECEIVED_CALLBACK)
void USBCBEP0DataReceived(void) {
}
#endif
BOOL USER_USB_CALLBACK_EVENT_HANDLER(int event, void *pdata, WORD size)
{
switch(event)
{
case EVENT_TRANSFER:
//Add application specific callback task or callback function here if desired.
break;
case EVENT_SOF:
USBCB_SOF_Handler();
break;
case EVENT_SUSPEND:
USBCBSuspend();
break;
case EVENT_RESUME:
USBCBWakeFromSuspend();
break;
case EVENT_CONFIGURED:
USBCBInitEP();
break;
case EVENT_SET_DESCRIPTOR:
USBCBStdSetDscHandler();
break;
case EVENT_EP0_REQUEST:
USBCBCheckOtherReq();
break;
case EVENT_BUS_ERROR:
USBCBErrorHandler();
break;
case EVENT_TRANSFER_TERMINATED:
//Add application specific callback task or callback function here if desired.
//The EVENT_TRANSFER_TERMINATED event occurs when the host performs a CLEAR
//FEATURE (endpoint halt) request on an application endpoint which was
//previously armed (UOWN was = 1). Here would be a good place to:
//1. Determine which endpoint the transaction that just got terminated was
// on, by checking the handle value in the *pdata.
//2. Re-arm the endpoint if desired (typically would be the case for OUT
// endpoints).
break;
default:
break;
}
return TRUE;
}