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-.
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...