From 8ff9826b3d179e116d0061c8c3302994b0fa4b12 Mon Sep 17 00:00:00 2001 From: mhwlng Date: Fri, 27 Sep 2024 17:49:56 +0200 Subject: [PATCH] add support for lilygo t-panel (#370) * add support for lilygo t-panel add support for lilygo t-panel (the rs485 version with an ESP32-S3 and ESP32-G2) https://www.lilygo.cc/products/t-panel-s3 * refactor * moved gfx library to lib2 folder and removed examples * removed lib2\Arduino_GFX-1.3.7 and switch to moononournation/GFX Library for Arduino@^1.4.9 * added lilygo lib2\Arduino_GFX-1.3.7 back and removed all unused files. went from 166 files to 15 files! --- .../OpenEPaperLink_esp32_C6_AP/compile.ps1 | 42 + .../OpenEPaperLink_esp32_H2_AP/.gitignore | 8 + .../OpenEPaperLink_esp32_H2_AP/CMakeLists.txt | 6 + .../OpenEPaperLink_esp32_H2_AP/compile.ps1 | 45 + .../main/CMakeLists.txt | 9 + .../main/Kconfig.projbuild | 104 + .../main/SubGigRadio.c | 562 ++++ .../main/SubGigRadio.h | 40 + .../main/cc1101_radio.c | 790 +++++ .../main/cc1101_radio.h | 118 + .../OpenEPaperLink_esp32_H2_AP/main/led.c | 48 + .../OpenEPaperLink_esp32_H2_AP/main/led.h | 6 + .../OpenEPaperLink_esp32_H2_AP/main/main.c | 833 +++++ .../OpenEPaperLink_esp32_H2_AP/main/main.h | 1 + .../OpenEPaperLink_esp32_H2_AP/main/proto.h | 194 ++ .../OpenEPaperLink_esp32_H2_AP/main/radio.c | 150 + .../OpenEPaperLink_esp32_H2_AP/main/radio.h | 20 + .../main/second_uart.c | 116 + .../main/second_uart.h | 18 + .../OpenEPaperLink_esp32_H2_AP/main/utils.c | 36 + .../OpenEPaperLink_esp32_H2_AP/main/utils.h | 5 + .../OpenEPaperLink_esp32_H2_AP/partitions.csv | 5 + .../sdkconfig.defaults | 8 + ESP32_AP-Flasher/compile.ps1 | 76 + ESP32_AP-Flasher/compileyellow.ps1 | 69 + ESP32_AP-Flasher/data/tagtypes/E2.json | 64 + .../data/www/content_cards.json.gz | Bin 4677 -> 4681 bytes ESP32_AP-Flasher/data/www/flash.js.gz | Bin 1418 -> 1411 bytes ESP32_AP-Flasher/data/www/index.html.gz | Bin 6354 -> 6347 bytes .../data/www/jsontemplate-demo.html.gz | Bin 623 -> 618 bytes ESP32_AP-Flasher/data/www/main.js.gz | Bin 14693 -> 14765 bytes ESP32_AP-Flasher/data/www/ota.js.gz | Bin 5466 -> 5459 bytes ESP32_AP-Flasher/include/ips_display.h | 94 +- .../Arduino_GFX-1.3.7/src/Arduino_DataBus.cpp | 167 + .../Arduino_GFX-1.3.7/src/Arduino_DataBus.h | 293 ++ .../lib2/Arduino_GFX-1.3.7/src/Arduino_G.cpp | 279 ++ .../lib2/Arduino_GFX-1.3.7/src/Arduino_G.h | 50 + .../Arduino_GFX-1.3.7/src/Arduino_GFX.cpp | 2898 +++++++++++++++++ .../lib2/Arduino_GFX-1.3.7/src/Arduino_GFX.h | 447 +++ .../src/Arduino_GFX_Library.h | 208 ++ .../src/databus/Arduino_ESP32RGBPanel.cpp | 129 + .../src/databus/Arduino_ESP32RGBPanel.h | 100 + .../src/databus/Arduino_XL9535SWSPI.cpp | 276 ++ .../src/databus/Arduino_XL9535SWSPI.h | 57 + .../src/display/Arduino_RGB_Display.cpp | 498 +++ .../src/display/Arduino_RGB_Display.h | 1325 ++++++++ .../Arduino_GFX-1.3.7/src/font/glcdfont.h | 280 ++ .../lib2/Arduino_GFX-1.3.7/src/gfxfont.h | 31 + ESP32_AP-Flasher/platformio.ini | 67 + ESP32_AP-Flasher/src/ips_display.cpp | 225 ++ ESP32_AP-Flasher/src/makeimage.cpp | 23 + resources/tagtypes/E2.json | 64 + 52 files changed, 10883 insertions(+), 1 deletion(-) create mode 100644 ARM_Tag_FW/OpenEPaperLink_esp32_C6_AP/compile.ps1 create mode 100644 ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/.gitignore create mode 100644 ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/CMakeLists.txt create mode 100644 ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/compile.ps1 create mode 100644 ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/CMakeLists.txt create mode 100644 ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/Kconfig.projbuild create mode 100644 ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/SubGigRadio.c create mode 100644 ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/SubGigRadio.h create mode 100644 ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/cc1101_radio.c create mode 100644 ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/cc1101_radio.h create mode 100644 ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/led.c create mode 100644 ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/led.h create mode 100644 ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/main.c create mode 100644 ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/main.h create mode 100644 ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/proto.h create mode 100644 ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/radio.c create mode 100644 ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/radio.h create mode 100644 ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/second_uart.c create mode 100644 ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/second_uart.h create mode 100644 ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/utils.c create mode 100644 ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/utils.h create mode 100644 ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/partitions.csv create mode 100644 ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/sdkconfig.defaults create mode 100644 ESP32_AP-Flasher/compile.ps1 create mode 100644 ESP32_AP-Flasher/compileyellow.ps1 create mode 100644 ESP32_AP-Flasher/data/tagtypes/E2.json create mode 100644 ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/Arduino_DataBus.cpp create mode 100644 ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/Arduino_DataBus.h create mode 100644 ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/Arduino_G.cpp create mode 100644 ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/Arduino_G.h create mode 100644 ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/Arduino_GFX.cpp create mode 100644 ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/Arduino_GFX.h create mode 100644 ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/Arduino_GFX_Library.h create mode 100644 ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/databus/Arduino_ESP32RGBPanel.cpp create mode 100644 ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/databus/Arduino_ESP32RGBPanel.h create mode 100644 ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/databus/Arduino_XL9535SWSPI.cpp create mode 100644 ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/databus/Arduino_XL9535SWSPI.h create mode 100644 ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/display/Arduino_RGB_Display.cpp create mode 100644 ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/display/Arduino_RGB_Display.h create mode 100644 ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/font/glcdfont.h create mode 100644 ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/gfxfont.h create mode 100644 resources/tagtypes/E2.json diff --git a/ARM_Tag_FW/OpenEPaperLink_esp32_C6_AP/compile.ps1 b/ARM_Tag_FW/OpenEPaperLink_esp32_C6_AP/compile.ps1 new file mode 100644 index 00000000..1673ea4e --- /dev/null +++ b/ARM_Tag_FW/OpenEPaperLink_esp32_C6_AP/compile.ps1 @@ -0,0 +1,42 @@ +&(Join-Path $env:USERPROFILE '\esp\esp-idf\export.ps1') + +idf.py fullclean + +idf.py build + +#python -m esptool -p COM8 -b 460800 --before default_reset --after hard_reset --chip esp32c6 write_flash --flash_mode dio --flash_size detect --flash_freq 80m 0x0 build\bootloader\bootloader.bin 0x8000 build\partition_table\partition-table.bin 0x10000 build\OpenEPaperLink_esp32_C6.bin + + +#idf.py -p COM8 flash + + +#esptool.py v4.7.0 +#Serial port COM8 +#Connecting... +#Chip is ESP32-C6 (QFN40) (revision v0.0) +#Features: WiFi 6, BT 5, IEEE802.15.4 +#Crystal is 40MHz +#MAC: 40:4c:ca:ff:fe:47:b4:f0 +#BASE MAC: 40:4c:ca:47:b4:f0 +#MAC_EXT: ff:fe +#Uploading stub... +#Running stub... +#Stub running... +#Changing baud rate to 460800 +#Changed. +#Configuring flash size... +#Auto-detected Flash size: 4MB +#Flash will be erased from 0x00000000 to 0x00005fff... +#Flash will be erased from 0x00008000 to 0x00008fff... +#Flash will be erased from 0x00010000 to 0x00051fff... +#Compressed 22336 bytes to 13474... +#Wrote 22336 bytes (13474 compressed) at 0x00000000 in 0.3 seconds (effective 624.9 kbit/s)... +#Hash of data verified. +#Compressed 3072 bytes to 104... +#Wrote 3072 bytes (104 compressed) at 0x00008000 in 0.1 seconds (effective 361.3 kbit/s)... +#Hash of data verified. +#Compressed 268624 bytes to 140956... +#Wrote 268624 bytes (140956 compressed) at 0x00010000 in 1.2 seconds (effective 1725.6 kbit/s)... +#Hash of data verified. +#Leaving... +#Hard resetting via RTS pin... diff --git a/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/.gitignore b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/.gitignore new file mode 100644 index 00000000..a932ece5 --- /dev/null +++ b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/.gitignore @@ -0,0 +1,8 @@ +build +*.axf +# Allow +!*.bin + +.vscode +sdkconfig +sdkconfig.old diff --git a/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/CMakeLists.txt b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/CMakeLists.txt new file mode 100644 index 00000000..031a676d --- /dev/null +++ b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(OpenEPaperLink_esp32_C6) diff --git a/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/compile.ps1 b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/compile.ps1 new file mode 100644 index 00000000..84d0c3fa --- /dev/null +++ b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/compile.ps1 @@ -0,0 +1,45 @@ + +#| Function | ESP32H2 Pin | ESP32S3 Pin +#| :--------------------------------: | :-------------------: | :------------------ +#| Activate The BOOT Mode Of ESP32H2 | ESP32H2_IO9 | ESP32S3_IO33 +#| Reset ESP32H2 | ESP32H2_Pin Number 8 | ESP32S3_IO34 +#| Uart | ESP32H2_TX_IO24 | ESP32S3_RX_IO48 +#| Uart | ESP32H2_RX_IO23 | ESP32S3_TX_IO47 + +&(Join-Path $env:USERPROFILE '\esp\esp-idf\export.ps1') + +idf.py fullclean + +idf.py build + +#python -m esptool -p COM11 -b 460800 --before default_reset --after hard_reset --chip esp32h2 write_flash --flash_mode dio --flash_size detect --flash_freq 48m 0x0 build\bootloader\bootloader.bin 0x8000 build\partition_table\partition-table.bin 0x10000 build\OpenEPaperLink_esp32_H2.bin + + +#idf.py -p COM8 flash + + +#Chip is ESP32-H2 (revision v0.1) +#Features: BLE, IEEE802.15.4 +#Crystal is 32MHz +#MAC: 74:4d:bd:ff:fe:63:84:e8 +#BASE MAC: 74:4d:bd:63:84:e8 +#MAC_EXT: ff:fe +#Uploading stub... +#Running stub... +#Stub running... +#Changing baud rate to 460800 +#Changed. +#Configuring flash size... +#Auto-detected Flash size: 4MB +#Flash will be erased from 0x00000000 to 0x00005fff... +#Flash will be erased from 0x00008000 to 0x00008fff... +#Flash will be erased from 0x00010000 to 0x0004dfff... +#Compressed 22080 bytes to 13385... +#Wrote 22080 bytes (13385 compressed) at 0x00000000 in 0.3 seconds (effective 511.5 kbit/s)... +#Hash of data verified. +#Compressed 3072 bytes to 104... +#Wrote 3072 bytes (104 compressed) at 0x00008000 in 0.1 seconds (effective 309.1 kbit/s)... +#Hash of data verified. +#Compressed 253952 bytes to 133359... +#Wrote 253952 bytes (133359 compressed) at 0x00010000 in 1.9 seconds (effective 1098.1 kbit/s)... +#Hash of data verified. diff --git a/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/CMakeLists.txt b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/CMakeLists.txt new file mode 100644 index 00000000..88baa379 --- /dev/null +++ b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/CMakeLists.txt @@ -0,0 +1,9 @@ +idf_component_register( SRCS + SRCS "utils.c" + SRCS "second_uart.c" + SRCS "radio.c" + SRCS "SubGigRadio.c" + SRCS "cc1101_radio.c" + SRCS "led.c" + SRCS "main.c" + INCLUDE_DIRS ".") diff --git a/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/Kconfig.projbuild b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/Kconfig.projbuild new file mode 100644 index 00000000..7bac71c1 --- /dev/null +++ b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/Kconfig.projbuild @@ -0,0 +1,104 @@ +menu "OEPL Hardware config" + + choice OEPL_HARDWARE_PROFILE + prompt "Hardware profile" + default OEPL_HARDWARE_PROFILE_DEFAULT + + config OEPL_HARDWARE_PROFILE_DEFAULT + bool "Default" + + config OEPL_HARDWARE_PROFILE_POE_AP + bool "PoE-AP" + + config OEPL_HARDWARE_PROFILE_CUSTOM + bool "Custom" + + endchoice + + config OEPL_HARDWARE_UART_TX + depends on OEPL_HARDWARE_PROFILE_CUSTOM + int "GPIO - UART TX" + default 3 + + config OEPL_HARDWARE_UART_RX + depends on OEPL_HARDWARE_PROFILE_CUSTOM + int "GPIO - UART RX" + default 2 + + config OEPL_SUBGIG_SUPPORT + bool "Enable SubGhz Support" + default "n" + + menu "CC1101 Configuration" + depends on OEPL_SUBGIG_SUPPORT + + config GPIO_RANGE_MAX + int + default 33 if IDF_TARGET_ESP32 + default 46 if IDF_TARGET_ESP32S2 + default 48 if IDF_TARGET_ESP32S3 + default 18 if IDF_TARGET_ESP32C2 + default 19 if IDF_TARGET_ESP32C3 + default 30 if IDF_TARGET_ESP32C6 + default 30 if IDF_TARGET_ESP32H2 + + config MISO_GPIO + int "CC1101 MISO GPIO" + range 0 GPIO_RANGE_MAX + default 7 + help + Pin Number to be used as the MISO SPI signal. + + config SCK_GPIO + int "CC1101 SCK GPIO" + range 0 GPIO_RANGE_MAX + default 0 + help + Pin Number to be used as the SCK SPI signal. + + config MOSI_GPIO + int "CC1101 MOSI GPIO" + default 1 + help + Pin Number to be used as the MOSI SPI signal. + + config CSN_GPIO + int "CC1101 CSN GPIO" + range 0 GPIO_RANGE_MAX + default 4 + help + Pin Number to be used as the CSN SPI signal. + + config GDO0_GPIO + int "CC1101 GDO0 GPIO" + range 0 GPIO_RANGE_MAX + default 5 + help + Pin Number to be used as the GDO0 signal. + + config GDO2_GPIO + int "CC1101 GDO2 GPIO" + range 0 GPIO_RANGE_MAX + default 6 + help + Pin Number to be used as the GDO2 signal. + + choice SPI_HOST + prompt "SPI peripheral that controls this bus" + default SPI2_HOST + help + Select SPI peripheral that controls this bus. + config SPI2_HOST + bool "SPI2_HOST" + help + Use SPI2_HOST. This is also called HSPI_HOST. + config SPI3_HOST + depends on IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + bool "SPI3_HOST" + help + USE SPI3_HOST. This is also called VSPI_HOST + endchoice + endmenu +endmenu + + diff --git a/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/SubGigRadio.c b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/SubGigRadio.c new file mode 100644 index 00000000..6ad6c775 --- /dev/null +++ b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/SubGigRadio.c @@ -0,0 +1,562 @@ +#include "sdkconfig.h" +#ifdef CONFIG_OEPL_SUBGIG_SUPPORT +#include +#include +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include +#include +#include "esp_log.h" + +#include "radio.h" +#include "proto.h" +#include "cc1101_radio.h" +#include "SubGigRadio.h" + +void DumpHex(void *AdrIn,int Len); + +#define LOGE(format, ... ) \ + printf("%s#%d: " format,__FUNCTION__,__LINE__,## __VA_ARGS__) + +#if 0 +#define LOG(format, ... ) printf("%s: " format,__FUNCTION__,## __VA_ARGS__) +#define LOG_RAW(format, ... ) printf(format,## __VA_ARGS__) +#define LOG_HEX(x,y) DumpHex(x,y) +#else +#define LOG(format, ... ) +#define LOG_RAW(format, ... ) +#define LOG_HEX(x,y) +#endif + + +// SPI Stuff +#if CONFIG_SPI2_HOST + #define HOST_ID SPI2_HOST +#elif CONFIG_SPI3_HOST + #define HOST_ID SPI3_HOST +#endif + +// Address Config = No address check +// Base Frequency = xxx.xxx +// CRC Enable = false +// Carrier Frequency = 915.000000 +// Channel Number = 0 +// Channel Spacing = 199.951172 +// Data Rate = 1.19948 +// Deviation = 5.157471 +// Device Address = 0 +// Manchester Enable = false +// Modulated = false +// Modulation Format = ASK/OOK +// PA Ramping = false +// Packet Length = 255 +// Packet Length Mode = Reserved +// Preamble Count = 4 +// RX Filter BW = 58.035714 +// Sync Word Qualifier Mode = No preamble/sync +// TX Power = 10 +// Whitening = false +// Rf settings for CC1110 +const RfSetting gCW[] = { + {CC1101_PKTCTRL0,0x22}, // PKTCTRL0: Packet Automation Control + {CC1101_FSCTRL1,0x06}, // FSCTRL1: Frequency Synthesizer Control + {CC1101_MDMCFG4,0xF5}, // MDMCFG4: Modem configuration + {CC1101_MDMCFG3,0x83}, // MDMCFG3: Modem Configuration + {CC1101_MDMCFG2,0xb0}, // MDMCFG2: Modem Configuration + {CC1101_DEVIATN,0x15}, // DEVIATN: Modem Deviation Setting + {CC1101_MCSM0,0x18}, // MCSM0: Main Radio Control State Machine Configuration + {CC1101_FOCCFG,0x17}, // FOCCFG: Frequency Offset Compensation Configuration + {CC1101_FSCAL3,0xE9}, // FSCAL3: Frequency Synthesizer Calibration + {CC1101_FSCAL2,0x2A}, // FSCAL2: Frequency Synthesizer Calibration + {CC1101_FSCAL1,0x00}, // FSCAL1: Frequency Synthesizer Calibration + {CC1101_FSCAL0,0x1F}, // FSCAL0: Frequency Synthesizer Calibration + {CC1101_TEST1,0x31}, // TEST1: Various Test Settings + {CC1101_TEST0,0x09}, // TEST0: Various Test Settings + {0xff,0} // end of table +}; + + +// Set Base Frequency to 865.999634 +const RfSetting g866Mhz[] = { + {CC1101_FREQ2,0x21}, // FREQ2: Frequency Control Word, High Byte + {CC1101_FREQ1,0x4e}, // FREQ1: Frequency Control Word, Middle Byte + {CC1101_FREQ0,0xc4}, // FREQ0: Frequency Control Word, Low Byte + {0xff,0} // end of table +}; + +// Set Base Frequency to 863.999756 +const RfSetting g864Mhz[] = { + {CC1101_FREQ2,0x21}, // FREQ2: Frequency Control Word, High Byte + {CC1101_FREQ1,0x3b}, // FREQ1: Frequency Control Word, Middle Byte + {CC1101_FREQ0,0x13}, // FREQ0: Frequency Control Word, Low Byte + {0xff,0} // end of table +}; + +// Set Base Frequency to 902.999756 +const RfSetting g903Mhz[] = { + {CC1101_FREQ2,0x22}, // FREQ2: Frequency Control Word, High Byte + {CC1101_FREQ1,0xbb}, // FREQ1: Frequency Control Word, Middle Byte + {CC1101_FREQ0,0x13}, // FREQ0: Frequency Control Word, Low Byte + {0xff,0} // end of table +}; + + +// Seet Base Frequency to 915.000000 +const RfSetting g915Mhz[] = { + {CC1101_FREQ2,0x23}, // FREQ2: Frequency Control Word, High Byte + {CC1101_FREQ1,0x31}, // FREQ1: Frequency Control Word, Middle Byte + {CC1101_FREQ0,0x3B}, // FREQ0: Frequency Control Word, Low Byte + {0xff,0} // end of table +}; + +// Address Config = No address check +// Base Frequency = 901.934937 (adjusted to compensate for the crappy crystal on the CC1101 board) +// CRC Enable = true +// Carrier Frequency = 901.934937 +// Channel Number = 0 +// Channel Spacing = 199.951172 +// Data Rate = 38.3835 +// Deviation = 20.629883 +// Device Address = ff +// Manchester Enable = false +// Modulated = true +// Modulation Format = GFSK +// PA Ramping = false +// Packet Length = 61 +// Packet Length Mode = Variable packet length mode. Packet length configured by the first byte after sync word +// Preamble Count = 4 +// RX Filter BW = 101.562500 +// Sync Word Qualifier Mode = 30/32 sync word bits detected +// TX Power = 10 +// Whitening = false +// The following was generated by setting the spec for Register to "{CC1101_@RN@,0x@VH@}," +const RfSetting gIDF_Basic[] = { + {CC1101_SYNC1,0xC7}, + {CC1101_SYNC0,0x0A}, + {CC1101_PKTLEN,0x3D}, + {CC1101_PKTCTRL0,0x05}, + {CC1101_ADDR,0xFF}, + {CC1101_FSCTRL1,0x08}, + {CC1101_FREQ2,0x22}, + {CC1101_FREQ1,0xB1}, + {CC1101_FREQ0,0x3B}, + {CC1101_MDMCFG4,0xCA}, + {CC1101_MDMCFG3,0x83}, + {CC1101_MDMCFG2,0x93}, + {CC1101_DEVIATN,0x35}, +// {CC1101_MCSM0,0x18}, FS_AUTOCAL = 1, PO_TIMEOUT = 2 + {CC1101_MCSM0,0x18}, + {CC1101_FOCCFG,0x16}, + {CC1101_AGCCTRL2,0x43}, + {CC1101_FSCAL3,0xEF}, + {CC1101_FSCAL2,0x2D}, + {CC1101_FSCAL1,0x25}, + {CC1101_FSCAL0,0x1F}, + {CC1101_TEST2,0x81}, + {CC1101_TEST1,0x35}, + {CC1101_TEST0,0x09}, + {0xff,0} // end of table +}; + +// RF configuration from Dimitry's orginal code +// Address Config = No address check +// Base Frequency = 902.999756 +// CRC Autoflush = false +// CRC Enable = true +// Carrier Frequency = 902.999756 +// Channel Number = 0 +// Channel Spacing = 335.632324 +// Data Format = Normal mode +// Data Rate = 249.939 +// Deviation = 165.039063 +// Device Address = 22 +// Manchester Enable = false +// Modulated = true +// Modulation Format = GFSK +// PA Ramping = false +// Packet Length = 255 +// Packet Length Mode = Variable packet length mode. Packet length configured by the first byte after sync word +// Preamble Count = 24 +// RX Filter BW = 650.000000 +// Sync Word Qualifier Mode = 30/32 sync word bits detected +// TX Power = 0 +// Whitening = true +// Rf settings for CC1101 +// The following was generated by setting the spec for Register to "{CC1101_@RN@,0x@VH@}," +const RfSetting gDmitry915[] = { + {CC1101_FREQ2,0x22}, + {CC1101_FREQ1,0xBB}, + {CC1101_FREQ0,0x13}, + {CC1101_MDMCFG4,0x1D}, + {CC1101_MDMCFG3,0x3B}, + {CC1101_MDMCFG2,0x13}, + {CC1101_MDMCFG1,0x73}, + {CC1101_MDMCFG0,0xA7}, + {CC1101_DEVIATN,0x65}, + {CC1101_MCSM0,0x18}, + {CC1101_FOCCFG,0x1E}, + {CC1101_BSCFG,0x1C}, + {CC1101_AGCCTRL2,0xC7}, + {CC1101_AGCCTRL1,0x00}, + {CC1101_AGCCTRL0,0xB0}, + {CC1101_FREND1,0xB6}, + {CC1101_FSCAL3,0xEA}, + {CC1101_FSCAL2,0x2A}, + {CC1101_FSCAL1,0x00}, + {CC1101_FSCAL0,0x1F}, + {CC1101_TEST0,0x09}, + {0xff,0} // end of table +}; + +SubGigData gSubGigData; + +int CheckSubGigState(void); +void SubGig_CC1101_reset(void); +void SubGig_CC1101_SetConfig(const RfSetting *pConfig); + +static void IRAM_ATTR gpio_isr_handler(void *arg) +{ + gSubGigData.RxAvailable = true; +} + +// return SUBGIG_ERR_NONE aka ESP_OK aka 0 if CC1101 is detected and all is good +SubGigErr SubGig_radio_init(uint8_t ch) +{ + esp_err_t Err; + spi_device_interface_config_t devcfg = { + .clock_speed_hz = 5000000, // SPI clock is 5 MHz! + .queue_size = 7, + .mode = 0, // SPI mode 0 + .spics_io_num = -1, // we will use manual CS control + .flags = SPI_DEVICE_NO_DUMMY + }; + gpio_config_t io_conf = { + .intr_type = GPIO_INTR_NEGEDGE, // GPIO interrupt type : falling edge + //bit mask of the pins + .pin_bit_mask = 1ULL<= FIRST_866_CHAN && ch < FIRST_866_CHAN + NUM_866_CHANNELS) { + // Base Frequency = 863.999756 + // total channels 6 (0 -> 5) (CHANNR 0 -> 15) + // Channel 100 / CHANNR 0: 863.999756 + // Channel 101 / CHANNR 3: 865.006 Mhz + // Channel 102 / CHANNR 6: 866.014 Mhz + // Channel 103 / CHANNR 9: 867.020 Mhz + // Channel 104 / CHANNR 12: 868.027 Mhz + // Channel 105 / CHANNR 15: 869.034 Mhz + SubGig_CC1101_SetConfig(g864Mhz); + SetChannr[0].Value = (ch - FIRST_866_CHAN) * 3; + } + else { + // Base Frequency = 902.999756 + // Dmitry's orginal code used 25 channels in 915 Mhz + // We don't want to have to scan that many so for OEPL we'll just use 6 + // to match 866. + // Channel 200 / CHANNR 0: 903.000 Mhz + // Channel 201 / CHANNR 12: 907.027 Mhz + // Channel 202 / CHANNR 24: 911.054 Mhz + // Channel 203 / CHANNR 24: 915.083 Mhz + // Channel 204 / CHANNR 48: 919.110 Mhz + // Channel 205 / CHANNR 60: 923.138 Mhz + SubGig_CC1101_SetConfig(g903Mhz); + + if(ch >= FIRST_915_CHAN && ch < FIRST_915_CHAN + NUM_915_CHANNELS) { + SetChannr[0].Value = (ch - FIRST_915_CHAN) * 12; + } + else { + Ret = SUBGIG_INVALID_CHANNEL; + SetChannr[0].Value = 0; // default to the first channel on 915 + } + } + SubGig_CC1101_SetConfig(SetChannr); + CC1101_setRxState(); + } while(false); + + return Ret; +} + +SubGigErr SubGig_radioTx(uint8_t *packet) +{ + SubGigErr Ret = SUBGIG_ERR_NONE; + + do { + if(gSubGigData.FreqTest) { + break; + } + if((Ret = CheckSubGigState()) != SUBGIG_ERR_NONE) { + break; + } + + if(packet[0] < 3 || packet[0] > RADIO_MAX_PACKET_LEN + RAW_PKT_PADDING) { + Ret = SUBGIG_TX_BAD_LEN; + break; + } + + // All packets seem to be padded by RAW_PKT_PADDING (2 bytes) + // Remove the padding before sending so the length is correct when received + packet[0] -= RAW_PKT_PADDING; + LOG("Sending %d byte subgig frame:\n",packet[0]); + LOG_HEX(&packet[1],packet[0]); + if(CC1101_Tx(packet)) { + Ret = SUBGIG_TX_FAILED; + } + // Clear RxAvailable, in TX GDO0 deasserts on TX FIFO underflows + gSubGigData.RxAvailable = false; + // restore original len just in case anyone cares + packet[0] += RAW_PKT_PADDING; + } while(false); + + return Ret; +} + +// returns packet size in bytes data in data +int8_t SubGig_commsRxUnencrypted(uint8_t *data) +{ + int RxBytes; + int8_t Ret = 0; + + do { + if(CheckSubGigState() != SUBGIG_ERR_NONE) { + break; + } + + if(gSubGigData.FreqTest) { + break; + } + CC1101_logState(); + + if(!gSubGigData.RxAvailable && gpio_get_level(CONFIG_GDO0_GPIO) == 1) { + // Did we miss an interrupt? + if(gpio_get_level(CONFIG_GDO0_GPIO) == 1) { + // Yup! + LOGE("SubGhz lost interrupt\n"); + gSubGigData.RxAvailable = true; + } + } + + if(gSubGigData.RxAvailable){ + gSubGigData.RxAvailable = false; + RxBytes = CC1101_Rx(data,128,NULL,NULL); + + if(RxBytes >= 2) { + // NB: RxBytes includes the CRC, deduct it + Ret = (uint8_t) RxBytes - 2; + LOG("Received %d byte subgig frame:\n",Ret); + LOG_HEX(data,Ret); + } + } + } while(false); + + return Ret; +} + +int CheckSubGigState() +{ + int Err = SUBGIG_ERR_NONE; + + if(!gSubGigData.Present) { + Err = SUBGIG_CC1101_NOT_FOUND; + } + else if(!gSubGigData.Initialized) { + Err = SUBGIG_NOT_INITIALIZED; + } + else if(!gSubGigData.Enabled) { + Err = SUBGIG_NOT_ENABLED; + } + + if(Err != SUBGIG_ERR_NONE) { + LOG("CheckSubGigState: returing %d\n",Err); + } + + return Err; +} + +SubGigErr SubGig_FreqTest(bool b866Mhz,bool bStart) +{ + SubGigErr Err = SUBGIG_ERR_NONE; +#if 0 + uint8_t TxData = 0; // len = 0 + + do { + if((Err = CheckSubGigState()) != SUBGIG_ERR_NONE) { + break; + } + if(bStart) { + LOG_RAW("Starting %sMhz Freq test\n",b866Mhz ? "866" : "915"); + SubGig_CC1101_reset(); + SubGig_CC1101_SetConfig(gCW); + SubGig_CC1101_SetConfig(b866Mhz ? g866Mhz : g915Mhz); + CC1101_cmdStrobe(CC1101_SIDLE); + CC1101_cmdStrobe(CC1101_SFTX); // flush Tx Fifo + CC1101_cmdStrobe(CC1101_STX); + gRfState = RFSTATE_TX; + gSubGigData.FreqTest = true; + } + else { + LOG_RAW("Ending Freq test\n"); + gSubGigData.FreqTest = false; + SubGig_CC1101_reset(); + SubGig_CC1101_SetConfig(gSubGigData.pConfig); + } + } while(false); +#endif + return Err; +} + +void SubGig_CC1101_reset() +{ + gSubGigData.Initialized = false; + gSubGigData.FixedRegsSet = false; + CC1101_reset(); +} + +void SubGig_CC1101_SetConfig(const RfSetting *pConfig) +{ + CC1101_SetConfig(pConfig); + gSubGigData.Initialized = true; +} + +void DumpHex(void *AdrIn,int Len) +{ + unsigned char *Adr = (unsigned char *) AdrIn; + int i = 0; + int j; + + while(i < Len) { + for(j = 0; j < 16; j++) { + if((i + j) == Len) { + break; + } + LOG_RAW("%02x ",Adr[i+j]); + } + + LOG_RAW(" "); + for(j = 0; j < 16; j++) { + if((i + j) == Len) { + break; + } + if(isprint(Adr[i+j])) { + LOG_RAW("%c",Adr[i+j]); + } + else { + LOG_RAW("."); + } + } + i += 16; + LOG_RAW("\n"); + } +} +#endif // CONFIG_OEPL_SUBGIG_SUPPORT + diff --git a/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/SubGigRadio.h b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/SubGigRadio.h new file mode 100644 index 00000000..e41cf445 --- /dev/null +++ b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/SubGigRadio.h @@ -0,0 +1,40 @@ +#ifndef _SUBGIG_RADIO_H_ +#define _SUBGIG_RADIO_H_ + +//sub-GHz 866 Mhz channels start at 100 +#define FIRST_866_CHAN (100) +#define NUM_866_CHANNELS (6) + +//sub-GHz 915 Mhz channels start at 200 +#define FIRST_915_CHAN (200) +#define NUM_915_CHANNELS (6) + +typedef enum { + SUBGIG_ERR_NONE, + SUBGIG_CC1101_NOT_FOUND, + SUBGIG_NOT_INITIALIZED, + SUBGIG_NOT_ENABLED, + SUBGIG_TX_FAILED, + SUBGIG_TX_BAD_LEN, + SUBGIG_INVALID_CHANNEL, +} SubGigErr; + +typedef struct { + uint8_t Present:1; + uint8_t Enabled:1; + uint8_t FreqTest:1; + uint8_t RxAvailable:1; + uint8_t Initialized:1; + uint8_t FixedRegsSet:1; +} SubGigData; + +extern SubGigData gSubGigData; + +SubGigErr SubGig_radio_init(uint8_t ch); +SubGigErr SubGig_radioTx(uint8_t *packet); +SubGigErr SubGig_radioSetChannel(uint8_t ch); +int8_t SubGig_commsRxUnencrypted(uint8_t *data); +SubGigErr SubGig_FreqTest(bool b866Mhz,bool bStart); + +#endif // _SUBGIG_RADIO_H_ + diff --git a/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/cc1101_radio.c b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/cc1101_radio.c new file mode 100644 index 00000000..13ecaffe --- /dev/null +++ b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/cc1101_radio.c @@ -0,0 +1,790 @@ +// Large portions of this code was copied from: +// https://github.com/nopnop2002/esp-idf-cc1101 with the following copyright + +/* + * Copyright (c) 2011 panStamp + * Copyright (c) 2016 Tyler Sommer + * + * This file is part of the CC1101 project. + * + * CC1101 is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * any later version. + * + * CC1101 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with CC1101; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 + * USA + * + * Author: Daniel Berenguer + * Creation date: 03/03/2011 + */ + +#include "sdkconfig.h" +#ifdef CONFIG_OEPL_SUBGIG_SUPPORT + +#include +#include +#include +#include +#include "proto.h" +#include "cc1101_radio.h" +#include "radio.h" + +#define ENABLE_LOGGING 0 + +// LOGA - generic logging, always enabled +#define LOGA(format, ... ) printf(format,## __VA_ARGS__) +// LOGE - error logging, always enabled +#define LOGE(format, ... ) printf("%s: " format,__FUNCTION__,## __VA_ARGS__) + +#if ENABLE_LOGGING +#define LOG(format, ... ) printf("%s: " format,__FUNCTION__,## __VA_ARGS__) +#define LOG_RAW(format, ... ) printf(format,## __VA_ARGS__) +#else +#define LOG(format, ... ) +#define LOG_RAW(format, ... ) +#endif + +#define ENABLE_VERBOSE_LOGGING 0 + +#if ENABLE_VERBOSE_LOGGING +#define LOGV(format, ... ) printf("%s: " format,__FUNCTION__,## __VA_ARGS__) +#define LOGV_RAW(format, ... ) printf(format,## __VA_ARGS__) +#else +#define LOGV(format, ... ) +#define LOGB_RAW(format, ... ) +#endif + +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include +#include +#include "esp_log.h" + +#include "sdkconfig.h" + +/** + * RF STATES + */ +enum RFSTATE { + RFSTATE_IDLE = 0, + RFSTATE_RX, + RFSTATE_TX +}; + +/** + * Type of transfers + */ +#define WRITE_BURST 0x40 +#define READ_SINGLE 0x80 +#define READ_BURST 0xC0 + +/** + * Type of register + */ +#define CC1101_CONFIG_REGISTER READ_SINGLE +#define CC1101_STATUS_REGISTER READ_BURST + +/** + * PATABLE & FIFO's + */ +#define CC1101_PATABLE 0x3E // PATABLE address +#define CC1101_TXFIFO 0x3F // TX FIFO address +#define CC1101_RXFIFO 0x3F // RX FIFO address + +/** + * Command strobes + */ +#define CC1101_SRES 0x30 // Reset CC1101 chip +#define CC1101_SFSTXON 0x31 // Enable and calibrate frequency synthesizer (if MCSM0.FS_AUTOCAL=1). If in RX (with CCA): +// Go to a wait state where only the synthesizer is running (for quick RX / TX turnaround). +#define CC1101_SXOFF 0x32 // Turn off crystal oscillator +#define CC1101_SCAL 0x33 // Calibrate frequency synthesizer and turn it off. SCAL can be strobed from IDLE mode without +// setting manual calibration mode (MCSM0.FS_AUTOCAL=0) +#define CC1101_SRX 0x34 // Enable RX. Perform calibration first if coming from IDLE and MCSM0.FS_AUTOCAL=1 +#define CC1101_STX 0x35 // In IDLE state: Enable TX. Perform calibration first if MCSM0.FS_AUTOCAL=1. +// If in RX state and CCA is enabled: Only go to TX if channel is clear +#define CC1101_SIDLE 0x36 // Exit RX / TX, turn off frequency synthesizer and exit Wake-On-Radio mode if applicable +#define CC1101_SWOR 0x38 // Start automatic RX polling sequence (Wake-on-Radio) as described in Section 19.5 if +// WORCTRL.RC_PD=0 +#define CC1101_SPWD 0x39 // Enter power down mode when CSn goes high +#define CC1101_SFRX 0x3A // Flush the RX FIFO buffer. Only issue SFRX in IDLE or RXFIFO_OVERFLOW states +#define CC1101_SFTX 0x3B // Flush the TX FIFO buffer. Only issue SFTX in IDLE or TXFIFO_UNDERFLOW states +#define CC1101_SWORRST 0x3C // Reset real time clock to Event1 value +#define CC1101_SNOP 0x3D // No operation. May be used to get access to the chip status byte + +#define CC1101_STATE_SLEEP 0x00 +#define CC1101_STATE_IDLE 0x01 +#define CC1101_STATE_XOFF 0x02 +#define CC1101_STATE_VCOON_MC 0x03 +#define CC1101_STATE_REGON_MC 0x04 +#define CC1101_STATE_MANCAL 0x05 +#define CC1101_STATE_VCOON 0x06 +#define CC1101_STATE_REGON 0x07 +#define CC1101_STATE_STARTCAL 0x08 +#define CC1101_STATE_BWBOOST 0x09 +#define CC1101_STATE_FS_LOCK 0x0A +#define CC1101_STATE_IFADCON 0x0B +#define CC1101_STATE_ENDCAL 0x0C +#define CC1101_STATE_RX 0x0D +#define CC1101_STATE_RX_END 0x0E +#define CC1101_STATE_RX_RST 0x0F +#define CC1101_STATE_TXRX_SWITCH 0x10 +#define CC1101_STATE_RXFIFO_OVERFLOW 0x11 +#define CC1101_STATE_FSTXON 0x12 +#define CC1101_STATE_TX 0x13 +#define CC1101_STATE_TX_END 0x14 +#define CC1101_STATE_RXTX_SWITCH 0x15 +#define CC1101_STATE_TXFIFO_UNDERFLOW 0x16 + +// Masks for first byte read from RXFIFO +#define CC1101_NUM_RXBYTES_MASK 0x7f +#define CC1101_RXFIFO_OVERFLOW_MASK 0x80 + +// Masks for last byte read from RXFIFO +#define CC1101_LQI_MASK 0x7f +#define CC1101_CRC_OK_MASK 0x80 + +// IOCFG2 GDO2: high when TX FIFO at or above the TX FIFO threshold +#define CC1101_DEFVAL_IOCFG2 0x02 + +// IOCFG1 GDO1: High impedance (3-state) +#define CC1101_DEFVAL_IOCFG1 0x2E + +// GDO0 goes high when sync word has been sent / received, and +// goes low at the end of the packet. +// In TX mode the pin will go low if the TX FIFO underflows. +#define CC1101_DEFVAL_IOCFG0 0x06 + +// Threshold = 32 bytes (1/2 of FIFO len) +#define CC1101_DEFVAL_FIFOTHR 0x07 +#define CC1101_DEFVAL_RCCTRL1 0x41 +#define CC1101_DEFVAL_RCCTRL0 0x00 +#define CC1101_DEFVAL_AGCTEST 0x3F +#define CC1101_DEFVAL_MCSM1 0x20 +#define CC1101_DEFVAL_WORCTRL 0xFB +#define CC1101_DEFVAL_FSCTRL0 0 + +#define CC1101_DEFVAL_PATABLE 0xc0 // full power + +RfSetting gFixedConfig[] = { + {CC1101_IOCFG2,CC1101_DEFVAL_IOCFG2}, + {CC1101_IOCFG1,CC1101_DEFVAL_IOCFG1}, + {CC1101_IOCFG0,CC1101_DEFVAL_IOCFG0}, + {CC1101_FIFOTHR,CC1101_DEFVAL_FIFOTHR}, + {CC1101_FSCTRL0,CC1101_DEFVAL_FSCTRL0}, + {CC1101_RCCTRL1,CC1101_DEFVAL_RCCTRL1}, + {CC1101_RCCTRL0,CC1101_DEFVAL_RCCTRL0}, + {CC1101_MCSM1,CC1101_DEFVAL_MCSM1}, + {CC1101_WORCTRL,CC1101_DEFVAL_WORCTRL}, + {0xff,0}, +}; + +void CC1101_readBurstReg(uint8_t *buffer,uint8_t regAddr,uint8_t len); +void CC1101_cmdStrobe(uint8_t cmd); +void CC1101_wakeUp(void); +uint8_t CC1101_readReg(uint8_t regAddr, uint8_t regType); +void CC1101_writeReg(uint8_t regAddr, uint8_t value); +void CC1101_setTxState(void); + +void setIdleState(void); + +spi_device_handle_t gSpiHndl; + +#define readConfigReg(regAddr) CC1101_readReg(regAddr, CC1101_CONFIG_REGISTER) +#define readStatusReg(regAddr) CC1101_readReg(regAddr, CC1101_STATUS_REGISTER) +#define flushRxFifo() CC1101_cmdStrobe(CC1101_SFRX) +#define flushTxFifo() CC1101_cmdStrobe(CC1101_SFTX) + +const char *RegNamesCC1101[] = { + "IOCFG2", // 0x00 GDO2 output pin configuration + "IOCFG1", // 0x01 GDO1 output pin configuration + "IOCFG0", // 0x02 GDO0 output pin configuration + "FIFOTHR", // 0x03 RX FIFO and TX FIFO thresholds + "SYNC1", // 0x04 Sync word, high INT8U + "SYNC0", // 0x05 Sync word, low INT8U + "PKTLEN", // 0x06 Packet length + "PKTCTRL1", // 0x07 Packet automation control + "PKTCTRL0", // 0x08 Packet automation control + "ADDR", // 0x09 Device address + "CHANNR", // 0x0A Channel number + "FSCTRL1", // 0x0B Frequency synthesizer control + "FSCTRL0", // 0x0C Frequency synthesizer control + "FREQ2", // 0x0D Frequency control word, high INT8U + "FREQ1", // 0x0E Frequency control word, middle INT8U + "FREQ0", // 0x0F Frequency control word, low INT8U + "MDMCFG4", // 0x10 Modem configuration + "MDMCFG3", // 0x11 Modem configuration + "MDMCFG2", // 0x12 Modem configuration + "MDMCFG1", // 0x13 Modem configuration + "MDMCFG0", // 0x14 Modem configuration + "DEVIATN", // 0x15 Modem deviation setting + "MCSM2", // 0x16 Main Radio Control State Machine configuration + "MCSM1", // 0x17 Main Radio Control State Machine configuration + "MCSM0", // 0x18 Main Radio Control State Machine configuration + "FOCCFG", // 0x19 Frequency Offset Compensation configuration + "BSCFG", // 0x1A Bit Synchronization configuration + "AGCCTRL2", // 0x1B AGC control + "AGCCTRL1", // 0x1C AGC control + "AGCCTRL0", // 0x1D AGC control + "WOREVT1", // 0x1E High INT8U Event 0 timeout + "WOREVT0", // 0x1F Low INT8U Event 0 timeout + "WORCTRL", // 0x20 Wake On Radio control + "FREND1", // 0x21 Front end RX configuration + "FREND0", // 0x22 Front end TX configuration + "FSCAL3", // 0x23 Frequency synthesizer calibration + "FSCAL2", // 0x24 Frequency synthesizer calibration + "FSCAL1", // 0x25 Frequency synthesizer calibration + "FSCAL0", // 0x26 Frequency synthesizer calibration + "RCCTRL1", // 0x27 RC oscillator configuration + "RCCTRL0", // 0x28 RC oscillator configuration + "FSTEST", // 0x29 Frequency synthesizer calibration control + "PTEST", // 0x2A Production test + "AGCTEST", // 0x2B AGC test + "TEST2", // 0x2C Various test settings + "TEST1", // 0x2D Various test settings + "TEST0", // 0x2E Various test settings + "0x2f", // 0x2f +//CC1101 Strobe commands + "SRES", // 0x30 Reset chip. + "SFSTXON", // 0x31 Enable and calibrate frequency synthesizer (if MCSM0.FS_AUTOCAL=1). + "SXOFF", // 0x32 Turn off crystal oscillator. + "SCAL", // 0x33 Calibrate frequency synthesizer and turn it off + "SRX", // 0x34 Enable RX. Perform calibration first if coming from IDLE and + "STX", // 0x35 In IDLE state: Enable TX. Perform calibration first if + "SIDLE", // 0x36 Exit RX / TX, turn off frequency synthesizer and exit + "SAFC", // 0x37 Perform AFC adjustment of the frequency synthesizer + "SWOR", // 0x38 Start automatic RX polling sequence (Wake-on-Radio) + "SPWD", // 0x39 Enter power down mode when CSn goes high. + "SFRX", // 0x3A Flush the RX FIFO buffer. + "SFTX", // 0x3B Flush the TX FIFO buffer. + "SWORRST", // 0x3C Reset real time clock. + "SNOP", // 0x3D No operation. May be used to pad strobe commands to two + "PATABLE" // 0x3E +}; + + +// SPI Stuff +#if CONFIG_SPI2_HOST + #define HOST_ID SPI2_HOST +#elif CONFIG_SPI3_HOST + #define HOST_ID SPI3_HOST +#endif + +/* + * RF state + */ +static uint8_t gRfState; + +#define cc1101_Select() gpio_set_level(CONFIG_CSN_GPIO, LOW) +#define cc1101_Deselect() gpio_set_level(CONFIG_CSN_GPIO, HIGH) +#define wait_Miso() while(gpio_get_level(CONFIG_MISO_GPIO)>0) +#define getGDO0state() gpio_get_level(CONFIG_GDO0_GPIO) +#define wait_GDO0_high() while(!getGDO0state()) +#define wait_GDO0_low() while(getGDO0state()) +#define getGDO2state() gpio_get_level(CONFIG_GDO2_GPIO) +#define wait_GDO2_low() while(getGDO2state()) + +/** + * Arduino Macros + */ +#define bitRead(value, bit) (((value) >> (bit)) & 0x01) +#define bitSet(value, bit) ((value) |= (1UL << (bit))) +#define bitClear(value, bit) ((value) &= ~(1UL << (bit))) +#define bitWrite(value, bit, bitvalue) ((bitvalue) ? bitSet(value, bit) : bitClear(value, bit)) +#define delayMicroseconds(us) esp_rom_delay_us(us) +#define LOW 0 +#define HIGH 1 + +int32_t gFreqErrSum; +uint8_t gFreqErrSumCount; +int8_t gFreqCorrection; + +bool spi_write_byte(uint8_t* Dataout,size_t DataLength) +{ + spi_transaction_t SPITransaction; + + if(DataLength > 0) { + memset(&SPITransaction,0,sizeof(spi_transaction_t)); + SPITransaction.length = DataLength * 8; + SPITransaction.tx_buffer = Dataout; + SPITransaction.rx_buffer = NULL; + spi_device_transmit(gSpiHndl,&SPITransaction); + } + + return true; +} + +bool spi_read_byte(uint8_t* Datain,uint8_t* Dataout,size_t DataLength) +{ + spi_transaction_t SPITransaction; + + if(DataLength > 0) { + memset(&SPITransaction,0,sizeof(spi_transaction_t)); + SPITransaction.length = DataLength * 8; + SPITransaction.tx_buffer = Dataout; + SPITransaction.rx_buffer = Datain; + spi_device_transmit(gSpiHndl,&SPITransaction); + } + + return true; +} + +uint8_t spi_transfer(uint8_t address) +{ + uint8_t datain[1]; + uint8_t dataout[1]; + dataout[0] = address; + //spi_write_byte(dev, dataout, 1 ); + //spi_read_byte(datain, dataout, 1 ); + + spi_transaction_t SPITransaction; + memset(&SPITransaction,0,sizeof(spi_transaction_t)); + SPITransaction.length = 8; + SPITransaction.tx_buffer = dataout; + SPITransaction.rx_buffer = datain; + spi_device_transmit(gSpiHndl,&SPITransaction); + + return datain[0]; +} + +/** + * CC1101_wakeUp + * + * Wake up CC1101 from Power Down state + */ +void CC1101_wakeUp(void) +{ + cc1101_Select(); + wait_Miso(); + cc1101_Deselect(); +} + +/** + * CC1101_writeReg + * + * Write single register into the CC1101 IC via SPI + * + * @param regAddr Register address + * @param value Value to be writen + */ +void CC1101_writeReg(uint8_t regAddr, uint8_t value) +{ + if(regAddr < 0x3f) { + LOGV("0x%x -> %s(0x%x)\n",value,RegNamesCC1101[regAddr],regAddr); + } + else { + LOGV("0x%x -> 0x%x\n",value,regAddr); + } + cc1101_Select(); // Select CC1101 + wait_Miso(); // Wait until MISO goes low + spi_transfer(regAddr); // Send register address + spi_transfer(value); // Send value + cc1101_Deselect(); // Deselect CC1101 +} + + +/** + * CC1101_cmdStrobe + * + * Send command strobe to the CC1101 IC via SPI + * + * @param cmd Command strobe + */ +void CC1101_cmdStrobe(uint8_t cmd) +{ + cc1101_Select(); + wait_Miso(); + spi_transfer(cmd); + cc1101_Deselect(); +} + +/** + * CC1101_readReg + * + * Read CC1101 register via SPI + * + * @param regAddr Register address + * @param regType Type of register: CONFIG_REGISTER or STATUS_REGISTER + * + * Return: + * Data uint8_t returned by the CC1101 IC + */ +uint8_t CC1101_readReg(uint8_t regAddr,uint8_t regType) +{ + uint8_t addr, val; + + addr = regAddr | regType; + cc1101_Select(); + wait_Miso(); + spi_transfer(addr); + val = spi_transfer(0x00); // Read result + cc1101_Deselect(); + + return val; +} + +/** + * CC1101_readBurstReg + * + * Read burst data from CC1101 via SPI + * + * @param buffer Buffer where to copy the result to + * @param regAddr Register address + * @param len Data length + */ +void CC1101_readBurstReg(uint8_t *buffer,uint8_t regAddr,uint8_t len) +{ + uint8_t addr, i; + + addr = regAddr | READ_BURST; + cc1101_Select(); + wait_Miso(); + spi_transfer(addr); // Send register address + for(i = 0; i < len; i++) { + buffer[i] = spi_transfer(0x00); // Read result uint8_t by uint8_t + } + cc1101_Deselect(); +} + +/** + * reset + * + * Reset CC1101 + */ +void CC1101_reset(void) +{ +// See sectin 19.1.2 of the CC1101 spec sheet for reasons for the following + cc1101_Deselect(); + delayMicroseconds(5); + cc1101_Select(); + delayMicroseconds(10); + cc1101_Deselect(); + delayMicroseconds(41); + cc1101_Select(); + +// Wait until MISO goes low indicating XOSC stable + wait_Miso(); + spi_transfer(CC1101_SRES); // Send reset command strobe + wait_Miso(); + cc1101_Deselect(); +} + + +/** + * CC1101_setRxState + * + * Enter Rx state + */ +void CC1101_setRxState(void) +{ + CC1101_cmdStrobe(CC1101_SRX); + gRfState = RFSTATE_RX; +} + +/** + * CC1101_setTxState + * + * Enter Tx state + */ +void CC1101_setTxState(void) +{ + CC1101_cmdStrobe(CC1101_STX); + gRfState = RFSTATE_TX; +} + + +void CC1101_DumpRegs() +{ +#if ENABLE_LOGGING + uint8_t regAddr; + uint8_t value; + + LOG("\n"); + for(regAddr = 0; regAddr < 0x2f; regAddr++) { + value = CC1101_readReg(regAddr,READ_SINGLE); + LOG("%02x %s: 0x%02X\n",regAddr,RegNamesCC1101[regAddr],value); + } + +#if 0 + for(regAddr = 0; regAddr < 0x2f; regAddr++) { + value = CC1101_readReg(regAddr,READ_SINGLE); + LOG("%s0x%02X\n",RegNamesCC1101[regAddr],value); + } +#endif +#endif +} + + +bool CC1101_Tx(uint8_t *TxData) +{ + bool Ret = false; + int ErrLine = 0; + spi_transaction_t SPITransaction; + uint8_t BytesSent = 0; + uint8_t Bytes2Send; + uint8_t len; + uint8_t CanSend; + esp_err_t Err; + + do { + // The first byte in the buffer is the number of data bytes to send, + // we also need to send the first byte + len = 1 + *TxData; + memset(&SPITransaction,0,sizeof(spi_transaction_t)); + SPITransaction.tx_buffer = TxData; + + setIdleState(); + flushTxFifo(); + + while(BytesSent < len) { + Bytes2Send = len - BytesSent; + if(BytesSent == 0) { + // First chunk, the FIFO is empty and can take 64 bytes + if(Bytes2Send > 64) { + Bytes2Send = 64; + } + } + else { + // Not the first chunk, we can only send FIFO_THRESHOLD bytes + // and only when GDO2 says we can + if(getGDO2state()) { + wait_GDO2_low(); + } + CanSend = readStatusReg(CC1101_TXBYTES); + if(CanSend & 0x80) { + LOGE("TX FIFO underflow, BytesSent %d\n",BytesSent); + ErrLine = __LINE__; + break; + } + CanSend = 64 - CanSend; + if(CanSend == 0) { + LOGE("CanSend == 0, GDO2 problem\n"); + ErrLine = __LINE__; + break; + } + + if(Bytes2Send > CanSend) { + Bytes2Send = CanSend; + } + } + SPITransaction.length = Bytes2Send * 8; + SPITransaction.rxlength = 0; + cc1101_Select(); + wait_Miso(); + spi_transfer(CC1101_TXFIFO | WRITE_BURST); + if((Err = spi_device_transmit(gSpiHndl,&SPITransaction)) != ESP_OK) { + ErrLine = __LINE__; + LOGE("spi_device_transmit failed %d\n",Err); + break; + } + cc1101_Deselect(); +// LOG("Sending %d bytes\n",Bytes2Send); + if(BytesSent == 0) { + // some or all of the tx data has been written to the FIFO, + // start transmitting +// LOG("Start tx\n"); + CC1101_setTxState(); + // Wait for the sync word to be transmitted + wait_GDO0_high(); + } + SPITransaction.tx_buffer += Bytes2Send; + BytesSent += Bytes2Send; + } + + // Wait until the end of the TxData transmission + wait_GDO0_low(); + Ret = true; + } while(false); + + setIdleState(); + CC1101_setRxState(); + + if(ErrLine != 0) { + LOGE("%s#%d: failure\n",__FUNCTION__,ErrLine); + } + + return Ret; +} + +// Called when GDO0 goes low, i.e. end of packet. +// Everything has been received. +// NB: this means the entire packet must fit in the FIFO so maximum +// message length is 64 bytes. +int CC1101_Rx(uint8_t *RxBuf,size_t RxBufLen,uint8_t *pRssi,uint8_t *pLqi) +{ + uint8_t rxBytes = readStatusReg(CC1101_RXBYTES); + uint8_t Rssi; + uint8_t Lqi; + int Ret; + int8_t FreqErr; + int8_t FreqCorrection; + +// Any data waiting to be read and no overflow? + do { + if(rxBytes & CC1101_RXFIFO_OVERFLOW_MASK) { + LOGE("RxFifo overflow\n"); + Ret = -2; + break; + } + + if(rxBytes < 2) { + // should have at least 2 bytes, packet len and one byte of data + LOGE("Internal error, rxBytes = %d\n",rxBytes); + Ret = -2; + break; + } + + // Get packet length + Ret = readConfigReg(CC1101_RXFIFO); + if(Ret > RxBufLen) { + // Toss the data + LOGE("RxBuf too small %d < %d\n",RxBufLen,Ret); + Ret = -1; + break; + } + // Read the data + CC1101_readBurstReg(RxBuf,CC1101_RXFIFO,Ret); + // Read RSSI + Rssi = readConfigReg(CC1101_RXFIFO); + // Read LQI and CRC_OK + Lqi = readConfigReg(CC1101_RXFIFO); + if(!(Lqi & CC1101_CRC_OK_MASK)) { + // Crc error, ignore the packet + LOG("Ignoring %d byte packet, CRC error\n",Ret); + Ret = 0; + break; + } + // CRC is valid + if(pRssi != NULL) { + *pRssi = Rssi; + } + if(pLqi != NULL) { + *pLqi = Lqi & CC1101_LQI_MASK; + } + FreqErr = (int8_t) CC1101_readReg(CC1101_FREQEST,CC1101_STATUS_REGISTER); + if(FreqErr != 0 && gFreqErrSumCount < 255) { + gFreqErrSum += FreqErr + gFreqCorrection; + gFreqErrSumCount++; + FreqCorrection = (int8_t) (gFreqErrSum / gFreqErrSumCount); + if(gFreqCorrection != FreqCorrection) { + LOGA("FreqCorrection %d -> %d\n",gFreqCorrection,FreqCorrection); + gFreqCorrection = FreqCorrection; + CC1101_writeReg(CC1101_FSCTRL0,gFreqCorrection); + } + if(gFreqErrSumCount == 255) { + LOGA("Final FreqCorrection %d\n",gFreqCorrection); + } + } + } while(false); + + setIdleState(); + flushRxFifo(); + CC1101_setRxState(); + + return Ret; +} + +bool CC1101_Present() +{ + bool Ret = false; + uint8_t PartNum = CC1101_readReg(CC1101_PARTNUM, CC1101_STATUS_REGISTER); + uint8_t ChipVersion = CC1101_readReg(CC1101_VERSION, CC1101_STATUS_REGISTER); + + if(PartNum == 0 && (ChipVersion == 20 || ChipVersion == 4)) { + LOGA("CC1101 detected\n"); + Ret = true; + } + else { + if(PartNum != 0) { + LOGA("Invalid PartNum 0x%x\n",PartNum); + } + else { + LOGA("Invalid or unsupported ChipVersion 0x%x\n",ChipVersion); + } + } + + return Ret; +} + +void CC1101_SetConfig(const RfSetting *pConfig) +{ + int i; + uint8_t RegWasSet[CC1101_TEST0 + 1]; + uint8_t Reg; + + memset(RegWasSet,0,sizeof(RegWasSet)); + setIdleState(); + + if(pConfig == NULL) { +// Just set the fixed registers + LOG("Setting fixed registers\n"); + for(i = 0; (Reg = gFixedConfig[i].Reg) != 0xff; i++) { + CC1101_writeReg(Reg,gFixedConfig[i].Value); + } + // Set TX power + CC1101_writeReg(CC1101_PATABLE,CC1101_DEFVAL_PATABLE); + } + else { + for(i = 0; (Reg = gFixedConfig[i].Reg) != 0xff; i++) { + RegWasSet[Reg] = 1; + } + + while((Reg = pConfig->Reg) != 0xff) { + if(RegWasSet[Reg] == 1) { + LOG("%s value ignored\n",RegNamesCC1101[Reg]); + } + else { + if(RegWasSet[Reg] == 2) { + LOG("%s value set before\n",RegNamesCC1101[Reg]); + } + CC1101_writeReg(pConfig->Reg,pConfig->Value); + RegWasSet[Reg] = 2; + } + pConfig++; + } +#if 0 + for(Reg = 0; Reg <= CC1101_TEST0; Reg++) { + if(RegWasSet[Reg] == 0) { + LOG("%s value not set\n",RegNamesCC1101[Reg]); + } + } +#endif + } +} + +void setIdleState() +{ + uint8_t MarcState; + CC1101_cmdStrobe(CC1101_SIDLE); +// Wait for it + do { + MarcState = readStatusReg(CC1101_MARCSTATE); + } while(MarcState != CC1101_STATE_IDLE); +} + + +void CC1101_logState() +{ + static uint8_t LastMarcState = 0xff; + uint8_t MarcState; + + MarcState = readStatusReg(CC1101_MARCSTATE); + if(LastMarcState != MarcState) { + LOG("MarcState 0x%x -> 0x%x\n",LastMarcState,MarcState); + LastMarcState = MarcState; + } +} + +#endif // CONFIG_OEPL_SUBGIG_SUPPORT + diff --git a/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/cc1101_radio.h b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/cc1101_radio.h new file mode 100644 index 00000000..a8335be6 --- /dev/null +++ b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/cc1101_radio.h @@ -0,0 +1,118 @@ +// Large portions of this code was copied from: +// https://github.com/nopnop2002/esp-idf-cc1101 with the following copyright + +/* + * Copyright (c) 2011 panStamp + * Copyright (c) 2016 Tyler Sommer + * + * This file is part of the CC1101 project. + * + * CC1101 is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * any later version. + * + * CC1101 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with CC1101; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 + * USA + * + * Author: Daniel Berenguer + * Creation date: 03/03/2011 + */ + +#ifndef __CC1101_RADIO_H_ +#define __CC1101_RADIO_H_ + +/** + * CC1101 configuration registers + */ +#define CC1101_IOCFG2 0x00 // GDO2 Output Pin Configuration +#define CC1101_IOCFG1 0x01 // GDO1 Output Pin Configuration +#define CC1101_IOCFG0 0x02 // GDO0 Output Pin Configuration +#define CC1101_FIFOTHR 0x03 // RX FIFO and TX FIFO Thresholds +#define CC1101_SYNC1 0x04 // Sync Word, High Byte +#define CC1101_SYNC0 0x05 // Sync Word, Low Byte +#define CC1101_PKTLEN 0x06 // Packet Length +#define CC1101_PKTCTRL1 0x07 // Packet Automation Control +#define CC1101_PKTCTRL0 0x08 // Packet Automation Control +#define CC1101_ADDR 0x09 // Device Address +#define CC1101_CHANNR 0x0A // Channel Number +#define CC1101_FSCTRL1 0x0B // Frequency Synthesizer Control +#define CC1101_FSCTRL0 0x0C // Frequency Synthesizer Control +#define CC1101_FREQ2 0x0D // Frequency Control Word, High Byte +#define CC1101_FREQ1 0x0E // Frequency Control Word, Middle Byte +#define CC1101_FREQ0 0x0F // Frequency Control Word, Low Byte +#define CC1101_MDMCFG4 0x10 // Modem Configuration +#define CC1101_MDMCFG3 0x11 // Modem Configuration +#define CC1101_MDMCFG2 0x12 // Modem Configuration +#define CC1101_MDMCFG1 0x13 // Modem Configuration +#define CC1101_MDMCFG0 0x14 // Modem Configuration +#define CC1101_DEVIATN 0x15 // Modem Deviation Setting +#define CC1101_MCSM2 0x16 // Main Radio Control State Machine Configuration +#define CC1101_MCSM1 0x17 // Main Radio Control State Machine Configuration +#define CC1101_MCSM0 0x18 // Main Radio Control State Machine Configuration +#define CC1101_FOCCFG 0x19 // Frequency Offset Compensation Configuration +#define CC1101_BSCFG 0x1A // Bit Synchronization Configuration +#define CC1101_AGCCTRL2 0x1B // AGC Control +#define CC1101_AGCCTRL1 0x1C // AGC Control +#define CC1101_AGCCTRL0 0x1D // AGC Control +#define CC1101_WOREVT1 0x1E // High Byte Event0 Timeout +#define CC1101_WOREVT0 0x1F // Low Byte Event0 Timeout +#define CC1101_WORCTRL 0x20 // Wake On Radio Control +#define CC1101_FREND1 0x21 // Front End RX Configuration +#define CC1101_FREND0 0x22 // Front End TX Configuration +#define CC1101_FSCAL3 0x23 // Frequency Synthesizer Calibration +#define CC1101_FSCAL2 0x24 // Frequency Synthesizer Calibration +#define CC1101_FSCAL1 0x25 // Frequency Synthesizer Calibration +#define CC1101_FSCAL0 0x26 // Frequency Synthesizer Calibration +#define CC1101_RCCTRL1 0x27 // RC Oscillator Configuration +#define CC1101_RCCTRL0 0x28 // RC Oscillator Configuration +#define CC1101_FSTEST 0x29 // Frequency Synthesizer Calibration Control +#define CC1101_PTEST 0x2A // Production Test +#define CC1101_AGCTEST 0x2B // AGC Test +#define CC1101_TEST2 0x2C // Various Test Settings +#define CC1101_TEST1 0x2D // Various Test Settings +#define CC1101_TEST0 0x2E // Various Test Settings + +/** + * Status registers + */ +#define CC1101_PARTNUM 0x30 // Chip ID +#define CC1101_VERSION 0x31 // Chip ID +#define CC1101_FREQEST 0x32 // Frequency Offset Estimate from Demodulator +#define CC1101_LQI 0x33 // Demodulator Estimate for Link Quality +#define CC1101_RSSI 0x34 // Received Signal Strength Indication +#define CC1101_MARCSTATE 0x35 // Main Radio Control State Machine State +#define CC1101_WORTIME1 0x36 // High Byte of WOR Time +#define CC1101_WORTIME0 0x37 // Low Byte of WOR Time +#define CC1101_PKTSTATUS 0x38 // Current GDOx Status and Packet Status +#define CC1101_VCO_VC_DAC 0x39 // Current Setting from PLL Calibration Module +#define CC1101_TXBYTES 0x3A // Underflow and Number of Bytes +#define CC1101_RXBYTES 0x3B // Overflow and Number of Bytes +#define CC1101_RCCTRL1_STATUS 0x3C // Last RC Oscillator Calibration Result +#define CC1101_RCCTRL0_STATUS 0x3D // Last RC Oscillator Calibration Result + +typedef struct { + uint16_t Reg; + uint8_t Value; +} RfSetting; + +extern spi_device_handle_t gSpiHndl; + +void CC1101_SetConfig(const RfSetting *pConfig); +int CC1101_Rx(uint8_t *RxBuf,size_t RxBufLen,uint8_t *pRssi,uint8_t *pLqi); +bool CC1101_Tx(uint8_t *TxData); +bool CC1101_Present(void); +void CC1101_DumpRegs(void); +void CC1101_reset(void); +void CC1101_logState(void); +void CC1101_setRxState(void); + +#endif // __CC1101_RADIO_H_ + diff --git a/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/led.c b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/led.c new file mode 100644 index 00000000..87469298 --- /dev/null +++ b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/led.c @@ -0,0 +1,48 @@ +#include "led.h" +#include "driver/gpio.h" +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/task.h" +#include "freertos/timers.h" +#include "proto.h" +#include +#include +#include +#include + +#define NUM_LEDS 2 + +const gpio_num_t led_pins[NUM_LEDS] = {LED1, LED2}; +TimerHandle_t led_timers[NUM_LEDS] = {0}; + +void led_timer_callback(TimerHandle_t xTimer) { + int led_index = (int)pvTimerGetTimerID(xTimer); + if (led_index >= 0 && led_index < NUM_LEDS) { + gpio_set_level(led_pins[led_index], 0); + } +} + +void init_led() { + gpio_config_t led1 = {}; + led1.intr_type = GPIO_INTR_DISABLE; + led1.mode = GPIO_MODE_OUTPUT; + led1.pin_bit_mask = ((1ULL << LED1) | (1ULL << LED2)); + led1.pull_down_en = 0; + led1.pull_up_en = 0; + gpio_config(&led1); + + for (int i = 0; i < NUM_LEDS; i++) { + led_timers[i] = xTimerCreate("led_timer", pdMS_TO_TICKS(50), pdFALSE, (void *)i, led_timer_callback); + } +} + +void led_flash(int nr) { + gpio_set_level(led_pins[nr], 1); + if (nr >= 0 && nr < NUM_LEDS) { + xTimerStart(led_timers[nr], 0); + } +} + +void led_set(int nr, bool state) { + gpio_set_level(nr, state); +} \ No newline at end of file diff --git a/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/led.h b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/led.h new file mode 100644 index 00000000..a76467c7 --- /dev/null +++ b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/led.h @@ -0,0 +1,6 @@ +#pragma once +#include + +void init_led(); +void led_set(int nr, bool state); +void led_flash(int nr); diff --git a/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/main.c b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/main.c new file mode 100644 index 00000000..f675bb0f --- /dev/null +++ b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/main.c @@ -0,0 +1,833 @@ +// Ported to ESP32-H2 By ATC1441(ATCnetz.de) for OpenEPaperLink at ~08.2023 + +#include "main.h" +#include "driver/gpio.h" +#include "driver/uart.h" +#include "esp_err.h" +#include "esp_event.h" +#include "esp_ieee802154.h" +#include "esp_log.h" +#include "esp_phy_init.h" +#include "esp_timer.h" +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/task.h" +#include "led.h" +#include "proto.h" +#include "radio.h" +#include "sdkconfig.h" +#include "second_uart.h" +//#include "soc/lp_uart_reg.h" +#include "soc/uart_struct.h" +#include "utils.h" +#include +#include +#include +#include +#include +#include +#include "SubGigRadio.h" + + +static const char *TAG = "MAIN"; + +const uint8_t channelList[6] = {11, 15, 20, 25, 26, 27}; + +#define DATATYPE_NOUPDATE 0 +#define HW_TYPE 0xC6 + +#define MAX_PENDING_MACS 250 +#define HOUSEKEEPING_INTERVAL 60UL + +struct pendingData pendingDataArr[MAX_PENDING_MACS]; + +// VERSION GOES HERE! +uint16_t version = 0x001d; + +#define RAW_PKT_PADDING 2 + +uint8_t radiotxbuffer[128]; +uint8_t radiorxbuffer[128]; + +static uint32_t housekeepingTimer; + +struct blockRequest requestedData = {0}; // holds which data was requested by the tag + +uint8_t dstMac[8]; // target for the block transfer +uint16_t dstPan; // + +static uint32_t blockStartTimer = 0; // reference that holds when the AP sends the next block +uint32_t nextBlockAttempt = 0; // reference time for when the AP can request a new block from the ESP32 +uint8_t seq = 0; // holds current sequence number for transmission +uint8_t blockbuffer[BLOCK_XFER_BUFFER_SIZE + 5]; // block transfer buffer +uint8_t lastAckMac[8] = {0}; + +// these variables hold the current mac were talking to +#define CONCURRENT_REQUEST_DELAY 1200UL +uint32_t lastBlockRequest = 0; +uint8_t lastBlockMac[8]; +uint8_t lastTagReturn[8]; + +#define NO_SUBGHZ_CHANNEL 255 +uint8_t curSubGhzChannel; +uint8_t curChannel = 25; +uint8_t curPower = 10; + +uint8_t curPendingData = 0; +uint8_t curNoUpdate = 0; + +bool highspeedSerial = false; + +void sendXferCompleteAck(uint8_t *dst); +void sendCancelXfer(uint8_t *dst); +void espNotifyAPInfo(); + +// tools +void addCRC(void *p, uint8_t len) { + uint8_t total = 0; + for (uint8_t c = 1; c < len; c++) { + total += ((uint8_t *) p)[c]; + } + ((uint8_t *) p)[0] = total; +} +bool checkCRC(void *p, uint8_t len) { + uint8_t total = 0; + for (uint8_t c = 1; c < len; c++) { + total += ((uint8_t *) p)[c]; + } + return ((uint8_t *) p)[0] == total; +} + +uint8_t getPacketType(void *buffer) { + struct MacFcs *fcs = buffer; + if ((fcs->frameType == 1) && (fcs->destAddrType == 2) && (fcs->srcAddrType == 3) && (fcs->panIdCompressed == 0)) { + // broadcast frame + uint8_t type = ((uint8_t *) buffer)[sizeof(struct MacFrameBcast)]; + return type; + } else if ((fcs->frameType == 1) && (fcs->destAddrType == 3) && (fcs->srcAddrType == 3) && (fcs->panIdCompressed == 1)) { + // normal frame + uint8_t type = ((uint8_t *) buffer)[sizeof(struct MacFrameNormal)]; + return type; + } + return 0; +} +uint8_t getBlockDataLength() { + uint8_t partNo = 0; + for (uint8_t c = 0; c < BLOCK_MAX_PARTS; c++) { + if (requestedData.requestedParts[c / 8] & (1 << (c % 8))) { + partNo++; + } + } + return partNo; +} + +// pendingdata slot stuff +int8_t findSlotForMac(const uint8_t *mac) { + for (uint8_t c = 0; c < MAX_PENDING_MACS; c++) { + if (memcmp(mac, ((uint8_t *) &(pendingDataArr[c].targetMac)), 8) == 0) { + if (pendingDataArr[c].attemptsLeft != 0) { + return c; + } + } + } + return -1; +} +int8_t findFreeSlot() { + for (uint8_t c = 0; c < MAX_PENDING_MACS; c++) { + if (pendingDataArr[c].attemptsLeft == 0) { + return c; + } + } + return -1; +} +int8_t findSlotForVer(const uint8_t *ver) { + for (uint8_t c = 0; c < MAX_PENDING_MACS; c++) { + if (memcmp(ver, ((uint8_t *) &(pendingDataArr[c].availdatainfo.dataVer)), 8) == 0) { + if (pendingDataArr[c].attemptsLeft != 0) return c; + } + } + return -1; +} +void deleteAllPendingDataForVer(const uint8_t *ver) { + int8_t slot = -1; + do { + slot = findSlotForVer(ver); + if (slot != -1) pendingDataArr[slot].attemptsLeft = 0; + } while (slot != -1); +} +void deleteAllPendingDataForMac(const uint8_t *mac) { + int8_t slot = -1; + do { + slot = findSlotForMac(mac); + if (slot != -1) pendingDataArr[slot].attemptsLeft = 0; + } while (slot != -1); +} + +void countSlots() { + curPendingData = 0; + curNoUpdate = 0; + for (uint8_t c = 0; c < MAX_PENDING_MACS; c++) { + if (pendingDataArr[c].attemptsLeft != 0) { + if (pendingDataArr[c].availdatainfo.dataType != 0) { + curPendingData++; + } else { + curNoUpdate++; + } + } + } +} + +// processing serial data +#define ZBS_RX_WAIT_HEADER 0 +#define ZBS_RX_WAIT_SDA 1 // send data avail +#define ZBS_RX_WAIT_CANCEL 2 // cancel traffic for mac +#define ZBS_RX_WAIT_SCP 3 // set channel power +#define ZBS_RX_WAIT_BLOCKDATA 4 + +bool isSame(uint8_t *in1, char *in2, int len) { + bool flag = 1; + for (int i = 0; i < len; i++) { + if (in1[i] != in2[i]) flag = 0; + } + return flag; +} + +int blockPosition = 0; +void processSerial(uint8_t lastchar) { + static uint8_t cmdbuffer[4]; + static uint8_t RXState = 0; + static uint8_t serialbuffer[48]; + static uint8_t *serialbufferp; + static uint8_t bytesRemain = 0; + static uint32_t lastSerial = 0; + static uint32_t blockStartTime = 0; + if ((RXState != ZBS_RX_WAIT_HEADER) && ((getMillis() - lastSerial) > 1000)) { + RXState = ZBS_RX_WAIT_HEADER; + ESP_LOGI(TAG, "UART Timeout"); + } + lastSerial = getMillis(); + switch (RXState) { + case ZBS_RX_WAIT_HEADER: + // shift characters in + for (uint8_t c = 0; c < 3; c++) { + cmdbuffer[c] = cmdbuffer[c + 1]; + } + cmdbuffer[3] = lastchar; + + if (isSame(cmdbuffer + 1, ">D>", 3)) { + pr("ACK>"); + blockStartTime = getMillis(); + ESP_LOGI(TAG, "Starting BlkData, %lu ms after request", blockStartTime - nextBlockAttempt ); + blockPosition = 0; + RXState = ZBS_RX_WAIT_BLOCKDATA; + } + + if (isSame(cmdbuffer, "SDA>", 4)) { + ESP_LOGI(TAG, "SDA In"); + RXState = ZBS_RX_WAIT_SDA; + bytesRemain = sizeof(struct pendingData); + serialbufferp = serialbuffer; + break; + } + if (isSame(cmdbuffer, "CXD>", 4)) { + ESP_LOGI(TAG, "CXD In"); + RXState = ZBS_RX_WAIT_CANCEL; + bytesRemain = sizeof(struct pendingData); + serialbufferp = serialbuffer; + break; + } + if (isSame(cmdbuffer, "SCP>", 4)) { + ESP_LOGI(TAG, "SCP In"); + RXState = ZBS_RX_WAIT_SCP; + bytesRemain = sizeof(struct espSetChannelPower); + serialbufferp = serialbuffer; + break; + } + if (isSame(cmdbuffer, "NFO?", 4)) { + pr("ACK>"); + ESP_LOGI(TAG, "NFO? In"); + espNotifyAPInfo(); + RXState = ZBS_RX_WAIT_HEADER; + } + if (isSame(cmdbuffer, "RDY?", 4)) { + pr("ACK>"); + ESP_LOGI(TAG, "RDY? In"); + RXState = ZBS_RX_WAIT_HEADER; + } + if (isSame(cmdbuffer, "RSET", 4)) { + pr("ACK>"); + ESP_LOGI(TAG, "RSET In"); + delay(100); + // TODO RESET US HERE + RXState = ZBS_RX_WAIT_HEADER; + } + if (isSame(cmdbuffer, "HSPD", 4)) { + pr("ACK>"); + ESP_LOGI(TAG, "HSPD In, switching to 2000000"); + delay(100); + uart_switch_speed(2000000); + delay(100); + highspeedSerial = true; + pr("ACK>"); + RXState = ZBS_RX_WAIT_HEADER; + } + break; + case ZBS_RX_WAIT_BLOCKDATA: + blockbuffer[blockPosition++] = 0xAA ^ lastchar; + if (blockPosition >= 4100) { + ESP_LOGI(TAG, "Blockdata fully received in %lu ms, %lu ms after the request", getMillis() - blockStartTime, getMillis() - nextBlockAttempt); + RXState = ZBS_RX_WAIT_HEADER; + } + break; + + case ZBS_RX_WAIT_SDA: + *serialbufferp = lastchar; + serialbufferp++; + bytesRemain--; + if (bytesRemain == 0) { + if (checkCRC(serialbuffer, sizeof(struct pendingData))) { + struct pendingData *pd = (struct pendingData *) serialbuffer; + int8_t slot = findSlotForMac(pd->targetMac); + if (slot == -1) slot = findFreeSlot(); + if (slot != -1) { + memcpy(&(pendingDataArr[slot]), serialbuffer, sizeof(struct pendingData)); + pr("ACK>"); + } else { + pr("NOQ>"); + } + } else { + pr("NOK>"); + } + + RXState = ZBS_RX_WAIT_HEADER; + } + break; + case ZBS_RX_WAIT_CANCEL: + *serialbufferp = lastchar; + serialbufferp++; + bytesRemain--; + if (bytesRemain == 0) { + if (checkCRC(serialbuffer, sizeof(struct pendingData))) { + struct pendingData *pd = (struct pendingData *) serialbuffer; + deleteAllPendingDataForMac((uint8_t *) &pd->targetMac); + pr("ACK>"); + } else { + pr("NOK>"); + } + + RXState = ZBS_RX_WAIT_HEADER; + } + break; + case ZBS_RX_WAIT_SCP: + *serialbufferp = lastchar; + serialbufferp++; + bytesRemain--; + if (bytesRemain == 0) { + if (checkCRC(serialbuffer, sizeof(struct espSetChannelPower))) { + struct espSetChannelPower *scp = (struct espSetChannelPower *) serialbuffer; +#ifdef CONFIG_OEPL_SUBGIG_SUPPORT + if(curSubGhzChannel != scp->subghzchannel + && curSubGhzChannel != NO_SUBGHZ_CHANNEL) + { + curSubGhzChannel = scp->subghzchannel; + ESP_LOGI(TAG,"Set SubGhz channel: %d",curSubGhzChannel); + SubGig_radioSetChannel(scp->subghzchannel); + if(scp->channel == 0) { + // Not setting 802.15.4 channel + goto SCPchannelFound; + } + } +#endif + for (uint8_t c = 0; c < sizeof(channelList); c++) { + if (channelList[c] == scp->channel) goto SCPchannelFound; + } + goto SCPfailed; + SCPchannelFound: + pr("ACK>"); + if (curChannel != scp->channel) { + radioSetChannel(scp->channel); + curChannel = scp->channel; + } + curPower = scp->power; + radioSetTxPower(scp->power); + ESP_LOGI(TAG, "Set channel: %d power: %d", curChannel, curPower); + } else { + SCPfailed: + pr("NOK>"); + } + RXState = ZBS_RX_WAIT_HEADER; + } + break; + } +} + +// sending data to the ESP +void espBlockRequest(const struct blockRequest *br, uint8_t *src) { + struct espBlockRequest *ebr = (struct espBlockRequest *) blockbuffer; + uartTx('R'); + uartTx('Q'); + uartTx('B'); + uartTx('>'); + memcpy(&(ebr->ver), &(br->ver), 8); + memcpy(&(ebr->src), src, 8); + ebr->blockId = br->blockId; + addCRC(ebr, sizeof(struct espBlockRequest)); + for (uint8_t c = 0; c < sizeof(struct espBlockRequest); c++) { + uartTx(((uint8_t *) ebr)[c]); + } +} +void espNotifyAvailDataReq(const struct AvailDataReq *adr, const uint8_t *src) { + uartTx('A'); + uartTx('D'); + uartTx('R'); + uartTx('>'); + + struct espAvailDataReq eadr = {0}; + memcpy((void *) eadr.src, (void *) src, 8); + memcpy((void *) &eadr.adr, (void *) adr, sizeof(struct AvailDataReq)); + addCRC(&eadr, sizeof(struct espAvailDataReq)); + for (uint8_t c = 0; c < sizeof(struct espAvailDataReq); c++) { + uartTx(((uint8_t *) &eadr)[c]); + } +} +void espNotifyXferComplete(const uint8_t *src) { + struct espXferComplete exfc; + memcpy(&exfc.src, src, 8); + uartTx('X'); + uartTx('F'); + uartTx('C'); + uartTx('>'); + addCRC(&exfc, sizeof(exfc)); + for (uint8_t c = 0; c < sizeof(exfc); c++) { + uartTx(((uint8_t *) &exfc)[c]); + } +} +void espNotifyTimeOut(const uint8_t *src) { + struct espXferComplete exfc; + memcpy(&exfc.src, src, 8); + uartTx('X'); + uartTx('T'); + uartTx('O'); + uartTx('>'); + addCRC(&exfc, sizeof(exfc)); + for (uint8_t c = 0; c < sizeof(exfc); c++) { + uartTx(((uint8_t *) &exfc)[c]); + } +} +void espNotifyAPInfo() { + pr("TYP>%02X", HW_TYPE); + pr("VER>%04X", version); + pr("MAC>%02X%02X", mSelfMac[0], mSelfMac[1]); + pr("%02X%02X", mSelfMac[2], mSelfMac[3]); + pr("%02X%02X", mSelfMac[4], mSelfMac[5]); + pr("%02X%02X", mSelfMac[6], mSelfMac[7]); + pr("ZCH>%02X", curChannel); +#ifdef CONFIG_OEPL_SUBGIG_SUPPORT + pr("SCH>%03d",curSubGhzChannel); +#endif + pr("ZPW>%02X", curPower); + countSlots(); + pr("PEN>%02X", curPendingData); + pr("NOP>%02X", curNoUpdate); +} + +void espNotifyTagReturnData(uint8_t *src, uint8_t len) { + struct tagReturnData *trd = (struct tagReturnData *)(radiorxbuffer + sizeof(struct MacFrameBcast) + 1); // oh how I'd love to pass this as an argument, but sdcc won't let me + struct espTagReturnData *etrd = (struct espTagReturnData *)radiotxbuffer; + + if (memcmp((void *) & trd->dataVer, lastTagReturn, 8) == 0) { + return; + } else { + memcpy(lastTagReturn, &trd->dataVer, 8); + } + + memcpy(etrd->src, src, 8); + etrd->len = len; + memcpy(&etrd->returnData, trd, len); + addCRC(etrd, len + 10); + + uartTx('T'); + uartTx('R'); + uartTx('D'); + uartTx('>'); + for (uint8_t c = 0; c < len + 10; c++) { + uartTx(((uint8_t *)etrd)[c]); + } +} + +// process data from tag +void processBlockRequest(const uint8_t *buffer, uint8_t forceBlockDownload) { + struct MacFrameNormal *rxHeader = (struct MacFrameNormal *) buffer; + struct blockRequest *blockReq = (struct blockRequest *) (buffer + sizeof(struct MacFrameNormal) + 1); + if (!checkCRC(blockReq, sizeof(struct blockRequest))) return; + + // check if we're already talking to this mac + if (memcmp(rxHeader->src, lastBlockMac, 8) == 0) { + lastBlockRequest = getMillis(); + } else { + // we weren't talking to this mac, see if there was a transfer in progress from another mac, recently + if ((getMillis() - lastBlockRequest) > CONCURRENT_REQUEST_DELAY) { + // mark this mac as the new current mac we're talking to + memcpy((void *) lastBlockMac, (void *) rxHeader->src, 8); + lastBlockRequest = getMillis(); + } else { + // we're talking to another mac, let this mac know we can't accomodate another request right now + pr("BUSY!\n"); + sendCancelXfer(rxHeader->src); + return; + } + } + + // check if we have data for this mac + if (findSlotForMac(rxHeader->src) == -1) { + // no data for this mac, politely tell it to fuck off + sendCancelXfer(rxHeader->src); + return; + } + + bool requestDataDownload = false; + if ((blockReq->blockId != requestedData.blockId) || (blockReq->ver != requestedData.ver)) { + // requested block isn't already in the buffer + requestDataDownload = true; + } else { + // requested block is already in the buffer + if (forceBlockDownload) { + if ((getMillis() - nextBlockAttempt) > 380) { + requestDataDownload = true; + pr("FORCED\n"); + } else { + pr("IGNORED\n"); + } + } + } + + // copy blockrequest into requested data + memcpy(&requestedData, blockReq, sizeof(struct blockRequest)); + + struct MacFrameNormal *txHeader = (struct MacFrameNormal *) (radiotxbuffer + 1); + struct blockRequestAck *blockRequestAck = (struct blockRequestAck *) (radiotxbuffer + sizeof(struct MacFrameNormal) + 2); + radiotxbuffer[0] = sizeof(struct MacFrameNormal) + 1 + sizeof(struct blockRequestAck) + RAW_PKT_PADDING; + radiotxbuffer[sizeof(struct MacFrameNormal) + 1] = PKT_BLOCK_REQUEST_ACK; + + if (blockStartTimer == 0) { + if (requestDataDownload) { + if (highspeedSerial == true) { + blockRequestAck->pleaseWaitMs = 140; + } else { + blockRequestAck->pleaseWaitMs = 550; + } + } else { + // block is already in buffer + blockRequestAck->pleaseWaitMs = 30; + } + } else { + blockRequestAck->pleaseWaitMs = 30; + } + blockStartTimer = getMillis() + blockRequestAck->pleaseWaitMs; + + memcpy(txHeader->src, mSelfMac, 8); + memcpy(txHeader->dst, rxHeader->src, 8); + + txHeader->pan = rxHeader->pan; + txHeader->fcs.frameType = 1; + txHeader->fcs.panIdCompressed = 1; + txHeader->fcs.destAddrType = 3; + txHeader->fcs.srcAddrType = 3; + txHeader->seq = seq++; + + addCRC((void *) blockRequestAck, sizeof(struct blockRequestAck)); + + radioTx(radiotxbuffer); + + // save the target for the blockdata + memcpy(dstMac, rxHeader->src, 8); + dstPan = rxHeader->pan; + + if (requestDataDownload) { + blockPosition = 0; + espBlockRequest(&requestedData, rxHeader->src); + nextBlockAttempt = getMillis(); + } +} + +void processAvailDataReq(uint8_t *buffer) { + struct MacFrameBcast *rxHeader = (struct MacFrameBcast *) buffer; + struct AvailDataReq *availDataReq = (struct AvailDataReq *) (buffer + sizeof(struct MacFrameBcast) + 1); + + if (!checkCRC(availDataReq, sizeof(struct AvailDataReq))) return; + + // prepare tx buffer to send a response + memset(radiotxbuffer, 0, sizeof(struct MacFrameNormal) + sizeof(struct AvailDataInfo) + 2); // 120); + struct MacFrameNormal *txHeader = (struct MacFrameNormal *) (radiotxbuffer + 1); + struct AvailDataInfo *availDataInfo = (struct AvailDataInfo *) (radiotxbuffer + sizeof(struct MacFrameNormal) + 2); + radiotxbuffer[0] = sizeof(struct MacFrameNormal) + 1 + sizeof(struct AvailDataInfo) + RAW_PKT_PADDING; + radiotxbuffer[sizeof(struct MacFrameNormal) + 1] = PKT_AVAIL_DATA_INFO; + + // check to see if we have data available for this mac + bool haveData = false; + for (uint8_t c = 0; c < MAX_PENDING_MACS; c++) { + if (pendingDataArr[c].attemptsLeft) { + if (memcmp(pendingDataArr[c].targetMac, rxHeader->src, 8) == 0) { + haveData = true; + memcpy((void *) availDataInfo, &(pendingDataArr[c].availdatainfo), sizeof(struct AvailDataInfo)); + break; + } + } + } + + // couldn't find data for this mac + if (!haveData) availDataInfo->dataType = DATATYPE_NOUPDATE; + + memcpy(txHeader->src, mSelfMac, 8); + memcpy(txHeader->dst, rxHeader->src, 8); + txHeader->pan = rxHeader->dstPan; + txHeader->fcs.frameType = 1; + txHeader->fcs.panIdCompressed = 1; + txHeader->fcs.destAddrType = 3; + txHeader->fcs.srcAddrType = 3; + txHeader->seq = seq++; + addCRC(availDataInfo, sizeof(struct AvailDataInfo)); + radioTx(radiotxbuffer); + memset(lastAckMac, 0, 8); // reset lastAckMac, so we can record if we've received exactly one ack packet + espNotifyAvailDataReq(availDataReq, rxHeader->src); +} +void processXferComplete(uint8_t *buffer) { + struct MacFrameNormal *rxHeader = (struct MacFrameNormal *) buffer; + sendXferCompleteAck(rxHeader->src); + if (memcmp(lastAckMac, rxHeader->src, 8) != 0) { + memcpy((void *) lastAckMac, (void *) rxHeader->src, 8); + espNotifyXferComplete(rxHeader->src); + int8_t slot = findSlotForMac(rxHeader->src); + if (slot != -1) pendingDataArr[slot].attemptsLeft = 0; + } +} + +void processTagReturnData(uint8_t *buffer, uint8_t len) { + struct MacFrameBcast *rxframe = (struct MacFrameBcast *)buffer; + struct MacFrameNormal *frameHeader = (struct MacFrameNormal *)(radiotxbuffer + 1); + + if (!checkCRC((buffer + sizeof(struct MacFrameBcast) + 1), len - (sizeof(struct MacFrameBcast) + 1))) { + return; + } + radiotxbuffer[sizeof(struct MacFrameNormal) + 1] = PKT_TAG_RETURN_DATA_ACK; + radiotxbuffer[0] = sizeof(struct MacFrameNormal) + 1 + RAW_PKT_PADDING; + memcpy(frameHeader->src, mSelfMac, 8); + memcpy(frameHeader->dst, rxframe->src, 8); + radiotxbuffer[1] = 0x41; // fast way to set the appropriate bits + radiotxbuffer[2] = 0xCC; // normal frame + frameHeader->seq = seq++; + frameHeader->pan = rxframe->srcPan; + radioTx(radiotxbuffer); + + espNotifyTagReturnData(rxframe->src, len - (sizeof(struct MacFrameBcast) + 1)); +} + +// send block data to the tag +void sendPart(uint8_t partNo) { + struct MacFrameNormal *frameHeader = (struct MacFrameNormal *) (radiotxbuffer + 1); + struct blockPart *blockPart = (struct blockPart *) (radiotxbuffer + sizeof(struct MacFrameNormal) + 2); + memset(radiotxbuffer + 1, 0, sizeof(struct blockPart) + sizeof(struct MacFrameNormal)); + radiotxbuffer[sizeof(struct MacFrameNormal) + 1] = PKT_BLOCK_PART; + radiotxbuffer[0] = sizeof(struct MacFrameNormal) + sizeof(struct blockPart) + BLOCK_PART_DATA_SIZE + 1 + RAW_PKT_PADDING; + memcpy(frameHeader->src, mSelfMac, 8); + memcpy(frameHeader->dst, dstMac, 8); + blockPart->blockId = requestedData.blockId; + blockPart->blockPart = partNo; + memcpy(&(blockPart->data), blockbuffer + (partNo * BLOCK_PART_DATA_SIZE), BLOCK_PART_DATA_SIZE); + addCRC(blockPart, sizeof(struct blockPart) + BLOCK_PART_DATA_SIZE); + frameHeader->fcs.frameType = 1; + frameHeader->fcs.panIdCompressed = 1; + frameHeader->fcs.destAddrType = 3; + frameHeader->fcs.srcAddrType = 3; + frameHeader->seq = seq++; + frameHeader->pan = dstPan; + radioTx(radiotxbuffer); +} +void sendBlockData() { + if (getBlockDataLength() == 0) { + pr("Invalid block request received, 0 parts..\n"); + requestedData.requestedParts[0] |= 0x01; + } + + pr("Sending parts:"); + for (uint8_t c = 0; (c < BLOCK_MAX_PARTS); c++) { + if (c % 10 == 0) pr(" "); + if (requestedData.requestedParts[c / 8] & (1 << (c % 8))) { + pr("X"); + } else { + pr("."); + } + } + pr("\n"); + + uint8_t partNo = 0; + while (partNo < BLOCK_MAX_PARTS) { + for (uint8_t c = 0; (c < BLOCK_MAX_PARTS) && (partNo < BLOCK_MAX_PARTS); c++) { + if (requestedData.requestedParts[c / 8] & (1 << (c % 8))) { + sendPart(c); + partNo++; + } + } + if(dstPan == PROTO_PAN_ID_SUBGHZ) { + // Don't send BLOCK_MAX_PARTS for subgig, it requests what it + // can handle with its limited RAM + break; + } + } +} +void sendXferCompleteAck(uint8_t *dst) { + struct MacFrameNormal *frameHeader = (struct MacFrameNormal *) (radiotxbuffer + 1); + memset(radiotxbuffer + 1, 0, sizeof(struct blockPart) + sizeof(struct MacFrameNormal)); + radiotxbuffer[sizeof(struct MacFrameNormal) + 1] = PKT_XFER_COMPLETE_ACK; + radiotxbuffer[0] = sizeof(struct MacFrameNormal) + 1 + RAW_PKT_PADDING; + memcpy(frameHeader->src, mSelfMac, 8); + memcpy(frameHeader->dst, dst, 8); + frameHeader->fcs.frameType = 1; + frameHeader->fcs.panIdCompressed = 1; + frameHeader->fcs.destAddrType = 3; + frameHeader->fcs.srcAddrType = 3; + frameHeader->seq = seq++; + frameHeader->pan = dstPan; + radioTx(radiotxbuffer); +} +void sendCancelXfer(uint8_t *dst) { + struct MacFrameNormal *frameHeader = (struct MacFrameNormal *) (radiotxbuffer + 1); + memset(radiotxbuffer + 1, 0, sizeof(struct blockPart) + sizeof(struct MacFrameNormal)); + radiotxbuffer[sizeof(struct MacFrameNormal) + 1] = PKT_CANCEL_XFER; + radiotxbuffer[0] = sizeof(struct MacFrameNormal) + 1 + RAW_PKT_PADDING; + memcpy(frameHeader->src, mSelfMac, 8); + memcpy(frameHeader->dst, dst, 8); + frameHeader->fcs.frameType = 1; + frameHeader->fcs.panIdCompressed = 1; + frameHeader->fcs.destAddrType = 3; + frameHeader->fcs.srcAddrType = 3; + frameHeader->seq = seq++; + frameHeader->pan = dstPan; + radioTx(radiotxbuffer); +} +void sendPong(void *buf) { + struct MacFrameBcast *rxframe = (struct MacFrameBcast *) buf; + struct MacFrameNormal *frameHeader = (struct MacFrameNormal *) (radiotxbuffer + 1); + radiotxbuffer[sizeof(struct MacFrameNormal) + 1] = PKT_PONG; +#ifdef CONFIG_OEPL_SUBGIG_SUPPORT + if(rxframe->srcPan == PROTO_PAN_ID_SUBGHZ) { + radiotxbuffer[sizeof(struct MacFrameNormal) + 2] = curSubGhzChannel; + } + else +#endif + radiotxbuffer[sizeof(struct MacFrameNormal) + 2] = curChannel; + radiotxbuffer[0] = sizeof(struct MacFrameNormal) + 1 + 1 + RAW_PKT_PADDING; + memcpy(frameHeader->src, mSelfMac, 8); + memcpy(frameHeader->dst, rxframe->src, 8); + radiotxbuffer[1] = 0x41; // fast way to set the appropriate bits + radiotxbuffer[2] = 0xCC; // normal frame + frameHeader->seq = seq++; + frameHeader->pan = rxframe->srcPan; + radioTx(radiotxbuffer); +} + +void app_main(void) { + esp_event_loop_create_default(); + + init_nvs(); + init_led(); + init_second_uart(); + + requestedData.blockId = 0xFF; + // clear the array with pending information + memset(pendingDataArr, 0, sizeof(pendingDataArr)); + + radio_init(curChannel); +#ifdef CONFIG_OEPL_SUBGIG_SUPPORT + if(SubGig_radio_init(curSubGhzChannel)) { + // Ether we don't have a cc1101 or it's not working + curSubGhzChannel = NO_SUBGHZ_CHANNEL; + ESP_LOGI(TAG,"CC1101 NOT detected."); + } + else { + ESP_LOGI(TAG,"CC1101 detected."); + } +#endif + radioSetTxPower(10); + + pr("RES>"); + pr("RDY>"); + ESP_LOGI(TAG, "H2 ready!"); + + housekeepingTimer = getMillis(); + while (1) { + while ((getMillis() - housekeepingTimer) < ((1000 * HOUSEKEEPING_INTERVAL) - 100)) { + int8_t ret = commsRxUnencrypted(radiorxbuffer); + if (ret > 1) { + led_flash(0); + // received a packet, lets see what it is + switch (getPacketType(radiorxbuffer)) { + case PKT_AVAIL_DATA_REQ: + if (ret == 28) { + // old version of the AvailDataReq struct, set all the new fields to zero, so it will pass the CRC + memset(radiorxbuffer + 1 + sizeof(struct MacFrameBcast) + sizeof(struct oldAvailDataReq), 0, + sizeof(struct AvailDataReq) - sizeof(struct oldAvailDataReq) + 2); + processAvailDataReq(radiorxbuffer); + } else if (ret == 40) { + // new version of the AvailDataReq struct + processAvailDataReq(radiorxbuffer); + } + break; + case PKT_BLOCK_REQUEST: + processBlockRequest(radiorxbuffer, 1); + break; + case PKT_BLOCK_PARTIAL_REQUEST: + processBlockRequest(radiorxbuffer, 0); + break; + case PKT_XFER_COMPLETE: + processXferComplete(radiorxbuffer); + break; + case PKT_PING: + sendPong(radiorxbuffer); + break; + case PKT_AVAIL_DATA_SHORTREQ: + // a short AvailDataReq is basically a very short (1 byte payload) packet that requires little preparation on the tx side, for optimal + // battery use bytes of the struct are set 0, so it passes the checksum test, and the ESP32 can detect that no interesting payload is + // sent + if (ret == 18) { + memset(radiorxbuffer + 1 + sizeof(struct MacFrameBcast), 0, sizeof(struct AvailDataReq) + 2); + processAvailDataReq(radiorxbuffer); + } + break; + case PKT_TAG_RETURN_DATA: + processTagReturnData(radiorxbuffer, ret); + break; + default: + ESP_LOGI(TAG, "t=%02X" , getPacketType(radiorxbuffer)); + break; + } + } else if (blockStartTimer == 0) { + vTaskDelay(10 / portTICK_PERIOD_MS); + } + + uint8_t curr_char; + while (getRxCharSecond(&curr_char)) processSerial(curr_char); + + if (blockStartTimer) { + if (getMillis() > blockStartTimer) { + sendBlockData(); + blockStartTimer = 0; + } + } + } + + memset(&lastTagReturn, 0, 8); + for (uint8_t cCount = 0; cCount < MAX_PENDING_MACS; cCount++) { + if (pendingDataArr[cCount].attemptsLeft == 1) { + if (pendingDataArr[cCount].availdatainfo.dataType != DATATYPE_NOUPDATE) { + espNotifyTimeOut(pendingDataArr[cCount].targetMac); + } + pendingDataArr[cCount].attemptsLeft = 0; + } else if (pendingDataArr[cCount].attemptsLeft > 1) { + pendingDataArr[cCount].attemptsLeft--; + if (pendingDataArr[cCount].availdatainfo.nextCheckIn) pendingDataArr[cCount].availdatainfo.nextCheckIn--; + } + } + housekeepingTimer = getMillis(); + } +} diff --git a/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/main.h b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/main.h new file mode 100644 index 00000000..7b9637ef --- /dev/null +++ b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/main.h @@ -0,0 +1 @@ +#pragma once \ No newline at end of file diff --git a/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/proto.h b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/proto.h new file mode 100644 index 00000000..0e02d1ea --- /dev/null +++ b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/proto.h @@ -0,0 +1,194 @@ +#ifndef _PROTO_H_ +#define _PROTO_H_ +#include + +#define LED1 22 +#define LED2 25 + +#define PROTO_PAN_ID (0x4447) // PAN ID compression shall be used +#define PROTO_PAN_ID_SUBGHZ (0x1337) // PAN ID compression shall be used + +#define RADIO_MAX_PACKET_LEN (125) // useful payload, not including the crc + +#define ADDR_MODE_NONE (0) +#define ADDR_MODE_SHORT (2) +#define ADDR_MODE_LONG (3) + +#define FRAME_TYPE_BEACON (0) +#define FRAME_TYPE_DATA (1) +#define FRAME_TYPE_ACK (2) +#define FRAME_TYPE_MAC_CMD (3) + +#define SHORT_MAC_UNUSED (0x10000000UL) // for radioRxFilterCfg's myShortMac + +struct MacFcs { + uint8_t frameType : 3; + uint8_t secure : 1; + uint8_t framePending : 1; + uint8_t ackReqd : 1; + uint8_t panIdCompressed : 1; + uint8_t rfu1 : 1; + uint8_t rfu2 : 2; + uint8_t destAddrType : 2; + uint8_t frameVer : 2; + uint8_t srcAddrType : 2; +} __attribute__((packed, aligned(1))); + +struct MacFrameFromMaster { + struct MacFcs fcs; + uint8_t seq; + uint16_t pan; + uint8_t dst[8]; + uint16_t from; +} __attribute__((packed, aligned(1))); + +struct MacFrameNormal { + struct MacFcs fcs; + uint8_t seq; + uint16_t pan; + uint8_t dst[8]; + uint8_t src[8]; +} __attribute__((packed, aligned(1))); + +struct MacFrameBcast { + struct MacFcs fcs; + uint8_t seq; + uint16_t dstPan; + uint16_t dstAddr; + uint16_t srcPan; + uint8_t src[8]; +} __attribute__((packed, aligned(1))); + +#define PKT_TAG_RETURN_DATA 0xE1 +#define PKT_TAG_RETURN_DATA_ACK 0xE2 +#define PKT_AVAIL_DATA_SHORTREQ 0xE3 +#define PKT_AVAIL_DATA_REQ 0xE5 +#define PKT_AVAIL_DATA_INFO 0xE6 +#define PKT_BLOCK_PARTIAL_REQUEST 0xE7 +#define PKT_BLOCK_REQUEST_ACK 0xE9 +#define PKT_BLOCK_REQUEST 0xE4 +#define PKT_BLOCK_PART 0xE8 +#define PKT_XFER_COMPLETE 0xEA +#define PKT_XFER_COMPLETE_ACK 0xEB +#define PKT_CANCEL_XFER 0xEC +#define PKT_PING 0xED +#define PKT_PONG 0xEE + +struct AvailDataReq { + uint8_t checksum; + uint8_t lastPacketLQI; + int8_t lastPacketRSSI; + int8_t temperature; + uint16_t batteryMv; + uint8_t hwType; + uint8_t wakeupReason; + uint8_t capabilities; + uint16_t tagSoftwareVersion; + uint8_t currentChannel; + uint8_t customMode; + uint8_t reserved[8]; +} __attribute__((packed, aligned(1))); + +struct oldAvailDataReq { + uint8_t checksum; + uint8_t lastPacketLQI; + int8_t lastPacketRSSI; + int8_t temperature; + uint16_t batteryMv; + uint8_t hwType; + uint8_t wakeupReason; + uint8_t capabilities; +} __attribute__((packed, aligned(1))); + +struct AvailDataInfo { + uint8_t checksum; + uint64_t dataVer; // MD5 of potential traffic + uint32_t dataSize; + uint8_t dataType; + uint8_t dataTypeArgument; // extra specification or instruction for the tag (LUT to be used for drawing image) + uint16_t nextCheckIn; // when should the tag check-in again? Measured in minutes +} __attribute__((packed, aligned(1))); + +struct pendingData { + struct AvailDataInfo availdatainfo; + uint16_t attemptsLeft; + uint8_t targetMac[8]; +} __attribute__((packed, aligned(1))); + +struct blockPart { + uint8_t checksum; + uint8_t blockId; + uint8_t blockPart; + uint8_t data[]; +} __attribute__((packed, aligned(1))); + +struct blockData { + uint16_t size; + uint16_t checksum; + uint8_t data[]; +} __attribute__((packed, aligned(1))); + +#define TAG_RETURN_DATA_SIZE 90 + +struct tagReturnData { + uint8_t checksum; + uint8_t partId; + uint64_t dataVer; + uint8_t dataType; + uint8_t data[TAG_RETURN_DATA_SIZE]; +} __attribute__((packed, aligned(1))); + +#define BLOCK_PART_DATA_SIZE 99 +#define BLOCK_MAX_PARTS 42 +#define BLOCK_DATA_SIZE 4096UL +#define BLOCK_XFER_BUFFER_SIZE BLOCK_DATA_SIZE + sizeof(struct blockData) +#define BLOCK_REQ_PARTS_BYTES 6 + +struct blockRequest { + uint8_t checksum; + uint64_t ver; + uint8_t blockId; + uint8_t type; + uint8_t requestedParts[BLOCK_REQ_PARTS_BYTES]; +} __attribute__((packed, aligned(1))); + +struct blockRequestAck { + uint8_t checksum; + uint16_t pleaseWaitMs; +} __attribute__((packed, aligned(1))); + +struct espBlockRequest { + uint8_t checksum; + uint64_t ver; + uint8_t blockId; + uint8_t src[8]; +} __attribute__((packed, aligned(1))); + +struct espXferComplete { + uint8_t checksum; + uint8_t src[8]; +} __attribute__((packed, aligned(1))); + +struct espAvailDataReq { + uint8_t checksum; + uint8_t src[8]; + struct AvailDataReq adr; +} __attribute__((packed, aligned(1))); + +struct espSetChannelPower { + uint8_t checksum; + uint8_t channel; + uint8_t power; +#ifdef CONFIG_OEPL_SUBGIG_SUPPORT + uint8_t subghzchannel; +#endif +} __attribute__((packed, aligned(1))); + +struct espTagReturnData { + uint8_t checksum; + uint8_t src[8]; + uint8_t len; + struct tagReturnData returnData; +} __attribute__((packed, aligned(1))); + +#endif diff --git a/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/radio.c b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/radio.c new file mode 100644 index 00000000..e919b5f4 --- /dev/null +++ b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/radio.c @@ -0,0 +1,150 @@ +#include +#include "radio.h" +#include "driver/gpio.h" +#include "driver/uart.h" +#include "esp_err.h" +#include "esp_event.h" +#include "esp_ieee802154.h" +#include "esp_log.h" +#include "esp_phy_init.h" +#include "esp_timer.h" +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/task.h" +#include "led.h" +#include "main.h" +#include "proto.h" +#include "sdkconfig.h" +// if you get an error about soc/lp_uart_reg.h not being found, +// you didn't choose the right build target. :-) +//#include "soc/lp_uart_reg.h" +#include "soc/uart_struct.h" +#include "utils.h" +#include +#include +#include +#include +#include "SubGigRadio.h" + + +static const char *TAG = "RADIO"; + +uint8_t mSelfMac[8]; +volatile uint8_t isInTransmit = 0; +QueueHandle_t packet_buffer = NULL; +void esp_ieee802154_receive_done(uint8_t *frame, esp_ieee802154_frame_info_t *frame_info) { + ESP_EARLY_LOGI(TAG, "RX %d", frame[0]); + BaseType_t xHigherPriorityTaskWoken; + static uint8_t inner_rxPKT[130]; + memcpy(inner_rxPKT, &frame[0], frame[0] + 1); + xQueueSendFromISR(packet_buffer, (void *)&inner_rxPKT, &xHigherPriorityTaskWoken); + portYIELD_FROM_ISR_ARG(xHigherPriorityTaskWoken); + esp_ieee802154_receive_sfd_done(); +} + +void esp_ieee802154_transmit_failed(const uint8_t *frame, esp_ieee802154_tx_error_t error) { + isInTransmit = 0; + ESP_EARLY_LOGE(TAG, "TX Err: %d", error); +} + +void esp_ieee802154_transmit_done(const uint8_t *frame, const uint8_t *ack, esp_ieee802154_frame_info_t *ack_frame_info) { + isInTransmit = 0; + ESP_EARLY_LOGI(TAG, "TX %d", frame[0]); + esp_ieee802154_receive_sfd_done(); +} +static bool zigbee_is_enabled = false; +void radio_init(uint8_t ch) { + if (packet_buffer == NULL) packet_buffer = xQueueCreate(32, 130); + + // this will trigger a "IEEE802154 MAC sleep init failed" when called a second time, but it works + if(zigbee_is_enabled) + { + zigbee_is_enabled = false; + esp_ieee802154_disable(); + } + zigbee_is_enabled = true; + esp_ieee802154_enable(); + esp_ieee802154_set_channel(ch); + // esp_ieee802154_set_txpower(int8_t power); + esp_ieee802154_set_panid(PROTO_PAN_ID); + esp_ieee802154_set_promiscuous(false); + esp_ieee802154_set_coordinator(false); + esp_ieee802154_set_pending_mode(ESP_IEEE802154_AUTO_PENDING_ZIGBEE); + + // esp_ieee802154_set_extended_address needs the MAC in reversed byte order + esp_read_mac(mSelfMac, ESP_MAC_IEEE802154); + uint8_t eui64_rev[8] = {0}; + for (int i = 0; i < 8; i++) { + eui64_rev[7 - i] = mSelfMac[i]; + } + esp_ieee802154_set_extended_address(eui64_rev); + esp_ieee802154_get_extended_address(mSelfMac); + + esp_ieee802154_set_short_address(0xFFFE); + esp_ieee802154_set_rx_when_idle(true); + esp_ieee802154_receive(); + + led_flash(1); + vTaskDelay(100 / portTICK_PERIOD_MS); + led_flash(0); + vTaskDelay(100 / portTICK_PERIOD_MS); + led_flash(1); + vTaskDelay(100 / portTICK_PERIOD_MS); + led_flash(0); + + ESP_LOGI(TAG, "Receiver ready, panId=0x%04x, channel=%d, long=%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x, short=%04x", + esp_ieee802154_get_panid(), esp_ieee802154_get_channel(), + mSelfMac[0], mSelfMac[1], mSelfMac[2], mSelfMac[3], + mSelfMac[4], mSelfMac[5], mSelfMac[6], mSelfMac[7], + esp_ieee802154_get_short_address()); +} + +// uint32_t lastZbTx = 0; +bool radioTx(uint8_t *packet) { +#ifdef CONFIG_OEPL_SUBGIG_SUPPORT +// The subghz driver uses DMA + static DMA_ATTR uint8_t txPKT[130]; +#else + static uint8_t txPKT[130]; +#endif + led_flash(1); + // while (getMillis() - lastZbTx < 6) { + // } + // lastZbTx = getMillis(); + memcpy(txPKT, packet, packet[0]); +#ifdef CONFIG_OEPL_SUBGIG_SUPPORT + struct MacFrameNormal *txHeader = (struct MacFrameNormal *) (packet + 1); + + if(txHeader->pan == PROTO_PAN_ID_SUBGHZ) { + return SubGig_radioTx(packet); + } +#endif + while (isInTransmit) { + } + isInTransmit = 1; + esp_ieee802154_transmit(txPKT, false); + return true; +} + +void radioSetChannel(uint8_t ch) { + radio_init(ch); +} + +void radioSetTxPower(uint8_t power) {} + +int8_t commsRxUnencrypted(uint8_t *data) { + static uint8_t inner_rxPKT_out[130]; + if (xQueueReceive(packet_buffer, (void *)&inner_rxPKT_out, pdMS_TO_TICKS(100)) == pdTRUE) { + memcpy(data, &inner_rxPKT_out[1], inner_rxPKT_out[0] + 1); + return inner_rxPKT_out[0] - 2; + } +#ifdef CONFIG_OEPL_SUBGIG_SUPPORT + if(gSubGigData.Enabled) { + int8_t Ret = SubGig_commsRxUnencrypted(data); + if(Ret > 0) { + return Ret; + } + } +#endif + return 0; +} diff --git a/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/radio.h b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/radio.h new file mode 100644 index 00000000..6bd55d42 --- /dev/null +++ b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/radio.h @@ -0,0 +1,20 @@ +#pragma once +#include +#include + +#define RAW_PKT_PADDING 2 +extern uint8_t mSelfMac[8]; + +void radio_init(uint8_t ch); +bool radioTx(uint8_t *packet); +void radioSetChannel(uint8_t ch); +void radioSetTxPower(uint8_t power); +int8_t commsRxUnencrypted(uint8_t *data); + +#ifdef SUBGIG_SUPPORT +void SubGig_radio_init(uint8_t ch); +bool SubGig_radioTx(uint8_t *packet); +void SubGig_radioSetChannel(uint8_t ch); +void SubGig_radioSetTxPower(uint8_t power); +int8_t SubGig_commsRxUnencrypted(uint8_t *data); +#endif \ No newline at end of file diff --git a/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/second_uart.c b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/second_uart.c new file mode 100644 index 00000000..6bb109c6 --- /dev/null +++ b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/second_uart.c @@ -0,0 +1,116 @@ +#include +#include +#include +#include +#include "driver/gpio.h" +#include "driver/uart.h" +#include "esp_err.h" +#include "esp_event.h" +#include "esp_ieee802154.h" +#include "esp_log.h" +#include "esp_phy_init.h" +#include "esp_timer.h" +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/task.h" +#include "main.h" +#include "nvs.h" +#include "nvs_flash.h" +#include "proto.h" +#include "sdkconfig.h" +#include "soc/uart_struct.h" +//#include "soc/lp_uart_reg.h" +#include "second_uart.h" + +//static const char *TAG = "SECOND_UART"; + +#define BUF_SIZE (1024) +#define RD_BUF_SIZE (BUF_SIZE) +static QueueHandle_t uart0_queue; + +#define MAX_BUFF_POS 8000 +volatile int curr_buff_pos = 0; +volatile int worked_buff_pos = 0; +volatile uint8_t buff_pos[MAX_BUFF_POS + 5]; + +static void uart_event_task(void *pvParameters); +void init_second_uart() { + uart_config_t uart_config = { + .baud_rate = 115200, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + .source_clk = UART_SCLK_DEFAULT, + }; + ESP_ERROR_CHECK(uart_driver_install(1, BUF_SIZE * 2, BUF_SIZE * 2, 20, &uart0_queue, 0)); + ESP_ERROR_CHECK(uart_param_config(1, &uart_config)); + ESP_ERROR_CHECK(uart_set_pin(1, CONFIG_OEPL_HARDWARE_UART_TX, CONFIG_OEPL_HARDWARE_UART_RX, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE)); + + xTaskCreate(uart_event_task, "uart_event_task", 16384, NULL, 12, NULL); +} + +void uart_switch_speed(int baudrate) { + uart_config_t uart_config = { + .baud_rate = baudrate, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + .source_clk = UART_SCLK_DEFAULT, + }; + ESP_ERROR_CHECK(uart_param_config(1, &uart_config)); +} + +void uartTx(uint8_t data) { uart_write_bytes(1, (const char *) &data, 1); } + + +bool getRxCharSecond(uint8_t *newChar) { + if (curr_buff_pos != worked_buff_pos) { + *newChar = buff_pos[worked_buff_pos]; + worked_buff_pos++; + worked_buff_pos %= MAX_BUFF_POS; + return true; + } + return false; +} + +static void uart_event_task(void *pvParameters) { + uart_event_t event; + uint8_t *dtmp = (uint8_t *) malloc(RD_BUF_SIZE); + for (;;) { + if (xQueueReceive(uart0_queue, (void *) &event, (TickType_t) portMAX_DELAY)) { + bzero(dtmp, RD_BUF_SIZE); + switch (event.type) { + case UART_DATA: + uart_read_bytes(1, dtmp, event.size, portMAX_DELAY); + for (int i = 0; i < event.size; i++) { + buff_pos[curr_buff_pos] = dtmp[i]; + curr_buff_pos++; + curr_buff_pos %= MAX_BUFF_POS; + } + break; + default: + // ESP_LOGI(TAG, "uart event type: %d", event.type); + break; + } + } + } + free(dtmp); + dtmp = NULL; + vTaskDelete(NULL); +} + +void uart_printf(const char *format, ...) { + va_list args; + va_start(args, format); + + char buffer[128]; + int len = vsnprintf(buffer, sizeof(buffer), format, args); + + va_end(args); + + if (len > 0) { + uart_write_bytes(1, buffer, len); + } +} \ No newline at end of file diff --git a/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/second_uart.h b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/second_uart.h new file mode 100644 index 00000000..48e5698a --- /dev/null +++ b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/second_uart.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +void init_second_uart(); +void uart_switch_speed(int baudrate); + +void uartTx(uint8_t data); +bool getRxCharSecond(uint8_t *newChar); + +void uart_printf(const char *format, ...); + +#define pr uart_printf + + + + #define CONFIG_OEPL_HARDWARE_UART_TX 24 + #define CONFIG_OEPL_HARDWARE_UART_RX 23 diff --git a/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/utils.c b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/utils.c new file mode 100644 index 00000000..5771ccf5 --- /dev/null +++ b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/utils.c @@ -0,0 +1,36 @@ +#include +#include +#include +#include +#include "driver/gpio.h" +#include "driver/uart.h" +#include "esp_err.h" +#include "esp_event.h" +#include "esp_ieee802154.h" +#include "esp_log.h" +#include "esp_phy_init.h" +#include "esp_timer.h" +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/task.h" +#include "main.h" +#include "proto.h" +#include "sdkconfig.h" +#include "soc/uart_struct.h" +//#include "soc/lp_uart_reg.h" +#include "nvs_flash.h" + +void delay(int ms) { vTaskDelay(pdMS_TO_TICKS(ms)); } + +uint32_t getMillis() { return (uint32_t) (esp_timer_get_time() / 1000); } + +void init_nvs() +{ + // Initialize NVS + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK( ret ); +} \ No newline at end of file diff --git a/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/utils.h b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/utils.h new file mode 100644 index 00000000..7109b160 --- /dev/null +++ b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/main/utils.h @@ -0,0 +1,5 @@ +#pragma once + +void delay(int ms); +uint32_t getMillis(); +void init_nvs(); \ No newline at end of file diff --git a/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/partitions.csv b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/partitions.csv new file mode 100644 index 00000000..40643958 --- /dev/null +++ b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/partitions.csv @@ -0,0 +1,5 @@ +# ESP-IDF Partition Table +# Name, Type, SubType, Offset, Size, Flags +nvs,data,nvs,0x9000,0x6000,, +factory,app,factory,0x10000,1M,, +littlefs,data,spiffs,,3008K,, diff --git a/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/sdkconfig.defaults b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/sdkconfig.defaults new file mode 100644 index 00000000..717f35ea --- /dev/null +++ b/ARM_Tag_FW/OpenEPaperLink_esp32_H2_AP/sdkconfig.defaults @@ -0,0 +1,8 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_IDF_TARGET="esp32h2" +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +CONFIG_ESPTOOLPY_HEADER_FLASHSIZE_UPDATE=y +CONFIG_PARTITION_TABLE_CUSTOM=y diff --git a/ESP32_AP-Flasher/compile.ps1 b/ESP32_AP-Flasher/compile.ps1 new file mode 100644 index 00000000..e571dabb --- /dev/null +++ b/ESP32_AP-Flasher/compile.ps1 @@ -0,0 +1,76 @@ + +#| Function | ESP32H2 Pin | ESP32S3 Pin +#| :--------------------------------: | :-------------------: | :------------------ +#| Activate The BOOT Mode Of ESP32H2 | ESP32H2_IO9 | ESP32S3_IO33 +#| Reset ESP32H2 | ESP32H2_Pin Number 8 | ESP32S3_IO34 +#| Uart | ESP32H2_TX_IO24 | ESP32S3_RX_IO48 +#| Uart | ESP32H2_RX_IO23 | ESP32S3_TX_IO47 + +# cd ESP32_AP-Flasher + +# export PLATFORMIO_BUILD_FLAGS="-D BUILD_VERSION=${{ github.ref_name }} -D SHA=$GITHUB_SHA" + +python gzip_wwwfiles.py + +&(Join-Path $env:USERPROFILE '\.platformio\penv\Scripts\pio') run --environment ESP32_S3_16_8_LILYGO_AP + +&(Join-Path $env:USERPROFILE '\.platformio\penv\Scripts\pio') run --target buildfs --environment ESP32_S3_16_8_LILYGO_AP + +# mkdir ESP32_S3_16_8_LILYGO_AP + +copy "$env:USERPROFILE\.platformio\packages\framework-arduinoespressif32\tools\partitions\boot_app0.bin" ESP32_S3_16_8_LILYGO_AP\boot_app0.bin + +copy .pio\build\ESP32_S3_16_8_LILYGO_AP\firmware.bin ESP32_S3_16_8_LILYGO_AP\firmware.bin + +copy .pio\build\ESP32_S3_16_8_LILYGO_AP\bootloader.bin ESP32_S3_16_8_LILYGO_AP\bootloader.bin + +copy .pio\build\ESP32_S3_16_8_LILYGO_AP\partitions.bin ESP32_S3_16_8_LILYGO_AP\partitions.bin + +copy .pio\build\ESP32_S3_16_8_LILYGO_AP\littlefs.bin ESP32_S3_16_8_LILYGO_AP\littlefs.bin + +copy ESP32_S3_16_8_LILYGO_AP\firmware.bin espbinaries\ESP32_S3_16_8_LILYGO_AP.bin + +copy ESP32_S3_16_8_LILYGO_AP\merged-firmware.bin espbinaries\ESP32_S3_16_8_LILYGO_AP_full.bin + +cd ESP32_S3_16_8_LILYGO_AP + +#python -m esptool --chip esp32-s3 merge_bin -o merged-firmware.bin --flash_mode dio --flash_freq 80m --flash_size 16MB 0x0000 bootloader.bin 0x8000 partitions.bin 0xe000 boot_app0.bin 0x10000 firmware.bin 0x00910000 littlefs.bin + +#python -m esptool -p COM12 -b 460800 --before default_reset --after hard_reset --chip esp32s3 write_flash --flash_mode dio --flash_size detect 0x0000 bootloader.bin 0x8000 partitions.bin 0xe000 boot_app0.bin 0x10000 firmware.bin 0x00910000 littlefs.bin + +cd .. + +#esptool.py v4.7.0 +#Serial port COM12 +#Connecting... +#Chip is ESP32-S3 (QFN56) (revision v0.2) +#Features: WiFi, BLE +#Crystal is 40MHz +#MAC: f4:12:fa:af:5b:9c +#Uploading stub... +#Running stub... +#Stub running... +#Changing baud rate to 460800 +#Changed. +#Configuring flash size... +#Auto-detected Flash size: 16MB +#Flash will be erased from 0x00000000 to 0x00003fff... +#Flash will be erased from 0x00008000 to 0x00008fff... +#Flash will be erased from 0x0000e000 to 0x0000ffff... +#Flash will be erased from 0x00010000 to 0x00203fff... +#Flash will be erased from 0x00910000 to 0x00feffff... +#Compressed 15104 bytes to 10401... +#Wrote 15104 bytes (10401 compressed) at 0x00000000 in 0.3 seconds (effective 375.1 kbit/s)... +#Hash of data verified. +#Compressed 3072 bytes to 146... +#Wrote 3072 bytes (146 compressed) at 0x00008000 in 0.1 seconds (effective 272.1 kbit/s)... +#Hash of data verified. +#Compressed 8192 bytes to 47... +#Wrote 8192 bytes (47 compressed) at 0x0000e000 in 0.1 seconds (effective 447.3 kbit/s)... +#Hash of data verified. +#Compressed 2047040 bytes to 1259376... +#Wrote 2047040 bytes (1259376 compressed) at 0x00010000 in 18.7 seconds (effective 876.9 kbit/s)... +#Hash of data verified. +#Compressed 7208960 bytes to 302805... +#Wrote 7208960 bytes (302805 compressed) at 0x00910000 in 47.0 seconds (effective 1227.2 kbit/s)... +#Hash of data verified. diff --git a/ESP32_AP-Flasher/compileyellow.ps1 b/ESP32_AP-Flasher/compileyellow.ps1 new file mode 100644 index 00000000..20065dab --- /dev/null +++ b/ESP32_AP-Flasher/compileyellow.ps1 @@ -0,0 +1,69 @@ +# cd ESP32_AP-Flasher + +# export PLATFORMIO_BUILD_FLAGS="-D BUILD_VERSION=${{ github.ref_name }} -D SHA=$GITHUB_SHA" + +python gzip_wwwfiles.py + +&(Join-Path $env:USERPROFILE '\.platformio\penv\Scripts\pio') run --environment ESP32_S3_16_8_YELLOW_AP + +&(Join-Path $env:USERPROFILE '\.platformio\penv\Scripts\pio') run --target buildfs --environment ESP32_S3_16_8_YELLOW_AP + +# mkdir ESP32_S3_16_8_YELLOW_AP + +copy "$env:USERPROFILE\.platformio\packages\framework-arduinoespressif32\tools\partitions\boot_app0.bin" ESP32_S3_16_8_YELLOW_AP\boot_app0.bin + +copy .pio\build\ESP32_S3_16_8_YELLOW_AP\firmware.bin ESP32_S3_16_8_YELLOW_AP\firmware.bin + +copy .pio\build\ESP32_S3_16_8_YELLOW_AP\bootloader.bin ESP32_S3_16_8_YELLOW_AP\bootloader.bin + +copy .pio\build\ESP32_S3_16_8_YELLOW_AP\partitions.bin ESP32_S3_16_8_YELLOW_AP\partitions.bin + +copy .pio\build\ESP32_S3_16_8_YELLOW_AP\littlefs.bin ESP32_S3_16_8_YELLOW_AP\littlefs.bin + +copy ESP32_S3_16_8_YELLOW_AP\firmware.bin espbinaries\ESP32_S3_16_8_YELLOW_AP.bin + +copy ESP32_S3_16_8_YELLOW_AP\merged-firmware.bin espbinaries\ESP32_S3_16_8_YELLOW_AP_full.bin + +cd ESP32_S3_16_8_YELLOW_AP + +#python -m esptool --chip esp32-s3 merge_bin -o merged-firmware.bin --flash_mode dio --flash_freq 80m --flash_size 16MB 0x0000 bootloader.bin 0x8000 partitions.bin 0xe000 boot_app0.bin 0x10000 firmware.bin 0x00910000 littlefs.bin + +#python -m esptool -p COM4 -b 460800 --before default_reset --after hard_reset --chip esp32s3 write_flash --flash_mode dio --flash_size detect 0x0000 bootloader.bin 0x8000 partitions.bin 0xe000 boot_app0.bin 0x10000 firmware.bin 0x00910000 littlefs.bin + +cd .. + +#esptool.py v4.7.0 +#Serial port COM4 +#Connecting... +#Chip is ESP32-S3 (QFN56) (revision v0.2) +#Features: WiFi, BLE, Embedded PSRAM 8MB (AP_3v3) +#Crystal is 40MHz +#MAC: dc:da:0c:16:cf:4c +#Uploading stub... +#Running stub... +#Stub running... +#Changing baud rate to 460800 +#Changed. +#Configuring flash size... +#Auto-detected Flash size: 16MB +#Flash will be erased from 0x00000000 to 0x00003fff... +#Flash will be erased from 0x00008000 to 0x00008fff... +#Flash will be erased from 0x0000e000 to 0x0000ffff... +#Flash will be erased from 0x00010000 to 0x001dbfff... +#Flash will be erased from 0x00910000 to 0x00feffff... +#Compressed 15104 bytes to 10401... +#Wrote 15104 bytes (10401 compressed) at 0x00000000 in 0.4 seconds (effective 277.3 kbit/s)... +#Hash of data verified. +#Compressed 3072 bytes to 146... +#Wrote 3072 bytes (146 compressed) at 0x00008000 in 0.1 seconds (effective 425.0 kbit/s)... +#Hash of data verified. +#Compressed 8192 bytes to 47... +#Wrote 8192 bytes (47 compressed) at 0x0000e000 in 0.1 seconds (effective 632.5 kbit/s)... +#Hash of data verified. +#Compressed 1881424 bytes to 1196500... +#Wrote 1881424 bytes (1196500 compressed) at 0x00010000 in 27.6 seconds (effective 544.8 kbit/s)... +#Hash of data verified. +#Compressed 7208960 bytes to 302804... +#Wrote 7208960 bytes (302804 compressed) at 0x00910000 in 35.0 seconds (effective 1648.8 kbit/s)... +#Hash of data verified. + diff --git a/ESP32_AP-Flasher/data/tagtypes/E2.json b/ESP32_AP-Flasher/data/tagtypes/E2.json new file mode 100644 index 00000000..591ce03b --- /dev/null +++ b/ESP32_AP-Flasher/data/tagtypes/E2.json @@ -0,0 +1,64 @@ +{ + "version": 1, + "name": "LILYGO TPANEL 4\"", + "width": 480, + "height": 480, + "rotatebuffer": 0, + "bpp": 16, + "colortable": { + "white": [ 255, 255, 255 ], + "black": [ 0, 0, 0 ], + "red": [ 255, 0, 0 ] + }, + "shortlut": 0, + "options": [ ], + "contentids": [ 22, 1, 2, 3, 4, 8, 7, 19, 10, 11, 21 ], + "template": { + "21": [ + { "box": [ 0, 0, 480, 480, 1 ] }, + { "text": [ 10, 15, "OpenEpaperLink AP", "calibrib30", 2, 0, 0, 1 ] }, + { "text": [ 10, 70, "IP address:", "bahnschrift30", "#888888", 0, 0, 1 ] }, + { "text": [ 180, 70, "{ap_ip}", "bahnschrift30", 0, 0, 0, 1 ] }, + { "text": [ 10, 110, "Channel:", "bahnschrift30", "#888888", 0, 0, 1 ] }, + { "text": [ 180, 110, "{ap_ch}", "bahnschrift30", 0, 0, 0, "1" ] }, + { "text": [ 10, 150, "Tag count:", "bahnschrift30", "#888888", 0, 0, 1 ] }, + { "text": [ 180, 150, "{ap_tagcount}", "bahnschrift30", 0, 0, 0, "1" ] } + ], + "1": { + "weekday": [ 240, 30, "Signika-SB.ttf", 90 ], + "month": [ 240, 330, "Signika-SB.ttf", 90 ], + "day": [ 240, 80, "Signika-SB.ttf", 250 ] + }, + "4": { + "location": [ 20, 20, "fonts/calibrib30" ], + "wind": [ 90, 95, "fonts/calibrib50" ], + "temp": [ 20, 200, "fonts/calibrib100" ], + "icon": [ 400, 30, 150, 2 ], + "dir": [ 40, 70, 80 ], + "umbrella": [ 325, 250, 150 ] + }, + "2": { + "fonts": [ "Signika-SB.ttf", 150, 150, 110, 80, 60, 50 ], + "xy": [ 240, 240 ] + }, + "8": { + "location": [ 10, 20, "fonts/calibrib50" ], + "column": [ 6, 80 ], + "day": [ 40, 100, "fonts/bahnschrift30", 144, 270 ], + "rain": [ 40, 320 ], + "icon": [ 40, 180, 50 ], + "wind": [ 17, 120 ], + "line": [ 100, 340 ] + }, + "10": { + "title": [ 240, 10, "fonts/bahnschrift20" ], + "pos": [ 240, 35 ] + }, + "11": { + "rotate": 0, + "mode": 1, + "days": 4, + "gridparam": [ 5, 17, 20, "calibrib16.vlw", "tahoma9.vlw", 14 ] + } + } +} diff --git a/ESP32_AP-Flasher/data/www/content_cards.json.gz b/ESP32_AP-Flasher/data/www/content_cards.json.gz index 050d82914d4380ade128cf93ecfc136673b62cce..4bb80d90b066b2d2d9b14d243fc55aa38e70f25c 100644 GIT binary patch literal 4681 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 LF{<_|Yg7OL@3#X4 literal 4677 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 HbyNTV>v+Y;mQ(ibEZg-q~dsi|Mwk9$@+9U zY#lm3#4+#t?ss=&#FbLi5*E8T%?l0>C{{?Mj1e{m zsaZ%R&Q7PPQk;!csBnpLcn}#I5B=WY`eO8c(!aUrUH2y!_kZJ|IRM_%eN6Ww`@4S! zT;Ddh2E)ty5m3H6Jit#YE)=RU^seu<E9)1SD*o8e;y5Qe5nK!kIXi1p*Qzb z@@R>bFX70|<#sL~xt&)rzep7~hC80mSwu;+)aB0OC^AB3$jy-AA%RsYSY{r28y5(yuhU_awmABd# z3uZR^`q2yq|0~`#6u&kXtK%JFB+$#?fOEPxBa71w^oC-tkfR*(LV_3W&|8lMUbb=~hCE;cq<(VqN zAaWE5PJbzm$GpcOMG0ss8IXqh9+UY(q1R;!lpVs%C~sSY#(Wjj%`v>bhUAu076}ZC zD$APNiOJ(GQTmffz_8pv*l%QwTp1sQ@fmx2%R- z)KK;|uqBEZ=atzLDx5lA_1b?ysoSyldDyiuQu<-EaP0v) ze^>V09{QOreL=N$GXJ)DR;sUq9nZFSA%D!UiWV0vzU5Nq-`=*}RM0MBfW|Ibg=*oI z%qy`edt#TUldj-etM{=3?P)|B>p)<+f`5!7>yTuowW%iWj8I&F!m{V`{*>~m>jlmI z1}RIjJ2#*M4YHqZS%1!xVae@iXFJ`7&ka8967ZEgvl(i0BwbM+#&_)jcPLSc;(wW( ztWpIUYk*9!T5xz=bZ&>Eai?1)PIiY zqSBEzx&72dj=-@~?{X{z7xMTx_*}PVrUpbL>rh~mMz|KNnf4LKTK`;N%p9+!#ZVMN z;$Q%3YhDw59YcVW0rt$lE#B5V)qibkQdWCjmLHAPJK$_>e1@yn3D75lhskXiE%j5~ zsXY7u&zi|7nrM-qG_=ljUtkvT7~kK0xa0{WS6_jwRZ2g%UeKU%JvoRWeil*Fs`gBA6;d5fH~s({%StVm2#5J zE!SBnwUTEiCn8+?b6CjfG?iFHwohTdCnrPb;{I*8!UDF&vi>uH6^F$M6f+bAOI_hc zmBF3nQMG;*(AHCsg>Qgpai-_xEXxLb`5GwOGt}(IPk#^4hX;QFTd3izS`q*N#&)!z delta 1400 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 N{|mxz^Idyf008L}L4W`N literal 6354 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 literal 14693 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 Jz1+cL002e!m~{XE literal 5466 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 #include +#ifdef HAS_LILYGO_TPANEL + +#define LV_ATTRIBUTE_TICK_INC IRAM_ATTR +#define TOUCH_MODULES_CST_MUTUAL + +// SD +#define SD_CS 38 +#define SD_SCLK 36 +#define SD_MOSI 35 +#define SD_MISO 37 + +// IIC +#define IIC_SDA 17 +#define IIC_SCL 18 + +// ESP32H2 +#define ESP32H2_TX 48 +#define ESP32H2_RX 47 + +// #define T_Panel_V1_0_RS485 + #define T_Panel_V1_2_RS485 +// #define T_Panel_V1_2_CAN + +#if defined T_Panel_V1_0_RS485 +#define RS485_TX 15 +#define RS485_RX 16 +#endif + +#if defined T_Panel_V1_2_RS485 +#define RS485_TX 16 +#define RS485_RX 15 +#endif + +#if defined T_Panel_V1_2_CAN +#define CAN_TX 16 +#define CAN_RX 15 +#endif + +// YDP395BT001-V2 +#define LCD_WIDTH 480 +#define LCD_HEIGHT 480 +#define LCD_VSYNC 40 +#define LCD_HSYNC 39 +#define LCD_PCLK 41 +#define LCD_B0 1 +#define LCD_B1 2 +#define LCD_B2 3 +#define LCD_B3 4 +#define LCD_B4 5 +#define LCD_G0 6 +#define LCD_G1 7 +#define LCD_G2 8 +#define LCD_G3 9 +#define LCD_G4 10 +#define LCD_G5 11 +#define LCD_R0 12 +#define LCD_R1 13 +#define LCD_R2 42 +#define LCD_R3 46 +#define LCD_R4 45 +#define LCD_BL 14 + +// CST3240 +#define CST3240_ADDRESS 0x5A +#define TOUCH_SDA 17 +#define TOUCH_SCL 18 +#define TOUCH_INT 21 +#define TOUCH_RST 4 + +// XL95x5 +#define XL95X5_CS 17 +#define XL95X5_SCLK 15 +#define XL95X5_MOSI 16 +#define XL95X5_TOUCH_RST 4 +#define XL95X5_RS485_CON 7 +#define XL95X5_LCD_RST 5 +#define XL95X5_ESP32H2_IO12 1 +#define XL95X5_ESP32H2_IO4 2 +#define XL95X5_ESP32H2_IO5 3 + +// ESP32H2 +#define ESP32H2_EN 34 +#define ESP32H2_BOOT 33 + +#include "Arduino_GFX_Library.h" + +extern Arduino_RGB_Display *gfx; + +#endif + #ifdef HAS_TFT extern TFT_eSPI tft2; @@ -10,4 +102,4 @@ extern bool tftOverride; void TFTLog(String text); void sendAvail(uint8_t wakeupReason); -#endif +#endif \ No newline at end of file diff --git a/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/Arduino_DataBus.cpp b/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/Arduino_DataBus.cpp new file mode 100644 index 00000000..5460f872 --- /dev/null +++ b/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/Arduino_DataBus.cpp @@ -0,0 +1,167 @@ +/* + * start rewrite from: + * https://github.com/adafruit/Adafruit-GFX-Library.git + */ +#include "Arduino_DataBus.h" + +Arduino_DataBus::Arduino_DataBus() {} + +void Arduino_DataBus::writeC8D8(uint8_t c, uint8_t d) +{ + writeCommand(c); + write(d); +} + +void Arduino_DataBus::writeC8D16(uint8_t c, uint16_t d) +{ + writeCommand(c); + write16(d); +} + +void Arduino_DataBus::writeC16D16(uint16_t c, uint16_t d) +{ + writeCommand16(c); + write16(d); +} + +void Arduino_DataBus::writeC8D16D16(uint8_t c, uint16_t d1, uint16_t d2) +{ + writeCommand(c); + write16(d1); + write16(d2); +} + +void Arduino_DataBus::writeC8D16D16Split(uint8_t c, uint16_t d1, uint16_t d2) +{ + writeCommand(c); + _data16.value = d1; + write(_data16.msb); + write(_data16.lsb); + _data16.value = d2; + write(_data16.msb); + write(_data16.lsb); +} + +void Arduino_DataBus::sendCommand(uint8_t c) +{ + beginWrite(); + writeCommand(c); + endWrite(); +} + +void Arduino_DataBus::sendCommand16(uint16_t c) +{ + beginWrite(); + writeCommand16(c); + endWrite(); +} + +void Arduino_DataBus::sendData(uint8_t d) +{ + beginWrite(); + write(d); + endWrite(); +} + +void Arduino_DataBus::sendData16(uint16_t d) +{ + beginWrite(); + write16(d); + endWrite(); +} + +void Arduino_DataBus::batchOperation(const uint8_t *operations, size_t len) +{ + for (size_t i = 0; i < len; ++i) + { + uint8_t l = 0; + switch (operations[i]) + { + case BEGIN_WRITE: + beginWrite(); + break; + case WRITE_C8_D16: + writeCommand(operations[++i]); + l = 2; + break; + case WRITE_C8_D8: + writeC8D8(operations[++i], operations[++i]); + break; + case WRITE_COMMAND_8: + writeCommand(operations[++i]); + break; + case WRITE_C16_D16: + + break; + case WRITE_COMMAND_16: + + break; + case WRITE_DATA_8: + l = 1; + break; + case WRITE_DATA_16: + l = 2; + break; + case WRITE_BYTES: + l = operations[++i]; + break; + case END_WRITE: + endWrite(); + break; + case DELAY: + delay(operations[++i]); + break; + default: + printf("Unknown operation id at %d: %d", i, operations[i]); + break; + } + while (l--) + { + write(operations[++i]); + } + } +} + +#if !defined(LITTLE_FOOT_PRINT) +void Arduino_DataBus::writePattern(uint8_t *data, uint8_t len, uint32_t repeat) +{ + while (repeat--) + { + writeBytes(data, len); + } +} + +void Arduino_DataBus::writeIndexedPixels(uint8_t *data, uint16_t *idx, uint32_t len) +{ + while (len--) + { + write16(idx[*(data++)]); + } +} + +void Arduino_DataBus::writeIndexedPixelsDouble(uint8_t *data, uint16_t *idx, uint32_t len) +{ + uint8_t *d = data; + while (len--) + { + _data16.value = idx[*(d++)]; + write(_data16.msb); + write(_data16.lsb); + write(_data16.msb); + write(_data16.lsb); + } +} +#endif // !defined(LITTLE_FOOT_PRINT) + +void Arduino_DataBus::pinMode(uint8_t pin, uint8_t mode) +{ +} + +void Arduino_DataBus::digitalWrite(uint8_t pin, uint8_t val) +{ +} + +int Arduino_DataBus::digitalRead(uint8_t pin) +{ +} + diff --git a/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/Arduino_DataBus.h b/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/Arduino_DataBus.h new file mode 100644 index 00000000..7038c2c8 --- /dev/null +++ b/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/Arduino_DataBus.h @@ -0,0 +1,293 @@ +/* + * start rewrite from: + * https://github.com/adafruit/Adafruit-GFX-Library.git + */ +#ifndef _ARDUINO_DATABUS_H_ +#define _ARDUINO_DATABUS_H_ + +#include + +#define GFX_SKIP_OUTPUT_BEGIN -2 +#define GFX_NOT_DEFINED -1 +#define GFX_STR_HELPER(x) #x +#define GFX_STR(x) GFX_STR_HELPER(x) + +#if defined(__AVR__) +#define LITTLE_FOOT_PRINT // reduce program size for limited flash MCU +#define USE_FAST_PINIO ///< Use direct PORT register access +typedef uint8_t ARDUINOGFX_PORT_t; +#elif defined(ARDUINO_ARCH_NRF52840) +#define USE_FAST_PINIO ///< Use direct PORT register access +#define HAS_PORT_SET_CLR ///< PORTs have set & clear registers +typedef uint32_t ARDUINOGFX_PORT_t; +#elif defined(TARGET_RP2040) +#define USE_FAST_PINIO ///< Use direct PORT register access +#define HAS_PORT_SET_CLR ///< PORTs have set & clear registers +typedef uint32_t ARDUINOGFX_PORT_t; +#elif defined(ESP32) +#define USE_FAST_PINIO ///< Use direct PORT register access +#define HAS_PORT_SET_CLR ///< PORTs have set & clear registers +typedef uint32_t ARDUINOGFX_PORT_t; +#elif defined(ESP8266) +#define ESP8266SAFEBATCHBITSIZE (2048 * 8 * 9) +#define USE_FAST_PINIO ///< Use direct PORT register access +typedef uint32_t ARDUINOGFX_PORT_t; +#elif defined(ARDUINO_ARCH_STM32) +#define USE_FAST_PINIO ///< Use direct PORT register access +typedef uint32_t ARDUINOGFX_PORT_t; +#elif defined(__arm__) +#if defined(ARDUINO_ARCH_SAMD) +// Adafruit M0, M4 +#define USE_FAST_PINIO ///< Use direct PORT register access +#define HAS_PORT_SET_CLR ///< PORTs have set & clear registers +typedef uint32_t ARDUINOGFX_PORT_t; +#elif defined(CONFIG_ARCH_CHIP_CXD56XX) // Sony Spresense +#define USE_FAST_PINIO ///< Use direct PORT register access +typedef uint8_t ARDUINOGFX_PORT_t; +#elif defined(RTL8722DM) +#define USE_FAST_PINIO ///< Use direct PORT register access +typedef uint32_t ARDUINOGFX_PORT_t; +#elif defined(CORE_TEENSY) +#define USE_FAST_PINIO ///< Use direct PORT register access +#define HAS_PORT_SET_CLR ///< PORTs have set & clear registers +#if defined(__IMXRT1052__) || defined(__IMXRT1062__) +// PJRC Teensy 4.x +typedef uint32_t ARDUINOGFX_PORT_t; +#else +// PJRC Teensy 3.x +typedef uint8_t ARDUINOGFX_PORT_t; +#endif +#else +// Arduino Due? +// USE_FAST_PINIO not available here (yet)...Due has a totally different +// GPIO register set and will require some changes elsewhere (e.g. in +// constructors especially). +#endif +#else // !ARM +// Unknow architecture, USE_FAST_PINIO is not available here (yet) +// but don't worry about it too much...the digitalWrite() implementation +// on these platforms is reasonably efficient and already RAM-resident, +// only gotcha then is no parallel connection support for now. +#endif // !ARM + +#ifdef USE_FAST_PINIO +typedef volatile ARDUINOGFX_PORT_t *PORTreg_t; +#endif + +#if defined(ARDUINO_ARCH_ARC32) || defined(ARDUINO_MAXIM) +#define SPI_DEFAULT_FREQ 16000000 +// Teensy 3.0, 3.1/3.2, 3.5, 3.6 +#elif defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) +#define SPI_DEFAULT_FREQ 40000000 +// Teensy 4.x +#elif defined(__IMXRT1052__) || defined(__IMXRT1062__) +#define SPI_DEFAULT_FREQ 40000000 +#elif defined(__AVR__) || defined(TEENSYDUINO) +#define SPI_DEFAULT_FREQ 8000000 +#elif defined(ARDUINO_ARCH_NRF52840) +#define SPI_DEFAULT_FREQ 8000000 +#elif defined(ESP8266) || defined(ESP32) +#define SPI_DEFAULT_FREQ 40000000 +#elif defined(RTL8722DM) +#define SPI_DEFAULT_FREQ 20000000 +#elif defined(RASPI) +#define SPI_DEFAULT_FREQ 80000000 +#elif defined(ARDUINO_ARCH_STM32F1) +#define SPI_DEFAULT_FREQ 36000000 +#elif defined(ARDUINO_BLACKPILL_F411CE) +#define SPI_DEFAULT_FREQ 50000000 +#elif defined(F_CPU) +#define SPI_DEFAULT_FREQ (F_CPU / 4) +#else +#define SPI_DEFAULT_FREQ 24000000 ///< Default SPI data clock frequency +#endif + +#ifndef UNUSED +#define UNUSED(x) (void)(x) +#endif +#define ATTR_UNUSED __attribute__((unused)) + +#define MSB_16(val) (((val)&0xFF00) >> 8) | (((val)&0xFF) << 8) +#define MSB_16_SET(var, val) \ + { \ + (var) = MSB_16(val); \ + } +#define MSB_32_SET(var, val) \ + { \ + uint8_t *v = (uint8_t *)&(val); \ + (var) = v[3] | (v[2] << 8) | (v[1] << 16) | (v[0] << 24); \ + } +#define MSB_32_16_16_SET(var, v1, v2) \ + { \ + (var) = (((uint32_t)v2 & 0xff00) << 8) | (((uint32_t)v2 & 0xff) << 24) | ((v1 & 0xff00) >> 8) | ((v1 & 0xff) << 8); \ + } +#define MSB_32_8_ARRAY_SET(var, a) \ + { \ + (var) = ((uint32_t)a[0] << 8 | a[1] | a[2] << 24 | a[3] << 16); \ + } + +#if defined(ESP32) +#define INLINE __attribute__((always_inline)) inline +#else +#define INLINE inline +#endif + +#if defined(ESP32) && (CONFIG_IDF_TARGET_ESP32S3) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct esp_lcd_i80_bus_t esp_lcd_i80_bus_t; +typedef struct lcd_panel_io_i80_t lcd_panel_io_i80_t; +typedef struct lcd_i80_trans_descriptor_t lcd_i80_trans_descriptor_t; + +struct esp_lcd_i80_bus_t +{ + int bus_id; // Bus ID, index from 0 + portMUX_TYPE spinlock; // spinlock used to protect i80 bus members(hal, device_list, cur_trans) + lcd_hal_context_t hal; // Hal object + size_t bus_width; // Number of data lines + intr_handle_t intr; // LCD peripheral interrupt handle + esp_pm_lock_handle_t pm_lock; // Power management lock + size_t num_dma_nodes; // Number of DMA descriptors + uint8_t *format_buffer; // The driver allocates an internal buffer for DMA to do data format transformer + size_t resolution_hz; // LCD_CLK resolution, determined by selected clock source + gdma_channel_handle_t dma_chan; // DMA channel handle + size_t psram_trans_align; // DMA transfer alignment for data allocated from PSRAM + size_t sram_trans_align; // DMA transfer alignment for data allocated from SRAM + lcd_i80_trans_descriptor_t *cur_trans; // Current transaction + lcd_panel_io_i80_t *cur_device; // Current working device + LIST_HEAD(i80_device_list, lcd_panel_io_i80_t) + device_list; // Head of i80 device list + struct + { + unsigned int exclusive : 1; // Indicate whether the I80 bus is owned by one device (whose CS GPIO is not assigned) exclusively + } flags; + dma_descriptor_t dma_nodes[]; // DMA descriptor pool, the descriptors are shared by all i80 devices +}; + +struct lcd_i80_trans_descriptor_t +{ + lcd_panel_io_i80_t *i80_device; // i80 device issuing this transaction + int cmd_value; // Command value + uint32_t cmd_cycles; // Command cycles + const void *data; // Data buffer + uint32_t data_length; // Data buffer size + void *user_ctx; // private data used by trans_done_cb + esp_lcd_panel_io_color_trans_done_cb_t trans_done_cb; // transaction done callback +}; + +struct lcd_panel_io_i80_t +{ + esp_lcd_panel_io_t base; // Base class of generic lcd panel io + esp_lcd_i80_bus_t *bus; // Which bus the device is attached to + int cs_gpio_num; // GPIO used for CS line + unsigned int pclk_hz; // PCLK clock frequency + size_t clock_prescale; // Prescaler coefficient, determined by user's configured PCLK frequency + QueueHandle_t trans_queue; // Transaction queue, transactions in this queue are pending for scheduler to dispatch + QueueHandle_t done_queue; // Transaction done queue, transactions in this queue are finished but not recycled by the caller + size_t queue_size; // Size of transaction queue + size_t num_trans_inflight; // Number of transactions that are undergoing (the descriptor not recycled yet) + int lcd_cmd_bits; // Bit width of LCD command + int lcd_param_bits; // Bit width of LCD parameter + void *user_ctx; // private data used when transfer color data + esp_lcd_panel_io_color_trans_done_cb_t on_color_trans_done; // color data trans done callback + LIST_ENTRY(lcd_panel_io_i80_t) + device_list_entry; // Entry of i80 device list + struct + { + unsigned int dc_idle_level : 1; // Level of DC line in IDLE phase + unsigned int dc_cmd_level : 1; // Level of DC line in CMD phase + unsigned int dc_dummy_level : 1; // Level of DC line in DUMMY phase + unsigned int dc_data_level : 1; // Level of DC line in DATA phase + } dc_levels; + struct + { + unsigned int cs_active_high : 1; // Whether the CS line is active on high level + unsigned int reverse_color_bits : 1; // Reverse the data bits, D[N:0] -> D[0:N] + unsigned int swap_color_bytes : 1; // Swap adjacent two data bytes before sending out + unsigned int pclk_active_neg : 1; // The display will write data lines when there's a falling edge on WR line + unsigned int pclk_idle_low : 1; // The WR line keeps at low level in IDLE phase + } flags; + lcd_i80_trans_descriptor_t trans_pool[]; // Transaction pool +}; +#endif // #if defined(ESP32) && (CONFIG_IDF_TARGET_ESP32S3) + +typedef enum +{ + BEGIN_WRITE, + WRITE_COMMAND_8, + WRITE_COMMAND_16, + WRITE_DATA_8, + WRITE_DATA_16, + WRITE_BYTES, + WRITE_C8_D8, + WRITE_C8_D16, + WRITE_C16_D16, + END_WRITE, + DELAY, +} spi_operation_type_t; + +union +{ + uint16_t value; + struct + { + uint8_t lsb; + uint8_t msb; + }; +} _data16; + +class Arduino_DataBus +{ +public: + Arduino_DataBus(); + + void unused() { UNUSED(_data16); } // avoid compiler warning + + virtual bool begin(int32_t speed = SPI_DEFAULT_FREQ, int8_t dataMode = GFX_NOT_DEFINED) = 0; + virtual void beginWrite() = 0; + virtual void endWrite() = 0; + virtual void writeCommand(uint8_t c) = 0; + virtual void writeCommand16(uint16_t c) = 0; + virtual void write(uint8_t) = 0; + virtual void write16(uint16_t) = 0; + virtual void writeC8D8(uint8_t c, uint8_t d); + virtual void writeC16D16(uint16_t c, uint16_t d); + virtual void writeC8D16(uint8_t c, uint16_t d); + virtual void writeC8D16D16(uint8_t c, uint16_t d1, uint16_t d2); + virtual void writeC8D16D16Split(uint8_t c, uint16_t d1, uint16_t d2); + virtual void writeRepeat(uint16_t p, uint32_t len) = 0; + virtual void writePixels(uint16_t *data, uint32_t len) = 0; + + void sendCommand(uint8_t c); + void sendCommand16(uint16_t c); + void sendData(uint8_t d); + void sendData16(uint16_t d); + + void batchOperation(const uint8_t *operations, size_t len); + +#if !defined(LITTLE_FOOT_PRINT) + virtual void writeBytes(uint8_t *data, uint32_t len) = 0; + virtual void writePattern(uint8_t *data, uint8_t len, uint32_t repeat); + virtual void writeIndexedPixels(uint8_t *data, uint16_t *idx, uint32_t len); + virtual void writeIndexedPixelsDouble(uint8_t *data, uint16_t *idx, uint32_t len); +#endif // !defined(LITTLE_FOOT_PRINT) + + virtual void pinMode(uint8_t pin, uint8_t mode); + virtual void digitalWrite(uint8_t pin, uint8_t val); + virtual int digitalRead(uint8_t pin); + +protected: + int32_t _speed; + int8_t _dataMode; +}; + +#endif // _ARDUINO_DATABUS_H_ diff --git a/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/Arduino_G.cpp b/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/Arduino_G.cpp new file mode 100644 index 00000000..8d9dfd36 --- /dev/null +++ b/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/Arduino_G.cpp @@ -0,0 +1,279 @@ +#if !defined(LITTLE_FOOT_PRINT) + +#include "Arduino_G.h" + +/**************************************************************************/ +/*! + @brief Instatiate a GFX context for graphics! Can only be done by a superclass + @param w Display width, in pixels + @param h Display height, in pixels +*/ +/**************************************************************************/ +Arduino_G::Arduino_G(int16_t w, int16_t h) : WIDTH(w), HEIGHT(h) +{ +} + +// utility functions +bool gfx_draw_bitmap_to_framebuffer( + uint16_t *from_bitmap, int16_t bitmap_w, int16_t bitmap_h, + uint16_t *framebuffer, int16_t x, int16_t y, int16_t framebuffer_w, int16_t framebuffer_h) +{ + int16_t max_X = framebuffer_w - 1; + int16_t max_Y = framebuffer_h - 1; + if ( + ((x + bitmap_w - 1) < 0) || // Outside left + ((y + bitmap_h - 1) < 0) || // Outside top + (x > max_X) || // Outside right + (y > max_Y) // Outside bottom + ) + { + return false; + } + else + { + int16_t xskip = 0; + if ((y + bitmap_h - 1) > max_Y) + { + bitmap_h -= (y + bitmap_h - 1) - max_Y; + } + if (y < 0) + { + from_bitmap -= y * bitmap_w; + bitmap_h += y; + y = 0; + } + if ((x + bitmap_w - 1) > max_X) + { + xskip = (x + bitmap_w - 1) - max_X; + bitmap_w -= xskip; + } + if (x < 0) + { + from_bitmap -= x; + xskip -= x; + bitmap_w += x; + x = 0; + } + + uint16_t *row = framebuffer; + row += y * framebuffer_w; // shift framebuffer to y offset + row += x; // shift framebuffer to x offset + if (((framebuffer_w & 1) == 0) && ((xskip & 1) == 0) && ((bitmap_w & 1) == 0)) + { + uint32_t *row2 = (uint32_t *)row; + uint32_t *from_bitmap2 = (uint32_t *)from_bitmap; + int16_t framebuffer_w2 = framebuffer_w >> 1; + int16_t xskip2 = xskip >> 1; + int16_t w2 = bitmap_w >> 1; + + int16_t j = bitmap_h; + while (j--) + { + for (int16_t i = 0; i < w2; ++i) + { + row2[i] = *from_bitmap2++; + } + from_bitmap2 += xskip2; + row2 += framebuffer_w2; + } + } + else + { + int16_t j = bitmap_h; + while (j--) + { + for (int i = 0; i < bitmap_w; ++i) + { + row[i] = *from_bitmap++; + } + from_bitmap += xskip; + row += framebuffer_w; + } + } + return true; + } +} + +bool gfx_draw_bitmap_to_framebuffer_rotate_1( + uint16_t *from_bitmap, int16_t bitmap_w, int16_t bitmap_h, + uint16_t *framebuffer, int16_t x, int16_t y, int16_t framebuffer_w, int16_t framebuffer_h) +{ + int16_t max_X = framebuffer_w - 1; + int16_t max_Y = framebuffer_h - 1; + if ( + ((x + bitmap_w - 1) < 0) || // Outside left + ((y + bitmap_h - 1) < 0) || // Outside top + (x > max_X) || // Outside right + (y > max_Y) // Outside bottom + ) + { + return false; + } + else + { + int16_t xskip = 0; + if ((y + bitmap_h - 1) > max_Y) + { + bitmap_h -= (y + bitmap_h - 1) - max_Y; + } + if (y < 0) + { + from_bitmap -= y * bitmap_w; + bitmap_h += y; + y = 0; + } + if ((x + bitmap_w - 1) > max_X) + { + xskip = (x + bitmap_w - 1) - max_X; + bitmap_w -= xskip; + } + if (x < 0) + { + from_bitmap -= x; + xskip -= x; + bitmap_w += x; + x = 0; + } + + uint16_t *p; + int16_t i; + for (int16_t j = 0; j < bitmap_h; j++) + { + p = framebuffer; + p += (x * framebuffer_h); // shift framebuffer to y offset + p += (framebuffer_h - y - j); // shift framebuffer to x offset + + i = bitmap_w; + while (i--) + { + *p = *from_bitmap++; + p += framebuffer_h; + } + from_bitmap += xskip; + } + return true; + } +} + +bool gfx_draw_bitmap_to_framebuffer_rotate_2( + uint16_t *from_bitmap, int16_t bitmap_w, int16_t bitmap_h, + uint16_t *framebuffer, int16_t x, int16_t y, int16_t framebuffer_w, int16_t framebuffer_h) +{ + int16_t max_X = framebuffer_w - 1; + int16_t max_Y = framebuffer_h - 1; + if ( + ((x + bitmap_w - 1) < 0) || // Outside left + ((y + bitmap_h - 1) < 0) || // Outside top + (x > max_X) || // Outside right + (y > max_Y) // Outside bottom + ) + { + return false; + } + else + { + int16_t xskip = 0; + if ((y + bitmap_h - 1) > max_Y) + { + bitmap_h -= (y + bitmap_h - 1) - max_Y; + } + if (y < 0) + { + from_bitmap -= y * bitmap_w; + bitmap_h += y; + y = 0; + } + if ((x + bitmap_w - 1) > max_X) + { + xskip = (x + bitmap_w - 1) - max_X; + bitmap_w -= xskip; + } + if (x < 0) + { + from_bitmap -= x; + xskip -= x; + bitmap_w += x; + x = 0; + } + + uint16_t *row = framebuffer; + row += (max_Y - y) * framebuffer_w; // shift framebuffer to y offset + row += framebuffer_w - x - bitmap_w; // shift framebuffer to x offset + int16_t i; + int16_t j = bitmap_h; + while (j--) + { + i = bitmap_w; + while (i--) + { + row[i] = *from_bitmap++; + } + from_bitmap += xskip; + row -= framebuffer_w; + } + return true; + } +} + +bool gfx_draw_bitmap_to_framebuffer_rotate_3( + uint16_t *from_bitmap, int16_t bitmap_w, int16_t bitmap_h, + uint16_t *framebuffer, int16_t x, int16_t y, int16_t framebuffer_w, int16_t framebuffer_h) +{ + int16_t max_X = framebuffer_w - 1; + int16_t max_Y = framebuffer_h - 1; + if ( + ((x + bitmap_w - 1) < 0) || // Outside left + ((y + bitmap_h - 1) < 0) || // Outside top + (x > max_X) || // Outside right + (y > max_Y) // Outside bottom + ) + { + return false; + } + else + { + int16_t xskip = 0; + if ((y + bitmap_h - 1) > max_Y) + { + bitmap_h -= (y + bitmap_h - 1) - max_Y; + } + if (y < 0) + { + from_bitmap -= y * bitmap_w; + bitmap_h += y; + y = 0; + } + if ((x + bitmap_w - 1) > max_X) + { + xskip = (x + bitmap_w - 1) - max_X; + bitmap_w -= xskip; + } + if (x < 0) + { + from_bitmap -= x; + xskip -= x; + bitmap_w += x; + x = 0; + } + + uint16_t *p; + int16_t i; + for (int16_t j = 0; j < bitmap_h; j++) + { + p = framebuffer; + p += ((max_X - x) * framebuffer_h); // shift framebuffer to y offset + p += y + j; // shift framebuffer to x offset + + i = bitmap_w; + while (i--) + { + *p = *from_bitmap++; + p -= framebuffer_h; + } + from_bitmap += xskip; + } + return true; + } +} + +#endif // !defined(LITTLE_FOOT_PRINT) diff --git a/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/Arduino_G.h b/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/Arduino_G.h new file mode 100644 index 00000000..81a89d44 --- /dev/null +++ b/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/Arduino_G.h @@ -0,0 +1,50 @@ +#if !defined(LITTLE_FOOT_PRINT) + +#ifndef _ARDUINO_G_H_ +#define _ARDUINO_G_H_ + +#include + +#include "Arduino_DataBus.h" + +/// A generic graphics superclass that can handle all sorts of drawing. At a minimum you can subclass and provide drawPixel(). At a maximum you can do a ton of overriding to optimize. Used for any/all Adafruit displays! +class Arduino_G +{ +public: + Arduino_G(int16_t w, int16_t h); // Constructor + + // This MUST be defined by the subclass: + virtual bool begin(int32_t speed = GFX_NOT_DEFINED) = 0; + + virtual void drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint16_t bg) = 0; + virtual void drawIndexedBitmap(int16_t x, int16_t y, uint8_t *bitmap, uint16_t *color_index, int16_t w, int16_t h, int16_t x_skip = 0) = 0; + virtual void draw3bitRGBBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h) = 0; + virtual void draw16bitRGBBitmap(int16_t x, int16_t y, uint16_t *bitmap, int16_t w, int16_t h) = 0; + virtual void draw24bitRGBBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h) = 0; + +protected: + int16_t + WIDTH, ///< This is the 'raw' display width - never changes + HEIGHT; ///< This is the 'raw' display height - never changes +}; + +#endif // _ARDUINO_G_H_ + +// utility functions +bool gfx_draw_bitmap_to_framebuffer( + uint16_t *from_bitmap, int16_t bitmap_w, int16_t bitmap_h, + uint16_t *framebuffer, int16_t x, int16_t y, int16_t framebuffer_w, int16_t framebuffer_h); + +bool gfx_draw_bitmap_to_framebuffer_rotate_1( + uint16_t *from_bitmap, int16_t bitmap_w, int16_t bitmap_h, + uint16_t *framebuffer, int16_t x, int16_t y, int16_t framebuffer_w, int16_t framebuffer_h); + +bool gfx_draw_bitmap_to_framebuffer_rotate_2( + uint16_t *from_bitmap, int16_t bitmap_w, int16_t bitmap_h, + uint16_t *framebuffer, int16_t x, int16_t y, int16_t framebuffer_w, int16_t framebuffer_h); + +bool gfx_draw_bitmap_to_framebuffer_rotate_3( + uint16_t *from_bitmap, int16_t bitmap_w, int16_t bitmap_h, + uint16_t *framebuffer, int16_t x, int16_t y, int16_t framebuffer_w, int16_t framebuffer_h); + +#endif // !defined(LITTLE_FOOT_PRINT) diff --git a/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/Arduino_GFX.cpp b/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/Arduino_GFX.cpp new file mode 100644 index 00000000..7820ad32 --- /dev/null +++ b/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/Arduino_GFX.cpp @@ -0,0 +1,2898 @@ +/* + * start rewrite from: + * https://github.com/adafruit/Adafruit-GFX-Library.git + * + * Arc function come from: + * https://github.com/lovyan03/LovyanGFX.git + */ +#include "Arduino_DataBus.h" +#include "Arduino_GFX.h" +#include "font/glcdfont.h" +#include "float.h" +#ifdef __AVR__ +#include +#elif defined(ESP8266) || defined(ESP32) +#include +#endif + +/**************************************************************************/ +/*! + @brief Instatiate a GFX context for graphics! Can only be done by a superclass + @param w Display width, in pixels + @param h Display height, in pixels +*/ +/**************************************************************************/ +#if defined(LITTLE_FOOT_PRINT) +Arduino_GFX::Arduino_GFX(int16_t w, int16_t h) : WIDTH(w), HEIGHT(h) +#else +Arduino_GFX::Arduino_GFX(int16_t w, int16_t h) : Arduino_G(w, h) +#endif // !defined(LITTLE_FOOT_PRINT) +{ + _width = WIDTH; + _height = HEIGHT; + _max_x = _width - 1; ///< x zero base bound + _max_y = _height - 1; ///< y zero base bound + _rotation = 0; + cursor_y = cursor_x = 0; + textsize_x = textsize_y = 1; + text_pixel_margin = 0; + textcolor = textbgcolor = 0xFFFF; + wrap = true; +#if !defined(ATTINY_CORE) + gfxFont = NULL; +#if defined(U8G2_FONT_SUPPORT) + u8g2Font = NULL; +#endif // defined(U8G2_FONT_SUPPORT) +#endif // !defined(ATTINY_CORE) +} + +/**************************************************************************/ +/*! + @brief Write a line. Check straight or slash line and call corresponding function + @param x0 Start point x coordinate + @param y0 Start point y coordinate + @param x1 End point x coordinate + @param y1 End point y coordinate + @param color 16-bit 5-6-5 Color to draw with +*/ +/**************************************************************************/ +void Arduino_GFX::writeLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, + uint16_t color) +{ + if (x0 == x1) + { + if (y0 > y1) + { + _swap_int16_t(y0, y1); + } + writeFastVLine(x0, y0, y1 - y0 + 1, color); + } + else if (y0 == y1) + { + if (x0 > x1) + { + _swap_int16_t(x0, x1); + } + writeFastHLine(x0, y0, x1 - x0 + 1, color); + } + else + { + writeSlashLine(x0, y0, x1, y1, color); + } +} + +/**************************************************************************/ +/*! + @brief Write a line. Bresenham's algorithm - thx wikpedia + @param x0 Start point x coordinate + @param y0 Start point y coordinate + @param x1 End point x coordinate + @param y1 End point y coordinate + @param color 16-bit 5-6-5 Color to draw with +*/ +/**************************************************************************/ +void Arduino_GFX::writeSlashLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, + uint16_t color) +{ + bool steep = _diff(y1, y0) > _diff(x1, x0); + if (steep) + { + _swap_int16_t(x0, y0); + _swap_int16_t(x1, y1); + } + + if (x0 > x1) + { + _swap_int16_t(x0, x1); + _swap_int16_t(y0, y1); + } + + int16_t dx = x1 - x0; + int16_t dy = _diff(y1, y0); + int16_t err = dx >> 1; + int16_t step = (y0 < y1) ? 1 : -1; + + for (; x0 <= x1; x0++) + { + if (steep) + { + writePixel(y0, x0, color); + } + else + { + writePixel(x0, y0, color); + } + err -= dy; + if (err < 0) + { + err += dx; + y0 += step; + } + } +} + +/**************************************************************************/ +/*! + @brief Start a display-writing routine, overwrite in subclasses. +*/ +/**************************************************************************/ +INLINE void Arduino_GFX::startWrite() +{ +} + +void Arduino_GFX::writePixel(int16_t x, int16_t y, uint16_t color) +{ + if (_ordered_in_range(x, 0, _max_x) && _ordered_in_range(y, 0, _max_y)) + { + writePixelPreclipped(x, y, color); + } +} + +/**************************************************************************/ +/*! + @brief Write a pixel, overwrite in subclasses if startWrite is defined! + @param x x coordinate + @param y y coordinate + @param color 16-bit 5-6-5 Color to fill with +*/ +/**************************************************************************/ +void Arduino_GFX::drawPixel(int16_t x, int16_t y, uint16_t color) +{ + startWrite(); + writePixel(x, y, color); + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Write a perfectly vertical line, overwrite in subclasses if startWrite is defined! + @param x Top-most x coordinate + @param y Top-most y coordinate + @param h Height in pixels + @param color 16-bit 5-6-5 Color to fill with +*/ +/**************************************************************************/ +void Arduino_GFX::writeFastVLine(int16_t x, int16_t y, + int16_t h, uint16_t color) +{ + for (int16_t i = y; i < y + h; i++) + { + writePixel(x, i, color); + } +} + +/**************************************************************************/ +/*! + @brief Write a perfectly horizontal line, overwrite in subclasses if startWrite is defined! + @param x Left-most x coordinate + @param y Left-most y coordinate + @param w Width in pixels + @param color 16-bit 5-6-5 Color to fill with +*/ +/**************************************************************************/ +void Arduino_GFX::writeFastHLine(int16_t x, int16_t y, + int16_t w, uint16_t color) +{ + for (int16_t i = x; i < x + w; i++) + { + writePixel(i, y, color); + } +} + +/**************************************************************************/ +/*! + @brief Draw a filled rectangle to the display. Not self-contained; + should follow startWrite(). Typically used by higher-level + graphics primitives; user code shouldn't need to call this and + is likely to use the self-contained fillRect() instead. + writeFillRect() performs its own edge clipping and rejection; + see writeFillRectPreclipped() for a more 'raw' implementation. + @param x Horizontal position of first corner. + @param y Vertical position of first corner. + @param w Rectangle width in pixels (positive = right of first + corner, negative = left of first corner). + @param h Rectangle height in pixels (positive = below first + corner, negative = above first corner). + @param color 16-bit fill color in '565' RGB format. + @note Written in this deep-nested way because C by definition will + optimize for the 'if' case, not the 'else' -- avoids branches + and rejects clipped rectangles at the least-work possibility. +*/ +/**************************************************************************/ +void Arduino_GFX::writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, + uint16_t color) +{ + if (w && h) + { // Nonzero width and height? + if (w < 0) + { // If negative width... + x += w + 1; // Move X to left edge + w = -w; // Use positive width + } + if (x <= _max_x) + { // Not off right + if (h < 0) + { // If negative height... + y += h + 1; // Move Y to top edge + h = -h; // Use positive height + } + if (y <= _max_y) + { // Not off bottom + int16_t x2 = x + w - 1; + if (x2 >= 0) + { // Not off left + int16_t y2 = y + h - 1; + if (y2 >= 0) + { // Not off top + // Rectangle partly or fully overlaps screen + if (x < 0) + { + x = 0; + w = x2 + 1; + } // Clip left + if (y < 0) + { + y = 0; + h = y2 + 1; + } // Clip top + if (x2 > _max_x) + { + w = _max_x - x + 1; + } // Clip right + if (y2 > _max_y) + { + h = _max_y - y + 1; + } // Clip bottom + writeFillRectPreclipped(x, y, w, h, color); + } + } + } + } + } +} + +/**************************************************************************/ +/*! + @brief Write a rectangle completely with one color, overwrite in subclasses if startWrite is defined! + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param w Width in pixels + @param h Height in pixels + @param color 16-bit 5-6-5 Color to fill with +*/ +/**************************************************************************/ +void Arduino_GFX::writeFillRectPreclipped(int16_t x, int16_t y, int16_t w, int16_t h, + uint16_t color) +{ + // Overwrite in subclasses if desired! + for (int16_t i = y; i < y + h; i++) + { + writeFastHLine(x, i, w, color); + } +} + +/**************************************************************************/ +/*! + @brief End a display-writing routine, overwrite in subclasses if startWrite is defined! +*/ +/**************************************************************************/ +INLINE void Arduino_GFX::endWrite() +{ +} + +/**************************************************************************/ +/*! + @brief Draw a perfectly vertical line (this is often optimized in a subclass!) + @param x Top-most x coordinate + @param y Top-most y coordinate + @param h Height in pixels + @param color 16-bit 5-6-5 Color to fill with +*/ +/**************************************************************************/ +void Arduino_GFX::drawFastVLine(int16_t x, int16_t y, + int16_t h, uint16_t color) +{ + startWrite(); + writeFastVLine(x, y, h, color); + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a perfectly horizontal line (this is often optimized in a subclass!) + @param x Left-most x coordinate + @param y Left-most y coordinate + @param w Width in pixels + @param color 16-bit 5-6-5 Color to fill with +*/ +/**************************************************************************/ +void Arduino_GFX::drawFastHLine(int16_t x, int16_t y, + int16_t w, uint16_t color) +{ + startWrite(); + writeFastHLine(x, y, w, color); + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Fill a rectangle completely with one color. Update in subclasses if desired! + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param w Width in pixels + @param h Height in pixels + @param color 16-bit 5-6-5 Color to fill with +*/ +/**************************************************************************/ +void Arduino_GFX::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, + uint16_t color) +{ + startWrite(); + writeFillRect(x, y, w, h, color); + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Fill the screen completely with one color. Update in subclasses if desired! + @param color 16-bit 5-6-5 Color to fill with +*/ +/**************************************************************************/ +void Arduino_GFX::fillScreen(uint16_t color) +{ + fillRect(0, 0, _width, _height, color); +} + +/**************************************************************************/ +/*! + @brief Draw a line + @param x0 Start point x coordinate + @param y0 Start point y coordinate + @param x1 End point x coordinate + @param y1 End point y coordinate + @param color 16-bit 5-6-5 Color to draw with +*/ +/**************************************************************************/ +void Arduino_GFX::drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, + uint16_t color) +{ + // Update in subclasses if desired! + startWrite(); + writeLine(x0, y0, x1, y1, color); + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a circle outline + @param x Center-point x coordinate + @param y Center-point y coordinate + @param r Radius of circle + @param color 16-bit 5-6-5 Color to draw with +*/ +/**************************************************************************/ +void Arduino_GFX::drawCircle(int16_t x, int16_t y, + int16_t r, uint16_t color) +{ + startWrite(); + drawEllipseHelper(x, y, r, r, 0xf, color); + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Quarter-ellipse drawer, used to do circles and roundrects + @param x Center-point x coordinate + @param y Center-point y coordinate + @param rx radius of x coordinate + @param ry radius of y coordinate + @param cornername Mask bit #1 or bit #2 to indicate which quarters of the circle we're doing + @param color 16-bit 5-6-5 Color to draw with +*/ +/**************************************************************************/ +void Arduino_GFX::drawEllipseHelper(int32_t x, int32_t y, + int32_t rx, int32_t ry, + uint8_t cornername, uint16_t color) +{ + if (rx < 0 || ry < 0 || ((rx == 0) && (ry == 0))) + { + return; + } + if (ry == 0) + { + drawFastHLine(x - rx, y, (ry << 2) + 1, color); + return; + } + if (rx == 0) + { + drawFastVLine(x, y - ry, (rx << 2) + 1, color); + return; + } + + int32_t xt, yt, s, i; + int32_t rx2 = rx * rx; + int32_t ry2 = ry * ry; + + i = -1; + xt = 0; + yt = ry; + s = (ry2 << 1) + rx2 * (1 - (ry << 1)); + do + { + while (s < 0) + s += ry2 * ((++xt << 2) + 2); + if (cornername & 0x1) + { + writeFastHLine(x - xt, y - yt, xt - i, color); + } + if (cornername & 0x2) + { + writeFastHLine(x + i + 1, y - yt, xt - i, color); + } + if (cornername & 0x4) + { + writeFastHLine(x + i + 1, y + yt, xt - i, color); + } + if (cornername & 0x8) + { + writeFastHLine(x - xt, y + yt, xt - i, color); + } + i = xt; + s -= (--yt) * rx2 << 2; + } while (ry2 * xt <= rx2 * yt); + + i = -1; + yt = 0; + xt = rx; + s = (rx2 << 1) + ry2 * (1 - (rx << 1)); + do + { + while (s < 0) + s += rx2 * ((++yt << 2) + 2); + if (cornername & 0x1) + { + writeFastVLine(x - xt, y - yt, yt - i, color); + } + if (cornername & 0x2) + { + writeFastVLine(x + xt, y - yt, yt - i, color); + } + if (cornername & 0x4) + { + writeFastVLine(x + xt, y + i + 1, yt - i, color); + } + if (cornername & 0x8) + { + writeFastVLine(x - xt, y + i + 1, yt - i, color); + } + i = yt; + s -= (--xt) * ry2 << 2; + } while (rx2 * yt <= ry2 * xt); +} + +/**************************************************************************/ +/*! + @brief Draw a circle with filled color + @param x Center-point x coordinate + @param y Center-point y coordinate + @param r Radius of circle + @param color 16-bit 5-6-5 Color to fill with +*/ +/**************************************************************************/ +void Arduino_GFX::fillCircle(int16_t x, int16_t y, + int16_t r, uint16_t color) +{ + startWrite(); + fillEllipseHelper(x, y, r, r, 3, 0, color); + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Quarter-circle drawer with fill, used for circles and roundrects + @param x Center-point x coordinate + @param y Center-point y coordinate + @param rx Radius of x coordinate + @param ry Radius of y coordinate + @param corners Mask bits indicating which quarters we're doing + @param delta Offset from center-point, used for round-rects + @param color 16-bit 5-6-5 Color to fill with +*/ +/**************************************************************************/ +void Arduino_GFX::fillEllipseHelper(int32_t x, int32_t y, + int32_t rx, int32_t ry, + uint8_t corners, int16_t delta, uint16_t color) +{ + if (rx < 0 || ry < 0 || ((rx == 0) && (ry == 0))) + { + return; + } + if (ry == 0) + { + drawFastHLine(x - rx, y, (ry << 2) + 1, color); + return; + } + if (rx == 0) + { + drawFastVLine(x, y - ry, (rx << 2) + 1, color); + return; + } + + int32_t xt, yt, i; + int32_t rx2 = (int32_t)rx * rx; + int32_t ry2 = (int32_t)ry * ry; + int32_t s; + + writeFastHLine(x - rx, y, (rx << 1) + 1, color); + i = 0; + yt = 0; + xt = rx; + s = (rx2 << 1) + ry2 * (1 - (rx << 1)); + do + { + while (s < 0) + { + s += rx2 * ((++yt << 2) + 2); + } + if (corners & 1) + { + writeFillRect(x - xt, y - yt, (xt << 1) + 1 + delta, yt - i, color); + } + if (corners & 2) + { + writeFillRect(x - xt, y + i + 1, (xt << 1) + 1 + delta, yt - i, color); + } + i = yt; + s -= (--xt) * ry2 << 2; + } while (rx2 * yt <= ry2 * xt); + + xt = 0; + yt = ry; + s = (ry2 << 1) + rx2 * (1 - (ry << 1)); + do + { + while (s < 0) + { + s += ry2 * ((++xt << 2) + 2); + } + if (corners & 1) + { + writeFastHLine(x - xt, y - yt, (xt << 1) + 1 + delta, color); + } + if (corners & 2) + { + writeFastHLine(x - xt, y + yt, (xt << 1) + 1 + delta, color); + } + s -= (--yt) * rx2 << 2; + } while (ry2 * xt <= rx2 * yt); +} + +/**************************************************************************/ +/*! + @brief Draw an ellipse outline + @param x Center-point x coordinate + @param y Center-point y coordinate + @param rx radius of x coordinate + @param ry radius of y coordinate + @param start degree of ellipse start + @param end degree of ellipse end + @param color 16-bit 5-6-5 Color to draw with +*/ +/**************************************************************************/ +void Arduino_GFX::drawEllipse(int16_t x, int16_t y, int16_t rx, int16_t ry, uint16_t color) +{ + startWrite(); + drawEllipseHelper(x, y, rx, ry, 0xf, color); + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw an ellipse with filled color + @param x Center-point x coordinate + @param y Center-point y coordinate + @param rx radius of x coordinate + @param ry radius of y coordinate + @param start degree of ellipse start + @param end degree of ellipse end + @param color 16-bit 5-6-5 Color to fill with +*/ +/**************************************************************************/ +void Arduino_GFX::fillEllipse(int16_t x, int16_t y, int16_t rx, int16_t ry, uint16_t color) +{ + startWrite(); + fillEllipseHelper(x, y, rx, ry, 3, 0, color); + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw an arc outline + @param x Center-point x coordinate + @param y Center-point y coordinate + @param r1 Outer radius of arc + @param r2 Inner radius of arc + @param start degree of arc start + @param end degree of arc end + @param color 16-bit 5-6-5 Color to draw with +*/ +/**************************************************************************/ +void Arduino_GFX::drawArc(int16_t x, int16_t y, int16_t r1, int16_t r2, float start, float end, uint16_t color) +{ + if (r1 < r2) + { + _swap_int16_t(r1, r2); + } + if (r1 < 1) + { + r1 = 1; + } + if (r2 < 1) + { + r2 = 1; + } + bool equal = fabsf(start - end) < FLT_EPSILON; + start = fmodf(start, 360); + end = fmodf(end, 360); + if (start < 0) + start += 360.0; + if (end < 0) + end += 360.0; + + startWrite(); + fillArcHelper(x, y, r1, r2, start, start, color); + fillArcHelper(x, y, r1, r2, end, end, color); + if (!equal && (fabsf(start - end) <= 0.0001)) + { + start = .0; + end = 360.0; + } + fillArcHelper(x, y, r1, r1, start, end, color); + fillArcHelper(x, y, r2, r2, start, end, color); + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw an arc with filled color + @param x Center-point x coordinate + @param y Center-point y coordinate + @param r1 Outer radius of arc + @param r2 Inner radius of arc + @param start degree of arc start + @param end degree of arc end + @param color 16-bit 5-6-5 Color to fill with +*/ +/**************************************************************************/ +void Arduino_GFX::fillArc(int16_t x, int16_t y, int16_t r1, int16_t r2, float start, float end, uint16_t color) +{ + if (r1 < r2) + { + _swap_int16_t(r1, r2); + } + if (r1 < 1) + { + r1 = 1; + } + if (r2 < 1) + { + r2 = 1; + } + bool equal = fabsf(start - end) < FLT_EPSILON; + start = fmodf(start, 360); + end = fmodf(end, 360); + if (start < 0) + start += 360.0; + if (end < 0) + end += 360.0; + if (!equal && (fabsf(start - end) <= 0.0001)) + { + start = .0; + end = 360.0; + } + + startWrite(); + fillArcHelper(x, y, r1, r2, start, end, color); + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Arc drawer with fill + @param cx Center-point x coordinate + @param cy Center-point y coordinate + @param oradius Outer radius of arc + @param iradius Inner radius of arc + @param start degree of arc start + @param end degree of arc end + @param color 16-bit 5-6-5 Color to fill with +*/ +/**************************************************************************/ +void Arduino_GFX::fillArcHelper(int16_t cx, int16_t cy, int16_t oradius, int16_t iradius, float start, float end, uint16_t color) +{ + if ((start == 90.0) || (start == 180.0) || (start == 270.0) || (start == 360.0)) + { + start -= 0.1; + } + + if ((end == 90.0) || (end == 180.0) || (end == 270.0) || (end == 360.0)) + { + end -= 0.1; + } + + float s_cos = (cos(start * DEGTORAD)); + float e_cos = (cos(end * DEGTORAD)); + float sslope = s_cos / (sin(start * DEGTORAD)); + float eslope = e_cos / (sin(end * DEGTORAD)); + float swidth = 0.5 / s_cos; + float ewidth = -0.5 / e_cos; + --iradius; + int32_t ir2 = iradius * iradius + iradius; + int32_t or2 = oradius * oradius + oradius; + + bool start180 = !(start < 180.0); + bool end180 = end < 180.0; + bool reversed = start + 180.0 < end || (end < start && start < end + 180.0); + + int32_t xs = -oradius; + int32_t y = -oradius; + int32_t ye = oradius; + int32_t xe = oradius + 1; + if (!reversed) + { + if ((end >= 270 || end < 90) && (start >= 270 || start < 90)) + { + xs = 0; + } + else if (end < 270 && end >= 90 && start < 270 && start >= 90) + { + xe = 1; + } + if (end >= 180 && start >= 180) + { + ye = 0; + } + else if (end < 180 && start < 180) + { + y = 0; + } + } + do + { + int32_t y2 = y * y; + int32_t x = xs; + if (x < 0) + { + while (x * x + y2 >= or2) + { + ++x; + } + if (xe != 1) + { + xe = 1 - x; + } + } + float ysslope = (y + swidth) * sslope; + float yeslope = (y + ewidth) * eslope; + int32_t len = 0; + do + { + bool flg1 = start180 != (x <= ysslope); + bool flg2 = end180 != (x <= yeslope); + int32_t distance = x * x + y2; + if (distance >= ir2 && ((flg1 && flg2) || (reversed && (flg1 || flg2))) && x != xe && distance < or2) + { + ++len; + } + else + { + if (len) + { + writeFastHLine(cx + x - len, cy + y, len, color); + len = 0; + } + if (distance >= or2) + break; + if (x < 0 && distance < ir2) + { + x = -x; + } + } + } while (++x <= xe); + } while (++y <= ye); +} + +/**************************************************************************/ +/*! + @brief Draw a rectangle with no fill color + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param w Width in pixels + @param h Height in pixels + @param color 16-bit 5-6-5 Color to draw with +*/ +/**************************************************************************/ +void Arduino_GFX::drawRect(int16_t x, int16_t y, int16_t w, int16_t h, + uint16_t color) +{ + startWrite(); + writeFastHLine(x, y, w, color); + writeFastHLine(x, y + h - 1, w, color); + writeFastVLine(x, y, h, color); + writeFastVLine(x + w - 1, y, h, color); + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a rounded rectangle with no fill color + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param w Width in pixels + @param h Height in pixels + @param r Radius of corner rounding + @param color 16-bit 5-6-5 Color to draw with +*/ +/**************************************************************************/ +void Arduino_GFX::drawRoundRect(int16_t x, int16_t y, int16_t w, + int16_t h, int16_t r, uint16_t color) +{ + int16_t max_radius = ((w < h) ? w : h) / 2; // 1/2 minor axis + if (r > max_radius) + r = max_radius; + // smarter version + startWrite(); + writeFastHLine(x + r, y, w - 2 * r, color); // Top + writeFastHLine(x + r, y + h - 1, w - 2 * r, color); // Bottom + writeFastVLine(x, y + r, h - 2 * r, color); // Left + writeFastVLine(x + w - 1, y + r, h - 2 * r, color); // Right + // draw four corners + drawEllipseHelper(x + r, y + r, r, r, 1, color); + drawEllipseHelper(x + w - r - 1, y + r, r, r, 2, color); + drawEllipseHelper(x + w - r - 1, y + h - r - 1, r, r, 4, color); + drawEllipseHelper(x + r, y + h - r - 1, r, r, 8, color); + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a rounded rectangle with fill color + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param w Width in pixels + @param h Height in pixels + @param r Radius of corner rounding + @param color 16-bit 5-6-5 Color to draw/fill with +*/ +/**************************************************************************/ +void Arduino_GFX::fillRoundRect(int16_t x, int16_t y, int16_t w, + int16_t h, int16_t r, uint16_t color) +{ + int16_t max_radius = ((w < h) ? w : h) / 2; // 1/2 minor axis + if (r > max_radius) + r = max_radius; + // smarter version + startWrite(); + writeFillRect(x, y + r, w, h - (r << 1), color); + // draw four corners + fillEllipseHelper(x + r, y + r, r, r, 1, w - 2 * r - 1, color); + fillEllipseHelper(x + r, y + h - r - 1, r, r, 2, w - 2 * r - 1, color); + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a triangle with no fill color + @param x0 Vertex #0 x coordinate + @param y0 Vertex #0 y coordinate + @param x1 Vertex #1 x coordinate + @param y1 Vertex #1 y coordinate + @param x2 Vertex #2 x coordinate + @param y2 Vertex #2 y coordinate + @param color 16-bit 5-6-5 Color to draw with +*/ +/**************************************************************************/ +void Arduino_GFX::drawTriangle(int16_t x0, int16_t y0, + int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color) +{ + startWrite(); + writeLine(x0, y0, x1, y1, color); + writeLine(x1, y1, x2, y2, color); + writeLine(x2, y2, x0, y0, color); + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a triangle with color-fill + @param x0 Vertex #0 x coordinate + @param y0 Vertex #0 y coordinate + @param x1 Vertex #1 x coordinate + @param y1 Vertex #1 y coordinate + @param x2 Vertex #2 x coordinate + @param y2 Vertex #2 y coordinate + @param color 16-bit 5-6-5 Color to fill/draw with +*/ +/**************************************************************************/ +void Arduino_GFX::fillTriangle(int16_t x0, int16_t y0, + int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color) +{ + int16_t a, b, y, last; + + // Sort coordinates by Y order (y2 >= y1 >= y0) + if (y0 > y1) + { + _swap_int16_t(y0, y1); + _swap_int16_t(x0, x1); + } + if (y1 > y2) + { + _swap_int16_t(y2, y1); + _swap_int16_t(x2, x1); + } + if (y0 > y1) + { + _swap_int16_t(y0, y1); + _swap_int16_t(x0, x1); + } + + startWrite(); + if (y0 == y2) + { // Handle awkward all-on-same-line case as its own thing + a = b = x0; + if (x1 < a) + a = x1; + else if (x1 > b) + b = x1; + if (x2 < a) + a = x2; + else if (x2 > b) + b = x2; + writeFastHLine(a, y0, b - a + 1, color); + endWrite(); + return; + } + + int16_t + dx01 = x1 - x0, + dy01 = y1 - y0, + dx02 = x2 - x0, + dy02 = y2 - y0, + dx12 = x2 - x1, + dy12 = y2 - y1; + int32_t + sa = 0, + sb = 0; + + // For upper part of triangle, find scanline crossings for segments + // 0-1 and 0-2. If y1=y2 (flat-bottomed triangle), the scanline y1 + // is included here (and second loop will be skipped, avoiding a /0 + // error there), otherwise scanline y1 is skipped here and handled + // in the second loop...which also avoids a /0 error here if y0=y1 + // (flat-topped triangle). + if (y1 == y2) + { + last = y1; // Include y1 scanline + } + else + { + last = y1 - 1; // Skip it + } + + for (y = y0; y <= last; y++) + { + a = x0 + sa / dy01; + b = x0 + sb / dy02; + sa += dx01; + sb += dx02; + /* longhand: + a = x0 + (x1 - x0) * (y - y0) / (y1 - y0); + b = x0 + (x2 - x0) * (y - y0) / (y2 - y0); + */ + if (a > b) + { + _swap_int16_t(a, b); + } + writeFastHLine(a, y, b - a + 1, color); + } + + // For lower part of triangle, find scanline crossings for segments + // 0-2 and 1-2. This loop is skipped if y1=y2. + sa = (int32_t)dx12 * (y - y1); + sb = (int32_t)dx02 * (y - y0); + for (; y <= y2; y++) + { + a = x1 + sa / dy12; + b = x0 + sb / dy02; + sa += dx12; + sb += dx02; + /* longhand: + a = x1 + (x2 - x1) * (y - y1) / (y2 - y1); + b = x0 + (x2 - x0) * (y - y0) / (y2 - y0); + */ + if (a > b) + { + _swap_int16_t(a, b); + } + writeFastHLine(a, y, b - a + 1, color); + } + endWrite(); +} + +// BITMAP / XBITMAP / GRAYSCALE / RGB BITMAP FUNCTIONS --------------------- + +/**************************************************************************/ +/*! + @brief Draw a PROGMEM-resident 1-bit image at the specified (x,y) position, using the specified foreground color (unset bits are transparent). + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array with monochrome bitmap + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels + @param color 16-bit 5-6-5 Color to draw with +*/ +/**************************************************************************/ +void Arduino_GFX::drawBitmap(int16_t x, int16_t y, + const uint8_t bitmap[], int16_t w, int16_t h, uint16_t color) +{ + int16_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte + uint8_t byte = 0; + + startWrite(); + for (int16_t j = 0; j < h; j++, y++) + { + for (int16_t i = 0; i < w; i++) + { + if (i & 7) + { + byte <<= 1; + } + else + { + byte = pgm_read_byte(&bitmap[j * byteWidth + i / 8]); + } + if (byte & 0x80) + { + writePixel(x + i, y, color); + } + } + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a PROGMEM-resident 1-bit image at the specified (x,y) position, using the specified foreground (for set bits) and background (unset bits) colors. + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array with monochrome bitmap + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels + @param color 16-bit 5-6-5 Color to draw pixels with + @param bg 16-bit 5-6-5 Color to draw background with +*/ +/**************************************************************************/ +void Arduino_GFX::drawBitmap(int16_t x, int16_t y, + const uint8_t bitmap[], int16_t w, int16_t h, + uint16_t color, uint16_t bg) +{ + int16_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte + uint8_t byte = 0; + + startWrite(); + for (int16_t j = 0; j < h; j++, y++) + { + for (int16_t i = 0; i < w; i++) + { + if (i & 7) + { + byte <<= 1; + } + else + { + byte = pgm_read_byte(&bitmap[j * byteWidth + i / 8]); + } + writePixel(x + i, y, (byte & 0x80) ? color : bg); + } + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a RAM-resident 1-bit image at the specified (x,y) position, using the specified foreground color (unset bits are transparent). + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array with monochrome bitmap + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels + @param color 16-bit 5-6-5 Color to draw with +*/ +/**************************************************************************/ +void Arduino_GFX::drawBitmap(int16_t x, int16_t y, + uint8_t *bitmap, int16_t w, int16_t h, uint16_t color) +{ + int16_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte + uint8_t byte = 0; + + startWrite(); + for (int16_t j = 0; j < h; j++, y++) + { + for (int16_t i = 0; i < w; i++) + { + if (i & 7) + { + byte <<= 1; + } + else + { + byte = bitmap[j * byteWidth + i / 8]; + } + if (byte & 0x80) + { + writePixel(x + i, y, color); + } + } + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a RAM-resident 1-bit image at the specified (x,y) position, using the specified foreground (for set bits) and background (unset bits) colors. + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array with monochrome bitmap + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels + @param color 16-bit 5-6-5 Color to draw pixels with + @param bg 16-bit 5-6-5 Color to draw background with +*/ +/**************************************************************************/ +void Arduino_GFX::drawBitmap(int16_t x, int16_t y, + uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint16_t bg) +{ + int16_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte + uint8_t byte = 0; + + startWrite(); + for (int16_t j = 0; j < h; j++, y++) + { + for (int16_t i = 0; i < w; i++) + { + if (i & 7) + { + byte <<= 1; + } + else + { + byte = bitmap[j * byteWidth + i / 8]; + } + writePixel(x + i, y, (byte & 0x80) ? color : bg); + } + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw PROGMEM-resident XBitMap Files (*.xbm), exported from GIMP. + Usage: Export from GIMP to *.xbm, rename *.xbm to *.c and open in editor. + C Array can be directly used with this function. + There is no RAM-resident version of this function; if generating bitmaps + in RAM, use the format defined by drawBitmap() and call that instead. + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array with monochrome bitmap + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels + @param color 16-bit 5-6-5 Color to draw pixels with +*/ +/**************************************************************************/ +void Arduino_GFX::drawXBitmap(int16_t x, int16_t y, + const uint8_t bitmap[], int16_t w, int16_t h, uint16_t color) +{ + int16_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte + uint8_t byte = 0; + + startWrite(); + for (int16_t j = 0; j < h; j++, y++) + { + for (int16_t i = 0; i < w; i++) + { + if (i & 7) + { + byte >>= 1; + } + else + { + byte = pgm_read_byte(&bitmap[j * byteWidth + i / 8]); + } + // Nearly identical to drawBitmap(), only the bit order + // is reversed here (left-to-right = LSB to MSB): + if (byte & 0x01) + { + writePixel(x + i, y, color); + } + } + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a PROGMEM-resident 8-bit image (grayscale) at the specified (x,y) pos. + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array with grayscale bitmap + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels +*/ +/**************************************************************************/ +void Arduino_GFX::drawGrayscaleBitmap(int16_t x, int16_t y, + const uint8_t bitmap[], int16_t w, int16_t h) +{ + uint8_t v; + startWrite(); + for (int16_t j = 0; j < h; j++, y++) + { + for (int16_t i = 0; i < w; i++) + { + v = (uint8_t)pgm_read_byte(&bitmap[j * w + i]); + writePixel(x + i, y, color565(v, v, v)); + } + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a RAM-resident 8-bit image (grayscale) at the specified (x,y) pos. + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array with grayscale bitmap + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels +*/ +/**************************************************************************/ +void Arduino_GFX::drawGrayscaleBitmap(int16_t x, int16_t y, + uint8_t *bitmap, int16_t w, int16_t h) +{ + uint8_t v; + startWrite(); + for (int16_t j = 0; j < h; j++, y++) + { + for (int16_t i = 0; i < w; i++) + { + v = bitmap[j * w + i]; + writePixel(x + i, y, color565(v, v, v)); + } + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a PROGMEM-resident 8-bit image (grayscale) with a 1-bit mask + (set bits = opaque, unset bits = clear) at the specified (x,y) position. + BOTH buffers (grayscale and mask) must be PROGMEM-resident. + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array with grayscale bitmap + @param mask byte array with mask bitmap + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels +*/ +/**************************************************************************/ +void Arduino_GFX::drawGrayscaleBitmap(int16_t x, int16_t y, + const uint8_t bitmap[], const uint8_t mask[], + int16_t w, int16_t h) +{ + int16_t bw = (w + 7) / 8; // Bitmask scanline pad = whole byte + uint8_t byte = 0; + uint8_t v; + startWrite(); + for (int16_t j = 0; j < h; j++, y++) + { + for (int16_t i = 0; i < w; i++) + { + if (i & 7) + { + byte <<= 1; + } + else + { + byte = pgm_read_byte(&mask[j * bw + i / 8]); + } + if (byte & 0x80) + { + v = (uint8_t)pgm_read_byte(&bitmap[j * w + i]); + writePixel(x + i, y, color565(v, v, v)); + } + } + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a RAM-resident 8-bit image (grayscale) with a 1-bit mask + (set bits = opaque, unset bits = clear) at the specified (x,y) position. + BOTH buffers (grayscale and mask) must be RAM-residentt, no mix-and-match + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array with grayscale bitmap + @param mask byte array with mask bitmap + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels +*/ +/**************************************************************************/ +void Arduino_GFX::drawGrayscaleBitmap(int16_t x, int16_t y, + uint8_t *bitmap, uint8_t *mask, int16_t w, int16_t h) +{ + int16_t bw = (w + 7) / 8; // Bitmask scanline pad = whole byte + uint8_t byte = 0; + uint8_t v; + startWrite(); + for (int16_t j = 0; j < h; j++, y++) + { + for (int16_t i = 0; i < w; i++) + { + if (i & 7) + { + byte <<= 1; + } + else + { + byte = mask[j * bw + i / 8]; + } + if (byte & 0x80) + { + v = bitmap[j * w + i]; + writePixel(x + i, y, color565(v, v, v)); + } + } + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a Indexed 16-bit image (RGB 5/6/5) at the specified (x,y) position. + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array of Indexed color bitmap + @param color_index byte array of 16-bit color index + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels + @param x_skip number of pixels required to skip for every bitmap row +*/ +/**************************************************************************/ +void Arduino_GFX::drawIndexedBitmap( + int16_t x, int16_t y, + uint8_t *bitmap, uint16_t *color_index, int16_t w, int16_t h, int16_t x_skip) +{ + int32_t offset = 0; + startWrite(); + for (int16_t j = 0; j < h; j++, y++) + { + for (int16_t i = 0; i < w; i++) + { + writePixel(x + i, y, color_index[bitmap[offset++]]); + } + offset += x_skip; + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a Indexed 16-bit image (RGB 5/6/5) at the specified (x,y) position. + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array of Indexed color bitmap + @param color_index byte array of 16-bit color index + @param chroma_key transparent color index + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels + @param x_skip number of pixels required to skip for every bitmap row +*/ +/**************************************************************************/ +void Arduino_GFX::drawIndexedBitmap( + int16_t x, int16_t y, + uint8_t *bitmap, uint16_t *color_index, uint8_t chroma_key, int16_t w, int16_t h, int16_t x_skip) +{ + int32_t offset = 0; + uint8_t color_key; + startWrite(); + for (int16_t j = 0; j < h; j++, y++) + { + for (int16_t i = 0; i < w; i++) + { + color_key = bitmap[offset++]; + if (color_key != chroma_key) + { + writePixel(x + i, y, color_index[color_key]); + } + offset += x_skip; + } + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a RAM-resident 3-bit image (RGB 1/1/1) at the specified (x,y) position. + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array with 3-bit color bitmap + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels +*/ +/**************************************************************************/ +void Arduino_GFX::draw3bitRGBBitmap(int16_t x, int16_t y, + uint8_t *bitmap, int16_t w, int16_t h) +{ + int32_t offset = 0, idx = 0; + uint8_t c = 0; + uint16_t d; + startWrite(); + for (int16_t j = 0; j < h; j++, y++) + { + for (int16_t i = 0; i < w; i++) + { + if (offset & 1) + { + d = (((c & 0b100) ? RED : 0) | + ((c & 0b010) ? GREEN : 0) | + ((c & 0b001) ? BLUE : 0)); + } + else + { + c = bitmap[idx++]; + d = (((c & 0b100000) ? RED : 0) | + ((c & 0b010000) ? GREEN : 0) | + ((c & 0b001000) ? BLUE : 0)); + } + writePixel(x + i, y, d); + offset++; + } + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a PROGMEM-resident 16-bit image (RGB 5/6/5) at the specified (x,y) position. + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array with 16-bit color bitmap + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels +*/ +/**************************************************************************/ +void Arduino_GFX::draw16bitRGBBitmap(int16_t x, int16_t y, + const uint16_t bitmap[], int16_t w, int16_t h) +{ + int32_t offset = 0; + startWrite(); + for (int16_t j = 0; j < h; j++, y++) + { + for (int16_t i = 0; i < w; i++) + { + writePixel(x + i, y, pgm_read_word(&bitmap[offset++])); + } + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a RAM-resident 16-bit image (RGB 5/6/5) at the specified (x,y) position. + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array with 16-bit color bitmap + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels +*/ +/**************************************************************************/ +void Arduino_GFX::draw16bitRGBBitmap(int16_t x, int16_t y, + uint16_t *bitmap, int16_t w, int16_t h) +{ + int32_t offset = 0; + startWrite(); + for (int16_t j = 0; j < h; j++, y++) + { + for (int16_t i = 0; i < w; i++) + { + writePixel(x + i, y, bitmap[offset++]); + } + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a RAM-resident 16-bit image (RGB 5/6/5) at the specified (x,y) position. + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array with 16-bit color bitmap + @param transparent_color + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels +*/ +/**************************************************************************/ +void Arduino_GFX::draw16bitRGBBitmap( + int16_t x, int16_t y, + uint16_t *bitmap, uint16_t transparent_color, int16_t w, int16_t h) +{ + int32_t offset = 0; + uint16_t color; + startWrite(); + for (int16_t j = 0; j < h; j++, y++) + { + for (int16_t i = 0; i < w; i++) + { + color = bitmap[offset++]; + if (color != transparent_color) + { + writePixel(x + i, y, color); + } + } + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a RAM-resident 16-bit Big Endian image (RGB 5/6/5) at the specified (x,y) position. + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array with 16-bit color bitmap + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels +*/ +/**************************************************************************/ +void Arduino_GFX::draw16bitBeRGBBitmap(int16_t x, int16_t y, + uint16_t *bitmap, int16_t w, int16_t h) +{ + int32_t offset = 0; + uint16_t p; + startWrite(); + for (int16_t j = 0; j < h; j++, y++) + { + for (int16_t i = 0; i < w; i++) + { + p = bitmap[offset++]; + MSB_16_SET(p, p); + writePixel(x + i, y, p); + } + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a PROGMEM-resident 16-bit image (RGB 5/6/5) with a 1-bit mask + (set bits = opaque, unset bits = clear) at the specified (x,y) position. + BOTH buffers (color and mask) must be PROGMEM-resident. + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array with 16-bit color bitmap + @param mask byte array with monochrome mask bitmap + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels +*/ +/**************************************************************************/ +void Arduino_GFX::draw16bitRGBBitmap(int16_t x, int16_t y, + const uint16_t bitmap[], const uint8_t mask[], + int16_t w, int16_t h) +{ + int32_t offset = 0; + int16_t bw = (w + 7) / 8; // Bitmask scanline pad = whole byte + uint8_t byte = 0; + startWrite(); + for (int16_t j = 0; j < h; j++, y++) + { + for (int16_t i = 0; i < w; i++) + { + if (i & 7) + { + byte <<= 1; + } + else + { + byte = pgm_read_byte(&mask[j * bw + i / 8]); + } + if (byte & 0x80) + { + writePixel(x + i, y, pgm_read_word(&bitmap[offset])); + } + offset++; + } + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a RAM-resident 16-bit image (RGB 5/6/5) with a 1-bit mask + (set bits = opaque, unset bits = clear) at the specified (x,y) position. + BOTH buffers (color and mask) must be RAM-resident. + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array with 16-bit color bitmap + @param mask byte array with monochrome mask bitmap + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels +*/ +/**************************************************************************/ +void Arduino_GFX::draw16bitRGBBitmap(int16_t x, int16_t y, + uint16_t *bitmap, uint8_t *mask, int16_t w, int16_t h) +{ + int32_t offset = 0, maskIdx = 0; + uint8_t byte = 0; + startWrite(); + for (int16_t j = 0; j < h; j++, y++) + { + for (int16_t i = 0; i < w; i++) + { + if (i & 7) + { + byte <<= 1; + } + else + { + byte = mask[maskIdx++]; + } + if (byte & 0x80) + { + writePixel(x + i, y, bitmap[offset]); + } + offset++; + } + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a PROGMEM-resident 24-bit image (RGB 5/6/5) at the specified (x,y) position. + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array with 16-bit color bitmap + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels +*/ +/**************************************************************************/ +void Arduino_GFX::draw24bitRGBBitmap(int16_t x, int16_t y, + const uint8_t bitmap[], int16_t w, int16_t h) +{ + int32_t offset = 0; + startWrite(); + for (int16_t j = 0; j < h; j++, y++) + { + for (int16_t i = 0; i < w; i++) + { + writePixel(x + i, y, color565(pgm_read_byte(&bitmap[offset]), pgm_read_byte(&bitmap[offset + 1]), pgm_read_byte(&bitmap[offset + 2]))); + offset += 3; + } + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a RAM-resident 24-bit image (RGB 5/6/5) at the specified (x,y) position. + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array with 16-bit color bitmap + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels +*/ +/**************************************************************************/ +void Arduino_GFX::draw24bitRGBBitmap(int16_t x, int16_t y, + uint8_t *bitmap, int16_t w, int16_t h) +{ + int32_t offset = 0; + startWrite(); + for (int16_t j = 0; j < h; j++, y++) + { + for (int16_t i = 0; i < w; i++) + { + writePixel(x + i, y, color565(bitmap[offset], bitmap[offset + 1], bitmap[offset + 2])); + offset += 3; + } + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a PROGMEM-resident 24-bit image (RGB 5/6/5) with a 1-bit mask + (set bits = opaque, unset bits = clear) at the specified (x,y) position. + BOTH buffers (color and mask) must be PROGMEM-resident. + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array with 16-bit color bitmap + @param mask byte array with monochrome mask bitmap + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels +*/ +/**************************************************************************/ +void Arduino_GFX::draw24bitRGBBitmap(int16_t x, int16_t y, + const uint8_t bitmap[], const uint8_t mask[], + int16_t w, int16_t h) +{ + int32_t offset = 0; + int16_t bw = (w + 7) / 8; // Bitmask scanline pad = whole byte + uint8_t byte = 0; + startWrite(); + for (int16_t j = 0; j < h; j++, y++) + { + for (int16_t i = 0; i < w; i++) + { + if (i & 7) + { + byte <<= 1; + } + else + { + byte = pgm_read_byte(&mask[j * bw + i / 8]); + } + if (byte & 0x80) + { + writePixel(x + i, y, color565(pgm_read_byte(&bitmap[offset]), pgm_read_byte(&bitmap[offset + 1]), pgm_read_byte(&bitmap[offset + 2]))); + } + offset += 3; + } + } + endWrite(); +} + +/**************************************************************************/ +/*! + @brief Draw a RAM-resident 24-bit image (RGB 5/6/5) with a 1-bit mask + (set bits = opaque, unset bits = clear) at the specified (x,y) position. + BOTH buffers (color and mask) must be RAM-resident. + @param x Top left corner x coordinate + @param y Top left corner y coordinate + @param bitmap byte array with 16-bit color bitmap + @param mask byte array with monochrome mask bitmap + @param w Width of bitmap in pixels + @param h Height of bitmap in pixels +*/ +/**************************************************************************/ +void Arduino_GFX::draw24bitRGBBitmap(int16_t x, int16_t y, + uint8_t *bitmap, uint8_t *mask, int16_t w, int16_t h) +{ + int32_t offset = 0; + int16_t bw = (w + 7) / 8; // Bitmask scanline pad = whole byte + uint8_t byte = 0; + startWrite(); + for (int16_t j = 0; j < h; j++, y++) + { + for (int16_t i = 0; i < w; i++) + { + if (i & 7) + { + byte <<= 1; + } + else + { + byte = mask[j * bw + i / 8]; + } + if (byte & 0x80) + { + writePixel(x + i, y, color565(bitmap[offset], bitmap[offset + 1], bitmap[offset + 2])); + } + offset += 3; + } + } + endWrite(); +} + +#if defined(U8G2_FONT_SUPPORT) +uint16_t Arduino_GFX::u8g2_font_get_word(const uint8_t *font, uint8_t offset) +{ + uint16_t pos; + font += offset; + pos = pgm_read_byte(font); + font++; + pos <<= 8; + pos += pgm_read_byte(font); + + return pos; +} + +uint8_t Arduino_GFX::u8g2_font_decode_get_unsigned_bits(uint8_t cnt) +{ + uint8_t val; + uint8_t bit_pos = _u8g2_decode_bit_pos; + uint8_t bit_pos_plus_cnt; + + val = pgm_read_byte(_u8g2_decode_ptr); + + val >>= bit_pos; + bit_pos_plus_cnt = bit_pos; + bit_pos_plus_cnt += cnt; + if (bit_pos_plus_cnt >= 8) + { + uint8_t s = 8; + s -= bit_pos; + _u8g2_decode_ptr++; + val |= pgm_read_byte(_u8g2_decode_ptr) << (s); + bit_pos_plus_cnt -= 8; + } + val &= (1U << cnt) - 1; + + _u8g2_decode_bit_pos = bit_pos_plus_cnt; + + return val; +} + +int8_t Arduino_GFX::u8g2_font_decode_get_signed_bits(uint8_t cnt) +{ + int8_t v, d; + v = (int8_t)u8g2_font_decode_get_unsigned_bits(cnt); + d = 1; + cnt--; + d <<= cnt; + v -= d; + + return v; +} + +void Arduino_GFX::u8g2_font_decode_len(uint8_t len, uint8_t is_foreground, uint16_t color, uint16_t bg) +{ + uint8_t cnt; /* total number of remaining pixels, which have to be drawn */ + uint8_t rem; /* remaining pixel to the right edge of the glyph */ + uint8_t current; /* number of pixels, which need to be drawn for the draw procedure */ + /* current is either equal to cnt or equal to rem */ + + /* target position on the screen */ + uint16_t x, y; + + cnt = len; + + /* get the local position */ + uint8_t lx = _u8g2_dx; + uint8_t ly = _u8g2_dy; + + for (;;) + { + /* calculate the number of pixel to the right edge of the glyph */ + rem = _u8g2_char_width; + rem -= lx; + + /* calculate how many pixel to draw. This is either to the right edge */ + /* or lesser, if not enough pixel are left */ + current = rem; + if (cnt < rem) + current = cnt; + + /* now draw the line, but apply the rotation around the glyph target position */ + // u8g2_font_decode_draw_pixel(u8g2, lx,ly,current, is_foreground); + + if (textsize_x == 1 && textsize_y == 1) + { + /* get target position */ + x = _u8g2_target_x + lx; + y = _u8g2_target_y + ly; + + /* draw foreground and background (if required) */ + if (is_foreground) + { + writeFastHLine(x, y, current, color); + } + else if (bg != color) + { + writeFastHLine(x, y, current, bg); + } + } + else + { + /* get target position */ + x = _u8g2_target_x + (lx * textsize_x); + y = _u8g2_target_y + (ly * textsize_y); + + /* draw foreground and background (if required) */ + if (is_foreground) + { + writeFillRect(x, y, (current * textsize_x) - text_pixel_margin, + textsize_y - text_pixel_margin, color); + } + else if (bg != color) + { + writeFillRect(x, y, (current * textsize_x) - text_pixel_margin, + textsize_y - text_pixel_margin, bg); + } + } + + /* check, whether the end of the run length code has been reached */ + if (cnt < rem) + break; + cnt -= rem; + lx = 0; + ly++; + } + lx += cnt; + + _u8g2_dx = lx; + _u8g2_dy = ly; +} +#endif // defined(U8G2_FONT_SUPPORT) + +// TEXT- AND CHARACTER-HANDLING FUNCTIONS ---------------------------------- + +// Draw a character +/**************************************************************************/ +/*! + @brief Draw a single character + @param x Bottom left corner x coordinate + @param y Bottom left corner y coordinate + @param c The 8-bit font-indexed character (likely ascii) + @param color 16-bit 5-6-5 Color to draw chraracter with + @param bg 16-bit 5-6-5 Color to fill background with (if same as color, no background) +*/ +/**************************************************************************/ +void Arduino_GFX::drawChar(int16_t x, int16_t y, unsigned char c, + uint16_t color, uint16_t bg) +{ + int16_t block_w; + int16_t block_h; + +#if !defined(ATTINY_CORE) + if (gfxFont) // custom font + { + // Character is assumed previously filtered by write() to eliminate + // newlines, returns, non-printable characters, etc. Calling + // drawChar() directly with 'bad' characters of font may cause mayhem! + + c -= (uint8_t)pgm_read_byte(&gfxFont->first); + GFXglyph *glyph = pgm_read_glyph_ptr(gfxFont, c); + uint8_t *bitmap = pgm_read_bitmap_ptr(gfxFont); + + uint16_t bo = pgm_read_word(&glyph->bitmapOffset); + uint8_t w = pgm_read_byte(&glyph->width), + h = pgm_read_byte(&glyph->height), + xAdvance = pgm_read_byte(&glyph->xAdvance), + yAdvance = pgm_read_byte(&gfxFont->yAdvance), + baseline = yAdvance * 2 / 3; // TODO: baseline is an arbitrary currently, may be define in font file + int8_t xo = pgm_read_byte(&glyph->xOffset), + yo = pgm_read_byte(&glyph->yOffset); + uint8_t xx, yy, bits = 0, bit = 0; + int16_t xo16 = xo, yo16 = yo; + + if (xAdvance < w) + { + xAdvance = w; // Don't know why it exists + } + + block_w = xAdvance * textsize_x; + block_h = yAdvance * textsize_y; + if ( + (x > _max_x) || // Clip right + (y > _max_y) || // Clip bottom + ((x + block_w - 1) < 0) || // Clip left + ((y + block_h - 1) < 0) // Clip top + ) + { + return; + } + + // NOTE: Different from Adafruit_GFX design, Adruino_GFX also cater background. + // Since it may introduce many ugly output, it should limited using on mono font only. + startWrite(); + if (bg != color) // have background color + { + writeFillRect(x, y - (baseline * textsize_y), block_w, block_h, bg); + } + for (yy = 0; yy < h; yy++) + { + for (xx = 0; xx < w; xx++) + { + if (!(bit++ & 7)) + { + bits = pgm_read_byte(&bitmap[bo++]); + } + if (bits & 0x80) + { + if (textsize_x == 1 && textsize_y == 1) + { + writePixel(x + xo + xx, y + yo + yy, color); + } + else + { + writeFillRect(x + (xo16 + xx) * textsize_x, y + (yo16 + yy) * textsize_y, + textsize_x - text_pixel_margin, textsize_y - text_pixel_margin, color); + } + } + bits <<= 1; + } + } + endWrite(); + } + else // 'Classic' built-in font +#endif // !defined(ATTINY_CORE) +#if defined(U8G2_FONT_SUPPORT) + if (u8g2Font) + { + if ((_u8g2_decode_ptr) && (_u8g2_char_width > 0)) + { + uint8_t a, b; + + _u8g2_target_x = x + (_u8g2_char_x * textsize_x); + _u8g2_target_y = y - ((_u8g2_char_height + _u8g2_char_y) * textsize_y); + // log_d("_u8g2_target_x: %d, _u8g2_target_y: %d", _u8g2_target_x, _u8g2_target_y); + + /* reset local x/y position */ + _u8g2_dx = 0; + _u8g2_dy = 0; + /* decode glyph */ + startWrite(); + for (;;) + { + a = u8g2_font_decode_get_unsigned_bits(_u8g2_bits_per_0); + b = u8g2_font_decode_get_unsigned_bits(_u8g2_bits_per_1); + // log_d("a: %d, b: %d", a, b); + do + { + u8g2_font_decode_len(a, 0, color, bg); + u8g2_font_decode_len(b, 1, color, bg); + } while (u8g2_font_decode_get_unsigned_bits(1) != 0); + + if (_u8g2_dy >= _u8g2_char_height) + break; + } + endWrite(); + } + } + else // glcdfont +#endif // defined(U8G2_FONT_SUPPORT) + { + block_w = 6 * textsize_x; + block_h = 8 * textsize_y; + if ( + (x > _max_x) || // Clip right + (y > _max_y) || // Clip bottom + ((x + block_w - 1) < 0) || // Clip left + ((y + block_h - 1) < 0) // Clip top + ) + { + return; + } + + startWrite(); + for (int8_t i = 0; i < 5; i++) + { // Char bitmap = 5 columns + uint8_t line = pgm_read_byte(&font[c * 5 + i]); + for (int8_t j = 0; j < 8; j++, line >>= 1) + { + if (line & 1) + { + if (textsize_x == 1 && textsize_y == 1) + { + writePixel(x + i, y + j, color); + } + else + { + if (text_pixel_margin > 0) + { + writeFillRect(x + (i * textsize_x), y + j * textsize_y, textsize_x - text_pixel_margin, textsize_y - text_pixel_margin, color); + writeFillRect(x + ((i + 1) * textsize_x) - text_pixel_margin, y + j * textsize_y, text_pixel_margin, textsize_y, bg); + writeFillRect(x + (i * textsize_x), y + ((j + 1) * textsize_y) - text_pixel_margin, textsize_x - text_pixel_margin, text_pixel_margin, bg); + } + else + { + writeFillRect(x + i * textsize_x, y + j * textsize_y, textsize_x, textsize_y, color); + } + } + } + else if (bg != color) + { + if (textsize_x == 1 && textsize_y == 1) + { + writePixel(x + i, y + j, bg); + } + else + { + writeFillRect(x + i * textsize_x, y + j * textsize_y, textsize_x, textsize_y, bg); + } + } + } + } + if (bg != color) + { // If opaque, draw vertical line for last column + if (textsize_x == 1 && textsize_y == 1) + { + writeFastVLine(x + 5, y, 8, bg); + } + else + { + writeFillRect(x + 5 * textsize_x, y, textsize_x, 8 * textsize_y, bg); + } + } + endWrite(); + } +} + +/**************************************************************************/ +/*! + @brief Print one byte/character of data, used to support print() + @param c The 8-bit ascii character to write +*/ +/**************************************************************************/ +size_t Arduino_GFX::write(uint8_t c) +{ +#if !defined(ATTINY_CORE) + if (gfxFont) // custom font + { + if (c == '\n') // Newline + { + cursor_x = 0; // Reset x to zero, advance y by one line + cursor_y += (int16_t)textsize_y * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); + } + else if (c != '\r') // Not a carriage return; is normal char + { + uint8_t first = pgm_read_byte(&gfxFont->first), + last = pgm_read_byte(&gfxFont->last); + if ((c >= first) && (c <= last)) // Char present in this font? + { + GFXglyph *glyph = pgm_read_glyph_ptr(gfxFont, c - first); + uint8_t gw = pgm_read_byte(&glyph->width), + xa = pgm_read_byte(&glyph->xAdvance); + int16_t xo = pgm_read_byte(&glyph->xOffset); + if (wrap && ((cursor_x + ((xo + gw) * textsize_x) - 1) > _max_x)) + { + cursor_x = 0; // Reset x to zero, advance y by one line + cursor_y += (int16_t)textsize_y * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); + } + drawChar(cursor_x, cursor_y, c, textcolor, textbgcolor); + cursor_x += (int16_t)textsize_x * xa; + } + } + } + else // not gfxFont +#endif // !defined(ATTINY_CORE) +#if defined(U8G2_FONT_SUPPORT) + if (u8g2Font) + { + _u8g2_decode_ptr = 0; + + if (_enableUTF8Print) + { + if (_utf8_state == 0) + { + if (c >= 0xfc) /* 6 byte sequence */ + { + _utf8_state = 5; + c &= 1; + } + else if (c >= 0xf8) + { + _utf8_state = 4; + c &= 3; + } + else if (c >= 0xf0) + { + _utf8_state = 3; + c &= 7; + } + else if (c >= 0xe0) + { + _utf8_state = 2; + c &= 15; + } + else if (c >= 0xc0) + { + _utf8_state = 1; + c &= 0x01f; + } + _encoding = c; + } + else + { + _utf8_state--; + /* The case b < 0x080 (an illegal UTF8 encoding) is not checked here. */ + _encoding <<= 6; + c &= 0x03f; + _encoding |= c; + } + } // _enableUTF8Print + else + { + _encoding = c; + } + + if (_utf8_state == 0) + { + if (_encoding == '\n') + { + cursor_x = 0; + cursor_y += (int16_t)textsize_y * _u8g2_max_char_height; + } + else if (_encoding != '\r') + { // Ignore carriage returns + uint8_t *font = u8g2Font; + const uint8_t *glyph_data = 0; + + // extract from u8g2_font_get_glyph_data() + font += 23; // U8G2_FONT_DATA_STRUCT_SIZE + if (_encoding <= 255) + { + if (_encoding >= 'a') + { + font += _u8g2_start_pos_lower_a; + } + else if (_encoding >= 'A') + { + font += _u8g2_start_pos_upper_A; + } + + for (;;) + { + if (pgm_read_byte(font + 1) == 0) + break; + if (pgm_read_byte(font) == _encoding) + { + glyph_data = font + 2; /* skip encoding and glyph size */ + } + font += pgm_read_byte(font + 1); + } + } +#ifdef U8G2_WITH_UNICODE + else + { + uint16_t e; + font += _u8g2_start_pos_unicode; + const uint8_t *unicode_lookup_table = font; + + /* issue 596: search for the glyph start in the unicode lookup table */ + do + { + font += u8g2_font_get_word(unicode_lookup_table, 0); + e = u8g2_font_get_word(unicode_lookup_table, 2); + unicode_lookup_table += 4; + } while (e < _encoding); + + for (;;) + { + e = u8g2_font_get_word(font, 0); + + if (e == 0) + break; + + if (e == _encoding) + { + glyph_data = font + 3; /* skip encoding and glyph size */ + break; + } + font += pgm_read_byte(font + 2); + } + } +#endif + + if (glyph_data) + { + // u8g2_font_decode_glyph + _u8g2_decode_ptr = glyph_data; + _u8g2_decode_bit_pos = 0; + + _u8g2_char_width = u8g2_font_decode_get_unsigned_bits(_u8g2_bits_per_char_width); + _u8g2_char_height = u8g2_font_decode_get_unsigned_bits(_u8g2_bits_per_char_height); + _u8g2_char_x = u8g2_font_decode_get_signed_bits(_u8g2_bits_per_char_x); + _u8g2_char_y = u8g2_font_decode_get_signed_bits(_u8g2_bits_per_char_y); + _u8g2_delta_x = u8g2_font_decode_get_signed_bits(_u8g2_bits_per_delta_x); + // log_d("c: %c, _encoding: %d, _u8g2_char_width: %d, _u8g2_char_height: %d, _u8g2_char_x: %d, _u8g2_char_y: %d, _u8g2_delta_x: %d", + // c, _encoding, _u8g2_char_width, _u8g2_char_height, _u8g2_char_x, _u8g2_char_y, _u8g2_delta_x); + + if (_u8g2_char_width > 0) + { + if (wrap && ((cursor_x + (textsize_x * _u8g2_char_width) - 1) > _max_x)) + { + cursor_x = 0; + cursor_y += (int16_t)textsize_y * _u8g2_max_char_height; + } + } + + drawChar(cursor_x, cursor_y, c, textcolor, textbgcolor); + cursor_x += (int16_t)textsize_x * _u8g2_delta_x; + } + } + } + } + else // glcdfont +#endif // defined(U8G2_FONT_SUPPORT) + { + if (c == '\n') + { // Newline? + cursor_x = 0; // Reset x to zero, + cursor_y += textsize_y * 8; // advance y one line + } + else if (c != '\r') // Normal char; ignore carriage returns + { + if (wrap && ((cursor_x + (textsize_x * 6) - 1) > _max_x)) // Off right? + { + cursor_x = 0; // Reset x to zero, + cursor_y += textsize_y * 8; // advance y one line + } + drawChar(cursor_x, cursor_y, c, textcolor, textbgcolor); + cursor_x += textsize_x * 6; // Advance x one char + } + } + return 1; +} + +/**************************************************************************/ +/*! + @brief Set text 'magnification' size. Each increase in s makes 1 pixel that much bigger. + @param s Desired text size. 1 is default 6x8, 2 is 12x16, 3 is 18x24, etc +*/ +/**************************************************************************/ +void Arduino_GFX::setTextSize(uint8_t s) +{ + setTextSize(s, s, 0); +} + +/**************************************************************************/ +/*! + @brief Set text 'magnification' size. Each increase in s makes 1 pixel that much bigger. + @param s_x Desired text width magnification level in X-axis. 1 is default + @param s_y Desired text width magnification level in Y-axis. 1 is default +*/ +/**************************************************************************/ +void Arduino_GFX::setTextSize(uint8_t s_x, uint8_t s_y) +{ + setTextSize(s_x, s_y, 0); +} + +/**************************************************************************/ +/*! + @brief Set text 'magnification' size. Each increase in s makes 1 pixel that much bigger. + @param s_x Desired text width magnification level in X-axis. 1 is default + @param s_y Desired text width magnification level in Y-axis. 1 is default + @param pixel_margin Margin for each text pixel. 0 is default +*/ +/**************************************************************************/ +void Arduino_GFX::setTextSize(uint8_t s_x, uint8_t s_y, uint8_t pixel_margin) +{ + text_pixel_margin = ((pixel_margin < s_x) && (pixel_margin < s_y)) ? pixel_margin : 0; + textsize_x = (s_x > 0) ? s_x : 1; + textsize_y = (s_y > 0) ? s_y : 1; +} + +/**************************************************************************/ +/*! + @brief Set rotation setting for display + @param r 0 thru 3 corresponding to 4 cardinal rotations +*/ +/**************************************************************************/ +void Arduino_GFX::setRotation(uint8_t r) +{ + _rotation = (r & 7); + switch (_rotation) + { + case 7: + case 5: + case 3: + case 1: + _width = HEIGHT; + _height = WIDTH; + _max_x = _width - 1; ///< x zero base bound + _max_y = _height - 1; ///< y zero base bound + break; + case 6: + case 4: + case 2: + default: // case 0: + _width = WIDTH; + _height = HEIGHT; + _max_x = _width - 1; ///< x zero base bound + _max_y = _height - 1; ///< y zero base bound + break; + } +} + +#if !defined(ATTINY_CORE) +/**************************************************************************/ +/*! + @brief Set the font to display when print()ing, either custom or default + @param f The GFXfont object, if NULL use built in 6x8 font +*/ +/**************************************************************************/ +void Arduino_GFX::setFont(const GFXfont *f) +{ + gfxFont = (GFXfont *)f; +#if defined(U8G2_FONT_SUPPORT) + u8g2Font = NULL; +#endif // defined(U8G2_FONT_SUPPORT) +} + +/**************************************************************************/ +/*! + @brief flush framebuffer to output (for Canvas or NeoPixel sub-class) +*/ +/**************************************************************************/ +void Arduino_GFX::flush() +{ +} +#endif // !defined(ATTINY_CORE) + +#if defined(U8G2_FONT_SUPPORT) +void Arduino_GFX::setFont(const uint8_t *font) +{ + gfxFont = NULL; + u8g2Font = (uint8_t *)font; + + // extract from u8g2_read_font_info() + /* offset 0 */ + _u8g2_glyph_cnt = pgm_read_byte(font); + // uint8_t bbx_mode = pgm_read_byte(font + 1); + _u8g2_bits_per_0 = pgm_read_byte(font + 2); + _u8g2_bits_per_1 = pgm_read_byte(font + 3); + // log_d("_u8g2_glyph_cnt: %d, bbx_mode: %d, _u8g2_bits_per_0: %d, _u8g2_bits_per_1: %d", + // _u8g2_glyph_cnt, bbx_mode, _u8g2_bits_per_0, _u8g2_bits_per_1); + + /* offset 4 */ + _u8g2_bits_per_char_width = pgm_read_byte(font + 4); + _u8g2_bits_per_char_height = pgm_read_byte(font + 5); + _u8g2_bits_per_char_x = pgm_read_byte(font + 6); + _u8g2_bits_per_char_y = pgm_read_byte(font + 7); + _u8g2_bits_per_delta_x = pgm_read_byte(font + 8); + // log_d("_u8g2_bits_per_char_width: %d, _u8g2_bits_per_char_height: %d, _u8g2_bits_per_char_x: %d, _u8g2_bits_per_char_y: %d, _u8g2_bits_per_delta_x: %d", + // _u8g2_bits_per_char_width, _u8g2_bits_per_char_height, _u8g2_bits_per_char_x, _u8g2_bits_per_char_y, _u8g2_bits_per_delta_x); + + /* offset 9 */ + _u8g2_max_char_width = pgm_read_byte(font + 9); + _u8g2_max_char_height = pgm_read_byte(font + 10); + // int8_t x_offset = pgm_read_byte(font + 11); + // int8_t y_offset = pgm_read_byte(font + 12); + // log_d("_u8g2_max_char_width: %d, _u8g2_max_char_height: %d, x_offset: %d, y_offset: %d", + // _u8g2_max_char_width, _u8g2_max_char_height, x_offset, y_offset); + + /* offset 13 */ + // int8_t ascent_A = pgm_read_byte(font + 13); + // int8_t descent_g = pgm_read_byte(font + 14); + // int8_t ascent_para = pgm_read_byte(font + 15); + // int8_t descent_para = pgm_read_byte(font + 16); + // log_d("ascent_A: %d, descent_g: %d, ascent_para: %d, descent_para: %d", + // ascent_A, descent_g, ascent_para, descent_para); + + /* offset 17 */ + _u8g2_start_pos_upper_A = u8g2_font_get_word(font, 17); + _u8g2_start_pos_lower_a = u8g2_font_get_word(font, 19); +#ifdef U8G2_WITH_UNICODE + _u8g2_start_pos_unicode = u8g2_font_get_word(font, 21); +#endif + _u8g2_first_char = pgm_read_byte(font + 23); + // log_d("_u8g2_start_pos_upper_A: %d, _u8g2_start_pos_lower_a: %d, _u8g2_start_pos_unicode: %d, _u8g2_first_char: %d", + // _u8g2_start_pos_upper_A, _u8g2_start_pos_lower_a, _u8g2_start_pos_unicode, _u8g2_first_char); +} + +void Arduino_GFX::setUTF8Print(bool isEnable) +{ + _enableUTF8Print = isEnable; +} +#endif // defined(U8G2_FONT_SUPPORT) + +/**************************************************************************/ +/*! + @brief Helper to determine size of a character with current font/size. + Broke this out as it's used by both the PROGMEM- and RAM-resident getTextBounds() functions. + @param c The ascii character in question + @param x Pointer to x location of character + @param y Pointer to y location of character + @param minx Minimum clipping value for X + @param miny Minimum clipping value for Y + @param maxx Maximum clipping value for X + @param maxy Maximum clipping value for Y +*/ +/**************************************************************************/ +void Arduino_GFX::charBounds(char c, int16_t *x, int16_t *y, + int16_t *minx, int16_t *miny, int16_t *maxx, int16_t *maxy) +{ +#if !defined(ATTINY_CORE) + if (gfxFont) // custom font + { + if (c == '\n') // Newline + { + *x = 0; // Reset x to zero, advance y by one line + *y += (int16_t)textsize_y * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); + } + else if (c != '\r') // Not a carriage return; is normal char + { + uint8_t first = pgm_read_byte(&gfxFont->first), + last = pgm_read_byte(&gfxFont->last); + if ((c >= first) && (c <= last)) // Char present in this font? + { + GFXglyph *glyph = pgm_read_glyph_ptr(gfxFont, c - first); + uint8_t gw = pgm_read_byte(&glyph->width), + gh = pgm_read_byte(&glyph->height), + xa = pgm_read_byte(&glyph->xAdvance); + int8_t xo = pgm_read_byte(&glyph->xOffset), + yo = pgm_read_byte(&glyph->yOffset); + if (wrap && ((*x + ((xo + gw) * textsize_x) - 1) > _max_x)) + { + *x = 0; // Reset x to zero, advance y by one line + *y += (int16_t)textsize_y * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); + } + int16_t x1 = *x + ((int16_t)xo * textsize_x), + y1 = *y + ((int16_t)yo * textsize_y), + x2 = x1 + ((int16_t)gw * textsize_x) - 1, + y2 = y1 + ((int16_t)gh * textsize_y) - 1; + if (x1 < *minx) + { + *minx = x1; + } + if (y1 < *miny) + { + *miny = y1; + } + if (x2 > *maxx) + { + *maxx = x2; + } + if (y2 > *maxy) + { + *maxy = y2; + } + *x += (int16_t)textsize_x * xa; + } + } + } + else // not gfxFont +#endif // !defined(ATTINY_CORE) +#if defined(U8G2_FONT_SUPPORT) + if (u8g2Font) + { + _u8g2_decode_ptr = 0; + + if (_enableUTF8Print) + { + if (_utf8_state == 0) + { + if (c >= 0xfc) /* 6 byte sequence */ + { + _utf8_state = 5; + c &= 1; + } + else if (c >= 0xf8) + { + _utf8_state = 4; + c &= 3; + } + else if (c >= 0xf0) + { + _utf8_state = 3; + c &= 7; + } + else if (c >= 0xe0) + { + _utf8_state = 2; + c &= 15; + } + else if (c >= 0xc0) + { + _utf8_state = 1; + c &= 0x01f; + } + _encoding = c; + } + else + { + _utf8_state--; + /* The case b < 0x080 (an illegal UTF8 encoding) is not checked here. */ + _encoding <<= 6; + c &= 0x03f; + _encoding |= c; + } + } // _enableUTF8Print + else + { + _encoding = c; + } + + if (_utf8_state == 0) + { + if (_encoding == '\n') + { + *x = 0; + *y += (int16_t)textsize_y * _u8g2_max_char_height; + } + else if (_encoding != '\r') + { // Ignore carriage returns + uint8_t *font = u8g2Font; + const uint8_t *glyph_data = 0; + + // extract from u8g2_font_get_glyph_data() + font += 23; // U8G2_FONT_DATA_STRUCT_SIZE + if (_encoding <= 255) + { + if (_encoding >= 'a') + { + font += _u8g2_start_pos_lower_a; + } + else if (_encoding >= 'A') + { + font += _u8g2_start_pos_upper_A; + } + + for (;;) + { + if (pgm_read_byte(font + 1) == 0) + break; + if (pgm_read_byte(font) == _encoding) + { + glyph_data = font + 2; /* skip encoding and glyph size */ + } + font += pgm_read_byte(font + 1); + } + } +#ifdef U8G2_WITH_UNICODE + else + { + uint16_t e; + font += _u8g2_start_pos_unicode; + const uint8_t *unicode_lookup_table = font; + + /* issue 596: search for the glyph start in the unicode lookup table */ + do + { + font += u8g2_font_get_word(unicode_lookup_table, 0); + e = u8g2_font_get_word(unicode_lookup_table, 2); + unicode_lookup_table += 4; + } while (e < _encoding); + + for (;;) + { + e = u8g2_font_get_word(font, 0); + + if (e == 0) + break; + + if (e == _encoding) + { + glyph_data = font + 3; /* skip encoding and glyph size */ + break; + } + font += pgm_read_byte(font + 2); + } + } +#endif + + if (glyph_data) + { + // u8g2_font_decode_glyph + _u8g2_decode_ptr = glyph_data; + _u8g2_decode_bit_pos = 0; + + _u8g2_char_width = u8g2_font_decode_get_unsigned_bits(_u8g2_bits_per_char_width); + _u8g2_char_height = u8g2_font_decode_get_unsigned_bits(_u8g2_bits_per_char_height); + _u8g2_char_x = u8g2_font_decode_get_signed_bits(_u8g2_bits_per_char_x); + _u8g2_char_y = u8g2_font_decode_get_signed_bits(_u8g2_bits_per_char_y); + _u8g2_delta_x = u8g2_font_decode_get_signed_bits(_u8g2_bits_per_delta_x); + // log_d("c: %c, _encoding: %d, _u8g2_char_width: %d, _u8g2_char_height: %d, _u8g2_char_x: %d, _u8g2_char_y: %d, _u8g2_delta_x: %d", + // c, _encoding, _u8g2_char_width, _u8g2_char_height, _u8g2_char_x, _u8g2_char_y, _u8g2_delta_x); + + if (_u8g2_char_width > 0) + { + if (wrap && ((*x + (textsize_x * _u8g2_char_width) - 1) > _max_x)) + { + *x = 0; + *y += (int16_t)textsize_y * _u8g2_max_char_height; + } + } + + int16_t x1 = *x + ((int16_t)_u8g2_char_x * textsize_x), + y1 = *y - (((int16_t)_u8g2_char_height + _u8g2_char_y) * textsize_y), + x2 = x1 + ((int16_t)_u8g2_char_width * textsize_x) - 1, + y2 = y1 + ((int16_t)_u8g2_char_height * textsize_y) - 1; + if (x1 < *minx) + { + *minx = x1; + } + if (y1 < *miny) + { + *miny = y1; + } + if (x2 > *maxx) + { + *maxx = x2; + } + if (y2 > *maxy) + { + *maxy = y2; + } + *x += (int16_t)textsize_x * _u8g2_delta_x; + } + } + } + } + else // glcdfont +#endif // defined(U8G2_FONT_SUPPORT) + { + if (c == '\n') + { // Newline? + *x = 0; // Reset x to zero, + *y += textsize_y * 8; // advance y one line + // min/max x/y unchaged -- that waits for next 'normal' character + } + else if (c != '\r') // Normal char; ignore carriage returns + { + if (wrap && ((*x + (textsize_x * 6) - 1) > _max_x)) // Off right? + { + *x = 0; // Reset x to zero, + *y += textsize_y * 8; // advance y one line + } + int16_t x2 = *x + textsize_x * 6 - 1; // Lower-right pixel of char + int16_t y2 = *y + textsize_y * 8 - 1; + if (x2 > *maxx) + { + *maxx = x2; // Track max x, y + } + if (y2 > *maxy) + { + *maxy = y2; + } + if (*x < *minx) + { + *minx = *x; // Track min x, y + } + if (*y < *miny) + { + *miny = *y; + } + *x += textsize_x * 6; // Advance x one char + } + } +} + +/**************************************************************************/ +/*! + @brief Helper to determine size of a string with current font/size. Pass string and a cursor position, returns UL corner and W,H. + @param str The ascii string to measure + @param x The current cursor X + @param y The current cursor Y + @param x1 The boundary X coordinate, set by function + @param y1 The boundary Y coordinate, set by function + @param w The boundary width, set by function + @param h The boundary height, set by function +*/ +/**************************************************************************/ +void Arduino_GFX::getTextBounds(const char *str, int16_t x, int16_t y, + int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h) +{ + uint8_t c; // Current character + + *x1 = x; + *y1 = y; + *w = *h = 0; + + int16_t minx = _width, miny = _height, maxx = -1, maxy = -1; + + while ((c = *str++)) + { + charBounds(c, &x, &y, &minx, &miny, &maxx, &maxy); + } + + if (maxx >= minx) + { + *x1 = minx; + *w = maxx - minx + 1; + } + if (maxy >= miny) + { + *y1 = miny; + *h = maxy - miny + 1; + } +} + +/**************************************************************************/ +/*! + @brief Helper to determine size of a string with current font/size. Pass string and a cursor position, returns UL corner and W,H. + @param str The ascii string to measure (as an arduino String() class) + @param x The current cursor X + @param y The current cursor Y + @param x1 The boundary X coordinate, set by function + @param y1 The boundary Y coordinate, set by function + @param w The boundary width, set by function + @param h The boundary height, set by function +*/ +/**************************************************************************/ +void Arduino_GFX::getTextBounds(const String &str, int16_t x, int16_t y, + int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h) +{ + if (str.length() != 0) + { + getTextBounds(const_cast(str.c_str()), x, y, x1, y1, w, h); + } +} + +/**************************************************************************/ +/*! + @brief Helper to determine size of a PROGMEM string with current font/size. Pass string and a cursor position, returns UL corner and W,H. + @param str The flash-memory ascii string to measure + @param x The current cursor X + @param y The current cursor Y + @param x1 The boundary X coordinate, set by function + @param y1 The boundary Y coordinate, set by function + @param w The boundary width, set by function + @param h The boundary height, set by function +*/ +/**************************************************************************/ +void Arduino_GFX::getTextBounds(const __FlashStringHelper *str, + int16_t x, int16_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h) +{ + uint8_t *s = (uint8_t *)str, c; + + *x1 = x; + *y1 = y; + *w = *h = 0; + + int16_t minx = _width, miny = _height, maxx = -1, maxy = -1; + + while ((c = pgm_read_byte(s++))) + charBounds(c, &x, &y, &minx, &miny, &maxx, &maxy); + + if (maxx >= minx) + { + *x1 = minx; + *w = maxx - minx + 1; + } + if (maxy >= miny) + { + *y1 = miny; + *h = maxy - miny + 1; + } +} + +/**************************************************************************/ +/*! + @brief Invert the display (ideally using built-in hardware command) + @param i True if you want to invert, false to make 'normal' +*/ +/**************************************************************************/ +void Arduino_GFX::invertDisplay(bool i) +{ + // Do nothing, must be subclassed if supported by hardware + UNUSED(i); +} + +/**************************************************************************/ +/*! + @brief Turn on display after turned off +*/ +/**************************************************************************/ +void Arduino_GFX::displayOn() +{ +} + +/**************************************************************************/ +/*! + @brief Turn off display +*/ +/**************************************************************************/ +void Arduino_GFX::displayOff() +{ +} diff --git a/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/Arduino_GFX.h b/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/Arduino_GFX.h new file mode 100644 index 00000000..596c99ef --- /dev/null +++ b/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/Arduino_GFX.h @@ -0,0 +1,447 @@ +/* + * start rewrite from: + * https://github.com/adafruit/Adafruit-GFX-Library.git + */ +#ifndef _ARDUINO_GFX_H_ +#define _ARDUINO_GFX_H_ + +#include +#include +#include "Arduino_G.h" +#include "Arduino_DataBus.h" + +#if !defined(ATTINY_CORE) +#include "gfxfont.h" +#endif // !defined(ATTINY_CORE) + +#ifndef DEGTORAD +#define DEGTORAD 0.017453292519943295769236907684886F +#endif + + +#define RGB565(r, g, b) ((((r)&0xF8) << 8) | (((g)&0xFC) << 3) | ((b) >> 3)) +#define RGB16TO24(c) ((((uint32_t)c & 0xF800) << 8) | ((c & 0x07E0) << 5) | ((c & 0x1F) << 3)) + +#define RGB565_BLACK RGB565(0, 0, 0) +#define RGB565_NAVY RGB565(0, 0, 123) +#define RGB565_DARKGREEN RGB565(0, 125, 0) +#define RGB565_DARKCYAN RGB565(0, 125, 123) +#define RGB565_MAROON RGB565(123, 0, 0) +#define RGB565_PURPLE RGB565(123, 0, 123) +#define RGB565_OLIVE RGB565(123, 125, 0) +#define RGB565_LIGHTGREY RGB565(198, 195, 198) +#define RGB565_DARKGREY RGB565(123, 125, 123) +#define RGB565_BLUE RGB565(0, 0, 255) +#define RGB565_GREEN RGB565(0, 255, 0) +#define RGB565_CYAN RGB565(0, 255, 255) +#define RGB565_RED RGB565(255, 0, 0) +#define RGB565_MAGENTA RGB565(255, 0, 255) +#define RGB565_YELLOW RGB565(255, 255, 0) +#define RGB565_WHITE RGB565(255, 255, 255) +#define RGB565_ORANGE RGB565(255, 165, 0) +#define RGB565_GREENYELLOW RGB565(173, 255, 41) +#define RGB565_PINK RGB565(255, 130, 198) + +// Color definitions +#ifndef DISABLE_COLOR_DEFINES +#define BLACK RGB565_BLACK +#define NAVY RGB565_NAVY +#define DARKGREEN RGB565_DARKGREEN +#define DARKCYAN RGB565_DARKCYAN +#define MAROON RGB565_MAROON +#define PURPLE RGB565_PURPLE +#define OLIVE RGB565_OLIVE +#define LIGHTGREY RGB565_LIGHTGREY +#define DARKGREY RGB565_DARKGREY +#define BLUE RGB565_BLUE +#define GREEN RGB565_GREEN +#define CYAN RGB565_CYAN +#define RED RGB565_RED +#define MAGENTA RGB565_MAGENTA +#define YELLOW RGB565_YELLOW +#define WHITE RGB565_WHITE +#define ORANGE RGB565_ORANGE +#define GREENYELLOW RGB565_GREENYELLOW +#define PINK RGB565_PINK +#endif + +// Many (but maybe not all) non-AVR board installs define macros +// for compatibility with existing PROGMEM-reading AVR code. +// Do our own checks and defines here for good measure... + +#ifndef pgm_read_byte +#define pgm_read_byte(addr) (*(const unsigned char *)(addr)) +#endif +#ifndef pgm_read_word +#define pgm_read_word(addr) (*(const unsigned short *)(addr)) +#endif +#ifndef pgm_read_dword +#define pgm_read_dword(addr) (*(const unsigned long *)(addr)) +#endif +// workaround of a15 asm compile error +#ifdef ESP8266 +#undef pgm_read_word +#define pgm_read_word(addr) (*(const unsigned short *)(addr)) +#endif + +// Pointers are a peculiar case...typically 16-bit on AVR boards, +// 32 bits elsewhere. Try to accommodate both... + +#if !defined(__INT_MAX__) || (__INT_MAX__ > 0xFFFF) +#define pgm_read_pointer(addr) ((void *)pgm_read_dword(addr)) +#else +#define pgm_read_pointer(addr) ((void *)pgm_read_word(addr)) +#endif + +#ifndef _swap_uint8_t +#define _swap_uint8_t(a, b) \ + { \ + uint8_t t = a; \ + a = b; \ + b = t; \ + } +#endif + +#ifndef _swap_int16_t +#define _swap_int16_t(a, b) \ + { \ + int16_t t = a; \ + a = b; \ + b = t; \ + } +#endif + +#ifndef _diff +#define _diff(a, b) ((a > b) ? (a - b) : (b - a)) +#endif + +#ifndef _ordered_in_range +#define _ordered_in_range(v, a, b) ((a <= v) && (v <= b)) +#endif + +#ifndef _in_range +#define _in_range(v, a, b) ((a > b) ? _ordered_in_range(v, b, a) : _ordered_in_range(v, a, b)) +#endif + +#if !defined(ATTINY_CORE) +INLINE GFXglyph *pgm_read_glyph_ptr(const GFXfont *gfxFont, uint8_t c) +{ +#ifdef __AVR__ + return &(((GFXglyph *)pgm_read_pointer(&gfxFont->glyph))[c]); +#else + // expression in __AVR__ section may generate "dereferencing type-punned pointer will break strict-aliasing rules" warning + // In fact, on other platforms (such as STM32) there is no need to do this pointer magic as program memory may be read in a usual way + // So expression may be simplified + return gfxFont->glyph + c; +#endif //__AVR__ +} + +INLINE uint8_t *pgm_read_bitmap_ptr(const GFXfont *gfxFont) +{ +#ifdef __AVR__ + return (uint8_t *)pgm_read_pointer(&gfxFont->bitmap); +#else + // expression in __AVR__ section generates "dereferencing type-punned pointer will break strict-aliasing rules" warning + // In fact, on other platforms (such as STM32) there is no need to do this pointer magic as program memory may be read in a usual way + // So expression may be simplified + return gfxFont->bitmap; +#endif //__AVR__ +} +#endif // !defined(ATTINY_CORE) + +/// A generic graphics superclass that can handle all sorts of drawing. At a minimum you can subclass and provide drawPixel(). At a maximum you can do a ton of overriding to optimize. Used for any/all Adafruit displays! +#if defined(LITTLE_FOOT_PRINT) +class Arduino_GFX : public Print +#else +class Arduino_GFX : public Print, public Arduino_G +#endif // !defined(LITTLE_FOOT_PRINT) +{ +public: + Arduino_GFX(int16_t w, int16_t h); // Constructor + + // This MUST be defined by the subclass: + virtual bool begin(int32_t speed = GFX_NOT_DEFINED) = 0; + virtual void writePixelPreclipped(int16_t x, int16_t y, uint16_t color) = 0; + + // TRANSACTION API / CORE DRAW API + // These MAY be overridden by the subclass to provide device-specific + // optimized code. Otherwise 'generic' versions are used. + virtual void startWrite(); + virtual void writeFillRectPreclipped(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color); + virtual void writeFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); + virtual void writeFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color); + virtual void writeLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color); + virtual void endWrite(void); + + // CONTROL API + // These MAY be overridden by the subclass to provide device-specific + // optimized code. Otherwise 'generic' versions are used. + virtual void setRotation(uint8_t r); + virtual void invertDisplay(bool i); + virtual void displayOn(); + virtual void displayOff(); + + // BASIC DRAW API + // These MAY be overridden by the subclass to provide device-specific + // optimized code. Otherwise 'generic' versions are used. + // It's good to implement those, even if using transaction API + void writePixel(int16_t x, int16_t y, uint16_t color); + void drawPixel(int16_t x, int16_t y, uint16_t color); + void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); + void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color); + void writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color); + void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color); + void fillScreen(uint16_t color); + void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color); + void drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color); + void drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color); + void fillCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color); + void drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color); + void fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color); + void drawRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h, int16_t radius, uint16_t color); + void fillRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h, int16_t radius, uint16_t color); + void drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w, int16_t h, uint16_t color); + void drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h, uint16_t color); + void drawXBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w, int16_t h, uint16_t color); + void drawGrayscaleBitmap(int16_t x, int16_t y, const uint8_t bitmap[], const uint8_t mask[], int16_t w, int16_t h); + void drawGrayscaleBitmap(int16_t x, int16_t y, uint8_t *bitmap, uint8_t *mask, int16_t w, int16_t h); + void draw16bitRGBBitmap(int16_t x, int16_t y, const uint16_t bitmap[], const uint8_t mask[], int16_t w, int16_t h); + void draw24bitRGBBitmap(int16_t x, int16_t y, const uint8_t bitmap[], const uint8_t mask[], int16_t w, int16_t h); + void draw24bitRGBBitmap(int16_t x, int16_t y, uint8_t *bitmap, uint8_t *mask, int16_t w, int16_t h); + void getTextBounds(const char *string, int16_t x, int16_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h); + void getTextBounds(const __FlashStringHelper *s, int16_t x, int16_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h); + void getTextBounds(const String &str, int16_t x, int16_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h); + void setTextSize(uint8_t s); + void setTextSize(uint8_t sx, uint8_t sy); + void setTextSize(uint8_t sx, uint8_t sy, uint8_t pixel_margin); + +#if !defined(ATTINY_CORE) + void setFont(const GFXfont *f = NULL); +#if defined(U8G2_FONT_SUPPORT) + void setFont(const uint8_t *font); + void setUTF8Print(bool isEnable); + uint16_t u8g2_font_get_word(const uint8_t *font, uint8_t offset); + uint8_t u8g2_font_decode_get_unsigned_bits(uint8_t cnt); + int8_t u8g2_font_decode_get_signed_bits(uint8_t cnt); + void u8g2_font_decode_len(uint8_t len, uint8_t is_foreground, uint16_t color, uint16_t bg); +#endif // defined(U8G2_FONT_SUPPORT) + virtual void flush(void); +#endif // !defined(ATTINY_CORE) + + // adopt from LovyanGFX + void drawEllipse(int16_t x, int16_t y, int16_t rx, int16_t ry, uint16_t color); + void drawEllipseHelper(int32_t x, int32_t y, int32_t rx, int32_t ry, uint8_t cornername, uint16_t color); + void fillEllipse(int16_t x, int16_t y, int16_t rx, int16_t ry, uint16_t color); + void fillEllipseHelper(int32_t x, int32_t y, int32_t rx, int32_t ry, uint8_t cornername, int16_t delta, uint16_t color); + void drawArc(int16_t x, int16_t y, int16_t r1, int16_t r2, float start, float end, uint16_t color); + void fillArc(int16_t x, int16_t y, int16_t r1, int16_t r2, float start, float end, uint16_t color); + void fillArcHelper(int16_t cx, int16_t cy, int16_t oradius, int16_t iradius, float start, float end, uint16_t color); + +// TFT optimization code, too big for ATMEL family +#if defined(LITTLE_FOOT_PRINT) + void writeSlashLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color); + void drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w, int16_t h, uint16_t color, uint16_t bg); + void drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint16_t bg); + void drawGrayscaleBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w, int16_t h); + void drawGrayscaleBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h); + void drawIndexedBitmap(int16_t x, int16_t y, uint8_t *bitmap, uint16_t *color_index, int16_t w, int16_t h, int16_t x_skip = 0); + void drawIndexedBitmap(int16_t x, int16_t y, uint8_t *bitmap, uint16_t *color_index, uint8_t chroma_key, int16_t w, int16_t h, int16_t x_skip = 0); + void draw3bitRGBBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h); + void draw16bitRGBBitmap(int16_t x, int16_t y, uint16_t *bitmap, uint8_t *mask, int16_t w, int16_t h); + void draw16bitRGBBitmap(int16_t x, int16_t y, const uint16_t bitmap[], int16_t w, int16_t h); + void draw16bitRGBBitmap(int16_t x, int16_t y, uint16_t *bitmap, int16_t w, int16_t h); + void draw16bitRGBBitmap(int16_t x, int16_t y, uint16_t *bitmap, uint16_t transparent_color, int16_t w, int16_t h); + void draw16bitBeRGBBitmap(int16_t x, int16_t y, uint16_t *bitmap, int16_t w, int16_t h); + void draw24bitRGBBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w, int16_t h); + void draw24bitRGBBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h); + void drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t bg); +#else // !defined(LITTLE_FOOT_PRINT) + virtual void writeSlashLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color); + virtual void drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w, int16_t h, uint16_t color, uint16_t bg); + virtual void drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint16_t bg); + virtual void drawGrayscaleBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w, int16_t h); + virtual void drawGrayscaleBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h); + virtual void drawIndexedBitmap(int16_t x, int16_t y, uint8_t *bitmap, uint16_t *color_index, int16_t w, int16_t h, int16_t x_skip = 0); + virtual void drawIndexedBitmap(int16_t x, int16_t y, uint8_t *bitmap, uint16_t *color_index, uint8_t chroma_key, int16_t w, int16_t h, int16_t x_skip = 0); + virtual void draw3bitRGBBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h); + virtual void draw16bitRGBBitmap(int16_t x, int16_t y, uint16_t *bitmap, uint8_t *mask, int16_t w, int16_t h); + virtual void draw16bitRGBBitmap(int16_t x, int16_t y, const uint16_t bitmap[], int16_t w, int16_t h); + virtual void draw16bitRGBBitmap(int16_t x, int16_t y, uint16_t *bitmap, int16_t w, int16_t h); + virtual void draw16bitRGBBitmap(int16_t x, int16_t y, uint16_t *bitmap, uint16_t transparent_color, int16_t w, int16_t h); + virtual void draw16bitBeRGBBitmap(int16_t x, int16_t y, uint16_t *bitmap, int16_t w, int16_t h); + virtual void draw24bitRGBBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w, int16_t h); + virtual void draw24bitRGBBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h); + virtual void drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t bg); +#endif // !defined(LITTLE_FOOT_PRINT) + + /**********************************************************************/ + /*! + @brief Set text cursor location + @param x X coordinate in pixels + @param y Y coordinate in pixels + */ + /**********************************************************************/ + void setCursor(int16_t x, int16_t y) + { + cursor_x = x; + cursor_y = y; + } + + /**********************************************************************/ + /*! + @brief Set text font color with transparant background + @param c 16-bit 5-6-5 Color to draw text with + @note For 'transparent' background, background and foreground + are set to same color rather than using a separate flag. + */ + /**********************************************************************/ + void setTextColor(uint16_t c) { textcolor = textbgcolor = c; } + + /**********************************************************************/ + /*! + @brief Set text font color with custom background color + @param c 16-bit 5-6-5 Color to draw text with + @param bg 16-bit 5-6-5 Color to draw background/fill with + */ + /**********************************************************************/ + void setTextColor(uint16_t c, uint16_t bg) + { + textcolor = c; + textbgcolor = bg; + } + + /**********************************************************************/ + /*! + @brief Set whether text that is too long for the screen width should + automatically wrap around to the next line (else clip right). + @param w true for wrapping, false for clipping + */ + /**********************************************************************/ + void setTextWrap(bool w) { wrap = w; } + + virtual size_t write(uint8_t); + + /************************************************************************/ + /*! + @brief Get width of the display, accounting for current rotation + @returns Width in pixels + */ + /************************************************************************/ + int16_t width(void) const { return _width; }; + + /************************************************************************/ + /*! + @brief Get height of the display, accounting for current rotation + @returns Height in pixels + */ + /************************************************************************/ + int16_t height(void) const { return _height; } + + /************************************************************************/ + /*! + @brief Get rotation setting for display + @returns 0 thru 3 corresponding to 4 cardinal rotations + */ + /************************************************************************/ + uint8_t getRotation(void) const { return _rotation; } + + // get current cursor position (get rotation safe maximum values, + // using: width() for x, height() for y) + /************************************************************************/ + /*! + @brief Get text cursor X location + @returns X coordinate in pixels + */ + /************************************************************************/ + int16_t getCursorX(void) const { return cursor_x; } + + /************************************************************************/ + /*! + @brief Get text cursor Y location + @returns Y coordinate in pixels + */ + /************************************************************************/ + int16_t getCursorY(void) const { return cursor_y; }; + + /*! + @brief Given 8-bit red, green and blue values, return a 'packed' + 16-bit color value in '565' RGB format (5 bits red, 6 bits + green, 5 bits blue). This is just a mathematical operation, + no hardware is touched. + @param red 8-bit red brightnesss (0 = off, 255 = max). + @param green 8-bit green brightnesss (0 = off, 255 = max). + @param blue 8-bit blue brightnesss (0 = off, 255 = max). + @return 'Packed' 16-bit color value (565 format). + */ + uint16_t color565(uint8_t red, uint8_t green, uint8_t blue) + { + return ((red & 0xF8) << 8) | ((green & 0xFC) << 3) | (blue >> 3); + } + +protected: + void charBounds(char c, int16_t *x, int16_t *y, int16_t *minx, int16_t *miny, int16_t *maxx, int16_t *maxy); + int16_t + _width, ///< Display width as modified by current rotation + _height, ///< Display height as modified by current rotation + _max_x, ///< x zero base bound (_width - 1) + _max_y, ///< y zero base bound (_height - 1) + cursor_x, ///< x location to start print()ing text + cursor_y; ///< y location to start print()ing text + uint16_t + textcolor, ///< 16-bit background color for print() + textbgcolor; ///< 16-bit text color for print() + uint8_t + textsize_x, ///< Desired magnification in X-axis of text to print() + textsize_y, ///< Desired magnification in Y-axis of text to print() + text_pixel_margin, ///< Margin for each text pixel + _rotation; ///< Display rotation (0 thru 3) + bool + wrap; ///< If set, 'wrap' text at right edge of display +#if !defined(ATTINY_CORE) + GFXfont *gfxFont; ///< Pointer to special font +#endif // !defined(ATTINY_CORE) + +#if defined(U8G2_FONT_SUPPORT) + uint8_t *u8g2Font; + bool _enableUTF8Print = false; + uint8_t _utf8_state = 0; + uint16_t _encoding; + + uint8_t _u8g2_glyph_cnt; + uint8_t _u8g2_bits_per_0; + uint8_t _u8g2_bits_per_1; + uint8_t _u8g2_bits_per_char_width; + uint8_t _u8g2_bits_per_char_height; + uint8_t _u8g2_bits_per_char_x; + uint8_t _u8g2_bits_per_char_y; + uint8_t _u8g2_bits_per_delta_x; + int8_t _u8g2_max_char_width; + int8_t _u8g2_max_char_height; + uint16_t _u8g2_start_pos_upper_A; + uint16_t _u8g2_start_pos_lower_a; + uint16_t _u8g2_start_pos_unicode; + uint8_t _u8g2_first_char; + + uint8_t _u8g2_char_width; + uint8_t _u8g2_char_height; + int8_t _u8g2_char_x; + int8_t _u8g2_char_y; + int8_t _u8g2_delta_x; + + int8_t _u8g2_dx; + int8_t _u8g2_dy; + uint16_t _u8g2_target_x; + uint16_t _u8g2_target_y; + + const uint8_t *_u8g2_decode_ptr; + uint8_t _u8g2_decode_bit_pos; +#endif // defined(U8G2_FONT_SUPPORT) + +#if defined(LITTLE_FOOT_PRINT) + int16_t + WIDTH, ///< This is the 'raw' display width - never changes + HEIGHT; ///< This is the 'raw' display height - never changes +#endif // defined(LITTLE_FOOT_PRINT) +}; + +#endif // _ARDUINO_GFX_H_ diff --git a/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/Arduino_GFX_Library.h b/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/Arduino_GFX_Library.h new file mode 100644 index 00000000..10c709f4 --- /dev/null +++ b/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/Arduino_GFX_Library.h @@ -0,0 +1,208 @@ +#ifndef _ARDUINO_GFX_LIBRARIES_H_ +#define _ARDUINO_GFX_LIBRARIES_H_ + +#include "Arduino_DataBus.h" +#include "databus/Arduino_ESP32RGBPanel.h" +#include "databus/Arduino_XL9535SWSPI.h" + +#include "Arduino_GFX.h" // Core graphics library + +#include "display/Arduino_RGB_Display.h" + +#if defined(ARDUINO_ARCH_SAMD) && defined(SEEED_GROVE_UI_WIRELESS) +#define DISPLAY_DEV_KIT +#define WIO_TERMINAL +#define DF_GFX_CS LCD_SS_PIN +#define DF_GFX_DC LCD_DC +#define DF_GFX_RST GFX_NOT_DEFINED +#define DF_GFX_BL LCD_BACKLIGHT +#elif defined(ARDUINO_ESP32_S3_BOX) +#define DISPLAY_DEV_KIT +#define ESP32_S3_BOX +#define DF_GFX_SCK TFT_CLK +#define DF_GFX_MOSI TFT_MOSI +#define DF_GFX_MISO TFT_MISO +#define DF_GFX_CS TFT_CS +#define DF_GFX_DC TFT_DC +#define DF_GFX_RST TFT_RST +#define DF_GFX_BL TFT_BL +#elif defined(ARDUINO_M5Stack_Core_ESP32) || defined(ARDUINO_M5STACK_FIRE) +#define DISPLAY_DEV_KIT +#define M5STACK_CORE +#define DF_GFX_SCK 18 +#define DF_GFX_MOSI 23 +#define DF_GFX_MISO 19 +#define DF_GFX_CS 14 +#define DF_GFX_DC 27 +#define DF_GFX_RST 33 +#define DF_GFX_BL 32 +#elif defined(ARDUINO_M5Stack_ATOMS3) +#define DISPLAY_DEV_KIT +#define M5STACK_ATOMS3 +#define DF_GFX_SCK 17 +#define DF_GFX_MOSI 21 +#define DF_GFX_MISO GFX_NOT_DEFINED +#define DF_GFX_CS 15 +#define DF_GFX_DC 33 +#define DF_GFX_RST 34 +#define DF_GFX_BL 16 +#elif defined(ARDUINO_ODROID_ESP32) +#define DISPLAY_DEV_KIT +#define ODROID_GO +#define DF_GFX_SCK 18 +#define DF_GFX_MOSI 23 +#define DF_GFX_MISO 19 +#define DF_GFX_CS 5 +#define DF_GFX_DC 21 +#define DF_GFX_RST GFX_NOT_DEFINED +#define DF_GFX_BL 14 +/* TTGO T-Watch */ +#elif defined(ARDUINO_T) || defined(ARDUINO_TWATCH_BASE) || defined(ARDUINO_TWATCH_2020_V1) || defined(ARDUINO_TWATCH_2020_V2) +#define DISPLAY_DEV_KIT +#define TTGO_T_WATCH +#define DF_GFX_SCK 18 +#define DF_GFX_MOSI 19 +#define DF_GFX_MISO GFX_NOT_DEFINED +#define DF_GFX_CS 5 +#define DF_GFX_DC 27 +#define DF_GFX_RST GFX_NOT_DEFINED +#define DF_GFX_BL 12 +/* Waveshare RP2040-LCD-1.28 */ +#elif defined(ARDUINO_WAVESHARE_RP2040_LCD_1_28) +#define DISPLAY_DEV_KIT +#define WAVESHARE_RP2040_LCD_1_28 +#define DF_GFX_SCK 10 +#define DF_GFX_MOSI 11 +#define DF_GFX_MISO 12 +#define DF_GFX_CS 9 +#define DF_GFX_DC 8 +#define DF_GFX_RST 12 +#define DF_GFX_BL 25 +#define DF_GFX_SPI spi1 +#elif defined(ARDUINO_ARCH_NRF52840) +#define DF_GFX_SCK 13 +#define DF_GFX_MOSI 11 +#define DF_GFX_MISO 12 +#define DF_GFX_CS 9 +#define DF_GFX_DC 8 +#define DF_GFX_RST 7 +#define DF_GFX_BL 6 +#elif defined(__IMXRT1052__) || defined(__IMXRT1062__) +// PJRC Teensy 4.x +#define DF_GFX_SCK 13 +#define DF_GFX_MOSI 11 +#define DF_GFX_MISO 12 +#define DF_GFX_CS 39 // GFX_NOT_DEFINED for display without CS pin +#define DF_GFX_DC 41 +#define DF_GFX_RST 40 +#define DF_GFX_BL 22 +#elif defined(ARDUINO_BLACKPILL_F411CE) +#define DF_GFX_SCK 5 +#define DF_GFX_MOSI 7 +#define DF_GFX_MISO 6 +#define DF_GFX_CS 4 +#define DF_GFX_DC 3 +#define DF_GFX_RST 2 +#define DF_GFX_BL 1 +#elif defined(TARGET_RP2040) +#define DF_GFX_SCK 18 +#define DF_GFX_MOSI 19 +#define DF_GFX_MISO 16 +#define DF_GFX_CS 17 +#define DF_GFX_DC 27 +#define DF_GFX_RST 26 +#define DF_GFX_BL 28 +#define DF_GFX_SPI spi0 +#elif defined(ESP32) && (CONFIG_IDF_TARGET_ESP32) +#define DF_GFX_SCK 18 +#define DF_GFX_MOSI 23 +#define DF_GFX_MISO GFX_NOT_DEFINED +#define DF_GFX_CS 5 +#define DF_GFX_DC 27 +#define DF_GFX_RST 33 +#define DF_GFX_BL 22 +#elif defined(ESP32) && (CONFIG_IDF_TARGET_ESP32S2) +#define DF_GFX_SCK 36 +#define DF_GFX_MOSI 35 +#define DF_GFX_MISO GFX_NOT_DEFINED +#define DF_GFX_CS 34 +#define DF_GFX_DC 38 +#define DF_GFX_RST 33 +#define DF_GFX_BL 21 +#elif defined(ESP32) && (CONFIG_IDF_TARGET_ESP32S3) +#define DF_GFX_SCK 36 +#define DF_GFX_MOSI 35 +#define DF_GFX_MISO GFX_NOT_DEFINED +#define DF_GFX_CS 40 +#define DF_GFX_DC 41 +#define DF_GFX_RST 42 +#define DF_GFX_BL 48 +#elif defined(ESP32) && (CONFIG_IDF_TARGET_ESP32C3) +#define DF_GFX_SCK 4 +#define DF_GFX_MOSI 6 +#define DF_GFX_MISO GFX_NOT_DEFINED +#define DF_GFX_CS 7 +#define DF_GFX_DC 2 +#define DF_GFX_RST 1 +#define DF_GFX_BL 3 +#elif defined(ESP8266) +#define DF_GFX_SCK 14 +#define DF_GFX_MOSI 13 +#define DF_GFX_MISO 12 +#define DF_GFX_CS 15 +#define DF_GFX_DC 4 +#define DF_GFX_RST 2 +#define DF_GFX_BL 5 +#elif defined(RTL8722DM) +#if defined(BOARD_RTL8720DN_BW16) +#define DF_GFX_SCK 10 +#define DF_GFX_MOSI 12 +#define DF_GFX_MISO 11 +#define DF_GFX_CS 9 +#define DF_GFX_DC 8 +#define DF_GFX_RST 6 +#define DF_GFX_BL 3 +#elif defined(BOARD_RTL8722DM) +#define DF_GFX_SCK 13 +#define DF_GFX_MOSI 11 +#define DF_GFX_MISO 12 +#define DF_GFX_CS 18 +#define DF_GFX_DC 17 +#define DF_GFX_RST 22 +#define DF_GFX_BL 23 +#elif defined(BOARD_RTL8722DM_MINI) +#define DF_GFX_SCK 11 +#define DF_GFX_MOSI 9 +#define DF_GFX_MISO 10 +#define DF_GFX_CS 12 +#define DF_GFX_DC 14 +#define DF_GFX_RST 15 +#define DF_GFX_BL 13 +#else // old version +#define DF_GFX_SCK 19 +#define DF_GFX_MOSI 21 +#define DF_GFX_MISO 20 +#define DF_GFX_CS 18 // GFX_NOT_DEFINED for display without CS pin +#define DF_GFX_DC 17 +#define DF_GFX_RST 2 +#define DF_GFX_BL 23 +#endif +#elif defined(SEEED_XIAO_M0) +#define DF_GFX_SCK 8 +#define DF_GFX_MOSI 10 +#define DF_GFX_MISO 9 +#define DF_GFX_CS 3 // GFX_NOT_DEFINED for display without CS pin +#define DF_GFX_DC 2 +#define DF_GFX_RST 1 +#define DF_GFX_BL 0 +#else // default pins for Arduino Nano, Mini, Micro and more +#define DF_GFX_SCK 13 +#define DF_GFX_MOSI 11 +#define DF_GFX_MISO 12 +#define DF_GFX_CS 9 +#define DF_GFX_DC 8 +#define DF_GFX_RST 7 +#define DF_GFX_BL 6 +#endif + +#endif // _ARDUINO_GFX_LIBRARIES_H_ diff --git a/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/databus/Arduino_ESP32RGBPanel.cpp b/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/databus/Arduino_ESP32RGBPanel.cpp new file mode 100644 index 00000000..265be506 --- /dev/null +++ b/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/databus/Arduino_ESP32RGBPanel.cpp @@ -0,0 +1,129 @@ +#include "Arduino_ESP32RGBPanel.h" + +#if defined(ESP32) && (CONFIG_IDF_TARGET_ESP32S3) + +Arduino_ESP32RGBPanel::Arduino_ESP32RGBPanel( + int8_t de, int8_t vsync, int8_t hsync, int8_t pclk, + int8_t r0, int8_t r1, int8_t r2, int8_t r3, int8_t r4, + int8_t g0, int8_t g1, int8_t g2, int8_t g3, int8_t g4, int8_t g5, + int8_t b0, int8_t b1, int8_t b2, int8_t b3, int8_t b4, + uint16_t hsync_polarity, uint16_t hsync_front_porch, uint16_t hsync_pulse_width, uint16_t hsync_back_porch, + uint16_t vsync_polarity, uint16_t vsync_front_porch, uint16_t vsync_pulse_width, uint16_t vsync_back_porch, + uint16_t pclk_active_neg, int32_t prefer_speed, bool useBigEndian, + uint16_t de_idle_high, uint16_t pclk_idle_high) + : _de(de), _vsync(vsync), _hsync(hsync), _pclk(pclk), + _r0(r0), _r1(r1), _r2(r2), _r3(r3), _r4(r4), + _g0(g0), _g1(g1), _g2(g2), _g3(g3), _g4(g4), _g5(g5), + _b0(b0), _b1(b1), _b2(b2), _b3(b3), _b4(b4), + _hsync_polarity(hsync_polarity), _hsync_front_porch(hsync_front_porch), _hsync_pulse_width(hsync_pulse_width), _hsync_back_porch(hsync_back_porch), + _vsync_polarity(vsync_polarity), _vsync_front_porch(vsync_front_porch), _vsync_pulse_width(vsync_pulse_width), _vsync_back_porch(vsync_back_porch), + _pclk_active_neg(pclk_active_neg), _prefer_speed(prefer_speed), _useBigEndian(useBigEndian), + _de_idle_high(de_idle_high), _pclk_idle_high(pclk_idle_high) +{ +} + +bool Arduino_ESP32RGBPanel::begin(int32_t speed) +{ + if (speed == GFX_NOT_DEFINED) + { +#ifdef CONFIG_SPIRAM_MODE_QUAD + _speed = 6000000L; +#else + _speed = 12000000L; +#endif + } + else + { + _speed = speed; + } + + return true; +} + +uint16_t *Arduino_ESP32RGBPanel::getFrameBuffer(int16_t w, int16_t h) +{ + esp_lcd_rgb_panel_config_t *_panel_config = (esp_lcd_rgb_panel_config_t *)heap_caps_calloc(1, sizeof(esp_lcd_rgb_panel_config_t), MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL); + + _panel_config->clk_src = LCD_CLK_SRC_PLL160M; + + _panel_config->timings.pclk_hz = (_prefer_speed == GFX_NOT_DEFINED) ? _speed : _prefer_speed; + _panel_config->timings.h_res = w; + _panel_config->timings.v_res = h; + // The following parameters should refer to LCD spec + _panel_config->timings.hsync_pulse_width = _hsync_pulse_width; + _panel_config->timings.hsync_back_porch = _hsync_back_porch; + _panel_config->timings.hsync_front_porch = _hsync_front_porch; + _panel_config->timings.vsync_pulse_width = _vsync_pulse_width; + _panel_config->timings.vsync_back_porch = _vsync_back_porch; + _panel_config->timings.vsync_front_porch = _vsync_front_porch; + _panel_config->timings.flags.hsync_idle_low = (_hsync_polarity == 0) ? 1 : 0; + _panel_config->timings.flags.vsync_idle_low = (_vsync_polarity == 0) ? 1 : 0; + _panel_config->timings.flags.de_idle_high = _de_idle_high; + _panel_config->timings.flags.pclk_active_neg = _pclk_active_neg; + _panel_config->timings.flags.pclk_idle_high = _pclk_idle_high; + + _panel_config->data_width = 16; // RGB565 in parallel mode, thus 16bit in width + _panel_config->sram_trans_align = 8; + _panel_config->psram_trans_align = 64; + _panel_config->hsync_gpio_num = _hsync; + _panel_config->vsync_gpio_num = _vsync; + _panel_config->de_gpio_num = _de; + _panel_config->pclk_gpio_num = _pclk; + + if (_useBigEndian) + { + _panel_config->data_gpio_nums[0] = _g3; + _panel_config->data_gpio_nums[1] = _g4; + _panel_config->data_gpio_nums[2] = _g5; + _panel_config->data_gpio_nums[3] = _r0; + _panel_config->data_gpio_nums[4] = _r1; + _panel_config->data_gpio_nums[5] = _r2; + _panel_config->data_gpio_nums[6] = _r3; + _panel_config->data_gpio_nums[7] = _r4; + _panel_config->data_gpio_nums[8] = _b0; + _panel_config->data_gpio_nums[9] = _b1; + _panel_config->data_gpio_nums[10] = _b2; + _panel_config->data_gpio_nums[11] = _b3; + _panel_config->data_gpio_nums[12] = _b4; + _panel_config->data_gpio_nums[13] = _g0; + _panel_config->data_gpio_nums[14] = _g1; + _panel_config->data_gpio_nums[15] = _g2; + } + else + { + _panel_config->data_gpio_nums[0] = _b0; + _panel_config->data_gpio_nums[1] = _b1; + _panel_config->data_gpio_nums[2] = _b2; + _panel_config->data_gpio_nums[3] = _b3; + _panel_config->data_gpio_nums[4] = _b4; + _panel_config->data_gpio_nums[5] = _g0; + _panel_config->data_gpio_nums[6] = _g1; + _panel_config->data_gpio_nums[7] = _g2; + _panel_config->data_gpio_nums[8] = _g3; + _panel_config->data_gpio_nums[9] = _g4; + _panel_config->data_gpio_nums[10] = _g5; + _panel_config->data_gpio_nums[11] = _r0; + _panel_config->data_gpio_nums[12] = _r1; + _panel_config->data_gpio_nums[13] = _r2; + _panel_config->data_gpio_nums[14] = _r3; + _panel_config->data_gpio_nums[15] = _r4; + } + + _panel_config->disp_gpio_num = GPIO_NUM_NC; + + _panel_config->flags.disp_active_low = 0; + _panel_config->flags.relax_on_idle = 0; + _panel_config->flags.fb_in_psram = 1; // allocate frame buffer in PSRAM + + ESP_ERROR_CHECK(esp_lcd_new_rgb_panel(_panel_config, &_panel_handle)); + ESP_ERROR_CHECK(esp_lcd_panel_reset(_panel_handle)); + ESP_ERROR_CHECK(esp_lcd_panel_init(_panel_handle)); + + uint16_t color = random(0xffff); + ESP_ERROR_CHECK(_panel_handle->draw_bitmap(_panel_handle, 0, 0, 1, 1, &color)); + + _rgb_panel = __containerof(_panel_handle, esp_rgb_panel_t, base); + + return (uint16_t *)_rgb_panel->fb; +} +#endif // #if defined(ESP32) && (CONFIG_IDF_TARGET_ESP32S3) diff --git a/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/databus/Arduino_ESP32RGBPanel.h b/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/databus/Arduino_ESP32RGBPanel.h new file mode 100644 index 00000000..3f701116 --- /dev/null +++ b/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/databus/Arduino_ESP32RGBPanel.h @@ -0,0 +1,100 @@ +#include "Arduino_DataBus.h" + +#if defined(ESP32) && (CONFIG_IDF_TARGET_ESP32S3) + +#ifndef _ARDUINO_ESP32RGBPANEL_H_ +#define _ARDUINO_ESP32RGBPANEL_H_ + +#include "esp_lcd_panel_io.h" +#include "esp_lcd_panel_rgb.h" +#include "esp_lcd_panel_vendor.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_panel_interface.h" +#include "esp_private/gdma.h" +#include "esp_pm.h" +#include "hal/dma_types.h" + +#include "hal/lcd_hal.h" +#include "hal/lcd_ll.h" + +#include "esp32s3/rom/cache.h" +// This function is located in ROM (also see esp_rom/${target}/ld/${target}.rom.ld) +extern int Cache_WriteBack_Addr(uint32_t addr, uint32_t size); + +// extract from esp-idf esp_lcd_rgb_panel.c +struct esp_rgb_panel_t +{ + esp_lcd_panel_t base; // Base class of generic lcd panel + int panel_id; // LCD panel ID + lcd_hal_context_t hal; // Hal layer object + size_t data_width; // Number of data lines (e.g. for RGB565, the data width is 16) + size_t sram_trans_align; // Alignment for framebuffer that allocated in SRAM + size_t psram_trans_align; // Alignment for framebuffer that allocated in PSRAM + int disp_gpio_num; // Display control GPIO, which is used to perform action like "disp_off" + intr_handle_t intr; // LCD peripheral interrupt handle + esp_pm_lock_handle_t pm_lock; // Power management lock + size_t num_dma_nodes; // Number of DMA descriptors that used to carry the frame buffer + uint8_t *fb; // Frame buffer + size_t fb_size; // Size of frame buffer + int data_gpio_nums[SOC_LCD_RGB_DATA_WIDTH]; // GPIOs used for data lines, we keep these GPIOs for action like "invert_color" + size_t resolution_hz; // Peripheral clock resolution + esp_lcd_rgb_timing_t timings; // RGB timing parameters (e.g. pclk, sync pulse, porch width) + gdma_channel_handle_t dma_chan; // DMA channel handle + esp_lcd_rgb_panel_frame_trans_done_cb_t on_frame_trans_done; // Callback, invoked after frame trans done + void *user_ctx; // Reserved user's data of callback functions + int x_gap; // Extra gap in x coordinate, it's used when calculate the flush window + int y_gap; // Extra gap in y coordinate, it's used when calculate the flush window + struct + { + unsigned int disp_en_level : 1; // The level which can turn on the screen by `disp_gpio_num` + unsigned int stream_mode : 1; // If set, the LCD transfers data continuously, otherwise, it stops refreshing the LCD when transaction done + unsigned int fb_in_psram : 1; // Whether the frame buffer is in PSRAM + } flags; + dma_descriptor_t dma_nodes[]; // DMA descriptor pool of size `num_dma_nodes` +}; + +class Arduino_ESP32RGBPanel +{ +public: + Arduino_ESP32RGBPanel( + int8_t de, int8_t vsync, int8_t hsync, int8_t pclk, + int8_t r0, int8_t r1, int8_t r2, int8_t r3, int8_t r4, + int8_t g0, int8_t g1, int8_t g2, int8_t g3, int8_t g4, int8_t g5, + int8_t b0, int8_t b1, int8_t b2, int8_t b3, int8_t b4, + uint16_t hsync_polarity, uint16_t hsync_front_porch, uint16_t hsync_pulse_width, uint16_t hsync_back_porch, + uint16_t vsync_polarity, uint16_t vsync_front_porch, uint16_t vsync_pulse_width, uint16_t vsync_back_porch, + uint16_t pclk_active_neg = 0, int32_t prefer_speed = GFX_NOT_DEFINED, bool useBigEndian = false, + uint16_t de_idle_high = 0, uint16_t pclk_idle_high = 0); + + bool begin(int32_t speed = GFX_NOT_DEFINED); + + uint16_t *getFrameBuffer(int16_t w, int16_t h); + +protected: +private: + int32_t _speed; + int8_t _de, _vsync, _hsync, _pclk; + int8_t _r0, _r1, _r2, _r3, _r4; + int8_t _g0, _g1, _g2, _g3, _g4, _g5; + int8_t _b0, _b1, _b2, _b3, _b4; + uint16_t _hsync_polarity; + uint16_t _hsync_front_porch; + uint16_t _hsync_pulse_width; + uint16_t _hsync_back_porch; + uint16_t _vsync_polarity; + uint16_t _vsync_front_porch; + uint16_t _vsync_pulse_width; + uint16_t _vsync_back_porch; + uint16_t _pclk_active_neg; + int32_t _prefer_speed; + bool _useBigEndian; + uint16_t _de_idle_high; + uint16_t _pclk_idle_high; + + esp_lcd_panel_handle_t _panel_handle = NULL; + esp_rgb_panel_t *_rgb_panel; +}; + +#endif // _ARDUINO_ESP32RGBPANEL_H_ + +#endif // #if defined(ESP32) && (CONFIG_IDF_TARGET_ESP32S3) diff --git a/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/databus/Arduino_XL9535SWSPI.cpp b/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/databus/Arduino_XL9535SWSPI.cpp new file mode 100644 index 00000000..2fd54af3 --- /dev/null +++ b/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/databus/Arduino_XL9535SWSPI.cpp @@ -0,0 +1,276 @@ +#include "Arduino_XL9535SWSPI.h" + +Arduino_XL9535SWSPI::Arduino_XL9535SWSPI(int8_t sda, int8_t scl, int8_t pwd, int8_t cs, int8_t sck, int8_t mosi, TwoWire *wire) + : _sda(sda), _scl(scl), _pwd(pwd), _cs(cs), _sck(sck), _mosi(mosi), _wire(wire) +{ +} + +bool Arduino_XL9535SWSPI::begin(int32_t speed, int8_t dataMode) +{ + UNUSED(speed); + UNUSED(dataMode); + + _address = XL9535_IIC_ADDRESS; + _wire->beginTransmission(_address); + if (!_wire->endTransmission()) + { + Serial.println("Found xl9535"); + is_found = true; + + if (_pwd != GFX_NOT_DEFINED) + { + this->pinMode(_pwd, OUTPUT); + } + this->pinMode(_cs, OUTPUT); + this->pinMode(_sck, OUTPUT); + this->pinMode(_mosi, OUTPUT); + + if (_pwd != GFX_NOT_DEFINED) + { + this->digitalWrite(_pwd, 1); + } + this->digitalWrite(_cs, 1); + this->digitalWrite(_sck, 1); + this->digitalWrite(_mosi, 1); + + // while(1) + // { + // this->digitalWrite(4, 0); + // delay(1000); + // this->digitalWrite(4, 1); + // delay(1000); + // } + + // this->digitalWrite(_cs, 0); + // this->digitalWrite(5, 1); + // delay(100); + // this->digitalWrite(5, 0); + // delay(800); + // this->digitalWrite(5, 1); + // delay(800); + // this->digitalWrite(_cs, 1); + } + else + { + Serial.println("xl9535 not found"); + is_found = false; + } + + return true; +} + +void Arduino_XL9535SWSPI::beginWrite() +{ + this->digitalWrite(_cs, 0); +} + +void Arduino_XL9535SWSPI::endWrite() +{ + this->digitalWrite(_cs, 1); +} + +void Arduino_XL9535SWSPI::writeCommand(uint8_t c) +{ + // D/C bit, command + this->digitalWrite(_mosi, 0); + this->digitalWrite(_sck, 0); + this->digitalWrite(_sck, 1); + + uint8_t bit = 0x80; + while (bit) + { + if (c & bit) + { + this->digitalWrite(_mosi, 1); + } + else + { + this->digitalWrite(_mosi, 0); + } + this->digitalWrite(_sck, 0); + bit >>= 1; + this->digitalWrite(_sck, 1); + } +} + +void Arduino_XL9535SWSPI::writeCommand16(uint16_t) +{ +} + +void Arduino_XL9535SWSPI::write(uint8_t d) +{ + // D/C bit, data + this->digitalWrite(_mosi, 1); + this->digitalWrite(_sck, 0); + this->digitalWrite(_sck, 1); + + uint8_t bit = 0x80; + while (bit) + { + if (d & bit) + { + this->digitalWrite(_mosi, 1); + } + else + { + this->digitalWrite(_mosi, 0); + } + this->digitalWrite(_sck, 0); + bit >>= 1; + this->digitalWrite(_sck, 1); + } +} + +void Arduino_XL9535SWSPI::write16(uint16_t) +{ + // not implemented +} + +void Arduino_XL9535SWSPI::writeRepeat(uint16_t p, uint32_t len) +{ + // not implemented +} + +void Arduino_XL9535SWSPI::writePixels(uint16_t *data, uint32_t len) +{ + // not implemented +} + +#if !defined(LITTLE_FOOT_PRINT) +void Arduino_XL9535SWSPI::writeBytes(uint8_t *data, uint32_t len) +{ + // not implemented +} +#endif // !defined(LITTLE_FOOT_PRINT) + +void Arduino_XL9535SWSPI::writeRegister(uint8_t reg, uint8_t *data, size_t len) +{ + _wire->beginTransmission(_address); + _wire->write(reg); + for (size_t i = 0; i < len; i++) + { + _wire->write(data[i]); + } + _wire->endTransmission(); +} + +uint8_t Arduino_XL9535SWSPI::readRegister(uint8_t reg, uint8_t *data, size_t len) +{ + _wire->beginTransmission(_address); + _wire->write(reg); + _wire->endTransmission(); + _wire->requestFrom(_address, len); + size_t index = 0; + while (index < len) + data[index++] = _wire->read(); + return 0; +} + +void Arduino_XL9535SWSPI::pinMode(uint8_t pin, uint8_t mode) +{ + if (is_found) + { + uint8_t port = 0; + if (pin > 7) + { + this->readRegister(XL9535_CONFIG_PORT_1_REG, &port, 1); + if (mode == OUTPUT) + { + port = port & (~(1 << (pin - 10))); + } + else + { + port = port | (1 << (pin - 10)); + } + this->writeRegister(XL9535_CONFIG_PORT_1_REG, &port, 1); + } + else + { + this->readRegister(XL9535_CONFIG_PORT_0_REG, &port, 1); + if (mode == OUTPUT) + { + port = port & (~(1 << pin)); + } + else + { + port = port | (1 << pin); + } + this->writeRegister(XL9535_CONFIG_PORT_0_REG, &port, 1); + } + } + else + { + Serial.println("xl9535 not found"); + } +} +void Arduino_XL9535SWSPI::pinMode8(uint8_t port, uint8_t pin, uint8_t mode) +{ + if (is_found) + { + uint8_t _pin = (mode != OUTPUT) ? pin : ~pin; + if (port) + { + this->writeRegister(XL9535_CONFIG_PORT_1_REG, &_pin, 1); + } + else + { + this->writeRegister(XL9535_CONFIG_PORT_0_REG, &_pin, 1); + } + } + else + { + Serial.println("xl9535 not found"); + } +} + +void Arduino_XL9535SWSPI::digitalWrite(uint8_t pin, uint8_t val) +{ + if (is_found) + { + uint8_t port = 0; + uint8_t reg_data = 0; + if (pin > 7) + { + this->readRegister(XL9535_OUTPUT_PORT_1_REG, ®_data, 1); + reg_data = reg_data & (~(1 << (pin - 10))); + port = reg_data | val << (pin - 10); + this->writeRegister(XL9535_OUTPUT_PORT_1_REG, &port, 1); + } + else + { + this->readRegister(XL9535_OUTPUT_PORT_0_REG, ®_data, 1); + reg_data = reg_data & (~(1 << pin)); + port = reg_data | val << pin; + this->writeRegister(XL9535_OUTPUT_PORT_0_REG, &port, 1); + } + } + else + { + Serial.println("xl9535 not found"); + } +} + +int Arduino_XL9535SWSPI::digitalRead(uint8_t pin) +{ + if (is_found) + { + int state = 0; + uint8_t port = 0; + if (pin > 7) + { + this->readRegister(XL9535_INPUT_PORT_1_REG, &port, 1); + state = port >> (pin - 10) & 0x01 ? 1 : 0; + } + else + { + this->readRegister(XL9535_INPUT_PORT_0_REG, &port, 1); + state = port >> pin & 0x01 ? 1 : 0; + } + return state; + } + else + { + Serial.println("xl9535 not found"); + } + return 0; +} diff --git a/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/databus/Arduino_XL9535SWSPI.h b/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/databus/Arduino_XL9535SWSPI.h new file mode 100644 index 00000000..9c838d90 --- /dev/null +++ b/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/databus/Arduino_XL9535SWSPI.h @@ -0,0 +1,57 @@ +#ifndef _ARDUINO_XL9535SWSPI_H_ +#define _ARDUINO_XL9535SWSPI_H_ + +#include + +#include "Arduino_DataBus.h" + +#define XL9535_IIC_ADDRESS 0X20 + +#define XL9535_INPUT_PORT_0_REG 0X00 +#define XL9535_INPUT_PORT_1_REG 0X01 +#define XL9535_OUTPUT_PORT_0_REG 0X02 +#define XL9535_OUTPUT_PORT_1_REG 0X03 +#define XL9535_INVERSION_PORT_0_REG 0X04 +#define XL9535_INVERSION_PORT_1_REG 0X05 +#define XL9535_CONFIG_PORT_0_REG 0X06 +#define XL9535_CONFIG_PORT_1_REG 0X07 + +class Arduino_XL9535SWSPI : public Arduino_DataBus +{ +public: + Arduino_XL9535SWSPI(int8_t sda, int8_t scl, int8_t pwd, int8_t cs, int8_t sck, int8_t mosi, TwoWire *wire = &Wire); + + bool begin(int32_t speed = GFX_NOT_DEFINED, int8_t dataMode = GFX_NOT_DEFINED) override; + void beginWrite() override; + void endWrite() override; + void writeCommand(uint8_t) override; + void writeCommand16(uint16_t) override; + void write(uint8_t) override; + void write16(uint16_t) override; + void writeRepeat(uint16_t p, uint32_t len) override; + void writePixels(uint16_t *data, uint32_t len) override; + +#if !defined(LITTLE_FOOT_PRINT) + void writeBytes(uint8_t *data, uint32_t len) override; +#endif // !defined(LITTLE_FOOT_PRINT) + + void pinMode(uint8_t pin, uint8_t mode)override; + void pinMode8(uint8_t port, uint8_t pin, uint8_t mode); + + void digitalWrite(uint8_t pin, uint8_t val) override; + int digitalRead(uint8_t pin) override; + +protected: + void writeRegister(uint8_t reg, uint8_t *data, size_t len); + uint8_t readRegister(uint8_t reg, uint8_t *data, size_t len); + + uint8_t _address; + bool is_found; + + int8_t _sda, _scl, _pwd, _cs, _sck, _mosi; + TwoWire *_wire; + +private: +}; + +#endif // _ARDUINO_XL9535SWSPI_H_ diff --git a/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/display/Arduino_RGB_Display.cpp b/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/display/Arduino_RGB_Display.cpp new file mode 100644 index 00000000..63bd7c13 --- /dev/null +++ b/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/display/Arduino_RGB_Display.cpp @@ -0,0 +1,498 @@ +#include "../Arduino_DataBus.h" + +#if defined(ESP32) && (CONFIG_IDF_TARGET_ESP32S3) + +#include "../Arduino_GFX.h" +#include "Arduino_RGB_Display.h" + +Arduino_RGB_Display::Arduino_RGB_Display( + int16_t w, int16_t h, Arduino_ESP32RGBPanel *rgbpanel, uint8_t r, bool auto_flush, + Arduino_DataBus *bus, int8_t rst, const uint8_t *init_operations, size_t init_operations_len) + : Arduino_GFX(w, h), _rgbpanel(rgbpanel), _auto_flush(auto_flush), + _bus(bus), _rst(rst), _init_operations(init_operations), _init_operations_len(init_operations_len) +{ + _framebuffer_size = w * h * 2; + MAX_X = WIDTH - 1; + MAX_Y = HEIGHT - 1; + setRotation(r); +} + +bool Arduino_RGB_Display::begin(int32_t speed) +{ + if (_bus) + { + if (!_bus->begin()) + { + return false; + } + } + + if (_rst != GFX_NOT_DEFINED) + { + pinMode(_rst, OUTPUT); + digitalWrite(_rst, HIGH); + delay(100); + digitalWrite(_rst, LOW); + delay(120); + digitalWrite(_rst, HIGH); + delay(120); + } + else + { + if (_bus) + { + // Software Rest + _bus->sendCommand(0x01); + delay(120); + } + } + + if (_bus) + { + if (_init_operations_len > 0) + { + _bus->batchOperation((uint8_t *)_init_operations, _init_operations_len); + } + } + + _rgbpanel->begin(speed); + _framebuffer = _rgbpanel->getFrameBuffer(WIDTH, HEIGHT); + + if (!_framebuffer) + { + return false; + } + + return true; +} + +void Arduino_RGB_Display::writePixelPreclipped(int16_t x, int16_t y, uint16_t color) +{ + uint16_t *fb = _framebuffer; + switch (_rotation) + { + case 1: + fb += (int32_t)x * _height; + fb += _max_y - y; + *fb = color; + if (_auto_flush) + { + Cache_WriteBack_Addr((uint32_t)fb, 2); + } + break; + case 2: + fb += (int32_t)(_max_y - y) * _width; + fb += _max_x - x; + *fb = color; + if (_auto_flush) + { + Cache_WriteBack_Addr((uint32_t)fb, 2); + } + break; + case 3: + fb += (int32_t)(_max_x - x) * _height; + fb += y; + *fb = color; + if (_auto_flush) + { + Cache_WriteBack_Addr((uint32_t)fb, 2); + } + break; + default: // case 0: + fb += (int32_t)y * _width; + fb += x; + *fb = color; + if (_auto_flush) + { + Cache_WriteBack_Addr((uint32_t)fb, 2); + } + } +} + +void Arduino_RGB_Display::writeFastVLine(int16_t x, int16_t y, + int16_t h, uint16_t color) +{ + // log_i("writeFastVLine(x: %d, y: %d, h: %d)", x, y, h); + switch (_rotation) + { + case 1: + writeFastHLineCore(_height - y - h, x, h, color); + break; + case 2: + writeFastVLineCore(_max_x - x, _height - y - h, h, color); + break; + case 3: + writeFastHLineCore(y, _max_x - x, h, color); + break; + default: // case 0: + writeFastVLineCore(x, y, h, color); + } +} + +void Arduino_RGB_Display::writeFastVLineCore(int16_t x, int16_t y, + int16_t h, uint16_t color) +{ + // log_i("writeFastVLineCore(x: %d, y: %d, h: %d)", x, y, h); + if (_ordered_in_range(x, 0, MAX_X) && h) + { // X on screen, nonzero height + if (h < 0) + { // If negative height... + y += h + 1; // Move Y to top edge + h = -h; // Use positive height + } + if (y <= MAX_Y) + { // Not off bottom + int16_t y2 = y + h - 1; + if (y2 >= 0) + { // Not off top + // Line partly or fully overlaps screen + if (y < 0) + { + y = 0; + h = y2 + 1; + } // Clip top + if (y2 > MAX_Y) + { + h = MAX_Y - y + 1; + } // Clip bottom + + uint16_t *fb = _framebuffer + ((int32_t)y * WIDTH) + x; + if (_auto_flush) + { + while (h--) + { + *fb = color; + Cache_WriteBack_Addr((uint32_t)fb, 2); + fb += WIDTH; + } + } + else + { + while (h--) + { + *fb = color; + fb += WIDTH; + } + } + } + } + } +} + +void Arduino_RGB_Display::writeFastHLine(int16_t x, int16_t y, + int16_t w, uint16_t color) +{ + // log_i("writeFastHLine(x: %d, y: %d, w: %d)", x, y, w); + switch (_rotation) + { + case 1: + writeFastVLineCore(_max_y - y, x, w, color); + break; + case 2: + writeFastHLineCore(_width - x - w, _max_y - y, w, color); + break; + case 3: + writeFastVLineCore(y, _width - x - w, w, color); + break; + default: // case 0: + writeFastHLineCore(x, y, w, color); + } +} + +void Arduino_RGB_Display::writeFastHLineCore(int16_t x, int16_t y, + int16_t w, uint16_t color) +{ + // log_i("writeFastHLineCore(x: %d, y: %d, w: %d)", x, y, w); + if (_ordered_in_range(y, 0, MAX_Y) && w) + { // Y on screen, nonzero width + if (w < 0) + { // If negative width... + x += w + 1; // Move X to left edge + w = -w; // Use positive width + } + if (x <= MAX_X) + { // Not off right + int16_t x2 = x + w - 1; + if (x2 >= 0) + { // Not off left + // Line partly or fully overlaps screen + if (x < 0) + { + x = 0; + w = x2 + 1; + } // Clip left + if (x2 > MAX_X) + { + w = MAX_X - x + 1; + } // Clip right + + uint16_t *fb = _framebuffer + ((int32_t)y * WIDTH) + x; + uint32_t cachePos = (uint32_t)fb; + int16_t writeSize = w * 2; + while (w--) + { + *(fb++) = color; + } + if (_auto_flush) + { + Cache_WriteBack_Addr(cachePos, writeSize); + } + } + } + } +} + +void Arduino_RGB_Display::writeFillRectPreclipped(int16_t x, int16_t y, + int16_t w, int16_t h, uint16_t color) +{ + // log_i("writeFillRectPreclipped(x: %d, y: %d, w: %d, h: %d)", x, y, w, h); + if (_rotation > 0) + { + int16_t t = x; + switch (_rotation) + { + case 1: + x = WIDTH - y - h; + y = t; + t = w; + w = h; + h = t; + break; + case 2: + x = WIDTH - x - w; + y = HEIGHT - y - h; + break; + case 3: + x = y; + y = HEIGHT - t - w; + t = w; + w = h; + h = t; + break; + } + } + // log_i("adjusted writeFillRectPreclipped(x: %d, y: %d, w: %d, h: %d)", x, y, w, h); + uint16_t *row = _framebuffer; + row += y * WIDTH; + uint32_t cachePos = (uint32_t)row; + row += x; + for (int j = 0; j < h; j++) + { + for (int i = 0; i < w; i++) + { + row[i] = color; + } + row += WIDTH; + } + if (_auto_flush) + { + Cache_WriteBack_Addr(cachePos, WIDTH * h * 2); + } +} + +void Arduino_RGB_Display::drawIndexedBitmap(int16_t x, int16_t y, uint8_t *bitmap, uint16_t *color_index, int16_t w, int16_t h, int16_t x_skip) +{ + if ( + ((x + w - 1) < 0) || // Outside left + ((y + h - 1) < 0) || // Outside top + (x > _max_x) || // Outside right + (y > _max_y) // Outside bottom + ) + { + return; + } + else + { + if (_rotation > 0) + { + Arduino_GFX::drawIndexedBitmap(x, y, bitmap, color_index, w, h, x_skip); + } + else + { + if ((y + h - 1) > _max_y) + { + h -= (y + h - 1) - _max_y; + } + if (y < 0) + { + bitmap -= y * w; + h += y; + y = 0; + } + if ((x + w - 1) > _max_x) + { + x_skip += (x + w - 1) - _max_x; + w -= (x + w - 1) - _max_x; + } + if (x < 0) + { + bitmap -= x; + x_skip -= x; + w += x; + x = 0; + } + uint16_t *row = _framebuffer; + row += y * _width; + uint32_t cachePos = (uint32_t)row; + row += x; + for (int j = 0; j < h; j++) + { + for (int i = 0; i < w; i++) + { + row[i] = color_index[*bitmap++]; + } + bitmap += x_skip; + row += _width; + } + if (_auto_flush) + { + Cache_WriteBack_Addr(cachePos, _width * h * 2); + } + } + } +} + +void Arduino_RGB_Display::draw16bitRGBBitmap(int16_t x, int16_t y, + uint16_t *bitmap, int16_t w, int16_t h) +{ + bool result; + + switch (_rotation) + { + case 1: + result = gfx_draw_bitmap_to_framebuffer_rotate_1(bitmap, w, h, _framebuffer, x, y, _width, _height); + break; + case 2: + result = gfx_draw_bitmap_to_framebuffer_rotate_2(bitmap, w, h, _framebuffer, x, y, _width, _height); + break; + case 3: + result = gfx_draw_bitmap_to_framebuffer_rotate_3(bitmap, w, h, _framebuffer, x, y, _width, _height); + break; + default: // case 0: + result = gfx_draw_bitmap_to_framebuffer(bitmap, w, h, _framebuffer, x, y, _width, _height); + } + + if (result) + { + if (_auto_flush) + { + uint32_t cachePos; + size_t cache_size; + switch (_rotation) + { + case 1: + cachePos = (uint32_t)(_framebuffer + (x * WIDTH)); + cache_size = HEIGHT * w * 2; + break; + case 2: + cachePos = (uint32_t)(_framebuffer + ((MAX_Y - y) * WIDTH)); + cache_size = HEIGHT * h * 2; + break; + case 3: + cachePos = (uint32_t)(_framebuffer + ((MAX_Y - x) * WIDTH)); + cache_size = HEIGHT * w * 2; + break; + default: // case 0: + cachePos = (uint32_t)(_framebuffer + (y * WIDTH) + x); + cache_size = (WIDTH * (h - 1) + w) * 2; + } + Cache_WriteBack_Addr(cachePos, cache_size); + } + } +} + +void Arduino_RGB_Display::draw16bitBeRGBBitmap(int16_t x, int16_t y, + uint16_t *bitmap, int16_t w, int16_t h) +{ + if ( + ((x + w - 1) < 0) || // Outside left + ((y + h - 1) < 0) || // Outside top + (x > _max_x) || // Outside right + (y > _max_y) // Outside bottom + ) + { + return; + } + else + { + if (_rotation > 0) + { + Arduino_GFX::draw16bitBeRGBBitmap(x, y, bitmap, w, h); + } + else + { + int16_t xskip = 0; + if ((y + h - 1) > _max_y) + { + h -= (y + h - 1) - _max_y; + } + if (y < 0) + { + bitmap -= y * w; + h += y; + y = 0; + } + if ((x + w - 1) > _max_x) + { + xskip = (x + w - 1) - _max_x; + w -= xskip; + } + if (x < 0) + { + bitmap -= x; + xskip -= x; + w += x; + x = 0; + } + uint16_t *row = _framebuffer; + row += y * _width; + uint32_t cachePos = (uint32_t)row; + row += x; + uint16_t color; + for (int j = 0; j < h; j++) + { + for (int i = 0; i < w; i++) + { + color = *bitmap++; + MSB_16_SET(row[i], color); + } + bitmap += xskip; + row += _width; + } + if (_auto_flush) + { + Cache_WriteBack_Addr(cachePos, _width * h * 2); + } + } + } +} + +void Arduino_RGB_Display::flush(void) +{ + if (!_auto_flush) + { + Cache_WriteBack_Addr((uint32_t)_framebuffer, _framebuffer_size); + } +} + +uint16_t *Arduino_RGB_Display::getFramebuffer() +{ + return _framebuffer; +} + +#endif // #if defined(ESP32) && (CONFIG_IDF_TARGET_ESP32S3) + +void Arduino_RGB_Display::XL_pinMode(uint8_t pin, uint8_t mode) +{ + _bus->pinMode(pin, mode); +} + +void Arduino_RGB_Display::XL_digitalWrite(uint8_t pin, uint8_t val) +{ + _bus->digitalWrite(pin, val); +} + +int Arduino_RGB_Display::XL_digitalRead(uint8_t pin) +{ + return _bus->digitalRead(pin); +} diff --git a/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/display/Arduino_RGB_Display.h b/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/display/Arduino_RGB_Display.h new file mode 100644 index 00000000..98de2390 --- /dev/null +++ b/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/display/Arduino_RGB_Display.h @@ -0,0 +1,1325 @@ +#include "../Arduino_DataBus.h" + +#if defined(ESP32) && (CONFIG_IDF_TARGET_ESP32S3) + +#ifndef _ARDUINO_RGB_DISPLAY_H_ +#define _ARDUINO_RGB_DISPLAY_H_ + +#include "../Arduino_GFX.h" +#include "../databus/Arduino_ESP32RGBPanel.h" + +static const uint8_t gc9503v_type1_init_operations[] = { + BEGIN_WRITE, + WRITE_COMMAND_8, 0xF0, + WRITE_BYTES, 5, 0x55, 0xAA, 0x52, 0x08, 0x00, + + WRITE_C8_D16, 0xF6, 0x5A, 0x87, + + WRITE_C8_D8, 0xC1, 0x3F, + WRITE_C8_D8, 0xC2, 0x0E, + WRITE_C8_D8, 0xC6, 0xF8, + WRITE_C8_D8, 0xC9, 0x10, + WRITE_C8_D8, 0xCD, 0x25, + WRITE_C8_D8, 0xF8, 0x8A, + WRITE_C8_D8, 0xAC, 0x45, + WRITE_C8_D8, 0xA0, 0xDD, + WRITE_C8_D8, 0xA7, 0x47, + + WRITE_COMMAND_8, 0xFA, + WRITE_BYTES, 4, 0x00, 0x00, 0x00, 0x04, + + WRITE_C8_D8, 0xA3, 0xEE, + + WRITE_COMMAND_8, 0xFD, + WRITE_BYTES, 3, 0x28, 0x28, 0x00, + + WRITE_C8_D8, 0x71, 0x48, + WRITE_C8_D8, 0x72, 0x48, + WRITE_C8_D16, 0x73, 0x00, 0x44, + WRITE_C8_D8, 0x97, 0xEE, + WRITE_C8_D8, 0x83, 0x93, + WRITE_C8_D8, 0x9A, 0x72, + WRITE_C8_D8, 0x9B, 0x5a, + WRITE_C8_D16, 0x82, 0x2c, 0x2c, + WRITE_C8_D8, 0xB1, 0x10, + + WRITE_COMMAND_8, 0x6D, + WRITE_BYTES, 32, + 0x00, 0x1F, 0x19, 0x1A, + 0x10, 0x0e, 0x0c, 0x0a, + 0x02, 0x07, 0x1E, 0x1E, + 0x1E, 0x1E, 0x1E, 0x1E, + 0x1E, 0x1E, 0x1E, 0x1E, + 0x1E, 0x1E, 0x08, 0x01, + 0x09, 0x0b, 0x0D, 0x0F, + 0x1a, 0x19, 0x1f, 0x00, + + WRITE_COMMAND_8, 0x64, + WRITE_BYTES, 16, + 0x38, 0x05, 0x01, 0xdb, + 0x03, 0x03, 0x38, 0x04, + 0x01, 0xdc, 0x03, 0x03, + 0x7A, 0x7A, 0x7A, 0x7A, + + WRITE_COMMAND_8, 0x65, + WRITE_BYTES, 16, + 0x38, 0x03, 0x01, 0xdd, + 0x03, 0x03, 0x38, 0x02, + 0x01, 0xde, 0x03, 0x03, + 0x7A, 0x7A, 0x7A, 0x7A, + + WRITE_COMMAND_8, 0x66, + WRITE_BYTES, 16, + 0x38, 0x01, 0x01, 0xdf, + 0x03, 0x03, 0x38, 0x00, + 0x01, 0xe0, 0x03, 0x03, + 0x7A, 0x7A, 0x7A, 0x7A, + + WRITE_COMMAND_8, 0x67, + WRITE_BYTES, 16, + 0x30, 0x01, 0x01, 0xe1, + 0x03, 0x03, 0x30, 0x02, + 0x01, 0xe2, 0x03, 0x03, + 0x7A, 0x7A, 0x7A, 0x7A, + + WRITE_COMMAND_8, 0x68, + WRITE_BYTES, 13, + 0x00, 0x08, 0x15, 0x08, + 0x15, 0x7A, 0x7A, 0x08, + 0x15, 0x08, 0x15, 0x7A, + 0x7A, + + WRITE_COMMAND_8, 0x60, + WRITE_BYTES, 8, + 0x38, 0x08, 0x7A, 0x7A, + 0x38, 0x09, 0x7A, 0x7A, + + WRITE_COMMAND_8, 0x63, + WRITE_BYTES, 8, + 0x31, 0xe4, 0x7A, 0x7A, + 0x31, 0xe5, 0x7A, 0x7A, + + WRITE_C8_D8, 0x6B, 0x07, + + WRITE_C8_D16, 0x7A, 0x08, 0x13, + + WRITE_C8_D16, 0x7B, 0x08, 0x13, + + WRITE_COMMAND_8, 0xD1, + WRITE_BYTES, 52, + 0x00, 0x00, 0x00, 0x04, + 0x00, 0x12, 0x00, 0x18, + 0x00, 0x21, 0x00, 0x2a, + 0x00, 0x35, 0x00, 0x47, + 0x00, 0x56, 0x00, 0x90, + 0x00, 0xe5, 0x01, 0x68, + 0x01, 0xd5, 0x01, 0xd7, + 0x02, 0x36, 0x02, 0xa6, + 0x02, 0xee, 0x03, 0x48, + 0x03, 0xa0, 0x03, 0xba, + 0x03, 0xc5, 0x03, 0xd0, + 0x03, 0xE0, 0x03, 0xea, + 0x03, 0xFa, 0x03, 0xFF, + + WRITE_COMMAND_8, 0xD2, + WRITE_BYTES, 52, + 0x00, 0x00, 0x00, 0x04, + 0x00, 0x12, 0x00, 0x18, + 0x00, 0x21, 0x00, 0x2a, + 0x00, 0x35, 0x00, 0x47, + 0x00, 0x56, 0x00, 0x90, + 0x00, 0xe5, 0x01, 0x68, + 0x01, 0xd5, 0x01, 0xd7, + 0x02, 0x36, 0x02, 0xa6, + 0x02, 0xee, 0x03, 0x48, + 0x03, 0xa0, 0x03, 0xba, + 0x03, 0xc5, 0x03, 0xd0, + 0x03, 0xE0, 0x03, 0xea, + 0x03, 0xFa, 0x03, 0xFF, + + WRITE_COMMAND_8, 0xD3, + WRITE_BYTES, 52, + 0x00, 0x00, 0x00, 0x04, + 0x00, 0x12, 0x00, 0x18, + 0x00, 0x21, 0x00, 0x2a, + 0x00, 0x35, 0x00, 0x47, + 0x00, 0x56, 0x00, 0x90, + 0x00, 0xe5, 0x01, 0x68, + 0x01, 0xd5, 0x01, 0xd7, + 0x02, 0x36, 0x02, 0xa6, + 0x02, 0xee, 0x03, 0x48, + 0x03, 0xa0, 0x03, 0xba, + 0x03, 0xc5, 0x03, 0xd0, + 0x03, 0xE0, 0x03, 0xea, + 0x03, 0xFa, 0x03, 0xFF, + + WRITE_COMMAND_8, 0xD4, + WRITE_BYTES, 52, + 0x00, 0x00, 0x00, 0x04, + 0x00, 0x12, 0x00, 0x18, + 0x00, 0x21, 0x00, 0x2a, + 0x00, 0x35, 0x00, 0x47, + 0x00, 0x56, 0x00, 0x90, + 0x00, 0xe5, 0x01, 0x68, + 0x01, 0xd5, 0x01, 0xd7, + 0x02, 0x36, 0x02, 0xa6, + 0x02, 0xee, 0x03, 0x48, + 0x03, 0xa0, 0x03, 0xba, + 0x03, 0xc5, 0x03, 0xd0, + 0x03, 0xE0, 0x03, 0xea, + 0x03, 0xFa, 0x03, 0xFF, + + WRITE_COMMAND_8, 0xD5, + WRITE_BYTES, 52, + 0x00, 0x00, 0x00, 0x04, + 0x00, 0x12, 0x00, 0x18, + 0x00, 0x21, 0x00, 0x2a, + 0x00, 0x35, 0x00, 0x47, + 0x00, 0x56, 0x00, 0x90, + 0x00, 0xe5, 0x01, 0x68, + 0x01, 0xd5, 0x01, 0xd7, + 0x02, 0x36, 0x02, 0xa6, + 0x02, 0xee, 0x03, 0x48, + 0x03, 0xa0, 0x03, 0xba, + 0x03, 0xc5, 0x03, 0xd0, + 0x03, 0xE0, 0x03, 0xea, + 0x03, 0xFa, 0x03, 0xFF, + + WRITE_COMMAND_8, 0xD6, + WRITE_BYTES, 52, + 0x00, 0x00, 0x00, 0x04, + 0x00, 0x12, 0x00, 0x18, + 0x00, 0x21, 0x00, 0x2a, + 0x00, 0x35, 0x00, 0x47, + 0x00, 0x56, 0x00, 0x90, + 0x00, 0xe5, 0x01, 0x68, + 0x01, 0xd5, 0x01, 0xd7, + 0x02, 0x36, 0x02, 0xa6, + 0x02, 0xee, 0x03, 0x48, + 0x03, 0xa0, 0x03, 0xba, + 0x03, 0xc5, 0x03, 0xd0, + 0x03, 0xE0, 0x03, 0xea, + 0x03, 0xFa, 0x03, 0xFF, + + WRITE_C8_D8, 0x3a, 0x66, + + WRITE_COMMAND_8, 0x11, + END_WRITE, + + DELAY, 200, + + BEGIN_WRITE, + WRITE_COMMAND_8, 0x29, + END_WRITE}; + +static const uint8_t st7701_type1_init_operations[] = { + BEGIN_WRITE, + WRITE_COMMAND_8, 0xFF, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x10, + + WRITE_C8_D16, 0xC0, 0x3B, 0x00, + WRITE_C8_D16, 0xC1, 0x0D, 0x02, + WRITE_C8_D16, 0xC2, 0x31, 0x05, + WRITE_C8_D8, 0xCD, 0x08, + + WRITE_COMMAND_8, 0xB0, // Positive Voltage Gamma Control + WRITE_BYTES, 16, + 0x00, 0x11, 0x18, 0x0E, + 0x11, 0x06, 0x07, 0x08, + 0x07, 0x22, 0x04, 0x12, + 0x0F, 0xAA, 0x31, 0x18, + + WRITE_COMMAND_8, 0xB1, // Negative Voltage Gamma Control + WRITE_BYTES, 16, + 0x00, 0x11, 0x19, 0x0E, + 0x12, 0x07, 0x08, 0x08, + 0x08, 0x22, 0x04, 0x11, + 0x11, 0xA9, 0x32, 0x18, + + // PAGE1 + WRITE_COMMAND_8, 0xFF, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x11, + + WRITE_C8_D8, 0xB0, 0x60, // Vop=4.7375v + WRITE_C8_D8, 0xB1, 0x32, // VCOM=32 + WRITE_C8_D8, 0xB2, 0x07, // VGH=15v + WRITE_C8_D8, 0xB3, 0x80, + WRITE_C8_D8, 0xB5, 0x49, // VGL=-10.17v + WRITE_C8_D8, 0xB7, 0x85, + WRITE_C8_D8, 0xB8, 0x21, // AVDD=6.6 & AVCL=-4.6 + WRITE_C8_D8, 0xC1, 0x78, + WRITE_C8_D8, 0xC2, 0x78, + + WRITE_COMMAND_8, 0xE0, + WRITE_BYTES, 3, 0x00, 0x1B, 0x02, + + WRITE_COMMAND_8, 0xE1, + WRITE_BYTES, 11, + 0x08, 0xA0, 0x00, 0x00, + 0x07, 0xA0, 0x00, 0x00, + 0x00, 0x44, 0x44, + + WRITE_COMMAND_8, 0xE2, + WRITE_BYTES, 12, + 0x11, 0x11, 0x44, 0x44, + 0xED, 0xA0, 0x00, 0x00, + 0xEC, 0xA0, 0x00, 0x00, + + WRITE_COMMAND_8, 0xE3, + WRITE_BYTES, 4, 0x00, 0x00, 0x11, 0x11, + + WRITE_C8_D16, 0xE4, 0x44, 0x44, + + WRITE_COMMAND_8, 0xE5, + WRITE_BYTES, 16, + 0x0A, 0xE9, 0xD8, 0xA0, + 0x0C, 0xEB, 0xD8, 0xA0, + 0x0E, 0xED, 0xD8, 0xA0, + 0x10, 0xEF, 0xD8, 0xA0, + + WRITE_COMMAND_8, 0xE6, + WRITE_BYTES, 4, 0x00, 0x00, 0x11, 0x11, + + WRITE_C8_D16, 0xE7, 0x44, 0x44, + + WRITE_COMMAND_8, 0xE8, + WRITE_BYTES, 16, + 0x09, 0xE8, 0xD8, 0xA0, + 0x0B, 0xEA, 0xD8, 0xA0, + 0x0D, 0xEC, 0xD8, 0xA0, + 0x0F, 0xEE, 0xD8, 0xA0, + + WRITE_COMMAND_8, 0xEB, + WRITE_BYTES, 7, + 0x02, 0x00, 0xE4, 0xE4, + 0x88, 0x00, 0x40, + + WRITE_C8_D16, 0xEC, 0x3C, 0x00, + + WRITE_COMMAND_8, 0xED, + WRITE_BYTES, 16, + 0xAB, 0x89, 0x76, 0x54, + 0x02, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x20, + 0x45, 0x67, 0x98, 0xBA, + + //-----------VAP & VAN--------------- + WRITE_COMMAND_8, 0xFF, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x13, + + WRITE_C8_D8, 0xE5, 0xE4, + + WRITE_COMMAND_8, 0xFF, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x00, + + WRITE_COMMAND_8, 0x21, // 0x20 normal, 0x21 IPS + WRITE_C8_D8, 0x3A, 0x60, // 0x70 RGB888, 0x60 RGB666, 0x50 RGB565 + + WRITE_COMMAND_8, 0x11, // Sleep Out + END_WRITE, + + DELAY, 120, + + BEGIN_WRITE, + WRITE_COMMAND_8, 0x29, // Display On + END_WRITE}; + +static const uint8_t st7701_type2_init_operations[] = { + BEGIN_WRITE, + WRITE_COMMAND_8, 0xFF, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x10, + + WRITE_C8_D16, 0xC0, 0xE9, 0x03, + WRITE_C8_D16, 0xC1, 0x11, 0x02, + WRITE_C8_D16, 0xC2, 0x31, 0x08, + + WRITE_COMMAND_8, 0xB0, // Positive Voltage Gamma Control + WRITE_BYTES, 16, + 0x00, 0x0D, 0x14, 0x0D, + 0x10, 0x05, 0x02, 0x08, + 0x08, 0x1E, 0x05, 0x13, + 0x11, 0xA3, 0x29, 0x18, + + WRITE_COMMAND_8, 0xB1, // Negative Voltage Gamma Control + WRITE_BYTES, 16, + 0x00, 0x0C, 0x14, 0x0C, + 0x10, 0x05, 0x03, 0x08, + 0x07, 0x20, 0x05, 0x13, + 0x11, 0xA4, 0x29, 0x18, + + // PAGE1 + WRITE_COMMAND_8, 0xFF, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x11, + + WRITE_C8_D8, 0xB0, 0x6C, + WRITE_C8_D8, 0xB1, 0x43, + WRITE_C8_D8, 0xB2, 0x07, + WRITE_C8_D8, 0xB3, 0x80, + WRITE_C8_D8, 0xB5, 0x47, + WRITE_C8_D8, 0xB7, 0x8A, + WRITE_C8_D8, 0xB8, 0x20, + WRITE_C8_D8, 0xC1, 0x78, + WRITE_C8_D8, 0xC2, 0x78, + + WRITE_C8_D8, 0xD0, 0x88, + + WRITE_COMMAND_8, 0xE0, + WRITE_BYTES, 3, 0x00, 0x00, 0x02, + + WRITE_COMMAND_8, 0xE1, + WRITE_BYTES, 11, + 0x08, 0x00, 0x0A, 0x00, + 0x07, 0x00, 0x09, 0x00, + 0x00, 0x33, 0x33, + + WRITE_COMMAND_8, 0xE2, + WRITE_BYTES, 12, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + WRITE_COMMAND_8, 0xE3, + WRITE_BYTES, 4, 0x00, 0x00, 0x33, 0x33, + + WRITE_C8_D16, 0xE4, 0x44, 0x44, + + WRITE_COMMAND_8, 0xE5, + WRITE_BYTES, 16, + 0x0E, 0x60, 0xA0, 0xA0, + 0x10, 0x60, 0xA0, 0xA0, + 0x0A, 0x60, 0xA0, 0xA0, + 0x0C, 0x60, 0xA0, 0xA0, + + WRITE_COMMAND_8, 0xE6, + WRITE_BYTES, 4, 0x00, 0x00, 0x33, 0x33, + + WRITE_C8_D16, 0xE7, 0x44, 0x44, + + WRITE_COMMAND_8, 0xE8, + WRITE_BYTES, 16, + 0x0D, 0x60, 0xA0, 0xA0, + 0x0F, 0x60, 0xA0, 0xA0, + 0x09, 0x60, 0xA0, 0xA0, + 0x0B, 0x60, 0xA0, 0xA0, + + WRITE_COMMAND_8, 0xEB, + WRITE_BYTES, 7, + 0x02, 0x01, 0xE4, 0xE4, + 0x44, 0x00, 0x40, + + WRITE_C8_D16, 0xEC, 0x02, 0x01, + + WRITE_COMMAND_8, 0xED, + WRITE_BYTES, 16, + 0xAB, 0x89, 0x76, 0x54, + 0x01, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x10, + 0x45, 0x67, 0x98, 0xBA, + + //-----------------------------------------End GIP Setting-----------------------------------------// + //--------------------------- Power Control Registers Initial End------------------------------// + //-------------------------------------Bank1 Setting------------------------------------------------// + WRITE_COMMAND_8, 0xFF, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x00, + + WRITE_COMMAND_8, 0x21, // 0x20 normal, 0x21 IPS + WRITE_C8_D8, 0x3A, 0x77, // RGB 24bits D[23:0] + + WRITE_COMMAND_8, 0x11, // Sleep Out + END_WRITE, + + DELAY, 100, + + BEGIN_WRITE, + WRITE_COMMAND_8, 0x29, // Display On + END_WRITE}; + +static const uint8_t st7701_type3_init_operations[] = { + BEGIN_WRITE, + WRITE_COMMAND_8, 0xFF, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x10, + + WRITE_C8_D16, 0xC0, 0x3B, 0x00, + WRITE_C8_D16, 0xC1, 0x0B, 0x02, // VBP + WRITE_C8_D16, 0xC2, 0x00, 0x02, + WRITE_C8_D8, 0xCC, 0x10, + WRITE_C8_D8, 0xCD, 0x08, + + WRITE_COMMAND_8, 0xB0, // Positive Voltage Gamma Control + WRITE_BYTES, 16, + 0x02, 0x13, 0x1B, 0x0D, + 0x10, 0x05, 0x08, 0x07, + 0x07, 0x24, 0x04, 0x11, + 0x0E, 0x2C, 0x33, 0x1D, + + WRITE_COMMAND_8, 0xB1, // Negative Voltage Gamma Control + WRITE_BYTES, 16, + 0x05, 0x13, 0x1B, 0x0D, + 0x11, 0x05, 0x08, 0x07, + 0x07, 0x24, 0x04, 0x11, + 0x0E, 0x2C, 0x33, 0x1D, + + // PAGE1 + WRITE_COMMAND_8, 0xFF, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x11, + + WRITE_C8_D8, 0xB0, 0x5d, // 5d + WRITE_C8_D8, 0xB1, 0x43, // VCOM amplitude setting + WRITE_C8_D8, 0xB2, 0x81, // VGH Voltage setting, 12V + WRITE_C8_D8, 0xB3, 0x80, + WRITE_C8_D8, 0xB5, 0x43, // VGL Voltage setting, -8.3V + WRITE_C8_D8, 0xB7, 0x85, + WRITE_C8_D8, 0xB8, 0x20, + + WRITE_C8_D8, 0xC1, 0x78, + WRITE_C8_D8, 0xC2, 0x78, + + WRITE_C8_D8, 0xD0, 0x88, + + WRITE_COMMAND_8, 0xE0, + WRITE_BYTES, 3, 0x00, 0x00, 0x02, + + WRITE_COMMAND_8, 0xE1, + WRITE_BYTES, 11, + 0x03, 0xA0, 0x00, 0x00, + 0x04, 0xA0, 0x00, 0x00, + 0x00, 0x20, 0x20, + + WRITE_COMMAND_8, 0xE2, + WRITE_BYTES, 12, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + WRITE_COMMAND_8, 0xE3, + WRITE_BYTES, 4, 0x00, 0x00, 0x11, 0x00, + + WRITE_C8_D16, 0xE4, 0x22, 0x00, + + WRITE_COMMAND_8, 0xE5, + WRITE_BYTES, 16, + 0x05, 0xEC, 0xA0, 0xA0, + 0x07, 0xEE, 0xA0, 0xA0, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + WRITE_COMMAND_8, 0xE6, + WRITE_BYTES, 4, 0x00, 0x00, 0x11, 0x00, + + WRITE_C8_D16, 0xE7, 0x22, 0x00, + + WRITE_COMMAND_8, 0xE8, + WRITE_BYTES, 16, + 0x06, 0xED, 0xA0, 0xA0, + 0x08, 0xEF, 0xA0, 0xA0, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + WRITE_COMMAND_8, 0xEB, + WRITE_BYTES, 7, + 0x00, 0x00, 0x40, 0x40, + 0x00, 0x00, 0x00, + + WRITE_COMMAND_8, 0xED, + WRITE_BYTES, 16, + 0xFF, 0xFF, 0xFF, 0xBA, + 0x0A, 0xBF, 0x45, 0xFF, + 0xFF, 0x54, 0xFB, 0xA0, + 0xAB, 0xFF, 0xFF, 0xFF, + + WRITE_COMMAND_8, 0xEF, + WRITE_BYTES, 6, + 0x10, 0x0D, 0x04, 0x08, + 0x3F, 0x1F, + + WRITE_COMMAND_8, 0xFF, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x13, + + WRITE_C8_D8, 0xEF, 0x08, + + WRITE_COMMAND_8, 0xFF, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x00, + + WRITE_COMMAND_8, 0x11, // Sleep Out + END_WRITE, + + DELAY, 120, + + BEGIN_WRITE, + WRITE_COMMAND_8, 0x29, // Display On + WRITE_COMMAND_8, 0x21, // 0x20 normal, 0x21 IPS + WRITE_C8_D8, 0x36, 0x00, // Display data access control + WRITE_C8_D8, 0x3A, 0x60, // 0x60 18bit 0x50 16bit + END_WRITE}; + +static const uint8_t st7701_type4_init_operations[] = { + BEGIN_WRITE, + + WRITE_COMMAND_8, 0xFF, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x10, + + WRITE_C8_D16, 0xC0, 0x3b, 0x00, + WRITE_C8_D16, 0xC1, 0x0b, 0x02, + WRITE_C8_D16, 0xC2, 0x07, 0x02, + WRITE_C8_D8, 0xCC, 0x10, + WRITE_C8_D8, 0xCD, 0x08, + + WRITE_COMMAND_8, 0xB0, // Positive Voltage Gamma Control + WRITE_BYTES, 16, + 0x00, 0x11, 0x16, 0x0e, + 0x11, 0x06, 0x05, 0x09, + 0x08, 0x21, 0x06, 0x13, + 0x10, 0x29, 0x31, 0x18, + + WRITE_COMMAND_8, 0xB1, // Negative Voltage Gamma Control + WRITE_BYTES, 16, + 0x00, 0x11, 0x16, 0x0e, + 0x11, 0x07, 0x05, 0x09, + 0x09, 0x21, 0x05, 0x13, + 0x11, 0x2a, 0x31, 0x18, + + WRITE_COMMAND_8, 0xFF, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x11, + + WRITE_C8_D8, 0xb0, 0x6d, + WRITE_C8_D8, 0xb1, 0x37, + WRITE_C8_D8, 0xb2, 0x81, + WRITE_C8_D8, 0xb3, 0x80, + WRITE_C8_D8, 0xb5, 0x43, + WRITE_C8_D8, 0xb7, 0x85, + WRITE_C8_D8, 0xb8, 0x20, + + WRITE_C8_D8, 0xc1, 0x78, + WRITE_C8_D8, 0xc2, 0x78, + WRITE_C8_D8, 0xc3, 0x8c, + + WRITE_C8_D8, 0xd0, 0x88, + + WRITE_COMMAND_8, 0xe0, + WRITE_BYTES, 3, 0x00, 0x00, 0x02, + WRITE_COMMAND_8, 0xe1, + WRITE_BYTES, 11, + 0x03, 0xa0, 0x00, 0x00, + 0x04, 0xa0, 0x00, 0x00, + 0x00, 0x20, 0x20, + WRITE_COMMAND_8, 0xe2, + WRITE_BYTES, 13, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, + WRITE_COMMAND_8, 0xe3, + WRITE_BYTES, 4, 0x00, 0x00, 0x11, 0x00, + WRITE_C8_D16, 0xe4, 0x22, 0x00, + WRITE_COMMAND_8, 0xe5, + WRITE_BYTES, 16, + 0x05, 0xec, 0xa0, 0xa0, + 0x07, 0xee, 0xa0, 0xa0, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + WRITE_COMMAND_8, 0xe6, + WRITE_BYTES, 4, 0x00, 0x00, 0x11, 0x00, + WRITE_C8_D16, 0xe7, 0x22, 0x00, + WRITE_COMMAND_8, 0xe8, + WRITE_BYTES, 16, + 0x06, 0xed, 0xa0, 0xa0, + 0x08, 0xef, 0xa0, 0xa0, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + WRITE_COMMAND_8, 0xeb, + WRITE_BYTES, 7, + 0x00, 0x00, 0x40, 0x40, + 0x00, 0x00, 0x00, + WRITE_COMMAND_8, 0xed, + WRITE_BYTES, 16, + 0xff, 0xff, 0xff, 0xba, + 0x0a, 0xbf, 0x45, 0xff, + 0xff, 0x54, 0xfb, 0xa0, + 0xab, 0xff, 0xff, 0xff, + WRITE_COMMAND_8, 0xef, + WRITE_BYTES, 6, + 0x10, 0x0d, 0x04, 0x08, + 0x3f, 0x1f, + WRITE_COMMAND_8, 0xff, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x13, + WRITE_C8_D8, 0xef, 0x08, + WRITE_COMMAND_8, 0xff, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x00, + WRITE_C8_D8, 0x36, 0x08, + WRITE_C8_D8, 0x3a, 0x66, + WRITE_C8_D8, 0x11, 0x00, + WRITE_C8_D8, 0x29, 0x00, + + WRITE_COMMAND_8, 0x11, // Sleep Out + END_WRITE, + + DELAY, 120, + + BEGIN_WRITE, + WRITE_COMMAND_8, 0x29, // Display On + WRITE_C8_D8, 0x36, 0x08, // Display data access control + WRITE_C8_D8, 0x3A, 0x60, // 0x60 18bit 0x50 16bit + END_WRITE}; + +static const uint8_t st7701_type5_init_operations[] = { + BEGIN_WRITE, + WRITE_COMMAND_8, 0xFF, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x10, + + WRITE_C8_D16, 0xC0, 0x3B, 0x00, + WRITE_C8_D16, 0xC1, 0x0B, 0x02, // VBP + WRITE_C8_D16, 0xC2, 0x00, 0x02, + + WRITE_C8_D8, 0xCC, 0x10, + WRITE_C8_D8, 0xCD, 0x08, + + WRITE_COMMAND_8, 0xB0, // Positive Voltage Gamma Control + WRITE_BYTES, 16, + 0x02, 0x13, 0x1B, 0x0D, + 0x10, 0x05, 0x08, 0x07, + 0x07, 0x24, 0x04, 0x11, + 0x0E, 0x2C, 0x33, 0x1D, + + WRITE_COMMAND_8, 0xB1, // Negative Voltage Gamma Control + WRITE_BYTES, 16, + 0x05, 0x13, 0x1B, 0x0D, + 0x11, 0x05, 0x08, 0x07, + 0x07, 0x24, 0x04, 0x11, + 0x0E, 0x2C, 0x33, 0x1D, + + WRITE_COMMAND_8, 0xFF, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x11, + + WRITE_C8_D8, 0xB0, 0x5d, // 5d + WRITE_C8_D8, 0xB1, 0x43, // VCOM amplitude setting + WRITE_C8_D8, 0xB2, 0x81, // VGH Voltage setting, 12V + WRITE_C8_D8, 0xB3, 0x80, + + WRITE_C8_D8, 0xB5, 0x43, // VGL Voltage setting, -8.3V + + WRITE_C8_D8, 0xB7, 0x85, + WRITE_C8_D8, 0xB8, 0x20, + + WRITE_C8_D8, 0xC1, 0x78, + WRITE_C8_D8, 0xC2, 0x78, + + WRITE_C8_D8, 0xD0, 0x88, + + WRITE_COMMAND_8, 0xE0, + WRITE_BYTES, 3, 0x00, 0x00, 0x02, + + WRITE_COMMAND_8, 0xE1, + WRITE_BYTES, 11, + 0x03, 0xA0, 0x00, 0x00, + 0x04, 0xA0, 0x00, 0x00, + 0x00, 0x20, 0x20, + + WRITE_COMMAND_8, 0xE2, + WRITE_BYTES, 13, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, + + WRITE_COMMAND_8, 0xE3, + WRITE_BYTES, 4, 0x00, 0x00, 0x11, 0x00, + + WRITE_C8_D16, 0xE4, 0x22, 0x00, + + WRITE_COMMAND_8, 0xE5, + WRITE_BYTES, 16, + 0x05, 0xEC, 0xA0, 0xA0, + 0x07, 0xEE, 0xA0, 0xA0, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + WRITE_COMMAND_8, 0xE6, + WRITE_BYTES, 4, 0x00, 0x00, 0x11, 0x00, + + WRITE_C8_D16, 0xE7, 0x22, 0x00, + + WRITE_COMMAND_8, 0xE8, + WRITE_BYTES, 16, + 0x06, 0xED, 0xA0, 0xA0, + 0x08, 0xEF, 0xA0, 0xA0, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + WRITE_COMMAND_8, 0xEB, + WRITE_BYTES, 7, + 0x00, 0x00, 0x40, 0x40, + 0x00, 0x00, 0x00, + + WRITE_COMMAND_8, 0xED, + WRITE_BYTES, 16, + 0xFF, 0xFF, 0xFF, 0xBA, + 0x0A, 0xBF, 0x45, 0xFF, + 0xFF, 0x54, 0xFB, 0xA0, + 0xAB, 0xFF, 0xFF, 0xFF, + + WRITE_COMMAND_8, 0xEF, + WRITE_BYTES, 6, + 0x10, 0x0D, 0x04, 0x08, + 0x3F, 0x1F, + + WRITE_COMMAND_8, 0xFF, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x13, + + WRITE_C8_D8, 0xEF, 0x08, + + WRITE_COMMAND_8, 0xFF, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x00, + + WRITE_C8_D8, 0x36, 0x08, + WRITE_C8_D8, 0x3A, 0x60, // 0x70 RGB888, 0x60 RGB666, 0x50 RGB565 + + WRITE_COMMAND_8, 0x11, // Sleep Out + END_WRITE, + + DELAY, 100, + + BEGIN_WRITE, + WRITE_COMMAND_8, 0x29, // Display On + END_WRITE, + + DELAY, 50}; + +static const uint8_t st7701_type6_init_operations[] = { + BEGIN_WRITE, + WRITE_COMMAND_8, 0xFF, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x13, + WRITE_C8_D8, 0xEF, 0x08, + + WRITE_COMMAND_8, 0xFF, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x10, + + WRITE_C8_D16, 0xC0, 0x3B, 0x00, + + WRITE_C8_D16, 0xC1, 0x10, 0x0C, + + WRITE_C8_D16, 0xC2, 0x07, 0x0A, + + WRITE_C8_D8, 0xC7, 0x00, + + WRITE_C8_D8, 0xCC, 0x10, + + WRITE_COMMAND_8, 0xB0, + WRITE_BYTES, 16, + 0x05, 0x12, 0x98, 0x0E, + 0x0F, 0x07, 0x07, 0x09, + 0x09, 0x23, 0x05, 0x52, + 0x0F, 0x67, 0x2C, 0x11, + + WRITE_COMMAND_8, 0xB1, + WRITE_BYTES, 16, + 0x0B, 0x11, 0x97, 0x0C, + 0x12, 0x06, 0x06, 0x08, + 0x08, 0x22, 0x03, 0x51, + 0x11, 0x66, 0x2B, 0x0F, + + WRITE_COMMAND_8, 0xFF, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x11, + + WRITE_C8_D8, 0xB0, 0x5D, + WRITE_C8_D8, 0xB1, 0x2D, + WRITE_C8_D8, 0xB2, 0x81, + WRITE_C8_D8, 0xB3, 0x80, + + WRITE_C8_D8, 0xB5, 0x4E, + + WRITE_C8_D8, 0xB7, 0x85, + WRITE_C8_D8, 0xB8, 0x20, + + WRITE_C8_D8, 0xC1, 0x78, + WRITE_C8_D8, 0xC2, 0x78, + + WRITE_C8_D8, 0xD0, 0x88, + + WRITE_COMMAND_8, 0xE0, + WRITE_BYTES, 3, 0x00, 0x00, 0x02, + + WRITE_COMMAND_8, 0xE1, + WRITE_BYTES, 11, + 0x06, 0x30, 0x08, 0x30, + 0x05, 0x30, 0x07, 0x30, + 0x00, 0x33, 0x33, + + WRITE_COMMAND_8, 0xE2, + WRITE_BYTES, 12, + 0x11, 0x11, 0x33, 0x33, + 0xF4, 0x00, 0x00, 0x00, + 0xF4, 0x00, 0x00, 0x00, + + WRITE_COMMAND_8, 0xE3, + WRITE_BYTES, 4, 0x00, 0x00, 0x11, 0x11, + + WRITE_C8_D16, 0xE4, 0x44, 0x44, + + WRITE_COMMAND_8, 0xE5, + WRITE_BYTES, 16, + 0x0D, 0xF5, 0x30, 0xF0, + 0x0F, 0xF7, 0x30, 0xF0, + 0x09, 0xF1, 0x30, 0xF0, + 0x0B, 0xF3, 0x30, 0xF0, + + WRITE_COMMAND_8, 0xE6, + WRITE_BYTES, 4, 0x00, 0x00, 0x11, 0x11, + + WRITE_C8_D16, 0xE7, 0x44, 0x44, + + WRITE_COMMAND_8, 0xE8, + WRITE_BYTES, 16, + 0x0C, 0xF4, 0x30, 0xF0, + 0x0E, 0xF6, 0x30, 0xF0, + 0x08, 0xF0, 0x30, 0xF0, + 0x0A, 0xF2, 0x30, 0xF0, + + WRITE_C8_D16, 0xE9, 0x36, 0x01, + + WRITE_COMMAND_8, 0xEB, + WRITE_BYTES, 7, + 0x00, 0x01, 0xE4, 0xE4, + 0x44, 0x88, 0x40, + + WRITE_COMMAND_8, 0xED, + WRITE_BYTES, 16, + 0xFF, 0x10, 0xAF, 0x76, + 0x54, 0x2B, 0xCF, 0xFF, + 0xFF, 0xFC, 0xB2, 0x45, + 0x67, 0xFA, 0x01, 0xFF, + + WRITE_COMMAND_8, 0xEF, + WRITE_BYTES, 6, + 0x08, 0x08, 0x08, 0x45, + 0x3F, 0x54, + + WRITE_COMMAND_8, 0xFF, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x00, + + WRITE_COMMAND_8, 0x11, + END_WRITE, + + DELAY, 120, // ms + + BEGIN_WRITE, + WRITE_C8_D8, 0x3A, 0x66, + + WRITE_C8_D8, 0x36, 0x00, + + WRITE_C8_D8, 0x35, 0x00, + + WRITE_COMMAND_8, 0x29, // Display On + END_WRITE}; + +static const uint8_t st7701_type7_init_operations[] = { + BEGIN_WRITE, + + WRITE_COMMAND_8, 0xFF, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x10, + + WRITE_C8_D16, 0xC0, 0x3b, 0x00, + WRITE_C8_D16, 0xC1, 0x0b, 0x02, + WRITE_C8_D16, 0xC2, 0x07, 0x02, + WRITE_C8_D8, 0xCC, 0x10, + WRITE_C8_D8, 0xCD, 0x08, + + WRITE_COMMAND_8, 0xB0, // Positive Voltage Gamma Control + WRITE_BYTES, 16, + 0x00, 0x11, 0x16, 0x0e, + 0x11, 0x06, 0x05, 0x09, + 0x08, 0x21, 0x06, 0x13, + 0x10, 0x29, 0x31, 0x18, + + WRITE_COMMAND_8, 0xB1, // Negative Voltage Gamma Control + WRITE_BYTES, 16, + 0x00, 0x11, 0x16, 0x0e, + 0x11, 0x07, 0x05, 0x09, + 0x09, 0x21, 0x05, 0x13, + 0x11, 0x2a, 0x31, 0x18, + + WRITE_COMMAND_8, 0xFF, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x11, + + WRITE_C8_D8, 0xb0, 0x6d, + WRITE_C8_D8, 0xb1, 0x37, + WRITE_C8_D8, 0xb2, 0x81, + WRITE_C8_D8, 0xb3, 0x80, + WRITE_C8_D8, 0xb5, 0x43, + WRITE_C8_D8, 0xb7, 0x85, + WRITE_C8_D8, 0xb8, 0x20, + + WRITE_C8_D8, 0xc1, 0x78, + WRITE_C8_D8, 0xc2, 0x78, + + WRITE_C8_D8, 0xd0, 0x88, + + WRITE_COMMAND_8, 0xe0, + WRITE_BYTES, 3, 0x00, 0x00, 0x02, + WRITE_COMMAND_8, 0xe1, + WRITE_BYTES, 11, + 0x03, 0xa0, 0x00, 0x00, + 0x04, 0xa0, 0x00, 0x00, + 0x00, 0x20, 0x20, + WRITE_COMMAND_8, 0xe2, + WRITE_BYTES, 13, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, + WRITE_COMMAND_8, 0xe3, + WRITE_BYTES, 4, 0x00, 0x00, 0x11, 0x00, + WRITE_C8_D16, 0xe4, 0x22, 0x00, + WRITE_COMMAND_8, 0xe5, + WRITE_BYTES, 16, + 0x05, 0xec, 0xa0, 0xa0, + 0x07, 0xee, 0xa0, 0xa0, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + WRITE_COMMAND_8, 0xe6, + WRITE_BYTES, 4, 0x00, 0x00, 0x11, 0x00, + WRITE_C8_D16, 0xe7, 0x22, 0x00, + WRITE_COMMAND_8, 0xe8, + WRITE_BYTES, 16, + 0x06, 0xed, 0xa0, 0xa0, + 0x08, 0xef, 0xa0, 0xa0, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + WRITE_COMMAND_8, 0xeb, + WRITE_BYTES, 7, + 0x00, 0x00, 0x40, 0x40, + 0x00, 0x00, 0x00, + WRITE_COMMAND_8, 0xed, + WRITE_BYTES, 16, + 0xff, 0xff, 0xff, 0xba, + 0x0a, 0xbf, 0x45, 0xff, + 0xff, 0x54, 0xfb, 0xa0, + 0xab, 0xff, 0xff, 0xff, + WRITE_COMMAND_8, 0xef, + WRITE_BYTES, 6, + 0x10, 0x0d, 0x04, 0x08, + 0x3f, 0x1f, + WRITE_COMMAND_8, 0xff, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x13, + WRITE_C8_D8, 0xef, 0x08, + WRITE_COMMAND_8, 0xff, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x00, + WRITE_C8_D8, 0x36, 0x00, + WRITE_C8_D8, 0x3a, 0x66, + WRITE_C8_D8, 0x11, 0x00, + + WRITE_COMMAND_8, 0x11, // Sleep Out + END_WRITE, + + DELAY, 120, + + BEGIN_WRITE, + WRITE_COMMAND_8, 0x29, // Display On + END_WRITE}; + +static const uint8_t st7701_type8_init_operations[] = { + BEGIN_WRITE, + + WRITE_COMMAND_8, 0xFF, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x13, + + WRITE_C8_D8, 0xEF, 0x08, + + WRITE_COMMAND_8, 0xFF, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x10, + + WRITE_C8_D16, 0xC0, 0x2C, 0x00, + WRITE_C8_D16, 0xC1, 0x0D, 0x02, + WRITE_C8_D16, 0xC2, 0x31, 0x05, + WRITE_C8_D8, 0xCC, 0x10, + + WRITE_COMMAND_8, 0xB0, // Positive Voltage Gamma Control + WRITE_BYTES, 16, + 0x0A, 0x14, 0x1B, 0x0D, + 0x10, 0x05, 0x07, 0x08, + 0x06, 0x22, 0x03, 0x11, + 0x10, 0xAD, 0x31, 0x1B, + + WRITE_COMMAND_8, 0xB1, // Negative Voltage Gamma Control + WRITE_BYTES, 16, + 0x0A, 0x14, 0x1B, 0x0D, + 0x10, 0x05, 0x07, 0x08, + 0x06, 0x22, 0x03, 0x11, + 0x10, 0xAD, 0x31, 0x1B, + + WRITE_COMMAND_8, 0xFF, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x11, + + WRITE_C8_D8, 0xB0, 0x50, + WRITE_C8_D8, 0xB1, 0x5E, + WRITE_C8_D8, 0xB2, 0x87, + WRITE_C8_D8, 0xB3, 0x80, + WRITE_C8_D8, 0xB5, 0x47, + WRITE_C8_D8, 0xB7, 0x85, + WRITE_C8_D8, 0xB8, 0x21, + + WRITE_C8_D8, 0xC1, 0x78, + WRITE_C8_D8, 0xC2, 0x78, + + WRITE_C8_D8, 0xD0, 0x88, + + WRITE_C8_D8, 0xE0, 0x00, + + WRITE_C8_D8, 0x1B, 0x02, + + WRITE_COMMAND_8, 0xE1, + WRITE_BYTES, 11, + 0x08, 0xA0, 0x00, 0x00, + 0x07, 0xA0, 0x00, 0x00, + 0x00, 0x44, 0x44, + + WRITE_COMMAND_8, 0xE2, + WRITE_BYTES, 12, + 0x11, 0x11, 0x44, 0x44, + 0x75, 0xA0, 0x00, 0x00, + 0x74, 0xA0, 0x00, 0x00, + + WRITE_COMMAND_8, 0xE3, + WRITE_BYTES, 4, 0x00, 0x00, 0x11, 0x11, + + WRITE_C8_D16, 0xE4, 0x44, 0x44, + + WRITE_COMMAND_8, 0xE5, + WRITE_BYTES, 16, + 0x0A, 0x71, 0xD8, 0xA0, + 0x0C, 0x73, 0xD8, 0xA0, + 0x0E, 0x75, 0xD8, 0xA0, + 0x10, 0x77, 0xD8, 0xA0, + + WRITE_COMMAND_8, 0xE6, + WRITE_BYTES, 4, 0x00, 0x00, 0x11, 0x11, + + WRITE_C8_D16, 0xE7, 0x44, 0x44, + + WRITE_COMMAND_8, 0xE8, + WRITE_BYTES, 16, + 0x09, 0x70, 0xD8, 0xA0, + 0x0B, 0x72, 0xD8, 0xA0, + 0x0D, 0x74, 0xD8, 0xA0, + 0x0F, 0x76, 0xD8, 0xA0, + + WRITE_COMMAND_8, 0xEB, + WRITE_BYTES, 7, + 0x02, 0x00, 0xE4, 0xE4, + 0x88, 0x00, 0x40, + + WRITE_C8_D16, 0xEC, 0x3C, 0x00, + + WRITE_COMMAND_8, 0xED, + WRITE_BYTES, 16, + 0xAB, 0x89, 0x76, 0x54, + 0x02, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x20, + 0x45, 0x67, 0x98, 0xBA, + + WRITE_COMMAND_8, 0xEF, + WRITE_BYTES, 6, + 0x08, 0x08, 0x08, 0x45, + 0x3F, 0x54, + + WRITE_COMMAND_8, 0xFF, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x13, + + WRITE_C8_D16, 0xE8, 0x00, 0x0E, + + WRITE_COMMAND_8, 0x20, // 0x20 normal, 0x21 IPS + WRITE_C8_D8, 0x3A, 0x50, // 0x70 RGB888, 0x60 RGB666, 0x50 RGB565 + + WRITE_COMMAND_8, 0x11, // Sleep Out + END_WRITE, + + DELAY, 150, + + BEGIN_WRITE, + WRITE_C8_D16, 0xE8, 0x00, 0x0C, + END_WRITE, + + DELAY, 100, + + BEGIN_WRITE, + WRITE_C8_D16, 0xE8, 0x00, 0x00, + + WRITE_COMMAND_8, 0xFF, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x00, + + WRITE_COMMAND_8, 0x29, // Display On + END_WRITE, + DELAY, 20}; + +static const uint8_t st7701_type9_init_operations[] = { + + BEGIN_WRITE, + WRITE_COMMAND_8, 0xFF, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x13, + + WRITE_C8_D8, 0xEF, 0x08, + + WRITE_COMMAND_8, 0xFF, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x10, + + WRITE_C8_D16, 0xC0, 0x3B, 0x00, + WRITE_C8_D16, 0xC1, 0x0B, 0x02, + + WRITE_COMMAND_8, 0xC2, + WRITE_BYTES, 3, 0x30, 0x02, 0x37, + + WRITE_C8_D8, 0xCC, 0x10, + + WRITE_COMMAND_8, 0xB0, // Positive Voltage Gamma Control + WRITE_BYTES, 16, + 0x00, 0x0F, 0x16, 0x0E, + 0x11, 0x07, 0x09, 0x09, + 0x08, 0x23, 0x05, 0x11, + 0x0F, 0x28, 0x2D, 0x18, + + WRITE_COMMAND_8, 0xB1, // Negative Voltage Gamma Control + WRITE_BYTES, 16, + 0x00, 0x0F, 0x16, 0x0E, + 0x11, 0x07, 0x09, 0x08, + 0x09, 0x23, 0x05, 0x11, + 0x0F, 0x28, 0x2D, 0x18, + + WRITE_COMMAND_8, 0xFF, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x11, + + WRITE_C8_D8, 0xB0, 0x4D, + WRITE_C8_D8, 0xB1, 0x33, + WRITE_C8_D8, 0xB2, 0x87, + WRITE_C8_D8, 0xB5, 0x4B, + WRITE_C8_D8, 0xB7, 0x8C, + WRITE_C8_D8, 0xB8, 0x20, + WRITE_C8_D8, 0xC1, 0x78, + WRITE_C8_D8, 0xC2, 0x78, + WRITE_C8_D8, 0xD0, 0x88, + + WRITE_COMMAND_8, 0xE0, + WRITE_BYTES, 3, 0x00, 0x00, 0x02, + + WRITE_COMMAND_8, 0xE1, + WRITE_BYTES, 11, + 0x02, 0xF0, 0x00, 0x00, + 0x03, 0xF0, 0x00, 0x00, + 0x00, 0x44, 0x44, + + WRITE_COMMAND_8, 0xE2, + WRITE_BYTES, 12, + 0x10, 0x10, 0x40, 0x40, + 0xF2, 0xF0, 0x00, 0x00, + 0xF2, 0xF0, 0x00, 0x00, + + WRITE_COMMAND_8, 0xE3, + WRITE_BYTES, 4, 0x00, 0x00, 0x11, 0x11, + + WRITE_C8_D16, 0xE4, 0x44, 0x44, + + WRITE_COMMAND_8, 0xE5, + WRITE_BYTES, 16, + 0x07, 0xEF, 0xF0, 0xF0, + 0x09, 0xF1, 0xF0, 0xF0, + 0x03, 0xF3, 0xF0, 0xF0, + 0x05, 0xED, 0xF0, 0xF0, + + WRITE_COMMAND_8, 0xE6, + WRITE_BYTES, 4, 0x00, 0x00, 0x11, 0x11, + + WRITE_C8_D16, 0xE7, 0x44, 0x44, + + WRITE_COMMAND_8, 0xE8, + WRITE_BYTES, 16, + 0x08, 0xF0, 0xF0, 0xF0, + 0x0A, 0xF2, 0xF0, 0xF0, + 0x04, 0xF4, 0xF0, 0xF0, + 0x06, 0xEE, 0xF0, 0xF0, + + WRITE_COMMAND_8, 0xEB, + WRITE_BYTES, 7, + 0x00, 0x00, 0xE4, 0xE4, + 0x44, 0x88, 0x40, + + WRITE_C8_D16, 0xEC, 0x78, 0x00, + + WRITE_COMMAND_8, 0xED, + WRITE_BYTES, 16, + 0x20, 0xF9, 0x87, 0x76, + 0x65, 0x54, 0x4F, 0xFF, + 0xFF, 0xF4, 0x45, 0x56, + 0x67, 0x78, 0x9F, 0x02, + + WRITE_COMMAND_8, 0xEF, + WRITE_BYTES, 6, + 0x10, 0x0D, 0x04, 0x08, + 0x3F, 0x1F, + + // WRITE_C8_D8, 0xCD, 0x05,//Test + + WRITE_C8_D8, 0x3A, 0x55, + + WRITE_C8_D8, 0x36, 0x08, + + WRITE_COMMAND_8, 0x11, + + // WRITE_COMMAND_8, 0xFF,//Test + // WRITE_BYTES, 5, + // 0x77, 0x01, 0x00, 0x00, + // 0x12, + + // WRITE_C8_D8, 0xD1, 0x81,//Test + // WRITE_C8_D8, 0xD2, 0x08,//Test + + WRITE_COMMAND_8, 0x29, // Display On + + // WRITE_C8_D8, 0x35, 0x00,//Test + // WRITE_C8_D8, 0xCE, 0x04,//Test + + // WRITE_COMMAND_8, 0xF2,//Test + // WRITE_BYTES, 4, + // 0xF0, 0xA3, 0xA3, 0x71, + + END_WRITE}; + +class Arduino_RGB_Display : public Arduino_GFX +{ +public: + Arduino_RGB_Display( + int16_t w, int16_t h, Arduino_ESP32RGBPanel *rgbpanel, uint8_t r = 0, bool auto_flush = true, + Arduino_DataBus *bus = NULL, int8_t rst = GFX_NOT_DEFINED, const uint8_t *init_operations = NULL, size_t init_operations_len = GFX_NOT_DEFINED); + + bool begin(int32_t speed = GFX_NOT_DEFINED) override; + void writePixelPreclipped(int16_t x, int16_t y, uint16_t color) override; + void writeFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color) override; + void writeFastVLineCore(int16_t x, int16_t y, int16_t h, uint16_t color); + void writeFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color) override; + void writeFastHLineCore(int16_t x, int16_t y, int16_t w, uint16_t color); + void writeFillRectPreclipped(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) override; + void drawIndexedBitmap(int16_t x, int16_t y, uint8_t *bitmap, uint16_t *color_index, int16_t w, int16_t h, int16_t x_skip = 0) override; + void draw16bitRGBBitmap(int16_t x, int16_t y, uint16_t *bitmap, int16_t w, int16_t h) override; + void draw16bitBeRGBBitmap(int16_t x, int16_t y, uint16_t *bitmap, int16_t w, int16_t h) override; + void flush(void) override; + + uint16_t *getFramebuffer(); + + void XL_pinMode(uint8_t pin, uint8_t mode); + void XL_digitalWrite(uint8_t pin, uint8_t val); + int XL_digitalRead(uint8_t pin); + +protected: + uint16_t *_framebuffer; + size_t _framebuffer_size; + Arduino_ESP32RGBPanel *_rgbpanel; + bool _auto_flush; + Arduino_DataBus *_bus; + int8_t _rst; + const uint8_t *_init_operations; + size_t _init_operations_len; + int16_t MAX_X, MAX_Y; + +private: +}; + +#endif // _ARDUINO_RGB_DISPLAY_H_ + +#endif // #if defined(ESP32) && (CONFIG_IDF_TARGET_ESP32S3) diff --git a/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/font/glcdfont.h b/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/font/glcdfont.h new file mode 100644 index 00000000..630cba63 --- /dev/null +++ b/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/font/glcdfont.h @@ -0,0 +1,280 @@ +// This is the 'classic' fixed-space bitmap font for Adafruit_GFX since 1.0. +// See gfxfont.h for newer custom bitmap font info. + +#ifndef FONT5X7_H +#define FONT5X7_H + +#ifdef __AVR__ +#include +#include +#elif defined(ESP8266) +#include +#elif defined(__IMXRT1052__) || defined(__IMXRT1062__) +// PROGMEM is defefind for T4 to place data in specific memory section +#undef PROGMEM +#define PROGMEM +#else +#define PROGMEM +#endif + +// Standard ASCII 5x7 font + +static const unsigned char font[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3E, 0x5B, 0x4F, 0x5B, 0x3E, + 0x3E, 0x6B, 0x4F, 0x6B, 0x3E, + 0x1C, 0x3E, 0x7C, 0x3E, 0x1C, + 0x18, 0x3C, 0x7E, 0x3C, 0x18, + 0x1C, 0x57, 0x7D, 0x57, 0x1C, + 0x1C, 0x5E, 0x7F, 0x5E, 0x1C, + 0x00, 0x18, 0x3C, 0x18, 0x00, + 0xFF, 0xE7, 0xC3, 0xE7, 0xFF, + 0x00, 0x18, 0x24, 0x18, 0x00, + 0xFF, 0xE7, 0xDB, 0xE7, 0xFF, + 0x30, 0x48, 0x3A, 0x06, 0x0E, + 0x26, 0x29, 0x79, 0x29, 0x26, + 0x40, 0x7F, 0x05, 0x05, 0x07, + 0x40, 0x7F, 0x05, 0x25, 0x3F, + 0x5A, 0x3C, 0xE7, 0x3C, 0x5A, + 0x7F, 0x3E, 0x1C, 0x1C, 0x08, + 0x08, 0x1C, 0x1C, 0x3E, 0x7F, + 0x14, 0x22, 0x7F, 0x22, 0x14, + 0x5F, 0x5F, 0x00, 0x5F, 0x5F, + 0x06, 0x09, 0x7F, 0x01, 0x7F, + 0x00, 0x66, 0x89, 0x95, 0x6A, + 0x60, 0x60, 0x60, 0x60, 0x60, + 0x94, 0xA2, 0xFF, 0xA2, 0x94, + 0x08, 0x04, 0x7E, 0x04, 0x08, + 0x10, 0x20, 0x7E, 0x20, 0x10, + 0x08, 0x08, 0x2A, 0x1C, 0x08, + 0x08, 0x1C, 0x2A, 0x08, 0x08, + 0x1E, 0x10, 0x10, 0x10, 0x10, + 0x0C, 0x1E, 0x0C, 0x1E, 0x0C, + 0x30, 0x38, 0x3E, 0x38, 0x30, + 0x06, 0x0E, 0x3E, 0x0E, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x5F, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x07, 0x00, + 0x14, 0x7F, 0x14, 0x7F, 0x14, + 0x24, 0x2A, 0x7F, 0x2A, 0x12, + 0x23, 0x13, 0x08, 0x64, 0x62, + 0x36, 0x49, 0x56, 0x20, 0x50, + 0x00, 0x08, 0x07, 0x03, 0x00, + 0x00, 0x1C, 0x22, 0x41, 0x00, + 0x00, 0x41, 0x22, 0x1C, 0x00, + 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, + 0x08, 0x08, 0x3E, 0x08, 0x08, + 0x00, 0x80, 0x70, 0x30, 0x00, + 0x08, 0x08, 0x08, 0x08, 0x08, + 0x00, 0x00, 0x60, 0x60, 0x00, + 0x20, 0x10, 0x08, 0x04, 0x02, + 0x3E, 0x51, 0x49, 0x45, 0x3E, + 0x00, 0x42, 0x7F, 0x40, 0x00, + 0x72, 0x49, 0x49, 0x49, 0x46, + 0x21, 0x41, 0x49, 0x4D, 0x33, + 0x18, 0x14, 0x12, 0x7F, 0x10, + 0x27, 0x45, 0x45, 0x45, 0x39, + 0x3C, 0x4A, 0x49, 0x49, 0x31, + 0x41, 0x21, 0x11, 0x09, 0x07, + 0x36, 0x49, 0x49, 0x49, 0x36, + 0x46, 0x49, 0x49, 0x29, 0x1E, + 0x00, 0x00, 0x14, 0x00, 0x00, + 0x00, 0x40, 0x34, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x22, 0x41, + 0x14, 0x14, 0x14, 0x14, 0x14, + 0x00, 0x41, 0x22, 0x14, 0x08, + 0x02, 0x01, 0x59, 0x09, 0x06, + 0x3E, 0x41, 0x5D, 0x59, 0x4E, + 0x7C, 0x12, 0x11, 0x12, 0x7C, + 0x7F, 0x49, 0x49, 0x49, 0x36, + 0x3E, 0x41, 0x41, 0x41, 0x22, + 0x7F, 0x41, 0x41, 0x41, 0x3E, + 0x7F, 0x49, 0x49, 0x49, 0x41, + 0x7F, 0x09, 0x09, 0x09, 0x01, + 0x3E, 0x41, 0x41, 0x51, 0x73, + 0x7F, 0x08, 0x08, 0x08, 0x7F, + 0x00, 0x41, 0x7F, 0x41, 0x00, + 0x20, 0x40, 0x41, 0x3F, 0x01, + 0x7F, 0x08, 0x14, 0x22, 0x41, + 0x7F, 0x40, 0x40, 0x40, 0x40, + 0x7F, 0x02, 0x1C, 0x02, 0x7F, + 0x7F, 0x04, 0x08, 0x10, 0x7F, + 0x3E, 0x41, 0x41, 0x41, 0x3E, + 0x7F, 0x09, 0x09, 0x09, 0x06, + 0x3E, 0x41, 0x51, 0x21, 0x5E, + 0x7F, 0x09, 0x19, 0x29, 0x46, + 0x26, 0x49, 0x49, 0x49, 0x32, + 0x03, 0x01, 0x7F, 0x01, 0x03, + 0x3F, 0x40, 0x40, 0x40, 0x3F, + 0x1F, 0x20, 0x40, 0x20, 0x1F, + 0x3F, 0x40, 0x38, 0x40, 0x3F, + 0x63, 0x14, 0x08, 0x14, 0x63, + 0x03, 0x04, 0x78, 0x04, 0x03, + 0x61, 0x59, 0x49, 0x4D, 0x43, + 0x00, 0x7F, 0x41, 0x41, 0x41, + 0x02, 0x04, 0x08, 0x10, 0x20, + 0x00, 0x41, 0x41, 0x41, 0x7F, + 0x04, 0x02, 0x01, 0x02, 0x04, + 0x40, 0x40, 0x40, 0x40, 0x40, + 0x00, 0x03, 0x07, 0x08, 0x00, + 0x20, 0x54, 0x54, 0x78, 0x40, + 0x7F, 0x28, 0x44, 0x44, 0x38, + 0x38, 0x44, 0x44, 0x44, 0x28, + 0x38, 0x44, 0x44, 0x28, 0x7F, + 0x38, 0x54, 0x54, 0x54, 0x18, + 0x00, 0x08, 0x7E, 0x09, 0x02, + 0x18, 0xA4, 0xA4, 0x9C, 0x78, + 0x7F, 0x08, 0x04, 0x04, 0x78, + 0x00, 0x44, 0x7D, 0x40, 0x00, + 0x20, 0x40, 0x40, 0x3D, 0x00, + 0x7F, 0x10, 0x28, 0x44, 0x00, + 0x00, 0x41, 0x7F, 0x40, 0x00, + 0x7C, 0x04, 0x78, 0x04, 0x78, + 0x7C, 0x08, 0x04, 0x04, 0x78, + 0x38, 0x44, 0x44, 0x44, 0x38, + 0xFC, 0x18, 0x24, 0x24, 0x18, + 0x18, 0x24, 0x24, 0x18, 0xFC, + 0x7C, 0x08, 0x04, 0x04, 0x08, + 0x48, 0x54, 0x54, 0x54, 0x24, + 0x04, 0x04, 0x3F, 0x44, 0x24, + 0x3C, 0x40, 0x40, 0x20, 0x7C, + 0x1C, 0x20, 0x40, 0x20, 0x1C, + 0x3C, 0x40, 0x30, 0x40, 0x3C, + 0x44, 0x28, 0x10, 0x28, 0x44, + 0x4C, 0x90, 0x90, 0x90, 0x7C, + 0x44, 0x64, 0x54, 0x4C, 0x44, + 0x00, 0x08, 0x36, 0x41, 0x00, + 0x00, 0x00, 0x77, 0x00, 0x00, + 0x00, 0x41, 0x36, 0x08, 0x00, + 0x02, 0x01, 0x02, 0x04, 0x02, + 0x3C, 0x26, 0x23, 0x26, 0x3C, + 0x1E, 0xA1, 0xA1, 0x61, 0x12, + 0x3A, 0x40, 0x40, 0x20, 0x7A, + 0x38, 0x54, 0x54, 0x55, 0x59, + 0x21, 0x55, 0x55, 0x79, 0x41, + 0x22, 0x54, 0x54, 0x78, 0x42, // a-umlaut + 0x21, 0x55, 0x54, 0x78, 0x40, + 0x20, 0x54, 0x55, 0x79, 0x40, + 0x0C, 0x1E, 0x52, 0x72, 0x12, + 0x39, 0x55, 0x55, 0x55, 0x59, + 0x39, 0x54, 0x54, 0x54, 0x59, + 0x39, 0x55, 0x54, 0x54, 0x58, + 0x00, 0x00, 0x45, 0x7C, 0x41, + 0x00, 0x02, 0x45, 0x7D, 0x42, + 0x00, 0x01, 0x45, 0x7C, 0x40, + 0x7D, 0x12, 0x11, 0x12, 0x7D, // A-umlaut + 0xF0, 0x28, 0x25, 0x28, 0xF0, + 0x7C, 0x54, 0x55, 0x45, 0x00, + 0x20, 0x54, 0x54, 0x7C, 0x54, + 0x7C, 0x0A, 0x09, 0x7F, 0x49, + 0x32, 0x49, 0x49, 0x49, 0x32, + 0x3A, 0x44, 0x44, 0x44, 0x3A, // o-umlaut + 0x32, 0x4A, 0x48, 0x48, 0x30, + 0x3A, 0x41, 0x41, 0x21, 0x7A, + 0x3A, 0x42, 0x40, 0x20, 0x78, + 0x00, 0x9D, 0xA0, 0xA0, 0x7D, + 0x3D, 0x42, 0x42, 0x42, 0x3D, // O-umlaut + 0x3D, 0x40, 0x40, 0x40, 0x3D, + 0x3C, 0x24, 0xFF, 0x24, 0x24, + 0x48, 0x7E, 0x49, 0x43, 0x66, + 0x2B, 0x2F, 0xFC, 0x2F, 0x2B, + 0xFF, 0x09, 0x29, 0xF6, 0x20, + 0xC0, 0x88, 0x7E, 0x09, 0x03, + 0x20, 0x54, 0x54, 0x79, 0x41, + 0x00, 0x00, 0x44, 0x7D, 0x41, + 0x30, 0x48, 0x48, 0x4A, 0x32, + 0x38, 0x40, 0x40, 0x22, 0x7A, + 0x00, 0x7A, 0x0A, 0x0A, 0x72, + 0x7D, 0x0D, 0x19, 0x31, 0x7D, + 0x26, 0x29, 0x29, 0x2F, 0x28, + 0x26, 0x29, 0x29, 0x29, 0x26, + 0x30, 0x48, 0x4D, 0x40, 0x20, + 0x38, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x38, + 0x2F, 0x10, 0xC8, 0xAC, 0xBA, + 0x2F, 0x10, 0x28, 0x34, 0xFA, + 0x00, 0x00, 0x7B, 0x00, 0x00, + 0x08, 0x14, 0x2A, 0x14, 0x22, + 0x22, 0x14, 0x2A, 0x14, 0x08, + 0x55, 0x00, 0x55, 0x00, 0x55, // #176 (25% block) missing in old code + 0xAA, 0x55, 0xAA, 0x55, 0xAA, // 50% block + 0xFF, 0x55, 0xFF, 0x55, 0xFF, // 75% block + 0x00, 0x00, 0x00, 0xFF, 0x00, + 0x10, 0x10, 0x10, 0xFF, 0x00, + 0x14, 0x14, 0x14, 0xFF, 0x00, + 0x10, 0x10, 0xFF, 0x00, 0xFF, + 0x10, 0x10, 0xF0, 0x10, 0xF0, + 0x14, 0x14, 0x14, 0xFC, 0x00, + 0x14, 0x14, 0xF7, 0x00, 0xFF, + 0x00, 0x00, 0xFF, 0x00, 0xFF, + 0x14, 0x14, 0xF4, 0x04, 0xFC, + 0x14, 0x14, 0x17, 0x10, 0x1F, + 0x10, 0x10, 0x1F, 0x10, 0x1F, + 0x14, 0x14, 0x14, 0x1F, 0x00, + 0x10, 0x10, 0x10, 0xF0, 0x00, + 0x00, 0x00, 0x00, 0x1F, 0x10, + 0x10, 0x10, 0x10, 0x1F, 0x10, + 0x10, 0x10, 0x10, 0xF0, 0x10, + 0x00, 0x00, 0x00, 0xFF, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0xFF, 0x10, + 0x00, 0x00, 0x00, 0xFF, 0x14, + 0x00, 0x00, 0xFF, 0x00, 0xFF, + 0x00, 0x00, 0x1F, 0x10, 0x17, + 0x00, 0x00, 0xFC, 0x04, 0xF4, + 0x14, 0x14, 0x17, 0x10, 0x17, + 0x14, 0x14, 0xF4, 0x04, 0xF4, + 0x00, 0x00, 0xFF, 0x00, 0xF7, + 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0xF7, 0x00, 0xF7, + 0x14, 0x14, 0x14, 0x17, 0x14, + 0x10, 0x10, 0x1F, 0x10, 0x1F, + 0x14, 0x14, 0x14, 0xF4, 0x14, + 0x10, 0x10, 0xF0, 0x10, 0xF0, + 0x00, 0x00, 0x1F, 0x10, 0x1F, + 0x00, 0x00, 0x00, 0x1F, 0x14, + 0x00, 0x00, 0x00, 0xFC, 0x14, + 0x00, 0x00, 0xF0, 0x10, 0xF0, + 0x10, 0x10, 0xFF, 0x10, 0xFF, + 0x14, 0x14, 0x14, 0xFF, 0x14, + 0x10, 0x10, 0x10, 0x1F, 0x00, + 0x00, 0x00, 0x00, 0xF0, 0x10, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, + 0x38, 0x44, 0x44, 0x38, 0x44, + 0xFC, 0x4A, 0x4A, 0x4A, 0x34, // sharp-s or beta + 0x7E, 0x02, 0x02, 0x06, 0x06, + 0x02, 0x7E, 0x02, 0x7E, 0x02, + 0x63, 0x55, 0x49, 0x41, 0x63, + 0x38, 0x44, 0x44, 0x3C, 0x04, + 0x40, 0x7E, 0x20, 0x1E, 0x20, + 0x06, 0x02, 0x7E, 0x02, 0x02, + 0x99, 0xA5, 0xE7, 0xA5, 0x99, + 0x1C, 0x2A, 0x49, 0x2A, 0x1C, + 0x4C, 0x72, 0x01, 0x72, 0x4C, + 0x30, 0x4A, 0x4D, 0x4D, 0x30, + 0x30, 0x48, 0x78, 0x48, 0x30, + 0xBC, 0x62, 0x5A, 0x46, 0x3D, + 0x3E, 0x49, 0x49, 0x49, 0x00, + 0x7E, 0x01, 0x01, 0x01, 0x7E, + 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, + 0x44, 0x44, 0x5F, 0x44, 0x44, + 0x40, 0x51, 0x4A, 0x44, 0x40, + 0x40, 0x44, 0x4A, 0x51, 0x40, + 0x00, 0x00, 0xFF, 0x01, 0x03, + 0xE0, 0x80, 0xFF, 0x00, 0x00, + 0x08, 0x08, 0x6B, 0x6B, 0x08, + 0x36, 0x12, 0x36, 0x24, 0x36, + 0x00, 0x06, 0x09, 0x09, 0x06, + 0x00, 0x00, 0x18, 0x18, 0x00, + 0x00, 0x00, 0x10, 0x10, 0x00, + 0x30, 0x40, 0xFF, 0x01, 0x01, + 0x00, 0x1F, 0x01, 0x01, 0x1E, + 0x00, 0x19, 0x1D, 0x17, 0x12, + 0x00, 0x3C, 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, 0x00, 0x00 // #255 NBSP +}; +#endif // FONT5X7_H diff --git a/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/gfxfont.h b/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/gfxfont.h new file mode 100644 index 00000000..42cfcfc6 --- /dev/null +++ b/ESP32_AP-Flasher/lib2/Arduino_GFX-1.3.7/src/gfxfont.h @@ -0,0 +1,31 @@ +// Font structures for newer Adafruit_GFX (1.1 and later). +// Example fonts are included in 'Fonts' directory. +// To use a font in your Arduino sketch, #include the corresponding .h +// file and pass address of GFXfont struct to setFont(). Pass NULL to +// revert to 'classic' fixed-space bitmap font. + +#ifndef _GFXFONT_H_ +#define _GFXFONT_H_ + +/// Font data stored PER GLYPH +typedef struct +{ + uint16_t bitmapOffset; ///< Pointer into GFXfont->bitmap + uint8_t width; ///< Bitmap dimensions in pixels + uint8_t height; ///< Bitmap dimensions in pixels + uint8_t xAdvance; ///< Distance to advance cursor (x axis) + int8_t xOffset; ///< X dist from cursor pos to UL corner + int8_t yOffset; ///< Y dist from cursor pos to UL corner +} GFXglyph; + +/// Data stored for FONT AS A WHOLE +typedef struct +{ + uint8_t *bitmap; ///< Glyph bitmaps, concatenated + GFXglyph *glyph; ///< Glyph array + uint8_t first; ///< ASCII extents (first char) + uint8_t last; ///< ASCII extents (last char) + uint8_t yAdvance; ///< Newline distance (y axis) +} GFXfont; + +#endif // _GFXFONT_H_ diff --git a/ESP32_AP-Flasher/platformio.ini b/ESP32_AP-Flasher/platformio.ini index 57a3f5c6..bba91667 100644 --- a/ESP32_AP-Flasher/platformio.ini +++ b/ESP32_AP-Flasher/platformio.ini @@ -260,6 +260,73 @@ board_build.psram_type=qspi_opi board_upload.maximum_size = 16777216 board_upload.maximum_ram_size = 327680 board_upload.flash_size = 16MB +[env:ESP32_S3_16_8_LILYGO_AP] +board = esp32-s3-devkitc-1 +board_build.partitions = large_spiffs_16MB.csv +build_unflags = + -std=gnu++11 + -D ARDUINO_USB_MODE=1 + -D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y + ;-D ILI9341_DRIVER +lib_deps = + ${env.lib_deps} + lib2\Arduino_GFX-1.3.7 +build_flags = + -std=gnu++17 + ${env.build_flags} + -D HAS_TFT + -D HAS_LILYGO_TPANEL + -D CORE_DEBUG_LEVEL=0 + -D ARDUINO_USB_MODE=0 + -D CONFIG_ESP32S3_SPIRAM_SUPPORT=1 + -D CONFIG_SPIRAM_USE_MALLOC=1 + -D POWER_NO_SOFT_POWER + -D BOARD_HAS_PSRAM + -D CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y + -D HAS_BLE_WRITER + -D FLASHER_AP_SS=-1 + -D FLASHER_AP_CLK=-1 + -D FLASHER_AP_MOSI=-1 + -D FLASHER_AP_MISO=-1 + -D FLASHER_AP_RESET=34 + -D FLASHER_AP_POWER={-1} + -D FLASHER_AP_TEST=-1 + + -D FLASHER_AP_TXD=48 + -D FLASHER_AP_RXD=47 + + -D FLASHER_DEBUG_TXD=43 + -D FLASHER_DEBUG_RXD=44 + -D FLASHER_DEBUG_PROG=33 + -D FLASHER_LED=-1 + ;-D HAS_RGB_LED + ;-D FLASHER_RGB_LED=48 + ;-D ST7789_DRIVER + -D TFT_WIDTH=480 + -D TFT_HEIGHT=480 + ;-D TFT_MISO=-1 + ;-D TFT_MOSI=13 + ;-D TFT_SCLK=12 + ;-D TFT_CS=10 + ;-D TFT_DC=11 + ;-D TFT_RST=1 + ;-D TFT_RGB_ORDER=TFT_BGR + -D USE_HSPI_PORT + -D LOAD_FONT2 + -D MD5_ENABLED=1 + -D SERIAL_FLASHER_INTERFACE_UART=1 + -D SERIAL_FLASHER_BOOT_HOLD_TIME_MS=50 + -D SERIAL_FLASHER_RESET_HOLD_TIME_MS=100 + ;-D C6_OTA_FLASHING + ;-D HAS_SUBGHZ +build_src_filter = + +<*>--- +board_build.flash_mode=qio +board_build.arduino.memory_type = qio_qspi ;Enable external PSRAM +board_build.psram_type=qspi_opi +board_upload.maximum_size = 16777216 +board_upload.maximum_ram_size = 327680 +board_upload.flash_size = 16MB ; ---------------------------------------------------------------------------------------- ; !!! this configuration expects an ESP32-S3 16MB Flash 8MB RAM ; ---------------------------------------------------------------------------------------- diff --git a/ESP32_AP-Flasher/src/ips_display.cpp b/ESP32_AP-Flasher/src/ips_display.cpp index 50926e4b..b4f0fdd1 100644 --- a/ESP32_AP-Flasher/src/ips_display.cpp +++ b/ESP32_AP-Flasher/src/ips_display.cpp @@ -19,7 +19,191 @@ uint8_t YellowSense = 0; bool tftLogscreen = true; bool tftOverride = false; +#ifdef HAS_LILYGO_TPANEL + + +static const uint8_t st7701_type9_init_operations_lilygo[] = { + + BEGIN_WRITE, + WRITE_COMMAND_8, 0xFF, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x13, + + WRITE_C8_D8, 0xEF, 0x08, + + WRITE_COMMAND_8, 0xFF, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x10, + + WRITE_C8_D16, 0xC0, 0x3B, 0x00, + WRITE_C8_D16, 0xC1, 0x0B, 0x02, + + WRITE_COMMAND_8, 0xC2, + WRITE_BYTES, 3, 0x30, 0x02, 0x37, + + WRITE_C8_D8, 0xCC, 0x10, + + WRITE_COMMAND_8, 0xB0, // Positive Voltage Gamma Control + WRITE_BYTES, 16, + 0x00, 0x0F, 0x16, 0x0E, + 0x11, 0x07, 0x09, 0x09, + 0x08, 0x23, 0x05, 0x11, + 0x0F, 0x28, 0x2D, 0x18, + + WRITE_COMMAND_8, 0xB1, // Negative Voltage Gamma Control + WRITE_BYTES, 16, + 0x00, 0x0F, 0x16, 0x0E, + 0x11, 0x07, 0x09, 0x08, + 0x09, 0x23, 0x05, 0x11, + 0x0F, 0x28, 0x2D, 0x18, + + WRITE_COMMAND_8, 0xFF, + WRITE_BYTES, 5, 0x77, 0x01, 0x00, 0x00, 0x11, + + WRITE_C8_D8, 0xB0, 0x4D, + WRITE_C8_D8, 0xB1, 0x33, + WRITE_C8_D8, 0xB2, 0x87, + WRITE_C8_D8, 0xB5, 0x4B, + WRITE_C8_D8, 0xB7, 0x8C, + WRITE_C8_D8, 0xB8, 0x20, + WRITE_C8_D8, 0xC1, 0x78, + WRITE_C8_D8, 0xC2, 0x78, + WRITE_C8_D8, 0xD0, 0x88, + + WRITE_COMMAND_8, 0xE0, + WRITE_BYTES, 3, 0x00, 0x00, 0x02, + + WRITE_COMMAND_8, 0xE1, + WRITE_BYTES, 11, + 0x02, 0xF0, 0x00, 0x00, + 0x03, 0xF0, 0x00, 0x00, + 0x00, 0x44, 0x44, + + WRITE_COMMAND_8, 0xE2, + WRITE_BYTES, 12, + 0x10, 0x10, 0x40, 0x40, + 0xF2, 0xF0, 0x00, 0x00, + 0xF2, 0xF0, 0x00, 0x00, + + WRITE_COMMAND_8, 0xE3, + WRITE_BYTES, 4, 0x00, 0x00, 0x11, 0x11, + + WRITE_C8_D16, 0xE4, 0x44, 0x44, + + WRITE_COMMAND_8, 0xE5, + WRITE_BYTES, 16, + 0x07, 0xEF, 0xF0, 0xF0, + 0x09, 0xF1, 0xF0, 0xF0, + 0x03, 0xF3, 0xF0, 0xF0, + 0x05, 0xED, 0xF0, 0xF0, + + WRITE_COMMAND_8, 0xE6, + WRITE_BYTES, 4, 0x00, 0x00, 0x11, 0x11, + + WRITE_C8_D16, 0xE7, 0x44, 0x44, + + WRITE_COMMAND_8, 0xE8, + WRITE_BYTES, 16, + 0x08, 0xF0, 0xF0, 0xF0, + 0x0A, 0xF2, 0xF0, 0xF0, + 0x04, 0xF4, 0xF0, 0xF0, + 0x06, 0xEE, 0xF0, 0xF0, + + WRITE_COMMAND_8, 0xEB, + WRITE_BYTES, 7, + 0x00, 0x00, 0xE4, 0xE4, + 0x44, 0x88, 0x40, + + WRITE_C8_D16, 0xEC, 0x78, 0x00, + + WRITE_COMMAND_8, 0xED, + WRITE_BYTES, 16, + 0x20, 0xF9, 0x87, 0x76, + 0x65, 0x54, 0x4F, 0xFF, + 0xFF, 0xF4, 0x45, 0x56, + 0x67, 0x78, 0x9F, 0x02, + + WRITE_COMMAND_8, 0xEF, + WRITE_BYTES, 6, + 0x10, 0x0D, 0x04, 0x08, + 0x3F, 0x1F, + + // WRITE_C8_D8, 0xCD, 0x05,//Test + + WRITE_C8_D8, 0x3A, 0x55, + + WRITE_C8_D8, 0x36, 0x08, + + WRITE_COMMAND_8, 0x11, + + // WRITE_COMMAND_8, 0xFF,//Test + // WRITE_BYTES, 5, + // 0x77, 0x01, 0x00, 0x00, + // 0x12, + + // WRITE_C8_D8, 0xD1, 0x81,//Test + // WRITE_C8_D8, 0xD2, 0x08,//Test + + WRITE_COMMAND_8, 0x29, // Display On + + // WRITE_C8_D8, 0x35, 0x00,//Test + // WRITE_C8_D8, 0xCE, 0x04,//Test + + // WRITE_COMMAND_8, 0xF2,//Test + // WRITE_BYTES, 4, + // 0xF0, 0xA3, 0xA3, 0x71, + + END_WRITE}; + +Arduino_DataBus *bus = new Arduino_XL9535SWSPI(IIC_SDA /* SDA */, IIC_SCL /* SCL */, -1 /* XL PWD */, + XL95X5_CS /* XL CS */, XL95X5_SCLK /* XL SCK */, XL95X5_MOSI /* XL MOSI */); +Arduino_ESP32RGBPanel *rgbpanel = new Arduino_ESP32RGBPanel( + -1 /* DE */, LCD_VSYNC /* VSYNC */, LCD_HSYNC /* HSYNC */, LCD_PCLK /* PCLK */, + LCD_B0 /* B0 */, LCD_B1 /* B1 */, LCD_B2 /* B2 */, LCD_B3 /* B3 */, LCD_B4 /* B4 */, + LCD_G0 /* G0 */, LCD_G1 /* G1 */, LCD_G2 /* G2 */, LCD_G3 /* G3 */, LCD_G4 /* G4 */, LCD_G5 /* G5 */, + LCD_R0 /* R0 */, LCD_R1 /* R1 */, LCD_R2 /* R2 */, LCD_R3 /* R3 */, LCD_R4 /* R4 */, + 1 /* hsync_polarity */, 20 /* hsync_front_porch */, 2 /* hsync_pulse_width */, 0 /* hsync_back_porch */, + 1 /* vsync_polarity */, 30 /* vsync_front_porch */, 8 /* vsync_pulse_width */, 1 /* vsync_back_porch */, + 10 /* pclk_active_neg */, 6000000L /* prefer_speed */, false /* useBigEndian */, + 0 /* de_idle_high*/, 0 /* pclk_idle_high */); +Arduino_RGB_Display *gfx = new Arduino_RGB_Display( + LCD_WIDTH /* width */, LCD_HEIGHT /* height */, rgbpanel, 0 /* rotation */, true /* auto_flush */, + bus, -1 /* RST */, st7701_type9_init_operations_lilygo, sizeof(st7701_type9_init_operations_lilygo)); + +#endif + void TFTLog(String text) { + + #ifdef HAS_LILYGO_TPANEL + + gfx->setTextSize(2); + + if (tftLogscreen == false) { + gfx->fillScreen(BLACK); + gfx->setCursor(0, 0); + tftLogscreen = true; + } + if (text.isEmpty()) return; + gfx->setTextColor(LIGHTGREY); + if (text.startsWith("!")) { + gfx->setTextColor(RED); + text = text.substring(1); + } else if (text.indexOf("http") != -1) { + int httpIndex = text.indexOf("http"); + gfx->print(text.substring(0, httpIndex)); + gfx->setTextColor(YELLOW); + text = text.substring(httpIndex); + } else if (text.indexOf(":") != -1) { + int colonIndex = text.indexOf(":"); + gfx->setTextColor(LIGHTGREY); + gfx->print(text.substring(0, colonIndex + 1)); + gfx->setTextColor(WHITE); + text = text.substring(colonIndex + 1); + } else if (text.endsWith("!")) { + gfx->setTextColor(GREEN); + } + gfx->println(text); + + #else + if (tftLogscreen == false) { tft2.fillScreen(TFT_BLACK); tft2.setCursor(0, 0, (tft2.width() == 160 ? 1 : 2)); @@ -53,6 +237,7 @@ void TFTLog(String text) { tft2.setTextColor(TFT_GREEN); } tft2.println(text); + #endif } int32_t findId(uint8_t mac[8]) { @@ -72,7 +257,11 @@ void sendAvail(uint8_t wakeupReason) { memcpy(&eadr.src, mac, 6); eadr.adr.lastPacketRSSI = WiFi.RSSI(); eadr.adr.currentChannel = config.channel; + #ifdef HAS_LILYGO_TPANEL + eadr.adr.hwType = 0xE2; + #else eadr.adr.hwType = (tft2.width() == 160 ? 0xE1 : 0xE0); + #endif eadr.adr.wakeupReason = wakeupReason; eadr.adr.capabilities = 0; eadr.adr.tagSoftwareVersion = 0; @@ -81,6 +270,20 @@ void sendAvail(uint8_t wakeupReason) { } void yellow_ap_display_init(void) { + + #ifdef HAS_LILYGO_TPANEL + + tftLogscreen = true; + + pinMode(LCD_BL, OUTPUT); + digitalWrite(LCD_BL, HIGH); + + Wire.begin(IIC_SDA, IIC_SCL); + + gfx->begin(); + gfx->fillScreen(BLACK); + + #else pinMode(YELLOW_SENSE, INPUT_PULLDOWN); vTaskDelay(100 / portTICK_PERIOD_MS); if (digitalRead(YELLOW_SENSE) == HIGH) YellowSense = 1; @@ -105,6 +308,7 @@ void yellow_ap_display_init(void) { GPIO.func_out_sel_cfg[TFT_BACKLIGHT].inv_sel = 1; } ledcWrite(6, config.tft); + #endif } void yellow_ap_display_loop(void) { @@ -146,7 +350,28 @@ void yellow_ap_display_loop(void) { void* spriteData = spr.getPointer(); size_t bytesRead = file.readBytes((char*)spriteData, spr.width() * spr.height() * 2); file.close(); + + #ifdef HAS_LILYGO_TPANEL + long dy = spr.height(); + long dx = spr.width(); + + uint16_t* data = static_cast(const_cast(spriteData)); + + for (int16_t j = 0; j < dy; j++) + { + for (int16_t i = 0; i < dx; i++) + { + uint16_t color = *data; + color = color<<8 | color>>8; + *data = color; + data++; + } + } + gfx->draw16bitRGBBitmap(0, 0, (uint16_t *)spriteData, dx, dy); + #else spr.pushSprite(0, 0); + #endif + tftLogscreen = false; struct espXferComplete xfc = {0}; diff --git a/ESP32_AP-Flasher/src/makeimage.cpp b/ESP32_AP-Flasher/src/makeimage.cpp index f3d1c5a5..49b1116d 100644 --- a/ESP32_AP-Flasher/src/makeimage.cpp +++ b/ESP32_AP-Flasher/src/makeimage.cpp @@ -80,6 +80,7 @@ uint32_t colorDistance(Color &c1, Color &c2, Error &e1) { } void spr2color(TFT_eSprite &spr, imgParam &imageParams, uint8_t *buffer, size_t buffer_size, bool is_red) { + uint8_t rotate = imageParams.rotate; long bufw = spr.width(), bufh = spr.height(); @@ -286,7 +287,29 @@ void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams) { size_t dataSize = spr.width() * spr.height() * (spr.getColorDepth() / 8); memcpy(spriteData2, spriteData, dataSize); + #ifdef HAS_LILYGO_TPANEL + if (spr.getColorDepth() == 16) + { + long dy = spr.height(); + long dx = spr.width(); + + uint16_t* data = static_cast(const_cast(spriteData2)); + + for (int16_t j = 0; j < dy; j++) + { + for (int16_t i = 0; i < dx; i++) + { + uint16_t color = *data; + color = color<<8 | color>>8; + *data = color; + data++; + } + } + gfx->draw16bitRGBBitmap(0, 0, (uint16_t *)spriteData2, dx, dy); + } + #else spr2.pushSprite(0, 0); + #endif } return; } diff --git a/resources/tagtypes/E2.json b/resources/tagtypes/E2.json new file mode 100644 index 00000000..591ce03b --- /dev/null +++ b/resources/tagtypes/E2.json @@ -0,0 +1,64 @@ +{ + "version": 1, + "name": "LILYGO TPANEL 4\"", + "width": 480, + "height": 480, + "rotatebuffer": 0, + "bpp": 16, + "colortable": { + "white": [ 255, 255, 255 ], + "black": [ 0, 0, 0 ], + "red": [ 255, 0, 0 ] + }, + "shortlut": 0, + "options": [ ], + "contentids": [ 22, 1, 2, 3, 4, 8, 7, 19, 10, 11, 21 ], + "template": { + "21": [ + { "box": [ 0, 0, 480, 480, 1 ] }, + { "text": [ 10, 15, "OpenEpaperLink AP", "calibrib30", 2, 0, 0, 1 ] }, + { "text": [ 10, 70, "IP address:", "bahnschrift30", "#888888", 0, 0, 1 ] }, + { "text": [ 180, 70, "{ap_ip}", "bahnschrift30", 0, 0, 0, 1 ] }, + { "text": [ 10, 110, "Channel:", "bahnschrift30", "#888888", 0, 0, 1 ] }, + { "text": [ 180, 110, "{ap_ch}", "bahnschrift30", 0, 0, 0, "1" ] }, + { "text": [ 10, 150, "Tag count:", "bahnschrift30", "#888888", 0, 0, 1 ] }, + { "text": [ 180, 150, "{ap_tagcount}", "bahnschrift30", 0, 0, 0, "1" ] } + ], + "1": { + "weekday": [ 240, 30, "Signika-SB.ttf", 90 ], + "month": [ 240, 330, "Signika-SB.ttf", 90 ], + "day": [ 240, 80, "Signika-SB.ttf", 250 ] + }, + "4": { + "location": [ 20, 20, "fonts/calibrib30" ], + "wind": [ 90, 95, "fonts/calibrib50" ], + "temp": [ 20, 200, "fonts/calibrib100" ], + "icon": [ 400, 30, 150, 2 ], + "dir": [ 40, 70, 80 ], + "umbrella": [ 325, 250, 150 ] + }, + "2": { + "fonts": [ "Signika-SB.ttf", 150, 150, 110, 80, 60, 50 ], + "xy": [ 240, 240 ] + }, + "8": { + "location": [ 10, 20, "fonts/calibrib50" ], + "column": [ 6, 80 ], + "day": [ 40, 100, "fonts/bahnschrift30", 144, 270 ], + "rain": [ 40, 320 ], + "icon": [ 40, 180, 50 ], + "wind": [ 17, 120 ], + "line": [ 100, 340 ] + }, + "10": { + "title": [ 240, 10, "fonts/bahnschrift20" ], + "pos": [ 240, 35 ] + }, + "11": { + "rotate": 0, + "mode": 1, + "days": 4, + "gridparam": [ 5, 17, 20, "calibrib16.vlw", "tahoma9.vlw", 14 ] + } + } +}