#include <avr/io.h> // Biblioteca de I/O do AVR
#include <util/delay.h> // Biblioteca para delay
#include "usart.h" // Biblioteca USART para comunicação serial
/*Usamos o conversor ADC para fazer a leitura de duas tensões
analógicas nos canais ADC4 (porta PF4) e ADC1 (porta PF1), em intervalos de 2 s.
Transmitimos pela USART0 a diferença de tensão ADC4 - ADC1. Use a fonte interna
de tensão de 1,1 V. Escreva o programa em C. Faça a simulação com o programa
em https://wokwi.com. Use potenciômetros nas entrada ADC4 e ADC1 para testar
o circuito.*/
// Função para inicializar o ADC
void ADC_init(void) {
/*
ADMUX - Registrador para configurar a referência de tensão e o canal de entrada
----------------------------------------------------------------------------
| REFS1 | REFS0 | ADLAR | MUX4 | MUX3 | MUX2 | MUX1 | MUX0 |
----------------------------------------------------------------------------
REFS1:0 - Seleção da referência de tensão
00 - AREF, pino externo
01 - AVcc, com capacitor externo no pino AREF (normalmente 5V)
10 - Tensão interna de referência de 1.1V (para precisão em medições mais baixas)
ADLAR - Ajuste do alinhamento do resultado
0 - Alinhado à direita (o mais comum, usa todo o intervalo de 10 bits)
1 - Alinhado à esquerda
MUX[4:0] - Seleção do canal de entrada ADC (0 a 7 no ATmega2560)
Neste exemplo, configuramos:
- REFS0 = 1 (AVcc como referência de tensão, geralmente 5V)
- REFS1 = 0
- ADLAR = 0 (alinhamento à direita)
- MUX[4:0] = 00000 (canal ADC0)
*/
ADMUX = (1 << REFS0); // Configura AVcc como referência de tensão e seleciona o canal ADC0
/*
ADCSRA - Registrador de controle e status do ADC
----------------------------------------------------------------------------
| ADEN | ADSC | ADATE | ADIF | ADIE | ADPS2 | ADPS1 | ADPS0 |
----------------------------------------------------------------------------
ADEN - Habilita o ADC (1 = habilitado, 0 = desabilitado)
ADSC - Inicia uma conversão (1 = inicia, 0 = sem efeito)
ADATE - Modo de disparo automático (1 = habilitado, 0 = manual)
ADIF - Indicador de fim de conversão (1 = concluído, 0 = não concluído)
ADIE - Habilita interrupção do ADC (1 = interrupção habilitada, 0 = desabilitada)
ADPS[2:0] - Configura o prescaler do ADC para dividir a frequência do clock
000 = divisor de 2
001 = divisor de 2
010 = divisor de 4
011 = divisor de 8
100 = divisor de 16
101 = divisor de 32
110 = divisor de 64
111 = divisor de 128
Neste exemplo, configuramos:
- ADEN = 1 (Habilita o ADC)
- ADPS[2:0] = 111 (Prescaler de 128, ideal para clock de 16MHz)
*/
ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // Habilita o ADC com prescaler de 128
}
// Função para ler o valor do ADC em um canal específico (0 a 7)
uint16_t ADC_read(uint8_t channel) {
/* Seleciona o canal do ADC:
- Limpa os bits MUX4:0 no ADMUX para configurar o novo canal
- Adiciona o canal selecionado (0 a 7) para o ADMUX
*/
ADMUX = (ADMUX & 0xF0) | (channel & 0x0F); // Mantém a referência e ajusta o canal desejado
/* Inicia a conversão:
- ADSC = 1 para iniciar a conversão. Esse bit é automaticamente resetado após a conversão.
*/
ADCSRA |= (1 << ADSC); // Inicia a conversão
/* Espera a conversão:
- O loop "while" espera até o bit ADSC retornar a 0, indicando o fim da conversão.
*/
while (ADCSRA & (1 << ADSC)); // Espera a conversão finalizar
/* Retorna o valor convertido:
- O resultado da conversão é armazenado em dois registradores: ADCL (byte inferior) e ADCH (byte superior)
- O retorno combina os dois em um valor de 10 bits (0-1023).
*/
return ADC; // Retorna o valor convertido (entre 0 e 1023)
}
int main(void) {
uint16_t adc_value; // Variável para armazenar o valor lido do ADC
float voltage; // Variável para armazenar a tensão calculada
USART0_configura(); // Inicializa a comunicação serial (USART0) configurada em outra biblioteca
ADC_init(); // Inicializa o ADC para leituras
while (1) {
adc_value = ADC_read(0); // Lê o valor do ADC no canal 0 (ADC0)
/* Calcula a tensão medida:
- O valor do ADC varia de 0 a 1023 (10 bits).
- Como estamos usando uma referência de 5V, calculamos a tensão multiplicando por (5.0 / 1023.0).
*/
voltage = (adc_value / 1023.0) * 5.0; // Converte o valor ADC para uma tensão em volts (0 a 5V)
/* Transmite o valor da tensão pela USART:
- Usa snprintf para formatar o valor da tensão em uma string e enviar pela função USART0_transmite_string_RAM.
*/
char buffer[20]; // Buffer para armazenar a string da tensão
snprintf(buffer, sizeof(buffer), "Tensao: %.2fV\n", voltage); // Converte o valor para uma string
USART0_transmite_string_RAM((uint8_t*)buffer); // Envia a string pela USART
_delay_ms(1000); // Aguarda 1 segundo antes de fazer a próxima leitura
}
}