/Designs/Tools/i2c_AVR_USB/SW/firmware/usbtiny/common.mk
7,21 → 7,24
# 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
# FUSES_CMD - command to program the fuse bytes
# 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
# Copyright 2006-2010 Dick Streefland
#
# This is free software, licensed under the terms of the GNU General
# Public License as published by the Free Software Foundation.
# ======================================================================
 
check = $(shell $(CC) $1 -c -xc /dev/null -o/dev/null 2>/dev/null && echo $1)
 
CC = avr-gcc
CFLAGS = -Os -g -Wall -I. -I$(USBTINY)
ASFLAGS = -Os -g -Wall -I.
LDFLAGS = -g
OPTIM = -Os -ffunction-sections $(call check,-fno-split-wide-types)
CFLAGS = -g -Wall -Wattributes -I. -I$(USBTINY) $(OPTIM)
LDFLAGS = -g -Wl,--relax,--gc-sections
MODULES = crc.o int.o usb.o $(OBJECTS)
UTIL = $(USBTINY)/../util
 
42,6 → 45,9
@python $(UTIL)/check.py main.elf $(STACK) $(FLASH) $(SRAM)
avr-objcopy -j .text -j .data -O ihex main.elf main.hex
 
check: main.elf $(UTIL)/check.py
@python $(UTIL)/check.py main.elf $(STACK) $(FLASH) $(SRAM)
 
disasm: main.elf
avr-objdump -S main.elf
 
48,6 → 54,9
flash: main.hex
$(FLASH_CMD)
 
fuses:
$(FUSES_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
/Designs/Tools/i2c_AVR_USB/SW/firmware/usbtiny/crc.S
1,19 → 1,19
; ======================================================================
; 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.
; There are two versions of the CRC16 calculation, selectable by the
; USBTINY_FAST_CRC macro. The default implementation calculates one bit
; at a time, and is compact but relatively slow. The "fast" version
; processes 4 bits at a time, and is about twice as fast, but 42 bytes
; larger.
;
; 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,
; The fast version calculates 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. An 8 bit algoritm would be even faster,
; but requires a lookup table of 512 bytes.
;
; Copyright (C) 2006 Dick Streefland
; Copyright 2006-2010 Dick Streefland
;
; This is free software, licensed under the terms of the GNU General
; Public License as published by the Free Software Foundation.
21,6 → 21,7
 
#include "def.h"
 
#if USBTINY_FAST_CRC
; ----------------------------------------------------------------------
; void crc(unsigned char *data, unsigned char len);
; ----------------------------------------------------------------------
41,30 → 42,42
movw XL, r24
ldi crc_h, 0xff
ldi crc_l, 0xff
lsl len
breq done
ldi zl, lo8(crc4tab)
ldi ZH, hi8(crc4tab)
rjmp entry
 
next_nibble:
; b = (len & 1 ? b >> 4 : *data++)
swap b
sbrs len, 0
next_byte:
; crc ^= b
ld b, X+
eor crc_l, b
 
; index = (crc ^ b) & 0x0f
; index1 = crc & 0x0f
mov ZL, crc_l
eor ZL, b
andi ZL, 0x0f
 
; crc >>= 4
; tmp = crc4tab[index1]
add ZL, zl
lpm tmp, Z+
 
; index2 = (crc >> 4)
mov ZL, crc_l
swap ZL
 
; crc >>= 8
mov crc_l, crc_h
 
; index2 = (index2 ^ tmp) & 0xf
mov crc_h, tmp
andi tmp, 1
eor ZL, tmp
andi ZL, 0x0f
 
; treat upper byte of CRC remainder
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
andi tmp, 0xe0
eor crc_l, tmp
 
; crc ^= crc4tab[index]
add ZL, zl
74,9 → 87,10
eor crc_h, tmp
eor crc_l, tmp
 
; next nibble
entry:
; next byte
dec len
brne next_nibble
brpl next_byte
 
done:
; crc ^= 0xffff
93,7 → 107,7
; CRC table. As bits 1..8 are always zero, omit them.
; ----------------------------------------------------------------------
.section .progmem.crc,"a",@progbits
;;; .align 4 ; avoid crossing a page boundary
;;; .align 4 ; crude way to avoid crossing a page boundary
crc4tab:
.byte 0x00+0x00
.byte 0xcc+0x01
121,3 → 135,49
crc ^= 0xA001 # X^16 + X^15 + X^2 + 1 (reversed)
print "\t.byte\t0x%02x+0x%02x" % (crc >> 8, crc & 0xff)
\* ---------------------------------------------------------------------- */
#else
; ----------------------------------------------------------------------
; void crc(unsigned char *data, unsigned char len);
; ----------------------------------------------------------------------
#define data r24
#define len r22
 
#define b r18
#define con_01 r19
#define con_a0 r20
#define crc_l r24
#define crc_h r25
 
.text
.global crc
.type crc, @function
crc:
movw XL, r24
ldi crc_h, 0xff
ldi crc_l, 0xff
tst len
breq done1
ldi con_a0, 0xa0
ldi con_01, 0x01
next_byte:
ld b, X+
eor crc_l, b
ldi b, 8
next_bit:
lsr crc_h
ror crc_l
brcc noxor
eor crc_h, con_a0
eor crc_l, con_01
noxor:
dec b
brne next_bit
dec len
brne next_byte
done1:
com crc_l
com crc_h
st X+, crc_l
st X+, crc_h
ret
#endif
/Designs/Tools/i2c_AVR_USB/SW/firmware/usbtiny/def.h
1,7 → 1,7
// ======================================================================
// Common definitions for the USB driver
//
// Copyright (C) 2006 Dick Streefland
// Copyright 2006-2010 Dick Streefland
//
// This is free software, licensed under the terms of the GNU General
// Public License as published by the Free Software Foundation.
19,12 → 19,12
#define CAT3(a,b,c) CAT3EXP(a, b, c)
#define CAT3EXP(a,b,c) a ## b ## c
 
// I/O Ports
// I/O Ports for USB
#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
// I/O bit masks for USB
#define USB_MASK_DMINUS (1 << (USBTINY_DMINUS))
#define USB_MASK_DPLUS (1 << (USBTINY_DPLUS))
#define USB_MASK (USB_MASK_DMINUS | USB_MASK_DPLUS)
36,11 → 36,7
# 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
#define USB_INT_VECTOR CAT3(INT, USBTINY_INT, _vect)
 
// Interrupt enable
#if defined GIMSK
59,6 → 55,9
# define USB_INT_PENDING GIFR
#endif
#define USB_INT_PENDING_BIT CAT2(INTF,USBTINY_INT)
#if defined INF0 && ! defined INTF0
# define INTF0 INF0 // fix for incorrect definition in iotn13.h
#endif
 
// USB PID values
#define USB_PID_SETUP 0x2d
72,3 → 71,26
 
// Various constants
#define USB_BUFSIZE 11 // PID + data + CRC
 
// Bit manipulation macros
#define BIT_CLR(reg,bit) { (reg) &= ~ _BV(bit); }
#define BIT_SET(reg,bit) { (reg) |= _BV(bit); }
#define BIT_TST(reg,bit) (((reg) & _BV(bit)) != 0)
 
// I/O port manipulation macros
#define DDR_CLR(p,b) BIT_CLR(DDR ## p, b)
#define DDR_SET(p,b) BIT_SET(DDR ## p, b)
#define PORT_CLR(p,b) BIT_CLR(PORT ## p, b)
#define PORT_SET(p,b) BIT_SET(PORT ## p, b)
#define PORT_TST(p,b) BIT_TST(PORT ## p, b)
#define PIN_TST(p,b) BIT_TST(PIN ## p, b)
#define PIN_SET(p,b) BIT_SET(PIN ## p, b)
 
// Macros that can be used with an argument of the form (port,bit)
#define INPUT(bit) DDR_CLR bit
#define OUTPUT(bit) DDR_SET bit
#define CLR(bit) PORT_CLR bit
#define SET(bit) PORT_SET bit
#define ISSET(bit) PORT_TST bit
#define TST(bit) PIN_TST bit
#define TOGGLE(bit) PIN_SET bit
/Designs/Tools/i2c_AVR_USB/SW/firmware/usbtiny/int.S
9,20 → 9,26
; 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.
; decoded directly in the same invocation. A packet immediately following
; an ignored packet is also decoded directly.
;
; 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.
; between the reception of a packet and the reply, which is 7.5 bit times
; (TRSPIPD2) for a low-speed USB captive cable. The worst-case delay here
; is 51 cycles, which is well below the 60 cycles limit, and even below the
; 6.5 bit times limit for a detachable cable (TRSPIPD1).
;
; 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.
; for the first time. The interrupt response time is 4 cycles, and the RJMP
; in the vector table takes 2 cycles. Therefore, the interrupts should not
; be disabled for longer than: 34 - 4 - 2 = 28 cycles. When the I-bit is
; reenabled, a single instruction is always executed before a pending
; interrupt is served, so this instruction should be included in the
; calculation. For RETI, the next instruction can be anything, so we
; should assume the worst-case of 4 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
30,7 → 36,7
;
; Stack usage including the return address: 11 bytes.
;
; Copyright (C) 2006 Dick Streefland
; Copyright 2006-2010 Dick Streefland
;
; This is free software, licensed under the terms of the GNU General
; Public License as published by the Free Software Foundation.
45,6 → 51,7
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
.global __do_copy_data
 
; ----------------------------------------------------------------------
; register definitions
291,46 → 298,51
; 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
; calculate packet length
subi count, USB_BUFSIZE
neg count ; count = packet length
cpi count, 3
brlo ignore
; get PID
sub YL, count
sbci YH, 0
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)
; separate out the non-Token packets (SE0+11)
sbrc pid, 1
rjmp is_data_handshake ; jump for Data or Handshake packet
; check ADDR of Token packet (SE0+13)
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)
; dispatch Token packets (SE0+20)
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
brne is_setup_out ; handle SETUP and OUT packets
 
; ----------------------------------------------------------------------
; exit point for ignored packets
; Handle IN (SE0+22)
; ----------------------------------------------------------------------
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)
lds tmp, usb_new_address
sts usb_address, tmp ; assign new address at end of transfer
ldi YL, lo8(usb_tx_buf)
ldi YH, hi8(usb_tx_buf)
rjmp send_packet ; SE0+40, SE0 --> SOP <= 51
 
; ----------------------------------------------------------------------
; exit point for ignored packets (SE0+21)
; ----------------------------------------------------------------------
ignore:
clr tmp
sts token_pid, tmp
pop even
pop fixup
pop byte
rjmp return
clr pid
ignore0:
 
; ----------------------------------------------------------------------
; Handle SETUP/OUT (SE0+30)
; Handle SETUP/OUT (SE0+23)
; ----------------------------------------------------------------------
is_setup_out:
sts token_pid, pid ; save PID of token packet
339,10 → 351,10
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)
rjmp sync ; yes, get it right away (SE0+35)
 
; ----------------------------------------------------------------------
; restore registers and return from interrupt
; restore registers and return from interrupt (SE0+34)
; ----------------------------------------------------------------------
return:
pop count
355,27 → 367,26
reti
 
; ----------------------------------------------------------------------
; Handle IN (SE0+26)
; send NAK packet (SE0+31)
; ----------------------------------------------------------------------
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
nak:
ldi YL, lo8(tx_nak)
ldi YH, hi8(tx_nak)
rjmp send_token
 
; ----------------------------------------------------------------------
; Handle DATA0/DATA1 (SE0+17)
; Handle Data and Handshake packets (SE0+14)
; ----------------------------------------------------------------------
is_data:
is_data_handshake:
andi pid, 0x01
breq ignore0 ; ignore ACK/NAK/STALL
 
; ----------------------------------------------------------------------
; Handle DATA0/DATA1 (SE0+16)
; ----------------------------------------------------------------------
lds pid, token_pid
tst pid ; data following our SETUP/OUT
breq ignore ; no, ignore
breq ignore0 ; no, ignore
lds tmp, usb_rx_len
tst tmp ; buffer free?
brne nak ; no, reply with NAK
387,21 → 398,12
sts usb_rx_off, tmp
 
; ----------------------------------------------------------------------
; send ACK packet (SE0+35)
; send ACK packet (SE0+34)
; ----------------------------------------------------------------------
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
ldi count, 1 ; SE0+37, SE0 --> SOP <= 48
 
; ----------------------------------------------------------------------
; acquire the bus and send a packet (11 cycles to SOP)
/Designs/Tools/i2c_AVR_USB/SW/firmware/usbtiny/usb.c
17,10 → 17,10
// 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
// Maximum stack usage (gcc 4.1.0 & 4.3.4) of usb_poll(): 5 bytes plus
// possible additional stack usage in usb_setup(), usb_in() or usb_out().
//
// Copyright (C) 2006 Dick Streefland
// Copyright 2006-2010 Dick Streefland
//
// This is free software, licensed under the terms of the GNU General
// Public License as published by the Free Software Foundation.
58,7 → 58,8
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
byte_t usb_address; // assigned device address
byte_t usb_new_address; // new device address
 
// ----------------------------------------------------------------------
// Local data
75,7 → 76,6
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
83,7 → 83,7
byte_t length;
byte_t type;
int string[sizeof(USBTINY_VENDOR_NAME)-1];
} string_vendor PROGMEM =
} const string_vendor PROGMEM =
{
2 * sizeof(USBTINY_VENDOR_NAME),
DESCRIPTOR_TYPE_STRING,
100,7 → 100,7
byte_t length;
byte_t type;
int string[sizeof(USBTINY_DEVICE_NAME)-1];
} string_device PROGMEM =
} const string_device PROGMEM =
{
2 * sizeof(USBTINY_DEVICE_NAME),
DESCRIPTOR_TYPE_STRING,
117,7 → 117,7
byte_t length;
byte_t type;
int string[sizeof(USBTINY_SERIAL)-1];
} string_serial PROGMEM =
} const string_serial PROGMEM =
{
2 * sizeof(USBTINY_SERIAL),
DESCRIPTOR_TYPE_STRING,
129,7 → 129,7
#endif
 
#if VENDOR_NAME_ID || DEVICE_NAME_ID || SERIAL_ID
static byte_t string_langid [] PROGMEM =
static byte_t const string_langid [] PROGMEM =
{
4, // bLength
DESCRIPTOR_TYPE_STRING, // bDescriptorType (string)
138,11 → 138,11
#endif
 
// Device Descriptor
static byte_t descr_device [18] PROGMEM =
static byte_t const descr_device [18] PROGMEM =
{
18, // bLength
DESCRIPTOR_TYPE_DEVICE, // bDescriptorType
LE(0x0110), // bcdUSB
LE(0x0101), // bcdUSB
USBTINY_DEVICE_CLASS, // bDeviceClass
USBTINY_DEVICE_SUBCLASS, // bDeviceSubClass
USBTINY_DEVICE_PROTOCOL, // bDeviceProtocol
157,7 → 157,7
};
 
// Configuration Descriptor
static byte_t descr_config [] PROGMEM =
static byte_t const descr_config [] PROGMEM =
{
9, // bLength
DESCRIPTOR_TYPE_CONFIGURATION, // bDescriptorType
223,7 → 223,10
}
else if ( data[1] == 5 ) // SET_ADDRESS
{
new_address = data[2];
usb_new_address = data[2];
#ifdef USBTINY_USB_OK_LED
SET(USBTINY_USB_OK_LED);// LED on
#endif
}
else if ( data[1] == 6 ) // GET_DESCRIPTOR
{
371,6 → 374,13
{
USB_INT_CONFIG |= USB_INT_CONFIG_SET;
USB_INT_ENABLE |= (1 << USB_INT_ENABLE_BIT);
#ifdef USBTINY_USB_OK_LED
OUTPUT(USBTINY_USB_OK_LED);
#endif
#ifdef USBTINY_DMINUS_PULLUP
SET(USBTINY_DMINUS_PULLUP);
OUTPUT(USBTINY_DMINUS_PULLUP); // enable pullup on D-
#endif
sei();
}
 
392,16 → 402,9
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_len == 0 && usb_tx_state != TX_STATE_IDLE )
{
if ( usb_tx_state != TX_STATE_IDLE )
{
usb_transmit();
}
else
{ // change the USB address at the end of a transfer
usb_address = new_address;
}
usb_transmit();
}
// check for USB bus reset
for ( i = 10; i > 0 && ! (USB_IN & USB_MASK_DMINUS); i-- )
409,10 → 412,10
}
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();
usb_new_address = 0;
usb_address = 0;
#ifdef USBTINY_USB_OK_LED
CLR(USBTINY_USB_OK_LED); // LED off
#endif
}
}
/Designs/Tools/i2c_AVR_USB/SW/firmware/usbtiny/usb.h
1,7 → 1,7
// ======================================================================
// Public interface of the USB driver
//
// Copyright (C) 2006 Dick Streefland
// Copyright 2006-2008 Dick Streefland
//
// This is free software, licensed under the terms of the GNU General
// Public License as published by the Free Software Foundation.