Módulo 3: Exercícios Práticos de Programação em C para o PIC18F4550

Estes cinco exercícios têm um único objetivo: colocar você em frente ao compilador e ao hardware o mais rapidamente possível. Cada exercício é deliberadamente simples e concreto — você verá LEDs acendendo, botões respondendo e padrões piscando como resultado direto do código que escrever. Ao mesmo tempo, cada exercício aponta explicitamente para conceitos da ISA do PIC18F4550 estudados no material: quais instruções o compilador gera, como os dados são endereçados e por que certas construções em C resultam em código de máquina mais eficiente do que outras. Leia o enunciado com atenção, implemente o código no MPLAB X IDE com o compilador XC8, grave no kit ACEPIC PRO V8.2 e observe o resultado antes de responder às questões de reflexão.

Antes de começar, certifique-se de que seu projeto no MPLAB X está configurado com as diretivas de configuração corretas para o PIC18F4550 e com _XTAL_FREQ definida de acordo com o clock utilizado. O esqueleto de código fornecido em cada exercício já inclui o cabeçalho mínimo necessário; complete apenas as partes marcadas com /* SEU CÓDIGO AQUI */.


Exercício 1 — Primeiros Passos: Configuração de I/O e Escrita em Porta

O primeiro contato com qualquer microcontrolador começa pela configuração das portas de entrada e saída. No PIC18F4550, cada porta física (PORTA, PORTB, PORTC, PORTD, PORTE) possui um registrador de direção correspondente chamado TRIS (abreviação de tri-state): escrever 0 em um bit do TRIS configura o pino correspondente como saída, e escrever 1 o configura como entrada. A escrita em um pino configurado como saída é feita pelo registrador LAT (latch), e a leitura de um pino de entrada é feita pelo registrador PORT.

No kit ACEPIC PRO V8.2, os oito LEDs do banco principal estão conectados aos pinos RD0 a RD7 (bits 0 a 7 do PORTD). Seu objetivo neste exercício é fazer com que todos os oito LEDs acendam simultaneamente ao ligar o kit.

O esqueleto de código a seguir deve ser completado com as instruções necessárias para configurar TRISD e escrever o valor adequado em LATD:

#include <xc.h>
#include <stdint.h>

/* Configurações do processador */
#pragma config FOSC = HS
#pragma config WDT  = OFF
#pragma config LVP  = OFF
#define _XTAL_FREQ 8000000

void main(void)
{
    /* SEU CÓDIGO AQUI:
       1. Configure todos os pinos de PORTD como saída.
       2. Acenda todos os oito LEDs simultaneamente.
    */

    while (1); /* Laço infinito: mantém o estado */
}

Após fazer o programa funcionar, responda às seguintes questões de reflexão. Quando você escreve TRISD = 0x00; em C, o compilador XC8 precisa carregar o valor 0x00 no registrador W e depois transferi-lo para o endereço de memória mapeado a TRISD. Qual modo de endereçamento é utilizado para especificar o valor 0x00 (o operando imediato) e qual instrução da categoria “literais” da ISA do PIC18 realiza essa carga? Em seguida, qual instrução da categoria “operações sobre bytes” transfere o conteúdo de W para o registrador TRISD, e qual modo de endereçamento é utilizado para localizar TRISD na memória de dados?


Exercício 2 — Piscar um LED: Temporização por Software e Manipulação de Bits Isolados

Fazer um LED piscar é o equivalente embarcado do “Olá, mundo!” e exige que você combine configuração de I/O, escrita de valores em registradores e geração de atraso por software. Neste exercício, você fará apenas o LED conectado a RD0 (bit 0 de PORTD) piscar com um período visível a olho nu, deixando os demais LEDs apagados.

A função __delay_ms(t) do compilador XC8 gera um atraso de t milissegundos por meio de um laço de instruções NOP, cujo número exato de iterações depende do valor de _XTAL_FREQ. É importante que você compreenda que esse atraso é implementado inteiramente em software: o processador permanece executando instruções de espera dentro do laço, sem realizar nenhum trabalho útil nesse tempo. Em módulos futuros você aprenderá a gerar atrasos por hardware usando timers, o que libera o processador para outras tarefas.

Para acender e apagar apenas o bit 0 de LATD sem perturbar os demais bits, você pode usar as macros de acesso individual a bits fornecidas pelo XC8 (LATDbits.LATD0) ou operações de máscara com AND e OR. Complete o esqueleto a seguir para que o LED pisque indefinidamente com 500 ms aceso e 500 ms apagado:

#include <xc.h>
#include <stdint.h>

#pragma config FOSC = HS
#pragma config WDT  = OFF
#pragma config LVP  = OFF
#define _XTAL_FREQ 8000000

void main(void)
{
    /* Configure RD0 como saída e os demais pinos de PORTD
       como entrada (não conectados a LEDs neste exercício) */
    TRISDbits.TRISD0 = 0; /* RD0 = saída */

    while (1)
    {
        /* SEU CÓDIGO AQUI:
           1. Acenda o LED em RD0.
           2. Aguarde 500 ms.
           3. Apague o LED em RD0.
           4. Aguarde 500 ms.
        */
    }
}

Questões de reflexão: a instrução BSF LATD, 0, ACCESS (Set bit 0 de LATD) e a instrução BCF LATD, 0, ACCESS (Clear bit 0 de LATD) pertencem à categoria “operações sobre bits” da ISA do PIC18. Qual é a vantagem de usar BSF/BCF em comparação com a sequência MOVF + IORWF (para setar) ou MOVF + ANDWF (para limpar) quando se deseja modificar apenas um bit sem afetar os demais? Como o compilador XC8 decide qual dessas abordagens usar quando você escreve LATDbits.LATD0 = 1; versus LATD |= 0x01;?


Exercício 3 — Operações Lógicas: Máscaras de Bits e Padrões nos LEDs

As instruções de operação lógica da ISA do PIC18 — AND, OR, XOR e complemento — são fundamentais para manipular bits individuais ou grupos de bits em registradores de controle. Neste exercício você as explorará diretamente por meio de operadores bit a bit do C, observando o resultado visualmente nos LEDs.

Você implementará uma sequência de quatro estados nos LEDs do PORTD, com um atraso de 400 ms entre cada transição, repetindo a sequência indefinidamente. Os quatro estados são:

  • Estado A: apenas os quatro LEDs inferiores acesos (bits 3 a 0), ou seja, LATD = 0x0F
  • Estado B: apenas os quatro LEDs superiores acesos (bits 7 a 4), ou seja, LATD = 0xF0
  • Estado C: LEDs alternados — acesos os bits 1, 3, 5, 7 (LATD = 0xAA)
  • Estado D: complemento do Estado C — acesos os bits 0, 2, 4, 6 (LATD = 0x55)

A restrição do exercício é que você não pode atribuir diretamente os valores 0x0F, 0xF0, 0xAA e 0x55 ao LATD no laço principal. Em vez disso, deve partir do valor 0x0F no Estado A e chegar a cada estado seguinte aplicando uma única operação lógica sobre o valor atual de LATD. Descubra qual operação (e com qual máscara, se necessário) leva de A a B, de B a C e de C a D.

#include <xc.h>
#include <stdint.h>

#pragma config FOSC = HS
#pragma config WDT  = OFF
#pragma config LVP  = OFF
#define _XTAL_FREQ 8000000

void main(void)
{
    TRISD = 0x00; /* Todos os pinos de PORTD como saída */

    while (1)
    {
        /* Estado A: quatro LEDs inferiores */
        LATD = 0x0F;
        __delay_ms(400);

        /* Estado B: quatro LEDs superiores
           SEU CÓDIGO AQUI: chegue ao Estado B aplicando
           uma operação lógica sobre o valor atual de LATD */
        __delay_ms(400);

        /* Estado C: LEDs alternados (bits ímpares)
           SEU CÓDIGO AQUI: chegue ao Estado C a partir de B */
        __delay_ms(400);

        /* Estado D: complemento do Estado C
           SEU CÓDIGO AQUI: chegue ao Estado D a partir de C */
        __delay_ms(400);
    }
}

Questão de reflexão: identifique, para cada transição que você implementou, a instrução assembly equivalente gerada pelo compilador. A transição de C para D é a mais simples de todas — ela corresponde diretamente a qual instrução da categoria “operações sobre bytes” da ISA do PIC18? Por que essa instrução não precisa de uma máscara?


Exercício 4 — Leitura de Entrada: Botão e Desvio Condicional

Até aqui todos os exercícios foram de saída pura. Neste exercício você introduz a leitura de um pino de entrada e o desvio condicional — que na ISA do PIC18 corresponde às instruções de teste de bit BTFSS (Bit Test File, Skip if Set) e BTFSC (Bit Test File, Skip if Clear).

No kit ACEPIC PRO V8.2, um dos botões táteis está conectado ao pino RB0 (bit 0 de PORTB) com lógica invertida: quando o botão está solto o pino lê 1, e quando está pressionado o pino lê 0 (o botão conecta o pino ao GND). Seu objetivo é implementar o seguinte comportamento: enquanto o botão estiver solto, nenhum LED acende; enquanto estiver pressionado, todos os oito LEDs de PORTD acendem.

#include <xc.h>
#include <stdint.h>

#pragma config FOSC = HS
#pragma config WDT  = OFF
#pragma config LVP  = OFF
#define _XTAL_FREQ 8000000

void main(void)
{
    TRISD = 0x00;             /* PORTD: todos como saída  */
    TRISBbits.TRISB0 = 1;    /* RB0: entrada (botão)     */
    LATD = 0x00;              /* LEDs inicialmente apagados */

    while (1)
    {
        /* SEU CÓDIGO AQUI:
           Leia o pino RB0 e controle os LEDs conforme
           descrito no enunciado.
           Dica: use PORTBbits.RB0 para ler o estado do pino.
        */
    }
}

Após implementar a versão básica, acrescente uma segunda funcionalidade: ao detectar que o botão foi pressionado, conte quantas vezes isso ocorreu (armazene em uma variável uint8_t) e exiba o valor do contador em binário nos LEDs de PORTD. Cada pressão e soltura do botão incrementa o contador em um; quando o contador atingir 255 e for incrementado novamente, voltará a 0 naturalmente por estouro de 8 bits (overflow do tipo uint8_t).

Questão de reflexão: a instrução BTFSC PORTB, 0, ACCESS faz exatamente o que a estrutura if (PORTBbits.RB0 == 0) expressa em C. Qual é a vantagem desta instrução em relação à sequência MOVF PORTB, W / ANDLW 0x01 / BZ label para testar um único bit? Calcule, com base nos CPI de cada instrução, a diferença em ciclos de clock entre as duas abordagens para uma verificação do botão.


Exercício 5 — Endereçamento Indexado: Percorrendo um Vetor de Padrões

No material do Módulo 3, você estudou que o acesso a elementos de um array em C é compilado para endereçamento indexado ou indireto na ISA do PIC18, usando os registradores FSR e INDF com modificadores como POSTINC (acessa o endereço apontado por FSR e depois incrementa FSR automaticamente). Neste exercício, você utilizará esse conceito na prática ao criar uma sequência de animação com os LEDs.

Declare um array de 8 padrões de 8 bits na memória de dados (RAM). Cada elemento do array representa um padrão de iluminação para os LEDs de PORTD. O seu array deve conter os seguintes valores em ordem: 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF. O programa deve percorrer o array ciclicamente do primeiro ao último elemento, exibindo cada padrão nos LEDs por 150 ms antes de avançar para o próximo. Ao atingir o último elemento, volta ao primeiro.

#include <xc.h>
#include <stdint.h>

#pragma config FOSC = HS
#pragma config WDT  = OFF
#pragma config LVP  = OFF
#define _XTAL_FREQ 8000000

/* Array de padrões de LED armazenado na RAM */
const uint8_t padroes[8] = {
    0x01, 0x03, 0x07, 0x0F,
    0x1F, 0x3F, 0x7F, 0xFF
};

void main(void)
{
    uint8_t indice;

    TRISD = 0x00; /* PORTD como saída */

    while (1)
    {
        /* SEU CÓDIGO AQUI:
           Percorra o array 'padroes' do índice 0 ao 7,
           exibindo cada elemento em LATD por 150 ms.
           Ao chegar ao índice 7, reinicie a partir do 0.
        */
    }
}

Após fazer a animação básica funcionar, implemente uma segunda variação: percorra o array também no sentido inverso após atingir o último elemento (do índice 7 de volta ao índice 0), criando um efeito de “vai e vem”. Isso exige que você controle a direção do percurso com uma variável adicional.

Questão de reflexão: acesse o arquivo de listagem (.lst) gerado pelo MPLAB X após a compilação. Localize no código assembly gerado a linha correspondente à expressão LATD = padroes[indice];. Identifique se o compilador utilizou o registrador FSR com o modo POSTINC ou se calculou o endereço de cada elemento de outra forma. O uso de const na declaração do array influencia onde o compilador armazena os dados (RAM versus Flash)? Qual é a implicação prática disso para o modo de endereçamento utilizado?

Ao examinar o arquivo .lst, procure pela seção onde a variável padroes está declarada. Se o compilador a colocou na memória de programa (Flash), o acesso a ela requer instruções especiais da família TBLRD (Table Read), que pertencem à categoria “controle do processador” da ISA do PIC18. Se a colocou na RAM, o acesso é feito com as instruções convencionais de acesso a registradores de arquivo. Essa diferença é um exemplo direto de como a arquitetura Harvard modificada do PIC18F4550 — com espaços de endereçamento separados para programa e dados — afeta o código gerado pelo compilador.

Entregue um arquivo .c por grupo contendo:

  • o código-fonte completo de cada exercício implementado
  • a resposta às questões de reflexão (como comentário)
  • a seção relevante do arquivo .lst no Exercício 5 (também como comentário).

Somente 1 entrega por grupo.