You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by ma...@apache.org on 2016/11/19 01:55:53 UTC

incubator-mynewt-core git commit: nrf51 i2c; implement driver without using nordic's SDK.

Repository: incubator-mynewt-core
Updated Branches:
  refs/heads/1_0_0_b1_dev e4b6b6819 -> 183dff92c


nrf51 i2c; implement driver without using nordic's SDK.


Project: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/commit/183dff92
Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/tree/183dff92
Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/diff/183dff92

Branch: refs/heads/1_0_0_b1_dev
Commit: 183dff92ce64817f3b2d53b180fe87a7a4cb609e
Parents: e4b6b68
Author: Marko Kiiskila <ma...@runtime.io>
Authored: Fri Nov 18 17:55:08 2016 -0800
Committer: Marko Kiiskila <ma...@runtime.io>
Committed: Fri Nov 18 17:55:08 2016 -0800

----------------------------------------------------------------------
 hw/mcu/nordic/nrf51xxx/src/hal_i2c.c | 388 +++++++++++++++++++++---------
 1 file changed, 277 insertions(+), 111 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/183dff92/hw/mcu/nordic/nrf51xxx/src/hal_i2c.c
----------------------------------------------------------------------
diff --git a/hw/mcu/nordic/nrf51xxx/src/hal_i2c.c b/hw/mcu/nordic/nrf51xxx/src/hal_i2c.c
index ed651b2..394a325 100644
--- a/hw/mcu/nordic/nrf51xxx/src/hal_i2c.c
+++ b/hw/mcu/nordic/nrf51xxx/src/hal_i2c.c
@@ -17,89 +17,227 @@
  * under the License.
  */
 
-#include <hal/hal_i2c.h>
 #include <string.h>
 #include <errno.h>
+#include <limits.h>
+#include <assert.h>
+
+#include <os/os_time.h>
+
 #include "syscfg/syscfg.h"
-#include <nrf.h>
-#include <nrf_drv_twi.h>
+#include <hal/hal_i2c.h>
+#include <hal/hal_gpio.h>
 #include <mcu/nrf51_hal.h>
-#include <assert.h>
 
-struct nrf51_hal_i2c {
-    nrf_drv_twi_t nhi_nrf_master;
-};
+#include <nrf.h>
 
 #define NRF51_HAL_I2C_MAX (2)
 
+#define NRF51_SCL_PIN_CONF                                              \
+    ((GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) |          \
+      (GPIO_PIN_CNF_DRIVE_S0D1    << GPIO_PIN_CNF_DRIVE_Pos) |          \
+      (GPIO_PIN_CNF_PULL_Pullup   << GPIO_PIN_CNF_PULL_Pos) |           \
+      (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |          \
+      (GPIO_PIN_CNF_DIR_Input     << GPIO_PIN_CNF_DIR_Pos))
+#define NRF51_SDA_PIN_CONF NRF51_SCL_PIN_CONF
+
+#define NRF51_HAL_I2C_RESOLVE(__n, __v)                      \
+    if ((__n) >= NRF51_HAL_I2C_MAX) {                        \
+        rc = EINVAL;                                         \
+        goto err;                                            \
+    }                                                        \
+    (__v) = (struct nrf51_hal_i2c *) nrf51_hal_i2cs[(__n)];  \
+    if ((__v) == NULL) {                                     \
+        rc = EINVAL;                                         \
+        goto err;                                            \
+    }
+
+struct nrf51_hal_i2c {
+    NRF_TWI_Type *nhi_regs;
+};
+
 #if MYNEWT_VAL(I2C_0)
-struct nrf51_hal_i2c hal_twi_i2c0 = {NRF_DRV_TWI_INSTANCE(0)};
+struct nrf51_hal_i2c hal_twi_i2c0 = {
+    .nhi_regs = NRF_TWI0
+};
 #endif
 #if MYNEWT_VAL(I2C_1)
-struct nrf51_hal_i2c hal_twi_i2c1 = {NRF_DRV_TWI_INSTANCE(1)};
+struct nrf51_hal_i2c hal_twi_i2c1 = {
+    .nhi_regs = NRF_TWI1
+};
 #endif
 
 static const struct nrf51_hal_i2c *nrf51_hal_i2cs[NRF51_HAL_I2C_MAX] = {
 #if MYNEWT_VAL(I2C_0)
-        &hal_twi_i2c0,
+    &hal_twi_i2c0,
 #else
-        NULL,
+    NULL,
 #endif
 #if MYNEWT_VAL(I2C_1)
-        &hal_twi_i2c1
+    &hal_twi_i2c1
 #else
-        NULL
+    NULL
 #endif
 };
 
-#define NRF51_HAL_I2C_RESOLVE(__n, __v)                      \
-    if ((__n) >= NRF51_HAL_I2C_MAX) {                        \
-        rc = EINVAL;                                         \
-        goto err;                                            \
-    }                                                        \
-    (__v) = (struct nrf51_hal_i2c *) nrf51_hal_i2cs[(__n)];  \
-    if ((__v) == NULL) {                                     \
-        rc = EINVAL;                                         \
-        goto err;                                            \
+static void
+hal_i2c_delay_us(uint32_t number_of_us)
+{
+register uint32_t delay __ASM ("r0") = number_of_us;
+__ASM volatile (
+#ifdef NRF51
+        ".syntax unified\n"
+#endif
+    "1:\n"
+    " SUBS %0, %0, #1\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+#ifdef NRF52
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+    " NOP\n"
+#endif
+    " BNE 1b\n"
+#ifdef NRF51
+    ".syntax divided\n"
+#endif
+    : "+r" (delay));
+}
+
+/*
+ * Clear the bus after reset by clocking 9 bits manually.
+ * This should reset state from (most of) the devices on the other end.
+ */
+static void
+hal_i2c_clear_bus(struct nrf51_hal_i2c_cfg *cfg)
+{
+    int i;
+
+    hal_gpio_init_out(cfg->scl_pin, 1);
+    hal_gpio_init_in(cfg->sda_pin, HAL_GPIO_PULL_UP);
+
+    hal_i2c_delay_us(4);
+
+    for (i = 0; i < 9; i++) {
+        if (hal_gpio_read(cfg->sda_pin)) {
+            if (i == 0) {
+                /*
+                 * Nothing to do here.
+                 */
+                return;
+            } else {
+                break;
+            }
+            hal_gpio_write(cfg->scl_pin, 0);
+            hal_i2c_delay_us(4);
+            hal_gpio_write(cfg->sda_pin, 1);
+            hal_i2c_delay_us(4);
+        }
     }
 
+    /*
+     * Send STOP.
+     */
+    hal_gpio_write(cfg->sda_pin, 0);
+    hal_i2c_delay_us(4);
+    hal_gpio_write(cfg->sda_pin, 1);
+}
+
 int
 hal_i2c_init(uint8_t i2c_num, void *usercfg)
 {
     struct nrf51_hal_i2c *i2c;
-    struct nrf51_hal_i2c_cfg *i2c_cfg;
-    nrf_drv_twi_config_t cfg;
+    NRF_TWI_Type *regs;
+    struct nrf51_hal_i2c_cfg *cfg;
+    uint32_t freq;
     int rc;
 
     assert(usercfg != NULL);
 
     NRF51_HAL_I2C_RESOLVE(i2c_num, i2c);
 
-    i2c_cfg = (struct nrf51_hal_i2c_cfg *) usercfg;
-
-    cfg.scl = i2c_cfg->scl_pin;
-    cfg.sda = i2c_cfg->sda_pin;
-    switch (i2c_cfg->i2c_frequency) {
-        case 100:
-            cfg.frequency = NRF_TWI_FREQ_100K;
-            break;
-        case 250:
-            cfg.frequency = NRF_TWI_FREQ_250K;
-            break;
-        case 400:
-            cfg.frequency = NRF_TWI_FREQ_400K;
-            break;
-        default:
-            rc = EINVAL;
-            goto err;
-    }
-
-    rc = nrf_drv_twi_init(&i2c->nhi_nrf_master, &cfg, NULL, NULL);
-    if (rc != 0) {
+    cfg = (struct nrf51_hal_i2c_cfg *) usercfg;
+    regs = i2c->nhi_regs;
+
+    switch (cfg->i2c_frequency) {
+    case 100:
+        freq = TWI_FREQUENCY_FREQUENCY_K100;
+        break;
+    case 250:
+        freq = TWI_FREQUENCY_FREQUENCY_K250;
+        break;
+    case 400:
+        freq = TWI_FREQUENCY_FREQUENCY_K400;
+        break;
+    default:
+        rc = EINVAL;
         goto err;
     }
 
-    nrf_drv_twi_enable(&i2c->nhi_nrf_master);
+    NRF_GPIO->PIN_CNF[cfg->scl_pin] = NRF51_SCL_PIN_CONF;
+    NRF_GPIO->PIN_CNF[cfg->sda_pin] = NRF51_SDA_PIN_CONF;
+    hal_i2c_clear_bus(cfg);
+
+    NRF_GPIO->PIN_CNF[cfg->scl_pin] = NRF51_SCL_PIN_CONF;
+    NRF_GPIO->PIN_CNF[cfg->sda_pin] = NRF51_SDA_PIN_CONF;
+
+    regs->PSELSCL = cfg->scl_pin;
+    regs->PSELSDA = cfg->sda_pin;
+    regs->FREQUENCY = freq;
+    regs->ENABLE = TWI_ENABLE_ENABLE_Enabled;
 
     return (0);
 err:
@@ -111,26 +249,59 @@ hal_i2c_master_write(uint8_t i2c_num, struct hal_i2c_master_data *pdata,
                      uint32_t timo, uint8_t last_op)
 {
     struct nrf51_hal_i2c *i2c;
-    bool no_stop;
+    NRF_TWI_Type *regs = NULL;
     int rc;
+    int i;
+    uint32_t start;
 
     NRF51_HAL_I2C_RESOLVE(i2c_num, i2c);
-
-    /* If last_op is zero it means we dont put a stop at end. */
-    if (last_op == 0) {
-        no_stop = true;
-    } else {
-        no_stop = false;
+    regs = i2c->nhi_regs;
+
+    regs->ADDRESS = pdata->address;
+
+    regs->EVENTS_ERROR = 0;
+    regs->EVENTS_STOPPED = 0;
+    regs->EVENTS_SUSPENDED = 0;
+    regs->SHORTS = 0;
+
+    regs->TASKS_STARTTX = 1;
+    regs->TASKS_RESUME = 1;
+
+    start = os_time_get();
+    for (i = 0; i < pdata->len; i++) {
+        regs->EVENTS_TXDSENT = 0;
+        regs->TXD = pdata->buffer[i];
+        while (!regs->EVENTS_TXDSENT && !regs->EVENTS_ERROR) {
+            if (os_time_get() - start > timo) {
+                rc = -1;
+                regs->TASKS_STOP = 1;
+                goto err;
+            }
+        }
+        if (regs->EVENTS_ERROR) {
+            goto err;
+        }
     }
-
-    rc = nrf_drv_twi_tx(&i2c->nhi_nrf_master, pdata->address, pdata->buffer,
-            pdata->len, no_stop);
-    if (rc != 0) {
-        goto err;
+    /* If last_op is zero it means we dont put a stop at end. */
+    if (last_op) {
+        regs->EVENTS_STOPPED = 0;
+        regs->TASKS_STOP = 1;
+        while (!regs->EVENTS_STOPPED && !regs->EVENTS_ERROR) {
+            if (os_time_get() - start > timo) {
+                rc = -1;
+                goto err;
+            }
+        }
+        if (regs->EVENTS_ERROR) {
+            goto err;
+        }
     }
-
     return (0);
 err:
+    if (regs && regs->EVENTS_ERROR) {
+        rc = regs->ERRORSRC;
+        regs->ERRORSRC = rc;
+    }
     return (rc);
 }
 
@@ -139,73 +310,68 @@ hal_i2c_master_read(uint8_t i2c_num, struct hal_i2c_master_data *pdata,
                     uint32_t timo, uint8_t last_op)
 {
     struct nrf51_hal_i2c *i2c;
-    bool no_stop;
-    int rc;
+    NRF_TWI_Type *regs = NULL;
+    int rc = -1;
+    int i;
+    uint32_t start;
 
     NRF51_HAL_I2C_RESOLVE(i2c_num, i2c);
+    regs = i2c->nhi_regs;
 
-    if (last_op) {
-        no_stop = false;
-    } else {
-        no_stop = true;
-    }
+    start = os_time_get();
 
-    rc = nrf_drv_twi_rx_ext(&i2c->nhi_nrf_master, pdata->address, pdata->buffer,
-                            pdata->len, no_stop);
-    if (rc != 0) {
-        goto err;
-    }
+    regs->EVENTS_ERROR = 0;
+    regs->EVENTS_STOPPED = 0;
+    regs->EVENTS_SUSPENDED = 0;
+    regs->EVENTS_RXDREADY = 0;
 
-    return (0);
-err:
-    return (rc);
-}
-
-int
-hal_i2c_master_begin(uint8_t i2c_num)
-{
-    return (0);
-}
-
-int
-hal_i2c_master_end(uint8_t i2c_num)
-{
-    struct nrf51_hal_i2c *i2c;
-    int rc;
-
-    NRF51_HAL_I2C_RESOLVE(i2c_num, i2c);
-
-    /**
-     * XXX: Nordic doesn't provide a function for generating the stop
-     * character, and I don't trust issue'ing nrf_drv_twi_tx() with NULL
-     * data, and 0 length, so directly use the stop task in HAL driver.
-     * This is subject to break as NRF SDK is updated, as reg.p_twi is
-     * private, however, seems like it will be reasonably easy to spot.
-     * Famous last words.
-     */
-    nrf_twi_task_trigger(i2c->nhi_nrf_master.reg.p_twi, NRF_TWI_TASK_RESUME);
-    nrf_twi_task_trigger(i2c->nhi_nrf_master.reg.p_twi, NRF_TWI_TASK_STOP);
+    regs->ADDRESS = pdata->address;
 
+    if (pdata->len == 1 && last_op) {
+        regs->SHORTS = TWI_SHORTS_BB_STOP_Msk;
+    } else {
+        regs->SHORTS = TWI_SHORTS_BB_SUSPEND_Msk;
+    }
+    regs->TASKS_STARTRX = 1;
+
+    for (i = 0; i < pdata->len; i++) {
+        regs->TASKS_RESUME = 1;
+
+        while (!regs->EVENTS_RXDREADY && !regs->EVENTS_ERROR) {
+            if (os_time_get() - start > timo) {
+                regs->SHORTS = TWI_SHORTS_BB_STOP_Msk;
+                goto err;
+            }
+        }
+        if (regs->EVENTS_ERROR) {
+            goto err;
+        }
+        pdata->buffer[i] = regs->RXD;
+        if (i == pdata->len - 2) {
+            if (last_op) {
+                regs->SHORTS = TWI_SHORTS_BB_STOP_Msk;
+            }
+        }
+        regs->EVENTS_RXDREADY = 0;
+    }
     return (0);
 err:
+    if (regs && regs->EVENTS_ERROR) {
+        rc = regs->ERRORSRC;
+        regs->ERRORSRC = rc;
+    }
     return (rc);
 }
 
 int
 hal_i2c_master_probe(uint8_t i2c_num, uint8_t address, uint32_t timo)
 {
-    struct nrf51_hal_i2c *i2c;
+    struct hal_i2c_master_data rx;
     uint8_t buf;
-    int rc;
-
-    NRF51_HAL_I2C_RESOLVE(i2c_num, i2c);
 
-    rc = nrf_drv_twi_rx(&i2c->nhi_nrf_master, address, &buf, 1);
-    if (rc != 0) {
-        goto err;
-    }
+    rx.address = address;
+    rx.buffer = &buf;
+    rx.len = 1;
 
-    return (0);
-err:
-    return (rc);
+    return hal_i2c_master_read(i2c_num, &rx, timo, 1);
 }