From df5bf2719ffd6b56434a767b0c9da93349430b3f Mon Sep 17 00:00:00 2001 From: Pirata Date: Fri, 8 Aug 2025 16:10:49 -0300 Subject: [PATCH 01/30] esp32c5 --- .vscode/settings.json | 16 +- boards/ESP32-C5/ESP32-C5.ini | 162 ++ boards/ESP32-C5/interface.cpp | 49 + boards/ESP32-C5/pins_arduino.h | 48 + boards/_boards_json/esp32-c5-devkitc-1.json | 36 + boards/pinouts/pins_arduino.h | 2 + lib/Bad_Usb_Lib/BleKeyboard.cpp | 43 +- lib/Bad_Usb_Lib/BleKeyboard.h | 21 +- lib/HAL/SD.h | 2 +- lib/HAL/sd_card/SD.cpp | 2 +- lib/HAL/sd_card/sd_diskio.cpp | 814 ++++++++++ lib/HAL/sd_card/sd_diskio_crc.c | 61 + lib/HAL/sd_diskio2.h | 34 + lib/TFT_eSPI/Processors/TFT_eSPI_ESP32.h | 886 ++++++----- lib/TFT_eSPI/Processors/TFT_eSPI_ESP32_C3.h | 902 ++++++----- lib/TFT_eSPI/TFT_eSPI.h | 1571 ++++++++++--------- patch-c5.py | 169 ++ platformio.ini | 3 +- src/core/configPins.cpp | 1 + src/core/connect/esp_connection.cpp | 10 + src/core/connect/esp_connection.h | 6 +- src/core/menu_items/NRF24.cpp | 2 + src/core/menu_items/OthersMenu.cpp | 2 + src/core/menu_items/RFIDMenu.cpp | 2 + src/core/menu_items/RFMenu.cpp | 2 + src/core/powerSave.cpp | 8 +- src/core/sd_functions.cpp | 66 +- src/core/serialcmds.cpp | 6 +- src/core/settings.cpp | 2 + src/core/startup_app.cpp | 2 + src/modules/bjs_interpreter/interpreter.cpp | 11 + src/modules/ble/ble_common.cpp | 21 +- src/modules/ble/ble_ninebot.cpp | 203 ++- src/modules/ble/ble_spam.cpp | 33 +- src/modules/ble/ble_spam.h | 7 +- src/modules/ethernet/ARPScanner.cpp | 6 +- src/modules/ethernet/EthernetHelper.cpp | 9 +- src/modules/others/openhaystack.cpp | 2 + src/modules/others/openhaystack.h | 6 - src/modules/rf/emit.cpp | 3 +- src/modules/rf/emit.h | 4 +- src/modules/rf/record.cpp | 3 +- src/modules/rf/rf_spectrum.cpp | 3 +- src/modules/rf/rf_utils.cpp | 8 +- src/modules/rf/rf_utils.h | 5 +- src/modules/rf/rf_waterfall.cpp | 2 + src/modules/rf/save.cpp | 2 + src/modules/rf/save.h | 4 +- src/modules/rf/structs.h | 3 +- src/modules/rfid/PN532KillerTools.cpp | 2 + src/modules/rfid/PN532KillerTools.h | 3 +- src/modules/rfid/amiibo.cpp | 3 +- src/modules/rfid/amiibo.h | 3 +- src/modules/rfid/chameleon.cpp | 3 +- src/modules/rfid/chameleon.h | 3 +- src/modules/rfid/pn532ble.cpp | 2 + src/modules/rfid/pn532ble.h | 3 +- src/modules/wifi/clients.cpp | 8 + 58 files changed, 3562 insertions(+), 1733 deletions(-) create mode 100644 boards/ESP32-C5/ESP32-C5.ini create mode 100644 boards/ESP32-C5/interface.cpp create mode 100644 boards/ESP32-C5/pins_arduino.h create mode 100644 boards/_boards_json/esp32-c5-devkitc-1.json create mode 100644 lib/HAL/sd_card/sd_diskio.cpp create mode 100644 lib/HAL/sd_card/sd_diskio_crc.c create mode 100644 lib/HAL/sd_diskio2.h create mode 100644 patch-c5.py diff --git a/.vscode/settings.json b/.vscode/settings.json index 8f25f9f13..2be0b0503 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -70,7 +70,21 @@ "*.tpp": "cpp", "condition_variable": "cpp", "mutex": "cpp", - "thread": "cpp" + "thread": "cpp", + "bit": "cpp", + "charconv": "cpp", + "compare": "cpp", + "concepts": "cpp", + "netfwd": "cpp", + "source_location": "cpp", + "format": "cpp", + "numbers": "cpp", + "semaphore": "cpp", + "span": "cpp", + "stop_token": "cpp", + "text_encoding": "cpp", + "variant": "cpp", + "usb_serial_jtag.h": "c" }, "files.trimTrailingWhitespace": true, "files.insertFinalNewline": true, diff --git a/boards/ESP32-C5/ESP32-C5.ini b/boards/ESP32-C5/ESP32-C5.ini new file mode 100644 index 000000000..fa32d73df --- /dev/null +++ b/boards/ESP32-C5/ESP32-C5.ini @@ -0,0 +1,162 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:esp32-c5] +board = esp32-c5-devkitc-1 +board_build.partitions = custom_8Mb.csv +board_build.f_flash = 40000000L +board_upload.flash_size = 8MB +board_upload.maximum_size = 8388608 +platform = https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip +platform_packages = + framework-arduinoespressif32-libs @ https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-idf-release_v5.5-b66b5448-v1.zip +extra_scripts = + pre:patch-c5.py + pre:pre_build_current_year.py + post:build.py +build_src_filter =${env.build_src_filter} +<../boards/ESP32-C5> +build_flags = + ${env.build_flags} + -Iboards/ESP32-C5 + -DBAD_RX=3 + -DBAD_TX=4 + ; grove pins + ; defaults from https://github.com/espressif/arduino-esp32/blob/master/variants/esp32s3/pins_arduino.h + -DGROVE_SDA=1 ; default RF TX pin + -DGROVE_SCL=2 ; default IR/RF RX pin + ;-DALLOW_ALL_GPIO_FOR_IR_RF=1 ; Set this option to make use of all GPIOs, from 1 to 44 to be chosen, except TFT and SD pins + + ; ir led pin + -DIR_TX_PINS='{{"Grove W", GROVE_SCL}, {"Grove Y", GROVE_SDA}}' + -DIR_RX_PINS='{{"Grove W", GROVE_SCL}, {"Grove Y", GROVE_SDA}}' + -DLED=40 + -DLED_ON=HIGH + -DLED_OFF=LOW + + ;Radio Frequency (one pin modules) pin setting + -DRF_TX_PINS='{{"Grove W", GROVE_SCL}, {"Grove Y", GROVE_SDA}}' + -DRF_RX_PINS='{{"Grove W", GROVE_SCL}, {"Grove Y", GROVE_SDA}}' + + ; sd card pins + ; suggested https://github.com/espressif/esp-idf/tree/master/examples/storage/sd_card/sdmmc + -DSDCARD_CS=-1 + -DSDCARD_SCK=-1 + -DSDCARD_MISO=-1 + -DSDCARD_MOSI=-1 + + ; tft vars + -DROTATION=1 + -DBACKLIGHT=-1 ; tft backlight pin + -DMINBRIGHT=160 ; unused? + -DUSER_SETUP_LOADED=1 ; dont redefine pins + -DILI9341_DRIVER=1 ; example only + -DSMOOTH_FONT=1 + -DTFT_DISPON=0x29 + -DTFT_DISPOFF=0x28 + -DTFT_CS=-1 + -DTFT_DC=-1 + -DTFT_RST=-1 + -DTOUCH_CS=-1 + -DTFT_MOSI=-1 + -DTFT_SCLK=-1 + -DTFT_BL=-1 + ; text sizes + -DFP=1 + -DFM=2 + -DFG=3 + ; ui control buttons + ;-DSEL_BTN=1 + ;-DUP_BTN=2 ; also work as ESC + ;-DDW_BTN=3 ; also work as NEXT + -DBTN_ALIAS='"OK"' + + ;FM Radio + ;-DFM_SI4713=1 ;Uncomment to activate FM Radio using Adafruit Si4713 + -DFM_RSTPIN=12 + ;Microphone + ;-DMIC_SPM1423=1 ; uncomment to enable Applicable for SPM1423 device + ;-DPIN_CLK=-1 + ;-DI2S_SCLK_PIN=-1 + ;-DI2S_DATA_PIN=-1 + ;-DPIN_DATA=-1 + + ;CC1101 SPI connection pins + ; best connection pins for higher speed https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/peripherals/spi_master.html#gpio-matrix-and-io-mux + -DUSE_CC1101_VIA_SPI + -DCC1101_GDO0_PIN=5 + -DCC1101_SS_PIN=SPI_SS_PIN + -DCC1101_MOSI_PIN=SPI_MOSI_PIN + -DCC1101_SCK_PIN=SPI_SCK_PIN + -DCC1101_MISO_PIN=SPI_MISO_PIN + ;-DCC1101_GDO2_PIN=14 ; optional + + ; connections are the same as CC1101 + ;-DUSE_NRF24_VIA_SPI + ;-DNRF24_CE_PIN=5 + ;-DNRF24_SS_PIN=SPI_SS_PIN ; chip select + ;-DNRF24_MOSI_PIN=SPI_MOSI_PIN + ;-DNRF24_SCK_PIN=SPI_SCK_PIN + ;-DNRF24_MISO_PIN=SPI_MISO_PIN + + -DSPI_SCK_PIN=10 + -DSPI_MOSI_PIN=8 + -DSPI_MISO_PIN=9 + -DSPI_SS_PIN=6 + + ; connections are the same as W5500 + ; -DUSE_W5500_VIA_SPI + ; -DW5500_SS_PIN=SPI_SS_PIN + ; -DW5500_MOSI_PIN=SPI_MOSI_PIN + ; -DW5500_SCK_PIN=SPI_SCK_PIN + ; -DW5500_MISO_PIN=SPI_MISO_PIN + ; -DW5500_INT_PIN=5 + -DDEVICE_NAME='"ESP32-C5"' + +lib_deps = + https://github.com/sayacom/WireGuard-ESP32-Arduino#feature/esp-netif + ;WireGuard-ESP32 + https://github.com/BorisKofman/IRremoteESP8266#Espressif-version-3 + ;IRremoteESP8266 + Time + LibSSH-ESP32 + https://github.com/bmorcelli/ESPping + Adafruit BusIO=https://github.com/emericklaw/Adafruit-BusIO_Bruce + Adafruit PN532=https://github.com/emericklaw/Adafruit-PN532_Bruce + https://github.com/rennancockles/Arduino_MFRC522v2.git + ;https://github.com/rennancockles/ESP-ChameleonUltra + ;https://github.com/rennancockles/ESP-Amiibolink + https://github.com/rennancockles/SimpleCLI + ;https://github.com/whywilson/ESP-PN532BLE + https://github.com/whywilson/ESP-PN532-UART@^0.0.2 + ;https://github.com/whywilson/ESP-PN532Killer@^0.0.4 + NTPClient + Timezone + ESP32Time + bblanchon/ArduinoJson + https://github.com/bmorcelli/rc-switch + ESP8266Audio + FFat + earlephilhower/ESP8266SAM@^1.0.1 + mikalhart/TinyGPSPlus + tinyu-zhao/FFT@^0.0.1 + h2zero/NimBLE-Arduino@^2.3.3 + nrf24/RF24 @ 1.4.11 + Adafruit Si4713 Library@1.2.3 + Bodmer/JPEGDecoder + https://github.com/bmorcelli/SmartRC-CC1101-Driver-Lib/ + ducktape=https://github.com/bmorcelli/duktape/releases/download/2.7.0-lite/duktape-2.7.0.zip + ;paulstoffregen/OneWire@^2.3.8 + https://github.com/bmorcelli/OneWire#patch-1 + ;jackjansen/esp32_idf5_https_server_compat + tobozo/ESP32-PSRamFS + ;chegewara/EspTinyUSB + bitbank2/AnimatedGIF + bitbank2/PNGdec @ ^1.1.2 + ESP32Async/ESPAsyncWebServer diff --git a/boards/ESP32-C5/interface.cpp b/boards/ESP32-C5/interface.cpp new file mode 100644 index 000000000..7f6c4f721 --- /dev/null +++ b/boards/ESP32-C5/interface.cpp @@ -0,0 +1,49 @@ +#include "core/powerSave.h" +#include + +/*************************************************************************************** +** Function name: _setup_gpio() +** Location: main.cpp +** Description: initial setup for the device +***************************************************************************************/ +void _setup_gpio() {} + +/*************************************************************************************** +** Function name: getBattery() +** location: display.cpp +** Description: Delivers the battery value from 1-100 +***************************************************************************************/ +int getBattery() { return 0; } + +/*************************************************************************************** +** Function name: isCharging() +** Description: Default implementation that returns false +***************************************************************************************/ +bool isCharging() { return false; } + +/********************************************************************* +** Function: setBrightness +** location: settings.cpp +** set brightness value +**********************************************************************/ +void _setBrightness(uint8_t brightval) {} + +/********************************************************************* +** Function: InputHandler +** Handles the variables PrevPress, NextPress, SelPress, AnyKeyPress and EscPress +**********************************************************************/ +void InputHandler(void) {} + +/********************************************************************* +** Function: powerOff +** location: mykeyboard.cpp +** Turns off the device (or try to) +**********************************************************************/ +void powerOff() {} + +/********************************************************************* +** Function: checkReboot +** location: mykeyboard.cpp +** Btn logic to turnoff the device (name is odd btw) +**********************************************************************/ +void checkReboot() {} diff --git a/boards/ESP32-C5/pins_arduino.h b/boards/ESP32-C5/pins_arduino.h new file mode 100644 index 000000000..1d4c731cf --- /dev/null +++ b/boards/ESP32-C5/pins_arduino.h @@ -0,0 +1,48 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include "soc/soc_caps.h" +#include + +#define PIN_RGB_LED 27 +// BUILTIN_LED can be used in new Arduino API digitalWrite() like in Blink.ino +static const uint8_t LED_BUILTIN = SOC_GPIO_PIN_COUNT + PIN_RGB_LED; +#define BUILTIN_LED LED_BUILTIN // backward compatibility +#define LED_BUILTIN LED_BUILTIN // allow testing #ifdef LED_BUILTIN +// RGB_BUILTIN and RGB_BRIGHTNESS can be used in new Arduino API rgbLedWrite() +#define RGB_BUILTIN LED_BUILTIN +#define RGB_BRIGHTNESS 64 + +static const uint8_t TX = 11; +static const uint8_t RX = 12; + +// static const uint8_t USB_DM = 13; +// static const uint8_t USB_DP = 14; + +static const uint8_t SDA = 0; +static const uint8_t SCL = 1; + +static const uint8_t SS = 6; +static const uint8_t MOSI = 8; +static const uint8_t MISO = 9; +static const uint8_t SCK = 10; + +static const uint8_t A0 = 1; +static const uint8_t A1 = 2; +static const uint8_t A2 = 3; +static const uint8_t A3 = 4; +static const uint8_t A4 = 5; +static const uint8_t A5 = 6; + +// LP I2C Pins are fixed on ESP32-C5 +static const uint8_t LP_SDA = 2; +static const uint8_t LP_SCL = 3; +#define WIRE1_PIN_DEFINED +#define SDA1 LP_SDA +#define SCL1 LP_SCL + +// LP UART Pins are fixed on ESP32-C5 +static const uint8_t LP_RX = 4; +static const uint8_t LP_TX = 5; + +#endif /* Pins_Arduino_h */ diff --git a/boards/_boards_json/esp32-c5-devkitc-1.json b/boards/_boards_json/esp32-c5-devkitc-1.json new file mode 100644 index 000000000..6e637b2a7 --- /dev/null +++ b/boards/_boards_json/esp32-c5-devkitc-1.json @@ -0,0 +1,36 @@ +{ + "build": { + "core": "esp32", + "extra_flags": [ + "-DESP32C5_DEVKITC_1", + "-DBOARD_HAS_PSRAM", + "-DESP32C5" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "mcu": "esp32c5", + "variant": "pinouts" + }, + "connectivity": [ + "bluetooth", + "wifi" + ], + "debug": { + "openocd_target": "esp32c5.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "Espressif ESP32-C5-DevKitC-1 4MB no PSRAM", + "upload": { + "flash_size": "4MB", + "maximum_ram_size": 327680, + "maximum_size": 4194304, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32c5/esp32-c5-devkitc-1/user_guide.html", + "vendor": "Espressif" +} diff --git a/boards/pinouts/pins_arduino.h b/boards/pinouts/pins_arduino.h index 56dde08a1..91309dce6 100644 --- a/boards/pinouts/pins_arduino.h +++ b/boards/pinouts/pins_arduino.h @@ -38,4 +38,6 @@ #include "../marauder-mini/pins_arduino.h" #elif LILYGO_T_HMI #include "../lilygo-t-hmi/pins_arduino.h" +#elif ESP32C5_DEVKITC_1 +#include "../ESP32-C5/pins_arduino.h" #endif diff --git a/lib/Bad_Usb_Lib/BleKeyboard.cpp b/lib/Bad_Usb_Lib/BleKeyboard.cpp index d6ffb2869..9a6afb70e 100644 --- a/lib/Bad_Usb_Lib/BleKeyboard.cpp +++ b/lib/Bad_Usb_Lib/BleKeyboard.cpp @@ -173,18 +173,29 @@ void BleKeyboard::begin(const uint8_t *layout, uint16_t showAs) { pServer->setCallbacks(this); hid = new BLEHIDDevice(pServer); +#ifdef ESP32C5 + inputKeyboard = hid->getInputReport(KEYBOARD_ID); // <-- input REPORTID from report map + outputKeyboard = hid->getOutputReport(KEYBOARD_ID); + inputMediaKeys = hid->getInputReport(MEDIA_KEYS_ID); +#else inputKeyboard = hid->inputReport(KEYBOARD_ID); // <-- input REPORTID from report map outputKeyboard = hid->outputReport(KEYBOARD_ID); inputMediaKeys = hid->inputReport(MEDIA_KEYS_ID); +#endif inputKeyboard->setCallbacks(this); outputKeyboard->setCallbacks(this); inputMediaKeys->setCallbacks(this); +#ifdef ESP32C5 + hid->setManufacturer("Espressif"); + hid->setPnp(0x02, vid, pid, version); + hid->setHidInfo(0x00, 0x01); +#else hid->manufacturer()->setValue(deviceManufacturer); - hid->pnp(0x02, vid, pid, version); hid->hidInfo(0x00, 0x01); +#endif #if defined(USE_NIMBLE) @@ -197,7 +208,11 @@ void BleKeyboard::begin(const uint8_t *layout, uint16_t showAs) { #endif // USE_NIMBLE +#ifdef ESP32C5 + hid->setReportMap((uint8_t *)_hidReportDescriptor, sizeof(_hidReportDescriptor)); +#else hid->reportMap((uint8_t *)_hidReportDescriptor, sizeof(_hidReportDescriptor)); +#endif hid->startServices(); onStarted(pServer); @@ -207,9 +222,19 @@ void BleKeyboard::begin(const uint8_t *layout, uint16_t showAs) { if (_randUUID) { // this workaround makes 2 Bruce connect and work on the same Android device advertising->addServiceUUID(BLEUUID((uint16_t)(ESP.getEfuseMac() & 0xFFFF))); } else { +#ifdef ESP32C5 + advertising->addServiceUUID(hid->getHidService()->getUUID()); + +#else advertising->addServiceUUID(hid->hidService()->getUUID()); +#endif } +#ifdef ESP32C5 + advertising->enableScanResponse(false); +#else advertising->setScanResponse(false); + +#endif advertising->start(); hid->setBatteryLevel(batteryLevel); } @@ -250,8 +275,12 @@ void BleKeyboard::set_product_id(uint16_t pid) { this->pid = pid; } void BleKeyboard::set_version(uint16_t version) { this->version = version; } void BleKeyboard::sendReport(KeyReport *keys) { - // if (this->isConnected()) - if (this->isConnected() && this->inputKeyboard->getSubscribedCount() > 0) { +#ifdef ESP32C5 + if (this->isConnected()) +#else + if (this->isConnected() && this->inputKeyboard->getSubscribedCount() > 0) +#endif + { this->inputKeyboard->setValue((uint8_t *)keys, sizeof(KeyReport)); this->inputKeyboard->notify(); #if defined(USE_NIMBLE) @@ -262,8 +291,12 @@ void BleKeyboard::sendReport(KeyReport *keys) { } void BleKeyboard::sendReport(MediaKeyReport *keys) { - // if (this->isConnected()) - if (this->isConnected() && this->inputKeyboard->getSubscribedCount() > 0) { +#ifdef ESP32C5 + if (this->isConnected()) +#else + if (this->isConnected() && this->inputKeyboard->getSubscribedCount() > 0) +#endif + { this->inputMediaKeys->setValue((uint8_t *)keys, sizeof(MediaKeyReport)); this->inputMediaKeys->notify(); #if defined(USE_NIMBLE) diff --git a/lib/Bad_Usb_Lib/BleKeyboard.h b/lib/Bad_Usb_Lib/BleKeyboard.h index 1df53f004..18b79d331 100644 --- a/lib/Bad_Usb_Lib/BleKeyboard.h +++ b/lib/Bad_Usb_Lib/BleKeyboard.h @@ -10,7 +10,10 @@ #include "NimBLECharacteristic.h" #include "NimBLEHIDDevice.h" - +#ifdef ESP32C5 +#include "NimBLEAdvertising.h" +#include "NimBLEServer.h" +#endif #define BLEDevice NimBLEDevice #define BLEServerCallbacks NimBLEServerCallbacks #define BLECharacteristicCallbacks NimBLECharacteristicCallbacks @@ -97,12 +100,18 @@ class BleKeyboard : public BLEServerCallbacks, public BLECharacteristicCallbacks protected: bool _randUUID = false; virtual void onStarted(BLEServer *pServer) {}; - virtual void onConnect(BLEServer *pServer) override; - virtual void onDisconnect(BLEServer *pServer) override; virtual void onAuthenticationComplete(ble_gap_conn_desc *desc); - virtual void onWrite(BLECharacteristic *me) override; - virtual void - onSubscribe(NimBLECharacteristic *pCharacteristic, ble_gap_conn_desc *desc, uint16_t subValue) override; +#ifndef ESP32C5 +#define OVERRIDE_BLE override +#else +#define OVERRIDE_BLE +#endif + virtual void onConnect(BLEServer *pServer) OVERRIDE_BLE; + virtual void onDisconnect(BLEServer *pServer) OVERRIDE_BLE; + virtual void onWrite(BLECharacteristic *me) OVERRIDE_BLE; + virtual void onSubscribe( + NimBLECharacteristic *pCharacteristic, ble_gap_conn_desc *desc, uint16_t subValue + ) OVERRIDE_BLE; }; #endif // CONFIG_BT_ENABLED diff --git a/lib/HAL/SD.h b/lib/HAL/SD.h index ff0ffd52f..3f9b8105e 100644 --- a/lib/HAL/SD.h +++ b/lib/HAL/SD.h @@ -23,7 +23,7 @@ #else #include -#include +#include #endif namespace fs { diff --git a/lib/HAL/sd_card/SD.cpp b/lib/HAL/sd_card/SD.cpp index e6dcf2946..2a9b3d00d 100644 --- a/lib/HAL/sd_card/SD.cpp +++ b/lib/HAL/sd_card/SD.cpp @@ -16,7 +16,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "sd_diskio.h" +#include "sd_diskio2.h" #include "vfs_api.h" using namespace fs; diff --git a/lib/HAL/sd_card/sd_diskio.cpp b/lib/HAL/sd_card/sd_diskio.cpp new file mode 100644 index 000000000..1330e070d --- /dev/null +++ b/lib/HAL/sd_card/sd_diskio.cpp @@ -0,0 +1,814 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Disable the automatic pin remapping of the API calls in this file +#define ARDUINO_CORE_BUILD + +#include "sd_diskio2.h" +#include "esp_system.h" +#include "esp32-hal-periman.h" + +extern "C" { +#include "ff.h" +#include "diskio.h" +#if ESP_IDF_VERSION_MAJOR > 3 +#include "diskio_impl.h" +#endif +//#include "esp_vfs.h" +#include "esp_vfs_fat.h" +char CRC7(const char *data, int length); +unsigned short CRC16(const char *data, int length); +} + +typedef enum { + GO_IDLE_STATE = 0, + SEND_OP_COND = 1, + SEND_CID = 2, + SEND_RELATIVE_ADDR = 3, + SEND_SWITCH_FUNC = 6, + SEND_IF_COND = 8, + SEND_CSD = 9, + STOP_TRANSMISSION = 12, + SEND_STATUS = 13, + SET_BLOCKLEN = 16, + READ_BLOCK_SINGLE = 17, + READ_BLOCK_MULTIPLE = 18, + SEND_NUM_WR_BLOCKS = 22, + SET_WR_BLK_ERASE_COUNT = 23, + WRITE_BLOCK_SINGLE = 24, + WRITE_BLOCK_MULTIPLE = 25, + APP_OP_COND = 41, + APP_CLR_CARD_DETECT = 42, + APP_CMD = 55, + READ_OCR = 58, + CRC_ON_OFF = 59 +} ardu_sdcard_command_t; + +typedef struct { + uint8_t ssPin; + SPIClass *spi; + int frequency; + char *base_path; + sdcard_type_t type; + unsigned long sectors; + bool supports_crc; + int status; +} ardu_sdcard_t; + +static ardu_sdcard_t *s_cards[FF_VOLUMES] = {NULL}; + +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR +const char *fferr2str[] = { + "(0) Succeeded", + "(1) A hard error occurred in the low level disk I/O layer", + "(2) Assertion failed", + "(3) The physical drive cannot work", + "(4) Could not find the file", + "(5) Could not find the path", + "(6) The path name format is invalid", + "(7) Access denied due to prohibited access or directory full", + "(8) Access denied due to prohibited access", + "(9) The file/directory object is invalid", + "(10) The physical drive is write protected", + "(11) The logical drive number is invalid", + "(12) The volume has no work area", + "(13) There is no valid FAT volume", + "(14) The f_mkfs() aborted due to any problem", + "(15) Could not get a grant to access the volume within defined period", + "(16) The operation is rejected according to the file sharing policy", + "(17) LFN working buffer could not be allocated", + "(18) Number of open files > FF_FS_LOCK", + "(19) Given parameter is invalid" +}; +#endif + +/* + * SD SPI + * */ + +bool sdWait(uint8_t pdrv, int timeout) { + char resp; + uint32_t start = millis(); + + do { + resp = s_cards[pdrv]->spi->transfer(0xFF); + } while (resp == 0x00 && (millis() - start) < (unsigned int)timeout); + + if (!resp) { + log_w("Wait Failed"); + } + return (resp > 0x00); +} + +void sdStop(uint8_t pdrv) { + s_cards[pdrv]->spi->write(0xFD); +} + +void sdDeselectCard(uint8_t pdrv) { + ardu_sdcard_t *card = s_cards[pdrv]; + digitalWrite(card->ssPin, HIGH); +} + +bool sdSelectCard(uint8_t pdrv) { + ardu_sdcard_t *card = s_cards[pdrv]; + digitalWrite(card->ssPin, LOW); + bool s = sdWait(pdrv, 500); + if (!s) { + log_e("Select Failed"); + digitalWrite(card->ssPin, HIGH); + return false; + } + return true; +} + +char sdCommand(uint8_t pdrv, char cmd, unsigned int arg, unsigned int *resp) { + char token; + ardu_sdcard_t *card = s_cards[pdrv]; + + for (int f = 0; f < 3; f++) { + if (cmd == SEND_NUM_WR_BLOCKS || cmd == SET_WR_BLK_ERASE_COUNT || cmd == APP_OP_COND || cmd == APP_CLR_CARD_DETECT) { + token = sdCommand(pdrv, APP_CMD, 0, NULL); + sdDeselectCard(pdrv); + if (token > 1) { + break; + } + if (!sdSelectCard(pdrv)) { + token = 0xFF; + break; + } + } + + char cmdPacket[7]; + cmdPacket[0] = cmd | 0x40; + cmdPacket[1] = arg >> 24; + cmdPacket[2] = arg >> 16; + cmdPacket[3] = arg >> 8; + cmdPacket[4] = arg; + if (card->supports_crc || cmd == GO_IDLE_STATE || cmd == SEND_IF_COND) { + cmdPacket[5] = (CRC7(cmdPacket, 5) << 1) | 0x01; + } else { + cmdPacket[5] = 0x01; + } + cmdPacket[6] = 0xFF; + + card->spi->writeBytes((uint8_t *)cmdPacket, (cmd == STOP_TRANSMISSION) ? 7 : 6); + + for (int i = 0; i < 9; i++) { + token = card->spi->transfer(0xFF); + if (!(token & 0x80)) { + break; + } + } + + if (token == 0xFF) { + log_w("no token received"); + sdDeselectCard(pdrv); + delay(100); + sdSelectCard(pdrv); + continue; + } else if (token & 0x08) { + log_w("crc error"); + sdDeselectCard(pdrv); + delay(100); + sdSelectCard(pdrv); + continue; + } else if (token > 1) { + log_w("token error [%u] 0x%x", cmd, token); + break; + } + + if (cmd == SEND_STATUS && resp) { + *resp = card->spi->transfer(0xFF); + } else if ((cmd == SEND_IF_COND || cmd == READ_OCR) && resp) { + *resp = card->spi->transfer32(0xFFFFFFFF); + } + + break; + } + if (token == 0xFF) { + log_e("Card Failed! cmd: 0x%02x", cmd); + card->status = STA_NOINIT; + } + return token; +} + +bool sdReadBytes(uint8_t pdrv, char *buffer, int length) { + char token; + unsigned short crc; + ardu_sdcard_t *card = s_cards[pdrv]; + + uint32_t start = millis(); + do { + token = card->spi->transfer(0xFF); + } while (token == 0xFF && (millis() - start) < 500); + + if (token != 0xFE) { + return false; + } + + card->spi->transferBytes(NULL, (uint8_t *)buffer, length); + crc = card->spi->transfer16(0xFFFF); + return (!card->supports_crc || crc == CRC16(buffer, length)); +} + +char sdWriteBytes(uint8_t pdrv, const char *buffer, char token) { + ardu_sdcard_t *card = s_cards[pdrv]; + unsigned short crc = (card->supports_crc) ? CRC16(buffer, 512) : 0xFFFF; + if (!sdWait(pdrv, 500)) { + return 0; + } + + card->spi->write(token); + card->spi->writeBytes((uint8_t *)buffer, 512); + card->spi->write16(crc); + return (card->spi->transfer(0xFF) & 0x1F); +} + +/* + * SPI SDCARD Communication + * */ + +char sdTransaction(uint8_t pdrv, char cmd, unsigned int arg, unsigned int *resp) { + if (!sdSelectCard(pdrv)) { + return 0xFF; + } + char token = sdCommand(pdrv, cmd, arg, resp); + sdDeselectCard(pdrv); + return token; +} + +bool sdReadSector(uint8_t pdrv, char *buffer, unsigned long long sector) { + for (int f = 0; f < 3; f++) { + if (!sdSelectCard(pdrv)) { + return false; + } + if (!sdCommand(pdrv, READ_BLOCK_SINGLE, (s_cards[pdrv]->type == CARD_SDHC) ? sector : sector << 9, NULL)) { + bool success = sdReadBytes(pdrv, buffer, 512); + sdDeselectCard(pdrv); + if (success) { + return true; + } + } else { + break; + } + } + sdDeselectCard(pdrv); + return false; +} + +bool sdReadSectors(uint8_t pdrv, char *buffer, unsigned long long sector, int count) { + for (int f = 0; f < 3;) { + if (!sdSelectCard(pdrv)) { + return false; + } + + if (!sdCommand(pdrv, READ_BLOCK_MULTIPLE, (s_cards[pdrv]->type == CARD_SDHC) ? sector : sector << 9, NULL)) { + do { + if (!sdReadBytes(pdrv, buffer, 512)) { + f++; + break; + } + + sector++; + buffer += 512; + f = 0; + } while (--count); + + if (sdCommand(pdrv, STOP_TRANSMISSION, 0, NULL)) { + log_e("command failed"); + break; + } + + sdDeselectCard(pdrv); + if (count == 0) { + return true; + } + } else { + break; + } + } + sdDeselectCard(pdrv); + return false; +} + +bool sdWriteSector(uint8_t pdrv, const char *buffer, unsigned long long sector) { + for (int f = 0; f < 3; f++) { + if (!sdSelectCard(pdrv)) { + return false; + } + if (!sdCommand(pdrv, WRITE_BLOCK_SINGLE, (s_cards[pdrv]->type == CARD_SDHC) ? sector : sector << 9, NULL)) { + char token = sdWriteBytes(pdrv, buffer, 0xFE); + sdDeselectCard(pdrv); + + if (token == 0x0A) { + continue; + } else if (token == 0x0C) { + return false; + } + + unsigned int resp; + if (sdTransaction(pdrv, SEND_STATUS, 0, &resp) || resp) { + return false; + } + return true; + } else { + break; + } + } + sdDeselectCard(pdrv); + return false; +} + +bool sdWriteSectors(uint8_t pdrv, const char *buffer, unsigned long long sector, int count) { + char token; + const char *currentBuffer = buffer; + unsigned long long currentSector = sector; + int currentCount = count; + ardu_sdcard_t *card = s_cards[pdrv]; + + for (int f = 0; f < 3;) { + if (card->type != CARD_MMC) { + if (sdTransaction(pdrv, SET_WR_BLK_ERASE_COUNT, currentCount, NULL)) { + return false; + } + } + + if (!sdSelectCard(pdrv)) { + return false; + } + + if (!sdCommand(pdrv, WRITE_BLOCK_MULTIPLE, (card->type == CARD_SDHC) ? currentSector : currentSector << 9, NULL)) { + do { + token = sdWriteBytes(pdrv, currentBuffer, 0xFC); + if (token != 0x05) { + f++; + break; + } + currentBuffer += 512; + f = 0; + } while (--currentCount); + + if (!sdWait(pdrv, 500)) { + break; + } + + if (currentCount == 0) { + sdStop(pdrv); + sdDeselectCard(pdrv); + + unsigned int resp; + if (sdTransaction(pdrv, SEND_STATUS, 0, &resp) || resp) { + return false; + } + return true; + } else { + if (sdCommand(pdrv, STOP_TRANSMISSION, 0, NULL)) { + break; + } + + if (token == 0x0A) { + sdDeselectCard(pdrv); + unsigned int writtenBlocks = 0; + if (card->type != CARD_MMC && sdSelectCard(pdrv)) { + if (!sdCommand(pdrv, SEND_NUM_WR_BLOCKS, 0, NULL)) { + char acmdData[4]; + if (sdReadBytes(pdrv, acmdData, 4)) { + writtenBlocks = acmdData[0] << 24; + writtenBlocks |= acmdData[1] << 16; + writtenBlocks |= acmdData[2] << 8; + writtenBlocks |= acmdData[3]; + } + } + sdDeselectCard(pdrv); + } + currentBuffer = buffer + (writtenBlocks << 9); + currentSector = sector + writtenBlocks; + currentCount = count - writtenBlocks; + continue; + } else { + break; + } + } + } else { + break; + } + } + sdDeselectCard(pdrv); + return false; +} + +unsigned long sdGetSectorsCount(uint8_t pdrv) { + for (int f = 0; f < 3; f++) { + if (!sdSelectCard(pdrv)) { + return 0; + } + + if (!sdCommand(pdrv, SEND_CSD, 0, NULL)) { + char csd[16]; + bool success = sdReadBytes(pdrv, csd, 16); + sdDeselectCard(pdrv); + if (success) { + if ((csd[0] >> 6) == 0x01) { + unsigned long size = (((unsigned long)(csd[7] & 0x3F) << 16) | ((unsigned long)csd[8] << 8) | csd[9]) + 1; + return size << 10; + } + unsigned long size = (((unsigned long)(csd[6] & 0x03) << 10) | ((unsigned long)csd[7] << 2) | ((csd[8] & 0xC0) >> 6)) + 1; + size <<= ((((csd[9] & 0x03) << 1) | ((csd[10] & 0x80) >> 7)) + 2); + size <<= (csd[5] & 0x0F); + return size >> 9; + } + } else { + break; + } + } + + sdDeselectCard(pdrv); + return 0; +} + +namespace { + +struct AcquireSPI { + ardu_sdcard_t *card; + explicit AcquireSPI(ardu_sdcard_t *card) : card(card) { + card->spi->beginTransaction(SPISettings(card->frequency, MSBFIRST, SPI_MODE0)); + } + AcquireSPI(ardu_sdcard_t *card, int frequency) : card(card) { + card->spi->beginTransaction(SPISettings(frequency, MSBFIRST, SPI_MODE0)); + } + ~AcquireSPI() { + card->spi->endTransaction(); + } + +private: + AcquireSPI(AcquireSPI const &); + AcquireSPI &operator=(AcquireSPI const &); +}; + +} // namespace + +/* + * FATFS API + * */ + +DSTATUS ff_sd_initialize(uint8_t pdrv) { + char token; + unsigned int resp; + unsigned int start; + ardu_sdcard_t *card = s_cards[pdrv]; + + if (!(card->status & STA_NOINIT)) { + return card->status; + } + + AcquireSPI card_locked(card, 400000); + + digitalWrite(card->ssPin, HIGH); + for (uint8_t i = 0; i < 20; i++) { + card->spi->transfer(0XFF); + } + + // Fix mount issue - sdWait fail ignored before command GO_IDLE_STATE + digitalWrite(card->ssPin, LOW); + if (!sdWait(pdrv, 500)) { + log_w("sdWait fail ignored, card initialize continues"); + } + if (sdCommand(pdrv, GO_IDLE_STATE, 0, NULL) != 1) { + sdDeselectCard(pdrv); + log_w("GO_IDLE_STATE failed"); + goto unknown_card; + } + sdDeselectCard(pdrv); + + token = sdTransaction(pdrv, CRC_ON_OFF, 1, NULL); + if (token == 0x5) { + //old card maybe + card->supports_crc = false; + } else if (token != 1) { + log_w("CRC_ON_OFF failed: %u", token); + goto unknown_card; + } + + if (sdTransaction(pdrv, SEND_IF_COND, 0x1AA, &resp) == 1) { + if ((resp & 0xFFF) != 0x1AA) { + log_w("SEND_IF_COND failed: %03X", resp & 0xFFF); + goto unknown_card; + } + + if (sdTransaction(pdrv, READ_OCR, 0, &resp) != 1 || !(resp & (1 << 20))) { + log_w("READ_OCR failed: %X", resp); + goto unknown_card; + } + + start = millis(); + do { + token = sdTransaction(pdrv, APP_OP_COND, 0x40100000, NULL); + } while (token == 1 && (millis() - start) < 1000); + + if (token) { + log_w("APP_OP_COND failed: %u", token); + goto unknown_card; + } + + if (!sdTransaction(pdrv, READ_OCR, 0, &resp)) { + if (resp & (1 << 30)) { + card->type = CARD_SDHC; + } else { + card->type = CARD_SD; + } + } else { + log_w("READ_OCR failed: %X", resp); + goto unknown_card; + } + } else { + if (sdTransaction(pdrv, READ_OCR, 0, &resp) != 1 || !(resp & (1 << 20))) { + log_w("READ_OCR failed: %X", resp); + goto unknown_card; + } + + start = millis(); + do { + token = sdTransaction(pdrv, APP_OP_COND, 0x100000, NULL); + } while (token == 0x01 && (millis() - start) < 1000); + + if (!token) { + card->type = CARD_SD; + } else { + start = millis(); + do { + token = sdTransaction(pdrv, SEND_OP_COND, 0x100000, NULL); + } while (token != 0x00 && (millis() - start) < 1000); + + if (token == 0x00) { + card->type = CARD_MMC; + } else { + log_w("SEND_OP_COND failed: %u", token); + goto unknown_card; + } + } + } + + if (card->type != CARD_MMC) { + if (sdTransaction(pdrv, APP_CLR_CARD_DETECT, 0, NULL)) { + log_w("APP_CLR_CARD_DETECT failed"); + goto unknown_card; + } + } + + if (card->type != CARD_SDHC) { + if (sdTransaction(pdrv, SET_BLOCKLEN, 512, NULL) != 0x00) { + log_w("SET_BLOCKLEN failed"); + goto unknown_card; + } + } + + card->sectors = sdGetSectorsCount(pdrv); + + if (card->frequency > 25000000) { + card->frequency = 25000000; + } + + card->status &= ~STA_NOINIT; + return card->status; + +unknown_card: + card->type = CARD_UNKNOWN; + return card->status; +} + +DSTATUS ff_sd_status(uint8_t pdrv) { + ardu_sdcard_t *card = s_cards[pdrv]; + AcquireSPI lock(card); + + if (sdTransaction(pdrv, SEND_STATUS, 0, NULL)) { + log_e("Check status failed"); + return STA_NOINIT; + } + return s_cards[pdrv]->status; +} + +DRESULT ff_sd_read(uint8_t pdrv, uint8_t *buffer, DWORD sector, UINT count) { + ardu_sdcard_t *card = s_cards[pdrv]; + if (card->status & STA_NOINIT) { + return RES_NOTRDY; + } + DRESULT res = RES_OK; + + AcquireSPI lock(card); + + if (count > 1) { + res = sdReadSectors(pdrv, (char *)buffer, sector, count) ? RES_OK : RES_ERROR; + } else { + res = sdReadSector(pdrv, (char *)buffer, sector) ? RES_OK : RES_ERROR; + } + return res; +} + +DRESULT ff_sd_write(uint8_t pdrv, const uint8_t *buffer, DWORD sector, UINT count) { + ardu_sdcard_t *card = s_cards[pdrv]; + if (card->status & STA_NOINIT) { + return RES_NOTRDY; + } + + if (card->status & STA_PROTECT) { + return RES_WRPRT; + } + DRESULT res = RES_OK; + + AcquireSPI lock(card); + + if (count > 1) { + res = sdWriteSectors(pdrv, (const char *)buffer, sector, count) ? RES_OK : RES_ERROR; + } else { + res = sdWriteSector(pdrv, (const char *)buffer, sector) ? RES_OK : RES_ERROR; + } + return res; +} + +DRESULT ff_sd_ioctl(uint8_t pdrv, uint8_t cmd, void *buff) { + switch (cmd) { + case CTRL_SYNC: + { + AcquireSPI lock(s_cards[pdrv]); + if (sdSelectCard(pdrv)) { + sdDeselectCard(pdrv); + return RES_OK; + } + } + return RES_ERROR; + case GET_SECTOR_COUNT: *((unsigned long *)buff) = s_cards[pdrv]->sectors; return RES_OK; + case GET_SECTOR_SIZE: *((WORD *)buff) = 512; return RES_OK; + case GET_BLOCK_SIZE: *((uint32_t *)buff) = 1; return RES_OK; + } + return RES_PARERR; +} + +bool sd_read_raw(uint8_t pdrv, uint8_t *buffer, DWORD sector) { + return ff_sd_read(pdrv, buffer, sector, 1) == ESP_OK; +} + +bool sd_write_raw(uint8_t pdrv, uint8_t *buffer, DWORD sector) { + return ff_sd_write(pdrv, buffer, sector, 1) == ESP_OK; +} + +/* + * Public methods + * */ + +uint8_t sdcard_uninit(uint8_t pdrv) { + ardu_sdcard_t *card = s_cards[pdrv]; + if (pdrv >= FF_VOLUMES || card == NULL) { + return 1; + } + { + AcquireSPI lock(card); + sdTransaction(pdrv, GO_IDLE_STATE, 0, NULL); + } // lock is destructed here + ff_diskio_register(pdrv, NULL); + s_cards[pdrv] = NULL; + esp_err_t err = ESP_OK; + if (card->base_path) { + err = esp_vfs_fat_unregister_path(card->base_path); + free(card->base_path); + } + free(card); + return err; +} + +uint8_t sdcard_init(uint8_t cs, SPIClass *spi, int hz) { + + uint8_t pdrv = 0xFF; + if (ff_diskio_get_drive(&pdrv) != ESP_OK || pdrv == 0xFF) { + return pdrv; + } + + ardu_sdcard_t *card = (ardu_sdcard_t *)malloc(sizeof(ardu_sdcard_t)); + if (!card) { + return 0xFF; + } + + card->base_path = NULL; + card->frequency = hz; + card->spi = spi; + card->ssPin = digitalPinToGPIONumber(cs); + + card->supports_crc = true; + card->type = CARD_NONE; + card->status = STA_NOINIT; + + pinMode(card->ssPin, OUTPUT); + digitalWrite(card->ssPin, HIGH); + perimanSetPinBusExtraType(card->ssPin, "SD_SS"); + + s_cards[pdrv] = card; + + static const ff_diskio_impl_t sd_impl = { + .init = &ff_sd_initialize, .status = &ff_sd_status, .read = &ff_sd_read, .write = &ff_sd_write, .ioctl = &ff_sd_ioctl + }; + ff_diskio_register(pdrv, &sd_impl); + + return pdrv; +} + +uint8_t sdcard_unmount(uint8_t pdrv) { + ardu_sdcard_t *card = s_cards[pdrv]; + if (pdrv >= FF_VOLUMES || card == NULL) { + return 1; + } + card->status |= STA_NOINIT; + card->type = CARD_NONE; + + char drv[3] = {(char)('0' + pdrv), ':', 0}; + f_mount(NULL, drv, 0); + return 0; +} + +bool sdcard_mount(uint8_t pdrv, const char *path, uint8_t max_files, bool format_if_empty) { + ardu_sdcard_t *card = s_cards[pdrv]; + if (pdrv >= FF_VOLUMES || card == NULL) { + return false; + } + + if (card->base_path) { + free(card->base_path); + } + card->base_path = strdup(path); + + FATFS *fs; + char drv[3] = {(char)('0' + pdrv), ':', 0}; + esp_err_t err = esp_vfs_fat_register(path, drv, max_files, &fs); + if (err == ESP_ERR_INVALID_STATE) { + log_e("esp_vfs_fat_register failed 0x(%x): SD is registered.", err); + return false; + } else if (err != ESP_OK) { + log_e("esp_vfs_fat_register failed 0x(%x)", err); + return false; + } + + FRESULT res = f_mount(fs, drv, 1); + if (res != FR_OK) { + log_e("f_mount failed: %s", fferr2str[res]); + if (res == 13 && format_if_empty) { + BYTE *work = (BYTE *)malloc(sizeof(BYTE) * FF_MAX_SS); + if (!work) { + log_e("alloc for f_mkfs failed"); + return false; + } + //FRESULT f_mkfs (const TCHAR* path, const MKFS_PARM* opt, void* work, UINT len); + const MKFS_PARM opt = {(BYTE)FM_ANY, 0, 0, 0, 0}; + res = f_mkfs(drv, &opt, work, sizeof(BYTE) * FF_MAX_SS); + free(work); + if (res != FR_OK) { + log_e("f_mkfs failed: %s", fferr2str[res]); + esp_vfs_fat_unregister_path(path); + return false; + } + res = f_mount(fs, drv, 1); + if (res != FR_OK) { + log_e("f_mount failed: %s", fferr2str[res]); + esp_vfs_fat_unregister_path(path); + return false; + } + } else { + esp_vfs_fat_unregister_path(path); + return false; + } + } + AcquireSPI lock(card); + card->sectors = sdGetSectorsCount(pdrv); + return true; +} + +uint32_t sdcard_num_sectors(uint8_t pdrv) { + ardu_sdcard_t *card = s_cards[pdrv]; + if (pdrv >= FF_VOLUMES || card == NULL) { + return 0; + } + return card->sectors; +} + +uint32_t sdcard_sector_size(uint8_t pdrv) { + if (pdrv >= FF_VOLUMES || s_cards[pdrv] == NULL) { + return 0; + } + return 512; +} + +sdcard_type_t sdcard_type(uint8_t pdrv) { + ardu_sdcard_t *card = s_cards[pdrv]; + if (pdrv >= FF_VOLUMES || card == NULL) { + return CARD_NONE; + } + return card->type; +} diff --git a/lib/HAL/sd_card/sd_diskio_crc.c b/lib/HAL/sd_card/sd_diskio_crc.c new file mode 100644 index 000000000..9c839a583 --- /dev/null +++ b/lib/HAL/sd_card/sd_diskio_crc.c @@ -0,0 +1,61 @@ +/* SD/MMC File System Library + * Copyright (c) 2014 Neil Thiessen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const char m_CRC7Table[] = {0x00, 0x09, 0x12, 0x1B, 0x24, 0x2D, 0x36, 0x3F, 0x48, 0x41, 0x5A, 0x53, 0x6C, 0x65, 0x7E, 0x77, 0x19, 0x10, 0x0B, 0x02, 0x3D, 0x34, + 0x2F, 0x26, 0x51, 0x58, 0x43, 0x4A, 0x75, 0x7C, 0x67, 0x6E, 0x32, 0x3B, 0x20, 0x29, 0x16, 0x1F, 0x04, 0x0D, 0x7A, 0x73, 0x68, 0x61, + 0x5E, 0x57, 0x4C, 0x45, 0x2B, 0x22, 0x39, 0x30, 0x0F, 0x06, 0x1D, 0x14, 0x63, 0x6A, 0x71, 0x78, 0x47, 0x4E, 0x55, 0x5C, 0x64, 0x6D, + 0x76, 0x7F, 0x40, 0x49, 0x52, 0x5B, 0x2C, 0x25, 0x3E, 0x37, 0x08, 0x01, 0x1A, 0x13, 0x7D, 0x74, 0x6F, 0x66, 0x59, 0x50, 0x4B, 0x42, + 0x35, 0x3C, 0x27, 0x2E, 0x11, 0x18, 0x03, 0x0A, 0x56, 0x5F, 0x44, 0x4D, 0x72, 0x7B, 0x60, 0x69, 0x1E, 0x17, 0x0C, 0x05, 0x3A, 0x33, + 0x28, 0x21, 0x4F, 0x46, 0x5D, 0x54, 0x6B, 0x62, 0x79, 0x70, 0x07, 0x0E, 0x15, 0x1C, 0x23, 0x2A, 0x31, 0x38, 0x41, 0x48, 0x53, 0x5A, + 0x65, 0x6C, 0x77, 0x7E, 0x09, 0x00, 0x1B, 0x12, 0x2D, 0x24, 0x3F, 0x36, 0x58, 0x51, 0x4A, 0x43, 0x7C, 0x75, 0x6E, 0x67, 0x10, 0x19, + 0x02, 0x0B, 0x34, 0x3D, 0x26, 0x2F, 0x73, 0x7A, 0x61, 0x68, 0x57, 0x5E, 0x45, 0x4C, 0x3B, 0x32, 0x29, 0x20, 0x1F, 0x16, 0x0D, 0x04, + 0x6A, 0x63, 0x78, 0x71, 0x4E, 0x47, 0x5C, 0x55, 0x22, 0x2B, 0x30, 0x39, 0x06, 0x0F, 0x14, 0x1D, 0x25, 0x2C, 0x37, 0x3E, 0x01, 0x08, + 0x13, 0x1A, 0x6D, 0x64, 0x7F, 0x76, 0x49, 0x40, 0x5B, 0x52, 0x3C, 0x35, 0x2E, 0x27, 0x18, 0x11, 0x0A, 0x03, 0x74, 0x7D, 0x66, 0x6F, + 0x50, 0x59, 0x42, 0x4B, 0x17, 0x1E, 0x05, 0x0C, 0x33, 0x3A, 0x21, 0x28, 0x5F, 0x56, 0x4D, 0x44, 0x7B, 0x72, 0x69, 0x60, 0x0E, 0x07, + 0x1C, 0x15, 0x2A, 0x23, 0x38, 0x31, 0x46, 0x4F, 0x54, 0x5D, 0x62, 0x6B, 0x70, 0x79}; + +char CRC7(const char *data, int length) { + char crc = 0; + for (int i = 0; i < length; i++) { + crc = m_CRC7Table[(crc << 1) ^ data[i]]; + } + return crc; +} + +const unsigned short m_CRC16Table[256] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210, 0x3273, + 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, + 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, 0xB75B, + 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, + 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, + 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, + 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, + 0xC12D, 0xF14E, 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, + 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0, + 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, + 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, 0xCB7D, + 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, + 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, + 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 +}; + +unsigned short CRC16(const char *data, int length) { + unsigned short crc = 0; + for (int i = 0; i < length; i++) { + crc = (crc << 8) ^ m_CRC16Table[((crc >> 8) ^ data[i]) & 0x00FF]; + } + return crc; +} diff --git a/lib/HAL/sd_diskio2.h b/lib/HAL/sd_diskio2.h new file mode 100644 index 000000000..762cb6ed1 --- /dev/null +++ b/lib/HAL/sd_diskio2.h @@ -0,0 +1,34 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef _SD_DISKIO_H_ +#define _SD_DISKIO_H_ + +#include "Arduino.h" +#include "SPI.h" +#include "sd_defines.h" +// #include "diskio.h" + +uint8_t sdcard_init(uint8_t cs, SPIClass *spi, int hz); +uint8_t sdcard_uninit(uint8_t pdrv); + +bool sdcard_mount(uint8_t pdrv, const char *path, uint8_t max_files, bool format_if_empty); +uint8_t sdcard_unmount(uint8_t pdrv); + +sdcard_type_t sdcard_type(uint8_t pdrv); +uint32_t sdcard_num_sectors(uint8_t pdrv); +uint32_t sdcard_sector_size(uint8_t pdrv); +bool sd_read_raw(uint8_t pdrv, uint8_t *buffer, uint32_t sector); +bool sd_write_raw(uint8_t pdrv, uint8_t *buffer, uint32_t sector); + +#endif /* _SD_DISKIO_H_ */ diff --git a/lib/TFT_eSPI/Processors/TFT_eSPI_ESP32.h b/lib/TFT_eSPI/Processors/TFT_eSPI_ESP32.h index ad60e3dd2..ef414afd9 100644 --- a/lib/TFT_eSPI/Processors/TFT_eSPI_ESP32.h +++ b/lib/TFT_eSPI/Processors/TFT_eSPI_ESP32.h @@ -1,6 +1,6 @@ - //////////////////////////////////////////////////// - // TFT_eSPI driver functions for ESP32 processors // - //////////////////////////////////////////////////// +//////////////////////////////////////////////////// +// TFT_eSPI driver functions for ESP32 processors // +//////////////////////////////////////////////////// #ifndef _TFT_eSPI_ESP32H_ #define _TFT_eSPI_ESP32H_ @@ -9,34 +9,38 @@ #define PROCESSOR_ID 0x32 // Include processor specific header -#include "soc/spi_reg.h" #include "driver/spi_master.h" #include "hal/gpio_ll.h" +#include "soc/spi_reg.h" -#if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32) - #define CONFIG_IDF_TARGET_ESP32 +#if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S2) && \ + !defined(CONFIG_IDF_TARGET_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C5) && \ + !defined(CONFIG_IDF_TARGET_ESP32C6) +#define CONFIG_IDF_TARGET_ESP32 #endif // Fix IDF problems with ESP32C3 -#if CONFIG_IDF_TARGET_ESP32C3 - // Fix ESP32C3 IDF bug for missing definition - #ifndef REG_SPI_BASE - #define REG_SPI_BASE(i) (DR_REG_SPI1_BASE + (((i)>1) ? (((i)* 0x1000) + 0x20000) : (((~(i)) & 1)* 0x1000 ))) - #endif +#if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) || \ + defined(CONFIG_IDF_TARGET_ESP32C6) +// Fix ESP32C3 IDF bug for missing definition +#ifndef REG_SPI_BASE +#define REG_SPI_BASE(i) \ + (DR_REG_SPI1_BASE + (((i) > 1) ? (((i) * 0x1000) + 0x20000) : (((~(i)) & 1) * 0x1000))) +#endif - // Fix ESP32C3 IDF bug for name change - #ifndef SPI_MOSI_DLEN_REG - #define SPI_MOSI_DLEN_REG(x) SPI_MS_DLEN_REG(x) - #endif +// Fix ESP32C3 IDF bug for name change +#ifndef SPI_MOSI_DLEN_REG +#define SPI_MOSI_DLEN_REG(x) SPI_MS_DLEN_REG(x) +#endif - // Fix ESP32C3 specific register reference - #define out_w1tc out_w1tc.val - #define out_w1ts out_w1ts.val +// Fix ESP32C3 specific register reference +#define out_w1tc out_w1tc.val +#define out_w1ts out_w1ts.val #endif // SUPPORT_TRANSACTIONS is mandatory for ESP32 so the hal mutex is toggled -#if !defined (SUPPORT_TRANSACTIONS) - #define SUPPORT_TRANSACTIONS +#if !defined(SUPPORT_TRANSACTIONS) +#define SUPPORT_TRANSACTIONS #endif /* @@ -63,451 +67,531 @@ SPI3_HOST = 2 // ESP32 specific SPI port selection #ifdef USE_HSPI_PORT - #ifdef CONFIG_IDF_TARGET_ESP32 - #define SPI_PORT HSPI //HSPI is port 2 on ESP32 - #else - #define SPI_PORT 3 //HSPI is port 3 on ESP32 S2 - #endif +#ifdef CONFIG_IDF_TARGET_ESP32 +#define SPI_PORT HSPI // HSPI is port 2 on ESP32 +#else +#define SPI_PORT 3 // HSPI is port 3 on ESP32 S2 +#endif #elif defined(USE_FSPI_PORT) - #define SPI_PORT 2 //FSPI(ESP32 S2) +#define SPI_PORT 2 // FSPI(ESP32 S2) +#else +#ifdef CONFIG_IDF_TARGET_ESP32 +#define SPI_PORT VSPI #else - #ifdef CONFIG_IDF_TARGET_ESP32 - #define SPI_PORT VSPI - #else - #define SPI_PORT 2 //FSPI(ESP32 S2) - #endif +#define SPI_PORT 2 // FSPI(ESP32 S2) +#endif #endif #ifdef RPI_DISPLAY_TYPE - #define CMD_BITS (16-1) +#define CMD_BITS (16 - 1) #else - #define CMD_BITS (8-1) +#define CMD_BITS (8 - 1) #endif // Initialise processor specific SPI functions, used by init() #define INIT_TFT_DATA_BUS // Not used // Define a generic flag for 8-bit parallel -#if defined (ESP32_PARALLEL) // Specific to ESP32 for backwards compatibility - #if !defined (TFT_PARALLEL_8_BIT) - #define TFT_PARALLEL_8_BIT // Generic parallel flag - #endif +#if defined(ESP32_PARALLEL) // Specific to ESP32 for backwards compatibility +#if !defined(TFT_PARALLEL_8_BIT) +#define TFT_PARALLEL_8_BIT // Generic parallel flag +#endif #endif // Ensure ESP32 specific flag is defined for 8-bit parallel -#if defined (TFT_PARALLEL_8_BIT) - #if !defined (ESP32_PARALLEL) - #define ESP32_PARALLEL - #endif +#if defined(TFT_PARALLEL_8_BIT) +#if !defined(ESP32_PARALLEL) +#define ESP32_PARALLEL +#endif #endif // Processor specific code used by SPI bus transaction startWrite and endWrite functions -#if !defined (ESP32_PARALLEL) - #if (TFT_SPI_MODE == SPI_MODE1) || (TFT_SPI_MODE == SPI_MODE2) - #define SET_BUS_WRITE_MODE *_spi_user = SPI_USR_MOSI | SPI_CK_OUT_EDGE - #define SET_BUS_READ_MODE *_spi_user = SPI_USR_MOSI | SPI_USR_MISO | SPI_DOUTDIN | SPI_CK_OUT_EDGE - #else - #define SET_BUS_WRITE_MODE *_spi_user = SPI_USR_MOSI - #define SET_BUS_READ_MODE *_spi_user = SPI_USR_MOSI | SPI_USR_MISO | SPI_DOUTDIN - #endif +#if !defined(ESP32_PARALLEL) +#if (TFT_SPI_MODE == SPI_MODE1) || (TFT_SPI_MODE == SPI_MODE2) +#define SET_BUS_WRITE_MODE *_spi_user = SPI_USR_MOSI | SPI_CK_OUT_EDGE +#define SET_BUS_READ_MODE *_spi_user = SPI_USR_MOSI | SPI_USR_MISO | SPI_DOUTDIN | SPI_CK_OUT_EDGE #else - // Not applicable to parallel bus - #define SET_BUS_WRITE_MODE - #define SET_BUS_READ_MODE +#define SET_BUS_WRITE_MODE *_spi_user = SPI_USR_MOSI +#define SET_BUS_READ_MODE *_spi_user = SPI_USR_MOSI | SPI_USR_MISO | SPI_DOUTDIN +#endif +#else +// Not applicable to parallel bus +#define SET_BUS_WRITE_MODE +#define SET_BUS_READ_MODE #endif // Code to check if DMA is busy, used by SPI bus transaction transaction and endWrite functions #if !defined(TFT_PARALLEL_8_BIT) && !defined(SPI_18BIT_DRIVER) - #define ESP32_DMA - // Code to check if DMA is busy, used by SPI DMA + transaction + endWrite functions - #define DMA_BUSY_CHECK dmaWait() +#define ESP32_DMA +// Code to check if DMA is busy, used by SPI DMA + transaction + endWrite functions +#define DMA_BUSY_CHECK dmaWait() #else - #define DMA_BUSY_CHECK +#define DMA_BUSY_CHECK #endif #if defined(TFT_PARALLEL_8_BIT) - #define SPI_BUSY_CHECK +#define SPI_BUSY_CHECK #else - #define SPI_BUSY_CHECK while (*_spi_cmd&SPI_USR) +#define SPI_BUSY_CHECK while (*_spi_cmd & SPI_USR) #endif // If smooth font is used then it is likely SPIFFS will be needed #ifdef SMOOTH_FONT - // Call up the SPIFFS (SPI FLASH Filing System) for the anti-aliased fonts - #define FS_NO_GLOBALS - #include - #include "SPIFFS.h" // ESP32 only - #define FONT_FS_AVAILABLE +// Call up the SPIFFS (SPI FLASH Filing System) for the anti-aliased fonts +#define FS_NO_GLOBALS +#include "SPIFFS.h" // ESP32 only +#include +#define FONT_FS_AVAILABLE #endif - //////////////////////////////////////////////////////////////////////////////////////// // Define the DC (TFT Data/Command or Register Select (RS))pin drive code //////////////////////////////////////////////////////////////////////////////////////// #ifndef TFT_DC - #define DC_C // No macro allocated so it generates no code - #define DC_D // No macro allocated so it generates no code +#define DC_C // No macro allocated so it generates no code +#define DC_D // No macro allocated so it generates no code #else - #if defined (TFT_PARALLEL_8_BIT) - // TFT_DC, by design, must be in range 0-31 for single register parallel write - #if (TFT_DC >= 0) && (TFT_DC < 32) - #define DC_C GPIO.out_w1tc = (1 << TFT_DC) - #define DC_D GPIO.out_w1ts = (1 << TFT_DC) - #elif (TFT_DC >= 32) - #define DC_C GPIO.out1_w1tc.val = (1 << (TFT_DC- 32)) - #define DC_D GPIO.out1_w1ts.val = (1 << (TFT_DC- 32)) - #else - #define DC_C - #define DC_D - #endif - #else - #if (TFT_DC >= 32) - #ifdef RPI_DISPLAY_TYPE // RPi displays need a slower DC change - #define DC_C GPIO.out1_w1ts.val = (1 << (TFT_DC - 32)); \ - GPIO.out1_w1tc.val = (1 << (TFT_DC - 32)) - #define DC_D GPIO.out1_w1tc.val = (1 << (TFT_DC - 32)); \ - GPIO.out1_w1ts.val = (1 << (TFT_DC - 32)) - #else - #define DC_C GPIO.out1_w1tc.val = (1 << (TFT_DC - 32))//;GPIO.out1_w1tc.val = (1 << (TFT_DC - 32)) - #define DC_D GPIO.out1_w1ts.val = (1 << (TFT_DC - 32))//;GPIO.out1_w1ts.val = (1 << (TFT_DC - 32)) - #endif - #elif (TFT_DC >= 0) - #if defined (RPI_DISPLAY_TYPE) - #if defined (ILI9486_DRIVER) - // RPi ILI9486 display needs a slower DC change - #define DC_C GPIO.out_w1tc = (1 << TFT_DC); \ - GPIO.out_w1tc = (1 << TFT_DC) - #define DC_D GPIO.out_w1tc = (1 << TFT_DC); \ - GPIO.out_w1ts = (1 << TFT_DC) - #else - // Other RPi displays need a slower C->D change - #define DC_C GPIO.out_w1tc = (1 << TFT_DC) - #define DC_D GPIO.out_w1tc = (1 << TFT_DC); \ - GPIO.out_w1ts = (1 << TFT_DC) - #endif - #else - #define DC_C GPIO.out_w1tc = (1 << TFT_DC)//;GPIO.out_w1tc = (1 << TFT_DC) - #define DC_D GPIO.out_w1ts = (1 << TFT_DC)//;GPIO.out_w1ts = (1 << TFT_DC) - #endif - #else - #define DC_C - #define DC_D - #endif - #endif +#if defined(TFT_PARALLEL_8_BIT) +// TFT_DC, by design, must be in range 0-31 for single register parallel write +#if (TFT_DC >= 0) && (TFT_DC < 32) +#define DC_C GPIO.out_w1tc = (1 << TFT_DC) +#define DC_D GPIO.out_w1ts = (1 << TFT_DC) +#elif (TFT_DC >= 32) +#define DC_C GPIO.out1_w1tc.val = (1 << (TFT_DC - 32)) +#define DC_D GPIO.out1_w1ts.val = (1 << (TFT_DC - 32)) +#else +#define DC_C +#define DC_D +#endif +#else +#if (TFT_DC >= 32) +#ifdef RPI_DISPLAY_TYPE // RPi displays need a slower DC change +#define DC_C \ + GPIO.out1_w1ts.val = (1 << (TFT_DC - 32)); \ + GPIO.out1_w1tc.val = (1 << (TFT_DC - 32)) +#define DC_D \ + GPIO.out1_w1tc.val = (1 << (TFT_DC - 32)); \ + GPIO.out1_w1ts.val = (1 << (TFT_DC - 32)) +#else +#define DC_C GPIO.out1_w1tc.val = (1 << (TFT_DC - 32)) //;GPIO.out1_w1tc.val = (1 << (TFT_DC - 32)) +#define DC_D GPIO.out1_w1ts.val = (1 << (TFT_DC - 32)) //;GPIO.out1_w1ts.val = (1 << (TFT_DC - 32)) +#endif +#elif (TFT_DC >= 0) +#if defined(RPI_DISPLAY_TYPE) +#if defined(ILI9486_DRIVER) +// RPi ILI9486 display needs a slower DC change +#define DC_C \ + GPIO.out_w1tc = (1 << TFT_DC); \ + GPIO.out_w1tc = (1 << TFT_DC) +#define DC_D \ + GPIO.out_w1tc = (1 << TFT_DC); \ + GPIO.out_w1ts = (1 << TFT_DC) +#else +// Other RPi displays need a slower C->D change +#define DC_C GPIO.out_w1tc = (1 << TFT_DC) +#define DC_D \ + GPIO.out_w1tc = (1 << TFT_DC); \ + GPIO.out_w1ts = (1 << TFT_DC) +#endif +#else +#define DC_C GPIO.out_w1tc = (1 << TFT_DC) //;GPIO.out_w1tc = (1 << TFT_DC) +#define DC_D GPIO.out_w1ts = (1 << TFT_DC) //;GPIO.out_w1ts = (1 << TFT_DC) +#endif +#else +#define DC_C +#define DC_D +#endif +#endif #endif //////////////////////////////////////////////////////////////////////////////////////// // Define the CS (TFT chip select) pin drive code //////////////////////////////////////////////////////////////////////////////////////// #ifndef TFT_CS - #define TFT_CS -1 // Keep DMA code happy - #define CS_L // No macro allocated so it generates no code - #define CS_H // No macro allocated so it generates no code +#define TFT_CS -1 // Keep DMA code happy +#define CS_L // No macro allocated so it generates no code +#define CS_H // No macro allocated so it generates no code +#else +#if defined(TFT_PARALLEL_8_BIT) +#if TFT_CS >= 32 +#define CS_L GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)) +#define CS_H GPIO.out1_w1ts.val = (1 << (TFT_CS - 32)) +#elif TFT_CS >= 0 +#define CS_L GPIO.out_w1tc = (1 << TFT_CS) +#define CS_H GPIO.out_w1ts = (1 << TFT_CS) +#else +#define CS_L +#define CS_H +#endif +#else +#if (TFT_CS >= 32) +#ifdef RPI_DISPLAY_TYPE // RPi display needs a slower CS change +#define CS_L \ + GPIO.out1_w1ts.val = (1 << (TFT_CS - 32)); \ + GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)) +#define CS_H \ + GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)); \ + GPIO.out1_w1ts.val = (1 << (TFT_CS - 32)) +#else +#define CS_L \ + GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)); \ + GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)) +#define CS_H GPIO.out1_w1ts.val = (1 << (TFT_CS - 32)) //;GPIO.out1_w1ts.val = (1 << (TFT_CS - 32)) +#endif +#elif (TFT_CS >= 0) +#ifdef RPI_DISPLAY_TYPE // RPi display needs a slower CS change +#define CS_L \ + GPIO.out_w1ts = (1 << TFT_CS); \ + GPIO.out_w1tc = (1 << TFT_CS) +#define CS_H \ + GPIO.out_w1tc = (1 << TFT_CS); \ + GPIO.out_w1ts = (1 << TFT_CS) +#else +#define CS_L \ + GPIO.out_w1tc = (1 << TFT_CS); \ + GPIO.out_w1tc = (1 << TFT_CS) +#define CS_H GPIO.out_w1ts = (1 << TFT_CS) //;GPIO.out_w1ts = (1 << TFT_CS) +#endif #else - #if defined (TFT_PARALLEL_8_BIT) - #if TFT_CS >= 32 - #define CS_L GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)) - #define CS_H GPIO.out1_w1ts.val = (1 << (TFT_CS - 32)) - #elif TFT_CS >= 0 - #define CS_L GPIO.out_w1tc = (1 << TFT_CS) - #define CS_H GPIO.out_w1ts = (1 << TFT_CS) - #else - #define CS_L - #define CS_H - #endif - #else - #if (TFT_CS >= 32) - #ifdef RPI_DISPLAY_TYPE // RPi display needs a slower CS change - #define CS_L GPIO.out1_w1ts.val = (1 << (TFT_CS - 32)); \ - GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)) - #define CS_H GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)); \ - GPIO.out1_w1ts.val = (1 << (TFT_CS - 32)) - #else - #define CS_L GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)); GPIO.out1_w1tc.val = (1 << (TFT_CS - 32)) - #define CS_H GPIO.out1_w1ts.val = (1 << (TFT_CS - 32))//;GPIO.out1_w1ts.val = (1 << (TFT_CS - 32)) - #endif - #elif (TFT_CS >= 0) - #ifdef RPI_DISPLAY_TYPE // RPi display needs a slower CS change - #define CS_L GPIO.out_w1ts = (1 << TFT_CS); GPIO.out_w1tc = (1 << TFT_CS) - #define CS_H GPIO.out_w1tc = (1 << TFT_CS); GPIO.out_w1ts = (1 << TFT_CS) - #else - #define CS_L GPIO.out_w1tc = (1 << TFT_CS); GPIO.out_w1tc = (1 << TFT_CS) - #define CS_H GPIO.out_w1ts = (1 << TFT_CS)//;GPIO.out_w1ts = (1 << TFT_CS) - #endif - #else - #define CS_L - #define CS_H - #endif - #endif +#define CS_L +#define CS_H +#endif +#endif #endif //////////////////////////////////////////////////////////////////////////////////////// // Define the WR (TFT Write) pin drive code //////////////////////////////////////////////////////////////////////////////////////// -#if defined (TFT_WR) - #if (TFT_WR >= 32) - // Note: it will be ~1.25x faster if the TFT_WR pin uses a GPIO pin lower than 32 - #define WR_L GPIO.out1_w1tc.val = (1 << (TFT_WR - 32)) - #define WR_H GPIO.out1_w1ts.val = (1 << (TFT_WR - 32)) - #elif (TFT_WR >= 0) - // TFT_WR, for best performance, should be in range 0-31 for single register parallel write - #define WR_L GPIO.out_w1tc = (1 << TFT_WR) - #define WR_H GPIO.out_w1ts = (1 << TFT_WR) - #else - #define WR_L - #define WR_H - #endif +#if defined(TFT_WR) +#if (TFT_WR >= 32) +// Note: it will be ~1.25x faster if the TFT_WR pin uses a GPIO pin lower than 32 +#define WR_L GPIO.out1_w1tc.val = (1 << (TFT_WR - 32)) +#define WR_H GPIO.out1_w1ts.val = (1 << (TFT_WR - 32)) +#elif (TFT_WR >= 0) +// TFT_WR, for best performance, should be in range 0-31 for single register parallel write +#define WR_L GPIO.out_w1tc = (1 << TFT_WR) +#define WR_H GPIO.out_w1ts = (1 << TFT_WR) #else - #define WR_L - #define WR_H +#define WR_L +#define WR_H +#endif +#else +#define WR_L +#define WR_H #endif //////////////////////////////////////////////////////////////////////////////////////// // Define the touch screen chip select pin drive code //////////////////////////////////////////////////////////////////////////////////////// #ifndef TOUCH_CS - #define T_CS_L // No macro allocated so it generates no code - #define T_CS_H // No macro allocated so it generates no code -#else // XPT2046 is slow, so use slower digitalWrite here - #define T_CS_L digitalWrite(TOUCH_CS, LOW) - #define T_CS_H digitalWrite(TOUCH_CS, HIGH) +#define T_CS_L // No macro allocated so it generates no code +#define T_CS_H // No macro allocated so it generates no code +#else // XPT2046 is slow, so use slower digitalWrite here +#define T_CS_L digitalWrite(TOUCH_CS, LOW) +#define T_CS_H digitalWrite(TOUCH_CS, HIGH) #endif //////////////////////////////////////////////////////////////////////////////////////// // Make sure SPI default pins are assigned if not specified by user or set to -1 //////////////////////////////////////////////////////////////////////////////////////// -#if !defined (TFT_PARALLEL_8_BIT) - - #ifdef USE_HSPI_PORT - - #ifndef TFT_MISO - #define TFT_MISO -1 - #endif - - #ifndef TFT_MOSI - #define TFT_MOSI 13 - #endif - #if (TFT_MOSI == -1) - #undef TFT_MOSI - #define TFT_MOSI 13 - #endif - - #ifndef TFT_SCLK - #define TFT_SCLK 14 - #endif - #if (TFT_SCLK == -1) - #undef TFT_SCLK - #define TFT_SCLK 14 - #endif - - #else // VSPI port - - #ifndef TFT_MISO - #define TFT_MISO -1 - #endif - - #ifndef TFT_MOSI - #define TFT_MOSI 23 - #endif - #if (TFT_MOSI == -1) - #undef TFT_MOSI - #define TFT_MOSI 23 - #endif - - #ifndef TFT_SCLK - #define TFT_SCLK 18 - #endif - #if (TFT_SCLK == -1) - #undef TFT_SCLK - #define TFT_SCLK 18 - #endif - - #if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) - #if (TFT_MISO == -1) - #undef TFT_MISO - #define TFT_MISO TFT_MOSI - #endif - #endif - - #endif +#if !defined(TFT_PARALLEL_8_BIT) + +#ifdef USE_HSPI_PORT + +#ifndef TFT_MISO +#define TFT_MISO -1 +#endif + +#ifndef TFT_MOSI +#define TFT_MOSI 13 +#endif +#if (TFT_MOSI == -1) +#undef TFT_MOSI +#define TFT_MOSI 13 +#endif + +#ifndef TFT_SCLK +#define TFT_SCLK 14 +#endif +#if (TFT_SCLK == -1) +#undef TFT_SCLK +#define TFT_SCLK 14 +#endif + +#else // VSPI port + +#ifndef TFT_MISO +#define TFT_MISO -1 +#endif + +#ifndef TFT_MOSI +#define TFT_MOSI 23 +#endif +#if (TFT_MOSI == -1) +#undef TFT_MOSI +#define TFT_MOSI 23 +#endif + +#ifndef TFT_SCLK +#define TFT_SCLK 18 +#endif +#if (TFT_SCLK == -1) +#undef TFT_SCLK +#define TFT_SCLK 18 +#endif + +#if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) || \ + defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32C6) +#if (TFT_MISO == -1) +#undef TFT_MISO +#define TFT_MISO TFT_MOSI +#endif +#endif + +#endif #endif //////////////////////////////////////////////////////////////////////////////////////// // Define the parallel bus interface chip pin drive code //////////////////////////////////////////////////////////////////////////////////////// -#if defined (TFT_PARALLEL_8_BIT) - - // Create a bit set lookup table for data bus - wastes 1kbyte of RAM but speeds things up dramatically - // can then use e.g. GPIO.out_w1ts = set_mask(0xFF); to set data bus to 0xFF - #define PARALLEL_INIT_TFT_DATA_BUS \ - for (int32_t c = 0; c<256; c++) \ - { \ - xset_mask[c] = 0; \ - if ( c & 0x01 ) xset_mask[c] |= (1 << TFT_D0); \ - if ( c & 0x02 ) xset_mask[c] |= (1 << TFT_D1); \ - if ( c & 0x04 ) xset_mask[c] |= (1 << TFT_D2); \ - if ( c & 0x08 ) xset_mask[c] |= (1 << TFT_D3); \ - if ( c & 0x10 ) xset_mask[c] |= (1 << TFT_D4); \ - if ( c & 0x20 ) xset_mask[c] |= (1 << TFT_D5); \ - if ( c & 0x40 ) xset_mask[c] |= (1 << TFT_D6); \ - if ( c & 0x80 ) xset_mask[c] |= (1 << TFT_D7); \ - } \ - - // Mask for the 8 data bits to set pin directions - #define GPIO_DIR_MASK ((1 << TFT_D0) | (1 << TFT_D1) | (1 << TFT_D2) | (1 << TFT_D3) | (1 << TFT_D4) | (1 << TFT_D5) | (1 << TFT_D6) | (1 << TFT_D7)) - - #if (TFT_WR >= 32) - // Data bits and the write line are cleared sequentially - #define GPIO_OUT_CLR_MASK (GPIO_DIR_MASK); WR_L - #elif (TFT_WR >= 0) - // Data bits and the write line are cleared to 0 in one step (1.25x faster) - #define GPIO_OUT_CLR_MASK (GPIO_DIR_MASK | (1 << TFT_WR)) - #else - #define GPIO_OUT_CLR_MASK - #endif - - // A lookup table is used to set the different bit patterns, this uses 1kByte of RAM - #define set_mask(C) xset_mask[C] // 63fps Sprite rendering test 33% faster, graphicstest only 1.8% faster than shifting in real time - - // Real-time shifting alternative to above to save 1KByte RAM, 47 fps Sprite rendering test - /*#define set_mask(C) (((C)&0x80)>>7)<>6)<>5)<>4)<>3)<>2)<>1)<>0)<> 8)); WR_H; \ - GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) (((C) & 0x07E0)>> 3)); WR_H; \ - GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) (((C) & 0x001F)<< 3)); WR_H - - // 18-bit color write with swapped bytes - #define tft_Write_16S(C) Cswap = ((C) >>8 | (C) << 8); tft_Write_16(Cswap) - - #else - - #ifdef PSEUDO_16_BIT - // One write strobe for both bytes - #define tft_Write_16(C) GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 0)); WR_H - #define tft_Write_16S(C) GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 8)); WR_H - #else - // Write 16 bits to TFT - #define tft_Write_16(C) GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 8)); WR_H; \ - GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 0)); WR_H - - // 16-bit write with swapped bytes - #define tft_Write_16S(C) GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 0)); WR_H; \ - GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 8)); WR_H - #endif - - #endif - - // Write 32 bits to TFT - #define tft_Write_32(C) GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 24)); WR_H; \ - GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 16)); WR_H; \ - GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 8)); WR_H; \ - GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 0)); WR_H - - // Write two concatenated 16-bit values to TFT - #define tft_Write_32C(C,D) GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 8)); WR_H; \ - GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 0)); WR_H; \ - GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((D) >> 8)); WR_H; \ - GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((D) >> 0)); WR_H - - // Write 16-bit value twice to TFT - used by drawPixel() - #define tft_Write_32D(C) GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 8)); WR_H; \ - GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 0)); WR_H; \ - GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 8)); WR_H; \ - GPIO.out_w1tc = GPIO_OUT_CLR_MASK; GPIO.out_w1ts = set_mask((uint8_t) ((C) >> 0)); WR_H - - // Read pin - #ifdef TFT_RD - #if (TFT_RD >= 32) - #define RD_L GPIO.out1_w1tc.val = (1 << (TFT_RD - 32)) - #define RD_H GPIO.out1_w1ts.val = (1 << (TFT_RD - 32)) - #elif (TFT_RD >= 0) - #define RD_L GPIO.out_w1tc = (1 << TFT_RD) - //#define RD_L digitalWrite(TFT_WR, LOW) - #define RD_H GPIO.out_w1ts = (1 << TFT_RD) - //#define RD_H digitalWrite(TFT_WR, HIGH) - #else - #define RD_L - #define RD_H - #endif - #else - #define TFT_RD -1 - #define RD_L - #define RD_H - #endif +#if defined(TFT_PARALLEL_8_BIT) + +// Create a bit set lookup table for data bus - wastes 1kbyte of RAM but speeds things up dramatically +// can then use e.g. GPIO.out_w1ts = set_mask(0xFF); to set data bus to 0xFF +#define PARALLEL_INIT_TFT_DATA_BUS \ + for (int32_t c = 0; c < 256; c++) { \ + xset_mask[c] = 0; \ + if (c & 0x01) xset_mask[c] |= (1 << TFT_D0); \ + if (c & 0x02) xset_mask[c] |= (1 << TFT_D1); \ + if (c & 0x04) xset_mask[c] |= (1 << TFT_D2); \ + if (c & 0x08) xset_mask[c] |= (1 << TFT_D3); \ + if (c & 0x10) xset_mask[c] |= (1 << TFT_D4); \ + if (c & 0x20) xset_mask[c] |= (1 << TFT_D5); \ + if (c & 0x40) xset_mask[c] |= (1 << TFT_D6); \ + if (c & 0x80) xset_mask[c] |= (1 << TFT_D7); \ + } + +// Mask for the 8 data bits to set pin directions +#define GPIO_DIR_MASK \ + ((1 << TFT_D0) | (1 << TFT_D1) | (1 << TFT_D2) | (1 << TFT_D3) | (1 << TFT_D4) | (1 << TFT_D5) | \ + (1 << TFT_D6) | (1 << TFT_D7)) + +#if (TFT_WR >= 32) +// Data bits and the write line are cleared sequentially +#define GPIO_OUT_CLR_MASK \ + (GPIO_DIR_MASK); \ + WR_L +#elif (TFT_WR >= 0) +// Data bits and the write line are cleared to 0 in one step (1.25x faster) +#define GPIO_OUT_CLR_MASK (GPIO_DIR_MASK | (1 << TFT_WR)) +#else +#define GPIO_OUT_CLR_MASK +#endif + +// A lookup table is used to set the different bit patterns, this uses 1kByte of RAM +#define set_mask(C) \ + xset_mask[C] // 63fps Sprite rendering test 33% faster, graphicstest only 1.8% faster than shifting in + // real time + +// Real-time shifting alternative to above to save 1KByte RAM, 47 fps Sprite rendering test +/*#define set_mask(C) (((C)&0x80)>>7)<>6)<>5)<>4)<>3)<>2)<>1)<>0)<> 8)); \ + WR_H; \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts = set_mask((uint8_t)(((C) & 0x07E0) >> 3)); \ + WR_H; \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts = set_mask((uint8_t)(((C) & 0x001F) << 3)); \ + WR_H + +// 18-bit color write with swapped bytes +#define tft_Write_16S(C) \ + Cswap = ((C) >> 8 | (C) << 8); \ + tft_Write_16(Cswap) + +#else + +#ifdef PSEUDO_16_BIT +// One write strobe for both bytes +#define tft_Write_16(C) \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts = set_mask((uint8_t)((C) >> 0)); \ + WR_H +#define tft_Write_16S(C) \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts = set_mask((uint8_t)((C) >> 8)); \ + WR_H +#else +// Write 16 bits to TFT +#define tft_Write_16(C) \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts = set_mask((uint8_t)((C) >> 8)); \ + WR_H; \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts = set_mask((uint8_t)((C) >> 0)); \ + WR_H + +// 16-bit write with swapped bytes +#define tft_Write_16S(C) \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts = set_mask((uint8_t)((C) >> 0)); \ + WR_H; \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts = set_mask((uint8_t)((C) >> 8)); \ + WR_H +#endif + +#endif + +// Write 32 bits to TFT +#define tft_Write_32(C) \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts = set_mask((uint8_t)((C) >> 24)); \ + WR_H; \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts = set_mask((uint8_t)((C) >> 16)); \ + WR_H; \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts = set_mask((uint8_t)((C) >> 8)); \ + WR_H; \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts = set_mask((uint8_t)((C) >> 0)); \ + WR_H + +// Write two concatenated 16-bit values to TFT +#define tft_Write_32C(C, D) \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts = set_mask((uint8_t)((C) >> 8)); \ + WR_H; \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts = set_mask((uint8_t)((C) >> 0)); \ + WR_H; \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts = set_mask((uint8_t)((D) >> 8)); \ + WR_H; \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts = set_mask((uint8_t)((D) >> 0)); \ + WR_H + +// Write 16-bit value twice to TFT - used by drawPixel() +#define tft_Write_32D(C) \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts = set_mask((uint8_t)((C) >> 8)); \ + WR_H; \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts = set_mask((uint8_t)((C) >> 0)); \ + WR_H; \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts = set_mask((uint8_t)((C) >> 8)); \ + WR_H; \ + GPIO.out_w1tc = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts = set_mask((uint8_t)((C) >> 0)); \ + WR_H + +// Read pin +#ifdef TFT_RD +#if (TFT_RD >= 32) +#define RD_L GPIO.out1_w1tc.val = (1 << (TFT_RD - 32)) +#define RD_H GPIO.out1_w1ts.val = (1 << (TFT_RD - 32)) +#elif (TFT_RD >= 0) +#define RD_L GPIO.out_w1tc = (1 << TFT_RD) +// #define RD_L digitalWrite(TFT_WR, LOW) +#define RD_H GPIO.out_w1ts = (1 << TFT_RD) +// #define RD_H digitalWrite(TFT_WR, HIGH) +#else +#define RD_L +#define RD_H +#endif +#else +#define TFT_RD -1 +#define RD_L +#define RD_H +#endif //////////////////////////////////////////////////////////////////////////////////////// // Macros to write commands/pixel colour data to a SPI ILI948x TFT //////////////////////////////////////////////////////////////////////////////////////// -#elif defined (SPI_18BIT_DRIVER) // SPI 18-bit colour +#elif defined(SPI_18BIT_DRIVER) // SPI 18-bit colour - // Write 8 bits to TFT - #define tft_Write_8(C) spi.transfer(C) +// Write 8 bits to TFT +#define tft_Write_8(C) spi.transfer(C) - // Convert 16-bit colour to 18-bit and write in 3 bytes - #define tft_Write_16(C) spi.transfer(((C) & 0xF800)>>8); \ - spi.transfer(((C) & 0x07E0)>>3); \ - spi.transfer(((C) & 0x001F)<<3) +// Convert 16-bit colour to 18-bit and write in 3 bytes +#define tft_Write_16(C) \ + spi.transfer(((C) & 0xF800) >> 8); \ + spi.transfer(((C) & 0x07E0) >> 3); \ + spi.transfer(((C) & 0x001F) << 3) - // Future option for transfer without wait - #define tft_Write_16N(C) tft_Write_16(C) +// Future option for transfer without wait +#define tft_Write_16N(C) tft_Write_16(C) - // Convert swapped byte 16-bit colour to 18-bit and write in 3 bytes - #define tft_Write_16S(C) spi.transfer((C) & 0xF8); \ - spi.transfer(((C) & 0xE000)>>11 | ((C) & 0x07)<<5); \ - spi.transfer(((C) & 0x1F00)>>5) +// Convert swapped byte 16-bit colour to 18-bit and write in 3 bytes +#define tft_Write_16S(C) \ + spi.transfer((C) & 0xF8); \ + spi.transfer(((C) & 0xE000) >> 11 | ((C) & 0x07) << 5); \ + spi.transfer(((C) & 0x1F00) >> 5) - // Write 32 bits to TFT - #define tft_Write_32(C) spi.write32(C) +// Write 32 bits to TFT +#define tft_Write_32(C) spi.write32(C) - // Write two concatenated 16-bit values to TFT - #define tft_Write_32C(C,D) spi.write32((C)<<16 | (D)) +// Write two concatenated 16-bit values to TFT +#define tft_Write_32C(C, D) spi.write32((C) << 16 | (D)) - // Write 16-bit value twice to TFT - #define tft_Write_32D(C) spi.write32((C)<<16 | (C)) +// Write 16-bit value twice to TFT +#define tft_Write_32D(C) spi.write32((C) << 16 | (C)) //////////////////////////////////////////////////////////////////////////////////////// // Macros to write commands/pixel colour data to an Raspberry Pi TFT //////////////////////////////////////////////////////////////////////////////////////// -#elif defined (RPI_DISPLAY_TYPE) +#elif defined(RPI_DISPLAY_TYPE) - // ESP32 low level SPI writes for 8, 16 and 32-bit values - // to avoid the function call overhead - #define TFT_WRITE_BITS(D, B) \ - WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), B-1); \ - WRITE_PERI_REG(SPI_W0_REG(SPI_PORT), D); \ - SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); \ - while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); +// ESP32 low level SPI writes for 8, 16 and 32-bit values +// to avoid the function call overhead +#define TFT_WRITE_BITS(D, B) \ + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), B - 1); \ + WRITE_PERI_REG(SPI_W0_REG(SPI_PORT), D); \ + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); \ + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT)) & SPI_USR); - // Write 8 bits - #define tft_Write_8(C) TFT_WRITE_BITS((C)<<8, 16) +// Write 8 bits +#define tft_Write_8(C) TFT_WRITE_BITS((C) << 8, 16) - // Write 16 bits with corrected endianness for 16-bit colours - #define tft_Write_16(C) TFT_WRITE_BITS((C)<<8 | (C)>>8, 16) +// Write 16 bits with corrected endianness for 16-bit colours +#define tft_Write_16(C) TFT_WRITE_BITS((C) << 8 | (C) >> 8, 16) - // Future option for transfer without wait - #define tft_Write_16N(C) tft_Write_16(C) +// Future option for transfer without wait +#define tft_Write_16N(C) tft_Write_16(C) - // Write 16 bits - #define tft_Write_16S(C) TFT_WRITE_BITS(C, 16) +// Write 16 bits +#define tft_Write_16S(C) TFT_WRITE_BITS(C, 16) - // Write 32 bits - #define tft_Write_32(C) TFT_WRITE_BITS(C, 32) +// Write 32 bits +#define tft_Write_32(C) TFT_WRITE_BITS(C, 32) - // Write two address coordinates - #define tft_Write_32C(C,D) TFT_WRITE_BITS((C)<<24 | (C), 32); \ - TFT_WRITE_BITS((D)<<24 | (D), 32) +// Write two address coordinates +#define tft_Write_32C(C, D) \ + TFT_WRITE_BITS((C) << 24 | (C), 32); \ + TFT_WRITE_BITS((D) << 24 | (D), 32) - // Write same value twice - #define tft_Write_32D(C) tft_Write_32C(C,C) +// Write same value twice +#define tft_Write_32D(C) tft_Write_32C(C, C) //////////////////////////////////////////////////////////////////////////////////////// // Macros for all other SPI displays @@ -541,51 +625,55 @@ SPI3_HOST = 2 #define tft_Write_32D(C) TFT_WRITE_BITS((uint16_t)((C)<<8 | (C)>>8)<<16 | (uint16_t)((C)<<8 | (C)>>8), 32) //*/ //* Replacement slimmer macros - #define TFT_WRITE_BITS(D, B) *_spi_mosi_dlen = B-1; \ - *_spi_w = D; \ - *_spi_cmd = SPI_USR; \ - while (*_spi_cmd & SPI_USR); +#define TFT_WRITE_BITS(D, B) \ + *_spi_mosi_dlen = B - 1; \ + *_spi_w = D; \ + *_spi_cmd = SPI_USR; \ + while (*_spi_cmd & SPI_USR); - // Write 8 bits - #define tft_Write_8(C) TFT_WRITE_BITS(C, 8) +// Write 8 bits +#define tft_Write_8(C) TFT_WRITE_BITS(C, 8) - // Write 16 bits with corrected endianness for 16-bit colours - #define tft_Write_16(C) TFT_WRITE_BITS((C)<<8 | (C)>>8, 16) +// Write 16 bits with corrected endianness for 16-bit colours +#define tft_Write_16(C) TFT_WRITE_BITS((C) << 8 | (C) >> 8, 16) - // Future option for transfer without wait - #define tft_Write_16N(C) *_spi_mosi_dlen = 16-1; \ - *_spi_w = ((C)<<8 | (C)>>8); \ - *_spi_cmd = SPI_USR; +// Future option for transfer without wait +#define tft_Write_16N(C) \ + *_spi_mosi_dlen = 16 - 1; \ + *_spi_w = ((C) << 8 | (C) >> 8); \ + *_spi_cmd = SPI_USR; - // Write 16 bits - #define tft_Write_16S(C) TFT_WRITE_BITS(C, 16) +// Write 16 bits +#define tft_Write_16S(C) TFT_WRITE_BITS(C, 16) - // Write 32 bits - #define tft_Write_32(C) TFT_WRITE_BITS(C, 32) +// Write 32 bits +#define tft_Write_32(C) TFT_WRITE_BITS(C, 32) - // Write two address coordinates - #define tft_Write_32C(C,D) TFT_WRITE_BITS((uint16_t)((D)<<8 | (D)>>8)<<16 | (uint16_t)((C)<<8 | (C)>>8), 32) +// Write two address coordinates +#define tft_Write_32C(C, D) \ + TFT_WRITE_BITS((uint16_t)((D) << 8 | (D) >> 8) << 16 | (uint16_t)((C) << 8 | (C) >> 8), 32) - // Write same value twice - #define tft_Write_32D(C) TFT_WRITE_BITS((uint16_t)((C)<<8 | (C)>>8)<<16 | (uint16_t)((C)<<8 | (C)>>8), 32) +// Write same value twice +#define tft_Write_32D(C) \ + TFT_WRITE_BITS((uint16_t)((C) << 8 | (C) >> 8) << 16 | (uint16_t)((C) << 8 | (C) >> 8), 32) //*/ #endif #ifndef tft_Write_16N - #define tft_Write_16N tft_Write_16 +#define tft_Write_16N tft_Write_16 #endif //////////////////////////////////////////////////////////////////////////////////////// // Macros to read from display using SPI or software SPI //////////////////////////////////////////////////////////////////////////////////////// -#if !defined (TFT_PARALLEL_8_BIT) - // Read from display using SPI or software SPI - // Use a SPI read transfer - #define tft_Read_8() spi.transfer(0) +#if !defined(TFT_PARALLEL_8_BIT) +// Read from display using SPI or software SPI +// Use a SPI read transfer +#define tft_Read_8() spi.transfer(0) #endif // Concatenate a byte sequence A,B,C,D to CDAB, P is a uint8_t pointer -#define DAT8TO32(P) ( (uint32_t)P[0]<<8 | P[1] | P[2]<<24 | P[3]<<16 ) +#define DAT8TO32(P) ((uint32_t)P[0] << 8 | P[1] | P[2] << 24 | P[3] << 16) #endif // Header end diff --git a/lib/TFT_eSPI/Processors/TFT_eSPI_ESP32_C3.h b/lib/TFT_eSPI/Processors/TFT_eSPI_ESP32_C3.h index 6fa185854..86a663f19 100644 --- a/lib/TFT_eSPI/Processors/TFT_eSPI_ESP32_C3.h +++ b/lib/TFT_eSPI/Processors/TFT_eSPI_ESP32_C3.h @@ -1,6 +1,6 @@ - //////////////////////////////////////////////////// - // TFT_eSPI driver functions for ESP32 processors // - //////////////////////////////////////////////////// +//////////////////////////////////////////////////// +// TFT_eSPI driver functions for ESP32 processors // +//////////////////////////////////////////////////// // Temporarily a separate file to TFT_eSPI_ESP32.h until board package low level API stabilises @@ -8,41 +8,44 @@ #define _TFT_eSPI_ESP32H_ #if !defined(DISABLE_ALL_LIBRARY_WARNINGS) - #warning >>>>------>> DMA is not supported on the ESP32 C3 (possible future update) +#warning >>>>------>> DMA is not supported on the ESP32 C3 (possible future update) #endif // Processor ID reported by getSetup() #define PROCESSOR_ID 0x32 // Include processor specific header -#include "soc/spi_reg.h" #include "driver/spi_master.h" #include "hal/gpio_ll.h" +#include "soc/spi_reg.h" -#if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32) - #define CONFIG_IDF_TARGET_ESP32 +#if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S2) && \ + !defined(CONFIG_IDF_TARGET_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C5) && \ + !defined(CONFIG_IDF_TARGET_ESP32C6) +#define CONFIG_IDF_TARGET_ESP32 #endif #ifndef VSPI - #define VSPI FSPI +#define VSPI FSPI #endif // Fix IDF problems with ESP32C3 -#if CONFIG_IDF_TARGET_ESP32C3 - // Fix ESP32C3 IDF bug for missing definition (VSPI/FSPI only tested at the moment) - #ifndef REG_SPI_BASE - #define REG_SPI_BASE(i) DR_REG_SPI2_BASE - #endif +#if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) || \ + defined(CONFIG_IDF_TARGET_ESP32C6) +// Fix ESP32C3 IDF bug for missing definition (VSPI/FSPI only tested at the moment) +#ifndef REG_SPI_BASE +#define REG_SPI_BASE(i) DR_REG_SPI2_BASE +#endif - // Fix ESP32C3 IDF bug for name change - #ifndef SPI_MOSI_DLEN_REG - #define SPI_MOSI_DLEN_REG(x) SPI_MS_DLEN_REG(x) - #endif +// Fix ESP32C3 IDF bug for name change +#ifndef SPI_MOSI_DLEN_REG +#define SPI_MOSI_DLEN_REG(x) SPI_MS_DLEN_REG(x) +#endif #endif // SUPPORT_TRANSACTIONS is mandatory for ESP32 so the hal mutex is toggled -#if !defined (SUPPORT_TRANSACTIONS) - #define SUPPORT_TRANSACTIONS +#if !defined(SUPPORT_TRANSACTIONS) +#define SUPPORT_TRANSACTIONS #endif /* @@ -71,434 +74,515 @@ SPI3_HOST = 2 #define SPI_PORT SPI2_HOST #ifdef RPI_DISPLAY_TYPE - #define CMD_BITS (16-1) +#define CMD_BITS (16 - 1) #else - #define CMD_BITS (8-1) +#define CMD_BITS (8 - 1) #endif // Initialise processor specific SPI functions, used by init() #define INIT_TFT_DATA_BUS // Not used // Define a generic flag for 8-bit parallel -#if defined (ESP32_PARALLEL) // Specific to ESP32 for backwards compatibility - #if !defined (TFT_PARALLEL_8_BIT) - #define TFT_PARALLEL_8_BIT // Generic parallel flag - #endif +#if defined(ESP32_PARALLEL) // Specific to ESP32 for backwards compatibility +#if !defined(TFT_PARALLEL_8_BIT) +#define TFT_PARALLEL_8_BIT // Generic parallel flag +#endif #endif // Ensure ESP32 specific flag is defined for 8-bit parallel -#if defined (TFT_PARALLEL_8_BIT) - #if !defined (ESP32_PARALLEL) - #define ESP32_PARALLEL - #endif +#if defined(TFT_PARALLEL_8_BIT) +#if !defined(ESP32_PARALLEL) +#define ESP32_PARALLEL +#endif #endif // Processor specific code used by SPI bus transaction startWrite and endWrite functions -#if !defined (ESP32_PARALLEL) - #if (TFT_SPI_MODE == SPI_MODE1) || (TFT_SPI_MODE == SPI_MODE2) - #define SET_BUS_WRITE_MODE *_spi_user = SPI_USR_MOSI | SPI_CK_OUT_EDGE - #define SET_BUS_READ_MODE *_spi_user = SPI_USR_MOSI | SPI_USR_MISO | SPI_DOUTDIN | SPI_CK_OUT_EDGE - #else - #define SET_BUS_WRITE_MODE *_spi_user = SPI_USR_MOSI - #define SET_BUS_READ_MODE *_spi_user = SPI_USR_MOSI | SPI_USR_MISO | SPI_DOUTDIN - #endif +#if !defined(ESP32_PARALLEL) +#if (TFT_SPI_MODE == SPI_MODE1) || (TFT_SPI_MODE == SPI_MODE2) +#define SET_BUS_WRITE_MODE *_spi_user = SPI_USR_MOSI | SPI_CK_OUT_EDGE +#define SET_BUS_READ_MODE *_spi_user = SPI_USR_MOSI | SPI_USR_MISO | SPI_DOUTDIN | SPI_CK_OUT_EDGE #else - // Not applicable to parallel bus - #define SET_BUS_WRITE_MODE - #define SET_BUS_READ_MODE +#define SET_BUS_WRITE_MODE *_spi_user = SPI_USR_MOSI +#define SET_BUS_READ_MODE *_spi_user = SPI_USR_MOSI | SPI_USR_MISO | SPI_DOUTDIN +#endif +#else +// Not applicable to parallel bus +#define SET_BUS_WRITE_MODE +#define SET_BUS_READ_MODE #endif // Code to check if DMA is busy, used by SPI bus transaction transaction and endWrite functions #if !defined(TFT_PARALLEL_8_BIT) && !defined(SPI_18BIT_DRIVER) - #define ESP32_DMA - // Code to check if DMA is busy, used by SPI DMA + transaction + endWrite functions - #define DMA_BUSY_CHECK dmaWait() +#define ESP32_DMA +// Code to check if DMA is busy, used by SPI DMA + transaction + endWrite functions +#define DMA_BUSY_CHECK dmaWait() #else - #define DMA_BUSY_CHECK +#define DMA_BUSY_CHECK #endif #if defined(TFT_PARALLEL_8_BIT) - #define SPI_BUSY_CHECK +#define SPI_BUSY_CHECK #else - #define SPI_BUSY_CHECK while (*_spi_cmd&SPI_USR) +#define SPI_BUSY_CHECK while (*_spi_cmd & SPI_USR) #endif // If smooth font is used then it is likely SPIFFS will be needed #ifdef SMOOTH_FONT - // Call up the SPIFFS (SPI FLASH Filing System) for the anti-aliased fonts - #define FS_NO_GLOBALS - #include - #include "SPIFFS.h" // ESP32 only - #define FONT_FS_AVAILABLE +// Call up the SPIFFS (SPI FLASH Filing System) for the anti-aliased fonts +#define FS_NO_GLOBALS +#include "SPIFFS.h" // ESP32 only +#include +#define FONT_FS_AVAILABLE #endif //////////////////////////////////////////////////////////////////////////////////////// // Define the DC (TFT Data/Command or Register Select (RS))pin drive code //////////////////////////////////////////////////////////////////////////////////////// #ifndef TFT_DC - #define DC_C // No macro allocated so it generates no code - #define DC_D // No macro allocated so it generates no code +#define DC_C // No macro allocated so it generates no code +#define DC_D // No macro allocated so it generates no code +#else +#if defined(TFT_PARALLEL_8_BIT) +// TFT_DC, by design, must be in range 0-31 for single register parallel write +#if (TFT_DC >= 0) && (TFT_DC < 32) +#define DC_C GPIO.out_w1tc.val = (1 << TFT_DC) +#define DC_D GPIO.out_w1ts.val = (1 << TFT_DC) +#elif (TFT_DC >= 32) +#define DC_C GPIO.out_w1tc.val = (1 << (TFT_DC - 32)) +#define DC_D GPIO.out_w1ts.val = (1 << (TFT_DC - 32)) +#else +#define DC_C +#define DC_D +#endif +#else +#if (TFT_DC >= 32) +#ifdef RPI_DISPLAY_TYPE // RPi displays need a slower DC change +#define DC_C \ + GPIO.out_w1ts.val = (1 << (TFT_DC - 32)); \ + GPIO.out_w1tc.val = (1 << (TFT_DC - 32)) +#define DC_D \ + GPIO.out_w1tc.val = (1 << (TFT_DC - 32)); \ + GPIO.out_w1ts.val = (1 << (TFT_DC - 32)) #else - #if defined (TFT_PARALLEL_8_BIT) - // TFT_DC, by design, must be in range 0-31 for single register parallel write - #if (TFT_DC >= 0) && (TFT_DC < 32) - #define DC_C GPIO.out_w1tc.val = (1 << TFT_DC) - #define DC_D GPIO.out_w1ts.val = (1 << TFT_DC) - #elif (TFT_DC >= 32) - #define DC_C GPIO.out_w1tc.val = (1 << (TFT_DC- 32)) - #define DC_D GPIO.out_w1ts.val = (1 << (TFT_DC- 32)) - #else - #define DC_C - #define DC_D - #endif - #else - #if (TFT_DC >= 32) - #ifdef RPI_DISPLAY_TYPE // RPi displays need a slower DC change - #define DC_C GPIO.out_w1ts.val = (1 << (TFT_DC - 32)); \ - GPIO.out_w1tc.val = (1 << (TFT_DC - 32)) - #define DC_D GPIO.out_w1tc.val = (1 << (TFT_DC - 32)); \ - GPIO.out_w1ts.val = (1 << (TFT_DC - 32)) - #else - #define DC_C GPIO.out_w1tc.val = (1 << (TFT_DC - 32))//;GPIO.out_w1tc.val = (1 << (TFT_DC - 32)) - #define DC_D GPIO.out_w1ts.val = (1 << (TFT_DC - 32))//;GPIO.out_w1ts.val = (1 << (TFT_DC - 32)) - #endif - #elif (TFT_DC >= 0) - #if defined (RPI_DISPLAY_TYPE) - #if defined (ILI9486_DRIVER) - // RPi ILI9486 display needs a slower DC change - #define DC_C GPIO.out_w1tc.val = (1 << TFT_DC); \ - GPIO.out_w1tc.val = (1 << TFT_DC) - #define DC_D GPIO.out_w1tc.val = (1 << TFT_DC); \ - GPIO.out_w1ts.val = (1 << TFT_DC) - #else - // Other RPi displays need a slower C->D change - #define DC_C GPIO.out_w1tc.val = (1 << TFT_DC) - #define DC_D GPIO.out_w1tc.val = (1 << TFT_DC); \ - GPIO.out_w1ts.val = (1 << TFT_DC) - #endif - #else - #define DC_C GPIO.out_w1tc.val = (1 << TFT_DC)//;GPIO.out_w1tc.val = (1 << TFT_DC) - #define DC_D GPIO.out_w1ts.val = (1 << TFT_DC)//;GPIO.out_w1ts.val = (1 << TFT_DC) - #endif - #else - #define DC_C - #define DC_D - #endif - #endif +#define DC_C GPIO.out_w1tc.val = (1 << (TFT_DC - 32)) //;GPIO.out_w1tc.val = (1 << (TFT_DC - 32)) +#define DC_D GPIO.out_w1ts.val = (1 << (TFT_DC - 32)) //;GPIO.out_w1ts.val = (1 << (TFT_DC - 32)) +#endif +#elif (TFT_DC >= 0) +#if defined(RPI_DISPLAY_TYPE) +#if defined(ILI9486_DRIVER) +// RPi ILI9486 display needs a slower DC change +#define DC_C \ + GPIO.out_w1tc.val = (1 << TFT_DC); \ + GPIO.out_w1tc.val = (1 << TFT_DC) +#define DC_D \ + GPIO.out_w1tc.val = (1 << TFT_DC); \ + GPIO.out_w1ts.val = (1 << TFT_DC) +#else +// Other RPi displays need a slower C->D change +#define DC_C GPIO.out_w1tc.val = (1 << TFT_DC) +#define DC_D \ + GPIO.out_w1tc.val = (1 << TFT_DC); \ + GPIO.out_w1ts.val = (1 << TFT_DC) +#endif +#else +#define DC_C GPIO.out_w1tc.val = (1 << TFT_DC) //;GPIO.out_w1tc.val = (1 << TFT_DC) +#define DC_D GPIO.out_w1ts.val = (1 << TFT_DC) //;GPIO.out_w1ts.val = (1 << TFT_DC) +#endif +#else +#define DC_C +#define DC_D +#endif +#endif #endif //////////////////////////////////////////////////////////////////////////////////////// // Define the CS (TFT chip select) pin drive code //////////////////////////////////////////////////////////////////////////////////////// #ifndef TFT_CS - #define TFT_CS -1 // Keep DMA code happy - #define CS_L // No macro allocated so it generates no code - #define CS_H // No macro allocated so it generates no code +#define TFT_CS -1 // Keep DMA code happy +#define CS_L // No macro allocated so it generates no code +#define CS_H // No macro allocated so it generates no code +#else +#if defined(TFT_PARALLEL_8_BIT) +#if TFT_CS >= 32 +#define CS_L GPIO.out_w1tc.val = (1 << (TFT_CS - 32)) +#define CS_H GPIO.out_w1ts.val = (1 << (TFT_CS - 32)) +#elif TFT_CS >= 0 +#define CS_L GPIO.out_w1tc.val = (1 << TFT_CS) +#define CS_H GPIO.out_w1ts.val = (1 << TFT_CS) +#else +#define CS_L +#define CS_H +#endif +#else +#if (TFT_CS >= 32) +#ifdef RPI_DISPLAY_TYPE // RPi display needs a slower CS change +#define CS_L \ + GPIO.out_w1ts.val = (1 << (TFT_CS - 32)); \ + GPIO.out_w1tc.val = (1 << (TFT_CS - 32)) +#define CS_H \ + GPIO.out_w1tc.val = (1 << (TFT_CS - 32)); \ + GPIO.out_w1ts.val = (1 << (TFT_CS - 32)) +#else +#define CS_L \ + GPIO.out_w1tc.val = (1 << (TFT_CS - 32)); \ + GPIO.out_w1tc.val = (1 << (TFT_CS - 32)) +#define CS_H GPIO.out_w1ts.val = (1 << (TFT_CS - 32)) //;GPIO.out_w1ts.val = (1 << (TFT_CS - 32)) +#endif +#elif (TFT_CS >= 0) +#ifdef RPI_DISPLAY_TYPE // RPi display needs a slower CS change +#define CS_L \ + GPIO.out_w1ts.val = (1 << TFT_CS); \ + GPIO.out_w1tc.val = (1 << TFT_CS) +#define CS_H \ + GPIO.out_w1tc.val = (1 << TFT_CS); \ + GPIO.out_w1ts.val = (1 << TFT_CS) +#else +#define CS_L \ + GPIO.out_w1tc.val = (1 << TFT_CS); \ + GPIO.out_w1tc.val = (1 << TFT_CS) +#define CS_H GPIO.out_w1ts.val = (1 << TFT_CS) //;GPIO.out_w1ts.val = (1 << TFT_CS) +#endif #else - #if defined (TFT_PARALLEL_8_BIT) - #if TFT_CS >= 32 - #define CS_L GPIO.out_w1tc.val = (1 << (TFT_CS - 32)) - #define CS_H GPIO.out_w1ts.val = (1 << (TFT_CS - 32)) - #elif TFT_CS >= 0 - #define CS_L GPIO.out_w1tc.val = (1 << TFT_CS) - #define CS_H GPIO.out_w1ts.val = (1 << TFT_CS) - #else - #define CS_L - #define CS_H - #endif - #else - #if (TFT_CS >= 32) - #ifdef RPI_DISPLAY_TYPE // RPi display needs a slower CS change - #define CS_L GPIO.out_w1ts.val = (1 << (TFT_CS - 32)); \ - GPIO.out_w1tc.val = (1 << (TFT_CS - 32)) - #define CS_H GPIO.out_w1tc.val = (1 << (TFT_CS - 32)); \ - GPIO.out_w1ts.val = (1 << (TFT_CS - 32)) - #else - #define CS_L GPIO.out_w1tc.val = (1 << (TFT_CS - 32)); GPIO.out_w1tc.val = (1 << (TFT_CS - 32)) - #define CS_H GPIO.out_w1ts.val = (1 << (TFT_CS - 32))//;GPIO.out_w1ts.val = (1 << (TFT_CS - 32)) - #endif - #elif (TFT_CS >= 0) - #ifdef RPI_DISPLAY_TYPE // RPi display needs a slower CS change - #define CS_L GPIO.out_w1ts.val = (1 << TFT_CS); GPIO.out_w1tc.val = (1 << TFT_CS) - #define CS_H GPIO.out_w1tc.val = (1 << TFT_CS); GPIO.out_w1ts.val = (1 << TFT_CS) - #else - #define CS_L GPIO.out_w1tc.val = (1 << TFT_CS); GPIO.out_w1tc.val = (1 << TFT_CS) - #define CS_H GPIO.out_w1ts.val = (1 << TFT_CS)//;GPIO.out_w1ts.val = (1 << TFT_CS) - #endif - #else - #define CS_L - #define CS_H - #endif - #endif +#define CS_L +#define CS_H +#endif +#endif #endif //////////////////////////////////////////////////////////////////////////////////////// // Define the WR (TFT Write) pin drive code //////////////////////////////////////////////////////////////////////////////////////// -#if defined (TFT_WR) - #if (TFT_WR >= 32) - // Note: it will be ~1.25x faster if the TFT_WR pin uses a GPIO pin lower than 32 - #define WR_L GPIO.out_w1tc.val = (1 << (TFT_WR - 32)) - #define WR_H GPIO.out_w1ts.val = (1 << (TFT_WR - 32)) - #elif (TFT_WR >= 0) - // TFT_WR, for best performance, should be in range 0-31 for single register parallel write - #define WR_L GPIO.out_w1tc.val = (1 << TFT_WR) - #define WR_H GPIO.out_w1ts.val = (1 << TFT_WR) - #else - #define WR_L - #define WR_H - #endif +#if defined(TFT_WR) +#if (TFT_WR >= 32) +// Note: it will be ~1.25x faster if the TFT_WR pin uses a GPIO pin lower than 32 +#define WR_L GPIO.out_w1tc.val = (1 << (TFT_WR - 32)) +#define WR_H GPIO.out_w1ts.val = (1 << (TFT_WR - 32)) +#elif (TFT_WR >= 0) +// TFT_WR, for best performance, should be in range 0-31 for single register parallel write +#define WR_L GPIO.out_w1tc.val = (1 << TFT_WR) +#define WR_H GPIO.out_w1ts.val = (1 << TFT_WR) +#else +#define WR_L +#define WR_H +#endif #else - #define WR_L - #define WR_H +#define WR_L +#define WR_H #endif //////////////////////////////////////////////////////////////////////////////////////// // Define the touch screen chip select pin drive code //////////////////////////////////////////////////////////////////////////////////////// #ifndef TOUCH_CS - #define T_CS_L // No macro allocated so it generates no code - #define T_CS_H // No macro allocated so it generates no code -#else // XPT2046 is slow, so use slower digitalWrite here - #define T_CS_L digitalWrite(TOUCH_CS, LOW) - #define T_CS_H digitalWrite(TOUCH_CS, HIGH) +#define T_CS_L // No macro allocated so it generates no code +#define T_CS_H // No macro allocated so it generates no code +#else // XPT2046 is slow, so use slower digitalWrite here +#define T_CS_L digitalWrite(TOUCH_CS, LOW) +#define T_CS_H digitalWrite(TOUCH_CS, HIGH) #endif //////////////////////////////////////////////////////////////////////////////////////// // Make sure SPI default pins are assigned if not specified by user or set to -1 //////////////////////////////////////////////////////////////////////////////////////// -#if !defined (TFT_PARALLEL_8_BIT) - - #ifdef USE_HSPI_PORT - - #ifndef TFT_MISO - #define TFT_MISO -1 - #endif - - #ifndef TFT_MOSI - #define TFT_MOSI 13 - #endif - #if (TFT_MOSI == -1) - #undef TFT_MOSI - #define TFT_MOSI 13 - #endif - - #ifndef TFT_SCLK - #define TFT_SCLK 14 - #endif - #if (TFT_SCLK == -1) - #undef TFT_SCLK - #define TFT_SCLK 14 - #endif - - #else // VSPI port - - #ifndef TFT_MISO - #define TFT_MISO -1 - #endif - - #ifndef TFT_MOSI - #define TFT_MOSI 23 - #endif - #if (TFT_MOSI == -1) - #undef TFT_MOSI - #define TFT_MOSI 23 - #endif - - #ifndef TFT_SCLK - #define TFT_SCLK 18 - #endif - #if (TFT_SCLK == -1) - #undef TFT_SCLK - #define TFT_SCLK 18 - #endif - - #if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) - #if (TFT_MISO == -1) - #undef TFT_MISO - #define TFT_MISO TFT_MOSI - #endif - #endif - - #endif +#if !defined(TFT_PARALLEL_8_BIT) + +#ifdef USE_HSPI_PORT + +#ifndef TFT_MISO +#define TFT_MISO -1 +#endif + +#ifndef TFT_MOSI +#define TFT_MOSI 13 +#endif +#if (TFT_MOSI == -1) +#undef TFT_MOSI +#define TFT_MOSI 13 +#endif + +#ifndef TFT_SCLK +#define TFT_SCLK 14 +#endif +#if (TFT_SCLK == -1) +#undef TFT_SCLK +#define TFT_SCLK 14 +#endif + +#else // VSPI port + +#ifndef TFT_MISO +#define TFT_MISO -1 +#endif + +#ifndef TFT_MOSI +#define TFT_MOSI 23 +#endif +#if (TFT_MOSI == -1) +#undef TFT_MOSI +#define TFT_MOSI 23 +#endif + +#ifndef TFT_SCLK +#define TFT_SCLK 18 +#endif +#if (TFT_SCLK == -1) +#undef TFT_SCLK +#define TFT_SCLK 18 +#endif + +#if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) || \ + defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32C6) +#if (TFT_MISO == -1) +#undef TFT_MISO +#define TFT_MISO TFT_MOSI +#endif +#endif + +#endif #endif //////////////////////////////////////////////////////////////////////////////////////// // Define the parallel bus interface chip pin drive code //////////////////////////////////////////////////////////////////////////////////////// -#if defined (TFT_PARALLEL_8_BIT) - - // Create a bit set lookup table for data bus - wastes 1kbyte of RAM but speeds things up dramatically - // can then use e.g. GPIO.out_w1ts.val = set_mask(0xFF); to set data bus to 0xFF - #define PARALLEL_INIT_TFT_DATA_BUS \ - for (int32_t c = 0; c<256; c++) \ - { \ - xset_mask[c] = 0; \ - if ( c & 0x01 ) xset_mask[c] |= (1 << TFT_D0); \ - if ( c & 0x02 ) xset_mask[c] |= (1 << TFT_D1); \ - if ( c & 0x04 ) xset_mask[c] |= (1 << TFT_D2); \ - if ( c & 0x08 ) xset_mask[c] |= (1 << TFT_D3); \ - if ( c & 0x10 ) xset_mask[c] |= (1 << TFT_D4); \ - if ( c & 0x20 ) xset_mask[c] |= (1 << TFT_D5); \ - if ( c & 0x40 ) xset_mask[c] |= (1 << TFT_D6); \ - if ( c & 0x80 ) xset_mask[c] |= (1 << TFT_D7); \ - } \ - - // Mask for the 8 data bits to set pin directions - #define GPIO_DIR_MASK ((1 << TFT_D0) | (1 << TFT_D1) | (1 << TFT_D2) | (1 << TFT_D3) | (1 << TFT_D4) | (1 << TFT_D5) | (1 << TFT_D6) | (1 << TFT_D7)) - - #if (TFT_WR >= 32) - // Data bits and the write line are cleared sequentially - #define GPIO_OUT_CLR_MASK (GPIO_DIR_MASK); WR_L - #elif (TFT_WR >= 0) - // Data bits and the write line are cleared to 0 in one step (1.25x faster) - #define GPIO_OUT_CLR_MASK (GPIO_DIR_MASK | (1 << TFT_WR)) - #else - #define GPIO_OUT_CLR_MASK - #endif - - // A lookup table is used to set the different bit patterns, this uses 1kByte of RAM - #define set_mask(C) xset_mask[C] // 63fps Sprite rendering test 33% faster, graphicstest only 1.8% faster than shifting in real time - - // Real-time shifting alternative to above to save 1KByte RAM, 47 fps Sprite rendering test - /*#define set_mask(C) (((C)&0x80)>>7)<>6)<>5)<>4)<>3)<>2)<>1)<>0)<> 8)); WR_H; \ - GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; GPIO.out_w1ts.val = set_mask((uint8_t) (((C) & 0x07E0)>> 3)); WR_H; \ - GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; GPIO.out_w1ts.val = set_mask((uint8_t) (((C) & 0x001F)<< 3)); WR_H - - // 18-bit color write with swapped bytes - #define tft_Write_16S(C) Cswap = ((C) >>8 | (C) << 8); tft_Write_16(Cswap) - - #else - - #ifdef PSEUDO_16_BIT - // One write strobe for both bytes - #define tft_Write_16(C) GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; GPIO.out_w1ts.val = set_mask((uint8_t) ((C) >> 0)); WR_H - #define tft_Write_16S(C) GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; GPIO.out_w1ts.val = set_mask((uint8_t) ((C) >> 8)); WR_H - #else - // Write 16 bits to TFT - #define tft_Write_16(C) GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; GPIO.out_w1ts.val = set_mask((uint8_t) ((C) >> 8)); WR_H; \ - GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; GPIO.out_w1ts.val = set_mask((uint8_t) ((C) >> 0)); WR_H - - // 16-bit write with swapped bytes - #define tft_Write_16S(C) GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; GPIO.out_w1ts.val = set_mask((uint8_t) ((C) >> 0)); WR_H; \ - GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; GPIO.out_w1ts.val = set_mask((uint8_t) ((C) >> 8)); WR_H - #endif - - #endif - - // Write 32 bits to TFT - #define tft_Write_32(C) GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; GPIO.out_w1ts.val = set_mask((uint8_t) ((C) >> 24)); WR_H; \ - GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; GPIO.out_w1ts.val = set_mask((uint8_t) ((C) >> 16)); WR_H; \ - GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; GPIO.out_w1ts.val = set_mask((uint8_t) ((C) >> 8)); WR_H; \ - GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; GPIO.out_w1ts.val = set_mask((uint8_t) ((C) >> 0)); WR_H - - // Write two concatenated 16-bit values to TFT - #define tft_Write_32C(C,D) GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; GPIO.out_w1ts.val = set_mask((uint8_t) ((C) >> 8)); WR_H; \ - GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; GPIO.out_w1ts.val = set_mask((uint8_t) ((C) >> 0)); WR_H; \ - GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; GPIO.out_w1ts.val = set_mask((uint8_t) ((D) >> 8)); WR_H; \ - GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; GPIO.out_w1ts.val = set_mask((uint8_t) ((D) >> 0)); WR_H - - // Write 16-bit value twice to TFT - used by drawPixel() - #define tft_Write_32D(C) GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; GPIO.out_w1ts.val = set_mask((uint8_t) ((C) >> 8)); WR_H; \ - GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; GPIO.out_w1ts.val = set_mask((uint8_t) ((C) >> 0)); WR_H; \ - GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; GPIO.out_w1ts.val = set_mask((uint8_t) ((C) >> 8)); WR_H; \ - GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; GPIO.out_w1ts.val = set_mask((uint8_t) ((C) >> 0)); WR_H - - // Read pin - #ifdef TFT_RD - #if (TFT_RD >= 32) - #define RD_L GPIO.out_w1tc.val = (1 << (TFT_RD - 32)) - #define RD_H GPIO.out_w1ts.val = (1 << (TFT_RD - 32)) - #elif (TFT_RD >= 0) - #define RD_L GPIO.out_w1tc.val = (1 << TFT_RD) - //#define RD_L digitalWrite(TFT_WR, LOW) - #define RD_H GPIO.out_w1ts.val = (1 << TFT_RD) - //#define RD_H digitalWrite(TFT_WR, HIGH) - #else - #define RD_L - #define RD_H - #endif - #else - #define TFT_RD -1 - #define RD_L - #define RD_H - #endif +#if defined(TFT_PARALLEL_8_BIT) + +// Create a bit set lookup table for data bus - wastes 1kbyte of RAM but speeds things up dramatically +// can then use e.g. GPIO.out_w1ts.val = set_mask(0xFF); to set data bus to 0xFF +#define PARALLEL_INIT_TFT_DATA_BUS \ + for (int32_t c = 0; c < 256; c++) { \ + xset_mask[c] = 0; \ + if (c & 0x01) xset_mask[c] |= (1 << TFT_D0); \ + if (c & 0x02) xset_mask[c] |= (1 << TFT_D1); \ + if (c & 0x04) xset_mask[c] |= (1 << TFT_D2); \ + if (c & 0x08) xset_mask[c] |= (1 << TFT_D3); \ + if (c & 0x10) xset_mask[c] |= (1 << TFT_D4); \ + if (c & 0x20) xset_mask[c] |= (1 << TFT_D5); \ + if (c & 0x40) xset_mask[c] |= (1 << TFT_D6); \ + if (c & 0x80) xset_mask[c] |= (1 << TFT_D7); \ + } + +// Mask for the 8 data bits to set pin directions +#define GPIO_DIR_MASK \ + ((1 << TFT_D0) | (1 << TFT_D1) | (1 << TFT_D2) | (1 << TFT_D3) | (1 << TFT_D4) | (1 << TFT_D5) | \ + (1 << TFT_D6) | (1 << TFT_D7)) + +#if (TFT_WR >= 32) +// Data bits and the write line are cleared sequentially +#define GPIO_OUT_CLR_MASK \ + (GPIO_DIR_MASK); \ + WR_L +#elif (TFT_WR >= 0) +// Data bits and the write line are cleared to 0 in one step (1.25x faster) +#define GPIO_OUT_CLR_MASK (GPIO_DIR_MASK | (1 << TFT_WR)) +#else +#define GPIO_OUT_CLR_MASK +#endif + +// A lookup table is used to set the different bit patterns, this uses 1kByte of RAM +#define set_mask(C) \ + xset_mask[C] // 63fps Sprite rendering test 33% faster, graphicstest only 1.8% faster than shifting in + // real time + +// Real-time shifting alternative to above to save 1KByte RAM, 47 fps Sprite rendering test +/*#define set_mask(C) (((C)&0x80)>>7)<>6)<>5)<>4)<>3)<>2)<>1)<>0)<> 8)); \ + WR_H; \ + GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts.val = set_mask((uint8_t)(((C) & 0x07E0) >> 3)); \ + WR_H; \ + GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts.val = set_mask((uint8_t)(((C) & 0x001F) << 3)); \ + WR_H + +// 18-bit color write with swapped bytes +#define tft_Write_16S(C) \ + Cswap = ((C) >> 8 | (C) << 8); \ + tft_Write_16(Cswap) + +#else + +#ifdef PSEUDO_16_BIT +// One write strobe for both bytes +#define tft_Write_16(C) \ + GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts.val = set_mask((uint8_t)((C) >> 0)); \ + WR_H +#define tft_Write_16S(C) \ + GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts.val = set_mask((uint8_t)((C) >> 8)); \ + WR_H +#else +// Write 16 bits to TFT +#define tft_Write_16(C) \ + GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts.val = set_mask((uint8_t)((C) >> 8)); \ + WR_H; \ + GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts.val = set_mask((uint8_t)((C) >> 0)); \ + WR_H + +// 16-bit write with swapped bytes +#define tft_Write_16S(C) \ + GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts.val = set_mask((uint8_t)((C) >> 0)); \ + WR_H; \ + GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts.val = set_mask((uint8_t)((C) >> 8)); \ + WR_H +#endif + +#endif + +// Write 32 bits to TFT +#define tft_Write_32(C) \ + GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts.val = set_mask((uint8_t)((C) >> 24)); \ + WR_H; \ + GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts.val = set_mask((uint8_t)((C) >> 16)); \ + WR_H; \ + GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts.val = set_mask((uint8_t)((C) >> 8)); \ + WR_H; \ + GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts.val = set_mask((uint8_t)((C) >> 0)); \ + WR_H + +// Write two concatenated 16-bit values to TFT +#define tft_Write_32C(C, D) \ + GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts.val = set_mask((uint8_t)((C) >> 8)); \ + WR_H; \ + GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts.val = set_mask((uint8_t)((C) >> 0)); \ + WR_H; \ + GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts.val = set_mask((uint8_t)((D) >> 8)); \ + WR_H; \ + GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts.val = set_mask((uint8_t)((D) >> 0)); \ + WR_H + +// Write 16-bit value twice to TFT - used by drawPixel() +#define tft_Write_32D(C) \ + GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts.val = set_mask((uint8_t)((C) >> 8)); \ + WR_H; \ + GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts.val = set_mask((uint8_t)((C) >> 0)); \ + WR_H; \ + GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts.val = set_mask((uint8_t)((C) >> 8)); \ + WR_H; \ + GPIO.out_w1tc.val = GPIO_OUT_CLR_MASK; \ + GPIO.out_w1ts.val = set_mask((uint8_t)((C) >> 0)); \ + WR_H + +// Read pin +#ifdef TFT_RD +#if (TFT_RD >= 32) +#define RD_L GPIO.out_w1tc.val = (1 << (TFT_RD - 32)) +#define RD_H GPIO.out_w1ts.val = (1 << (TFT_RD - 32)) +#elif (TFT_RD >= 0) +#define RD_L GPIO.out_w1tc.val = (1 << TFT_RD) +// #define RD_L digitalWrite(TFT_WR, LOW) +#define RD_H GPIO.out_w1ts.val = (1 << TFT_RD) +// #define RD_H digitalWrite(TFT_WR, HIGH) +#else +#define RD_L +#define RD_H +#endif +#else +#define TFT_RD -1 +#define RD_L +#define RD_H +#endif //////////////////////////////////////////////////////////////////////////////////////// // Macros to write commands/pixel colour data to a SPI ILI948x TFT //////////////////////////////////////////////////////////////////////////////////////// -#elif defined (SPI_18BIT_DRIVER) // SPI 18-bit colour +#elif defined(SPI_18BIT_DRIVER) // SPI 18-bit colour - // Write 8 bits to TFT - #define tft_Write_8(C) spi.transfer(C) +// Write 8 bits to TFT +#define tft_Write_8(C) spi.transfer(C) - // Convert 16-bit colour to 18-bit and write in 3 bytes - #define tft_Write_16(C) spi.transfer(((C) & 0xF800)>>8); \ - spi.transfer(((C) & 0x07E0)>>3); \ - spi.transfer(((C) & 0x001F)<<3) +// Convert 16-bit colour to 18-bit and write in 3 bytes +#define tft_Write_16(C) \ + spi.transfer(((C) & 0xF800) >> 8); \ + spi.transfer(((C) & 0x07E0) >> 3); \ + spi.transfer(((C) & 0x001F) << 3) - // Future option for transfer without wait - #define tft_Write_16N(C) tft_Write_16(C) +// Future option for transfer without wait +#define tft_Write_16N(C) tft_Write_16(C) - // Convert swapped byte 16-bit colour to 18-bit and write in 3 bytes - #define tft_Write_16S(C) spi.transfer((C) & 0xF8); \ - spi.transfer(((C) & 0xE000)>>11 | ((C) & 0x07)<<5); \ - spi.transfer(((C) & 0x1F00)>>5) +// Convert swapped byte 16-bit colour to 18-bit and write in 3 bytes +#define tft_Write_16S(C) \ + spi.transfer((C) & 0xF8); \ + spi.transfer(((C) & 0xE000) >> 11 | ((C) & 0x07) << 5); \ + spi.transfer(((C) & 0x1F00) >> 5) - // Write 32 bits to TFT - #define tft_Write_32(C) spi.write32(C) +// Write 32 bits to TFT +#define tft_Write_32(C) spi.write32(C) - // Write two concatenated 16-bit values to TFT - #define tft_Write_32C(C,D) spi.write32((C)<<16 | (D)) +// Write two concatenated 16-bit values to TFT +#define tft_Write_32C(C, D) spi.write32((C) << 16 | (D)) - // Write 16-bit value twice to TFT - #define tft_Write_32D(C) spi.write32((C)<<16 | (C)) +// Write 16-bit value twice to TFT +#define tft_Write_32D(C) spi.write32((C) << 16 | (C)) //////////////////////////////////////////////////////////////////////////////////////// // Macros to write commands/pixel colour data to an Raspberry Pi TFT //////////////////////////////////////////////////////////////////////////////////////// -#elif defined (RPI_DISPLAY_TYPE) +#elif defined(RPI_DISPLAY_TYPE) - // ESP32 low level SPI writes for 8, 16 and 32-bit values - // to avoid the function call overhead - #define TFT_WRITE_BITS(D, B) \ - WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), B-1); \ - WRITE_PERI_REG(SPI_W0_REG(SPI_PORT), D); \ - SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); \ - while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); +// ESP32 low level SPI writes for 8, 16 and 32-bit values +// to avoid the function call overhead +#define TFT_WRITE_BITS(D, B) \ + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), B - 1); \ + WRITE_PERI_REG(SPI_W0_REG(SPI_PORT), D); \ + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); \ + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT)) & SPI_USR); - // Write 8 bits - #define tft_Write_8(C) TFT_WRITE_BITS((C)<<8, 16) +// Write 8 bits +#define tft_Write_8(C) TFT_WRITE_BITS((C) << 8, 16) - // Write 16 bits with corrected endianness for 16-bit colours - #define tft_Write_16(C) TFT_WRITE_BITS((C)<<8 | (C)>>8, 16) +// Write 16 bits with corrected endianness for 16-bit colours +#define tft_Write_16(C) TFT_WRITE_BITS((C) << 8 | (C) >> 8, 16) - // Future option for transfer without wait - #define tft_Write_16N(C) tft_Write_16(C) +// Future option for transfer without wait +#define tft_Write_16N(C) tft_Write_16(C) - // Write 16 bits - #define tft_Write_16S(C) TFT_WRITE_BITS(C, 16) +// Write 16 bits +#define tft_Write_16S(C) TFT_WRITE_BITS(C, 16) - // Write 32 bits - #define tft_Write_32(C) TFT_WRITE_BITS(C, 32) +// Write 32 bits +#define tft_Write_32(C) TFT_WRITE_BITS(C, 32) - // Write two address coordinates - #define tft_Write_32C(C,D) TFT_WRITE_BITS((C)<<24 | (C), 32); \ - TFT_WRITE_BITS((D)<<24 | (D), 32) +// Write two address coordinates +#define tft_Write_32C(C, D) \ + TFT_WRITE_BITS((C) << 24 | (C), 32); \ + TFT_WRITE_BITS((D) << 24 | (D), 32) - // Write same value twice - #define tft_Write_32D(C) tft_Write_32C(C,C) +// Write same value twice +#define tft_Write_32D(C) tft_Write_32C(C, C) //////////////////////////////////////////////////////////////////////////////////////// // Macros for all other SPI displays @@ -532,67 +616,75 @@ SPI3_HOST = 2 #define tft_Write_32D(C) TFT_WRITE_BITS((uint16_t)((C)<<8 | (C)>>8)<<16 | (uint16_t)((C)<<8 | (C)>>8), 32) //*/ //* Replacement slimmer macros - #if !defined(CONFIG_IDF_TARGET_ESP32C3) - #define TFT_WRITE_BITS(D, B) *_spi_mosi_dlen = B-1; \ - *_spi_w = D; \ - *_spi_cmd = SPI_USR; \ - while (*_spi_cmd & SPI_USR); - #else - #define TFT_WRITE_BITS(D, B) *_spi_mosi_dlen = B-1; \ - *_spi_w = D; \ - *_spi_cmd = SPI_UPDATE; \ - while (*_spi_cmd & SPI_UPDATE); \ - *_spi_cmd = SPI_USR; \ - while (*_spi_cmd & SPI_USR); - #endif - // Write 8 bits - #define tft_Write_8(C) TFT_WRITE_BITS(C, 8) - - // Write 16 bits with corrected endianness for 16-bit colours - #define tft_Write_16(C) TFT_WRITE_BITS((C)<<8 | (C)>>8, 16) +#if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) && \ + !defined(CONFIG_IDF_TARGET_ESP32C6) +#define TFT_WRITE_BITS(D, B) \ + *_spi_mosi_dlen = B - 1; \ + *_spi_w = D; \ + *_spi_cmd = SPI_USR; \ + while (*_spi_cmd & SPI_USR); +#else +#define TFT_WRITE_BITS(D, B) \ + *_spi_mosi_dlen = B - 1; \ + *_spi_w = D; \ + *_spi_cmd = SPI_UPDATE; \ + while (*_spi_cmd & SPI_UPDATE); \ + *_spi_cmd = SPI_USR; \ + while (*_spi_cmd & SPI_USR); +#endif +// Write 8 bits +#define tft_Write_8(C) TFT_WRITE_BITS(C, 8) + +// Write 16 bits with corrected endianness for 16-bit colours +#define tft_Write_16(C) TFT_WRITE_BITS((C) << 8 | (C) >> 8, 16) + +// Future option for transfer without wait +#if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32C5) && \ + !defined(CONFIG_IDF_TARGET_ESP32C6) +#define tft_Write_16N(C) \ + *_spi_mosi_dlen = 16 - 1; \ + *_spi_w = ((C) << 8 | (C) >> 8); \ + *_spi_cmd = SPI_USR; +#else +#define tft_Write_16N(C) \ + *_spi_mosi_dlen = 16 - 1; \ + *_spi_w = ((C) << 8 | (C) >> 8); \ + *_spi_cmd = SPI_UPDATE; \ + while (*_spi_cmd & SPI_UPDATE); \ + *_spi_cmd = SPI_USR; +#endif - // Future option for transfer without wait - #if !defined(CONFIG_IDF_TARGET_ESP32C3) - #define tft_Write_16N(C) *_spi_mosi_dlen = 16-1; \ - *_spi_w = ((C)<<8 | (C)>>8); \ - *_spi_cmd = SPI_USR; - #else - #define tft_Write_16N(C) *_spi_mosi_dlen = 16-1; \ - *_spi_w = ((C)<<8 | (C)>>8); \ - *_spi_cmd = SPI_UPDATE; \ - while (*_spi_cmd & SPI_UPDATE); \ - *_spi_cmd = SPI_USR; - #endif +// Write 16 bits +#define tft_Write_16S(C) TFT_WRITE_BITS(C, 16) - // Write 16 bits - #define tft_Write_16S(C) TFT_WRITE_BITS(C, 16) +// Write 32 bits +#define tft_Write_32(C) TFT_WRITE_BITS(C, 32) - // Write 32 bits - #define tft_Write_32(C) TFT_WRITE_BITS(C, 32) - - // Write two address coordinates - #define tft_Write_32C(C,D) TFT_WRITE_BITS((uint16_t)((D)<<8 | (D)>>8)<<16 | (uint16_t)((C)<<8 | (C)>>8), 32) +// Write two address coordinates +#define tft_Write_32C(C, D) \ + TFT_WRITE_BITS((uint16_t)((D) << 8 | (D) >> 8) << 16 | (uint16_t)((C) << 8 | (C) >> 8), 32) - // Write same value twice - #define tft_Write_32D(C) TFT_WRITE_BITS((uint16_t)((C)<<8 | (C)>>8)<<16 | (uint16_t)((C)<<8 | (C)>>8), 32) +// Write same value twice +#define tft_Write_32D(C) \ + TFT_WRITE_BITS((uint16_t)((C) << 8 | (C) >> 8) << 16 | (uint16_t)((C) << 8 | (C) >> 8), 32) //*/ #endif #ifndef tft_Write_16N - #define tft_Write_16N tft_Write_16 +#define tft_Write_16N tft_Write_16 #endif //////////////////////////////////////////////////////////////////////////////////////// // Macros to read from display using SPI or software SPI //////////////////////////////////////////////////////////////////////////////////////// -#if !defined (TFT_PARALLEL_8_BIT) - // Read from display using SPI or software SPI - // Use a SPI read transfer - #define tft_Read_8() spi.transfer(0) +#if !defined(TFT_PARALLEL_8_BIT) +// Read from display using SPI or software SPI +// Use a SPI read transfer +#define tft_Read_8() spi.transfer(0) #endif // Concatenate a byte sequence A,B,C,D to CDAB, P is a uint8_t pointer -#define DAT8TO32(P) ( (uint32_t)P[0]<<8 | P[1] | P[2]<<24 | P[3]<<16 ) +#define DAT8TO32(P) ((uint32_t)P[0] << 8 | P[1] | P[2] << 24 | P[3] << 16) #endif // Header end diff --git a/lib/TFT_eSPI/TFT_eSPI.h b/lib/TFT_eSPI/TFT_eSPI.h index cf3232f26..ee354dbde 100644 --- a/lib/TFT_eSPI/TFT_eSPI.h +++ b/lib/TFT_eSPI/TFT_eSPI.h @@ -26,11 +26,11 @@ ** Section 1: Load required header files ***************************************************************************************/ -//Standard support +// Standard support #include #include -#if !defined (TFT_PARALLEL_8_BIT) && !defined (RP2040_PIO_INTERFACE) - #include +#if !defined(TFT_PARALLEL_8_BIT) && !defined(RP2040_PIO_INTERFACE) +#include #endif /*************************************************************************************** ** Section 2: Load library and processor specific header files @@ -38,122 +38,125 @@ // Include header file that defines the fonts loaded, the TFT drivers // available and the pins to be used, etc. etc. #ifdef CONFIG_TFT_eSPI_ESPIDF - #include "TFT_config.h" +#include "TFT_config.h" #endif // New ESP8266 board package uses ARDUINO_ARCH_ESP8266 // old package defined ESP8266 -#if defined (ESP8266) - #ifndef ARDUINO_ARCH_ESP8266 - #define ARDUINO_ARCH_ESP8266 - #endif +#if defined(ESP8266) +#ifndef ARDUINO_ARCH_ESP8266 +#define ARDUINO_ARCH_ESP8266 +#endif #endif // The following lines allow the user setup to be included in the sketch folder, see // "Sketch_with_tft_setup" generic example. #if !defined __has_include - #if !defined(DISABLE_ALL_LIBRARY_WARNINGS) - #warning Compiler does not support __has_include, so sketches cannot define the setup - #endif +#if !defined(DISABLE_ALL_LIBRARY_WARNINGS) +#warning Compiler does not support __has_include, so sketches cannot define the setup +#endif #else - #if __has_include() - // Include the sketch setup file - #include - #ifndef USER_SETUP_LOADED - // Prevent loading further setups - #define USER_SETUP_LOADED - #endif - #endif +#if __has_include() +// Include the sketch setup file +#include +#ifndef USER_SETUP_LOADED +// Prevent loading further setups +#define USER_SETUP_LOADED +#endif +#endif #endif #include // Handle FLASH based storage e.g. PROGMEM #if defined(ARDUINO_ARCH_RP2040) - #undef pgm_read_byte - #define pgm_read_byte(addr) (*(const unsigned char *)(addr)) - #undef pgm_read_word - #define pgm_read_word(addr) ({ \ - typeof(addr) _addr = (addr); \ - *(const unsigned short *)(_addr); \ - }) - #undef pgm_read_dword - #define pgm_read_dword(addr) ({ \ - typeof(addr) _addr = (addr); \ - *(const unsigned long *)(_addr); \ - }) +#undef pgm_read_byte +#define pgm_read_byte(addr) (*(const unsigned char *)(addr)) +#undef pgm_read_word +#define pgm_read_word(addr) \ + ({ \ + typeof(addr) _addr = (addr); \ + *(const unsigned short *)(_addr); \ + }) +#undef pgm_read_dword +#define pgm_read_dword(addr) \ + ({ \ + typeof(addr) _addr = (addr); \ + *(const unsigned long *)(_addr); \ + }) #elif defined(__AVR__) - #include +#include #elif defined(ARDUINO_ARCH_ESP8266) || defined(ESP32) - #include +#include #else - #ifndef PROGMEM - #define PROGMEM - #endif +#ifndef PROGMEM +#define PROGMEM +#endif #endif // Include the processor specific drivers #if defined(CONFIG_IDF_TARGET_ESP32S3) - #include "Processors/TFT_eSPI_ESP32_S3.h" -#elif defined(CONFIG_IDF_TARGET_ESP32C3) - #include "Processors/TFT_eSPI_ESP32_C3.h" -#elif defined (ESP32) - #include "Processors/TFT_eSPI_ESP32.h" -#elif defined (ARDUINO_ARCH_ESP8266) - #include "Processors/TFT_eSPI_ESP8266.h" -#elif defined (STM32) - #include "Processors/TFT_eSPI_STM32.h" +#include "Processors/TFT_eSPI_ESP32_S3.h" +#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) || \ + defined(CONFIG_IDF_TARGET_ESP32C6) +#include "Processors/TFT_eSPI_ESP32_C3.h" +#elif defined(ESP32) +#include "Processors/TFT_eSPI_ESP32.h" +#elif defined(ARDUINO_ARCH_ESP8266) +#include "Processors/TFT_eSPI_ESP8266.h" +#elif defined(STM32) +#include "Processors/TFT_eSPI_STM32.h" #elif defined(ARDUINO_ARCH_RP2040) - #include "Processors/TFT_eSPI_RP2040.h" +#include "Processors/TFT_eSPI_RP2040.h" #else - #include "Processors/TFT_eSPI_Generic.h" - #define GENERIC_PROCESSOR +#include "Processors/TFT_eSPI_Generic.h" +#define GENERIC_PROCESSOR #endif /*************************************************************************************** ** Section 3: Interface setup ***************************************************************************************/ #ifndef TAB_COLOUR - #define TAB_COLOUR 0 +#define TAB_COLOUR 0 #endif // If the SPI frequency is not defined, set a default #ifndef SPI_FREQUENCY - #define SPI_FREQUENCY 20000000 +#define SPI_FREQUENCY 20000000 #endif // If the SPI read frequency is not defined, set a default #ifndef SPI_READ_FREQUENCY - #define SPI_READ_FREQUENCY 10000000 +#define SPI_READ_FREQUENCY 10000000 #endif // Some ST7789 boards do not work with Mode 0 #ifndef TFT_SPI_MODE - #if defined(ST7789_DRIVER) || defined(ST7789_2_DRIVER) - #define TFT_SPI_MODE SPI_MODE3 - #else - #define TFT_SPI_MODE SPI_MODE0 - #endif +#if defined(ST7789_DRIVER) || defined(ST7789_2_DRIVER) +#define TFT_SPI_MODE SPI_MODE3 +#else +#define TFT_SPI_MODE SPI_MODE0 +#endif #endif // If the XPT2046 SPI frequency is not defined, set a default #ifndef SPI_TOUCH_FREQUENCY - #define SPI_TOUCH_FREQUENCY 2500000 +#define SPI_TOUCH_FREQUENCY 2500000 #endif #ifndef SPI_BUSY_CHECK - #define SPI_BUSY_CHECK +#define SPI_BUSY_CHECK #endif // If half duplex SDA mode is defined then MISO pin should be -1 #ifdef TFT_SDA_READ - #ifdef TFT_MISO - #if TFT_MISO != -1 - #undef TFT_MISO - #define TFT_MISO -1 - #warning TFT_MISO set to -1 - #endif - #endif +#ifdef TFT_MISO +#if TFT_MISO != -1 +#undef TFT_MISO +#define TFT_MISO -1 +#warning TFT_MISO set to -1 +#endif +#endif #endif /*************************************************************************************** @@ -162,65 +165,65 @@ // Use GLCD font in error case where user requests a smooth font file // that does not exist (this is a temporary fix to stop ESP32 reboot) #ifdef SMOOTH_FONT - #ifndef LOAD_GLCD - #define LOAD_GLCD - #endif +#ifndef LOAD_GLCD +#define LOAD_GLCD +#endif #endif // Only load the fonts defined in User_Setup.h (to save space) // Set flag so RLE rendering code is optionally compiled #ifdef LOAD_GLCD - #include +#include #endif #ifdef LOAD_FONT2 - #include +#include #endif #ifdef LOAD_FONT4 - #include - #define LOAD_RLE +#include +#define LOAD_RLE #endif #ifdef LOAD_FONT6 - #include - #ifndef LOAD_RLE - #define LOAD_RLE - #endif +#include +#ifndef LOAD_RLE +#define LOAD_RLE +#endif #endif #ifdef LOAD_FONT7 - #include - #ifndef LOAD_RLE - #define LOAD_RLE - #endif +#include +#ifndef LOAD_RLE +#define LOAD_RLE +#endif #endif #ifdef LOAD_FONT8 - #include - #ifndef LOAD_RLE - #define LOAD_RLE - #endif +#include +#ifndef LOAD_RLE +#define LOAD_RLE +#endif #elif defined LOAD_FONT8N // Optional narrower version - #define LOAD_FONT8 - #include - #ifndef LOAD_RLE - #define LOAD_RLE - #endif +#define LOAD_FONT8 +#include +#ifndef LOAD_RLE +#define LOAD_RLE +#endif #endif #ifdef LOAD_GFXFF - // We can include all the free fonts and they will only be built into - // the sketch if they are used - #include - // Call up any user custom fonts - #include +// We can include all the free fonts and they will only be built into +// the sketch if they are used +#include +// Call up any user custom fonts +#include #endif // #ifdef LOAD_GFXFF // Create a null default font in case some fonts not used (to prevent crash) -const uint8_t widtbl_null[1] = {0}; +const uint8_t widtbl_null[1] = {0}; PROGMEM const uint8_t chr_null[1] = {0}; -PROGMEM const uint8_t* const chrtbl_null[1] = {chr_null}; +PROGMEM const uint8_t *const chrtbl_null[1] = {chr_null}; // This is a structure to conveniently hold information on the default fonts // Stores pointer to font character image address table, width table and height @@ -229,72 +232,72 @@ typedef struct { const uint8_t *widthtbl; uint8_t height; uint8_t baseline; - } fontinfo; +} fontinfo; // Now fill the structure -const PROGMEM fontinfo fontdata [] = { - #ifdef LOAD_GLCD - { (const uint8_t *)font, widtbl_null, 0, 0 }, - #else - { (const uint8_t *)chrtbl_null, widtbl_null, 0, 0 }, - #endif - // GLCD font (Font 1) does not have all parameters - { (const uint8_t *)chrtbl_null, widtbl_null, 8, 7 }, - - #ifdef LOAD_FONT2 - { (const uint8_t *)chrtbl_f16, widtbl_f16, chr_hgt_f16, baseline_f16}, - #else - { (const uint8_t *)chrtbl_null, widtbl_null, 0, 0 }, - #endif - - // Font 3 current unused - { (const uint8_t *)chrtbl_null, widtbl_null, 0, 0 }, - - #ifdef LOAD_FONT4 - { (const uint8_t *)chrtbl_f32, widtbl_f32, chr_hgt_f32, baseline_f32}, - #else - { (const uint8_t *)chrtbl_null, widtbl_null, 0, 0 }, - #endif - - // Font 5 current unused - { (const uint8_t *)chrtbl_null, widtbl_null, 0, 0 }, - - #ifdef LOAD_FONT6 - { (const uint8_t *)chrtbl_f64, widtbl_f64, chr_hgt_f64, baseline_f64}, - #else - { (const uint8_t *)chrtbl_null, widtbl_null, 0, 0 }, - #endif - - #ifdef LOAD_FONT7 - { (const uint8_t *)chrtbl_f7s, widtbl_f7s, chr_hgt_f7s, baseline_f7s}, - #else - { (const uint8_t *)chrtbl_null, widtbl_null, 0, 0 }, - #endif - - #ifdef LOAD_FONT8 - { (const uint8_t *)chrtbl_f72, widtbl_f72, chr_hgt_f72, baseline_f72} - #else - { (const uint8_t *)chrtbl_null, widtbl_null, 0, 0 } - #endif +const PROGMEM fontinfo fontdata[] = { +#ifdef LOAD_GLCD + {(const uint8_t *)font, widtbl_null, 0, 0 }, +#else + {(const uint8_t *)chrtbl_null, widtbl_null, 0, 0}, +#endif + // GLCD font (Font 1) does not have all parameters + {(const uint8_t *)chrtbl_null, widtbl_null, 8, 7 }, + +#ifdef LOAD_FONT2 + {(const uint8_t *)chrtbl_f16, widtbl_f16, chr_hgt_f16, baseline_f16}, +#else + {(const uint8_t *)chrtbl_null, widtbl_null, 0, 0}, +#endif + + // Font 3 current unused + {(const uint8_t *)chrtbl_null, widtbl_null, 0, 0 }, + +#ifdef LOAD_FONT4 + {(const uint8_t *)chrtbl_f32, widtbl_f32, chr_hgt_f32, baseline_f32}, +#else + {(const uint8_t *)chrtbl_null, widtbl_null, 0, 0}, +#endif + + // Font 5 current unused + {(const uint8_t *)chrtbl_null, widtbl_null, 0, 0 }, + +#ifdef LOAD_FONT6 + {(const uint8_t *)chrtbl_f64, widtbl_f64, chr_hgt_f64, baseline_f64}, +#else + {(const uint8_t *)chrtbl_null, widtbl_null, 0, 0}, +#endif + +#ifdef LOAD_FONT7 + {(const uint8_t *)chrtbl_f7s, widtbl_f7s, chr_hgt_f7s, baseline_f7s}, +#else + {(const uint8_t *)chrtbl_null, widtbl_null, 0, 0}, +#endif + +#ifdef LOAD_FONT8 + {(const uint8_t *)chrtbl_f72, widtbl_f72, chr_hgt_f72, baseline_f72} +#else + {(const uint8_t *)chrtbl_null, widtbl_null, 0, 0} +#endif }; /*************************************************************************************** ** Section 5: Font datum enumeration ***************************************************************************************/ -//These enumerate the text plotting alignment (reference datum point) -#define TL_DATUM 0 // Top left (default) -#define TC_DATUM 1 // Top centre -#define TR_DATUM 2 // Top right -#define ML_DATUM 3 // Middle left -#define CL_DATUM 3 // Centre left, same as above -#define MC_DATUM 4 // Middle centre -#define CC_DATUM 4 // Centre centre, same as above -#define MR_DATUM 5 // Middle right -#define CR_DATUM 5 // Centre right, same as above -#define BL_DATUM 6 // Bottom left -#define BC_DATUM 7 // Bottom centre -#define BR_DATUM 8 // Bottom right -#define L_BASELINE 9 // Left character baseline (Line the 'A' character would sit on) +// These enumerate the text plotting alignment (reference datum point) +#define TL_DATUM 0 // Top left (default) +#define TC_DATUM 1 // Top centre +#define TR_DATUM 2 // Top right +#define ML_DATUM 3 // Middle left +#define CL_DATUM 3 // Centre left, same as above +#define MC_DATUM 4 // Middle centre +#define CC_DATUM 4 // Centre centre, same as above +#define MR_DATUM 5 // Middle right +#define CR_DATUM 5 // Centre right, same as above +#define BL_DATUM 6 // Bottom left +#define BC_DATUM 7 // Bottom centre +#define BR_DATUM 8 // Bottom right +#define L_BASELINE 9 // Left character baseline (Line the 'A' character would sit on) #define C_BASELINE 10 // Centre character baseline #define R_BASELINE 11 // Right character baseline @@ -302,30 +305,30 @@ const PROGMEM fontinfo fontdata [] = { ** Section 6: Colour enumeration ***************************************************************************************/ // Default color definitions -#define TFT_BLACK 0x0000 /* 0, 0, 0 */ -#define TFT_NAVY 0x000F /* 0, 0, 128 */ -#define TFT_DARKGREEN 0x03E0 /* 0, 128, 0 */ -#define TFT_DARKCYAN 0x03EF /* 0, 128, 128 */ -#define TFT_MAROON 0x7800 /* 128, 0, 0 */ -#define TFT_PURPLE 0x780F /* 128, 0, 128 */ -#define TFT_OLIVE 0x7BE0 /* 128, 128, 0 */ -#define TFT_LIGHTGREY 0xD69A /* 211, 211, 211 */ -#define TFT_DARKGREY 0x7BEF /* 128, 128, 128 */ -#define TFT_BLUE 0x001F /* 0, 0, 255 */ -#define TFT_GREEN 0x07E0 /* 0, 255, 0 */ -#define TFT_CYAN 0x07FF /* 0, 255, 255 */ -#define TFT_RED 0xF800 /* 255, 0, 0 */ -#define TFT_MAGENTA 0xF81F /* 255, 0, 255 */ -#define TFT_YELLOW 0xFFE0 /* 255, 255, 0 */ -#define TFT_WHITE 0xFFFF /* 255, 255, 255 */ -#define TFT_ORANGE 0xFDA0 /* 255, 180, 0 */ -#define TFT_GREENYELLOW 0xB7E0 /* 180, 255, 0 */ -#define TFT_PINK 0xFE19 /* 255, 192, 203 */ //Lighter pink, was 0xFC9F -#define TFT_BROWN 0x9A60 /* 150, 75, 0 */ -#define TFT_GOLD 0xFEA0 /* 255, 215, 0 */ -#define TFT_SILVER 0xC618 /* 192, 192, 192 */ -#define TFT_SKYBLUE 0x867D /* 135, 206, 235 */ -#define TFT_VIOLET 0x915C /* 180, 46, 226 */ +#define TFT_BLACK 0x0000 /* 0, 0, 0 */ +#define TFT_NAVY 0x000F /* 0, 0, 128 */ +#define TFT_DARKGREEN 0x03E0 /* 0, 128, 0 */ +#define TFT_DARKCYAN 0x03EF /* 0, 128, 128 */ +#define TFT_MAROON 0x7800 /* 128, 0, 0 */ +#define TFT_PURPLE 0x780F /* 128, 0, 128 */ +#define TFT_OLIVE 0x7BE0 /* 128, 128, 0 */ +#define TFT_LIGHTGREY 0xD69A /* 211, 211, 211 */ +#define TFT_DARKGREY 0x7BEF /* 128, 128, 128 */ +#define TFT_BLUE 0x001F /* 0, 0, 255 */ +#define TFT_GREEN 0x07E0 /* 0, 255, 0 */ +#define TFT_CYAN 0x07FF /* 0, 255, 255 */ +#define TFT_RED 0xF800 /* 255, 0, 0 */ +#define TFT_MAGENTA 0xF81F /* 255, 0, 255 */ +#define TFT_YELLOW 0xFFE0 /* 255, 255, 0 */ +#define TFT_WHITE 0xFFFF /* 255, 255, 255 */ +#define TFT_ORANGE 0xFDA0 /* 255, 180, 0 */ +#define TFT_GREENYELLOW 0xB7E0 /* 180, 255, 0 */ +#define TFT_PINK 0xFE19 /* 255, 192, 203 */ // Lighter pink, was 0xFC9F +#define TFT_BROWN 0x9A60 /* 150, 75, 0 */ +#define TFT_GOLD 0xFEA0 /* 255, 215, 0 */ +#define TFT_SILVER 0xC618 /* 192, 192, 192 */ +#define TFT_SKYBLUE 0x867D /* 135, 206, 235 */ +#define TFT_VIOLET 0x915C /* 180, 46, 226 */ // Next is a special 16-bit colour value that encodes to 8 bits // and will then decode back to the same 16-bit value. @@ -334,22 +337,22 @@ const PROGMEM fontinfo fontdata [] = { // Default palette for 4-bit colour sprites static const uint16_t default_4bit_palette[] PROGMEM = { - TFT_BLACK, // 0 ^ - TFT_BROWN, // 1 | - TFT_RED, // 2 | - TFT_ORANGE, // 3 | - TFT_YELLOW, // 4 Colours 0-9 follow the resistor colour code! - TFT_GREEN, // 5 | - TFT_BLUE, // 6 | - TFT_PURPLE, // 7 | - TFT_DARKGREY, // 8 | - TFT_WHITE, // 9 v - TFT_CYAN, // 10 Blue+green mix - TFT_MAGENTA, // 11 Blue+red mix - TFT_MAROON, // 12 Darker red colour - TFT_DARKGREEN,// 13 Darker green colour - TFT_NAVY, // 14 Darker blue colour - TFT_PINK // 15 + TFT_BLACK, // 0 ^ + TFT_BROWN, // 1 | + TFT_RED, // 2 | + TFT_ORANGE, // 3 | + TFT_YELLOW, // 4 Colours 0-9 follow the resistor colour code! + TFT_GREEN, // 5 | + TFT_BLUE, // 6 | + TFT_PURPLE, // 7 | + TFT_DARKGREY, // 8 | + TFT_WHITE, // 9 v + TFT_CYAN, // 10 Blue+green mix + TFT_MAGENTA, // 11 Blue+red mix + TFT_MAROON, // 12 Darker red colour + TFT_DARKGREEN, // 13 Darker green colour + TFT_NAVY, // 14 Darker blue colour + TFT_PINK // 15 }; /*************************************************************************************** @@ -360,60 +363,59 @@ static const uint16_t default_4bit_palette[] PROGMEM = { // This structure allows sketches to retrieve the user setup parameters at runtime // by calling getSetup(), zero impact on code size unless used, mainly for diagnostics -typedef struct -{ -String version = TFT_ESPI_VERSION; -String setup_info; // Setup reference name available to use in a user setup -uint32_t setup_id; // ID available to use in a user setup -int32_t esp; // Processor code -uint8_t trans; // SPI transaction support -uint8_t serial; // Serial (SPI) or parallel +typedef struct { + String version = TFT_ESPI_VERSION; + String setup_info; // Setup reference name available to use in a user setup + uint32_t setup_id; // ID available to use in a user setup + int32_t esp; // Processor code + uint8_t trans; // SPI transaction support + uint8_t serial; // Serial (SPI) or parallel #ifndef GENERIC_PROCESSOR -uint8_t port; // SPI port -#endif -uint8_t overlap; // ESP8266 overlap mode -uint8_t interface; // Interface type - -uint16_t tft_driver; // Hexadecimal code -uint16_t tft_width; // Rotation 0 width and height -uint16_t tft_height; - -uint8_t r0_x_offset; // Display offsets, not all used yet -uint8_t r0_y_offset; -uint8_t r1_x_offset; -uint8_t r1_y_offset; -uint8_t r2_x_offset; -uint8_t r2_y_offset; -uint8_t r3_x_offset; -uint8_t r3_y_offset; - -int8_t pin_tft_mosi; // SPI pins -int8_t pin_tft_miso; -int8_t pin_tft_clk; -int8_t pin_tft_cs; - -int8_t pin_tft_dc; // Control pins -int8_t pin_tft_rd; -int8_t pin_tft_wr; -int8_t pin_tft_rst; - -int8_t pin_tft_d0; // Parallel port pins -int8_t pin_tft_d1; -int8_t pin_tft_d2; -int8_t pin_tft_d3; -int8_t pin_tft_d4; -int8_t pin_tft_d5; -int8_t pin_tft_d6; -int8_t pin_tft_d7; - -int8_t pin_tft_led; -int8_t pin_tft_led_on; - -int8_t pin_tch_cs; // Touch chip select pin - -int16_t tft_spi_freq;// TFT write SPI frequency -int16_t tft_rd_freq; // TFT read SPI frequency -int16_t tch_spi_freq;// Touch controller read/write SPI frequency + uint8_t port; // SPI port +#endif + uint8_t overlap; // ESP8266 overlap mode + uint8_t interface; // Interface type + + uint16_t tft_driver; // Hexadecimal code + uint16_t tft_width; // Rotation 0 width and height + uint16_t tft_height; + + uint8_t r0_x_offset; // Display offsets, not all used yet + uint8_t r0_y_offset; + uint8_t r1_x_offset; + uint8_t r1_y_offset; + uint8_t r2_x_offset; + uint8_t r2_y_offset; + uint8_t r3_x_offset; + uint8_t r3_y_offset; + + int8_t pin_tft_mosi; // SPI pins + int8_t pin_tft_miso; + int8_t pin_tft_clk; + int8_t pin_tft_cs; + + int8_t pin_tft_dc; // Control pins + int8_t pin_tft_rd; + int8_t pin_tft_wr; + int8_t pin_tft_rst; + + int8_t pin_tft_d0; // Parallel port pins + int8_t pin_tft_d1; + int8_t pin_tft_d2; + int8_t pin_tft_d3; + int8_t pin_tft_d4; + int8_t pin_tft_d5; + int8_t pin_tft_d6; + int8_t pin_tft_d7; + + int8_t pin_tft_led; + int8_t pin_tft_led_on; + + int8_t pin_tch_cs; // Touch chip select pin + + int16_t tft_spi_freq; // TFT write SPI frequency + int16_t tft_rd_freq; // TFT read SPI frequency + int16_t tch_spi_freq; // Touch controller read/write SPI frequency } setup_t; /*************************************************************************************** @@ -424,541 +426,589 @@ int16_t tch_spi_freq;// Touch controller read/write SPI frequency typedef uint16_t (*getColorCallback)(uint16_t x, uint16_t y); // Class functions and variables -class TFT_eSPI : public Print { friend class TFT_eSprite; // Sprite class has access to protected members - - //--------------------------------------- public ------------------------------------// - public: - - TFT_eSPI(int16_t _W = TFT_WIDTH, int16_t _H = TFT_HEIGHT); - - // init() and begin() are equivalent, begin() included for backwards compatibility - // Sketch defined tab colour option is for ST7735 displays only - void init(uint8_t tc = TAB_COLOUR), begin(uint8_t tc = TAB_COLOUR); - - void sleep(bool value); - - // These are virtual so the TFT_eSprite class can override them with sprite specific functions - virtual void drawPixel(int32_t x, int32_t y, uint32_t color), - drawChar(int32_t x, int32_t y, uint16_t c, uint32_t color, uint32_t bg, uint8_t size), - drawLine(int32_t xs, int32_t ys, int32_t xe, int32_t ye, uint32_t color), - drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color), - drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color), - fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color); - - virtual int16_t drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t font), - drawChar(uint16_t uniCode, int32_t x, int32_t y), - height(void), - width(void); - - // Read the colour of a pixel at x,y and return value in 565 format - virtual uint16_t readPixel(int32_t x, int32_t y); - - virtual void setWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye); // Note: start + end coordinates - - // Push (aka write pixel) colours to the set window - virtual void pushColor(uint16_t color); - - // These are non-inlined to enable override - virtual void begin_nin_write(); - virtual void end_nin_write(); - - void setRotation(uint8_t r); // Set the display image orientation to 0, 1, 2 or 3 - uint8_t getRotation(void); // Read the current rotation - - // Change the origin position from the default top left - // Note: setRotation, setViewport and resetViewport will revert origin to top left corner of screen/sprite - void setOrigin(int32_t x, int32_t y); - int32_t getOriginX(void); - int32_t getOriginY(void); - - void invertDisplay(bool i); // Tell TFT to invert all displayed colours - void setDisplayOff(); // Turn off display - void setDisplayOn(); // Turn on display - - - // The TFT_eSprite class inherits the following functions (not all are useful to Sprite class - void setAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h); // Note: start coordinates + width and height - - // Viewport commands, see "Viewport_Demo" sketch - void setViewport(int32_t x, int32_t y, int32_t w, int32_t h, bool vpDatum = true); - bool checkViewport(int32_t x, int32_t y, int32_t w, int32_t h); - int32_t getViewportX(void); - int32_t getViewportY(void); - int32_t getViewportWidth(void); - int32_t getViewportHeight(void); - bool getViewportDatum(void); - void frameViewport(uint16_t color, int32_t w); - void resetViewport(void); - - // Clip input window to viewport bounds, return false if whole area is out of bounds - bool clipAddrWindow(int32_t* x, int32_t* y, int32_t* w, int32_t* h); - // Clip input window area to viewport bounds, return false if whole area is out of bounds - bool clipWindow(int32_t* xs, int32_t* ys, int32_t* xe, int32_t* ye); - - // Push (aka write pixel) colours to the TFT (use setAddrWindow() first) - void pushColor(uint16_t color, uint32_t len), // Deprecated, use pushBlock() - pushColors(uint16_t *data, uint32_t len, bool swap = true), // With byte swap option - pushColors(uint8_t *data, uint32_t len); // Deprecated, use pushPixels() - - // Write a solid block of a single colour - void pushBlock(uint16_t color, uint32_t len); - - // Write a set of pixels stored in memory, use setSwapBytes(true/false) function to correct endianess - void pushPixels(const void * data_in, uint32_t len); - - // Support for half duplex (bi-directional SDA) SPI bus where MOSI must be switched to input - #ifdef TFT_SDA_READ - #if defined (TFT_eSPI_ENABLE_8_BIT_READ) - uint8_t tft_Read_8(void); // Read 8-bit value from TFT command register - #endif - void begin_SDA_Read(void); // Begin a read on a half duplex (bi-directional SDA) SPI bus - sets MOSI to input - void end_SDA_Read(void); // Restore MOSI to output - #endif - - - // Graphics drawing - void fillScreen(uint32_t color), - drawRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color), - drawRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t radius, uint32_t color), - fillRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t radius, uint32_t color); - - void fillRectVGradient(int16_t x, int16_t y, int16_t w, int16_t h, uint32_t color1, uint32_t color2); - void fillRectHGradient(int16_t x, int16_t y, int16_t w, int16_t h, uint32_t color1, uint32_t color2); - - void drawCircle(int32_t x, int32_t y, int32_t r, uint32_t color), - drawCircleHelper(int32_t x, int32_t y, int32_t r, uint8_t cornername, uint32_t color), - fillCircle(int32_t x, int32_t y, int32_t r, uint32_t color), - fillCircleHelper(int32_t x, int32_t y, int32_t r, uint8_t cornername, int32_t delta, uint32_t color), - - drawEllipse(int16_t x, int16_t y, int32_t rx, int32_t ry, uint16_t color), - fillEllipse(int16_t x, int16_t y, int32_t rx, int32_t ry, uint16_t color), - - // Corner 1 Corner 2 Corner 3 - drawTriangle(int32_t x1,int32_t y1, int32_t x2,int32_t y2, int32_t x3,int32_t y3, uint32_t color), - fillTriangle(int32_t x1,int32_t y1, int32_t x2,int32_t y2, int32_t x3,int32_t y3, uint32_t color); - - - // Smooth (anti-aliased) graphics drawing - // Draw a pixel blended with the background pixel colour (bg_color) specified, return blended colour - // If the bg_color is not specified, the background pixel colour will be read from TFT or sprite - uint16_t drawPixel(int32_t x, int32_t y, uint32_t color, uint8_t alpha, uint32_t bg_color = 0x00FFFFFF); - - // Draw an anti-aliased (smooth) arc between start and end angles. Arc ends are anti-aliased. - // By default the arc is drawn with square ends unless the "roundEnds" parameter is included and set true - // Angle = 0 is at 6 o'clock position, 90 at 9 o'clock etc. The angles must be in range 0-360 or they will be clipped to these limits - // The start angle may be larger than the end angle. Arcs are always drawn clockwise from the start angle. - void drawSmoothArc(int32_t x, int32_t y, int32_t r, int32_t ir, uint32_t startAngle, uint32_t endAngle, uint32_t fg_color, uint32_t bg_color, bool roundEnds = false); - - // As per "drawSmoothArc" except the ends of the arc are NOT anti-aliased, this facilitates dynamic arc length changes with - // arc segments and ensures clean segment joints. - // The sides of the arc are anti-aliased by default. If smoothArc is false sides will NOT be anti-aliased - void drawArc(int32_t x, int32_t y, int32_t r, int32_t ir, uint32_t startAngle, uint32_t endAngle, uint32_t fg_color, uint32_t bg_color, bool smoothArc = true); - - // Draw an anti-aliased filled circle at x, y with radius r - // Note: The thickness of line is 3 pixels to reduce the visible "braiding" effect of anti-aliasing narrow lines - // this means the inner anti-alias zone is always at r-1 and the outer zone at r+1 - void drawSmoothCircle(int32_t x, int32_t y, int32_t r, uint32_t fg_color, uint32_t bg_color); - - // Draw an anti-aliased filled circle at x, y with radius r - // If bg_color is not included the background pixel colour will be read from TFT or sprite - void fillSmoothCircle(int32_t x, int32_t y, int32_t r, uint32_t color, uint32_t bg_color = 0x00FFFFFF); - - // Draw a rounded rectangle that has a line thickness of r-ir+1 and bounding box defined by x,y and w,h - // The outer corner radius is r, inner corner radius is ir - // The inside and outside of the border are anti-aliased - void drawSmoothRoundRect(int32_t x, int32_t y, int32_t r, int32_t ir, int32_t w, int32_t h, uint32_t fg_color, uint32_t bg_color = 0x00FFFFFF, uint8_t quadrants = 0xF); - - // Draw a filled rounded rectangle , corner radius r and bounding box defined by x,y and w,h - void fillSmoothRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t radius, uint32_t color, uint32_t bg_color = 0x00FFFFFF); - - // Draw a small anti-aliased filled circle at ax,ay with radius r (uses drawWideLine) - // If bg_color is not included the background pixel colour will be read from TFT or sprite - void drawSpot(float ax, float ay, float r, uint32_t fg_color, uint32_t bg_color = 0x00FFFFFF); - - // Draw an anti-aliased wide line from ax,ay to bx,by width wd with radiused ends (radius is wd/2) - // If bg_color is not included the background pixel colour will be read from TFT or sprite - void drawWideLine(float ax, float ay, float bx, float by, float wd, uint32_t fg_color, uint32_t bg_color = 0x00FFFFFF); - - // Draw an anti-aliased wide line from ax,ay to bx,by with different width at each end aw, bw and with radiused ends - // If bg_color is not included the background pixel colour will be read from TFT or sprite - void drawWedgeLine(float ax, float ay, float bx, float by, float aw, float bw, uint32_t fg_color, uint32_t bg_color = 0x00FFFFFF); - - - // Image rendering - // Swap the byte order for pushImage() and pushPixels() - corrects endianness - void setSwapBytes(bool swap); - bool getSwapBytes(void); - - // Draw bitmap - void drawBitmap( int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t fgcolor), - drawBitmap( int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t fgcolor, uint16_t bgcolor), - drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t fgcolor), - drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t fgcolor, uint16_t bgcolor), - setBitmapColor(uint16_t fgcolor, uint16_t bgcolor); // Define the 2 colours for 1bpp sprites - - // Set TFT pivot point (use when rendering rotated sprites) - void setPivot(int16_t x, int16_t y); - int16_t getPivotX(void), // Get pivot x - getPivotY(void); // Get pivot y - - // The next functions can be used as a pair to copy screen blocks (or horizontal/vertical lines) to another location - // Read a block of pixels to a data buffer, buffer is 16-bit and the size must be at least w * h - void readRect(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data); - // Write a block of pixels to the screen which have been read by readRect() - void pushRect(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data); - - // These are used to render images or sprites stored in RAM arrays (used by Sprite class for 16bpp Sprites) - void pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data); - void pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data, uint16_t transparent); - - // These are used to render images stored in FLASH (PROGMEM) - void pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint16_t *data, uint16_t transparent); - void pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint16_t *data); - - // These are used by Sprite class pushSprite() member function for 1, 4 and 8 bits per pixel (bpp) colours - // They are not intended to be used with user sketches (but could be) - // Set bpp8 true for 8bpp sprites, false otherwise. The cmap pointer must be specified for 4bpp - void pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *data, bool bpp8 = true, uint16_t *cmap = nullptr); - void pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *data, uint8_t transparent, bool bpp8 = true, uint16_t *cmap = nullptr); - // FLASH version - void pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint8_t *data, bool bpp8, uint16_t *cmap = nullptr); - - // Render a 16-bit colour image with a 1bpp mask - void pushMaskedImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *img, uint8_t *mask); - - // This next function has been used successfully to dump the TFT screen to a PC for documentation purposes - // It reads a screen area and returns the 3 RGB 8-bit colour values of each pixel in the buffer - // Set w and h to 1 to read 1 pixel's colour. The data buffer must be at least w * h * 3 bytes - void readRectRGB(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *data); - - - // Text rendering - value returned is the pixel width of the rendered text - int16_t drawNumber(long intNumber, int32_t x, int32_t y, uint8_t font), // Draw integer using specified font number - drawNumber(long intNumber, int32_t x, int32_t y), // Draw integer using current font - - // Decimal is the number of decimal places to render - // Use with setTextDatum() to position values on TFT, and setTextPadding() to blank old displayed values - drawFloat(float floatNumber, uint8_t decimal, int32_t x, int32_t y, uint8_t font), // Draw float using specified font number - drawFloat(float floatNumber, uint8_t decimal, int32_t x, int32_t y), // Draw float using current font - - // Handle char arrays - // Use with setTextDatum() to position string on TFT, and setTextPadding() to blank old displayed strings - drawString(const char *string, int32_t x, int32_t y, uint8_t font), // Draw string using specified font number - drawString(const char *string, int32_t x, int32_t y), // Draw string using current font - drawString(const String& string, int32_t x, int32_t y, uint8_t font),// Draw string using specified font number - drawString(const String& string, int32_t x, int32_t y), // Draw string using current font - - drawCentreString(const char *string, int32_t x, int32_t y, uint8_t font), // Deprecated, use setTextDatum() and drawString() - drawRightString(const char *string, int32_t x, int32_t y, uint8_t font), // Deprecated, use setTextDatum() and drawString() - drawCentreString(const String& string, int32_t x, int32_t y, uint8_t font),// Deprecated, use setTextDatum() and drawString() - drawRightString(const String& string, int32_t x, int32_t y, uint8_t font); // Deprecated, use setTextDatum() and drawString() +class TFT_eSPI : public Print { + friend class TFT_eSprite; // Sprite class has access to protected members + + //--------------------------------------- public ------------------------------------// +public: + TFT_eSPI(int16_t _W = TFT_WIDTH, int16_t _H = TFT_HEIGHT); + + // init() and begin() are equivalent, begin() included for backwards compatibility + // Sketch defined tab colour option is for ST7735 displays only + void init(uint8_t tc = TAB_COLOUR), begin(uint8_t tc = TAB_COLOUR); + + void sleep(bool value); + + // These are virtual so the TFT_eSprite class can override them with sprite specific functions + virtual void drawPixel(int32_t x, int32_t y, uint32_t color), + drawChar(int32_t x, int32_t y, uint16_t c, uint32_t color, uint32_t bg, uint8_t size), + drawLine(int32_t xs, int32_t ys, int32_t xe, int32_t ye, uint32_t color), + drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color), + drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color), + fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color); + + virtual int16_t drawChar(uint16_t uniCode, int32_t x, int32_t y, uint8_t font), + drawChar(uint16_t uniCode, int32_t x, int32_t y), height(void), width(void); + + // Read the colour of a pixel at x,y and return value in 565 format + virtual uint16_t readPixel(int32_t x, int32_t y); + + virtual void setWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye); // Note: start + end coordinates + + // Push (aka write pixel) colours to the set window + virtual void pushColor(uint16_t color); + + // These are non-inlined to enable override + virtual void begin_nin_write(); + virtual void end_nin_write(); + + void setRotation(uint8_t r); // Set the display image orientation to 0, 1, 2 or 3 + uint8_t getRotation(void); // Read the current rotation + + // Change the origin position from the default top left + // Note: setRotation, setViewport and resetViewport will revert origin to top left corner of screen/sprite + void setOrigin(int32_t x, int32_t y); + int32_t getOriginX(void); + int32_t getOriginY(void); + + void invertDisplay(bool i); // Tell TFT to invert all displayed colours + void setDisplayOff(); // Turn off display + void setDisplayOn(); // Turn on display + + // The TFT_eSprite class inherits the following functions (not all are useful to Sprite class + void + setAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h); // Note: start coordinates + width and height + + // Viewport commands, see "Viewport_Demo" sketch + void setViewport(int32_t x, int32_t y, int32_t w, int32_t h, bool vpDatum = true); + bool checkViewport(int32_t x, int32_t y, int32_t w, int32_t h); + int32_t getViewportX(void); + int32_t getViewportY(void); + int32_t getViewportWidth(void); + int32_t getViewportHeight(void); + bool getViewportDatum(void); + void frameViewport(uint16_t color, int32_t w); + void resetViewport(void); + + // Clip input window to viewport bounds, return false if whole area is out of bounds + bool clipAddrWindow(int32_t *x, int32_t *y, int32_t *w, int32_t *h); + // Clip input window area to viewport bounds, return false if whole area is out of bounds + bool clipWindow(int32_t *xs, int32_t *ys, int32_t *xe, int32_t *ye); + + // Push (aka write pixel) colours to the TFT (use setAddrWindow() first) + void pushColor(uint16_t color, uint32_t len), // Deprecated, use pushBlock() + pushColors(uint16_t *data, uint32_t len, bool swap = true), // With byte swap option + pushColors(uint8_t *data, uint32_t len); // Deprecated, use pushPixels() + + // Write a solid block of a single colour + void pushBlock(uint16_t color, uint32_t len); + + // Write a set of pixels stored in memory, use setSwapBytes(true/false) function to correct endianess + void pushPixels(const void *data_in, uint32_t len); + +// Support for half duplex (bi-directional SDA) SPI bus where MOSI must be switched to input +#ifdef TFT_SDA_READ +#if defined(TFT_eSPI_ENABLE_8_BIT_READ) + uint8_t tft_Read_8(void); // Read 8-bit value from TFT command register +#endif + void + begin_SDA_Read(void); // Begin a read on a half duplex (bi-directional SDA) SPI bus - sets MOSI to input + void end_SDA_Read(void); // Restore MOSI to output +#endif + // Graphics drawing + void fillScreen(uint32_t color), drawRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color), + drawRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t radius, uint32_t color), + fillRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t radius, uint32_t color); + + void fillRectVGradient(int16_t x, int16_t y, int16_t w, int16_t h, uint32_t color1, uint32_t color2); + void fillRectHGradient(int16_t x, int16_t y, int16_t w, int16_t h, uint32_t color1, uint32_t color2); + + void drawCircle(int32_t x, int32_t y, int32_t r, uint32_t color), + drawCircleHelper(int32_t x, int32_t y, int32_t r, uint8_t cornername, uint32_t color), + fillCircle(int32_t x, int32_t y, int32_t r, uint32_t color), + fillCircleHelper(int32_t x, int32_t y, int32_t r, uint8_t cornername, int32_t delta, uint32_t color), + + drawEllipse(int16_t x, int16_t y, int32_t rx, int32_t ry, uint16_t color), + fillEllipse(int16_t x, int16_t y, int32_t rx, int32_t ry, uint16_t color), + + // Corner 1 Corner 2 Corner 3 + drawTriangle(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x3, int32_t y3, uint32_t color), + fillTriangle(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x3, int32_t y3, uint32_t color); + + // Smooth (anti-aliased) graphics drawing + // Draw a pixel blended with the background pixel colour (bg_color) specified, return blended colour + // If the bg_color is not specified, the background pixel colour will be read from TFT or sprite + uint16_t drawPixel(int32_t x, int32_t y, uint32_t color, uint8_t alpha, uint32_t bg_color = 0x00FFFFFF); + + // Draw an anti-aliased (smooth) arc between start and end angles. Arc ends are anti-aliased. + // By default the arc is drawn with square ends unless the "roundEnds" parameter is included and set true + // Angle = 0 is at 6 o'clock position, 90 at 9 o'clock etc. The angles must be in range 0-360 or they will + // be clipped to these limits The start angle may be larger than the end angle. Arcs are always drawn + // clockwise from the start angle. + void drawSmoothArc( + int32_t x, int32_t y, int32_t r, int32_t ir, uint32_t startAngle, uint32_t endAngle, + uint32_t fg_color, uint32_t bg_color, bool roundEnds = false + ); + + // As per "drawSmoothArc" except the ends of the arc are NOT anti-aliased, this facilitates dynamic arc + // length changes with arc segments and ensures clean segment joints. The sides of the arc are + // anti-aliased by default. If smoothArc is false sides will NOT be anti-aliased + void drawArc( + int32_t x, int32_t y, int32_t r, int32_t ir, uint32_t startAngle, uint32_t endAngle, + uint32_t fg_color, uint32_t bg_color, bool smoothArc = true + ); + + // Draw an anti-aliased filled circle at x, y with radius r + // Note: The thickness of line is 3 pixels to reduce the visible "braiding" effect of anti-aliasing narrow + // lines + // this means the inner anti-alias zone is always at r-1 and the outer zone at r+1 + void drawSmoothCircle(int32_t x, int32_t y, int32_t r, uint32_t fg_color, uint32_t bg_color); + + // Draw an anti-aliased filled circle at x, y with radius r + // If bg_color is not included the background pixel colour will be read from TFT or sprite + void fillSmoothCircle(int32_t x, int32_t y, int32_t r, uint32_t color, uint32_t bg_color = 0x00FFFFFF); + + // Draw a rounded rectangle that has a line thickness of r-ir+1 and bounding box defined by x,y and w,h + // The outer corner radius is r, inner corner radius is ir + // The inside and outside of the border are anti-aliased + void drawSmoothRoundRect( + int32_t x, int32_t y, int32_t r, int32_t ir, int32_t w, int32_t h, uint32_t fg_color, + uint32_t bg_color = 0x00FFFFFF, uint8_t quadrants = 0xF + ); + + // Draw a filled rounded rectangle , corner radius r and bounding box defined by x,y and w,h + void fillSmoothRoundRect( + int32_t x, int32_t y, int32_t w, int32_t h, int32_t radius, uint32_t color, + uint32_t bg_color = 0x00FFFFFF + ); + + // Draw a small anti-aliased filled circle at ax,ay with radius r (uses drawWideLine) + // If bg_color is not included the background pixel colour will be read from TFT or sprite + void drawSpot(float ax, float ay, float r, uint32_t fg_color, uint32_t bg_color = 0x00FFFFFF); + + // Draw an anti-aliased wide line from ax,ay to bx,by width wd with radiused ends (radius is wd/2) + // If bg_color is not included the background pixel colour will be read from TFT or sprite + void drawWideLine( + float ax, float ay, float bx, float by, float wd, uint32_t fg_color, uint32_t bg_color = 0x00FFFFFF + ); + + // Draw an anti-aliased wide line from ax,ay to bx,by with different width at each end aw, bw and with + // radiused ends If bg_color is not included the background pixel colour will be read from TFT or sprite + void drawWedgeLine( + float ax, float ay, float bx, float by, float aw, float bw, uint32_t fg_color, + uint32_t bg_color = 0x00FFFFFF + ); + + // Image rendering + // Swap the byte order for pushImage() and pushPixels() - corrects endianness + void setSwapBytes(bool swap); + bool getSwapBytes(void); + + // Draw bitmap + void drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t fgcolor), + drawBitmap( + int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t fgcolor, + uint16_t bgcolor + ), + drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t fgcolor), + drawXBitmap( + int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t fgcolor, + uint16_t bgcolor + ), + setBitmapColor(uint16_t fgcolor, uint16_t bgcolor); // Define the 2 colours for 1bpp sprites + + // Set TFT pivot point (use when rendering rotated sprites) + void setPivot(int16_t x, int16_t y); + int16_t getPivotX(void), // Get pivot x + getPivotY(void); // Get pivot y + + // The next functions can be used as a pair to copy screen blocks (or horizontal/vertical lines) to + // another location Read a block of pixels to a data buffer, buffer is 16-bit and the size must be at + // least w * h + void readRect(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data); + // Write a block of pixels to the screen which have been read by readRect() + void pushRect(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data); + + // These are used to render images or sprites stored in RAM arrays (used by Sprite class for 16bpp + // Sprites) + void pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data); + void pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data, uint16_t transparent); + + // These are used to render images stored in FLASH (PROGMEM) + void pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint16_t *data, uint16_t transparent); + void pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint16_t *data); + + // These are used by Sprite class pushSprite() member function for 1, 4 and 8 bits per pixel (bpp) colours + // They are not intended to be used with user sketches (but could be) + // Set bpp8 true for 8bpp sprites, false otherwise. The cmap pointer must be specified for 4bpp + void pushImage( + int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *data, bool bpp8 = true, uint16_t *cmap = nullptr + ); + void pushImage( + int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *data, uint8_t transparent, bool bpp8 = true, + uint16_t *cmap = nullptr + ); + // FLASH version + void pushImage( + int32_t x, int32_t y, int32_t w, int32_t h, const uint8_t *data, bool bpp8, uint16_t *cmap = nullptr + ); + + // Render a 16-bit colour image with a 1bpp mask + void pushMaskedImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *img, uint8_t *mask); + + // This next function has been used successfully to dump the TFT screen to a PC for documentation purposes + // It reads a screen area and returns the 3 RGB 8-bit colour values of each pixel in the buffer + // Set w and h to 1 to read 1 pixel's colour. The data buffer must be at least w * h * 3 bytes + void readRectRGB(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *data); + + // Text rendering - value returned is the pixel width of the rendered text + int16_t drawNumber( + long intNumber, int32_t x, int32_t y, uint8_t font + ), // Draw integer using specified font number + drawNumber(long intNumber, int32_t x, int32_t y), // Draw integer using current font + + // Decimal is the number of decimal places to render + // Use with setTextDatum() to position values on TFT, and setTextPadding() to blank old displayed + // values + drawFloat( + float floatNumber, uint8_t decimal, int32_t x, int32_t y, uint8_t font + ), // Draw float using specified font number + drawFloat(float floatNumber, uint8_t decimal, int32_t x, int32_t y), // Draw float using current font + + // Handle char arrays + // Use with setTextDatum() to position string on TFT, and setTextPadding() to blank old displayed + // strings + drawString( + const char *string, int32_t x, int32_t y, uint8_t font + ), // Draw string using specified font number + drawString(const char *string, int32_t x, int32_t y), // Draw string using current font + drawString( + const String &string, int32_t x, int32_t y, uint8_t font + ), // Draw string using specified font number + drawString(const String &string, int32_t x, int32_t y), // Draw string using current font + + drawCentreString( + const char *string, int32_t x, int32_t y, uint8_t font + ), // Deprecated, use setTextDatum() and drawString() + drawRightString( + const char *string, int32_t x, int32_t y, uint8_t font + ), // Deprecated, use setTextDatum() and drawString() + drawCentreString( + const String &string, int32_t x, int32_t y, uint8_t font + ), // Deprecated, use setTextDatum() and drawString() + drawRightString( + const String &string, int32_t x, int32_t y, uint8_t font + ); // Deprecated, use setTextDatum() and drawString() + + // Text rendering and font handling support functions + void setCursor(int16_t x, int16_t y), // Set cursor for tft.print() + setCursor(int16_t x, int16_t y, uint8_t font); // Set cursor and font number for tft.print() + + int16_t getCursorX(void), // Read current cursor x position (moves with tft.print()) + getCursorY(void); // Read current cursor y position + + void setTextColor(uint16_t color), // Set character (glyph) color only (background not over-written) + setTextColor( + uint16_t fgcolor, uint16_t bgcolor, bool bgfill = false + ), // Set character (glyph) foreground and background colour, optional background fill for smooth + // fonts + setTextSize(uint8_t size); // Set character size multiplier (this increases pixel size) + + void + setTextWrap(bool wrapX, bool wrapY = false); // Turn on/off wrapping of text in TFT width and/or height + + void setTextDatum(uint8_t datum); // Set text datum position (default is top left), see Section 5 above + uint8_t getTextDatum(void); + + void + setTextPadding(uint16_t x_width); // Set text padding (background blanking/over-write) width in pixels + uint16_t getTextPadding(void); // Get text padding - // Text rendering and font handling support functions - void setCursor(int16_t x, int16_t y), // Set cursor for tft.print() - setCursor(int16_t x, int16_t y, uint8_t font); // Set cursor and font number for tft.print() +#ifdef LOAD_GFXFF + void setFreeFont(const GFXfont *f = NULL), // Select the GFX Free Font + setTextFont(uint8_t font); // Set the font number to use in future +#else + void setFreeFont(uint8_t font), // Not used, historical fix to prevent an error + setTextFont(uint8_t font); // Set the font number to use in future +#endif - int16_t getCursorX(void), // Read current cursor x position (moves with tft.print()) - getCursorY(void); // Read current cursor y position + int16_t textWidth(const char *string, uint8_t font), // Returns pixel width of string in specified font + textWidth(const char *string), // Returns pixel width of string in current font + textWidth(const String &string, uint8_t font), // As above for String types + textWidth(const String &string), + fontHeight(int16_t font), // Returns pixel height of specified font + fontHeight(void); // Returns pixel height of current font - void setTextColor(uint16_t color), // Set character (glyph) color only (background not over-written) - setTextColor(uint16_t fgcolor, uint16_t bgcolor, bool bgfill = false), // Set character (glyph) foreground and background colour, optional background fill for smooth fonts - setTextSize(uint8_t size); // Set character size multiplier (this increases pixel size) + // Used by library and Smooth font class to extract Unicode point codes from a UTF8 encoded string + uint16_t decodeUTF8(uint8_t *buf, uint16_t *index, uint16_t remaining), decodeUTF8(uint8_t c); - void setTextWrap(bool wrapX, bool wrapY = false); // Turn on/off wrapping of text in TFT width and/or height + // Support function to UTF8 decode and draw characters piped through print stream + size_t write(uint8_t); + // size_t write(const uint8_t *buf, size_t len); - void setTextDatum(uint8_t datum); // Set text datum position (default is top left), see Section 5 above - uint8_t getTextDatum(void); + // Used by Smooth font class to fetch a pixel colour for the anti-aliasing + void setCallback(getColorCallback getCol); - void setTextPadding(uint16_t x_width); // Set text padding (background blanking/over-write) width in pixels - uint16_t getTextPadding(void); // Get text padding + uint16_t fontsLoaded(void); // Each bit in returned value represents a font type that is loaded - used for + // debug/error handling only -#ifdef LOAD_GFXFF - void setFreeFont(const GFXfont *f = NULL), // Select the GFX Free Font - setTextFont(uint8_t font); // Set the font number to use in future + // Low level read/write + void spiwrite(uint8_t); // legacy support only +#ifdef RM68120_DRIVER + void writecommand(uint16_t c); // Send a 16-bit command, function resets DC/RS high ready for data + void writeRegister8(uint16_t c, uint8_t d); // Write 8-bit data data to 16-bit command register + void writeRegister16(uint16_t c, uint16_t d); // Write 16-bit data data to 16-bit command register #else - void setFreeFont(uint8_t font), // Not used, historical fix to prevent an error - setTextFont(uint8_t font); // Set the font number to use in future + void writecommand(uint8_t c); // Send an 8-bit command, function resets DC/RS high ready for data #endif + void writedata(uint8_t d); // Send data with DC/RS set high + + void commandList(const uint8_t *addr); // Send a initialisation sequence to TFT stored in FLASH + + uint8_t readcommand8(uint8_t cmd_function, uint8_t index = 0); // read 8 bits from TFT + uint16_t readcommand16(uint8_t cmd_function, uint8_t index = 0); // read 16 bits from TFT + uint32_t readcommand32(uint8_t cmd_function, uint8_t index = 0); // read 32 bits from TFT + + // Colour conversion + // Convert 8-bit red, green and blue to 16 bits + uint16_t color565(uint8_t red, uint8_t green, uint8_t blue); + + // Convert 8-bit colour to 16 bits + uint16_t color8to16(uint8_t color332); + // Convert 16-bit colour to 8 bits + uint8_t color16to8(uint16_t color565); + + // Convert 16-bit colour to/from 24-bit, R+G+B concatenated into LS 24 bits + uint32_t color16to24(uint16_t color565); + uint32_t color24to16(uint32_t color888); + + // Alpha blend 2 colours, see generic "alphaBlend_Test" example + // alpha = 0 = 100% background colour + // alpha = 255 = 100% foreground colour + uint16_t alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc); + + // 16-bit colour alphaBlend with alpha dither (dither reduces colour banding) + uint16_t alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc, uint8_t dither); + // 24-bit colour alphaBlend with optional alpha dither + uint32_t alphaBlend24(uint8_t alpha, uint32_t fgc, uint32_t bgc, uint8_t dither = 0); + + // Direct Memory Access (DMA) support functions + // These can be used for SPI writes when using the ESP32 (original) or STM32 processors. + // DMA also works on a RP2040 processor with PIO based SPI and parallel (8 and 16-bit) interfaces + // Bear in mind DMA will only be of benefit in particular circumstances and can be tricky + // to manage by noobs. The functions have however been designed to be noob friendly and + // avoid a few DMA behaviour "gotchas". + // + // At best you will get a 2x TFT rendering performance improvement when using DMA because + // this library handles the SPI bus so efficiently during normal (non DMA) transfers. The best + // performance improvement scenario is the DMA transfer time is exactly the same as the time it + // takes for the processor to prepare the next image buffer and initiate another DMA transfer. + // + // DMA transfer to the TFT is done while the processor moves on to handle other tasks. Bear + // this in mind and watch out for "gotchas" like the image buffer going out of scope as the + // processor leaves a function or its content being changed while the DMA engine is reading it. + // + // The compiler MAY change the implied scope of a buffer which has been set aside by creating + // an array. For example a buffer defined before a "for-next" loop may get de-allocated when + // the loop ends. To avoid this use, for example, malloc() and free() to take control of when + // the buffer space is available and ensure it is not released until DMA is complete. + // + // Clearly you should not modify a buffer that is being DMA'ed to the TFT until the DMA is over. + // Use the dmaBusy() function to check this. Use tft.startWrite() before invoking DMA so the + // TFT chip select stays low. If you use tft.endWrite() before DMA is complete then the endWrite + // function will wait for the DMA to complete, so this may defeat any DMA performance benefit. + // + + bool initDMA( + bool ctrl_cs = false + ); // Initialise the DMA engine and attach to SPI bus - typically used in setup() + // Parameter "true" enables DMA engine control of TFT chip select (ESP32 only) + // For ESP32 only, TFT reads will not work if parameter is true + void deInitDMA(void); // De-initialise the DMA engine and detach from SPI bus - typically not used + + // Push an image to the TFT using DMA, buffer is optional and grabs (double buffers) a copy of the image + // Use the buffer if the image data will get over-written or destroyed while DMA is in progress + // + // Note 1: If swapping colour bytes is defined, and the double buffer option is NOT used, then the bytes + // in the original image buffer content will be byte swapped by the function before DMA is initiated. + // + // Note 2: If part of the image will be off screen or outside of a set viewport, then the the original + // image buffer content will be altered to a correctly clipped image before DMA is initiated. + // + // The function will wait for the last DMA to complete if it is called while a previous DMA is still + // in progress, this simplifies the sketch and helps avoid "gotchas". + void pushImageDMA(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data, uint16_t *buffer = nullptr); + +#if defined(ESP32) // ESP32 only at the moment + // For case where pointer is a const and the image data must not be modified (clipped or byte swapped) + void pushImageDMA(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t const *data); +#endif + // Push a block of pixels into a window set up using setAddrWindow() + void pushPixelsDMA(uint16_t *image, uint32_t len); + + // Check if the DMA is complete - use while(tft.dmaBusy); for a blocking wait + bool dmaBusy(void); // returns true if DMA is still in progress + void dmaWait(void); // wait until DMA is complete + + bool DMA_Enabled = false; // Flag for DMA enabled state + uint8_t spiBusyCheck = 0; // Number of ESP32 transfer buffers to check + + // Bare metal functions + void startWrite(void); // Begin SPI transaction + void writeColor(uint16_t color, uint32_t len); // Deprecated, use pushBlock() + void endWrite(void); // End SPI transaction + + // Set/get an arbitrary library configuration attribute or option + // Use to switch ON/OFF capabilities such as UTF8 decoding - each attribute has a unique ID + // id = 0: reserved - may be used in future to reset all attributes to a default state + // id = 1: Turn on (a=true) or off (a=false) GLCD cp437 font character error correction + // id = 2: Turn on (a=true) or off (a=false) UTF8 decoding + // id = 3: Enable or disable use of ESP32 PSRAM (if available) +#define CP437_SWITCH 1 +#define UTF8_SWITCH 2 +#define PSRAM_ENABLE 3 + void setAttribute(uint8_t id = 0, uint8_t a = 0); // Set attribute value + uint8_t getAttribute(uint8_t id = 0); // Get attribute value + + // Used for diagnostic sketch to see library setup adopted by compiler, see Section 7 above + void getSetup(setup_t &tft_settings); // Sketch provides the instance to populate + bool verifySetupID(uint32_t id); + + // Global variables +#if !defined(TFT_PARALLEL_8_BIT) && !defined(RP2040_PIO_INTERFACE) + static SPIClass &getSPIinstance(void); // Get SPI class handle +#endif + uint32_t textcolor, textbgcolor; // Text foreground and background colours - int16_t textWidth(const char *string, uint8_t font), // Returns pixel width of string in specified font - textWidth(const char *string), // Returns pixel width of string in current font - textWidth(const String& string, uint8_t font), // As above for String types - textWidth(const String& string), - fontHeight(int16_t font), // Returns pixel height of specified font - fontHeight(void); // Returns pixel height of current font - - // Used by library and Smooth font class to extract Unicode point codes from a UTF8 encoded string - uint16_t decodeUTF8(uint8_t *buf, uint16_t *index, uint16_t remaining), - decodeUTF8(uint8_t c); + uint32_t bitmap_fg, bitmap_bg; // Bitmap foreground (bit=1) and background (bit=0) colours - // Support function to UTF8 decode and draw characters piped through print stream - size_t write(uint8_t); - // size_t write(const uint8_t *buf, size_t len); + uint8_t textfont, // Current selected font number + textsize, // Current font size multiplier + textdatum, // Text reference datum + rotation; // Display rotation (0-3) - // Used by Smooth font class to fetch a pixel colour for the anti-aliasing - void setCallback(getColorCallback getCol); + uint8_t decoderState = 0; // UTF8 decoder state - not for user access + uint16_t decoderBuffer; // Unicode code-point buffer - not for user access - uint16_t fontsLoaded(void); // Each bit in returned value represents a font type that is loaded - used for debug/error handling only + // Moved here to Core2 capture it + bool locked, inTransaction, lockTransaction; // SPI transaction and mutex lock flags + int32_t cursor_x, cursor_y, padX; // Text cursor x,y and padding setting + //--------------------------------------- private ------------------------------------// +private: + // Legacy begin and end prototypes - deprecated TODO: delete + void spi_begin(); + void spi_end(); + void spi_begin_read(); + void spi_end_read(); - // Low level read/write - void spiwrite(uint8_t); // legacy support only -#ifdef RM68120_DRIVER - void writecommand(uint16_t c); // Send a 16-bit command, function resets DC/RS high ready for data - void writeRegister8(uint16_t c, uint8_t d); // Write 8-bit data data to 16-bit command register - void writeRegister16(uint16_t c, uint16_t d); // Write 16-bit data data to 16-bit command register -#else - void writecommand(uint8_t c); // Send an 8-bit command, function resets DC/RS high ready for data -#endif - void writedata(uint8_t d); // Send data with DC/RS set high - - void commandList(const uint8_t *addr); // Send a initialisation sequence to TFT stored in FLASH - - uint8_t readcommand8( uint8_t cmd_function, uint8_t index = 0); // read 8 bits from TFT - uint16_t readcommand16(uint8_t cmd_function, uint8_t index = 0); // read 16 bits from TFT - uint32_t readcommand32(uint8_t cmd_function, uint8_t index = 0); // read 32 bits from TFT - - - // Colour conversion - // Convert 8-bit red, green and blue to 16 bits - uint16_t color565(uint8_t red, uint8_t green, uint8_t blue); - - // Convert 8-bit colour to 16 bits - uint16_t color8to16(uint8_t color332); - // Convert 16-bit colour to 8 bits - uint8_t color16to8(uint16_t color565); - - // Convert 16-bit colour to/from 24-bit, R+G+B concatenated into LS 24 bits - uint32_t color16to24(uint16_t color565); - uint32_t color24to16(uint32_t color888); - - // Alpha blend 2 colours, see generic "alphaBlend_Test" example - // alpha = 0 = 100% background colour - // alpha = 255 = 100% foreground colour - uint16_t alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc); - - // 16-bit colour alphaBlend with alpha dither (dither reduces colour banding) - uint16_t alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc, uint8_t dither); - // 24-bit colour alphaBlend with optional alpha dither - uint32_t alphaBlend24(uint8_t alpha, uint32_t fgc, uint32_t bgc, uint8_t dither = 0); - - // Direct Memory Access (DMA) support functions - // These can be used for SPI writes when using the ESP32 (original) or STM32 processors. - // DMA also works on a RP2040 processor with PIO based SPI and parallel (8 and 16-bit) interfaces - // Bear in mind DMA will only be of benefit in particular circumstances and can be tricky - // to manage by noobs. The functions have however been designed to be noob friendly and - // avoid a few DMA behaviour "gotchas". - // - // At best you will get a 2x TFT rendering performance improvement when using DMA because - // this library handles the SPI bus so efficiently during normal (non DMA) transfers. The best - // performance improvement scenario is the DMA transfer time is exactly the same as the time it - // takes for the processor to prepare the next image buffer and initiate another DMA transfer. - // - // DMA transfer to the TFT is done while the processor moves on to handle other tasks. Bear - // this in mind and watch out for "gotchas" like the image buffer going out of scope as the - // processor leaves a function or its content being changed while the DMA engine is reading it. - // - // The compiler MAY change the implied scope of a buffer which has been set aside by creating - // an array. For example a buffer defined before a "for-next" loop may get de-allocated when - // the loop ends. To avoid this use, for example, malloc() and free() to take control of when - // the buffer space is available and ensure it is not released until DMA is complete. - // - // Clearly you should not modify a buffer that is being DMA'ed to the TFT until the DMA is over. - // Use the dmaBusy() function to check this. Use tft.startWrite() before invoking DMA so the - // TFT chip select stays low. If you use tft.endWrite() before DMA is complete then the endWrite - // function will wait for the DMA to complete, so this may defeat any DMA performance benefit. - // - - bool initDMA(bool ctrl_cs = false); // Initialise the DMA engine and attach to SPI bus - typically used in setup() - // Parameter "true" enables DMA engine control of TFT chip select (ESP32 only) - // For ESP32 only, TFT reads will not work if parameter is true - void deInitDMA(void); // De-initialise the DMA engine and detach from SPI bus - typically not used - - // Push an image to the TFT using DMA, buffer is optional and grabs (double buffers) a copy of the image - // Use the buffer if the image data will get over-written or destroyed while DMA is in progress - // - // Note 1: If swapping colour bytes is defined, and the double buffer option is NOT used, then the bytes - // in the original image buffer content will be byte swapped by the function before DMA is initiated. - // - // Note 2: If part of the image will be off screen or outside of a set viewport, then the the original - // image buffer content will be altered to a correctly clipped image before DMA is initiated. - // - // The function will wait for the last DMA to complete if it is called while a previous DMA is still - // in progress, this simplifies the sketch and helps avoid "gotchas". - void pushImageDMA(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t* data, uint16_t* buffer = nullptr); - -#if defined (ESP32) // ESP32 only at the moment - // For case where pointer is a const and the image data must not be modified (clipped or byte swapped) - void pushImageDMA(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t const* data); -#endif - // Push a block of pixels into a window set up using setAddrWindow() - void pushPixelsDMA(uint16_t* image, uint32_t len); - - // Check if the DMA is complete - use while(tft.dmaBusy); for a blocking wait - bool dmaBusy(void); // returns true if DMA is still in progress - void dmaWait(void); // wait until DMA is complete - - bool DMA_Enabled = false; // Flag for DMA enabled state - uint8_t spiBusyCheck = 0; // Number of ESP32 transfer buffers to check - - // Bare metal functions - void startWrite(void); // Begin SPI transaction - void writeColor(uint16_t color, uint32_t len); // Deprecated, use pushBlock() - void endWrite(void); // End SPI transaction - - // Set/get an arbitrary library configuration attribute or option - // Use to switch ON/OFF capabilities such as UTF8 decoding - each attribute has a unique ID - // id = 0: reserved - may be used in future to reset all attributes to a default state - // id = 1: Turn on (a=true) or off (a=false) GLCD cp437 font character error correction - // id = 2: Turn on (a=true) or off (a=false) UTF8 decoding - // id = 3: Enable or disable use of ESP32 PSRAM (if available) - #define CP437_SWITCH 1 - #define UTF8_SWITCH 2 - #define PSRAM_ENABLE 3 - void setAttribute(uint8_t id = 0, uint8_t a = 0); // Set attribute value - uint8_t getAttribute(uint8_t id = 0); // Get attribute value - - // Used for diagnostic sketch to see library setup adopted by compiler, see Section 7 above - void getSetup(setup_t& tft_settings); // Sketch provides the instance to populate - bool verifySetupID(uint32_t id); - - // Global variables -#if !defined (TFT_PARALLEL_8_BIT) && !defined (RP2040_PIO_INTERFACE) - static SPIClass& getSPIinstance(void); // Get SPI class handle -#endif - uint32_t textcolor, textbgcolor; // Text foreground and background colours - - uint32_t bitmap_fg, bitmap_bg; // Bitmap foreground (bit=1) and background (bit=0) colours - - uint8_t textfont, // Current selected font number - textsize, // Current font size multiplier - textdatum, // Text reference datum - rotation; // Display rotation (0-3) - - uint8_t decoderState = 0; // UTF8 decoder state - not for user access - uint16_t decoderBuffer; // Unicode code-point buffer - not for user access - - //Moved here to Core2 capture it - bool locked, inTransaction, lockTransaction; // SPI transaction and mutex lock flags - int32_t cursor_x, cursor_y, padX; // Text cursor x,y and padding setting - //--------------------------------------- private ------------------------------------// - private: - // Legacy begin and end prototypes - deprecated TODO: delete - void spi_begin(); - void spi_end(); - - void spi_begin_read(); - void spi_end_read(); - - // New begin and end prototypes - // begin/end a TFT write transaction - // For SPI bus the transmit clock rate is set - inline void begin_tft_write() __attribute__((always_inline)); - inline void end_tft_write() __attribute__((always_inline)); - - // begin/end a TFT read transaction - // For SPI bus: begin lowers SPI clock rate, end reinstates transmit clock rate - inline void begin_tft_read() __attribute__((always_inline)); - inline void end_tft_read() __attribute__((always_inline)); - - // Initialise the data bus GPIO and hardware interfaces - void initBus(void); + // New begin and end prototypes + // begin/end a TFT write transaction + // For SPI bus the transmit clock rate is set + inline void begin_tft_write() __attribute__((always_inline)); + inline void end_tft_write() __attribute__((always_inline)); - // Temporary library development function TODO: remove need for this - void pushSwapBytePixels(const void* data_in, uint32_t len); + // begin/end a TFT read transaction + // For SPI bus: begin lowers SPI clock rate, end reinstates transmit clock rate + inline void begin_tft_read() __attribute__((always_inline)); + inline void end_tft_read() __attribute__((always_inline)); - // Same as setAddrWindow but exits with CGRAM in read mode - void readAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h); + // Initialise the data bus GPIO and hardware interfaces + void initBus(void); - // Byte read prototype - uint8_t readByte(void); + // Temporary library development function TODO: remove need for this + void pushSwapBytePixels(const void *data_in, uint32_t len); - // GPIO parallel bus input/output direction control - void busDir(uint32_t mask, uint8_t mode); + // Same as setAddrWindow but exits with CGRAM in read mode + void readAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h); - // Single GPIO input/output direction control - void gpioMode(uint8_t gpio, uint8_t mode); + // Byte read prototype + uint8_t readByte(void); - // Smooth graphics helper - uint8_t sqrt_fraction(uint32_t num); + // GPIO parallel bus input/output direction control + void busDir(uint32_t mask, uint8_t mode); - // Helper function: calculate distance of a point from a finite length line between two points - float wedgeLineDistance(float pax, float pay, float bax, float bay, float dr); + // Single GPIO input/output direction control + void gpioMode(uint8_t gpio, uint8_t mode); - // Display variant settings - uint8_t tabcolor, // ST7735 screen protector "tab" colour (now invalid) - colstart = 0, rowstart = 0; // Screen display area to CGRAM area coordinate offsets + // Smooth graphics helper + uint8_t sqrt_fraction(uint32_t num); - // Port and pin masks for control signals (ESP826 only) - TODO: remove need for this - volatile uint32_t *dcport, *csport; - uint32_t cspinmask, dcpinmask, wrpinmask, sclkpinmask; + // Helper function: calculate distance of a point from a finite length line between two points + float wedgeLineDistance(float pax, float pay, float bax, float bay, float dr); - #if defined(ESP32_PARALLEL) - // Bit masks for ESP32 parallel bus interface - uint32_t xclr_mask, xdir_mask; // Port set/clear and direction control masks + // Display variant settings + uint8_t tabcolor, // ST7735 screen protector "tab" colour (now invalid) + colstart = 0, rowstart = 0; // Screen display area to CGRAM area coordinate offsets - // Lookup table for ESP32 parallel bus interface uses 1kbyte RAM, - uint32_t xset_mask[256]; // Makes Sprite rendering test 33% faster, for slower macro equivalent - // see commented out #define set_mask(C) within TFT_eSPI_ESP32.h - #endif + // Port and pin masks for control signals (ESP826 only) - TODO: remove need for this + volatile uint32_t *dcport, *csport; + uint32_t cspinmask, dcpinmask, wrpinmask, sclkpinmask; - //uint32_t lastColor = 0xFFFF; // Last colour - used to minimise bit shifting overhead +#if defined(ESP32_PARALLEL) + // Bit masks for ESP32 parallel bus interface + uint32_t xclr_mask, xdir_mask; // Port set/clear and direction control masks - getColorCallback getColor = nullptr; // Smooth font callback function pointer + // Lookup table for ESP32 parallel bus interface uses 1kbyte RAM, + uint32_t xset_mask[256]; // Makes Sprite rendering test 33% faster, for slower macro equivalent + // see commented out #define set_mask(C) within TFT_eSPI_ESP32.h +#endif + // uint32_t lastColor = 0xFFFF; // Last colour - used to minimise bit shifting overhead - //-------------------------------------- protected ----------------------------------// - protected: + getColorCallback getColor = nullptr; // Smooth font callback function pointer - //int32_t win_xe, win_ye; // Window end coords - not needed + //-------------------------------------- protected ----------------------------------// +protected: + // int32_t win_xe, win_ye; // Window end coords - not needed - int32_t _init_width, _init_height; // Display w/h as input, used by setRotation() - int32_t _width, _height; // Display w/h as modified by current rotation - int32_t addr_row, addr_col; // Window position - used to minimise window commands + int32_t _init_width, _init_height; // Display w/h as input, used by setRotation() + int32_t _width, _height; // Display w/h as modified by current rotation + int32_t addr_row, addr_col; // Window position - used to minimise window commands - int16_t _xPivot; // TFT x pivot point coordinate for rotated Sprites - int16_t _yPivot; // TFT x pivot point coordinate for rotated Sprites + int16_t _xPivot; // TFT x pivot point coordinate for rotated Sprites + int16_t _yPivot; // TFT x pivot point coordinate for rotated Sprites - // Viewport variables - int32_t _vpX, _vpY, _vpW, _vpH; // Note: x start, y start, x end + 1, y end + 1 - int32_t _xDatum; - int32_t _yDatum; - int32_t _xWidth; - int32_t _yHeight; - bool _vpDatum; - bool _vpOoB; + // Viewport variables + int32_t _vpX, _vpY, _vpW, _vpH; // Note: x start, y start, x end + 1, y end + 1 + int32_t _xDatum; + int32_t _yDatum; + int32_t _xWidth; + int32_t _yHeight; + bool _vpDatum; + bool _vpOoB; - int32_t bg_cursor_x; // Background fill cursor - int32_t last_cursor_x; // Previous text cursor position when fill used + int32_t bg_cursor_x; // Background fill cursor + int32_t last_cursor_x; // Previous text cursor position when fill used - uint32_t fontsloaded; // Bit field of fonts loaded + uint32_t fontsloaded; // Bit field of fonts loaded - uint8_t glyph_ab, // Smooth font glyph delta Y (height) above baseline - glyph_bb; // Smooth font glyph delta Y (height) below baseline + uint8_t glyph_ab, // Smooth font glyph delta Y (height) above baseline + glyph_bb; // Smooth font glyph delta Y (height) below baseline - bool isDigits; // adjust bounding box for numbers to reduce visual jiggling - bool textwrapX, textwrapY; // If set, 'wrap' text at right and optionally bottom edge of display - bool _swapBytes; // Swap the byte order for TFT pushImage() + bool isDigits; // adjust bounding box for numbers to reduce visual jiggling + bool textwrapX, textwrapY; // If set, 'wrap' text at right and optionally bottom edge of display + bool _swapBytes; // Swap the byte order for TFT pushImage() - bool _booted; // init() or begin() has already run once + bool _booted; // init() or begin() has already run once - // User sketch manages these via set/getAttribute() - bool _cp437; // If set, use correct CP437 charset (default is OFF) - bool _utf8; // If set, use UTF-8 decoder in print stream 'write()' function (default ON) - bool _psram_enable; // Enable PSRAM use for library functions (TBD) and Sprites + // User sketch manages these via set/getAttribute() + bool _cp437; // If set, use correct CP437 charset (default is OFF) + bool _utf8; // If set, use UTF-8 decoder in print stream 'write()' function (default ON) + bool _psram_enable; // Enable PSRAM use for library functions (TBD) and Sprites - uint32_t _lastColor; // Buffered value of last colour used + uint32_t _lastColor; // Buffered value of last colour used - bool _fillbg; // Fill background flag (just for for smooth fonts at the moment) + bool _fillbg; // Fill background flag (just for for smooth fonts at the moment) -#if defined (SSD1963_DRIVER) - uint16_t Cswap; // Swap buffer for SSD1963 - uint8_t r6, g6, b6; // RGB buffer for SSD1963 +#if defined(SSD1963_DRIVER) + uint16_t Cswap; // Swap buffer for SSD1963 + uint8_t r6, g6, b6; // RGB buffer for SSD1963 #endif #ifdef LOAD_GFXFF - GFXfont *gfxFont; + GFXfont *gfxFont; #endif /*************************************************************************************** @@ -966,42 +1016,43 @@ class TFT_eSPI : public Print { friend class TFT_eSprite; // Sprite class has ac ***************************************************************************************/ // Load the Touch extension #ifdef TOUCH_CS - #if defined (TFT_PARALLEL_8_BIT) || defined (RP2040_PIO_INTERFACE) - #if !defined(DISABLE_ALL_LIBRARY_WARNINGS) - #error >>>>------>> Touch functions not supported in 8/16-bit parallel mode or with RP2040 PIO. - #endif - #else - #include "Extensions/Touch.h" // Loaded if TOUCH_CS is defined by user - #endif +#if defined(TFT_PARALLEL_8_BIT) || defined(RP2040_PIO_INTERFACE) +#if !defined(DISABLE_ALL_LIBRARY_WARNINGS) +#error >>>>------>> Touch functions not supported in 8/16-bit parallel mode or with RP2040 PIO. +#endif #else - #if !defined(DISABLE_ALL_LIBRARY_WARNINGS) - #warning >>>>------>> TOUCH_CS pin not defined, TFT_eSPI touch functions will not be available! - #endif +#include "Extensions/Touch.h" // Loaded if TOUCH_CS is defined by user +#endif +#else +#if !defined(DISABLE_ALL_LIBRARY_WARNINGS) +#warning >>>>------>> TOUCH_CS pin not defined, TFT_eSPI touch functions will not be available! +#endif #endif // Load the Anti-aliased font extension #ifdef SMOOTH_FONT - #include "Extensions/Smooth_font.h" // Loaded if SMOOTH_FONT is defined by user +#include "Extensions/Smooth_font.h" // Loaded if SMOOTH_FONT is defined by user #endif }; // End of class TFT_eSPI // Swap any type -template static inline void -transpose(T& a, T& b) { T t = a; a = b; b = t; } +template static inline void transpose(T &a, T &b) { + T t = a; + a = b; + b = t; +} // Fast alphaBlend -template static inline uint16_t -fastBlend(A alpha, F fgc, B bgc) -{ - // Split out and blend 5-bit red and blue channels - uint32_t rxb = bgc & 0xF81F; - rxb += ((fgc & 0xF81F) - rxb) * (alpha >> 2) >> 6; - // Split out and blend 6-bit green channel - uint32_t xgx = bgc & 0x07E0; - xgx += ((fgc & 0x07E0) - xgx) * alpha >> 8; - // Recombine channels - return (rxb & 0xF81F) | (xgx & 0x07E0); +template static inline uint16_t fastBlend(A alpha, F fgc, B bgc) { + // Split out and blend 5-bit red and blue channels + uint32_t rxb = bgc & 0xF81F; + rxb += ((fgc & 0xF81F) - rxb) * (alpha >> 2) >> 6; + // Split out and blend 6-bit green channel + uint32_t xgx = bgc & 0x07E0; + xgx += ((fgc & 0x07E0) - xgx) * alpha >> 8; + // Recombine channels + return (rxb & 0xF81F) | (xgx & 0x07E0); } /*************************************************************************************** diff --git a/patch-c5.py b/patch-c5.py new file mode 100644 index 000000000..7396e8212 --- /dev/null +++ b/patch-c5.py @@ -0,0 +1,169 @@ +import hashlib +from typing import TYPE_CHECKING, Any +import requests + +if TYPE_CHECKING: + Import: Any = None + env: Any = {} + +import glob +import gzip +from os import makedirs, remove, rename +from os.path import basename, dirname, exists, isfile, join + +Import("env") # type: ignore + +FRAMEWORK_DIR = env.PioPlatform().get_package_dir("framework-arduinoespressif32-libs") +patchflag_path = join(FRAMEWORK_DIR, ".patched") +board_mcu = env.BoardConfig() +mcu = board_mcu.get("build.mcu", "") + +# patch file only if we didn't do it befored +if not isfile(join(FRAMEWORK_DIR, ".patched")): + original_file = join(FRAMEWORK_DIR, mcu, "lib", "libnet80211.a") + patched_file = join( + FRAMEWORK_DIR, mcu, "lib", "libnet80211.a.patched" + ) + + env.Execute( + "pio pkg exec -p toolchain-riscv32-esp -- riscv32-esp-elf-objcopy --weaken-symbol=s %s %s" + % (original_file, patched_file) + ) + if isfile("%s.old" % (original_file)): + remove("%s.old" % (original_file)) + rename(original_file, "%s.old" % (original_file)) + env.Execute( + "pio pkg exec -p toolchain-riscv32-esp -- riscv32-esp-elf-objcopy --weaken-symbol=ieee80211_raw_frame_sanity_check %s %s" + % (patched_file, original_file) + ) + + def _touch(path): + with open(path, "w") as fp: + fp.write("") + + env.Execute(lambda *args, **kwargs: _touch(patchflag_path)) + + +def hash_file(file_path): + """Generate SHA-256 hash for a single file.""" + hasher = hashlib.sha256() + with open(file_path, "rb") as f: + # Read the file in chunks to avoid memory issues + for chunk in iter(lambda: f.read(4096), b""): + hasher.update(chunk) + return hasher.hexdigest() + + +def hash_files(file_paths): + """Generate a combined hash for multiple files.""" + combined_hash = hashlib.sha256() + + for file_path in file_paths: + file_hash = hash_file(file_path) + combined_hash.update(file_hash.encode("utf-8")) # Update with the file's hash + + return combined_hash.hexdigest() + + +def save_checksum_file(hash_value, output_file): + """Save the hash value to a specified output file.""" + with open(output_file, "w") as f: + f.write(hash_value) + + +def load_checksum_file(input_file): + """Load the hash value from a specified input file.""" + with open(input_file, "r") as f: + return f.readline().strip() + +def minify_css(c): + minify_req = requests.post("https://www.toptal.com/developers/cssminifier/api/raw", {"input": c.read().decode('utf-8')}) + return c if minify_req is False else minify_req.text.encode('utf-8') + +def minify_js(js): + minify_req = requests.post('https://www.toptal.com/developers/javascript-minifier/api/raw', {'input': js.read().decode('utf-8')}) + return js if minify_req is False else minify_req.text.encode('utf-8') + +def minify_html(html): + minify_req = requests.post('https://www.toptal.com/developers/html-minifier/api/raw', {'input': html.read().decode('utf-8')}) + return html if minify_req is False else minify_req.text.encode('utf-8') + +# gzip web files +def prepare_www_files(): + HEADER_FILE = join(env.get("PROJECT_DIR"), "include", "webFiles.h") + filetypes_to_gzip = ["html", "css", "js"] + data_src_dir = join(env.get("PROJECT_DIR"), "embedded_resources/web_interface") + checksum_file = join(data_src_dir, "checksum.sha256") + checksum = "" + + if not exists(data_src_dir): + print(f'Error: Source directory "{data_src_dir}" does not exist!') + return + + if exists(checksum_file): + checksum = load_checksum_file(checksum_file) + + files_to_gzip = [] + for extension in filetypes_to_gzip: + files_to_gzip.extend(glob.glob(join(data_src_dir, "*." + extension))) + + files_checksum = hash_files(files_to_gzip) + if files_checksum == checksum: + print("[GZIP & EMBED INTO HEADER] - Nothing to process.") + return + + print(f"[GZIP & EMBED INTO HEADER] - Processing {len(files_to_gzip)} files.") + + makedirs(dirname(HEADER_FILE), exist_ok=True) + + with open(HEADER_FILE, "w") as header: + header.write( + "#ifndef WEB_FILES_H\n#define WEB_FILES_H\n\n#include \n\n" + ) + header.write( + "// THIS FILE IS AUTOGENERATED DO NOT MODIFY IT. MODIFY FILES IN /embedded_resources/web_interface\n\n" + ) + + for file in files_to_gzip: + gz_file = file + ".gz" + with open(file, "rb") as src, gzip.open(gz_file, "wb") as dst: + ext = basename(file).rsplit(".", 1)[-1].lower() + if ext == 'html': + minified = minify_html(src) + elif ext == 'css': + minified = minify_css(src) + elif ext == 'js': + minified = minify_js(src) + else: + raise ValueError(f"Unsupported file type: {ext}") + + dst.write(minified) + + with open(gz_file, "rb") as gz: + compressed_data = gz.read() + var_name = basename(file).replace(".", "_") + + header.write(f"const uint8_t {var_name}[] PROGMEM = {{\n") + + # Write hex values, inserting a newline every 15 bytes + for i in range(0, len(compressed_data), 15): + hex_chunk = ", ".join( + f"0x{byte:02X}" for byte in compressed_data[i : i + 15] + ) + header.write(f" {hex_chunk},\n") + + header.write("};\n\n") + header.write( + f"const uint32_t {var_name}_size = {len(compressed_data)};\n\n" + ) + + remove(gz_file) # Clean up temporary gzip file + + header.write("#endif // WEB_FILES_H\n") + + save_checksum_file(files_checksum, checksum_file) + + print(f"[DONE] Gzipped files embedded into {HEADER_FILE}") + + +prepare_www_files() diff --git a/platformio.ini b/platformio.ini index 81ce42c7f..e8781c961 100644 --- a/platformio.ini +++ b/platformio.ini @@ -19,7 +19,8 @@ default_envs = ;m5stack-cores3 ;CYD-2432S028 ;CYD-2USB - lilygo-t-embed-cc1101 + ;lilygo-t-embed-cc1101 + esp32-c5 ;lilygo-t-deck ;lilygo-t-display-s3 ;lilygo-t-lora-pager diff --git a/src/core/configPins.cpp b/src/core/configPins.cpp index 42064187d..43fdf8a38 100644 --- a/src/core/configPins.cpp +++ b/src/core/configPins.cpp @@ -1,4 +1,5 @@ #include "configPins.h" +#include "esp_mac.h" #include "sd_functions.h" String getMacAddress() { diff --git a/src/core/connect/esp_connection.cpp b/src/core/connect/esp_connection.cpp index dda6ba359..39dbf9983 100644 --- a/src/core/connect/esp_connection.cpp +++ b/src/core/connect/esp_connection.cpp @@ -201,3 +201,13 @@ void EspConnection::onDataRecv(const uint8_t *mac, const uint8_t *incomingData, recvQueue.push_back(recvMessage); } + +#ifdef ESP32C5 +void EspConnection::onDataSentStatic(const wifi_tx_info_t *info, esp_now_send_status_t status) { + if (instance) instance->onDataSent(info->src_addr, status); +} + +void EspConnection::onDataRecvStatic(const esp_now_recv_info_t *info, const uint8_t *incomingData, int len) { + if (instance) instance->onDataRecv(info->src_addr, incomingData, len); +} +#endif diff --git a/src/core/connect/esp_connection.h b/src/core/connect/esp_connection.h index 2c5e257ed..f529d4369 100644 --- a/src/core/connect/esp_connection.h +++ b/src/core/connect/esp_connection.h @@ -44,13 +44,17 @@ class EspConnection { static void setInstance(EspConnection *conn) { instance = conn; } +#ifdef ESP32C5 + static void onDataSentStatic(const wifi_tx_info_t *info, esp_now_send_status_t status); + static void onDataRecvStatic(const esp_now_recv_info_t *info, const uint8_t *incomingData, int len); +#else static void onDataSentStatic(const uint8_t *mac_addr, esp_now_send_status_t status) { if (instance) instance->onDataSent(mac_addr, status); }; static void onDataRecvStatic(const uint8_t *mac, const uint8_t *incomingData, int len) { if (instance) instance->onDataRecv(mac, incomingData, len); }; - +#endif protected: Status recvStatus; Status sendStatus; diff --git a/src/core/menu_items/NRF24.cpp b/src/core/menu_items/NRF24.cpp index 2eaf1eb59..b9b338808 100644 --- a/src/core/menu_items/NRF24.cpp +++ b/src/core/menu_items/NRF24.cpp @@ -50,6 +50,7 @@ void NRF24Menu::configMenu() { GPIO_NUM_NC} ); } +#ifndef ESP32C5 if (opt == 2) { bruceConfigPins.setNrf24Pins( {(gpio_num_t)SDCARD_SCK, @@ -60,6 +61,7 @@ void NRF24Menu::configMenu() { GPIO_NUM_NC} ); } +#endif } void NRF24Menu::drawIconImg() { drawImg( diff --git a/src/core/menu_items/OthersMenu.cpp b/src/core/menu_items/OthersMenu.cpp index 0149c49b9..28d5964ff 100644 --- a/src/core/menu_items/OthersMenu.cpp +++ b/src/core/menu_items/OthersMenu.cpp @@ -25,7 +25,9 @@ void OthersMenu::optionsMenu() { {"Clicker", clicker_setup }, #endif #ifndef LITE_VERSION +#ifndef ESP32C5 {"Openhaystack", openhaystack_setup }, +#endif {"Interpreter", run_bjs_script }, #endif {"iButton", setup_ibutton }, diff --git a/src/core/menu_items/RFIDMenu.cpp b/src/core/menu_items/RFIDMenu.cpp index 2f153af23..ee6c0d983 100644 --- a/src/core/menu_items/RFIDMenu.cpp +++ b/src/core/menu_items/RFIDMenu.cpp @@ -17,10 +17,12 @@ void RFIDMenu::optionsMenu() { {"Load file", [=]() { TagOMatic(TagOMatic::LOAD_MODE); } }, {"Erase data", [=]() { TagOMatic(TagOMatic::ERASE_MODE); } }, {"Write NDEF", [=]() { TagOMatic(TagOMatic::WRITE_NDEF_MODE); }}, +#ifndef ESP32C5 {"Amiibolink", [=]() { Amiibo(); } }, {"Chameleon", [=]() { Chameleon(); } }, {"PN532 BLE", [=]() { Pn532ble(); } }, {"PN532Killer", [=]() { PN532KillerTools(); } }, +#endif {"Config", [=]() { configMenu(); } }, }; addOptionToMainMenu(); diff --git a/src/core/menu_items/RFMenu.cpp b/src/core/menu_items/RFMenu.cpp index 4174ed4a0..e79901265 100644 --- a/src/core/menu_items/RFMenu.cpp +++ b/src/core/menu_items/RFMenu.cpp @@ -12,6 +12,7 @@ #include "modules/rf/rf_waterfall.h" void RFMenu::optionsMenu() { +#ifndef ESP32C5 options = { {"Scan/copy", [=]() { RFScan(); } }, {"Record RAW", rf_raw_record }, // Pablo-Ortiz-Lopez @@ -35,6 +36,7 @@ void RFMenu::optionsMenu() { else txt += " Tx: " + String(bruceConfig.rfTx) + " Rx: " + String(bruceConfig.rfRx); loopOptions(options, MENU_TYPE_SUBMENU, txt.c_str()); +#endif } void RFMenu::configMenu() { diff --git a/src/core/powerSave.cpp b/src/core/powerSave.cpp index 52d160c96..0db2738bd 100644 --- a/src/core/powerSave.cpp +++ b/src/core/powerSave.cpp @@ -37,12 +37,12 @@ void sleepModeOn() { fadeOutScreen(startDimmerBright); - panelSleep(true); // power down screen - disableCore0WDT(); +#ifndef ESP32C5 disableCore1WDT(); +#endif disableLoopWDT(); delay(200); } @@ -51,13 +51,13 @@ void sleepModeOff() { isSleeping = false; setCpuFrequencyMhz(240); - panelSleep(false); // wake the screen back up - getBrightness(); enableCore0WDT(); +#ifndef ESP32C5 enableCore1WDT(); +#endif enableLoopWDT(); feedLoopWDT(); delay(200); diff --git a/src/core/sd_functions.cpp b/src/core/sd_functions.cpp index 850e0d01a..1de7900a5 100644 --- a/src/core/sd_functions.cpp +++ b/src/core/sd_functions.cpp @@ -14,9 +14,13 @@ #include #include -#include // for std::sort -#include // for CRC32 +#include // for std::sort +#ifdef ESP32C5 +#include +#else +#include // for CRC32 +#endif // SPIClass sdcardSPI; String fileToCopy; std::vector fileList; @@ -427,10 +431,15 @@ String md5File(FS &fs, String filepath) { String crc32File(FS &fs, String filepath) { if (!fs.exists(filepath)) return ""; String txt = readSmallFile(fs, filepath); - // derived from - // https://techoverflow.net/2022/08/05/how-to-compute-crc32-with-ethernet-polynomial-0x04c11db7-on-esp32-crc-h/ +// derived from +// https://techoverflow.net/2022/08/05/how-to-compute-crc32-with-ethernet-polynomial-0x04c11db7-on-esp32-crc-h/ +#ifdef ESP32C5 + uint32_t romCRC = + (~esp_rom_crc32_le((uint32_t)~(0xffffffff), (const uint8_t *)txt.c_str(), txt.length())) ^ 0xffffffff; +#else uint32_t romCRC = (~crc32_le((uint32_t)~(0xffffffff), (const uint8_t *)txt.c_str(), txt.length())) ^ 0xffffffff; +#endif char s[18] = {0}; char crcBytes[4] = {0}; memcpy(crcBytes, &romCRC, sizeof(uint32_t)); @@ -769,36 +778,33 @@ String loopSD(FS &fs, bool filePicker, String allowed_ext, String rootPath) { } if (filepath.endsWith(".enc")) { // encrypted files options.insert( - options.begin(), - {"Decrypt+Type", - [&]() { - String plaintext = readDecryptedFile(fs, filepath); - if (plaintext.length() == 0) - return displayError( - "Decryption failed", true - ); // file is too big or cannot read, or cancelled - // else - plaintext.trim(); // remove newlines - key_input_from_string(plaintext); - }} + options.begin(), {"Decrypt+Type", [&]() { + String plaintext = readDecryptedFile(fs, filepath); + if (plaintext.length() == 0) + return displayError( + "Decryption failed", true + ); // file is too big or cannot read, or cancelled + // else + plaintext.trim(); // remove newlines + key_input_from_string(plaintext); + }} ); } #endif if (filepath.endsWith(".enc")) { // encrypted files - options.insert(options.begin(), {"Decrypt+Show", [&]() { - String plaintext = - readDecryptedFile(fs, filepath); - delay(200); - if (plaintext.length() == 0) - return displayError( - "Decryption failed", true - ); - plaintext.trim(); // remove newlines - // if(plaintext.length()<..) - displaySuccess(plaintext, true); - // else - // TODO: show in the text viewer - }}); + options.insert( + options.begin(), {"Decrypt+Show", [&]() { + String plaintext = readDecryptedFile(fs, filepath); + delay(200); + if (plaintext.length() == 0) + return displayError("Decryption failed", true); + plaintext.trim(); // remove newlines + // if(plaintext.length()<..) + displaySuccess(plaintext, true); + // else + // TODO: show in the text viewer + }} + ); } #if defined(HAS_NS4168_SPKR) if (isAudioFile(filepath)) diff --git a/src/core/serialcmds.cpp b/src/core/serialcmds.cpp index 3a69cfcf2..4d59093e9 100644 --- a/src/core/serialcmds.cpp +++ b/src/core/serialcmds.cpp @@ -29,9 +29,13 @@ void startSerialCommandsHandlerTask() { 20000, // Stack size in bytes NULL, // This is a pointer to the parameter that will be passed to the new task. We are not using it // here and therefore it is set to NULL. - 2, // Priority of the task + 2, // Priority of the task &serialcmdsTaskHandle, // Task handle (optional, can be NULL). +#ifndef ESP32C5 1 // Core where the task should run. By default, all your Arduino code runs on Core 1 and the Wi-Fi // and RF functions +#else + 0 // Core where the task should run. ESP32-C5 has only one core +#endif ); // (these are usually hidden from the Arduino environment) use the Core 0. } diff --git a/src/core/settings.cpp b/src/core/settings.cpp index b67fc4b6e..31fee2b6d 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -536,6 +536,7 @@ void setRFModuleMenu() { GPIO_NUM_NC} ); } else if (pins_setup == 2) { +#ifndef ESP32C5 result = CC1101_SPI_MODULE; bruceConfigPins.setCC1101Pins( {(gpio_num_t)SDCARD_SCK, @@ -545,6 +546,7 @@ void setRFModuleMenu() { GPIO_NUM_32, GPIO_NUM_NC} ); +#endif } if (initRfModule()) { bruceConfig.setRfModule(CC1101_SPI_MODULE); diff --git a/src/core/startup_app.cpp b/src/core/startup_app.cpp index 790129fe1..c8f129864 100644 --- a/src/core/startup_app.cpp +++ b/src/core/startup_app.cpp @@ -32,8 +32,10 @@ StartupApp::StartupApp() { #endif _startupApps["Wardriving"] = []() { Wardriving(); }; _startupApps["WebUI"] = []() { startWebUi(); }; +#ifndef ESP32C5 _startupApps["PN532 BLE"] = []() { Pn532ble(); }; _startupApps["PN532Killer"] = []() { PN532KillerTools(); }; +#endif } bool StartupApp::startApp(const String &appName) const { diff --git a/src/modules/bjs_interpreter/interpreter.cpp b/src/modules/bjs_interpreter/interpreter.cpp index 01b1dcc6f..01ea7835a 100644 --- a/src/modules/bjs_interpreter/interpreter.cpp +++ b/src/modules/bjs_interpreter/interpreter.cpp @@ -103,8 +103,10 @@ static duk_ret_t native_analogRead(duk_context *ctx) { } static duk_ret_t native_touchRead(duk_context *ctx) { +#ifndef ESP32C5 int val = touchRead(duk_to_int(ctx, 0)); duk_push_int(ctx, val); +#endif return 1; } @@ -118,13 +120,22 @@ static duk_ret_t native_dacWrite(duk_context *ctx) { } static duk_ret_t native_ledcSetup(duk_context *ctx) { +#ifdef ESP32C5 + int val = ledcAttach(duk_get_int(ctx, 0), 50, duk_get_int(ctx, 1)); +#else int val = ledcSetup(duk_get_int(ctx, 0), duk_get_int(ctx, 1), duk_get_int(ctx, 2)); +#endif duk_push_int(ctx, val); + return 1; } static duk_ret_t native_ledcAttachPin(duk_context *ctx) { +#ifdef ESP32C5 + ledcAttach(duk_get_int(ctx, 0), 50, duk_get_int(ctx, 1)); +#else ledcAttachPin(duk_get_int(ctx, 0), duk_get_int(ctx, 1)); +#endif return 0; } diff --git a/src/modules/ble/ble_common.cpp b/src/modules/ble/ble_common.cpp index 1c94e77da..15e99caa1 100644 --- a/src/modules/ble/ble_common.cpp +++ b/src/modules/ble/ble_common.cpp @@ -1,7 +1,7 @@ #include "ble_common.h" #include "core/mykeyboard.h" #include "core/utils.h" - +#include "esp_mac.h" #define SERVICE_UUID "1bc68b2a-f3e3-11e9-81b4-2a2ae2dbcce4" #define CHARACTERISTIC_RX_UUID "1bc68da0-f3e3-11e9-81b4-2a2ae2dbcce4" #define CHARACTERISTIC_TX_UUID "1bc68efe-f3e3-11e9-81b4-2a2ae2dbcce4" @@ -56,8 +56,11 @@ void ble_info(String name, String address, String signal) { break; } } - +#ifdef ESP32C5 +class AdvertisedDeviceCallbacks : public NimBLEScanCallbacks { +#else class AdvertisedDeviceCallbacks : public NimBLEAdvertisedDeviceCallbacks { +#endif void onResult(NimBLEAdvertisedDevice *advertisedDevice) { String bt_title; String bt_name; @@ -84,7 +87,12 @@ class AdvertisedDeviceCallbacks : public NimBLEAdvertisedDeviceCallbacks { void ble_scan_setup() { BLEDevice::init(""); pBLEScan = BLEDevice::getScan(); +#ifdef ESP32C5 + pBLEScan->setScanCallbacks(new AdvertisedDeviceCallbacks()); +#else pBLEScan->setAdvertisedDeviceCallbacks(new AdvertisedDeviceCallbacks()); +#endif + // Active scan uses more power, but get results faster pBLEScan->setActiveScan(true); pBLEScan->setInterval(SCAN_INT); @@ -92,7 +100,12 @@ void ble_scan_setup() { pBLEScan->setWindow(SCAN_WINDOW); // Bluetooth MAC Address +#ifdef ESP32C5 esp_read_mac(sta_mac, ESP_MAC_BT); +#else + esp_read_mac(sta_mac, ESP_MAC_BT); +#endif + sprintf( strID, "%02X:%02X:%02X:%02X:%02X:%02X", @@ -111,7 +124,11 @@ void ble_scan() { options = {}; ble_scan_setup(); +#ifdef ESP32C5 + pBLEScan->start(scanTime, false); +#else BLEScanResults foundDevices = pBLEScan->start(scanTime, false); +#endif addOptionToMainMenu(); diff --git a/src/modules/ble/ble_ninebot.cpp b/src/modules/ble/ble_ninebot.cpp index e01a2b77b..3a13c4a27 100644 --- a/src/modules/ble/ble_ninebot.cpp +++ b/src/modules/ble/ble_ninebot.cpp @@ -8,69 +8,81 @@ */ #include "ble_ninebot.h" -#include "core/utils.h" #include "core/mykeyboard.h" -#include +#include "core/utils.h" #include +#include + +#define SCAN_TIME 5 // Scan duration in seconds +#define SCAN_INTERVAL 100 // BLE scan interval +#define SCAN_WINDOW 99 // BLE scan window +#define CMD_DELAY 500 // UI delay after commands +#define UI_READ_DELAY 2000 // UI delay for read feedback -#define SCAN_TIME 5 // Scan duration in seconds -#define SCAN_INTERVAL 100 // BLE scan interval -#define SCAN_WINDOW 99 // BLE scan window -#define CMD_DELAY 500 // UI delay after commands -#define UI_READ_DELAY 2000 // UI delay for read feedback +#ifdef ESP32C5 +#define __Override__ +#else +#define __Override__ override +#endif -static NimBLEScan* pBLEScan; -static NimBLEClient* pClient = nullptr; +static NimBLEScan *pBLEScan; +static NimBLEClient *pClient = nullptr; -//Nordic UART Service (NUS) +// Nordic UART Service (NUS) static NimBLEUUID uartServiceUUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E"); -static NimBLEUUID txCharUUID ("6E400002-B5A3-F393-E0A9-E50E24DCCA9E"); +static NimBLEUUID txCharUUID("6E400002-B5A3-F393-E0A9-E50E24DCCA9E"); bool scooterDisconnected = true; class ScooterClientCallbacks : public NimBLEClientCallbacks { - void onDisconnect(NimBLEClient* client) override { - scooterDisconnected = true; - } + void onDisconnect(NimBLEClient *client) __Override__ { scooterDisconnected = true; } }; - -static const uint8_t max2Gpayload[] = {0x55,0xAB,0x4D,0x41,0x58,0x32,0x53,0x63,0x6F,0x6F,0x74,0x65,0x72,0x5F,0x31}; -static const uint8_t f2payload[] = {0x55,0xAB,0x46,0x32,0x53,0x63,0x6F,0x6F,0x74,0x65,0x72,0x5F,0x31}; -static const uint8_t gen1[] = {0x5A,0xA5,0x00,0x4B,0x48,0x94,0xE3,0x3A,0x91,0xE0,0x32,0x7E,0xC2}; -static const uint8_t gen2[] = {0x5A,0xA5,0x00,0x4B,0x48,0x94,0xE3,0x3A,0x91,0xE0,0x43,0x3E,0xC2}; -static const uint8_t gen3[] = {0x5A,0xA5,0x00,0x4B,0x48,0x94,0xE3,0x3A,0x91,0xE0,0x42,0x7E,0xC4}; - -struct PayloadDef { const char* label; const uint8_t* data; size_t len; }; +static const uint8_t max2Gpayload[] = { + 0x55, 0xAB, 0x4D, 0x41, 0x58, 0x32, 0x53, 0x63, 0x6F, 0x6F, 0x74, 0x65, 0x72, 0x5F, 0x31 +}; +static const uint8_t f2payload[] = { + 0x55, 0xAB, 0x46, 0x32, 0x53, 0x63, 0x6F, 0x6F, 0x74, 0x65, 0x72, 0x5F, 0x31 +}; +static const uint8_t gen1[] = {0x5A, 0xA5, 0x00, 0x4B, 0x48, 0x94, 0xE3, 0x3A, 0x91, 0xE0, 0x32, 0x7E, 0xC2}; +static const uint8_t gen2[] = {0x5A, 0xA5, 0x00, 0x4B, 0x48, 0x94, 0xE3, 0x3A, 0x91, 0xE0, 0x43, 0x3E, 0xC2}; +static const uint8_t gen3[] = {0x5A, 0xA5, 0x00, 0x4B, 0x48, 0x94, 0xE3, 0x3A, 0x91, 0xE0, 0x42, 0x7E, 0xC4}; + +struct PayloadDef { + const char *label; + const uint8_t *data; + size_t len; +}; static const PayloadDef payloads[] = { {"Ninebot Max 2G / G30", max2Gpayload, sizeof(max2Gpayload)}, - {"Ninebot F2", f2payload, sizeof(f2payload)}, - {"Ninebot Generic 1", gen1, sizeof(gen1)}, - {"Ninebot Generic 2", gen2, sizeof(gen2)}, - {"Ninebot Generic 3", gen3, sizeof(gen3)} + {"Ninebot F2", f2payload, sizeof(f2payload) }, + {"Ninebot Generic 1", gen1, sizeof(gen1) }, + {"Ninebot Generic 2", gen2, sizeof(gen2) }, + {"Ninebot Generic 3", gen3, sizeof(gen3) } }; -static std::vector