#include <avr/io.h> // Encabezado estándar de AVR
#include <util/delay.h> // Encabezado de retraso
#include <string.h>
#include <stdio.h>
#include <avr/interrupt.h>
#include <stdlib.h>
#define KEY_PRT PORTC // Puerto del teclado
#define KEY_DDR DDRC // DDR (Registro de dirección) del teclado
#define KEY_PIN PINC // PIN del teclado
#define F_CPU 16000000UL
#ifndef XPCU
#define XPCU 16000000UL
#endif
// Definición de pines para la LCD en modo de 4 bits
#define LCD_RS_PIN 2
#define LCD_EN_PIN 3
#define LCD_D4_PIN 4
#define LCD_D5_PIN 5
#define LCD_D6_PIN 6
#define LCD_D7_PIN 7
#define LCD_PORT PORTA
#define LCD_DDR DDRA
const char KEY[4][3] = {{'1','2','3'}, {'4','5','6'}, {'7', '8', '9'}, {'*', '0', '#'}};
unsigned char colloc, rowloc;
// Buzzer
int buzzer_status = 0;
unsigned long int duty;
int prescaler = 1;
unsigned int high_delay = 0, low_delay = 0;
int stop_buzzer = 1;
unsigned long int frequency;
// Funciones para controlar la LCD en modo de 4 bits
void LCD_command(unsigned char cmnd);
void LCD_data(unsigned char data);
void LCD_init();
void LCD_clear();
void LCD_setCursor(int row, int column);
void LCD_displayString(const char *str);
void LCD_displayInt(int num);
// Funciones del teclado
void KEYPAD_Init(){
DDRD = 0xFF;
KEY_DDR = 0xF0; // Configura el teclado como entrada y salida
KEY_PRT = 0xFF; // Configura el puerto del teclado como entrada con pull-up
}
// Implementaciones de las funciones para controlar la LCD en modo de 4 bits
void LCD_command(unsigned char cmnd) {
LCD_PORT = (LCD_PORT & 0x0F) | (cmnd & 0xF0); // Envío del comando alto
LCD_PORT &= ~(1 << LCD_RS_PIN); // RS = 0 para comandos
LCD_PORT |= (1 << LCD_EN_PIN); // Habilitar EN
_delay_us(1);
LCD_PORT &= ~(1 << LCD_EN_PIN); // Deshabilitar EN
_delay_us(200);
LCD_PORT = (LCD_PORT & 0x0F) | (cmnd << 4); // Envío del comando bajo
LCD_PORT |= (1 << LCD_EN_PIN); // Habilitar EN
_delay_us(1);
LCD_PORT &= ~(1 << LCD_EN_PIN); // Deshabilitar EN
_delay_ms(2);
}
void LCD_data(unsigned char data) {
LCD_PORT = (LCD_PORT & 0x0F) | (data & 0xF0); // Envío del dato alto
LCD_PORT |= (1 << LCD_RS_PIN); // RS = 1 para datos
LCD_PORT |= (1 << LCD_EN_PIN); // Habilitar EN
_delay_us(1);
LCD_PORT &= ~(1 << LCD_EN_PIN); // Deshabilitar EN
_delay_us(200);
LCD_PORT = (LCD_PORT & 0x0F) | (data << 4); // Envío del dato bajo
LCD_PORT |= (1 << LCD_EN_PIN); // Habilitar EN
_delay_us(1);
LCD_PORT &= ~(1 << LCD_EN_PIN); // Deshabilitar EN
_delay_ms(2);
}
void LCD_init() {
// Configurar los pines de la LCD como salida
LCD_DDR |= (1 << LCD_RS_PIN) | (1 << LCD_EN_PIN) | (1 << LCD_D4_PIN) | (1 << LCD_D5_PIN) | (1 << LCD_D6_PIN) | (1 << LCD_D7_PIN);
LCD_command(0x02); // Inicialización en modo de 4 bits
LCD_command(0x28); // 2 líneas, matriz de 5x8
LCD_command(0x0C); // Mostrar cursor apagado, no parpadeo
LCD_command(0x06); // Incremento del cursor
LCD_clear(); // Limpiar la pantalla
}
void LCD_clear() {
LCD_command(0x01); // Limpiar la pantalla
_delay_ms(2);
}
void LCD_setCursor(int row, int column) {
int row_offsets[] = {0x00, 0x40};
LCD_command(0x80 | (row_offsets[row] + column)); // Establecer posición del cursor
}
void LCD_displayString(const char *str) {
while (*str) {
LCD_data(*str++);
}
}
// Función para inicializar el ADC
void ADC_init() {
ADCSRA |= (1 << ADEN); // Habilitar el ADC
ADMUX |= (1 << REFS0); // Voltaje de referencia en AVCC
ADCSRA |= (1 << ADPS0) | (1 << ADPS1) | (1 << ADPS2); // Divisor de frecuencia de 128
}
// Función para leer el valor del potenciómetro en porcentaje
int readPotentiometer() {
// Seleccionar el canal ADC (ejemplo: ADC0)
ADMUX &= 0xF0; // Borrar los bits de selección de canal
// Configurar el canal, por ejemplo, ADC0
// ADMUX |= (0 << MUX0) | (0 << MUX1) | (0 << MUX2) | (0 << MUX3);
// Iniciar la conversión
ADCSRA |= (1 << ADSC);
// Esperar a que la conversión termine
while (ADCSRA & (1 << ADSC));
// Retornar el valor convertido
return ADC;
}
char call_matricial(){
int column;
for (column=0; column<3; column++){
PORTC = (~(1 << (column+4)))|(0b00001111);
if (!(PINC&0x01)){
return KEY[0][column];
}
else if (!(PINC&0x02)){
return KEY[1][column];
}
else if (!(PINC&0x04)){
return KEY[2][column];
}
else if (!(PINC&0x08)){
return KEY[3][column];
}
}
_delay_ms(13);
return ' ';
}
ISR(TIMER1_COMPA_vect) {
if (stop_buzzer == 0) {
PORTB ^= (1 << PB7); // Alternar el estado del pin PB7 (toggle)
}
}
void calcular_delay(unsigned long int frequency, unsigned long int duty, int prescaler) {
unsigned long int period = F_CPU / (prescaler * frequency);
unsigned long int high_time = (period * duty) / 1000; // Asegúrate de que 'duty' esté en el rango correcto (0 a 1000)
unsigned long int low_time = period - high_time;
unsigned long int high_delay = (high_time * F_CPU) / F_CPU;
unsigned long int low_delay = (low_time * F_CPU) / F_CPU;
OCR1A = high_delay; // Ajustar el ciclo de trabajo directamente al tiempo alto
}
// Bucle principal
int main(void) {
KEYPAD_Init();
LCD_init();
ADC_init();
DDRD &= ~(1 << DDD0);
LCD_setCursor(0, 0);
LCD_displayString("FREQ: ");
LCD_setCursor(1, 0);
LCD_displayString("VOL : ");
int potValue = 0;
float percentage = 0.0;
char buffer[20];
char key = ' '; // Variable para almacenar la tecla presionada
char sequence[8] = ""; // Array para almacenar la secuencia de teclas presionadas
// Buzzer
DDRB |= (1 << PB7); // Configurar PB7 como salida
TCCR1A = 0x00;
TCCR1B = (1 << WGM12) | (1 << CS10); // Modo CTC, prescaler 1
OCR1A = F_CPU / (prescaler * frequency) - 1; // Valor de comparación para la frecuencia
TIMSK1 = (1 << OCIE1A); // Habilitar interrupción en comparación OCR1A
sei();
while (1) {
potValue = readPotentiometer();
percentage = ((float)potValue / 1023.0) * 100.0;
duty = (percentage / 100.0) * 1000; // Convertir a un valor entre 0 y 1000
sprintf(buffer, "%d%", (int)percentage);
LCD_setCursor(1, 6);
LCD_displayString(" "); // Espacios en blanco para borrar el valor anterior
// Mostrar el valor en porcentaje actualizado en la LCD
LCD_setCursor(1, 6);
LCD_displayString(buffer);
LCD_displayString("%");
_delay_ms(300); // Espera para una actualización más lenta
// Obtener la tecla presionada
key = call_matricial(); // Obtener la tecla presionada
if (key != ' ' && key != '*' && key != '#') {
if (strlen(sequence) < 7) { // Verificar espacio en la secuencia
sprintf(sequence + strlen(sequence), "%c", key); // Agregar tecla a la secuencia
LCD_setCursor(0, 6);
LCD_displayString(sequence); // Mostrar la secuencia en la LCD
}
} else if (key == '#') {
stop_buzzer = 0; // Activar el buzzer
frequency = atoi(sequence); // Actualizar la frecuencia con la secuencia
calcular_delay(frequency, duty, prescaler);
OCR1A = F_CPU / (prescaler * frequency) - 1; // Actualizar la frecuencia del zumbador
memset(sequence, 0, sizeof(sequence)); // Limpiar la secuencia
} else if (key == '*') {
stop_buzzer = 1; // Desactivar el buzzer
LCD_clear();
LCD_setCursor(0, 0);
LCD_displayString("FREQ: ");
LCD_setCursor(1, 0);
LCD_displayString("VOL : ");
}
}
return 0;
}