RTOS Memory Pool

A fejezet tartalma:
Az előző fejezetben megmutattuk, hogy a Queue objektumosztály segítségével hogyan küldhetünk egyszerű adatokat egyik programszálból a másiknak. Amennyiben nagyobb, vagy komplexebb felépítésű üzenetcsomagokat szeretnénk küldeni, akkor gondoskodnunk kell az adatok ideiglenes tárolásáról (a Queue üzenetsor kezelés ebben az esetben csak egy mutatót  továbbít a címzettnek). Ehhez nyújt segítséget a MemoryPool (memória készlet) objektumosztály, amely futás közbeni helyfoglalást biztosít előre definiált méretű/szerkezetű adatstruktúrák tárolására.

A MemoryPool osztály a Queue objektumosztályhoz hasonlóan szintén templát osztály, azaz nekünk kell megadnunk, hogy milyen adatstruktúrákat akarunk tárolni a lefoglalt memóriában. Ugyancsak nekünk kell megadnunk azt is, hogy hány struktúrának akarunk helyet biztosítani, azaz mekkora tároló készletet akarunk létrehozni.

Az üzenetküldéshez egy-egy azonos típusra és készletszámra definiált Queue és MemoryPool objektumra van szükségünk. Az üzenetküldéshez le kell foglalnunk egy memória blokkot (alloc()), bele kell írni az adatokat, majd az adatstruktúrára mutató pointert elküldjük a Queue put() funkciójával. A vételnél az üzenetsor get() metódusával várunk az üzenetre, majd a kapott üzenetbe csomagolt mutatóval hozzáférünk az adatokhoz. Ezután a memória blokkot fel kell szabadítanunk az újrahasznosításhoz. A folyamatot az alábbi ábrán szemléltetjük:


1. ábra: A Que és Memory Pool objektumok összekapcsolása komplex üzenetek küldéséhez


Memóriakészlet létrehozása

Meg kell adnunk az eltárolni kívánt struktúrák típusát, és a memóriakészlet (MemoryPool) méretét (a tárolt adatstruktúrák maximális darabszámát). Például:
typedef struct {
float voltage; // ADC feszültségmérés eredménye
float current; // ADC árammérés eredménye
uint32_t counter; // Egy kiolvasott számláló értéke
} message_t;

MemoryPool <message_t, 16> mpool; // 16 elemű memória készlet létrehozása
Queue <message_t,16> queue; // 16 elemű üzenetsor létrehozása
Megjegyzés: Szigorúan véve a MemoryPool osztály létrehozott példányához nem feltétlenül kell üzenetsort (Queue) rendelni, hiszen a memória készlet más célra is felhasználható. Most viszont az üzenetküldést szeretnénk bemutatni, ehhez a létrehozott memória készlet mellé ugyanolyan típus és elemszám megadásával egy üzenetsort is létre kell hoznunk a fenti listán látható módon.

Memóriablokk lefoglalása


A memória blokkok lefoglalása a MemoryPool objektumpéldányok alloc() metódusával végezhető:
T*  alloc ( void )       
Paraméterek:

Az alloc() függvénynek nincs paramétere.

Visszatérési érték:

A függvény visszatérési értéke a szabad memória blokkra mutató pointer, vagy NULL, ha nincs szabad lefoglalható blokk. A kapott pointer típusa (amit itt a T* jelöl) olyan adatstruktúrára mutató lesz, amilyen struktúra típust a MemoryPool példányosításakor megadtunk.

Megjegyzés: Ez a függvény megszakításból is hívható.

Memóriablokk felszabadítása

A felhasznált memória blokk felszabadítása a MemoryPool objektumpéldányok free() metódusával végezhető:
osStatus free ( T *  block )        
Paraméterek:

block - az absztrakt T típusú üzenetre mutató pointer, azaz olyan adatstruktúrára mutató pointer, amilyen struktúra típust a MemoryPool példányosításakor megadtunk.

Visszatérési érték:

A függvény visszatérési értéke az alábbi státusz, vagy hibakódok valamelyike lehet:
  • osOK: a memória blokk felszabadult
  • osErrorValue: a blokk nem tartozik a memória készlethez
  • osErrorParameter: érvénytelen paraméter
Megjegyzés: Ez a függvény megszakításból is hívható.

Mintapélda: Üzenetek küldése MemoryPool használatával

Az alábbi programban három lebegőpontos (float) típusú adatot tartalmazó adatstruktúrákat küldözgetünk üzenet gyanánt, melyeknek egy MemoryPool típusú memória készlet biztosít tárterületet.

Az első programszál véletlen színek közötti átmenetekhez generál RGB színkódokat interpolációval (a színkomponensek 0 - 1.0 közötti lebegőpontos számok), s ezeket a színkódokat küldjük el üzenetként, s minden üzenet után 0.1 másodpercet várunk.

A második programszál fogadja az üzeneteket, az RGB LED-en megjeleníti a kapott színkombinációt, majd felszabadítja a kapott üzenethez tartozó memória blokkot.

  Hardver követelmények:
1. lista: A 11_rtos_mempool/main.cpp program listája
#include "mbed.h"
#include "rtos.h"
PwmOut rled(LED_RED);
PwmOut gled(LED_GREEN);
PwmOut bled(LED_BLUE);

typedef struct {
float red;
float green;
float blue;
} message_t;

MemoryPool <message_t, 4> mpool; //Memory pool for data storage
Queue <message_t,4> queue; //Message queue for 4 items

void led_thread(void const *argument)
{
rled.period_ms(20); //Set period to 20 ms
rled.write(1.0f); //Initialize to 0% duty cycle
gled.period_ms(20); //Set period to 20 ms
gled.write(1.0f); //Initialize to 0% duty cycle
bled.period_ms(20); //Set period to 20 ms
bled.write(1.0f); //Initialize to 0% duty cycle
while (true) {
osEvent evt = queue.get(); //Wait for a message
if(evt.status == osEventMessage) {
message_t *mymessage = (message_t*)evt.value.p;
rled = 1.0f - mymessage->red;
gled = 1.0f - mymessage->green;
bled = 1.0f - mymessage->blue;
mpool.free(mymessage); //Free up memory
}
}
}

float frand(void)
{
float rv = (float)(rand()&0xFFFF);
return (rv/65536.0f);
}

int main (void)
{
float RGB1[3];
float RGB2[3];
float INC[3];
Thread thread2(led_thread);
//--- Create a random color ---------------------
for (int x=0; x<3; x++) {
RGB1[x] = frand();
}

while (true) {
//--- Create a new random color -----------------
for (int x=0; x<3; x++) {
RGB2[x] = frand();
}
//--- Determine increments to go from color 1 to color 2 in 25 steps
for (int x=0; x<3; x++) {
INC[x] = (RGB1[x] - RGB2[x]) / 25;
}
//--- Send color codes to thread2 ---------------
for (int s=0; s<25; s++) {
message_t *message = mpool.alloc(); //Allocate memory
message->red = RGB1[0];
message->green = RGB1[1];
message->blue = RGB1[2];
queue.put(message); //Send data as message
Thread::wait(100);
for (int x=0; x<3; x++) {
RGB1[x] -= INC[x]; //Approach to second colour
}
}
}
}