Atividade #373: Estudo e aplicação mais aprofundada de um firmware com FreeRTOS - Eletronica - Redmine RoboIME
Atividade #373
Meta #318: Firmware: Implementação do RTOS
Estudo e aplicação mais aprofundada de um firmware com FreeRTOS
Added by Onias Castelo Branco over 3 years ago. Updated over 3 years ago.
A ideia dessa tarefa é para que eu possa tentar desenvolver algo usando FreeRTOS para criar algum hábito antes de partir pro código enorme do firmware.
new.cpp (1.88 KB) Luiz Renault Leite Rodrigues, 10/31/2017 10:15 AM
TX.JPG (40.9 KB) Onias Castelo Branco, 11/02/2017 07:50 PM
RX.PNG (9.17 KB) Gustavo Claudio Karl Couto, 11/02/2017 07:52 PM
161204_Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf (3.12 MB) Onias Castelo Branco, 11/04/2017 02:06 PM
#1 Updated by Onias Castelo Branco over 3 years ago
O foco da atividade esta em fazer o firmware atual rodar em um projeto com os arquivos do freertos, com o firmware do robo seja uma atividade única do rtos. A partir disso, será dividido em mais atividades.
#2 Updated by Onias Castelo Branco over 3 years ago
Foi feito o primeiro exemplo do livro Mastering FreeRTOS. Tivemos diversos problemas que aprendemos a resolver, como as bibliotecas de freertos nao sendo compiladas e a main não tinha funções importantes para o funcionamento do freertos.
O exemplo do livro, ele imprime no terminal que esta alternando as tarefas. Isso nao foi feito ainda. Será a próxima parte que farei desta tarefa: imprimir no terminal do atollic que as tarefas estão sendo alternadas.
Código usado: https://github.com/OniasC/FreeRTOS_study
#3 Updated by Onias Castelo Branco over 3 years ago
Juntamos ao projeto o freertos. Porém, apareceram vários problemas, como múltiplas definições de variaveis (até a ultima compilação, estava sendo acusada a PendSV_Handler). E comentamos toda a main para ver se os erros em relação ao freertos sumiam (por ex, multiplas referências). Por enquanto nao.
https://github.com/roboime/roboime-firmware/tree/FreeRTOS-test
A PendSV deve estar duplicada, pois é usada no FreeRTOS e está implementada vazia no firmware padrão. Basta comentá-la.
Código compilando comitado.
#6 Updated by Onias Castelo Branco over 3 years ago
Eu e o gustavo começamos fazendo um firmware com o freertos que piscava um led com uma tarefa unica. Após isso começamos a inserir as funções do firmware da ssl com o objetivo de fazer funcionar a comunicação serial dele. Conseguimos fazer o mesmo led piscar e se comunicar com o tera term em uma única tarefa. Em seguida, separamos as tarefas para estudar como reagiria o código.
Problemas que tivemos no desenvolvimento:
o firmware que estamos usando está ocupando mta memória - o que ocupa o espaço das filas das tasks, fora fazer o firmware demorar a passar pra placa depois de compilado.
o erro que acusava não era de compilação, mas ora o firmware entrava no loop do vApplicationMallocFailedHook (quando colocavamos stacks de memória muito altos pras 2 tasks), ora dava hardfault, ora dava Stackoverflow (quando botavamos memória de menos). No fim conseguimos fazer funcionar a comunicação com o tera term escolhendo tamanho de stacks carteados de 150 pro led piscar e 800 pra COM. Eu li na documentação que era difícil estimar esse número com precisão. Mas algo parece errado.
link para o código que fizemos ontem: https://github.com/OniasC/TXFreeRTOS_SSL
#7 Updated by Onias Castelo Branco over 3 years ago
Falamos pro atollic ignorar alguns arquivos na hr de compilar e passar pra placa, aos moldes do firmware atual, mas mesmo assim estamos com esse problema no rendimento. O sr tem ideia de onde podemos ter errado?
#8 Updated by Luiz Renault Leite Rodrigues over 3 years ago
Qual o tamanho do código compilado?
Quanto tempo está levando para gravar?
É possível estimar o tamanho da stack fazendo a análise do código. O tamanho necessário será o das variáveis alocadas em tempo de execução mais o número de registradores vezes o número de chamadas aninhadas a funções.
Entretanto, como é uma tarefa chada, pode usar em torno de 120 + tamanho em palavras das variáveis alocadas.
O Hardfault deve ser devido à stack overflow em situações onde o FreeRTOS não consegue detectar (ele só detecta na transição entre as tarefas).
Durante o desenvolvimento, use sempre um breakpoint em vApplicationMallocFailedHook e vApplicationStackOverflowHook.
A memória total disponível está como 7KB: #define configTOTAL_HEAP_SIZE ( ( size_t ) ( 7 * 1024 ) )
Uma pilha de tamanho 800 ocupa 800*4 bytes
Qual esquema de memória estão usando?
#9 Updated by Onias Castelo Branco over 3 years ago
Documentação do site do freeRTOS sobre as diferenças entre os heaps (heaps_1, heaps_2, heap_3, heap_4 e heap_5): http://www.freertos.org/a00111.html
heap_2 - permits memory to be freed, but not does coalescence adjacent free blocks.
#10 Updated by Onias Castelo Branco over 3 years ago
Tentei implementar uma tarefa com nrf porém houveram problemas:
primeiro que quando coloquei as funções na bsp.cpp o atollic deu um erro "arm-none-eabi-gdb.exe has stopped working". Comentei todas uma a uma para achar o erro quando descomentei todas o programa rodou e foi passado pra placa sem problemas.
Criei uma task a mais para inicializar o nrf24, porém ficou chaveando entre MallocFailedHook e HardFault dependendo do quanto de stacks eu colocava para cada função.
A modificação mais recente foi commitada no https://github.com/OniasC/TXFreeRTOS_SSL
#11 Updated by Luiz Renault Leite Rodrigues over 3 years ago
Este problema do gdbserver ocorre normalmente por ter um número excessivo de breakpoints definidos, inclusive em múltiplos projetos abertos. Não é relativo ao código em si.
Deve fazer com que as áreas de stack e heap comportem as aplicações das tarefas.
Qual o tamanho das áreas de stack e heap estão usando?
#12 Updated by Luiz Renault Leite Rodrigues over 3 years ago
File new.cpp added
Outra questão é quando você está utilizando o gerenciamento de memória do RTOS, você precisa usar suas funções de alocação e liberação para todos os objetos.
Então não pode-se utilizar malloc, calloc, e free diretamente da linguagem C.
Também deverá sobrecarregar os operadores New, Delete, New[] e Delete[] de C++.
Segue o código que faz isso.
Ao analisar rapidamente o código vejo alocações de memória do tipo:
NRF24L01P::NRF24L01P(SPI &spi, IO_Pin &SS_PIN, IO_Pin &CE_PIN, IO_Pin &NIRQ_PIN):
MODEM(),
spi(&spi),
_SS_PIN(&SS_PIN),
_NIRQ_PIN(&NIRQ_PIN),
_CE_PIN(&CE_PIN),
_rxbuffer(0,2048),
_txbuffer(0,2048),
_channel(0),
_retransmitirq_count(0),
receive_irq_count(0),
_transmit_irq_count(0),
_busy(1),
REG(REGDEFAULT)
Onde são criados dois objetos _rxbuffer(0,2048), _txbuffer(0,2048), que na verdade usam o operador new[] para a alocação de memória.
Assim, sua Heap deverá comportar todos esses objetos criados.
Basicamente, do ponto de vista do usuário, na stack são alocadas as variáveis locais quando declaradas como "uint8_t test[2048];" e na heap como "uint8_t *test=malloc(2048);".
No lugar de malloc deve-se usar pvPortMalloc eno lugar de free, vPortFree.
Faça a análise da quantidade de memória heap necessária e configure o FreeRTOS de acordo.
#13 Updated by Gustavo Claudio Karl Couto over 3 years ago
#14 Updated by Gustavo Claudio Karl Couto over 3 years ago
A stack é mais rápida e quando você declara normalmente as variáveis elas vão para a stack, que é uma pilha toda vez que sai do processo as variáveis ligadas aquele processo são guardadas na pilha. Ela também é de tamanho mais limitado, então seria melhor que esses vetores grandes fossem guardados na heap, para usar a heap no cpp declare as variáveis através do método new, como no exemplo de código acima,
Pelo que o capitão observou talvez precise sobrecarregar o método new e delete das classes para usar o malloc do freertos,
É importante fazer isso soh para as classes que tem muito consumo de memória, no caso a que vc usa é soh a do buffer circular, que é importante alocar na heap, pq essas estruturas são grandes e serão compartilhadas por vários processos.
#15 Updated by Gustavo Claudio Karl Couto over 3 years ago
Aki tem o bizú de como sobrecarregar o new para usar o pVmalloc do freeRtos ao invés de usar o malloc padrão que é do linker,
http://jacobjennin.gs/post/141861963680/freertos-and-the-c-new-operator
#16 Updated by Luiz Renault Leite Rodrigues over 3 years ago
Não confundam as coisas. A Stack possui exatamente a mesma velocidade de acesso que a memória Heap pois eles compartilham as mesmas memórias físicas.
Os acessos às variáveis na pilha e heap são feitos exatamente da mesma forma. Se você checar o código em assembly gerado pelo compilador, verá que são idênticos.
A única diferença é que na alocação, há um overhead de processamento de alocação dinâmica de memória para manter, por exemplo, as tabelas de memórias alocadas e livres. Só isso. Na execução do código e utilização da memória, as velocidades são as mesmas. Podem até fazer um código demo para mostrar esta questão, se quiserem.
Os tamanhos de memória são completamente customizáveis. Assim, a pilha pode ocupar muitas vezes o tamanho da heap disponível, vai depender da sua programação. De fato, imagine um espaço físico de memória. A stack começa do final para o início, e a heap do início para o final. Se as duas se encontrarem, significa que seu sistema está sem memória disponível. Mas o crescimento delas, só depende da sua programação.
Na verdade, é mandatório sobrecarregar os operadores new e delete, pois ele são utilizados quando instanciados objetos. Para fazer isso, basta incluir no seu projeto o arquivo new.cpp que enviei. Este já está testado. Não vai achar muita coisa sobre isso na net, pois não está tão comum o uso de FreeRTOS com C++.
Como sugestão, sempre que copiar um texto, coloque a referência, para caso os leitores queiram se aprofundar no assunto. https://stackoverflow.com/questions/79923/what-and-where-are-the-stack-and-heap
Nota: A heap não é responsável pelo vazamento de memória. Esta responsabilidade é do usuário, quando a utiliza de forma errada, esquecendo de liberar a memória.
#17 Updated by Onias Castelo Branco over 3 years ago
Para a task de acender o led: 150 de stack depth. Acender o segundo led e inicializar o nrf: 700 de stack. Inicializar a com via serial: 700 de stack.
Quanto a memoria heap, eu n sei como checar isso. No livro q tem no site deles fala que a partir do 9.0.0 pode usar alocação estatica de memoria, sem a necessidade de gerenciador de memoria heap.
#18 Updated by Luiz Renault Leite Rodrigues over 3 years ago
Quando me refiro ao tamanho, quero dizer o disponível para o OS.
É interessante tentarem entender o funcionamento, pois o firmware, ou o OS, seja ele real time ou não, não é um ente mágico, que funciona sem explicação.
Esse entendimento pode ser obtido acompanhando a execução do código do próprio FreeRTOS.
Quando criam uma tarefa, por exemplo, ele aloca na heap a pilha da tarefa. Assim, a heap tem que ser grande o suficiente para caber todas as pilhas.
E onde está essa heap?
O OS não adivinha qual uC você está usando e escolhe automaticamente o local da memória e tamanho da heap. A declaração está no próprio código.
Tente identificar este parâmetro no header de configuração do FreeRTOS e onde ela é alocada.
Entendendo estas questões, e sabendo onde cada variável ou objeto é alocado, vocês não terão problema com organização de memória no FreeRTOS ou qualquer outro sistema operacional de tempo real.
Tentem responder agora onde é qual o tamanho alocado para a heap, e qual o tamanho mínimo necessário.
#19 Updated by Onias Castelo Branco over 3 years ago
File TX.JPG added
Fizemos a comunicação entre dois robos usando o freeRTOS. O que escrevi no meu pc apareceu no do gustavo através de dois robos, o RX e o TX.
#20 Updated by Gustavo Claudio Karl Couto over 3 years ago
File RX.PNG added
#21 Updated by Onias Castelo Branco over 3 years ago
Alterações no firmware disponíveis em:
https://github.com/OniasC/TXFreeRTOS_SSL/commit/ebf1f7a4064fffbfb02bb8c3401a0eaa937a0e2a
#22 Updated by Onias Castelo Branco over 3 years ago
Começamos a fazer a task para controlar os motores. Conseguimos mandar um degrau predefinido pra ela. Porém tivemos problemas para setar o controle de velocidade, mais especificamente na hora de implementar a interrupção por timer. Amanhã analisaremos isso com mais cuidado. As mudanças mais recentes ja foram commitadas.
#23 Updated by Onias Castelo Branco over 3 years ago
Consertamos a interrupção para que fosse possível controle de velocidade. O problema era que havia um conflito nas prioridades das interrupções, este link - http://www.freertos.org/RTOS-Cortex-M3-M4.html - mostra como corrigir o problema.
Após isso, implementamos uma fila de referência (ponteiro de velocidade dos motores) para estabelecer contato entre a task da linha de comando e da controle de velocidade.
No último commit, foi subida uma versão que permite o controle de velocidade de uma roda através da linha de comando. Sendo uma task responsavel pela cmd line, outra pra controle da roda e uma queue por referecia para mandar dados de uma pra outra.
#24 Updated by Luiz Renault Leite Rodrigues over 3 years ago
O que é uma queue por referência? O FreeRTOS sempre passa os itens da queue por cópia.
#25 Updated by Onias Castelo Branco over 3 years ago
Segundo a documentação do freeRTOS, a queue por copia pode sr usada como uma queue por referencia.
There are two ways in which queue behavior could have been implemented:
1. Queue by copy
Queuing by copy means the data sent to the queue is copied byte for byte into the
2. Queue by reference
Queuing by reference means the queue only holds pointers to the data sent to the
queue, not the data itself.
fonte: Mastering the FreeRTOS Real Time Kernel pag 131
#26 Updated by Onias Castelo Branco over 3 years ago
File 161204_Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf added
Segue em anexo o livro
#27 Updated by Luiz Renault Leite Rodrigues over 3 years ago
A forma de fazer está certa, mas não pode chamar de by reference. Só para não misturar os termos. O correto é por cópia. Entretanto, estão enviando cópias de ponteiros.
Há outras implementações de RTOS que implementam fila por referência, o que não é o que o livro descreve.
Utilizando as cópias das estruturas (que contém os ponteiros) é uma forma eficiente de fazer. Se os dados forem de tamanhos definidos e fixos, podem utilizar a estrutura completa, já com o espaço para os dados. Vai ser ainda mais eficiente, ao custo de pré-alocar a memória necessária para a fila. Com isso não terão que se preocupar com alocação e liberação de memória.