Instrukcja dla debili Lab5
Opracował: Maciej CZERWIŃSKI
Celem tej instrukcji jest pozwolić Ci zaliczyć na spokojnie przedmiot AKiSO. Są to już ostatnie zajęcia jakie będziemy razem realizować. Mam nadzieję że wyniosłeś z całego przedmiotu choć odrobinę więcej niż tylko ocenę. Dobra bierzmy się do pracy.
Zadanie zostały wykonane przeze mnie i działały w sposób jaki zinterpretowałem tą instrukcje przekazaną do laborki. Jeśli nie przygotujesz się teoretycznie do tych zajęć słabo widzę osiągnięcie sensownego wyniku w trakcie zajęć.
Całe informacje odnośnie systemy FreeRTOS możemy wziąć z tych miejsc:
-http://www.freertos.org/a00106.html – tutaj mamy wszystko dotyczące wszystkich funkcji API dla RTOS-a. Możemy sobie otworzyć każdą dowolną funkcję gdzie będziemy mieli opisane wszystkie jej właściwości oraz parametry potrzebne do prawidłowego działania
-http://ep.com.pl/files/2434.pdf
-http://ep.com.pl/files/2562.pdf – to są 2 artykuły stworzone przez człowieka który napisał książkę o STM-ach, dosyć długie ale z fajnym i zrozumiałym opisem całości
Zagadnienia do wejściówki:
-Zadanie- są wykorzystywane standardowo w systemach operacyjnych czasu rzeczywistego, są to niezależne od siebie procesy wyposażone we własne konteksty- z perspektywy takiego zadania wszystkie rejestry i stos mikrokontrolera należą tylko do niego. O tym które zadanie jest wykonywane w danym momencie decyduje algorytm szeregowania.
-Semafor – chroniona zmienna lub abstrakcyjny typ danych, który stanowi klasyczną metodę kontroli dostępu przez wiele procesów do wspólnego zasobu w środowisku programowania równoległego.
-Kolejka – liniowa struktura danych, w której nowe dane dopisywane są na końcu kolejki, a z początku kolejki pobierane są dane do dalszego przetwarzania (bufor typu FIFO, First In, First Out; pierwszy na wejściu, pierwszy na wyjściu).
Jak zwykle trzeba będzie zacząć od utworzenia na samym wstępie projektu. Tworzenie projektu:
Zgodnie z instrukcją lab5 będziemy korzystać jak zwykle z pliku evbLib. Dodatkowo mamy do dołączenia całkiem sporo plików dodatkowych.
W keilu wybieramy Project> new uvision project
Wybieramy mikroprocesor ten co zawsze czyli stm32f103vb
Ok>ok
Tworzymy main, File>New>Save>”main.c”
Wciskamy 3 klocki kolorowe
Dodajemy evbLib (ten większy plik-lib)
Dodajemy plik main.c
Dodajemy pozostałe potrzebne biblioteki:
W ustawieniach (różdżka) wybieramy w zakładce Debug zaznaczamy programator ST-Link Debugger, później Settings>Flash Download > i zaznaczamy opcje reset and run
W C/C++ dodajemy w paths ścieżki do:
Robimy dopisek w polu Define : STM32F10X_MD, USE_STDPERIPH_DRIVER
Dodajemy w main na samej górze dopisek dołączający biblioteki
#include "evbLib.h"
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h> // do uzycia funkcji sprintf
#include "semphr.h" // do obslugi semafora
Dodajemy pusty szkielet programu (main i while i patrzymy czy się skompiluje i wgra na procesor)
Jeśli tak to projekt gotowy!
Komentarzu wymaga dodanie do bibliotek heap_2.c zamiast zgodnie z instrukcją heap_3.c. Sam Pan Sawicki skomentował to w ten sposób:
Instrukcje pisałem z myślą o projekcie samodzielnym ale potem zdecydowałem się dołączyć do tego skompilowaną bibliotekę żeby nie zawracać Wam głowy konfigurowaniem LCD czy diodek a skupić się na samym mechanizmie działania RTOS. startup w kompilowany do tych bibliotek ma ustawioną bardzo mała stertę. Dlatego model 3 który korzysta z malloc implementowanego przez kompilator spowoduje że przy dwóch zadaniach RTOS sie nie uruchomi bo nie bedzie już pamięci. Model heap2 sam implementuje funkcje alokacji pamięci i ma jej do dyspozycji tyle ile ustawiono we freeRTOS_config.h.
Dobra przystępujemy do wykonywania zadań:
1 zad
void task1(void *param) //stworzenie zadania 1
{
while(1)
{
ledToggle(1);
vTaskDelay(100); // dzieki zastosowanie tej funkcji nie mamy problemu z wykonywaniem funkcji o jednakowym priorytecie w tym samym momencie
}
}
void task2(void *param) //stworzenie zadania 2
{
while(1)
{
ledToggle(2);
vTaskDelay(500);
}
}
int main(void) //stworzenie programu glownego
{
xTaskCreate(task1,"Zadanie 1",configMINIMAL_STACK_SIZE,NULL,1,NULL); //tworzenie zadan z jednakowym priorytetem i rozmiarem stosu
xTaskCreate(task2,"Zadanie 2",configMINIMAL_STACK_SIZE,NULL,1 ,NULL);
vTaskStartScheduler(); // uruchomienie planisty
return 0;
}
2 zad
void task1(void *pvParameters)
{
while(1)
{
if(( int ) pvParameters ==1) //pojebane ale dziala
{
ledToggle(1);
}
if(( int ) pvParameters== 2)
{
ledToggle(2);
}
if(( int ) pvParameters== 3)
{
ledToggle(3);
}
if(( int ) pvParameters==4)
{
ledToggle(4);
}
if(( int ) pvParameters==5)
{
ledToggle(5);
}
if(( int ) pvParameters==6)
{
ledToggle(6);
}
if(( int ) pvParameters==7)
{
ledToggle(7);
}
if(( int ) pvParameters==8)
{
ledToggle(8);
}
vTaskDelay(200);
}
}
int main(void) //stworzenie programu glownego
{
int i;
char nazwa[10];
for(i=0;i<8;i++)
{
sprintf(nazwa,"Zadanie %i",i);
xTaskCreate(&task1, nazwa,configMINIMAL_STACK_SIZE, (void *) (i+1),1,NULL);
}
vTaskStartScheduler(); // uruchomienie planisty
return 0; }
Komentarzu wymaga tutaj spora ilość if-ów w tasku. Niestety nie udało mi się opracować pobierania innego typu zmiennych niż typu int więc żeby dało radę działać z tym co mam zastosowałem ify do tego. Mało eleganckie a wręcz głupie i nielogiczne ale działa.
3 zad
Wer. 1 ze zmienną globalną
int zmienna_globalna=1;
void czyszczenie_lcd()
{
lcdGoTo(0,0);
lcdWrite(" ");
lcdGoTo(0,0);
}
void task1(void *param)
{
TickType_t xLastWakeTime;
const TickType_t xFrequency = 1000;
while(1)
{
if(zmienna_globalna==1)
{
lcdWrite("kbzddm");
zmienna_globalna++;
}
if(zmienna_globalna==3)
{
czyszczenie_lcd();
xLastWakeTime = xTaskGetTickCount();
vTaskDelayUntil(&xLastWakeTime, xFrequency);
zmienna_globalna=1;
}
}
}
void task2(void *param) //stworzenie zadania 2
{
TickType_t xLastWakeTime;
const TickType_t xFrequency = 1000;
while(1)
{
if(zmienna_globalna==2)
{
xLastWakeTime = xTaskGetTickCount();
lcdGoTo(1,0);
lcdWrite("serio");
vTaskDelayUntil(&xLastWakeTime, xFrequency);
zmienna_globalna++;
}
}
}
int main(void) //stworzenie programu glownego
{
xTaskCreate(task1,"Zadanie 1",configMINIMAL_STACK_SIZE,NULL,1,NULL); //tworzenie zadan z jednakowym priorytetem i rozmiarem stosu
xTaskCreate(task2,"Zadanie 2",configMINIMAL_STACK_SIZE,NULL,1 ,NULL);
vTaskStartScheduler(); // uruchomienie planisty
return 0;
}
3 zad
Wer. 2 z semaforem
xSemaphoreHandle semafor;
void czyszczenie_lcd()
{
lcdGoTo(0,0);
lcdWrite(" ");
lcdGoTo(0,0);
}
void task1(void *param)
{
TickType_t xLastWakeTime;
const TickType_t xFrequency = 2000;
while(1)
{
if(xSemaphoreTake(semafor, 1000 ))
{
xLastWakeTime = xTaskGetTickCount();
lcdWrite("kbzddm");
xSemaphoreGive(semafor);
vTaskDelayUntil(&xLastWakeTime, xFrequency);
xSemaphoreTake(semafor, 1000);
czyszczenie_lcd();
xLastWakeTime = xTaskGetTickCount();
vTaskDelayUntil(&xLastWakeTime, xFrequency);
xSemaphoreGive(semafor);
}
}
}
void task2(void *param) //stworzenie zadania 2
{
TickType_t xLastWakeTime;
const TickType_t xFrequency = 1000;
vTaskDelay(1);
while(1)
{
if(xSemaphoreTake(semafor, 1000 ))
{
lcdGoTo(1,0);
lcdWrite("kbzddm2");
xSemaphoreGive(semafor);
}
}
}
int main(void) //stworzenie programu glownego
{
semafor = xSemaphoreCreateMutex();
xTaskCreate(task1,"Zadanie 1",configMINIMAL_STACK_SIZE,NULL,1,NULL); xTaskCreate(task2,"Zadanie 2",configMINIMAL_STACK_SIZE,NULL,1 ,NULL);
vTaskStartScheduler(); // uruchomienie planisty
return 0;
}
I tutaj najważniejsza kwestia dotycząca tego zadania. Aby program z semaforem działał musimy dodać jeszcze jedną linijkę kodu w pliku konfiguracyjnym RTOS-a.
#define configUSE_MUTEXES 1
Aby wejść w ten plik wystarczy że przeniesiemy się do definicji przy configMINIMAL_STACK_SIZE i tam wklejamy tą linijkę kodu i po problemie.