========= Ovládací panel pro PC aplikace ====== Vypracoval: Dan Raszka ---- ===== Zadání ===== Navrhněte zařízení sloužící ke zjednodušenému ovládání pro různé programy v PC. Zařízení realizujte pomocí vlastního návrhu DPS s procesorem řady STM32F1. Jako ovládací prvky zvolte tlačítka a rotační enkodér. K zobrazení stavu zařízení využijte RGB LED. Napájení uvažujte přes USB rozhraní. ===== Úvod ===== Základní myšlenkou tohoto projektu bylo vytvoření ovládacího zařízení, které by zjednodušilo používání PC s vícero zvukovými kartami. Vyhneme se tak otravnému přepojování kabeláže v případě, že si chceme něco poslechout na sluchátka. V případě, že k integrované zvukové kartě jsou připojeny reproduktory, k externí zvukové kartě jsou připojeny sluchátka a navíc využíváme například domácího kina s prostorovým zvukem přes HDMI konektor, je otavné i přepínání v systému Windows. U Windows 10 už je zakomponováno zjednodušení, díky kterému lze přepínat výstup bez nutnosti otevírání systémového nastavení. I tak je proces zdlouhavý, zejména když by šel nahradit pouhým stiskem tlačítka. Naštěstí existují programy umožňující nastavení klávesové zkratky pro cyklování mezi vybranými zvukovými výstupy. V tomto projektu bude využit konkrétně program AudioSwitch. V něm pouze nastavíme požadovanou klávesovou zkratku pro cyklování mezi výstupy. ===== Hardware ===== Protože předpokladem bylo vytvořit ovládací prvek, který bude nejenom praktický, ale bude taktéž dobře vypadat, byla zvolena cesta vytvoření hardwaru přímo na míru aplikaci. Jako řídící procesor byl vybrán STM32F103 zejména z důvodu dobré dostupnosti, nízké ceny a také malého ale lehce osaditelného pouzdra. Jako ovládací prvky byly zvoleny 4 tlačítka a rotační enkodér. Pro signalizaci byly zvoleny RGB LED diody podsvětlující knoflík rotačního enkodéru. Ty jsou řízeny pomocí trojice NMOS tranzistorů spínaných pomocí PWM z procesoru. {{ :2017:pc-app-ctrl:projekt_blok.png?500 |}} Dále byl implementován taktéž kapacitní dotykový senzor připojený na tělo rotačního enkodéru. Díky tomu lze signalizovat dotyk zařízení. Pro snížení napájecího napětí z 5V z USB sběrnice na 3,3V pro procesor byl zvolen miniaturní DC/DC měnič. Protože předem nebylo jasné jakou bude mít zařízení finální podobu, byla nejprve vytvořena DPS obsahující napájecí obvody, USB konektor, procesor STM32F103 a konektory umožňující připojení desky s ovládacími a zobrazovacími prvky. Na ní byly otestovány funkčnosti jednotlivých bloků a psán obslužný program. {{ :2017:pc-app-ctrl:proc_dps.png?500 |}} V mezičase se začalo pracovat na mechanickém provedení, kdy vzhledem k použití optického rotačního enkodéru byla zvolena koncepce typu stojánek. Zvoleno bylo provedení z fošny švestkového dřeva s hliníkovými krytkami tlačítek a knoflíkem enkodéru. Ty ale vzhledem k vytíženosti CNC frézky zatím vytvořeny nebyly a proto byly vytvořeny knoflíky tlačítek z březového dřeva pro otestování funkčnosti a vytvoření demonstračního videa. Tlačítka, LED diody, kapacitní senzor i rotační onkodér jsou osazeny na druhé DPS, která zároveň plní funkci montážního subpanelu a je zapuštěna ve vyfrézované kapse fošny. Jeden z montážních otvorů rotačního enkodéru navíc slouží jako elektroda kapacitního senzoru. {{ :2017:pc-app-ctrl:enc_dps.png?500 |}} ===== Software ===== ==== USB ==== Při tvorbě obslužného programu jsem vycházel Z PC cvičení. Díky HAL knihovnám nebylo těžké upravit vygenerovaný kód z CubeMX. Problém ovšem nastal u multimediální kláves typu Play/Pause atd. Ukázalo se, že i přesto, že byly definovány v deskriptoru klávesnice, tak nedocházelo k odezvě na straně PC. Proto byl upraven deskriptor tak, aby se zařízení hlásilo jako složené zařízení sestávající se z klávesnice, myši a takzvaného uživatelského zařízení. Díky tomu je možné zadefinovat různé multimediální klávesy. Deskriptor byl vygenerován pomocí programu HID Descriptor Tool. 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 0x09, 0x06, // USAGE (Keyboard) 0xa1, 0x01, // COLLECTION (Application) 0x85, 0x01, // REPORT ID (1) 0x05, 0x07, // USAGE_PAGE (Keyboard) 0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl) 0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI) 0x15, 0x00, // LOGICAL_MINIMUM (0) 0x25, 0x01, // LOGICAL_MAXIMUM (1) 0x75, 0x01, // REPORT_SIZE (1) 0x95, 0x08, // REPORT_COUNT (8) 0x81, 0x02, // INPUT (Data, Var, Abs) 0x75, 0x08, // REPORT_SIZE (8) 0x95, 0x01, // REPORT_COUNT (1) 0x81, 0x03, // INPUT (Cnst, Var, Abs) 0x75, 0x08, // REPORT_SIZE (8) 0x95, 0x06, // REPORT_COUNT (6) 0x15, 0x00, // LOGICAL_MINIMUM (0) 0x25, 0x65, // LOGICAL_MAXIMUM (101) 0x05, 0x07, // USAGE_PAGE (Keyboard) 0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated)) 0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application) 0x81, 0x00, // INPUT (Data, Ary, Abs) 0xc0, // END_COLLECTION 0x05, 0x01, // USAGE_PAIGE (Generic Desktop) 0x09, 0x02, // USAGE (Mouse) 0xa1, 0x01, // COLLECTION (Application) 0x09, 0x01, // USAGE (Pointer) 0xa1, 0x00, // COLLECTION (Physical) 0x85, 0x02, // REPORT_ID (2) 0x05, 0x09, // USAGE_PAGE (Button) 0x19, 0x01, // USAGE_MINIMUM (Button 1) 0x29, 0x03, // USAGE_MAXIMUM (Button 3) 0x15, 0x00, // LOGICAL_MINIMUM (0) 0x25, 0x01, // LOGICAL_MAXIMUM (1) 0x95, 0x03, // REPORT_SIZE (1) 0x75, 0x01, // REPORT_COUNT (3) 0x81, 0x02, // INPUT (Data,Var,Abs) 0x95, 0x01, // REPORT_COUNT (1) 0x75, 0x05, // REPORT_SIZE (5) 0x81, 0x03, // INPUT (Cnst, Var, Abs) 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 0x09, 0x30, // USAGE (X) 0x09, 0x31, // USAGE (Y) 0x09, 0x38, // USAGE (Wheel) 0x15, 0x81, // LOGICAL_MINIMUM (-127) 0x25, 0x7f, // LOGICAL_MAXIMUM (127) 0x95, 0x03, // REPORT_SIZE (8) 0x75, 0x08, // REPORT_COUNT (3) 0x81, 0x06, // INPUT (Data,Var, Rel) 0xc0, // END_COLLECTION 0xc0, // END_COLLECTION 0x05, 0x0c, // USAGE_PAGGE (Consumer Devices) 0x09, 0x01, // USAGE (Consumer Control) 0xa1, 0x01, // COLLECTION (Application) 0x85, 0x03, // REPORT_ID (3) 0x05, 0x0c, // USAGE_PAGE (Consumer Devices) 0x15, 0x00, // LOGICAL_MINIMUM (0) 0x25, 0x01, // LOGICAL_MAXIMUM (1) 0x75, 0x01, // REPORT_SIZE (1) 0x95, 0x07, // REPORT_COUNT (7) 0x09, 0xB5, // USAGE (Scan Next Track) 0x09, 0xB6, // USAGE (Scan Previoun Track) 0x09, 0xB7, // USAGE (Stop) 0x09, 0xCD, // USAGE (Pause) 0x09, 0xe2, // USAGE (Mute) 0x09, 0xe9, // USAGE (Volume Up) 0x09, 0xea, // USAGE (Volume Down) 0x81, 0x02, // INPUT (Data, Var, Abs) 0x95, 0x01, // REPORT_COUNT (1) 0x81, 0x01, // INPUT (Cnst, Ary, Abs) 0xc0 // END_COLLECTION Když už se zařízení hlásilo korektně v systémů, zbývalo pouze naprogramovat obslužné rutiny pro periferie procesoru obsluhující ovládací a signalizační prvky. ==== Rotační enkodér ==== Protože časovače procesoru podporují připojení rotačních enkodérů, je jejich použití značně zjednodušeno, protože se periferie sama stará o debouncing. Lze nastavit parametry jako filtraci vstupních pulzů, děličky pulzů a taky citlivost na náběžnou/sestupnou hranu. Důležité je nastavit časovač tak, aby reagoval na oba směry otáčení (byl v módu TI12). Použit byl časovač TIM4. Vzhledem k použitému optickému enkodéru s 400 pulzy na otáčku, byla zvolena vysoká předdělička z důvodu nevyužití tohoto rozlišení. htim4.Instance = TIM4; htim4.Init.Prescaler = 1; htim4.Init.CounterMode = TIM_COUNTERMODE_UP; htim4.Init.Period = 10000; htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; sConfig.EncoderMode = TIM_ENCODERMODE_TI12; sConfig.IC1Polarity = TIM_ICPOLARITY_FALLING; sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI; sConfig.IC1Prescaler = TIM_ICPSC_DIV2; sConfig.IC1Filter = 15; sConfig.IC2Polarity = TIM_ICPOLARITY_RISING; sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI; sConfig.IC2Prescaler = TIM_ICPSC_DIV2; sConfig.IC2Filter = 15; ==== RGB LED ==== Pro napájení RGB LED diod bylo využito napětí 5V s předřadnými rezistory. LED diody jsou zapojeny paralelně a jsou rozděleny do tří samostatných okruhů podle barvy. Pro řízení jsou využity 3 miniaturní MOSFET tranzistory s N kanálem. Ty jsou řízeny PWM výstupy procesoru. Konkrétně byl využit časovač TIM3 obsahující 4 nezávislé výstupní kanály. Využity byly samozřejmě pouze 3 pro RGB kanály. Předdělička časovače byla nastavena tak aby byla frekvence čítače 1 MHz. S periodou čítání nastavenou na 1000 dostaneme výslednou periodu PWM 1 kHz. Zařízení signalizuje navolený ovládací mód podle barvy. Byl zvolený efekt pomalého pulzování. Navíc při dotyku rotačního enkodéru (snímaném kapacitním snímačem) dojde k zvýšení intenzity a zrychlení efektu. Tohoto efektu bylo docíleno vyčítáním z předpřipravené tabulky. Protože intenzita svitu LED není lineární s dobou pulzu PWM a navíc nebylo chtěné aby docházelo k úplnému zhasnutí LED byla tabulka vygenerována pomocí polynomu vyššího řádu s přidaným offsetem. Zjištění optimálního nastavení bylo spíše experimentální metodou. Cílem bylo aby zařízení neoslňovalo i v temné místnosti - proto nebyl využit ani celý rozsah PWM. ==== Tlačítka a kapacitní snímač ==== Tlačítka jsou připojena pomocí pull-up rezistorů k napájecímu napětí 3,3 V. Při sepnutí je tedy generována sestupná hrana, na kterou reaguje přerušení procesoru. Ošetření zákmitů je řešeno tak, že při vyvolání přerušení je do proměnné odpovídající danému tlačítku zapsána zpožďovací konstanta, která je v hlavním cyklu postupně dekrementována. Při dosažení nulové hodnoty je znova načtená hodnota na daném pinu a pokud je rovna nule (stále zmáčknuté tlačítko) je provedena obsluha stisknutého tlačítka. Kapacitní snímač je řešen pomocí integrovaného obvodu AT42QT1011. Při dotyku je vyvoláno přerušení, které nastaví vlajku. Toho je využito v obsluze LED diod, kde dojde ke zvýšení intenzity svitu a zrychlení pulzování. Zde bylo zapotřebí pouze experimentálně zjistit hodnotu kondenzátoru, kterým byla ovlivněna citlivost tak, aby k detekci docházelo pouze při dotyku enkodéru. Funkčnost tohoto řešení lze vidět na konci demonstračního videa níže. ==== Hlavní obslužný program ==== V hlavní smyčce jsou 3 neblokující funkce: while (1) { EncoderTask(); LEDTask(); ButtonTask(); } Funkce EncoderTask se stará o vyhodnocení otáčení rotačního enkodéru. To je řešeno pouhým načtením čítacího registru časovače TIM4. Vyhodnocení je řešeno porovnáním s minulou hodnotou s nastaveným prahem citlivosti. Aby vyhodnocování nedocházelo příliš často, je použito následující konstrukce zaručující neblokující čekání: static uint32_t delay = 18000; // Zpozdeni mezi vyhodnocenim uint16_t i; // Aktualni hodnota citaciho registru static uint16_t li; // Minula hodnota citaciho registru delay--; if(delay == 0) { delay = 18000; i = TIM4->CNT ; if(li - i >= THRESHOLD) { switch(MODE) { // Vyhodnoceni provedeni funkce na zaklade modu zarizeni case MEDIA: case SPOTIFY: media(VOL_UP); break; case USER: wheel(1); break; } li = i; ... } Stejná konstrukce je použita také ve funkci LEDTask starající se o obsluhu LED diod a tedy plnění časovače. Funkce ButtonTask zajišťuje ošetření zákmitů při stisku tlačítka a provedení dané funkce při korektním stisku. Jak lze vidět z předcházejícího kódu, Zařízení pracuje ve 3 módech, které lze přepínat prvním tlačítkem, a které jsou signalizovány barvou podsvícení. Jedná se o módy Media, Spotify a User. V tabulce níže lze vidět funkce jednotlivých tlačítek: ^ Mód ^ Barva podsvětlení ^ Rotační enkodér ^ Tlačítko 1 ^ Tlačítko 2 ^ Tlačítko 3 ^ Tlačítko 4 ^ | Media | Modrá | Systémová hlasitost | Přepínání módů | Mute | Změna zvukové karty | Spuštění Spotify a přepnutí do tohoto módu | | Spotify| Zelenomodrá | ::: | ::: | ::: | Play/Pause | Další skladba | | User| Fialová | Kolečko myši | ::: | ::: | Spuštění Google Chrome | Spuštění kalkulačky | ==== Pomocné funkce ==== Jedná se především o podpůrné funkce zajišťující provedení zadané akce. Jde o odeslání specifického reportu pro jednotlivé akce po USB. void wheel(int8_t change); // Otočení kolečkem myši void media(uint8_t change); // Zmáčknutí multimediální klávesy void changeDevice(void); // Změna zvukového výstupu - klávesová zkratka void runSpotify(void); // Spuštění Spotify void runChrome(void); // Spuštění Google Chrome void runCalc(void); // Spuštění kalkulačky ===== Demonstrační video ===== {{youtube>mtFv-kO_Qkk?medium}} Zdrojová data: {{ :2017:pc-app-ctrl:xraszk04_mpoa_projekt.zip |}} ===== Závěr ===== Zařízení je jak po hardwarové tak i po softwarové stránce funkční. Zbývají pouze kosmetické dokončovací práce typu finální broušení a namoření dřeva a instalace vyfrézovaných hmatníků. Úpravou jistě projde stojánek protože díky vyšší hmotnosti hmatníků tlačítek a celkově vyššímu těžišti zařízení nebylo dosaženo dostatečné stability. Překvapením byl kapacitní dotykový senzor, který přes mou počáteční nedůvěru funguje skvěle a díky tomu jsou v budoucnu možné úpravy kódu typ poklepání na enkodér jako stisk tlačítka myši apod. Dobrou volbou byl taky použitý optický rotační enkodér (přestože jde o čínský výrobek), díky jehož mechanické konstrukci bylo možné se vyvarovat složitých mechanických úprav. Zařízení je nyní ve fázi dlouhodobého testování, kdy bude třeba vypozorovat způsob využití a nejčastější úkony a případně optimalizovat program pro lepší uživatelský komfort. ===== Zdroje ===== USB HID Development on STM32 - [[http://andybrown.me.uk/2016/01/09/f042usbhid/]] HID Consumer Device for volume control - [[http://www.microchip.com/forums/m618147.aspx]] HID Descriptor Tool - [[http://www.usb.org/developers/hidpage/]] AudioSwitch - [[https://github.com/sirWest/AudioSwitch]]