diff options
-rw-r--r-- | lib/pbdrv/drv/arduino/include.cmake | 4 | ||||
-rw-r--r-- | lib/pbdrv/drv/arduino/index.dox | 4 | ||||
-rw-r--r-- | lib/pbdrv/drv/arduino/mod.cpp | 51 |
3 files changed, 46 insertions, 13 deletions
diff --git a/lib/pbdrv/drv/arduino/include.cmake b/lib/pbdrv/drv/arduino/include.cmake index 520bfc1..5ec1124 100644 --- a/lib/pbdrv/drv/arduino/include.cmake +++ b/lib/pbdrv/drv/arduino/include.cmake @@ -5,3 +5,7 @@ endif() target_sources(pbdrv-mod PRIVATE "${CMAKE_CURRENT_LIST_DIR}/mod.cpp") target_link_arduino_libraries(pbdrv-mod PRIVATE core Wire) +# freertos is used to defer the handling of i2c messages outside the receive +# interrupt service routine +include("${CMAKE_CURRENT_LIST_DIR}/../../ext/freertos/include.cmake") + diff --git a/lib/pbdrv/drv/arduino/index.dox b/lib/pbdrv/drv/arduino/index.dox index 1856918..03510dd 100644 --- a/lib/pbdrv/drv/arduino/index.dox +++ b/lib/pbdrv/drv/arduino/index.dox @@ -10,12 +10,14 @@ This driver is known to work with the following MCUs: \par Usage - Link the \c pbdrv-mod library with your main executable -- Load an \ref pb_ext "extension" \note This driver is automatically enabled if the variable \c ARDUINO is defined in your CMakeLists.txt (it is by default when using Arduino-CMake-Toolchain). +\note This driver automatically includes the \ref pb_ext_freertos +"FreeRTOS extension" for deferring calls to \c pb_i2c_recv() from the I2C ISR. + A complete example for this driver is available in the \ref puzzle/dummy folder. diff --git a/lib/pbdrv/drv/arduino/mod.cpp b/lib/pbdrv/drv/arduino/mod.cpp index d2a6402..c381077 100644 --- a/lib/pbdrv/drv/arduino/mod.cpp +++ b/lib/pbdrv/drv/arduino/mod.cpp @@ -1,22 +1,33 @@ -#ifndef ARDUINO -#error This driver only works on the Arduino platform! -#endif - #include <Arduino.h> #include <Wire.h> #include <avr/delay.h> +#include <FreeRTOS.h> +#include <timers.h> +#include <task.h> + #include "../../pb.h" #include "../../pb-mod.h" #include "../../pb-types.h" +#include "../../pb-buf.h" +#include "../../pb-mem.h" + +static void async_pb_i2c_recv(void * _msg, uint32_t _) { + pb_buf_t * msg = (pb_buf_t *) _msg; + pb_i2c_recv((uint8_t *) msg->data, msg->size); + pb_buf_free(msg); + pb_free(msg); +} static void recv_event(int bytes) { - uint8_t data[bytes]; - size_t sz = 0; + pb_buf_t * msg = (pb_buf_t *) pb_malloc(sizeof(pb_buf_t)); + msg->data = (char *) pb_malloc(bytes); + msg->size = 0; while (Wire.available()) - data[sz++] = Wire.read(); + msg->data[msg->size++] = Wire.read(); - pb_i2c_recv(data, sz); + // defer pb_i2c_recv call + xTimerPendFunctionCallFromISR(async_pb_i2c_recv, msg, 0, NULL); } static void pb_setup() { @@ -27,7 +38,17 @@ static void pb_setup() { Wire.onReceive(recv_event); } +/** + * \ingroup pb_drv_arduino + * \warning This function includes a hard-coded 10ms delay before sending. This + * is to work around a weird issue where the Arduino pulls both SDA and SCL low + * while attempting to initiate an I2C transmission. We were able to verify + * that the Arduino correctly handles bus arbitration under a test scenario + * with 2 Uno's, but ran into issues while integrating the Arduino's with the + * RP2040. + */ __weak void pb_i2c_send(i2c_addr_t addr, const uint8_t * buf, size_t sz) { + vTaskDelay(10 / portTICK_PERIOD_MS); // prevent bus collisions Wire.beginTransmission((int) addr); Wire.write(buf, sz); Wire.endTransmission(true); @@ -41,6 +62,14 @@ extern void loop(void); //! Arduino internal initialization void init(void); +//! FreeRTOS loop task +void loop_task() { + for(;;) { + loop(); + if (serialEventRun) serialEventRun(); + } +} + /** * \ingroup pb_drv_arduino * \brief Application entrypoint @@ -59,10 +88,8 @@ int main(void) { init(); // call arduino internal setup setup(); // call regular arduino setup pb_setup(); // call pbdrv-mod setup - for(;;) { - loop(); - if (serialEventRun) serialEventRun(); - } + xTaskCreate((TaskFunction_t) loop_task, "loop", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL); + vTaskStartScheduler(); // start freertos scheduler return 0; } |