/**
* Projeto: Exercício 1
* Autor: Alexandre Gonçalves
* Data: 01/02/2025
1. Elabore um programa para acionar um LED quando o botão A for pressionado 5 vezes, tomando cuidado para registrar essa contagem.
Quando o valor da contagem chegar a 5, um LED deve piscar por 10 segundos, na frequência de 10 Hz.
obs: O oled não funciona no wikwo.
*configuração para ser usado no BitDogLab
Para rodar no BitDogLab, é necessário configurar o arquivo CMakeLists.txt,
incluindo o arquivo 'library/ssd1306_i2c.c' no add_executable, por exemplo:
add_executable(exercicio1 exercicio1.c library/ssd1306_i2c.c)
target_link_libraries(exercicio1
pico_stdlib
hardware_pwm
hardware_i2c
)
target_include_directories(exercicio1 PRIVATE
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/library
)
cria uma pasta chamada 'library' e coloca todas as bibliotecas dentro dela
e descomente as linhas marcadas com "Descomente para usar o OLED".
**/
// --- Inclusão de bibliotecas ---
#include "pico/stdlib.h"
#include "hardware/pwm.h"
#include "hardware/clocks.h"
#include "hardware/pwm.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "pico/binary_info.h"
// #include "ssd1306.h" // Descomente para usar o OLED
// #include "hardware/i2c.h" // Descomente para usar o OLED
#include "hardware/clocks.h"
// --- Definição de constantes ---
const uint LED_PIN = 13;
const uint BUTTON_PIN = 5;
//const uint I2C_SDA = 14;// Descomente para usar o OLED
//const uint I2C_SCL = 15;// Descomente para usar o OLED
// Variáveis globais
volatile int button_press_count = 0;
volatile bool blinking = false;
volatile uint slice_num;
// struct render_area frame_area; // Descomente para usar o OLED
// uint8_t ssd[ssd1306_buffer_length]; // Descomente para usar o OLED
absolute_time_t led_start_time = 0;
// Protótipos de funções
void start_led_pwm();
int64_t stop_led_pwm(alarm_id_t id, void *user_data);
// void update_display(); // Descomente para usar o OLED
bool button_timer_callback(struct repeating_timer *t);
int main()
{
stdio_init_all();
// Inicialização do i2c
// i2c_init(i2c1, ssd1306_i2c_clock * 1000); // Descomente para usar o OLED
// gpio_set_function(I2C_SDA, GPIO_FUNC_I2C); // Descomente para usar o OLED
// gpio_set_function(I2C_SCL, GPIO_FUNC_I2C); // Descomente para usar o OLED
// gpio_pull_up(I2C_SDA); // Descomente para usar o OLED
// gpio_pull_up(I2C_SCL); // Descomente para usar o OLED
// Processo de inicialização completo do OLED SSD1306
// ssd1306_init(); // Descomente para usar o OLED
// Configuração do pino do LED para PWM
gpio_set_function(LED_PIN, GPIO_FUNC_PWM);
// Configuração do botão com pull-up
gpio_init(BUTTON_PIN);
gpio_set_dir(BUTTON_PIN, GPIO_IN);
gpio_pull_up(BUTTON_PIN);
// Configuração do timer para leitura do botão a cada 100ms
struct repeating_timer button_timer;
add_repeating_timer_ms(100, button_timer_callback, NULL, &button_timer);
// Preparar área de renderização para o display (ssd1306_width pixels por ssd1306_n_pages páginas)
// frame_area = (struct render_area){ // Descomente para usar o OLED
// start_column : 0, // Descomente para usar o OLED
// end_column : ssd1306_width - 1, // Descomente para usar o OLED
// start_page : 0, // Descomente para usar o OLED
// end_page : ssd1306_n_pages - 1 // Descomente para usar o OLED
// }; // Descomente para usar o OLED
// calculate_render_area_buffer_length(&frame_area); // Descomente para usar o OLED
// zera o display inteiro
// memset(ssd, 0, ssd1306_buffer_length); // Descomente para usar o OLED
// render_on_display(ssd, &frame_area); // Descomente para usar o OLED
// ssd1306_draw_string(ssd, 5, 0, " Embarcatech "); // Descomente para usar o OLED
// render_on_display(ssd, &frame_area); // Descomente para usar o OLED
printf(" Embarcatech \n"); // Log no terminal
while (true)
{
tight_loop_contents();
}
}
// Função para configurar PWM no LED com 10 Hz
void start_led_pwm()
{
// Reconfigurar o pino como PWM novamente
gpio_set_function(LED_PIN, GPIO_FUNC_PWM);
slice_num = pwm_gpio_to_slice_num(LED_PIN);
// Ajuste do clock divider e do wrap para ~10 Hz
pwm_set_clkdiv(slice_num, 190.0f); // Aumentar o divisor
pwm_set_wrap(slice_num, 65535); // Máximo suportado pelo hardware
// Duty cycle de 50% (metade de 65535 é ~32767)
pwm_set_gpio_level(LED_PIN, 32767);
pwm_set_enabled(slice_num, true);
}
// Função para parar o PWM e desligar o LED
int64_t stop_led_pwm(alarm_id_t id, void *user_data)
{
// Seta blinking como false antes de parar o PWM
blinking = false;
pwm_set_enabled(slice_num, false);
// Retornar ao modo GPIO e forçar pino em nível baixo
gpio_set_function(LED_PIN, GPIO_FUNC_SIO);
gpio_set_dir(LED_PIN, GPIO_OUT);
gpio_put(LED_PIN, false);
printf("Piscar concluído, LED desligado.\n");
return 0;
}
// Função para atualizar o display OLED
// void update_display() // Descomente para usar o OLED
// { // Descomente para usar o OLED
// memset(ssd, 0, ssd1306_buffer_length); // Descomente para usar o OLED
// char buffer[20]; // Descomente para usar o OLED
// ssd1306_draw_string(ssd, 5, 0, " Embarcatech "); // Descomente para usar o OLED
// sprintf(buffer, "Toques: %d", button_press_count); // Descomente para usar o OLED
// ssd1306_draw_string(ssd, 5, 16, buffer); // Pula uma linha // Descomente para usar o OLED
// static int last_printed_time = -1; // Para controlar quando imprimir // Descomente para usar o OLED
// if (blinking) // Descomente para usar o OLED
// { // Descomente para usar o OLED
// absolute_time_t now = get_absolute_time(); // Descomente para usar o OLED
// int elapsed_time = (absolute_time_diff_us(led_start_time, now) / 1000000); // Descomente para usar o OLED
// int current_time = 10 - elapsed_time; // Descomente para usar o OLED
// if (current_time != last_printed_time) // Descomente para usar o OLED
// { // Descomente para usar o OLED
// printf("Tempo: %2d\n", current_time); // Descomente para usar o OLED
// last_printed_time = current_time; // Descomente para usar o OLED
// } // Descomente para usar o OLED
// sprintf(buffer, "Tempo: %2d", current_time); // Descomente para usar o OLED
// ssd1306_draw_string(ssd, 5, 32, buffer); // Pula uma linha // Descomente para usar o OLED
// // Exibir frequência real sem casas decimais // Descomente para usar o OLED
// sprintf(buffer, "Freq: 10Hz"); // Descomente para usar o OLED
// ssd1306_draw_string(ssd, 5, 48, buffer); // Pula uma linha // Descomente para usar o OLED
// } // Descomente para usar o OLED
// render_on_display(ssd, &frame_area); // Descomente para usar o OLED
// } // Descomente para usar o OLED
// Callback para verificar o botão (com debounce)
bool button_timer_callback(struct repeating_timer *t)
{
static absolute_time_t last_press_time = 0;
static bool button_last_state = false;
bool button_pressed = !gpio_get(BUTTON_PIN);
if (button_pressed && !button_last_state)
{
// Evita múltiplas contagens devido ao bounce mecânico
if (absolute_time_diff_us(last_press_time, get_absolute_time()) > 200000)
{
last_press_time = get_absolute_time();
// Só conta se blinking for false
if (!blinking)
{
button_press_count++;
printf("Botão pressionado %d vezes\n", button_press_count);
if (button_press_count == 5)
{
blinking = true;
led_start_time = get_absolute_time();
printf("Iniciando piscar do LED por 10 segundos\n");
start_led_pwm();
// Calcular e imprimir a frequência real apenas aqui
float freq = (float)clock_get_hz(clk_sys) / (190.0f * 65536.0f);
printf("Freq: %.2fHz\n", freq);
add_alarm_in_ms(10000, stop_led_pwm, NULL, false);
button_press_count = 0; // Reinicia a contagem
}
}
}
}
button_last_state = button_pressed;
// Chamar update_display() a cada ciclo, sem verificar button_pressed
// update_display(); // Descomente para usar o OLED
return true;
}