STM32F4DISCOVERY: USB‑OTG i Labview
30.12.2013 | aktual.: 31.12.2013 14:25
Jestem posiadaczem STM32F4 discovery(Poprzedni wpis ) od przeszło ponad roku ale jak to w życiu bywa, czasu nigdy za wiele. Korzystając z chwili wolnego postanowiłem sprawdzić jak w łatwy sposób bez żadnych przejściówek połączyć płytkę z PC. Zamiast wykorzystać układ MAX232 i przejściówkę Rs232<=>USB postanowiłem uruchomić USB‑OTG (USB-OTG ) znajdujące się na płytce. Stmelectronics udostępnia w swoim demie technologicznym dla discovery m.in. przykład uruchomienia usb‑otg. Jest tam jednak sporo kodu który dotyczy innych peryferiów, po krótkich poszukiwaniach udało mi się znaleźć zredukowaną do obsługi interfejsu wersję dema(USB-otg Keil ). Nim jednak przejdziemy dalej zapoznajmy się z prostszym programem. Jego zadaniem jest miganie diodą LED(cztery diody na płytce), dodatkowo po przyciśnięciu przycisku( USER na płytce), częstotliwość migania maleje.
Miganie
W tym przykładzie będzie nieco inaczej niż w większości projektów z internetu. Będzie tu mniej SPL. Zazwyczaj z braku czasu używam SPL zamiast bezpośrednio rejestrów ale to jest dość trywialny program, dodatkowo jest on napisany dość prosto(i miejscami przez to nie optymalnie, w archiwum kod(wraz z projektem dla truestudio)). Zacznijmy od inicjacji wyjść
RCC->AHB1ENR |= RCC_AHB1Periph_GPIOD; //1 int x=0; for(;x<5;x++){ //2 int pin = 12+x;//3 uint32_t mode = GPIO_Mode_OUT << (pin * 2); //4 uint32_t speed = GPIO_Speed_100MHz << (pin * 2); uint32_t type = GPIO_OType_PP << pin; uint32_t pullup = GPIO_PuPd_NOPULL << (pin * 2); GPIOD->MODER |= mode; //5 //GPIOD->MODER |= (GPIO_MODER_MODER12_0 | GPIO_MODER_MODER13_0) GPIOD->OSPEEDR |= speed; GPIOD->OTYPER |= type; GPIOD->PUPDR |= pullup;}
- 1. Uruchamiam zegar dla portów
- 2. Pętla for, płytka wyposażona jest w 4 diody LED, dla każdego portu inicjacja ma miejsce w osobnym przebiegu, niemniej można to było by zrobić bez pętli w jednym ciągu poleceń( np. pod pkt. 5 wy komentowany kawałek dla typu wyjścia)
- 3. Diody LED są podłączone do pinów PD12 do PD15
- 4. Musimy ustalić sposób działania wejścia(wejście,wyjście,analog,alternatywny), jest to rejestr 32 bitowy, dlaczego zmienną pin mnożymy razy dwa? Ponieważ dwa bity określają tryb dlatego też mamy pin * 2(bit 24 i 25 dla PD12). Zauważmy że GPIO_Mode_OUT to tylko makro pod którym kryje się 0x01(wystarczy najechać w IDE na nazwę). Poniżej ustawiamy jeszcze prędkość portu, konfiguracja typu wyjścia, rezystor podciągający.
- 5. Prosta operacja bitowa na rejestrach, po prostu wpisujemy to co wcześniej zainicjowaliśmy. Jak wcześniej wspomniałem w taki sposób nadkładamy drogi, i poniżej pokazałem jak można dla rejestru MODER za jednym zamachem zainicjować dwa wyjścia. Inicjalizacja przycisku USER na płytce jest taka sama, także nie ma powodu jej powtarzać tutaj. Dobrze a jak migać diodami?
while (1) { if(((GPIOA->IDR & 0x1) != 0)){ //1 time+=1000; } GPIOD->BSRRL = (1 << pin_out); //2 for (index = 0; index < time; index++); GPIOD->BSRRH = (1 << pin_out); for (index = 0; index < time; index++); }
- 1. Jeśli wykryjesz przyciśnięcie przycisku USER zwiększ czas oczekiwania czyli zmniejsz częstotliwość migania.
- 2. Rejestr BSRR składa się z dwóch części dolnej i górnej. Jedna służy do resetowania bitu druga do ustawiania, w ten właśnie sposób będziemy gasić i zapalać diodę. BSRRL pozwala ustawić bit a BSRRH kasuje go.
Teraz mając ten prosty program możemy przystąpić do połączenia z komputerem PC.
Połączenie z PC
Naszym system „SCADA” będzie labview. Zadanie będzie takie samo jak poprzednio z tą różnicą że dodatkowo z labview będziemy mogli wybrać diodę do zaświecenia. Zacznijmy od programu w labview(Atmega z labview ), mamy cztery przyciski które mówią którą diodę na płytce mamy zapalić, parametry połączenia, okno do wypisywania danych z discovery(nic nie wysyłamy z procesora także to bonus) i przycisk stop.
Block diagram jest już bardziej skomplikowany ale wszystko po kolei.
W czarnej ramce znajdują się przyciski, przyciśnięcie którego kol wiek przycisku powoduje(trzy bramki 'lub' służą do aktywacji bloku CASE) uruchomienie bloku CASE i jednocześnie (patrz ramka czerwona) ustawienie wartości 1 na którymś z bloków bool to number, następnie każdy z tych bloków podłączony jest do mnożnika który zależnie od wybranego przycisku wystawia wartość od 0(gdy nie pobudzony) do 4. Taka prosta sztuczka pozwala ustalić który przycisk wciśnięto. Ponieważ wciśnięty jest zawsze tylko jeden przycisk to na sumatorze mamy poprawną wartość. Sygnał z kodem przycisku wędruje do (patrz blok żółty) bloczka wybierającego z mulitistringa(po prostu const string z czterema wierszami) określony wiersz. Następnie aktywowany jest blok przesyłu danych i informacja wędruje do STM32F4.
Po stronie discovery w pliku usbd_cdc_vdp.c(w tym pliku jest wiele ciekawych funkcji zachęcam do lektury) w linii 243 znajduje się procedura odbioru:
static uint16_t VCP_DataRx (uint8_t* Buf, uint32_t Len) { GPIOD->BSRRH = (1 << pin_out); // Wyleciało przez przypadek z programu trzeba dodać! uint32_t i; for (i = 0; i < Len; i++) // 1 { if (*(Buf + i) == 'a' || *(Buf + i) == 'A' ) // 2 { pin_out = 12; //3 } else if (*(Buf + i) == 'b' || *(Buf + i) == 'B' ) { pin_out = 13; }
- 1. Długość naszej wiadomości
- 2. Tu po prostu czytamy z bufora który jest wskaźnikiem stąd konstrukcja *(Buf+i) czyli odpowiednie przesuwanie się po nim w przód.
- 3. Zmiana pinu
Zmienna pin_out zadeklarowana jest w pliku variable.h jako extern aby mogła być widoczna także w main.c (gdzie znajduje się jej definicja). Zajrzyjmy tam na chwilę z poczucia obowiązku :)
volatile int index=0; volatile int time = 400000; setOut(); setIn(); //1 USBD_Init(&USB_OTG_dev, #ifdef USE_USB_OTG_HS USB_OTG_HS_CORE_ID, #else USB_OTG_FS_CORE_ID, #endif &USR_desc, &USBD_CDC_cb, &USR_cb); //2 while (1) { if(((GPIOA->IDR & 0x1) != 0)){ time+=1000; } GPIOD->BSRRL = (1 << pin_out); for (index = 0; index < time; index++); GPIOD->BSRRH = (1 << pin_out); for (index = 0; index < time; index++); }
- 1. Jak widać incjalizacja USB, nie będę się nad tym rozwodził ponieważ temat obejmuje sporo więcej niż ten krótki wpis w blogu.
- 2. W zasadzie nic się nie zmieniło, tylko teraz dodatkowo możemy z PC zmieniać diody. W pliku usbd_cdc_vdp.c znajduje się przerwanie dla przycisku USER niemniej jest ono wyłączone. Zrobiłem to aby nie wprowadzać zamętu. Dla niezorientowanych tutaj aby dodać do time + 1000 w każdym obiegu pętli sprawdzam czy nie przyciśnięto przycisku. Ma to spore wady ponieważ możliwe jest przegapienie przycisku(szczególnie w dużym programie) i zabieramy czas procesora niepotrzebnie. Gdybyśmy mieli podłączone przerwanie to przyciśnięcie przycisku powodowało by natychmiastowe przejście funkcji obsługi przerwania i wykonanie kodu w nim. Niemniej w przypadku ARM( i np. XMEGA też) mamy bardziej skomplikowaną obsługę przerwań niż w prostych 8-bitowcach jak AVR Mega.
Po słowie
Jak widać gdy ma się gotowy zestaw funkcji obsługi peryferiów zabawa z ARM nie jest taka trudna. Połączenie z komputerem PC także nie należy do najtrudniejszych ponieważ obsługujemy je po „staremu”(tak jak pokazałem poprzednio max232 <=> PC). Drobna moja uwaga względem środowisk IDE. Miałem już do czynienia z Keil uvision przy okazji 8051 w asm i jakoś nie mogę się przekonać, środowiska oparte o eclipse(jak true studio czy CCS) są bogatsze i bardziej przypadają mi do gustu. Ciekawy też jest Atmel studio opary o visual studio(ale dzięki temu nie ma go na linuksa). Mam nadzieję że Keil(dziś przecież należący do koncernu ARM) przejdzie na eclipse( Netbeans też było by ciekawe) platform tak jak zrobił to Texas instrument. Tyle mojego ględzenia :D