Continuando a série de artigos sobre o microcontrolador CH552, hoje vamos gerar PWM com este pequeno poderoso. O PWM, ou do Inglês “pulse width modulation” significa modulação por largura de pulso. Já falamos sobre o PWM anteriormente aqui.
Não vou me ater muito à teoria, pois se você chegou até aqui significa que já tem algum conhecimento. Mas basicamente a característica do PWM é ser um sinal de frequência fixa e largura de pulso variável. Devido ao fato da largura de pulso variar, sua principal característica é a tensão média variável.

E para que é útil um sinal de tensão média variável?. Basicamente para controle de circuitos de potência como motores, válvulas proporcionais e outras cargas resistivas. Com o PWM conseguimos por exemplo variar a luminosidade de um LED de forma direta.
De acordo com seu datasheet, o CH552 tem dois conjuntos de PWM de 8 bit. O PWM1 pode ser usados nos pinos P1.5 e P3.0 (alternativo), enquanto o PWM2 pode ser usado nos pinos P3.4 e P3.1 (alternativo). Devido a serem canais de 8 bit, os valores possíveis de controle são entre 0 e 255 inteiros É possível configurar o PWM como “normalmente 0” ou “normalmente 1”.
Neste artigo escreveremos um exemplo de uso de PWM que fica fazendo efeito de “fade” em um LED, no pino P1.5. Basicamente o LED vai acendendo aos poucos, chega no brilho máximo e depois vai apagando aos poucos. O código todo é não-bloqueante, e para provar isto eu coloquei um LED para ficar piscando no pino P3.0. Isto significa que não vamos usar os famosos “delay()” presentes nos códigos do Arduino.
O hardware para nosso projeto
Assim como na maioria dos projetos aqui do FritzenLab, o hardware para esta montagem é bem simples. Nós vamos precisar de um diodo LED, pode ser tanto de 3mm como 5mm, qualquer cor. Precisaremos também de um resistor para colocar em série com o LED; o valor pode ser entre 220 Ohm e 1,5k Ohm, você escolhe.
O segundo LED (aquele que vai estar piscando) é o LED onboard da placa do CH552, que está montado ao lado do microcontrolador. Para esta série de artigos eu estou utilizando a plaquinha do CH552 montada sobre uma protoboard de 400 pinos, mais que suficiente para nossos projetos.
O diagrama esquemático da montagem está abaixo, além de uma foto da montagem em minha bancada. Repare que ali tem também um conversor USB-serial. É usado apenas para outros artigos e outras montagens, não se preocupe.


Obs: para comprar esta plaquinha do CH552, use meu link do Aliexpress. O osciloscópio que tenho em bancada e vou utilizar logo abaixo é este.
O firmware/código do nosso experimento
Conforme já comentado, eu estou tendo bastante ajuda da inteligência artificial para aprender e estudar a programação do CH552. Uso e aprendo com o ChatGPT, Claude, Copilot e Gemini. Até mesmo porque existe pouca informação sobre o CH552 na internet. O que mais achei foram artigos da Adafruit rodando uma “transpilação” de código Arduino para esta placa.
Com muito custo consegui achar estas bibliotecas para o CH554 que servem no CH552. Principalmente o arquivo “ch554.h” que está sendo a base para esta série de artigos. Antes conseguir compilar alguma coisa eu tive que modifica o dito arquivo, que pode ser encontrado em sua forma final no meu GitHub. Uso este arquivo que é sucesso.
Quando ao código em “C” para este experimento, está abaixo na sua forma completa, bem como neste link do meu GitHub. O arquivo se chama “pwm-testing.c”.
#include "inc\ch554.h"
#include <stdint.h>
volatile unsigned int tick_10ms = 0;
static unsigned int last_tick = 0;
static uint8_t pwm_value = 0;
static __bit going_up = 1;
void timer0_ISR(void) __interrupt(1);
void clock_init(void);
void pwm_init(void);
void blink_led(void);
void clock_init(void) {
SAFE_MOD = 0x55;
SAFE_MOD = 0xAA;
CLOCK_CFG |= bOSC_EN_INT;
CLOCK_CFG = (CLOCK_CFG & ~MASK_SYS_CK_SEL) | 0x06;
SAFE_MOD = 0x00;
}
void timer0_ISR(void) __interrupt(1) __using(1) {
TF0 = 0;
TH0 = 0xB1;
TL0 = 0xE0;
tick_10ms++;
}
void timer0_init(void) {
T2MOD &= ~bTMR_CLK;
T2MOD &= ~bT0_CLK;
TMOD &= ~0x03;
TMOD |= 0x01;
TH0 = 0xB1;
TL0 = 0xE0;
TF0 = 0;
ET0 = 1;
TR0 = 1;
EA = 1;
}
void blink_led(void) {
if(tick_10ms % 50 < 25){
P3 |= (1 << 0); // LED ON
} else {
P3 &= ~(1 << 0); // LED OFF
}
}
void pwm_init(void) {
// Explicitly select PWM1 on P1.5 (not P3.0)
PIN_FUNC &= ~bPWM1_PIN_X;
// Disable SPI0 so it doesn't override P1.5
SPI0_CTRL &= ~bS0_MOSI_OE; // make sure MOSI output is disabled
// P1.5 push-pull output
P1_MOD_OC &= ~(1 << 5);
P1_DIR_PU |= (1 << 5);
// PWM clock = Fsys/12 (PWM_CK_SE=0 means divide by 1 of the
// already-divided clock, gives plenty of resolution)
PWM_CK_SE = 0x00; // this gives 2MHz / 256 = 7812.5 Hz ≈ 7.8kHz (without any divider)
//PWM_CK_SE = 0xFF; // this gives 2MHz / 256 = 7812.5 Hz ≈ 7.8kHz divided by 256 which gives us 30Hz
// Start at zero brightness, default low polarity (active high)
PWM_DATA1 = 0;
//PWM_DATA1 = 128; // 50% duty cycle for testing
// Enable PWM1 output, clear the bPWM_CLR_ALL bit
PWM_CTRL = bPWM1_OUT_EN;
}
void main(void) {
clock_init();
timer0_init();
pwm_init();
// 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
while (1) {
unsigned int t;
EA = 0;
t = tick_10ms;
EA = 1;
blink_led();
// Update brightness every 10ms tick
if ((t - last_tick) >= 1) {
last_tick = t;
if(pwm_value > 253){
pwm_value = 0;
}else{
pwm_value += 1;
}
// this is the register that receives the 8-bit PWM value
PWM_DATA1 = pwm_value;
}
}
}
Dentro da função “main()” acontecem algumas coisas primeiro nós configuramos o clock para 24MHz, depois timer 0 para 10 ms e depois a função PWM. Tudo isso é feito apenas uma vez, toda vez que o microcontrolador é inicializado.
Já dentro do loop infinito “while(1)”, eu leio o valor de “tick_10ms” e guardo dentro da variável “t”. Este valor é incrementado a cada 10 milisegundos que o microcontrolador está rodando. Serve para controlar o incremento do PWM e o piscar do LED no pino P3.0.
O controle do valor do PWM é feito na condicionar IF() mostrada abaixo. O valor do PWM é um byte, portanto 8 bits, então seu valor em inteiros vai de 0 a 254. Por este motivo eu fico verificando se o valor passou de 253, caso sim eu já zero ele para reiniciar o ciclo. Caso ele não tenha passado de 253 ainda, eu apenas vou incrementando ele.
if(pwm_value > 253){
pwm_value = 0;
}else{
pwm_value += 1;
}
Então o valor de “pwm_value” é alimentado para “PWM_DATA1”, que é responsável por efetivamente controlar o PWM do canal 1. Um detalhe interessante: fazemos “t = tick_10ms” entre “EA = 0” e “EA = 1”. Isto é para desabilitar as interrupções enquanto lemos o valor de “tick_10ms”, para evitar valores corrompidos. Isto pois uma interrupção poderia acontecer bem no meio da escrita da variável “t”, o que poderia ser prejudicial.
EA = 0;
t = tick_10ms;
EA = 1;
Gravando o código no CH552
Conforme já comentado anteriormente, no artigo #1 da série, você precisa instalar e configurar algumas coisas para poder programar seu CH552. Um editor de texto de sua preferência é o primeiro passo; no meu caso instalei o Visual Studio code. Depois você precisa instalar o Python e coloca-lo no PATH do seu Windows.
Aí é necessário instalar o compilador SDCC e o gravador CHprog. Volte no artigo do link acima para fazer toda esta configuração. Eu tive ajuda da inteligência artificial para criar um script em Python, com objetivo de automatizar todo processo de compilação, linkagem, conversão e gravação. O arquivo se chama “build_flash.py” e está disponível no meu GitHub.
O arquivo do nosso código completo acima, chamado “pwm-testing.c” precisa estar na mesma pasta do aquivo Python “build_flash.py”. No meu caso eu salvei tudo em “C:\Users\Clovisf\Documents\ch552”. Isto porque o script em Python não relativiza os caminhos, ele espera os arquivos nos lugares certos. Abra a linha de comando do Windows (CMD), navegue até a pasta onde salvou tudo e digite:
python build_flash.py pwm-testing.c
Pressione ENTER e aguarde alguns segundos. A mensagem abaixo deve aparecer.
>> Ready to flash: pwm-testing.bin
>> Put the CH552 into BOOT mode (hold BOOT, plug USB, release).
>> Press ENTER when ready...
Neste momento vem a gravação do microcontrolador. Pressione e segure pressionado o botão BOOT que está na plaquinha do CH552. Conecte o cabo USB da plaquinha ao computador, só então solte o botão BOOT. Rapidamente pressione o ENTER do computador e aguarde uns segundos. Caso a gravação tenha sucesso, uma mensagem similar à esta vai aparecer:
Using v2 protocol
Chip ID=52
Bootloader: 2.5.0
Write & verify complete. All seems OK.
✔ Build + Flash completed successfully!
Testando o projeto
Não é necessário remover a USB do computador e reinserir, nem pressionar o botão reset da placa. Mas se quiser fazer é melhor para o código rodar do zero. Neste momento você vai ver o LED onboard (P3.0) piscando a 2Hz e o LED no pino P1.5 variando seu brilho. Caso tudo esteja desta forma, obtivemos sucesso no nosso projeto.
Abaixo eu deixo uma imagem do meu osciloscópio medindo o pino P1.5 do PWM. No início do artigo (lá no segundo parágrafo) tem um vídeo demonstrando como este código funciona. Veja se ficou igual à sua implementação. Observe a forma de onda em azul, sobre o LED do P3.0, oscilando a exatamente 2Hz. Ciclo ativo exatamente 50%, como pode ser visto na parte inferior da tela (em “+Du”).
Já a forma de onda em amarelo, do PWM, não fica com cara de PWM pois passa pelo “filtro” do resistor e do LED no pino P1.5. A frequência medida no canal do PWM (10Hz) não está correta. Cada ciclo do PWM vai de 0 a 254, um passo a cada 10ms, portanto demora 10ms x 254 = 2,54 segundos.

Fique à vontade para comentar aqui em baixo do post ou até mesmo lá no Youtube do FritzenLab. Até a próxima, pessoal!.