/*Residência Tecnológica em Software Embarcado - EMBARCATECH
Tarefa 3 - Aplicação temporizadores
Aluno: Antonio José Portela de Jesus Santos
Matrícula: 20251RSE.MTC0006
VERSÃO SDK UTILIZADA - 2.1.1
Questão 1 - Semáforo de Trânsito Interativo - Criar um semáforo
de trânsito, com acionamento de travessia para pedestres e
indicação de tempo restante
*/
//Declaração de bibliotecas
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/timer.h"
#include "hardware/gpio.h"
#include "hardware/pwm.h"
#include "ssd1306.h"
#include "ssd1306_i2c.h"
#include "hardware/i2c.h"
#include "pico/binary_info.h"
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
//Definição das GPIO utilizadas baseado no PinOut da BitDogLab
#define LED_VERMELHO 13
#define LED_VERDE 11
#define LED_AZUL 12
#define BOTAO_A 5
#define BOTAO_B 6
#define BUZZER_PIN 21
#define I2C_SDA 14
#define I2C_SCL 15
//Definição de constantes, flags, variáveis globais e estruturas utilizadas
const uint8_t DURACAO_VERDE = 10;
const uint8_t DURACAO_VERMELHO = 10;
const uint8_t DURACAO_AMARELO = 3;
bool debounce_ativo = false;
bool acionamento_pedestre = false;
int32_t t_decorrido = 0;
struct repeating_timer timer; // Declaração de uma estrutura de temporizador de repetição.
// Buffer para cada linha do display OLED
char buffer1[16] = {};
char buffer2[16] = {};
char buffer3[16] = {};
char buffer4[16] = {};
char buffer5[16] = {};
char buffer6[16] = {};
char buffer7[16] = {};
char buffer8[16] = {};
char *text[] = {buffer1, buffer2, buffer3, buffer4, buffer5, buffer6, buffer7, buffer8};
// Preparar área de renderização para o display (ssd1306_width pixels por ssd1306_n_pages páginas)
struct render_area frame_area = {
start_column : 0,
end_column : ssd1306_width - 1,
start_page : 0,
end_page : ssd1306_n_pages - 1
};
uint8_t ssd[ssd1306_buffer_length];
//Definicao do protótipo das funções
void setup();
void init_pwm();
void oled_config();
bool repeating_timer_callback(struct repeating_timer *t);
void semaforo_estado(int32_t t_decorrido);
void set_led(char cor);
void msg_serial(char cor);
void escreve_disp(char cor);
void alerta_sonoro();
void buzzer_pwm(int state);
void tratar_botao(uint gpio, uint32_t event);
int64_t debounce_callback(alarm_id_t id, void *user_data);
//Definição da main
void main()
{
stdio_init_all(); //Necessário para o monitor serial
setup(); //Configuração de todos os recursos utilizados do RP2040
// Configura o temporizador para chamar a função de callback a cada 1 segundo.
// Parâmetros:
// 1000: Intervalo de tempo em milissegundos (1 segundo).
// repeating_timer_callback: Função de callback que será chamada a cada intervalo.
// NULL: Dados adicionais passados para a função de callback (não utilizados neste caso).
// &timer: Ponteiro para a estrutura que armazenará informações do temporizador.
add_repeating_timer_ms(1000, repeating_timer_callback, NULL, &timer);
gpio_set_irq_enabled_with_callback(BOTAO_A, GPIO_IRQ_EDGE_FALL, true, tratar_botao);
gpio_set_irq_enabled(BOTAO_B, GPIO_IRQ_EDGE_FALL, true);
// Loop infinito que mantém o programa em execução.
// Aqui você pode adicionar outras tarefas que deseja que o microcontrolador execute.
while (true)
{
// Pausa de 1 segundo para reduzir o uso da CPU, caso execução no wokwi para evitar travamentos
sleep_ms(1000);
//Executa operações para evitar a otimização do loop vazio na compilação
//No wokwi essa função gera travamentos, recomenda-se comentar e usar sleep_ms para uma melhor experiência NO WOKWI
//tight_loop_contents();
}
}
//Funções implementadas
//Função que inicializa os pinos do LED RGB, os Botões A e B, o pwm para o buzzer e a comunicação I2C para o display OLED
void setup()
{
gpio_init(LED_VERMELHO);
gpio_set_dir(LED_VERMELHO, GPIO_OUT);
gpio_put(LED_VERMELHO, 0);
gpio_init(LED_VERDE);
gpio_set_dir(LED_VERDE, GPIO_OUT);
gpio_put(LED_VERDE, 0);
gpio_init(LED_AZUL);
gpio_set_dir(LED_AZUL, GPIO_OUT);
gpio_put(LED_AZUL, 0);
gpio_init(BOTAO_A);
gpio_set_dir(BOTAO_A, GPIO_IN);
gpio_pull_up(BOTAO_A);
gpio_init(BOTAO_B);
gpio_set_dir(BOTAO_B, GPIO_IN);
gpio_pull_up(BOTAO_B);
init_pwm();
oled_config();
}
//Inicializa e habilita o PWM
void init_pwm()
{
gpio_set_function(BUZZER_PIN, GPIO_FUNC_PWM);
uint slice_num = pwm_gpio_to_slice_num(BUZZER_PIN);
pwm_set_clkdiv(slice_num, 16.0);
pwm_set_wrap(slice_num, 2000);
pwm_set_enabled(slice_num, true);
}
//Inicializa e habilita a comunicação I2C
void oled_config()
{
// Inicialização do 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);
// Processo de inicialização completo do OLED SSD1306
ssd1306_init();
calculate_render_area_buffer_length(&frame_area);
// zera o display inteiro
memset(ssd, 0, ssd1306_buffer_length);
render_on_display(ssd, &frame_area);
}
// Função de callback que será chamada repetidamente pelo temporizador
// O tipo bool indica que a função deve retornar verdadeiro ou falso para continuar ou parar o temporizador.
bool repeating_timer_callback(struct repeating_timer *t)
{
t_decorrido++; // Incrementa 1 segundo no contador de tempo decorrido
semaforo_estado(t_decorrido); //Passa o tempo decorrido para a função que define o estado do semáforo
if (t_decorrido == (DURACAO_VERMELHO + DURACAO_VERDE + DURACAO_AMARELO))
{
t_decorrido = 0; //Se o tempo decorrido for igual a duração dos três estados somados este deve ser resetado
}
return true; // Retorna true para manter o temporizador repetindo. Se retornar false, o temporizador para.
}
//Função que define o estado do semáforo baseada no tempo decoorrido
//e atualiza o monitor serial e display OLED i2c com as informações do estado corrente
//Estados na seguinte ordem: 1 - Vermelho por 10s, 2 - Verde por 10s e 3 - Amarelo por 1s
void semaforo_estado(int32_t t_decorrido)
{
if (t_decorrido <= DURACAO_VERMELHO) //Verifica o tempo de duração do estado vermelho
{
acionamento_pedestre = false; //Pedestre não pode solicitar travessia com o semáforo vermelho
set_led('r'); //Aciona o LED RGB com a cor vermelha 'r'
msg_serial('r'); //Envia as informações de Semáforo acionado 'r' para o monitor serial
escreve_disp('r'); //Envia as informações do estado acionado 'r' para o display OLED
if ((DURACAO_VERMELHO - t_decorrido) < 5)
{
alerta_sonoro(); //O alerta sonoro é ativado quando faltam 5 segundos para o fim do estado vermelho
}
}
else if (t_decorrido > DURACAO_VERMELHO && t_decorrido <= (DURACAO_VERMELHO + DURACAO_VERDE)) //Verifica o tempo de duração do estado verde
{
set_led('g'); //Aciona o LED RGB com a cor vermelha 'g'
msg_serial('g'); //Envia as informações de Semáforo acionado 'g' para o monitor serial
escreve_disp('g'); //Envia as informações do estado acionado 'g' para o display OLED
acionamento_pedestre = true; //Pedestre pode solicitar travessia com o semáforo verde
}
else if (t_decorrido > (DURACAO_VERMELHO + DURACAO_VERDE) && t_decorrido <= (DURACAO_VERMELHO + DURACAO_VERDE + DURACAO_AMARELO)) //Verifica o tempo de duração do estado amarelo
{
acionamento_pedestre = false; //Pedestre não pode solicitar travessia com o semáforo amarelo
set_led('y'); //Aciona o LED RGB com a cor vermelha 'y'
msg_serial('y'); //Envia as informações de Semáforo acionado 'y' para o monitor serial
escreve_disp('y'); //Envia as informações do estado acionado 'y' para o display OLED
}
}
//Função que define a cor do LED RGB de acordo com a cor configurada: 'r', 'g' ou 'y'
void set_led(char cor)
{
if (cor == 'r')
{
gpio_put(LED_VERMELHO, 1);
gpio_put(LED_AZUL, 0);
gpio_put(LED_VERDE, 0);
}
else if (cor == 'g')
{
gpio_put(LED_VERMELHO, 0);
gpio_put(LED_AZUL, 0);
gpio_put(LED_VERDE, 1);
}
else if (cor == 'y')
{
gpio_put(LED_VERMELHO, 1);
gpio_put(LED_AZUL, 0);
gpio_put(LED_VERDE, 1);
}
}
//Função que define o formato da mensagem a ser enviada via comunicação serial de acordo com o estado corrente
void msg_serial(char cor)
{
if (cor == 'r')
{
printf("\e[H\e[2J\e[3J"); // Expressao formatada que move o cursor, limpa a tela e o buffer de rolagem
printf("Sinal: Vermelho! Tempo restante: %02d segundos!\n", (DURACAO_VERMELHO - t_decorrido + 1));
}
else if (cor == 'g')
{
printf("\e[H\e[2J\e[3J");
printf("Sinal: Verde! Tempo restante: %02d segundos!\n", (DURACAO_VERDE + DURACAO_VERMELHO - t_decorrido + 1));
}
else if (cor == 'y')
{
printf("\e[H\e[2J\e[3J");
printf("Sinal: Amarelo! Tempo restante: %02d segundos!\n", (DURACAO_VERDE + DURACAO_AMARELO + DURACAO_VERMELHO - t_decorrido + 1));
}
}
//Função que possibilita escrever com fonte bitmap 8x8 no dispaly OLED
//A mensagem escrita varia com o estado corrente
void escreve_disp(char cor)
{
// Envia texto para os buffers do display
if (cor == 'r')
{
snprintf(buffer4, sizeof(buffer1), "Sinal: VERMELHO");
snprintf(buffer5, sizeof(buffer2), "Tempo restante:");
snprintf(buffer6, sizeof(buffer3), "%02d segundos !",(DURACAO_VERMELHO - t_decorrido + 1));
}
else if (cor == 'y')
{
snprintf(buffer4, sizeof(buffer1), "Sinal: AMARELO ");
snprintf(buffer5, sizeof(buffer2), "Tempo restante:");
snprintf(buffer6, sizeof(buffer3), "%02d segundos !",(DURACAO_VERDE + DURACAO_AMARELO + DURACAO_VERMELHO - t_decorrido + 1));
}
else if (cor == 'g')
{
snprintf(buffer4, sizeof(buffer1), "Sinal: VERDE ");
snprintf(buffer5, sizeof(buffer2), "Tempo restante:");
snprintf(buffer6, sizeof(buffer3), "%02d segundos !",(DURACAO_VERDE + DURACAO_VERMELHO - t_decorrido + 1));
}
// Desenha as strings buffer1 a buffer8 no display
int y = 0;
for (uint i = 0; i < count_of(text); i++)
{
ssd1306_draw_string(ssd, 5, y, text[i]);
y += 8;
}
render_on_display(ssd, &frame_area);
}
//Função que realiza o alerta sonoro com base no estado corrente
void alerta_sonoro()
{
buzzer_pwm((t_decorrido) % 2); //Comuta o estado do buzzer a cada 1 segundo
}
//Função que define o buzzer pwm de acordo com o estado selecionado (1 - Ligado e qualquer outro número - desligado)
void buzzer_pwm(int state)
{
if (state == 1)
{
pwm_set_gpio_level(BUZZER_PIN, 100);
}
else
{
pwm_set_gpio_level(BUZZER_PIN, 0);
}
}
//Função que trata a interrupção de botão gerada pelo botão A ou B.
//Se ambos pressionados botão A é avaliado de forma prioritária em detrimento de botão B
void tratar_botao(uint gpio, uint32_t event)
{
if (debounce_ativo)
return; // Evita acionamentos indesejados
debounce_ativo = true; // Ativa debounce
add_alarm_in_ms(500, debounce_callback, NULL, false); // 500 ms de debounce
if ((gpio == BOTAO_A) && acionamento_pedestre == true) //Avalia se a interrupção foi gerada pelo botão A e se o acionamento de pedrestes é permitido
{
printf("Botão A de pedestres acionado!\n"); //Imprime mensagem no monitor serial
acionamento_pedestre = false; //Desabilita a flag para acionamento de pedestre
t_decorrido = 20; //Configura o tempo decorrido para iniciar o estado amarelo
}
else if ((gpio == BOTAO_B) && acionamento_pedestre == true) //Avalia se a interrupção foi gerada pelo botão B e se o acionamento de pedrestes é permitido
{
printf("Botão B de pedestres acionado!\n"); //Imprime mensagem no monitor serial
acionamento_pedestre = false; //Desabilita a flag para acionamento de pedestre
t_decorrido = 20; //Configura o tempo decorrido para iniciar o estado amarelo
}
}
// Callback para debounce do botão - Garante que não haverá atuações indesejadas
int64_t debounce_callback(alarm_id_t id, void *user_data)
{
debounce_ativo = false; //Desabilita flag para debouce após 500ms
return 0;
}Loading
pi-pico-w
pi-pico-w
Loading
ssd1306
ssd1306