From 045ae9da74dcfa96e5cfde2ec297eb43ee3ca30f Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 30 Jul 2025 14:50:04 +0200 Subject: [PATCH 1/3] loader: Export missing ring_buf symbols. Signed-off-by: iabdalkader --- loader/llext_exports.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/loader/llext_exports.c b/loader/llext_exports.c index f1370a92..5579e4db 100644 --- a/loader/llext_exports.c +++ b/loader/llext_exports.c @@ -199,13 +199,16 @@ EXPORT_SYMBOL(printf); EXPORT_SYMBOL(sprintf); EXPORT_SYMBOL(snprintf); EXPORT_SYMBOL(cbvprintf); -; FORCE_EXPORT_SYM(abort); + #if defined(CONFIG_RING_BUFFER) EXPORT_SYMBOL(ring_buf_get); EXPORT_SYMBOL(ring_buf_peek); EXPORT_SYMBOL(ring_buf_put); +EXPORT_SYMBOL(ring_buf_area_claim); +EXPORT_SYMBOL(ring_buf_area_finish); #endif + EXPORT_SYMBOL(sys_clock_cycle_get_32); FORCE_EXPORT_SYM(__aeabi_dcmpun); FORCE_EXPORT_SYM(__aeabi_dcmple); @@ -239,4 +242,4 @@ FORCE_EXPORT_SYM(__aeabi_dcmpge); #if defined (CONFIG_CPP) FORCE_EXPORT_SYM(__cxa_pure_virtual); -#endif \ No newline at end of file +#endif From d5424c35956f05acf384c7e076d80514f1da6207 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 29 Jul 2025 19:18:53 +0200 Subject: [PATCH 2/3] libraries/Wire: Add support for target mode. Signed-off-by: iabdalkader --- libraries/Wire/Wire.cpp | 244 ++++++++++++++++++++++++++++------------ libraries/Wire/Wire.h | 29 +++-- 2 files changed, 193 insertions(+), 80 deletions(-) diff --git a/libraries/Wire/Wire.cpp b/libraries/Wire/Wire.cpp index 0453db62..0c28557f 100644 --- a/libraries/Wire/Wire.cpp +++ b/libraries/Wire/Wire.cpp @@ -5,104 +5,148 @@ */ #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) { + return reinterpret_cast( + reinterpret_cast(config) - offsetof(arduino::ZephyrI2C, i2c_cfg) + ); +} + +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_dev(i2c), i2c_cfg({0}) { + 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 = I2C_SPEED_STANDARD; - if(freq > 0x06u ) { - 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; - } - } else { - speed = (uint8_t) freq; - } - uint32_t i2c_cfg = I2C_MODE_CONTROLLER | - I2C_SPEED_SET(speed); - - if (i2c_configure(i2c_dev, i2c_cfg)) { - //Serial.println("Failed to configure i2c interface."); - } -} - -void arduino::ZephyrI2C::beginTransmission(uint8_t address) { // TODO for ADS1115 + 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; + } + + i2c_configure(i2c_dev, I2C_SPEED_SET(speed) | I2C_MODE_CONTROLLER); +} + +void arduino::ZephyrI2C::beginTransmission(uint8_t address) { _address = address; - usedTxBuffer = 0; + 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 + int ret = 0; + uint8_t *buf = NULL; + size_t max = ring_buf_capacity_get(&txRingBuffer.rb); + size_t len = ring_buf_get_claim(&txRingBuffer.rb, &buf, max); + + if (len && buf) { + ret = i2c_write(i2c_dev, buf, len, _address); } - return 0; + + // 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 +uint8_t arduino::ZephyrI2C::endTransmission(void) { return endTransmission(true); } -size_t arduino::ZephyrI2C::requestFrom(uint8_t address, size_t len, - bool stopBit) { - uint8_t buf[len]; - int ret = i2c_read(i2c_dev, buf, len, address); - if (ret != 0) - { - return 0; - } - ret = ring_buf_put(&rxRingBuffer.rb, buf, len); - if (ret == 0) - { - return 0; +size_t arduino::ZephyrI2C::requestFrom(uint8_t address, size_t len_in, bool stopBit) { + int ret = 0; + uint8_t *buf = NULL; + size_t len = ring_buf_put_claim(&rxRingBuffer.rb, &buf, len_in); + + if (len && buf) { + ret = i2c_read(i2c_dev, buf, len, address); } - return len; + + // Must be called even if 0 bytes claimed. + ring_buf_put_finish(&rxRingBuffer.rb, len); + + return ret ? 1 : 0; } -size_t arduino::ZephyrI2C::requestFrom(uint8_t address, size_t len) { // TODO for ADS1115 +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_size_get(&rxRingBuffer.rb)) { - int ret = ring_buf_get(&rxRingBuffer.rb, buf, 1); - if (ret == 0) { - return -1; - } - return (int)buf[0]; + uint8_t buf; + if (ring_buf_get(&rxRingBuffer.rb, &buf, 1)) { + return (int) buf; } return -1; } @@ -112,18 +156,74 @@ int arduino::ZephyrI2C::available() { } int arduino::ZephyrI2C::peek() { - uint8_t buf[1]; - int bytes_read = ring_buf_peek(&rxRingBuffer.rb, buf, 1); - if (bytes_read == 0){ - return 0; + uint8_t buf; + if (ring_buf_peek(&rxRingBuffer.rb, &buf, 1)) { + return (int) buf; } - return (int)buf[0]; + return -1; +} + +void arduino::ZephyrI2C::flush() { + +} + +void arduino::ZephyrI2C::onReceive(voidFuncPtrParamInt cb) { + onReceiveCb = cb; +} + +void arduino::ZephyrI2C::onRequest(voidFuncPtr cb) { + onRequestCb = cb; +} + +int arduino::ZephyrI2C::writeRequestedCallback(struct i2c_target_config *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); + + // 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; } -void arduino::ZephyrI2C::flush() {} +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); +} -void arduino::ZephyrI2C::onReceive(voidFuncPtrParamInt cb) {} -void arduino::ZephyrI2C::onRequest(voidFuncPtr cb) {} +int arduino::ZephyrI2C::readProcessedCallback(struct i2c_target_config *config, uint8_t *val) { + *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) { + // 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) diff --git a/libraries/Wire/Wire.h b/libraries/Wire/Wire.h index 6e289f8c..cb5fb0c7 100644 --- a/libraries/Wire/Wire.h +++ b/libraries/Wire/Wire.h @@ -15,6 +15,11 @@ 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); @@ -43,16 +48,24 @@ class ZephyrI2C : public HardwareI2C { 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; + + struct i2c_ring txRingBuffer; + struct i2c_ring rxRingBuffer; + const struct device *i2c_dev; + + voidFuncPtr onRequestCb = NULL; + voidFuncPtrParamInt onReceiveCb = NULL; }; } // namespace arduino From 6b0a1ff8c3f221ededa630d6f7443d81644d8f62 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 31 Jul 2025 14:21:55 +0200 Subject: [PATCH 3/3] variants/arduino_giga_r1_stm32h747xx_m7: Enable I2C target. Signed-off-by: iabdalkader --- .../arduino_giga_r1_stm32h747xx_m7.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/variants/arduino_giga_r1_stm32h747xx_m7/arduino_giga_r1_stm32h747xx_m7.conf b/variants/arduino_giga_r1_stm32h747xx_m7/arduino_giga_r1_stm32h747xx_m7.conf index 14f73783..86a2c86b 100644 --- a/variants/arduino_giga_r1_stm32h747xx_m7/arduino_giga_r1_stm32h747xx_m7.conf +++ b/variants/arduino_giga_r1_stm32h747xx_m7/arduino_giga_r1_stm32h747xx_m7.conf @@ -21,6 +21,7 @@ CONFIG_FPU=y CONFIG_ADC=y CONFIG_DAC=y CONFIG_PWM=y +CONFIG_I2C_TARGET=y CONFIG_ICACHE=y CONFIG_DCACHE=y