FreeRTOS V10.0.0 e as novas APIs de mensagens
- por Sergio Prado
Na semana passada escrevi sobre o lançamento do Amazon FreeRTOS, que aconteceu junto com a liberação do FreeRTOS V10.0.0.
Esta nova versão do FreeRTOS vem com diversas mudanças, dentre elas a inclusão de duas novas classes de funções para trocas de mensagens.
Até então, o único recurso básico existente para troca de mensagens no FreeRTOS eram os queues. A partir de agora, temos também os stream buffers e os message buffers.
STREAM BUFFERS
Stream Buffers implementam um conjunto de funções para trocas de dados (sequência de bytes) entre duas tarefas ou entre uma rotina de tratamento de interrupção (ISR) e uma tarefa.
Comparado a queues, os stream buffers são basicamente otimizados para comunicação um-para-um (tarefa->tarefa ou ISR->tarefa).
Para utilizar esta API é necessário incluir no projeto o arquivo stream_buffer.c. Um stream buffer pode ser criado com a função xStreamBufferCreate(), onde é possível definir o tamanho máximo do buffer e um trigger que será utilizado para desbloquear a rotina de leitura quando a quantidade de bytes enviados ao stream buffer atingir o valor configurado.
As rotinas xStreamBufferSend() e xStreamBufferReceive() podem ser utilizadas para respectivamente enviar e receber bytes do stream buffer.
Por exemplo, a tarefa abaixo irá tratar os dados recebidos da porta serial, se comunicando com a ISR da serial através de um stream buffer. Na inicialização, a tarefa irá criar um stream buffer de 256 bytes e depois bloquear esperando indefinidamente dados no stream buffer. A tarefa será desbloqueada quando receber pelo menos 16 bytes (valor definido como trigger na função de criação do stream buffer).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
void taskUART(void *pvParameters) { unsigned int qtdbytes; char msg[256]; xStreamBuffer = xStreamBufferCreate(256, 16); for (;;) { qtdbytes = xStreamBufferReceive(xStreamBuffer, (void *)msg, sizeof(msg), portMAX_DELAY); if (qtdbytes > 0) { // handle received bytes } } } |
A rotina de tratamento de interrupção da serial será responsável por enviar os dados recebidos para o stream buffer. Apesar da ISR enviar para o stream buffer os dados recebidos byte-a-byte, a rotina de leitura será desbloqueada apenas quando o valor configurado no trigger for atingido (16 bytes no exemplo acima).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
void UART0_IRQHandler(uint32_t instance) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; unsigned char ch; /* handle RX interrupt */ if((LPSCI_HAL_GetRxDataRegFullIntCmd(baseAddr)) && (LPSCI_HAL_IsRxDataRegFull(baseAddr))) { LPSCI_HAL_Getchar(baseAddr, &ch); xStreamBufferSendFromISR(xStreamBuffer, &ch, 1, &xHigherPriorityTaskWoken); } portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } |
Conforme comentei mais acima, é importante ressaltar que esta API está otimizada e permite apenas a comunicação um-para-um, onde apenas uma tarefa (ou ISR) é responsável por enviar dados para o stream buffer, e uma outra tarefa é responsável por receber estes dados. Se você precisar enviar ou receber dados com mais de uma tarefa, é necessário proteger as rotinas do stream buffer dentro de uma seção crítica, utilizando por exemplo um mutex ou desabilitando as interrupções (neste caso, o uso de um queue talvez faça mais sentido).
Outras funções de stream buffer ainda estão disponíveis e documentadas no site do FreeRTOS.
MESSAGE BUFFERS
As funções da API de message buffers permitem o envio de mensagens de tamanho variável. Como estas funções utilizam internamente os stream buffers, acabam herdando suas características como a obrigatoriedade da comunicação um-para-um (tarefa->tarefa ou ISR->tarefa).
Assim como no uso de stream buffers, para utilizar a API de message buffers é necessário incluir no projeto o arquivo stream_buffer.c. Um message buffer pode ser criado com a função xMessageBufferCreate() e as funções xMessageBufferSend() e xMessageBufferReceive() podem ser utilizadas respectivamente para enviar e receber mensagens do buffer.
Por exemplo, a tarefa abaixo irá receber e imprimir mensagens (strings) de tamanho variável. Na inicialização, a tarefa irá criar um message buffer de 256 bytes e depois bloquear indefinidamente esperando mensagens neste buffer.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
void taskRxMSG(void *pvParameters) { unsigned int qtdbytes; char msg[32]; xMessageBuffer = xMessageBufferCreate(256); for (;;) { qtdbytes = xMessageBufferReceive(xMessageBuffer, (void *)msg, sizeof(msg) - 1, portMAX_DELAY); msg[qtdbytes] = 0; printf("%s\n", msg); } } |
E a tarefa abaixo irá enviar, a cada 1 segundo, mensagens de tamanho variável para o message buffer:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
void taskTxMSG(void *pvParameters) { char *msgs[] = { "nostromo", "patna", "auriga", "prometheus", "covenant", }; unsigned int qtd = sizeof(msgs)/sizeof(msgs[0]); unsigned int ind = 0; for (;;) { xMessageBufferSend(xMessageBuffer, msgs[ind], strlen(msgs[ind]), portMAX_DELAY); if (++ind >= qtd) ind = 0; vTaskDelay(1000/portTICK_PERIOD_MS); } } |
A API completa da funcionalidade de message buffers está documentada no site do FreeRTOS.
OUTRAS ALTERAÇÕES
Além da inclusão destas duas novas APIs, o FreeRTOS v10.0.0 trouxe a implementação da pilha de protocolos TCP/IP (FreeRTOS+TCP) desenvolvida pela própria equipe do FreeRTOS.
Quem já precisou integrar ao FreeRTOS uma pilha de protocolos TCP/IP como a uIP ou a lwIP sabe das dificuldades que é realizar este trabalho. Esta nova pilha de protocolos vem para facilitar a nossa vida, é de código-aberto (licença MIT) e já está integrada às APIs do FreeRTOS.
No momento, estão disponíveis as implementações de ARP, IP, TCP, UDP, Berkeley Sockets, DHCP e DNS, e segundo a documentação tem um footprint de aproximadamente 20K quando compilada com o GCC para ARM Cortex-M (otimizando com o parâmetro -Os).
Para mais informações sobre esta pilha de protocolos, consulte a documentação no site do FreeRTOS.
Nesta nova versão do FreeRTOS foram adicionados ainda novos projetos de demonstração, correções e otimizações em alguns portes e mais algumas alterações cosméticas.
A lista completa de mudanças está disponível no histórico de alterações do FreeRTOS.
Um abraço,
Sergio Prado