diff options
author | tcsullivan <tullivan99@gmail.com> | 2019-03-10 15:37:07 -0400 |
---|---|---|
committer | tcsullivan <tullivan99@gmail.com> | 2019-03-10 15:37:07 -0400 |
commit | dd33956654589ded6644a75088e50069b1744ef9 (patch) | |
tree | eddd51f1aac130f6c7082a2de53b8e46f0387187 /drivers_nrf/twi_master | |
parent | 3c3f87b4cab153b49e3cde105dd2f34712e0b790 (diff) |
rtc, keeping time
Diffstat (limited to 'drivers_nrf/twi_master')
-rw-r--r-- | drivers_nrf/twi_master/deprecated/config/twi_master_config.h | 55 | ||||
-rw-r--r-- | drivers_nrf/twi_master/deprecated/twi_hw_master.c | 331 | ||||
-rw-r--r-- | drivers_nrf/twi_master/deprecated/twi_master.h | 137 | ||||
-rw-r--r-- | drivers_nrf/twi_master/deprecated/twi_sw_master.c | 519 | ||||
-rw-r--r-- | drivers_nrf/twi_master/nrf_drv_twi.c | 1264 | ||||
-rw-r--r-- | drivers_nrf/twi_master/nrf_drv_twi.h | 438 |
6 files changed, 2744 insertions, 0 deletions
diff --git a/drivers_nrf/twi_master/deprecated/config/twi_master_config.h b/drivers_nrf/twi_master/deprecated/config/twi_master_config.h new file mode 100644 index 0000000..8a161cc --- /dev/null +++ b/drivers_nrf/twi_master/deprecated/config/twi_master_config.h @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2012 - 2017, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef TWI_MASTER_CONFIG +#define TWI_MASTER_CONFIG + +#ifdef __cplusplus +extern "C" { +#endif + +#define TWI_MASTER_CONFIG_CLOCK_PIN_NUMBER (24U) +#define TWI_MASTER_CONFIG_DATA_PIN_NUMBER (25U) + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/drivers_nrf/twi_master/deprecated/twi_hw_master.c b/drivers_nrf/twi_master/deprecated/twi_hw_master.c new file mode 100644 index 0000000..767abf0 --- /dev/null +++ b/drivers_nrf/twi_master/deprecated/twi_hw_master.c @@ -0,0 +1,331 @@ +/** + * Copyright (c) 2009 - 2017, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "twi_master.h" +#include "twi_master_config.h" +#include <stdbool.h> +#include <stdint.h> +#include "nrf.h" +#include "nrf_delay.h" +#include "nrf_gpio.h" + +/* Max cycles approximately to wait on RXDREADY and TXDREADY event, + * This is optimized way instead of using timers, this is not power aware. */ +#define MAX_TIMEOUT_LOOPS (20000UL) /**< MAX while loops to wait for RXD/TXD event */ + +static bool twi_master_write(uint8_t * data, uint8_t data_length, bool issue_stop_condition) +{ + uint32_t timeout = MAX_TIMEOUT_LOOPS; /* max loops to wait for EVENTS_TXDSENT event*/ + + if (data_length == 0) + { + /* Return false for requesting data of size 0 */ + return false; + } + + NRF_TWI1->TXD = *data++; + NRF_TWI1->TASKS_STARTTX = 1; + + /** @snippet [TWI HW master write] */ + while (true) + { + while (NRF_TWI1->EVENTS_TXDSENT == 0 && NRF_TWI1->EVENTS_ERROR == 0 && (--timeout)) + { + // Do nothing. + } + + if (timeout == 0 || NRF_TWI1->EVENTS_ERROR != 0) + { + // Recover the peripheral as indicated by PAN 56: "TWI: TWI module lock-up." found at + // Product Anomaly Notification document found at + // https://www.nordicsemi.com/eng/Products/Bluetooth-R-low-energy/nRF51822/#Downloads + NRF_TWI1->EVENTS_ERROR = 0; + NRF_TWI1->ENABLE = TWI_ENABLE_ENABLE_Disabled << TWI_ENABLE_ENABLE_Pos; + NRF_TWI1->POWER = 0; + nrf_delay_us(5); + NRF_TWI1->POWER = 1; + NRF_TWI1->ENABLE = TWI_ENABLE_ENABLE_Enabled << TWI_ENABLE_ENABLE_Pos; + + (void)twi_master_init(); + + return false; + } + NRF_TWI1->EVENTS_TXDSENT = 0; + if (--data_length == 0) + { + break; + } + + NRF_TWI1->TXD = *data++; + } + /** @snippet [TWI HW master write] */ + + if (issue_stop_condition) + { + NRF_TWI1->EVENTS_STOPPED = 0; + NRF_TWI1->TASKS_STOP = 1; + /* Wait until stop sequence is sent */ + while (NRF_TWI1->EVENTS_STOPPED == 0) + { + // Do nothing. + } + } + return true; +} + + +/** @brief Function for read by twi_master. + */ +static bool twi_master_read(uint8_t * data, uint8_t data_length, bool issue_stop_condition) +{ + uint32_t timeout = MAX_TIMEOUT_LOOPS; /* max loops to wait for RXDREADY event*/ + + if (data_length == 0) + { + /* Return false for requesting data of size 0 */ + return false; + } + else if (data_length == 1) + { + NRF_PPI->CH[0].TEP = (uint32_t)&NRF_TWI1->TASKS_STOP; + } + else + { + NRF_PPI->CH[0].TEP = (uint32_t)&NRF_TWI1->TASKS_SUSPEND; + } + + NRF_PPI->CHENSET = PPI_CHENSET_CH0_Msk; + NRF_TWI1->EVENTS_RXDREADY = 0; + NRF_TWI1->TASKS_STARTRX = 1; + + /** @snippet [TWI HW master read] */ + while (true) + { + while (NRF_TWI1->EVENTS_RXDREADY == 0 && NRF_TWI1->EVENTS_ERROR == 0 && (--timeout)) + { + // Do nothing. + } + NRF_TWI1->EVENTS_RXDREADY = 0; + + if (timeout == 0 || NRF_TWI1->EVENTS_ERROR != 0) + { + // Recover the peripheral as indicated by PAN 56: "TWI: TWI module lock-up." found at + // Product Anomaly Notification document found at + // https://www.nordicsemi.com/eng/Products/Bluetooth-R-low-energy/nRF51822/#Downloads + NRF_TWI1->EVENTS_ERROR = 0; + NRF_TWI1->ENABLE = TWI_ENABLE_ENABLE_Disabled << TWI_ENABLE_ENABLE_Pos; + NRF_TWI1->POWER = 0; + nrf_delay_us(5); + NRF_TWI1->POWER = 1; + NRF_TWI1->ENABLE = TWI_ENABLE_ENABLE_Enabled << TWI_ENABLE_ENABLE_Pos; + + (void)twi_master_init(); + + return false; + } + + *data++ = NRF_TWI1->RXD; + + /* Configure PPI to stop TWI master before we get last BB event */ + if (--data_length == 1) + { + NRF_PPI->CH[0].TEP = (uint32_t)&NRF_TWI1->TASKS_STOP; + } + + if (data_length == 0) + { + break; + } + + // Recover the peripheral as indicated by PAN 56: "TWI: TWI module lock-up." found at + // Product Anomaly Notification document found at + // https://www.nordicsemi.com/eng/Products/Bluetooth-R-low-energy/nRF51822/#Downloads + nrf_delay_us(20); + NRF_TWI1->TASKS_RESUME = 1; + } + /** @snippet [TWI HW master read] */ + + /* Wait until stop sequence is sent */ + while (NRF_TWI1->EVENTS_STOPPED == 0) + { + // Do nothing. + } + NRF_TWI1->EVENTS_STOPPED = 0; + + NRF_PPI->CHENCLR = PPI_CHENCLR_CH0_Msk; + return true; +} + + +/** + * @brief Function for detecting stuck slaves (SDA = 0 and SCL = 1) and tries to clear the bus. + * + * @return + * @retval false Bus is stuck. + * @retval true Bus is clear. + */ +static bool twi_master_clear_bus(void) +{ + uint32_t twi_state; + bool bus_clear; + uint32_t clk_pin_config; + uint32_t data_pin_config; + + // Save and disable TWI hardware so software can take control over the pins. + twi_state = NRF_TWI1->ENABLE; + NRF_TWI1->ENABLE = TWI_ENABLE_ENABLE_Disabled << TWI_ENABLE_ENABLE_Pos; + + clk_pin_config = \ + NRF_GPIO->PIN_CNF[TWI_MASTER_CONFIG_CLOCK_PIN_NUMBER]; + NRF_GPIO->PIN_CNF[TWI_MASTER_CONFIG_CLOCK_PIN_NUMBER] = \ + (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_Output << GPIO_PIN_CNF_DIR_Pos); + + data_pin_config = \ + NRF_GPIO->PIN_CNF[TWI_MASTER_CONFIG_DATA_PIN_NUMBER]; + NRF_GPIO->PIN_CNF[TWI_MASTER_CONFIG_DATA_PIN_NUMBER] = \ + (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_Output << GPIO_PIN_CNF_DIR_Pos); + + TWI_SDA_HIGH(); + TWI_SCL_HIGH(); + TWI_DELAY(); + + if ((TWI_SDA_READ() == 1) && (TWI_SCL_READ() == 1)) + { + bus_clear = true; + } + else + { + uint_fast8_t i; + bus_clear = false; + + // Clock max 18 pulses worst case scenario(9 for master to send the rest of command and 9 + // for slave to respond) to SCL line and wait for SDA come high. + for (i=18; i--;) + { + TWI_SCL_LOW(); + TWI_DELAY(); + TWI_SCL_HIGH(); + TWI_DELAY(); + + if (TWI_SDA_READ() == 1) + { + bus_clear = true; + break; + } + } + } + + NRF_GPIO->PIN_CNF[TWI_MASTER_CONFIG_CLOCK_PIN_NUMBER] = clk_pin_config; + NRF_GPIO->PIN_CNF[TWI_MASTER_CONFIG_DATA_PIN_NUMBER] = data_pin_config; + + NRF_TWI1->ENABLE = twi_state; + + return bus_clear; +} + + +/** @brief Function for initializing the twi_master. + */ +bool twi_master_init(void) +{ + /* To secure correct signal levels on the pins used by the TWI + master when the system is in OFF mode, and when the TWI master is + disabled, these pins must be configured in the GPIO peripheral. + */ + NRF_GPIO->PIN_CNF[TWI_MASTER_CONFIG_CLOCK_PIN_NUMBER] = \ + (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); + + NRF_GPIO->PIN_CNF[TWI_MASTER_CONFIG_DATA_PIN_NUMBER] = \ + (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); + + NRF_TWI1->EVENTS_RXDREADY = 0; + NRF_TWI1->EVENTS_TXDSENT = 0; + NRF_TWI1->PSELSCL = TWI_MASTER_CONFIG_CLOCK_PIN_NUMBER; + NRF_TWI1->PSELSDA = TWI_MASTER_CONFIG_DATA_PIN_NUMBER; + NRF_TWI1->FREQUENCY = TWI_FREQUENCY_FREQUENCY_K100 << TWI_FREQUENCY_FREQUENCY_Pos; + NRF_PPI->CH[0].EEP = (uint32_t)&NRF_TWI1->EVENTS_BB; + NRF_PPI->CH[0].TEP = (uint32_t)&NRF_TWI1->TASKS_SUSPEND; + NRF_PPI->CHENCLR = PPI_CHENCLR_CH0_Msk; + NRF_TWI1->ENABLE = TWI_ENABLE_ENABLE_Enabled << TWI_ENABLE_ENABLE_Pos; + + return twi_master_clear_bus(); +} + + +/** @brief Function for transfer by twi_master. + */ +bool twi_master_transfer(uint8_t address, + uint8_t * data, + uint8_t data_length, + bool issue_stop_condition) +{ + bool transfer_succeeded = false; + if (data_length > 0 && twi_master_clear_bus()) + { + NRF_TWI1->ADDRESS = (address >> 1); + + if ((address & TWI_READ_BIT)) + { + transfer_succeeded = twi_master_read(data, data_length, issue_stop_condition); + } + else + { + transfer_succeeded = twi_master_write(data, data_length, issue_stop_condition); + } + } + return transfer_succeeded; +} + +/*lint --flb "Leave library region" */ diff --git a/drivers_nrf/twi_master/deprecated/twi_master.h b/drivers_nrf/twi_master/deprecated/twi_master.h new file mode 100644 index 0000000..4bceb87 --- /dev/null +++ b/drivers_nrf/twi_master/deprecated/twi_master.h @@ -0,0 +1,137 @@ +/** + * Copyright (c) 2009 - 2017, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef TWI_MASTER_H +#define TWI_MASTER_H + +/*lint ++flb "Enter library region" */ + +#include <stdbool.h> +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** @file +* @brief Software controlled TWI Master driver. +* +* +* @defgroup lib_driver_twi_master Software controlled TWI Master driver +* @{ +* @ingroup nrf_twi +* @brief Software controlled TWI Master driver (deprecated). +* +* @warning This module is deprecated. +* +* Supported features: +* - Repeated start +* - No multi-master +* - Only 7-bit addressing +* - Supports clock stretching (with optional SMBus style slave timeout) +* - Tries to handle slaves stuck in the middle of transfer +*/ + +#define TWI_READ_BIT (0x01) //!< If this bit is set in the address field, transfer direction is from slave to master. + +#define TWI_ISSUE_STOP ((bool)true) //!< Parameter for @ref twi_master_transfer +#define TWI_DONT_ISSUE_STOP ((bool)false) //!< Parameter for @ref twi_master_transfer + +/* These macros are needed to see if the slave is stuck and we as master send dummy clock cycles to end its wait */ +/*lint -e717 -save "Suppress do {} while (0) for these macros" */ +/*lint ++flb "Enter library region" */ +#define TWI_SCL_HIGH() do { NRF_GPIO->OUTSET = (1UL << TWI_MASTER_CONFIG_CLOCK_PIN_NUMBER); } while (0) /*!< Pulls SCL line high */ +#define TWI_SCL_LOW() do { NRF_GPIO->OUTCLR = (1UL << TWI_MASTER_CONFIG_CLOCK_PIN_NUMBER); } while (0) /*!< Pulls SCL line low */ +#define TWI_SDA_HIGH() do { NRF_GPIO->OUTSET = (1UL << TWI_MASTER_CONFIG_DATA_PIN_NUMBER); } while (0) /*!< Pulls SDA line high */ +#define TWI_SDA_LOW() do { NRF_GPIO->OUTCLR = (1UL << TWI_MASTER_CONFIG_DATA_PIN_NUMBER); } while (0) /*!< Pulls SDA line low */ +#define TWI_SDA_INPUT() do { NRF_GPIO->DIRCLR = (1UL << TWI_MASTER_CONFIG_DATA_PIN_NUMBER); } while (0) /*!< Configures SDA pin as input */ +#define TWI_SDA_OUTPUT() do { NRF_GPIO->DIRSET = (1UL << TWI_MASTER_CONFIG_DATA_PIN_NUMBER); } while (0) /*!< Configures SDA pin as output */ +#define TWI_SCL_OUTPUT() do { NRF_GPIO->DIRSET = (1UL << TWI_MASTER_CONFIG_CLOCK_PIN_NUMBER); } while (0) /*!< Configures SCL pin as output */ +/*lint -restore */ + +#define TWI_SDA_READ() ((NRF_GPIO->IN >> TWI_MASTER_CONFIG_DATA_PIN_NUMBER) & 0x1UL) /*!< Reads current state of SDA */ +#define TWI_SCL_READ() ((NRF_GPIO->IN >> TWI_MASTER_CONFIG_CLOCK_PIN_NUMBER) & 0x1UL) /*!< Reads current state of SCL */ + +#define TWI_DELAY() nrf_delay_us(4) /*!< Time to wait when pin states are changed. For fast-mode the delay can be zero and for standard-mode 4 us delay is sufficient. */ + + +/** + * @brief Function for initializing TWI bus IO pins and checks if the bus is operational. + * + * Both pins are configured as Standard-0, No-drive-1 (open drain). + * + * @return + * @retval true TWI bus is clear for transfers. + * @retval false TWI bus is stuck. + */ +bool twi_master_init(void); + +/** + * @brief Function for transferring data over TWI bus. + * + * If TWI master detects even one NACK from the slave or timeout occurs, STOP condition is issued + * and the function returns false. + * Bit 0 (@ref TWI_READ_BIT) in the address parameter controls transfer direction; + * - If 1, master reads data_length number of bytes from the slave + * - If 0, master writes data_length number of bytes to the slave. + * + * @note Make sure at least data_length number of bytes is allocated in data if TWI_READ_BIT is set. + * @note @ref TWI_ISSUE_STOP + * + * @param address Data transfer direction (LSB) / Slave address (7 MSBs). + * @param data Pointer to data. + * @param data_length Number of bytes to transfer. + * @param issue_stop_condition If @ref TWI_ISSUE_STOP, STOP condition is issued before exiting function. If @ref TWI_DONT_ISSUE_STOP, STOP condition is not issued before exiting function. If transfer failed for any reason, STOP condition will be issued in any case. + * @return + * @retval true Data transfer succeeded without errors. + * @retval false Data transfer failed. + */ +bool twi_master_transfer(uint8_t address, uint8_t *data, uint8_t data_length, bool issue_stop_condition); + +/** + *@} + **/ + +/*lint --flb "Leave library region" */ + +#ifdef __cplusplus +} +#endif + +#endif //TWI_MASTER_H diff --git a/drivers_nrf/twi_master/deprecated/twi_sw_master.c b/drivers_nrf/twi_master/deprecated/twi_sw_master.c new file mode 100644 index 0000000..44f03da --- /dev/null +++ b/drivers_nrf/twi_master/deprecated/twi_sw_master.c @@ -0,0 +1,519 @@ +/** + * Copyright (c) 2009 - 2017, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include <stdbool.h> +#include <stdint.h> +#include "twi_master.h" +#include "nrf_delay.h" + +#include "twi_master_config.h" + +/*lint -e415 -e845 -save "Out of bounds access" */ +#define TWI_SDA_STANDARD0_NODRIVE1() do { \ + NRF_GPIO->PIN_CNF[TWI_MASTER_CONFIG_DATA_PIN_NUMBER] = (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); \ +} while (0) /*!< Configures SDA pin to Standard-0, No-drive 1 */ + + +#define TWI_SCL_STANDARD0_NODRIVE1() do { \ + NRF_GPIO->PIN_CNF[TWI_MASTER_CONFIG_CLOCK_PIN_NUMBER] = (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); \ +} while (0) /*!< Configures SCL pin to Standard-0, No-drive 1 */ + + +/*lint -restore */ + +#ifndef TWI_MASTER_TIMEOUT_COUNTER_LOAD_VALUE +#define TWI_MASTER_TIMEOUT_COUNTER_LOAD_VALUE (0UL) //!< Unit is number of empty loops. Timeout for SMBus devices is 35 ms. Set to zero to disable slave timeout altogether. +#endif + +static bool twi_master_clear_bus(void); +static bool twi_master_issue_startcondition(void); +static bool twi_master_issue_stopcondition(void); +static bool twi_master_clock_byte(uint_fast8_t databyte); +static bool twi_master_clock_byte_in(uint8_t * databyte, bool ack); +static bool twi_master_wait_while_scl_low(void); + +bool twi_master_init(void) +{ + // Configure both pins to output Standard 0, No-drive (open-drain) 1 + TWI_SDA_STANDARD0_NODRIVE1(); /*lint !e416 "Creation of out of bounds pointer" */ + TWI_SCL_STANDARD0_NODRIVE1(); /*lint !e416 "Creation of out of bounds pointer" */ + + // Configure SCL as output + TWI_SCL_HIGH(); + TWI_SCL_OUTPUT(); + + // Configure SDA as output + TWI_SDA_HIGH(); + TWI_SDA_OUTPUT(); + + return twi_master_clear_bus(); +} + +bool twi_master_transfer(uint8_t address, uint8_t * data, uint8_t data_length, bool issue_stop_condition) +{ + bool transfer_succeeded = true; + + transfer_succeeded &= twi_master_issue_startcondition(); + transfer_succeeded &= twi_master_clock_byte(address); + + if (address & TWI_READ_BIT) + { + /* Transfer direction is from Slave to Master */ + while (data_length-- && transfer_succeeded) + { + // To indicate to slave that we've finished transferring last data byte + // we need to NACK the last transfer. + if (data_length == 0) + { + transfer_succeeded &= twi_master_clock_byte_in(data, (bool)false); + } + else + { + transfer_succeeded &= twi_master_clock_byte_in(data, (bool)true); + } + data++; + } + } + else + { + /* Transfer direction is from Master to Slave */ + while (data_length-- && transfer_succeeded) + { + transfer_succeeded &= twi_master_clock_byte(*data); + data++; + } + } + + if (issue_stop_condition || !transfer_succeeded) + { + transfer_succeeded &= twi_master_issue_stopcondition(); + } + + return transfer_succeeded; +} + +/** + * @brief Function for detecting stuck slaves and tries to clear the bus. + * + * @return + * @retval false Bus is stuck. + * @retval true Bus is clear. + */ +static bool twi_master_clear_bus(void) +{ + bool bus_clear; + + TWI_SDA_HIGH(); + TWI_SCL_HIGH(); + TWI_DELAY(); + + + if (TWI_SDA_READ() == 1 && TWI_SCL_READ() == 1) + { + bus_clear = true; + } + else if (TWI_SCL_READ() == 1) + { + bus_clear = false; + // Clock max 18 pulses worst case scenario(9 for master to send the rest of command and 9 for slave to respond) to SCL line and wait for SDA come high + for (uint_fast8_t i = 18; i--;) + { + TWI_SCL_LOW(); + TWI_DELAY(); + TWI_SCL_HIGH(); + TWI_DELAY(); + + if (TWI_SDA_READ() == 1) + { + bus_clear = true; + break; + } + } + } + else + { + bus_clear = false; + } + + return bus_clear; +} + +/** + * @brief Function for issuing TWI START condition to the bus. + * + * START condition is signaled by pulling SDA low while SCL is high. After this function SCL and SDA will be low. + * + * @return + * @retval false Timeout detected + * @retval true Clocking succeeded + */ +static bool twi_master_issue_startcondition(void) +{ +#if 0 + if (TWI_SCL_READ() == 1 && TWI_SDA_READ() == 1) + { + // Pull SDA low + TWI_SDA_LOW(); + } + else if (TWI_SCL_READ() == 1 && TWI_SDA_READ() == 0) + { + // Issue Stop by pulling SDA high + TWI_SDA_HIGH(); + TWI_DELAY(); + + // Then Start by pulling SDA low + TWI_SDA_LOW(); + } + else if (TWI_SCL_READ() == 0 && TWI_SDA_READ() == 0) + { + // First pull SDA high + TWI_SDA_HIGH(); + + // Then SCL high + if (!twi_master_wait_while_scl_low()) + { + return false; + } + + // Then SDA low + TWI_SDA_LOW(); + } + else if (TWI_SCL_READ() == 0 && TWI_SDA_READ() == 1) + { + // SCL high + if (!twi_master_wait_while_scl_low()) + { + return false; + } + + // Then SDA low + TWI_SDA_LOW(); + } + + TWI_DELAY(); + TWI_SCL_LOW(); +#endif + + // Make sure both SDA and SCL are high before pulling SDA low. + TWI_SDA_HIGH(); + TWI_DELAY(); + if (!twi_master_wait_while_scl_low()) + { + return false; + } + + TWI_SDA_LOW(); + TWI_DELAY(); + + // Other module function expect SCL to be low + TWI_SCL_LOW(); + TWI_DELAY(); + + return true; +} + +/** + * @brief Function for issuing TWI STOP condition to the bus. + * + * STOP condition is signaled by pulling SDA high while SCL is high. After this function SDA and SCL will be high. + * + * @return + * @retval false Timeout detected + * @retval true Clocking succeeded + */ +static bool twi_master_issue_stopcondition(void) +{ +#if 0 + if (TWI_SCL_READ() == 1 && TWI_SDA_READ() == 1) + { + // Issue start, then issue stop + + // Pull SDA low to issue START + TWI_SDA_LOW(); + TWI_DELAY(); + + // Pull SDA high while SCL is high to issue STOP + TWI_SDA_HIGH(); + } + else if (TWI_SCL_READ() == 1 && TWI_SDA_READ() == 0) + { + // Pull SDA high while SCL is high to issue STOP + TWI_SDA_HIGH(); + } + else if (TWI_SCL_READ() == 0 && TWI_SDA_READ() == 0) + { + if (!twi_master_wait_while_scl_low()) + { + return false; + } + + // Pull SDA high while SCL is high to issue STOP + TWI_SDA_HIGH(); + } + else if (TWI_SCL_READ() == 0 && TWI_SDA_READ() == 1) + { + TWI_SDA_LOW(); + TWI_DELAY(); + + // SCL high + if (!twi_master_wait_while_scl_low()) + { + return false; + } + + // Pull SDA high while SCL is high to issue STOP + TWI_SDA_HIGH(); + } + + TWI_DELAY(); +#endif + + TWI_SDA_LOW(); + TWI_DELAY(); + if (!twi_master_wait_while_scl_low()) + { + return false; + } + + TWI_SDA_HIGH(); + TWI_DELAY(); + + return true; +} + +/** + * @brief Function for clocking one data byte out and reads slave acknowledgment. + * + * Can handle clock stretching. + * After calling this function SCL is low and SDA low/high depending on the + * value of LSB of the data byte. + * SCL is expected to be output and low when entering this function. + * + * @param databyte Data byte to clock out. + * @return + * @retval true Slave acknowledged byte. + * @retval false Timeout or slave didn't acknowledge byte. + */ +static bool twi_master_clock_byte(uint_fast8_t databyte) +{ + bool transfer_succeeded = true; + + /** @snippet [TWI SW master write] */ + // Make sure SDA is an output + TWI_SDA_OUTPUT(); + + // MSB first + for (uint_fast8_t i = 0x80; i != 0; i >>= 1) + { + TWI_SCL_LOW(); + TWI_DELAY(); + + if (databyte & i) + { + TWI_SDA_HIGH(); + } + else + { + TWI_SDA_LOW(); + } + + if (!twi_master_wait_while_scl_low()) + { + transfer_succeeded = false; // Timeout + break; + } + } + + // Finish last data bit by pulling SCL low + TWI_SCL_LOW(); + TWI_DELAY(); + + /** @snippet [TWI SW master write] */ + + // Configure TWI_SDA pin as input for receiving the ACK bit + TWI_SDA_INPUT(); + + // Give some time for the slave to load the ACK bit on the line + TWI_DELAY(); + + // Pull SCL high and wait a moment for SDA line to settle + // Make sure slave is not stretching the clock + transfer_succeeded &= twi_master_wait_while_scl_low(); + + // Read ACK/NACK. NACK == 1, ACK == 0 + transfer_succeeded &= !(TWI_SDA_READ()); + + // Finish ACK/NACK bit clock cycle and give slave a moment to release control + // of the SDA line + TWI_SCL_LOW(); + TWI_DELAY(); + + // Configure TWI_SDA pin as output as other module functions expect that + TWI_SDA_OUTPUT(); + + return transfer_succeeded; +} + + +/** + * @brief Function for clocking one data byte in and sends ACK/NACK bit. + * + * Can handle clock stretching. + * SCL is expected to be output and low when entering this function. + * After calling this function, SCL is high and SDA low/high depending if ACK/NACK was sent. + * + * @param databyte Data byte to clock out. + * @param ack If true, send ACK. Otherwise send NACK. + * @return + * @retval true Byte read succesfully + * @retval false Timeout detected + */ +static bool twi_master_clock_byte_in(uint8_t *databyte, bool ack) +{ + uint_fast8_t byte_read = 0; + bool transfer_succeeded = true; + + /** @snippet [TWI SW master read] */ + // Make sure SDA is an input + TWI_SDA_INPUT(); + + // SCL state is guaranteed to be high here + + // MSB first + for (uint_fast8_t i = 0x80; i != 0; i >>= 1) + { + if (!twi_master_wait_while_scl_low()) + { + transfer_succeeded = false; + break; + } + + if (TWI_SDA_READ()) + { + byte_read |= i; + } + else + { + // No need to do anything + } + + TWI_SCL_LOW(); + TWI_DELAY(); + } + + // Make sure SDA is an output before we exit the function + TWI_SDA_OUTPUT(); + /** @snippet [TWI SW master read] */ + + *databyte = (uint8_t)byte_read; + + // Send ACK bit + + // SDA high == NACK, SDA low == ACK + if (ack) + { + TWI_SDA_LOW(); + } + else + { + TWI_SDA_HIGH(); + } + + // Let SDA line settle for a moment + TWI_DELAY(); + + // Drive SCL high to start ACK/NACK bit transfer + // Wait until SCL is high, or timeout occurs + if (!twi_master_wait_while_scl_low()) + { + transfer_succeeded = false; // Timeout + } + + // Finish ACK/NACK bit clock cycle and give slave a moment to react + TWI_SCL_LOW(); + TWI_DELAY(); + + return transfer_succeeded; +} + + +/** + * @brief Function for pulling SCL high and waits until it is high or timeout occurs. + * + * SCL is expected to be output before entering this function. + * @note If TWI_MASTER_TIMEOUT_COUNTER_LOAD_VALUE is set to zero, timeout functionality is not compiled in. + * @return + * @retval true SCL is now high. + * @retval false Timeout occurred and SCL is still low. + */ +static bool twi_master_wait_while_scl_low(void) +{ +#if TWI_MASTER_TIMEOUT_COUNTER_LOAD_VALUE != 0 + uint32_t volatile timeout_counter = TWI_MASTER_TIMEOUT_COUNTER_LOAD_VALUE; +#endif + + // Pull SCL high just in case if something left it low + TWI_SCL_HIGH(); + TWI_DELAY(); + + while (TWI_SCL_READ() == 0) + { + // If SCL is low, one of the slaves is busy and we must wait + +#if TWI_MASTER_TIMEOUT_COUNTER_LOAD_VALUE != 0 + if (timeout_counter-- == 0) + { + // If timeout_detected, return false + return false; + } +#endif + } + + return true; +} + +/*lint --flb "Leave library region" */ diff --git a/drivers_nrf/twi_master/nrf_drv_twi.c b/drivers_nrf/twi_master/nrf_drv_twi.c new file mode 100644 index 0000000..6cb2aac --- /dev/null +++ b/drivers_nrf/twi_master/nrf_drv_twi.c @@ -0,0 +1,1264 @@ +/** + * Copyright (c) 2015 - 2017, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "sdk_common.h" +#if NRF_MODULE_ENABLED(TWI) +#define ENABLED_TWI_COUNT (TWI0_ENABLED+TWI1_ENABLED) +#if ENABLED_TWI_COUNT +#include "nrf_drv_twi.h" +#include "nrf_drv_common.h" +#include "nrf_gpio.h" +#include "nrf_assert.h" +#include "app_util_platform.h" +#include "nrf_delay.h" + +#include <stdio.h> + +#define NRF_LOG_MODULE_NAME "TWI" + +#if TWI_CONFIG_LOG_ENABLED +#define NRF_LOG_LEVEL TWI_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR TWI_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR TWI_CONFIG_DEBUG_COLOR +#define EVT_TO_STR(event) (event == NRF_DRV_TWI_EVT_DONE ? "EVT_DONE" : \ + (event == NRF_DRV_TWI_EVT_ADDRESS_NACK ? "EVT_ADDRESS_NACK" : \ + (event == NRF_DRV_TWI_EVT_DATA_NACK ? "EVT_DATA_NACK" : "UNKNOWN ERROR"))) +#define EVT_TO_STR_TWI(event) (event == NRF_TWI_EVENT_STOPPED ? "NRF_TWI_EVENT_STOPPED" : \ + (event == NRF_TWI_EVENT_RXDREADY ? "NRF_TWI_EVENT_RXDREADY" : \ + (event == NRF_TWI_EVENT_TXDSENT ? "NRF_TWI_EVENT_TXDSENT" : \ + (event == NRF_TWI_EVENT_ERROR ? "NRF_TWI_EVENT_ERROR" : \ + (event == NRF_TWI_EVENT_BB ? "NRF_TWI_EVENT_BB" : \ + (event == NRF_TWI_EVENT_SUSPENDED ? "NRF_TWI_EVENT_SUSPENDED" : "UNKNOWN ERROR")))))) +#define EVT_TO_STR_TWIM(event) (event == NRF_TWIM_EVENT_STOPPED ? "NRF_TWIM_EVENT_STOPPED" : \ + (event == NRF_TWIM_EVENT_ERROR ? "NRF_TWIM_EVENT_ERROR" : \ + (event == NRF_TWIM_EVENT_SUSPENDED ? "NRF_TWIM_EVENT_SUSPENDED" : \ + (event == NRF_TWIM_EVENT_RXSTARTED ? "NRF_TWIM_EVENT_RXSTARTED" : \ + (event == NRF_TWIM_EVENT_TXSTARTED ? "NRF_TWIM_EVENT_TXSTARTED" : \ + (event == NRF_TWIM_EVENT_LASTRX ? "NRF_TWIM_EVENT_LASTRX" : \ + (event == NRF_TWIM_EVENT_LASTTX ? "NRF_TWIM_EVENT_LASTTX" : "UNKNOWN ERROR"))))))) +#define TRANSFER_TO_STR(type) (type == NRF_DRV_TWI_XFER_TX ? "XFER_TX" : \ + (type == NRF_DRV_TWI_XFER_RX ? "XFER_RX" : \ + (type == NRF_DRV_TWI_XFER_TXRX ? "XFER_TXRX" : \ + (type == NRF_DRV_TWI_XFER_TXTX ? "XFER_TXTX" : "UNKNOWN TRANSFER TYPE")))) +#else //TWI_CONFIG_LOG_ENABLED +#define EVT_TO_STR(event) "" +#define EVT_TO_STR_TWI(event) "" +#define EVT_TO_STR_TWIM(event) "" +#define TRANSFER_TO_STR(event) "" +#define NRF_LOG_LEVEL 0 +#endif //TWI_CONFIG_LOG_ENABLED +#include "nrf_log.h" +#include "nrf_log_ctrl.h" + + +#define TWI0_IRQ_HANDLER SPI0_TWI0_IRQHandler +#define TWI1_IRQ_HANDLER SPI1_TWI1_IRQHandler + +#if (defined(TWIM_IN_USE) && defined(TWI_IN_USE)) + // TWIM and TWI combined + #define CODE_FOR_TWIM(code) if (p_instance->use_easy_dma) { code } + #define CODE_FOR_TWI(code) else { code } +#elif (defined(TWIM_IN_USE) && !defined(TWI_IN_USE)) + // TWIM only + #define CODE_FOR_TWIM(code) { code } + #define CODE_FOR_TWI(code) +#elif (!defined(TWIM_IN_USE) && defined(TWI_IN_USE)) + // TWI only + #define CODE_FOR_TWIM(code) + #define CODE_FOR_TWI(code) { code } +#else + #error "Wrong configuration." +#endif + +// All interrupt flags +#define DISABLE_ALL_INT_SHORT 0xFFFFFFFF + +#define SCL_PIN_INIT_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 SDA_PIN_INIT_CONF SCL_PIN_INIT_CONF + +#define SDA_PIN_UNINIT_CONF ( (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) \ + | (GPIO_PIN_CNF_DRIVE_H0H1 << GPIO_PIN_CNF_DRIVE_Pos) \ + | (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos) \ + | (GPIO_PIN_CNF_INPUT_Disconnect << GPIO_PIN_CNF_INPUT_Pos) \ + | (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos)) +#define SCL_PIN_UNINIT_CONF SDA_PIN_UNINIT_CONF + +#define SCL_PIN_INIT_CONF_CLR ( (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_Output << GPIO_PIN_CNF_DIR_Pos)) +#define SDA_PIN_INIT_CONF_CLR SCL_PIN_INIT_CONF_CLR + +#define HW_TIMEOUT 10000 + +// Control block - driver instance local data. +typedef struct +{ + nrf_drv_twi_evt_handler_t handler; + void * p_context; + volatile uint32_t int_mask; + nrf_drv_twi_xfer_desc_t xfer_desc; + uint32_t flags; + uint8_t * p_curr_buf; + uint8_t curr_length; + bool curr_no_stop; + nrf_drv_state_t state; + bool error; + volatile bool busy; + bool repeated; + uint8_t bytes_transferred; + bool hold_bus_uninit; +#if NRF_MODULE_ENABLED(TWIM_NRF52_ANOMALY_109_WORKAROUND) + nrf_twim_frequency_t bus_frequency; +#endif +} twi_control_block_t; + +static twi_control_block_t m_cb[ENABLED_TWI_COUNT]; + +#if NRF_MODULE_ENABLED(PERIPHERAL_RESOURCE_SHARING) + #define IRQ_HANDLER_NAME(n) irq_handler_for_instance_##n + #define IRQ_HANDLER(n) static void IRQ_HANDLER_NAME(n)(void) + + #if NRF_MODULE_ENABLED(TWI0) + IRQ_HANDLER(0); + #endif + #if NRF_MODULE_ENABLED(TWI1) + IRQ_HANDLER(1); + #endif + static nrf_drv_irq_handler_t const m_irq_handlers[ENABLED_TWI_COUNT] = { + #if NRF_MODULE_ENABLED(TWI0) + IRQ_HANDLER_NAME(0), + #endif + #if NRF_MODULE_ENABLED(TWI1) + IRQ_HANDLER_NAME(1), + #endif + }; +#else + #define IRQ_HANDLER(n) void SPI##n##_TWI##n##_IRQHandler(void) +#endif // NRF_MODULE_ENABLED(PERIPHERAL_RESOURCE_SHARING) + +static ret_code_t twi_process_error(uint32_t errorsrc) +{ + ret_code_t ret = NRF_ERROR_INTERNAL; + + if (errorsrc & NRF_TWI_ERROR_OVERRUN) + { + ret = NRF_ERROR_DRV_TWI_ERR_OVERRUN; + } + + if (errorsrc & NRF_TWI_ERROR_ADDRESS_NACK) + { + ret = NRF_ERROR_DRV_TWI_ERR_ANACK; + } + + if (errorsrc & NRF_TWI_ERROR_DATA_NACK) + { + ret = NRF_ERROR_DRV_TWI_ERR_DNACK; + } + + return ret; +} + +static void twi_clear_bus(nrf_drv_twi_config_t const * p_config) +{ + NRF_GPIO->PIN_CNF[p_config->scl] = SCL_PIN_INIT_CONF; + NRF_GPIO->PIN_CNF[p_config->sda] = SDA_PIN_INIT_CONF; + + nrf_gpio_pin_set(p_config->scl); + nrf_gpio_pin_set(p_config->sda); + + NRF_GPIO->PIN_CNF[p_config->scl] = SCL_PIN_INIT_CONF_CLR; + NRF_GPIO->PIN_CNF[p_config->sda] = SDA_PIN_INIT_CONF_CLR; + + nrf_delay_us(4); + + for (int i = 0; i < 9; i++) + { + if (nrf_gpio_pin_read(p_config->sda)) + { + if (i == 0) + { + return; + } + else + { + break; + } + } + nrf_gpio_pin_clear(p_config->scl); + nrf_delay_us(4); + nrf_gpio_pin_set(p_config->scl); + nrf_delay_us(4); + } + nrf_gpio_pin_clear(p_config->sda); + nrf_delay_us(4); + nrf_gpio_pin_set(p_config->sda); +} + +ret_code_t nrf_drv_twi_init(nrf_drv_twi_t const * p_instance, + nrf_drv_twi_config_t const * p_config, + nrf_drv_twi_evt_handler_t event_handler, + void * p_context) +{ + ASSERT(p_config); + ASSERT(p_config->scl != p_config->sda); + twi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx]; + ret_code_t err_code; + + if (p_cb->state != NRF_DRV_STATE_UNINITIALIZED) + { + err_code = NRF_ERROR_INVALID_STATE; + NRF_LOG_WARNING("Function: %s, error code: %s.\r\n", + (uint32_t)__func__, + (uint32_t)NRF_LOG_ERROR_STRING_GET(err_code)); + return err_code; + } + +#if NRF_MODULE_ENABLED(PERIPHERAL_RESOURCE_SHARING) + if (nrf_drv_common_per_res_acquire(p_instance->reg.p_twi, + m_irq_handlers[p_instance->drv_inst_idx]) != NRF_SUCCESS) + { + err_code = NRF_ERROR_BUSY; + NRF_LOG_WARNING("Function: %s, error code: %s.\r\n", + (uint32_t)__func__, + (uint32_t)NRF_LOG_ERROR_STRING_GET(err_code)); + return err_code; + } +#endif // NRF_MODULE_ENABLED(PERIPHERAL_RESOURCE_SHARING) + + p_cb->handler = event_handler; + p_cb->p_context = p_context; + p_cb->int_mask = 0; + p_cb->repeated = false; + p_cb->busy = false; + p_cb->hold_bus_uninit = p_config->hold_bus_uninit; +#if NRF_MODULE_ENABLED(TWIM_NRF52_ANOMALY_109_WORKAROUND) + p_cb->bus_frequency = (nrf_twim_frequency_t)p_config->frequency; +#endif + + if(p_config->clear_bus_init) + { + /* Send clocks (max 9) until slave device back from stuck mode */ + twi_clear_bus(p_config); + } + + /* To secure correct signal levels on the pins used by the TWI + master when the system is in OFF mode, and when the TWI master is + disabled, these pins must be configured in the GPIO peripheral. + */ + NRF_GPIO->PIN_CNF[p_config->scl] = SCL_PIN_INIT_CONF; + NRF_GPIO->PIN_CNF[p_config->sda] = SDA_PIN_INIT_CONF; + + CODE_FOR_TWIM + ( + NRF_TWIM_Type * p_twim = p_instance->reg.p_twim; + nrf_twim_pins_set(p_twim, p_config->scl, p_config->sda); + nrf_twim_frequency_set(p_twim, + (nrf_twim_frequency_t)p_config->frequency); + ) + CODE_FOR_TWI + ( + NRF_TWI_Type * p_twi = p_instance->reg.p_twi; + nrf_twi_pins_set(p_twi, p_config->scl, p_config->sda); + nrf_twi_frequency_set(p_twi, + (nrf_twi_frequency_t)p_config->frequency); + ) + + if (p_cb->handler) + { + CODE_FOR_TWIM + ( + nrf_drv_common_irq_enable(nrf_drv_get_IRQn((void *)p_instance->reg.p_twim), + p_config->interrupt_priority); + ) + CODE_FOR_TWI + ( + nrf_drv_common_irq_enable(nrf_drv_get_IRQn((void *)p_instance->reg.p_twi), + p_config->interrupt_priority); + ) + } + + p_cb->state = NRF_DRV_STATE_INITIALIZED; + + err_code = NRF_SUCCESS; + NRF_LOG_INFO("Function: %s, error code: %s.\r\n", + (uint32_t)__func__, + (uint32_t)NRF_LOG_ERROR_STRING_GET(err_code)); + return err_code; +} + +void nrf_drv_twi_uninit(nrf_drv_twi_t const * p_instance) +{ + twi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx]; + ASSERT(p_cb->state != NRF_DRV_STATE_UNINITIALIZED); + + if (p_cb->handler) + { + CODE_FOR_TWIM + ( + nrf_drv_common_irq_disable(nrf_drv_get_IRQn((void *)p_instance->reg.p_twim)); + ) + CODE_FOR_TWI + ( + nrf_drv_common_irq_disable(nrf_drv_get_IRQn((void *)p_instance->reg.p_twi)); + ) + } + nrf_drv_twi_disable(p_instance); + +#if NRF_MODULE_ENABLED(PERIPHERAL_RESOURCE_SHARING) + nrf_drv_common_per_res_release(p_instance->reg.p_twi); +#endif + + if (!p_cb->hold_bus_uninit) + { + CODE_FOR_TWIM + ( + NRF_GPIO->PIN_CNF[p_instance->reg.p_twim->PSEL.SCL] = SCL_PIN_UNINIT_CONF; + NRF_GPIO->PIN_CNF[p_instance->reg.p_twim->PSEL.SDA] = SDA_PIN_UNINIT_CONF; + ) + CODE_FOR_TWI + ( + NRF_GPIO->PIN_CNF[p_instance->reg.p_twi->PSELSCL] = SCL_PIN_UNINIT_CONF; + NRF_GPIO->PIN_CNF[p_instance->reg.p_twi->PSELSDA] = SDA_PIN_UNINIT_CONF; + ) + } + + p_cb->state = NRF_DRV_STATE_UNINITIALIZED; + NRF_LOG_INFO("Instance uninitialized: %d.\r\n", p_instance->drv_inst_idx); +} + +void nrf_drv_twi_enable(nrf_drv_twi_t const * p_instance) +{ + twi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx]; + ASSERT(p_cb->state == NRF_DRV_STATE_INITIALIZED); + + CODE_FOR_TWIM + ( + NRF_TWIM_Type * p_twim = p_instance->reg.p_twim; + + nrf_twim_enable(p_twim); + ) + CODE_FOR_TWI + ( + NRF_TWI_Type * p_twi = p_instance->reg.p_twi; + + nrf_twi_enable(p_twi); + ) + + p_cb->state = NRF_DRV_STATE_POWERED_ON; + NRF_LOG_INFO("Instance enabled: %d.\r\n", p_instance->drv_inst_idx); +} + +void nrf_drv_twi_disable(nrf_drv_twi_t const * p_instance) +{ + twi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx]; + ASSERT(p_cb->state != NRF_DRV_STATE_UNINITIALIZED); + + CODE_FOR_TWIM + ( + NRF_TWIM_Type * p_twim = p_instance->reg.p_twim; + p_cb->int_mask = 0; + nrf_twim_int_disable(p_twim, DISABLE_ALL_INT_SHORT); + nrf_twim_shorts_disable(p_twim, DISABLE_ALL_INT_SHORT); + nrf_twim_disable(p_twim); + ) + CODE_FOR_TWI + ( + NRF_TWI_Type * p_twi = p_instance->reg.p_twi; + nrf_twi_int_disable(p_twi, DISABLE_ALL_INT_SHORT); + nrf_twi_shorts_disable(p_twi, DISABLE_ALL_INT_SHORT); + nrf_twi_disable(p_twi); + ) + + p_cb->state = NRF_DRV_STATE_INITIALIZED; + NRF_LOG_INFO("Instance disabled: %d.\r\n", p_instance->drv_inst_idx); +} + +#ifdef TWI_IN_USE +static bool twi_send_byte(NRF_TWI_Type * p_twi, + uint8_t const * p_data, + uint8_t length, + uint8_t * p_bytes_transferred, + bool no_stop) +{ + if (*p_bytes_transferred < length) + { + nrf_twi_txd_set(p_twi, p_data[*p_bytes_transferred]); + ++(*p_bytes_transferred); + } + else + { + if (no_stop) + { + nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_SUSPEND); + return false; + } + else + { + nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_STOP); + } + } + return true; +} + +static void twi_receive_byte(NRF_TWI_Type * p_twi, + uint8_t * p_data, + uint8_t length, + uint8_t * p_bytes_transferred) +{ + if (*p_bytes_transferred < length) + { + p_data[*p_bytes_transferred] = nrf_twi_rxd_get(p_twi); + + ++(*p_bytes_transferred); + + if (*p_bytes_transferred == length - 1) + { + nrf_twi_shorts_set(p_twi, NRF_TWI_SHORT_BB_STOP_MASK); + } + else if (*p_bytes_transferred == length) + { + return; + } + + nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_RESUME); + } +} + +static bool twi_transfer(NRF_TWI_Type * p_twi, + bool * p_error, + uint8_t * p_bytes_transferred, + uint8_t * p_data, + uint8_t length, + bool no_stop) +{ + bool do_stop_check = ((*p_error) || ((*p_bytes_transferred) == length)); + + if (*p_error) + { + nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_ERROR); + nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_TXDSENT); + nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_RXDREADY); + } + else if (nrf_twi_event_check(p_twi, NRF_TWI_EVENT_ERROR)) + { + nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_ERROR); + NRF_LOG_DEBUG("TWI: Event: %s.\r\n", (uint32_t)EVT_TO_STR_TWI(NRF_TWI_EVENT_ERROR)); + nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_STOP); + *p_error = true; + } + else + { + if (nrf_twi_event_check(p_twi, NRF_TWI_EVENT_TXDSENT)) + { + nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_TXDSENT); + NRF_LOG_DEBUG("TWI: Event: %s.\r\n", (uint32_t)EVT_TO_STR_TWI(NRF_TWI_EVENT_TXDSENT)); + if (nrf_twi_event_check(p_twi, NRF_TWI_EVENT_ERROR)) + { + nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_ERROR); + NRF_LOG_DEBUG("TWI: Event: %s.\r\n", (uint32_t)EVT_TO_STR_TWI(NRF_TWI_EVENT_ERROR)); + nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_STOP); + *p_error = true; + } + else + { + if (!twi_send_byte(p_twi, p_data, length, p_bytes_transferred, no_stop)) + { + return false; + } + } + } + else if (nrf_twi_event_check(p_twi, NRF_TWI_EVENT_RXDREADY)) + { + nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_RXDREADY); + NRF_LOG_DEBUG("TWI: Event: %s.\r\n", (uint32_t)EVT_TO_STR_TWI(NRF_TWI_EVENT_RXDREADY)); + if (nrf_twi_event_check(p_twi, NRF_TWI_EVENT_ERROR)) + { + NRF_LOG_DEBUG("TWI: Event: %s.\r\n", (uint32_t)EVT_TO_STR_TWI(NRF_TWI_EVENT_ERROR)); + nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_ERROR); + nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_STOP); + *p_error = true; + } + else + { + twi_receive_byte(p_twi, p_data, length, p_bytes_transferred); + } + } + } + + if (do_stop_check && nrf_twi_event_check(p_twi, NRF_TWI_EVENT_STOPPED)) + { + nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_STOPPED); + NRF_LOG_DEBUG("TWI: Event: %s.\r\n", (uint32_t)EVT_TO_STR_TWI(NRF_TWI_EVENT_STOPPED)); + return false; + } + + return true; +} + +static ret_code_t twi_tx_start_transfer(twi_control_block_t * p_cb, + NRF_TWI_Type * p_twi, + uint8_t const * p_data, + uint8_t length, + bool no_stop) +{ + ret_code_t ret_code = NRF_SUCCESS; + volatile int32_t hw_timeout; + + hw_timeout = HW_TIMEOUT; + + nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_STOPPED); + nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_ERROR); + nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_TXDSENT); + nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_RXDREADY); + nrf_twi_shorts_set(p_twi, 0); + + p_cb->bytes_transferred = 0; + p_cb->error = false; + + // In case TWI is suspended resume its operation. + nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_RESUME); + nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_STARTTX); + + (void)twi_send_byte(p_twi, p_data, length, &p_cb->bytes_transferred, no_stop); + + if (p_cb->handler) + { + p_cb->int_mask = NRF_TWI_INT_STOPPED_MASK | + NRF_TWI_INT_ERROR_MASK | + NRF_TWI_INT_TXDSENT_MASK | + NRF_TWI_INT_RXDREADY_MASK; + nrf_twi_int_enable(p_twi, p_cb->int_mask); + } + else + { + while ((hw_timeout > 0) && + twi_transfer(p_twi, + &p_cb->error, + &p_cb->bytes_transferred, + (uint8_t *)p_data, + length, + no_stop)) + { + hw_timeout--; + } + + if (p_cb->error) + { + uint32_t errorsrc = nrf_twi_errorsrc_get_and_clear(p_twi); + + if (errorsrc) + { + ret_code = twi_process_error(errorsrc); + } + } + + if (hw_timeout <= 0) + { + nrf_twi_disable(p_twi); + nrf_twi_enable(p_twi); + ret_code = NRF_ERROR_INTERNAL; + } + + } + return ret_code; +} + +static ret_code_t twi_rx_start_transfer(twi_control_block_t * p_cb, + NRF_TWI_Type * p_twi, + uint8_t const * p_data, + uint8_t length) +{ + ret_code_t ret_code = NRF_SUCCESS; + volatile int32_t hw_timeout; + + hw_timeout = HW_TIMEOUT; + + nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_STOPPED); + nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_ERROR); + nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_TXDSENT); + nrf_twi_event_clear(p_twi, NRF_TWI_EVENT_RXDREADY); + + p_cb->bytes_transferred = 0; + p_cb->error = false; + + if (length == 1) + { + nrf_twi_shorts_set(p_twi, NRF_TWI_SHORT_BB_STOP_MASK); + } + else + { + nrf_twi_shorts_set(p_twi, NRF_TWI_SHORT_BB_SUSPEND_MASK); + } + // In case TWI is suspended resume its operation. + nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_RESUME); + nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_STARTRX); + + if (p_cb->handler) + { + p_cb->int_mask = NRF_TWI_INT_STOPPED_MASK | + NRF_TWI_INT_ERROR_MASK | + NRF_TWI_INT_TXDSENT_MASK | + NRF_TWI_INT_RXDREADY_MASK; + nrf_twi_int_enable(p_twi, p_cb->int_mask); + } + else + { + while ((hw_timeout > 0) && + twi_transfer(p_twi, + &p_cb->error, + &p_cb->bytes_transferred, + (uint8_t*)p_data, + length, + false)) + { + hw_timeout--; + } + + if (p_cb->error) + { + uint32_t errorsrc = nrf_twi_errorsrc_get_and_clear(p_twi); + + if (errorsrc) + { + ret_code = twi_process_error(errorsrc); + } + } + if (hw_timeout <= 0) + { + nrf_twi_disable(p_twi); + nrf_twi_enable(p_twi); + ret_code = NRF_ERROR_INTERNAL; + } + } + return ret_code; +} + +__STATIC_INLINE ret_code_t twi_xfer(twi_control_block_t * p_cb, + NRF_TWI_Type * p_twi, + nrf_drv_twi_xfer_desc_t const * p_xfer_desc, + uint32_t flags) +{ + + ret_code_t err_code = NRF_SUCCESS; + + /* Block TWI interrupts to ensure that function is not interrupted by TWI interrupt. */ + nrf_twi_int_disable(p_twi, DISABLE_ALL_INT_SHORT); + + if (p_cb->busy) + { + nrf_twi_int_enable(p_twi, p_cb->int_mask); + err_code = NRF_ERROR_BUSY; + NRF_LOG_WARNING("Function: %s, error code: %s.\r\n", + (uint32_t)__func__, + (uint32_t)NRF_LOG_ERROR_STRING_GET(err_code)); + return err_code; + } + else + { + p_cb->busy = (NRF_DRV_TWI_FLAG_NO_XFER_EVT_HANDLER & flags) ? false : true; + } + + if (flags & NRF_DRV_TWI_FLAG_HOLD_XFER) + { + err_code = NRF_ERROR_NOT_SUPPORTED; + NRF_LOG_WARNING("Function: %s, error code: %s.\r\n", + (uint32_t)__func__, + (uint32_t)NRF_LOG_ERROR_STRING_GET(err_code)); + return err_code; + } + + p_cb->flags = flags; + p_cb->xfer_desc = *p_xfer_desc; + p_cb->curr_length = p_xfer_desc->primary_length; + p_cb->p_curr_buf = p_xfer_desc->p_primary_buf; + nrf_twi_address_set(p_twi, p_xfer_desc->address); + + if (p_xfer_desc->type != NRF_DRV_TWI_XFER_RX) + { + p_cb->curr_no_stop = ((p_xfer_desc->type == NRF_DRV_TWI_XFER_TX) && + !(flags & NRF_DRV_TWI_FLAG_TX_NO_STOP)) ? false : true; + + err_code = twi_tx_start_transfer(p_cb, + p_twi, + p_xfer_desc->p_primary_buf, + p_xfer_desc->primary_length, + p_cb->curr_no_stop); + } + else + { + p_cb->curr_no_stop = false; + + err_code = twi_rx_start_transfer(p_cb, + p_twi, + p_xfer_desc->p_primary_buf, + p_xfer_desc->primary_length); + } + if (p_cb->handler == NULL) + { + p_cb->busy = false; + } + return err_code; +} +#endif + + +bool nrf_drv_twi_is_busy(nrf_drv_twi_t const * p_instance) +{ + twi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx]; + return p_cb->busy; +} + + +#ifdef TWIM_IN_USE +__STATIC_INLINE void twim_list_enable_handle(NRF_TWIM_Type * p_twim, uint32_t flags) +{ + if (NRF_DRV_TWI_FLAG_TX_POSTINC & flags) + { + nrf_twim_tx_list_enable(p_twim); + } + else + { + nrf_twim_tx_list_disable(p_twim); + } + + if (NRF_DRV_TWI_FLAG_RX_POSTINC & flags) + { + nrf_twim_rx_list_enable(p_twim); + } + else + { + nrf_twim_rx_list_disable(p_twim); + } +} +__STATIC_INLINE ret_code_t twim_xfer(twi_control_block_t * p_cb, + NRF_TWIM_Type * p_twim, + nrf_drv_twi_xfer_desc_t const * p_xfer_desc, + uint32_t flags) +{ + ret_code_t err_code = NRF_SUCCESS; + nrf_twim_task_t start_task = NRF_TWIM_TASK_STARTTX; + nrf_twim_event_t evt_to_wait = NRF_TWIM_EVENT_STOPPED; + + if (!nrf_drv_is_in_RAM(p_xfer_desc->p_primary_buf)) + { + err_code = NRF_ERROR_INVALID_ADDR; + NRF_LOG_WARNING("Function: %s, error code: %s.\r\n", + (uint32_t)__func__, + (uint32_t)NRF_LOG_ERROR_STRING_GET(err_code)); + return err_code; + } + /* Block TWI interrupts to ensure that function is not interrupted by TWI interrupt. */ + nrf_twim_int_disable(p_twim, DISABLE_ALL_INT_SHORT); + if (p_cb->busy) + { + nrf_twim_int_enable(p_twim, p_cb->int_mask); + err_code = NRF_ERROR_BUSY; + NRF_LOG_WARNING("Function: %s, error code: %s.\r\n", + (uint32_t)__func__, + (uint32_t)NRF_LOG_ERROR_STRING_GET(err_code)); + return err_code; + } + else + { + p_cb->busy = ((NRF_DRV_TWI_FLAG_NO_XFER_EVT_HANDLER & flags) || + (NRF_DRV_TWI_FLAG_REPEATED_XFER & flags)) ? false: true; + } + + p_cb->xfer_desc = *p_xfer_desc; + p_cb->repeated = (flags & NRF_DRV_TWI_FLAG_REPEATED_XFER) ? true : false; + nrf_twim_address_set(p_twim, p_xfer_desc->address); + + nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_STOPPED); + nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_ERROR); + + twim_list_enable_handle(p_twim, flags); + switch (p_xfer_desc->type) + { + case NRF_DRV_TWI_XFER_TXTX: + ASSERT(!(flags & NRF_DRV_TWI_FLAG_REPEATED_XFER)); + ASSERT(!(flags & NRF_DRV_TWI_FLAG_HOLD_XFER)); + ASSERT(!(flags & NRF_DRV_TWI_FLAG_NO_XFER_EVT_HANDLER)); + if (!nrf_drv_is_in_RAM(p_xfer_desc->p_secondary_buf)) + { + err_code = NRF_ERROR_INVALID_ADDR; + NRF_LOG_WARNING("Function: %s, error code: %s.\r\n", + (uint32_t)__func__, + (uint32_t)NRF_LOG_ERROR_STRING_GET(err_code)); + return err_code; + } + nrf_twim_shorts_set(p_twim, NRF_TWIM_SHORT_LASTTX_SUSPEND_MASK); + nrf_twim_tx_buffer_set(p_twim, p_xfer_desc->p_primary_buf, p_xfer_desc->primary_length); + nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_TXSTARTED); + nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_LASTTX); + nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_SUSPENDED); + nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_RESUME); + nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_STARTTX); + while (!nrf_twim_event_check(p_twim, NRF_TWIM_EVENT_TXSTARTED)) + {} + NRF_LOG_DEBUG("TWIM: Event: %s.\r\n", (uint32_t)EVT_TO_STR_TWIM(NRF_TWIM_EVENT_TXSTARTED)); + nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_TXSTARTED); + nrf_twim_tx_buffer_set(p_twim, p_xfer_desc->p_secondary_buf, p_xfer_desc->secondary_length); + p_cb->int_mask = NRF_TWIM_INT_SUSPENDED_MASK | NRF_TWIM_INT_ERROR_MASK; + break; + case NRF_DRV_TWI_XFER_TXRX: + nrf_twim_tx_buffer_set(p_twim, p_xfer_desc->p_primary_buf, p_xfer_desc->primary_length); + if (!nrf_drv_is_in_RAM(p_xfer_desc->p_secondary_buf)) + { + err_code = NRF_ERROR_INVALID_ADDR; + NRF_LOG_WARNING("Function: %s, error code: %s.\r\n", + (uint32_t)__func__, + (uint32_t)NRF_LOG_ERROR_STRING_GET(err_code)); + return err_code; + } + nrf_twim_rx_buffer_set(p_twim, p_xfer_desc->p_secondary_buf, p_xfer_desc->secondary_length); + nrf_twim_shorts_set(p_twim, NRF_TWIM_SHORT_LASTTX_STARTRX_MASK | + NRF_TWIM_SHORT_LASTRX_STOP_MASK); + p_cb->int_mask = NRF_TWIM_INT_STOPPED_MASK | NRF_TWIM_INT_ERROR_MASK; + break; + case NRF_DRV_TWI_XFER_TX: + nrf_twim_tx_buffer_set(p_twim, p_xfer_desc->p_primary_buf, p_xfer_desc->primary_length); + if (NRF_DRV_TWI_FLAG_TX_NO_STOP & flags) + { + nrf_twim_shorts_set(p_twim, NRF_TWIM_SHORT_LASTTX_SUSPEND_MASK); + p_cb->int_mask = NRF_TWIM_INT_SUSPENDED_MASK | NRF_TWIM_INT_ERROR_MASK; + nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_SUSPENDED); + evt_to_wait = NRF_TWIM_EVENT_SUSPENDED; + } + else + { + nrf_twim_shorts_set(p_twim, NRF_TWIM_SHORT_LASTTX_STOP_MASK); + p_cb->int_mask = NRF_TWIM_INT_STOPPED_MASK | NRF_TWIM_INT_ERROR_MASK; + } + nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_RESUME); + break; + case NRF_DRV_TWI_XFER_RX: + nrf_twim_rx_buffer_set(p_twim, p_xfer_desc->p_primary_buf, p_xfer_desc->primary_length); + nrf_twim_shorts_set(p_twim, NRF_TWIM_SHORT_LASTRX_STOP_MASK); + p_cb->int_mask = NRF_TWIM_INT_STOPPED_MASK | NRF_TWIM_INT_ERROR_MASK; + start_task = NRF_TWIM_TASK_STARTRX; + nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_RESUME); + break; + default: + err_code = NRF_ERROR_INVALID_PARAM; + break; + } + + if (!(flags & NRF_DRV_TWI_FLAG_HOLD_XFER) && (p_xfer_desc->type != NRF_DRV_TWI_XFER_TXTX)) + { + nrf_twim_task_trigger(p_twim, start_task); + } + + if (p_cb->handler) + { + if (flags & NRF_DRV_TWI_FLAG_NO_XFER_EVT_HANDLER) + { + p_cb->int_mask = NRF_TWIM_INT_ERROR_MASK; + } + nrf_twim_int_enable(p_twim, p_cb->int_mask); + +#if NRF_MODULE_ENABLED(TWIM_NRF52_ANOMALY_109_WORKAROUND) + if ((flags & NRF_DRV_TWI_FLAG_HOLD_XFER) && ((p_xfer_desc->type == NRF_DRV_TWI_XFER_TX) || + (p_xfer_desc->type == NRF_DRV_TWI_XFER_TXRX))) + { + p_cb->flags = flags; + twim_list_enable_handle(p_twim, 0); + p_twim->FREQUENCY = 0; + nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_TXSTARTED); + nrf_twim_int_enable(p_twim, NRF_TWIM_INT_TXSTARTED_MASK); + } +#endif + } + else + { + while (!nrf_twim_event_check(p_twim, evt_to_wait)) + { + if (nrf_twim_event_check(p_twim, NRF_TWIM_EVENT_ERROR)) + { + NRF_LOG_DEBUG("TWIM: Event: %s.\r\n", + (uint32_t)EVT_TO_STR_TWIM(NRF_TWIM_EVENT_ERROR)); + nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_ERROR); + nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_RESUME); + nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_STOP); + evt_to_wait = NRF_TWIM_EVENT_STOPPED; + } + } + + uint32_t errorsrc = nrf_twim_errorsrc_get_and_clear(p_twim); + + p_cb->busy = false; + + if (errorsrc) + { + err_code = twi_process_error(errorsrc); + } + } + return err_code; +} +#endif + +ret_code_t nrf_drv_twi_xfer(nrf_drv_twi_t const * p_instance, + nrf_drv_twi_xfer_desc_t const * p_xfer_desc, + uint32_t flags) +{ + + ret_code_t err_code = NRF_SUCCESS; + twi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx]; + + // TXRX and TXTX transfers are support only in non-blocking mode. + ASSERT( !((p_cb->handler == NULL) && (p_xfer_desc->type == NRF_DRV_TWI_XFER_TXRX))); + ASSERT( !((p_cb->handler == NULL) && (p_xfer_desc->type == NRF_DRV_TWI_XFER_TXTX))); + + NRF_LOG_INFO("Transfer type: %s.\r\n", (uint32_t)TRANSFER_TO_STR(p_xfer_desc->type)); + NRF_LOG_INFO("Transfer buffers length: primary: %d, secondary: %d.\r\n", + p_xfer_desc->primary_length, p_xfer_desc->secondary_length); + NRF_LOG_DEBUG("Primary buffer data:\r\n"); + NRF_LOG_HEXDUMP_DEBUG((uint8_t *)p_xfer_desc->p_primary_buf, + p_xfer_desc->primary_length * sizeof(p_xfer_desc->p_primary_buf)); + NRF_LOG_DEBUG("Secondary buffer data:\r\n"); + NRF_LOG_HEXDUMP_DEBUG((uint8_t *)p_xfer_desc->p_secondary_buf, + p_xfer_desc->secondary_length * sizeof(p_xfer_desc->p_secondary_buf)); + + CODE_FOR_TWIM + ( + + err_code = twim_xfer(p_cb, (NRF_TWIM_Type *)p_instance->reg.p_twim, p_xfer_desc, flags); + ) + CODE_FOR_TWI + ( + if ( (NRF_DRV_TWI_FLAG_TX_POSTINC | NRF_DRV_TWI_FLAG_RX_POSTINC) & flags) + { + err_code = NRF_ERROR_NOT_SUPPORTED; + NRF_LOG_WARNING("Function: %s, error code: %s.\r\n", + (uint32_t)__func__, + (uint32_t)NRF_LOG_ERROR_STRING_GET(err_code)); + return err_code; + } + + err_code = twi_xfer(p_cb, (NRF_TWI_Type *)p_instance->reg.p_twi, p_xfer_desc, flags); + ) + NRF_LOG_WARNING("Function: %s, error code: %s.\r\n", + (uint32_t)__func__, (uint32_t)NRF_LOG_ERROR_STRING_GET(err_code)); + return err_code; +} + +ret_code_t nrf_drv_twi_tx(nrf_drv_twi_t const * p_instance, + uint8_t address, + uint8_t const * p_data, + uint8_t length, + bool no_stop) +{ + nrf_drv_twi_xfer_desc_t xfer = NRF_DRV_TWI_XFER_DESC_TX(address, (uint8_t*)p_data, length); + + return nrf_drv_twi_xfer(p_instance, &xfer, no_stop ? NRF_DRV_TWI_FLAG_TX_NO_STOP : 0); +} + +ret_code_t nrf_drv_twi_rx(nrf_drv_twi_t const * p_instance, + uint8_t address, + uint8_t * p_data, + uint8_t length) +{ + nrf_drv_twi_xfer_desc_t xfer = NRF_DRV_TWI_XFER_DESC_RX(address, p_data, length); + return nrf_drv_twi_xfer(p_instance, &xfer, 0); +} + +uint32_t nrf_drv_twi_data_count_get(nrf_drv_twi_t const * const p_instance) +{ + CODE_FOR_TWIM + ( + ASSERT(false); + return 0; + ) + CODE_FOR_TWI + ( + return m_cb[p_instance->drv_inst_idx].bytes_transferred; + ) +} + +uint32_t nrf_drv_twi_start_task_get(nrf_drv_twi_t const * p_instance, + nrf_drv_twi_xfer_type_t xfer_type) +{ + CODE_FOR_TWIM + ( + return (uint32_t)nrf_twim_task_address_get(p_instance->reg.p_twim, + (xfer_type != NRF_DRV_TWI_XFER_RX) ? NRF_TWIM_TASK_STARTTX : NRF_TWIM_TASK_STARTRX); + ) + CODE_FOR_TWI + ( + return (uint32_t)nrf_twi_task_address_get(p_instance->reg.p_twi, + (xfer_type != NRF_DRV_TWI_XFER_RX) ? NRF_TWI_TASK_STARTTX : NRF_TWI_TASK_STARTRX); + ) +} + +uint32_t nrf_drv_twi_stopped_event_get(nrf_drv_twi_t const * p_instance) +{ + CODE_FOR_TWIM + ( + return (uint32_t)nrf_twim_event_address_get(p_instance->reg.p_twim, NRF_TWIM_EVENT_STOPPED); + ) + CODE_FOR_TWI + ( + return (uint32_t)nrf_twi_event_address_get(p_instance->reg.p_twi, NRF_TWI_EVENT_STOPPED); + ) +} + +#ifdef TWIM_IN_USE +static void irq_handler_twim(NRF_TWIM_Type * p_twim, twi_control_block_t * p_cb) +{ + +#if NRF_MODULE_ENABLED(TWIM_NRF52_ANOMALY_109_WORKAROUND) + /* Handle only workaround case. Can be used without TWIM handler in IRQs. */ + if (nrf_twim_event_check(p_twim, NRF_TWIM_EVENT_TXSTARTED)) + { + nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_TXSTARTED); + nrf_twim_int_disable(p_twim, NRF_TWIM_INT_TXSTARTED_MASK); + if (p_twim->FREQUENCY == 0) + { + // Set enable to zero to reset TWIM internal state. + nrf_twim_disable(p_twim); + nrf_twim_enable(p_twim); + + // Set proper frequency. + nrf_twim_frequency_set(p_twim, p_cb->bus_frequency); + twim_list_enable_handle(p_twim, p_cb->flags); + + // Start proper transmission. + nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_STARTTX); + return; + } + } +#endif + + ASSERT(p_cb->handler); + + if (nrf_twim_event_check(p_twim, NRF_TWIM_EVENT_ERROR)) + { + nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_ERROR); + NRF_LOG_DEBUG("TWIM: Event: %s.\r\n", (uint32_t)EVT_TO_STR_TWIM(NRF_TWIM_EVENT_ERROR)); + if (!nrf_twim_event_check(p_twim, NRF_TWIM_EVENT_STOPPED)) + { + nrf_twim_int_disable(p_twim, p_cb->int_mask); + p_cb->int_mask = NRF_TWIM_INT_STOPPED_MASK; + nrf_twim_int_enable(p_twim, p_cb->int_mask); + + nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_RESUME); + nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_STOP); + return; + } + } + + nrf_drv_twi_evt_t event; + + if (nrf_twim_event_check(p_twim, NRF_TWIM_EVENT_STOPPED)) + { + NRF_LOG_DEBUG("TWIM: Event: %s.\r\n", (uint32_t)EVT_TO_STR_TWIM(NRF_TWIM_EVENT_STOPPED)); + nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_STOPPED); + event.xfer_desc = p_cb->xfer_desc; + if (p_cb->error) + { + + event.xfer_desc.primary_length = (p_cb->xfer_desc.type == NRF_DRV_TWI_XFER_RX) ? + (uint8_t)nrf_twim_rxd_amount_get(p_twim) : (uint8_t)nrf_twim_txd_amount_get(p_twim); + event.xfer_desc.secondary_length = (p_cb->xfer_desc.type == NRF_DRV_TWI_XFER_TXRX) ? + (uint8_t)nrf_twim_rxd_amount_get(p_twim) : (uint8_t)nrf_twim_txd_amount_get(p_twim); + + } + nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_LASTTX); + nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_LASTRX); + if (!p_cb->repeated || p_cb->error) + { + nrf_twim_shorts_set(p_twim, 0); + p_cb->int_mask = 0; + nrf_twim_int_disable(p_twim, DISABLE_ALL_INT_SHORT); + } + } + else + { + nrf_twim_event_clear(p_twim, NRF_TWIM_EVENT_SUSPENDED); + NRF_LOG_DEBUG("TWIM: Event: %s.\r\n", (uint32_t)EVT_TO_STR_TWIM(NRF_TWIM_EVENT_SUSPENDED)); + if (p_cb->xfer_desc.type == NRF_DRV_TWI_XFER_TX) + { + event.xfer_desc = p_cb->xfer_desc; + if (!p_cb->repeated) + { + nrf_twim_shorts_set(p_twim, 0); + p_cb->int_mask = 0; + nrf_twim_int_disable(p_twim, DISABLE_ALL_INT_SHORT); + } + } + else + { + nrf_twim_shorts_set(p_twim, NRF_TWIM_SHORT_LASTTX_STOP_MASK); + p_cb->int_mask = NRF_TWIM_INT_STOPPED_MASK | NRF_TWIM_INT_ERROR_MASK; + nrf_twim_int_disable(p_twim, DISABLE_ALL_INT_SHORT); + nrf_twim_int_enable(p_twim, p_cb->int_mask); + nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_STARTTX); + nrf_twim_task_trigger(p_twim, NRF_TWIM_TASK_RESUME); + return; + } + } + + uint32_t errorsrc = nrf_twim_errorsrc_get_and_clear(p_twim); + if (errorsrc & NRF_TWIM_ERROR_ADDRESS_NACK) + { + event.type = NRF_DRV_TWI_EVT_ADDRESS_NACK; + NRF_LOG_DEBUG("Event: %s.\r\n", (uint32_t)EVT_TO_STR(NRF_DRV_TWI_EVT_ADDRESS_NACK)); + } + else if (errorsrc & NRF_TWIM_ERROR_DATA_NACK) + { + event.type = NRF_DRV_TWI_EVT_DATA_NACK; + NRF_LOG_DEBUG("Event: %s.\r\n", (uint32_t)EVT_TO_STR(NRF_DRV_TWI_EVT_DATA_NACK)); + } + else + { + event.type = NRF_DRV_TWI_EVT_DONE; + NRF_LOG_DEBUG("Event: %s.\r\n", (uint32_t)EVT_TO_STR(NRF_DRV_TWI_EVT_DONE)); + } + + if (!p_cb->repeated) + { + p_cb->busy = false; + } + p_cb->handler(&event, p_cb->p_context); +} +#endif // TWIM_IN_USE + +#ifdef TWI_IN_USE +static void irq_handler_twi(NRF_TWI_Type * p_twi, twi_control_block_t * p_cb) +{ + ASSERT(p_cb->handler); + + if (twi_transfer(p_twi, + &p_cb->error, + &p_cb->bytes_transferred, + p_cb->p_curr_buf, + p_cb->curr_length, + p_cb->curr_no_stop )) + { + return; + } + + if (!p_cb->error && + ((p_cb->xfer_desc.type == NRF_DRV_TWI_XFER_TXRX) || + (p_cb->xfer_desc.type == NRF_DRV_TWI_XFER_TXTX)) && + p_cb->p_curr_buf == p_cb->xfer_desc.p_primary_buf) + { + p_cb->p_curr_buf = p_cb->xfer_desc.p_secondary_buf; + p_cb->curr_length = p_cb->xfer_desc.secondary_length; + p_cb->curr_no_stop = (p_cb->flags & NRF_DRV_TWI_FLAG_TX_NO_STOP); + + if (p_cb->xfer_desc.type == NRF_DRV_TWI_XFER_TXTX) + { + (void)twi_tx_start_transfer(p_cb, + p_twi, + p_cb->p_curr_buf, + p_cb->curr_length, + p_cb->curr_no_stop); + } + else + { + (void)twi_rx_start_transfer(p_cb, p_twi, p_cb->p_curr_buf, p_cb->curr_length); + } + } + else + { + nrf_drv_twi_evt_t event; + event.xfer_desc = p_cb->xfer_desc; + + if (p_cb->error) + { + uint32_t errorsrc = nrf_twi_errorsrc_get_and_clear(p_twi); + if (errorsrc & NRF_TWI_ERROR_ADDRESS_NACK) + { + event.type = NRF_DRV_TWI_EVT_ADDRESS_NACK; + NRF_LOG_DEBUG("Event: %s.\r\n", (uint32_t)EVT_TO_STR(NRF_DRV_TWI_EVT_ADDRESS_NACK)); + } + else if (errorsrc & NRF_TWI_ERROR_DATA_NACK) + { + event.type = NRF_DRV_TWI_EVT_DATA_NACK; + NRF_LOG_DEBUG("Event: %s.\r\n", (uint32_t)EVT_TO_STR(NRF_DRV_TWI_EVT_DATA_NACK)); + } + } + else + { + event.type = NRF_DRV_TWI_EVT_DONE; + NRF_LOG_DEBUG("Event: %s.\r\n", (uint32_t)EVT_TO_STR(NRF_DRV_TWI_EVT_DONE)); + } + + p_cb->busy = false; + + if (!(NRF_DRV_TWI_FLAG_NO_XFER_EVT_HANDLER & p_cb->flags)) + { + p_cb->handler(&event, p_cb->p_context); + } + } + +} +#endif // TWI_IN_USE + +#if NRF_MODULE_ENABLED(TWI0) +IRQ_HANDLER(0) +{ + #if (TWI0_USE_EASY_DMA == 1) + irq_handler_twim(NRF_TWIM0, + #else + irq_handler_twi(NRF_TWI0, + #endif + &m_cb[TWI0_INSTANCE_INDEX]); +} +#endif // NRF_MODULE_ENABLED(TWI0) + +#if NRF_MODULE_ENABLED(TWI1) +IRQ_HANDLER(1) +{ + #if (TWI1_USE_EASY_DMA == 1) + irq_handler_twim(NRF_TWIM1, + #else + irq_handler_twi(NRF_TWI1, + #endif + &m_cb[TWI1_INSTANCE_INDEX]); +} +#endif // NRF_MODULE_ENABLED(TWI1) +#endif // TWI_COUNT +#endif // NRF_MODULE_ENABLED(TWI) diff --git a/drivers_nrf/twi_master/nrf_drv_twi.h b/drivers_nrf/twi_master/nrf_drv_twi.h new file mode 100644 index 0000000..fb9c8eb --- /dev/null +++ b/drivers_nrf/twi_master/nrf_drv_twi.h @@ -0,0 +1,438 @@ +/** + * Copyright (c) 2015 - 2017, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/**@file + * @addtogroup nrf_twi Two Wire master interface (TWI/TWIM) + * @ingroup nrf_drivers + * @brief Two Wire master interface (TWI/TWIM) APIs. + * + * + * @defgroup nrf_drv_twi TWIS driver + * @{ + * @ingroup nrf_twi + * @brief TWI master APIs. + */ +#ifndef NRF_DRV_TWI_H__ +#define NRF_DRV_TWI_H__ + +#include "nordic_common.h" +#include "sdk_config.h" + +// This set of macros makes it possible to exclude parts of code when one type +// of supported peripherals is not used. +#if ((TWI0_ENABLED == 1 && TWI0_USE_EASY_DMA == 1) || \ + (TWI1_ENABLED == 1 && TWI1_USE_EASY_DMA == 1)) + #define TWIM_IN_USE +#endif +#if ((TWI0_ENABLED == 1 && TWI0_USE_EASY_DMA != 1) || \ + (TWI1_ENABLED == 1 && TWI1_USE_EASY_DMA != 1)) + #define TWI_IN_USE +#endif + +#include "nrf_twi.h" +#ifdef TWIM_IN_USE + #include "nrf_twim.h" +#endif +#include "sdk_errors.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(TWIM_IN_USE) + #define NRF_DRV_TWI_PERIPHERAL(id) \ + (CONCAT_3(TWI, id, _USE_EASY_DMA) == 1 ? \ + (void *)CONCAT_2(NRF_TWIM, id) \ + : (void *)CONCAT_2(NRF_TWI, id)) +#else + #define NRF_DRV_TWI_PERIPHERAL(id) (void *)CONCAT_2(NRF_TWI, id) +#endif + + +/** + * @brief Structure for the TWI master driver instance. + */ +typedef struct +{ + union + { +#ifdef TWIM_IN_USE + NRF_TWIM_Type * p_twim; ///< Pointer to a structure with TWIM registers. +#endif + NRF_TWI_Type * p_twi; ///< Pointer to a structure with TWI registers. + } reg; + uint8_t drv_inst_idx; ///< Driver instance index. + bool use_easy_dma; ///< True if the peripheral with EasyDMA (TWIM) shall be used. +} nrf_drv_twi_t; + +#define TWI0_INSTANCE_INDEX 0 +#define TWI1_INSTANCE_INDEX TWI0_INSTANCE_INDEX+TWI0_ENABLED + +/** + * @brief Macro for creating a TWI master driver instance. + */ +#define NRF_DRV_TWI_INSTANCE(id) \ +{ \ + .reg = {NRF_DRV_TWI_PERIPHERAL(id)}, \ + .drv_inst_idx = CONCAT_3(TWI, id, _INSTANCE_INDEX), \ + .use_easy_dma = CONCAT_3(TWI, id, _USE_EASY_DMA) \ +} + +/** + * @brief Structure for the TWI master driver instance configuration. + */ +typedef struct +{ + uint32_t scl; ///< SCL pin number. + uint32_t sda; ///< SDA pin number. + nrf_twi_frequency_t frequency; ///< TWI frequency. + uint8_t interrupt_priority; ///< Interrupt priority. + bool clear_bus_init; ///< Clear bus during init. + bool hold_bus_uninit; ///< Hold pull up state on gpio pins after uninit. +} nrf_drv_twi_config_t; + +/** + * @brief TWI master driver instance default configuration. + */ +#define NRF_DRV_TWI_DEFAULT_CONFIG \ +{ \ + .frequency = (nrf_twi_frequency_t)TWI_DEFAULT_CONFIG_FREQUENCY, \ + .scl = 31, \ + .sda = 31, \ + .interrupt_priority = TWI_DEFAULT_CONFIG_IRQ_PRIORITY, \ + .clear_bus_init = TWI_DEFAULT_CONFIG_CLR_BUS_INIT, \ + .hold_bus_uninit = TWI_DEFAULT_CONFIG_HOLD_BUS_UNINIT, \ +} + +#define NRF_DRV_TWI_FLAG_TX_POSTINC (1UL << 0) /**< TX buffer address incremented after transfer. */ +#define NRF_DRV_TWI_FLAG_RX_POSTINC (1UL << 1) /**< RX buffer address incremented after transfer. */ +#define NRF_DRV_TWI_FLAG_NO_XFER_EVT_HANDLER (1UL << 2) /**< Interrupt after each transfer is suppressed, and the event handler is not called. */ +#define NRF_DRV_TWI_FLAG_HOLD_XFER (1UL << 3) /**< Set up the transfer but do not start it. */ +#define NRF_DRV_TWI_FLAG_REPEATED_XFER (1UL << 4) /**< Flag indicating that the transfer will be executed multiple times. */ +#define NRF_DRV_TWI_FLAG_TX_NO_STOP (1UL << 5) /**< Flag indicating that the TX transfer will not end with a stop condition. */ + +/** + * @brief TWI master driver event types. + */ +typedef enum +{ + NRF_DRV_TWI_EVT_DONE, ///< Transfer completed event. + NRF_DRV_TWI_EVT_ADDRESS_NACK, ///< Error event: NACK received after sending the address. + NRF_DRV_TWI_EVT_DATA_NACK ///< Error event: NACK received after sending a data byte. +} nrf_drv_twi_evt_type_t; + +/** + * @brief TWI master driver transfer types. + */ +typedef enum +{ + NRF_DRV_TWI_XFER_TX, ///< TX transfer. + NRF_DRV_TWI_XFER_RX, ///< RX transfer. + NRF_DRV_TWI_XFER_TXRX, ///< TX transfer followed by RX transfer with repeated start. + NRF_DRV_TWI_XFER_TXTX ///< TX transfer followed by TX transfer with repeated start. +} nrf_drv_twi_xfer_type_t; + +/** + * @brief Structure for a TWI transfer descriptor. + */ +typedef struct +{ + nrf_drv_twi_xfer_type_t type; ///< Type of transfer. + uint8_t address; ///< Slave address. + uint8_t primary_length; ///< Number of bytes transferred. + uint8_t secondary_length; ///< Number of bytes transferred. + uint8_t * p_primary_buf; ///< Pointer to transferred data. + uint8_t * p_secondary_buf; ///< Pointer to transferred data. +} nrf_drv_twi_xfer_desc_t; + + +/**@brief Macro for setting the TX transfer descriptor. */ +#define NRF_DRV_TWI_XFER_DESC_TX(addr, p_data, length) \ + { \ + .type = NRF_DRV_TWI_XFER_TX, \ + .address = addr, \ + .primary_length = length, \ + .p_primary_buf = p_data, \ + } + +/**@brief Macro for setting the RX transfer descriptor. */ +#define NRF_DRV_TWI_XFER_DESC_RX(addr, p_data, length) \ + { \ + .type = NRF_DRV_TWI_XFER_RX, \ + .address = addr, \ + .primary_length = length, \ + .p_primary_buf = p_data, \ + } + +/**@brief Macro for setting the TXRX transfer descriptor. */ +#define NRF_DRV_TWI_XFER_DESC_TXRX(addr, p_tx, tx_len, p_rx, rx_len) \ + { \ + .type = NRF_DRV_TWI_XFER_TXRX, \ + .address = addr, \ + .primary_length = tx_len, \ + .secondary_length = rx_len, \ + .p_primary_buf = p_tx, \ + .p_secondary_buf = p_rx, \ + } + +/**@brief Macro for setting the TXTX transfer descriptor. */ +#define NRF_DRV_TWI_XFER_DESC_TXTX(addr, p_tx, tx_len, p_tx2, tx_len2) \ + { \ + .type = NRF_DRV_TWI_XFER_TXTX, \ + .address = addr, \ + .primary_length = tx_len, \ + .secondary_length = tx_len2, \ + .p_primary_buf = p_tx, \ + .p_secondary_buf = p_tx2, \ + } + +/** + * @brief Structure for a TWI event. + */ +typedef struct +{ + nrf_drv_twi_evt_type_t type; ///< Event type. + nrf_drv_twi_xfer_desc_t xfer_desc; ///< Transfer details. +} nrf_drv_twi_evt_t; + +/** + * @brief TWI event handler prototype. + */ +typedef void (* nrf_drv_twi_evt_handler_t)(nrf_drv_twi_evt_t const * p_event, + void * p_context); + +/** + * @brief Function for initializing the TWI driver instance. + * + * @param[in] p_instance Pointer to the driver instance structure. + * @param[in] p_config Initial configuration. If NULL, the default configuration is used. + * @param[in] event_handler Event handler provided by the user. If NULL, blocking mode is enabled. + * @param[in] p_context Context passed to event handler. + * + * @retval NRF_SUCCESS If initialization was successful. + * @retval NRF_ERROR_INVALID_STATE If the driver is in invalid state. + * @retval NRF_ERROR_BUSY If some other peripheral with the same + * instance ID is already in use. This is + * possible only if PERIPHERAL_RESOURCE_SHARING_ENABLED + * is set to a value other than zero. + */ +ret_code_t nrf_drv_twi_init(nrf_drv_twi_t const * p_instance, + nrf_drv_twi_config_t const * p_config, + nrf_drv_twi_evt_handler_t event_handler, + void * p_context); + +/** + * @brief Function for uninitializing the TWI instance. + * + * @param[in] p_instance Pointer to the driver instance structure. + */ +void nrf_drv_twi_uninit(nrf_drv_twi_t const * p_instance); + +/** + * @brief Function for enabling the TWI instance. + * + * @param[in] p_instance Pointer to the driver instance structure. + */ +void nrf_drv_twi_enable(nrf_drv_twi_t const * p_instance); + +/** + * @brief Function for disabling the TWI instance. + * + * @param[in] p_instance Pointer to the driver instance structure. + */ +void nrf_drv_twi_disable(nrf_drv_twi_t const * p_instance); + +/** + * @brief Function for sending data to a TWI slave. + * + * The transmission will be stopped when an error occurs. If a transfer is ongoing, + * the function returns the error code @ref NRF_ERROR_BUSY. + * + * @param[in] p_instance Pointer to the driver instance structure. + * @param[in] address Address of a specific slave device (only 7 LSB). + * @param[in] p_data Pointer to a transmit buffer. + * @param[in] length Number of bytes to send. + * @param[in] no_stop If set, the stop condition is not generated on the bus + * after the transfer has completed successfully (allowing + * for a repeated start in the next transfer). + * + * @retval NRF_SUCCESS If the procedure was successful. + * @retval NRF_ERROR_BUSY If the driver is not ready for a new transfer. + * @retval NRF_ERROR_INTERNAL If an error was detected by hardware. + * @retval NRF_ERROR_INVALID_ADDR If the EasyDMA is used and memory adress in not in RAM. + * @retval NRF_ERROR_DRV_TWI_ERR_ANACK If NACK received after sending the address in polling mode. + * @retval NRF_ERROR_DRV_TWI_ERR_DNACK If NACK received after sending a data byte in polling mode. + */ +ret_code_t nrf_drv_twi_tx(nrf_drv_twi_t const * p_instance, + uint8_t address, + uint8_t const * p_data, + uint8_t length, + bool no_stop); + +/** + * @brief Function for reading data from a TWI slave. + * + * The transmission will be stopped when an error occurs. If a transfer is ongoing, + * the function returns the error code @ref NRF_ERROR_BUSY. + * + * @param[in] p_instance Pointer to the driver instance structure. + * @param[in] address Address of a specific slave device (only 7 LSB). + * @param[in] p_data Pointer to a receive buffer. + * @param[in] length Number of bytes to be received. + * + * @retval NRF_SUCCESS If the procedure was successful. + * @retval NRF_ERROR_BUSY If the driver is not ready for a new transfer. + * @retval NRF_ERROR_INTERNAL If an error was detected by hardware. + * @retval NRF_ERROR_DRV_TWI_ERR_OVERRUN If the unread data was replaced by new data + * @retval NRF_ERROR_DRV_TWI_ERR_ANACK If NACK received after sending the address in polling mode. + * @retval NRF_ERROR_DRV_TWI_ERR_DNACK If NACK received after sending a data byte in polling mode. + */ +ret_code_t nrf_drv_twi_rx(nrf_drv_twi_t const * p_instance, + uint8_t address, + uint8_t * p_data, + uint8_t length); + +/** + * @brief Function for preparing a TWI transfer. + * + * The following transfer types can be configured (@ref nrf_drv_twi_xfer_desc_t::type): + * - @ref NRF_DRV_TWI_XFER_TXRX<span></span>: Write operation followed by a read operation (without STOP condition in between). + * - @ref NRF_DRV_TWI_XFER_TXTX<span></span>: Write operation followed by a write operation (without STOP condition in between). + * - @ref NRF_DRV_TWI_XFER_TX<span></span>: Write operation (with or without STOP condition). + * - @ref NRF_DRV_TWI_XFER_RX<span></span>: Read operation (with STOP condition). + * + * Additional options are provided using the flags parameter: + * - @ref NRF_DRV_TWI_FLAG_TX_POSTINC and @ref NRF_DRV_TWI_FLAG_RX_POSTINC<span></span>: Post-incrementation of buffer addresses. Supported only by TWIM. + * - @ref NRF_DRV_TWI_FLAG_NO_XFER_EVT_HANDLER<span></span>: No user event handler after transfer completion. In most cases, this also means no interrupt at the end of the transfer. + * - @ref NRF_DRV_TWI_FLAG_HOLD_XFER<span></span>: Driver is not starting the transfer. Use this flag if the transfer is triggered externally by PPI. Supported only by TWIM. + * Use @ref nrf_drv_twi_start_task_get to get the address of the start task. + * - @ref NRF_DRV_TWI_FLAG_REPEATED_XFER<span></span>: Prepare for repeated transfers. You can set up a number of transfers that will be triggered externally (for example by PPI). + * An example is a TXRX transfer with the options @ref NRF_DRV_TWI_FLAG_RX_POSTINC, @ref NRF_DRV_TWI_FLAG_NO_XFER_EVT_HANDLER, and @ref NRF_DRV_TWI_FLAG_REPEATED_XFER. + * After the transfer is set up, a set of transfers can be triggered by PPI that will read, for example, the same register of an + * external component and put it into a RAM buffer without any interrupts. @ref nrf_drv_twi_stopped_event_get can be used to get the + * address of the STOPPED event, which can be used to count the number of transfers. If @ref NRF_DRV_TWI_FLAG_REPEATED_XFER is used, + * the driver does not set the driver instance into busy state, so you must ensure that the next transfers are set up + * when TWIM is not active. Supported only by TWIM. + * - @ref NRF_DRV_TWI_FLAG_TX_NO_STOP<span></span>: No stop condition after TX transfer. + * + * @note + * Some flag combinations are invalid: + * - @ref NRF_DRV_TWI_FLAG_TX_NO_STOP with @ref nrf_drv_twi_xfer_desc_t::type different than @ref NRF_DRV_TWI_XFER_TX + * - @ref NRF_DRV_TWI_FLAG_REPEATED_XFER with @ref nrf_drv_twi_xfer_desc_t::type set to @ref NRF_DRV_TWI_XFER_TXTX + * + * If @ref nrf_drv_twi_xfer_desc_t::type is set to @ref NRF_DRV_TWI_XFER_TX and the @ref NRF_DRV_TWI_FLAG_TX_NO_STOP and @ref NRF_DRV_TWI_FLAG_REPEATED_XFER + * flags are set, two tasks must be used to trigger a transfer: TASKS_RESUME followed by TASKS_STARTTX. If no stop condition is generated, + * TWIM is in SUSPENDED state. Therefore, it must be resumed before the transfer can be started. + * + * @note + * This function should be used only if the instance is configured to work in non-blocking mode. If the function is used in blocking mode, the driver asserts. + * @note If you are using this function with TWI, the only supported flag is @ref NRF_DRV_TWI_FLAG_TX_NO_STOP. All other flags require TWIM. + * + * @param[in] p_instance Pointer to the driver instance structure. + * @param[in] p_xfer_desc Pointer to the transfer descriptor. + * @param[in] flags Transfer options (0 for default settings). + * + * @retval NRF_SUCCESS If the procedure was successful. + * @retval NRF_ERROR_BUSY If the driver is not ready for a new transfer. + * @retval NRF_ERROR_NOT_SUPPORTED If the provided parameters are not supported. + * @retval NRF_ERROR_INTERNAL If an error was detected by hardware. + * @retval NRF_ERROR_INVALID_ADDR If the EasyDMA is used and memory adress in not in RAM + * @retval NRF_ERROR_DRV_TWI_ERR_OVERRUN If the unread data was replaced by new data (TXRX and RX) + * @retval NRF_ERROR_DRV_TWI_ERR_ANACK If NACK received after sending the address. + * @retval NRF_ERROR_DRV_TWI_ERR_DNACK If NACK received after sending a data byte. + */ +ret_code_t nrf_drv_twi_xfer(nrf_drv_twi_t const * p_instance, + nrf_drv_twi_xfer_desc_t const * p_xfer_desc, + uint32_t flags); + +/** + * @brief Function for checking the TWI driver state. + * + * @param[in] p_instance TWI instance. + * + * @retval true If the TWI driver is currently busy performing a transfer. + * @retval false If the TWI driver is ready for a new transfer. + */ +bool nrf_drv_twi_is_busy(nrf_drv_twi_t const * p_instance); + +/** + * @brief Function for getting the transferred data count. + * + * This function provides valid results only in legacy mode. + * + * @param[in] p_instance Pointer to the driver instance structure. + * + * @return Data count. + */ +uint32_t nrf_drv_twi_data_count_get(nrf_drv_twi_t const * const p_instance); + +/** + * @brief Function for returning the address of a TWI/TWIM start task. + * + * This function should be used if @ref nrf_drv_twi_xfer was called with the flag @ref NRF_DRV_TWI_FLAG_HOLD_XFER. + * In that case, the transfer is not started by the driver, but it must be started externally by PPI. + * + * @param[in] p_instance Pointer to the driver instance structure. + * @param[in] xfer_type Transfer type used in the last call of the @ref nrf_drv_twi_xfer function. + * + * @return Start task address (TX or RX) depending on the value of xfer_type. + */ +uint32_t nrf_drv_twi_start_task_get(nrf_drv_twi_t const * p_instance, nrf_drv_twi_xfer_type_t xfer_type); + +/** + * @brief Function for returning the address of a STOPPED TWI/TWIM event. + * + * A STOPPED event can be used to detect the end of a transfer if the @ref NRF_DRV_TWI_FLAG_NO_XFER_EVT_HANDLER + * option is used. + * + * @param[in] p_instance Pointer to the driver instance structure. + * + * @return STOPPED event address. + */ +uint32_t nrf_drv_twi_stopped_event_get(nrf_drv_twi_t const * p_instance); +/** + *@} + **/ + + +#ifdef __cplusplus +} +#endif + +#endif // NRF_DRV_TWI_H__ |