Page 1 of 1

The simplest possible V-USB device

Posted: Wed Feb 24, 2010 9:10 pm
by PHermansson
My goal is to create the simplest possible V-USB based device. It won't to anything, just be visible to the system as a Libusb device. May not sound too interesting, but see it as a framework for further developement.

Hardware
Based on a ATTiny25. Two 1N4148 in the USB power line, 100n and 10-47u capacitors over plus and ground.
68 ohm resistors on D+ and D-, 1k5 pull-up on D-.

Image

Software
First, the standard V-USB code is used.
In usbconfig.h the following is changed:
#define USB_CFG_MAX_BUS_POWER 100
#define USB_CFG_VENDOR_ID 0x16, 0xc0
#define USB_CFG_DEVICE_ID 0x05, 0xdf
#define USB_CFG_VENDOR_NAME 'p', 'a', 't', 'r', 'i', 'k', 'h', 'e', 'r', 'm', 'a', 'n', 's', 's', 'o', 'n', '.','s','e'
#define USB_CFG_VENDOR_NAME_LEN 20
#define USB_CFG_DEVICE_NAME 'u', 's', 'b', '-', 't', 'e', 's', 't'
#define USB_CFG_DEVICE_NAME_LEN 8

I'm not yet convinced how to set the and USB_CFG_VENDOR_ID and USB_CFG_DEVICE_ID for my non-commercial project. I don't own a VID/PID .

main.c:

Code: Select all

/* Name: main.c
The goal is to create the simplest possible USB device
Patrik Hermansson 2010
*/
#include "usbdrv.h"
#include "oddebug.h"
#include <avr/eeprom.h>   //Eeprom read
#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/interrupt.h>   //sei()
#include <avr/pgmspace.h>
#include <util/delay.h>      //_delay_ms
#include <stdlib.h>

#define UTIL_BIN4(x)        (uchar)((0##x & 01000)/64 + (0##x & 0100)/16 + (0##x & 010)/4 + (0##x & 1))
#define UTIL_BIN8(hi, lo)   (uchar)(UTIL_BIN4(hi) * 16 + UTIL_BIN4(lo))

/* ------------------------------------------------------------------------- */

/* Function that runs when data is requested from host */

uchar   usbFunctionSetup(uchar data[8])
{
   return 0;
}


/* ------------------------------------------------------------------------- */
/* ------------------------ Oscillator Calibration ------------------------- */
/* ------------------------------------------------------------------------- */

/* Calibrate the RC oscillator to 8.25 MHz. The core clock of 16.5 MHz is
 * derived from the 66 MHz peripheral clock by dividing. Our timing reference
 * is the Start Of Frame signal (a single SE0 bit) available immediately after
 * a USB RESET. We first do a binary search for the OSCCAL value and then
 * optimize this value with a neighboorhod search.
 * This algorithm may also be used to calibrate the RC oscillator directly to
 * 12 MHz (no PLL involved, can therefore be used on almost ALL AVRs), but this
 * is wide outside the spec for the OSCCAL value and the required precision for
 * the 12 MHz clock! Use the RC oscillator calibrated to 12 MHz for
 * experimental purposes only!
 */
static void calibrateOscillator(void)
{
uchar       step = 128;
uchar       trialValue = 0, optimumValue;
int         x, optimumDev, targetValue = (unsigned)(1499 * (double)F_CPU / 10.5e6 + 0.5);

    /* do a binary search: */
    do{
        OSCCAL = trialValue + step;
        x = usbMeasureFrameLength();    /* proportional to current real frequency */
        if(x < targetValue)             /* frequency still too low */
            trialValue += step;
        step >>= 1;
    }while(step > 0);
    /* We have a precision of +/- 1 for optimum OSCCAL here */
    /* now do a neighborhood search for optimum value */
    optimumValue = trialValue;
    optimumDev = x; /* this is certainly far away from optimum */
    for(OSCCAL = trialValue - 1; OSCCAL <= trialValue + 1; OSCCAL++){
        x = usbMeasureFrameLength() - targetValue;
        if(x < 0)
            x = -x;
        if(x < optimumDev){
            optimumDev = x;
            optimumValue = OSCCAL;
        }
    }
    OSCCAL = optimumValue;
}
/*
Note: This calibration algorithm may try OSCCAL values of up to 192 even if
the optimum value is far below 192. It may therefore exceed the allowed clock
frequency of the CPU in low voltage designs!
You may replace this search algorithm with any other algorithm you like if
you have additional constraints such as a maximum CPU clock.
For version 5.x RC oscillators (those with a split range of 2x128 steps, e.g.
ATTiny25, ATTiny45, ATTiny85), it may be useful to search for the optimum in
both regions.
*/

void    usbEventResetReady(void)
{
    calibrateOscillator();
    eeprom_write_byte(0, OSCCAL);   /* store the calibrated value in EEPROM */
}


/* Main */
uchar main(void)
{
   DDRB= (1<<1);   //Set PB1 (pin 6) as output
   uchar   i;
   uchar   calibrationValue;
   
   //Clock calibration setup
   calibrationValue = eeprom_read_byte(0); /* calibration value from last time */
   if(calibrationValue != 0xff){
      OSCCAL = calibrationValue;
   }
   usbDeviceDisconnect();
   for(i=0;i<20;i++){  /* 300 ms disconnect */
      _delay_ms(15);
   }
   usbDeviceConnect();
   usbInit();
   sei();         // enable interrupts
   for(;;){            /* main event loop */
      usbPoll();   //USB communication
   }
   return 0;
}


Makefile:

Code: Select all

# Name: Makefile
# Project: EasyLogger
# Author: Christian Starkjohann
# Creation Date: 2007-06-23
# Tabsize: 4
# Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH
# License: GPLv2.
# This Revision: $Id: Makefile 362 2007-06-25 14:38:21Z cs $

DEVICE=attiny25
AVRDUDE = avrdude -c usbasp -p $(DEVICE)
# The two lines above are for "avrdude" and the STK500 programmer connected
# to an USB to serial converter to a Mac running Mac OS X.
# Choose your favorite programmer and interface.

COMPILE = avr-gcc -Wall -Os -Iusbdrv -I. -mmcu=$(DEVICE) -DF_CPU=16500000 -DDEBUG_LEVEL=0
# NEVER compile the final product with debugging! Any debug output will
# distort timing so that the specs can't be met.

OBJECTS = usbdrv/usbdrv.o usbdrv/usbdrvasm.o usbdrv/oddebug.o main.o

# symbolic targets:
all:   main.hex

.c.o:
   $(COMPILE) -c $< -o $@

.S.o:
   $(COMPILE) -x assembler-with-cpp -c $< -o $@
# "-x assembler-with-cpp" should not be necessary since this is the default
# file type for the .S (with capital S) extension. However, upper case
# characters are not always preserved on Windows. To ensure WinAVR
# compatibility define the file type manually.

.c.s:
   $(COMPILE) -S $< -o $@

flash:   all
   $(AVRDUDE) -U flash:w:main.hex:i


# Fuse high byte:
# 0xdd = 1 1 0 1   1 1 0 1
#        ^ ^ ^ ^   ^ \-+-/
#        | | | |   |   +------ BODLEVEL 2..0 (brownout trigger level -> 2.7V)
#        | | | |   +---------- EESAVE (preserve EEPROM on Chip Erase -> not preserved)
#        | | | +-------------- WDTON (watchdog timer always on -> disable)
#        | | +---------------- SPIEN (enable serial programming -> enabled)
#        | +------------------ DWEN (debug wire enable)
#        +-------------------- RSTDISBL (disable external reset -> enabled)
#
# Fuse low byte:
# 0xe1 = 1 1 1 0   0 0 0 1
#        ^ ^ \+/   \--+--/
#        | |  |       +------- CKSEL 3..0 (clock selection -> HF PLL)
#        | |  +--------------- SUT 1..0 (BOD enabled, fast rising power)
#        | +------------------ CKOUT (clock output on CKOUT pin -> disabled)
#        +-------------------- CKDIV8 (divide clock by 8 -> don't divide)
fuse:
   $(AVRDUDE) -U hfuse:w:0xdd:m -U lfuse:w:0xe1:m

readcal:
   $(AVRDUDE) -U calibration:r:/dev/stdout:i | head -1


clean:
   rm -f main.hex main.lst main.obj main.cof main.list main.map main.eep.hex main.bin *.o usbdrv/*.o main.s usbdrv/oddebug.s usbdrv/usbdrv.s

# file targets:
main.bin:   $(OBJECTS)
   $(COMPILE) -o main.bin $(OBJECTS)

main.hex:   main.bin
   rm -f main.hex main.eep.hex
   avr-objcopy -j .text -j .data -O ihex main.bin main.hex
# do the checksize script as our last action to allow successful compilation
# on Windows with WinAVR where the Unix commands will fail.

disasm:   main.bin
   avr-objdump -d main.bin

cpp:
   $(COMPILE) -E main.c


Note: Not convinced on the fuse settings, have used Windows and AVR Studio to set the fuses:
spien enabled
bod disabled
Nothing else marked
pll clock, startuptime pwrdwn/reset:1k ck/14 +4ms
Ext 0xff
high 0xDF
low 0xc1


The code builds to a size of 1682 bytes. When mounted in the target board it seems to do nothing, but dmesg says:

new low speed USB device using ehci_hcd and address 101
[314209.638232] usb 1-5.4: configuration #1 chosen from 1 choice
[314218.822680] usb 1-5.1: usbfs: USBDEVFS_CONTROL failed cmd lsusb rqt 128 rq 0 len 2 ret -110

lsusb -v|less
Bus 001 Device 101: ID c016:df05
...
iManufacturer 1 patrikhermansson.se
iProduct 2 usb-test
...
MaxPower 100mA
...

It's working with a error message. Guess the PC gets a little upset when the usbFunctionSetup() just contains "return 0". When less is showing the info this pops up:
":cannot read device status, Connection timed out (110)"

To be continued...

Re: The simplest possible V-USB device

Posted: Mon Mar 22, 2010 12:00 pm
by Toby
Hi!
Could you please post your AVR-Studio project?
I'm having a few problems getting V-USB to run on AVR Studio..
Thank you very much!

Re: The simplest possible V-USB device

Posted: Tue Mar 30, 2010 10:13 pm
by PHermansson
Hello!
No sorry, I don't have a Studio project for this. I've just used Studio to set the fuses, the rest is done with a text editor, avr-gcc and avrdude.

Re: The simplest possible V-USB device

Posted: Thu Apr 08, 2010 7:11 pm
by AL
Hi,

This is very interesting project. A couple basic questions:

1. It's fine to run as 20Mhz, since I don't see the 12Mhz crystal in the schematic?

2. I noticed that your D+/D- wires are connected to the ADC pins of the Attiny. I didn't know that you can use the ADC pins rather than the serial input/output pins.

Thanks

Re: The simplest possible V-USB device

Posted: Thu Apr 08, 2010 7:17 pm
by AL
By the way, could you explain why TWO 1N4148 in the USB power line? Thanks

Re: The simplest possible V-USB device

Posted: Thu Apr 08, 2010 9:21 pm
by maxi
AL wrote:By the way, could you explain why TWO 1N4148 in the USB power line? Thanks


Answers to this and other questions can be found in the V-USB documentation wiki: http://vusb.wikidot.com/.
See in particular, hardware considerations. Hope it helps.

Re: The simplest possible V-USB device

Posted: Fri Apr 09, 2010 2:25 pm
by PHermansson
Well I remember how much I searched for information and the hours spend to understand ;)
So I can give some answers, although the Wiki is essential reading:

1/ The Attiny25/45/85 can be used with their internal oscillator, no crystal needed.
2/ The "ADC"-pins are not just "ADC"-pins, they have lots of functions (see the datasheet). Here they are used as regular I/O-pins. The important thing is that D+ is connected to Int0 (AKA PB2/SCK/ADC1 etc).

The reason for using two 1N4148's are the voltage level problem, as described in the wiki. The voltage level at the Tiny's Vcc is about 3.6 volt with this construction. This works well here, but can be a problem as it's out of range (the MCU aint supposed to work at the correct clock speed if Vcc is below 4.5 volt).