#include <Arduino.h> #include <M5GFX.h> #include <M5UnitLCD.h> #include <esp_spi_flash.h> #include "firmware.h" M5GFX display; M5UnitLCD display2; std::uint32_t _crc_table[256]; static void initCRCtable(void) { std::size_t i = 0; do { std::uint32_t c = i << 24; std::size_t j = 0; do { c = ( c << 1) ^ ( ( c & 0x80000000) ? 0x04C11DB7 : 0); } while ( ++j < 8 ); _crc_table[i] = c; } while ( ++i < 256 ); } static uint32_t crc32(const std::uint8_t *buf, std::size_t len) { std::uint32_t c = 0xffffffff; for (std::size_t i = 0; i < len; i++) { c = (c << 8) ^ _crc_table[((c >> 24) ^ buf[i]) & 0xff]; } return c; } bool update(void) { display.fillScreen(TFT_WHITE); display.setCursor(0, 0); display.setFont(&fonts::Font4); display.setTextColor(TFT_BLACK, TFT_WHITE); display.drawString("UnitLCD", 0, 0); display.drawString("update", 0, 28); display.fillRect(10, 112, display.width() - 20, 17, TFT_BLACK); display.fillCircle( 10, 120, 8, TFT_BLACK); display.fillCircle( display.width() - 10, 120, 8, TFT_BLACK); auto panel = (lgfx::Panel_M5UnitLCD*)display2.getPanel(); auto bus = (lgfx::Bus_I2C*) panel->getBus(); auto cfg = bus->config(); std::uint8_t readbuf[8] = { 0 }; std::size_t length = sizeof(firmware); std::size_t block = (length + SPI_FLASH_SEC_SIZE - 1) / SPI_FLASH_SEC_SIZE; /// ファームウェア更新コマンド列 std::uint8_t data[16] = { lgfx::Panel_M5UnitLCD::CMD_UPDATE_BEGIN, 0x77, 0x89, lgfx::Panel_M5UnitLCD::CMD_UPDATE_BEGIN }; data[4] = length >> 24; data[5] = length >> 16; data[6] = length >> 8; data[7] = length >> 0; if (lgfx::i2c::beginTransaction(cfg.i2c_port, cfg.i2c_addr, 400000).has_error() || lgfx::i2c::writeBytes(cfg.i2c_port, data, 8).has_error() || lgfx::i2c::endTransaction(cfg.i2c_port).has_error()) { return false; } delay(50); data[3] = data[0] = lgfx::Panel_M5UnitLCD::CMD_UPDATE_DATA; /// セクタブロック単位(4096Byte) でデータ送信を繰り返す for (std::size_t b = 0; b < block; ++b) { display.fillCircle( 10 + (display.width() - 20) * b / block, 120, 4, TFT_GREEN ); if (!display.displayBusy()) { display.display(); } auto len = std::min<std::size_t>(SPI_FLASH_SEC_SIZE, length); auto crc = crc32(&firmware[b * SPI_FLASH_SEC_SIZE], len); data[4] = crc >> 24; /// 送信するデータのCRC32 data[5] = crc >> 16; data[6] = crc >> 8; data[7] = crc >> 0; Serial.printf("block %d :", b); /// ヘッダおよびデータブロック送信 if (lgfx::i2c::beginTransaction(cfg.i2c_port, cfg.i2c_addr, 400000).has_error() || lgfx::i2c::writeBytes(cfg.i2c_port, data, 8).has_error() || lgfx::i2c::writeBytes(cfg.i2c_port, &firmware[b * SPI_FLASH_SEC_SIZE], len).has_error() || lgfx::i2c::endTransaction(cfg.i2c_port).has_error()) { Serial.println("fail"); return false; } Serial.println("ok"); /// ビジーチェック int retry = 100; do { delay(10); readbuf[0] = lgfx::Panel_M5UnitLCD::UPDATE_RESULT_BUSY; if (!lgfx::i2c::beginTransaction(cfg.i2c_port, cfg.i2c_addr, 400000, true) || !lgfx::i2c::readBytes(cfg.i2c_port, readbuf, 1) || !lgfx::i2c::endTransaction(cfg.i2c_port)) { break; } } while (lgfx::Panel_M5UnitLCD::UPDATE_RESULT_OK != readbuf[0] && --retry); if (readbuf[0] != lgfx::Panel_M5UnitLCD::UPDATE_RESULT_OK) { Serial.printf("fail:%02x\r\n", readbuf[0]); return false; } length -= len; } data[3] = data[0] = lgfx::Panel_M5UnitLCD::CMD_UPDATE_END; lgfx::i2c::endTransaction(cfg.i2c_port); if (lgfx::i2c::beginTransaction(cfg.i2c_port, cfg.i2c_addr, 400000).has_error() || lgfx::i2c::writeBytes(cfg.i2c_port, data, 4).has_error() || lgfx::i2c::endTransaction(cfg.i2c_port).has_error()) { return false; } return true; } bool searchUnitLCD(void) { auto board = display.getBoard(); if (board == m5gfx::board_t::board_M5Stack) { if (display2.init(21, 22)) return true; } else if (board == m5gfx::board_t::board_M5Paper) { lgfx::gpio_hi(5); lgfx::pinMode(5, lgfx::pin_mode_t::output); if (display2.init(25, 32)) return true; } else if (board == m5gfx::board_t::board_M5StickC || board == m5gfx::board_t::board_M5StickCPlus || board == m5gfx::board_t::board_M5StackCore2 || board == m5gfx::board_t::board_M5StackCoreInk ) { if (board == m5gfx::board_t::board_M5StackCore2) { m5gfx::i2c::writeRegister8( 1 , 0x34 , 0x12, 0x40, ~0x00); // EXTEN enable } if (display2.init(32, 33)) return true; } else { if (display2.init(26, 32)) return true; // ATOM if (display2.init( 4, 13)) return true; // TimerCam } return false; } void setup(void) { Serial.begin(115200); display.init(); display.setEpdMode(lgfx::epd_mode_t::epd_fastest); initCRCtable(); display.println("search UnitLCD."); Serial.println("search UnitLCD."); while (!searchUnitLCD()) { delay(100); } } void loop(void) { auto panel = (lgfx::Panel_M5UnitLCD*)display2.getPanel(); auto bus = (lgfx::Bus_I2C*) panel->getBus(); auto cfg = bus->config(); std::uint8_t buf[4] = { 0x04 }; if (lgfx::i2c::beginTransaction(cfg.i2c_port, cfg.i2c_addr, 400000).has_value() && lgfx::i2c::writeBytes(cfg.i2c_port, buf, 1).has_value() && lgfx::i2c::restart(cfg.i2c_port, cfg.i2c_addr, 400000, true).has_value() && lgfx::i2c::readBytes(cfg.i2c_port, buf, 4).has_value() && lgfx::i2c::endTransaction(cfg.i2c_port).has_value()) { Serial.printf("found : device_id : %02x %02x %02x %02x\r\n", buf[0], buf[1], buf[2], buf[3]); if (buf[0] == DEVICE_ID_0 && buf[1] == DEVICE_ID_1) { if (buf[2] != MAJOR_VER || buf[3] != MINOR_VER) { display.startWrite(); if (update()) { display.drawString("success", 0, 56); Serial.println("success"); } else { display.drawString("fail", 0, 56); Serial.println("fail"); } display.endWrite(); delay(8192); } else { Serial.println("updated."); delay(2048); } } } }