📄 usb_ch9.asm
字号:
movwf UEIE
movlw DEFAULT_STATE
movwf USWSTAT
bcf STATUS,RP0 ; select bank 2
movlw 0x01
movwf USB_status_device ; Self powered, remote wakeup disabled
bcf STATUS,RP1 ; bank 0
#ifdef SHOW_ENUM_STATUS
bsf PORTB,1 ; set bit one to indicate Reset status
#endif
bsf STATUS,RP1
return ; to keep straight with host controller tests
; ******************************************************************
; Enable Wakeup on interupt and Activity interrupt then put the
; device to sleep to save power. Activity on the D+/D- lines will
; set the ACTIVITY interrupt, waking up the part.
; ******************************************************************
USBSleep ; starts from bank2
bsf STATUS, RP0 ; up to bank 3
bcf UIE,UIDLE
bcf UIR,UIDLE
bcf UIR,ACTIVITY
bsf UIE,ACTIVITY
bsf UCTRL, SUSPND
banksel PIR1 ; switch to bank 0
bcf PIR1,USBIF
bsf STATUS, RP1 ; switch to bank 2
bsf IS_IDLE, 0
return
; ******************************************************************
; This is activated by the STALL bit in the UIR register. It really
; just tells us that the SIE sent a STALL handshake. So far, Don't
; see that any action is required. Clear the bit and move on.
; ******************************************************************
USBStall ; starts in bank 2
bsf STATUS, RP0 ; bank 3
bcf UIR, STALL ; clear STALL
banksel PIR1 ; switch to bank 0
bcf PIR1,USBIF
bsf STATUS,RP1 ; bank 2
return
; ******************************************************************
; The SIE detected an error. This code increments the appropriate
; error counter and clears the flag.
; ******************************************************************
USBError ; starts in bank 2
bsf STATUS, RP0 ; bank 3
bcf UIR,UERR
movf UEIR,w ; get the error register
andwf UEIE,w ; mask with the enables
clrf UEIR
banksel PIR1 ; switch to bank 0
bcf PIR1,USBIF ; clear the USB interrupt flag.
bsf STATUS,RP1 ; switch to bank 2
movwf USBMaskedErrors ; save the masked errors
#ifdef COUNTERRORS
btfss USBMaskedErrors,PID_ERR
goto CRC5Error
INCREMENT16 USB_PID_ERR
CRC5Error
btfss USBMaskedErrors,CRC5
goto CRC16Error
INCREMENT16 USB_CRC5_ERR
CRC16Error
btfss USBMaskedErrors,CRC16
goto DFN8Error
INCREMENT16 USB_CRC16_ERR
DFN8Error
btfss USBMaskedErrors,DFN8
goto BTOError
INCREMENT16 USB_DFN8_ERR
BTOError
btfss USBMaskedErrors,BTO_ERR
goto WRTError
INCREMENT16 USB_BTO_ERR
WRTError
btfss USBMaskedErrors,WRT_ERR
goto OWNError
INCREMENT16 USB_WRT_ERR
OWNError
btfss USBMaskedErrors,OWN_ERR
goto BTSError
INCREMENT16 USB_OWN_ERR
BTSError
btfss USBMaskedErrors,BTS_ERR
goto EndError
INCREMENT16 USB_BTS_ERR
EndError
#endif
banksel USBMaskedInterrupts
return
; ******************************************************************
; Service the Activity Interrupt. This is only enabled when the
; device is put to sleep as a result of inactivity on the bus. This
; code wakes up the part, disables the activity interrupt and reenables
; the idle interrupt.
; ******************************************************************
USBActivity ; starts in bank 2
bsf STATUS, RP0 ; Bank 3
bcf UIE,ACTIVITY ; clear the Activity and Idle bits
bcf UIR,ACTIVITY
bcf UIR,UIDLE
bsf UIE,UIDLE
bcf UCTRL, SUSPND
banksel PIR1 ; switch to bank 0
bcf PIR1,USBIF ; clear the USB interrupt flag.
bsf STATUS,RP1 ; switch to bank 2
clrf IS_IDLE
return
; ******************************************************************
; Process token done interrupt... Most of the work gets done through
; this interrupt. Token Done is signaled in response to an In, Out,
; or Setup transaction.
; ******************************************************************
TokenDone ; starts in bank 2
COPYBUFFERDESCRIPTOR ; copy BD from dual port to unbanked RAM
banksel USTAT
movf USTAT,w ; copy USTAT register before...
bcf UIR,TOK_DNE ; clearing the token done interrupt.
banksel PIR1 ; switch to bank 0
bcf PIR1,USBIF ; clear the USB interrupt flag.
bsf STATUS,RP1 ; switch to bank 2
movwf USB_USTAT ; Save USTAT in bank 2
#ifdef SHOW_ENUM_STATUS
; This toggles the activity bits on portB (EP0 -> Bit 5; EP1 -> bit 6; EP2 -> bit 7)
bcf STATUS,RP1 ; bank 0
andlw 0x18 ; save endpoint bits
pagesel tryEP1activity
btfss STATUS,Z ; is it EP0?
goto tryEP1activity
movlw 0x20
pagesel maskport
goto maskport
tryEP1activity
xorlw 0x08 ; is it bit one?
btfss STATUS,Z
movlw 0x80 ; No, It's not EP0, nor 1 so it must be EP2. toggle bit 7
btfsc STATUS,Z
movlw 0x40 ; Yes, toggle bit 6 to Show EP1 activity
maskport
xorwf PORTB,f
bsf STATUS,RP1 ; bank 2
#endif
; check UOWN bit here if desired
movf BufferDescriptor,w ; get the first byte of the BD
andlw 0x3c ; save the PIDs
movwf PIDs
xorlw TOKEN_IN
pagesel TokenInPID
btfsc STATUS,Z
goto TokenInPID
movf PIDs,w
xorlw TOKEN_OUT
pagesel TokenOutPID
btfsc STATUS,Z
goto TokenOutPID
movf PIDs,w
xorlw TOKEN_SETUP
pagesel TokenSetupPID
btfsc STATUS,Z
goto TokenSetupPID
return ; should never get here...
; ******************************************************************
; Process out tokens
; For EP0, just turn the buffer around. There should be no EP0
; tokens to deal with.
; EP1 and EP2 have live data destined for the application
; ******************************************************************
TokenOutPID ; STARTS IN BANK2
movf USB_USTAT,w ; get the status register
pagesel tryEP1
btfss STATUS,Z ; was it EP0?
goto tryEP1 ; no, try EP1
movf USB_dev_req,w
xorlw HID_SET_REPORT
pagesel ResetEP0OutBuffer
btfss STATUS,Z
goto ResetEP0OutBuffer
HIDSetReport
; ******************************************************************
; You must write your own SET_REPORT routine. The following
; commented out code is provided if you desire to make a SET_REPORT
; look like a EP1 OUT Interrupt transfer. Uncomment it and use it
; if you desire this functionality.
; ******************************************************************
; movlw 0xFF
; movwf USB_dev_req ; clear the request type
; banksel BD1IST
; movf BD0OST,w
; movwf BD1OST ; Copy status register to EP1 Out
; movf BD0OAL,w ; get EP0 Out buffer address
; bcf STATUS,RP0 ; bank 2
; movwf hid_source_ptr
; bsf STATUS,RP0 ; bank 3
; movf BD1OAL,w ; get EP1 Out Buffer Address
; bcf STATUS,RP0 ; bank 2
; movwf hid_dest_ptr
; bsf STATUS,RP0 ; bank 3
; movf BD0OBC,w ; Get byte count
; movwf BD1OBC ; copy to EP1 Byte count
; bcf STATUS,RP0 ; bank 2
; movwf counter
; bankisel BD1IST ; indirectly to bank 3
;HIDSRCopyLoop
; movf hid_source_ptr,w
; movwf FSR
; movf INDF,w
; movwf temp
; movf hid_dest_ptr,w
; movwf FSR
; movf temp,w
; movwf INDF
; incf hid_source_ptr,f
; incf hid_dest_ptr,f
; decfsz counter,f
; goto HIDSRCopyLoop
;
; bsf STATUS,RP0 ; bank 3
; movlw 0x08
; movwf BD0OST ; REset EP0 Status back to SIE
ResetEP0OutBuffer
bsf STATUS,RP0 ; no, just reset buffer and move on.
movlw 0x08 ; it's EP0.. buffer already copied,
movwf BD0OBC ; just reset the buffer
movlw 0x88
movwf BD0OST ; set OWN and DTS Bit
pagesel Send_0Len_pkt
bcf STATUS,RP0 ; bank 2
goto Send_0Len_pkt
return
tryEP1 ; bank 3
xorlw 0x08 ; was it EP1?
pagesel tryEP2
btfss STATUS,Z
goto tryEP2
; **** Add Callout here to service EP1 in transactions. ****
return
tryEP2 ; bank 3
movf USB_USTAT,w
xorlw 0x10 ; was it EP2?
btfsc STATUS,Z
return ; unrecognized EP (Should never take this exit)
; **** Add Callout here to service EP2 in transactions. ****
return
; ******************************************************************
; Process in tokens
; ******************************************************************
TokenInPID ; starts in bank2
; Assumes EP0 vars are setup in a previous call to setup.
EP0_in
movf USB_USTAT,w ; get the status register
andlw 0x18 ; save only EP bits (we already know it's an IN)
pagesel tryEP1in
btfss STATUS,Z ; was it EP0?
goto tryEP1in ; no, try EP1
movf USB_dev_req,w
xorlw GET_DESCRIPTOR
pagesel check_GSD
btfss STATUS,Z
goto check_GSD
pagesel copy_descriptor_to_EP0
call copy_descriptor_to_EP0
goto exitEP0in
; Check for Get String Descriptor
check_GSD
movf USB_dev_req,w
xorlw GET_STRING_DESCRIPTOR
pagesel check_SA
btfss STATUS,Z
goto check_SA
pagesel copy_descriptor_to_EP0
call copy_descriptor_to_EP0
pagesel exitEP0in
goto exitEP0in
; Check for Set Address
check_SA
movf USB_dev_req,w
xorlw SET_ADDRESS
pagesel check_SF
btfss STATUS,Z
goto check_SF
pagesel finish_set_address
call finish_set_address
pagesel exitEP0in
goto exitEP0in
check_SF
movf USB_dev_req,w
xorlw SET_FEATURE
pagesel check_CF
btfss STATUS,Z
goto check_CF
pagesel exitEP0in
goto exitEP0in
check_CF
movf USB_dev_req,w
xorlw CLEAR_FEATURE
pagesel Class_Specific
btfss STATUS,Z
goto Class_Specific
movf BufferData+4, w ; clear endpoint 1 stall bit
xorlw 1
pagesel clear_EP2
btfss STATUS,Z
goto clear_EP2
bsf STATUS, RP0 ; bank 3
bsf UEP1, EP_STALL
bcf STATUS, RP0 ; bank 2
pagesel exitEP0in
goto exitEP0in
clear_EP2
movf BufferData+wIndex, w ; clear endpoint 2 stall bit
xorlw 2
pagesel exitEP0in
btfss STATUS,Z
goto exitEP0in
bsf STATUS, RP0 ; bank 3
bsf UEP2, EP_STALL
bcf STATUS, RP0 ; bank 2
pagesel exitEP0in
goto exitEP0in
Class_Specific
pagesel Check_Class_Specific_IN
goto Check_Class_Specific_IN
exitEP0in
return
; ******************************************************************
; though not required, it might be nice to have a callback function here
; that would take some action like setting up the next buffer when the
; previous one is complete. Not necessary because the same functionality
; can be provided through the PutUSB call.
; ******************************************************************
tryEP1in ; starts in bank 2
xorlw 0x08 ; was it EP1?
pagesel tryEP1in
btfss STATUS,Z
goto tryEP2in
; **** Add Callout here to service EP1 in transactions. ****
return
tryEP2in ; starts in bank 2
; **** Add Callout here to service EP2 in transactions. ****
return
; ******************************************************************
; Return a zero length packet on EP0 In
; ******************************************************************
Send_0Len_pkt
global Send_0Len_pkt
banksel BD0IBC
clrf BD0IBC ; set byte count to 0
movlw 0xc8
movwf BD0IST ; set owns bit
bcf STATUS,RP0 ; back to bank 2
clrf USB_dev_req
return
; ********************************************************************
; process setup tokens
; ******************************************************************
TokenSetupPID ; starts in bank 2
bsf STATUS,IRP ; indirectly to pages 2/3
movf BufferDescriptor+ADDRESS,w ; get the status register
movwf FSR ; save in the FSR.
movf INDF,w
movwf BufferData ; in shared RAM
incf FSR,f
movf INDF,w
movwf BufferData+1
incf FSR,f
movf INDF,w
movwf BufferData+2
incf FSR,f
movf INDF,w
movwf BufferData+3
incf FSR,f
movf INDF,w
movwf BufferData+4
incf FSR,f
movf INDF,w
movwf BufferData+5
incf FSR,f
movf INDF,w
movwf BufferData+6
incf FSR,f
movf INDF,w
movwf BufferData+7
bsf STATUS, RP0 ; bank 3
movlw 0x08
movwf BD0OBC ; reset the byte count too.
movwf BD0IST ; return the in buffer to us (dequeue any pending requests)
bcf STATUS, RP0 ; bank 2
movf BufferData+bmRequestType, w
xorlw HID_SET_REPORT ; set EP0 OUT UOWNs back to SIE
movlw 0x88 ; set DATA0/DATA1 packet according to request type
btfsc STATUS, Z
movlw 0xC8
bsf STATUS, RP0 ; bank 3
movwf BD0OST
bcf UCTRL,PKT_DIS ; Assuming there is nothing to dequeue, clear the packet disable bit
bcf STATUS,RP0 ; bank 2
clrf USB_dev_req ; clear the device request..
movf BufferData+bmRequestType,w
pagesel HostToDevice
btfsc STATUS,Z
goto HostToDevice
movf BufferData+bmRequestType,w
xorlw 0x01 ; test for host to Interface tokens
pagesel HostToInterface
btfsc STATUS,Z
goto HostToInterface
movf BufferData+bmRequestType,w
xorlw 0x02 ; test for host to Endpoint tokens
pagesel HostToEndpoint
btfsc STATUS,Z
goto HostToEndpoint
movf BufferData+bmRequestType,w
xorlw 0x80 ; test for device to Host tokens
pagesel DeviceToHost
btfsc STATUS,Z
goto DeviceToHost
movf BufferData+bmRequestType,w
xorlw 0x81 ; test for device to Interface tokens
pagesel InterfaceToHost
btfsc STATUS,Z
goto InterfaceToHost
movf BufferData+bmRequestType,w
xorlw 0x82 ; test for device to Endpoint tokens
pagesel EndpointToHost
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -