Sunteți pe pagina 1din 335

Programarea aplicatiilor

de timp real

Obiectiv disciplină:

Elaborarea aplicaţiilor multitasking


Parte I Parte II
Monotasking Multitasking
= main +ISR = taskuri (+corutine) +ISR
Hardware disponibil: 8086, 8253, 8259 SO de timp real - FreeRTOS
BIOS, facilitati C
Planificare taskuri
Scriere rutine proprii de tratare a INT Comunicare intre taskuri
Partajare variabile globale Partajare resurse intre taskuri si ISR:

C MPLAB
SO
C
BIOS
FreeRTOS

8253
8259 Placa dezvoltare cu
PC DsPIC33

Simplu Complexitate medie


Partea I: Aplicatii monotasking ce gestionează
intreruperi
C

SO

1. Caracteristicile arhitecturii IBM-PC (relevante pentru BIOS

gestionarea intreruperilor) PC
8253
8259

2. Accesarea dispozitivelor periferice 8253, 8259 SO

BIOS

8253
8259
PC

C
3. Facilitati C. Tehnici de programare in intreruperi SO

BIOS

C 8253
8259
SO PC

BIOS

8253
8259
PC
I. 1. Caracteristicile ahitecturii IBM-PC, importante
pentru gestionarea intreruperilor

I. 1. 1. Microprocesorul INTEL 8086


[http://pdos.csail.mit.edu/6.828/2006/readings/i386/toc.htm]

Magistrala adrese:
magistrala adrese IO: 16b (cei mai nesemnificativi)
mag adrese: 20b
MAXIMUM MINIMUM
MODE MODE

GND 1 40 Vcc
AD14 AD15
Mod minim (monoprocesor)-
AD13 A16,S3 - semnalelede control magistrala sunt
AD12 A17,S4 generate direct de 8086
AD11 A18,S5 - magistrala poate fi patajata cu DMA prin
AD10 A19,S6 HOLD/HLDA
AD9 /BHE,S7
AD8 MN,/MX
AD7 /RD
AD6 /RQ,/GT0 HOLD
AD5
8086 /RQ,/GT1 HLDA
Mod maxim (multiprocessor sau cu coprocessor
AD4 /LOCK /WR matematic)
AD3 /S2 IO/M - semnalele de control magistrala sunt
AD2 /S1 DT/R produse de un controller special (8288)
AD1 /S0 /DEN - RQ/GT0 si RQ/GT1 - accesare magistrala
AD0 QS0 ALE de catre procesoare; LOCK blocare acces la
NMI QS1 /INTA
magistrala
INTR /TEST
CLK
- QS0 si QS1 pentru monitorizarea starii cozii
READY
GND 20 21 RESET
de instructiuni
Registri:
 generali: de date 16b:AX,BX,CX,DX; 8b: AL,AH, etc
de pointer şi index: SP, BP, SI, DI
 segment: CS, DS, ES, SS
 pointer de instrucţiuni: IP (adresa instr. CS:IP)
 indicatori de control şi stare: 6 indicatori stare (CF, AF, ZF, SF, OF, PF) şi 3
indicatori de control:
DF=1 – la instr pe şiruri se foloseşte decrementarea (de la adrese mari la adrese
mici, de la dreapta la stânga)
TF=1 – procesorul execută o instrucţiune de depanare după fiecare instrucţiune
IF=1 – sunt recunoscute cereri de întrerupere mascabile
Gestionare memorie:
- până la 1MB (2^20)
- paginată pe segmente continue de 64kB (2^16)

Adrese fizice
- se folosesc regiştri pe 16b

adr_segment: adr_deplasament ⇔ adr_segment ⋅ 2 4 + adr_deplasament

⇒ mai multe variante pentru a adresa aceeaşi locaţie


Ex: (H ) F000:FFF0 ≡ FFFF:0 ≡ FF00:FF0
(F0000+FFF0=FFFF0+0=FF000+FF0=FFFF0)

OBS: 1 paragraf = 16B (2^4)

Memoria organizata pe 2 bankuri: adrese pare >> magistrala date D0-D7,


adrese impare >> magistrala date D8-D15
Sistem întreruperi: (INT tip)

 Externe: conex RESET, NMI, INTR


RESET: CS←FFFF; IP,DS,ES, SS←0 – tratat diferit, nu ca INT obisnuit
NMI: recunoscut ∀IF – INT 2
INTR: recunoscute dacă IF=1 – tip gestionat de 8259 >> pot fi “trap”
 Interne + software generate implicit sau de instructiuni INT x
o Exceptii - primesc controlul în anumite situatii, automat – ca răspuns
al procesorului la o comportare anormală – fara generare întrerupere
de către utilizator prin apel explicit INT x
INT 0 împărţire la 0 (OF=1)
INT 1 execuţie pas cu pas (TF=1)
INT 3 breakpoint – poate fi şi trap
INT 4 – overflow- apelată de INT 0
Dacă OF=1 se execută INT 4
Dacă OF=0, NOP
o Software – cu rutine specializate, ce pot fi rescrise de utilizator INTx
>> pot fi “trap”
Tabela vectori întreruperi: primul kB de memorie = 256 intrări
Intrare / vector interupere = adr_segment (2B) şi adr_depl (2B) pentru
rutina de tratare

1kB ……...

Adr_segment (2B)
vector intrerupere pentru INT x
Adr_deplas (2B) (4 octeti)
4x

……..

0
Intrerupere interna Tipul este depistat
& software intern Construieste
vectorul de
intrerupere

NMI Tipul este 2

Intreruperi
externe/hardware
INTR Vectorul de
(cu IF=1) intrerupere este
format cu ajutorul
8259
Executie instructiune INT x
- Salvează reg indicatori in stivă
- TF←0, IF←0
- Salvează CS în stivă, salvează IP în stivă
- Înscrie CS şi IP cu vectorul de întrerupere de la adresa fizică 4x
Urmează salt la rutina de tratare

Rutina se termină cu IRET care asigură refacerea parţială a contextului şi revenire


corectă în program:
Reface reg indicatori salvat în stivă
Reface CS şi IP cu valorile salvate în stivă
indicatori

CS
IP
1kB
CS stiva
Adr_segment (2B)

Adr_deplas (2B) Rutina de tratare


4x IP intrerupe re x

------------
IRET
0

OBS: Diferit de CALL +RET – IP sau far CALL +RET – CS şi IP


Intreruperi hard: salvare toti registrii – refacere completa context
Partajare vectori intrerupere
!!! Unii vectori de intreruperi nu sunt acesibili programatorului – exceptii
- salvează vector întrerupere vechi
- scrie noul vector de întrerupere
- daca este cazul, asigura executie servicii vechi
In totalitate: prin apel veche rutina
- la sfarsit aplicaţie: refacere vector întrerupere vechi!!!!!

Modifica vector intrerupere Rutina noua de tratare


1kB intrerupe re x
Asigurare servicii vechi
Adr_segment (2B)

Adr_deplas (2B)
4x
Rutina veche de tratare
intrerupe re x
0
Probl. de reentranţă!!! – marcare sectiune critica (SC) şi forţare IF=0 pe durata SC!!
I. 1. 2 Configuratia intreruperilor la sisteme IBM PC

Software:

Exceptii:
INT 0 – S/BIOS– împărţire cu depăşire
INT 1 – S/BIOS – trasare pas cu pas
INT 3 – S/BIOS - breakpoint
INT 4 – S/BIOS - depăşire OF=1

Hardware:
INT 2 – NMI – BIOS – eroare paritate de memorie sau pentru coprocesor
ACCES NEPERMIS UTILIZATORILOR!!
INT 08H – IRQ0_8259_I – BIOS – ceas de timp real (implicit nemascată de 8259)
INT 09H – IRQ1_8259_I – BIOS – tastatura (implicit nemascată de 8259)
INT 0AH – IRQ2_8259_I - rezervata
INT 0BH – IRQ3_8259_I – BIOS – COM (implicit mascată de 8259)
INT 0CH – IRQ4_8259_I – BIOS – COM (implicit mascată de 8259)
INT 0DH – IRQ5_8259_I – BIOS – LPT, disc
INT 0EH – IRQ6_8259_I – BIOS – disc flexibil
INT 0FH – IRQ7_8259_I – BIOS – LPT

INT 70H – IRQ0_8259_II – BIOS – ceas CMOS


INT 71H – IRQ1_8259_II – BIOS – redirectata spre INT0AH /cascadare
INT 72H – IRQ2_8259_II – BIOS – rezervata
INT 73H – IRQ3_8259_II – BIOS – rezervata
INT 74H – IRQ4_8259_II – BIOS – rezervata Aceste tipuri rezulta din
cuvintele de initializare trimise
INT 75H – IRQ5_8259_II – BIOS – rezervata catre 8259_I si 8259_II
INT 76H – IRQ6_8259_II – BIOS – disc dur
INT 77H – IRQ7_8259_II – BIOS – rezervata

8259_I şi 8259_II iniţializate de BIOS. Se pot schimba priorităţile prin programare


-> nerecomandat.
8086

8259_I
8259_II
IRQ7

IRQ7
.
.
. INT INTR

. IRQ2
. INT
.
IRQ0 IRQ1

IRQ0

Controller Out canal 0,


tastatura 8253
OBS:
 In rutina de tratare a unei intreruperi hard se poate genera o intrerupere soft
(INT 08H → INT 1CH)
 SO pot desconsidera sugestiile INTEL
INT 05H – INTEL – depăşire limite super/infer intr-un bloc de date Bound
Range Exceed
INT 05H – BIOS – realizează print screen
De aceea, când depăşirea este atinsă → print screen

Prioritati:

INT interne/soft (fara INT 1),


cu INT i mai prioritar ca INT j, dacă 4 < i < j -
NMI
INTR (mascabile)
scade INT 1 – diferit la 80386 (pas cu pas)
I. 1. 3 BIOS
BIOS

= ansamblu de rutine de tratare a unor întreruperi ce asigură servicii de bază


(pentru un hardware funcţional)

= interfată SO – hardware

Zona de lucru rezervată:

începe de la adresa 0000:0400 H


- rezervarea se face la iniţializarea calculatorului
- structura de date este pentru configuraţia maximă permisa
- unele date sunt iniţializate la pornirea calc
Ex:
0000:0400 H, 2B – adresa COM1
0000:0408 H, 2B – adresa LPT1
0000:041C H, 2B – pointer pozitie curenta in coada tampon a tastaturii
0000:0440 H, 1B – contor ce indică ce interval timp mai trebuie menţinut pornit
motorul unităţii floppy (motor oprit cand contorul este 0)
0000:04F0 H, 16B – zonă de comunicaţie între aplicaţii

Întreruperi BIOS
5, 8, 9, 10-1C, 4A, 70
1D, 1E, 1F, 41, 46 – vectori de întrerupere ce pointeaza spre tabele BIOS

Se vor utiliza in acest curs INT08H, INT09H.


INT 08H – ceas pentru aplicatii de timp real /implicit nemascata de 8259 – IRQ0
_8259I
 Cererea de întrerupere apare de la canal 0 / 8253
- implicit la fiecare 55msec (de 18.2 ori pe sec)
 Pe programarea implicită 8259 – întreruperea de nivel prioritar
 Rutina de tratare asigură:
 Incrementează contorul de la adresa 0000:046C H;
dacă valoarea acestuia indică scurgerea a 24 ore, atunci acesta devine
0 şi se poziţioneaza indicatorul de la adresa 0000:0470
 citeste contorul de la adresa 0000:0440
daca valoarea este 0 opreşte motorul la floppy şi poziţionează
indicatorul de la 000:043F
altfel decrementeaza contorul
 genereaza intrerupere utilizator INT 1CH
rutina de tratare este implicit IRET
 achită întrerurperea la 8259
INT 09H – tastatura /implicit nemascata de 8259 – IRQ1 _8259I
 Cererea de întrerupere apare la fiecare apasare şi eliberare de tastă
 Pe programarea implicită 8259 – întreruperea de nivel prioritar maxim după
INT08H
 Rutina de tratare asigură:
 Citeşte cod scan al tastei (din port de adresa 60H);
 Determina codul ASCII
 Salvează codul scan şi ASCII într-o coadă circulară a tastaturii
Coda tampon are 32 octeţi (spaţiu pentru 16 taste) începând cu
0000:041E H
Pointerul la poziţia curentă este la adresa 0000:041C H
La salvare:
OH OL
cod SCAN cod ASCII
- pentru funcţionale:
OH OL
cod ASCII extins 0
 Dacă au fost apasate tastele ALT, sau CTRL sau Shift sau INS sau
CAPS se actualizeaza octetii de stare de la adresele 0000:0417 si
0000:0418
Aceşti octeţi pot fi citiţi şi cu INT 16H
 Dacă s-a apăsat CTR+ALT+DEL se înscrie 1234H la adresa 0000:0472
şi se predă controlul rutinei de iniţializare

În toate situaţiile se reiniţializează controlerul de tastatura pentru a se putea citi tasta


următoare şi se achita intreruperea la 8259 pentru o funcţionare corecta!!!!!.

Porturile 60H si 61H sunt folosite pentru comunicarea cu tastatura


- citire 60H pentru aflare cod scan tasta (bit D7=0 la apasare si 1 la eliberare)
- port 61H
D7 D6 D5 D4 D3 D2 D1 D0
= 0 enable Pentru alte dispozitive
= 1 disable periferice
Secvenţa de reiniţializare controller tastatura:
- obligatorie
Citeşte octet_1 de la port 61H
Poziţioneaza in octetul citit bitul D7 pe 1 şi scrie rezultatul în port 61H
Scrie octet_1 (nemodificat) la portul 61H

Sugestie – rutina proprie de testare daca o tasta a fost apasata:


Citeste port 60H
Verifica daca este codul scan al tastei cautate
Daca da executa serviciile suplimentare dorite
Asigura servicii vechi ale rutinei (apel veche rutina)
- servicii obligatorii: reinitializare controler tastatura şi achitare
intrerupere la 8259
Alte rutine BIOS:

INT 5H
- tipareşte continutul ecranului la imprimantă dacă se apasă Shift + PrintScreen

INT 10 H – servicii video

INT 11H – determinarea configuraţiei calculatorului


Rutina de tratare asigură înscrierea lui AX cu informaţiile luate de la 0000:0410H

D15 D14 D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0


Nr Nr porturi Nr unit Mod =1 : Exista
imprimante seriale floppy video coprocesor
initial
INT 12H – determină dimensiunea memoriei pentru DOS şi aplicaţii
Valoarea este ştiută de la initializare / testul de memorie
Rutina de tratare asigură înscrierea lui AX cu informaţiile luate de la 0000:0413H

INT 13H – servicii disc – pachet de funcţii ce permit acces la nivel de sector
Înainte de generarea întreruperii:
se înscrie AH cu numărul funcţiei
se înscrie în DL – număr unitate

Exemple:
funcţia 00H: poziţionare la 0 a controlerului de disc + calibrare
funcţia 01H: citire stare după ultima operaţie efectuată
(în AL se returnează un cod de eroare)
funcţia 02H: citire sectoare
funcţia 03H: scriere sectoare

Tabele parametri: disc floppy 0000:0078H; disc dur 0000:0104H


INT 14H – servicii porturi seriale
desi în zona BIOS este spaţiu rezervat pentru 4 porturi COM, numai 2 sunt
iniţializate implicit la pornirea calc

funcţia 00H: iniţializare


Înainte de generarea întreruperii:
AH = numărul funcţiei
DX = 0 sau 1 (nr port COM)
AL = parametri initializare
returneaza: AX- stare
funcţia 01H: emitere caracter
Înainte de generarea întreruperii:
AH = numărul funcţiei
DX = 0 sau 1 (nr port COM)
AL = caracter
returneaza: AX=stare
funcţia 02H: recepţie caracter
Înainte de generarea întreruperii:
AH = numărul funcţiei
DX = 0 sau 1 (nr port COM)
returneaza: AL = caracter, AH=stare
funcţia 03H: citire stare
Înainte de generarea întreruperii:
AH = numărul funcţiei
DX = 0 sau 1 (nr port COM)
returneaza: AX=stare

INT 15H – diverse (ex: joystick, determinare disponibil memorie extended)


INT16H – interfată tastatura
- foloseşte informaţiile înscrise de INT09H în coada tampon tastatura sau la
octeţii de stare tastatura
Înainte de generarea întrerurperii: se înscrie AH cu numărul funcţiei

funcţia 00H: aşteaptă cod tasta apăsată şi citeşte codul


funcţia 10H: idem pentru taste extinse
funcţia 01H: verifică dacă este un cod disponibil de tasta apasată
returnează: ZF=0 cod disponibil; în AL cod ASCII, în AH cod scan
ZF=1 cod nedisponibil
funcţia 02H: citire stare
AL = shift status bits
0 = right shift key depressed
1 = left shift key depressed
2 = CTRL depressed
3 = ALT depressed
4 = SCROLL LOCK active
5 = NUM LOCK active
6 = CAPS LOCK active
7 = INSERT state active
INT 17H – servicii port paralel
funcţia 00H: tipărire caracter
înainte de generarea întreruperii:
AH = numărul funcţiei
DX = 0 , 1, 2 (nr port LPT)
AL = caracter de tipărit
returneaza: AH=stare port
funcţia 01H: iniţializare port
Înainte de generarea întreruperii:
AH = numărul funcţiei
DX = 0 , 1, 2 (nr port LPT)
returneaza: AH- stare port
funcţia 02H: citire stare port
Înainte de generarea întreruperii:
AH = numărul funcţiei
DX = 0 , 1, 2 (nr port LPT)
returneaza: AH- stare port
INT 18H, 19H – interpretor ROM BASIC – pentru încărcare SO

INT 1AH - acces la ceas CMOS: actualizat fără intervenţia CPU


- acces la contor zona BIOS (tick count): actualizat de 18.2 ori pe
secunda (valori: o ora:65543; 1 zi:1573040) de INT08H

- citire/scriere data (funcţii 4/5) sau ora CMOS (funcţii 2/3)


- inscriere/anulare alarmă:
funcţia 6: setare alarma – la ora precizată se va executa rutina utilizator
INT 4AH – atentie: returneaza cod de eroare daca ceasul nu este
functional!! sau alarma era deja setata
functia 7: reset alarma
pentru modificare ora alarma: reset alarma veche, apoi setare ora noua
- citeste/scrie contor tick (functii 0/1)

INT 1BH – tratare Ctrl +BREAK


- se execută daca se apasă CTRL+BREAK
- implicit: IRET; preluata de DOS
INT 1CH – rutina ceas utilizator
- generată de INT08H !
- implicit: IRET

INT 4AH – alarma utilizator


- generată de INT 70H
- implicit: IRET

INT 70H – ceas CMOS – pe IRQ0-8259II /implicit mascata la 8259 si CMOS


- cererea de întrerupere (dupa activare generare periodica in CMOS) apare de
1024 ori pe sec
- dacă ora_CMOS = ora_alarma, se genereaza INT 4AH
Tabele de parametri

INT 1DH tabel parametri video

INT 1EH tabel parametri floppy (10B) – folosita de INT 13H


Ex: timp salt la alta pista, timp poziţionare cap, dimensiune sector, numar sectoare etc

INT 41H Tabel parametri disc dur (16B) – folosita de INT 13H

INT 46H Tabel parametri al doilea disc dur (16B) – folosita de INT 13H

INT 1FH Tabel cararactere ACSII – set extins


- folosita în mod grafic, coduri 128 – 255 (pentru coduri 0-127 in ROM la adresa
F000: FA6E H)
- la initializare pointerul este F000:0000, dar caracterele - nedisponibile; pot fi
inscrise de SO.
1 caracter = 8B
1 1 1 1 1 1 1 1 ← margine inferioara pe ecran
1 1 1 1 1 1 1 1
1 1 1 1 1 1
1 1 1 1 1 1
1 1 1 1
1 1 1 1
1 1
1 ← margine inferioara superioara
Informaţii despre memorie

- dimensiune memorie de bazǎ: în kB la CMOS 15H, CMOS 16H:


Accesare CMOS la citire:
scrie in port de adresǎ 70H - adresa CMOS
citeşte din port de adresa 71H
scrie in port de adresǎ 70H octetul 15H
citeşte din port de adresa 71H octetul_low
scrie in port de adresǎ 70H octetul 16H
citeşte din port de adresa 71H octetul_high
size=octetul_low+256*octetul_high [kB]
- dimensiune memorie extinsǎ: în kB la CMOS 17H, CMOS 18H
- dimensiune memorie de bazǎ disponibilǎ pentru DOS şi aplicaţii
la AdrPSP:0002H – în paragrafe
la 0000:0413H (BIOS) – în kB
folosind INT12H – in AX – în kB
- dimensiune memorie pentru aplicaţie
limita superioara (SUP) de la AdrPSP:0002H
SUP-AdrPSP=memorie alocatǎ în paragrafe - fǎrǎ header MCBZ şi
mediu
I. 2. Accesarea dispozitivelor periferice

Sstem interfata Dispozitiv


calcul periferic

o Interfaţa asigurǎ legǎtura între sistemul de calcul şi periferic


o Conţine:
- sistem IN/OUT: registru de control/comanda periferic,
registru de stare periferic, registri date
- circuite specializate pentru control/comanda periferic
Configuratii pentru Sisteme IN/OUT

- cu spaţiu IN/OUT dedicat (IO MAP) – registrii sunt adresati prin operatii IO
(spatiu adrese separate pentru memorie si porturi)
- cu pagini memorie pentru IO (Memory MAP) – porturile sunt accesate ca
locatii de memorie

Transfer CPU ↔ periferic


1. Transfer în bucla programatǎ
- se iniţiaza transferul şi se verificǎ în buclǎ cand este gata

2. Transfer concurent (prin întreruperi)


- se iniţiazǎ transferul; CPU executǎ alte instrucţiuni; perifericul lasneaza o cerere
de înrerupere cand operaţia este gata

3. Transfer fǎrǎ CPU, gestionat IO


- ex DMA
I. 3. 1. Programarea 8253
Rol
o generator de impulsuri
o contor de evenimente, ceas de timp real
o monostabil programabil

Componente
Registru de comanda
3 numaratoare (#0, #1, #2) pe 16 biti, cu decrementare
GATEi

CLKi
CONTOR i
OUTi

– cu un registru de date (pe 16 biti) corespunzator


- frecventa maxima 2.63MHZ (Tmin=380nsec);
- numarare implicita pe frontul descrescator al CLKi
GATE0
Tampon
D7..D0 date CLK0
CONTOR 0
OUT0
RD

WR Logica de GATE1
citire/scriere
CS CLK1
CONTOR 1
A0,A1 OUT1

GATE2
Reg cuvant
CLK2
comanda CONTOR 2
OUT2
Moduri de lucru

Mod 0 numarator de evenimente


- dupa primirea cuvâtului de comandǎ, OUT trece pe 0; rǎmâne pe 0 pânǎ când se numara n
evenimente;
- numǎrare pe front descrescator CLK;
- numararea se executa daca GATE=1; GATE=0 inhibǎ numǎrarea; cand GATE comutǎ pe 1
numararea se reia de unde a ramas

Mod 1 monostabil programabil, redeclansabil hardware


- dupa un front crescator GATE, la primul front descrescator CLK, OUT trece pe 0; rǎmâne
pe 0 pânǎ când se numara n evenimente;
- numǎrare pe front descrescator CLK, pentru orice GATE.
- daca se incarca noi constante in numarator, ele vor fi considerate dupa o succesiune de un
front crescator GATE, front descrescator CLK

Mod2: generator de impulsuri


- genereaza OUT periodic, daca CLK are aceeasi frecventa: primele n-1 tacte Out este pe 1,
ultimul tact pe 0;
-numǎrare pe front descrescator CLK, cu reincarcare automata a constantei.
- daca se incarca alta constanta, ea va fi considerata dupa prima trecere prin 0;
- numararea se executa daca GATE=1; GATE=0 inhibǎ numǎrarea; cand GATE comutǎ pe 1
numararea se reia de la valoarea initial inscrisa in numǎrator (n)

Mod 3: generator de unda dreptunghiulara


- idem cu mod 2, dar asigura factor umplere aproximativ 1:1

Mod 4: impuls declansat software


- dupǎ primire cuvant comanda, OUT trece pe 1; va fi pe 0 pe durata tactului n+1; apoi
comuta iar pe 1;
- numǎrare pe front descrescator CLK;
- constanta trebuie reincarcata pentru a relua functionarea;
- numararea se executa daca GATE=1; GATE=0 inhibǎ numǎrarea; cand GATE trece pe 1,
numaraea se reia de unde a ramas.

Mod 5: impuls declansat hardware


-dupa primirea cuvantului decomada OUT trece pe 1; va fi pe 0 pe durata tactului n+1
- un front crescator GATE declanseaza numǎrarea, la primul front descrescator CLK
- numǎrare pe front descrescator CLK, pentru orice GATE.
- un front crescator GATE reinitializeaza numararea de la valorea inscrisa în contor (n).
Structura cuvânt comandǎ

7 6 5 4 3 2 1 0
SC1 SC0 RL1 RL0 M2 M1 M0 BCD
Selectie contor: Scriere/citire Mod lucru:
00=contor 0 00=memorare continut contor 000 (mod0) 0=binar,
01=contor 1 01=scriere/citire LSB(MSB=0) 001(mod1) 1=BCD
10= contor 2 10=scriere/citire MSB(LSB=0) x10(mod2)
11=nepermis 11=scriere/citire LSB, apoi MSB x11(mod3)
100(mod4)
101(mod5)

Specificatii pentru arhitectura IBM PC

Adrese regiştri:
registru comanda – adresa 0x43
registri date contor 0 – adresa 0x40 ; contor 1 – adresa 0x41 ; contor 2– adresa 0x42
Mod conectare:
- contor 0:
Gate0=5V;
CLK0 la 1,19MHz;
OUT0 la IRQ0-8259-INT08H
mod 2 sau 3, constanta 0 (T = 55msec, f=18.150Hz)
REPROGRAMARE PERMISA:
- se va reface programarea implicita la final
- se va rescrie rutina de tratare a intreruperii de ceas INT08 si se vor
asigura vechile servicii cu periodicitate aprox. 55msec

- contor 1:
Gate1=5V;
CLK1 la 1,19MHz;
OUT1 pentru refresh DRAM
mod 2, constanta 18 (T = 15 microsec)
INTERZISA UTILIZAREA pentru APLICATII
- contor 2:
Gate2=D0 port 0x61;
CLK2 la 1,19MHz;
OUT2 AND D1 port 0x61 – intrare BELL
mod 3, constanta 1190 (f=10Hz)
PENTRU APLICATII
Port0x61
D7 D6 D5 D4 D3 D2 D1 D0
BELL GATE2
I. 2. 1. Programarea 8259

Rol
gestioneaza cererile de intreruperi de la 8 surse de intreruperi (prin cascadare pânǎ la
64 intreruperi) in functie de prioritati

Componente
ISR registru intreruperi in servire
IMR registru mascare intreruperi
PR registru prioritati
IRR registru cereri intreruperi

- dacǎ apare o cerere IRQi, si IMMi≠1, atunci bitul i din IRR devine 1;
- dacǎ IRR≠0, se lanseaza cerere de intrerupere la procesor pe linia INT; daca
cererea este acceptata, procesorul trimite un INTA=0 şi atunci se seteaza bitul i
din ISR si se sterge bitul i din IRR
INTA INT

Tampon
D7..D0 date Logica comanda

RD

WR Logica de
citire/scriere IRR
CS ISR
PR IRQ0, ..IRQ7
A0

CAS0,CAS1, Logica
IMR
CAS2 comparare.
cascadare
SP/EN
-
la master SP=1; la slave SP=0
Initializare

IC1 A0 D7 D6 D5 D4 D3 D2 D1 D0
0 A7 A6 A5 1 LTM ADI S IC4
IC2 A0 D7 D6 D5 D4 D3 D2 D1 D0
1 A15 A14 A13 A12 A11 A10 A9 A8

A15…A5 = adresa de inceput a tabelului vectori de intreruperi alocat


daca ADI=1 se aloca 4 octeti/linie si adresa de inceput IRQi este
4
A15 .... A5 ⋅ 2 + 4i
daca ADI=0 se aloca 8 octeti/linie si adresa de inceput IRQi este
A15 .... A6 ⋅ 25 + 8i

LTM=0 daca IRQ active pe front crescator; LTM=1 daca IRQ active pe
palier

S=1 exista un singur 8259; S=0 exista mai multe 8259


IC4=0 nu exista IC4; =1 exista IC4
IC3M A0 D7 D6 D5 D4 D3 D2 D1 D0
1 S7 S6 S5 S4 S3 S2 S1 S0
IC3S A0 D7 D6 D5 D4 D3 D2 D1 D0
1 x x x x x ID2 ID1 ID0
Si = 1 daca IRQi este conectat la slave
ID1ID2 ID3 = cod identificare slave (codul liniei IRQi pe care este conectat la
master)

IC4 A0 D7 D6 D5 D4 D3 D2 D1 D0
1 1 0 0 SFNM BUF M/S AEOI µP
SFNM = 0 o unica cerere de pe slave inregistrata (prima); =1 sunt inregistrate
simultan de pe slave toate cererile de intreruperi (ca la un 8259 cu mai multe linii
IRQi)
BUF=1 bufferat, caz in care SP/EN devine enable si M/S stabileste master
slave.
AEOI=0 resetare bit ISR printr-un cuvant de comanda de la µP; =1 resetare bit
ISR dupa ce µP primeste adresa completa pentru rutina de tratare intrerupere
µP=0 cu 8080; =1 cu 80x86

Moduri de lucru:
- cu prioritati fixe (Fully Nested): IRQ0 >…> IRQ7
- cu rotire prioritati (Rotating Mode):
o rotire normala – dupǎ tratarea unei cereri de intreruperi pe de IRQi, linia
devine de prioritate minimǎ
o rotire speciala- se transmite prin program linia de prioritate minimǎ
- cu mascare
o speciala: inhiba cereri de pe liniile mascate si valideaza solicitarile de pe
linii nemascate, indiferent de prioritate
- mod cu interogare
 se citeste un octet cu A0=x:
D7 D6 D5 D4 D3 D2 D1 D0
=1?? W2 W1 W0
Daca D7=1, atunci s-a inregistrat cererea de intrerupere pe linia i = W2W1W0
Citirea va fi intrepretata ca acceptare de intrerupere (INTA=0),
=> se seteaza bit ISR
Cuvinte comanda

OC1 A0 D7 D6 D5 D4 D3 D2 D1 D0
1 M7 M6 M5 M4 M3 M2 M1 M0

Mi=1 daca IRQi este mascata

OC2 A0 D7 D6 D5 D4 D3 D2 D1 D0
0 R SEOI EOI 0 0 L2 L1 L0

R=0 fara rotire prioritati; =1 cu rotire


SEOI=0 mod nespecific; =1 mod specific
EOI=1 reset bit ISR

Situatii posibile:
R SEOI AEOI explicatie
0 0 1 Achitare intrerupere nespecific (sterge bit prioritate
maxima din ISR)
0 1 1 Achitare intrerupere specific (sterge din ISR bitul indicat
de L2L1L0)
1 0 1 Rotire prioritati si achitare intrerupere nespecific
1 1 1 Rotire prioritati si achitare intrerupere specific
1 1 0 Rotire specific
1 0 0 Rotire nespecific, setare AEOI
0 0 0 nimic

OC3 A0 D7 D6 D5 D4 D3 D2 D1 D0
0 x ESMM SMM 0 1 P RR RIS

ESMM=1, SMM=1 setare mascare speciala


ESMM=1, SMM=0 resetare mascare speciala

P=1 cu interogare;=0 pe intreruperi

RR=1, RIS=0 solicitare citire IRR => citirea cu primul inportb


RR=1 RIS=1 solicitare citire ISR => citirea cu primul inportb

Conversatie 8259-procesor
- apare o cerere pe linia IRQi 8259S; 8259M va trimite INT=1 spre procesor;
procesorul raspunde cu INTA=0 cǎtre 8259M si 8259S; se pozitioneaza bit ISR
la master si slave;
- 8259M trimite cod de CALL date spre processor si cod CAS spre slave
(verificare: 8259S trimite cod ID2…0 spre master)
- procesorul trimite inca doua impulsuri INTA=0 spre 8259S; acesta depune pe
rand, pe magistrala de date, octet low si high al adresei vectorului de
intreruperi;
- ……………..
- la sfarsit tratare intrerupere: procesorul achita intreruperea la 8259M si 8259S

Specificatii pentru arhitectura IBM PC

Initializare pe mod fara rotire prioritati, fara restare automata bit ISR
- la primirea comenzii achitare intrerupere se va reseta bitul ISR de prioritate
maxima; achitarea unei intreruperi cu OC2:
8259M: outportb(0x20,0x20) 0010 0000

Adrese regsitri:

8259M: adrese 20H si 21H


OC1 – scris la adresa 21H
OC2, OC3 – scris la adresa 20H

ISR, IRR – citire de la adresa 20H imediat dupa trimiterea unui OC3
corespunzator
outportb(0x20,0x6b) // citire ISR 0110 1011
ISR=inportb(0x20)
outportb(0x20,0x6a) // citire IRR 0110 1010
IRR=inportb(0x20)
IMR – citire de la dresa 21H: IMR=inportb(0x21)

8259S: adrese A0H, A1H


I. 3. Dezvoltarea aplicaţiilor C pentru gestionarea
intreruperilor

I. 3. 1. Facilităţi C pentru gestionarea intreruperilor

Facilităţi DOS.H
DOS.H3) Functii de gestiune a vectorilor de intrerupere
Ex

Modificator : interrupt
Asigură suplimentar salvarea/refacerea in/din stiva a registrilor:
AX,BX, CX,DX, ES,DS,SI,DI,BP,
Implicit INTEL asigura: flags, CS,IP
= total 12 registri pe 16b
Revenire in context gresit: prin modificare SS, SP
void interrupt (*getvect (int intrno))()
IN: intrno
Efect: citeşte vector întrerupere intrno
OUT: returneaza vectorul de întrerupere (pointer la rutina de tratare de tip interrupt)

void setvect(int intrno, void interrupt (*rutinanoua)())


IN: intrno
void interrupt (*rutinanoua)() – pointer la rutina noua de tratare
Efect: rescrie vectorul întrerupere intrno (va fi pointer la o noua rutina de tratare: de tip
void interrupt!!!!)

void enable(void);
Efect: seteaza IF=1

void disable(void);
Efect: seteaza IF=0
Ex1. //exemplu utilizare interrupt,setvect, getvect
//instalez o rutina proprie pentru tratarea cererii de intrerupere
// de la tastatura
//=============================================================
#include <stdio.h>
#include <dos.h>
#include <conio.h>
int astept=1; //variabila globala
void interrupt (*oldvect)();
void interrupt newtast(void)
{
char codscan; codscan=inportb(0x60);
if (codscan==0x12) astept=0;
(*oldvect)();
}

void main(void){
clrscr();
oldvect=getvect(0x09);setvect(0x09,newtast);
printf("este rescrisa rutina de tratare INT09H\n");
printf("apasa tasta e, altfel programul sta in bucla\n");
while(astept);
printf("am citit tasta e");
setvect(0x09,oldvect);}
Ex2. //exemplu utilizare interrupt,setvect, getvect
//instalez o rutina proprie pentru tratarea cererii de intrerupere
// de la tastatura
//===============================================================
#include <stdio.h>
#include <dos.h>
#include <conio.h>
int numar=0; //variabila globala
void interrupt (*oldvect)();
void interrupt newtast(void) {
numar++;
(*oldvect)();}
void main(void){
int urm_numar=1;
clrscr();

oldvect=getvect(0x09);
setvect(0x09,newtast);

printf("este rescrisa rutina de tratare INT09H\n");


printf("apasa niste taste\n");
sleep(4);
setvect(0x09,oldvect);
printf("ai apasat %d taste",numar/2);}
Ex3.
//exemplu utilizare interrupt,setvect, getvect
//instalez o rutina proprie pentru tratarea cererii de intrerupere
// de la tastatura
//=====================================================
#include <stdio.h>
#include <dos.h>
#include <conio.h>

int numar=0; //variabila globala


void interrupt (*oldvect)();
void interrupt newtast(void){
numar++;
(*oldvect)();}

void main(void){
int urm_numar=1;
clrscr();
oldvect=getvect(0x09);setvect(0x09,newtast);
printf("este rescrisa rutina de tratare INT09H\n");
printf("apasa niste taste, cu asteptare intre 2 taste consecutive\n");

while (numar<10 {
if (urm_numar<numar)
{
printf("ai apasat o tasta, continua\n");
urm_numar=urm_numar+2;
}
}

setvect(0x09,oldvect);
printf("ai apasat %d taste",numar/2);

}
Ex4.
//exemplu utilizare interrupt,setvect, getvect
//instalez o rutina proprie pentru tratarea cererii de intrerupere
// de ceas
//===============================================================
#include <stdio.h>
#include <dos.h>
#include <conio.h>
int contor=20; //variabila globala
void interrupt (*oldvect)();
void interrupt newceas(void) {
contor--;
(*oldvect)();}

void main(void){
clrscr();
oldvect=getvect(0x08);setvect(0x08,newceas);
printf("este rescrisa rutina de tratare INT08\n");
printf("programul asteapta...\n");
while(astept);
setvect(0x08,oldvect);
puts("gata");}
Ex5.
//exemplu utilizare interrupt,setvect, getvect
//instalez o rutina proprie pentru tratarea cererii de intrerupere
// de ceas
//===================================================================

#include <stdio.h>
#include <dos.h>
#include <conio.h>
int contor=20,flag=0; //variabila globala
void interrupt (*oldvect)();
void interrupt newceas(void){ contor--;}

void main(void){
int urm_flag=0;
clrscr();
oldvect=getvect(0x1c);setvect(0x1c,newceas);
printf("este rescrisa rutina de tratare INT08\n");
printf("programul asteapta...\n");
while(contor);
setvect(0x1c,oldvect);
puts("gata");
}
Ex6.
//exemplu utilizare interrupt,setvect, getvect
//instalez o rutina proprie pentru tratarea cererii de intrerupere
// de la tastatura
//===================================================================

#include <stdio.h>
#include <dos.h>
#include <conio.h>

int contor=20,flag=0; //variabila globala

void interrupt (*oldvect)();

void interrupt newceas(void)


{
contor--;
if (contor==0)
{
contor=20;
flag++;
}
(*oldvect)();
}
void main(void)
{
int urm_flag=-1;

clrscr();

oldvect=getvect(0x08);
setvect(0x08,newceas);

printf("este rescrisa rutina de tratare INT08\n");


printf("programul asteapta...\n");

while(flag<5)
{
if (urm_flag!=flag)
{
printf("a mai trecut o per ");
urm_flag++;
if (urm_flag%2==0) printf("para\n");
else printf("impara\n");
}
}

setvect(0x08,oldvect);
puts("gata");
}
Ex7
//modul in care rutinele de tip interrupt lucreaza cu stiva
//valorile IF
//==============================================================

#include <stdio.h>
#include <dos.h>
#include<conio.h>

void interrupt (*oldceas)();


void interrupt newceas();
unsigned flag;
unsigned long dif;
unsigned ssmain, ss1, ss2, spmain, sp1,sp2,if1,if2;

void main(void){
clrscr();
disable();
printf("dupa disable: IF= %x, ar trebui sa fie 0\n",(_FLAGS & 0x0200)>>9);
oldceas=getvect(0x08);setvect(0x08,newceas);
ssmain=_SS;spmain=_SP;
enable();
printf("dupa enable: IF= %x, ar trebui sa fie 1\n\n\n",((_FLAGS & 0x0200)>>9));

while (!flag);
setvect(0x08,oldceas);
printf("inainte de a intra in rutina de tratare intrerupere:\n");
printf(" varf stiva %04x:%04x\n\n",ssmain,spmain);
printf("in propria rutina de trat.intrer., inainte de apel veche rutina:\n");
printf(" varf stiva %04x:%04x, IF=%x\n",ss1,sp1,if1);
printf("in propria rutina de trat. intrer., dupa apel veche rutina:\n");
printf(" varf stiva %04x:%04x, IF=%x\n\n",ss2,sp2,if2);
dif=ssmain*16+spmain-ss1*16-sp1;
printf("au fost salvati: %d octeti\n",dif);
printf("\nla finalul programului principal:\n");
printf(" varf stiva %x:%04x,",_SS,_SP);
printf(" IF=%x\n",(_FLAGS&0x0200)>>9);
}

void interrupt newceas(void){


sp1=_SP;ss1=_SS;
if1=_FLAGS;if1=(if1 & 0x0200)>>9;
(*oldceas)();
sp2=_SP;ss2=_SS;
if2=_FLAGS;if2=(if2 & 0x0200)>>9;
flag=1;}
DOS.H1) Funcţii pentru generare intreruperi software
union REGS {
struct WORDREGS x;
struct BYTEREGS h; };
struct WORDREGS {unsigned int ax,bx,cx,dx,si,di,cflag,flags;};
struct BYTEREGS {unsigned char al,ah,bl,bh,cl,ch,dl,dh;};
struct SREGS { unsigned int es,cs,ss,ds;};
struct REGPACK {
unsigned r_ax,r_bx,r_cx,r_dx;
unsigned r_bp,r_si,r_di,r_ds,r_es,r_flag;};

int int86(int intno,union REGS *inregs,union REGS *outregs); Ex


IN: intno,
union REGS * inregs: continut registri inainte de generare intrerupere
OUT: union REGS *outregs: continut registri la iesirea din rutina de tratare intrer.
returneaza: AX;
Efect: genereaza intrerupere software intno
pozitioneaza dupa tratarea intreruperii: x.cflag si x.flags
pozitioneaza _doserrno (daca CF=1)
int int86x(int intno,union REGS *inregs,union REGS *outregs, struct SREGS
*segregs);
IN: intno
union REGS * inregs: continut registri inainte de generare intrerupere
union SREGS *sregs
OUT: union REGS *outregs: continut registri la iesirea din rutina de tratare intrer.
union SREGS *sregs
returneaza: AX
Efect: genereaza software intno, copiind sregs->ds in DS; sregs->es in ES;
pozitioneaza dupa tratarea intreruperii: x.cflag si x.flags
pozitioneaza _doserrno (daca CF=1)

void intr(int intno, struct REGPACK *preg);


IN: intno
struct REGPACK *preg: continut registri inainte de generare intrerupere
OUT: struct REGPACK *preg: continut registri după tratare intrerupere
Efect: genereaza intrerupere intno

void geninterrupt(int intr_num);


IN: intr_num Efect: genereaza intrerupere intr_num
Ex1.
/*exemplu cu int86*/
/* se floseste INT 10H BIOS, fct 2(setare pozitie cursor)*/
/* si fct 6(scroll in sus intr-o fereastra)*/
//====================================================================

#include <stdio.h>
#include <conio.h>
#include <dos.h>
#define VIDEO 0x10

void movetoxy(int x, int y){


union REGS regs;
regs.h.ah = 2; /* functie pentru setare pozitie cursor */
regs.h.dh = y; //nr rand
regs.h.dl = x; //nr coloana
regs.h.bh = 0; /* pagina video 0 */
int86(VIDEO, &regs, &regs);
}

void defil(int no,char xsus,char ysus, char xjos, char yjos){


union REGS reg;
reg.h.ah = 0x06; /* functie defilare pe o fereastra, in sus */
reg.h.al=no; //cate randuri defileaza
reg.h.ch=ysus; //fereastra, varf stanga sus
reg.h.cl=xsus; //idem
reg.h.dh=yjos; //fereastra,varf dreapta jos
reg.h.dl=xjos; //idem
reg.h.bh = 0x00; /*atribut afisare - noile randuri sunt goale==stergere*/
int86(VIDEO, &reg, &reg);
}

int main(void){
clrscr();

movetoxy(35, 10);
printf("calculatorul este harnicut!!\n");
movetoxy(48, 20);
printf("este prietenul meu!!\n");
sleep(2);
defil(10,48,5,70,20);

//defileaza 5-10 randuri in sus pe fer. preciz.

sleep(5);
return 0;
}
DOS.H3) Functii de citire/scriere a locatiilor de memorie si a
porturilor de intrare/iesire
int inport(int portid);
unsigned char inportb(int portid);
void outport(int portid,int value);
void outportb(int portid,unsigned char value);

int peek(unsigned segment, unsigned offset);


char peekb(unsigned segment, unsigned offset);
void poke(unsigned segment, unsigned offset,int value);
void pokeb(unsigned segment, unsigned offset, char value);
Exemple
Ex1: 8253 - Generare semnal sonor, cu frecventa de 2500HZ,pe durata 4sec (se
foloseste constanta de divizare a numaratorului: c_div=1.190.000/2500).
#include <stdio.h>
#include <dos.h>
void main(void){
int c_div;
c_div=1190000L/2500;
printf("cdiv=%d\n",c_div);
outportb(0x43,0xb6);/*mod 3 binar, contor 2 : 1011 x110*/
outportb(0x42,c_div%256);/*c_div,octet low*/
outportb(0x42,c_div/256);/*c_div,octet high*/
outportb(0x61,inportb(0x61)|0x03);/*validare sunet; gate=bo=1*/
sleep(4);
outportb(0x61,inportb(0x61)&0xfc);/*invalidare sunet; gate=bo=0*/
}
Ex2 – masurarea intevalului de timp necesar executiei unor instructiuni folosind
8253- canal2
#include <stdio.h>
#include <dos.h>
void main(void){
unsigned k,j;
float a,b;
a=3.14;b=123.6;

outportb(0x43,0xb0);/*canal 2, mod 0,: 1011 000 */


outportb(0x42,0xff);outportb(0x42,0xff);/*c_div=0xffff*/
outportb(0x61,inportb(0x61)|1);/* gate 1 pentru numarare*/

/* ==================*/
b=b+a;
printf("suma %f\n",b);
/* ========================*/

outportb(0x61,inportb(0x61)&0xfe);/*invalidare numarare*/
//outport(0x43,0x80);/*citire continut numarator prin memorarare*/
k=inportb(0x42);/*octet low*/
j=inportb(0x42);/*octet high*/
k=k|(j<<8);k=0xffff-k; k=(double)(k)/1.19;
printf("durata de executie a secventei marcate este %f microsec\n",(float)k);

}
Ex3. 8253 implementarea unei functii delay proprii cu rezolutia de msec
#include <stdio.h>
#include <dos.h>
void interrupt (*old)();
int CONTOR;
void interrupt my1c(void){
if (CONTOR!=0) CONTOR--;}
void interrupt my08(void){
if (CONTOR!=0) CONTOR--;
outportb(0x20,0x20);}
//===================================================
void Ddelay(int time){/*TIME IN MSEC*/
int low,high;
high=time/55;low=time%55;

CONTOR=high;old=getvect(0x1c);setvect(0x1c,my1c);
while(CONTOR);
setvect(0x1c,old);

P_8253(0,3,0,1192);/*perioada ceas 1msec*/


CONTOR=low;old=getvect(0x08);setvect(0x08,my08);
while(CONTOR);

P_8253(0,3,0,0);/*reprogramez vechea per de 55msec*/


setvect(0x08,old);}
//=====================================================

P_8253(char canal, char mod, char bcd, int div){

int port=0x40;
char octet=0;
if(canal==1) return 0;
port+=canal;

disable();
octet=octet| (0xc0 & (canal<<6));
octet=octet| (0x0e & (mod<<1 ) );
octet=octet| (0x01&bcd);
octet=octet| 0x30;

outportb(0x43,octet);
outportb(port,(char)(div&0xff));/*div, octet low*/
outportb(port,(char)(div>>8)); /*div, octet high*/
enable();
}
//=====================================================
void speaker_on(void){outportb(0x61,inportb(0x61)|0x03);}
//======================================================

void speaker_off(void){outportb(0x61,inportb(0x61)&0xfc);}
//======================================================
void beep(int frecventa,int durata){
int div;
div=(int)(1190000L/frecventa);
P_8253(2,3,0,div);
speaker_on();
Ddelay(durata);
speaker_off();
}
//=======================================================

void main(void){
beep(2500,4000);}
EX4 8259 ilustreaza modul de lucru al 8259 cu regsitrii ISR si IRR
/*preiau intreruperea de la tastatura;
in timpul rutinei de tratare a intreruperii de la tastatura
citesc registrii IRR si ISR pentru a verifica modul de functionare
a controllerului 8259;

Rezultate:
-----------
In registrul ISR bitul 1 este pozitionat ===rutina in curs de executie
(ISR=0x02); IRR=00;
======================================================*/

#include <stdio.h>
#include <conio.h>
#include <dos.h>
unsigned int ISR,IRR,flag;
void interrupt (*oldtast)();

void interrupt newtast(void){


flag++;
outportb(0x20,0x0a);IRR=inportb(0x20);
outportb(0x20,0x0b);ISR=inportb(0x20);
(*oldtast)();}

void main(void){
clrscr();
oldtast=getvect(0x09);
setvect(0x09,newtast);
while(flag<2);
setvect(0x09,oldtast);
printf("ISR=%02x, IRR=%02x\n",ISR,IRR);
}
EX5. Ilustreaza modul de lucru cu 8259 slave
/* Lucrul cu rutina 70H = IRQ0, al doilea 8259
- in timpul rutinei de intreruperi registrul ISR1 indica IRQ2
in deservire si registrul ISR2 indica IRQ0 in deservire
- pentru achitare intrerupere se achita intreruperea IRQ0 -II
si IRQ2 - I; aceste achitari sunt incluse de vechea rutina 70H
(altfel sistemul se blocheaza;)

Pentru demascare:
- IRQ0II si IRQ2I la 8259 trebuie demascat in IMR
- bitul D6, registrul de stare CMOS (adresa 0bH) trebuie pus pe 1

Intreruperea apare o data la 976 microsecunde


=======================================================*/

#include <stdio.h>
#include <dos.h>
#include <conio.h>

int flag;
unsigned char ISR1,ISR2;
void interrupt(*old70)();
void interrupt my70(void){
flag++;
outportb(0x20,0x0b); ISR1=inportb(0x20);
outportb(0xa0,0x0b); ISR2=inportb(0xa0);

/*outportb(0xa0,0x20);*/ /*achitare int IRQ0, 8259-II*/


/*outportb(0x20,0x20);*/ /*achitare int IRQ2, 8259 -I*/
/*comenzi obligatorii daca nu se asigura vechile servicii*/

(*old70)(); /*vechile servicii*/


}

void main(void){
char c;
clrscr();
outportb(0xa1,((inportb(0xa1))&0xfe));/*demascare IRQ0-II*/
outportb(0x21,((inportb(0x21))&0xfb));/*demascare IRQ2-I*/
/*outportb(0xa1,((inportb(0xa1))|0x01));*//*mascare IRQ0-II */

outportb(0x70,0x0b);/*(validare intrerupere CMOS, din registrul stare CMOS)*/


c=inportb(0x71);outportb(0x70,0x0b);outportb(0x71,c|0x40);
/*(bit D6 pe 1 - enable periodic interrupt)*/

old70=getvect(0x70);setvect(0x70,my70);
sleep(3);
setvect(0x70,old70);
printf("in intreruperea CMOS ISR1=%02x, ISR2=%02x\n",ISR1,ISR2);
if(flag>0) printf("intreruperea CMOS a fost deservita de %d ori\n",flag);
else printf("intreruperea CMOS nu a fost executata\n");

outportb(0x70,0x0b);/*invalidare intrerupere CMOS, din registru stare CMOS*/


c=inportb(0x71);outportb(0x70,0x0b);outportb(0x71,c&0xbf);
/*bit D6 pe 0 - enable periodic interrupt*/
}

EX6
/* informatii despre memorie*/
//=======================================

#include <dos.h>
#include <stdio.h>
#include <conio.h>

void main(void){

union{
struct {
unsigned char octetl, octeth;
} oct; //ATENTIE LA ORDINE!!
unsigned int var;
} vv;
clrscr();
/*dimensiunea memoriei de baza*/

outportb(0x70,0x15);
vv.oct.octetl=inportb(0x71);
outportb(0x70,0x16);
vv.oct.octeth=inportb(0x71);
printf("dimensiune memorie de baza -citit din CMOS:");
printf("%d KB\n", vv.var);

/*dimensiunea memoriei de baza disponibila pentru DOS si aplicatii*/


/*varianta 1- cu PSP*/
vv.var=peek(_psp, 0x0002);//paragrafe
printf("\ndimens mem de baza disp pentru DOS si apli- cu PSP:%dKB\n",
vv.var/64);

/*varianta2- din zonaBIOS*/


vv.var =peek(0x0000,0x0413);
printf("dimens mem de baza disp pentru DOS si apli- cu zonaBIOS:%dKB\n",vv.var);

/*varianta 3- cu INT12H*/
geninterrupt(0x12);
printf("dimens mem de baza disp pentru DOS si apl. -cu INT12H:%dKB\n", _AX);

/*disponibil pentru aplicatie*/


vv.var=peek(_psp, 0x0002); //paragrafe
printf("\n mem de baza disp pentru apl. :%dKB\n", (peek(_psp,0x002)-_psp)/64);
/*memorie extinsa*/
outportb(0x70,0x17);
vv.oct.octetl=inportb(0x71);
outportb(0x70,0x18);
vv.oct.octeth=inportb(0x71);
printf("\nmemorie extinsa -citit din CMOS:");
printf("%u KB\n", vv.var);

}
DOS.H6) Functii diverse
unsigned FP_OFF(farpointer);
unsigned FP_SEG(farpointer);
void far *MK_FP(unsigned seg,unsigned ofs);

void delay(unsigned miliseconds);


void sleep(unsigned seconds);
Facilităţi BIOS.H
int bioskey(int cmd); - vezi INT16H
cmd = 0 citire tastaturã;
returnează:
Cod Scan Cod ASCII
Sau
Cod ASCII Ext 0

cmd = 1 testeazã dacã au fost apăsate taste şi codul lor este disponibil
returnează: 0 – nu sunt taste disponibile
nenul – sunt taste disponibile – pot fi citite
la urmatorul apel bioskey folosind cmd=0
-1 : s-a apasat Ctrl+Brk

cmd = 2 cere starea tastaturii


returneaza: - valoarea 1 pentru tasta apăsată
D7 D6 D5 D4 D3 D2 D1 D0
INS Caps Numlock Scroll ALT Ctrl Shift - Shift-
stanga dreapta
int bioscom(int cmd,char abyte,int port);

port =1 pentru COM2, port=0 pentru COM1

cmd = 0 seteazã parametrii de comandã cu valoarea din abyte;


Semnificaţie abyte:
D7 D6 D5 D4 D3 D2 D1 D0
=111 -9600bauds =0 impar =0 - 1 bit stop =0 - 7biti date
=110 – 4800bauds =1 par =1 –2 biti stop =1 – 8 biti date
etc
cmd = 1 trimite caracterul din abyte pe linia de comunicatie;
cmd = 2 recepţioneazã un caracter;
returneaza:
Octet stare Caracter recepţionat
(toti bitii să fie pe 0!!)
cmd = 3 returneazã starea curentã a portului de comunicatie
dacă se aşteaptă recepţie caracter: testeaza dacă D8=1 (data ready)
int biosprint(int cmd,int abyte,int port);
port =1 pentru LPT2, port=0 pentru LPT1

abyte intre 0 şi 255


cmd = 0 tipãreste la imprimantã caracterul din abyte;
cmd = 1 initializeazã imprimanta;
cmd = 2 citeste starea imprimantei
returnează:
D7 D6 D5 D4 D3 D2 D1 D0
Imprimanta Semnal =1 =1 =1
libera achitat Out of paper eroare IO time out
I. 3. 2. Tehnici de programare în intreruperi

Instalare rutine proprii


1. citire vector intrerupere current (getvect)
2. instalare rutina proprie cu setvect: rutina este de tip interrupt
--------
3. reinstalare rutina veche pentru tratare cerere întrerupere (setvect)

Nu se vor prelua exceptiile (INT 0H, INT1H, INT3H, INT4H) si NMI (INT2H)
Scrierea rutinelor proprii

- sunt functii care nu returneaza nimic si care nu primesc argumente intrare


⇒ comunicare cu main() prin variabile globale

- variabilele globale sunt resurse partajabile


⇒ accesul concurent se rezolva prin excludere mutuala:
• se marcheaza sectiunile critice si se pozitioneaza eventual
IF=0 pe durata sectiunii critice;
⇒ accesul concurent se poate evita
- modificand variabila globala dintr-o singura functie; celelalte
functii au acces doar la citire;
- utilizand variabile de urmarire in main() care nu sunt modificate
de rutinele de tratare
- pentru a evita probleme de reentranta: -
⇒ se pot folosi functii disable()/enable()
⇒ nu acceseaza in rutinele de tratare alte dispozitive fizice:
interzisa scriere pe disc sau monitor, etc – aceste operatii se vor executa
in main()- pentru validare operatii se vor utiliza flaguri
⇒ nu se recomanda schimbarea modului de lucru la 8259
⇒ nu se recomanda setarea IF=1 in rutina de tratare

- daca rutina trateaza o cerere de intrerupere hardware – trebuie inclusa achitarea


intreruperii la 8259 la FINAL!!

- daca este posibil sau necesar se vor asigura vechile servicii


(*oldvect)()
Cand se lucreaza în întreruperi si cand prin interogare?
Conteaza:
o daca este un eveniment periodic /aperiodic
- tastatura lucreaza bine in intreruperi, nu prin interogare

o care este frecventa de aparitie a cererii de întrerupere (pentru evenimente


periodice)
- portul serial poate lucra bine si in intreruperi si prin interogare
- la aplicatiile audio/video: frecventa mare de achizitie

o timpul de executie al rutinei de tratare

Eficienta = frecventa * texecutie


Ex: intreruperea de ceas:
18 intreruperi/sec * 0.1msec = 1.8 msec – 0.18% - convenabil
Restricţii de timp
Atentie la timp executie, timp raspuns la intrerupere

Executie

T1 T2
T1: cererea sta în coada la 8259
T2: dupa ce cererea este acceptata de procesor: salvare registri, formare
adresa rutina
Observatii
- implementare asteptare in main()
- testare “tasta calda”
- variabile de urmarire

Implementare asteptare + operatii periodice – cu variabile de urmarire

#include <stdio.h>
#include <conio.h>
#include <dos.h>

void interrupt (*oldvect)();


void interrupt ceas(void);

int CONTOR=20;
int flag;

void main(void)
{
clrscr();
oldvect=getvect(0x1c);
setvect(0x1c,ceas);

while (flag!=4)
if (CONTOR==0)
{
>>>>>>>>>>>>>
CONTOR=20; //nerecomandat --> sectiune critica
flag++;
printf("flag=%d\n",flag);
}
setvect(0x1c,oldvect);
}

void interrupt ceas(void){


CONTOR--;
}
Preferabil ???:
#include <stdio.h>
#include <conio.h>
#include <dos.h>

void interrupt (*oldvect)();


void interrupt ceas(void);

int CONTOR=20;
int flag, cflag;

void main(void)
{
clrscr();
oldvect=getvect(0x1c);
setvect(0x1c,ceas);
while (flag!=4)
if (cflag==1)
{
cflag=0;
flag++;
printf("flag=%d\n",flag);
}
setvect(0x1c,oldvect);
}

void interrupt ceas(void){


CONTOR--;
if (CONTOR==0)
{
CONTOR=20;
cflag==1;}
}
Preferabil:
#include <stdio.h>
#include <conio.h>
#include <dos.h>

void interrupt (*oldvect)();


void interrupt ceas(void);

int CONTOR=20;
int flag;

void main(void)
{
int ancflag=flag;
clrscr();
oldvect=getvect(0x1c);
setvect(0x1c,ceas);

while (flag!=4)
if (ancflag!=flag)
{
ancflag=flag;
printf("flag=%d\n",ancflag);
}
setvect(0x1c,oldvect);}

void interrupt ceas(void){


CONTOR--;
if (CONTOR==0)
if (flag<4)
{ //sunt numarate 4 perioade de cate 55msec*20=1.1sec
//valorile lui flag vor fi:
// 1.1 sec - valoarea 0
// urmatoarele 1.1 sec - valoarea 1
// urmatoarele 1.1 sec - valoarea 2
// urmatoarele 1.1 sec - valoarea 3
// apoi - valoarea 4 ==>iesire din program
flag++; CONTOR=20;
}}
#include <stdio.h>
#include <conio.h>
#include <dos.h>
void interrupt (*oldvect)();
void interrupt ceas(void);
int CONTOR=20;
int flag;

void main(void)
{
int flag_urm=-1;
float rez;
float u[4]={0.1, 0.2, 0.3,0.4};
float y[4]={1,2,3,4};

clrscr();
oldvect=getvect(0x1c);
setvect(0x1c,ceas);
while (flag<4)
if (flag_urm!=flag)
{
flag_urm=flag;
rez=u[flag_urm]+y[flag_urm];
printf("flag=%d, suma u+y=%f\n",flag_urm,rez);
//disable();
//……………………
//rez=u[flag]+y[flag];
//printf("flag=%d, suma+y=%f\n",flag,rez);
//…………
//enable();
}
setvect(0x1c,oldvect);
}
Implementare asteptare - cu sau fara flag

#include <stdio.h>
#include <conio.h>
#include <dos.h>

void interrupt (*oldvect)();


void interrupt ceas(void);

int CONTOR=20;
int flag;

void main(void)
{
clrscr();
oldvect=getvect(0x1c);
setvect(0x1c,ceas);

while (flag!=1);//asteptare
sau
while (CONTOR);//asteptare

//execut operatii ce au necesitat asteptarea impusa


……….

setvect(0x1c,oldvect);
}

void interrupt ceas(void){


CONTOR--;
if (CONTOR==0)//daca se foloseste flag!!
flag=1;
}
Implementare “tasta caldǎ”
//ALT+H
void interrupt scrie(void)
{
char c;
c=inportb(0x60);
if((c&7F)==0x38) flag=1;//ALT
else if(c==0x23 && flag==1) flag_cald=1;//H
else flag=0;
(*int9vechi)();
}

ATENTIE: outport, outportb!!!!


Exercitii

1. Găsiţi 2 greşeli, corectati programul (facând si modificarea de a folosi INT08H) şi


precizaţi intervalul de timp pentru care se asigură întârzierea, în varianta corectată.
#include <stdio.h>
#include <dos.h>
void interrupt (*old1c)();
void interrupt new1c(void);
void main(void){
int CONTOR=100;
old1c=getvect(0x1c);setvect(0x1c,new1c);
while(CONTOR);
setvect(0x1c,old1c);}
void interrupt new1c(void){
CONTOR--;
printf(“CONTOR=%d”,CONTOR);}
2. Scrieti instructiunile C care asigura:
- demascarea IRQ14
- mascarea IRQ6
- citirea adresei rutinei de tratare corespunzatoare IRQ10
- stergerea bitului mai prioritar din registrul ISR al circuitului 8259 master, stiind ca
este instalat modul de lucru fara achitare automata, folosind mod nespecific, fara
rotire de prioritati;
- afisarea vectorului de intrerupere curent pentru rutina de tratare a intreruperii de
ceas 1CH;
- instalarea unei rutine proprii de tratare a intreruperii de tastatura care sa seteze
variabila globala flagtast pe 1 dupa apasarea de trei ori a tastei ESC (cod scan
0x01);
- instalarea unei rutine proprii de tratare a intreruperii software de ceas care sa seteze
o variabila globala flagceas pe 1 dupa scurgerea a 1.1 minute de la instalarea sa
(se vor considera setarile implicite BIOS);
- generarea intreruperii BIOS 0x15H cu inscrierea prealabila a registrului AH=0x2;
3. Indicati doua variante de separare pe segment:offset (fiecare pe 16b) pentru adresa
fizica F5678H

4. Scrieti un progam C care sa permita afisarea adresei rutinei de tratare a intreruperii


INT09H si adresa la care se gaseste vectorul de intrerupere

5. Explicati de ce este necesara salvarea registrilor inainte de executia rutinei de


tratare a INT08H?

6. Scrieti o functie C care sa permita initializarea portului serial, folosind INT14H,


functia 00H. Trebuie ca inainte de generarea întreruperii:AH = numărul funcţiei, DX
= 0 sau 1 (nr port COM), AL = parametri initializare; la iesirea din rutina de tratare a
intreruperii: AX- stare.
7. 80x86 executa o rutina de tratare a unei intreruperi INT09H. Indicati dacă pe
durata executiei acestei rutine el poate implicit accepta o alta cerere de intrerupere
externă mascabilă şi argumentaţi răspunsul. Cum ar putea fi modificat acest lucru?
Exemplificati pe o rutina proprie generica void interrupt new(void) de tratare a
unei intreruperi, care nu trebuie sa modifice altceva faţă de serviciile vechi.
Se pot accepta pe durata executiei acestei rutine alte cereri de la INT09H?
Dar de la INT08H?
Partea a II a: Aplicatii de timp real multitasking
MPLAB

1. Noţiuni introductive privind aplicaţiile multitasking de timp real RTOS

Placa dezvoltare cu
DsPIC33

MPLAB

2. Dezvoltarea aplicaţiilor de timp real sub FreeRTOS FreeRTOS

Placa dezvoltare cu
DsPIC33

MPLAB

3. Utilizarea proceselor de tip ISR in aplicatii multitasking – FreeRTOS

Placa dezvoltare cu
particularizare dsPIC 33, C - MPLAB DsPIC33
MPLAB

RTOS

Placa dezvoltare cu
MPLAB DsPIC33
4. Planificarea in RTOS clock driven C

FreeRTOS

Placa dezvoltare cu
DsPIC33
II.1. Noţiuni introductive privind aplicaţiile
multi-tasking de timp real
 Sistem numeric:
= hardware (resurse) + sistem de operare + aplicaţie utilizator (rutine/metode)

 Sistem de timp real - un sistem care trebuie să răspundă unor restricţii de timp
prestabilite

Hardware -ul asigura răspuns rapid la evenimente externe, sincronizare


temporală, siguranţă

Aplicaţia dezvoltată multitasking – procese modulare, cu priorităţi diferite

Sistemul de operare oferă anumite facilităţi suplimentare – accent pe răspuns


rapid la evenimente externe, partajare resurse, executie multitasking
(comutare de la un task la altul + planificare & alocare)
- resurse

o active: procesoare – asigura execuţia instrucţiunilor

o pasive: periferice (DA, AD, PWM, numaratoare), linii de comunicatie,


locatii memorie (variabile)
+

procese care lupta pentru ocuparea resurselor (implementate de programator)


-
o Task = proces care implementeaza o anumita facilitate a aplicatiei

o ISR = proces prin care se raspunde unei cereri de intreruperi lansata de un


dispozitiv periferic

- sistemul de operare de timp real – asigura servicii pentru execuţia proceselor şi


ocupare/eliberarea resurselor in sensul respectării restrictiilor de timp
← procesele trebuie să coopereze pentru a produce rezultate

← procesele lupta pentru ocuparea resurselor: algoritmi de planificare si


alocare

← procesele ocupa

cel putin o resursa activa (procesor, server)

eventual anumite resurse pasive (memorie, etc)

se considera ca o resursa pasivă poate fi ocupata de un singur


proces la un moment dat (acces exclusiv)

după ce a ocupat o resursa, un proces poate ceda temporar resursa


unui alt proces mai prioritar (acces preemtiv)
Ocuparea resurselor active/pasive este tratată distinct se SOTR

1. Ocuparea resurselor active:

Caz multiprocesor – planificare si alocare >> procese concurente


CAND (planificare) şi UNDE (alocare) se execută taskurile (procesoarele
vor comunica prin mesaje sau zone partajate de memorie)
Caz nedetaliat în curs!!!!!!

Caz monoprocesor - planificare:


CAND se execută taskurile

o pentru taskuri exista un arbitru care decide ce task preia procesorul:


planificatorul (componenta a SOTR);

o pentru ISR arbitrul este de obicei un controller hard, nu o componenta a


SOTR;
Motontasking Multitasking
ISR
ISR
while(1){ while(1){
if() operatii task 1
if(…) operatii task 2 }
etc Task3
} Task1
Task2

Avantaje abordare multitasking:


- Extensie simpla – adaugare taskuri T2 T1 T1T2 T2 T2T3 T3
- Prioritati diferite
- Temporizari & restrictii de timp mai
flexibil de gestionat, independent
pentru fiecare task
SOTR asigura comutarea corecta de la un proces la altul (comutarea
contextelor)

Context task

= conţinut regiştri + adresa de început task + stare task +adresa stivei iniţiale +
var. specifice ale SOTR

orice proces trebuie executat consistent, indiferent ce comutări de


contexte au loc
2. Ocuparea resurselor pasive:

o Resursele sunt partajabile intre procese

o Un proces care ocupa resursa trebuie să îşi poată finaliza în timp finit
operaţiile, fără a pierde consistenţa acestora

o Un proces mai puţin prioritar nu trebuie să întârzie un alt proces mai


prioritar la primirea resursei (inversare de prioritate)

o Procesele nu trebuie să se blocheze în aşteptarea resurselor (deadlock)


II. 1.1. Detalii despre ocuparea resurselor active in SOTR
II. 1. 1. 1. Clasificare taskuri

1) după mod activare

o periodice, deterministe – activate cu regularitate


 la fiecare activare: citesc stare sistem, execută calcule, trimit comenzi
de afişare-modificare stare sistem
Ex: aviaţie - ajustarea poziţiei supapei rezervorului de combustibil în
funcţie de puterea solicitată; automobilism – verificare blocare roţi;
achiziţie date; control periodic; refresh DRAM.

o aperiodice – activate când anumite evenimente au loc


 pot fi critice (cu deadline ferm - sporadice) sau obişnuite (fără
deadline ferm - aperiodice)
Ex: reconfigurarea sistemului de control atunci când anumite anomalii sunt
detectate; operaţii de întreţinere; înregistrare de evenimente.
2) după importanţă
o critice – hard deadline - uzual : periodice
o esenţiale – deadline ferm, important
o neesenţiale – deadline-ul poate fi încălcat fără efecte imediate

⇒ necesitate considerare priorităţi pentru taskuri


II. 1. 1. 2. Starile posibile ale unui task

in curs de executie
(“running”)
Cere asteptarea
unui eveniment Se termina de
executat
Pierde Primeste
procesorul procesorul
in asteptare (“preempt”) (“start)
(“waiting”) suspendata

Evenimentul asteptat pregatita pentru activata


a avut loc executie (“ready”)

Pot exista diferente intre SOTR cu privire la modul de trecere del a o stare la alta sau
denumirea stării
II. 1. 1. 3. Moduri execuţie a taskurilor

o preemtiv

un proces poate ceda procesorul altui proces atunci cand planificatorul


decide acest lucru, fără a i se cere acordul:

activare T1 Terminare T1

T1
suspendat in executie suspendat

v
in executie “ready” in executie
T2
o nepreemtiv

un proces nu cedează procesorul altui proces activ, fără acordul său


activare T1

T1
suspendat ready” in executie

v
in executie suspendat
T2
Terminare T2

>> pot fi admise moduri mixte: unele procese preemtive, altele nepreemtive
II. 1. 1. 4. Planificator

Planificatorul (scheduler) monitorizează coada de taskuri ready şi stabileşte ce task


trece executie.

Elemente ce definesc planificatorul:

o cand se activează planificatorul (când poate decide comutarea)

clock driven:
planificatorul se activează periodic (o perioadă = frame)
+ procesul in executie cere acces la planificator, se termina sau intra in
asteptare

event driven:
planificatorul se activează cand apare un eveniment ce modifică coada
ready
+ procesul in executie cere acces la planificator, se termina sau intra in
asteptare
o algoritmul de planificare – cum alege taskul câştigător:
 uzual pe bază de prioritate
 priorităţile pot fi statice (un task are aceeaşi priroitate pe durata
execuţiei aplicaţiei) sau dinamice (un task îşi modifică prioritatea)
• la multe SOTR: statice (stabilite de designer)
II. 1. 1. 5. Execuţia ISR

Atentie: ISR nu sunt supervizate de planificator!

Cererile nu sunt stocate in coada ready

Uzual ISR sunt gestionate pe nivele de prioritate superioare:


un ISR intrerupe orice task daca IF permite

Un ISR poate intrerupe un ISR mai putin prioritar daca IF permite


II. 1.2 Restricţiile de timp – definiţii utile

 Restricţia de timp - cerinţa de a executa o operaţie după ce sunt îndeplinite


anumite condiţii şi înainte de un termen prestabilit

( I d , t release , t exec , p I d ,t deadline )

cu t exec < t deadline − t release ,

p perioada de activare a proceselor periodice


Id – identificator task
interval in care procesul
trebuie sa se execute
(occurance interval, feasible interval)

Di
(deadline relativ)

procesi timp

ri di
activare ei termen limita
(release) (executie) (deadline absolut)

li Taski
rezerva
(laxity, slack)
intarziere
(tardeness)

Taski
Timpul de activare ri
poate incorpora şi restricţii de precedenţa (procesul se poate executa doar dacă
alte procese s-au executat)

Timpul de executie ei
este calculat pentru un proces care se executa fara a fi intrerupt-preemtat, avand
toate resursele necesare disponibile si toate restrictiile de precedenta indeplinite
Depinde de viteza resurselor active
Nu depinde de secventa de executie a taskurilor
Poate fi diferit daca exista instructiuni conditionale,
daca se foloseste memorie cash
ei ∈ [ei− , ei + ] >> se va considera in analiza ei +
Obs:
In aplicatiile de timp real exista multe procese periodice
Aplicaţii on-line: Tdeadline < ∞ ; aplicaţii off-line: Tdeadline → ∞

Tipuri restrictii de timp


• hard (ferme)
• soft (mai putin severe, optionale)

Functia de utilitate
Indica daca este utila executia procesului chiar si dupa expirarea termenului limita
utilitate

intarziere

0
Timp = resursa STR ⇒ corectitudine în funcţionarea STR

Alte restricţii ce trebuie îndeplinite:


 Performanţe impuse pentru anumite operaţii
 Fiabilitate STR
 Restricţii privind accesul la resursele comune limitate şi partajabile:
procesoare, dispozitive IO, baze date, resurse reţea comunicaţie

De multe ori SOTR oferă doar ajutor in gestionarea resurselor


active/pasive conform prioritatilor proceselor

Respectarea restricţiilor ramâne in sarcina designer –ului!!!!


Respectarea restrictiilor de timp se verifica pe cazul cel mai nefavorabil

Acesta este greu de gasit la sisteme cu multe procese, principalele dificultăţi


fiind: determinarea ei + , ri, lucru cu procese aperiodice sau sporadice, intârzieri,
situaţii neprevăzute
Plan = secventa/ordinea in care se vor executa taskurile

Plan valid (corect) – asigura respectarea conditiilor:

• Un procesor are asignat un singur job la un moment dat

• Un proces este asignat maxim unui singur procesor la un moment dat

• Niciun task nu incepe inainte de ri

• Restrictiile de precedenta si de utilizare a resurselor sunt indeplinite

• Un task ocupa procesoarele maxim un timp egal cu ei

Plan admisibil (“feasible”) – plan valid ce asigura respectarea deadline-urilor


II. 2. Dezvoltarea aplicaţiilor de timp real sub
FreeRTOS
II. 2.1. Facilităţi FreeRTOS - descriere generală:
- SOTR open source, dezvoltat sub C
 se asigura fiabilitate ridicată + stabilitate (există şi versiunea
SAFE RTOS);
 există companii dedicate în a oferi suport;
 aplicaţiile nu sunt open source, dar există multe exemple oferite
gratuit.

- compatibilitate cu diverse arhitecturi, portabilitate.

- servicii generoase de depanare: trace, detectie depasire stiva, kit dezvoltare sub
Win.
- asigura flexibilitate:
nu exista limite pentru nr taskuri, nr prioritati,
taskurile se pot crea dinamic, accepta mod preemtiv-cooperativ (corutine),
pot fi mai multe taskuri pe acelasi nivel de pioritate.

- facilitati diverse pentru gestionarea resurselor pasive si sincronizarea intre procese

Consum redus de memorie

Simplu de înţeles

Uşor de utilizat

- procese acceptate de FreeRTOS: Taskuri + ISR + Corutine

- planificator clock driven


Motontasking Multitasking
ISR
ISR
while(1){ while(1){
if() operatii task 1
if(…) operatii task 2 }
etc Task3
} Task1
Task2

T2 T1 T1T2 T2 T2T3 T3
Avantaje abordare multitasking:
- Extensie simpla – adaugare taskuri - in cele mai multe situatii,
- Prioritati diferite periodic FreeRTOS va decide ce
- Temporizari & restrictii de timp mai task preia procesorul
flexibil de gestionat independent
pentru fiecare task
Distributie

FreeRTOS este compatibilizat cu numeroase arhitecturi/procesoare


>> pentru simplitate toate variantele incluse în kit
(fiind disponibile printr-un singur download)

Structura directoare

FreeRTOS
¦
+-Demo
¦ ¦
¦ +-Common Fisiere demo commune tuturor arhitecturilor.
¦ +-Dir x Fisiere demo specifice arhitecturii x
¦ +-Dir y Fisiere demo specifice arhitecturii y
¦
+-Source Cod Kernel
¦
+-Portable cod kernel specific arhitecturii.
Fisierele kernel principale:

tasks.c,
queue.c
list.c \ FreeRTOS\ Source
corutine.c

La laborator se va utiliza particularizarea pentru dsPIC33 + MPLAB

Se pot crea aplicatii proprii, pornind de la demonstrativele oferite, modificand


convenabil fisierele din directorul Demo
(acest mod de lucru permite folosirea setarilor pentru compilare si
linkeditare)

Aplicatiile sunt construite ca proiecte ce cuprind:


- fisierele sursa utilizator (eventual demonstrative)
- fisierele sistemului de operare
Fisierele FreeRTOS din proiect:
Source/Task.c, Source/List.c, Source/Queue.c, Source/Corutine.c
Source/portable/MLPAB/PIC24_dsPIC/ port.c
(particularizari pe arhitectura hardware folosita)
Source/portable/MemMang/heap_1.c (sau heap_2.c sau heap_3.c)
(asigura o schema de memorie heap)
+
Următoarele directoare trebuie să fie în “path”: Source/include ,
Source/portable/MLPAB/PIC24_dsPIC

Fisierele sursa utilizator:


Obligatoriu main.c, pot fi si altele
Optional: Demo/[Processor name]/ParTest/ParTest.c (daca se folosesc functiile
din acest fisier dedicate gestionarii ledurilor conectate pe PORTB
Recomandare - surse plasate in calea Demo/, caz in care următoarele directoare
trebuie să fie în “path”: Demo/Common, Demo/dsPIC_MPLAB
II. 2. 2 Taskuri
- sunt considerate procese independente

int main( void ) Creare Task


{
T1 si T2 Taskurile sunt
….
xTaskCreate(TaskFct,”T1” …); implementate ca
xTaskCreate(TaskFct, “T2”,….); bucla infinita
…..
vTaskStartScheduler();
return 0;
}
void TaskFct( void *pvParameters )
{
for( ;; )
Taskurile nu {
returneaza nimic Corpul task-ului
}
}
Activare
planificator
(+idle task) Va contine intarzieri + alte
asteptari de evenimente
Stările posbile sunt:

- in execuţie (“running”): ocupă procesorul

- gata de execuţie (“ready”): aşteaptă ocuparea procesorului;

In punctele de planificare, planificatorul alege taskul ce va ocupa procesorul


(candidati la primirea procesorului: taskul running, taskurile din lista ready).

- în aşteptare-blocată (“blocking”): aşteapta un eveniment (sosirea unui mesaj,


eliberarea unei resurse, trecerea unui interval de timp, etc) şi nu este considerat
de planificator.

- suspendată (“suspended”): task ce nu mai cere să fie executat, nu va fi


considerat de planificator >> starea este forţată temporar de programator cu
vTaskSuspend
in curs de executie
Blocare prin (“running”)
apelul unor Apel
servicii API vTaskSuspend()
Pierde Primeste
procesorul procesorul
in blocare (“preempt”)
(“blocking”) suspendata
(“suspended”)
Evenimentul asteptat vTaskSuspend()
a avut loc
pregatita pentru vTaskResume()
executie (“ready”)

vTaskSuspend()

vTaskCreate()
Implementare taskuri - generalitati:

- sunt create/sterse dinamic


construire xTaskCreate(), distrugere vTaskDelete().

- prioritatea între 0 şi configMAX_PRIORITIES – 1


(setare configMAX_PRIORITIES în FreeRTOSConfig.h)

- programatorul lansează în main şi planificatorul, care va arbitra ulterior


execuţia taskurilor vTaskStartScheduler

- taskurile sunt implementate ca bucle infinite

- in gestionarea taskurilor, FreeRTOS poate folosi modul preemtiv (toate


taskurile preemtive) sau modul nepreemtiv (toate taskurile nepreemtive)
Cateva servicii API:

vTaskCreate, vTaskDelete - creare/stergere task

xTaskGetCurrentTaskHandle – determinare handler task curent

vTaskSuspend, vTaskResume, vTaskResumeFromISR – forţare stare


“suspendată”/ revenire din starea “suspendată”
un task se poate autosuspenda sau poate suspenda alt task
orice task sau ISR poate cere reactivarea unui task suspendat

vTaskDelay, vTaskDelayUntil – forţare aşteptare pentru un interval de


timp; pe acest interval taskul intră în blocare.

vTaskPrioritySet, uxTaskPriorityGet – modificare / citire prioritate


Observatii:

- taskurile au stiva proprie (dimensiunea stivei este indicată la creare)>> se poate


ajunge la consum mare de memorie, dar există o protecţie a datelor.

- un task poate avea acces doar la stiva proprie (mod User) sau la intreaga
memorie (mod Privileged – folosind Memory Unit Protection)

- orice task poate modifica on-line prioritatea unui alt task sau prioritatea sa

- planificatorul poate fi pornit/oprit de programator cu


vTaskStartSheduler/ vTaskEndSheduler

- modul preemtiv/nepreemtiv este stabilit a priori din FreeRTOSConfig.h


(#define configUSE_PREEMPTION 1) si nu poate fi schimbat on-line
void Task1( void *pvParameters ){
for( ;; ){
int main( void ) …….
{ vTaskDelay( 1000 );
…. }
xTaskCreate(Task1, …); }
xTaskCreate(Task2, ….);
…..
vTaskStartScheduler();
return 0;
} void Task2( void *pvParameters ){
for( ;; ){
…….
vTaskDelay( 2000 );
}
}
+

Setari in FreeRTOSConfig.h:

#define configUSE_PREEMPTION 1
#define configTICK_RATE_HZ ((portTickType)1000)

etc
Folosirea planificatorului FreeRTOS:

- se configurează frame- ul
 >> gestionat intern cu o rutina de tratare a unei intreruperi venite
de la un numărator de timp

- se activează planificatorul.

CÂND planificatorul arbitrează ocuparea procesorului:

In mod preemtiv: la fiecare început de frame (periodic)

Replanificarea asigurată din rutina de ceas a SOTR


Suplimentar, indiferent de modul preemtiv/nepreemtiv, FreeRTOS forteaza
replanificarea dacă:

o taskul in curs de executie intră în suspendare sau blocare (pentru a evita


ocuparea inutilă a procesorului de către un task care nu mai are nevoie de
resursa activă);

o in anumite situaţii “excepţionale”:


 un task de prioritate maxima este creat (se presupune ca se
foloseste un model de memorie care permite crearea dinamica a
taskurilor);
 un task prioritar iese din suspendare.
 un task este sters.

Observatie: crearea/stergerea, suspendarea/revenirea din suspendare


trebuie rar folosite, uzual taskurile sunt create la inceputul aplicaţiei şi
blocate la nevoie
CE TASK preia procesorul la replanificare - algoritm planificare:

o taskul mai prioritar preia procesorul

o daca exista mai multe taskuri pe nivelul maxim de prioritate

 in mod preemtiv se foloseste planificarea de tip “round robin” -


fiecare task primeste pe rand procesorul (câte un frame)

o Exceptie: taskul iddle – vezi detalii in subcapitol urmator

 in mod nepreemtiv, unul din taskurile de prioritate maxima


primeste procesorul, fara a asigura replanificare la fiecare inceput
de frame
Ex: mod preemtiv, priorităţi P_T1 > P_T2 > P_T3

T2 se
blocheaza,
Inceput de frame= punct de Se deblochează T2
punct de replanificare
replanificare
Taskuri ready T2 si
T3
T2 T1 T1T2 T2 T2T3 T3 T3I I T2

T1 intra in blocare, punct de


replanificare
Se deblocheaza T1
Punct replanificare cerut de
T3 care intra in blocare
Se trece la task Idle
Ex: mod preemtiv

T1, T2, T3 taskuri ready, de prioritate egala, maxima

Inceput de frame = punct de


replanificare, se trece la
Inceput de frame = punct de urmatorul task ready de
replanificare prioritate maxima
T1, T2, T3 sunt ready

T1 T2 T3 T1 T2 T3 T1T2 T3 T2 T3

T1 intra in blocare, punct de


replanificare, se trece la
Inceput de frame =
punct de urmatorul task ready de
replanificare prioritate maxima
Ex: mod nepreemtiv

P_T1 > P_T2 > P_T3

T1 se
blocheaza,
T2 in curs de punct de Se deblochează T2
executie, T3 ready replanificare

T2 T2 T2T1 T1 T1T3 T3 T3 I I T2

T2 intra in blocare, punct de


replanificare
Se deblocheaza T1, dar
acesta va sta in lista Punct replanificare cerut de
ready pana la T3 care intra in blocare
urmatorul punct de Task Idle preia procesorul.
replanificare
Ex: mod nepreemtiv

T1, T2, T3 taskuri ready, de prioritate egala, maxima

Un punct replanificare
execeptional, de exemplu un
task mai putin prioritar sters

T1 T1 T1T2 T2 T2T3 T3 T3T4 T4 T4 T4

T1 intra in blocare, punct


de replanificare, se trece la Punct replanificare cerut de
Inceput de frame T3 care intra in blocare
urmatorul task ready de
nu este punct de T4 (cel mai prioritar)
prioritate maxima
replanificare primeste procesorul

Se deblocheaza un task
prioritar T4, dar el va astepta in
lista ready pana la urmatorul
punct de replanificare
Observatii:

- apariţia unui task prioritar în lista ready prin deblocare este considerată doar dacă se
lucrează in mod preemtiv
iesirea din blocare este decisa de rutina de ceas a sistemului de operare,
- replanificarea are loc imediat doar dacă se lucrează in mod preemtiv.
- in mod nepreemtiv, acesta nu este punct de replanificare - taskurile deblocate
prioritare nu pot prelua imediat procesorul

- apariţia unui task prioritar în lista ready prin xTaskCreate sau revenire din starea
suspendata forţează replanificare, indiferent de modul de lucru preemptiv/
nepreemptiv
 acest punct de replanificare este decis de taskul in curs de executie
(este un apel voit la planificator)
 avantaj: tratarea rapida a proceselor prioritare noi sau revenite din
suspendare
Totusi, la introducerea in lista de taskuri ready a unui task nou prin
xTaskCreate sau revenire din starea suspendata, acest task nu poate
preempta un task de aceeasi prioritate.

>> pe mod nepreemtiv, daca taskul nou nu primeste imediat


controlul, el ramane in lista ready până la urmatorul punct de
replanificare (care poate fi peste mai multe frame-uri).
Atentie:

- orice task e implementat ca buclă infinita

 un task prioritar trebuie să intre in blocare pentru a permite


accesul taskurilor mai putin prioritare la procesor

- taskul idle este implicit creat şi trebuie să primească acces la procesor.

 acest lucru este posibil doar cand toate taskurile de prioritate mai
mare sunt in blocare
Procesorul este mereu cedat catre ISR, daca IF permite

o - daca ISR foloseste un serviciu API ce conduce la necesitatea unei


comutări de task, acest lucru este semnalat, pentru a putea forta din ISR
predarea procesorului catre un task diferit de cel care a fost intrerupt.

Replanificarea ceruta de ISR


asigura executia lui T2
T1 este taskul
prioritar ready

T1 T1 ISR T2 T2

Cerere de intrerupere ISR-ul forteaza revenirea din


acceptata, se predă suspendare a lui T2 care este
controlul rutinei de tratare mai prioritar ca T1
ISR cere replanificare
Gestionare frame

Pentru a implementa frame-ul, FreeRTOS foloseste o rutina de tratare a unei


intreruperi venita de la un numarator hardware – numita intreruperea sistem

- rutina este disponibila în port.c


FreeRTOS/source/portable/[compiler]/[microcontroller]/port.c.

Lungimea frame-ului (tick-ului) este stabilita de

#define configTICK_RATE_HZ ( ( portTickType ) 1000 ) // 1 msec.

şi influenţează

o frecventa punctelor de replanificare periodica, pe modul preemtiv

o frecventa punctelor in care se decide deblocarea taskurilor şi rezolutia


intarzierilor asigurate
void __attribute__((__interrupt__, auto_psv)) _T1Interrupt( void )
{
/* Clear the timer interrupt. */
IFS0bits.T1IF = 0;

vTaskIncrementTick();//inclusiv deblocari de taskuri

#if configUSE_PREEMPTION == 1
portYIELD(); //(portasm_dsPIC.s)
// - fortează o comutare de context
#endif
}

In portYIELD()este apelată vTaskSwitchContext:


void vTaskSwitchContext( void )
{
Apelul unor hookuri utile pentru monitorizare
Cedează controlul taskului din capul listei de taskuri ready

}
Atentie, la SOTR trebuie asigurat un compromis intre doua obiective conflictuale:

Eficienta SOTR: interval alocat executiei taskurilor (fără comutare) în unitatea


de timp
Obiectiv: se doreste alocarea unui timp procesor mai mare pentru
executie taskuri, nu pentru comutari de context

Debit: nr. taskuri tratate pe unitatea de timp


Obiectiv: se doreste “avansarea” la cat mai multe taskuri de prioritate
egala, respectiv tratarea cat mai prompta a taskurilor prioritate mai mare
(reducerea timpului de asteptare in coada ready, fara acces la procesor)

>> lungimea frame-ului este esentiala


Serviciile periodice pot fi incluse prin Tick Hook

- un hook = o rutina lansata implicit de SO, într-un anumit context

TickHook se va executa implicit la fiecare frame, dacă configUSE_TICK_HOOK =1

Poate fi scrisa de utilizator folosind prototipul:

void vApplicationTickHook( void );

atentie: functia va fi apelata de o rutina de tratare a unei intreruperi:


sa implice timp executie mic
sa nu foloseasca servicii/operatii nepermise
(alege doar serviciile FreeRTOS care contin sufixul ISR)
Taskul idle

- odata cu planificatorul se activează implicit şi taskul idle, de prioritate 0 (cea


mai mică)

o acest task asigura eliberarea memoriei la distrugerea taskurilor cu


vTaskDelete()
 trebuie să primească procesorul, adică taskurile mai prioritare
trebuie să intre în aşteptare (din când în când)

Observatie: sunt acceptate şi alte taskuri de prioritate 0


(tskIDLE_PRIORITY), desi toate serviciile de aceasta prioritate se
prefera a fi asigurate de Idle Hook

Idle task este implementat ca bucla infinita for (;;){......} - la fiecare ciclu,
apeleaza Idle Hook, care poate fi scrisa de utilizator pentru a implementa servicii
proprii.
for( ;; ){
Verifica daca anumite taskuri au fost sterse, eliberează memoria si
decrementeaza uxCurrentNumberOfTasks

#if ( configUSE_IDLE_HOOK == 1 ){
vApplicationIdleHook();
}
#endif

#if ( mod nepreemtiv ) {


//forteaza o comutare de context (pot exista taskuri disponibile):
taskYIELD();
}
#endif

#if ( ( mod preemtiv ) && ( configIDLE_SHOULD_YIELD == 1 ) ){


//Daca exista un task de prioritate tskIDLE_PRIORITY, ii cedeaza
//procesorul:
taskYIELD();
}
#endif

}
IdleTaskHook

IdleHook este implicit aplelata in taskul idle la fiecare parcugere a buclei for, dacă
configUSE_IDLE_HOOK =1 (in FreeRTOSConfig.h).

Mod de implementare:

Scrie functia conform prototipului:


void vApplicationIdleHook( void );

- aceasta functie poate include facilităţi dorite pentru prioritate minima

- este varianta preferata (fata de optiunea de a crea taskuri suplimentare de


prioritate tskIDLE_PRIORITY )
Ex: mod preemtiv, configIDLE_SHOULD_YIELD =0, P_T1 = P_T2 = 0, P_T3=3

Se vor executa mai multe


cicluri for, la fiecare ciclu
este apelat idle hook

T1 T2 I T1 T3 T3 T3T2 I T1 T2

Punct replanificare cerut de


T1, T2 ready, T3 blocat T3 care intra in blocare
Round rubin
Planificare round rubin
intre T1, T2, idle

Deblocare T3, punct de


replanificare
Ex: mod preemtiv, configIDLE_SHOULD_YIELD =1, P_T1 = P_T2 = 0, P_T3=3
Replanificare ceruta de idle
hook.
Idle hook apelat la T1 va primi procesorul pentru
fiecare ciclu mai putin de un frame.

T1 T2 IT1 T2 T3 T3 T3T1 T2 IT1 T2

Punct replanificare cerut de


T1, T2 ready, T3 blocat T3 care intra in blocare
Round rubin
Planificare round rubin
intre T1, T2, idle

Deblocare T3, punct de replanificare


Replanificare ceruta de rutina cerut de rutina de ceas
de ceas a sistemului de operare
Ex: mod nepreemtiv, ∀ configIDLE_SHOULD_YIELD, P_T1 = 0, P_T2=3

T2 deblocat
Idle hook apelat la
fiecare ciclu for

I T1 T1T2 T2 T2 T2 T2I I I I

T1, T2 blocate Punct replanificare cerut de


T2 care intra in blocare

Punct de replanificare
T1 deblocat cerut de T1 prin blocare

Replanificare ceruta
de idle task
Exemplu

Desenati secventele de ocupare a procesorului pentru cazurile: mod preemptiv cu


configIDLE_SHOULD_YIELD = 0 sau =1, respectiv mod nepreemtiv.

Aplicatia creează taskurile T1 (prioritate 0), T2 (prioritate 1), T3 (prioritate 1), T4


(prioritate 4) si apoi starteaza planificatorul.
Taskul T4 creează un task nou T5, de prioritate 3 (daca acesta nu exista deja), si
apoi se blocheaza pentru 6 frame-uri. Daca taskul T5 exista, executa cateva
actualizari de variabile si apoi se blocheaza pentru 6 frame-uri
Taskul T1 sterge taskul T5 (daca acesta exista), fortează revenirea din suspendare
pentru T3 (daca acesta este suspendat) si se blocheaza pentru 3 frame-uri
Taskul T2 suspendă taskul T3 si se blochează pentru 3 frame-uri
Taskul T3 executa cateva actualizari de variabile si apoi se blocheaza pentru 3
frame-uri
Taskul T5 executa cateva actualizari de variabile si apoi se blocheaza pentru 6
frame-uri
Observatii:

- planul este admisibil (restrictiile de timp sunt respectate) doar daca prioritatile
au fost corect alocate de programator!!!

- algoritmul de planificare nu modifică dinamic priorităţile, pentru algoritmul de


planificare priorităţile sunt date de intrare;

- in anumite situaţii, programatorul poate forţa creşterea/scăderea priorităţilor


pentru anumite taskuri;

- dacă anumite facilităţi trebuie asigurate în rare situaţii (de exemplu,


reconfigurarea unui sistem în cazul detectării unei anomalii), taskul specific
poate fi creat «la nevoie», dinamic, pentru a nu încărca inutil procesorul
(folosind cel putin modelul de memorie heap_2.c).
Detalii implementare a aplicatiilor de timp real cu procese de tip taskuri

Conventii de nume in fişierele kernel

- pentru variabile:
o tip char - prefix c
o tip short - prefix s Exemple:
o tip long - prefix l ucPriority, pcPointerChar
o tip float - prefix f
o tip double - prefix d
o enumeratii - prefix e
o alte tipuri (ex. struct, union) - prefix x
o pointeri - prefix suplimentar p
o unsigned – prefix suplimentar u

-pentru funcţii:
o Serviciu API – prefix în funcţie de tip
o Numele funcţiei începe cu numele fişierului în care apare definiţia
(vTaskDelete - tip void, in task. C)
Tipuri de date predefinite

- vezi portmacro.h (din FreeRTOS\Source\portable\MPLAB\PIC24_dsPIC)

– definitii particularizate pentru fiecare arhitectura


#define portCHAR char
#define portFLOAT float
#define portDOUBLE double
#define portLONG long
#define portSHORT short
#define portSTACK_TYPE unsigned short
#define portBASE_TYPE short

#if( configUSE_16_BIT_TICKS == 1 )
typedef unsigned portSHORT portTickType;
#define portMAX_DELAY ( portTickType ) 0xffff
#else
typedef unsigned portLONG portTickType;
#define portMAX_DELAY ( portTickType ) 0xffffffff
#endif
Pentru a folosi o anumita configuratie a SOTR, sunt necesare setari specifice in
FreeRTOSConfig.h:
#include <p33FJ128MC802.h> // legatura cu arhitectura hardware

#define configUSE_PREEMPTION 1 //toate taskurile sunt preemtive


// = 0 >> nepreemtive

#define configUSE_IDLE_HOOK 1 // cu TaskIddleHook


#define configUSE_TICK_HOOK 0 // fara TickHooh

#define configTICK_RATE_HZ ( ( portTickType ) 1000 )


//frecventa nucleului >> frame de 1 msec
#define configCPU_CLOCK_HZ ( ( unsigned long ) 40000000 ) /* Fosc / 2 */
//ceasul magistralei

#define configMAX_PRIORITIES ( ( unsigned portBASE_TYPE ) 4 )


//nivele de prioritate admise < 4

#define configMINIMAL_STACK_SIZE ( 105 ) // stiva minima 105B


#define configTOTAL_HEAP_SIZE ( ( size_t ) 5120 )
//RAM folosit pentru kernel;
//valoarea trebuie sa permita folosirea
// schemei de alocare a memoriei

#define configMAX_TASK_NAME_LEN ( 4 )
//maxim 4 car. pentru numele unui task
// (inclusiv terminatorul NULL)

#define configUSE_TRACE_FACILITY 0 //fara facilitati de trace


// =1 solicita si rezervarea spatiului
// pentru bufferul de trace

#define configUSE_16_BIT_TICKS 0 // portTickType este unsigned pe 32 biti


// daca =1, e pe 16 biti
// stabileste lungimea constantei pentru contorul soft ce
// numara tickurile, pentru a implementa intarzieri

#define configIDLE_SHOULD_YIELD 1 // valoarea conteaza doar in mod preemtiv,


//pentru planific. taskurilor de priorit. egala cu iddle task
// =1 fortează ca iddle task sa cedeze procesorul imediat ce
// alt task de prioritate 0 este ready
// =0 iddle task >>iddle task ocupa frame-uri intregi
#define INCLUDE_vTaskPrioritySet 1
#define INCLUDE_uxTaskPriorityGet 0
#define INCLUDE_vTaskDelete 0
#define INCLUDE_vTaskCleanUpResources 0
#define INCLUDE_vTaskSuspend 1
#define INCLUDE_vTaskDelayUntil 1
#define INCLUDE_vTaskDelay 1
Detalii servicii API specifice - vezi task.h şi task.c şi validări din FreeRTOSConfig.h

1. Creare/stergere taskuri

typedef void * xTaskHandle;= handler la un task

portBASE_TYPE xTaskCreate(
pdTASK_CODE pvTaskCode, const portCHAR * const pcName,
unsigned portSHORT usStackDepth,
void *pvParameters, unsigned portBASE_TYPE uxPriority,
xTaskHandle *pvTask );

pvTaskCode Pointer la functia asociata taskului


pcName Numele taskului cu nr. maxim de caractere precizat de configMAX_TASK_NAME_LEN.
usStackDepth Adancimea stivei (dimeniunea stivei = adancime x latime, cu latimea = sizeof
(portSTACK_TYPE)) (aici unsigned short)
pvParameters Pointer la parametrii taskului
uxPriority Prioritatea taskului.
pxTask Handler la taskul creat

Returnează : pdPASS pentru executie cu succes, un cod de eroare la executie esuata - vezi projdefs. h – exemplu
#define errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY ( -1 )
void vTaskDelete( xTaskHandle pxTask );

Exemplu: Un task poate fi creat in main


sau in alta functie, doar daca
void vTaskCode( void * pvParameters ){
for( ;; ){
modelul de memorie heap o
//instructiuni diverse permite!!!
} }

void vOtherFunction( void ){


static unsigned char ucParameterToPass = ‘a’;
xTaskHandle xHandle;
xTaskCreate( vTaskCode, "NAME", STACK_SIZE, &ucParameterToPass,
………………………………………………….tskIDLE_PRIORITY, &xHandle );
.......
vTaskDelete( xHandle );
}
xTaskCreate (caz fara Memory Protection Unit)
{
aloca memorie pentru Task Control Block si stiva
fixează vârful stivei si initializează in TCB nume, prioritate, adâncime
stiva, etc

uxCurrentNumberOfTasks++;

if (este primul task)


initilizează lista de taskuri si pxCurrentTCB = pxNewTCB;
else {
adauga taskul in lista de taskuri ready
if( scheduler activat )
if( taskul nou are prioritate maxima)
pxCurrentTCB = pxNewTCB;
}
#if ( configUSE_TRACE_FACILITY == 1 )
{
Asigura servicii de monitorizare specifice
}

dacă planificatorul este activat si taskul este de prioritate maxima


forteaza replanificarea apeland portYIELD_WITHIN_API();
}
#if ( INCLUDE_vTaskDelete == 1 ) {

vTaskDelete
{
Elimina taskul din lista
Actualizează nr. taskuri sterse
Asigura servicii de monitorizare (dacă sunt activate)

if( planificatorul este activ )


fortează o replanificare: portYIELD_WITHIN_API();
}
}

#endif
2. Activare/dezactivare planificator

Initial, planificatorul nu este activ. Activarea este realizata prin apelul

void vTaskStartScheduler( void );

care startează “modulul” de gestionare a frame-ului şi asigură planificarea taskurilor.

Planificatorul este oprit cu

void vTaskEndScheduler( void );

care asigura
- stergerea tuturor taskurilor şi oprirea planificatorului
- oprirea “modulului” de gestionare tick,
- eliberarea resurselor ocupate de SOTR.
Exemplu de utilizare:
void vAFunction( void ) {

// Creează cel putin un task inainte de activarea planificatorului


xTaskCreate( vTaskCode, "TP", STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );

// Start planificator.
vTaskStartScheduler();

// in acest punct se ajunge doar dupa apel vTaskEndScheduler ()

void vTaskCode( void * pvParameters ) {


for( ;; ) { vTaskEndScheduler (); }
}
Implementare vTaskStartScheduler/ vTaskEndScheduler

void vTaskStartScheduler( void )


{

creeaza task idle

xSchedulerRunning = pdTRUE;
xTickCount = ( portTickType ) 0;

configurează frecventa (mai mare) pentru calculul timpilor de executie


(monitorizare)
}

void vTaskEndScheduler( void )


{

xSchedulerRunning = pdFALSE;
vPortEndScheduler();
}
3. Suspendare/ revenire din starea suspendată

void vTaskSuspend( xTaskHandle pxTaskToSuspend );

pxTaskToSuspend Handler la taskul ce este suspendat


NULL - autosuspendare.

void vTaskResume( xTaskHandle pxTaskToResume );


pxTaskToSuspend Handler la taskul ce este reactivat

portBASE_TYPE xTaskResumeFromISR( xTaskHandle pxTaskToResume );


pxTaskToSuspend Handler la taskul ce este reactivat

Returnează: pdTRUE – va fi necesara o comutare de context dupa terminarea ISR


pdFALSE – nu va fi necesară o comutare de context dupa terminarea ISR
Exemplu de utilizare:
Cu INCLUDE_vTaskSuspend =1, INCLUDE_xTaskResumeFromISR =1
xTaskHandle xHandle;

void vAFunction( void ) {


xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );

// ... Restul codului.


}

void vTaskCode( void *pvParameters ){


for( ;; ) {
vTaskSuspend( NULL ); // autosuspendare
}
}

void vAnExampleISR( void ) {


portBASE_TYPE xYieldRequired;
xYieldRequired = xTaskResumeFromISR( xHandle );
if( xYieldRequired == pdTRUE ){ portYIELD_FROM_ISR();}
}
#if ( INCLUDE_vTaskSuspend == 1 ){
void vTaskSuspend( xTaskHandle pxTaskToSuspend )
{
Sterge taskul din lista de taskuri ready si scrie-l in lista de taskuri
suspendate
Asigura servicii de monitorizare
Daca suspend taskul curent, fortează o comutare
}
}
#endif

#if ( INCLUDE_vTaskSuspend == 1 ){
void vTaskResume( xTaskHandle pxTaskToResume )
{
Insereaza taskul in lista de taskuri ready si sterge-l din lista de taskuri
suspendate
Asigura servicii de monitorizare
Daca prioritate acestui task este >= cu cea a taskului curent, asigura
replanificare
}
} #endif
#if ( ( INCLUDE_xTaskResumeFromISR == 1 ) && ( INCLUDE_vTaskSuspend == 1 ) )
{
portBASE_TYPE xTaskResumeFromISR( xTaskHandle pxTaskToResume )
{
Insereaza taskul in lista de taskuri ready si sterge-l din lista de taskuri
suspendate
Asigura servicii de monitorizare
Daca taskul este de prioritate >= prioritatea taskului intrerupt, marcheaza ca
necesara o replanificare
)

}
#endif
4. Modificare prioritati

unsigned portBASE_TYPE uxTaskPriorityGet( xTaskHandle pxTask );

pxTask Handler la taskul la care se citeste prioritatea


NULL – taskul running (citirea propriei prioritati)

Returnează: prioritate task

Cerinta: INCLUDE_uxTaskPriorityGet = 1

void vTaskPrioritySet( xTaskHandle pxTask,


unsigned portBASE_TYPE uxNewPriority );
pxTask Handler la taskul la care se citeste prioritatea
NULL – taskul running (setarea propriei prioritati)
uxNewPriority Prioritatea alocata taskului

Cerinta: INCLUDE_vTaskPrioritySet = 1
Exemplu de utilizare:
Cu INCLUDE_uxTaskPriorityGet = 1, INCLUDE_vTaskPrioritySet = 1

xTaskHandle xHandle;

void vAFunction1( void ) {


xTaskCreate(vTaskCode,"T1",STACK_SIZE,NULL,tskIDLE_PRIORITY, &xHandle);
}

void vAFunction2( void ) {


if( uxTaskPriorityGet( xHandle ) == tskIDLE_PRIORITY )
{
vTaskPrioritySet( xHandle, uxTaskPriorityGet( NULL ) + 1 );
}
}
#if (INCLUDE_vTaskPrioritySet == 1)
void vTaskPrioritySet( xTaskHandle pxTask, unsigned portBASE_TYPE
uxNewPriority ){
if (noua prioritate >= configMAX_PRIORITIES ) {
uxNewPriority = configMAX_PRIORITIES - 1; }
asigura servicii de monitorizare
obtin prioritatea taskului curent (uxCurrentPriority)
if( uxCurrentPriority != uxNewPriority ) {
if( uxNewPriority > uxCurrentPriority ){
if( schimb. de priorit se face pentru alt task decat cel curent)
marchez ca va fi necesara replanificarea;}
else if(schimb. de priorit de face pentru taskul curent)
marchez ca va fi necesara replanificarea;
actualizeaza prioritatea gestionata de mutexuri
modifica prioritatea
if (taskul e ready), pentru reordonarea listei de taskuri ready,
taskul e sters din lista si apoi introdus in pozitia noua
if (a fost solicitata replanificarea} portYIELD_WITHIN_API();
}
}
#endif

#if (INCLUDE_uxTaskPriorityGet = 1)
unsigned portBASE_TYPE uxTaskPriorityGet( xTaskHandle pxTask ){
returnează prioritate taskului pxTask }
#endif
5. Asteptare pe durata unui anumit un interval de timp

void vTaskDelay( portTickType xTicksToDelay );


xTicksToDelay Intervalul de asteptare/ blocare, exprimat in tick-uri

Explicaţii:
taskul va intra imediat in blocare pentru nr. de tickuri precizat;
nr tickuri = interval exprimat in msec / portTick_RATE_MS
Observatie:
În portmacro.h exista
#define portTICK_RATE_MS ( ( portTickType ) 1000 / configTICK_RATE_HZ ),
iar configTICK_RATE_HZ este setat in FreeRTOSConfig.h

Exemplu:
void vTaskFunction( void * pvParameters ) {
const portTickType xDelay = 500 / portTICK_RATE_MS;//astept 500 msec
for( ;; ){
vToggleLED();
vTaskDelay( xDelay );}
}
void vTaskDelayUntil( portTickType *pxPreviousWakeTime,
portTickType xTimeIncrement );

pxPreviousWakeTime Pointer la o variabila care indica momentul la care taskul a fost ultima data
deblocat. Variabila trebuie initializata (de exemplu cu momentul actual de timp
inainte de prima utilizare a functiei). Apoi variabila este actualizata de apelurile
successive ale acestui serviciu API.
xTimeIncrement Taskul va intra in blocare pana la momentul (*pxPreviousWakeTime +
xTimeIncrement), cand se va debloca. Momentul deblocarii este automat memorat
in *pxPreviousWakeTime, pentru utilizari viitoare.
Daca (*pxPreviousWakeTime + xTimeIncrement) este din trecut, taskul nu este
blocat.

Exemplu:
void vTaskFunction( void * pvParameters ){
portTickType xLastWakeTime; const portTickType xPer = 10;
xLastWakeTime = xTaskGetTickCount();
for( ;; ) {
vTaskDelayUntil( &xLastWakeTime, xPer );
// aici - instructiuni diverse.
}}
#if ( INCLUDE_vTaskDelay == 1 )
void vTaskDelay( portTickType xTicksToDelay ){
if( xTicksToDelay > ( portTickType ) 0 ){//e necesara intarzierea
Blocheaza planificatorul
Asigura servicii de monitorizare
Calculeaza momentul de deblocare:
xTimeToWake = xTickCount + xTicksToDelay;
Elimina taskul din lista taskurilor ready
Adauga taskul la lista de taskuri blocate, verificand daca pana la
deblocare nu va aparea overflow pe numarator
- if( xTimeToWake < xTickCount )
Deblocheaza planificatorul
}
Forteaza replanificarea
}
#endif
#if ( INCLUDE_vTaskDelayUntil == 1 )
void vTaskDelayUntil( portTickType * const pxPreviousWakeTime, portTickType
xTimeIncrement ) {
Blocheaza planificatorul
Calculeaza momentul de deblocare:
xTimeToWake = *pxPreviousWakeTime + xTimeIncrement;
Verifica daca e overflow si marchez daca intarzierea poate avea loc
Actualizeaza *pxPreviousWakeTime = xTimeToWake;
if (e necesara intarzierea){
Asigura servicii de monitorizare
Elimina taskul din lista taskurilor ready
Insereaza taskul in lista de taskuri blocate (eventual intaziere cu
overflow la contorul timp)
}
Deblocheaza planificatorul
Forteaza replanificarea
}
#endif
Iesirea taskurilor din starea blocat este decisa din rutina de ceas a sistemului

- la fiecare frame este verificata lista de taskuri blocate si se stabileste care trebuie
trecut in starea ready
void vTaskIncrementTick( void )
{
if( planificator activ si nesuspendat )
{
++xTickCount;
if( xTickCount == ( portTickType ) 0 )//overflow contor de timp
{ xNumOfOverflows++;
Aleg lista de taskuri blocate cu intarzieri ce conduceau la
overflow
}
Alege taskurile care trebuie deblocate
Elimina aceste taskuri din lista taskurilor in asteptare
Trece aceste taskuri in lista de taskuri ready
}
#if ( configUSE_TICK_HOOK == 1 )
vApplicationTickHook();
#endif
}
Asigura servicii de monitorizare
}
6 Alte servicii pentru controlul kernelului

taskYIELD () - macro ce forţează o comutare de context daca planificatorul nu este suspendat:


salveaza in stiva TCB, registri; alege taskul prioritar ready si preda controlul acestuia

taskDISABLE_INTERRUPTS inactivează acceptarea cererilor de întreruperi mascabile (inclusiv


rutina de ceas a sistemului de operare)
taskENABLE_INTERRUPTS activează acceptarea cererilor de întreruperi mascabile

taskENTER_CRITICAL, taskEXIT_CRITICAL - marcheaza sectiuni critice ce nu vor


permite replanificare la inceput de frame
void vPortEnterCritical( void ){
portDISABLE_INTERRUPTS();//inclusiv rutina de ceas SOTR
uxCriticalNesting++;
}
void vPortExitCritical( void ){
uxCriticalNesting--;
if( uxCriticalNesting == 0 ){portENABLE_INTERRUPTS(); }
}
Atentie: nu folosi apeluri de servicii API in secventa critica!!!
void vTaskSuspendAll( void )
– suspendă planificatorul: nu pot avea loc deblocari de taskuri, etc
– nu sunt inactivate ISR-uri, ci anumite servciii oferite de rutina de ceas a sistemului:

→ taskul în curs de execuţie se va executa fără preemtare, până la apelul următor


de xTaskResumeAll (); pâna atunci NU pot fi folosite servicii API care pot
genera schimbare de context.
+
portBASE_TYPE xTaskResumeAll( void )
- revenire din starea suspendata a planificatorului;
Returnează: pdTRUE (e necesara o comutare de context), altfel pdFALSE.

Exemplu
void vTask1( void * pvParameters ){
for( ;; ) {
xTaskSuspendAll ();
………………// nicio comutare de context nu poate avea loc
if( !xTaskResumeAll () ) { taskYIELD (); }
}
}
void vTaskIncrementTick( void )
{
if( planificator activ si nesuspendat )
{
++xTickCount;
if( xTickCount == ( portTickType ) 0 )//overflow contor de timp
{ xNumOfOverflows++;
Aleg lista de taskuri blocate cu intarzieri ce conduceau a
overflow
}
Alege taskurile care trebuie deblocate
Elimina aceste taskuri din ista taskurilor in asteptare
Trece aceste taskuri in lista de taskuri ready
}
#if ( configUSE_TICK_HOOK == 1 )
vApplicationTickHook();
#endif
}
Asigura servicii de monitorizare
}
Numarul de tickuri pierdute pe perioada
cat planificatorul este blocat este
contorizat si folosit pentru actualizarea
corecta a lui xTickCount la
revalidarea planificatorului
7. Servicii APIce oferă informaţii despre SOTR:

xTaskHandle xTaskGetCurrentTaskHandle( void );


- returnează handler la taskul curent

unsigned portBASE_TYPE uxTaskGetNumberOfTasks( void );


- returnează numărul de taskuri create in sistem şi neşterse din memorie
- incrementat de xTaskCreate,
- decrementat de idle task la prima execuţie după un apel vTaskDelete
sau acces direct la citirea si vizualizarea in Watch a variabilei
extern portBASE_TYPE uxCurrentNumberOfTasks;

volatile portTickType xTaskGetTickCount( void );


- returnează nr de tickuri scurse de la activarea planificatorului
sau acces direct la citirea si vizualizarea in Watch a variabilei
extern portTickType xTickCount;

Atentie: Nu este recomandat sa modificati valorile xTickCount sau


uxCurrentNumberOfTasks (desi e posibil), deoarece va fi distorsionata functionarea SOTR.
Folosirea serviciilor API echivalente este preferabilă.
Exemplul 1 Verifica mod preemtiv/nepreemtiv, round rubin, etc

FreeRTOSConfig.h
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H

#include <p33FJ128MC802.h>

#define configUSE_PREEMPTION 0 // sau 1


#define configUSE_IDLE_HOOK 1
#define configUSE_TICK_HOOK 0
#define configTICK_RATE_HZ ( ( portTickType ) 100 )
#define configCPU_CLOCK_HZ ( ( unsigned long ) 40000000
#define configMAX_PRIORITIES ( ( unsigned portBASE_TYPE ) 4 )
#define configMINIMAL_STACK_SIZE ( 105 )
#define configTOTAL_HEAP_SIZE ( ( size_t ) 5120 )
#define configMAX_TASK_NAME_LEN ( 4 )
#define configUSE_TRACE_FACILITY 1
#define configUSE_16_BIT_TICKS 1
#define configIDLE_SHOULD_YIELD 1
#define configUSE_CO_ROUTINES 1
#define INCLUDE_vTaskPrioritySet 1
#define INCLUDE_uxTaskPriorityGet 0
#define INCLUDE_vTaskDelete 0
#define INCLUDE_vTaskCleanUpResources 0
#define INCLUDE_vTaskSuspend 1
#define INCLUDE_vTaskDelayUntil 1
#define INCLUDE_vTaskDelay 1
#define INCLUDE_xTaskGetCurrentTaskHandle 1
#define INCLUDE_xTaskGetTickCount 1

#define configKERNEL_INTERRUPT_PRIORITY 0x01


#endif /* FREERTOS_CONFIG_H */
Main.c
#include <stdio.h>
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "croutine.h"
#include "partest.h"
_FOSCSEL(FNOSC_FRC); // Select Internal FRC at POR
_FOSC(FCKSM_CSECMD & OSCIOFNC_OFF); // Enable Clock Switching and Configure
// FRC + PLL

xTaskHandle handT1; xTaskHandle handT2;

unsigned int varaux1, varaux2, varauxidle;

portTickType no_ticks;

extern portBASE_TYPE uxCurrentNumberOfTasks;

static char t2first;


static void prvSetupHardware( void );

void initPLL(void){
// Configure PLL prescaler, PLL postscaler, PLL divisor
PLLFBD = 41; // M = 43 FRC
CLKDIVbits.PLLPOST=0; // N1 = 2
CLKDIVbits.PLLPRE=0; // N2 = 2
// Initiate Clock Switch to Internal FRC with PLL (NOSC = 0b001)
__builtin_write_OSCCONH(0x01); // FRC
__builtin_write_OSCCONL(0x01);

// Wait for Clock switch to occur


while (OSCCONbits.COSC != 0b001); // FRC
// Wait for PLL to lock
while(OSCCONbits.LOCK!=1) {};
}

static void prvSetupHardware( void ){


vParTestInitialise();
initPLL();
}
void Task1(void *params) {
portTickType xLastWakeTime= xTaskGetTickCount();
for (;;){
varaux1=varaux1+1;
no_ticks=xTaskGetTickCount();
vParTestToggleLED(15);
vTaskDelayUntil( &xLastWakeTime, 45 );
}}

void Task2(void *params) {


portTickType xLastWakeTime; xLastWakeTime = xTaskGetTickCount();
for (;;){
no_ticks=xTaskGetTickCount();
varaux2=varaux2+1;
if (no_ticks <50) {
vParTestSetLED( 14, 0 );
vTaskDelayUntil( &xLastWakeTime, 1 );//un singur ciclu for pe frame
}
if (no_ticks ==50) {
if (uxCurrentNumberOfTasks==2)
xTaskCreate(Task1, (signed portCHAR *) "T1",
configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY+1, &handT1);
vParTestSetLED( 14, 1 );
t2first=1;
vTaskDelayUntil( &xLastWakeTime, 15 );}
if (no_ticks >50){
vParTestToggleLED(14); vTaskDelayUntil( &xLastWakeTime, 10 );}}}
void vApplicationIdleHook( void ) { varauxidle++;}

int main( void ){


/* Configure any hardware required for this demo. */
prvSetupHardware();

xTaskCreate(Task2, (signed portCHAR *) "T2", configMINIMAL_STACK_SIZE, NULL,


tskIDLE_PRIORITY + 1, &handT2);

vTaskStartScheduler();
return 0; Ce se schimba pe
} mod nepreemtiv?
Observatii:
- pe mod preemptiv:
P_T1=P_T2, T1 nu va preempta T2, fiind de aceeasi prioritate (la breakpointul
indicat se va gasi t2first=1 si no_ticks=50).
P_T1>P_T2, T1 va preempta T2 (la breakpointul indicat se va gasi t2first=0 si
no_ticks=50).
Exemplu 2: idem, inlocuind P_ T1=P_T2=P_idle si:
void Task1(void *params) {
portTickType xLastTime = xTaskGetTickCount()-1;
for (;;){
no_ticks=xTaskGetTickCount();
if (xLastTime!=no_ticks){varaux1=varaux1+1;xLastTime=no_ticks;}
if ((no_ticks/15)*15 == no_ticks) {vParTestToggleLED(15);}
}}

void Task2(void *params) {


portTickType xLastTime = xTaskGetTickCount();//la primul frame valoarea este 0
for (;;){
no_ticks=xTaskGetTickCount();
if (xLastTime!=no_ticks){varaux2=varaux2+1;xLastTime=no_ticks;}
if (no_ticks <50) { vParTestSetLED( 14, 0 );}
if (no_ticks ==50) {
if (uxCurrentNumberOfTasks==2)
xTaskCreate(Task1, (signed portCHAR *) "T1",
configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, &handT1);
vParTestSetLED( 14, 1 );}
if (no_ticks >50){
if ((no_ticks/15)*15 == no_ticks) {vParTestToggleLED(14);}
}}}
Q: cu P_T1=P_T2=P_idle, obtin varauxidle≈ varaux1, varaux1+varaux2 =no_ticks.
(executiile pe frame-uri sunt: T2&I, T1, ). Pe modul nepreemtiv se executa doar T2.
Exemple

– demo site:

partest.c – functii dedicate gestionării ledurilor

main.c - start-stop planificator

death.c – creare si stergere taskuri

flash.c – joc cu leduri


FLASH (+ main.c) // joc cu leduri

#include <stdlib.h>

/* Scheduler include files. */


#include "FreeRTOS.h"
#include "task.h"

/* Demo program include files. */


#include "partest.h"
#include "flash.h"

#define ledSTACK_SIZE configMINIMAL_STACK_SIZE


#define ledNUMBER_OF_LEDS ( 3 )
#define ledFLASH_RATE_BASE ( ( portTickType ) 333 )

/* Variable used by the created tasks to calculate the LED number to use, and
the rate at which they should flash the LED. */
static volatile unsigned portBASE_TYPE uxFlashTaskNumber = 0;

/* The task that is created three times. */


static portTASK_FUNCTION_PROTO( vLEDFlashTask, pvParameters ); !
Exista in portmacro.h:
#define portTASK_FUNCTION_PROTO( vFunction, pvParameters )
void vFunction( void *pvParameters )
/*-----------------------------------------------------------*/
void vStartLEDFlashTasks( unsigned portBASE_TYPE uxPriority ){
signed portBASE_TYPE xLEDTask;

/* Create the three tasks. */


for( xLEDTask = 0; xLEDTask < ledNUMBER_OF_LEDS; ++xLEDTask ){

! xTaskCreate( vLEDFlashTask, ( signed char * ) "LEDx", ledSTACK_SIZE, NULL,


uxPriority, ( xTaskHandle * ) NULL );
}
} Exista in portmacro.h:
/*-----------------------------------------------------------*/
#define portTASK_FUNCTION( vFunction, pvParameters )
void vFunction( void *pvParameters )

static portTASK_FUNCTION( vLEDFlashTask, pvParameters ){


portTickType xFlashRate, xLastFlashTime;
unsigned portBASE_TYPE uxLED;
( void ) pvParameters; /* The parameters are not used. */

!
portENTER_CRITICAL();{/* Calculate the LED and flash rate. */
uxLED = uxFlashTaskNumber;
uxFlashTaskNumber++;}
portEXIT_CRITICAL();

xFlashRate = ledFLASH_RATE_BASE + ( ledFLASH_RATE_BASE *( portTickType )uxLED );


xFlashRate /= portTICK_RATE_MS;
xFlashRate /= ( portTickType ) 2;

xLastFlashTime = xTaskGetTickCount();

for(;;)
{
vTaskDelayUntil( &xLastFlashTime, xFlashRate );
vParTestToggleLED( uxLED );//led on
vTaskDelayUntil( &xLastFlashTime, xFlashRate );
vParTestToggleLED( uxLED );//led oFF
}
}
DEATH.C //creare si stergere taskuri

#include <stdlib.h>
#include "FreeRTOS.h"
#include "task.h"

#include "death.h"
#define deathSTACK_SIZE ( configMINIMAL_STACK_SIZE + 60 )

static portTASK_FUNCTION_PROTO( vCreateTasks, pvParameters );


static portTASK_FUNCTION_PROTO( vSuicidalTask, pvParameters );

/* A variable which is incremented every time the dynamic tasks are created.
This is used to check that the task is still running. */
static volatile unsigned short usCreationCount = 0;

/* Used to store the number of tasks that were originally running so the creator

!
task can tell if any of the suicidal tasks have failed to die.
static volatile unsigned portBASE_TYPE uxTasksRunningAtStart = 0;

/* Tasks are deleted by the idle task. Under heavy load the idle task might
not get much processing time, so it would be legitimate for several tasks to
remain undeleted for a short period. */
static const unsigned portBASE_TYPE uxMaxNumberOfExtraTasksRunning = 2;

xTaskHandle xCreatedTask;
!
void vCreateSuicidalTasks( unsigned portBASE_TYPE uxPriority ){
unsigned portBASE_TYPE *puxPriority;

puxPriority = ( unsigned portBASE_TYPE * ) pvPortMalloc( sizeof( unsigned


portBASE_TYPE ) );
*puxPriority = uxPriority;

xTaskCreate( vCreateTasks, ( signed char * ) "CREATOR", deathSTACK_SIZE,


( void * ) puxPriority, uxPriority, NULL );

uxTasksRunningAtStart = ( unsigned portBASE_TYPE ) uxTaskGetNumberOfTasks();

/*From FreeRTOS V3.0 on, the idle task is started when the scheduler is
started. Therefore the idle task is not yet accounted for. We correct

!
this by increasing uxTasksRunningAtStart by 1. */
uxTasksRunningAtStart++;
}
static portTASK_FUNCTION( vCreateTasks, pvParameters ){

const portTickType xDelay = ( portTickType ) 1000 / portTICK_RATE_MS;


unsigned portBASE_TYPE uxPriority;

uxPriority = *( unsigned portBASE_TYPE * ) pvParameters;


vPortFree( pvParameters );

for( ;; ) {

/* Just loop round, delaying then creating the four suicidal tasks. */
vTaskDelay( xDelay );
xCreatedTask = NULL;
xTaskCreate( vSuicidalTask, ( signed char * ) "SUICID1",
configMINIMAL_STACK_SIZE, NULL, uxPriority, &xCreatedTask );
xTaskCreate( vSuicidalTask, ( signed char * ) "SUICID2",
configMINIMAL_STACK_SIZE, &xCreatedTask, uxPriority, NULL );

++usCreationCount;
} pvParameters
}
static portTASK_FUNCTION( vSuicidalTask, pvParameters ){

volatile long l1, l2;


xTaskHandle xTaskToKill;
const portTickType xDelay = ( portTickType ) 200 / portTICK_RATE_MS;

if( pvParameters != NULL ){


/* This task is periodically created four times. Two created tasks are
passed a handle to the other task so it can kill it before killing itself.
The other task is passed in null. */
xTaskToKill = *( xTaskHandle* )pvParameters;
}
else {xTaskToKill = NULL;}

for( ;; ) {
/* Do something random just to use some stack and registers. */
l1 = 2;l2 = 89;l2 *= l1;
vTaskDelay( xDelay );
if( xTaskToKill != NULL ) {
vTaskDelete( xTaskToKill );
vTaskDelete( NULL ); /* Kill ourselves. */
}
}
}
/* This is called to check that the creator task is still running and that
there are not any more than four extra tasks. */

portBASE_TYPE xIsCreateTaskStillRunning( void ){


usLastCreationCount
static unsigned short usLastCreationCount = 0xfff; este o var de urmarire. Se
verifica daca au fost intre timp
portBASE_TYPE xReturn = pdTRUE;
executat CREATOR
static unsigned portBASE_TYPE uxTasksRunningNow;

if( usLastCreationCount == usCreationCount ){xReturn = pdFALSE;}


else { usLastCreationCount = usCreationCount;}

uxTasksRunningNow = ( unsigned portBASE_TYPE ) uxTaskGetNumberOfTasks();


if( uxTasksRunningNow < uxTasksRunningAtStart ){xReturn = pdFALSE;} !
else
if( ( uxTasksRunningNow - uxTasksRunningAtStart ) >
uxMaxNumberOfExtraTasksRunning ){ Au fost sterese prea multe
xReturn = pdFALSE; taskuri
}
else {
/* Everything is okay. */
Taskuri sterse, dar dupa stergerea lor nu s-a
}
executat inca idle task pentru a actualiza
uxCurrentNumberOfTasks
return xReturn;}
II. 2. 3. Facilităţi pentru monitorizare şi debug
II. 2. 3. 1. Functii “trace”

Prin configurari adecvate in FreeRTOSconfig.h se pot activa functii de “trace”

– acestea sunt apelate în anumite contexte, indicând dacă anumite


evenimente au avut loc

– pot fi scrise se programator implementand servicii specifice.

Macro definitie Observatii


traceTASK_INCREMENT_TICK() Apelata din intreurperea tick kernel.
Apelata înainte de a preda procesorul unui task după o replanificare.
traceTASK_SWITCHED_OUT()
pxCurrentTCB conţine handlerul la taskul care predă procesorul.
Apelata dupa ce procesorul este predat unui task, la replanificare.
traceTASK_SWITCHED_IN()
pxCurrentTCB conţine handlerul la taskul care preia procesorul.
traceBLOCKING_ON_QUEUE_ Indica dacă taskul in curs de excutie este pe cale sa treacă in starea blocat la
RECEIVE(pxQueue) citirea dintr-o coadă goală sau solicitarea accesului la un semafor/mutex ocupat.
traceBLOCKING_ON_QUEUE_SEND Indica dacă taskul in curs de excutie este pe cale sa treacă in starea blocat la
(pxQueue) scrierea într-o coadă plină.
traceGIVE_MUTEX_RECURSIVE(pxMutex) Apelata din xSemaphoreGiveRecursive().
traceQUEUE_CREATE() Apelata din xQueueCreate() – succes
traceQUEUE_CREATE_FAILED() Apelata din xQueueCreate() – esec (memorie heap insuficienta).
traceCREATE_MUTEX() Apelata din xSemaphoreCreateMutex() – caz mutexul creat cu succes.
traceCREATE_MUTEX_FAILED() Apelata din xSemaphoreCreateMutex() la esec (memorie heap insuficienta)
traceGIVE_MUTEX_RECURSIVE(pxMutex) Apelata din xSemaphoreGiveRecursive() pe succes
traceGIVE_MUTEX_RECURSIVE_ Apelata din xSemaphoreGiveRecursive() – esec (taskul solicitanr nu a fost
FAILED(pxMutex) proprietarul mutexului).
traceTAKE_MUTEX_RECURSIVE(pxMutex) Apelata din xQueueTakeMutexRecursive().
traceCREATE_COUNTING_SEMAPHORE() Apelata din xSemaphoreCreateCounting() – succes
traceCREATE_COUNTING_SEMAPHORE_
Apelata din xSemaphoreCreateCounting() la esec (memorie heap insuficienta)
FAILED()
Apelata din xQueueSend(), xQueueSendToFront(), xQueueSendToBack(),, etc –
traceQUEUE_SEND(pxQueue)
succes
Apelata din xQueueSend(), xQueueSendToFront(), xQueueSendToBack(), esec
traceQUEUE_SEND_FAILED(pxQueue)
(coada plina)
traceQUEUE_RECEIVE(pxQueue) Apelata din xQueueReceive() , etc – succes
traceQUEUE_RECEIVE_FAILED() Apelata din xQueueReceive(), etc - în caz de esec – coadă goală.
traceQUEUE_PEEK() Apelata din xQueuePeek()
traceQUEUE_SEND_FROM_ISR(pxQueue) Apelata din xQueueSendFromISR() - succes.
traceQUEUE_SEND_FROM_ISR_
Apelata din xQueueSendFromISR() – esec (coada plina).
FAILED(pxQueue)
traceQUEUE_RECEIVE_FROM_
Apelata din xQueueReceiveFromISR() - succes.
ISR(pxQueue)
traceQUEUE_RECEIVE_FROM_ISR_
Apelata din xQueueReceiveFromISR() în caz de esec – coadă goală.
FAILED(pxQueue)
traceQUEUE_DELETE(pxQueue) Apelata din vQueueDelete().
traceTASK_CREATE(pxTask) Apelata din xTaskCreate() daca taskul este creat cu succes.
Apelata din xTaskCreate() dacă taskul nu este creat cu success, din cauză ca nu
traceTASK_CREATE_FAILED()
exista memorie heap suficientă
traceTASK_DELETE(pxTask) Apelata din vTaskDelete().
traceTASK_DELAY_UNTIL() Apelata din vTaskDelayUntil().
traceTASK_DELAY() Apelata din vTaskDelay().
traceTASK_PRIORITY_SET(pxTask,
Apelata din vTaskPrioritySet().
uxNewPriority)
traceTASK_SUSPEND(pxTask) Apelata din vTaskSuspend().
traceTASK_RESUME(pxTask) Apelata din vTaskResume().
traceTASK_RESUME_FROM_ISR(pxTask) Apelata din xTaskResumeFromISR().

! O variantă simplă de extragere informatii pentru debug, fără a folosi direct


variabilele gestionate de FreeRTOS.
Implementarea functiilor proprii de trace:

Functiile trace trebuie sa fie cu timp foarte mic de executie, fara apeluri la sistem.

Cel mai frecvent sunt folosite pentru debug:

o numărarea apelurilor pentru a indica de cate ori “evenimentul” semnalat apare


in STR şi realizarea unor statistici - interesant in special pentru evenimente
greu de stabilit a priori cand au loc
 exemplu: traceTASK_SWITCHED_IN() poate indica numarul
de comutări spre un anumit task, ultimele 10 momente la care a
avut loc comutarea, etc.

o dacă respectivul eveniment nu ar fi trebuit să apară (e un simptom de situaţie


nedorită) – obţinerea unor informaţii suplimentare despre acest caz
 exemplu traceTASK_CREATE_FAILED().
Exemple:

TraceTASK_SWITCHED_OUT() şi traceTASK_SWITCHED_IN() sunt apelate de


vTaskSwitchContext.

In FreeRTOSconfig.h:

#define configUSE_TRACE_FACILITY 1

#define traceTASK_SWITCHED_OUT() vMainMineOut()


#define traceTASK_SWITCHED_IN() vMainMineIn()

1. numarare comutări:

In main.c:
unsigned int nr_switch_T2;

void vMainMineIn(void){
if ( handT2 == pxCurrentTCB ) nr_switch_T2++;
}
2. numarare comutari de la T2 la T1

unsigned int nr_switch_T2, ies_T2;

void vMainMineOut(void){
if ( handT2 == pxCurrentTCB) ies_T2=1;
}
void vMainMineIn(void){
if ( handT1 == pxCurrentTCB && ies_T2==1 ) nr_switch_T2++;
ies_T2=0;
}

3. numarare comutari de la T2 la T2
unsigned int nr_switch_T2, ies_T2;

void vMainMineOut(void){
if ( handT2 == pxCurrentTCB) ies_T2=1;
}
void vMainMineIn(void){
if ( handT2 == pxCurrentTCB && ies_T2==1 ) nr_switch_T2++;
ies_T2=0;
}
4. semnalare cazuri cu mai multe de 2 comutari in cadrul aceluiasi ciclu for

unsigned int nr_cazuri, com_in_for_T2=1, flagT2=0;

void vMainMineOut(void){
static unsigned int urmflagT2=-1;

if ( handT2 == pxCurrentTCB) {
urmflag++;
if(urmflagT2!=flagT2) com_in_for_T2++;

Modificare:
void Task2(void *params) {
portTickType xLastTime = xTaskGetTickCount();//la primul frame valoarea este 0
for (;;){
flagT2++;
no_ticks=xTaskGetTickCount();
if (xLastTime!=no_ticks){varaux2=varaux2+1;xLastTime=no_ticks;}
if (no_ticks <50) { vParTestSetLED( 14, 0 );}
if (no_ticks ==50) {
if (uxCurrentNumberOfTasks==2)
xTaskCreate(Task1, (signed portCHAR *) "T1",
configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY+1, &handT1);
vParTestSetLED( 14, 1 );
}
if (no_ticks >50){
vParTestToggleLED(14);
vTaskDelayUntil(&xLastTime,35);
vParTestToggleLED(14);
vTaskDelayUntil(&xLastTime,35);
vParTestToggleLED(14);
vTaskDelayUntil(&xLastTime,35);
vParTestToggleLED(14);
vTaskDelayUntil(&xLastTime,35); }
}}
Servicii API de trace

Atentie, exista servciile

vTaskStartTrace, vTaskList, ulTaskEndTrace

care pot lista anumite informatii despre taskuri, dar care trebuie folosite cu precautie.

void vTaskStartTrace( portCHAR * pcBuffer, unsigned portLONG


ulBufferSize );
- pregateste modul de lucru

pcBuffer Buffer pentru inscrierea rezultatelor.


ulBufferSize Dimensiunea bufferului in octeti. Monitorizarea este realizata pana la umplerea bufferului
sau un apel ulTaskEndTrace() .
void vTaskList( portCHAR *pcWriteBuffer );
scrie in pcWriteBuffer detalii despre starea curenta a taskurilor (blocata 'B', ready 'R',
stearsa 'D', suspendata 'S'), prioritate si dimensiunea stivei neutilizate (“high
watermark”)

- Atentie: inscrierea informatiilor este marcata intre TaskSuspendAll…TaskResumeAll

Se recomanda folosire foarte rara – la debug, in cazuri exceptionale

unsigned portLONG ulTaskEndTrace( void );


Returneaza: Nr. octeti scrisi in buffer.
Exemplu NERECOMANDAT
portCHAR myBuffer[100];
unsigned portBASE_TYPE uxHighWaterMark;
unsigned portLONG no_byte_in_buffer;

void Task2(void *params) {


portTickType xLastTime = xTaskGetTickCount()-1;//la primul frame valoarea este 0
for (;;){
flagT2++;
no_ticks=xTaskGetTickCount();
if (xLastTime!=no_ticks){varaux2=varaux2+1;xLastTime=no_ticks;}
if (no_ticks <50) { vParTestSetLED( 14, 0 );}
if (no_ticks ==50) {
if (uxCurrentNumberOfTasks==2)
xTaskCreate(Task1, (signed portCHAR *)
"T1",configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY+1,
&handT1);
vParTestSetLED( 14, 1 );
vTaskStartTrace( &myBuffer[0], 100 );}
if (no_ticks >50){
uxHighWaterMark = uxTaskGetStackHighWaterMark( NULL );
vTaskList(&myBuffer[0]);
vParTestToggleLED(14);vTaskDelayUntil(&xLastTime,35);}
if (no_ticks >100) {no_byte_in_buffer=ulTaskEndTrace();}}}
Servicii API recomandate pentru implementarea trace

Anumite servicii pot fi simplu particularizate pentru fiecare taskurile aplicatiei,


folosind taguri:

void vTaskSetApplicationTaskTag( xTaskHandle xTask,


pdTASK_HOOK_CODE pxTagValue );
- asociaza tagul de valoare pxTagValue taskului cu handler xTask
- daca pxTagValue este numele unei functii, aceasta va fi interpretata ca un
hook ce va putea fi apelat prin xTaskCallApplicationTaskHook()

portBASE_TYPE xTaskCallApplicationTaskHook( xTaskHandle xTask,


void * pvParameter );
- apelează hook-ul asociat taskului de handler xTask
xTask Handler la taskul caruia ii corespunde hookul.
NULL pentru taskul in curs de executie.
pvParameter Valoarea pasata functiei hook .

Cerinta: configUSE_APPLICATION_TASK_TAG = 1
Exemplu: RECOMANDAT
#define traceTASK_SWITCHED_OUT() xTaskCallApplicationTaskHook( pxCurrentTCB, 0 )

void vATask( void *pvParameters ) {


vTaskSetApplicationTaskTag( NULL, ( void * ) 1 ); // Asigneaza valoarea tag 1
for( ;; ) {
// corpul taskului
}}

static portBASE_TYPE prvExampleTaskHook( void * pvParameter) {


…………..
return 0;
}

void vAnotherTask( void *pvParameters ) {


vTaskSetApplicationTaskTag( NULL, prvExampleTaskHook );
// Asociază prvExampleTaskHook ca hook
for( ;; ){
/* corpul taskului. */
}}
Exemplu

In FreeRTOSConfig.h:
#define configUSE_APPLICATION_TASK_TAG 1
#define traceTASK_SWITCHED_IN() xTaskCallApplicationTaskHook( pxCurrentTCB, 0 )

In main:

unsigned int nr_cazuri, com_in_for_T2=1, flagT2=0;

static portBASE_TYPE HookT2_In( void * pvParameter) {


static unsigned int urmflagT2=-1;
if(urmflagT2!=flagT2) com_in_for_T2++;
return 0;
}
void Task1(void *params) {
portTickType xLastTime = xTaskGetTickCount()-1;
TaskSetApplicationTaskTag( NULL, 0 );
for (;;){
no_ticks=xTaskGetTickCount();
if (xLastTime!=no_ticks){varaux1=varaux1+1;xLastTime=no_ticks;}
//if ((no_ticks/15)*15 == no_ticks) { vParTestToggleLED(15);}
vParTestToggleLED(15);
vTaskDelayUntil(&xLastTime,15);
}}

void Task2(void *params) {


portTickType xLastTime = xTaskGetTickCount();//la primul frame valoarea este 0
vTaskSetApplicationTaskTag( NULL, HookT2_In );
for (;;){
flagT2++;
no_ticks=xTaskGetTickCount();
if (xLastTime!=no_ticks){varaux2=varaux2+1;xLastTime=no_ticks;}
if (no_ticks <50) { vParTestSetLED( 14, 0 );}
if (no_ticks ==50) {
if (uxCurrentNumberOfTasks==2)
xTaskCreate(Task1, (signed portCHAR *)
"T1",configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY+1,
&handT1);
vParTestSetLED( 14, 1 );}
if (no_ticks >50){
vParTestToggleLED(14);
vTaskDelayUntil(&xLastTime,35);
vParTestToggleLED(14);
vTaskDelayUntil(&xLastTime,35);
vParTestToggleLED(14);
vTaskDelayUntil(&xLastTime,35);
}
}
}
Informatii despre starea planficatorului:

portBASE_TYPE xTaskGetSchedulerState( void );

- returnează starea planificatorului

valori posibile: taskSCHEDULER_NOT_STARTED,


taskSCHEDULER_RUNNING,
taskSCHEDULER_SUSPENDED

sau direct cu variabilele gestionate de FreeRTOS.

static volatile signed portBASE_TYPE xSchedulerRunning


static volatile unsigned portBASE_TYPE uxSchedulerSuspended
II:2.3.2. Determinarea timpilor de executie a taskurilor

Timpii de executie asociati unui task in cadrul unui frame sau pe portiuni de cod pot
fi determinati

+ folosind functiile trace cu traceTASK_SWITCHED_IN()/


traceTASK_SWITCHED_OUT()

+ gestionand un numarator hardware pentru o rezolutie bună.

+ - marcand inceputul /sfarsitul secventei dorite printr-un flag

Aceste servicii sunt deja oferite foarte simplu de FreeRTOS care poate evidenţia
timpii de execuţie ai proceselor ca valoare absoluta şi % din timp total de execuţie.
Necesar: configGENERATE_RUN_TIME_STATS = 1

→ Atunci vTaskStartScheduler() apelează portCONFIGURE_TIMER_


FOR_RUN_TIME_STATS(), pentru a seta o rezolutie mai mică decât cea a
tickului la calcularea timpilor de execuţie.
Setarea este vizibilă prin portGET_RUN_TIME_COUNTER_VALUE()

void vTaskGetRunTimeStats( portCHAR *pcWriteBuffer );

pcWriteBuffer Un buffer în care sunt memorati timpii de executie


(valori ASCII) – spatiu rezervat anterior
(recomandare 40 B per task).

Oferă statistica realizată de FreeRTOS.


Nerecomandată pentru utilizare frecventă (ci doar pentru debug), deoarece dezactivează ISR
pe durata executiei sale
Exemplu

In FreeRTOSConfig.h
#define configTICK_RATE_HZ ( ( portTickType ) 100 )

#define configGENERATE_RUN_TIME_STATS 1
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() ( 0UL )
#define portGET_RUN_TIME_COUNTER_VALUE() 5000UL

In Main.c se modifica
void Task2(void *params) {

portTickType xLastTime = xTaskGetTickCount();//la primul frame valoarea este 0


vTaskSetApplicationTaskTag( NULL, HookT2_In );
for (;;){
flagT2++;
no_ticks=xTaskGetTickCount();
if (xLastTime!=no_ticks){varaux2=varaux2+1;xLastTime=no_ticks;}
if (no_ticks <50) { vParTestSetLED( 14, 0 );}
if (no_ticks ==50) {
if (uxCurrentNumberOfTasks==2)
xTaskCreate(Task1, (signed portCHAR *)
"T1",configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY+1,
&handT1);
vParTestSetLED( 14, 1 );
}

if (no_ticks >50){
uxHighWaterMark = uxTaskGetStackHighWaterMark( NULL );
vTaskGetRunTimeStats( &myBuffer[0] );
vParTestToggleLED(14);
vTaskDelayUntil(&xLastTime,35);

}
}

La prima oprire pe breakpoint, in buffer apare:


T2 timp exec 5000msec, grad ocupare 100%; T1 0, 0%
(Taskul T1 dupa ce a fost creat, nu a primit inca procesorul. S-au scurs 50 tickuri)
II. 2. 3. 3. Monitorizarea utilizării stivei (avertizare în caz de depăşire)

Trebuie setat configCHECK_FOR_STACK_OVERFLOW pe o valoare nenulă.

Se poate particulariza o rutină Hook apelată implicit la fiecare detectare a unei


depăsiri de stivă (parametrii pasaţi indică taskul cu probleme):

void vApplicationStackOverflowHook( xTaskHandle *pxTask, signed


portCHAR *pcTaskName );

-→ programatorul poate oferi servicii specifice, in caz de eroare.


Metode prin care FreeRTOS verifică depăşirea de stivă:

1. Verificare rapidă:
(trebuie configCHECK_FOR_STACK_OVERFLOW = 1)

Depăşirea este verificată doar la comutarea de context din starea running (atunci se
salveaza contextul în stivă şi e un caz nefavorabil). Daca pointerul de varf de stiva
este in domeniul nepermis, este apelat hook-ul de depăşire stivă.

2. Verificare suplimentară:
(trebuie setat configCHECK_FOR_STACK_OVERFLOW = 2)

La crearea taskului, stiva este intializată cu un marcator specific.


La comutarea din starea “in curs de executie” se verifică dacă ultimii 16 octeti.
introdusi in stivă nu sunt modificati de proces. In caz de eroare, este apelat hook-ul
de depăşire stivă.
Nu garantează detectarea tuturor depăşirilor, dar este destul de bună.
Alte servicii API utile

unsigned portBASE_TYPE uxTaskGetStackHighWaterMark( xTaskHandle


xTask );

- returnează nr itemi liberi în stiva; 0 dacă a apărut depăşire de stivă


II. 2. 4 Comunicare intertask şi gestionarea resurselor pasive
II. 2.4.1. Variante de comunicare comunicare intre taskuri

In general, taskurile pot comunica folosind memoria, prin:

- cutie poştală = zonă tampon de dimensiune fixă


dimensiunea mesajului este aprioric stabilită
se cunoaşte adresa cutiei
aceasta zona trebuie tratata ca resursă partajată

- tuburi = coadă FIFO de mesaje (fiecare mesaj de dimensiune fixă)


- acces unidirecţional, parcurgere circulară
- mesajele citite sunt sterse din coada, eliberand spaţiul
- trebuie implementate protectii pentru: citire dintr-o coada goala
scriere într-o coada plină, asteptarea accesului la coada să
implice trecerea in starea de blocare

Scrie-adauga Citeşte şi sterge un


un mesaj mesaj
II. 2. 4. 2. Facilităţi FreeRTOS pentru comunicarea prin tuburi (cozi)

- in FreeRTOS comunicarea prin cozi de mesaje este permisa

o intre taskuri,

o respectiv intre taskuri si ISR

Mesajele sunt copiate prin valoare, nu prin referinta – pentru a evita probleme legate
de consistenta datelor.

Programatorul poate pasa şi mesaje ce au continut de tip pointer, dar validitatea


datelor pointate este în responsabilitatea sa.
Daca un task doreşte să citească o coadă goală/ să scrie într-o coadă plină, atunci
acesta intră in blocare pentru un numar specificat de tickuri (perioada maxim permisa
pentru asteptare poate fi configurata de programator).

La ieşirea din blocare, accesul la coada de mesaje se face pe bază de priorităţi.

Daca nici atunci accesul la coada nu e posibil, serviciul API returnează eroare.

La scriere: mesajul poate fi scris în faţă/la sfârşitul cozii


La citire: mesajul poate fi şters sau nu din coadă.
S- unde se va scrie urmatorul mesaj; R- urmatorul mesaj de citit
P_T1>P_T2>P_T3>P_T4, toate sunt initial ready

M3 M2 M1 Task 1 a scris 3 mesaje in coada, apoi intra in asteptare


S R un interval de timp

Ready: T2, T3, T4


Task 2 a citit 2 mesaje si intra in asteptare un interval
M3 de timp
S R

Ready T3 si T4
Task 3 a scris 2 mesaje in coada (in front), intra in
M3 M4 M5
S R asteptare un interval de timp

Ready: T1, T4
S Task 1 a scris 1 mesaj in coada (back) si doreste sa
M6 M3 M4 M5
R mai scrie unul, dar coada este plina si intra in blocare

Ready T2 si T4
M6 M3 M4 Task 2 citeste un mesaj in intervalul de blocare maxim
R S acceptat de Task 1.

Ready T1, T2, T4


S Task 1 (mai prioritar) a fost deblocat de eliberarea
M6 M3 M4 M7 unui spatiu in coada si scrie mesajul dorit, apoi intra in
R
asteptare un interval de timp.
Ready T2 si T4
Task 2 primeste procesorul si cititeste 3 mesaje, apoi
S M7
R intra in asteptare un interval de timp

Ready T4

S M7 Task 4 citeste un mesaj fara stergere, executa alte


R operatii si pierde procesorul pentru ca Task 2 devine
ready

Ready T2 si T4
Task 2 citeste mesajul, ar dori sa mai citeasca unul dar
S R nu mai exista mesaje in coada si intra in blocare. Se
executa in continuare T4.

Ready T1, T4
M8 Task 1 devine ready si scrie un mesaj in coada, dupa
S R care intra in asteptare un interval de timp.

Ready T2 si T4 Task 2 a fost deblocat de aparitia unui mesaj in coada


si citeste mesajul dorit, apoi intra in asteptare un
S R interval de timp. Se continua cu Task 4.
SAU
Mesajul a fost scris prea tarziu, Task 2 a fost deblocat
M8 fara a mai putea citi mesajul (timeout depasit). Se va
S R
returna cod de eroare la citire, etc
Exemplu:

P_T1>P_T2>P_T3, coada de 2 mesaje (initial vida)


Se deblocheaza T2, dar timeout
T3 citeste un este depasit, nu mai scrie mesajul.
T2 si T1 blocate
mesaj. Se Dupa alte instructiuni se
T3 ready
deblocheaza blocheaza (prin temporizare)
T3 scrie 1 mesaj
T1 T3 citeste un mesaj.

T3 T1T2T3 T3T1 T1T3 T3 T2T3 T3 I I T3

Se deblocheaza T1, T2.


T1 vrea sa scrie 2 T1 se blocheaza
mesaje, dupa prima T2 vrea sa scrie un (temporizare)
scriere se va bloca – mesaj, se blocheaza-
coada plina coada plina
Exercitii: - schitati in pseudocod programul descris mai jos si discutati cum sunt
folosite cozile de mesaje

1. Coada Q1 de mesaje admite maxim 2 mesaje. Q2 admite maxim 3 mesaje


T1 citeste la fiecare 1 sec intrarile PORTB 15, 13, 12 si scrie in Q1 un mesaj -
valoarea intreaga corespunzatoare codului binar B15B14B13.
T2 citeste la fiecare 2 sec codurile din Q1. Daca gaseste o schimbare intre doua citiri
succesive, T2 scrie in Q2 un mesaj - distanta hamming depistata.
T3 verifica la fiecare 6 sec cate mesaje sunt in Q2 si elibereaza coada.

2. Coada Q1 admite maxim 3 mesaje, coada Q2 admite maxim 3 mesaje


La fiecare 1sec, Taskul T1 citeste un mesaj din coada Q1 (scris de T2); daca valoarea
citita este cu cel putin 100 mai mic decat valoarea curenta a contorului de timp real,
modifica starea ledului 15 si trimite un mesaj in Q2 cu momentul actual (tick count).
Altfel trimite in Q2 valoarea din mesajul citit. La fiecare 2sec, Taskul T2 citeste un
mesaj din coada Q2 (scris de T1); daca valoarea citita este cu cel putin 100 mai mic
decat valoarea curenta a contorului de timp real, modifica starea ledului 15 si trimite
un mesaj in Q1 cu momentul actual (tick count).
3.
T2 citeste rezultatul unei convesii AD (iesirea unui sistem automat). T2 verifica daca
eroarea de urmarire pe ultimele 10 tacte a fost intre limitele permise (eroare de
urmarire = referinta – iesire sistem automat) si trimite un mesaj catre T1 prin care
indica în cate tacte (din cele 10) conditia nu a fost indeplinita si % maxim de depasire
a pragului impus. In functie de datele citite, T1 decide ce reconfigurari trebuie
considerate:
- reconfigurat modelul (T4 cu texec = 6 frame-uri) si apoi reconfigurare
algoritm reglare (T3 cu texec = 1 frame)
- declansare procedura avarie
Pe durata unei reconfigurari , mesajele venite de la T1 sunt neglijate de T2.
Dupa o reconfigurare, T2 sterge complet coada de mesaje pentru a primi mesaje
actualizate la noua configuratie. Procedura de avarie este declansata dupa doua
reconfigurari succesive esuate, efectuate la interval de mai putin de 60 tacte succesive
sau daca erorile semnalate de T1 sunt catastrofale. Reconfigurarea esuata este aceea
pentru care erorile nu rezulta acceptabile (T1 trimite alte solicitari de reconfiguare
dupa 10 tacte).

4. sincronizare T1 si T2: T1 scrie in Q1 si citeste Q2. T2 scrie in Q2 si citeste Q1.


Servicii API pentru lucrul cu cozi de mesaje (vezi queue.h si queue.c)

1. Creare/ştergere coadă

xQueueHandle xQueueCreate(
unsigned portBASE_TYPE uxQueueLength,
unsigned portBASE_TYPE uxItemSize
);
- creează o nouă coadă (alocă spaţiu) + returnează handlerul.

uxQueueLength Nr maxim de mesaje ce pot fi stocate în coada


uxItemSize Nr de biti per mesaj.

Returnează: handler coada (succes); 0 (esec)

void vQueueDelete( xQueueHandle xQueue );


sterge coada-eliberează memoria

xQueue Hanlder la coada


Exemplu de utilizare
struct AMessage {
portCHAR ucMessageID;
portCHAR ucData[ 20 ]; };
void vATask( void *pvParameters ) {
xQueueHandle xQueueMine;
xQueueMine = xQueueCreate( 20, sizeof( unsigned portLONG ) );
if( xQueueMine == 0 ){
// eroare la creare
}
}
void vBTask( void *pvParameters ) {
xQueueDelete(xQueueMine); }

2. Numărare mesaje existente în coadă


unsigned portBASE_TYPE uxQueueMessagesWaiting(xQueueHandle
xQueue);

xQueue Handler coară.

Returnează: nr. mesaje stocate in coadă.


3. Scriere mesaje în coadă

portBASE_TYPE xQueueSend(
xQueueHandle xQueue,
const void * pvItemToQueue,
portTickType xTicksToWait
);

- trimite un mesaj în coadă (versiune mai veche);


- atunci când coada este plină, taskul apelant trece in blocare; ieşirea din blocare este
asigurata la eliberarea unui spatiu in coada; taskul poate astepta maxim
xTicksToWait tickuri, interval de timp după care iese din blocare.

xQueue Handler la coada


pvItemToQueue Pointer la mesaj (acesta va fi copiat in coadă prin valoare).
xTicksToWait Numar maxim de tickuri cat taskul poate sta in blocare, asteptand eliberarea unui spatiu in
coadă
Dacă INCLUDE_vTaskSuspend =1 şi xTicksToWait = portMAX_DELAY, taskul
poate ramane blocat oricat de mult timp.

Returnează: pdTRUE (mesaj copiat cu succes, eventual dupa asteptare),


errQUEUE_FULL (mesaj necopiat, inclusiv după asteptare).
Exemplu
struct AMessage {
portCHAR ucMessageID;
portCHAR ucData[ 20 ];
} xMessage;

unsigned portLONG ulVar = 10UL;

void vATask( void *pvParameters ) {

xQueueHandle xQueue1, xQueue2;


struct AMessage *pxMessage;
xQueue1 = xQueueCreate( 10, sizeof( unsigned portLONG ) );
xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
if( xQueue1 != 0 ){
if( xQueueSend( xQueue1, ( void *)&ulVar, ( portTickType ) 10 ) != pdPASS )
{
// eroare de scriere mesaj inclusiv dupa 10 tickuri
}
}
if( xQueue2 != 0 ) {
pxMessage = & xMessage;
xQueueSend( xQueue2, ( void * )&pxMessage, ( portTickType ) 0 );}
}
Servicii mai noi: 2 macrou-ri pentru scriere mesaje la inceput/sfârşit

portBASE_TYPE xQueueSendToToFront(
xQueueHandle xQueue,
const void * pvItemToQueue,
portTickType xTicksToWait
);

portBASE_TYPE xQueueSendToBack(
xQueueHandle xQueue,
const void * pvItemToQueue,
portTickType xTicksToWait
);
4. Citire mesaje din coadă

- cu stergerea mesajului din coadă

portBASE_TYPE xQueueReceive(
xQueueHandle xQueue,
void *pvBuffer,
portTickType xTicksToWait
);

Mesajul este citit prin valoare şi şters din coadă. Spaţiul de memorie unde este copiat după citire
trebuie rezervat de programator. Dacă se găseşte coada goală, taskul intră în blocare pentru maxim
xTicksToWait tickuri.

pxQueue Handler coadă


pvBuffer Pointer la bufferul unde va fi copiat mesajul citit.
xTicksToWait Nr. maxim de tickuri acceptat pentru starea de blocare, daca coada este goala.
xTicksToWait = 0, taskul nu asteaptă.
Dacă INCLUDE_vTaskSuspend =1 şi xTicksToWait =portMAX_DELAY , taskul
poate sta interval nelimitat de timp în blocare.
Returnează: pdTRUE (un mesaj citit cu success), altfel pdFALSE.
Exemplu:
struct AMessage {
portCHAR ucMessageID; portCHAR ucData[ 20 ];} xMessage;
xQueueHandle xQueue;

void vATask( void *pvParameters ) {


struct AMessage *pxMessage;
xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) );
if( xQueue == 0 ) { }
pxMessage = & xMessage;
xQueueSend( xQueue, ( void * ) &pxMessage, ( portTickType ) 0 );}

void vADifferentTask( void *pvParameters ) {


struct AMessage *pxRxedMessage;
if( xQueue != 0 ) {
if( xQueueReceive( xQueue, &( pxRxedMessage ), ( portTickType ) 10 ) ) {
// citire corecta
}
}
}
- fără ştergerea mesajului din coadă

portBASE_TYPE xQueuePeek(
xQueueHandle xQueue,
void *pvBuffer,
portTickType xTicksToWait
);
signed portBASE_TYPE xQueueGenericSend( ....... ){
for( ;; ){
taskENTER_CRITICAL();
if (exista loc liber in coada){
Scrie mesajul nou si actualizeaza pointer scriere;
if (exista un task care asteapta citirea din acesta coada){
deblocheaza taskul din capul listei(trece-l in ready);
forteaza replanificare (taskul deblocat poate fi mai
prioritar);
}
taskEXIT_CRITICAL();
return pdPASS;// succes
}
else{
if( timpul de asteptare este 0 ) {
taskEXIT_CRITICAL();
trace + return esec scriere; } Pot interveni ISR‚ taskuri care
else seteaza timp asteptare sa modifice starea cozii
taskEXIT_CRITICAL();
if (timpul de asteptare nu a expirat)
if (coada este plina)
trece taskul in lista de taskuri blocate la sciere pt aceasta coada
(lista este gestionata pe baza de prioritati)
asigura trace
forteaza replanificare }
else eroare scriere (trace+return)
}}
signed portBASE_TYPE xQueueGenericReceive( ....... ){

for( ;; ){
taskENTER_CRITICAL();
if( coada nu e goala ){
citeste mesajul din capul cozii si sterge-l (eventual);
actualizeaza pointer;
asigura trace;
if (exista taskuri blocate la scriere in coada){
deblocheaza taskul din capul listei
forteaza replanificare;
taskEXIT_CRITICAL();
return pdPASS;}

else if ( timpul de asteptare este 0 ) {


taskEXIT_CRITICAL(); trace + return esec scriere; }
else seteaza timp asteptare
taskEXIT_CRITICAL();

if (timpul de asteptare nu a expirat)


if (coada este goala){
trece taskul in lista de taskuri blocate la citire pt aceasta coada
asigura trace
forteaza replanificare }
else eroare citire (trace+return)
}}
Exemplu:

T1 – perioada 1sec, semnalizare LED 15, scrie mesaj „1” dupa fiecare schimbare a
starii ledului; T2 idem, pentru LED 14, cu perioada 2 sec, mesaj „2”
T3 – perioada 2 sec – verifica daca se executa ok actualizarile de leduri

Pentru verificare >> se poate schimba perioada la T2 la 2.5 sec (simuland intarzieri
nedorita)
#include <stdio.h>
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "croutine.h"
#include "partest.h"
_FOSCSEL(FNOSC_FRC); // Select Internal FRC at POR
_FOSC(FCKSM_CSECMD & OSCIOFNC_OFF); // Enable Clock Switching and Configure
// FRC + PLL
unsigned int flag_corect=1;
unsigned int nr;
xQueueHandle xMineQueue;

void Task1(void *params) {


portTickType xLastTime = xTaskGetTickCount();
char mess=1;
for (;;){
vParTestToggleLED(15);
xQueueSend( xMineQueue, &mess, (portTickType) 10 );
nr++;
vTaskDelayUntil(&xLastTime,1000);
}
}

void Task2(void *params) {


portTickType xLastTime = xTaskGetTickCount();
char mess=2;
for (;;){
vParTestToggleLED(14);
xQueueSend( xMineQueue, &mess, (portTickType) 10 );
nr++;
vTaskDelayUntil(&xLastTime,2000);
}
}
void Task3(void *params) {
portTickType xLastTime = xTaskGetTickCount();
char mess_tipar[3]={1,2,1}; //0:T1, T2,1000: T1; || 2000: T1, T2; 3000: T1; ||
4000: T1, T2, etc
char mess;
unsigned int index_mes=0;
for (;;){
vParTestToggleLED(13);
if (xLastTime>1000) //nu la prima activare
{for (index_mes=0; index_mes<3;index_mes++ )
if( xQueueReceive( xMineQueue, &mess, (portTickType) 10 ) == pdPASS ){
if(mess!=mess_tipar[index_mes]) flag_corect=0;}//avarie
else flag_corect=0;//avarie;
}
vTaskDelayUntil(&xLastTime,2000);
}}

void initPLL(void){
// Configure PLL prescaler, PLL postscaler, PLL divisor
PLLFBD = 41; // M = 43 FRC
CLKDIVbits.PLLPOST=0; // N1 = 2
CLKDIVbits.PLLPRE=0; // N2 = 2

// Initiate Clock Switch to Internal FRC with PLL (NOSC = 0b001)


__builtin_write_OSCCONH(0x01); // FRC
__builtin_write_OSCCONL(0x01);
// Wait for Clock switch to occur
while (OSCCONbits.COSC != 0b001); // FRC
// Wait for PLL to lock
while(OSCCONbits.LOCK!=1) {};
}

void prvSetupHardware( void ){


vParTestInitialise();
initPLL();
}

int main( void )


{
prvSetupHardware();
xMineQueue=xQueueCreate(10,(unsigned portBASE_TYPE)sizeof(char *));
xTaskCreate(Task1, (signed portCHAR *) "T1", configMINIMAL_STACK_SIZE,
NULL, tskIDLE_PRIORITY+3, NULL);
xTaskCreate(Task2, (signed portCHAR *) "T2", configMINIMAL_STACK_SIZE,
NULL, tskIDLE_PRIORITY+2, NULL);
xTaskCreate(Task3, (signed portCHAR *) "T3", configMINIMAL_STACK_SIZE,
NULL, tskIDLE_PRIORITY+6, NULL);
vTaskStartScheduler();
return (0);
}
Exemplu demo print.C
/*
* o coada de mesaje eroare pentru afisare pe un display
*/

static xQueueHandle xPrintQueue;

void vPrintInitialise( void ){


const unsigned portBASE_TYPE uxQueueSize = 20;
// Creeaza coada cu mesaje de
xPrintQueue=xQueueCreate(uxQueueSize,(unsigned portBASE_TYPE)sizeof(char *));
}

void vPrintDisplayMessage( const char * const * ppcMessageToSend ){


xQueueSend( xPrintQueue, ( void * ) ppcMessageToSend, (portTickType) 0 );
}

const char *pcPrintGetNextMessage( portTickType xPrintRate ){


char *pcMessage;
if( xQueueReceive( xPrintQueue, &pcMessage, xPrintRate ) == pdPASS ) {
return pcMessage; }
else {return NULL;}
}
Exemplu demo BlockQ
/*
* o coada cu adancimea 1 mesaj: T1 citeste, T2 scrie, P_T1 > P_T2
* o coada cu adancimea 1 mesaj: T3 citeste, T4 scrie, P_T3 < P_T4
* o coada cu mai multe mesaje: T5 citeste, T5 scrie, P_T3 = P_T4
*/

#include <stdlib.h>
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "BlockQ.h"

#define blckqSTACK_SIZE configMINIMAL_STACK_SIZE


#define blckqNUM_TASK_SETS ( 3 )

typedef struct BLOCKING_QUEUE_PARAMETERS{


xQueueHandle xQueue; //coada folosita
portTickType xBlockTime;// timeout
volatile short *psCheckVariable;/*este inrmentata la fiecare ciclu for,
pentru a indica daca taskul etse running sau nu*/
} xBlockingQueueParameters;
static portTASK_FUNCTION_PROTO( vBlockingQueueProducer, pvParameters );
static portTASK_FUNCTION_PROTO( vBlockingQueueConsumer, pvParameters );

/* Variabile incrementate la fiecare citire de mesaj */


static volatile short sBlockingConsumerCount[ blckqNUM_TASK_SETS ] = { (
unsigned short ) 0, ( unsigned short ) 0, ( unsigned short ) 0 };

/* Variabile incrementate la fiecare scriere de mesaj */


static volatile short sBlockingProducerCount[ blckqNUM_TASK_SETS ] = { (
unsigned short ) 0, ( unsigned short ) 0, ( unsigned short ) 0 };

void vStartBlockingQueueTasks( unsigned portBASE_TYPE uxPriority ){


xBlockingQueueParameters *pxQueueParameters1, *pxQueueParameters2;
xBlockingQueueParameters *pxQueueParameters3, *pxQueueParameters4;
xBlockingQueueParameters *pxQueueParameters5, *pxQueueParameters6;
const unsigned portBASE_TYPE uxQueueSize1 = 1, uxQueueSize5 = 5;
const portTickType xBlockTime = ( portTickType ) 1000 / portTICK_RATE_MS;
const portTickType xDontBlock = ( portTickType ) 0;
// creeaza coada 1, T1, T2
pxQueueParameters1 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof(
xBlockingQueueParameters ) );
pxQueueParameters1->xQueue = xQueueCreate( uxQueueSize1, ( unsigned
portBASE_TYPE ) sizeof( unsigned short ) );

pxQueueParameters1->xBlockTime = xBlockTime;
pxQueueParameters1->psCheckVariable = &( sBlockingConsumerCount[ 0 ] );

pxQueueParameters2 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof(


xBlockingQueueParameters ) );
pxQueueParameters2->xQueue = pxQueueParameters1->xQueue;
pxQueueParameters2->xBlockTime = xDontBlock;
pxQueueParameters2->psCheckVariable = &( sBlockingProducerCount[ 0 ] );

xTaskCreate( vBlockingQueueConsumer, ( signed char * ) "QConsB1",


blckqSTACK_SIZE, ( void * ) pxQueueParameters1, uxPriority, NULL );
xTaskCreate( vBlockingQueueProducer, ( signed char * ) "QProdB2",
blckqSTACK_SIZE, ( void * ) pxQueueParameters2, tskIDLE_PRIORITY, NULL );
// creeaza coada 2, T3, T4
pxQueueParameters3 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof(
xBlockingQueueParameters ) );
pxQueueParameters3->xQueue = xQueueCreate( uxQueueSize1, ( unsigned
portBASE_TYPE ) sizeof( unsigned short ) );
pxQueueParameters3->xBlockTime = xDontBlock;
pxQueueParameters3->psCheckVariable = &( sBlockingProducerCount[ 1 ] );

pxQueueParameters4 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof(


xBlockingQueueParameters ) );
pxQueueParameters4->xQueue = pxQueueParameters3->xQueue;
pxQueueParameters4->xBlockTime = xBlockTime;
pxQueueParameters4->psCheckVariable = &( sBlockingConsumerCount[ 1 ] );

xTaskCreate( vBlockingQueueConsumer, ( signed char * ) "QProdB3",


blckqSTACK_SIZE, ( void * ) pxQueueParameters3, tskIDLE_PRIORITY, NULL );
xTaskCreate( vBlockingQueueProducer, ( signed char * ) "QConsB4",
blckqSTACK_SIZE, ( void * ) pxQueueParameters4, uxPriority, NULL );
// creeaza coada 2, T5, T6
pxQueueParameters5 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof(
xBlockingQueueParameters ) );
pxQueueParameters5->xQueue = xQueueCreate( uxQueueSize5, ( unsigned
portBASE_TYPE ) sizeof( unsigned short ) );
pxQueueParameters5->xBlockTime = xBlockTime;
pxQueueParameters5->psCheckVariable = &( sBlockingProducerCount[ 2 ] );

pxQueueParameters6 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof(


xBlockingQueueParameters ) );
pxQueueParameters6->xQueue = pxQueueParameters5->xQueue;
pxQueueParameters6->xBlockTime = xBlockTime;
pxQueueParameters6->psCheckVariable = &( sBlockingConsumerCount[ 2 ] );

xTaskCreate( vBlockingQueueProducer, ( signed char * ) "QProdB5",


blckqSTACK_SIZE, ( void * ) pxQueueParameters5, tskIDLE_PRIORITY, NULL );
xTaskCreate( vBlockingQueueConsumer, ( signed char * ) "QConsB6",
blckqSTACK_SIZE, ( void * ) pxQueueParameters6, tskIDLE_PRIORITY, NULL );
}
static portTASK_FUNCTION( vBlockingQueueProducer, pvParameters )
{
unsigned short usValue = 0;
xBlockingQueueParameters *pxQueueParameters;
short sErrorEverOccurred = pdFALSE;

pxQueueParameters = ( xBlockingQueueParameters * ) pvParameters;

for( ;; ){

if( xQueueSend( pxQueueParameters->xQueue, ( void * ) &usValue,


pxQueueParameters->xBlockTime ) != pdPASS ){
sErrorEverOccurred = pdTRUE;}

else {
if( sErrorEverOccurred == pdFALSE ) {
( *pxQueueParameters->psCheckVariable )++; }

++usValue;}
}
}
static portTASK_FUNCTION( vBlockingQueueConsumer, pvParameters )
{
unsigned short usData, usExpectedValue = 0;
xBlockingQueueParameters *pxQueueParameters;
short sErrorEverOccurred = pdFALSE;

pxQueueParameters = ( xBlockingQueueParameters * ) pvParameters;

for( ;; ){
if( xQueueReceive( pxQueueParameters->xQueue, &usData,
pxQueueParameters->xBlockTime ) == pdPASS ){

if( usData != usExpectedValue ){


usExpectedValue = usData;
sErrorEverOccurred = pdTRUE;}

else{
if( sErrorEverOccurred == pdFALSE ) {
( *pxQueueParameters->psCheckVariable )++;}
++usExpectedValue;}
}
}
}
portBASE_TYPE xAreBlockingQueuesStillRunning( void ){
static short sLastBlockingConsumerCount[ blckqNUM_TASK_SETS ] = { ( unsigned
short ) 0, ( unsigned short ) 0, ( unsigned short ) 0 };
static short sLastBlockingProducerCount[ blckqNUM_TASK_SETS ] = { ( unsigned
short ) 0, ( unsigned short ) 0, ( unsigned short ) 0 };
portBASE_TYPE xReturn = pdPASS, xTasks;

for( xTasks = 0; xTasks < blckqNUM_TASK_SETS; xTasks++ ){

if( sBlockingConsumerCount[ xTasks] == sLastBlockingConsumerCount[ xTasks ]){


xReturn = pdFALSE;}

sLastBlockingConsumerCount[ xTasks] = sBlockingConsumerCount[ xTasks ];

if( sBlockingProducerCount[ xTasks] == sLastBlockingProducerCount[ xTasks ]){


xReturn = pdFALSE;}

sLastBlockingProducerCount[ xTasks ] = sBlockingProducerCount[ xTasks ];


}

return xReturn;
}
II. 2. 4. 3. Probleme in gestionarea resurselor pasive partajabile

Acces concurent – mai multe taskuri folosesc aceeaşi resursă pasivă


La monotasking: partajarea apare între funcţii şi ISR

Probleme principale:
Reentranta: continuarea unor operaţii pe o resursa partajata, după ce resursa a
fost „furată” de alt proces şi apoi cedată
un exemplu este acela in care resursa partajată este un dispozitiv periferic
care acceptă mai multe moduri de lucru
Proces 1 ocupa procesorul.
Proces 1 Proces 2 Pe durata folosirii resursei
pe modul 1 pierde
procesorul care este preluat
.............. .............. de Proces 2.
Setez mod de lucru 1 Setez mod de lucru 2 Proces 2 instalează alt mod
Folosesc resursa pe mod 2 de lucru pe resursa.
Folosesc resursa pe mod 1 Refacerea contextului la
........................ ........................ revenirea in procesul 1 nu e
suficenta pentru continurea
corecta a executiei aplicatiei
Exemple resurse

- citire AD
- iesire pe PWM
- citire/setare IN/OUT digitale
- o variabila din memorie (de orice tip)
- un numator
- un canal serial
etc
Reentranta neadecvat gestionată poate conduce si la pierderea consistenţei
datelor – stocate în memorie, registri IO, etc.

Soluţia: excludere mutuală


= se acceptă la un moment dat accesul unui singur task la o resursă
partajată (dispozitiv IO, dată, coprocesor)

⇒ trebuie găsite secţiunile critice (SC)


= secvenţa de program care conduce la conflict de acces

Reguli:
- o SC este executată de un singur task
- un task aşteaptă un timp finit înainte de o secţiune critică
- în afara secţiunii critice nu există alte blocaje
Metode de excludere mutuală
1. blocarea planificatorului înainte de intrarea în secţiunea critică

2. prin programare - cu variabilă de blocare: blocare =1 (acces interzis la resursă)


blocare =0 (acces permis la resursă)

3. prin semafoare

s = număr de taskuri ce pot face apel simultan la resursa (iniţial toate


resursele sunt disponibile)

- la excludere mutuala smax=1 (exista o singura resursa de un anumit tip şi


aceasta poate fi ocupată de un singur task, la un moment de timp)
Take – funcţie ce asigură solicitare acces la resursa (Take, Get)
s→ s-1
Dacă s>=0 , exista resurse libere si una dintre resursele libere este
ocupată acum.
Dacă s< 0, nu exista resurse libere. Taskul intra in
blocare/waiting, aşteptand eliberarea resursei.

Give – eliberarea resursei de un task care a ocupat-o (Give, Release)


s→ s+1
Dacă s<= 0, pentru resursa eliberată există o solicitare anterioară
de ocupare care nu a fost deservită. Resursa este oferită unui task
(in general la SOTR: în ordinea solicitărilor sau priorităţilor)

>> FreeRTOS foloseşte o coada de mesaje pentru implementarea simpla


a semafoarelor
Probleme tipice generate de excluderea mutuală:

Inversiunea de prioritate:

- un task mai putin prioritar conduce la întârzierea unui alt task mai prioritar.
- in acest caz ocupând o resursă la care cere ulterior acces şi taskul
mai prioritar

Soluţia uzual acceptată: inferenţa de prioritate


Se creşte temporar prioritatea pentru taskul mai puţin prioritar,
grabind executia sa şi, implicit, eliberarea resursei
Inversiunea de prioritate apare astfel pe un interval de timp mai mic
Exemplu
P_T1 > P_T2 > P_T3. Se considera modul preemtiv.

Initial T1, T2 sunt blocate. T3 primeste procesorul si ocupa resursa R


Se debocheaza T1 si T2. T1 primeste procesorul, cere resursa R, nu o poate
primi, este blocata.
T2 primeste procesorul, il tine cat are nevoie, apoi cedeaza procesorul catre T3
T3 foloseste resursa, o elibereaza. Acum resursa poate fi folosita de T1.

T1 asteapta executia lui T2 care este mai putin prioritar!


Inferenta de prioritate: cand T1 cere acces la resursa R, T3 primeste
temporar prioritate lui T1:

Initial T1, T2 sunt blocate. T3 primeste procesorul si ocupa resursa R


Se debocheaza T1 si T2. T1 primeste procesorul, cere resursa R, nu o poate
primi, este blocata. T3 primeste prioritatea P_T1
T3 foloseste resursa, o elibereaza, trece la prioritatea P_T3. Acum resursa
poate fi folosita de T1.
Deadlock

Două taskuri nu mai pot progresa, împiedicându-se reciproc să primească acces


la procesor

Ex: Task1 ocupă Resursa R1, Task2 ocupă resursa R2.


Task1 cere acces la resursa R1, Task2 cere acces la resursa R2

Task1 Resursa 1

Resursa 2
Task2

Soluţia:
eliberarea resurselor ocupate înainte de a cere acces la alta
II. 2. 4. 3. 1. Facilităţi FreeRTOS pentru lucrul cu semafoare

Mecanismul folosit pentru implementare se bazează pe lucrul cu cozi de mesaje.

Un semafor are asociată o coada de mesaje la care continutul mesajului nu are


relevanta. Este folosita doar informatia „exista sau nu mesaje mesaje in coada”.

Resursa disponibilă: există mesaje în coadă - semafor pe verde (acces permis)


Resursa nedisponibila: nu există mesaje în coadă - semafor pe rosu

Semafor binar

Coada cu 1 mesaj. Iniţial coada este plină.


Operaţia Take – pune semaforul pe roşu citind mesajul
Operaţia Give – pune semaforul pe verde scriind mesajul

Este util pentru excludere mutuală – un singur task poate ocupa la un moment
dat o resursa
Task1 Taskurile pot ajunge în orice ordine la
{ secvenţa critică.
..........
Take R Primul task care are nevoie de resursă, o
.....// instructiuni ce folosesc resursa R
ocupă şi pune semaforul pe roşu, până
termină operaţiile necesare cu acea
Give R resursă.
..............
} Dacă între timp alte taskuri au nevoie de
resursă, acestea găsesc semaforul pe roşu,
Task2 întră în blocare (conform detaliilor
{ precizate la cozi de mesaje).
..........
Take R La eliberarea resursei, toate taskurile
solicitante cu timeout convenabil devin
.....// instructiuni ce folosesc resursa R
gata de execuţie, cel mai prioritar va ocupa
Give R resursa.
.............. !!! Un task cu timeout mic poate
} pierde resursa, indiferent de
prioritate
Exercitii
Mod preemtiv, P_T1>P_T2>P_T3. Alte situatii P_T3=P_T1>P_T2,
P_T3=P_T2>P_T1.

T1 se blocheaza
(temporizare)
T3 si T1 blocate Se deblochează T2
T2 ready
T2 foloseste Take R

T2 T1T2 T2T1 T1 T1T2 T2T3 T3 I I T2

T2 foloseste GiveR
Se deblocheaza T1, T3. T1 este deblocat
T1 doreste Take R, intra (timeout nedepasit), T2 se blocheaza
in blocare executa Take R, cateva (temporizare)
instructiuni specifice si
apoi Give R.

T1 este prioritar, dar


asteapta eliberarea
resursei
mod preemtiv, P_T1>P_T2>P_T3
T3 se blocheaza
(temporizare)
T3 si T1 blocate Se deblochează T2
T2 ready
T2 foloseste Take R

T2 T1T2 T2 T1T2T3 T3I T2 T2T3 T3 I I T2

T2 se blocheaza
(temporizare)
Se deblocheaza T1, T3.
T1 doreste Take R, intra T1 este deblocat
in blocare (timeout depasit, R
nefolosit in T1)
T2 foloseste GiveR si intra
in blocare
T2 foloseste T3 se deblocheaza
resursa

- atentie, taskurile prioritare pot pierde accesul la


resursa daca timeout e mic

- verifica mereu daca folosirea resursei a fost posibila


sau nu (if!!!!!)
T1 doreste Take R, intra
mod preemtiv in blocare,
P_T1>P_T2>P_T3. T2 doreste Take R, intra
in blocare

T1 si T2 blocate
T1 se blocheaza
T3 ready
(temporizare)
T3 foloseste Take R

T3 T1T2T3 T3T1 T1 T1T2 T2T3 T3 I I

Se deblocheaza T2 primeste resursa


T3 foloseste GiveR (timeout nedepasit), o
T1, T2. T1 este deblocat elibereaza, etc
(timeout nedepasit)

T1 foloseste Give R,
T2 se deblocheaza (timeout
inca nedepasit), dar T2 nu
primeste procesorul
Probleme

Daca mai multe taskuri folosesc aceeasi resursa, pot exista probleme in
stabilirea timeout (mai ales daca exista si alte taskuri mai prioritare)

→ semafoarele binare sunt greu de gestionat daca sunt multe taskuri &
taskuri care partajeaza resursa

poti folosi resurse diferite?

? ocupa resursa pe intervale cat mai scurte de timp posibil


foloseste alt tip de semafoare
Exercitiu:
Schitati in pseudocod urmatoarele aplicatii, folosind semafoare binare:

1.
T1 (prioritate 2) asigura un joc de leduri: aprinde ledurile 15, 14, 13 pentru 300 msec
apoi le stinge pentru alte 300msec, apoi aprinde doar ledul 14 pentru 300msec. Acest
joc nu trebuie distorsionat de alte taskuri, dar dupa o sectiune de joc, ledurile pot fi
folosite pentru alte semnalizari dorite. Sesiunea de joc este repetata la fiecare 4 sec.
T2 (prioritate 3) asigura alt joc de leduri: aprinde ledul 13 pentru 300 msec si apoi il
stinge pentru 300 msec. Sesiunea este reluata la fiecare 2 sec.
T3 (prioritate 4) cere schimbarea starii ledului 15 la fiecare 4 sec.
2. Semafoare de tip contor

Coada cu mai multe mesaje


Operaţia Take – citeşte un mesaj; pune semaforul pe roşu când coada este
goală
Operaţia Give – scrie un mesaj; pune semaforul pe verde

Este util pentru:

1. Acces la cluster de resurse identice

Iniţial coada este plină, iar numărul resurselor = nr maxim de mesaje din
coada.

Dacă există mesaje în coadă, interpretam ca mai există resurse libere în


cluster.
Când coada devine goală, nu mai există resurse disponbile, taskurile
solicitante intră în blocare.
Cere resursa = Take, elibereaza resursa = Give
2. Numărare evenimente.

Iniţial coada este goală, iar adancimea cozii = numărul evenimentelor ce


aşteaptă să fie procesate.

Sosirea unui eveniment corespunde unei operatii Give (scrie in coadă).


Procesarea (folosirea) unui eveniment este însoţită de o operaţie Take
(şterge un mesaj din coadă).

Exemplu: interpretarea rezultatelor unor achiziţii de date, interpretarea unor


comenzi de la tastatură.
>> nu pot suprascrie date ce nu au fost interpretate inca (astept eliberare
spatiu in coada)

Poate apărea >> inversiunea de prioritate / pericol diminuat cu semafoare mutex


Exercitii

Schitati in pseudocod urmatoarele aplicatii, folosind semafoare contor:

1. T1 (de prioritate 2) perioada 1 sec, T2 (de prioritate 3) – perioada 2 sec si T3 (de prioritate 4) –
perioada 1 sec – cer ocuparea unui canal serial pentru transmitere unor blocuri de date. Exista doar
2 canale seriale disponibile.

2. Se considera un cluster de resurse de tip led (led 15, 14, 13, 12). Ocuparea se face in aceasta
ordine (prioritar se ocupa led 15; led 12 este ocupat doar cand nu exista alte leduri libere)
T1 (de prioritate 2) – perioada 1 sec, T2 (de prioritate 2) – perioada 2 sec, T3 (de prioritate 3) –
perioada 1 sec, T4 (de prioritate 3) – perioada 2 sec, T5 (de prioritate 4) – perioada 1 sec - cer
ocuparea unui led (pentru o setare specifica): T1, T2 aprind ledul ocupat, T3, T4, T5 il sting.

3. T1 (de prioritate 2) - perioada 1 sec - citeste codul achizitionat de la AD1 si efectueaza niste
calcule. T2 (de prioritate 3) – perioada 3 sec - actualizeaza valoarea maxima achizitionata pana
atunci de la AD1.

Indicati modul de ocupare a resurselor si de executie a taskurilor.


Mutex

= Semafor binar implementat cu inferenţă de prioritate

Dacă un task (T1) care intră în blocare (atunci când cere acces la o resursă deja
ocupată) are prioritate mai mare decât taskul ce ocupă resursa (T2), atunci T2 este
temporar crescut în prioritate, pentru a grăbi execuţia sa şi eliberarea resursei.

Mutex recursiv

= un mutex ce acceptă cereri recursive de la acelaşi task

Un task poate trimite cereri suplimentare de ocupare a unei resurse, in timp ce este
deja proprietar pe ea. Pentru fiecare cerere de ocupare va exista o cerere de eliberare
pereche.

Resursa poate fi folosita de alte taskuri dupa rezolvarea tuturor acestor cereri de
ocupare (preluate de la taskul proprietar deja pe resursa).
Exercitiu: diferenta dintre semafor binar si mutex

Semafor binar. P_T1>P_T2>P_T3.

T2 se blocheaza
(temporizare) T1 se blocheaza
T2 si T1 blocate
T3 ready (temporizare)
T3 foloseste Take R

T3 T1T2 T2T3 T3T1 T1 T1 T3 T3 I I I

T3 foloseste GiveR
Se deblocheaza T1, T2. T1 este deblocat
T1 doreste Take R, va (timeout nedepasit), T3 se blocheaza
intra in blocare executa Take R, cateva (temporizare)
instructiuni specifice si
apoi Give R.

T1 este prioritar, dar va


astepta eliberarea resursei
Exercitiu: diferenta dintre semafor binar si mutex

Mutex. P_T1>P_T2>P_T3.

T1 se blocheaza T2 se blocheaza
(temporizare) (temporizare)
T2 si T1 blocate
T3 ready
T3 foloseste Take R

T3 T1T3T1 T1 T1T2 T2 T2 T3 T3 I I I

Se deblocheaza T1, T2.


T1 doreste Take R, va T3 foloseste GiveR
intra in blocare T1 este deblocat
T3 se blocheaza
(timeout nedepasit),
(temporizare)
executa Take R, cateva
instructiuni specifice si
T1 este prioritar, dar va
apoi Give R.
astepta eliberarea resursei.
T3 creste temporar in
prioritate
In continuare timeout mic poate conduce
la pierderea accesului de catre T1 prioritar
Exercitiu: diferenta dintre semafor binar si mutex

Mutex recursiv. P_T1>P_T2>P_T3.

T1 se blocheaza T2 se blocheaza
(temporizare) (temporizare)
T2 si T1 blocate
T3 ready
T3 foloseste Take R

T3 T1T3T1 T1 T1T2 T2 T2 T3 T3 I I I

Se deblocheaza T1, T2. T3 foloseste apeluri


T1 doreste Take R, va recursive, ultimul GiveR
T3 se blocheaza
intra in blocare deblocheaza (timeout
(temporizare)
nedepasit), executa Take
R, cateva instructiuni
specifice si apoi Give R.
T1 este prioritar, dar va
astepta eliberarea resursei.
T3 creste temporar in timeout mic poate conduce la pierderea
prioritate si poate efectua
ocupari recursive de resursa accesului de catre T1 prioritar
Exercitiu:

Schitati in pseudocod urmatoarea aplicatie, folosind semafoare mutex.

T1 (prioritate 2) asigura un joc de leduri: aprinde ledurile 15, 14, 13 pentru 300 msec
apoi le stinge pentru alte 300msec, apoi aprinde doar ledul 14 pentru 300msec. Acest
joc nu trebuie distorsionat de alte taskuri, dar dupa o sectiune de joc, ledurile pot fi
folosite pentru alte semnalizari dorite. Sesiunea de joc este repetata la fiecare 4 sec.
Din 12 in 12 secunde este dorita aplicarea (nepreemtata) a 2 sesiuni de joc succesive.
T2 (prioritate 3) asigura alt joc de leduri: aprinde ledul 13 pentru 300 msec si apoi il
stinge pentru 300 msec. Sesiunea este reluata la fiecare 2 sec si nu trebuie
distorsionata de alte taskuri.
T3 (prioritate 4) cere schimbarea starea ledului 15 la fiecare 8 sec.
Servicii API pentru lucrul cu semafoare (vezi queue.h si queue.c)

Creare semafor

vSemaphoreCreateBinary( xSemaphoreHandle xSemaphore )

xSemaphore Handler la semafor - de tip xSemaphoreHandle.

Exemplu:
xSemaphoreHandle xSemaphore;
void vATask( void * pvParameters ){
vSemaphoreCreateBinary( xSemaphore );
if( xSemaphore != NULL ) {
// semafor creat cu succes.
}
}
xSemaphoreHandle xSemaphoreCreateCounting( unsigned portBASE_TYPE
uxMaxCount, unsigned portBASE_TYPE uxInitialCount )

uxMaxCount Valoare maxima contor. Daca aceasta valoare este atinsa, operatiile Give determina
intrarea in starea de blocare
uxInitialCount Valoare initiala

Returnează: handler la semafor (succes), NULL (esec)

Atentie: setare in FreeRTOSconfig.h

Exemplu:
void vATask( void * pvParameters ) {
xSemaphoreHandle xSemaphore;
xSemaphore = xSemaphoreCreateCounting( 10, 0 );
if( xSemaphore != NULL ) {
// semafor creat cu succes.
}
}
xSemaphoreHandle xSemaphoreCreateMutex( void )

xSemaphoreHandle xSemaphoreCreateRecursiveMutex( void )

Returnează un handler la semafor, de tip xSemaphoreHandle

Atentie: setare in FreeRTOSconfig.h

Exemplu
xSemaphoreHandle xMutex;

void vATask( void * pvParameters )


{
xMutex = xSemaphoreCreateRecursiveMutex();
if( xMutex != NULL ) {
// mutex creat cu succes.
}
}
Operaţii de tip Take
xSemaphoreTake(
xSemaphoreHandle xSemaphore,
portTickType xBlockTime
)
- pentru semafoare de tip binar, contor, mutex

xSemaphoreTakeRecursive( xSemaphoreHandle xMutex, portTickType


xBlockTime )
- pentru semafoare mutex recursive;
- solicită configUSE_RECURSIVE_MUTEXES =1 (din FreeRTOSConfig.h)

xSemaphor Handler la semafor


xBlockTime Nr. maxim tickuri permis pentru asteptare.
Daca valoarea este 0, fără asteptare.
Daca valoarea = portMAX_DELAY şi INCLUDE_vTaskSuspend =1, taskul poate rămâne
oricât în aşteptare.
Returnează: pdTRUE (semafor primit), pdFALSE (xBlockTime expiră fără primire acces)
Operaţii de tip Give
xSemaphoreGive( xSemaphoreHandle xSemaphore )
- pentru semafoare de tip binar, contor, mutex

xSemaphoreGiveRecursive( xSemaphoreHandle xMutex )


- pentru semafoare mutex recursive;
- solicită configUSE_RECURSIVE_MUTEXES =1 (din FreeRTOSConfig.h)

xSemaphore Hanlder la semafor


Returnează: pdTRUE ( eliberare cu success), pdFALSE (eroare la eliberare)
Exemple
xSemaphoreHandle xSemaphore = NULL;
void vATask(void * pvParameters) {
xSemaphore = xSemaphoreCreateMutex();}
void vAnotherTask( void * pvParameters ) {
if( xSemaphore != NULL ) {
if( xSemaphoreTake(xSemaphore,(portTickType) 10 ) == pdTRUE ){
// ...foloseste resursa
xSemaphoreGive( xSemaphore );}
else {
// acces neprimit
}} }
xSemaphoreHandle xSemaphore = NULL;
void vATask( void * pvParameters ){
xSemaphore = xSemaphoreCreateMutex();
if( xSemaphore != NULL ) {
if( xSemaphoreGive( xSemaphore ) != pdTRUE ){
//va fi esec, nefiind un Take preliminar
}
if( xSemaphoreTake( xSemaphore, ( portTickType ) 0 ) ){
if( xSemaphoreGive( xSemaphore ) != pdTRUE )
{
// aici va fi cu succes
}
}
}
}
xSemaphoreHandle xMutex = NULL;
void vATask( void * pvParameters ) {
xMutex = xSemaphoreCreateRecursiveMutex(); }

void vAnotherTask( void * pvParameters ){


if( xMutex != NULL ){
if(xSemaphoreTakeRecursive(xMutex,(portTickType)10)==pdTRUE )
{
xSemaphoreTakeRecursive( xMutex, ( portTickType ) 10 );
xSemaphoreTakeRecursive( xMutex, ( portTickType ) 10 );

xSemaphoreGiveRecursive( xMutex );
xSemaphoreGiveRecursive( xMutex );
xSemaphoreGiveRecursive( xMutex );}
else {
// mutex recursiv neprimit
}
}
}
Protecţii suplimentare pentru secţiuni critice

Variante de protectie directe:


- suspendarea temporara a tuturor celorlalte taskuri
- invalidare ISR
Exemplu cu semafor binar
#include <stdio.h> T1 (prioritate 2) – stinge ledurile 15,14, 13, la fiecare 1 sec
#include "FreeRTOS.h" (mentine aceasta setare pe perioada cat executa anumite
#include "task.h" instructiuni)
#include "queue.h"
#include "croutine.h" T2 (prioritate 4)– aprinde ledurile 15,14, 13, la fiecare 1 sec
#include "partest.h" (mentine aceasta setare pe perioada cat executa anumite
instructiuni)
T3 (prioritate 3) – foloseste led 12 (perioada 2 sec)
#include "semphr.h"

_FOSCSEL(FNOSC_FRC); // Select Internal FRC at POR


_FOSC(FCKSM_CSECMD & OSCIOFNC_OFF); // Enable Clock Switching and Configure
// FRC + PLL

unsigned int flag_corect=1;


unsigned int nr;
xSemaphoreHandle xMineSemaphore;
portTickType del;
void Task1(void *params) {
portTickType xLastTime = xTaskGetTickCount(); int i,j;
for (;;){
if( xSemaphoreTake(xMineSemaphore,(portTickType) 500 ) == pdTRUE ){
vParTestSetLED(15,1); vParTestSetLED(14,1); vParTestSetLED(13,1);
for (i=0; i<100; i++) for (j=0; j<15000; j++) ; //orice instr.
nr++;
xSemaphoreGive( xMineSemaphore );}
vTaskDelayUntil(&xLastTime,1000);}
}

void Task2(void *params) {


portTickType xLastTime = xTaskGetTickCount(); int i,j;
for (;;){
if( xSemaphoreTake(xMineSemaphore,(portTickType) 500 ) == pdTRUE ){
vParTestSetLED(15,0); vParTestSetLED(14,0); vParTestSetLED(13,0);
del=xTaskGetTickCount();
for (i=0; i<100; i++) for (j=0; j<15000; j++) ; //orice instr.
del=xTaskGetTickCount()-del;
nr++;
xSemaphoreGive( xMineSemaphore ); }
vTaskDelayUntil(&xLastTime,1000);} // vTaskDelayUntil(&xLastTime,1010);
}
void Task3(void *params) {
portTickType xLastTime = xTaskGetTickCount();
int i,j;
for (;;){
vParTestToggleLED(12);
for (i=0; i<100; i++) for (j=0; j<5000; j++); //orice instr.
vTaskDelayUntil(&xLastTime,2000);} // vTaskDelayUntil(&xLastTime,2010);
}

void initPLL(void){
// Configure PLL prescaler, PLL postscaler, PLL divisor
PLLFBD = 41; // M = 43 FRC
CLKDIVbits.PLLPOST=0; // N1 = 2
CLKDIVbits.PLLPRE=0; // N2 = 2

// Initiate Clock Switch to Internal FRC with PLL (NOSC = 0b001)


__builtin_write_OSCCONH(0x01); // FRC
__builtin_write_OSCCONL(0x01);

// Wait for Clock switch to occur


while (OSCCONbits.COSC != 0b001); // FRC
// Wait for PLL to lock
while(OSCCONbits.LOCK!=1) {};}

void prvSetupHardware( void ){ vParTestInitialise(); initPLL();}


int main( void ){
prvSetupHardware();
vSemaphoreCreateBinary( xMineSemaphore );

xTaskCreate(Task1, (signed portCHAR *) "T1", configMINIMAL_STACK_SIZE, NULL,


tskIDLE_PRIORITY+2, NULL);
xTaskCreate(Task2, (signed portCHAR *) "T2", configMINIMAL_STACK_SIZE, NULL,
tskIDLE_PRIORITY+4, NULL);
xTaskCreate(Task3, (signed portCHAR *) "T3", configMINIMAL_STACK_SIZE, NULL,
tskIDLE_PRIORITY+3, NULL);
vTaskStartScheduler();
return 0;}

Situatii de discutat:
- cu/fara T3;
- cu timeout mai mic < del
- ce influenta poate avea valoarea lui del

Q: ce se intampla daca se foloseste mutex in loc de semafor binar.


Exemplu cu semafor de tip contor
in FreeRTOSconfig. h foloseste #define configUSE_COUNTING_SEMAPHORES 1

#include <stdio.h> T1, T2, T3 pot accesa clusterul de leduri


#include "FreeRTOS.h" 14, 13. (in aceasta ordine)
#include "task.h"
#include "queue.h"
#include "croutine.h"
#include "partest.h"
#include "semphr.h"

_FOSCSEL(FNOSC_FRC); // Select Internal FRC at POR


_FOSC(FCKSM_CSECMD & OSCIOFNC_OFF); // Enable Clock Switching and Configure
// FRC + PLL

unsigned int led_14=1, led_13=1; //1 - led liber, 0 - led ocupat

xSemaphoreHandle xMineSemaphore;
portTickType no_tick;
void Task1(void *params) {
portTickType xLastTime = xTaskGetTickCount();
static unsigned int led;
for (;;){
if( xSemaphoreTake(xMineSemaphore,(portTickType) 15) == pdTRUE ){
//exista sigur cel putin un led liber; le ocup in ordinea 14, 13
if (led_14 ==1) {led_14=0; led=14;} else {led=13; led_13=0;}
vParTestToggleLED(led);
! vTaskDelay(10);
if (led==14) {led_14=1;} else led_13=1;
xSemaphoreGive( xMineSemaphore );}
vTaskDelayUntil(&xLastTime,1000);} Asigura ca taskul nu poate fi
} preemtat in ▄ de altul care doreste
acces la aceeasi resursa
void Task2(void *params) {
portTickType xLastTime = xTaskGetTickCount();
for (;;){
if( xSemaphoreTake(xMineSemaphore,(portTickType) 5) == pdTRUE ){
//exista sigur cel putin un led liber; le ocup in ordinea 14, 13
if (led_14 ==1) {led=14; led_14=0;} else {led=13; led_13=0;}
vParTestToggleLED(led);
vTaskDelay(10);
if (led==14) {led_14=1;} else led_13=1;
xSemaphoreGive( xMineSemaphore );}
vTaskDelayUntil(&xLastTime,2000);}
}
void Task3(void *params) {
portTickType xLastTime = xTaskGetTickCount();
for (;;){
if( xSemaphoreTake(xMineSemaphore,(portTickType) 5 ) == pdTRUE ){
//exista sigur cel putin un led liber; le ocup in ordinea 14, 13
if (led_14 ==1) {led=14; led_14=0;} else {led=13; led_13=0;}
vParTestToggleLED(led);
vTaskDelay(10);
if (led==14) {led_14=1;} else led_13=1;
xSemaphoreGive( xMineSemaphore );}
vTaskDelayUntil(&xLastTime,1000);
}}

void initPLL(void){
// Configure PLL prescaler, PLL postscaler, PLL divisor
PLLFBD = 41; // M = 43 FRC
CLKDIVbits.PLLPOST=0; // N1 = 2
CLKDIVbits.PLLPRE=0; // N2 = 2

// Initiate Clock Switch to Internal FRC with PLL (NOSC = 0b001)


__builtin_write_OSCCONH(0x01); // FRC
__builtin_write_OSCCONL(0x01);

// Wait for Clock switch to occur


while (OSCCONbits.COSC != 0b001); // FRC
// Wait for PLL to lock
while(OSCCONbits.LOCK!=1) {};
}

void prvSetupHardware( void ){vParTestInitialise();initPLL();}

int main( void ){


prvSetupHardware();
xMineSemaphore=xSemaphoreCreateCounting(2,2);

xTaskCreate(Task1, (signed portCHAR *) "T1", configMINIMAL_STACK_SIZE, NULL,


tskIDLE_PRIORITY+2, NULL);
xTaskCreate(Task2, (signed portCHAR *) "T2", configMINIMAL_STACK_SIZE, NULL,
tskIDLE_PRIORITY+3, NULL);
xTaskCreate(Task3, (signed portCHAR *) "T3", configMINIMAL_STACK_SIZE, NULL,
tskIDLE_PRIORITY+4, NULL);
vTaskStartScheduler();
return (0);
}
II. 2. 4. Gestionarea memoriei
Pentru alocarea dinamică a memoriei, se foloseste
pvPortMalloc(), vPortFree().

- care ţin cont de caracteristicile temporale ale RAM-ului folosit

Scheme disponibile pentru alocarea de memorie heap:

1. (heap_1.c) – sisteme mici, taskuri & cozi static create

Dimensiunea totala a memoriei este stabilită prin configTOTAL_HEAP_SIZE

Nu este permisă eliberarea unei zone de memorie ce a fost alocată:


o Taskurile, cozile nu pot fi sterse (fără apel la vTaskDelete() or
vQueueDelete())
o Dupa apelul vTaskStartScheduler(), nu mai pot fi create taskuri noi.
2. (heap_2.c) - sisteme mici, taskuri & cozi dinamic create
Dimensiunea totala a memoriei este stabilită prin configTOTAL_HEAP_SIZE

Este permisă eliberarea unei zone de memorie ce a fost alocată, dar nu pot fi
concatenate blocuri de memorie.
o Taskurile pot fi create/ sterse dupa apelul vTaskStartScheduler(), dar
trebuie sa fie folosite cu aceeasi dimensiune a stivei
o Cozile pot fi create/ sterse dupa apelul vTaskStartScheduler(), dar
trebuie sa aibă aceeasi lungime.
→ memoria poate deveni fragmentată

3. (heap_3.c)
Cere în compilator implementări pentru malloc şi free.
+
Cere la linker să poată seta memoria heap

Nu impune restricţii la alocare/dealocare


→ codul rezultat va fi de dimensiune mai mare
II. 4. Planificare în sisteme clock - driven

II. 4. 1 Recapitulare – elemente teoretice necesare

Mod preemtiv/ nepreemtiv

Taskuri
- periodice
- apriodice & sporadice

- dependendente (- restrictii de precedenta, resurse partajate)


- independente ( fără resurse partajate, fără aşteptare rezultate)
Caracteristici planificator (DOAR pentru taskuri):

1. pe mod preemtiv - când este implicit realizată planificarea de către SO

clock driven (FreeRTOS):


periodic
o perioadă - frame

event driven:
când intervine un eveniment care modifică lista de taskuri ready
2. algoritmul de planificare - ce task primeşte procesorul într-un punct de
replanificare

Procesorul este „oferit” taskului mai prioritar

→ cum sunt stabilite priorităţile:

Static – prioritatea unui task nu se modifică pe durata


execuţiei aplicaţiei: stabilită de PROGRAMATOR
(FreeRTOS – atentie program. poate schimba P)
sau
Dinamic: prioritatea unui task este modificată pe durata
execuţiei aplicaţiei: de SO

→ ce algoritm este folosit la priorităţi egale


FreeRTOS: round rubin,
FIFO, etc
Restricţii de timp

interval in care procesul


trebuie sa se execute
(occurance interval, feasible interval)

Di
(deadline relativ)

procesi timp

ri di
activare ei termen limita
(release) (executie) (deadline absolut)

li Taski
rezerva
(laxity, slack)
intarziere
(tardeness)

Taski
Plan

- valid (corect): SO + PROGRAMATOR

- admisibil (“feasible”) – ce asigura respectarea restricţiilor

La prioritaţi statice: verificare realizată de programator pe cazul cel mai


nefavorabil

La priorităţi dinamice:
verificare realizată de programator pe cazul cel mai nefavorabil – mai
dificil
există teroeme utile pentru cazuri simple, particulare.
II. 4. 2. Variante de planificare clock driven

II. 4. 2. 1. Gestionarea momentelor când are loc preempţiunea

Preemptiune la intervale egale de timp (un interval între două preemţiuni succesive
este numit cadru – „frame” şi are lungimea f)

- Triggerul de preemţiune poate fi implementat folosind un timer!!!

Observatii:
- la algoritmii statici deciziile sunt stabilite offline.
- la algoritmii dinamici deciziile planificatorului sunt reînnoite la fiecare inceput
de cadru, în funcţie de taskurile găsite ready la acel moment;
 la momentele de preempţiune, algoritmul de planificare poate
considera priorităţile taskurilor neschimbate (statice) sau le
poate actualiza (dinamice).
Pentru alegerea lui f pot fi respectate condiţiile:

 preferabil, lungimea f a unui cadru este divizor al fazelor (faza =


decalaj între momentul când începe perioada taskului şi momentul
de la care taskul se poate executa)
– la faza 0 este implicit asigurată condiţia

 f este divizor pentru hiperperioada H (condiţie implicit asigurată


daca f este divizor al unei perioade pi)
un ciclu major = secvenţa de cadre dintr-o hiperperioada

 dacă f ≥ max ei , taskurile (periodice) se pot executa fără


i =1,..,n
preempţiune (ipoteza un singur task per frame – fără preemtiuni in
interiorul frame-ului)
 există cel putin un cadru intre ri si di pentru fiecare task (f mic)

Inceput t’+Di
cadru

t t’ = ri t+f t+2f

pentru t ' > t :


t + 2 f ≤ t '+ Di (plasez 2 cadre) ⇒ 2 f − (t '−t ) ≤ Di ⇒
2 f − cmmdc( pi , f ) ≤ Di (conditie necesara)

Ex: 0 2 4 6 8 10 12
T T (p = 10, f = 4)
F F F F

pentru t ' = t
taskul redevine ready la început de cadru ⇒ f ≤ Di
Exemple

1. Fie taskurile (p1=15,e1=1,D1=14, fază nulă),


(p2=20,e2= 3,D2=6, fază nulă),
(p3=D3=20,e3=3, fază nulă).

f ≥ max ei ⇒ f ≥ 3
i =1,.., n

f divizor al unei perioade de task: ∈{1, 3, 4, 5, 10, 15, 20}

f ≤ Di : f ≤ 14 , f ≤ 6 , f ≤ 20

⇒ f ∈{3, 4, 5}

Aleg f = 5.
Secventa de executie pentru f = 5 si planificare dinamică (e1=1, e2= 3, e3=3)

F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13


0 5 10 15 20 25 30 35 40 45 50 55 60
T1 I T1 I T1 I T1 I T1
T2 I T2 I T2 I T2
T3 T3 T3 T3

P2 > P3 > P1:


A) cu comutări în interiorul frame-ului:
F1-T2(3), T3(2); F2-T3(1),T1(1); F4-T1; F5 –T2(3), T3(2); F6 –T3(1); etc - plan
admisibil
B) fără comutări în interiorul frame-ului
F1-T2(3), F2- T3(3), F3-T3(1), F4-T1; F5 –T2(3), F6 T3; etc – plan admisibil

P1>P3>P2: – plan neadmisibil


A) F1-T1,T3, T2, F2-T2
B) F1-T1, F2-T3, F3-T2
2. Fie taskurile (p1=D1=4,e1=1, fază nulă),
(p2=5,e2=2,D2=7, fază nulă),
(p3=D3=20,e3=5, fază nulă)

f ≥ max ei ⇒ f ≥ 5
i =1,.., n
f divizor al unei perioade de task: ∈{1, 2, 4, 5, 10, 20}
f ≤ Di : f ≤ 4 , f ≤ 7 , f ≤ 20

⇒ imposibil:

se renunţă la prima condiţie f ≥ 5 - va exista cel putin un task ce nu


poate fi complet executat într-un singur frame
- echivalent cu divizarea lui T3 in subtaskuri:

Exemple: pentru f = 2, (20,5) ⇒ (20,2), (20,1), (20,1), (20,1) sau


(20,1) x 5 (vezi plan)
pentru f = 2 si planificare dinamică pentru P1>P2>P3 fixe (e1=1, e2=2, e3=5).
F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11
0 2 4 6 8 10 12 14 16 18 20
T1 T1 T1 T1 T1 T1
T2 T2 T2 T2 T2
T3 T3

F1-T1(1), T2(1); F2-T2(1),T3(1); F3-T1,T3 (1); F4 –T2(2); F5 –T1,T3(1);


F6 –T2(2); F7 –T1,T3(1); F8 –T3(1); F9-T1(1), T2(1); F10- T2(1) plan admisibil

pentru f = 2 si planificare statica, fara preemtiuni la T1 si T2, cu T3 1+1+1+2


F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11
0 2 4 6 8 10 12 14 16 18 20
T1 T1 T1 T1 T1 T1
T2 T2 T2 T2 T2
T3 T3

F1-T2(2); F2-T1(1),T3(1); F3- T1 (1), T3(1); F4 –T2(2); F5 T1, T3(1); F6-T2; F7-
T1; F8-T3(2); F9-T2, F10- T1 plan admisibil
II. 4. 2. 2. Gestionarea priorităţilor taskurilor

Algoritmi de planificare cu priorităţi statice

Algoritm cu frecventa monotona RM:


= asignează prioritatea în funcţie de frecvenţa taskului periodic:
>> prioritate maximă pentru taskul cu frecvenţa (1/pi) maximă

Algoritm cu deadline monoton DM:


= asignează prioritatea în funcţie de deadline-ul relativ al taskului
periodic:
>> prioritate maximă pentru taskul cu deadline-ul relativ (Di) mai
mic
Exemple: T1, T2 periodice, independente, preemptive

1. T1: p1=2 sec, are de executat niste operatii cu e1 = 1 sec; D1 = 1.5 (sau 2)
T2: p2=1 sec, are de executat niste operatii cu e2 = 0.5sec; D2 = 0.7 (sau 1).

2. T1: p1=2 sec, are de executat niste operatii cu e1 = 1 sec; D1 = 1.5 (sau 2)
T2: p2=2 sec, are de executat niste operatii cu e2 = 0.5sec; D2 = 1.7 (sau 2).

3. T1: p1=2 sec, are de executat niste operatii cu e1 = 1 sec; D1 = 1.1 (sau 2)
T2: p2=2.5 sec, are de executat niste operatii cu e2 = 0.5sec; D2 = 1 (sau 2.5).

Analiza se va face pe o hiperperioadă


= cmmmc al perioadelor taskurilor periodice

OBS: Dacă Di= pi >> RM si DM sunt identici


Gradul de utilizare trebuie să fie cel mult unitar:
(altfel nu există resurse active suficiente)

n e
U = ∑ i , cu
i =1 pi U ≤1
ei = timp executie task Ti
pi = perioada taskului Ti
n = numar taskuri

1 1 1 1
(pentru exemplul 1: + = + = 1)
p1 p 2 2 2

Obs: în practică, situaţia U =1 nu este robustă, deoarece pot apărea:


 comutări de contexte,
 întârzieri,
 ISR.
Algoritmi de planificare cu priorităţi dinamice

Algoritm EDF:
= asigneaza prioritatea maxima pentru taskul cu deadline-ul
absolut (di) cel mai apropiat, faţă de punctul de replanificare
curent.

Algoritm MLF:
= asigneaza prioritatea maxima pentru taskul cu rezerva (laxity)
mai mică, calculul acesteia fiind realizat în punctul de
replanificare curent.

Dezavantaj MLF: solicita calcularea timpilor de executie

Ex: r1 = 0, d1 = 10, e1 = 3
r2 = 2, d 2 = 14, e2 = 6
r3 = 4, d 3 = 12, e3 = 4 pentru f = 1.
Exemple MLF: T2 se poate executa dupa teminarea executiei pentru T1

T1, l1=3
e1=3 e2=2
T1 r1=0 T2 r2=5 T3, l3=3
d1=6 d2=8

1 3 2
e3=2 k
r3=2 T3
d3=7 l= d–r-e
T2, l2=1

F0, F1: ready T1 (l1=3).


F2: ready T1 (l1=3), T3(l3=3).
F3, F4: ready T3 (l3=2).
F5, F6: ready T2 (l2=1) – restr. de precedenta indeplinita.
T1

T3

e1=3 e2=2
T1 r1=0 T2 r2=5
d1=6 d2=8
1 3 2
k

e3=2 Alt task T2


r3=2 T3
d3=7 l1=1 l3=0
l3=3 l2=1

F2: ready T1 (l1=1), T3(l3=3). F3: ready T1 (l1=1), T3(l3=2).


F4: ready T1 (l1=1), T3(l3=1).
F5: ready T2 (l2=1), T3(l3=0). F6: ready T2 (l2=0), T3(l3=0).
Restrictie de timp pentru T2 neindeplinită
T1

T3

e1=2 e2=2
T1 r1=0 T2 r2=5
d1=10 d2=8 3 2 3 1
k k

T2
e3=4
T3 l1=6 l1=3
r3=2
l3=3 l3=3 l1=1
d3=9
l2=1 l3=1

F2: ready T1 (l1=6), T3(l3=3). F3: ready T1 (l1=5), T3(l3=3).


F4: ready T1 (l1=4), T3(l3=3). F5: ready T1 (l1=3), T3(l3=3), T2 (l2=1)
Restr. de precedenţă neîndeplinită - continuare eliminând această restricţie.
F6: ready T1 (l1=2), T3(l3=2), T2 (l2=1).
F7: ready T1 (l1=1), T3(l3=1), F8: ready T1 (l1=0)
Teorema1:
Ipoteze: caz monoprocesor, mod preemptiv, taskuri independente.
EDF (Earliest Deadline First) este optimal

Demo:
Orice plan admisibil poate fi transformat intr-un plan admisibil produs cu EDF

rk dk di

Ti Tk

rk dk di

Tk Tk Ti
Pentru taskuri nonpreemptive EDF nu este optimal
Ex: r1 = 0, d1 = 10, e1 = 3
r2 = 2, d 2 = 14, e2 = 6
r3 = 4, d 3 = 12, e3 = 4

Pentru cazul multiprocesor EDF nu este optimal


Ex: r1 = 0, d1 = 1, e1 = 1
r2 = 0, d 2 = 2, e2 = 1
r3 = 0, d 3 = 5, e3 = 5
doua procesoare

Teorema2:
Ipoteze: caz monoprocesor, mod preemptiv, taskuri independente.
MLF (Earliest Deadline First) este optimal
Exercitii

1. 9 taskuri cu timpii de executie 3, 2, 2, 2, 4, 4, 4, 4, 9, faza 0, deadline 12. T1 este


predecesor imediat pentru T9, T4 este predecesor imediat pentru T5-T8. Nu exista
alte restrictii de precedenta. Ti are prioritate mai mare decat Tk daca i < k.
- exista un plan admisibil daca taskurile sunt preemptive/nonpreemptive? (3
procesoare)
- pentru timp de executie 1 pentru T1-T9, exista un plan admisibil (1 procesor)?
Dar daca T9, T4, T8 au deadline 4, 7, respectiv 9?

2. 5 taskuri: A, B, C, D, E: faza 0, Di = pi
A, B, C: perioada 2, timp executie 1
D, E perioada 8, timp executie 6
a) indicati planul MLF pe 3 procesoare
b) gasiti un plan admisibil pe 3 procesoare
c) refaceti a) pentru taskuri cu aceeasi perioada 2 si timp de executie 1.
Tratarea taskurilor non-periodice

Ipoteze:
1. STR are n taskuri periodice, n= fix
2. parametrii taskurilor sunt cunoscuti
3. niciun task aperiodice nu este critic (nu exista taskuri sporadice)

Variante de lucru:

Taskurile aper. ready sunt introduse într-o listă separată, fără activarea
planificatorului

Procesorul execută taskuri aperiodice din această listă, DOAR atunci cand
nu trebuie executate taskuri periodice.
1. Taskurile aper. sunt introduse în listă fără a le verifica indeplinirea deadline-
ului.

Execuţia taskurilor aperiodice din coada de taskuri aperiodice ready se


poate realiza astfel:

i) taskurile aperiodice se execută dacă nu există taskuri periodice


ready (după execuţia taskurilor periodice în acel frame)

ii) taskurile aperiodice se execută în fiecare frame înainte de


taskurile periodice, in limita timpului permis de rezerva
REZ = f - e ,
cu e = timpul de executie pentru taskuri periodice
planificate in acel cadru
 Dezav: trebuie să existe un plan offline pentru taskuri periodice
 Avantaj: se asigură timp de răspuns mai bun la taskurile aper.
2. Un task aperiodic este inclus în lista de taskuri aperiodice ready numai dacă
trece un test de acceptanţă.

Trebuie să existe un plan offline pentru taskuri periodice – cu rezervele


disponibile la fiecare frame.

la fiecare început de frame se aplică testul de acceptare pentru taskurile


aperiodice activate în frame-ul anterior: dacă poate fi găsit un plan
admisibil, taskul e acceptat, altfel taskul e rejectat.

Planificare EDF (on - line) doar pentru taskurile aperiodice


EDF aplicat pentru coada de taskuri aperiodice acceptate
>> taskul aperiodic executat primul este cel cu deadline mai apropiat.
Reject A4

A4: 5 >4.5
Reject A1
A4(44,5)
A1:4.5> 4

A1 (17, 4.5) A3 (22, 1.5)


A2(29,4
)

S2

3.5 7 10 15 23.5 27 30
19 35 39
4 8 12 16 20 24 28 36
32 40
1 2 3 4 5 6 7 8

hiperperioada hiperperioada

Accept A2
A2: 4 < 5,5

Accept A3
A3: 1,5 <=2 Notatie: A(d, e)
A2: rest 2, rezerva cu A3 1,5
EDF ciclic este optimal:
= poate gasi un plan admisibil pentru taskurile aperiodice
preemptive acceptate,
daca testul de acceptanta este facut la inceputul fiecarui cadru

ATENTIE!!!!:

EDF presupune ca anumite taskuri aperiodice vor fi rejectate.

EDF ciclic nu este optimal daca e comparat cu algoritmi care fac testul de
acceptanta oriunde (ex: se poate accepta S1),
dar e avantajos pentru takurile periodice (nu se ajunge la nr. mare
de preemptiuni)

Rezerva de timp stabilită offline poate fi uneori consumata de taskuri


periodice in situatii de supraincarcare (f. f. rare – ex: volum f. mare de date
de intrare) .

S-ar putea să vă placă și