Botão pulsador e LED

MicrocontroladorPICPIC16F887MPLABX-XC8SimulIDE

Agora que sabemos como acionar uma saída digital, vamos fazer a leitura de uma entrada também digital, utilizando um botão pulsador (push button) para ligar um LED.

Toda construção de programa deve ser feita de forma incremental, iniciando com uma estrutura básica e agregando parte a parte do código até atingir o objetivo.

Objetivo

Acionar um LED copiando o estado lógico de um botão.

Conteúdo

  • Configuração e leitura de pino como entrada digital;
  • Arquivo de cabeçalho;
  • Estrutura condicional if e else;
  • Estrutura de repetição infinita while( 1 );
  • Definições;
  • Simplificação de código.

Circuito (Hardware)

O circuito para atender o objetivo possui um LED como elemento de saída e um botão pulsador como elemento de entrada.

Na saída temos uma saída configurada como fonte (source), como tratado em C1 - Pisca LED.

A entrada deve possuir um estado lógico bem definido, mas um botão pulsador não garante esse estado lógico, pois possui apenas dois contatos, podendo sua configuração ser normalmente aberto (NA) ou normalmente fechado(NF). Na Figura 1 é utilizado um botão pulsador NA, que é a configuração mais comum.

Para garantir o tal estado lógico bem definido, é montado um ramo com o botão e um resistor, geralmente com 10kΩ, ligados em série, entre o Vcc e o GND. No ponto de conexão entre esses dois componentes temos, em relação ao terra (GND), uma tensão que é a tensão do componente conectado ao terra. Este ponto é conectado ao pino do uC, assim a tensão no pino é a mesma tensão sobre o componente ligado ao terra.

As duas configurações possíves são mostradas na Figura 1(a) e 1(b), em que o botão está conectado ao Vcc e a outra em que o botão está conectado ao terra.

Figura 1(a): Resistor de Pull-down Figura 1(b): Resistor de Pull-up
circuito circuito

Na configuração pull-down o resistor está conectado ao terra, o que garante, quando o botão estiver aberto, o nível lógico 0 (terra/GND).

Ao pressionar o botão, ele fecha e conecta o ponto de conexão com o pino ao Vcc, aplicando o nível lógico 1 a ele.

Na configuração pull-up o resistor está conectado ao positivo da fonte, Vcc. O botão na condição de não acionado está com contato aberto, assim a tensão sobre ele é a tensão da fonte, nesse caso 5V, conforme a 2ª lei de Kirchhoff, garantindo o nível lógico 1.

Ao pressionar o botão, ele fecha, ligando o ponto de conexão com o pino do uC ao GND, aplicando o nível lógico 0 a ele.

A configuração com o resistor de pull-down é a que proporciona uma lógica direta a entrada do dado: botão pressionado.

Programa (Firmware)

A Figura 2 mostra a árvore de diretórios do projeto em que são utilizados dois arquivos, sendo que o main.c possui o programa e o config.h as diretivas de configuração.

Figura 2: Árvore de diretório do projeto
circuito

O arquivo de configuração deve ser incluído no main.c com o comando #include "config.h" logo no início do código.

#ifndef CONFIG_H
#define CONFIG_H

// CONFIG1
#pragma config FOSC = INTRC_NOCLKOUT    // Oscillator Selection bits (INTOSCIO oscillator: I/O function on RA6/OSC2/CLKOUT pin, I/O function on RA7/OSC1/CLKIN)
#pragma config WDTE = OFF               // Watchdog Timer Enable bit (WDT disabled and can be enabled by SWDTEN bit of the WDTCON register)
#pragma config PWRTE = OFF              // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = OFF              // RE3/MCLR pin function select bit (RE3/MCLR pin function is digital input, MCLR internally tied to VDD)
#pragma config CP = OFF                 // Code Protection bit (Program memory code protection is disabled)
#pragma config CPD = OFF                // Data Code Protection bit (Data memory code protection is disabled)
#pragma config BOREN = OFF              // Brown Out Reset Selection bits (BOR disabled)
#pragma config IESO = OFF               // Internal External Switchover bit (Internal/External Switchover mode is disabled)
#pragma config FCMEN = OFF              // Fail-Safe Clock Monitor Enabled bit (Fail-Safe Clock Monitor is disabled)
#pragma config LVP = OFF                // Low Voltage Programming Enable bit (RB3 pin has digital I/O, HV on MCLR must be used for programming)

// CONFIG2
#pragma config BOR4V = BOR40V           // Brown-out Reset Selection bit (Brown-out Reset set to 4.0V)
#pragma config WRT = OFF                // Flash Program Memory Self Write Enable bits (Write protection off)

#endif

A separação das diretivas de compilação é interessante por facilitar o tratamento dessas configurações, no caso de mudanças ou mesmo na inclusão de outras diretivas. Esse arquivo pode ser copiado para os demais projetos ou pode ser criado conforme descrito em config.h.

Segue o código fonte do main.c.

/*
 * File:   main.c
 * Author: josewrpereira
 *
 * Created on 13 April 2021, 21:45
 * 
 * IDE:                 MPLAB X IDE v5.45
 * Compiler:            XC8 v2.31
 * Operating System:    Debian GNU/Linux bullseye/sid
 * Kernel:              Linux 5.10.0-5-amd64
 * Architecture:        x86-64
 * 
 * Objetivo: 
 *      Acionar o LED em função do botão
 * 
 * Pinos    |nº     |Conexão
 *  VDD     |11,32  | Alimentação (Vcc/+5V)
 *  VSS     |12,31  | Alimentação (GND/0V)
 *  RD0     |19     | LED   (source)
 *  RD3     |22     | BOTAO (pullDown)
 */


#include "config.h"             // Inclui diretivas de configuração do uC
#include <xc.h>                 // Inclui biblioteca padrão do compilador XC8 
                                // para microcontroladores Microchip.

void main(void)                 // Função principal = main.
{                               // Início do escopo da função main.
    PORTDbits.RD0 = 0;          // Inicia RD0 com o valor 0.
    TRISDbits.TRISD0 = 0;       // Configura RD0 como Saída.
    TRISDbits.TRISD3 = 1;       // Configura RD3 como Entrada;
                                // Não precisa inicialização do valor do pino.

    while( 1 )                  // Laço de repetição infinito.
    {                           // Inicio do escopo do laço de repetição.
        if( PORTDbits.RD3 == 1 )// Condição: Se o botão estiver pressionado.
            PORTDbits.RD0 = 1;  // Liga RD0.
        else                    // Senão.
            PORTDbits.RD0 = 0;  // Desliga RD0.
    }                           // Fim do escopo do laço de repetição.
    return;                     // Caracteriza main como uma função sem retorno.
}                               // Fim do escopo da função main.

Estrutura do programa

O programa presente no arquivo fonte main.c possui o seguinte arranjo:

  • Comentários;
  • Inclusão de configuração e bibliotecas;
  • Programa principal.

Comentários

Os comentários estão descritos em C1 - Pisca LED e são semelhantes em todos os projetos aqui apresentados, assim serão omitidos para simplificar e focar no código que resolve o objetivo apresentado, mas é importante que você faça os comentários em todos os seus projetos.

Inclusão de bibliotecas

Note que além da biblioteca padrão xc.h, agora está incluído o arquivo de cabeçalho config.h, como já citado anteriormente.

Programa principal

O programa principal pode ser dividido em duas partes: configuração e laço de repetição.

O trecho de configuração no código inicializa o pino RD0 do PORTD com o valor zero, que é para iniciar com o LED desligado.

A linha seguinte configura o RD0 como saída, porque é o pino que aciona o LED.

Em seguida é configurado como entrada o pino RD3.

    PORTDbits.RD0 = 0;          // Inicia RD0 com o valor 0.
    TRISDbits.TRISD0 = 0;       // Configura RD0 como Saída.
    TRISDbits.TRISD3 = 1;       // Configura RD3 como Entrada;

Note que o RD3 não é inicializado porque o dado vem de fora do uC, assim não faz sentido inicializá-lo pois não é um dado de saída.

O loop infinito possui o ciclo de execução que atende o objetivo proposto sendo estruturado da seguinte forma:

  • leitura do estado lógico no pino de entrada;
  • ligar o LED se o estado lógico do pino é 1;
  • desligar o LED se o estado lógico do pino é 0;

Segue a codificação da estrutura do laço de repetição.

    while( 1 )                  // Laço de repetição infinito.
    {                           // Inicio do laço de repetição.
        if( PORTDbits.RD3 == 1 )// Condição: Se o botão estiver pressionado.
            PORTDbits.RD0 = 1;  // Liga RD0.
        else                    // Senão.
            PORTDbits.RD0 = 0;  // Desliga RD0.
    }                           // Fim do laço de repetição.

Utilizando defines

Uma das ferramentas mais interessantes de se trabalhar é a utilização dos defines pois facilitam muito a interpretação e a leitura do código, podendo-se utilizar um termo que faça sentido com a aplicação.

Sendo assim, pode-se definir os pinos utilizados com um termo mais coerente com as suas funções.

#include <xc.h>
#include "config.h"

#define LED     PORTDbits.RD0
#define BOTAO   PORTDbits.RD3

void main(void)
{
    LED = 0;
    TRISDbits.TRISD0 = 0;
    TRISDbits.TRISD3 = 1;

    while( 1 )
    {
        if( BOTAO == 1 )
            LED = 1;
        else
            LED = 0;
    }
    return;
}

Observando a estrutura condicional dentro do laço de repetição, pode-se ver que a leitura e interpretação do funcionamento do programa está mais evidente.

Se o BOTAO estiver acionado ligar o LED senão desligar o LED

A estrutura condicional if verifica se o conteúdo entre os parênteses é verdadeiro para executar o código à seguir. A condição BOTAO == 1 é uma comparação que verifica a igualdade(==) entre os dois termos BOTAO e 1.

A condição verdadeira dentro da estrutura do if não precisa ser uma comparação, pode ser o estado lógico de uma variável ou de um registrador. Assim pode-se utilizar a seguinte condição: if( BOTAO ) pois BOTAO é o PORTDbits.RD3, que é um registrador e pode assumir os estado lógicos 1 ou 0, respectivamente verdadeiro ou falso.

O código abaixo utiliza a condicional sem a comparação, apenas com o estado lógico da entrada.

#include "config.h"
#include <xc.h>

#define LED     PORTDbits.RD0
#define BOTAO   PORTDbits.RD3

void main(void)
{
    LED = 0;
    TRISDbits.TRISD0 = 0;
    TRISDbits.TRISD3 = 1;

    while( 1 )
    {
        if( BOTAO )
            LED = 1;
        else
            LED = 0;
    }
    return;
}

Simplificando o código

O processo de codificação envolve muitas etapas e compromissos com um equilíbrio entre simplicidade, tamanho e velocidade de execução, sendo que o ideal seria um código o mais simples possível, com o menor tamanho e com a maior velocidade de execução. Porém essa condição combinada é praticamente uma utopia. Então o programador deve escolher qual dessas características é prioritária.

A simplificação do codigo exposto acima, num dos raros casos, pode ser melhorado nesses três aspectos.

#include "config.h"
#include <xc.h>

#define LED             PORTDbits.RD0
#define BOTAO           PORTDbits.RD3

void main(void)
{
    LED = 0;
    TRISDbits.TRISD0 = 0;
    TRISDbits.TRISD3 = 1;
    
    while( 1 )
    {
        LED = BOTAO;
    }
    return;
}

Como a condição para acionar o LED é o botão estar pressionado, a lógica é direta nesse acionamento. Assim, basta atribuir o valor da entrada (BOTAO) à saída (LED).

O código dentro do laço de repetição assim ficou mais simples de compreender, menor em extensão e por consequência com execução mais rápida.

Botão com resistor de pull-down
circuito

Inversão da entrada

Como visto no início desta postagem, existe a possibilidade de ligar o botão com um resistor de pull-down ou pull-up. Caso a ligação já esteja feita, é muito mais simples mudar o estado lógico do pino no programa do que mudar no circuito, seja no esquemático ou na montagem.

A condição do circuito abaixo utilizando o código já apresentado, mostra que o comportamento do LED está invertido em relação ao pressionar do botão. O botão pressionado apaga o LED e o botão não pressionado mantém o LED ligado.

Botão com resistor de pull-up
circuito

Para inverter o estado lógico da entrada, e garantir que ao pressionar o botão o LED acenda, basta inserir o operador de inversão lógica !, equivalente a uma porta lógica inversora: LED = !BOTAO;. Essa instrução significa que o LED recebe o estado lógico inverso do BOTAO.

Segue o código com o estado lógico do botão invertido e abaixo o seu comportamento no simulador.

#include "config.h"
#include <xc.h>

#define LED             PORTDbits.RD0
#define BOTAO           PORTDbits.RD3

void main(void)
{
    LED = 0;
    TRISDbits.TRISD0 = 0;
    TRISDbits.TRISD3 = 1;
    
    while( 1 )
    {
        LED = !BOTAO;
    }
    return;
}
Botão com resistor de pull-up e inversão de estado lógico em código
circuito

Agora é a sua vez!

Crie o seu projeto, copie o código, execute-o, procure os erros, arrume-os, seja resiliente, leia novamente a explicação, busque outras fontes, pergunte, responda, explique, faça alterações conscientes no código, explore, divirta-se.

Ficou com alguma dúvida, entre em contato.

Bom trabalho!


« C1 - Pisca LED « C1 - Botão pulsador e LED » C1 - Semáforo de Veículos »


Voltar