Apesar de a maior parte do BRTOS ter sido coberta, existem alguns detalhes importantes que precisam ser explicados para o adequado funcionamento do sistema. Neste post vamos explorar a implementação da rotina BRTOS_CreateTask().
O controle de tarefas do sistemas é extremamente simples, composto por um vetor de TCBs chamado asBrtosTasks (veja post IV). Ao pedir a criação de uma tarefa, uma nova posição é usada neste vetor. Os parâmetros necessários são o ponto de entrada da tarefa (entry_point), o endereço da pilha (stack_addr), time slice (time_slice) e prioridade (pri). Estes parâmetros são guardados no vetor de TCBs (asBrtosTasks) e a tarefa é colocada no estado de “READY”, ou seja, pronta para executar. Vale lembrar que a maioria dos RTOS permite iniciar uma tarefa em estado de espera, ou seja, que não saia rodando imediatamente. Isto não está implementado no BRTOS.
asBrtosTasks[usNumTasks].pfEntryPoint = entry_point; asBrtosTasks[usNumTasks].pusStackBeg = (unsigned short *) stack_addr; /* ponteiro corrente da pilha */ asBrtosTasks[usNumTasks].pusStackPtr = (unsigned short *) stack_addr; asBrtosTasks[usNumTasks].usTimeSlice = MSEC_TO_TICKS(time_slice); asBrtosTasks[usNumTasks].ucPriority = pri; asBrtosTasks[usNumTasks].ucTaskState = BRTOS_TASK_STATE_READY;
Até aqui tudo bem, fácil e previsível. O que não é óbvio é que você precisa preparar a pilha inicial da tarefa. Releia o post anterior sobre o escalonador e verá que ele começa salvando os registros gerais (R3 a R15) e confia plenamente que na pilha estarão o SP e o PC, geralmente colocados lá pelo acontecimento da interrupção do escalonador (interrupção de watchdog, veja posts II e III). No entanto, isto não aconteceu no momento da criação da tarefa e é necessário imitar esta pilha inicial. Erre aqui e seu sistema provavelmente vai resetar.
E tem mais um detalhe que não pode ser ignorado. Comentamos no post IV que uma tarefa geralmente é um laço infinito mas que alguns cuidados precisam ser tomados caso ela retorne ou não seja um laço. Um retorno de modo não interrompido no MSP430, feita pela instrução ret,, apenas muda o PC para que está na pilha (sem SR, neste caso). O macete aqui é colocar um local válido e seguro para este novo PC, no nosso caso uma função chamada BRTOS_TaskEnd(), descrita mais abaixo.
O diagrama da pilha inicial da tarefa ficará então como a seguir. Lembre-se que a lógica de pilha do MSP430 é apontar para um valor livre e decrescer quando valores são adicionados (isto também é dependente de plataforma).
+-----------------+ | | <-- pusStackPtr +-----------------+ | R15 | +-----------------+ | R14 | +-----------------+ | ... | +-----------------+ | R3 | +-----------------+ | PC | +-----------------+ | SR | +-----------------+ | BRTOS_TaskEnd | <-- pusStackBeg +-----------------+
O código necessário para criar esta pilha inicial está a seguir:
/* if task returns some day, prepare stack */
*(asBrtosTasks[usNumTasks].pusStackPtr) = (unsigned short) BRTOS_TaskEnd;
asBrtosTasks[usNumTasks].pusStackPtr -=1;
/* status register and program counter */
*(asBrtosTasks[usNumTasks].pusStackPtr) = (unsigned short) 0;
asBrtosTasks[usNumTasks].pusStackPtr -=1;
*(asBrtosTasks[usNumTasks].pusStackPtr) = (unsigned short) entry_point;
asBrtosTasks[usNumTasks].pusStackPtr -=1;
/* prepare for first RestoreContext(): dummy NUM_REGS_IN_CONTEXT regs */
for(i = 0 ; i < NUM_REGS_IN_CONTEXT ; i++)
{
*(asBrtosTasks[usNumTasks].pusStackPtr) = (unsigned short) 0;
asBrtosTasks[usNumTasks].pusStackPtr -= 1;
}
E a implementação da função BRTOS_TaskEnd() é bem simples também, apenas coloca a tarefa no estado de “TERMINATED” e fica num laço infinito de forma que ela não estrague nada até que o escalonador rode novamente e escolha uma tarefa do grupo de tarefas prontas para serem executadas (READY). Daria para fazer algo melhor aqui sem grandes modificações, como uma implementação que permita um reinício (RESTART) da tarefa mesmo depois de ela ter ido para terminate, algo geralmente encontrado em sistemas de tempo real.
static void BRTOS_TaskEnd(void)
{
EnterCriticalSection();
asBrtosTasks[ucCurrentTask].ucTaskState = BRTOS_TASK_STATE_TERMINATED;
LeaveCriticalSection();
while(1);
}
No próximo post falaremos de mais um detalhe relacionado à inicialização do sistema. Até lá !