Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions src/M5GFX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "lgfx/v1/panel/Panel_ILI9342.hpp"
#include "lgfx/v1/panel/Panel_SSD1306.hpp"
#include "lgfx/v1/panel/Panel_SSD1677.hpp"
#include "lgfx/v1/panel/Panel_TM1680.hpp"
#include "lgfx/v1/panel/Panel_ST7735.hpp"
#include "lgfx/v1/panel/Panel_ST7789.hpp"
#include "lgfx/v1/panel/Panel_GC9A01.hpp"
Expand Down Expand Up @@ -50,6 +51,11 @@ static constexpr int_fast16_t in_i2c_port = I2C_NUM_1;
#include <lgfx/v1/platforms/esp32/Panel_EPD.hpp>

#endif

#elif defined ( CONFIG_IDF_TARGET_ESP32C61 )

#include "lgfx/v1/platforms/esp32/Bus_I2C.hpp"

#endif

#else
Expand Down Expand Up @@ -879,6 +885,12 @@ namespace m5gfx

// Touch_M5ToughC5 は lgfx::Touch_CHSC6540 に統合済み

#elif defined (CONFIG_IDF_TARGET_ESP32C61)

static constexpr int32_t i2c_freq = 400000;
static constexpr int_fast16_t i2c_port = I2C_NUM_0;
static constexpr std::uint8_t tm1680_i2c_addr = 0x72; // CoreMatrix LED matrix driver

#elif defined (CONFIG_IDF_TARGET_ESP32C6)

static constexpr int32_t i2c_freq = 400000;
Expand Down Expand Up @@ -3449,6 +3461,81 @@ The usage of each pin is as follows.
for (auto &bup : backup_pins) { bup.restore(); }
}

#elif defined (CONFIG_IDF_TARGET_ESP32C61)

if (board == 0 || board == board_t::board_M5CoreMatrix)
{
// CoreMatrix: system I2C SDA=G0 / SCL=G1 (physically shared with Grove and M-Bus)
static constexpr int_fast16_t corematrix_i2c_sda = GPIO_NUM_0;
static constexpr int_fast16_t corematrix_i2c_scl = GPIO_NUM_1;

gpio::pin_backup_t backup_pins[] =
{ GPIO_NUM_0
, GPIO_NUM_1
};

// Probe over software I2C; the hardware port is left untouched until the board is confirmed
lgfx::i2c::init(probe_i2c_port, corematrix_i2c_sda, corematrix_i2c_scl);

if (_check_m5pm1(probe_i2c_port) && _check_m5ioe1(probe_i2c_port)) {
lgfx::i2c::writeRegister8(probe_i2c_port, m5pm1_i2c_addr, 0x09, 0x00, 0, m5pm1_i2c_freq); // I2C sleep disable
lgfx::i2c::writeRegister8(probe_i2c_port, m5pm1_i2c_addr, 0x0A, 0x00, 0, m5pm1_i2c_freq); // WDT disable

// Drive M5IOE1 PIN4 (LEDS_EN) high to power the LED matrix rail.
// The TM1680 sits on that rail and does not respond on I2C until powered.
lgfx::i2c::writeRegister8(probe_i2c_port, m5ioe1_i2c_addr, 0x23, 0x00, 0, m5ioe1_i2c_freq); // I2C sleep disable
lgfx::i2c::bitOff(probe_i2c_port, m5ioe1_i2c_addr, 0x13, 1 << 3, m5ioe1_i2c_freq); // PIN4 push-pull
lgfx::i2c::bitOn (probe_i2c_port, m5ioe1_i2c_addr, 0x03, 1 << 3, m5ioe1_i2c_freq); // PIN4 output
lgfx::i2c::bitOn (probe_i2c_port, m5ioe1_i2c_addr, 0x05, 1 << 3, m5ioe1_i2c_freq); // PIN4 HIGH
lgfx::delay(20); // wait for the rail to rise and the TM1680 to complete POR

// The TM1680 has no ID register; check for an address ACK only
bool hit = lgfx::i2c::beginTransaction(probe_i2c_port, tm1680_i2c_addr, 100000, false).has_value()
&& lgfx::i2c::endTransaction(probe_i2c_port).has_value();
if (hit)
{
board = board_t::board_M5CoreMatrix;
ESP_LOGI(LIBRARY_NAME, "[Autodetect] board_M5CoreMatrix");

// Board confirmed: hand the bus over to the hardware I2C port
lgfx::i2c::release(probe_i2c_port);

auto bus_i2c = new Bus_I2C();
{
auto cfg = bus_i2c->config();
cfg.i2c_port = i2c_port;
cfg.freq_write = i2c_freq;
cfg.freq_read = i2c_freq;
cfg.pin_sda = corematrix_i2c_sda;
cfg.pin_scl = corematrix_i2c_scl;
cfg.i2c_addr = tm1680_i2c_addr;
cfg.prefix_len = 0; // the TM1680 protocol has no command/data prefix byte
bus_i2c->config(cfg);
}
_bus_last.reset(bus_i2c);

auto p = new lgfx::Panel_TM1680();
{
auto cfg = p->config();
cfg.bus_shared = false;
// rotation 1 (the default) shows upright with the buttons at the top
cfg.offset_rotation = 3;
p->config(cfg);
p->setRotation(1);
}
p->bus(bus_i2c);
_panel_last.reset(p);

goto init_clear;
}
// Not this board: restore the LED matrix power rail
lgfx::i2c::bitOff(probe_i2c_port, m5ioe1_i2c_addr, 0x05, 1 << 3, m5ioe1_i2c_freq);
}
// Reached only when no board was detected; only the software port was touched
lgfx::i2c::release(probe_i2c_port);
for (auto &bup : backup_pins) { bup.restore(); }
}

#elif defined (CONFIG_IDF_TARGET_ESP32H2)
#endif

Expand Down Expand Up @@ -3514,6 +3601,7 @@ The usage of each pin is as follows.
case board_M5Tough: title = "M5Tough"; break;
case board_M5ToughC5: title = "M5ToughC5"; break;
case board_M5StampC5: title = "M5StampC5"; break;
case board_M5CoreMatrix: title = "M5CoreMatrix"; break;
case board_M5Station: title = "M5Station"; break;
case board_M5StopWatch: title = "M5StopWatch"; break;
case board_M5ChainCaptain: title = "M5ChainCaptain"; break;
Expand Down
142 changes: 142 additions & 0 deletions src/lgfx/v1/panel/Panel_TM1680.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*----------------------------------------------------------------------------/
Lovyan GFX - Graphics library for embedded devices.

Original Source:
https://github.com/lovyan03/LovyanGFX/

Licence:
[FreeBSD](https://github.com/lovyan03/LovyanGFX/blob/master/license.txt)

Author:
[lovyan03](https://twitter.com/lovyan03)

Contributors:
[ciniml](https://github.com/ciniml)
[mongonta0716](https://github.com/mongonta0716)
[tobozo](https://github.com/tobozo)
/----------------------------------------------------------------------------*/
#include "Panel_TM1680.hpp"
#include "../Bus.hpp"
#include "../platforms/common.hpp"

#include <string.h>

namespace lgfx
{
inline namespace v1
{
//----------------------------------------------------------------------------

bool Panel_TM1680::init(bool use_reset)
{
// The TM1680 does not respond to I2C reads, so the read probe of
// Panel_1bitOLED::init cannot be used; send the init commands without probing.
if (!Panel_HasBuffer::init(use_reset))
{
return false;
}
// Panel_HasBuffer::init does not clear the allocated buffer;
// clear it here so setInvert below does not push garbage to the LEDs.
memset(_buf, 0, _get_buffer_length());

startWrite(true);
for (size_t i = 0; auto cmds = getInitCommands(i); i++)
{
size_t idx = 0;
while (cmds[idx] != 0xFF || cmds[idx + 1] != 0xFF) ++idx;
if (idx) { _bus->writeBytes(cmds, idx, false, true); }
}
setInvert(_invert);
setRotation(_rotation);
endWrite();

return true;
}

void Panel_TM1680::setInvert(bool invert)
{
_invert = invert;
// The TM1680 has no display-invert command; invert while packing in display().
if (_buf)
{
startWrite();
display(0, 0, _cfg.panel_width, _cfg.panel_height);
endWrite();
}
}

void Panel_TM1680::setSleep(bool flg)
{
startWrite();
if (flg)
{
_bus->writeCommand(CMD_LED_OFF, 8);
_bus->writeCommand(CMD_SYS_DIS, 8);
}
else
{
_bus->writeCommand(CMD_SYS_EN, 8);
_bus->writeCommand(CMD_LED_ON, 8);
}
endWrite();
}

void Panel_TM1680::setBrightness(uint8_t brightness)
{
startWrite();
if (brightness)
{
_bus->writeCommand(CMD_PWM_DUTY + (((brightness * 16) >> 8) & 0x0F), 8);
_bus->writeCommand(CMD_LED_ON, 8);
}
else
{
_bus->writeCommand(CMD_LED_OFF, 8);
}
endWrite();
}

void Panel_TM1680::display(uint_fast16_t x, uint_fast16_t y, uint_fast16_t w, uint_fast16_t h)
{
if (0 < w && 0 < h)
{
uint_fast16_t xs = x, xe = x + w - 1;
uint_fast16_t ys = y, ye = y + h - 1;
_update_transferred_rect(xs, ys, xe, ye);
}
if (_range_mod.empty()) { return; }

// The whole frame is only 32 bytes; always send it in full.
// The leading byte is the RAM write address.
uint8_t sendbuf[1 + ((16 * 16) >> 3)];
sendbuf[0] = 0x00;
bool invert = _invert ^ _cfg.invert;

for (uint_fast8_t py = 0; py < 16; ++py)
{
uint32_t mask = 1 << (py & 7);
auto src = &_buf[(py >> 3) * 16];
for (uint_fast8_t xb = 0; xb < 2; ++xb)
{
uint_fast8_t v = 0;
for (uint_fast8_t bit = 0; bit < 8; ++bit)
{
if (src[(xb << 3) + bit] & mask)
{ // in TM1680 RAM, rows A3-A0 occupy the upper nibble half
v |= 1 << ((bit > 3) ? (bit & 3) : (4 + bit));
}
}
sendbuf[1 + py * 2 + xb] = invert ? (v ^ 0xFF) : v;
}
}

_bus->writeBytes(sendbuf, sizeof(sendbuf), true, true);
_range_mod.top = INT16_MAX;
_range_mod.left = INT16_MAX;
_range_mod.right = 0;
_range_mod.bottom = 0;
}

//----------------------------------------------------------------------------
}
}
76 changes: 76 additions & 0 deletions src/lgfx/v1/panel/Panel_TM1680.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*----------------------------------------------------------------------------/
Lovyan GFX - Graphics library for embedded devices.

Original Source:
https://github.com/lovyan03/LovyanGFX/

Licence:
[FreeBSD](https://github.com/lovyan03/LovyanGFX/blob/master/license.txt)

Author:
[lovyan03](https://twitter.com/lovyan03)

Contributors:
[ciniml](https://github.com/ciniml)
[mongonta0716](https://github.com/mongonta0716)
[tobozo](https://github.com/tobozo)
/----------------------------------------------------------------------------*/
#pragma once

#include "Panel_SSD1306.hpp"

namespace lgfx
{
inline namespace v1
{
//----------------------------------------------------------------------------

// Titan Micro TM1680 LED matrix driver (I2C, 24ROW x 16COM mode).
// The frame buffer keeps the Panel_1bitOLED page layout and is converted
// to the TM1680 RAM layout when pushed by display().
// The protocol has no command/data prefix byte: configure Bus_I2C with prefix_len = 0.
struct Panel_TM1680 : public Panel_1bitOLED
{
Panel_TM1680(void)
{
_cfg.memory_width = _cfg.panel_width = 16;
_cfg.memory_height = _cfg.panel_height = 16;
_auto_display = true;
}

bool init(bool use_reset) override;
void setInvert(bool invert) override;
void setSleep(bool flg) override;
void setBrightness(uint8_t brightness) override;
void display(uint_fast16_t x, uint_fast16_t y, uint_fast16_t w, uint_fast16_t h) override;

protected:
static constexpr uint8_t CMD_SYS_DIS = 0x80;
static constexpr uint8_t CMD_SYS_EN = 0x81;
static constexpr uint8_t CMD_LED_OFF = 0x82;
static constexpr uint8_t CMD_LED_ON = 0x83;
static constexpr uint8_t CMD_RC_MASTER = 0x98; // internal RC oscillator, master mode
static constexpr uint8_t CMD_COM_16NMOS = 0xA4; // 24ROW x 16COM (NMOS)
static constexpr uint8_t CMD_PWM_DUTY = 0xB0; // +0..15 = duty 1/16..16/16

const uint8_t* getInitCommands(uint8_t listno) const override
{
static constexpr uint8_t list0[] = {
CMD_SYS_DIS,
CMD_COM_16NMOS,
CMD_RC_MASTER,
CMD_SYS_EN,
CMD_PWM_DUTY + 1,
CMD_LED_ON,
0xFF, 0xFF, // end
};
switch (listno) {
case 0: return list0;
default: return nullptr;
}
}
};

//----------------------------------------------------------------------------
}
}
Loading