diff --git a/libraries/SPI/SPI.cpp b/libraries/SPI/SPI.cpp index d47d84cc2..33ddd319a 100644 --- a/libraries/SPI/SPI.cpp +++ b/libraries/SPI/SPI.cpp @@ -8,118 +8,117 @@ #include "zephyrInternal.h" #include -/* Serial Peripheral Control Register */ -uint8_t SPCR; - -arduino::ZephyrSPI::ZephyrSPI(const struct device *spi) : spi_dev(spi) {} +arduino::ZephyrSPI::ZephyrSPI(const struct device *spi) : spi_dev(spi) { +} uint8_t arduino::ZephyrSPI::transfer(uint8_t data) { - int ret; - uint8_t rx; - const struct spi_buf tx_buf = {.buf = &data, .len = sizeof(data)}; - const struct spi_buf_set tx_buf_set = { - .buffers = &tx_buf, - .count = 1, - }; - const struct spi_buf rx_buf = {.buf = &rx, .len = sizeof(rx)}; - const struct spi_buf_set rx_buf_set = { - .buffers = &rx_buf, - .count = 1, - }; - - ret = spi_transceive(spi_dev, &config, &tx_buf_set, &rx_buf_set); - if (ret < 0) { - return 0; - } - - return rx; + uint8_t rx = data; + if (transfer(&rx, sizeof(rx), &config) < 0) { + return 0; + } + return rx; } uint16_t arduino::ZephyrSPI::transfer16(uint16_t data) { - int ret; - uint16_t rx; - const struct spi_buf tx_buf = {.buf = &data, .len = sizeof(data)}; - const struct spi_buf_set tx_buf_set = { - .buffers = &tx_buf, - .count = 1, - }; - const struct spi_buf rx_buf = {.buf = &rx, .len = sizeof(rx)}; - const struct spi_buf_set rx_buf_set = { - .buffers = &rx_buf, - .count = 1, - }; - - ret = spi_transceive(spi_dev, &config, &tx_buf_set, &rx_buf_set); - if (ret < 0) { - return 0; - } - - return rx; + uint16_t rx = data; + if (transfer(&rx, sizeof(rx), &config16) < 0) { + return 0; + } + return rx; } void arduino::ZephyrSPI::transfer(void *buf, size_t count) { - int ret; - const struct spi_buf tx_buf = {.buf = buf, .len = count}; - const struct spi_buf_set tx_buf_set = { - .buffers = &tx_buf, - .count = 1, - }; - - ret = spi_write(spi_dev, &config, &tx_buf_set); - if (ret < 0) { - return; - } - - ret = spi_read(spi_dev, &config, &tx_buf_set); - if (ret < 0) { - return; - } + int ret = transfer(buf, count, &config); + (void)ret; +} + +int arduino::ZephyrSPI::transfer(void *buf, size_t len, const struct spi_config *config) { + int ret; + + const struct spi_buf tx_buf = {.buf = buf, .len = len}; + const struct spi_buf_set tx_buf_set = { + .buffers = &tx_buf, + .count = 1, + }; + + const struct spi_buf rx_buf = {.buf = buf, .len = len}; + const struct spi_buf_set rx_buf_set = { + .buffers = &rx_buf, + .count = 1, + }; + + return spi_transceive(spi_dev, config, &tx_buf_set, &rx_buf_set); } void arduino::ZephyrSPI::usingInterrupt(int interruptNumber) { - interrupt[interrupt_pos++] = interruptNumber; } void arduino::ZephyrSPI::notUsingInterrupt(int interruptNumber) { - for (size_t i = 0; i < interrupt_pos; ++i) { - if (interrupt[i] == interruptNumber) { - memmove(&interrupt[i], &interrupt[i + 1], interrupt_pos - i - 1); - interrupt_pos--; - break; - } - } } void arduino::ZephyrSPI::beginTransaction(SPISettings settings) { - memset(&config, 0, sizeof(config)); - config.frequency = settings.getClockFreq(); - config.operation = ((settings.getBitOrder() ^ 1) << 4) | - (settings.getDataMode() << 1) | ((SPCR >> MSTR) & 1) | - SPI_WORD_SET(8); - - detachInterrupt(); + uint32_t mode = SPI_HOLD_ON_CS; + + // Set bus mode + switch (settings.getBusMode()) { + case SPI_CONTROLLER: + break; + case SPI_PERIPHERAL: + mode |= SPI_OP_MODE_SLAVE; + break; + } + + // Set data format + switch (settings.getBitOrder()) { + case LSBFIRST: + mode |= SPI_TRANSFER_LSB; + break; + case MSBFIRST: + mode |= SPI_TRANSFER_MSB; + break; + } + + // Set data mode + switch (settings.getDataMode()) { + case SPI_MODE0: + break; + case SPI_MODE1: + mode |= SPI_MODE_CPHA; + break; + case SPI_MODE2: + mode |= SPI_MODE_CPOL; + break; + case SPI_MODE3: + mode |= SPI_MODE_CPOL | SPI_MODE_CPHA; + break; + } + + // Set SPI configuration structure for 8-bit transfers + memset(&config, 0, sizeof(struct spi_config)); + config.operation = mode | SPI_WORD_SET(8); + config.frequency = max(SPI_MIN_CLOCK_FREQUENCY, settings.getClockFreq()); + + // Set SPI configuration structure for 16-bit transfers + memset(&config16, 0, sizeof(struct spi_config)); + config16.operation = mode | SPI_WORD_SET(16); + config16.frequency = max(SPI_MIN_CLOCK_FREQUENCY, settings.getClockFreq()); } void arduino::ZephyrSPI::endTransaction(void) { - spi_release(spi_dev, &config); - attachInterrupt(); + spi_release(spi_dev, &config); } void arduino::ZephyrSPI::attachInterrupt() { - for (size_t i = 0; i < interrupt_pos; ++i) { - enableInterrupt(interrupt[i]); - } } void arduino::ZephyrSPI::detachInterrupt() { - for (size_t i = 0; i < interrupt_pos; ++i) { - disableInterrupt(interrupt[i]); - } } -void arduino::ZephyrSPI::begin() {} +void arduino::ZephyrSPI::begin() { +} -void arduino::ZephyrSPI::end() {} +void arduino::ZephyrSPI::end() { +} #if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), spis) #if (DT_PROP_LEN(DT_PATH(zephyr_user), spis) > 1) diff --git a/libraries/SPI/SPI.h b/libraries/SPI/SPI.h index 5d448d7a2..2a25a8bf5 100644 --- a/libraries/SPI/SPI.h +++ b/libraries/SPI/SPI.h @@ -10,48 +10,63 @@ #include #include +#undef SPI +#undef SPI1 + #define SPR0 0 #define SPR1 1 #define CPHA 2 #define CPOL 3 #define MSTR 4 #define DORD 5 -#define SPE 6 +#define SPE 6 #define SPIE 7 +#define SPI_HAS_PERIPHERAL_MODE (1) + +// TODO: +// This depends on the clock settings, can't be used for all boards. +#ifndef SPI_MIN_CLOCK_FREQUENCY +#define SPI_MIN_CLOCK_FREQUENCY 1000000 +#endif + /* Count the number of GPIOs for limit of number of interrupts */ #define INTERRUPT_HELPER(n, p, i) 1 -#define INTERRUPT_COUNT \ - DT_FOREACH_PROP_ELEM_SEP(DT_PATH(zephyr_user), digital_pin_gpios, \ +#define INTERRUPT_COUNT \ + DT_FOREACH_PROP_ELEM_SEP(DT_PATH(zephyr_user), digital_pin_gpios, \ INTERRUPT_HELPER, (+)) namespace arduino { class ZephyrSPI : public HardwareSPI { public: - ZephyrSPI(const struct device *spi); + ZephyrSPI(const struct device *spi); - virtual uint8_t transfer(uint8_t data); - virtual uint16_t transfer16(uint16_t data); - virtual void transfer(void *buf, size_t count); + virtual uint8_t transfer(uint8_t data); + virtual uint16_t transfer16(uint16_t data); + virtual void transfer(void *buf, size_t count); - // Transaction Functions - virtual void usingInterrupt(int interruptNumber); - virtual void notUsingInterrupt(int interruptNumber); - virtual void beginTransaction(SPISettings settings); - virtual void endTransaction(void); + // Transaction Functions + virtual void usingInterrupt(int interruptNumber); + virtual void notUsingInterrupt(int interruptNumber); + virtual void beginTransaction(SPISettings settings); + virtual void endTransaction(void); - // SPI Configuration methods - virtual void attachInterrupt(); - virtual void detachInterrupt(); + // SPI Configuration methods + virtual void attachInterrupt(); + virtual void detachInterrupt(); - virtual void begin(); - virtual void end(); + virtual void begin(); + virtual void end(); private: - const struct device *spi_dev; - struct spi_config config; - int interrupt[INTERRUPT_COUNT]; - size_t interrupt_pos = 0; + int transfer(void *buf, size_t len, const struct spi_config *config); + +protected: + const struct device *spi_dev; + struct spi_config config; + struct spi_config config16; + int interrupt[INTERRUPT_COUNT]; + size_t interrupt_pos = 0; }; } // namespace arduino @@ -75,7 +90,6 @@ extern arduino::ZephyrSPI SPI; #endif /* Serial Peripheral Control Register */ -extern uint8_t SPCR; using arduino::SPI_MODE0; using arduino::SPI_MODE1; diff --git a/libraries/Wire/Wire.cpp b/libraries/Wire/Wire.cpp index 7fc64b030..c7d97587f 100644 --- a/libraries/Wire/Wire.cpp +++ b/libraries/Wire/Wire.cpp @@ -5,106 +5,236 @@ */ #include +#include #include -arduino::ZephyrI2C::ZephyrI2C(const struct device *i2c) : i2c_dev(i2c) -{ +// Helper function to get ZephyrI2C instance from config pointer. +static arduino::ZephyrI2C *getInstance(struct i2c_target_config *config) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Winvalid-offsetof" + return reinterpret_cast(reinterpret_cast(config) - + offsetof(arduino::ZephyrI2C, i2c_cfg)); +#pragma GCC diagnostic pop +} + +static int i2c_target_stop_cb(struct i2c_target_config *config) { + arduino::ZephyrI2C *instance = getInstance(config); + return instance->stopCallback(config); +} + +static int i2c_target_write_requested_cb(struct i2c_target_config *config) { + arduino::ZephyrI2C *instance = getInstance(config); + return instance->writeRequestedCallback(config); +} + +static int i2c_target_write_received_cb(struct i2c_target_config *config, uint8_t val) { + arduino::ZephyrI2C *instance = getInstance(config); + return instance->writeReceivedCallback(config, val); +} + +static int i2c_target_read_requested_cb(struct i2c_target_config *config, uint8_t *val) { + arduino::ZephyrI2C *instance = getInstance(config); + return instance->readRequestedCallback(config, val); +} + +static int i2c_target_read_processed_cb(struct i2c_target_config *config, uint8_t *val) { + arduino::ZephyrI2C *instance = getInstance(config); + return instance->readProcessedCallback(config, val); +} + +// I2C target callback structure. +static struct i2c_target_callbacks target_callbacks = { + .write_requested = i2c_target_write_requested_cb, + .read_requested = i2c_target_read_requested_cb, + .write_received = i2c_target_write_received_cb, + .read_processed = i2c_target_read_processed_cb, + .stop = i2c_target_stop_cb, +}; + +arduino::ZephyrI2C::ZephyrI2C(const struct device *i2c) : i2c_cfg({0}), i2c_dev(i2c) { + ring_buf_init(&txRingBuffer.rb, sizeof(txRingBuffer.buffer), txRingBuffer.buffer); + ring_buf_init(&rxRingBuffer.rb, sizeof(rxRingBuffer.buffer), rxRingBuffer.buffer); } void arduino::ZephyrI2C::begin() { - ring_buf_init(&rxRingBuffer.rb, sizeof(rxRingBuffer.buffer), rxRingBuffer.buffer); } void arduino::ZephyrI2C::begin(uint8_t slaveAddr) { + i2c_cfg.address = slaveAddr; + i2c_cfg.callbacks = &target_callbacks; + // Register I2C target + i2c_target_register(i2c_dev, &i2c_cfg); } -void arduino::ZephyrI2C::end() {} +void arduino::ZephyrI2C::end() { + // Unregister I2C target + if (i2c_cfg.address) { + i2c_target_unregister(i2c_dev, &i2c_cfg); + memset(&i2c_cfg, 0, sizeof(i2c_cfg)); + } +} + +void arduino::ZephyrI2C::setClock(uint32_t freq) { + uint8_t speed; + + if (freq == 100000) { + speed = I2C_SPEED_STANDARD; + } else if (freq == 400000) { + speed = I2C_SPEED_FAST; + } else if (freq == 1000000) { + speed = I2C_SPEED_FAST_PLUS; + } else { + speed = I2C_SPEED_STANDARD; + } -void arduino::ZephyrI2C::setClock(uint32_t freq) {} + i2c_configure(i2c_dev, I2C_SPEED_SET(speed) | I2C_MODE_CONTROLLER); +} -void arduino::ZephyrI2C::beginTransmission(uint8_t address) { // TODO for ADS1115 - _address = address; - usedTxBuffer = 0; +void arduino::ZephyrI2C::beginTransmission(uint8_t address) { + _address = address; + ring_buf_reset(&txRingBuffer.rb); + ring_buf_reset(&rxRingBuffer.rb); } uint8_t arduino::ZephyrI2C::endTransmission(bool stopBit) { - int ret = i2c_write(i2c_dev, txBuffer, usedTxBuffer, _address); - if (ret) { - return 1; // fail - } - return 0; + int ret = -EIO; + uint8_t *buf = NULL; + size_t max = ring_buf_capacity_get(&txRingBuffer.rb); + size_t len = ring_buf_get_claim(&txRingBuffer.rb, &buf, max); + + ARG_UNUSED(stopBit); + + ret = i2c_write(i2c_dev, buf, len, _address); + + // Must be called even if 0 bytes claimed. + ring_buf_get_finish(&txRingBuffer.rb, len); + + return ret ? 1 : 0; } -uint8_t arduino::ZephyrI2C::endTransmission(void) { // TODO for ADS1115 - return endTransmission(true); +uint8_t arduino::ZephyrI2C::endTransmission(void) { + return endTransmission(true); } -size_t arduino::ZephyrI2C::requestFrom(uint8_t address, size_t len, - bool stopBit) { - int ret = i2c_read(i2c_dev, rxRingBuffer.buffer, len, address); - if (ret != 0) - { - printk("\n\nERR: i2c burst read fails\n\n\n"); - return 0; - } - ret = ring_buf_put(&rxRingBuffer.rb, rxRingBuffer.buffer, len); - if (ret == 0) - { - printk("\n\nERR: buff put fails\n\n\n"); - return 0; - } - return len; +size_t arduino::ZephyrI2C::requestFrom(uint8_t address, size_t len_in, bool stopBit) { + int ret = -EIO; + uint8_t *buf = NULL; + size_t len; + + ARG_UNUSED(stopBit); + + len = ring_buf_put_claim(&rxRingBuffer.rb, &buf, len_in); + if (len && buf) { + ret = i2c_read(i2c_dev, buf, len, address); + } + + // Must be called even if 0 bytes claimed. + ring_buf_put_finish(&rxRingBuffer.rb, len); + + return ret ? 0 : len; } -size_t arduino::ZephyrI2C::requestFrom(uint8_t address, size_t len) { // TODO for ADS1115 - return requestFrom(address, len, true); +size_t arduino::ZephyrI2C::requestFrom(uint8_t address, size_t len) { + return requestFrom(address, len, true); } -size_t arduino::ZephyrI2C::write(uint8_t data) { // TODO for ADS1115 - txBuffer[usedTxBuffer++] = data; - return 1; +size_t arduino::ZephyrI2C::write(uint8_t data) { + return ring_buf_put(&txRingBuffer.rb, &data, 1); } size_t arduino::ZephyrI2C::write(const uint8_t *buffer, size_t size) { - if (usedTxBuffer + size > 256) { - size = 256 - usedTxBuffer; - } - memcpy(txBuffer + usedTxBuffer, buffer, size); - usedTxBuffer += size; - return size; + return ring_buf_put(&txRingBuffer.rb, buffer, size); } int arduino::ZephyrI2C::read() { - uint8_t buf[1]; - if (ring_buf_peek(&rxRingBuffer.rb, buf, 1) > 0) { - int ret = ring_buf_get(&rxRingBuffer.rb, buf, 1); - if (ret == 0) { - printk("\n\nERR: buff empty\n\n\n"); - return 0; - } - return (int)buf[0]; - } - return EXIT_FAILURE; -} - -int arduino::ZephyrI2C::available() { // TODO for ADS1115 - return !ring_buf_is_empty(&rxRingBuffer.rb); // ret 0 if empty - } + uint8_t buf; + if (ring_buf_get(&rxRingBuffer.rb, &buf, 1)) { + return (int)buf; + } + return -1; +} + +int arduino::ZephyrI2C::available() { + return ring_buf_size_get(&rxRingBuffer.rb); +} int arduino::ZephyrI2C::peek() { - uint8_t buf[1]; - int bytes_read = ring_buf_peek(&rxRingBuffer.rb, buf, 1); - if (bytes_read == 0){ - return 0; - } - return (int)buf[0]; + uint8_t buf; + if (ring_buf_peek(&rxRingBuffer.rb, &buf, 1)) { + return (int)buf; + } + return -1; +} + +void arduino::ZephyrI2C::flush() { +} + +void arduino::ZephyrI2C::onReceive(voidFuncPtrParamInt cb) { + onReceiveCb = cb; } -void arduino::ZephyrI2C::flush() {} +void arduino::ZephyrI2C::onRequest(voidFuncPtr cb) { + onRequestCb = cb; +} + +int arduino::ZephyrI2C::writeRequestedCallback(struct i2c_target_config *config) { + ARG_UNUSED(config); + + // Reset the buffer on write requests. + ring_buf_reset(&rxRingBuffer.rb); + return 0; +} + +int arduino::ZephyrI2C::writeReceivedCallback(struct i2c_target_config *config, uint8_t val) { + size_t len = ring_buf_size_get(&rxRingBuffer.rb); + size_t max = ring_buf_capacity_get(&rxRingBuffer.rb); + + ARG_UNUSED(config); -void arduino::ZephyrI2C::onReceive(voidFuncPtrParamInt cb) {} -void arduino::ZephyrI2C::onRequest(voidFuncPtr cb) {} + // If the buffer is about to overflow, invoke the callback + // with the current length. + if (onReceiveCb && ((len + 1) > max)) { + onReceiveCb(len); + } + + return ring_buf_put(&rxRingBuffer.rb, &val, 1) ? 0 : -1; +} + +int arduino::ZephyrI2C::readRequestedCallback(struct i2c_target_config *config, uint8_t *val) { + // Reset the buffer on read requests. + ring_buf_reset(&txRingBuffer.rb); + + if (onRequestCb) { + onRequestCb(); + } + + return readProcessedCallback(config, val); +} + +int arduino::ZephyrI2C::readProcessedCallback(struct i2c_target_config *config, uint8_t *val) { + ARG_UNUSED(config); + + *val = 0xFF; + ring_buf_get(&txRingBuffer.rb, val, 1); + // Returning a negative value here is not handled gracefully and + // causes the target/board to stop responding (at least with ST). + return 0; +} + +int arduino::ZephyrI2C::stopCallback(struct i2c_target_config *config) { + ARG_UNUSED(config); + + // If the RX buffer is not empty invoke the callback with the + // remaining data length. + if (onReceiveCb) { + size_t len = ring_buf_size_get(&rxRingBuffer.rb); + if (len) { + onReceiveCb(len); + } + } + return 0; +} #if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), i2cs) #if (DT_PROP_LEN(DT_PATH(zephyr_user), i2cs) > 1) @@ -121,7 +251,7 @@ DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), i2cs, DECLARE_WIRE_N) #undef DECL_WIRE_N #undef DECL_WIRE_0 #undef ARDUINO_WIRE_DEFINED_0 -#else // PROP_LEN(i2cs) > 1 +#elif (DT_PROP_LEN(DT_PATH(zephyr_user), i2cs) == 1) /* When PROP_LEN(i2cs) == 1, DT_FOREACH_PROP_ELEM work not correctly. */ arduino::ZephyrI2C Wire(DEVICE_DT_GET(DT_PHANDLE_BY_IDX(DT_PATH(zephyr_user), i2cs, 0))); #endif // HAS_PORP(i2cs) diff --git a/libraries/Wire/Wire.h b/libraries/Wire/Wire.h index 6e289f8c1..47b126965 100644 --- a/libraries/Wire/Wire.h +++ b/libraries/Wire/Wire.h @@ -15,44 +15,61 @@ typedef void (*voidFuncPtrParamInt)(int); namespace arduino { +struct i2c_ring { + struct ring_buf rb; + uint8_t buffer[256]; +}; + class ZephyrI2C : public HardwareI2C { public: - ZephyrI2C(const struct device* i2c); + ZephyrI2C(const struct device *i2c); + + virtual void begin(); + virtual void end(); + virtual void begin(uint8_t address); + virtual void setClock(uint32_t freq); + + virtual void beginTransmission(uint8_t address); + virtual uint8_t endTransmission(bool stopBit); + virtual uint8_t endTransmission(void); - virtual void begin(); - virtual void end(); - virtual void begin(uint8_t address); - virtual void setClock(uint32_t freq); + virtual size_t requestFrom(uint8_t address, size_t len, bool stopBit); + virtual size_t requestFrom(uint8_t address, size_t len); - virtual void beginTransmission(uint8_t address); - virtual uint8_t endTransmission(bool stopBit); - virtual uint8_t endTransmission(void); + virtual void onReceive(void (*)(int)); + virtual void onRequest(void (*)(void)); - virtual size_t requestFrom(uint8_t address, size_t len, bool stopBit); - virtual size_t requestFrom(uint8_t address, size_t len); + virtual size_t write(uint8_t data); - virtual void onReceive(void (*)(int)); - virtual void onRequest(void (*)(void)); + virtual size_t write(int data) { + return write((uint8_t)data); + }; - virtual size_t write(uint8_t data); - virtual size_t write(int data) { return write((uint8_t)data); }; - virtual size_t write(const uint8_t *buffer, size_t size); - using Print::write; - virtual int read(); - virtual int peek(); - virtual void flush(); - virtual int available(); + virtual size_t write(const uint8_t *buffer, size_t size); + using Print::write; + virtual int read(); + virtual int peek(); + virtual void flush(); + virtual int available(); + + // I2C target callbacks + int writeRequestedCallback(struct i2c_target_config *config); + int writeReceivedCallback(struct i2c_target_config *config, uint8_t val); + int readRequestedCallback(struct i2c_target_config *config, uint8_t *val); + int readProcessedCallback(struct i2c_target_config *config, uint8_t *val); + int stopCallback(struct i2c_target_config *config); + + struct i2c_target_config i2c_cfg; private: - int _address; - uint8_t txBuffer[256]; - uint32_t usedTxBuffer; - struct rx_ring_buf{ - struct ring_buf rb; - uint8_t buffer[256]; - }; - struct rx_ring_buf rxRingBuffer; - const struct device* i2c_dev; + int _address; + + struct i2c_ring txRingBuffer; + struct i2c_ring rxRingBuffer; + const struct device *i2c_dev; + + voidFuncPtr onRequestCb = NULL; + voidFuncPtrParamInt onReceiveCb = NULL; }; } // namespace arduino