From 5cb02880b3d2f0904af7d93a1b97bc16b1f673ea Mon Sep 17 00:00:00 2001 From: kazu Date: Thu, 17 Jan 2019 23:12:01 +0100 Subject: [PATCH] many updates.. new sensors.. display.. led.. drawing.. stuff.. --- Debug.h | 45 +- data/Color.h | 17 + data/Vector.h | 35 ++ ext/lcd/Draw.h | 190 +++++++ ext/lcd/ILI9341.h | 365 +++++++++++++ ext/lcd/SSD1306.h | 255 +++++++++ ext/lcd/SSD1306_old.h | 249 +++++++++ ext/lcd/ST7735.h | 295 +++++++++++ ext/lcd/ST7735_old.h | 413 +++++++++++++++ ext/lcd/ui/UI.h | 101 ++++ ext/lcd/ui/UIButton.h | 97 ++++ ext/lcd/ui/UIElement.h | 121 +++++ ext/lcd/ui/UILabel.h | 45 ++ ext/lcd/ui/UIList.h | 208 ++++++++ ext/lcd/ui/UIPainter.h | 52 ++ ext/lcd/ui/UIStructs.h | 39 ++ ext/lcd/ui/ui.tar.gz | Bin 0 -> 6892 bytes ext/led/WS2812B.h | 48 +- ext/sens/BME280.h | 277 ++++++++++ ext/sens/MPU6050.h | 154 ++++++ ext/sens/VL53L0X.h | 1127 ++++++++++++++++++++++++++++++++++++++++ ext/sens/XPT2046.h | 132 +++++ ext/time/DS3231.h | 124 +++++ io/GPIO.h | 122 +++++ io/HardSPI.h | 228 ++++++++ io/PWM.h | 1 + io/SoftI2C.h | 240 +++++++++ io/SoftSPI.h | 402 +++++++++++--- net/IP.h | 6 +- net/UDP.h | 14 +- 30 files changed, 5305 insertions(+), 97 deletions(-) create mode 100644 data/Vector.h create mode 100644 ext/lcd/Draw.h create mode 100644 ext/lcd/ILI9341.h create mode 100644 ext/lcd/SSD1306.h create mode 100644 ext/lcd/SSD1306_old.h create mode 100644 ext/lcd/ST7735.h create mode 100644 ext/lcd/ST7735_old.h create mode 100644 ext/lcd/ui/UI.h create mode 100644 ext/lcd/ui/UIButton.h create mode 100644 ext/lcd/ui/UIElement.h create mode 100644 ext/lcd/ui/UILabel.h create mode 100644 ext/lcd/ui/UIList.h create mode 100644 ext/lcd/ui/UIPainter.h create mode 100644 ext/lcd/ui/UIStructs.h create mode 100644 ext/lcd/ui/ui.tar.gz create mode 100644 ext/sens/BME280.h create mode 100644 ext/sens/MPU6050.h create mode 100644 ext/sens/VL53L0X.h create mode 100644 ext/sens/XPT2046.h create mode 100644 ext/time/DS3231.h create mode 100644 io/GPIO.h create mode 100644 io/HardSPI.h create mode 100644 io/PWM.h create mode 100644 io/SoftI2C.h diff --git a/Debug.h b/Debug.h index 637ab96..d1aefa2 100644 --- a/Debug.h +++ b/Debug.h @@ -34,16 +34,28 @@ extern "C" { #define debugMod1(module, str, val) #define debugMod2(module, str, v1, v2) #define debugMod3(module, str, v1, v2, v3) + #define debugMod4(module, str, v1, v2, v3, v4) + #define debugMod5(module, str, v1, v2, v3, v4, v5) + #define debugMod6(module, str, v1, v2, v3, v4, v5, v6) + #define debugMod7(module, str, v1, v2, v3, v4, v5, v6, v7) + #define debugMod8(module, str, v1, v2, v3, v4, v5, v6, v7, v8) + #define debugMod9(module, str, v1, v2, v3, v4, v5, v6, v7, v8, v9) #define IF_DEBUG(a) #define debugShow(a, b) -#elif (PLATFORM == WEMOS_D1_MINI) || (PLATFORM == NODE_MCU) +#elif ESP8266 - #define debug(str) os_printf(str) - #define debugMod(module, str) os_printf("[%s] %s\n", module, str) - #define debugMod1(module, str, val) os_printf("[%s] ", module); os_printf(str, val); os_printf("\n"); - #define debugMod2(module, str, v1, v2) os_printf("[%s] ", module); os_printf(str, v1, v2); os_printf("\n"); - #define debugMod3(module, str, v1, v2, v3) os_printf("[%s] ", module); os_printf(str, v1, v2, v3); os_printf("\n"); + #define debug(str) os_printf(str) + #define debugMod(module, str) os_printf("[%s] %s\n", module, str) + #define debugMod1(module, str, val) os_printf("[%s] ", module); os_printf(str, val); os_printf("\n"); + #define debugMod2(module, str, v1, v2) os_printf("[%s] ", module); os_printf(str, v1, v2); os_printf("\n"); + #define debugMod3(module, str, v1, v2, v3) os_printf("[%s] ", module); os_printf(str, v1, v2, v3); os_printf("\n"); + #define debugMod4(module, str, v1, v2, v3, v4) os_printf("[%s] ", module); os_printf(str, v1, v2, v3, v4); os_printf("\n"); + #define debugMod5(module, str, v1, v2, v3, v4, v5) os_printf("[%s] ", module); os_printf(str, v1, v2, v3, v4, v5); os_printf("\n"); + #define debugMod6(module, str, v1, v2, v3, v4, v5, v6) os_printf("[%s] ", module); os_printf(str, v1, v2, v3, v4, v5, v6); os_printf("\n"); + #define debugMod7(module, str, v1, v2, v3, v4, v5, v6, v7) os_printf("[%s] ", module); os_printf(str, v1, v2, v3, v4, v5, v6, v7); os_printf("\n"); + #define debugMod8(module, str, v1, v2, v3, v4, v5, v6, v7, v8) os_printf("[%s] ", module); os_printf(str, v1, v2, v3, v4, v5, v6, v7, v8); os_printf("\n"); + #define debugMod9(module, str, v1, v2, v3, v4, v5, v6, v7, v8, v9) os_printf("[%s] ", module); os_printf(str, v1, v2, v3, v4, v5, v6, v7, v8, v9); os_printf("\n"); #define IF_DEBUG(a) a #define debugShow(buf, len) hexdump(buf,len) @@ -56,13 +68,20 @@ extern "C" { #elif (PLATFORM == WROOM32_DEVKIT) - #define debug(str) ESP_LOGI("", str); - #define debugMod(module, str) ESP_LOGI(module, str); - #define debugMod1(module, str, val) ESP_LOGI(module, str, val); - #define debugMod2(module, str, v1, v2) ESP_LOGI(module, str, v1, v2); - #define debugMod3(module, str, v1, v2, v3) ESP_LOGI(module, str, v1, v2, v3); - #define IF_DEBUG(a) a - #define debugShow(buf, len) hexdump(buf,len) + #ifndef CONFIG_LOG_DEFAULT_LEVEL + #define CONFIG_LOG_DEFAULT_LEVEL 3 + #endif + #include "esp_log.h" + + + #define debug(str) ESP_LOGI("", str); + #define debugMod(module, str) ESP_LOGI(module, str); + #define debugMod1(module, str, val) ESP_LOGI(module, str, val); + #define debugMod2(module, str, v1, v2) ESP_LOGI(module, str, v1, v2); + #define debugMod3(module, str, v1, v2, v3) ESP_LOGI(module, str, v1, v2, v3); + #define debugMod4(module, str, v1, v2, v3, v4) ESP_LOGI(module, str, v1, v2, v3, v4); + #define IF_DEBUG(a) a + #define debugShow(buf, len) hexdump(buf,len) #else #error "unsupported platform" diff --git a/data/Color.h b/data/Color.h index bb7878b..6fe0fba 100644 --- a/data/Color.h +++ b/data/Color.h @@ -1,3 +1,9 @@ +#ifndef COLOR_H +#define COLOR_H + +#include + + struct Color { uint8_t r; @@ -40,6 +46,15 @@ public: Color c; c.setHSV(h,s,v); return c; } + /** get color with new brightness (0..255) */ + Color brightness(const uint8_t brightness) const { + return Color( + ((uint16_t)r)*brightness/255, + ((uint16_t)g)*brightness/255, + ((uint16_t)b)*brightness/255 + ); + } + void setHSV(const uint8_t h, const uint8_t s, const uint8_t v) { uint8_t region, remainder, p, q, t; @@ -97,3 +112,5 @@ public: } }; + +#endif // COLOR_H diff --git a/data/Vector.h b/data/Vector.h new file mode 100644 index 0000000..7c72733 --- /dev/null +++ b/data/Vector.h @@ -0,0 +1,35 @@ +#ifndef DATA_VECTOR_H +#define DATA_VECTOR_H + +#include + +template class Vector { + +private: + + T* data = nullptr; + size_t nextIdx = 0; + size_t total = 8; + +public: + + Vector() { + data = (T*) malloc(total*sizeof(T)); + } + + void push_back(T elem) { + data[nextIdx] = elem; + ++nextIdx; + } + + T& operator[] (const size_t idx) { + return data[idx]; + } + + size_t size() const { + return nextIdx; + } + +}; + +#endif // DATA_VECTOR_H diff --git a/ext/lcd/Draw.h b/ext/lcd/Draw.h new file mode 100644 index 0000000..d1244b6 --- /dev/null +++ b/ext/lcd/Draw.h @@ -0,0 +1,190 @@ +#ifndef LCD_DRAW +#define LCD_DRAW + +#include +#include +#include "../../data/Color.h" +//struct RGB { + +// uint8_t r; +// uint8_t g; +// uint8_t b; + +// RGB() : r(0), g(0), b(0) {;} +// RGB(uint8_t r, uint8_t g, uint8_t b) : r(r), g(g), b(b) {;} + +// uint16_t toRGB565() const { +// return +// ((r >> 3) << 11) | +// ((g >> 2) << 5) | +// ((b >> 3) << 0); +// } + +//}; + +class FontWrap { + + uint16_t w; + uint16_t h; + const uint8_t* data; + const uint16_t* offsets; + + static constexpr uint8_t off = 32; + +public: + + /** ctor */ + FontWrap(const uint16_t w, const uint16_t h, const uint8_t* data, const uint16_t* offsets) : w(w), h(h), data(data), offsets(offsets) { + ; + } + + /** get the pixel-width of the given char */ + uint8_t getCharWidht(char c) const { + if (c == 32) {return 3;} // whitespace + return offsets[c-off+1] - offsets[c-off]; + } + + uint8_t getHeight() const { + return h; + } + + uint16_t getWidth(const char* txt) const { + uint16_t sum = 0; + while(*txt) { + sum += getCharWidht(*txt); + ++txt; + } + return sum; + } + + /** draw the given char at the given position */ + template void draw(const char* c, Scalar dx, Scalar dy, Destination& dst) { + while(*c) { + draw(*c, dx, dy, dst); + dx += getCharWidht(*c);// + 1; + ++c; + } + } + + /** draw the given char at the given position */ + template void draw(unsigned char c, Scalar dx, Scalar dy, Destination& dst) { + + if (c == 32) {return;} // skip whitespace + + + const uint16_t x1 = offsets[c-off]; + const uint16_t x2 = offsets[c-off+1]; + const uint16_t y1 = 0; + const uint16_t y2 = h; + + for (uint16_t y = y1; y < y2; ++y) { + for (uint16_t x = x1; x < x2; ++x) { + const uint16_t idx = (x/8) + (y*this->w/8); + const uint8_t pixel = data[idx] & (1<<(x&7)); + if (pixel) { + dst.setPixel(dx+x-x1, dy+y); + } + } + } + + } + +}; + + + +template class Draw { + + Destination& dst; + +public: + + Draw(Destination& dst) : dst(dst) { + + } + + void set(const Scalar x1, const Scalar y1) { + dst.set(x1, y1); + } + + void setColor(const Color color) { + dst.setColor(color); + } + + /** draw a line from (x0,y0) to (x1,y1) */ + void drawLine(Scalar x1, Scalar y1, const Scalar x2, const Scalar y2) { + +// int dx, dy, p, x, y; + +// dx=x1-x0; +// dy=y1-y0; + +// x=x0; +// y=y0; + +// p=2*dy-dx; + +// while(x < x1) { +// if(p >= 0) { +// dst.set(x,y); +// y=y+1; +// p=p+2*dy-2*dx; +// } else { +// dst.set(x,y); +// p=p+2*dy; +// } +// x=x+1; +// } + + Scalar dx = abs(x2-x1); + Scalar sx = (x1 dy) { err += dy; x1 += sx; } + if (e2 < dx) { err += dx; y1 += sy; } + } + + } + + void fillRect(const Scalar x, const Scalar y, const Scalar w, const Scalar h) { + dst.fillRect(x,y,w,h); + } + + void drawLineHor(const Scalar x1, const Scalar x2, const Scalar y) { + dst.fillRect(x1,y,x2-x1,1); + } + void drawLineVer(const Scalar y1, const Scalar y2, const Scalar x) { + dst.fillRect(x,y1,1,y2-y1); + } + + void drawRect(const Scalar x, const Scalar y, const Scalar w, const Scalar h) { + drawLineHor(x,x+w, y); // top + drawLineHor(x,x+w, y+h); // bottom + drawLineVer(y,y+h, x); // left + drawLineVer(y,y+h, x+w); // right + } + +// void drawRect(const Scalar x1, const Scalar y1, const Scalar x2, const Scalar y2) { + +// for (Scalar s = x1; s <= x2; ++s) {dst.set(s, y1);} +// for (Scalar s = x1; s <= x2; ++s) {dst.set(s, y2);} + +// for (Scalar s = y1+1; s < y2; ++s) {dst.set(x1, s);} +// for (Scalar s = y1+1; s < y2; ++s) {dst.set(x2, s);} + +// } + + Scalar abs(Scalar v) const { + if (v >= 0) {return v;} + return -v; + } + +}; + +#endif diff --git a/ext/lcd/ILI9341.h b/ext/lcd/ILI9341.h new file mode 100644 index 0000000..5f0a4c4 --- /dev/null +++ b/ext/lcd/ILI9341.h @@ -0,0 +1,365 @@ +#ifndef LCD_ILI9341 +#define LCD_ILI9341 + +#include "../../Debug.h" + +// https://github.com/adafruit/Adafruit_ILI9341/blob/master/Adafruit_ILI9341.cpp +// https://github.com/adafruit/Adafruit_ILI9341/blob/master/Adafruit_ILI9341.h + +// http://datasheets.gpio.dk/dl/ILI9341.pdf + +// e.g. +// TJCTM24024-SPI +// http://play.fallows.ca/wp/projects/electronics-projects/tjctm24024-mystery-modules-china/ +// http://datasheets.gpio.dk/dl/2.4inch-tft-touch/2.4%25e4%25b8%25b2%25e5%258f%25a3_TJC-024-9341/Module_SCH.pdf +// http://datasheets.gpio.dk/dl/ILI9341.pdf + +#define ILI9341_TFTWIDTH 240 ///< ILI9341 max TFT width +#define ILI9341_TFTHEIGHT 320 ///< ILI9341 max TFT height + +#define ILI9341_NOP 0x00 ///< No-op register +#define ILI9341_SWRESET 0x01 ///< Software reset register +#define ILI9341_RDDID 0x04 ///< Read display identification information +#define ILI9341_RDDST 0x09 ///< Read Display Status + +#define ILI9341_SLPIN 0x10 ///< Enter Sleep Mode +#define ILI9341_SLPOUT 0x11 ///< Sleep Out +#define ILI9341_PTLON 0x12 ///< Partial Mode ON +#define ILI9341_NORON 0x13 ///< Normal Display Mode ON + +#define ILI9341_RDMODE 0x0A ///< Read Display Power Mode +#define ILI9341_RDMADCTL 0x0B ///< Read Display MADCTL +#define ILI9341_RDPIXFMT 0x0C ///< Read Display Pixel Format +#define ILI9341_RDIMGFMT 0x0D ///< Read Display Image Format +#define ILI9341_RDSELFDIAG 0x0F ///< Read Display Self-Diagnostic Result + +#define ILI9341_INVOFF 0x20 ///< Display Inversion OFF +#define ILI9341_INVON 0x21 ///< Display Inversion ON +#define ILI9341_GAMMASET 0x26 ///< Gamma Set +#define ILI9341_DISPOFF 0x28 ///< Display OFF +#define ILI9341_DISPON 0x29 ///< Display ON + +#define ILI9341_CASET 0x2A ///< Column Address Set +#define ILI9341_PASET 0x2B ///< Page Address Set +#define ILI9341_RAMWR 0x2C ///< Memory Write +#define ILI9341_RAMRD 0x2E ///< Memory Read + +#define ILI9341_PTLAR 0x30 ///< Partial Area +#define ILI9341_MADCTL 0x36 ///< Memory Access Control +#define ILI9341_VSCRSADD 0x37 ///< Vertical Scrolling Start Address +#define ILI9341_PIXFMT 0x3A ///< COLMOD: Pixel Format Set + +#define ILI9341_FRMCTR1 0xB1 ///< Frame Rate Control (In Normal Mode/Full Colors) +#define ILI9341_FRMCTR2 0xB2 ///< Frame Rate Control (In Idle Mode/8 colors) +#define ILI9341_FRMCTR3 0xB3 ///< Frame Rate control (In Partial Mode/Full Colors) +#define ILI9341_INVCTR 0xB4 ///< Display Inversion Control +#define ILI9341_DFUNCTR 0xB6 ///< Display Function Control + +#define ILI9341_PWCTR1 0xC0 ///< Power Control 1 +#define ILI9341_PWCTR2 0xC1 ///< Power Control 2 +#define ILI9341_PWCTR3 0xC2 ///< Power Control 3 +#define ILI9341_PWCTR4 0xC3 ///< Power Control 4 +#define ILI9341_PWCTR5 0xC4 ///< Power Control 5 +#define ILI9341_VMCTR1 0xC5 ///< VCOM Control 1 +#define ILI9341_VMCTR2 0xC7 ///< VCOM Control 2 + +#define ILI9341_RDID1 0xDA ///< Read ID 1 +#define ILI9341_RDID2 0xDB ///< Read ID 2 +#define ILI9341_RDID3 0xDC ///< Read ID 3 +#define ILI9341_RDID4 0xDD ///< Read ID 4 + +#define ILI9341_GMCTRP1 0xE0 ///< Positive Gamma Correction +#define ILI9341_GMCTRN1 0xE1 ///< Negative Gamma Correction +//#define ILI9341_PWCTR6 0xFC + + + + + +#include "../../io/SoftSPI.h" +#include "../../io/HardSPI.h" + + +static constexpr const uint8_t initcmd[] = { + 0xEF, 3, 0x03, 0x80, 0x02, + 0xCF, 3, 0x00, 0xC1, 0x30, + 0xED, 4, 0x64, 0x03, 0x12, 0x81, + 0xE8, 3, 0x85, 0x00, 0x78, + 0xCB, 5, 0x39, 0x2C, 0x00, 0x34, 0x02, + 0xF7, 1, 0x20, + 0xEA, 2, 0x00, 0x00, + ILI9341_PWCTR1, 1, 0x23, // Power control VRH[5:0] + ILI9341_PWCTR2, 1, 0x10, // Power control SAP[2:0];BT[3:0] + ILI9341_VMCTR1, 2, 0x3e, 0x28, // VCM control + ILI9341_VMCTR2, 1, 0x86, // VCM control2 + ILI9341_MADCTL, 1, 0x48, // Memory Access Control + ILI9341_VSCRSADD, 1, 0x00, // Vertical scroll zero + ILI9341_PIXFMT, 1, 0x55, + ILI9341_FRMCTR1, 2, 0x00, 0x18, + ILI9341_DFUNCTR, 3, 0x08, 0x82, 0x27, // Display Function Control + 0xF2, 1, 0x00, // 3Gamma Function Disable + ILI9341_GAMMASET, 1, 0x01, // Gamma curve selected + ILI9341_GMCTRP1, 15, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00, + ILI9341_GMCTRN1, 15, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F, // Set Gamma + //ILI9341_SLPOUT, 0x80, // Exit Sleep + //ILI9341_DISPON, 0x80, // Display on + 0x00 // End of list +}; + +//template class ILI9341 { +template class ILI9341 { + +private: + + int w = 240; + int h = 320; + + static constexpr const char* MOD = "ILI9341"; + + //HardSPI spi; + //SoftSPI spi; + SPI& spi; + +public: + + ILI9341(SPI& spi) : spi(spi) { + + GPIO::setOutput(PIN_DATA_COMMAND); + GPIO::setOutput(PIN_CS); + + //spi.init(); + + } + + void setPixel(const uint16_t x, const uint16_t y, const uint16_t color) { + chipSelect(); + setAddrWindow(x,y,1,1); + sendWord(color); + chipDeselect(); + } + + void setPixel(const uint16_t x, const uint16_t y, const uint8_t w, const uint16_t color) { + chipSelect(); + setAddrWindow(x-w/2,y-w/2,w,w); + for (int i = 0; i < w*w; ++i) { + sendWord(color); + } + chipDeselect(); + } + + /** perform display software reset and initialization */ + void init() { + + chipSelect(); + + // send reset command + wait(); + sendCommand(ILI9341_SWRESET); + wait(); + + // send init sequence + const uint8_t* bytes = initcmd; + while(*bytes) { + const uint8_t cmd = *bytes; ++bytes; + uint8_t numArgs = *bytes; ++bytes; + debugMod2(MOD, "send command %d (%d bytes)", cmd, numArgs); + sendCommand(cmd); + while(numArgs--) { + sendByte(*bytes); + ++bytes; + } + } + + // finalize init sequence + sendCommand(ILI9341_SLPOUT); wait(); + sendCommand(ILI9341_DISPON); wait(); + + // done + chipDeselect(); + + } + + uint32_t getID() { + chipSelect(); + sendCommand16(ILI9341_RDDID); + uint32_t res = 0xFFFFFFFF; + readBytes(reinterpret_cast(&res), 4); // dummy-byte + 3 data bytes + chipDeselect(); + return res; + } + + void getStatus() { + chipSelect(); + uint8_t buf[5]; + sendCommand16(ILI9341_RDDID); + uint32_t res = 0xFFFFFFFF; + readBytes(buf, 5); // dummy-byte + 4 data bytes + chipDeselect(); + ESP_LOGI(MOD, "%d %d %d %d", buf[1],buf[2],buf[3],buf[4]); + read(); + } + + void read() { + chipSelect(); + setAddrWindow(0,0,100,100); + sendCommand(ILI9341_RAMRD); + uint8_t buf[32]; + readBytes(buf, 32); + for (int i = 0; i < 32; ++i) { + printf("%d ", buf[i]); + } + printf("\n"); + chipDeselect(); + } + + + void draw(uint16_t x, uint16_t y, uint16_t w, uint16_t h, const uint16_t* data) { + chipSelect(); + setAddrWindow(x,y,w,h); + const int len = w*h; +// for (int i = 0; i < len; ++i) { +// spi::writeWord(data[i]); +// } + spi.write((const uint8_t*)data, w*h*2); + chipDeselect(); + } + + void fillRand() { + +// chipSelect(); +// setAddrWindow(0,0,w,h); +// for (int i = 0; i < w*h/2; ++i) { +// sendQuad(rand()); +// } +// chipDeselect(); + + chipSelect(); + setAddrWindow(0,0,w,h); + for (int y = 0; y < h; ++y) { + uint16_t buf[w]; + for (int x = 0; x < w; ++x) {buf[x] = rand();} + sendBytes(reinterpret_cast(buf), w*2); + } + chipDeselect(); + + } + + void fill(const uint16_t color) { + + chipSelect(); + setAddrWindow(0,0,w,h); + for (int y = 0; y < h; ++y) { + uint16_t buf[w]; + for (int x = 0; x < w; ++x) {buf[x] = color;} + sendBytes(reinterpret_cast(buf), w*2); + } + chipDeselect(); + + } + + void fillRect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, const uint16_t color) { + + chipSelect(); + setAddrWindow(x,y,w,h); + for (int i = 0; i < w*h; ++i) { + sendWord(color); + } + chipDeselect(); + + } + +private: + + + + void setAddrWindow(const uint16_t x, const uint16_t y, const uint16_t w, const uint16_t h) { + + const uint32_t xa = ((uint32_t)x << 16) | (x+w-1); + const uint32_t ya = ((uint32_t)y << 16) | (y+h-1); + + sendCommand(ILI9341_CASET); // Column addr set + sendQuad(xa); + + sendCommand(ILI9341_PASET); // Row addr set + sendQuad(ya); + + sendCommand(ILI9341_RAMWR); // write to RAM + // transmit data now + + } + + void wait() { + vTaskDelay(100 / portTICK_PERIOD_MS); + } + + + /** select the display (CS=0) */ + inline void chipSelect() { + //spi::chipSelect(); + GPIO::clear(PIN_CS); + } + + + /** send the given command to the display */ + void sendCommand(const uint8_t cmd) { + modeCMD(); + spi.writeByte(cmd); + modeDATA(); + } + + /** send the given command to the display */ + void sendCommand16(const uint8_t cmd) { + modeCMD(); + spi.writeByte(cmd); + spi.writeByte(0x00); + modeDATA(); + } + + /** send the given data byte to the display */ + void sendByte(const uint8_t data) { + spi.writeByte(data); + } + + /** send the given data word to the display */ + void sendWord(const uint16_t data) { + spi.writeWord(data); + } + + /** send the given data quad to the display */ + void sendQuad(const uint32_t data) { + spi.writeQuad(data); + } + + /** send the given data to the display */ + void sendBytes(const uint8_t* data, const size_t len) { + spi.write(data, len); + } + + /** read the given data from the display */ + void readBytes(uint8_t* data, const size_t len) { + spi.read(data, len); + } + + + /** unselect the display (CS=1) */ + inline void chipDeselect() { + //spi::chipDeselect(); + GPIO::set(PIN_CS); + } + + /** switch to command-mode */ + inline void modeCMD() { + //gpio_set_level((gpio_num_t)PIN_DATA_COMMAND, 0); + GPIO::clear(PIN_DATA_COMMAND); + } + + /** switch to data-mode */ + inline void modeDATA() { + //gpio_set_level((gpio_num_t)PIN_DATA_COMMAND, 1); + GPIO::set(PIN_DATA_COMMAND); + } + + +}; + +#endif diff --git a/ext/lcd/SSD1306.h b/ext/lcd/SSD1306.h new file mode 100644 index 0000000..fee11a0 --- /dev/null +++ b/ext/lcd/SSD1306.h @@ -0,0 +1,255 @@ +#ifndef LCD_SSD1306 +#define LCD_SSD1306 + +#include "../../io/SoftI2C.h" + +//#define SSD1306_128_64 1 +//#define SSD1306_64_48 1 // https://github.com/mcauser/Adafruit_SSD1306/blob/esp8266-64x48/Adafruit_SSD1306.cpp + +#if defined SSD1306_128_64 + #define SSD1306_LCDWIDTH 128 + #define SSD1306_LCDHEIGHT 64 +#elif defined SSD1306_128_32 + #define SSD1306_LCDWIDTH 128 + #define SSD1306_LCDHEIGHT 32 +#elif defined SSD1306_96_16 + #define SSD1306_LCDWIDTH 96 + #define SSD1306_LCDHEIGHT 16 +#elif defined SSD1306_64_48 + #define SSD1306_LCDWIDTH 64 + #define SSD1306_LCDHEIGHT 48 +#else + #error "SSD1306 display size not defined" +#endif + +#define SSD1306_SETCONTRAST 0x81 +#define SSD1306_DISPLAYALLON_RESUME 0xA4 +#define SSD1306_DISPLAYALLON 0xA5 +#define SSD1306_NORMALDISPLAY 0xA6 +#define SSD1306_INVERTDISPLAY 0xA7 +#define SSD1306_DISPLAYOFF 0xAE +#define SSD1306_DISPLAYON 0xAF + +#define SSD1306_SETDISPLAYOFFSET 0xD3 +#define SSD1306_SETCOMPINS 0xDA + +#define SSD1306_SETVCOMDETECT 0xDB + +#define SSD1306_SETDISPLAYCLOCKDIV 0xD5 +#define SSD1306_SETPRECHARGE 0xD9 + +#define SSD1306_SETMULTIPLEX 0xA8 + +#define SSD1306_SETLOWCOLUMN 0x00 +#define SSD1306_SETHIGHCOLUMN 0x10 + +#define SSD1306_SETSTARTLINE 0x40 + +#define SSD1306_MEMORYMODE 0x20 +#define SSD1306_COLUMNADDR 0x21 +#define SSD1306_PAGEADDR 0x22 + +#define SSD1306_COMSCANINC 0xC0 +#define SSD1306_COMSCANDEC 0xC8 + +#define SSD1306_SEGREMAP 0xA0 + +#define SSD1306_CHARGEPUMP 0x8D + +#define SSD1306_EXTERNALVCC 0x1 +#define SSD1306_SWITCHCAPVCC 0x2 + +// Scrolling #defines +#define SSD1306_ACTIVATE_SCROLL 0x2F +#define SSD1306_DEACTIVATE_SCROLL 0x2E +#define SSD1306_SET_VERTICAL_SCROLL_AREA 0xA3 +#define SSD1306_RIGHT_HORIZONTAL_SCROLL 0x26 +#define SSD1306_LEFT_HORIZONTAL_SCROLL 0x27 +#define SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL 0x29 +#define SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL 0x2A + + + +class SSD1306 { + +private: + + static constexpr uint8_t ADDR7 = 0b0111100; + bool inited = false; + +public: + + bool isPresent() { + return i2c::query(ADDR7); + } + + void initOnce() { + if (inited) {return;} + init(); + inited = true; + } + + + void flush(const uint8_t* data) { + + sendCommand(SSD1306_COLUMNADDR); + + // special handling for 64x48 oled shield + #if SSD1306_LCDWIDTH == 64 && SSD1306_LCDHEIGHT == 48 + sendCommand(32); + sendCommand(32 + SSD1306_LCDWIDTH - 1); + #else + sendCommand(0); // Column start address (0 = reset) + sendCommand(SSD1306_LCDWIDTH-1); // Column end address (127 = reset) + #endif + + sendCommand(SSD1306_PAGEADDR); + sendCommand(0); // Page start address (0 = reset) + sendCommand( (SSD1306_LCDHEIGHT/8)-1 ); // Page end address +// #if SSD1306_LCDHEIGHT == 64 +// sendCommand(7); // Page end address +// #endif +// #if SSD1306_LCDHEIGHT == 48 +// sendCommand(3); // Page end address +// #endif +// #if SSD1306_LCDHEIGHT == 32 +// sendCommand(3); // Page end address +// #endif +// #if SSD1306_LCDHEIGHT == 16 +// sendCommand(1); // Page end address +// #endif + + + + + +// for (uint16_t i=0; i<(SSD1306_LCDWIDTH*SSD1306_LCDHEIGHT/8); i++) { +// // send a bunch of data in one xmission +// i2c::startWrite(ADDR7); +// bool ok = i2c::writeByteAndCheck(0x40); +// if (!ok) {os_printf("failed line\n");} + +// for (uint8_t x = 0; x < 16; ++x) { +// i2c::writeByteAndCheck(data[i]); +// ++i; +// } +// i--; +// i2c::stop(); +// } + + i2c::startWrite(ADDR7); + bool ok = i2c::writeByteAndCheck(0x40); + if (!ok) {os_printf("failed write data\n");} + + for (uint16_t i=0; i < (SSD1306_LCDWIDTH*SSD1306_LCDHEIGHT/8); i++) { + i2c::writeByteAndCheck(data[i]); + } + + i2c::stop(); + + } + + +private: + + void init() { + + uint8_t vccstate = 0; + + sendCommand(SSD1306_DISPLAYOFF); // 0xAE + sendCommand(SSD1306_SETDISPLAYCLOCKDIV); // 0xD5 + sendCommand(0x80); // the suggested ratio 0x80 + + sendCommand(SSD1306_SETMULTIPLEX); // 0xA8 + sendCommand(SSD1306_LCDHEIGHT - 1); + + sendCommand(SSD1306_SETDISPLAYOFFSET); // 0xD3 + sendCommand(0x0); // no offset + sendCommand(SSD1306_SETSTARTLINE | 0x0); // line #0 + sendCommand(SSD1306_CHARGEPUMP); // 0x8D + if (vccstate == SSD1306_EXTERNALVCC) { + sendCommand(0x10); + } else { + sendCommand(0x14); + } + sendCommand(SSD1306_MEMORYMODE); // 0x20 + sendCommand(0x00); // 0x0 act like ks0108 + sendCommand(SSD1306_SEGREMAP | 0x1); + + sendCommand(SSD1306_COMSCANDEC); // rotate 180? + + // OLD +// #if defined SSD1306_128_32 +// sendCommand(SSD1306_SETCOMPINS); // 0xDA +// sendCommand(0x02); +// sendCommand(SSD1306_SETCONTRAST); // 0x81 +// sendCommand(0x8F); + +// #elif defined SSD1306_128_64 +// sendCommand(SSD1306_SETCOMPINS); // 0xDA +// sendCommand(0x12); +// sendCommand(SSD1306_SETCONTRAST); // 0x81 +// if (vccstate == SSD1306_EXTERNALVCC) +// { sendCommand(0x9F); } +// else +// { sendCommand(0xCF); } + +// #elif defined SSD1306_96_16 +// sendCommand(SSD1306_SETCOMPINS); // 0xDA +// sendCommand(0x2); //ada x12 +// sendCommand(SSD1306_SETCONTRAST); // 0x81 +// if (vccstate == SSD1306_EXTERNALVCC) +// { sendCommand(0x10); } +// else +// { sendCommand(0xAF); } + +// #endif + + // NEW + sendCommand(SSD1306_SETCOMPINS); // 0xDA + sendCommand(0x12); + + if (vccstate == SSD1306_EXTERNALVCC) { + sendCommand(0x9F); + } else { + sendCommand(0xCF); + } + + + + + sendCommand(SSD1306_SETPRECHARGE); // 0xd9 + if (vccstate == SSD1306_EXTERNALVCC) { + sendCommand(0x22); + } else { + sendCommand(0xF1); + } + + sendCommand(SSD1306_SETVCOMDETECT); // 0xDB + sendCommand(0x40); + sendCommand(SSD1306_DISPLAYALLON_RESUME); // 0xA4 + sendCommand(SSD1306_NORMALDISPLAY); // 0xA6 + + sendCommand(SSD1306_DEACTIVATE_SCROLL); + + sendCommand(SSD1306_DISPLAYON);//--turn on oled panel + + } + +private: + + void sendCommand(uint8_t cmd) { + bool ok; + ok = i2c::startWrite(ADDR7); + if (!ok) {os_printf("failed start write\n");} + ok = i2c::writeByteAndCheck(0x00); // command + if (!ok) {os_printf("failed command mode\n");} + ok = i2c::writeByteAndCheck(cmd); + if (!ok) {os_printf("failed command\n");} + i2c::stop(); + } + +}; + + +#endif diff --git a/ext/lcd/SSD1306_old.h b/ext/lcd/SSD1306_old.h new file mode 100644 index 0000000..1d86d8e --- /dev/null +++ b/ext/lcd/SSD1306_old.h @@ -0,0 +1,249 @@ +#ifndef LCD_SSD1306 +#define LCD_SSD1306 + +#include "../../io/SoftI2C.h" + +//#define SSD1306_128_64 1 +#define SSD1306_64_48 1 + +#if defined SSD1306_128_64 + #define SSD1306_LCDWIDTH 128 + #define SSD1306_LCDHEIGHT 64 +#endif +#if defined SSD1306_128_32 + #define SSD1306_LCDWIDTH 128 + #define SSD1306_LCDHEIGHT 32 +#endif +#if defined SSD1306_96_16 + #define SSD1306_LCDWIDTH 96 + #define SSD1306_LCDHEIGHT 16 +#endif +#if defined SSD1306_64_48 + #define SSD1306_LCDWIDTH 64 + #define SSD1306_LCDHEIGHT 48 +#endif + +#define SSD1306_SETCONTRAST 0x81 +#define SSD1306_DISPLAYALLON_RESUME 0xA4 +#define SSD1306_DISPLAYALLON 0xA5 +#define SSD1306_NORMALDISPLAY 0xA6 +#define SSD1306_INVERTDISPLAY 0xA7 +#define SSD1306_DISPLAYOFF 0xAE +#define SSD1306_DISPLAYON 0xAF + +#define SSD1306_SETDISPLAYOFFSET 0xD3 +#define SSD1306_SETCOMPINS 0xDA + +#define SSD1306_SETVCOMDETECT 0xDB + +#define SSD1306_SETDISPLAYCLOCKDIV 0xD5 +#define SSD1306_SETPRECHARGE 0xD9 + +#define SSD1306_SETMULTIPLEX 0xA8 + +#define SSD1306_SETLOWCOLUMN 0x00 +#define SSD1306_SETHIGHCOLUMN 0x10 + +#define SSD1306_SETSTARTLINE 0x40 + +#define SSD1306_MEMORYMODE 0x20 +#define SSD1306_COLUMNADDR 0x21 +#define SSD1306_PAGEADDR 0x22 + +#define SSD1306_COMSCANINC 0xC0 +#define SSD1306_COMSCANDEC 0xC8 + +#define SSD1306_SEGREMAP 0xA0 + +#define SSD1306_CHARGEPUMP 0x8D + +#define SSD1306_EXTERNALVCC 0x1 +#define SSD1306_SWITCHCAPVCC 0x2 + +// Scrolling #defines +#define SSD1306_ACTIVATE_SCROLL 0x2F +#define SSD1306_DEACTIVATE_SCROLL 0x2E +#define SSD1306_SET_VERTICAL_SCROLL_AREA 0xA3 +#define SSD1306_RIGHT_HORIZONTAL_SCROLL 0x26 +#define SSD1306_LEFT_HORIZONTAL_SCROLL 0x27 +#define SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL 0x29 +#define SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL 0x2A + + + +class SSD1306 { + +private: + + static constexpr uint8_t ADDR7 = 0b0111100; + bool inited = false; + +public: + + bool isPresent() { + return i2c::query(ADDR7); + } + + void initOnce() { + if (inited) {return;} + init(); + inited = true; + } + + + void flush(const uint8_t* data) { + + sendCommand(SSD1306_COLUMNADDR); + sendCommand(0); // Column start address (0 = reset) + sendCommand(SSD1306_LCDWIDTH-1); // Column end address (127 = reset) + + sendCommand(SSD1306_PAGEADDR); + sendCommand(0); // Page start address (0 = reset) + sendCommand( (SSD1306_LCDHEIGHT/8)-1 ); // Page end address +// #if SSD1306_LCDHEIGHT == 64 +// sendCommand(7); // Page end address +// #endif +// #if SSD1306_LCDHEIGHT == 48 +// sendCommand(3); // Page end address +// #endif +// #if SSD1306_LCDHEIGHT == 32 +// sendCommand(3); // Page end address +// #endif +// #if SSD1306_LCDHEIGHT == 16 +// sendCommand(1); // Page end address +// #endif + + + + + +// for (uint16_t i=0; i<(SSD1306_LCDWIDTH*SSD1306_LCDHEIGHT/8); i++) { +// // send a bunch of data in one xmission +// i2c::startWrite(ADDR7); +// bool ok = i2c::writeByteAndCheck(0x40); +// if (!ok) {os_printf("failed line\n");} + +// for (uint8_t x = 0; x < 16; ++x) { +// i2c::writeByteAndCheck(data[i]); +// ++i; +// } +// i--; +// i2c::stop(); +// } + + i2c::startWrite(ADDR7); + bool ok = i2c::writeByteAndCheck(0x40); + if (!ok) {os_printf("failed write data\n");} + + for (uint16_t i=0; i < 256; ++i) {//i<(SSD1306_LCDWIDTH*SSD1306_LCDHEIGHT/8); i++) { + i2c::writeByteAndCheck(data[i]); + } + + i2c::stop(); + + } + + +private: + + void init() { + + uint8_t vccstate = 0; + + sendCommand(SSD1306_DISPLAYOFF); // 0xAE + sendCommand(SSD1306_SETDISPLAYCLOCKDIV); // 0xD5 + sendCommand(0x80); // the suggested ratio 0x80 + + sendCommand(SSD1306_SETMULTIPLEX); // 0xA8 + sendCommand(SSD1306_LCDHEIGHT - 1); + + sendCommand(SSD1306_SETDISPLAYOFFSET); // 0xD3 + sendCommand(0x0); // no offset + sendCommand(SSD1306_SETSTARTLINE | 0x0); // line #0 + sendCommand(SSD1306_CHARGEPUMP); // 0x8D + if (vccstate == SSD1306_EXTERNALVCC) { + sendCommand(0x10); + } else { + sendCommand(0x14); + } + sendCommand(SSD1306_MEMORYMODE); // 0x20 + sendCommand(0x00); // 0x0 act like ks0108 + sendCommand(SSD1306_SEGREMAP | 0x1); + + sendCommand(SSD1306_COMSCANDEC); // rotate 180? + + // OLD +// #if defined SSD1306_128_32 +// sendCommand(SSD1306_SETCOMPINS); // 0xDA +// sendCommand(0x02); +// sendCommand(SSD1306_SETCONTRAST); // 0x81 +// sendCommand(0x8F); + +// #elif defined SSD1306_128_64 +// sendCommand(SSD1306_SETCOMPINS); // 0xDA +// sendCommand(0x12); +// sendCommand(SSD1306_SETCONTRAST); // 0x81 +// if (vccstate == SSD1306_EXTERNALVCC) +// { sendCommand(0x9F); } +// else +// { sendCommand(0xCF); } + +// #elif defined SSD1306_96_16 +// sendCommand(SSD1306_SETCOMPINS); // 0xDA +// sendCommand(0x2); //ada x12 +// sendCommand(SSD1306_SETCONTRAST); // 0x81 +// if (vccstate == SSD1306_EXTERNALVCC) +// { sendCommand(0x10); } +// else +// { sendCommand(0xAF); } + +// #endif + + // NEW + sendCommand(SSD1306_SETCOMPINS); // 0xDA + sendCommand(0x12); + + if (vccstate == SSD1306_EXTERNALVCC) { + sendCommand(0x9F); + } else { + sendCommand(0xCF); + } + + + + + sendCommand(SSD1306_SETPRECHARGE); // 0xd9 + if (vccstate == SSD1306_EXTERNALVCC) { + sendCommand(0x22); + } else { + sendCommand(0xF1); + } + + sendCommand(SSD1306_SETVCOMDETECT); // 0xDB + sendCommand(0x40); + sendCommand(SSD1306_DISPLAYALLON_RESUME); // 0xA4 + sendCommand(SSD1306_NORMALDISPLAY); // 0xA6 + + sendCommand(SSD1306_DEACTIVATE_SCROLL); + + sendCommand(SSD1306_DISPLAYON);//--turn on oled panel + + } + +private: + + void sendCommand(uint8_t cmd) { + bool ok; + ok = i2c::startWrite(ADDR7); + if (!ok) {os_printf("failed start write\n");} + ok = i2c::writeByteAndCheck(0x00); // command + if (!ok) {os_printf("failed command mode\n");} + ok = i2c::writeByteAndCheck(cmd); + if (!ok) {os_printf("failed command\n");} + i2c::stop(); + } + +}; + + +#endif diff --git a/ext/lcd/ST7735.h b/ext/lcd/ST7735.h new file mode 100644 index 0000000..47a47f9 --- /dev/null +++ b/ext/lcd/ST7735.h @@ -0,0 +1,295 @@ +#ifndef LCD_SSD1306 +#define LCD_SSD1306 + +#include "../../io/SoftSPI.h" + + +/** + * https://www.displayfuture.com/Display/datasheet/controller/ST7735.pdf + * https://www.aliexpress.com/item/1-8-inch-TFT-color-screen-module-ST7735-SPI-at-least-4IO-support-C51-STM32-with/32809505663.html?spm=2114.search0302.3.266.14d83b8cQXOxfz&ws_ab_test=searchweb0_0,searchweb201602_0_450_452_532_533_534_10618_535_317_318_319_10059_10696_10084_100031_10083_10547_10304_10546_10843_10887_320_10307_10548_204_448_10065_449_10068_10320_10103_10884_10820,searchweb201603_0,ppcSwitch_0&algo_pvid=7cf0bb24-ac60-4e97-9878-8716f61a79f3&algo_expid=7cf0bb24-ac60-4e97-9878-8716f61a79f3-33 + * https://sites.google.com/site/arduinomega2560projects/microchip-pic/level-3/st7735-1-8-tft (!!!!) + * + * display is SPI + * CLK = clock + * SDA = mosi + * RS = data/command + * RST = reset + * CS = chip-select + */ + +class ST7735 { + +private: + + static constexpr const char* NAME = "ST7735"; + + bool inited = false; + + /** switch D/C line low */ + inline void modeCommand() { + GPIO5_OUTPUT_SET; + GPIO5_L; + } + + /** switch D/C line high */ + inline void modeData() { + GPIO5_OUTPUT_SET; + GPIO5_H; + } + + /** set CS = low */ + inline void select() { + spi::chipSelect(); + } + + /** set CS = high */ + inline void deselect() { + spi::chipDeselect(); + } + + +public: + + + void initOnce() { + if (inited) {return;} + init(); + inited = true; + } + +#define ST7735_NOP 0x00 +#define ST7735_SWRESET 0x01 +#define ST7735_RDDID 0x04 +#define ST7735_RDDST 0x09 + +#define ST7735_SLPIN 0x10 +#define ST7735_SLPOUT 0x11 +#define ST7735_PTLON 0x12 +#define ST7735_NORON 0x13 + +#define ST7735_INVOFF 0x20 +#define ST7735_INVON 0x21 +#define ST7735_DISPOFF 0x28 +#define ST7735_DISPON 0x29 +#define ST7735_CASET 0x2A +#define ST7735_RASET 0x2B +#define ST7735_RAMWR 0x2C +#define ST7735_RAMRD 0x2E + +#define ST7735_PTLAR 0x30 +#define ST7735_COLMOD 0x3A +#define ST7735_MADCTL 0x36 + +#define ST7735_FRMCTR1 0xB1 +#define ST7735_FRMCTR2 0xB2 +#define ST7735_FRMCTR3 0xB3 +#define ST7735_INVCTR 0xB4 +#define ST7735_DISSET5 0xB6 + +#define ST7735_PWCTR1 0xC0 +#define ST7735_PWCTR2 0xC1 +#define ST7735_PWCTR3 0xC2 +#define ST7735_PWCTR4 0xC3 +#define ST7735_PWCTR5 0xC4 +#define ST7735_VMCTR1 0xC5 + +#define ST7735_RDID1 0xDA +#define ST7735_RDID2 0xDB +#define ST7735_RDID3 0xDC +#define ST7735_RDID4 0xDD + +#define ST7735_PWCTR6 0xFC + +#define ST7735_GMCTRP1 0xE0 +#define ST7735_GMCTRN1 0xE1 + + +private: + +#define V 128 +#define H 160 + + void waitLong() { + for (int i = 0; i < 50; ++i) { + os_delay_us(10*1000); + } + } + + void waitShort() { + os_delay_us(10*1000); + } + + void init() { + + spi::init(); + + select(); + + sendCommand(ST7735_SWRESET); // 1: Software reset, 0 args + waitLong(); + + sendCommand(ST7735_SLPOUT); // 2: Out of sleep mode, 0 args + waitLong(); + + sendCommand(ST7735_FRMCTR1); // 3: Frame rate ctrl - normal mode) 3 args: 0xb1 + sendData(0x01); sendData(0x2C); sendData(0x2D); // Rate = fosc/(1x2+40) * (LINE+2C+2D) + + sendCommand(ST7735_FRMCTR2); // 4: Frame rate control - idle mode) 3 args: 0xb2 + sendData(0x01); sendData(0x2C); sendData(0x2D); // Rate = fosc/(1x2+40) * (LINE+2C+2D) + + sendCommand(ST7735_FRMCTR3); // 5: Frame rate ctrl - partial mode) 6 args: 0xb3 + sendData(0x01); sendData(0x2C); sendData(0x2D); // Dot inversion mode + sendData(0x01); sendData(0x2C); sendData(0x2D); // Line inversion mode + + sendCommand(ST7735_INVCTR); // 6: Display inversion ctrl) 1 arg) no delay: 0xb4 + sendData(0x07); // No inversion + + sendCommand(ST7735_PWCTR1); // 7: Power control) 3 args) no delay: 0xc0 + sendData(0xA2); + sendData(0x02); // -4.6V + sendData(0x84); // AUTO mode + + sendCommand(ST7735_PWCTR2); // 8: Power control) 1 arg) no delay: 0xc1 + sendData(0xC5); // VGH25 = 2.4C VGSEL = -10 VGH = 3 * AVDD + + sendCommand(ST7735_PWCTR3); // 9: Power control) 2 args) no delay: 0xc2 + sendData(0x0A); // Opamp current small + sendData(0x00); // Boost frequency + + sendCommand(ST7735_PWCTR4); // 10: Power control) 2 args) no delay: + sendData(0x8A); // BCLK/2) Opamp current small & Medium low + sendData(0x2A); + + sendCommand(ST7735_PWCTR5); // 11: Power control) 2 args) no delay: + sendData(0x8A); sendData(0xEE); + + sendCommand(ST7735_VMCTR1); // 12: Power control) 1 arg) no delay: + sendData(0x0E); + + sendCommand(ST7735_INVOFF); // 13: Don't invert display) no args) no delay 0x20 + + sendCommand(ST7735_MADCTL); // 14: Memory access control (directions)) 1 arg: + sendData(0xC8); // row addr/col addr); bottom to top refresh + + sendCommand(ST7735_COLMOD); // 15: set color mode); 1 arg); no delay: + sendData(0x05); // 16-bit color + + + // required??? + if (1 == 1) { + + sendCommand(ST7735_GMCTRP1); // 1: Magical unicorn dust, 16 args, no delay: + sendData(0x02); sendData(0x1c); sendData(0x07); sendData(0x12); + sendData(0x37); sendData(0x32); sendData(0x29); sendData(0x2d); + sendData(0x29); sendData(0x25); sendData(0x2B); sendData(0x39); + sendData(0x00); sendData(0x01); sendData(0x03); sendData(0x10); + + sendCommand(ST7735_GMCTRN1); // 2: Sparkles and rainbows, 16 args, no delay: + sendData(0x03); sendData(0x1d); sendData(0x07); sendData(0x06); + sendData(0x2E); sendData(0x2C); sendData(0x29); sendData(0x2D); + sendData(0x2E); sendData(0x2E); sendData(0x37); sendData(0x3F); + sendData(0x00); sendData(0x00); sendData(0x02); sendData(0x10); + + } + + sendCommand(ST7735_NORON); // 3: Normal display on, no args + waitShort(); + + sendCommand(ST7735_DISPON); // 4: Main screen turn on, no args + waitLong(); + + deselect(); + + debugMod(NAME, "init() done"); + + } + + + void clean(uint16_t color){ + + select(); + + uint8_t lo = color & 0xFF; + uint8_t hi = color >> 8; + + sendCommand(ST7735_CASET); // Column addr set + sendData(0x00); + sendData(0); // XSTART + sendData(0x00); + sendData(V-1); // XEND + + sendCommand(ST7735_RASET); // Row addr set + sendData(0x00); + sendData(0); // YSTART + sendData(0x00); + sendData(H-1); // YEND + + sendCommand(ST7735_RAMWR); + + for(char x=0;x> 8; + //sendData(lo); + //sendData(hi); + spi::writeWord(color); + } + + deselect(); + + } + + + +private: + + void sendData(uint8_t data) { + modeData(); + //select(); + spi::writeByte(data); + //deselect(); + } + + void sendCommand(uint8_t cmd) { + modeCommand(); + //select(); + spi::writeByte(cmd); + //deselect(); + } + +}; + + +#endif diff --git a/ext/lcd/ST7735_old.h b/ext/lcd/ST7735_old.h new file mode 100644 index 0000000..5a1912f --- /dev/null +++ b/ext/lcd/ST7735_old.h @@ -0,0 +1,413 @@ +#ifndef LCD_SSD1306 +#define LCD_SSD1306 + +#include "../../io/SoftSPI.h" + + +/** + * https://www.displayfuture.com/Display/datasheet/controller/ST7735.pdf + * https://www.aliexpress.com/item/1-8-inch-TFT-color-screen-module-ST7735-SPI-at-least-4IO-support-C51-STM32-with/32809505663.html?spm=2114.search0302.3.266.14d83b8cQXOxfz&ws_ab_test=searchweb0_0,searchweb201602_0_450_452_532_533_534_10618_535_317_318_319_10059_10696_10084_100031_10083_10547_10304_10546_10843_10887_320_10307_10548_204_448_10065_449_10068_10320_10103_10884_10820,searchweb201603_0,ppcSwitch_0&algo_pvid=7cf0bb24-ac60-4e97-9878-8716f61a79f3&algo_expid=7cf0bb24-ac60-4e97-9878-8716f61a79f3-33 + * https://sites.google.com/site/arduinomega2560projects/microchip-pic/level-3/st7735-1-8-tft (!!!!) + * + * display is SPI + * CLK = clock + * SDA = mosi + * RS = data/command + * RST = reset + * CS = chip-select + */ + +class ST7735 { + +private: + + static constexpr const char* NAME = "ST7735"; + + bool inited = false; + + /** switch D/C line low */ + inline void modeCommand() { + GPIO5_OUTPUT_SET; + GPIO5_L; + } + + /** switch D/C line high */ + inline void modeData() { + GPIO5_OUTPUT_SET; + GPIO5_H; + } + + /** set CS = low */ + inline void select() { + spi::chipSelect(); + } + + /** set CS = high */ + inline void deselect() { + spi::chipDeselect(); + } + + +public: + + + void initOnce() { + if (inited) {return;} + init(); + inited = true; + } + +//#define ST77XX_NOP 0x00 +//#define ST77XX_SWRESET 0x01 +//#define ST77XX_RDDID 0x04 +//#define ST77XX_RDDST 0x09 + +//#define ST77XX_SLPIN 0x10 +//#define ST77XX_SLPOUT 0x11 +//#define ST77XX_PTLON 0x12 +//#define ST77XX_NORON 0x13 + +//#define ST77XX_INVOFF 0x20 +//#define ST77XX_INVON 0x21 +//#define ST77XX_DISPOFF 0x28 +//#define ST77XX_DISPON 0x29 +//#define ST77XX_CASET 0x2A +//#define ST77XX_RASET 0x2B +//#define ST77XX_RAMWR 0x2C +//#define ST77XX_RAMRD 0x2E + +//#define ST77XX_PTLAR 0x30 +//#define ST77XX_COLMOD 0x3A +//#define ST77XX_MADCTL 0x36 + +//#define ST77XX_MADCTL_MY 0x80 +//#define ST77XX_MADCTL_MX 0x40 +//#define ST77XX_MADCTL_MV 0x20 +//#define ST77XX_MADCTL_ML 0x10 +//#define ST77XX_MADCTL_RGB 0x00 + +//#define ST77XX_RDID1 0xDA +//#define ST77XX_RDID2 0xDB +//#define ST77XX_RDID3 0xDC +//#define ST77XX_RDID4 0xDD + +//#define ST7735_MADCTL_BGR 0x08 +//#define ST7735_MADCTL_MH 0x04 + +//#define ST7735_FRMCTR1 0xB1 +//#define ST7735_FRMCTR2 0xB2 +//#define ST7735_FRMCTR3 0xB3 +//#define ST7735_INVCTR 0xB4 +//#define ST7735_DISSET5 0xB6 + +//#define ST7735_PWCTR1 0xC0 +//#define ST7735_PWCTR2 0xC1 +//#define ST7735_PWCTR3 0xC2 +//#define ST7735_PWCTR4 0xC3 +//#define ST7735_PWCTR5 0xC4 +//#define ST7735_VMCTR1 0xC5 + +//#define ST7735_PWCTR6 0xFC + +//#define ST7735_GMCTRP1 0xE0 +//#define ST7735_GMCTRN1 0xE1 + + + +#define ST7735_NOP 0x00 +#define ST7735_SWRESET 0x01 +#define ST7735_RDDID 0x04 +#define ST7735_RDDST 0x09 + +#define ST7735_SLPIN 0x10 +#define ST7735_SLPOUT 0x11 +#define ST7735_PTLON 0x12 +#define ST7735_NORON 0x13 + +#define ST7735_INVOFF 0x20 +#define ST7735_INVON 0x21 +#define ST7735_DISPOFF 0x28 +#define ST7735_DISPON 0x29 +#define ST7735_CASET 0x2A +#define ST7735_RASET 0x2B +#define ST7735_RAMWR 0x2C +#define ST7735_RAMRD 0x2E + +#define ST7735_PTLAR 0x30 +#define ST7735_COLMOD 0x3A +#define ST7735_MADCTL 0x36 + +#define ST7735_FRMCTR1 0xB1 +#define ST7735_FRMCTR2 0xB2 +#define ST7735_FRMCTR3 0xB3 +#define ST7735_INVCTR 0xB4 +#define ST7735_DISSET5 0xB6 + +#define ST7735_PWCTR1 0xC0 +#define ST7735_PWCTR2 0xC1 +#define ST7735_PWCTR3 0xC2 +#define ST7735_PWCTR4 0xC3 +#define ST7735_PWCTR5 0xC4 +#define ST7735_VMCTR1 0xC5 + +#define ST7735_RDID1 0xDA +#define ST7735_RDID2 0xDB +#define ST7735_RDID3 0xDC +#define ST7735_RDID4 0xDD + +#define ST7735_PWCTR6 0xFC + +#define ST7735_GMCTRP1 0xE0 +#define ST7735_GMCTRN1 0xE1 + + +private: + +#define V 128 +#define H 160 + + void waitLong() { + for (int i = 0; i < 50; ++i) { + os_delay_us(10*1000); + } + } + + void waitShort() { + os_delay_us(10*1000); + } + + void init() { + + spi::init(); + + debugMod(NAME, "init()"); + +// sendCommand(ST77XX_SWRESET); waitLong(); +// sendCommand(ST77XX_SLPOUT); waitLong(); +// sendCommand(ST77XX_COLMOD, 0x05); waitShort(); + +// sendCommand(ST7735_FRMCTR1, 0x00, 0x06, 0x03); waitShort(); +// sendCommand(ST77XX_MADCTL, 0x08); +// sendCommand(ST7735_DISSET5, 0x15, 0x02); +// sendCommand(ST7735_INVCTR, 0x0); + +// sendCommand(ST7735_PWCTR1, 0x02, 0x70); waitShort(); + +// sendCommand(ST7735_PWCTR2, 0x05); +// sendCommand(ST7735_PWCTR3, 0x01, 0x02); +// sendCommand(ST7735_VMCTR1, 0x3C, 0x38); waitShort(); +// sendCommand(ST7735_PWCTR6, 0x11, 0x15); + +// sendCommand(ST7735_GMCTRP1, +// 0x09, 0x16, 0x09, 0x20, +// 0x21, 0x1B, 0x13, 0x19, +// 0x17, 0x15, 0x1E, 0x2B, +// 0x04, 0x05, 0x02, 0x0E); +// sendCommand(ST7735_GMCTRN1, +// 0x0B, 0x14, 0x08, 0x1E, +// 0x22, 0x1D, 0x18, 0x1E, +// 0x1B, 0x1A, 0x24, 0x2B, +// 0x06, 0x06, 0x02, 0x0F); +// waitShort(); + +// sendCommand(ST77XX_CASET, 0x00, 0x02, 0x00, 0x81); +// sendCommand(ST77XX_RASET, 0x00, 0x02, 0x00, 0x81); +// sendCommand(ST77XX_NORON); waitShort(); +// sendCommand(ST77XX_DISPON); waitLong(); + + + sendCommand(ST7735_SWRESET); // 1: Software reset, 0 args, w/delay 150ms 0x01 + waitLong(); + sendCommand(ST7735_SLPOUT); // 2: Out of sleep mode, 0 args, w/delay 500ms 0x11 + waitLong(); + sendCommand(ST7735_FRMCTR1); // 3: Frame rate ctrl - normal mode) 3 args: 0xb1 + sendData(0x01); sendData(0x2C); sendData(0x2D); // Rate = fosc/(1x2+40) * (LINE+2C+2D) + sendCommand(ST7735_FRMCTR2); // 4: Frame rate control - idle mode) 3 args: 0xb2 + sendData(0x01); sendData(0x2C); sendData(0x2D); // Rate = fosc/(1x2+40) * (LINE+2C+2D) + sendCommand(ST7735_FRMCTR3); // 5: Frame rate ctrl - partial mode) 6 args: 0xb3 + sendData(0x01); sendData(0x2C); sendData(0x2D); // Dot inversion mode + sendData(0x01); sendData(0x2C); sendData(0x2D); // Line inversion mode + sendCommand(ST7735_INVCTR); // 6: Display inversion ctrl) 1 arg) no delay: 0xb4 + sendData(0x07); // No inversion + sendCommand(ST7735_PWCTR1); // 7: Power control) 3 args) no delay: 0xc0 + sendData(0xA2); + sendData(0x02); // -4.6V + sendData(0x84); // AUTO mode + sendCommand(ST7735_PWCTR2); // 8: Power control) 1 arg) no delay: 0xc1 + sendData(0xC5); // VGH25 = 2.4C VGSEL = -10 VGH = 3 * AVDD + sendCommand(ST7735_PWCTR3); // 9: Power control) 2 args) no delay: 0xc2 + sendData(0x0A); // Opamp current small + sendData(0x00); // Boost frequency + sendCommand(ST7735_PWCTR4); // 10: Power control) 2 args) no delay: + sendData(0x8A); // BCLK/2) Opamp current small & Medium low + sendData(0x2A); + sendCommand(ST7735_PWCTR5); // 11: Power control) 2 args) no delay: + sendData(0x8A); sendData(0xEE); + sendCommand(ST7735_VMCTR1); // 12: Power control) 1 arg) no delay: + sendData(0x0E); + sendCommand(ST7735_INVOFF); // 13: Don't invert display) no args) no delay 0x20 + sendCommand(ST7735_MADCTL); // 14: Memory access control (directions)) 1 arg: + sendData(0xC8); // row addr/col addr); bottom to top refresh + sendCommand(ST7735_COLMOD); // 15: set color mode); 1 arg); no delay: + sendData(0x05); // 16-bit color + sendCommand(ST7735_NORON); // 3: Normal display on, no args, w/delay 10ms 0x13 + waitShort(); + sendCommand(ST7735_DISPON); // 4: Main screen turn on, no args w/delay 100ms 0x29 + waitLong(); + + + + debugMod(NAME, "init() done"); + + clean(0x1111110000001111); + + } + + + void clean(uint16_t color){ + + uint8_t lo = color & 0xFF; + uint8_t hi = color >> 8; + + sendCommand(ST7735_CASET); // Column addr set + sendData(0x00); + sendData(0); // XSTART + sendData(0x00); + sendData(V); // XEND + + sendCommand(ST7735_RASET); // Row addr set + sendData(0x00); + sendData(0); // YSTART + sendData(0x00); + sendData(H); // YEND + + sendCommand(ST7735_RAMWR); + + for(char x=0;x> 8; + sendData(lo); + sendData(hi); + } + + } + + + +private: + + void sendData(uint8_t data) { + modeData(); + select(); + spi::writeByte(data); + deselect(); + } + + void sendCommand(uint8_t cmd) { + modeCommand(); + select(); + spi::writeByte(cmd); + deselect(); + } + +// void sendCommand(uint8_t cmd, uint8_t arg1) { +// modeCommand(); +// select(); +// spi::writeByte(cmd); +// deselect(); +// sendData(arg1); +// } + +// void sendCommand(uint8_t cmd, uint8_t arg1, uint8_t arg2) { +// modeCommand(); +// select(); +// spi::writeByte(cmd); +// deselect(); +// sendData(arg1); +// sendData(arg2); +// } + +// void sendCommand(uint8_t cmd, uint8_t arg1, uint8_t arg2, uint8_t arg3) { +// modeCommand(); +// select(); +// spi::writeByte(cmd); +// deselect(); +// sendData(arg1); +// sendData(arg2); +// sendData(arg3); +// } + +// void sendCommand(uint8_t cmd, uint8_t arg1, uint8_t arg2, uint8_t arg3, uint8_t arg4) { +// modeCommand(); +// select(); +// spi::writeByte(cmd); +// deselect(); +// sendData(arg1); +// sendData(arg2); +// sendData(arg3); +// sendData(arg4); +// } + +// void sendCommand(uint8_t cmd, +// uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, +// uint8_t a5, uint8_t a6, uint8_t a7, uint8_t a8, +// uint8_t a9, uint8_t a10, uint8_t a11, uint8_t a12, +// uint8_t a13, uint8_t a14, uint8_t a15, uint8_t a16) { +// modeCommand(); +// select(); +// spi::writeByte(cmd); +// deselect(); + +// sendData(a1); +// sendData(a2); +// sendData(a3); +// sendData(a4); + +// sendData(a5); +// sendData(a6); +// sendData(a7); +// sendData(a8); + +// sendData(a9); +// sendData(a10); +// sendData(a11); +// sendData(a12); + +// sendData(a13); +// sendData(a14); +// sendData(a15); +// sendData(a16); + +// } + +}; + + +#endif diff --git a/ext/lcd/ui/UI.h b/ext/lcd/ui/UI.h new file mode 100644 index 0000000..5475164 --- /dev/null +++ b/ext/lcd/ui/UI.h @@ -0,0 +1,101 @@ +#ifndef UI_H +#define UI_H + +#include +#include "../Draw.h" +#include "../../../Debug.h" + + +class Setter; +extern Setter setter; + +//uint8_t f1_data[] = {84,18,71,192,137,130,0,0,32,135,28,7,250,156,207,113,0,0,112,124,156,199,121,190,207,137,130,162,32,40,114,30,231,113,190,40,10,138,162,239,226,8,16,64,0,64,0,7,4,65,65,0,0,0,0,0,0,0,0,0,0,0,172,1,84,146,170,32,74,164,2,0,160,200,162,8,11,34,40,138,0,0,136,130,162,40,138,130,32,138,130,162,32,40,138,162,40,138,136,40,10,138,34,40,130,20,32,64,0,64,128,0,4,0,65,0,0,0,0,0,32,0,0,0,0,0,34,74,84,191,162,36,40,200,33,0,160,168,32,136,10,2,40,138,10,129,128,178,162,40,136,130,32,136,130,146,96,44,138,162,40,10,136,40,74,138,34,40,130,34,0,206,227,120,142,224,61,65,81,55,143,243,120,153,243,68,81,80,20,125,34,74,4,146,66,34,32,168,34,0,144,140,16,72,122,2,36,138,128,60,65,170,162,40,136,130,32,136,130,138,160,106,138,162,40,10,136,40,74,82,20,36,132,0,0,81,20,69,209,19,69,65,73,73,81,20,69,69,36,68,81,80,20,65,34,74,4,18,7,193,33,136,248,240,144,138,8,38,130,30,194,241,64,0,34,170,162,39,136,142,35,251,130,134,32,169,138,158,232,113,136,40,74,34,8,34,132,0,0,80,20,68,145,16,69,65,69,73,81,20,69,67,32,68,81,146,18,33,33,76,4,18,138,36,34,8,32,0,144,137,4,232,131,34,33,130,128,60,33,170,190,40,136,130,32,138,130,138,32,40,139,130,168,128,136,40,74,82,8,33,132,0,0,94,20,68,159,16,69,65,67,73,81,20,69,129,35,68,81,18,17,17,34,74,4,63,74,42,34,8,32,0,136,136,2,8,130,162,32,130,0,129,32,114,162,40,136,130,32,138,130,146,32,40,138,130,42,129,136,40,74,138,136,32,136,0,0,81,20,68,129,16,69,65,69,73,81,20,69,1,36,68,81,146,18,9,34,74,0,146,10,42,66,4,0,0,136,136,130,8,138,162,32,138,0,0,0,2,162,40,138,130,32,138,138,162,32,40,138,130,36,138,136,72,73,138,136,32,136,0,0,81,20,69,145,224,69,65,73,65,81,20,69,65,36,68,74,82,228,5,34,74,4,18,7,196,131,2,0,2,10,135,62,7,114,156,192,113,10,0,32,124,162,199,121,190,192,139,114,162,47,40,114,2,43,114,8,135,176,137,136,239,232,0,0,222,227,120,142,0,69,65,81,65,145,243,120,129,195,57,132,77,4,125,172,73,}; +//uint16_t f1_offsets[] = {0,1,3,7,14,20,28,34,36,40,44,50,56,59,64,66,70,76,80,86,92,98,104,110,116,122,128,130,133,137,142,146,152,160,166,172,178,184,190,196,202,208,210,216,222,228,236,242,248,254,260,266,272,278,284,290,298,304,310,316,320,324,328,334,340,343,349,355,361,367,373,379,385,391,393,399,405,407,415,421,427,433,439,445,451,457,463,469,477,483,489,495,500,502,507,}; +//FontWrap fnt_f1(512,9,f1_data,f1_offsets); + +//uint8_t f1_data[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,128,0,128,20,0,0,0,0,0,0,0,0,0,0,0,0,0,240,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,1,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,53,0,2,229,155,6,162,138,0,128,156,227,251,162,239,251,190,15,8,226,11,250,158,239,249,190,47,250,184,40,136,162,239,249,158,239,139,162,40,138,190,4,34,0,124,207,247,124,223,23,125,92,20,124,223,247,125,223,247,69,81,21,69,159,36,0,130,175,88,18,34,135,0,64,34,2,130,162,32,128,162,168,228,4,234,138,162,32,10,130,32,34,160,36,216,166,40,138,162,128,136,162,72,137,144,8,82,0,64,81,16,69,65,16,17,80,18,84,81,20,69,65,64,68,81,165,68,136,36,0,2,229,35,62,162,239,227,32,34,226,227,190,239,131,190,15,2,136,171,250,190,32,58,142,238,35,160,35,168,170,232,171,190,143,136,162,138,248,136,16,138,0,124,95,16,125,71,247,17,208,17,84,81,244,85,193,71,68,81,69,124,196,100,0,128,143,210,18,34,135,0,16,34,34,128,32,40,130,34,8,228,4,232,139,162,32,10,130,40,34,160,36,136,178,40,72,18,136,136,148,77,33,132,32,2,0,68,81,16,5,65,20,17,80,18,84,81,20,36,1,68,68,74,165,64,130,36,0,2,229,203,62,162,138,8,10,156,239,251,160,231,131,190,175,8,130,8,136,158,239,249,130,47,250,158,232,139,162,47,184,162,143,248,136,40,34,190,64,2,0,124,207,247,124,193,23,125,79,244,85,209,23,92,193,71,124,196,23,125,159,36,0,0,128,0,0,20,0,8,0,0,0,0,0,0,0,0,128,0,0,240,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,1,3,62,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,49,0,}; +//uint16_t f1_offsets[] = {0,1,3,7,13,19,25,31,33,36,39,45,51,53,57,59,65,71,77,83,89,95,101,107,113,119,125,127,129,133,137,141,147,155,161,167,173,179,185,191,197,203,209,215,221,227,233,239,245,251,257,263,269,275,281,287,293,299,305,311,314,320,323,329,335,338,344,350,356,362,368,374,380,386,392,398,404,410,416,422,428,434,440,446,452,458,464,470,476,482,488,494,498,500,}; +//FontWrap fnt_f1(512,9,f1_data,f1_offsets); + +//static uint8_t f1_data[] = {40,0,4,0,0,130,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,10,158,16,132,130,42,0,0,64,140,56,142,228,49,15,195,0,16,32,48,0,140,195,57,239,113,210,129,36,33,52,113,56,140,195,249,164,72,80,36,242,137,0,2,4,4,0,1,12,16,36,4,1,0,0,0,0,16,0,0,0,0,48,26,0,2,10,69,9,138,130,8,0,0,32,210,64,144,36,8,136,36,1,8,64,72,0,146,36,72,33,8,146,128,36,97,54,137,72,146,36,32,164,72,144,34,2,9,1,2,12,4,0,1,2,16,0,36,1,0,0,0,0,16,0,0,0,0,16,18,0,130,63,133,4,132,130,0,2,0,16,146,64,144,36,8,136,36,1,196,135,64,60,146,36,72,33,8,146,128,20,161,85,137,72,146,36,32,164,72,146,66,129,8,2,2,192,28,199,25,135,113,36,20,253,112,204,193,53,183,148,130,34,233,17,18,0,2,10,14,194,10,130,0,2,30,8,146,48,12,231,56,4,195,73,2,0,33,66,158,35,72,231,105,158,128,12,33,84,137,56,146,195,32,36,133,10,65,65,8,4,2,0,165,32,37,66,146,36,12,37,145,82,34,141,144,148,146,20,9,9,34,0,130,63,20,9,17,130,128,15,0,4,146,8,16,4,73,132,4,1,196,135,16,89,146,36,72,33,72,146,144,20,33,84,137,8,146,4,33,36,133,138,130,32,8,8,2,192,165,32,29,66,146,36,20,37,145,82,34,5,147,148,146,8,201,16,18,0,2,10,148,20,17,130,0,66,0,2,146,8,16,4,73,132,4,65,8,64,0,85,146,36,72,33,72,146,144,36,33,148,137,8,146,4,33,36,133,138,130,16,8,16,2,32,165,32,5,66,146,36,36,37,145,82,34,5,148,164,146,20,41,16,18,0,2,10,79,136,14,130,0,66,64,1,140,120,14,228,48,4,195,72,16,32,16,57,146,195,57,47,48,210,97,36,47,148,113,8,140,228,32,24,130,77,132,240,9,32,226,193,29,199,25,130,147,36,36,37,145,204,193,133,35,71,108,34,238,49,26,0,0,0,4,0,0,130,0,32,0,0,0,0,0,0,0,0,0,32,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,8,0,2,0,0,0,0,0,2,32,0,0,0,64,0,1,0,0,0,0,8,0,0,0,2,0,0,0,0,68,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,124,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,2,0,0,0,0,128,1,16,0,0,0,64,0,1,0,0,0,0,6,0,0,0,}; +//static uint16_t f1_offsets[] = {0,1,3,7,15,22,30,38,41,45,49,55,61,65,70,72,80,86,90,96,102,108,114,120,126,132,138,141,144,150,156,162,168,176,182,188,194,200,205,210,216,222,227,233,239,245,252,259,266,272,278,284,290,297,303,310,318,325,332,339,343,351,355,356,362,365,370,375,380,386,391,397,403,409,412,417,423,426,435,441,446,452,458,463,468,471,476,481,489,495,501,507,512,515,}; +//static FontWrap fnt_f1(528,10,f1_data,f1_offsets); + +//static uint8_t f1_data[] = {42,202,49,130,17,133,0,0,228,144,227,32,158,243,57,14,0,0,224,224,3,225,195,121,124,31,71,40,20,10,130,66,28,15,199,199,243,133,130,33,36,20,228,91,140,0,4,8,0,8,32,32,40,33,0,0,0,0,0,2,0,0,0,0,128,10,42,170,74,65,146,200,1,0,20,89,20,49,66,132,68,17,0,0,16,25,132,34,38,138,4,129,72,40,20,9,198,70,34,145,72,40,68,132,130,81,68,34,2,73,72,1,8,8,0,8,16,32,0,33,0,0,0,0,0,2,0,0,0,0,64,18,170,175,72,65,82,144,16,0,18,21,4,41,65,128,68,81,65,64,0,201,138,34,22,8,5,65,80,40,148,8,198,74,65,81,80,40,64,132,68,82,66,34,130,136,72,1,192,105,28,203,57,171,41,169,158,199,105,172,114,39,138,34,138,162,79,18,2,197,177,128,65,80,17,0,18,17,228,40,207,67,56,17,56,159,131,164,139,226,23,8,125,79,192,47,212,8,170,74,65,81,208,199,64,132,68,82,130,65,129,136,40,2,32,154,162,44,146,108,42,165,165,40,154,178,137,34,138,82,82,34,68,18,2,133,130,70,65,16,124,0,18,17,2,37,80,68,68,30,4,0,68,148,201,38,22,8,5,65,92,40,180,8,170,82,65,79,80,2,67,132,40,138,130,129,64,136,8,0,192,139,130,232,147,40,42,163,164,40,138,162,48,34,82,84,33,20,34,34,130,143,66,41,70,16,16,28,18,17,1,125,80,36,68,16,56,159,67,148,201,39,22,8,5,65,80,168,20,9,170,82,65,65,86,4,68,132,40,138,66,130,64,136,8,0,32,138,130,40,144,40,42,165,164,40,138,162,64,34,82,84,33,20,66,18,128,162,66,41,66,16,16,0,17,145,16,33,81,36,68,17,64,64,0,148,37,40,38,138,4,129,72,168,20,9,146,98,34,129,72,36,68,132,16,4,65,130,32,8,9,0,32,155,162,44,146,44,42,165,164,40,154,178,136,34,35,136,80,8,65,18,130,194,33,198,69,16,0,65,225,208,231,32,142,35,56,78,1,0,64,228,35,232,195,121,124,1,71,40,19,250,146,66,28,1,87,200,67,120,16,4,33,132,240,11,9,0,192,106,28,203,17,43,42,169,164,200,105,172,112,198,34,136,136,136,79,18,0,128,0,0,128,8,0,1,0,0,0,0,0,0,0,0,1,0,0,8,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,8,0,0,0,0,0,0,8,32,0,0,0,8,32,0,0,0,0,0,8,64,18,0,0,0,0,0,5,0,1,0,0,0,0,0,0,0,0,1,0,0,240,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,12,248,1,0,0,0,128,7,16,0,0,0,8,32,0,0,0,0,0,4,128,10,}; +//static uint16_t f1_offsets[] = {0,1,3,7,13,19,29,36,38,42,46,50,56,58,62,64,68,74,78,84,90,96,102,108,114,120,126,128,130,136,142,148,154,165,173,180,187,194,200,206,214,221,223,228,235,241,249,256,264,270,278,285,292,298,305,313,325,332,340,347,350,354,357,363,370,373,379,385,391,397,403,407,413,419,421,424,429,431,439,445,451,457,463,467,473,477,483,489,499,505,511,517,521,523,}; +//static FontWrap fnt_f1(528,10,f1_data,f1_offsets); + +static uint8_t f1_data[] = {0,0,5,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,137,159,16,35,155,10,0,0,144,179,231,65,62,231,115,28,0,1,193,241,113,30,231,249,62,39,58,40,10,66,145,243,56,30,231,139,162,160,40,250,46,224,4,16,32,0,32,128,1,65,10,1,0,0,0,0,64,0,0,0,0,0,204,102,0,170,95,69,137,164,32,7,0,0,72,36,8,98,130,0,138,34,128,0,34,10,138,162,40,10,130,40,18,40,9,102,81,20,69,162,136,136,162,160,40,130,66,128,10,32,32,0,32,64,0,1,8,1,0,0,0,0,64,0,0,0,0,0,34,137,0,2,73,133,132,164,160,138,0,0,68,38,8,82,130,0,137,162,77,0,4,234,138,162,32,10,130,32,18,168,8,90,83,20,69,162,128,136,162,36,37,66,130,128,0,0,231,113,60,231,120,79,74,253,60,206,227,53,239,69,81,82,20,125,34,136,0,2,137,15,130,131,32,130,0,0,66,37,199,73,158,7,113,188,45,124,8,169,250,158,32,122,158,238,19,104,8,66,85,20,69,34,135,136,162,36,66,33,2,129,0,0,40,138,162,72,68,81,42,37,69,81,20,141,64,68,81,146,18,33,33,8,1,130,31,21,73,138,32,224,195,3,193,164,0,250,160,136,136,32,64,0,132,232,139,162,32,10,130,40,18,168,8,66,89,244,68,30,136,136,20,21,133,16,2,130,0,0,47,10,162,79,68,81,26,37,69,81,20,5,71,68,74,18,17,17,66,132,0,0,9,149,84,132,32,128,24,152,64,164,0,66,160,136,136,160,141,124,2,8,136,162,40,10,130,40,146,40,9,66,81,20,100,146,136,136,20,149,136,8,2,132,0,128,40,138,162,64,68,81,42,37,69,81,20,5,72,68,74,146,18,9,130,130,0,2,192,79,136,11,27,128,24,88,128,163,239,65,30,71,112,156,13,1,129,240,137,30,231,249,2,39,58,39,250,66,145,19,120,34,135,112,8,138,136,248,14,232,224,7,239,113,60,79,120,81,74,37,69,206,227,133,135,57,132,77,228,125,12,97,0,0,0,5,0,0,0,0,16,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,2,0,0,64,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,56,0,1,0,0,64,0,1,0,0,0,0,224,0,0,0,0,}; +static uint16_t f1_offsets[] = {0,1,3,7,14,22,30,37,39,43,47,53,59,62,67,70,78,84,87,93,99,105,111,117,123,129,135,138,141,146,152,157,163,171,177,183,189,195,201,207,213,219,223,229,235,241,248,254,260,266,273,279,285,291,297,303,311,317,323,329,333,341,345,349,356,359,365,371,377,383,389,394,400,406,408,411,416,418,426,432,438,444,450,455,461,466,472,478,486,492,498,504,509,517,}; +static FontWrap fnt_f1(528,10,f1_data,f1_offsets); + + + +#include "UIElement.h" +#include "UIStructs.h" +#include "UIPainter.h" + +#undef min +#undef max +#include + +class UI { + + UIPainter p; + UIElement root; + + UIElement* eFocused = nullptr; + UIElement* eDown = nullptr; + + Color cBackground = Color::fromRGB(180,240,180); + +public: + + UI() { + root.setRect(0,0,240,320); + root.setVisible(true); + p.setFG(cBackground); + p.fillRect(root.getRect()); + } + + void add(UIElement* e) { + root.addChild(e); + } + + void onTouch(const uint16_t x, const uint16_t y) { + + if (eDown) { + + const uint16_t x1 = x - eDown->getRect().x; // (x,y) relative to elements top-left + const uint16_t y1 = y - eDown->getRect().y; + eDown->onTouch(x1, y1); + + } else { + + ESP_LOGI("UI", "root_onTouch(%d, %d)", x,y ); + eDown = root._onTouch(x,y); + + } + + + } + + void onTouchDone() { + if (eDown) { + eDown->onTouchUp(); + eDown = nullptr; + } + } + + + void draw() { + //debugMod1("UI", "draw %zu elements", elements.size()); +// for (UIElement* e : elements) { +// e->_draw(p); +// } + root._draw(p); + } + +}; + +#endif // UI_H diff --git a/ext/lcd/ui/UIButton.h b/ext/lcd/ui/UIButton.h new file mode 100644 index 0000000..a70d4a9 --- /dev/null +++ b/ext/lcd/ui/UIButton.h @@ -0,0 +1,97 @@ +#ifndef UI_BUTTON_H +#define UI_BUTTON_H + +#include "UIElement.h" + +class UIButton : public UIElement { + +public: + + class Listener { + public: + virtual void onClick(UIButton*) = 0; + }; + +private: + + bool enabled = true; + + const char* txt; + uint8_t txtH; + uint16_t txtW; + bool down = false; + + const Color fillNormal = Color::fromRGB(180,180,180); + const Color fillDown = Color::fromRGB(120,120,120); + const Color frameBright = Color::fromRGB(230,230,230); + const Color frameDark = Color::fromRGB(50,50,50); + + const Color cText = Color::fromRGB(0,0,0); + const Color cTextDisabled = Color::fromRGB(140,140,140); + + Listener* listener = nullptr; + + static constexpr const char* TAG = "UIButton"; + +public: + + UIButton(const char* txt) : txt(txt) { + txtW = fnt_f1.getWidth(txt); + txtH = fnt_f1.getHeight(); + } + + void setText(const char* txt) { + this->txt = txt; + } + const char* getText() const { + return this->txt; + } + + void setListener(Listener* l) { + this->listener = l; + } + + void setEnabled(const bool en) { + this->enabled = en; + setNeedsRedraw(); + } + + void draw(UIPainter& p) override { + + p.setFG( down ? fillDown : fillNormal ); + p.fillRect(rect); + + p.setFG( down ? frameDark : frameBright ); +// p.drawLine(rect.x, rect.y, rect.x+rect.w, rect.y); // top +// p.drawLine(rect.x, rect.y, rect.x, rect.y+rect.h); // left + p.drawLineHor(rect.x, rect.x+rect.w, rect.y); // top + p.drawLineVer(rect.y, rect.y+rect.h, rect.x); // left; + + p.setFG( down ? frameBright : frameDark ); +// p.drawLine(rect.x, rect.y+rect.h, rect.x+rect.w, rect.y+rect.h); // bottom +// p.drawLine(rect.x+rect.w, rect.y, rect.x+rect.w, rect.y+rect.h); // right + p.drawLineHor(rect.x, rect.x+rect.w, rect.y+rect.h); // bottom + p.drawLineVer(rect.y, rect.y+rect.h, rect.x+rect.w); // right; + + p.setFG( enabled ? cText : cTextDisabled ); + uint16_t o = down ? 1 : 0; + p.drawText(rect.x+rect.w/2-txtW/2+o, rect.y+rect.h/2-txtH/2+o, txt); + + } + + void onTouchDown(const uint16_t, const uint16_t) override { + ESP_LOGI(TAG, "onTouchDown()"); + if (!enabled) {return;} + down = true; + setNeedsRedraw(); + } + void onTouchUp() override { + if (!enabled) {return;} + down = false; + setNeedsRedraw(); + if (listener) {listener->onClick(this);} + } + +}; + +#endif // UI_BUTTON_H diff --git a/ext/lcd/ui/UIElement.h b/ext/lcd/ui/UIElement.h new file mode 100644 index 0000000..19be6cd --- /dev/null +++ b/ext/lcd/ui/UIElement.h @@ -0,0 +1,121 @@ +#ifndef UI_ELEMENT_H +#define UI_ELEMENT_H + +#include "UIStructs.h" +#include "UIPainter.h" + +#undef min +#undef max +#include + +class UIElement { + +protected: + + UIRect rect; + bool _visible = true; + bool _needsRedraw = true; + + std::vector children; + + static constexpr const char* TAG = "UIElement"; + +public: + + void setRect(const UIRect r) { + this->rect = r; + setNeedsRedraw(); + reLayout(); + } + void setRect(const uint16_t x, const uint16_t y, const uint16_t w, const uint16_t h) { + this->rect = UIRect(x,y,w,h); + setNeedsRedraw(); + reLayout(); + } + const UIRect& getRect() const { + return rect; + } + + void setVisible(bool visible) { + this->_visible = visible; + setNeedsRedraw(); + } + bool isVisible() const { + return this->_visible; + } + + void setNeedsRedraw() { + this->_needsRedraw = true; + } + bool needsRedraw() const { + return this->_needsRedraw; + } + + void addChild(UIElement* e) { + children.push_back(e); + } + + + + +protected: + + friend class UI; + + virtual void draw(UIPainter& p) {;} + + /** layout needs updating */ + virtual void reLayout() {;} + + virtual void onTouchDown(uint16_t, uint16_t) {;} // relative (x,y) coordinate + virtual void onTouch(uint16_t, uint16_t) {;} // relative (x,y) coordinate + virtual void onTouchUp() {;} + +protected: + + UIElement* _onTouch(const uint16_t x, const uint16_t y) { + + + // if i'm invisible, ignore for me and children + if (!isVisible()) {return nullptr;} + + // touch outside of me, ignore for me and children + if (!getRect().contains(x,y)) {return nullptr;} + + // find first child that takes the event + for (UIElement* e : children) { + UIElement* taken = e->_onTouch(x, y); + if (taken) {return taken;} + } + + // take the event myself + const uint16_t x1 = x - getRect().x; // (x,y) relative to elements top-left + const uint16_t y1 = y - getRect().y; + debugMod2(TAG, "onTouchDown(%d,%d)", x1, y1); + onTouchDown(x1, y1); + return this; + + } + + void _draw(UIPainter& p) { + + // if hidde, ignore for me and children + if (!_visible) {return;} + + // draw myself (if needed) + if (_needsRedraw) { + draw(p); + _needsRedraw = false; + } + + // call for children as well + for (UIElement* child : children) { + child->_draw(p); + } + + } + + +}; + +#endif // UI_ELEMENT_H diff --git a/ext/lcd/ui/UILabel.h b/ext/lcd/ui/UILabel.h new file mode 100644 index 0000000..d19915b --- /dev/null +++ b/ext/lcd/ui/UILabel.h @@ -0,0 +1,45 @@ +#ifndef UI_LABEL_H +#define UI_LABEL_H + +#include "UIElement.h" +#include "UIStructs.h" + +#undef min +#undef max +#include + +class UILabel : public UIElement { + + std::string txt; + + Color cBackground = Color::fromRGB(255,255,255); + Color cText = Color::fromRGB(0,0,0); + +public: + + UILabel() { + ; + } + + void setText(const std::string& txt) { + this->txt = txt; + setNeedsRedraw(); + } + + void draw(UIPainter& p) { + + p.setFG(cBackground); + p.fillRect(rect); + + //const uint16_t txtW = fnt_f1.getWidth(txt); + //const uint16_t txtH = fnt_f1.getHeight(); + + p.setFG(cText); + p.drawText(rect.x, rect.y, txt.c_str()); + + } + + +}; + +#endif // UI_LABEL_H diff --git a/ext/lcd/ui/UIList.h b/ext/lcd/ui/UIList.h new file mode 100644 index 0000000..83fa08d --- /dev/null +++ b/ext/lcd/ui/UIList.h @@ -0,0 +1,208 @@ +#ifndef UI_LIST_H +#define UI_LIST_H + +#undef min +#undef max +#include +#include + +#include "UIElement.h" +#include "UIButton.h" + +class UIListModel { + + std::vector entries; + +public: + + void add(const std::string& str) { + entries.push_back(str); + } + + void remove(const size_t idx) { + entries.erase(entries.begin()+idx); + } + + size_t size() const { + return entries.size(); + } + + const std::string& get(const size_t idx) const { + return entries[idx]; + } + +}; + + + + +class UIList : public UIElement, public UIButton::Listener { + +public: + + class Listener { + public: + virtual void onSelected(UIList* lst, int idx) = 0; + }; + +private: + + UIListModel model; + int offset = 0; + int selectedIndex = -1; + + UIButton btnUp; + UIButton btnDown; + + static constexpr const int btnW = 32; + + Color cRect = Color::fromRGB(0,0,0); + Color cNormalBG = Color::fromRGB(230,230,230); + Color cSelectedBG = Color::fromRGB(190,190,255); + Color cText = Color::fromRGB(0,0,0); + + Listener* listener = nullptr; + + static constexpr const char* TAG = "UIList"; + +public: + + /** ctor */ + UIList() : btnUp("<"), btnDown(">") { + addChild(&btnUp); + addChild(&btnDown); + btnUp.setListener(this); + btnDown.setListener(this); + } + + void setListener(Listener* l) { + this->listener = l; + } + +// UIListModel& getModel() { +// setNeedsRedraw(); +// return model; +// } + +// const UIListModel& getModel() const { +// return model; +// } + + void add(const std::string& str) { + model.add(str); + btnUp.setVisible(needsScroll()); + btnDown.setVisible(needsScroll()); + setNeedsRedraw(); + } + + void remove(const size_t idx) { + model.remove(idx); + if (idx == selectedIndex) { + selectedIndex = -1; + if (listener) {listener->onSelected(this, selectedIndex);} + } else if (idx < selectedIndex) { + --selectedIndex; + if (listener) {listener->onSelected(this, selectedIndex);} + } + if (offset > maxOffset()) {offset = maxOffset();} + btnUp.setVisible(needsScroll()); + btnDown.setVisible(needsScroll()); + setNeedsRedraw(); + } + + size_t size() const { + return model.size(); + } + + const std::string& get(const size_t idx) const { + return model.get(idx); + } + + int getSelectedIndex() const { + return this->selectedIndex; + } + + void reLayout() override { + int h = rect.h / 2 - 1; + btnUp.setRect(rect.x+rect.w-btnW-1, rect.y+1, btnW, h); + btnDown.setRect(rect.x+rect.w-btnW-1, rect.y+h+2, btnW, h); + } + + virtual void draw(UIPainter& p) override { + + debugMod(TAG, "draw()"); + debugMod4(TAG, "rect: %d %d %d %d", rect.x, rect.y, rect.w, rect.h); + + const uint8_t oy = (elementHeight() - fnt_f1.getHeight()) / 2; + const uint16_t entryW = rect.w - (needsScroll() ? btnW : 0); + + // outline rectangle + p.setFG(cNormalBG); + p.fillRect(rect.x+1, rect.y+1, entryW, rect.h-2); + p.setFG(cRect); + p.drawRect(rect); + + // display as many elements as fit within the list's height + for (unsigned int i = 0; i < elementsVisible(); ++i) { + + // determine position (y-coordinate) + const uint16_t y = i * elementHeight(); + + // determine element from model + const int idx = i+offset; + const bool selected = idx == selectedIndex; + + // draw background depending on selection + //p.setFG( selected ? cSelectedBG : cNormalBG ); + //p.fillRect(UIRect(rect.x+1, rect.y+y+1, entryW-1, elementHeight()-1)); + if (selected) { + p.setFG( cSelectedBG ); + p.fillRect(UIRect(rect.x+1, rect.y+y+1, entryW-1, elementHeight()-1)); + } + + // draw text + p.setFG(cText); + p.drawText(rect.x+3, rect.y + y + oy, model.get(idx).c_str()); + + } + + + + } + + virtual void onTouchDown(uint16_t, uint16_t y) override { + unsigned int idx = y / elementHeight() + offset; + this->selectedIndex = (idx < numElements()) ? (idx) : (-1); + debugMod1(TAG, "selected: %d", selectedIndex); + setNeedsRedraw(); + if (listener) {listener->onSelected(this, selectedIndex);} + } + + virtual void onTouch(uint16_t, uint16_t) override { + + } + + virtual void onTouchUp() override { + + } + + virtual void onClick(UIButton* btn) override { + if (btn == &btnUp) { + if (offset > 0) {--offset; setNeedsRedraw();} + } else if (btn == &btnDown) { + if (offset < maxOffset()) {++offset; setNeedsRedraw();} + } + } + +private: + + unsigned int elementHeight() const {return 16;} + unsigned int maxElementsVisible() const {return rect.h / elementHeight();} + unsigned int elementsVisible() const {return std::min(maxElementsVisible(), numElements());} + unsigned int numElements() const {return model.size();} + unsigned int maxOffset() const {return numElements()-maxElementsVisible();} + bool needsScroll() const {return numElements() > maxElementsVisible();} + +}; + +#endif // UI_LIST_H diff --git a/ext/lcd/ui/UIPainter.h b/ext/lcd/ui/UIPainter.h new file mode 100644 index 0000000..689b788 --- /dev/null +++ b/ext/lcd/ui/UIPainter.h @@ -0,0 +1,52 @@ +#ifndef UI_PAINTER_H +#define UI_PAINTER_H + +#include "../../../Debug.h" +#include "../Draw.h" +#include "UIStructs.h" + +class Setter; +class FontWrap; + +extern FontWrap fnt_f1; +extern Setter setter; + +class UIPainter { + +public: + + void drawLine(const uint16_t x1, const uint16_t y1, const uint16_t x2, const uint16_t y2) { + debugMod("UI", "draw line"); + drawer.drawLine(x1,y1, x2,y2); + } + + void drawLineHor(const uint16_t x1, const uint16_t x2, const uint16_t y) { + drawer.drawLineHor(x1,x2,y); + } + void drawLineVer(const uint16_t y1, const uint16_t y2, const uint16_t x) { + drawer.drawLineVer(y1,y2,x); + } + + void drawRect(UIRect r) { + drawer.drawRect(r.x, r.y, r.w, r.h); + } + + void drawText(const uint16_t x, const uint16_t y, const char* str) { + fnt_f1.draw(str, x, y, setter); + } + + void setFG(const Color fg) { + drawer.setColor(fg); + } + + void fillRect(const UIRect rect) { + drawer.fillRect(rect.x, rect.y, rect.w, rect.h); + } + void fillRect(const uint16_t x, const uint16_t y, const uint16_t w, const uint16_t h) { + drawer.fillRect(x,y,w,h); + } + +}; + +#endif // UI_PAINTER_H + diff --git a/ext/lcd/ui/UIStructs.h b/ext/lcd/ui/UIStructs.h new file mode 100644 index 0000000..af876b8 --- /dev/null +++ b/ext/lcd/ui/UIStructs.h @@ -0,0 +1,39 @@ +#ifndef UI_STRUCTS_H +#define UI_STRUCTS_H + +#include + +struct UIPoint { + + uint16_t x; + uint16_t y; + + UIPoint() : x(0), y(0) {;} + + UIPoint(uint16_t x, uint16_t y) : x(x), y(y) {;} + +}; + +struct UIRect { + + uint16_t x; + uint16_t y; + + uint16_t w; + uint16_t h; + + UIRect() : x(0), y(0), w(0), h(0) {;} + + UIRect(const uint16_t x, const uint16_t y, const uint16_t w, const uint16_t h) : x(x), y(y), w(w), h(h) {;} + + bool contains(const uint16_t x, const uint16_t y) const { + return + (this->x <= x) && + (this->y <= y) && + (x <= this->x+this->w) && + (y <= this->y+this->h); + } + +}; + +#endif // UI_STRUCTS_H diff --git a/ext/lcd/ui/ui.tar.gz b/ext/lcd/ui/ui.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..6cfb2e9eef379977b980d300cafe8d902b787066 GIT binary patch literal 6892 zcmV1!@y9CJfEt;go zi=qe=OOAy)QXt8ReMSEFcb=J9a!E=4Xd4G_PxKeH+^^Z4k7s6<$#=uEC*$F0Jiqty z>HhIwKFO73nRhOszoslQ{LPGf*30|ER1{&)ZA z=w$e2@zwZjIi4I-~lYrjlMvq(XNqiK{p?UFuGV~fWg z6OxHg&nC?sk4uAc>)bc<)M9*O(yKlNI?<+{k0=P;Vl>nb1Q?5f_(M!VZsQLQGX@E@ zk@2`^Ogqn&2IDK4mB)zfBP zq669@+HG$@NoCTapt3x*^ux~!xu<#3!;06z*g}bu?eQKTF)Lrm2nG|vkUqseS{fte z8COh`=VimLUO*Ys17iF}+A7Q2f(o+mG({@##Bmv|UX42%sLb)*Jfm-F5)l25vaUGWi>IQGr|s#%Gg96j2>s z!iFH3A60h+3lI}TzdHn#%$iIPi%W&s(|75GBIW_Fo!hWKIb@KhkhAWG}OLk>8f)=lYw1mHVtkk9$Ajj7?u(O#t; z*%pPzy;;^%>zJ)-#3=8m3TH(EJ930i%AygsRZ?@q=9re*bZ=^?m{eVhP?%C%+!|UR@jB4iwrA~OI%LM zfi{;Ui6%?ZT#>%EX$=)mLt%-CaiAbYaIj>#5Cskde-u_Ng%_g;d%#8|<^(cBXkLrEIepar7~L0jaqrB%iD*s;c9?3hE5#dgYy-iG;vXFhRjep?L{~M3bvrN6ABDiM{e{h1pp6X4^!ix6r=S ziD740>q4pULO0u{sIJs~tC^Of67UHaQ3zRuK!`FESW;>SE^p`ptzWDC>YvN4Iz-hh7+@uH5?7}YqHL+89}&$`-6SW1cHPuvkhytTjsK8 z%VZ5I#uHXxnHCa3qy-fx<(36IY(dIS61Z<=yU-N%#3UQpI<@&>w``V&?GpHE)Z+_+ z<<)r$y9JhQ(1-5|S>6g+K(9fXN-;_phP{Ud1?FO^g5TDYF>Hs*SXqZX0(hrFzyR5; zut{38k+D{#vPN6guFd0X>ok8=<%$NhHrZ9d0(|5Y4wPY#?%^$vS){EX)kt=+5a?yV z5o8w0DuT5$S_Jjcjj3f77~#|rU;x4_S*;q6orMqp#Zw*TeC%+^9KkCL!6c={>Nd}H zm&ISPG!aV%+QOA85iFpxipsEEWaRS<1P}%A{uwY3DbNihEjc@!R9--$p*rCEQ5oDD z;^4dj&G3>S3Z$!nfQio?0T+kGgty8dipmK5IAS$N?BhysAC-|ha}^ey%BT+jhTi}b z7R%vsa+s{e3v90g9%pEGbwhj6SCwARXp0^xDp7SdI3}5>b2!+AR}Y?<&=ibjMPFf+ za0^`}5SO*GQSD|~hG^dlLFC+%RApJG0Z2<#Vr-n46OK^u!ayc@6!tJ%Veri@V5bi> z5F6EvM4CVRAcwrNrBo~IAAc1 zI%_La)~d;z2jN#6_lTf)}P>=|ydp94B6{WljxSMCDc8-6CCijApZl7+o1v*P?D4 z$GhS!VwY8@p-pS4K|e7yg=lD{1-xJyp3|+V)Yi+^com^FwD3Z5=n1T}fe;aKVTOw+ zG1|X?7ontx2*G!skc#t~@M02Y%RLV6)ZAPsZEOAuEUv7A0rM^%Y6>@N`o2Bs+XdTp;ze)uTeh|RWyT5M>9-ju!9tCC>`f&Uv+a$n>BaB|m;>qt ztBODX))syshi1x5Z4RO&S<0~7@{e2N4qbOUx+1$3#&Q!x0i-oPriL-h>q#WEu7UCLm-vzNFZnwUWSS z%Z`!@?jO4YE*Htw3XXqhiLIa#4)3AFK)Re{5>$!%RP;870U@O>SM`*sva&K}EgA%3 zSTVomA>NtC_`b)(63#6-j_V6mS;EWIZ&kAb6TK~gbW^4Z2UuUQ%F_s|W4l~hr2zA- zz_@Iy3Vpy{v!I5GT{OlbrKo1M8JXzYTutk`se6gtm*SLBOV8SuS)L;d(2Brg64ta; z@M;SYRW>e?{jo4qVtXYNL7cc^&2u!W8U|u+2o{QYOw5<5ib{KidY$IeLK;^1INu0?$(ti|16b z+`@WT$%EOcGIic_%_LT8{tbAo-LhKZ4OONzRm=%QEs{f69@CBZgB&K)GoLl#^jyO# z5>ID}a$diNhe`WYSV#ut=>WXm|(2Tky=;x?8?%iK@GN$nh@0 zZhq94h44qDtAncMkVO_86NUwd29OT*nr2a08K9Rj zOqZw&AJhd5UM_Trhsj#F(H52`p1U?M2DMYGOQv2p{#G3z(x`fcFxT_e`}M09dsSV4 zSE)vBONtBTfmJB4H5072U~S7d?aL%NZ&J zobncXab&U7AQQv^cs^I$)z3@?+=Z+Z{U!P(GX)u=dlHNF*(o?5c`rjna5Y1?j_?<3 z67^Hq{I}z;i_lkKu`VpQ*)NU!Xucj=2YO1`6S@jv+gw101_ewZ5i=uxmy?r0dPoQx zEk`7ZO3!n!mnk_}Am|W}t`*R;iih?AYO|bc4!B8k9;^U6u1W0iuxFQXyn&db<^(;H zZ;V{wxL2jt7;Po zxog62WdOz!E#l0uo(%AOHl+bJnNrDV?d+ng5{w4`5oEo}V;Z82lDNuhikrB)9{UPO z1j~*;=?+Ate0$QU5Kl`IWtEK%5gE-@2$QKkG41gUUNJnY`B()YriQ^00xsA}Rcuc} zS|XPNN0Olc=;Ks7qX^C|@cAD}>_oJAHurMqgZaMQ?=qaNaT2Slb)$_O{aJJFhTUeY zEoTMw%tbB-tvDtop$0+en6}RverebN^*1GznjP~T#zNjiaAb272ClAficj0MMC>FB zC|o9PeTS$*vty-ekKXw9fEA!kErGEncgbf3dDcP^W2ts3eD4HL*tBJ+t{6Elas+~8 zbu%fAC^D{A1ZZ_aM%;u5>#g0XLiezA?HL^5G*PX9DIs7*4az`P6=0&WZ-FI7Xb4aR z05@mgTMOiYGterzpyP7kOjUB6yU7~<6F)@{tvZf|2;wEnEbx*o3{s#^b5_-44?ENm18 zBWrzSxa2~>s^m-^-~eap*o@)iTBbmWkPZoTaDNTB1dNocds##=D?QbN9fJ!7j)EhM zuze*5lHf0Ko|Mxywc@2Z!b|;AJ@g!=m@$)yVpd4fl~IBYCwXw(uh0`cFzU6&tf(A$ zXqrb(^T?54%OIYRO~mVrB3BYn6Va%)=2SI0yZ6KRa5{>Yhxe(Q_p`|;8IDGSxc(kZXJ;pjw*T&Q zas2A_@bH(xm==zA8CNaSZ8}QnF5w4SMvVRR z=-cslG#(A!?X~m%2)#6(j);ERF@8(rbHBf`y?)>IUG)CvqtoSbHvQw@2fNPupN`-E zDzfeSpEq;$-~X&LvtLwI>1ywa?faj9`s>q4eS2LZ|9Mfkp8V(J5N_Z9yooD%|MSty z7cc(j+wNFdA6hqAr4KSp6EwLiuCzM`uut} zJ4r@@%#MZxtq+=BzMh>BoI0AEoP0Z*zonUcB}Mn|AI)cPzyH^xf#d`D2e_!;OaWiN zk=64;cD2!b_;&ngK6!JzTy1AL6vIDj+kG>f|FYT&c@TWF_YfBP%j6_1`FbZTEc2kZ zo_n;T^k=}gz>io(tQ>o~`-HfEoQ#&o{QN*3Kj}PvG6r#jj@L$B?hCRct7Gv1$CJg~ zhxC!Ukq!OW-Q_phdQW{kG@6f>r}JsjwC#-+mSGUBM?6}zI45yaM(B-Cd^>r!m(1Re z=kv)(9kO@()cZgGH4ujSd&>m(V>aTIdAE-e_D|;YOPsrv_RYTD@9@SRaxTHm-fAL!R&TmT%pJ zuC-BoJHy(^be|JRuHKX{C*`+wGWXW{?Z%x(R@o47juUs30#^82N+ zMO9?E+4_Hf`s>q4eS2LZ|28XKPyTb8yRH9s6IbN_J%0B1-;ck2(RBdZ!s{lMf@2uF zf%Dmt;J|n!9tW^6!-U|Ky?QSxh}NV_@w7F_&7*z-X`!)y!3&}D802FlYCgXNDReu! zD5bPg@?oXqc+CW4Zpe3}AJXH!&C`iVxt%o0ja9ct*D&^YLNbRJP?t5HOSIH_Cu4H` zI2W|R$9fgFYKQGm5f8U+3!Cw#omQJhjB53Lv?g28_0>YI=C7~X6_7rG)$~!+ZTB%x zN75g?=?Aa~mWg8)YurVSS?u;h8!g;zfo=2Nt--`&Qp zenVdjld0|Q8aUf(gwL(Jw%6~!V*LMX_V~ z`@Amw_TT^f^IxA%>f7rQ`8TF0dh!nj+~WV6xFY=j>>rOFKkK6ZP;||+W)1dd-rA58 z>St~`iHRLaa~wVz9{%!XK0BR`)}%)Yyz|OG0BPC+QOVW164~r!Ja~2rQ-4Q#Vm*4_ zoIz;Lq5V}lZ6Ie9n2iYEoIJR9ujg%ClABpqe^at1o(mRZZ*AN+M0qzx0wSqc1CRC@58_6_XMJU9{#~%IiF15JiPqa z5V-H(vk{?0z@o)+bpO8e_JHrTpM;vtC*#HX_XY&jMD=ge2Mzsd!{`M9JW_ZR?XBtICo2E>pvex$v~L2(@yfi#Z)#4kEs+;V^e7Yx z7UL5RsXwKCIivEsCW6m${^ZfWEss&&NY{bdJNkAEuh97H-8_i_^^1RC zj9WtItv)gjv~$>-?-Zx6OV8_Khh_TMkGEcWil>FeI5##sr-=>pTzo*p$=x+Dc`vz_ zSjNhH4F7bxn7o;eM+x?2&0+i|s2(53m$A9U2>Um=BU^mXKI9mA*k@Eh#jmkZnv6l>0sSiyf^2x-z$q5T*AH} z4$$TS`Lu6Ut`}gKzjHZG)eY{-ODOHDA0!fKax3(6Kl$6}R>~^nA}8~znSc(*Z)({; z9>X=G!$*YW=fYxw++*}ngABiG9NAGY3i^xGGYzwf$$ZQ+Ig&(|gYG1$87 zK=?1gNo!1VZoaUFRuT(;v+VPw^v?AUOm&ZT6AVtYuWBwyPcPI zPiUlDg6#O6{`TxRjfeiJL+YTPBnaohFQ4PO3F&;g1EM=>kMZIIPVwRK%gH}q9qnxG zXy}gGQ~k3Q;v%OZoXLt>3vOpef)BFu`S<(fbndtG||!`Ytv z=Xtipe>ZYP`0x3P?_YlX;&~VUwT0{cKiJ&`GY`<$Y@(6t)-=a2-=51q7{t{E2!EXo zvOO}u^ji*^HVBji`ECC7ovZvKNBtxQLaYM`P@W|Zz5;f;eLE^VN8x!>$aS>(9r^pA md3fGFJlBUCK}yRvpPR$C*Y?_8+iQERyZ#S?W{b)Iga81MW~;pb literal 0 HcmV?d00001 diff --git a/ext/led/WS2812B.h b/ext/led/WS2812B.h index fb299d6..6028650 100644 --- a/ext/led/WS2812B.h +++ b/ext/led/WS2812B.h @@ -4,6 +4,7 @@ #include "../../data/Color.h" #include "../../Platforms.h" +#include "../../Debug.h" @@ -14,9 +15,11 @@ template class WS2812B { - #define LED_SET_PIN_TO_OUTPUT GPIO5_OUTPUT_SET - #define LED_SET_PIN_H GPIO5_H - #define LED_SET_PIN_L GPIO5_L + static constexpr const char* NAME = "WS2812B"; + +// #define LED_SET_PIN_TO_OUTPUT GPIO5_OUTPUT_SET +// #define LED_SET_PIN_H GPIO5_H +// #define LED_SET_PIN_L GPIO5_L //#define NS_PER_TICK ( (1000ul*1000ul*1000ul) / (80ul*1000ul*1000ul) ) @@ -35,6 +38,7 @@ void init() { LED_SET_PIN_TO_OUTPUT; + debugMod1(NAME, "init with %d leds", numLEDs); } /** set the color for the given LED */ @@ -78,7 +82,7 @@ ets_intr_lock(); // process each LED - for (uint8_t i = 0; i < numLEDs; ++i) { + for (int i = 0; i < numLEDs; ++i) { // send each LEDs 24-bit GRB data if (enabled[i]) { @@ -100,6 +104,36 @@ } + /** flush configured changes, including global brightness */ + void flushBrightness(const uint8_t brightness) { + + LED_SET_PIN_TO_OUTPUT; + + ets_intr_lock(); + + // process each LED + for (int i = 0; i < numLEDs; ++i) { + + // send each LEDs 24-bit GRB data + if (enabled[i]) { + const Color rgb = colors[i].brightness(brightness); + sendByte(rgb.g); + sendByte(rgb.r); + sendByte(rgb.b); + } else { + sendByte(0); + sendByte(0); + sendByte(0); + } + + } + + ets_intr_unlock(); + + reset(); + + } + private: inline void sendByte(uint8_t b) { @@ -212,7 +246,7 @@ }; -#elif ESP32aaa +#elif false// ESP32aaa //#include #include @@ -419,7 +453,7 @@ }; -#elif ESP32 +#elif false //ESP32 #include #include @@ -612,7 +646,7 @@ }; -#elif othertest +#elif false //othertest //#include #include diff --git a/ext/sens/BME280.h b/ext/sens/BME280.h new file mode 100644 index 0000000..e7a09d3 --- /dev/null +++ b/ext/sens/BME280.h @@ -0,0 +1,277 @@ +#ifndef SENS_BME280 +#define SENS_BME280 + +#include "../../io/SoftI2C.h" + +class BME280 { + + static constexpr const char* NAME = "BME280"; + + static constexpr uint8_t ADDR7 = 0b1110110; + + static constexpr uint8_t REG_CTRL1 = 0xF2; // humidity + static constexpr uint8_t REG_CTRL2 = 0xF4; // temp, pressure, mode + + static constexpr uint8_t REG_STATUS = 0xF3; + static constexpr uint8_t REG_PRESSURE = 0xF7; + static constexpr uint8_t REG_TEMPERATURE= 0xFA; + static constexpr uint8_t REG_HUMIDITY = 0xFD; + + static constexpr uint8_t REG_DIG_T1 = 0x88; + static constexpr uint8_t REG_DIG_T2 = 0x8A; + static constexpr uint8_t REG_DIG_T3 = 0x8C; + + +public: + + bool started = false; + + /** internal sensor calibration values */ + struct Calibration { + + uint16_t dig_T1 = 0; + int16_t dig_T2 = 0; + int16_t dig_T3 = 0; + + uint16_t dig_P1 = 0; + int16_t dig_P2 = 0; + int16_t dig_P3 = 0; + int16_t dig_P4 = 0; + int16_t dig_P5 = 0; + int16_t dig_P6 = 0; + int16_t dig_P7 = 0; + int16_t dig_P8 = 0; + int16_t dig_P9 = 0; + + uint8_t dig_H1 = 0; + int16_t dig_H2 = 0; + uint8_t dig_H3 = 0; + int16_t dig_H4 = 0; + int16_t dig_H5 = 0; + int8_t dig_H6 = 0; + + } cal; + + + + + + bool isPresent() { + return i2c::query(ADDR7); + } + +private: + + void readCalib() { + + debugMod(NAME, "readCalib()"); + + // read all 24 calibration bytes for temperature and pressure + uint8_t b1[24]; + readRegister(REG_DIG_T1, b1, 24); + + cal.dig_T1 = (b1[1] << 8) | b1[0]; + cal.dig_T2 = (b1[3] << 8) | b1[2]; + cal.dig_T3 = (b1[5] << 8) | b1[4]; + + cal.dig_P1 = (b1[7] << 8) | b1[6]; + cal.dig_P2 = (b1[9] << 8) | b1[8]; + cal.dig_P3 = (b1[11] << 8) | b1[10]; + cal.dig_P4 = (b1[13] << 8) | b1[12]; + cal.dig_P5 = (b1[15] << 8) | b1[14]; + cal.dig_P6 = (b1[17] << 8) | b1[16]; + cal.dig_P7 = (b1[19] << 8) | b1[18]; + cal.dig_P8 = (b1[21] << 8) | b1[20]; + cal.dig_P9 = (b1[23] << 8) | b1[22]; + + // humidity + readRegister(0xA1, &cal.dig_H1, 1); + readRegister(0xE1, b1, 7); + cal.dig_H2 = (b1[1] << 8) | b1[0]; + cal.dig_H3 = b1[3]; + cal.dig_H4 = (b1[3] << 4) | (b1[4] & 0b000001111); + cal.dig_H5 = (b1[5] << 4) | ((b1[4] & 0b111100000) >> 4); + cal.dig_H6 = (b1[6]); + + //os_printf("calib temp: %d %d %d\n", cal.dig_T1, cal.dig_T2, cal.dig_T3); + //os_printf("calib pres: %d %d %d %d %d %d %d %d %d\n", cal.dig_P1, cal.dig_P2, cal.dig_P3, cal.dig_P4, cal.dig_P5, cal.dig_P6, cal.dig_P7, cal.dig_P8, cal.dig_P9); + //os_printf("calib humi: %d %d %d %d %d %d\n", cal.dig_H1, cal.dig_H2, cal.dig_H3, cal.dig_H4, cal.dig_H5, cal.dig_H6); + debugMod3(NAME, "calTemp: %d %d %d", cal.dig_T1, cal.dig_T2, cal.dig_T3); + debugMod9(NAME, "calPres: %d %d %d %d %d %d %d %d %d", cal.dig_P1, cal.dig_P2, cal.dig_P3, cal.dig_P4, cal.dig_P5, cal.dig_P6, cal.dig_P7, cal.dig_P8, cal.dig_P9); + debugMod6(NAME, "calHumi: %d %d %d %d %d %d", cal.dig_H1, cal.dig_H2, cal.dig_H3, cal.dig_H4, cal.dig_H5, cal.dig_H6); + + } + + void start() { + debugMod(NAME, "start()"); + const uint8_t cfgHumi = 0b101; // 16x oversampling + const uint8_t cfgPres = 0b101; // 16x oversampling + const uint8_t cfgTemp = 0b101; // 16x oversampling + const uint8_t cfgMode = 0b11; + const uint8_t cfg1 = (cfgHumi << 1); + const uint8_t cfg2 = (cfgTemp << 5) | (cfgPres << 2) | (cfgMode << 0); + writeRegister(REG_CTRL1, &cfg1, 1); + writeRegister(REG_CTRL2, &cfg2, 1); + } + + + +public: + + void startOnce() { + if (started) {return;} + debugMod(NAME, "startOnce()"); + readCalib(); + start(); + started = true; + } + + uint8_t getStatus() { + uint8_t res[1]; + readRegister(REG_STATUS, res, 1); + //os_printf("Status: %d \n", res[0]); + return 0; + } + + /** get current pressure in hPa */ + float getPressure() { + uint8_t res[3]; + readRegister(REG_PRESSURE, res, 3); + //os_printf("res: %d - %d - %d \n", res[0], res[1], res[2]); + const uint32_t tmp = ((res[0] << 16) | (res[1] << 8) | (res[2] << 0)) >> 4; + const uint32_t pres = BME280_compensate_P_int64(tmp); + const float presF = pres / 256.0f / 100.0f; // convert from Q24.8 to float and from Pa to hPa + const uint32_t p0 = pres / 256; + const uint32_t p1 = (uint32_t) presF; + const uint32_t p2 = (presF - p1) * 100000; + debugMod4(NAME, "[pres] ADC: %d -> %d Pa | %d.%d hPa", tmp, p0, p1,p2); + return presF; + } + + float getTemperature() { + uint8_t res[3]; + readRegister(REG_TEMPERATURE, res, 3); + //os_printf("res: %d - %d - %d \n", res[0], res[1], res[2]); + const uint32_t tmp = ((res[0] << 16) | (res[1] << 8) | (res[2] << 0)) >> 4; + const int32_t temp = BME280_compensate_T_int32(tmp); + const float tempF = temp / 100.0f; + debugMod2(NAME, "[temp] ADC: %d -> %d", tmp, temp); + return tempF; + } + + float getHumidity() { + uint8_t res[2]; + readRegister(REG_HUMIDITY, res, 2); + //os_printf("res: %d - %d \n", res[0], res[1]); + const uint32_t tmp = (res[0] << 8) | (res[1] << 0); + const int32_t humi = bme280_compensate_H_int32(tmp); + const float humiF = humi / 1024.0f; + const uint16_t h0 = humi / 1024; + const uint16_t h1 = (uint16_t) humiF; + const uint16_t h2 = (humiF - humi) * 10000; + debugMod4(NAME, "[humi] ADC: %d -> %d -> %d.%d %%", tmp, h0, h1,h2); + return humiF; + } + + bool readRegister(const uint8_t addr, uint8_t* dst, const uint8_t len) { + + bool ok; + + // address the slave in write mode and select the first register to read + ok = i2c::startWrite(ADDR7); + if (!ok) {os_printf("failed start write\n"); return false;} + ok = i2c::writeByteAndCheck(addr); + if (!ok) {os_printf("failed to select register %d\n", addr); return false;} + + //i2c::stop(); + + // address the slave in read mode and read [len] registers + ok = i2c::startRead(ADDR7); + if (!ok) {os_printf("failed start read\n"); return 0;} + i2c::readBytes(dst, len); + + // done + i2c::stop(); + return true; + + } + + bool writeRegister(const uint8_t addr, const uint8_t* src, const uint8_t len) { + + bool ok; + + // address the slave in write mode and select the first register to read + ok = i2c::startWrite(ADDR7); + if (!ok) {os_printf("failed start write\n"); return false;} + ok = i2c::writeByteAndCheck(addr); + if (!ok) {os_printf("failed to select register %d\n", addr); return false;} + ok = i2c::writeBytesAndCheck(src, len); + if (!ok) {os_printf("failed to write register contents \n"); return false;} + + // done + i2c::stop(); + return true; + + } + + + +private: + + /** conversions from ADC values to real-world values. from Bosch BMP280 manual! */ + + using BME280_S32_t = int32_t; + using BME280_U32_t = uint32_t; + using BME280_S64_t = int64_t; + BME280_S32_t t_fine = 0; + + // Returns temperature in DegC, resolution is 0.01 DegC. Output value of “5123” equals 51.23 DegC. + // t_fine carries fine temperature as global value + BME280_S32_t BME280_compensate_T_int32(BME280_S32_t adc_T) { + BME280_S32_t var1, var2, T; + var1 = ((((adc_T>>3) - ((BME280_S32_t)cal.dig_T1<<1))) * ((BME280_S32_t)cal.dig_T2)) >> 11; + var2 = (((((adc_T>>4) - ((BME280_S32_t)cal.dig_T1)) * ((adc_T>>4) - ((BME280_S32_t)cal.dig_T1))) >> 12) * ((BME280_S32_t)cal.dig_T3)) >> 14; + t_fine = var1 + var2; + T = (t_fine * 5 + 128) >> 8; + return T; + } + + // Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format (24 integer bits and 8 fractional bits). + // Output value of “24674867” represents 24674867/256 = 96386.2 Pa = 963.862 hPa + BME280_U32_t BME280_compensate_P_int64(BME280_S32_t adc_P) { + BME280_S64_t var1, var2, p; + var1 = ((BME280_S64_t)t_fine) - 128000; + var2 = var1 * var1 * (BME280_S64_t)cal.dig_P6; + var2 = var2 + ((var1*(BME280_S64_t)cal.dig_P5)<<17); + var2 = var2 + (((BME280_S64_t)cal.dig_P4)<<35); + var1 = ((var1 * var1 * (BME280_S64_t)cal.dig_P3)>>8) + ((var1 * (BME280_S64_t)cal.dig_P2)<<12); + var1 = (((((BME280_S64_t)1)<<47)+var1))*((BME280_S64_t)cal.dig_P1)>>33; + if (var1 == 0) {return 0;} // avoid exception caused by division by zero + p = 1048576-adc_P; + p = (((p<<31)-var2)*3125)/var1; + var1 = (((BME280_S64_t)cal.dig_P9) * (p>>13) * (p>>13)) >> 25; + var2 = (((BME280_S64_t)cal.dig_P8) * p) >> 19; + p = ((p + var1 + var2) >> 8) + (((BME280_S64_t)cal.dig_P7)<<4); + return (BME280_U32_t)p; + } + + // Returns humidity in %RH as unsigned 32 bit integer in Q22.10 format (22 integer and 10 fractional bits). + // Output value of “47445” represents 47445/1024 = 46.333 %RH + BME280_U32_t bme280_compensate_H_int32(BME280_S32_t adc_H) { + BME280_S32_t v_x1_u32r; + v_x1_u32r = (t_fine - ((BME280_S32_t)76800)); + v_x1_u32r = (((((adc_H << 14) - (((BME280_S32_t)cal.dig_H4) << 20) - (((BME280_S32_t)cal.dig_H5) * v_x1_u32r)) + + ((BME280_S32_t)16384)) >> 15) * (((((((v_x1_u32r * ((BME280_S32_t)cal.dig_H6)) >> 10) * (((v_x1_u32r * + ((BME280_S32_t)cal.dig_H3)) >> 11) + ((BME280_S32_t)32768))) >> 10) + ((BME280_S32_t)2097152)) * + ((BME280_S32_t)cal.dig_H2) + 8192) >> 14)); + v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * ((BME280_S32_t)cal.dig_H1)) >> + 4)); + v_x1_u32r = (v_x1_u32r < 0 ? 0 : v_x1_u32r); + v_x1_u32r = (v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r); + return (BME280_U32_t)(v_x1_u32r>>12); + } + + +}; + +#endif diff --git a/ext/sens/MPU6050.h b/ext/sens/MPU6050.h new file mode 100644 index 0000000..e1a1f1a --- /dev/null +++ b/ext/sens/MPU6050.h @@ -0,0 +1,154 @@ +#ifndef SENS_MPU6050 +#define SENS_MPU6050 + +#include "../../io/SoftI2C.h" +#include "../../Debug.h" + +/** + * accelereomter/gyroscope + * https://www.invensense.com/wp-content/uploads/2015/02/MPU-6000-Datasheet1.pdf + * https://www.invensense.com/wp-content/uploads/2015/02/MPU-6000-Register-Map1.pdf + * https://github.com/kriswiner/MPU6050/blob/master/MPU6050BasicExample.ino + */ +class MPU6050 { + +private: + + static constexpr const char* NAME = "MPU6050"; + + static constexpr uint8_t ADDR = 0b1101000 ; + + static constexpr uint8_t REG_CFG_GYRO = 0x1B; + static constexpr uint8_t REG_CFG_ACC = 0x1C; + static constexpr uint8_t REG_ACCEL_XOUT_H = 0x3B; + + static constexpr uint8_t REG_SMPLRT_DIV = 0x19; + static constexpr uint8_t REG_CONFIG = 0x1A; + + static constexpr uint8_t REG_PWR_MGMT_1 = 0x6B; + + bool inited = false; + + +private: + + void init() { + + debugMod(NAME, "init()"); + + // set clock source to be PLL with x-axis gyroscope reference, bits 2:0 = 001 + writeRegister(REG_PWR_MGMT_1, 0b001); // table on page 40 + + // Configure Gyro and Accelerometer + // Disable FSYNC and set accelerometer and gyro bandwidth to 44 and 42 Hz, respectively; + // DLPF_CFG = bits 2:0 = 010; this sets the sample rate at 1 kHz for both + //writeRegister(REG_CONFIG, 0x03); // table on page 13 + writeRegister(REG_CONFIG, 0x00); // table on page 13 + + // Set sample rate = gyroscope output rate/(1 + SMPLRT_DIV) + writeRegister(REG_SMPLRT_DIV, 0x04); // Use a 200 Hz sample rate + + // configure gyro as +/- 4G -> +32767 = +4G, -32768 = -4G + writeRegister(REG_CFG_ACC, 0b01 << 3); // table on page 15 + #define oneGto1000(x) ((int)x)*1000/(32768/4) + } + + bool writeRegister(const uint8_t addr, const uint8_t val) { + + bool ok; + + // address the slave in write mode and select the first register to read + ok = i2c::startWrite(ADDR); + if (!ok) {os_printf("failed start write\n"); return false;} + ok = i2c::writeByteAndCheck(addr); + if (!ok) {os_printf("failed to select register %d\n", addr); return false;} + ok = i2c::writeBytesAndCheck(&val, 1); + if (!ok) {os_printf("failed to write register contents \n"); return false;} + + // done + i2c::stop(); + return true; + + } + + + + +public: + + struct Acc { + int16_t x; + int16_t y; + int16_t z; + }; + + struct Gyro { + int16_t x; + int16_t y; + int16_t z; + }; + + struct Res { + Acc acc; + int16_t temp; + Gyro gyro; + }; + + void initOnce() { + if (inited) {return;} + init(); + inited = true; + } + + + bool isPresent() { + return i2c::query(ADDR); + } + + bool query(Res& res) { + + bool ok; + + // select register(s) to read + //i2c::start(); + ok = i2c::startWrite(ADDR); + if (!ok) {os_printf("failed start write1\n"); return false;} + ok = i2c::writeByteAndCheck(REG_ACCEL_XOUT_H); + if (!ok) {os_printf("failed select register\n"); return false;} + i2c::stop(); + + // read registers + uint8_t buf[14]; + //i2c::start(); + ok = i2c::startRead(ADDR); + if (!ok) {os_printf("failed start write2\n"); return false;} + i2c::readBytes(buf, 14); + i2c::stop(); + + res.acc.x = (buf[0] << 8) | buf[1]; + res.acc.y = (buf[2] << 8) | buf[3]; + res.acc.z = (buf[4] << 8) | buf[5]; + + res.temp = (buf[6] << 8) | buf[7]; + + res.gyro.x = (buf[8] << 8) | buf[9]; + res.gyro.y = (buf[10] << 8) | buf[11]; + res.gyro.z = (buf[12] << 8) | buf[13]; + + res.acc.x = oneGto1000(res.acc.x); + res.acc.y = oneGto1000(res.acc.y); + res.acc.z = oneGto1000(res.acc.z); + + + //os_printf("Acc (%d, %d, %d)\n", res.acc.x, res.acc.y, res.acc.z); + //os_printf("Gyro (%d, %d, %d)\n", res.gyro.x, res.gyro.y, res.gyro.z); + //os_printf("Temp (%d)\n", res.temp); + + return true; + + } + + +}; + +#endif diff --git a/ext/sens/VL53L0X.h b/ext/sens/VL53L0X.h new file mode 100644 index 0000000..81ea92c --- /dev/null +++ b/ext/sens/VL53L0X.h @@ -0,0 +1,1127 @@ +#ifndef SENS_VL53L0X_H +#define SENS_VL53L0X_H + +#include "../../io/SoftI2C.h" +#include "../../Debug.h" + +/** + * TOF distance measurement sensor + * + * there seems to be no register-map from the vendor. only a poor API.. + * used values from this library: + * https://github.com/pololu/vl53l0x-arduino/blob/master/VL53L0X.h + * https://github.com/pololu/vl53l0x-arduino/blob/master/VL53L0X.cpp + */ +class VL53L0X { + +private: + + static constexpr const char* NAME = "VL53L0X"; + + static constexpr uint8_t ADDR = 0b0101001 ; + + + bool inited = false; + + + enum Register { + + SYSRANGE_START = 0x00, + + SYSTEM_THRESH_HIGH = 0x0C, + SYSTEM_THRESH_LOW = 0x0E, + + SYSTEM_SEQUENCE_CONFIG = 0x01, + SYSTEM_RANGE_CONFIG = 0x09, + SYSTEM_INTERMEASUREMENT_PERIOD = 0x04, + + SYSTEM_INTERRUPT_CONFIG_GPIO = 0x0A, + + GPIO_HV_MUX_ACTIVE_HIGH = 0x84, + + SYSTEM_INTERRUPT_CLEAR = 0x0B, + + RESULT_INTERRUPT_STATUS = 0x13, + RESULT_RANGE_STATUS = 0x14, + + RESULT_CORE_AMBIENT_WINDOW_EVENTS_RTN = 0xBC, + RESULT_CORE_RANGING_TOTAL_EVENTS_RTN = 0xC0, + RESULT_CORE_AMBIENT_WINDOW_EVENTS_REF = 0xD0, + RESULT_CORE_RANGING_TOTAL_EVENTS_REF = 0xD4, + RESULT_PEAK_SIGNAL_RATE_REF = 0xB6, + + ALGO_PART_TO_PART_RANGE_OFFSET_MM = 0x28, + + I2C_SLAVE_DEVICE_ADDRESS = 0x8A, + + MSRC_CONFIG_CONTROL = 0x60, + + PRE_RANGE_CONFIG_MIN_SNR = 0x27, + PRE_RANGE_CONFIG_VALID_PHASE_LOW = 0x56, + PRE_RANGE_CONFIG_VALID_PHASE_HIGH = 0x57, + PRE_RANGE_MIN_COUNT_RATE_RTN_LIMIT = 0x64, + + FINAL_RANGE_CONFIG_MIN_SNR = 0x67, + FINAL_RANGE_CONFIG_VALID_PHASE_LOW = 0x47, + FINAL_RANGE_CONFIG_VALID_PHASE_HIGH = 0x48, + FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT = 0x44, + + PRE_RANGE_CONFIG_SIGMA_THRESH_HI = 0x61, + PRE_RANGE_CONFIG_SIGMA_THRESH_LO = 0x62, + + PRE_RANGE_CONFIG_VCSEL_PERIOD = 0x50, + PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI = 0x51, + PRE_RANGE_CONFIG_TIMEOUT_MACROP_LO = 0x52, + + SYSTEM_HISTOGRAM_BIN = 0x81, + HISTOGRAM_CONFIG_INITIAL_PHASE_SELECT = 0x33, + HISTOGRAM_CONFIG_READOUT_CTRL = 0x55, + + FINAL_RANGE_CONFIG_VCSEL_PERIOD = 0x70, + FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI = 0x71, + FINAL_RANGE_CONFIG_TIMEOUT_MACROP_LO = 0x72, + CROSSTALK_COMPENSATION_PEAK_RATE_MCPS = 0x20, + + MSRC_CONFIG_TIMEOUT_MACROP = 0x46, + + SOFT_RESET_GO2_SOFT_RESET_N = 0xBF, + IDENTIFICATION_MODEL_ID = 0xC0, + IDENTIFICATION_REVISION_ID = 0xC2, + + OSC_CALIBRATE_VAL = 0xF8, + + GLOBAL_CONFIG_VCSEL_WIDTH = 0x32, + GLOBAL_CONFIG_SPAD_ENABLES_REF_0 = 0xB0, + GLOBAL_CONFIG_SPAD_ENABLES_REF_1 = 0xB1, + GLOBAL_CONFIG_SPAD_ENABLES_REF_2 = 0xB2, + GLOBAL_CONFIG_SPAD_ENABLES_REF_3 = 0xB3, + GLOBAL_CONFIG_SPAD_ENABLES_REF_4 = 0xB4, + GLOBAL_CONFIG_SPAD_ENABLES_REF_5 = 0xB5, + + GLOBAL_CONFIG_REF_EN_START_SELECT = 0xB6, + DYNAMIC_SPAD_NUM_REQUESTED_REF_SPAD = 0x4E, + DYNAMIC_SPAD_REF_EN_START_OFFSET = 0x4F, + POWER_MANAGEMENT_GO1_POWER_FORCE = 0x80, + + VHV_CONFIG_PAD_SCL_SDA__EXTSUP_HV = 0x89, + + ALGO_PHASECAL_LIM = 0x30, + ALGO_PHASECAL_CONFIG_TIMEOUT = 0x30, + + }; + + // Calculate macro period in *nanoseconds* from VCSEL period in PCLKs + // based on VL53L0X_calc_macro_period_ps() + // PLL_period_ps = 1655; macro_period_vclks = 2304 + #define calcMacroPeriod(vcsel_period_pclks) ((((uint32_t)2304 * (vcsel_period_pclks) * 1655) + 500) / 1000) + + // Decode VCSEL (vertical cavity surface emitting laser) pulse period in PCLKs + // from register value + // based on VL53L0X_decode_vcsel_period() + #define decodeVcselPeriod(reg_val) (((reg_val) + 1) << 1) + + // Encode VCSEL pulse period register value from period in PCLKs + // based on VL53L0X_encode_vcsel_period() + #define encodeVcselPeriod(period_pclks) (((period_pclks) >> 1) - 1) + + uint8_t stop_variable; + uint32_t measurement_timing_budget_us; + + struct SequenceStepEnables { + bool tcc, msrc, dss, pre_range, final_range; + }; + + struct SequenceStepTimeouts { + uint16_t pre_range_vcsel_period_pclks, final_range_vcsel_period_pclks; + uint16_t msrc_dss_tcc_mclks, pre_range_mclks, final_range_mclks; + uint32_t msrc_dss_tcc_us, pre_range_us, final_range_us; + }; + + enum vcselPeriodType { + VcselPeriodPreRange, + VcselPeriodFinalRange + }; + + +private: + + bool init() { + + debugMod(NAME, "init()"); + + + // VL53L0X_DataInit() begin + +// // sensor uses 1V8 mode for I/O by default; switch to 2V8 mode if necessary +// if (io_2v8) +// { +// writeReg8(VHV_CONFIG_PAD_SCL_SDA__EXTSUP_HV, +// readReg8(VHV_CONFIG_PAD_SCL_SDA__EXTSUP_HV) | 0x01); // set bit 0 +// } + + // "Set I2C standard mode" + writeReg8(0x88, 0x00); + + writeReg8(0x80, 0x01); + writeReg8(0xFF, 0x01); + writeReg8(0x00, 0x00); + stop_variable = readReg8(0x91); + writeReg8(0x00, 0x01); + writeReg8(0xFF, 0x00); + writeReg8(0x80, 0x00); + + // disable SIGNAL_RATE_MSRC (bit 1) and SIGNAL_RATE_PRE_RANGE (bit 4) limit checks + writeReg8(MSRC_CONFIG_CONTROL, readReg8(MSRC_CONFIG_CONTROL) | 0x12); + + // set final range signal rate limit to 0.25 MCPS (million counts per second) +////////setSignalRateLimit(0.25); + + writeReg8(SYSTEM_SEQUENCE_CONFIG, 0xFF); + + // VL53L0X_DataInit() end + + // VL53L0X_StaticInit() begin + + debugMod(NAME, "getting SPAD info"); + uint8_t spad_count; + bool spad_type_is_aperture; + if (!getSpadInfo(&spad_count, &spad_type_is_aperture)) { + debugMod(NAME, "failed to get SPAD info"); + return false; + } + + // The SPAD map (RefGoodSpadMap) is read by VL53L0X_get_info_from_device() in + // the API, but the same data seems to be more easily readable from + // GLOBAL_CONFIG_SPAD_ENABLES_REF_0 through _6, so read it from there + uint8_t ref_spad_map[6]; + readRegN(GLOBAL_CONFIG_SPAD_ENABLES_REF_0, ref_spad_map, 6); + + + + + + // -- VL53L0X_set_reference_spads() begin (assume NVM values are valid) + writeReg8(0xFF, 0x01); + writeReg8(DYNAMIC_SPAD_REF_EN_START_OFFSET, 0x00); + writeReg8(DYNAMIC_SPAD_NUM_REQUESTED_REF_SPAD, 0x2C); + writeReg8(0xFF, 0x00); + writeReg8(GLOBAL_CONFIG_REF_EN_START_SELECT, 0xB4); + + uint8_t first_spad_to_enable = spad_type_is_aperture ? 12 : 0; // 12 is the first aperture spad + uint8_t spads_enabled = 0; + + for (uint8_t i = 0; i < 48; i++) { + if (i < first_spad_to_enable || spads_enabled == spad_count) { + // This bit is lower than the first one that should be enabled, or + // (reference_spad_count) bits have already been enabled, so zero this bit + ref_spad_map[i / 8] &= ~(1 << (i % 8)); + } else if ((ref_spad_map[i / 8] >> (i % 8)) & 0x1) { + spads_enabled++; + } + } + + debugMod1(NAME, "enabled SPADs: %d", spads_enabled); + + writeRegN(GLOBAL_CONFIG_SPAD_ENABLES_REF_0, ref_spad_map, 6); + + // -- VL53L0X_set_reference_spads() end + + // -- VL53L0X_load_tuning_settings() begin + // DefaultTuningSettings from vl53l0x_tuning.h + + writeReg8(0xFF, 0x01); + writeReg8(0x00, 0x00); + + writeReg8(0xFF, 0x00); + writeReg8(0x09, 0x00); + writeReg8(0x10, 0x00); + writeReg8(0x11, 0x00); + + writeReg8(0x24, 0x01); + writeReg8(0x25, 0xFF); + writeReg8(0x75, 0x00); + + writeReg8(0xFF, 0x01); + writeReg8(0x4E, 0x2C); + writeReg8(0x48, 0x00); + writeReg8(0x30, 0x20); + + writeReg8(0xFF, 0x00); + writeReg8(0x30, 0x09); + writeReg8(0x54, 0x00); + writeReg8(0x31, 0x04); + writeReg8(0x32, 0x03); + writeReg8(0x40, 0x83); + writeReg8(0x46, 0x25); + writeReg8(0x60, 0x00); + writeReg8(0x27, 0x00); + writeReg8(0x50, 0x06); + writeReg8(0x51, 0x00); + writeReg8(0x52, 0x96); + writeReg8(0x56, 0x08); + writeReg8(0x57, 0x30); + writeReg8(0x61, 0x00); + writeReg8(0x62, 0x00); + writeReg8(0x64, 0x00); + writeReg8(0x65, 0x00); + writeReg8(0x66, 0xA0); + + writeReg8(0xFF, 0x01); + writeReg8(0x22, 0x32); + writeReg8(0x47, 0x14); + writeReg8(0x49, 0xFF); + writeReg8(0x4A, 0x00); + + writeReg8(0xFF, 0x00); + writeReg8(0x7A, 0x0A); + writeReg8(0x7B, 0x00); + writeReg8(0x78, 0x21); + + writeReg8(0xFF, 0x01); + writeReg8(0x23, 0x34); + writeReg8(0x42, 0x00); + writeReg8(0x44, 0xFF); + writeReg8(0x45, 0x26); + writeReg8(0x46, 0x05); + writeReg8(0x40, 0x40); + writeReg8(0x0E, 0x06); + writeReg8(0x20, 0x1A); + writeReg8(0x43, 0x40); + + writeReg8(0xFF, 0x00); + writeReg8(0x34, 0x03); + writeReg8(0x35, 0x44); + + writeReg8(0xFF, 0x01); + writeReg8(0x31, 0x04); + writeReg8(0x4B, 0x09); + writeReg8(0x4C, 0x05); + writeReg8(0x4D, 0x04); + + writeReg8(0xFF, 0x00); + writeReg8(0x44, 0x00); + writeReg8(0x45, 0x20); + writeReg8(0x47, 0x08); + writeReg8(0x48, 0x28); + writeReg8(0x67, 0x00); + writeReg8(0x70, 0x04); + writeReg8(0x71, 0x01); + writeReg8(0x72, 0xFE); + writeReg8(0x76, 0x00); + writeReg8(0x77, 0x00); + + writeReg8(0xFF, 0x01); + writeReg8(0x0D, 0x01); + + writeReg8(0xFF, 0x00); + writeReg8(0x80, 0x01); + writeReg8(0x01, 0xF8); + + writeReg8(0xFF, 0x01); + writeReg8(0x8E, 0x01); + writeReg8(0x00, 0x01); + writeReg8(0xFF, 0x00); + writeReg8(0x80, 0x00); + + // -- VL53L0X_load_tuning_settings() end + + // "Set interrupt config to new sample ready" + // -- VL53L0X_SetGpioConfig() begin + + writeReg8(SYSTEM_INTERRUPT_CONFIG_GPIO, 0x04); + writeReg8(GPIO_HV_MUX_ACTIVE_HIGH, readReg8(GPIO_HV_MUX_ACTIVE_HIGH) & ~0x10); // active low + writeReg8(SYSTEM_INTERRUPT_CLEAR, 0x01); + + // -- VL53L0X_SetGpioConfig() end + + measurement_timing_budget_us = getMeasurementTimingBudget(); + debugMod1(NAME, "timing budget: %d", measurement_timing_budget_us) + + + // "Disable MSRC and TCC by default" + // MSRC = Minimum Signal Rate Check + // TCC = Target CentreCheck + // -- VL53L0X_SetSequenceStepEnable() begin + + writeReg8(SYSTEM_SEQUENCE_CONFIG, 0xE8); + + // -- VL53L0X_SetSequenceStepEnable() end + + // KAZU + setVcselPulsePeriod(VcselPeriodPreRange, 18); + setVcselPulsePeriod(VcselPeriodFinalRange, 14); + + + // "Recalculate timing budget" + debugMod(NAME, "new setting timing budget"); + bool ok = setMeasurementTimingBudget(measurement_timing_budget_us); + if (!ok) { + debugMod(NAME, "failed to set timing budget"); + return false; + } + + + // VL53L0X_StaticInit() end + // VL53L0X_PerformRefCalibration() begin (VL53L0X_perform_ref_calibration()) + // -- VL53L0X_perform_vhv_calibration() begin + + debugMod(NAME, "VHV calibration"); + writeReg8(SYSTEM_SEQUENCE_CONFIG, 0x01); + if (!performSingleRefCalibration(0x40)) { + debugMod(NAME, "VHV calibration failed"); + return false; + } else { + debugMod(NAME, "VHV calibration OK"); + } + + // -- VL53L0X_perform_vhv_calibration() end + // -- VL53L0X_perform_phase_calibration() begin + + debugMod(NAME, "PHASE calibration"); + writeReg8(SYSTEM_SEQUENCE_CONFIG, 0x02); + if (!performSingleRefCalibration(0x00)) { + debugMod(NAME, "PHASE calibration failed"); + return false; + } else { + debugMod(NAME, "PHASE calibration OK"); + } + + // -- VL53L0X_perform_phase_calibration() end + + // "restore the previous Sequence Config" + writeReg8(SYSTEM_SEQUENCE_CONFIG, 0xE8); + + // VL53L0X_PerformRefCalibration() end + + return true; + + } + + + + // Get reference SPAD (single photon avalanche diode) count and type + // based on VL53L0X_get_info_from_device(), + // but only gets reference SPAD count and type + bool getSpadInfo(uint8_t * count, bool * type_is_aperture) { + uint8_t tmp; + + writeReg8(0x80, 0x01); + writeReg8(0xFF, 0x01); + writeReg8(0x00, 0x00); + + writeReg8(0xFF, 0x06); + writeReg8(0x83, readReg8(0x83) | 0x04); + writeReg8(0xFF, 0x07); + writeReg8(0x81, 0x01); + + writeReg8(0x80, 0x01); + + writeReg8(0x94, 0x6b); + writeReg8(0x83, 0x00); + + + int cnt = 0; + while (readReg8(0x83) == 0x00) { + if (++cnt > 100) { + debugMod(NAME, "SPAD timeout"); + return false; + } + } + writeReg8(0x83, 0x01); + tmp = readReg8(0x92); + + *count = tmp & 0x7f; + *type_is_aperture = (tmp >> 7) & 0x01; + + writeReg8(0x81, 0x00); + writeReg8(0xFF, 0x06); + writeReg8(0x83, readReg8(0x83) & ~0x04); + writeReg8(0xFF, 0x01); + writeReg8(0x00, 0x01); + + writeReg8(0xFF, 0x00); + writeReg8(0x80, 0x00); + + return true; + + } + + + // Get the measurement timing budget in microseconds + // based on VL53L0X_get_measurement_timing_budget_micro_seconds() + // in us + uint32_t getMeasurementTimingBudget(void) { + + SequenceStepEnables enables; + SequenceStepTimeouts timeouts; + + uint16_t const StartOverhead = 1910; // note that this is different than the value in set_ + uint16_t const EndOverhead = 960; + uint16_t const MsrcOverhead = 660; + uint16_t const TccOverhead = 590; + uint16_t const DssOverhead = 690; + uint16_t const PreRangeOverhead = 660; + uint16_t const FinalRangeOverhead = 550; + + // "Start and end overhead times always present" + uint32_t budget_us = StartOverhead + EndOverhead; + + getSequenceStepEnables(&enables); + getSequenceStepTimeouts(&enables, &timeouts); + + if (enables.tcc) { + budget_us += (timeouts.msrc_dss_tcc_us + TccOverhead); + } + + if (enables.dss) { + budget_us += 2 * (timeouts.msrc_dss_tcc_us + DssOverhead); + } else if (enables.msrc) { + budget_us += (timeouts.msrc_dss_tcc_us + MsrcOverhead); + } + + if (enables.pre_range) { + budget_us += (timeouts.pre_range_us + PreRangeOverhead); + } + + if (enables.final_range) { + budget_us += (timeouts.final_range_us + FinalRangeOverhead); + } + + measurement_timing_budget_us = budget_us; // store for internal reuse + return budget_us; + + } + + // Set the measurement timing budget in microseconds, which is the time allowed + // for one measurement; the ST API and this library take care of splitting the + // timing budget among the sub-steps in the ranging sequence. A longer timing + // budget allows for more accurate measurements. Increasing the budget by a + // factor of N decreases the range measurement standard deviation by a factor of + // sqrt(N). Defaults to about 33 milliseconds; the minimum is 20 ms. + // based on VL53L0X_set_measurement_timing_budget_micro_seconds() + bool setMeasurementTimingBudget(uint32_t budget_us) { + + SequenceStepEnables enables; + SequenceStepTimeouts timeouts; + + uint16_t const StartOverhead = 1320; // note that this is different than the value in get_ + uint16_t const EndOverhead = 960; + uint16_t const MsrcOverhead = 660; + uint16_t const TccOverhead = 590; + uint16_t const DssOverhead = 690; + uint16_t const PreRangeOverhead = 660; + uint16_t const FinalRangeOverhead = 550; + + uint32_t const MinTimingBudget = 20000; + + if (budget_us < MinTimingBudget) { return false; } + + uint32_t used_budget_us = StartOverhead + EndOverhead; + + getSequenceStepEnables(&enables); + getSequenceStepTimeouts(&enables, &timeouts); + + if (enables.tcc) { + used_budget_us += (timeouts.msrc_dss_tcc_us + TccOverhead); + } + + if (enables.dss) { + used_budget_us += 2 * (timeouts.msrc_dss_tcc_us + DssOverhead); + } else if (enables.msrc) { + used_budget_us += (timeouts.msrc_dss_tcc_us + MsrcOverhead); + } + + if (enables.pre_range) { + used_budget_us += (timeouts.pre_range_us + PreRangeOverhead); + } + + if (enables.final_range) { + + used_budget_us += FinalRangeOverhead; + + // "Note that the final range timeout is determined by the timing + // budget and the sum of all other timeouts within the sequence. + // If there is no room for the final range timeout, then an error + // will be set. Otherwise the remaining time will be applied to + // the final range." + + if (used_budget_us > budget_us) { + // "Requested timeout too big." + return false; + } + + uint32_t final_range_timeout_us = budget_us - used_budget_us; + + // set_sequence_step_timeout() begin + // (SequenceStepId == VL53L0X_SEQUENCESTEP_FINAL_RANGE) + + // "For the final range timeout, the pre-range timeout + // must be added. To do this both final and pre-range + // timeouts must be expressed in macro periods MClks + // because they have different vcsel periods." + + uint16_t final_range_timeout_mclks = timeoutMicrosecondsToMclks(final_range_timeout_us, timeouts.final_range_vcsel_period_pclks); + + if (enables.pre_range) { + final_range_timeout_mclks += timeouts.pre_range_mclks; + } + + writeReg16(FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI, encodeTimeout(final_range_timeout_mclks)); + + // set_sequence_step_timeout() end + measurement_timing_budget_us = budget_us; // store for internal reuse + + } + + return true; + + } + + + // Get sequence step enables + // based on VL53L0X_GetSequenceStepEnables() + void getSequenceStepEnables(SequenceStepEnables* enables) { + + uint8_t sequence_config = readReg8(SYSTEM_SEQUENCE_CONFIG); + enables->tcc = (sequence_config >> 4) & 0x1; + enables->dss = (sequence_config >> 3) & 0x1; + enables->msrc = (sequence_config >> 2) & 0x1; + enables->pre_range = (sequence_config >> 6) & 0x1; + enables->final_range = (sequence_config >> 7) & 0x1; + + } + + + // Get sequence step timeouts + // based on get_sequence_step_timeout(), + // but gets all timeouts instead of just the requested one, and also stores + // intermediate values + void getSequenceStepTimeouts(SequenceStepEnables const* enables, SequenceStepTimeouts* timeouts) { + + timeouts->pre_range_vcsel_period_pclks = getVcselPulsePeriod(VcselPeriodPreRange); + + timeouts->msrc_dss_tcc_mclks = readReg8(MSRC_CONFIG_TIMEOUT_MACROP) + 1; + timeouts->msrc_dss_tcc_us = + timeoutMclksToMicroseconds(timeouts->msrc_dss_tcc_mclks, timeouts->pre_range_vcsel_period_pclks); + + timeouts->pre_range_mclks = + decodeTimeout(readReg16(PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI)); + timeouts->pre_range_us = + timeoutMclksToMicroseconds(timeouts->pre_range_mclks, timeouts->pre_range_vcsel_period_pclks); + + timeouts->final_range_vcsel_period_pclks = getVcselPulsePeriod(VcselPeriodFinalRange); + + timeouts->final_range_mclks = + decodeTimeout(readReg16(FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI)); + + if (enables->pre_range) { + timeouts->final_range_mclks -= timeouts->pre_range_mclks; + } + + timeouts->final_range_us = timeoutMclksToMicroseconds(timeouts->final_range_mclks, timeouts->final_range_vcsel_period_pclks); + + debugMod2(NAME, "current timing is pre: %d, final: %d", timeouts->pre_range_vcsel_period_pclks, timeouts->final_range_vcsel_period_pclks); + + } + + // based on VL53L0X_perform_single_ref_calibration() + bool performSingleRefCalibration(uint8_t vhv_init_byte) { + + writeReg8(SYSRANGE_START, 0x01 | vhv_init_byte); // VL53L0X_REG_SYSRANGE_MODE_START_STOP + + int cnt = 0; + while ((readReg8(RESULT_INTERRUPT_STATUS) & 0x07) == 0) { + if (++cnt > 100) { + debugMod(NAME, "calibration timeout"); + return false; + } + } + + writeReg8(SYSTEM_INTERRUPT_CLEAR, 0x01); + writeReg8(SYSRANGE_START, 0x00); + + return true; + + } + + + // Convert sequence step timeout from MCLKs to microseconds with given VCSEL period in PCLKs + // based on VL53L0X_calc_timeout_us() + uint32_t timeoutMclksToMicroseconds(uint16_t timeout_period_mclks, uint8_t vcsel_period_pclks) { + uint32_t macro_period_ns = calcMacroPeriod(vcsel_period_pclks); + return ((timeout_period_mclks * macro_period_ns) + (macro_period_ns / 2)) / 1000; + } + + // Convert sequence step timeout from microseconds to MCLKs with given VCSEL period in PCLKs + // based on VL53L0X_calc_timeout_mclks() + uint32_t timeoutMicrosecondsToMclks(uint32_t timeout_period_us, uint8_t vcsel_period_pclks) { + uint32_t macro_period_ns = calcMacroPeriod(vcsel_period_pclks); + return (((timeout_period_us * 1000) + (macro_period_ns / 2)) / macro_period_ns); + } + + // Get the VCSEL pulse period in PCLKs for the given period type. + // based on VL53L0X_get_vcsel_pulse_period() + uint8_t getVcselPulsePeriod(vcselPeriodType type) { + if (type == VcselPeriodPreRange) { + return decodeVcselPeriod(readReg8(PRE_RANGE_CONFIG_VCSEL_PERIOD)); + } else if (type == VcselPeriodFinalRange) { + return decodeVcselPeriod(readReg8(FINAL_RANGE_CONFIG_VCSEL_PERIOD)); + } else { + return 255; + } + } + + // Decode sequence step timeout in MCLKs from register value + // based on VL53L0X_decode_timeout() + // Note: the original function returned a uint32_t, but the return value is + // always stored in a uint16_t. + uint16_t decodeTimeout(uint16_t reg_val) { + // format: "(LSByte * 2^MSByte) + 1" + return (uint16_t)((reg_val & 0x00FF) << (uint16_t)((reg_val & 0xFF00) >> 8)) + 1; + } + + // Encode sequence step timeout register value from timeout in MCLKs + // based on VL53L0X_encode_timeout() + // Note: the original function took a uint16_t, but the argument passed to it + // is always a uint16_t. + uint16_t encodeTimeout(uint16_t timeout_mclks) { + // format: "(LSByte * 2^MSByte) + 1" + + uint32_t ls_byte = 0; + uint16_t ms_byte = 0; + + if (timeout_mclks > 0) { + ls_byte = timeout_mclks - 1; + + while ((ls_byte & 0xFFFFFF00) > 0) { + ls_byte >>= 1; + ms_byte++; + } + + return (ms_byte << 8) | (ls_byte & 0xFF); + } else { + return 0; + } + } + + + + // Set the VCSEL (vertical cavity surface emitting laser) pulse period for the + // given period type (pre-range or final range) to the given value in PCLKs. + // Longer periods seem to increase the potential range of the sensor. + // Valid values are (even numbers only): + // pre: 12 to 18 (initialized default: 14) + // final: 8 to 14 (initialized default: 10) + // based on VL53L0X_set_vcsel_pulse_period() + bool setVcselPulsePeriod(vcselPeriodType type, uint8_t period_pclks) { + + uint8_t vcsel_period_reg = encodeVcselPeriod(period_pclks); + + SequenceStepEnables enables; + SequenceStepTimeouts timeouts; + + getSequenceStepEnables(&enables); + getSequenceStepTimeouts(&enables, &timeouts); + + // "Apply specific settings for the requested clock period" + // "Re-calculate and apply timeouts, in macro periods" + + // "When the VCSEL period for the pre or final range is changed, + // the corresponding timeout must be read from the device using + // the current VCSEL period, then the new VCSEL period can be + // applied. The timeout then must be written back to the device + // using the new VCSEL period. + // + // For the MSRC timeout, the same applies - this timeout being + // dependant on the pre-range vcsel period." + + if (type == VcselPeriodPreRange) { + + // "Set phase check limits" + switch (period_pclks) { + case 12: + writeReg8(PRE_RANGE_CONFIG_VALID_PHASE_HIGH, 0x18); + break; + + case 14: + writeReg8(PRE_RANGE_CONFIG_VALID_PHASE_HIGH, 0x30); + break; + + case 16: + writeReg8(PRE_RANGE_CONFIG_VALID_PHASE_HIGH, 0x40); + break; + + case 18: + writeReg8(PRE_RANGE_CONFIG_VALID_PHASE_HIGH, 0x50); + break; + + default: + // invalid period + return false; + } + + writeReg8(PRE_RANGE_CONFIG_VALID_PHASE_LOW, 0x08); + + // apply new VCSEL period + writeReg8(PRE_RANGE_CONFIG_VCSEL_PERIOD, vcsel_period_reg); + + // update timeouts + + // set_sequence_step_timeout() begin + // (SequenceStepId == VL53L0X_SEQUENCESTEP_PRE_RANGE) + + uint16_t new_pre_range_timeout_mclks = timeoutMicrosecondsToMclks(timeouts.pre_range_us, period_pclks); + + writeReg16(PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI, encodeTimeout(new_pre_range_timeout_mclks)); + + // set_sequence_step_timeout() end + + // set_sequence_step_timeout() begin + // (SequenceStepId == VL53L0X_SEQUENCESTEP_MSRC) + + uint16_t new_msrc_timeout_mclks = timeoutMicrosecondsToMclks(timeouts.msrc_dss_tcc_us, period_pclks); + + writeReg8(MSRC_CONFIG_TIMEOUT_MACROP, (new_msrc_timeout_mclks > 256) ? 255 : (new_msrc_timeout_mclks - 1)); + + // set_sequence_step_timeout() end + } else if (type == VcselPeriodFinalRange) { + + switch (period_pclks) { + case 8: + writeReg8(FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, 0x10); + writeReg8(FINAL_RANGE_CONFIG_VALID_PHASE_LOW, 0x08); + writeReg8(GLOBAL_CONFIG_VCSEL_WIDTH, 0x02); + writeReg8(ALGO_PHASECAL_CONFIG_TIMEOUT, 0x0C); + writeReg8(0xFF, 0x01); + writeReg8(ALGO_PHASECAL_LIM, 0x30); + writeReg8(0xFF, 0x00); + break; + + case 10: + writeReg8(FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, 0x28); + writeReg8(FINAL_RANGE_CONFIG_VALID_PHASE_LOW, 0x08); + writeReg8(GLOBAL_CONFIG_VCSEL_WIDTH, 0x03); + writeReg8(ALGO_PHASECAL_CONFIG_TIMEOUT, 0x09); + writeReg8(0xFF, 0x01); + writeReg8(ALGO_PHASECAL_LIM, 0x20); + writeReg8(0xFF, 0x00); + break; + + case 12: + writeReg8(FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, 0x38); + writeReg8(FINAL_RANGE_CONFIG_VALID_PHASE_LOW, 0x08); + writeReg8(GLOBAL_CONFIG_VCSEL_WIDTH, 0x03); + writeReg8(ALGO_PHASECAL_CONFIG_TIMEOUT, 0x08); + writeReg8(0xFF, 0x01); + writeReg8(ALGO_PHASECAL_LIM, 0x20); + writeReg8(0xFF, 0x00); + break; + + case 14: + writeReg8(FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, 0x48); + writeReg8(FINAL_RANGE_CONFIG_VALID_PHASE_LOW, 0x08); + writeReg8(GLOBAL_CONFIG_VCSEL_WIDTH, 0x03); + writeReg8(ALGO_PHASECAL_CONFIG_TIMEOUT, 0x07); + writeReg8(0xFF, 0x01); + writeReg8(ALGO_PHASECAL_LIM, 0x20); + writeReg8(0xFF, 0x00); + break; + + default: + // invalid period + return false; + } + + // apply new VCSEL period + writeReg8(FINAL_RANGE_CONFIG_VCSEL_PERIOD, vcsel_period_reg); + + // update timeouts + + // set_sequence_step_timeout() begin + // (SequenceStepId == VL53L0X_SEQUENCESTEP_FINAL_RANGE) + + // "For the final range timeout, the pre-range timeout + // must be added. To do this both final and pre-range + // timeouts must be expressed in macro periods MClks + // because they have different vcsel periods." + + uint16_t new_final_range_timeout_mclks = timeoutMicrosecondsToMclks(timeouts.final_range_us, period_pclks); + + if (enables.pre_range) { + new_final_range_timeout_mclks += timeouts.pre_range_mclks; + } + + writeReg16(FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI, encodeTimeout(new_final_range_timeout_mclks)); + + // set_sequence_step_timeout end + + } else { + // invalid type + return false; + } + + // "Finally, the timing budget must be re-applied" + + setMeasurementTimingBudget(measurement_timing_budget_us); + + // "Perform the phase calibration. This is needed after changing on vcsel period." + // VL53L0X_perform_phase_calibration() begin + + uint8_t sequence_config = readReg8(SYSTEM_SEQUENCE_CONFIG); + writeReg8(SYSTEM_SEQUENCE_CONFIG, 0x02); + performSingleRefCalibration(0x0); + writeReg8(SYSTEM_SEQUENCE_CONFIG, sequence_config); + + // VL53L0X_perform_phase_calibration() end + + return true; + + } + + + + +private: + + uint8_t readReg8(const uint8_t reg) { + + beginReadRegister(reg); + + uint8_t res = 0; + i2c::readBytes(&res, 1); + i2c::stop(); + + return res; + + } + + uint16_t readReg16(const uint8_t reg) { + + beginReadRegister(reg); + + uint8_t buf[2]; + i2c::readBytes(buf, 2); + i2c::stop(); + + uint16_t res = buf[0]; + res <<= 8; + res |= buf[1]; + return res; + + } + + void readRegN(const uint8_t reg, uint8_t* dst, const size_t len) { + + beginReadRegister(reg); + + i2c::readBytes(dst, len); + i2c::stop(); + + } + + + + /** internal helper method. begin writing to the device */ + void beginWriteRegister(const uint8_t reg) { + + bool ok; + + // select device for writing + ok = i2c::startWrite(ADDR); + if (!ok) {debugMod(NAME, "failed startWrite()");} + + // select register to write to + ok = i2c::writeByteAndCheck(reg); + if (!ok) {debugMod1(NAME, "failed to select register %d", reg);} + + } + + /** internal helper method. begin reading from the device */ + void beginReadRegister(const uint8_t reg) { + + bool ok; + + // select device for writing + ok = i2c::startWrite(ADDR); + if (!ok) {debugMod(NAME, "failed startWrite()");} + + // select register to read from + ok = i2c::writeByteAndCheck(reg); + if (!ok) {debugMod1(NAME, "failed to select register %d", reg);} + i2c::stop(); + + // re-address in read mode + ok = i2c::startRead(ADDR); + if (!ok) {debugMod(NAME, "failed startRead()");} + + } + + + + + + + + + + + + void writeReg8(const uint8_t reg, const uint8_t val) { + + beginWriteRegister(reg); + bool ok = i2c::writeByteAndCheck(val); + if (!ok) {debugMod(NAME, "failed to write byte");} + i2c::stop(); + + } + + void writeReg16(const uint8_t reg, const uint16_t val) { + + beginWriteRegister(reg); + bool ok; + ok = i2c::writeByteAndCheck(val >> 8); + if (!ok) {debugMod(NAME, "failed to write 1st byte");} + ok = i2c::writeByteAndCheck(val >> 0); + if (!ok) {debugMod(NAME, "failed to write 2nd byte");} + i2c::stop(); + + } + + void writeReg32(const uint8_t reg, const uint32_t val) { + + beginWriteRegister(reg); + bool ok; + ok = i2c::writeByteAndCheck(val >> 24); + if (!ok) {debugMod(NAME, "failed to write 1st byte");} + ok = i2c::writeByteAndCheck(val >> 16); + if (!ok) {debugMod(NAME, "failed to write 2nd byte");} + ok = i2c::writeByteAndCheck(val >> 8); + if (!ok) {debugMod(NAME, "failed to write 3rd byte");} + ok = i2c::writeByteAndCheck(val >> 0); + if (!ok) {debugMod(NAME, "failed to write 4th byte");} + i2c::stop(); + + } + + void writeRegN(const uint8_t reg, const uint8_t* data, const size_t len) { + + beginWriteRegister(reg); + + for (size_t i = 0; i < len; ++i) { + bool ok = i2c::writeByteAndCheck(data[i]); + if (!ok) {debugMod(NAME, "failed to write byte"); break;} + } + i2c::stop(); + + } + + +public: + + + void initOnce() { + if (inited) {return;} + init(); + inited = true; + } + + + bool isPresent() { + return i2c::query(ADDR); + } + + /** check whether the sensor responds with some known values */ + bool check() { + const uint8_t v1 = readReg8(0xC0); // should be 0xEE + const uint8_t v2 = readReg8(0xC1); // should be 0xAA + const uint8_t v3 = readReg8(0xC2); // should be 0x10 + const bool ok = (v1 == 0xEE) && (v2 == 0xAA) && (v3 == 0x10); + os_printf("res. %d %d %d\n\n", v1, v2, v3); + return ok; + } + + // Start continuous ranging measurements. If period_ms (optional) is 0 or not + // given, continuous back-to-back mode is used (the sensor takes measurements as + // often as possible); otherwise, continuous timed mode is used, with the given + // inter-measurement period in milliseconds determining how often the sensor + // takes a measurement. + // based on VL53L0X_StartMeasurement() + void startContinuous(uint32_t period_ms) { + + writeReg8(0x80, 0x01); + writeReg8(0xFF, 0x01); + writeReg8(0x00, 0x00); + writeReg8(0x91, stop_variable); + writeReg8(0x00, 0x01); + writeReg8(0xFF, 0x00); + writeReg8(0x80, 0x00); + + if (period_ms != 0) { + + // continuous timed mode + + // VL53L0X_SetInterMeasurementPeriodMilliSeconds() begin + + uint16_t osc_calibrate_val = readReg16(OSC_CALIBRATE_VAL); + + if (osc_calibrate_val != 0) { + period_ms *= osc_calibrate_val; + } + + writeReg32(SYSTEM_INTERMEASUREMENT_PERIOD, period_ms); + + // VL53L0X_SetInterMeasurementPeriodMilliSeconds() end + + writeReg8(SYSRANGE_START, 0x04); // VL53L0X_REG_SYSRANGE_MODE_TIMED + + } else { + + // continuous back-to-back mode + writeReg8(SYSRANGE_START, 0x02); // VL53L0X_REG_SYSRANGE_MODE_BACKTOBACK + + } + + } + + // Stop continuous measurements + // based on VL53L0X_StopMeasurement() + void stopContinuous(void) { + + writeReg8(SYSRANGE_START, 0x01); // VL53L0X_REG_SYSRANGE_MODE_SINGLESHOT + + writeReg8(0xFF, 0x01); + writeReg8(0x00, 0x00); + writeReg8(0x91, 0x00); + writeReg8(0x00, 0x01); + writeReg8(0xFF, 0x00); + + } + + // Returns a range reading in millimeters when continuous mode is active + // (readRangeSingleMillimeters() also calls this function after starting a + // single-shot range measurement) + uint16_t readRangeContinuousMillimeters(void) { + + int cnt = 0; + while ((readReg8(RESULT_INTERRUPT_STATUS) & 0x07) == 0) { + if (++cnt > 100) { + debugMod(NAME, "measurement timeout!"); + return 65535; + } + } + + // assumptions: Linearity Corrective Gain is 1000 (default); + // fractional ranging is not enabled + uint16_t range = readReg16(RESULT_RANGE_STATUS + 10); + + writeReg8(SYSTEM_INTERRUPT_CLEAR, 0x01); + + return range; + + } + + + +}; + +#endif // VL53L0X diff --git a/ext/sens/XPT2046.h b/ext/sens/XPT2046.h new file mode 100644 index 0000000..f261f3b --- /dev/null +++ b/ext/sens/XPT2046.h @@ -0,0 +1,132 @@ +#ifndef XPT2046_H +#define XPT2046_H + +#include "../../Platforms.h" +#include "../../io/HardSPI.h" +#include "../../io/SoftSPI.h" + +// https://www.buydisplay.com/download/ic/XPT2046.pdf + +/** SPI-like touch controller */ +//template class XPT2046 { +template class XPT2046 { + + //SoftSPI spi; + //HardSPI spi; + + + static constexpr int PD0 = 0; + static constexpr int PD1 = 1; + static constexpr int SER_DFR = 2; + static constexpr int MODE = 3; + static constexpr int ADDR = 4; + static constexpr int START = 7; + + SPI& spi; + +public: + + XPT2046(SPI& spi) : spi(spi) { + //spi.init(); + GPIO::setOutput(PIN_CS); + } + + struct Result { + bool valid; + uint16_t x; + uint16_t y; + }; + + Result read() { + + GPIO::clear(PIN_CS); + //vTaskDelay(1 / portTICK_PERIOD_MS); + + Result res; + + if (!isTouched()) { + res.valid = false; + } else { + const uint16_t y = readAvg(1); + const uint16_t x = readAvg(5); + res.y = y; + res.x = x; + res.valid = (x != 0xFFFF) && (y != 0xFFFF); + } + + GPIO::set(PIN_CS); + + return res; + + } + +private: + + /** read 12-bit result from the given input-address using either differential mode or not */ + uint16_t read(const int inp, const bool differential) { + + //uint8_t buf[2]; + const uint8_t diff = differential ? 0 : 1; + const uint8_t cmd = (1 << START) | (inp << ADDR) | (0 << MODE) | (diff << SER_DFR) | (1 << PD1) | (1 << PD0); + + //printf("touch cmd: %d\n", cmd); +// spi.writeByte(cmd); +// uint8_t v1 = spi.readByte(); +// uint8_t v2 = spi.readByte(); +// spi.read(buf, 2); + + // mainly needed for hardware SPI, where send8+read8+read8 does not work as expected + // output: cccccccc 00000000 00000000 00000000 + // input: -------- -rrrrrrr rrrrr--- -------- + const uint32_t tmp = spi.readWriteQuad(cmd << 24); + const uint8_t v1 = (tmp >> 16) & 0xFF; + const uint8_t v2 = (tmp >> 8) & 0xFF; + const uint16_t res = (v1 << 8 | v2) >> 3; + + //printf("res: %d\n", res); + + return res; + + } + + bool isTouched() { + + // read Y-axis in non-differential mode. if result is close to 4095, screen is not touched + for (int i = 0; i < 4; ++i) { + if (read(1,false) > 3900) {return false;} + } + + // read X-axis in non-differential mode. if result is close to 4095, screen is not touched + for (int i = 0; i < 4; ++i) { + if (read(5,false) > 3900) {return false;} + } + + return true; + + } + + uint16_t readAvg(const int inp) { + + // dummy-reads to stabilize the result + for (int i = 0; i < 4; ++i) {read(inp, true);} + + // perform several reads and estimate std-dev + static constexpr uint8_t cnt = 12; + uint32_t sum = 0; + uint32_t sum2 = 0; + for (int i = 0; i < cnt; ++i) { + const uint16_t cur = read(inp, true); + sum += cur; + sum2 += cur*cur; + } + const uint32_t stdDev = (sum2/cnt) - (sum/cnt)*(sum/cnt); + //printf("%d\n", stdDev); + + return (stdDev < 4096) ? ((sum/cnt)&0xFFFF) : (0xFFFF); + + } + + +}; + +#endif // XPT2046_H diff --git a/ext/time/DS3231.h b/ext/time/DS3231.h new file mode 100644 index 0000000..8f96f35 --- /dev/null +++ b/ext/time/DS3231.h @@ -0,0 +1,124 @@ +#ifndef TIME_DS3231 +#define TIME_DS3231 + +#include "../../io/SoftI2C.h" + +class DS3231 { + + static constexpr uint8_t ADDR7 = 0x68; + + static constexpr uint8_t REG_SECONDS = 0x00; + static constexpr uint8_t REG_MINUTES = 0x01; + static constexpr uint8_t REG_HOURS = 0x02; + + +public: + + bool isPresent() { + return i2c::query(ADDR7); + } + + struct Time { + uint8_t h; + uint8_t m; + uint8_t s; + }; + struct Date { + uint8_t d; + uint8_t m; + uint8_t y; + }; + + struct Res { + Time time; + Date date; + }; + + Res get() { + + uint8_t buf[8]; + readRegister(0x00, buf, 8); + + Res res; + + res.time.h = (((buf[2] & 0b00110000)>>4)*10) + (buf[2] & 0b1111); + res.time.m = (((buf[1] & 0b01110000)>>4)*10) + (buf[1] & 0b1111); + res.time.s = (((buf[0] & 0b01110000)>>4)*10) + (buf[0] & 0b1111); + + res.date.d = (((buf[4] & 0b00110000)>>4)*10) + (buf[4] & 0b1111); + res.date.m = (((buf[5] & 0b00010000)>>4)*10) + (buf[5] & 0b1111); + res.date.y = (((buf[6] & 0b11110000)>>4)*10) + (buf[6] & 0b1111); + + os_printf("%d %d %d \n", res.time.h, res.time.m, res.time.s); + os_printf("%d %d %d \n", res.date.d, res.date.m, res.date.y); + + return res; + + } + + void setTime(uint8_t h, uint8_t m, uint8_t s) { + + uint8_t buf[3]; + buf[0] = ((s/10)<<4) + (s % 10); + buf[1] = ((m/10)<<4) + (m % 10); + buf[2] = ((h/10)<<4) + (h % 10); + writeRegister(0x00, buf, 3); + + } + + void setDate(uint8_t d, uint8_t m, uint8_t y) { + + uint8_t buf[3]; + buf[0] = ((d/10)<<4) + (d % 10); + buf[1] = ((m/10)<<4) + (m % 10); + buf[2] = ((y/10)<<4) + (y % 10); + writeRegister(0x04, buf, 3); + + } + +private: + + bool readRegister(const uint8_t addr, uint8_t* dst, const uint8_t len) { + + bool ok; + + // address the slave in write mode and select the first register to read + ok = i2c::startWrite(ADDR7); + if (!ok) {os_printf("failed start write\n"); return false;} + ok = i2c::writeByteAndCheck(addr); + if (!ok) {os_printf("failed to select register %d\n", addr); return false;} + + //i2c::stop(); + + // address the slave in read mode and read [len] registers + ok = i2c::startRead(ADDR7); + if (!ok) {os_printf("failed start read\n"); return 0;} + i2c::readBytes(dst, len); + + // done + i2c::stop(); + return true; + + } + + bool writeRegister(const uint8_t addr, const uint8_t* src, const uint8_t len) { + + bool ok; + + // address the slave in write mode and select the first register to read + ok = i2c::startWrite(ADDR7); + if (!ok) {os_printf("failed start write\n"); return false;} + ok = i2c::writeByteAndCheck(addr); + if (!ok) {os_printf("failed to select register %d\n", addr); return false;} + ok = i2c::writeBytesAndCheck(src, len); + if (!ok) {os_printf("failed to write register contents \n"); return false;} + + // done + i2c::stop(); + return true; + + } + +}; + +#endif diff --git a/io/GPIO.h b/io/GPIO.h new file mode 100644 index 0000000..e102bc0 --- /dev/null +++ b/io/GPIO.h @@ -0,0 +1,122 @@ +#ifndef ESP_LIB_GPIO_H +#define ESP_LIB_GPIO_H + +#include "../Platforms.h" + + +#if ESP8266 + + #include "fastGPIO.h" + + struct GPIO { + + static inline bool get(const uint8_t num) { + return GPIO_INPUT_GET(num); + } + + static inline void set(const uint8_t num) { + GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, 1 << num); + } + + static inline void clear(const uint8_t num) { + GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, 1 << num); + } + + static inline void setOutput(const uint8_t num) { + switch(num) { + case 0: GPIO0_OUTPUT_SET; break; + case 1: GPIO1_OUTPUT_SET; break; + case 2: GPIO2_OUTPUT_SET; break; + case 3: GPIO3_OUTPUT_SET; break; + case 4: GPIO4_OUTPUT_SET; break; + case 5: GPIO5_OUTPUT_SET; break; + //case 6: GPIO6_OUTPUT_SET; break; + //case 7: GPIO7_OUTPUT_SET; break; + //case 8: GPIO8_OUTPUT_SET; break; + case 9: GPIO9_OUTPUT_SET; break; + case 10: GPIO10_OUTPUT_SET; break; + //case 11: GPIO11_OUTPUT_SET; break; + case 12: GPIO12_OUTPUT_SET; break; + case 13: GPIO13_OUTPUT_SET; break; + case 14: GPIO14_OUTPUT_SET; break; + case 15: GPIO15_OUTPUT_SET; break; + } + } + + static inline void setInput(const uint8_t num) { + switch(num) { + case 0: GPIO0_INPUT_SET; break; + case 1: GPIO1_INPUT_SET; break; + case 2: GPIO2_INPUT_SET; break; + case 3: GPIO3_INPUT_SET; break; + case 4: GPIO4_INPUT_SET; break; + case 5: GPIO5_INPUT_SET; break; + //case 6: GPIO6_INPUT_SET; break; + //case 7: GPIO7_INPUT_SET; break; + //case 8: GPIO8_INPUT_SET; break; + case 9: GPIO9_INPUT_SET; break; + case 10: GPIO10_INPUT_SET; break; + //case 11: GPIO11_INPUT_SET; break; + case 12: GPIO12_INPUT_SET; break; + case 13: GPIO13_INPUT_SET; break; + case 14: GPIO14_INPUT_SET; break; + case 15: GPIO15_INPUT_SET; break; + } + } + + }; + +#elif ESP32 + + #include "driver/gpio.h" + + struct MyGPIO { + + static inline bool get(const uint8_t num) { + return get((gpio_num_t)num); + } + static inline bool get(const gpio_num_t num) { + return gpio_get_level(num); // TODO faster access like READ_PERI_REG?? + } + + template static inline void setOrClear(const uint8_t num, const T val) { + if (val) {set(num);} else {clear(num);} + //WRITE_PERI_REG(GPIO_OUT_W1TS_REG, (val != 0) << num); // branchless but usually slower + //WRITE_PERI_REG(GPIO_OUT_W1TC_REG, (val == 0) << num); + } + + static inline void set(const uint8_t num) { + WRITE_PERI_REG(GPIO_OUT_W1TS_REG, 1 << num); + } + + static inline void clear(const uint8_t num) { + WRITE_PERI_REG(GPIO_OUT_W1TC_REG, 1 << num); + } + + static inline void setOutput(const uint8_t num) { + setOutput((gpio_num_t)num); + } + static inline void setOutput(const gpio_num_t num) { + gpio_set_direction(num, GPIO_MODE_OUTPUT); + } + + static inline void setInput(const uint8_t num) { + setInput((gpio_num_t)num); + } + static inline void setInput(const gpio_num_t num) { + gpio_set_direction(num, GPIO_MODE_INPUT); + } + + static void toggleBuiltInLED() { + static bool level = false; + setOutput(GPIO_NUM_2); + level = !level; + if (level) {set(GPIO_NUM_2);} else {clear(GPIO_NUM_2);} + } + + }; + +#endif + + +#endif // ESP_LIB_GPIO_H diff --git a/io/HardSPI.h b/io/HardSPI.h new file mode 100644 index 0000000..22e17d5 --- /dev/null +++ b/io/HardSPI.h @@ -0,0 +1,228 @@ +#ifndef HARDSPI_H +#define HARDSPI_H + +#include "../Debug.h" +#include "driver/spi_master.h" +#include +#include + +//static spi_transaction_t trans[2]; + +#define PIN_NUM_MISO GPIO_NUM_19 +#define PIN_NUM_MOSI GPIO_NUM_23 +#define PIN_NUM_CLK GPIO_NUM_18 +#define PIN_NUM_CS GPIO_NUM_2 + + +template class HardSPI { +//class HardSPI { + + static constexpr int MAX_LEN = 4000; + + spi_device_handle_t spi; + + spi_bus_config_t buscfg; + + spi_device_interface_config_t devcfg; + + static constexpr const char* TAG = "hwSPI"; + +public: + + // can be called before every transmit +// static void IRAM_ATTR pre_transfer_callback(spi_transaction_t *t) { +// } + + /** ctor */ + HardSPI() { + init(); + } + +private: + + void init() { + + int ret; + + // BUS specific options + memset(&buscfg, 0, sizeof(buscfg)); + buscfg.miso_io_num = PIN_NUM_MISO; + buscfg.mosi_io_num = PIN_NUM_MOSI; + buscfg.sclk_io_num = PIN_NUM_CLK; + buscfg.quadwp_io_num = -1; + buscfg.quadhd_io_num = -1; + buscfg.max_transfer_sz = MAX_LEN;//320*240*2; + buscfg.flags = 0; + buscfg.intr_flags = 0; + + // DEVICE specific options + memset(&devcfg, 0, sizeof(devcfg)); + devcfg.clock_speed_hz = 10 * 1000 * 1000; + devcfg.mode = 0; + devcfg.spics_io_num = -1;//PIN_NUM_CS; // currently not used + devcfg.queue_size = 2; + devcfg.pre_cb = nullptr; + //devcfg.pre_cb = pre_transfer_callback; // can be called before every transmit + + // Initialize the SPI bus + ret = spi_bus_initialize(HSPI_HOST, &buscfg, 1); + ESP_ERROR_CHECK(ret); + + // Attach the device to the SPI bus + ret = spi_bus_add_device(HSPI_HOST, &devcfg, &spi); + ESP_ERROR_CHECK(ret); + + } + +public: + + /** blocking transmit the given bytes */ + void write(const uint8_t* data, size_t len) { + + spi_transaction_t t; + memset(&t, 0, sizeof(spi_transaction_t)); + + while(len > 0) { + + const size_t chunkLen = min(MAX_LEN, (len)); + t.length = 8 * chunkLen; // in bits + t.tx_buffer = data; // MOSI only + t.flags = 0; + + // blocking transfer + esp_err_t ret = spi_device_polling_transmit(spi, &t); + ESP_ERROR_CHECK(ret); + + // update remaining length and data pointer + len -= chunkLen; + data += chunkLen; + + } + + } + + void read(uint8_t* dst, size_t len) { + + // sanity check + { + const size_t addr = reinterpret_cast(dst); + if ((addr % 4) != 0) { + ESP_LOGE(TAG, "read-buffer must be 32-bit aligned"); + } + } + + spi_transaction_t t; + memset(&t, 0, sizeof(spi_transaction_t)); + + //debugMod1(TAG, "read %d bytes", len); + + while(len > 0) { + + const size_t chunkLen = min(MAX_LEN, (len)); + //ESP_LOGI(TAG, "- read %d byte chunk", chunkLen); + t.length = 8 * chunkLen; // in bits + t.rx_buffer = dst; // MISO only + t.flags = 0; + + // blocking transfer + esp_err_t ret = spi_device_polling_transmit(spi, &t); + ESP_ERROR_CHECK(ret); + + // update remaining length and destination-data pointer + len -= chunkLen; + dst += chunkLen; + + } + + } + + /** send 4 bytes onto the bus while reading 4 bytes response */ + uint32_t readWriteQuad(uint32_t write) { + + spi_transaction_t t; + memset(&t, 0, sizeof(spi_transaction_t)); + t.length = 8*4; + t.rxlength = 8*4; + t.tx_data[0] = (write >> 24) & 0xFF; + t.tx_data[1] = (write >> 16) & 0xFF; + t.tx_data[2] = (write >> 8) & 0xFF; + t.tx_data[3] = (write >> 0) & 0xFF; + t.flags = SPI_TRANS_USE_RXDATA | SPI_TRANS_USE_TXDATA; + + esp_err_t ret = spi_device_polling_transmit(spi, &t); + ESP_ERROR_CHECK(ret); + + return + (static_cast(t.rx_data[0]) << 24) | + (static_cast(t.rx_data[1]) << 16) | + (static_cast(t.rx_data[2]) << 8) | + (static_cast(t.rx_data[3]) << 0); + + } + + + /** read one byte from the bus */ + uint8_t readByte() { + + spi_transaction_t t; + memset(&t, 0, sizeof(spi_transaction_t)); + t.length = 8*1; + t.flags = SPI_TRANS_USE_RXDATA; + + esp_err_t ret = spi_device_polling_transmit(spi, &t); + ESP_ERROR_CHECK(ret); + + return t.rx_data[0]; + + } + + + + void writeByte(const uint8_t byte) { + + spi_transaction_t t; + memset(&t, 0, sizeof(spi_transaction_t)); + t.length = 8*1; + t.tx_data[0] = byte; + t.flags = SPI_TRANS_USE_TXDATA; + + esp_err_t ret = spi_device_polling_transmit(spi, &t); + ESP_ERROR_CHECK(ret); + + } + + void writeWord(const uint16_t word) { + + spi_transaction_t t; + memset(&t, 0, sizeof(spi_transaction_t)); + t.length = 8*2; + t.tx_data[0] = static_cast(word>>8); + t.tx_data[1] = static_cast(word>>0); + t.flags = SPI_TRANS_USE_TXDATA; + + esp_err_t ret = spi_device_polling_transmit(spi, &t); + ESP_ERROR_CHECK(ret); + + } + + void writeQuad(const uint32_t quad) { + + spi_transaction_t t; + memset(&t, 0, sizeof(spi_transaction_t)); + t.length = 8*4; + t.tx_data[0] = static_cast(quad>>24); + t.tx_data[1] = static_cast(quad>>16); + t.tx_data[2] = static_cast(quad>>8); + t.tx_data[3] = static_cast(quad>>0); + t.flags = SPI_TRANS_USE_TXDATA; + + esp_err_t ret = spi_device_polling_transmit(spi, &t); + ESP_ERROR_CHECK(ret); + + } + + + +}; + +#endif // HARDSPI_H diff --git a/io/PWM.h b/io/PWM.h new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/io/PWM.h @@ -0,0 +1 @@ + diff --git a/io/SoftI2C.h b/io/SoftI2C.h new file mode 100644 index 0000000..55fe3a4 --- /dev/null +++ b/io/SoftI2C.h @@ -0,0 +1,240 @@ +#ifndef SOFTI2C_H +#define SOFTI2C_H + +#include "../Platforms.h" +#include "../Debug.h" +#include "GPIO.h" + + +#if ESP8266 + #include "fastGPIO.h" +#elif ESP32 + # error "Not yet supported" +#endif + +// https://www.best-microcontroller-projects.com/i2c-tutorial.html + +namespace i2c { + + static constexpr const char* NAME = "SoftI2C"; + + #if ESP8266 + + // static inline void init() { + // //debugMod(NAME, "init()"); + // GPIO5_OUTPUT_SET; // clock is always output + // //GPIO5_INPUT_PULLUP_SET; // pullup for i2c + // //GPIO4_INPUT_PULLUP_SET; // pullup for i2c + // } + + // static inline void sclLo() {GPIO5_L;} + // static inline void sclHi() {GPIO5_H;} + // static inline void sclDirOut() {GPIO5_OUTPUT_SET;} + + // static inline void sdaDirIn() {GPIO4_INPUT_SET;}// GPIO4_INPUT_PULLUP_SET;} + // static inline void sdaDirOut() {GPIO4_OUTPUT_SET;} + // static inline uint8_t sdaRead() {return GPIO4_IN;} + // static inline void sdaLo() {GPIO4_L;} + // static inline void sdaHi() {GPIO4_H;} + + static constexpr const uint8_t PIN_SCL = 5; + static constexpr const uint8_t PIN_SDA = 4; + + #elif ESP32 + //# error "Not yet supported" + #endif + + + #if (SOFTI2C_SPEED == 1) + + // safe + static inline void waitShort() {os_delay_us(1);} + static inline void waitLong() {os_delay_us(10);} + + #elif (SOFTI2C_SPEED == 2) + + // fast + static inline void waitShort() {asm("nop");asm("nop");} + static inline void waitLong() {os_delay_us(1);} + + #elif (SOFTI2C_SPEED == 3) + + // ultra fast + static inline void waitShort() {} + static inline void waitLong() {asm("nop");asm("nop");asm("nop");asm("nop");} + + #else + + #error "SOFTI2C Speed not set" + + #endif + + + + + static inline void init() { + GPIO::setOutput(PIN_SCL); // always output + } + + static inline void sclLo() {GPIO::clear(PIN_SCL);} + static inline void sclHi() {GPIO::set(PIN_SCL);} + static inline void sclDirOut() {GPIO::setOutput(PIN_SCL);} + + static inline void sdaDirIn() {GPIO::setInput(PIN_SDA);}// GPIO4_INPUT_PULLUP_SET;} + static inline void sdaDirOut() {GPIO::setOutput(PIN_SDA);} + static inline uint8_t sdaRead() {return GPIO::get(PIN_SDA);} + static inline void sdaLo() {GPIO::clear(PIN_SDA);} + static inline void sdaHi() {GPIO::set(PIN_SDA);} + + + static inline void start() { + + init(); + sdaDirOut(); + sclDirOut(); + + sdaHi(); + sclHi(); + + waitLong(); + sdaLo(); + + waitLong(); + sclLo(); + + waitLong(); + + } + + static inline void stop() { + + sdaDirOut(); + sclDirOut(); + + sdaLo(); + sclLo(); + + waitLong(); + sclHi(); + + waitLong(); + sdaHi(); + + sdaDirIn(); // free the bus + + waitLong(); + + } + + + /** write one bit to the bus */ + static inline void writeBit(const bool out) { + sdaDirOut(); // switch to output mode + if(out) {sdaHi();} else {sdaLo();} // apply data + sclHi(); // clock pulse + waitShort(); + sclLo(); + waitShort(); + sdaDirIn(); // free the bus + } + + /** read one bit from the bus */ + static inline uint8_t readBit() { + sdaDirIn(); // switch to input mode + sclHi(); // clock pulse and read + waitShort(); + const uint8_t val = sdaRead(); + sclLo(); + waitShort(); + return val; + } + + static inline bool readAck() { + const uint8_t res = readBit(); + //os_printf("readAck() - Bus: %d\n", res); + return res == 0; // ACK when input pulled low + } + + static inline void writeAck() { + writeBit(0); + } + + static inline void writeNAck() { + writeBit(1); + } + + /** read 8 bits from the bus, WITHOUT sending ACK/NACK */ + static uint8_t IN_FLASH readByte() { + return + (readBit() << 7) | + (readBit() << 6) | + (readBit() << 5) | + (readBit() << 4) | + (readBit() << 3) | + (readBit() << 2) | + (readBit() << 1) | + (readBit() << 0); + } + + /** read the given number of bytes from the slave and generate ACK/NACK as needed */ + static void readBytes(uint8_t* dst, uint8_t len) { + while(len) { + *dst = readByte(); + --len; + ++dst; + if (len) {writeAck();} else {writeNAck();} // done? or want more? + } + } + + /** write one byte to the bus */ + static void IN_FLASH writeByte(const uint8_t byte) { + writeBit(byte & BIT( 7)); + writeBit(byte & BIT( 6)); + writeBit(byte & BIT( 5)); + writeBit(byte & BIT( 4)); + writeBit(byte & BIT( 3)); + writeBit(byte & BIT( 2)); + writeBit(byte & BIT( 1)); + writeBit(byte & BIT( 0)); + } + + /** write one byte to the bus and check slave's ACK/NACK */ + static bool IN_FLASH writeByteAndCheck(const uint8_t byte) { + writeByte(byte); + return readAck(); + } + + /** write several bytes to the bus and check slave's ACK/NACK */ + static bool IN_FLASH writeBytesAndCheck(const uint8_t* src, uint8_t len) { + while(len) { + const bool ok = writeByteAndCheck(*src); + if (!ok) {return false;} + --len; ++src; + } + return true; + } + + + static inline bool startWrite(const uint8_t addr7) { + start(); + writeByte( (addr7<<1) | 0 ); + const bool ok = readAck(); + return ok; + } + + static inline bool startRead(const uint8_t addr7) { + start(); + writeByte( (addr7<<1) | 1 ); + const bool ok = readAck(); + return ok; + } + + static inline bool query(const uint8_t addr7) { + bool ok = startWrite(addr7); + stop(); + return ok; + } + +} + +#endif // SOFTSPI_H diff --git a/io/SoftSPI.h b/io/SoftSPI.h index 69fa790..21371fc 100644 --- a/io/SoftSPI.h +++ b/io/SoftSPI.h @@ -1,11 +1,19 @@ #ifndef SOFTSPI_H #define SOFTSPI_H -//#include "IO.h" +#include "GPIO.h" #include "../Platforms.h" #include "../Debug.h" + +#pragma GCC push_options +#pragma GCC optimize ("O3") + + + +/* + #if (PLATFORM == NODE_MCU) || (PLATFORM == WEMOS_D1_MINI) #include "fastGPIO.h" @@ -34,29 +42,35 @@ #include "driver/gpio.h" #include - #define SPI_PIN_MISO GPIO_NUM_35 - #define SPI_PIN_MOSI GPIO_NUM_32 - #define SPI_PIN_CLK GPIO_NUM_27 - #define SPI_PIN_CS GPIO_NUM_25 +// #define SPI_PIN_MISO GPIO_NUM_35 +// #define SPI_PIN_MOSI GPIO_NUM_32 +// #define SPI_PIN_CLK GPIO_NUM_27 +// #define SPI_PIN_CS GPIO_NUM_25 - #define SPI_CS_OUTPUT gpio_set_direction(SPI_PIN_CS, GPIO_MODE_OUTPUT) - #define SPI_MOSI_OUTPUT gpio_set_direction(SPI_PIN_MOSI, GPIO_MODE_OUTPUT) - #define SPI_MISO_INPUT gpio_set_direction(SPI_PIN_MISO, GPIO_MODE_INPUT) + #define SPI_PIN_MISO GPIO_NUM_19 + #define SPI_PIN_MOSI GPIO_NUM_23 + #define SPI_PIN_CLK GPIO_NUM_18 + #define SPI_PIN_CS GPIO_NUM_2 + + #define SPI_CS_OUTPUT GPIO::setOutput(SPI_PIN_CS) + #define SPI_MOSI_OUTPUT GPIO::setOutput(SPI_PIN_MOSI) + #define SPI_MISO_INPUT GPIO::setInput(SPI_PIN_MISO) #define SPI_MISO_NO_PULLUP //gpio_set_pull_mode(SPI_PIN_MISO, GPIO_FLOATING) /// ?????? - #define SPI_CLK_OUTPUT gpio_set_direction(SPI_PIN_CLK, GPIO_MODE_OUTPUT) + #define SPI_CLK_OUTPUT GPIO::setOutput(SPI_PIN_CLK) - #define SPI_CS_LO gpio_set_level(SPI_PIN_CS, 0) - #define SPI_CS_HI gpio_set_level(SPI_PIN_CS, 1) + #define SPI_CS_LO GPIO::clear(SPI_PIN_CS) + #define SPI_CS_HI GPIO::set(SPI_PIN_CS) - #define SPI_CLK_LO gpio_set_level(SPI_PIN_CLK, 0) - #define SPI_CLK_HI gpio_set_level(SPI_PIN_CLK, 1) + #define SPI_CLK_LO GPIO::clear(SPI_PIN_CLK) + #define SPI_CLK_HI GPIO::set(SPI_PIN_CLK) - #define SPI_MOSI_LO gpio_set_level(SPI_PIN_MOSI, 0) - #define SPI_MOSI_HI gpio_set_level(SPI_PIN_MOSI, 1) + #define SPI_MOSI_LO GPIO::clear(SPI_PIN_MOSI) + #define SPI_MOSI_HI GPIO::set(SPI_PIN_MOSI) + #define SPI_MOSI_SC(v) GPIO::setOrClear(SPI_PIN_MOSI, v) - #define SPI_MISO_READ gpio_get_level(SPI_PIN_MISO) + #define SPI_MISO_READ GPIO::get(SPI_PIN_MISO); - #define SPI_FAST + #define SPI_FAST #else #error "not supported" @@ -66,18 +80,6 @@ namespace spi { static constexpr const char* NAME = "SoftSPI"; - // MTDI GPIO12 MISO (DIN) D6 - // MTCK GPIO13 MOSI (DOUT) D7 - // MTMS GPIO14 CLOCK D5 - // MTDO GPIO15 CS / SS D8 - -// #define PIN_CLK 14 -// #define PIN_MISO 12 -// #define PIN_MOSI 13 - -// #define BIT(nr) (1 << 0) - -//public: // SoftSPI() { // // NOT CALLED! @@ -94,12 +96,14 @@ namespace spi { static inline bool getMISO() {return SPI_MISO_READ;} // D6 + #ifdef SPI_FAST static inline void wait() { - __asm__ __volatile__("nop"); + //__asm__ __volatile__("nop"); } static inline void waitLong() { - DELAY_US(1); + //DELAY_US(1); + __asm__ __volatile__("nop"); __asm__ __volatile__("nop"); } #else static inline void wait() { @@ -136,7 +140,7 @@ namespace spi { return (inp) ? 1 : 0; } - /** write one bit to the bus */ + // write one bit to the bus static inline void writeBit(const bool out) { if(out) {SPI_MOSI_HI;} else {SPI_MOSI_LO;} wait(); @@ -146,7 +150,7 @@ namespace spi { wait(); } - /** read one bit from the bus */ + // read one bit from the bus static inline uint8_t readBit() { clkHi(); wait(); @@ -172,7 +176,7 @@ namespace spi { } - /** read 16 bits */ + // read 16 bits static uint16_t IN_FLASH readWord() { return (readBit() << 15) | @@ -193,54 +197,87 @@ namespace spi { (readBit() << 0); } - /** read 8 bits */ - static uint8_t IN_FLASH readByte() { + // read 8 bits + static uint8_t IRAM_ATTR readByte() { return - (readBit() << 7) | - (readBit() << 6) | - (readBit() << 5) | - (readBit() << 4) | - (readBit() << 3) | - (readBit() << 2) | - (readBit() << 1) | - (readBit() << 0); + (readBit() << 7) | + (readBit() << 6) | + (readBit() << 5) | + (readBit() << 4) | + (readBit() << 3) | + (readBit() << 2) | + (readBit() << 1) | + (readBit() << 0); } - - - static void IN_FLASH writeWord(const uint16_t word) { - writeBit(word & BIT(15)); - writeBit(word & BIT(14)); - writeBit(word & BIT(13)); - writeBit(word & BIT(12)); - writeBit(word & BIT(11)); - writeBit(word & BIT(10)); - writeBit(word & BIT( 9)); - writeBit(word & BIT( 8)); - writeBit(word & BIT( 7)); - writeBit(word & BIT( 6)); - writeBit(word & BIT( 5)); - writeBit(word & BIT( 4)); - writeBit(word & BIT( 3)); - writeBit(word & BIT( 2)); - writeBit(word & BIT( 1)); - writeBit(word & BIT( 0)); + static void IRAM_ATTR writeQuad(const uint32_t quad) { + writeBit(quad & BIT(31)); + writeBit(quad & BIT(30)); + writeBit(quad & BIT(29)); + writeBit(quad & BIT(28)); + writeBit(quad & BIT(27)); + writeBit(quad & BIT(26)); + writeBit(quad & BIT(25)); + writeBit(quad & BIT(24)); + writeBit(quad & BIT(23)); + writeBit(quad & BIT(22)); + writeBit(quad & BIT(21)); + writeBit(quad & BIT(20)); + writeBit(quad & BIT(19)); + writeBit(quad & BIT(18)); + writeBit(quad & BIT(17)); + writeBit(quad & BIT(16)); + writeBit(quad & BIT(15)); + writeBit(quad & BIT(14)); + writeBit(quad & BIT(13)); + writeBit(quad & BIT(12)); + writeBit(quad & BIT(11)); + writeBit(quad & BIT(10)); + writeBit(quad & BIT( 9)); + writeBit(quad & BIT( 8)); + writeBit(quad & BIT( 7)); + writeBit(quad & BIT( 6)); + writeBit(quad & BIT( 5)); + writeBit(quad & BIT( 4)); + writeBit(quad & BIT( 3)); + writeBit(quad & BIT( 2)); + writeBit(quad & BIT( 1)); + writeBit(quad & BIT( 0)); } - static void IN_FLASH writeByte(const uint8_t byte) { - writeBit(byte & BIT( 7)); - writeBit(byte & BIT( 6)); - writeBit(byte & BIT( 5)); - writeBit(byte & BIT( 4)); - writeBit(byte & BIT( 3)); - writeBit(byte & BIT( 2)); - writeBit(byte & BIT( 1)); - writeBit(byte & BIT( 0)); + static void IRAM_ATTR writeWord(const uint16_t word) { + writeBit(word & BIT(15)); + writeBit(word & BIT(14)); + writeBit(word & BIT(13)); + writeBit(word & BIT(12)); + writeBit(word & BIT(11)); + writeBit(word & BIT(10)); + writeBit(word & BIT( 9)); + writeBit(word & BIT( 8)); + writeBit(word & BIT( 7)); + writeBit(word & BIT( 6)); + writeBit(word & BIT( 5)); + writeBit(word & BIT( 4)); + writeBit(word & BIT( 3)); + writeBit(word & BIT( 2)); + writeBit(word & BIT( 1)); + writeBit(word & BIT( 0)); + } + + static void IRAM_ATTR writeByte(uint8_t byte) { + writeBit(byte & BIT( 7)); + writeBit(byte & BIT( 6)); + writeBit(byte & BIT( 5)); + writeBit(byte & BIT( 4)); + writeBit(byte & BIT( 3)); + writeBit(byte & BIT( 2)); + writeBit(byte & BIT( 1)); + writeBit(byte & BIT( 0)); } static uint16_t IN_FLASH readWriteWord(const uint16_t word) { return - (readWriteBit(word & BIT(15)) << 15) | + (readWriteBit(word & BIT(15)) << 15) | (readWriteBit(word & BIT(14)) << 14) | (readWriteBit(word & BIT(13)) << 13) | (readWriteBit(word & BIT(12)) << 12) | @@ -273,6 +310,225 @@ namespace spi { } + + +*/ + + + + + + + + + + + + + + + + + + + + + +template class SoftSPI { + + static constexpr const char* NAME = "softSPI"; + +public: + + /** ctor */ + SoftSPI() { + init(); + } + +private: + void init() { + debugMod(NAME, "init()"); + GPIO::setInput(PIN_MISO); + GPIO::setOutput(PIN_MOSI); + GPIO::setOutput(PIN_CLK); + } + +private: + + static inline void wait() { + if (!fast) { + for (int i = 0; i < 8; ++i) { + __asm__ __volatile__("nop"); + } + } + } + + inline void clkLo() { GPIO::clear(PIN_CLK); } + inline void clkHi() { GPIO::set(PIN_CLK); } + + /** write one bit to the bus */ + inline void writeBit(const bool out) { + if(out) {GPIO::set(PIN_MOSI);} else {GPIO::clear(PIN_MOSI);} + wait(); + clkHi(); + wait(); + clkLo(); + wait(); + } + + /** read one bit from the bus */ + inline uint8_t readBit() { + clkHi(); + wait(); + const bool val = GPIO::get(PIN_MISO); + wait(); + clkLo(); + wait(); + return (val) ? 1 : 0; + } + + inline uint8_t readWriteBit(const bool out) { + if(out) {GPIO::set(PIN_MOSI);} else {GPIO::clear(PIN_MOSI);} + wait(); + clkHi(); + wait(); + const bool inp = GPIO::get(PIN_MISO); + wait(); + clkLo(); + wait(); + return (inp) ? 1 : 0; + } + +public: + + void IRAM_ATTR writeQuad(const uint32_t quad) { +// writeBit(quad & BIT(31)); +// writeBit(quad & BIT(30)); +// writeBit(quad & BIT(29)); +// writeBit(quad & BIT(28)); +// writeBit(quad & BIT(27)); +// writeBit(quad & BIT(26)); +// writeBit(quad & BIT(25)); +// writeBit(quad & BIT(24)); +// writeBit(quad & BIT(23)); +// writeBit(quad & BIT(22)); +// writeBit(quad & BIT(21)); +// writeBit(quad & BIT(20)); +// writeBit(quad & BIT(19)); +// writeBit(quad & BIT(18)); +// writeBit(quad & BIT(17)); +// writeBit(quad & BIT(16)); +// writeBit(quad & BIT(15)); +// writeBit(quad & BIT(14)); +// writeBit(quad & BIT(13)); +// writeBit(quad & BIT(12)); +// writeBit(quad & BIT(11)); +// writeBit(quad & BIT(10)); +// writeBit(quad & BIT( 9)); +// writeBit(quad & BIT( 8)); +// writeBit(quad & BIT( 7)); +// writeBit(quad & BIT( 6)); +// writeBit(quad & BIT( 5)); +// writeBit(quad & BIT( 4)); +// writeBit(quad & BIT( 3)); +// writeBit(quad & BIT( 2)); +// writeBit(quad & BIT( 1)); +// writeBit(quad & BIT( 0)); + writeByte((quad>>24)&0xFF); + writeByte((quad>>16)&0xFF); + writeByte((quad>> 8)&0xFF); + writeByte((quad>> 0)&0xFF); + } + + void IRAM_ATTR writeWord(const uint16_t word) { +// writeBit(word & BIT(15)); +// writeBit(word & BIT(14)); +// writeBit(word & BIT(13)); +// writeBit(word & BIT(12)); +// writeBit(word & BIT(11)); +// writeBit(word & BIT(10)); +// writeBit(word & BIT( 9)); +// writeBit(word & BIT( 8)); +// writeBit(word & BIT( 7)); +// writeBit(word & BIT( 6)); +// writeBit(word & BIT( 5)); +// writeBit(word & BIT( 4)); +// writeBit(word & BIT( 3)); +// writeBit(word & BIT( 2)); +// writeBit(word & BIT( 1)); +// writeBit(word & BIT( 0)); + writeByte((word>>8)&0xFF); + writeByte((word>>0)&0xFF); + } + + void IRAM_ATTR writeByte(uint8_t byte) { + writeBit(byte & BIT( 7)); + writeBit(byte & BIT( 6)); + writeBit(byte & BIT( 5)); + writeBit(byte & BIT( 4)); + writeBit(byte & BIT( 3)); + writeBit(byte & BIT( 2)); + writeBit(byte & BIT( 1)); + writeBit(byte & BIT( 0)); + } + + /** read 8 bits */ + uint8_t readByte() { + GPIO::clear(PIN_MOSI); + return + (readBit() << 7) | + (readBit() << 6) | + (readBit() << 5) | + (readBit() << 4) | + (readBit() << 3) | + (readBit() << 2) | + (readBit() << 1) | + (readBit() << 0); + } + + uint8_t readWriteByte(const uint8_t byte) { + return + (readWriteBit(byte & BIT( 7)) << 7) | + (readWriteBit(byte & BIT( 6)) << 6) | + (readWriteBit(byte & BIT( 5)) << 5) | + (readWriteBit(byte & BIT( 4)) << 4) | + (readWriteBit(byte & BIT( 3)) << 3) | + (readWriteBit(byte & BIT( 2)) << 2) | + (readWriteBit(byte & BIT( 1)) << 1) | + (readWriteBit(byte & BIT( 0)) << 0); + } + + uint32_t readWriteQuad(const uint32_t quad) { + return + readWriteByte((quad>>24)&0xFF) << 24 | + readWriteByte((quad>>16)&0xFF) << 16 | + readWriteByte((quad>> 8)&0xFF) << 8 | + readWriteByte((quad>> 0)&0xFF) << 0; + } + + + void write(const uint8_t* data, size_t len) { + while(len) { + writeByte(*data); + ++data; + --len; + } + } + + void read(uint8_t* dst, size_t len) { + while(len) { + *dst = readByte(); + ++dst; + --len; + } + } + + +}; + + +#pragma GCC pop_options + // create an instance ONCE //SoftSPI spi; //using spi = SoftSPI; diff --git a/net/IP.h b/net/IP.h index f23030e..f8f0687 100644 --- a/net/IP.h +++ b/net/IP.h @@ -13,17 +13,17 @@ ip_addr_t addr; /** empty ctor */ - explicit IP() { + explicit IP4() { addr.addr = 0; } /** ctor with IP-string */ - explicit IP(const char* ipStr) { + explicit IP4(const char* ipStr) { set(ipStr); } /** ctor with ip_addr_t */ - explicit IP(ip_addr addr) : addr(addr) { + explicit IP4(ip_addr addr) : addr(addr) { ; } diff --git a/net/UDP.h b/net/UDP.h index 3c4e08c..8d22cfd 100644 --- a/net/UDP.h +++ b/net/UDP.h @@ -46,7 +46,7 @@ public: } - bool send(const IP ip, const Port port, const void* data, const uint16_t dataLen) { + bool send(const IP4 ip, const Port port, const void* data, const uint16_t dataLen) { debugMod1(NAME, "sending packet to remote port %d", port); @@ -65,9 +65,21 @@ public: /** set the callback to call whenever a packet is received */ void setRecvCallback(UDPCallback callback) { + debugMod(NAME, "setRecvCallback()"); espconn_regist_recvcb(con, callback); } + /** get the IP address of the most recent packet's sender */ + IP4 getSenderIP() { + + remot_info* rem; + espconn_get_connection_info(con, &rem, 0); + ip_addr_t* _ip = (ip_addr_t*) rem->remote_ip; + return IP4(*_ip); + //const uint16_t port = rem->remote_port; + + } + private: /** initialize the UDP "connection" */