Repository: incubator-mynewt-core Updated Branches: refs/heads/1_0_0_b1_dev 8f8d26a7b -> e4b6b6819
nrf52 i2c; implementation which does not depend on 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/e4b6b681 Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/tree/e4b6b681 Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/diff/e4b6b681 Branch: refs/heads/1_0_0_b1_dev Commit: e4b6b68197b8691b8ce15c18a7d6fae12f1ccca8 Parents: 8f8d26a Author: Marko Kiiskila <ma...@runtime.io> Authored: Fri Nov 18 17:37:53 2016 -0800 Committer: Marko Kiiskila <ma...@runtime.io> Committed: Fri Nov 18 17:37:53 2016 -0800 ---------------------------------------------------------------------- hw/mcu/nordic/nrf52xxx/src/hal_i2c.c | 388 +++++++++++++++++++++--------- 1 file changed, 277 insertions(+), 111 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/e4b6b681/hw/mcu/nordic/nrf52xxx/src/hal_i2c.c ---------------------------------------------------------------------- diff --git a/hw/mcu/nordic/nrf52xxx/src/hal_i2c.c b/hw/mcu/nordic/nrf52xxx/src/hal_i2c.c index 2bd00e9..a520f33 100644 --- a/hw/mcu/nordic/nrf52xxx/src/hal_i2c.c +++ b/hw/mcu/nordic/nrf52xxx/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/nrf52_hal.h> -#include <assert.h> -struct nrf52_hal_i2c { - nrf_drv_twi_t nhi_nrf_master; -}; +#include <nrf.h> #define NRF52_HAL_I2C_MAX (2) +#define NRF52_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 NRF52_SDA_PIN_CONF NRF52_SCL_PIN_CONF + +#define NRF52_HAL_I2C_RESOLVE(__n, __v) \ + if ((__n) >= NRF52_HAL_I2C_MAX) { \ + rc = EINVAL; \ + goto err; \ + } \ + (__v) = (struct nrf52_hal_i2c *) nrf52_hal_i2cs[(__n)]; \ + if ((__v) == NULL) { \ + rc = EINVAL; \ + goto err; \ + } + +struct nrf52_hal_i2c { + NRF_TWI_Type *nhi_regs; +}; + #if MYNEWT_VAL(I2C_0) -struct nrf52_hal_i2c hal_twi_i2c0 = {NRF_DRV_TWI_INSTANCE(0)}; +struct nrf52_hal_i2c hal_twi_i2c0 = { + .nhi_regs = NRF_TWI0 +}; #endif #if MYNEWT_VAL(I2C_1) -struct nrf52_hal_i2c hal_twi_i2c1 = {NRF_DRV_TWI_INSTANCE(1)}; +struct nrf52_hal_i2c hal_twi_i2c1 = { + .nhi_regs = NRF_TWI1 +}; #endif static const struct nrf52_hal_i2c *nrf52_hal_i2cs[NRF52_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 NRF52_HAL_I2C_RESOLVE(__n, __v) \ - if ((__n) >= NRF52_HAL_I2C_MAX) { \ - rc = EINVAL; \ - goto err; \ - } \ - (__v) = (struct nrf52_hal_i2c *) nrf52_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 nrf52_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 nrf52_hal_i2c *i2c; - struct nrf52_hal_i2c_cfg *i2c_cfg; - nrf_drv_twi_config_t cfg; + NRF_TWI_Type *regs; + struct nrf52_hal_i2c_cfg *cfg; + uint32_t freq; int rc; assert(usercfg != NULL); NRF52_HAL_I2C_RESOLVE(i2c_num, i2c); - i2c_cfg = (struct nrf52_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 nrf52_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] = NRF52_SCL_PIN_CONF; + NRF_GPIO->PIN_CNF[cfg->sda_pin] = NRF52_SDA_PIN_CONF; + hal_i2c_clear_bus(cfg); + + NRF_GPIO->PIN_CNF[cfg->scl_pin] = NRF52_SCL_PIN_CONF; + NRF_GPIO->PIN_CNF[cfg->sda_pin] = NRF52_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 nrf52_hal_i2c *i2c; - bool no_stop; + NRF_TWI_Type *regs = NULL; int rc; + int i; + uint32_t start; NRF52_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 nrf52_hal_i2c *i2c; - bool no_stop; - int rc; + NRF_TWI_Type *regs = NULL; + int rc = -1; + int i; + uint32_t start; NRF52_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 nrf52_hal_i2c *i2c; - int rc; - - NRF52_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 nrf52_hal_i2c *i2c; + struct hal_i2c_master_data rx; uint8_t buf; - int rc; - - NRF52_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); }