I2C Fallback - add support for non Open-drain boards

Use this forum for requesting small changes in ChibiOS. Large changes should be discussed in the development forum. This forum is NOT for support.
dexter93
Posts: 1
Joined: Mon May 29, 2023 9:20 pm

I2C Fallback - add support for non Open-drain boards

Postby dexter93 » Mon May 29, 2023 9:28 pm

Not all supported boards have Open-drain as an option for GPIO configuration, which is an assumption the I2C fallback driver makes.
Small patch to enable support for basic input/output I2C bitbang. Tested up to ~350kHz

furthermore, some delays have been added here to make sure the driver is following the I2C specifications

Code: Select all

diff --git a/os/hal/lib/fallback/I2C/hal_i2c_lld.c b/os/hal/lib/fallback/I2C/hal_i2c_lld.c
index 205fa19d47..f01acce89a 100644
--- a/os/hal/lib/fallback/I2C/hal_i2c_lld.c
+++ b/os/hal/lib/fallback/I2C/hal_i2c_lld.c
@@ -29,13 +29,23 @@
 /*===========================================================================*/
 /* Driver local definitions.                                                 */
 /*===========================================================================*/
-
-#define CHECK_ERROR(msg)                                                    \
-  if ((msg) < (msg_t)0) {                                                   \
-    palSetLine(i2cp->config->sda);                                          \
-    palSetLine(i2cp->config->scl);                                          \
-    return MSG_TIMEOUT;                                                     \
-  }
+#if (SW_I2C_USE_OPENDRAIN == TRUE) || defined(__DOXYGEN__)
+#  define CHECK_ERROR(msg)                                                    \
+    if ((msg) < (msg_t)0) {                                                   \
+      palSetLine(i2cp->config->sda);                                          \
+      palSetLine(i2cp->config->scl);                                          \
+      return MSG_TIMEOUT;                                                     \
+    }
+#else
+#  define CHECK_ERROR(msg)                                                    \
+    if ((msg) < (msg_t)0) {                                                   \
+      palSetLineMode(i2cp->config->scl, PAL_MODE_OUTPUT_PUSHPULL);            \
+      palSetLineMode(i2cp->config->sda, PAL_MODE_OUTPUT_PUSHPULL);            \
+      palSetLine(i2cp->config->sda);                                          \
+      palSetLine(i2cp->config->scl);                                          \
+      return MSG_TIMEOUT;                                                     \
+    }
+#endif
 
 /*===========================================================================*/
 /* Driver constants.                                                         */
@@ -85,12 +95,17 @@ static inline void i2c_delay(I2CDriver *i2cp) {
 }
 
 static inline msg_t i2c_check_arbitration(I2CDriver *i2cp) {
-
+#if (SW_I2C_USE_OPENDRAIN == FALSE)
+  palSetLineMode(i2cp->config->sda, PAL_MODE_INPUT);
+#endif
+  i2c_delay(i2cp);
   if (palReadLine(i2cp->config->sda) == PAL_LOW) {
     i2cp->errors |= I2C_ARBITRATION_LOST;
     return MSG_RESET;
   }
-
+#if (SW_I2C_USE_OPENDRAIN == FALSE)
+  palSetLineMode(i2cp->config->sda, PAL_MODE_OUTPUT_PUSHPULL);
+#endif
   return MSG_OK;
 }
 
@@ -106,6 +121,10 @@ static inline msg_t i2c_check_timeout(I2CDriver *i2cp) {
 }
 
 static msg_t i2c_wait_clock(I2CDriver *i2cp) {
+#if (SW_I2C_USE_OPENDRAIN == FALSE)
+  palSetLineMode(i2cp->config->scl, PAL_MODE_INPUT);
+#endif
+  i2c_delay(i2cp);
 
   while (palReadLine(i2cp->config->scl) == PAL_LOW) {
     if ((i2cp->start != i2cp->end) &&
@@ -114,7 +133,9 @@ static msg_t i2c_wait_clock(I2CDriver *i2cp) {
     }
     i2c_delay(i2cp);
   }
-
+#if (SW_I2C_USE_OPENDRAIN == FALSE)
+  palSetLineMode(i2cp->config->scl, PAL_MODE_OUTPUT_PUSHPULL);
+#endif
   return MSG_OK;
 }
 
@@ -126,6 +147,7 @@ static inline msg_t i2c_write_start(I2CDriver *i2cp) {
   palClearLine(i2cp->config->sda);
   i2c_delay(i2cp);
   palClearLine(i2cp->config->scl);
+  i2c_delay(i2cp);
 
   return MSG_OK;
 }
@@ -169,6 +191,7 @@ static msg_t i2c_write_stop(I2CDriver *i2cp) {
 static msg_t i2c_write_bit(I2CDriver *i2cp, unsigned bit) {
 
   palWriteLine(i2cp->config->sda, bit);
+
   i2c_delay(i2cp);
   palSetLine(i2cp->config->scl);
   i2c_delay(i2cp);
@@ -182,14 +205,16 @@ static msg_t i2c_write_bit(I2CDriver *i2cp, unsigned bit) {
   }
 
   palClearLine(i2cp->config->scl);
+  i2c_delay(i2cp);
 
   return MSG_OK;
 }
 
 static msg_t i2c_read_bit(I2CDriver *i2cp) {
   msg_t bit;
-
-  palSetLine(i2cp->config->sda);
+#if (SW_I2C_USE_OPENDRAIN == FALSE)
+  palSetLineMode(i2cp->config->sda, PAL_MODE_INPUT);
+#endif
   i2c_delay(i2cp);
   palSetLine(i2cp->config->scl);
 
@@ -197,22 +222,33 @@ static msg_t i2c_read_bit(I2CDriver *i2cp) {
   CHECK_ERROR(i2c_wait_clock(i2cp));
 
   i2c_delay(i2cp);
+
   bit = palReadLine(i2cp->config->sda);
+#if (SW_I2C_USE_OPENDRAIN == FALSE)
+  palSetLineMode(i2cp->config->sda, PAL_MODE_OUTPUT_PUSHPULL);
+#endif
   palClearLine(i2cp->config->scl);
+  i2c_delay(i2cp);
 
   return bit;
 }
 
 static msg_t i2c_write_byte(I2CDriver *i2cp, uint8_t byte) {
   msg_t msg;
-  uint8_t mask;
 
   CHECK_ERROR(i2c_check_timeout(i2cp));
 
+#if (SW_I2C_USE_OPENDRAIN == TRUE) || defined(__DOXYGEN__)
+  uint8_t mask;
   for (mask = 0x80U; mask > 0U; mask >>= 1U) {
     CHECK_ERROR(i2c_write_bit(i2cp, (byte & mask) != 0));
   }
-
+#else
+  for(uint8_t i = 0; i < 8; i++) {
+    unsigned bit = ((0x80U >> i) & byte);
+    CHECK_ERROR(i2c_write_bit(i2cp, bit));
+  }
+#endif
   msg = i2c_read_bit(i2cp);
   CHECK_ERROR(msg);
 
diff --git a/os/hal/lib/fallback/I2C/hal_i2c_lld.h b/os/hal/lib/fallback/I2C/hal_i2c_lld.h
index c0a8d37baf..f2c43ac2b8 100644
--- a/os/hal/lib/fallback/I2C/hal_i2c_lld.h
+++ b/os/hal/lib/fallback/I2C/hal_i2c_lld.h
@@ -39,6 +39,16 @@
  * @name    Configuration options
  * @{
  */
+/**
+ * @brief   Use Open-drain configuration.
+ * @details If set to @p TRUE then the driver expects the SDA and SCL
+ *          pins to be in open-drain configuration else it handles
+ *          I2C functionality by switching input and output state.
+ */
+#if !defined(SW_I2C_USE_OPENDRAIN) || defined(__DOXYGEN__)
+#define SW_I2C_USE_OPENDRAIN                FALSE
+#endif
+
 /**
  * @brief   Use OSAL delays.
  * @details If set to @p TRUE then delays are implemented using the

Return to “Small Change Requests”

Who is online

Users browsing this forum: No registered users and 12 guests