picard-usbserial
PIC 16F1454 USB to Serial Adapter Firmware
/********************************************************************
FileName: main.c
Dependencies: See INCLUDES section
Processor: PIC16F1455 USB Microcontrollers
Complier: Microchip XC8
Company: Microchip Technology, Inc.
// --- Copyright (C) 2011-2013 Kazuo Iwamoto All Rights Reserved. --
********************************************************************
File Description:
Change History:
Rev Description
---- -----------------------------------------
1.0 Initial release 16F1459�p��rev2.9f����ɍ쐬
********************************************************************/
/** INCLUDES *******************************************************/
#include "./USB/usb.h"
#include "./USB/usb_function_cdc.h"
#include "HardwareProfile.h"
/** CONFIGURATION **************************************************/
// PIC 16F1459 fuse configuration:
#if defined (USE_INTERNAL_OSC) //Definition in the hardware profile
__CONFIG(FOSC_INTOSC & WDTE_OFF & PWRTE_ON & MCLRE_OFF & CP_OFF & BOREN_ON & CLKOUTEN_OFF & IESO_OFF & FCMEN_OFF);
__CONFIG(WRT_OFF & CPUDIV_NOCLKDIV & USBLSCLK_48MHz & PLLMULT_3x & PLLEN_ENABLED & STVREN_ON & BORV_LO & LPBOR_OFF & LVP_ON);
#else
__CONFIG(FOSC_HS & WDTE_OFF & PWRTE_ON & MCLRE_OFF & CP_OFF & BOREN_ON & CLKOUTEN_OFF & IESO_OFF & FCMEN_OFF);
__CONFIG(WRT_OFF & CPUDIV_NOCLKDIV & USBLSCLK_48MHz & PLLMULT_4x & PLLEN_ENABLED & STVREN_ON & BORV_LO & LPBOR_OFF & LVP_ON);
#endif
/** I N C L U D E S **********************************************************/
#include "GenericTypeDefs.h"
#include "Compiler.h"
#include "usb_config.h"
#include "USB/usb_device.h"
#include "USB/usb.h"
#include "HardwareProfile.h"
/** V A R I A B L E S ********************************************************/
char USB_Out_Buffer[CDC_DATA_OUT_EP_SIZE];
char RS232_Out_Data[CDC_DATA_IN_EP_SIZE];
unsigned char NextUSBOut;
unsigned char NextUSBOut;
//char RS232_In_Data;
unsigned char LastRS232Out; // Number of characters in the buffer
unsigned char RS232cp; // current position within the buffer
unsigned char RS232_Out_Data_Rdy = 0;
USB_HANDLE lastTransmission;
unsigned char sending= 0;
unsigned char receiving= 0;
/** P R I V A T E P R O T O T Y P E S ***************************************/
static void InitializeSystem(void);
void ProcessIO(void);
void USBDeviceTasks(void);
void YourHighPriorityISRCode();
void YourLowPriorityISRCode();
void USBCBSendResume(void);
void BlinkUSBStatus(void);
void UserInit(void);
void InitializeUSART(void);
void putcUSART(char c);
unsigned char getcUSART ();
//These are your actual interrupt handling routines.
void interrupt ISRCode()
{
//Check which interrupt flag caused the interrupt.
//Service the interrupt
//Clear the interrupt flag
//Etc.
#if defined(USB_INTERRUPT)
USBDeviceTasks();
#endif
}
/** DECLARATIONS ***************************************************/
/******************************************************************************
* Function: void main(void)
*
* PreCondition: None
*
* Input: None
*
* Output: None
*
* Side Effects: None
*
* Overview: Main program entry point.
*
* Note: None
*****************************************************************************/
#if defined(__18CXX)
void main(void)
#else
int main(void)
#endif
{
InitializeSystem();
#if defined(USB_INTERRUPT)
USBDeviceAttach();
#endif
while(1)
{
#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
/********************************************************************
* Function: static void InitializeSystem(void)
*
* PreCondition: None
*
* Input: None
*
* Output: None
*
* Side Effects: None
*
* Overview: InitializeSystem is a centralize initialization
* routine. All required USB initialization routines
* are called from here.
*
* User application initialization routine should
* also be called from here.
*
* Note: None
*******************************************************************/
static void InitializeSystem(void)
{
#if defined(_PIC14E)
//Configure all pins for digital mode, except RB4, which has a POT on it
ANSELA = 0x00;
#if defined(_16F1459) || defined(_16LF1459)
ANSELB = 0x10; //RB4 has a POT on it, on the Low Pin Count USB Dev Kit board
#endif
ANSELC = 0x00;
#if defined (USE_INTERNAL_OSC)
OSCTUNE = 0;
OSCCON = 0xFC; //16MHz HFINTOSC with 3x PLL enabled (48MHz operation)
ACTCON = 0x90; //Enable active clock tuning with USB
#endif
#endif
// 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 - (platform).h, and that an appropriate I/O pin
// has been mapped to it.
#if defined(USE_SELF_POWER_SENSE_IO)
tris_self_power = INPUT_PIN; // See HardwareProfile.h
#endif
UserInit();
USBDeviceInit(); //usb_device.c. Initializes USB module SFRs and firmware
//variables to known states.
}//end InitializeSystem
/******************************************************************************
* Function: void UserInit(void)
*
* PreCondition: None
*
* Input: None
*
* Output: None
*
* Side Effects: None
*
* Overview: This routine should take care of all of the demo code
* initialization that is required.
*
* Note:
*
*****************************************************************************/
void UserInit(void)
{
unsigned char i;
InitializeUSART();
// Initialize the arrays
for (i=0; i<sizeof(USB_Out_Buffer); i++)
{
USB_Out_Buffer[i] = 0;
}
NextUSBOut = 0;
LastRS232Out = 0;
lastTransmission = 0;
mInitAllLEDs();
}//end UserInit
/******************************************************************************
* Function: void InitializeUSART(void)
*
* PreCondition: None
*
* Input: None
*
* Output: None
*
* Side Effects: None
*
* Overview: This routine initializes the UART to 19200
*
* Note:
*
*****************************************************************************/
void InitializeUSART(void)
{
unsigned char c;
#if defined(_16F1459) || defined(_16LF1459)
ANSELBbits.ANSB5 = 0; //Make RB5 digital
#endif
UART_TRISRx=1; // RX
UART_TRISTx=0; // TX
TXSTA = 0x24; // TX enable BRGH=1
RCSTA = 0x90; // Single Character RX
SPBRG = 0x71;
SPBRGH = 0x02; // 0x0271 for 48MHz -> 19200 baud
BAUDCON = 0x08; // BRG16 = 1
c = RCREG; // read
}//end InitializeUSART
#define mDataRdyUSART() PIR1bits.RCIF
#define mTxRdyUSART() TXSTAbits.TRMT
/******************************************************************************
* Function: void putcUSART(char c)
*
* PreCondition: None
*
* Input: char c - character to print to the UART
*
* Output: None
*
* Side Effects: None
*
* Overview: Print the input character to the UART
*
* Note:
*
*****************************************************************************/
void putcUSART(char c)
{
TXREG = c;
sending= 1;
}
/******************************************************************************
* Function: void mySetLineCodingHandler(void)
*
* PreCondition: USB_CDC_SET_LINE_CODING_HANDLER is defined
*
* Input: None
*
* Output: None
*
* Side Effects: None
*
* Overview: This function gets called when a SetLineCoding command
* is sent on the bus. This function will evaluate the request
* and determine if the application should update the baudrate
* or not.
*
* Note:
*
*****************************************************************************/
#if defined(USB_CDC_SET_LINE_CODING_HANDLER)
void mySetLineCodingHandler(void)
{
//If the request is not in a valid range
if(cdc_notice.GetLineCoding.dwDTERate.Val > 115200)
{
//NOTE: There are two ways that an unsupported baud rate could be
//handled. The first is just to ignore the request and don't change
//the values. That is what is currently implemented in this function.
//The second possible method is to stall the STATUS stage of the request.
//STALLing the STATUS stage will cause an exception to be thrown in the
//requesting application. Some programs, like HyperTerminal, handle the
//exception properly and give a pop-up box indicating that the request
//settings are not valid. Any application that does not handle the
//exception correctly will likely crash when this requiest fails. For
//the sake of example the code required to STALL the status stage of the
//request is provided below. It has been left out so that this demo
//does not cause applications without the required exception handling
//to crash.
//---------------------------------------
//USBStallEndpoint(0,1);
}
else
{
//Update the baudrate info in the CDC driver
CDCSetBaudRate(cdc_notice.GetLineCoding.dwDTERate.Val);
//Update the baudrate of the UART
#if defined(__18CXX) || defined(__XC8)
{
DWORD_VAL dwBaud;
dwBaud.Val = (DWORD)(GetSystemClock()/4)/line_coding.dwDTERate.Val-1;
SPBRG = dwBaud.v[0];
SPBRGH = dwBaud.v[1];
}
#elif defined(__C30__) || defined __XC16__
{
DWORD_VAL dwBaud;
#if defined(__dsPIC33EP512MU810__) || defined (__PIC24EP512GU810__)
dwBaud.Val = ((GetPeripheralClock()/(unsigned long)(16 * line_coding.dwDTERate.Val)))- 1;
#else
dwBaud.Val = (((GetPeripheralClock()/2)+(BRG_DIV2/2*line_coding.dwDTERate.Val))/BRG_DIV2/line_coding.dwDTERate.Val-1);
#endif
U2BRG = dwBaud.Val;
}
#elif defined(__C32__)
{
U2BRG = ((GetPeripheralClock()+(BRG_DIV2/2*line_coding.dwDTERate.Val))/BRG_DIV2/line_coding.dwDTERate.Val-1);
//U2MODE = 0;
U2MODEbits.BRGH = BRGH2;
//U2STA = 0;
}
#endif
}
}
#endif
/******************************************************************************
* Function: void putcUSART(char c)
*
* PreCondition: None
*
* Input: None
*
* Output: unsigned char c - character to received on the UART
*
* Side Effects: None
*
* Overview: Print the input character to the UART
*
* Note:
*
*****************************************************************************/
unsigned char getcUSART ()
{
char c;
if (RCSTAbits.OERR) // in case of overrun error
{ // we should never see an overrun error, but if we do,
RCSTAbits.CREN = 0; // reset the port
c = RCREG;
RCSTAbits.CREN = 1; // and keep going.
}
else
{
c = RCREG;
}
// not necessary. EUSART auto clears the flag when RCREG is cleared
// PIR1bits.RCIF = 0; // clear Flag
receiving= 1;
return c;
}
/********************************************************************
* Function: void ProcessIO(void)
*
* PreCondition: None
*
* Input: None
*
* Output: None
*
* Side Effects: None
*
* Overview: This function is a place holder for other user
* routines. It is a mixture of both USB and
* non-USB tasks.
*
* Note: None
*******************************************************************/
void ProcessIO(void)
{
//Blink the LEDs according to the USB device status
BlinkUSBStatus();
// User Application USB tasks
if((USBDeviceState < CONFIGURED_STATE)||(USBSuspendControl==1)) return;
if (RS232_Out_Data_Rdy == 0) // only check for new USB buffer if the old RS232 buffer is
{ // empty. This will cause additional USB packets to be NAK'd
LastRS232Out = getsUSBUSART(RS232_Out_Data,64); //until the buffer is free.
if(LastRS232Out > 0)
{
RS232_Out_Data_Rdy = 1; // signal buffer full
RS232cp = 0; // Reset the current position
}
}
//Check if one or more bytes are waiting in the physical UART transmit
//queue. If so, send it out the UART TX pin.
if(RS232_Out_Data_Rdy && mTxRdyUSART())
{
#if defined(USB_CDC_SUPPORT_HARDWARE_FLOW_CONTROL)
//Make sure the receiving UART device is ready to receive data before
//actually sending it.
if(UART_CTS == USB_CDC_CTS_ACTIVE_LEVEL)
{
putcUSART(RS232_Out_Data[RS232cp]);
++RS232cp;
if (RS232cp == LastRS232Out)
RS232_Out_Data_Rdy = 0;
}
#else
//Hardware flow control not being used. Just send the data.
putcUSART(RS232_Out_Data[RS232cp]);
++RS232cp;
if (RS232cp == LastRS232Out)
RS232_Out_Data_Rdy = 0;
#endif
}
//Check if we received a character over the physical UART, and we need
//to buffer it up for eventual transmission to the USB host.
if(mDataRdyUSART() && (NextUSBOut < (CDC_DATA_OUT_EP_SIZE - 1)))
{
USB_Out_Buffer[NextUSBOut] = getcUSART();
++NextUSBOut;
USB_Out_Buffer[NextUSBOut] = 0;
}
#if defined(USB_CDC_SUPPORT_HARDWARE_FLOW_CONTROL)
//Drive RTS pin, to let UART device attached know if it is allowed to
//send more data or not. If the receive buffer is almost full, we
//deassert RTS.
if(NextUSBOut <= (CDC_DATA_OUT_EP_SIZE - 5u))
{
UART_RTS = USB_CDC_RTS_ACTIVE_LEVEL;
}
else
{
UART_RTS = (USB_CDC_RTS_ACTIVE_LEVEL ^ 1);
}
#endif
//Check if any bytes are waiting in the queue to send to the USB host.
//If any bytes are waiting, and the endpoint is available, prepare to
//send the USB packet to the host.
if((USBUSARTIsTxTrfReady()) && (NextUSBOut > 0))
{
putUSBUSART(&USB_Out_Buffer[0], NextUSBOut);
NextUSBOut = 0;
}
CDCTxService();
}//end ProcessIO
/********************************************************************
* Function: void BlinkUSBStatus(void)
*
* PreCondition: None
*
* Input: None
*
* Output: None
*
* Side Effects: None
*
* Overview: BlinkUSBStatus turns on and off LEDs
* corresponding to the USB device state.
*
* Note: mLED macros can be found in HardwareProfile.h
* USBDeviceState is declared and updated in
* usb_device.c.
*******************************************************************/
void BlinkUSBStatus(void)
{
static WORD led_count=0;
if(led_count == 0)led_count = 10000U;
led_count--;
#define mLED_Both_Off() {mLED_1_Off();mLED_2_Off();}
#define mLED_Both_On() {mLED_1_On();mLED_2_On();}
#define mLED_Only_1_On() {mLED_1_On();mLED_2_Off();}
#define mLED_Only_2_On() {mLED_1_Off();mLED_2_On();}
if(USBSuspendControl == 1)
{
if(led_count==0)
{
mLED_1_Toggle();
if(mGetLED_1())
{
mLED_2_On();
}
else
{
mLED_2_Off();
}
}//end if
}
else
{
if(USBDeviceState == DETACHED_STATE)
{
mLED_Both_Off();
}
else if(USBDeviceState == ATTACHED_STATE)
{
mLED_Both_On();
}
else if(USBDeviceState == POWERED_STATE)
{
mLED_Only_1_On();
}
else if(USBDeviceState == DEFAULT_STATE)
{
mLED_Only_2_On();
}
else if(USBDeviceState == ADDRESS_STATE)
{
if(led_count == 0)
{
mLED_1_Toggle();
mLED_2_Off();
}//end if
}
else if(USBDeviceState == CONFIGURED_STATE)
{
if(led_count==0)
{
if(UART_DTR) { // no DTR
if(mGetLED_1()) {
mLED_1_Off();
receiving= 0;
} else if(receiving)
mLED_1_On();
} else { // DTR
if(!mGetLED_1()) {
mLED_1_On();
receiving= 0;
} else if(receiving)
mLED_1_Off();
}
if(mGetLED_2()) {
mLED_2_Off();
sending= 0;
} else if(sending)
mLED_2_On();
}//end if
}//end if(...)
}//end if(UCONbits.SUSPND...)
}//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.
// Note *: The "usb_20.pdf" specs indicate 500uA or 2.5mA, depending upon device classification. However,
// the USB-IF has officially issued an ECN (engineering change notice) changing this to 2.5mA for all
// devices. Make sure to re-download the latest specifications to get all of the newest ECNs.
/******************************************************************************
* Function: void USBCBSuspend(void)
*
* PreCondition: None
*
* Input: None
*
* Output: None
*
* Side Effects: None
*
* Overview: Call back that is invoked when a USB suspend is detected
*
* Note: None
*****************************************************************************/
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.
}
/******************************************************************************
* Function: void USBCBWakeFromSuspend(void)
*
* PreCondition: None
*
* Input: None
*
* Output: None
*
* Side Effects: None
*
* Overview: The host may put USB peripheral devices in low power
* suspend mode (by "sending" 3+ms of idle). Once in suspend
* mode, the host may wake the device back up by sending non-
* idle state signalling.
*
* This call back is invoked when a wakeup from USB suspend
* is detected.
*
* Note: None
*****************************************************************************/
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
// 10+ 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).
// Make sure the selected oscillator settings are consistent with USB
// operation before returning from this function.
}
/********************************************************************
* Function: void USBCB_SOF_Handler(void)
*
* PreCondition: None
*
* Input: None
*
* Output: None
*
* Side Effects: None
*
* Overview: The USB host sends out a SOF packet to full-speed
* devices every 1 ms. This interrupt may be useful
* for isochronous pipes. End designers should
* implement callback routine as necessary.
*
* Note: None
*******************************************************************/
void USBCB_SOF_Handler(void)
{
// No need to clear UIRbits.SOFIF to 0 here.
// Callback caller is already doing that.
}
/*******************************************************************
* Function: void USBCBErrorHandler(void)
*
* PreCondition: None
*
* Input: None
*
* Output: None
*
* Side Effects: None
*
* Overview: The purpose of this callback is mainly for
* debugging during development. Check UEIR to see
* which error causes the interrupt.
*
* Note: None
*******************************************************************/
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.
}
/*******************************************************************
* Function: void USBCBCheckOtherReq(void)
*
* PreCondition: None
*
* Input: None
*
* Output: None
*
* Side Effects: None
*
* Overview: When SETUP packets arrive from the host, some
* firmware must process the request and respond
* appropriately to fulfill the request. Some of
* the SETUP packets will be for standard
* USB "chapter 9" (as in, fulfilling chapter 9 of
* the official USB specifications) requests, while
* others may be specific to the USB device class
* that is being implemented. For example, a HID
* class device needs to be able to respond to
* "GET REPORT" type of requests. This
* is not a standard USB chapter 9 request, and
* therefore not handled by usb_device.c. Instead
* this request should be handled by class specific
* firmware, such as that contained in usb_function_hid.c.
*
* Note: None
*******************************************************************/
void USBCBCheckOtherReq(void)
{
USBCheckCDCRequest();
}//end
/*******************************************************************
* Function: void USBCBStdSetDscHandler(void)
*
* PreCondition: None
*
* Input: None
*
* Output: None
*
* Side Effects: None
*
* Overview: The USBCBStdSetDscHandler() callback function is
* called when a SETUP, bRequest: SET_DESCRIPTOR request
* arrives. Typically SET_DESCRIPTOR requests are
* not used in most applications, and it is
* optional to support this type of request.
*
* Note: None
*******************************************************************/
void USBCBStdSetDscHandler(void)
{
// Must claim session ownership if supporting this request
}//end
/*******************************************************************
* Function: void USBCBInitEP(void)
*
* PreCondition: None
*
* Input: None
*
* Output: None
*
* Side Effects: None
*
* Overview: This function is called when the device becomes
* initialized, which occurs after the host sends a
* SET_CONFIGURATION (wValue not = 0) request. This
* callback function should initialize the endpoints
* for the device's usage according to the current
* configuration.
*
* Note: None
*******************************************************************/
void USBCBInitEP(void)
{
//Enable the CDC data endpoints
CDCInitEP();
}
/********************************************************************
* Function: void USBCBSendResume(void)
*
* PreCondition: None
*
* Input: None
*
* Output: None
*
* Side Effects: None
*
* Overview: The USB specifications allow some types of USB
* peripheral devices to wake up a host PC (such
* as if it is in a low power suspend to RAM state).
* This can be a very useful feature in some
* USB applications, such as an Infrared remote
* control receiver. If a user presses the "power"
* button on a remote control, it is nice that the
* IR receiver can detect this signalling, and then
* send a USB "command" to the PC to wake up.
*
* The USBCBSendResume() "callback" function is used
* to send this special USB signalling which wakes
* up the PC. This function may be called by
* application firmware to wake up the PC. This
* function will only be able to wake up the host if
* all of the below are true:
*
* 1. The USB driver used on the host PC supports
* the remote wakeup capability.
* 2. The USB configuration descriptor indicates
* the device is remote wakeup capable in the
* bmAttributes field.
* 3. The USB host PC is currently sleeping,
* and has previously sent your device a SET
* FEATURE setup packet which "armed" the
* remote wakeup capability.
*
* If the host has not armed the device to perform remote wakeup,
* then this function will return without actually performing a
* remote wakeup sequence. This is the required behavior,
* as a USB device that has not been armed to perform remote
* wakeup must not drive remote wakeup signalling onto the bus;
* doing so will cause USB compliance testing failure.
*
* This callback should send a RESUME signal that
* has the period of 1-15ms.
*
* Note: This function does nothing and returns quickly, if the USB
* bus and host are not in a suspended condition, or are
* otherwise not in a remote wakeup ready state. Therefore, it
* is safe to optionally call this function regularly, ex:
* anytime application stimulus occurs, as the function will
* have no effect, until the bus really is in a state ready
* to accept remote wakeup.
*
* When this function executes, it may perform clock switching,
* depending upon the application specific code in
* USBCBWakeFromSuspend(). This is needed, since the USB
* bus will no longer be suspended by the time this function
* returns. Therefore, the USB module will need to be ready
* to receive traffic from the host.
*
* The modifiable section in this routine may be changed
* to meet the application needs. Current implementation
* temporary blocks other functions from executing for a
* period of ~3-15 ms depending on the core frequency.
*
* According to USB 2.0 specification section 7.1.7.7,
* "The remote wakeup device must hold the resume signaling
* for at least 1 ms but for no more than 15 ms."
* The idea here is to use a delay counter loop, using a
* common value that would work over a wide range of core
* frequencies.
* That value selected is 1800. See table below:
* ==========================================================
* Core Freq(MHz) MIP RESUME Signal Period (ms)
* ==========================================================
* 48 12 1.05
* 4 1 12.6
* ==========================================================
* * These timing could be incorrect when using code
* optimization or extended instruction mode,
* or when having other interrupts enabled.
* Make sure to verify using the MPLAB SIM's Stopwatch
* and verify the actual signal on an oscilloscope.
*******************************************************************/
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();
}
}
}
/*******************************************************************
* Function: void USBCBEP0DataReceived(void)
*
* PreCondition: ENABLE_EP0_DATA_RECEIVED_CALLBACK must be
* defined already (in usb_config.h)
*
* Input: None
*
* Output: None
*
* Side Effects: None
*
* Overview: This function is called whenever a EP0 data
* packet is received. This gives the user (and
* thus the various class examples a way to get
* data that is received via the control endpoint.
* This function needs to be used in conjunction
* with the USBCBCheckOtherReq() function since
* the USBCBCheckOtherReq() function is the apps
* method for getting the initial control transfer
* before the data arrives.
*
* Note: None
*******************************************************************/
#if defined(ENABLE_EP0_DATA_RECEIVED_CALLBACK)
void USBCBEP0DataReceived(void)
{
}
#endif
/*******************************************************************
* Function: BOOL USER_USB_CALLBACK_EVENT_HANDLER(
* USB_EVENT event, void *pdata, WORD size)
*
* PreCondition: None
*
* Input: USB_EVENT event - the type of event
* void *pdata - pointer to the event data
* WORD size - size of the event data
*
* Output: None
*
* Side Effects: None
*
* Overview: This function is called from the USB stack to
* notify a user application that a USB event
* occured. This callback is in interrupt context
* when the USB_INTERRUPT option is selected.
*
* Note: None
*******************************************************************/
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;
}
/** EOF main.c *************************************************/