#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "pico/stdlib.h"
#include "pico/binary_info.h"
#include "ssd1306.h"
#include "hardware/i2c.h"
#include "hardware/adc.h"
#include "hardware/pwm.h"
#include "hardware/clocks.h"
// Definição dos pinos I2C
const uint I2C_SDA = 14;
const uint I2C_SCL = 15;
// Definição dos pinos do Joystick
#define JOY_X 26
#define JOY_Y 27
#define JOY_SW 22 // Botão do joystick
// Definição dos pinos dos LEDs RGB
#define LED_R_PIN 11
#define LED_G_PIN 12
#define LED_B_PIN 13
// Definição do pino do buzzer
#define BUZZER_PIN 21
#define DURACAO_NOTA 400 // Duração de cada nota em milissegundos
// Notas musicais para a música tema de Star Wars
const uint star_wars_notes[] = {
330, 330, 330, 262, 392, 523, 330, 262,
392, 523, 330, 659, 659, 659, 698, 523,
415, 349, 330, 262, 392, 523, 330, 262,
392, 523, 330, 659, 659, 659, 698, 523,
415, 349, 330, 523, 494, 440, 392, 330,
659, 784, 659, 523, 494, 440, 392, 330,
659, 659, 330, 784, 880, 698, 784, 659
};
// Duração das notas em milissegundos
const uint note_duration[] = {
500, 500, 500, 350, 150, 300, 500, 350,
150, 300, 500, 500, 500, 500, 350, 150,
300, 500, 500, 350, 150, 300, 500, 350,
150, 300, 650, 500, 150, 300, 500, 350,
150, 300, 500, 150, 300, 500, 350, 150,
300, 650, 500, 350, 150, 300, 500, 350,
150, 300, 500, 500, 500, 500, 350, 150
};
// Variáveis para PWM
const float DIVIDER_PWM = 16.0;
const uint16_t PERIOD = 4096;
uint16_t led_b_level = 100, led_r_level = 100;
uint slice_led_b, slice_led_r;
// Variáveis para o LED PWM
const uint LED = 12;
const uint16_t LED_STEP = 100;
uint16_t led_level = 100;
// Declarações de funções
void setup_pwm();
int read_joy_button();
void set_rgb(uint r, uint g, uint b) {
gpio_put(LED_R_PIN, r);
gpio_put(LED_G_PIN, g);
gpio_put(LED_B_PIN, b);
}
// Função para configurar o Buzzer
void configurar_buzzer(int pin) {
gpio_set_function(pin, GPIO_FUNC_PWM);
uint slice_num = pwm_gpio_to_slice_num(pin);
pwm_config configuracao = pwm_get_default_config();
pwm_config_set_clkdiv(&configuracao, 1.0);
pwm_init(slice_num, &configuracao, true);
}
// Inicializa o PWM no pino do buzzer
void pwm_init_buzzer(uint pin) {
gpio_set_function(pin, GPIO_FUNC_PWM);
uint slice_num = pwm_gpio_to_slice_num(pin);
pwm_config config = pwm_get_default_config();
pwm_config_set_clkdiv(&config, 4.0f); // Ajusta divisor de clock
pwm_init(slice_num, &config, true);
pwm_set_gpio_level(pin, 0);
}
// Toca uma nota com a frequência e duração especificadas
void play_tone(uint pin, uint frequency, uint duration_ms) {
uint slice_num = pwm_gpio_to_slice_num(pin);
uint32_t clock_freq = clock_get_hz(clk_sys);
uint32_t top = clock_freq / frequency - 1;
pwm_set_wrap(slice_num, top);
pwm_set_gpio_level(pin, top / 2);
sleep_ms(duration_ms);
pwm_set_gpio_level(pin, 0);
sleep_ms(50);
}
// Função principal para tocar a música
void play_star_wars(uint pin, volatile int *interrupt_flag) {
for (int i = 0; i < sizeof(star_wars_notes) / sizeof(star_wars_notes[0]); i++) {
// Verifica se o botão do joystick foi pressionado
if (read_joy_button() == 0) {
*interrupt_flag = 1;
break;
}
if (star_wars_notes[i] == 0) {
sleep_ms(note_duration[i]);
} else {
play_tone(pin, star_wars_notes[i], note_duration[i]);
}
}
}
// Função para exibir as opções no menu
void display_oled(char *menu[], int selected_option) {
struct render_area frame_area = {
.start_column = 0,
.end_column = ssd1306_width - 1,
.start_page = 0,
.end_page = ssd1306_n_pages - 1
};
calculate_render_area_buffer_length(&frame_area);
// Criação de um buffer zerado
uint8_t ssd[ssd1306_buffer_length];
memset(ssd, 0, ssd1306_buffer_length);
// Desenha as opções com o "0" na opção selecionada
for (int i = 0; i < 3; i++) {
int y_offset = 16 + i * 20; // Deslocamento vertical das opções
int text_offset_x = (128 - strlen(menu[i]) * 8) / 2; // Centraliza o texto
// Desenha a opção dentro da tela
ssd1306_draw_string(ssd, text_offset_x, y_offset, menu[i]);
// Desenha o "x" ao lado da opção selecionada
if (i == selected_option) {
// A posição horizontal do "x" será 18 pixels à esquerda do texto
ssd1306_draw_string(ssd, text_offset_x - 20, y_offset, "x");
}
}
// Renderiza o conteúdo no display
render_on_display(ssd, &frame_area);
}
// Função para exibir múltiplas mensagens dentro de uma opção
void display_messages(const char *messages[], int count) {
struct render_area frame_area = {
.start_column = 0,
.end_column = ssd1306_width - 1,
.start_page = 0,
.end_page = ssd1306_n_pages - 1
};
calculate_render_area_buffer_length(&frame_area);
// Criação de um buffer zerado
uint8_t ssd[ssd1306_buffer_length];
memset(ssd, 0, ssd1306_buffer_length);
// Desenha as mensagens
for (int i = 0; i < count; i++) {
int text_offset_x = (128 - strlen(messages[i]) * 8) / 2; // Centraliza o texto
int y_offset = 32 + i * 10; // Posição vertical para a mensagem
ssd1306_draw_string(ssd, text_offset_x, y_offset, (char *)messages[i]);
}
// Renderiza o conteúdo no display
render_on_display(ssd, &frame_area);
}
// Função para limpar o display OLED
void clear_display() {
struct render_area frame_area = {
.start_column = 0,
.end_column = ssd1306_width - 1,
.start_page = 0,
.end_page = ssd1306_n_pages - 1
};
calculate_render_area_buffer_length(&frame_area);
uint8_t ssd[ssd1306_buffer_length];
memset(ssd, 0, ssd1306_buffer_length); // Zera o buffer
render_on_display(ssd, &frame_area); // Limpa o display
}
// Função para ler o valor do eixo X do joystick
int read_joy_x() {
adc_select_input(0);
int joy_x = adc_read();
// Mapear o valor para -1 ou 1 baseado em uma faixa do joystick
if (joy_x < 1500) {
return 1; // Movimento para cima
} else if (joy_x > 2500) {
return -1; // Movimento para baixo
}
return 0; // Nenhuma movimentação
}
// Função para ler o valor do eixo Y do joystick
int read_joy_y() {
adc_select_input(1);
int joy_y = adc_read();
// Mapear o valor para -1 ou 1 baseado em uma faixa do joystick
if (joy_y < 1500) {
return -1; // Movimento para cima
} else if (joy_y > 2500) {
return 1; // Movimento para baixo
}
return 0; // Nenhuma movimentação
}
// Função para ler o botão do joystick
int read_joy_button() {
return gpio_get(JOY_SW); // Botão do joystick
}
// Função para inicializar o Joystick
void init_joystick() {
gpio_init(JOY_X);
gpio_set_dir(JOY_X, GPIO_IN);
gpio_init(JOY_Y);
gpio_set_dir(JOY_Y, GPIO_IN);
gpio_init(JOY_SW);
gpio_set_dir(JOY_SW, GPIO_IN);
gpio_pull_up(JOY_SW);
adc_init();
}
// Função para configurar o joystick (pinos de leitura e ADC)
void setup_joystick() {
adc_init();
adc_gpio_init(JOY_X);
adc_gpio_init(JOY_Y);
gpio_init(JOY_SW);
gpio_set_dir(JOY_SW, GPIO_IN);
gpio_pull_up(JOY_SW); // Ativa o pull-up no pino do botão para evitar flutuações
}
// Função para ler os valores dos eixos do joystick (X e Y)
void joystick_read_axis(uint16_t *vrx_value, uint16_t *vry_value) {
// Leitura do valor do eixo X do joystick
adc_select_input(0);
sleep_us(2);
*vrx_value = adc_read();
// Leitura do valor do eixo Y do joystick
adc_select_input(1);
sleep_us(2); // Pequeno delay para estabilidade
*vry_value = adc_read();
}
// Função para configurar o PWM de um LED (genérica para azul e vermelho)
void setup_pwm_led(uint led, uint *slice, uint16_t level) {
gpio_set_function(led, GPIO_FUNC_PWM);
*slice = pwm_gpio_to_slice_num(led);
pwm_set_clkdiv(*slice, DIVIDER_PWM);
pwm_set_wrap(*slice, PERIOD);
pwm_set_gpio_level(led, level);
pwm_set_enabled(*slice, true);
}
// Função para configurar o PWM do LED
void setup_pwm() {
uint slice;
gpio_set_function(LED, GPIO_FUNC_PWM);
slice = pwm_gpio_to_slice_num(LED);
pwm_set_clkdiv(slice, DIVIDER_PWM);
pwm_set_wrap(slice, PERIOD);
pwm_set_gpio_level(LED, led_level);
pwm_set_enabled(slice, true);
}
// Função para a lógica do menu interativo
void menu_interativo() {
int selected_option = 0;
int entered_option = -1; // Nenhuma opção foi selecionada inicialmente
int previous_button_state = 1; // Estado anterior do botão (considerando que o botão começa em 1)
// Define as opções do menu
char *menu[] = {
"CODE 1",
"CODE 2",
"CODE 3"
};
// Exibe o menu inicial
display_oled(menu, selected_option);
// Configura o buzzer
configurar_buzzer(BUZZER_PIN);
pwm_init_buzzer(BUZZER_PIN);
while (true) {
// Detecta o movimento do joystick no eixo X (mapeado para -1 ou 1)
int joy_x = read_joy_x();
if (joy_x == -1) { // Move para a esquerda
selected_option--;
} else if (joy_x == 1) { // Move para a direita
selected_option++;
}
// Garantir que a seleção esteja dentro dos limites
if (selected_option < 0) selected_option = 0;
if (selected_option > 2) selected_option = 2;
// Atualiza o menu com a opção selecionada
display_oled(menu, selected_option);
// Lê o estado atual do botão
int current_button_state = read_joy_button();
// Se o botão for pressionado (estado muda de 1 para 0)
if (current_button_state == 0 && previous_button_state == 1) {
if (entered_option == -1) {
// Se ainda não entrou em uma opção, entra nela
entered_option = selected_option; // Marca que entrou na opção
switch (selected_option) {
case 0: {
// Inicializa os pinos GPIO como saída
gpio_init(LED_R_PIN);
gpio_init(LED_G_PIN);
gpio_init(LED_B_PIN);
gpio_set_dir(LED_R_PIN, GPIO_OUT);
gpio_set_dir(LED_G_PIN, GPIO_OUT);
gpio_set_dir(LED_B_PIN, GPIO_OUT);
// Configura o joystick
setup_joystick();
setup_pwm_led(LED_B_PIN, &slice_led_b, led_b_level);
setup_pwm_led(LED_R_PIN, &slice_led_r, led_r_level);
//Depuração
printf("Entrou na Opção 1\n");
//Contador para sair do laço
int count = 1;
while (true) {
const char *messages[] = {
"Movimente o Joy"
};
display_messages(messages, 1);
uint16_t vrx_value, vry_value;
joystick_read_axis(&vrx_value, &vry_value); // Lê os valores dos eixos do joystick
// Ajusta os níveis PWM dos LEDs de acordo com os valores do joystick
pwm_set_gpio_level(LED_B_PIN, vrx_value);
pwm_set_gpio_level(LED_R_PIN, vry_value);
// Valores lidos para depuração
printf("X: %d, Y: %d\n", vrx_value, vry_value);
// Verifica se o botão do joystick foi pressionado para sair
int button_state = read_joy_button();
printf("Botão estado: %d\n", button_state); // Depuração
if (button_state == 0) {
sleep_ms(100);
count++;
printf("Contador: %d\n", count);
}
if (count > 2) {
sleep_ms(100); // Debounce
printf("BOTÃO APERTADO, SAINDO. e contador igual á: %d\n", count);
break; // Sai do laço for
}
// Atraso de 100ms entre as leituras
sleep_ms(100);
}
// Apaga os LEDs ao sair do case 0
pwm_set_gpio_level(LED_B_PIN, 0);
pwm_set_gpio_level(LED_R_PIN, 0);
const char *messages[] = {
"click",
"novamente"
};
display_messages(messages, 2);
break;
}
case 1: {
// Depuração
printf("Entrou na Opção 2\n");
sleep_ms(250);
//Contador para sair do laço
int count = 0;
volatile int interrupt_flag = 0; // Flag para interrupção
while (true) {
const char *messages[] = {
"Tocando Buzzer"
};
display_messages(messages, 1);
sleep_ms(50);
// Toca a música tema de Star Wars
play_star_wars(BUZZER_PIN, &interrupt_flag);
// Verifica se o botão do joystick foi pressionado para sair
int button_state = read_joy_button();
printf("Botão estado: %d\n", button_state); // Depuração
if (button_state == 0) {
sleep_ms(100);
count++;
printf("Contador: %d\n", count);
}
if (count >= 1) {
sleep_ms(100); // Debounce
printf("BOTÃO APERTADO, SAINDO. e contador igual á: %d\n", count);
break;
}
}
// Reseta a flag de interrupção
interrupt_flag = 0;
pwm_set_gpio_level(BUZZER_PIN, 0); // Desliga o buzzer
const char *messages[] = {
"click",
"novamente"
};
display_messages(messages, 2);
break;
}
case 2: {
printf("Entrou na Opção 3\n");
setup_pwm(); // Configura o PWM para o LED
uint up_down = 1; // Variável para controlar se o nível do LED aumenta ou diminui
while (true) {
const char *messages[] = {
"Aumentado e",
"Diminundo LED"
};
display_messages(messages, 2);
sleep_ms(50);
// Define o nível atual do PWM (duty cycle)
pwm_set_gpio_level(LED, led_level);
sleep_ms(100);
// Lógica para aumentar ou diminuir o nível do LED
if (up_down) {
led_level += LED_STEP;
if (led_level >= PERIOD)
up_down = 0; // Muda direção para diminuir quando atingir o período máximo
} else {
led_level -= LED_STEP;
if (led_level <= LED_STEP)
up_down = 1;
}
// Verifica se o botão do joystick foi pressionado para sair
if (read_joy_button() == 0) {
sleep_ms(100);
break;
}
}
// Desliga o LED ao sair da opção
pwm_set_gpio_level(LED, 0);
const char *messages[] = {
"click",
"novamente"
};
display_messages(messages, 2);
break;
}
}
// Mantém na tela até que o botão seja pressionado novamente
while (read_joy_button() == 0) {
sleep_ms(100);
}
// Aguarda o botão ser solto antes de voltar ao menu
while (read_joy_button() == 1) {
sleep_ms(100);
}
// Sai da opção e volta ao menu
entered_option = -1;
display_oled(menu, selected_option);
}
sleep_ms(200); // Atraso para evitar múltiplas leituras do botão
}
// Atualiza o estado anterior do botão
previous_button_state = current_button_state;
sleep_ms(100); // Delay para evitar leituras rápidas
}
}
int main() {
stdio_init_all();
// Inicialização do barramento I2C
i2c_init(i2c1, ssd1306_i2c_clock * 1000);
gpio_set_function(I2C_SDA, GPIO_FUNC_I2C);
gpio_set_function(I2C_SCL, GPIO_FUNC_I2C);
gpio_pull_up(I2C_SDA);
gpio_pull_up(I2C_SCL);
// Inicialização do display OLED
ssd1306_init();
clear_display();
// Inicializa o Joystick
init_joystick();
// Inicia o menu interativo
menu_interativo();
return 0;
}