Toda a verdade sobre o RTOS. Artigo 26. Canais: serviços auxiliares e estruturas de dados



Neste artigo, continuaremos a considerar os canais de transmissão de dados.

Serviços de suporte ao canal


O Nucleus RTOS possui quatro chamadas de API que fornecem funções auxiliares relacionadas aos canais: redefinir um canal, recuperar informações do canal, recuperar o número de canais em um aplicativo e recuperar ponteiros para todos os canais em um aplicativo. As três primeiras funções são implementadas no Nucleus SE.

Artigos anteriores da série:

Artigo 25. Canais de Dados: Introdução e Serviços Básicos
Artigo 24. Filas: serviços auxiliares e estruturas de dados
Artigo 23. Filas: introdução e serviços básicos
Artigo 22. Caixas de correio: serviços auxiliares e estruturas de dados
Artigo 21. Caixas de correio: Introdução e serviços básicos
Artigo 20. Semáforos: Serviços Auxiliares e Estruturas de Dados
Artigo 19. Semáforos: introdução e serviços básicos
Artigo # 18 Grupos de Sinalizadores de Eventos: Serviços Auxiliares e Estruturas de Dados
Artigo 17. Grupos de Sinalizadores de Eventos: Introdução e Serviços Básicos
Artigo 16. Signals
Artigo 15. Partições de memória: serviços e estruturas de dados
Artigo 14. Seções de memória: introdução e serviços básicos
Artigo 13. Estruturas de dados da tarefa e chamadas de API não suportadas
Artigo 12. Serviços para trabalhar com tarefas
Artigo 11. Tarefas: configuração e introdução à API
Artigo 10. Agendador: recursos avançados e preservação de contexto
Artigo 9. Agendador: implementação
Artigo 8. Núcleo SE: Projeto Interno e Implantação
Artigo # 7 Núcleo SE: Introdução
Artigo 6. Outros serviços RTOS
Artigo 5. Interação e sincronização de tarefas
Artigo 4. Tarefas, alternância de contexto e interrupções
Artigo # 3 Tarefas e planejamento
Artigo 2. RTOS: estrutura e modo em tempo real
Artigo 1. RTOS: introdução.

Redefinição de canal


Essa chamada de API redefine o canal para seu estado original e não utilizado. Quaisquer mensagens armazenadas nele serão perdidas. Todas as tarefas suspensas no canal são retomadas com o código de retorno NUSE_PIPE_WAS_RESET .

Chamada de redefinição de canal no Nucleus RTOS

Protótipo de chamada de serviço:

STATUS NU_Reset_Pipe (NU_PIPE * pipe);

Parâmetros:

pipe - um ponteiro para um bloco de controle de canal definido pelo usuário.

Valor de retorno:

NU_SUCCESS - a chamada foi concluída com sucesso;
NU_INVALID_PIPE - ponteiro de canal inválido.

Desafio de redefinição de canal no Nucleus SE

Esta chamada de serviço da API suporta a funcionalidade principal da API Nucleus RTOS.

Protótipo de chamada de serviço:

STATUS NUSE_Pipe_Reset (canal NUSE_PIPE);

Parâmetros:

pipe é o índice (ID) do pipe que está sendo descartado.

Valor de retorno:

NUSE_SUCCESS - a chamada foi concluída com sucesso;
NUSE_INVALID_PIPE - índice de canal inválido.

Implementação de redefinição de canal no Nucleus SE

O código para a função NUSE_Pipe_Reset () (depois de verificar os parâmetros) é bastante simples. Os índices de início e término do canal, bem como o contador de mensagens no canal, são definidos como 0.

Se o bloqueio de tarefas estiver ativado, um código adicional será responsável por restaurar as tarefas suspensas:

while (NUSE_Pipe_Blocking_Count[pipe] != 0) { U8 index; /* check whether any tasks are blocked */ /* on this pipe */ for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_PIPE_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == pipe)) { NUSE_Task_Blocking_Return[index] = NUSE_PIPE_RESET; NUSE_Task_Status[index] = NUSE_READY; break; } } NUSE_Pipe_Blocking_Count[pipe]--; } #if NUSE_SCHEDULER_TYPE == NUSE_PRIORITY_SCHEDULER NUSE_Reschedule(NUSE_NO_TASK); #endif 

Cada tarefa suspensa no canal recebe o status "pronto" com o código de retorno NUSE_PIPE_WAS_RESET . Após a conclusão desse processo, se o agendador de prioridade for usado, a função NUSE_Reschedule () será chamada , pois uma ou mais tarefas com alta prioridade podem estar prontas para execução.

Informações do canal


Esta chamada de serviço retorna informações do canal. A implementação dessa chamada no Nucleus SE difere do Nucleus RTOS, pois retorna menos informações. Isso ocorre porque a nomeação de objetos, as mensagens de tamanho variável e a ordem de pausa da tarefa não são suportadas no Nucleus SE e a pausa da tarefa pode ser desativada.

Chamada para informações de canal no Nucleus RTOS
Protótipo de chamada de serviço:

STATUS NU_Pipe_Information (NU_PIPE * pipe, nome CHAR *, VOID ** start_address, UNSIGNED * pipe_size, UNSIGNED * disponível, mensagens UNSIGNED *, OPTION * message_type, UNSIGNED * message_size, OPTION * suspend_type, UNSIGNED * tasks_waask **;

Parâmetros:

pipe - ponteiro para o bloco de controle de canal fornecido pelo usuário;
nome - ponteiro para a área de 8 caracteres para o nome da mensagem do canal;
start_address - um ponteiro para um ponteiro no qual o endereço do início da área de dados do canal será gravado;
pipe_size - um ponteiro para uma variável para armazenar o número total de bytes no canal;
available - ponteiro para uma variável para armazenar o número de bytes disponíveis no canal;
messages - um ponteiro para uma variável para armazenar o número de mensagens no canal;
message_type - um ponteiro para uma variável para armazenar o tipo de mensagem suportado pelo canal. Pode levar os valores NU_FIXED_SIZE e NU_VARIABLE_SIZE ;
message_size - ponteiro para uma variável para armazenar o número de bytes em cada mensagem do canal. Se o canal suportar mensagens de tamanho variável, esse número será o tamanho máximo da mensagem;
suspend_type - um ponteiro para uma variável para armazenar o tipo de tarefa de suspensão. Pode levar os valores NU_FIFO e NU_PRIORITY ;
tasks_waiting - um ponteiro para uma variável para armazenar o número de tarefas suspensas neste canal;
first_task - um ponteiro para um ponteiro para a primeira tarefa pausada.

Valor de retorno:

NU_SUCCESS - a chamada foi concluída com sucesso;
NU_INVALID_PIPE - ponteiro de canal inválido.

Ligue para obter informações sobre o canal no Nucleus SE
Essa chamada de API suporta a funcionalidade principal da API do Nucleus RTOS.

Protótipo de chamada de serviço:

STATUS NUSE_Pipe_Information (canal NUSE_PIPE, endereço ADDR * start_address, U8 * tamanho_do_ pipe, U8 * disponível, U8 * disponível, mensagens U8 *, U8 * tamanho_da mensagem, U8 * tarefas_a espera, NUSE_TASK * first_task);

Parâmetros:

pipe - índice do canal, informações sobre as quais é solicitada;
start_address - ponteiro para uma variável do tipo ADDR para armazenar o endereço do início da área de dados do canal;
pipe_size - um ponteiro para uma variável do tipo U8 para armazenar o número total de mensagens que o canal pode receber;
disponível - um ponteiro para uma variável do tipo U8 para armazenar o número de mensagens para as quais resta espaço livre no canal;
messages - um ponteiro para uma variável do tipo U8 para armazenar o número atual de mensagens no canal;
message_size - ponteiro para uma variável do tipo U8 para armazenar o tamanho das mensagens processadas por este canal;
tasks_waiting - um ponteiro para uma variável para armazenar o número de tarefas suspensas neste canal (nada será retornado se a suspensão da tarefa estiver desativada);
first_task - um ponteiro para uma variável do tipo NUSE_TASK , que aceita o índice da primeira tarefa suspensa (nada será retornado se a suspensão da tarefa estiver desativada).

Valor de retorno:

NUSE_SUCCESS - a chamada foi concluída com sucesso;
NUSE_INVALID_PIPE - índice de canal inválido;
NUSE_INVALID_POINTER - um ou mais parâmetros do ponteiro estão incorretos.

Implementando informações do canal no Nucleus SE

A implementação desta chamada de API é bastante simples:

 *start_address = NUSE_Pipe_Data[pipe]; *pipe_size = NUSE_Pipe_Size[pipe]; *available = NUSE_Pipe_Size[pipe] - NUSE_Pipe_Items[pipe]; *messages = NUSE_Pipe_Items[pipe]; *message_size = NUSE_Pipe_Message_Size[pipe]; #if NUSE_BLOCKING_ENABLE *tasks_waiting = NUSE_Pipe_Blocking_Count[pipe]; if (NUSE_Pipe_Blocking_Count[pipe] != 0) { U8 index; for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_PIPE_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == pipe)) { *first_task = index; break; } } } else { *first_task = 0; } #else *tasks_waiting = 0; *first_task = 0; #endif 

A função retorna o status do canal. Em seguida, se os bloqueios de tarefas estiverem ativados, o número de tarefas pendentes e o índice do primeiro deles serão retornados (caso contrário, esses dois parâmetros serão definidos como 0).

Obtendo o número de canais


Esta chamada de serviço retorna o número de canais configurados no aplicativo. No Nucleus RTOS, esse valor pode mudar com o tempo e o valor de retorno indicará o número atual de canais. No Nucleus SE, o valor de retorno é definido durante a fase de construção e não pode ser alterado.

Ligue para um contador de canais no Nucleus RTOS
Protótipo de chamada de serviço:

UNSIGNED NU_Established_Pipes (VOID);

Parâmetros:
Estão ausentes.

Valor de retorno:
O número de canais criados no sistema.

Ligue para um contador de canais no Nucleus SE
Esta chamada de utilitário suporta a funcionalidade principal da API Nucleus RTOS

Protótipo de chamada de serviço:
U8 NUSE_Pipe_Count (nulo);

Parâmetros:
Estão ausentes.

Valor de retorno:
O número de canais configurados no aplicativo.

Implementando um contador de canais no Nucleus SE
A implementação desta chamada de API é bastante simples: o valor do símbolo #define NUSE_PIPE_NUMBER é retornado .

Estruturas de dados


Os canais usam seis ou sete estruturas de dados (que estão na RAM ou na ROM), que são (como outros objetos do Nucleus SE) um conjunto de tabelas, cujo tamanho e número correspondem ao número de canais configurados e seus parâmetros.

Eu recomendo fortemente que o código do aplicativo não use acesso direto a essas estruturas de dados, mas faça referência a elas através das funções de API fornecidas. Isso evitará a incompatibilidade com versões futuras do Nucleus SE e efeitos colaterais indesejados, além de simplificar a portabilidade de aplicativos para o Nucleus RTOS. A seguir, é apresentada uma visão geral detalhada das estruturas de dados para simplificar o entendimento da chamada de serviço e do código de depuração.

Dados do kernel na RAM


Esses dados têm a seguinte estrutura:

NUSE_Pipe_Head [] é uma matriz de ponteiros do tipo U8 , com uma entrada para cada canal configurado e indicando o início do canal de mensagens. Usado como um índice de endereço em NUSE_Pipe_Data [] (veja abaixo).
NUSE_Pipe_Tail [] é uma matriz U8 que possui uma entrada para cada canal configurado e aponta para o final do canal de mensagens. Usado como um índice de endereço em NUSE_Pipe_Data [] (veja abaixo).
NUSE_Pipe_Items [] é uma matriz do tipo U8 , que possui uma entrada para cada canal configurado e é um contador do número atual de mensagens no canal. Esses dados são redundantes, pois esse valor pode ser obtido através dos índices de início e fim do canal, mas a presença de um contador simplifica o código.
NUSE_Pipe_Blocking_Count [] - essa matriz do tipo U8 contém contadores do número de tarefas bloqueadas em cada canal. Essa matriz é criada apenas se o suporte ao bloqueio de tarefas estiver ativado.

Todas essas estruturas de dados são inicializadas com zeros pela função NUSE_Init_Pipe () quando o Nucleus SE é iniciado. Isso é lógico, pois todos os canais são criados vazios (não utilizados). Um dos artigos a seguir fornecerá uma descrição completa dos procedimentos de inicialização do Nucleus SE.

A seguir, estão as definições dessas estruturas de dados no arquivo nuse_init.c :

 RAM U8 NUSE_Pipe_Head[NUSE_PIPE_NUMBER]; RAM U8 NUSE_Pipe_Tail[NUSE_PIPE_NUMBER]; RAM U8 NUSE_Pipe_Items[NUSE_PIPE_NUMBER]; #if NUSE_BLOCKING_ENABLE RAM U8 NUSE_Pipe_Blocking_Count[NUSE_PIPE_NUMBER]; #endif 

Dados do usuário da RAM


É de responsabilidade do usuário fornecer uma área de dados na RAM para armazenar os dados de cada canal configurado. O tamanho dessa área deve conter uma matriz do tipo U8 , na qual todas as mensagens do canal serão ajustadas.

Dados ROM


A estrutura desses dados é a seguinte:

NUSE_Pipe_Data [] é uma matriz do tipo ADDR que possui um registro para cada canal configurado e indica a área de dados de cada canal (consulte a seção "Dados do usuário na RAM" acima).
NUSE_Pipe_Size [] é uma matriz do tipo U8 , com uma entrada para cada canal configurado e mostrando o número de mensagens que podem caber em cada canal.
NUSE_Pipe_Message_Size [] é uma matriz do tipo U8 que possui um registro para cada canal configurado e mostra o tamanho das mensagens (em bytes) que podem ser colocadas em cada canal.

Essas estruturas de dados são declaradas e inicializadas (estaticamente) no arquivo nuse_config.c , assim:

 ROM ADDR *NUSE_Pipe_Data[NUSE_PIPE_NUMBER] = { /* addresses of pipe data areas ------ */ }; ROM U8 NUSE_Pipe_Size[NUSE_PIPE_NUMBER] = { /* pipe sizes ------ */ }; ROM U8 NUSE_Pipe_Message_Size[NUSE_PIPE_NUMBER] = { /* pipe message sizes ------ */ }; 

Memória do canal


Como todos os outros objetos principais do Nucleus SE, a quantidade de memória necessária para os canais é previsível.

A quantidade de dados na ROM (em bytes) para todos os canais no aplicativo pode ser calculada da seguinte maneira:

NUSE_PIPE_NUMBER * (sizeof (ADDR) + 2)

A quantidade de dados do kernel na RAM (em bytes) para todos os canais de aplicativos quando as tarefas são ativadas pode ser calculada da seguinte maneira:

NUSE_PIPE_NUMBER * 4

Caso contrário:

NUSE_PIPE_NUMBER * 3

A quantidade de dados do usuário em RAM (em bytes) para o canal com o índice de canal:

NUSE_Pipe_Size [pipe] * NUSE_Pipe_Message_Size [pipe]

Chamadas de API não realizadas


Quatro chamadas de serviço da API Nucleus RTOS não são implementadas no Nucleus SE.

Criação de canal


Essa chamada de API cria um canal. O Nucleus SE não precisa disso porque os canais são criados estaticamente.

Protótipo de chamada de serviço:

STATUS NU_Create_Pipe (NU_PIPE * pipe, nome CHAR *, VOID * start_address, UNSIGNED pipe_size, OPTION message_type, UNSIGNED message_size, OPTION suspend_type);

Parâmetros:

pipe - um ponteiro para o bloco de controle de canal fornecido pelo usuário; ele será usado como o ativador do canal principal em outras chamadas de API;
name - ponteiro para um nome de canal de 7 caracteres com um zero final;
endereço_início - endereço inicial do canal;
pipe_size - o número total de bytes no canal;
message_type - tipo de mensagem suportado pelo canal. Pode levar os valores NU_FIXED_SIZE ou NU_VARIABLE_SIZE ;
message_size - se o canal suportar mensagens de tamanho fixo, este parâmetro indica o tamanho exato de cada mensagem. Caso contrário, se o canal suportar mensagens de tamanho variável, esse valor será o tamanho máximo da mensagem;
suspend_type - indica o tipo de suspensão da tarefa no canal. Pode levar os valores NU_FIFO e NU_PRIORITY (planejador FIFO e planejador PRIORITY, respectivamente).

Valor de retorno:

NU_SUCCESS - a chamada foi concluída com sucesso;
NU_INVALID_PIPE - ponteiro nulo para a unidade de controle de canal ( NULL ), ou a unidade de controle já está em uso;
NU_INVALID_MEMORY - uma área de dados incorreta foi especificada em start_address ;
NU_INVALID_MESSAGE - parâmetro message_type inválido;
NU_INVALID_SIZE - o tamanho da mensagem é maior que o tamanho do canal ou o tamanho do canal ou da mensagem é zero;
NU_INVALID_SUSPEND - parâmetro suspend_type inválido.

Excluir canal


Essa chamada de API exclui um canal criado anteriormente. O Nucleus SE não precisa, porque os canais são criados estaticamente e não podem ser excluídos.

Protótipo de chamada de serviço:
STATUS NU_Delete_Pipe (NU_PIPE * pipe);

Parâmetros:
pipe - um ponteiro para um bloco de controle de canal.

Valor de retorno:
NU_SUCCESS - a chamada foi concluída com sucesso;
NU_INVALID_PIPE - ponteiro de canal inválido.

Ponteiros de canal


Essa chamada de API cria uma lista seqüencial de ponteiros para todos os canais no sistema. No Nucleus SE, não é necessário, pois os canais são identificados por um índice simples, não por um ponteiro; portanto, essa função seria redundante.

Protótipo de chamada de serviço:
UNSIGNED NU_Pipe_Pointers (NU_PIPE ** pointer_list, UNSIGNED maximum_pointers);

Parâmetros:
pointer_list - um ponteiro para uma matriz de ponteiros NU_PIPE . Essa matriz será preenchida com ponteiros para os canais criados anteriormente no sistema;
ponteiros máximos - o número máximo de ponteiros na matriz.

Valor de retorno:
O número de ponteiros NU_PIPE na matriz

Transmitir para um canal


Essa chamada de API passa a mensagem para todas as tarefas que aguardam mensagens de um canal específico. No Nucleus SE, esse recurso não foi implementado, pois adiciona complexidade redundante.

Protótipo de chamada de serviço:
STATUS NU_Broadcast_To_Pipe (canal NU_PIPE *, mensagem VOID *, tamanho NÃO ASSINADO, suspensão NÃO ASSINADA);

Parâmetros:
pipe - um ponteiro para um bloco de controle de canal;
message - ponteiro para a mensagem transmitida;
tamanho - o número de elementos de dados NÃO ASSINADOS na mensagem. Se o canal suportar mensagens de tamanho variável, esse parâmetro deverá ser igual ou menor que o tamanho da mensagem suportada pelo canal. Se o canal suportar mensagens de tamanho fixo, esse parâmetro deverá ser exatamente igual ao tamanho das mensagens suportadas pelo canal;
suspender - indica se a tarefa de chamada deve ser suspensa se o canal já estiver cheio. Pode ser NU_NO_SUSPEND , NU_SUSPEND ou um valor de tempo limite.

Valor de retorno:
NU_SUCCESS - a chamada foi concluída com sucesso;
NU_INVALID_PIPE - ponteiro inválido para o canal;
NU_INVALID_POINTER - ponteiro nulo para uma mensagem ( NULL );
NU_INVALID_SIZE - o tamanho da mensagem especificado é incompatível com o tamanho da mensagem especificado ao criar o canal;
NU_INVALID_SUSPEND - tente suspender de um thread não relacionado à tarefa;
NU_PIPE_FULL - não há espaço suficiente no canal para a mensagem;
NU_TIMEOUT - o canal ainda está cheio, mesmo após o término do tempo limite especificado;
NU_PIPE_DELETED - o canal foi excluído enquanto a tarefa estava suspensa;
NU_PIPE_RESET - O canal foi redefinido enquanto a tarefa estava suspensa.

Compatível com núcleo RTOS


Como com todos os outros objetos do Nucleus SE, meu objetivo era maximizar a compatibilidade do código do aplicativo com o Nucleus RTOS. Os canais não são exceção e, do ponto de vista do usuário, são implementados da mesma maneira que no Nucleus RTOS. Há também uma certa incompatibilidade, que eu considerei aceitável, já que, como resultado, o código se tornará mais compreensível e mais eficiente em termos da quantidade de memória necessária. Caso contrário, as chamadas da API do Nucleus RTOS poderão ser portadas quase diretamente para o Nucleus SE.

Identificadores de objeto


No Nucleus RTOS, todos os objetos são descritos por uma estrutura de dados (bloco de controle) que possui um tipo de dados específico. Um ponteiro para esta unidade de controle serve como um identificador de canal. Decidi que no Nucleus SE é 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 objetos configurados de cada tipo. O identificador de um objeto específico é o índice nesta tabela. Então, eu defini NUSE_PIPE como o equivalente a U8 , uma variável (não um ponteiro) desse tipo serve como identificador de canal. Essa pequena incompatibilidade é fácil de lidar se o código for portado do Nucleus SE para o Nucleus RTOS e vice-versa. Normalmente, nenhuma operação é executada nos identificadores de objeto além de mover e armazenar.

O núcleo RTOS também suporta a nomeação de canais. Esses nomes são usados ​​apenas para depuração. Excluí-os do Nucleus SE para economizar memória.

Tamanho e tipo da mensagem


No Nucleus RTOS, um canal pode ser configurado para processar mensagens que consistem em um número arbitrário de bytes, assim como o Nucleus SE. O núcleo RTOS também suporta canais de mensagens de comprimento variável para os quais apenas o tamanho máximo da mensagem é especificado no momento da criação. Mensagens de tamanho variável não são suportadas no Nucleus SE.

Tamanho do canal


No Nucleus SE, o número máximo de mensagens por canal é 256, pois todas as variáveis ​​e constantes são do tipo U8 . O Nucleuts RTOS não tem essas limitações.

Chamadas de API não realizadas


O núcleo RTOS suporta dez sobrecargas de canal. Quatro deles não estão implementados no Nucleus SE. Uma descrição detalhada dessas chamadas, bem como os motivos dessa decisão, pode ser encontrada na seção "Chamadas de API não realizadas", anteriormente neste artigo.

No artigo a seguir, consideraremos a hora do sistema.

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.

Source: https://habr.com/ru/post/pt433374/


All Articles