atmelavr
Toto je starší verze dokumentu!
Obsah
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
- gtkwave umí vizualizovat chování io
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)
- update: nejde o napětí ale o proud :) LED dioda i GPIO mají limit na max proud/napětí.
- 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
-
- 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
- podle webové stránky
- další pomocná makra
#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
-
- univerzální USB stack pro AVR mikroprocesory
- umožňuje vytvořit libovolné USB 1.1 zařízení
- licence umožňuje free použití, ale projekt musí být zveřejněn s kompletním návodem na použití
- další omezení je v nějakých vendor kódech?
- obsahuje hodně tutoriálů, například:
- AVR HID - USB HID využívající 6x ADC
- projekty bez knihovny (vesměs pouze assembler)
- HW implementace USB
-
- AT90USB162 - používá se i v Teensy
- viz Seznam všech AVR chipů (stačí koukat po těch s USB controllerem)
-
-
- Lightweight USB Framework for the USB-enabled Atmel AVR8, formerly known as MyUSB
- původně jsem myslel, že jde o konkurenci k V-USB, ale evidentně jde jen o framework pro AVR USB HW
- má hodně volnou licenci..
- The USB Generic HID Open Source Framework - tohle je asi priklad vyyuziti LUFA jako HID :)
- nějaký getting start blog http://cholla.mmto.org/computers/avr/usb/lufa/
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 :)
- ALE BACHA… crystal není ex. oscilátor :)
- po přepnutí na externí oscilátor zařízení jen s krystalem vůbec nefunguje, protože nemá hodinový impulz :)
- naštěstí to lze napravit
- já jsem to oživil pomocí Raspberry PI.
- stáhl jsem si a rozchodil wiringPI (raspigpio)
- vytáhl jsem si GND a GPIO0 z PI
- nejprve jsem k pinům připojil měřák a spustil blink.sh z examples. Ukazoval impulzy 3.3V
- potom jsem přivedl piny na XTAL1 a GND AVR MCU, Upravil blick.c na usleep(1) a AVR připojil k napájení (programátoru)
- nic to nedělalo, dokud jsem mezi piny nepřidal kondenzátor 27pF
- AVR MCU ožil, začal provádět program, začal komunikovat s programátorem, opravil jsem nastavení FUSE bitů
- 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
-
- používají destičku USnooBie, alternativní popis HID deskriptorů
- 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 I2C/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
-
- tohle me nadchlo, ale neni to primo pouzitelne.. spis motivace :)
-
- tohle jsem si pujcil
-
- obecne kecy, na uvod
-
- pekny navod, jak se zapojuje ISP konektor
-
- NOTE: I tried to use the programmer with the software 'avrdude' but it not worked (-c siprog). The avrdude failed sometimes in diferente steps. So I find out that I need to use the option '-i 100' (It causes a delay on the process). I found someone saying the same thing when he used an Athlon X4 (That's my case).
-
- zde se da koupit zapujceny USB AVR
-
- nejake zajimave priklady pouziti mikroprocesoru… napriklad programovani rotary encoderu :)
-
- rotary encodery od 15 kc :D
-
- mjoy16 nejaky user manual (nic ohledne fw)
-
- dalsi mjoy16 vcetne fw
-
- avr usb joy
-
- návod na key metrix 4×4 na ATmega, na stránce je i několik dalších pokročilých návodů
-
- návod na tlačítko a ledku
-
- návod na tlačítko a ledku (interní pull-up rezistor)
atmelavr.1450643316.txt.gz · Poslední úprava: (upraveno mimo DokuWiki)
