
Neste terceiro e último artigo de tarefas, examinarei as estruturas de dados do Nucleus SE e descreverei as chamadas da API do RTOS que não são implementadas no Nucleus SE, além de outros problemas de compatibilidade.
Artigos anteriores da série:
Artigo 12. Serviços para trabalhar com tarefasArtigo 11. Tarefas: configuração e introdução à APIArtigo 10. Agendador: recursos avançados e preservação de contextoArtigo 9. Agendador: implementaçãoArtigo 8. Núcleo SE: Projeto Interno e ImplantaçãoArtigo # 7 Núcleo SE: IntroduçãoArtigo 6. Outros serviços RTOSArtigo 5. Interação e sincronização de tarefasArtigo 4. Tarefas, alternância de contexto e interrupçõesArtigo # 3 Tarefas e planejamentoArtigo 2. RTOS: estrutura e modo em tempo real
Artigo 1. RTOS: introdução.
Estruturas de dados
As tarefas usam várias estruturas de dados (na RAM e na ROM), que, como outros objetos do Nucleus SE, são um conjunto de tabelas cujo tamanho corresponde ao número de tarefas e parâmetros selecionados.
Eu recomendo que o código do aplicativo acesse essas estruturas de dados usando funções da API, e não diretamente. Isso evita efeitos colaterais indesejados, incompatibilidade com versões futuras do Nucleus SE e também simplifica a portabilidade do aplicativo para o Nucleus RTOS. Para uma melhor compreensão da operação do código de chamada de serviço e do processo de depuração, é fornecida abaixo uma descrição detalhada das estruturas de dados.
Estruturas de dados do kernel hospedadas na RAM
Essas estruturas de dados incluem:
NUSE_Task_Context [] [] - uma matriz bidimensional do tipo
ADDR , possui uma linha para cada tarefa. O número de colunas depende da arquitetura do controlador e é determinado pelo símbolo
NUSE_REGISTERS , definido em
nuse_types.h . Essa matriz é usada pelo planejador para salvar o contexto de cada tarefa e foi descrita em detalhes na seção "Salvando o contexto" do artigo 10. Não criado se o agendador RTC for usado.
NUSE_Task_Signal_Flags [] - uma matriz do tipo
U8 , criada se os sinais estiverem ativados, e contém 8 sinalizadores de sinal para cada tarefa. Os sinais serão discutidos em um dos seguintes artigos.
NUSE_Task_Timeout_Counter [] é uma matriz do tipo
U16 , consiste em subtrair contadores para cada tarefa e é criada se a chamada para a API
NUSE_Task_Sleep () estiver ativada.
NUSE_Task_Status [] - uma matriz do tipo U8, contém o status de cada tarefa -
NUSE_READY ou status de suspensão. Criado apenas se a suspensão da tarefa estiver ativada.
NUSE_Task_Blocking_Return [] - uma matriz do tipo U8, criada se o bloqueio de chamadas da API estiver ativado. Ele contém um código de retorno que será usado após o bloqueio de chamadas da API. Geralmente, contém
NUSE_SUCCESS ou código indicando que o objeto foi redefinido (por exemplo,
NUSE_MAILBOX_WAS_RESET ).
NUSE_Task_Schedule_Count [] - uma matriz do tipo
U16 , contém um contador para cada tarefa e é criada apenas se a contagem do planejador tiver sido ativada.
NUSE_Task_Context [] [] é inicializado principalmente por zeros, exceto para entradas correspondentes ao registro de status (registro de status, SR), contador de programa (contador de programa, PC) e ponteiro de pilha (ponteiro de pilha, SP), aos quais são atribuídos valores iniciais (consulte "Dados na ROM "abaixo), e todas as outras estruturas de dados
NUSE_Init_Task () recebem zeros ao iniciar o Nucleus SE. Um dos artigos a seguir conterá uma lista completa dos procedimentos de inicialização do Nucleus SE com sua descrição.
A seguir, são definidas as estruturas de dados contidas no arquivo nuse_init.c.

Dados do usuário da RAM
O usuário deve definir uma pilha para cada tarefa (se o planejador RTC não for usado). Essas devem ser matrizes
ADDR , geralmente definidas em
nuse_config.c . Endereços e tamanhos de pilha devem ser colocados nas entradas de tarefa
NUSE_Task_Stack_Base [] e
NUSE_Task_Stack_Size [], respectivamente (consulte Dados na ROM).
Dados ROM
Uma ROM armazena de uma a quatro estruturas de dados relacionadas às tarefas. A quantidade exata depende dos parâmetros selecionados:
NUSE_Task_Start_Address [] é uma matriz do tipo
ADDR que possui uma entrada para cada tarefa, que é um ponteiro para o ponto de entrada de código da tarefa.
NUSE_Task_Stack_Base [] é uma matriz do tipo
ADDR que possui uma entrada para cada tarefa, que é um ponteiro para o endereço base da pilha da tarefa. Essa matriz é criada se qualquer agendador diferente do RTC for usado.
NUSE_Task_Stack_Size [] é uma matriz do tipo
U16 que possui uma entrada para cada tarefa, que mostra o tamanho da pilha da tarefa (em palavras). Essa matriz é criada se qualquer agendador diferente do RTC for usado.
NUSE_Task_Initial_State [] é uma matriz do tipo
U8 , com uma entrada para cada tarefa, que mostra o estado inicial da tarefa. Pode ser
NUSE_READY ou
NUSE_PURE_SUSPEND . Essa matriz é criada se o suporte ao estado inicial da tarefa estiver selecionado.
Essas estruturas de dados são declaradas e inicializadas (estaticamente) em
nuse_config.c :

A quantidade de memória para armazenar dados da tarefa (pegada de dados da tarefa)
Como todos os objetos principais do Nucleus SE, a quantidade de memória necessária para armazenar dados é previsível.
Tamanho da ROM (em bytes) necessário para todas as tarefas do aplicativo:
NUSE_TASK_NUMBER * sizeof (ADDR)Além disso, se qualquer agendador diferente de RTC estiver selecionado:
NUSE_TASK_NUMBER * (tamanho (ADDR) +2)Além disso, se o suporte ao estado inicial da tarefa estiver selecionado:
NUSE_TASK_NUMBERPara armazenar dados na RAM, a quantidade de memória (em bytes) é determinada pelos parâmetros selecionados e pode ter um valor zero se nenhum dos parâmetros for selecionado.
Se um agendador diferente de RTC for selecionado:
NUSE_TASK_NUMBER * NUSE REGISTERS * sizeof (ADDR)Além disso, se o suporte ao sinal estiver selecionado:
NUSE_TASK_NUMBERAlém disso, se a chamada para a API NUSE_Task_Sleep () estiver ativada:
NUSE_TASK_NUMBER * 2Além disso, se a suspensão da tarefa estiver ativada:
NUSE_TASK_NUMBERAlém disso, se o bloqueio de chamadas da API estiver ativado:
NUSE_TASK_NUMBERAlém disso, se o contador do planejador estiver ativado:
NUSE_TASK_NUMBER * 2Chamadas de API não implementadas no Nucleus SE
Listados abaixo estão sete chamadas de API disponíveis no Nucleus RTOS que não são implementadas no Nucleus SE.
Criar tarefa
Essa chamada de API cria uma tarefa do aplicativo. O Nucleus SE não precisa desse recurso porque as tarefas são criadas estaticamente.
Protótipo de chamada:
STATUS NU_Create_Task (tarefa NU_TASK *, nome CHAR *, VOID (* task_entry) (UNSIGNED, VOID *), args UNSIGNED, VOID * argv, endereço_ Stack_ID, número_da_ pilha, tamanho_da_ pilha, prioridade da opção ,_slice de tempo UNSIGNED, prioridade da opçãoParâmetros:
task - um ponteiro para um bloco de controle de tarefas do usuário, pode ser usado como um identificador / link ("identificador") de uma tarefa em outras chamadas de API;
nome - ponteiros para o nome da tarefa, uma cadeia de 7 caracteres com um zero final;
task_entry - indica a função de entrada para a tarefa;
argc - elemento de dados
NÃO ASSINADO que pode ser usado para passar informações iniciais para a tarefa;
argv - um ponteiro que pode ser usado para transmitir informações para a tarefa;
stack_address - define o setor inicial da memória para a pilha de tarefas;
stack_size - indica o número de bytes na pilha;
prioridade - indica o valor da prioridade da tarefa: de 0 a 255, onde números mais baixos correspondem à prioridade mais alta;
time_slice - indica o número máximo de intervalos de tempo que podem decorrer durante esta tarefa. Um valor de "0" desativa o tempo de corte para esta tarefa;
preempt - indica se a tarefa é suplantada ou não. Pode ter valores
NU_PREEMPT e
NU_NO_PREEMPT ;
auto_start - mostra o estado inicial da tarefa.
NU_START significa que a tarefa está pronta para execução e
NU_NO_START significa que a tarefa está suspensa.
Valor de retorno:
NU_SUCCESS - indica uma conclusão bem sucedida do serviço;
NU_INVALID_TASK - indica que o ponteiro para a unidade de controle de tarefas é
NULL ;
NU_INVALID_ENTRY - indica que o ponteiro para a função de entrada da tarefa é
NULL ;
NU_INVALID_MEMORY - indica que o setor de memória atribuído pelo parâmetro stack_address é zero (
NULL );
NU_INVALID_SIZE - indica que o tamanho da pilha especificado é insuficiente;
NU_INVALID_PREEMPT - indica que o parâmetro
preempt está definido incorretamente;
NU_INVALID_START - indica que o parâmetro
auto_start está definido incorretamente.
Excluir tarefa
Essa chamada de API exclui uma tarefa de aplicativo criada anteriormente que deve ser
concluída ou
finalizada . Essa chamada também não é necessária para o Nucleus SE, pois as tarefas são criadas estaticamente e não podem ser excluídas.
Protótipo de chamada:
STATUS NU_Delete_Task (tarefa NU_TASK *);Parâmetros:
task - ponteiro para o bloco de controle de tarefas
Valor de retorno:
NU_SUCCESS - indica uma conclusão bem sucedida do serviço;
NU_INVALID_TASK - indica que o ponteiro para a tarefa está definido incorretamente;
NU_INVALID_DELETE - indica que a tarefa não está no estado Concluído ou Terminado.
Obter indicadores de tarefas
Essa chamada de API compõe uma lista seqüencial de ponteiros para todas as tarefas no sistema. Não é necessário no Nucleus SE, pois as tarefas são identificadas usando um índice simples, não um ponteiro.
Protótipo de chamada:
UNSIGNED NU_Task_Pointers (NU_TASK ** pointer_list, UNSIGNED maximum_pointers);Parâmetros:
pointer_list - ponteiro para uma matriz de ponteiros
NU_TASK . Essa matriz será preenchida com ponteiros para as tarefas instaladas no sistema;
maximum_pointers - o número máximo de ponteiros que podem ser colocados na matriz.
Valor de retorno:
O número de ponteiros
NU_TASK colocados na matriz.
Alterar prioridade da tarefa
Essa chamada de API atribui à tarefa uma nova prioridade. No Nucleus SE, isso não é obrigatório, pois as prioridades das tarefas são constantes.
Protótipo de chamada:
OPTION NU_Change_Priority (tarefa NU_TASK *, OPTION new_priority);Parâmetros:
tarefa - um ponteiro para um bloco de controle de tarefas;
new_priority - define a prioridade de 0 a 255.
Valor de retorno:
O valor da prioridade da tarefa anterior.
Alterar algoritmo de preempção de tarefa
Essa chamada de API altera a ordem de exclusão da tarefa. O Nucleus SE não precisa dele porque usa um algoritmo de agendamento mais simples.
Protótipo de chamada:
OPTION NU_Change_Preemption (opção antecipada de OPTION);Parâmetros:
preempt - novo algoritmo
preemptivo , aceita
NU_PREEMPT ou
NU_NO_PREEMPTValor de retorno:
O algoritmo anterior para excluir uma tarefa.
Alterar a fatia do tempo da tarefa
Essa chamada de API altera o intervalo de tempo de uma tarefa específica. O Nucleus SE não precisa, pois as fatias de tempo da tarefa são corrigidas.
Protótipo de chamada:
UNSIGNED NU_Change_Time_Slice (tarefa NU_TASK *, UNSIGNED time_slice);Parâmetros:
tarefa - um ponteiro para um bloco de controle de tarefas;
time_slice - o número máximo de intervalos de tempo que podem decorrer durante esta tarefa; um valor zero desse campo desativa a quantização de tempo para esta tarefa.
Valor de retorno:
O valor anterior do quantum de tempo da tarefa.
Finalizar tarefa
Essa chamada de API conclui uma tarefa específica. O Núcleo SE não precisa disso porque o estado
Terminado não é suportado.
Protótipo de chamada:
ESTADO NU_Terminate_Task (NU_TASK * tarefa);Parâmetros:
task - um ponteiro para um bloco de controle de tarefas.
Valor de retorno:
NU_SUCCESS - indica uma conclusão bem sucedida do serviço;
NU_INVALID_TASK - indica que o ponteiro da tarefa está incorreto.
Compatível com núcleo RTOS
Ao desenvolver o Nucleus SE, um dos principais objetivos era garantir um alto nível de compatibilidade de código com o Nucleus RTOS. As tarefas não são exceção e, do ponto de vista do usuário, são implementadas da mesma maneira que no Nucleus RTOS. Em algumas áreas incompatíveis, cheguei à conclusão de que tal incompatibilidade seria aceitável, uma vez que o código final é mais fácil de entender e pode usar a memória com mais eficiência. No entanto, além dessas incompatibilidades, o restante das chamadas da API do Nucleus RTOS pode ser usado quase diretamente como o Nucleus SE chama. Um dos artigos a seguir fornecerá mais detalhes sobre a transição do Nucleus RTOS para o Nucleus SE
Identificadores de objeto
No Nucleus RTOS, todos os objetos são descritos por uma estrutura de dados (unidades de controle) que são de um tipo específico. Um ponteiro para esta unidade de controle serve como um identificador para a tarefa. No Nucleus SE, decidi que era necessária uma abordagem diferente para o uso eficiente da memória. Todos os objetos do kernel são descritos por um conjunto de tabelas na RAM e / ou ROM. O tamanho dessas tabelas é determinado pelo número de tipos de objetos. O identificador de um objeto específico é o índice nessas tabelas. Então, eu defini
NUSE_TASK como o equivalente a
U8 . Uma variável desse tipo (não um ponteiro) serve como um identificador para tarefas. Essa é uma pequena incompatibilidade que é fácil descobrir se o código é portado para ou a partir do Nucleus RTOS. Os identificadores de objetos geralmente são armazenados e transmitidos inalterados.
O núcleo RTOS também suporta a nomeação de tarefas. Esses nomes são usados apenas para depuração. Excluí-os do Nucleus SE para economizar memória.
Estados da tarefa
No Nucleus RTOS, as tarefas podem estar em um dos vários estados:
Execução ,
Pronto ,
Suspenso (o que leva à incerteza: a tarefa está em espera ou bloqueada por uma chamada de API),
Encerrada ou Concluída.
O Nucleus SE também suporta os estados
Executando e
Pronto . Todas as três opções
suspensas são suportadas opcionalmente. Não há suporte para Terminado e Concluído. Nenhuma chamada de API para concluir tarefas. Uma função de tarefa externa nunca deve retornar um valor explícita ou implicitamente (isso resultará em um estado
Finalizado no Nucleus RTOS).
Chamadas de API não realizadas
O núcleo RTOS suporta 16 chamadas do escritório para trabalhar com tarefas. Destes, 7 não estão implementados no Nucleus SE. Sua descrição e o motivo de sua exclusão estão descritos acima.
No próximo artigo, começaremos a analisar o gerenciamento de memória RTOS.
Sobre o autor: Colin Walls trabalha na indústria eletrônica há mais de trinta anos, dedicando a maior parte de seu tempo ao firmware. Ele agora é engenheiro de firmware na Mentor Embedded (uma divisão da Mentor Graphics). Colin Walls frequentemente fala em conferências e seminários, autor de vários artigos técnicos e dois livros sobre firmware. Vive no Reino Unido.
Blog profissional
de Colin , e-mail: colin_walls@mentor.com.