Trilha de aprendizado · Nível 13 · Tutorial 1

Período de atualização e orçamento de execução

Definir um período de atualização e verificar, no Arduino Uno próprio, se aquisição, cálculo e saída cabem no tempo disponível com margem.

  • Nível: Intermediário
  • Duração: 20 min
  • 9 passos
Período de atualização e orçamento de execução

O que você vai percorrer

  1. Separe os tempos de uma atualização Diferencie os tempos planejados dos tempos que realmente acontecem em uma tarefa periódica. 2 min
  2. Escolha um período inicial justificável Escolha um período inicial comparando a banda útil, a dinâmica conhecida, o atraso aceitável e o tempo que restará para executar o programa. 2 min
  3. Distribua o orçamento de execução Distribua o período entre a tarefa periódica, custos indiretos e uma reserva antes de medir o comportamento no Uno. 2 min
  4. Meça durações com micros Use micros() para delimitar e interpretar medições curtas de duração no Arduino Uno R3. 2 min
  5. Interprete latência e jitter Use registros de tempo para distinguir latência, jitter, intervalos reais e perda de prazo. 2 min
  6. Trate atrasos sem acumular trabalho vencido Mantenha uma referência periódica, descarte liberações vencidas de forma contada e execute somente a atualização mais recente disponível. 3 min
  7. Instrumente uma tarefa no seu Uno Execute localmente uma tarefa periódica instrumentada no Arduino Uno R3 e obtenha métricas iniciais de duração, latência, intervalos, descartes e perdas de prazo. 3 min
  8. Teste variações e revise a margem Confronte o orçamento com condições de execução diferentes, teste a política de descarte e revise a margem com base em evidências limitadas. 2 min
  9. Valide e registre sua decisão Consolide período, orçamento, medições e política de recuperação em um registro técnico conciso e prudente. 3 min

O que você vai aprender

  • Escolher e justificar um período inicial usando a dinâmica conhecida e a banda útil do sinal.
  • Distribuir o tempo disponível entre as operações de uma tarefa periódica.
  • Medir duração, atraso de atendimento e variação dos intervalos com micros.
  • Detectar atualizações atrasadas e aplicar uma política limitada de recuperação.

Antes de começar

  • Temporização sem bloqueio com millis
  • Interrupções com rotinas curtas e dados compartilhados
  • Taxa de amostragem e prevenção de aliasing
  • Leitura de tensão com o ADC do Arduino Uno
  • Comunicação UART pelo monitor serial

Passo 1 de 9

Separe os tempos de uma atualização

Diferencie os tempos planejados dos tempos que realmente acontecem em uma tarefa periódica.

Os marcos da atualização

Quatro referências de tempo

Uma tarefa periódica tem instantes previstos para atualizar. O período programado (T) é a distância entre dois desses instantes previstos — não é, necessariamente, o tempo que o código leva para executar.

Nesta trilha, cada atualização terá prazo relativo igual ao período: se uma atualização era prevista para t₀, ela deve terminar até t₀ + T. Essa convenção facilita verificar se há tempo para a próxima atualização.

Linha do tempo de uma tarefa

Diagrama com dois instantes previstos igualmente espaçados, inícios efetivos ligeiramente atrasados, barras de execução e limites de prazo.

Compare sempre os marcos previstos com os eventos que realmente ocorreram.

Um mesmo registro, medidas diferentes

O que cada trecho representa

Leia uma atualização por quatro trechos:

  • Período programado (T): distância entre instantes previstos consecutivos.
  • Início efetivo: momento em que o código da atualização realmente começa.
  • Duração de execução: do início efetivo ao fim daquela atualização.
  • Intervalo real: distância entre os inícios efetivos de duas atualizações consecutivas.
  • Prazo: janela do instante previsto até o limite para terminar.

Assim, duração e intervalo real começam em referências diferentes. Uma execução pode ser curta, mas começar atrasada porque outra atividade ocupou o processador antes dela.

Exemplo

Exemplo

Considere T = 10 ms. Uma atualização era prevista para 20 ms, começou em 22 ms e terminou em 25 ms. A seguinte começou em 31 ms.

  • Período programado: 10 ms (por exemplo, de 20 a 30 ms).
  • Duração da primeira atualização: 3 ms (de 22 a 25 ms).
  • Prazo da primeira: até 30 ms; ela terminou dentro do prazo.
  • Intervalo real entre inícios: 9 ms (de 22 a 31 ms).

O início foi posterior ao previsto, mas isso não implica, sozinho, que o prazo foi perdido.

Pratique a leitura da linha do tempo

Nomeie as medidas

Associe cada grandeza à definição correta.

Faça as associações.

Toque em um item e depois no par correspondente.

Feche a distinção

Duração curta basta?

Se uma atualização dura pouco, ela necessariamente começou no instante previsto e cumpriu o prazo.

Passo 2 de 9

Escolha um período inicial justificável

Escolha um período inicial comparando a banda útil, a dinâmica conhecida, o atraso aceitável e o tempo que restará para executar o programa.

Critérios além da amostragem

Do sinal ao período inicial

Uma escolha inicial de período é uma hipótese de engenharia, não uma fórmula universal. Use as informações já conhecidas sobre o sinal e a dinâmica para comparar candidatos.

Considere dois critérios:

  • Banda útil: o período precisa atender ao critério de amostragem que você já conhece. Isso evita aliasing, mas não define sozinho uma boa atualização.
  • Dinâmica e atraso aceitável: pergunte quantas atualizações ocorrem durante a constante de tempo conhecida e se esperar até o próximo atendimento é aceitável para a aplicação.

Um período menor traz atualizações mais frequentes e potencialmente menor atraso de resposta. Em troca, deixa menos tempo para aquisição, cálculo, saída e as demais atividades do firmware.

Atualizações dentro da dinâmica

Comparação visual entre períodos curto, intermediário e longo sobre uma mesma resposta lenta, mostrando quantas atualizações cabem em uma constante de tempo.

O mesmo sistema pode ser atualizado em ritmos diferentes; compare o atraso e a quantidade de oportunidades de reagir.

Dica

Evite regra pronta

Não existe uma razão fixa, como “sempre usar dez atualizações por constante de tempo”, que sirva para todo sistema. Uma aplicação pode tolerar poucas atualizações em uma dinâmica lenta; outra pode exigir resposta mais rápida mesmo com a mesma constante de tempo.

A decisão também é provisória: nos próximos steps, você vai montar o orçamento e medir o que o Uno realmente consegue executar com margem.

Compare candidatos, não receitas

Exemplo com requisitos fornecidos

Caso de comparação: um sinal tem banda útil de 5 Hz e o comportamento relevante possui constante de tempo conhecida de 200 ms. A aplicação aceita até cerca de 50 ms de espera até uma nova atualização.

Pelo critério mínimo de Nyquist, a taxa deve ser maior que 10 Hz; portanto, o período deve ser menor que 100 ms. Compare agora três candidatos. A coluna “atualizações por τ” é apenas uma forma de visualizar a dinâmica; ela não é uma regra de aprovação isolada.

Períodos candidatos

Comparação de períodos candidatos para o exemplo.

O candidato de 50 ms equilibra os requisitos declarados neste caso.
PeríodoTaxaAtualizações por τLeitura inicial
20 ms50 Hz10Resposta frequente; pouco tempo para o programa
50 ms20 Hz4Atende a banda e o atraso aceito
80 ms12,5 Hz2,5Atende Nyquist, mas excede o atraso aceito

Exemplo

Conclusão do exemplo

Para este caso, 50 ms é uma escolha inicial justificável: gera 20 Hz, acima dos 10 Hz mínimos associados à banda de 5 Hz; oferece quatro atualizações em 200 ms; e não ultrapassa os 50 ms de espera declarados.

Já 80 ms não serve porque viola o requisito de atraso, apesar de passar no critério mínimo de amostragem. O período de 20 ms não é automaticamente melhor: ele só será mantido se o orçamento e as medições mostrarem tempo suficiente com margem.

Verifique sua justificativa

Escolha fundamentada

No caso apresentado, qual justificativa sustenta melhor a escolha provisória de 50 ms?

Selecione a melhor justificativa.

Passo 3 de 9

Distribua o orçamento de execução

Distribua o período entre a tarefa periódica, custos indiretos e uma reserva antes de medir o comportamento no Uno.

Enxergue tudo que ocupa tempo

O período é um orçamento

Um período é um recipiente de tempo: tudo o que pode afetar a próxima atualização precisa caber nele. Comece separando a tarefa crítica — adquirir, calcular e atualizar a saída — das demais atividades que também disputam o processador.

Um orçamento preliminar não é uma medição: é uma estimativa declarada para orientar o projeto. Ele deve reservar tempo para variações, caminhos condicionais, interrupções e diagnósticos. Depois, os valores serão confrontados com medições no Uno.

Linha do tempo segmentada mostrando que aquisição, cálculo, saída, comunicação, interrupções e reserva ocupam partes de um mesmo período.

Cada bloco consome parte do mesmo período. A reserva não é tempo “sobrando”: ela protege o atendimento contra variações.

Monte uma reserva explícita

Liste também os custos indiretos

Monte o orçamento em blocos. Inclua custos pequenos que costumam ser esquecidos: verificar se chegou o instante da tarefa, marcas de tempo para diagnóstico, interrupções e o restante do loop().

A comunicação serial deve ficar fora do trecho crítico sempre que possível, mas não fica fora do orçamento. Formatar e enviar uma mensagem pode consumir tempo ou bloquear; portanto, pode atrasar o início da atualização seguinte.

Orçamento de 10 ms

Exemplo de orçamento preliminar para período de 10.000 µs. Os valores são estimativas de projeto, não medições do seu Uno.

A soma dos blocos, incluindo a reserva, deve ser igual ao período.
BlocoEstimativa (µs)Papel
Agendamento120Verificar liberação
Aquisição260Ler entrada
Cálculo90Processar valor
Atualização da saída50Escrever LED ou PWM
Instrumentação120Marcas de tempo
Outras atividades800Restante do loop
Diagnóstico serial3.800Formatar e transmitir
Interrupções previstas500Reserva para atendimentos
Margem explícita4.260Variações e caminhos extras

Exemplo

Crítico curto não basta

A duração estimada da tarefa crítica neste exemplo é 260 + 90 + 50 = 400 µs. Isso é bem menor que 10.000 µs, mas ainda não prova que ela começará no instante previsto: uma atividade serial ou outro trecho do loop pode estar em execução quando chegar a hora.

Por isso, considere dois testes no orçamento: (1) a soma de todos os blocos cabe no período? (2) há margem positiva e justificável? Se a reserva for zero, qualquer variação já transforma a estimativa em atraso.

Verifique o orçamento

Calcule a reserva

No orçamento da tabela, qual é a reserva restante em microssegundos após contabilizar todos os blocos, exceto a própria margem?

Escreva pelo menos 1 caracteres (0/1).

Identifique a omissão

Qual proposta é mais adequada para um orçamento preliminar de uma tarefa periódica?

Passo 4 de 9

Meça durações com micros

Use micros() para delimitar e interpretar medições curtas de duração no Arduino Uno R3.

Delimite o que será medido

Duas marcas de tempo

Para medir a duração de um trecho, capture uma marca de tempo imediatamente antes e outra imediatamente depois dele. A diferença entre as marcas é a duração observada.

No Uno R3, use marcas e diferenças sem sinal de 32 bits (uint32_t). Isso mantém o cálculo correto mesmo quando o contador de micros() retorna a zero.

A marca inicial deve ficar próxima do começo que você quer medir; a final, logo após o fim. Tudo que estiver entre elas — inclusive uma interrupção que ocorra nesse intervalo — compõe a duração observada.

Medição ao redor do trecho

Linha do tempo com uma marca antes e outra depois de um trecho crítico; a distância entre elas representa a duração observada.

Meça somente o trecho que interessa: as marcas delimitam o intervalo observado.

Considere a resolução do Uno

Padrão de medição com micros()

Este padrão mede a duração da função tarefa(). O código não deve imprimir pela Serial dentro do trecho medido: comunicação e formatação também consomem tempo e devem ser orçadas separadamente.

cpp
uint32_t inicio = micros();

tarefa();

uint32_t fim = micros();
uint32_t duracaoUs = fim - inicio;

Resolução e custo da medida

No Uno R3 de 16 MHz, com a configuração padrão, micros() avança em passos de 4 µs. Portanto, uma duração lida como 0 significa apenas que o trecho ficou abaixo da resolução observável, ou caiu entre duas atualizações da contagem; não significa execução instantânea.

A própria leitura de micros() também tem custo. Para trechos muito curtos, a instrumentação e a quantização podem ser comparáveis ao tempo que você quer medir. Meça blocos significativos e interprete valores pequenos com prudência.

Atenção

Condição para confiar em micros()

micros() depende do Timer0. Mantenha a configuração padrão desse temporizador e evite desabilitar interrupções por períodos longos. Caso contrário, a contagem pode deixar de representar adequadamente o tempo transcorrido.

Atravesse o retorno a zero com segurança

Exemplo

O retorno da contagem não quebra a diferença

A contagem de micros() é de 32 bits sem sinal e retorna a zero aproximadamente a cada 71,6 minutos. Isso não exige um tratamento especial na subtração usual.

Exemplo: pouco antes do retorno, inicio = 4294967292; após o retorno, fim = 8. Em aritmética sem sinal de 32 bits, fim - inicio resulta em 12: o intervalo real foi 12 µs.

A regra vale desde que o intervalo medido seja menor que uma volta completa do contador — condição amplamente atendida ao medir uma tarefa curta.

Subtração modular

Diagrama de um contador que passa do valor máximo para zero, com um intervalo curto atravessando esse retorno.

Com marcas sem sinal de 32 bits, a diferença preserva um intervalo curto mesmo através do retorno a zero.

Pratique a subtração sem sinal

Complete a medição

Complete a captura da marca final e o cálculo da duração observada.

cpp
uint32_t inicio = micros();

// trecho cuja duração será observada
tarefa();

uint32_t fim = ();
uint32_t duracaoUs = fim - ;

Passo 5 de 9

Interprete latência e jitter

Use registros de tempo para distinguir latência, jitter, intervalos reais e perda de prazo.

Localize o atraso e a variação

Latência: atraso para começar

Em cada atualização, compare o instante previsto com a marca de início registrada pelo software, idealmente imediatamente antes ou depois da aquisição. A diferença é a latência de atendimento:

latência = início efetivo − instante previsto

Se a atualização estava prevista para 11.000 µs e começou em 11.100 µs, a latência foi 100 µs.

Essa marca descreve quando a tarefa realmente começou a ser atendida. Ela não é o instante em que uma mensagem de diagnóstico aparece no Monitor Serial: a transmissão e a formatação serial podem ocorrer bem depois e não devem virar a referência da aquisição.

Linha do tempo de atendimentos

Diagrama com instantes previstos, inícios efetivos, barras de execução e limites de prazo em três atualizações periódicas.

A distância entre a marca prevista e o início efetivo é a latência. Quando essa distância muda de uma atualização para outra, há variação temporal no atendimento.

Jitter e intervalos reais

Jitter é a variação desses tempos ao longo das atualizações. Um atraso constante de 100 µs em todas as chamadas é latência, mas não produz variação entre elas. Já atrasos de 0 µs, 100 µs e 50 µs indicam atendimento variável.

Também registre o intervalo real entre inícios consecutivos:

intervalo[i] = início[i] − início[i−1]

Para uma sequência observada, anote o menor intervalo, o maior intervalo e a amplitude: máximo − mínimo. São indicadores observados no ensaio, não uma garantia para todas as condições possíveis.

Calcule prazo, intervalo e amplitude

Dois registros para comparar

Considere período programado de 1.000 µs e prazo relativo também de 1.000 µs. Portanto, cada prazo termina 1.000 µs depois do respectivo instante previsto.

Registros de tempo

Caso A mantém o mesmo deslocamento; caso B varia o deslocamento. No caso B, a segunda atualização também ultrapassa seu prazo.

Tempos em microssegundos (µs).
CasoPrevistoInícioDuraçãoFimLatência
A110.00010.10070010.800100
A211.00011.10070011.800100
A312.00012.10070012.800100
B110.00010.00070010.7000
B211.00011.10092012.020100
B312.00012.05070012.75050

Exemplo

Leitura dos intervalos

No caso A, os intervalos reais são 1.000 µs e 1.000 µs. Mínimo = máximo = 1.000 µs; amplitude = 0 µs. Há latência constante de 100 µs, mas não há jitter observado nesses inícios.

No caso B, os intervalos são 1.100 µs e 950 µs. Mínimo = 950 µs; máximo = 1.100 µs; amplitude = 150 µs. Os atendimentos variaram.

Duração isolada não basta

Para verificar o prazo, não basta comparar a duração com o período. Use:

latência + duração ≤ prazo relativo

Em B2: 100 µs + 920 µs = 1.020 µs. O prazo era 1.000 µs; o fim em 12.020 µs passou 20 µs do limite de 12.000 µs.

Assim, B2 começou atrasada e perdeu o prazo. Já A1 começou 100 µs atrasada, porém terminou em 10.800 µs, antes do limite de 11.000 µs: início atrasado não implica, sozinho, perda de prazo.

Pratique a interpretação

Verifique a perda de prazo

No registro B2, o início efetivo foi 11.100 µs, o instante previsto foi 11.000 µs e a duração foi 920 µs. O prazo termina em 12.000 µs.

A atualização terminou quantos microssegundos depois do prazo? Preencha somente o número.

Cálculo do atraso final

Atraso além do prazo: ___ µs

Interprete o registro

Qual afirmação interpreta corretamente a atualização B2?

Passo 6 de 9

Trate atrasos sem acumular trabalho vencido

Mantenha uma referência periódica, descarte liberações vencidas de forma contada e execute somente a atualização mais recente disponível.

Mantenha a referência periódica

Preserve a cadência planejada

Quando uma tarefa atrasa, definir a próxima agenda como agora + periodo desloca todas as liberações futuras. Um atraso isolado passa a determinar a cadência.

Mantenha nextDue como o próximo instante previsto. Ao encontrar uma ou mais liberações vencidas, avance essa referência em múltiplos inteiros do período. Assim, o atendimento atrasado não redefine os instantes planejados.

Referência fixa e atendimento atual

Linha do tempo com instantes previstos igualmente espaçados, liberações antigas descartadas, a liberação vencida mais recente atendida e o próximo instante previsto preservado.

A referência permanece alinhada à grade periódica; apenas a liberação mais recente vencida é atendida.

Descarte o passado; adquira no presente

Localize a liberação mais recente

Use uma definição única: nextDue é sempre a próxima liberação prevista que ainda não foi atendida nem descartada.

Quando agora alcança nextDue, calcule:

elapsed = agora - nextDue

skipped = elapsed / PERIOD_US

skipped informa quantos períodos completos se passaram depois de nextDue. A liberação atendida é:

scheduled = nextDue + skipped × PERIOD_US

As skipped liberações anteriores são descartadas. Depois de atender scheduled, a nova referência é scheduled + PERIOD_US. A aquisição é feita agora; ela não reconstrói dados dos instantes descartados.

Exemplo

Rastreamento de um atraso

Considere PERIOD_US = 1.000, nextDue = 10.000 e agora = 13.450 µs.

  • elapsed = 13.450 − 10.000 = 3.450 µs
  • skipped = 3.450 / 1.000 = 3
  • scheduled = 10.000 + 3 × 1.000 = 13.000 µs

As liberações de 10.000, 11.000 e 12.000 µs são descartadas: discarded += 3. A liberação de 13.000 µs é atendida uma única vez, com uma aquisição feita agora. Ao final, nextDue passa a 14.000 µs.

Não repita a leitura atual para fingir que ela foi adquirida nos três instantes passados.

Atenção

Limite da comparação assinada

A subtração sem sinal continua válida através do retorno de micros(), desde que o intervalo real seja menor que uma volta completa. Porém, o teste de vencimento usado adiante converte a diferença para int32_t:

(int32_t)(now - nextDue) >= 0

Essa comparação só é inequívoca na meia faixa do contador: a diferença relevante deve ser muito menor que 2^31 µs, cerca de 35,8 minutos — e PERIOD_US também deve estar nessa faixa. Na prática, consulte o agendador continuamente no loop(), nunca depois de uma pausa longa. Isso é mais restritivo que a volta completa de aproximadamente 71,6 minutos de micros().

Execute no máximo uma atualização

Avanço contado, sem recuperação em série

Trecho central com nextDue como próxima liberação pendente. Ele executa, no máximo, uma atualização em cada passagem pelo loop().

cpp
const uint32_t PERIOD_US = 1000;
uint32_t nextDue;
uint32_t discarded = 0;
uint32_t deadlineMisses = 0;

void loop() {
  uint32_t now = micros();

  if ((int32_t)(now - nextDue) >= 0) {
    uint32_t elapsed = now - nextDue;
    uint32_t skipped = elapsed / PERIOD_US;
    uint32_t scheduled = nextDue + skipped * PERIOD_US;

    discarded += skipped;
    nextDue = scheduled + PERIOD_US;

    uint32_t start = micros();
    int reading = analogRead(A0);  // aquisição nova, feita agora
    digitalWrite(LED_BUILTIN, reading > 512 ? HIGH : LOW);
    uint32_t end = micros();

    // Prazo relativo de um período contado de scheduled.
    if (end - scheduled > PERIOD_US) {
      deadlineMisses++;
    }
  }
}

Registre eventos diferentes

Há somente um if, não um laço que execute repetidamente a tarefa vencida. Mesmo que skipped seja 3 ou maior, ocorre no máximo uma atualização nesta passagem pelo loop().

discarded conta liberações que nem chegaram a executar. deadlineMisses conta outra coisa: uma atualização que foi executada, mas cujo end ficou depois de scheduled + PERIOD_US. Um descarte não é automaticamente uma perda de prazo da atualização atendida.

Pratique e reconheça o limite

Avance sem deslocar a agenda

Complete o cálculo da liberação vencida mais recente e o avanço para a próxima referência. A atualização atendida continua sendo única; as liberações anteriores são contabilizadas em discarded.

cpp
uint32_t elapsed = now - nextDue;
uint32_t skipped = ;
uint32_t scheduled = ;

discarded += skipped;
nextDue = ;
runTask(scheduled);

Rastreie o atraso

Com período de 1.000 µs, nextDue = 10.000 µs e agora = 13.450 µs, qual resultado segue a política apresentada?

Atenção

Descartes não corrigem sobrecarga

Evite usar um while para executar tarefas vencidas até “alcançar” a agenda. Essa recuperação pode monopolizar o loop(), aumentar ainda mais os atrasos e criar várias execuções com dados que não pertencem ao passado.

Descartes ocasionais podem ser uma política explícita. Porém, descartes frequentes indicam sobrecarga persistente: reduza trabalho, limite diagnósticos ou reveja o período somente se os requisitos de banda e dinâmica permitirem.

Passo 7 de 9

Instrumente uma tarefa no seu Uno

Execute localmente uma tarefa periódica instrumentada no Arduino Uno R3 e obtenha métricas iniciais de duração, latência, intervalos, descartes e perdas de prazo.

Prepare o ensaio

Preparação segura

Monte apenas este ensaio de baixa energia: conecte o Uno R3 ao computador por USB, ligue A0 ao GND e não conecte motor, ponte H ou outra carga externa. O sketch lê A0, faz um cálculo simples e atualiza o LED integrado.

Com A0 no GND, a leitura tende a ficar próxima de zero; isso torna o sinal previsível para o primeiro teste. Abra a IDE Arduino, selecione sua placa e porta e deixe o Monitor Serial em 115200 baud.

Montagem do ensaio

Diagrama de um Uno ligado somente ao computador por USB, com A0 conectado ao GND e uso do LED integrado.

Use somente USB, A0–GND e o LED integrado neste ensaio.

Dica

O que será medido

O período de referência será 10.000 µs (10 ms). As medições incluem aquisição, cálculo, saída e o trecho crítico completo. Uma interrupção que ocorra dentro de uma dessas janelas também aparece na duração medida.

Copie a tarefa medida

Bloco 1 — constantes, registros e setup

Cole este primeiro bloco no início de um novo sketch.

cpp
const uint32_t PERIOD_US = 10000UL;
const uint32_t REPORT_US = 1000000UL;

uint32_t nextDue, nextReport;
uint32_t served = 0, discarded = 0, deadlineMisses = 0;
uint32_t maxAcq = 0, maxCalc = 0, maxOut = 0;
uint32_t maxCritical = 0, maxLatency = 0;
uint32_t minInterval = UINT32_MAX, maxInterval = 0;
uint32_t previousStart = 0;
bool hasPreviousStart = false;

uint32_t reportServed, reportDiscarded, reportMisses;
uint32_t reportAcq, reportCalc, reportOut, reportCritical;
uint32_t reportLatency, reportMinInterval, reportMaxInterval;
uint8_t reportPhase = 0;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(115200);

  uint32_t now = micros();
  nextDue = now + PERIOD_US;
  nextReport = now + REPORT_US;
}

Bloco 2 — tarefa crítica instrumentada

Cole este segundo bloco depois do anterior. As marcas internas separam aquisição, cálculo e saída; a janela de start até end mede o trecho crítico completo, inclusive o custo dessas marcas e interrupções que ocorram nele.

cpp
void runTask(uint32_t scheduled) {
  uint32_t start = micros();
  uint32_t latency = start - scheduled;

  uint32_t t0 = micros();
  int raw = analogRead(A0);
  uint32_t t1 = micros();

  int scaled = raw * 3 + 7;
  uint32_t t2 = micros();

  digitalWrite(LED_BUILTIN, scaled > 100 ? HIGH : LOW);
  uint32_t end = micros();

  uint32_t acq = t1 - t0;
  uint32_t calc = t2 - t1;
  uint32_t out = end - t2;
  uint32_t critical = end - start;

  if (acq > maxAcq) maxAcq = acq;
  if (calc > maxCalc) maxCalc = calc;
  if (out > maxOut) maxOut = out;
  if (critical > maxCritical) maxCritical = critical;
  if (latency > maxLatency) maxLatency = latency;

  if (hasPreviousStart) {
    uint32_t interval = start - previousStart;
    if (interval < minInterval) minInterval = interval;
    if (interval > maxInterval) maxInterval = interval;
  }
  previousStart = start;
  hasPreviousStart = true;

  if (latency + critical > PERIOD_US) deadlineMisses++;
  served++;
}

Execute e leia o relatório

Bloco 3 — atendimento periódico

Cole este bloco. A função recalcula o instante atual ao ser chamada, preserva a referência periódica e executa no máximo uma atualização. Se você inserir mais adiante uma perturbação antes da chamada desta função, ela ainda calculará os descartes a partir do instante atual.

cpp
void serveDueTask() {
  uint32_t now = micros();

  if ((int32_t)(now - nextDue) >= 0) {
    uint32_t elapsed = now - nextDue;
    uint32_t skipped = elapsed / PERIOD_US;
    uint32_t scheduled = nextDue + skipped * PERIOD_US;

    discarded += skipped;
    nextDue = scheduled + PERIOD_US;
    runTask(scheduled);
  }
}

Bloco 4 — relatório serial espaçado

Complete o sketch com este bloco. O relatório é dividido em três linhas curtas. Cada linha só é enviada se houver espaço suficiente no buffer serial para ela inteira; assim, o envio adiado evita bloquear a tarefa por falta de espaço. Clique em Verificar e depois em Carregar.

cpp
bool sendLine(const char *line) {
  int n = strlen(line);
  if (n > 0 && Serial.availableForWrite() >= n) {
    Serial.write((const uint8_t *)line, n);
    return true;
  }
  return false;
}

void captureReport() {
  reportServed = served; reportDiscarded = discarded;
  reportMisses = deadlineMisses;
  reportAcq = maxAcq; reportCalc = maxCalc; reportOut = maxOut;
  reportCritical = maxCritical; reportLatency = maxLatency;
  reportMinInterval = hasPreviousStart ? minInterval : 0;
  reportMaxInterval = hasPreviousStart ? maxInterval : 0;
  reportPhase = 1;
}

void emitReport() {
  char line[72];
  if (reportPhase == 1) {
    snprintf(line, sizeof(line), "cont n=%lu d=%lu p=%lu\n",
      (unsigned long)reportServed, (unsigned long)reportDiscarded,
      (unsigned long)reportMisses);
  } else if (reportPhase == 2) {
    snprintf(line, sizeof(line), "dur aq=%lu ca=%lu sa=%lu cr=%lu\n",
      (unsigned long)reportAcq, (unsigned long)reportCalc,
      (unsigned long)reportOut, (unsigned long)reportCritical);
  } else {
    snprintf(line, sizeof(line), "tmp la=%lu in=%lu/%lu\n",
      (unsigned long)reportLatency, (unsigned long)reportMinInterval,
      (unsigned long)reportMaxInterval);
  }
  if (sendLine(line)) reportPhase++;
  if (reportPhase == 4) reportPhase = 0;
}

void loop() {
  serveDueTask();

  uint32_t now = micros();
  if (reportPhase == 0 && (int32_t)(now - nextReport) >= 0) {
    captureReport();
    nextReport = now + REPORT_US;
  }
  if (reportPhase != 0) emitReport();
}
Saída esperada
cont n=100 d=0 p=0
dur aq=112 ca=4 sa=8 cr=140
tmp la=16 in=9996/10008
cont n=200 d=0 p=0
dur aq=116 ca=4 sa=8 cr=144
tmp la=16 in=9996/10008

Os valores variam entre placas, versões do núcleo Arduino e condições de execução. As três linhas formam um único relatório: contadores, durações e tempos.

Confira seu ensaio

Significado dos campos

Cada conjunto de três linhas é um relatório:

  • cont: n = atualizações atendidas; d = liberações descartadas; p = atualizações executadas que terminaram após o prazo.
  • dur: aq = maior duração de aquisição; ca = maior duração do cálculo; sa = maior duração da atualização de saída; cr = maior duração do trecho crítico completo.
  • tmp: la = maior latência; in = menor/maior intervalo entre inícios.

Todos os tempos são em microssegundos. O primeiro atendimento não entra em in, pois ainda não há início anterior. A formatação e a transmissão serial ficam fora de runTask(), mas pertencem ao orçamento: se consumirem tempo, podem atrasar o atendimento seguinte.

Atenção

Intervalos ampliados exigem contexto

Um in máximo maior que 10.000 µs não é automaticamente apenas jitter. Se d também aumentou, o intervalo foi ampliado porque uma ou mais atualizações foram descartadas. Registre descartes separadamente da variação comum dos atendimentos.

Registre sua evidência

Após observar pelo menos três relatórios completos, registre os valores mais recentes de n, d, p, aq, ca, sa, cr, la e in. Houve descarte ou perda de prazo? Se os números diferirem do exemplo, isso é esperado. Informe também se A0 estava ligado ao GND.

Escreva pelo menos 40 caracteres (0/40).

Passo 8 de 9

Teste variações e revise a margem

Confronte o orçamento com condições de execução diferentes, teste a política de descarte e revise a margem com base em evidências limitadas.

Declare e separe as condições

Compare causas, não apenas máximos

Compare ensaios sob condições declaradas, mantendo a mesma placa, montagem com A0 no GND e período de 10.000 µs. Mude uma condição por vez e registre cr, la, in, d e p.

Use quatro condições: operação básica; perturbação dentro da tarefa; perturbação antes da decisão do agendador; e relatórios seriais periódicos. Uma perturbação interna tende a elevar a duração crítica (cr). Uma perturbação antes da decisão tende a elevar a latência (la) e, se atravessar períodos, pode gerar descartes (d).

A comunicação serial fica fora do trecho crítico, mas não é gratuita: formatação e escrita ocupam o loop() e podem atrasar o próximo atendimento.

Efeitos temporais distintos

Três linhas do tempo distinguem uma tarefa normal, uma tarefa com duração maior e um atraso antes da decisão do agendador.

A posição da perturbação muda a métrica predominante: dentro da tarefa aumenta a duração; antes da decisão pode aumentar latência e descartes.

Roteiro de comparação

Anote uma linha por condição. Os máximos observados só têm significado junto das condições declaradas do ensaio.

Indicadores para confrontar no sketch instrumentado.
CondiçãoAlteraçãoEfeito principal a observar
BásicaSem perturbaçãoReferência para cr, la e in
InternaEspera de 3.000 µs na tarefacr maior; componentes isolados preservados
Pré-atendimentoEspera única de 35.000 µsla, d e intervalos extremos maiores
SerialRelatórios durante a janelaPossível aumento de la ou in

Provoque efeitos mensuráveis

Aplique perturbações reproduzíveis

Faça os ensaios em execuções separadas, carregando o sketch a cada mudança de constante. A espera é proposital, finita e serve somente para testar as métricas.

Ensaio interno: use PERTURBAR_DENTRO_US = 3000 e PERTURBAR_ANTES_US = 0. A espera entra no trecho crítico completo, mas fica fora das janelas individuais de aquisição, cálculo e saída. Assim, cr deve crescer aproximadamente 3.000 µs, enquanto aq, ca e sa tendem a ficar próximos aos valores básicos.

Ensaio pré-atendimento: depois, use PERTURBAR_DENTRO_US = 0 e PERTURBAR_ANTES_US = 35000. A espera ocorre somente depois de três tarefas atendidas, para que existam inícios anteriores e posteriores para comparar.

Constantes e espera de teste

Adicione este trecho junto às constantes e funções globais do sketch da etapa anterior. Para cada ensaio, altere somente os valores das duas constantes conforme a instrução desta tela.

cpp
const uint32_t PERTURBAR_DENTRO_US = 3000UL;
const uint32_t PERTURBAR_ANTES_US = 0UL;
bool prePerturbUsed = false;

void esperaAtivaUs(uint32_t duracaoUs) {
  uint32_t inicio = micros();
  while ((uint32_t)(micros() - inicio) < duracaoUs) {
    // Espera proposital e limitada.
  }
}

Posicione as esperas

Em runTask(), insira a primeira linha após o cálculo de latency e antes de t0 = micros(). Depois, substitua serveDueTask() por esta versão. A perturbação pré-atendimento só ocorre depois de três tarefas atendidas; portanto, o intervalo ampliado poderá ser comparado com o início anterior.

cpp
// Em runTask(), insira após:
// uint32_t latency = start - scheduled;
esperaAtivaUs(PERTURBAR_DENTRO_US);

// Em seguida permanece:
uint32_t t0 = micros();

// Substitua serveDueTask() por esta versão.
void serveDueTask() {
  if (!prePerturbUsed && PERTURBAR_ANTES_US > 0 && served >= 3) {
    prePerturbUsed = true;
    esperaAtivaUs(PERTURBAR_ANTES_US);
  }

  uint32_t now = micros();
  if ((int32_t)(now - nextDue) >= 0) {
    uint32_t elapsed = now - nextDue;
    uint32_t skipped = elapsed / PERIOD_US;
    uint32_t scheduled = nextDue + skipped * PERIOD_US;

    discarded += skipped;
    nextDue = scheduled + PERIOD_US;
    runTask(scheduled);
  }
}
Saída esperada
// Ensaio interno: PERTURBAR_DENTRO_US = 3000, PERTURBAR_ANTES_US = 0
cont n=100 d=0 p=0
dur aq=112 ca=4 sa=4 cr=3132
tmp la=16 in=9996/10004

// Ensaio pré-atendimento: PERTURBAR_DENTRO_US = 0, PERTURBAR_ANTES_US = 35000
cont n=98 d=2 p=0
dur aq=112 ca=4 sa=4 cr=132
tmp la=5016 in=4860/35140

Os tempos exatos variam entre placas e condições. No ensaio interno, `cr` deve aumentar cerca de 3.000 µs, sem que a espera seja atribuída a `aq`, `ca` ou `sa`. No pré-atendimento, espere `d` próximo de 2 e `la` próximo de 5.000 µs. A grade preservada produz um intervalo longo durante o bloqueio e, logo depois, um intervalo curto até a próxima liberação prevista.

Observe o efeito da comunicação

Compare a comunicação serial

Compare a influência da comunicação sem deixar de obter um resumo. Execute duas vezes, sempre com PERTURBAR_DENTRO_US = 0 e PERTURBAR_ANTES_US = 0:

  1. Use RELATORIOS_PERIODICOS = true: há relatórios durante cinco segundos e um último resumo ao fim.
  2. Use RELATORIOS_PERIODICOS = false: as métricas são acumuladas em silêncio por cinco segundos e só então é emitido um resumo.

Em cada mudança, carregue o sketch novamente. A versão silenciosa não torna a comunicação “gratuita”; ela apenas remove os envios durante a janela para comparar seu efeito.

Janela de cinco segundos e resumo final

Faça todas estas edições: adicione as declarações globais; em setup(), acrescente a linha que inicializa testEnd; por fim, substitua loop() inteiro. A janela termina após 5 segundos nas duas condições.

cpp
// Adicione junto às variáveis globais.
const bool RELATORIOS_PERIODICOS = true;
const uint32_t TEST_WINDOW_US = 5000000UL;
uint32_t testEnd;
bool testEnded = false, finalCaptured = false;

// Em setup(), após: nextReport = now + REPORT_US;
testEnd = now + TEST_WINDOW_US;

// Substitua loop() inteiro por esta versão.
void loop() {
  serveDueTask();
  uint32_t now = micros();

  if (!testEnded && RELATORIOS_PERIODICOS && reportPhase == 0 &&
      (int32_t)(now - nextReport) >= 0) {
    captureReport();
    nextReport = now + REPORT_US;
  }

  if (!testEnded && (int32_t)(now - testEnd) >= 0) {
    testEnded = true;
  }

  if (testEnded && !finalCaptured && reportPhase == 0) {
    captureReport();
    finalCaptured = true;
  }

  if (reportPhase != 0) emitReport();
}
Saída esperada
// Com RELATORIOS_PERIODICOS = true, após cerca de 5 s:
cont n=500 d=0 p=0
dur aq=112 ca=4 sa=4 cr=132
tmp la=36 in=9996/10024

// Com RELATORIOS_PERIODICOS = false, após cerca de 5 s:
cont n=500 d=0 p=0
dur aq=112 ca=4 sa=4 cr=132
tmp la=12 in=9996/10004

Os números são representativos, não obrigatórios. A escrita é adiada quando o buffer não tem espaço para uma linha inteira, mas a formatação e os envios ainda ocupam tempo no `loop()`. Compare principalmente `cr`, `la`, `in`, `d` e `p` entre as duas execuções.

Interprete a comparação

É possível que as duas condições deem números parecidos: isso também é um resultado válido. Se a condição periódica aumentar la, o maior in, d ou p, a comunicação está disputando tempo com o próximo atendimento. A duração crítica cr pode permanecer semelhante, porque a emissão ocorre fora de runTask().

Antes de seguir para a validação, restaure PERTURBAR_DENTRO_US = 0, PERTURBAR_ANTES_US = 0 e remova ou desative a janela de teste, voltando ao loop() normal da etapa anterior.

Revise antes da validação

Atualize a margem com prudência

O maior valor medido é uma evidência das condições exercitadas e do tempo do ensaio; não prova um limite garantido para todos os caminhos futuros, interrupções ou usos da comunicação.

Revise o orçamento com a condição relevante mais exigente: duração crítica, agendamento, instrumentação, demais atividades do loop() e comunicação. Preserve uma reserva explícita. Se descartes se repetirem em condições normais, reduza trabalho ou comunicação; reveja o período apenas se banda, dinâmica e atraso aceitável continuarem atendidos.

Reconheça o limite da evidência

Qual conclusão sobre a maior duração observada é adequada?

Registre a revisão

Registre uma conclusão curta: informe qual condição elevou mais cr; qual elevou mais la, d ou os extremos de intervalo; o que ocorreu na comparação serial; e como ajustará a margem. Inclua que as perturbações foram restauradas para zero e a limitação das evidências.

Escreva pelo menos 180 caracteres (0/180).

Passo 9 de 9

Valide e registre sua decisão

Consolide período, orçamento, medições e política de recuperação em um registro técnico conciso e prudente.

1. Reúna as evidências

Verificação final

Feche o ensaio nas condições reais

Faça uma última execução sem as perturbações artificiais usadas para testar duração ou atraso. Mantenha o mesmo período, montagem e sketch que pretende usar como referência.

Registre as condições que importam: placa usada, alimentação por USB, sinais conectados, período configurado, duração aproximada do ensaio e se o Monitor Serial estava ativo. Então anote os maiores valores observados de duração e latência, os limites dos intervalos reais, descartes e perdas de prazo.

A conclusão deve separar duas afirmações:

  • Evidência observada: o que ocorreu nas condições e no tempo ensaiados.
  • Garantia: um limite demonstrado para todas as condições possíveis.

Mesmo que descartes e perdas de prazo sejam zero, isso é uma boa evidência para o ensaio realizado — não uma garantia absoluta.

O que sustenta a decisão

Diagrama de um relatório de validação reunindo linha do tempo, métricas medidas, margem e condições do ensaio.

A decisão combina requisito temporal, orçamento revisado, métricas observadas e condições declaradas.

2. Registre uma decisão auditável

Modelo conciso

Registro mínimo de decisão

Use este modelo no seu caderno técnico ou em um arquivo local. Preencha com os valores do seu ensaio final.

  1. Período escolhido: _ µs ou _ ms.
  2. Justificativa: dinâmica/banda considerada e atraso de atualização aceito.
  3. Orçamento revisado: aquisição _; cálculo _; saída _; demais atividades _; margem ___.
  4. Condições do ensaio: montagem, alimentação, duração, comunicação serial e caminhos de execução exercitados.
  5. Métricas observadas: maior duração _; maior latência _; intervalo mínimo/máximo _ / _; descartes _; perdas de prazo _.
  6. Política em atraso: atender no máximo uma vez por passagem, descartar liberações antigas, adquirir uma leitura nova e contar descartes.
  7. Conclusão e limite: o que os dados apoiam; o que ainda não está garantido.

Prefira valores com unidade. Se ainda não mediu algum valor, registre isso como pendência, não como zero.

Dica

Critério de conclusão

A tarefa é viável nas condições ensaiadas quando o orçamento revisado e as métricas finais deixam margem para operar, e a política de atraso evita recuperação ilimitada. Uma conclusão cuidadosa sempre declara suas condições e limites.

3. Decida, documente e revise

Ação corretiva

Quando o tempo não cabe

Se a duração, a latência ou as perdas de prazo mostrarem que a margem é insuficiente, não trate descartes como correção permanente. Escolha a ação que ataca a causa:

  • Reduzir trabalho: simplificar cálculos, aquisição ou caminhos condicionais desnecessários.
  • Limitar comunicação: reduzir formatação e envio serial; manter diagnósticos fora da tarefa crítica.
  • Rever o período: só aumentar o período se a banda útil, a dinâmica e o atraso aceitável ainda permitirem.

Depois da mudança, refaça orçamento e medição. Diminuir a frequência de atualização apenas para "parar os atrasos" pode prejudicar o requisito do sinal ou da resposta do sistema.

Exemplo

Sua conclusão

Seu registro final deve explicar, em poucas linhas: qual período adotou, por que ele é aceitável para a dinâmica, qual margem restou no orçamento, quais foram as métricas medidas e o que acontecerá se houver liberações vencidas. Inclua também uma frase de limite: por exemplo, “não foram observadas perdas neste ensaio; isso não demonstra um pior caso garantido”.

Aplicação final

Escreva seu registro final de decisão. Inclua: período e justificativa dinâmica; orçamento com margem; condições e métricas do ensaio final; política para atrasos; e uma limitação explícita da evidência obtida.

Escreva pelo menos 300 caracteres (0/300).

4. Fechamento

Resumo

Síntese do tutorial

  • Período programado, orçamento e métricas medidas precisam sustentar a mesma decisão.
  • A validação final ocorre sem perturbações artificiais e com condições do ensaio registradas.
  • Maior duração, maior latência, intervalos, descartes e perdas de prazo são evidências observadas, não garantia automática de pior caso.
  • Se o tempo não cabe, reduza trabalho ou comunicação; reveja o período apenas se os requisitos de dinâmica e sinal permitirem.
  • A política limitada atende no máximo uma atualização por passagem, descarta trabalho vencido e conta as ocorrências.

Tutorial concluído

Parabéns! Você concluiu: Período de atualização e orçamento de execução

Decisão temporal registrada: prossiga usando o período e as condições que você conseguiu justificar.

Baixe o Aplicativo agora para ter acesso a + de 5000 cursos gratuitos, exercícios, certificado e muito conteúdo sem pagar nada!

  • Cursos online 100% gratuitos do início ao fim

    Milhares de cursos online em vídeo, ebooks e áudiobooks.

  • Mais de 60 mil exercícios gratuitos

    Para testar seus conhecimentos no decorrer dos cursos online

  • Certificado Digital gratuito válido em todo o Brasil

    Gerado diretamente na galeria de fotos do seu celular e enviado ao seu e-mail

Aplicativo Cursa na tela de ebook, na tela de curso em vídeo e na tela de exercícios do curso, mais o certificado de conclusão de curso