📄 usbdrvasm.s
字号:
cpi x1, 0 ;1 brne sendNakAndReti ;1 no buffer space available / {30, 38} from SE0 end; 2006-03-11: The following two lines fix a problem where the device was not; recognized if usbPoll() was called less frequently than once every 4 ms. cpi cnt, 4 ;1 zero sized data packets are status phase only -- ignore and ack brmi sendAckAndReti ;1 keep rx buffer clean -- we must not NAK next SETUP sts usbRxLen, cnt ;2 store received data, swap buffers sts usbRxToken, x2 ;2 lds x1, usbAppBuf ;2 sts usbAppBuf, YL ;2 sts usbInputBuf, x1 ;2 buffers now swapped rjmp sendAckAndReti ;2 -> {43, 51} from SE0 endhandleIn: ; {18, 26} from SE0 end cp x2, shift ;1 shift contains our device addr brne rxDoReturn ;1 other device#if USB_CFG_HAVE_INTRIN_ENDPOINT sbrc x3, 7 ;2 x3 contains addr + endpoint rjmp handleIn1 ;0#endif lds cnt, usbTxLen ;2 sbrc cnt, 4 ;2 rjmp sendCntAndReti ;0 -> {27, 35} from SE0 end ldi x1, USBPID_NAK ;1 sts usbTxLen, x1 ;2 buffer is now free ldi YL, lo8(usbTxBuf) ;1 ldi YH, hi8(usbTxBuf) ;1 rjmp usbSendAndReti ;2 -> {34, 43} from SE0 end; Comment about when to set usbTxLen to USBPID_NAK:; We should set it back when we receive the ACK from the host. This would; be simple to implement: One static variable which stores whether the last; tx was for endpoint 0 or 1 and a compare in the receiver to distinguish the; ACK. However, we set it back immediately when we send the package,; assuming that no error occurs and the host sends an ACK. We save one byte; RAM this way and avoid potential problems with endless retries. The rest of; the driver assumes error-free transfers anyway.otherOutOrSetup: clr x1 sts usbCurrentTok, x1rxDoReturn: pop x3 ;2 pop YL ;2 pop YH ;2 rjmp sofError ;2isSetupOrOut: ; we must be fast here -- a data package may follow / {,24} into next frame cp x2, shift ;1 shift contains our device addr brne otherOutOrSetup ;1 other device -- ignore#if USB_CFG_IMPLEMENT_FN_WRITEOUT /* if we need second OUT endpoint, store endpoint address */ andi x1, 0x7f ;1 mask out MSb in token andi x3, 0x80 ;1 mask out all but endpoint address or x1, x3 ;1 merge endpoint into currentToken sts usbCurrentTok, x1 ;2 brmi dontResetEP0 ;1 endpoint 1 -> don't reset endpoint 0 input#else sts usbCurrentTok, x1 ;2#endif;A transmission can still have data in the output buffer while we receive a;SETUP package with an IN phase. To avoid that the old data is sent as a reply,;we abort transmission. We don't need to reset usbMsgLen because it is used;from the main loop only where the setup is processed anyway. ldi x1, USBPID_NAK ;1 sts usbTxLen, x1 ;2 abort transmissiondontResetEP0: pop x3 ;2 pop YL ;2 in x1, USB_INTR_PENDING;1 sbrc x1, USB_INTR_PENDING_BIT;1 check whether data is already arriving {,41} into next frame rjmp shortcutToStart ;2 save the pops and pushes -- a new interrupt is aready pending;If the jump above was not taken, we can be at {,2} into the next frame here pop YH ;2txDoReturn:sofError: ; error in start of frame -- ignore frame ldi x1, 1<<USB_INTR_PENDING_BIT;1 many int0 events occurred during our processing -- clear pending flag out USB_INTR_PENDING, x1;1 pop shift ;2 pop cnt ;2 pop x2 ;2 pop x1 ;2 out SREG, x1 ;1 pop x1 ;2 reti ;4 -> {,21} into next frame -> up to 3 sync bits missedsendCntAndReti: ; 19 cycles until SOP mov x3, cnt ;1 rjmp usbSendX3 ;2sendNakAndReti: ; 19 cycles until SOP ldi x3, USBPID_NAK ;1 rjmp usbSendX3 ;2sendAckAndReti: ; 17 cycles until SOP ldi x3, USBPID_ACK ;1usbSendX3: ldi YL, 20 ;1 'x3' is R20 ldi YH, 0 ;1 ldi cnt, 2 ;1;;;;rjmp usbSendAndReti fallthrough; USB spec says:; idle = J; J = (D+ = 0), (D- = 1) or USBOUT = 0x01; K = (D+ = 1), (D- = 0) or USBOUT = 0x02; Spec allows 7.5 bit times from EOP to SOP for replies (= 60 cycles);usbSend:;pointer to data in 'Y';number of bytes in 'cnt' -- including sync byte;uses: x1...x4, shift, cnt, YusbSendAndReti: ; SOP starts 13 cycles after call push x4 ;2 ldi x4, USBMASK ;1 exor mask sbi USBOUT, USBMINUS;1 prepare idle state; D+ and D- must have been 0 (no pullups) in x1, USBOUT ;1 port mirror for tx loop sbi USBDDR, USBMINUS;1 sbi USBDDR, USBPLUS ;1 set D+ and D- to output: acquire bus; need not init x2 (bitstuff history) because sync starts with 0 ldi shift, 0x80 ;1 sync byte is first byte sent rjmp txLoop ;2 -> 13 + 3 = 16 cycles until SOP#if USB_CFG_HAVE_INTRIN_ENDPOINT /* placed here due to relative jump range */handleIn1: ;{23, 31} from SE0 ldi x1, USBPID_NAK ;1#if USB_CFG_HAVE_INTRIN_ENDPOINT3; 2006-06-10 as suggested by O.Tamura: support second INTR IN / BULK IN endpoint ldd x2, y+2 ;2 sbrc x2, 0 ;2 1 rjmp handleIn3 ;0 2#endif lds cnt, usbTxLen1 ;2 sbrc cnt, 4 ;2 rjmp sendCntAndReti ;0 sts usbTxLen1, x1 ;2 ldi YL, lo8(usbTxBuf1);1 ldi YH, hi8(usbTxBuf1);1 rjmp usbSendAndReti ;2 -> arrives at usbSendAndReti {34, 42} from SE0#if USB_CFG_HAVE_INTRIN_ENDPOINT3handleIn3: lds cnt, usbTxLen3 ;2 sbrc cnt, 4 ;2 rjmp sendCntAndReti ;0 sts usbTxLen3, x1 ;2 ldi YL, lo8(usbTxBuf3);1 ldi YH, hi8(usbTxBuf3);1 rjmp usbSendAndReti ;2 -> arrives at usbSendAndReti {39, 47} from SE0#endif#endifbitstuff0: ;1 (for branch taken) eor x1, x4 ;1 ldi x2, 0 ;1 out USBOUT, x1 ;1 <-- out rjmp didStuff0 ;2 branch back 2 cycles earlierbitstuff1: ;1 (for branch taken) eor x1, x4 ;1 ldi x2, 0 ;1 sec ;1 set carry so that brsh will not jump out USBOUT, x1 ;1 <-- out rjmp didStuff1 ;2 jump back 1 cycle earlerbitstuff2: ;1 (for branch taken) eor x1, x4 ;1 ldi x2, 0 ;1 rjmp didStuff2 ;2 jump back 3 cycles earlier and do outbitstuff3: ;1 (for branch taken) eor x1, x4 ;1 ldi x2, 0 ;1 rjmp didStuff3 ;2 jump back earliertxLoop: sbrs shift, 0 ;1 eor x1, x4 ;1 out USBOUT, x1 ;1 <-- out ror shift ;1 ror x2 ;1didStuff0: cpi x2, 0xfc ;1 brsh bitstuff0 ;1 sbrs shift, 0 ;1 eor x1, x4 ;1 ror shift ;1 out USBOUT, x1 ;1 <-- out ror x2 ;1 cpi x2, 0xfc ;1didStuff1: brsh bitstuff1 ;1 sbrs shift, 0 ;1 eor x1, x4 ;1 ror shift ;1 ror x2 ;1didStuff2: out USBOUT, x1 ;1 <-- out cpi x2, 0xfc ;1 brsh bitstuff2 ;1 sbrs shift, 0 ;1 eor x1, x4 ;1 ror shift ;1 ror x2 ;1didStuff3: cpi x2, 0xfc ;1 out USBOUT, x1 ;1 <-- out brsh bitstuff3 ;1 nop2 ;2 ld x3, y+ ;2 sbrs shift, 0 ;1 eor x1, x4 ;1 out USBOUT, x1 ;1 <-- out ror shift ;1 ror x2 ;1didStuff4: cpi x2, 0xfc ;1 brsh bitstuff4 ;1 sbrs shift, 0 ;1 eor x1, x4 ;1 ror shift ;1 out USBOUT, x1 ;1 <-- out ror x2 ;1 cpi x2, 0xfc ;1didStuff5: brsh bitstuff5 ;1 sbrs shift, 0 ;1 eor x1, x4 ;1 ror shift ;1 ror x2 ;1didStuff6: out USBOUT, x1 ;1 <-- out cpi x2, 0xfc ;1 brsh bitstuff6 ;1 sbrs shift, 0 ;1 eor x1, x4 ;1 ror shift ;1 ror x2 ;1didStuff7: cpi x2, 0xfc ;1 out USBOUT, x1 ;1 <-- out brsh bitstuff7 ;1 mov shift, x3 ;1 dec cnt ;1 brne txLoop ;2 | 1 cbr x1, USBMASK ;1 prepare SE0 [spec says EOP may be 15 to 18 cycles] pop x4 ;2 out USBOUT, x1 ;1 <-- out SE0 -- from now 2 bits = 16 cycles until bus idle ldi cnt, 2 ;| takes cnt * 3 cyclesse0Delay: ;| dec cnt ;| brne se0Delay ;| -> 2 * 3 = 6 cycles;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm:;set address only after data packet was sent, not after handshake lds x2, usbNewDeviceAddr;2 subi YL, 20 + 2 ;1 sbci YH, 0 ;1 breq skipAddrAssign ;2 sts usbDeviceAddr, x2 ;0 if not skipped: SE0 is one cycle longerskipAddrAssign:;end of usbDeviceAddress transfer ori x1, USBIDLE ;1 in x2, USBDDR ;1 cbr x2, USBMASK ;1 set both pins to input out USBOUT, x1 ;1 <-- out J (idle) -- end of SE0 (EOP signal) cbr x1, USBMASK ;1 configure no pullup on both pins pop x3 ;2 pop YL ;2 out USBDDR, x2 ;1 <-- release bus now out USBOUT, x1 ;1 set pullup state pop YH ;2 rjmp txDoReturn ;2 [we want to jump to rxDoReturn, but this saves cycles]bitstuff4: ;1 (for branch taken) eor x1, x4 ;1 ldi x2, 0 ;1 out USBOUT, x1 ;1 <-- out rjmp didStuff4 ;2 jump back 2 cycles earlierbitstuff5: ;1 (for branch taken) eor x1, x4 ;1 ldi x2, 0 ;1 sec ;1 set carry so that brsh is not taken out USBOUT, x1 ;1 <-- out rjmp didStuff5 ;2 jump back 1 cycle earlierbitstuff6: ;1 (for branch taken) eor x1, x4 ;1 ldi x2, 0 ;1 rjmp didStuff6 ;2 jump back 3 cycles earlier and do out therebitstuff7: ;1 (for branch taken) eor x1, x4 ;1 ldi x2, 0 ;1 rjmp didStuff7 ;2 jump back 4 cycles earlier; ######################## utility functions #########################ifdef __IAR_SYSTEMS_ASM__/* Register assignments for usbCrc16 on IAR cc *//* Calling conventions on IAR: * First parameter passed in r16/r17, second in r18/r19 and so on. * Callee must preserve r4-r15, r24-r29 (r28/r29 is frame pointer) * Result is passed in r16/r17 * In case of the "tiny" memory model, pointers are only 8 bit with no * padding. We therefore pass argument 1 as "16 bit unsigned". */RTMODEL "__rt_version", "3"/* The line above will generate an error if cc calling conventions change. * The value "3" above is valid for IAR 4.10B/W32 */# define argLen r18 /* argument 2 */# define argPtrL r16 /* argument 1 */# define argPtrH r17 /* argument 1 */# define resCrcL r16 /* result */# define resCrcH r17 /* result */# define ptrL ZL# define ptrH ZH# define ptr Z# define byte r22# define bitCnt r19# define polyL r20# define polyH r21# define scratch r23#else /* __IAR_SYSTEMS_ASM__ */ /* Register assignments for usbCrc16 on gcc *//* Calling conventions on gcc: * First parameter passed in r24/r25, second in r22/23 and so on. * Callee must preserve r1-r17, r28/r29 * Result is passed in r24/r25 */# define argLen r22 /* argument 2 */# define argPtrL r24 /* argument 1 */# define argPtrH r25 /* argument 1 */# define resCrcL r24 /* result */# define resCrcH r25 /* result */# define ptrL XL# define ptrH XH# define ptr x# define byte r18# define bitCnt r19# define polyL r20# define polyH r21# define scratch r23#endif; extern unsigned usbCrc16(unsigned char *data, unsigned char len);; data: r24/25; len: r22; temp variables:; r18: data byte; r19: bit counter; r20/21: polynomial; r23: scratch; r24/25: crc-sum; r26/27=X: ptrusbCrc16: mov ptrL, argPtrL mov ptrH, argPtrH ldi resCrcL, 0xff ldi resCrcH, 0xff ldi polyL, lo8(0xa001) ldi polyH, hi8(0xa001)crcByteLoop: subi argLen, 1 brcs crcReady ld byte, ptr+ ldi bitCnt, 8crcBitLoop: mov scratch, byte eor scratch, resCrcL lsr resCrcH ror resCrcL lsr byte sbrs scratch, 0 rjmp crcNoXor eor resCrcL, polyL eor resCrcH, polyHcrcNoXor: dec bitCnt brne crcBitLoop rjmp crcByteLoopcrcReady: com resCrcL com resCrcH ret; extern unsigned usbCrc16Append(unsigned char *data, unsigned char len);usbCrc16Append: rcall usbCrc16 st ptr+, resCrcL st ptr+, resCrcH ret
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -