#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "hardware/irq.h"
#include "hardware/timer.h"
#include "hardware/pwm.h"
#include "hardware/adc.h"
#include "hardware/i2c.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// Definiciones de pines
#define BUZZER_PIN 16
#define POTENTIOMETER_PIN 26
#define KEYPAD_ROWS {6, 7, 8, 9}
#define KEYPAD_COLS {10, 11, 12, 13}
#define I2C_SDA_PIN 4
#define I2C_SCL_PIN 5
#define I2C_PORT i2c0
#define LCD_ADDRESS 0x27 // Dirección I2C del LCD
volatile uint16_t frequency = 0; // Frecuencia inicial en Hz
volatile uint8_t volume = 0; // Volumen inicial en porcentaje
char input_buffer[5]; // Buffer para almacenar la entrada del teclado
uint8_t buffer_index = 0; // Índice del buffer
// Prototipos de funciones
void init_peripherals(void);
void init_pwm(void);
void init_adc(void);
void set_frequency(uint16_t freq);
void set_volume(uint8_t vol);
void display_update(uint16_t freq, uint8_t vol);
void keypad_isr(uint gpio, uint32_t events);
void scan_keypad(void);
// Funciones LCD
void lcd_init(void);
void lcd_clear(void);
void lcd_set_cursor(int row, int col);
void lcd_print(const char *str);
void lcd_send_cmd(uint8_t cmd);
void lcd_send_data(uint8_t data);
void lcd_send_byte(uint8_t val, uint8_t mode);
void i2c_write_byte(uint8_t val);
void lcd_backlight_on(void);
void lcd_backlight_off(void);
// Configuración de pines del teclado matricial
const uint8_t rows[] = KEYPAD_ROWS;
const uint8_t cols[] = KEYPAD_COLS;
int main() {
stdio_init_all(); // Inicializar todos los puertos estándar de E/S
init_peripherals(); // Inicializar periféricos
init_pwm(); // Inicializar PWM
init_adc(); // Inicializar ADC
while (1) {
// Leer el valor del potenciómetro y ajustar el volumen
adc_select_input(0); // Asegurarse de seleccionar el canal adecuado
uint16_t pot_value = adc_read(); // Leer valor del potenciómetro
set_volume(pot_value * 100 / 4095); // Convertir a porcentaje y ajustar volumen
display_update(frequency, volume); // Actualizar la pantalla LCD
sleep_ms(500); // Esperar 500 ms
}
return 0;
}
// Inicializar periféricos
void init_peripherals(void) {
gpio_init(BUZZER_PIN); // Inicializar pin del buzzer
gpio_set_dir(BUZZER_PIN, GPIO_OUT); // Establecer dirección de salida
// Inicializar filas del teclado matricial
for (int i = 0; i < 4; ++i) {
gpio_init(rows[i]);
gpio_set_dir(rows[i], GPIO_OUT);
gpio_put(rows[i], 0);
}
// Inicializar columnas del teclado matricial
for (int i = 0; i < 4; ++i) {
gpio_init(cols[i]);
gpio_set_dir(cols[i], GPIO_IN);
gpio_pull_up(cols[i]);
gpio_set_irq_enabled_with_callback(cols[i], GPIO_IRQ_EDGE_FALL, true, &keypad_isr);
}
// Inicialización de I2C
i2c_init(I2C_PORT, 100 * 1000); // Inicializar I2C a 100kHz
gpio_set_function(I2C_SDA_PIN, GPIO_FUNC_I2C);
gpio_set_function(I2C_SCL_PIN, GPIO_FUNC_I2C);
gpio_pull_up(I2C_SDA_PIN);
gpio_pull_up(I2C_SCL_PIN);
// Inicialización de la pantalla LCD
lcd_init();
lcd_backlight_on();
}
// Inicializar PWM para el buzzer
void init_pwm(void) {
gpio_set_function(BUZZER_PIN, GPIO_FUNC_PWM); // Configurar función PWM para el pin del buzzer
uint slice_num = pwm_gpio_to_slice_num(BUZZER_PIN); // Obtener número de slice del PWM
pwm_set_wrap(slice_num, 12500); // Establecer el periodo del PWM
pwm_set_enabled(slice_num, true); // Habilitar PWM
set_frequency(frequency); // Establecer frecuencia inicial
set_volume(volume); // Establecer volumen inicial
}
// Inicializar ADC para el potenciómetro
void init_adc(void) {
adc_init(); // Inicializar ADC
adc_gpio_init(POTENTIOMETER_PIN); // Inicializar pin del potenciómetro para ADC
adc_select_input(0); // Seleccionar canal de entrada ADC
}
// Establecer frecuencia del PWM para el buzzer
void set_frequency(uint16_t freq) {
frequency = freq; // Actualizar variable de frecuencia
uint slice_num = pwm_gpio_to_slice_num(BUZZER_PIN); // Obtener número de slice del PWM
uint32_t clock = 125000000; // Frecuencia del reloj del sistema en Hz
if (freq > 0) {
uint16_t top = clock / freq - 1; // Calcular valor de top para el PWM
pwm_set_wrap(slice_num, top); // Establecer valor de top para el PWM
pwm_set_gpio_level(BUZZER_PIN, (top + 1) * volume / 100); // Establecer nivel del GPIO basado en el volumen
} else {
pwm_set_gpio_level(BUZZER_PIN, 0); // Apagar PWM si la frecuencia es 0
}
}
// Establecer volumen ajustando el duty cycle del PWM
void set_volume(uint8_t vol) {
volume = vol; // Actualizar variable de volumen
uint slice_num = pwm_gpio_to_slice_num(BUZZER_PIN); // Obtener número de slice del PWM
uint16_t top = pwm_hw->slice[slice_num].top; // Obtener valor de top actual
uint16_t level = top * vol / 100; // Calcular nivel del GPIO basado en el volumen
pwm_set_gpio_level(BUZZER_PIN, level); // Establecer nivel del GPIO
}
// Actualizar la pantalla LCD con la frecuencia y el volumen
void display_update(uint16_t freq, uint8_t vol) {
char buffer[17];
lcd_clear();
snprintf(buffer, sizeof(buffer), "Freq: %d Hz", freq);
lcd_set_cursor(0, 0);
lcd_print(buffer);
snprintf(buffer, sizeof(buffer), "Vol : %d %%", vol);
lcd_set_cursor(1, 0);
lcd_print(buffer);
}
// Procesar la entrada del buffer del teclado
void process_input() {
if (buffer_index > 0) {
input_buffer[buffer_index] = '\0'; // Terminar cadena en el buffer
uint16_t new_frequency = atoi(input_buffer); // Convertir cadena a número
set_frequency(new_frequency); // Establecer nueva frecuencia
printf("New frequency set: %d\n", new_frequency); // Depuración
buffer_index = 0; // Reiniciar índice del buffer
memset(input_buffer, 0, sizeof(input_buffer)); // Limpiar buffer
}
}
// Interrupción del teclado matricial
void keypad_isr(uint gpio, uint32_t events) {
static absolute_time_t last_interrupt_time;
absolute_time_t current_time = get_absolute_time();
if (absolute_time_diff_us(last_interrupt_time, current_time) < 20000) {
return; // Debounce: ignorar interrupciones dentro de 20ms
}
last_interrupt_time = current_time;
scan_keypad(); // Escanear teclado matricial
}
// Escanear el teclado matricial
void scan_keypad(void) {
for (int r = 0; r < 4; ++r) {
gpio_put(rows[r], 1); // Activar fila actual
sleep_us(5); // Pequeño retraso para estabilizar la señal
for (int c = 0; c < 4; ++c) {
if (gpio_get(cols[c]) == 0) { // Verificar si se presionó una tecla
char key;
// Asignar teclas correspondientes
if (r == 3 && c == 0) key = '*';
else if (r == 3 && c == 3) key = '#';
else if (r == 3) key = '0' + c + 1;
else key = '0' + r * 3 + c + 1;
printf("Key pressed: %c\n", key); // Depuración
if (key == '*') {
set_frequency(0); // Detener el tono
printf("Tone stopped\n"); // Depuración
} else if (key == '#') {
process_input(); // Procesar la entrada y actualizar el LCD
} else {
if (buffer_index < sizeof(input_buffer) - 1) {
input_buffer[buffer_index++] = key; // Almacenar tecla en el buffer
printf("Buffer: %s\n", input_buffer); // Depuración
}
}
// Espera hasta que se suelte la tecla
while (gpio_get(cols[c]) == 0) {
sleep_ms(10);
}
}
}
gpio_put(rows[r], 0); // Desactivar fila actual
}
}
// Inicializar LCD
void lcd_init(void) {
sleep_ms(50);
lcd_send_cmd(0x33); // Inicializar LCD
lcd_send_cmd(0x32); // Inicializar LCD
lcd_send_cmd(0x28); // Función de configuración: 4 bits, 2 líneas, 5x8 puntos
lcd_send_cmd(0x0C); // Display ON, cursor OFF, sin parpadeo
lcd_send_cmd(0x06); // Incremento de dirección, sin desplazamiento
lcd_send_cmd(0x01); // Limpiar display
sleep_ms(2);
}
// Limpiar display del LCD
void lcd_clear(void) {
lcd_send_cmd(0x01); // Limpiar display
sleep_ms(2);
}
// Establecer cursor en el LCD
void lcd_set_cursor(int row, int col) {
int address = (row == 0 ? 0x00 : 0x40) + col;
lcd_send_cmd(0x80 | address);
}
// Imprimir cadena en el LCD
void lcd_print(const char *str) {
while (*str) {
lcd_send_data(*str++);
}
}
// Enviar comando al LCD
void lcd_send_cmd(uint8_t cmd) {
lcd_send_byte(cmd, 0);
}
// Enviar datos al LCD
void lcd_send_data(uint8_t data) {
lcd_send_byte(data, 1);
}
// Enviar byte al LCD
void lcd_send_byte(uint8_t val, uint8_t mode) {
uint8_t high_nibble = (val & 0xF0) | (mode ? 0x09 : 0x08); // Enciende la luz de fondo
uint8_t low_nibble = ((val << 4) & 0xF0) | (mode ? 0x09 : 0x08); // Enciende la luz de fondo
i2c_write_byte(high_nibble);
i2c_write_byte(high_nibble | 0x04);
i2c_write_byte(high_nibble & ~0x04);
i2c_write_byte(low_nibble);
i2c_write_byte(low_nibble | 0x04);
i2c_write_byte(low_nibble & ~0x04);
}
// Escribir byte en I2C
void i2c_write_byte(uint8_t val) {
i2c_write_blocking(I2C_PORT, LCD_ADDRESS, &val, 1, false);
}
// Encender la luz de fondo del LCD
void lcd_backlight_on(void) {
uint8_t val = 0x08; // Enciende la luz de fondo
i2c_write_byte(val);
}
// Apagar la luz de fondo del LCD
void lcd_backlight_off(void) {
uint8_t val = 0x00; // Apaga la luz de fondo
i2c_write_byte(val);
}