Documente Academic
Documente Profesional
Documente Cultură
0
The Ultimate Guide
In primo luogo vorrei esprimere la mia profonda gratitudine a Giovanni Di Sirio e tutti
coloro che lavorano, per il loro lavoro e il tempo a disposizione in questo eccellente RTOS -
ChibiOS.
A tradução deste guia foi feita com ajuda das ferramentas de tradução online, do Google e
da Microsoft.
Logo, ainda é um trabalho em andamento e após a tradução propriamente dita, foram feitas
algumas revisões.
Algumas palavras foram deixadas sem tradução intencionalmente, para que não fosse
perdido o real sentido, dentro da lógica de programação.
Qualquer ajuda para a melhoria e correção de erros será muito bem-vinda.
The translation of this guide was made with the help of online translation tools, Google and
Microsoft.
Therefore, it is still a work in progress and after the translation itself, have made some
revisions.
Some words were intentionally left untranslated, so it would not lost the real meaning within
the programming logic.
Any help to improve and error correction will be very welcome.
Página - 1 de 109
Sumário
Capítulo - 1 Introdução.........................................................................................................................8
1.1 Objetivos livro...........................................................................................................................8
1.2 História.......................................................................................................................................8
1.3 Background................................................................................................................................9
1.4 Instalação...................................................................................................................................9
Capítulo - 2 Conceitos de sistemas de tempo real................................................................................9
2.1 Divide et impera.........................................................................................................................9
2.2 Classificação............................................................................................................................10
2.3 Jitter.........................................................................................................................................12
2.4 O que é um RTOS....................................................................................................................12
2.5 O que não é um RTOS.............................................................................................................12
Capítulo - 3 RTOSes embarcados.......................................................................................................12
3.1 Prioridades e Programação......................................................................................................13
3.1.1 Prioridades estáticas e modificáveis.................................................................................13
3.2 Interrupções e Tarefas..............................................................................................................14
3.3 Interrupções.............................................................................................................................14
3.4 Tarefas e Threads.....................................................................................................................14
3.4.1 Task States........................................................................................................................15
3.5 Tipos de tarefas........................................................................................................................15
3.5.1 Tarefas periódicas.............................................................................................................16
3.5.2 Tarefas não periódicas......................................................................................................16
3.5.3 Tarefas contínuas..............................................................................................................16
3.6 Sincronização...........................................................................................................................16
3.6.1 Operações Atômicas.........................................................................................................17
3.7 Zonas críticas...........................................................................................................................17
3.7.1 Uso Adequado de zonas críticas.......................................................................................18
3.7.1.1 Vantagens..................................................................................................................18
3.7.1.2 Restrições potenciais................................................................................................18
3.7.1.3 Uso recomendado.....................................................................................................19
3.8 Exclusão mútua........................................................................................................................19
3.9 Inversão de prioridade.............................................................................................................19
3.9.1 Soluções possíveis............................................................................................................20
3.9.2 Teto prioridade.................................................................................................................20
3.9.3 Herança prioridade...........................................................................................................21
Capítulo - 4 Arquitetura Geral do ChibiOS........................................................................................21
4.1 Requisitos do sistema...............................................................................................................22
4.2 Modelo de Aplicação...............................................................................................................22
4.3 A Imagem no geral...................................................................................................................22
4.3.1 Código de inicialização....................................................................................................23
4.3.2 Aplicação..........................................................................................................................23
4.3.3 ChibiOS/RT......................................................................................................................23
4.3.4 ChibiOS/HAL..................................................................................................................24
4.3.5 Considerações..................................................................................................................24
4.4 Detalhes da Abstração..............................................................................................................24
Capítulo - 5 Introdução ao Kernel RT................................................................................................25
5.1 Convenções de Codificação.....................................................................................................26
5.1.1 Estilo do Código...............................................................................................................26
Página - 2 de 109
5.1.2 Convenções de Nomenclatura..........................................................................................26
5.1.2.1 Funções da API.........................................................................................................27
5.1.2.2 Funções internas.......................................................................................................27
5.1.2.3 Variáveis, Campos e Estruturas................................................................................27
5.1.2.4 Tipos.........................................................................................................................28
5.1.2.5 Macros......................................................................................................................28
5.2 Arquitetura...............................................................................................................................28
5.3 Estados do Sistema..................................................................................................................29
5.4 Classes da API.........................................................................................................................30
5.4.1 Funções normais..............................................................................................................30
5.4.2 Funções S-Class...............................................................................................................30
5.4.3 Funções I-Class................................................................................................................31
5.4.4 Funções X-Class..............................................................................................................31
5.4.5 Funções especiais.............................................................................................................31
5.4.6 Inicializadores de objeto..................................................................................................31
5.5 Thread Working Areas.............................................................................................................31
5.5.1 Declarando Áreas de Trabalho.........................................................................................32
5.6 Thread States............................................................................................................................33
5.7 Funções Thread........................................................................................................................33
5.7.1 Declarando funções Thread..............................................................................................34
Capítulo - 6 Camada Sistema RT.......................................................................................................34
6.1 Inicialização.............................................................................................................................35
6.1.1 API...................................................................................................................................35
6.1.2 Exemplos..........................................................................................................................35
6.1.2.1 Kernel de Inicialização.............................................................................................35
6.2 Finalização anormal.................................................................................................................35
6.2.1 API...................................................................................................................................35
6.2.2 Exemplos..........................................................................................................................35
6.2.2.1 Parada de pânico.......................................................................................................35
6.3 Tratamneto de Interrupções.....................................................................................................36
6.3.1 API...................................................................................................................................36
6.3.2 Exemplos..........................................................................................................................36
6.3.2.1 Escrevendo um ISR OS............................................................................................36
6.3.2.2 Escrever um ISR Rápido..........................................................................................36
6.4 Seções críticas..........................................................................................................................37
6.4.1 API...................................................................................................................................37
6.4.2 Exemplos..........................................................................................................................37
6.4.2.1 Seções críticas em Threads.......................................................................................37
6.4.2.2 Seções críticas em ISRs............................................................................................38
6.4.2.3 Seções críticas Reentrante........................................................................................38
6.5 Gerenciamento de energia........................................................................................................38
6.5.1 API...................................................................................................................................39
6.5.2 Exemplos..........................................................................................................................39
6.6 Real time Contador..................................................................................................................39
6.6.1 API...................................................................................................................................39
6.6.2 Exemplos..........................................................................................................................39
6.6.2.1 Pequeno atraso..........................................................................................................39
6.6.2.2 Loop com Timeout...................................................................................................40
Capítulo - 7 RT Timers virtuais..........................................................................................................40
7.1 Configurações globais..............................................................................................................41
Página - 3 de 109
7.2 Time System............................................................................................................................41
7.2.1 API...................................................................................................................................41
7.2.2 Exemplos..........................................................................................................................41
7.2.2.1 Reading Time System...............................................................................................41
7.2.2.2 Loop com Tempo limitado.......................................................................................41
7.3 Utilitários de conversão de tempo...........................................................................................42
7.3.1 API...................................................................................................................................42
7.3.2 Exemplos..........................................................................................................................42
7.3.2.1 Hora do sistema em segundos..................................................................................42
7.3.2.2 Timeout em milissegundos.......................................................................................42
7.4 Timers One Shot......................................................................................................................42
7.4.1 Callbacks “Retornos de chamada”...................................................................................43
7.4.2 API...................................................................................................................................43
7.4.3 Exemplos..........................................................................................................................43
7.4.3.1 LED pisca-pisca Monoestável.................................................................................43
7.4.3.2 LED pisca-pisca Contínuo.......................................................................................44
7.5 Modo Tickless..........................................................................................................................45
Capítulo - 8 RT Scheduler..................................................................................................................46
8.1 Configurações globais..............................................................................................................46
8.2 O escalonador..........................................................................................................................46
8.2.1 Características..................................................................................................................46
8.3 O Sistema de Classes...............................................................................................................47
8.4 A Ready List.............................................................................................................................47
8.5 O Thread Idle...........................................................................................................................48
Capítulo - 9 RT Threading..................................................................................................................48
9.1 Declaração...............................................................................................................................49
9.1.1 API...................................................................................................................................49
9.1.2 Exemplos..........................................................................................................................49
9.1.2.1 Declaração do Thread estático..................................................................................49
9.2 Ciclo da vida............................................................................................................................49
9.2.1 API...................................................................................................................................49
9.2.2 Exemplos..........................................................................................................................50
9.2.2.1 Gerando e Ressincronização....................................................................................50
9.3 Delays......................................................................................................................................51
9.3.1 API...................................................................................................................................51
9.3.2 Exemplos..........................................................................................................................51
9.3.2.1 Intervalos Fixos # 1..................................................................................................51
9.3.2.2 Intervalos Fixos #2...................................................................................................52
9.3.2.3 Intervalos Fixos # 3..................................................................................................52
9.4 Threads Referências.................................................................................................................53
9.4.1 API...................................................................................................................................53
9.4.3 Exemplos..........................................................................................................................53
9.4.3.1 Thread Servindo um IRQ.........................................................................................53
9.5 Filas de Threads.......................................................................................................................55
9.5.1 API...................................................................................................................................55
9.5.2 Exemplos..........................................................................................................................55
9.5.2.1 Processamento de Frames........................................................................................55
9.6 Thread Time.............................................................................................................................57
9.6.1 API...................................................................................................................................57
9.6.2 Exemplos..........................................................................................................................57
Página - 4 de 109
9.6.2.1 Pulso CPU................................................................................................................57
9.7 Gestão prioridade.....................................................................................................................58
9.7.1 API...................................................................................................................................58
9.7.2 Exemplos..........................................................................................................................58
9.7.2.1 Priority Ceiling “Teto de prioridade”......................................................................58
9.8 Round Robin............................................................................................................................58
9.8.1 API...................................................................................................................................59
9.8.2 Exemplos..........................................................................................................................59
9.8.2.1 Round Robin Cooperativo........................................................................................59
Capítulo - 10 RT Semáforos...............................................................................................................60
10.1 Configurações globais............................................................................................................60
10.2 Semáforos de contagem.........................................................................................................60
10.2.1 Extensões.......................................................................................................................61
10.2.2 API.................................................................................................................................61
10.2.3 Exemplos........................................................................................................................62
10.2.3.1 Alocador de recursos..............................................................................................62
10.3 Semáforos binários................................................................................................................63
10.3.1 Extensões.......................................................................................................................64
10.3.2 API.................................................................................................................................64
10.3.3 Exemplos........................................................................................................................65
10.3.3.1 Thread Servindo um IRQ.......................................................................................65
Capítulo - 11 RT Mutexes e variáveis de condição............................................................................66
11.1 Configurações globais............................................................................................................66
11.2 Mutexes..................................................................................................................................67
11.2.1 API..................................................................................................................................68
11.2.2 Exemplos........................................................................................................................68
11.2.2.1 Exclusão mútua......................................................................................................68
11.2.3 Notas...............................................................................................................................69
11.3 Variáveis de condição e Monitores........................................................................................70
11.3.1 Monitores.......................................................................................................................70
11.3.1.1 Modelo de código...................................................................................................71
11.3.2 API..................................................................................................................................72
11.3.4 Exemplos........................................................................................................................72
11.3.4.1 Produtores e consumidores.....................................................................................72
11.3.4.2 Produtor de ISRs.....................................................................................................74
Capítulo - 12 RT Síncronos de Mensagens........................................................................................75
12.1 Configurações globais............................................................................................................75
12.2 Descrição...............................................................................................................................76
12.2.1 Mensagens......................................................................................................................76
12.2.2 API.................................................................................................................................77
12.3 Passe mensagem....................................................................................................................77
12.3.1 Exemplos........................................................................................................................78
12.3.1.1 Thread servidor.......................................................................................................78
12.3.1.2 Console do Servidor...............................................................................................78
Capítulo - 13 RT Mailboxes...............................................................................................................79
13.1 Configurações globais............................................................................................................79
13.2 Descrição...............................................................................................................................79
13.2.1 Mensagens......................................................................................................................80
13.2.2 Buffer.............................................................................................................................80
13.2.3 API.................................................................................................................................81
Página - 5 de 109
13.2.4 Exemplos........................................................................................................................81
13.2.4.1 Mensagens grandes.................................................................................................81
Capítulo - 14 Eventos.........................................................................................................................84
14.1 Configurações globais............................................................................................................84
14.2 Descrição...............................................................................................................................84
14.2.1 Fontes de Eventos..........................................................................................................84
14.2.2 Bandeiras de Eventos.....................................................................................................84
14.2.3 Ouvintes de eventos.......................................................................................................85
14.2.4 Máscaras de eventos.......................................................................................................85
14.2.5 Operações.......................................................................................................................85
14.2.5.1 Registrando.............................................................................................................86
14.2.5.2 Esperando...............................................................................................................86
14.2.5.3 Broadcasting...........................................................................................................86
14.2.6 Eventos simplificados....................................................................................................87
14.2.6.1 Signaling.................................................................................................................87
14.2.7 API.................................................................................................................................87
14.2.8 Exemplos........................................................................................................................88
14.2.8.1 Múltiplos Eventos...................................................................................................88
14.2.8.2 Sinalização direta...................................................................................................89
Capítulo - 15 I/O Queues....................................................................................................................91
15.1 Configurações globais............................................................................................................91
15.2 Descrição...............................................................................................................................91
15.2.1 Filas genéricas I/O.........................................................................................................91
15.2.1.1 Obtendo tamanho da fila........................................................................................92
15.2.1.2 Resetando de fila....................................................................................................92
15.2.2 Filas de entrada..............................................................................................................92
15.2.2.1 Obtendo Espaço......................................................................................................93
15.2.2.2 Colocando Dados...................................................................................................93
15.2.2.3 Obtendo dados........................................................................................................93
15.2.2.4 Leitura de Dados....................................................................................................93
15.2.3 As filas de saída..............................................................................................................93
15.2.3.1 Obtendo Espaço......................................................................................................93
15.2.3.2 Obtendo dados........................................................................................................93
15.2.3.3 Colocando Dados...................................................................................................94
15.2.3.4 Escrevendo Dados..................................................................................................94
15.2.4 API.................................................................................................................................94
15.2.5 Exemplos........................................................................................................................94
15.2.5.1 Driver UART com buffer.......................................................................................94
Capítulo - 16 RT Streams...................................................................................................................96
16.1 Descrição...............................................................................................................................96
16.1.1 API.................................................................................................................................96
16.1.2 Exemplos........................................................................................................................97
16.1.2.1 Estendendo Streams...............................................................................................97
16.1.2.2 Implementando Streams.........................................................................................98
Capítulo - 17 Gerenciamento de memória RT..................................................................................101
17.1 Configurações globais..........................................................................................................101
17.2 Núcleo Alocador de Memória..............................................................................................101
17.2.1 API...............................................................................................................................102
17.3 Alocador de pilha.................................................................................................................102
17.3.1 API...............................................................................................................................102
Página - 6 de 109
17.4 Alocador de memória Pool..................................................................................................102
17.4.1 API...............................................................................................................................102
17.5 Alocadores de comparação..................................................................................................102
17.6 Dynamic Threading.............................................................................................................102
Capítulo - 18 RT Debug....................................................................................................................103
18.1 Verificações em tempo compilação.....................................................................................103
18.2 Verificações em tempo de execução....................................................................................103
18.2.1 Kernel Estatísticas........................................................................................................103
18.2.2 Estado do Sistema........................................................................................................104
18.2.3 Parâmetros de Funções.................................................................................................105
18.2.4 System Assertions........................................................................................................105
18.2.5 Trace Buffer.................................................................................................................105
18.2.6 Stack Overflow............................................................................................................105
18.2.7 Working Area Filling....................................................................................................106
18.2.8 Threads Profiling..........................................................................................................106
Capítulo - 19 Siglas – Acrónimos.....................................................................................................106
Página - 7 de 109
Capítulo - 1 Introdução
Uma pergunta frequente que tive de responder é: Por que criar um outro RTOS, a minha
resposta habitual é? Por que não? Não que um RTOS perfeito já não tenha sido escrito.
1.2 História
O projeto ChibiOS / RT tornou-se público em setembro de 2007 no SourceForge, mas as
suas raízes vão muito para trás no tempo. Meu interesse em sistemas operacionais originou-se
quando eu comprei o excelente livro “Operating System Design: The Xinu Approach” por Douglas
Comer, o livro mais inspirador para mim, moldou o futuro na minha trajetória profissional.
A partir do código do livro, comecei a escrever em 1989 um sistema operacional inspirado
em Unix e em execução no meu antigo Atari ST, depois de alguns anos o sistema operacional,
chamado BDP, estava completo o suficiente para ser autossustentável, executando o EMACS, GCC
e a maioria dos utilitários de estilo Unix. Quando o Linux começou a se tornar popular, decidi que o
projeto era redundante e parei de trabalhar nele.
O kernel do BDP foi interessante para a época, era totalmente preemptivo e com suporte em
tempo real “co-rotinas” que eu aprendi a ser chamado corretamente de “threads” depois de alguns
anos. Em 1992, eu precisava de um pequeno núcleo multitarefa para aplicações embarcadas, eu
decidi não usar diretamente o código BDP, mas em escrever algo mínimo a partir do zero, o
resultado foi “MK”, provavelmente um dos primeiros RTOSes embarcado, no momento não havia
Internet e o projeto teve pouco uso e eu o esqueci por cerca de 15 anos. Ele ficou na minha mente
como “algo bom” que eu escrevi anos atrás.
Em 2006, eu precisava de um RTOS para um projeto e, em vez de usar uma das opções
disponíveis, eu decidi dar uma olhada naquele “algo bom”, que era ainda melhor do que me
lembrava, então eu comecei imediatamente a melhorar o estilo do código obsoleto, escrevendo
documentação, adicionando extensões e assim por diante. Depois de um tempo eu decidi torná-lo
Página - 8 de 109
open source e dei um nome bobo, em 2007 MK renasceu como ChibiOS/RT
1.3 Background
A razão para ressuscitar o já mencionado MK foi a minha insatisfação com RTOSes
existentes, pelo menos aqueles que eu conhecia. Meu RTOS ideal tinha que ser:
• Elegante
• Rápido
• Pequeno
• Estático
Os que examinei, falharam em um ou mais dos pontos acima, pontos que depois se tornam
as exigências fundamentais do ChibiOS/RT:
• Foco para código com elegância e consistência, deve ser um prazer trabalhar com o
código.
• Totalmente, de forma inequívoca estática.
• Caminhos de código curtos para todas as operações, ele tem que ser muito rápido.
• Compacto.
• Funcionalidade completa.
• Forte abstração.
Basicamente, eu queria que fosse um concorrente real, não um outro “me too” sou RTOS.
1.4 Instalação
A maneira mais fácil de começar com ChibiOS é usar o conjunto de ferramentas pré-
configurada chamado ChibiStudio. Ele inclui todo o software necessário e o próprio ChibiOS.
ChibiStudio não é obrigatório, muitos outros toolchains também são utilizáveis.
Página - 9 de 109
Nós não entraremos neste momento em detalhes sobre a natureza dessas entidades, nem o
que um evento ou uma reação pode ser, uma abordagem abstrata é preferível neste ponto, além
disso, estamos supondo que pode haver um único evento possível e uma única reação possível, isso
não é necessariamente o caso.
Sistemas complexos sempre podem ser decompostos em um conjunto de processos
elementares conectados em uma rede, o sistema tem um conjunto de sinais de entrada e saída, ainda
podemos considerá-los eventos e reações, mas em um nível de sistema. Um sistema também pode
ter um estado global, informação que opcionalmente pode ser acessado por vários processos no
sistema.
Observe que em um sistema, existem vários caminhos que levam de um evento para uma
reação, vamos nomear os caminhos dessas atividades.
Um pequeno resumo:
• Process. Uma entidade elementar que, em resposta a um evento produz uma reação.
• Activity. Um conjunto de processos dependentes interconectados.
• System. Um conjunto de atividades dependentes ou independentes.
• Event. Um evento desencadeia uma reação de um processo.
• Reaction. A reação programada para um evento.
• Response Time. O tempo entre um evento e a reação programada.
2.2 Classificação
Processos, atividades e, por extensão, os sistemas podem ser classificados em uma das
seguintes categorias.
Página - 10 de 109
• Non Real Time. Um sistema de tempo não real é um sistema onde não há prazos
envolvidos. Sistemas em tempo não real pode ser descrito como:
“Um sistema de tempo não real é um sistema onde a reação programada para um evento, com
certeza, vai acontecer em algum momento no futuro”.
• Soft Real Time. Um sistema Soft Real Time (SRT) é um sistema onde não cumprir um
prazo pode ter efeitos indesejáveis, mas não catastróficas, uma degradação de desempenho,
por exemplo. Tais sistemas podem ser descritos como:
“Um sistema de soft real time é um sistema onde a reação programada para um evento é quase
sempre concluída dentro de um tempo finito conhecido”.
• Hard Real Time. Um sistema de Hard Real Time (HRT) é um sistema onde não cumprir
um prazo pode ter efeitos catastróficos. Sistemas hard real time requerem uma definição
mais restrita e poderiam ser descritos como:
“Um sistema hard real time é um sistema onde a reação programada para um evento é garantido
que seja concluída dentro de um tempo finito conhecido”.
Note que num sistema, todos os tipos de processos podem estar presentes ao mesmo tempo,
cada um com uma classificação potencialmente diferente. A classificação de uma atividade em um
sistema deve ser considerado igual à classificação da qualificação do pior processo a influenciá-lo.
Se um sistema inclui atividades com classificação diferente, então é um sistema misto, este é
um caso comum. Um exemplo:
Neste sistema, o processo 2 é um não-realtime, o seu tempo de resposta não pode ser
calculado. Devido a isso o caminho interno I2 → O1 deve ser considerado não-realtime também.
Note que também processo 1 poderia ser afetado pelo tempo de resposta não-determinista do
processo 2 porque há algum compartilhamento de dados global. Se a exclusão mútua for usada,
então, processo 1 e 2 afetam uns aos outros para a duração do tempo de pior caso, passado-se na
zona de exclusão mútua.
Página - 11 de 109
2.3 Jitter
Processos nunca reagem em um tempo constante, em uma escala de tempo pequeno
suficientemente qualquer processo físico é obrigado a ter um jitter.
Ao avaliar o tempo de resposta de um sistema o único jitter de cada processo interno deve
ser contabilizado e, note, o único valor significativo é o jitter no pior caso. Jitter ilimitado ou não
calculado não é compatível com um sistema hard realtime.
Página - 12 de 109
um tipo especializado de RTOSes. Muitos RTOSes , incluindo ChibiOS/RT compartilham um
conjunto de características comuns:
• Modelo Multi Threaded simples aplicação.
• Prioridades de agendamento fixa.
• Tratamento de ISR como parte da API.
Vamos nos concentrar sobre o tipo embarcado a partir de agora em diante.
O nível de prioridade mais baixo é geralmente reservado para uma tarefa comumente
chamado de “idle task”. É a tarefa executada quando todas as outras tarefas ou ISRs não estão
prontos para execução. A regra de programação é muito simples: em qualquer instante, a tarefa a
ser executada é a tarefa pronta com o mais alto nível de prioridade.
Isto é verdade para ambas as tarefas e ISRs no modelo proposto.
Página - 13 de 109
3.2 Interrupções e Tarefas
Os dois tipos de processos que podemos encontrar nos RTOSes embarcado (lembra-se do
capítulo anterior?) são chamados ISRs e tarefas. Fundamentalmente ISRs e tarefas podem ser
considerados semelhantes, com a diferença que um ISR não pode esperar internamente, ele só pode
ser executado e, em seguida, termina. Geralmente, existem diferenças na API, algumas funções
podem ser chamadas de nível de tarefa apenas e/ou vice-versa.
3.3 Interrupções
Em aplicações embarcadas interrupções são a fonte primária de eventos. Interrupções
acionam diretamente ISRs que por sua vez podem despertar tarefas.
Muitos RTOSes dividem as interrupções em duas categorias (os nomes podem mudar):
Classes de interrupção
Esta classe de interrupção é processada de forma muito eficiente, mas
Interrupções rápidas
não pode chamar diretamente qualquer função OS.
Pode usar serviços do sistema operacional, mas pode haver alguma
OS Interrupções
sobrecarga relacionada com OS no código ISR.
Dependendo da arquitetura HW e da capacidade do OS, interrupções podem ser processadas de
forma diferente.
Tratamento de Interrupção
Tratamento mais simples, todas as fontes interrupções têm a mesma
Sem Prioridade
prioridade, sem preempção.
Prioridades múltiplas, mas ISRs não podem tomar o lugar “preempt” um
Múltiplas Prioridades
sobre o outro.
Prioridades múltiplas e ISRs podem tomar o lugar “preempt” de ISRs
Interrupções aninhadas
com prioridade mais baixa.
Página - 14 de 109
Alguns RTOSes, como ChibiOS por exemplo, utiliza o termo threads para as suas tarefas. A
distinção é sutil, o termo threads é usado com mais frequência em sistemas mais complexos, por
exemplo Posix Win32 dispõe de funcionalidades threading.
Pessoalmente eu prefiro usar o termo threads quando o sistema é capaz de suportar as seguintes
características mínimas:
• Create. Threads pode ser iniciado em tempo de execução não apenas declarada ou
configurada estaticamente. Note, isto não implica alocação dinâmica, um thread pode
ser alocada estaticamente e iniciado/terminado em tempo de execução.
• Exit. Threads são capazes de finalizar retornando um valor de saída, bem como o
valor retornado por uma função C.
• Join. Threads são capazes de gerar outros Threads e, em seguida, aguardar o
resultado de sua execução.
Thread pode ser visto como funções que podem ser executadas em paralelo com a função
geradora, a função geradora em seguida, é capaz de ressincronizar com o thread gerado e recuperar
o valor do resultado. Se um sistema não suporta todas as características acima, então é mais
apropriado o uso do termo task.
Diferentes implementações RTOS podem ter diferentes estados e máquinas de estado, mas o
que está acima é o mais comum.
Página - 15 de 109
tarefas são acionados (o evento as acorda).
3.6 Sincronização
Normalmente, as tarefas podem usar áreas de memória como dados compartilhados,
normalmente também chamados de recursos compartilhados, o acesso simultâneo aos recursos
pode levar à corrupção de dados, porque as operações executadas por tarefas podem não ser
atômicas.
Por exemplo, vamos supor que há duas tarefas e uma variável compartilhada inicializada a 10. Uma
tarefa incrementa a variável compartilhada executando as seguintes pseudo-instruções:
1. WAIT event
2. LOAD r0,var
3. INC r0
4. STORE r0,var
5. JUMP 1
E outra tarefa em vez disso, realiza um decremento da mesma variável:
1. WAIT event
2. LOAD R1,var
3. DEC r1
4. STORE R1,var
5. JUMP 1
Página - 16 de 109
Se os eventos forem acionados quase ao mesmo tempo, as duas sequências poderiam
ocorrerem entrelaçadamente e chegar a um resultado incorreto, por exemplo:
1. LOAD r0,var
2. INC r0
3. <preemption by the second task>
4. LOAD r1,var
5. DEC r1
6. STORE r1,var
7. <returns to the first task>
8. STORE r0,var
O resultado normal ainda deve ser 10, mas como a sequência de instruções foram
entrelaçadas por preempção, o resultado final é 11. Observe que você teria o mesmo problema, se
em vez de tarefas as entidades envolvidas fossem ISRs em sistemas onde os ISRs podem prevalecer
um sobre o outro.
Manipulação de dados compartilhado requer um acesso sincronizado ao recurso, a solução
usual é chamado Exclusão Mútua e há várias maneiras de conseguir isso, vamos examinar vários
mecanismos possíveis quando as primitivas do kernel ChibiOS for explicado.
Página - 17 de 109
simples zonas críticas muitas vezes são implementadas em desabilitar/habilitar interrupções, isso
faz com que o código na zona crítica não possa ser tomado por um IRQ ou outra tarefa.
Claro que as coisas nem sempre são tão simples, zonas críticas não devem ser consideradas
simplesmente uma abstração em torno do ato de desabilitar/habilitar interrupções. Há algumas
coisas a serem consideradas:
• Globalmente, Desabilitar interrupções é geralmente ruim, ela aumenta o jitter no
sistema e potencialmente pode tornar o pior caso no tempo de resposta ainda pior.
Em uma arquitetura pode ser preferível a máscara de interrupções só até um certo
nível de prioridade em vez de mascará-las globalmente.
• Desabilitar interrupções não funciona se as tarefas estão sendo executados por vários
núcleos físicos ou threads de hardware. Nestes casos, a implementação zona crítica
também precisa usar algum tipo de HW semáforo, a fim de coordenar os vários
núcleos/threads.
• A ação de entrar numa zona crítica poderia ativar verificações de nível de sistema
operacional ou ser utilizada para fins de estatísticas.
• Otimizações do compilador podem mover instruções geradas em torno de funções ou
código inline. Isto significa que algum código poderia ser potencialmente colocado
ou retirado de uma zona crítica. O resultado seria que o código visto em uma zona
crítica de alto nível não seria realmente atômico. Um RTOS bem desenhado
implementa barreiras para o compilador dentro da API zona crítica. Uma abordagem
simplista pode levar a resultados desastrosos.
A maioria dos RTOSes têm uma API específica para tratamento de zonas críticas, a
abordagem correta é usar a API fornecida pelo RTOS para não se fazer suposições sobre como
zonas críticas são ou devem ser implementadas. Um bom RTOS deve ter cuidado sobre a
implementação do que é melhor em determinada arquitetura.
3.7.1.1 Vantagens
• Muito leve, o mínimo de sobrecarga em tempo de execução.
• Pode implementar exclusão mútua entre as tarefas e ISRs.
Página - 18 de 109
3.7.1.3 Uso recomendado
• Exclusão mútua em paths de código muito curto com tempo de execução limitado, e
de preferência, sem nó.
Legend:
0..3 - Priority levels
*** - Mutex taken
+++ - Running
--- - Ready
... - Waiting
xL - Lock operation on mutex 'x'
xU - Unlock operation on mutex 'x'
S,G - Start, Goal
Página - 19 de 109
4. A tarefa de alta prioridade (2) começa em execução e tenta levar o recurso
compartilhado A que já está ocupado por (0), a tarefa (2) inicia a espera do recurso A
ser liberado.
5. Tarefa (1) retoma a execução atrasando as tarefas (0) e (2). Tarefa (1) detém a tarefa
(0) porque sua maior é prioridade, tarefa (0) detém a tarefa (2) porque mantém o
recurso bloqueado e é incapaz de liberá-lo.
O resultado final é que tarefa (1) mantém indefinidamente a maior prioridade e a não
relacionada tarefa (2) por causa de um conflito sobre o acesso dos recursos A. A ordem de
prioridades não é respeitada, o objetivo das quatro tarefas não é alcançado em ordem de prioridade.
Legend:
0..3 - Priority levels
4 - Priority associated to Mutex A
*** - Mutex taken
+++ - Running
--- - Ready
... - Waiting or Terminated
xLn - Lock operation on mutex 'x' with priority boost to level 'n'
xUn - Unlock operation on mutex 'x' with priority returning to level 'n'
S,G - Start, Goal
^ - Priority transition (boost or return).
Você pode ver que a inversão de prioridades não ocorrer mais, todas as tarefas alcançam seu
Página - 20 de 109
objetivo em sua ordem de prioridade.
O algoritmo de teto Prioridade resolve a inversão de prioridades, mas de uma forma não
ideal porque afeta todas as tarefas com prioridade abaixo do teto, veja como a tarefa com prioridade
3 é adiada. Teto de prioridade é melhor usado em sistemas simples, é a solução adotada em RTOSes
conhecidos como aqueles implementando a especificação OSEK.
Legend:
0..3 - Priority levels
*** - Mutex locked
+++ - Running
--- - Ready
... - Waiting or Terminated
xLn - Lock operation on mutex 'x' with priority boost to level 'n'
xUn - Unlock operation on mutex 'x' with priority returning to level 'n'
S,G - Start, Goal
^ - Priority transition (boost or return).
Novamente a inversão de prioridades que não mais ocorrer, todas as tarefas alcançam seu
objetivo na sua ordem de prioridade.
O algoritmo de herança de prioridade resolve a prioridade de uma forma mais geral, ele
afeta apenas as tarefas que tentam acessar o mesmo mutex. Esta é a solução implementada na
RTOSes mais complexos, incluindo ChibiOS/RT.
Página - 21 de 109
periféricos mais comuns.
Página - 22 de 109
Os vários elementos serão descritos em maiores detalhes nos próximos capítulos, esta é uma
breve descrição:
4.3.2 Aplicação
É o código do usuário, ChibiOS fornece um simples modelo da função main(), o restante começa
a partir daí.
4.3.3 ChibiOS/RT
Este é o kernel escalonador RT que é dividido em duas camadas internas:
• RT Portable Kernel. É a parte do kernel RTOS que é independente da arquitetura e
Página - 23 de 109
compilador. O código RT está localizado sob ./os/hal/ports.
• RT Port Layer. É a parte do kernel RTOS específico para uma arquitetura e um ou
mais compiladores. O código do port RT está localizado sob /os/rt/ports.
4.3.4 ChibiOS/HAL
HAL é a sigla para Hardware Abstraction Layer, um conjunto de drivers de dispositivo para
os periféricos mais comumente encontrados em microcontroladores. O HAL é dividida em várias
camadas:
• HAL API Layer. Esta camada contém uma série de controladores de dispositivos
portáteis. O código portátil HAL está localizado sob /os/hal.
• HAL Port Layer.. Esta é a implementação do driver de dispositivo para um
microcontrolador específico ou uma família de microcontroladores. O código HAL
port está localizado sob /os/hal/ports.
• HAL Board Layer. Este módulo contém todos os detalhes da construção de uma
placa específica do microcontrolador. A inicialização em nível de placa é realizada
neste módulo. O código HAL boards está localizado sob /os/hal/boards.
• HAL OSAL Layer. Esta é a camada de abstração do sistema operacional. O HAL
tem que usar alguns serviços RTOS, a fim de implementar a sua funcionalidade. O
acesso aos serviços do RTOS é feito através desta camada de abstração para não
bloquear o HAL para um específico RTOS. O código HAL OSAL está localizado em
/os/hal/osal.
4.3.5 Considerações
Observe que na arquitetura acima fica evidente que o RT não precisa da HAL e pode ser
utilizado sozinho se a HAL não for necessária. Por outro lado, nesta arquitetura HAL usa os
serviços da RT através da OSAL mais poderia usar outro RTOS ou até mesmo trabalhar sem RTOS
através da implementação de um Osal sobre a máquina bare metal.
Os exemplos neste livro iram cobrir tanto RT e HAL juntos e sozinhos.
Página - 24 de 109
• ISRs são diferentes de arquitetura para arquitetura.
• Arquitetura de interrupções é muitas vezes diferente.
• Interfaces periféricas são diferentes.
• Bibliotecas fornecida pelo fornecedor são incompatíveis, mesmo permanecendo com
o mesmo fornecedor.
Em geral, há falta de soluções completas, os desenvolvedores são responsáveis pela
integração de pedaços de código de fontes diferentes em um sistema de trabalho, muitas vezes o
maior esforço está em resolver problemas de integração.
ChibiOS oferece uma solução end-to-end, os componentes fornecidos já estão bem
integrados entre si e a aplicação pode ser escrita ignorando a maioria dos detalhes HW usando a
API de alto nível já prevista.
No ChibiOS a API é constante, os detalhes específicos de HW existem, claro, mas são
encapsulados em arquivos de configuração plataforma-dependente. O aplicativo pode ser portado
desde que haja recursos equivalentes e serão criados novos arquivos de configuração, o resto do
código permanece o mesmo.
Entre as abstrações fornecidas pelo RT e HAL:
• Periféricos visto como “streams” ou “dispositivos de bloco” utilizando interfaces C+
+/Java-like (mas ainda escritos em C).
• API padrão para periféricos mais comuns como: ADC, CAN, DAC, GPIO, I2C,
Input Caputure, PWM, SPI, Timers, UART, USB, Ethernet e muitos outros.
• Não há necessidade de escrever manipuladores de interrupção, tudo é encapsulado
nos drivers, os drivers trazem a notificação de retornos de chamada para o aplicativo
se necessário. Otimiza a utilização dos DMAs, ou outras características que
melhoram o desempenho são realizadas nos drivers de forma transparente para o
usuário, se for caso.
• Se um ISR é necessária, até mesmo o código ISR é abstraído em construções de
macro e é independente da plataforma.
• A API do RTOS é exatamente o mesmo em todas as arquiteturas.
• Tratamento de Timeout a nível API para todos os drivers periféricos onde aplicável.
Página - 25 de 109
2. O código deve ser otimizado para o tamanho, a menos que este entre em conflito
com o ponto 1.
3. O kernel deve oferecer um conjunto completo de características RTOS, a menos que
isso entre em conflito com a exigência 1.
4. Ele deve ser intrinsecamente seguro. Primitivas com casos de ângulo perigosos
simplesmente não são permitidos. A regra é para orientar o usuário a escolher uma
abordagem segura, se possível.
5. A base de código deve ser elegante, consistente, legível e orientada por regras.
6. Isso pode ser subjetivo, mas trabalhar com o código deve ser uma experiência
agradável.
Página - 26 de 109
5.1.2.1 Funções da API
O nome de uma função que pretende ser uma API é sempre composta da seguinte forma:
ch<subsystem><verb>[<object>][Timeout][<I|S|X>]()
Onde:
• ch. Identifica um ChibiOS/RT API.
• < subsystem>. Identifica o subsistema do kernel onde pertence a esta função.
• <verb>. A ação executada por esta função.
• <object>. O objeto opcional da ação ou outras informações de contexto.
• Timeout. Se a função é capaz de impedir a execução do thread chamado e tem uma
capacidade de tempo limite.
• <I|S|X>. Atributos opcionais da função da classe. Esse atributo, se presente, define
estritamente o estado do sistema compatível com a função API. A seção “Classes API”
descreverá a relação entre classes de função e o estado do sistema.
Por exemplo:
chSemWaitTimeoutS(&sem, 1000);
Esta é uma função ChibiOS/RT "ch" pertencente ao subsistema de semáforos "Sem", ele
executa uma operação de espera com capacidade de Timeout. A classe da função é "S", requer que
a função seja chamada a partir do contexto thread dentro de uma zona crítica.
Você pode ver que há uma grande quantidade de informação codificada em um nome de
uma função aparentemente banal.
Um caso especial de funções da API são os inicializadores de objeto que têm sempre a
forma:
ch<subsystem>ObjectInit()
chSemObjectInit(&sem, 1)
Página - 27 de 109
separador.
5.1.2.4 Tipos
Tipo simples ou estruturada seguem a mesma convenção, o símbolo deve ser escrito todo em
letras minúsculas, underline é usado como separador e um "_t" é adicionado no final. Exemplos:
thread_t semaphore_t
5.1.2.5 Macros
Macros são escritos inteiramente em letras maiúsculas. Um prefixo "CH_" é encorajada mas
não imposto. Um prefixo comum para macros agrupados é obrigatória. Exemplos:
CH_IRQ_EPILOGUE(), THD_FUNCTION()
5.2 Arquitetura
O kernel ChibiOS/RT é dividido internamente em módulos bem definidos, muito cuidado foi
tomado para manter os subsistemas bem isolado, o que permite ativar ou desativar cada subsistema
através do arquivo de configuração. Desabilitar os subsistemas não utilizados permite grande
economia no espaço de código e/ou dados.
O kernel está estruturado da seguinte forma:
Página - 28 de 109
5.3 Estados do Sistema
Um conceito importante no ChibiOS/RT são os chamados, estados do sistema, o
comportamento global do sistema é regulado por uma máquina de estados de alto nível:
Os estados têm um forte significado e deve ser entendido de forma a utilizar o RTOS
corretamente:
• Init. Este estado representa a execução de código antes do RTOS ser inicializado
usando chSysInit(). Os únicos tipos de funções do SO que podem ser
chamados no estado "Init" são os inicializadores de objeto.
• Thread. Este estado representa o RTOS executando um dos seus threads. APIs
normais pode ser chamado neste estado.
• Suspended. Neste estado todos os OS-IRQs estão desativados, mas Fast-IRQs ainda
são servidos.
• Disabled. Neste estado de todas as fontes de interrupção são desativados.
• S-Locked. Este é o estado das zonas críticas no contexto thread.
• I-Locked. Este é o estado das zonas críticas no contexto ISR.
• IRQ WAIT. Quando o sistema não tiver nenhum thread pronto para execução ele
entra em um estado onde ele apenas aguarda interrupções. Este estado pode ser
mapeado em um estado de baixa energia da arquitetura do host.
• ISR. Este estado representa a execução de código do ISR.
Página - 29 de 109
As transições de estados são reguladas por eventos físicos como IRQs e chamada da APIs do
sistema. Os estados marcado como círculos duplos são os estados onde as APIs RTOS podem ser
invocadas.
Observe que a operação de mudança de contexto é sempre executada de forma síncrona
dentro de uma zona crítica, o protocolo de comutação é: entrar zona crítica, executar a mudança,
sair da zona de crítica. O sistema nunca fica parado dentro de uma zona crítica. Os estados do
sistema também podem ser visto como estados HW abstratos, algumas arquiteturas podem não
suportar alguns dos estados, por exemplo, “as interrupções rápidas” e as funcionalidades
relacionadas estão fora.
Existem alguns Estados adicionais não diretamente relacionados à atividade RTOS, mas
continuam a ser importante do ponto de vista do sistema:
• Fast ISR. Este estado representa a execução de código ISR de uma fonte de IRQ rápido.
• NMI. Este estado representa a execução de código ISR de uma fonte de interrupção não-
mascarável.
Invocar as APIs RTOS nunca é permitido nos estados acima.
Página - 30 de 109
5.4.3 Funções I-Class
Função com sufixo "I" pode ser chamada no estado “I-Locked” e no estado “S-Locked”.
Zonas críticas tanto ISR-level e thread-level são compatíveis com essa classe.
Note que esta classe de funções não reescalona internamente, se chamado do estado “S-
Locked” um reescalonamento deve ser realizado, por exemplo, chamando
chSchRescheduleS() antes de sair da zona crítica. Deixar de realizar um reescalonamento
necessário será pego por uma asserção então a condição é facilmente detectável durante o
desenvolvimento.
Página - 31 de 109
As várias regiões precisam de uma explicação:
• Thread Stack. É a área de pilha usada pelo thread, todos threads são executados em
pilhas privadas.
• port_extctx. É o ISR colocado na estrutura de pilha, é uma estrutura que depende do
port.
• PORT_INT_REQUIRED_STACK. É uma área de memória de tamanho fixo
exigido pelo serviço de interrupções. O tamanho desta área depende da arquitetura da
CPU e requisitos do IRQ.
• port_intctx. Este é o contexto da troca de contexto, é uma estrutura dependente do
port.
• thread_t. É a estrutura que contém todos os dados fixo do thread. É o tipo mais
importante de objeto manipulado pelo kernel. Muitas funções da API levam
ponteiros para thread_t como parâmetros, mas não deve haver nenhuma
suposição de que a estrutura é alocada no início da área de trabalho, isto é um detalhe
de implementação.
Note que port_extctx e PORT_INT_REQUIRED_STACK só são colocados na pilha
quando um IRQ está sendo atendido. A estrutura port_intctx é colocada quando o thread é
comutado para fora e não está sendo executado. Do ponto de vista da aplicação a estrutura interna
da área de trabalho não é relevante, as peças relacionadas com os ports não têm impacto
portabilidade.
A declaração acima aloca estaticamente uma área de trabalho com um espaço de pilha 128
bytes. Note que uma área de trabalho não pode ser declarado usando simplesmente uma matriz
porque a macro cuida de várias coisas:
• Considera o tamanho das diversas regiões a serem alocados na área de trabalho, o
Página - 32 de 109
tamanho descrito é o tamanho para apenas o espaço de pilha.
• Isso força os alinhamentos necessários e quaisquer outras restrições dependentes de
arquitetura.
Isso significa que o tamanho final do processo é maior do que os 128 bytes declarados, mas
é garantida a portabilidade da declaração e é invariável.
Note que no ChibiOS/RT existem vários estados de sono que são indicados no diagrama
como um único estado. Cada objeto de sincronização tem seus próprios estados de sono, isso é feito
para entender em que tipo de objeto, o thread está dormindo.
Página - 33 de 109
Cada variável cai necessariamente em um dos casos acima.
/* Função Thread.*/
static THD_FUNCTION(Counter, arg) {
static uint32_t cows = 0; /* variável compartilhada do Thread.*/
bool condition = true; /* variável privada do Thread.*/
while (condition) {
/* código do Thread.*/
cows++;
condition = (bool)(cows < 100);
/* Pequeno intervalo.*/
chThdSleepMilliseconds(100);
}
chThdExit((msg_t)cows);
}
O thread conta até 100 cows com intervalos de 100ms, em seguida retorna o número de
cows, o valor retornado, opcionalmente pode ser recuperado por outro thread.
Note que a variável cows é estática, se houver mais de um thread contando cows, logo, haverá um
problema de atomicidade porque a variável é compartilhada entre todos os threads.
Note que este thread recebe um parâmetro do tipo void* passado pelo gerador de thread e
retorna um tipo escalar msg_t que é garantido que seja grande o suficiente para conter um
ponteiro. O parâmetro do thread é privado da instância do thread, então, se vários threads estão
executando a mesma função, cada um poderia ter recebido um parâmetro diferente.
Página - 34 de 109
• Seções críticas.
• Gerenciamento de energia.
• Contador em tempo real.
6.1 Inicialização
Este serviço trata a inicialização do sistema:
6.1.1 API
Inicia o kernel RT. Esta função deve ser chamada uma vez da função
chSysInit()
main().
6.1.2 Exemplos
6.1.2.1 Kernel de Inicialização
O kernel RT é sempre inicializado como segue:
#include "ch.h"
void main(void) {
/*
* Inicializações do sistema.
* - Inicialização do kernel, a função main () torna-se um thread e do
* RTOS está ativo. Interrupções estão habilitados na saída chSysInit().
*/
chSysInit ();
.
.
.
}
6.2.1 API
CH_CFG_SYSTEM_HALT_HOOK(reason)
Macro hook para inserir uma ação personalizada
antes da interrupção do sistema.
chSysHalt(const char *reason)
Pára o sistema, o comportamento da função pode ser
substituído utilizando um hook.
6.2.2 Exemplos
6.2.2.1 Parada de pânico
Pânico é usado geralmente em resposta de uma condição inesperada irrecuperável.
Página - 35 de 109
#include "ch.h"
void main(void) {
.
.
.
chSysHalt ("condição inesperada # 12");
}
6.3.1 API
CH_IRQ_HANDLER () Declaração de um manipulador de interrupção normal.
CH_IRQ_PROLOGUE () Código inicial ISR.
CH_IRQ_EPILOGUE () Código final ISR.
CH_FAST_IRQ_HANDLER () Declaração de um não-OS(rápido) manipulador de interrupção.
chSysEnable () Habilita todas as interrupções.
chSysSuspend ()
Desativa as interrupções normais, interrupções rápidas são
mantidas ativados.
chSysDisable () Desabilita todas as interrupções.
6.3.2 Exemplos
6.3.2.1 Escrevendo um ISR OS
ISRs podem ser escritos de forma independente da arquitetura/compilador, myISR é o nome
da função de vetor de interrupção. Note que existe uma macro para a declaração ISR e duas macros
que marcam o início e o fim do ISR, todo o código específico do RTOS é escondido dentro das
macros.
CH_IRQ_HANDLER (myISR) {
CH_IRQ_PROLOGUE ();
/* Código ISR. */
...;
CH_IRQ_EPILOGUE ();
}
Página - 36 de 109
CH_FAST_IRQ_HANDLER (myISR) {
6.4.1 API
chSysLock() Entra em uma seção crítica à nível de thread.
chSysUnlock() Deixa uma seção crítica à nível de thread.
chSysLockFromISR() Entra em uma seção crítica à nível ISR.
chSysUnlockFromISR() Deixa uma seção crítica à nível ISR.
chSysUnconditionalLock()
Entra em uma seção crítica à nível de thread,
independentemente do estado anterior.
chSysUnconditionalUnlock()
Deixa uma seção crítica à nível de thread,
independentemente do estado anterior.
chSysGetStatusAndLockX()
Entra em uma seção crítica de qualquer contexto,
retornando ao estado anterior.
Restaura o estatus de uma seção crítica salvo por
chSysGetStatusAndLockX(), uma operação de
chSysRestoreStatusX()
reescalonamento é realizada, se for necessário e
aplicável.
6.4.2 Exemplos
6.4.2.1 Seções críticas em Threads
THD_FUNCTION (myThread, arg) {
/* Código Thread. */
...;
/* Código protegido. */
...;
Página - 37 de 109
6.4.2.2 Seções críticas em ISRs
CH_IRQ_HANDLER (myISR) {
CH_IRQ_PROLOGUE ();
/* Código ISR. */
...;
/* Código protegido. */
...;
CH_IRQ_EPILOGUE ();
}
/* Código protegido. */
...;
Página - 38 de 109
6.5.1 API
Este hook é chamado quando o sistema vai passar para
CH_CFG_IDLE_ENTER_HOOK() a thread idle. Ele pode ser usado para entrar num modo
de economia de energia.
Esse hook é chamado quando o sistema vai sair do
CH_CFG_IDLE_LEAVE_HOOK() thread idle. Ele pode ser utilizado para sair do modo de
economia de energia.
CH_CFG_IDLE_LOOP_HOOK() Hook chamado de dentro do loop do thread idle.
6.5.2 Exemplos
Exemplos não são fornecidos para os hooks acima porque qualquer aplicação seria
necessariamente dependente da arquitetura.
6.6.1 API
Se habilitado, esta macro indica que o atual port do kernel
suporta o recurso Contador Realtime. Ele pode ser usado
PORT_SUPPORTS_RT
para código condicional ou erros em tempo de
compilação.
S2RTC() Converte de segundos para relógios RT.
MS2RTC() Converte de milissegundos para relógios RT.
US2RTC() Converte de microssegundos para relógios RT.
RTC2S() Converte de relógios RT para segundos.
RTC2MS() Converte de relógios RT para milissegundos.
RTC2US() Converte de relógios RT para microssegundos.
Determina se o valor passado pelo contador está dentro de
chSysIsCounterWithinX()
um intervalo específico.
Insere um atraso exato dentro da execução do atual thread
chSysPolledDelayX()
ou ISR.
6.6.2 Exemplos
6.6.2.1 Um pequeno atraso
Esta é uma necessidade comum quando se trata de dispositivos de HW, que é muitas vezes
necessária para fazer alguma coisa depois de um pequeno período de tempo. Tais atrasos não podem
Página - 39 de 109
ser implementadas de forma confiável como loops de software. Uma coisa interessante é que a
função pode ser superada e a execução de ISRs/threads é contabilizada como parte dos atrasos, não
como um atraso extra-adicionado, melhorando a precisão.
void start_conversion(void) {
/* Ativa o periférico.*/
ADC->CR1 |= ADC_CR1_ACTIVE;
/* Inicia a conversão.*/
ADC->CR1 |= ADC_CR_START;
}
Página - 40 de 109
7.1 Configurações globais
Timers virtuais são afetados pelas seguintes configurações globais:
7.2.2 Exemplos
7.2.2.1 Reading Time System
/* Recebe o número de tiques desde a “época”: o instante de
inicialização do sistema. */
systime_t now = chVTGetSystemTime();
Página - 41 de 109
...;
if (condition)
break;
...;
}
7.3.2 Exemplos
7.3.2.1 Hora do sistema em segundos
/* Recebe o número de segundos desde a "época": o instante de a inicialização
do sistema. */
uint32_t now = ST2S(chVTGetSystemTime());
Página - 42 de 109
acionar um retorno de chamada depois que expirou o tempo programado. O timer máquina de
estado é:
7.4.2 API
chVTObjectInit() Inicializa um virtual timer object virtual_timer_t
chVTSet() Inicia ou reinicia um virtual timer.
chVTSetI() Inicia ou reinicia um virtual timer (variante I-Class).
chVTReset() Paralisa um virtual timer, se ativo.
chVTResetI() Paralisa um virtual timer, se ativo(variante I-Class).
chVTIsArmedI() Retorna true se o timer estiver armado.
Inicia um virtual timer, o timer não deve estar armado. Ligeiramente
chVTDoSetI()
mais rápido do que chVTSetI()
Paralisa um virtual timer, o temporizador tem de estar armado.
chVTDoResetI()
Ligeiramente mais rápido do que chVTResetI()
7.4.3 Exemplos
7.4.3.1 LED pisca-pisca Monoestável
Uma função pisca um LED uma vez por um período fixo, se for chamado repetidamente o
LED permanece aceso.
static virtual_timer_t led_vt;
/*
* Temporizador de chamada de retorno do LED.
*/
static void led_cb(void *arg) {
LED_off();
}
Página - 43 de 109
/*
* A função de pisca-pisca Monoestável.
*/
void blink(void) {
LED_on();
chVTSet(&led_vt, MS2ST(500), led_cb, NULL);
}
/*
* A função principal do aplicativo.
*/
void main(void) {
/*
* Inicializações do sistema.
* - Inicialização do kernel, a função main () torna-se um thread e o
* RTOS é ativado. Interrupções são habilitados na saída chSysInit().
*/
chSysInit ();
while (true) {
if (key_pressed())
blink();
chThdSleepMilliseconds(50);
}
}
/*
* LED timer callback.
*/
static void led_cb(void *arg) {
LED_toggle();
chSysLockFromISR();
chVTSetI(&led_vt, MS2ST(500), led_cb, NULL);
chSysUnlockFromISR();
}
/*
* A função principal do aplicativo.
*/
void main(void) {
/*
* Inicializações do sistema.
Página - 44 de 109
* - Inicialização do kernel, a função main () torna-se um thread e do
* RTOS está ativo. Interrupções estão habilitados em chSysInit () saída.
*/
chSysInit();
/* Inicia o pisca-pisca.*/
chVTSet(&led_vt, MS2ST(500), led_cb, NULL);
...;
/* Para o pisca-pisca.*/
chVTReset(&led_vt);
LED_off();
}
Página - 45 de 109
• Um novo port ChibiOS/ RT é mais complexa se o modo tickless tem de ser
implementado.
• Um timer especial deve estar presente em HW e dedicado ao modo tickless. Deve ser um
up-counter com um registo de comparação. Um contador de 16 bits é suficiente, no
entanto, um contador de 32 bits é recomendado.
• O comportamento do system time dentro das zonas críticas é ligeiramente diferente, no
modo tick o system time não é incrementado, no modo tickless o tempo é sempre
incrementado, porque o registro contador do timer é usado e isto não é afetado pela zona
crítica.
• Imagem do kernel um pouco maior.
O sistema pode usar ambos os modos sem impactos nas aplicações para que o modo possa
ser alterado sem problemas, a fim de fazer comparações, por exemplo.
Capítulo - 8 RT Scheduler
O ChibiOS/RT implementa uma estratégia de programação estritamente com base na
prioridade, o módulo responsável pelo agendamento dos threads é chamado scheduler
“escalonador”. Neste módulo também são definidas as estruturas de dados utilizadas globalmente
pelo RTOS.
8.2 O escalonador
O escalonador ChibiOS/RT é o módulo responsável pelo agendamento dos threads, que
também exporta uma API de baixo nível que é utilizado por outros módulos, a fim de implementar
primitivas de sincronização de qualquer tipo.
8.2.1 Características
• Muito rápido mudança de contexto síncrona.
• Passagem de mensagem intrínseca, a operação de mudança de contexto sempre carrega uma
mensagem do thread que está sendo trocado para o outro thread.
• Intimamente ligado com Timers virtuais, a fim de implementar um recurso de timeout para
todas as primitivas.
Página - 46 de 109
8.3 O Sistema de Classes
ChibiOS/RT é projetado para ser atualizável como um RTOS capaz de usar vários núcleos.
Devido a isso todas as estruturas de dados internas estão encapsuladas em uma única classe de
sistema. Em uma única implementação do núcleo existe um único objeto do sistema. Quando
MCUs multi-núcleo se tornarem comum múltiplas instâncias do sistema serão possíveis.
Diagrama de classes do sistema:
Página - 47 de 109
Note que o thread ativo não é parte da lista, que é apontado por um ponteiro global.
Capítulo - 9 RT Threading
O módulo de threading é responsável pelas operações relacionadas aos threads. Um conceito
importante é o current thread, algumas funções operam inerentemente ou o thread executa a função.
Os serviços do módulo threading são:
• Declaration.
• Life Cycle.
• Delays.
Página - 48 de 109
• Threads Suspension.
• Threads Queues.
• Thread Time.
• Priority Management.
• Round Robin.
Este capítulo só abordará o thread estático, a opção alocação dinâmica de thread será
descrito no capítulo “RT Gerenciamento de memória”.
9.1 Declaração
9.1.1 API
THD_WORKING_AREA() Aloca estaticamente uma área de trabalho para o thread.
Declara uma função thread escondendo eventuais palavras-
THD_FUNCTION()
chave específicas do compilador.
9.1.2 Exemplos
9.1.2.1 Declaração do Thread estático
/* Área de Trabalho MyThread. */
static THD_WORKING_AREA(waMyThread, 128);
/* Função MyThread. */
static THD_FUNCTION(MyThread, arg) {
9.2.1 API
chThdGetSelfX() Retorna um ponteiro para o current thread.
chThdCreateStatic() Cria e inicia um thread estático.
chThdCreateI() Cria um thread sem iniciá-lo.
Inicializa um thread criado anteriormente usando
chThdStart()
chThdCreateI()
Inicializa um thread criado anteriormente usando
chThdStartI()
chThdCreateI()
chThdExit() Finaliza o current thread retornando uma mensagem.
chThdExitS() Finaliza o current thread retornando uma mensagem.
chThdWait() Aguarda um thread específico terminar, então em seguida,
Página - 49 de 109
retorna a sua mensagem de saída. O thread pode ser
criado novamente, se necessário.
Seta a flag de término no thread de destino. O thread não
chThdTerminate() é eliminado, apenas pedimos para sair. O término é
cooperativo.
Retorna true se o current thread tem a flag de término
chThdShouldTerminateX()
setada.
chThdTerminatedX() Retorna true se o thread especifico foi finalizado.
9.2.2 Exemplos
9.2.2.1 Gerando e Ressincronização
Um LED pisca enquanto o sistema está ocupado com uma operação. Um thread é gerado
para piscar o LED e ele é finalizado quando a operação for concluída, o thread principal
ressincroniza com o resultado retornado pelo thread gerado.
/*
* Blinker thread.
*/
static THD_WORKING_AREA(waBlinker, 128);
static THD_FUNCTION(Blinker, arg) {
unsigned i = 0;
while (!chThdShouldTerminateX()) {
/* Alternando um LED enquanto o thread principal está ocupado. */
toggle_led();
/*
* Main application.
*/
int main(void) {
/*
* Inicialização do sistema.
* - Inicialização do Kernel, a função main() torna-se um thread e o
* RTOS é ativado.
*/
chSysInit();
/* Main code.*/
...;
Página - 50 de 109
/* Executar operações enquanto o LED estiver piscando.*/
...;
chThdTerminate(tp);
msg_t n = chThdWait(tp);
/* Continuing.*/
...;
}
9.3 Delays
Um problema comum é inserir atrasos na execução de threads. ChibiOS/RT fornece várias
soluções para este problema. Atrasos em threads são caracterizados por:
1. A resolução alcançável depende da frequência do system tick, se a frequência é 1000 Hz,
então a resolução de atrasos é de 1ms.
2. O tempo gasto em um atraso é usado para executar outros threads, não permanece
aguardando ocupado.
9.3.1 API
Insere um atraso especificado como o número de ticks
chThdSleep() de sistema, o atraso é arredondado para o próximo
limite de ticks.
Insere um atraso especificado em segundos, o atraso é
chThdSleepSeconds()
arredondado para o próximo limite de ticks.
Insere um atraso especificado em milissegundos.
chThdSleepMilliseconds() Note que a resolução real depende do system tick, o
atraso é arredondado para o próximo limite de ticks.
Insere um atraso especificado em microssegundos.
chThdSleepMicroseconds() Note que a resolução real depende do system tick, o
atraso é arredondado para o próximo limite de ticks.
Dorme até o contador system time atinga o valor
chThdSleepUntil()
especificado.
Caso especial de chThdSleepUntil(), onde
chThdSleepUntilWindowed()
uma janela de tempo é especificada.
9.3.2 Exemplos
9.3.2.1 Intervalos Fixos # 1
Este exemplo mostra um thread piscar um LED em intervalos fixos de um segundo.
static THD_WORKING_AREA(waBlinker, 128);
static THD_FUNCTION(Blinker, arg) {
Página - 51 de 109
while (true) {
LED_on();
chThdSleepMilliseconds(500);
LED_off();
chThdSleepMilliseconds(500);
}
}
Note que no exemplo acima, há uma forte suposição de que as funções LED_on() e
LED_off() são executados num tempo finito que deve ser menor do que o intervalo do tick
(digamos 1mS). Se o tempo de execução da função exceder o intervalo do tick, em seguida, um erro
acumula após cada ciclo e do período de pisca-pisca não é exatamente um segundo.
Página - 52 de 109
static THD_FUNCTION(Blinker, arg) {
LED_off();
prev = chThdSleepUntilWindowed(prev, prev + MS2ST(500));
}
}
Esta versão do pisca-pisca é tolerante com prazos perdidos porque a janela de tempo do
ciclo atual está totalmente especificada. Quando a função é invocada, a hora do sistema deve estar
dentro do intervalo especificado ou a função retornaria imediatamente sem dormir.
9.4.1 API
chThdSuspendS() Suspende um thread invocando uma variável de referência.
Suspende um thread invocando uma variável de referência
chThdSuspendTimeoutS()
com uma especificação de tempo limite.
chThdResume() Reinicia um thread suspenso.
chThdResumeI() Reinicia um thread suspenso (variante I-Class).
chThdResumeS() Reinicia um thread suspenso (variante S-Class).
9.4.2
9.4.3 Exemplos
9.4.3.1 Thread Servindo um IRQ
Este é um caso de uso comum, precisamos que um thread seja acordado quando um IRQ
específico é acionado.
Página - 53 de 109
/*
* A variável de referência deve ser inicialmente definida como NULL.
*/
thread_reference_t uart_thread_ref = NULL;
/*
* ISR atendendo a interrupção UART RX FIFO.
*/
CH_IRQ_HANDLER(UART_RX_IRQ) {
CH_IRQ_PROLOGUE();
CH_IRQ_EPILOGUE ();
}
/*
* O thread opera dentro de uma zona crítica, excepto quando:
* 1-Os dados estão sendo processados.
* 2-O thread está à espera de dados.
* Isto é feito, a fim de verificar atomicamente se há disponibilidade de dados
* considerando que existe um ISR envolvido. A duração da
* zona crítica é muito curta.
*/
static THD_WORKING_AREA(waUartReceiveThread, 128);
static THD_FUNCTION(UartReceiveThread, arg) {
chSysLock();
while (true) {
/* Certifica-se de esvaziar o FIFO RX antes suspender*/
while ((UART->SR & UART_SR_RXFIFO_CNT) > 0) {
Página - 54 de 109
9.5 Filas de Threads
Filas de Threads são um tipo especial de objeto FIFO, as seguintes operações são definidas:
• Enfileira-se e vai dormir.
• Retira o próximo thread da threads file se for o caso.
• Retira todos os threads da threads file.
Operações de Retirada da fila também são possíveis a partir do contexto ISR. Operações de
enfileiramento pode ter uma especificação de tempo limite opcional. O tipo da variável threads file
é threads_queue_t.
9.5.1 API
chThdQueueObjectInit() Inicializa um objeto thread queue.
chThdQueueIsEmptyI() Retorna true se a fila está vazia.
chThdEnqueueTimeoutS() Enfileira o thread chamando a fila.
Retira da fila o próximo thread da fila, assumindo-se que
chThdDoDequeueNextI()
fila contem pelo menos um elemento.
chThdDequeueNextI() Retira da fila o próximo thread da fila, se houver.
chThdDequeueAllI() Retira da fila todos os threads da fila, se houver.
9.5.2 Exemplos
9.5.2.1 Processamento de Frames
Vamos supor que tenhamos algum tipo periférico de rede, frames são recebidos e são
processados por um ou mais threads. A interface de rede pode cair e o evento deve ser enviado para
os threads em processamento.
/*
* ISR atendendo a interrupção de rede.
*/
CH_IRQ_HANDLER(RX_FRAME_IRQ) {
CH_IRQ_PROLOGUE();
Página - 55 de 109
/* Entrando no estado I-Locked e acordar um thread, se disponível,
o frame será perdido se não houver threads disponíveis. */
chSysLockFromISR ();
chSysUnlockFromISR();
CH_IRQ_EPILOGUE();
}
/*
* Thread de processmento do Frame, pode haver mais de um thread executando
* este código.
* Nota, 4 áreas de trabalho são alocados, um para cada thread.
*/
static THD_WORKING_AREA(waProcessFrameThread1, 128);
static THD_WORKING_AREA(waProcessFrameThread2, 128);
static THD_WORKING_AREA(waProcessFrameThread3, 128);
static THD_WORKING_AREA(waProcessFrameThread4, 128);
while (true) {
/* À espera de um evento de rede.*/
chSysLock();
msg_t msg = chThdEnqueueTimeoutS(&rx_frames_queue,
TIME_INFINITE);
chSysUnlock();
/* O processamento do evento.*/
if (msg == MSG_RESET) {
/* Tratamento de falha na rede.*/
process_failure();
}
else {
/* Processando o frame que está chegando.*/
process_frame((void *)msg);
net_return_frame_buffer((void *)msg);
}
}
}
/*
* Inicialização.
*/
void main(void) {
/*
Página - 56 de 109
* Inicializações do sistema.
* - Inicialização do kernel, a função main () torna-se um thread e o
* RTOS é ativado.
*/
chSysInit();
/* Continuing.*/
...;
}
9.6.1 API
Retorna o tempo do CPU consumido pelo especifico thread no
chThdGetTicksX()
system ticks.
9.6.2 Exemplos
9.6.2.1 Pulso CPU
Este exemplo faz com que o thread chamado consuma o tempo da CPU para o número
especificado de ticks. O pulso não é afetado por outras threads. Não confunda isto com um atraso, o
tempo total de execução depende da duração do pulso e do tempo gasto por outros threads
interrompendo isto.
void cpu_pulse(unsigned duration) {
systime_t start, end, current;
start = chThdGetTicksX(chThdGetSelfX());
end = start + MS2ST(duration);
do {
current = chThdGetTicksX(chThdGetSelfX());
} while (current - start < end - start);
}
Página - 57 de 109
9.7 Gestão prioridade
No ChibiOS/RT é possível que um thread altere a sua própria prioridade, isto normalmente
não é necessário, mas há casos de utilização.
9.7.1 API
chThdGetPriorityX() Retorna a prioridade do thread atual.
Altera o nível de prioridade do thread, retorna a antiga
chThdSetPriority()
prioridade.
9.7.2 Exemplos
9.7.2.1 Priority Ceiling “Teto de prioridade”
Este exemplo implementa a exclusão mútua em um recurso compartilhado usando um
mecanismo de teto de prioridade, isto é comumente encontrado em RTOSes que implementam a
especificação OSEK.
A suposição é que ao recurso compartilhado é dada uma prioridade mais elevada do que os
threads que tentam acessá-lo.
#define MY_RESOURCE_PRIORITY (NORMALPRIO + 10)
void access_resource(void) {
prio_t oldprio;
Página - 58 de 109
2. Cooperative Round Robin. Este modo é ativado, definindo
CH_CFG_TIME_QUANTUM a zero. Neste modo, a troca entre os threads ao mesmo
nível de prioridade é sempre cooperativa. O modo cooperativo é preferido porque o
núcleo se torna ligeiramente mais eficiente porque não tem que lidar com intervalos
de tempo.
9.8.1 API
O thread atual abandona sua fatia de tempo para o próximo thread na
chThdYield() cadeia round robin. Esta função não tem efeito se o thread atual é o único
no nível de prioridade atual.
9.8.2 Exemplos
9.8.2.1 Round Robin Cooperativo
Vários threads usam intensivamente a CPU e abandonam voluntariamente a CPU aos seus
pares depois de um tempo. Note que threads usando intensivamente a CPU normalmente são
colocados na parte inferior da escala de prioridades.
/*
* Threads de processamento.
*/
static THD_WORKING_AREA(waProcessThread1, 128);
static THD_WORKING_AREA(waProcessThread2, 128);
static THD_WORKING_AREA(waProcessThread3, 128);
static THD_WORKING_AREA(waProcessThread4, 128);
while (true) {
/*
* Inicialização.
*/
void main(void) {
/*
* Inicializações do sistema.
* - Inicialização do kernel, a função main () torna-se um thread e o
* RTOS é ativado.
*/
chSysInit();
Página - 59 de 109
chThdCreateStatic(waProcessThread3, sizeof(waProcessThread3), NORMALPRIO-10,
ProcessThread, NULL);
chThdCreateStatic(waProcessThread4, sizeof(waProcessThread4), NORMALPRIO-10,
ProcessThread, NULL);
Capítulo - 10 RT Semáforos
Semáforos são uma das características mais comuns encontrados em RTOSes embarcados, é
claro que existem diferenças na implementação e nos detalhes.
ChibiOS/RT implementa duas variantes de semáforos:
• Counting Semaphores.
• Binary Semaphores.
Página - 60 de 109
10.2.1 Extensões
ChibiOS/RT implementa uma versão estendida dos semáforos Dijkstra, existem várias
melhorias sobre a definição inicial:
• Reset Operation. Além das clássicas operações Wait e Signal foi adicionada uma
nova operação Reset. Esta operação é capaz de redefinir um contador de semáforo
para qualquer valor não-negativo, todos os threads em espera serão retirados da fila,
se for o caso.
• Timeouts. A operação de espera tem um parâmetro de tempo limite opcional, um
thread na fila é capaz de ser retirado da fila, se um Signal ou Reset não for executado
dentro do intervalo de tempo específico.
• Message. A operação de Wait retorna uma mensagem código indicando a forma
como o thread foi sinalizado:
• MSG_OK O thread recebeu o recurso normalmente.
• MSG_RESET O thread foi colocado na fila e uma operação de Reset foi
executada no semáforo.
• MSG_TIMEOUT O thread foi colocado na fila e um timeou ocorreu.
• Atomic Signal and Wait. Uma operação Signal foi realizada em um semáforo e uma
operação de Wait foi executada no outro semáforo atomicamente.
10.2.2 API
SEMAPHORE_DECL() Inicializador estático do semáforo.
chSemObjectInit() Inicializa um objeto semáforo do tipo semaphore_t
chSemWait() Executa uma operação Wait no semáforo.
chSemWaitS() Executa uma operação Wait no semáforo (variante S-Class).
chSemWaitTimeout() Executa uma operação Wait no semáforo com a especificação
Página - 61 de 109
de tempo limite.
Executa uma operação de espera no semáforo com a
chSemWaitTimeoutS()
especificação de tempo limite (variante S-Class).
chSemSignal() Executa uma operação Signal no semáforo.
chSemSignalI() Executa uma operação Signal no semáforo (variante I-Class).
chSemReset() Realiza uma operação Reset no semáforo.
chSemResetI() Realiza uma operação Reset no semáforo (variante I-Class).
Adiciona uma constante ao semáforo de contagem, threads são
chSemAddCounterI()
removidos fila conforme necessário (variante I-Class).
Atomicamente executa um Signal em um semáforo e um Wait
chSemSignalWait()
em outro semáforo.
Retorna o valor atual do semáforo de contagem (variante I-
chSemGetCounterI()
Class).
Versão ultrarrápida de Wait utilizável em condições onde o
chSemFastWaitI() contador é conhecido por ser maior que zero, é um decremento
puro (variante I-Class).
Versão ultrarrápida de Signal utilizável em condições onde o
chSemFastSignalI() contador é conhecido por ser não-negativo, é um incremento
puro (variante I-Class).
10.2.3 Exemplos
10.2.3.1 Alocador de recursos
Imagine ter um conjunto finito de recursos, canais DMA, por exemplo e querendo
implementar um alocador dos ditos canais DMA para uso em drivers de dispositivo. Cada driver
deverá:
1. Alocar um canal.
2. Executar a operação.
3. Liberar o canal.
O problema é que há mais driver do que canal e não queremos que a operação falhe, então
os drivers farão fila se um canal não estiver disponível imediatamente.
#define DMA_NUM_CHANNELS 16
/*
* Aloca um canal DMA. Usuários devem utilizar o canal e
* em seguida, liberá-lo em um tempo determinado.
*/
channel_t dmaAllocateChannel(void) {
/* Obtêm um canal do pool, os semáforos garante que haja pelo menos um.*/
return dma_get_channel();
}
Página - 62 de 109
/*
* Retorna um canal para o pool após o uso.
*/
void dmaReleaseChannel(channel_t channel) {
/*
* Inicialização.
*/
void main(void) {
/*
* Inicializações do sistema.
* - Inicialização do kernel, a função main () torna-se um thread e o
* RTOS é ativado.
*/
chSysInit();
chSemObjectInit(&dmasem, DMA_CHANNELS);
/* Continuing.*/
...;
}
Página - 63 de 109
10.3.1 Extensões
ChibiOS/RT implementa uma versão estendida dos semáforos binários, existem várias
melhorias em relação a definição usual:
• Reset Operation. Além das clássicas operações Wait e Signal foi adicionada uma nova
operação Reset. Esta operação é capaz de resetar o estado do semáforo para “ocupado”
ou “não ocupado”, todos os threads em espera são retirados da fila, se for o caso.
• Timeouts. A operação Wait tem um parâmetro de tempo limite opcional, um thread na
fila é capaz de ser retirado da fila, se um Signal ou Reset não for executado dentro do
intervalo de tempo específico.
• Message. A operação de Wait retorna uma mensagem código indicando a forma como o
thread foi sinalizado:
• MSG_OK O thread recebeu o recurso normalmente.
• MSG_RESET O thread foi colocado na fila e uma operação de Reset foi
executada no semáforo.
• MSG_TIMEOUT O thread foi colocado na fila e um timeou ocorreu.
10.3.2 API
BSEMAPHORE_DECL() Inicializador estático do semáforo.
chBSemObjectInit() Inicializa um objeto de semáforo de tipo semaphore_t.
chBSemWait() Executa uma operação Wait no semáforo.
Executa uma operação Wait no semáforo (variante de S-
chBSemWaitS()
Class).
chBSemWaitTimeout() Executa uma operação Wait no semáforo com especificação
Página - 64 de 109
de tempo limite.
Executa uma operação Wait no semáforo com especificação
chBSemWaitTimeoutS()
de tempo limite (variante de S-Class).
chBSemSignal() Executa uma operação Signal no semáforo.
Executa uma operação Signal no semáforo (variante de I-
chBSemSignalI()
Class).
chBSemReset() Executa uma operação Reset no semáforo.
Executa uma operação Reset no semáforo (variante de I-
chBSemResetI()
Class).
chBSemGetStateI() Retorne true se o semáforo é tomado (variante de I-Class).
10.3.3 Exemplos
/*
* ISR servindo a interrupção UART RX FIFO.
*/
CH_IRQ_HANDLER(UART_RX_IRQ) {
CH_IRQ_PROLOGUE();
CH_IRQ_EPILOGUE();
}
/*
* IRQ servindo o thread, os tempos de espera de comunicação serão tratados.
*/
static THD_WORKING_AREA(waUartReceiveThread, 128);
static THD_FUNCTION(UartReceiveThread, arg) {
while (true) {
/* Aguarda por uma interrupção. Se a interrupção já ocorreu então o thread
não vai parar em chBSemWaitTimeout() porque o semáforo binário estaria no
estado "não ocupado". Está programado um tempo de espera de 500 ms.*/
msg_t msg = chBSemWaitTimeout(&uart_bsem, MS2ST(500));
Página - 65 de 109
especial será necessário.*/
if (msg == MSG_TIMEOUT) {
handle_comm_timeout();
continue;
}
/*
* Inicialização.
*/
void main(void) {
/*
* Inicializações do sistema.
* - Inicialização do kernel, a função main() torna-se um thread e o
* RTOS é ativado.
*/
chSysInit();
/* Continuing.*/
...;
}
Observe que um semáforo contador não deve usado como neste exemplo, porque o contador
contaria após cada interrupção e pode transbordar se não decrementado rápido o suficientemente
pelo thread. O semáforo binário é seguro, porque há apenas um único estado "não ocupado",
múltipla sinalização não tem efeito.
Página - 66 de 109
CH_CFG_USE_MUTEXES Essa opção habilita a API mutexes no kernel.
Se setado, então semáforos podem ser usados de
CH_CFG_USE_MUTEXES_RECURSIVE
forma recursiva.
Essa opção habilita a API Variáveis
CH_CFG_USE_CONDVARS
Condicionadas no kernel.
Habilita o suporte de tempo limite para variáveis
CH_CFG_USE_CONDVARS_TIMEOUT
de condição.
11.2 Mutexes
Mutexes são o mecanismo destinado a implementar exclusão mútua, da maneira mais geral.
Muitas vezes há confusão entre Mutexes e Semáforos binários, ambos são aparentemente capazes
de resolver o mesmo problema, mas há diferenças importantes:
• Mutexes tem um atributo proprietário, semáforos não têm proprietários. Devido a isso
os mutexes só pode ser desbloqueado pela mesma thread que o bloqueou. Isso não é
necessário para os semáforos que podem ser desbloqueados por qualquer thread ou
mesmo ISRs.
• Mutexes pode implementar protocolos para lidar com inversão de prioridade, sabendo
que no mutexe o proprietário é obrigatório a fim de que possa ser capaz de implementar
a Herança de Prioridade ou de Algoritmos de Teto Prioridade. ChibiOS/RT Mutexes
implementa o algoritmo herança de prioridade sem restrições sobre o número de threads
ou o número de zonas de exclusão mútuas aninhadas.
• Mutexes podem ser implementados para ser mutexes recursivos, isto significa que o
mesmo thread pode bloquear o mesmo mutex repetidamente e, em seguida, tem que
desbloqueá-lo o mesmo número de vezes. No ChibiOS/RT mutexes recursivo pode ser
habilitado ativando uma opção de configuração.
Mutexes são regulados pelo seguinte diagrama de estado:
Página - 67 de 109
Cada mutex tem uma referência para o thread proprietário e uma fila de thread em espera,
por outro lado, threads tem uma pilha de propriedade mutexes. O campo cnt conta quantas vezes o
proprietário ocupou o mutex, isto está presente apenas se o modo recursivo estiver habilitado.
11.2.1 API
MUTEX_DECL() Inicializador estático Mutexes.
chMtxObjectInit() Inicializa um objeto mutex do tipo mutex_t
chMtxLock() Bloqueia o mutex especificado.
chMtxLockS() Bloqueia o mutex especificado (variante S-Class).
Tenta bloquear um mutex, se o mutex já está ocupado por outro
chMtxTryLock()
thread, então a função sai sem esperar.
Tenta bloquear e mutex, se o mutex já está ocupado por outro
chMtxTryLockS()
thread, então a função sai sem esperar (variante S-Class).
Desbloqueia o próximo mutex de propriedade, a fim de bloqueio
chMtxUnlock()
reverso.
Desbloqueia o próximo mutex de propriedade, a fim de bloqueio
chMtxUnlockS()
reverso (variante S-Class).
chMtxUnlockAll() Desbloqueia todos os mutexes propriedade invocando o thread.
11.2.2 Exemplos
Mutexes têm um único uso, exclusão mútua.
while (true) {
Página - 68 de 109
/* Liberar o recurso.*/
chMtxUnlock(&mtx1);
/* O thread continua.*/
...;
}
}
while (true) {
/* Liberar o recurso.*/
chMtxUnlock(&mtx1);
/* O thread continua.*/
...;
}
}
/*
* Inicialização.
*/
void main(void) {
/*
* Inicializações do sistema.
* -Inicialização do kernel, a função main() torna-se um thread e o
* RTOS está ativo.
*/
chSysInit();
/* Inicializa os threads.*/
chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO + 1, Thread1,
NULL);
chThdCreateStatic(waThread2, sizeof(waThread2), NORMALPRIO + 1, Thread2,
NULL);
/* Continuing.*/
...;
}
11.2.3 Notas
Em todas as situações em que o acesso aos recursos compartilhados pode ser adiada, em
seguida, recomenda-se usar chMtxTryLock() em vez de chMtxLock() a primeira é muito
Página - 69 de 109
mais eficiente e não tem que entrar no estado de espera.
A função chMtxUnlockAll() libera todos os mutexes pertencentes a um thread, e por
razões relacionadas com o algoritmo de herança de prioridade, liberando todos os mutexes é muito
mais rápido do que liberando apenas um. Nas situações em que um thread é conhecido por possuir
apenas um mutex a utilização de chMtxUnlockAll() pode melhorar o desempenho.
Note como que a função chCondWait() opera implicitamente nos últimos semáforos ocupados.
11.3.1 Monitores
Uma maneira menos formal, mas, provavelmente, mais fácil de entender, e explicar como
um monitor funciona é imaginar isso como uma casa com três quartos.
Página - 70 de 109
A casa tem:
• A porta principal levando para a antessala.
• Uma antessala, a fila de mutex.
• Uma porta levando para a sala principal, quando o mutex é adquirido.
• A sala principal, onde apenas uma pessoa pode ficar a qualquer momento, a zona de
exclusão mútua.
• Uma porta de saída, quando o mutex é liberado.
• Uma porta levando a uma sala de espera, esperando na variável de condição.
• A sala de espera para as pessoas que têm de esperar por um evento externo, uma chamada
talvez, sem ocupar a sala principal, a fila de variável de condição.
• Uma porta levando da sala de espera de volta para a antessala, quando o evento ocorreu
externo.
• Uma porta de emergência que permita escapar sala de espera, tempos de espera em filas de
variáveis de condição.
Threads entram na antessala e esperam lá sua vez para entrar na sala principal. Quando na
sala principal threads podem também decidir deixar o edifício ou iniciar a espera por um sinal
externo, sem manter a sala principal ocupada.
Note que os monitores podem ter mais de uma fila de variáveis condicionais, nesse caso, não
haveria vários corredores trazendo de volta para a antessala.
/* Entrando no monitor.*/
chMtxLock(&mtx);
Página - 71 de 109
/* Verificar a condição e continua esperando até que esteja satisfeito, a
parte relacionada com o tempo de espera pode ser omitido em vez de usar
chCondWait().*/
while (!condition) {
msg_t msg = chCondWaitTimeout(&cond1, MS2ST(500));
if (msg == MSG_TIMEOUT)
return;
}
11.3.2 API
CONDVAR_DECL() Inicializador da Variável de condição estático.
Inicializa um objeto variável de condição do tipo
chCondObjectInit()
condition_variable_t
chCondSignal() Sinaliza uma variável de condição.
chCondSignalI() Sinais de uma variável de condição (variante I-Class).
chCondBroadcast() Transmite uma variável de condição.
chCondBroadcastI() Transmite uma condição variável (variante I-Class).
Faz com que o thread invocado libere o último mutex capturado e
chCondWait()
entre na fila de espera variável de condição.
Faz com que o thread invocado libere o último mutex capturado e
chCondWaitS()
entre na fila de espera variável de condição. (variante S-Class).
Faz com que o thread invocado libere o último mutex capturado e
chCondWaitTimeout() entre na fila de espera variável de condição com uma
especificação de limite de tempo.
Faz com que o thread invocado libere o último mutex capturado e
chCondWaitTimeoutS() entre na fila de espera variável de condição com uma
especificação de limite de tempo. (variante S-Class).
11.3.3
11.3.4 Exemplos
11.3.4.1 Produtores e consumidores
Neste exemplo temos uma função produtor sincronizada e uma função consumidor
sincronizada, ambos operando em uma fila circular de mensagens.
Página - 72 de 109
#define QUEUE_SIZE 128
/*
* Inicialização da fila sincronizada.
*/
void qInit(void) {
chMtxObjectInit(&qmtx);
chCondObjectInit(&qempty);
chCondObjectInit(&qfull);
/*
* Grava uma mensagem para a fila, se a fila está cheia aguarda
* um slot livre.*/
void qProduce(msg_t msg) {
/* Entrando no monitor.*/
chMtxLock(&qmtx);
/* Deixando o monitor.*/
chMtxUnlock(&qmtx);
}
/*
* Lê uma mensagem da fila, se a fila estiver vazia espera por uma mensagem.*/
msg_t qConsume(void) {
msg_t msg;
/* Entrando no monitor.*/
chMtxLock(&qmtx);
Página - 73 de 109
rd = &queue[0];
qsize--;
/* Deixando o monitor.*/
chMtxUnlock(&qmtx);
return msg;
}
/*
* Inicialização da fila sincronizada.
*/
void qInit(void) {
chMtxObjectInit(&qmtx);
chCondObjectInit(&qempty);
/*
* Grava uma mensagem na fila, se a fila está cheia aguarda um slot livre.
* Note que funções I-Class são usadas de dentro da zona crítica.
*/
void qProduceFromISR(msg_t msg) {
/* Entrando no monitor.*/
chSysLockFromISR();
Página - 74 de 109
}
/* Saindo do monitor.*/
chSysUnlockFromISR();
}
/*
* Lê uma mensagem da fila, se a fila estiver vazia espera por uma mensagem.
* Note que as funções da S-classe são usadas de dentro da zona crítica.
*/
msg_t qConsume(void) {
msg_t msg;
/* Saindo do monitor.*/
chMtxUnlockS(&qmtx);
chSysUnlock();
return msg;
}
Página - 75 de 109
12.2 Descrição
Ao usar mensagens síncronos existem dois tipos de threads: os clientes e servidores.
• Os clientes são threads que iniciam uma transação enviando uma mensagem para um
threads servidor, em seguida, esperar por uma mensagem de resposta.
• Os servidores são threads que esperam por um início transação de um cliente, uma
vez que uma mensagem é recebida, o servidor processa-a e, finalmente, envia uma
mensagem de resposta para o cliente. Os servidores são capazes de lidar apenas com
uma mensagem de cada vez, mas também pode lidar com várias mensagens de
diferentes clientes e o retorna as respostas em uma ordem arbitrária.
O diagrama a seguir mostra a sequência de operações entre clientes e servidores.
Note que pode haver threads que são clientes e servidores ao mesmo tempo.
12.2.1 Mensagens
As mensagens são sempre sinalizadas e do tipo msg_t. Este tipo é garantido para ser capaz
de ser convertido “cast-able” de/para ponteiros de dados. Os clientes podem enviar mensagens
codificadas simples, mas também ponteiros para estruturas que representam operações complexas.
Há três mensagens predefinidas usadas internamente pelo kernel RT:
• MSG_OK Definido como 0 é uma mensagem de reconhecimento genérico.
• MSG_TIMEOUT Definido como -1 é a mensagem enviada em caso de timeouts.
• MSG_RESET Definido como -2 é a mensagem enviada para informar sobre uma
condição de objeto resete.
Presume-se que ponteiros com valores 0, -1 e -2 a não sejam ponteiros válidos. Observe
também que não exitem objetos dedicados envolvidos, a troca é feita diretamente entre threads,
Página - 76 de 109
cada thread tem sua própria fila de mensagens recebidas.
12.2.2 API
chMsgSend() Envia uma mensagem para o thread especifico.
Espera por uma mensagem, retorna o ponteiro encaminhado como
chMsgWait()
uma thread_t *
chMsgGet() Recupera a mensagem depois de sair de chMsgWait()
chMsgRelease() Retorna uma resposta ao específico remetente.
chMsgIsPendingI() Avalia como true se houver uma mensagem em espera na fila.
Note que os threads não trocam mensagens complexas, mas apenas ponteiros para estruturas
de dados, a fim de optimizar o desempenho. Observe também que um thread pode ser cliente e
servidor ao mesmo tempo, o serviço FS no cenário anterior, por exemplo.
Uma vantagem adicional desta abordagem deriva de um problema comum quando se
Página - 77 de 109
trabalha com RTOSes, muitas vezes necessários para integrar uma biblioteca que não foi concebido
para trabalhar com um RTOS que provavelmente poderia ser não reentrante ou usar um monte de
espaço de pilha, por exemplo uma pilha gráfica.
Se for este o caso, então o código externo pode ser encapsulado num thread servidor, outros
threads solicitariam o acesso ao código protegido pelo servidor, enviando mensagens que codificam
as várias operações possíveis.
As vantagens seriam:
• O código não reentrante agora é utilizável por vários threads, não há preocupações de
exclusão mútuos.
• A grande pilha deve ser atribuído apenas para o thread servidor e não para os threads que
pedem acesso ao código protegido.
• O código sempre é executado em um nível de prioridade fixa, a prioridade do seu thread.
12.3.1 Exemplos
12.3.1.1 Thread servidor
Threads servidor deve seguir este modelo.
static THD_FUNCTION(ServerThread, arg) {
while (true) {
/* À espera de uma mensagem na fila, em seguida, retorna-a*/
thread_t *tp = chMsgWait();
msg_t msg = chMsgGet(tp);
/* Processando a mensagem.*/
...;
/*
* Thread servidor do console, o argumento é o stream de onde a mensagem
* deve ser impresso.
*/
static THD_WORKING_AREA(waConsoleServerThread, 512);
static THD_FUNCTION(ConsoleServerThread, arg) {
BaseSequentialStream *stream = (BaseSequentialStream *)arg;
while (true) {
/* À espera de uma mensagem na fila, em seguida, retorna-a.*/
thread_t *tp = chMsgWait();
const char *msg = (const char *msg)chMsgGet(tp);
Página - 78 de 109
/* Imprime a mensagem, a mensagem prefixada com um timestamp.*/
chprintf(stream, "%010d: %s\r\n", chVTGetSystemTime(), msg);
/*
* Initialization.
*/
void main(void) {
/*
* Inicializações do sistema.
* -A camada de abstração de Hardware é inicializada.
* -Inicialização do kernel, a função main() torna-se um thread e o
* RTOS está ativo.
*/
halInit();
chSysInit();
/* Continuing.*/
...;
}
Capítulo - 13 RT Mailboxes
No ChibiOS/RT um objeto mailboxes é uma fila circular de mensagens que podem ser
inseridas e recuperadas tanto por threads como contextos ISR.
13.2 Descrição
Ao contrário de mensagens síncronas, mailboxes são assíncronas na natureza e mono-
direcionais, mensagens fluem em uma direção pela ordem na FIFO. Mailboxes são um recursos de
comunicação muito flexível e podem ser usada em muitas situações:
Página - 79 de 109
• Comunicação Thread/ISR para Thread/ISR, vários remetentes e múltiplos receptores são
suportados.
• Pool de objetos pré-inicializada.
13.2.1 Mensagens
Mailboxes usam os mesmos tipos de msg_t já vistos no capítulo mensagens síncronas, mas
sem a limitação do reservado constantes 0, -1 e -2 . O intervalo numérico está totalmente
disponível
13.2.2 Buffer
Objetos mailboxes têm um buffer circular de mensagens associada, normalmente uma matriz
de msg_t, a mailboxes pode ser preenchido até a capacidade do buffer, nova tentativa de postar
novos dados resultará no thread aguardando um slot de mensagem ficar livre.
Página - 80 de 109
Internamente, dois semáforos de contagem são usados para proteger as operações post e fetch.
13.2.3 API
MAILBOX_DECL() Inicializador estático Mailboxes.
chMBObjectInit() Inicializa um objeto Mailboxes do tipo mailbox_t.
Uma Mailboxes é imediatamente esvaziada, todos os threads na
chMBReset()
espera são notificados com uma mensagem MSG_RESET.
Uma Mailboxes é imediatamente esvaziado, todos os threads na
chMBResetI() espera são notificados com uma mensagem MSG_RESET (variante
I-Class).
chMBPost() A mensagem é postada na Mailboxes.
chMBPostS() A mensagem é postada na Mailboxes (variante S-Class).
chMBPostI() A mensagem é postada na Mailboxes (variante I-Class).
chMBPostAhead() A mensagem é postada na Mailboxes à frente de outras mensagens.
A mensagem é postada na Mailboxes à frente de outras mensagens
chMBPostAheadS()
(variante S-Class).
A mensagem é postada na Mailboxes à frente de outras mensagens
chMBPostAheadI()
(variante I-Class).
chMBFetch() Traz uma mensagem de uma Mailboxes.
chMBFetchS() Traz uma mensagem de uma Mailboxes (variante S-Class).
chMBFetchI() Traz uma mensagem de uma Mailboxes (variante I-Class).
chMBGetSizeI() Retorna o tamanho do buffer de Mailboxes (variante I-Class).
Retorna o número de slots de mensagens livres em uma Mailboxes
chMBGetFreeCountI()
(variante I-Class).
Retorna o número de slots de mensagem utilizados em uma
chMBGetUsedCountI
Mailboxes (variante I-Class).
Retorna a próxima mensagem na fila sem removê-la (variante I-
chMBPeekI()
Class).
13.2.4 Exemplos
13.2.4.1 Mensagens grandes
Imagine querer trocar grandes buffers entre um produtor ISR e um ou mais threads
Página - 81 de 109
consumidores, os buffers representam frames recebidos da rede.
Este exemplo utiliza duas mailboxes:
• Um pool de objetos de buffers livre.
• Uma fila de mensagens com buffers preenchidos enviados pelo ISR para os threads
consumidores.
Existem várias limitações:
• Uma vez recuperado por um consumidor o buffer permanece “em uso” e não deve ser
tocado até que o consumidor o devolva para o pool.
• Buffers devem estar disponíveis quando o ISR solicitar um frame ou o frame será perdido.
Isto significa que deve haver threads consumidores o suficiente e largura de banda da CPU
para processá-los rápido o suficiente.
• Falhas de rede devem ser enviadas para os threads consumidores.
#define NUM_BUFFERS 16
#define BUFFERS_SIZE 256
/*
* ISR servindo a interrupção de rede..
*/
CH_IRQ_HANDLER(RX_FRAME_IRQ) {
CH_IRQ_PROLOGUE();
Página - 82 de 109
fill_buffer(pbuf);
(void)chMBPostI(&filled_buffers, (msg_t)pbuf);
}
chSysUnlockFromISR();
CH_IRQ_EPILOGUE();
}
while (true) {
void *pbuf;
/* Processando o evento.*/
if (msg == MSG_RESET) {
/* A caixa de correio foi resetada, o que significa falha de rede.*/
process_failure();
}
else {
/* Processamento do frame na entrada.*/
process_frame(pbuf);
/*
* Inicialização.
*/
void main(void) {
unsigned i;
/*
* Inicializações do sistema.
* -Inicialização do kernel, a função main() torna-se um thread e o
* RTOS está ativo.
*/
chSysInit();
/* Criando o mailboxes.*/
chMBObjectInit(&filled_buffers, filled_buffers_queue, NUM_BUFFERS);
chMBObjectInit(&free_buffers, free_buffers_queue, NUM_BUFFERS);
/* Preencher o pool de buffers livres com os buffers disponíveis, o post não vai parar
porque a caixa de correio é grande o suficiente.*/
for (i == 0; i < NUM_BUFFERS; i++)
(void)chMBPost(&free_buffers, (msg_t)&buffers[i]);
Página - 83 de 109
chThdCreateStatic(waProcessFrameThread3, sizeof(waProcessFrameThread3),
NORMALPRIO + 1, ProcessFrameThread, NULL);
chThdCreateStatic(waProcessFrameThread4, sizeof(waProcessFrameThread4),
NORMALPRIO + 1, ProcessFrameThread, NULL);
/* Continuing.*/
...;
}
Capítulo - 14 Eventos
Um dos recursos mais poderosos no ChibiOS/RT é o subsistema de eventos. Eventos
abordam uma classe específica de problemas:
• Espera por vários eventos. Outras primitivas só esperam por um evento específico.
• Vários threads, cada um interessado em um ou mais eventos, em uma relação many-to-many
“muitos-para-muitos”.
• Os eventos são transmitidos de forma assíncrona mas verificados sincronicamente, threads
decidem quando procurar eventos ou esperar por eventos.
14.2 Descrição
Existem várias classes de objetos envolvidos no tratamento de eventos: Event Sources, Event
Listeners, Event Flags e os próprios threads.
Página - 84 de 109
14.2.2 Bandeiras de Eventos
Eventos também carregam informações, a informação é codificada como bandeiras de
Eventos, uma máscara de bandeiras do tipo eventflags_t é transmitida pelas fontes junto com
o próprio evento.
Por exemplo, uma fonte de evento é associado a um driver serial para sinalizar eventos na
linha, as bandeiras representam a condição da linha: dados transmitidos, dados recebidos, erro de
paridade, framing erro etc. Basicamente, o evento em si apenas diz que algo aconteceu, as bandeiras
associadas indicam o que aconteceu, a razão para o evento.
Página - 85 de 109
14.2.5 Operações
Há três operações fundamentais que envolvem eventos.
14.2.5.1 Registrando
A operação register é realizada por um thread, a fim de se tornar um ouvinte de uma fonte de
eventos, a associação é intermediada por um objeto ouvinte de evento como segue:
PROCEDURE register(source, listener, events, wflags)
LET listener.flags = 0
LET listener.wflags = wflags
LET listener.events = events
LET listener.thread = current_thread
source.listeners = source.listeners + listener
END
Note que deve haver um objeto ouvinte de evento diferente para cada segmento.
14.2.5.2 Esperando
A operação wait permite que um thread possa verificar se existem eventos pendentes ou espera por
eles se não houver nenhum:
FUNCTION wait(events)
LET current_thread.ewmask = events
IF current_thread.epending AND current_thread.ewmask = 0
WAIT
END
RETURN current_thread.epending AND current_thread.ewmask
END
14.2.5.3 Broadcasting
A operação broadcast é muito complexa:
PROCEDURE broadcast(source, flags)
FOR EACH source.listeners AS listener
LET listener.flags = listener.flags OR flags
IF (listener.flags AND listener.wflags) <> 0
LET listener.thread.epending = listener.thread.epending OR listener.events
IF listener.thread.epending AND listener.thread.ewmask <> 0
WAKEUP listener.thread
END
END
END
Página - 86 de 109
END
Como você pode ver o thread na escuta deve estar interessado em ambos os broadcasted flags e o
specific event, a fim de ser notificado.
14.2.6.1 Signaling
PROCEDURE signal(thread, events)
LET thread.epending = thread.epending OR events
IF thread.epending AND thread.ewmask <> 0
WAKEUP thread
END
END
14.2.7 API
EVENTSOURCE_DECL() Inicializador estático de fontes de evento.
Converte de um identificador de evento a uma máscara
EVENT_MASK()
de evento.
Inicializa um objeto fonte de evento do tipo
chEvtObjectInit()
event_source_t.
Registra o atual thread em uma fonte de evento,
chEvtRegister()
atribuindo a ele um identificador evento.
Registra o atual thread em uma fonte de evento,
chEvtRegisterMask()
atribuindo a ele uma máscara de eventos.
Registra o atual thread em uma fonte de evento,
chEvtRegisterMaskWithFlags() atribuindo a ele uma máscara de eventos e um conjunto
de sinalizadores de evento.
Cancela o registro do atual thread de uma fonte de
chEvtUnregister()
evento.
chEvtGetAndClearEvents() Retorna os eventos pendentes para o thread atual.
chEvtAddEvents() Adiciona uma máscara de eventos para o thread atual.
Adiciona uma máscara de eventos para o thread
chEvtSignal()
especificado.
Adiciona uma máscara de eventos para o thread
chEvtSignalI()
especificado (variante I-Class).
Executa a operação de transmissão em uma fonte de
chEvtBroadcast()
evento sem bandeiras.
Página - 87 de 109
Executa a operação de transmissão em uma fonte de
chEvtBroadcastI()
evento sem bandeiras (variante I-Class).
Executa a operação de transmissão em uma fonte de
chEvtBroadcastFlags() eventos e adiciona os sinalizadores especificados para
os ouvintes de eventos registrados.
Executa a operação de transmissão em uma fonte de
chEvtBroadcastFlagsI() eventos e adiciona os sinalizadores especificados para
os ouvintes de eventos registrados (variante I-Class).
Retorna as bandeiras de eventos pendentes no ouvinte
chEvtGetAndClearFlags()
de evento especificado.
Retorna as bandeiras de eventos pendentes no ouvinte
chEvtGetAndClearFlagsI()
de evento especificado (variante I-Class).
chEvtWaitOne() Aguarda exatamente um dos eventos especificados.
chEvtWaitAny() Espera por qualquer dos eventos especificados.
chEvtWaitAll() Espera que todos os eventos especificados.
Aguarda exatamente um dos eventos especificados
chEvtWaitOneTimeout()
com timeout.
Espera por qualquer dos eventos especificados com
chEvtWaitAnyTimeout()
timeout.
Espera que todos os eventos especificados com
chEvtWaitAllTimeout()
timeout.
Verifica se existe pelo menos um ouvinte registrado na
chEvtIsListeningI()
origem do evento (variante I-Class).
Chama as funções associadas a uma máscara de
chEvtDispatch()
eventos.
14.2.8 Exemplos
14.2.8.1 Múltiplos Eventos
Imaginem ter um único segmento lidando com a atividade de E/S de um aplicativo, o thread
é registrado em várias fontes de eventos e manipula todos os eventos.
event_source_t network_event_source;
event_source_t serial_event_source;
Página - 88 de 109
&serial_listener,
EVENT_MASK(1),
SERIAL_FRAMING_ERROR | SERIAL_PARITY_ERROR |
SERIAL_DATA_IN);
/* Atividade do thread.*/
while (true) {
/* Estamos esperando por qualquer um dos eventos registrados.*/
eventmask_t evt = chEvtWaitAny(ALL_EVENTS);
/* Servindo eventos.*/
if (evt & EVENT_MASK(0)) {
/* Evento de rede, chamando o manipulador, não há bandeiras para lidar
com interface de rede.*/
network_handler();
}
if (evt & EVENT_MASK(1)) {
/* Eventos a partir da interface serial, obtendo bandeiras da serial
primeiro.*/
eventflags_t flags = chEvtGetAndClearFlags(&serial_listener);
/* Tratando errors.*/
if (flags & (SERIAL_FRAMING_ERROR | SERIAL_PARITY_ERROR))
handle_serial_errors();
if (flags & SERIAL_DATA_IN)
handle_serial_data();
}
}
}
/*
* Ponteiro para a thread de manejo da UART.
*/
thread_t *uart_thread;
/*
* ISR servindo a interrupção UART RX FIFO.
*/
CH_IRQ_HANDLER(UART_RX_IRQ) {
eventmask_t events = 0;
uint32_t sr;
CH_IRQ_PROLOGUE();
Página - 89 de 109
/* Máscara de eventos inicialmente no zero.*/
events = 0;
/* Se houver erros.*/
if ((UART->SR & UART_SR_ERRORS) != 0)
events |= EVT_UART_ERR;
CH_IRQ_EPILOGUE();
}
/* Atividade do thread.*/
while (true) {
/* Aguardando por qualquer evento.*/
eventmask_t evt = chEvtWaitAny(ALL_EVENTS);
/* Servindo eventos.*/
if (evt & EVT_UART_ERR) {
/* Evento Erro.*/
error_handler();
}
if (evt & EVT_UART_RX) {
/* Evento de Dados disponíveis.*/
rx_handler();
}
if (evt & EVT_UART_TX) {
/* Evento TX pronto.*/
tx_handler();
}
}
}
/*
* Inicialização.
*/
void main(void) {
/*
* Inicializações do sistema.
* -Inicialização do kernel, a função main() torna-se um thread e o
* RTOS está ativo.
*/
chSysInit();
Página - 90 de 109
/* Iniciando o thread do console tornando imprimível através da porta serial.*/
uart_thread = chThdCreateStatic(waUARTThread, sizeof(waUARTThread),
NORMALPRIO + 1, UARTThread, NULL);
/* Continua.*/
...;
}
15.2 Descrição
As filas I/O são explicitamente concebidas para ligar ISRs e threads. Existem dois tipos de
filas derivadas de um predecessor comum chamado Generic I/O Queue.
Página - 91 de 109
Filas de I/O tem um mecanismo de notificação que informa o lado ISR quando dados foram lidos
ou escritos do lado do thread. Retornos de notificação de chamada pode ser usado para reabilitar
interrupções ou reiniciar as operações a nível HW.
As filas são de natureza assimétrica, o lado ISR é sempre non-blocking, isso significa que a
tentativa de escrever em uma fila de entrada que está completa ou ler uma fila de saída que está
vazia gera uma falha com um código de erro, não há wait envolvidos. Por outro lado, o lado do
thread pode ser:
• Blocking. Threads são suspensos até que a quantidade especificada de dados foi lido a partir
de uma fila de entrada ou escrito em uma fila de saída.
• Blocking with Timeout. A operação está bloqueando mas tem uma especificação de tempo
limite.
• Non-Blocking. A operação de leitura ou escrever transfere apenas a quantidade de dados que
podem ser transferidos imediatamente sem bloqueio.
As seguintes operações são definidas por todas as filas I/O.
Página - 92 de 109
15.2.2 Filas de entrada
Um objeto fila de entrada é um buffer circular para ser escrito do lado ISR e é lido do lado do
thread.
Página - 93 de 109
As operações seguintes são definidos para as filas de saída.
15.2.4 API
chQSizeI() Retorna o tamanho da fila.
chQSpaceI() Retorna o espaço cheio/vazio de fila.
chQGetLinkX() Retornar os dados do usuário associados à fila.
INPUTQUEUE_DECL() Inicializador estático de filas de entrada.
chIQObjectInit() Inicializa um objeto de caixa de correio de tipo input_queue_t.
chIQResetI() Redefine uma fila de entrada.
chIQPutI() Coloca um byte para a fila de entrada (variante I-Class).
chIQGetTimeout() Busca um byte da fila de entrada com timeout.
chIQReadTimeout() Lê um bloco de dados da fila de entrada com timeout.
OUTPUTQUEUE_DECL() Inicializador estático de filas de saída.
Página - 94 de 109
chOQObjectInit() Inicializa um objeto de caixa de correio de tipo output_queue_t.
chOQResetI() Redefine uma fila de saída.
chOQGetI() Busca um byte da fila de saída (variante I-Class).
chOQPutTimeout() Coloca um byte para a fila de saída com tempo limite.
chOQWriteTimeout() Grava um bloco de dados na fila de saída com tempo limite.
15.2.5 Exemplos
15.2.5.1 Driver UART com buffer
O caso de uso mais comum para filas E/S é o driver UART com buffers circulares. Neste exemplo,
uma fila de entrada e uma fila de saída estão associados a um UART, a fim de implementar o driver.
/*
* ISR servindo a interrupção UART.
*/
CH_IRQ_HANDLER(UART_RX_IRQ) {
msg_t msg;
CH_IRQ_PROLOGUE();
Página - 95 de 109
/* Transmiti os dados, a interrupção permanece habilitada.*/
UART->DR = (uint8_t)msg;
}
}
CH_IRQ_EPILOGUE();
}
/*
* Notificação da fila de saída, chamada quando forem inserido dados
* na fila de saída.
*/
(void)qp;
/*
* Inicialização.
*/
void main(void) {
/*
* Inicializações do sistema.
* -Inicialização do kernel, a função main() torna-se um thread e o
* RTOS está ativo.
*/
chSysInit();
/* Continua.*/
...;
}
Capítulo - 16 RT Streams
Uma característica peculiar em ChibiOS/RT são Streams, isso não é um subsistema
funcional mas uma declaração de uma interface genérica para objetos que podem ser lidos e
escritos.
16.1 Descrição
Streams são uma abstração sobre os objetos que precisam implementar funcionalidades de
Página - 96 de 109
leitura e escrita. Mesmo ChibiOS sendo escrito em C sua arquitetura é fortemente orientado a
objeto, existem classes de objetos como semáforos, filas e também existem interfaces abstratas,
streams é um exemplo de tal interface.
Esta é a interface para objetos que se pode ler e de bloqueio para os que se pode escrever.
Métodos desta interface só retornam quando uma quantidade especificada de dados for lido ou
escrito, o comportamento é sempre bloqueado.
16.1.1 API
chSequentialStreamWrite() Grava dados em uma stream.
chSequentialStreamRead() Lê os dados de uma stream.
chSequentialStreamPut() Grava um único byte na stream.
chSequentialStreamGet() Lê um único byte de uma stream.
Note que as declarações acima não são funções C, mas sim os métodos da interface, o
primeiro parâmetro é convencionalmente um ponteiro para um objeto (uma estrutura basicamente)
implementar a interface Sequential Stream.
16.1.2 Exemplos
16.1.2.1 Estendendo Streams
Uma característica importante é que nós podemos estender as interfaces escrevendo
interfaces ou classes que os herdam. Neste exemplo vamos ver como escrever uma nova interface
herdando Sequential Stream e adicionando métodos não-bloqueio, a nova interface é chamado
BaseChannel e se entende como predecessor de interfaces I/O-oriented.
Página - 97 de 109
O código:
/*
*Métodos específicos de BaseChannel.
*Nota, o macro _base_sequential_stream_methods herda os métodos da interface
*pai, em seguida, são adicionados novos métodos.
*/
#define _base_channel_methods \
_base_sequential_stream_methods \
/* Método put Channel com especificação de tempo limite.*/ \
msg_t (*putt)(void *instance, uint8_t b, systime_t time); \
/* Método get Channel com especificação de tempo limite.*/ \
msg_t (*gett)(void *instance, systime_t time); \
/* Método write Channel com especificação de tempo limite.*/ \
size_t (*writet)(void *instance, const uint8_t *bp, \
size_t n, systime_t time); \
/* Método read Channel com especificação de tempo limite.*/ \
size_t (*readt)(void *instance, uint8_t *bp, size_t n, systime_t time);
/**
* Dados específicos do BaseChannel.
* Note que é vazio, porque BaseChannel é apenas uma interface sem
* implementação.
*/
#define _base_channel_data \
_base_sequential_stream_data
/**
* BaseChannel tabela de métodos virtuais.
*/
struct BaseChannelVMT {
_base_channel_methods
};
/**
* Interface de canal base.
* Isso representa uma interface genérica de um canal de I/O, com
* largura de um byte.
*/
typedef struct {
Página - 98 de 109
/** @brief Tabela de Métodos Virtuais.*/
const struct BaseChannelVMT *vmt;
_base_channel_data
} BaseChannel;
/**
* Channel blocking byte write with timeout.
*/
#define chnPutTimeout(ip, b, time) ((ip)->vmt->putt(ip, b, time))
/**
* Channel blocking byte read with timeout.
*/
#define chnGetTimeout(ip, time) ((ip)->vmt->gett(ip, time))
/**
* Channel blocking write with timeout.
*/
#define chnWriteTimeout(ip, bp, n, time) ((ip)->vmt->writet(ip, bp, n, time))
/**
* Channel blocking read with timeout.
*/
#define chnReadTimeout(ip, bp, n, time) ((ip)->vmt->readt(ip, bp, n, time))
/** @} */
O código acima é um pouco difícil de entender, mas este é o preço a pagar, a fim de
implementar conceitos de programação orientada a objetos em uma linguagem que não foi
concebida para apoiá-lo.
O código:
Página - 99 de 109
/*
* memstream.h header.
*/
/*
* MemStream specific data.
*/
#define _memory_stream_data \
_base_sequential_stream_data \
/* Ponteiro para o buffer stream.*/ \
uint8_t *buffer; \
/* Tamanho do stream.*/ \
size_t size; \
/* Fim do stream.*/ \
size_t eos; \
/* Offset lido.*/ \
size_t offset;
/*
* Tabela de métodos virtuais MemStream, nada acrescentado.
*/
struct MemStreamVMT {
_base_sequential_stream_methods
};
/**
* Objeto Memory stream.
*/
typedef struct {
/** @brief Tabela de métodos virtuais.*/
const struct MemStreamVMT *vmt;
_memory_stream_data
} MemoryStream;
#include <string.h>
#include "ch.h"
#include "memstreams.h"
/*
* Inicialização do objeto stream memory.
*
* msp Ponteiro para o objeto MemoryStream para ser inicializado
* buffer Ponteiro do buffer de memória para o memory stream
* size Tamanho total do buffer memory stream
* eos Final do Stream offset
*/
msp->vmt = &vmt;
msp->buffer = buffer;
msp->size = size;
msp->eos = eos;
msp->offset = 0;
}
Note que o módulo núcleo alocador espera dois símbolos globais a serem definidos:
• __heap_base__ é o endereço base da área de RAM não utilizado.
• __heap_end__ é o endereço final da zona de RAM não utilizado.
17.2.1 API
chCoreAlloc() Aloca um bloco de memória do tamanho especificado.
Aloca um bloco de memória do tamanho especificado (variante I-
chCoreAllocI()
Class).
chCoreGetStatusX() Retorna o tamanho da memória restante.
Alocador Can Free Constant Time Variable Size From ISR Thread Safe
Core Allocator Não Sim Sim Sim Sim
Heap Allocator Sim Não Sim Não Sim
Memory Pool
Sim Sim Não Sim Sim
Allocator
C Library
Sim Não Sim Não Não
Allocator
• Can Free indica a capacidade do alocador de blocos de retornar à memória disponível.
• Constant Time é a capacidade de alocar/liberar blocos em um tempo constante, isto é
importante para o determinismo de sistema.
• Variable Size é a capacidade de alocar/liberar blocos de tamanho variável.
• From ISR indica que os serviços alocadores também podem ser chamados de contexto ISR.
• Thread Safe indica que o alocador pode ser usado em um ambiente multi-thread.
A seleção do alocador adequado varia de acordo com os requisitos do seu aplicativo, não há uma
melhor solução universal.
Capítulo - 18 RT Debug
Uma das características mais importantes que um RTOS deve fornecer é o suporte para o
desenvolvimento. A fase de desenvolvimento é onde se espera que os erros de codificação possam
encontrado, note que não estamos falando sobre o teste aqui, estamos falando de erros que devem
ser tratados durante a fase de concepção e implementação.
ChibiOS/RT fornece um conjunto abrangente de opções de depuração destinadas a auxiliar o
desenvolvedor durante a implementação do sistema e fase de depuração. Todas as opções de
Os tempos são medidos usando o contador em tempo real e são os ciclo de relógio preciso.
O plugin ChibiOS/RT Eclipse é capaz de mostrar as estatísticas de tempo de execução do aplicativo
em depuração.
• SV #1. A função chSysDisable() foi chamado do contexto ISR ou de dentro uma zona
crítica.
• SV #2. A função chSysSuspend() foi chamado do contexto ISR ou de dentro uma zona
crítica.
• SV #3. A função chSysEnable() foi chamado do contexto ISR ou de dentro uma zona
crítica.
• SV #4. A função chSysLock() foi chamado do contexto ISR ou de dentro uma zona
crítica. Esta função se destina a inicializar uma zona crítica do contexto do thread. Isso
também pode acontecer quando uma API normal é chamado de dentro de uma zona crítica.
• SV #5. A função chSysUnlock() foi chamado de contexto ISR ou de fora de uma zona
crítica. Esta função se destina a saída de uma zona crítica do contexto do thread.
• SV #6. A função chSysLockFromISR() foi chamado do contexto do thread ou de dentro
uma zona crítica. Esta função se destina a inicializar uma zona crítica do contexto ISR.
• SV #7. A função chSysUnlockFromISR() foi chamado de contexto do thread ou de
fora de uma zona crítica. Esta função se destina a saída de uma zona crítica do contexto ISR.
• SV #8. Macro CH_IRQ_PROLOGUE() está fora de seu lugar. Não foi colocado no início do
ISR ou foi chamado a partir de uma zona crítica.
• SV #9. Macro CH_IRQ_EPROLOGUE() está fora de seu lugar. Não foi colocado no início
do ISR ou foi chamado a partir de uma zona crítica.
• SV #10. Função de I-Class chamada de fora de uma zona crítica.
• SV #11. Função de S-Class chamada de fora de uma zona crítica ou de um ISR.