Avançar para o conteúdo

LED e botão com CH552 (8051) #3

Aqui vamos aprender como controlar e usar LED e botão com CH552 (8051). Usaremos o temporizador Timer0 para piscar um LED quando o botão for pressionado uma vez. Então parar de piscar quando pressionarmos o botão novamente. E assim repetindo o ciclo indefinidamente.

Parte #1 da série (preparando o ambiente): https://fritzenlab.com.br/2026/04/25/o-que-eu-preciso-para-programar-o-ch552-8051-1/

Parte #2 da série (piscando um LED): https://fritzenlab.com.br/2026/04/26/piscar-led-com-ch552-8051/

O setup de hardware

Como de costume aqui no blog, o setup de hardware é sempre bem simples e direto. No nosso caso, podemos usar apenas um botão push-button e dois fios “jumper wire”. Se você quiser pode (opcional) inserir um LED (3mm ou 5mm, qualquer cor) em série com um resistor entre 220 Ohm e 1k5 Ohm.

Mas de qualquer forma não precisa, pois usaremos o LED presente/onboard na placa do Ch552, no pino P3.0. Veja abaixo o diagrama esquemático do experimento e repare que o botão push button fecha para o GND. Isto porque os pinos do microcontrolador Ch552 tem pull-up internos. O datasheet do Ch552 está aqui.

LED e botão com Ch552
LED e botão com Ch552

Então nosso código teremos que “inverter” a lógica, pois quando fechamos o botão o pino receberá GND. Queremos que o valor na variável de controle seja “1”, então pegamos o “0” recebido e invertermos com “!”.

O código/firmware

Usei como base para começar a fazer o código, tudo que já havia criado no post anterior, aqui. Portanto o LED do nosso exemplo “LED e botão com CH552” ainda estará no pino P3.0. Já o botão poderia ir em qualquer pino, eu selecionei o pino P1.4 para a tarefa.

Toda a “mágica” da leitura do botão acontece em apenas uma linha, dentro do while(1), veja abaixo.

raw = !(P1 & (1 << 4)); // read raw pin, pressed = 1

Basicamente fazemos com que nossa leitura aconteça no P1, pino 4 e seja invertida. Isto porque conforme já explicado o push button fecha para o GND no nosso microcotrolador. Por usa vez isto é devido à presença de resistores internos de pull-up no Ch552.

Após isso, todo o código abaixo faz o debounce do botão (para 200 ms – duzentos milisegundos). Isto significa que após a primeira detecção da pressão do botão, os próximos 200 ms são “mascarados”, se detecção de botão. Setamos então também a variável “currentButton” para decidir se pisca ou não o LED.

if (raw != raw_button_prev) {
            // pin changed — restart the debounce timer
            debounce_start = tick_100us;
            raw_button_prev = raw;
        } else if ((tick_100us - debounce_start) >= DEBOUNCE_TICKS) {
            // pin has been stable for 20ms — accept it
            if (raw == 1 && raw_button_prev_accepted == 0) {
                // rising edge only: toggle blink state on each press
                currentButton = !currentButton;
                raw_button_prev_accepted = 1;
            } else if (raw == 0) {
                // button released — arm for next press
                raw_button_prev_accepted = 0;
            }
        }

Com base na informação acima, ou piscamos o LED ou o desligamos:

// LED e botão com CH552
if (currentButton) blink_led();
else P3 &= ~0x01; // LED OFF

Definições iniciais

Não posso deixar de citar as definições iniciais necessárias para o funcionamento do microcontrolador. Elas são inicialização do clock “clock_init()” e logo depois do temporizador Timer0:

void clock_init(void) {
    SAFE_MOD = 0x55;
    SAFE_MOD = 0xAA;
    // Set Fsys = 24 MHz (Fpll/4, bits[2:0] = 110 = 0x06)
    CLOCK_CFG = (CLOCK_CFG & ~MASK_SYS_CK_SEL) | 0x06;
    
    SAFE_MOD = 0x00;
}
void timer0_init(void) {
    TMOD &= ~0x03;  // clear Timer0 mode bits
    TMOD |=  0x01;  // Timer0 mode 1: 16-bit

    /*// 10ms @ 24MHz: tick = 24M/12 = 2MHz = 0.5us
    // 10ms / 0.5us = 20000 ticks; 65536-20000 = 45536 = 0xB1E0
    TH0 = 0xB1;
    TL0 = 0xE0;*/

    // 100us @ 24MHz: tick = 24M/12 = 2MHz = 0.5us
    // 100us / 0.5us = 200 ticks; 65536-200 = 65336 = 0xFF38
    TH0 = 0xFF;
    TL0 = 0x38;

    ET0 = 1;   // enable Timer0 interrupt
    TR0 = 1;   // start Timer0
    EA = 1;
}

Lembrando que no artigo anterior, pisca LED, usamos um tick de 10 ms (dez milisegundos). Agora para este exemplo usaremos 100 us (cem microsegundos). Para o “usuário” não muda nada, apenas deixamos a onda gerada sobre o LED mais “quadadra” o possível. Isso significa um ciclo ativo mais próximo de 50%.

Lembrando ainda que configuramos nosso clock para 24MHz, pois justamente o cristal oscilador montado na placa do Ch552 tem este valor. Para isso lançamos mão da função SAFE_MOD, afim de fazer esta configuração com segurança.

O código completo e testes

Veja abaixo e aqui neste link o código completo para este experimento. Nomeie seu arquivo fonte como “button_and_led.c”.

#include "inc\ch554.h"
#include <stdint.h>
#include <compiler.h> 

volatile unsigned int tick_100us = 0;
unsigned int button;
unsigned char led_state = 0;
__bit currentButton= 0;
unsigned int counter= 0;
// Debounce state
volatile unsigned int debounce_start = 0;
__bit raw= 0;
__bit raw_button_prev = 0;
__bit raw_button_prev_accepted = 0;  // tracks last accepted (debounced) state
// 20ms debounce threshold: 20ms / 100us = 200 ticks
#define DEBOUNCE_TICKS 200

void timer0_ISR(void) __interrupt(1) __using(1);
void blink_led(void);
void clock_init(void);

void clock_init(void) {
    SAFE_MOD = 0x55;
    SAFE_MOD = 0xAA;
    // Set Fsys = 24 MHz (Fpll/4, bits[2:0] = 110 = 0x06)
    CLOCK_CFG = (CLOCK_CFG & ~MASK_SYS_CK_SEL) | 0x06;
    
    SAFE_MOD = 0x00;
}

void timer0_ISR(void) __interrupt(INT_NO_TMR0) {
    TF0 = 0;  // clear overflow flag (important for robustness)

    // This is for 10ms ticks
    /*TH0 = 0xB1;
    TL0 = 0xE0;*/

    // 100us @ 24MHz: tick = 24M/12 = 2MHz = 0.5us
    // 100us / 0.5us = 200 ticks; 65536-200 = 65336 = 0xFF38
    TH0 = 0xFF;
    TL0 = 0x38;

    if(tick_100us <= 5000){
        tick_100us++;
    }else{
        tick_100us= 0;
    }
        
}

void timer0_init(void) {
    TMOD &= ~0x03;  // clear Timer0 mode bits
    TMOD |=  0x01;  // Timer0 mode 1: 16-bit

    /*// 10ms @ 24MHz: tick = 24M/12 = 2MHz = 0.5us
    // 10ms / 0.5us = 20000 ticks; 65536-20000 = 45536 = 0xB1E0
    TH0 = 0xB1;
    TL0 = 0xE0;*/

    // 100us @ 24MHz: tick = 24M/12 = 2MHz = 0.5us
    // 100us / 0.5us = 200 ticks; 65536-200 = 65336 = 0xFF38
    TH0 = 0xFF;
    TL0 = 0x38;

    ET0 = 1;   // enable Timer0 interrupt
    TR0 = 1;   // start Timer0
    EA = 1;
}

void blink_led(void) {
    if(tick_100us % 5000 < 2500){
        P3 |= (1 << 0);  // LED ON
    } else {
        P3 &= ~(1 << 0); // LED OFF
    }
}

void main(void) {
    clock_init();
    timer0_init();

    // Disable watchdog (important on CH55x)
    SAFE_MOD = 0x55;
    SAFE_MOD = 0xAA;
    GLOBAL_CFG &= ~bWDOG_EN;   // turn off watchdog
    SAFE_MOD = 0x00;

    // Configure P3.0 as push-pull output
    // P3.0 bit = 0x01
    P3_MOD_OC &= ~0x01;   // not open-drain
    P3_DIR_PU |= 0x01;    // output, with pull-up

    // Configure P1.4 as input WITH internal pull-up (CH554 requires BOTH bits = 1)
    P1_MOD_OC |=  (1 << 4);   // open-drain mode → required for pull-up
    P1_DIR_PU  |=  (1 << 4);  // pull-up enabled
    //P1 |= (1 << 4);           // write 1 → enables internal pull-up

    Serial_begin();

    while (1) {
        raw = !(P1 & (1 << 4)); // read raw pin, pressed = 1

        if (raw != raw_button_prev) {
            // pin changed — restart the debounce timer
            debounce_start = tick_100us;
            raw_button_prev = raw;
        } else if ((tick_100us - debounce_start) >= DEBOUNCE_TICKS) {
            // pin has been stable for 20ms — accept it
            if (raw == 1 && raw_button_prev_accepted == 0) {
                // rising edge only: toggle blink state on each press
                currentButton = !currentButton;
                raw_button_prev_accepted = 1;
            } else if (raw == 0) {
                // button released — arm for next press
                raw_button_prev_accepted = 0;
            }
        }

        if (currentButton) blink_led();
        else P3 &= ~0x01; // LED OFF
    }
}

Vamos relembrar:

  • Ao inicializar o código, o LED estará apagado,
  • Ao pressionar brevemente o botão push button, o LED começa a piscar,
  • O LED fica piscando mesmo ao soltar o botão,
  • Ao pressionar o botão novamente, o LED para de pisca/apaga,
  • O ciclo se reinicia.

Veja o vídeo do funcionamento do experimento “LED e botão com CH552” lá no início do artigo. Lembrando que se quiser compra o microcontrolador Ch552 clique aqui e se quiser o osciloscópio do vídeo, compre aqui.

LED e botão com CH552!

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *