#include <util/delay.h>
#include <avr/io.h>
// ========================================================================
// PINOS LCD
// ========================================================================
// LCD - Pinos de controle
#define CONTR_LCD PORTA
#define RS PA1
//#define RW PA3
#define E PA5
// Definições do Sensor DHT22
// Pino 8 do Arduino Mega
#define DHT_PIN PH5
// LCD - Pinos de dados (8 bits)
#define DADOS_LCD PORTC
#define nibble_dados 1
#define pulse_enable() _delay_us(1); set_bit(CONTR_LCD, E); _delay_us(1); clr_bit(CONTR_LCD, E); _delay_us(45);
// ========================================================================
// Macros para o trabalho com bits
// ========================================================================
#define set_bit(y,bit) (y|=(1<<bit)) // Coloca em 1 o bit x da variável Y
#define clr_bit(y,bit) (y&=~(1<<bit)) // Coloca em 0 o bit x da variável Y
#define cpl_bit(y,bit) (y^=(1<<bit)) // Troca o estado lógico do bit x da variável Y
#define tst_bit(y,bit) (y&(1<<bit)) // Retorna 0 ou 1 conforme leitura do bit
char* itoa(int value, char* str, int base);
uint8_t dht_read(uint8_t dht);
// ========================================================================
// LCD - Enviar caracteres/instruções
// ========================================================================
void lcd_cmd(unsigned char c, char cd)
{
// cd : indica se instrução (0) ou caractere (1)
if(cd == 0)
clr_bit(CONTR_LCD,RS);
else
set_bit(CONTR_LCD,RS);
// Primeiro nibble de dados - 4 MSB
#if (nibble_dados)
// Compila o código para os pinos de dados do LCD nos 4 MSB do PORT
DADOS_LCD = (DADOS_LCD & 0x0F) | (0xF0 & c);
#else
// Compila o código para os pinos de dados do LCD nos 4 LSB do PORT
DADOS_LCD = (DADOS_LCD & 0xF0) | (c >> 4);
#endif
pulse_enable();
// Segundo nibble de dados - 4 LSB
#if (nibble_dados)
// Compila o código para os pinos de dados do LCD nos 4 MSB do PORT
DADOS_LCD = (DADOS_LCD & 0x0F) | (0xF0 & (c << 4));
#else
// Compila o código para os pinos de dados do LCD nos 4 LSB do PORT
DADOS_LCD = (DADOS_LCD & 0xF0) | (0x0F & c);
#endif
pulse_enable();
// Se for instrução de retorno ou limpeza espera LCD estar pronto
if((cd == 0) && (c < 4)) _delay_ms(2);
}
// ========================================================================
// LCD - Inicializacao do LCD com via de dados de 4 Bits
// ========================================================================
void lcd_init_4bits()
{
// Como o LCD será só escrito, R/W é sempre zero.
clr_bit(CONTR_LCD,RS); // RS em zero indicando que o dado para o LCD será uma instrução
clr_bit(CONTR_LCD,E); // Pino de habilitação em zero
// Tempo para estabilizar a tensão do LCD, após VCC ultrapassar 4.5 V (na prática pode ser maior)
_delay_ms(20);
// Interface de 8 bits
#if (nibble_dados)
DADOS_LCD = (DADOS_LCD & 0x0F) | 0x30;
#else
DADOS_LCD = (DADOS_LCD & 0xF0) | 0x03;
#endif
pulse_enable(); // Habilitação respeitando os tempos de resposta do LCD
_delay_ms(5);
pulse_enable();
_delay_us(200);
pulse_enable();
// Interface de 4 bits
// Deve ser enviado duas vezes (a outra está abaixo)
#if (nibble_dados)
DADOS_LCD = (DADOS_LCD & 0x0F) | 0x20;
#else
DADOS_LCD = (DADOS_LCD & 0xF0) | 0x02;
#endif
pulse_enable();
// Interface de 4 bits 2 linhas
// Aqui se habilita as 2 linhas
lcd_cmd(0x28,0);
// São enviados os 2 nibbles (0x2 e 0x8)
lcd_cmd(0x08,0); // Desliga o display
lcd_cmd(0x01,0); // Limpa todo o display
lcd_cmd(0x0C,0); // Mensagem aparente cursor inativo não piscando
lcd_cmd(0x80,0); // Inicializa cursor na primeira posição a esquerda
}
// ========================================================================
// LCD - Escrita no LCD
// ========================================================================
void lcd_write(char *c)
{
for (; *c!=0;c++) lcd_cmd(*c,1);
}
// Protótipo da função itoa()
char* itoa(int value, char* str, int base)
{
// Verifica se a base é suportada (apenas base 10 é implementada aqui)
if (base != 10) {
*str = '\0';
return str;
}
// Inicializa o índice para a string resultante
int i = 0;
// Armazena o sinal do número (positivo ou negativo)
int is_negative = 0;
// Caso o número seja zero, adiciona '0' à string e encerra
if (value == 0) {
str[i++] = '0';
str[i] = '\0';
return str;
}
// Se o número for negativo, armazena o sinal e transforma em positivo
if (value < 0) {
is_negative = 1;
value = -value;
}
// Loop para converter o número em string reversa
while (value != 0) {
int rem = value % 10; // Calcula o dígito menos significativo
str[i++] = rem + '0'; // Converte o dígito em caractere e armazena
value /= 10; // Remove o dígito menos significativo
}
// Adiciona o sinal '-' se o número for negativo
if (is_negative) {
str[i++] = '-';
}
// Adiciona o terminador de string
str[i] = '\0';
// Inverte a string resultante
int start = 0;
int end = i - 1;
while (start < end) {
char temp = str[start];
str[start] = str[end];
str[end] = temp;
start++;
end--;
}
return str;
}
int main()
{
// Configurar portas de saida do LCD
DDRA = 0xff;
DDRC = 0xff;
// Inicializar LCD
lcd_init_4bits();
// Buffer para armazenar a temperatura como texto
// char temperature_text[16];
// // Mostrar a mensagem "Olá Mundo"
// lcd_cmd(0x80, 0);
// lcd_write("Temperatura: ");
while (1)
{
uint8_t result = dht_read(DHT_PIN);
if (result != 0)
{
lcd_cmd(0x01, 0); // Limpa o display
_delay_ms(2);
lcd_cmd(0x80, 0); // Posiciona o cursor na primeira linha
lcd_write("Erro: ");
char buffer[2];
itoa(result, buffer, 10);
lcd_write(buffer);
}
_delay_ms(2000); // Aguarda 2 segundos antes de fazer a próxima leitura
}
}
// Função para ler o valor digital do sensor DHT22
uint8_t dht_read(uint8_t dht)
{
uint8_t dht_data[5] = {0, 0, 0, 0, 0};
// Configurando o pino como saída
DDRH |= (1 << dht);
// Enviando sinal de inicialização
PORTH &= ~(1 << dht);
_delay_ms(18);
PORTH |= (1 << dht);
_delay_us(1);
DDRH &= ~(1 << dht); // Configurando o pino como entrada
// Esperando resposta do sensor
uint8_t timeout_counter = 0;
while (PINH & (1 << dht))
{
if (++timeout_counter > 100)
{
return 1; // Erro de timeout
}
_delay_us(1);
}
timeout_counter = 0;
while (!(PINH & (1 << dht)))
{
if (++timeout_counter > 100)
{
return 2; // Erro de timeout
}
_delay_us(1);
}
timeout_counter = 0;
while (PINH & (1 << dht))
{
if (++timeout_counter > 100)
{
return 3; // Erro de timeout
}
_delay_us(1);
}
// Lendo os dados do sensor
for (uint8_t i = 0; i < 5; ++i)
{
uint8_t byte = 0;
for (uint8_t j = 0; j < 8; ++j)
{
timeout_counter = 0;
while (!(PINH & (1 << dht)))
{
if (++timeout_counter > 100)
{
return 4; // Erro de timeout
}
_delay_us(1);
}
_delay_us(30); // Espera para determinar se o bit é 0 ou 1
if (PINH & (1 << dht))
{
byte |= (1 << (7 - j)); // Bit é 1
timeout_counter = 0;
while (PINH & (1 << dht))
{
if (++timeout_counter > 100)
{
return 5; // Erro de timeout
}
_delay_us(1);
}
}
}
dht_data[i] = byte;
}
// Verificando a soma de verificação
if ((uint8_t)(dht_data[0] + dht_data[1] + dht_data[2] + dht_data[3]) != dht_data[4])
{
return 6; // Erro de checksum
}
// Calculando temperatura e umidade
uint16_t temperature = ((uint16_t)dht_data[2] << 8) | dht_data[3];
uint16_t humidity = ((uint16_t)dht_data[0] << 8) | dht_data[1];
// Exibindo os valores lidos
lcd_cmd(0x01, 0); // Limpa o display
_delay_ms(2);
lcd_cmd(0x80, 0); // Posiciona o cursor na primeira linha
lcd_write("Temp: ");
lcd_cmd(0x86, 0); // Posiciona o cursor após "Temp: "
char buffer[8];
itoa(temperature / 10, buffer, 10);
lcd_write(buffer);
lcd_write(".");
itoa(temperature % 10, buffer, 10);
lcd_write(buffer);
lcd_write(" C");
lcd_cmd(0xC0, 0); // Posiciona o cursor na segunda linha
lcd_write("Umidade: ");
lcd_cmd(0xC9, 0); // Posiciona o cursor após "Umidade: "
itoa(humidity / 10, buffer, 10);
lcd_write(buffer);
lcd_write(".");
itoa(humidity % 10, buffer, 10);
lcd_write(buffer);
lcd_write(" %");
return 0; // Sucesso
}