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