I recently had the need to communicate with a device over I2C. I'm using the nRF52 as the master. Everything works fine, but power is a concern. I am putting the processor into the ON low power mode. Currently, I'm typically running at around 120 uA, as measured with a Current Ranger. I'm measuring the current at the external power input to the PCA10040 board (P21). I wrote my own I2C driver, as I have with all my drivers.
I have a timer (RTC) that periodically (3-30 seconds, depending on various factors) initiates a communication with the external part over the TWIM interface. The actual communication exchange takes no longer than 60 ms at 400 KHz bus speed. I'm switching this external part on and off using a GPIO pin around the exchange. The first exchange happens 10 seconds after power up.
Before the first communication, my current consumption is about 120 uA. After the first communication, the power consumption is about 400 or so uA higher and it remains there. It certainly seems like after turning on the TWIM instance and then turning it off, it is not returning to the same state. I'm turning the TWIM on and off using the ENABLE register.
Is there something I need to do with the EasyDMA that I need to know about? I'll post a couple of code snippets.
//====================================================================== /*! @brief Enable the I2C. @details This function controls the functioning of the I2C. The I2C must be disabled before changing any configuration parameters. It may also be disabled for minimizing power consumption. @param i2c Identifies the I2C. @param enable TRUE to enable the I2C. @author John Heaney @test 03/14/2020 Unit Test: UNTESTED */ void I2cEnable(I2cEnum i2c, BOOL enable) { NRF_TWIM_Type * i2cBaseReg; UINT32 enableValue; //The register used to enable either the master or slave is the same, but the // value used is different. enableValue = I2cIsMaster(i2c) ? TWIM_ENABLE_ENABLE_Enabled : TWIS_ENABLE_ENABLE_Enabled; i2cBaseReg = I2cGetBaseRegister(i2c); i2cBaseReg->ENABLE = enable ? enableValue : TWIM_ENABLE_ENABLE_Disabled; }
//====================================================================== /*! @brief IRQ handler for I2C. @detail Each I2C interface has an interrupt vector. This is a common handler for all of them. The parameter identifies the specific instance, which can be directly related to a I2cEnum. @param systemID Idenfier (ordinal number) for the I2C interrupt source. @author John Heaney @test 05/07/2020 Unit Test: UNTESTED */ static void irq_handler(UINT8 systemID) { I2cEnum i2c; NRF_TWIM_Type * i2cBaseReg; ErrorCodeEnum errCode; UINT32 i2cError; EventData eventData; //Go from the systemID to its base register. i2c = i2cReferences[systemID]; i2cBaseReg = I2cGetBaseRegister(i2c); //Check for errors first. if (i2cBaseReg->EVENTS_ERROR) { i2cBaseReg->EVENTS_ERROR = 0; i2cError = i2cBaseReg->ERRORSRC; if (TWIM_ERRORSRC_OVERRUN_Msk & i2cError) { errCode = I2C_ERROR_OVERRUN; } else if (TWIM_ERRORSRC_ANACK_Msk & i2cError) { i2cBaseReg->ERRORSRC = TWIM_ERRORSRC_ANACK_Msk; errCode = I2C_ERROR_ADDR_NAK; } else if (TWIM_ERRORSRC_DNACK_Msk & i2cError) { i2cBaseReg->ERRORSRC = TWIM_ERRORSRC_DNACK_Msk; errCode = I2C_ERROR_DATA_NAK; } else { errCode = I2C_ERROR_UNKNOWN; i2cError = ERROR_DATA_DEFAULT; } ErrorSetErrorWithData(errCode, (ErrorCodeData)i2cError); //Issue a stop after any errors. i2cBaseReg->TASKS_STOP = 1; } //Check for being done. Every interaction ends with a STOP. if (i2cBaseReg->EVENTS_STOPPED) { i2cBaseReg->EVENTS_STOPPED = 0; //Event data is number of bytes transmitted. Determine the mode and which // amount to look at by looking at how the SHORTS register has been configured. eventData = TWIM_SHORTS_LASTRX_STOP_Msk == i2cBaseReg->SHORTS ? i2cBaseReg->RXD.AMOUNT : i2cBaseReg->TXD.AMOUNT; //Handle the result in the main context. EventPostWithData(I2cGetDoneEvent(i2c), TASK_ANY, eventData); //These are initialized at the start of each communication, but it doesn't // hurt to clean up at the end. i2cBaseReg->EVENTS_RXSTARTED = 0; i2cBaseReg->EVENTS_TXSTARTED = 0; i2cBaseReg->EVENTS_LASTRX = 0; i2cBaseReg->EVENTS_LASTTX = 0; i2cBaseReg->SHORTS = 0; //All done with the interface. Power it down. I2cEnable(i2c, FALSE); } }