; -------------------------------------------------------------------- TITLE "usb14k.asm: pic18f14K50 cdc based usb boot loader" ; -------------------------------------------------------------------- ; This code is probably full of bugs - use it at your own risk ; 2012/12, paddy.strebel@datacomm.ch - NO WARRANTY, NO SUPPORT ; -------------------------------------------------------------------- ; ; A simple USB bootloader to program flash memory > H'0800'. ; (based on usbolo.asm, adapted to reduced resources) ; ; Serial communication over the USB is available to the user: ; usbrx EQU H'0004' ; USB receive to W, usbds while waiting ; usbtx EQU H'0006' ; USB transmit W as soon as buffer ready ; usbds EQU H'0012' ; USB driver service (keeps USB alive) ; usbbr EQU H'0014' ; USB busy receive to W: carry set if ok ; usbbt EQU H'0016' ; USB busy transmit W: carry set if ok ; ; usbds must be called regularly for USB handling (polling or interrupt), ; The blocking i/o routines usbrx and usbtx call usbds while waiting. ; BSR, FSRs, TBLPTR are conserved (W and STATUS if applicable). ; ; User code example at H'0800' implements a USB to RS232 adapter: ; 9600bps, 8 bits, no parity, handshake signals are not available. ; ; - Code memory < H'0800' is reserved. User code starts at H'0800'. ; - High/low priority interrupts are passed to H'0808'/H'0818'. ; - RAM 0x40..0x5F and 0x200..0x2FF and USB registers are reserved. ; ; The USB code basically refers to Microchip's CDC example in C. ; Includes bug fixes from 2007-May-14. Compiles with gpasm/mpasm. ; For simplicity the device is BUS POWERED (VDD from USB, max 100mA) ; ; ; ********** ********** ; * *** * ; VDD=5V-------- VDD ** 1 20 ** VSS------------GND ; * * ; +--15p---+---CLKI ** 2 19 ** RA0-----USB+ ; GND 12MHz * * USB ; +--15p---+---CLKO ** 3 18 ** RA1-----USB- ; * * ; +-------BOLO/MCLR ** 4 17 ** VUSB---220nf---GND ; | * * ; ) jumper RC5 ** 5 16 ** RC0 ; | * * ; GND RC4 ** 6 15 ** RC1 ; * * ; RC3 ** 7 14 ** RC2 ; * * ; RC6 ** 8 13 ** RB4 ; * * ; RC7 ** 9 12 ** RB5-----(RX) ; * * ; (TX)-----RB7 ** 10 11 ** RB6 ; * PIC18F14K50 * ; *********************** ; ; For initial programming with an ICD device remove the jumper and ; connect VPP to MCLR and VDD, VSS, PGD, PGC to the USP connector. ; ; Remove jumper to start bootloader. At boot time the BOLO pin weak pullup ; is temporarly enabled to test if the jumper is set. ; ; If the bootloader receives "UP", flash from H'0800' to H'3FFF' is erased. ; Erase time is about 1s. Code memory is programmed from intel hex data. ; If "UV" is received first, memory is only verified against hex data. ; Addresses below H'0800', configuration bits and id locations are ignored. ; ; On a Linux box a little script can do the upload (hexfile as parameter): ; ; #!/bin/sh ; stty -F /dev/ttyACM0 -echo -icanon min 0 time 2 clocal cread -isig ; xterm -e tail -f /dev/ttyACM0 & ; sleep 1 ; echo UP > /dev/ttyACM0 ; sleep 1 ; cat $1 > /dev/ttyACM0 ; sleep 2 ; killall tail ; ; Status report to USB: '*'=hello '?'=ready '+'=success '-'=error ; ; Some routines at fixed addresses might be useful for convenience: ; usbof EQU H'000C' ; USB off, leaving USB RAM uninitialized ; usbon EQU H'000E' ; USB on if previously turned off ; usblo EQU H'0010' ; jump to bootloader, disable interrupts ; usarx EQU H'001C' ; USART receive, usbds while waiting ; usatx EQU H'001E' ; USART transmit as soon as buffer ready ; ; Watchdog: Though not enabled here, it is regularly reset by the bootloader ; and also by the blocking read routines usarx and rxusb. ; ; Performance: a loopback data rate of about 73kB/s (compares to 730kbps on ; an asynchronous serial port) has been measured with the following loop: ; ; loop CALL usbrx ; CALL usbtx ; BRA loop ; ; Start a terminal to receive data : gtkterm -p /dev/ttyACM0 & ; Measure time to transmit 1MB data : time cat file_of_1MB > /dev/ttyACM0 ; ; Interrupts: usbds can be called by an interrupt instead of polling: ; ; ORG H'0818' ; low priority interrupt service ; CALL usbds ; BCF PIR2,USBIF ; RETFIE ; ; Enable the USB interrupt: ; ; BSF RCON,IPEN ; enable interrupt priorities ; BCF IPR2,USBIP ; set low priority ; BSF PIE2,USBIE ; enable USB interrupt ; BSF INTCON,GIEL ; enable low priority interrupts ; BSF INTCON,GIEH ; enable high priority interrupts ; ; Using USB interrupt, it's not necessary to call usbds (and you shouldn't). ; The routines usbrx and usarx skip usbds calls if USB interrupts are used. ; ; Suspend: ; ; On bus idle sleep mode is entered (USB suspend, current supply < 500uA) ; The user may install handlers for USBSuspend() and USBWakeFromSuspend() ; at H'0810' and H'0814' and enable suspend callback (susen at H'0002'). ; Due to a bug I can't find the device gets a new address after suspend. LIST P=18F14K50 INCLUDE p18f14k50.inc ; PLLEN = ON/OFF for 12MHz/48MHz cristal CONFIG CPUDIV = NOCLKDIV, USBDIV = OFF CONFIG FOSC = HS, PLLEN=ON, PCLKEN=ON, FCMEN = OFF, IESO = OFF CONFIG PWRTEN = OFF, BOREN = ON, BORV = 30 CONFIG WDTEN = OFF, WDTPS = 32768 CONFIG HFOFST = OFF, MCLRE = OFF CONFIG STVREN = OFF, LVP = OFF, BBSIZ = ON, XINST = OFF; (ENHCPU/BKBUG) CONFIG CP0 = OFF, CP1 = OFF CONFIG CPB = OFF, CPD = OFF CONFIG WRT0 = OFF, WRT1 = OFF CONFIG WRTC = OFF, WRTB = OFF, WRTD = OFF CONFIG EBTR0 = OFF, EBTR1 = OFF CONFIG EBTRB = OFF ;;; constants: A EQU 0 ; use access bank B EQU 1 ; use BSR bank BOLO EQU 3 ; PORTA bootloader pin (=MCLR/VPP) CODEBEG EQU H'08' ; first flash bank erased by the bootloader CODEEND EQU H'3F' ; last flash bank erased by the bootloader EP0SIZ EQU D'8' ; ep0 buffer size BULSIZ EQU D'64' ; bulk buffer size USBANK EQU H'02' ; upper byte of USB RAM address ; devstate (usb_device_state) DETACHED_ST EQU H'00' ; no bits ATTACHED_ST EQU H'01' ; bit 0 POWERED_ST EQU H'02' ; bit 1 DEFAULT_ST EQU H'04' ; bit 2 ADDRPEND_ST EQU H'08' ; bit 3 ADDRESS_ST EQU H'10' ; bit 4 CONFIGURED_ST EQU H'20' ; bit 5 ; ctrlstate (ctrl_trf_state) WAIT_SETUP EQU H'00' CTRL_TRF_TX EQU H'01' CTRL_TRF_RX EQU H'02' ; Microchip USB class id MUID_NULL EQU D'0' MUID_USB9 EQU D'1' MUID_CDC EQU D'2' ; descriptor types DSC_DEV EQU D'1' DSC_CFG EQU D'2' DSC_STR EQU D'3' DSC_INTF EQU D'4' DSC_EP EQU D'5' ; offsets in USB buffer descriptors ST EQU 0 ; status CN EQU 1 ; count AL EQU 2 ; address low AH EQU 3 ; address high ;;; variables: can't be placed with RES directive in absolute mode (why?) tmp0 EQU H'040' ; temporary storage savw EQU H'041' ; context save savs EQU H'042' savb EQU H'043' savf0l EQU H'044' savf0h EQU H'045' savf1l EQU H'046' savf1h EQU H'047' devstate EQU H'048' ; usb_device_state usbflags EQU H'049' ; 7:int 5:4:lc 3:prg 2:t1buf 1:rom 0:rwake activecfg EQU H'04A' ; usb_active_cfg altinterf EQU H'04B' ; usb_alt_intf (not an array) ctrlstate EQU H'04C' ; ctrl_trf_state shortpkt EQU H'04D' ; short_pkt_status ctrlowner EQU H'04E' ; ctrl_trf_session_owner wcount EQU H'04F' ; wCount (1 byte only) pdstl EQU H'050' ; pDst pdsth EQU H'051' psrcl EQU H'052' ; pSrc psrch EQU H'053' rxcnt EQU H'054' rxpos EQU H'055' txcnt EQU H'056' bram EQU H'058' ; bootloader variables (temporary 8 bytes) ; USB buffer descriptors (bank 2), pingpong support of SIE not used EP0BO EQU H'200' ; setuppkt EP0BI EQU H'204' ; ctrldata EP1BO EQU H'208' EP1BI EQU H'20C' ; cdcntice EP2BO EQU H'210' ; cdcrxbuf EP2BI EQU H'214' ; cdctxbuf (software controlled pingpong) savtpl EQU H'218' ; TBLPTR save location savtph EQU H'219' savtpu EQU H'21A' ; USB buffers setuppkt EQU H'220' ; 8 SetupPkt ctrldata EQU H'228' ; 8 CtrlTrfData dummypkt EQU H'230' ; 8 dummy_encapsulated_cmd_response cdcntice EQU H'230' ; 0 cdc_notice (overlaps dummypkt) linecod EQU H'238' ; 7 line_coding ctrlsig EQU H'23F' ; 1 control_signal_bitmap cdcrxbuf EQU H'240' ; 64 cdc_data_rx cdct0buf EQU H'280' ; 64 cdc_data_tx (ping) cdct1buf EQU H'2C0' ; 64 cdc_data_tx (pong) ;;; code: reset, interrupt vectors, exported calls ORG H'0000' ; reset BRA start ORG H'0002' susen BRA susen0 ; H'02' suspend callback enable usbrx BRA usbrx0 ; H'04' wait for USB receive usbtx BRA usbtx0 ; H'06' wait for USB transmit ORG H'0008' ; high priority interrupt GOTO H'0808' ; to user code area ORG H'000C' usbof BRA usbof0 ; H'0C' turn USB off usbon BRA usbon0 ; H'0E' turn USB on usblo BRA usblo0 ; H'10' jump to bootloader usbds BRA usbds0 ; H'12' USB tasks, USBDriverService() usbbr BRA usbbr0 ; H'14' busy receive, getsUSBUSART() usbbt BRA usbbt0 ; H'16' busy transmit, mUSBUSARTTxRam ORG H'0018' ; low priority interrupt GOTO H'0818' ; to user code area ORG H'001C' usarx BRA usarx0 ; H'1C' wait for USART receive usatx BRA usatx0 ; H'1E' wait for USART transmit ;;; usb descriptors (DB defines always an even number of bytes!) ORG H'0020' devdsc DB DEVSIZ,DSC_DEV ; device descriptor: size, type DW H'0200' ; USB spec release in BCD format DB H'02',H'00' ; class:communication, subclass:0 DB H'00',EP0SIZ ; protocol, max ep0 buffer size DW H'16C0' ; vend ID: http://www.voti.nl/pids DW H'03EB' ; prod ID: 03E8..03F1 free for lab DW H'0010' ; device release 0.1 in BCD format DB H'01',H'02' ; manufact str idx, product str idx DB H'00',H'01' ; dev s/n str idx, number of configs DEVSIZ EQU $-devdsc ; size is H'12' = D'18' cfg01 ; conf(9) ;(csize,type,lenlo,lenhi,#intf,#con,stridx,attr=buspwr DB D'9', DSC_CFG, CFGSIZ, H'00', 2, 1, 0, B'10000000' ; intf(9) ;*2mA)(size,type,inf#,alt#,#ep,class,acm,V25TER,stridx) DB D'50', D'9', DSC_INTF, 0, 0, 1, 2, 2, 1, 0 ; head(5)acm(4) ;(size,type,subt,bcdlo,bcdhi)(size,type,subt,cap)(size DB D'5', H'24', 0, H'10', H'01', D'4', H'24', 2, 2, D'5' ; uni(5)call(5) ;type,subt,ctri,subi)(size,type,subt,cap,dati)(size DB H'24', H'6', 0, 1, D'5', H'24', H'1', 0, 1, D'7' ; endpoint(7) ;type,adr,attr,maxlo,maxhi,interval) DB DSC_EP, H'81', 3, EP0SIZ, H'00', D'2' ; intf(9) ;(size,type,inf#,alt#,#ep,class,acm,class,stridx)(size DB D'9', DSC_INTF, 1, 0, 2, D'10', 0, 0, 0, D'7' ; endpoint(7) ;type,adr,attr,maxlo,maxhi,interval) DB DSC_EP, H'02', 2, BULSIZ, H'00', 0 ; endpoint(7) ;(size,type,adr,attr,maxlo,maxhi,interval)(padbyte) DB D'7', DSC_EP, H'82', 2, BULSIZ, H'00', 0, 0 CFGSIZ EQU $-cfg01-1 ; -1 to truncate the padded byte ; string descriptors sd000 DB SD0SIZ, DSC_STR ; size, type, language DW H'0409' SD0SIZ EQU $-sd000 sd001 DB SD1SIZ, DSC_STR DW 'p','.','s','.' SD1SIZ EQU $-sd001 sd002 DB SD2SIZ, DSC_STR DW 'U','S','B','o','L','o' SD2SIZ EQU $-sd002 ;;; start: initialization, usb, bootloader ORG H'00A0' start BCF INTCON2,7 ; enable AB pullups temporarly LFSR FSR0,0 ; init RAM 0..0x2FF to 0 initvar CLRF POSTINC0 BTFSS FSR0H,1 BRA initvar BTFSS FSR0H,0 BRA initvar MOVF PORTA,W ; read the BOLO pin BSF INTCON2,7 ; disable AB pullups again ANDLW H'08' ; bit 3 set? usblo0 BNZ bolo ; bootloader RCALL usbon0 ; user code GOTO H'0800' ; ------- begin bootloader ------- bolo CLRF INTCON ; disable interrupts BCF PIE2,USBIE ; disable USB interrupt RCALL usbon0 ; if called after usbof BCF usbflags,3,A ; program/verify MOVLW '*' ; hello RCALL bolot bolob RCALL bolor ; first char is 'U'? XORLW 'U' BNZ bolob ; no: try again RCALL bolor XORLW 'V' ; second char is 'V'? BZ bolon ; yes: skip erase XORLW 'V' XORLW 'P' ; second char is 'P'? BNZ boloe ; no: error BSF usbflags,3,A ; set flag (program) CLRF TBLPTRL ; erase flash begin MOVLW CODEBEG MOVWF TBLPTRH CLRF TBLPTRU boloc RCALL usbds0 ; usbds between erase blocks CLRWDT ; and clear watchdog MOVLW B'10010100' ; EEPGD, FREE, WREN MOVWF EECON1 RCALL bolow MOVLW D'64' ADDWF TBLPTRL,F BTFSC STATUS,C INCF TBLPTRH,F MOVF TBLPTRH,W ; erase flash end? SUBLW CODEEND BC boloc bolon MOVLW '?' ; ready RCALL bolot bolol RCALL bolor ; wait for new hex record XORLW ':' BNZ bolol CLRF bram+2,A ; checksum RCALL bolox MOVWF bram+3,A ; count RCALL bolox MOVWF bram+5,A ; address high byte RCALL bolox MOVWF bram+4,A ; address low byte RCALL bolox MOVWF bram+7,A DECF bram+7,W,A ; type == 01 ? (eof) BZ bolok ; ok! MOVF bram+7,W,A ; type == 04 ? (extaddr) XORLW H'04' BZ bolou MOVF bram+7,W,A ; type != 00 ? (unknown) BNZ bolon MOVF bram+3,W,A ; count == 00 ? BZ bolon MOVLW CODEBEG ; ignore address < CODEBEG*256 SUBWF bram+5,W,A BNC bolon MOVF bram+5,W,A ; ignore address >= CODEEND*256 SUBLW CODEEND BNC bolon MOVF bram+6,W,A ; ignore address >= H'010000' BNZ bolon MOVF bram+4,W,A ; tablepointer to flash holding register MOVWF TBLPTRL ; which are preloaded with H'FF' MOVF bram+5,W,A MOVWF TBLPTRH CLRF TBLPTRU BTFSS usbflags,3,A ; program or verify? BRA bolov bolod RCALL bolox ; latch count data bytes MOVWF TABLAT TBLWT*+ DECFSZ bram+3,F,A BRA bolod RCALL bolox ; read checksum MOVF bram+2,W,A BNZ boloe MOVF bram+4,W,A ; table pointer to 16 byte write block ANDLW H'F0' ; limitted to records with max 16 bytes MOVWF TBLPTRL ; (pic18f13K50 has 8 byte write block!) MOVF bram+5,W,A MOVWF TBLPTRH CLRF TBLPTRU MOVLW B'10000100' ; EEPGD, WREN MOVWF EECON1 RCALL bolow CLRF EECON1 ; disable flash writes BRA bolon ; next record bolov RCALL bolox ; verify count data bytes TBLRD*+ ; read to compare XORWF TABLAT,W BNZ boloe DECFSZ bram+3,F,A BRA bolov RCALL bolox ; read checksum MOVF bram+2,W,A BNZ boloe BRA bolon ; next record bolou RCALL bolox ; extended address RCALL bolox MOVWF bram+6,A ; address upper byte RCALL bolox ; checksum MOVF bram+2,W,A BNZ boloe BRA bolon bolok MOVLW '+' ; success RCALL bolot CLRF bram+0,A ; counter bolop RCALL usbds0 ; purge rx buffer RCALL usbbr0 BTFSC STATUS,C CLRF bram+0,A DECFSZ bram+0,F,A BRA bolop GOTO H'0800' boloe MOVLW '-' ; error RCALL bolot boloj BRA boloj ; loop forever bolow MOVLW H'55' ; flash write sequence MOVWF EECON2 MOVLW H'AA' MOVWF EECON2 BSF EECON1,1 RETURN bolox RCALL boloh ; receive two digit hex byte MOVWF bram+1,A SWAPF bram+1,F,A RCALL boloh IORWF bram+1,W,A ADDWF bram+2,F,A ; update checksum RETURN boloh RCALL bolor ; receive hex char MOVWF bram+0,A BTFSC bram+0,6,A ADDLW H'09' ANDLW H'0F' RETURN bolor BRA usbrx0 bolot BRA usbtx0 ; ------- end bootloader ------- ; susen0: enable suspend callback susen0 BSF usbflags,6 RETURN ; usbrx0: wait for USB receive, handling USB while waiting usbrxs BTFSS PIE2,USBIE RCALL usbds0 ; skip if USB interrupt usbrx0 CLRWDT RCALL usbbr0 BNC usbrxs RETURN ; usbtx0: queue for USB transmit, handling USB while buffer full usbtxs BTFSS PIE2,USBIE RCALL usbds0 ; skip if USB interrupt usbtx0 RCALL usbbt0 BNC usbtxs RETURN ; usarx0: USART receive char, handling USB while waiting usarxs BTFSS PIE2,USBIE RCALL usbds0 ; skip if USB interrupt usarx0 CLRWDT BTFSS PIR1,5 BRA usarxs MOVF RCREG,W RETURN ; usatx0: USART transmit char, won't block a long time usatx0 BTFSS TXSTA,TRMT BRA usatx0 MOVWF TXREG RETURN ; initialize USB variables and turn USB on usbon0 BTFSC UCON,USBEN ; USB off? RETURN RCALL regsav ; save context MOVLW D'24' ; init USB variables >= devstate MOVWF tmp0,A LFSR FSR0,devstate iniuvar CLRF POSTINC0 DECFSZ tmp0,F,A BRA iniuvar MOVLW B'00010100' ; external pullup, full speed MOVWF UCFG BSF UCON,USBEN ; bus-power: enable usb directly BSF devstate,1,A ; go straight to POWERED_ST waitse0 BTFSC UCON,SE0 ; wait until out of SE0 BRA waitse0 CLRF UIR ; clear all USB interrupts CLRF UIE ; mask all USB interrupts BSF UIE,URSTIE ; unmask RESET interrupt BSF UIE,IDLEIE ; unmask IDLE interrupt BRA regrst ; restore context ; turn USB off, leaving USB RAM uninitialized usbof0 BTFSS UCON,USBEN ; USB on? RETURN CLRF UCON ; turn USB module off RCALL regsav ; save context RCALL dis1to7 ; change BSR, disable EP1..EP7 CLRF UEP0,B ; and disable EP0 RCALL regrst ; restore modified context of caller CLRF UIE ; mask all USB interrupts CLRF UIR ; clear all USB interrupts RETURN ;;; usbbr0: read byte from cdcrxbuf, pass to SIE when empty, getsUSBUSART() usbbr0 BCF usbflags,7,A ; disable USB interrupt temporarily BTFSC PIE2,USBIE BSF usbflags,7,A BCF PIE2,USBIE RCALL regsav ; save context MOVLB USBANK ; BSR to bank of buffer descriptors CLRF savw,A ; clear data and carry (in saved context) BCF savs,C,A BTFSS UCON,USBEN ; USB on? BRA usbbrx BTFSS devstate,5,A ; configured? BRA usbbrx BTFSC UCON,SUSPND ; suspend? BRA usbbrx BTFSC EP2BO+ST,7,B ; if (!mCDCUsartRxIsBusy()) BRA usbbrx MOVF rxcnt,W,A ; still data in buffer? BNZ usbbrb MOVF EP2BO+CN,W,B ; no: it is a new buffer BZ usbbra ; arm empty buffer (should not happen) MOVWF rxcnt,A ; init count CLRF rxpos ; index to buffer start usbbrb LFSR FSR0,cdcrxbuf MOVF rxpos,W,A ADDWF FSR0L,F MOVF INDF0,W ; set data and carry MOVWF savw,A BSF savs,C,A INCF rxpos,F,A ; index to next byte DECF rxcnt,F,A ; arm buffer if empty BNZ usbbrx usbbra MOVLW BULSIZ ; prepare ep for new rx MOVWF EP2BO+CN,B MOVLW H'48' ; mUSBBufferReady(CDC_BULK_BD_OUT) BTFSC EP2BO+ST,6,B ; DTSEN, toggled DTS MOVLW H'08' MOVWF EP2BO+ST,B ; write descriptor status BSF EP2BO+ST,7,B ; set USIE separately usbbrx RCALL regrst ; restore modified context of caller BTFSC usbflags,7,A ; if USB interrupt BSF PIE2,USBIE ; reenable USB interrupt RETURN ;;; usbbt0: write byte to current cdctxbuf (transmit handled by cdcsrv) usbbt0 BCF usbflags,7,A ; disable USB interrupt temporarily BTFSC PIE2,USBIE BSF usbflags,7,A BCF PIE2,USBIE RCALL regsav ; save context MOVLB USBANK ; BSR to bank of buffer descriptors BCF savs,C,A ; clear carry (in saved context) BTFSS UCON,USBEN ; USB on? BRA usbbtx BTFSS devstate,5,A ; configured? BRA usbbtx BTFSC UCON,SUSPND ; suspend? BRA usbbtx MOVLW BULSIZ-1 ; buffer almost full? SUBWF txcnt,W,A BC usbbtx MOVLW HIGH(cdct0buf) ; no: write to current user buffer MOVWF FSR0H MOVLW LOW(cdct0buf) BTFSC usbflags,2,A MOVLW LOW(cdct1buf) MOVWF FSR0L MOVF txcnt,W,A ADDWF FSR0L,F MOVF savw,W,A MOVWF INDF0 INCF txcnt,F,A ; increment counter BSF savs,C,A ; set carry usbbtx BTFSC usbflags,7,A ; if USB interrupt RCALL cdcsrv ; independent of interrupt RCALL regrst ; restore modified context of caller BTFSC usbflags,7,A ; if USB interrupt BSF PIE2,USBIE ; reenable USB interrupt RETURN ;;; usbds0: USBDriverService(), CDCTxService(), "Spaghetticode" usbds0 BTFSS UCON,USBEN ; USB on? RETURN RCALL regsav ; save context MOVFF FSR1L,savf1l MOVFF FSR1H,savf1h MOVLB USBANK ; BSR to bank of buffer descriptors BTFSS UIR,ACTVIF ; USBDriverService() BRA cksusp ; task a: service usb activity interrupt BTFSS UIE,ACTVIE BRA cksusp uwake BCF UCON,SUSPND ; USBWakeFromSuspend() BCF UIE,ACTVIE BTFSC usbflags,6 ; if callback enabled: CALL H'0814' clract BTFSS UIR,ACTVIF ; clear ACTVIF according to datasheet BRA cksusp BCF UIR,ACTVIF BRA clract cksusp BTFSC UCON,SUSPND ; pointless to continue if in suspend BRA usbdsx BTFSS UIR,URSTIF ; task b: service usb bus reset interrupt BRA ckidle BTFSS UIE,URSTIE BRA ckidle uprst CLRF UIR ; USBProtocolResetHandler() MOVLW B'00111001' ; STALLIE, IDLEIE, TRNIE, URSTIE MOVWF UIE RCALL dis1to7 ; change BSR, disable EP1..EP7 CLRF UADDR,B MOVLW B'00010110' ; init EP0 as a ctrl ep MOVWF UEP0,B MOVLB USBANK ; BSR to bank of buffer descriptors uprst1 BTFSS UIR,TRNIF BRA uprst2 BCF UIR,TRNIF RCALL delay6 ; TRNIF needs time BRA uprst1 uprst2 BCF UCON,PKTDIS ; make sure packet processing is enabled RCALL prnxset ; USBPrepareForNextSetupTrf() BCF usbflags,0,A ; disable remote wakeup CLRF activecfg,A MOVLW DEFAULT_ST MOVWF devstate,A ckidle BTFSS UIR,IDLEIF ; task c: check IDLEIF BRA ckstal BTFSS UIE,IDLEIE BRA ckstal BSF UIE,ACTVIE ; USBSuspend(), ena bus activity interrupt BCF UIR,IDLEIF BSF UCON,SUSPND ; USB module in power conserve mode ; /* Modifiable Section */ BTFSC usbflags,6 ; if callback enabled: CALL H'0810' ; reduce power to < 0.5mA until uwake BCF PIR2,USBIF ; set USB wakeup source BSF PIE2,USBIE ; SLEEP BCF PIE2,USBIE ; ; /* End Modifiable Section */ ckstal BTFSS UIR,STALLIF ; task c: check STALLIF BRA ckdefl BTFSS UIE,STALLIE BRA ckdefl MOVLB H'0F' ; F53h to F5Fh not in virtual bank BTFSS UEP0,EPSTALL,B ; USBStallHandler(), if(UEP0bits.EPSTALL == 1) BRA ustalh1 MOVLB USBANK ; BSR to bank of buffer descriptors MOVLW H'80' ; if ep0Bo.Stat._byte == _USIE XORWF EP0BO+ST,W,B BNZ ustalh0 MOVLW H'84' ; && ep0Bi.Stat._byte == _USIE|_BSTALL XORWF EP0BI+ST,W,B BNZ ustalh0 MOVLW H'8C' ; ep0Bo.Stat._byte = _USIE|_DAT0|_DTSEN|_BSTALL MOVWF EP0BO+ST,B ustalh0 MOVLB H'0F' ; F53h to F5Fh not in virtual bank BCF UEP0,EPSTALL,B ; UEP0bits.EPSTALL = 0 ustalh1 MOVLB USBANK ; BSR to bank of buffer descriptors BCF UIR,STALLIF ; UIRbits.STALLIF = 0 ckdefl MOVLW DEFAULT_ST ; done if devstate < DEFAULT_ST SUBWF devstate,W,A BNC usbdsx BCF usbflags,4 ; task D: transaction complete interrupt BCF usbflags,5 trncisl BTG usbflags,4 ; interrupt service loop (max 4 times) BTFSC usbflags,4 BRA trncint BTG usbflags,5 BTFSS usbflags,5 BRA usbdsx trncint BTFSS UIR,TRNIF ; if (UIRbits.TRNIF && UIEbits.TRNIE) BRA usbdsx BTFSS UIE,TRNIE BRA usbdsx ep0osrv MOVF USTAT,W ; USBCtrlEPService() BNZ ep0isrv ; if (USTAT == EP00_OUT) BCF UIR,TRNIF MOVF EP0BO+ST,W,B ; if (ep0Bo.Stat.PID == SETUP_TOKEN) ANDLW H'3C' XORLW B'00110100' BNZ ep0osr0 RCALL ctshan ; USBCtrlTrfSetupHandler() BRA trncisl ep0osr0 RCALL ctohan ; USBCtrlTrfOutHandler() BRA trncisl ep0isrv MOVF USTAT,W ; if(USTAT == EP00_IN) XORLW H'04' BNZ trncisx BCF UIR,TRNIF RCALL ctihan ; USBCtrlTrfInHandler() BRA trncisl trncisx BCF UIR,TRNIF RCALL delay4 ; TRNIF needs time BRA trncisl usbdsx RCALL cdcsrv MOVFF savf1l,FSR1L ; restore context MOVFF savf1h,FSR1H BRA regrst ;;; CDCTxService is very simple, since no full buffers are transmitted cdcsrv BTFSC EP2BI+ST,7,B ; CDCUsartTxIsBusy BRA cdcsrvx MOVF txcnt,W,A ; nothing to transmit BZ cdcsrvx MOVWF EP2BI+CN,B ; user buffer to SIE MOVLW LOW(cdct0buf) BTFSC usbflags,2,A MOVLW LOW(cdct1buf) MOVWF EP2BI+AL,B MOVLW H'C8' ; mUSBBufferReady(CDC_BULK_BD_IN); BTFSC EP2BI+ST,6,B ; _USIE | _DTSEN, with toggled DTS MOVLW H'88' MOVWF EP2BI+ST,B BTG usbflags,2,A ; swap tx buffers CLRF txcnt,A ; user buffer now empty cdcsrvx RETURN ;;; do not nest regsav/regrst calls! regsav MOVFF STATUS,savs ; save STATUS, W, BSR, FSR0 MOVWF savw,A MOVFF BSR,savb MOVFF FSR0L,savf0l MOVFF FSR0H,savf0h RETURN regrst MOVFF savf0l,FSR0L ; restore FSR0, BSR, W, STATUS MOVFF savf0h,FSR0H MOVFF savb,BSR MOVF savw,W,A MOVFF savs,STATUS RETURN dis1to7 MOVLB H'0F' ; F53h to F5Fh not in virtual bank CLRF UEP1,B ; Disable EP1..EP7 CLRF UEP2,B CLRF UEP3,B CLRF UEP4,B CLRF UEP5,B CLRF UEP6,B CLRF UEP7,B RETURN ; BSR must be restored after call delay4 RETURN ; call consumes 4 cycles delay6 BRA delay4 ; call consumes 6 cycles ; USBCtrlTrfSetupHandler() ctshan BTFSC EP0BI+ST,7,B ; if (ep0Bi.Stat.UOWN) CLRF EP0BI+ST,B ; take back from SIE CLRF shortpkt,A ; SHORT_PKT_NOT_USED CLRF ctrlstate,A ; stage 1, WAIT_SETUP CLRF ctrlowner,A ; MUID_NULL CLRF wcount,A ; wCount = 0 RCALL ckstdrq ; stage 2, USBCheckStdRequest() MOVF ctrlowner,W,A ; only 1 ClassReqHandler BNZ ctlepsc ; break if != MUID_NULL ckcdcrq MOVF setuppkt,W,B ; USBCheckCDCRequest() ANDLW H'1F' ; if (SetupPkt.Recipient == RCPT_INTF) XORLW H'01' BNZ ckcdcrqx MOVF setuppkt,W,B ; && (SetupPkt.RequestType == CLASS) ANDLW H'60' XORLW B'00100000' BNZ ckcdcrqx MOVF setuppkt+4,W,B ; && (SetupPkt.bIntfID == 0 or 1) ANDLW H'FE' ; CDC_COMM_INTF_ID or CDC_DATA_INTF_ID BNZ ckcdcrqx ckcdcrq0 MOVF setuppkt+1,W,B ; switch(SetupPkt.bRequest) BNZ ckcdcrq1 MOVLW LOW(dummypkt) ; SEND_ENCAPSULATED_COMMAND MOVWF psrcl,A MOVLW HIGH(dummypkt) MOVWF psrch,A BCF usbflags,1,A ; set memory type RAM MOVLW EP0SIZ ; set packet count MOVWF wcount,A BRA ckcdcrqu ; ctrl_trf_session_owner = MUID_CDC ckcdcrq1 DECF setuppkt+1,W,B BNZ ckcdcrq2 MOVLW LOW(dummypkt) ; GET_ENCAPSULATED_RESPONSE MOVWF pdstl,A MOVLW HIGH(dummypkt) MOVWF pdsth,A BRA ckcdcrqu ; ctrl_trf_session_owner = MUID_CDC ckcdcrq2 MOVF setuppkt+1,W,B XORLW H'20' ; SET_LINE_CODING BNZ ckcdcrq3 MOVLW LOW(linecod) MOVWF pdstl,A MOVLW HIGH(linecod) MOVWF pdsth,A BRA ckcdcrqu ; ctrl_trf_session_owner = MUID_CDC ckcdcrq3 MOVF setuppkt+1,W,B XORLW H'21' ; GET_LINE_CODING BNZ ckcdcrq4 MOVLW LOW(linecod) MOVWF psrcl,A MOVLW HIGH(linecod) MOVWF psrch,A BCF usbflags,1,A ; set memory type RAM MOVLW D'7' ; set packet count MOVWF wcount,A BRA ckcdcrqu ; ctrl_trf_session_owner = MUID_CDC ckcdcrq4 MOVF setuppkt+1,W,B XORLW H'22' ; SET_CONTROL_LINE_STATE BNZ ckcdcrqx MOVF setuppkt+2,W,B ; control_signal_bitmap MOVWF ctrlsig,B ckcdcrqu MOVLW MUID_CDC ; ctrl_trf_session_owner = MUID_CDC MOVWF ctrlowner,A ckcdcrqx ;;; USBCtrlEPServiceComplete(): ctlepsc BCF UCON,PKTDIS ; first thing to do (another story) MOVF ctrlowner,W,A ; MUID_NULL? BNZ ctlepsc0 MOVLW EP0SIZ MOVWF EP0BO+CN,B MOVLW LOW(setuppkt) ; ep0Bo.ADR = &SetupPkt MOVWF EP0BO+AL,B MOVLW HIGH(setuppkt) MOVWF EP0BO+AH,B MOVLW B'10000100' ; ep0Bo.Stat = _USIE|_BSTALL MOVWF EP0BO+ST,B MOVWF EP0BI+ST,B ; ep0Bi.Stat = _USIE|_BSTALL RETURN ctlepsc0 BTFSS setuppkt+0,7,B ; if DEV_TO_HOST BRA ctlepsc2 MOVF setuppkt+7,W,B ; bugfix: if host offers buffer > 255 BNZ ctlepsc1 ; as Windows when getting the cfg dsc MOVF wcount,W,A ; if SetupPkt.wLength < wCount SUBWF setuppkt+6,W,B BC ctlepsc1 MOVF setuppkt+6,W,B ; wCount = SetupPkt.wLength MOVWF wcount,A ctlepsc1 RCALL ctltxsrv ; USBCtrlTrfTxService() MOVLW CTRL_TRF_TX; MOVWF ctrlstate,A MOVLW EP0SIZ MOVWF EP0BO+CN,B MOVLW LOW(setuppkt) ; ep0Bo.ADR = &SetupPkt MOVWF EP0BO+AL,B MOVLW HIGH(setuppkt) MOVWF EP0BO+AH,B MOVLW B'10000000' ; ep0Bo.Stat = _USIE MOVWF EP0BO+ST,B MOVLW LOW(ctrldata) ; ep0Bi.ADR = &CtrlTrfData MOVWF EP0BI+AL,B MOVLW HIGH(ctrldata) MOVWF EP0BI+AH,B MOVLW B'11001000' ; ep0Bi.Stat = _USIE|_DAT1|_DTSEN MOVWF EP0BI+ST,B RETURN ctlepsc2 MOVLW CTRL_TRF_RX; MOVWF ctrlstate,A CLRF EP0BI+CN,B MOVLW B'11001000' ; ep0Bi.Stat = _USIE|_DAT1|_DTSEN MOVWF EP0BI+ST,B MOVLW EP0SIZ MOVWF EP0BO+CN,B MOVLW LOW(ctrldata) ; ep0Bo.ADR = &CtrlTrfData MOVWF EP0BO+AL,B MOVLW HIGH(ctrldata) MOVWF EP0BO+AH,B MOVLW B'11001000' ; ep0Bo.Stat = _USIE|_DAT1|_DTSEN MOVWF EP0BO+ST,B RETURN ; USBCtrlTrfOutHandler() ctohan BTFSS ctrlstate,1,A ; if (ctrl_trf_state == CTRL_TRF_RX) BRA prnxset ; USBPrepareForNextSetupTrf, return from there ctlrxsrv MOVF EP0BO+CN,W,B ; USBCtrlTrfRxService() MOVWF tmp0,A ; byte_to_read ADDWF wcount,F,A ; accumulate total number of bytes read LFSR FSR1,ctrldata ; psrc = FSR1 = ctrldata MOVF pdstl,W,A ; pdst = FSR0 MOVWF FSR0L MOVF pdsth,W,A MOVWF FSR0H RCALL copyram MOVLW H'C8' ; if (ep0Bo.Stat.DTS == 0) BTFSC EP0BO+ST,6,B ; ep0Bo.Stat = _USIE|_DAT1|_DTSEN MOVLW H'88' ; else ep0Bo.Stat = _USIE|_DAT0|_DTSEN MOVWF EP0BO+ST,B RETURN ; USBCtrlTrfInHandler() ctihan BTFSS devstate,3,A ; if ADDRPEND_ST BRA ctihan0 MOVLW DEFAULT_ST MOVWF devstate,A MOVF setuppkt+2,W,B ; UADDR = SetupPkt.bDevADR MOVLB H'0F' ; F53h to F5Fh not in virtual bank MOVWF UADDR MOVLB USBANK ; BSR to bank of buffer descriptors XORLW 0 BZ ctihan0 MOVLW ADDRESS_ST ; if (UADDR > 0) MOVWF devstate,A ctihan0 BTFSS ctrlstate,0,A ; if CTRL_TRF_TX BRA prnxset ; USBPrepareForNextSetupTrf, return from there RCALL ctltxsrv ; USBCtrlTrfTxService() BTFSS shortpkt,1,A ; if (short_pkt_status == SHORT_PKT_SENT) BRA ctihan1 MOVLW H'84' ; ep0Bi.Stat = _USIE|_BSTALL MOVWF EP0BI+ST,B BRA ctihan2 ctihan1 MOVLW H'C8' ; if (ep0Bi.Stat.DTS == 0) BTFSC EP0BI+ST,6,B ; ep0Bi.Stat = _USIE|_DAT1|_DTSEN MOVLW H'88' ; else ep0Bi.Stat = _USIE|_DAT0|_DTSEN MOVWF EP0BI+ST,B ctihan2 RETURN ; USBPrepareForNextSetupTrf() prnxset MOVLW LOW(setuppkt) ; ep0Bo.ADR = &SetupPkt MOVWF EP0BO+AL,B ; (taken out of if-else) MOVLW HIGH(setuppkt) MOVWF EP0BO+AH,B BTFSS ctrlstate,1,A ; if (ctrl_trf_state == CTRL_TRF_RX) { BRA prnxset0 BTFSS UCON,PKTDIS ; && (UCONbits.PKTDIS == 1) BRA prnxset0 MOVF EP0BO+CN,W,B ; && (ep0Bo.Cnt == sizeof(CTRL_TRF_SETUP)) XORLW EP0SIZ BNZ prnxset0 MOVF EP0BO+ST,W,B ; && (ep0Bo.Stat.PID == SETUP_TOKEN) ANDLW B'10111100' ; && (ep0Bo.Stat.UOWN == 0)) { XORLW B'00110100' BNZ prnxset0 MOVLW EP0SIZ ; copy ctrldata to setuppkt MOVWF tmp0,A LFSR FSR0,setuppkt LFSR FSR1,ctrldata prnxcp MOVF POSTINC1,W MOVWF POSTINC0 DECFSZ tmp0,F,A BRA prnxcp RETURN prnxset0 CLRF ctrlstate,A ; } else { ctrl_trf_state = WAIT_SETUP; MOVLW EP0SIZ MOVWF EP0BO+CN,B MOVLW B'10001100' ; ep0Bo.Stat = _USIE|_DAT0|_DTSEN|_BSTALL MOVWF EP0BO+ST,B CLRF EP0BI+ST,B ; ep0Bi.Stat = _UCPU } RETURN ; USBCheckStdRequest() ckstdrq MOVF setuppkt,W,B ; if (SetupPkt.RequestType == STANDARD) ANDLW H'60' BTFSS STATUS,Z RETURN MOVF setuppkt+1,W,B BZ sgshan ; 0 GET_STATUS, USBStdGetStatusHandler() ADDLW -1 BZ sfrhan ; 1 CLR_FEATURE, USBStdFeatureReqHandler() ADDLW -2 BZ sfrhan ; 3 SET_FEATURE, USBStdFeatureReqHandler() ADDLW -2 BZ ckstdrq5 ; 5 SET_ADR ADDLW -1 BZ sgdhan ; 6 GET_DSC, USBStdGetDscHandler() ADDLW -1 BZ ckstdrqx ; 7 SET_DSC ADDLW -1 BZ ckstdrq8 ; 8 GET_CFG ADDLW -1 BZ ckstdrq9 ; 9 SET_CFG, USBStdSetCfgHandler() ADDLW -1 BZ ckstdrqa ; 10 GET_INTF ADDLW -1 BZ ckstdrqb ; 11 SET_INTF BRA ckstdrqx ; 12 SYNCH_FRAME and others ckstdrq5 MOVLW ADDRPEND_ST ; SET_ADR, update state only MOVWF devstate,A BRA ckstdrqu ; ctrl_trf_session_owner = MUID_USB9 ckstdrq8 MOVLW LOW(activecfg) ; GET_CFG, set source MOVWF psrcl,A MOVLW HIGH(activecfg) MOVWF psrch,A BCF usbflags,1,A ; set memory type RAM MOVLW 1 ; set data count 1 MOVWF wcount,A BRA ckstdrqu ; ctrl_trf_session_owner = MUID_USB9 ckstdrq9 RCALL dis1to7 ; change BSR, disable EP1..EP7 MOVLB USBANK ; BSR to bank of buffer descriptors CLRF altinterf,A ; the array has only 1 item MOVF setuppkt+2,W,B ; usb_active_cfg = SetupPkt.bCfgValue MOVWF activecfg,A BZ sschan0 MOVLW CONFIGURED_ST ; if (SetupPkt.bCfgValue > 0) MOVWF devstate,A BRA cdciep ; CDCInitEP, return from there sschan0 MOVLW ADDRESS_ST ; if (SetupPkt.bCfgValue == 0) MOVWF devstate,A BRA ckstdrqu ; ctrl_trf_session_owner = MUID_USB9 ckstdrqa MOVLW LOW(altinterf) ; GET_INTF, set source MOVWF psrcl,A ; only 1 usb_alt_intf MOVLW HIGH(altinterf) MOVWF psrch,A BCF usbflags,1,A ; set memory type RAM MOVLW 1 ; set data count 1 MOVWF wcount,A BRA ckstdrqu ; ctrl_trf_session_owner = MUID_USB9 ckstdrqb MOVF setuppkt+2,W,B ; SET_INTF, SetupPkt.bAltID MOVWF altinterf,A ; only 1 usb_alt_intf ckstdrqu MOVLW MUID_USB9 ; ctrl_trf_session_owner = MUID_USB9 MOVWF ctrlowner,A ckstdrqx RETURN ; USBStdGetStatusHandler() sgshan CLRF ctrldata+0,B ; initialize content CLRF ctrldata+1,B MOVF setuppkt,W,B ; switch(setuppkt.recipient) ANDLW H'1F' BZ sgshan0 ; 0 RCPT_DEV ADDLW -1 BZ sgshan1 ; 1 CLR_FEATURE ADDLW -2 BZ sgshan2 ; 3 SET_FEATURE BRA sgshan3 sgshan0 MOVLW MUID_USB9 ; case RCPT_DEV MOVWF ctrlowner,A MOVLW B'00000000' ; bit1: rem wakeup ena, bit0: selfpowered MOVWF setuppkt+0,B BRA sgshan3 sgshan1 MOVLW MUID_USB9 ; case CLR_FEATURE MOVWF ctrlowner,A BRA sgshan3 sgshan2 MOVLW MUID_USB9 ; case SET_FEATURE MOVWF ctrlowner,A RCALL adrcal BTFSC INDF0,2 ; if (*pDst.bRam & _BSTALL) BSF ctrldata+0,0,B ; set bit0 sgshan3 MOVLW MUID_USB9 ; end switch CPFSEQ ctrlowner,A RETURN MOVLW LOW(ctrldata) ; psrc = &ctrldata MOVWF psrcl,A MOVLW HIGH(ctrldata) MOVWF psrch,A BCF usbflags,1,A ; set memory type RAM MOVLW D'2' ; set data count = 2 MOVWF wcount,A RETURN ; USBStdFeatureReqHandler() sfrhan DECF setuppkt+2,W,B ; if (Feature == DEVICE_REMOTE_WAKEUP) BNZ sfrhan0 MOVF setuppkt,W,B ; && (Recipient == RCPT_DEV) ANDLW H'1F' BNZ sfrhan0 MOVLW MUID_USB9 MOVWF ctrlowner,A BCF usbflags,0,A MOVF setuppkt+1,W,B ; if (bRequest == SET_FEATURE) XORLW H'03' BTFSC STATUS,Z BSF usbflags,0,A ; enable remote wakeup sfrhan0 MOVF setuppkt+2,W,B ; if (Feature == ENDPOINT_HALT) BNZ sfrhanx MOVF setuppkt,W,B ; && (Recipient == RCPT_EP) ANDLW H'1F' XORLW H'02' BNZ sfrhanx MOVF setuppkt+4,W,B ; && (EPNum > 0) ANDLW H'0F' BZ sfrhanx MOVLW MUID_USB9 MOVWF ctrlowner,A RCALL adrcal MOVF setuppkt+1,W,B; if (bRequest == SET_FEATURE) XORLW H'03' BNZ sfrhan1 MOVLW H'84' ; *pdst = _USIE|_BSTALL MOVWF INDF0 BRA sfrhanx sfrhan1 MOVLW H'00' ; if EPDir *pdst = _UCPU BTFSS setuppkt+4,7,B MOVLW H'88' ; else *pdst = _USIE|_BSTALL MOVWF INDF0 sfrhanx RETURN ; USBStdGetDscHandler() sgdhan MOVF setuppkt,W,B ; if (bmRequestType == H'80') XORLW H'80' BNZ sgdhanx sgdhan1 DECF setuppkt+3,W,B ; switch(SetupPkt.bDscType) BNZ sgdhan2 ; 1 DSC_DEV LFSR FSR0,devdsc MOVLW DEVSIZ BRA sgdhans sgdhan2 MOVF setuppkt+3,W,B ; 2 DSC_CFG XORLW DSC_CFG BNZ sgdhan3 LFSR FSR0,cfg01 MOVLW CFGSIZ BRA sgdhans sgdhan3 MOVF setuppkt+3,W,B ; 3 DSC_STR XORLW DSC_STR BNZ sgdhanx INCF setuppkt+2,W,B ; string index + 1 to tmp MOVWF tmp0,A LFSR FSR0,sd000 MOVLW SD0SIZ DECF tmp0,F,A ; index 0? BZ sgdhans LFSR FSR0,sd001 MOVLW SD1SIZ DECF tmp0,F,A ; index 1? BZ sgdhans LFSR FSR0,sd002 ; index >= 2 MOVLW SD2SIZ sgdhans MOVWF wcount,A ; data count in W MOVF FSR0L,W ; address in FSR0 MOVWF psrcl,A MOVF FSR0H,W MOVWF psrch,A BSF usbflags,1,A ; set memory type ROM sgdhanu MOVLW MUID_USB9 ; ctrl_trf_session_owner = MUID_USB9 MOVWF ctrlowner,A sgdhanx RETURN ; CDCInitEP() cdciep MOVLW MUID_USB9 ; ctrl_trf_session_owner = MUID_USB9 MOVWF ctrlowner,A MOVLW LOW(D'9600') ; baud rate: 4 bytes MOVWF linecod+0 MOVLW HIGH(D'9600') MOVWF linecod+1 MOVLW UPPER(D'9600') MOVWF linecod+2 CLRF linecod+3 CLRF linecod+4 ; 1 stop bit CLRF linecod+5 ; no parity MOVLW H'08' ; 8 data bits MOVWF linecod+6 MOVLB H'0F' ; F53h to F5Fh not in virtual bank MOVLW B'00011010' ; enable 1 comm pipe MOVWF UEP1,B ; CDC_COMM_EP MOVLW B'00011110' ; enable 2 data pipes MOVWF UEP2,B ; CDC_DATA_EP MOVLB USBANK ; BSR to bank of buffer descriptors MOVLW LOW(cdcntice) ; cdc_notice MOVWF EP1BI+AL,B MOVLW HIGH(cdcntice) MOVWF EP1BI+AH,B MOVLW H'40' MOVWF EP1BI+ST,B MOVLW LOW(cdcrxbuf) ; cdc_data_rx MOVWF EP2BO+AL,B MOVLW HIGH(cdcrxbuf) MOVWF EP2BO+AH,B MOVLW BULSIZ MOVWF EP2BO+CN,B MOVLW H'88' ; arm buffer MOVWF EP2BO+ST,B MOVLW LOW(cdct0buf) ; cdc_data_tx MOVWF EP2BI+AL,B MOVLW HIGH(cdct0buf) MOVWF EP2BI+AH,B MOVLW H'40' MOVWF EP2BI+ST,B RETURN ; address calculation: pDst=&ep0Bo+(SetupPkt.EPNum*8)+(SetupPkt.EPDir*4) adrcal MOVLW USBANK ; pDst = FSR0 = &ep0Bo MOVWF FSR0H MOVWF pdsth,A MOVF setuppkt+4,W,B ; +(SetupPkt.EPNum*8) ANDLW H'0F' MOVWF pdstl,A SWAPF pdstl,F,A ; * 16 RRNCF pdstl,W,A ; / 2 BTFSC setuppkt+4,7,B ; +(SetupPkt.EPDir*4) ADDLW D'4' MOVWF FSR0L MOVWF pdstl,A RETURN ; USBCtrlTrfTxService() ; ctltxsrv MOVLW EP0SIZ ; if (wCount < EP0SIZ) SUBWF wcount,W,A BC ctltxsv0 BTFSS shortpkt,1,A ; 0 -> 1; 1 -> 2; INCF shortpkt,F,A MOVF wcount,W,A ; byte_to_send = wcount BRA ctltxsv1 ctltxsv0 MOVLW EP0SIZ ; else byte_to_send = EP0SIZ ctltxsv1 MOVWF tmp0,A MOVWF EP0BI+CN,B ; BC7..0 = byte_to_send SUBWF wcount,F,A ; wcount -= byte_to_send LFSR FSR0,ctrldata BTFSS usbflags,1,A ; if (ctrl_trf_mem == _ROM) BRA ctltxsv4 MOVFF TBLPTRL,savtpl ; save TBLPTR MOVFF TBLPTRH,savtph MOVFF TBLPTRU,savtpu MOVF psrcl,W,A ; ROM pointer to psrc MOVWF TBLPTRL MOVF psrch,W,A MOVWF TBLPTRH CLRF TBLPTRU ctltxsv2 MOVF tmp0,W,A ; while byte_to_send BZ ctltxsv3 TBLRD*+ ; *pDst = *pSrc MOVF TABLAT,W MOVWF POSTINC0 DECF tmp0,F,A ; byte_to_send-- BRA ctltxsv2 ctltxsv3 MOVF TBLPTRL,W ; update psrc MOVWF psrcl,A MOVF TBLPTRH,W MOVWF psrch,A MOVFF savtpl,TBLPTRL ; restore TBLPTR MOVFF savtph,TBLPTRH MOVFF savtpu,TBLPTRU BRA copyramd ctltxsv4 MOVF psrcl,W,A ; RAM pointer to psrc MOVWF FSR1L MOVF psrch,W,A MOVWF FSR1H ; copy tmp0 bytes FRS1 to FSR0 and write FSRS back to psrc and pdst copyram MOVF tmp0,W,A ; while byte_to_send BZ copyrams MOVF POSTINC1,W ; *pDst = *pSrc MOVWF POSTINC0 DECF tmp0,F,A ; byte_to_send-- BRA copyram copyrams MOVF FSR1L,W ; update psrc MOVWF psrcl,A MOVF FSR1H,W MOVWF psrch,A copyramd MOVF FSR0L,W ; update pdst MOVWF pdstl,A MOVF FSR0H,W MOVWF pdsth,A RETURN ; -------------------------------------------------------------------- ; user code example ; -------------------------------------------------------------------- ; the essential USB routines ; -------------------------- usbds EQU H'0012' ; USB driver service usbbr EQU H'0014' ; busy receive usbbt EQU H'0016' ; busy transmit ; small helpers for convenience ; ----------------------------- usarx EQU H'001C' ; wait for USART receive usatx EQU H'001E' ; wait for USART transmit usbrx EQU H'0004' ; wait for USB receive usbtx EQU H'0006' ; wait for USB transmit ; relocated reset address and interrupt vectors ; --------------------------------------------- ORG H'0800' ; user code example GOTO mstart ORG H'0808' ; high priority interrupt RETFIE ORG H'0818' ; low priority interrupt RETFIE ; simple USB to RS232 adapter, connect RB7 to RB5 for echo ; -------------------------------------------------------- ORG H'0820' mstart BCF ANSELH,3 ; RB5/AN11/RX digital MOVLW HIGH(D'1249') ; (48MHz/4/9600)-1 MOVWF SPBRGH MOVLW LOW(D'1249') MOVWF SPBRG MOVLW B'00001000' ; BRG16 MOVWF BAUDCON MOVLW B'00100100' ; TXEN (turns TX to output), BRGH MOVWF TXSTA MOVLW B'10010000' ; SPEN, CREN MOVWF RCSTA mloop CALL usbds ; USB device service in main loop CALL usbbr ; nonblocking check for USB receive BTFSC STATUS,C CALL usatx ; if byte received: send to USART BTFSS PIR1,5 ; nonblocking check for USART receive BRA mloop MOVF RCREG,W ; if byte received: send to USB CALL usbtx BRA mloop END ; end of usb14k.asm