Prosty rejestrator sygnałów z interfejsem USB na STM32

 

W artykule pokazano sposób przygotowania aplikacji na STM32, która okresowo pobiera próbki sygnału na wejściu wewnętrznego przetwornika A/C, zapisuje je do bufora wewnętrznego, pakuje w ramki, które zostaną przesłane przez port USB do komputera. Współpracująca z urządzeniem-rejestratorem aplikacja na PC ma służyć do wizualizacji sygnału odtworzonego z pobranych próbek oraz powinna posiadać funkcje zmiany skali czasu i amplitudy sygnału.

Czasami zdarzają się sytuacje, w których pojawia się potrzeba podejrzenia sygnałów zmiennych i powstaje pytanie czym to zrobić? Obecnie na rynku jest dostępnych wiele specjalizowanych do tego celu urządzeń, od tanich klonów analizatorów USB do drogich i rozbudowanych oscyloskopów. Jednak gdy wymagania do takiego rejestratora nie są duże, a pod ręką znajduje się odpowiedni sprzęt to można samemu zbudować takie urządzenie, nawet na prostym mikrokontrolerze z przetwornikiem A/D.

W tym celu wybrano zestaw ZL31ARM, który jest oparty na mikrokontrolerze STM32F103RB posiadającym 12-bitowy przetwornik A/D, kontroler DMA, 16-bitowy timer oraz port USB. Niestety ta ostatnia funkcja nie została uwzględniona w zestawie i należy podłączyć kilka elementów na płytce stykowej.

Idea urządzenia jest dość prosta i wygląda następująco. Po skonfigurowaniu wszystkich potrzebnych układów peryferyjnych jest włączany timer 1, który przez pewien czas będzie wyzwalał przetwornik A/D1, a otrzymane próbki w postaci cyfrowej będą kopiowane kolejno do bufora w pamięci za pomocą kontrolera DMA. Po zapełnieniu takiego bufora timer 1 jest wyłączany, a co za tym idzie próbki nie będą pobierane przez przetwornik. W tej sytuacji urządzenie pozostaje w stanie bezczynności, aż do momentu kiedy zestaw skonwertowanych próbek zostanie w całości przesłany do aplikacji PC. Kiedy to nastąpi timer 1 jest ponownie załączany i wykonuje się kolejny cykl pomiarów.

Realizacja takiego rejestratora wiąże się z pewnymi utrudnieniami o których warto napisać. Po pierwsze aplikacja PC powinna pobierać komplet próbek i wyświetlać przebieg z częstotliwością, która jest odbierana jako płynna przez oko ludzkie, a z drugiej strony powinna ona być taka, aby urządzenie-rejestrator zdążyło wykonać pełny cykl pomiarów. Stąd też wybrano częstotliwość 20 Hz, co z punktu widzenia funkcjonalności daje okno 50 ms w którym powinien się zmieścić proces pomiarowy. Dla pewności w działaniu urządzenia warto przyjąć 25 ms jako maksymalny czas wykonania pomiarów.

Druga sprawa dotyczy czasu przetwarzania pojedynczej próbki, a w przypadku przetwornika A/D wybranego mikrokontrolera wynosi on co najmniej 1 us. Mniejszy czas próbkowania wiąże się z pogorszeniem pomiarów i mniejszą ich zgodnością z wartościami rzeczywistymi. Dlatego, gdy próbki będą pobierane w większej skali czasowej czas próbkowania przetwornika A/D będzie ustawiony na najwyższy jaki spełnia wymagania okna pomiarowego, a gdy skala czasowa będzie mała to czas próbkowania będzie minimalny, czyli 1-2 us.

Również zakres próbkowanych napięć jest ograniczony do 3,3 V, ale gdy ważnym jest tylko podejrzenie jak mniej więcej przebiega sygnał o napięciu wyższym, np. 10V to można zbudować najprostszy rezystorowy dzielnik napięcia, który na wyjściu pozwoli otrzymać zalecane napięcie do 3,3 V, ale co prawda taki przebieg sygnału będzie posiadał zakłócenia.

Pierwszym etapem w realizacji projektu było dodanie obsługi portu USB, aby umożliwić budowanemu układowi komunikację z aplikacją na komputerze. W tym celu zostało wykorzystane gotowe rozwiązanie w postaci biblioteki USB oraz gotowych projektów realizacji różnych klas urządzeń USB takich jak CDC i HID, które można znaleźć w archiwum „STM32F10x and STM32L1xx USB full-speed device library v3.4.0” [1]. Z pośród kilku takich projektów został wybrany projekt wirtualnego portu COM urządzenia klasy CDC ze względu na dwie charakterystyczne cechy. Po pierwsze urządzenie tej klasy umożliwia transmisję danych z większą prędkością i większym rozmiarem pakietów danych w porównaniu do klasy HID, a po drugie jest łatwiejsze w realizacji i dość popularne. Jednak występuje tutaj niewielkie utrudnienie, gdyż aby takie urządzenie działało poprawnie po podłączeniu do portu USB komputera niezbędnym jest napisanie właściwego sterownika i umieszczenie go w systemie operacyjnym – dotyczy to systemów Windows, natomiast w przypadku Linuxa problem ten nie występuje i urządzenie po podłączeniu jest gotowe do pracy.

Aplikacja PC była stworzona w środowisku Qt Creator w języku C++ i ważnym było dokonanie wyboru API umożliwiającego komunikację przez port USB komputera. Z myślą o zaprojektowaniu uniwersalnej aplikacji, która po skompilowaniu będzie działała zarówno w systemie Windows jak i w Linux wybrano rozwiązanie w postaci biblioteki libusb. W czasie tworzenia projektu dostępne były wersje biblioteki: 0.1 dla Windows oraz Linux i 1.0 tylko dla Linux [2]. Projekt oparto na bibliotece libusb-win32 w systemie Windows [3], ale w razie potrzeby przeportowanie aplikacji na Linux i ewentualnie na nowszą i lepiej dopracowaną wersję biblioteki 1.0 nie powinno sprawić wielu problemów.

 

Schemat elektryczny

Schemat elektryczny połączeń linii komunikacyjnych portu USB mikrokontrolera STM32 przedstawiono na poniższych rysunkach.

 

Rys. 1. Schemat podłączenia mikrokontrolera STM32 oraz diód

 

Rys. 2. Schemat podłączenia USB

 

Konfiguracja projektu

Gotowy projekt wirtualnego portu COM jest przeznaczony do wykorzystania jako interfejs między komputerem z portem USB, a urządzeniem posiadającym port USART, jednak na potrzeby tworzonego urządzenia-rejestratora zostanie on zmodyfikowany. Po otwarciu pliku projektu VirtualCOMPort.uvproj z katalogu Project\Virtual_COM_Port\MDK-ARM pobranej biblioteki USB została wybrana platforma STM3210B-EVAL posiadająca mikrokontroler STM32F103VB, który jest praktycznie podobny do tego jaki znajduje się w zestawie ZL31ARM. Stąd też w okienku Options for Target ‘STM3210B-EVAL’ wystarczy zmienić docelowy mikrokontroler na STM32F103RB. Praktycznie taki sam procesor sprawia, że zaprogramowanie nawet gotowego projektu wirtualnego portu COM z niewielką niezbędną pozwoli uzyskać gotowe do pracy urządzenie po podłączeniu do portu USB komputera. Dalej w zakładce Debug należy wybrać Use ST-Link (Deprecated Version) i to samo w zakładce Utilities, gdzie występuje pole Use Target Driver for Flash Programming. Przykładowe ścieżki do plików źródłowych pokazano na rysunku 3.

Rys. 3. Przykładowe ścieżki dostępu w „Include Paths”

Rys. 4. Struktura projektu

 

Pierwszym plikiem, który zostanie zmodyfikowany będzie platform_config.h (tutaj i w kolejnych plikach w celach przejrzystości projektu zostaną usunięte fragmenty kodów, które dotyczą innych platform lub nie są wykorzystywane jak np. funkcja USART). Powinien on zawierać następujące elementy:

Ostatnie 3 wiersze z dyrektywą #define opisują wyprowadzenie PA0, które będzie służyło do sterowania włączeniem/wyłączeniem urządzenia z szyny USB. W pliku hw_config.c znajduje się funkcja USB_Interrupts_Config(), w której jest wybierana grupa priorytetów i konfigurowane samo  przerwania od modułu USB – należy to uwzględnić przy konfigurowaniu przerwań aplikacji użytkowej:

Dalej ważnym będzie określenie jakie endpointy urządzenia USB są dostępne w projekcie i w razie potrzeby ich zmodyfikowanie i/lub dodanie nowych (można to zrobić czytając sam kod projektu lub wykorzystać program testlibusb-win dostępny w pobranej bibliotece libusb-win32 do obsługi portu USB z poziomu PC. Gotowy projekt wirtualnego portu COM umożliwia komunikację z wykorzystaniem jednego z 4 endpointów:

  • Endpoint 0 IN oraz OUT jest wykorzystywany w procesie enumeracji, gdy urządzenie jest podłączane do komputera i konfigurowane,
  • Endpoint 1 IN, który jest przypisany do interfejsu 1 i który posiada adres 0x81, służy do wysyłania do komputera 64-bajtowych paczek (transmisja typu bulk),
  • Endpoint 2 IN, przypisany do interfejsu 0 pod adresem 0x82 z maksymalny rozmiarem wysyłanych do komputera paczek 8-bajtów (transmisja typu interrupt),
  • Endpoint 3 OUT, podobnie jak EP1 jest przypisany do interfejsu 1, ale posiada adres 0x03 i służy do odbierania z komputera 64-bajtowych paczek (transmisja typu bulk).

Do odbierania komend z komputera zostanie wykorzystany EP3_OUT, a do wysyłania danych do komputera będzie otwarty i skonfigurowany EP3_IN w celu przedstawienia sposobu w jaki można to zrobić. Pozostałe włączone już endpointy nie będą wykorzystywane i mogą nie działać poprawnie przy próbie dostępu do nich z aplikacji PC.

W pliku usb_conf.h znajduje się poniższy fragment kodu:

Dla otwieranego EP3_IN należy jeszcze dodać następujący wiersz:

Powyższy adres wynika z tego, że EP3_OUT ma rozmiar 64 bajtów, a więc kończy się na adresie 0x150. Dalej w pliku znajduje się fragment kodu, w którym należy zakomentować te definicje endpointów które będą otwarte, czyli:

Teraz już można wykonać właściwe otwarcie EP3_IN dodając w funkcji Virtual_Com_Port_Reset() w pliku usb_prop.c następujący fragment:

Oraz dodać wpis do deskryptora konfiguracji w tablicy Virtual_Com_Port_ConfigDescriptor[] pliku usb_desc.c zaraz za deskryptorem EP3_OUT:

Również należy zmienić rozmiar tej tablicy w pliku usb_desc.h dodając 7 bajtów:

Ostatnią czynnością przy konfiguracji komunikacji po USB i zahaczającą o kolejny etap projektu jest dodanie/modyfikacja callback’ów w pliku usb_endp.c. Należy tutaj wiedzieć, że wywołanie tychże callback’ów następuje po transmisji, czyli po zapisaniu/odczytaniu do/z endpointa danych. W obu przypadkach na końcu każdej takiej funkcji są umieszczane wywołania procedur zapisu kolejnych danych do wysłania, ustawieniu ich rozmiaru oraz aktywowanie gotowości endpointa do pracy. Callback od EP3_OUT wygląda następująco:

Należy zauważyć, że wysyłanych jest 10 paczek po 64-bajty co wynika z rozmiaru bufora próbek, który jest 320-elementową tablicą typu unsigned short, czyli ma długość 640 bajtów. Natomiast callback od EP3_IN ma podobną strukturę wysyłania kolejnych paczek:

Utworzenie sterownika w systemie operacyjym

Po podłączeniu urządzenia-rejestratora do portu USB komputera należy włączyć aplikację „libusb-win32 Inf-Wizard”, w której powinna zostać wyświetlona informacja o nowym urządzeniu USB z portem wirtualnym COM. Dalej wystarczy postępować zgodnie z zaleceniami w efekcie czego zostanie utworzony plik .inf urządzenia USB gotowy do zapisu w systemie operacyjnym.

Czasami podczas pracy w środowisku uKeil wraz z urządzeniem mogą pojawić się problemy jak niemożliwość dostępu współpracującej aplikacji PC. Należy wtedy wyjść z trybu debuggingu w uKeil, oraz wyłączyć i ponownie podłączyć urządzenie do portu USB.

 

Konfiguracja bloków peryferyjnych

Przed konfiguracją poszczególnych peryferii należy włączyć sygnały zegarowe na nie podawane:

Następnie należy poinformować układ NVIC przy jakich zdarzeniach powinny być generowane przerwania:

Dalej następuje konfiguracja wyprowadzenia PA1 na które będzie podawany rejestrowany sygnał:

Kolejno należy skonfigurować układ podstawy czasu timera 1 oraz jego kanał 1 i należy pamiętać o tym, że timer ten jest podłączony do szyny APB2 która jest taktowana sygnałem 72 MHz. Wstępnie kanał 1 jest skonfigurowany do odliczania 62,5 us, czyli sygnał będzie próbkowany z częstotliwością 16 kHz.

Następnie jest konfigurowany i włączany kontroler DMA:

Ostatnią czynnością jest skonfigurowanie kanału 1 przetwornika A/D:

Funkcja main() wygląda następująco:

Organizacja pomiarów

Sprawa jest dość prosta, a najważniejszy kod tej części programu znajduje się w pliku stm32_it.c i wygląda następująco:

W funkcji obsługi przerwania od kanału 1 timera 1 jest inicjowany kolejny pomiar, czyli ten który zostanie wykonany przy następnym wyzwoleniu oraz jest ustawiana następna wartość do której ma liczyć timer 1 po czym wygeneruje przerwanie. Gdy zostanie wykonany cykl pomiarowy liczący 320 próbek kontroler DMA wygeneruje zdarzenie, a w konsekwencji zostanie wywołana funkcja obsługi przerwania DM1_Channel1_IRQHandler(), w której timer 1 zostanie wyłączony i ustawiona będzie flaga gotowości danych do wysłania.

 

Przykładowe przebiegi rejestrowane przez urządzenie

Po przeprowadzeniu kilku testów można stwierdzić, że urządzenie-rejestrator dość dobrze sprawuje się z sygnałami do 20 kHz. Powyżej tej granicy obraz na oscylogramie staje się mniej czytelny, a nawet przekłamany. Tak jak wcześniej wspomniano w przypadku sygnałów do 3.3V można je podawać bezpośrednio na wejście A/C, jednak przy wyższych napięciach należy stosować odpowiednie konwertory, np. najprostszy dzielnik rezystorowy tylko należy pamiętać, żeby odpowiednio przeliczyć wskazywaną wartość.

Rys. 5. Sygnał prostokątny, F=19,2 kHz

Rys. 6. Sygnał prostokątny, F=1920 Hz

Rys. 7. Napięcie na rezystorze w układzie różniczkującym RC

Jan Szemiet

Materiały pomocnicze

[1] http://www.st.com/internet/com/SOFTWARE_RESOURCES/SW_COMPONENT/FIRMWARE/stm32_usb-fs-device_lib.zip

[2] http://www.libusb.org/

[3] http://sourceforge.net/projects/libusb-win32/

[4] http://www.beyondlogic.org/usbnutshell/

 

Do pobrania

Autor: