Sunteți pe pagina 1din 108

ChibiOS/RT 3.

0
The Ultimate Guide

Copyright (C) 2014..2015


Giovanni Di Sirio
todos os direitos reservados
http://www.chibios.org/dokuwiki/doku.php?id=chibios:book:start

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.

Primeiramente gostaria de expressar meu profundo agradecimento ao Giovanni Di Sirio e


a todos os que colaboram, pelo seu trabalho e tempo disponibilizado neste excelente 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.

ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio

Traduzido por Ivan Braga


ivanlbraga@mail.com

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.1 Objetivos livro


O objetivo do livro é apresentar um RTOS real, a partir dos conceitos, a arquitetura e todos
os componentes de software, muitas experiências e exemplos de código serão propostos ao leitor.
O livro permitirá ao leitor conhecer o suficiente sobre ChibiOS/RT para permitir o uso em
projetos complexos do mundo real e a apreciar suas inerentes qualidades.
O conhecimento da linguagem C é necessária, experiência anterior em sistemas embarcados
não é estritamente necessário, mas faria alguns conceitos mais claro para o leitor.
O livro não pretende ser um substituto para a documentação, é aconselhável ter uma cópia
da documentação da API na mão. Detalhes da API não estão documentados no livro, a API é apenas
descrita e casos de uso fornecida. A documentação detalhada está disponível em www.chibios.org,
seção “Documentação”.

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.

Capítulo - 2 Conceitos de sistemas de tempo


real
Antes de começar a brincar com um RTOS é aconselhável adquirir alguns conceitos gerais
sobre sistemas em tempo real e, em seguida, sobre RTOSes para uso embracado. Inicialmente
vamos usar uma terminologia muito genérica, vamos mapear esses termos, em termos mais
específicos no próximo capítulo.

2.1 Divide et impera


Imagine um sistema em tempo real como algo composto por um ou mais, provavelmente,
muitos processos em tempo real. A nossa definição de processo em tempo real é: uma entidade que,
em resposta a eventos externos, produz uma reação num tempo finito.

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.

2.4 O que é um RTOS


Um RTOS é um sistema operacional cujos processos internos são garantidos para ser
compatível com os requisitos de tempo real (hard ou soft). As qualidades fundamentais de um
RTOS são:
• Previsibilidade. É a qualidade de ser previsível no comportamento planejado.
• Determinista. É a qualidade de ser capaz de produzir consistentemente os mesmos
resultados, nas mesmas condições.
RTOS são muitas vezes confundidos como sistemas operacionais “rápidos”. Enquanto a
eficiência é um atributo positivo de um RTOS, eficiência por si só não qualifica um sistema
operacional como RTOS mas poderia separar um bom RTOS de um não tão bom.

2.5 O que não é um RTOS


Um RTOS não é uma varinha mágica, seu sistema não será “realtime” só porque você está
usando um RTOS, o que importa é a sua concepção do sistema. O próprio RTOS é apenas uma
caixa de ferramentas que lhe oferece as ferramentas necessárias para a criação de um sistema em
tempo real, você pode usar as ferramentas corretamente ou de forma errada.

Capítulo - 3 RTOSes embarcados


Nem todos os RTOSes são destinados a aplicações embarcadas, no entanto, programações
embarcadas é onde os RTOSes são usados principalmente. RTOSes para aplicações embarcadas são

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.

3.1 Prioridades e Programação


Na programação de prioridade fixa, tarefas modelo são alocadas para níveis de prioridade
fixas. As prioridades das tarefas são geralmente um intervalo numérico contínuo que vão do mínimo
ao máximo. O intervalo numérico exato não é importante. Normalmente ISRs são considerados
exatamente como tarefas colocadas em um intervalo numérico acima da faixa de prioridades das
tarefas.

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.

3.1.1 Prioridades estáticas e modificáveis


Em RTOSes simples, prioridades geralmente são atribuídos estáticos e não podem ser
alterados em tempo de execução, neste caso falamos de Static Priorities..
Em RTOSes mais complexos, muitas vezes permitem a mudança de prioridade em tempo de
execução, a fim de implementar determinadas estratégias de escalonamento, neste caso falamos de
Modifiable Priorities.
Note que, ao contrário dos sistemas operacionais de propósito geral, as prioridades nunca
mudam automaticamente em função da carga do sistema de outros parâmetros (Dynamic
Priorities), as mudanças de prioridade são ou processualmente comandados ou um resultado de
estratégias de agendamento deterministas, consulte a seção herança de prioridade abaixo para um
exemplo disto.

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.

Outra diferença importante é a possibilidade da existência de uma pilha dedicada para


processar interrupções, uma pilha dedicada significa que não é necessário atribuir espaço para
interrupções na área da pilha de cada tarefa. Pilha dedicada pode ser implementada em SW ou
tratadas diretamente pela CPU, é claro que o último é a solução mais eficiente em termos de ciclos e
latência.
Se o processamento de interrupções é um requisito importante para o seu sistema, então
você deve olhar para uma combinação RTOS/core capaz de lidar eficientemente com interrupções
aninhadas em uma pilha de interrupções dedicadas.

3.4 Tarefas e Threads


As tarefas são as entidades fundamentais em um ambiente RTOS. Uma tarefa pode ser visto
como uma CPU virtual dentro do sistema com a sua própria área de banco de registos e pilha. As
tarefas são agendadas pelo RTOS com base em sua prioridade, tal como descrito antes.

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.

3.4.1 Task States


É bem comum em representar os possíveis estados que uma tarefa pode tomar como uma
máquina de estado. Por exemplo:

O significado dos estados acima são:


• INIT. A tarefa ainda não foi criada.
• READY. A tarefa está pronta para execução, mas não está rodando.
• RUNNING. A tarefa está em execução.
• WAITING. A tarefa está aguardando eventos, a fim de retomar a execução.

Diferentes implementações RTOS podem ter diferentes estados e máquinas de estado, mas o
que está acima é o mais comum.

3.5 Tipos de tarefas


Alguns RTOSes fazem uma clara distinção entre os tipos de tarefas com base em como as

Página - 15 de 109
tarefas são acionados (o evento as acorda).

3.5.1 Tarefas periódicas


Uma tarefa periódica é uma tarefa disparada periodicamente com um intervalo de tempo
fixo. A tarefa permanece em espera até estar pronta para execução, quando o seu temporizador
interno é acionado. A tarefa, em seguida, executa uma breve ação e retorna ao estado de espera.

3.5.2 Tarefas não periódicas


Estes tipos de tarefas são acionadas por um evento externo, por exemplo, um ISR, então elas
não são periódicas. Depois de realizar sua ação programada a tarefa retorna ao estado de espera.

3.5.3 Tarefas contínuas


Tarefas nunca devem ocupar a CPU indefinidamente, uma tarefa na execução de um loop
vazio não permitiria a execução de tarefas no menor nível de prioridade. A regra é que a tarefa deve
esperar por um evento, fazer sua ação programada e, em seguida, voltar para a espera de eventos. Se
a tarefa que não liberar o recurso do CPU, então devem ser colocadas no nível mais baixo de
prioridade no sistema.
A maioria dos RTOSes não fazem uma forte distinção entre tipos de tarefas, todas as tarefas
são iguais, sendo o seu código interno que define o comportamento. Veremos exemplos de tarefas
em vários momentos no capítulo que descreve o kernel ChibiOS/RT.

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.

3.6.1 Operações Atômicas


O problema acima existe porque partimos do princípio que as operações de incremento e
decremento em uma variável na memória possa ser operações não-atômicas, isto é normalmente
verdade em máquinas RISC, mas poderia ser falsa em algumas arquiteturas CISC, qualquer
operação pode ser atômica ou não atômica, dependendo da arquitetura da CPU, em alto nível, não
há nenhuma maneira de se saber se uma operação é atômica, especialmente se o código é gerado
por um compilador.
Todas as operações devem ser consideradas não atômica, mas, em geral, é seguro assumir
que as operações de carga e armazenamento são atômicas se o tamanho dos dados for inferior ou
igual ao do tamanho da máquina. Por exemplo, operações de carga e armazenamento de 8 e 16 bits
podem ser consideradas atômicas nos processadores de 16 bits, já 32 e 64 bits não devem ser
considerados atômicos porque o compilador pode gerar múltiplas operações de load e store. De
qualquer forma, a abordagem mais segura é a de considerar tudo não atômica.
A incapacidade em entender a atomicidade e de implementar uma exclusão mútua
adequada é a receita para o desastre, os erros são geralmente de natureza aleatória e muito difícil
de detectar e depurar. De maneira ideal, o problema deve ser resolvido nas fases de análise e
projeto, definindo cuidadosamente os recursos compartilhados e definir protocolos corretos para
acesso simultâneo.

3.7 Zonas críticas


Zonas críticas são provavelmente a maneira mais simples e comum para fazer uma
sequência não-atômica de código se comportar atomicamente. Nos RTOSes simples em arquiteturas

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 Uso Adequado de zonas críticas


Zonas críticas são a forma mais leve e eficiente para implementar exclusão mútua, vamos ver as
vantagens e as restrições.

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.

3.7.1.2 Restrições potenciais


• Pode aumentar a figura jitter no sistema.
• Pode fazer o tempo de resposta de pior caso ainda pior se as zonas protegidas são
muito longas.
• Alguns RTOSes não permitem o uso de API de dentro das zonas críticas.
• Alguns RTOSes não têm uma abstração para zonas críticas.

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ó.

3.8 Exclusão mútua


Um dos problemas mais comuns em sistemas com múltiplas tarefas é sincronizar o acesso a
recursos compartilhados. As seções críticas são uma solução fácil para este problema que é
conhecido com a uma definição geral de exclusão mútua.
A exclusão mútua pode ser implementada usando uma variedade de mecanismos
normalmente presentes em RTOSes como por exemplo, e não se limitando a: Counting
Semaphores, Binary Semaphores, Mutexes, Priority Escalation, Messages, Critical Zones. Cada
mecanismo tem suas vantagens e desvantagens, dos mecanismos mencionados Mutexes são os
únicos especificamente concebidos para resolver o problema de exclusão mútua da maneira mais
geral. Vamos ver maneiras de implementar exclusões mútuas quando os vários mecanismos forem
discutidos.

3.9 Inversão de prioridade


Um efeito colateral bem conhecido ao usar Exclusão Mútua é a possibilidade de desencadear
o problema inversão de prioridade. Inversão de prioridade pode causar com que uma tarefa com
uma certa prioridade possa atrasar tarefas com maior prioridade quando um recurso é protegido com
exclusão mútua e acessado por tarefas em vários níveis de prioridade.
Este é um exemplo:
Time | | | | | | | | | | | | | | |
0 S+++++AL++++------------------------------------++++++++++++++++++AU----------++++++G
1 ..................S+++++------++++++++++++++++++G
2 ........................S+++++AL..................................++++++AU++++G
3 ............S+++++G
A ******************************************************************

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

No exemplo acima, isto é o que acontece:


1. A tarefa em baixa prioridade (0) inicia a execução e leva o recurso A usando um
mutex.
2. A tarefa prioridade muito alta (3) começa a executar e termina, ele não é afetado por
(0).
3. A tarefa de prioridade média (1) começa a executar.

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.

3.9.1 Soluções possíveis


Existem vários métodos que podem ser utilizados a fim de resolver o problema de inversão
de prioridade:
• Evitar zonas de exclusão mútua ou partilha de recursos, em geral, nem sempre é
possível.
• Reordenar as prioridades, a fim de ter todas as tarefas que acessam um recurso
compartilhado em níveis prioridades próximas, nem sempre é possível.
• Implementar a Exclusão Mútua como uma seção crítica, onde preempção é proibido,
nem sempre é possível.
• Usar algoritmos de suavização como teto Prioridade ou herança de prioridade.

3.9.2 Teto prioridade


Imagine atribuir a um Mutex um nível de prioridade, essa prioridade seria maior que a
prioridade de todas as tarefas que tentar adquiri-lo. Tarefas de bloqueio Mutex adquiririam
temporariamente a sua prioridade.
Isto é como o diagrama de tempo muda neste cenário:
Time | | | | | | | | | | | | | | |
0 S+++++AL4+++++++++++++++++++++AU0---------------------------------------------++++++G
1 ..................S-----------------------------------++++++++++++++++++++++++G
2 ........................S-----------++++++AL4+++AU3+++G
3 ............S-----------------++++++G
A ^***********************^...........^*****^

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.

3.9.3 Herança prioridade


A herança de prioridade é um algoritmo onde as tarefas que ocupam um mutex são dadas
temporariamente a mesma prioridade da tarefa de maior prioridade entre todas as tarefas que
requerem o mesmo mutex.
Isto é como o diagrama de temporização acima muda neste cenário:
Time | | | | | | | | | | | | | | |
0 S+++++AL++++------------------3+++++++++++++++++AU0---------------------------++++++G
1 ..................S+++++------------------------------------++++++++++++++++++G
2 ........................S+++++AL----------------++++++AU++++G
3 ...........,S+++++G
A ************************^*****************^*****

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.

Capítulo - 4 Arquitetura Geral do ChibiOS


Neste capítulo vamos começar descrevendo especificamente o ChibiOS em seus detalhes de
alto nível.
A primeira coisa que deve ser descrita é que ChibiOS não se refere a apenas um escalonador
RTOS mas a um conjunto de componentes embutidos parte de uma arquitetura geral definindo um
sistema típico embutido. Neste livro abordaremos:
• ChibiOS/RT, o scheduler RTOS. RT é um RTOS de muito alto desempenho com um
conjunto completo de recursos e dimensões reduzidas.
• ChibiOS/HAL, Hardware Abstraction Layer incluindo drivers abstratos para os

Página - 21 de 109
periféricos mais comuns.

4.1 Requisitos do sistema


A primeira coisa a explicar são os sistemas de destino para aplicação ChibiOS. ChibiOS é
feito para ser usado em microcontroladores de 8, 16 e 32 bits a partir de 2 KB de RAM e 16KB de
flash. Ele pode ser portado para qualquer arquitetura de CPU, desde que:
• Exista um ponteiro de pilha real.
• Exista suporte para um compilador C99.

Uma arquitetura de CPU limpa geralmente facilita o processo de portabilidade.

4.2 Modelo de Aplicação


Agora precisamos definir o tipo de aplicação que se pode criar com ChibiOS, o modelo de
aplicativo é: Aplicação simples com vários threads. Isso significa:
• O ambiente em tempo de execução é de confiança, o aplicativo não precisa se
defender de si mesmo.
• Vários threads fazem parte do aplicativo e compartilham o espaço de endereço. Não
há nenhuma proteção entre thread e thread e sem virtualização.
• Aplicação e sistema operacional estão ligados entre si em uma única imagem de
memória, um único programa.
• Não existe o conceito de “carga de aplicativos”, exceto se um bootloader for usado
para cuidar disso.

4.3 A Imagem no geral


O sistema ChibiOS é fortemente modular, a estrutura é sempre a mesma, independentemente
da arquitetura alvo:

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.1 Código de inicialização


É o código executado após o reset. O código de inicialização é responsável por:
1. Inicialização do Core.
2. Inicialização de Pilhas.
3. Inicialização C Runtime.
4. Chamar a função main().

No ChibiOS o código de inicialização é fornecido com o sistema operacional e está


localizado sob ./os/common/ports para as várias arquiteturas e compiladores suportados,
arquivos e tudo mais que é necessário para a inicialização do sistema também é fornecido.

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.

4.4 Detalhes da Abstração


Um ponto importante sobre ter um sistema operacional é a abstração dos detalhes internos, a
fim de manter o código da aplicação geral tão portátil quanto possível. O código escrito sobre
abstrações e bem desenhado aumenta a portabilidade das aplicações de microcontroladores mais
novos, mesmo de diferentes fornecedores.
A portabilidade do código é difícil em software embarcado, há uma incrível série de detalhes
que são específicos ao compilador, a arquitetura e específicos aos microcontroladores. Uma lista
incompleta é:
• Construções de C não-padrão exigido, a fim de fazer coisas simples como escrever
ISRs.

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.

Capítulo - 5 Introdução ao Kernel RT


O núcleo ChibiOS RTOS é chamado RT, o nome ChibiOS/RT refere-se ao componente
RTOS ChibiOS. O kernel RT é vagamente inspirado em POSIX, a maioria dos mecanismos de
threading POSIX e API está presente no ChibiOS mesmo se não houver uma compatibilidade estrita
a nível API, há uma equivalência ao nível da funcionalidade.
O kernel RT é projetado em torno de alguns conceitos simples:
1. Deve ser rápido, eficiência na execução é o principal requisito.

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.

5.1 Convenções de Codificação


Um aspecto muito importante são as convenções de codificação consistentemente adotados
no projeto ChibiOS.

5.1.1 Estilo do Código


O estilo do código ChibiOS / RT é o estilo padrão K&R com algumas mudanças:
• Tabs são proibidos.
• São proibidos caracteres não UTF-8.
• Comentários de estilo C++ são proibidos.
• Recuo é de 2 espaços.
• Várias linhas vazias são proibidas.
• Inserção de linhas em branco é regulado.
• O código é escrito em “blocos”, os blocos são compostos por:
• Uma linha em branco.
• Um comentário descrevendo o bloco. O comentário pode estar em uma única
ou várias linhas.
• Uma ou mais linhas de código.
• Comentários na a mesma linha de declarações são permitidos, mas não
encorajados.
• Os arquivos são todos derivados de modelos.
• Sem espaços no final das linhas.

Uma especificação mais detalhada para o estilo de codificação está disponível na


documentação web.

5.1.2 Convenções de Nomenclatura


Há um forte foco em convenções de nomenclatura em ChibiOS, a compreensão das regras
de nomenclatura permite compreender a finalidade e o contexto de um objeto de código, basta olhar
para o nome.

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()

Observe que os subsistemas em ChibiOS/RT estão associados a tipos de objeto, por


exemplo, o subsistema “Sem” é referente a objetos semaphore_t. Por exemplo:

chSemObjectInit(&sem, 1)

Estamos realizando uma inicialização em um objeto semaphore_t. Os inicializadores


são funções exclusivas, porque podem ser chamados de qualquer contexto, mesmo antes da
inicialização do kernel. Isso significa que você pode inicializar um objeto RT global antes que RT
seja ativado, chamando chSysInit().

5.1.2.2 Funções internas


Funções não-estáticos que não são destinadas a ser funções da API deve ser escrita
inteiramente em letras minúsculas e precedido por um underline.

5.1.2.3 Variáveis, Campos e Estruturas


Os nomes devem ser totalmente escrito em letras minúsculas, underline é usado como

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:

Todos os serviços do kernel são construídos em torno do módulo escalonador “scheduler”. O


escalonador exporta uma API de baixo nível que permite construir praticamente qualquer tipo de
sincronização primitiva, outros módulos são construídos usando os serviços do escalonador e não
existem interações. Há poucas exceções:
• Mailboxes e semáforos binários são construídos usando semáforos contadores.
• Variáveis de condição são construídas para trabalhar com exclusões mútuas, a fim de formar
construtores Monitor.
Outros módulos fundamentais presentes no kernel são, o módulo de threading, o módulo
Timers Virtual e os módulos opcionais de gerenciamento de memória que serão descritos
separadamente nos capítulos seguintes.

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.

5.4 Classes da API


Como mencionado antes, as APIs ChibiOS/RT são agrupadas em várias classes,
caracterizadas por uma letra do sufixo. A classe da função define o estado do sistema onde a função
pode ser usada com segurança.
Ao contrário de outros RTOSes não há uma “matriz de compatibilidade” para as funções do
sistema. A classe de funções define o seu comportamento e o contexto compatível para APIs.

5.4.1 Funções normais


Funções normais não tem nenhum sufixo e só podem ser chamadas de estado "Thread" a
menos que a documentação da função estabeleça novas restrições ou define um contexto especial.

5.4.2 Funções S-Class


Funções com sufixo “S” só pode ser invocado a partir do estado “S-Locked”, isso significa
que esta classe de funções são destinadas a serem chamadas em uma zona crítica à nível de thread.

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.

5.4.4 Funções X-Class


Esta classe de funções não tem nenhum requisito especial e podem ser chamado de qualquer
contexto onde as funções da API podem ser chamadas: “Thread”, “S-Locked” and “I-Locked”.

5.4.5 Funções especiais


Funções especiais não têm um sufixo específico, mas tem requisitos especiais de execução
descritos em sua documentação.

5.4.6 Inicializadores de objeto


Estes tipos de funções são destinadas para inicialização de objetos e podem ser usadas em
qualquer contexto, mesmo antes que o kernel seja inicializado. Inicialização de objetos em si são
“passivos” até que referenciado por alguma outra função. Note que a maioria dos objetos do kernel
também tem inicializadores estáticos, macros que alocam objetos e os inicializa usando uma
inicialização de variável estática.

5.5 Thread Working Areas


No ChibiOS/RT, threads ocupam uma única região continua da memória chamada Thread
Working Area. Áreas de trabalho podem ser estaticamente ou dinamicamente alocada e sempre
estão alinhadas com o mesmo alinhamento necessário para ponteiros de pilha, por exemplo, para
dispositivos ARM Cortex-M que um alinhamento de 8 bytes é aplicada a fim de respeitar a
especificação EABI.
As estruturas de dados de thread são colocadas na memória como segue:

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.

5.5.1 Declarando Áreas de Trabalho


As áreas de trabalho são declaradas usando uma macro especial:
THD_WORKING_AREA(wa_name, 128);

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.

5.6 Thread States


Durante o seu ciclo de vida threads passam por vários estados, a máquina de estado é
regulada pela API e eventos no kernel:

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.

5.7 Funções Thread


No ChibiOS/RT o código thread é exatamente equivalente a uma função C com uma
declaração especial. Vários thread, cada um com a sua área de trabalho, podem executar
simultaneamente a mesma função.
Uma coisa deve ficar muito claro, cada thread tem sua própria pilha, isto significa que:
1. Variáveis automáticas C são sempre thread-private, a menos que passada por
referência para outros threads.
2. Variáveis estáticas C declaradas dentro do corpo da função são particulares à
função, mas compartilhada entre todos os threads executando essa função.
3. Variáveis estáticas C declaradas fora do corpo da função são privados para o
módulo C, mas acessível por threads que acessam as funções do módulo.
4. As variáveis globais C são sempre potencialmente acessíveis a todos os threads no
sistema.

Página - 33 de 109
Cada variável cai necessariamente em um dos casos acima.

5.7.1 Declarando funções Thread


Esta é uma típica declaração de um thread:
/* Variáveis estáticas compartilhadas entre os threads. */
uint32_t counter;
static unsigned abc[16];

/* Área de trabalho para o thread.*/


static THD_WORKING_AREA(waCounter, 128);

/* 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.

Capítulo - 6 Camada Sistema RT


A camada sistema é a parte mais fundamental do kernel RT, encontra-se um pouco acima das
camadas do port e oferece uma série de serviços importantes:
• Inicialização.
• Finalização anormal.
• Tratamento de interrupções.

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 ();
.
.
.
}

Note que a função main() não retorna, ou o sistema pararia.

6.2 Finalização anormal


Este serviço lida com a parada de pânico do sistema, uma razão pode ser fornecida pela
inspeção post-mortem ou registro de log:

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 Tratamneto de Interrupções


Este serviço oferece abstração sobre o ISR e funções de manipulação de IRQ.

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 ();
}

6.3.2.2 Escrever um ISR Rápido


Interrupções que não foram feitas para interagir com o Kernel podem ser escritas usando
esta forma mais leve. Nenhuma sobrecarga relacionada com o OS é adicionada ao caminho do
código ISR:

Página - 36 de 109
CH_FAST_IRQ_HANDLER (myISR) {

/* Código ISR rápido. */


...;

6.4 Seções críticas


Este serviço lida com seções críticas em vários sabores:
• Seções críticas não reentrantes à nível de thread.
• Seções críticas não reentrantes à nível ISR.
• Seções críticas “universais” reentrantes.

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. */
...;

/* Entra em uma seção crítica. */


chSysLock ();

/* Código protegido. */
...;

/* Deixa a seção crítica. */


chSysUnlock ();

/* Mais código do Thread. */


...;
}

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. */
...;

/* Entra em uma seção crítica. */


chSysLockFromISR ();

/* Código protegido. */
...;

/* Deixa da seção crítica. */


chSysUnlockFromISR ();

/* Mais código ISR. */


...;

CH_IRQ_EPILOGUE ();
}

6.4.2.3 Seções críticas Reentrante


/*
* Esta função pode ser chamada de dentro ou de fora de seções críticas
* tanto no contexto thread ou ISR.
*/
void myFunc (void) {

/* Código protegido ou desprotegido. */


...;

/* Entrar condicionalmente uma secção crítica. */


syssts_t pts = chSysGetStatusAndLockX ();

/* Código protegido. */
...;

/* Deixando Condicionalmente a seção crítica. */


chSysRestoreStatusX (STS);

/* Código protegido ou desprotegido. */


...;
}

6.5 Gerenciamento de energia


O kernel não lida com o gerenciamento de energia diretamente, no entanto, existem
características destinadas a permitir a implementação de qualquer tipo de gerenciamento de energia
em resposta das ações importantes do kernel.
Outra característica importante relacionada com a energia, é o modo Tickless que será
discutido no capítulo Timers Virtual.

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 Contador Realtime


O contador em tempo real é um recurso opcional do kernel RT. Está presente apenas se a
arquitetura base oferece suporte ao recurso necessário: um driver contador incremental muito rápido
pelo relógio do sistema.
O contador em tempo real é utilizado para:
• Atrasos de tempo exato.
• Loops curtos com limite tempo.

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;

/* Espere pelo menos 12US antes da ADC pode ser usado.*/


chSysPolledDelayX(US2RTC(SYSCLK, 12));

/* Inicia a conversão.*/
ADC->CR1 |= ADC_CR_START;
}

6.6.2.2 Loop com Timeout


Outra necessidade comum é ter uma forma de escape para loops de software:
void wait_conversion (void) {

* Espera o ADC concluir a operação, após 10ms assume-se uma falha.*/


rtcnt_t start = chSysGetRealtimeCounterX();
rtcnt_t end = start + MS2RTC(10);
do {
if ((ADC->SR & ADC_SR_DONE) != 0)
return;
} while (chSysIsCounterWithinX(chSysGetRealtimeCounterX(), start, end));

/* Isto é inesperado, provavelmente uma falha no ADC. */


chSysHalt ("falha ADC");
}

Capítulo - 7 RT Timers virtuais


Timers virtuais são uma característica única no ChibiOS/RT. É um sistema de software
capaz de fornecer um número “ilimitado” de temporizadores one-shot com a mesma resolução do
ticks do sistema. Timers virtuais também são utilizados internamente, a fim de implementar o
recurso de limite tempo presente em muitas funções da API.
Os serviços do módulo de threading são:
• System Time.
• Utilitários de conversão de tempo.
• Utilitários de intervalos de tempo.
• Temporizadores One Shot.
• Modo de Tickless.

Página - 40 de 109
7.1 Configurações globais
Timers virtuais são afetados pelas seguintes configurações globais:

A resolução em bits do tipo systime_t do system time, que


CH_CFG_ST_RESOLUTION pode ser de 16 ou 32 bits. O system time é um contador global
incrementado um por temporizador HW dedicado.
Frequência system time. Define a frequência de trabalho do
CH_CFG_ST_FREQUENCY
system time dedicado.
Se zero, em seguida, o kernel usa o modo de ticks clássico, em
alternativa, o novo modo tickless é ativado. Valores maiores ou
CH_CFG_ST_TIMEDELTA
iguais a dois define o número mínimo de ticks por intervalos, o
valor um (01) é proibido.

7.2 Time System


7.2.1 API
chVTGetSystemTime() Retorna o system time.
Retorna o system time (variante X-Class). Esta função pode
ser chamada de qualquer contexto, mas a sua atomicidade
chVTGetSystemTimeX()
não é garantida em arquiteturas cuja word “tamanho” é
menor do que o tamanho de systime_t.
Retorna o tempo passado desde o tempo especificado no
chVTTimeElapsedSinceX() sistema de ticks. É equivalente a:
(chVTGetSystemTimeX() - time).
Retorna true se o tempo especificado está dentro do
chVTIsTimeWithinX()
intervalo especificado.
Retorna true se o system time está dentro do intervalo
chVTIsSystemTimeWithin()
especificado.
Retorna true se o system time está dentro do intervalo
chVTIsSystemTimeWithinX()
especificado (variante-Class X).

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();

7.2.2.2 Loop com Tempo limitado


Executa o loop e executa uma atividade interna até que uma condição seja atendida ou o
período de tempo determinado expire.
/* O loop é executado por 200 milissegundos ou até que uma condição interna
seja atendida, o que ocorrer primeiro. */
systime_t start = chVTGetSystemTime();
systime_t end = start + MS2ST(200);
while (chVTIsSystemTimeWithin(start, end)) {

Página - 41 de 109
...;

if (condition)
break;

...;
}

7.3 Utilitários de conversão de tempo


7.3.1 API
Segundos para system ticks. O resultado é arredondado para cima, para o
S2ST()
próximo limite de ticks.
Milissegundos para system ticks. O resultado é arredondado para cima, para o
MS2ST()
próximo limite de ticks.
Microssegundos para system ticks. O resultado é arredondado para cima, para
US2ST()
o próximo limite de ticks.
System ticks para segundos. O resultado é arredondado para o próximo
ST2S()
segundo limite.
System ticks para milissegundos. O resultado é arredondado para o próximo
ST2MS()
milissegundo limite.
System ticks para microssegundos. O resultado é arredondado para o próximo
ST2US()
microssegundo limite.

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());

7.3.2.2 Timeout em milissegundos


Muitas funções podem ter uma especificação do timeout em system ticks, também é possível
especificar o timeout usando unidades de tempo normais.
/* À espera de um evento de rede com um timeout de 200 milissegundos. */
chSysLock();
msg_t msg = chThdEnqueueTimeoutS(&rx_frames_queue,MS2ST(200));
if (msg == MSG_TIMEOUT) {
/* Timeout management.*/
...;
}
chSysUnlock();

7.4 Timers One Shot


Timers one-shot são timers virtuais que podem ser iniciados, parados repentinamente ou

Página - 42 de 109
acionar um retorno de chamada depois que expirou o tempo programado. O timer máquina de
estado é:

7.4.1 Callbacks “Retornos de chamada”


Timers callbacks são sempre chamados do contexto ISR, isto significa que se API pode ser
utilizada por um timers callback, ela está sujeita às mesmas restrições aplicáveis aos ISRs. Por re-
armar um timer virtual a partir de uma callback também é possível implementar temporizadores
periódicos ou não-periódicos.

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 ();

/* Inicialização do temporizador LED. */


chVTObjectInit(&led_vt);

/* Piscando o LED quando a tecla for pressionada. */

while (true) {
if (key_pressed())
blink();

chThdSleepMilliseconds(50);
}
}

7.4.3.2 LED pisca-pisca Contínuo


Um timer virtual é usado em um pisca-pisca de LED dedicado. O pisca-pisca pode ser
iniciado e interrompido com um único comando.
static virtual_timer_t led_vt;

/*
* 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();

/* Inicialização temporizador LED*/


chVTObjectInit(&led_vt);

/* Inicia o pisca-pisca.*/
chVTSet(&led_vt, MS2ST(500), led_cb, NULL);

...;

/* Para o pisca-pisca.*/
chVTReset(&led_vt);
LED_off();
}

7.5 Modo Tickless


kernels RTOS comuns são acionados por uma interrupção periódica chamada system tick,
que conduz os mecanismos internos relacionados ao tempo. No ChibiOS/RT o system tick é tratado
de forma eficiente, no entanto, ainda pode limitar o sistema de várias maneiras:
• Uso da CPU é aumentado pelo uso frequente do IRQ, o problema fica pior em frequências
mais altas.
• O system tick limita a resolução do virtual timer e do system time porque uma frequência
muito alta afetaria negativamente o desempenho do sistema.
• O jitter do sistema é agravada pelas contínuas interrupções.
• Interrupções frequentes podem impedir o sistema entrar em modos de sono mais profundos.
Isso afeta negativamente o uso de energia do sistema.

ChibiOS/RT implementa um único modo tickless no seu subsistema de timers virtuais.


Quando o modo de tickless é ativado o temporizador do sistema não gerará mais interrupções
periódicas, mas é programado para gerar uma interrupção quando o sistema tem alguma atividade
programada para executar, geralmente o término de um virtual timer.
Esta abordagem tem vários aspectos positivos:
• Menor consumo de energia graças à utilização de modos de sono mais profundo não
seja continuamente interrompido pelo system tick.
• Melhora geral no jitter no atendimento ISR.
• Maior resolução para o system time e o virtual timers porque a freqüência de timer
não é mais restrita.
Há algumas coisas a considerar:

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.1 Configurações globais


O escalonador é afetado pelas seguintes configurações globais:

CH_CFG_TIME_QUANTUM Fatia de tempo, em system ticks, do intervalo round-robin.

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:

Este, diagrama da classe de sistema mostra os relacionamentos mais importantes no


ChibiOS/RT:
• Uma lista de threads prontos chamada ready list.
• Uma lista de threads ativos chamada registry.
• Uma referência para o thread em execução chamado current.
• Uma lista de timers virtuais armados.
• Informação relacionada com a depuração para uso de depurador.
• Dados de calibração de medição de tempo.
• Estatísticas de tempo de execução do kernel.

8.4 A Ready List


A Ready List é provavelmente a estrutura de dados mais importante no ChibiOS/RT. É uma
lista bidirecional fechada ordenada de threads que representam os threads aptos para execução. A
lista está organizada da seguinte forma:

Página - 47 de 109
Note que o thread ativo não é parte da lista, que é apontado por um ponteiro global.

8.5 O Thread Idle


O sistema sempre executa um thread especial chamado “thread Idle”. Este thread tem o nível
de prioridade mais baixo no sistema 01 (um), e é executado somente quando nenhum outro thread
está pronto para execução.
O objetivo do thread idle é de definir o que o sistema faz quando não há nada a fazer, geralmente, é
um loop vazio ou um loop contendo uma única instrução, arquitetura-dependente,“Wait for
Interrupt” para a CPU até que uma interrupção seja detectada. Parando o CPU durante os períodos
ociosos pode reduzir o consumo de energia do sistema.
Um detalhe importante é que o thread idle só pode estar nos estados READY ou CURRENT,
ele não tem permissão para entrar em qualquer um dos estados de sono nem de terminar ”parar”. O
thread idle é criado automaticamente na inicialização do sistema e dura até que o sistema seja
desligado.

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) {

/* Thread body code.*/


...;
}

9.2 Ciclo da vida


Este serviço processa a criação e extinção dos threads.

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();

/* Atraso de 250 milissegundos.*/


chThdSleepMilliseconds(250);

/* Contando o número de piscadas.*/


i++;
}

/* Retornando o número de iterações.*/


chThdExit((msg_t)i);
}

/*
* 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.*/
...;

/* O pisca Led é criado.*/


thread_t *tp = chThdCreateStatic(waBlinker, sizeof(waBlinker),
NORMALPRIO + 1, Blinker, NULL);

Página - 50 de 109
/* Executar operações enquanto o LED estiver piscando.*/
...;

/* Parar o thread pisca-pisca e recupera o número de vezes


que o led piscou.*/

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.

9.3.2.2 Intervalos Fixos #2


Esta solução não sofre a limitação descrita no exemplo anterior, no entanto, é um pouco mais
complexo para entender.
static THD_WORKING_AREA(waBlinker, 128);
static THD_FUNCTION(Blinker, arg) {

systime_t time = chVTGetSystemTime(); // Current system time.


while (TRUE) {
time += MS2ST(500); // From Milliseconds to Tick conversion.
LED_on();
chThdSleepUntil(time);

time += MS2ST(500); // From Milliseconds to Tick conversion.


LED_off();
chThdSleepUntil(time);
}
}

Neste exemplo, o tempo de execução de LED_on() e LED_off() não mais importa se o


tempo é menor do que 500 MS, uma margem muito maior do que o 1mS do exemplo anterior.
No caso do tempo ser ultrapassado o thread dormiria por um tempo muito longo, porque ele
teria que esperar para o contador de tempo de sistema voltar a zero e atingir o valor de tempo
especificado. Considerando-se que o contador é normalmente de 32 bits de largura isso significa
que o thread seria acordado depois de um tempo muito longo, estaria praticamente bloqueado.
Este método é válido, desde que saibamos que o tempo de execução entre as chamadas para
chThdSleepUntil() é estritamente menor que o intervalo obrigatório. Este método é válido
para sistema em tempo real complicado, onde os prazos nunca devem ser violado.

9.3.2.3 Intervalos Fixos # 3


O método a seguir é tolerante a violação ocasional dos prazos calculados, é adequada para
casos de uso suave em tempo real porque o intervalo ocasionalmente pode exceder o intervalo
projetado, mas é capaz de se recuperar.
static THD_WORKING_AREA(waBlinker, 128);

Página - 52 de 109
static THD_FUNCTION(Blinker, arg) {

systime_t prev = chVTGetSystemTime(); // Current system time.


while (true) {
LED_on();
prev = chThdSleepUntilWindowed(prev, prev + MS2ST(500));

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 Threads Referências


Uma necessidade comum é ter um thread suspenso e à espera de um evento síncrono.
ChibiOS/RT oferece um mecanismo leve de baixo nível chamado de Thread References.
Basicamente um thread reference é um ponteiro que pode ser NULL ou apontar para um thread em
espera.
Existem duas operações possíveis: suspend o que torna um ponto de referência para um
thread e resume que acorda um thread em espera redefinir a referência para NULL O tipo de variável
de referência thread é thread_reference_t. Note que, apenas um thread pode suspender
em uma referência, tentando suspender em uma referência não-NULL será um erro pego por uma
asserção.
Uma característica adicional interessante é que é possível passar uma mensagem entre a
entidade chamadando resume e o thread que está suspend.

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();

/* Se houver dados disponíveis no UART RX FIFO. */


if ((UART->SR & UART_SR_DATA_AVAILABLE) != 0) {

/* Entra no estado I-Locked e retoma o thread, se suspensa. */


chSysLockFromISR();
chThdResumeI(&uart_thread_ref, MSG_OK);
chSysUnlockFromISR();

/* Reinicia a fonte de interrupção. */


UART -> SR & = ~ UART_SR_DATA_AVAILABLE;
}

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) {

/* Depois de sair da zona crítica mais IRQs podem ocorrer, mas


o thread não está suspensa então a operação resume não faz
nada porque a referência de uart_thread_ref é NULL
*/
chSysUnlock();
process_data(UART->DR);
chSysLock();
}

/* O FIFO agora está vazia, à espera de mais dados ou uma interrupção


* no stream de dados RX de pelo menos 500 ms.
*/
msg_t msg = chThdSuspendTimeoutS(&uart_thread_ref, MS2ST(500));
if (msg == MSG_TIMEOUT)
handle_comm_timeout();
}
}

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();

/* Se a rede cair todos os threads aguardando serão notificados.*/


if ((NET->SR & NET_SR_ERROR) != 0) {
/* Entrando no estado I-Locked e acordando todos os threads com uma
menssagem especial.*/
chSysLockFromISR();
chThdDequeueAllI(&rx_frames_queue, MSG_RESET);
chSysUnlockFromISR();

/* Reiniciando a fonte de interrupção.*/


NET->SR &= ~NET_SR_ERROR;
}

/* Se não houver um frame disponível na interface de rede.*/


if ((NET->SR & NET_SR_FRAME_AVAILABLE) != 0) {

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 ();

/* Obtendo o ponteiro para o frame buffer e o passa como mensagem.*/


void *frame_ptr = net_get_frame_buffer();
if (chThdQueueIsEmptyI(&rx_frames_queue) {
/* Frame perdido, nenhuma thread pronta para atendê-lo agora.*/
process_dropped_frame(frame_ptr);
net_return_frame_buffer(frame_ptr);
}
else
chThdDequeueNextI(&rx_frames_queue, (msg_t)frame_ptr);

chSysUnlockFromISR();

/* Reiniciando a fonte de interrupção.*/


NET->SR &= ~NET_SR_FRAME_AVAILABLE;
}

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);

static THD_FUNCTION(ProcessFrameThread, arg) {

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();

/* Iniciando os threads processamento dos frames.*/


chThdCreateStatic(waProcessFrameThread1, sizeof(waProcessFrameThread1),
NORMALPRIO + 1, ProcessFrameThread, NULL);
chThdCreateStatic(waProcessFrameThread2, sizeof(waProcessFrameThread2),
NORMALPRIO + 1, ProcessFrameThread, NULL);
chThdCreateStatic(waProcessFrameThread3, sizeof(waProcessFrameThread3),
NORMALPRIO + 1, ProcessFrameThread, NULL);
chThdCreateStatic(waProcessFrameThread4, sizeof(waProcessFrameThread4),
NORMALPRIO + 1, ProcessFrameThread, NULL);

/* Inicialização do subsistema de rede, é iniciado o processamento.*/


net_init();

/* Continuing.*/
...;
}

9.6 Thread Time


O tempo da CPU usado por um thread é acumulado em um contador interno. Note que o
tempo é estimado e arredondado, porque o contador é incrementado quando ocorre a interrupção
system tick. Este recurso é habilitado, opcionalmente, usando o
CH_DBG_THREADS_PROFILING.
Um método melhor de medição está disponível através do módulo de estatísticas que utiliza
o contador em tempo real para realizar medições precisas de ciclo.

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;

/* Verificar se há violações de prioridade, utilizando uma asserção.*/


chDbgAssert(chThdGetPriorityX() < MY_RESOURCE_PRIORITY,
"resource priority violation");

/* Aumenta a prioridade para acessar o recurso com segurança.*/


oldprio = chThdSetPriority(MY_RESOURCE_PRIORITY);

/* Acessa com segurança o recurso durante a execução em seu nível de


prioridade..*/
...;

/* Retorna a antiga prioridade.*/


chThdSetPriority(oldprio);
}

9.8 Round Robin


Normalmente o escalonamento round robin só é útil quando existem vários threads fazendo
uso intensivo da CPU colocados ao mesmo nível de prioridade. Escalonamento round robin pode
funcionar de duas maneiras distintas:
1. Preemptive Round Robin. Este modo é ativado definindo
CH_CFG_TIME_QUANTUM para um valor maior do que zero. Neste modo, o thread
usando a CPU é interrompido pelos seus pares após o seu slot de tempo ter sido
usado.

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);

static THD_FUNCTION(ProcessThread, arg) {

while (true) {

/* Fazendo algum processamento pesado na CPU.*/


...;

/* Voluntariamente passa o controle para o próximo thread.*/


chThdYield();
}
}

/*
* 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();

/* Starting the frames processor threads.*/


chThdCreateStatic(waProcessThread1, sizeof(waProcessThread1), NORMALPRIO-10,
ProcessThread, NULL);
chThdCreateStatic(waProcessThread2, sizeof(waProcessThread2), NORMALPRIO-10,
ProcessThread, NULL);

Página - 59 de 109
chThdCreateStatic(waProcessThread3, sizeof(waProcessThread3), NORMALPRIO-10,
ProcessThread, NULL);
chThdCreateStatic(waProcessThread4, sizeof(waProcessThread4), NORMALPRIO-10,
ProcessThread, NULL);

/* Continuing on top of the round robin bunch.*/


...;
}

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.

O usuário pode utilizar os dois tipos sem restrições.

10.1 Configurações globais


Semáforos são afetados pelas seguintes configurações globais:

Essa opção habilita a API semáforos no kernel


CH_CFG_USE_SEMAPHORES
(ambos semáforos de contagem e binários).
Se habilitado, então threads estão na fila por
CH_CFG_USE_SEMAPHORES_PRIORITY prioridade, em vez de em ordem FIFO em
semáforos. O padrão é desativado.

10.2 Semáforos de contagem


Os semáforos de contagem foram formalizadas pela primeira vez pelo cientista da
computação holandês Edsger Dijkstra em 1965. Semáforos de contagem tem uma variável de
contador interno assinado, o valor da variável é o estado do interno do semáforo. O significado do
contador é:
• N <0. O semáforo está ocupado e há-N threads na fila.
• N == 0. O semáforo está ocupado, mas não existem threads na fila.
• N> 0. O semáforo não está ocupado e pode ser usado N vezes.

Basicamente, o contador de um semáforo pode proteger um recurso disponível em medida


finita.
Semáforos de contagem são regulados pelo seguinte diagrama de estado:

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

static semaphore_t dmasem;

/*
* Aloca um canal DMA. Usuários devem utilizar o canal e
* em seguida, liberá-lo em um tempo determinado.
*/
channel_t dmaAllocateChannel(void) {

msg_t msg = chSemWaitTimeout(&dmasem, MS2ST(500));


if (msg == MSG_TIMEOUT)
panic("unreleased DMA channels";

/* 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) {

/* Devolvendo um canal para o pool.*/


dma_return_channel(channel);

/* Então sinaliza para o semáforo.*/


chSemSignal(&dmasem);
}

/*
* 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();

/* Inicializa o semáforo DMA para permitir até DMA_NUM_CHANNELS


alocações.*/

chSemObjectInit(&dmasem, DMA_CHANNELS);

/* Continuing.*/
...;
}

10.3 Semáforos binários


No ChibiOS/RT semáforos binários são construídos sobre semáforos de contagem usando
um inline code muito eficiente. O semáforo de contador binário não tem permissão para contar
além de um, portanto, o semáforo tem apenas dois estados possíveis:
• Taken, quando o contador tem um valor de zero ou inferior a zero. Um número negativo
representam o número de threads na fila do semáforo binário.
• Not Taken, quando seu contador tem um valor de um.

Semáforos binários são regulados pelo seguinte diagrama de estado:

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

10.3.3.1 Thread Servindo um IRQ


/*
* Semáforo sincronização.
*/
binary_semaphore_t uart_bsem;

/*
* ISR servindo a interrupção UART RX FIFO.
*/
CH_IRQ_HANDLER(UART_RX_IRQ) {

CH_IRQ_PROLOGUE();

/* Se houver dados disponíveis no UART RX FIFO.*/


if ((UART->SR & UART_SR_DATA_AVAILABLE) != 0) {

/* Entrando no estado I-Locked e sinalização do semáforo.*/


chSysLockFromISR();
chBSemSignalI(&uart_bsem);
chSysUnlockFromISR();

/* Resetando a fonte de interrupção.*/


UART->SR &= ~UART_SR_DATA_AVAILABLE;
}

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));

/* Se um tempo limite de comunicação ocorreu em seguida, algum tratamento

Página - 65 de 109
especial será necessário.*/
if (msg == MSG_TIMEOUT) {
handle_comm_timeout();
continue;
}

/* Esvazia o UART RX FIFO e processa todos os dados antes de


tentar ocupar o semáforo novamente.*/
while ((UART->SR & UART_SR_RXFIFO_CNT) > 0) {
process_data(UART->DR);
}
}
}

/*
* 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();

/* Inicializa o semáforo UART no estado “ocupado”, em seguida,


inicializa a comunicação.*/
chBSemObjectInit(&uart_bsem, true);
uart_init();

/* Inicializa o thread UART.*/


chThdCreateStatic(waUartReceiveThread, sizeof(waUartReceiveThread),
NORMALPRIO + 1, UartReceiveThread, NULL);

/* 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.

Capítulo - 11 RT Mutexes e variáveis de


condição
ChibiOS/RT implementa Mutexes com inspiração POSIX e variáveis de condição. Juntos os
dois formam uma construção de alto nível, conhecida como Monitor.

11.1 Configurações globais


Mutexes e Variáveis Condicionadas são afetadas pelas seguintes configurações globais:

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:

Existe uma relação estrita entre mutexes e threads:

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.

11.2.2.1 Exclusão mútua


static mutex_t mtx1;

static THD_WORKING_AREA(waThread1, 128);


static THD_FUNCTION(Thread1, arg) {

while (true) {

/* Atividade do thread normal.*/


...;

/* Solicitação de acesso a recurso protegido.*/


chMtxLock(&mtx1);

/* Acessando recurso compartilhado.*/


...;

Página - 68 de 109
/* Liberar o recurso.*/
chMtxUnlock(&mtx1);

/* O thread continua.*/
...;
}
}

static THD_WORKING_AREA(waThread2, 128);


static THD_FUNCTION(Thread2, arg) {

while (true) {

/* Atividade do thread normal.*/


...;

/* Solicitação de acesso a recurso protegido.*/


chMtxLock(&mtx1);

/* Acessando recurso compartilhado.*/


...;

/* 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 o compartilhamento do recurso de exclusão mútua.*/


chMtxObjectInit(&mtx1);

/* 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.

11.3 Variáveis de condição e Monitores


Variáveis de condição é uma construção adicional trabalhando com exclusões mútuas, a fim
de criar construtores Monitor muito semelhantes, no comportamento, aos construtores Java
synchronized.
Uma variável de condição é uma fila de threads liberando temporariamente um mutex na
espera para um evento, uma vez que o evento é recebido, em seguida, o thread automaticamente
readquire a exclusão mútua, à espera de sua vez, se necessário.
Variáveis de condição e mutexes, juntos, são regulamentadas pelos seguintes diagramas de estado:

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.

11.3.1.1 Modelo de código


Os monitores devem sempre escrito seguindo este modelo:
void synchronized(void) {

/* Entrando no monitor.*/
chMtxLock(&mtx);

/* Código protegido antes da verificação de condição, opcional.*/


...;

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;
}

/* Código protegido com condição satisfeito.*/


...;

/* Leaving the monitor.*/


chMtxUnlock();
}

Os objetos “variáveis de condição” são simplesmente filas de threads, a fila é chamada de


“variável de condição”, porque entrar em fila geralmente ocorre após a verificação de uma condição
que não é parte do próprio objeto. Acima o “while” construido em sua totalidade é a variável de
condição.

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

static msg_t queue[QUEUE_SIZE], *rdp, *wrp;


static size_t qsize;
static mutex_t qmtx;
static condition_variable_t qempty;
static condition_variable_t qfull;

/*
* Inicialização da fila sincronizada.
*/
void qInit(void) {

chMtxObjectInit(&qmtx);
chCondObjectInit(&qempty);
chCondObjectInit(&qfull);

rdp = wrp = &queue[0];


qsize = 0;
}

/*
* 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);

/* Aguardando espaço na fila.*/


while (qsize >= QUEUE_SIZE)
chCondWait(&qfull);

/* Gravando a menssagem na fila.*/


*wr = msg;
if (++wr >= &queue[QUEUE_SIZE])
wr = &queue[0];
qsize++;

/* Sinalizando que existe pelo menos uma mensagem.*/


chCondSignal(&qempty);

/* 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);

/* Aguardando uma menssagem na fila.*/


while (qsize == 0)
chCondWait(&qempty);

/* Lendo a menssagem da fila.*/


msg = *rd
if (++rd >= &queue[QUEUE_SIZE])

Página - 73 de 109
rd = &queue[0];
qsize--;

/* Sinalizando que existe pelo menos um slot livre.*/


chCondSignal(&qfull);

/* Deixando o monitor.*/
chMtxUnlock(&qmtx);

return msg;
}

11.3.4.2 Produtor de ISRs


É o mesmo problema do exemplo anterior, mas assumimos que as mensagens são originadas
do contexto ISR. Vamos misturar um monitor com uma secção crítica de modo a manter a
atomicidade. Note que o produtor não é capaz de esperar, neste caso, mensagens em excesso são
perdidas.
#define QUEUE_SIZE 128

static msg_t queue[QUEUE_SIZE], *rdp, *wrp;


static size_t qsize;
static mutex_t qmtx;
static condition_variable_t qempty;

/*
* Inicialização da fila sincronizada.
*/
void qInit(void) {

chMtxObjectInit(&qmtx);
chCondObjectInit(&qempty);

rdp = wrp = &queue[0];


qsize = 0;
}

/*
* 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();

/* Checando por espaço vazio na fila.*/


if (qsize < QUEUE_SIZE) {

/* Gravando a mensagem na fila.*/


*wr = msg;
if (++wr >= &queue[QUEUE_SIZE];
wr = &queue[0];
qsize++;

/* Sinalização de que há, pelo menos, uma mensagem..*/


chCondSignalI(&qempty);

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;

/* Entrando no monitor, usando tanto o mutex e uma zona crítica.*/


chSysLock();
chMtxLockS(&qmtx);

/* Aguardando por mensagens na fila.*/


while (qsize == 0)
chCondWaitS(&qempty);

/* Lendo a mensagem da fila.*/


msg = *rd
if (++rd >= &queue[QUEUE_SIZE];
rd = &queue[0];
qsize--;

/* Saindo do monitor.*/
chMtxUnlockS(&qmtx);
chSysUnlock();

return msg;
}

Capítulo - 12 RT Síncronos de Mensagens


Mensagens síncronas são uma característica única do ChibiOS/RT que permite criar
arquitetura cliente/servidor em um sistema embarcado. RT implementa em sua camada interna no
escalonador um mecanismo de passagem de mensagens, quando uma troca de contexto é realizada
uma mensagem sempre é passado entre o thread que está saindo e o que está entrando. A mensagem
é trocada com quase nenhuma sobrecarga. Este mecanismo interno é usado para implementar a
funcionalidade de alto nível de mensagens síncronas.

12.1 Configurações globais


Essa opção habilita a API de síncrono de mensagens
CH_CFG_USE_MESSAGES
no kernel.
Se habilitado, então threads estão na fila por
CH_CFG_USE_MESSAGES_PRIORITY prioridade, em vez da em ordem na fila FIFO do
servidor. O padrão é desativado.

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.

12.3 Passando mensagem


Neste cenário existem vários threads no sistema que nunca partilham dados, tudo é feito
através da troca de mensagens. Cada thread representa um serviço, outros threads podem solicitar o
serviço através do envio de uma mensagem.
A vantagem dessa abordagem é a de não ter de lidar com a exclusão mútua, cada
funcionalidade é encapsulado num thread do servidor que serve sequencialmente todos os pedidos.
Por exemplo, você pode ter o seguinte cenário:
• Um servidor alocador de buffers.
• Um servidor de driver de disco.
• Um servidor de sistema de arquivos.
• Um ou mais threads cliente.

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.*/
...;

/* Envia de volta uma confirmação.*/


chMsgRelease(tp, MSG_OK);
}
}

12.3.1.2 Console do Servidor


Um Thread console servidor manipula as mensagens geradas pelo sistema.
static thread_t *console;

/*
* 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);

/* Envia de volta uma confirmação.*/


chMsgRelease(tp, MSG_OK);
}
}

/*
* 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();

/* Abrindo a porta serial 1.*/


sdStart(&SD1, NULL);

/* Iniciando o thread console fazendo-o imprimir através da porta serial.*/


console = chThdCreateStatic(waConsoleServerThread,
sizeof(waConsoleServerThread),
NORMALPRIO + 1, ConsoleServerThread,
(void*)&SD1);

(void)chMsgSend(console, (msg_t)"System started.");

/* 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.1 Configurações globais


Essa opção habilita a API mailboxes no kernel. Mailboxes também
CH_CFG_USE_MAILBOXES
exige CH_CFG_USE_SEMAPHORES

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.

As mailboxes são regulados pelo seguinte diagrama de estado:

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

static char buffers[NUM_BUFFERS][BUFFERS_SIZE];

static msg_t free_buffers_queue[NUM_BUFFERS];


static mailbox_t free_buffers;

static msg_t filled_buffers_queue[NUM_BUFFERS];


static mailbox_t filled_buffers;

/*
* ISR servindo a interrupção de rede..
*/
CH_IRQ_HANDLER(RX_FRAME_IRQ) {

CH_IRQ_PROLOGUE();

/* Se a rede cair todos os threads em espera são notificados.*/


if ((NET->SR & NET_SR_ERROR) != 0) {
/* Entrandono estado I-Locked e acordando todos os threads com uma mailbox
notificação de reset*/
chSysLockFromISR();
chMBResetI(&filled_buffers);
chSysUnlockFromISR();

/* Resetando a fonte de interrupção.*/


NET->SR &= ~NET_SR_ERROR;
}

/* Se houver um frame disponível na interface de rede.*/


if ((NET->SR & NET_SR_FRAME_AVAILABLE) != 0) {
void *pbuf;

/* Entrando no estado I-Locked e acordar um thread, se estiver disponível, o frame


será perdido se não houver threads disponíveis*/
chSysLockFromISR();

/* Se um buffer está disponível, então, preencher e envia-lo ao


thread processador, sabemos que a operação post não vai
falhar porque as duas mailboxes têm o mesmo tamanho.*/

if (chMBFetchI(&free_buffers, (msg_t *)&pbuf) == MSG_OK) {

Página - 82 de 109
fill_buffer(pbuf);
(void)chMBPostI(&filled_buffers, (msg_t)pbuf);
}

chSysUnlockFromISR();

/* Resetando a fonte de interrupção.*/


NET->SR &= ~NET_SR_FRAME_AVAILABLE;
}

CH_IRQ_EPILOGUE();
}

static THD_WORKING_AREA(waProcessFrameThread1, 128);


static THD_WORKING_AREA(waProcessFrameThread2, 128);
static THD_WORKING_AREA(waProcessFrameThread3, 128);
static THD_WORKING_AREA(waProcessFrameThread4, 128);

static THD_FUNCTION(ProcessFrameThread, arg) {

while (true) {
void *pbuf;

/* Esperando por um buffer preenchido.*/


msg_t msg = chMBFetch(&filled_buffers, (msg_t *)&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);

/* Retornando o buffer para o pool de buffers livre.*/


(void)chMBPost(&free_buffers, (msg_t)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]);

/* Iniciando os threads processadores de frames.*/


chThdCreateStatic(waProcessFrameThread1, sizeof(waProcessFrameThread1),
NORMALPRIO + 1, ProcessFrameThread, NULL);
chThdCreateStatic(waProcessFrameThread2, sizeof(waProcessFrameThread2),
NORMALPRIO + 1, ProcessFrameThread, NULL);

Página - 83 de 109
chThdCreateStatic(waProcessFrameThread3, sizeof(waProcessFrameThread3),
NORMALPRIO + 1, ProcessFrameThread, NULL);
chThdCreateStatic(waProcessFrameThread4, sizeof(waProcessFrameThread4),
NORMALPRIO + 1, ProcessFrameThread, NULL);

/* Inicialização do subsistema de rede, é iniciado o processamento.*/


net_init();

/* 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.1 Configurações globais


CH_CFG_USE_EVENTS Essa opção habilita a API mailboxes no kernel.
CH_CFG_USE_EVENTS_TIMEOUT Habilita o suporte de tempo limite para 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.

14.2.1 Fontes de Eventos


Fontes de eventos são os objetos que transmitem eventos para o sistema.

Há duas possíveis operações em fontes de eventos:


• Register. Registrar-se em uma fonte faz com que o thread seja notificado de eventos.
• Broadcast. Transmitir a uma fonte notifica os threads registrados na fonte que um evento
ocorreu.

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.

14.2.3 Ouvintes de eventos


Em cada fonte de eventos um ou mais eventos ouvintes podem ser registrados, cada um
evento ouvinte está ligado a um único thread. A estrutura de estabelecer um relacionamento muitos-
para-muitos entre fontes de eventos e threads.

Bandeiras de evento estão vinculadas ao campo flags do evento ouvinte, o thread


associado pode recuperá-los usando chEvtGetAndClearFlags().

14.2.4 Máscaras de eventos


Um conjunto de eventos pendentes é chamado de Máscara de eventos e tem o tipo eventmask_t
este tipo deve não ser confundido com sinalizadores de evento. Cada thread tem dois campos
relacionados ao tratamento de eventos:
• ewmask representa a máscara de eventos em que thread está interessado, um AND é
realizado entre esta máscara e a máscara dos eventos pendentes.
• epending é a máscara dos eventos que está esperando para ser servido pelo thread.

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

ChibiOS/RT implementa três variantes da espera primitivo chamado:


• WaitOne. Qualquer dos eventos especificados pode despertar o thread, apenas um é
devolvido.
• WaitAny. Qualquer combinação dos eventos especificados pode despertar o thread, todos
eles são devolvidos (condição OR).
• WaitAll. Só a combinação exata dos eventos especificados pode despertar o thread. Isto
significa que uma combinação de eventos deve ter acontecido (condição AND).

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 Eventos simplificados


Há também uma outra maneira de usar eventos sem recorrer a fontes de eventos e ouvintes. Um
thread pode sinalizar diretamente outro thread. Neste cenário, não há dissociação entre as fontes e
thread, thread específicos ou ISRs signal specific threads com uma máscara de sinalizadores de
evento.
O objetivo alvo é o thread manipular diretamente o evento.

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;

static THD_WORKING_AREA(waListenerThread, 128);

static THD_FUNCTION(ListenerThread, arg) {


event_listener_t network_listener;
event_listener_t serial_listener;

/* Registrar o driver de rede como evento 0,


não há bandeiras específicas.*/
chEvtRegisterMask(&network_event_source,
&network_listener,
EVENT_MASK(0));

/* Registra-se no driver serial como um evento 1,


interessado apenas em bandeiras de erro e dados disponíveis,
outras bandeiras não acordará o thread.*/
chEvtRegisterMaskWithFlags(&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();
}
}
}

14.2.8.2 Sinalização direta


Um thread serve eventos originados em uma UART ISR. Note que o thread pode estar
servindo eventos provenientes de várias fontes, o número máximo de fontes diferentes é
sizeof(eventmask_t).

#define EVT_UART_RX EVENT_MASK(0)


#define EVT_UART_TX EVENT_MASK(1)
#define EVT_UART_ERR EVENT_MASK(2)

/*
* 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;

/* Resetando status bits.*/


sr = UART->SR;
UART->SR = ~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;

/* Se houver dados disponíveis na UART RX FIFO.*/


if ((UART->SR & UART_SR_DATA_AVAILABLE) != 0)
events |= EVT_UART_RX;

/* Se não houver espaço na UART TX FIFO.*/


if ((UART->SR & UART_SR_TX_EMPTY) != 0) {
events |= EVT_UART_TX;

/* Sinalizando eventos para o thread, se houver.*/


if (events) {
chSysLockFromISR();
chEvtSignalI(uart_thread, events);
chSysUnlockFromISR();
}

CH_IRQ_EPILOGUE();
}

static THD_WORKING_AREA(waUARTThread, 128);

static THD_FUNCTION(UARTThread, arg) {

/* 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.*/
...;
}

Capítulo - 15 I/O Queues


As filas I/O são um sistema genérico de buffers circulares especializados em comunicações
de I/O orientados a byte.

15.1 Configurações globais


CH_CFG_USE_QUEUES Essa opção habilita a API I/O queues no kernel.

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.

15.2.1 Filas genéricas I/O


Isto é o predecessor comum para todas as filas de E/S. Este objeto não pode ser utilizado
diretamente, exceto para derivar objetos mais especializados.

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.

15.2.1.1 Obtendo tamanho da fila


A operação size retorna o tamanho da memória do buffer circular alocado para a fila.

15.2.1.2 Resetando de fila


A operação reset limpa a fila, threads a espera são notificados através de uma mensagem
MSG_RESET.

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.

As seguintes operações são definidas para filas de entrada.

15.2.2.1 Obtendo Espaço


A operação space retorna o número de slots preenchidos na fila de entrada.

15.2.2.2 Colocando Dados


A operação put é realizada no lado do ISR e escreve um byte para a fila, se a fila está cheia, então, a
operação falhará.

15.2.2.3 Obtendo dados


A operação get retorna um byte retirado da fila de entrada, se necessário aguardará. Um tempo
limite pode ser especificado.

15.2.2.4 Leitura de Dados


A operação read recebe a quantidade especificada de dados a partir da fila de entrada, se necessário
aguardará. Um tempo limite pode ser especificado.

15.2.3 As filas de saída


Uma fila de saída é um buffer circular escrito do lado do thread e é lido do lado ISR.

Página - 93 de 109
As operações seguintes são definidos para as filas de saída.

15.2.3.1 Obtendo Espaço


A operação space retorna o número de slots vazios na fila de saída.

15.2.3.2 Obtendo dados


A operação get é realizada no lado do ISR e lê um byte da fila, se a fila está vazia, então, a operação
falhará.

15.2.3.3 Colocando Dados


A operação put escreve um byte para a fila de espera de saída, se necessário. Um tempo limite pode
ser especificado.

15.2.3.4 Escrevendo Dados


A operação write envia a quantidade especificada de dados para a fila de espera de saída, se
necessário. Um tempo limite pode ser especificado.

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.

#define SERIAL_BUFFERS_SIZE 128

static uint8_t ibuf[SERIAL_BUFFERS_SIZE];


static uint8_t obuf[SERIAL_BUFFERS_SIZE];
static input_queue_t iq;
static output_queue_t oq;

/*
* ISR servindo a interrupção UART.
*/
CH_IRQ_HANDLER(UART_RX_IRQ) {
msg_t msg;

CH_IRQ_PROLOGUE();

/* Se houver dados disponíveis no FIFO UART RX.*/


while ((UART->SR & UART_SR_RXNOTEMPTY) != 0) {

/* Entrando no estado I-Locked e enviando o frame


para a fila de entrada.*/
chSysLockFromISR();
msg = chIQPutI(&iq, UART->DR);
chSysUnlockFromISR();

/* Manuseamento a condição de uma fila de entrada completa.*/


if (msg == MSG_TIMEOUT) {
handle_overflow();
}
}

/* Se não houver espaço na FIFO UART TX.*/


while ((UART->SR & UART_SR_TXNOTFULL) != 0) {

/* Entrando no estado I-Locked e obtem um frame da fila de saída.*/


chSysLockFromISR();
msg = chOQGetI(&oq);
chSysUnlockFromISR();

if (msg < MSG_OK) {


/* Não há dados para transmitir, desabilita a interrupção TX.*/
UART->IER &= ~UART_IER_TXNOTFULL;
break;
}
else {

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.
*/

static void notify(io_queue_t *qp) {

(void)qp;

/* Habilitando a interrupção TX, a transmissão será reiniciada com a próxima


ISR.*/
UART->IER |= UART_IER_TXNOTFULL;
}

/*
* 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();

/* Inicializando as filas UART.*/


iqObjectInit(&iq, ibuf, SERIAL_BUFFERS_SIZE, NULL, NULL);
oqObjectInit(&oq, obuf, SERIAL_BUFFERS_SIZE, notify, NULL);
uart_init();

/* Imprimindo na porta serial.*/


chOQWriteTimeout(&oq, (uint8_t *)"Hello world!\r\n", 14, TIME_INFINITE);

/* 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.

O predecessor de todas as interfaces streams-like é o Sequential Stream.

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.

16.1.2.2 Implementando Streams


O próximo passo lógico é implementar a interface sequential streams em uma classe
utilizável. Este exemplo implementa um Stream de Memória, um stream capaz de ler e escrever em
um buffer de memória.

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;

void msObjectInit(MemoryStream *msp, uint8_t *buffer, size_t size, size_t eos);


/*
* Implementação memstream.c.
*/

#include <string.h>

#include "ch.h"
#include "memstreams.h"

/* Implementação dos Metodos.*/


static size_t writes(void *ip, const uint8_t *bp, size_t n) {
MemoryStream *msp = ip;

if (msp->size - msp->eos < n)


n = msp->size - msp->eos;
memcpy(msp->buffer + msp->eos, bp, n);
msp->eos += n;
return n;
}

static size_t reads(void *ip, uint8_t *bp, size_t n) {


MemoryStream *msp = ip;

if (msp->eos - msp->offset < n)


n = msp->eos - msp->offset;
memcpy(bp, msp->buffer + msp->offset, n);

Página - 100 de 109


msp->offset += n;
return n;
}

static msg_t put(void *ip, uint8_t b) {


MemoryStream *msp = ip;

if (msp->size - msp->eos <= 0)


return MSG_RESET;
*(msp->buffer + msp->eos) = b;
msp->eos += 1;
return MSG_OK;
}

static msg_t get(void *ip) {


uint8_t b;
MemoryStream *msp = ip;

if (msp->eos - msp->offset <= 0)


return MSG_RESET;
b = *(msp->buffer + msp->offset);
msp->offset += 1;
return b;
}

static const struct MemStreamVMT vmt = {writes, reads, put, get};

/*
* 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
*/

void msObjectInit(MemoryStream *msp, uint8_t *buffer,


size_t size, size_t eos) {

msp->vmt = &vmt;
msp->buffer = buffer;
msp->size = size;
msp->eos = eos;
msp->offset = 0;
}

Capítulo - 17 Gerenciamento de memória


RT
Alocação de memória é um recurso opcional de ChibiOS/RT. O kernel em si nunca aloca
nem libera objetos em tempo de execução, mas os serviços de gerenciamento de memória são

Página - 101 de 109


oferecidos para a aplicação, se necessário.

17.1 Configurações globais


Essa opção habilita o módulo núcleo alocador, este módulo é
CH_CFG_USE_MEMCORE exigido por todos os outros módulos de gerenciamento de
memória.
Tamanho do bloco de memória a ser usada como área de
CH_CFG_MEMCORE_SIZE memória do núcleo. Se definido como zero, então, toda a RAM
disponível é utilizada.
CH_CFG_USE_HEAP Essa opção habilita o módulo alocador heap.
CH_CFG_USE_MEMPOOLS Essa opção habilita o módulo alocador do pool de memória.
CH_CFG_USE_DYNAMIC Essa opção habilita os serviços de threading dinâmico.

17.2 Núcleo Alocador de Memória


Este módulo é responsável pela alocação de blocos de memória RAM do sistema, os blocos
alocados não podem ser devolvidos. A aplicação é bastante simples:

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.

Os símbolos são geralmente exportados pelo linker graças a um arquivo de dispersão


especialmente criado. O núcleo alocador aloca blocos a partir do endereço de base até o endereço
final. Os blocos alocados são garantidos para ser alinhado com o tipo de dados mais restritivo para a
arquitetura atual. Solicitando mais memória do que o tamanho restante termina em um erro.

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.

Página - 102 de 109


17.3 Alocador de pilha
17.3.1 API

17.4 Alocador de memória Pool


17.4.1 API

17.5 Alocadores de comparação


Todos os alocadores podem ser usados juntos, mas alguns são mais adequados em cenários
específicos, esta é uma breve comparação:

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.

17.6 Dynamic Threading


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

Página - 103 de 109


depuração são acessíveis em um o arquivo de configuração do kernel chconf.h cada projeto tem
sua própria cópia desse arquivo.

18.1 Verificações em tempo compilação


Erros de configuração no projeto, são detectados em tempo de compilação, os cabeçalhos do
sistema incluem verificações lógicas que resultam em erros de compilação em casos de um erro de
configuração. Se você vê um erro durante a compilação leia atentamente a mensagem, ela poderia
ser uma verificação de configuração que falhou em vez de um erro de sintaxe normal.

18.2 Verificações em tempo de execução


A maioria das opções de depuração operam em tempo de execução, a fim de pegar erros de
projeto ou de programação. Se for detectado um problema, em seguida, o sistema ficará parado na
função chSysHalt() e as variáveis globais ch.dbg_panic_msg apontaram para uma
sequência de mensagens de erro.
Se o sistema for interrompido a coisa correta a fazer é:
1. Parar o aplicativo usando o depurador.
2. Verifique se de fato a aplicação parou em chSysHalt()
3. Recuperar a mensagem de erro usando a exibição da memória do depurador ou o plugin de
depuração ChibiOS/RT Eclipse. As mensagens podem ser tanto strings descritivas como
“stack overflow”, “NULL parameter” ou códigos de erro codificados como “SV#4”,
dependendo da opção de depuração que desencadeou.
4. Inspecione o rastreamento de pilha para compreender em que ponto do código
chSysHalt() foi chamado. A condição que disparou o travamento poderia dar uma dica
sobre a natureza do problema.

18.2.1 Kernel Estatísticas


A opção de depuração CH_DBG_STATISTICS habilita o suporte para as estatísticas do
kernel. As estatísticas incluem:
• Número de IRQs servidos.
• Número de trocas de contexto.
• Medição do tempo em nível de thread em seções críticas: melhor, pior, últimos casos são
armazenados.
• Medição do tempo em nível de ISR em seções críticas: melhor, pior, últimos casos são
armazenados.
• Para cada thread os seguintes contadores são mantidos:
• Maior tempo de execução.

• Menor tempo de execução.

• Última vez de execução.

Página - 104 de 109


• Tempo de execução acumulado.

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.

18.2.2 Estado do Sistema


A opção de debug CH_DBG_SYSTEM_STATE_CHECK habilita um único System State
Checker no ChibiOS/RT. Esta opção é capaz de detectar qualquer violação de protocolo de
chamada, chamada OS APIs fora do contexto apropriado é uma das maiores fontes problemas e
difícil detectar e as falhas aleatórias.
Em caso de detecção de violação, a execução é interrompida e uma mensagem é apontada pela
variável ch.dbg_panic_msg:

• 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.

Se um aplicativo é capaz de rodar com o verificador de estado habilitado em toda sua


extensão, então há um nível muito grande de segurança que o aplicativo está livre de problemas de
integração relacionados com RTOS ou erros de programação. É aconselhável manter esta opção
habilitada através de todo processo de desenvolvimento.

Página - 105 de 109


18.2.3 Parâmetros de Funções
A opção de depuração CH_DBG_ENABLE_CHECKS habilita os parâmetros de controle a
nível da API. Esta opção é capaz de detectar erros de aplicativos fazendo com que o aplicativo passe
parâmetros inválidos para o RTOS, um exemplo típico são ponteiros NULL passados onde se espera
uma referência a um objeto válido. É aconselhável manter esta opção ativada através de todo o
processo de desenvolvimento. As preocupações de segurança podem exigir que se mantenha este
tipo de controle no local e também no código final como uma medida defensiva.

18.2.4 System Assertions


A opção de depuração CH_DBG_ENABLE_ASSERTS habilita verificações de integridade de
todo o sistema sobre as estruturas de dados RTOS. O sistema também é verificado para situações
inesperadas em tempo de execução. É aconselhável manter esta opção ativada através de todo o
processo de desenvolvimento. As preocupações de segurança podem exigir que se mantenha este
tipo de controle no local e também no código final como uma medida defensiva.

18.2.5 Trace Buffer


A opção de depuração CH_DBG_ENABLE_TRACE habilita rastrear a troca de contexto no
buffer circular. Este buffer armazena as últimas N operações de troca de contexto. O buffer é útil
como uma ferramenta post-mortem, pois é capaz de dizer a sequência de operações que levaram o
sistema a um impasse. O conteúdo do buffer é processado em um formato tabulado legível pelo
plugin ChibiOS/RT Eclipse. Esta é uma característica estritamente em tempo de desenvolvimento.

18.2.6 Stack Overflow


A opção de depuração CH_DBG_ENABLE_STACK_CHECK habilita controles sobre as
condições de estouro de pilha. A implementação da detecção é dependente do port e podem ser
implementadas de forma diferente em cada port ou mesmo não ser suportado. A aplicação mais
comum é o de verificar a posição de pilha quando uma troca de contexto está prestes a ser realizada,
se a nova posição calculada da pilha estoura o limite da pilha então o sistema é interrompido. As
preocupações de segurança podem exigir que se mantenha este tipo de controle no local e também
no código final como uma medida defensiva.

18.2.7 Working Area Filling


A opção de depuração CH_DBG_FILL_THREADS preenche os threads na área de trabalho
com um padrão 0x55 fixo antes que o thread seja executado, o que permite calcular o uso eficaz da
pilha pelos vários threads. O plugin ChibiOS/RT Eclipse é capaz de calcular o tamanho da pilha não
utilizado para cada segmento, se essa opção for ativada. Otimização das pilhas não utilizadas só
devem ser realizadas:
• No final do desenvolvimento.
• Com todas as outras opções de depuração desativadas ou em suas configurações finais.
• Usando as opções do compilador no final.

Página - 106 de 109


Caso de uso em pilhas otimizando porque diferentes opções de compilação ou versão do
compilador pode mudar dramaticamente o tamanho das pilhas e há o risco de introdução de erros
não detectados facilmente no código final (com verificações desabilitadas).

18.2.8 Threads Profiling


A opção de depuração CH_DBG_THREADS_PROFILING habilita um system tick counter
em cada thread, depois de um longo tempo de execução valores relativos dos contadores indicam o
“peso” relativo de todos os threads. Esta opção foi substituída pela CH_DBG_STATISTICS e não
é compatível com o modo tick-less.

Capítulo - 19 Siglas – Acrónimos


RTOS - Real-Time Operating System
CPU - Central Processing Unit
NMI - Non Maskable Interrupt
FIQ - Fast interrupt request
IRQ - Interrupt request
ISR - Interrupt Service Routine
VIC - Vectored Interrupt Controller
FIFO - First In, First Out
OSEK - Offene Systeme und deren Schnittstellen für die Elektronik in Kraftfahrzeugen;
OSEK - English: "Open Systems and their Interfaces for the Electronics in Motor Vehicles"

Página - 107 de 109

S-ar putea să vă placă și