STM32 - Free RTOS dla dociekliwych.pdf
(
220 KB
)
Pobierz
untitled
Free RTOS dla dociekliwych
PODZESPOŁY
Free RTOS dla dociekliwych
W EP5/09 zostało przedstawione zagadnienie systemu operacyjnego
FreeRTOS w odniesieniu do mikrokontrolerów STM32. Wykorzystując
te informacje, w niniejszym artykule przedstawiono sposób tworzenia
nieco bardziej zaawansowanych aplikacji z użyciem systemu FreeRTOS
i mikrokontrolerów STM32. Pokazano m. in. jak nawiązać wymianę
danych pomiędzy uruchomionymi w systemie zadaniami oraz jak
zabezpieczyć zasoby mikrokontrolera przed nieuprawnionym dostępem.
odpowiada oddzielne zadanie, a więc sumie
w systemie są uruchomione trzy zadania:
vTa-
skLD1()
,
vTaskLD2()
,
vTaskLD3()
. Kod zadania
vTaskLD1()
został zamieszczony na
list. 1
, na-
tomiast funkcja obsługi przerwania dla lewego
położenia joysticka znajduje się na
list. 2
. Pozo-
stałe zadania i funkcje obsługi przerwań są róż-
nią się tylko sterowanymi lub monitorowany-
mi wyprowadzeniami. Pozycja lewa joysticka
jest podłączona do wyprowadzenia PE1, stąd
wykorzystana jest funkcja obsługi przerwania
EXTI1_IRQHandler()
. Główna funkcja progra-
mu
main()
, która została zamieszczona na
list.
3
, ma za zadanie skon
fi
gurować mikrokontroler
wraz z wszystkimi wykorzystywanymi peryfe-
riami do pracy – funkcja
prvSetupHardware()
.
Ponadto następuje tutaj uruchomienie zadań
oraz planisty, ten ostatni jest aktywowany przez
wywołanie funkcji
vTaskStartScheduler()
.
Każdy semafor jest tworzony przed wej-
ściem danego zadania do nieskończonej pętli.
Zmienna
xSemaphoreLD1
jest zadeklarowa-
na jako globalna, tak jak pozostałe semafory.
Sprawdzenie stanu semafora odbywa się wraz
z wywołaniem funkcji
xSemaphoreTake()
.
W nieco ściślejszym rozumowaniu wymieniona
funkcja próbuje „
wziąć
” semafor, co oznacza, że
jeśli jest on ustawiony to następuje wykonanie
dotychczas zablokowanej części zadania, a se-
mafor zostaje dezaktywowany (skasowany).
Jeśli funkcja
xSemaphoreTake()
zwróci war-
tość
pdTRUE
, to wtenczas następuje zmiana
stanu wyprowadzenia na przeciwny. Jako argu-
menty do funkcji należy przekazać nazwę se-
mafora oraz (pośrednio) czas, przez jaki zadanie
będzie oczekiwać, aż semafor stanie się aktyw-
ny. W omawianym przy-
padku wartość ta wynosi
0, ponieważ zadanie
zajmuje się tylko spraw-
dzaniem stanu semafora
i niczym więcej.
Głównym zadaniem
mikrokontrolera w funk-
cji obsługi przerwania
jest aktywowanie se-
mafora, dzięki czemu zadanie
będzie wiedziało, że należy
zmienić stan wyprowadzenia,
do którego podłączona jest dio-
da LED. Odpowiada za to funk-
cja
xSemaphoreGiveFromISR()
,
której należy przekazać dwa
argumenty, pierwszy to uchwyt
(nazwa) semafora. Drugi, prze-
kazywany przez referencję,
System operacyjny czasu rzeczywistego
FreeRTOS udostępnia programistom w sumie
pięć mechanizmów wykorzystywanych do ko-
munikacji pomiędzy zadaniami (lub przerwa-
niami i zadaniami) oraz do zabezpieczania za-
sobów mikrokontrolera. Są to: semafory binar-
ne, kolejki, semaforów licznikowe, muteksy,
muteksy rekurencyjne. Każdy przedstawiony
w artykule mechanizm został poparty stosow-
nym przykładem, przygotowanym dla płytki
ewaluacyjnej STM3210B-EVAL, która jest
wyposażona w mikrokontroler
STM32F103
.
Szczegółowych informacji na temat systemu
operacyjnego FreeRTOS należy szukać na jego
stronie internetowej
www.freertos.org
.
W praktyce systemów wbudowanych se-
mafory binarne są zazwyczaj wykorzystywane
od synchronizacji zadań lub zadania i prze-
rwania. Zagadnienie synchronizacji zadania
za pomocą przerwania przedstawiono na
rys.
1
. Dopóty, dopóki w systemie nie jest zareje-
strowane przerwanie, zadanie pozostaje w sta-
nie „
ZABLOKOWANE
”. W chwili, gdy wystąpi
przerwanie, a w funkcji jego obsługi nastąpi
uaktywnienie semafora, system operacyjny
wprowadzi zablokowane zadanie w stan „
GO-
TOWE DO WYKONANIA
”. Od tego momentu
zadanie oczekuje na zwolnienie zasobów, a je-
śli to nastąpi, to zacznie być realizowane.
Aplikacja działająca w oparciu o identycz-
ny mechanizm została omówiona poniżej, jej
zadaniem jest reagowanie na zmiany stanów
przycisków zapalaniem lub gaszeniem diod
LED na płytce ewaluacyjnej. Sterowane mają
być diody LD1, LD2, LD3, za pomocą położe-
nia joysticka odpowiednio: lewo, góra, prawo.
Schemat działania programu został przedsta-
wiony na
rys. 2
.
Do komunikacji pomiędzy funkcjami ob-
sługi przerwań i zadaniami wykorzystano trzy
semafory binarne, dla każdego zestawu, przy-
cisk i dioda, po jednym. Za stan każdej z diod
Semafory binarne
Semafory binarne, służą do sterowania
wykonywaniem zadań. Gdy semafor jest nie-
aktywny, to wykonywanie czynności (zadania)
jest zablokowane. Innymi słowy, aby zadanie,
którego działanie jest uzależnione od semafo-
ra, mogło się wykonać, to semafor musi zostać
aktywowany.
List. 1.
void vTaskLD1(void * pvParameters)
{
vSemaphoreCreateBinary(xSemaphoreLD1);
// Nieskonczona petla zadania
for(;;)
{
if(xSemaphoreTake(xSemaphoreLD1, 0) == pdTRUE)
{
vhToggleLD1();
}
}
}
mafora
Rys. 1.
List. 2.
void EXTI1_IRQHandler(void)
{
static portBASE_TYPE xHigherPriorityTaskWoken;
xHigherPriorityTaskWoken = pdFALSE;
if(EXTI_GetITStatus(EXTI_Line1) != RESET)
{
xSemaphoreGiveFromISR(xSemaphoreLD1,
&xHigherPriorityTaskWoken);
EXTI_ClearITPendingBit(EXTI_Line1);
}
}
Rys. 2.
ELEKTRONIKA PRAKTYCZNA 7/2009
109
STM32
PODZESPOŁY
List. 3.
int main( void )
{
// Konfi guracja sprzetu
prvSetupHardware();
// Uruchomienie zadan
vStartLDTasks( TASK_PRIORITY );
jakie można do niej wpisać, oraz rozmiar po-
jedynczego elementu. Obydwa parametry są
określane na etapie tworzenia (deklarowania)
kolejki.
Elementy w kolejce są umieszczane jako
kopie danych źródłowych, dzięki czemu ko-
munikujące się zadania nie mogą bezpośred-
nio uzyskać dostępu do pamięci, w której
znajdują się dane źródłowe. Mechanizm ko-
lejek w systemie FreeRTOS ma zaimplemen-
towaną obsługę wszystkich zagadnień zwią-
zanych z wzajemnymi wykluczeniami, zatem
programista nie musi już o to zabiegać.
Jeśli zaistnieje potrzeba kolejkowania da-
nych o większym rozmiarze, niż to przewi-
duje zadeklarowana kolejka, to wtedy można
użyć wskaźnika na element. W takich wypad-
kach należy się zawsze upewniać, kto (które
zadanie) jest właścicielem danej zmiennej,
oraz ostrożnie wykonywać operacje na otrzy-
manym adresie elementu tak, aby nie zdesta-
bilizować pracy całego systemu.
Niekiedy może się zdarzyć, że w kolejce
nie ma żadnych danych do odebrania przez
określone zadanie. W takich sytuacjach mocy
nabiera możliwość ustawienia czasu, a kon-
kretniej liczby taktów zegara systemu opera-
cyjnego, po jakim, jeśli żadne ważne dane nie
pojawią się w kolejce, zadanie przejdzie do
stanu „
ZABLOKOWANE
”. Sytuacja może być
również odwrotna: kolejka może być zapeł-
niona, wtenczas zadanie, które chce wpisać
dane do kolejki, oczekuje zadeklarowaną ilość
taktów zegara na zwolnienie się miejsca w ko-
lejce, gdy to nie nastąpi to również przechodzi
do stanu „
ZABLOKOWANE
”. Zasada działania
kolejki została omówiona w EP5/09.
Sposób użycia kolejek zostanie przed-
stawiony na przykładzie aplikacji, której za-
daniem będzie przetwarzanie A/C i pokazy-
wanie wyniku na gra
fi
cznym wyświetlaczu
LCD zamontowanym na płytce ewaluacyjnej
STM3210B-EVAL. Mierzone napięcie pocho-
dzi od potencjometru podłączonego do wy-
prowadzenia PC4.
Kon
fi
guracja ADC została dokładnie omó-
wiona w EP, zatem tutaj nie będziemy się tym
bliżej zajmować, podobnie jak w przypadku
aplikacji demonstrującej działanie semafo-
rów, również tutaj wszystkie czynności zwią-
zane z kon
fi
guracją są umieszczone w funkcji
prvSetupHardware()
.
W systemie są utworzone dwa zadania, na-
tomiast jedyna kolejka – xQueueLCD – została
utworzona jako zmienna globalna (uchwyt)
typu
xQueueHandle
. Na
list. 4
został zamiesz-
czony kod zadania
vTaskADC()
, które tworzy
za pomocą wywołania funkcji
xQueueCreate()
kolejkę. Następnie już w pętli nieskończonej
w 300 ms odstępach odczytuje wartość z prze-
twornika A/C, by w kolejnym kroku zapisać ją
do kolejki. Do tego celu użyta jest funkcja
xQu-
eueSend()
, której w argumentach należy podać
kolejno: nazwę kolejki, zmienną do wysłania,
oraz liczbę cykli systemowych, jakie będą od-
czekane w razie pełnej kolejki.
Drugie uruchomione w systemie zadanie
–
vTaskLCD()
– jest przedstawione na
list. 5
.
Pierwszą czynnością, jaką zadanie wykonuje,
jest inicjalizacja wyświetlacza LCD, po czym
w pętli nieskończonej, również co 300 ms, na-
stępuje odbieranie danych z kolejki i wyświe-
tlanie ich na LCD.
Efektem pracy aplikacji jest pokazywanie
wyniku przetwarzania, którym jest liczba z za-
kresu od 0 do 4096. Czas odświeżania został
tak dobrany, aby można było dobrze zaobser-
wować efekt kolejkowania danych. Zmieniając
dość szybko położenie potencjometru P1 wi-
dać, jak dopiero po chwili wynik osiąga swoją
właściwą wartość.
// Uruchomienie planisty
vTaskStartScheduler();
return 0;
}
argument jest zmienną, która otrzyma wartość
pdTRUE
, jeśli odblokowane przez semafor za-
danie będzie miało wyższy priorytet, niż aktu-
alnie wykonywane. Wszystkie nazwy funkcji
API systemu FreeRTOS, jakie są używane pod-
czas obsługi przerwań muszą kończyć się przy-
rostkiem „
ISR
”. Jest to niezbędne dla poprawnej
pracy mikrokontrolera.
Kolejki
Kolejki są głównym mechanizmem, jaki
jest wykorzystywany do wymiany informacji
pomiędzy zadaniami. Mogą być wykorzy-
stywane do przesyłania wiadomości między
zadaniami oraz pomiędzy przerwaniami i za-
daniami. W większości przypadków kolejki
są wykorzystywane jako bezpieczne bufory
FIFO.
Każda zde
fi
niowana w systemie kolejka
ma ustaloną długość, czyli liczbę elementów,
List. 4.
void vTaskADC(void * pvParameters)
{
u16 wynik_adc;
xQueueLCD = xQueueCreate(10, sizeof(u16));
// Nieskonczona petla zadania
for(;;)
{
vTaskDelay(300 / portTICK_RATE_MS); // Odczekanie 300 ms
wynik_adc = ADC_GetConversionValue(ADC1);
xQueueSend(xQueueLCD, (void *) &wynik_adc, (portTickType) 10);
}
}
List. 5.
void vTaskLCD(void * pvParameters)
{
u16 wynik;
char wynik_lcd[5];
STM3210B_LCD_Init();
LCD_Clear(Blue);
// Nieskonczona petla zadania
for(;;)
{
xQueueReceive(xQueueLCD, &wynik, (portTickType) 10);
vTaskDelay(300 / portTICK_RATE_MS); // Odczekanie 300 ms
sprintf(wynik_lcd, „%4d”, wynik);
LCD_DisplayStringLine(0, (u8*) wynik_lcd);
}
}
Semafory licznikowe
Semafory licznikowe (
Counting Semapho-
res
) są hybrydą zwykłej kolejki i semafora binar-
nego, zatem nie niosą ze sobą więcej informacji
poza aktualną wartością semafora. Mechanizm
semaforów licznikowych jest wykorzystywany
przede wszystkim w implementacji zadań, któ-
re wymagają zliczania zdarzeń.
Od strony praktycznej wygląda to tak, że
np. Zadanie A „daje” (
give
), czyli inkrementuje
semafor licznikowy, natomiast Zadanie B „za-
biera” (
take
) ten sam semafor – dekrementuje
go. Tym sposobem wartość semafora liczniko-
List. 6.
void vTaskSemphr(void * pvParameters)
{
u8 wynik_sem = 0;
xQueueLCD = xQueueCreate(10, sizeof(xSemaphoreHandle));
xSemaphoreCnt = xSemaphoreCreateCounting( 50, 0 );
// Nieskonczona petla zadania
for(;;)
{
vTaskDelay(1000 portTICK_RATE_MS); //Odczekanie 1 sek
xQueueSend(xQueueLCD, (void *) &wynik_sem, 0);
if(xSemaphoreTake(xSemaphoreCnt,0) == pdPASS)
wynik_sem = 1;
else
wynik_sem = 0;
}
}
110
ELEKTRONIKA PRAKTYCZNA 7/2009
110
Free RTOS dla dociekliwych
List. 7.
void vTask25PWM(void * pvParameters)
{
xSemaphoreMuteks = xSemaphoreCreateMutex();
// Nieskonczona petla zadania
for(;;)
{
if(xSemaphoreTake( xSemaphoreJoyUp, 0 ) == pdTRUE)
{
if(xSemaphoreTake(xSemaphoreMuteks, 0) == pdTRUE)
{
vhSetPWM();
vTaskDelay(500 / portTICK_RATE_MS);
xSemaphoreGive(xSemaphoreMuteks);
}
}
}
}
void vTask75PWM(void * pvParameters)
{
// Nieskonczona petla zadania
for(;;)
{
if(xSemaphoreTake( xSemaphoreJoyDown, 0 ) == pdTRUE)
{
if(xSemaphoreTake(xSemaphoreMuteks, 0) == pdTRUE)
{
vhSetPWM();
vTaskDelay(500 / portTICK_RATE_MS);
xSemaphoreGive(xSemaphoreMuteks);
}
}
}
}
w przypadku tego ostat-
niego. Dopiero podczas
tworzenia semafora
licznikowego należy
użyć innej funkcji API,
ściślej –
xSemaphore-
CreateCounting()
. Funk-
cji tej należy przekazać
dwa argumenty: pierw-
szy to maksymalna war-
tość semafora, a druga
wartość początkowa.
W omawianej aplika-
cji tworzenie semafora
odbywa się w zadaniu
vTaskSemphr()
, przed-
stawionym na
list. 6
.
W pętli nieskończonej
odczekuje 1 sekundę, po
czym sprawdza semafor
licznikowy
xSemapho-
reCnt
i wysyła do kolejki
jego wartość. Zawartość
kolejki jest odbierana przez za-
danie
vTaskLCD()
, które zajmuje
się obsługą wyświetlacza gra-
fi
cznego. Naciskając przycisk
użytkownika np. 10 razy widzi-
my na LCD, że przez czas około
10 sekund semafor będzie jesz-
cze ustawiony, a dopiero po tym czasie nastąpi
jego skasowanie.
lenie jakiegoś zasobu sprzętowego pomiędzy
kilka zadań.
Zasada działania
muteksów
została wyjaśnio-
na na
rys. 4
. Zadanie A, w chwili, gdy potrze-
buje dostępu do chronionego zasobu, sprawdza
stan
muteksa
, jeśli jest ustawiony, to wtenczas
wiadomo, że zasób jest wolny i można z niego
skorzystać. W trakcie wykorzystywania chronio-
nego zasobu przez Zadanie A
muteks
jest „
pusty
”.
Jeśli w takiej sytuacji Zadanie B podejmie próbę
skorzystania z danego zasobu to z racji „
pustego
”
muteksa
dostęp do zasobu nie będzie możliwy.
Dopiero po oddaniu
muteksa
przez Zadanie A,
Zadanie B może wykorzystać do swoich celów
chroniony zasób mikrokontrolera.
Przedstawimy teraz przykład aplikacji dzia-
łającej z wykorzystaniem
muteksów
do ochrony
zasobów. Załóżmy sytuację, w której dwa zada-
nia, jeśli zaistnieje taka potrzeba, zmieniają wy-
pełnienie generowanego przez mikrokontroler
sygnału PWM. Nowowprowadzony współczyn-
nik wypełnienia nie może się zmieniać przez
czas 500 ms, a jeśli zostanie zmieniony to może
to spowodować nieprawidłowe działanie całego
systemu. Aby zabezpieczyć timer TIM3 pracują-
cy w roli generatora PWM, przed nieuprawnio-
nym dostępem zostanie wykorzystany muteks.
W systemie uruchomione są dwa zadania:
vTask25PWM()
oraz
vTask75PWM()
. Wychylnie
joysticka na płytce ewaluacyjnej w górę powo-
duje odblokowanie pierwszego zadania i, jak
nietrudno się domyślić, zmianę współczynnika
wypełnienia sygnału PWM na 25%. Przeciwna
pozycja joysticka (w dół) odblokowuje drugie
zadania, a tym samym ustawia wypełnienie
na 75%. Generator PWM – timer TIM3 – po
pełnym przemapowaniu steruje wyprowadze-
niem PC6, a więc diodą LD1. Efektem działania
aplikacji jest zmiana intensywności świecenia
diody w takt zmian położenia joysticka. Obec-
ność w systemie pracującego
muteksa
można
zaobserwować próbę zmian położenia joysticka
z częstotliwością większą niż 1 Hz.
Muteks
chro-
niący zasób w postaci timera TIM3 nie pozwoli
na częstsze zmiany intensywności świecenia
diody LED niż co 500 ms.
Kod zadań
vTask25PWM()
i
vTask75PWM()
został zamieszczony na
list. 7
. Do synchroni-
zacji z wyprowadzeniami mikrokontrolera wy-
korzystano omówione już wcześniej semafory
binarne.
Muteks
jest tworzony w zadaniu
vTa-
sk25PWM()
za pomocą wywołania funkcji
xSe-
maphoreCreateMutex()
, jeszcze przed wejściem
zadania do pętli nieskończonej. Sprawdzenie
i próba zabrania
muteksa
odbywa się wraz z wy-
wołaniem znanej już funkcji
xSemaphoreTake()
.
Wywołanie to następuje tylko wtedy, kiedy se-
mafor
xSemaphoreJoyUp
jest aktywny, co jest
jednoznaczne z położeniem górnym joysticka.
Jeśli
muteks
jest dostępny to wtenczas wypełnie-
nie generowanego przebiegu zostanie ustawione
na 25%, w przeciwnym wypadku współczynnik
wypełnienia pozostanie niezmieniony.
Krzysztof Paprocki
paprocki.krzysztof@gmail.com
kolejki j
Rys. 3.
wego określa różnicę w liczbie wystąpień zda-
rzenia i jego przetworzeń.
Sposób implementacji semaforów liczniko-
wych prezentuje niżej omówiona przykładowa
aplikacja. Jej zadaniem jest utworzenie w sys-
temie semafora licznikowego, który ma być in-
krementowany przez przerwanie pochodzące
od wyprowadzenia PB9, do którego podłączo-
ny jest przycisk użytkownika. Proces dekre-
mentowania semafora należy do uruchomione-
go w systemie zadania
vTaskSemphr()
. Zadanie
w odstępach 1 sekundowych sprawdza stan
semafora i jeśli nie jest zerowy to go dekremen-
tuje. Dodatkowo aplikacja wykorzystuje omó-
wione poprzednio kolejki do pokazywania na
LCD wartości semafora. Schematycznie sposób
pracy mikrokontrolera w tym przykładzie ilu-
struje
rys. 3
.
Semafor licznikowy jest identycznym
typem zmiennej jak zwykły semafor binar-
ny, a więc jego deklaracja jest taka sama jak
Muteksy
Nazwa
„muteks”
jest określeniem angiel-
skim i raczej nieprzetłumaczalnym na język
polski. Słowo „
MUTEX”
powstało z połączenia
wyrazów „
mutual
” oraz „
exclusion
”. Można,
zatem mechanizm działania
muteksów
okre-
ślić jako „wzajemne wykluczanie”, które dość
dobrze oddaje istotę ich działania.
Muteksy
są
nieco podobne do binarnych semaforów, wy-
korzystują te same funkcje API, lecz zostały
wzbogacone o system priorytetów. Najistot-
niejsze jest to, że wykorzystanie semaforów
i
muteksów
jest zupełnie różne. O ile semafory,
jak już to zostało wyżej napisane, służą naj-
częściej do synchronizacji zadań, to
muteksy
zostały stworzone przede wszystkim z myślą
o implementacjach, w których występuje dzie-
Rys. 4.
ELEKTRONIKA PRAKTYCZNA 7/2009
111
Plik z chomika:
kaczor1000
Inne pliki z tego folderu:
Peczarski M. - Mikrokontrolery STM 32 w sieci Ethernet w przykładach.pdf
(12152 KB)
The Insaider's Guide To The STM32.pdf
(6627 KB)
STM32Butterfly.pdf
(2632 KB)
Reference manual.pdf
(8589 KB)
Alternatywna metoda programowania pamięci Flash mikrokontrolerów STM32(1).pdf
(2108 KB)
Inne foldery tego chomika:
8051
Arduino
Bascom
CNC
DipTrace
Zgłoś jeśli
naruszono regulamin