From 09e5be3233d9b36862b673b077e9bfefb7934453 Mon Sep 17 00:00:00 2001 From: master Date: Sun, 28 Dec 2025 18:29:40 +0000 Subject: [PATCH] feat(i2c-display): driver HT16K33 robusto + fix inversao TOP + I2C estabilizado --- main/display.c | 125 ++++++++++++++++++++++++-------------- main/i2c_helper.c | 74 ++++++++++++++++++++-- main/include/display.h | 3 +- main/include/i2c_helper.h | 3 +- main/main.c | 46 +++++++++++--- tatus | 3 + 6 files changed, 192 insertions(+), 62 deletions(-) create mode 100644 tatus diff --git a/main/display.c b/main/display.c index e2f92b0..95ded5b 100644 --- a/main/display.c +++ b/main/display.c @@ -1,5 +1,13 @@ +// display.c (HT16K33 14-seg / 4 dígitos) +// TOP = 0x71 | BOTTOM = 0x70 +// I2C_PORT = I2C_NUM_0 +// +// NOTA: agora o código NÃO engole erros de I2C. +// display_init() devolve esp_err_t e podes usar ESP_ERROR_CHECK(display_init()). + #include "driver/i2c.h" #include "esp_log.h" +#include "esp_err.h" #include "display.h" #include @@ -8,12 +16,14 @@ // 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"; +static uint16_t rotate180(uint16_t m); +static uint16_t charset(char c); + +static const char *TAG = "DISPLAY"; // ======================================================= -// MAPA DE SEGMENTOS (mantido igual ao teu) +// MAPA DE SEGMENTOS (igual ao teu) // ======================================================= #define SEG_A (1 << 0) @@ -40,31 +50,31 @@ const char *TAG = "DISPLAY"; // ======================================================= -// FUNÇÕES GENÉRICAS PARA QUALQUER DISPLAY +// FUNÇÕES GENÉRICAS PARA QUALQUER DISPLAY (AGORA COM ERRO) // ======================================================= -static void disp_send_cmd(uint8_t addr, uint8_t cmd) +static esp_err_t disp_send_cmd(uint8_t addr, uint8_t cmd) { - i2c_master_write_to_device(I2C_PORT, addr, &cmd, 1, 20 / portTICK_PERIOD_MS); + return i2c_master_write_to_device(I2C_PORT, addr, &cmd, 1, pdMS_TO_TICKS(50)); } -static void disp_raw(uint8_t addr, int pos, uint16_t mask) +static esp_err_t disp_raw(uint8_t addr, int pos, uint16_t mask) { - if (pos < 0 || pos > 3) return; + if (pos < 0 || pos > 3) return ESP_ERR_INVALID_ARG; uint8_t buf[3]; - buf[0] = pos * 2; - buf[1] = mask & 0xFF; - buf[2] = (mask >> 8) & 0xFF; + buf[0] = (uint8_t)(pos * 2); + buf[1] = (uint8_t)(mask & 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}; 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)); } @@ -72,25 +82,42 @@ static void disp_clear(uint8_t addr) // INIT PARA OS DOIS DISPLAYS (TOP 0x71 | BOTTOM 0x70) // ======================================================= -void display_init(void) +esp_err_t display_init(void) { - uint8_t cmd1 = 0x21; // oscillator ON - uint8_t cmd2 = 0x81; // display ON, blink OFF - uint8_t cmd3 = 0xEF; // brightness MAX + 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 - 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); + err = disp_send_cmd(DISP_TOP_ADDR, cmd1); + if (err != ESP_OK) { ESP_LOGE(TAG, "TOP 0x%02X cmd1 falhou: %s", DISP_TOP_ADDR, esp_err_to_name(err)); return err; } + + err = disp_send_cmd(DISP_TOP_ADDR, cmd2); + if (err != ESP_OK) { ESP_LOGE(TAG, "TOP 0x%02X cmd2 falhou: %s", DISP_TOP_ADDR, esp_err_to_name(err)); return err; } + + err = disp_send_cmd(DISP_TOP_ADDR, cmd3); + if (err != ESP_OK) { ESP_LOGE(TAG, "TOP 0x%02X cmd3 falhou: %s", DISP_TOP_ADDR, esp_err_to_name(err)); return err; } + + err = disp_clear(DISP_TOP_ADDR); + if (err != ESP_OK) { ESP_LOGE(TAG, "TOP 0x%02X clear falhou: %s", DISP_TOP_ADDR, esp_err_to_name(err)); return err; } // 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); + err = disp_send_cmd(DISP_BOTTOM_ADDR, cmd1); + if (err != ESP_OK) { ESP_LOGE(TAG, "BOTTOM 0x%02X cmd1 falhou: %s", DISP_BOTTOM_ADDR, esp_err_to_name(err)); return err; } - ESP_LOGI(TAG, "📟 Displays inicializados: TOP=0x71 BOTTOM=0x70"); + err = disp_send_cmd(DISP_BOTTOM_ADDR, cmd2); + if (err != ESP_OK) { ESP_LOGE(TAG, "BOTTOM 0x%02X cmd2 falhou: %s", DISP_BOTTOM_ADDR, esp_err_to_name(err)); return err; } + + err = disp_send_cmd(DISP_BOTTOM_ADDR, cmd3); + if (err != ESP_OK) { ESP_LOGE(TAG, "BOTTOM 0x%02X cmd3 falhou: %s", DISP_BOTTOM_ADDR, esp_err_to_name(err)); return err; } + + err = disp_clear(DISP_BOTTOM_ADDR); + if (err != ESP_OK) { ESP_LOGE(TAG, "BOTTOM 0x%02X clear falhou: %s", DISP_BOTTOM_ADDR, esp_err_to_name(err)); return err; } + + ESP_LOGI(TAG, "📟 Displays OK: TOP=0x%02X BOTTOM=0x%02X", DISP_TOP_ADDR, DISP_BOTTOM_ADDR); + return ESP_OK; } @@ -99,27 +126,34 @@ void display_init(void) // ======================================================= 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); + esp_err_t err = disp_raw(DISP_TOP_ADDR, pos, mask); + if (err != ESP_OK) { + ESP_LOGE(TAG, "raw_top falhou (pos=%d addr=0x%02X): %s", pos, DISP_TOP_ADDR, esp_err_to_name(err)); + } } void display_raw_bottom(int pos, uint16_t mask) { - disp_raw(DISP_BOTTOM_ADDR, pos, mask); + esp_err_t err = disp_raw(DISP_BOTTOM_ADDR, pos, mask); + if (err != ESP_OK) { + ESP_LOGE(TAG, "raw_bottom falhou (pos=%d addr=0x%02X): %s", pos, DISP_BOTTOM_ADDR, esp_err_to_name(err)); + } } void display_clear_top(void) { - disp_clear(DISP_TOP_ADDR); + esp_err_t err = disp_clear(DISP_TOP_ADDR); + if (err != ESP_OK) ESP_LOGE(TAG, "clear_top falhou: %s", esp_err_to_name(err)); } void display_clear_bottom(void) { - disp_clear(DISP_BOTTOM_ADDR); + esp_err_t err = disp_clear(DISP_BOTTOM_ADDR); + if (err != ESP_OK) ESP_LOGE(TAG, "clear_bottom falhou: %s", esp_err_to_name(err)); } + // ===================== // ROTATE 180 para 14 segmentos REAL // (mapa correto para o teu HT16K33) @@ -231,22 +265,21 @@ void display_char_top(int pos, char c) void display_char_bottom(int pos, char c) { - disp_raw(DISP_BOTTOM_ADDR, pos, charset(c)); + display_raw_bottom(pos, charset(c)); } void display_text_top(const char *txt) { for (int i = 0; i < 4; i++) { - char c = txt[i] ? txt[i] : ' '; + char c = (txt && 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] : ' '; + char c = (txt && txt[i]) ? txt[i] : ' '; display_char_bottom(i, c); } } @@ -276,11 +309,10 @@ void display_digit_top(int pos, uint8_t val) 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]); + display_raw_bottom(pos, digit_mask[val]); } void display_number_top(int num) @@ -294,21 +326,20 @@ void display_number_top(int num) 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_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 +// display_set_time_top() — mantém SEG_DP no sítio certo // ======================================================= void display_set_time_top(int horas, int minutos) { @@ -324,8 +355,8 @@ void display_set_time_top(int horas, int minutos) uint16_t mid = digit_mask[h2] | SEG_DP; - display_digit_top(0, h1); + display_digit_top(0, (uint8_t)h1); display_raw_top(1, mid); - display_digit_top(2, m1); - display_digit_top(3, m2); + display_digit_top(2, (uint8_t)m1); + display_digit_top(3, (uint8_t)m2); } diff --git a/main/i2c_helper.c b/main/i2c_helper.c index b4259ce..bcfba33 100644 --- a/main/i2c_helper.c +++ b/main/i2c_helper.c @@ -1,20 +1,86 @@ #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 SCL_PIN 22 #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 = { .mode = I2C_MODE_MASTER, .sda_io_num = SDA_PIN, .scl_io_num = SCL_PIN, .sda_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); - i2c_driver_install(I2C_PORT, cfg.mode, 0, 0, 0); + esp_err_t err; + + 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; } diff --git a/main/include/display.h b/main/include/display.h index 29dae8b..c5d7494 100644 --- a/main/include/display.h +++ b/main/include/display.h @@ -1,5 +1,6 @@ #pragma once #include +#include "esp_err.h" // ======================================================= // ENDEREÇOS I2C (definidos no .c) @@ -8,7 +9,7 @@ // BOTTOM = 0x70 // Inicialização dos dois displays -void display_init(void); +esp_err_t display_init(void); // ======================================================= // RAW ACCESS (usa 16 bits de segmentos) diff --git a/main/include/i2c_helper.h b/main/include/i2c_helper.h index 771b402..2dc4156 100644 --- a/main/include/i2c_helper.h +++ b/main/include/i2c_helper.h @@ -1,3 +1,4 @@ #pragma once +#include "esp_err.h" -void i2c_init(void); +esp_err_t i2c_init(void); diff --git a/main/main.c b/main/main.c index 63e00ad..072de09 100644 --- a/main/main.c +++ b/main/main.c @@ -32,6 +32,11 @@ #include "display.h" #include +esp_err_t i2c_init(void); +esp_err_t display_init(void); + + + bool modo_bloqueado = false; // definição oficial #define SDA_PIN 21 @@ -136,25 +141,37 @@ void ht16_test() 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"); - // --- 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_master_start(cmd); i2c_master_write_byte(cmd, (addr << 1) | I2C_MASTER_WRITE, true); 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); 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 // ============================ @@ -185,11 +202,22 @@ void app_main(void) { - i2c_init(); // <- o helper entra aqui - display_init(); // <- inicializa o HT16K33 + // 1) Inicializa I2C (não aborta) + 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(); - display_text_top("INIT"); - + + // 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"); + diff --git a/tatus b/tatus new file mode 100644 index 0000000..6f3708a --- /dev/null +++ b/tatus @@ -0,0 +1,3 @@ +c5aeddc (HEAD -> main, tag: portal-top, origin/main, origin/HEAD) chore: ignore vscode settings +0d828ca wifi: portal Wi-Fi top com scan, clear_wifi MQTT e fluxo limpo STA/AP +4d28d62 commit final pc2