[Introdução]
A tecnologia de microcontroladores é uma tecnologia indispensável na automação industrial moderna, na eletrônica, na engenharia elétrica e na Internet das Coisas (IoT). À medida que as nossas vidas se tornam cada vez mais inteligentes, a tecnologia dos microcontroladores tem permeado praticamente todos os aspectos da nossa vida quotidiana, como nas panelas elétricas de arroz inteligentes, nos altifalantes inteligentes e muito mais.
Com isso em mente, a série de artigos "Reaprendendo o Microcontrolador 51" tem como objetivo ajudar os iniciantes a começarem a usar a tecnologia de microcontroladores. Começaremos com a tarefa mais simples-ligar um único LED-e avançaremos gradualmente para a implementação de módulos como controles de botão, display LCD1602, sensores de temperatura DS18B20 e DS1302 e comunicação entre dois microcontroladores. Também abordaremos protocolos de comunicação de hardware como UART, I²C e SPI. Ao combinar esses conceitos com técnicas de programação C, usaremos projetos de engenharia do mundo-real para ilustrar abordagens de programação, permitindo que você aplique indicadores e estruturas C de maneira flexível para alcançar uma programação modular.
Agora, voltemos ao tópico principal: usar um microcontrolador 51 para controlar um LED e criar um efeito de luz respiratória.
[Como funcionam as luzes respiratórias]
Vamos primeiro dar uma olhada em como funciona o efeito de luz respiratória.
Uma luz de respiração aumenta gradualmente e depois diminui gradualmente, repetindo esse ciclo de uma maneira que se assemelha à respiração. No entanto, como os pinos de um microcontrolador só podem produzir 1 (ligado) ou 0 (desligado), como pode ser alcançado um efeito de transição gradual?
Isto se deve à persistência da visão em nossos olhos. Quando olhamos para algo, a imagem formada pelos nossos olhos persiste por 0,04 segundos (este valor foi encontrado online).
Se calcularmos com base em 0,04 segundos, isso equivale a 40 ms. Portanto, quando o LED fica ligado e desligado por 20 ms cada, parece ao olho humano que está constantemente aceso.

O efeito de um LED piscando por 20 ms e apagado por 20 ms é o mesmo que mantê-lo aceso o tempo todo?
Haha, é definitivamente diferente. Quando a luz alterna entre ligada e desligada a cada 20 milissegundos, o efeito que vemos é mais fraco do que quando permanece acesa continuamente. Se assumirmos que o brilho de uma luz continuamente acesa é de 100%, então o brilho de uma luz que alterna entre ligada e desligada a cada 20 milissegundos é de 50%. Com base nisso, podemos ajustar o brilho do LED.

Neste ponto, podemos ajustar o brilho do LED (definindo a duração do nível alto dentro do ciclo de 40ms). Este é o princípio por trás do-conhecido método PWM (Pulse Width Modulation) de controle de brilho, e definir a duração do nível alto é equivalente a ajustar o ciclo de trabalho (ou seja, a duração do nível alto dividida pelo ciclo total: 20/40=50%).
Aqui, o fator mais importante é este ciclo de trabalho. Por exemplo, se o período for de 20 ms, com o LED alternando entre 10 ms ligado e 10 ms desligado, o brilho percebido ainda será de 50% (ou seja, o ciclo de trabalho é de 10/20=50%).
A seguir, vamos ver como isso é implementado no programa.
[Implementação do Programa]
Ligando um LED
Primeiro, vamos começar ligando um LED e depois implementaremos gradualmente um efeito de luz respiratória. O hardware que usaremos é o seguinte:
| Conselho de Desenvolvimento | Placa de desenvolvimento de treinamento de microcontrolador ZeroOne |
|---|---|
| Modelo de microcontrolador | STC89C52 |
| Interface LED | Pino P4 ^ 4 |

No esquema, podemos ver que o LED está conectado ao pino P4 ^ 4 do microcontrolador. Quando o microcontrolador gera 1, o LED acende; quando gera 0, o LED apaga. Portanto, o programa para acender um LED é bastante simples, conforme mostrado abaixo:

O programa para acender um LED é bastante simples; Tenho certeza que todo mundo sabe como fazer isso.
Ajustando o brilho do LED
A seguir, implementaremos uma função que nos permite ajustar o brilho (ou seja, ajustar o ciclo de trabalho), como segue:

Defina uma variável estática `duty_cycle` para armazenar o ciclo de trabalho. Quando `flag` é 1, o ciclo de trabalho aumenta gradualmente para 255, então defina `flag` como 0 e `duty_cycle` diminui gradualmente de 255 para 0. Repita este ciclo.
Haha, neste momento você pode pensar que a luz de respiração já está funcionando, mas não está. Se você não acredita em mim, tente você mesmo o código acima.
Então, onde exatamente está o problema?
O problema está em nossa chamada direta à função de configuração-de brilho `set_led_luminance()`. Esta função leva 40 ms para completar um ciclo, o que significa que o ciclo de trabalho não pode ser alterado durante esses 40 ms; caso contrário, o ajuste de brilho não funcionará. Vamos dar uma outra olhada na função `breath_led`. Após cada chamada para `set_led_luminance()` para definir o ciclo de trabalho, ele altera imediatamente o valor de `duty_cycle` sem esperar 40 ms.
Neste ponto, precisamos adicionar um temporizador de software para atualizar o valor `duty_cycle` após decorridos 40 ms. O programa modificado é o seguinte:

Nota: A duração do temporizador só precisa ser superior a 40 ms (o que significa que o valor de `s_breathCounter` deve ser superior a 255), mas é melhor configurá-lo para um múltiplo do ciclo. Por exemplo, se nosso ciclo for 255 (ou seja, 256 valores de 0 a 255), podemos configurá-lo para o dobro desse valor: 256 * 2 - 1=511 (ou seja, 512 valores de 0 a 511).
E aí está-nossa luz de respiração está completa! Não é simples? (* ̄︶ ̄)
A seguir está a seção de bônus de hoje.
Embora tenhamos alcançado o efeito de luz respiratória, o código não é conciso ou elegante o suficiente-ele usa várias instruções if e else. Vamos ver se podemos simplificar ainda mais.
Primeiro, vamos simplificar esta seção da função set_led_luminance(), conforme mostrado na figura abaixo.

Antes de simplificarmos isso, vamos dar uma dica rápida sobre C: a operação AND bit a bit.

A partir disso, sabemos que seja 1 ou 0, realizar uma operação AND bit a bit com 0 resulta em 0.
Quer seja 1 ou 0, executar uma operação AND bit a bit com 1 resulta no valor original.
Por conveniência, usamos notação hexadecimal (prefixada com “0x”); por exemplo, 0xff corresponde a 255 em decimal. Portanto,
Quando um número menor ou igual a 0xff recebe AND com 0xff, o resultado é o próprio número, conforme mostrado abaixo

O que acontece se você executar uma operação AND bit a bit entre um número maior que 0xff e 0xff?

O resultado é o resto quando este número é dividido por (0xff + 1) (ou seja, o resultado ainda está entre 0 e 0xff).
Com esta operação AND bit a bit, o código acima pode ser simplificado para

Desta forma, o valor de s_Counter estará sempre dentro do intervalo de 0x00 a 0xff.
Da mesma forma, o temporizador de software na função breath_led acima também pode ser simplificado da seguinte forma:

Na linha 3, 0x1ff é 511 em decimal. A condição é verdadeira quando o valor de s_breathCounter é (0x1ff+1), ou 512, porque 512 & 0x1ff=0. O ponto de exclamação antes de indicar uma operação NOT bit a bit (para ser preciso, a condição é atendida quando o valor de `s_breathCounter` é um múltiplo de 512; isso elimina a necessidade de redefinir `s_breathCounter`. Espero que esta explicação seja claro-pense bem). Se a condição for atendida, o ciclo de trabalho começa a aumentar ou diminuir.
Mas o ciclo de trabalho não está na faixa de 0 a 255? Por que a linha 5 também se torna 0x1ff (511)? Não se preocupe-veja a linha 8. Subtraímos 0xff novamente, então o intervalo do ciclo de trabalho permanece de 0 a 255.
As linhas 7–10 significam: Quando `duty_cycle > (0xff)`, ou seja, 256–511, subtrair 0xff é equivalente a aumentar de 1 a 255, então o brilho aumenta gradualmente.
Quando duty_cycle<= 0xff, the duty_cycle increases from 0 to 255, while the set brightness is 255 - duty_cycle. This effectively decreases the brightness from 255 to 0, causing the light to gradually dim. This achieves the breathing light effect.
Haha, você achou que nossa simplificação acabou?
Não, não, não
Na verdade, as linhas 7 a 10 poderiam ser ainda mais simplificadas. É aqui que a função de valor absoluto se torna útil.
O que? Por que usar a função de valor absoluto?
Veja a linha 10: 0xff - duty_cycle é equivalente a duty_cycle - 0xff e então pega o valor absoluto. Ok, aqui está o código simplificado:
A função macro para obter o valor absoluto é a seguinte
Por fim, incluí todo o código simplificado abaixo

O que você acha? Não é simples? (* ̄︶ ̄)




