18F87J50USB
This is an updated version of the USB HID module supplied with the compiler. This version allows the use of the USB module on the new Microchip PIC18F87J50 3.3v part.
The example program shown here is a modification of the basic HID example supplied with the Compiler Help File. It will interact with the HID example program available from here & was developed for the Microchip PIC18F87J50 Full Speed USB Plug-In Module (Part Number: MA180021). The use of the new Mircochip USB HID bootloader is a highly recommended (updated 17/11/08 to correct Bootloader Descriptor Strings issue).
You can download the modified files from here (plus they are listed below for safety). You will need to place them into your UserLibrary Folder to ensure they are used by the compiler, and to ensure they are not overwritten in an update.
Please note
Although the 18F87J50 has 4096 bytes of RAM; due to the part having its USB buffers in RAM BANK 4 for compatibility, Swordfish can only allocate 1024 bytes of RAM automatically (and will report as such on compile).
You can however safely utilize memory above RAM BANK 7 ($0800 hex address and above, up to $0EFF) by directly allocating the memory yourself.
DIM BigBuffer(1000) as byte absolute $0800 DIM SecondBuffer(100) as byte absolute $0BE8
The limitation of this is that you need to keep track of the size of each variable to calculate the correct address for the next one. So probably the best use for this RAM is for large arrays or structures.
Todos
- At present, the module is hardcoded to use the built-in transceiver & internal USB pullups, due to an issue with the compiler addressing UCFG on this part.
Thanks
Big thanks to Nigle for pointing out how to use the high RAM area & a work around for UCFG. Thanks to richardb and Huge thanks to David Barker for this great compiler and for getting the ICD working - without it I was just thrashing around in the dark.
Example Code
{ ***************************************************************************** * Name : 18F87J50HIDExample.BAS * * Author : Nathan Herbert * * Notice : Copyright (c) 2008 Nathan (Rangerbob) * * : All Rights Reserved * * Date : 17/11/2008 * * Version : 1.1 * * Notes : Modified USB HID example from the Swordfish Help file * * : To show support for 18F87J50 part * * : Use with Mechanique HIDExample.exe & 18F87J50 PIM * * : 1.1 - Fix for bootloader Descriptor strings issue * ***************************************************************************** } // device and clock... Device = 18F87J50 Clock = 48 // 12Mhz crystal on 18F87J50 Board Config xinst = off, stvren = on, PLLDIV = 3, wdten = off, cp0 = off, CPUDIV = OSC1, ieso = off, fcmen = off, FOSC = HSPLL, wdtps = 32768, msspmsk = msk5, ccp2mx = default, wait = off, bw = 16, mode = mm, eashft = off, pmpmx = default, eccpmx = default // Enable this option to use with the Microchip USB HID bootloader '#option org_reset = $1000 // Fix for Bootloader Descriptor Strings Issue - Ensure this is first include Include "ClearRegisters.bas" // import the HID module... Include "usbhid.bas" // TX report... Structure TTXReport Time As Word Message As String End Structure Dim TXReport As TTXReport Absolute TXReportRAM // RX report... Structure TRXReport LED0 As Bit LED1 As Bit End Structure Dim RXReport As TRXReport Absolute RXReportRAM // alias port pins to LEDs... // Porte.0 & Porte.1 available as LEDs on 18F87J50 PIM module Dim LED0 As PORTE.0, LED1 As PORTE.1 // initialise... // Set all digital //Configure all I/O pins to use digital input buffers. The PIC18F87J50 Family devices //use the ANCONx registers to control this, which is different from other devices which //use the ADCON1 register for this purpose. WDTCON.4 = 1 // selcet alternate sfr locations ANCON0 = $FF ANCON1 = $FF WDTCON.4 = 0 // select normal sfr locations TXReport.Time = 0 Low(LED0) Low(LED1) // connect to USB... Repeat Until Attached // main program loop... While true // if we have data, set port values, update message // and then reset time counter... If DataAvailable Then ReadReport LED0 = RXReport.LED0 LED1 = RXReport.LED1 TXReport.Message = "PORT CHANGED" WriteReport TXReport.Time = 0 DelayMS(100) // no data, set waiting message... Else TXReport.Message = "WAITING..." WriteReport Inc(TXReport.Time) EndIf Wend
Module Code
ClearRegisters.bas
{ ***************************************************************************** * Name : ClearRegisters.BAS * * Author : Nathan Herbert * * Notice : * * : All Rights Reserved * * Date : 17/11/2008 * * Version : 1.0 * * Notes : Use with Microchip HID bootloader to ensure correct operation * * : of main program * ***************************************************************************** } Module ClearRegisters #if _device = 18F87J50 TBLPTR = %00000000 TABLAT = %00000000 INTCON = %00000000 INTCON2 = %11111111 INTCON3 = %11000000 TMR0H = %00000000 T0CON = %11111111 CM1CON = %00011111 CM2CON = %00011111 ODCON1 = %00000000 ODCON2 = %00000000 T1CON = %00000000 ODCON3 = %00000000 TMR2 = %00000000 T2CON = %00000000 IPR3 = %11111111 PIR3 = %00000000 PIE3 = %00000000 IPR2 = %11111111 PIR2 = %00000000 PIE2 = %00000000 IPR1 = %11111111 PIR1 = %00000000 PIE1 = %00000000 UCON = %00000000 UEIR = %00000000 UIR = %00000000 UCFG = %00000000 UEIE = %00000000 UIE = %00000000 #endif
USBHID.bas
{ ***************************************************************************** * Name : USBHID.bas * * Author : David John Barker * * Notice : Copyright (c) 2007 Mecanique * * : All Rights Reserved * * Date : 15/09/2008 * * Version : 1.1a Branch by Nathan(RangerBob) for 18F87J50 Support * * : 1.1 ISR now uses USB interrupts * * : Renamed 'Connected' to 'Attached' * * : 1.0 Release * * Notes : This HID module provides a simple way for you to transfer * * : blocks of data to and from your PC using the USB HID class. * ***************************************************************************** } Module HID // import USB modules... #option USB_USE_HID = true Include USB_DESCRIPTOR Include "usbsfr.bas" Include "usbdefs.bas" Include "usbsystem.bas" Include "system.bas" // interrupt driven service fired every 1 millisecond... Const PriorityLevel = USB_SERVICE_PRIORITY // ISR priority // Public buffers - these are just mapped onto USB RAM space, thus saving // program RAM. You can use a large 256 byte buffer, which can be useful // for trasnferring large blocks of data using ReadArray() and WriteArray(). // The RX and TX buffers match the HID report size, and are typically use with // ReaReport() and WriteReport(). However, they can also be used with ReadArray() // and WriteArray(). Note that these buffers are only available for USB devices // that have extended dual port USB RAM... #if USB_EXTENDED_RAM Public Const BufferRAM = $600, TXReportRAM = BufferRAM, RXReportRAM = BufferRAM + HIDReportBytesIn Public Dim Buffer(256) As Byte Absolute BufferRAM, TXReport(HIDReportBytesIn) As Byte Absolute TXReportRAM, RXReport(HIDReportBytesOut) As Byte Absolute RXReportRAM #endif Public Dim Attached As USBSystem.Attached { **************************************************************************** * Name : Service * * Purpose : Service a USB connection. You need to call this routine * * : periodically from your main program to maintain a connection * * : (every 1 ms or so). You DO NOT need to call this routine if * * : you have enabled the OnService() interrupt handler (it is on * * : by default) * **************************************************************************** } Public Inline Sub Service() USBCheckBusStatus USBDriverService ClrWDT End Sub { **************************************************************************** * Name : NoISRService * * Purpose : Private service helper routine for maintain USB connection * * : for some blocking calls * **************************************************************************** } Inline Sub NoISRService() #if Not USB_SERVICE Service #endif End Sub { **************************************************************************** * Name : OnService (PRIVATE) * * Purpose : USB service event handler * **************************************************************************** } #if USB_SERVICE Interrupt OnService(PriorityLevel) PIR2Bits.USBIF = 0 Save(FSR0, FSR1, Service) Service Restore End Interrupt #endif { **************************************************************************** * Name : EnableISR * * Purpose : Enable the ISR to service USB connection * **************************************************************************** } Public Inline Sub EnableISR() #if USB_SERVICE Enable(OnService) #endif End Sub { **************************************************************************** * Name : DisableISR * * Purpose : Disable the ISR that services the USB connection * **************************************************************************** } Public Inline Sub DisableISR() #if USB_SERVICE Disable(OnService) #endif End Sub { **************************************************************************** * Name : WaitForTX (PRIVATE) * * Purpose : Blocks processing until USB TX buffer is available * **************************************************************************** } Sub WaitForTX() While mHIDTxIsBusy <> 0 And Attached Service Wend End Sub { **************************************************************************** * Name : DataAvailable * * Purpose : Returns true if RX data is available, false otherwise * **************************************************************************** } Public Function DataAvailable() As Boolean DisableISR Result = (mHIDRxIsBusy = 0) EnableISR End Function { **************************************************************************** * Name : ReadReport * * Purpose : Read a HID report into RXReport buffer * **************************************************************************** } #if USB_EXTENDED_RAM Public Sub ReadReport() DisableISR While mHIDRxIsBusy <> 0 And Attached Service Wend HIDRxReport(@RXReport, HIDReportBytesOut) NoISRService EnableISR End Sub { **************************************************************************** * Name : WriteReport * * Purpose : Write the TXReport buffer * **************************************************************************** } Public Sub WriteReport() DisableISR WaitForTX HIDTxReport(@TXReport, HIDReportBytesIn) NoISRService EnableISR End Sub #endif { **************************************************************************** * Name : ReadArray * * Purpose : Read an array of bytes - pCount is an optional number of bytes * * : to read. If pCount is not given, the size of the array is used * **************************************************************************** } Public Sub ReadArray(ByRef pBuffer() As Byte, pCount As Word = 0) Dim BytesRead As Byte Dim Buffer As Word Buffer = @pBuffer If pCount = 0 Then pCount = Bound(pBuffer) + 1 EndIf DisableISR While pCount > 0 And Attached // wait for data If mHIDRxIsBusy = 0 Then // pCount > max report size If pCount > HIDReportBytesOut Then BytesRead = HIDRxReport(Buffer, HIDReportBytesOut) Dec(pCount, BytesRead) Inc(Buffer, BytesRead) // pCount <= max report size Else BytesRead = HIDRxReport(Buffer, pCount.Byte0) Dec(pCount.Byte0, BytesRead) Inc(Buffer, BytesRead) EndIf EndIf Service Wend EnableISR End Sub { **************************************************************************** * Name : WriteArray * * Purpose : Write an array of bytes - pCount is an optional number of * * : to write. If pCount is not given, the array size is used * * : Bytes are transfered in HIDReportBytesIn sized packets - 8, 16 * * : 32 or 64 bytes. If you pass an array which is only dimensioned * * : 4 bytes (for example, DIM Array(4) AS BYTE then a * * : 'HIDReportBytesIn' sized data packet is still transferred. * * : This isn't really a problem, as the host application should * * : know what to do with the data anyway. * **************************************************************************** } Public Sub WriteArray(ByRef pBuffer() As Byte, pLength As Word = 0) Dim Buffer As Word Buffer = @pBuffer If pLength = 0 Then pLength = Bound(pBuffer) + 1 EndIf DisableISR While (pLength > HIDReportBytesIn) And Attached WaitForTX HIDTxReport(Buffer, HIDReportBytesIn) Dec(pLength, HIDReportBytesIn) Inc(Buffer, HIDReportBytesIn) Service Wend If pLength.Byte0 > 0 Then WaitForTX HIDTxReport(Buffer, HIDReportBytesIn) Service EndIf EnableISR End Sub { **************************************************************************** * Name : Initialize * * Purpose : Initialize the USB module * **************************************************************************** } Sub Initialize() #if _device = 18F87J50 //On the PIC18F87J50 Family of USB microcontrollers, the PLL will not power up and be enabled //by default, even if a PLL enabled oscillator configuration is selected (such as HS+PLL). //This allows the device to power up at a lower initial operating frequency, which can be //advantageous when powered from a source which is not gauranteed to be adequate for 48MHz //operation. On these devices, user firmware needs to manually set the OSCTUNE<PLLEN> bit to //power up the PLL. //Enable the PLL and wait 2+ms until the PLL locks before enabling USB module OSCTUNE.6 = 1 DelayMS(2) #endif mInitializeUSBDriver() #if USB_SERVICE #if USB_SERVICE_PRIORITY = ipLow #if _device = 18F87J50 IPR2.4 = 0 #else IPR2.5 = 0 #endif #endif PIR2Bits.USBIF = 0 // clear USBIF PIE2Bits.USBIE = 1 // enable USB Int Service UIE = $7F // Enable all interrupts UEIE = $9F // Unmask all USB error interrupts Enable(OnService) #endif End Sub // initialize the module... Initialize
USBSFR.bas
{ ***************************************************************************** * Name : USBSFR.bas * * Author : David John Barker * * Notice : Copyright (c) 2007 Mecanique * * : All Rights Reserved * * Date : 15/09/2008 * * Version : 1.0a Branch by Nathan(RangerBob) for 18F87J50 Support * * : 1.0 * * Notes : This module implements SFRs as structures, which allows you to * * : refer to bits using their respective datasheet names. For * * : example, you can use something like UIRBits.STALLIF = 0. Note * * : that if you want to access as a complete byte, you should use * * : UIRBits._byte = $00 - this is because the compiler sees the * * : variables as structures and not bytes * ***************************************************************************** } Module USBSFR // UFRML Structure TUFRML _byte As Byte FRM0 As _byte.0 FRM1 As _byte.1 FRM2 As _byte.2 FRM3 As _byte.3 FRM4 As _byte.4 FRM5 As _byte.5 FRM6 As _byte.6 FRM7 As _byte.7 End Structure // UFRMH Structure TUFRMH _byte As Byte FRM8 As _byte.0 FRM9 As _byte.1 FRM10 As _byte.2 End Structure // UIR Structure TUIR _byte As Byte URSTIF As _byte.0 UERRIF As _byte.1 ACTVIF As _byte.2 TRNIF As _byte.3 IDLEIF As _byte.4 STALLIF As _byte.5 SOFIF As _byte.6 End Structure // UIE Structure TUIE _byte As Byte URSTIE As _byte.0 UERRIE As _byte.1 ACTVIE As _byte.2 TRNIE As _byte.3 IDLEIE As _byte.4 STALLIE As _byte.5 SOFIE As _byte.6 End Structure // UEIR Structure TUEIR _byte As Byte PIDEF As _byte.0 CRC5EF As _byte.1 CRC16EF As _byte.2 DFN8EF As _byte.3 BTOEF As _byte.4 BTSEF As _byte.7 End Structure // UEIE Structure TUEIE _byte As Byte PIDEE As _byte.0 CRC5EE As _byte.1 CRC16EE As _byte.2 DFN8EE As _byte.3 BTOEE As _byte.4 BTSEE As _byte.7 End Structure // USTAT Structure TUSTAT _byte As Byte PPBI As _byte.1 DIR As _byte.2 ENDP0 As _byte.3 ENDP1 As _byte.4 ENDP2 As _byte.5 ENDP3 As _byte.6 End Structure // UCON Structure TUCON _byte As Byte SUSPND As _byte.1 RESUME As _byte.2 USBEN As _byte.3 PKTDIS As _byte.4 SE0 As _byte.5 PPBRST As _byte.6 End Structure // UADDR Structure TUADDR _byte As Byte ADDR0 As _byte.0 ADDR1 As _byte.1 ADDR2 As _byte.2 ADDR3 As _byte.3 ADDR4 As _byte.4 ADDR5 As _byte.5 ADDR6 As _byte.6 End Structure // UCFG Structure TUCFG _byte As Byte PPB0 As _byte.0 PPB1 As _byte.1 FSEN As _byte.2 UTRDIS As _byte.3 UPUEN As _byte.4 ' UOEMON As _byte.6 UTEYE As _byte.7 End Structure // UEP Structure TUEP _byte As Byte EPSTALL As _byte.0 EPINEN As _byte.1 EPOUTEN As _byte.2 EPCONDIS As _byte.3 EPHSHK As _byte.4 End Structure #if _device = 18F87J50 // PIR2 Structure TPIR2 _byte As Byte CCP2IF As _byte.0 TMR3IF As _byte.1 LVDIF As _byte.2 BCLIF As _byte.3 USBIF As _byte.4 CMIF As _byte.5 OSCFIF As _byte.7 HLVDIF As _byte.2 End Structure #else // PIR2 Structure TPIR2 _byte As Byte CCP2IF As _byte.0 TMR3IF As _byte.1 LVDIF As _byte.2 BCLIF As _byte.3 EEIF As _byte.4 USBIF As _byte.5 CMIF As _byte.6 OSCFIF As _byte.7 HLVDIF As _byte.2 End Structure #endif // INTCON Structure TINTCON _byte As Byte RBIF As _byte.0 INT0IF As _byte.1 TMR0IF As _byte.2 RBIE As _byte.3 INT0IE As _byte.4 TMR0IE As _byte.5 PEIE As _byte.6 GIE As _byte.7 INT0F As _byte.1 T0IF As _byte.2 INT0E As _byte.4 T0IE As _byte.5 GIEL As _byte.6 GIEH As _byte.7 End Structure #if _device = 18F87J50 // PIE2 Structure TPIE2 _byte As Byte CCP2IE As _byte.0 TMR3IE As _byte.1 LVDIE As _byte.2 BCLIE As _byte.3 USBIE As _byte.4 CMIE As _byte.5 OSCFIE As _byte.7 HLVDIE As _byte.2 End Structure #else // PIE2 Structure TPIE2 _byte As Byte CCP2IE As _byte.0 TMR3IE As _byte.1 LVDIE As _byte.2 BCLIE As _byte.3 EEIE As _byte.4 USBIE As _byte.5 CMIE As _byte.6 OSCFIE As _byte.7 HLVDIE As _byte.2 End Structure #endif // public aliases... #if _device = 18F87J50 Public Dim UFRMLBits As TUFRML Absolute $0F60, UFRMHBits As TUFRMH Absolute $0F61, UIRBits As TUIR Absolute $0F62, UIEBits As TUIE Absolute $0F5C, UEIRBits As TUEIR Absolute $0F63, UEIEBits As TUEIE Absolute $0F5D, USTATBits As TUSTAT Absolute $0F64, UCONBits As TUCON Absolute $0F65, UADDRBits As TUADDR Absolute $0F5E, UCFGBits As TUCFG Absolute $0F5F, UEP0Bits As TUEP Absolute $0F4C, UEP1Bits As TUEP Absolute $0F4D, UEP2Bits As TUEP Absolute $0F4E, UEP3Bits As TUEP Absolute $0F4F, UEP4Bits As TUEP Absolute $0F50, UEP5Bits As TUEP Absolute $0F51, UEP6Bits As TUEP Absolute $0F52, UEP7Bits As TUEP Absolute $0F53, UEP8Bits As TUEP Absolute $0F54, UEP9Bits As TUEP Absolute $0F55, UEP10Bits As TUEP Absolute $0F56, UEP11Bits As TUEP Absolute $0F57, UEP12Bits As TUEP Absolute $0F58, UEP13Bits As TUEP Absolute $0F59, UEP14Bits As TUEP Absolute $0F5A, UEP15Bits As TUEP Absolute $0F5B, PIE2Bits As TPIE2 Absolute $0FA0, PIR2Bits As TPIR2 Absolute $0FA1, INTCONBits As TINTCON Absolute $0FF2 #else Public Dim UFRMLBits As TUFRML Absolute $0F66, UFRMHBits As TUFRMH Absolute $0F67, UIRBits As TUIR Absolute $0F68, UIEBits As TUIE Absolute $0F69, UEIRBits As TUEIR Absolute $0F6A, UEIEBits As TUEIE Absolute $0F6B, USTATBits As TUSTAT Absolute $0F6C, UCONBits As TUCON Absolute $0F6D, UADDRBits As TUADDR Absolute $0F6E, UCFGBits As TUCFG Absolute $0F6F, UEP0Bits As TUEP Absolute $0F70, UEP1Bits As TUEP Absolute $0F71, UEP2Bits As TUEP Absolute $0F72, UEP3Bits As TUEP Absolute $0F73, UEP4Bits As TUEP Absolute $0F74, UEP5Bits As TUEP Absolute $0F75, UEP6Bits As TUEP Absolute $0F76, UEP7Bits As TUEP Absolute $0F77, UEP8Bits As TUEP Absolute $0F78, UEP9Bits As TUEP Absolute $0F79, UEP10Bits As TUEP Absolute $0F7A, UEP11Bits As TUEP Absolute $0F7B, UEP12Bits As TUEP Absolute $0F7C, UEP13Bits As TUEP Absolute $0F7D, UEP14Bits As TUEP Absolute $0F7E, UEP15Bits As TUEP Absolute $0F7F, PIE2Bits As TPIE2 Absolute $0FA0, PIR2Bits As TPIR2 Absolute $0FA1, INTCONBits As TINTCON Absolute $0FF2 #endif
USBSystem.bas
{ ***************************************************************************** * Name : USBSystem.Bas * * Author : David John Barker * * Notice : Copyright (c) 2007 Mecanique * * : All Rights Reserved * * Date : 15/09/2008 * * Version : 1.2a Branch by Nathan(RangerBob) for 18F87J50 Support * * : 1.2 Added GetPID() to correctly mask BD_STAT PID * * : Added string index check in USBStdGetDscHandler() * * : Added 'Attached' flag * * : 1.1 Fixed RAM allocation for smaller USB devices * * : 1.0 Release * * Notes : This module is based on the Microchip USB C18 Firmware Version * * : 1.0 by Rawin Rojvanit. Whilst many of the routines could be * * : optimised for the Swordfish compiler, it was felt that * * : maintaining the code as close to possible to the original C * * : version would make it much easier to make changes and update * * : the firmware. In addition, all the documentation relating to * * : the original version can be easily mapped to this module * * : -------------------------------------------------------------- * * : IT IS STRONGLY advised you do not make changes to this module * * : unless you are familiar with USB or the original Microchip * * : firmware * ***************************************************************************** } Module USBSystem // import modules... Include "USBConfig.bas" // configurations Include "USBSFR.bas" // USB Special Function Registers (SFR) Include "USBDefs.bas" // constants, structures etc Include USB_DESCRIPTOR // descriptor file // general module variables... Dim 'usbmap.c usb_device_state As Byte, // Device States: DETACHED, ATTACHED, ... usb_stat As USB_DEVICE_STATUS, // Global USB flags usb_active_cfg As Byte, // Value of current configuration usb_alt_intf(MAX_NUM_INT) As Byte, // Array to keep track of the current alternate setting for each interface ID usb_Attached As Boolean // device attached flag // public... Public Dim Attached As usb_Attached // USB device attached flag // fixed location variables // Cannot Modify MAXRAM or USB_RAM_EP as 18F87J50 uses bank 4 for USB buffers - to look at #if _device = 18F87J50 #if USB_EXTENDED_RAM #variable _maxram = $0400 #else #variable _maxram = $0200 #endif #define EP_SIZE = 4 // endpoint size (byte) #define USB_RAM_EP = $400 // USB RAM start location #else #if USB_EXTENDED_RAM #variable _maxram = $0400 #else #variable _maxram = $0200 #endif #define EP_SIZE = 4 // endpoint size (byte) #define USB_RAM_EP = $400 // USB RAM start location #endif // endpoint 0, OUT and IN #if(0 <= MAX_EP_NUMBER) Dim 'usbmap.c ep0Bo As BDT Absolute (USB_RAM_EP + EP_SIZE * 0), ep0Bi As BDT Absolute (USB_RAM_EP + EP_SIZE * 1) #endif // endpoint 1, OUT and IN #if(1 <= MAX_EP_NUMBER) Dim 'usbmap.c ep1Bo As BDT Absolute (USB_RAM_EP + EP_SIZE * 2), ep1Bi As BDT Absolute (USB_RAM_EP + EP_SIZE * 3) #endif // endpoint 2, OUT and IN #if(2 <= MAX_EP_NUMBER) Dim 'usbmap.c ep2Bo As BDT Absolute (USB_RAM_EP + EP_SIZE * 4), ep2Bi As BDT Absolute (USB_RAM_EP + EP_SIZE * 5) #endif // endpoint 3, OUT and IN #if(3 <= MAX_EP_NUMBER) Dim 'usbmap.c ep3Bo As BDT Absolute (USB_RAM_EP + EP_SIZE * 6), ep3Bi As BDT Absolute (USB_RAM_EP + EP_SIZE * 7) #endif // endpoint 4, OUT and IN #if(4 <= MAX_EP_NUMBER) Dim 'usbmap.c ep4Bo As BDT Absolute (USB_RAM_EP + EP_SIZE * 8), ep4Bi As BDT Absolute (USB_RAM_EP + EP_SIZE * 9) #endif // endpoint 5, OUT and IN #if(5 <= MAX_EP_NUMBER) Dim 'usbmap.c ep5Bo As BDT Absolute (USB_RAM_EP + EP_SIZE * 10), ep5Bi As BDT Absolute (USB_RAM_EP + EP_SIZE * 11) #endif // endpoint 6, OUT and IN #if(6 <= MAX_EP_NUMBER) Dim 'usbmap.c ep6Bo As BDT Absolute (USB_RAM_EP + EP_SIZE * 12), ep6Bi As BDT Absolute (USB_RAM_EP + EP_SIZE * 13) #endif // endpoint 7, OUT and IN #if(7 <= MAX_EP_NUMBER) Dim 'usbmap.c ep7Bo As BDT Absolute (USB_RAM_EP + EP_SIZE * 14), ep7Bi As BDT Absolute (USB_RAM_EP + EP_SIZE * 15) #endif // endpoint 8, OUT and IN #if(8 <= MAX_EP_NUMBER) Dim 'usbmap.c ep8Bo As BDT Absolute (USB_RAM_EP + EP_SIZE * 16), ep8Bi As BDT Absolute (USB_RAM_EP + EP_SIZE * 17) #endif // endpoint 9, OUT and IN #if(9 <= MAX_EP_NUMBER) Dim 'usbmap.c ep9Bo As BDT Absolute (USB_RAM_EP + EP_SIZE * 18), ep9Bi As BDT Absolute (USB_RAM_EP + EP_SIZE * 19) #endif // endpoint 10, OUT and IN #if(10 <= MAX_EP_NUMBER) Dim 'usbmap.c ep10Bo As BDT Absolute (USB_RAM_EP + EP_SIZE * 20), ep10Bi As BDT Absolute (USB_RAM_EP + EP_SIZE * 21) #endif // endpoint 11, OUT and IN #if(11 <= MAX_EP_NUMBER) Dim 'usbmap.c ep11Bo As BDT Absolute (USB_RAM_EP + EP_SIZE * 22), ep11Bi As BDT Absolute (USB_RAM_EP + EP_SIZE * 23) #endif // endpoint 12, OUT and IN #if(12 <= MAX_EP_NUMBER) Dim 'usbmap.c ep12Bo As BDT Absolute (USB_RAM_EP + EP_SIZE * 24), ep12Bi As BDT Absolute (USB_RAM_EP + EP_SIZE * 25) #endif // endpoint 13, OUT and IN #if(13 <= MAX_EP_NUMBER) Public Dim 'usbmap.c ep13Bo As BDT Absolute (USB_RAM_EP + EP_SIZE * 26), ep13Bi As BDT Absolute (USB_RAM_EP + EP_SIZE * 27) #endif // endpoint 14, OUT and IN #if(14 <= MAX_EP_NUMBER) Dim 'usbmap.c ep14Bo As BDT Absolute (USB_RAM_EP + EP_SIZE * 28), ep14Bi As BDT Absolute (USB_RAM_EP + EP_SIZE * 29) #endif // endpoint 15, OUT and IN #if(15 <= MAX_EP_NUMBER) Dim 'usbmap.c ep15Bo As BDT Absolute (USB_RAM_EP + EP_SIZE * 30), ep15Bi As BDT Absolute (USB_RAM_EP + EP_SIZE * 31) #endif // located after enpoints... #define USB_RAM_BUFF = USB_RAM_EP + EP_SIZE * (MAX_EP_NUMBER + 1) * 2 Dim 'usbmap.c SetupPkt As CTRL_TRF_SETUP Absolute USB_RAM_BUFF, CtrlTrfData As CTRL_TRF_DATA Absolute (USB_RAM_BUFF + USB_EP0_BUFF_SIZE) // HID - located after endpoint buffers... #if USB_USE_HID #define USB_RAM_HID = USB_RAM_BUFF + USB_EP0_BUFF_SIZE * 2 Dim 'usbmap.c hid_report_out(HID_INT_OUT_EP_SIZE) As Byte Absolute USB_RAM_HID, hid_report_in(HID_INT_IN_EP_SIZE) As Byte Absolute (USB_RAM_HID + HID_INT_OUT_EP_SIZE), hid_report_feature(USB_EP0_BUFF_SIZE) As Byte Absolute (USB_RAM_HID + HID_INT_OUT_EP_SIZE + HID_INT_IN_EP_SIZE) Dim 'usbcfg.h HID_UEP As UEP1, HID_BD_OUT As ep1Bo, HID_BD_IN As ep1Bi #endif // USB_USE_HID // CDC - located after endpoint buffers... #if USB_USE_CDC #define USB_RAM_CDC = USB_RAM_BUFF + USB_EP0_BUFF_SIZE * 2 Dim 'usbmap.c cdc_notice(CDC_INT_EP_SIZE) As Byte Absolute USB_RAM_CDC, cdc_data_rx(CDC_BULK_OUT_EP_SIZE) As Byte Absolute (USB_RAM_CDC + CDC_INT_EP_SIZE), cdc_data_tx(CDC_BULK_IN_EP_SIZE) As Byte Absolute (USB_RAM_CDC + CDC_INT_EP_SIZE + CDC_BULK_OUT_EP_SIZE) Dim CDC_COMM_UEP As UEP2, 'usbcfg.h CDC_INT_BD_IN As ep2Bi, CDC_DATA_UEP As UEP3, CDC_BULK_BD_OUT As ep3Bo, CDC_BULK_BD_IN As ep3Bi #endif // USB_USE_CDC Dim 'usbctrltr.h and usbctrltr.c ctrl_trf_state As Byte, // control transfer state ctrl_trf_session_owner As Byte, // session owner pSrc As POINTER, // source pointer pDst As POINTER, // destination pointer wCount As Word // originally coded in ROM, we have to use RAM in Swordfish... Dim 'usbdsc.c USB_CD_Ptr(2) As Word, // {&cfg01,&cfg01}; #if USB_HAVE_SERIAL_STRING USB_SD_Ptr(4) As Word // {&sd000,&sd001,&sd002,&sd003}; #else USB_SD_Ptr(3) As Word // {&sd000,&sd001,&sd002}; #endif { **************************************************************************** * Name : SetRAMPtr * * Purpose : Helper routine for setting RAM * **************************************************************************** } Inline Sub SetRAMPtr(pAddress As FSR0, pValue As POSTINC0) End Sub { **************************************************************************** * Name : GetRAMPtr * * Purpose : Helper routine for getting RAM byte * **************************************************************************** } Inline Function GetRAMPtr(pAddress As FSR1) As POSTINC1 End Function { **************************************************************************** * Name : ReadROM * * Purpose : Helper routine for getting ROM byte * **************************************************************************** } Inline Function ReadROM() As TABLAT ASM- TBLRD *+ End ASM End Function { **************************************************************************** * Name :GetROMPtr * * Purpose : Helper routine for getting ROM byte * **************************************************************************** } Inline Function GetROMPtr(pAddress As TABLEPTR) As TABLAT ReadROM End Function { **************************************************************************** * Name : GetRequestType * * Purpose : Helper routine to obtain USB request type * **************************************************************************** } Function GetRequestType() As Byte result = (SetupPkt.RequestType_f2 >> 5) And %00000011 End Function { **************************************************************************** * Name : GetRecipient * * Purpose : Helper routine to get USB recipient * **************************************************************************** } Function GetRecipient() As Byte result = SetupPkt.Recipient_f5 And $1F End Function { **************************************************************************** * Name : GetEPNum * * Purpose : Helper routineto get USB EP number * **************************************************************************** } Function GetEPNum() As Byte result = SetupPkt.EPNum_f4 And $0F End Function { **************************************************************************** * Name : GetPID (version 1.2) * * Purpose : Helper routineto get USB PID * **************************************************************************** } Function GetPID() As Byte result = (ep0Bo.Stat.PID >> 2) And $0F End Function { **************************************************************************** * Name : mInitializeUSBDriver * * Purpose : Configures the USB module * * : This register determines: USB Speed, On-chip pull-up resistor * * : selection, On-chip tranceiver selection, bus eye pattern * * : generation mode, Ping-pong buffering mode selection * **************************************************************************** } Public Sub mInitializeUSBDriver() 'usbdrv.h #if _device = 18F87J50 ASM MOVLW 20 MOVLB 15 MOVWF UCFG End ASM #else UCFG = UCFG_VAL #endif usb_device_state = DETACHED_STATE usb_stat._byte = $00 usb_active_cfg = $00 End Sub { **************************************************************************** * Name : mDisableEP1to15 * * Purpose : This macro disables all endpoints except EP0. This macro * * : should be called when the host sends a RESET signal or a * * : SET_CONFIGURATION request. * **************************************************************************** } Sub mDisableEP1to15() 'usbdrv.h UEP1 = $00 UEP2 = $00 UEP3 = $00 UEP4 = $00 UEP5 = $00 UEP6 = $00 UEP7 = $00 UEP8 = $00 UEP9 = $00 UEP10 = $00 UEP11 = $00 UEP12 = $00 UEP13 = $00 UEP14 = $00 UEP15 = $00 End Sub { **************************************************************************** * Name : mUSBBufferReady * * Require : IN Endpoint: Buffer is loaded and ready to be sent. * * : OUT Endpoint: Buffer is free to be written to by SIE. * * Input : buffer_dsc: Root name of the buffer descriptor group. i.e. * * : ep0Bo, ep1Bi, ... Declared IN usbmmap.c. Names can be * * : remapped for readability, see examples in usbcfg.h * * Purpose : This macro should be called each time after: * * : 1. A non-EP0 IN endpoint buffer is populated with data * * : 2. A non-EP0 OUT endpoint buffer is read. * * : This macro turns the buffer ownership to SIE for * * : servicing. It also toggles the DTS bit for * * : synchronization. * **************************************************************************** } Sub mUSBBufferReady(ByRef buffer_dsc As BDT) 'usbdrv.h buffer_dsc.Stat._byte = buffer_dsc.Stat._byte And _DTSMASK // Save only DTS bit buffer_dsc.Stat.DTS = Not buffer_dsc.Stat.DTS // Toggle DTS bit buffer_dsc.Stat._byte = buffer_dsc.Stat._byte Or _USIE Or _DTSEN // Turn ownership to SIE End Sub { **************************************************************************** * Name : mUSBCheckAdrPendingState * * Purpose : Specialized checking routine, it checks if the device is in * * : the ADDRESS PENDING STATE and services it if it is. * **************************************************************************** } Sub mUSBCheckAdrPendingState() 'usb9.h If usb_device_state = ADR_PENDING_STATE Then UADDR = SetupPkt.bDevADR If (UADDR > 0) Then usb_device_state = ADDRESS_STATE Else usb_device_state = DEFAULT_STATE EndIf EndIf End Sub // ============================ HID CLASS ============================ #if USB_USE_HID Dim hid_rpt_rx_len As Byte // make public for CDC module... Public Dim HIDRXBytesAvailable As HID_BD_OUT.Cnt { **************************************************************************** * Name : mUSBGetHIDDscAdr * * Purpose : Get HID descriptor address * **************************************************************************** } Sub mUSBGetHIDDscAdr() 'usbcfg.h If usb_active_cfg = 1 Then pSrc.bRom = USB_CD_Ptr(1) // @cfg01.hid_i00a00 Inc(pSrc.bRom,18) EndIf End Sub { **************************************************************************** * Name : mUSBGetHIDRptDscAdr * * Purpose : Get HID descriptor report address * **************************************************************************** } Sub mUSBGetHIDRptDscAdr() 'usbcfg.h If usb_active_cfg = 1 Then pSrc.bRom = @hid_rpt01 EndIf End Sub { **************************************************************************** * Name : mUSBGetHIDRptDscSize * * Purpose : Get HID report descriptor size * **************************************************************************** } Sub mUSBGetHIDRptDscSize() 'usbcfg.h If usb_active_cfg = 1 Then wCount = SizeOf(hid_rpt01) EndIf End Sub { **************************************************************************** * Name : mHIDRxIsBusy * * Purpose : This macro is used to check if HID OUT endpoint is busy (owned * * : by SIE) or not. * **************************************************************************** } Public Dim mHIDRxIsBusy As HID_BD_OUT.Stat.UOWN 'hid.h { **************************************************************************** * Name : mHIDTxIsBusy * * Purpose : This macro is used to check if HID IN endpoint is busy (owned * * : by SIE) or not. * **************************************************************************** } Public Dim mHIDTxIsBusy As HID_BD_IN.Stat.UOWN 'hid.h { **************************************************************************** * Name : HandleControlOutReport * * Purpose : Checks to see if an output or Feature report has arrived on * * : the control pipe. if yes, extracts and uses the data. * **************************************************************************** } Sub HandleControlOutReport() 'user_generic_hid.c Dim count As Byte // Find out if an Output or Feature report has arrived on the control pipe. // Get the report type from the Setup packet. Select SetupPkt.W_Value.Byte1 Case 0x02: // Output report // Get the report ID from the Setup packet. Select SetupPkt.W_Value.Byte0 Case 0: // Report ID 0 // This example application copies the Output report data // to hid_report_in. // (Assumes Input and Output reports are the same length.) // A "real" application would do something more useful with the data. // wCount holds the number of bytes read in the Data stage. // This example assumes the report fits in one transaction. count = 0 While count < HID_OUTPUT_REPORT_BYTES hid_report_in(count) = hid_report_out(count) Inc(count) Wend // The number of bytes in the report (from usbcfg.h). End Select Case 0x03: // Feature report // Get the report ID from the Setup packet. Select SetupPkt.W_Value.Byte0 Case 0: // Report ID 0 // The Feature report data is in hid_report_feature. // This example application just sends the data back in the next // Get_Report request for a Feature report. // wCount holds the number of bytes read in the Data stage. // This example assumes the report fits in one transaction. End Select End Select End Sub { **************************************************************************** * Name : HIDGetReportHandler * * Purpose : Called on receiving a Setup packet with a GET_REPORT request. * * : Extracts the report type and the report ID of the report the * * : device will send in the Data stage of the transfer. Calls a * * : function to provide the data. * **************************************************************************** } Sub HIDGetReportHandler() 'hid.c // The report type is in the high byte of the setup packet's Value field. // 3 = Feature; 1 = Input. Select SetupPkt.W_Value.Byte1 // Input report Case 1: // Find out which report ID was specified. // The report ID is in the low byte (LSB) of the Value field. // This example supports Report ID 0. Select SetupPkt.W_Value.Byte0 Case 0: // Report ID 0 // HID will handle the request. ctrl_trf_session_owner = MUID_HID // Provide the data to send. pSrc.bRam = @hid_report_in wCount.Byte0 = HID_INPUT_REPORT_BYTES // The number of bytes in the report Case 1: // Report ID 1 // Place similar code to handle Report ID 1 here. End Select // Feature report Case 3: // The report ID is in the low byte of the Setup packet's Value field. // This example supports Report ID 0. Select SetupPkt.W_Value.Byte0 Case 0: // Report ID 0 // The HID class code will handle the request. ctrl_trf_session_owner = MUID_HID // Provide the data to send. pSrc.bRam = @hid_report_feature wCount.Byte0 = HID_FEATURE_REPORT_BYTES // The number of bytes in the report End Select End Select // The data is in RAM. usb_stat.ctrl_trf_mem = _RAM // Set memory type End Sub { **************************************************************************** * Name : HIDSetReportHandler * * Purpose : Called on receiving a Setup packet with a SET_REPORT request. * * : Extracts the report type and the report ID of the report the * * : host will send IN the Data stage of the transfer. Places the * * : report type and report ID IN out_report_status. * **************************************************************************** } Sub HIDSetReportHandler() 'hid.c // The report type is in the high byte of the Setup packet's Value field. // 3 = Feature; 2 = Output. Select SetupPkt.W_Value.Byte1 Case 2: // Output report // The report ID is in the low byte of the Setup packet's Value field. // This example supports Report ID 0. Select SetupPkt.W_Value.Byte0 Case 0: // Report ID 0 // HID will handle the request. ctrl_trf_session_owner = MUID_HID // Save the report type and report ID in out_report_status. //out_report_status = SetOutReportStatus // (MSB(SetupPkt.W_Value), // LSB(SetupPkt.W_Value)); // When the report arrives in the data stage, the data will be // stored in hid_report_out (defined in usbmmap.c). pDst.bRam = @hid_report_out End Select Case 3: // Feature report // Find out which report ID was specified. // The report ID is in the low byte (LSB) of the Value field. // This example supports Report ID 0. Select SetupPkt.W_Value.Byte0 Case 0: // Report ID 0 // HID will handle the request. ctrl_trf_session_owner = MUID_HID // Save the report type and report ID in out_report_status. //out_report_status = SetOutReportStatus // (MSB(SetupPkt.W_Value), // LSB(SetupPkt.W_Value)); // When the report arrives in the data stage, the data will be // stored in hid_report_feature (defined in usbmmap.c). pDst.bRam = @hid_report_feature End Select End Select End Sub { **************************************************************************** * Name : USBCheckHIDRequest * * Purpose : This routine checks the setup data packet to see if it knows * * : how to handle it * **************************************************************************** } // variables - used in USBCheckHIDRequest 'hid.c Dim idle_rate As Byte Dim active_protocol As Byte // [0] Boot Protocol [1] Report Protocol Sub USBCheckHIDRequest() 'hid.c // The packet must be directed to an interface with the same ID as the // device's HID interface. If GetRecipient <> RCPT_INTF Then Exit EndIf If SetupPkt.bIntfID <> HID_INTF_ID Then Exit EndIf // There are two standard requests that hid.c may support. // 1. GET_DSC(DSC_HID,DSC_RPT,DSC_PHY); // 2. SET_DSC(DSC_HID,DSC_RPT,DSC_PHY); If SetupPkt.bRequest = GET_DSC Then // Request for a descriptor. Select SetupPkt.bDscType // HID descriptor. Case DSC_HID: ctrl_trf_session_owner = MUID_HID mUSBGetHIDDscAdr() // See usbcfg.h wCount = SizeOf(USB_HID_DSC) // Report descriptor. Case DSC_RPT: ctrl_trf_session_owner = MUID_HID mUSBGetHIDRptDscAdr() // See usbcfg.h mUSBGetHIDRptDscSize() // See usbcfg.h // Physical descriptor. Case DSC_PHY: // ctrl_trf_session_owner = MUID_HID End Select usb_stat.ctrl_trf_mem = _ROM EndIf If GetRequestType <> CLASS Then Exit EndIf // HID-specific requests. Select SetupPkt.bRequest // required for all HID tranfers Case GET_REPORT: HIDGetReportHandler() // optional - needed for control transfers only Case SET_REPORT: HIDSetReportHandler() // optional Case GET_IDLE: ctrl_trf_session_owner = MUID_HID pSrc.bRam = @idle_rate // Set source usb_stat.ctrl_trf_mem = _RAM // Set memory type wCount.Byte0 = 1 // Set data count // optional - except for keyboards using the boot protocol Case SET_IDLE: ctrl_trf_session_owner = MUID_HID idle_rate = SetupPkt.W_Value.Byte1 // required for HIDs that support the boot protocol Case GET_PROTOCOL: ctrl_trf_session_owner = MUID_HID pSrc.bRam = @active_protocol // Set source usb_stat.ctrl_trf_mem = _RAM // Set memory type wCount.Byte0 = 1 // Set data count // required for HIDs that support the boot protocol Case SET_PROTOCOL: ctrl_trf_session_owner = MUID_HID active_protocol = SetupPkt.W_Value.Byte0 End Select End Sub { **************************************************************************** * Name : HIDInitEP * * Purpose : HIDInitEP initializes HID endpoints, buffer descriptors, * * : internal state-machine, and variables. It should be called * * : after the USB host has sent OUT a SET_CONFIGURATION request. * **************************************************************************** } Sub HIDInitEP() 'hid.c hid_rpt_rx_len =0 HID_UEP = EP_OUT_IN Or HSHK_EN // Enable 2 data pipes HID_BD_OUT.Cnt = SizeOf(hid_report_out) // Set buffer size HID_BD_OUT.ADR = @hid_report_out // Set buffer address HID_BD_OUT.Stat._byte = _USIE Or _DAT0 Or _DTSEN // Set status (* Do not have to init Cnt of IN pipes here. Reason: Number of bytes to send to the host varies from one transaction to another. Cnt should equal the exact number of bytes to transmit for a given IN transaction. This number of bytes will only be known right before the data is sent. *) HID_BD_IN.ADR = @hid_report_in // Set buffer address HID_BD_IN.Stat._byte = _UCPU Or _DAT1 // Set status End Sub { **************************************************************************** * Name : HIDTxReport * * Require : mHIDTxIsBusy() must return false. Value of 'len' must be equal * * : to or smaller than HID_INT_IN_EP_SIZE for an interrupt * * : endpoint, the largest buffer size is 64 bytes. * * Input: : buffer : POINTER to the starting location of data bytes * * : len : Number of bytes to be transferred * * Purpose : Use this macro to transfer data located in data memory. * * : NOTE : mHIDTxIsBusy() must return false before user can call * * : this function. Unexpected behavior will occur if this function * * : when mHIDTxIsBusy * **************************************************************************** } Public Sub HIDTxReport(Buffer As FSR1, len As Byte) 'hid.c Dim i As Byte If len > HID_INT_IN_EP_SIZE Then len = HID_INT_IN_EP_SIZE EndIf // Copy data from user's buffer to dual-ram buffer FSR0 = @hid_report_in i = 0 While i < len POSTINC0 = POSTINC1 Inc(i) Wend HID_BD_IN.Cnt = len mUSBBufferReady(HID_BD_IN) End Sub { **************************************************************************** * Name : HIDRxReport * * Require : Value of input argument 'len' should be smaller than the * * : maximum endpoint size responsible for receiving report data * * : from USB host for HID CLASS. Input argument 'buffer' should * * : point to a buffer area that is bigger or equal to the size * * : specified by 'len'. * * Input : buffer : POINTER to where received bytes are to be stored * * : len : The number of bytes expected. * * Output : The number of bytes copied to buffer. Publicly accessible * * : variable hid_rpt_rx_len is updated with the number of bytes * * : copied to buffer. Once HIDRxReport is called, subsequent * * : retrieval of hid_rpt_rx_len can be done by calling macro * * : mHIDGetRptRxLength(). * * Purpose:: HIDRxReport copies a string of bytes received through * * : USB HID OUT endpoint to a user's specified location. It is a * * : non-blocking function. It does not wait for data if there is * * : no data available. Instead it returns '0' to notify the * * : caller that there is no data available. If the actual number * * : of bytes received is larger than the number of bytes expected * * : (len), only the expected number of bytes specified will be * * : copied to buffer. If the actual number of bytes received is * * : smaller than the number of bytes expected (len), only the * * : actual number of bytes received will be copied to buffer. * **************************************************************************** } Public Function HIDRxReport(Buffer As FSR0, len As Byte) As Byte 'hid.c hid_rpt_rx_len = 0 If mHIDRxIsBusy = 0 Then // Adjust the expected number of bytes to equal // the actual number of bytes received. If len > HID_BD_OUT.Cnt Then len = HID_BD_OUT.Cnt EndIf // Copy data from dual-ram buffer to user's buffer FSR1 = @hid_report_out hid_rpt_rx_len = 0 While hid_rpt_rx_len < len POSTINC0 = POSTINC1 Inc(hid_rpt_rx_len) Wend // Prepare dual-ram buffer for next OUT transaction HID_BD_OUT.Cnt = SizeOf(hid_report_out) mUSBBufferReady(HID_BD_OUT) EndIf result = hid_rpt_rx_len End Function #endif // USB_USE_HID // ========================== END HID CLASS ========================== // ============================ CDC CLASS ============================ #if USB_USE_CDC Dim 'cdc.c cdc_trf_state As Byte, // States are defined cdc.h pCDCSrc As POINTER, // Dedicated source pointer pCDCDst As POINTER, // Dedicated destination pointer cdc_tx_len As Word, // total tx length cdc_mem_type As Byte, // _ROM, _RAM line_coding As CDC_LINE_CODING, // Buffer to store line coding information control_signal_bitmap As CDC_CONTROL_SIGNAL_BITMAP // make public for CDC module... Public Dim CDCControlSignal As control_signal_bitmap, CDCDataRX As cdc_data_rx, CDCDataTX As cdc_data_tx, CDCRXBytesAvailable As CDC_BULK_BD_OUT.Cnt // SEND_ENCAPSULATED_COMMAND and GET_ENCAPSULATED_RESPONSE are required // requests according to the CDC specification. // However, it is not really being used here, therefore a dummy buffer is // used for conformance. Const dummy_length = $08 Dim dummy_encapsulated_cmd_response(dummy_length) As Byte { **************************************************************************** * Name : mUSBUSARTIsTxTrfReady * * Purpose : This macro is used to check if the CDC CLASS is ready to send * * : more data. * **************************************************************************** } Public Function mUSBUSARTIsTxTrfReady() As Boolean 'cdc.h result = (cdc_trf_state = CDC_TX_READY) End Function { **************************************************************************** * Name : mCDCUsartRxIsBusy * * Purpose : This macro is used to check if CDC bulk OUT endpoint is busy * * : (owned by SIE) or not. * **************************************************************************** } Public Dim mCDCUsartRxIsBusy As CDC_BULK_BD_OUT.Stat.UOWN 'cdc.h { **************************************************************************** * Name : mCDCUsartTxIsBusy * * Purpose : This macro is used to check if CDC bulk IN endpoint is busy * * : (owned by SIE) or not. * **************************************************************************** } Public Dim mCDCUsartTxIsBusy As CDC_BULK_BD_IN.Stat.UOWN 'cdc.h { **************************************************************************** * Name : mUSBUSARTTxRam * * Require : cdc_trf_state must be IN the CDC_TX_READY state. * * Purpose : Use this macro to transfer data located IN data memory. Use * * : this macro when: * * : 1. Data stream is not null-terminated * * : 2. Transfer length is known * * : Remember: cdc_trf_state must == CDC_TX_READY. The actual * * : transfer is handled by CDCTxService(). * **************************************************************************** } Public Sub mUSBUSARTTxRam(pData As Word,len As Word) 'cdc.h pCDCSrc.bRam = pData cdc_tx_len = len cdc_mem_type = _RAM cdc_trf_state = CDC_TX_BUSY End Sub { **************************************************************************** * Name : mUSBUSARTTxRom * * Require : cdc_trf_state must be IN the CDC_TX_READY state. * * Purpose : Use this macro to transfer data located IN data memory. Use * * : this macro when: * * : 1. Data stream is not null-terminated * * : 2. Transfer length is known * * : Remember: cdc_trf_state must == CDC_TX_READY. The actual * * : transfer is handled by CDCTxService(). * **************************************************************************** } Public Sub mUSBUSARTTxRom(pData As Word,len As Word) 'cdc.h pCDCSrc.bRom = pData cdc_tx_len = len cdc_mem_type = _ROM cdc_trf_state = CDC_TX_BUSY End Sub { **************************************************************************** * Name : USBCheckCDCRequest * * Purpose : This routine checks the setup data packet to see if it knows * * : how to use it * **************************************************************************** } Sub USBCheckCDCRequest() 'cdc.c // If request recipient is not an interface then return If GetRecipient <> RCPT_INTF Then Exit EndIf // If request type is not class-specific then return If GetRequestType <> CLASS Then Exit EndIf // Interface ID must match interface numbers associated with // CDC class, else return If (SetupPkt.bIntfID <> CDC_COMM_INTF_ID) And (SetupPkt.bIntfID <> CDC_DATA_INTF_ID) Then Exit EndIf Select SetupPkt.bRequest Case SEND_ENCAPSULATED_COMMAND: ctrl_trf_session_owner = MUID_CDC pSrc.bRam = @dummy_encapsulated_cmd_response usb_stat.ctrl_trf_mem = _RAM wCount.Byte0 = dummy_length Case GET_ENCAPSULATED_RESPONSE: ctrl_trf_session_owner = MUID_CDC // Populate dummy_encapsulated_cmd_response first. pDst.bRam = @dummy_encapsulated_cmd_response ' case SET_COMM_FEATURE: // Optional ' case GET_COMM_FEATURE: // Optional ' case CLEAR_COMM_FEATURE: // Optional Case SET_LINE_CODING: ctrl_trf_session_owner = MUID_CDC pDst.bRam = @LINE_CODING // Set destination Case GET_LINE_CODING: ctrl_trf_session_owner = MUID_CDC pSrc.bRam = @LINE_CODING // Set source usb_stat.ctrl_trf_mem = _RAM // Set memory type wCount.Byte0 = LINE_CODING_LENGTH // Set data count Case SET_CONTROL_LINE_STATE: ctrl_trf_session_owner = MUID_CDC control_signal_bitmap._byte = SetupPkt.W_Value.Byte0 ' case SEND_BREAK: // Optional End Select End Sub { **************************************************************************** * Name : CDCInitEP * * Purpose : CDCInitEP initializes CDC endpoints, buffer descriptors, * * : internal state-machine, and variables. It should be called * * : after the USB host has sent OUT a SET_CONFIGURATION request. * **************************************************************************** } Sub CDCInitEP() 'cdc.c // abstract line coding information LINE_CODING.dwDTERate = 115200 // baud rate LINE_CODING.bCharFormat = $00 // 1 stop bit LINE_CODING.bParityType = $00 // None LINE_CODING.bDataBits = $08 // 5,6,7,8, or 16 cdc_trf_state = CDC_TX_READY CDC_COMM_UEP = EP_IN Or HSHK_EN // Enable 1 Comm pipe CDC_DATA_UEP = EP_OUT_IN Or HSHK_EN // Enable 2 data pipes (* Do not have to init Cnt of IN pipes here. Reason: Number of bytes to send to the host varies from one transaction to another. Cnt should equal the exact number of bytes to transmit for a given IN transaction. This number of bytes will only be known right before the data is sent. *) CDC_INT_BD_IN.ADR = @cdc_notice // Set buffer address CDC_INT_BD_IN.Stat._byte = _UCPU Or _DAT1 // Set status CDC_BULK_BD_OUT.Cnt = SizeOf(cdc_data_rx) // Set buffer size CDC_BULK_BD_OUT.ADR = @cdc_data_rx // Set buffer address CDC_BULK_BD_OUT.Stat._byte = _USIE Or _DAT0 Or _DTSEN // Set status CDC_BULK_BD_IN.ADR = @cdc_data_tx // Set buffer address CDC_BULK_BD_IN.Stat._byte = _UCPU Or _DAT1 // Set status End Sub { **************************************************************************** * Name : CDCRXPrepareForNext * * Purpose : Prepare for next CDC transmission * **************************************************************************** } Public Sub CDCRXPrepareForNext() CDC_BULK_BD_OUT.Cnt = SizeOf(cdc_data_rx) mUSBBufferReady(CDC_BULK_BD_OUT) End Sub { **************************************************************************** * Name : CDCTxService * * Purpose : CDCTxService handles device-to-host transaction(s). This * * : function should be called once per Main program loop. * **************************************************************************** } Public Sub CDCTxService() 'cdc.c Dim byte_to_send As Byte If mCDCUsartTxIsBusy <> 0 Then Exit EndIf // Completing stage is necessary while [ mCDCUSartTxIsBusy()==1 ]. // By having this stage, user can always check cdc_trf_state, // and not having to call mCDCUsartTxIsBusy() directly. If cdc_trf_state = CDC_TX_COMPLETING Then cdc_trf_state = CDC_TX_READY EndIf // If CDC_TX_READY state, nothing to do, just return. If cdc_trf_state = CDC_TX_READY Then Exit EndIf // If CDC_TX_BUSY_ZLP state, send zero length packet If cdc_trf_state = CDC_TX_BUSY_ZLP Then CDC_BULK_BD_IN.Cnt = 0 cdc_trf_state = CDC_TX_COMPLETING Else If cdc_trf_state = CDC_TX_BUSY Then // First, have to figure out how many byte of data to send. If cdc_tx_len > SizeOf(cdc_data_tx) Then byte_to_send = SizeOf(cdc_data_tx) Else byte_to_send = cdc_tx_len EndIf // Next, load the number of bytes to send to Cnt in buffer descriptor CDC_BULK_BD_IN.Cnt = byte_to_send // Subtract the number of bytes just about to be sent from the total. cdc_tx_len = cdc_tx_len - byte_to_send pCDCDst.bRam = @cdc_data_tx // Set destination pointer If cdc_mem_type = _ROM Then // Determine type of memory source TABLEPTR = pCDCSrc.bRom FSR0 = pCDCDst.bRam Inc(pCDCDst.bRam, byte_to_send) Inc(pCDCSrc.bRam, byte_to_send) While byte_to_send > 0 POSTINC0 = ReadROM Dec(byte_to_send) Wend (* while byte_to_send > 0 SetRAMPtr(pCDCDst.bRam,GetROMPtr(pCDCSrc.bRom)) inc(pCDCDst.bRam) inc(pCDCSrc.bRom) dec(byte_to_send) wend *) Else // _RAM - optimised... FSR0 = pCDCDst.bRam FSR1 = pCDCSrc.bRam Inc(pCDCDst.bRam, byte_to_send) Inc(pCDCSrc.bRam, byte_to_send) While byte_to_send > 0 POSTINC0 = POSTINC1 Dec(byte_to_send) Wend EndIf // Lastly, determine if a zero length packet state is necessary. // See explanation in USB Specification 2.0: Section 5.8.3 If cdc_tx_len = 0 Then If CDC_BULK_BD_IN.Cnt = SizeOf(cdc_data_tx) Then cdc_trf_state = CDC_TX_BUSY_ZLP Else cdc_trf_state = CDC_TX_COMPLETING EndIf EndIf EndIf EndIf // Both CDC_TX_BUSY and CDC_TX_BUSY_ZLP states use the following macro mUSBBufferReady(CDC_BULK_BD_IN) End Sub (* ========================== END CDC CLASS ========================== *) #endif //USB_USE_CDC { **************************************************************************** * Name : USBStdGetDscHandler * * Purpose : This routine handles the STANDARD GET_DESCRIPTOR request. It * * : utilizes tables dynamically looks up descriptor size. This * * : routine should never have to be modified if the tables in * * : usbdsc.c are declared correctly. * **************************************************************************** } Sub USBStdGetDscHandler() 'usb9.c If SetupPkt.bmRequestType = $80 Then Select SetupPkt.bDscType Case DSC_DEV: ctrl_trf_session_owner = MUID_USB9 pSrc.bRom = @device_dsc // djb USB_DD_Ptr // @device_dsc wCount = SizeOf(device_dsc) // djb USB_DD_Size // sizeof(device_dsc) Case DSC_CFG: ctrl_trf_session_owner = MUID_USB9 pSrc.bRom = USB_CD_Ptr(SetupPkt.bDscIndex) wCount = GetROMPtr(pSrc.wRom + 2) // Set data count Case DSC_STR: ctrl_trf_session_owner = MUID_USB9 // ensure we have a valid string index - version 1.2... If SetupPkt.bDscIndex > Bound(USB_SD_Ptr) Then wCount = 0 Else pSrc.bRom = USB_SD_Ptr(SetupPkt.bDscIndex) wCount = GetROMPtr(pSrc.bRom) // Set data count EndIf End Select usb_stat.ctrl_trf_mem = _ROM // Set memory type EndIf End Sub { **************************************************************************** * Name : USBStdSetCfgHandler * * Purpose : This routine first disables all endpoints by clearing UEP * * : registers. It then configures (initializes) endpoints specified* * : in the modifiable section. * **************************************************************************** } Sub USBStdSetCfgHandler() 'usb9.c ctrl_trf_session_owner = MUID_USB9 mDisableEP1to15() Clear(usb_alt_intf) usb_active_cfg = SetupPkt.bCfgValue If SetupPkt.bCfgValue = 0 Then usb_device_state = ADDRESS_STATE Else usb_device_state = CONFIGURED_STATE // Modifiable Section #if USB_USE_HID HIDInitEP() #endif #if USB_USE_CDC CDCInitEP() #endif // End modifiable section EndIf End Sub { **************************************************************************** * Name : USBStdGetStatusHandler * * Purpose : This routine handles the STANDARD GET_STATUS request * **************************************************************************** } Sub USBStdGetStatusHandler() 'usb9.c CtrlTrfData._byte0 = 0 // Initialize content CtrlTrfData._byte1 = 0 Select GetRecipient Case RCPT_DEV: ctrl_trf_session_owner = MUID_USB9 // _byte0: bit0: Self-Powered Status [0] Bus-Powered [1] Self-Powered // bit1: RemoteWakeup [0] Disabled [1] Enabled If self_power = 1 Then // self_power defined in io_cfg.h CtrlTrfData._byte0 = CtrlTrfData._byte0 Or %000000001 // Set bit0 If usb_stat.RemoteWakeup = 1 Then // usb_stat defined in usbmmap.c CtrlTrfData._byte0 = CtrlTrfData._byte0 Or $00000010 // Set bit1 EndIf EndIf Case RCPT_INTF: ctrl_trf_session_owner = MUID_USB9 // No data to update Case RCPT_EP: ctrl_trf_session_owner = MUID_USB9 // _byte0: bit0: Halt Status [0] Not Halted [1] Halted pDst.bRam = @ep0Bo + (GetEPNum * 8) + (SetupPkt.EPDir * 4) If (GetRAMPtr(pDst.bRam) And _BSTALL) <> 0 Then // Use _BSTALL as a bit mask CtrlTrfData._byte0 = $01// Set bit0 EndIf End Select If ctrl_trf_session_owner = MUID_USB9 Then pSrc.bRam = @CtrlTrfData // Set Source usb_stat.ctrl_trf_mem = _RAM // Set memory type wCount.Byte0 = 2 // Set data count EndIf End Sub { **************************************************************************** * Name : USBStdFeatureReqHandler * * Purpose : This routine handles the STANDARD SET & clear FEATURES requests* **************************************************************************** } Sub USBStdFeatureReqHandler() 'usb9.c If (SetupPkt.bFeature = DEVICE_REMOTE_WAKEUP) And (GetRecipient = RCPT_DEV) Then ctrl_trf_session_owner = MUID_USB9 If SetupPkt.bRequest = SET_FEATURE Then usb_stat.RemoteWakeup = 1 Else usb_stat.RemoteWakeup = 0 EndIf EndIf If (SetupPkt.bFeature = ENDPOINT_HALT) And (GetRecipient = RCPT_EP) And (GetEPNum <> 0) Then ctrl_trf_session_owner = MUID_USB9 // Must do address calculation here pDst.bRam = @ep0Bo + (GetEPNum * 8) + (SetupPkt.EPDir * 4) If SetupPkt.bRequest = SET_FEATURE Then SetRAMPtr(pDst.bRam, _USIE Or _BSTALL) Else If SetupPkt.EPDir = 1 Then// IN SetRAMPtr(pDst.bRam, _UCPU) Else SetRAMPtr(pDst.bRam, _USIE Or _DAT0 Or _DTSEN) EndIf EndIf EndIf End Sub { **************************************************************************** * Name : USBCheckStdRequest * * Purpose : This routine checks the setup data packet to see if it knows * * : how to handle it * **************************************************************************** } Sub USBCheckStdRequest() 'usb9.c If GetRequestType <> STANDARD Then Exit EndIf Select SetupPkt.bRequest Case SET_ADR: ctrl_trf_session_owner = MUID_USB9 usb_device_state = ADR_PENDING_STATE // Update state only // See USBCtrlTrfInHandler() in usbctrltrf.c for the next step Case GET_DSC: USBStdGetDscHandler() Case SET_CFG: USBStdSetCfgHandler() Case GET_CFG: ctrl_trf_session_owner = MUID_USB9 pSrc.bRam = @usb_active_cfg // Set Source usb_stat.ctrl_trf_mem = _RAM // Set memory type wCount.Byte0 = 1 // Set data count Case GET_STATUS: USBStdGetStatusHandler() Case CLR_FEATURE: USBStdFeatureReqHandler() Case CLR_FEATURE USBStdFeatureReqHandler() Case GET_INTF: ctrl_trf_session_owner = MUID_USB9 pSrc.bRam = @usb_alt_intf + SetupPkt.bIntfID // Set source usb_stat.ctrl_trf_mem = _RAM // Set memory type wCount.Byte0 = 1 // Set data count Case SET_INTF: ctrl_trf_session_owner = MUID_USB9 usb_alt_intf(SetupPkt.bIntfID) = SetupPkt.bAltID Case SET_DSC: Case SYNCH_FRAME: End Select End Sub { **************************************************************************** * Name : USBCtrlTrfTxService * * Require : pSrc, wCount, and usb_stat.ctrl_trf_mem are setup properly. * * Purpose : This routine should be called from only two places. One from * * : USBCtrlEPServiceComplete() and one from USBCtrlTrfInHandler(). * * : It takes care of managing a transfer over multiple USB * * : transactions. This routine works with isochronous endpoint * * : larger than 256 bytes and is shown here as an example of how to* * : deal with BC9 and BC8. IN reality, a control endpoint can never* * : be larger than 64 bytes. * **************************************************************************** } Sub USBCtrlTrfTxService() 'usbctrltr.c // This code executes in the Data stage of a Control Read transfer. Dim byte_to_send As Word // First, have to figure out how many byte of data to send. If wCount < EP0BufferSize Then byte_to_send = wCount Else byte_to_send = EP0BufferSize EndIf // Next, load the number of bytes to send to BC9..0 in buffer descriptor ep0Bi.Stat.BC9 = 0 ep0Bi.Stat.BC8 = 0 ep0Bi.Stat._byte = ep0Bi.Stat._byte Or byte_to_send.Byte1 ep0Bi.Cnt = byte_to_send.Byte0 // Subtract the number of bytes just about to be sent from the total. wCount = wCount - byte_to_send pDst.bRam = @CtrlTrfData // Set destination pointer If usb_stat.ctrl_trf_mem = _ROM Then // Determine type of memory source TABLEPTR = pSrc.bRom FSR0 = pDst.bRam Inc(pSrc.bRom,byte_to_send) While byte_to_send > 0 POSTINC0 = ReadROM Dec(byte_to_send) Wend Else // RAM FSR1 = pSrc.bRam FSR0 = pDst.bRam Inc(pSrc.bRam,byte_to_send) While byte_to_send > 0 POSTINC0 = POSTINC1 Dec(byte_to_send) Wend EndIf End Sub { **************************************************************************** * Name : USBCtrlEPServiceComplete * * Purpose : This routine wrap up the ramaining tasks IN servicing a Setup * * : Request. Its main task is to set the endpoint controls * * : appropriately for a given situation. See code below. * * : There are three main scenarios: * * : a) There was no handler for the Request, IN this case a STALL* * : should be sent OUT. * * : b) The host has requested a read control transfer, endpoints * * : are required to be setup IN a specific way. * * : c) The host has requested a write control transfer, or a * * : control data stage is not required, endpoints are required* * : to be setup IN a specific way. * * : Packet processing is resumed by clearing PKTDIS bit. * **************************************************************************** } Sub USBCtrlEPServiceComplete() 'usbctrltr.c // PKTDIS bit is set when a Setup Transaction is received. // Clear before modifying ep0Bi.Stat or ep0Bo.Stat. UCONBits.PKTDIS = 0 If ctrl_trf_session_owner = MUID_NULL Then // If no one knows how to service this request then stall. // Must also prepare EP0 to receive the next SETUP transaction. ep0Bo.Cnt = EP0BufferSize ep0Bo.ADR = @SetupPkt ep0Bo.Stat._byte = _USIE Or _BSTALL ep0Bi.Stat._byte = _USIE Or _BSTALL // A module has claimed ownership of the control transfer session. Else If SetupPkt.DataDir = DEV_TO_HOST Then If SetupPkt.wLength < wCount Then wCount = SetupPkt.wLength EndIf USBCtrlTrfTxService() ctrl_trf_state = CTRL_TRF_TX // Control Read: // <SETUP[0]><IN[1]><IN[0]>...<OUT[1]> | <SETUP[0]> // 1. Prepare OUT EP to respond to early termination // NOTE: // If something went wrong during the control transfer, // the last status stage may not be sent by the host. // When this happens, two different things could happen // depending on the host. // a) The host could send out a RESET. // b) The host could send out a new SETUP transaction // without sending a RESET first. // To properly handle case (b), the OUT EP must be setup // to receive either a zero length OUT transaction, or a // new SETUP transaction. // Since the SETUP transaction requires the DTS bit to be // DAT0 while the zero length OUT status requires the DTS // bit to be DAT1, the DTS bit check by the hardware should // be disabled. This way the SIE could accept either of // the two transactions. // Furthermore, the Cnt byte should be set to prepare for // the SETUP data (8-byte or more), and the buffer address // should be pointed to SetupPkt. ep0Bo.Cnt = EP0BufferSize ep0Bo.ADR = @SetupPkt ep0Bo.Stat._byte = _USIE // Note: DTSEN is 0! // 2. Prepare IN EP to transfer data, Cnt should have // been initialized by responsible request owner. ep0Bi.ADR = @CtrlTrfData ep0Bi.Stat._byte = _USIE Or _DAT1 Or _DTSEN // (SetupPkt.DataDir == HOST_TO_DEV) Else ctrl_trf_state = CTRL_TRF_RX // Control Write: // <SETUP[0]><OUT[1]><OUT[0]>...<IN[1]> | <SETUP[0]> // 1. Prepare IN EP to respond to early termination // This is the same as a Zero Length Packet Response // for control transfer without a data stage ep0Bi.Cnt = 0 ep0Bi.Stat._byte = _USIE Or _DAT1 Or _DTSEN // Prepare OUT EP to receive data. ep0Bo.Cnt = EP0BufferSize ep0Bo.ADR = @CtrlTrfData ep0Bo.Stat._byte = _USIE Or _DAT1 Or _DTSEN EndIf EndIf End Sub { **************************************************************************** * Name : USBCtrlTrfSetupHandler * * Require : SetupPkt buffer is loaded with valid USB Setup Data * * Purpose : This routine is a task dispatcher and has 3 stages. * * : 1. It initializes the control transfer state machine. * * : 2. It calls on each of the module that may know how to * * : service the Setup Request from the host. * * : module Example: USB9, HID, CDC, MSD, ... * * : as new classes are added, ClassReqHandler table IN * * : usbdsc.c should be updated to call all available * * : CLASS handlers. * * : 3. Once each of the modules has had a chance to check if * * : it is responsible for servicing the request, stage 3 * * : then checks direction of the transfer to determine how * * : to prepare EP0 for the control transfer. * * : Refer to USBCtrlEPServiceComplete() for more details. * * Note : Microchip USB Firmware has three different states for the * * : control transfer state machine: * * : 1. WAIT_SETUP * * : 2. CTRL_TRF_TX * * : 3. CTRL_TRF_RX * * : A Control Transfer is composed of many USB transactions. When * * : transferring data over multiple transactions, it is important * * : to keep track of data source, data destination, and data count * * : These three parameters are stored IN pSrc,pDst, and wCount. A * * : flag is used to note if the data source is from ROM or RAM. * **************************************************************************** } Sub USBCtrlTrfSetupHandler() 'usbctrltr.c // Stage 1 ctrl_trf_state = WAIT_SETUP ctrl_trf_session_owner = MUID_NULL // Set owner to NULL wCount = 0 // Stage 2 */ USBCheckStdRequest() // See system\usb9\usb9.c // Modifiable Section #if USB_USE_HID USBCheckHIDRequest #endif #if USB_USE_CDC USBCheckCDCRequest #endif // End Modifiable Section // Stage 3 USBCtrlEPServiceComplete() End Sub { **************************************************************************** * Name : USBCtrlTrfRxService * * Require : pDst and wCount are setup properly. pSrc is always &CtrlTrfData* * : usb_stat.ctrl_trf_mem is always _RAM. wCount should be set to * * : 0 at the start of each control transfer. * * Purpose : This routine is only partially complete. Check for new version * * : of the firmware. * **************************************************************************** } Sub USBCtrlTrfRxService() 'usbctrltr.c // This code executes in the Data stage of a Control Write transfer. Dim byte_to_read As Word byte_to_read.Byte1 = $03 And ep0Bo.Stat._byte // Filter out last 2 bits byte_to_read.Byte0 = ep0Bo.Cnt // Accumulate total number of bytes read wCount = wCount + byte_to_read pSrc.bRam = @CtrlTrfData While byte_to_read > 0 SetRAMPtr(pDst.bRam,GetRAMPtr(pSrc.bRam)) Inc(pDst.bRam) Inc(pSrc.bRam) Dec(byte_to_read) Wend End Sub { **************************************************************************** * Name : USBPrepareForNextSetupTrf * * Purpose : The routine forces EP0 OUT to be ready for a new Setup * * : transaction, and forces EP0 IN to be owned by CPU. * **************************************************************************** } Sub USBPrepareForNextSetupTrf() 'usbctrltr.c ctrl_trf_state = WAIT_SETUP // See usbctrltrf.h ep0Bo.Cnt = EP0BufferSize // Defined in usbcfg.h ep0Bo.ADR = @SetupPkt ep0Bo.Stat._byte = _USIE Or _DAT0 Or _DTSEN // EP0 buff dsc init, see usbmmap.h ep0Bi.Stat._byte = _UCPU // EP0 IN buffer initialization End Sub { **************************************************************************** * Name : USBCtrlTrfOutHandler * * Purpose : This routine handles an OUT transaction according to which * * : control transfer state is currently active. Note that if the * * : the control transfer was from host to device, the session owner* * : should be notified at the end of each OUT transaction to * * : service the received data. * **************************************************************************** } Sub USBCtrlTrfOutHandler() 'usbctrltr.c // Direction of data packet: host to device. If ctrl_trf_state = CTRL_TRF_RX Then // It's the Data stage of a Control Write transfer. USBCtrlTrfRxService() // for control transfers only ' if ctrl_trf_session_owner = MUID_HID then ' // The only HID-class request with a host-to-device Data stage ' // is Set_Report. ' HandleControlOutReport() ' endif // Don't have to worry about overwriting _KEEP bit // because if _KEEP was set, TRNIF would not have been // generated in the first place. If ep0Bo.Stat.DTS = 0 Then ep0Bo.Stat._byte = _USIE Or _DAT1 Or _DTSEN Else ep0Bo.Stat._byte = _USIE Or _DAT0 Or _DTSEN EndIf Else // It's the Status stage of a Control Read transfer. USBPrepareForNextSetupTrf() EndIf End Sub { **************************************************************************** * Name : USBCtrlTrfInHandler * * Purpose : This routine handles an IN transaction according to which * * : control transfer state is currently active. A Set Address * * : Request must not change the acutal address of the device until * * : the completion of the control transfer. The end of the control * * : transfer for Set Address. Request is an in transaction. * * : Therefore it is necessary to service this unique situation when* * : the condition is right. Macro mUSBCheckAdrPendingState is * * : defined in usb9.h and its function is to specifically service * * : this event. * **************************************************************************** } Sub USBCtrlTrfInHandler() 'usbctrltr.c // Direction of data packet: device to host. mUSBCheckAdrPendingState() // Must check if in ADR_PENDING_STATE If ctrl_trf_state = CTRL_TRF_TX Then // It's the Data stage of a Control Read transfer. USBCtrlTrfTxService() If ep0Bi.Stat.DTS = 0 Then ep0Bi.Stat._byte = _USIE Or _DAT1 Or _DTSEN Else ep0Bi.Stat._byte = _USIE Or _DAT0 Or _DTSEN EndIf Else // CTRL_TRF_RX // It's the Status stage of a Control Write transfer. USBPrepareForNextSetupTrf() EndIf End Sub { **************************************************************************** * Name : USBCtrlEPService * * Require : USTAT is loaded with a valid endpoint address * * Purpose : USBCtrlEPService checks for three transaction types that it * * : knows how to service and services them. * * : 1. EP0 SETUP * * : 2. EP0 OUT * * : 3. EP0 IN * * : It ignores all other types (i.e. EP1, EP2, etc.) * **************************************************************************** } Sub USBCtrlEPService() 'usbctrltr.c If USTAT = EP00_OUT Then If GetPID = SETUP_TOKEN Then // EP0 SETUP USBCtrlTrfSetupHandler() Else // EP0 OUT USBCtrlTrfOutHandler() EndIf ElseIf USTAT = EP00_IN Then // EP0 IN USBCtrlTrfInHandler() EndIf End Sub { **************************************************************************** * Name : USBModuleEnable * * Purpose : This routine enables the USB module. An end designer should * * : never have to call this routine manually. This routine should * * : only be called from USBCheckBusStatus(). * **************************************************************************** } Sub USBModuleEnable() 'usbdrv.c UCON = 0 UIE = 0 // Mask all USB interrupts UCONBits.USBEN = 1 // Enable module & attach to bus usb_device_state = ATTACHED_STATE // Defined in usbmmap.c & .h End Sub { **************************************************************************** * Name : USBModuleDisable * * Purpose : This routine disables the USB module. An end designer should * * : never have to call this routine manually. This routine should * * : only be called from USBCheckBusStatus(). * **************************************************************************** } Sub USBModuleDisable() 'usbdrv.c UCON = 0 // Disable module & detach from bus UIE = 0 // Mask all USB interrupts usb_device_state = DETACHED_STATE // Defined in usbmmap.c & .h End Sub { **************************************************************************** * Name : USBCheckBusStatus * * Purpose : This routine enables/disables the USB module by monitoring the * * : USB power signal. * **************************************************************************** } Public Sub USBCheckBusStatus() 'usbdrv.c // Bus Attachment & Detachment Detection // usb_bus_sense is an i/o pin defined in io_cfg.h Const USB_BUS_ATTACHED = 1, USB_BUS_DETACHED = 0 If usb_bus_sense = USB_BUS_ATTACHED Then // Is USB bus attached? If UCONBits.USBEN = 0 Then // Is the module off? USBModuleEnable() // Is off, enable it EndIf Else If UCONBits.USBEN = 1 Then // Is the module on? USBModuleDisable() // Is on, disable it EndIf EndIf // After enabling the USB module, it takes some time for the voltage // on the D+ or D- line to rise high enough to get out of the SE0 condition. // The USB Reset interrupt should not be unmasked until the SE0 condition is // cleared. This helps preventing the firmware from misinterpreting this // unique event as a USB bus reset from the USB host. If usb_device_state = ATTACHED_STATE Then If UCONBits.SE0 = 0 Then UIR = 0 // Clear all USB interrupts UIE = 0 // Mask all USB interrupts UIEBits.URSTIE = 1 // Unmask RESET interrupt UIEBits.IDLEIE = 1 // Unmask IDLE interrupt usb_device_state = POWERED_STATE EndIf // else wait until SE0 is cleared EndIf End Sub { **************************************************************************** * Name : USBSoftDetach * * Purpose : The device will have to be re-enumerated to function again * * : USBSoftDetach electrically disconnects the device from the bus * * : This is done by stop supplying Vusb voltage to pull-up resistor* * : The pull-down resistors on the host side will pull both * * : differential signal lines low and the host registers the event * * : as a disconnect. * * : Since the USB cable is not physically disconnected, the power * * : supply through the cable can still be sensed by the device. The* * : next time USBCheckBusStatus() function is called, it will * * : reconnect the device back to the bus. * **************************************************************************** } Sub USBSoftDetach() 'usbdrv.c USBModuleDisable() End Sub { **************************************************************************** * Name : USBWakeFromSuspend * * Purpose : * **************************************************************************** } Sub USBWakeFromSuspend() 'usbdrv.c // If using clock switching, this is the place to restore the // original clock frequency. UCONBits.SUSPND = 0 UIEBits.ACTVIE = 0 UIRBits.ACTVIF = 0 End Sub { **************************************************************************** * Name : USBProtocolResetHandler * * Require : A USB bus reset is received from the host * * Purpose : Currently, this routine flushes any pending USB transactions. * * : It empties OUT the USTAT FIFO. This action might not be * * : desirable in some applications. * * : Once a USB bus reset is received from the host, this routine * * : should be called. It resets the device address to zero, * * : disables all non-EP0 endpoints, initializes EP0 to be ready for* * : default communication, clears all USB interrupt flags, unmasks * * : applicable USB interrupts, and reinitializes internal * * : state-machine variables. * **************************************************************************** } Sub USBProtocolResetHandler() 'usbdrv.bas UEIR = 0 // Clear all USB error flags UIR = 0 // Clears all USB interrupts UEIE = %10011111 // Unmask all USB error interrupts UIE = %01111011 // Enable all interrupts except ACTVIE UADDR = $00 // Reset to default address mDisableEP1to15() // Reset all non-EP0 UEPn registers UEP0 = EP_CTRL Or HSHK_EN // Init EP0 as a Ctrl EP, see usbdrv.h While UIRBits.TRNIF = 1 // Flush any pending transactions UIRBits.TRNIF = 0 Wend UCONBits.PKTDIS = 0 // Make sure packet processing is enabled USBPrepareForNextSetupTrf() // Declared in usbctrltrf.c usb_stat.RemoteWakeup = 0 // Default status flag to disable usb_active_cfg = 0 // Clear active configuration usb_device_state = DEFAULT_STATE End Sub { **************************************************************************** * Name : USBRemoteWakeup * * Purpose : This function should be called by user when the device is waken* * : up by an external stimulus other than ACTIVIF. Please read the * * : note below to understand the limitations. * * : The modifiable section IN this routine should be changed to * * : meet the application needs. Current implementation temporary * * : blocks other functions from executing for a period of 1-13 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 lest 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. * * : ========================================================== * * : 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 * **************************************************************************** } Sub USBRemoteWakeup() 'usbdrv.c If usb_stat.RemoteWakeup = 1 Then // Check if RemoteWakeup function // has been enabled by the host. USBWakeFromSuspend() // Unsuspend USB modue UCONBits.RESUME = 1 // Start RESUME signaling // Modifiable Section // Set RESUME line for 1-13 ms // End Modifiable Section UCONBits.RESUME = 0 EndIf End Sub { **************************************************************************** * Name : USBSuspend * * Purpose : * **************************************************************************** } Sub USBSuspend() 'usbdrv.c // NOTE: Do not clear UIRbits.ACTVIF here! // Reason: // ACTVIF is only generated once an IDLEIF has been generated. // This is a 1:1 ratio interrupt generation. // For every IDLEIF, there will be only one ACTVIF regardless of // the number of subsequent bus transitions. // // If the ACTIF is cleared here, a problem could occur when: // [ IDLE ][bus activity -> // <--- 3 ms -----> ^ // ^ ACTVIF=1 // IDLEIF=1 // # # # # (#=Program polling flags) // ^ // This polling loop will see both // IDLEIF=1 and ACTVIF=1. // However, the program services IDLEIF first // because ACTIVIE=0. // If this routine clears the only ACTIVIF, // then it can never get out of the suspend // mode. UIEBits.ACTVIE = 1 // Enable bus activity interrupt UIRBits.IDLEIF = 0 UCONBits.SUSPND = 1 // Put USB module in power conserve // mode, SIE clock inactive // At this point the PIC can go into sleep,idle, or // switch to a slower clock, etc. // Modifiable Section ' PIR2Bits.USBIF = 0 ' INTCONBits.RBIF = 0 ' PIE2Bits.USBIE = 1 // Set USB wakeup source ' INTCONBits.RBIE = 1 // Set sw2,3 wakeup source ' asm- // Goto sleep ' sleep ' end asm ' if INTCONBits.RBIF = 1 then // Check if external stimulus ' USBRemoteWakeup() // If yes, attempt RWU ' endif ' PIE2Bits.USBIE = 0 ' INTCONBits.RBIE = 0 // End Modifiable Section End Sub { **************************************************************************** * Name : USB_SOF_Handler * * Purpose : 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. * **************************************************************************** } Sub USB_SOF_Handler() 'usbdrv.c UIRBits.SOFIF = 0 usb_Attached = (usb_device_state = CONFIGURED_STATE) End Sub { **************************************************************************** * Name : USBStallHandler * * Require : A STALL packet is sent to the host by the SIE. * * Purpose : The STALLIF is set anytime the SIE sends OUT a STALL packet * * : regardless of which endpoint causes it. A Setup transaction * * : overrides the STALL function. A stalled endpoint stops stalling* * : once it receives a setup packet. In this case, the SIE will * * : accept the Setup packet and set the TRNIF flag to notify the * * : firmware. STALL function for that particular endpoint pipe will* * : be automatically disabled (direction specific). * * : There are a few reasons for an endpoint to be stalled. * * : 1. When a non-supported USB request is received. * * : Example: GET_DESCRIPTOR(DEVICE_QUALIFIER) * * : 2. When an endpoint is currently halted. * * : 3. When the device CLASS specifies that an endpoint must * * : stall in response to a specific event. * * : UEPn.EPSTALL can be scanned to see which endpoint causes the * * : stall event. * **************************************************************************** } Sub USBStallHandler() 'usbdrv.c // Does not really have to do anything here, // even for the control endpoint. // All BDs of Endpoint 0 are owned by SIE right now, // but once a Setup Transaction is received, the ownership // for EP0_OUT will be returned to CPU. // When the Setup Transaction is serviced, the ownership // for EP0_IN will then be forced back to CPU by firmware. If UEP0Bits.EPSTALL = 1 Then USBPrepareForNextSetupTrf() // Firmware work-around UEP0Bits.EPSTALL = 0 EndIf UIRBits.STALLIF = 0 End Sub { **************************************************************************** * Name : USBErrorHandler * * Purpose : The purpose of this interrupt is mainly for debugging during * * : development. Check UEIR to see which error causes the interrupt* **************************************************************************** } Sub USBErrorHandler() 'usbdrv.c UIRBits.UERRIF = 0 End Sub { **************************************************************************** * Name : USBDriverService * * Purpose : This routine is the heart of this firmware. It manages all USB * * : interrupts. Device state transitions through the following * * : stages: DETACHED -> ATTACHED -> POWERED -> DEFAULT -> * * : ADDRESS_PENDING -> ADDRESSED -> CONFIGURED -> READY * **************************************************************************** } Public Sub USBDriverService() 'usbdrv.c // Pointless to continue servicing if USB cable is not even attached. If usb_device_state = DETACHED_STATE Then Exit EndIf // Task A: Service USB Activity Interrupt If (UIRBits.ACTVIF <> 0) And (UIEBits.ACTVIE <> 0) Then USBWakeFromSuspend() EndIf // Pointless to continue servicing if the device is in suspend mode. If UCONBits.SUSPND = 1 Then usb_Attached = false Exit EndIf // Task B: Service USB Bus Reset Interrupt. // When bus reset is received during suspend, ACTVIF will be set first, // once the UCONbits.SUSPND is clear, then the URSTIF bit will be asserted. // This is why URSTIF is checked after ACTVIF. // The USB reset flag is masked when the USB state is in // DETACHED_STATE or ATTACHED_STATE, and therefore cannot // cause a USB reset event during these two states. If (UIRBits.URSTIF <> 0) And (UIEBits.URSTIE <> 0) Then USBProtocolResetHandler() EndIf // Task C: Service other USB interrupts If (UIRBits.IDLEIF <> 0) And (UIEBits.IDLEIE <> 0) Then USBSuspend() EndIf If (UIRBits.SOFIF <> 0) And (UIEBits.SOFIE <> 0) Then USB_SOF_Handler() EndIf If (UIRBits.STALLIF <> 0) And (UIEBits.STALLIE <> 0) Then USBStallHandler() EndIf If (UIRBits.UERRIF <> 0) And (UIEBits.UERRIE <> 0) Then USBErrorHandler() EndIf // Pointless to continue servicing if the host has not sent a bus reset. // Once bus reset is received, the device transitions into the DEFAULT // state and is ready for communication. If usb_device_state < DEFAULT_STATE Then Exit EndIf // Task D: Servicing USB Transaction Complete Interrupt If (UIRBits.TRNIF <> 0) And (UIEBits.TRNIE <> 0) Then // USBCtrlEPService only services transactions over EP0. // It ignores all other EP transactions. USBCtrlEPService() // Other EP can be serviced later by responsible device class firmware. // Each device driver knows when an OUT or IN transaction is ready by // checking the buffer ownership bit. // An OUT EP should always be owned by SIE until the data is ready. // An IN EP should always be owned by CPU until the data is ready. // Because of this logic, it is not necessary to save the USTAT value // of non-EP0 transactions. UIRBits.TRNIF = 0 EndIf End Sub // version 1.2 usb_Attached = false // initialise descriptor pointers... USB_CD_Ptr(0) = @cfg01 USB_CD_Ptr(1) = @cfg01 // configuration USB_SD_Ptr(0) = @sd000 // language string USB_SD_Ptr(1) = @sd001 // manufacturer string USB_SD_Ptr(2) = @sd002 // product string #if USB_HAVE_SERIAL_STRING USB_SD_Ptr(3) = @sd003 // serial number string #endif // HID defaults... #if USB_USE_HID idle_rate = 0 active_protocol = 0 #endif // CDC defaults... #if USB_USE_CDC cdc_tx_len = 0 cdc_mem_type = _RAM cdc_trf_state = CDC_TX_READY #endif // USB_USE_CDC
USBConfig.bas
{ ***************************************************************************** * Name : USBConfig.bas * * Author : David John Barker * * Notice : Copyright (c) 2007 Mecanique * * : All Rights Reserved * * Date : 15/09/2008 * * Version : 1.1a Branch by Nathan(RangerBob) for 18F87J50 Support * * : 1.1 Removed USB_SERVICE_US as ISR now uses USB interrupts * * : 1.0 Release * * Notes : USB configuration options * * : ==================== DO NOT EDIT THIS FILE =================== * * : Change the options FROM YOU MAIN PROGRAM. For example, * * : #option USB_VID = $1234 * * : This will AUTOMATICALLY override these default values. DONT * * : FORGET to add options BEFORE any include files!! * ***************************************************************************** } Module USBConfig // *************************************************************************** // device version (device_dsc) // *************************************************************************** #option USB_DEVICE_VERSION = $0110 // (BCD) #if Not (USB_DEVICE_VERSION in (0 to 65535)) #error USB_DEVICE_VERSION, "Invalid option. USB_DEVICE_VERSION must be between 0 and 65535." #endif // *************************************************************************** // descriptor string index (device_desc) // *************************************************************************** #define USB_MANUFACTURER_INDEX = 1 #define USB_PRODUCT_INDEX = 2 #variable USB_SERIAL_INDEX = 0 #option USB_HAVE_SERIAL_STRING = false #if Not (USB_HAVE_SERIAL_STRING in (true, false)) #error USB_HAVE_SERIAL_STRING, "Invalid option. USB_HAVE_SERIAL_STRING must be TRUE or FALSE." #endif #if USB_HAVE_SERIAL_STRING #variable USB_SERIAL_INDEX = 3 #endif // *************************************************************************** // bus power (cfg01) // *************************************************************************** #option USB_BUS_POWER = 50 // x2 #if Not (USB_BUS_POWER in (50 to 250)) #error USB_BUS_POWER, "Invalid option. USB_BUS_POWER must be between 50 and 250." #endif // *************************************************************************** // link in HID code option... // *************************************************************************** #option USB_USE_HID = false #if Not (USB_USE_HID in (true, false)) #error USB_USE_HID, "Invalid option. USB_USE_HID must be TRUE or FALSE." #endif // *************************************************************************** // link in CDC code option... // *************************************************************************** #option USB_USE_CDC = false #if Not (USB_USE_CDC in (true, false)) #error USB_USE_CDC, "Invalid option. USB_USE_CDC must be TRUE or FALSE." #endif // *************************************************************************** // default option for CDC... // *************************************************************************** #if USB_USE_CDC #option USB_DESCRIPTOR = "CDCDescriptor.bas" // name of descriptor file #option USB_VID = $1781 // VID - 6017 is Mecanique #option USB_PID = $07D1 // PID - 2001 #define MAX_EP_NUMBER = 3 // UEP1 minimum, up to 16 'usbcfg.h // *************************************************************************** // default options for HID... // *************************************************************************** #else #option USB_DESCRIPTOR = "HIDDescriptor.bas" // name of descriptor file #option USB_VID = $1781 // VID - 6017 is Mecanique #option USB_PID = $07D0 // PID - 2000 #define MAX_EP_NUMBER = 1 // UEP1 minimum, up to 16 'usbcfg.h #endif // *************************************************************************** // verify VID... // *************************************************************************** #if Not (USB_VID in (0 to 65535)) #error USB_VID, "Invalid option. USB_VID must be between 0 and 65535." #endif // *************************************************************************** // verify PID... // *************************************************************************** #if Not (USB_PID in (0 to 65535)) #error USB_PID, "Invalid option. USB_PID must be between 0 and 65535." #endif // *************************************************************************** // CDC options // *************************************************************************** #option CDC_RX_SIZE = 64 // CDCDataRX #if Not (CDC_RX_SIZE in (8, 16, 32, 64)) #error CDC_RX_SIZE, "Invalid option. CDC_RX_SIZE must be 8, 16, 32 or 64." #endif #option CDC_TX_SIZE = 64 // CDCDataTX #if Not (CDC_TX_SIZE in (8, 16, 32, 64)) #error CDC_TX_SIZE, "Invalid option. CDC_TX_SIZE must be 8, 16, 32 or 64." #endif // *************************************************************************** // HID buffer size // *************************************************************************** #option HID_BUFFER_SIZE = 64 // HID EP1 can be 8, 16, 32 or 64 #if Not (HID_BUFFER_SIZE in (8, 16, 32, 64)) #error HID_BUFFER_SIZE, "Invalid option. HID_BUFFER_SIZE must be 8, 16, 32 or 64." #endif // will equal HID_BUFFER_SIZE unless overridden in main program... #option HID_REPORT_BYTES_IN = HID_BUFFER_SIZE // HID Report bytes in #if Not (HID_REPORT_BYTES_IN in (8, 16, 32, 64)) #error HID_REPORT_BYTES_IN, "Invalid option. HID_REPORT_BYTES_IN must be 8, 16, 32 or 64." #endif // will equal HID_BUFFER_SIZE unless overridden in main program... #option HID_REPORT_BYTES_OUT = HID_BUFFER_SIZE // HID_Report bytes out #if Not (HID_REPORT_BYTES_OUT in (8, 16, 32, 64)) #error HID_REPORT_BYTES_OUT, "Invalid option. HID_REPORT_BYTES_OUT must be 8, 16, 32 or 64." #endif // *************************************************************************** // HID EP Polling // *************************************************************************** #option HID_EP_OUT_POLLING_MS = 1 // HID - EP01 - 1ms (EP01Out) #if Not (HID_EP_OUT_POLLING_MS in (1 to 10)) #error HID_EP_OUT_POLLING_MS, "Invalid option. HID_EP_OUT_POLLING_MS must be between 1 and 10." #endif #option HID_EP_IN_POLLING_MS = 1 // HID - EP01 - 1ms (EP01In) #if Not (HID_EP_IN_POLLING_MS in (1 to 10)) #error HID_EP_IN_POLLING_MS, "Invalid option. HID_EP_IN_POLLING_MS must be between 1 and 10." #endif // *************************************************************************** // self power sensing... // *************************************************************************** #option USB_POWER_SENSE_IO = false 'usbcfg.h #option USB_POWER_SENSE_PIN = PORTA.2 #if IsOption(USB_POWER_SENSE_PIN) And Not IsValidPortPin(USB_POWER_SENSE_PIN) #error USB_POWER_SENSE_PIN, "Invalid option. USB_POWER_SENSE_PIN must be a valid port pin." #endif #if USB_POWER_SENSE_IO 'io_cfg.h Public Dim self_power As USB_POWER_SENSE_PIN.USB_POWER_SENSE_PIN@ #else Public Const self_power = 1 #endif // *************************************************************************** // bus sense... // *************************************************************************** #option USB_BUS_SENSE_IO = false 'usbcfg.h #option USB_BUS_SENSE_PIN = PORTA.1 #if IsOption(USB_BUS_SENSE_PIN) And Not IsValidPortPin(USB_BUS_SENSE_PIN) #error USB_BUS_SENSE_PIN, "Invalid option. USB_BUS_SENSE_PIN must be a valid port pin." #endif #if USB_BUS_SENSE_IO 'io_cfg.h Public Dim usb_bus_sense As USB_BUS_SENSE_PIN.USB_BUS_SENSE_PIN@ #else Public Const usb_bus_sense = 1 #endif // *************************************************************************** // Use interrupt polling... // *************************************************************************** #option USB_SERVICE = true #if Not (USB_SERVICE in (true, false)) #error USB_SERVICE, "Invalid option. USB_SERVICE must be TRUE or FALSE." #endif #option USB_SERVICE_PRIORITY = ipHigh // set interrupt priority level... #if IsOption(USB_SERVICE_PRIORITY) And Not (USB_SERVICE_PRIORITY in (ipLow, ipHigh)) #error USB_SERVICE_PRIORITY, "Invalid option. Priority must be ipHigh or ipLow." #endif // *************************************************************************** // Timeout between CONFIGURED_STATE and setting 'USBSystem.Connected' // *************************************************************************** #option USB_CONNECT_MS = 1200 #if Not (USB_CONNECT_MS in (0 to 5000)) #error USB_CONNECT_MS, "Invalid option. USB_CONNECT_MS must be between 0 and 5000 ms." #endif // *************************************************************************** // does device support extended dual port RAM // *************************************************************************** #if _device in (18F2455, 18F2550, 18F4455, 18F4550, 18F87J50) #define USB_EXTENDED_RAM = true #else #define USB_EXTENDED_RAM = false #endif