diff --git a/doc/help_mla_usb.jar b/doc/help_mla_usb.jar index 9b814b5..8325082 100644 Binary files a/doc/help_mla_usb.jar and b/doc/help_mla_usb.jar differ diff --git a/doc/help_mla_usb.pdf b/doc/help_mla_usb.pdf index f5ba529..fa9222e 100644 Binary files a/doc/help_mla_usb.pdf and b/doc/help_mla_usb.pdf differ diff --git a/inc/usb_hal.h b/inc/usb_hal.h index 497c112..416e50c 100644 --- a/inc/usb_hal.h +++ b/inc/usb_hal.h @@ -43,6 +43,12 @@ please contact mla_licensing@microchip.com #else #include "usb_hal_pic24f.h" #endif +#elif defined(__XC32__) + #if defined(__PIC32MM__) + #include "usb_hal_pic32mm.h" + #else + #error "Silicon Platform not defined" + #endif #else #error "Silicon Platform not defined" #endif diff --git a/inc/usb_hal_pic24f.h b/inc/usb_hal_pic24f.h index ea3a88c..7d4e7bf 100644 --- a/inc/usb_hal_pic24f.h +++ b/inc/usb_hal_pic24f.h @@ -48,7 +48,7 @@ please contact mla_licensing@microchip.com //IEC0-IEC7 on PIC24FJ128GC010 Family devices //IEC0-IEC7 on PIC24FJ128GB204 Family devices //IEC0-IEC7 on PIC24FJ256GB412 Family devices -#if defined(__PIC24FJ64GB004__) || defined(__PIC24FJ32GB004__) || defined(__PIC24FJ32GB004__) || defined(__PIC24FJ32GB002__) \ +#if defined(__PIC24FJ64GB004__) || defined(__PIC24FJ64GB002__) || defined(__PIC24FJ32GB004__) || defined(__PIC24FJ32GB002__) \ || defined(__PIC24FJ256GB110__) || defined(__PIC24FJ192GB110__) || defined(__PIC24FJ128GB110__) || defined(__PIC24FJ64GB110__) || defined(__PIC24FJ256GB108__) || defined(__PIC24FJ192GB108__) || defined(__PIC24FJ128GB108__) || defined(__PIC24FJ64GB108__) || defined(__PIC24FJ256GB106__) || defined(__PIC24FJ192GB106__) || defined(__PIC24FJ128GB106__) || defined(__PIC24FJ64GB106__) #define DEVICE_SPECIFIC_IEC_REGISTER_COUNT 6 //Number of IECx registers implemented in the microcontroller (varies from device to device, make sure this is set correctly for the intended CPU) diff --git a/inc/usb_hal_pic32mm.h b/inc/usb_hal_pic32mm.h new file mode 100644 index 0000000..415ca52 --- /dev/null +++ b/inc/usb_hal_pic32mm.h @@ -0,0 +1,776 @@ +// DOM-IGNORE-BEGIN +/******************************************************************************* +Copyright 2015 Microchip Technology Inc. (www.microchip.com) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +To request to license the code under the MLA license (www.microchip.com/mla_license), +please contact mla_licensing@microchip.com +*******************************************************************************/ +//DOM-IGNORE-END + +#if !defined(USB_HAL_PIC32MM_H) +#define USB_HAL_PIC32MM_H + +/*****************************************************************************/ +/****** include files ********************************************************/ +/*****************************************************************************/ + +#include +#include +#include +#include "usb_config.h" + +/*****************************************************************************/ +/****** Constant definitions *************************************************/ +/*****************************************************************************/ + + +//Device specific IECx register count. Useful during interrupt context save and +//restore operations (such as prior to and after entering sleep on suspend). +#if defined(__32MM0256GPM064__) || defined(__32MM0128GPM064__) || defined(__32MM0064GPM064__) || \ + defined(__32MM0256GPM048__) || defined(__32MM0128GPM048__) || defined(__32MM0064GPM048__) || \ + defined(__32MM0256GPM036__) || defined(__32MM0128GPM036__) || defined(__32MM0064GPM036__) || \ + defined(__32MM0256GPM028__) || defined(__32MM0128GPM028__) || defined(__32MM0064PM028__) + + #define DEVICE_SPECIFIC_IEC_REGISTER_COUNT 4 //Number of IECx registers implemented in the microcontroller (varies from device to device, make sure this is set correctly for the intended CPU) + #define USB_HAL_VBUSTristate() {TRISBbits.TRISB6 = 1;} + +#else + #warning "Add processor specific definitions here." +#endif + + + + +#if (USB_PING_PONG_MODE != USB_PING_PONG__FULL_PING_PONG) + #error "PIC32 only supports full ping pong mode. A different mode other than full ping pong is selected in the usb_config.h file." +#endif +#define BDT_NUM_ENTRIES ((USB_MAX_EP_NUMBER + 1) * 4) + + +//----- USBEnableEndpoint() input definitions ---------------------------------- +#define USB_HANDSHAKE_ENABLED 0x01 +#define USB_HANDSHAKE_DISABLED 0x00 + +#define USB_OUT_ENABLED 0x08 +#define USB_OUT_DISABLED 0x00 + +#define USB_IN_ENABLED 0x04 +#define USB_IN_DISABLED 0x00 + +#define USB_ALLOW_SETUP 0x00 +#define USB_DISALLOW_SETUP 0x10 + +#define USB_STALL_ENDPOINT 0x02 + +#define USB_PULLUP_ENABLE 0x00 +#define USB_PULLUP_DISABLE 0x04 + +#define USB_INTERNAL_TRANSCEIVER 0x00 +#define USB_EXTERNAL_TRANSCEIVER 0x01 + +#define USB_FULL_SPEED 0x00 +#define USB_LOW_SPEED 0x08 //Note: Low speed device mode is only supported on certain devices. + + +#define USBPingPongBufferReset U1CONbits.PPBRST + +#define USB_OTG_ENABLE 0x04 +#define USB_OTG_DPLUS_ENABLE 0x80 + + +//----- Interrupt Flag definitions -------------------------------------------- +#define USBTransactionCompleteIE U1IEbits.TRNIE +#define USBTransactionCompleteIF U1IRbits.TRNIF +#define USBTransactionCompleteIFReg U1IR +#define USBTransactionCompleteIFBitNum 3 + +#define USBResetIE U1IEbits.URSTIE +#define USBResetIF U1IRbits.URSTIF +#define USBResetIFReg U1IR +#define USBResetIFBitNum 0 + +#define USBIdleIE U1IEbits.IDLEIE +#define USBIdleIF U1IRbits.IDLEIF +#define USBIdleIFReg U1IR +#define USBIdleIFBitNum 4 + +#define USBActivityIE U1OTGIEbits.ACTVIE +#define USBActivityIF U1OTGIRbits.ACTVIF +#define USBActivityIFReg U1OTGIR +#define USBActivityIFBitNum 4 + +#define USBSOFIE U1IEbits.SOFIE +#define USBSOFIF U1IRbits.SOFIF +#define USBSOFIFReg U1IR +#define USBSOFIFBitNum 2 + +#define USBStallIE U1IEbits.STALLIE +#define USBStallIF U1IRbits.STALLIF +#define USBStallIFReg U1IR +#define USBStallIFBitNum 7 + +#define USBErrorIE U1IEbits.UERRIE +#define USBErrorIF U1IRbits.UERRIF +#define USBErrorIFReg U1IR +#define USBErrorIFBitNum 1 + +#define USBSE0Event 0// U1IRbits.URSTIF// U1CONbits.SE0 +#define USBSuspendControl U1PWRCbits.USUSPEND +#define USBPacketDisable U1CONbits.PKTDIS +#define USBResumeControl U1CONbits.RESUME + +#define USBT1MSECIE U1OTGIEbits.T1MSECIE +#define USBT1MSECIF U1OTGIRbits.T1MSECIF +#define USBT1MSECIFReg U1OTGIR +#define USBT1MSECIFBitNum 6 + +#define USBIDIE U1OTGIEbits.IDIE +#define USBIDIF U1OTGIRbits.IDIF +#define USBIDIFReg U1OTGIR +#define USBIDIFBitNum 7 + +#define USBSESVDIE U1OTGIEbits.SESVDIE +#define USBSESVDIF U1OTGIRbits.SESVDIF +#define USBSESVDReg U1OTGIR +#define USBSESVDBitNum 3 + +#define USBRESUMEIE U1IEbits.RESUMEIE +#define USBRESUMEIF U1IRbits.RESUMEIF +#define USBRESUMEIFReg U1IR +#define USBRESUMEIFBitNum 5 + +//----- Event call back definitions -------------------------------------------- +#if defined(USB_DISABLE_SOF_HANDLER) + #define USB_SOF_INTERRUPT 0x00 +#else + #define USB_SOF_INTERRUPT 0x04 +#endif +#if defined(USB_DISABLE_ERROR_HANDLER) + #define USB_ERROR_INTERRUPT 0x02 +#else + #define USB_ERROR_INTERRUPT 0x02 +#endif + +//STALLIE, IDLEIE, TRNIE, and URSTIE are all enabled by default and are required +#if defined(USB_INTERRUPT) + #define USBEnableInterrupts() {\ + IEC0SET = _IEC0_USBIE_MASK;\ + IPC7CLR = 0x0000FF00;\ + IPC7SET = 0x00001000;\ + __builtin_enable_interrupts();\ + } +#else + #define USBEnableInterrupts() +#endif + +#define USBDisableInterrupts() {IEC0CLR = _IEC0_USBIE_MASK;} + +#if defined(USB_INTERRUPT) + #define USBMaskInterrupts() {IEC0CLR = _IEC0_USBIE_MASK;} + #define USBUnmaskInterrupts() {IEC0SET = _IEC0_USBIE_MASK;} +#else + #define USBMaskInterrupts() + #define USBUnmaskInterrupts() +#endif + + + +//----- BDnSTAT bit definitions ----------------------------------------------- +#define _BSTALL 0x04 //Buffer Stall enable +#define _DTSEN 0x08 //Data Toggle Synch enable +#define _DAT0 0x00 //DATA0 packet expected next +#define _DAT1 0x40 //DATA1 packet expected next +#define _DTSMASK 0x40 //DTS Mask +#define _USIE 0x80 //SIE owns buffer +#define _UCPU 0x00 //CPU owns buffer +#define _STAT_MASK 0xFC + +//----- USTAT bit definitions ------------------------------------------------- +#define USTAT_EP0_PP_MASK ~0x04 +#define USTAT_EP_MASK 0xFC +#define USTAT_EP0_OUT 0x00 +#define USTAT_EP0_OUT_EVEN 0x00 +#define USTAT_EP0_OUT_ODD 0x04 +#define USTAT_EP0_IN 0x08 +#define USTAT_EP0_IN_EVEN 0x08 +#define USTAT_EP0_IN_ODD 0x0C +#define ENDPOINT_MASK 0xF0 + +//----- U1EP bit definitions -------------------------------------------------- +#define UEP_STALL 0x0002 +// Cfg Control pipe for this ep +#define EP_CTRL 0x0C +#define EP_OUT 0x18 // Cfg OUT only pipe for this ep +#define EP_IN 0x14 // Cfg IN only pipe for this ep +#define EP_OUT_IN 0x1C // Cfg both OUT & IN pipes for this ep +#define HSHK_EN 0x01 // Enable handshake packet + // Handshake should be disable for isoch + + +#define BDT_BASE_ADDR_TAG __attribute__ ((aligned (512))) +#define CTRL_TRF_SETUP_ADDR_TAG +#define CTRL_TRF_DATA_ADDR_TAG + +//----- Depricated defintions - will be removed at some point of time---------- +//--------- Depricated in v2.2 +#define _LS 0x00 // Use Low-Speed USB Mode +#define _FS 0x00 // Use Full-Speed USB Mode +#define _TRINT 0x00 // Use internal transceiver +#define _TREXT 0x00 // Use external transceiver +#define _PUEN 0x00 // Use internal pull-up resistor +#define _OEMON 0x00 // Use SIE output indicator + +/*****************************************************************************/ +/****** Type definitions *****************************************************/ +/*****************************************************************************/ + +// Buffer Descriptor Status Register layout. +typedef union __attribute__ ((packed)) _BD_STAT +{ + struct __attribute__ ((packed)){ + unsigned :2; + unsigned BSTALL :1; //Buffer Stall Enable + unsigned DTSEN :1; //Data Toggle Synch Enable + unsigned :2; //Reserved - write as 00 + unsigned DTS :1; //Data Toggle Synch Value + unsigned UOWN :1; //USB Ownership + }; + struct __attribute__ ((packed)){ + unsigned :2; + unsigned PID0 :1; + unsigned PID1 :1; + unsigned PID2 :1; + unsigned PID3 :1; + + }; + struct __attribute__ ((packed)){ + unsigned :2; + unsigned PID :4; //Packet Identifier + }; + uint16_t Val; +} BD_STAT; + +// BDT Entry Layout +typedef union __attribute__ ((packed))__BDT +{ + struct __attribute__ ((packed)) + { + BD_STAT STAT; + uint16_t CNT:10; + uint32_t ADR; //Buffer Address + }; + struct __attribute__ ((packed)) + { + uint32_t res :16; + uint32_t count:10; + }; + uint32_t w[2]; + uint16_t v[4]; + uint64_t Val; +} BDT_ENTRY; + +// USTAT Register Layout +typedef union __USTAT +{ + struct + { + unsigned char filler1 :2; + unsigned char ping_pong :1; + unsigned char direction :1; + unsigned char endpoint_number :4; + }; + uint8_t Val; +} USTAT_FIELDS; + +//Macros for fetching parameters from USTAT_FIELDS variable. +#define USBHALGetLastEndpoint(stat) stat.endpoint_number +#define USBHALGetLastDirection(stat) stat.direction +#define USBHALGetLastPingPong(stat) stat.ping_pong + + +typedef union _POINTER +{ + struct + { + uint8_t bLow; + uint8_t bHigh; + //uint8_t bUpper; + }; + uint16_t _uint16_t; // bLow & bHigh + + //pFunc _pFunc; // Usage: ptr.pFunc(); Init: ptr.pFunc = &; + + uint8_t* bRam; // Ram byte pointer: 2 bytes pointer pointing + // to 1 byte of data + uint16_t* wRam; // Ram word pointer: 2 bytes pointer pointing + // to 2 bytes of data + + const uint8_t* bRom; // Size depends on compiler setting + const uint16_t* wRom; + //const near uint8_t* nbRom; // Near = 2 bytes pointer + //const near uint16_t* nwRom; + //const far uint8_t* fbRom; // Far = 3 bytes pointer + //const far uint16_t* fwRom; +} POINTER; + +/*****************************************************************************/ +/****** Function prototypes and macro functions ******************************/ +/*****************************************************************************/ + + +#define ConvertToPhysicalAddress(a) ((uint32_t)KVA_TO_PA(a)) +#define ConvertToVirtualAddress(a) PA_TO_KVA1(a) + +#if defined(__PIC32__) + #if defined(_IFS1_USBIF_MASK) + #define _ClearUSBIF() {IFS1CLR = _ISF1_USBIF_MASK;} + #define _SetUSBIF() {IEC1SET = _IEC1_USBIE_MASK;} + #define USBClearUSBInterrupt() {IFS1CLR = _IFS1_USBIF_MASK;} + #elif defined(_IFS0_USBIF_MASK) + #define _ClearUSBIF() {IFS0CLR = _IFS0_USBIF_MASK;} + #define _SetUSBIE() {IEC0SET = _IEC0_USBIE_MASK;} + #define USBClearUSBInterrupt() {IFS0CLR = _IFS0_USBIF_MASK;} + #else + #error Cannot clear USB interrupt. + #endif +#endif + + +#ifndef KVA_TO_PA + #define KVA_TO_PA(kva) ((uint32_t)(kva) & 0x1fffffff) +#endif + +#ifndef PA_TO_KVA1 + #define PA_TO_KVA1(pa) ((uint32_t)(pa) | 0xA0000000) +#endif + +#define USBInterruptFlag IFS0bits.USBIF +#define USBInterruptEnableBit IEC0bits.USBIE +#define USBIPLBits IPC7bits.USBIP + + +/******************************************************************** + * Function (macro): void USBClearInterruptFlag(register, uint8_t if_flag_offset) + * + * PreCondition: None + * + * Input: + * register - the register mnemonic for the register holding the interrupt + * flag to be "kleared" + * uint8_t if_flag_offset - the bit position offset (for the interrupt flag to + * "klear") fconst the "right of the register" + * + * Output: None + * + * Side Effects: None + * + * Overview: Klears the specified USB interrupt flag. + * + * Note: Individual USB interrupt flag bits are "Kleared" by writing + * '1' to the bit + *******************************************************************/ +#define USBClearInterruptFlag(reg_name, if_flag_offset) (reg_name = (1 << if_flag_offset)) + + + + +#if !defined(Sleep) + #define REGISTER_UNLOCK_SEQUENCE() {SYSKEY = 0; SYSKEY = 0xAA996655; SYSKEY = 0x556699AA;} + #define REGISTER_LOCK_SEQUENCE() {SYSKEY = 0;} + #define WRITE_OSCCON(newVal) {REGISTER_UNLOCK_SEQUENCE(); OSCCON = newVal; REGISTER_LOCK_SEQUENCE();} + #define Sleep() {WRITE_OSCCON(OSCCON | 0x00000010); _wait();} //Set OSCCON = 1 (to sleep instead of idle), then execute wait instruction) +#endif + + +#define SetConfigurationOptions() {U1CNFG1 = USB_SPEED_OPTION; U1EIE = 0x9F; U1IE = 0x99 | USB_SOF_INTERRUPT | USB_ERROR_INTERRUPT; U1OTGCON &= 0x000F; U1OTGCON |= USB_PULLUP_OPTION;} + + +/******************************************************************** +Function: + bool USBSleepOnSuspend(void) + +Summary: + Places the core into sleep and sets up the USB module + to wake up the device on USB activity (either USB host resume or + USB VBUS going away, such as due to USB cable detach). + +PreCondition: + The USBDeviceInit() function should have been called at least once and the + USB host should have suspended the USB device. + +Parameters: + None + +Return Values: + true - if entered sleep successfully + false - if there was an error entering sleep + +Remarks: + Please note that before calling this function that it is the + responsibility of the application to place all of the other + peripherals or board features into a lower power state if + required. + + Based on the USB specifications, upon detection of the suspend condition, a + USB device should promptly put itself into a low power mode (such that it consumes + no more than 2.5mA from the USB host (unless the host is a USB type-C host and + is actively advertising higher than standard USB spec current capability). + +*******************************************************************/ +bool USBSleepOnSuspend(void); + +/**************************************************************** + Function: + void USBPowerModule(void) + + Description: + This macro is used to power up the USB module if required: + PIC16: defines as nothing + PIC18: defines as nothing + PIC24: defines as U1PWRCbits.USBPWR = 1; + PIC32: defines as U1PWRCbits.USBPWR = 1; + + Parameters: + None + + Return Values: + None + + Remarks: + None + + ****************************************************************/ +#define USBPowerModule() U1PWRCbits.USBPWR = 1; + +/**************************************************************** + Function: + void USBModuleDisable(void) + + Description: + This macro is used to disable the USB module. This will perform a + USB soft detach operation from the host, if the device was already plugged + into the host at the time of calling this function. All USB module + features including the internal USB 1ms timer and the USB VBUS + monitoring comparators will be disabled. + + Parameters: + None + + Return Values: + None + + Remarks: + None + + ****************************************************************/ +#define USBModuleDisable() {\ + U1CON = 0;\ + U1IE = 0;\ + U1OTGIE = 0;\ + U1PWRCbits.USUSPND = 0;\ + U1PWRCbits.USBPWR = 0;\ + USBDeviceState = DETACHED_STATE;\ +} + +/**************************************************************** + Function: + USBSetBDTAddress(addr) + + Description: + This macro is used to power up the USB module if required + + Parameters: + None + + Return Values: + None + + Remarks: + None + + ****************************************************************/ +#define USBSetBDTAddress(addr) {U1BDTP3 = (((uint32_t)KVA_TO_PA(addr)) >> 24); U1BDTP2 = (((uint32_t)KVA_TO_PA(addr)) >> 16); U1BDTP1 = (((uint32_t)KVA_TO_PA(addr)) >> 8);} + +/**************************************************************** + Function: + void USBClearInterruptRegister(int register) + + Description: + Clears all of the interrupts in the requested register + + Parameters: + register - the register that needs to be cleared. + + Return Values: + None + + Remarks: + Note that on these devices to clear an interrupt you must + write a '1' to the interrupt location. + + ****************************************************************/ +#define USBClearInterruptRegister(reg) {reg = 0xFFFFFFFF;} + +/******************************************************************** + Function: + void USBClearInterruptFlag(register, uint8_t if_flag_offset) + + Summary: + Clears the specified USB interrupt flag. + + PreCondition: + None + + Parameters: + register - the register mnemonic for the register holding the interrupt + flag to be cleared + uint8_t if_flag_offset - the bit position offset (for the interrupt flag to + clear) from the "right of the register" + + Return Values: + None + + Remarks: + Individual USB interrupt flag bits are cleared by writing '1' to the + bit, in a word write operation. + + *******************************************************************/ +#define USBClearInterruptFlag(reg_name, if_flag_offset) (reg_name = (1 << if_flag_offset)) + +/******************************************************************** + Function: + void DisableNonZeroEndpoints(UINT8 last_ep_num) + + Summary: + Clears the control registers for the specified non-zero endpoints + + PreCondition: + None + + Parameters: + UINT8 last_ep_num - the last endpoint number to clear. This + number should include all endpoints used in any configuration. + + Return Values: + None + + Remarks: + None + + *******************************************************************/ +#define DisableNonZeroEndpoints(last_ep_num) {\ + uint8_t i;\ + uint32_t *p = (uint32_t*)&U1EP1;\ + for(i=0;i= 1, to allow it to cause wake from sleep. + //IEC1bits.CNIE = 1; + } + + + //Stop clocks and put the microcontroller core to sleep now. Note: This is done + //in a loop, since in some wake up scenarios (ex: wake by periodic WDT timeout), + //the USB bus may still be suspended, and the device should go back to sleep. + while(1) + { + Sleep(); + + //Check wakeup source. It may have been due to USB activity from the host (ex: resume from suspend), + //or it could have been due to some other source. + if(USBActivityIF == 1) + { + //The device woke up due to some kind of USB activity. This could have been + //either due to the USB host sending resume signaling, or, it could be due + //to VBUS falling below the SESSVD threshold, due to either a USB detach event, + //or the host powering itself down fully (and thereby turning off the +5V VBUS supply). + + //Check the VBUS level. If VBUS is no longer present, then a user detach event + //must have occurred, or, the cable is still plugged in, but the host itself + //powered down. + if(USBVBUSSessionValidStateGet(true) == 0) + { + //A detach event must have occurred. You may optionally stay awake and + //continue executing code in this case, or, go back to sleep until + //the next re-attachment event. + + + //Uncomment below break statement, if you want to continue executing + //code during the detached interval. + //break; + + //If the user left the above break statement commented, then + //just clear the prior wake up event and go back to sleep. + USBClearInterruptFlag(USBActivityIFReg, USBActivityIFBitNum); + USBInterruptFlag = 0; + } + else + { + //VBUS is still above SESSVD, implying that the device just woke up + //due to the host sending resume (or other non-idle) bus signaling + //to the device. In this case, we need to fully wake up and go back + //into a state ready to receive new USB packets/commands from the host. + + break; //Exit the while(1) sleep loop, so we can restore normal execution of this firmware. + } + } + else + { + //We woke up by some means other than resume from the host. Figure out what that source was. + + //Check if the wakeup was due to the (optional) remote wakeup source, and if + //it is actually legal to perform a remote wakeup. + #if 0 + if(YOUR_REMOTE_WAKEUP_TRIGGER_SOURCE_ASSERTED) //Replace YOUR_REMOTE_WAKEUP_TRIGGER_SOURCE_ASSERTED with your proper interrupt source (such as: IFS0bits.CNAIF == 1) + { + //Clear the wakeup source interrupt status flag, since we are now consuming/processing it. + YOUR_REMOTE_WAKEUP_TRIGGER_SOURCE_FLAG = 0; //Replace with real code (ex: IFS0CLR = _IFS0_CNAIF_MASK;, etc.) + + //Try to wake up the host. + if(USBRemoteWakeupAssertBlocking() == true) + { + //It was legal to send remote wakeup signaling, and it was sent. + //Exit the while(1) loop, so we stay out of sleep. + break; + } + + } + #endif + } + } + + + //Restore all interrupt settings back to what they were before at the start + //of this function. According to the USB specs, USB devices should return + //to their exact same USB state/operating condition after the end of suspend, + //as they were in prior to entering suspend. + USBRestorePreviousInterruptSettings(); + + return true; +} + + + + + + +/******************************************************************** +Function: + bool USBRemoteWakeupAssertBlocking(void) + +Summary: + Checks if it is currently legal to send remote wakeup signaling to the + host, and if so, it sends it. This is a blocking function that takes ~5-6ms + to execute. + +PreCondition: + +Parameters: + None + +Return Values: + true - if it was legal to send remote wakeup signaling and the signaling was sent + false - if it was not legal to send remote wakeup signaling (in this case, no signaling gets sent) + +Remarks: + To successfully use remote wakeup in an application, the device must first + let the host know that it supports remote wakeup (by setting the appropriate + bit in the attributes field of the USB configuration descriptor). Additionally, + the end user must allow/enable remote wakeup for the device. Hosts are not + obligated to always allow remote wakeup, and the end user can typically enable/disable this + feature for each device at their discretion. Under Windows, devices supporting + remote wakeup will normally have an extra checkbox setting under the device + manager properties pages for the device, which the user can change. + + Additionally, remote wakeup capability requires driver support, in the USB + class drivers being used for the device on the host. Under Windows 7, the CDC + drivers do not support remote wakeup capability, while other drivers like HID + and WinUSB fully support this capability. If the intended application is + CDC or based on some other driver not supporting remote wakeup, it is potentially + possible to make the device a composite device (ex: CDC+HID) and then use the + HID driver as a means of achieving the remote wakeup effect of the host. + + Sometime prior to entering USB suspend (usually just prior), if the host and + driver(s) support remote wakeup, and the user has enabled the feature for the + device, then the host will send notification to the device firmware letting it + know that remote wakeup is legal. The USBGetRemoteWakeupStatus() API function + can be used to check if the host has allowed remote wakeup or not. The application + firmware should not send remote wakeup signaling to the host, unless both + USBGetRemoteWakeupStatus() and USBIsBusSuspended() return true. Attempting to + send the remote wakeup signaling to the host when it is not legal to do so + will normally not wake up the host, and will instead create a violation of + the USB compliance criteria. Additionally, devices should not send remote + wakeup signaling immediately upon entry into the suspended bus state. Based on + the USB specifications, the bus must be continuously idle for at least 5ms before + a device may send remote wakeup signaling. After the signaling is sent, the + device must become fully ready for normal USB communication/request + processing within 10ms. + *******************************************************************/ +bool USBRemoteWakeupAssertBlocking(void) +{ + //Make sure we are in a state where it is legal to send remote wakeup signaling + if((USBGetRemoteWakeupStatus() == true) && (USBIsBusSuspended() == true)) + { + //Make sure the USB module is not suspended. We need it to be clocked and + //fully operational. + if(USBSuspendControl == 1) + { + USBSuspendControl = 0; + } + + USBMaskAllUSBInterrupts(); //Temporarily disable USB interrupts, to prevent the stack from using/gobbling up the T1MSECIF events if USB interrupts are enabled/used. + + //USB specs require the device not send resume signaling within the first 5ms of bus idle time. + //Since it takes 3ms of continuous idle for the suspend condition to occur, + //this means the firmware must wait a minimum of two additional milliseconds + //from the point that suspend is detected before sending the resume signaling. + //To ensure that this requirement is always met, we add deliberate delay + //below, to wait 2-3ms of extra time, before starting to send the resume signaling. + USBClearInterruptFlag(USBT1MSECIFReg, USBT1MSECIFBitNum); + while(U1OTGIRbits.T1MSECIF == 0); //Wait 0-1 ms + USBClearInterruptFlag(USBT1MSECIFReg, USBT1MSECIFBitNum); + while(U1OTGIRbits.T1MSECIF == 0); //Wait 1 full ms + USBClearInterruptFlag(USBT1MSECIFReg, USBT1MSECIFBitNum); + while(U1OTGIRbits.T1MSECIF == 0); //Wait 1 full ms + + //Begin sending the actual resume K-state signaling to the host + USBResumeControl = 1; //Start RESUME signaling + + //USB specs require the RESUME signaling to persist for 1-15ms in duration. + USBClearInterruptFlag(USBT1MSECIFReg, USBT1MSECIFBitNum); + while(U1OTGIRbits.T1MSECIF == 0); //Wait 0-1 ms + USBClearInterruptFlag(USBT1MSECIFReg, USBT1MSECIFBitNum); + while(U1OTGIRbits.T1MSECIF == 0); //Wait 1 full ms + USBResumeControl = 0; //Stop driving resume signaling + + //Restore previous interrupt settings that we may have changed. + USBRestoreUSBInterrupts(); + + //We sent the signaling and the host should be waking up now. + return true; + } + + //Wasn't legal to send remote wakeup signaling to the host. We must return + //without actually doing anything. + return false; +} + + + + + +/******************************************************************** +Function: + int8_t USBVBUSSessionValidStateGet(bool AllowInvasiveReads) + +Summary: + This function tries to check the current VBUS voltage level to see if it is + above the session valid threshold or not. This is useful for detecting if the + device is currently attached to a host or hub that is actively supplying + power on the +5V VBUS net (instead of being powered down). + +PreCondition: + This function assumes that USBDeviceInit() has been called at least once, + although it is not necessary for the USB module to be already enabled, if + "invasive reads" are allowed. However, it is always the caller's responsibility + to make sure that the microcontroller system clock settings have been + pre-configured for an operating mode that are compatible with USB operation + at the right frequency (ex: the USB module must have 48MHz clocking available + internally), prior to calling this function. + +Parameters: + bool AllowInvasiveReads - Specify false if you want this function to perform + a "passive" read attempt, which will not block or change the state of the USB module. + If this parameter is false, the function can return a negative number, indicating + that the read could not be performed (ex: if the comparators are currently off). + + Specify true, if you want this function to perform a forceful read operation. + In this case, this function will turn on the USB module and/or unsuspend it if + needed, in order to read the actual value. The function will always return 0 or 1 + in this case. However, the function may block for as much time as required to + ensure that any necessary analog startup/settling/propagation times have + elapsed, so as to get an accurate reading. If invasive reads are allowed, and + this function turns on the USB module or unsuspends it, the module will remain + on and unsuspended subsequent to returning from this function. + It is the caller's responsibility to turn the USB module off if + desired/appropriate for the application (ex: because the returned value was + 0, indicating VBUS is not currently powered, in which case the application may + wish to shut things down for lower power consumption). + + +Return Values: + Returns a signed 8-bit byte indicating the current VBUS level. If VBUS is above + the session valid state (ex: because the cable is attached and a host or hub is actively + sourcing VBUS power), the return value will be 1. If the VBUS level is below + the session valid level (ex: because the cable is detached or the host/hub is + powered down and not sourcing anything on VBUS), the return value will be 0. + If the function could not conclusively determine the state of the VBUS line + (ex: because the VBUS sensing comparator wasn't powered on for example), the + return value will be a negative number (ex: return value of -1 or less, with + the actual value indicating a code representing the reason why the value + couldn't be read). + +Remarks: + This function may block for upwards of ~5ms if AllowInvasiveReads was true + and the USB module wasn't previously turned or or was recently (or is still) suspended. + + *******************************************************************/ +int8_t USBVBUSSessionValidStateGet(bool AllowInvasiveReads) +{ + unsigned char i; + int8_t retValue; + + //Check if module is already on and non-suspended + if((U1PWRCbits.USBPWR == 1) && (USBSuspendControl == 0) && (USBGetTicksSinceSuspendEnd() >= 4)) + { + return U1OTGSTATbits.SESVD; + } + else + { + //The module wasn't in a state where we can rely on the SESVD bit value yet. + if(AllowInvasiveReads == true) + { + //Turn on the module and make sure it is not suspended. + USBSuspendControl = 0; + USBPowerModule(); + + //Make sure USB interrupt vectoring is disabled, to prevent interrupt + //handler from "stealing" the T1MSECIF events and clearing the flag before returning. + USBMaskAllUSBInterrupts(); + + //Wait ~4-5ms analog settling time for bandgap and comparators to + //power up, and at least one comparator propagation delay has elapsed, + //so as to provide trustworthy output status. + for(i = 0; i < 5; i++) + { + USBClearInterruptFlag(USBT1MSECIFReg, USBT1MSECIFBitNum); + while(USBT1MSECIF == 0); //Wait for assertion + } + + //The SESVD bit value should now be trustworthy to read. + retValue = 0; + if(U1OTGSTATbits.SESVD == 1) + { + retValue = 1; + } + + //Restore normal USB interrupt settings. + USBRestoreUSBInterrupts(); + + return retValue; + } + else + { + //Couldn't read the value... Module is not in a state where the + //value can be meaningful, and the caller didn't allow us to make + //module setting changes. + + if(U1PWRCbits.USBPWR == 0) + { + return -1; //-1 indicates the USB module wasn't powered + } + else if(USBSuspendControl == 0) + { + return -2; //-2 indicates the USB module was powered, but was suspended + } + else if(USBGetTicksSinceSuspendEnd() < 4) + { + return -3; //-3 indicates the USB module was powered and not suspended, + //but that insufficient settling time has elapsed yet to + //get a trustworthy reading. + } + + //Shouldn't get here. + return -4; + } + } +} + + + + + +/******************************************************************** +Function: + void USBMaskAllUSBInterrupts(void) + +Summary: + This function saves the current USBIE bit state, and then clears it + to prevent any USB interrupt vectoring. + +PreCondition: + None + +Parameters: + None + +Return Values: + None + +Remarks: + This function should always be called in a exact 1:1 ratio with the + corresponding USBRestoreUSBInterrupts() function (which restores the + setting saved by this function). This function should not be called + more than once, prior to calling USBRestoreUSBInterrupts(), + as this will cause the previously saved value to get overwritten. + *******************************************************************/ +void USBMaskAllUSBInterrupts(void) +{ + unsigned int tempInterruptStatus; + + //Save and clear the USBIE bit to prevent USB interrupt vectoring. + tempInterruptStatus = __builtin_get_isr_state(); + __builtin_disable_interrupts(); //Disable all interrupts to make sure the USB interrupt enable state doesn't change in an interrupt handler + USBIESave = 0; + if(USBInterruptEnableBit == 1) + { + USBIESave = 1; + } + USBInterruptEnableBit = 0; //Disable USB interrupt vectoring. + + //Restore global interrupt enable status. + __builtin_set_isr_state(tempInterruptStatus); +} + + + +/******************************************************************** +Function: + void USBRestoreUSBInterrupts(void) + +Summary: + This function restores the previous USB interrupt setting that was + in effect prior to calling the corresponding USBMaskAllUSBInterrupts(). + +PreCondition: + None + +Parameters: + None + +Return Values: + None + +Remarks: + This function should only be called in an exact 1:1 ratio with the + USBMaskAllUSBInterrupts() function. This function should never be + called without first being preceded by a call to USBMaskAllUSBInterrupts(). + *******************************************************************/ +void USBRestoreUSBInterrupts(void) +{ + //Restore the previous USBIE setting, that was saved when USBMaskAllUSBInterrupts() was called. + if(USBIESave) + { + USBInterruptEnableBit = 1; + } +} + + + + + +/******************************************************************** +Function: + static void USBSaveAndPrepareInterruptsForSleep(void) + +Summary: + This is a private function that should never be called directly by + user application code. This function saves the state of all IECx interupt + enable register bits, and then clears/disables all interrupts. It then + re-enables only the very specific ones needed for wakeup from sleep due + to USB activity. + +PreCondition: + Must only be called by USB stack code in the context of a USB suspend or + USB detach event handler. + +Parameters: + None + +Return Values: + None + +Remarks: + Application code should not call or modify this function. If in the + application it is desired to wake up from sleep by additional sources, + such as for remote wakeup purposes, only these additional interrupt sources + must be enabled after returning from this function, and prior to executing + the sleep instruction. When called, this function must always be called in + an exact 1:1 ratio with the USBRestorePreviousInterruptSettings() function, in + order to restore the application interrupt settings to their previous values. + Calling this function more than one (without calling USBRestorePreviousInterruptSettings() + will result in a loss of state information. + *******************************************************************/ +static void USBSaveAndPrepareInterruptsForSleep(void) +{ + unsigned int i; + volatile unsigned int* pRegister; + + + //Save status and then disable all (maskable) interrupts + CPUIPLSave = __builtin_get_isr_state(); //Save the current interrupt enable and CPU IPL state info + __builtin_disable_interrupts(); //Disable all maskable interrupts + + //Set CPU priority level to 0, so that it can be woken up by a new USB interrupt + //(at IPL = 4, which is higher than the CPU priority) but without going to the interrupt vector location. + //Note: If the USB stack is operated in USB_INTERRUPT mode, then the current + //CPU IPL may currently be 4, since this code is already operating within the interrupt context + //of the USB interrupt (ex: IDLEIF asserted, causing the USB stack to call + //the user suspend handler, which then called the USBSleepOnSuspend() handler, + //which then called this function). In this case, we need to lower the CPU priority + //level to < USB IPL, so that the CPU can wake back up from sleep. + __builtin_set_isr_state(0); //Interrupts disabled, with CPU IPL = 0 (which is < USB IPL) + + //Now save and disable all other interrupt enable bits settings. Note: This code + //assumes all IECx registers are packed together little endian with no other registers intermingled + //(make sure this is true if using a hypothetical device where this might not be the case). + pRegister = &IEC0; + for(i = 0; i < DEVICE_SPECIFIC_IEC_REGISTER_COUNT; i++) + { + IECRegSaves[i] = *pRegister; //Save the current IECx register contents + *pRegister = 0; //Clear out the current IECx register + pRegister += 4; //Add 4 to skip pass inv/set/clr versions of the same IECx register + } + U1IE_save = U1IE; //Also save and clear USB module sub-interrupts + U1IE = 0; + U1OTGIE_save = U1OTGIE; + U1OTGIE = 0; + USBIPLSave = USBIPLBits; + + //Now enable only the USBIE + ACVIE interrupt sources, plus possible remote + //wakeup stimulus interrupt source(s) (but only when legal to perform remote wakeup), + //so that they make wake the microcontroller from sleep mode (but not to vector, due to the CP0 reg 12 global interrupt enable bit being clear) + USBInterruptFlag = 0; + USBActivityIE = 1; + USBIPLBits = 4; //Make sure USB module IPL is > 0, so it can at least wake the device from sleep + USBInterruptEnableBit = 1; //Enable the top level USB module IE bit now, to enable wake from sleep. +} + + + +/******************************************************************** +Function: + static void USBRestorePreviousInterruptSettings(void) + +Summary: + This is a private function that should never be called directly by + user application code. This function restores the state of all IECx interrupt + enable register bits that were previously modified by the + USBSaveAndPrepareInterruptsForSleep() function. + +PreCondition: + Must only be called by USB stack code in the context of a USB suspend or + USB detach event handler, after the event has ended and it is time to + restore the previous operating status. + +Parameters: + None + +Return Values: + None + +Remarks: + Application code should not call or modify this function. This function + must always be called in an exact 1:1 ratio with the + USBSaveAndPrepareInterruptsForSleep() function. + *******************************************************************/ +static void USBRestorePreviousInterruptSettings(void) +{ + unsigned int i; + volatile unsigned int* pRegister; + + //Restore the previously saved interrupt settings. + + //Restore all the previous application interrupt settings that we modified + //previously by the USBSaveAndPrepareInterruptsForSleep() function. + pRegister = &IEC0; + for(i = 0; i < DEVICE_SPECIFIC_IEC_REGISTER_COUNT; i++) + { + *pRegister = IECRegSaves[i]; + pRegister += 4; //Add 4 to skip pass inv/set/clr versions of the same IECx register + } + U1IE = U1IE_save; + U1OTGIE = U1OTGIE_save; + USBIPLBits = USBIPLSave; + + + //Restore CPU interrupt priority level to allow normal vectoring again (if they were enabled previously). + __builtin_set_isr_state(CPUIPLSave); +} + + + + +//------------------------------------------------------------------------------------------- +#endif //#if defined(__XC32__) +#endif //__USB_HAL_32BIT_C \ No newline at end of file diff --git a/src/usb_hal_local.h b/src/usb_hal_local.h index 3f44213..4c01b25 100644 --- a/src/usb_hal_local.h +++ b/src/usb_hal_local.h @@ -30,14 +30,12 @@ please contact mla_licensing@microchip.com #if defined (__dsPIC33EP512MU810__) #include #endif -#elif defined (__PIC32MX__) - #include - #include "usb_pic32.h" +#elif defined (__PIC32MX__) || defined (__PIC32MM__) + #include #else #error "Error! Unsupported processor" #endif - // Misc Definitions: #ifndef USB_DEVICE_ATTACH_DEBOUNCE_TIME diff --git a/src/usb_host.c b/src/usb_host.c index 09ec8d9..5747f16 100644 --- a/src/usb_host.c +++ b/src/usb_host.c @@ -77,7 +77,7 @@ please contact mla_licensing@microchip.com //****************************************************************************** // When using the PIC32, ping pong mode must be set to FULL. -#if defined (__PIC32MX__) +#if defined (__PIC32__) #if (USB_PING_PONG_MODE != USB_PING_PONG__FULL_PING_PONG) #undef USB_PING_PONG_MODE #define USB_PING_PONG_MODE USB_PING_PONG__FULL_PING_PONG @@ -1229,7 +1229,7 @@ void USBHostTasks( void ) // The PIC32MX detach interrupt is not reliable. If we are not in one of // the detached states, we'll do a check here to see if we've detached. // If the ATTACH bit is 0, we have detached. - #ifdef __PIC32MX__ + #ifdef __PIC32__ #ifdef USE_MANUAL_DETACH_DETECT if (((usbHostState & STATE_MASK) != STATE_DETACHED) && !U1IRbits.ATTACHIF) { @@ -1249,7 +1249,7 @@ void USBHostTasks( void ) USB_EVENT_DATA *item; #if defined( __C30__ ) || defined __XC16__ uint16_t interrupt_mask; - #elif defined( __PIC32MX__ ) + #elif defined( __PIC32__ ) uint32_t interrupt_mask; #else #error Cannot save interrupt status @@ -1345,7 +1345,7 @@ void USBHostTasks( void ) // Initialize the Buffer Descriptor Table pointer. #if defined(__C30__) || defined __XC16__ U1BDTP1 = (uint16_t)(&BDT) >> 8; - #elif defined(__PIC32MX__) + #elif defined(__PIC32__) U1BDTP1 = ((uint32_t)KVA_TO_PA(&BDT) & 0x0000FF00) >> 8; U1BDTP2 = ((uint32_t)KVA_TO_PA(&BDT) & 0x00FF0000) >> 16; U1BDTP3 = ((uint32_t)KVA_TO_PA(&BDT) & 0xFF000000) >> 24; @@ -1363,7 +1363,7 @@ void USBHostTasks( void ) U1OTGCON = USB_DPLUS_PULLDOWN_ENABLE | USB_DMINUS_PULLDOWN_ENABLE; // Pull down D+ and D- #endif - #if defined(__PIC32MX__) + #if defined(__PIC32__) U1OTGCON |= USB_VBUS_ON; #endif @@ -1403,9 +1403,9 @@ void USBHostTasks( void ) IPC21 &= 0xF0FF; IPC21 |= 0x0600; IEC5 |= 0x0040; - #elif defined( __PIC32MX__ ) + #elif defined( __PIC32__ ) // Enable the USB interrupt. - IFS1CLR = _IFS1_USBIF_MASK; + _ClearUSBIF(); #if defined(_IPC11_USBIP_MASK) IPC11CLR = _IPC11_USBIP_MASK | _IPC11_USBIS_MASK; IPC11SET = _IPC11_USBIP_MASK & (0x00000004 << _IPC11_USBIP_POSITION); @@ -1415,7 +1415,7 @@ void USBHostTasks( void ) #else #error "The selected PIC32 device is not currently supported by usb_host.c." #endif - IEC1SET = _IEC1_USBIE_MASK; + _SetUSBIE(); #else #error Cannot enable USB interrupt. #endif @@ -5281,7 +5281,7 @@ void _USB_SetBDT( uint8_t token ) // Load up the BDT address. if (token == USB_TOKEN_SETUP) { - #if defined(__C30__) || defined(__PIC32MX__) || defined __XC16__ + #if defined(__C30__) || defined(__PIC32__) || defined __XC16__ pBDT->ADR = ConvertToPhysicalAddress(pCurrentEndpoint->pUserDataSETUP); #else #error Cannot set BDT address. @@ -5298,7 +5298,7 @@ void _USB_SetBDT( uint8_t token ) { pBDT->ADR = ConvertToPhysicalAddress((uint16_t)pCurrentEndpoint->pUserData + (uint16_t)pCurrentEndpoint->dataCount); } - #elif defined(__PIC32MX__) + #elif defined(__PIC32__) if (pCurrentEndpoint->bmAttributes.bfTransferType == USB_TRANSFER_TYPE_ISOCHRONOUS) { pBDT->ADR = ConvertToPhysicalAddress(((ISOCHRONOUS_DATA *)(pCurrentEndpoint->pUserData))->buffers[((ISOCHRONOUS_DATA *)(pCurrentEndpoint->pUserData))->currentBufferUSB].pBuffer); @@ -5426,8 +5426,8 @@ void USB_HostInterruptHandler(void) #if defined( __C30__) || defined __XC16__ IFS5 &= 0xFFBF; - #elif defined( __PIC32MX__) - IFS1CLR = _IFS1_USBIF_MASK; + #elif defined( __PIC32__) + _ClearUSBIF(); #else #error Cannot clear USB interrupt. #endif @@ -5650,7 +5650,7 @@ void USB_HostInterruptHandler(void) { #if defined(__C30__) || defined __XC16__ U1STATBITS copyU1STATbits; - #elif defined(__PIC32MX__) + #elif defined(__PIC32__) __U1STATbits_t copyU1STATbits; #else #error Need structure name for copyU1STATbits. @@ -6006,7 +6006,7 @@ void USB_HostInterruptHandler(void) pCurrentEndpoint->bErrorCode = USB_ENDPOINT_ERROR_END_OF_FRAME; if (U1EIRbits.PIDEF) pCurrentEndpoint->bErrorCode = USB_ENDPOINT_ERROR_PID_CHECK; - #if defined(__PIC32MX__) + #if defined(__PIC32__) if (U1EIRbits.BMXEF) pCurrentEndpoint->bErrorCode = USB_ENDPOINT_ERROR_BMX; #endif diff --git a/src/usb_host_local.h b/src/usb_host_local.h index bb23d45..6e47a79 100644 --- a/src/usb_host_local.h +++ b/src/usb_host_local.h @@ -348,7 +348,7 @@ HOLDING state machine values #define USB_RESET_TIME (50+1) // RESET signaling time - 50ms #if defined( __C30__ ) || defined __XC16__ #define USB_RESET_RECOVERY_TIME (10+1) // RESET recovery time. -#elif defined( __PIC32MX__ ) +#elif defined( __PIC32__ ) #define USB_RESET_RECOVERY_TIME (100+1) // RESET recovery time - Changed to 100 ms from 10ms. Some devices take longer. #else #error Unknown USB_RESET_RECOVERY_TIME