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..8c8974e --- /dev/null +++ b/example/echodw/echodw.cpp @@ -0,0 +1,27 @@ + +#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; + + // 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..fed9272 --- /dev/null +++ b/example/echodw/uartsoft.cpp @@ -0,0 +1,465 @@ +#include "uartsoft.hpp" +#include +#include +#include + +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() { + // 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, 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 4*idel +delay%=: + nop ;1 + dec %[idel] ;1 + brne delay%= ;? 4*idel end + nop ;1 + 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_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; + __asm__ __volatile__ (R"assembly( + + ; divisor >= 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__ + + ; 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 + +all_done%=: + ret +)assembly" + : [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) + ); +} + +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; + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + UartSoft_break(break_bits); + while (n--) + UartSoft_write(pgm_read_byte(s++)); + } + return r; +} +#endif + +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)() { + + // working registers declared as early clobbers + register uint8_t delay asm("r25"); + 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"); + __asm__ __volatile__ (R"assembly( + ; 00 + 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 r29 ;2 13 + push r28 ;2 15 + push r21 ;2 17 + + ; 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] + rcall UartSoft_break_detected + rjmp done%= + +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 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 + lsl %[idel] ;1 09 *2 + adc %[ibit],%[ibit] ;1 10 +loopstart%=: ; 6*idel timeout + sbis %[pin],%[bit] ;? + rjmp havestart%= ;2 + sbiw %[idel],1 ;2 + brne loopstart%= ;? 6*idel end + rjmp haveall%= ;2 timeout for start bit +havestart%=: + mov %[idel],%[delay] ;1 0.5 bits + lsr %[idel] ;1 +delays%=: ; wait for 1st bit + dec %[idel] ;1 + 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%=: + dec %[idel] ;1 00 + nop ;1 01 + brne delay%= ;? 4*delay + + lsr %[recv] ;1 01 sample + sbic %[pin],%[bit] ;? + ori %[recv],0x80 ;1 03 set msb + rjmp . ;2 05 nop + dec %[ibit] ;1 06 + brne loop%= ;? 08 more bits? + + mov %[idel],%[delay] ;1 1 bit +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 +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 + 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 + 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) + ,[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)) + : + ); +} + +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..786185c --- /dev/null +++ b/example/echodw/uartsoft.hpp @@ -0,0 +1,65 @@ +#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(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(UARTSOFT_ARGS) +#error DW ports +#endif // defined(UARTSOFT_ARGS) + +extern class UartSoft : public RpcSend { +public: + + 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); +#endif + 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(); + + void send(Rpc& rpc, uint8_t index); + + // 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 + // 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/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); + } +} 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 +} 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 527ff2f..d896f0c 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 @@ -995,7 +996,14 @@ uchar usbFunctionSetup(uchar data[8]) if (req == 60) { // debugWIRE transfer - 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) { @@ -1006,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) { @@ -1296,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,5 ; End break \n" - " cbi 0x17,5 ; Set DDRB 5 (reset/dwire) direction input \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" @@ -1309,7 +1318,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 +1343,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" @@ -1346,10 +1355,70 @@ 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" - :::"r24","r26","r27","r30","r31"); + ::[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 @@ -1384,15 +1453,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" - " sbi 0x18,5 ; Make sure line is idle (high) \n" - " sbi 0x17,5 ; DDRB pin5 is output \n" + " cbi 0x17,%[bit] ; DDRB pin5 is input \n" + " sbi 0x18,%[bit] ; Make sure line is idle (high) \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" @@ -1400,39 +1474,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,5 ; 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,5 ; 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" - " cbi 0x18,5 ; 1. Set dwire port low \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" @@ -1441,8 +1521,9 @@ 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" - " adiw r30,1 ; 2. Extra iteration \n" + " cbi 0x17,%[bit] ; 1. DDRB pin5 is input \n" + " sbi 0x18,%[bit] ; 1. Set dwire port high \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" @@ -1450,9 +1531,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: \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 +1571,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 +1587,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 +1607,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 +1626,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" @@ -1555,13 +1636,111 @@ 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" - :::"r21","r22","r23","r24","r25","r26","r27","r30","r31"); + ::[bit]"I"(DW_BIT):"r21","r22","r23","r24","r25","r26","r27","r30","r31"); } +/* +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. +*/ +__attribute__((naked, used)) void PCINT0_vect(void) { + __asm__ __volatile__ (R"string-literal( + ; 00 + sbis %[pcmsk],%[dwbit] ;? not for dw if dw interrupt disabled + rjmp DW_INTR_VECTOR ;2 03 leave interrupts disabled for usb + sbis %[pinb],%[dwbit] ;? break detected when dw line low + rjmp dw_break%= ;2 + ; 04 + sbis %[pcmsk],%[usbbit];? call usb interrupt when set + 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,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 +dw_done%=: + pop r21 + out __SREG__,r21 + pop r21 + reti +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)) + ,[dwbit] "I" (DW_BIT) + ,[usbbit] "I" (USB_INTR_CFG_BIT) + :); +} /* ------------------------------------------------------------------------- */ @@ -1905,6 +2084,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 @@ -1919,12 +2099,14 @@ 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) {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]=0; dwLen=1; cbi(DDRB,5); sbi(PCMSK,5); sei();} // Capture dWIRE pin change - if (dwState & 0x10) {dwReadBytes();} - if (dwState & 0x20) {dwCaptureWidths();} + 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) {dwLen=0;dwReadBytes();} + if (dwState & 0x20) {dwLen=0;dwCaptureWidths();} jobState = 0; dwState = 0; diff --git a/usbtiny/main.hex b/usbtiny/main.hex index 64e1f11..ed8bb58 100644 --- a/usbtiny/main.hex +++ b/usbtiny/main.hex @@ -1,204 +1,204 @@ -:1000000046C060C08CC05EC05DC05CC05BC05AC0F2 -:1000100059C058C057C056C055C054C053C001CB7A -:1000200032C97EC99CC9C1C9ECC9FFC916CA55CA23 -:1000300056CA5ECA75CAF3CAF2CAF1CAF0CAEFCA92 -:10004000A5CAEDCAECCAB2CA0902190001010080B2 +:1000000046C060C0E9C85EC05DC05CC05BC05AC08D +:1000100059C058C057C056C055C054C053C087CBF4 +:10002000A5C9F1C90FCA34CA5FCA72CA89CAC8CA87 +:10003000C9CAD1CAE8CA79CB78CB77CB76CB75CB96 +:1000400018CB73CB72CB25CB0902190001010080BC :10005000640904000001FF00000007058103080097 :1000600064120110010000000881179F0C040100B8 :100070000203011603550053004200740069006E2C :1000800000790053005000490004030904001124C2 :100090001FBECFE5D2E0DEBFCDBF10E0A0E6B0E0EE -:1000A000E0EEF6E102C005900D92A037B107D9F756 +:1000A000ECEEF7E102C005900D92A037B107D9F749 :1000B00022E0A0E7B0E001C01D92AD31B207E1F748 -:1000C00056D80CCB9DCFA82FB92F80E090E041E00F +:1000C000C9D892CB9DCFA82FB92F80E090E041E016 :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 +: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 +:1006300087D3C0937000DF91CF910895AC9A8BB7A8 +: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 +:1008B00043C2213011F486B30BC16A81223011F496 +:1008C00068BB39C2962F362F377041E050E0032EB7 +:1008D00002C0440F551F0A94E2F7233009F496C072 +:1008E000243021F488B3482B48BB25C2253051F46D +:1008F00060936400B99887B3856287BB88B3887DAD +:1009000088BB19C2263049F4B898C098B998C198E4 +:10091000BA98C298BD98C5980EC2273051F4BE014E +:10092000CE01029696DED0936C01C0936B0194E0E9 +:1009300003C2283031F4609350018B8180934F01C2 +:10094000FAC1EC81FD81F0934E01E0934D01293015 +:1009500011F480E203C02B3021F480EA80934A0135 +:10096000AAC1EA81FB81F0934C01E0934B012A304C +:1009700011F480E4F3CF2C3011F480ECEFCF2D3064 +:1009800021F487B34095482304C02E3021F487B367 +:10099000482B47BBD0C12F3009F5611105C08091AC +:1009A000350187B9A59A0CC0613031F480913501C9 +:1009B000816087B9A29A04C0623011F48FE887B9C8 +:1009C000369A3699FECF84B1888385B18983D093D6 +:1009D0006C01C0936B0114BA92E0AEC1203159F49E +:1009E000B89AB99A8AB5806A8ABD8AB583608ABD89 +:1009F00083B7856044C0213121F469BD8C8188BDF5 +:100A00009AC1223109F46ECF233121F488B3409585 +:100A100048236ACF243159F486B390E04823592300 +:100A200002C0559547953A95E2F7488352C0263162 +:100A3000A1F561110AC083B78B7F83BF83B78D7F18 +:100A400083BF83B7816083BF77C1613031F483B7DF +:100A50008B7F83BF83B782600FC0623031F483B76E +:100A60008B7F83BF83B7826014C0633051F483B738 +:100A7000846083BF83B78D7F83BF83B78E7F83BF3F +:100A80005AC1643009F057C183B7846083BF83B70C +:100A90008D7F83BF83B78160F2CF2F3159F48B8173 +:100AA00090E0982F8827860F911D90936300809384 +:100AB000620041C1203219F41ABC13BE3CC121327C +:100AC00021F46093140281E07FC0223241F483E17B +:100AD0008883D0936C01C0936B0191E02DC12332C8 +:100AE000C9F4379A96B18A81892B86B99B81913056 +:100AF00059F038F0923009F01EC1809135018069BB +:100B000006C01092350118C180913501806880932C +:100B1000350111C1283249F48BE891E090936C01C2 +:100B200080936B019091930107C1293211F482E007 +:100B30004BC02A3211F483E00BC02B3211F484E055 +:100B400043C02C3211F488E03FC02D3231F489E0EB +:100B50008093340160931402EEC02E3261F48BE076 +:100B600080933401609314028B81809315028C81F1 +:100B700080931602E0C02F3259F4613031F4B89AF4 +:100B8000B99ABA9A60933C01D6C010923C01D3C086 +:100B9000203349F4609338018B81809337018C8135 +:100BA00080933601C8C0213331F470E07093410165 +:100BB00060934001C0C0223311F485E005C02333A7 +:100BC00031F46093140286E080933401B4C026337C +:100BD00071F565FF1BC0E0917200E03CB8F48B81B9 +:100BE000AE2FB0E0AC58BF4F8C938C81A1E0AE0F1C +:100BF000B0E0AC58BF4F8C9383E08E0F80937200AF +:100C00008D81EE5FF0E0EC58FF4F80838A8184FF96 +:100C100092C080917200882309F48DC081E1809395 +:100C2000340187B3842B87BB4093730084C0273380 +:100C3000A1F480916D0090916E00C8D580916D00F7 +:100C400090916E006B810196C1D580916D0090915D +:100C50006E006C810296BAD56EC02C3371F580910E +:100C60003401882321F080918A01811164C0888138 +:100C700087FF09C084E991E090936C0180936B0138 +:100C80009091890159C0AA988A8180938A018E81A6 :100C90008093890190918901911104C084E180932E :100CA00034014AC080918901813818F080E880932E :100CB000890110921C029FEF3FC0822F807F803EEF @@ -218,151 +218,168 @@ :100D9000994097FF03C09195819591098E159F0504 :100DA00014F4C1B77C0181B78F5F81BFEACFC1BFA7 :100DB00061B780E090E0DF91CF911F910F91FF909C -:100DC000EF90DF907DC4CF93DF93D82F882311F06D +:100DC000EF90DF9003C5CF93DF93D82F882311F0E6 :100DD000B89801C0B89AC0E02C2F30E08091400153 -:100DE000909141012817390728F481E090E086DCD2 +:100DE000909141012817390728F481E090E080DCD8 :100DF000CF5FF2CFBA98C0E02C2F30E08091400155 -:100E0000909141012817390728F481E090E076DCC1 +:100E0000909141012817390728F481E090E070DCC7 :100E1000CF5FF2CFBA9AC0E02C2F30E08091400132 -:100E2000909141012817390728F481E090E066DCB1 +:100E2000909141012817390728F481E090E060DCB7 :100E3000CF5FF2CFD111B89AC0E08C2F90E0209113 :100E40004001309141018217930728F481E090E03E -:100E500055DCCF5FF2CFDF91CF9108950F931F93B1 +:100E50004FDCCF5FF2CFDF91CF9108950F931F93B7 :100E6000CF93DF93B898C0E08C2F90E020914001A1 -:100E7000309141018217930728F481E090E03EDC35 +:100E7000309141018217930728F481E090E038DC3B :100E8000CF5FF2CFBA98C0E00091400110914101CC -:100E90008C2F90E08017910728F481E090E02EDC01 +:100E90008C2F90E08017910728F481E090E028DC07 :100EA000CF5FF2CFD6B3C0E08C2F90E080179107D0 -:100EB00028F481E090E022DCCF5FF6CFBA9AC0E060 +:100EB00028F481E090E01CDCCF5FF6CFBA9AC0E066 :100EC0002C2F30E080914001909141012817390783 -:100ED00028F481E090E012DCCF5FF2CF8D2F81709B +:100ED00028F481E090E00CDCCF5FF2CF8D2F8170A1 :100EE000DF91CF911F910F910895CF9388B38A7F9F :100EF00088BBBA98B898C0E08C2F90E02091400150 -:100F0000309141018217930728F481E090E0F6DBED +:100F0000309141018217930728F481E090E0F0DBF3 :100F1000CF5FF2CFCF910895CF9387B38A7F87BBFE :100F2000C0E08C2F90E02091400130914101821768 -:100F3000930728F481E090E0E1DBCF5FF2CFB89A2D +:100F3000930728F481E090E0DBDBCF5FF2CFB89A33 :100F4000C0E08C2F90E02091400130914101821748 -:100F5000930728F481E090E0D1DBCF5FF2CFBA9A1B +:100F5000930728F481E090E0CBDBCF5FF2CFBA9A21 :100F6000C0E08C2F90E02091400130914101821728 -:100F7000930728F481E090E0C1DBCF5FF2CFCF91FF +:100F7000930728F481E090E0BBDBCF5FF2CFCF9105 :100F80000895CF93B89AC0E08C2F90E02091400153 -:100F9000309141018217930728F481E090E0AEDBA5 +:100F9000309141018217930728F481E090E0A8DBAB :100FA000CF5FF2CFBA9AC0E08C2F90E02091400141 -:100FB000309141018217930728F481E090E09EDB95 +:100FB000309141018217930728F481E090E098DB9B :100FC000CF5FF2CFBA98C0E08C2F90E02091400123 -:100FD000309141018217930728F481E090E08EDB85 +:100FD000309141018217930728F481E090E088DB8B :100FE000CF5FF2CFB898C0E08C2F90E02091400105 -:100FF000309141018217930728F481E090E07EDB75 +:100FF000309141018217930728F481E090E078DB7B :10100000CF5FF2CFCF910895CF93DF93D82FC8E071 :101010008D2F8078D8DEDD0FC150D1F7DF91CF91D1 :101020001DCFFF920F931F93CF93DF93F82ED8E03D :10103000C0E0CC0F13DFC82BD150D9F7009140018D :10104000109141018D2F90E08017910728F481E0E5 -:1010500090E054DBDF5FF2CFFF2011F080E001C0B1 +:1010500090E04EDBDF5FF2CFFF2011F080E001C0B7 :1010600081E0B1DED0E08D2F90E08017910728F469 -:1010700081E090E043DBDF5FF6CF8C2FDF91CF91F3 +:1010700081E090E03DDBDF5FF6CF8C2FDF91CF91F9 :101080001F910F91FF900895A4E9B1E08827F8948B -:10109000C59ABD98EE27FF27319688F0B599FCCF09 +:10109000BA98C29AEE27FF27319688F0B299FCCF12 :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 +:1010B00030F0B29BFCCFED93FD938395EBCF880F7F +: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 diff --git a/usbtiny/usbconfig.h b/usbtiny/usbconfig.h index 15e128e..2bc0e5f 100644 --- a/usbtiny/usbconfig.h +++ b/usbtiny/usbconfig.h @@ -324,23 +324,9 @@ extern void usbEventResetReady(void); further interrupts from the debugWIRE pin changing. */ -#ifdef __ASSEMBLER__ -macro nonUsbPinChange - .global dwBuf - sbic PINB,5 - rjmp dWirePinIdle - - ldi YL,1 - sts dwBuf,YL - cbi PCMSK,5 ; 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__ */ 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 0000000..fbad0a5 Binary files /dev/null and b/utility/dwlink/dwlink.jpg differ 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/build/command.cpp.o.d b/utility/usbmon/build/command.cpp.o.d new file mode 100644 index 0000000..1350e25 --- /dev/null +++ b/utility/usbmon/build/command.cpp.o.d @@ -0,0 +1 @@ +build/command.cpp.o: command.cpp command.hpp usbmon.hpp diff --git a/utility/usbmon/build/hostio.cpp.o.d b/utility/usbmon/build/hostio.cpp.o.d new file mode 100644 index 0000000..a1f3dab --- /dev/null +++ b/utility/usbmon/build/hostio.cpp.o.d @@ -0,0 +1 @@ +build/hostio.cpp.o: hostio.cpp hostio.hpp diff --git a/utility/usbmon/build/hostser.cpp.o.d b/utility/usbmon/build/hostser.cpp.o.d new file mode 100644 index 0000000..a3e1337 --- /dev/null +++ b/utility/usbmon/build/hostser.cpp.o.d @@ -0,0 +1,2 @@ +build/hostser.cpp.o: hostser.cpp command.hpp rpc.hpp ../core/rpc.hpp \ + ../core/rpc.hpp hostser.hpp hostio.hpp diff --git a/utility/usbmon/build/hostusb.cpp.o.d b/utility/usbmon/build/hostusb.cpp.o.d new file mode 100644 index 0000000..3bc6085 --- /dev/null +++ b/utility/usbmon/build/hostusb.cpp.o.d @@ -0,0 +1,2 @@ +build/hostusb.cpp.o: hostusb.cpp command.hpp rpc.hpp ../core/rpc.hpp \ + ../core/rpc.hpp hostusb.hpp hostio.hpp diff --git a/utility/usbmon/build/rpc.cpp.o.d b/utility/usbmon/build/rpc.cpp.o.d new file mode 100644 index 0000000..35702ff --- /dev/null +++ b/utility/usbmon/build/rpc.cpp.o.d @@ -0,0 +1,2 @@ +build/rpc.cpp.o: rpc.cpp command.hpp rpc.hpp ../core/rpc.hpp \ + ../core/rpc.hpp diff --git a/utility/usbmon/build/targexec.cpp.o.d b/utility/usbmon/build/targexec.cpp.o.d new file mode 100644 index 0000000..f217e70 --- /dev/null +++ b/utility/usbmon/build/targexec.cpp.o.d @@ -0,0 +1 @@ +build/targexec.cpp.o: targexec.cpp command.hpp targexec.hpp diff --git a/utility/usbmon/build/usbmon.cpp.o.d b/utility/usbmon/build/usbmon.cpp.o.d new file mode 100644 index 0000000..d71925c --- /dev/null +++ b/utility/usbmon/build/usbmon.cpp.o.d @@ -0,0 +1,2 @@ +build/usbmon.cpp.o: usbmon.cpp command.hpp hostser.hpp hostio.hpp \ + hostusb.hpp targexec.hpp usbmon.hpp diff --git a/utility/usbmon/build/zaloh/make/dwire-debug/utility/core/rpc.cpp.o.d b/utility/usbmon/build/zaloh/make/dwire-debug/utility/core/rpc.cpp.o.d new file mode 100644 index 0000000..2f158d3 --- /dev/null +++ b/utility/usbmon/build/zaloh/make/dwire-debug/utility/core/rpc.cpp.o.d @@ -0,0 +1,3 @@ +build//zaloh/make/dwire-debug/utility/core/rpc.cpp.o: \ + /zaloh/make/dwire-debug/utility/core/rpc.cpp \ + /zaloh/make/dwire-debug/utility/core/rpc.hpp diff --git a/utility/usbmon/command.cpp b/utility/usbmon/command.cpp new file mode 100644 index 0000000..b7c5bdf --- /dev/null +++ b/utility/usbmon/command.cpp @@ -0,0 +1,199 @@ +#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 void* s) +{ + size_t l = strlen((const char*)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* opt_device = nullptr; +int opt_power = false; +int opt_reset = false; +int opt_debug = false; +int opt_nomon = false; +char* opt_set_serial = 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 { }; + static char usageText[] + = R"usageText(usage: usbmon [OPTION]... + + -d, --device T# serial number prefixed by device type character + (Usb|usbTiny|Serial) + -p, --power cycle (requires dw) + -r, --reset (requires usb) + -l, --load FILE hex (experimental) + -n, --no-monitor don't launch monitor + + --debug show debug messages + --set-serial # update serial number (requires dw) +)usageText"; + try { + enum { + lo_flag, + lo_set_serial, + }; + static struct option options[] = { + {"device", required_argument, 0, 'd' }, + {"power", no_argument, 0, 'p' }, + {"reset", no_argument, 0, 'r' }, + {"load", required_argument, 0, 'l' }, + {"no-monitor", no_argument, 0, 'n' }, + {"debug", no_argument, &opt_debug, true }, + {"set-serial", required_argument, 0, lo_set_serial }, + {nullptr, 0, nullptr, 0} + }; + for (int c, i; (c = getopt_long(argc, argv, "d:prl:nt", 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 0: // set a flag + break; + + case 'd': // device + opt_device = optarg; + break; + + case 'p': // power + opt_power = true; + break; + + case 'r': // reset + opt_reset = true; + break; + + case 'l': //load + load(optarg); + break; + + case 'n': // no-monitor + opt_nomon = true; + break; + + case lo_set_serial: + opt_set_serial = optarg; + 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 << usageText; + exit(3); + } + if (!opt_device || (*opt_device != 'u' && *opt_device != 't' && *opt_device != 's')) { + std::cerr << "invalid device\n\n" << usageText; + 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); + + // execute commands + monitor(); + + return 0; +} diff --git a/utility/usbmon/command.hpp b/utility/usbmon/command.hpp new file mode 100644 index 0000000..be94bc5 --- /dev/null +++ b/utility/usbmon/command.hpp @@ -0,0 +1,20 @@ +#ifndef command_hpp +#define command_hpp + +void SysAbort(const char* e, ...); +void Debug(const void* format, ...); +int SysConWrite(const void* s); +int SysConAvail(); +int SysConRead(); + +extern char* opt_device; +extern int opt_power; +extern int opt_reset; +extern int opt_debug; +extern int opt_nomon; +extern char* opt_set_serial; + +#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/hostio.cpp b/utility/usbmon/hostio.cpp new file mode 100644 index 0000000..385ffe9 --- /dev/null +++ b/utility/usbmon/hostio.cpp @@ -0,0 +1,102 @@ +#include "hostio.hpp" + +#if __GNUC__ > 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 new file mode 100644 index 0000000..91cd42b --- /dev/null +++ b/utility/usbmon/hostusb.cpp @@ -0,0 +1,434 @@ +#include "command.hpp" +#include "rpc.hpp" +#include "hostusb.hpp" + +#include +#include +#include // apt-get install libusb-1.0-0-dev +#include +#include +#include + +static struct libusb_context* lu_ctx = nullptr; +RunExit(libusb) { if (lu_ctx) libusb_exit(lu_ctx); } + +LuXactUsb::LuXactUsb(uint32_t _vidpid, const char* vendor, const char* product, const char* serial) + : vidpid(_vidpid) +{ + 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(); +} + +LuXactUsb::~LuXactUsb() +{ + Close(); +} + +bool LuXactUsb::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 (!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 LuXactUsb::matchVidpid(uint32_t _vidpid) { + return vidpid == _vidpid; +} + +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)); +} + +bool LuXactUsb::matchSerial(const char16_t* serial) { + std::string sSerial{label.Segment(sgSerial)}; + return sSerial.length() == 0 || sSerial == to_bytes(serial); +} + +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 LuXactUsb::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 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 + | (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); + 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 LuXactUsb::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(); + } +} + +LuXactUsbRpc::LuXactUsbRpc(const char* serialnumber) + : LuXactUsb(0x16c005df, nullptr, "RpcUsb", serialnumber) { +} + +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 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; + } + // 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 LuXactUsbRpc::Send(char data) +{ + try { + XferRetry(0, (uint8_t)data); + } catch(...) { } +} + +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; + uint8_t xfer = XferRetry(index, arg, (char*)&rpc + sizeof(arg), size, false); + if (xfer != size) + throw Exception(__PRETTY_FUNCTION__); +} + +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) { + if (xfer == 1) data[1] = '\0'; + if (sink) + sink(data); + return; + } + if (data[xfer-1] >= RpcNumber) + throw Exception(__PRETTY_FUNCTION__); + ((Rpc*)data)->VtAct(data[xfer-1]); + } +} + +bool LuXactUsbRpc::Reset() +{ + if (!lu_dev) + return false; + Xfer(0xFF, 0); + Close(); + return true; +} + +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; + return LuXactUsb::Open(); +} + +void LuXactLw::Send(char data) { + // Cancel any dw commands + XferRetry(60/*dw*/, 0); + + // send bytes and read if listening + char buf[2] = ""; buf[1] = data; + Xfer(60/*dw*/, (listening ? 0x1C : 0x04), buf, sizeof(buf), false); +} + +void LuXactLw::Send(struct Rpc& rpc, uint8_t index) { +} + +void LuXactLw::Recv() { + if (listening) { + // libusb calls taken from dwire/DigiSpark.c + + // Read back dWIRE bytes + 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) + 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) { + 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]); + } + } + } + + // Wait for start bit and Read bytes + XferRetry(60/*dw*/, 0x18); + listening = true; +} + +bool LuXactLw::Reset() { +} + +bool LuXactLw::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; + + // 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; + + // Display the baud rate + 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 + char data = 0x06; + if (Xfer(60/*dw*/, 0x3C, &data, sizeof(data), false) < 0) + return false; + } + + break; + } + + Label(); + return true; +} + +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; + 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+ 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 new file mode 100644 index 0000000..f198752 --- /dev/null +++ b/utility/usbmon/hostusb.hpp @@ -0,0 +1,85 @@ +#ifndef hostusb_hpp +#define hostusb_hpp + +#include "hostio.hpp" + +class LuXactUsb : public LuXact { +public: + + const uint32_t timeout = 10; + + LuXactUsb(uint32_t vidpid, const char* vendor, const char* product, const char* serial); + ~LuXactUsb(); + + bool Open(); + bool IsOpen() const; + bool Close(); + 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); + +protected: + + // 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; +}; + +class LuXactUsbRpc : public LuXactUsb { +public: + + LuXactUsbRpc(const char* serialnumber); + char16_t* Serial(const char* serialnumber); + + void Send(char data); + void Send(struct Rpc& rpc, uint8_t index); + void Recv(); + + bool Reset(); + +protected: + bool matchSerial(const char16_t* serial); + void storeDescriptors(const char16_t* vendor, const char16_t* product, const char16_t* serial); +}; + +class LuXactLw : public LuXactUsb { +public: + + LuXactLw(const char* serialnumber); + + bool Open(); + + 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: + enum { + sgBaudrate = LuXactUsb::sgNumber, + sgNumber + }; + + bool listening; +}; + +#endif 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/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/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 new file mode 100644 index 0000000..647d92d --- /dev/null +++ b/utility/usbmon/usbmon.cpp @@ -0,0 +1,210 @@ +#include "command.hpp" +#include "hostser.hpp" +#include "hostusb.hpp" +#include "targexec.hpp" +#include "usbmon.hpp" + +#include +#include +#include +#include + +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; + } + + case 't': { + LuXactLw luXact(opt_device + 1); + if (!luXact.Open()) { + std::cerr << "error opening device" << std::endl; + break; + } + + 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; + } + + case 's': { + LuXactSer luXact(opt_device + 1); + if (!luXact.Open()) { + std::cerr << "error opening device" << std::endl; + break; + } + + 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; + } + + } +} + +void load(const char* file) { + + TargExec targExec(file); + if (!targExec.data.blob) { + std::cerr << "no data" << std::endl; + return; + } + 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"); + } +} + + 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); +} + +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 new file mode 100644 index 0000000..114b5d1 --- /dev/null +++ b/utility/usbmon/usbmon.hpp @@ -0,0 +1,8 @@ +#ifndef usbmon_hpp +#define usbmon_hpp + +void monitor(); +void load(const char* file); +void test(); + +#endif