LED_shit/main/display.c

332 lines
9.5 KiB
C

#include "driver/i2c.h"
#include "esp_log.h"
#include "display.h"
#include <stdint.h>
#define I2C_PORT I2C_NUM_0
// Endereços reais
#define DISP_TOP_ADDR 0x71 // display superior
#define DISP_BOTTOM_ADDR 0x70 // display inferior
static uint16_t rotate180(uint16_t m);
const char *TAG = "DISPLAY";
// =======================================================
// MAPA DE SEGMENTOS (mantido igual ao teu)
// =======================================================
#define SEG_A (1 << 0)
#define SEG_B (1 << 1)
#define SEG_C (1 << 2)
#define SEG_D (1 << 3)
#define SEG_E (1 << 4)
#define SEG_F (1 << 5)
#define SEG_ML (1 << 6)
#define SEG_MR (1 << 7)
#define SEG_TL (1 << 8)
#define SEG_TM (1 << 9)
#define SEG_TR (1 << 10)
#define SEG_BL (1 << 11)
#define SEG_BM (1 << 12)
#define SEG_BR (1 << 13)
#define SEG_DP (1 << 14)
#define SEG_G (SEG_ML | SEG_MR)
// =======================================================
// FUNÇÕES GENÉRICAS PARA QUALQUER DISPLAY
// =======================================================
static void disp_send_cmd(uint8_t addr, uint8_t cmd)
{
i2c_master_write_to_device(I2C_PORT, addr, &cmd, 1, 20 / portTICK_PERIOD_MS);
}
static void disp_raw(uint8_t addr, int pos, uint16_t mask)
{
if (pos < 0 || pos > 3) return;
uint8_t buf[3];
buf[0] = pos * 2;
buf[1] = mask & 0xFF;
buf[2] = (mask >> 8) & 0xFF;
i2c_master_write_to_device(I2C_PORT, addr, buf, 3, 20 / portTICK_PERIOD_MS);
}
static void disp_clear(uint8_t addr)
{
uint8_t buf[17] = {0};
buf[0] = 0x00;
i2c_master_write_to_device(I2C_PORT, addr, buf, sizeof(buf), 20 / portTICK_PERIOD_MS);
}
// =======================================================
// INIT PARA OS DOIS DISPLAYS (TOP 0x71 | BOTTOM 0x70)
// =======================================================
void display_init(void)
{
uint8_t cmd1 = 0x21; // oscillator ON
uint8_t cmd2 = 0x81; // display ON, blink OFF
uint8_t cmd3 = 0xEF; // brightness MAX
// TOP
disp_send_cmd(DISP_TOP_ADDR, cmd1);
disp_send_cmd(DISP_TOP_ADDR, cmd2);
disp_send_cmd(DISP_TOP_ADDR, cmd3);
disp_clear(DISP_TOP_ADDR);
// BOTTOM
disp_send_cmd(DISP_BOTTOM_ADDR, cmd1);
disp_send_cmd(DISP_BOTTOM_ADDR, cmd2);
disp_send_cmd(DISP_BOTTOM_ADDR, cmd3);
disp_clear(DISP_BOTTOM_ADDR);
ESP_LOGI(TAG, "📟 Displays inicializados: TOP=0x71 BOTTOM=0x70");
}
// =======================================================
// RAW PARA TOP E BOTTOM
// =======================================================
void display_raw_top(int pos, uint16_t mask)
{
int p = 3 - pos; // inverter ordem dos dígitos
uint16_t m = rotate180(mask); // rodar segmentos
disp_raw(DISP_TOP_ADDR, p, m);
}
void display_raw_bottom(int pos, uint16_t mask)
{
disp_raw(DISP_BOTTOM_ADDR, pos, mask);
}
void display_clear_top(void)
{
disp_clear(DISP_TOP_ADDR);
}
void display_clear_bottom(void)
{
disp_clear(DISP_BOTTOM_ADDR);
}
// =====================
// ROTATE 180 para 14 segmentos REAL
// (mapa correto para o teu HT16K33)
// =====================
static uint16_t rotate180(uint16_t m)
{
uint16_t r = 0;
// A (topo) <-> D (baixo)
if (m & SEG_A) r |= SEG_D;
if (m & SEG_D) r |= SEG_A;
// B (top-right) <-> E (bottom-left)
if (m & SEG_B) r |= SEG_E;
if (m & SEG_E) r |= SEG_B;
// C (bottom-right) <-> F (top-left)
if (m & SEG_C) r |= SEG_F;
if (m & SEG_F) r |= SEG_C;
// Meio vertical ML ↔ MR
if (m & SEG_ML) r |= SEG_MR;
if (m & SEG_MR) r |= SEG_ML;
// Alfanuméricos topo TL/TM/TR ↔ BL/BM/BR
if (m & SEG_TL) r |= SEG_BL;
if (m & SEG_BL) r |= SEG_TL;
if (m & SEG_TM) r |= SEG_BM;
if (m & SEG_BM) r |= SEG_TM;
if (m & SEG_TR) r |= SEG_BR;
if (m & SEG_BR) r |= SEG_TR;
// DP é DP (ponto)
if (m & SEG_DP) r |= SEG_DP;
return r;
}
// =======================================================
// CHARSET — mantido EXACTAMENTE como o teu
// =======================================================
static uint16_t charset(char c)
{
switch (c)
{
// NÚMEROS
case '0': return SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F;
case '1': return SEG_B | SEG_C;
case '2': return SEG_A | SEG_B | SEG_G | SEG_E | SEG_D;
case '3': return SEG_A | SEG_B | SEG_G | SEG_C | SEG_D;
case '4': return SEG_F | SEG_G | SEG_B | SEG_C;
case '5': return SEG_A | SEG_F | SEG_G | SEG_C | SEG_D;
case '6': return SEG_A | SEG_F | SEG_G | SEG_E | SEG_D | SEG_C;
case '7': return SEG_A | SEG_B | SEG_C;
case '8': return SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G;
case '9': return SEG_A | SEG_B | SEG_C | SEG_D | SEG_F | SEG_G;
// LETRAS COMPLETAS (mantidas)
case 'A': case 'a': return SEG_A | SEG_B | SEG_C | SEG_E | SEG_F | SEG_G;
case 'B': case 'b': return SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G;
case 'C': case 'c': return SEG_A | SEG_F | SEG_E | SEG_D;
case 'D': case 'd': return SEG_B | SEG_C | SEG_D | SEG_E | SEG_G;
case 'E': case 'e': return SEG_A | SEG_F | SEG_G | SEG_E | SEG_D;
case 'F': case 'f': return SEG_A | SEG_F | SEG_G | SEG_E;
case 'G': case 'g': return SEG_A | SEG_F | SEG_E | SEG_D | SEG_C | SEG_G;
case 'H': case 'h': return SEG_F | SEG_E | SEG_G | SEG_B | SEG_C;
case 'I': case 'i': return SEG_B | SEG_C;
case 'J': case 'j': return SEG_B | SEG_C | SEG_D;
case 'K': case 'k': return SEG_F | SEG_E | SEG_G | SEG_TR | SEG_BR;
case 'L': case 'l': return SEG_F | SEG_E | SEG_D;
case 'M': case 'm': return SEG_F | SEG_E | SEG_TL | SEG_TR | SEG_B | SEG_C;
case 'N': case 'n': return SEG_F | SEG_E | SEG_TL | SEG_BR | SEG_C | SEG_B;
case 'O': case 'o': return SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F;
case 'P': case 'p': return SEG_A | SEG_B | SEG_F | SEG_G | SEG_E;
case 'Q': case 'q': return SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_BR;
case 'R': case 'r': return SEG_A | SEG_B | SEG_F | SEG_G | SEG_E | SEG_BR;
case 'S': case 's': return SEG_A | SEG_F | SEG_G | SEG_C | SEG_D;
case 'T': case 't': return SEG_A | SEG_TM | SEG_BR;
case 'U': case 'u': return SEG_F | SEG_E | SEG_D | SEG_C | SEG_B;
case 'V': case 'v': return SEG_F | SEG_E | SEG_D | SEG_B | SEG_TR;
case 'W': case 'w': return SEG_F | SEG_E | SEG_D | SEG_C | SEG_B | SEG_BR | SEG_TR;
case 'X': case 'x': return SEG_TL | SEG_TR | SEG_ML | SEG_MR | SEG_BL | SEG_BR;
case 'Y': case 'y': return SEG_F | SEG_B | SEG_G | SEG_C | SEG_D;
case 'Z': case 'z': return SEG_A | SEG_TR | SEG_G | SEG_BL | SEG_D;
// símbolos
case '-': return SEG_G;
case '.': return SEG_DP;
case '_': return SEG_D;
case ' ': return 0;
default: return 0;
}
}
// =======================================================
// DISPLAY CARACTER / TEXTO
// =======================================================
void display_char_top(int pos, char c)
{
display_raw_top(pos, charset(c));
}
void display_char_bottom(int pos, char c)
{
disp_raw(DISP_BOTTOM_ADDR, pos, charset(c));
}
void display_text_top(const char *txt)
{
for (int i = 0; i < 4; i++) {
char c = txt[i] ? txt[i] : ' ';
display_char_top(i, c);
}
}
void display_text_bottom(const char *txt)
{
for (int i = 0; i < 4; i++) {
char c = txt[i] ? txt[i] : ' ';
display_char_bottom(i, c);
}
}
// =======================================================
// DISPLAY DE NÚMEROS (igual ao teu)
// =======================================================
static const uint16_t digit_mask[10] =
{
[0] = SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F,
[1] = SEG_B | SEG_C,
[2] = SEG_A | SEG_B | SEG_G | SEG_E | SEG_D,
[3] = SEG_A | SEG_B | SEG_G | SEG_C | SEG_D,
[4] = SEG_F | SEG_G | SEG_B | SEG_C,
[5] = SEG_A | SEG_F | SEG_G | SEG_C | SEG_D,
[6] = SEG_A | SEG_F | SEG_G | SEG_E | SEG_D | SEG_C,
[7] = SEG_A | SEG_B | SEG_C,
[8] = SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G,
[9] = SEG_A | SEG_B | SEG_C | SEG_D | SEG_F | SEG_G,
};
void display_digit_top(int pos, uint8_t val)
{
if (val > 9) val = 0;
display_raw_top(pos, digit_mask[val]);
}
void display_digit_bottom(int pos, uint8_t val)
{
if (val > 9) val = 0;
disp_raw(DISP_BOTTOM_ADDR, pos, digit_mask[val]);
}
void display_number_top(int num)
{
if (num < 0) num = 0;
if (num > 9999) num = 9999;
display_digit_top(3, num % 10);
display_digit_top(2, (num / 10) % 10);
display_digit_top(1, (num / 100) % 10);
display_digit_top(0, (num / 1000) % 10);
}
void display_number_bottom(int num)
{
if (num < 0) num = 0;
if (num > 9999) num = 9999;
display_digit_bottom(3, num % 10);
display_digit_bottom(2, (num/10) % 10);
display_digit_bottom(1, (num/100) % 10);
display_digit_bottom(0, (num/1000) % 10);
}
// =======================================================
// display_set_time() — mantém SEG_DP no sítio certo
// =======================================================
void display_set_time_top(int horas, int minutos)
{
if (horas < 0) horas = 0;
if (horas > 99) horas = 99;
if (minutos < 0) minutos = 0;
if (minutos > 59) minutos = 59;
int h1 = horas / 10;
int h2 = horas % 10;
int m1 = minutos / 10;
int m2 = minutos % 10;
uint16_t mid = digit_mask[h2] | SEG_DP;
display_digit_top(0, h1);
display_raw_top(1, mid);
display_digit_top(2, m1);
display_digit_top(3, m2);
}