diff --git a/Platforms.h b/Platforms.h index d7f0800..2f91052 100644 --- a/Platforms.h +++ b/Platforms.h @@ -23,8 +23,8 @@ #define IN_FLASH ICACHE_FLASH_ATTR #endif -#define min(a,b) (ab) ? (a) : (b) +//#define min(a,b) (ab) ? (a) : (b) //template inline T min(const T a, const T b) { // return (a #include diff --git a/data/RingBuffer.h b/data/RingBuffer.h index 22a7474..8fda0d7 100644 --- a/data/RingBuffer.h +++ b/data/RingBuffer.h @@ -108,7 +108,7 @@ public: Scalar get() { const Scalar res = data[tail]; tail = (tail + 1) % size; - --used; + if (used>0) {--used;} return res; } diff --git a/ext/lcd/Draw.h b/ext/lcd/Draw.h index d1244b6..2152e94 100644 --- a/ext/lcd/Draw.h +++ b/ext/lcd/Draw.h @@ -41,7 +41,8 @@ public: /** 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]; + const int i = c-off; + return offsets[i+1] - offsets[i]; } uint8_t getHeight() const { @@ -57,8 +58,8 @@ public: return sum; } - /** draw the given char at the given position */ - template void draw(const char* c, Scalar dx, Scalar dy, Destination& dst) { + /** draw the given string at the given position */ + template void draw(const char* c, Scalar dx, Scalar dy, Destination& dst) const { while(*c) { draw(*c, dx, dy, dst); dx += getCharWidht(*c);// + 1; @@ -67,11 +68,10 @@ public: } /** draw the given char at the given position */ - template void draw(unsigned char c, Scalar dx, Scalar dy, Destination& dst) { + template void draw(unsigned char c, Scalar dx, Scalar dy, Destination& dst) const { 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; @@ -80,8 +80,8 @@ public: 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) { + const uint8_t pxInFont = data[idx] & (1<<(x&7)); // pixel from font glyph + if (pxInFont) { dst.setPixel(dx+x-x1, dy+y); } } diff --git a/ext/lcd/SSD1306.h b/ext/lcd/SSD1306.h index fee11a0..4f66896 100644 --- a/ext/lcd/SSD1306.h +++ b/ext/lcd/SSD1306.h @@ -70,17 +70,23 @@ -class SSD1306 { +template class SSD1306 { private: static constexpr uint8_t ADDR7 = 0b0111100; bool inited = false; + I2C& i2c; + public: + SSD1306(I2C& i2c) : i2c(i2c) { + + } + bool isPresent() { - return i2c::query(ADDR7); + return i2c.query(ADDR7); } void initOnce() { @@ -137,15 +143,15 @@ public: // i2c::stop(); // } - i2c::startWrite(ADDR7); - bool ok = i2c::writeByteAndCheck(0x40); + 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.writeByteAndCheck(data[i]); } - i2c::stop(); + i2c.stop(); } @@ -240,13 +246,13 @@ private: void sendCommand(uint8_t cmd) { bool ok; - ok = i2c::startWrite(ADDR7); + ok = i2c.startWrite(ADDR7); if (!ok) {os_printf("failed start write\n");} - ok = i2c::writeByteAndCheck(0x00); // command + ok = i2c.writeByteAndCheck(0x00); // command if (!ok) {os_printf("failed command mode\n");} - ok = i2c::writeByteAndCheck(cmd); + ok = i2c.writeByteAndCheck(cmd); if (!ok) {os_printf("failed command\n");} - i2c::stop(); + i2c.stop(); } }; diff --git a/ext/led/WS2812B.h b/ext/led/WS2812B.h index 6028650..0f0e9b5 100644 --- a/ext/led/WS2812B.h +++ b/ext/led/WS2812B.h @@ -1,11 +1,10 @@ #ifndef WS2812B_H #define WS2812B_H - #include "../../data/Color.h" #include "../../Platforms.h" #include "../../Debug.h" - +#include "../../io/GPIO.h" @@ -17,9 +16,9 @@ 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 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) ) @@ -453,36 +452,21 @@ }; -#elif false //ESP32 +#elif ESP32 - #include - #include - #include - #include "driver/timer.h" + #include "../../io/I2S.h" - static constexpr timer_group_t grp = TIMER_GROUP_0; - static constexpr timer_idx_t idx = TIMER_0; + template class WS2812B { - template class WS2812B; - - static IRAM_ATTR void myISR(void* arg) { - printf("interrupt\n"); - //WS2812B* leds = (WS2812B*) arg; - //timer_pause(grp, idx); - //leds->flushOLD(); - } - - template class WS2812B { - - static constexpr const char* TAG = "WS2812"; - - /** enable/disable each led */ - bool enabled[numLEDs] = {true}; + static constexpr const char* TAG = "WS2818B"; /** color-value for each attached LED */ - Color colors[numLEDs]; + Color colors[NUM_LEDS]; - public: + /** enable/disable each led */ + bool enabled[NUM_LEDS] = {true}; + + public: /** ctor */ WS2812B() { @@ -490,13 +474,11 @@ } void init() { - - ESP_LOGI(TAG, "init()"); - - gpio_set_direction(outPin, GPIO_MODE_OUTPUT); - + debugMod1(TAG, "init with %d leds", NUM_LEDS); + cfg(); } + /** set the color for the given LED */ void setColor(const uint8_t idx, const Color rgb) { colors[idx] = rgb; @@ -504,7 +486,7 @@ /** set the color for all LEDs */ void setColor(const Color rgb) { - for (int idx = 0; idx < numLEDs; ++idx) { + for (int idx = 0; idx < NUM_LEDS; ++idx) { colors[idx] = rgb; } } @@ -516,7 +498,7 @@ /** enable/disable all LEDs */ void setEnabled(const bool en) { - for (int idx = 0; idx < numLEDs; ++idx) { + for (int idx = 0; idx < NUM_LEDS; ++idx) { enabled[idx] = en; } } @@ -527,78 +509,49 @@ } Color& getColor(const uint8_t idx) { - return colors[idx]; + return colors[idx]; } - - timer_config_t config; - /** flush configured changes */ - IRAM_ATTR void flush() { - - printf("flush()\n"); - - config.alarm_en = 1; - config.auto_reload = 1; - config.counter_dir = TIMER_COUNT_UP; - config.divider = 2; - config.intr_type = TIMER_INTR_LEVEL; - config.counter_en = TIMER_PAUSE; - - - - ESP_ERROR_CHECK(timer_init(grp, idx, &config)); - - ESP_ERROR_CHECK(timer_set_counter_value(grp, idx, 0)); - ESP_ERROR_CHECK(timer_set_alarm_value(grp, idx, 2)); - ESP_ERROR_CHECK(timer_enable_intr(grp, idx)); - ESP_ERROR_CHECK(timer_isr_register(grp, idx, myISR, (void*)this, ESP_INTR_FLAG_IRAM, NULL)); - - printf("start()\n"); - ESP_ERROR_CHECK(timer_start(grp, idx)); - + void flush() { + flushBrightness(255); } + void flushBrightness(const uint8_t brightness) { + portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; + portENTER_CRITICAL(&mux); - IRAM_ATTR void flushOLD() { - - ESP_LOGI(TAG, "sending %d LEDs", numLEDs); - - // important! otherwise often interrupted within - portDISABLE_INTERRUPTS(); - - //portMUX_TYPE myMutex = portMUX_INITIALIZER_UNLOCKED; - //portENTER_CRITICAL(&myMutex); + //for (volatile int i = 0; i < 256; ++i) {;} + //while (_getCycleCount() > 10000000) {;} + volatile uint8_t _x; + for (int i = 4; i >= 0; --i) {_x = colors[i].r;} // process each LED - for (int i = 0; i < numLEDs; ++i) { + for (int i = 0; i < NUM_LEDS; ++i) { - portDISABLE_INTERRUPTS(); - const volatile Color c = colors[i]; + _x = colors[i+1].r; - if (enabled[i]) { - sendByte(c.g); - sendByte(c.r); - sendByte(c.b); - } else { - sendByte(0); - sendByte(0); - sendByte(0); - } + // send each LEDs 24-bit GRB data + const Color rgb = colors[i].brightness(brightness); + sendByte(rgb.g); + sendByte(rgb.r); + sendByte(rgb.b); } - //portEXIT_CRITICAL(&myMutex); - - portENABLE_INTERRUPTS(); + portEXIT_CRITICAL(&mux); + delay5000(); } - private: + static inline uint32_t _getCycleCount(void) { + uint32_t ccount; + __asm__ __volatile__("rsr %0,ccount":"=a" (ccount)); + return ccount; + } - /** send one whole byte */ inline void sendByte(const uint8_t b) { if (b & 0b10000000) {send1();} else {send0();} if (b & 0b01000000) {send1();} else {send0();} @@ -610,300 +563,111 @@ if (b & 0b00000001) {send1();} else {send0();} } - /** send a 0 (short 1 pulse, long 0 pulse) */ - inline void send0() { - gpio_set_level(outPin, 1); - delayShort(); - gpio_set_level(outPin, 0); - delayLong(); - } - - /** send a 1 (long 1 pulse, short 0 pulse) */ inline void send1() { - gpio_set_level(outPin, 1); - delayLong(); - gpio_set_level(outPin, 0); - delayShort(); + MyGPIO::set(PIN_DATA); + delay800(); + MyGPIO::clear(PIN_DATA); + delay100(); } - inline void delayShort() { - volatile int i; - i = 0; i = 1; i = 2; i = 3; - (void) i; + inline void send0() { + MyGPIO::set(PIN_DATA); + delay100(); + MyGPIO::clear(PIN_DATA); + delay800(); } - inline void delayLong() { - volatile int j; - j = 0; j = 1; j = 2; j = 3; - j = 4; j = 5; j = 6; j = 7; - j = 8; j = 9; j = 1; j = 2; - (void) j; -// for (size_t i = 0; i < 16; ++i) { -// asm volatile("nop"); -// } + void delay100() { + //for (volatile uint8_t i = 0; i < 1; ++i) {;} + const uint32_t start = _getCycleCount(); + while (_getCycleCount() < start + 10) {;} + } + + void delay800() { + //for (volatile uint8_t i = 0; i < 10; ++i) {;} + const uint32_t start = _getCycleCount(); + while (_getCycleCount() < start + 100) {;} + } + + void delay5000() { + //for (volatile uint8_t i = 0; i < 10; ++i) {;} + const uint32_t start = _getCycleCount(); + while (_getCycleCount() < start + 1000) {;} + } + + void cfg() { + MyGPIO::setOutput(PIN_DATA); } - }; + #ifdef xxx -#elif false //othertest + /** comm via I2S */ + using MyI2S = I2S; + MyI2S i2s; -//#include -#include -#include -#include -#include - -template class WS2812B { - - static constexpr const char* TAG = "WS2812"; - static constexpr rmt_channel_t chan = RMT_CHANNEL_0; - - //#define portDISABLE_INTERRUPTS() do { XTOS_SET_INTLEVEL(XCHAL_EXCM_LEVEL); portbenchmarkINTERRUPT_DISABLE(); } while (0) - //#define portENABLE_INTERRUPTS() do { portbenchmarkINTERRUPT_RESTORE(0); XTOS_SET_INTLEVEL(0); } while (0) - //#define ENABLE_INTERRUPTS() portENABLE_INTERRUPTS() - //#define DISABLE_INTERRUPTS() portDISABLE_INTERRUPTS() - - static constexpr int numBits = (3*8) * 2; // (r,g,b) each 8 bit NOTE! must be < 64 to fit into the buffer! - //static constexpr int numItems = 256;//(numLEDs*3)*8; - //static constexpr int numItems = (numLEDs*3)*8; - rmt_item32_t items[numBits+1]; // + one entry has two bits + 0-terminator (just like within strings) - - /** enable/disable each led */ - bool enabled[numLEDs] = {true}; - - /** color-value for each attached LED */ - Color colors[numLEDs]; - - public: - - /** ctor */ - WS2812B() { - init(); - } - - void init() { - - ESP_LOGI(TAG, "init()"); - - rmt_config_t cfg; - cfg.rmt_mode = RMT_MODE_TX; - cfg.channel = chan; - cfg.gpio_num = outPin; - cfg.mem_block_num = 1;//chan+1;//8-chan;//chan+1;//8 - chan; //chan+1;// - cfg.clk_div = 8; - cfg.tx_config.loop_en = false; - cfg.tx_config.carrier_en = false; - cfg.tx_config.idle_output_en = true; - cfg.tx_config.idle_level = (rmt_idle_level_t)0; - cfg.tx_config.carrier_freq_hz = 10000; - cfg.tx_config.carrier_level = (rmt_carrier_level_t)1; - cfg.tx_config.carrier_duty_percent = 50; - - ESP_ERROR_CHECK(rmt_config(&cfg)); - - // disable when using custom ISR - //ESP_ERROR_CHECK(rmt_driver_install(chan, 0, 0)); - - // setup constant values once - for (int idx = 0; idx < numBits; ++idx) { - items[idx].duration0 = 0; - items[idx].level0 = 1; - items[idx].duration1 = 0; - items[idx].level1 = 0; - } - items[numBits].val = 0; // 0 terminator - - ESP_LOGI(TAG, "init OK!"); - - } - - /** set the color for the given LED */ - void setColor(const uint8_t idx, const Color rgb) { - colors[idx] = rgb; - } - - /** set the color for all LEDs */ - void setColor(const Color rgb) { - for (int idx = 0; idx < numLEDs; ++idx) { - colors[idx] = rgb; - } - } - - /** enable/disable the given LED */ - void setEnabled(const uint8_t idx, const bool en) { - enabled[idx] = en; - } - - /** enable/disable all LEDs */ - void setEnabled(const bool en) { - for (int idx = 0; idx < numLEDs; ++idx) { - enabled[idx] = en; - } - } - - /** is the given LED enabled? */ - bool isEnabled(const uint8_t idx) const { - return enabled[idx]; - } - - Color& getColor(const uint8_t idx) { - return colors[idx]; - } - - int nextLED; - - /** flush configured changes */ - /* - void flush() { - - nextLED = 0; - ESP_LOGI(TAG, "sending %d LEDs", numLEDs); - - // important! otherwise often interrupted within - portDISABLE_INTERRUPTS(); - - for (int i = 0; i < 1024; ++i) { - asm volatile("nop"); + void cfg() { + const uint32_t sRate = 1000*1000 / (1.0) / 2 / 2; + i2s.configure(sRate, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_STEREO); } - //waitForTX(); + /** flush configured changes, including global brightness */ + void flushBrightness(const uint8_t brightness) { - const size_t toWrite = 8+8+8+1;// 8-bit R, 8-bit G, 8-bit B, null-terminator + // seems important for i2s ? + reset(); + uint8_t tmp[8*3]; - // add null terminator - items[toWrite-1].val = 0; + // process each LED + for (int i = 0; i < NUM_LEDS; ++i) { - // process each LED - for (int i = 0; i < numLEDs; i+=2) { + // send each LEDs 24-bit GRB data + if (enabled[i]) { + const Color rgb = colors[i].brightness(brightness); + sendByte(rgb.g, &tmp[0]); + sendByte(rgb.r, &tmp[8]); + sendByte(rgb.b, &tmp[16]); + } else { + sendByte(0, &tmp[0]); + sendByte(0, &tmp[8]); + sendByte(0, &tmp[16]); + } - enqueueNext(); + i2s.add(tmp, sizeof(tmp)); - // add null terminator - //items[idx].val = 0; - - // wait for sending to complete using the RMT Unit's status - waitForTX(); - - ESP_ERROR_CHECK(rmt_fill_tx_items(this->chan, items, toWrite, 0)); - ESP_ERROR_CHECK(rmt_tx_start(this->chan, true)); - - // reset for next round - //idx = 0; + } + reset(); } - //waitForTX(); - - portENABLE_INTERRUPTS(); - - } - */ - - static constexpr int toWrite = 8+8+8+1; - rmt_isr_handle_t isrHandle; - - void flush() { - - nextLED = 0; - ESP_LOGI(TAG, "sending %d LEDs", numLEDs); - - enqueueNext(); - - ESP_ERROR_CHECK(rmt_isr_register(onISR, this, ESP_INTR_FLAG_LEVEL1, &isrHandle)); - ESP_ERROR_CHECK(rmt_set_tx_thr_intr_en(this->chan, true, toWrite/2)); - //ESP_ERROR_CHECK(rmt_set_tx_intr_en(this->chan, true)); - - printf("_start\n"); - ESP_ERROR_CHECK(rmt_tx_start(this->chan, true)); - - } - -private: - - static IRAM_ATTR void onISR(void* arg) { - printf(".\n"); - WS2812B* led = (WS2812B*) arg; - if (led->nextLED < numLEDs) { - led->enqueueNext(); - } else { - rmt_tx_stop(led->chan); - } - } - - void IRAM_ATTR enqueueNext() { - - printf("e\n"); - int idx = 0; - - if (enabled[nextLED]) { - emplaceByte(colors[nextLED].g, idx); - emplaceByte(colors[nextLED].r, idx); - emplaceByte(colors[nextLED].b, idx); - } else { - emplaceByte(0, idx); - emplaceByte(0, idx); - emplaceByte(0, idx); + void reset() { + const uint8_t lo[64] = { + 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,0,0,0,0,0, + }; + for (int i = 0; i < 8 ; ++i) { + i2s.add(lo, sizeof(lo)); + } } - ESP_ERROR_CHECK(rmt_fill_tx_items(this->chan, items, toWrite, 0)); + static constexpr const uint8_t DATA0 = 0b11100000; + static constexpr const uint8_t DATA1 = 0b11111000; - ++nextLED; - - } - - void waitForTX() { - - uint32_t status; // status while sending: 16797696 ?? - for (int i = 0; i < 2000; ++i) { - rmt_get_status(this->chan, &status); - if (status == 0) {break;} - //if (status != 16797696) {break;} - //if ( (status & (1<<24)) == 0 ) {break;} // somewhat OK + // send 1 LED byte. 1 bit to send = 8 byte i2s. also converts to little endian + inline void sendByte(const uint8_t b, uint8_t* dst) { + if (b & 0b10000000) {dst[1] = DATA1;} else {dst[1] = DATA0;} + if (b & 0b01000000) {dst[0] = DATA1;} else {dst[0] = DATA0;} + if (b & 0b00100000) {dst[3] = DATA1;} else {dst[3] = DATA0;} + if (b & 0b00010000) {dst[2] = DATA1;} else {dst[2] = DATA0;} + if (b & 0b00001000) {dst[5] = DATA1;} else {dst[5] = DATA0;} + if (b & 0b00000100) {dst[4] = DATA1;} else {dst[4] = DATA0;} + if (b & 0b00000010) {dst[7] = DATA1;} else {dst[7] = DATA0;} + if (b & 0b00000001) {dst[6] = DATA1;} else {dst[6] = DATA0;} } + #endif - -// for (int i = 0; i < 1000; ++i) { -// asm volatile("nop"); -// } - - } - - /** send one whole byte */ - inline void emplaceByte(const uint8_t b, int& idx) { - if (b & 0b10000000) {emplace1(idx);} else {emplace0(idx);} - if (b & 0b01000000) {emplace1(idx);} else {emplace0(idx);} - if (b & 0b00100000) {emplace1(idx);} else {emplace0(idx);} - if (b & 0b00010000) {emplace1(idx);} else {emplace0(idx);} - if (b & 0b00001000) {emplace1(idx);} else {emplace0(idx);} - if (b & 0b00000100) {emplace1(idx);} else {emplace0(idx);} - if (b & 0b00000010) {emplace1(idx);} else {emplace0(idx);} - if (b & 0b00000001) {emplace1(idx);} else {emplace0(idx);} - } - - /** emplace a 0 (short 1 pulse, long 0 pulse) */ - inline void emplace0(int& idx) { - items[idx].duration0 = 2;//1; - //items[idx].level0 = 1; - items[idx].duration1 = 8;//7; - //items[idx].level1 = 0; - ++idx; - } - - /** emplace a 1 (long 1 pulse, short 0 pulse) */ - inline void emplace1(int& idx) { - items[idx].duration0 = 7;//6; - //items[idx].level0 = 1; - items[idx].duration1 = 3;//2; - //items[idx].level1 = 0; - ++idx; - } - - -}; - - + }; #endif diff --git a/ext/poti/MCP42xxx.h b/ext/poti/MCP42xxx.h new file mode 100644 index 0000000..d06a47a --- /dev/null +++ b/ext/poti/MCP42xxx.h @@ -0,0 +1,33 @@ +#include "../../io/GPIO.h" +#include "../../Debug.h" + + +template class MCP42xxx { + +private: + + SPI& spi; + +public: + + /** ctor */ + MCP42xxx(SPI& spi) : spi(spi) { + ; + } + + void setVolume(const uint8_t vol) { + + debugMod1(TAG, "setVolume(%d)", vol); + + MyGPIO::setOutput(PIN_CS); + MyGPIO::clear(PIN_CS); + + const uint8_t cmd = 0b00010011; // xx 01=write xx 11=both + spi.writeByte(cmd); + spi.writeByte(vol); + + MyGPIO::set(PIN_CS); + + } + +}; diff --git a/ext/rf/SX1276.h b/ext/rf/SX1276.h new file mode 100644 index 0000000..bb60d97 --- /dev/null +++ b/ext/rf/SX1276.h @@ -0,0 +1,9 @@ +#ifndef RF_SX1276 +#define RF_SX1276 + +class SX1276 { + + +}; + +#endif diff --git a/ext/sens/BME280.h b/ext/sens/BME280.h index e7a09d3..843b998 100644 --- a/ext/sens/BME280.h +++ b/ext/sens/BME280.h @@ -1,9 +1,10 @@ #ifndef SENS_BME280 #define SENS_BME280 -#include "../../io/SoftI2C.h" +#include "../../Platforms.h" +#include "../../Debug.h" -class BME280 { +template class BME280 { static constexpr const char* NAME = "BME280"; @@ -53,11 +54,15 @@ public: } cal; + I2C& i2c; + BME280(I2C& i2c) : i2c(i2c) { + + } bool isPresent() { - return i2c::query(ADDR7); + return i2c.query(ADDR7); } private: @@ -178,20 +183,20 @@ public: bool ok; // address the slave in write mode and select the first register to read - ok = i2c::startWrite(ADDR7); + ok = i2c.startWrite(ADDR7); if (!ok) {os_printf("failed start write\n"); return false;} - ok = i2c::writeByteAndCheck(addr); + 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); + ok = i2c.startRead(ADDR7); if (!ok) {os_printf("failed start read\n"); return 0;} - i2c::readBytes(dst, len); + i2c.readBytes(dst, len); // done - i2c::stop(); + i2c.stop(); return true; } @@ -201,15 +206,15 @@ public: bool ok; // address the slave in write mode and select the first register to read - ok = i2c::startWrite(ADDR7); + ok = i2c.startWrite(ADDR7); if (!ok) {os_printf("failed start write\n"); return false;} - ok = i2c::writeByteAndCheck(addr); + ok = i2c.writeByteAndCheck(addr); if (!ok) {os_printf("failed to select register %d\n", addr); return false;} - ok = i2c::writeBytesAndCheck(src, len); + ok = i2c.writeBytesAndCheck(src, len); if (!ok) {os_printf("failed to write register contents \n"); return false;} // done - i2c::stop(); + i2c.stop(); return true; } diff --git a/ext/sens/MAX30102.h b/ext/sens/MAX30102.h new file mode 100644 index 0000000..b1208bd --- /dev/null +++ b/ext/sens/MAX30102.h @@ -0,0 +1,415 @@ +#ifndef MAX30102_H +#define MAX30102_H + +#include "../../Platforms.h" +//#include "../../io/HardI2C.h" +#include "../../io/SoftI2C.h" + +// https://www.roboter-bausatz.de/media/pdf/bf/52/37/RBS12853_MAX30102-DS-917698.pdf +// https://github.com/sparkfun/SparkFun_MAX3010x_Sensor_Library/blob/master/src/MAX30105.cpp +// https://github.com/vrano714/max30102-tutorial-raspberrypi/blob/master/max30102.py + +/** SPI-like touch controller */ +template class Max30102 { + +public: + + class Listener { + public: + virtual void onIR(const uint32_t ir) = 0; + virtual void onRed(const uint32_t red) = 0; + }; + +private: + + I2C& i2c; + + static constexpr const char* NAME = "Max30102"; + static constexpr uint8_t ADDR = 0b1010111; + + // Status Registers + static const uint8_t MAX30105_INTSTAT1 = 0x00; + static const uint8_t MAX30105_INTSTAT2 = 0x01; + static const uint8_t MAX30105_INTENABLE1 = 0x02; + static const uint8_t MAX30105_INTENABLE2 = 0x03; + + // FIFO Registers + static const uint8_t MAX30105_FIFOWRITEPTR = 0x04; + static const uint8_t MAX30105_FIFOOVERFLOW = 0x05; + static const uint8_t MAX30105_FIFOREADPTR = 0x06; + static const uint8_t MAX30105_FIFODATA = 0x07; + + // Configuration Registers + static const uint8_t MAX30105_FIFOCONFIG = 0x08; + static const uint8_t MAX30105_MODECONFIG = 0x09; + static const uint8_t MAX30105_PARTICLECONFIG = 0x0A; // Note, sometimes listed as "SPO2" config in datasheet (pg. 11) + static const uint8_t MAX30105_LED1_PULSEAMP = 0x0C; + static const uint8_t MAX30105_LED2_PULSEAMP = 0x0D; + //static const uint8_t MAX30105_LED3_PULSEAMP = 0x0E; // not available for MAX30102 + static const uint8_t MAX30105_LED_PROX_AMP = 0x10; + static const uint8_t MAX30105_MULTILEDCONFIG1 = 0x11; + static const uint8_t MAX30105_MULTILEDCONFIG2 = 0x12; + + // Die Temperature Registers + static const uint8_t MAX30105_DIETEMPINT = 0x1F; + static const uint8_t MAX30105_DIETEMPFRAC = 0x20; + static const uint8_t MAX30105_DIETEMPCONFIG = 0x21; + + // Proximity Function Registers + static const uint8_t MAX30105_PROXINTTHRESH = 0x30; + + // Part ID Registers + static const uint8_t MAX30105_REVISIONID = 0xFE; + static const uint8_t MAX30105_PARTID = 0xFF; // Should always be 0x15. Identical to MAX30102. + + // MAX30105 Commands + // Interrupt configuration (pg 13, 14) + static const uint8_t MAX30105_INT_A_FULL_MASK = (uint8_t)~0b10000000; + static const uint8_t MAX30105_INT_A_FULL_ENABLE = 0x80; + static const uint8_t MAX30105_INT_A_FULL_DISABLE = 0x00; + + static const uint8_t MAX30105_INT_DATA_RDY_MASK = (uint8_t)~0b01000000; + static const uint8_t MAX30105_INT_DATA_RDY_ENABLE = 0x40; + static const uint8_t MAX30105_INT_DATA_RDY_DISABLE = 0x00; + + static const uint8_t MAX30105_INT_ALC_OVF_MASK = (uint8_t)~0b00100000; + static const uint8_t MAX30105_INT_ALC_OVF_ENABLE = 0x20; + static const uint8_t MAX30105_INT_ALC_OVF_DISABLE = 0x00; + + static const uint8_t MAX30105_INT_PROX_INT_MASK = (uint8_t)~0b00010000; + static const uint8_t MAX30105_INT_PROX_INT_ENABLE = 0x10; + static const uint8_t MAX30105_INT_PROX_INT_DISABLE = 0x00; + + static const uint8_t MAX30105_INT_DIE_TEMP_RDY_MASK = (uint8_t)~0b00000010; + static const uint8_t MAX30105_INT_DIE_TEMP_RDY_ENABLE = 0x02; + static const uint8_t MAX30105_INT_DIE_TEMP_RDY_DISABLE = 0x00; + + static const uint8_t MAX30105_SAMPLEAVG_MASK = (uint8_t)~0b11100000; + static const uint8_t MAX30105_SAMPLEAVG_1 = 0x00; + static const uint8_t MAX30105_SAMPLEAVG_2 = 0x20; + static const uint8_t MAX30105_SAMPLEAVG_4 = 0x40; + static const uint8_t MAX30105_SAMPLEAVG_8 = 0x60; + static const uint8_t MAX30105_SAMPLEAVG_16 = 0x80; + static const uint8_t MAX30105_SAMPLEAVG_32 = 0xA0; + + static const uint8_t MAX30105_ROLLOVER_MASK = 0xEF; + static const uint8_t MAX30105_ROLLOVER_ENABLE = 0x10; + static const uint8_t MAX30105_ROLLOVER_DISABLE = 0x00; + + static const uint8_t MAX30105_A_FULL_MASK = 0xF0; + + // Mode configuration commands (page 19) + static const uint8_t MAX30105_SHUTDOWN_MASK = 0x7F; + static const uint8_t MAX30105_SHUTDOWN = 0x80; + static const uint8_t MAX30105_WAKEUP = 0x00; + + static const uint8_t MAX30105_RESET_MASK = 0xBF; + static const uint8_t MAX30105_RESET = 0x40; + + static const uint8_t MAX30105_MODE_MASK = 0xF8; + static const uint8_t MAX30105_MODE_REDONLY = 0x02; + static const uint8_t MAX30105_MODE_REDIRONLY = 0x03; + static const uint8_t MAX30105_MODE_MULTILED = 0x07; + + // Particle sensing configuration commands (pgs 19-20) + static const uint8_t MAX30105_ADCRANGE_MASK = 0x9F; + static const uint8_t MAX30105_ADCRANGE_2048 = 0x00; + static const uint8_t MAX30105_ADCRANGE_4096 = 0x20; + static const uint8_t MAX30105_ADCRANGE_8192 = 0x40; + static const uint8_t MAX30105_ADCRANGE_16384 = 0x60; + + static const uint8_t MAX30105_SAMPLERATE_MASK = 0xE3; + static const uint8_t MAX30105_SAMPLERATE_50 = 0x00; + static const uint8_t MAX30105_SAMPLERATE_100 = 0x04; + static const uint8_t MAX30105_SAMPLERATE_200 = 0x08; + static const uint8_t MAX30105_SAMPLERATE_400 = 0x0C; + static const uint8_t MAX30105_SAMPLERATE_800 = 0x10; + static const uint8_t MAX30105_SAMPLERATE_1000 = 0x14; + static const uint8_t MAX30105_SAMPLERATE_1600 = 0x18; + static const uint8_t MAX30105_SAMPLERATE_3200 = 0x1C; + + static const uint8_t MAX30105_PULSEWIDTH_MASK = 0xFC; + static const uint8_t MAX30105_PULSEWIDTH_69 = 0x00; + static const uint8_t MAX30105_PULSEWIDTH_118 = 0x01; + static const uint8_t MAX30105_PULSEWIDTH_215 = 0x02; + static const uint8_t MAX30105_PULSEWIDTH_411 = 0x03; + + //Multi-LED Mode configuration (pg 22) + static const uint8_t MAX30105_SLOT1_MASK = 0xF8; + static const uint8_t MAX30105_SLOT2_MASK = 0x8F; + static const uint8_t MAX30105_SLOT3_MASK = 0xF8; + static const uint8_t MAX30105_SLOT4_MASK = 0x8F; + + static const uint8_t SLOT_NONE = 0x00; + static const uint8_t SLOT_RED_LED = 0x01; + static const uint8_t SLOT_IR_LED = 0x02; + static const uint8_t SLOT_GREEN_LED = 0x03; + static const uint8_t SLOT_NONE_PILOT = 0x04; + static const uint8_t SLOT_RED_PILOT = 0x05; + static const uint8_t SLOT_IR_PILOT = 0x06; + static const uint8_t SLOT_GREEN_PILOT = 0x07; + + static const uint8_t MAX_30105_EXPECTEDPARTID = 0x15; + + Listener* listener = nullptr; + + +public: + + Max30102(I2C& i2c) : i2c(i2c) { + ; + } + + void setListener(Listener* l) { + this->listener = l; + } + + bool isPresent() { + return i2c.query(ADDR); + } + + uint8_t getRevisionID() { + return i2c.readReg8(ADDR, MAX30105_REVISIONID); + } + + uint8_t getPartID() { + return i2c.readReg8(ADDR, MAX30105_PARTID); + } + + + void softReset(void) { + + bitMask(MAX30105_MODECONFIG, MAX30105_RESET_MASK, MAX30105_RESET); + + // Poll for bit to clear, reset is then complete + for (int i = 0; i < 100; ++i) { + const uint8_t response = i2c.readReg8(ADDR, MAX30105_MODECONFIG); + if ((response & MAX30105_RESET) == 0) break; //We're done! + vTaskDelay(1 / portTICK_PERIOD_MS); + } + + ESP_LOGI(NAME, "softReset done"); + + } + +// void MAX30105::shutDown(void) { +// // Put IC into low power mode (datasheet pg. 19) +// // During shutdown the IC will continue to respond to I2C commands but will +// // not update with or take new readings (such as temperature) +// bitMask(MAX30105_MODECONFIG, MAX30105_SHUTDOWN_MASK, MAX30105_SHUTDOWN); +// } + + + + void setModePulse() { + + //wakeUp(); + softReset(); + //wakeUp(); + + writeReg8(MAX30105_INTENABLE1, 0x00);// | MAX30105_INT_DATA_RDY_ENABLE);//64);//16|64);//0xC0); + writeReg8(MAX30105_INTENABLE2, 0x00); + + //writeReg8(MAX30105_PROXINTTHRESH, 0); + //writeReg8(MAX30105_PifROXINTTHRESH, 64); + + + //setLEDMode(MAX30105_MODE_REDONLY); + setLEDMode(MAX30105_MODE_REDIRONLY); + //setLEDMode(MAX30105_MODE_MULTILED); + + writeReg8(MAX30105_FIFOCONFIG, MAX30105_SAMPLEAVG_8 | MAX30105_ROLLOVER_ENABLE); + + //setFIFOAverage(MAX30105_SAMPLEAVG_8); + setADCRange(MAX30105_ADCRANGE_16384); + setSampleRate(MAX30105_SAMPLERATE_200); + setPulseWidth(MAX30105_PULSEWIDTH_215); + + //Default is 0x1F which gets us 6.4mA + //powerLevel = 0x02, 0.4mA - Presence detection of ~4 inch + //powerLevel = 0x1F, 6.4mA - Presence detection of ~8 inch + //powerLevel = 0x7F, 25.4mA - Presence detection of ~8 inch + //powerLevel = 0xFF, 50.0mA - Presence detection of ~12 inch + + const int powerLevel = 0x30; + setPulseAmplitudeRed(powerLevel); + setPulseAmplitudeIR(powerLevel); + setPulseAmplitudeProximity(powerLevel); + + // THIS ONE ONLY WORKS FOR "MAX30105_MODE_MULTILED" ?! +// writeReg8(MAX30105_MULTILEDCONFIG1, (SLOT_RED_PILOT<<4) | (SLOT_RED_LED<<0) ); +// writeReg8(MAX30105_MULTILEDCONFIG2, (SLOT_IR_PILOT<<4) | (SLOT_IR_LED<<0) ); +// ------ +// writeReg8(MAX30105_MULTILEDCONFIG1, (SLOT_IR_LED<<4) | (SLOT_RED_LED<<0) ); +// writeReg8(MAX30105_MULTILEDCONFIG2, 0 ); + + clearFIFO(); + } + + void writeReg8(const uint8_t reg, const uint8_t val) { + ESP_LOGI(NAME, "write reg: 0x%02x <- val: %d", reg, val); + i2c.writeReg8(ADDR, reg, val); + } + + uint8_t readReg8(const uint8_t reg) { + return i2c.readReg8(ADDR, reg); + } + +// //Check for new data but give up after a certain amount of time +// //Returns true if new data was found +// //Returns false if new data was not found +// bool safeCheck(uint8_t maxTimeToCheck) { + +// for (int i = 0; i < maxTimeToCheck; ++i) { +// if(check() == true) { +// return(true); +// } +// vTaskDelay(1 / portTICK_PERIOD_MS); +// } +// return false; + +// } + + + void check(void) { + +// const uint8_t intr1 = readReg8(MAX30105_INTSTAT1); +// const uint8_t intr2 = readReg8(MAX30105_INTSTAT2); +// // new data available? +// //if (intr1 & 64) { .. } + + + uint8_t readPointer = getReadPointer(); + const uint8_t writePointer = getWritePointer(); + + //uint8_t readPointer = getReadPointer(); + //const uint8_t writePointer = getWritePointer(); + //ESP_LOGI(NAME, "intr. %d %d", readPointer, writePointer); + + while (readPointer != writePointer) { + + //printf(";%d;%d\n", readPointer, writePointer); + + uint8_t tmp[6]; + i2c.readReg(ADDR, MAX30105_FIFODATA, 6, tmp); + + const uint32_t val1 = ((tmp[0] << 16) | (tmp[1] << 8) | (tmp[2] << 0)) & 0x3FFFF; //Zero out all but 18 bits + const uint32_t val2 = ((tmp[3] << 16) | (tmp[4] << 8) | (tmp[5] << 0)) & 0x3FFFF; //Zero out all but 18 bits + + //printf(";%d;%d;%d;%d\n", readPointer, writePointer, val1, val2);//, val3, val4); + + // inc + readPointer = (readPointer + 1) % 32; + + // callback + if (listener) { + listener->onRed(val1); + listener->onIR(val2); + } + + } + + } + + + + + int8_t getTemp() { + + // trigger temp acquire + writeReg8(MAX30105_DIETEMPCONFIG, 1); + + int8_t full = readReg8(MAX30105_DIETEMPINT); + //uint8_f frac = i2c.readReg8(MAX30105_DIETEMPFRAC); + return full; + } + +private: + + void wakeUp(void) { + // Pull IC out of low power mode (datasheet pg. 19) + bitMask(MAX30105_MODECONFIG, MAX30105_SHUTDOWN_MASK, MAX30105_WAKEUP); + } + + void setLEDMode(uint8_t mode) { + // Set which LEDs are used for sampling -- Red only, RED+IR only, or custom. + // See datasheet, page 18 + bitMask(MAX30105_MODECONFIG, MAX30105_MODE_MASK, mode); + } + + + + void setPulseAmplitudeRed(uint8_t amplitude) { + writeReg8( MAX30105_LED1_PULSEAMP, amplitude); + } + + void setPulseAmplitudeIR(uint8_t amplitude) { + writeReg8(MAX30105_LED2_PULSEAMP, amplitude); + } + +// void setPulseAmplitudeGreen(uint8_t amplitude) { +// writeReg8(MAX30105_LED3_PULSEAMP, amplitude); +// } + + void setPulseAmplitudeProximity(uint8_t amplitude) { + writeReg8(MAX30105_LED_PROX_AMP, amplitude); + } + + void setFIFOAverage(uint8_t numberOfSamples) { + bitMask(MAX30105_FIFOCONFIG, MAX30105_SAMPLEAVG_MASK, numberOfSamples); + } + + void enableFIFORollover(void) { + bitMask(MAX30105_FIFOCONFIG, MAX30105_ROLLOVER_MASK, MAX30105_ROLLOVER_ENABLE); + } + + void setADCRange(uint8_t adcRange) { + // adcRange: one of MAX30105_ADCRANGE_2048, _4096, _8192, _16384 + bitMask(MAX30105_PARTICLECONFIG, MAX30105_ADCRANGE_MASK, adcRange); + } + + void setSampleRate(uint8_t sampleRate) { + // sampleRate: one of MAX30105_SAMPLERATE_50, _100, _200, _400, _800, _1000, _1600, _3200 + bitMask(MAX30105_PARTICLECONFIG, MAX30105_SAMPLERATE_MASK, sampleRate); + } + + void setPulseWidth(uint8_t pulseWidth) { + // pulseWidth: one of MAX30105_PULSEWIDTH_69, _188, _215, _411 + bitMask(MAX30105_PARTICLECONFIG, MAX30105_PULSEWIDTH_MASK, pulseWidth); + } + + + void clearFIFO(void) { + writeReg8(MAX30105_FIFOWRITEPTR, 0); + writeReg8(MAX30105_FIFOOVERFLOW, 0); + writeReg8(MAX30105_FIFOREADPTR, 0); + } + + //Read the FIFO Write Pointer + uint8_t getWritePointer(void) { + return readReg8(MAX30105_FIFOWRITEPTR); + } + + //Read the FIFO Read Pointer + uint8_t getReadPointer(void) { + return readReg8(MAX30105_FIFOREADPTR); + } + + //Given a register, read it, mask it, and then set the thing + void bitMask(const uint8_t reg, const uint8_t mask, const uint8_t thing) { + + const uint8_t orig = i2c.readReg8(ADDR, reg); + const uint8_t masked = orig & mask; + const uint8_t out = masked | thing; + + ESP_LOGI(NAME, "reg: %d - orig: %d new: %d", reg, orig, out); + + writeReg8(reg, out); + + } + + + +}; + +#endif // MAX30102_H diff --git a/io/GPIO.h b/io/GPIO.h index e102bc0..7fc23d9 100644 --- a/io/GPIO.h +++ b/io/GPIO.h @@ -8,7 +8,7 @@ #include "fastGPIO.h" - struct GPIO { + struct MyGPIO { static inline bool get(const uint8_t num) { return GPIO_INPUT_GET(num); @@ -64,6 +64,34 @@ } } + static inline void setPullUp(const uint8_t num) { + switch(num) { + case 0: GPIO0_INPUT_PULLUP_SET; break; + case 1: GPIO1_INPUT_PULLUP_SET; break; + case 2: GPIO2_INPUT_PULLUP_SET; break; + case 3: GPIO3_INPUT_PULLUP_SET; break; + case 4: GPIO4_INPUT_PULLUP_SET; break; + case 5: GPIO5_INPUT_PULLUP_SET; break; + //case 6: GPIO6_INPUT_PULLUP_SET; break; + //case 7: GPIO7_INPUT_PULLUP_SET; break; + //case 8: GPIO8_INPUT_PULLUP_SET; break; + case 9: GPIO9_INPUT_PULLUP_SET; break; + case 10: GPIO10_INPUT_PULLUP_SET; break; + //case 11: GPIO11_INPUT_PULLUP_SET; break; + case 12: GPIO12_INPUT_PULLUP_SET; break; + case 13: GPIO13_INPUT_PULLUP_SET; break; + case 14: GPIO14_INPUT_PULLUP_SET; break; + case 15: GPIO15_INPUT_PULLUP_SET; break; + } + } + + static void toggleBuiltInLED() { + static bool level = false; + setOutput(2); + level = !level; + if (level) {set(2);} else {clear(2);} + } + }; #elif ESP32 @@ -87,24 +115,40 @@ static inline void set(const uint8_t num) { WRITE_PERI_REG(GPIO_OUT_W1TS_REG, 1 << num); + //gpio_set_level((gpio_num_t)num, 1); } static inline void clear(const uint8_t num) { WRITE_PERI_REG(GPIO_OUT_W1TC_REG, 1 << num); + //gpio_set_level((gpio_num_t)num, 0); } 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); + //gpio_set_direction(num, GPIO_MODE_OUTPUT); // does not always suffice?! + gpio_config_t io_conf; + io_conf.intr_type = GPIO_INTR_DISABLE; + io_conf.mode = GPIO_MODE_OUTPUT; + io_conf.pin_bit_mask = (1< class I2S { + + static constexpr i2s_port_t port = I2S_NUM_0; + i2s_config_t cfg; + + struct Setup { + uint32_t sampleRate = 0; + i2s_bits_per_sample_t bitsPerSample = I2S_BITS_PER_SAMPLE_8BIT; + i2s_channel_t channels = I2S_CHANNEL_MONO; + } cur; + +public: + + /** ctor */ + I2S() { + + #ifdef CONFIG_A2DP_SINK_OUTPUT_INTERNAL_DAC + cfg.mode = static_cast(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN); + #else + cfg.mode = static_cast(I2S_MODE_MASTER | I2S_MODE_TX); + #endif + + cfg.dma_buf_count = 6; + cfg.dma_buf_len = 128; + + cfg.sample_rate = 44100; + cfg.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT; + cfg.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT; //2-channels + cfg.communication_format = I2S_COMM_FORMAT_I2S_MSB; + + + cfg.intr_alloc_flags = 0; //Default interrupt priority + cfg.tx_desc_auto_clear = true; //Auto clear tx descriptor on underflow + + i2s_driver_install(port, &cfg, 0, nullptr); + + #ifdef CONFIG_A2DP_SINK_OUTPUT_INTERNAL_DAC + i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN); + i2s_set_pin(0, NULL); + #else + i2s_pin_config_t pins; + pins.bck_io_num = PIN_CLK;//CONFIG_I2S_BCK_PIN; + pins.ws_io_num = PIN_LR;//CONFIG_I2S_LRCK_PIN; + pins.data_out_num = PIN_DATA;//CONFIG_I2S_DATA_PIN; + pins.data_in_num = -1; //Not used + i2s_set_pin(port, &pins); + #endif + + configure(44100, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_STEREO); + + } + + /** change the i2s configuration */ + void configure(const uint32_t sampleRate, const i2s_bits_per_sample_t bitsPerSample, const i2s_channel_t channels) { + + // only update when changed! + if (cur.sampleRate != sampleRate || cur.bitsPerSample != bitsPerSample || cur.channels != channels) { + i2s_set_clk(port, sampleRate, bitsPerSample, channels); // update + cur.sampleRate = sampleRate; + cur.bitsPerSample = bitsPerSample; + cur.channels = channels; + } + + } + + /** add the given samples for playback. returns the number of added samples, which might be less in case of timeouts */ + size_t add(const uint8_t* data, const size_t len) { + size_t written; + i2s_write(port, data, len, &written, portMAX_DELAY); + return written; + } + + uint16_t tmp[2048]; + + size_t addSignedToUnsigned(const uint8_t* data, const size_t len) { + const int16_t* src = (const int16_t*) data; + uint16_t* dst = (uint16_t*) data; + for (int i = 0; i < len/2; ++i) { + //dst[i] = (int)src[i] + (int)32768; + //dst[i] = (int)src[i] / 270 + 128; + tmp[i] = (int)src[i] + 32768; + } + //return add(data, len); + return add((const uint8_t*)tmp, len); + } + +// /** add the given samples for playback. blocks until all have been added */ +// void addAll(const uint8_t* data, const size_t len) { +// size_t added = add(data, len); +// len -= added; +// data += +// } + + + + + + +}; + +#endif // I2S_H diff --git a/io/IO.h b/io/IO.h.old similarity index 100% rename from io/IO.h rename to io/IO.h.old diff --git a/io/SoftI2C.h b/io/SoftI2C.h index 55fe3a4..6cbff65 100644 --- a/io/SoftI2C.h +++ b/io/SoftI2C.h @@ -6,10 +6,284 @@ #include "GPIO.h" +/** new implementation */ +template class SoftI2C { + + static constexpr const char* NAME = "softI2C"; + + inline void sdaDirOut() {MyGPIO::setOutput(PIN_SDA);} + inline void sdaDirIn() {MyGPIO::setInput(PIN_SDA);} + + inline void sclDirOut() {MyGPIO::setOutput(PIN_SCL);} + + inline void sdaHi() {MyGPIO::set(PIN_SDA);} + inline void sdaLo() {MyGPIO::clear(PIN_SDA);} + inline bool sdaRead() {return MyGPIO::get(PIN_SDA);} + + inline void sclHi() {MyGPIO::set(PIN_SCL);} + inline void sclLo() {MyGPIO::clear(PIN_SCL);} + + inline void waitLong() { + for (uint8_t i = 0; i < 240; ++i) { + __asm__ __volatile__("nop"); + __asm__ __volatile__("nop"); + } + } + + inline void waitShort() { + for (uint8_t i = 0; i < 240; ++i) { + __asm__ __volatile__("nop"); + __asm__ __volatile__("nop"); + } + } + + void init() { + debugMod2(NAME, "init. SDA: %d, SCL: %d", PIN_SDA, PIN_SCL); + } + +public: + + /** ctor */ + SoftI2C() { + init(); + } + + /** start communication */ + inline void start() { + + sdaDirOut(); + sclDirOut(); + + sdaHi(); + sclHi(); + + waitLong(); + sdaLo(); + + waitLong(); + sclLo(); + + waitLong(); + + } + + /** stop communication */ + inline void stop() { + + sdaDirOut(); + sclDirOut(); + + sdaLo(); + sclLo(); + + waitLong(); + sclHi(); + + waitLong(); + sdaHi(); + + sdaDirIn(); // free the bus + + waitLong(); + + } + + + /** read the given number of bytes from the slave and generate ACK/NACK as needed */ + 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 and check slave's ACK/NACK + bool IN_FLASH writeByteAndCheck(const uint8_t byte) { + _writeByte(byte); + return _readAck(); + } + + // write several bytes to the bus and check slave's ACK/NACK + 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; + } + + + inline bool startWrite(const uint8_t addr7) { + start(); + _writeByte( (addr7<<1) | 0 ); + const bool ok = _readAck(); + return ok; + } + + inline bool startRead(const uint8_t addr7) { + start(); + _writeByte( (addr7<<1) | 1 ); + const bool ok = _readAck(); + return ok; + } + + inline bool query(const uint8_t addr7) { + const bool ok = startWrite(addr7); + stop(); + return ok; + } + + + + + +public: + + uint8_t readReg8(const uint8_t addr, const uint8_t reg) { + uint8_t dst = 0xFF; + readReg(addr, reg, 1, &dst); + return dst; + } + + bool readReg(const uint8_t addr, const uint8_t reg, const uint8_t len, uint8_t* dst) { + + bool ok; + + // select register(s) to read + ok = startWrite(addr); + if (!ok) {debugMod(NAME, "failed start write1\n"); return false;} + ok = writeByteAndCheck(reg); + if (!ok) {debugMod(NAME, "failed select register\n"); return false;} + stop(); + + // read register(s) + ok = startRead(addr); + if (!ok) {debugMod(NAME, "failed start write2\n"); return false;} + readBytes(dst, len); + stop(); + + return true; + + } + + + bool writeReg(const uint8_t addr, const uint8_t reg, const uint8_t len, const uint8_t* src) { + + bool ok; + + // 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;} + ok = writeByteAndCheck(reg); + if (!ok) {debugMod1(NAME, "failed to select register %d\n", addr); return false;} + ok = writeBytesAndCheck(src, len); + if (!ok) {debugMod(NAME, "failed to write register contents \n"); return false;} + + // done + stop(); + return true; + + } + + bool writeReg8(const uint8_t addr, const uint8_t reg, const uint8_t val) { + return writeReg(addr, reg, 1, &val); + } + + + + + + +private: + + /** write one bit to the bus */ + 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 */ + inline uint8_t _readBit() { + //sdaDirIn(); // switch to input mode + sclHi(); // clock pulse and read + waitShort(); + const uint8_t val = sdaRead(); + sclLo(); + waitShort(); + return val; + } + + + + + + // read 8 bits from the bus, WITHOUT sending ACK/NACK + uint8_t _readByte() { + sdaDirIn(); // switch SDA to input mode + return + (_readBit() << 7) | + (_readBit() << 6) | + (_readBit() << 5) | + (_readBit() << 4) | + (_readBit() << 3) | + (_readBit() << 2) | + (_readBit() << 1) | + (_readBit() << 0); + } + + inline bool _readAck() { + sdaDirIn(); // switch to input mode + const uint8_t res = _readBit(); + return res == 0; // ACK when input pulled low + } + + + // write one byte to the bus + void _writeByte(const uint8_t byte) { + sdaDirOut(); // switch SDA to output mode + _writeBit(byte & (1<<7)); + _writeBit(byte & (1<<6)); + _writeBit(byte & (1<<5)); + _writeBit(byte & (1<<4)); + _writeBit(byte & (1<<3)); + _writeBit(byte & (1<<2)); + _writeBit(byte & (1<<1)); + _writeBit(byte & (1<<0)); + } + + inline void _writeAck() { + sdaDirOut(); // switch SDA to output mode + _writeBit(0); + } + + inline void _writeNAck() { + sdaDirOut(); // switch SDA to output mode + _writeBit(1); + } + + + + +}; + + + + +/* #if ESP8266 #include "fastGPIO.h" #elif ESP32 - # error "Not yet supported" + #include "GPIO.h" #endif // https://www.best-microcontroller-projects.com/i2c-tutorial.html @@ -42,6 +316,8 @@ namespace i2c { #elif ESP32 //# error "Not yet supported" + + #endif @@ -127,7 +403,7 @@ namespace i2c { } - /** write one bit to the bus */ + // 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 @@ -138,7 +414,7 @@ namespace i2c { sdaDirIn(); // free the bus } - /** read one bit from the bus */ + // read one bit from the bus static inline uint8_t readBit() { sdaDirIn(); // switch to input mode sclHi(); // clock pulse and read @@ -163,7 +439,7 @@ namespace i2c { writeBit(1); } - /** read 8 bits from the bus, WITHOUT sending ACK/NACK */ + // read 8 bits from the bus, WITHOUT sending ACK/NACK static uint8_t IN_FLASH readByte() { return (readBit() << 7) | @@ -176,7 +452,7 @@ namespace i2c { (readBit() << 0); } - /** read the given number of bytes from the slave and generate ACK/NACK as needed */ + // 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(); @@ -186,7 +462,7 @@ namespace i2c { } } - /** write one byte to the bus */ + // write one byte to the bus static void IN_FLASH writeByte(const uint8_t byte) { writeBit(byte & BIT( 7)); writeBit(byte & BIT( 6)); @@ -198,13 +474,13 @@ namespace i2c { writeBit(byte & BIT( 0)); } - /** write one byte to the bus and check slave's ACK/NACK */ + // 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 */ + // 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); @@ -236,5 +512,6 @@ namespace i2c { } } + */ #endif // SOFTSPI_H diff --git a/io/SoftSPI.h b/io/SoftSPI.h index 21371fc..0e5f625 100644 --- a/io/SoftSPI.h +++ b/io/SoftSPI.h @@ -347,10 +347,10 @@ public: private: void init() { - debugMod(NAME, "init()"); - GPIO::setInput(PIN_MISO); - GPIO::setOutput(PIN_MOSI); - GPIO::setOutput(PIN_CLK); + debugMod3(NAME, "init() MISO:%d MOSI:%d CLK:%d", PIN_MISO, PIN_MOSI, PIN_CLK); + if (PIN_MISO) {MyGPIO::setInput(PIN_MISO);} + if (PIN_MOSI) {MyGPIO::setOutput(PIN_MOSI);} + MyGPIO::setOutput(PIN_CLK); } private: @@ -363,12 +363,12 @@ private: } } - inline void clkLo() { GPIO::clear(PIN_CLK); } - inline void clkHi() { GPIO::set(PIN_CLK); } + inline void clkLo() { MyGPIO::clear(PIN_CLK); } + inline void clkHi() { MyGPIO::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);} + if(out) {MyGPIO::set(PIN_MOSI);} else {MyGPIO::clear(PIN_MOSI);} wait(); clkHi(); wait(); @@ -380,7 +380,7 @@ private: inline uint8_t readBit() { clkHi(); wait(); - const bool val = GPIO::get(PIN_MISO); + const bool val = MyGPIO::get(PIN_MISO); wait(); clkLo(); wait(); @@ -388,11 +388,11 @@ private: } inline uint8_t readWriteBit(const bool out) { - if(out) {GPIO::set(PIN_MOSI);} else {GPIO::clear(PIN_MOSI);} + if(out) {MyGPIO::set(PIN_MOSI);} else {MyGPIO::clear(PIN_MOSI);} wait(); clkHi(); wait(); - const bool inp = GPIO::get(PIN_MISO); + const bool inp = MyGPIO::get(PIN_MISO); wait(); clkLo(); wait(); @@ -474,7 +474,7 @@ public: /** read 8 bits */ uint8_t readByte() { - GPIO::clear(PIN_MOSI); + MyGPIO::clear(PIN_MOSI); return (readBit() << 7) | (readBit() << 6) | diff --git a/net/WiFiRaw.h b/net/WiFiRaw.h index 16b440d..ca8011f 100644 --- a/net/WiFiRaw.h +++ b/net/WiFiRaw.h @@ -183,12 +183,21 @@ namespace WiFiRaw { }; +#ifdef ESP8266 WiFiRaw::MACAddress getMyMAC() { WiFiRaw::MACAddress mine; wifi_get_macaddr(0x00, (uint8_t*)&mine); return mine; } +#endif +#ifdef ESP32 + WiFiRaw::MACAddress getMyMAC() { + WiFiRaw::MACAddress mine; + esp_wifi_get_mac(ESP_IF_WIFI_STA, (uint8_t*)&mine); + return mine; + } +#endif };