Aus RN-Wissen.de
Wechseln zu: Navigation, Suche
LiFePO4 Speicher Test

Mit V-USB von Objective Development ist es ohne weitere Hardware möglich, mit einem AVR Mikrocontroller ein USB-Gerät aufzubauen.


Hardware Vorraussetzungen des AVRs

Spannungspegel-Problem

Da der AVR mit anderen Spannungen als die des USB-Standarts arbeitet, müssen diese Angepasst werden. Der Computer und der AVR-Mikrocontroller haben beide bestimmte Anforderungen an die Spannung für einen High- bzw. Low-Pegel. Da wir die Computerseite nicht verändern können, müssen die Anpassungen am AVR statfinden. Vom PC wird ein Low-Pegel als 0V, ein High-Pegel als 3,3V übertragen. Jedoch ist die kein Problem, da die AVR-Mikrcontroller selbst bei 5V diesen Pegel sicher als High erkennen. Die andere Richtung ist ein größreres Problem. Der Computer erwartet als Low-Pegel eine Spannung von 0V - 0,8V und 2V - 3,6V als High. Um diese Vorraussetzungen zu erfüllen, muss entweder der AVR mit einer Spannung von 2V - 3,6V betreiben werden oder die Pegel müssen an D+ und D- des AVRs angepasst werden.

Lösung A: Verringern der Betreibsspannug des AVRs

Für diese Lösung wird die Betriebsspannung des AVRs auf 3,3V - 3,6V heruntergesetzt. Dies ist mit einem 3,3V-Spannungsregler (LE33CZ) möglich. Schaltplan

Vorteile

  • Saubere Lösung. Schnelle Übergänge auf D+ und D-
  • Gute Störfestigkeit am Signaleingang

Nachteile

  • 3,3V-Spannungsregler sind oft teuer und schwer zu beschaffen
  • Viele AVRs funktionieren nicht sicher bei den Angeforderten Taktraten
  • Hohe Stromaufnahme durch den Spannungsregler (überschreiten maximale Stromaufnahme von 500mA)

Ebenso ist es möglich, die Spannung mit Glechrichterdioden zu reduzieren. Hierfür werden einfach zwei oder drei Gleichrichterdioden in Reihe vor VCC des AVRs geschaltet. Schaltplan

Zusätzliche Vorteile

  • Niedrige kosten / Bauteile sind leicht zu bekommen.
  • kein Ruhestrom --> USB-Konformer Standby-Modus

Zusätzliche Nachteile

  • Keine Spannungsregelung --> Spannung kann bei hoher Last einbrechen
  • Die unregulierte Spannung ist problemtisch für Analoge Schaltungen (ADC,...)


Lösung B: Anpassen der Sapnnung an D+ und D-

Ebenso kann man die Spannung an den Datenleitungen (D+ und D-) mit Zenerdioden verringern. Es werden 3,6V Low-Power Zernerdioden (wie 1N4148, 500mW oder weniger) empfohlen, da diese eine geringe Kapazität haben und somit weniger Störungen auf den Datenleitungen verursachen. Schaltplan

Wenn diese Lösung verwendet wird, stellen sie vor benutzung sicher, dass die Spannungspegel mit den geforderten Pegeln(LOW:0V-0,8V HIGH:2V-3,6V) überinstimmen.

Vorteile

  • Niedrige kosten
  • Leicht zu bekommen
  • Ganze Schaltung kann mit 5V betrieben werden --> Hohe Taktraten möglich

Nachteile

  • Keine saubere Lösung: Es muss ein Kompromiss zwischen allen Möglichkeiten gefunden werden.
  • Zener-Dioden kommen mit einem breiten Spektrum an Merkmalen. Deshalb könnten die Ergebnisse nicht reproduzierbar sein.
  • Hohe Ströme beim Senden von High-Leveln


Allgemeine Anmerkungen zur Hardware

Es wird bei jeder der oben genannten Schaltungen ein PullUp-Widerstand (1,5k - 10k) von D- nach Vcc benötigt. Ebenso muss zwischen AVR und Die Datenleitungen ein Widerstand von je 68 Ohm.


Welche Taktraten können verwendet werden?

Momentan unterstützt V-USB Taktraten von 12 MHz, 12.8 MHz, 15 MHz, 16 MHz, 16.5 MHz, 18 MHz und 20 MHz. Diese Taktraten sind prezise, dies bedeutet, dass ein Quarz mit 11.9 MHz nicht funktionieren würde. Nur bei den 16.5 und 12.8 MHz Varianten ist eine Toleranz von 1%.16.5 Mhz kann z.B. mit dem internen RC Oszillator von AVRs wie dem ATTiny25/45/85 oder dem ATTiny26 erreicht werden. Entscheidungshilfe

  • Möchten sie den internen OC Oszillator benutzen? Dann nehmen sie die 16.5MHz-Variante.
  • Ist sehr wenig Speicher vorhanden? Verwenden sie am besten die 16MHz oder 20Mhz Variante.
  • Wird der AVR mit einer niedrigen Spannung betrieben? Dann verwenden sie die 12MHz-Variante.
  • Wollen sie CRC-Prüfsummen verwenden? Benutzen sie die 18Mhz-Variante.


Anschluss an den AVR

Die Datenleitung D+ muss über den 68 Ohm-Widerstand mit dem Port INT0 des AVRs verbunden werden. D- kann an einen beliebigen Port. Diese Ports müssen dann in der "usbconfig.h" angepasst werden.

Software des AVR: Die Firmware

Als erstes muss man sich die neueste Version von V-USB von folgender Seite herunterladen: [1] Nachdem sie das Archiv heruntergeladen haben, entpacken sie es und kopieren sie den Ordner "usbdrv" in ihr Projektverzeichnis. Kopieren bzw. erstellen das Makefile und passen darin die Taktrate und den verwendeten Controller an. Ebenso wird eine "usbconfig.h" benötigt, diese kann z.B. aus dem tests-Ordner kopiert werden. In dieser müssen nun die Punkte USB_CFG_IOPORTNAME, USB_CFG_DMINUS_BIT und USB_CFG_DPLUS_BIT angepasst werden.

In der main.c müssen immer als erstes folgende Dateien importiert werden:

#include <avr/io.h> //IO-Zugriff: Braucht man immer
#include <avr/interrupt.h> // V-USB braucht interrupts
#include <avr/pgmspace.h>
#include <avr/wdt.h> //Watchdog: Sollte immer aktiv sein.

#include "usbdrv.h" //Der USB-Treiber
#include <util/delay.h> //Wird später für Timing etc. benötigt

Gerät mit selbst geschriebenem PC-Treiber (kein HID)

Der Code für ein eigenes Gerät wird hier am Beispiel eines Gerätes veranschaulicht, bei dem sich über USB der PWM-Kanal steuern lässt. Damit kann man zum Beispiel eine LED dimmen.

Nach den Includes komm nun die Funktion zum Verarbeiten eingehender Daten. Das erste Empfangene Byte, also der Befehl wird mit rq->bRequest abgefragt. In diesem Fall ist dieser entweder 0 oder 1. 0 bedeutet den PWM-Wert zu setzen, 1 bedeutet den Status (PWM-Wert) zurückzusenden. Die einzelnen Parameter des Aufrufs können mit rq->wValue.bytes[id] abgefragt werden. Bei Befehl 0 wird in rq->wValue.bytes[0] der neue PWM-Wert übertragen. Durch setzen von replyBuf kann festgelegt werden, welche Daten zurückgesendet werden sollen. Dabei muss immer replyBuf[id] verwedet werden. Bei Befehl 1 wird mit replyBuf[0] = OCR1A der aktuelle PWM-Wert zurückgesendet. Mit return 2 wird dann noch mitgeteilt, dass überhaupt was zurückgesendet werden soll. Mit return 0 wird nichts zurückgesendet. Das ist in diesem Fall der Fall, wenn ein ungültiger Befehl übermittelt wurde.

USB_PUBLIC uchar usbFunctionSetup(uchar data[8])
{
usbRequest_t    *rq = (void *)data;
static uchar    replyBuf[2];

    usbMsgPtr = replyBuf;
    if(rq->bRequest == 0){
	replyBuf[0] = rq->wValue.bytes[0];
        replyBuf[1] = 0x0;

	OCR1A=rq->wValue.bytes[0];
	eeprom_write_byte(0,OCR1A);
	return 2;
    }
    else if (rq->bRequest == 1) {
	replyBuf[0] = OCR1A;
        replyBuf[1] = 0x0;
	return 2;
    }
    return 0;
}


Als nächstes komm void main: Hier schalten wir als erstes den Watchdog-Timer ein. Danach setzen wir alle Ports und DDR so wie wir sie brauchen. Es ist nur darauf zu achten, dass INT0 ein Eingang ist. Bei einem Atmega8 ist dies PORTD.2. Für unser Beispiel setzen wir TCCR1A als 8_bit PWM-Timer. Danach laden wir den letzten PWM-Wert aus dem EEPROM (Wird immer beim Ändern gespeichert). Nun trennen wir die Verbindung für > 500ms, dies wird benötigt, um nach einen Neustart des Mikrocontrollers (z.B. durch Watchdog) dem PC mitzuteilen, dass er neugestartet ist. Danach Verbinden wir uns wieder mit usbDeviceConnect(). Nun muss die Verbindung mit usbInit() initialisiert werden ud die Interrupts mit sei() eingeschaltet werden. Nun muss nur noch im Mainloop der Watchdog zurückgesetzt werden und usbPoll() aufgerufen werden.

int main(void)
{
uchar   i;

    wdt_enable(WDTO_1S);
    DDRD = ~(1 << 2);   /* all outputs except PD2 = INT0 */
    DDRB = 0xff;    /* output pins setzen */
    PORTD = 0;     /* USB-Verbindung */
    PORTB = 0;     /* output pins aus*/

	TCCR1A = (1<<WGM10)|(1<<COM1A1); // PWM, phase correct, 8 bit.
	OCR1A=eeprom_read_byte(0);


/* We fake an USB disconnect by pulling D+ and D- to 0 during reset. This is
 * necessary if we had a watchdog reset or brownout reset to notify the host
 * that it should re-enumerate the device. Otherwise the host's and device's
 * concept of the device-ID would be out of sync.
 */

    usbDeviceDisconnect();  /* enforce re-enumeration, do this while interrupts are disabled! */
    i = 0;
    while(--i){         /* fake USB disconnect for > 500 ms */
        wdt_reset();
        _delay_ms(2);
    }
    usbDeviceConnect();
    usbInit();
    sei();
    for(;;){    /* main event loop */
        wdt_reset();
        usbPoll();
    }
    return 0;
}

Damit ist die Firmware fertig. Hier noch ein Makefile für avr-gcc: (Chip: Mega8, Clock: 12MHz, Programmer: Ponyser)

# Name: Makefile
# Project: custom-class example
# Author: Christian Starkjohann
# Creation Date: 2008-04-07
# Tabsize: 4
# Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
# License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt)
# This Revision: $Id: Makefile 719 2009-03-16 18:51:56Z cs $

DEVICE  = attiny2313
F_CPU   = 16000000	# in Hz
DEFINES =

CFLAGS  = $(DEFINES) -Iusbdrv -I. -DDEBUG_LEVEL=0
OBJECTS = usbdrv/usbdrv.o usbdrv/usbdrvasm.o usbdrv/oddebug.o main.o

COMPILE = avr-gcc -Wall -Os -DF_CPU=$(F_CPU) $(CFLAGS) -mmcu=$(DEVICE)

SIZES_TMP = /tmp/sizetmp.txt

# symbolic targets:
help:
	@echo "This Makefile has no default rule. Use one of the following:"
	@echo "make clean ..... to delete objects and hex file"
	@echo "make sizes ..... compute code and RAM sizes for various options"
	@echo "make test ...... test with all features whether everything compiles"

sizes sizes.txt:
	rm -f $(SIZES_TMP) sizes.txt
	$(MAKE) null.elf
	avr-size null.elf | tail -1 | awk '{print "null", $$1+$$2, $$3+$$2}' >$(SIZES_TMP)
	$(MAKE) clean; $(MAKE) main.elf
	avr-size main.elf | tail -1 | awk '{print "Minimum_with_16_MHz", $$1+$$2, $$3+$$2}' >>$(SIZES_TMP)
	$(MAKE) clean; $(MAKE) main.elf F_CPU=12000000
	avr-size main.elf | tail -1 | awk '{print "Minimum_with_12_MHz", $$1+$$2, $$3+$$2}' >>$(SIZES_TMP)
	$(MAKE) clean; $(MAKE) main.elf F_CPU=12800000
	avr-size main.elf | tail -1 | awk '{print "Minimum_with_12_8_MHz", $$1+$$2, $$3+$$2}' >>$(SIZES_TMP)
	$(MAKE) clean; $(MAKE) main.elf F_CPU=15000000
	avr-size main.elf | tail -1 | awk '{print "Minimum_with_15_MHz", $$1+$$2, $$3+$$2}' >>$(SIZES_TMP)
	$(MAKE) clean; $(MAKE) main.elf F_CPU=16500000
	avr-size main.elf | tail -1 | awk '{print "Minimum_with_16_5_MHz", $$1+$$2, $$3+$$2}' >>$(SIZES_TMP)
	$(MAKE) clean; $(MAKE) main.elf F_CPU=18000000
	avr-size main.elf | tail -1 | awk '{print "Minimum_with_18_MHz+CRC", $$1+$$2, $$3+$$2}' >>$(SIZES_TMP)
	$(MAKE) clean; $(MAKE) main.elf F_CPU=20000000
	avr-size main.elf | tail -1 | awk '{print "Minimum_with_20_MHz", $$1+$$2, $$3+$$2}' >>$(SIZES_TMP)
	$(MAKE) clean; $(MAKE) main.elf DEFINES=-DUSB_CFG_IMPLEMENT_FN_WRITE=1
	avr-size main.elf | tail -1 | awk '{print "With_usbFunctionWrite", $$1+$$2, $$3+$$2}' >>$(SIZES_TMP)
	$(MAKE) clean; $(MAKE) main.elf DEFINES=-DUSB_CFG_IMPLEMENT_FN_READ=1
	avr-size main.elf | tail -1 | awk '{print "With_usbFunctionRead", $$1+$$2, $$3+$$2}' >>$(SIZES_TMP)
	$(MAKE) clean; $(MAKE) main.elf "DEFINES=-DUSB_CFG_IMPLEMENT_FN_READ=1 -DUSB_CFG_IMPLEMENT_FN_WRITE=1"
	avr-size main.elf | tail -1 | awk '{print "With_usbFunctionRead_and_Write", $$1+$$2, $$3+$$2}' >>$(SIZES_TMP)
	$(MAKE) clean; $(MAKE) main.elf "DEFINES=-DUSB_CFG_IMPLEMENT_FN_WRITEOUT=1"
	avr-size main.elf | tail -1 | awk '{print "With_usbFunctionWriteOut", $$1+$$2, $$3+$$2}' >>$(SIZES_TMP)
	$(MAKE) clean; $(MAKE) main.elf "DEFINES=-DUSB_CFG_HAVE_INTRIN_ENDPOINT=1"
	avr-size main.elf | tail -1 | awk '{print "With_Interrupt_In_Endpoint_1", $$1+$$2, $$3+$$2}' >>$(SIZES_TMP)
	$(MAKE) clean; $(MAKE) main.elf "DEFINES=-DUSB_CFG_IMPLEMENT_HALT=1 -DUSB_CFG_HAVE_INTRIN_ENDPOINT=1"
	avr-size main.elf | tail -1 | awk '{print "With_Interrupt_In_Endpoint_1_and_Halt", $$1+$$2, $$3+$$2}' >>$(SIZES_TMP)
	$(MAKE) clean; $(MAKE) main.elf "DEFINES=-DUSB_CFG_HAVE_INTRIN_ENDPOINT3=1"
	avr-size main.elf | tail -1 | awk '{print "With_Interrupt_In_Endpoint_1_and_3", $$1+$$2, $$3+$$2}' >>$(SIZES_TMP)
	$(MAKE) clean; $(MAKE) main.elf "DEFINES=-DUSE_DYNAMIC_DESCRIPTOR=1"
	avr-size main.elf | tail -1 | awk '{print "With_Dynamic_Descriptor", $$1+$$2, $$3+$$2}' >>$(SIZES_TMP)
	$(MAKE) clean; $(MAKE) main.elf "DEFINES=-DUSB_CFG_LONG_TRANSFERS=1"
	avr-size main.elf | tail -1 | awk '{print "With_Long_Transfers", $$1+$$2, $$3+$$2}' >>$(SIZES_TMP)
	cat $(SIZES_TMP) | awk 'BEGIN{printf("%39s %5s %5s %5s %5s\n"), "Variation", "Flash", "RAM", "+F", "+RAM"}\
		/^null/{nullRom=$$2; nullRam=$$3; next} \
		{rom=$$2-nullRom; ram=$$3-nullRam; if(!refRom){refRom=rom; refRam=ram} \
		printf("%39s %5d %5d %+5d %+5d\n", $$1, rom, ram, rom-refRom, ram-refRam)}' | tee sizes.txt
	rm $(SIZES_TMP)

test:
	for freq in 12000000 12800000 15000000 16000000 16500000 18000000 20000000; do \
		for opt in USB_COUNT_SOF USB_CFG_HAVE_INTRIN_ENDPOINT USB_CFG_HAVE_INTRIN_ENDPOINT3 USB_CFG_HAVE_MEASURE_FRAME_LENGTH USB_CFG_LONG_TRANSFERS; do \
			$(MAKE) clean; $(MAKE) main.elf F_CPU=$$freq "DEFINES=-D$$opt=1" || exit 1; \
			$(MAKE) clean; $(MAKE) main.elf F_CPU=$$freq "DEFINES=-D$$opt=1 -DDUSB_CFG_IMPLEMENT_FN_WRITEOUT=1" || exit 1; \
		done \
	done

# The following rule is used to check the compiler
devices: #exclude devices without RAM for stack and atmega603 for gcc 3
	excludes="at90s1200 attiny11 attiny12 attiny15 attiny28"; \
	for gccVersion in 3 4; do \
		avr-gcc-select $$gccVersion; \
		for device in `echo | avr-gcc -xc -mmcu=x - 2>&1 | egrep '^ *at[a-zA-Z0-9_-]+$$'`; do \
			if echo "$$excludes" | grep "$$device" >/dev/null; then continue; fi; \
			if [ "$$gccVersion" = 3 -a "$$device" = atmega603 ]; then continue; fi; \
			$(MAKE) clean; $(MAKE) null.elf DEVICE=$$device || exit 1; \
		done \
	done
	$(MAKE) clean
	avr-gcc-select 3
	@echo "+++ Device test succeeded!"

# rule for deleting dependent files (those which can be built by Make):
clean:
	rm -f *.hex *.lst *.map  *.elf *.o
	rm -rf usbdrv

# Generic rule for compiling C files:
.c.o:
	$(COMPILE) -c $< -o $@

# Generic rule for assembling Assembler source files:
.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.

# Generic rule for compiling C to assembler, used for debugging only.
.c.s:
	$(COMPILE) -S $< -o $@

# file targets:

# Since we don't want to ship the driver multipe times, we copy it into this project:
usbdrv:
	cp -r ../usbdrv .

main.elf: usbdrv $(OBJECTS)	# usbdrv dependency only needed because we copy it
	$(COMPILE) -o main.elf $(OBJECTS)

main_i.elf: usbdrv main.o usbdrv/usbdrvasm.o	# usbdrv dependency only needed because we copy it
	$(COMPILE) -o main_i.elf main.o usbdrv/usbdrvasm.o

null.elf: null.o
	$(COMPILE) -o null.elf null.o

D+ ist in diesem Beispiel mit PD2 verbunden, D- mit PD0. Die LED hängt an PB1. (Schaltplan noch in Arbeit.)

Die PC-Software zu diesem Beispiel wird bald verfügbar sein.

HID-Gerät (Tastatur/Maus/...)

[noch in Arbeit]

PC-Software bei nicht HID-Projekten (Treiber)

[noch in Arbeit]

Quellen

V-USB Homepage by Objective Development V-USB Wiki


LiFePO4 Speicher Test