Sistemas de Tempo Real — Parte 2

- por Sergio Prado

Categorias: RTOS Tags: , , , ,

Na parte 1 deste artigo apre­sen­tei os prin­ci­pais con­ceitos e a arquite­tura básica de um Sis­tema de Tempo Real. Neste artigo irei exem­pli­ficar alguns con­ceitos através do desen­volvi­mento de uma apli­cação uti­lizando um RTOS disponível no mer­cado.

Pro­jeto

O pro­jeto que ire­mos desen­volver é um Sis­tema de Alarme com Mon­i­tora­mento Remoto:

figura1 Sistemas de Tempo Real   Parte 2

O Sis­tema é divi­dido em duas apli­cações: Sis­tema de Alarme Local e Sis­tema de Mon­i­tora­mento Remoto. O Sis­tema de Alarme Local é com­posto por um sen­sor de pre­sença, um sinal lumi­noso e um dis­play para exibição de infor­mações. O Sis­tema de Mon­i­tora­mento Remoto é com­posto ape­nas por um dis­play para a exibição de infor­mações rece­bidas remo­ta­mente.

O fun­ciona­mento do pro­jeto é sim­ples: o Sis­tema de Alarme mon­i­tora con­stan­te­mente o sen­sor de pre­sença, e a recepção de um sinal de detecção implica no aciona­mento do alarme. Uma vez acionado, o alarme liga o sinal lumi­noso, e envia uma men­sagem para o Sis­tema de Mon­i­tora­mento Remoto. Ambos os sis­temas local e remoto man­tém as infor­mações atu­al­izadas do sta­tus do alarme no dis­play.

Pré-requisitos

Não será necessário nen­hum hard­ware especí­fico para a imple­men­tação deste pro­jeto. Todas as entradas (sen­sores) e saí­das (sinal lumi­noso e dis­play) serão emu­ladas por software.

Todo o pro­jeto será desen­volvido e exe­cu­tado em um PC de arquite­tura x86 Intel, rodando o Sis­tema Opera­cional GNU/Linux. Para o desen­volvi­mento da apli­cação será uti­lizado o uCOS-IIRTOS da Micrium. Uma ver­são de avali­ação pode ser baix­ada dire­ta­mente do site da Micrium em www.micrium.com.

Arquite­tura

O pro­jeto está divi­dido em duas apli­cações Linux, uma apli­cação respon­sável pelo Sis­tema de Alarme Local, e a outra pelo Sis­tema de Mon­i­tora­mento Remoto. As apli­cações se comu­ni­cam através de uma rede TCP/IP.

O sen­sor será emu­lado através do mecan­ismo de envio de sinais do Linux. Para acionar o sen­sor, basta enviar o sinal USR1 para o processo do Sis­tema de Alarme Local. 

O dis­play será rep­re­sen­tado pelo mon­i­tor, e as infor­mações de sta­tus do alarme serão exibidas na tela, tanto na apli­cação local, quanto na apli­cação remota. O aciona­mento do sinal lumi­noso será exibido tam­bém na tela da apli­cação.

UCOS-II

O uCOS-II, desen­volvido pela Micrium, pos­sui código-fonte aberto e disponível gra­tuita­mente para fins edu­ca­cionais. É pequeno (o Ker­nel com­pi­lado ocupa em torno de 20K de memória), mas alta­mente fun­cional, sendo por­tanto indi­cado para sis­temas com poucos recur­sos com­puta­cionais. O Ker­nel é pre­emp­tivo, e exis­tem portes para diver­sas arquite­turas, den­tre elas x86, 68K, ARM e PIC.

Mod­e­lagem da apli­cação

Para mod­e­lar a apli­cação, o primeiro passo é dividir o sis­tema em peque­nas tare­fas, com obje­tivos bem claros, e definir a pri­or­i­dade de cada tarefa per­ante as demais.

A apli­cação pode ser mod­e­lada con­forme a figura abaixo:

figura2 Sistemas de Tempo Real   Parte 2

O sis­tema foi divi­dido em qua­tro tare­fas, e suas pri­or­i­dades definidas con­forme abaixo:

Pri­or­i­dade 1 -> TaskRe­gras -> Lóg­ica da apli­cação

Pri­or­i­dade 2 -> TaskSen­sores -> Leitura dos sen­sores

Pri­or­i­dade 3 -> TaskComm -> Comu­ni­cação para o mon­i­tora­mento remoto

Pri­or­i­dade 4 -> TaskDis­play -> Exibição das infor­mações no dis­play

É de extrema importân­cia a definição cor­reta da pri­or­i­dade de cada tarefa, já que o sis­tema opera­cional irá uti­lizar esta infor­mação para realizar o escalon­a­mento das tare­fas da apli­cação.

A imple­men­tação das qua­tro tare­fas serão com­par­til­hadas tanto pelo Sis­tema de Alarme Local quanto pelo Sis­tema de Mon­i­tora­mento Remoto.

Sis­tema de Alarme Local

Este sis­tema uti­liza as qua­tro tare­fas. A tarefa TaskRe­gras é respon­sável pela lóg­ica da apli­cação, recebendo men­sagens de aciona­mento de sen­sores da tarefa TaskSen­sores, atu­al­izando o sta­tus do sis­tema, e enviando men­sagens para a tarefa TaskComm, respon­sável pela comu­ni­cação com o Sis­tema Remoto. A tarefa TaskDis­play irá aguardar a atu­al­iza­ção das infor­mações do alarme e atu­alizar as infor­mações no dis­play.

Sis­tema de Mon­i­tora­mento Remoto

Este sis­tema é mais sim­ples e uti­liza três tare­fas. A tarefa TaskComm irá aguardar o rece­bi­mento de men­sagens da rede de comu­ni­cação e enviar para a tarefa TaskRe­gras, que será respon­sável pela atu­al­iza­ção do sta­tus do sis­tema. A tarefa TaskDis­play irá aguardar a atu­al­iza­ção das infor­mações do alarme e atu­alizar as infor­mações no dis­play.

Cod­i­fi­cação

Nosso primeiro passo para a cod­i­fi­cação da apli­cação é criar a função prin­ci­pal do pro­jeto – main() – que será respon­sável por ini­ciar o sis­tema opera­cional e criar as tare­fas da apli­cação.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int main(void)
{
    /* inicializa uCOS-II */
    OSInit();
 
    /* cria a tarefa principal */
    OSTaskCreate(TaskRegras, NULL, TaskRegrasStk[TASK_STK_SIZE],
                 PRIO_TSK_REGRASCENTRAL);
 
    /* inicializa sistema operacional */
    OSStart();
 
    /* nunca deve chegar aqui! */
    return 0;
}

Na listagem acima podemos ver o código da função main() da apli­cação, que exe­cuta basi­ca­mente 3 oper­ações:

  1. Ini­cial­iza o sis­tema opera­cional chamando a função OSInit()
  2. Cria a tarefa TaskRe­gras chamando a função OSTaskCreate()
  3. Ini­cia o escalon­a­mento de tare­fas chamando a função OSStart()

Após a chamada a OSStart(), o sis­tema opera­cional ini­cia o escalon­a­mento de tare­fas, sendo que até o momento ape­nas uma tarefa da apli­cação para ser escalon­ada – a tarefa TaskRe­gras. Esta tarefa será a respon­sável pela cri­ação das out­ras tare­fas da apli­cação.

Imple­men­tação das tare­fas

Grande parte das tare­fas é imple­men­tada através de um laço infinito, e pos­sui um for­mato padrão, pare­cido com a listagem abaixo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void TaskX(void *pdata)
{
    /* codigo de inicializacao */
    ...
 
    while (1) {
 
        /* aguarda algum evento */
        ...
 
        /* toma alguma acao */
        ...
    }
}

Con­forme podemos ver, uma tarefa irá, na maio­ria das vezes, exe­cu­tar os três pas­sos abaixo:

  1. Ini­cializar var­iáveis uti­lizadas em seu processamento.
  2. Aguardar um evento, que pode ser o rece­bi­mento de uma men­sagem, o time­out de um pedido de delay, etc.
  3. Tomar uma ação em resposta ao evento rece­bido.

Podemos perce­ber este padrão anal­isando o código da tarefa TaskRe­gras na listagem abaixo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void TaskRegras(void *pdata)
{
    INT8U err;
    void *pmsg;
 
    /* inicializa as informacoes do sistema */
    initSystemInfo();
 
    /* inicializa as tarefas e recursos utilizados */
    initTasksData();
 
    while (1) {
 
        /* aguarda uma mensagem para tratar */
        pmsg = OSQPend(msgq_regras, 0, &err);
 
        /* trata mensagem recebida */
        if (err == OS_ERR_NONE)
            trataMensagem(pmsg);
    }
}

O primeiro passo é ini­cializar as var­iáveis através das chamadas a init­Sys­tem­Info() e init­Tasks­Data(). O próx­imo passo é aguardar um evento, que neste caso é a recepção de uma men­sagem, com a função OSQPend(), e logo após tratar a men­sagem rece­bida através da função trataMensagem().

Anal­isando o código da tarefa TaskSen­sores, na listagem abaixo, tam­bém podemos perce­ber este mesmo padrão:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void TaskSensores(void *pdata)
{
    /* inicializa a leitura dos sensores */
    initSensores();
 
    while (1) {
 
        /* suspende a execucao ate receber sinal
           de acionamento de sensor */
        OSTaskSuspend(PRIO_TSK_SENSORES);
 
        /* envia a mensagem de acionamento dos
           sensores para a tarefa de regras */
        txMsgSetSensores();
    }
}

A tarefa ini­cial­iza suas var­iáveis de con­t­role através da função init­Sen­sores(), logo após fica aguardando o aciona­mento do sen­sor (recepção do sinal USR1) através da chamada OSTaskSus­pend(), e logo em seguida toma uma ação em resposta ao aciona­mento do sen­sor, neste caso chamando a função txMs­gSet­Sen­sores() que será respon­sável por for­matar e enviar uma men­sagem de aciona­mento de sen­sor para a tarefa TaskRe­gras.

As out­ras duas tare­fas, TaskComm e TaskDis­play, são imple­men­tadas com este mesmo padrão.

Out­ras funções do uCOS-II

Além das funções exem­pli­fi­cadas nas lista­gens, out­ras funções foram uti­lizadas no pro­jeto, e devem ser mencionadas:

  • OSQ­Post(): envia uma men­sagem para outra tarefa.
  • OSQPend(): aguarda o rece­bi­mento de uma men­sagem de outra tarefa.
  • OSSem­Cre­ate(): cria e ini­cial­iza um semá­foro para o com­par­til­hamento de recur­sos.
  • OSSem­Pend() e OSSem­Post(): funções usadas para o com­par­til­hamento de recur­sos.

O código-fonte com­pleto da apli­cação pode ser baix­ado aqui.

Cob­ri­mos aqui ape­nas uma pequena parte das fun­cional­i­dades do uCOS-II e, para fins didáti­cos, exem­pli­fi­camos uma parte da apli­cação desen­volvida. A doc­u­men­tação do sis­tema opera­cional é extensa e bem didática, e é alta­mente acon­sel­hável uma leitura mais detal­hada para enten­der todas as funções disponíveis. Bons estudos!

Um abraço,

Ser­gio Prado

  • Dou­glas Campos

    Olá Ser­gio!
    Mais uma vez gostaria de agrade­cer e o parab­enizar pelo blog, a inter­net é uma fer­ra­menta poderosa, com muita infor­ma­cão disponível, porém muita coisa espal­hada pela web. Aqui é um dos poucos lugares que con­sigo encon­trar tudo o que me inter­essa em um lugar só!
    Bom ten­tei rodar seus exem­p­los porém não obtive sucesso. Não encon­trei o port para x86 no site da micrium, porém encon­trei o site de um pro­fes­sor ( http://www2.hs-esslingen.de/~zimmerma/software/index_uk.html ), que pelo que me parece min­is­tra uma dis­ci­plina e uti­liza este port para explicar os con­ceitos de RTOS para seus alunos.

    Ten­tei com­pi­lar os exem­p­los dele e tam­bém não con­segui, você pode­ria me dar alguma dica de como pro­ceder para con­seguir com­pi­lar seus exem­p­los e o ucos II em x86 ou 64 ?
     
    Agradećo desde já a atencão
     
    Douglas

    • http://sergioprado.org ser­gio­prado

      Olá Dou­glas,

      Você tem razão, a Micrium não esta mais disponi­bi­lizando o port do Ucos-II para x86/Linux…:(

      Vou ver se acho a ver­são que baixei e te envio.

      Abraços!

  • Car­los Sosa

    Muito bom trabalho!

Navegue
Creative Commons Este trabalho de Sergio Prado é licenciado pelo
Creative Commons BY-NC-SA 3.0.