From 64bd3426117b4af7dfb99bf569c28690fd85d968 Mon Sep 17 00:00:00 2001 From: Peter Maivald Date: Mon, 30 Oct 2017 16:49:27 +0100 Subject: [PATCH 01/18] #define DW_BIT for debugging --- usbtiny/main.c | 40 ++++++++++++++++++++-------------------- usbtiny/usbconfig.h | 7 +++++-- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/usbtiny/main.c b/usbtiny/main.c index 527ff2f..45fc157 100644 --- a/usbtiny/main.c +++ b/usbtiny/main.c @@ -1296,8 +1296,8 @@ void dwCaptureWidths() { " clr r24 ; Counter for number of measured pulses \n" " \n" " cli ; Disable interrupts while measuring pulse widths \n" - " sbi 0x18,5 ; End break \n" - " cbi 0x17,5 ; Set DDRB 5 (reset/dwire) direction input \n" + " sbi 0x18,%[bit] ; End break \n" + " cbi 0x17,%[bit] ; Set DDRB 5 (reset/dwire) direction input \n" " \n" "; Cycle loop - track a high pulse followed by a low pulse \n" " \n" @@ -1309,7 +1309,7 @@ void dwCaptureWidths() { "dwc4: adiw r30,1 ; 2. \n" " brcs dwc8 ; 1/2. If no change in 65536 loops (~24ms) \n" " \n" - " sbic 0x16,5 ; 1/2. Skip if Pin PB5 clear (space, zero) \n" + " sbic 0x16,%[bit] ; 1/2. Skip if Pin PB5 clear (space, zero) \n" " rjmp dwc4 ; 2. \n" " \n" " cpi r24,64 ; 1. Limit measurement to space available in dwBuf. \n" @@ -1334,7 +1334,7 @@ void dwCaptureWidths() { "dwc6: adiw r30,1 ; 2. \n" " brcs dwc8 ; 1/2. If no change in 65536 loops (~24ms) \n" " \n" - " sbis 0x16,5 ; 1/2. Skip if Pin PB5 set (mark, one) \n" + " sbis 0x16,%[bit] ; 1/2. Skip if Pin PB5 set (mark, one) \n" " rjmp dwc6 ; 2. \n" " \n" " st x+,r30 ; 2. Record low pulse time \n" @@ -1349,7 +1349,7 @@ void dwCaptureWidths() { "dwc8: sei ; Re-enable interrupts \n" " add r24,r24 ; Convert word count to byte count \n" " sts dwLen,r24 \n" - :::"r24","r26","r27","r30","r31"); + ::[bit]"I"(DW_BIT):"r24","r26","r27","r30","r31"); } // Sample pulse widths returned by attiny85 with internal clock as supplied @@ -1386,8 +1386,8 @@ void dwSendBytes() { " \n" "; Start with dwire pin as output in idle state \n" " \n" - " sbi 0x18,5 ; Make sure line is idle (high) \n" - " sbi 0x17,5 ; DDRB pin5 is output \n" + " sbi 0x18,%[bit] ; Make sure line is idle (high) \n" + " sbi 0x17,%[bit] ; DDRB pin5 is output \n" " \n" "; Preload registers \n" " \n" @@ -1409,7 +1409,7 @@ void dwSendBytes() { "; Send start bit (space / 0) \n" " \n" "dws2: movw r30,r24 ; Load wait count to r31:r30 \n" - " cbi 0x18,5 ; 1. Set dwire port low \n" + " cbi 0x18,%[bit] ; 1. Set dwire port low \n" " \n" " ld r22,x+ ; 2. Next byte to send \n" " ldi r21,8 ; 1. Bit count \n" @@ -1422,9 +1422,9 @@ void dwSendBytes() { "; Each bit takes a total of 4*dwBitTime + 8 cycles. \n" " \n" "dws6: sbrc r22,0 ; 1/2. Skip if sending zero \n" - " sbi 0x18,5 ; 1. Set dwire port high \n" + " sbi 0x18,%[bit] ; 1. Set dwire port high \n" " sbrs r22,0 ; 1/2. Skip if sending one \n" - " cbi 0x18,5 ; 1. Set dwire port low \n" + " cbi 0x18,%[bit] ; 1. Set dwire port low \n" " movw r30,r24 ; 1. Load wait count to r31:r30 \n" " \n" "; 5 cycles from dws6 to here. \n" @@ -1441,7 +1441,7 @@ void dwSendBytes() { "; Send stop bit (mark / 1) \n" " \n" " movw r30,r24 ; 1. Load wait count to r31:r30 \n" - " sbi 0x18,5 ; 1. Set dwire port high \n" + " sbi 0x18,%[bit] ; 1. Set dwire port high \n" " adiw r30,1 ; 2. Extra iteration \n" " \n" "dws10: sbiw r30,1 ; 2. Decrement wait count \n" @@ -1450,9 +1450,9 @@ void dwSendBytes() { " dec r23 \n" " brne dws2 ; While more bytes to transmit \n" " \n" - "dws12: cbi 0x17,5 ; DDRB pin5 is input \n" + "dws12: cbi 0x17,%[bit] ; DDRB pin5 is input \n" " \n" - :::"r21","r22","r23","r24","r25","r26","r27","r30","r31"); + ::[bit]"I"(DW_BIT):"r21","r22","r23","r24","r25","r26","r27","r30","r31"); } @@ -1490,7 +1490,7 @@ void dwReadBytes() { "dwr4: sbiw r30,1 ; 2. Check for timeout \n" " breq dwr14 ; 1/2. If no start bit encountered \n" " \n" - " sbic 0x16,5 ; 1/2. Skip if Pin PB5 clear \n" + " sbic 0x16,%[bit] ; 1/2. Skip if Pin PB5 clear \n" " rjmp dwr4 ; 2. While no start bit \n" " \n" "; Wait through half of start bit \n" @@ -1506,7 +1506,7 @@ void dwReadBytes() { "; If line not still low, assume it was a glitch and go \n" "; back to waiting for a start bit. \n" " \n" - " sbic 0x16,5 ; 1/2. Skip if Pin PB5 clear \n" + " sbic 0x16,%[bit] ; 1/2. Skip if Pin PB5 clear \n" " rjmp dwr2 ; 2. If not a real start bit \n" " \n" "; Loop for 8 bits sampling the middle of each pulse. \n" @@ -1526,7 +1526,7 @@ void dwReadBytes() { "; Sample one bit to top of r22 \n" " \n" " lsr r22 ; 1. \n" - " sbic 0x16,5 ; 1/2. \n" + " sbic 0x16,%[bit] ; 1/2. \n" " sbr r22,128 ; 1. Sets top bit of r22 \n" " rjmp . ; 2. 2 cycle noop \n" " dec r21 ; 1. \n" @@ -1545,7 +1545,7 @@ void dwReadBytes() { "dwr12: sbiw r30,1 ; 2. Check for timeout \n" " breq dwr14 ; 1/2. If line stuck low \n" " \n" - " sbis 0x16,5 ; 1/2. Skip if Pin PB5 set \n" + " sbis 0x16,%[bit] ; 1/2. Skip if Pin PB5 set \n" " rjmp dwr12 ; 2. While not stop bit \n" " \n" "; Check for buffer full and loop back to read next byte \n" @@ -1558,7 +1558,7 @@ void dwReadBytes() { "dwr14: sei ; Re-enable interrupts \n" " sts dwLen,r23 \n" " \n" - :::"r21","r22","r23","r24","r25","r26","r27","r30","r31"); + ::[bit]"I"(DW_BIT):"r21","r22","r23","r24","r25","r26","r27","r30","r31"); } @@ -1919,10 +1919,10 @@ int main(void) { if (dwState & 0x34) {_delay_ms(2);} // Allow USB transfer to complete before // any action that may disable interrupts - if (dwState & 0x01) {cbi(PORTB, 5); sbi(DDRB, 5); _delay_ms(100);} + if (dwState & 0x01) {cbi(PORTB, DW_BIT); sbi(DDRB, DW_BIT); _delay_ms(100);} if (dwState & 0x02) {((char*)&dwBitTime)[0] = dwBuf[0]; ((char*)&dwBitTime)[1] = dwBuf[1];} if (dwState & 0x04) {dwSendBytes();} - if (dwState & 0x08) {dwBuf[0]=0; dwLen=1; cbi(DDRB,5); sbi(PCMSK,5); sei();} // Capture dWIRE pin change + if (dwState & 0x08) {dwBuf[0]=0; dwLen=1; cbi(DDRB,DW_BIT); sbi(PCMSK,DW_BIT); sei();} // Capture dWIRE pin change if (dwState & 0x10) {dwReadBytes();} if (dwState & 0x20) {dwCaptureWidths();} diff --git a/usbtiny/usbconfig.h b/usbtiny/usbconfig.h index 15e128e..32d148a 100644 --- a/usbtiny/usbconfig.h +++ b/usbtiny/usbconfig.h @@ -324,15 +324,18 @@ extern void usbEventResetReady(void); further interrupts from the debugWIRE pin changing. */ +// debugWIRE bit normally 5, but can be changed for debugging +#define DW_BIT 2 + #ifdef __ASSEMBLER__ macro nonUsbPinChange .global dwBuf - sbic PINB,5 + sbic PINB,DW_BIT rjmp dWirePinIdle ldi YL,1 sts dwBuf,YL - cbi PCMSK,5 ; Disable further interrupts on dwire pin change + cbi PCMSK,DW_BIT ; Disable further interrupts on dwire pin change dWirePinIdle: endm From b171e5b0702be32f17b2d92b4b231743f25d2e72 Mon Sep 17 00:00:00 2001 From: Peter Maivald Date: Tue, 31 Oct 2017 08:13:09 +0100 Subject: [PATCH 02/18] make usb interrupt pre-emptable --- usbtiny/main.c | 48 ++- usbtiny/main.hex | 715 ++++++++++++++++++++++---------------------- usbtiny/usbconfig.h | 25 +- 3 files changed, 410 insertions(+), 378 deletions(-) diff --git a/usbtiny/main.c b/usbtiny/main.c index 45fc157..208dfea 100644 --- a/usbtiny/main.c +++ b/usbtiny/main.c @@ -173,6 +173,7 @@ static uint8_t ws2812_mask; static uint8_t ws2812_ptr=0; // ---------------------------------------------------------------------- // debugWIRE support +#define DW_BIT 2 // debugWIRE bit normally 5, but can be changed for debugging volatile uint8_t dwBuf[128]; volatile uint8_t dwLen; // Length being received from host or avr device volatile uint8_t dwIn; // Input pointer: where usbDwFunctionWrite writes into dwBuf @@ -1561,7 +1562,52 @@ void dwReadBytes() { ::[bit]"I"(DW_BIT):"r21","r22","r23","r24","r25","r26","r27","r30","r31"); } +/* +We hook the pin change interrupt to give priority to dw source interrupts. When +dw interrupts are enabled, the usb interrupt vector is run with the dw interrupt +enabled. This makes it so that we can respond to dw interrupts even in the middle +of a usb frame causing that frame to be missed. Also, interrupts are disabled +while servicing dw activities, leading to missed frames in those cases as well. + +Interrupts are disabled while executing all dw commands. But, only the wait for +start bit command allows the usb interrupt to be pre-empted. +*/ +__attribute__((naked, used)) void PCINT0_vect(void) { + asm("\n" +" sbis %[pcmsk],%[dwbit] ; not for us if dw interrupt disabled \n" +" rjmp DW_INTR_VECTOR ; leave interrupts disabled \n" +" push r16 \n" +" in r16,__SREG__ \n" +" push r16 \n" +" sbic %[pinb],%[dwbit] ; not for us if dw line high \n" +" rjmp dw_pass%= \n" +" \n" +" ldi r16,1 \n" +" sts dwBuf,r16 ; set break detected flag \n" +" cbi %[pcmsk],%[dwbit] ; Disable further dw interrupts \n" +" \n" +"dw_pass%=: \n" +" sbis %[pcmsk],%[usbbit] ; call usb interrupt when set \n" +" rjmp dw_done%= ; don't re-enter \n" +" cbi %[pcmsk],%[usbbit] ; clear to indicate in usb interrupt \n" +" ldi r16,0x80 ; enable interrupts to detect start bit and use\n" +" out __SREG__,r16 ; this value for timeout hack in usb interrupt \n" +" rcall DW_INTR_VECTOR ; call usb interrupt with interrupts enabled \n" +" cli ; disable to prevent re-entry from here \n" +" sbi %[pcmsk],%[usbbit] ; set to indicate not in usb interrupt \n" +"dw_done%=: \n" +" pop r16 \n" +" out __SREG__,r16 \n" +" pop r16 \n" +" reti \n" + : + : [pcmsk] "I" (_SFR_IO_ADDR(PCMSK)) + ,[pinb] "I" (_SFR_IO_ADDR(PINB)) + ,[dwbit] "I" (DW_BIT) + ,[usbbit] "I" (USB_INTR_CFG_BIT) + :"r16"); +} /* ------------------------------------------------------------------------- */ @@ -1922,7 +1968,7 @@ int main(void) { if (dwState & 0x01) {cbi(PORTB, DW_BIT); sbi(DDRB, DW_BIT); _delay_ms(100);} if (dwState & 0x02) {((char*)&dwBitTime)[0] = dwBuf[0]; ((char*)&dwBitTime)[1] = dwBuf[1];} if (dwState & 0x04) {dwSendBytes();} - if (dwState & 0x08) {dwBuf[0]=0; dwLen=1; cbi(DDRB,DW_BIT); sbi(PCMSK,DW_BIT); sei();} // Capture dWIRE pin change + if (dwState & 0x08) {dwBuf[0]=(dwState & ~0x0F); dwLen=1; cbi(DDRB,DW_BIT); sbi(PCMSK,DW_BIT); sei();} // Capture dWIRE pin change if (dwState & 0x10) {dwReadBytes();} if (dwState & 0x20) {dwCaptureWidths();} diff --git a/usbtiny/main.hex b/usbtiny/main.hex index 64e1f11..61e348c 100644 --- a/usbtiny/main.hex +++ b/usbtiny/main.hex @@ -1,368 +1,371 @@ -:1000000046C060C08CC05EC05DC05CC05BC05AC0F2 -:1000100059C058C057C056C055C054C053C001CB7A -:1000200032C97EC99CC9C1C9ECC9FFC916CA55CA23 -:1000300056CA5ECA75CAF3CAF2CAF1CAF0CAEFCA92 -:10004000A5CAEDCAECCAB2CA0902190001010080B2 +:1000000046C060C0AEC85EC05DC05CC05BC05AC0C8 +:1000100059C058C057C056C055C054C053C015CB66 +:1000200043C98FC9ADC9D2C9FDC910CA27CA66CA9A +:1000300067CA6FCA86CA07CB06CB05CB04CB03CBF6 +:10004000B6CA01CB00CBC3CA090219000101008066 :10005000640904000001FF00000007058103080097 :1000600064120110010000000881179F0C040100B8 :100070000203011603550053004200740069006E2C :1000800000790053005000490004030904001124C2 :100090001FBECFE5D2E0DEBFCDBF10E0A0E6B0E0EE -:1000A000E0EEF6E102C005900D92A037B107D9F756 +:1000A000E8E0F7E102C005900D92A037B107D9F75B :1000B00022E0A0E7B0E001C01D92AD31B207E1F748 -:1000C00056D80CCB9DCFA82FB92F80E090E041E00F +:1000C00067D820CB9DCFA82FB92F80E090E041E0EA :1000D00050EA609530E009C02D9182279795879569 :1000E00010F084279527305EC8F36F5FA8F308955A :1000F000EADF8D939D930895A6E088279927AA9516 :1001000069F00197E1F3B399FCCFB39BFECF81E097 :100110009927A6B3019611F0A871D9F70895CF9346 -:10012000CFB7CF93C395B39BE9F7B39B11C0B39BF4 -:100130000FC0B39B0DC0B39B0BC0B39B09C0B39B57 -:1001400007C0B59904C0C1E0C0939401AD98D5C073 -:100150000F92DF93C0916A01DD27CF58DE4F012E49 -:10016000B39B03C0DF910F90E0CF2F930F931F93AA -:100170004F932FEF4F6F06B303FB20F95F933F932D -:1001800050E03BE065C016B30126502953FDC895E9 -:1001900056B3012703FB25F92F7306B3B1F050279F -:1001A000102713FB26F906B22230F0F000C016B378 -:1001B000012703FB27F90126502906B22430E8F570 -:1001C0004F77206816B30000F6CF50274F7D20628E -:1001D00006B2102F000000C006B3002650291027D9 -:1001E00013FB26F906B2E2CF4F7B06B3206400C0B2 -:1001F000DACF01265029187106B269F14E7F2160CD -:10020000012F16B328C0002650294D7F06B2226068 -:10021000102F29C0012650294B7F06B22460012FE0 -:100220002DC016B301265029477F2860000006B272 -:100230002EC04F7E06B3206130C0422706B34993DB -:1002400000265029102706B24FEF13FB20F9297F13 -:1002500016B379F2187159F10126502906B2012717 -:1002600003FB21F9237F06B371F200265029315098 -:10027000D0F006B2102713FB22F9277E16B351F2F5 -:1002800001265029012703FB06B223F92F7C49F2EE -:10029000000006B3102713FB24F90026502906B2EC -:1002A0002F7939F270CF10E21ABF002719C03B50E6 -:1002B0003195C31BD04010E21ABF0881033C09F1FD -:1002C0000B34F9F0209168011981110F1213EDCF51 -:1002D0004A81441F093651F10D3211F0013E29F7D0 -:1002E00000936F013F915F914F911F910F912F915B -:1002F000DF910F90CAB7C5FD15CFCF91CFBFCF917A -:10030000189520916F01222369F310916D0111233B -:1003100079F534307AF130936D01209369011091B1 -:100320006A013BE0311B30936A0124C000916D01EA -:100330000130F4F40AE54F7049F43091610034FD66 -:100340001AC000936100C1E5D1E019C030915C0191 -:1003500034FD11C000935C01CDE5D1E010C005274C -:1003600010E000C021C0052710E0C89508BB14C0EC -:100370003AE501C032ED032EC0E0D0E032E017B321 -:100380001861C39A08B317BB58E120E84FEF20FF6C -:10039000052708BB279517951C3F28F700004552F5 -:1003A000B0F720FF0527279508BB17951C3FB8F627 -:1003B00029913A9561F7077E10916E01110F08BBE4 -:1003C000C250D04011F01093680110E21ABF0860CB -:1003D00017B3177E402F477E54E05A95F1F708BBBC -:1003E00017BB48BB7FCFCF93DF9360916D01635004 -:1003F00067FDB7C080916A01CCE0D0E0C81BD1098D -:10040000CF58DE4F809169018D3209F099C0683074 -:1004100009F0A5C083EC809351018AE580936100C7 -:10042000109271008881807639F0CE013AD2982FEF -:100430008F3F09F474C07AC09A8110925A01898161 -:10044000811106C010925B012AE531E092E062C0A2 -:10045000853019F490936E015AC0863009F041C07E -:100460008B81813049F481E690E090936C01809318 -:100470006B0180E492E132C0823049F488E490E07C -:1004800090936C0180936B0180E499E127C08330E5 -:1004900019F5911109C089E890E090936C0180935F -:1004A0006B0180E494E01AC09130B1F0923049F4CD -:1004B00083E790E090936C0180936B0180E496E178 -:1004C0000DC0933049F485E690E090936C018093E1 -:1004D0006B0180E098E002C080E490E080937100BE -:1004E00025C0883069F0893019F4909370010FC0ED -:1004F0008A3049F08B3059F48BE480935D0107C05A -:1005000020E731E002C02AE531E091E003C02AE5AE -:1005100031E090E030936C0120936B0107C088813B -:1005200087FD9E8180E88093710007C08F818111D3 -:1005300004C08E81891708F4982F909360000FC033 -:100540008091710087FF0BC0CE0112D18F3F21F443 -:100550008EE18093610003C08111109260001092BF -:100560006D018091610084FF56C0809160008F3FD3 -:1005700009F451C0C82F893008F0C8E08C1B809363 -:1005800060009091510188E8892780935101CC2324 -:1005900089F12091710027FF08C06C2F82E591E05E -:1005A000C8D0C82F893088F525C080916B01909103 -:1005B0006C0126FF0BC0A2E5B1E0FC012C2F280F37 -:1005C00034913D9331962E13FBCF09C0DC01E2E557 -:1005D000F1E02C2F2E0F3D9131932E13FCCF2FEFF6 -:1005E0002C0F30E02F5F3F4F820F931F90936C01D1 -:1005F00080936B016C2F82E591E07ADDCC5FCC308B -:1006000041F08FEF8093600004C08FEF8093600013 -:10061000CEE1C093610084E196B3987131F48150CA -:10062000D9F710926E0110926801C1E08111C0E00B -:10063000809170008C1729F0C11101C081D3C09343 -:100640007000DF91CF910895AC9A8BB780628BBF19 -:10065000ECE5F1E08BE481838AE580830895FC0179 -:10066000DB01AC014C5F5F4F619128E030E080E836 -:1006700090E0782F762309F0C09A70916400072EDD -:1006800000C000C000000A94D9F7C29A70916400BB -:10069000072E00C000C000000A94D9F7990FB19945 -:1006A0009F5FC098C29886952150310911F79D939C -:1006B000E417F507C9F6089580914D0190914E0118 -:1006C0009C012F5F3F4F30934E0120934D012091AD -:1006D0004A0127FF02C0880F991F80FD28602093E0 -:1006E0004601292F2695209347019695879580935B -:1006F000480162E471E086E491E0B1CF08C024E0F3 -:100700002A95F1F7019730F024E02A95F1F700C01F -:100710000197A8F70895DC0100C000C000C000C028 -:1007200000C000C087EE93E0E9DF00001197A0F75A -:100730000895EF92FF921F93CF93DF931F92CDB74F -:10074000DEB7182F7C018E2D811B861748F4698334 -:10075000B3DF80914501F70181937F016981F3CF78 -:10076000862F0F90DF91CF911F91FF90EF9008950A -:10077000DF92EF92FF920F931F93CF93DF93C62FD9 -:1007800020918A01211105C0D82F8C0130E6D32E8B -:100790004EC020911C024091890170E0620F711DD2 -:1007A00050E021E06417750714F420E008C0C09100 -:1007B000890130911C02C31B34E130933401382F7E -:1007C000DC018A2F831B8C1760F4E0911C0281E00E -:1007D0008E0F80931C02F0E08D91EC56FE4F8083CB -:1007E000F0CF822F37C08091640090E025E0880F21 -:1007F000991F2A95E1F7E80EF91E80914B0190911F -:100800004C01E816F90698F462E471E086E491E0A0 -:1008100026DF80914501909149018913E4CF9091A1 -:100820005001891701F390914F018917E1F2802F50 -:100830008D1B8C1770F4F80181918F018093490111 -:100840003BDF809146018D2580934601E12CF12C00 -:10085000D4CF81E0DF91CF911F910F91FF90EF9066 -:10086000DF90089528B3342F3095232338B3432BDA -:10087000680F791F8617970799F0FC013191CF0116 -:1008800058E048BB330F5A9500C008F028BB00C0A1 -:10089000000028BB21F000C000C000C0F2CFEACFAA -:1008A0000895CF93DF93EC012981211108C081E2E3 -:1008B0008983D0936C01C0936B0198E03DC22130D5 -:1008C00011F486B30BC16A81223011F468BB33C2C4 -:1008D000962F362F377041E050E0032E02C0440FB0 -:1008E000551F0A94E2F7233009F496C0243021F40E -:1008F00088B3482B48BB1FC2253051F46093640075 -:10090000B99887B3856287BB88B3887D88BB13C2DB -:10091000263049F4B898C098B998C198BA98C29846 -:10092000BD98C59808C2273051F4BE01CE01029689 -:1009300096DED0936C01C0936B0194E0FDC128302A -:1009400031F4609350018B8180934F01F4C1EC81AD -:10095000FD81F0934E01E0934D01293011F480E2C6 -:1009600003C02B3021F480EA80934A01A4C1EA81BC -:10097000FB81F0934C01E0934B012A3011F480E4A9 -:10098000F3CF2C3011F480ECEFCF2D3021F487B36E -:100990004095482304C02E3021F487B3482B47BB31 -:1009A000CAC12F3009F5611105C08091350187B9A1 -:1009B000A59A0CC0613031F480913501816087B90E -:1009C000A29A04C0623011F48FE887B9369A36993A -:1009D000FECF84B1888385B18983D0936C01C093A5 -:1009E0006B0114BA92E0A8C1203159F4B89AB99AAF -:1009F0008AB5806A8ABD8AB583608ABD83B78560FF -:100A000044C0213121F469BD8C8188BD94C122315B -:100A100009F46ECF233121F488B3409548236ACF7F -:100A2000243159F486B390E04823592302C05595E8 -:100A300047953A95E2F7488352C02631A1F56111F6 -:100A40000AC083B78B7F83BF83B78D7F83BF83B794 -:100A5000816083BF71C1613031F483B78B7F83BF05 -:100A600083B782600FC0623031F483B78B7F83BF5E -:100A700083B7826014C0633051F483B7846083BF4E -:100A800083B78D7F83BF83B78E7F83BF54C16430AC -:100A900009F051C183B7846083BF83B78D7F83BF63 -:100AA00083B78160F2CF2F3159F48B8190E0982F7A -:100AB0008827860F911D90936300809362003BC14D -:100AC000203219F41ABC13BE36C1213221F46093CE -:100AD000140281E07FC0223241F483E18883D09305 -:100AE0006C01C0936B0191E027C12332C9F4379A9E -:100AF00096B18A81892B86B99B81913059F038F063 -:100B0000923009F018C180913501806906C01092B9 -:100B1000350112C1809135018068809335010BC188 -:100B2000283249F48BE891E090936C0180936B013B -:100B30009091930101C1293211F482E04BC02A3215 -:100B400011F483E00BC02B3211F484E043C02C324B -:100B500011F488E03FC02D3231F489E080933401F4 -:100B600060931402E8C02E3261F48BE0809334016C -:100B7000609314028B81809315028C8180931602FE -:100B8000DAC02F3259F4613031F4B89AB99ABA9A6E -:100B900060933C01D0C010923C01CDC0203349F499 -:100BA000609338018B81809337018C81809336016B -:100BB000C2C0213331F470E0709341016093400171 -:100BC000BAC0223311F485E005C0233331F46093B9 -:100BD000140286E080933401AEC0263371F565FFC0 -:100BE0001BC0E0917200E03CB8F48B81AE2FB0E006 -:100BF000AC58BF4F8C938C81A1E0AE0FB0E0AC58E5 -:100C0000BF4F8C9383E08E0F809372008D81EE5FD7 -:100C1000F0E0EC58FF4F80838A8184FF8CC0809184 -:100C20007200882309F487C081E18093340187B37F -:100C3000842B87BB409373007EC02733A1F480913F -:100C40006D0090916E003CD580916D0090916E008A -:100C50006B81019635D580916D0090916E006C810D -:100C600002962ED568C02C3341F580918A018111FE -:100C700062C0888187FF09C084E991E090936C018C -:100C800080936B019091890157C060938A018E8196 -:100C90008093890190918901911104C084E180932E -:100CA00034014AC080918901813818F080E880932E -:100CB000890110921C029FEF3FC0822F807F803EEF -:100CC000E1F48AE080933401822F877080931402CC -:100CD00028702093150220E080911402281758F5FF -:100CE000822F90E00296FE01E80FF91F3081FC018F -:100CF000EC5EFD4F30832F5FEFCF803FE1F487E064 -:100D000080933401822F887080931402277020937F -:100D1000150220E080911502281768F4822F90E0D8 -:100D20000296FE01E80FF91F3081FC01EC5EFD4FD9 -:100D300030832F5FEFCF90E0892FDF91CF9108951F -:100D4000DF92EF92FF920F931F93CF93DF9308E010 -:100D500010E0C0E0D0E8DD2EDC0ED1BECDD97C01A4 -:100D600084E3E81689E0F8060CF4CD2DD695015001 -:100D7000110989F78FEF8C0F81BF0C2F10E00F5FE7 -:100D80001F4F21B730E0021713078CF0B5D98453F9 -:100D9000994097FF03C09195819591098E159F0504 -:100DA00014F4C1B77C0181B78F5F81BFEACFC1BFA7 -:100DB00061B780E090E0DF91CF911F910F91FF909C -:100DC000EF90DF907DC4CF93DF93D82F882311F06D -:100DD000B89801C0B89AC0E02C2F30E08091400153 -:100DE000909141012817390728F481E090E086DCD2 -:100DF000CF5FF2CFBA98C0E02C2F30E08091400155 -:100E0000909141012817390728F481E090E076DCC1 -:100E1000CF5FF2CFBA9AC0E02C2F30E08091400132 -:100E2000909141012817390728F481E090E066DCB1 -:100E3000CF5FF2CFD111B89AC0E08C2F90E0209113 -:100E40004001309141018217930728F481E090E03E -:100E500055DCCF5FF2CFDF91CF9108950F931F93B1 -:100E6000CF93DF93B898C0E08C2F90E020914001A1 -:100E7000309141018217930728F481E090E03EDC35 -:100E8000CF5FF2CFBA98C0E00091400110914101CC -:100E90008C2F90E08017910728F481E090E02EDC01 -:100EA000CF5FF2CFD6B3C0E08C2F90E080179107D0 -:100EB00028F481E090E022DCCF5FF6CFBA9AC0E060 -:100EC0002C2F30E080914001909141012817390783 -:100ED00028F481E090E012DCCF5FF2CF8D2F81709B -:100EE000DF91CF911F910F910895CF9388B38A7F9F -:100EF00088BBBA98B898C0E08C2F90E02091400150 -:100F0000309141018217930728F481E090E0F6DBED -:100F1000CF5FF2CFCF910895CF9387B38A7F87BBFE -:100F2000C0E08C2F90E02091400130914101821768 -:100F3000930728F481E090E0E1DBCF5FF2CFB89A2D -:100F4000C0E08C2F90E02091400130914101821748 -:100F5000930728F481E090E0D1DBCF5FF2CFBA9A1B -:100F6000C0E08C2F90E02091400130914101821728 -:100F7000930728F481E090E0C1DBCF5FF2CFCF91FF -:100F80000895CF93B89AC0E08C2F90E02091400153 -:100F9000309141018217930728F481E090E0AEDBA5 -:100FA000CF5FF2CFBA9AC0E08C2F90E02091400141 -:100FB000309141018217930728F481E090E09EDB95 -:100FC000CF5FF2CFBA98C0E08C2F90E02091400123 -:100FD000309141018217930728F481E090E08EDB85 -:100FE000CF5FF2CFB898C0E08C2F90E02091400105 -:100FF000309141018217930728F481E090E07EDB75 -:10100000CF5FF2CFCF910895CF93DF93D82FC8E071 -:101010008D2F8078D8DEDD0FC150D1F7DF91CF91D1 -:101020001DCFFF920F931F93CF93DF93F82ED8E03D -:10103000C0E0CC0F13DFC82BD150D9F7009140018D -:10104000109141018D2F90E08017910728F481E0E5 -:1010500090E054DBDF5FF2CFFF2011F080E001C0B1 -:1010600081E0B1DED0E08D2F90E08017910728F469 -:1010700081E090E043DBDF5FF6CF8C2FDF91CF91F3 -:101080001F910F91FF900895A4E9B1E08827F8948B -:10109000C59ABD98EE27FF27319688F0B599FCCF09 -:1010A000803468F4ED93FD938395EE27FF27319606 -:1010B00030F0B59BFCCFED93FD938395EBCF789407 -:1010C000880F809389010895C59ABD9A8091870100 -:1010D00090918801A4E9B1E0709189017723C1F072 -:1010E000F894FC01C5986D9158E03197F1F760FDD7 -:1010F000C59A60FFC598FC013197F1F766955A953E -:10110000B1F7FC01C59A31963197F1F77A9549F715 -:10111000BD98089577278091870190918801A4E96F -:10112000B1E0EE27FF273197F1F0B599FCCFFC0134 -:10113000F695E7953197F1F7B599F3CF662758E023 -:10114000FC013197F1F76695B599606800C05A9532 -:10115000B9F76D937395EE27FF27319721F0B59B73 -:10116000FCCF7038F0F2789470938901089580E292 -:1011700087BB88BB80E090E09BD28F3F09F081BFA6 -:10118000C0916D00D0916E00CE0192D2E82ECE01BA -:1011900001968ED2F82ECE0102968AD2982F80ED3B -:1011A0008E0D8A3008F072C280ED8F0D8A3008F003 -:1011B0006DC280ED890F8A3008F068C2C0916D0061 -:1011C000D0916E00CE0174D290E09093680080932D -:1011D0006700CE0101966CD290E090936A008093F4 -:1011E0006900CE01029664D290E090936C008093E7 -:1011F0006B00BB9AC4E18FE090E08DDAC150D9F763 -:10120000BB9885E090E0909341018093400110925B -:101210006300109262009EE088E10FB6F894A895F2 -:1012200081BD0FBE91BD10923B0110923A01109208 -:101230003901109238011092370110923601109244 -:101240003D011092340100DA7894B898C098B998AA -:10125000C198BA98C298BD98C598DAE130E4F32EE7 -:10126000C1E04BE1E42E20E8D22EA895BCD8E09155 -:1012700034018E2F90E08531910508F0D0C1FC013A -:10128000E15FFF4F0994B89AB998BA9AC29A10923E -:101290008B018091140280933F01D0923E01809196 -:1012A0003E01882309F458C0C098B12C8B2D90E0E2 -:1012B00020916200309163008217930728F481E047 -:1012C00090E01CDAB394F2CF90913E0180913F01FF -:1012D000892309F0C09AC29880918B01880F80936E -:1012E0008B0196B320918B0191FB882780F9820FA7 -:1012F00080938B01B12C8B2D90E020916200309176 -:1013000063008217930728F481E090E0F7D9B39443 -:10131000F2CFC29A80913E01869580933E01BFCF65 -:10132000BA9AC29880EE91E0E9D9BFB6F894C29A11 -:1013300086E490E0E3D9BA9886B382FB002700F9EF -:1013400010E0C80121E0822780938B01BFBE8AE9AB -:1013500091E0D4D9BA9AC29AC093930160C18091A6 -:10136000140280933E01BA9A98E0B92EAFB6F89471 -:10137000C29880913E0180FF07C086E090E0BED910 -:10138000C29A80E490E006C08CE390E0B7D9C29A9C -:101390008AE090E0B3D980913E01869580933E012A -:1013A000AFBEBA94B110E2CF3AC110928B0188E07F -:1013B000B82E80918B01869580938B01BA9AAFB637 -:1013C000F894C29886E090E099D9C29A8AE090E0B9 -:1013D00095D9BA9886B382FB882780F980933E011D -:1013E000AFBE87E390E08AD980913E01882329F03F -:1013F00080918B01806880938B01BA94B110D9CF12 -:10140000ABCFBA9ABFB6F894C29886E090E076D98E -:10141000C29A8AE090E072D9BA9886B382FB882794 -:1014200080F980938B01BFBE97CFBA9ABFB6F8946C -:10143000C2988091140280FF07C086E090E05ED9D8 -:10144000C29A80E490E006C08CE390E057D9C29A3B -:101450008AE090E053D9BFBEE2C0B99AB898BA9A70 -:10146000C298E0923F01DDB9809114028111C598C4 -:1014700010923E01E0913E0180911502E81730F58F -:10148000F0E0EA5EFD4F80818FB9FEB880913F01A8 -:101490008DB9B12C8B2D90E02091620030916300CA -:1014A0008217930728F481E090E028D9B394F2CF13 -:1014B000769BECCF80913E01E82FF0E09FB1E5579D -:1014C000FE4F90838F5F80933E01D4CF80911402B2 -:1014D0008111C59A80913E0156C007DDA0C080E011 -:1014E00090E01ADD809114028FDD80933E0115C0DB -:1014F000B12C80911402B81650F4EB2DF0E0EA5EA6 -:10150000FD4F808181DD80933E01B394F2CF8091C5 -:101510001502811136DD80913E0180938B011CCF35 -:1015200080911402811102C0B12C19C0912C292D77 -:1015300030E08091150290E00197A90145575E4F78 -:101540005A012817390734F481E06BDDF5018083F7 -:101550009394EDCF80E065DDF50180830EC080912E -:101560001502B81650F48B2C912C81E05ADDF40151 -:10157000E557FE4F8083B394F2CF8091160281111C -:1015800000DD809115028093930149C081E090E0D5 -:10159000C2D8BFB6F8946091720070E040917300B9 -:1015A00084E790E05FD91092720055CF80918A0154 -:1015B000847319F082E090E0AED880918A0180FFB8 -:1015C00005C0C598BD9A84E690E0A5D880918A01AF -:1015D00081FF08C0809194018093870180919501DB -:1015E0008093880180918A0182FD6EDD80918A015D -:1015F00083FF07C010929401C0938901BD98AD9AF2 -:10160000789480918A0184FD85DD80918A0185FD31 -:101610003BDD1092340110928A01789402C010923E -:10162000340180913C01882309F41FCE80913D0153 -:1016300081110DC08091380180933B018091370169 -:1016400080933A01809136018093390119C09091BD -:101650003B01981710F4C09801C0C09A90913D01C9 -:1016600080913A01891710F4C19801C0C19A9091F4 -:101670003D0180913901891710F4C29801C0C29AC6 -:1016800080913D018F5F80933D01EDCD65E3CE01FB -:1016900017D080916D0090916E0061E3019610D09B -:1016A00080916D0090916E0062E3029609D086CD24 -:1016B000E199FECF9FBB8EBBE09A99278DB3089529 -:1016C000262FE199FECF1CBA9FBB8EBB2DBB0FB658 -:1016D000F894E29AE19A0FBE01960895F894FFCF2C -:1016E000FF5A0A000A0803350031003200200000CA +:10012000CFB7CF93C395B39BE9F7B39B0BC0B39BFA +:1001300009C0B39B07C0B39B05C0B39B03C0B39B6F +:1001400001C0D5C00F92DF93C0916A01DD27CF585F +:10015000DE4F012EB39B03C0DF910F90E6CF2F93AC +:100160000F931F934F932FEF4F6F06B303FB20F9AD +:100170005F933F9350E03BE065C016B301265029E2 +:1001800053FDC89556B3012703FB25F92F7306B31A +:10019000B1F05027102713FB26F906B22230F0F0F9 +:1001A00000C016B3012703FB27F90126502906B228 +:1001B0002430E8F54F77206816B30000F6CF5027BB +:1001C0004F7D206206B2102F000000C006B300264B +:1001D0005029102713FB26F906B2E2CF4F7B06B356 +:1001E000206400C0DACF01265029187106B269F1E7 +:1001F0004E7F2160012F16B328C0002650294D7F65 +:1002000006B22260102F29C0012650294B7F06B26A +:100210002460012F2DC016B301265029477F286086 +:10022000000006B22EC04F7E06B3206130C04227C8 +:1002300006B3499300265029102706B24FEF13FB4F +:1002400020F9297F16B379F2187159F10126502946 +:1002500006B2012703FB21F9237F06B371F20026C2 +:1002600050293150D0F006B2102713FB22F9277E17 +:1002700016B351F201265029012703FB06B223F9D8 +:100280002F7C49F2000006B3102713FB24F9002647 +:10029000502906B22F7939F270CF10E21ABF002729 +:1002A00019C03B503195C31BD04010E21ABF0881E2 +:1002B000033C09F10B34F9F0209168011981110F09 +:1002C0001213EDCF4A81441F093651F10D3211F05E +:1002D000013E29F700936F013F915F914F911F916C +:1002E0000F912F91DF910F90CAB7C5FD1BCFCF9112 +:1002F000CFBFCF91189520916F01222369F3109100 +:100300006D01112379F534307AF130936D0120932A +:10031000690110916A013BE0311B30936A0124C0EE +:1003200000916D010130F4F40AE54F7049F4309109 +:10033000610034FD1AC000936100C1E5D1E019C02D +:1003400030915C0134FD11C000935C01CDE5D1E03A +:1003500010C0052710E000C021C0052710E0C89597 +:1003600008BB14C03AE501C032ED032EC0E0D0E076 +:1003700032E017B31861C39A08B317BB58E120E8FD +:100380004FEF20FF052708BB279517951C3F28F73F +:1003900000004552B0F720FF0527279508BB1795A9 +:1003A0001C3FB8F629913A9561F7077E10916E01CE +:1003B000110F08BBC250D04011F01093680110E239 +:1003C0001ABF086017B3177E402F477E54E05A9536 +:1003D000F1F708BB17BB48BB7FCFCF93DF9360918A +:1003E0006D01635067FDB7C080916A01CCE0D0E039 +:1003F000C81BD109CF58DE4F809169018D3209F0B9 +:1004000099C0683009F0A5C083EC809351018AE55A +:1004100080936100109271008881807639F0CE015E +:100420003AD2982F8F3F09F474C07AC09A81109203 +:100430005A018981811106C010925B012AE531E0E1 +:1004400092E062C0853019F490936E015AC08630F4 +:1004500009F041C08B81813049F481E690E09093AE +:100460006C0180936B0180E492E132C0823049F4E8 +:1004700088E490E090936C0180936B0180E499E1B3 +:1004800027C0833019F5911109C089E890E0909355 +:100490006C0180936B0180E494E01AC09130B1F05C +:1004A000923049F483E790E090936C0180936B0164 +:1004B00080E496E10DC0933049F485E690E0909396 +:1004C0006C0180936B0180E098E002C080E490E0D2 +:1004D0008093710025C0883069F0893019F49093B9 +:1004E00070010FC08A3049F08B3059F48BE480934F +:1004F0005D0107C020E731E002C02AE531E091E06C +:1005000003C02AE531E090E030936C0120936B0149 +:1005100007C0888187FD9E8180E88093710007C0B5 +:100520008F81811104C08E81891708F4982F9093D0 +:1005300060000FC08091710087FF0BC0CE0112D107 +:100540008F3F21F48EE18093610003C081111092EE +:10055000600010926D018091610084FF56C080910F +:1005600060008F3F09F451C0C82F893008F0C8E0FF +:100570008C1B809360009091510188E889278093BB +:100580005101CC2389F12091710027FF08C06C2F05 +:1005900082E591E0C8D0C82F893088F525C08091C8 +:1005A0006B0190916C0126FF0BC0A2E5B1E0FC014C +:1005B0002C2F280F34913D9331962E13FBCF09C079 +:1005C000DC01E2E5F1E02C2F2E0F3D9131932E134B +:1005D000FCCF2FEF2C0F30E02F5F3F4F820F931F88 +:1005E00090936C0180936B016C2F82E591E080DD2C +:1005F000CC5FCC3041F08FEF8093600004C08FEF70 +:1006000080936000CEE1C093610084E196B398715D +:1006100031F48150D9F710926E0110926801C1E057 +:100620008111C0E0809170008C1729F0C11101C0C8 +:1006300081D3C0937000DF91CF910895AC9A8BB7AE +:1006400080628BBFECE5F1E08BE481838AE58083F7 +:100650000895FC01DB01AC014C5F5F4F619128E024 +:1006600030E080E890E0782F762309F0C09A70910E +:100670006400072E00C000C000000A94D9F7C29A97 +:1006800070916400072E00C000C000000A94D9F7E2 +:10069000990FB1999F5FC098C298869521503109F2 +:1006A00011F79D93E417F507C9F6089580914D0160 +:1006B00090914E019C012F5F3F4F30934E0120934C +:1006C0004D0120914A0127FF02C0880F991F80FD2C +:1006D000286020934601292F26952093470196955F +:1006E00087958093480162E471E086E491E0B1CFA0 +:1006F00008C024E02A95F1F7019730F024E02A950C +:10070000F1F700C00197A8F70895DC0100C000C010 +:1007100000C000C000C000C087EE93E0E9DF000029 +:100720001197A0F70895EF92FF921F93CF93DF9355 +:100730001F92CDB7DEB7182F7C018E2D811B861737 +:1007400048F46983B3DF80914501F70181937F010C +:100750006981F3CF862F0F90DF91CF911F91FF908A +:10076000EF900895DF92EF92FF920F931F93CF9334 +:10077000DF93C62F20918A01211105C0D82F8C014B +:1007800030E6D32E4EC020911C024091890170E0CA +:10079000620F711D50E021E06417750714F420E02A +:1007A00008C0C091890130911C02C31B34E1309311 +:1007B0003401382FDC018A2F831B8C1760F4E09101 +:1007C0001C0281E08E0F80931C02F0E08D91EC56AC +:1007D000FE4F8083F0CF822F37C08091640090E07D +:1007E00025E0880F991F2A95E1F7E80EF91E809100 +:1007F0004B0190914C01E816F90698F462E471E01F +:1008000086E491E026DF80914501909149018913AA +:10081000E4CF90915001891701F390914F0189170E +:10082000E1F2802F8D1B8C1770F4F80181918F01FC +:10083000809349013BDF809146018D2580934601DD +:10084000E12CF12CD4CF81E0DF91CF911F910F915A +:10085000FF90EF90DF90089528B3342F3095232335 +:1008600038B3432B680F791F8617970799F0FC015F +:100870003191CF0158E048BB330F5A9500C008F0C2 +:1008800028BB00C0000028BB21F000C000C000C091 +:10089000F2CFEACF0895CF93DF93EC0129812111A4 +:1008A00008C081E28983D0936C01C0936B0198E00A +:1008B0003DC2213011F486B30BC16A81223011F49C +:1008C00068BB33C2962F362F377041E050E0032EBD +:1008D00002C0440F551F0A94E2F7233009F496C072 +:1008E000243021F488B3482B48BB1FC2253051F473 +:1008F00060936400B99887B3856287BB88B3887DAD +:1009000088BB13C2263049F4B898C098B998C198EA +:10091000BA98C298BD98C59808C2273051F4BE0154 +:10092000CE01029696DED0936C01C0936B0194E0E9 +:10093000FDC1283031F4609350018B8180934F01C9 +:10094000F4C1EC81FD81F0934E01E0934D0129301B +:1009500011F480E203C02B3021F480EA80934A0135 +:10096000A4C1EA81FB81F0934C01E0934B012A3052 +:1009700011F480E4F3CF2C3011F480ECEFCF2D3064 +:1009800021F487B34095482304C02E3021F487B367 +:10099000482B47BBCAC12F3009F5611105C08091B2 +:1009A000350187B9A59A0CC0613031F480913501C9 +:1009B000816087B9A29A04C0623011F48FE887B9C8 +:1009C000369A3699FECF84B1888385B18983D093D6 +:1009D0006C01C0936B0114BA92E0A8C1203159F4A4 +:1009E000B89AB99A8AB5806A8ABD8AB583608ABD89 +:1009F00083B7856044C0213121F469BD8C8188BDF5 +:100A000094C1223109F46ECF233121F488B340958B +:100A100048236ACF243159F486B390E04823592300 +:100A200002C0559547953A95E2F7488352C0263162 +:100A3000A1F561110AC083B78B7F83BF83B78D7F18 +:100A400083BF83B7816083BF71C1613031F483B7E5 +:100A50008B7F83BF83B782600FC0623031F483B76E +:100A60008B7F83BF83B7826014C0633051F483B738 +:100A7000846083BF83B78D7F83BF83B78E7F83BF3F +:100A800054C1643009F051C183B7846083BF83B718 +:100A90008D7F83BF83B78160F2CF2F3159F48B8173 +:100AA00090E0982F8827860F911D90936300809384 +:100AB00062003BC1203219F41ABC13BE36C1213288 +:100AC00021F46093140281E07FC0223241F483E17B +:100AD0008883D0936C01C0936B0191E027C12332CE +:100AE000C9F4379A96B18A81892B86B99B81913056 +:100AF00059F038F0923009F018C1809135018069C1 +:100B000006C01092350112C1809135018068809332 +:100B100035010BC1283249F48BE891E090936C01C8 +:100B200080936B019091930101C1293211F482E00D +:100B30004BC02A3211F483E00BC02B3211F484E055 +:100B400043C02C3211F488E03FC02D3231F489E0EB +:100B50008093340160931402E8C02E3261F48BE07C +:100B600080933401609314028B81809315028C81F1 +:100B700080931602DAC02F3259F4613031F4B89AFA +:100B8000B99ABA9A60933C01D0C010923C01CDC092 +:100B9000203349F4609338018B81809337018C8135 +:100BA00080933601C2C0213331F470E0709341016B +:100BB00060934001BAC0223311F485E005C02333AD +:100BC00031F46093140286E080933401AEC0263382 +:100BD00071F565FF1BC0E0917200E03CB8F48B81B9 +:100BE000AE2FB0E0AC58BF4F8C938C81A1E0AE0F1C +:100BF000B0E0AC58BF4F8C9383E08E0F80937200AF +:100C00008D81EE5FF0E0EC58FF4F80838A8184FF96 +:100C10008CC080917200882309F487C081E18093A1 +:100C2000340187B3842B87BB409373007EC0273386 +:100C3000A1F480916D0090916E0056D580916D0069 +:100C400090916E006B8101964FD580916D009091CF +:100C50006E006C81029648D568C02C3341F58091B6 +:100C60008A01811162C0888187FF09C084E991E00F +:100C700090936C0180936B019091890157C06093B0 +:100C80008A018E818093890190918901911104C01C +:100C900084E1809334014AC080918901813818F041 +:100CA00080E88093890110921C029FEF3FC0822F41 +:100CB000807F803EE1F48AE080933401822F877048 +:100CC0008093140228702093150220E08091140272 +:100CD000281758F5822F90E00296FE01E80FF91FC1 +:100CE0003081FC01EC5EFD4F30832F5FEFCF803F02 +:100CF000E1F487E080933401822F8870809314029E +:100D000027702093150220E080911502281768F4BF +:100D1000822F90E00296FE01E80FF91F3081FC015E +:100D2000EC5EFD4F30832F5FEFCF90E0892FDF9196 +:100D3000CF910895DF92EF92FF920F931F93CF937D +:100D4000DF9308E010E0C0E0D0E8DD2EDC0ED1BE7D +:100D5000D3D97C0184E3E81689E0F8060CF4CD2DA4 +:100D6000D6950150110989F78FEF8C0F81BF0C2F99 +:100D700010E00F5F1F4F21B730E0021713078CF010 +:100D8000BBD98453994097FF03C0919581959109F0 +:100D90008E159F0514F4C1B77C0181B78F5F81BFA9 +:100DA000EACFC1BF61B780E090E0DF91CF911F91A2 +:100DB0000F91FF90EF90DF9097C4CF93DF93D82FE0 +:100DC000882311F0B89801C0B89AC0E02C2F30E009 +:100DD00080914001909141012817390728F481E062 +:100DE00090E086DCCF5FF2CFBA98C0E02C2F30E0E5 +:100DF00080914001909141012817390728F481E042 +:100E000090E076DCCF5FF2CFBA9AC0E02C2F30E0D2 +:100E100080914001909141012817390728F481E021 +:100E200090E066DCCF5FF2CFD111B89AC0E08C2F92 +:100E300090E020914001309141018217930728F4FE +:100E400081E090E055DCCF5FF2CFDF91CF91089544 +:100E50000F931F93CF93DF93B898C0E08C2F90E04F +:100E600020914001309141018217930728F481E0DD +:100E700090E03EDCCF5FF2CFBA98C0E00091400135 +:100E8000109141018C2F90E08017910728F481E0A8 +:100E900090E02EDCCF5FF2CFD6B3C0E08C2F90E095 +:100EA0008017910728F481E090E022DCCF5FF6CF35 +:100EB000BA9AC0E02C2F30E080914001909141011E +:100EC0002817390728F481E090E012DCCF5FF2CFD9 +:100ED0008D2F8170DF91CF911F910F910895CF9346 +:100EE00088B38A7F88BBBA98B898C0E08C2F90E00E +:100EF00020914001309141018217930728F481E04D +:100F000090E0F6DBCF5FF2CFCF910895CF9387B318 +:100F10008A7F87BBC0E08C2F90E020914001309108 +:100F200041018217930728F481E090E0E1DBCF5F75 +:100F3000F2CFB89AC0E08C2F90E020914001309120 +:100F400041018217930728F481E090E0D1DBCF5F65 +:100F5000F2CFBA9AC0E08C2F90E0209140013091FE +:100F600041018217930728F481E090E0C1DBCF5F55 +:100F7000F2CFCF910895CF93B89AC0E08C2F90E034 +:100F800020914001309141018217930728F481E0BC +:100F900090E0AEDBCF5FF2CFBA9AC0E08C2F90E04A +:100FA00020914001309141018217930728F481E09C +:100FB00090E09EDBCF5FF2CFBA98C0E08C2F90E03C +:100FC00020914001309141018217930728F481E07C +:100FD00090E08EDBCF5FF2CFB898C0E08C2F90E02E +:100FE00020914001309141018217930728F481E05C +:100FF00090E07EDBCF5FF2CFCF910895CF93DF9368 +:10100000D82FC8E08D2F8078D8DEDD0FC150D1F702 +:10101000DF91CF911DCFFF920F931F93CF93DF935B +:10102000F82ED8E0C0E0CC0F13DFC82BD150D9F791 +:1010300000914001109141018D2F90E080179107A0 +:1010400028F481E090E054DBDF5FF2CFFF2011F065 +:1010500080E001C081E0B1DED0E08D2F90E080170C +:10106000910728F481E090E043DBDF5FF6CF8C2F1F +:10107000DF91CF911F910F91FF900895A4E9B1E006 +:101080008827F894C29ABA98EE27FF27319688F0FD +:10109000B299FCCF803468F4ED93FD938395EE27ED +:1010A000FF27319630F0B29BFCCFED93FD938395F3 +:1010B000EBCF7894880F809389010895C29ABA9AE9 +:1010C0008091870190918801A4E9B1E07091890134 +:1010D0007723C1F0F894FC01C2986D9158E03197E4 +:1010E000F1F760FDC29A60FFC298FC013197F1F7F9 +:1010F00066955A95B1F7FC01C29A31963197F1F78E +:101100007A9549F7BA980895772780918701909149 +:101110008801A4E9B1E0EE27FF273197F1F0B299F9 +:10112000FCCFFC01F695E7953197F1F7B299F3CF33 +:10113000662758E0FC013197F1F76695B29960682F +:1011400000C05A95B9F76D937395EE27FF27319735 +:1011500021F0B29BFCCF7038F0F278947093890143 +:101160000895AA9BDCC70F930FB70F93B29904C0E1 +:1011700001E000939401AA98AC9B06C0AC9800E8EB +:101180000FBFCDD7F894AC9A0F910FBF0F91189560 +:1011900080E287BB88BB80E090E09ED28F3F09F061 +:1011A00081BFC0916D00D0916E00CE0195D2E82E26 +:1011B000CE01019691D2F82ECE0102968DD2982FB3 +:1011C00080ED8E0D8A3008F075C280ED8F0D8A306B +:1011D00008F070C280ED890F8A3008F06BC2C091B0 +:1011E0006D00D0916E00CE0177D290E090936800B0 +:1011F00080936700CE0101966FD290E090936A00D1 +:1012000080936900CE01029667D290E090936C00C3 +:1012100080936B00BB9AC4E18FE090E076DAC15016 +:10122000D9F7BB9885E090E090934101809340010D +:1012300010926300109262009EE088E10FB6F8946D +:10124000A89581BD0FBE91BD10923B0110923A014D +:101250001092390110923801109237011092360124 +:1012600010923D0110923401E9D97894B898C09851 +:10127000B998C198BA98C298BD98C598DAE130E497 +:10128000F32EC1E04BE1E42E20E8D22EA895A5D89C +:10129000E09134018E2F90E08531910508F0D3C1A3 +:1012A000FC01E15FFF4F0994B89AB998BA9AC29AC3 +:1012B00010928B018091140280933F01D0923E01E5 +:1012C00080913E01882309F458C0C098B12C8B2D21 +:1012D00090E020916200309163008217930728F418 +:1012E00081E090E005DAB394F2CF90913E018091D5 +:1012F0003F01892309F0C09AC29880918B01880F21 +:1013000080938B0196B320918B0191FB882780F904 +:10131000820F80938B01B12C8B2D90E02091620085 +:10132000309163008217930728F481E090E0E0D9C0 +:10133000B394F2CFC29A80913E01869580933E018C +:10134000BFCFBA9AC29880EE91E0D2D9BFB6F894D6 +:10135000C29A86E490E0CCD9BA9886B382FB002783 +:1013600000F910E0C80121E0822780938B01BFBE05 +:101370008AE991E0BDD9BA9AC29AC093930163C138 +:101380008091140280933E01BA9A98E0B92EAFB6CC +:10139000F894C29880913E0180FF07C086E090E0FB +:1013A000A7D9C29A80E490E006C08CE390E0A0D96F +:1013B000C29A8AE090E09CD980913E018695809304 +:1013C0003E01AFBEBA94B110E2CF3DC110928B0185 +:1013D00088E0B82E80918B01869580938B01BA9A14 +:1013E000AFB6F894C29886E090E082D9C29A8AE0BB +:1013F00090E07ED9BA9886B382FB882780F98093E3 +:101400003E01AFBE87E390E073D980913E0188230F +:1014100029F080918B01806880938B01BA94B11080 +:10142000D9CFABCFBA9ABFB6F894C29886E090E015 +:101430005FD9C29A8AE090E05BD9BA9886B382FB02 +:10144000882780F980938B01BFBE97CFBA9ABFB629 +:10145000F894C2988091140280FF07C086E090E063 +:1014600047D9C29A80E490E006C08CE390E040D96E +:10147000C29A8AE090E03CD9BFBEE5C0B99AB8985C +:10148000BA9AC298E0923F01DDB9809114028111AD +:10149000C59810923E01E0913E0180911502E81737 +:1014A00030F5F0E0EA5EFD4F80818FB9FEB88091A3 +:1014B0003F018DB9B12C8B2D90E0209162003091CD +:1014C00063008217930728F481E090E011D9B39468 +:1014D000F2CF769BECCF80913E01E82FF0E09FB1F8 +:1014E000E557FE4F90838F5F80933E01D4CF80916C +:1014F00014028111C59A80913E0156C0F0DCA3C050 +:1015000080E090E003DD8091140278DD80933E015D +:1015100015C0B12C80911402B81650F4EB2DF0E0F8 +:10152000EA5EFD4F80816ADD80933E01B394F2CF85 +:101530008091150281111FDD80913E0180938B0106 +:101540001CCF80911402811102C0B12C19C0912CC2 +:10155000292D30E08091150290E00197A9014557AF +:101560005E4F5A012817390734F481E054DDF50144 +:1015700080839394EDCF80E04EDDF50180830EC033 +:1015800080911502B81650F48B2C912C81E043DD2C +:10159000F401E557FE4F8083B394F2CF8091160299 +:1015A0008111E9DC80911502809393014CC081E0A8 +:1015B00090E0ABD8BFB6F8946091720070E04091B3 +:1015C000730084E790E048D91092720055CF809163 +:1015D0008A01847319F082E090E097D880918A01A3 +:1015E00080FF05C0C298BA9A84E690E08ED88091B8 +:1015F0008A0181FF08C080919401809387018091C6 +:1016000095018093880180918A0182FD57DD809148 +:101610008A0183FF0AC080918A01807F80939401B0 +:10162000C0938901BA98AA9A789480918A0184FD1E +:101630006BDD80918A0185FD21DD109234011092CD +:101640008A01789402C01092340180913C01882371 +:1016500009F41CCE80913D0181110DC080913801AB +:1016600080933B018091370180933A01809136014C +:101670008093390119C090913B01981710F4C098DC +:1016800001C0C09A90913D0180913A01891710F4F0 +:10169000C19801C0C19A90913D018091390189178B +:1016A00010F4C29801C0C29A80913D018F5F80936F +:1016B0003D01EACD65E3CE0117D080916D00909198 +:1016C0006E0061E3019610D080916D0090916E00E4 +:1016D00062E3029609D083CDE199FECF9FBB8EBB1A +:1016E000E09A99278DB30895262FE199FECF1CBA71 +:1016F0009FBB8EBB2DBB0FB6F894E29AE19A0FBE4A +:0817000001960895F894FFCF53 +:10170800FF5A0A000A0803350031003200200000A1 :00000001FF diff --git a/usbtiny/usbconfig.h b/usbtiny/usbconfig.h index 32d148a..2bc0e5f 100644 --- a/usbtiny/usbconfig.h +++ b/usbtiny/usbconfig.h @@ -324,26 +324,9 @@ extern void usbEventResetReady(void); further interrupts from the debugWIRE pin changing. */ -// debugWIRE bit normally 5, but can be changed for debugging -#define DW_BIT 2 - -#ifdef __ASSEMBLER__ -macro nonUsbPinChange - .global dwBuf - sbic PINB,DW_BIT - rjmp dWirePinIdle - - ldi YL,1 - sts dwBuf,YL - cbi PCMSK,DW_BIT ; Disable further interrupts on dwire pin change - -dWirePinIdle: - endm -#endif - -#define USB_SOF_HOOK nonUsbPinChange - - - +// non-USB pin change now handled in main.c +#undef USB_INTR_VECTOR // nest the usb interrupt inside the debugWIRE interrupt +#define USB_INTR_VECTOR DW_INTR_VECTOR +#define USB_INTR_CFG_BIT USB_CFG_DPLUS_BIT #endif /* __usbconfig_h_included__ */ From 602b278bdface58a3aa8444fd05f3a24d7c16f10 Mon Sep 17 00:00:00 2001 From: Peter Maivald Date: Tue, 31 Oct 2017 13:39:22 +0100 Subject: [PATCH 03/18] run dwReadBytes from interrupt --- usbtiny/Makefile | 2 +- usbtiny/main.c | 72 +++++++++++---- usbtiny/main.hex | 232 ++++++++++++++++++++++++----------------------- 3 files changed, 175 insertions(+), 131 deletions(-) diff --git a/usbtiny/Makefile b/usbtiny/Makefile index 2ff9e2c..4a54d69 100644 --- a/usbtiny/Makefile +++ b/usbtiny/Makefile @@ -71,7 +71,7 @@ usbdrv: cp -r ../../../usbdrv . main.elf: usbdrv $(OBJECTS) # usbdrv dependency only needed because we copy it - $(COMPILE) -o main.elf $(OBJECTS) + $(COMPILE) -Wl,-Map,main.map -o main.elf $(OBJECTS) main.hex: main.elf rm -f main.hex main.eep.hex diff --git a/usbtiny/main.c b/usbtiny/main.c index 208dfea..7392b36 100644 --- a/usbtiny/main.c +++ b/usbtiny/main.c @@ -1347,7 +1347,7 @@ void dwCaptureWidths() { " \n" "; Measurement complete \n" " \n" - "dwc8: sei ; Re-enable interrupts \n" + "dwc8: ; Caller will re-enable interrupts \n" " add r24,r24 ; Convert word count to byte count \n" " sts dwLen,r24 \n" ::[bit]"I"(DW_BIT):"r24","r26","r27","r30","r31"); @@ -1556,7 +1556,7 @@ void dwReadBytes() { " \n" "; Read complete \n" " \n" - "dwr14: sei ; Re-enable interrupts \n" + "dwr14: ; Caller will re-enable interrupts \n" " sts dwLen,r23 \n" " \n" ::[bit]"I"(DW_BIT):"r21","r22","r23","r24","r25","r26","r27","r30","r31"); @@ -1576,37 +1576,76 @@ start bit command allows the usb interrupt to be pre-empted. __attribute__((naked, used)) void PCINT0_vect(void) { asm("\n" " sbis %[pcmsk],%[dwbit] ; not for us if dw interrupt disabled \n" -" rjmp DW_INTR_VECTOR ; leave interrupts disabled \n" -" push r16 \n" -" in r16,__SREG__ \n" -" push r16 \n" +" rjmp DW_INTR_VECTOR ; leave interrupts disabled for usb \n" +" \n" +" push r21 \n" +" in r21,__SREG__ \n" +" push r21 \n" " sbic %[pinb],%[dwbit] ; not for us if dw line high \n" " rjmp dw_pass%= \n" " \n" -" ldi r16,1 \n" -" sts dwBuf,r16 ; set break detected flag \n" +" lds r21,dwBuf ; get dwState right shifted by 4 bits \n" +" tst r21 \n" +" breq dw_det%= \n" +" \n" +" push r22 ; save registers for called functions \n" +" push r23 \n" +" push r24 \n" +" push r25 \n" +" push r26 \n" +" push r27 \n" +" push r30 \n" +" push r31 \n" +" \n" +" asr r21 \n" +" brcs dw_rb%= \n" +" asr r21 \n" +" brcs dw_cw%= \n" +" rjmp dw_res%= \n" +" \n" +"dw_rb%=: ; dwState & 0x10 \n" +" rcall dwReadBytes \n" +" rjmp dw_res%= \n" +"dw_cw%=: ; dwState & 0x20 \n" +" rcall dwCaptureWidths \n" +"; rjmp dw_res%= \n" +" \n" +"dw_res%=: \n" +" pop r31 \n" +" pop r30 \n" +" pop r27 \n" +" pop r26 \n" +" pop r25 \n" +" pop r24 \n" +" pop r23 \n" +" pop r22 ; restore registers \n" +" rjmp dw_pass%= \n" +" \n" +"dw_det%=: \n" +" ldi r21,1 ; no further commands \n" +" sts dwBuf,r21 ; set break detected flag \n" " cbi %[pcmsk],%[dwbit] ; Disable further dw interrupts \n" " \n" -"dw_pass%=: \n" +"dw_pass%=: ; pass interrupt to usb \n" " sbis %[pcmsk],%[usbbit] ; call usb interrupt when set \n" " rjmp dw_done%= ; don't re-enter \n" " cbi %[pcmsk],%[usbbit] ; clear to indicate in usb interrupt \n" -" ldi r16,0x80 ; enable interrupts to detect start bit and use\n" -" out __SREG__,r16 ; this value for timeout hack in usb interrupt \n" +" ldi r21,0x80 ; enable interrupts to detect start bit and use\n" +" out __SREG__,r21 ; this value for timeout hack in usb interrupt \n" " rcall DW_INTR_VECTOR ; call usb interrupt with interrupts enabled \n" " cli ; disable to prevent re-entry from here \n" " sbi %[pcmsk],%[usbbit] ; set to indicate not in usb interrupt \n" "dw_done%=: \n" -" pop r16 \n" -" out __SREG__,r16 \n" -" pop r16 \n" +" pop r21 \n" +" out __SREG__,r21 \n" +" pop r21 \n" " reti \n" : : [pcmsk] "I" (_SFR_IO_ADDR(PCMSK)) ,[pinb] "I" (_SFR_IO_ADDR(PINB)) ,[dwbit] "I" (DW_BIT) ,[usbbit] "I" (USB_INTR_CFG_BIT) - :"r16"); + :); } @@ -1968,7 +2007,8 @@ int main(void) { if (dwState & 0x01) {cbi(PORTB, DW_BIT); sbi(DDRB, DW_BIT); _delay_ms(100);} if (dwState & 0x02) {((char*)&dwBitTime)[0] = dwBuf[0]; ((char*)&dwBitTime)[1] = dwBuf[1];} if (dwState & 0x04) {dwSendBytes();} - if (dwState & 0x08) {dwBuf[0]=(dwState & ~0x0F); dwLen=1; cbi(DDRB,DW_BIT); sbi(PCMSK,DW_BIT); sei();} // Capture dWIRE pin change + if (dwState & 0x08) {dwBuf[0]=dwState>>4; dwLen=dwBuf[0]?0:1; dwState=0; + cbi(DDRB,DW_BIT); sbi(PCMSK,DW_BIT);} // Capture dWIRE pin change async if (dwState & 0x10) {dwReadBytes();} if (dwState & 0x20) {dwCaptureWidths();} diff --git a/usbtiny/main.hex b/usbtiny/main.hex index 61e348c..fed2d68 100644 --- a/usbtiny/main.hex +++ b/usbtiny/main.hex @@ -1,16 +1,16 @@ -:1000000046C060C0AEC85EC05DC05CC05BC05AC0C8 -:1000100059C058C057C056C055C054C053C015CB66 -:1000200043C98FC9ADC9D2C9FDC910CA27CA66CA9A -:1000300067CA6FCA86CA07CB06CB05CB04CB03CBF6 -:10004000B6CA01CB00CBC3CA090219000101008066 +:1000000046C060C0ACC85EC05DC05CC05BC05AC0CA +:1000100059C058C057C056C055C054C053C037CB44 +:100020005EC9AAC9C8C9EDC918CA2BCA42CA81CAC1 +:1000300082CA8ACAA1CA29CB28CB27CB26CB25CBFB +:10004000D1CA23CB22CBDECA0902190001010080EC :10005000640904000001FF00000007058103080097 :1000600064120110010000000881179F0C040100B8 :100070000203011603550053004200740069006E2C :1000800000790053005000490004030904001124C2 :100090001FBECFE5D2E0DEBFCDBF10E0A0E6B0E0EE -:1000A000E8E0F7E102C005900D92A037B107D9F75B +:1000A000ECE4F7E102C005900D92A037B107D9F753 :1000B00022E0A0E7B0E001C01D92AD31B207E1F748 -:1000C00067D820CB9DCFA82FB92F80E090E041E0EA +:1000C00082D842CB9DCFA82FB92F80E090E041E0AD :1000D00050EA609530E009C02D9182279795879569 :1000E00010F084279527305EC8F36F5FA8F308955A :1000F000EADF8D939D930895A6E088279927AA9516 @@ -193,9 +193,9 @@ :100C00008D81EE5FF0E0EC58FF4F80838A8184FF96 :100C10008CC080917200882309F487C081E18093A1 :100C2000340187B3842B87BB409373007EC0273386 -:100C3000A1F480916D0090916E0056D580916D0069 -:100C400090916E006B8101964FD580916D009091CF -:100C50006E006C81029648D568C02C3341F58091B6 +:100C3000A1F480916D0090916E0078D580916D0047 +:100C400090916E006B81019671D580916D009091AD +:100C50006E006C8102966AD568C02C3341F5809194 :100C60008A01811162C0888187FF09C084E991E00F :100C700090936C0180936B019091890157C06093B0 :100C80008A018E818093890190918901911104C01C @@ -217,7 +217,7 @@ :100D8000BBD98453994097FF03C0919581959109F0 :100D90008E159F0514F4C1B77C0181B78F5F81BFA9 :100DA000EACFC1BF61B780E090E0DF91CF911F91A2 -:100DB0000F91FF90EF90DF9097C4CF93DF93D82FE0 +:100DB0000F91FF90EF90DF90B9C4CF93DF93D82FBE :100DC000882311F0B89801C0B89AC0E02C2F30E009 :100DD00080914001909141012817390728F481E062 :100DE00090E086DCCF5FF2CFBA98C0E02C2F30E0E5 @@ -265,107 +265,111 @@ :101080008827F894C29ABA98EE27FF27319688F0FD :10109000B299FCCF803468F4ED93FD938395EE27ED :1010A000FF27319630F0B29BFCCFED93FD938395F3 -:1010B000EBCF7894880F809389010895C29ABA9AE9 -:1010C0008091870190918801A4E9B1E07091890134 -:1010D0007723C1F0F894FC01C2986D9158E03197E4 -:1010E000F1F760FDC29A60FFC298FC013197F1F7F9 -:1010F00066955A95B1F7FC01C29A31963197F1F78E -:101100007A9549F7BA980895772780918701909149 -:101110008801A4E9B1E0EE27FF273197F1F0B299F9 -:10112000FCCFFC01F695E7953197F1F7B299F3CF33 -:10113000662758E0FC013197F1F76695B29960682F -:1011400000C05A95B9F76D937395EE27FF27319735 -:1011500021F0B29BFCCF7038F0F278947093890143 -:101160000895AA9BDCC70F930FB70F93B29904C0E1 -:1011700001E000939401AA98AC9B06C0AC9800E8EB -:101180000FBFCDD7F894AC9A0F910FBF0F91189560 -:1011900080E287BB88BB80E090E09ED28F3F09F061 -:1011A00081BFC0916D00D0916E00CE0195D2E82E26 -:1011B000CE01019691D2F82ECE0102968DD2982FB3 -:1011C00080ED8E0D8A3008F075C280ED8F0D8A306B -:1011D00008F070C280ED890F8A3008F06BC2C091B0 -:1011E0006D00D0916E00CE0177D290E090936800B0 -:1011F00080936700CE0101966FD290E090936A00D1 -:1012000080936900CE01029667D290E090936C00C3 -:1012100080936B00BB9AC4E18FE090E076DAC15016 -:10122000D9F7BB9885E090E090934101809340010D -:1012300010926300109262009EE088E10FB6F8946D -:10124000A89581BD0FBE91BD10923B0110923A014D -:101250001092390110923801109237011092360124 -:1012600010923D0110923401E9D97894B898C09851 -:10127000B998C198BA98C298BD98C598DAE130E497 -:10128000F32EC1E04BE1E42E20E8D22EA895A5D89C -:10129000E09134018E2F90E08531910508F0D3C1A3 -:1012A000FC01E15FFF4F0994B89AB998BA9AC29AC3 -:1012B00010928B018091140280933F01D0923E01E5 -:1012C00080913E01882309F458C0C098B12C8B2D21 -:1012D00090E020916200309163008217930728F418 -:1012E00081E090E005DAB394F2CF90913E018091D5 -:1012F0003F01892309F0C09AC29880918B01880F21 -:1013000080938B0196B320918B0191FB882780F904 -:10131000820F80938B01B12C8B2D90E02091620085 -:10132000309163008217930728F481E090E0E0D9C0 -:10133000B394F2CFC29A80913E01869580933E018C -:10134000BFCFBA9AC29880EE91E0D2D9BFB6F894D6 -:10135000C29A86E490E0CCD9BA9886B382FB002783 -:1013600000F910E0C80121E0822780938B01BFBE05 -:101370008AE991E0BDD9BA9AC29AC093930163C138 -:101380008091140280933E01BA9A98E0B92EAFB6CC -:10139000F894C29880913E0180FF07C086E090E0FB -:1013A000A7D9C29A80E490E006C08CE390E0A0D96F -:1013B000C29A8AE090E09CD980913E018695809304 -:1013C0003E01AFBEBA94B110E2CF3DC110928B0185 -:1013D00088E0B82E80918B01869580938B01BA9A14 -:1013E000AFB6F894C29886E090E082D9C29A8AE0BB -:1013F00090E07ED9BA9886B382FB882780F98093E3 -:101400003E01AFBE87E390E073D980913E0188230F -:1014100029F080918B01806880938B01BA94B11080 -:10142000D9CFABCFBA9ABFB6F894C29886E090E015 -:101430005FD9C29A8AE090E05BD9BA9886B382FB02 -:10144000882780F980938B01BFBE97CFBA9ABFB629 -:10145000F894C2988091140280FF07C086E090E063 -:1014600047D9C29A80E490E006C08CE390E040D96E -:10147000C29A8AE090E03CD9BFBEE5C0B99AB8985C -:10148000BA9AC298E0923F01DDB9809114028111AD -:10149000C59810923E01E0913E0180911502E81737 -:1014A00030F5F0E0EA5EFD4F80818FB9FEB88091A3 -:1014B0003F018DB9B12C8B2D90E0209162003091CD -:1014C00063008217930728F481E090E011D9B39468 -:1014D000F2CF769BECCF80913E01E82FF0E09FB1F8 -:1014E000E557FE4F90838F5F80933E01D4CF80916C -:1014F00014028111C59A80913E0156C0F0DCA3C050 -:1015000080E090E003DD8091140278DD80933E015D -:1015100015C0B12C80911402B81650F4EB2DF0E0F8 -:10152000EA5EFD4F80816ADD80933E01B394F2CF85 -:101530008091150281111FDD80913E0180938B0106 -:101540001CCF80911402811102C0B12C19C0912CC2 -:10155000292D30E08091150290E00197A9014557AF -:101560005E4F5A012817390734F481E054DDF50144 -:1015700080839394EDCF80E04EDDF50180830EC033 -:1015800080911502B81650F48B2C912C81E043DD2C -:10159000F401E557FE4F8083B394F2CF8091160299 -:1015A0008111E9DC80911502809393014CC081E0A8 -:1015B00090E0ABD8BFB6F8946091720070E04091B3 -:1015C000730084E790E048D91092720055CF809163 -:1015D0008A01847319F082E090E097D880918A01A3 -:1015E00080FF05C0C298BA9A84E690E08ED88091B8 -:1015F0008A0181FF08C080919401809387018091C6 -:1016000095018093880180918A0182FD57DD809148 -:101610008A0183FF0AC080918A01807F80939401B0 -:10162000C0938901BA98AA9A789480918A0184FD1E -:101630006BDD80918A0185FD21DD109234011092CD -:101640008A01789402C01092340180913C01882371 -:1016500009F41CCE80913D0181110DC080913801AB -:1016600080933B018091370180933A01809136014C -:101670008093390119C090913B01981710F4C098DC -:1016800001C0C09A90913D0180913A01891710F4F0 -:10169000C19801C0C19A90913D018091390189178B -:1016A00010F4C29801C0C29A80913D018F5F80936F -:1016B0003D01EACD65E3CE0117D080916D00909198 -:1016C0006E0061E3019610D080916D0090916E00E4 -:1016D00062E3029609D083CDE199FECF9FBB8EBB1A -:1016E000E09A99278DB30895262FE199FECF1CBA71 -:1016F0009FBB8EBB2DBB0FB6F894E29AE19A0FBE4A -:0817000001960895F894FFCF53 -:10170800FF5A0A000A0803350031003200200000A1 +:1010B000EBCF880F809389010895C29ABA9A8091E4 +:1010C000870190918801A4E9B1E0709189017723AB +:1010D000C1F0F894FC01C2986D9158E03197F1F796 +:1010E00060FDC29A60FFC298FC013197F1F76695E6 +:1010F0005A95B1F7FC01C29A31963197F1F77A957A +:1011000049F7BA98089577278091870190918801CF +:10111000A4E9B1E0EE27FF273197F1F0B299FCCFB7 +:10112000FC01F695E7953197F1F7B299F3CF662771 +:1011300058E0FC013197F1F76695B299606800C0FC +:101140005A95B9F76D937395EE27FF27319721F0E4 +:10115000B29BFCCF7038F0F2709389010895AA9B7E +:10116000DEC75F935FB75F93B29921C0509194013E +:101170005523C9F06F937F938F939F93AF93BF9342 +:10118000EF93FF93559518F0559518F003C0BBDF0A +:1011900001C074DFFF91EF91BF91AF919F918F914B +:1011A0007F916F9104C051E050939401AA98AC9B39 +:1011B00006C0AC9850E85FBFB2D7F894AC9A5F9184 +:1011C0005FBF5F91189580E287BB88BB80E090E0AD +:1011D000A5D28F3F09F081BFC0916D00D0916E0004 +:1011E000CE019CD2E82ECE01019698D2F82ECE01E7 +:1011F000029694D2982F80ED8E0D8A3008F07CC232 +:1012000080ED8F0D8A3008F077C280ED890F8A302B +:1012100008F072C2C0916D00D0916E00CE017ED2F6 +:1012200090E09093680080936700CE01019676D29B +:1012300090E090936A0080936900CE0102966ED28E +:1012400090E090936C0080936B00BB9AC4E18FE0B8 +:1012500090E05BDAC150D9F7BB9885E090E09093BD +:1012600041018093400110926300109262009EE061 +:1012700088E10FB6F894A89581BD0FBE91BD10927C +:101280003B0110923A0110923901109238011092EC +:1012900037011092360110923D0110923401CED9DF +:1012A0007894B898C098B998C198BA98C298BD98DF +:1012B000C598DAE130E4F32EC1E04BE1E42E20E8FA +:1012C000D22EA8958AD8E09134018E2F90E08531F6 +:1012D000910508F0DAC1FC01E15FFF4F0994B89A6B +:1012E000B998BA9AC29A10928B0180911402809395 +:1012F0003F01D0923E0180913E01882309F458C0FD +:10130000C098B12C8B2D90E0209162003091630049 +:101310008217930728F481E090E0EAD9B394F2CFE2 +:1013200090913E0180913F01892309F0C09AC298B3 +:1013300080918B01880F80938B0196B320918B0154 +:1013400091FB882780F9820F80938B01B12C8B2D24 +:1013500090E020916200309163008217930728F497 +:1013600081E090E0C5D9B394F2CFC29A80913E015A +:10137000869580933E01BFCFBA9AC29880EE91E0E5 +:10138000B7D9BFB6F894C29A86E490E0B1D9BA98BA +:1013900086B382FB002700F910E0C80121E0822714 +:1013A00080938B01BFBE8AE991E0A2D9BA9AC29A12 +:1013B000C09393016AC18091140280933E01BA9A4E +:1013C00098E0B92EAFB6F894C29880913E0180FFA4 +:1013D00007C086E090E08CD9C29A80E490E006C015 +:1013E0008CE390E085D9C29A8AE090E081D980911F +:1013F0003E01869580933E01AFBEBA94B110E2CF14 +:1014000044C110928B0188E0B82E80918B018695A3 +:1014100080938B01BA9AAFB6F894C29886E090E0B8 +:1014200067D9C29A8AE090E063D9BA9886B382FB02 +:10143000882780F980933E01AFBE87E390E058D9BA +:1014400080913E01882329F080918B0180688093F0 +:101450008B01BA94B110D9CFABCFBA9ABFB6F8947A +:10146000C29886E090E044D9C29A8AE090E040D9E0 +:10147000BA9886B382FB882780F980938B01BFBE20 +:1014800097CFBA9ABFB6F894C2988091140280FFA1 +:1014900007C086E090E02CD9C29A80E490E006C0B4 +:1014A0008CE390E025D9C29A8AE090E021D9BFBEB2 +:1014B000ECC0B99AB898BA9AC298E0923F01DDB9E7 +:1014C000809114028111C59810923E01E0913E0175 +:1014D00080911502E81730F5F0E0EA5EFD4F80815B +:1014E0008FB9FEB880913F018DB9B12C8B2D90E062 +:1014F00020916200309163008217930728F481E005 +:1015000090E0F6D8B394F2CF769BECCF80913E0179 +:10151000E82FF0E09FB1E557FE4F90838F5F8093F7 +:101520003E01D4CF809114028111C59A80913E0171 +:1015300056C0D5DCAAC080E090E0E8DC80911402BF +:101540005DDD80933E0115C0B12C80911402B81668 +:1015500050F4EB2DF0E0EA5EFD4F80814FDD80938B +:101560003E01B394F2CF80911502811104DD809188 +:101570003E0180938B011CCF80911402811102C027 +:10158000B12C19C0912C292D30E08091150290E0EA +:101590000197A90145575E4F5A012817390734F4BE +:1015A00081E039DDF50180839394EDCF80E033DD78 +:1015B000F50180830EC080911502B81650F48B2C73 +:1015C000912C81E028DDF401E557FE4F8083B39430 +:1015D000F2CF809116028111CEDC809115028093AA +:1015E000930153C081E090E090D8BFB6F894609129 +:1015F000720070E04091730084E790E02DD9109262 +:10160000720055CF80918A01847319F082E090E0D6 +:101610007CD880918A0180FF05C0C298BA9A84E67E +:1016200090E073D880918A0181FF08C08091940175 +:1016300080938701809195018093880180918A0130 +:1016400082FD3BDD80918A0183FF11C080918A0178 +:1016500082958F70809394019091940181E0911113 +:1016600080E08093890110928A01BA98AA9A8091A9 +:101670008A0184FD48DD80918A0185FDFFDC10929E +:10168000340110928A01789402C010923401809142 +:101690003C01882309F415CE80913D0181110DC0D4 +:1016A0008091380180933B018091370180933A010A +:1016B000809136018093390119C090913B019817B0 +:1016C00010F4C09801C0C09A90913D0180913A01F8 +:1016D000891710F4C19801C0C19A90913D01809181 +:1016E0003901891710F4C29801C0C29A80913D0156 +:1016F0008F5F80933D01E3CD65E3CE0117D08091EC +:101700006D0090916E0061E3019610D080916D00A4 +:1017100090916E0062E3029609D07CCDE199FECFF4 +:101720009FBB8EBBE09A99278DB30895262FE19930 +:10173000FECF1CBA9FBB8EBB2DBB0FB6F894E29AAE +:0C174000E19A0FBE01960895F894FFCFC7 +:10174C00FF5A0A000A08033500310032002000005D :00000001FF From 507536a109869c8a8a98878cc91a239ec3ec8283 Mon Sep 17 00:00:00 2001 From: Peter Maivald Date: Tue, 31 Oct 2017 21:34:07 +0100 Subject: [PATCH 04/18] use serial number descriptor to identify usbtiny device --- src/dwire/DigiSpark.c | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/src/dwire/DigiSpark.c b/src/dwire/DigiSpark.c index 9aeebe2..1c94288 100644 --- a/src/dwire/DigiSpark.c +++ b/src/dwire/DigiSpark.c @@ -52,7 +52,7 @@ struct UPort { void FindUsbtinys(void) { - int usbtinyindex = 1; + int PortCount0 = PortCount; if (UsbInit()) { usb_find_busses(); @@ -66,11 +66,23 @@ void FindUsbtinys(void) { if ( device->descriptor.idVendor == VENDOR_ID && device->descriptor.idProduct == PRODUCT_ID) { + + int usbtinyindex = 0; + usb_dev_handle* h = usb_open(device); + if (h) { + char serialnumber[] = { 0,0,0,0 }; + if (usb_get_string_simple(h, device->descriptor.iSerialNumber, + serialnumber, sizeof(serialnumber)) >= 0) { + usbtinyindex = atoi(serialnumber); + } + usb_close(h); + } + Assert(PortCount < countof(Ports)); Ports[PortCount] = malloc(sizeof(struct UPort)); Assert(Ports[PortCount]); Ports[PortCount]->kind = 'u'; - Ports[PortCount]->index = usbtinyindex++; + Ports[PortCount]->index = usbtinyindex; Ports[PortCount]->character = -1; // Currently undetermined Ports[PortCount]->baud = 0; // Currently undetermined ((struct UPort*)Ports[PortCount])->handle = 0; // Currently unconnected @@ -84,9 +96,29 @@ void FindUsbtinys(void) { bus = bus->next; } } -} + // 0 or 1 port is always ok + if (PortCount - PortCount0 <= 1) return; + + // if they're all the same, number them sequentially + char same = 1; + for (int i = PortCount0 + 1; i < PortCount; ++i) + same = same && Ports[PortCount0]->index == Ports[i]->index; + if (same) { + for (int i = PortCount0; i < PortCount; ++i) + Ports[i]->index = i - PortCount0 + 1; + return; + } + // for any duplicates, increment subsequent ones by 1000 + for (int i = PortCount0; i < PortCount; ++i) { + int index = Ports[i]->index; + int increment = 0; + for (int j = i + 1; j < PortCount; ++j) + if (Ports[j]->index == index) + Ports[j]->index += (increment += 1000); + } +} void PortFail(struct UPort *up, char *msg) { usb_close(up->handle); @@ -295,4 +327,4 @@ void ConnectUsbtinyPort(struct UPort *up) { Ws("\r \r"); // Ws(" -- ConnectUsbtinyPort complete. "); WriteUPort(up); -} \ No newline at end of file +} From 6641552f082062e661ef7c3d0951a545171caf81 Mon Sep 17 00:00:00 2001 From: Peter Maivald Date: Sun, 5 Nov 2017 00:30:13 +0100 Subject: [PATCH 05/18] fix robustness and check timing --- usbtiny/main.c | 143 ++++++++++++++++--------------- usbtiny/main.hex | 213 ++++++++++++++++++++++++----------------------- 2 files changed, 181 insertions(+), 175 deletions(-) diff --git a/usbtiny/main.c b/usbtiny/main.c index 7392b36..d2c2934 100644 --- a/usbtiny/main.c +++ b/usbtiny/main.c @@ -1563,83 +1563,88 @@ void dwReadBytes() { } /* +In the absense of the Wait for start bit command, interrupts are handled as +compatibly with the usb interrupt as possible. That means that they are +taken care of in less than the 52 cycle spec demanded by the usb interrupt. +And, then the usb interrupt is executed with interrupts disabled as expected. +This makes usb service as robust as possible for dwdebug. + We hook the pin change interrupt to give priority to dw source interrupts. When dw interrupts are enabled, the usb interrupt vector is run with the dw interrupt enabled. This makes it so that we can respond to dw interrupts even in the middle of a usb frame causing that frame to be missed. Also, interrupts are disabled while servicing dw activities, leading to missed frames in those cases as well. - -Interrupts are disabled while executing all dw commands. But, only the wait for -start bit command allows the usb interrupt to be pre-empted. */ __attribute__((naked, used)) void PCINT0_vect(void) { - asm("\n" -" sbis %[pcmsk],%[dwbit] ; not for us if dw interrupt disabled \n" -" rjmp DW_INTR_VECTOR ; leave interrupts disabled for usb \n" -" \n" -" push r21 \n" -" in r21,__SREG__ \n" -" push r21 \n" -" sbic %[pinb],%[dwbit] ; not for us if dw line high \n" -" rjmp dw_pass%= \n" -" \n" -" lds r21,dwBuf ; get dwState right shifted by 4 bits \n" -" tst r21 \n" -" breq dw_det%= \n" -" \n" -" push r22 ; save registers for called functions \n" -" push r23 \n" -" push r24 \n" -" push r25 \n" -" push r26 \n" -" push r27 \n" -" push r30 \n" -" push r31 \n" -" \n" -" asr r21 \n" -" brcs dw_rb%= \n" -" asr r21 \n" -" brcs dw_cw%= \n" -" rjmp dw_res%= \n" -" \n" -"dw_rb%=: ; dwState & 0x10 \n" -" rcall dwReadBytes \n" -" rjmp dw_res%= \n" -"dw_cw%=: ; dwState & 0x20 \n" -" rcall dwCaptureWidths \n" -"; rjmp dw_res%= \n" -" \n" -"dw_res%=: \n" -" pop r31 \n" -" pop r30 \n" -" pop r27 \n" -" pop r26 \n" -" pop r25 \n" -" pop r24 \n" -" pop r23 \n" -" pop r22 ; restore registers \n" -" rjmp dw_pass%= \n" -" \n" -"dw_det%=: \n" -" ldi r21,1 ; no further commands \n" -" sts dwBuf,r21 ; set break detected flag \n" -" cbi %[pcmsk],%[dwbit] ; Disable further dw interrupts \n" -" \n" -"dw_pass%=: ; pass interrupt to usb \n" -" sbis %[pcmsk],%[usbbit] ; call usb interrupt when set \n" -" rjmp dw_done%= ; don't re-enter \n" -" cbi %[pcmsk],%[usbbit] ; clear to indicate in usb interrupt \n" -" ldi r21,0x80 ; enable interrupts to detect start bit and use\n" -" out __SREG__,r21 ; this value for timeout hack in usb interrupt \n" -" rcall DW_INTR_VECTOR ; call usb interrupt with interrupts enabled \n" -" cli ; disable to prevent re-entry from here \n" -" sbi %[pcmsk],%[usbbit] ; set to indicate not in usb interrupt \n" -"dw_done%=: \n" -" pop r21 \n" -" out __SREG__,r21 \n" -" pop r21 \n" -" reti \n" + __asm__ __volatile__ (R"string-literal( + sbis %[pcmsk],%[dwbit] ;? not for us if dw interrupt disabled + rjmp DW_INTR_VECTOR ;2 03 leave interrupts disabled for usb + push r21 ;2 04 + in r21,__SREG__ ;1 05 + push r21 ;2 07 + sbis %[pinb],%[dwbit] ;? break detected when dw line low + rjmp dw_break%= ;2 + ; pass interrupt to usb + sbis %[pcmsk],%[usbbit];? call usb interrupt when set + rjmp dw_done%= ;2 13 don't re-enter + cbi %[pcmsk],%[usbbit];2 15 clear to indicate in usb interrupt + ldi r21,0x80 ;1 16 enable interrupts to detect start bit + out __SREG__,r21 ;1 17 and timeout hack for usb interrupt + rcall DW_INTR_VECTOR ;3 20 call usb interrupt with interrupts enabled + cli ;1 21 disable to prevent re-entry from here + sbi %[pcmsk],%[usbbit];2 23 set to indicate not in usb interrupt + ldi r21,0 ;1 24 + sbis %[pcmsk],%[dwbit] ;? we preempted if dw interrupt now disabled + sts usbRxLen,r21 ;2 27 throw out the frame since we trashed it +dw_done%=: + pop r21 ;2 29 + out __SREG__,r21 ;1 30 + pop r21 ;2 32 + reti ;4 36 + +dw_break%=: + cbi %[pcmsk],%[dwbit] ; Disable further dw interrupts + lds r21,dwBuf ; get dwState right shifted by 4 bits + tst r21 + brne dw_comm%= + ldi r21,1 ; no further commands + sts dwBuf,r21 ; set break detected flag + rjmp dw_done%= +dw_comm%=: ; execute further commands + push r22 ; save registers for called functions + push r23 + push r24 + push r25 + push r26 + push r27 + push r30 + push r31 + + asr r21 + brcs dw_rb%= + asr r21 + brcs dw_cw%= + rjmp dw_res%= + +dw_rb%=: ; dwState & 0x10 + rcall dwReadBytes + rjmp dw_res%= +dw_cw%=: ; dwState & 0x20 + rcall dwCaptureWidths +; rjmp dw_res%= + +dw_res%=: + pop r31 + pop r30 + pop r27 + pop r26 + pop r25 + pop r24 + pop r23 + pop r22 ; restore registers + rjmp dw_done%= ; frame will be invalidated if preempted usb +)string-literal" : : [pcmsk] "I" (_SFR_IO_ADDR(PCMSK)) ,[pinb] "I" (_SFR_IO_ADDR(PINB)) diff --git a/usbtiny/main.hex b/usbtiny/main.hex index fed2d68..242d91f 100644 --- a/usbtiny/main.hex +++ b/usbtiny/main.hex @@ -1,16 +1,16 @@ :1000000046C060C0ACC85EC05DC05CC05BC05AC0CA -:1000100059C058C057C056C055C054C053C037CB44 -:100020005EC9AAC9C8C9EDC918CA2BCA42CA81CAC1 -:1000300082CA8ACAA1CA29CB28CB27CB26CB25CBFB -:10004000D1CA23CB22CBDECA0902190001010080EC +:1000100059C058C057C056C055C054C053C03CCB3F +:1000200063C9AFC9CDC9F2C91DCA30CA47CA86CA99 +:1000300087CA8FCAA6CA2ECB2DCB2CCB2BCB2ACBD3 +:10004000D6CA28CB27CBE3CA0902190001010080D8 :10005000640904000001FF00000007058103080097 :1000600064120110010000000881179F0C040100B8 :100070000203011603550053004200740069006E2C :1000800000790053005000490004030904001124C2 :100090001FBECFE5D2E0DEBFCDBF10E0A0E6B0E0EE -:1000A000ECE4F7E102C005900D92A037B107D9F753 +:1000A000E6E5F7E102C005900D92A037B107D9F758 :1000B00022E0A0E7B0E001C01D92AD31B207E1F748 -:1000C00082D842CB9DCFA82FB92F80E090E041E0AD +:1000C00087D847CB9DCFA82FB92F80E090E041E0A3 :1000D00050EA609530E009C02D9182279795879569 :1000E00010F084279527305EC8F36F5FA8F308955A :1000F000EADF8D939D930895A6E088279927AA9516 @@ -193,9 +193,9 @@ :100C00008D81EE5FF0E0EC58FF4F80838A8184FF96 :100C10008CC080917200882309F487C081E18093A1 :100C2000340187B3842B87BB409373007EC0273386 -:100C3000A1F480916D0090916E0078D580916D0047 -:100C400090916E006B81019671D580916D009091AD -:100C50006E006C8102966AD568C02C3341F5809194 +:100C3000A1F480916D0090916E007DD580916D0042 +:100C400090916E006B81019676D580916D009091A8 +:100C50006E006C8102966FD568C02C3341F580918F :100C60008A01811162C0888187FF09C084E991E00F :100C700090936C0180936B019091890157C06093B0 :100C80008A018E818093890190918901911104C01C @@ -217,7 +217,7 @@ :100D8000BBD98453994097FF03C0919581959109F0 :100D90008E159F0514F4C1B77C0181B78F5F81BFA9 :100DA000EACFC1BF61B780E090E0DF91CF911F91A2 -:100DB0000F91FF90EF90DF90B9C4CF93DF93D82FBE +:100DB0000F91FF90EF90DF90BEC4CF93DF93D82FB9 :100DC000882311F0B89801C0B89AC0E02C2F30E009 :100DD00080914001909141012817390728F481E062 :100DE00090E086DCCF5FF2CFBA98C0E02C2F30E0E5 @@ -276,100 +276,101 @@ :1011300058E0FC013197F1F76695B299606800C0FC :101140005A95B9F76D937395EE27FF27319721F0E4 :10115000B29BFCCF7038F0F2709389010895AA9B7E -:10116000DEC75F935FB75F93B29921C0509194013E -:101170005523C9F06F937F938F939F93AF93BF9342 -:10118000EF93FF93559518F0559518F003C0BBDF0A -:1011900001C074DFFF91EF91BF91AF919F918F914B -:1011A0007F916F9104C051E050939401AA98AC9B39 -:1011B00006C0AC9850E85FBFB2D7F894AC9A5F9184 -:1011C0005FBF5F91189580E287BB88BB80E090E0AD -:1011D000A5D28F3F09F081BFC0916D00D0916E0004 -:1011E000CE019CD2E82ECE01019698D2F82ECE01E7 -:1011F000029694D2982F80ED8E0D8A3008F07CC232 -:1012000080ED8F0D8A3008F077C280ED890F8A302B -:1012100008F072C2C0916D00D0916E00CE017ED2F6 -:1012200090E09093680080936700CE01019676D29B -:1012300090E090936A0080936900CE0102966ED28E -:1012400090E090936C0080936B00BB9AC4E18FE0B8 -:1012500090E05BDAC150D9F7BB9885E090E09093BD -:1012600041018093400110926300109262009EE061 -:1012700088E10FB6F894A89581BD0FBE91BD10927C -:101280003B0110923A0110923901109238011092EC -:1012900037011092360110923D0110923401CED9DF -:1012A0007894B898C098B998C198BA98C298BD98DF -:1012B000C598DAE130E4F32EC1E04BE1E42E20E8FA -:1012C000D22EA8958AD8E09134018E2F90E08531F6 -:1012D000910508F0DAC1FC01E15FFF4F0994B89A6B -:1012E000B998BA9AC29A10928B0180911402809395 -:1012F0003F01D0923E0180913E01882309F458C0FD -:10130000C098B12C8B2D90E0209162003091630049 -:101310008217930728F481E090E0EAD9B394F2CFE2 -:1013200090913E0180913F01892309F0C09AC298B3 -:1013300080918B01880F80938B0196B320918B0154 -:1013400091FB882780F9820F80938B01B12C8B2D24 -:1013500090E020916200309163008217930728F497 -:1013600081E090E0C5D9B394F2CFC29A80913E015A -:10137000869580933E01BFCFBA9AC29880EE91E0E5 -:10138000B7D9BFB6F894C29A86E490E0B1D9BA98BA -:1013900086B382FB002700F910E0C80121E0822714 -:1013A00080938B01BFBE8AE991E0A2D9BA9AC29A12 -:1013B000C09393016AC18091140280933E01BA9A4E -:1013C00098E0B92EAFB6F894C29880913E0180FFA4 -:1013D00007C086E090E08CD9C29A80E490E006C015 -:1013E0008CE390E085D9C29A8AE090E081D980911F -:1013F0003E01869580933E01AFBEBA94B110E2CF14 -:1014000044C110928B0188E0B82E80918B018695A3 -:1014100080938B01BA9AAFB6F894C29886E090E0B8 -:1014200067D9C29A8AE090E063D9BA9886B382FB02 -:10143000882780F980933E01AFBE87E390E058D9BA -:1014400080913E01882329F080918B0180688093F0 -:101450008B01BA94B110D9CFABCFBA9ABFB6F8947A -:10146000C29886E090E044D9C29A8AE090E040D9E0 -:10147000BA9886B382FB882780F980938B01BFBE20 -:1014800097CFBA9ABFB6F894C2988091140280FFA1 -:1014900007C086E090E02CD9C29A80E490E006C0B4 -:1014A0008CE390E025D9C29A8AE090E021D9BFBEB2 -:1014B000ECC0B99AB898BA9AC298E0923F01DDB9E7 -:1014C000809114028111C59810923E01E0913E0175 -:1014D00080911502E81730F5F0E0EA5EFD4F80815B -:1014E0008FB9FEB880913F018DB9B12C8B2D90E062 -:1014F00020916200309163008217930728F481E005 -:1015000090E0F6D8B394F2CF769BECCF80913E0179 -:10151000E82FF0E09FB1E557FE4F90838F5F8093F7 -:101520003E01D4CF809114028111C59A80913E0171 -:1015300056C0D5DCAAC080E090E0E8DC80911402BF -:101540005DDD80933E0115C0B12C80911402B81668 -:1015500050F4EB2DF0E0EA5EFD4F80814FDD80938B -:101560003E01B394F2CF80911502811104DD809188 -:101570003E0180938B011CCF80911402811102C027 -:10158000B12C19C0912C292D30E08091150290E0EA -:101590000197A90145575E4F5A012817390734F4BE -:1015A00081E039DDF50180839394EDCF80E033DD78 -:1015B000F50180830EC080911502B81650F48B2C73 -:1015C000912C81E028DDF401E557FE4F8083B39430 -:1015D000F2CF809116028111CEDC809115028093AA -:1015E000930153C081E090E090D8BFB6F894609129 -:1015F000720070E04091730084E790E02DD9109262 -:10160000720055CF80918A01847319F082E090E0D6 -:101610007CD880918A0180FF05C0C298BA9A84E67E -:1016200090E073D880918A0181FF08C08091940175 -:1016300080938701809195018093880180918A0130 -:1016400082FD3BDD80918A0183FF11C080918A0178 -:1016500082958F70809394019091940181E0911113 -:1016600080E08093890110928A01BA98AA9A8091A9 -:101670008A0184FD48DD80918A0185FDFFDC10929E -:10168000340110928A01789402C010923401809142 -:101690003C01882309F415CE80913D0181110DC0D4 -:1016A0008091380180933B018091370180933A010A -:1016B000809136018093390119C090913B019817B0 -:1016C00010F4C09801C0C09A90913D0180913A01F8 -:1016D000891710F4C19801C0C19A90913D01809181 -:1016E0003901891710F4C29801C0C29A80913D0156 -:1016F0008F5F80933D01E3CD65E3CE0117D08091EC -:101700006D0090916E0061E3019610D080916D00A4 -:1017100090916E0062E3029609D07CCDE199FECFF4 -:101720009FBB8EBBE09A99278DB30895262FE19930 -:10173000FECF1CBA9FBB8EBB2DBB0FB6F894E29AAE -:0C174000E19A0FBE01960895F894FFCFC7 -:10174C00FF5A0A000A08033500310032002000005D +:10116000DEC75F935FB75F93B29B10C0AC9B0AC0B2 +:10117000AC9850E85FBFD3D7F894AC9A50E0AA9BE4 +:1011800050936D015F915FBF5F911895AA98509140 +:101190009401552321F451E050939401F3CF6F93C0 +:1011A0007F938F939F93AF93BF93EF93FF93559547 +:1011B00018F0559518F003C0A6DF01C05FDFFF915E +:1011C000EF91BF91AF919F918F917F916F91DACF06 +:1011D00080E287BB88BB80E090E0A5D28F3F09F01A +:1011E00081BFC0916D00D0916E00CE019CD2E82EDF +:1011F000CE01019698D2F82ECE01029694D2982F65 +:1012000080ED8E0D8A3008F07CC280ED8F0D8A3023 +:1012100008F077C280ED890F8A3008F072C2C09161 +:101220006D00D0916E00CE017ED290E09093680068 +:1012300080936700CE01019676D290E090936A0089 +:1012400080936900CE0102966ED290E090936C007C +:1012500080936B00BB9AC4E18FE090E056DAC150F6 +:10126000D9F7BB9885E090E09093410180934001CD +:1012700010926300109262009EE088E10FB6F8942D +:10128000A89581BD0FBE91BD10923B0110923A010D +:1012900010923901109238011092370110923601E4 +:1012A00010923D0110923401C9D97894B898C09831 +:1012B000B998C198BA98C298BD98C598DAE130E457 +:1012C000F32EC1E04BE1E42E20E8D22EA89585D87C +:1012D000E09134018E2F90E08531910508F0DAC15C +:1012E000FC01E15FFF4F0994B89AB998BA9AC29A83 +:1012F00010928B018091140280933F01D0923E01A5 +:1013000080913E01882309F458C0C098B12C8B2DE0 +:1013100090E020916200309163008217930728F4D7 +:1013200081E090E0E5D9B394F2CF90913E018091B5 +:101330003F01892309F0C09AC29880918B01880FE0 +:1013400080938B0196B320918B0191FB882780F9C4 +:10135000820F80938B01B12C8B2D90E02091620045 +:10136000309163008217930728F481E090E0C0D9A0 +:10137000B394F2CFC29A80913E01869580933E014C +:10138000BFCFBA9AC29880EE91E0B2D9BFB6F894B6 +:10139000C29A86E490E0ACD9BA9886B382FB002763 +:1013A00000F910E0C80121E0822780938B01BFBEC5 +:1013B0008AE991E09DD9BA9AC29AC09393016AC111 +:1013C0008091140280933E01BA9A98E0B92EAFB68C +:1013D000F894C29880913E0180FF07C086E090E0BB +:1013E00087D9C29A80E490E006C08CE390E080D96F +:1013F000C29A8AE090E07CD980913E0186958093E4 +:101400003E01AFBEBA94B110E2CF44C110928B013D +:1014100088E0B82E80918B01869580938B01BA9AD3 +:10142000AFB6F894C29886E090E062D9C29A8AE09A +:1014300090E05ED9BA9886B382FB882780F98093C2 +:101440003E01AFBE87E390E053D980913E018823EF +:1014500029F080918B01806880938B01BA94B11040 +:10146000D9CFABCFBA9ABFB6F894C29886E090E0D5 +:101470003FD9C29A8AE090E03BD9BA9886B382FB02 +:10148000882780F980938B01BFBE97CFBA9ABFB6E9 +:10149000F894C2988091140280FF07C086E090E023 +:1014A00027D9C29A80E490E006C08CE390E020D96E +:1014B000C29A8AE090E01CD9BFBEECC0B99AB89835 +:1014C000BA9AC298E0923F01DDB98091140281116D +:1014D000C59810923E01E0913E0180911502E817F7 +:1014E00030F5F0E0EA5EFD4F80818FB9FEB8809163 +:1014F0003F018DB9B12C8B2D90E02091620030918D +:1015000063008217930728F481E090E0F1D8B39448 +:10151000F2CF769BECCF80913E01E82FF0E09FB1B7 +:10152000E557FE4F90838F5F80933E01D4CF80912B +:1015300014028111C59A80913E0156C0D0DCAAC028 +:1015400080E090E0E3DC8091140258DD80933E015E +:1015500015C0B12C80911402B81650F4EB2DF0E0B8 +:10156000EA5EFD4F80814ADD80933E01B394F2CF65 +:10157000809115028111FFDC80913E0180938B01E7 +:101580001CCF80911402811102C0B12C19C0912C82 +:10159000292D30E08091150290E00197A90145576F +:1015A0005E4F5A012817390734F481E034DDF50124 +:1015B00080839394EDCF80E02EDDF50180830EC013 +:1015C00080911502B81650F48B2C912C81E023DD0C +:1015D000F401E557FE4F8083B394F2CF8091160259 +:1015E0008111C9DC809115028093930153C081E081 +:1015F00090E08BD8BFB6F8946091720070E0409193 +:10160000730084E790E028D91092720055CF809142 +:101610008A01847319F082E090E077D880918A0182 +:1016200080FF05C0C298BA9A84E690E06ED8809197 +:101630008A0181FF08C08091940180938701809185 +:1016400095018093880180918A0182FD36DD809129 +:101650008A0183FF11C080918A0182958F708093E7 +:1016600094019091940181E0911180E0809389012F +:1016700010928A01BA98AA9A80918A0184FD43DD6A +:1016800080918A0185FDFADC1092340110928A0162 +:10169000789402C01092340180913C01882309F4AF +:1016A00015CE80913D0181110DC08091380180934C +:1016B0003B018091370180933A01809136018093FC +:1016C000390119C090913B01981710F4C09801C0DE +:1016D000C09A90913D0180913A01891710F4C19808 +:1016E00001C0C19A90913D0180913901891710F490 +:1016F000C29801C0C29A80913D018F5F80933D01E5 +:10170000E3CD65E3CE0117D080916D0090916E001E +:1017100061E3019610D080916D0090916E0062E3BC +:10172000029609D07CCDE199FECF9FBB8EBBE09A9B +:1017300099278DB30895262FE199FECF1CBA9FBB40 +:101740008EBB2DBB0FB6F894E29AE19A0FBE0196BC +:061750000895F894FFCF9C +:10175600FF5A0A000A080335003100320020000053 :00000001FF From 415b8f1a53c973c3efe85d6fe7f026f925f7830f Mon Sep 17 00:00:00 2001 From: Peter Maivald Date: Sun, 5 Nov 2017 10:48:21 +0100 Subject: [PATCH 06/18] uart use example --- example/hellodw/Makefile | 32 +++++++++++++ example/hellodw/hellodw.c | 99 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 example/hellodw/Makefile create mode 100644 example/hellodw/hellodw.c diff --git a/example/hellodw/Makefile b/example/hellodw/Makefile new file mode 100644 index 0000000..28215f5 --- /dev/null +++ b/example/hellodw/Makefile @@ -0,0 +1,32 @@ + +DEVICE = attiny84 +F_CPU = 8000000 + +CFLAGS = +CFLAGS += -Wno-deprecated-declarations +CFLAGS += -Wl,--gc-sections +CFLAGS += -fdata-sections -ffunction-sections +COMPILE = avr-gcc -Wall -Os --std=gnu99 -DF_CPU=$(F_CPU) $(CFLAGS) -mmcu=$(DEVICE) + +BASENAME = hellodw +OBJECTS = $(BASENAME).o + +all: $(BASENAME).elf + +dwload: + dwdebug l $(BASENAME).elf ,qr + +clean: + rm -f *.elf *.map *.o *.i *.lss + +.c.o: + $(COMPILE) -c $< -o $@ + +%.i: %.c + $(COMPILE) -E -c $< -o $@ + +$(BASENAME).elf: $(BASENAME).o + $(COMPILE) -Wl,-Map,$(BASENAME).map -o $(BASENAME).elf $(BASENAME).o + +disasm: $(BASENAME).elf + avr-objdump -d $(BASENAME).elf > $(BASENAME).lss diff --git a/example/hellodw/hellodw.c b/example/hellodw/hellodw.c new file mode 100644 index 0000000..8711492 --- /dev/null +++ b/example/hellodw/hellodw.c @@ -0,0 +1,99 @@ + + +#include +#include +#include +#include + +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny13__) +#define DW_DDR DDRB +#define DW_PORT PORTB +#define DW_BIT 5 +#elif defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) +#define DW_DDR DDRB +#define DW_PORT PORTB +#define DW_BIT 3 +#else // defined(__AVR_*) +#error DW ports +#endif // defined(__AVR_*) + +void RpcDw_begin() { + DW_DDR &=~ (1<= 16 and multiple of 4 + divisor = (divisor - 12) >> 2; + + // c is passed in r24, with r25 allows adiw instruction + register uint8_t c asm ("r24") = _c; + register uint8_t ch asm ("r25"); + uint8_t sreg; + __asm__ __volatile__ (R"string-literal( + + ; ch:c = 0x200 | (uint16_t(c) << 1); + ldi %[ch],0x02 + add %[c],%[c] + adc %[ch],__zero_reg__ + + ; uint8_t sreg = SREG; cli(); + in %[sreg],__SREG__ + cli + + ; shift right and output lsb +loop%=: ;0. 00 + lsr %[ch] ;1. 01 + ror %[c] ;1. 02 + brcc set_low%= ;12 03 +set_high%=: ;0. 03 + cbi %[ddr],%[bit] ;2. 05 ;input + sbi %[port],%[bit];2. 07 ;pullup + rjmp set_done%= ;.2 09 +set_low%=: ;0. 04 + cbi %[port],%[bit];2. 06 ;low + sbi %[ddr],%[bit] ;2. 08 ;output + nop ;1. 09 +set_done%=: ;0. 09 + + ; loop until all bits sent + adiw %[c],0 ;2. 11 + breq all_done%= ;12 12 + + ; delay = 4*count + mov __tmp_reg__,%[count];1. +delay%=: + dec __tmp_reg__ ;1. + breq loop%= ;12 + rjmp delay%= ;.2 + + ; SREG = sreg; +all_done%=: + out __SREG__,%[sreg] +)string-literal" + : [sreg] "=&r" (sreg) + ,[c] "+w" (c), [ch] "+w" (ch) + : [ddr] "I" (_SFR_IO_ADDR(DW_DDR)) + ,[port] "I" (_SFR_IO_ADDR(DW_PORT)) + ,[bit] "I" (DW_BIT) + ,[count] "r" (divisor) + ); +} + +void RpcDw_puts(const char* s) { + while (*s) { + RpcDw_write(*s++, 128); + _delay_us(20); + } +} + +int main(void) { + RpcDw_begin(); + char str[] = "hellodw 0\n"; + for (;;) { + RpcDw_puts(str); + if (++str[8] > '9') + str[8] = '0'; + _delay_ms(1000); + } +} From e358a7853ade70879565cdf65023a524d605eb3e Mon Sep 17 00:00:00 2001 From: Peter Maivald Date: Sun, 5 Nov 2017 11:12:03 +0100 Subject: [PATCH 07/18] usbmon monitoring utility --- utility/usbmon/command.cpp | 173 ++++++++++++++++ utility/usbmon/command.hpp | 15 ++ utility/usbmon/hostusb.cpp | 405 +++++++++++++++++++++++++++++++++++++ utility/usbmon/hostusb.hpp | 71 +++++++ utility/usbmon/makefile | 51 +++++ utility/usbmon/rpc.hpp | 18 ++ utility/usbmon/usbmon.cpp | 93 +++++++++ utility/usbmon/usbmon.hpp | 11 + 8 files changed, 837 insertions(+) create mode 100644 utility/usbmon/command.cpp create mode 100644 utility/usbmon/command.hpp create mode 100644 utility/usbmon/hostusb.cpp create mode 100644 utility/usbmon/hostusb.hpp create mode 100644 utility/usbmon/makefile create mode 100644 utility/usbmon/rpc.hpp create mode 100644 utility/usbmon/usbmon.cpp create mode 100644 utility/usbmon/usbmon.hpp diff --git a/utility/usbmon/command.cpp b/utility/usbmon/command.cpp new file mode 100644 index 0000000..f963661 --- /dev/null +++ b/utility/usbmon/command.cpp @@ -0,0 +1,173 @@ +#include "command.hpp" +#include "usbmon.hpp" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +void Debug(const char* f, ...) +{ + va_list a; va_start(a, f); + vfprintf(stderr, f, a); + va_end(a); +} + +void SysAbort(const char* f, ...) +{ + if (f) + { + fprintf(stderr, "SysAbort: "); + va_list a; va_start(a, f); + vfprintf(stderr, f, a); + va_end(a); + fprintf(stderr, " "); + } + abort(); +} + +void SysAbort_unhandled() +{ + std::cerr << "SysAbort: "; + std::exception_ptr ep = std::current_exception(); + try { + std::rethrow_exception(ep); + } catch (std::exception& ex) { + const char* error = ex.what(); + std::cerr << (error ? error : "std::exception unhandled"); + } catch (...) { + std::cerr << "... unhandled"; + } + std::cerr << '\n'; + abort(); +} + +int SysConWrite(const char* s) +{ + size_t l = strlen(s); + return write(STDOUT_FILENO, s, l) == l; +} + +int SysConAvail() +{ + timeval tv = { 0, 0 }; + fd_set fds; FD_ZERO(&fds); + FD_SET(STDIN_FILENO, &fds); + return select(STDIN_FILENO+1, &fds, NULL, NULL, &tv); +} + +int SysConRead() +{ + unsigned char c; + int r = read(STDIN_FILENO, &c, sizeof(c)); + return r <= 0 ? 0 : (unsigned int)c; +} + +char* serialnumber = nullptr; +static termios ttystate; +void reset_terminal_mode() { tcsetattr(STDIN_FILENO, TCSANOW, &ttystate); } + +void args(int argc, char** argv) { + // true to print optopt errors to stderr, getopt returns '?' in either case + opterr = true; + struct usage { }; + try { + enum { + lo_flag, + lo_change_serialnumber, + }; + static struct option options[] = { + {"change-serialnumber", required_argument, 0, lo_change_serialnumber}, + {nullptr, 0, nullptr, 0} + }; + for (int c, i; (c = getopt_long(argc, argv, "s:rpdut", options, &i)) != -1; ) { + // optopt gets option character for unknown option or missing required argument + // optind gets index of the next element of argv to be processed + // optarg points to value of option's argument + switch (c) { + default: + throw usage(); + + case 's': + serialnumber = strdup(optarg); + break; + + case lo_change_serialnumber: + change_serialnumber(optarg); + break; + + case 'r': + reset(); + break; + + case 'p': + power(); + break; + + case 'd': + monitor_dw(); + break; + + case 'u': + monitor(); + break; + + case 't': + test(); + break; + + } + } + if (optind < argc) { + + // process positional arguments + std::cerr << argv[0] << ": invalid argument "; + for (int i = optind; i < argc; ++i) + std::cerr << argv[i] << ' '; + std::cerr << std::endl; + exit(3); + } + } catch (usage) { + std::cerr << R"00here-text00(usage: rpcusbmon [OPTION]... + -s # require serial number + -r reset (requires usb) + -p power cycle (requires dw) + -d dw monitor + -u usb monitor + + --change-serialnumber # change to new serial number (requires dw) +)00here-text00"; + exit(3); + } +} + +int main(int argc, char *argv[]) { + // make unbuffered + setbuf(stdout, nullptr); + setbuf(stderr, nullptr); + +//http://stackoverflow.com/questions/12625224/can-someone-give-me-an-example-of-how-select-is-alerted-to-an-fd-becoming-rea +//http://stackoverflow.com/questions/448944/c-non-blocking-keyboard-input +//http://cc.byexamples.com/2007/04/08/non-blocking-user-input-in-loop-without-ncurses/ + + // get console settings + tcgetattr(STDIN_FILENO, &ttystate); + atexit(reset_terminal_mode); +{ + // set console noblock and noecho modes + termios ttystate_new = ttystate; + ttystate_new.c_lflag &= ~(ICANON | ECHO); + ttystate.c_cc[VMIN] = 1; ttystate.c_cc[VTIME] = 0; + tcsetattr(STDIN_FILENO, TCSANOW, &ttystate_new); +} + // process arguments + args(argc, argv); + + return 0; +} diff --git a/utility/usbmon/command.hpp b/utility/usbmon/command.hpp new file mode 100644 index 0000000..4bd67ff --- /dev/null +++ b/utility/usbmon/command.hpp @@ -0,0 +1,15 @@ +#ifndef command_hpp +#define command_hpp + +void SysAbort(const char* e, ...); +void Debug(const char* format, ...); +int SysConWrite(const char* s); +int SysConAvail(); +int SysConRead(); + +extern char* serialnumber; + +#define RunInit(module) struct _##module##_Init{_##module##_Init();}__##module##_Init;_##module##_Init::_##module##_Init() +#define RunExit(module) struct _##module##_Exit{~_##module##_Exit();}__##module##_Exit;_##module##_Exit::~_##module##_Exit() + +#endif diff --git a/utility/usbmon/hostusb.cpp b/utility/usbmon/hostusb.cpp new file mode 100644 index 0000000..6cff8ad --- /dev/null +++ b/utility/usbmon/hostusb.cpp @@ -0,0 +1,405 @@ +#include "command.hpp" +#include "rpc.hpp" +#include "hostusb.hpp" + +#if __GNUC__ > 4 +#include +#endif +#include +#include +#include +#include +#include // apt-get install libusb-1.0-0-dev +#include +#include +#include +#include + +static struct libusb_context* lu_ctx = nullptr; +RunInit(libusb) { if (libusb_init(&lu_ctx) < 0) SysAbort(__PRETTY_FUNCTION__); } +RunExit(libusb) { libusb_exit(lu_ctx); } + +LuXact::LuXact(uint32_t vidpid, const char16_t vendor[], const char16_t product[], const char16_t serial[]) + : id_int(vidpid) +{ + size_t vl = vendor ? std::char_traits::length(vendor) : 0; + size_t pl = product ? std::char_traits::length(product) : 0; + size_t sl = serial ? std::char_traits::length(serial) : 0; + + id_string = new char16_t[vl + 1 + pl + 1 + sl + 1]; + char16_t blank[1] = { 0 }; + std::char_traits::copy(id_string, vl ? vendor : blank, vl + 1); + std::char_traits::copy(id_string + vl + 1, pl ? product : blank, pl + 1); + std::char_traits::copy(id_string + vl + 1 + pl + 1, sl ? serial : blank, sl + 1); + + Label(); +} + +LuXact::~LuXact() +{ + Close(); + delete id_string; +} + +void LuXact::Label() +{ + // only output label to tty + if (!isatty(fileno(stdout)) || !isatty(fileno(stderr))) return; + + std::cerr << "\e]0;"; + if (!lu_dev) std::cerr << '['; +#if __GNUC__ > 4 + // convert utf16 to utf8 + std::wstring_convert,char16_t> convert; +#else + // this only handles ASCII + struct { std::string (*to_bytes)(const char16_t* p); } convert; + convert.to_bytes = [&](const char16_t* p)->std::string { + std::string r; + for (; *p; ++p) + r += (char)(*p & 0xFF); + return r; }; +#endif + char16_t* p = id_string; + std::cerr << convert.to_bytes(p); + p += std::char_traits::length(p) + 1; + std::cerr << ' ' << convert.to_bytes(p); + p += std::char_traits::length(p) + 1; + if (p[0]) + { + std::cerr << ' '; + if (p[1]) + std::cerr << convert.to_bytes(p); + else { + std::ios::fmtflags fmtfl = std::cerr.setf(std::ios::hex, std::ios::basefield); + std::cerr << "0x" << (uint16_t)*p; + std::cerr.setf(fmtfl, std::ios::basefield); + } + } + if (!lu_dev) std::cerr << ']'; + std::cerr << '\a'; +} + +bool LuXact::Open(bool claim) +{ + libusb_device* *devs; + ssize_t cnt = libusb_get_device_list(lu_ctx, &devs); + if (cnt < 0) SysAbort(__PRETTY_FUNCTION__); + + for (ssize_t i = 0; i < cnt; i++) + { + libusb_device* dev = devs[i]; + + libusb_device_descriptor desc; + if (libusb_get_device_descriptor(dev, &desc) < 0) + SysAbort(__PRETTY_FUNCTION__); + + // require vidpid to match + if ((id_int >> 16) != desc.idVendor || (id_int & 0xFFFF) != desc.idProduct) + continue; + + // open with automatic handle + struct _handle { ~_handle() { if (h) libusb_close(h); } + operator libusb_device_handle*() { return h; } + libusb_device_handle* h = nullptr; } handle; + if (libusb_open(dev, &handle.h) != 0) continue; + + const size_t buffer_size = 256; + std::unique_ptr buffer(new char16_t[3 * buffer_size]); + char16_t* p = buffer.get(); +{ + // read Manufacturer Product SerialNumber + auto desc_get = [&](uint8_t index)->bool { + int r = libusb_get_string_descriptor(handle, index, 0x0409, (unsigned char*)p, buffer_size); + if (r < 0 || r >= buffer_size || (r & 1) || *p != (0x300 | r)) + return false; + std::char_traits::move(p, p + 1, r - sizeof(char16_t)); + p[r / sizeof(char16_t) - 1] = '\0'; + p += r / sizeof(char16_t); + return true; + }; + // require no errors + if (!desc_get(desc.iManufacturer) || !desc_get(desc.iProduct) || !desc_get(desc.iSerialNumber)) + continue; +} +{ + // check for a match where wild cards have l == 0 + char16_t* s = id_string; p = buffer.get(); + auto desc_eq = [&]()->bool { + size_t ls = std::char_traits::length(s); + size_t lp = std::char_traits::length(p); + bool r = ls == 0 || ls == lp && std::char_traits::compare(s, p, lp) == 0; + s += ls + 1; p += lp + 1; + return r; + }; + // require a match + if (!desc_eq() || !desc_eq() || !desc_eq()) + continue; + + // don't copy on exact match + if (s == p) + p = nullptr; +} +if (claim) { + // claim interface must succeed in order to own device + if (libusb_kernel_driver_active(handle, 0) == 1 + && libusb_detach_kernel_driver(handle, 0) != 0 + || libusb_claim_interface(handle, 0) != 0) + continue; +} + // if there were wild cards, store the string read + if (p) + { + id_string = (char16_t*)realloc(id_string, (p - buffer.get()) * sizeof(char16_t)); + if (!id_string) SysAbort(__PRETTY_FUNCTION__); + std::char_traits::copy(id_string, buffer.get(), p - buffer.get()); + } + + lu_dev = handle; + handle.h = nullptr; + break; + } + libusb_free_device_list(devs, 1); + + if (!lu_dev) return false; + Label(); + return true; +} + +bool LuXact::Close() +{ + if (!lu_dev) return false; + bool ret = true; + + if (libusb_release_interface(lu_dev, 0) != 0) + ret = false; + libusb_close(lu_dev); + lu_dev = nullptr; + + Label(); + return ret; +} + +int LuXact::Xfer(uint8_t req, uint32_t arg, char* data, uint8_t size, bool device_to_host) { + return libusb_control_transfer + (/* libusb_device_handle* */lu_dev, + /* bmRequestType */ LIBUSB_REQUEST_TYPE_CLASS + | (device_to_host ? LIBUSB_ENDPOINT_IN : LIBUSB_ENDPOINT_OUT), + /* bRequest */ req, + /* wValue */ (uint16_t)arg, + /* wIndex */ (uint16_t)(arg >> 16), + /* data */ (unsigned char*)data, + /* wLength */ size, + timeout); +} + +uint8_t LuXact::XferRetry(uint8_t req, uint32_t arg, char* data, uint8_t size, bool device_to_host) +{ + for (;;) { + if (!lu_dev) { + usleep(100 * 1000); + if (!Open()) + continue; + } + int xfer = Xfer(req, arg, data, size, device_to_host); + if (xfer >= 0 && xfer <= size) + return xfer; + Close(); + } +} + +LuXactUsb::LuXactUsb(const char* serialnumber) + : LuXact(0x16c005df, nullptr, nullptr, Serial(serialnumber)) { +} + +char16_t* LuXactUsb::Serial(const char* serialnumber) { + serial[0] = serialnumber ? strtol(serialnumber, nullptr, 0) : 0; + serial[1] = 0; + return serial; +} + +void LuXactUsb::Reset() +{ + if (!lu_dev) + return; + Xfer(0xFF, 0); + Close(); +} + +void LuXactUsb::Send(char data) +{ + try { + XferRetry(0, (uint8_t)data); + } catch(...) { } +} + +void LuXactUsb::Send(struct Rpc& rpc, uint8_t index) { + uint8_t size = rpc.Size(index); + uint32_t arg; memcpy(&arg, (char*)&rpc, sizeof(arg) < size ? sizeof(arg) : size); + size = sizeof(arg) < size ? size - sizeof(arg) : 0; + uint8_t xfer = XferRetry(index, arg, (char*)&rpc + sizeof(arg), size, false); + if (xfer != size) + throw Exception(__PRETTY_FUNCTION__); +} + +void LuXactUsb::Recv() { + for (;;) { + char data[RpcPayloadMax+1]; + uint8_t xfer = XferRetry(1, 0, data, sizeof(data), true); + if (xfer == 0) + break; + if (xfer == 1 || data[xfer-1] == 0) { + data[1] = '\0'; + if (sink) + sink(data); + return; + } + if (data[xfer-1] >= RpcNumber) + throw Exception(__PRETTY_FUNCTION__); + ((Rpc*)data)->VtAct(data[xfer-1]); + } +} + +LuXactDw::LuXactDw(const char* serialnumber) + : LuXact(0x17810c9f, nullptr, nullptr, Serial(serialnumber)) { +} + +char16_t* LuXactDw::Serial(const char* serialnumber) { + serial[0] = 0; + if (!serialnumber) return nullptr; + int sn = strtol(serialnumber, nullptr, 0); + if (sn < 0 || sn > 999) sn = 0; + std::string str = std::to_string(sn); + size_t len = str.length(); + if (len < 3) str.insert(0, 3 - len, '0'); + const char* p = str.c_str(); + serial[0] = *p++; + serial[1] = *p++; + serial[2] = *p++; + serial[3] = 0; + return serial; +} + +void LuXactDw::Reset() { +} + +void LuXactDw::Send(char data) { + // send bytes + Xfer(60/*dw*/, 0x04, &data, sizeof(data), false); +} + +void LuXactDw::Send(struct Rpc& rpc, uint8_t index) { +} + +void LuXactDw::Recv() { + // libusb calls taken from dwire/DigiSpark.c + + // Read back dWIRE bytes + char data[129]; + int status = Xfer(60/*dw*/, 0, data, sizeof(data)-1, true); + // 0 means no data, PIPE means garbled command + //static int last = 50000; if (status || last != status) std::cerr << "Recv status:" << status << '\n'; last = status; + if (status == 0 || status == LIBUSB_ERROR_TIMEOUT || status == LIBUSB_ERROR_PIPE) + return; + if (status == LIBUSB_ERROR_NO_DEVICE) { +#if 1 + XferRetry(0/*echo*/, 0); +#else + libusb_device* dev = libusb_get_device(lu_dev); +#endif + return; + } + if (status > 0 && sink) { + data[status] = '\0'; +#if 0 + std::cerr << '['; + std::ios::fmtflags fmtfl = std::cerr.setf(std::ios::hex, std::ios::basefield); + for (int i = 0 ; i < status; ++i) std::cerr << ((int)data[i]&0xFF) << ' '; + std::cerr.setf(fmtfl, std::ios::basefield); + std::cerr << ']'; +#endif + sink(data); + } + + if (status > 0) + ReadDw(); +} + +bool LuXactDw::ReadDw() +{ + // Wait for start bit and Read bytes + XferRetry(60/*dw*/, 0x18); + return true; +} + +bool LuXactDw::ResetDw() { + // libusb calls taken from dwire/DigiSpark.c + + // DigisparkBreakAndSync() + // Send break and read pulse widths + if (Xfer(60/*dw*/, 0x21) < 0) + return false; + usleep(120 * 1000); + + // SetDwireBaud() + uint16_t dwBitTime; + for (uint8_t tries = 0; ; ++tries) { + usleep(20 * 1000); + uint16_t times[64]; + // Read back timings + int status = Xfer(60/*dw*/, 0, (char*)times, sizeof(times), true); + if (status <= 0 && tries < 5) continue; + if (status < 18) return false; + + // Average measurements and determine baud rate as pulse time in device cycles. + uint32_t cyclesperpulse = 0; + int measurementCount = status / 2; + for (int i = measurementCount-9; i < measurementCount; i++) cyclesperpulse += times[i]; + + // Pulse cycle time for each measurement is 6*measurement + 8 cyclesperpulse. + cyclesperpulse = (6*cyclesperpulse) / 9 + 8; + + // Determine timing loop iteration counts for sending and receiving bytes + times[0] = (cyclesperpulse-8)/4; // dwBitTime + std::cerr << "baud: " << 16500000 / cyclesperpulse << std::endl; + + // Set timing parameter + if (Xfer(60/*dw*/, 0x02, (char*)times, sizeof(uint16_t), false) < 0) + return false; + break; + } + { + // Disable Dw + char data = 0x06; + if (Xfer(60/*dw*/, 0x3C, &data, sizeof(data), false) < 0) + return false; + } + + return true; +} + +bool LuXactDw::ResetPower() { + const uint8_t pin = 0; + if (Xfer(/*set high*/18, pin) < 0) return false; + if (Xfer(/*set output*/14, pin) < 0) return false; + usleep(100 * 1000); + if (Xfer(/*set input*/13, pin) < 0) return false; + if (Xfer(/*set low*/19, pin) < 0) return false; + usleep(100 * 1000); + return true; +} +/* power supply + +V+5--------------o + | _______ + | ---zz--o---|\ --|||-- + | | \C CBE + 5K | | +GND----zz--o o---->V+ + +V+ off at boot when PB0 tristated. +V+ on when PB0 set high to turn on Q. +V+ off when PB0 set low to turn off Q. +*/ diff --git a/utility/usbmon/hostusb.hpp b/utility/usbmon/hostusb.hpp new file mode 100644 index 0000000..a6e5aa2 --- /dev/null +++ b/utility/usbmon/hostusb.hpp @@ -0,0 +1,71 @@ +#ifndef hostusb_hpp +#define hostusb_hpp + +#include + +class LuXact { +public: + + const uint32_t timeout = 10; + + class Exception : public std::runtime_error { public: Exception(const char* what_arg) : std::runtime_error(what_arg) { } }; + + LuXact(uint32_t vidpid, const char16_t vendor[], const char16_t product[], const char16_t serial[]); + ~LuXact(); + void Label(); + inline bool IsOpen() const { return lu_dev != nullptr; }; + + bool Open(bool claim = true); + bool Close(); + inline void Sink(int (*_sink)(const char*)) { sink = _sink; } + int Xfer(uint8_t req, uint32_t arg, char* data = nullptr, uint8_t size = 0, bool device_to_host = false); + uint8_t XferRetry(uint8_t req, uint32_t arg, char* data = nullptr, uint8_t size = 0, bool device_to_host = false); + + virtual void Reset() = 0; + virtual void Send(char data) = 0; + virtual void Send(struct Rpc& rpc, uint8_t index) = 0; + virtual void Recv() = 0; + +protected: + + uint32_t id_int; + char16_t* id_string; + struct libusb_device_handle* lu_dev = nullptr; + int (*sink)(const char*) = nullptr; +}; + +class LuXactUsb : public LuXact { +public: + + LuXactUsb(const char* serialnumber); + char16_t* Serial(const char* serialnumber); + + void Reset(); + void Send(char data); + void Send(struct Rpc& rpc, uint8_t index); + void Recv(); + +protected: + char16_t serial[2]; +}; + +class LuXactDw : public LuXact { +public: + + LuXactDw(const char* serialnumber); + char16_t* Serial(const char* serialnumber); + + void Reset(); + void Send(char data); + void Send(struct Rpc& rpc, uint8_t index); + void Recv(); + + bool ReadDw(); + bool ResetDw(); + bool ResetPower(); + +protected: + char16_t serial[4]; +}; + +#endif diff --git a/utility/usbmon/makefile b/utility/usbmon/makefile new file mode 100644 index 0000000..483dd51 --- /dev/null +++ b/utility/usbmon/makefile @@ -0,0 +1,51 @@ +#!/usr/bin/make + +#ARCH=RPi +ifeq ($(ARCH),RPi) +#sudo apt-get install libudev-dev +#mv libusb.h /zaloh/make/RPtools/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/arm-linux-gnueabihf/sysroot/usr/include/libusb-1.0 +#mv libusb-1.0.a libudev.so /zaloh/make/RPtools/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/arm-linux-gnueabihf/sysroot/lib +RASPBERRYPI_TOOLS=/zaloh/make/RPtools/arm-bcm2708 +RASPBERRYPI_TOOLS_BIN=$(RASPBERRYPI_TOOLS)/arm-rpi-4.9.3-linux-gnueabihf/bin +CXX=$(RASPBERRYPI_TOOLS_BIN)/arm-linux-gnueabihf-g++ +CC=$(RASPBERRYPI_TOOLS_BIN)/arm-linux-gnueabihf-gcc +LIBS= -lusb-1.0 -lpthread -ludev +else +LIBS= -lusb-1.0 +endif + +# intermediate files +MKDIR=mkdir -p +INTDIR = build + +# -g for debug -Os for optimize +CPPFLAGS = -std=c++11 -Os + +sources = $(wildcard *.cpp) +objects = $(addprefix $(INTDIR)/,$(addsuffix .o,$(sources))) +targets = $(notdir $(PWD)) + +ifneq ($(wildcard ../core/rpc.hpp),) +CPPFLAGS += -DCORE_RPC_HPP +sources += $(abspath ../core/rpc.cpp) +endif + +all: $(targets) + +$(INTDIR)/%.cpp.o: %.cpp + @$(MKDIR) $(dir $@) ; echo $@ + @$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@ + @$(CXX) -MM $(CPPFLAGS) $(CXXFLAGS) $< -MT $@ -o $(addsuffix .d,$@) + +$(targets): $(objects) + @echo $@ + @$(CXX) $^ $(LIBS) -o $@ + +%.cpp.lst: %.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -O0 -g -fverbose-asm -Wa,-adhln $< > $@ + +clean: + rm -rf $(INTDIR) + +# include dependencies +-include $(wildcard $(INTDIR)/*.d) diff --git a/utility/usbmon/rpc.hpp b/utility/usbmon/rpc.hpp new file mode 100644 index 0000000..f84b31a --- /dev/null +++ b/utility/usbmon/rpc.hpp @@ -0,0 +1,18 @@ +#ifndef rpc_hpp +#define rpc_hpp + +#ifdef CORE_RPC_HPP +#include "../core/rpc.hpp" +#else + +#include + +const uint8_t RpcPayloadMax = 0; +const uint8_t RpcNumber = 0; +struct Rpc { + inline uint8_t Size(uint8_t i) { return 0; } + inline void VtAct(uint8_t i) { } +}; + +#endif // CORE_RPC_HPP +#endif diff --git a/utility/usbmon/usbmon.cpp b/utility/usbmon/usbmon.cpp new file mode 100644 index 0000000..755e876 --- /dev/null +++ b/utility/usbmon/usbmon.cpp @@ -0,0 +1,93 @@ +#include "command.hpp" +#include "hostusb.hpp" +#include "usbmon.hpp" + +#include +#include +#include +#include + +void reset() { + LuXactUsb luXactUsb(serialnumber); + if (!luXactUsb.Open(false)) { + std::cerr << "error opening device" << std::endl; + return; + } + + luXactUsb.Reset(); +} + +void power() { + LuXactDw luXactDw(serialnumber); + if (!luXactDw.Open()) { + std::cerr << "error opening device" << std::endl; + return; + } + + luXactDw.ResetPower(); +} + +void monitor() { + LuXactUsb luXact(serialnumber); + luXact.Open(); + + luXact.Sink(SysConWrite); + for (;;) { + luXact.Recv(); + if (SysConAvail()) { + int c = SysConRead(); + if (c <= 0) continue; + luXact.Send(c); + continue; + } + usleep(100 * 1000); + } +} + +void monitor_dw() { + LuXactDw luXact(serialnumber); + if (!luXact.Open() || !luXact.ResetDw()) { + std::cerr << "error opening device" << std::endl; + return; + } + + luXact.Sink(SysConWrite); + luXact.ReadDw(); + while (!SysConAvail()) { + luXact.Recv(); + usleep(100 * 1000); + } +} + +void change_serialnumber(const char* new_serialnumber) { + LuXactDw luXactDw(serialnumber); + if (!luXactDw.Open()) { + std::cerr << "error opening device" << std::endl; + return; + } + + char16_t* serial = luXactDw.Serial(new_serialnumber); + if (luXactDw.Xfer + (55/*Change serial number*/, + ((uint32_t)serial[0]&0xFF) + |(((uint32_t)serial[1]&0xFF)<<8) + |(((uint32_t)serial[2]&0xFF)<<16)) < 0) { + std::cerr << "error changing serial number" << std::endl; + return; + } +} + +void test() { + LuXactDw luXactDw(serialnumber); + if (!luXactDw.Open()) + return; + + if (!luXactDw.ResetDw()) + return; + + for (;;) { + while (!SysConAvail()) { } + SysConRead(); + luXactDw.Send(0x55); + } +} diff --git a/utility/usbmon/usbmon.hpp b/utility/usbmon/usbmon.hpp new file mode 100644 index 0000000..41db767 --- /dev/null +++ b/utility/usbmon/usbmon.hpp @@ -0,0 +1,11 @@ +#ifndef usbmon_hpp +#define usbmon_hpp + +void reset(); +void power(); +void monitor(); +void monitor_dw(); +void change_serialnumber(const char* new_serialnumber); +void test(); + +#endif From c3c79739951586e98437de1a4aefa3871f0239bb Mon Sep 17 00:00:00 2001 From: Peter Maivald Date: Sun, 5 Nov 2017 11:30:47 +0100 Subject: [PATCH 08/18] fix comment --- utility/usbmon/hostusb.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/utility/usbmon/hostusb.cpp b/utility/usbmon/hostusb.cpp index 6cff8ad..56e1144 100644 --- a/utility/usbmon/hostusb.cpp +++ b/utility/usbmon/hostusb.cpp @@ -399,7 +399,6 @@ PB0>---zz--o---|\ --|||-- 5K | | GND----zz--o o---->V+ -V+ off at boot when PB0 tristated. -V+ on when PB0 set high to turn on Q. -V+ off when PB0 set low to turn off Q. +V+ on at boot when PB0 tristated. +V+ off when PB0 set high to turn off Q. */ From 658acfc5e56fb9df7e863c7eaffc178d1487dcbf Mon Sep 17 00:00:00 2001 From: Peter Maivald Date: Thu, 9 Nov 2017 17:19:20 +0100 Subject: [PATCH 09/18] save 6 cycles of latency --- usbtiny/main.c | 49 ++++++----- usbtiny/main.hex | 214 +++++++++++++++++++++++------------------------ 2 files changed, 136 insertions(+), 127 deletions(-) diff --git a/usbtiny/main.c b/usbtiny/main.c index d2c2934..1185a5d 100644 --- a/usbtiny/main.c +++ b/usbtiny/main.c @@ -1578,39 +1578,48 @@ while servicing dw activities, leading to missed frames in those cases as well. __attribute__((naked, used)) void PCINT0_vect(void) { __asm__ __volatile__ (R"string-literal( - sbis %[pcmsk],%[dwbit] ;? not for us if dw interrupt disabled + ; 00 + sbis %[pcmsk],%[dwbit] ;? not for dw if dw interrupt disabled rjmp DW_INTR_VECTOR ;2 03 leave interrupts disabled for usb - push r21 ;2 04 - in r21,__SREG__ ;1 05 - push r21 ;2 07 sbis %[pinb],%[dwbit] ;? break detected when dw line low rjmp dw_break%= ;2 - ; pass interrupt to usb + ; 04 sbis %[pcmsk],%[usbbit];? call usb interrupt when set - rjmp dw_done%= ;2 13 don't re-enter - cbi %[pcmsk],%[usbbit];2 15 clear to indicate in usb interrupt - ldi r21,0x80 ;1 16 enable interrupts to detect start bit - out __SREG__,r21 ;1 17 and timeout hack for usb interrupt - rcall DW_INTR_VECTOR ;3 20 call usb interrupt with interrupts enabled - cli ;1 21 disable to prevent re-entry from here - sbi %[pcmsk],%[usbbit];2 23 set to indicate not in usb interrupt - ldi r21,0 ;1 24 + reti ;4 09 + ; 06 + cbi %[pcmsk],%[usbbit];2 08 clear to indicate in usb interrupt + rcall dw_nest%= ;3 11 returns from usb interrupt + + cli ;1 20 disable to prevent re-entry from here + sbi %[pcmsk],%[usbbit];2 22 set to indicate not in usb interrupt + push r28 ;2 24 + ldi r28,0 ;1 25 sbis %[pcmsk],%[dwbit] ;? we preempted if dw interrupt now disabled - sts usbRxLen,r21 ;2 27 throw out the frame since we trashed it -dw_done%=: - pop r21 ;2 29 - out __SREG__,r21 ;1 30 - pop r21 ;2 32 - reti ;4 36 + sts usbRxLen,r28 ;2 28 throw out the frame since we trashed it + pop r28 ;2 30 + reti ;4 34 +dw_nest%=: + push r28 ;2 13 match first 3 instructions in usb interrupt + in r28,__SREG__ ;1 14 + push r28 ;2 16 + sei ;1 17 + rjmp DW_INTR_VECTOR+6 ;2 19 call usb interrupt with interrupts enabled dw_break%=: + push r21 + in r21,__SREG__ + push r21 cbi %[pcmsk],%[dwbit] ; Disable further dw interrupts lds r21,dwBuf ; get dwState right shifted by 4 bits tst r21 brne dw_comm%= ldi r21,1 ; no further commands sts dwBuf,r21 ; set break detected flag - rjmp dw_done%= +dw_done%=: + pop r21 + out __SREG__,r21 + pop r21 + reti dw_comm%=: ; execute further commands push r22 ; save registers for called functions push r23 diff --git a/usbtiny/main.hex b/usbtiny/main.hex index 242d91f..e4b39d9 100644 --- a/usbtiny/main.hex +++ b/usbtiny/main.hex @@ -1,16 +1,16 @@ :1000000046C060C0ACC85EC05DC05CC05BC05AC0CA -:1000100059C058C057C056C055C054C053C03CCB3F -:1000200063C9AFC9CDC9F2C91DCA30CA47CA86CA99 -:1000300087CA8FCAA6CA2ECB2DCB2CCB2BCB2ACBD3 -:10004000D6CA28CB27CBE3CA0902190001010080D8 +:1000100059C058C057C056C055C054C053C041CB3A +:1000200068C9B4C9D2C9F7C922CA35CA4CCA8BCA71 +:100030008CCA94CAABCA33CB32CB31CB30CB2FCBAB +:10004000DBCA2DCB2CCBE8CA0902190001010080C4 :10005000640904000001FF00000007058103080097 :1000600064120110010000000881179F0C040100B8 :100070000203011603550053004200740069006E2C :1000800000790053005000490004030904001124C2 :100090001FBECFE5D2E0DEBFCDBF10E0A0E6B0E0EE -:1000A000E6E5F7E102C005900D92A037B107D9F758 +:1000A000E0E6F7E102C005900D92A037B107D9F75D :1000B00022E0A0E7B0E001C01D92AD31B207E1F748 -:1000C00087D847CB9DCFA82FB92F80E090E041E0A3 +:1000C0008CD84CCB9DCFA82FB92F80E090E041E099 :1000D00050EA609530E009C02D9182279795879569 :1000E00010F084279527305EC8F36F5FA8F308955A :1000F000EADF8D939D930895A6E088279927AA9516 @@ -193,9 +193,9 @@ :100C00008D81EE5FF0E0EC58FF4F80838A8184FF96 :100C10008CC080917200882309F487C081E18093A1 :100C2000340187B3842B87BB409373007EC0273386 -:100C3000A1F480916D0090916E007DD580916D0042 -:100C400090916E006B81019676D580916D009091A8 -:100C50006E006C8102966FD568C02C3341F580918F +:100C3000A1F480916D0090916E0082D580916D003D +:100C400090916E006B8101967BD580916D009091A3 +:100C50006E006C81029674D568C02C3341F580918A :100C60008A01811162C0888187FF09C084E991E00F :100C700090936C0180936B019091890157C06093B0 :100C80008A018E818093890190918901911104C01C @@ -217,7 +217,7 @@ :100D8000BBD98453994097FF03C0919581959109F0 :100D90008E159F0514F4C1B77C0181B78F5F81BFA9 :100DA000EACFC1BF61B780E090E0DF91CF911F91A2 -:100DB0000F91FF90EF90DF90BEC4CF93DF93D82FB9 +:100DB0000F91FF90EF90DF90C3C4CF93DF93D82FB4 :100DC000882311F0B89801C0B89AC0E02C2F30E009 :100DD00080914001909141012817390728F481E062 :100DE00090E086DCCF5FF2CFBA98C0E02C2F30E0E5 @@ -276,101 +276,101 @@ :1011300058E0FC013197F1F76695B299606800C0FC :101140005A95B9F76D937395EE27FF27319721F0E4 :10115000B29BFCCF7038F0F2709389010895AA9B7E -:10116000DEC75F935FB75F93B29B10C0AC9B0AC0B2 -:10117000AC9850E85FBFD3D7F894AC9A50E0AA9BE4 -:1011800050936D015F915FBF5F911895AA98509140 -:101190009401552321F451E050939401F3CF6F93C0 -:1011A0007F938F939F93AF93BF93EF93FF93559547 -:1011B00018F0559518F003C0A6DF01C05FDFFF915E -:1011C000EF91BF91AF919F918F917F916F91DACF06 -:1011D00080E287BB88BB80E090E0A5D28F3F09F01A -:1011E00081BFC0916D00D0916E00CE019CD2E82EDF -:1011F000CE01019698D2F82ECE01029694D2982F65 -:1012000080ED8E0D8A3008F07CC280ED8F0D8A3023 -:1012100008F077C280ED890F8A3008F072C2C09161 -:101220006D00D0916E00CE017ED290E09093680068 -:1012300080936700CE01019676D290E090936A0089 -:1012400080936900CE0102966ED290E090936C007C -:1012500080936B00BB9AC4E18FE090E056DAC150F6 -:10126000D9F7BB9885E090E09093410180934001CD -:1012700010926300109262009EE088E10FB6F8942D -:10128000A89581BD0FBE91BD10923B0110923A010D -:1012900010923901109238011092370110923601E4 -:1012A00010923D0110923401C9D97894B898C09831 -:1012B000B998C198BA98C298BD98C598DAE130E457 -:1012C000F32EC1E04BE1E42E20E8D22EA89585D87C -:1012D000E09134018E2F90E08531910508F0DAC15C -:1012E000FC01E15FFF4F0994B89AB998BA9AC29A83 -:1012F00010928B018091140280933F01D0923E01A5 -:1013000080913E01882309F458C0C098B12C8B2DE0 -:1013100090E020916200309163008217930728F4D7 -:1013200081E090E0E5D9B394F2CF90913E018091B5 -:101330003F01892309F0C09AC29880918B01880FE0 -:1013400080938B0196B320918B0191FB882780F9C4 -:10135000820F80938B01B12C8B2D90E02091620045 -:10136000309163008217930728F481E090E0C0D9A0 -:10137000B394F2CFC29A80913E01869580933E014C -:10138000BFCFBA9AC29880EE91E0B2D9BFB6F894B6 -:10139000C29A86E490E0ACD9BA9886B382FB002763 -:1013A00000F910E0C80121E0822780938B01BFBEC5 -:1013B0008AE991E09DD9BA9AC29AC09393016AC111 -:1013C0008091140280933E01BA9A98E0B92EAFB68C -:1013D000F894C29880913E0180FF07C086E090E0BB -:1013E00087D9C29A80E490E006C08CE390E080D96F -:1013F000C29A8AE090E07CD980913E0186958093E4 -:101400003E01AFBEBA94B110E2CF44C110928B013D -:1014100088E0B82E80918B01869580938B01BA9AD3 -:10142000AFB6F894C29886E090E062D9C29A8AE09A -:1014300090E05ED9BA9886B382FB882780F98093C2 -:101440003E01AFBE87E390E053D980913E018823EF -:1014500029F080918B01806880938B01BA94B11040 -:10146000D9CFABCFBA9ABFB6F894C29886E090E0D5 -:101470003FD9C29A8AE090E03BD9BA9886B382FB02 -:10148000882780F980938B01BFBE97CFBA9ABFB6E9 -:10149000F894C2988091140280FF07C086E090E023 -:1014A00027D9C29A80E490E006C08CE390E020D96E -:1014B000C29A8AE090E01CD9BFBEECC0B99AB89835 -:1014C000BA9AC298E0923F01DDB98091140281116D -:1014D000C59810923E01E0913E0180911502E817F7 -:1014E00030F5F0E0EA5EFD4F80818FB9FEB8809163 -:1014F0003F018DB9B12C8B2D90E02091620030918D -:1015000063008217930728F481E090E0F1D8B39448 -:10151000F2CF769BECCF80913E01E82FF0E09FB1B7 -:10152000E557FE4F90838F5F80933E01D4CF80912B -:1015300014028111C59A80913E0156C0D0DCAAC028 -:1015400080E090E0E3DC8091140258DD80933E015E -:1015500015C0B12C80911402B81650F4EB2DF0E0B8 -:10156000EA5EFD4F80814ADD80933E01B394F2CF65 -:10157000809115028111FFDC80913E0180938B01E7 -:101580001CCF80911402811102C0B12C19C0912C82 -:10159000292D30E08091150290E00197A90145576F -:1015A0005E4F5A012817390734F481E034DDF50124 -:1015B00080839394EDCF80E02EDDF50180830EC013 -:1015C00080911502B81650F48B2C912C81E023DD0C -:1015D000F401E557FE4F8083B394F2CF8091160259 -:1015E0008111C9DC809115028093930153C081E081 -:1015F00090E08BD8BFB6F8946091720070E0409193 -:10160000730084E790E028D91092720055CF809142 -:101610008A01847319F082E090E077D880918A0182 -:1016200080FF05C0C298BA9A84E690E06ED8809197 -:101630008A0181FF08C08091940180938701809185 -:1016400095018093880180918A0182FD36DD809129 -:101650008A0183FF11C080918A0182958F708093E7 -:1016600094019091940181E0911180E0809389012F -:1016700010928A01BA98AA9A80918A0184FD43DD6A -:1016800080918A0185FDFADC1092340110928A0162 -:10169000789402C01092340180913C01882309F4AF -:1016A00015CE80913D0181110DC08091380180934C -:1016B0003B018091370180933A01809136018093FC -:1016C000390119C090913B01981710F4C09801C0DE -:1016D000C09A90913D0180913A01891710F4C19808 -:1016E00001C0C19A90913D0180913901891710F490 -:1016F000C29801C0C29A80913D018F5F80933D01E5 -:10170000E3CD65E3CE0117D080916D0090916E001E -:1017100061E3019610D080916D0090916E0062E3BC -:10172000029609D07CCDE199FECF9FBB8EBBE09A9B -:1017300099278DB30895262FE199FECF1CBA9FBB40 -:101740008EBB2DBB0FB6F894E29AE19A0FBE0196BC -:061750000895F894FFCF9C -:10175600FF5A0A000A080335003100320020000053 +:10116000DEC7B29B12C0AC9B1895AC9809D0F8941E +:10117000AC9ACF93C0E0AA9BC0936D01CF91189514 +:10118000CF93CFB7CF937894CDC75F935FB75F937B +:10119000AA9850919401552339F451E05093940149 +:1011A0005F915FBF5F9118956F937F938F939F932C +:1011B000AF93BF93EF93FF93559518F0559518F0A3 +:1011C00003C0A1DF01C05ADFFF91EF91BF91AF9142 +:1011D0009F918F917F916F91E3CF80E287BB88BB16 +:1011E00080E090E0A5D28F3F09F081BFC0916D00F3 +:1011F000D0916E00CE019CD2E82ECE01019698D2FD +:10120000F82ECE01029694D2982F80ED8E0D8A3062 +:1012100008F07CC280ED8F0D8A3008F077C280ED37 +:10122000890F8A3008F072C2C0916D00D0916E00B3 +:10123000CE017ED290E09093680080936700CE014B +:10124000019676D290E090936A0080936900CE0177 +:1012500002966ED290E090936C0080936B00BB9AE4 +:10126000C4E18FE090E051DAC150D9F7BB9885E036 +:1012700090E090934101809340011092630010929E +:1012800062009EE088E10FB6F894A89581BD0FBE7C +:1012900091BD10923B0110923A01109239011092C7 +:1012A0003801109237011092360110923D011092D0 +:1012B0003401C4D97894B898C098B998C198BA98AC +:1012C000C298BD98C598DAE130E4F32EC1E04BE155 +:1012D000E42E20E8D22EA89580D8E09134018E2FFC +:1012E00090E08531910508F0DAC1FC01E15FFF4F24 +:1012F0000994B89AB998BA9AC29A10928B018091BF +:10130000140280933F01D0923E0180913E018823D8 +:1013100009F458C0C098B12C8B2D90E02091620048 +:10132000309163008217930728F481E090E0E0D9C0 +:10133000B394F2CF90913E0180913F01892309F04F +:10134000C09AC29880918B01880F80938B0196B3CD +:1013500020918B0191FB882780F9820F80938B016C +:10136000B12C8B2D90E020916200309163008217A8 +:10137000930728F481E090E0BBD9B394F2CFC29AEE +:1013800080913E01869580933E01BFCFBA9AC29864 +:1013900080EE91E0ADD9BFB6F894C29A86E490E0B1 +:1013A000A7D9BA9886B382FB002700F910E0C801DC +:1013B00021E0822780938B01BFBE8AE991E098D912 +:1013C000BA9AC29AC09393016AC180911402809321 +:1013D0003E01BA9A98E0B92EAFB6F894C2988091BF +:1013E0003E0180FF07C086E090E082D9C29A80E487 +:1013F00090E006C08CE390E07BD9C29A8AE090E04E +:1014000077D980913E01869580933E01AFBEBA9414 +:10141000B110E2CF44C110928B0188E0B82E8091C8 +:101420008B01869580938B01BA9AAFB6F894C298D7 +:1014300086E090E05DD9C29A8AE090E059D9BA98E6 +:1014400086B382FB882780F980933E01AFBE87E395 +:1014500090E04ED980913E01882329F080918B0144 +:10146000806880938B01BA94B110D9CFABCFBA9A70 +:10147000BFB6F894C29886E090E03AD9C29A8AE062 +:1014800090E036D9BA9886B382FB882780F980939A +:101490008B01BFBE97CFBA9ABFB6F894C29880911D +:1014A000140280FF07C086E090E022D9C29A80E44F +:1014B00090E006C08CE390E01BD9C29A8AE090E0ED +:1014C00017D9BFBEECC0B99AB898BA9AC298E09240 +:1014D0003F01DDB9809114028111C59810923E013F +:1014E000E0913E0180911502E81730F5F0E0EA5EE8 +:1014F000FD4F80818FB9FEB880913F018DB9B12C2D +:101500008B2D90E020916200309163008217930749 +:1015100028F481E090E0ECD8B394F2CF769BECCF46 +:1015200080913E01E82FF0E09FB1E557FE4F908398 +:101530008F5F80933E01D4CF809114028111C59AB0 +:1015400080913E0156C0CBDCAAC080E090E0DEDC9A +:101550008091140253DD80933E0115C0B12C80911F +:101560001402B81650F4EB2DF0E0EA5EFD4F8081D6 +:1015700045DD80933E01B394F2CF80911502811135 +:10158000FADC80913E0180938B011CCF8091140284 +:10159000811102C0B12C19C0912C292D30E080910D +:1015A000150290E00197A90145575E4F5A0128178F +:1015B000390734F481E02FDDF50180839394EDCF7A +:1015C00080E029DDF50180830EC080911502B816F8 +:1015D00050F48B2C912C81E01EDDF401E557FE4F79 +:1015E0008083B394F2CF809116028111C4DC809184 +:1015F00015028093930153C081E090E086D8BFB676 +:10160000F8946091720070E04091730084E790E07C +:1016100023D91092720055CF80918A01847319F0FA +:1016200082E090E072D880918A0180FF05C0C29864 +:10163000BA9A84E690E069D880918A0181FF08C057 +:101640008091940180938701809195018093880116 +:1016500080918A0182FD31DD80918A0183FF11C072 +:1016600080918A0182958F7080939401909194016A +:1016700081E0911180E08093890110928A01BA98EB +:10168000AA9A80918A0184FD3EDD80918A0185FDC0 +:10169000F5DC1092340110928A01789402C0109205 +:1016A000340180913C01882309F415CE80913D01DD +:1016B00081110DC08091380180933B0180913701E9 +:1016C00080933A01809136018093390119C090913D +:1016D0003B01981710F4C09801C0C09A90913D0149 +:1016E00080913A01891710F4C19801C0C19A909174 +:1016F0003D0180913901891710F4C29801C0C29A46 +:1017000080913D018F5F80933D01E3CD65E3CE0184 +:1017100017D080916D0090916E0061E3019610D01A +:1017200080916D0090916E0062E3029609D07CCDAD +:10173000E199FECF9FBB8EBBE09A99278DB30895A8 +:10174000262FE199FECF1CBA9FBB8EBB2DBB0FB6D7 +:10175000F894E29AE19A0FBE01960895F894FFCFAB +:10176000FF5A0A000A080335003100320020000049 :00000001FF From 484e2e1ac5d085f76ce4bc5712683304519f805b Mon Sep 17 00:00:00 2001 From: Peter Maivald Date: Wed, 15 Nov 2017 15:04:29 +0100 Subject: [PATCH 10/18] bidirectional uartsoft example --- example/echodw/Makefile | 36 +++++ example/echodw/core.hpp | 20 +++ example/echodw/echodw.cpp | 31 ++++ example/echodw/rpc.hpp | 19 +++ example/echodw/uartsoft.cpp | 283 ++++++++++++++++++++++++++++++++++++ example/echodw/uartsoft.hpp | 52 +++++++ utility/usbmon/usbmon.cpp | 10 +- 7 files changed, 449 insertions(+), 2 deletions(-) create mode 100644 example/echodw/Makefile create mode 100644 example/echodw/core.hpp create mode 100644 example/echodw/echodw.cpp create mode 100644 example/echodw/rpc.hpp create mode 100644 example/echodw/uartsoft.cpp create mode 100644 example/echodw/uartsoft.hpp diff --git a/example/echodw/Makefile b/example/echodw/Makefile new file mode 100644 index 0000000..f52b8de --- /dev/null +++ b/example/echodw/Makefile @@ -0,0 +1,36 @@ + +#MCU=attiny13 +#F_CPU = 9600000 +MCU=attiny84 +F_CPU = 8000000 +#MCU=attiny85 +#F_CPU = 1000000 + +CFLAGS = +CFLAGS += -Wno-deprecated-declarations +CFLAGS += -Wl,--gc-sections +CFLAGS += -fdata-sections -ffunction-sections +COMPILE = avr-gcc -Wall -Os --std=c++11 -save-temps=obj -DF_CPU=$(F_CPU) $(CFLAGS) -mmcu=$(MCU) + +BASENAME = $(notdir $(PWD)) +OBJECTS = $(addsuffix .o, $(basename $(wildcard *.cpp))) + +all: $(BASENAME).elf + +dwload: + dwdebug l $(BASENAME).elf ,qr + +clean: + rm -f *.elf *.map *.o *.i *.ii *.s *.lss + +.cpp.o: + $(COMPILE) -c $< -o $@ + +%.i: %.cpp + $(COMPILE) -E -c $< -o $@ + +$(BASENAME).elf: $(OBJECTS) + $(COMPILE) -Wl,-Map,$(BASENAME).map -o $(BASENAME).elf $(OBJECTS) + +disasm: $(BASENAME).elf + avr-objdump -d $(BASENAME).elf > $(BASENAME).lss diff --git a/example/echodw/core.hpp b/example/echodw/core.hpp new file mode 100644 index 0000000..7615005 --- /dev/null +++ b/example/echodw/core.hpp @@ -0,0 +1,20 @@ +// items used in assembler as well as C +#ifndef MUAPK_EXP +#define MUAPK_EXP(A) A +#define MUAPK_INT(A0,A1) A0##A1 +#define MUAPK_CAT(A0,A1) MUAPK_INT(A0,A1) +#define MUAPK_2_0(A0,A1) A0 +#define MUAPK_2_1(A0,A1) A1 +#define MUAPK_3_0(A0,A1,A2) A0 +#define MUAPK_3_1(A0,A1,A2) A1 +#define MUAPK_3_2(A0,A1,A2) A2 +#define MUAPK_4_0(A0,A1,A2,A3) A0 +#define MUAPK_4_1(A0,A1,A2,A3) A1 +#define MUAPK_4_2(A0,A1,A2,A3) A2 +#define MUAPK_4_3(A0,A1,A2,A3) A3 +#define MUAPK_5_0(A0,A1,A2,A3,A4) A0 +#define MUAPK_5_1(A0,A1,A2,A3,A4) A1 +#define MUAPK_5_2(A0,A1,A2,A3,A4) A2 +#define MUAPK_5_3(A0,A1,A2,A3,A4) A3 +#define MUAPK_5_4(A0,A1,A2,A3,A4) A4 +#endif // MUAPK_EXP diff --git a/example/echodw/echodw.cpp b/example/echodw/echodw.cpp new file mode 100644 index 0000000..ce850f0 --- /dev/null +++ b/example/echodw/echodw.cpp @@ -0,0 +1,31 @@ + +#include "uartsoft.hpp" + +#include +#include +#include + +int main() { + uartSoft.begin(); + + for (;;) { + wdt_reset(); + _delay_ms(100); + + // get the data + char* s; + uint8_t n = uartSoft.read(&s); + if (!n) continue; + + // skip any leading null + if (!s[0]) { ++s; --n; } + if (!n) continue; + + // send it back + char buf[32] = " got: "; + strcat(buf, s); + strcat(buf, "\n"); + _delay_ms(100); + uartSoft.write(buf, strlen(buf)); + } +} diff --git a/example/echodw/rpc.hpp b/example/echodw/rpc.hpp new file mode 100644 index 0000000..a5c5b49 --- /dev/null +++ b/example/echodw/rpc.hpp @@ -0,0 +1,19 @@ +#ifndef rpc_hpp +#define rpc_hpp + +#ifdef CORE_RPC_HPP +#include "../core/rpc.hpp" +#else + +#include + +const uint8_t RpcPayloadMax = 0; +const uint8_t RpcNumber = 0; +struct RpcSend { }; +struct Rpc { + inline uint8_t Size(uint8_t i) { return 0; } + inline void VtAct(uint8_t i) { } +}; + +#endif // CORE_RPC_HPP +#endif diff --git a/example/echodw/uartsoft.cpp b/example/echodw/uartsoft.cpp new file mode 100644 index 0000000..05f7448 --- /dev/null +++ b/example/echodw/uartsoft.cpp @@ -0,0 +1,283 @@ +#include "uartsoft.hpp" +#include +#include +#include + +UartSoft uartSoft; + +// divisor >= 16 and multiple of 4 +uint8_t UartSoft::divisor; +volatile char UartSoft::buff[UARTSOFT_BUFF_SIZE*2]; +volatile char* UartSoft::next = UartSoft::buff; + +extern "C" __attribute__ ((naked)) +char* UartSoft_last() { + // called from interrupt, so careful with registers + + // working registers declared as early clobbers + register uint16_t r asm("r24"); + register uint8_t t asm("r26"); + __asm__ __volatile__ (R"assembly( + + ldi %A[r],lo8(%[buff]) + ldi %B[r],hi8(%[buff]) + lds %[t],%[next] + cp %A[r],%[t] + lds %[t],%[next]+1 + cpc %B[r],%[t] + brne resetbuf%= + adiw %A[r],%[size] +resetbuf%=: + ret + +)assembly" + : [r] "=&r" (r) + ,[t] "=&r" (t) + : [buff] "m" (UartSoft::buff) + ,[next] "m" (UartSoft::next) + ,[size] "I" (UARTSOFT_BUFF_SIZE) + : + ); +} + +void UartSoft::begin(uint8_t _divisor) { + divisor = _divisor; + + // input, enable pullup + UARTSOFT_REG(DDR,UARTSOFT_ARGS) &=~ (1<= 16 and multiple of 4 + ; count = (divisor - 12) >> 2; + subi %[count],12 + lsr %[count] + lsr %[count] + + ; one start bit (0), one stop bit (1) + ; ch:c = 0x600 | (uint16_t(c) << 1); + ldi %B[c],0x06 + add %A[c],%A[c] + adc %B[c],__zero_reg__ + + ; uint8_t sreg = SREG; cli(); + in %[sreg],__SREG__ + cli + + ; shift right and output lsb +loop%=: ;0. 00 + lsr %B[c] ;1. 01 + ror %A[c] ;1. 02 + brcc set_low%= ;12 03 +set_high%=: ;0. 03 + cbi %[ddr],%[bit] ;2. 05 ;input + sbi %[port],%[bit];2. 07 ;pullup + rjmp set_done%= ;.2 09 +set_low%=: ;0. 04 + cbi %[port],%[bit];2. 06 ;low + sbi %[ddr],%[bit] ;2. 08 ;output + nop ;1. 09 +set_done%=: ;0. 09 + + ; loop until all bits sent + adiw %[c],0 ;2. 11 + breq all_done%= ;12 12 + + ; delay = 4*count + mov __tmp_reg__,%[count];1. +delay%=: + dec __tmp_reg__ ;1. + breq loop%= ;12 + rjmp delay%= ;.2 + + ; SREG = sreg; +all_done%=: + out __SREG__,%[sreg] + ret +)assembly" + : [sreg] "=&r" (sreg) + ,[c] "+w" (c) + : [ddr] "I" (_SFR_IO_ADDR(UARTSOFT_REG(DDR,UARTSOFT_ARGS))) + ,[port] "I" (_SFR_IO_ADDR(UARTSOFT_REG(PORT,UARTSOFT_ARGS))) + ,[bit] "I" (UARTSOFT_BIT(UARTSOFT_ARGS)) + ,[count] "r" (divisor) + ); +} + +#ifdef core_hpp +uint8_t UartSoft::write(const fstr_t* s, uint8_t n) { + uint8_t r = n; + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + while (n--) + UartSoft_write(pgm_read_byte(s++)); + } + return r; +} +#endif + +uint8_t UartSoft::write(const char* s, uint8_t n) { + uint8_t r = n; + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + while (n--) + UartSoft_write(*s++); + } + return r; +} + +extern "C" __attribute__ ((naked, used)) +void UARTSOFT_INT(UARTSOFT_ARGS)() { + + // working registers declared as early clobbers + register uint8_t delay asm("r25"); + register uint8_t ibit asm("r23"); + register uint8_t idel asm("r22"); + register uint8_t ibuf asm("r21"); + register uint8_t recv asm("r24"); + register uint16_t x asm("r26"); + __asm__ __volatile__ (R"assembly( + ; 00 + sbic %[pin],%[bit] ;? + reti ;2 give up if high + push r24 ;2 02 + in r24,__SREG__ ;1 03 + push r24 ;2 05 + push r27 ;2 07 + push r26 ;2 09 + push r25 ;2 11 + push r23 ;2 13 + push r22 ;2 15 + push r21 ;2 17 + + lds %A[x],%[next] ;2 19 buffer + lds %B[x],%[next]+1 ;2 21 + ldi %[ibuf],%[size]-1 ;1 22 max number of bytes + + ; intra bit delay (divisor - 8) >> 2 + lds %[delay],%[divisor] ;2 24 + subi %[delay],8 ;1 25 delay till next bit + lsr %[delay] ;1 26 + lsr %[delay] ;1 27 + +readbyte%=: + ldi %[ibit],8 ;1 number of timeout bits +waitstart%=: + mov %[idel],%[delay] ;1 4*idel timeout +loopstart%=: ; + dec %[idel] ;1 + sbic %[pin],%[bit] ;? + brne loopstart%= ;? 4*idel end + + sbis %[pin],%[bit] ;? + rjmp havestart%= ;2 + dec %[ibit] ;1 + brne waitstart%= ;? + rjmp haveall%= ;2 timeout for start bit + +havestart%=: + mov %[idel],%[delay] ;1 1.5 bits + lsr %[idel] ;1 +delays%=: ; wait for 1st bit + dec %[idel] ;1 + nop ;1 + brne delays%= ;? + + ldi %[ibit],8 ;1 number of data bits +loop%=: ; critical loop timing 4*delay + 8 + mov %[idel],%[delay] ;1 +delay%=: + dec %[idel] ;1 00 + nop ;1 01 + brne delay%= ;? 4*delay + + lsr %[recv] ;1 01 sample + sbic %[pin],%[bit] ;? skip if high + ori %[recv],0x80 ;1 03 set msb + rjmp . ;2 05 nop + dec %[ibit] ;1 06 + brne loop%= ;? 08 more bits? + + mov %[idel],%[delay] ;1 .5 bits + lsr %[idel] ;1 +delayt%=: ; wait past last bit + dec %[idel] ;1 00 + nop ;1 01 + brne delayt%= ;? 4*delay + + st X+,%[recv] ;2 store byte + dec %[ibuf] ;1 00 + brne readbyte%= ;? + ; buffer full +haveall%=: + ldi %[recv],%[size]-1 ;1 max number of bytes + sub %[recv],%[ibuf] ;1 bytes received + lds %A[x],%[next] ;2 + lds %B[x],%[next]+1 ;2 + adiw %A[x],%[size]-1 ;2 + st X,%[recv] ;2 save number of characters + rcall UartSoft_last ; swap buffers + sts %[next],r24 + sts %[next]+1,r25 + + pop r21 ;2 + pop r22 ;2 + pop r23 ;2 + pop r25 ;2 + pop r26 ;2 + pop r27 ;2 + pop r24 ;2 + out __SREG__,r24 ;1 + pop r24 ;2 + reti ;4 + +)assembly" + : [recv] "=&r" (recv) + ,[delay] "=&r" (delay) + ,[ibit] "=&r" (ibit) + ,[idel] "=&r" (idel) + ,[ibuf] "=&r" (ibuf) + ,[x] "=&r" (x) + : [divisor] "m" (UartSoft::divisor) + ,[next] "m" (UartSoft::next) + ,[size] "I" (UARTSOFT_BUFF_SIZE) + ,[pin] "I" (_SFR_IO_ADDR(UARTSOFT_REG(PIN,UARTSOFT_ARGS))) + ,[bit] "I" (UARTSOFT_BIT(UARTSOFT_ARGS)) + : + ); +} + +uint8_t UartSoft::read(char* d, uint8_t n) { + char* s; + uint8_t r = read(&s); + r = r < n ? r : n; + memcpy(d, s, r); + return r; +} + +uint8_t UartSoft::read(char** d) { + char* last = UartSoft_last(); + *d = last; + uint8_t r = last[UARTSOFT_BUFF_SIZE-1]; + last[UARTSOFT_BUFF_SIZE-1] = 0; + return r; +} + +bool UartSoft::eof() { + return UartSoft_last()[UARTSOFT_BUFF_SIZE-1] != 0; +} + +void UartSoft::send(Rpc& rpc, uint8_t index) { +} diff --git a/example/echodw/uartsoft.hpp b/example/echodw/uartsoft.hpp new file mode 100644 index 0000000..b9ea9e7 --- /dev/null +++ b/example/echodw/uartsoft.hpp @@ -0,0 +1,52 @@ +#ifndef uartsoft_hpp +#define uartsoft_hpp + +#include "core.hpp" +#include "rpc.hpp" + +#ifndef UARTSOFT_BUFF_SIZE +#define UARTSOFT_BUFF_SIZE 16 +#endif + +// UARTSOFT_ARGS (port,bit#,pci#,pcint), REG (DDR|PORT|PIN) +#define UARTSOFT_REG(REG,ARGS) MUAPK_CAT(REG,MUAPK_4_0 ARGS) +#define UARTSOFT_BIT(ARGS) MUAPK_4_1 ARGS +#define UARTSOFT_PCMSK(ARGS) MUAPK_CAT(PCMSK,MUAPK_4_2 ARGS) +#define UARTSOFT_PCIE(ARGS) MUAPK_CAT(PCIE,MUAPK_4_2 ARGS) +#define UARTSOFT_INT(ARGS) MUAPK_4_3 ARGS + +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny13__) +#define UARTSOFT_ARGS (B,5,,PCINT0_vect) +#elif defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) +#define UARTSOFT_ARGS (B,3,1,PCINT1_vect) +#else // defined(__AVR_*) +#error DW ports +#endif // defined(__AVR_*) + +extern class UartSoft : public RpcSend { +public: + + void begin(uint8_t divisor = 128); + +#ifdef core_hpp + static uint8_t write(const fstr_t* s, uint8_t n); +#endif + static uint8_t write(const char* s, uint8_t n); + static uint8_t read(char* d, uint8_t n); + static uint8_t read(char** d); + static bool eof(); + + void send(Rpc& rpc, uint8_t index); + + // divisor >= 16 and multiple of 4 + static uint8_t divisor; + + // double buffer, next gets loaded + // last byte has number of chars received + // 1st byte has command, 0: text, 0xff: reset + static volatile char buff[UARTSOFT_BUFF_SIZE*2]; + static volatile char* next; + +} uartSoft; + +#endif // uartsoft_hpp diff --git a/utility/usbmon/usbmon.cpp b/utility/usbmon/usbmon.cpp index 755e876..a8ae424 100644 --- a/utility/usbmon/usbmon.cpp +++ b/utility/usbmon/usbmon.cpp @@ -51,10 +51,16 @@ void monitor_dw() { return; } - luXact.Sink(SysConWrite); luXact.ReadDw(); - while (!SysConAvail()) { + luXact.Sink(SysConWrite); + for (;;) { luXact.Recv(); + if (SysConAvail()) { + int c = SysConRead(); + if (c <= 0) continue; + luXact.Send(c); + continue; + } usleep(100 * 1000); } } From 596bf17a4d3d381c5898b0ee8eeccbe2ee360104 Mon Sep 17 00:00:00 2001 From: Peter Maivald Date: Thu, 16 Nov 2017 23:33:50 +0100 Subject: [PATCH 11/18] better manage lw state --- usbtiny/main.c | 9 + usbtiny/main.hex | 432 +++++++++++++++++++------------------ utility/usbmon/command.cpp | 7 + utility/usbmon/command.hpp | 1 + utility/usbmon/hostusb.cpp | 119 ++++++---- utility/usbmon/hostusb.hpp | 14 +- utility/usbmon/usbmon.cpp | 31 +-- 7 files changed, 331 insertions(+), 282 deletions(-) diff --git a/usbtiny/main.c b/usbtiny/main.c index 1185a5d..582bdc3 100644 --- a/usbtiny/main.c +++ b/usbtiny/main.c @@ -996,6 +996,13 @@ uchar usbFunctionSetup(uchar data[8]) if (req == 60) { // debugWIRE transfer + if (!(data[0] & 0x80) && !data[2]) { + // command 0 always cancels prior operation + cbi(PCMSK,DW_BIT); + dwState = 0; + return 0; + } + if (dwState) {return 0;} // Prior operation has not yet completed if (data[0] & 0x80) { @@ -1007,6 +1014,7 @@ uchar usbFunctionSetup(uchar data[8]) } else { // OUT transfer - host to device. rq->wValue specifies action to take. + cbi(PCMSK,DW_BIT); // mask any dw sync interrupt dwState = data[2]; // action required, from low byte of rq->wValue dwLen = *((uint16_t*)(data+6)); // rq->wLength if (dwLen == 0) { @@ -2004,6 +2012,7 @@ int main(void) { // 00100000 0x20 Read pulse widths // // Supported combinations + // $00 - Cancel any pending command // $21 - Send break and read pulse widths // $02 - Set timing parameters // $04 - Send bytes diff --git a/usbtiny/main.hex b/usbtiny/main.hex index e4b39d9..436b58b 100644 --- a/usbtiny/main.hex +++ b/usbtiny/main.hex @@ -1,16 +1,16 @@ -:1000000046C060C0ACC85EC05DC05CC05BC05AC0CA -:1000100059C058C057C056C055C054C053C041CB3A -:1000200068C9B4C9D2C9F7C922CA35CA4CCA8BCA71 -:100030008CCA94CAABCA33CB32CB31CB30CB2FCBAB -:10004000DBCA2DCB2CCBE8CA0902190001010080C4 +:1000000046C060C0B6C85EC05DC05CC05BC05AC0C0 +:1000100059C058C057C056C055C054C053C04BCB30 +:1000200072C9BEC9DCC901CA2CCA3FCA56CA95CA20 +:1000300096CA9ECAB5CA3DCB3CCB3BCB3ACB39CB5B +:10004000E5CA37CB36CBF2CA09021900010100809C :10005000640904000001FF00000007058103080097 :1000600064120110010000000881179F0C040100B8 :100070000203011603550053004200740069006E2C :1000800000790053005000490004030904001124C2 :100090001FBECFE5D2E0DEBFCDBF10E0A0E6B0E0EE -:1000A000E0E6F7E102C005900D92A037B107D9F75D +:1000A000E4E7F7E102C005900D92A037B107D9F758 :1000B00022E0A0E7B0E001C01D92AD31B207E1F748 -:1000C0008CD84CCB9DCFA82FB92F80E090E041E099 +:1000C00096D856CB9DCFA82FB92F80E090E041E085 :1000D00050EA609530E009C02D9182279795879569 :1000E00010F084279527305EC8F36F5FA8F308955A :1000F000EADF8D939D930895A6E088279927AA9516 @@ -97,7 +97,7 @@ :1006000080936000CEE1C093610084E196B398715D :1006100031F48150D9F710926E0110926801C1E057 :100620008111C0E0809170008C1729F0C11101C0C8 -:1006300081D3C0937000DF91CF910895AC9A8BB7AE +:100630008BD3C0937000DF91CF910895AC9A8BB7A4 :1006400080628BBFECE5F1E08BE481838AE58083F7 :100650000895FC01DB01AC014C5F5F4F619128E024 :1006600030E080E890E0782F762309F0C09A70910E @@ -137,240 +137,242 @@ :1008800028BB00C0000028BB21F000C000C000C091 :10089000F2CFEACF0895CF93DF93EC0129812111A4 :1008A00008C081E28983D0936C01C0936B0198E00A -:1008B0003DC2213011F486B30BC16A81223011F49C -:1008C00068BB33C2962F362F377041E050E0032EBD +:1008B00047C2213011F486B30BC16A81223011F492 +:1008C00068BB3DC2962F362F377041E050E0032EB3 :1008D00002C0440F551F0A94E2F7233009F496C072 -:1008E000243021F488B3482B48BB1FC2253051F473 +:1008E000243021F488B3482B48BB29C2253051F469 :1008F00060936400B99887B3856287BB88B3887DAD -:1009000088BB13C2263049F4B898C098B998C198EA -:10091000BA98C298BD98C59808C2273051F4BE0154 +:1009000088BB1DC2263049F4B898C098B998C198E0 +:10091000BA98C298BD98C59812C2273051F4BE014A :10092000CE01029696DED0936C01C0936B0194E0E9 -:10093000FDC1283031F4609350018B8180934F01C9 -:10094000F4C1EC81FD81F0934E01E0934D0129301B +:1009300007C2283031F4609350018B8180934F01BE +:10094000FEC1EC81FD81F0934E01E0934D01293011 :1009500011F480E203C02B3021F480EA80934A0135 -:10096000A4C1EA81FB81F0934C01E0934B012A3052 +:10096000AEC1EA81FB81F0934C01E0934B012A3048 :1009700011F480E4F3CF2C3011F480ECEFCF2D3064 :1009800021F487B34095482304C02E3021F487B367 -:10099000482B47BBCAC12F3009F5611105C08091B2 +:10099000482B47BBD4C12F3009F5611105C08091A8 :1009A000350187B9A59A0CC0613031F480913501C9 :1009B000816087B9A29A04C0623011F48FE887B9C8 :1009C000369A3699FECF84B1888385B18983D093D6 -:1009D0006C01C0936B0114BA92E0A8C1203159F4A4 +:1009D0006C01C0936B0114BA92E0B2C1203159F49A :1009E000B89AB99A8AB5806A8ABD8AB583608ABD89 :1009F00083B7856044C0213121F469BD8C8188BDF5 -:100A000094C1223109F46ECF233121F488B340958B +:100A00009EC1223109F46ECF233121F488B3409581 :100A100048236ACF243159F486B390E04823592300 :100A200002C0559547953A95E2F7488352C0263162 :100A3000A1F561110AC083B78B7F83BF83B78D7F18 -:100A400083BF83B7816083BF71C1613031F483B7E5 +:100A400083BF83B7816083BF7BC1613031F483B7DB :100A50008B7F83BF83B782600FC0623031F483B76E :100A60008B7F83BF83B7826014C0633051F483B738 :100A7000846083BF83B78D7F83BF83B78E7F83BF3F -:100A800054C1643009F051C183B7846083BF83B718 +:100A80005EC1643009F05BC183B7846083BF83B704 :100A90008D7F83BF83B78160F2CF2F3159F48B8173 :100AA00090E0982F8827860F911D90936300809384 -:100AB00062003BC1203219F41ABC13BE36C1213288 +:100AB000620045C1203219F41ABC13BE40C1213274 :100AC00021F46093140281E07FC0223241F483E17B -:100AD0008883D0936C01C0936B0191E027C12332CE +:100AD0008883D0936C01C0936B0191E031C12332C4 :100AE000C9F4379A96B18A81892B86B99B81913056 -:100AF00059F038F0923009F018C1809135018069C1 -:100B000006C01092350112C1809135018068809332 -:100B100035010BC1283249F48BE891E090936C01C8 -:100B200080936B019091930101C1293211F482E00D +:100AF00059F038F0923009F022C1809135018069B7 +:100B000006C0109235011CC1809135018068809328 +:100B1000350115C1283249F48BE891E090936C01BE +:100B200080936B01909193010BC1293211F482E003 :100B30004BC02A3211F483E00BC02B3211F484E055 :100B400043C02C3211F488E03FC02D3231F489E0EB -:100B50008093340160931402E8C02E3261F48BE07C +:100B50008093340160931402F2C02E3261F48BE072 :100B600080933401609314028B81809315028C81F1 -:100B700080931602DAC02F3259F4613031F4B89AFA -:100B8000B99ABA9A60933C01D0C010923C01CDC092 +:100B700080931602E4C02F3259F4613031F4B89AF0 +:100B8000B99ABA9A60933C01DAC010923C01D7C07E :100B9000203349F4609338018B81809337018C8135 -:100BA00080933601C2C0213331F470E0709341016B -:100BB00060934001BAC0223311F485E005C02333AD -:100BC00031F46093140286E080933401AEC0263382 +:100BA00080933601CCC0213331F470E07093410161 +:100BB00060934001C4C0223311F485E005C02333A3 +:100BC00031F46093140286E080933401B8C0263378 :100BD00071F565FF1BC0E0917200E03CB8F48B81B9 :100BE000AE2FB0E0AC58BF4F8C938C81A1E0AE0F1C :100BF000B0E0AC58BF4F8C9383E08E0F80937200AF :100C00008D81EE5FF0E0EC58FF4F80838A8184FF96 -:100C10008CC080917200882309F487C081E18093A1 -:100C2000340187B3842B87BB409373007EC0273386 -:100C3000A1F480916D0090916E0082D580916D003D -:100C400090916E006B8101967BD580916D009091A3 -:100C50006E006C81029674D568C02C3341F580918A -:100C60008A01811162C0888187FF09C084E991E00F -:100C700090936C0180936B019091890157C06093B0 -:100C80008A018E818093890190918901911104C01C -:100C900084E1809334014AC080918901813818F041 -:100CA00080E88093890110921C029FEF3FC0822F41 -:100CB000807F803EE1F48AE080933401822F877048 -:100CC0008093140228702093150220E08091140272 -:100CD000281758F5822F90E00296FE01E80FF91FC1 -:100CE0003081FC01EC5EFD4F30832F5FEFCF803F02 -:100CF000E1F487E080933401822F8870809314029E -:100D000027702093150220E080911502281768F4BF -:100D1000822F90E00296FE01E80FF91F3081FC015E -:100D2000EC5EFD4F30832F5FEFCF90E0892FDF9196 -:100D3000CF910895DF92EF92FF920F931F93CF937D -:100D4000DF9308E010E0C0E0D0E8DD2EDC0ED1BE7D -:100D5000D3D97C0184E3E81689E0F8060CF4CD2DA4 -:100D6000D6950150110989F78FEF8C0F81BF0C2F99 -:100D700010E00F5F1F4F21B730E0021713078CF010 -:100D8000BBD98453994097FF03C0919581959109F0 -:100D90008E159F0514F4C1B77C0181B78F5F81BFA9 -:100DA000EACFC1BF61B780E090E0DF91CF911F91A2 -:100DB0000F91FF90EF90DF90C3C4CF93DF93D82FB4 -:100DC000882311F0B89801C0B89AC0E02C2F30E009 -:100DD00080914001909141012817390728F481E062 -:100DE00090E086DCCF5FF2CFBA98C0E02C2F30E0E5 -:100DF00080914001909141012817390728F481E042 -:100E000090E076DCCF5FF2CFBA9AC0E02C2F30E0D2 -:100E100080914001909141012817390728F481E021 -:100E200090E066DCCF5FF2CFD111B89AC0E08C2F92 -:100E300090E020914001309141018217930728F4FE -:100E400081E090E055DCCF5FF2CFDF91CF91089544 -:100E50000F931F93CF93DF93B898C0E08C2F90E04F -:100E600020914001309141018217930728F481E0DD -:100E700090E03EDCCF5FF2CFBA98C0E00091400135 -:100E8000109141018C2F90E08017910728F481E0A8 -:100E900090E02EDCCF5FF2CFD6B3C0E08C2F90E095 -:100EA0008017910728F481E090E022DCCF5FF6CF35 -:100EB000BA9AC0E02C2F30E080914001909141011E -:100EC0002817390728F481E090E012DCCF5FF2CFD9 -:100ED0008D2F8170DF91CF911F910F910895CF9346 -:100EE00088B38A7F88BBBA98B898C0E08C2F90E00E -:100EF00020914001309141018217930728F481E04D -:100F000090E0F6DBCF5FF2CFCF910895CF9387B318 -:100F10008A7F87BBC0E08C2F90E020914001309108 -:100F200041018217930728F481E090E0E1DBCF5F75 -:100F3000F2CFB89AC0E08C2F90E020914001309120 -:100F400041018217930728F481E090E0D1DBCF5F65 -:100F5000F2CFBA9AC0E08C2F90E0209140013091FE -:100F600041018217930728F481E090E0C1DBCF5F55 -:100F7000F2CFCF910895CF93B89AC0E08C2F90E034 -:100F800020914001309141018217930728F481E0BC -:100F900090E0AEDBCF5FF2CFBA9AC0E08C2F90E04A -:100FA00020914001309141018217930728F481E09C -:100FB00090E09EDBCF5FF2CFBA98C0E08C2F90E03C -:100FC00020914001309141018217930728F481E07C -:100FD00090E08EDBCF5FF2CFB898C0E08C2F90E02E -:100FE00020914001309141018217930728F481E05C -:100FF00090E07EDBCF5FF2CFCF910895CF93DF9368 -:10100000D82FC8E08D2F8078D8DEDD0FC150D1F702 -:10101000DF91CF911DCFFF920F931F93CF93DF935B -:10102000F82ED8E0C0E0CC0F13DFC82BD150D9F791 -:1010300000914001109141018D2F90E080179107A0 -:1010400028F481E090E054DBDF5FF2CFFF2011F065 -:1010500080E001C081E0B1DED0E08D2F90E080170C -:10106000910728F481E090E043DBDF5FF6CF8C2F1F -:10107000DF91CF911F910F91FF900895A4E9B1E006 -:101080008827F894C29ABA98EE27FF27319688F0FD -:10109000B299FCCF803468F4ED93FD938395EE27ED -:1010A000FF27319630F0B29BFCCFED93FD938395F3 -:1010B000EBCF880F809389010895C29ABA9A8091E4 -:1010C000870190918801A4E9B1E0709189017723AB -:1010D000C1F0F894FC01C2986D9158E03197F1F796 -:1010E00060FDC29A60FFC298FC013197F1F76695E6 -:1010F0005A95B1F7FC01C29A31963197F1F77A957A -:1011000049F7BA98089577278091870190918801CF -:10111000A4E9B1E0EE27FF273197F1F0B299FCCFB7 -:10112000FC01F695E7953197F1F7B299F3CF662771 -:1011300058E0FC013197F1F76695B299606800C0FC -:101140005A95B9F76D937395EE27FF27319721F0E4 -:10115000B29BFCCF7038F0F2709389010895AA9B7E -:10116000DEC7B29B12C0AC9B1895AC9809D0F8941E -:10117000AC9ACF93C0E0AA9BC0936D01CF91189514 -:10118000CF93CFB7CF937894CDC75F935FB75F937B -:10119000AA9850919401552339F451E05093940149 -:1011A0005F915FBF5F9118956F937F938F939F932C -:1011B000AF93BF93EF93FF93559518F0559518F0A3 -:1011C00003C0A1DF01C05ADFFF91EF91BF91AF9142 -:1011D0009F918F917F916F91E3CF80E287BB88BB16 -:1011E00080E090E0A5D28F3F09F081BFC0916D00F3 -:1011F000D0916E00CE019CD2E82ECE01019698D2FD -:10120000F82ECE01029694D2982F80ED8E0D8A3062 -:1012100008F07CC280ED8F0D8A3008F077C280ED37 -:10122000890F8A3008F072C2C0916D00D0916E00B3 -:10123000CE017ED290E09093680080936700CE014B -:10124000019676D290E090936A0080936900CE0177 -:1012500002966ED290E090936C0080936B00BB9AE4 -:10126000C4E18FE090E051DAC150D9F7BB9885E036 -:1012700090E090934101809340011092630010929E -:1012800062009EE088E10FB6F894A89581BD0FBE7C -:1012900091BD10923B0110923A01109239011092C7 -:1012A0003801109237011092360110923D011092D0 -:1012B0003401C4D97894B898C098B998C198BA98AC -:1012C000C298BD98C598DAE130E4F32EC1E04BE155 -:1012D000E42E20E8D22EA89580D8E09134018E2FFC -:1012E00090E08531910508F0DAC1FC01E15FFF4F24 -:1012F0000994B89AB998BA9AC29A10928B018091BF -:10130000140280933F01D0923E0180913E018823D8 -:1013100009F458C0C098B12C8B2D90E02091620048 -:10132000309163008217930728F481E090E0E0D9C0 -:10133000B394F2CF90913E0180913F01892309F04F -:10134000C09AC29880918B01880F80938B0196B3CD -:1013500020918B0191FB882780F9820F80938B016C -:10136000B12C8B2D90E020916200309163008217A8 -:10137000930728F481E090E0BBD9B394F2CFC29AEE -:1013800080913E01869580933E01BFCFBA9AC29864 -:1013900080EE91E0ADD9BFB6F894C29A86E490E0B1 -:1013A000A7D9BA9886B382FB002700F910E0C801DC -:1013B00021E0822780938B01BFBE8AE991E098D912 -:1013C000BA9AC29AC09393016AC180911402809321 -:1013D0003E01BA9A98E0B92EAFB6F894C2988091BF -:1013E0003E0180FF07C086E090E082D9C29A80E487 -:1013F00090E006C08CE390E07BD9C29A8AE090E04E -:1014000077D980913E01869580933E01AFBEBA9414 -:10141000B110E2CF44C110928B0188E0B82E8091C8 -:101420008B01869580938B01BA9AAFB6F894C298D7 -:1014300086E090E05DD9C29A8AE090E059D9BA98E6 -:1014400086B382FB882780F980933E01AFBE87E395 -:1014500090E04ED980913E01882329F080918B0144 -:10146000806880938B01BA94B110D9CFABCFBA9A70 -:10147000BFB6F894C29886E090E03AD9C29A8AE062 -:1014800090E036D9BA9886B382FB882780F980939A -:101490008B01BFBE97CFBA9ABFB6F894C29880911D -:1014A000140280FF07C086E090E022D9C29A80E44F -:1014B00090E006C08CE390E01BD9C29A8AE090E0ED -:1014C00017D9BFBEECC0B99AB898BA9AC298E09240 -:1014D0003F01DDB9809114028111C59810923E013F -:1014E000E0913E0180911502E81730F5F0E0EA5EE8 -:1014F000FD4F80818FB9FEB880913F018DB9B12C2D -:101500008B2D90E020916200309163008217930749 -:1015100028F481E090E0ECD8B394F2CF769BECCF46 -:1015200080913E01E82FF0E09FB1E557FE4F908398 -:101530008F5F80933E01D4CF809114028111C59AB0 -:1015400080913E0156C0CBDCAAC080E090E0DEDC9A -:101550008091140253DD80933E0115C0B12C80911F -:101560001402B81650F4EB2DF0E0EA5EFD4F8081D6 -:1015700045DD80933E01B394F2CF80911502811135 -:10158000FADC80913E0180938B011CCF8091140284 -:10159000811102C0B12C19C0912C292D30E080910D -:1015A000150290E00197A90145575E4F5A0128178F -:1015B000390734F481E02FDDF50180839394EDCF7A -:1015C00080E029DDF50180830EC080911502B816F8 -:1015D00050F48B2C912C81E01EDDF401E557FE4F79 -:1015E0008083B394F2CF809116028111C4DC809184 -:1015F00015028093930153C081E090E086D8BFB676 -:10160000F8946091720070E04091730084E790E07C -:1016100023D91092720055CF80918A01847319F0FA -:1016200082E090E072D880918A0180FF05C0C29864 -:10163000BA9A84E690E069D880918A0181FF08C057 -:101640008091940180938701809195018093880116 -:1016500080918A0182FD31DD80918A0183FF11C072 -:1016600080918A0182958F7080939401909194016A -:1016700081E0911180E08093890110928A01BA98EB -:10168000AA9A80918A0184FD3EDD80918A0185FDC0 -:10169000F5DC1092340110928A01789402C0109205 -:1016A000340180913C01882309F415CE80913D01DD -:1016B00081110DC08091380180933B0180913701E9 -:1016C00080933A01809136018093390119C090913D -:1016D0003B01981710F4C09801C0C09A90913D0149 -:1016E00080913A01891710F4C19801C0C19A909174 -:1016F0003D0180913901891710F4C29801C0C29A46 -:1017000080913D018F5F80933D01E3CD65E3CE0184 -:1017100017D080916D0090916E0061E3019610D01A -:1017200080916D0090916E0062E3029609D07CCDAD -:10173000E199FECF9FBB8EBBE09A99278DB30895A8 -:10174000262FE199FECF1CBA9FBB8EBB2DBB0FB6D7 -:10175000F894E29AE19A0FBE01960895F894FFCFAB -:10176000FF5A0A000A080335003100320020000049 +:100C100096C080917200882309F491C081E180938D +:100C2000340187B3842B87BB4093730088C027337C +:100C3000A1F480916D0090916E008CD580916D0033 +:100C400090916E006B81019685D580916D00909199 +:100C50006E006C8102967ED572C02C3391F588812E +:100C600087FD06C0611104C0AA9810928A0168C06D +:100C700090918A01911163C087FF09C084E991E0D6 +:100C800090936C0180936B019091890159C0AA984F +:100C90008A8180938A018E81809389019091890154 +:100CA000911104C084E1809334014AC0809189018C +:100CB000813818F080E88093890110921C029FEF20 +:100CC0003FC0822F807F803EE1F48AE08093340130 +:100CD000822F87708093140228702093150220E0E1 +:100CE00080911402281758F5822F90E00296FE0199 +:100CF000E80FF91F3081FC01EC5EFD4F30832F5F60 +:100D0000EFCF803FE1F487E080933401822F887039 +:100D10008093140227702093150220E08091150221 +:100D2000281768F4822F90E00296FE01E80FF91F61 +:100D30003081FC01EC5EFD4F30832F5FEFCF90E000 +:100D4000892FDF91CF910895DF92EF92FF920F9359 +:100D50001F93CF93DF9308E010E0C0E0D0E8DD2ED2 +:100D6000DC0ED1BEC9D97C0184E3E81689E0F8061F +:100D70000CF4CD2DD6950150110989F78FEF8C0F0A +:100D800081BF0C2F10E00F5F1F4F21B730E002171B +:100D900013078CF0B1D98453994097FF03C0919504 +:100DA000819591098E159F0514F4C1B77C0181B717 +:100DB0008F5F81BFEACFC1BF61B780E090E0DF9174 +:100DC000CF911F910F91FF90EF90DF90C3C4CF930D +:100DD000DF93D82F882311F0B89801C0B89AC0E0EB +:100DE0002C2F30E080914001909141012817390764 +:100DF00028F481E090E07CDCCF5FF2CFBA98C0E0CD +:100E00002C2F30E080914001909141012817390743 +:100E100028F481E090E06CDCCF5FF2CFBA9AC0E0BA +:100E20002C2F30E080914001909141012817390723 +:100E300028F481E090E05CDCCF5FF2CFD111B89A6A +:100E4000C0E08C2F90E02091400130914101821749 +:100E5000930728F481E090E04BDCCF5FF2CFDF9185 +:100E6000CF9108950F931F93CF93DF93B898C0E06D +:100E70008C2F90E02091400130914101821793071F +:100E800028F481E090E034DCCF5FF2CFBA98C0E084 +:100E900000914001109141018C2F90E08017910743 +:100EA00028F481E090E024DCCF5FF2CFD6B3C0E03D +:100EB0008C2F90E08017910728F481E090E018DCF7 +:100EC000CF5FF6CFBA9AC0E02C2F30E0809140017E +:100ED000909141012817390728F481E090E008DC5F +:100EE000CF5FF2CF8D2F8170DF91CF911F910F9146 +:100EF0000895CF9388B38A7F88BBBA98B898C0E02A +:100F00008C2F90E02091400130914101821793078E +:100F100028F481E090E0ECDBCF5FF2CFCF91089531 +:100F2000CF9387B38A7F87BBC0E08C2F90E020915E +:100F30004001309141018217930728F481E090E04D +:100F4000D7DBCF5FF2CFB89AC0E08C2F90E0209132 +:100F50004001309141018217930728F481E090E02D +:100F6000C7DBCF5FF2CFBA9AC0E08C2F90E0209120 +:100F70004001309141018217930728F481E090E00D +:100F8000B7DBCF5FF2CFCF910895CF93B89AC0E08F +:100F90008C2F90E0209140013091410182179307FE +:100FA00028F481E090E0A4DBCF5FF2CFBA9AC0E0F2 +:100FB0008C2F90E0209140013091410182179307DE +:100FC00028F481E090E094DBCF5FF2CFBA98C0E0E4 +:100FD0008C2F90E0209140013091410182179307BE +:100FE00028F481E090E084DBCF5FF2CFB898C0E0D6 +:100FF0008C2F90E02091400130914101821793079E +:1010000028F481E090E074DBCF5FF2CFCF910895B8 +:10101000CF93DF93D82FC8E08D2F8078D8DEDD0FF7 +:10102000C150D1F7DF91CF911DCFFF920F931F9346 +:10103000CF93DF93F82ED8E0C0E0CC0F13DFC82B9E +:10104000D150D9F700914001109141018D2F90E0CE +:101050008017910728F481E090E04ADBDF5FF2CF50 +:10106000FF2011F080E001C081E0B1DED0E08D2FE3 +:1010700090E08017910728F481E090E039DBDF5F92 +:10108000F6CF8C2FDF91CF911F910F91FF90089594 +:10109000A4E9B1E08827F894C29ABA98EE27FF270E +:1010A000319688F0B299FCCF803468F4ED93FD93CB +:1010B0008395EE27FF27319630F0B29BFCCFED935E +:1010C000FD938395EBCF880F809389010895C29A91 +:1010D000BA9A8091870190918801A4E9B1E070915A +:1010E00089017723C1F0F894FC01C2986D9158E012 +:1010F0003197F1F760FDC29A60FFC298FC01319709 +:10110000F1F766955A95B1F7FC01C29A319631977D +:10111000F1F77A9549F7BA98089577278091870172 +:1011200090918801A4E9B1E0EE27FF273197F1F013 +:10113000B299FCCFFC01F695E7953197F1F7B2999A +:10114000F3CF662758E0FC013197F1F76695B29925 +:10115000606800C05A95B9F76D937395EE27FF2725 +:10116000319721F0B29BFCCF7038F0F27093890177 +:101170000895AA9BD4C7B29B12C0AC9B1895AC989B +:1011800009D0F894AC9ACF93C0E0AA9BC0936D01AC +:10119000CF911895CF93CFB7CF937894C3C75F9370 +:1011A0005FB75F93AA9850919401552339F451E0A9 +:1011B000509394015F915FBF5F9118956F937F93F8 +:1011C0008F939F93AF93BF93EF93FF93559518F031 +:1011D000559518F003C0A1DF01C05ADFFF91EF91D0 +:1011E000BF91AF919F918F917F916F91E3CF80E2FB +:1011F00087BB88BB80E090E0A5D28F3F09F081BF1C +:10120000C0916D00D0916E00CE019CD2E82ECE012F +:10121000019698D2F82ECE01029694D2982F80EDA6 +:101220008E0D8A3008F07CC280ED8F0D8A3008F078 +:1012300077C280ED890F8A3008F072C2C0916D00CC +:10124000D0916E00CE017ED290E0909368008093A2 +:101250006700CE01019676D290E090936A00809369 +:101260006900CE0102966ED290E090936C0080935C +:101270006B00BB9AC4E18FE090E047DAC150D9F728 +:10128000BB9885E090E090934101809340011092DB +:101290006300109262009EE088E10FB6F894A89572 +:1012A00081BD0FBE91BD10923B0110923A01109288 +:1012B00039011092380110923701109236011092C4 +:1012C0003D0110923401BAD97894B898C098B99871 +:1012D000C198BA98C298BD98C598DAE130E4F32E67 +:1012E000C1E04BE1E42E20E8D22EA89576D8E0911B +:1012F00034018E2F90E08531910508F0DAC1FC01B0 +:10130000E15FFF4F0994B89AB998BA9AC29A1092BD +:101310008B018091140280933F01D0923E01809115 +:101320003E01882309F458C0C098B12C8B2D90E061 +:1013300020916200309163008217930728F481E0C6 +:1013400090E0D6D9B394F2CF90913E0180913F01C5 +:10135000892309F0C09AC29880918B01880F8093ED +:101360008B0196B320918B0191FB882780F9820F26 +:1013700080938B01B12C8B2D90E0209162003091F5 +:1013800063008217930728F481E090E0B1D9B39409 +:10139000F2CFC29A80913E01869580933E01BFCFE5 +:1013A000BA9AC29880EE91E0A3D9BFB6F894C29AD7 +:1013B00086E490E09DD9BA9886B382FB002700F9B5 +:1013C00010E0C80121E0822780938B01BFBE8AE92B +:1013D00091E08ED9BA9AC29AC09393016AC1809162 +:1013E000140280933E01BA9A98E0B92EAFB6F894F1 +:1013F000C29880913E0180FF07C086E090E078D9D6 +:10140000C29A80E490E006C08CE390E071D9C29A61 +:101410008AE090E06DD980913E01869580933E01EF +:10142000AFBEBA94B110E2CF44C110928B0188E0F4 +:10143000B82E80918B01869580938B01BA9AAFB6B6 +:10144000F894C29886E090E053D9C29A8AE090E07E +:101450004FD9BA9886B382FB882780F980933E01E2 +:10146000AFBE87E390E044D980913E01882329F004 +:1014700080918B01806880938B01BA94B110D9CF91 +:10148000ABCFBA9ABFB6F894C29886E090E030D954 +:10149000C29A8AE090E02CD9BA9886B382FB88275A +:1014A00080F980938B01BFBE97CFBA9ABFB6F894EC +:1014B000C2988091140280FF07C086E090E018D99E +:1014C000C29A80E490E006C08CE390E011D9C29A01 +:1014D0008AE090E00DD9BFBEECC0B99AB898BA9A2C +:1014E000C298E0923F01DDB9809114028111C59844 +:1014F00010923E01E0913E0180911502E81730F50F +:10150000F0E0EA5EFD4F80818FB9FEB880913F0127 +:101510008DB9B12C8B2D90E0209162003091630049 +:101520008217930728F481E090E0E2D8B394F2CFD9 +:10153000769BECCF80913E01E82FF0E09FB1E5571C +:10154000FE4F90838F5F80933E01D4CF8091140231 +:101550008111C59A80913E0156C0CBDCAAC080E0C3 +:1015600090E0DEDC8091140253DD80933E0115C0D3 +:10157000B12C80911402B81650F4EB2DF0E0EA5E25 +:10158000FD4F808145DD80933E01B394F2CF809181 +:1015900015028111FADC80913E0180938B011CCFF2 +:1015A00080911402811102C0B12C19C0912C292DF7 +:1015B00030E08091150290E00197A90145575E4FF8 +:1015C0005A012817390734F481E02FDDF5018083B3 +:1015D0009394EDCF80E029DDF50180830EC08091EA +:1015E0001502B81650F48B2C912C81E01EDDF4010D +:1015F000E557FE4F8083B394F2CF8091160281119C +:10160000C4DC809115028093930153C081E090E087 +:101610007CD8BFB6F8946091720070E0409173007E +:1016200084E790E019D91092720055CF80918A0119 +:10163000847319F082E090E068D880918A0180FF7D +:1016400005C0C298BA9A84E690E05FD880918A017A +:1016500081FF08C08091940180938701809195015A +:101660008093880180918A0182FD31DD80918A0119 +:1016700083FF11C080918A0182958F7080939401BD +:101680009091940181E0911180E080938901109202 +:101690008A01BA98AA9A80918A0184FD3EDD8091E0 +:1016A0008A0185FDF5DC1092340110928A0178944C +:1016B00002C01092340180913C01882309F415CEB8 +:1016C00080913D0181110DC08091380180933B01D3 +:1016D0008091370180933A018091360180933901DE +:1016E00019C090913B01981710F4C09801C0C09A9E +:1016F00090913D0180913A01891710F4C19801C081 +:10170000C19A90913D0180913901891710F4C298D6 +:1017100001C0C29A80913D018F5F80933D01E3CD6E +:1017200065E3CE0117D080916D0090916E0061E36A +:10173000019610D080916D0090916E0062E3029648 +:1017400009D07CCDE199FECF9FBB8EBBE09A992753 +:101750008DB30895262FE199FECF1CBA9FBB8EBB97 +:101760002DBB0FB6F894E29AE19A0FBE0196089548 +:04177000F894FFCF1B +:10177400FF5A0A000A080335003100320020000035 :00000001FF diff --git a/utility/usbmon/command.cpp b/utility/usbmon/command.cpp index f963661..99c45a3 100644 --- a/utility/usbmon/command.cpp +++ b/utility/usbmon/command.cpp @@ -69,6 +69,7 @@ int SysConRead() return r <= 0 ? 0 : (unsigned int)c; } +int opt_debug = false; char* serialnumber = nullptr; static termios ttystate; void reset_terminal_mode() { tcsetattr(STDIN_FILENO, TCSANOW, &ttystate); } @@ -83,6 +84,7 @@ void args(int argc, char** argv) { lo_change_serialnumber, }; static struct option options[] = { + {"debug", no_argument, &opt_debug, true}, {"change-serialnumber", required_argument, 0, lo_change_serialnumber}, {nullptr, 0, nullptr, 0} }; @@ -94,6 +96,10 @@ void args(int argc, char** argv) { default: throw usage(); + case 0: + // set a flag + break; + case 's': serialnumber = strdup(optarg); break; @@ -141,6 +147,7 @@ void args(int argc, char** argv) { -d dw monitor -u usb monitor + --debug show libusb messages --change-serialnumber # change to new serial number (requires dw) )00here-text00"; exit(3); diff --git a/utility/usbmon/command.hpp b/utility/usbmon/command.hpp index 4bd67ff..0b9308c 100644 --- a/utility/usbmon/command.hpp +++ b/utility/usbmon/command.hpp @@ -7,6 +7,7 @@ int SysConWrite(const char* s); int SysConAvail(); int SysConRead(); +extern int opt_debug; extern char* serialnumber; #define RunInit(module) struct _##module##_Init{_##module##_Init();}__##module##_Init;_##module##_Init::_##module##_Init() diff --git a/utility/usbmon/hostusb.cpp b/utility/usbmon/hostusb.cpp index 56e1144..aa69839 100644 --- a/utility/usbmon/hostusb.cpp +++ b/utility/usbmon/hostusb.cpp @@ -32,7 +32,9 @@ LuXact::LuXact(uint32_t vidpid, const char16_t vendor[], const char16_t product[ std::char_traits::copy(id_string + vl + 1, pl ? product : blank, pl + 1); std::char_traits::copy(id_string + vl + 1 + pl + 1, sl ? serial : blank, sl + 1); - Label(); + if (opt_debug) + libusb_set_debug(lu_ctx, LIBUSB_LOG_LEVEL_INFO); + Label(); } LuXact::~LuXact() @@ -76,11 +78,13 @@ void LuXact::Label() std::cerr.setf(fmtfl, std::ios::basefield); } } + if (id_more) + std::cerr << ' ' << id_more; if (!lu_dev) std::cerr << ']'; std::cerr << '\a'; } -bool LuXact::Open(bool claim) +bool LuXact::Open() { libusb_device* *devs; ssize_t cnt = libusb_get_device_list(lu_ctx, &devs); @@ -181,7 +185,7 @@ bool LuXact::Close() } int LuXact::Xfer(uint8_t req, uint32_t arg, char* data, uint8_t size, bool device_to_host) { - return libusb_control_transfer + int ret = libusb_control_transfer (/* libusb_device_handle* */lu_dev, /* bmRequestType */ LIBUSB_REQUEST_TYPE_CLASS | (device_to_host ? LIBUSB_ENDPOINT_IN : LIBUSB_ENDPOINT_OUT), @@ -191,6 +195,18 @@ int LuXact::Xfer(uint8_t req, uint32_t arg, char* data, uint8_t size, bool devic /* data */ (unsigned char*)data, /* wLength */ size, timeout); + if (opt_debug) { + std::cerr << (device_to_host ? "xi: " : "xo: ") << (int)req; + std::ios::fmtflags fmtfl = std::cerr.setf(std::ios::hex, std::ios::basefield); + std::cerr << 'x' << (uint16_t)req; + std::cerr.setf(fmtfl, std::ios::basefield); + std::cerr << ' ' << arg; + fmtfl = std::cerr.setf(std::ios::hex, std::ios::basefield); + std::cerr << 'x' << arg; + std::cerr.setf(fmtfl, std::ios::basefield); + std::cerr << " -> " << ret << ' ' << libusb_error_name(ret) << std::endl; + } + return ret; } uint8_t LuXact::XferRetry(uint8_t req, uint32_t arg, char* data, uint8_t size, bool device_to_host) @@ -260,11 +276,13 @@ void LuXactUsb::Recv() { } } -LuXactDw::LuXactDw(const char* serialnumber) +LuXactLw::LuXactLw(const char* serialnumber) : LuXact(0x17810c9f, nullptr, nullptr, Serial(serialnumber)) { + id[0] = '\0'; + id_more = id; } -char16_t* LuXactDw::Serial(const char* serialnumber) { +char16_t* LuXactLw::Serial(const char* serialnumber) { serial[0] = 0; if (!serialnumber) return nullptr; int sn = strtol(serialnumber, nullptr, 0); @@ -280,59 +298,77 @@ char16_t* LuXactDw::Serial(const char* serialnumber) { return serial; } -void LuXactDw::Reset() { +bool LuXactLw::Open() { + listening = false; + + if (!LuXact::Open()) + return false; + + // Cancel any dw commands + if (Xfer(60/*dw*/, 0) < 0) + return false; + return true; +} + +void LuXactLw::Reset() { } -void LuXactDw::Send(char data) { +void LuXactLw::Send(char data) { + // Cancel any dw commands + XferRetry(60/*dw*/, 0); + // send bytes - Xfer(60/*dw*/, 0x04, &data, sizeof(data), false); + char buf[2] = ""; buf[1] = data; + Xfer(60/*dw*/, 0x04, buf, sizeof(buf), false); + + // Xfer cancels dw interrupt, so restart it + if (listening) { + listening = false; + Recv(); + } } -void LuXactDw::Send(struct Rpc& rpc, uint8_t index) { +void LuXactLw::Send(struct Rpc& rpc, uint8_t index) { } -void LuXactDw::Recv() { - // libusb calls taken from dwire/DigiSpark.c +void LuXactLw::Recv() { + if (listening) { + // libusb calls taken from dwire/DigiSpark.c - // Read back dWIRE bytes - char data[129]; - int status = Xfer(60/*dw*/, 0, data, sizeof(data)-1, true); - // 0 means no data, PIPE means garbled command - //static int last = 50000; if (status || last != status) std::cerr << "Recv status:" << status << '\n'; last = status; - if (status == 0 || status == LIBUSB_ERROR_TIMEOUT || status == LIBUSB_ERROR_PIPE) - return; - if (status == LIBUSB_ERROR_NO_DEVICE) { + // Read back dWIRE bytes + char data[129]; + int status = Xfer(60/*dw*/, 0, data, sizeof(data)-1, true); + // 0 means no data, PIPE means garbled command + //static int last = 50000; if (status || last != status) std::cerr << "Recv status:" << status << '\n'; last = status; + if (status == 0 || status == LIBUSB_ERROR_TIMEOUT || status == LIBUSB_ERROR_PIPE) + return; + if (status == LIBUSB_ERROR_NO_DEVICE) { #if 1 - XferRetry(0/*echo*/, 0); + XferRetry(0/*echo*/, 0); #else - libusb_device* dev = libusb_get_device(lu_dev); + libusb_device* dev = libusb_get_device(lu_dev); #endif - return; - } - if (status > 0 && sink) { - data[status] = '\0'; + return; + } + if (status > 0 && sink) { + data[status] = '\0'; #if 0 - std::cerr << '['; - std::ios::fmtflags fmtfl = std::cerr.setf(std::ios::hex, std::ios::basefield); - for (int i = 0 ; i < status; ++i) std::cerr << ((int)data[i]&0xFF) << ' '; - std::cerr.setf(fmtfl, std::ios::basefield); - std::cerr << ']'; + std::cerr << '['; + std::ios::fmtflags fmtfl = std::cerr.setf(std::ios::hex, std::ios::basefield); + for (int i = 0 ; i < status; ++i) std::cerr << ((int)data[i]&0xFF) << ' '; + std::cerr.setf(fmtfl, std::ios::basefield); + std::cerr << ']'; #endif - sink(data); + sink(data); + } } - if (status > 0) - ReadDw(); -} - -bool LuXactDw::ReadDw() -{ // Wait for start bit and Read bytes XferRetry(60/*dw*/, 0x18); - return true; + listening = true; } -bool LuXactDw::ResetDw() { +bool LuXactLw::ResetDw() { // libusb calls taken from dwire/DigiSpark.c // DigisparkBreakAndSync() @@ -361,7 +397,7 @@ bool LuXactDw::ResetDw() { // Determine timing loop iteration counts for sending and receiving bytes times[0] = (cyclesperpulse-8)/4; // dwBitTime - std::cerr << "baud: " << 16500000 / cyclesperpulse << std::endl; + strcpy(id, (std::to_string(16500000 / cyclesperpulse)+"Bd").c_str()); // Set timing parameter if (Xfer(60/*dw*/, 0x02, (char*)times, sizeof(uint16_t), false) < 0) @@ -375,10 +411,11 @@ bool LuXactDw::ResetDw() { return false; } + Label(); return true; } -bool LuXactDw::ResetPower() { +bool LuXactLw::ResetPower() { const uint8_t pin = 0; if (Xfer(/*set high*/18, pin) < 0) return false; if (Xfer(/*set output*/14, pin) < 0) return false; diff --git a/utility/usbmon/hostusb.hpp b/utility/usbmon/hostusb.hpp index a6e5aa2..865c320 100644 --- a/utility/usbmon/hostusb.hpp +++ b/utility/usbmon/hostusb.hpp @@ -15,7 +15,7 @@ class LuXact { void Label(); inline bool IsOpen() const { return lu_dev != nullptr; }; - bool Open(bool claim = true); + virtual bool Open(); bool Close(); inline void Sink(int (*_sink)(const char*)) { sink = _sink; } int Xfer(uint8_t req, uint32_t arg, char* data = nullptr, uint8_t size = 0, bool device_to_host = false); @@ -30,6 +30,8 @@ class LuXact { uint32_t id_int; char16_t* id_string; + char* id_more = nullptr; + bool claim = true; struct libusb_device_handle* lu_dev = nullptr; int (*sink)(const char*) = nullptr; }; @@ -49,23 +51,27 @@ class LuXactUsb : public LuXact { char16_t serial[2]; }; -class LuXactDw : public LuXact { +class LuXactLw : public LuXact { public: - LuXactDw(const char* serialnumber); + LuXactLw(const char* serialnumber); char16_t* Serial(const char* serialnumber); + bool Open(); + void Reset(); void Send(char data); void Send(struct Rpc& rpc, uint8_t index); void Recv(); - bool ReadDw(); bool ResetDw(); bool ResetPower(); protected: char16_t serial[4]; + char id[20]; + + bool listening; }; #endif diff --git a/utility/usbmon/usbmon.cpp b/utility/usbmon/usbmon.cpp index a8ae424..a341130 100644 --- a/utility/usbmon/usbmon.cpp +++ b/utility/usbmon/usbmon.cpp @@ -9,7 +9,7 @@ void reset() { LuXactUsb luXactUsb(serialnumber); - if (!luXactUsb.Open(false)) { + if (!luXactUsb.Open()) { std::cerr << "error opening device" << std::endl; return; } @@ -18,13 +18,13 @@ void reset() { } void power() { - LuXactDw luXactDw(serialnumber); - if (!luXactDw.Open()) { + LuXactLw luXactLw(serialnumber); + if (!luXactLw.Open()) { std::cerr << "error opening device" << std::endl; return; } - luXactDw.ResetPower(); + luXactLw.ResetPower(); } void monitor() { @@ -45,13 +45,12 @@ void monitor() { } void monitor_dw() { - LuXactDw luXact(serialnumber); + LuXactLw luXact(serialnumber); if (!luXact.Open() || !luXact.ResetDw()) { std::cerr << "error opening device" << std::endl; return; } - luXact.ReadDw(); luXact.Sink(SysConWrite); for (;;) { luXact.Recv(); @@ -66,14 +65,14 @@ void monitor_dw() { } void change_serialnumber(const char* new_serialnumber) { - LuXactDw luXactDw(serialnumber); - if (!luXactDw.Open()) { + LuXactLw luXactLw(serialnumber); + if (!luXactLw.Open()) { std::cerr << "error opening device" << std::endl; return; } - char16_t* serial = luXactDw.Serial(new_serialnumber); - if (luXactDw.Xfer + char16_t* serial = luXactLw.Serial(new_serialnumber); + if (luXactLw.Xfer (55/*Change serial number*/, ((uint32_t)serial[0]&0xFF) |(((uint32_t)serial[1]&0xFF)<<8) @@ -84,16 +83,4 @@ void change_serialnumber(const char* new_serialnumber) { } void test() { - LuXactDw luXactDw(serialnumber); - if (!luXactDw.Open()) - return; - - if (!luXactDw.ResetDw()) - return; - - for (;;) { - while (!SysConAvail()) { } - SysConRead(); - luXactDw.Send(0x55); - } } From 2a82e7ec4909ca2d798d1cb792b8ae984d60312b Mon Sep 17 00:00:00 2001 From: Peter Maivald Date: Fri, 17 Nov 2017 22:34:11 +0100 Subject: [PATCH 12/18] dwSendBytes as open drain --- usbtiny/main.c | 45 +++++---- usbtiny/main.hex | 243 ++++++++++++++++++++++++----------------------- 2 files changed, 151 insertions(+), 137 deletions(-) diff --git a/usbtiny/main.c b/usbtiny/main.c index 582bdc3..054a1a0 100644 --- a/usbtiny/main.c +++ b/usbtiny/main.c @@ -1305,8 +1305,8 @@ void dwCaptureWidths() { " clr r24 ; Counter for number of measured pulses \n" " \n" " cli ; Disable interrupts while measuring pulse widths \n" - " sbi 0x18,%[bit] ; End break \n" " cbi 0x17,%[bit] ; Set DDRB 5 (reset/dwire) direction input \n" + " sbi 0x18,%[bit] ; End break \n" " \n" "; Cycle loop - track a high pulse followed by a low pulse \n" " \n" @@ -1393,15 +1393,20 @@ void dwSendBytes() { "; r27:r26 - current buffer address (x) \n" "; r31:r30 - bit time counter \n" " \n" - "; Start with dwire pin as output in idle state \n" + "; Disable interrupts during transmission \n" + " \n" + " cli \n" + " \n" + "; Start with dwire pin as pullup in idle state \n" " \n" + " cbi 0x17,%[bit] ; DDRB pin5 is input \n" " sbi 0x18,%[bit] ; Make sure line is idle (high) \n" - " sbi 0x17,%[bit] ; DDRB pin5 is output \n" " \n" "; Preload registers \n" " \n" " lds r24,dwBitTime \n" " lds r25,dwBitTime+1 \n" + " sbiw r24,1 ; Decrement since we added 4 cycles \n" " ldi r26,lo8(dwBuf) ; X register addresses dwBuf \n" " ldi r27,hi8(dwBuf) \n" " \n" @@ -1409,39 +1414,45 @@ void dwSendBytes() { " tst r23 \n" " breq dws12 ; If no bytes to transmit \n" " \n" - "; Disable interrupts during transmission \n" - " \n" - " cli \n" - " \n" "; Loop for each byte \n" " \n" "; Send start bit (space / 0) \n" " \n" - "dws2: movw r30,r24 ; Load wait count to r31:r30 \n" - " cbi 0x18,%[bit] ; 1. Set dwire port low \n" + "; Start bit takes a total of 4*dwBitTime + 12 cycles. \n" + " \n" + "dws2: cbi 0x18,%[bit] ; Set dwire port low \n" + " sbi 0x17,%[bit] ; DDRB pin5 is output \n" + " rjmp . ; 2. \n" + " nop ; 1. \n" " \n" " ld r22,x+ ; 2. Next byte to send \n" " ldi r21,8 ; 1. Bit count \n" " \n" + " movw r30,r24 ; 1. Load wait count to r31:r30 \n" + " adiw r30,1 ; 2. Increment wait count \n" "dws4: sbiw r30,1 ; 2. Decrement wait count \n" " brne dws4 ; 1/2. While more waiting required \n" " \n" "; Loop for each bit \n" " \n" - "; Each bit takes a total of 4*dwBitTime + 8 cycles. \n" + "; Each bit takes a total of 4*dwBitTime + 12 cycles. \n" " \n" "dws6: sbrc r22,0 ; 1/2. Skip if sending zero \n" - " sbi 0x18,%[bit] ; 1. Set dwire port high \n" + " cbi 0x17,%[bit] ; 1. Set dwire ddr input \n" + " sbrc r22,0 ; 1/2. Skip if sending zero \n" + " sbi 0x18,%[bit] ; 1. Set dwire port pullup \n" " sbrs r22,0 ; 1/2. Skip if sending one \n" " cbi 0x18,%[bit] ; 1. Set dwire port low \n" + " sbrs r22,0 ; 1/2. Skip if sending one \n" + " sbi 0x17,%[bit] ; 1. Set dwire ddr output \n" " movw r30,r24 ; 1. Load wait count to r31:r30 \n" " \n" - "; 5 cycles from dws6 to here. \n" + "; 9 cycles from dws6 to here. \n" " \n" "dws8: sbiw r30,1 ; 2. Decrement wait count \n" " brne dws8 ; 1/2. While more waiting required \n" " \n" - "; 4*dwBitTime+4 cycles from dws6 to here. \n" + "; 4*dwBitTime+8 cycles from dws6 to here. \n" " \n" " lsr r22 ; 1. Shift next bit to bit 0 \n" " dec r21 ; 1. Count transmitted bit \n" @@ -1450,8 +1461,9 @@ void dwSendBytes() { "; Send stop bit (mark / 1) \n" " \n" " movw r30,r24 ; 1. Load wait count to r31:r30 \n" + " cbi 0x17,%[bit] ; 1. DDRB pin5 is input \n" " sbi 0x18,%[bit] ; 1. Set dwire port high \n" - " adiw r30,1 ; 2. Extra iteration \n" + " adiw r30,2 ; 2. Extra iteration \n" " \n" "dws10: sbiw r30,1 ; 2. Decrement wait count \n" " brne dws10 ; 1/2. While more waiting required \n" @@ -1459,7 +1471,7 @@ void dwSendBytes() { " dec r23 \n" " brne dws2 ; While more bytes to transmit \n" " \n" - "dws12: cbi 0x17,%[bit] ; DDRB pin5 is input \n" + "dws12: \n" " \n" ::[bit]"I"(DW_BIT):"r21","r22","r23","r24","r25","r26","r27","r30","r31"); } @@ -2027,7 +2039,8 @@ int main(void) { if (dwState & 0x34) {_delay_ms(2);} // Allow USB transfer to complete before // any action that may disable interrupts - if (dwState & 0x01) {cbi(PORTB, DW_BIT); sbi(DDRB, DW_BIT); _delay_ms(100);} + if (dwState & 0x01) {ATOMIC_BLOCK(ATOMIC_RESTORESTATE){ + cbi(PORTB, DW_BIT); sbi(DDRB, DW_BIT);} _delay_ms(100);} if (dwState & 0x02) {((char*)&dwBitTime)[0] = dwBuf[0]; ((char*)&dwBitTime)[1] = dwBuf[1];} if (dwState & 0x04) {dwSendBytes();} if (dwState & 0x08) {dwBuf[0]=dwState>>4; dwLen=dwBuf[0]?0:1; dwState=0; diff --git a/usbtiny/main.hex b/usbtiny/main.hex index 436b58b..c6f0594 100644 --- a/usbtiny/main.hex +++ b/usbtiny/main.hex @@ -1,16 +1,16 @@ -:1000000046C060C0B6C85EC05DC05CC05BC05AC0C0 -:1000100059C058C057C056C055C054C053C04BCB30 -:1000200072C9BEC9DCC901CA2CCA3FCA56CA95CA20 -:1000300096CA9ECAB5CA3DCB3CCB3BCB3ACB39CB5B -:10004000E5CA37CB36CBF2CA09021900010100809C +:1000000046C060C0BFC85EC05DC05CC05BC05AC0B7 +:1000100059C058C057C056C055C054C053C057CB24 +:100020007BC9C7C9E5C90ACA35CA48CA5FCA9ECAD8 +:100030009FCAA7CABECA49CB48CB47CB46CB45CB04 +:10004000EECA43CB42CBFBCA090219000101008072 :10005000640904000001FF00000007058103080097 :1000600064120110010000000881179F0C040100B8 :100070000203011603550053004200740069006E2C :1000800000790053005000490004030904001124C2 :100090001FBECFE5D2E0DEBFCDBF10E0A0E6B0E0EE -:1000A000E4E7F7E102C005900D92A037B107D9F758 +:1000A000ECE8F7E102C005900D92A037B107D9F74F :1000B00022E0A0E7B0E001C01D92AD31B207E1F748 -:1000C00096D856CB9DCFA82FB92F80E090E041E085 +:1000C0009FD862CB9DCFA82FB92F80E090E041E070 :1000D00050EA609530E009C02D9182279795879569 :1000E00010F084279527305EC8F36F5FA8F308955A :1000F000EADF8D939D930895A6E088279927AA9516 @@ -193,9 +193,9 @@ :100C00008D81EE5FF0E0EC58FF4F80838A8184FF96 :100C100096C080917200882309F491C081E180938D :100C2000340187B3842B87BB4093730088C027337C -:100C3000A1F480916D0090916E008CD580916D0033 -:100C400090916E006B81019685D580916D00909199 -:100C50006E006C8102967ED572C02C3391F588812E +:100C3000A1F480916D0090916E0098D580916D0027 +:100C400090916E006B81019691D580916D0090918D +:100C50006E006C8102968AD572C02C3391F5888122 :100C600087FD06C0611104C0AA9810928A0168C06D :100C700090918A01911163C087FF09C084E991E0D6 :100C800090936C0180936B019091890159C0AA984F @@ -218,7 +218,7 @@ :100D900013078CF0B1D98453994097FF03C0919504 :100DA000819591098E159F0514F4C1B77C0181B717 :100DB0008F5F81BFEACFC1BF61B780E090E0DF9174 -:100DC000CF911F910F91FF90EF90DF90C3C4CF930D +:100DC000CF911F910F91FF90EF90DF90CFC4CF9301 :100DD000DF93D82F882311F0B89801C0B89AC0E0EB :100DE0002C2F30E080914001909141012817390764 :100DF00028F481E090E07CDCCF5FF2CFBA98C0E0CD @@ -263,116 +263,117 @@ :10106000FF2011F080E001C081E0B1DED0E08D2FE3 :1010700090E08017910728F481E090E039DBDF5F92 :10108000F6CF8C2FDF91CF911F910F91FF90089594 -:10109000A4E9B1E08827F894C29ABA98EE27FF270E +:10109000A4E9B1E08827F894BA98C29AEE27FF270E :1010A000319688F0B299FCCF803468F4ED93FD93CB :1010B0008395EE27FF27319630F0B29BFCCFED935E -:1010C000FD938395EBCF880F809389010895C29A91 -:1010D000BA9A8091870190918801A4E9B1E070915A -:1010E00089017723C1F0F894FC01C2986D9158E012 -:1010F0003197F1F760FDC29A60FFC298FC01319709 -:10110000F1F766955A95B1F7FC01C29A319631977D -:10111000F1F77A9549F7BA98089577278091870172 -:1011200090918801A4E9B1E0EE27FF273197F1F013 -:10113000B299FCCFFC01F695E7953197F1F7B2999A -:10114000F3CF662758E0FC013197F1F76695B29925 -:10115000606800C05A95B9F76D937395EE27FF2725 -:10116000319721F0B29BFCCF7038F0F27093890177 -:101170000895AA9BD4C7B29B12C0AC9B1895AC989B -:1011800009D0F894AC9ACF93C0E0AA9BC0936D01AC -:10119000CF911895CF93CFB7CF937894C3C75F9370 -:1011A0005FB75F93AA9850919401552339F451E0A9 -:1011B000509394015F915FBF5F9118956F937F93F8 -:1011C0008F939F93AF93BF93EF93FF93559518F031 -:1011D000559518F003C0A1DF01C05ADFFF91EF91D0 -:1011E000BF91AF919F918F917F916F91E3CF80E2FB -:1011F00087BB88BB80E090E0A5D28F3F09F081BF1C -:10120000C0916D00D0916E00CE019CD2E82ECE012F -:10121000019698D2F82ECE01029694D2982F80EDA6 -:101220008E0D8A3008F07CC280ED8F0D8A3008F078 -:1012300077C280ED890F8A3008F072C2C0916D00CC -:10124000D0916E00CE017ED290E0909368008093A2 -:101250006700CE01019676D290E090936A00809369 -:101260006900CE0102966ED290E090936C0080935C -:101270006B00BB9AC4E18FE090E047DAC150D9F728 -:10128000BB9885E090E090934101809340011092DB -:101290006300109262009EE088E10FB6F894A89572 -:1012A00081BD0FBE91BD10923B0110923A01109288 -:1012B00039011092380110923701109236011092C4 -:1012C0003D0110923401BAD97894B898C098B99871 -:1012D000C198BA98C298BD98C598DAE130E4F32E67 -:1012E000C1E04BE1E42E20E8D22EA89576D8E0911B -:1012F00034018E2F90E08531910508F0DAC1FC01B0 -:10130000E15FFF4F0994B89AB998BA9AC29A1092BD -:101310008B018091140280933F01D0923E01809115 -:101320003E01882309F458C0C098B12C8B2D90E061 -:1013300020916200309163008217930728F481E0C6 -:1013400090E0D6D9B394F2CF90913E0180913F01C5 -:10135000892309F0C09AC29880918B01880F8093ED -:101360008B0196B320918B0191FB882780F9820F26 -:1013700080938B01B12C8B2D90E0209162003091F5 -:1013800063008217930728F481E090E0B1D9B39409 -:10139000F2CFC29A80913E01869580933E01BFCFE5 -:1013A000BA9AC29880EE91E0A3D9BFB6F894C29AD7 -:1013B00086E490E09DD9BA9886B382FB002700F9B5 -:1013C00010E0C80121E0822780938B01BFBE8AE92B -:1013D00091E08ED9BA9AC29AC09393016AC1809162 -:1013E000140280933E01BA9A98E0B92EAFB6F894F1 -:1013F000C29880913E0180FF07C086E090E078D9D6 -:10140000C29A80E490E006C08CE390E071D9C29A61 -:101410008AE090E06DD980913E01869580933E01EF -:10142000AFBEBA94B110E2CF44C110928B0188E0F4 -:10143000B82E80918B01869580938B01BA9AAFB6B6 -:10144000F894C29886E090E053D9C29A8AE090E07E -:101450004FD9BA9886B382FB882780F980933E01E2 -:10146000AFBE87E390E044D980913E01882329F004 -:1014700080918B01806880938B01BA94B110D9CF91 -:10148000ABCFBA9ABFB6F894C29886E090E030D954 -:10149000C29A8AE090E02CD9BA9886B382FB88275A -:1014A00080F980938B01BFBE97CFBA9ABFB6F894EC -:1014B000C2988091140280FF07C086E090E018D99E -:1014C000C29A80E490E006C08CE390E011D9C29A01 -:1014D0008AE090E00DD9BFBEECC0B99AB898BA9A2C -:1014E000C298E0923F01DDB9809114028111C59844 -:1014F00010923E01E0913E0180911502E81730F50F -:10150000F0E0EA5EFD4F80818FB9FEB880913F0127 -:101510008DB9B12C8B2D90E0209162003091630049 -:101520008217930728F481E090E0E2D8B394F2CFD9 -:10153000769BECCF80913E01E82FF0E09FB1E5571C -:10154000FE4F90838F5F80933E01D4CF8091140231 -:101550008111C59A80913E0156C0CBDCAAC080E0C3 -:1015600090E0DEDC8091140253DD80933E0115C0D3 -:10157000B12C80911402B81650F4EB2DF0E0EA5E25 -:10158000FD4F808145DD80933E01B394F2CF809181 -:1015900015028111FADC80913E0180938B011CCFF2 -:1015A00080911402811102C0B12C19C0912C292DF7 -:1015B00030E08091150290E00197A90145575E4FF8 -:1015C0005A012817390734F481E02FDDF5018083B3 -:1015D0009394EDCF80E029DDF50180830EC08091EA -:1015E0001502B81650F48B2C912C81E01EDDF4010D -:1015F000E557FE4F8083B394F2CF8091160281119C -:10160000C4DC809115028093930153C081E090E087 -:101610007CD8BFB6F8946091720070E0409173007E -:1016200084E790E019D91092720055CF80918A0119 -:10163000847319F082E090E068D880918A0180FF7D -:1016400005C0C298BA9A84E690E05FD880918A017A -:1016500081FF08C08091940180938701809195015A -:101660008093880180918A0182FD31DD80918A0119 -:1016700083FF11C080918A0182958F7080939401BD -:101680009091940181E0911180E080938901109202 -:101690008A01BA98AA9A80918A0184FD3EDD8091E0 -:1016A0008A0185FDF5DC1092340110928A0178944C -:1016B00002C01092340180913C01882309F415CEB8 -:1016C00080913D0181110DC08091380180933B01D3 -:1016D0008091370180933A018091360180933901DE -:1016E00019C090913B01981710F4C09801C0C09A9E -:1016F00090913D0180913A01891710F4C19801C081 -:10170000C19A90913D0180913901891710F4C298D6 -:1017100001C0C29A80913D018F5F80933D01E3CD6E -:1017200065E3CE0117D080916D0090916E0061E36A -:10173000019610D080916D0090916E0062E3029648 -:1017400009D07CCDE199FECF9FBB8EBBE09A992753 -:101750008DB30895262FE199FECF1CBA9FBB8EBB97 -:101760002DBB0FB6F894E29AE19A0FBE0196089548 -:04177000F894FFCF1B -:10177400FF5A0A000A080335003100320020000035 +:1010C000FD938395EBCF880F809389010895F89461 +:1010D000BA98C29A80918701909188010197A4E9FA +:1010E000B1E070918901772301F1C298BA9A00C0EA +:1010F00000006D9158E0FC0131963197F1F760FDE9 +:10110000BA9860FDC29A60FFC29860FFBA9AFC016B +:101110003197F1F766955A9591F7FC01BA98C29A02 +:1011200032963197F1F77A9501F7089577278091F4 +:10113000870190918801A4E9B1E0EE27FF2731975C +:10114000F1F0B299FCCFFC01F695E7953197F1F7F4 +:10115000B299F3CF662758E0FC013197F1F7669515 +:10116000B299606800C05A95B9F76D937395EE27F0 +:10117000FF27319721F0B29BFCCF7038F0F27093CB +:1011800089010895AA9BCBC7B29B12C0AC9B18954E +:10119000AC9809D0F894AC9ACF93C0E0AA9BC093C6 +:1011A0006D01CF911895CF93CFB7CF937894BAC7ED +:1011B0005F935FB75F93AA9850919401552339F4D8 +:1011C00051E0509394015F915FBF5F9118956F93C9 +:1011D0007F938F939F93AF93BF93EF93FF93559517 +:1011E00018F0559518F003C0A1DF01C051DFFF9141 +:1011F000EF91BF91AF919F918F917F916F91E3CFCD +:1012000080E287BB88BB80E090E0A8D28F3F09F0E6 +:1012100081BFC0916D00D0916E00CE019FD2E82EAB +:10122000CE0101969BD2F82ECE01029697D2982F2E +:1012300080ED8E0D8A3008F07FC280ED8F0D8A30F0 +:1012400008F07AC280ED890F8A3008F075C2C0912B +:101250006D00D0916E00CE0181D290E09093680035 +:1012600080936700CE01019679D290E090936A0056 +:1012700080936900CE01029671D290E090936C0049 +:1012800080936B00BB9AC4E18FE090E03EDAC150DE +:10129000D9F7BB9885E090E090934101809340019D +:1012A00010926300109262009EE088E10FB6F894FD +:1012B000A89581BD0FBE91BD10923B0110923A01DD +:1012C00010923901109238011092370110923601B4 +:1012D00010923D0110923401B1D97894B898C09819 +:1012E000B998C198BA98C298BD98C598DAE130E427 +:1012F000F32EC1E04BE1E42E20E8D22EA8956DD864 +:10130000E09134018E2F90E08531910508F0DDC128 +:10131000FC01E15FFF4F0994B89AB998BA9AC29A52 +:1013200010928B018091140280933F01D0923E0174 +:1013300080913E01882309F458C0C098B12C8B2DB0 +:1013400090E020916200309163008217930728F4A7 +:1013500081E090E0CDD9B394F2CF90913E0180919D +:101360003F01892309F0C09AC29880918B01880FB0 +:1013700080938B0196B320918B0191FB882780F994 +:10138000820F80938B01B12C8B2D90E02091620015 +:10139000309163008217930728F481E090E0A8D988 +:1013A000B394F2CFC29A80913E01869580933E011C +:1013B000BFCFBA9AC29880EE91E09AD9BFB6F8949E +:1013C000C29A86E490E094D9BA9886B382FB00274B +:1013D00000F910E0C80121E0822780938B01BFBE95 +:1013E0008AE991E085D9BA9AC29AC09393016DC1F6 +:1013F0008091140280933E01BA9A98E0B92EAFB65C +:10140000F894C29880913E0180FF07C086E090E08A +:101410006FD9C29A80E490E006C08CE390E068D96E +:10142000C29A8AE090E064D980913E0186958093CB +:101430003E01AFBEBA94B110E2CF47C110928B010A +:1014400088E0B82E80918B01869580938B01BA9AA3 +:10145000AFB6F894C29886E090E04AD9C29A8AE082 +:1014600090E046D9BA9886B382FB882780F98093AA +:101470003E01AFBE87E390E03BD980913E018823D7 +:1014800029F080918B01806880938B01BA94B11010 +:10149000D9CFABCFBA9ABFB6F894C29886E090E0A5 +:1014A00027D9C29A8AE090E023D9BA9886B382FB02 +:1014B000882780F980938B01BFBE97CFBA9ABFB6B9 +:1014C000F894C2988091140280FF07C086E090E0F3 +:1014D0000FD9C29A80E490E006C08CE390E008D96E +:1014E000C29A8AE090E004D9BFBEEFC0B99AB8981A +:1014F000BA9AC298E0923F01DDB98091140281113D +:10150000C59810923E01E0913E0180911502E817C6 +:1015100030F5F0E0EA5EFD4F80818FB9FEB8809132 +:101520003F018DB9B12C8B2D90E02091620030915C +:1015300063008217930728F481E090E0D9D8B39430 +:10154000F2CF769BECCF80913E01E82FF0E09FB187 +:10155000E557FE4F90838F5F80933E01D4CF8091FB +:1015600014028111C59A80913E0156C0C2DCADC003 +:1015700080E090E0D5DC809114024ADD80933E014A +:1015800015C0B12C80911402B81650F4EB2DF0E088 +:10159000EA5EFD4F80813CDD80933E01B394F2CF43 +:1015A000809115028111F1DC80913E0180938B01C5 +:1015B0001CCF80911402811102C0B12C19C0912C52 +:1015C000292D30E08091150290E00197A90145573F +:1015D0005E4F5A012817390734F481E026DDF50102 +:1015E00080839394EDCF80E020DDF50180830EC0F1 +:1015F00080911502B81650F48B2C912C81E015DDEA +:10160000F401E557FE4F8083B394F2CF8091160228 +:101610008111BBDC809115028093930156C081E05B +:1016200090E073D8BFB6F8946091720070E040917A +:10163000730084E790E010D91092720055CF80912A +:101640008A01847319F082E090E05FD880918A016A +:1016500080FF08C08FB7F894C298BA9A8FBF84E60B +:1016600090E053D880918A0181FF08C08091940155 +:1016700080938701809195018093880180918A01F0 +:1016800082FD25DD80918A0183FF11C080918A014E +:1016900082958F70809394019091940181E09111D3 +:1016A00080E08093890110928A01BA98AA9A809169 +:1016B0008A0184FD3BDD80918A0185FDE9DC109281 +:1016C000340110928A01789402C010923401809102 +:1016D0003C01882309F412CE80913D0181110DC097 +:1016E0008091380180933B018091370180933A01CA +:1016F000809136018093390119C090913B01981770 +:1017000010F4C09801C0C09A90913D0180913A01B7 +:10171000891710F4C19801C0C19A90913D01809140 +:101720003901891710F4C29801C0C29A80913D0115 +:101730008F5F80933D01E0CD65E3CE0117D08091AE +:101740006D0090916E0061E3019610D080916D0064 +:1017500090916E0062E3029609D079CDE199FECFB7 +:101760009FBB8EBBE09A99278DB30895262FE199F0 +:10177000FECF1CBA9FBB8EBB2DBB0FB6F894E29A6E +:0C178000E19A0FBE01960895F894FFCF87 +:10178C00FF5A0A000A08033500310032002000001D :00000001FF From 0281a8984890a7273349ea8c31deedb34dc87b37 Mon Sep 17 00:00:00 2001 From: Peter Maivald Date: Sat, 18 Nov 2017 22:52:27 +0100 Subject: [PATCH 13/18] improve timing --- example/echodw/uartsoft.cpp | 49 ++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/example/echodw/uartsoft.cpp b/example/echodw/uartsoft.cpp index 05f7448..22d7ac0 100644 --- a/example/echodw/uartsoft.cpp +++ b/example/echodw/uartsoft.cpp @@ -143,8 +143,8 @@ void UARTSOFT_INT(UARTSOFT_ARGS)() { // working registers declared as early clobbers register uint8_t delay asm("r25"); - register uint8_t ibit asm("r23"); - register uint8_t idel asm("r22"); + register uint8_t ibit asm("r29"); // also high part of idel + register uint8_t idel asm("r28"); register uint8_t ibuf asm("r21"); register uint8_t recv asm("r24"); register uint16_t x asm("r26"); @@ -158,44 +158,38 @@ void UARTSOFT_INT(UARTSOFT_ARGS)() { push r27 ;2 07 push r26 ;2 09 push r25 ;2 11 - push r23 ;2 13 - push r22 ;2 15 + push r29 ;2 13 + push r28 ;2 15 push r21 ;2 17 lds %A[x],%[next] ;2 19 buffer lds %B[x],%[next]+1 ;2 21 ldi %[ibuf],%[size]-1 ;1 22 max number of bytes - ; intra bit delay (divisor - 8) >> 2 + ; inter bit delay (divisor - 8) >> 2 lds %[delay],%[divisor] ;2 24 subi %[delay],8 ;1 25 delay till next bit lsr %[delay] ;1 26 lsr %[delay] ;1 27 -readbyte%=: - ldi %[ibit],8 ;1 number of timeout bits -waitstart%=: - mov %[idel],%[delay] ;1 4*idel timeout -loopstart%=: ; - dec %[idel] ;1 - sbic %[pin],%[bit] ;? - brne loopstart%= ;? 4*idel end - +readbyte%=:; timeout = 2 * 6 * bit time + lds %[idel],%[divisor] ;2 07 one bit time + ldi %[ibit],0 ;1 08 high part of timeout + lsl %[idel] ;1 09 *2 + adc %[ibit],%[ibit] ;1 10 +loopstart%=: ; 6*idel timeout sbis %[pin],%[bit] ;? rjmp havestart%= ;2 - dec %[ibit] ;1 - brne waitstart%= ;? + sbiw %[idel],1 ;2 + brne loopstart%= ;? 6*idel end rjmp haveall%= ;2 timeout for start bit - havestart%=: - mov %[idel],%[delay] ;1 1.5 bits + mov %[idel],%[delay] ;1 0.5 bits lsr %[idel] ;1 delays%=: ; wait for 1st bit dec %[idel] ;1 - nop ;1 - brne delays%= ;? - ldi %[ibit],8 ;1 number of data bits + brne delays%= ;? 4*idel wait loop%=: ; critical loop timing 4*delay + 8 mov %[idel],%[delay] ;1 delay%=: @@ -210,16 +204,15 @@ delay%=: dec %[ibit] ;1 06 brne loop%= ;? 08 more bits? - mov %[idel],%[delay] ;1 .5 bits - lsr %[idel] ;1 + mov %[idel],%[delay] ;1 1 bit delayt%=: ; wait past last bit dec %[idel] ;1 00 nop ;1 01 brne delayt%= ;? 4*delay - st X+,%[recv] ;2 store byte - dec %[ibuf] ;1 00 - brne readbyte%= ;? + st X+,%[recv] ;2 00 store byte + dec %[ibuf] ;1 03 + brne readbyte%= ;? 05 ; buffer full haveall%=: ldi %[recv],%[size]-1 ;1 max number of bytes @@ -233,8 +226,8 @@ haveall%=: sts %[next]+1,r25 pop r21 ;2 - pop r22 ;2 - pop r23 ;2 + pop r28 ;2 + pop r29 ;2 pop r25 ;2 pop r26 ;2 pop r27 ;2 From d0bc0bfb4bc12b1a4c82d4bcd534d4c1ddd9a179 Mon Sep 17 00:00:00 2001 From: Peter Maivald Date: Mon, 20 Nov 2017 12:49:39 +0100 Subject: [PATCH 14/18] fix hang during heavy usb traffic --- usbtiny/main.c | 14 +- usbtiny/main.hex | 436 +++++++++++++++++++++++------------------------ 2 files changed, 225 insertions(+), 225 deletions(-) diff --git a/usbtiny/main.c b/usbtiny/main.c index 054a1a0..88d19b4 100644 --- a/usbtiny/main.c +++ b/usbtiny/main.c @@ -996,14 +996,14 @@ uchar usbFunctionSetup(uchar data[8]) if (req == 60) { // debugWIRE transfer - if (!(data[0] & 0x80) && !data[2]) { - // command 0 always cancels prior operation - cbi(PCMSK,DW_BIT); - dwState = 0; - return 0; - } - if (dwState) {return 0;} // Prior operation has not yet completed + // It is possible for usbFunctionSetup to receive a valid setup packet, setting + // dwState below, followed by a lost data packet leading to usbFunctionDwWrite + // never setting jobState. This happens frequently when there is heavy traffic to + // other devices on a shared hub, and leads to jobState == 0 && dwState != 0 once + // we get to this point. This is an invalid state, not a pending operation, so + // we should alllow subsequent operations to proceed rather than hang. + if (jobState && dwState) {return 0;} // Prior operation has not yet completed if (data[0] & 0x80) { diff --git a/usbtiny/main.hex b/usbtiny/main.hex index c6f0594..5e87a9e 100644 --- a/usbtiny/main.hex +++ b/usbtiny/main.hex @@ -1,16 +1,16 @@ -:1000000046C060C0BFC85EC05DC05CC05BC05AC0B7 -:1000100059C058C057C056C055C054C053C057CB24 -:100020007BC9C7C9E5C90ACA35CA48CA5FCA9ECAD8 -:100030009FCAA7CABECA49CB48CB47CB46CB45CB04 -:10004000EECA43CB42CBFBCA090219000101008072 +:1000000046C060C0BBC85EC05DC05CC05BC05AC0BB +:1000100059C058C057C056C055C054C053C053CB28 +:1000200077C9C3C9E1C906CA31CA44CA5BCA9ACAF8 +:100030009BCAA3CABACA45CB44CB43CB42CB41CB24 +:10004000EACA3FCB3ECBF7CA090219000101008082 :10005000640904000001FF00000007058103080097 :1000600064120110010000000881179F0C040100B8 :100070000203011603550053004200740069006E2C :1000800000790053005000490004030904001124C2 :100090001FBECFE5D2E0DEBFCDBF10E0A0E6B0E0EE -:1000A000ECE8F7E102C005900D92A037B107D9F74F +:1000A000E4E8F7E102C005900D92A037B107D9F757 :1000B00022E0A0E7B0E001C01D92AD31B207E1F748 -:1000C0009FD862CB9DCFA82FB92F80E090E041E070 +:1000C0009BD85ECB9DCFA82FB92F80E090E041E078 :1000D00050EA609530E009C02D9182279795879569 :1000E00010F084279527305EC8F36F5FA8F308955A :1000F000EADF8D939D930895A6E088279927AA9516 @@ -97,7 +97,7 @@ :1006000080936000CEE1C093610084E196B398715D :1006100031F48150D9F710926E0110926801C1E057 :100620008111C0E0809170008C1729F0C11101C0C8 -:100630008BD3C0937000DF91CF910895AC9A8BB7A4 +:1006300087D3C0937000DF91CF910895AC9A8BB7A8 :1006400080628BBFECE5F1E08BE481838AE58083F7 :100650000895FC01DB01AC014C5F5F4F619128E024 :1006600030E080E890E0782F762309F0C09A70910E @@ -137,243 +137,243 @@ :1008800028BB00C0000028BB21F000C000C000C091 :10089000F2CFEACF0895CF93DF93EC0129812111A4 :1008A00008C081E28983D0936C01C0936B0198E00A -:1008B00047C2213011F486B30BC16A81223011F492 -:1008C00068BB3DC2962F362F377041E050E0032EB3 +:1008B00043C2213011F486B30BC16A81223011F496 +:1008C00068BB39C2962F362F377041E050E0032EB7 :1008D00002C0440F551F0A94E2F7233009F496C072 -:1008E000243021F488B3482B48BB29C2253051F469 +:1008E000243021F488B3482B48BB25C2253051F46D :1008F00060936400B99887B3856287BB88B3887DAD -:1009000088BB1DC2263049F4B898C098B998C198E0 -:10091000BA98C298BD98C59812C2273051F4BE014A +:1009000088BB19C2263049F4B898C098B998C198E4 +:10091000BA98C298BD98C5980EC2273051F4BE014E :10092000CE01029696DED0936C01C0936B0194E0E9 -:1009300007C2283031F4609350018B8180934F01BE -:10094000FEC1EC81FD81F0934E01E0934D01293011 +:1009300003C2283031F4609350018B8180934F01C2 +:10094000FAC1EC81FD81F0934E01E0934D01293015 :1009500011F480E203C02B3021F480EA80934A0135 -:10096000AEC1EA81FB81F0934C01E0934B012A3048 +:10096000AAC1EA81FB81F0934C01E0934B012A304C :1009700011F480E4F3CF2C3011F480ECEFCF2D3064 :1009800021F487B34095482304C02E3021F487B367 -:10099000482B47BBD4C12F3009F5611105C08091A8 +:10099000482B47BBD0C12F3009F5611105C08091AC :1009A000350187B9A59A0CC0613031F480913501C9 :1009B000816087B9A29A04C0623011F48FE887B9C8 :1009C000369A3699FECF84B1888385B18983D093D6 -:1009D0006C01C0936B0114BA92E0B2C1203159F49A +:1009D0006C01C0936B0114BA92E0AEC1203159F49E :1009E000B89AB99A8AB5806A8ABD8AB583608ABD89 :1009F00083B7856044C0213121F469BD8C8188BDF5 -:100A00009EC1223109F46ECF233121F488B3409581 +:100A00009AC1223109F46ECF233121F488B3409585 :100A100048236ACF243159F486B390E04823592300 :100A200002C0559547953A95E2F7488352C0263162 :100A3000A1F561110AC083B78B7F83BF83B78D7F18 -:100A400083BF83B7816083BF7BC1613031F483B7DB +:100A400083BF83B7816083BF77C1613031F483B7DF :100A50008B7F83BF83B782600FC0623031F483B76E :100A60008B7F83BF83B7826014C0633051F483B738 :100A7000846083BF83B78D7F83BF83B78E7F83BF3F -:100A80005EC1643009F05BC183B7846083BF83B704 +:100A80005AC1643009F057C183B7846083BF83B70C :100A90008D7F83BF83B78160F2CF2F3159F48B8173 :100AA00090E0982F8827860F911D90936300809384 -:100AB000620045C1203219F41ABC13BE40C1213274 +:100AB000620041C1203219F41ABC13BE3CC121327C :100AC00021F46093140281E07FC0223241F483E17B -:100AD0008883D0936C01C0936B0191E031C12332C4 +:100AD0008883D0936C01C0936B0191E02DC12332C8 :100AE000C9F4379A96B18A81892B86B99B81913056 -:100AF00059F038F0923009F022C1809135018069B7 -:100B000006C0109235011CC1809135018068809328 -:100B1000350115C1283249F48BE891E090936C01BE -:100B200080936B01909193010BC1293211F482E003 +:100AF00059F038F0923009F01EC1809135018069BB +:100B000006C01092350118C180913501806880932C +:100B1000350111C1283249F48BE891E090936C01C2 +:100B200080936B019091930107C1293211F482E007 :100B30004BC02A3211F483E00BC02B3211F484E055 :100B400043C02C3211F488E03FC02D3231F489E0EB -:100B50008093340160931402F2C02E3261F48BE072 +:100B50008093340160931402EEC02E3261F48BE076 :100B600080933401609314028B81809315028C81F1 -:100B700080931602E4C02F3259F4613031F4B89AF0 -:100B8000B99ABA9A60933C01DAC010923C01D7C07E +:100B700080931602E0C02F3259F4613031F4B89AF4 +:100B8000B99ABA9A60933C01D6C010923C01D3C086 :100B9000203349F4609338018B81809337018C8135 -:100BA00080933601CCC0213331F470E07093410161 -:100BB00060934001C4C0223311F485E005C02333A3 -:100BC00031F46093140286E080933401B8C0263378 +:100BA00080933601C8C0213331F470E07093410165 +:100BB00060934001C0C0223311F485E005C02333A7 +:100BC00031F46093140286E080933401B4C026337C :100BD00071F565FF1BC0E0917200E03CB8F48B81B9 :100BE000AE2FB0E0AC58BF4F8C938C81A1E0AE0F1C :100BF000B0E0AC58BF4F8C9383E08E0F80937200AF :100C00008D81EE5FF0E0EC58FF4F80838A8184FF96 -:100C100096C080917200882309F491C081E180938D -:100C2000340187B3842B87BB4093730088C027337C -:100C3000A1F480916D0090916E0098D580916D0027 -:100C400090916E006B81019691D580916D0090918D -:100C50006E006C8102968AD572C02C3391F5888122 -:100C600087FD06C0611104C0AA9810928A0168C06D -:100C700090918A01911163C087FF09C084E991E0D6 -:100C800090936C0180936B019091890159C0AA984F -:100C90008A8180938A018E81809389019091890154 -:100CA000911104C084E1809334014AC0809189018C -:100CB000813818F080E88093890110921C029FEF20 -:100CC0003FC0822F807F803EE1F48AE08093340130 -:100CD000822F87708093140228702093150220E0E1 -:100CE00080911402281758F5822F90E00296FE0199 -:100CF000E80FF91F3081FC01EC5EFD4F30832F5F60 -:100D0000EFCF803FE1F487E080933401822F887039 -:100D10008093140227702093150220E08091150221 -:100D2000281768F4822F90E00296FE01E80FF91F61 -:100D30003081FC01EC5EFD4F30832F5FEFCF90E000 -:100D4000892FDF91CF910895DF92EF92FF920F9359 -:100D50001F93CF93DF9308E010E0C0E0D0E8DD2ED2 -:100D6000DC0ED1BEC9D97C0184E3E81689E0F8061F -:100D70000CF4CD2DD6950150110989F78FEF8C0F0A -:100D800081BF0C2F10E00F5F1F4F21B730E002171B -:100D900013078CF0B1D98453994097FF03C0919504 -:100DA000819591098E159F0514F4C1B77C0181B717 -:100DB0008F5F81BFEACFC1BF61B780E090E0DF9174 -:100DC000CF911F910F91FF90EF90DF90CFC4CF9301 -:100DD000DF93D82F882311F0B89801C0B89AC0E0EB -:100DE0002C2F30E080914001909141012817390764 -:100DF00028F481E090E07CDCCF5FF2CFBA98C0E0CD -:100E00002C2F30E080914001909141012817390743 -:100E100028F481E090E06CDCCF5FF2CFBA9AC0E0BA -:100E20002C2F30E080914001909141012817390723 -:100E300028F481E090E05CDCCF5FF2CFD111B89A6A -:100E4000C0E08C2F90E02091400130914101821749 -:100E5000930728F481E090E04BDCCF5FF2CFDF9185 -:100E6000CF9108950F931F93CF93DF93B898C0E06D -:100E70008C2F90E02091400130914101821793071F -:100E800028F481E090E034DCCF5FF2CFBA98C0E084 -:100E900000914001109141018C2F90E08017910743 -:100EA00028F481E090E024DCCF5FF2CFD6B3C0E03D -:100EB0008C2F90E08017910728F481E090E018DCF7 -:100EC000CF5FF6CFBA9AC0E02C2F30E0809140017E -:100ED000909141012817390728F481E090E008DC5F -:100EE000CF5FF2CF8D2F8170DF91CF911F910F9146 -:100EF0000895CF9388B38A7F88BBBA98B898C0E02A -:100F00008C2F90E02091400130914101821793078E -:100F100028F481E090E0ECDBCF5FF2CFCF91089531 -:100F2000CF9387B38A7F87BBC0E08C2F90E020915E -:100F30004001309141018217930728F481E090E04D -:100F4000D7DBCF5FF2CFB89AC0E08C2F90E0209132 -:100F50004001309141018217930728F481E090E02D -:100F6000C7DBCF5FF2CFBA9AC0E08C2F90E0209120 -:100F70004001309141018217930728F481E090E00D -:100F8000B7DBCF5FF2CFCF910895CF93B89AC0E08F -:100F90008C2F90E0209140013091410182179307FE -:100FA00028F481E090E0A4DBCF5FF2CFBA9AC0E0F2 -:100FB0008C2F90E0209140013091410182179307DE -:100FC00028F481E090E094DBCF5FF2CFBA98C0E0E4 -:100FD0008C2F90E0209140013091410182179307BE -:100FE00028F481E090E084DBCF5FF2CFB898C0E0D6 -:100FF0008C2F90E02091400130914101821793079E -:1010000028F481E090E074DBCF5FF2CFCF910895B8 -:10101000CF93DF93D82FC8E08D2F8078D8DEDD0FF7 -:10102000C150D1F7DF91CF911DCFFF920F931F9346 -:10103000CF93DF93F82ED8E0C0E0CC0F13DFC82B9E -:10104000D150D9F700914001109141018D2F90E0CE -:101050008017910728F481E090E04ADBDF5FF2CF50 -:10106000FF2011F080E001C081E0B1DED0E08D2FE3 -:1010700090E08017910728F481E090E039DBDF5F92 -:10108000F6CF8C2FDF91CF911F910F91FF90089594 -:10109000A4E9B1E08827F894BA98C29AEE27FF270E -:1010A000319688F0B299FCCF803468F4ED93FD93CB -:1010B0008395EE27FF27319630F0B29BFCCFED935E -:1010C000FD938395EBCF880F809389010895F89461 -:1010D000BA98C29A80918701909188010197A4E9FA -:1010E000B1E070918901772301F1C298BA9A00C0EA -:1010F00000006D9158E0FC0131963197F1F760FDE9 -:10110000BA9860FDC29A60FFC29860FFBA9AFC016B -:101110003197F1F766955A9591F7FC01BA98C29A02 -:1011200032963197F1F77A9501F7089577278091F4 -:10113000870190918801A4E9B1E0EE27FF2731975C -:10114000F1F0B299FCCFFC01F695E7953197F1F7F4 -:10115000B299F3CF662758E0FC013197F1F7669515 -:10116000B299606800C05A95B9F76D937395EE27F0 -:10117000FF27319721F0B29BFCCF7038F0F27093CB -:1011800089010895AA9BCBC7B29B12C0AC9B18954E -:10119000AC9809D0F894AC9ACF93C0E0AA9BC093C6 -:1011A0006D01CF911895CF93CFB7CF937894BAC7ED -:1011B0005F935FB75F93AA9850919401552339F4D8 -:1011C00051E0509394015F915FBF5F9118956F93C9 -:1011D0007F938F939F93AF93BF93EF93FF93559517 -:1011E00018F0559518F003C0A1DF01C051DFFF9141 -:1011F000EF91BF91AF919F918F917F916F91E3CFCD -:1012000080E287BB88BB80E090E0A8D28F3F09F0E6 -:1012100081BFC0916D00D0916E00CE019FD2E82EAB -:10122000CE0101969BD2F82ECE01029697D2982F2E -:1012300080ED8E0D8A3008F07FC280ED8F0D8A30F0 -:1012400008F07AC280ED890F8A3008F075C2C0912B -:101250006D00D0916E00CE0181D290E09093680035 -:1012600080936700CE01019679D290E090936A0056 -:1012700080936900CE01029671D290E090936C0049 -:1012800080936B00BB9AC4E18FE090E03EDAC150DE -:10129000D9F7BB9885E090E090934101809340019D -:1012A00010926300109262009EE088E10FB6F894FD -:1012B000A89581BD0FBE91BD10923B0110923A01DD -:1012C00010923901109238011092370110923601B4 -:1012D00010923D0110923401B1D97894B898C09819 -:1012E000B998C198BA98C298BD98C598DAE130E427 -:1012F000F32EC1E04BE1E42E20E8D22EA8956DD864 -:10130000E09134018E2F90E08531910508F0DDC128 -:10131000FC01E15FFF4F0994B89AB998BA9AC29A52 -:1013200010928B018091140280933F01D0923E0174 -:1013300080913E01882309F458C0C098B12C8B2DB0 -:1013400090E020916200309163008217930728F4A7 -:1013500081E090E0CDD9B394F2CF90913E0180919D -:101360003F01892309F0C09AC29880918B01880FB0 -:1013700080938B0196B320918B0191FB882780F994 -:10138000820F80938B01B12C8B2D90E02091620015 -:10139000309163008217930728F481E090E0A8D988 -:1013A000B394F2CFC29A80913E01869580933E011C -:1013B000BFCFBA9AC29880EE91E09AD9BFB6F8949E -:1013C000C29A86E490E094D9BA9886B382FB00274B -:1013D00000F910E0C80121E0822780938B01BFBE95 -:1013E0008AE991E085D9BA9AC29AC09393016DC1F6 -:1013F0008091140280933E01BA9A98E0B92EAFB65C -:10140000F894C29880913E0180FF07C086E090E08A -:101410006FD9C29A80E490E006C08CE390E068D96E -:10142000C29A8AE090E064D980913E0186958093CB -:101430003E01AFBEBA94B110E2CF47C110928B010A -:1014400088E0B82E80918B01869580938B01BA9AA3 -:10145000AFB6F894C29886E090E04AD9C29A8AE082 -:1014600090E046D9BA9886B382FB882780F98093AA -:101470003E01AFBE87E390E03BD980913E018823D7 -:1014800029F080918B01806880938B01BA94B11010 -:10149000D9CFABCFBA9ABFB6F894C29886E090E0A5 -:1014A00027D9C29A8AE090E023D9BA9886B382FB02 -:1014B000882780F980938B01BFBE97CFBA9ABFB6B9 -:1014C000F894C2988091140280FF07C086E090E0F3 -:1014D0000FD9C29A80E490E006C08CE390E008D96E -:1014E000C29A8AE090E004D9BFBEEFC0B99AB8981A -:1014F000BA9AC298E0923F01DDB98091140281113D -:10150000C59810923E01E0913E0180911502E817C6 -:1015100030F5F0E0EA5EFD4F80818FB9FEB8809132 -:101520003F018DB9B12C8B2D90E02091620030915C -:1015300063008217930728F481E090E0D9D8B39430 -:10154000F2CF769BECCF80913E01E82FF0E09FB187 -:10155000E557FE4F90838F5F80933E01D4CF8091FB -:1015600014028111C59A80913E0156C0C2DCADC003 -:1015700080E090E0D5DC809114024ADD80933E014A -:1015800015C0B12C80911402B81650F4EB2DF0E088 -:10159000EA5EFD4F80813CDD80933E01B394F2CF43 -:1015A000809115028111F1DC80913E0180938B01C5 -:1015B0001CCF80911402811102C0B12C19C0912C52 -:1015C000292D30E08091150290E00197A90145573F -:1015D0005E4F5A012817390734F481E026DDF50102 -:1015E00080839394EDCF80E020DDF50180830EC0F1 -:1015F00080911502B81650F48B2C912C81E015DDEA -:10160000F401E557FE4F8083B394F2CF8091160228 -:101610008111BBDC809115028093930156C081E05B -:1016200090E073D8BFB6F8946091720070E040917A -:10163000730084E790E010D91092720055CF80912A -:101640008A01847319F082E090E05FD880918A016A -:1016500080FF08C08FB7F894C298BA9A8FBF84E60B -:1016600090E053D880918A0181FF08C08091940155 -:1016700080938701809195018093880180918A01F0 -:1016800082FD25DD80918A0183FF11C080918A014E -:1016900082958F70809394019091940181E09111D3 -:1016A00080E08093890110928A01BA98AA9A809169 -:1016B0008A0184FD3BDD80918A0185FDE9DC109281 -:1016C000340110928A01789402C010923401809102 -:1016D0003C01882309F412CE80913D0181110DC097 -:1016E0008091380180933B018091370180933A01CA -:1016F000809136018093390119C090913B01981770 -:1017000010F4C09801C0C09A90913D0180913A01B7 -:10171000891710F4C19801C0C19A90913D01809140 -:101720003901891710F4C29801C0C29A80913D0115 -:101730008F5F80933D01E0CD65E3CE0117D08091AE -:101740006D0090916E0061E3019610D080916D0064 -:1017500090916E0062E3029609D079CDE199FECFB7 -:101760009FBB8EBBE09A99278DB30895262FE199F0 -:10177000FECF1CBA9FBB8EBB2DBB0FB6F894E29A6E -:0C178000E19A0FBE01960895F894FFCF87 -:10178C00FF5A0A000A08033500310032002000001D +:100C100092C080917200882309F48DC081E1809395 +:100C2000340187B3842B87BB4093730084C0273380 +:100C3000A1F480916D0090916E0094D580916D002B +:100C400090916E006B8101968DD580916D00909191 +:100C50006E006C81029686D56EC02C3371F5809142 +:100C60003401882321F080918A01811164C0888138 +:100C700087FF09C084E991E090936C0180936B0138 +:100C80009091890159C0AA988A8180938A018E81A6 +:100C90008093890190918901911104C084E180932E +:100CA00034014AC080918901813818F080E880932E +:100CB000890110921C029FEF3FC0822F807F803EEF +:100CC000E1F48AE080933401822F877080931402CC +:100CD00028702093150220E080911402281758F5FF +:100CE000822F90E00296FE01E80FF91F3081FC018F +:100CF000EC5EFD4F30832F5FEFCF803FE1F487E064 +:100D000080933401822F887080931402277020937F +:100D1000150220E080911502281768F4822F90E0D8 +:100D20000296FE01E80FF91F3081FC01EC5EFD4FD9 +:100D300030832F5FEFCF90E0892FDF91CF9108951F +:100D4000DF92EF92FF920F931F93CF93DF9308E010 +:100D500010E0C0E0D0E8DD2EDC0ED1BECDD97C01A4 +:100D600084E3E81689E0F8060CF4CD2DD695015001 +:100D7000110989F78FEF8C0F81BF0C2F10E00F5FE7 +:100D80001F4F21B730E0021713078CF0B5D98453F9 +:100D9000994097FF03C09195819591098E159F0504 +:100DA00014F4C1B77C0181B78F5F81BFEACFC1BFA7 +:100DB00061B780E090E0DF91CF911F910F91FF909C +:100DC000EF90DF90CFC4CF93DF93D82F882311F01B +:100DD000B89801C0B89AC0E02C2F30E08091400153 +:100DE000909141012817390728F481E090E080DCD8 +:100DF000CF5FF2CFBA98C0E02C2F30E08091400155 +:100E0000909141012817390728F481E090E070DCC7 +:100E1000CF5FF2CFBA9AC0E02C2F30E08091400132 +:100E2000909141012817390728F481E090E060DCB7 +:100E3000CF5FF2CFD111B89AC0E08C2F90E0209113 +:100E40004001309141018217930728F481E090E03E +:100E50004FDCCF5FF2CFDF91CF9108950F931F93B7 +:100E6000CF93DF93B898C0E08C2F90E020914001A1 +:100E7000309141018217930728F481E090E038DC3B +:100E8000CF5FF2CFBA98C0E00091400110914101CC +:100E90008C2F90E08017910728F481E090E028DC07 +:100EA000CF5FF2CFD6B3C0E08C2F90E080179107D0 +:100EB00028F481E090E01CDCCF5FF6CFBA9AC0E066 +:100EC0002C2F30E080914001909141012817390783 +:100ED00028F481E090E00CDCCF5FF2CF8D2F8170A1 +:100EE000DF91CF911F910F910895CF9388B38A7F9F +:100EF00088BBBA98B898C0E08C2F90E02091400150 +:100F0000309141018217930728F481E090E0F0DBF3 +:100F1000CF5FF2CFCF910895CF9387B38A7F87BBFE +:100F2000C0E08C2F90E02091400130914101821768 +:100F3000930728F481E090E0DBDBCF5FF2CFB89A33 +:100F4000C0E08C2F90E02091400130914101821748 +:100F5000930728F481E090E0CBDBCF5FF2CFBA9A21 +:100F6000C0E08C2F90E02091400130914101821728 +:100F7000930728F481E090E0BBDBCF5FF2CFCF9105 +:100F80000895CF93B89AC0E08C2F90E02091400153 +:100F9000309141018217930728F481E090E0A8DBAB +:100FA000CF5FF2CFBA9AC0E08C2F90E02091400141 +:100FB000309141018217930728F481E090E098DB9B +:100FC000CF5FF2CFBA98C0E08C2F90E02091400123 +:100FD000309141018217930728F481E090E088DB8B +:100FE000CF5FF2CFB898C0E08C2F90E02091400105 +:100FF000309141018217930728F481E090E078DB7B +:10100000CF5FF2CFCF910895CF93DF93D82FC8E071 +:101010008D2F8078D8DEDD0FC150D1F7DF91CF91D1 +:101020001DCFFF920F931F93CF93DF93F82ED8E03D +:10103000C0E0CC0F13DFC82BD150D9F7009140018D +:10104000109141018D2F90E08017910728F481E0E5 +:1010500090E04EDBDF5FF2CFFF2011F080E001C0B7 +:1010600081E0B1DED0E08D2F90E08017910728F469 +:1010700081E090E03DDBDF5FF6CF8C2FDF91CF91F9 +:101080001F910F91FF900895A4E9B1E08827F8948B +:10109000BA98C29AEE27FF27319688F0B299FCCF12 +:1010A000803468F4ED93FD938395EE27FF27319606 +:1010B00030F0B29BFCCFED93FD938395EBCF880F7F +:1010C000809389010895F894BA98C29A8091870113 +:1010D000909188010197A4E9B1E07091890177238B +:1010E00001F1C298BA9A00C000006D9158E0FC016D +:1010F00031963197F1F760FDBA9860FDC29A60FFB2 +:10110000C29860FFBA9AFC013197F1F766955A953B +:1011100091F7FC01BA98C29A32963197F1F77A9515 +:1011200001F7089577278091870190918801A4E9BC +:10113000B1E0EE27FF273197F1F0B299FCCFFC0127 +:10114000F695E7953197F1F7B299F3CF662758E016 +:10115000FC013197F1F76695B299606800C05A9525 +:10116000B9F76D937395EE27FF27319721F0B29B66 +:10117000FCCF7038F0F2709389010895AA9BCFC715 +:10118000B29B12C0AC9B1895AC9809D0F894AC9A5D +:10119000CF93C0E0AA9BC0936D01CF911895CF93D8 +:1011A000CFB7CF937894BEC75F935FB75F93AA988A +:1011B00050919401552339F451E0509394015F917B +:1011C0005FBF5F9118956F937F938F939F93AF93BA +:1011D000BF93EF93FF93559518F0559518F003C002 +:1011E000A1DF01C051DFFF91EF91BF91AF919F91BE +:1011F0008F917F916F91E3CF80E287BB88BB80E0C6 +:1012000090E0A8D28F3F09F081BFC0916D00D091CE +:101210006E00CE019FD2E82ECE0101969BD2F82E11 +:10122000CE01029697D2982F80ED8E0D8A3008F06D +:101230007FC280ED8F0D8A3008F07AC280ED890F71 +:101240008A3008F075C2C0916D00D0916E00CE0159 +:1012500081D290E09093680080936700CE01019660 +:1012600079D290E090936A0080936900CE01029653 +:1012700071D290E090936C0080936B00BB9AC4E1B4 +:101280008FE090E042DAC150D9F7BB9885E090E05A +:10129000909341018093400110926300109262008C +:1012A0009EE088E10FB6F894A89581BD0FBE91BD70 +:1012B00010923B0110923A011092390110923801BC +:1012C000109237011092360110923D0110923401B4 +:1012D000B5D97894B898C098B998C198BA98C29876 +:1012E000BD98C598DAE130E4F32EC1E04BE1E42E7D +:1012F00020E8D22EA89571D8E09134018E2F90E08D +:101300008531910508F0DDC1FC01E15FFF4F0994D3 +:10131000B89AB998BA9AC29A10928B018091140225 +:1013200080933F01D0923E0180913E01882309F4D1 +:1013300058C0C098B12C8B2D90E020916200309164 +:1013400063008217930728F481E090E0D1D9B39429 +:10135000F2CF90913E0180913F01892309F0C09A1C +:10136000C29880918B01880F80938B0196B3209156 +:101370008B0191FB882780F9820F80938B01B12C20 +:101380008B2D90E0209162003091630082179307CB +:1013900028F481E090E0ACD9B394F2CFC29A809166 +:1013A0003E01869580933E01BFCFBA9AC29880EEE7 +:1013B00091E09ED9BFB6F894C29A86E490E098D99D +:1013C000BA9886B382FB002700F910E0C80121E03B +:1013D000822780938B01BFBE8AE991E089D9BA9AAE +:1013E000C29AC09393016DC18091140280933E0113 +:1013F000BA9A98E0B92EAFB6F894C29880913E019F +:1014000080FF07C086E090E073D9C29A80E490E044 +:1014100006C08CE390E06CD9C29A8AE090E068D96B +:1014200080913E01869580933E01AFBEBA94B11083 +:10143000E2CF47C110928B0188E0B82E80918B01DA +:10144000869580938B01BA9AAFB6F894C29886E0DD +:1014500090E04ED9C29A8AE090E04AD9BA9886B311 +:1014600082FB882780F980933E01AFBE87E390E03E +:101470003FD980913E01882329F080918B018068BB +:1014800080938B01BA94B110D9CFABCFBA9ABFB6C3 +:10149000F894C29886E090E02BD9C29A8AE090E056 +:1014A00027D9BA9886B382FB882780F980938B016D +:1014B000BFBE97CFBA9ABFB6F894C2988091140273 +:1014C00080FF07C086E090E013D9C29A80E490E0E4 +:1014D00006C08CE390E00CD9C29A8AE090E008D96B +:1014E000BFBEEFC0B99AB898BA9AC298E0923F01CD +:1014F000DDB9809114028111C59810923E01E091EE +:101500003E0180911502E81730F5F0E0EA5EFD4FEC +:1015100080818FB9FEB880913F018DB9B12C8B2DA0 +:1015200090E020916200309163008217930728F4C5 +:1015300081E090E0DDD8B394F2CF769BECCF809140 +:101540003E01E82FF0E09FB1E557FE4F90838F5F9B +:1015500080933E01D4CF809114028111C59A80916D +:101560003E0156C0C2DCADC080E090E0D5DC809189 +:1015700014024ADD80933E0115C0B12C8091140203 +:10158000B81650F4EB2DF0E0EA5EFD4F80813CDDB3 +:1015900080933E01B394F2CF809115028111F1DC6A +:1015A00080913E0180938B011CCF809114028111A8 +:1015B00002C0B12C19C0912C292D30E08091150268 +:1015C00090E00197A90145575E4F5A012817390746 +:1015D00034F481E026DDF50180839394EDCF80E043 +:1015E00020DDF50180830EC080911502B81650F4FD +:1015F0008B2C912C81E015DDF401E557FE4F8083A3 +:10160000B394F2CF809116028111BBDC8091150258 +:101610008093930156C081E090E077D8BFB6F894EC +:101620006091720070E04091730084E790E014D9FB +:101630001092720055CF80918A01847319F082E074 +:1016400090E063D880918A0180FF08C08FB7F8943A +:10165000C298BA9A8FBF84E690E057D880918A01E9 +:1016600081FF08C08091940180938701809195014A +:101670008093880180918A0182FD25DD80918A0115 +:1016800083FF11C080918A0182958F7080939401AD +:101690009091940181E0911180E0809389011092F2 +:1016A0008A01BA98AA9A80918A0184FD3BDD8091D3 +:1016B0008A0185FDE9DC1092340110928A01789448 +:1016C00002C01092340180913C01882309F412CEAB +:1016D00080913D0181110DC08091380180933B01C3 +:1016E0008091370180933A018091360180933901CE +:1016F00019C090913B01981710F4C09801C0C09A8E +:1017000090913D0180913A01891710F4C19801C070 +:10171000C19A90913D0180913901891710F4C298C6 +:1017200001C0C29A80913D018F5F80933D01E0CD61 +:1017300065E3CE0117D080916D0090916E0061E35A +:10174000019610D080916D0090916E0062E3029638 +:1017500009D079CDE199FECF9FBB8EBBE09A992746 +:101760008DB30895262FE199FECF1CBA9FBB8EBB87 +:101770002DBB0FB6F894E29AE19A0FBE0196089538 +:04178000F894FFCF0B +:10178400FF5A0A000A080335003100320020000025 :00000001FF From 705bde82e40db51e2e2e4b8a71903afeb73c7c57 Mon Sep 17 00:00:00 2001 From: Peter Maivald Date: Wed, 22 Nov 2017 13:07:19 +0100 Subject: [PATCH 15/18] break detection and utilization --- example/echodw/echodw.cpp | 6 +- example/echodw/uartsoft.cpp | 165 ++++++++++++++++++++++++++++++++---- example/echodw/uartsoft.hpp | 25 ++++-- 3 files changed, 169 insertions(+), 27 deletions(-) diff --git a/example/echodw/echodw.cpp b/example/echodw/echodw.cpp index ce850f0..8c8974e 100644 --- a/example/echodw/echodw.cpp +++ b/example/echodw/echodw.cpp @@ -17,12 +17,8 @@ int main() { uint8_t n = uartSoft.read(&s); if (!n) continue; - // skip any leading null - if (!s[0]) { ++s; --n; } - if (!n) continue; - // send it back - char buf[32] = " got: "; + char buf[32] = "got: "; strcat(buf, s); strcat(buf, "\n"); _delay_ms(100); diff --git a/example/echodw/uartsoft.cpp b/example/echodw/uartsoft.cpp index 22d7ac0..199a4b2 100644 --- a/example/echodw/uartsoft.cpp +++ b/example/echodw/uartsoft.cpp @@ -7,8 +7,19 @@ UartSoft uartSoft; // divisor >= 16 and multiple of 4 uint8_t UartSoft::divisor; +uint8_t UartSoft::break_timeout; volatile char UartSoft::buff[UARTSOFT_BUFF_SIZE*2]; volatile char* UartSoft::next = UartSoft::buff; +volatile UartSoft::Flag UartSoft::flag = flNone; + +bool UartSoft::clear(Flag f) { + bool ret; + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + ret = (flag & f) != 0; + flag = (Flag)(flag & ~f); + } + return ret; +} extern "C" __attribute__ ((naked)) char* UartSoft_last() { @@ -40,8 +51,8 @@ resetbuf%=: ); } -void UartSoft::begin(uint8_t _divisor) { - divisor = _divisor; +void UartSoft::begin(uint8_t _divisor, uint8_t _break_timeout) { + divisor = _divisor; break_timeout = _break_timeout; // input, enable pullup UARTSOFT_REG(DDR,UARTSOFT_ARGS) &=~ (1<> 2 + subi %[idel],8 ;1 + lsr %[idel] ;1 + lsr %[idel] ;1 +delay%=: + nop ;1 + dec %[idel] ;1 + brne delay%= ;? 4*idel end + dec %[ibit] ;1 + brne loop%= ;? + + cbi %[ddr],%[bit] ;2 input + sbi %[port],%[bit] ;2 pullup + + lds %[idel],%[divisor] ;2 idel = divisor >> 2 + lsr %[idel] ;1 + lsr %[idel] ;1 +delays%=: + nop ;1 + dec %[idel] ;1 + brne delays%= ;? 4*idel end + + ret +)assembly" + : [ibit] "=&r" (ibit) + ,[idel] "=&r" (idel) + : [divisor] "m" (UartSoft::divisor) + ,[ddr] "I" (_SFR_IO_ADDR(UARTSOFT_REG(DDR,UARTSOFT_ARGS))) + ,[port] "I" (_SFR_IO_ADDR(UARTSOFT_REG(PORT,UARTSOFT_ARGS))) + ,[bit] "I" (UARTSOFT_BIT(UARTSOFT_ARGS)) + ); +} + extern "C" __attribute__ ((naked)) void UartSoft_write(uint8_t _c) { register uint8_t divisor = UartSoft::divisor; @@ -119,9 +178,10 @@ all_done%=: } #ifdef core_hpp -uint8_t UartSoft::write(const fstr_t* s, uint8_t n) { +uint8_t UartSoft::write(const fstr_t* s, uint8_t n, uint8_t break_bits) { uint8_t r = n; ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + UartSoft_break(break_bits); while (n--) UartSoft_write(pgm_read_byte(s++)); } @@ -129,15 +189,33 @@ uint8_t UartSoft::write(const fstr_t* s, uint8_t n) { } #endif -uint8_t UartSoft::write(const char* s, uint8_t n) { +uint8_t UartSoft::write(const char* s, uint8_t n, uint8_t break_bits) { uint8_t r = n; ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + UartSoft_break(break_bits); while (n--) UartSoft_write(*s++); } return r; } +/* Interrupt + +Packets of up to UARTSOFT_BUFF_SIZE-1 are received into one of two buffers: UartSoft::buff or +UartSoft::buff + UARTSOFT_BUFF_SIZE. The interrupt starts with UartSoft::next pointing to the +buffer that will receive. Location UartSoft::buff + UARTSOFT_BUFF_SIZE-1 gets the number of +chars received, and UartSoft::next is then switched to the other buffer. + +Packets must consist of a short break followed by up to UARTSOFT_BUFF_SIZE-1 chars. A break is +considered short when it is shorter than UartSoft::break_timeout bits. A packet is considered +complete once 12 high bits in a row have passed. The short break is required to allow for +interrupt latency. A short break is still required after a long break for synchronization +before data can be received. + +Once a break is longer than UartSoft::break_timeout bits, flag UartSoft::flBreakActive is set. +Once the long break is over, flag UartSoft::flBreakActive is cleared and flag UartSoft::flBreak +is set. The interrupt returns during the time that the long break is active. + */ extern "C" __attribute__ ((naked, used)) void UARTSOFT_INT(UARTSOFT_ARGS)() { @@ -150,8 +228,6 @@ void UARTSOFT_INT(UARTSOFT_ARGS)() { register uint16_t x asm("r26"); __asm__ __volatile__ (R"assembly( ; 00 - sbic %[pin],%[bit] ;? - reti ;2 give up if high push r24 ;2 02 in r24,__SREG__ ;1 03 push r24 ;2 05 @@ -162,16 +238,57 @@ void UARTSOFT_INT(UARTSOFT_ARGS)() { push r28 ;2 15 push r21 ;2 17 - lds %A[x],%[next] ;2 19 buffer - lds %B[x],%[next]+1 ;2 21 - ldi %[ibuf],%[size]-1 ;1 22 max number of bytes + ; handle break flags + lds %[idel],%[flag] ;2 19 is long break active? + andi %[idel],%[brka] ;1 20 + breq nolong%= ;? 22 + sbis %[pin],%[bit] ; still low? + rjmp done%= ; keep waiting + ; long break is over + lds %[idel],%[flag] + andi %[idel],~%[brka] ; clear active flag + ori %[idel],%[brk] ; set break flag + sts %[flag],%[idel] + rjmp done%= ; long break detected +nolong%=: + sbic %[pin],%[bit] ;? 23 + rjmp done%= ; skip uncleared interrupt + ; short break already in progress + + ; initialize variables + lds %A[x],%[next] ;2 25 buffer + lds %B[x],%[next]+1 ;2 27 + ldi %[ibuf],%[size]-1 ;1 28 max number of bytes ; inter bit delay (divisor - 8) >> 2 - lds %[delay],%[divisor] ;2 24 - subi %[delay],8 ;1 25 delay till next bit - lsr %[delay] ;1 26 - lsr %[delay] ;1 27 - + lds %[delay],%[divisor] ;2 30 + subi %[delay],8 ;1 31 delay till next bit + lsr %[delay] ;1 32 + lsr %[delay] ;1 33 + + ; short break up to breakto low bits + lds %[ibit],%[breakto] ;2 max number of break bits +loopbrto%=: ; (4*idel + 8)*ibit + mov %[idel],%[delay] ;1 idel = (divisor - 8) >> 2 +loopbito%=: ; 4*idel + dec %[idel] ;1 + sbis %[pin],%[bit] ;? still low? + brne loopbito%= ;? 4*idel end + rjmp . ;2 02 + rjmp . ;2 04 + dec %[ibit] ;1 05 + sbis %[pin],%[bit] ;? 06 still low? + brne loopbrto%= ;? (4*idel + 8)*ibit end + sbic %[pin],%[bit] ;? still low? + rjmp readbyte%= ;2 shorter than breakto, so ignore + ; now we have over breakto low bits + + lds %[idel],%[flag] ; set break active flag + ori %[idel],%[brka] + sts %[flag],%[idel] + rjmp done%= ; keep waiting + + ; wait for start bit readbyte%=:; timeout = 2 * 6 * bit time lds %[idel],%[divisor] ;2 07 one bit time ldi %[ibit],0 ;1 08 high part of timeout @@ -198,7 +315,7 @@ delay%=: brne delay%= ;? 4*delay lsr %[recv] ;1 01 sample - sbic %[pin],%[bit] ;? skip if high + sbic %[pin],%[bit] ;? ori %[recv],0x80 ;1 03 set msb rjmp . ;2 05 nop dec %[ibit] ;1 06 @@ -209,11 +326,15 @@ delayt%=: ; wait past last bit dec %[idel] ;1 00 nop ;1 01 brne delayt%= ;? 4*delay + ; entire byte received + + sbis %[pin],%[bit] ;? check for low + rjmp error%= st X+,%[recv] ;2 00 store byte dec %[ibuf] ;1 03 brne readbyte%= ;? 05 - ; buffer full + ; buffer full haveall%=: ldi %[recv],%[size]-1 ;1 max number of bytes sub %[recv],%[ibuf] ;1 bytes received @@ -224,7 +345,14 @@ haveall%=: rcall UartSoft_last ; swap buffers sts %[next],r24 sts %[next]+1,r25 + rjmp done%= + +error%=: + lds %[idel],%[flag] ; set error flag + ori %[idel],%[err] + sts %[flag],%[idel] +done%=: pop r21 ;2 pop r28 ;2 pop r29 ;2 @@ -244,7 +372,12 @@ haveall%=: ,[ibuf] "=&r" (ibuf) ,[x] "=&r" (x) : [divisor] "m" (UartSoft::divisor) + ,[breakto] "m" (UartSoft::break_timeout) ,[next] "m" (UartSoft::next) + ,[flag] "m" (UartSoft::flag) + ,[brk] "I" (UartSoft::flBreak) + ,[brka] "I" (UartSoft::flBreakActive) + ,[err] "I" (UartSoft::flError) ,[size] "I" (UARTSOFT_BUFF_SIZE) ,[pin] "I" (_SFR_IO_ADDR(UARTSOFT_REG(PIN,UARTSOFT_ARGS))) ,[bit] "I" (UARTSOFT_BIT(UARTSOFT_ARGS)) diff --git a/example/echodw/uartsoft.hpp b/example/echodw/uartsoft.hpp index b9ea9e7..e3e4e37 100644 --- a/example/echodw/uartsoft.hpp +++ b/example/echodw/uartsoft.hpp @@ -15,23 +15,28 @@ #define UARTSOFT_PCIE(ARGS) MUAPK_CAT(PCIE,MUAPK_4_2 ARGS) #define UARTSOFT_INT(ARGS) MUAPK_4_3 ARGS -#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny13__) +#if defined(UARTSOFT_ARGS) +// ok +#elif defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) \ + || defined(__AVR_ATtiny13__) #define UARTSOFT_ARGS (B,5,,PCINT0_vect) + #elif defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) #define UARTSOFT_ARGS (B,3,1,PCINT1_vect) -#else // defined(__AVR_*) + +#else // defined(UARTSOFT_ARGS) #error DW ports -#endif // defined(__AVR_*) +#endif // defined(UARTSOFT_ARGS) extern class UartSoft : public RpcSend { public: - void begin(uint8_t divisor = 128); + void begin(uint8_t divisor = 128, uint8_t break_timeout = 12); #ifdef core_hpp - static uint8_t write(const fstr_t* s, uint8_t n); + static uint8_t write(const fstr_t* s, uint8_t n, uint8_t break_bits = 8); #endif - static uint8_t write(const char* s, uint8_t n); + static uint8_t write(const char* s, uint8_t n, uint8_t break_bits = 8); static uint8_t read(char* d, uint8_t n); static uint8_t read(char** d); static bool eof(); @@ -40,6 +45,14 @@ extern class UartSoft : public RpcSend { // divisor >= 16 and multiple of 4 static uint8_t divisor; + static uint8_t break_timeout; + static volatile enum Flag : uint8_t { + flNone = 0, + flBreakActive = 0x01, // during break + flBreak = 0x02, // after break + flError = 0x04, // framing + } flag; + static bool clear(Flag f); // double buffer, next gets loaded // last byte has number of chars received From 606dbd1bcf2dfe0aedb4a5a58887ffd82d55d5c5 Mon Sep 17 00:00:00 2001 From: Peter Maivald Date: Thu, 23 Nov 2017 19:10:14 +0100 Subject: [PATCH 16/18] reestablish connection without switching to dw mode --- example/echodw/uartsoft.cpp | 78 +++++++++++++++++++++++++++++++------ example/echodw/uartsoft.hpp | 2 +- utility/usbmon/hostusb.cpp | 58 +++++++++++++++------------ 3 files changed, 101 insertions(+), 37 deletions(-) diff --git a/example/echodw/uartsoft.cpp b/example/echodw/uartsoft.cpp index 199a4b2..fed9272 100644 --- a/example/echodw/uartsoft.cpp +++ b/example/echodw/uartsoft.cpp @@ -82,11 +82,12 @@ loop%=: lds %[idel],%[divisor] ;2 idel = (divisor - 8) >> 2 subi %[idel],8 ;1 lsr %[idel] ;1 - lsr %[idel] ;1 + lsr %[idel] ;1 4*idel delay%=: nop ;1 dec %[idel] ;1 brne delay%= ;? 4*idel end + nop ;1 dec %[ibit] ;1 brne loop%= ;? @@ -112,13 +113,41 @@ delays%=: ); } +extern "C" __attribute__ ((naked)) +void UartSoft_delay(uint8_t bits) { + + // bits is passed in r24 + register uint8_t ibit asm("r24") = bits; + register uint8_t idel asm("r25"); + __asm__ __volatile__ (R"assembly( + +loop%=: + lds %[idel],%[divisor] ;2 idel = (divisor - 8) >> 2 + subi %[idel],8 ;1 + lsr %[idel] ;1 + lsr %[idel] ;1 4*idel +delay%=: + nop ;1 + dec %[idel] ;1 + brne delay%= ;? 4*idel end + nop ;1 + dec %[ibit] ;1 + brne loop%= ;? + + ret +)assembly" + : [ibit] "=&r" (ibit) + ,[idel] "=&r" (idel) + : [divisor] "m" (UartSoft::divisor) + ); +} + extern "C" __attribute__ ((naked)) void UartSoft_write(uint8_t _c) { register uint8_t divisor = UartSoft::divisor; // c is passed in r24, with r25 allows adiw instruction uint16_t c = _c; - uint8_t sreg; __asm__ __volatile__ (R"assembly( ; divisor >= 16 and multiple of 4 @@ -133,10 +162,6 @@ void UartSoft_write(uint8_t _c) { add %A[c],%A[c] adc %B[c],__zero_reg__ - ; uint8_t sreg = SREG; cli(); - in %[sreg],__SREG__ - cli - ; shift right and output lsb loop%=: ;0. 00 lsr %B[c] ;1. 01 @@ -163,13 +188,10 @@ delay%=: breq loop%= ;12 rjmp delay%= ;.2 - ; SREG = sreg; all_done%=: - out __SREG__,%[sreg] ret )assembly" - : [sreg] "=&r" (sreg) - ,[c] "+w" (c) + : [c] "+w" (c) : [ddr] "I" (_SFR_IO_ADDR(UARTSOFT_REG(DDR,UARTSOFT_ARGS))) ,[port] "I" (_SFR_IO_ADDR(UARTSOFT_REG(PORT,UARTSOFT_ARGS))) ,[bit] "I" (UARTSOFT_BIT(UARTSOFT_ARGS)) @@ -177,6 +199,38 @@ all_done%=: ); } +extern "C" __attribute__ ((naked)) +void UartSoft_break_detected() { + register uint8_t t asm("r24"); + __asm__ __volatile__ (R"assembly( + + lds %[t],%[flag] ; is long break detected? + andi %[t],%[brk] + breq nolong%= + lds %[t],%[flag] ; clear break detected flag + andi %[t],~%[brk] ; clear active flag + sts %[flag],%[t] + + ldi r24,8 ; delay + rcall UartSoft_delay + ldi r24,0x55 ; sync byte + rcall UartSoft_write + ldi r24,8 ; delay + rcall UartSoft_delay + ldi r24,0 ; short break + rcall UartSoft_write + +nolong%=: + ret + +)assembly" + : [t] "=&r" (t) + : [flag] "m" (UartSoft::flag) + ,[brk] "I" (UartSoft::flBreak) + : + ); +} + #ifdef core_hpp uint8_t UartSoft::write(const fstr_t* s, uint8_t n, uint8_t break_bits) { uint8_t r = n; @@ -249,7 +303,9 @@ void UARTSOFT_INT(UARTSOFT_ARGS)() { andi %[idel],~%[brka] ; clear active flag ori %[idel],%[brk] ; set break flag sts %[flag],%[idel] - rjmp done%= ; long break detected + rcall UartSoft_break_detected + rjmp done%= + nolong%=: sbic %[pin],%[bit] ;? 23 rjmp done%= ; skip uncleared interrupt diff --git a/example/echodw/uartsoft.hpp b/example/echodw/uartsoft.hpp index e3e4e37..786185c 100644 --- a/example/echodw/uartsoft.hpp +++ b/example/echodw/uartsoft.hpp @@ -31,7 +31,7 @@ extern class UartSoft : public RpcSend { public: - void begin(uint8_t divisor = 128, uint8_t break_timeout = 12); + static void begin(uint8_t divisor = 128, uint8_t break_timeout = 12); #ifdef core_hpp static uint8_t write(const fstr_t* s, uint8_t n, uint8_t break_bits = 8); diff --git a/utility/usbmon/hostusb.cpp b/utility/usbmon/hostusb.cpp index aa69839..b96354e 100644 --- a/utility/usbmon/hostusb.cpp +++ b/utility/usbmon/hostusb.cpp @@ -317,15 +317,9 @@ void LuXactLw::Send(char data) { // Cancel any dw commands XferRetry(60/*dw*/, 0); - // send bytes + // send bytes and read if listening char buf[2] = ""; buf[1] = data; - Xfer(60/*dw*/, 0x04, buf, sizeof(buf), false); - - // Xfer cancels dw interrupt, so restart it - if (listening) { - listening = false; - Recv(); - } + Xfer(60/*dw*/, (listening ? 0x1C : 0x04), buf, sizeof(buf), false); } void LuXactLw::Send(struct Rpc& rpc, uint8_t index) { @@ -385,31 +379,45 @@ bool LuXactLw::ResetDw() { // Read back timings int status = Xfer(60/*dw*/, 0, (char*)times, sizeof(times), true); if (status <= 0 && tries < 5) continue; - if (status < 18) return false; - // Average measurements and determine baud rate as pulse time in device cycles. - uint32_t cyclesperpulse = 0; - int measurementCount = status / 2; - for (int i = measurementCount-9; i < measurementCount; i++) cyclesperpulse += times[i]; + // 10 transitions for start, stop, and 8 data bits (20 bytes). + // Average over last 8 bits, first one is bad since it has no start, + // also thrown out second one to make number even since + // rise and fall times aren't symmetric. When dw has been disabled, + // the target will reply with 0x55, a one byte delay, then 0x00 + // which will cause status to return 24 instead of 20. + if (status != 20 && status != 24) return false; + + // Average measurements and determine baud rate as bit time in device cycles. + const int n = 8; + uint32_t sum = 0; + for (int i = 10 - n; i < 10; i++) + sum += times[i]; + + // Bit time for each measurement is 6 * measurement + 8 cycles. + // Don't divide by n yet to preserve precision. + sum = 6 * sum + 8 * n; - // Pulse cycle time for each measurement is 6*measurement + 8 cyclesperpulse. - cyclesperpulse = (6*cyclesperpulse) / 9 + 8; + // Display the baud rate + strcpy(id, (std::to_string(16500000 * n / sum)+"Bd").c_str()); - // Determine timing loop iteration counts for sending and receiving bytes - times[0] = (cyclesperpulse-8)/4; // dwBitTime - strcpy(id, (std::to_string(16500000 / cyclesperpulse)+"Bd").c_str()); + // Determine timing loop iteration counts for sending and receiving bytes, + // use rounding in the division. + uint16_t dwBitTime = (sum - 8 * n + (4 * n) / 2) / (4 * n); // Set timing parameter - if (Xfer(60/*dw*/, 0x02, (char*)times, sizeof(uint16_t), false) < 0) + if (Xfer(60/*dw*/, 0x02, (char*)&dwBitTime, sizeof(dwBitTime), false) < 0) return false; + + if (status == 20) { + // Disable Dw if it was enabled + char data = 0x06; + if (Xfer(60/*dw*/, 0x3C, &data, sizeof(data), false) < 0) + return false; + } + break; } - { - // Disable Dw - char data = 0x06; - if (Xfer(60/*dw*/, 0x3C, &data, sizeof(data), false) < 0) - return false; - } Label(); return true; From 7ca24910262105d1e754ce00137f7c8e618fa0c8 Mon Sep 17 00:00:00 2001 From: Peter Maivald Date: Wed, 29 Nov 2017 10:42:12 +0100 Subject: [PATCH 17/18] calculate dwBitTime on lw device --- usbtiny/main.c | 64 +++++++++++- usbtiny/main.hex | 248 ++++++++++++++++++++++++----------------------- 2 files changed, 189 insertions(+), 123 deletions(-) diff --git a/usbtiny/main.c b/usbtiny/main.c index 88d19b4..d896f0c 100644 --- a/usbtiny/main.c +++ b/usbtiny/main.c @@ -1359,6 +1359,66 @@ void dwCaptureWidths() { " add r24,r24 ; Convert word count to byte count \n" " sts dwLen,r24 \n" ::[bit]"I"(DW_BIT):"r24","r26","r27","r30","r31"); + + // calculate dwBitTime so that it can be immediately used + // do it in assembly to limit register usage so this can be + // called from an interrupt to immediately receive data + // following a sync byte + __asm__ __volatile__ (R"string-literal( +; uint16_t* p = dwBuf + 2; + ldi r30,lo8(dwBuf+4) ; z register + ldi r31,hi8(dwBuf+4) +; uint24_t sum = 0; ; sum r24,r25,r26 + clr r24 + clr r25 + clr r26 + clr r27 ; temp r27 +; for (int i = 0; i < 8; i++) + ldi r23,8 ; i r23 +; sum += *p++; +sum%=: + ld r22,z+ + add r24,r22 + ld r22,z+ + adc r25,r22 + adc r26,r27 + dec r23 + brne sum%= +; dwBitTime = (3 * sum + 8) / 16; + mov r30,r24 ; temp r30,r31,r27 + mov r31,r25 ; temp = sum + mov r27,r26 + add r30,r24 ; temp += sum + adc r31,r25 + adc r27,r26 + add r24,r30 ; sum += temp + adc r25,r31 + adc r26,r27 + adiw r24,8 ; sum += 8 // for rounding + clr r27 ; temp r27 + adc r26,r27 + lsr r26 ; sum /= 2 + ror r25 + ror r24 + lsr r26 ; sum /= 2 + ror r25 + ror r24 + lsr r26 ; sum /= 2 + ror r25 + ror r24 + lsr r26 ; sum /= 2 + ror r25 + ror r24 + ldi r30,lo8(dwBitTime) ; z register + ldi r31,hi8(dwBitTime) + st z+,24 + st z+,25 + ldi r30,lo8(dwBuf) ; for host + ldi r31,hi8(dwBuf) + st z+,24 + st z+,25 +)string-literal" + :::"r22","r23","r24","r25","r26","r27","r30","r31"); } // Sample pulse widths returned by attiny85 with internal clock as supplied @@ -2045,8 +2105,8 @@ int main(void) { if (dwState & 0x04) {dwSendBytes();} if (dwState & 0x08) {dwBuf[0]=dwState>>4; dwLen=dwBuf[0]?0:1; dwState=0; cbi(DDRB,DW_BIT); sbi(PCMSK,DW_BIT);} // Capture dWIRE pin change async - if (dwState & 0x10) {dwReadBytes();} - if (dwState & 0x20) {dwCaptureWidths();} + if (dwState & 0x10) {dwLen=0;dwReadBytes();} + if (dwState & 0x20) {dwLen=0;dwCaptureWidths();} jobState = 0; dwState = 0; diff --git a/usbtiny/main.hex b/usbtiny/main.hex index 5e87a9e..ed8bb58 100644 --- a/usbtiny/main.hex +++ b/usbtiny/main.hex @@ -1,16 +1,16 @@ -:1000000046C060C0BBC85EC05DC05CC05BC05AC0BB -:1000100059C058C057C056C055C054C053C053CB28 -:1000200077C9C3C9E1C906CA31CA44CA5BCA9ACAF8 -:100030009BCAA3CABACA45CB44CB43CB42CB41CB24 -:10004000EACA3FCB3ECBF7CA090219000101008082 +:1000000046C060C0E9C85EC05DC05CC05BC05AC08D +:1000100059C058C057C056C055C054C053C087CBF4 +:10002000A5C9F1C90FCA34CA5FCA72CA89CAC8CA87 +:10003000C9CAD1CAE8CA79CB78CB77CB76CB75CB96 +:1000400018CB73CB72CB25CB0902190001010080BC :10005000640904000001FF00000007058103080097 :1000600064120110010000000881179F0C040100B8 :100070000203011603550053004200740069006E2C :1000800000790053005000490004030904001124C2 :100090001FBECFE5D2E0DEBFCDBF10E0A0E6B0E0EE -:1000A000E4E8F7E102C005900D92A037B107D9F757 +:1000A000ECEEF7E102C005900D92A037B107D9F749 :1000B00022E0A0E7B0E001C01D92AD31B207E1F748 -:1000C0009BD85ECB9DCFA82FB92F80E090E041E078 +:1000C000C9D892CB9DCFA82FB92F80E090E041E016 :1000D00050EA609530E009C02D9182279795879569 :1000E00010F084279527305EC8F36F5FA8F308955A :1000F000EADF8D939D930895A6E088279927AA9516 @@ -193,9 +193,9 @@ :100C00008D81EE5FF0E0EC58FF4F80838A8184FF96 :100C100092C080917200882309F48DC081E1809395 :100C2000340187B3842B87BB4093730084C0273380 -:100C3000A1F480916D0090916E0094D580916D002B -:100C400090916E006B8101968DD580916D00909191 -:100C50006E006C81029686D56EC02C3371F5809142 +:100C3000A1F480916D0090916E00C8D580916D00F7 +:100C400090916E006B810196C1D580916D0090915D +:100C50006E006C810296BAD56EC02C3371F580910E :100C60003401882321F080918A01811164C0888138 :100C700087FF09C084E991E090936C0180936B0138 :100C80009091890159C0AA988A8180938A018E81A6 @@ -218,7 +218,7 @@ :100D9000994097FF03C09195819591098E159F0504 :100DA00014F4C1B77C0181B78F5F81BFEACFC1BFA7 :100DB00061B780E090E0DF91CF911F910F91FF909C -:100DC000EF90DF90CFC4CF93DF93D82F882311F01B +:100DC000EF90DF9003C5CF93DF93D82F882311F0E6 :100DD000B89801C0B89AC0E02C2F30E08091400153 :100DE000909141012817390728F481E090E080DCD8 :100DF000CF5FF2CFBA98C0E02C2F30E08091400155 @@ -266,114 +266,120 @@ :10109000BA98C29AEE27FF27319688F0B299FCCF12 :1010A000803468F4ED93FD938395EE27FF27319606 :1010B00030F0B29BFCCFED93FD938395EBCF880F7F -:1010C000809389010895F894BA98C29A8091870113 -:1010D000909188010197A4E9B1E07091890177238B -:1010E00001F1C298BA9A00C000006D9158E0FC016D -:1010F00031963197F1F760FDBA9860FDC29A60FFB2 -:10110000C29860FFBA9AFC013197F1F766955A953B -:1011100091F7FC01BA98C29A32963197F1F77A9515 -:1011200001F7089577278091870190918801A4E9BC -:10113000B1E0EE27FF273197F1F0B299FCCFFC0127 -:10114000F695E7953197F1F7B299F3CF662758E016 -:10115000FC013197F1F76695B299606800C05A9525 -:10116000B9F76D937395EE27FF27319721F0B29B66 -:10117000FCCF7038F0F2709389010895AA9BCFC715 -:10118000B29B12C0AC9B1895AC9809D0F894AC9A5D -:10119000CF93C0E0AA9BC0936D01CF911895CF93D8 -:1011A000CFB7CF937894BEC75F935FB75F93AA988A -:1011B00050919401552339F451E0509394015F917B -:1011C0005FBF5F9118956F937F938F939F93AF93BA -:1011D000BF93EF93FF93559518F0559518F003C002 -:1011E000A1DF01C051DFFF91EF91BF91AF919F91BE -:1011F0008F917F916F91E3CF80E287BB88BB80E0C6 -:1012000090E0A8D28F3F09F081BFC0916D00D091CE -:101210006E00CE019FD2E82ECE0101969BD2F82E11 -:10122000CE01029697D2982F80ED8E0D8A3008F06D -:101230007FC280ED8F0D8A3008F07AC280ED890F71 -:101240008A3008F075C2C0916D00D0916E00CE0159 -:1012500081D290E09093680080936700CE01019660 -:1012600079D290E090936A0080936900CE01029653 -:1012700071D290E090936C0080936B00BB9AC4E1B4 -:101280008FE090E042DAC150D9F7BB9885E090E05A -:10129000909341018093400110926300109262008C -:1012A0009EE088E10FB6F894A89581BD0FBE91BD70 -:1012B00010923B0110923A011092390110923801BC -:1012C000109237011092360110923D0110923401B4 -:1012D000B5D97894B898C098B998C198BA98C29876 -:1012E000BD98C598DAE130E4F32EC1E04BE1E42E7D -:1012F00020E8D22EA89571D8E09134018E2F90E08D -:101300008531910508F0DDC1FC01E15FFF4F0994D3 -:10131000B89AB998BA9AC29A10928B018091140225 -:1013200080933F01D0923E0180913E01882309F4D1 -:1013300058C0C098B12C8B2D90E020916200309164 -:1013400063008217930728F481E090E0D1D9B39429 -:10135000F2CF90913E0180913F01892309F0C09A1C -:10136000C29880918B01880F80938B0196B3209156 -:101370008B0191FB882780F9820F80938B01B12C20 -:101380008B2D90E0209162003091630082179307CB -:1013900028F481E090E0ACD9B394F2CFC29A809166 -:1013A0003E01869580933E01BFCFBA9AC29880EEE7 -:1013B00091E09ED9BFB6F894C29A86E490E098D99D -:1013C000BA9886B382FB002700F910E0C80121E03B -:1013D000822780938B01BFBE8AE991E089D9BA9AAE -:1013E000C29AC09393016DC18091140280933E0113 -:1013F000BA9A98E0B92EAFB6F894C29880913E019F -:1014000080FF07C086E090E073D9C29A80E490E044 -:1014100006C08CE390E06CD9C29A8AE090E068D96B -:1014200080913E01869580933E01AFBEBA94B11083 -:10143000E2CF47C110928B0188E0B82E80918B01DA -:10144000869580938B01BA9AAFB6F894C29886E0DD -:1014500090E04ED9C29A8AE090E04AD9BA9886B311 -:1014600082FB882780F980933E01AFBE87E390E03E -:101470003FD980913E01882329F080918B018068BB -:1014800080938B01BA94B110D9CFABCFBA9ABFB6C3 -:10149000F894C29886E090E02BD9C29A8AE090E056 -:1014A00027D9BA9886B382FB882780F980938B016D -:1014B000BFBE97CFBA9ABFB6F894C2988091140273 -:1014C00080FF07C086E090E013D9C29A80E490E0E4 -:1014D00006C08CE390E00CD9C29A8AE090E008D96B -:1014E000BFBEEFC0B99AB898BA9AC298E0923F01CD -:1014F000DDB9809114028111C59810923E01E091EE -:101500003E0180911502E81730F5F0E0EA5EFD4FEC -:1015100080818FB9FEB880913F018DB9B12C8B2DA0 -:1015200090E020916200309163008217930728F4C5 -:1015300081E090E0DDD8B394F2CF769BECCF809140 -:101540003E01E82FF0E09FB1E557FE4F90838F5F9B -:1015500080933E01D4CF809114028111C59A80916D -:101560003E0156C0C2DCADC080E090E0D5DC809189 -:1015700014024ADD80933E0115C0B12C8091140203 -:10158000B81650F4EB2DF0E0EA5EFD4F80813CDDB3 -:1015900080933E01B394F2CF809115028111F1DC6A -:1015A00080913E0180938B011CCF809114028111A8 -:1015B00002C0B12C19C0912C292D30E08091150268 -:1015C00090E00197A90145575E4F5A012817390746 -:1015D00034F481E026DDF50180839394EDCF80E043 -:1015E00020DDF50180830EC080911502B81650F4FD -:1015F0008B2C912C81E015DDF401E557FE4F8083A3 -:10160000B394F2CF809116028111BBDC8091150258 -:101610008093930156C081E090E077D8BFB6F894EC -:101620006091720070E04091730084E790E014D9FB -:101630001092720055CF80918A01847319F082E074 -:1016400090E063D880918A0180FF08C08FB7F8943A -:10165000C298BA9A8FBF84E690E057D880918A01E9 -:1016600081FF08C08091940180938701809195014A -:101670008093880180918A0182FD25DD80918A0115 -:1016800083FF11C080918A0182958F7080939401AD -:101690009091940181E0911180E0809389011092F2 -:1016A0008A01BA98AA9A80918A0184FD3BDD8091D3 -:1016B0008A0185FDE9DC1092340110928A01789448 -:1016C00002C01092340180913C01882309F412CEAB -:1016D00080913D0181110DC08091380180933B01C3 -:1016E0008091370180933A018091360180933901CE -:1016F00019C090913B01981710F4C09801C0C09A8E -:1017000090913D0180913A01891710F4C19801C070 -:10171000C19A90913D0180913901891710F4C298C6 -:1017200001C0C29A80913D018F5F80933D01E0CD61 -:1017300065E3CE0117D080916D0090916E0061E35A -:10174000019610D080916D0090916E0062E3029638 -:1017500009D079CDE199FECF9FBB8EBBE09A992746 -:101760008DB30895262FE199FECF1CBA9FBB8EBB87 -:101770002DBB0FB6F894E29AE19A0FBE0196089538 -:04178000F894FFCF0B -:10178400FF5A0A000A080335003100320020000025 +:1010C00080938901E8E9F1E088279927AA27BB27BF +:1010D00078E06191860F6191961FAB1F7A95C9F7F1 +:1010E000E82FF92FBA2FE80FF91FBA1F8E0F9F1F95 +:1010F000AB1F0896BB27AB1FA69597958795A6951E +:1011000097958795A69597958795A6959795879591 +:10111000E7E8F1E081939193E4E9F1E08193919321 +:101120000895F894BA98C29A8091870190918801A5 +:101130000197A4E9B1E070918901772301F1C29888 +:10114000BA9A00C000006D9158E0FC0131963197C9 +:10115000F1F760FDBA9860FDC29A60FFC29860FF27 +:10116000BA9AFC013197F1F766955A9591F7FC010F +:10117000BA98C29A32963197F1F77A9501F70895A5 +:1011800077278091870190918801A4E9B1E0EE274B +:10119000FF273197F1F0B299FCCFFC01F695E79566 +:1011A0003197F1F7B299F3CF662758E0FC013197F8 +:1011B000F1F76695B299606800C05A95B9F76D93DA +:1011C0007395EE27FF27319721F0B29BFCCF703843 +:1011D000F0F2709389010895AA9BA1C7B29B12C037 +:1011E000AC9B1895AC9809D0F894AC9ACF93C0E01A +:1011F000AA9BC0936D01CF911895CF93CFB7CF9392 +:10120000789490C75F935FB75F93AA9850919401C9 +:10121000552339F451E0509394015F915FBF5F9182 +:1012200018956F937F938F939F93AF93BF93EF9393 +:10123000FF93559518F0559518F003C0A1DF01C034 +:1012400023DFFF91EF91BF91AF919F918F917F919C +:101250006F91E3CF80E287BB88BB80E090E0AED2A5 +:101260008F3F09F081BFC0916D00D0916E00CE011B +:10127000A5D2E82ECE010196A1D2F82ECE0102967B +:101280009DD2982F80ED8E0D8A3008F085C280EDBA +:101290008F0D8A3008F080C280ED890F8A3008F007 +:1012A0007BC2C0916D00D0916E00CE0187D290E0DC +:1012B0009093680080936700CE0101967FD290E002 +:1012C00090936A0080936900CE01029677D290E0F5 +:1012D00090936C0080936B00BB9AC4E18FE090E028 +:1012E00014DAC150D9F7BB9885E090E090934101A2 +:1012F0008093400110926300109262009EE088E1AA +:101300000FB6F894A89581BD0FBE91BD10923B0118 +:1013100010923A011092390110923801109237015F +:101320001092360110923D011092340187D97894C1 +:10133000B898C098B998C198BA98C298BD98C598FD +:10134000DAE130E4F32EC1E04BE1E42E20E8D22EC6 +:10135000A89543D8E09134018E2F90E08531910516 +:1013600008F0E3C1FC01E15FFF4F0994B89AB99816 +:10137000BA9AC29A10928B018091140280933F0115 +:10138000D0923E0180913E01882309F458C0C09854 +:10139000B12C8B2D90E02091620030916300821778 +:1013A000930728F481E090E0A3D9B394F2CF909111 +:1013B0003E0180913F01892309F0C09AC298809133 +:1013C0008B01880F80938B0196B320918B0191FB49 +:1013D000882780F9820F80938B01B12C8B2D90E0B0 +:1013E00020916200309163008217930728F481E016 +:1013F00090E07ED9B394F2CFC29A80913E01869557 +:1014000080933E01BFCFBA9AC29880EE91E070D926 +:10141000BFB6F894C29A86E490E06AD9BA9886B3C7 +:1014200082FB002700F910E0C80121E082278093A9 +:101430008B01BFBE8AE991E05BD9BA9AC29AC09388 +:10144000930173C18091140280933E01BA9A98E08F +:10145000B92EAFB6F894C29880913E0180FF07C0C4 +:1014600086E090E045D9C29A80E490E006C08CE323 +:1014700090E03ED9C29A8AE090E03AD980913E014C +:10148000869580933E01AFBEBA94B110E2CF4DC1B4 +:1014900010928B0188E0B82E80918B018695809305 +:1014A0008B01BA9AAFB6F894C29886E090E020D942 +:1014B000C29A8AE090E01CD9BA9886B382FB88274A +:1014C00080F980933E01AFBE87E390E011D980910F +:1014D0003E01882329F080918B01806880938B01E5 +:1014E000BA94B110D9CFABCFBA9ABFB6F894C2981C +:1014F00086E090E0FDD8C29A8AE090E0F9D8BA98E8 +:1015000086B382FB882780F980938B01BFBE97CF7B +:10151000BA9ABFB6F894C2988091140280FF07C0AF +:1015200086E090E0E5D8C29A80E490E006C08CE3C3 +:1015300090E0DED8C29A8AE090E0DAD8BFBEF5C06B +:10154000B99AB898BA9AC298E0923F01DDB98091F1 +:1015500014028111C59810923E01E0913E018091E4 +:101560001502E81730F5F0E0EA5EFD4F80818FB993 +:10157000FEB880913F018DB9B12C8B2D90E0209168 +:101580006200309163008217930728F481E090E0B5 +:10159000AFD8B394F2CF769BECCF80913E01E82F89 +:1015A000F0E09FB1E557FE4F90838F5F80933E013F +:1015B000D4CF809114028111C59A80913E0156C00A +:1015C00094DCB3C080E090E0A7DC809114021CDDC5 +:1015D00080933E0115C0B12C80911402B81650F4CE +:1015E000EB2DF0E0EA5EFD4F80810EDD80933E0141 +:1015F000B394F2CF809115028111C3DC80913E013A +:1016000080938B011CCF80911402811102C0B12CF8 +:1016100019C0912C292D30E08091150290E001979E +:10162000A90145575E4F5A012817390734F481E064 +:10163000F8DCF50180839394EDCF80E0F2DCF501D6 +:1016400080830EC080911502B81650F48B2C912C1B +:1016500081E0E7DCF401E557FE4F8083B394F2CFDD +:101660008091160281118DDC809115028093930187 +:101670005CC081E090E049D8BFB6F89460917200F8 +:1016800070E04091730084E790E0E6D81092720019 +:1016900055CF80918A01847319F082E090E035D8AB +:1016A00080918A0180FF08C08FB7F894C298BA9AD7 +:1016B0008FBF84E690E029D880918A0181FF08C01D +:1016C0008091940180938701809195018093880196 +:1016D00080918A0182FD25DD80918A0183FF11C0FE +:1016E00080918A0182958F708093940190919401EA +:1016F00081E0911180E08093890110928A01BA986B +:10170000AA9A80918A0184FF03C01092890138DD72 +:1017100080918A0185FF03C010928901B5DC109287 +:10172000340110928A01789402C0109234018091A1 +:101730003C01882309F40CCE80913D0181110DC03C +:101740008091380180933B018091370180933A0169 +:10175000809136018093390119C090913B0198170F +:1017600010F4C09801C0C09A90913D0180913A0157 +:10177000891710F4C19801C0C19A90913D018091E0 +:101780003901891710F4C29801C0C29A80913D01B5 +:101790008F5F80933D01DACD65E3CE0117D0809154 +:1017A0006D0090916E0061E3019610D080916D0004 +:1017B00090916E0062E3029609D073CDE199FECF5D +:1017C0009FBB8EBBE09A99278DB30895262FE19990 +:1017D000FECF1CBA9FBB8EBB2DBB0FB6F894E29A0E +:0C17E000E19A0FBE01960895F894FFCF27 +:1017EC00FF5A0A000A0803350031003200200000BD :00000001FF From af818070511e915e98273747200b19b4d7d6c7fe Mon Sep 17 00:00:00 2001 From: Peter Maivald Date: Wed, 13 Dec 2017 16:02:05 +0100 Subject: [PATCH 18/18] dwlink --- utility/core/core.cpp | 63 ++ utility/core/core.hpp | 148 ++++ utility/core/coredef.h | 20 + utility/core/i2csoft.cpp | 280 +++++++ utility/core/i2csoft.hpp | 49 ++ utility/core/rpc.cpp | 132 ++++ utility/core/rpc.hpp | 156 ++++ utility/dwlink/Makefile | 38 + utility/dwlink/dwlink.cpp | 347 ++++++++ utility/dwlink/dwlink.jpg | Bin 0 -> 67722 bytes utility/usbmon/{makefile => Makefile} | 0 utility/usbmon/build/command.cpp.o.d | 1 + utility/usbmon/build/hostio.cpp.o.d | 1 + utility/usbmon/build/hostser.cpp.o.d | 2 + utility/usbmon/build/hostusb.cpp.o.d | 2 + utility/usbmon/build/rpc.cpp.o.d | 2 + utility/usbmon/build/targexec.cpp.o.d | 1 + utility/usbmon/build/usbmon.cpp.o.d | 2 + .../make/dwire-debug/utility/core/rpc.cpp.o.d | 3 + utility/usbmon/command.cpp | 81 +- utility/usbmon/command.hpp | 10 +- utility/usbmon/hostio.cpp | 102 +++ utility/usbmon/hostio.hpp | 75 ++ utility/usbmon/hostser.cpp | 742 ++++++++++++++++++ utility/usbmon/hostser.hpp | 103 +++ utility/usbmon/hostusb.cpp | 395 +++++----- utility/usbmon/hostusb.hpp | 64 +- utility/usbmon/rpc.cpp | 29 + utility/usbmon/targexec.cpp | 92 +++ utility/usbmon/targexec.hpp | 18 + utility/usbmon/usbmon.cpp | 232 ++++-- utility/usbmon/usbmon.hpp | 5 +- 32 files changed, 2870 insertions(+), 325 deletions(-) create mode 100644 utility/core/core.cpp create mode 100644 utility/core/core.hpp create mode 100644 utility/core/coredef.h create mode 100644 utility/core/i2csoft.cpp create mode 100644 utility/core/i2csoft.hpp create mode 100644 utility/core/rpc.cpp create mode 100644 utility/core/rpc.hpp create mode 100644 utility/dwlink/Makefile create mode 100644 utility/dwlink/dwlink.cpp create mode 100644 utility/dwlink/dwlink.jpg rename utility/usbmon/{makefile => Makefile} (100%) create mode 100644 utility/usbmon/build/command.cpp.o.d create mode 100644 utility/usbmon/build/hostio.cpp.o.d create mode 100644 utility/usbmon/build/hostser.cpp.o.d create mode 100644 utility/usbmon/build/hostusb.cpp.o.d create mode 100644 utility/usbmon/build/rpc.cpp.o.d create mode 100644 utility/usbmon/build/targexec.cpp.o.d create mode 100644 utility/usbmon/build/usbmon.cpp.o.d create mode 100644 utility/usbmon/build/zaloh/make/dwire-debug/utility/core/rpc.cpp.o.d create mode 100644 utility/usbmon/hostio.cpp create mode 100644 utility/usbmon/hostio.hpp create mode 100644 utility/usbmon/hostser.cpp create mode 100644 utility/usbmon/hostser.hpp create mode 100644 utility/usbmon/rpc.cpp create mode 100644 utility/usbmon/targexec.cpp create mode 100644 utility/usbmon/targexec.hpp diff --git a/utility/core/core.cpp b/utility/core/core.cpp new file mode 100644 index 0000000..f9bcc75 --- /dev/null +++ b/utility/core/core.cpp @@ -0,0 +1,63 @@ +#include "core.hpp" + +#ifdef TICKS_TIMER + +volatile uint16_t ticks_count; +#ifdef __cplusplus +extern "C" +#endif +void TICKS_VECT() __attribute__ ((signal, used)); +void TICKS_VECT() { ++ticks_count; } + +#endif // TICKS_TIMER + +volatile uint8_t wdt_ie; +#ifndef WDTCR +#define WDTCR WDTCSR +#endif +#ifndef WDIE +#define WDIE WDTIE +#endif + +// start timeout of given period +void wdt_wdto(uint8_t period) { + wdt_enable(period); + wdt_ie = 1; + WDTCR |= _BV(WDIE); +} +void wdt_period(uint16_t period) { + wdt_wdto(period <= 15 ? WDTO_15MS : + period <= 30 ? WDTO_30MS : + period <= 60 ? WDTO_60MS : + period <= 120 ? WDTO_120MS : + period <= 250 ? WDTO_250MS : + period <= 500 ? WDTO_500MS : + period <= 1000 ? WDTO_1S : + period <= 2000 ? WDTO_2S : + period <= 4000 ? WDTO_4S : WDTO_8S); +} +void wdt_delay(uint16_t period) { + wdt_period(period); + while (wdt_running()) { } +} + +// wdt timed out, restore default value +#ifdef __cplusplus +extern "C" +#endif +void WDT_vect() __attribute__ ((signal, used)); +void WDT_vect() { wdt_enable(WDTO_2S); wdt_ie = 0; } + +// initialize after registers, but before static data +__attribute__((naked, section(".init3"), used)) void __init3(void) { + WDT_vect(); +#ifdef TICKS_TIMER + ticks_init(); +#endif // TICKS_TIMER +} + +#ifndef WProgram_h +__attribute__((naked, weak)) int main(void) { + extern void setup(), loop(); + setup(); for (;;) loop(); } +#endif // WProgram_h diff --git a/utility/core/core.hpp b/utility/core/core.hpp new file mode 100644 index 0000000..efa4595 --- /dev/null +++ b/utility/core/core.hpp @@ -0,0 +1,148 @@ +#if !defined(core_hpp) && !defined(__ASSEMBLER__) +#define core_hpp +#define Arduino_h // this is an alternate for Arduino.h + +#include +#include +#include +#include +#include +#include +#include + +#include "coredef.h" + +#ifndef Print_h + +struct fstr_t { char c; }; +#define FSTR(s) ((fstr_t*)PSTR(s)) +#define F(s) ((fstr_t*)PSTR(s)) + +#define DEC 10 +#define HEX 16 + +#endif // Print_h + +// PIN (X,N,I), REG (DDR|PORT|PIN), OP (|=|&=~|&) +#define DIO_REG(PIN,REG) MUAPK_CAT(REG,MUAPK_3_0 PIN) +#define DIO_BIT(PIN) MUAPK_3_1 PIN +#define DIO_OP(PIN,REG,OP) (DIO_REG(PIN,REG)OP(1<= 3 && p <= 10 ? 10 - p : p != 11 ? p : 3) +#define PORT_PIN_REG_OP(p,r,o) ( (p >= 3 && p <= 10) ? (r##A o MASK_PIN(p)) : (r##B o MASK_PIN(p)) ); + +inline void pinMode(uint8_t p, uint8_t d) { + if (d) PORT_PIN_REG_OP(p,DDR,|=) else PORT_PIN_REG_OP(p,DDR,&=~) } +inline void digitalWrite(uint8_t p, uint8_t d) { + if (d) PORT_PIN_REG_OP(p,PORT,|=) else PORT_PIN_REG_OP(p,PORT,&=~) } +inline uint8_t digitalRead(uint8_t p) { + return 0 != PORT_PIN_REG_OP(p,PIN,&) } + +#endif // defined(__AVR_*) + +inline unsigned long millis() { extern int millis_broken(); return millis_broken(); } +#define delay(ms) _delay_ms(ms) +inline void delayMicroseconds(uint16_t _us) { uint8_t us = _us >>= 4; do _delay_us(16); while (us-- > 0); } + +#else // Wiring_h +#define TICKS_MILLIS +inline uint16_t ticks_atomic() { return (uint16_t)millis(); } +#define TICKS_FROM_CLOCKS ((uint32_t)F_CPU / 1000) +#endif // Wiring_h + +#ifdef TICKS_TIMER + +inline void ticks_init() { +#if TICKS_TIMER == 0 + TCCR0A = 0; + TCCR0B = (_BV(CS02)|_BV(CS00)); +#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) + TIMSK0 = _BV(TOIE0); +#elif defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) + TIMSK = _BV(TOIE0); +#else // defined(__AVR_*) +#error TIM register names +#endif // defined(__AVR_*) + +#define TICKS_VECT TIM0_OVF_vect +#define TICKS_PRESCALER 1024 // CS0[2:0]: 1-1, 2-8, 3-64, 4-256, 5-1024 +#define TICKS_FROM_CLOCKS ((uint32_t)TICKS_PRESCALER*256) + +#else // TICKS_TIMER +#error "TICKS_TIMER 0 only" +#endif // TICKS_TIMER +} + +extern volatile uint16_t ticks_count; +inline uint16_t ticks_atomic() { + cli(); + uint16_t t = ticks_count; + sei(); + return t; +} +#endif // TICKS_TIMER + +#if defined(__cplusplus) && (defined(TICKS_TIMER) || defined(TICKS_MILLIS)) +#define TICKS_FROM_MILLIS(ms) uint16_t(ms / 1000 * F_CPU / TICKS_FROM_CLOCKS) +struct ticks_timeout { + uint16_t start; + inline void begin() { start = ticks_atomic(); } + inline bool done(double ms) { + // it's important for this subtraction to wrap + uint16_t time_elapsed = ticks_atomic() - start; + // rely on optimizer to do the arithmetic + return time_elapsed > TICKS_FROM_MILLIS(ms); + } + inline void add(double ms) { + start += TICKS_FROM_MILLIS(ms); + } + inline void wait(double ms) { + while (!done(ms)) { } + } +}; +struct ticks_timeout_begin : ticks_timeout { + inline ticks_timeout_begin() { begin(); } +}; +#endif // __cplusplus + +// data offsets in eeprom +#define EEO_OSCCAL ((uint8_t*)0) +#define EEO_SERIAL ((uint16_t*)1) + +// wdt_reset must be called periodically to stay alive, +// alternatively wdt_period can be used to generate a timeout + +// indicates a timeout in progress, cleared by interrupt at timeout +extern volatile uint8_t wdt_ie; + +// start timeout of given period +void wdt_wdto(uint8_t period); +void wdt_period(uint16_t period); +void wdt_delay(uint16_t period); + +// indicates a timeout in progress, cleared by interrupt at timeout +//inline uint8_t wdt_running() { return (WDTCR | _BV(WDIE)) != 0; } +inline uint8_t wdt_running() { return wdt_ie; } + +#endif // core_hpp diff --git a/utility/core/coredef.h b/utility/core/coredef.h new file mode 100644 index 0000000..7615005 --- /dev/null +++ b/utility/core/coredef.h @@ -0,0 +1,20 @@ +// items used in assembler as well as C +#ifndef MUAPK_EXP +#define MUAPK_EXP(A) A +#define MUAPK_INT(A0,A1) A0##A1 +#define MUAPK_CAT(A0,A1) MUAPK_INT(A0,A1) +#define MUAPK_2_0(A0,A1) A0 +#define MUAPK_2_1(A0,A1) A1 +#define MUAPK_3_0(A0,A1,A2) A0 +#define MUAPK_3_1(A0,A1,A2) A1 +#define MUAPK_3_2(A0,A1,A2) A2 +#define MUAPK_4_0(A0,A1,A2,A3) A0 +#define MUAPK_4_1(A0,A1,A2,A3) A1 +#define MUAPK_4_2(A0,A1,A2,A3) A2 +#define MUAPK_4_3(A0,A1,A2,A3) A3 +#define MUAPK_5_0(A0,A1,A2,A3,A4) A0 +#define MUAPK_5_1(A0,A1,A2,A3,A4) A1 +#define MUAPK_5_2(A0,A1,A2,A3,A4) A2 +#define MUAPK_5_3(A0,A1,A2,A3,A4) A3 +#define MUAPK_5_4(A0,A1,A2,A3,A4) A4 +#endif // MUAPK_EXP diff --git a/utility/core/i2csoft.cpp b/utility/core/i2csoft.cpp new file mode 100644 index 0000000..2810460 --- /dev/null +++ b/utility/core/i2csoft.cpp @@ -0,0 +1,280 @@ +#include "i2csoft.hpp" +#include +#include +#include + +#ifdef I2CSOFT_ARGS + +I2cSoft i2cSoft; + +volatile uint8_t I2cSoft::buff[I2CSOFT_BUFF_SIZE]; +uint8_t I2cSoft::state; +uint8_t I2cSoft::ibit; +uint8_t I2cSoft::shft; +uint8_t I2cSoft::regn; +volatile I2cSoft::Flag I2cSoft::flag = flNone; + +void I2cSoft::begin() { + // input, enable pullup + DIO_OP(I2CSOFT_SCL,DDR,&=~); + DIO_OP(I2CSOFT_SCL,PORT,|=); + DIO_OP(I2CSOFT_SDA,DDR,&=~); + DIO_OP(I2CSOFT_SDA,PORT,|=); + + // enable pc interrupt + GIMSK |= (1<> 1) != I2CSOFT_ADDRESS) { + // not our address + state = sIdleWait; break; + } + state = (shft & 1) ? sAckSend : sAckAddr; + } + else if (state == sRecvRegn) { + regn = shft; + state = sAckRecv; + } + else if (regn < I2CSOFT_BUFF_SIZE) { + buff[regn++] = shft; + state = sAckRecv; + } + if (state == sAckAddr || regn < I2CSOFT_BUFF_SIZE) { + // ack set low, output + DIO_OP(I2CSOFT_SDA,PORT,&=~); + DIO_OP(I2CSOFT_SDA,DDR,|=); + } + break; + + case sAckAddr: + case sAckRecv: + case sAckSend: + // SCL low, SDA output + if (!DIO_OP(I2CSOFT_SCL,PIN,&)) + break; + // SCL rose + state = state == sAckAddr ? sAckAddrWait + : state == sAckRecv ? sAckRecvWait + : sAckSendWait; + break; + + case sAckAddrWait: + case sAckRecvWait: + case sAckSendWait: + // SCL high, SDA output (or input after send) + if ( DIO_OP(I2CSOFT_SCL,PIN,&)) + break; + // SCL fell + if (state == sAckSendWait && regn < I2CSOFT_BUFF_SIZE) { + shft = buff[regn++]; + ibit = 8; + goto SendData; + } + // set input, enable pullup + DIO_OP(I2CSOFT_SDA,DDR,&=~); + DIO_OP(I2CSOFT_SDA,PORT,|=); + ibit = 8; + state = state == sAckAddrWait ? sRecvRegn + : state == sAckRecvWait ? sRecvData + : sStopWaitPre; + break; + + case sSendData: + // SCL low + if (!DIO_OP(I2CSOFT_SCL,PIN,&)) + break; + // SCL rose + state = sSendDataWait; + break; + + case sSendDataWait: + // SCL high + if ( DIO_OP(I2CSOFT_SCL,PIN,&)) + break; + // SCL fell, send next bit unless done + SendData: + if (!ibit || (shft & 0x80)) { + // set input, enable pullup + DIO_OP(I2CSOFT_SDA,DDR,&=~); + DIO_OP(I2CSOFT_SDA,PORT,|=); + } + else { + // set low, output + DIO_OP(I2CSOFT_SDA,PORT,&=~); + DIO_OP(I2CSOFT_SDA,DDR,|=); + } + shft <<= 1; + state = ibit-- ? sSendData : sAckSent; + break; + + case sAckSent: + // SCL low, SDA input + if (!DIO_OP(I2CSOFT_SCL,PIN,&)) + break; + // SCL rose, check for ack from host + state = !DIO_OP(I2CSOFT_SDA,PIN,&) + ? sAckSendWait : sIdleWait; + break; + + } +} + +#endif // I2CSOFT_ARGS diff --git a/utility/core/i2csoft.hpp b/utility/core/i2csoft.hpp new file mode 100644 index 0000000..ad919a7 --- /dev/null +++ b/utility/core/i2csoft.hpp @@ -0,0 +1,49 @@ +#ifndef i2csoft_hpp +#define i2csoft_hpp + +#include "core.hpp" + +// I2CSOFT_ARGS (SCL,SDA,pcinterrupt#,pcinterrupt) +#define I2CSOFT_ADDRESS MUAPK_EXP(MUAPK_5_0 I2CSOFT_ARGS) +#define I2CSOFT_BUFF_SIZE MUAPK_EXP(MUAPK_5_1 I2CSOFT_ARGS) +#define I2CSOFT_SCL MUAPK_EXP(MUAPK_5_2 I2CSOFT_ARGS) +#define I2CSOFT_SDA MUAPK_EXP(MUAPK_5_3 I2CSOFT_ARGS) +#define I2CSOFT_INT MUAPK_EXP(MUAPK_5_4 I2CSOFT_ARGS) + +#if defined(I2CSOFT_ARGS) +// SCL and SDA must be in same register +#elif defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) \ + || defined(__AVR_ATtiny13__) +#define I2CSOFT_ARGS (0x25,2,(B,0,),(B,1,),PCINT0_vect) + +#elif defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) +#define I2CSOFT_ARGS (0x25,2,(A,0,0),(A,1,0),PCINT0_vect) + +#else // defined(I2CSOFT_ARGS) +#error DW ports +#endif // defined(I2CSOFT_ARGS) + +extern class I2cSoft { +public: + + static void begin(); + + static volatile uint8_t buff[I2CSOFT_BUFF_SIZE]; + static volatile enum Flag : uint8_t { + flNone = 0, + flHold = 0x01, // bus held until release() + flHoldRecv = 0x02, // hold after receive + } flag; + static bool set(Flag f, bool v); + static bool test(Flag f); + static void release(); + + static void interrupt(); + static uint8_t state; + static uint8_t ibit; + static uint8_t shft; + static uint8_t regn; + +} i2cSoft; + +#endif // i2csoft_hpp diff --git a/utility/core/rpc.cpp b/utility/core/rpc.cpp new file mode 100644 index 0000000..c7b5b8a --- /dev/null +++ b/utility/core/rpc.cpp @@ -0,0 +1,132 @@ +#define RPC_DEFINE +#include "rpc.hpp" + +#include +#include + +void RpcSend::sendConsole(RpcConsole& rpc) { + send(rpc, RpcConsole_index); +} + +void RpcSend::print(const char s[]) +{ + RPCALLOCA(RpcConsole,rpc) + rpc.radix = 0; + for (char* d = rpc.text;;) { + char c = *d++ = *s; + if (c) s++; + if (d == rpc.text + sizeof(rpc.text)) { + sendConsole(rpc); + d = rpc.text; + if (!c) + break; + } + } +} + +#ifdef core_hpp +void RpcSend::print(const fstr_t* s) +{ + RPCALLOCA(RpcConsole,rpc) + rpc.radix = 0; + for (char* d = rpc.text;;) { + char c = *d++ = pgm_read_byte(s); + if (c) s++; + if (d == rpc.text + sizeof(rpc.text)) { + sendConsole(rpc); + d = rpc.text; + if (!c) + break; + } + } +} +void RpcSend::println(const fstr_t* s) { print(s); println(); } +#endif + +void RpcSend::print(int32_t v, uint8_t r) { + RPCALLOCA(RpcConsole,rpc) + rpc.value = v; + rpc.radix = r ? r | 0x80 : 0; + sendConsole(rpc); +} + +void RpcSend::print(uint32_t v, uint8_t r) { + RPCALLOCA(RpcConsole,rpc) + rpc.value = v; + rpc.radix = r; + sendConsole(rpc); +} + +void RpcSend::print(char v, uint8_t r) { print((int32_t)v, r); } +void RpcSend::print(int8_t v, uint8_t r) { print((int32_t)v, r); } +void RpcSend::print(uint8_t v, uint8_t r) { print((uint32_t)v, r); } +void RpcSend::print(int16_t v, uint8_t r) { print((int32_t)v, r); } +void RpcSend::print(uint16_t v, uint8_t r) { print((uint32_t)v, r); } +void RpcSend::println() { print((int8_t)'\n', 0); } +void RpcSend::println(char v, uint8_t r) { print((int32_t)v, r); println(); } +void RpcSend::println(const char s[]) { print(s); println(); } +void RpcSend::println(int8_t v, uint8_t r) { print(v, r); println(); } +void RpcSend::println(uint8_t v, uint8_t r) { print(v, r); println(); } +void RpcSend::println(int16_t v, uint8_t r) { print(v, r); println(); } +void RpcSend::println(uint16_t v, uint8_t r) { print(v, r); println(); } +void RpcSend::println(int32_t v, uint8_t r) { print(v, r); println(); } +void RpcSend::println(uint32_t v, uint8_t r) { print(v, r); println(); } + +uint32_t RpcSend::COBSencode(uint8_t* dest, const uint8_t* sour, uint32_t size) { + + uint8_t* d = dest; + for (const uint8_t* p = sour; p < sour + size; ) { + uint8_t code = size - (p - sour) < 254 ? size - (p - sour) : 254; + const uint8_t* e = (const uint8_t*)memchr(p, 0, code); + code = e ? e - p + 1 : code < 254 ? code + 1 : 255; + *d++ = code; + for (uint8_t i = 1; i < code; i++) + *d++ = *p++; + if (code < 0xFF) + p++; + } + // Add the phantom zero when source is zero length or ends with zero. + if (!size || !sour[size - 1]) + *d++ = 1; + return d - dest; +} + +uint32_t RpcSend::COBSencode(void (*sink)(uint8_t value), const uint8_t* sour, uint32_t size) { + + uint32_t n = 0; + for (const uint8_t* p = sour; p < sour + size; ) { + uint8_t code = size - (p - sour) < 254 ? size - (p - sour) : 254; + const uint8_t* e = (const uint8_t*)memchr(p, 0, code); + sink(code = e ? e - p + 1 : code < 254 ? code + 1 : 255); + ++n; + for (uint8_t i = 1; i < code; i++) { + sink(*p++); + ++n; + } + if (code < 0xFF) + p++; + } + // Add the phantom zero when source is zero length or ends with zero. + if (!size || !sour[size - 1]) { + sink(1); + ++n; + } + return n; +} + +uint32_t RpcSend::COBSdecode(uint8_t* dest, const uint8_t* sour, uint32_t size) { + + uint8_t* d = dest; + const uint8_t* end = sour + size; + while (sour < end) { + uint8_t code = *sour++; + if (sour + code - 1 > end) + // invalid + return 0; + for (uint8_t i = 1; i < code; i++) + *d++ = *sour++; + if (code < 0xFF && sour < end) + *d++ = 0; + } + return d - dest; +} diff --git a/utility/core/rpc.hpp b/utility/core/rpc.hpp new file mode 100644 index 0000000..c6b5b24 --- /dev/null +++ b/utility/core/rpc.hpp @@ -0,0 +1,156 @@ +#ifdef RPC_ELEMENT // list all of the rpc structures +/******************************************************************************/ +// here is the leaf of the recursion, each class has an entry here +// this part is multiply evaluated with different RPC_ELEMENT definitions + +#define RPC_ELEMENT_BASE +// the abstract base class has index 0 +RPC_ELEMENT +(Rpc, + // return the size of each class + inline static uint8_t Size(uint8_t i) { return size[i]; } + static uint8_t size[RpcNumber]; + + // run the Act member function via a 'virtual table' + inline void VtAct(uint8_t i) { (this->*vtAct[i])(); } + static void (Rpc::*vtAct[RpcNumber])(); +) +#undef RPC_ELEMENT_BASE +#define RPC_ELEMENT_BASE :Rpc + +// index 1 +RPC_ELEMENT +(RpcConsole, + union { + uint32_t value; + char text[6]; + }; + // sign bit of radix indicates signed value + // a zero doubles as terminator for text + uint8_t radix; +) + +#undef RPC_ELEMENT_BASE +#undef RPC_ELEMENT +#else // RPC_ELEMENT +/******************************************************************************/ +#ifndef RPC_DECLARE +#define RPC_DECLARE // declare items by recursive include + +#include + +// __attribute__((packed)); doesn't pack the union in RpcConsole +#pragma pack(push,1) + +// declare the struct indicies +enum Rpc_index_type : uint8_t { +#define RPC_ELEMENT(NAME,ARGUMENT) NAME##_index, +#include "rpc.hpp" +// declare the total number of structs +RpcNumber = 0 +#define RPC_ELEMENT(NAME,ARGUMENT) +1 +#include "rpc.hpp" +}; + +// declare the structs +#define RPC_ELEMENT(NAME,DECLARATION) \ +struct NAME RPC_ELEMENT_BASE { \ + NAME() = delete; /* deleted constructor */ \ + void Act(); /* called after receive */ \ + DECLARATION }; +#include "rpc.hpp" + +constexpr inline uint8_t RpcElementMax(uint8_t s0, uint8_t s1) + { return s0 < s1 ? s1 : s0; }; +// declare max size to be checked by static_assert elsewhere +enum { RpcPayloadMax = +#define RPC_ELEMENT(NAME,ARGUMENT) RpcElementMax(sizeof(NAME), +#include "rpc.hpp" + 0 +#define RPC_ELEMENT(NAME,ARGUMENT) ) +#include "rpc.hpp" +}; + +// declare helper types + +// must be used to allocate rpc structures to create space for encoding +#define RPCALLOCA(type,var) type&var=*(type*)memset \ +(alloca(Rpc::Size(type##_index)+1),0,Rpc::Size(type##_index)+1); \ +((char*)&var)[Rpc::Size(type##_index)]=type##_index; + +struct fstr_t; +class RpcSend { +public: + virtual void send(Rpc& rpc, uint8_t index) = 0; + void sendConsole(RpcConsole& rpc); + + template void out(T data) { print(data); } + RpcSend& operator<<(const char* s) { out(s); return *this; } + RpcSend& operator<<(const fstr_t* s) { out(s); return *this; } + RpcSend& operator<<(const char s) { out(s); return *this; } + RpcSend& operator<<(const int8_t s) { out(s); return *this; } + RpcSend& operator<<(const uint8_t s) { out(s); return *this; } + RpcSend& operator<<(const int16_t s) { out(s); return *this; } + RpcSend& operator<<(const uint16_t s) { out(s); return *this; } + RpcSend& operator<<(const int32_t s) { out(s); return *this; } + RpcSend& operator<<(const uint32_t s) { out(s); return *this; } + + void print(const char s[]); + void print(const fstr_t* s); + void print(char v, uint8_t r = 0); + void print(int8_t v, uint8_t r = 10); + void print(uint8_t v, uint8_t r = 10); + void print(int16_t v, uint8_t r = 10); + void print(uint16_t v, uint8_t r = 10); + void print(int32_t v, uint8_t r = 10); + void print(uint32_t v, uint8_t r = 10); + void println(); + void println(const char s[]); + void println(const fstr_t* s); + void println(char v, uint8_t r = 0); + void println(int8_t v, uint8_t r = 10); + void println(uint8_t v, uint8_t r = 10); + void println(int16_t v, uint8_t r = 10); + void println(uint16_t v, uint8_t r = 10); + void println(int32_t v, uint8_t r = 10); + void println(uint32_t v, uint8_t r = 10); + +/* http://www.stuartcheshire.org/papers/COBSforToN.pdf + +COBS encoding allows a zero byte to be used as a delimiter by producing an encoded +stream with no zeros. The maximum overhead is one byte per 254 of unencoded message +(size / 254 + 1). + +Search up to 254 bytes for a zero, place the length into the first byte and copy up to, +but not including the zero. If there are no zeros, place 255 into the first byte and +copy the 254 nonzero bytes. Encode entire buffer as if there was a zero appended unless +the last block is a run of 254, then don't append. +*/ + static uint32_t COBSencode(uint8_t* dest, const uint8_t* sour, uint32_t size); + static uint32_t COBSencode(void (*sink)(uint8_t value), const uint8_t* sour, uint32_t size); + static uint32_t COBSdecode(uint8_t* dest, const uint8_t* sour, uint32_t size); +}; + +#pragma pack(pop) +#endif // RPC_DECLARE +/******************************************************************************/ +#ifdef RPC_DEFINE +#undef RPC_DEFINE // define items by recursive include + +// define size array +uint8_t Rpc::size[] = { +#define RPC_ELEMENT(NAME,ARGUMENT) sizeof(NAME), +#include "rpc.hpp" +}; + +extern "C" void Rpc_dummy() { } +// define jump table for Act() +#define RPC_ELEMENT(NAME,ARGUMENT) void NAME::Act() __attribute__ ((weak, alias ("Rpc_dummy"))); +#include "rpc.hpp" +void (Rpc::*Rpc::vtAct[])() = { +#define RPC_ELEMENT(NAME,ARGUMENT) (void (Rpc::*)())&NAME::Act, +#include "rpc.hpp" +}; + +#endif // RPC_DEFINE +#endif // RPC_ELEMENT diff --git a/utility/dwlink/Makefile b/utility/dwlink/Makefile new file mode 100644 index 0000000..664dd98 --- /dev/null +++ b/utility/dwlink/Makefile @@ -0,0 +1,38 @@ + +MCU=attiny45 +F_CPU = 16000000 + +CPPFLAGS+=-DI2CSOFT_ARGS='(0x25,10,(B,0,),(B,1,),PCINT0_vect)' +INCLUDE_LIBS += core + +OBJDIR = build +CFLAGS = +CFLAGS += -Wno-deprecated-declarations +CFLAGS += -Wl,--gc-sections +CFLAGS += -fdata-sections -ffunction-sections +COMPILE = avr-gcc -Wall -Os --std=c++11 -save-temps=obj -DF_CPU=$(F_CPU) $(CFLAGS) $(CPPFLAGS) -mmcu=$(MCU) + +BASENAME = $(notdir $(PWD)) +OBJECTS = $(addsuffix .o, $(patsubst ..%,$(OBJDIR)%, \ +../$(BASENAME)/$(BASENAME).cpp $(wildcard ../core/*.cpp))) + +all: $(OBJDIR)/$(BASENAME).elf + +dwload: + dwdebug l $(BASENAME).elf ,qr + +clean: + rm -rf *.elf *.map $(OBJDIR) + +$(OBJDIR)/%.cpp.o: ../%.cpp + mkdir -p $(dir $@) + $(COMPILE) -c $< -o $@ + +%.i: %.cpp + $(COMPILE) -E -c $< -o $@ + +$(OBJDIR)/$(BASENAME).elf: $(OBJECTS) + $(COMPILE) -Wl,-Map,$(BASENAME).map -o $(BASENAME).elf $(OBJECTS) + +disasm: $(BASENAME).elf + avr-objdump -d $(BASENAME).elf > $(BASENAME).lss diff --git a/utility/dwlink/dwlink.cpp b/utility/dwlink/dwlink.cpp new file mode 100644 index 0000000..41917c9 --- /dev/null +++ b/utility/dwlink/dwlink.cpp @@ -0,0 +1,347 @@ +#if 0 // MAKEFILE{ + +define help_source = +a=t45 +endef + +MCU=attiny45 +F_CPU = 16000000 +CPPFLAGS+=-DI2CSOFT_ARGS='(0x25,10,(B,0,),(B,1,),PCINT0_vect)' + +# for ticks_timeout +CPPFLAGS += -DTICKS_TIMER=0 + +INCLUDE_LIBS += core + +#endif // MAKEFILE} + +#include "../core/i2csoft.hpp" + +#include +#include + + // I2c items + const uint8_t dlVersion = 1; + enum Register : uint8_t { + dlrCommand, // commands to execute + dlrReturn, // ok to overwrite when issuing command + dlrData0, // args and returns for commands + dlrData1, + dlrData2, + dlrData3, + dlrBaudrate0, // baudrate stored until next sync + dlrBaudrate1, + dlrBaudrate2, + dlrBaudrate3, + }; + enum Command : uint8_t { + dlcIdle, // no command in progress + dlcVersion, // return dlVersion in dlrData0 + dlcGetSerial, // return serial number in dlrData1:dlrData0 + dlcSetSerial, // set serial number in dlrData1:dlrData0 + dlcFcpu, // return F_CPU in dlrData3:dlrData2:dlrData1:dlrData0 + dlcSync, // sync to 0x55, then store dlrBaudrate3:dlrBaudrate2:dlrBaudrate1:dlrBaudrate0 + dlcBreakSync, // output break, then sync, then store + dlcFlagUartSoft, // return true if FlagUartSoft detected at last sync, false indicates dw + dlcPowerOn, // turn power on + dlcPowerOff, // turn power off + dlcGioSet, // set DDR to bit 2 and PORT to bit 1 (PORT first when DDR set high), then dlcGioGet + dlcGioGet, // return DDR in bit 2, PORT in bit 1, and PIN in bit 0 + }; + +bool flagUartSoft; + +// int0 same for t84 and t85 +#define SYN_PIN (B,2,x) +#define POW_PIN (B,4,x) +#define GIO_PIN (B,3,x) + +void setup() { + wdt_reset(); + +#if 0 + uint8_t osccal = eeprom_read_byte(EEO_OSCCAL); + if (osccal != 0xFF) + OSCCAL = osccal; +#endif + + // sync: set input, always + // keep pullup disabled since + // target may run at 3V3 + DIO_OP(SYN_PIN,DDR,&=~); + DIO_OP(SYN_PIN,PORT,&=~); + + // power on: set low, output + DIO_OP(POW_PIN,PORT,&=~); + DIO_OP(POW_PIN,DDR,|=); + + // hold bus after receive + i2cSoft.begin(); + i2cSoft.set(I2cSoft::flHoldRecv, true); +} + +uint16_t SyncTimer() { + // available r0,r18-r27, r30-r31, zero r1 + // x r27:r26, y r29:r28, z r31:r30 + register uint16_t sum asm("r24"); + register uint16_t cnt asm("r26"); + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + __asm__ __volatile__ (R"assembly( + clr %A[sum] ;1 init timeout + clr %B[sum] ;1 + sts flagUartSoft,%A[sum]; clear flagUartSoft +x0_%=: ; wait for start bit + adiw %A[sum],1 ;2 + sbic %[pin],%[bit] ;? + brne x0_%= ;? + breq timeout%= ;? +x1_%=: ; wait for first bit + adiw %A[sum],1 ;2 + sbis %[pin],%[bit] ;? + brne x1_%= ;? + breq timeout%= ;? + ; start measurement 8 bits + clr %A[sum] ;1 + clr %B[sum] ;1 +x2_%=: ; wait for # bit + adiw %A[sum],1 ;2 + sbic %[pin],%[bit] ;? + brne x2_%= ;? + breq timeout%= ;? +x3_%=: ; wait for # bit + adiw %A[sum],1 ;2 + sbis %[pin],%[bit] ;? + brne x3_%= ;? + breq timeout%= ;? +x4_%=: ; wait for # bit + adiw %A[sum],1 ;2 + sbic %[pin],%[bit] ;? + brne x4_%= ;? + breq timeout%= ;? +x5_%=: ; wait for # bit + adiw %A[sum],1 ;2 + sbis %[pin],%[bit] ;? + brne x5_%= ;? + breq timeout%= ;? +x6_%=: ; wait for # bit + adiw %A[sum],1 ;2 + sbic %[pin],%[bit] ;? + brne x6_%= ;? + breq timeout%= ;? +x7_%=: ; wait for # bit + adiw %A[sum],1 ;2 + sbis %[pin],%[bit] ;? + brne x7_%= ;? + breq timeout%= ;? +x8_%=: ; wait for # bit + adiw %A[sum],1 ;2 + sbic %[pin],%[bit] ;? + brne x8_%= ;? + breq timeout%= ;? +x9_%=: ; wait for # bit + adiw %A[sum],1 ;2 + sbis %[pin],%[bit] ;? + brne x9_%= ;? + breq timeout%= ;? + ; end measurement, time = 5 * sum + ; now see if we get a short break within 2 byte times + clr %A[cnt] + clr %B[cnt] + sub %A[cnt],%A[sum] ; one byte's worth of time + sbc %B[cnt],%B[sum] +b0_%=: ; wait for start bit + adiw %A[cnt],1 ;2 + sbic %[pin],%[bit] ;? + brne b0_%= ;? + brne uartSoft%= ;? + sub %A[cnt],%A[sum] ; one byte's worth of time + sbc %B[cnt],%B[sum] +b1_%=: ; wait for start bit + adiw %A[cnt],1 ;2 + sbic %[pin],%[bit] ;? + brne b1_%= ;? + brne uartSoft%= ;? + ; timeout waiting for short break + rjmp done%= +timeout%=: + clr %A[sum] + clr %B[sum] + rjmp done%= +uartSoft%=: + ldi %A[cnt],1 + sts flagUartSoft,%A[cnt]; set flagUartSoft +done%=: + +)assembly" + : [sum] "=&r" (sum) + ,[cnt] "=&r" (cnt) + : [pin] "I" (_SFR_IO_ADDR(DIO_REG(SYN_PIN,PIN))) + ,[bit] "I" (DIO_BIT(SYN_PIN)) + : + ); + } + return sum; +} + +void loop() { + wdt_reset(); + + // execute commands after i2c receives (held bus) + if (!i2cSoft.test(I2cSoft::flHold)) return; + + i2cSoft.buff[dlrReturn] = 0; + switch (i2cSoft.buff[dlrCommand]) { + case dlcVersion: + i2cSoft.buff[dlrReturn] = dlVersion; + break; + + case dlcGetSerial: { + uint16_t v = eeprom_read_word(EEO_SERIAL); + if (v != (uint16_t)-1) { + i2cSoft.buff[dlrData0] = v; + i2cSoft.buff[dlrData1] = v >> 8; + } + break; + } + + case dlcSetSerial: { + uint16_t v = i2cSoft.buff[dlrData0] | ((uint16_t)i2cSoft.buff[dlrData1] << 8); + eeprom_write_word(EEO_SERIAL, v); + break; + } + + case dlcFcpu: + i2cSoft.buff[dlrData0] = (uint8_t)F_CPU; + i2cSoft.buff[dlrData1] = (uint8_t)(F_CPU >> 8); + i2cSoft.buff[dlrData2] = (uint8_t)(F_CPU >> 16); + i2cSoft.buff[dlrData3] = (uint8_t)(F_CPU >> 24); + break; + + case dlcBreakSync: + // set low, output + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + DIO_OP(SYN_PIN,PORT,&=~); + DIO_OP(SYN_PIN,DDR,|=); + } + + // requires interrupts on + wdt_delay(50); + + // now turn them off for timing + __asm__ __volatile__ ("cli" ::: "memory"); + + // set input, disable pullup + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + DIO_OP(SYN_PIN,DDR,&=~); + DIO_OP(SYN_PIN,PORT,&=~); + } + + // allow for rise time of 10us with no pullup + // rise time is below 2us with 10K pullup + // t84 @ 8MHz, divisor 128 has 160us delay before sync + // so, there should be plenty of time + _delay_us(10); + + // fall through + case dlcSync: { + uint32_t ticks = SyncTimer(); + uint32_t baud = !ticks ? 0 : (F_CPU * 8 + (ticks * 5) / 2) / (ticks * 5); + i2cSoft.buff[dlrBaudrate0] = baud; + i2cSoft.buff[dlrBaudrate1] = baud >> 8; + i2cSoft.buff[dlrBaudrate2] = baud >> 16; + i2cSoft.buff[dlrBaudrate3] = baud >> 24; + } + // turn ints back on + __asm__ __volatile__ ("sei" ::: "memory"); + break; + + case dlcFlagUartSoft: + i2cSoft.buff[dlrData0] = flagUartSoft; + break; + + case dlcPowerOn: + DIO_OP(POW_PIN,PORT,&=~); + break; + + case dlcPowerOff: + DIO_OP(POW_PIN,PORT,|=); + break; + + case dlcGioSet: + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + if (i2cSoft.buff[dlrData0] & 4) { + if (i2cSoft.buff[dlrData0] & 2) + DIO_OP(GIO_PIN,PORT,|=); + else + DIO_OP(GIO_PIN,PORT,&=~); + DIO_OP(GIO_PIN,DDR,|=); + } else { + DIO_OP(GIO_PIN,DDR,&=~); + if (i2cSoft.buff[dlrData0] & 2) + DIO_OP(GIO_PIN,PORT,|=); + else + DIO_OP(GIO_PIN,PORT,&=~); + } + } + + // fall through + case dlcGioGet: + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + i2cSoft.buff[dlrData0] + = (DIO_OP(GIO_PIN,DDR,&) ? 4 : 0) + | (DIO_OP(GIO_PIN,PORT,&) ? 2 : 0) + | (DIO_OP(GIO_PIN,PIN,&) ? 1 : 0); + } + break; + + } + // clear command and release bus + i2cSoft.buff[dlrCommand] = dlcIdle; + i2cSoft.release(); +} + +/* serial port raft + + 10K +5V-----zz---o--->|---GioSDAi,CTS# +SCL<--------------------zz--o---|\ --|||-- + | | \C CBE + | | + --||--o--->V+ + 10n | + .1u | +GND----||---o----- + | + z + 10K z + | + o--->|---RXD + | + ----------DW + ________________________ + | | 5V + | | J +------| | 3V3 + | | GIO + | | POW +------| | DW + | | GND + |______________________| V+ + +*/ diff --git a/utility/dwlink/dwlink.jpg b/utility/dwlink/dwlink.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fbad0a5551c7cd096c6dcce7741c85a5827d02ff GIT binary patch literal 67722 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 tinETn_dDXQJk;uFi- 4 +#include +#endif +#include +#include +#include +#include + +void LuXact::Label() +{ + // only output label to tty + if (!isatty(fileno(stdout)) || !isatty(fileno(stderr))) return; + + std::cerr << "\e]0;"; + if (!IsOpen()) std::cerr << '['; + std::cerr << label.Display(); + if (!IsOpen()) std::cerr << ']'; + std::cerr << '\a'; +} + +bool LuXact::Reset() { + return false; +}; + +bool LuXact::ResetDw() { + return false; +}; + +bool LuXact::ResetPower() { + return false; +}; + +bool LuXact::SetSerial(const char* serial) { + return false; +} + +void LuXact::Label::AddSegment(size_t n) { + for (size_t i = 0; i < n; ++i) + push_back(0); +} + +std::string LuXact::Label::Display() { + std::string ret{*this}; + for (size_t pos = 0; (pos = ret.find_first_of((char)0, pos)) != std::string::npos;) { + char space = ' '; + ret.replace(pos++, 1, &space, 1); + } + return ret; +} + +std::string LuXact::Label::Segment(size_t n) { + size_t pos = SegmentPos(n); + // std::cerr << "pos:" << pos << '\n'; + if (pos == std::string::npos) + return std::string{}; + size_t len = SegmentLen(pos); + //std::cerr << "len:" << len << '\n'; + return substr(pos, len); +} + +void LuXact::Label::Segment(size_t n, std::string v) { + size_t pos = SegmentPos(n); + if (pos == std::string::npos) + return; + size_t len = SegmentLen(pos); + replace(pos, len, v); +} + +size_t LuXact::Label::SegmentPos(size_t n) { + size_t pos = 0; + for (size_t i = 0; i < n; ++i) { + pos = find_first_of((char)0, pos); + if (pos == std::string::npos) + return pos; + ++pos; + } + return pos; +} + +size_t LuXact::Label::SegmentLen(size_t pos) { + size_t len = find_first_of((char)0, pos); + return len == std::string::npos ? len : len -= pos; +} + +std::string LuXact::to_bytes(const char16_t* wptr) { + if (!wptr) return std::string{}; +#if __GNUC__ > 4 + // convert utf16 to utf8 + std::wstring_convert,char16_t> convert; +#else + // this only handles ASCII + struct { std::string (*to_bytes)(const char16_t* p); } convert; + convert.to_bytes = [&](const char16_t* p)->std::string { + std::string r; + for (; *p; ++p) + r += (char)(*p & 0xFF); + return r; }; +#endif + return convert.to_bytes(wptr); +} diff --git a/utility/usbmon/hostio.hpp b/utility/usbmon/hostio.hpp new file mode 100644 index 0000000..14c76dd --- /dev/null +++ b/utility/usbmon/hostio.hpp @@ -0,0 +1,75 @@ +#ifndef hostio_hpp +#define hostio_hpp + +#include + +class LuXact { +public: + + class Exception : public std::runtime_error { using std::runtime_error::runtime_error; }; + + // draw label on console + virtual void Label(); + + // set callback for characters received + inline void Sink(int (*_sink)(const void*)) { sink = _sink; } + + virtual bool Open() = 0; + virtual bool IsOpen() const = 0; + virtual bool Close() = 0; + + // send a single character + virtual void Send(char data) = 0; + + // send an Rpc object, must have been allocated by + // RPCALLOCA to allow encoding for transmission + virtual void Send(struct Rpc& rpc, uint8_t index) = 0; + + // receive data and process + virtual void Recv() = 0; + + // software reset + virtual bool Reset(); + + // exit dw mode (reset) if possible + virtual bool ResetDw(); + + // power cycle if possible + virtual bool ResetPower(); + + // update serial number + virtual bool SetSerial(const char* serial); + +protected: + + // holds string for console label + // nulls get converted to spaces + class Label : public std::string { + public: + + // don't hide assignment operator + using std::string::operator=; + + // add blank segments to end + void AddSegment(size_t n); + + // return string for display + std::string Display(); + + // return, set segment + std::string Segment(size_t n); + void Segment(size_t n, std::string v); + + // return position, length of segment + size_t SegmentPos(size_t n); + size_t SegmentLen(size_t pos); + + } label; + + // convert char16_t[] to string + std::string to_bytes(const char16_t* wptr); + + int (*sink)(const void*) = nullptr; +}; + +#endif diff --git a/utility/usbmon/hostser.cpp b/utility/usbmon/hostser.cpp new file mode 100644 index 0000000..e4fe3a1 --- /dev/null +++ b/utility/usbmon/hostser.cpp @@ -0,0 +1,742 @@ +#include "command.hpp" +#include "rpc.hpp" +#include "hostser.hpp" + +#include +#include +#include +#include +#include +#include +#include // ioctl +#include // flock +#include + +LuXactSer::LuXactSer(const char* serialnumber) { + label.AddSegment(sgNumber - 1); + label.Segment(sgSerial, serialnumber); + + I2cAddr = 0x25; + I2cSCLo = TIOCM_DTR; + I2cSDAo = TIOCM_RTS; + I2cSDAi = TIOCM_CTS; +} + +LuXactSer::~LuXactSer() { + Close(); +} + +bool LuXactSer::Open() { + Close(); + + struct Dir { + Dir() { dir = opendir("/dev"); } + ~Dir() { closedir(dir); } + operator DIR*() { return dir; } + DIR* dir; + } dir; + + dirent* entry; + while ((entry = readdir(dir)) != nullptr) { + if (strncmp("ttyUSB", entry->d_name, 6)) + continue; + + // open file and acquire exclusive lock + fd = open((std::string("/dev/") + entry->d_name).c_str(), O_RDWR|O_NOCTTY); + if (fd < 0) + continue; + + if (flock(fd, LOCK_EX | LOCK_NB) < 0) { + close(fd); fd = -1; + continue; + } + + // initialize i2c + Gio(); + I2cInit(); + + // get dwlink version + { + char command = dlcVersion; + uint8_t version = 0; + if (I2cWrite(dlrCommand, &command, sizeof(command)) != sizeof(command) + || I2cRead(dlrReturn, (char*)&version, sizeof(version)) != sizeof(version) + || version != dlVersion) { + if (version != dlVersion) + std::cerr << "incorrect dwlink version\n"; + close(fd); fd = -1; + continue; + } + } + + // get serial number + union { + char buffer[]; + uint16_t value; + operator uint16_t() { return value; } + } serial; { + char command = dlcGetSerial; + if (I2cWrite(dlrCommand, &command, sizeof(command)) != sizeof(command) + || I2cRead(dlrData0, serial.buffer, sizeof(serial)) != sizeof(serial)) { + close(fd); fd = -1; + continue; + } + } + + // check serial number match + std::string sSerial{label.Segment(sgSerial)}; + if (sSerial.length() != 0 && (char16_t)strtol(sSerial.c_str(), nullptr, 0) != serial) { + close(fd); fd = -1; + continue; + } + + // save device name and serial number + label.Segment(sgDevice, entry->d_name); + if (sSerial.length() == 0) + label.Segment(sgSerial, std::to_string(serial)); + break; + } + if (!entry) + return false; + + // clear input buffer + recv_size = 0; + recv_skip = 0; + + Label(); + return true; +} + +bool LuXactSer::IsOpen() const { + return fd >= 0; +} + +bool LuXactSer::Close() { + if (!IsOpen()) return false; + close(fd); + fd = -1; + + Label(); + return true; +} + +void LuXactSer::Send(char _data) { + char data[3] = { 0, _data, 0 }; + int xfer = write(fd, data, sizeof(data)); + if (xfer > 0) + recv_skip += xfer; +} + +void LuXactSer::Send(struct Rpc& rpc, uint8_t index) { +} + +//#define DEBUG_Recv +void LuXactSer::Recv() { + for (;;) { + +#ifdef DEBUG_Recv + { + std::cerr << "recv_size: " << (int)recv_size << ", recv_skip: " << (int)recv_skip << ", read:"; + for (uint16_t i = 0; i < recv_size; ++i) std::cerr << ' ' << (int)recv_buff[i]; + std::cerr << "\n";} +#endif + + uint8_t data[4097]; + if (recv_skip) { + int xfer = read(fd, data, recv_skip < sizeof(data) ? recv_skip : sizeof(data)); + if (xfer < 0) return; + recv_skip -= xfer; + if (recv_skip) return; + } + + memcpy(data, recv_buff, recv_size); + int xfer = read(fd, data + recv_size, sizeof(data) - 1 - recv_size); + if (xfer <= 0) return; + +#ifdef DEBUG_Recv + if(xfer){ + std::cerr << "recv_size: " << (int)recv_size << ", xfer: " << xfer << ", read:"; + for (uint16_t i = 0; i < xfer; ++i) std::cerr << ' ' << (int)data[recv_size + i]; + std::cerr << "\n";} +#endif + + xfer += recv_size; + + // this make strlen work on the last packet if there's no zero + data[xfer] = '\0'; + + uint8_t* p = data; + while (p - data < xfer) { + if (!p[0]) { + ++p; + continue; + } + uint16_t size = strlen((char*)p); + uint8_t rpc[256]; + if (size > sizeof(rpc)) { + // too long + p += size + 1; +#ifdef DEBUG_Recv + std::cerr << "too long\n"; +#endif + continue; + } + if (p - data + size >= xfer) { + // partial packet (no terminating zero) +#ifdef DEBUG_Recv + std::cerr << "partial packet\n"; +#endif + break; + } + uint8_t* t = p; p += size + 1; + size = RpcSend::COBSdecode(rpc, t, size); +#ifdef DEBUG_Recv + { + std::cerr << "rpc: " << (int)size; + for (uint16_t i = 0; i < size; ++i) std::cerr << ' ' << (int)rpc[i]; + std::cerr << "\n";} +#endif + if (!rpc[size - 1]) { + if (sink) + sink(rpc); + } + else if (rpc[size - 1] < RpcNumber) + ((Rpc*)rpc)->VtAct(rpc[size - 1]); + } + recv_size = xfer - (p - data) < sizeof(recv_buff) ? xfer - (p - data) : 0; + memcpy(recv_buff, p, recv_size); + } +} + +void LuXactSer::Break(uint8_t length) { + Ioctl(TCFLSH, TCIOFLUSH); + Ioctl(TIOCSBRK); + usleep(length * 1000); + Ioctl(TIOCCBRK); +} + +bool LuXactSer::Reset() { + return false; +} + +bool LuXactSer::ResetDw() { + // break and ignore all received bytes + union { + char buffer[]; + uint32_t value; + operator uint32_t() { return value; } + } baudrate; { + char command = dlcBreakSync; + if (I2cWrite(dlrCommand, &command, sizeof(command)) != sizeof(command) + || I2cRead(dlrBaudrate0, baudrate.buffer, sizeof(baudrate)) != sizeof(baudrate) + || !baudrate) + return false; + } + + union { + char buffer[]; + uint8_t value; + operator uint8_t() { return value; } + } flagUartSoft; { + char command = dlcFlagUartSoft; + if (I2cWrite(dlrCommand, &command, sizeof(command)) != sizeof(command) + || I2cRead(dlrData0, flagUartSoft.buffer, sizeof(flagUartSoft)) != sizeof(flagUartSoft)) + return false; + } + + label.Segment(sgBaudrate, std::to_string(baudrate) + " Bd"); + std::cerr << label.Display() << (flagUartSoft ? " UartSoft" : " dw") << '\n'; + + // set baudrate + Baudrate(baudrate); + + // Disable Dw if it was enabled + if (!flagUartSoft) { + // this resets the target and turns off dw + // which stays off until a power cycle + char command = 6; + int xfer = write(fd, &command, sizeof(command)); + recv_skip = sizeof(command); + } + + Label(); + return true; +} + +bool LuXactSer::ResetPower() { + char command = dlcPowerOff; + if (I2cWrite(dlrCommand, &command, sizeof(command)) != sizeof(command)) return false; + usleep(100 * 1000); + command = dlcPowerOn; + if (I2cWrite(dlrCommand, &command, sizeof(command)) != sizeof(command)) return false; + usleep(100 * 1000); + return true; +} + +/* power supply + +V+5--------------o + | _______ + | ---zz--o---|\ --|||-- + | | \C CBE + | | + --||--o---->V+ + 10n + +V+ on at boot when PB0 tristated. +V+ off when PB0 set high to turn off Q. +*/ + +bool LuXactSer::SetSerial(const char* _serial) { + uint16_t serial = strtol(_serial, nullptr, 0); + char command[4]; + command[0] = dlcSetSerial; + command[1] = 0; + command[2] = serial; + command[3] = serial >> 8; + if (I2cWrite(dlrCommand, command, sizeof(command)) != sizeof(command)) { + std::cerr << "error changing serial number" << std::endl; + return false; + } + return true; +} + +void LuXactSer::Baudrate(uint32_t value) { + //https://linux.die.net/man/3/termios + //http://man7.org/linux/man-pages/man3/termios.3.html + //http://man7.org/linux/man-pages/man4/tty_ioctl.4.html + + termios2 config; + Ioctl(TCGETS2, &config); + + config.c_cflag = CS8 | BOTHER | CLOCAL; + config.c_iflag = IGNPAR | IGNBRK; + config.c_oflag = 0; + config.c_lflag = 0; + config.c_ispeed = value; + config.c_ospeed = value; + config.c_cc[VMIN] = 0; // minimum # of characters + config.c_cc[VTIME] = 0; // read timeout * .1 sec + Ioctl(TCSETS2, &config); + + // flush input and output + Ioctl(TCFLSH, TCIOFLUSH); + + // clear input buffer + recv_size = 0; + recv_skip = 0; +} + +void LuXactSer::Ioctl(unsigned long request, ...) { + + va_list va; + va_start(va, request); + void* argp = va_arg(va, void*); + va_end(va); + + for (uint8_t i = 0; i < 5; ++i) { + if (ioctl(fd, request, argp) >= 0) { + if (i > 0) + std::cerr << "ioctl: " << (int)i << '\n'; + return; + } + usleep(100); + } + SysAbort(__PRETTY_FUNCTION__); +} + +uint32_t LuXactSer::Gio() { + Ioctl(TIOCMGET, &gio); + return gio; +} + +void LuXactSer::Gio(uint32_t value, uint32_t mask) { + uint32_t last = gio; + gio = (gio &~ mask) | (value & mask); + if (gio != last) + Ioctl(TIOCMSET, &gio); +} + +//http://www.robot-electronics.co.uk/i2c-tutorial + +uint8_t LuXactSer::I2cWrite(uint8_t regn, const char* data, uint8_t size) { + // wait for bus + for (uint8_t i = 0; i < 100; ++i) { + if (I2cSDA()) break; + usleep(1000); + } + if (!I2cSDA()) return -1; + I2cStart(); + const char* p = data; + if (I2cSend(I2cAddr << 1) || I2cSend(regn)) + goto error; + while (p - data < size) + if (I2cSend(*p++)) + break; + error: + I2cStop(); + return p != data ? p - data : -1; +} + +uint8_t LuXactSer::I2cRead(uint8_t regn, char* data, uint8_t size) { + // wait for bus + for (uint8_t i = 0; i < 100; ++i) { + if (I2cSDA()) break; + usleep(1000); + } + if (!I2cSDA()) return -1; + I2cStart(); + char* p = data; + if (I2cSend(I2cAddr << 1) || I2cSend(regn)) + goto error; + I2CRestart(); + if (I2cSend((I2cAddr << 1) | 1)) + goto error; + while (p - data < size) { + *p++ = I2cRecv(); + if (p - data < size) + I2cAck(); else I2cNak(); + } + error: + I2cStop(); + return p != data ? p - data : -1; +} + +void LuXactSer::I2cSCL(bool v) { + Gio(!v ? -1 : 0, I2cSCLo); + usleep(4); +} + +void LuXactSer::I2cSDA(bool v) { + Gio(!v ? -1 : 0, I2cSDAo); + usleep(4); +} + +bool LuXactSer::I2cSDA() { + return (Gio() & I2cSDAi) == 0; +} + +void LuXactSer::I2cInit() { + I2cSDA(1); I2cSCL(1); } + +void LuXactSer::I2cStart() { + I2cSDA(0); I2cSCL(0); } + +void LuXactSer::I2CRestart() { + I2cSDA(1); I2cSCL(1); I2cSDA(0); I2cSCL(0); } + +void LuXactSer::I2cStop() { + I2cSCL(0); I2cSDA(0); I2cSCL(1); I2cSDA(1); } + +void LuXactSer::I2cAck() { + I2cSDA(0); I2cSCL(1); I2cSCL(0); I2cSDA(1); } + +void LuXactSer::I2cNak() { + I2cSDA(1); I2cSCL(1); I2cSCL(0); } + +bool LuXactSer::I2cSend(uint8_t data) { + for (uint8_t i = 0; i < 8; ++i) { + I2cSDA(data & 0x80); data <<= 1; + I2cSCL(1); I2cSCL(0); + } + I2cSDA(1); I2cSCL(1); + bool ack = I2cSDA(); + I2cSCL(0); + return ack; +} + +uint8_t LuXactSer::I2cRecv() { + + uint8_t data = 0; + for (uint8_t i = 0; i < 8; i++) { + I2cSCL(1); + data = (data << 1) | I2cSDA(); + I2cSCL(0); + } + return data; +} + +void LuXactSer::I2cReset() { + I2cSDA(1); + for (uint8_t i = 0; i < 16; ++i) { + I2cSCL(0); I2cSCL(1); + } + I2cStop(); +} + +void LuXactSer::I2cDebug() { + auto i2cWrite = [&](uint8_t regn, uint8_t data)->void { + I2cStart(); + std::cerr << (I2cSend((0x25<<1)) ? "nak " : "ack "); + std::cerr << (I2cSend(regn) ? "nak " : "ack "); + std::cerr << (I2cSend(data) ? "nak\n" : "ack\n"); + I2cStop(); + }; + auto i2cRead = [&](uint8_t regn)->uint8_t { + I2cStart(); + std::cerr << (I2cSend((0x25<<1)) ? "nak " : "ack "); + std::cerr << (I2cSend(regn) ? "nak " : "ack "); + I2CRestart(); + std::cerr << (I2cSend((0x25<<1) | 1) ? "nak " : "ack "); + uint8_t data = I2cRecv(); + std::cerr << std::hex << (int)data << std::dec << '\n'; + I2cNak(); + I2cStop(); + return data; + }; + + for (uint8_t i = 0;;++i) { + std::cerr << "c: SCL 0, C: SCL 1, d: SDA 0, D: SDA 1, 0: zero, 1: one\n" + "setup, f: write, g: read\n" + "read, h: -1, i: 0, j: 1, k: 2 \n" + "write, r: -1, s: 0, t: 1, u: 2 \n" + "2: idle, 3: start, 4: stop, 5: cycle clock, 6: cycle clock * 8, 8: reset\n"; + std::cerr << "sda: " << (int)I2cSDA() << '\n'; + switch (SysConRead()) { + case 'c':I2cSCL(0);break; + case 'C':I2cSCL(1);break; + case 'd':I2cSDA(0);break; + case 'D':I2cSDA(1);break; + case '0':I2cSDA(0);I2cSCL(1);I2cSCL(0);break; + case '1':I2cSDA(1);I2cSCL(1);I2cSCL(0);break; + case '2':I2cInit();break; + case '3':I2cStart();break; + case '4':I2cStop();break; + case '5':I2cSCL(0);I2cSCL(1);break; + case '6':for(uint8_t i=0;i<8;++i){I2cSCL(0);I2cSCL(1);}break; + case '8':I2cReset();break; + + case 'f':I2cStart();I2cSend((0x25<<1));break; + case 'g':I2cStart();I2cSend((0x25<<1));I2cSend(0);I2cStop(); + I2cStart();I2cSend((0x25<<1)|1);break; + case 'e':I2cStart();I2cSend((0x25<<1));I2cSend(0);I2cStop(); + I2cStart(); + + {uint8_t data=(0x25<<1)|1; + for (uint8_t i = 0; i < 8; ++i) { + I2cSDA(data & 0x80); data <<= 1; + I2cSCL(1); I2cSCL(0); + } + I2cSDA(1); + } + break; + + case 'h':i2cRead(-1);break; + case 'i':i2cRead(0);break; + case 'j':i2cRead(1);break; + case 'k':i2cRead(2);break; + + case 'r':i2cWrite(-1,i);break; + case 's':i2cWrite(0,i);break; + case 't':i2cWrite(1,i);break; + case 'u':i2cWrite(2,i);break; + } + } +} + +int count = 0; +uint8_t buf1[1024]; +int count2 = 0; +uint8_t buf2[1024]; + +void LuXactSer::Debug() { + + for(;;) { + { + char command = dlcGioSet; + char data = 2; + std::cerr << "data: " << (int)data << '\n'; + if (I2cWrite(dlrData0, &data, sizeof(data)) != sizeof(data) + || I2cWrite(dlrCommand, &command, sizeof(command)) != sizeof(command) + ) + std::cerr << "bad\n"; + } + { + char command = dlcGioGet; + char data = 4; + if (I2cWrite(dlrCommand, &command, sizeof(command)) != sizeof(command) + || I2cRead(dlrData0, &data, sizeof(data)) != sizeof(data)) + std::cerr << "bad\n"; + std::cerr << "data: " << (int)data << '\n'; + } + usleep(1000*1000); + { + char command = dlcGioSet; + char data = 4; + std::cerr << "data: " << (int)data << '\n'; + if (I2cWrite(dlrData0, &data, sizeof(data)) != sizeof(data) + || I2cWrite(dlrCommand, &command, sizeof(command)) != sizeof(command) + ) + std::cerr << "bad\n"; + } + { + char command = dlcGioGet; + char data = 4; + if (I2cWrite(dlrCommand, &command, sizeof(command)) != sizeof(command) + || I2cRead(dlrData0, &data, sizeof(data)) != sizeof(data)) + std::cerr << "bad\n"; + std::cerr << "data: " << (int)data << '\n'; + } + usleep(1000*1000); + } + + //I2cDebug(); + + + auto sink = [&](uint8_t value)->void { + buf1[count++] = value; + }; + auto sink2 = [&](uint8_t value)->void { + buf2[count2++] = value; + }; + +#if 0 + std::cerr << std::hex; + + for (uint32_t i = 0; i < 256; ++i) + sink(i); + std::cerr << '\n'; +#endif + + if(0){ +static uint8_t buf0[] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 +, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 +, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48 +, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64 +, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80 +, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96 +, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,112 +,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128 +,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144 +,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160 +,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176 +,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192 +,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208 +,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224 +,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240 +,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 + }; +} + + if(0){ +static uint8_t buf0[] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 +, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 +, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48 +, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64 +, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80 +, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96 +, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,112 +,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128 +,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144 +,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160 +,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176 +,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192 +,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208 +,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224 +,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240 + ,241,242,243,244,245,246,247,248,249,250,251,252,0 + }; + + std::cerr << "i: " << sizeof(buf0); + for (uint32_t i = 0; i < sizeof(buf0); ++i) { + if ((i & 0xF) == 0) + fprintf(stderr, "\n"); + fprintf(stderr, ",%3d", buf0[i]); + } + std::cerr << '\n'; + + count = 0; RpcSend::COBSencode(sink, buf0, sizeof(buf0)); + memset(buf1, 0, sizeof(buf1)); + RpcSend::COBSencode(buf1, buf0, sizeof(buf0)); + + std::cerr << "e: " << count; + for (uint32_t i = 0; i < count; ++i) { + if ((i & 0xF) == 0) + fprintf(stderr, "\n"); + fprintf(stderr, ",%3d", buf1[i]); + } + std::cerr << '\n'; + + count2 = 0; //RpcSend::COBSdecode(sink2, buf1, count); + memset(buf2, 0, sizeof(buf2)); + count2 = RpcSend::COBSdecode(buf2, buf1, count); + + std::cerr << "d: " << count2; + if (sizeof(buf0) != count2) std::cerr << "***"; + for (uint32_t i = 0; i < count2; ++i) { + if ((i & 0xF) == 0) + fprintf(stderr, "\n"); + fprintf(stderr, ",%3d", buf2[i]); + if (buf2[i] != buf0[i]) std::cerr << "***"; + } + std::cerr << '\n'; +} + if(1){ +static uint8_t buf0[] = { + 1, 2, 3 +}; + + std::cerr << "i: " << sizeof(buf0); + for (uint32_t i = 0; i < sizeof(buf0); ++i) { + if ((i & 0xF) == 0) + fprintf(stderr, "\n"); + fprintf(stderr, ",%3d", buf0[i]); + } + std::cerr << '\n'; + + memset(buf2, 0, sizeof(buf2)); + count2 = RpcSend::COBSdecode(buf2, buf0, sizeof(buf0)); + + std::cerr << "d: " << count2; + for (uint32_t i = 0; i < count2; ++i) { + if ((i & 0xF) == 0) + fprintf(stderr, "\n"); + fprintf(stderr, ",%3d", buf2[i]); + } + std::cerr << '\n'; + +} + if(1){ +static uint8_t buf0[] = { + 23,24,0 + }; + + std::cerr << "i: " << sizeof(buf0); + for (uint32_t i = 0; i < sizeof(buf0); ++i) { + if ((i & 0xF) == 0) + fprintf(stderr, "\n"); + fprintf(stderr, ",%3d", buf0[i]); + } + std::cerr << '\n'; + + count = 0; RpcSend::COBSencode(sink, buf0, sizeof(buf0)); + memset(buf1, 0, sizeof(buf1)); + RpcSend::COBSencode(buf1, buf0, sizeof(buf0)); + + std::cerr << "e: " << count; + for (uint32_t i = 0; i < count; ++i) { + if ((i & 0xF) == 0) + fprintf(stderr, "\n"); + fprintf(stderr, ",%3d", buf1[i]); + } + std::cerr << '\n'; + + count2 = 0; //RpcSend::COBSdecode(sink2, buf1, count); + memset(buf2, 0, sizeof(buf2)); + count2 = RpcSend::COBSdecode(buf2, buf1, count); + + std::cerr << "d: " << count2; + if (sizeof(buf0) != count2) std::cerr << "***"; + for (uint32_t i = 0; i < count2; ++i) { + if ((i & 0xF) == 0) + fprintf(stderr, "\n"); + fprintf(stderr, ",%3d", buf2[i]); + if (buf2[i] != buf0[i]) std::cerr << "***"; + } + std::cerr << '\n'; +} +} diff --git a/utility/usbmon/hostser.hpp b/utility/usbmon/hostser.hpp new file mode 100644 index 0000000..287ff85 --- /dev/null +++ b/utility/usbmon/hostser.hpp @@ -0,0 +1,103 @@ +#ifndef hostser_hpp +#define hostser_hpp + +#include "hostio.hpp" + +class LuXactSer : public LuXact { +public: + + LuXactSer(const char* serialnumber = nullptr); + ~LuXactSer(); + + bool Open(); + bool IsOpen() const; + bool Close(); + + void Send(char data); + void Send(struct Rpc& rpc, uint8_t index); + void Recv(); + + void Break(uint8_t length); + bool Reset(); + bool ResetDw(); + bool ResetPower(); + + bool SetSerial(const char* serial); + + void Debug(); + +protected: + + // label segments + enum { + sgDevice, + sgSerial, + sgBaudrate, + sgNumber + }; + + void Baudrate(uint32_t value); + void Ioctl(unsigned long request, ...); + uint32_t Gio(); + void Gio(uint32_t value, uint32_t mask); + + // I2c items + const uint8_t dlVersion = 1; + enum Register : uint8_t { + dlrCommand, // commands to execute + dlrReturn, // ok to overwrite when issuing command + dlrData0, // args and returns for commands + dlrData1, + dlrData2, + dlrData3, + dlrBaudrate0, // baudrate stored until next sync + dlrBaudrate1, + dlrBaudrate2, + dlrBaudrate3, + }; + enum Command : uint8_t { + dlcIdle, // no command in progress + dlcVersion, // return dlVersion in dlrData0 + dlcGetSerial, // return serial number in dlrData1:dlrData0 + dlcSetSerial, // set serial number in dlrData1:dlrData0 + dlcFcpu, // return F_CPU in dlrData3:dlrData2:dlrData1:dlrData0 + dlcSync, // sync to 0x55, then store dlrBaudrate3:dlrBaudrate2:dlrBaudrate1:dlrBaudrate0 + dlcBreakSync, // output break, then sync, then store + dlcFlagUartSoft, // return true if FlagUartSoft detected at last sync, false indicates dw + dlcPowerOn, // turn power on + dlcPowerOff, // turn power off + dlcGioSet, // set DDR to bit 2 and PORT to bit 1 (PORT first when DDR set high), then dlcGioGet + dlcGioGet, // return DDR in bit 2, PORT in bit 1, and PIN in bit 0 + }; + + uint8_t I2cWrite(uint8_t regn, const char* data, uint8_t size); + uint8_t I2cRead(uint8_t regn, char* data, uint8_t size); + void I2cSCL(bool v); + void I2cSDA(bool v); + bool I2cSDA(); + void I2cInit(); + void I2cStart(); + void I2CRestart(); + void I2cStop(); + void I2cAck(); + void I2cNak(); + bool I2cSend(uint8_t data); + uint8_t I2cRecv(); + void I2cReset(); + void I2cDebug(); + + int fd = -1; + uint32_t gio; + uint32_t recv_skip; + uint8_t recv_buff[254]; // RpcPayloadMax+1 + uint8_t recv_size; + + // gio outputs: TXD, DTR, RTS, inputs: RXD, RI, DSR, CD, CTS + uint32_t I2cAddr; // i2c address + uint32_t I2cSCLo; // SCL output + uint32_t I2cSDAo; // SDA output + uint32_t I2cSDAi; // SDA input + +}; + +#endif diff --git a/utility/usbmon/hostusb.cpp b/utility/usbmon/hostusb.cpp index b96354e..91cd42b 100644 --- a/utility/usbmon/hostusb.cpp +++ b/utility/usbmon/hostusb.cpp @@ -2,175 +2,141 @@ #include "rpc.hpp" #include "hostusb.hpp" -#if __GNUC__ > 4 -#include -#endif -#include #include -#include #include #include // apt-get install libusb-1.0-0-dev -#include #include -#include +#include #include static struct libusb_context* lu_ctx = nullptr; -RunInit(libusb) { if (libusb_init(&lu_ctx) < 0) SysAbort(__PRETTY_FUNCTION__); } -RunExit(libusb) { libusb_exit(lu_ctx); } +RunExit(libusb) { if (lu_ctx) libusb_exit(lu_ctx); } -LuXact::LuXact(uint32_t vidpid, const char16_t vendor[], const char16_t product[], const char16_t serial[]) - : id_int(vidpid) +LuXactUsb::LuXactUsb(uint32_t _vidpid, const char* vendor, const char* product, const char* serial) + : vidpid(_vidpid) { - size_t vl = vendor ? std::char_traits::length(vendor) : 0; - size_t pl = product ? std::char_traits::length(product) : 0; - size_t sl = serial ? std::char_traits::length(serial) : 0; - - id_string = new char16_t[vl + 1 + pl + 1 + sl + 1]; - char16_t blank[1] = { 0 }; - std::char_traits::copy(id_string, vl ? vendor : blank, vl + 1); - std::char_traits::copy(id_string + vl + 1, pl ? product : blank, pl + 1); - std::char_traits::copy(id_string + vl + 1 + pl + 1, sl ? serial : blank, sl + 1); - - if (opt_debug) - libusb_set_debug(lu_ctx, LIBUSB_LOG_LEVEL_INFO); - Label(); + if (!lu_ctx) { + if (libusb_init(&lu_ctx) < 0) + SysAbort(__PRETTY_FUNCTION__); + } + + label.AddSegment(sgNumber - 1); + storeDescriptors(vendor, product, serial); + + if (opt_debug) + libusb_set_debug(lu_ctx, LIBUSB_LOG_LEVEL_INFO); + Label(); } -LuXact::~LuXact() +LuXactUsb::~LuXactUsb() { - Close(); - delete id_string; + Close(); } -void LuXact::Label() +bool LuXactUsb::Open() { - // only output label to tty - if (!isatty(fileno(stdout)) || !isatty(fileno(stderr))) return; - - std::cerr << "\e]0;"; - if (!lu_dev) std::cerr << '['; -#if __GNUC__ > 4 - // convert utf16 to utf8 - std::wstring_convert,char16_t> convert; -#else - // this only handles ASCII - struct { std::string (*to_bytes)(const char16_t* p); } convert; - convert.to_bytes = [&](const char16_t* p)->std::string { - std::string r; - for (; *p; ++p) - r += (char)(*p & 0xFF); - return r; }; -#endif - char16_t* p = id_string; - std::cerr << convert.to_bytes(p); - p += std::char_traits::length(p) + 1; - std::cerr << ' ' << convert.to_bytes(p); - p += std::char_traits::length(p) + 1; - if (p[0]) - { - std::cerr << ' '; - if (p[1]) - std::cerr << convert.to_bytes(p); - else { - std::ios::fmtflags fmtfl = std::cerr.setf(std::ios::hex, std::ios::basefield); - std::cerr << "0x" << (uint16_t)*p; - std::cerr.setf(fmtfl, std::ios::basefield); - } - } - if (id_more) - std::cerr << ' ' << id_more; - if (!lu_dev) std::cerr << ']'; - std::cerr << '\a'; + libusb_device* *devs; + ssize_t cnt = libusb_get_device_list(lu_ctx, &devs); + if (cnt < 0) SysAbort(__PRETTY_FUNCTION__); + for (ssize_t i = 0; i < cnt; i++) { + libusb_device* dev = devs[i]; + + libusb_device_descriptor desc; + if (libusb_get_device_descriptor(dev, &desc) < 0) + SysAbort(__PRETTY_FUNCTION__); + + // require vidpid to match + if (!matchVidpid(((uint32_t)desc.idVendor << 16) | desc.idProduct)) + continue; + + // open with automatic handle + struct Handle { + ~Handle() { if (handle) libusb_close(handle); } + operator libusb_device_handle*() { return handle; } + void release() { handle = nullptr; } + libusb_device_handle* handle; + } handle; + if (libusb_open(dev, &handle.handle) != 0) continue; + + // read Manufacturer Product SerialNumber + const size_t desc_size = 256; + auto desc_get = [&](char16_t* p, uint8_t index)->bool { + int r = libusb_get_string_descriptor(handle, index, 0x0409, (unsigned char*)p, desc_size); + if (r < 0 || r >= desc_size || (r & 1) || *p != (0x300 | r)) + return false; + std::char_traits::move(p, p + 1, r - sizeof(char16_t)); + p[r / sizeof(char16_t) - 1] = '\0'; + return true; + }; + char16_t vendor[desc_size]; + char16_t product[desc_size]; + char16_t serial[desc_size]; + // require no errors + if (!desc_get(vendor, desc.iManufacturer) || !desc_get(product, desc.iProduct) + || !desc_get(serial, desc.iSerialNumber)) + continue; + + // require a match + if (!matchVidpid(vendor, product) || !matchSerial(serial)) + continue; + + if (claim) { + // claim interface must succeed in order to own device + if (libusb_kernel_driver_active(handle, 0) == 1 + && libusb_detach_kernel_driver(handle, 0) != 0 + || libusb_claim_interface(handle, 0) != 0) + continue; + } + + // store the strings read in case there were wild cards + storeDescriptors(vendor, product, serial); + + lu_dev = handle; + handle.release(); + break; + } + libusb_free_device_list(devs, 1); + + if (!lu_dev) return false; + Label(); + return true; } -bool LuXact::Open() -{ - libusb_device* *devs; - ssize_t cnt = libusb_get_device_list(lu_ctx, &devs); - if (cnt < 0) SysAbort(__PRETTY_FUNCTION__); - - for (ssize_t i = 0; i < cnt; i++) - { - libusb_device* dev = devs[i]; - - libusb_device_descriptor desc; - if (libusb_get_device_descriptor(dev, &desc) < 0) - SysAbort(__PRETTY_FUNCTION__); - - // require vidpid to match - if ((id_int >> 16) != desc.idVendor || (id_int & 0xFFFF) != desc.idProduct) - continue; - - // open with automatic handle - struct _handle { ~_handle() { if (h) libusb_close(h); } - operator libusb_device_handle*() { return h; } - libusb_device_handle* h = nullptr; } handle; - if (libusb_open(dev, &handle.h) != 0) continue; - - const size_t buffer_size = 256; - std::unique_ptr buffer(new char16_t[3 * buffer_size]); - char16_t* p = buffer.get(); -{ - // read Manufacturer Product SerialNumber - auto desc_get = [&](uint8_t index)->bool { - int r = libusb_get_string_descriptor(handle, index, 0x0409, (unsigned char*)p, buffer_size); - if (r < 0 || r >= buffer_size || (r & 1) || *p != (0x300 | r)) - return false; - std::char_traits::move(p, p + 1, r - sizeof(char16_t)); - p[r / sizeof(char16_t) - 1] = '\0'; - p += r / sizeof(char16_t); - return true; - }; - // require no errors - if (!desc_get(desc.iManufacturer) || !desc_get(desc.iProduct) || !desc_get(desc.iSerialNumber)) - continue; +bool LuXactUsb::matchVidpid(uint32_t _vidpid) { + return vidpid == _vidpid; } -{ - // check for a match where wild cards have l == 0 - char16_t* s = id_string; p = buffer.get(); - auto desc_eq = [&]()->bool { - size_t ls = std::char_traits::length(s); - size_t lp = std::char_traits::length(p); - bool r = ls == 0 || ls == lp && std::char_traits::compare(s, p, lp) == 0; - s += ls + 1; p += lp + 1; - return r; - }; - // require a match - if (!desc_eq() || !desc_eq() || !desc_eq()) - continue; - - // don't copy on exact match - if (s == p) - p = nullptr; + +bool LuXactUsb::matchVidpid(const char16_t* vendor, const char16_t* product) { + // wild cards have l == 0 + std::string sVendor{label.Segment(sgVendor)}; + std::string sProduct{label.Segment(sgProduct)}; + return (sVendor.length() == 0 || sVendor == to_bytes(vendor)) + && (sProduct.length() == 0 || sProduct == to_bytes(product)); } -if (claim) { - // claim interface must succeed in order to own device - if (libusb_kernel_driver_active(handle, 0) == 1 - && libusb_detach_kernel_driver(handle, 0) != 0 - || libusb_claim_interface(handle, 0) != 0) - continue; + +bool LuXactUsb::matchSerial(const char16_t* serial) { + std::string sSerial{label.Segment(sgSerial)}; + return sSerial.length() == 0 || sSerial == to_bytes(serial); } - // if there were wild cards, store the string read - if (p) - { - id_string = (char16_t*)realloc(id_string, (p - buffer.get()) * sizeof(char16_t)); - if (!id_string) SysAbort(__PRETTY_FUNCTION__); - std::char_traits::copy(id_string, buffer.get(), p - buffer.get()); - } - - lu_dev = handle; - handle.h = nullptr; - break; - } - libusb_free_device_list(devs, 1); - if (!lu_dev) return false; - Label(); - return true; +void LuXactUsb::storeDescriptors(const char16_t* vendor, const char16_t* product, const char16_t* serial) { + storeDescriptors(to_bytes(vendor).c_str(), to_bytes(product).c_str(), to_bytes(serial).c_str()); +} + +void LuXactUsb::storeDescriptors(const char* vendor, const char* product, const char* serial) { + if (label.Segment(sgVendor).length() == 0) + label.Segment(sgVendor, vendor ? vendor : ""); + if (label.Segment(sgProduct).length() == 0) + label.Segment(sgProduct, product ? product : ""); + if (label.Segment(sgSerial).length() == 0) + label.Segment(sgSerial, serial ? serial : ""); +} + +bool LuXactUsb::IsOpen() const { + return lu_dev != nullptr; } -bool LuXact::Close() +bool LuXactUsb::Close() { if (!lu_dev) return false; bool ret = true; @@ -184,7 +150,7 @@ bool LuXact::Close() return ret; } -int LuXact::Xfer(uint8_t req, uint32_t arg, char* data, uint8_t size, bool device_to_host) { +int LuXactUsb::Xfer(uint8_t req, uint32_t arg, char* data, uint8_t size, bool device_to_host) { int ret = libusb_control_transfer (/* libusb_device_handle* */lu_dev, /* bmRequestType */ LIBUSB_REQUEST_TYPE_CLASS @@ -209,7 +175,7 @@ int LuXact::Xfer(uint8_t req, uint32_t arg, char* data, uint8_t size, bool devic return ret; } -uint8_t LuXact::XferRetry(uint8_t req, uint32_t arg, char* data, uint8_t size, bool device_to_host) +uint8_t LuXactUsb::XferRetry(uint8_t req, uint32_t arg, char* data, uint8_t size, bool device_to_host) { for (;;) { if (!lu_dev) { @@ -224,32 +190,39 @@ uint8_t LuXact::XferRetry(uint8_t req, uint32_t arg, char* data, uint8_t size, b } } -LuXactUsb::LuXactUsb(const char* serialnumber) - : LuXact(0x16c005df, nullptr, nullptr, Serial(serialnumber)) { +LuXactUsbRpc::LuXactUsbRpc(const char* serialnumber) + : LuXactUsb(0x16c005df, nullptr, "RpcUsb", serialnumber) { } -char16_t* LuXactUsb::Serial(const char* serialnumber) { - serial[0] = serialnumber ? strtol(serialnumber, nullptr, 0) : 0; - serial[1] = 0; - return serial; +bool LuXactUsbRpc::matchSerial(const char16_t* serial) { + std::string sSerial{label.Segment(sgSerial)}; + if (sSerial.length() == 0) return true; + if (!serial || std::char_traits::length(serial) != 1) + return LuXactUsb::matchSerial(serial); + return (char16_t)strtol(sSerial.c_str(), nullptr, 0) == serial[0]; } -void LuXactUsb::Reset() -{ - if (!lu_dev) +void LuXactUsbRpc::storeDescriptors(const char16_t* vendor, const char16_t* product, const char16_t* serial) { + if (!serial || std::char_traits::length(serial) != 1) { + LuXactUsb::storeDescriptors(vendor, product, serial); return; - Xfer(0xFF, 0); - Close(); + } + // this will only set the strings if they are uninitialized + LuXactUsb::storeDescriptors(vendor, product, nullptr); + + std::ostringstream sn; + sn << (uint16_t)*serial << "x" << std::hex << (uint16_t)*serial; + LuXactUsb::storeDescriptors(nullptr, nullptr, sn.str().c_str()); } -void LuXactUsb::Send(char data) +void LuXactUsbRpc::Send(char data) { try { XferRetry(0, (uint8_t)data); } catch(...) { } } -void LuXactUsb::Send(struct Rpc& rpc, uint8_t index) { +void LuXactUsbRpc::Send(struct Rpc& rpc, uint8_t index) { uint8_t size = rpc.Size(index); uint32_t arg; memcpy(&arg, (char*)&rpc, sizeof(arg) < size ? sizeof(arg) : size); size = sizeof(arg) < size ? size - sizeof(arg) : 0; @@ -258,14 +231,14 @@ void LuXactUsb::Send(struct Rpc& rpc, uint8_t index) { throw Exception(__PRETTY_FUNCTION__); } -void LuXactUsb::Recv() { +void LuXactUsbRpc::Recv() { for (;;) { char data[RpcPayloadMax+1]; uint8_t xfer = XferRetry(1, 0, data, sizeof(data), true); if (xfer == 0) break; if (xfer == 1 || data[xfer-1] == 0) { - data[1] = '\0'; + if (xfer == 1) data[1] = '\0'; if (sink) sink(data); return; @@ -276,41 +249,32 @@ void LuXactUsb::Recv() { } } -LuXactLw::LuXactLw(const char* serialnumber) - : LuXact(0x17810c9f, nullptr, nullptr, Serial(serialnumber)) { - id[0] = '\0'; - id_more = id; +bool LuXactUsbRpc::Reset() +{ + if (!lu_dev) + return false; + Xfer(0xFF, 0); + Close(); + return true; } -char16_t* LuXactLw::Serial(const char* serialnumber) { - serial[0] = 0; - if (!serialnumber) return nullptr; - int sn = strtol(serialnumber, nullptr, 0); - if (sn < 0 || sn > 999) sn = 0; - std::string str = std::to_string(sn); - size_t len = str.length(); - if (len < 3) str.insert(0, 3 - len, '0'); - const char* p = str.c_str(); - serial[0] = *p++; - serial[1] = *p++; - serial[2] = *p++; - serial[3] = 0; - return serial; +LuXactLw::LuXactLw(const char* serialnumber) + : LuXactUsb(0x17810c9f, nullptr, nullptr, nullptr) { + + if (serialnumber && *serialnumber) { + int sn = strtol(serialnumber, nullptr, 0); + if (sn < 0 || sn > 999) sn = 0; + std::string str = std::to_string(sn); + size_t len = str.length(); + if (len < 3) str.insert(0, 3 - len, '0'); + label.Segment(sgSerial, str); + } + label.AddSegment(sgNumber - LuXactUsb::sgNumber); } bool LuXactLw::Open() { listening = false; - - if (!LuXact::Open()) - return false; - - // Cancel any dw commands - if (Xfer(60/*dw*/, 0) < 0) - return false; - return true; -} - -void LuXactLw::Reset() { + return LuXactUsb::Open(); } void LuXactLw::Send(char data) { @@ -330,8 +294,8 @@ void LuXactLw::Recv() { // libusb calls taken from dwire/DigiSpark.c // Read back dWIRE bytes - char data[129]; - int status = Xfer(60/*dw*/, 0, data, sizeof(data)-1, true); + char data[RpcPayloadMax+1]; + int status = Xfer(60/*dw*/, 0, data, sizeof(data), true); // 0 means no data, PIPE means garbled command //static int last = 50000; if (status || last != status) std::cerr << "Recv status:" << status << '\n'; last = status; if (status == 0 || status == LIBUSB_ERROR_TIMEOUT || status == LIBUSB_ERROR_PIPE) @@ -344,16 +308,18 @@ void LuXactLw::Recv() { #endif return; } - if (status > 0 && sink) { - data[status] = '\0'; -#if 0 - std::cerr << '['; - std::ios::fmtflags fmtfl = std::cerr.setf(std::ios::hex, std::ios::basefield); - for (int i = 0 ; i < status; ++i) std::cerr << ((int)data[i]&0xFF) << ' '; - std::cerr.setf(fmtfl, std::ios::basefield); - std::cerr << ']'; -#endif - sink(data); + if (status > 0) { + uint8_t xfer = status; + if (xfer == 1 || data[xfer-1] == 0) { + if (xfer == 1) data[1] = '\0'; + if (sink) + sink(data); + } + else { + if (data[xfer-1] >= RpcNumber) + throw Exception(__PRETTY_FUNCTION__); + ((Rpc*)data)->VtAct(data[xfer-1]); + } } } @@ -362,6 +328,9 @@ void LuXactLw::Recv() { listening = true; } +bool LuXactLw::Reset() { +} + bool LuXactLw::ResetDw() { // libusb calls taken from dwire/DigiSpark.c @@ -399,15 +368,18 @@ bool LuXactLw::ResetDw() { sum = 6 * sum + 8 * n; // Display the baud rate - strcpy(id, (std::to_string(16500000 * n / sum)+"Bd").c_str()); + label.Segment(sgBaudrate, std::to_string(16500000 * n / sum) + " Bd"); +#if 0 // done on the little wire // Determine timing loop iteration counts for sending and receiving bytes, // use rounding in the division. uint16_t dwBitTime = (sum - 8 * n + (4 * n) / 2) / (4 * n); + std::cerr << "dwBitTime: " << dwBitTime << '\n'; // Set timing parameter if (Xfer(60/*dw*/, 0x02, (char*)&dwBitTime, sizeof(dwBitTime), false) < 0) return false; +#endif if (status == 20) { // Disable Dw if it was enabled @@ -447,3 +419,16 @@ GND----zz--o o---->V+ V+ on at boot when PB0 tristated. V+ off when PB0 set high to turn off Q. */ + +bool LuXactLw::SetSerial(const char* _serial) { + uint16_t serial = strtol(_serial, nullptr, 0); + if (serial > 999) serial = 0; + if (Xfer(55/*Change serial number*/, + (uint32_t)('0' + serial%10) + |((uint32_t)('0' + serial/10%10) << 8) + |((uint32_t)('0' + serial/100%10) << 16)) < 0) { + std::cerr << "error changing serial number" << std::endl; + return false; + } + return true; +} diff --git a/utility/usbmon/hostusb.hpp b/utility/usbmon/hostusb.hpp index 865c320..f198752 100644 --- a/utility/usbmon/hostusb.hpp +++ b/utility/usbmon/hostusb.hpp @@ -1,75 +1,83 @@ #ifndef hostusb_hpp #define hostusb_hpp -#include +#include "hostio.hpp" -class LuXact { +class LuXactUsb : public LuXact { public: const uint32_t timeout = 10; - class Exception : public std::runtime_error { public: Exception(const char* what_arg) : std::runtime_error(what_arg) { } }; - - LuXact(uint32_t vidpid, const char16_t vendor[], const char16_t product[], const char16_t serial[]); - ~LuXact(); - void Label(); - inline bool IsOpen() const { return lu_dev != nullptr; }; + LuXactUsb(uint32_t vidpid, const char* vendor, const char* product, const char* serial); + ~LuXactUsb(); - virtual bool Open(); + bool Open(); + bool IsOpen() const; bool Close(); - inline void Sink(int (*_sink)(const char*)) { sink = _sink; } int Xfer(uint8_t req, uint32_t arg, char* data = nullptr, uint8_t size = 0, bool device_to_host = false); uint8_t XferRetry(uint8_t req, uint32_t arg, char* data = nullptr, uint8_t size = 0, bool device_to_host = false); - virtual void Reset() = 0; - virtual void Send(char data) = 0; - virtual void Send(struct Rpc& rpc, uint8_t index) = 0; - virtual void Recv() = 0; - protected: - uint32_t id_int; - char16_t* id_string; - char* id_more = nullptr; + // check if we match vidpid, descriptors + virtual bool matchVidpid(uint32_t vidpid); + virtual bool matchVidpid(const char16_t* vendor, const char16_t* product); + virtual bool matchSerial(const char16_t* serial); + virtual void storeDescriptors(const char16_t* vendor, const char16_t* product, const char16_t* serial); + virtual void storeDescriptors(const char* vendor, const char* product, const char* serial); + + // label segments that serve as device id + enum { + sgVendor, + sgProduct, + sgSerial, + sgNumber + }; + + uint32_t vidpid = 0; bool claim = true; struct libusb_device_handle* lu_dev = nullptr; - int (*sink)(const char*) = nullptr; }; -class LuXactUsb : public LuXact { +class LuXactUsbRpc : public LuXactUsb { public: - LuXactUsb(const char* serialnumber); + LuXactUsbRpc(const char* serialnumber); char16_t* Serial(const char* serialnumber); - void Reset(); void Send(char data); void Send(struct Rpc& rpc, uint8_t index); void Recv(); + bool Reset(); + protected: - char16_t serial[2]; + bool matchSerial(const char16_t* serial); + void storeDescriptors(const char16_t* vendor, const char16_t* product, const char16_t* serial); }; -class LuXactLw : public LuXact { +class LuXactLw : public LuXactUsb { public: LuXactLw(const char* serialnumber); - char16_t* Serial(const char* serialnumber); bool Open(); - void Reset(); void Send(char data); void Send(struct Rpc& rpc, uint8_t index); void Recv(); + bool Reset(); bool ResetDw(); bool ResetPower(); + bool SetSerial(const char* serial); + protected: - char16_t serial[4]; - char id[20]; + enum { + sgBaudrate = LuXactUsb::sgNumber, + sgNumber + }; bool listening; }; diff --git a/utility/usbmon/rpc.cpp b/utility/usbmon/rpc.cpp new file mode 100644 index 0000000..af91284 --- /dev/null +++ b/utility/usbmon/rpc.cpp @@ -0,0 +1,29 @@ +#ifdef CORE_RPC_HPP + +#include "command.hpp" +#include "rpc.hpp" + +#include + +void RpcConsole::Act() { + if (!radix) { + SysConWrite(text); + return; + } + + std::ostringstream out; + + if ((radix & 0x7f) == 16) + out << std::hex; + else if ((radix & 0x7f) == 8) + out << std::oct; + + if (radix & 0x80) + out << (int32_t)value; + else + out << value; + + SysConWrite(out.str().c_str()); +} + +#endif diff --git a/utility/usbmon/targexec.cpp b/utility/usbmon/targexec.cpp new file mode 100644 index 0000000..625e5da --- /dev/null +++ b/utility/usbmon/targexec.cpp @@ -0,0 +1,92 @@ +#include "command.hpp" +#include "targexec.hpp" + +#include +#include +#include + +TargExec::TargExec(const char* file) { + // hex file line format :nnaaaattdd+cc + // : only non-hex character + // nn number of data bytes + // aaaa address + // tt 00 data, 01 eof + // dd+ nn data bytes + // cc checksum + struct format{}; + try { + std::ifstream fs(file); + if (!fs.is_open()) + throw format{}; + for (;;) { + char line[528]; + fs.getline(line, sizeof(line)); + if (!fs.good() || line[0] != ':') + throw format{}; + uint16_t l = strlen(line); + if (line[l-1] == '\r') + line[--l] == '\0'; + + // convert 1, 2 or 4 digits, advance p, + // and calculate checksum + uint8_t checksum = 0; + auto cnv1 = [&](char*& p)->uint8_t { + uint8_t ret = *p >= '0' && *p <= '9' ? *p - '0' + : *p >= 'A' && *p <= 'Z' ? *p - 'A' + 10 + : *p - 'a' + 10; + ++p; return ret; + }; + auto cnv2 = [&](char*& p)->uint8_t { + uint8_t ret = cnv1(p) << 4; + ret |= cnv1(p); + checksum += ret; + return ret; + }; + auto cnv4 = [&](char*& p)->uint16_t { + uint16_t ret = (uint16_t)cnv2(p) << 8; + ret |= cnv2(p); + return ret; + }; + + char* p = line + 1; + uint8_t n = cnv2(p); + if (l != (uint16_t)n * 2 + 11) + throw format{}; + uint16_t a = cnv4(p); + uint8_t t = cnv2(p); + if (t == 1) { + cnv2(p); + if (checksum) + throw format{}; + break; + } + if (t != 0) + throw format{}; + if (data.blob && data.load_address + data.load_length != a) + // not contiguous + throw format{}; + if (!data.blob || data.load_length + n > data.buffer_size) { + if (!data.blob) { + // allocate initial buffer + data.load_address = a; + data.load_length = 0; + data.blob = (uint8_t*)malloc(data.buffer_size = 8192); + } + else + // grow by factor of 2 + data.blob = (uint8_t*)realloc(data.blob, data.buffer_size *= 2); + if (!data.blob) + SysAbort("memory"); + } + for (uint8_t i = 0; i < n; ++i) + data.blob[data.load_length++] = cnv2(p); + cnv2(p); + if (checksum) + throw format{}; + } + } catch (format) { + if (data.blob) + free(data.blob); + data.blob = nullptr; + } +} diff --git a/utility/usbmon/targexec.hpp b/utility/usbmon/targexec.hpp new file mode 100644 index 0000000..5b67124 --- /dev/null +++ b/utility/usbmon/targexec.hpp @@ -0,0 +1,18 @@ +#ifndef targexec_hpp +#define targexec_hpp + +#include + +class TargExec { +public: + TargExec(const char* file); + + struct Data { + uint8_t* blob = nullptr; + uint16_t load_address; + uint16_t load_length; + uint16_t buffer_size; + } data; +}; + +#endif diff --git a/utility/usbmon/usbmon.cpp b/utility/usbmon/usbmon.cpp index a341130..647d92d 100644 --- a/utility/usbmon/usbmon.cpp +++ b/utility/usbmon/usbmon.cpp @@ -1,5 +1,7 @@ #include "command.hpp" +#include "hostser.hpp" #include "hostusb.hpp" +#include "targexec.hpp" #include "usbmon.hpp" #include @@ -7,80 +9,202 @@ #include #include -void reset() { - LuXactUsb luXactUsb(serialnumber); - if (!luXactUsb.Open()) { - std::cerr << "error opening device" << std::endl; - return; +void monitor() { + switch (tolower(*opt_device)) { + case 'u': { + LuXactUsbRpc luXact(opt_device + 1); + luXact.Open(); + + if (opt_power) + luXact.ResetPower(); + if (opt_reset) + luXact.Reset(); + if (opt_set_serial) + luXact.SetSerial(opt_set_serial); + if (opt_nomon) + break; + + luXact.Sink(SysConWrite); + for (;;) { + luXact.Recv(); + if (SysConAvail()) { + int c = SysConRead(); + if (c <= 0) continue; + luXact.Send(c); + continue; + } + usleep(100 * 1000); + } + break; } - luXactUsb.Reset(); -} + case 't': { + LuXactLw luXact(opt_device + 1); + if (!luXact.Open()) { + std::cerr << "error opening device" << std::endl; + break; + } -void power() { - LuXactLw luXactLw(serialnumber); - if (!luXactLw.Open()) { - std::cerr << "error opening device" << std::endl; - return; + if (opt_power) + luXact.ResetPower(); + if (opt_reset) + luXact.Reset(); + if (opt_set_serial) + luXact.SetSerial(opt_set_serial); + if (opt_nomon) + break; + + if (!luXact.ResetDw()) { + std::cerr << "error connecting to device" << std::endl; + break; + } + + luXact.Sink(SysConWrite); + for (;;) { + luXact.Recv(); + if (SysConAvail()) { + int c = SysConRead(); + if (c <= 0) continue; + luXact.Send(c); + continue; + } + usleep(100 * 1000); + } + break; } - luXactLw.ResetPower(); -} + case 's': { + LuXactSer luXact(opt_device + 1); + if (!luXact.Open()) { + std::cerr << "error opening device" << std::endl; + break; + } -void monitor() { - LuXactUsb luXact(serialnumber); - luXact.Open(); - - luXact.Sink(SysConWrite); - for (;;) { - luXact.Recv(); - if (SysConAvail()) { - int c = SysConRead(); - if (c <= 0) continue; - luXact.Send(c); - continue; + if (opt_power) + luXact.ResetPower(); + if (opt_reset) + luXact.Reset(); + if (opt_set_serial) + luXact.SetSerial(opt_set_serial); + if (opt_nomon) + break; + + if (!luXact.ResetDw()) { + std::cerr << "error connecting to device" << std::endl; + break; + } + + luXact.Sink(SysConWrite); + for (;;) { + luXact.Recv(); + if (SysConAvail()) { + int c = SysConRead(); + if (c <= 0) continue; + luXact.Send(c); + continue; + } + usleep(100 * 1000); } - usleep(100 * 1000); + break; + } + } } -void monitor_dw() { - LuXactLw luXact(serialnumber); - if (!luXact.Open() || !luXact.ResetDw()) { - std::cerr << "error opening device" << std::endl; +void load(const char* file) { + + TargExec targExec(file); + if (!targExec.data.blob) { + std::cerr << "no data" << std::endl; return; } - - luXact.Sink(SysConWrite); - for (;;) { - luXact.Recv(); - if (SysConAvail()) { - int c = SysConRead(); - if (c <= 0) continue; - luXact.Send(c); - continue; - } - usleep(100 * 1000); + for (uint8_t* p = targExec.data.blob; p < targExec.data.blob + targExec.data.load_length; ) { + printf("%02X", *p++); + if (!((p - targExec.data.blob) & 0xF)) + printf("\n"); } } -void change_serialnumber(const char* new_serialnumber) { - LuXactLw luXactLw(serialnumber); - if (!luXactLw.Open()) { - std::cerr << "error opening device" << std::endl; - return; + class Label : public std::string { + + public: + + // don't hide assignment operator + using std::string::operator=; + + // add blank segments to end + void AddSegment(size_t n); + + // return string for display + std::string Display(); + + // return, set segment + std::string Segment(size_t n); + void Segment(size_t n, std::string v); + + // return position, length of segment + size_t SegmentPos(size_t n); + size_t SegmentLen(size_t pos); + + } label; + +void Label::AddSegment(size_t n) { + for (size_t i = 0; i < n; ++i) + push_back(0); +} + +std::string Label::Display() { + std::string ret{*this}; + for (size_t pos = 0; (pos = ret.find_first_of((char)0, pos)) != std::string::npos;) { + char space = ' '; + ret.replace(pos++, 1, &space, 1); } + return ret; +} + +std::string Label::Segment(size_t n) { + size_t pos = SegmentPos(n); + // std::cerr << "pos:" << pos << '\n'; + if (pos == std::string::npos) + return std::string{}; + size_t len = SegmentLen(pos); + //std::cerr << "len:" << len << '\n'; + return substr(pos, len); +} - char16_t* serial = luXactLw.Serial(new_serialnumber); - if (luXactLw.Xfer - (55/*Change serial number*/, - ((uint32_t)serial[0]&0xFF) - |(((uint32_t)serial[1]&0xFF)<<8) - |(((uint32_t)serial[2]&0xFF)<<16)) < 0) { - std::cerr << "error changing serial number" << std::endl; +void Label::Segment(size_t n, std::string v) { + size_t pos = SegmentPos(n); + if (pos == std::string::npos) return; + size_t len = SegmentLen(pos); + replace(pos, len, v); +} + +size_t Label::SegmentPos(size_t n) { + size_t pos = 0; + for (size_t i = 0; i < n; ++i) { + pos = find_first_of((char)0, pos); + if (pos == std::string::npos) + return pos; + ++pos; } + return pos; +} + +size_t Label::SegmentLen(size_t pos) { + size_t len = find_first_of((char)0, pos); + return len == std::string::npos ? len : len -= pos; } void test() { + + LuXactSer luXact(""); + if (!luXact.Open()) { + std::cerr << "error opening device" << std::endl; + exit(0); + } + + luXact.Debug(); + + exit(0); } diff --git a/utility/usbmon/usbmon.hpp b/utility/usbmon/usbmon.hpp index 41db767..114b5d1 100644 --- a/utility/usbmon/usbmon.hpp +++ b/utility/usbmon/usbmon.hpp @@ -1,11 +1,8 @@ #ifndef usbmon_hpp #define usbmon_hpp -void reset(); -void power(); void monitor(); -void monitor_dw(); -void change_serialnumber(const char* new_serialnumber); +void load(const char* file); void test(); #endif