Uživatelské nástroje

Nástroje pro tento web


atmelavr

Toto je starší verze dokumentu!


Atmel AVR

První kroky - programátor

  • programátorů je víc a mají i odlišný software. Některé programátory dělají většinu v hardware, některé to emulují v software. Jsou tedy mezi nimi rozdíly například v rychlosti
  • jako programátor lze prý využít zařízení s GPIO, třeba Raspberry PI
  • než si postavím/koupím svůj, tak jsem si zapůjčil USBasp se standardním ISP konektorem
  • zapojil jsem piny ISP konektoru na čip přes nepájivé pole
    • čip prý může být normálně v obvodu, stačí vyvést požadované piny
    • čip nepotřebuje (jestli vůbec smí) externí zdroj napájení, napájí se přímo z ISP konektoru
    • čip běží i s připojeným progrmátorem (pokud má zapojeny obě země). Při programování se akorát resetuje.
  • chtěl jsem postupovat podle návodu na ABClinux, ale článek je už zastaralý. Používají tam paralelní programátor, který je úplně jiný než ten USBasp
  • USBasp adaptér nemá podepsaný ovladač pro MS Windows, takže u třeba Win8 se musí nejprve vypnout kontrola podpisů u ovladačů. V Linuxu se to chytne hned, bez nějakých driverů
  • Doinstaloval jsem si akorát soft pro komunikaci s mikroprocesorem
avrdude
  • takhle se to chová, když najde USB programátor, který ale není spojen s cílovým mikroprocorem
avrdude -c usbasp -p atmega8

avrdude: warning: cannot set sck period. please check for usbasp firmware update.
avrdude: error: programm enable: target doesn't answer. 1 
avrdude: initialization failed, rc=-1
         Double check connections and try again, or use -F to override
         this check.
  • no a tohle je už správné chování
  • stačí nastavit typ programátoru (mám USBasp) a typ mikroprocesoru (mám ATmega8)
# avrdude -c usbasp -p atmega8

avrdude: warning: cannot set sck period. please check for usbasp firmware update.
avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.00s

avrdude: Device signature = 0x1e9307

avrdude: safemode: Fuses OK (E:FF, H:D9, L:E1)

avrdude done.  Thank you.

Kompilace

Atmel studio

  • funguje pouze pro Windows
  • obsahuje i simulátor (ale pouze pro některé mikroprocesory)
  • založit nový C executable projekt, vybrat ATmega8A procesor
  • nastavit v menu Tools - External Tools, založit nový:
Title: usbasp ATmega8
Command: B:\downloads\avr\avrdude.exe
Arguments: -c usbasp -p atmega8 -U flash:w:$(TargetDir)$(TargetName).hex:i 

[x] Use Output window

Linux

  • instalace kompilátoru a nástrojů
avr-libc
Makefile
MCU=atmega8a
AVRDUDEMCU=atmega8
CC=avr-gcc
OBJCOPY=avr-objcopy
PROJECT=pokus1
CFLAGS=-g -mmcu=$(MCU) -Wall -Wstrict-prototypes -Os -mcall-prologues
 
# add simulavr and gtkvawe
 
all: $(PROJECT).hex $(PROJECT).bin
 
$(PROJECT).hex : $(PROJECT).elf
        $(OBJCOPY) -R .eeprom -O ihex $(PROJECT).elf $(PROJECT).hex 
 
$(PROJECT).bin : $(PROJECT).elf
        $(OBJCOPY) -R .eeprom -O binary $(PROJECT).elf $(PROJECT).bin
 
$(PROJECT).elf : $(PROJECT).o
        $(CC) $(CFLAGS) -o $(PROJECT).elf -Wl,-Map,$(PROJECT).map $(PROJECT).o
 
$(PROJECT).o : $(PROJECT).c
        $(CC) $(CFLAGS) -c $(PROJECT).c
 
load: $(PROJECT).hex
        #uisp -dlpt=/dev/parport0 --erase --upload --verify if=$(PROJECT).hex -dprog=dapa  -v=3 --hash=12
        avrdude -c usbasp -p $(AVRDUDEMCU) -U flash:w:$(PROJECT).hex:i
 
simulate: $(PROJECT).bin
        simulavr -d $(AVRDUDEMCU) -P simulavr-disp $(PROJECT).bin
 
clean:
        rm -f *.o *.map *.elf *.hex *.bin

Ladění

  • Instalace nástrojů pro ladění
avr-gdb
simulavr
  • simulavr se umí spustit jako gdb server (parametr -g)
  • pak se dá k němu připojit pomocí
avr-gdb
(gdb) file pokus.elf
(gdb) target remote localhost:1212
(gdb) load
  • file - načítá debug symboly
  • target - připojení ke vzdálenému serveru přes tcp protokol
  • load - odeslání programu na server (ten který ladíme). Nemusí se provádět, pokud spustíme simulavr už nad binárkou
  • na vypsání io_registrů by mělo sloužit info io_registers. Mně to ale nějak nefungovalo, tak jsem na to šel jinak. Simulavr při spuštění vypsal něco jako:
devsupp.c:332: MESSAGE: attach: IO Reg 'PIND' at 0x0030: created
devsupp.c:316: MESSAGE: attach: IO Reg 'DDRD' at 0x0031: ref = 0x0030
devsupp.c:316: MESSAGE: attach: IO Reg 'PORTD' at 0x0032: ref = 0x0030
devsupp.c:332: MESSAGE: attach: IO Reg 'PINC' at 0x0033: created
devsupp.c:316: MESSAGE: attach: IO Reg 'DDRC' at 0x0034: ref = 0x0033
devsupp.c:316: MESSAGE: attach: IO Reg 'PORTC' at 0x0035: ref = 0x0033
devsupp.c:332: MESSAGE: attach: IO Reg 'PINB' at 0x0036: created
devsupp.c:316: MESSAGE: attach: IO Reg 'DDRB' at 0x0037: ref = 0x0036
devsupp.c:316: MESSAGE: attach: IO Reg 'PORTB' at 0x0038: ref = 0x0036
  • v GDB pak lze pak pro vypsání například PORTC registru provést jednoduše
(gdb) x/t 0x800035
0x800035:	00100000

Test rozsvěcování LED diody

  • cílem je nahrát jednoduchý program do mikroprocesoru a otestovat výstup
  • LED dioda bliká s frekvencí 1s
  • asi nefunguje při připojeném programátoru. Musí být odpojen ISC konektor (stačí vytáhnout programátor z USB) a tím pádem přivedeno externí napájení (možná stačí odpojit ten reset pin)
  • led dioda připojena na pin 14 (PB0) přes rezistor 220R
  • odpor 220R slouží k redukci napětí na LED diodě. Byl vypočítán pomocí ohmova zákonu. Pokud by se nepoužil, tak by LEDka asi umřela. LED má spotřebu cca 13mA. Odpor snižuje napětí na LED z 5V na zhruba 2.14V (220 * 0.013)
  • použitý zdroják
ledtest.c
1  #include <avr/io.h>
2  #include <util/delay.h>
3 
4  int main(void)
5  {
6    DDRB |= (1 << PINB0);
7    PORTB &= ~(1 << PINB0);
8	
9    while(1)
10   {
11     _delay_ms(1000);
12     PORTB ^= (1 << PINB0);	
13   }
14 }
  • DDRx je registr pro nastavování pinů portu x na vstup/výstup. Bit n registru odpovídá pinu n. Hodnota 1 odpovídá výstupu, hodnota 0 vstupu.
  • řádek 6 tedy říká: nastav pin 0 portu B (PB0) na výstup
  • port PB0 odpovídá podle nákresu v datasheetu právě pinu 14
  • PORTx registr má zvlášť význam pro vstupní a zvlášť pro výstupní piny. U výstupních pinu hodnota v registru odpovídá výstupní hodnotě. Tedy nastavení bitu n v registru PORTx na jedničku způsobí nastavení logické jedničky (napětí 5V) na výstupním pinu n portu x.
  • řádek 7 tedy říká: nastav pin 0 portu B na hodnotu 0 a všechny ostatní nech jak byly
  • řádek 12: nastav na pinu PB0 opačnou hodnotu

Test funkce Hall senzoru

  • Zapojení: pokud se na senzor díváme zepředu (zkosená hrana směrem k nám), tak úplně levá nožička je pro napájení (VCC), střed je zem (GND), pravá noha výstupní napětí.
  • Při prvním zapojování se mi podařilo otočit vstupní a výstupní napětí. Senzor to přežil, akorát při tomto zapojení nefungoval
  • Pracovní napětí je doporučené 4.5V - 6V. Já jsem použil 5V.
  • Bez magnetického pole to ukazovalo cca 2.5V (tj střed). Při přiblížení jednoho pólu magnetu (kulatý feritový magnet cca 20 x 5 mm) to ukazovalo až 3V. Při otočení magnetu napětí klesalo až na 2V.
  • U dostatečně silného magnetu to dává cca skutečně 0-2.5 a 2.5-5V
  • 0-5V je úplně ideální, protože ADC na ATmega8 digitalizuje přesně 0-5V (0-1024)
  • Pro snímání polohy joysticku lze použít
    • 2 vzájemné otočené magnety. Jeden bude způsobovat výchylku 0-2.5V, druhý 2.5-5V. Joystick bude krásně vycentrovaný, protože bez pole je tam přesně polovina. Čím dám magnety dál od sebe, tím větší bude null zone.
    • anebo stačí magnet jeden, ale je potřeba, aby měl póly daleko od sebe. Uprostřed pole je magnetizmus totiž nulový, a když se jde k pólům, tak se mění přesně podle potřeby. Testoval jsem to vytvořením válce spojením kulatých magnetů za sebe

Tlačítko

  • na digitální vstupy je možné přivést přímo signál
  • digitálních vstupů ale není moc, tak se často používá pro více tlačítek matice.

Jednoduché tlačítko

  • logicky si člověk řekne, že na pin prostě přivede napájení 5V přes tlačítko a je to. Problém je v tom, že pokud tlačítko není stisknuté, není pin připojen nikam. Na pinu se měří šum, mohou se zde objevit nečekané hodnoty zdroj
  • Používají se proto zapojení s pull-up a pull-down rezistory, které zajišťují, že je tam vždy zem nebo VCC
    • pull-down - přivedení VCC přes 4k7 odpor na zem
      • při stisku tlačítka je v registru PINx nastaven bit na 1
    • pull-up - přivedení přímo země a zapnutí interních pull-up rezistorů (zapínají se pres PORTx registr)
      • při stisku tlačítka je v registru PINx nastaven bit na 0
    • ve zkratce velikost pull-up odporu (R1) udává, jak velký proud poteče mezi VCC a GND (pokud stiskneme tlačítko)
    • R1=0 odpovídá nekonečnému proudu = zkrat
    • pin uvnitř MCU (mikroprocesor) bývá vnitřně připojen na GND přes velký odpor (R2), třeba 100k.
    • pokud tlačítko není stisknuto VCC je na GND připojen přes R1 + R2.
    • funguje to jako dělič napětí. Pokud R1=R2, tak na pinu (mezi odpory) napětí 1/2 VCC.
    • velikost R1 se proto doporučuje zhruba 1/10 R2 - napětí se vyhodnotí určitě úspěšně jako HIGH

Matice

  • všechny piny jsou input.
  • sloupce se postupně přepínají do stavu output, čímž dochází k jakoby vybrání sloupce
  • sloupce se musí přepínat zpět na input, jinak může dojít ke zkratu

Test ovládání LED diod tlačítkem

  • cílem je otestovat vstup i výstup: rozsvěcovat postupně tři LED diody v řadě a po stisknutí tlačítka změnit směr
  • LED diody připojeny přes odpor 220R na piny 14 (PB0), 13 (PD7), 12 (PD6)
  • tlačítko přivádí GND na pin 28 (PC5)
  • nejprve se nastavují piny na výstup/vstup
  • v cyklu
    • se vyčítá stav tlačítka. Při stisknutí se změní směr pomocí změny proměnné direction
    • se rozsvěcují diody podle proměnné active
led-switch.c
#include <avr/io.h>
#include <util/delay.h>
 
#define UP	0
#define DOWN	1
 
int main(void)
{
 
	/* Init LEDs */
	DDRD |= (1 << PIND6);
	DDRD |= (1 << PIND7);
	DDRB |= (1 << PINB0);
 
	/* Init push button */
	DDRC &= ~(1 << PINC5);
	PORTC |= (1 << PINC5);
 
	uint8_t active=0;
	uint8_t direction=DOWN;
	uint8_t btn_last=1;
	uint8_t counter=0;
 
    while(1)
    {
 
		if ((btn_last)&&(!(PINC & (1<<PINC5)))) {
			++direction;
			direction%=2; 
		}
		btn_last=(PINC & (1<<PINC5));
 
		if (counter++ > 10) {
 
			counter=0;
 
			if (direction==UP)
				++active;
			else 
				--active;
 
			switch(active) {
				case 2:
					PORTD &= ~(1 << PIND6);
					PORTD |= (1 << PIND7);
					PORTB &= ~(1 << PINB0);
					break;
				case 3: 
					PORTD |= (1 << PIND6);
					PORTD &= ~(1 << PIND7);
					PORTB &= ~(1 << PINB0);
					break;
				default: 
					PORTD &= ~(1 << PIND6);
					PORTD &= ~(1 << PIND7);
					PORTB |= (1 << PINB0);
					active=(direction==UP)?1:4;
			}
		}
 
		_delay_ms(50);
    }
}
  • Cílem bylo rozsvěcovat LED diody po 500 ms. Pokud se stav tlačítka vyčítal jen jednou za 500 ms, byla reakční doba nepříjemně pomalá. Tlačítko se muselo podržet delší dobu, jinak se stisk vůbec nezachytil.
  • Cyklus jsem zrychlil 10x. Zavedl jsem pomocnou proměnou counter, která stav LED diod mění až po dosažení hodnoty 10.
  • 50 ms pro čtení tlačítka bylo už dobré, ale asi by bylo lepší použít 20 ms.
  • U tlačítka se ukládá poslední stav, aby bylo možné detekovat stisknutí. Jako stisknutí se bere pouze přechod z logické hodnoty 1 (nestisknutý) na hodnotu 0 (stisknutý, přivedeno GND na pin).
  • Bez zavedení tohoto mechanismu by se směr při stisknutém tlačítku neustále měnil.
  • Změnu směru by bylo také možné po drobné úpravě vyvolat až uvolněním tlačítka
  • při psaní kódu jsem nejvíce bojoval s tím, jak zapsat kód efektivně a jednoduše. Čip má omezené zdroje.
  • po zkompilování v Atmel studiu se ukazuje spotřeba paměti výsledného kódu, pohybovala se okolo 150 B (1.5% max velikosti). Dá se podle toho zkoušet různé kombinace zápisu stejné funkce. Napsaný kód určitě není nejlepší možný, ale funguje :)
  • chyby v kódu se docela špatně ladí. Psaní kódu jsem si rozdělil na menší části, u kterých se lépe ověřovala správnost. Například pro zkoumání kódu pro tlačítko jsem napsal kód pouze na rozsvěcování ledky podle stavu tlačítka
  • chtělo by to nějaký pěkný simulátor :) V Atmel studiu pro ATmega8A asi žádný není.

Test rotačních enkodérů

  • rotační enkodér generuje při otáčení grayův kód.
  • na Internetu je mnoho nákresů, jak signál vypadá. Nejvíce se mi líbila interaktivní simulace na této stránce
  • jednotlivé stavy při otáčení lze popsat číslem 0-3 následovně
 PIN A:  __--__--__--__--    // weight 1
 PIN B:  ___--__--__--__--   // weight 2
 values: 00132013201320132
  • otáčka doprava je potom sekvence 0→1→3→2→0, otáčka doleva 0→2→3→1→0
  • jednotlivé stavy trvají různě dlouhou dobu. Závisí to například na rychlosti otáčení, konstrukci
  • hodnoty PINů A/B se musí vyčítat v dostatečně krátkém čase, jinak může být nějaký stav přeskočen
  • trvání stavu > 0 typicky nebývá delší než 2 ms
  • je třeba počítat s tím, že při rychlém vyčítání stavů bývá jeden stav detekován opakovaně
  • enkodéry jsou mechanické, během otáčení se negeneruje dokonalý signál bývá tam šum - skoky (bouncing) mezi logickou 0 a 1
  • odstraňováním se zabývá debouncing, který může proveden hardwarově (kondenzátor) nebo softwarově (zohledňování historie stavů)
  • pomocná makra
#define sbi(x, i) x |= (1 << i)
#define cbi(x, i) x &= ~(1 << i)
#define gbi(x, i) (!(x & (1 << i)))
  • definice použitých pinů (tlačítko enkodéru jsem nepoužíval)
#define ROT1_A_PORT     PORTC
#define ROT1_A_DDR      DDRC
#define ROT1_A_PIN      PINC
#define ROT1_A_P        PC3
#define ROT1_B_PORT     PORTC
#define ROT1_B_DDR      DDRC
#define ROT1_B_PIN      PINC
#define ROT1_B_P        PC4
  • inicializace vstupů (tlačítko enkodéru jsem nepoužíval)
/* Init Rotary Encoder */
cbi(ROT1_A_DDR, ROT1_A_P);
cbi(ROT1_B_DDR, ROT1_B_P);
cbi(ROT1_BTN_DDR, ROT1_BTN_P);

Algoritmus 1

  • můj vlastní, založen na myšlence sledování změn stavu
  • otáčení detekuje pomoci změny stavu 0→1 nebo 0→2
  • s HW debouncing asi nejlepší řešení (taky je moje) :D
uint8_t inline readRotary(void) {
  uint8_t rotval_now;
 
  rotval_now =  gbi(ROT1_A_PIN, ROT1_A_P);
  rotval_now += gbi(ROT1_B_PIN, ROT1_B_P)*2;
 
  if ((rotval_last==0)&&(rotval_now==1)) {
    rotval_last=rotval_now;
    return RIGHT;
  }
 
  if ((rotval_last==0)&&(rotval_now==2)) {
    rotval_last=rotval_now;
    return LEFT;
  }
 
  rotval_last=rotval_now;
  return NOCHNG;
}
  • otáčka detekována těsně před „lupnutím“
  • optimální frekvence vyčítání byla 1 ms
    • 0 ms - projevoval se hodně bouncing. Při plynulém pomalejším otáčení v jednom směru to přeskakovalo (detekovalo více otáček než se provedlo) a chybovalo (detekován opačný směr než směr otáčení)
    • 8 ms - dost dobrý, akorát extrémně rychlé otáčení už nebylo detekováno úplně 100%
    • 16 ms - pořád dobrý, extrémně rychlé otáčení už začínalo mít problémy s detekcí
    • 24+ ms - vynechává
  • zapojení kondenzátorů 2x 100nF mezi GND a PINA/PINB znatelně zlepšilo chování při 0 ms.
    • nedetekoval jsem žádné chyby ani přeskakování.
    • extrémně rychlé otáčky nedělaly problém. Asi nejlepší výsledky.
    • kondenzátory na 1 ms delay nemá asi žádný vliv
  • algoritmus jsem také zkoušel upravit na detekci změn jiných stavů
    • 1→3 R, 2→3 L
      • 0 ms - přeskakuje, chybuje, extra otáčky jsou horší než u 0→1 R, 0→2 L
      • 8 ms - občas vynechává (při extra pomalé otáčce), extra vysoké otáčky nic moc
    • 2→0 R. 1→0 L
      • 0 ms nic moc, 1 ms občas vynechá
      • výhoda ale byla, že otáčka byla detekována po „lupnutí“ :)
    • 1→3 R, 2→3 L nebo 0→1 R, 0→2 L
      • 0 ms - hodně divoké, hodně chybuje/přeskakuje
      • 8 ms - stále hodně divoké. On totiž generuje až dva kroky za jednu otáčku, ale jinak asi bez chyb
      • 16 ms - při otáčce občas detekoval 2 kroky a někdy jen 1 krok

Algoritmus 2

#define ROTA gbi(ROT1_A_PIN, ROT1_A_P)
#define ROTB gbi(ROT1_B_PIN, ROT1_B_P)

Varianta A

  • algoritmus obsahuje cyklus, který čeká na nastavení pinu na logickou nulu. Během čekání se neprovádí žádné instrukce.
  • pro vyčítání hodnot je opět nejlepší 1 ms
  • při 0 ms se občas přeskakuje, chybuje. Kondenzátor by to vyřešil.
uint8_t inline readRotary2(void) {
 
  if(ROTA & (!ROTB)){
    loop_until_bit_is_set(ROT1_A_PIN, ROT1_A_P);
    if (ROTB) return RIGHT;
  }
 
  if(ROTB & (!ROTA)){
    loop_until_bit_is_set(ROT1_B_PIN, ROT1_B_P);
    if (ROTA) return LEFT;
  }
 
  if (ROTA & ROTB){
    loop_until_bit_is_set(ROT1_A_PIN, ROT1_A_P);
    return (ROTB)?RIGHT:LEFT;
  }
 
  return NOCHNG;
}

Varianta B

  • úprava pro použití bez čekacího cyklu
  • při 0 ms to chybuje, přeskakuje
  • při 8 ms to občas malinečko chybuje (když jsou oba UP, tak vlastně detekuje vždy otáčení doleva)
uint8_t inline readRotary3(void) {
 
  if(ROTA & (!rotval_last)) rotval_last=1;
  if (ROTB & ROTA & (rotval_last)) {
    rotval_last=2;
    return LEFT;
  }
 
  if(ROTA & (!ROTB) & rotval_last) {
    rotval_last=2;    
    return RIGHT;
  }
 
  if ((!ROTA)&!(ROTB)&(rotval_last==2)) rotval_last=0;
 
  return NOCHNG;
}

Algoritmus 3

  • v algoritmu je implementován SW debouncing
  • reaguje uprostřed „lupnutí“, takže super
  • při 0 ms to reaguje asi nejlépe ze všech alg. Občas to sice přeskočí nebo chybuje, ale je to mnohem lepší než ostatní.
  • při 8 ms je to ale nic moc. Často vynechává, občas chybuje, rychlé otáčky to nedává vůbec
uint8_t inline readRotary5(void) {

  static uint8_t waitcount = 0;

  if ( gbi(ROT1_B_PIN, ROT1_B_P) ) { 	//we have a trigger signal

    if ( !gbi(ROT1_A_PIN, ROT1_A_P) ) {  // rotating left or right?
      if ( rotval_last != RIGHT ) 	//debounce for polling
	      waitcount++;
      rotval_last = RIGHT;
    }
    else {
      if ( rotval_last != LEFT )		//debounce for polling
	      waitcount++;
      rotval_last = LEFT;
    }

  }
  else {
    rotval_last = NOCHNG;
    waitcount = 0;
  }

  if ( waitcount > 1 ) {  //only give a L/R value after non-wait transition
    waitcount = 0;
    return rotval_last;
  }

  return NOCHNG;	  //ignore

}

USB HID

Test V-USB

Teorie

    • pro komunikaci s USB je potřeba minimálně frekvence 12 Mhz
    • na USB se používají napěťové úrovně 3.3V, přestože poskytované napájení je 5V
    • pro převod 5V na 3.3V se nejčastěji používají následující zapojení
      • snížení VCC pro mikroprocesor použitím stabilizátoru
        • čisté řešení, poskytuje stabilní napětí bez šumu
        • některé AVR nezvládají 12Mhz při 3.3V VCC (ATmega8a má operační napětí od 2.7V, tak nevím)
        • je potřeba low power stabilizátor, protože USB zařízení při idle musí celkově brát pouze 500uA. Pull-up odpor 1k5 bere 200uA, takže na stabilizátor zbývá jen 299uA
        • stabilizátor prý bývá dražší. V GM jsem našel pěkný stabilizátor HT7533-1 s odběrem Iq 10uA za 6,30kč
      • snížení VCC pro mikroprocesor sériovým zapojením dvou diod
        • oproti stabilizátoru nespotřebovává proud
        • napětí ale kolísá podle odběru obvodu, což působí problémy analogovým částem (například ADC)
      • snížení napětí na D+, D- pomocí zenerových diod 3V6
        • při signalizování 1 bere obvod výrazně vyšší proud než při 0, což může způsobit podivné chování
        • je potřeba zvolit low power diody a raději chování diody při 1/0 přeměřit (napětí při 1 = 3.3V, proud obvodu < 500uA)
    • USB je univerzální rozhraní pro různé typy zařízení.
    • Při implementaci USB zařízení se používají třídy (základ je stejný):
      • Custom Class - vyžaduje driver na hostu. Je ale nejjednodušší na implementaci (vlastní protokol)
      • Standard HID Class - nevyžaduje driver, pouze pro standardizované HID (myš, joystick, ..)
      • Custom HID Class - nevyžaduje driver, asi nějaké vlastní rozšíření pro HID
      • CDC class - modemy, virtuální COM port
      • další…
    • inicializace
      • zkontrolovat, že I/O piny pro USB jsou nastaveny na input s vypnutým pull-up rezistory (default stav po resetu)
      • usbDeviceDisconnect(), čekat 100ms, usbDeviceConnect() - pro jistotu, aby se host případně reinicializoval
      • usbInit() - inicializace driveru
      • sei() - zapne interrupts
    • následuje nekončený cyklus, ve kterém se každých 50 ms musí volat usbPoll()
    • mezi hostem a zařízením se posílají zprávy (na hosta control-in, na zařízení control-out). Zprávy mají strukturu usbRequest_t.
    • při přijetí zprávy se v kódu zavolá funkce usbFunctionSetup(uchar setupData[8]), která rozhodne, co se má provést za akci (například zaslat data ze zařízení, rozsvítit ledku, přijmout blok dat, …)
    • příjem zpráv ze zařízení
      • pokud jsou všechna data k dispozici, tak lze předat přímo v usbFunctionSetup
      • pokud se musí postupně vyčítat, tak se musí vytvořit funkce pro čtení dat usbFunctionRead
    • posílání dat na zařízení
      • pokud nám stačí data v kontrolní zprávě usbRequest_t (8B celkem), tak stačí v usbFunctionSetup
      • jinak musíme vytvořit usbFunctionWrite, která příjme payload stream
    • pokud se host uspí, USB zařízení by mělo přejít do suspend režimu. To musí zařídit přímo aplikace (V-USB neřeší). Pozná se to tak, že ustane aktivita na USB sběrnici. Více zde

Zprovoznění demo HID mouse

  • zapojení krystalu
    • krystal mezi XTAL1/2, potom přes 27pF na zem
    • změnit nastavení FUSE bitů podle kalkulátoru
    • vůbec netuším, jaký je rozdíl mezi Startup time: XX CK + YY ms. Asi jde jen o inicializaci? Fungují mi všechny :)
  • pomocí upraveného pokus1.c jsem otestoval funkčnost zapojení s krystalem (inspirováno Software side – preparing for V-USB)
  • nastavil jsem F_CPU na 1Mhz a ledka blikala cca 12x za jednu s
  • nastavil jsem F_CPU na 12Mhz a ledka blikala cca 1x za sekundu
  • zapojení zenerových diod a rezistoru 68R na D+/-, zapojení 1k5 na VCC (oni to mají na PD4, ale v textu pak píšou o VCC)
    • proč je tam ten záhadný 1k5 rezistor na D+?
      • The host includes 15 kΩ pull-down resistors on each data line. When no device is connected, this pulls both data lines low into the so-called „single-ended zero“ state (SE0 in the USB documentation), and indicates a reset or disconnected connection. A USB device pulls one of the data lines high with a 1.5 kΩ resistor. This overpowers one of the pull-down resistors in the host and leaves the data lines in an idle state called „J“. For USB 1.x, the choice of data line indicates of what signal rates the device is capable; full-bandwidth devices pull D+ high, while low-bandwidth devices pull D− high.
  • vůbec nevím, jak by to šlo otestovat, takže zkouška naostro pomocí návodu na rozjetí demo příkladu s myší http://www.workinprogress.ca/v-usb-tutorial-software-only-usb-for-mega-tiny/
  • z vusb složky jsem vykopíroval z examples obsah hid-mouse do pokus5
  • úprava usbconfig.h
    #define USB_CFG_DMINUS_BIT      2
    #define USB_CFG_DPLUS_BIT       3
  • úpravy v Makefile
    • úprava hlavičky
      DEVICE  = atmega8
      F_CPU   = 12000000      # in Hz
      FUSE_L  = 0xff
      FUSE_H  = 0xc9
    • oprava cesty k usbdrv
      usbdrv:
              cp -r ../../vusb-20121206/usbdrv
    • a pak už jen
      make hex
      sudo make program

Předělání HID mouse na gamepad s rotačním enkodérem

  • HID description
      • v obvodu doporučují použít vratnou tepelnou pojistku RXE025 (chrání proti zkratu napájení)
      • dále doporučují zapojit Reverse current protection diode, aby se externí napájení nevracelo do počítače
      • zenerky doporučují právě 500mW, menší (200mW) prý nejsou vhodné
      • ten 68R funguje prý pro snížení proudu mezi počítačem a zařízením a také jako zakončovací odpor (terminating resistor)
      • ten 1k5 pull-up rezistor se prý používá při připojení na 3.3V VCC. U 5V se prý používá 2k2
      • odpory se používají 1/4 Wají 1/4 W
      • The host and device talks over channels called „endpoints“, endpoints are identified by a number. There are some endpoints that a reserved for special use, while others can be configured to operate in different modes (interrupt, bulk, etc).
      • The host will always first use the „control endpoint“ (endpoint 0) first to request a description of the device
      • Then the configuration descriptor is requested, which also contains the number of endpoints available on the device. Each endpoint has its own descriptor as well.
  • Úpravy HID mouse
    • nahrál jsem tam joy deskriptor a report_t strukturu ze stránky výše
    • musel jsem změnit USB_CFG_INTERFACE_SUBCLASS na hodnotu 1 (to asi není potřeba) a opravit velikost USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH v usbconfig.h
    • přidal jsem vlastní knihovnu pro práci s LED a rotačními enkodéry (setLED, readRotary ..)
    • stav tlačítek se reportuje 1x za USB_CFG_INTR_POLL_INTERVAL
    • myšlenka je vyčítat hodnoty enkodéru hned a pamatovat si poslední směr až do reportování
      • pokud není spuštěn program, který by joystick vstupy odebíral, tak vůbec nenastává ten interrupt
      • po spuštění programu to zahlásilo stisk tlačítek, protože si to pamatoval
    • pokud se otáčí moc rychle, tak on vlastně hlásí pořád stisknuté tlačítko
    • vyřešil jsem to prokládáním. Vždy se jednou reportuje a jednou posílá prázdná hodnota tlačítek
    • prokládání ale zpomaluje reakční dobu, takže jsem začal zkoušet různé pool intervaly
      • původně tam bylo 100ms, minimum je prý 10ms
      • při 50ms to bylo v XP docela zpožděné, v XPL při rychlejším otáčení vynechávalo
      • při 20ms to ale už nereagovalo úplně dobře. Jakoby host systém nezachytil hodnotu
    • prokládání jsem nahradil pomocí podmíněného prokládání
      • stávalo se, že bylo potřeba nastavit button na ON, ale zrovna se reportovala prázdná hodnota tlačítek, takže se zbytečně čekalo na další interrupt
      • pokud je při interrupt směr enkodéru stejný jako předchozí, hlásí se vždy nula (vytváří pulz). Pokud jsou různé, tak se reportuje
    • zkracování interrupt intervalu
      • přes jstest –event jsem ověřil, že i při nejkratším intervalu (10ms) se pulzy tlačítka dostanou na hosta. Při max rychlosti otáčení zachyceno takových minimálně 6/10 pulzů. Pomalé otáčení zachyceno vždy všechno
        count=0
        jstest --event /dev/input/js1 |
        while read line; do
           echo $line | grep -q "number 1, value 1" && {
              count=$((count+1))
              echo $count
           }
           echo $line | grep -q "number 0, value 1" && {
              count=$((count-1))
              echo $count
           }
        done
      • ve Win XP, Win7, XPL se ale při 10ms pulzy nějak nezpracovávají. Jakoby nebyly. Podle mě je to skutečně tím, že aplikace nečte hodnoty dostatečně rychle. Že se prostě koukne na aktuální stav jednou za čas.
      • obecná optimální hodnota mi přijde 80ms. S tím nemá problém Windows ani XPL. To bohužel ale na rychlé otáčení nestačí a rotační enkodér postrádá smysl. Při max rychlosti otáčení zachyceno takových minimálně 2/10 pulzů. Lze asi cítit i mikro zpoždění při jedné otáčce
      • nejlepší hodnota pro XPL je 50ms. Při této hodnotě se už neztrácí žádné pomalé pulzy. Při max rychlosti to bývá minimálně cca 3/10. Od 80ms to ale žádný převratný rozdíl není, takže globálně je asi 80ms lepší
    • zavedení reportsleep
      • interrupt interval jsem dal na 10ms (používá to například i Logitech Extreme, viz lsusb -v)
      • pokud se zareportuje nějaká hodnota, nastavuje se sleep na 8. Jelikož je interval 10ms, dostávám cca 80ms
      • rozdíl mezi intervalem 80ms a 10×8 je v tom, že otáčka je reportována okamžitě :)
    • rotační enkodéry přes HID tedy fungují, ale tlačítka nejsou vhodná na reportování rychlých pulzů. Například rolovací kolečko u myši se reportuje jako relativní číselná hodnota (wheel). Není to button.
    • řešení by mohlo být vytvoření ovladače na straně hosta, který by pulzy převáděl rovnou na XPL dataref

TODO

  • záhada s axis
    • hodnoty axis jsem z počátku vyčítal z jstest aplikace
    • pak jsem to nahradil pomocí jstest-gtk, který navíc přehledně zobrazuje stav kalibrace (lze i upravit)
    • úpravy kalibrace se po odpojení joysticku ztratí
    • default kalibrace se evidentně nějak počítá z LOGICAL_MAXIMUM/MINIMUM
    • maximální zobrazená hodnota u joysticku v Linuxu může evidentně být 32768
    • pokud v HID deskriptoru nepoužiji vůbec LOGICAL_MAXIMUM/MINIMUM nebo nastavím hodnotu 0,
      • tak jstest ukazuje přesně zaslané hodnoty (raw hodnoty)
      • jako joystick se to ale úplně používat nedá, protože on nemá natavené hranice a střed (dead zone)
      • po kalibraci to je použitelné
    • pokud v HID deskriptoru použiji logical min/max
      • joystick v jstest začne vypisovat divné hodnoty. Pro některé bity se hodnota vůbec nezmění. Je to asi tím, že vypočtená hodnota přeteče zobrazovaný rozsah. Místo toho se zobrazí prostě buď minimum nebo maximum. Působí to, jako by se nic nedělo
      • kalibrace zobrazená v jstest-gtk je nějaká divná. Hodnota Min a Max u range neodpovídá log min/max.
      • i když kalibraci ručně opravím, tak axis nefungují stále dobře. Je to nějaký divný
      • u joysticku je rozsáhla dead zone, přestože u input je nastaveno, že nechci žádnou null position
  • nápady
    • zachytit HID deskriptor a zkontrolovat, že není třeba zmršenej :)
    • zachytit HID deskriptor z logitech joysticku… zajímalo by mě, co to přesně posílá (pro porovnání)
    • vybrečet se na fóru V-USB
  • podklady pro vybrečení (testy)
    • 1x 16bit osa, 16x tlačítko
      • zapsat hodnoty autokalibrace z GUI, z jcal, chování s autokalibrácí, chování při 0 kalibraci, chování ve winxp/w7 - zda celý range při továrním

^ log min/max ^ variable type ^ start + inc ^ alg notice ^ note ^

32k/32 int16_t -32k + 100 pak to přeteče a půjde od -32k
0/32k uint16_t 0 + 100 nejvyšší bit vždy nulový &7fff
-32k/0 int16_t -32k + 100 nejvyšší bit vždy jednička OR 0x8000
0/1023 uint16_t 0 + 10 nejvyšších 6bitů vždy nulových &3ff
-512/511 int16_t -512 + 10 když větší než 511, tak -512
0/64k uint16_t 0 + 100 pak přeteče a půjde od nula

Test ADC

Plány

  • vyzkoušet ADC převodník úvod, podrobnější
    • s hall senzorem
    • s potenciometrem
    • výstupy budu číst jako axis HID joysticku
  • sestrojit prototyp zařízení
    • 3x axis (levá brzda, pravá brzda, kormidlo)
    • 1x tlačítko (parkovací brzda)
    • 2x rotační enkodéry včetně tlačítka (trimování + reset)
  • druhý projekt :)
    • využít dataref library pro komunikaci s XPL
    • raspberry PI jako klient dataref pluginu
      • přes IC2/SPI bude komunikovat s druhým MCU
      • mohl by mít dokonce monitor na zobrazení přístrojů
    • vyzkoušet maticové zapojení tlačítek a enkodérů (MCU má málo pinů)
    • MCU bude ovládat
      • 10 rotary enkodérů (20 pinů)
      • 12 tlačítek/přepínačů?
      • 3x axis… throtle, natočení vrtule, směs?

Zdroje

atmelavr.1449606664.txt.gz · Poslední úprava: (upraveno mimo DokuWiki)

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki