#include "pico/stdlib.h" // Biblioteca padrão do Pico SDK
#include "hardware/gpio.h" // Biblioteca para controle dos pinos GPIO
#include <stdio.h> // Biblioteca para entrada e saída padrão (printf, etc.)
#include <stdlib.h> // Biblioteca para alocação de memória dinâmica (malloc, free)
#include <stdint.h> // Biblioteca para tipos de dados inteiros de tamanho fixo (uint8_t, etc.)
#include <stdbool.h> // Biblioteca para tipo de dado booleano (true, false)
// Define constantes para os comandos do TM1637
#define TM1637_CMD1 64 // 0x40 Comando de dados (incremento de endereço, modo normal)
#define TM1637_CMD2 192 // 0xC0 Comando de endereço (define o endereço inicial da memória do display)
#define TM1637_CMD3 128 // 0x80 Comando de controle do display (liga/desliga, define o brilho)
#define TM1637_DSP_ON 8 // 0x08 Bit para ligar o display
#define TM1637_DELAY 10 // 10us de delay entre os pulsos de clock e dados (para o timing do TM1637)
// Define o mapeamento dos segmentos para dígitos e caracteres
static const uint8_t _SEGMENTS[] = {
// 0-9
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,
// A-F
0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71,
// G-Z (alguns não ficam perfeitos no display de 7 segmentos)
0x3D, 0x76, 0x06, 0x1E, 0x76, 0x38, 0x55, 0x54, 0x3F, 0x73,
// a-f (minúsculas)
0x67, 0x50, 0x6D, 0x78, 0x3E, 0x1C,
// Outros símbolos
0x2A, 0x76, 0x6E, 0x5B,
// espaço, traço, símbolo de grau, símbolo de graus Celsius
0x00, 0x40, 0x63};
// Estrutura que representa o objeto TM1637
typedef struct {
int clk_pin; // Pino GPIO conectado à linha CLK do TM1637
int dio_pin; // Pino GPIO conectado à linha DIO do TM1637
int brightness; // Nível de brilho atual (0-7)
} TM1637;
// Protótipos das funções
TM1637 *TM1637_create(int clk_pin, int dio_pin, int brightness);
void TM1637_init(TM1637 *display, int clk_pin, int dio_pin, int brightness);
void TM1637_start(TM1637 *display);
void TM1637_stop(TM1637 *display);
void TM1637_write_data_cmd(TM1637 *display);
void TM1637_write_dsp_ctrl(TM1637 *display);
void TM1637_write_byte(TM1637 *display, uint8_t b);
int TM1637_brightness(TM1637 *display, int val);
void TM1637_write(TM1637 *display, uint8_t *segments, int pos);
uint8_t *TM1637_encode_string(TM1637 *display, const char *string);
uint8_t TM1637_encode_char(TM1637 *display, char c);
void TM1637_number(TM1637 *display, int num);
void TM1637_show(TM1637 *display, const char *string, bool colon);
void TM1637_free(TM1637 *display);
// Implementações
/**
* @brief Aloca memória para um objeto TM1637 e o inicializa.
*
* @param clk_pin Pino GPIO conectado à linha CLK do TM1637.
* @param dio_pin Pino GPIO conectado à linha DIO do TM1637.
* @param brightness Nível de brilho inicial (0-7).
* @return Um ponteiro para o objeto TM1637 recém-criado, ou NULL se a alocação de memória falhar.
*/
TM1637 *TM1637_create(int clk_pin, int dio_pin, int brightness) {
TM1637 *display = (TM1637 *)malloc(sizeof(TM1637)); // Aloca memória para o objeto TM1637
if (display == NULL) {
perror("Falha ao alocar memória para TM1637"); // Imprime mensagem de erro se a alocação de memória falhar
exit(EXIT_FAILURE); // Encerra o programa
}
TM1637_init(display, clk_pin, dio_pin, brightness); // Inicializa o objeto TM1637
return display; // Retorna o ponteiro para o objeto TM1637
}
/**
* @brief Inicializa o objeto TM1637 com os números dos pinos e o brilho fornecidos.
*
* @param display Ponteiro para o objeto TM1637.
* @param clk_pin Pino GPIO conectado à linha CLK do TM1637.
* @param dio_pin Pino GPIO conectado à linha DIO do TM1637.
* @param brightness Nível de brilho inicial (0-7).
*/
void TM1637_init(TM1637 *display, int clk_pin, int dio_pin, int brightness) {
display->clk_pin = clk_pin; // Armazena o número do pino CLK
display->dio_pin = dio_pin; // Armazena o número do pino DIO
// Verifica se o valor do brilho está dentro do intervalo válido (0-7)
if (brightness < 0 || brightness > 7) {
fprintf(stderr, "Erro: Brilho fora do intervalo (0-7). Definindo para o padrão (7).\n"); // Imprime uma mensagem de erro
display->brightness = 7; // Define o brilho para o valor padrão (7)
} else {
display->brightness = brightness; // Armazena o valor do brilho
}
gpio_init(display->clk_pin); // Inicializa o pino CLK como um GPIO
gpio_set_dir(display->clk_pin, GPIO_OUT); // Define o pino CLK como uma saída
gpio_init(display->dio_pin); // Inicializa o pino DIO como um GPIO
gpio_set_dir(display->dio_pin, GPIO_OUT); // Define o pino DIO como uma saída
gpio_put(display->clk_pin, 0); // Define o pino CLK como baixo
gpio_put(display->dio_pin, 0); // Define o pino DIO como baixo
sleep_us(TM1637_DELAY); // Espera por um curto período de tempo
TM1637_write_data_cmd(display); // Envia o comando de dados para o TM1637
TM1637_write_dsp_ctrl(display); // Envia o comando de controle do display para o TM1637
}
/**
* @brief Inicia a comunicação com o TM1637.
*
* @param display Ponteiro para o objeto TM1637.
*/
void TM1637_start(TM1637 *display) {
gpio_put(display->dio_pin, 0); // Define o pino DIO como baixo
sleep_us(TM1637_DELAY); // Espera por um curto período de tempo
gpio_put(display->clk_pin, 0); // Define o pino CLK como baixo
sleep_us(TM1637_DELAY); // Espera por um curto período de tempo
}
/**
* @brief Finaliza a comunicação com o TM1637.
*
* @param display Ponteiro para o objeto TM1637.
*/
void TM1637_stop(TM1637 *display) {
gpio_put(display->dio_pin, 0); // Define o pino DIO como baixo
sleep_us(TM1637_DELAY); // Espera por um curto período de tempo
gpio_put(display->clk_pin, 1); // Define o pino CLK como alto
sleep_us(TM1637_DELAY); // Espera por um curto período de tempo
gpio_put(display->dio_pin, 1); // Define o pino DIO como alto
}
/**
* @brief Envia o comando de dados para o TM1637.
*
* @param display Ponteiro para o objeto TM1637.
*/
void TM1637_write_data_cmd(TM1637 *display) {
TM1637_start(display); // Inicia a comunicação com o TM1637
TM1637_write_byte(display, TM1637_CMD1); // Envia o comando de dados
TM1637_stop(display); // Finaliza a comunicação com o TM1637
}
/**
* @brief Envia o comando de controle do display para o TM1637.
*
* @param display Ponteiro para o objeto TM1637.
*/
void TM1637_write_dsp_ctrl(TM1637 *display) {
TM1637_start(display); // Inicia a comunicação com o TM1637
TM1637_write_byte(display, TM1637_CMD3 | // Envia o comando de controle do display
TM1637_DSP_ON | // Liga o display
display->brightness); // Define o nível de brilho
TM1637_stop(display); // Finaliza a comunicação com o TM1637
}
/**
* @brief Escreve um byte de dado no TM1637.
*
* @param display Ponteiro para o objeto TM1637.
* @param b O byte a ser escrito.
*/
void TM1637_write_byte(TM1637 *display, uint8_t b) {
// Itera sobre cada bit do byte
for (int i = 0; i < 8; i++) {
gpio_put(display->dio_pin, (b >> i) & 1); // Define o pino DIO para o valor do bit atual
sleep_us(TM1637_DELAY); // Espera por um curto período de tempo
gpio_put(display->clk_pin, 1); // Define o pino CLK para alto
sleep_us(TM1637_DELAY); // Espera por um curto período de tempo
gpio_put(display->clk_pin, 0); // Define o pino CLK para baixo
sleep_us(TM1637_DELAY); // Espera por um curto período de tempo
}
// Bit de ACK
gpio_put(display->clk_pin, 0); // Define o pino CLK para baixo
sleep_us(TM1637_DELAY); // Espera por um curto período de tempo
gpio_put(display->clk_pin, 1); // Define o pino CLK para alto (para o pulso de ACK)
sleep_us(TM1637_DELAY); // Espera por um curto período de tempo
gpio_put(display->clk_pin, 0); // Define o pino CLK para baixo
sleep_us(TM1637_DELAY); // Espera por um curto período de tempo
}
/**
* @brief Define o brilho do display.
*
* @param display Ponteiro para o objeto TM1637.
* @param val O valor do brilho (0-7).
* @return 0 se bem-sucedido, -1 se o valor do brilho estiver fora do intervalo.
*/
int TM1637_brightness(TM1637 *display, int val) {
if (val == -1) { // Obtendo
return display->brightness;
}
// Verifica se o valor do brilho está dentro do intervalo válido (0-7)
if (val < 0 || val > 7) {
fprintf(stderr, "Erro: Brilho fora do intervalo (0-7).\n"); // Imprime uma mensagem de erro
return -1; // Retorna um código de erro
}
display->brightness = val; // Armazena o valor do brilho
TM1637_write_data_cmd(display); // Envia o comando de dados para o TM1637
TM1637_write_dsp_ctrl(display); // Envia o comando de controle do display para o TM1637
return 0; // Retorna um código de sucesso
}
/**
* @brief Escreve os dados dos segmentos no display TM1637.
*
* @param display Ponteiro para o objeto TM1637.
* @param segments Array de 4 bytes representando os dados dos segmentos para cada dígito.
* @param pos A posição inicial para escrever os dados (0-3).
*/
void TM1637_write(TM1637 *display, uint8_t *segments, int pos) {
if (pos < 0 || pos > 5) {
fprintf(stderr, "Erro: Posição fora do intervalo (0-5).\n"); // Imprime uma mensagem de erro
return; // Sai da função
}
TM1637_write_data_cmd(display); // Envia o comando de dados para o TM1637
TM1637_start(display); // Inicia a comunicação com o TM1637
TM1637_write_byte(display, TM1637_CMD2 | pos); // Envia o comando de endereço com a posição inicial
// Envia os dados dos segmentos para cada dígito
for (int i = 0; i < 4; i++) {
TM1637_write_byte(display, segments[i]);
}
TM1637_stop(display); // Finaliza a comunicação com o TM1637
TM1637_write_dsp_ctrl(display); // Envia o comando de controle do display para o TM1637
}
/**
* @brief Codifica uma string em um array de dados de segmento para o display TM1637.
*
* @param display Ponteiro para o objeto TM1637.
* @param string A string a ser codificada (até 4 caracteres).
* @return Um ponteiro para o array de dados de segmento. O chamador é responsável por liberar a memória.
*/
uint8_t *TM1637_encode_string(TM1637 *display, const char *string) {
int len = 0;
// Calcula o comprimento da string, limitando-o a 4 caracteres
while (string[len] != '\0' && len < 4) {
len++;
}
uint8_t *segments = (uint8_t *)malloc(len * sizeof(uint8_t)); // Aloca memória para os dados de segmento
if (segments == NULL) {
perror("Falha ao alocar memória para segmentos"); // Imprime uma mensagem de erro se a alocação de memória falhar
exit(EXIT_FAILURE); // Encerra o programa
}
// Codifica cada caractere na string
for (int i = 0; i < len; i++) {
segments[i] = TM1637_encode_char(display, string[i]);
}
return segments; // Retorna o ponteiro para os dados de segmento
}
/**
* @brief Codifica um único caractere em dados de segmento para o display TM1637.
*
* @param display Ponteiro para o objeto TM1637.
* @param c O caractere a ser codificado.
* @return Os dados de segmento para o caractere.
*/
uint8_t TM1637_encode_char(TM1637 *display, char c) {
int o = (int)c; // Obtém o valor ASCII do caractere
// Codifica o caractere com base em seu valor ASCII
if (o == 32) {
return _SEGMENTS[36]; // espaço
} else if (o == 42) {
return _SEGMENTS[38]; // estrela/graus
} else if (o == 45) {
return _SEGMENTS[37]; // traço
} else if (o >= 65 && o <= 90) {
return _SEGMENTS[o - 55]; // A-Z maiúsculo
} else if (o >= 97 && o <= 122) {
return _SEGMENTS[o - 87]; // a-z minúsculo
} else if (o >= 48 && o <= 57) {
return _SEGMENTS[o - 48]; // 0-9
} else {
fprintf(stderr, "Erro: Caractere fora do intervalo: %d '%c'\n", o, (char)o); // Imprime uma mensagem de erro
return 0x00; // Retorna em branco para caracteres inválidos
}
}
/**
* @brief Exibe um número no display TM1637.
*
* @param display Ponteiro para o objeto TM1637.
* @param num O número a ser exibido (-999 a 9999).
*/
void TM1637_number(TM1637 *display, int num) {
// Limita o número ao intervalo de -999 a 9999
num = (num < -999) ? -999 : (num > 9999) ? 9999 : num;
char string[5];
snprintf(string, sizeof(string), "%4d", num); // Formata o número como uma string de 4 dígitos
uint8_t *segments = TM1637_encode_string(display, string); // Codifica a string em dados de segmento
TM1637_write(display, segments, 0); // Escreve os dados de segmento no display TM1637
free(segments); // Libera a memória alocada
}
/**
* @brief Exibe uma string no display TM1637.
*
* @param display Ponteiro para o objeto TM1637.
* @param string A string a ser exibida (até 4 caracteres).
* @param colon Indica se deve exibir um dois pontos entre o 2º e o 3º dígitos.
*/
void TM1637_show(TM1637 *display, const char *string, bool colon) {
uint8_t *segments = TM1637_encode_string(display, string); // Codifica a string em dados de segmento
int len = 0;
// Calcula o comprimento da string, limitando-o a 4 caracteres
while (string[len] != '\0' && len < 4) {
len++;
}
uint8_t display_segments[4] = {0, 0, 0, 0}; // Inicializa com espaços em branco
// Copia os segmentos codificados para o array display_segments
for (int i = 0; i < len && i < 4; i++) {
display_segments[i] = segments[i];
}
TM1637_write(display, display_segments, 0); // Escreve os dados de segmento no display TM1637
free(segments); // Libera a memória alocada
}
/**
* @brief Libera a memória alocada para um objeto TM1637.
*
* @param display Ponteiro para o objeto TM1637.
*/
void TM1637_free(TM1637 *display) {
free(display); // Libera a memória alocada para o objeto TM1637
}
// Exemplo de uso para o Cofre IoT (em loop para testes)
int main() {
stdio_init_all(); // Inicializa a entrada e saída padrão
// Substitua pelos seus pinos GPIO reais
int clk_pin = 26; // Pino CLK
int dio_pin = 27; // Pino DIO
// Cria o objeto display TM1637
TM1637 *mydisplay = TM1637_create(clk_pin, dio_pin, 7); // Cria e inicializa o objeto TM1637
while (true) { // Loop infinito para testes contínuos
// Exibe "LOCK"
TM1637_show(mydisplay, "LOCK", false); // Exibe "LOCK" no display TM1637
sleep_ms(2000); // Espera por 2 segundos
// Exibe "OPEN"
TM1637_show(mydisplay, "OPEN", false); // Exibe "OPEN" no display TM1637
sleep_ms(2000); // Espera por 2 segundos
// Exibe um número (por exemplo, o código atual)
TM1637_number(mydisplay, 1234); // Exibe o número 1234 no display TM1637
sleep_ms(2000); // Espera por 2 segundos
// Limpa o display
TM1637_show(mydisplay, " ", false); // Exibe caracteres em branco para limpar o display
sleep_ms(1000); // Espera por 1 segundo
// Reduz o brilho
TM1637_brightness(mydisplay, 0); // Define o brilho para o nível mais baixo (0)
sleep_ms(1000); // Espera por 1 segundo
// Restaura o brilho
TM1637_brightness(mydisplay, 7); // Define o brilho para o nível mais alto (7)
sleep_ms(1000); // Espera por 1 segundo
}
// TM1637_free(mydisplay); // This line is never reached
return 0;
}