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.

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!






Pingback: O que eu preciso para programar o CH552 (8051) #1 - Fritzenlab eletrônica
Pingback: Piscar LED com CH552 (8051) #2 - Fritzenlab eletrônica