/Designs/Tools/i2c_AVR_USB/SW/firmware/usbtiny/common.mk |
---|
0,0 → 1,61 |
# ====================================================================== |
# Common Makefile for USBtiny applications |
# |
# Macros to be defined before including this file: |
# |
# USBTINY - the location of this directory |
# TARGET_ARCH - gcc -mmcu= option with AVR device type |
# OBJECTS - the objects in addition to the USBtiny objects |
# FLASH_CMD - command to upload main.hex to flash |
# STACK - maximum stack size (optional) |
# FLASH - flash size (optional) |
# SRAM - SRAM size (optional) |
# SCHEM - Postscript version of the schematic to be generated |
# |
# Copyright (C) 2006 Dick Streefland |
# |
# This is free software, licensed under the terms of the GNU General |
# Public License as published by the Free Software Foundation. |
# ====================================================================== |
CC = avr-gcc |
CFLAGS = -Os -g -Wall -I. -I$(USBTINY) |
ASFLAGS = -Os -g -Wall -I. |
LDFLAGS = -g |
MODULES = crc.o int.o usb.o $(OBJECTS) |
UTIL = $(USBTINY)/../util |
main.hex: |
all: main.hex $(SCHEM) |
clean: |
rm -f main.elf *.o tags *.sch~ gschem.log |
clobber: clean |
rm -f main.hex $(SCHEM) |
main.elf: $(MODULES) |
$(LINK.o) -o $@ $(MODULES) |
main.hex: main.elf $(UTIL)/check.py |
@python $(UTIL)/check.py main.elf $(STACK) $(FLASH) $(SRAM) |
avr-objcopy -j .text -j .data -O ihex main.elf main.hex |
disasm: main.elf |
avr-objdump -S main.elf |
flash: main.hex |
$(FLASH_CMD) |
crc.o: $(USBTINY)/crc.S $(USBTINY)/def.h usbtiny.h |
$(COMPILE.c) $(USBTINY)/crc.S |
int.o: $(USBTINY)/int.S $(USBTINY)/def.h usbtiny.h |
$(COMPILE.c) $(USBTINY)/int.S |
usb.o: $(USBTINY)/usb.c $(USBTINY)/def.h $(USBTINY)/usb.h usbtiny.h |
$(COMPILE.c) $(USBTINY)/usb.c |
main.o: $(USBTINY)/usb.h |
%.ps: %.sch $(UTIL)/sch2ps |
$(UTIL)/sch2ps $< |
/Designs/Tools/i2c_AVR_USB/SW/firmware/usbtiny/crc.S |
---|
0,0 → 1,123 |
; ====================================================================== |
; Calculate and append CRC |
; |
; The CRC is calculated 4 bits at a time, using a precomputed table of |
; 16 values. Each value is 16 bits, but only the 8 significant bits are |
; stored. The table should not cross a 256-byte page. The check.py script |
; will check for this. |
; |
; A bitwise algorithm would be a little smaller, but takes more time. |
; In fact, it takes too much time for the USB controller in my laptop. |
; The poll frequently is so high, that a lot of time is spent in the |
; interrupt handler, sending NAK packets, leaving little time for the |
; actual checksum calculation. An 8 bit algoritm would be even faster, |
; but requires a lookup table of 512 bytes. |
; |
; Copyright (C) 2006 Dick Streefland |
; |
; This is free software, licensed under the terms of the GNU General |
; Public License as published by the Free Software Foundation. |
; ====================================================================== |
#include "def.h" |
; ---------------------------------------------------------------------- |
; void crc(unsigned char *data, unsigned char len); |
; ---------------------------------------------------------------------- |
#define data r24 |
#define len r22 |
#define b r18 |
#define tmp r19 |
#define zl r20 |
#define crc_l r24 |
#define crc_h r25 |
.text |
.global crc |
.type crc, @function |
crc: |
; crc = 0xffff |
movw XL, r24 |
ldi crc_h, 0xff |
ldi crc_l, 0xff |
lsl len |
breq done |
ldi zl, lo8(crc4tab) |
ldi ZH, hi8(crc4tab) |
next_nibble: |
; b = (len & 1 ? b >> 4 : *data++) |
swap b |
sbrs len, 0 |
ld b, X+ |
; index = (crc ^ b) & 0x0f |
mov ZL, crc_l |
eor ZL, b |
andi ZL, 0x0f |
; crc >>= 4 |
swap crc_h |
swap crc_l |
andi crc_l, 0x0f |
mov tmp, crc_h |
andi tmp, 0xf0 |
or crc_l, tmp |
andi crc_h, 0x0f |
; crc ^= crc4tab[index] |
add ZL, zl |
lpm tmp, Z+ |
eor crc_h, tmp |
andi tmp, 1 |
eor crc_h, tmp |
eor crc_l, tmp |
; next nibble |
dec len |
brne next_nibble |
done: |
; crc ^= 0xffff |
com crc_l |
com crc_h |
; append crc to buffer |
st X+, crc_l |
st X+, crc_h |
ret |
; ---------------------------------------------------------------------- |
; CRC table. As bits 1..8 are always zero, omit them. |
; ---------------------------------------------------------------------- |
.section .progmem.crc,"a",@progbits |
;;; .align 4 ; avoid crossing a page boundary |
crc4tab: |
.byte 0x00+0x00 |
.byte 0xcc+0x01 |
.byte 0xd8+0x01 |
.byte 0x14+0x00 |
.byte 0xf0+0x01 |
.byte 0x3c+0x00 |
.byte 0x28+0x00 |
.byte 0xe4+0x01 |
.byte 0xa0+0x01 |
.byte 0x6c+0x00 |
.byte 0x78+0x00 |
.byte 0xb4+0x01 |
.byte 0x50+0x00 |
.byte 0x9c+0x01 |
.byte 0x88+0x01 |
.byte 0x44+0x00 |
/* ---------------------------------------------------------------------- *\ |
#!/usr/bin/python |
for crc in range(16): |
for bit in range(4): |
xor = crc & 1 |
crc >>= 1 |
if xor: |
crc ^= 0xA001 # X^16 + X^15 + X^2 + 1 (reversed) |
print "\t.byte\t0x%02x+0x%02x" % (crc >> 8, crc & 0xff) |
\* ---------------------------------------------------------------------- */ |
/Designs/Tools/i2c_AVR_USB/SW/firmware/usbtiny/def.h |
---|
0,0 → 1,74 |
// ====================================================================== |
// Common definitions for the USB driver |
// |
// Copyright (C) 2006 Dick Streefland |
// |
// This is free software, licensed under the terms of the GNU General |
// Public License as published by the Free Software Foundation. |
// ====================================================================== |
#ifdef __ASSEMBLER__ |
#define __SFR_OFFSET 0 |
#endif |
#include <avr/io.h> |
#include "usbtiny.h" |
// Preprocessor magic |
#define CAT2(a,b) CAT2EXP(a, b) |
#define CAT2EXP(a,b) a ## b |
#define CAT3(a,b,c) CAT3EXP(a, b, c) |
#define CAT3EXP(a,b,c) a ## b ## c |
// I/O Ports |
#define USB_IN CAT2(PIN, USBTINY_PORT) |
#define USB_OUT CAT2(PORT, USBTINY_PORT) |
#define USB_DDR CAT2(DDR, USBTINY_PORT) |
// I/O bit masks |
#define USB_MASK_DMINUS (1 << (USBTINY_DMINUS)) |
#define USB_MASK_DPLUS (1 << (USBTINY_DPLUS)) |
#define USB_MASK (USB_MASK_DMINUS | USB_MASK_DPLUS) |
// Interrupt configuration |
#if defined EICRA |
# define USB_INT_CONFIG EICRA |
#else |
# define USB_INT_CONFIG MCUCR |
#endif |
#define USB_INT_CONFIG_SET ((1 << CAT3(ISC,USBTINY_INT,1)) | (1 << CAT3(ISC,USBTINY_INT,0))) |
#if defined SIG_INT0 |
# define USB_INT_VECTOR CAT2(SIG_INT, USBTINY_INT) |
#else |
# define USB_INT_VECTOR CAT2(SIG_INTERRUPT, USBTINY_INT) |
#endif |
// Interrupt enable |
#if defined GIMSK |
# define USB_INT_ENABLE GIMSK |
#elif defined EIMSK |
# define USB_INT_ENABLE EIMSK |
#else |
# define USB_INT_ENABLE GICR |
#endif |
#define USB_INT_ENABLE_BIT CAT2(INT,USBTINY_INT) |
// Interrupt pending bit |
#if defined EIFR |
# define USB_INT_PENDING EIFR |
#else |
# define USB_INT_PENDING GIFR |
#endif |
#define USB_INT_PENDING_BIT CAT2(INTF,USBTINY_INT) |
// USB PID values |
#define USB_PID_SETUP 0x2d |
#define USB_PID_OUT 0xe1 |
#define USB_PID_IN 0x69 |
#define USB_PID_DATA0 0xc3 |
#define USB_PID_DATA1 0x4b |
#define USB_PID_ACK 0xd2 |
#define USB_PID_NAK 0x5a |
#define USB_PID_STALL 0x1e |
// Various constants |
#define USB_BUFSIZE 11 // PID + data + CRC |
/Designs/Tools/i2c_AVR_USB/SW/firmware/usbtiny/int.S |
---|
0,0 → 1,562 |
; ====================================================================== |
; USB interrupt handler |
; |
; This is the handler for the interrupt caused by the initial rising edge |
; on the D+ USB signal. The NRZI encoding and bit stuffing are removed, |
; and the packet is saved in one of the two input buffers. In some cases, |
; a reply packet is sent right away. |
; |
; When a DATA0/DATA1 packet directly follows a SETUP or OUT packet, while |
; this interrupt handler is not yet finished, there would be no time to |
; return and take another interrupt. In that case, the second packet is |
; decoded directly in the same invocation. |
; |
; This code is *extremely* time critical. For instance, there is not a |
; single spare cycle in the receiver loop, and only two in the transmitter |
; loop. In addition, the various code paths are laid out in such a way that |
; the various USB timeouts are not violated, in particular the maximum time |
; between the reception of a packet and the reply, which is 6.5 bit times |
; for a detachable cable (TRSPIPD1), and 7.5 bit times for a captive cable |
; (TRSPIPD2). The worst-case delay here is 51 cycles, which is just below |
; the 52 cycles for a detachable cable. |
; |
; The interrupt handler must be reached within 34 cycles after D+ goes high |
; for the first time, so the interrupts should not be disabled for longer |
; than 34-4-2=28 cycles. |
; |
; The end-of-packet (EOP) is sampled in the second bit, because the USB |
; standard allows the EOP to be delayed by up to one bit. As the EOP |
; duration is two bits, this is not a problem. |
; |
; Stack usage including the return address: 11 bytes. |
; |
; Copyright (C) 2006 Dick Streefland |
; |
; This is free software, licensed under the terms of the GNU General |
; Public License as published by the Free Software Foundation. |
; ====================================================================== |
#include "def.h" |
; ---------------------------------------------------------------------- |
; local data |
; ---------------------------------------------------------------------- |
.data |
tx_ack: .byte USB_PID_ACK ; ACK packet |
tx_nak: .byte USB_PID_NAK ; NAK packet |
.lcomm token_pid, 1 ; PID of most recent token packet |
; ---------------------------------------------------------------------- |
; register definitions |
; ---------------------------------------------------------------------- |
// receiver: |
#define count r16 |
#define usbmask r17 |
#define odd r18 |
#define byte r19 |
#define fixup r20 |
#define even r22 |
// transmitter: |
#define output odd |
#define done fixup |
#define next even |
// control: |
#define pid odd |
#define addr usbmask |
#define tmp fixup |
#define nop2 rjmp .+0 // not .+2 for some strange reason |
; ---------------------------------------------------------------------- |
; interrupt handler |
; ---------------------------------------------------------------------- |
.text |
.global USB_INT_VECTOR |
.type USB_INT_VECTOR, @function |
; ---------------------------------------------------------------------- |
; This handler must be reached no later than 34 cycles after D+ goes high |
; for the first time. |
; ---------------------------------------------------------------------- |
USB_INT_VECTOR: |
; save registers |
push count |
push usbmask |
push odd |
push YH |
push YL |
in count, SREG |
push count |
; ---------------------------------------------------------------------- |
; Synchronize to the pattern 10101011 on D+. This code must be reached |
; no later than 47 cycles after D+ goes high for the first time. |
; ---------------------------------------------------------------------- |
sync: |
; wait until D+ == 0 |
sbic USB_IN, USBTINY_DPLUS |
rjmp sync ; jump if D+ == 1 |
resync: |
; sync on 0-->1 transition on D+ with a 2 cycle resolution |
sbic USB_IN, USBTINY_DPLUS |
rjmp sync6 ; jump if D+ == 1 |
sbic USB_IN, USBTINY_DPLUS |
rjmp sync6 ; jump if D+ == 1 |
sbic USB_IN, USBTINY_DPLUS |
rjmp sync6 ; jump if D+ == 1 |
sbic USB_IN, USBTINY_DPLUS |
rjmp sync6 ; jump if D+ == 1 |
sbic USB_IN, USBTINY_DPLUS |
rjmp sync6 ; jump if D+ == 1 |
ldi count, 1<<USB_INT_PENDING_BIT |
out USB_INT_PENDING, count |
rjmp return ; ==> false start, bail out |
sync6: |
; we are now between -1 and +1 cycle from the center of the bit |
; following the 0-->1 transition |
lds YL, usb_rx_off |
clr YH |
subi YL, lo8(-(usb_rx_buf)) ; Y = & usb_rx_buf[usb_rx_off] |
sbci YH, hi8(-(usb_rx_buf)) |
ldi count, USB_BUFSIZE ; limit on number of bytes to receive |
ldi usbmask, USB_MASK ; why is there no eori instruction? |
ldi odd, USB_MASK_DPLUS |
sync7: |
; the last sync bit should also be 1 |
sbis USB_IN, USBTINY_DPLUS ; bit 7 of sync byte? |
rjmp resync ; no, wait for next transition |
push byte |
push fixup |
push even |
; ---------------------------------------------------------------------- |
; receiver loop |
; ---------------------------------------------------------------------- |
in even, USB_IN ; sample bit 0 |
ldi byte, 0x80 ; load sync byte for correct unstuffing |
rjmp rxentry ; 2 cycles |
rxloop: |
in even, USB_IN ; sample bit 0 |
or fixup, byte |
st Y+, fixup ; 2 cycles |
rxentry: |
clr fixup |
andi even, USB_MASK |
eor odd, even |
subi odd, 1 |
in odd, USB_IN ; sample bit 1 |
andi odd, USB_MASK |
breq eop ; ==> EOP detected |
ror byte |
cpi byte, 0xfc |
brcc skip0 |
skipped0: |
eor even, odd |
subi even, 1 |
in even, USB_IN ; sample bit 2 |
andi even, USB_MASK |
ror byte |
cpi byte, 0xfc |
brcc skip1 |
skipped1: |
eor odd, even |
subi odd, 1 |
ror byte |
in odd, USB_IN ; sample bit 3 |
andi odd, USB_MASK |
cpi byte, 0xfc |
brcc skip2 |
eor even, odd |
subi even, 1 |
ror byte |
skipped2: |
cpi byte, 0xfc |
in even, USB_IN ; sample bit 4 |
andi even, USB_MASK |
brcc skip3 |
eor odd, even |
subi odd, 1 |
ror byte |
skipped4: |
cpi byte, 0xfc |
skipped3: |
brcc skip4 |
in odd, USB_IN ; sample bit 5 |
andi odd, USB_MASK |
eor even, odd |
subi even, 1 |
ror byte |
skipped5: |
cpi byte, 0xfc |
brcc skip5 |
dec count |
in even, USB_IN ; sample bit 6 |
brmi overflow ; ==> overflow |
andi even, USB_MASK |
eor odd, even |
subi odd, 1 |
ror byte |
skipped6: |
cpi byte, 0xfc |
brcc skip6 |
in odd, USB_IN ; sample bit 7 |
andi odd, USB_MASK |
eor even, odd |
subi even, 1 |
ror byte |
cpi byte, 0xfc |
brcs rxloop ; 2 cycles |
rjmp skip7 |
eop: |
rjmp eop2 |
overflow: |
rjmp ignore |
; ---------------------------------------------------------------------- |
; out-of-line code to skip stuffing bits |
; ---------------------------------------------------------------------- |
skip0: ; 1+6 cycles |
eor even, usbmask |
in odd, USB_IN ; resample bit 1 |
andi odd, USB_MASK |
cbr byte, (1<<7) |
sbr fixup, (1<<0) |
rjmp skipped0 |
skip1: ; 2+5 cycles |
cbr byte, (1<<7) |
sbr fixup, (1<<1) |
in even, USB_IN ; resample bit 2 |
andi even, USB_MASK |
eor odd, usbmask |
rjmp skipped1 |
skip2: ; 3+7 cycles |
cbr byte, (1<<7) |
sbr fixup, (1<<2) |
eor even, usbmask |
in odd, USB_IN ; resample bit 3 |
andi odd, USB_MASK |
eor even, odd |
subi even, 1 |
ror byte |
rjmp skipped2 |
skip3: ; 4+7 cycles |
cbr byte, (1<<7) |
sbr fixup, (1<<3) |
eor odd, usbmask |
ori byte, 1 |
in even, USB_IN ; resample bit 4 |
andi even, USB_MASK |
eor odd, even |
subi odd, 1 |
ror byte |
rjmp skipped3 |
skip4: ; 5 cycles |
cbr byte, (1<<7) |
sbr fixup, (1<<4) |
eor even, usbmask |
rjmp skipped4 |
skip5: ; 5 cycles |
cbr byte, (1<<7) |
sbr fixup, (1<<5) |
eor odd, usbmask |
rjmp skipped5 |
skip6: ; 5 cycles |
cbr byte, (1<<7) |
sbr fixup, (1<<6) |
eor even, usbmask |
rjmp skipped6 |
skip7: ; 7 cycles |
cbr byte, (1<<7) |
sbr fixup, (1<<7) |
eor odd, usbmask |
nop2 |
rjmp rxloop |
; ---------------------------------------------------------------------- |
; end-of-packet detected (worst-case: 3 cycles after end of SE0) |
; ---------------------------------------------------------------------- |
eop2: |
; clear pending interrupt (SE0+3) |
ldi byte, 1<<USB_INT_PENDING_BIT |
out USB_INT_PENDING, byte ; clear pending bit at end of packet |
; ignore packets shorter than 3 bytes |
subi count, USB_BUFSIZE |
neg count ; count = packet length |
cpi count, 3 |
brlo ignore |
; get PID |
sub YL, count |
ld pid, Y |
; check for DATA0/DATA1 first, as this is the critical path (SE0+12) |
cpi pid, USB_PID_DATA0 |
breq is_data ; handle DATA0 packet |
cpi pid, USB_PID_DATA1 |
breq is_data ; handle DATA1 packet |
; check ADDR (SE0+16) |
ldd addr, Y+1 |
andi addr, 0x7f |
lds tmp, usb_address |
cp addr, tmp ; is this packet for me? |
brne ignore ; no, ignore |
; check for other PIDs (SE0+23) |
cpi pid, USB_PID_IN |
breq is_in ; handle IN packet |
cpi pid, USB_PID_SETUP |
breq is_setup_out ; handle SETUP packet |
cpi pid, USB_PID_OUT |
breq is_setup_out ; handle OUT packet |
; ---------------------------------------------------------------------- |
; exit point for ignored packets |
; ---------------------------------------------------------------------- |
ignore: |
clr tmp |
sts token_pid, tmp |
pop even |
pop fixup |
pop byte |
rjmp return |
; ---------------------------------------------------------------------- |
; Handle SETUP/OUT (SE0+30) |
; ---------------------------------------------------------------------- |
is_setup_out: |
sts token_pid, pid ; save PID of token packet |
pop even |
pop fixup |
pop byte |
in count, USB_INT_PENDING ; next packet already started? |
sbrc count, USB_INT_PENDING_BIT |
rjmp sync ; yes, get it right away (SE0+42) |
; ---------------------------------------------------------------------- |
; restore registers and return from interrupt |
; ---------------------------------------------------------------------- |
return: |
pop count |
out SREG, count |
pop YL |
pop YH |
pop odd |
pop usbmask |
pop count |
reti |
; ---------------------------------------------------------------------- |
; Handle IN (SE0+26) |
; ---------------------------------------------------------------------- |
is_in: |
lds count, usb_tx_len |
tst count ; data ready? |
breq nak ; no, reply with NAK |
lds tmp, usb_rx_len |
tst tmp ; unprocessed input packet? |
brne nak ; yes, don't send old data for new packet |
sts usb_tx_len, tmp ; buffer is available again (after reti) |
ldi YL, lo8(usb_tx_buf) |
ldi YH, hi8(usb_tx_buf) |
rjmp send_packet ; SE0+40, SE0 --> SOP <= 51 |
; ---------------------------------------------------------------------- |
; Handle DATA0/DATA1 (SE0+17) |
; ---------------------------------------------------------------------- |
is_data: |
lds pid, token_pid |
tst pid ; data following our SETUP/OUT |
breq ignore ; no, ignore |
lds tmp, usb_rx_len |
tst tmp ; buffer free? |
brne nak ; no, reply with NAK |
sts usb_rx_len, count ; pass buffer length |
sts usb_rx_token, pid ; pass PID of token (SETUP or OUT) |
lds count, usb_rx_off ; switch to other input buffer |
ldi tmp, USB_BUFSIZE |
sub tmp, count |
sts usb_rx_off, tmp |
; ---------------------------------------------------------------------- |
; send ACK packet (SE0+35) |
; ---------------------------------------------------------------------- |
ack: |
ldi YL, lo8(tx_ack) |
ldi YH, hi8(tx_ack) |
rjmp send_token |
; ---------------------------------------------------------------------- |
; send NAK packet (SE0+36) |
; ---------------------------------------------------------------------- |
nak: |
ldi YL, lo8(tx_nak) |
ldi YH, hi8(tx_nak) |
send_token: |
ldi count, 1 ; SE0+40, SE0 --> SOP <= 51 |
; ---------------------------------------------------------------------- |
; acquire the bus and send a packet (11 cycles to SOP) |
; ---------------------------------------------------------------------- |
send_packet: |
in output, USB_OUT |
cbr output, USB_MASK |
ori output, USB_MASK_DMINUS |
in usbmask, USB_DDR |
ori usbmask, USB_MASK |
out USB_OUT, output ; idle state |
out USB_DDR, usbmask ; acquire bus |
ldi usbmask, USB_MASK |
ldi byte, 0x80 ; start with sync byte |
; ---------------------------------------------------------------------- |
; transmitter loop |
; ---------------------------------------------------------------------- |
txloop: |
sbrs byte, 0 |
eor output, usbmask |
out USB_OUT, output ; output bit 0 |
ror byte |
ror done |
stuffed0: |
cpi done, 0xfc |
brcc stuff0 |
sbrs byte, 0 |
eor output, usbmask |
ror byte |
stuffed1: |
out USB_OUT, output ; output bit 1 |
ror done |
cpi done, 0xfc |
brcc stuff1 |
sbrs byte, 0 |
eor output, usbmask |
ror byte |
nop |
stuffed2: |
out USB_OUT, output ; output bit 2 |
ror done |
cpi done, 0xfc |
brcc stuff2 |
sbrs byte, 0 |
eor output, usbmask |
ror byte |
nop |
stuffed3: |
out USB_OUT, output ; output bit 3 |
ror done |
cpi done, 0xfc |
brcc stuff3 |
sbrs byte, 0 |
eor output, usbmask |
ld next, Y+ ; 2 cycles |
out USB_OUT, output ; output bit 4 |
ror byte |
ror done |
stuffed4: |
cpi done, 0xfc |
brcc stuff4 |
sbrs byte, 0 |
eor output, usbmask |
ror byte |
stuffed5: |
out USB_OUT, output ; output bit 5 |
ror done |
cpi done, 0xfc |
brcc stuff5 |
sbrs byte, 0 |
eor output, usbmask |
ror byte |
stuffed6: |
ror done |
out USB_OUT, output ; output bit 6 |
cpi done, 0xfc |
brcc stuff6 |
sbrs byte, 0 |
eor output, usbmask |
ror byte |
mov byte, next |
stuffed7: |
ror done |
out USB_OUT, output ; output bit 7 |
cpi done, 0xfc |
brcc stuff7 |
dec count |
brpl txloop ; 2 cycles |
rjmp gen_eop |
; ---------------------------------------------------------------------- |
; out-of-line code to insert stuffing bits |
; ---------------------------------------------------------------------- |
stuff0: ; 2+3 |
eor output, usbmask |
clr done |
out USB_OUT, output |
rjmp stuffed0 |
stuff1: ; 3 |
eor output, usbmask |
rjmp stuffed1 |
stuff2: ; 3 |
eor output, usbmask |
rjmp stuffed2 |
stuff3: ; 3 |
eor output, usbmask |
rjmp stuffed3 |
stuff4: ; 2+3 |
eor output, usbmask |
clr done |
out USB_OUT, output |
rjmp stuffed4 |
stuff5: ; 3 |
eor output, usbmask |
rjmp stuffed5 |
stuff6: ; 3 |
eor output, usbmask |
rjmp stuffed6 |
stuff7: ; 3 |
eor output, usbmask |
rjmp stuffed7 |
; ---------------------------------------------------------------------- |
; generate EOP, release the bus, and return from interrupt |
; ---------------------------------------------------------------------- |
gen_eop: |
cbr output, USB_MASK |
out USB_OUT, output ; output SE0 for 2 bit times |
pop even |
pop fixup |
pop byte |
ldi count, 1<<USB_INT_PENDING_BIT |
out USB_INT_PENDING, count ; interrupt was triggered by transmit |
pop YH ; this is the saved SREG |
pop YL |
in usbmask, USB_DDR |
mov count, output |
ori output, USB_MASK_DMINUS |
out USB_OUT, output ; output J state for 1 bit time |
cbr usbmask, USB_MASK |
out SREG, YH |
pop YH |
pop odd ; is the same register as output! |
nop |
out USB_DDR, usbmask ; release bus |
out USB_OUT, count ; disable D- pullup |
pop usbmask |
pop count |
reti |
/Designs/Tools/i2c_AVR_USB/SW/firmware/usbtiny/usb.c |
---|
0,0 → 1,418 |
// ====================================================================== |
// USB driver |
// |
// Entry points: |
// usb_init() - enable the USB interrupt |
// usb_poll() - poll for incoming packets and process them |
// |
// This code communicates with the interrupt handler through a number of |
// global variables, including two input buffers and one output buffer. |
// Packets are queued for transmission by copying them into the output |
// buffer. The interrupt handler will transmit such a packet on the |
// reception of an IN packet. |
// |
// Standard SETUP packets are handled here. Non-standard SETUP packets |
// are forwarded to the application code by calling usb_setup(). The |
// macros USBTINY_CALLBACK_IN and USBTINY_CALLBACK_OUT control whether |
// the callback functions usb_in() and usb_out() will be called for IN |
// and OUT transfers. |
// |
// Maximum stack usage (gcc 3.4.3 & 4.1.0) of usb_poll(): 5 bytes plus |
// possible additional stack usage in usb_setup(), usb_in() or usb_out(). |
// |
// Copyright (C) 2006 Dick Streefland |
// |
// This is free software, licensed under the terms of the GNU General |
// Public License as published by the Free Software Foundation. |
// ====================================================================== |
#include <avr/pgmspace.h> |
#include <avr/interrupt.h> |
#include "def.h" |
#include "usb.h" |
#define LE(word) (word) & 0xff, (word) >> 8 |
// ---------------------------------------------------------------------- |
// USB constants |
// ---------------------------------------------------------------------- |
enum |
{ |
DESCRIPTOR_TYPE_DEVICE = 1, |
DESCRIPTOR_TYPE_CONFIGURATION, |
DESCRIPTOR_TYPE_STRING, |
DESCRIPTOR_TYPE_INTERFACE, |
DESCRIPTOR_TYPE_ENDPOINT, |
}; |
// ---------------------------------------------------------------------- |
// Interrupt handler interface |
// ---------------------------------------------------------------------- |
byte_t usb_rx_buf[2*USB_BUFSIZE]; // two input buffers |
byte_t usb_rx_off; // buffer offset: 0 or USB_BUFSIZE |
byte_t usb_rx_len; // buffer size, 0 means empty |
byte_t usb_rx_token; // PID of token packet: SETUP or OUT |
byte_t usb_tx_buf[USB_BUFSIZE]; // output buffer |
byte_t usb_tx_len; // output buffer size, 0 means empty |
byte_t usb_address; // assigned USB address |
// ---------------------------------------------------------------------- |
// Local data |
// ---------------------------------------------------------------------- |
enum |
{ |
TX_STATE_IDLE = 0, // transmitter idle |
TX_STATE_RAM, // usb_tx_data is a RAM address |
TX_STATE_ROM, // usb_tx_data is a ROM address |
TX_STATE_CALLBACK, // call usb_in() to obtain transmit data |
}; |
static byte_t usb_tx_state; // TX_STATE_*, see enum above |
static byte_t usb_tx_total; // total transmit size |
static byte_t* usb_tx_data; // pointer to data to transmit |
static byte_t new_address; // new device address |
#if defined USBTINY_VENDOR_NAME |
struct |
{ |
byte_t length; |
byte_t type; |
int string[sizeof(USBTINY_VENDOR_NAME)-1]; |
} string_vendor PROGMEM = |
{ |
2 * sizeof(USBTINY_VENDOR_NAME), |
DESCRIPTOR_TYPE_STRING, |
{ CAT2(L, USBTINY_VENDOR_NAME) } |
}; |
# define VENDOR_NAME_ID 1 |
#else |
# define VENDOR_NAME_ID 0 |
#endif |
#if defined USBTINY_DEVICE_NAME |
struct |
{ |
byte_t length; |
byte_t type; |
int string[sizeof(USBTINY_DEVICE_NAME)-1]; |
} string_device PROGMEM = |
{ |
2 * sizeof(USBTINY_DEVICE_NAME), |
DESCRIPTOR_TYPE_STRING, |
{ CAT2(L, USBTINY_DEVICE_NAME) } |
}; |
# define DEVICE_NAME_ID 2 |
#else |
# define DEVICE_NAME_ID 0 |
#endif |
#if defined USBTINY_SERIAL |
struct |
{ |
byte_t length; |
byte_t type; |
int string[sizeof(USBTINY_SERIAL)-1]; |
} string_serial PROGMEM = |
{ |
2 * sizeof(USBTINY_SERIAL), |
DESCRIPTOR_TYPE_STRING, |
{ CAT2(L, USBTINY_SERIAL) } |
}; |
# define SERIAL_ID 3 |
#else |
# define SERIAL_ID 0 |
#endif |
#if VENDOR_NAME_ID || DEVICE_NAME_ID || SERIAL_ID |
static byte_t string_langid [] PROGMEM = |
{ |
4, // bLength |
DESCRIPTOR_TYPE_STRING, // bDescriptorType (string) |
LE(0x0409), // wLANGID[0] (American English) |
}; |
#endif |
// Device Descriptor |
static byte_t descr_device [18] PROGMEM = |
{ |
18, // bLength |
DESCRIPTOR_TYPE_DEVICE, // bDescriptorType |
LE(0x0110), // bcdUSB |
USBTINY_DEVICE_CLASS, // bDeviceClass |
USBTINY_DEVICE_SUBCLASS, // bDeviceSubClass |
USBTINY_DEVICE_PROTOCOL, // bDeviceProtocol |
8, // bMaxPacketSize0 |
LE(USBTINY_VENDOR_ID), // idVendor |
LE(USBTINY_DEVICE_ID), // idProduct |
LE(USBTINY_DEVICE_VERSION), // bcdDevice |
VENDOR_NAME_ID, // iManufacturer |
DEVICE_NAME_ID, // iProduct |
SERIAL_ID, // iSerialNumber |
1, // bNumConfigurations |
}; |
// Configuration Descriptor |
static byte_t descr_config [] PROGMEM = |
{ |
9, // bLength |
DESCRIPTOR_TYPE_CONFIGURATION, // bDescriptorType |
LE(9+9+7*USBTINY_ENDPOINT), // wTotalLength |
1, // bNumInterfaces |
1, // bConfigurationValue |
0, // iConfiguration |
(USBTINY_MAX_POWER ? 0x80 : 0xc0), // bmAttributes |
(USBTINY_MAX_POWER + 1) / 2, // MaxPower |
// Standard Interface Descriptor |
9, // bLength |
DESCRIPTOR_TYPE_INTERFACE, // bDescriptorType |
0, // bInterfaceNumber |
0, // bAlternateSetting |
USBTINY_ENDPOINT, // bNumEndpoints |
USBTINY_INTERFACE_CLASS, // bInterfaceClass |
USBTINY_INTERFACE_SUBCLASS, // bInterfaceSubClass |
USBTINY_INTERFACE_PROTOCOL, // bInterfaceProtocol |
0, // iInterface |
#if USBTINY_ENDPOINT |
// Additional Endpoint |
7, // bLength |
DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType |
USBTINY_ENDPOINT_ADDRESS, // bEndpointAddress |
USBTINY_ENDPOINT_TYPE, // bmAttributes |
LE(8), // wMaxPacketSize |
USBTINY_ENDPOINT_INTERVAL, // bInterval |
#endif |
}; |
// ---------------------------------------------------------------------- |
// Inspect an incoming packet. |
// ---------------------------------------------------------------------- |
static void usb_receive ( byte_t* data, byte_t rx_len ) |
{ |
byte_t len; |
byte_t type; |
byte_t limit; |
usb_tx_state = TX_STATE_RAM; |
len = 0; |
if ( usb_rx_token == USB_PID_SETUP ) |
{ |
limit = data[6]; |
if ( data[7] ) |
{ |
limit = 255; |
} |
type = data[0] & 0x60; |
if ( type == 0x00 ) |
{ // Standard request |
if ( data[1] == 0 ) // GET_STATUS |
{ |
len = 2; |
#if USBTINY_MAX_POWER == 0 |
data[0] = (data[0] == 0x80); |
#else |
data[0] = 0; |
#endif |
data[1] = 0; |
} |
else if ( data[1] == 5 ) // SET_ADDRESS |
{ |
new_address = data[2]; |
} |
else if ( data[1] == 6 ) // GET_DESCRIPTOR |
{ |
usb_tx_state = TX_STATE_ROM; |
if ( data[3] == 1 ) |
{ // DEVICE |
data = (byte_t*) &descr_device; |
len = sizeof(descr_device); |
} |
else if ( data[3] == 2 ) |
{ // CONFIGURATION |
data = (byte_t*) &descr_config; |
len = sizeof(descr_config); |
} |
#if VENDOR_NAME_ID || DEVICE_NAME_ID || SERIAL_ID |
else if ( data[3] == 3 ) |
{ // STRING |
if ( data[2] == 0 ) |
{ |
data = (byte_t*) &string_langid; |
len = sizeof(string_langid); |
} |
#if VENDOR_NAME_ID |
else if ( data[2] == VENDOR_NAME_ID ) |
{ |
data = (byte_t*) &string_vendor; |
len = sizeof(string_vendor); |
} |
#endif |
#if DEVICE_NAME_ID |
else if ( data[2] == DEVICE_NAME_ID ) |
{ |
data = (byte_t*) &string_device; |
len = sizeof(string_device); |
} |
#endif |
#if SERIAL_ID |
else if ( data[2] == SERIAL_ID ) |
{ |
data = (byte_t*) &string_serial; |
len = sizeof(string_serial); |
} |
#endif |
} |
#endif |
} |
else if ( data[1] == 8 ) // GET_CONFIGURATION |
{ |
data[0] = 1; // return bConfigurationValue |
len = 1; |
} |
else if ( data[1] == 10 ) // GET_INTERFACE |
{ |
data[0] = 0; |
len = 1; |
} |
} |
else |
{ // Class or Vendor request |
len = usb_setup( data ); |
#if USBTINY_CALLBACK_IN |
if ( len == 0xff ) |
{ |
usb_tx_state = TX_STATE_CALLBACK; |
} |
#endif |
} |
if ( len > limit ) |
{ |
len = limit; |
} |
usb_tx_data = data; |
} |
#if USBTINY_CALLBACK_OUT |
else if ( rx_len > 0 ) |
{ // usb_rx_token == USB_PID_OUT |
usb_out( data, rx_len ); |
} |
#endif |
usb_tx_total = len; |
usb_tx_buf[0] = USB_PID_DATA0; // next data packet will be DATA1 |
} |
// ---------------------------------------------------------------------- |
// Load the transmit buffer with the next packet. |
// ---------------------------------------------------------------------- |
static void usb_transmit ( void ) |
{ |
byte_t len; |
byte_t* src; |
byte_t* dst; |
byte_t i; |
byte_t b; |
usb_tx_buf[0] ^= (USB_PID_DATA0 ^ USB_PID_DATA1); |
len = usb_tx_total; |
if ( len > 8 ) |
{ |
len = 8; |
} |
dst = usb_tx_buf + 1; |
if ( len > 0 ) |
{ |
#if USBTINY_CALLBACK_IN |
if ( usb_tx_state == TX_STATE_CALLBACK ) |
{ |
len = usb_in( dst, len ); |
} |
else |
#endif |
{ |
src = usb_tx_data; |
if ( usb_tx_state == TX_STATE_RAM ) |
{ |
for ( i = 0; i < len; i++ ) |
{ |
*dst++ = *src++; |
} |
} |
else // usb_tx_state == TX_STATE_ROM |
{ |
for ( i = 0; i < len; i++ ) |
{ |
b = pgm_read_byte( src ); |
src++; |
*dst++ = b; |
} |
} |
usb_tx_data = src; |
} |
usb_tx_total -= len; |
} |
crc( usb_tx_buf + 1, len ); |
usb_tx_len = len + 3; |
if ( len < 8 ) |
{ // this is the last packet |
usb_tx_state = TX_STATE_IDLE; |
} |
} |
// ---------------------------------------------------------------------- |
// Initialize the low-level USB driver. |
// ---------------------------------------------------------------------- |
extern void usb_init ( void ) |
{ |
USB_INT_CONFIG |= USB_INT_CONFIG_SET; |
USB_INT_ENABLE |= (1 << USB_INT_ENABLE_BIT); |
sei(); |
} |
// ---------------------------------------------------------------------- |
// Poll USB driver: |
// - check for incoming USB packets |
// - refill an empty transmit buffer |
// - check for USB bus reset |
// ---------------------------------------------------------------------- |
extern void usb_poll ( void ) |
{ |
byte_t i; |
// check for incoming USB packets |
if ( usb_rx_len != 0 ) |
{ |
usb_receive( usb_rx_buf + USB_BUFSIZE - usb_rx_off + 1, usb_rx_len - 3 ); |
usb_tx_len = 0; // abort pending transmission |
usb_rx_len = 0; // accept next packet |
} |
// refill an empty transmit buffer, when the transmitter is active |
if ( usb_tx_len == 0 ) |
{ |
if ( usb_tx_state != TX_STATE_IDLE ) |
{ |
usb_transmit(); |
} |
else |
{ // change the USB address at the end of a transfer |
usb_address = new_address; |
} |
} |
// check for USB bus reset |
for ( i = 10; i > 0 && ! (USB_IN & USB_MASK_DMINUS); i-- ) |
{ |
} |
if ( i == 0 ) |
{ // SE0 for more than 2.5uS is a reset |
cli(); |
usb_tx_len=0; |
usb_rx_len=0; |
new_address = 0; |
sei(); |
} |
} |
/Designs/Tools/i2c_AVR_USB/SW/firmware/usbtiny/usb.h |
---|
0,0 → 1,28 |
// ====================================================================== |
// Public interface of the USB driver |
// |
// Copyright (C) 2006 Dick Streefland |
// |
// This is free software, licensed under the terms of the GNU General |
// Public License as published by the Free Software Foundation. |
// ====================================================================== |
#ifndef USB_H |
#define USB_H |
typedef unsigned char byte_t; |
typedef unsigned int uint_t; |
// usb.c |
extern void usb_init ( void ); |
extern void usb_poll ( void ); |
// crc.S |
extern void crc ( byte_t* data, byte_t len ); |
// application callback functions |
extern byte_t usb_setup ( byte_t data[8] ); |
extern void usb_out ( byte_t* data, byte_t len ); |
extern byte_t usb_in ( byte_t* data, byte_t len ); |
#endif // USB_H |