Embedded ScopeUTFPR Curitiba · DAELN

SISTEMAS EMBARCADOS

Manual de uso
da plataforma

Investigue a resposta do firmware com o código, os pinos e o escalonador. Cinco estudos de caso reconstroem o que aconteceu na BlackPill simulada.

Baixar manual em PDF ↓ Depurar threads →
Sinais digitais entre os pinos de entrada, núcleo Cortex-M4 e saída

01 / Bancada • primeiros passos

Acender o primeiro LED

Comece com um sinal cujo comportamento você possa prever. Na BlackPill, tanto o botão KEY em PA0 quanto o LED em PC13 são ativos em nível baixo.

Uma execução de referência

  1. Abra a base da aula 6. Em Laboratórios da UTFPR, abra o exemplo resolvido da aula sobre nível e evento. Selecione Abrir no editor. O projeto informa STM32F411CE e CMSIS.
  2. Compile e leia o retorno. Pressione F7. Aguarde a confirmação de compilação e verifique se o nome da placa e a entrada main.c correspondem ao roteiro. Se houver erro, abra Build / comandos e examine o primeiro diagnóstico com arquivo e linha.
  3. Aplique um estímulo. Pressione F5 e abra Ver circuito. Pressione o botão de PA0, mantenha-o pressionado e solte. Volte à análise de sinais para conferir a mudança de PC13.
AçãoPA0PC13LED físico
Botão solto11Apagado
Botão pressionado00Aceso
Botão solto outra vez11Apagado
Abrir o exemplo da aula 6 ↗

02 / Editor • depuração

Encontre a linha que decide

Um projeto guarda a placa, os arquivos, a configuração e os módulos associados. Abra sua própria cópia antes de alterar um exemplo para comparar o original e o ensaio.

Do arquivo à instrução

  1. Localize a entrada. Em Meus projetos, abra o projeto. Na árvore, selecione main.c ou o arquivo de entrada indicado. A busca do editor localiza ocorrências e Ctrl + clique sobre um símbolo, ou F12, abre a definição disponível em um arquivo do projeto.
  2. Formule uma previsão. No exemplo do botão, encontre button_pressed e a escrita em GPIOC->BSRR. Preveja o nível de PC13 em cada ramo. Em Fluxograma, confira quais decisões levam à saída.
  3. Pare em um ponto observável. Compile e marque um breakpoint em uma linha mapeada no binário. Em Código de máquina, localize o endereço correspondente. Um trecho em C pode gerar várias instruções.
AtalhoAção na bancadaUso no ensaio
F7CompilarGerar o binário após uma edição
F5Executar / pausarParar a CPU para examinar estado
F11PassoAvançar uma instrução
F10PassarAcompanhar o efeito de uma chamada
Ctrl + F11RetornarSair da função atual
Ctrl + F10Até cursorAvançar até a linha selecionada
Shift + F5ReiniciarRecomeçar CPU e captura

03 / Editor • máquinas de estados

Leia a máquina desenhada pelo código

A aba Estados desenha transições reconhecidas nos arquivos C do projeto. Use o mapa para prever uma mudança, localizar a atribuição responsável e decidir onde medir.

Da seta à instrução

  1. Abra a lanterna. Na aula 11 da UTFPR, abra o primeiro exemplo no editor. Entre em Estados. Se o projeto tiver várias máquinas, use o seletor Máquina para escolher a variável e o arquivo.
  2. Localize uma transição. Siga a seta de FIXA para PISCA. Selecione a transição para abrir a linha em que o programa atribui o destino. No código, confira a condição de entrada e a origem do evento PRESSIONOU.
  3. Faça uma alteração verificável. Troque o destino PISCA por APAGADA nessa atribuição. Consulte Estados novamente. Para testar o comportamento, compile, execute e pressione KEY duas vezes. Observe PC13 e a variável estado em Watches.
RecursoO que conferir
EstadosOrigem, destino e condição extraídos do código atual. Um diagrama parcial pede leitura dos ramos que o analisador não reconhece.
FluxogramaCaminhos de execução dentro da função. O teste do botão também aparece em funções que somente tratam a entrada.
Analisador lógico e WatchesA borda realmente recebida, o estado em memória e o nível de saída ao executar o estímulo.

Na aula 15, acompanhe separadamente cada máquina cooperativa. Localize quem produz o evento de pedido e quem consome a confirmação. Anote a ordem das duas chamadas no laço principal antes de prever o resultado.

Abrir a aula da lanterna ↗Abrir a aula de máquinas cooperativas ↗

04 / Circuito • estímulos

Do pino ao periférico

Ver circuito abre Chip e conexões com o projeto da prática. Ali o botão, o acelerômetro e o display respondem ao programa em execução. Montagem no protoboard apresenta a fiação para reproduzir o circuito físico.

Montagem do inclinômetro da bancada. O MPU6050 usa PB8 para SCL e PB9 para SDA. O adaptador recebe os dados transmitidos por PA2.
Montagem do inclinômetro da bancada. O MPU6050 usa PB8 para SCL e PB9 para SDA. O adaptador recebe os dados transmitidos por PA2.

Faça o mesmo percurso nos dois ambientes

Na aula 9, abra o circuito, execute o programa e mova o controle virtual do MPU6050. No terminal, acompanhe a leitura de Ax. No LED em PC13, observe o estado do alarme durante a ida e a volta pelo mesmo ângulo.

Na folha de montagem, siga os nomes das redes e confira alimentação, GND e os terminais do modelo do sensor. O botão KEY e o LED PC13 já estão na placa. Ao alterar um pino em Módulos externos, confira também a configuração correspondente no código.

Abrir o contador de lotação no ST7789 ↗

05 / Display • ensaio guiado

Investigue o contador de lotação

Um visitante pressiona KEY ao entrar. O painel ST7789 conta uma entrada por acionamento válido, mostra quantas vagas foram ocupadas e muda de estado quando atinge a capacidade.

Capturas do display geradas pela execução do firmware CMSIS no simulador. Três entradas deixam o painel aberto. Na oitava, a última célula fica vermelha e aparece LOTADO.
Capturas do display geradas pela execução do firmware CMSIS no simulador. Três entradas deixam o painel aberto. Na oitava, a última célula fica vermelha e aparece LOTADO.
  1. Abra a atividade e compile. Na aula 10, selecione Exemplo aplicado e abra Ver circuito. Use F7 e F5. No módulo ST7789, amplie a tela. Comece com 0/8 e a tecla solta.
  2. Separe pressão e soltura. Pressione e solte KEY três vezes, deixando o contato estabilizar entre as ações. Espere 3/8. Mantenha a quarta pressão e observe o valor até soltar. O código usa 20 ms para validar a mudança de estado do botão.
  3. Teste o limite. Complete oito entradas e tente uma nona. Registre o número, o estado do painel e a cor da última célula. Para experimentar outra capacidade, altere CAPACIDADE em main.c e recompile.
Bancada da aula 10 antes da execução. O módulo à esquerda usa SPI. O botão à direita representa KEY, integrado à BlackPill em PA0.
Bancada da aula 10 antes da execução. O módulo à esquerda usa SPI. O botão à direita representa KEY, integrado à BlackPill em PA0.
Abrir o circuito do contador ↗

06 / Instrumentos • analisador lógico

Meça entre duas bordas

Escolha os canais conforme a pergunta. Para investigar o tempo de resposta de uma saída GPIO, é preciso registrar o instante de entrada e a transição de saída correspondente.

  1. Defina os canais antes de executar. Na aula 6, abra Analisar sinais e selecione PA0 e PC13. Ajuste Janela para que as duas transições caibam na mesma captura.
  2. Capture o evento. Execute, pressione e solte KEY. Use Congelar para reter o trecho. Última borda · CH1 ajuda a encontrar uma transição recente do primeiro canal.
  3. Posicione A e B. Clique na descida de PA0 e depois na descida de PC13. A diferença B − A expressa o intervalo de resposta observado. Para medir período, use duas bordas consecutivas de mesma polaridade no mesmo canal.
Exemplo de leitura em passos do motor. A resposta ocupa 12 passos entre as bordas escolhidas. Os números ilustram a medição.
Exemplo de leitura em passos do motor. A resposta ocupa 12 passos entre as bordas escolhidas. Os números ilustram a medição.

Conserve a unidade original

Passos do motor indexam a execução simulada. Quando o painel também apresenta segundos simulados ou ticks do kernel, registre o valor e a unidade exibidos. Uma escala com ≈ indica estimativa. Duas bordas visualmente coincidentes podem ficar separadas no CSV.

07 / Thread Viewer • leitura da captura

O que o escalonador consegue mostrar

Comece pela decisão que deseja explicar. Qual tarefa estava pronta quando a CPU ficou livre? Para que ela precisava de CPU? A resposta costuma exigir o estado do kernel, um evento do programa e uma indicação do periférico.

Captura da sirene com duas perdas completas. A barra roxa mostra Sirene selecionada. × marca o prazo vencido, a hachura termina na subida verde de PB1 e ! marca o registro posterior. ×2 reúne duas seleções próximas na mesma linha.
Captura da sirene com duas perdas completas. A barra roxa mostra Sirene selecionada. × marca o prazo vencido, a hachura termina na subida verde de PB1 e ! marca o registro posterior. ×2 reúne duas seleções próximas na mesma linha.
  1. Preserve a ocorrência. F7 compila e F5 executa. Em Editor e sinais, abra Threads. Congelar captura retém o trace. Para confrontar o trace com um contador do programa no mesmo instante, use Pausar CPU antes de ler Progresso da prática.
  2. Encontre a tarefa no instante escolhido. Use Enquadrar para localizar o conjunto retido. Abra Histórico de escalonamento, filtre por nome e localize uma seleção. Amplie com a roda ou o gesto de zoom e arraste para mover a janela. Clique em uma barra e consulte Estados das threads. O cabeçalho indica cursor, fim da janela ou captura atual.
  3. Meça duas coisas separadamente. Em Medir tempo · A/B, Posicionar A e Posicionar B definem o intervalo. Alinhar às seleções facilita medir um despacho inteiro. Para usar as bordas de um GPIO, desmarque o alinhamento. Ampliar A–B mostra a região em detalhe.
Abrir o projeto da primeira investigação ↗

08 / Investigação 01 • espera por um evento

Por que Traffic desaparece?

Na travessia de pedestres, Button ganha pequenos intervalos de CPU enquanto o botão está solto. Traffic tem prioridade maior e passa quase todo esse período sem uma barra. Verifique se está aguardando uma requisição válida.

src/main.csample_button · signal_traffic · requestButton P1 lê PA0. Traffic P2 controla o sinal. O projeto usa um semáforo binário.
  1. Prepare o controle. Abra a prática Travessia de pedestres da trilha intermediária e clique em Abrir projeto da prática. Mantenha POLL_MS em 100 e KEY solto. Compile, execute e, em Threads, congele um trecho depois da inicialização.
  2. Leia a janela sem estímulo. Filtre Button em Histórico de escalonamento. Ache duas consultas vizinhas, distante a primeira da inicialização. Confira que reaparecem por volta de 100 ticks (0,100 s). Consulte Traffic em Estados das threads entre as consultas. Ela deve estar em Bloqueadas.
  3. Produza uma liberação. Volte ao vivo. Em Controles do circuito, pressione Pedido · PA0 e mantenha até acender o âmbar em PB1. Solte a tecla. Congele a captura. Em Latência de E/S, escolha PA0 / Descida e PB1 / Subida e use Medir na linha do tempo. Procure a seleção de Traffic logo após a leitura de Button.
  4. Encontre a condição no código. Em sample_button, lab_button() lê PA0. Só a transição false→true chama xSemaphoreGive(request). Em signal_traffic, xSemaphoreTake(request, portMAX_DELAY) libera a continuação que acende o âmbar. Segurar a tecla não gera um novo pedido a cada consulta.
Abrir a prática da travessia ↗

09 / Investigação 01 • repetir com a fase controlada

Meça a espera pela próxima leitura

Faça a mesma pressão em duas fases do laço de Button. O experimento separa a espera pela consulta do tempo entre a consulta e a saída. Use o firmware intermediário da página anterior.

  1. Pegue a amostra rápida. Com a CPU parada e PA0 solto, marque breakpoint na linha int pressed = lab_button(); de sample_button. Execute até parar nessa linha. Retire o breakpoint, pressione PA0 no circuito e mantenha a tecla enquanto continua a CPU. Solte após o âmbar.
  2. Pegue a amostra lenta. Reinicie com PA0 solto. Desta vez pare na linha xTaskDelayUntil(&next, pdMS_TO_TICKS(POLL_MS)), depois da leitura. Repita a pressão, volte a executar e capture as mesmas duas bordas.
  3. Reduza o intervalo de consulta. Em src/main.c, troque apenas POLL_MS de 100 para 20. Recompile e repita as duas fases. Para voltar à referência, restaure 100 e recompile.
Fase da pressãoPOLL_MS = 100POLL_MS = 20
Antes da leitura≈ 0,213 tick · 0,000213 s≈ 0,213 tick · 0,000213 s
Depois da leitura≈ 99,920 ticks · 0,099920 s≈ 19,920 ticks · 0,019920 s
Execução de 100 ms com pressão após a consulta. O detalhe amplia os últimos 0,211 ms entre a leitura de PA0 e a saída PB1. Os instantes em ms usam o relógio simulado.
Execução de 100 ms com pressão após a consulta. O detalhe amplia os últimos 0,211 ms entre a leitura de PA0 e a saída PB1. Os instantes em ms usam o relógio simulado.

10 / Investigação 02 • prazo de ponta a ponta

Localize o atraso da sirene

O requisito começa quando o sensor aceita o pedido e termina quando o alarme liga. A Sirene trabalha por 400 ms, mas a resposta pode levar mais de 2,8 s. Descubra em qual tarefa esse tempo foi gasto.

src/main.c + include/sinais_prazo.hsensor · painel · sirene · entregar_respostaSensor P4 acorda Painel P3 e Sirene P2 a cada 5.000 ms. O prazo relativo é 1.500 ms.
  1. Capture uma solicitação completa. Abra o Exemplo resolvido da aula 20. Compile e execute até o primeiro registro “Sirene ligada” no Terminal serial. Pause a CPU, abra Threads e congele a captura. Em Ocorrência, escolha o primeiro pedido e use Ir ao atraso.
  2. Meça o atraso com A/B. Em Medir tempo · A/B, desmarque Alinhar às seleções. Com Ir ao atraso já ampliado, coloque A sobre a marca de vencimento × e B em Sirene ligada · PB1 ↑. Leia B−A. O pedido anterior não tem glifo próprio na linha da Sirene. Para o intervalo completo, use o registro da ocorrência ou o terminal.
  3. Leia três instantes distintos. Amostra #1: o pedido chegou no tick 1, o vencimento caiu no tick 1.501 e PB1 subiu no tick 2.802. A/B sobre a hachura mede o atraso de 1.301 ticks (1,301 s). Do pedido até PB1, a resposta totalizou 2.801 ticks (2,801 s).
  4. Explique de onde vieram os números. No histórico, encontre Sirene selecionada no tick 2.402. Do pedido à primeira seleção passaram 2.401 ticks, e daí à resposta, 400. O “!” é o registro escrito pelo firmware, 59 passos depois da borda PB1 neste ensaio. A primeira barra de Sirene começa antes de o alarme estar ligado.
Abrir o firmware de referência da sirene ↗

11 / Investigação 02 • intervenção sob controle

Mantenha o painel aceso e libere a CPU

O painel precisa manter a saída azul por 2,400 s. Compare as mesmas duas solicitações, com a mesma prioridade e o mesmo tempo de luz, alterando somente a maneira como painel() espera.

Em painel(), dentro do laçoVersão de referênciaocupar_janela(ticks_ms(ANIMACAO_PAINEL_MS));Alteração experimentalvTaskDelay(ticks_ms(ANIMACAO_PAINEL_MS));
  1. Abra a segunda versão. O Exemplo aplicado da aula 20 já contém a substituição. Compare src/main.c, confirme PRIO_PAINEL = 3 e PRIO_SIRENE = 2, recompile e execute a primeira solicitação nas mesmas condições.
  2. Confira o efeito no painel. No terminal, leia “Sirene ligada em 401 ms”. Uma ocorrência atendida deixa de mostrar Ir ao atraso. Consulte a tabela de prazos do relatório e examine o canal azul PB10. Nas duas versões a subida ocorreu no tick 2 e a descida no tick 2.402.
Primeiro pedido em duas execuções CMSIS + FreeRTOS a 1.000 Hz. O marcador de vencimento parte da mesma liberação. Os valores são medições do simulador.
Primeiro pedido em duas execuções CMSIS + FreeRTOS a 1.000 Hz. O marcador de vencimento parte da mesma liberação. Os valores são medições do simulador.
Abrir a versão da aula 20 com espera bloqueante ↗Relatório exportado · espera ocupada ↗Relatório exportado · espera bloqueante ↗

12 / Investigação 02 • depuração em instruções

Pare a CPU antes de a sirene ligar

A aba Threads mostra quando a Sirene executou. Para achar a instrução que muda PB1, use uma segunda execução com breakpoint. Guarde a medição contínua da página anterior antes de começar.

  1. Pare no código que entrega a resposta. Na aula 20, abra src/main.c e encontre a chamada entregar_resposta("Sirene ligada", inicio) em sirene(). Siga para include/sinais_prazo.h. Use F7 para compilar. Clique no texto da linha led_externo(verde, 1); e pressione F9 para marcar o breakpoint. Era a linha 73 no build medido. Pressione F5 para executar até parar. Se o modo de depuração ficar lento, teste a versão corrigida, em que a chamada ocorre mais cedo.
  2. Veja qual contexto foi interrompido. Sem retomar, em Threads consulte a tarefa selecionada. Em Watches & breakpoints adicione PC e PSP. PC permite cruzar a instrução com Código de máquina; PSP é o ponteiro da pilha de processo neste contexto de tarefa. F11 executa uma instrução. Siga a chamada até a escrita em GPIOB→BSRR.
  3. Observe o pino mudar. Adicione a expressão mem32(0x40020414) & 2. O endereço é GPIOB→ODR na STM32F411. A máscara 2 isola PB1. Neste build, o PC parou em 0x080005E0 e a watch mostrou 0. Após sete F11 ainda valia 0. No oitavo passo, mostrou 2 e o PC chegou a 0x080005B2. Outra compilação pode mudar os endereços e a contagem de passos.
CampoO que escrever no caderno
PC antes / depoisEndereços exibidos por seu build, instrução de escrita e tarefa selecionada
PB1 antes / depoisWatch 0 antes de PB1 ligar e 2 depois da escrita em BSRR
Evento de programaA saída muda antes da mensagem “Sirene ligada” no terminal
Abrir o firmware para depurar ↗

13 / Investigação 03 • fila e capacidade

A fila copiou ou perdeu o pulso?

Uma produtora envia três estruturas em rápida sequência e reutiliza o mesmo espaço de memória. Verifique a cópia pelo conteúdo que a consumidora realmente recebe. Use a aula 17, Exemplo aplicado, com pulsos P, M e G.

src/main.cproduzir · exibir · pulsos · ultimo_rascunhoProdutora P2 envia com portMAX_DELAY. Consumidora P1 alterna PC13 e escreve na serial.
  1. Preveja a sequência. Localize Pulso rascunho, a atribuição rascunho = sequencia[i] e xQueueSend(pulsos, &rascunho, portMAX_DELAY). Logo depois, produzir troca o nome por “?” e a duração por 1. Se a fila guardasse somente o endereço dessa variável, os dados poderiam mudar antes da leitura.
  2. Meça três saídas. Compile, execute e aguarde dois grupos no terminal. No Analisador lógico, escolha PC13 e congele. A queda acende o LED. Meça queda→subida no primeiro P, M e G. Em Threads, localize o tick 50 dentro de P e leia o estado das duas tarefas.
  3. Relacione saídas e cópia. Confirme no Terminal serial o registro Pulso P, Pulso M, Pulso G. O comprimento das três saídas confirma que os valores originais de duração também chegaram. Na capacidade 2, o terceiro envio pode suspender a produtora até a consumidora retirar o primeiro item.
ItemQueda e subida de PC13Duração medida
Ptick 1 → tick 7675 ticks · 0,075 s
Mtick 226 → tick 376150 ticks · 0,150 s
Gtick 526 → tick 826300 ticks · 0,300 s
Abrir a fila P/M/G da aula 17 ↗

14 / Investigação 03 • pressão sobre a fila

Meça o custo de retirar uma vaga

A saída começa pelo mesmo grupo P/M/G com uma e duas vagas. Para testar a espera por espaço, meça o intervalo entre o início de dois grupos e descubra quando a produtora pôde sair do terceiro envio.

Em main(), altere apenas a capacidadeVersão de referênciapulsos = xQueueCreate(2, sizeof(Pulso));Alteração experimentalpulsos = xQueueCreate(1, sizeof(Pulso));
  1. Registre primeiro o período original. Antes de executar, no Analisador lógico ajuste Janela para 4.194.304 passos e escolha PC13. Congele assim que surgir o segundo P na serial. Marque A na queda que inicia P e B na queda que inicia o P seguinte, com M e G entre eles. Com duas vagas, os ticks foram 1 e 1.802. O clique no gráfico dá uma leitura aproximada.
  2. Repita e procure o desbloqueio. Troque a capacidade para 1, recompile e reinicie. Congele o primeiro grupo. Em Histórico de escalonamento, localize a seleção de Produtora no tick 226. Ela volta a executar quando a consumidora retira M e abre a vaga necessária para enviar G.
  3. Compare períodos equivalentes. Com uma vaga, o segundo P começa no tick 2.027. O período medido cresceu de 1.801 para 2.026 ticks (1,801 para 2,026 s). Os pulsos conservaram as larguras. Restaure a capacidade 2 e confira o resultado original.
Quedas de PC13 e estado do produtor nas duas execuções. A alteração do período entre grupos aparece 225 ticks depois, embora os pulsos do primeiro grupo coincidam.
Quedas de PC13 e estado do produtor nas duas execuções. A alteração do período entre grupos aparece 225 ticks depois, embora os pulsos do primeiro grupo coincidam.

15 / Investigação 03 • contagem de descartes

Diferencie fila cheia de mensagem perdida

Mude para o Exemplo resolvido da aula 17. As mensagens desta versão se chamam A, B e C e têm outras durações. Agora a pergunta é quantas tentativas viram saídas quando a produtora desiste de uma fila cheia.

  1. Conte três lotes sem perda. Mantenha a fila com 2 vagas. Em src/main.c, observe xQueueSend com portMAX_DELAY e o intervalo de 1.800 ms entre os grupos. Execute até o tick 5.000 (5,000 s). No terminal, conte três aparições de cada letra. As larguras de PC13 foram 100, 250 e 500 ticks.
  2. Torne o descarte observável. No envio em produzir, use o trecho abaixo. Ele faz a mesma tentativa e informa qual item não conseguiu entrar. O argumento 0 devolve o controle logo se não houver vaga. Recompile e parta do reset com as mesmas prioridades.
Substitua a única chamada a xQueueSend em produzir()
if (xQueueSend(pulsos, &sequencia[i], 0) != pdPASS) {
    char msg[] = "Descartado A\r\n";
    msg[11] = sequencia[i].nome;
    lab_print(msg);
}
PC13 no primeiro grupo do ensaio com espera indefinida e com timeout zero. A duração do pulso corresponde à mensagem que a consumidora retirou.
PC13 no primeiro grupo do ensaio com espera indefinida e com timeout zero. A duração do pulso corresponde à mensagem que a consumidora retirou.
Abrir a referência A/B/C da aula 17 ↗

16 / Investigação 04 • mutex e dependência circular

Duas tarefas bloqueadas provam deadlock?

Uplink e Archive fazem transferências que precisam de rádio e armazenamento. Com a CPU em IDLE e as duas tarefas bloqueadas, uma fotografia isolada é compatível tanto com espera normal quanto com travamento.

src/main.cuplink · archive · complete_transfer · completed_transfersO objeto radio e o objeto storage são mutexes distintos. A compilação inicial usa FIX_CONCURRENCY = 0.
  1. Cruze contador e tempo. Execute até depois de 2.000 ticks (2,000 s). Em Threads, pause a CPU e leia Progresso da prática → Transferências concluídas. Na serial, as duas tarefas anunciaram o recurso que ainda precisam. Localize os dois últimos pedidos.
  2. Reconstrua o ciclo a partir do código. Em uplink, a tarefa toma radio, espera 400 ms e tenta storage sem devolver radio. Em archive, o ramo original toma storage, espera 200 ms e tenta radio. Cada segunda chamada a xSemaphoreTake usa portMAX_DELAY. Nenhuma das tarefas alcança a devolução do primeiro recurso.
  3. Quebre apenas uma dependência. Mude #define FIX_CONCURRENCY 0 para 1. Recompile e repita. Agora as duas tomam radio primeiro e storage depois. Confira os dois registros “transfer OK” e o avanço de completed_transfers nas mesmas janelas.
As setas de espera e posse vêm da ordem das chamadas a xSemaphoreTake. As prioridades indicadas são as de criação.
As setas de espera e posse vêm da ordem das chamadas a xSemaphoreTake. As prioridades indicadas são as de criação.
Abrir o projeto dos dois mutexes ↗

17 / Investigação 04 • verificação independente

Prove se as transferências avançam

Repita o código defeituoso e o corrigido até o mesmo tick. As mensagens de aquisição mostram as intenções das tarefas. O contador em complete_transfer() responde à pergunta decisiva sobre a conclusão do trabalho.

Transferências concluídas nas simulações com FIX_CONCURRENCY = 0 e 1. O incremento conta uma transferência concluída, independentemente de qual tarefa executou.
Transferências concluídas nas simulações com FIX_CONCURRENCY = 0 e 1. O incremento conta uma transferência concluída, independentemente de qual tarefa executou.
Se os contadores…Confira na serial e no código
permanecem em 0Procure as duas mensagens de pedido e a ausência de transfer OK. Localize a segunda aquisição que cada tarefa espera.
aumentam de 0 para 4Procure uma conclusão de cada origem. Confira em archive que a ordem de aquisição agora coincide com a de uplink.
  1. Corrobore a correção. Em cada versão pause a CPU depois de 2.000 ticks, leia Transferências concluídas e confira as últimas mensagens na serial. Após a correção, aparecem Uplink: transfer OK e Archive: transfer OK. Só volte a executar depois de copiar os dois registros.
  2. Distingua a espera temporária. Volte aos instantes de 500 e 1.500 ticks. Ao ver as duas tarefas bloqueadas, consulte o contador nas observações vizinhas. Se há incremento, elas conseguiram sair da espera entre suas leituras.

18 / Investigação 05 • privação de CPU

Report está pronta. Por que não executa?

A aquisição continua gerando amostras e o relatório deixa de avançar. Para distinguir de um deadlock, procure quem está usando a CPU enquanto o contador de relatórios permanece imóvel.

src/main.cacquisition · report · sample_count · report_countAcquisition P3 espera a hora de uma amostra. Report P1 deveria alternar o LED a cada 250 ms.
  1. Capture após a partida. Execute com FIX_CONCURRENCY em 0. Aguarde “REPORT ready” e duas mensagens “ACQUIRE sample”. Pause em Threads. Em Estados das threads, procure Report em Prontas. Abra um trecho posterior à partida e descubra qual tarefa aparece nas seleções.
  2. Identifique por que a prioridade importa. No laço de acquisition, a leitura repetida de xTaskGetTickCount() mantém P3 pronta até o próximo período. Em Progresso da prática, confira os contadores após 500, 1.000, 1.500 e 2.000 ticks. No binário original, só o contador de aquisição evolui.
  3. Teste a espera periódica. Troque FIX_CONCURRENCY para 1. O ramo corrigido chama xTaskDelayUntil(&last_release, period). Recompile, repita a janela e confira “REPORT advanced” e as alternâncias de PC13. A espera entre amostras deixa tempo para Report trabalhar.
Contador de relatórios em quatro instantes da execução. Na versão de espera ocupada, a aquisição continuava produzindo amostras mesmo com o contador de relatórios parado.
Contador de relatórios em quatro instantes da execução. Na versão de espera ocupada, a aquisição continuava produzindo amostras mesmo com o contador de relatórios parado.
Abrir o projeto de starvation ↗

19 / Diagnóstico adicional • ciclo de vida

Quando só resta IDLE

No exemplo Duas tarefas, duas pilhas, a janela recente pode exibir exclusivamente IDLE. Antes de concluir que o scheduler parou, descubra se as tarefas ainda deveriam existir.

  1. Examine o projeto. Na galeria de projetos de exemplo, procure Duas tarefas, duas pilhas. Small tem prioridade 2 e Large tem prioridade 1. Em src/main.c, encontre a medida da menor folga de pilha e o encerramento explícito vTaskDelete(NULL) de cada tarefa.
  2. Recupere o início da execução. Depois que o terminal imprimir as duas medições, pause a CPU. Em Threads, procure Small → Large → IDLE em Últimas trocas. Enquadrar inclui a longa execução de IDLE e pode comprimir as primeiras seleções. Abra Histórico de escalonamento e use o filtro Small ou Large para localizar cada despacho.
  3. Verifique o fim de vida. O ensaio reteve os três despachos. Ao inspecionar os dois contextos após o retorno das respectivas tarefas, a condição é Excluída. O aviso de registros anteriores descartados só aparece quando houve descarte. Neste caso as chamadas de vTaskDelete explicam a ausência posterior.
  4. Varie uma reserva. Mude o argumento de pilha de Small de 256 para 384 na chamada a xTaskCreate, recompile e reinicie. Preserve as 512 palavras de Large. Compare a folga mínima em palavras, a ordem de seleção e o número de relatórios da serial.
ExecuçãoMenor folga de SmallMenor folga de Large
Reservas 256 / 512 palavras232 palavras488 palavras
Reservas 384 / 512 palavras360 palavras488 palavras

20 / Thread Viewer • limites e registro

Quando falta uma barra na captura

O histórico conserva uma quantidade finita de seleções. Uma janela curta, um período longo de bloqueio ou a perda dos registros mais antigos produzem telas parecidas. Faça estes testes antes de apontar uma falha no código.

SintomaVerificaçãoComo avançar
Linha vazia, LED funcionalHistórico de escalonamento, filtro por nome, em Toda a execuçãoLocalize uma seleção retida e amplie a janela ao redor dela. Confira a tarefa que escreveu o GPIO.
Tarefa aparece em BloqueadasVerifique o instante no cabeçalho de Estados das threadsLeia a chamada que pode bloquear e procure a liberação por evento, tempo ou recurso.
Uma barra longa parece únicaVerifique ×N e o número de seleções mostradoAmplie junto ao ponto de interesse. As barras compactadas agrupam seleções vizinhas.
O cursor não mostra estadoConfira se o painel informa histórico descartado ou sem registroCapture uma nova ocorrência. A faixa de estados retidos pode diferir do histórico de despachos.
O rastreio não iniciaConfira se o build é do projeto, se o scheduler iniciou e se o binário possui os símbolos do kernel reconhecidoUse o terminal e breakpoints em xTaskCreate e vTaskStartScheduler para localizar uma falha de partida.

O que fica guardado no FreeRTOS

Nesta versão, o modelo mantém até 65.536 seleções no histórico estendido e 1.024 seleções no conjunto recente. O histórico de mudanças de estado tem retenção própria. O tempo coberto varia com a taxa de eventos. Fixar lista congela a visualização daquele histórico. Exportar relatório em Threads conserva a análise da janela selecionada.

Baixar os resultados de referência em CSV ↗

21 / Arquitetura • memória e exceções

Siga o dado dentro do núcleo

Comece pela pergunta que o código permite responder. Um registrador mostra o valor produzido pela instrução. A memória mostra o que foi efetivamente gravado no endereço.

Experimento com uma chamada de função

  1. Localize a rotina da aula 4. Abra a instrução PUSH e anote o MSP antes da sua execução. Na BlackPill F411, a SRAM começa em 0x20000000. A aba Memória & pilhas permite consultar o endereço indicado pelo ponteiro.
  2. Avance uma instrução. Use F11. Ao salvar dois registradores de 32 bits, a diferença entre o valor antigo e o novo do MSP é de 8 bytes. Leia as duas palavras e identifique a cópia do endereço de retorno.
  3. Acompanhe o retorno. Avance até POP e confira MSP e PC. Em Arquitetura, siga a instrução e o caminho de dados que o modelo representa. Compare a leitura da SRAM com os endereços efetivamente acessados.
PerguntaInstrumentoRegistro a guardar
Qual instrução vem a seguir?Código de máquina / CPUEndereço no PC e instrução
Quais bytes mudaram?Memória & pilhasEndereço, largura e conteúdo
Por que a CPU entrou na ISR?NVIC / EventosInterrupção, prioridade e estado
O que o programa reservou?Arquitetura / memóriaCapacidade da placa e regiões informadas pelo build
Abrir o exemplo de pilha ↗

22 / Documentação • entrega

Registre um ensaio repetível

A trilha UTFPR reúne 20 aulas. Cada uma apresenta dois exemplos resolvidos e três laboratórios, A, B e C. São 40 exemplos e 60 laboratórios. Entregue um relatório por aula, com as três respostas e as evidências correspondentes.

Campo do registroExemplo de preenchimento
Projeto e versãoUTFPR 06, Laboratório A, main.c, cópia do projeto usada no ensaio
ConfiguraçãoBlackPill STM32F411CE, botão PA0 e LED PC13 ativos em nível baixo
EstímuloPartir com KEY solto, pressionar, manter e soltar uma vez
CritérioVerificar a sequência 1 → 0 → 1 em PA0 e a resposta em PC13
Resultado próprioInstantes A e B, diferença, unidades e captura com ambos os canais
InterpretaçãoComparar com a previsão e justificar a discrepância observada

Prepare o arquivo para entregar

  1. Preencha o relatório da aula. Registre identificação e, em A, B e C, descreva o procedimento, os parâmetros, a medição e a interpretação. Use capturas que mostrem canais, escalas e cursores.
  2. Exporte e reabra o HTML. Confira, fora do simulador, se as figuras estão legíveis e se as três respostas estão completas. Envie o arquivo e o projeto pelo canal indicado pelo professor.
  3. Preserve os arquivos de trabalho. Meus projetos e os rascunhos usam o armazenamento local do navegador. Baixe o projeto JSON para reabrir na bancada. Exporte o ZIP para continuar no ambiente de desenvolvimento quando necessário.
Abrir o manual de laboratórios da disciplina ↗

23 / Placa real • validação

Leve o ensaio para a placa

A sequência de instruções, as operações de registrador e os estados de periféricos apoiam a investigação no simulador. A latência e as formas de onda de uma montagem física dependem da placa, do clock, da carga e do sensor utilizado.

Preparação e gravação

  1. Confirme alvo, binário e montagem. Identifique o modelo gravado no encapsulamento, o endereço de Flash previsto e a revisão dos módulos. Abra Montagem no protoboard. Faça as ligações com a placa desenergizada e confira as referências de GND.
  2. Escolha a interface. Em Placa real, siga o procedimento específico da placa e do gravador. Na BlackPill por USART1, use adaptador USB–UART TTL de 3,3 V. TXD do adaptador vai a PA10, RXD a PA9 e GND a GND. Se a placa estiver alimentada pelo USB, deixe o VCC do adaptador desconectado.
  3. Grave e verifique. Coloque BOOT0 em 1 e dê RESET para entrar no carregador. Selecione a porta, aguarde a identificação e confira o resultado da verificação da memória. Depois, retorne BOOT0 a 0 e dê RESET para executar o firmware.

Dois pontos de prova, um mesmo estímulo

Para medir a resposta do botão, conecte dois canais do instrumento de bancada a PA0 e PC13, com referência comum. Use a borda de descida de PA0 como disparo. Registre a taxa de amostragem, a alimentação e a diferença até a descida de PC13. Repita as medidas para observar sua dispersão.

Abrir a bancada em Placa real ↗