Compare commits
2 Commits
c5aeddc653
...
87afe944bc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87afe944bc | ||
|
|
09e5be3233 |
429
main/display.c
429
main/display.c
@ -1,19 +1,36 @@
|
|||||||
|
// display.c (HT16K33 14-seg / 4 dígitos)
|
||||||
|
// TOP = 0x71 | BOTTOM = 0x70
|
||||||
|
// I2C_PORT = I2C_NUM_0
|
||||||
|
//
|
||||||
|
// MODO "SAFE":
|
||||||
|
// - Se I2C não estiver instalado, ou se não existir display, ignora tudo (sem spam)
|
||||||
|
// - display_init() NUNCA aborta o firmware (devolve ESP_OK e deixa display disabled)
|
||||||
|
|
||||||
#include "driver/i2c.h"
|
#include "driver/i2c.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
#include "esp_err.h"
|
||||||
#include "display.h"
|
#include "display.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
#define I2C_PORT I2C_NUM_0
|
#define I2C_PORT I2C_NUM_0
|
||||||
|
|
||||||
// Endereços reais
|
// Endereços reais
|
||||||
#define DISP_TOP_ADDR 0x71 // display superior
|
#define DISP_TOP_ADDR 0x71
|
||||||
#define DISP_BOTTOM_ADDR 0x70 // display inferior
|
#define DISP_BOTTOM_ADDR 0x70
|
||||||
static uint16_t rotate180(uint16_t m);
|
|
||||||
|
|
||||||
const char *TAG = "DISPLAY";
|
static const char *TAG = "DISPLAY";
|
||||||
|
|
||||||
// =======================================================
|
// =======================================================
|
||||||
// MAPA DE SEGMENTOS (mantido igual ao teu)
|
// FLAG GLOBAL
|
||||||
|
// =======================================================
|
||||||
|
static bool s_display_enabled = false;
|
||||||
|
|
||||||
|
void display_set_enabled(bool en) { s_display_enabled = en; }
|
||||||
|
bool display_is_enabled(void) { return s_display_enabled; }
|
||||||
|
|
||||||
|
// =======================================================
|
||||||
|
// MAPA DE SEGMENTOS (igual ao teu)
|
||||||
// =======================================================
|
// =======================================================
|
||||||
|
|
||||||
#define SEG_A (1 << 0)
|
#define SEG_A (1 << 0)
|
||||||
@ -38,131 +55,264 @@ const char *TAG = "DISPLAY";
|
|||||||
|
|
||||||
#define SEG_G (SEG_ML | SEG_MR)
|
#define SEG_G (SEG_ML | SEG_MR)
|
||||||
|
|
||||||
|
static uint16_t charset(char c);
|
||||||
|
|
||||||
// =======================================================
|
// =======================================================
|
||||||
// FUNÇÕES GENÉRICAS PARA QUALQUER DISPLAY
|
// FUNÇÕES I2C (internas)
|
||||||
// =======================================================
|
// =======================================================
|
||||||
|
|
||||||
static void disp_send_cmd(uint8_t addr, uint8_t cmd)
|
static inline bool i2c_driver_is_installed(void)
|
||||||
{
|
{
|
||||||
i2c_master_write_to_device(I2C_PORT, addr, &cmd, 1, 20 / portTICK_PERIOD_MS);
|
// truque simples: tenta criar um cmd e dar begin "vazio" é overkill.
|
||||||
|
// Mais seguro: tenta uma escrita curta e ver se dá INVALID_STATE.
|
||||||
|
// Aqui vamos só confiar na primeira escrita do display_init().
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void disp_raw(uint8_t addr, int pos, uint16_t mask)
|
static esp_err_t disp_send_cmd(uint8_t addr, uint8_t cmd)
|
||||||
{
|
{
|
||||||
if (pos < 0 || pos > 3) return;
|
return i2c_master_write_to_device(I2C_PORT, addr, &cmd, 1, pdMS_TO_TICKS(50));
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t disp_raw(uint8_t addr, int pos, uint16_t mask)
|
||||||
|
{
|
||||||
|
if (pos < 0 || pos > 3) return ESP_ERR_INVALID_ARG;
|
||||||
|
|
||||||
uint8_t buf[3];
|
uint8_t buf[3];
|
||||||
buf[0] = pos * 2;
|
buf[0] = (uint8_t)(pos * 2);
|
||||||
buf[1] = mask & 0xFF;
|
buf[1] = (uint8_t)(mask & 0xFF);
|
||||||
buf[2] = (mask >> 8) & 0xFF;
|
buf[2] = (uint8_t)((mask >> 8) & 0xFF);
|
||||||
|
|
||||||
i2c_master_write_to_device(I2C_PORT, addr, buf, 3, 20 / portTICK_PERIOD_MS);
|
return i2c_master_write_to_device(I2C_PORT, addr, buf, sizeof(buf), pdMS_TO_TICKS(50));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void disp_clear(uint8_t addr)
|
static esp_err_t disp_clear(uint8_t addr)
|
||||||
{
|
{
|
||||||
uint8_t buf[17] = {0};
|
uint8_t buf[17] = {0};
|
||||||
buf[0] = 0x00;
|
buf[0] = 0x00;
|
||||||
i2c_master_write_to_device(I2C_PORT, addr, buf, sizeof(buf), 20 / portTICK_PERIOD_MS);
|
return i2c_master_write_to_device(I2C_PORT, addr, buf, sizeof(buf), pdMS_TO_TICKS(50));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// =======================================================
|
// =======================================================
|
||||||
// INIT PARA OS DOIS DISPLAYS (TOP 0x71 | BOTTOM 0x70)
|
// INIT (SAFE)
|
||||||
// =======================================================
|
// =======================================================
|
||||||
|
|
||||||
void display_init(void)
|
esp_err_t display_init(void)
|
||||||
{
|
{
|
||||||
uint8_t cmd1 = 0x21; // oscillator ON
|
// por defeito: desligado
|
||||||
uint8_t cmd2 = 0x81; // display ON, blink OFF
|
s_display_enabled = false;
|
||||||
uint8_t cmd3 = 0xEF; // brightness MAX
|
|
||||||
|
// Se o driver I2C não estiver instalado, a primeira chamada vai dar INVALID_STATE.
|
||||||
|
// Nós tratamos isso como "não há display nesta board" e seguimos sem logs.
|
||||||
|
const uint8_t cmd1 = 0x21; // oscillator ON
|
||||||
|
const uint8_t cmd2 = 0x81; // display ON, blink OFF
|
||||||
|
const uint8_t cmd3 = 0xEF; // brightness MAX
|
||||||
|
|
||||||
|
esp_err_t err;
|
||||||
|
|
||||||
// TOP
|
// TOP
|
||||||
disp_send_cmd(DISP_TOP_ADDR, cmd1);
|
err = disp_send_cmd(DISP_TOP_ADDR, cmd1);
|
||||||
disp_send_cmd(DISP_TOP_ADDR, cmd2);
|
if (err == ESP_ERR_INVALID_STATE) return ESP_OK; // I2C não instalado -> ignora
|
||||||
disp_send_cmd(DISP_TOP_ADDR, cmd3);
|
if (err != ESP_OK) return ESP_OK; // display ausente/cabos -> ignora
|
||||||
disp_clear(DISP_TOP_ADDR);
|
|
||||||
|
err = disp_send_cmd(DISP_TOP_ADDR, cmd2);
|
||||||
|
if (err != ESP_OK) return ESP_OK;
|
||||||
|
|
||||||
|
err = disp_send_cmd(DISP_TOP_ADDR, cmd3);
|
||||||
|
if (err != ESP_OK) return ESP_OK;
|
||||||
|
|
||||||
|
err = disp_clear(DISP_TOP_ADDR);
|
||||||
|
if (err != ESP_OK) return ESP_OK;
|
||||||
|
|
||||||
// BOTTOM
|
// BOTTOM
|
||||||
disp_send_cmd(DISP_BOTTOM_ADDR, cmd1);
|
err = disp_send_cmd(DISP_BOTTOM_ADDR, cmd1);
|
||||||
disp_send_cmd(DISP_BOTTOM_ADDR, cmd2);
|
if (err != ESP_OK) return ESP_OK;
|
||||||
disp_send_cmd(DISP_BOTTOM_ADDR, cmd3);
|
|
||||||
disp_clear(DISP_BOTTOM_ADDR);
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "📟 Displays inicializados: TOP=0x71 BOTTOM=0x70");
|
err = disp_send_cmd(DISP_BOTTOM_ADDR, cmd2);
|
||||||
|
if (err != ESP_OK) return ESP_OK;
|
||||||
|
|
||||||
|
err = disp_send_cmd(DISP_BOTTOM_ADDR, cmd3);
|
||||||
|
if (err != ESP_OK) return ESP_OK;
|
||||||
|
|
||||||
|
err = disp_clear(DISP_BOTTOM_ADDR);
|
||||||
|
if (err != ESP_OK) return ESP_OK;
|
||||||
|
|
||||||
|
// OK -> ativa display
|
||||||
|
s_display_enabled = true;
|
||||||
|
ESP_LOGI(TAG, "📟 Displays OK: TOP=0x%02X BOTTOM=0x%02X", DISP_TOP_ADDR, DISP_BOTTOM_ADDR);
|
||||||
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =======================================================
|
||||||
|
// RAW / CLEAR (com guard)
|
||||||
|
// =======================================================
|
||||||
|
|
||||||
// =======================================================
|
|
||||||
// RAW PARA TOP E BOTTOM
|
|
||||||
// =======================================================
|
|
||||||
void display_raw_top(int pos, uint16_t mask)
|
void display_raw_top(int pos, uint16_t mask)
|
||||||
{
|
{
|
||||||
int p = 3 - pos; // inverter ordem dos dígitos
|
if (!s_display_enabled) return;
|
||||||
uint16_t m = rotate180(mask); // rodar segmentos
|
|
||||||
disp_raw(DISP_TOP_ADDR, p, m);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
esp_err_t err = disp_raw(DISP_TOP_ADDR, pos, mask);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
// se o driver desaparecer a meio, desativa e cala
|
||||||
|
if (err == ESP_ERR_INVALID_STATE) s_display_enabled = false;
|
||||||
|
// opcional: não logar para evitar spam
|
||||||
|
// ESP_LOGE(TAG, "raw_top falhou: %s", esp_err_to_name(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void display_raw_bottom(int pos, uint16_t mask)
|
void display_raw_bottom(int pos, uint16_t mask)
|
||||||
{
|
{
|
||||||
disp_raw(DISP_BOTTOM_ADDR, pos, mask);
|
if (!s_display_enabled) return;
|
||||||
|
|
||||||
|
esp_err_t err = disp_raw(DISP_BOTTOM_ADDR, pos, mask);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
if (err == ESP_ERR_INVALID_STATE) s_display_enabled = false;
|
||||||
|
// opcional: não logar
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void display_clear_top(void)
|
void display_clear_top(void)
|
||||||
{
|
{
|
||||||
disp_clear(DISP_TOP_ADDR);
|
if (!s_display_enabled) return;
|
||||||
|
|
||||||
|
esp_err_t err = disp_clear(DISP_TOP_ADDR);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
if (err == ESP_ERR_INVALID_STATE) s_display_enabled = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void display_clear_bottom(void)
|
void display_clear_bottom(void)
|
||||||
{
|
{
|
||||||
disp_clear(DISP_BOTTOM_ADDR);
|
if (!s_display_enabled) return;
|
||||||
|
|
||||||
|
esp_err_t err = disp_clear(DISP_BOTTOM_ADDR);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
if (err == ESP_ERR_INVALID_STATE) s_display_enabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// =====================
|
|
||||||
// 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
|
// TEXTO / CHARS
|
||||||
|
// =======================================================
|
||||||
|
|
||||||
|
void display_char_top(int pos, char c)
|
||||||
|
{
|
||||||
|
if (!s_display_enabled) return;
|
||||||
|
display_raw_top(pos, charset(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_char_bottom(int pos, char c)
|
||||||
|
{
|
||||||
|
if (!s_display_enabled) return;
|
||||||
|
display_raw_bottom(pos, charset(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_text_top(const char *txt)
|
||||||
|
{
|
||||||
|
if (!s_display_enabled) return;
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
char c = (txt && txt[i]) ? txt[i] : ' ';
|
||||||
|
display_char_top(i, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_text_bottom(const char *txt)
|
||||||
|
{
|
||||||
|
if (!s_display_enabled) return;
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
char c = (txt && txt[i]) ? txt[i] : ' ';
|
||||||
|
display_char_bottom(i, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================================================
|
||||||
|
// NÚMEROS
|
||||||
|
// =======================================================
|
||||||
|
|
||||||
|
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 (!s_display_enabled) return;
|
||||||
|
if (val > 9) val = 0;
|
||||||
|
display_raw_top(pos, digit_mask[val]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_digit_bottom(int pos, uint8_t val)
|
||||||
|
{
|
||||||
|
if (!s_display_enabled) return;
|
||||||
|
if (val > 9) val = 0;
|
||||||
|
display_raw_bottom(pos, digit_mask[val]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_number_top(int num)
|
||||||
|
{
|
||||||
|
if (!s_display_enabled) return;
|
||||||
|
|
||||||
|
if (num < 0) num = 0;
|
||||||
|
if (num > 9999) num = 9999;
|
||||||
|
|
||||||
|
display_digit_top(3, (uint8_t)(num % 10));
|
||||||
|
display_digit_top(2, (uint8_t)((num / 10) % 10));
|
||||||
|
display_digit_top(1, (uint8_t)((num / 100) % 10));
|
||||||
|
display_digit_top(0, (uint8_t)((num / 1000) % 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_number_bottom(int num)
|
||||||
|
{
|
||||||
|
if (!s_display_enabled) return;
|
||||||
|
|
||||||
|
if (num < 0) num = 0;
|
||||||
|
if (num > 9999) num = 9999;
|
||||||
|
|
||||||
|
display_digit_bottom(3, (uint8_t)(num % 10));
|
||||||
|
display_digit_bottom(2, (uint8_t)((num / 10) % 10));
|
||||||
|
display_digit_bottom(1, (uint8_t)((num / 100) % 10));
|
||||||
|
display_digit_bottom(0, (uint8_t)((num / 1000) % 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================================================
|
||||||
|
// RELÓGIO (HH:MM com DP no meio)
|
||||||
|
// =======================================================
|
||||||
|
|
||||||
|
void display_set_time_top(int horas, int minutos)
|
||||||
|
{
|
||||||
|
if (!s_display_enabled) return;
|
||||||
|
|
||||||
|
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, (uint8_t)h1);
|
||||||
|
display_raw_top(1, mid);
|
||||||
|
display_digit_top(2, (uint8_t)m1);
|
||||||
|
display_digit_top(3, (uint8_t)m2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================================================
|
||||||
|
// CHARSET (igual ao teu)
|
||||||
// =======================================================
|
// =======================================================
|
||||||
|
|
||||||
static uint16_t charset(char c)
|
static uint16_t charset(char c)
|
||||||
@ -181,7 +331,7 @@ static uint16_t charset(char c)
|
|||||||
case '8': return SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G;
|
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;
|
case '9': return SEG_A | SEG_B | SEG_C | SEG_D | SEG_F | SEG_G;
|
||||||
|
|
||||||
// LETRAS COMPLETAS (mantidas)
|
// LETRAS
|
||||||
case 'A': case 'a': return SEG_A | SEG_B | SEG_C | SEG_E | SEG_F | SEG_G;
|
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 '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 'C': case 'c': return SEG_A | SEG_F | SEG_E | SEG_D;
|
||||||
@ -219,113 +369,10 @@ static uint16_t charset(char c)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void display_debug_segment(uint16_t bitmask)
|
||||||
// =======================================================
|
|
||||||
// DISPLAY CARACTER / TEXTO
|
|
||||||
// =======================================================
|
|
||||||
|
|
||||||
void display_char_top(int pos, char c)
|
|
||||||
{
|
{
|
||||||
display_raw_top(pos, charset(c));
|
(void)bitmask;
|
||||||
}
|
// opcional: podes mostrar um padrão fixo se display estiver ativo
|
||||||
|
// if (!display_is_enabled()) return;
|
||||||
void display_char_bottom(int pos, char c)
|
// display_raw_top(0, bitmask);
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,20 +1,86 @@
|
|||||||
#include "driver/i2c.h"
|
#include "driver/i2c.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "esp_rom_sys.h"
|
||||||
|
|
||||||
#define SDA_PIN 21
|
#define SDA_PIN 21
|
||||||
#define SCL_PIN 22
|
#define SCL_PIN 22
|
||||||
#define I2C_PORT I2C_NUM_0
|
#define I2C_PORT I2C_NUM_0
|
||||||
|
|
||||||
void i2c_init(void)
|
static const char *TAG_I2C = "I2C";
|
||||||
|
|
||||||
|
static void i2c_bus_reset(void)
|
||||||
{
|
{
|
||||||
|
// Solta o bus: gera 9 clocks em SCL para libertar SDA se algum escravo ficou preso
|
||||||
|
gpio_config_t io = {
|
||||||
|
.pin_bit_mask = (1ULL << SDA_PIN) | (1ULL << SCL_PIN),
|
||||||
|
.mode = GPIO_MODE_OUTPUT_OD,
|
||||||
|
.pull_up_en = GPIO_PULLUP_ENABLE,
|
||||||
|
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||||
|
.intr_type = GPIO_INTR_DISABLE
|
||||||
|
};
|
||||||
|
gpio_config(&io);
|
||||||
|
|
||||||
|
gpio_set_level(SDA_PIN, 1);
|
||||||
|
gpio_set_level(SCL_PIN, 1);
|
||||||
|
esp_rom_delay_us(5);
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 0; i < 9; i++) {
|
||||||
|
gpio_set_level(SCL_PIN, 0);
|
||||||
|
esp_rom_delay_us(5);
|
||||||
|
|
||||||
|
gpio_set_level(SCL_PIN, 1);
|
||||||
|
esp_rom_delay_us(5);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// STOP
|
||||||
|
gpio_set_level(SDA_PIN, 0);
|
||||||
|
esp_rom_delay_us(5);
|
||||||
|
|
||||||
|
gpio_set_level(SCL_PIN, 1);
|
||||||
|
esp_rom_delay_us(5);
|
||||||
|
|
||||||
|
gpio_set_level(SDA_PIN, 1);
|
||||||
|
esp_rom_delay_us(5);
|
||||||
|
|
||||||
|
|
||||||
|
// devolve os pinos ao I2C driver depois
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t i2c_init(void)
|
||||||
|
{
|
||||||
|
// se já estava instalado, limpa e volta a instalar
|
||||||
|
i2c_driver_delete(I2C_PORT);
|
||||||
|
|
||||||
|
i2c_bus_reset();
|
||||||
|
|
||||||
i2c_config_t cfg = {
|
i2c_config_t cfg = {
|
||||||
.mode = I2C_MODE_MASTER,
|
.mode = I2C_MODE_MASTER,
|
||||||
.sda_io_num = SDA_PIN,
|
.sda_io_num = SDA_PIN,
|
||||||
.scl_io_num = SCL_PIN,
|
.scl_io_num = SCL_PIN,
|
||||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||||
.master.clk_speed = 400000
|
.master.clk_speed = 100000, // começa seguro
|
||||||
|
.clk_flags = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
i2c_param_config(I2C_PORT, &cfg);
|
esp_err_t err;
|
||||||
i2c_driver_install(I2C_PORT, cfg.mode, 0, 0, 0);
|
|
||||||
|
err = i2c_param_config(I2C_PORT, &cfg);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG_I2C, "i2c_param_config falhou: %s", esp_err_to_name(err));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = i2c_driver_install(I2C_PORT, cfg.mode, 0, 0, 0);
|
||||||
|
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
||||||
|
ESP_LOGE(TAG_I2C, "i2c_driver_install falhou: %s", esp_err_to_name(err));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(TAG_I2C, "I2C OK (SDA=%d SCL=%d) freq=100kHz", SDA_PIN, SCL_PIN);
|
||||||
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
// =======================================================
|
// =======================================================
|
||||||
// ENDEREÇOS I2C (definidos no .c)
|
// ENDEREÇOS I2C (definidos no .c)
|
||||||
@ -8,7 +10,7 @@
|
|||||||
// BOTTOM = 0x70
|
// BOTTOM = 0x70
|
||||||
|
|
||||||
// Inicialização dos dois displays
|
// Inicialização dos dois displays
|
||||||
void display_init(void);
|
esp_err_t display_init(void);
|
||||||
|
|
||||||
// =======================================================
|
// =======================================================
|
||||||
// RAW ACCESS (usa 16 bits de segmentos)
|
// RAW ACCESS (usa 16 bits de segmentos)
|
||||||
@ -18,6 +20,8 @@ void display_raw_bottom(int pos, uint16_t mask);
|
|||||||
|
|
||||||
void display_clear_top(void);
|
void display_clear_top(void);
|
||||||
void display_clear_bottom(void);
|
void display_clear_bottom(void);
|
||||||
|
void display_set_enabled(bool en);
|
||||||
|
bool display_is_enabled(void);
|
||||||
|
|
||||||
// =======================================================
|
// =======================================================
|
||||||
// TEXTO E CARACTERES
|
// TEXTO E CARACTERES
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include "esp_err.h"
|
||||||
|
|
||||||
void i2c_init(void);
|
esp_err_t i2c_init(void);
|
||||||
|
|||||||
42
main/main.c
42
main/main.c
@ -32,6 +32,11 @@
|
|||||||
#include "display.h"
|
#include "display.h"
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
esp_err_t i2c_init(void);
|
||||||
|
esp_err_t display_init(void);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool modo_bloqueado = false; // definição oficial
|
bool modo_bloqueado = false; // definição oficial
|
||||||
|
|
||||||
#define SDA_PIN 21
|
#define SDA_PIN 21
|
||||||
@ -136,24 +141,36 @@ void ht16_test()
|
|||||||
i2c_master_write_to_device(I2C_PORT, 0x70, buf, sizeof(buf), 20 / portTICK_PERIOD_MS);
|
i2c_master_write_to_device(I2C_PORT, 0x70, buf, sizeof(buf), 20 / portTICK_PERIOD_MS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void i2c_scan()
|
//************************************************************** */
|
||||||
|
void i2c_scan(void)
|
||||||
{
|
{
|
||||||
printf("\n--- A fazer scan ao I2C ---\n");
|
printf("\n--- A fazer scan ao I2C ---\n");
|
||||||
// --- SCAN I2C ---
|
|
||||||
for (uint8_t addr = 1; addr < 127; addr++) {
|
int found = 0;
|
||||||
|
|
||||||
|
for (uint8_t addr = 1; addr < 0x7F; addr++) {
|
||||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||||
i2c_master_start(cmd);
|
i2c_master_start(cmd);
|
||||||
i2c_master_write_byte(cmd, (addr << 1) | I2C_MASTER_WRITE, true);
|
i2c_master_write_byte(cmd, (addr << 1) | I2C_MASTER_WRITE, true);
|
||||||
i2c_master_stop(cmd);
|
i2c_master_stop(cmd);
|
||||||
|
|
||||||
esp_err_t r = i2c_master_cmd_begin(I2C_PORT, cmd, 20 / portTICK_PERIOD_MS);
|
esp_err_t r = i2c_master_cmd_begin(I2C_PORT, cmd, pdMS_TO_TICKS(50));
|
||||||
i2c_cmd_link_delete(cmd);
|
i2c_cmd_link_delete(cmd);
|
||||||
|
|
||||||
if (r == ESP_OK) {
|
if (r == ESP_OK) {
|
||||||
printf("ENCONTRADO I2C: 0x%02X\n", addr);
|
printf("✅ I2C encontrado: 0x%02X\n", addr);
|
||||||
|
found++;
|
||||||
|
} else if (r == ESP_ERR_TIMEOUT) {
|
||||||
|
printf("⏱️ TIMEOUT no addr 0x%02X (bus preso?)\n", addr);
|
||||||
|
break; // não vale a pena continuar
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
printf("❌ Nenhum dispositivo I2C encontrado\n");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ============================
|
// ============================
|
||||||
// MAIN
|
// MAIN
|
||||||
@ -185,9 +202,20 @@ void app_main(void) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
i2c_init(); // <- o helper entra aqui
|
// 1) Inicializa I2C (não aborta)
|
||||||
display_init(); // <- inicializa o HT16K33
|
esp_err_t ei = i2c_init();
|
||||||
|
if (ei != ESP_OK) {
|
||||||
|
printf("i2c_init falhou: %s\n", esp_err_to_name(ei));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Scan I2C (ver o que existe no barramento)
|
||||||
i2c_scan();
|
i2c_scan();
|
||||||
|
|
||||||
|
// 3) Inicializa displays SEM abortar (para não rebootar em loop)
|
||||||
|
esp_err_t ed = display_init();
|
||||||
|
printf("display_init = %s\n", esp_err_to_name(ed));
|
||||||
|
|
||||||
|
// 4) Teste simples no display (se existir)
|
||||||
display_text_top("INIT");
|
display_text_top("INIT");
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
3
tatus
Normal file
3
tatus
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[33mc5aeddc[m[33m ([m[1;36mHEAD[m[33m -> [m[1;32mmain[m[33m, [m[1;33mtag: [m[1;33mportal-top[m[33m, [m[1;31morigin/main[m[33m, [m[1;31morigin/HEAD[m[33m)[m chore: ignore vscode settings
|
||||||
|
[33m0d828ca[m wifi: portal Wi-Fi top com scan, clear_wifi MQTT e fluxo limpo STA/AP
|
||||||
|
[33m4d28d62[m commit final pc2
|
||||||
Loading…
Reference in New Issue
Block a user