Avançar para o conteúdo

Comparador analógico do CH552 (8051) #6

No sexto artigo desta série, falaremos sobre o comparador analógico do CH552. Este é um periférico rápido que serve para comparar níveis analógicos entre dois sinais. Diferente do condicional IF(), ele é um periférico rápido. Lembrando que estamos falando do CH552, que é baseado no saudoso 8051.

O periférico comparador analógico não gera interrupção no CH552, ele apenas seta a flag “CMP0” para valor 1. Temos então o trabalho assíncrono de ler esta flag e tomar atitude de acordo com o que necessitamos.

De acordo com o datasheet do CH552, é possível escolher entre quatro (4) canais analógicos para ser a comparação positiva: AIN0.. AIN3. Já a comparação negativa pode ser escolhida entre dois (2) canais: AIN1 ou AIN3. O funcionamento do comparador baseia-se na seguinte lógica:

  • Caso o pino comparador positivo tenha tensão menor que o pino comparador negativo, a flag “CMP0” fica em “0”,
  • Caso o pino comparador positivo tenha tensão maior que o pino comparador negativo, a flag “CMP0” vai a “1”.

Nós criaremos um exemplo onde o pino positivo será AIN0 (P1.1) – um potenciômetro. Já o pino negativo será AIN1 (P1.4) – um divisor resistivo de 4,7k Ohm + 4,7k Ohm. Para indicar atuação do comparador nós faremos piscar o LED onboard da placa CH552, no pino P3.0.

Enquanto a tensão em um potenciômetro (pino P1.1) for menor que a tensão no divisor resistivo (P1.4), LED apagado. Quando a tensão do potenciômetro for maior que a do divisor resistivo, o LED começa a piscar a 3Hz.

O Hardware/montagem

Para esta montagem vamos usar o já tradicionar CH552 do Aliexpress, comprado (aqui) ou (aqui). Precisaremos também de um potenciômetro, pode ser qualquer tipo e modelo. O valor pode ser entre 1k Ohm e 100k Ohm. Usaremos também dois resistores de valor idêntico (iguais): entre 1k Ohm e 100k Ohm. Podem ser de qualquer potência e tamanho, o que for mais fácil para você manusear.

Como de costume aqui no blog, a montagem é simples e rápida. Veja nas imagens abaixo. Eu fiz a minha montagem em uma pequena protoboard 400 pontos, é mais que suficiente.

Diagrama esquemático do comparador analógico do CH552
Diagrama esquemático do comparador analógico do CH552
ch552 na protoboard comparador analógico
Dois resistores e potenciômetro na protoboard com CH552

A alimentação do experimento pode ser feita via cabo USB, via o conector USB C que fica na placa do CH552. Lembrando que a placa do CH552 até tem um pino que oferece 3,3V, porém todos os recursos da mesma são 5V. Isto inclui pinos digitais e entradas analógicas.

O firmware/código explicado

Nosso código usará o temporizador Timer0, assim como já fizemos neste artigo anterior. Configuraremos um “tick” ou interrupção de 10 milisegundos (10 ms), que governará tudo. Desde a leitura da flag do comparador até a piscada do LED.

Iniciamos o clock em 24MHz, que é justamente o valor do cristal oscilador soldado na placa do CH552. Inicializamos então o temporizador Timer0 com “0xB1E0”, que nos dá algo muito próximo de 10 milisegundos em 24MHz. Também dentro da inicialização do Timer0 nós damos o start em todas as interrupções, fazendo “EA= 1”.

void clock_init(void) {
    SAFE_MOD = 0x55;
    SAFE_MOD = 0xAA;
    CLOCK_CFG |= bOSC_EN_INT; 
    // 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) {
    // Force Timer0 to use Fsys/12
    T2MOD &= ~bTMR_CLK;   // disable fast clock mode
    T2MOD &= ~bT0_CLK;    // Timer0 = Fsys/12
    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;

    TF0 = 0;

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

Faz-se então a inicialização do comparador, definindo quais pinos serão usados e como eles serão configurados.

void cmp_init(void) {
    // Enable comparator power only (no ADC needed)
    ADC_CFG = bCMP_EN;

    // IN+ = AIN0 (P1.1): ADC_CHAN1=0, ADC_CHAN0=0
    // IN- = AIN1 (P1.4): CMP_CHAN=0
    // Both already 0 at reset, but be explicit:
    ADC_CTRL = 0x00;

    // P1.1 as high-impedance input (IN+)
    P1_MOD_OC &= ~(1 << 1);
    P1_DIR_PU &= ~(1 << 1);

    // P1.4 as high-impedance input (IN-, reference divider)
    P1_MOD_OC &= ~(1 << 4);
    P1_DIR_PU &= ~(1 << 4);
}

Finalmente dentro do loop infinito “while(1) crio toda a lógica de piscar ou não o LED, conforme a tensão lida no comparador analógico:

while (1) {        
    
        EA = 0;
        t = tick_10ms;
        EA = 1;
        
        if(t - comparatorTime > 10){
            comparatorTime= t;

            if (CMPO) { // CMP0 does not latch and does not generate interrupts, it is just a mirror of the analog comparator
                if (!ledON) {
                    ledON = 1;
                    blink_base = t;  // reset blink phase when comparator first triggers
                }
            } else {
                ledON = 0;
                P3 &= ~(1 << 0);  // make sure LED goes off immediately when pot drops below reference
            }

        }        

        if (ledON) {
            blink_led(t);   // called every loop iteration while CMPO is high
        }
    }

Código completo do comparador analógico do CH552

Abaixo está o código completo do comparador analógico do CH552. Também disponível neste link do GitHub.

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


volatile unsigned int tick_10ms = 0;
volatile unsigned int comparatorTime = 0;
volatile __bit ledON = 0;
unsigned int serialTime= 0;
unsigned int counter= 0;
unsigned int t;
static __bit wdt_started = 0;
static unsigned int blink_base = 0;

void timer0_ISR(void) __interrupt(1);
void blink_led(unsigned int t);
void clock_init(void);
void cmp_init(void);

void clock_init(void) {
    SAFE_MOD = 0x55;
    SAFE_MOD = 0xAA;
    CLOCK_CFG |= bOSC_EN_INT; 
    // 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(1) __using(1){ 
    
    TF0 = 0;  // clear overflow flag (important for robustness)
    TH0 = 0xB1;
    TL0 = 0xE0;
    tick_10ms++; // this is the 10ms tick for LED blinking
    
}

void timer0_init(void) {
    // Force Timer0 to use Fsys/12
    T2MOD &= ~bTMR_CLK;   // disable fast clock mode
    T2MOD &= ~bT0_CLK;    // Timer0 = Fsys/12
    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;

    TF0 = 0;

    ET0 = 1;   // enable Timer0 interrupt
    TR0 = 1;   // start Timer0
    EA = 1;
}
void cmp_init(void) {
    // Enable comparator power only (no ADC needed)
    ADC_CFG = bCMP_EN;

    // IN+ = AIN0 (P1.1): ADC_CHAN1=0, ADC_CHAN0=0
    // IN- = AIN1 (P1.4): CMP_CHAN=0
    // Both already 0 at reset, but be explicit:
    ADC_CTRL = 0x00;

    // P1.1 as high-impedance input (IN+)
    P1_MOD_OC &= ~(1 << 1);
    P1_DIR_PU &= ~(1 << 1);

    // P1.4 as high-impedance input (IN-, reference divider)
    P1_MOD_OC &= ~(1 << 4);
    P1_DIR_PU &= ~(1 << 4);
}
void blink_led(unsigned int t) {
    unsigned int phase = t - blink_base;

    if (phase < 15) {
        P3 |= (1 << 0);
    } else if (phase < 30) {
        P3 &= ~(1 << 0);
    } else {
        blink_base = t;         
    }
}

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

    // Configure P3.0 (LED) as push-pull output
    // P3.0 bit = 0x01
    P3_MOD_OC &= ~(1 << 0);   // push-pull
    P3_DIR_PU |=  (1 << 0);   // enable strong output drive
        
    P3 &= ~(1 << 0);  // Make LED pin P3.0 "start" as OFF
    
    while (1) {        
    
        EA = 0;
        t = tick_10ms;
        EA = 1;
        
        if(t - comparatorTime > 10){
            comparatorTime= t;

            if (CMPO) { // CMP0 does not latch and does not generate interrupts, it is just a mirror of the analog comparator
                if (!ledON) {
                    ledON = 1;
                    blink_base = t;  // reset blink phase when comparator first triggers
                }
            } else {
                ledON = 0;
                P3 &= ~(1 << 0);  // make sure LED goes off immediately when pot drops below reference
            }

        }        

        if (ledON) {
            blink_led(t);   // called every loop iteration while CMPO is high
        }
    }
}

Compilando e testando

Salve o arquivo acima como “analog-comparator.c”. Abra o terminal de comando do Windows “CMD” e entre na pasta onde está o seu arquivo “analog-comparator.c”. No meu caso fiz assim:

cd Documents\ch552

Agora vamos compilar e gravar o microcontrolador. Para isso eu criei (com ajuda da inteligência artificial) um arquivo em Python, disponível aqui, chamado “build_flash.py”. Ele precisa estar na mesma pasta onde está o arquivo fonte “analog-comparator.c”.

Digite o comando abaixo e pressione ENTER. Ainda não conecte a placa na porta USB do computador. Obs: o Python precisa estar instalado na sua máquina, e adicionado ao PATH.

python build_flash.py analog-comparator.c

Em poucos segundo a mensagem

Put the CH552 into BOOT mode (hold BOOT, plug USB, release).
Press ENTER when ready…

vai aparecer, neste momento pressione (e segure) o botão BOOT da sua placa CH552. Conecte a USB ao computador e só então solte o botão BOOT. Pressione ENTER. A gravação ocorreu com sucesso se a mensagem abaixo apareceu:

Build + Flash completed successfully!

Eu fiz um vídeo para ilustra o funcionamento deste código, está lá em cima no começo do artigo. Caso tenha dúvida pode comentar abaixo ou então lá no Youtube do FritzenLab. Nos vemos na próxima, até logo.

Deixe um comentário

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