From ccd7f119d37f21943599385866e92d63ea1cbdfe Mon Sep 17 00:00:00 2001 From: kazu Date: Wed, 24 Jun 2020 21:28:44 +0200 Subject: [PATCH] worked on SPI, fixed some bugs adjusted LCD code added code for INA3221 worked on UI --- ext/lcd/ST7735.h | 120 +++++++++++++++++++++-------------------- ext/lcd/ui/UI.h | 25 +++++++-- ext/lcd/ui/UIButton.h | 9 +++- ext/lcd/ui/UIElement.h | 16 +++++- ext/lcd/ui/UILabel.h | 17 +++--- ext/rf/SX1276.h | 2 + ext/sens/INA3221.h | 89 ++++++++++++++++++++++++++++++ io/GPIO.h | 22 +++++++- io/HardSPI.h | 28 +++++++++- io/SoftI2C.h | 16 +++--- io/SoftSPI.h | 2 +- 11 files changed, 258 insertions(+), 88 deletions(-) create mode 100644 ext/sens/INA3221.h diff --git a/ext/lcd/ST7735.h b/ext/lcd/ST7735.h index dbd1918..9e8a525 100644 --- a/ext/lcd/ST7735.h +++ b/ext/lcd/ST7735.h @@ -84,80 +84,88 @@ public: private: -#define V 128 -#define H 160 + static constexpr const int V = 128; + static constexpr const int H = 160; - void waitLong() { - for (int i = 0; i < 50; ++i) { - //os_delay_us(10*1000); - vTaskDelay(10 / portTICK_PERIOD_MS); - } + inline void waitLong() { + //vTaskDelay(200 / portTICK_PERIOD_MS); + for (int i = 0; i < 0x1FFFFF; ++i) {asm("nop");} // vTaskDelay seems not to block? maybe when display is allocated outside of a function? } - void waitShort() { - //os_delay_us(10*1000); - vTaskDelay(10 / portTICK_PERIOD_MS); + inline void waitShort() { + //vTaskDelay(20 / portTICK_PERIOD_MS); + for (int i = 0; i < 0xFFFF; ++i) {asm("nop");} } void init() { - //spi::init(); + debugMod(NAME, "init()"); + + select(); + sendCommand(ST7735_SWRESET); // 1: Software reset, 0 args + deselect(); + waitLong(); + + select(); + sendCommand(ST7735_SLPOUT); // 2: Out of sleep mode, 0 args + deselect(); + waitLong(); 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) - + waitShort(); + 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) - + waitShort(); + 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 - + waitShort(); + 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 - + waitShort(); + sendCommand(ST7735_COLMOD); // 15: set color mode); 1 arg); no delay: sendData(0x05); // 16-bit color + deselect(); + + // required??? adjusts colors or something??? + if (1 == 1) { - // required??? - if (1 == 1) { - + select(); 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); @@ -169,17 +177,20 @@ private: sendData(0x2E); sendData(0x2C); sendData(0x29); sendData(0x2D); sendData(0x2E); sendData(0x2E); sendData(0x37); sendData(0x3F); sendData(0x00); sendData(0x00); sendData(0x02); sendData(0x10); + deselect(); + + } - } - + select(); sendCommand(ST7735_NORON); // 3: Normal display on, no args - waitShort(); - - sendCommand(ST7735_DISPON); // 4: Main screen turn on, no args - waitLong(); - deselect(); + waitLong(); + select(); + sendCommand(ST7735_DISPON); // 4: Main screen turn on, no args + deselect(); + waitLong(); + debugMod(NAME, "init() done"); } @@ -223,21 +234,22 @@ public: select(); - sendCommand(ST7735_CASET); // Column addr set + sendCommand(ST7735_CASET); // Column addr set sendData(0x00); - sendData(0); // XSTART + sendData(0); // XSTART sendData(0x00); - sendData(V-1); // XEND + sendData(V-1); // XEND - sendCommand(ST7735_RASET); // Row addr set + sendCommand(ST7735_RASET); // Row addr set sendData(0x00); - sendData(0); // YSTART + sendData(0); // YSTART sendData(0x00); - sendData(H-1); // YEND + sendData(H-1); // YEND sendCommand(ST7735_RAMWR); modeData(); + /* for (int i = 0; i < (V*H); ++i) { const uint16_t color = data[i]; //const uint8_t lo = color & 0xFF; @@ -246,7 +258,9 @@ public: //sendData(hi); //spi::writeWord(color); spi.writeWord(color); - } + }*/ + spi.write((const uint8_t*)data, V*H*sizeof(uint16_t)); + deselect(); @@ -258,43 +272,31 @@ private: void sendData(uint8_t data) { modeData(); - //select(); - //spi::writeByte(data); spi.writeByte(data); - //deselect(); } void sendCommand(uint8_t cmd) { modeCommand(); - //select(); - //spi::writeByte(cmd); spi.writeByte(cmd); - //deselect(); } /** switch D/C line low */ inline void modeCommand() { - //GPIO5_OUTPUT_SET; - //GPIO5_L; MyGPIO::clear(PIN_DATA_COMMAND); } /** switch D/C line high */ inline void modeData() { - //GPIO5_OUTPUT_SET; - //GPIO5_H; MyGPIO::set(PIN_DATA_COMMAND); } /** set CS = low */ inline void select() { MyGPIO::clear(PIN_CS); - //spi::chipSelect(); } /** set CS = high */ inline void deselect() { - //spi::chipDeselect(); MyGPIO::set(PIN_CS); } diff --git a/ext/lcd/ui/UI.h b/ext/lcd/ui/UI.h index 5475164..17aec06 100644 --- a/ext/lcd/ui/UI.h +++ b/ext/lcd/ui/UI.h @@ -44,15 +44,16 @@ class UI { UIPainter p; UIElement root; - UIElement* eFocused = nullptr; + int focusIdx = 0; + //UIElement* eFocused = nullptr; UIElement* eDown = nullptr; Color cBackground = Color::fromRGB(180,240,180); public: - UI() { - root.setRect(0,0,240,320); + UI(int w, int h) { + root.setRect(0,0,w,h); root.setVisible(true); p.setFG(cBackground); p.fillRect(root.getRect()); @@ -77,7 +78,6 @@ public: } - } void onTouchDone() { @@ -87,13 +87,28 @@ public: } } - + void focusNext() { + root.children[focusIdx]->setFocus(false); + focusIdx = (focusIdx + 1) % root.children.size(); + root.children[focusIdx]->setFocus(true); + } + void focusPrev() { + root.children[focusIdx]->setFocus(false); + focusIdx = ((focusIdx - 1) + root.children.size()) % root.children.size(); + root.children[focusIdx]->setFocus(true); + } + void draw() { //debugMod1("UI", "draw %zu elements", elements.size()); // for (UIElement* e : elements) { // e->_draw(p); // } + + //p.setFG(cBackground); + //p.fillRect(root.getRect()); + root._draw(p); + } }; diff --git a/ext/lcd/ui/UIButton.h b/ext/lcd/ui/UIButton.h index a70d4a9..e039152 100644 --- a/ext/lcd/ui/UIButton.h +++ b/ext/lcd/ui/UIButton.h @@ -23,6 +23,9 @@ private: const Color fillNormal = Color::fromRGB(180,180,180); const Color fillDown = Color::fromRGB(120,120,120); + const Color fillFocus = Color::fromRGB(120,120,140); + + const Color frameBright = Color::fromRGB(230,230,230); const Color frameDark = Color::fromRGB(50,50,50); @@ -58,7 +61,11 @@ public: void draw(UIPainter& p) override { - p.setFG( down ? fillDown : fillNormal ); + if (focus) { + p.setFG( fillFocus ); + } else { + p.setFG( down ? fillDown : fillNormal ); + } p.fillRect(rect); p.setFG( down ? frameDark : frameBright ); diff --git a/ext/lcd/ui/UIElement.h b/ext/lcd/ui/UIElement.h index 19be6cd..a77db68 100644 --- a/ext/lcd/ui/UIElement.h +++ b/ext/lcd/ui/UIElement.h @@ -15,13 +15,20 @@ protected: UIRect rect; bool _visible = true; bool _needsRedraw = true; - + + bool opaque = true; + bool focus = false; + std::vector children; static constexpr const char* TAG = "UIElement"; public: + void setOpaque(bool opaque) { + this->opaque = opaque; + } + void setRect(const UIRect r) { this->rect = r; setNeedsRedraw(); @@ -44,6 +51,11 @@ public: return this->_visible; } + void setFocus(bool focus) { + this->focus = focus; + setNeedsRedraw(); + } + void setNeedsRedraw() { this->_needsRedraw = true; } @@ -99,7 +111,7 @@ protected: void _draw(UIPainter& p) { - // if hidde, ignore for me and children + // if hiden, ignore for me and children if (!_visible) {return;} // draw myself (if needed) diff --git a/ext/lcd/ui/UILabel.h b/ext/lcd/ui/UILabel.h index d19915b..700f018 100644 --- a/ext/lcd/ui/UILabel.h +++ b/ext/lcd/ui/UILabel.h @@ -11,7 +11,7 @@ class UILabel : public UIElement { std::string txt; - + Color cBackground = Color::fromRGB(255,255,255); Color cText = Color::fromRGB(0,0,0); @@ -21,18 +21,21 @@ public: ; } + /** set the text to draw */ void setText(const std::string& txt) { this->txt = txt; setNeedsRedraw(); } + + - void draw(UIPainter& p) { + /** draw the label */ + void draw(UIPainter& p) override { - p.setFG(cBackground); - p.fillRect(rect); - - //const uint16_t txtW = fnt_f1.getWidth(txt); - //const uint16_t txtH = fnt_f1.getHeight(); + if (opaque) { + p.setFG(cBackground); + p.fillRect(rect); + } p.setFG(cText); p.drawText(rect.x, rect.y, txt.c_str()); diff --git a/ext/rf/SX1276.h b/ext/rf/SX1276.h index d6d02a0..b82997b 100644 --- a/ext/rf/SX1276.h +++ b/ext/rf/SX1276.h @@ -380,9 +380,11 @@ private: /** check the chip's version */ void check() { + idle(); uint8_t version = readRegister(REG_VERSION); debugMod1(NAME, "version %d", version); if (version != 0x12) {debugMod(NAME, "!! unsupported chip version detected");} + sleep(); } /** what exactly is this? */ diff --git a/ext/sens/INA3221.h b/ext/sens/INA3221.h new file mode 100644 index 0000000..f3cb60c --- /dev/null +++ b/ext/sens/INA3221.h @@ -0,0 +1,89 @@ +#ifndef SENS_INA_3221 +#define SENS_INA_3221 + +/** 3-Channel i2c volt/ampere sensor */ + +template class INA3221 { + +private: + + I2C& i2c; + static constexpr const uint8_t ADDR = 0b1000000; + static constexpr const char* NAME = "INA3221"; + + static constexpr const uint8_t REG_CFG = 0x00; + + static constexpr const uint8_t REG_CH1_VS = 0x01; // shunt voltage + static constexpr const uint8_t REG_CH1_VB = 0x02; // bus voltage + + static constexpr const uint8_t REG_CH2_VS = 0x03; + static constexpr const uint8_t REG_CH2_VB = 0x04; + + static constexpr const uint8_t REG_CH3_VS = 0x05; + static constexpr const uint8_t REG_CH3_VB = 0x06; + +public: + + struct Channel { + int16_t vShunt; + int16_t vBus; + }; + struct Voltages { + Channel ch1; + Channel ch2; + Channel ch3; + }; + +public: + + INA3221(I2C& i2c) : i2c(i2c) { + + } + + /** get the current config */ + uint16_t getConfig() { + uint16_t res = 0; + i2c.readReg(ADDR, REG_CFG, 2, (uint8_t*)&res); + return res; + } + + /** read all 6 voltages (3 channels, each shunt and bus) */ + Voltages getVoltages() { + + uint8_t buf[2]; + Voltages res; + + i2c.readReg(ADDR, REG_CH1_VS, sizeof(buf), buf); + res.ch1.vShunt = getVoltage(buf); + i2c.readReg(ADDR, REG_CH1_VB, sizeof(buf), buf); + res.ch1.vBus = getVoltage(buf); + + i2c.readReg(ADDR, REG_CH2_VS, sizeof(buf), buf); + res.ch2.vShunt = getVoltage(buf); + i2c.readReg(ADDR, REG_CH2_VB, sizeof(buf), buf); + res.ch2.vBus = getVoltage(buf); + + i2c.readReg(ADDR, REG_CH3_VS, sizeof(buf), buf); + res.ch3.vShunt = getVoltage(buf); + i2c.readReg(ADDR, REG_CH3_VB, sizeof(buf), buf); + res.ch3.vBus = getVoltage(buf); + + return res; + } + + /** is an INA3221 present on the bus? */ + bool isPresent() { + return i2c.query(ADDR); + } + +private: + + /** convert to bytes into a mV reading */ + int16_t getVoltage(const uint8_t* buf) { + return ((buf[0] << 8) | (buf[1] << 0)); // the lowest 3 bits are always zero. no shift required! + } + +}; + + +#endif // SENS_INA_3221 diff --git a/io/GPIO.h b/io/GPIO.h index 85e6784..d6ea87f 100644 --- a/io/GPIO.h +++ b/io/GPIO.h @@ -98,6 +98,11 @@ #include "driver/gpio.h" + // NOTE from the manual + // GPIO 6-11 are usually used for SPI flash. + // GPIO 34-39 can only be set as input mode and do not have software pullup or pulldown functions. + + struct MyGPIO { static inline bool get(const uint8_t num) { @@ -134,7 +139,7 @@ io_conf.pin_bit_mask = (1< 0) { + + // ensure the transaction is reset to defaults. + // this is mandatory! as the ESP adjusts some parameters while/after sending! + memset(&t, 0, sizeof(spi_transaction_t)); const size_t chunkLen = min(MAX_LEN, (len)); t.length = 8 * chunkLen; // in bits @@ -136,6 +139,23 @@ public: } + /** send 4 bytes onto the bus while reading 4 bytes response */ + uint8_t readWriteByte(uint8_t write) { + + spi_transaction_t t; + memset(&t, 0, sizeof(spi_transaction_t)); + t.length = 8*1; + t.rxlength = 8*1; + t.tx_data[0] = (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])); + + } + /** send 4 bytes onto the bus while reading 4 bytes response */ uint32_t readWriteQuad(uint32_t write) { @@ -220,6 +240,10 @@ public: ESP_ERROR_CHECK(ret); } + + static size_t min(size_t a, size_t b) { + return (a < b) ? a : b; + } diff --git a/io/SoftI2C.h b/io/SoftI2C.h index 6cbff65..f22d7b8 100644 --- a/io/SoftI2C.h +++ b/io/SoftI2C.h @@ -24,8 +24,7 @@ template class SoftI2C { inline void sclLo() {MyGPIO::clear(PIN_SCL);} inline void waitLong() { - for (uint8_t i = 0; i < 240; ++i) { - __asm__ __volatile__("nop"); + for (uint16_t i = 0; i < 1024; ++i) { __asm__ __volatile__("nop"); } } @@ -33,7 +32,6 @@ template class SoftI2C { inline void waitShort() { for (uint8_t i = 0; i < 240; ++i) { __asm__ __volatile__("nop"); - __asm__ __volatile__("nop"); } } @@ -156,14 +154,14 @@ public: // select register(s) to read ok = startWrite(addr); - if (!ok) {debugMod(NAME, "failed start write1\n"); return false;} + if (!ok) {debugMod(NAME, "failed start write(1)"); return false;} ok = writeByteAndCheck(reg); - if (!ok) {debugMod(NAME, "failed select register\n"); return false;} + if (!ok) {debugMod(NAME, "failed select register"); return false;} stop(); // read register(s) ok = startRead(addr); - if (!ok) {debugMod(NAME, "failed start write2\n"); return false;} + if (!ok) {debugMod(NAME, "failed start write(2)"); return false;} readBytes(dst, len); stop(); @@ -178,11 +176,11 @@ public: // address the slave in write mode and select the first register to read ok = startWrite(addr); - if (!ok) {debugMod(NAME, "failed start write\n"); return false;} + if (!ok) {debugMod(NAME, "failed start write"); return false;} ok = writeByteAndCheck(reg); - if (!ok) {debugMod1(NAME, "failed to select register %d\n", addr); return false;} + if (!ok) {debugMod1(NAME, "failed to select register %d", addr); return false;} ok = writeBytesAndCheck(src, len); - if (!ok) {debugMod(NAME, "failed to write register contents \n"); return false;} + if (!ok) {debugMod(NAME, "failed to write register contents"); return false;} // done stop(); diff --git a/io/SoftSPI.h b/io/SoftSPI.h index ebf17ee..6b18d72 100644 --- a/io/SoftSPI.h +++ b/io/SoftSPI.h @@ -8,7 +8,7 @@ #pragma GCC push_options -#pragma GCC optimize ("O3") +#pragma GCC optimize ("O2") template class SoftSPI {