This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Setting up TWI interface between the nRF52 DK and ADS1115

Hi,

I am trying to setup a TWI interface between the nRF52 DK and the 16-bit ADC ADS1115 from TI. I am using the breakout board from Adafruit (https://www.adafruit.com/product/1085). Anyway, using the TWI scanner I can see the device and the correct address at 0x48. When trying to so a simple single ended reading I run into some trouble.

I get this error:

<error> app: ERROR 33282 [NRF_ERROR_DRV_TWI_ERR_DNACK] at /home/emil/nRF_SDK/SDK15/examples/Projects/nRF52832-Potentiometric-Board/main.c:185

Which is this line of code

err_code = nrf_drv_twi_tx(p_instance, i2cAddress, &reg, sizeof(&reg), false);
APP_ERROR_CHECK(err_code);

This is my current code:

void twi_init(void) {
  ret_code_t err_code;

  const nrf_drv_twi_config_t twi_ADS1115_config = {
      .scl = 27,
      .sda = 26,
      .frequency = NRF_DRV_TWI_FREQ_100K,
      .interrupt_priority = APP_IRQ_PRIORITY_HIGH,
      .clear_bus_init = false};

      GPIO_PIN_CNF_PULL_Disabled;

  err_code = nrf_drv_twi_init(&m_twi, &twi_ADS1115_config, NULL, NULL);
  APP_ERROR_CHECK(err_code);

  nrf_drv_twi_enable(&m_twi);
}

static uint16_t readADC_SingleEnded(nrf_drv_twi_t const *const p_instance, uint8_t channel) {
  if (channel > 3) {
    return 0;
  }
  m_bitShift = 4;
  m_gain = GAIN_FOUR;
  //m_xfer_done = false;
  // Start with the default values
  uint16_t config = ADS1115_REG_CONFIG_CQUE_NONE |    // Disable the comparator (default val)
                    ADS1115_REG_CONFIG_CLAT_NONLAT |  // Non-latching (default val)
                    ADS1115_REG_CONFIG_CPOL_ACTVLOW | // Alert/Ready active low (default val)
                    ADS1115_REG_CONFIG_CMODE_TRAD |   // Traditional comparator
                    ADS1115_REG_CONFIG_DR_1600SPS |   // 1600 samples per second (default)
                    ADS1115_REG_CONFIG_MODE_SINGLE;   // Single shot mode (default)

  // Set PGA/Voltage gain
  config |= m_gain;

  // Set single-ended input channel to read from
  switch (channel) {
  case (0):
    config |= ADS1115_REG_CONFIG_MUX_SINGLE_0;
    break;
  case (1):
    config |= ADS1115_REG_CONFIG_MUX_SINGLE_1;
    break;
  case (2):
    config |= ADS1115_REG_CONFIG_MUX_SINGLE_2;
    break;
  case (3):
    config |= ADS1115_REG_CONFIG_MUX_SINGLE_3;
    break;
  }

  // Set "start single-conversion" bit
  config |= ADS1115_REG_CONFIG_OS_SINGLE;

  // Write config register to the ADC
  // Note: currently block until the write happens
  writeRegister(p_instance, m_i2cAddress, ADS1115_REG_POINTER_CONFIG, config);

  // Read the conversion results
  // Shift 16-bit results right 4 bits for the ADS1115
  uint16_t valInRegister = readRegister(p_instance, m_i2cAddress, ADS1115_REG_POINTER_CONVERT);

  return valInRegister >> m_bitShift;
}

void writeRegister(nrf_drv_twi_t const *const p_instance, uint8_t i2cAddress, uint8_t reg, uint16_t value) {
  uint8_t tx_data[3];
  uint32_t err_code;
  tx_data[0] = (uint8_t)reg;
  tx_data[1] = (uint8_t)(value >> 8);
  tx_data[2] = (uint8_t)(value & 0xFF);

  err_code = nrf_drv_twi_tx(p_instance, i2cAddress, tx_data, sizeof(tx_data), false);
  APP_ERROR_CHECK(err_code);
}

uint16_t readRegister(nrf_drv_twi_t const *const p_instance, uint8_t i2cAddress, uint8_t reg) {
  uint32_t err_code;

  // TBD: err_code (error handling) should be indentical to all other nordic modules
  err_code = nrf_drv_twi_tx(p_instance, i2cAddress, &reg, sizeof(&reg), false);
  APP_ERROR_CHECK(err_code);

  err_code = nrf_drv_twi_rx(p_instance, i2cAddress, m_sample, 2);
  APP_ERROR_CHECK(err_code);

  m_adcValue = (m_sample[0] << 8 | m_sample[1]);

  return m_adcValue;
}

So it seems to work fine using the writeRegister function, but when doing the readRegister function after that I get the error. Could there be a problem using two TX commands after each other or is there something else obvious that I am missing?

Let me know if I should provide any more code. If needed, I can try to get a hold of an oscilloscope to try and probe something.

Best regards,

Emil

  • I know its really old thread might not be any help to respond but just in case others are checking,
    " nrf_drv_twi_tx(p_instance, i2cAddress, &reg, sizeof(&reg), false);"

    shouldn't it be sizeof(reg). That migh not solve the issue but its getting address size.

  • So again, I know it will not help Emil anymore as its 2 years to the post but for others if they don't want to bang their head like me.
    There need to be small delay between reg index write and reading back the value . That did the trick. 1ms delay worked for me, may be even less will work not sure. For reference the code here. (this is doing rough calculation for Irms, SCT013 is connected to ADS1115)

    Mind the 1ms delay,  
    i2c_write(ADS1X15_ADDRESS, &reg, 1, true);
    nrf_delay_ms(1);
    i2c_read(ADS1X15_ADDRESS, &reg, ui8RxBuff, 2);


    // ====================== I2C - ADS1115 communicaiton =====================================
    
    #define ADS1X15_ADDRESS (0x48) ///< 1001 000 (ADDR = GND)
    #define PIN_SCL        (27)
    #define PIN_SDA        (26)
    
    static int sentbyte = 0;
    void i2c_write(uint8_t addr, uint8_t *pdata, uint16_t dataSize, bool bStop)
    {
      uint8_t tx_buf[2];
    	int i = 0;
    	if(bStop)
    	{
    		//NRF_TWIM0->SHORTS = TWIM_SHORTS_LASTTX_SUSPEND_Msk;
    		NRF_TWIM0->SHORTS = TWIM_SHORTS_LASTTX_STOP_Msk;
    	}
    	
      NRF_TWIM0->TXD.MAXCNT = dataSize;
      NRF_TWIM0->TXD.PTR = (uint32_t)pdata;
    	//NRF_TWIM0->TXD.LIST = 1;
    
      NRF_TWIM0->EVENTS_STOPPED = 0;
    	NRF_TWIM0->EVENTS_SUSPENDED = 0;
    	NRF_TWIM0->EVENTS_LASTTX = 0;
    	if(NRF_TWIM0->EVENTS_SUSPENDED){
    		NRF_TWIM0->TASKS_RESUME = 1;
    	}else{
    		NRF_TWIM0->TASKS_STARTTX = 1;
    	}
    	if(bStop)
    	{
    		//while (NRF_TWIM0->EVENTS_SUSPENDED == 0){};
    		while (NRF_TWIM0->EVENTS_STOPPED == 0){};
    		sentbyte = NRF_TWIM0->TXD.AMOUNT;
    		while(NRF_TWIM0->EVENTS_LASTTX == 0);
    		NRF_TWIM0->TASKS_STOP = 1;
    		//NRF_TWIM0->TASKS_SUSPEND = 1;
    	}
    }
    
    void i2c_read(uint8_t addr, uint8_t *reg, uint8_t *pdata, uint16_t dataSize)
    {
    	sentbyte = 0;
      NRF_TWIM0->SHORTS = TWIM_SHORTS_LASTRX_STOP_Msk;
    
      NRF_TWIM0->RXD.MAXCNT = dataSize;
      NRF_TWIM0->RXD.PTR = (uint32_t)pdata;
    
      NRF_TWIM0->EVENTS_STOPPED = 0;
    	NRF_TWIM0->EVENTS_SUSPENDED = 0;
    	NRF_TWIM0->EVENTS_LASTRX = 0;
      NRF_TWIM0->TASKS_STARTRX = 1;
      while (NRF_TWIM0->EVENTS_STOPPED == 0);
    	sentbyte = NRF_TWIM0->RXD.AMOUNT;
    //	while(NRF_TWIM0->EVENTS_LASTRX == 0);
    	NRF_TWIM0->TASKS_STOP = 1;
    }
    
    void i2c_setup (void)
    {
    		
      NRF_TWIM0->PSEL.SCL = PIN_SCL;
      NRF_TWIM0->PSEL.SDA = PIN_SDA;
    
      NRF_TWIM0->ADDRESS = ADS1X15_ADDRESS;
      NRF_TWIM0->FREQUENCY = TWIM_FREQUENCY_FREQUENCY_K100 << TWIM_FREQUENCY_FREQUENCY_Pos;
     // NRF_TWIM0->SHORTS = 0;
    
      NRF_TWIM0->ENABLE = TWIM_ENABLE_ENABLE_Enabled << TWIM_ENABLE_ENABLE_Pos;
    }
    
    
    /**************************************************************************/
    /*!
        @brief  Reads the conversion results, measuring the voltage
                difference between the P (AIN0) and N (AIN1) input.  Generates
                a signed value since the difference can be either
                positive or negative.
    
        @return the ADC reading
    */
    /**************************************************************************/
    #define ADS1X15_REG_POINTER_CONVERT (0x00)   ///< Conversion
    
    #define ADS1X15_REG_CONFIG_CQUE_NONE                                           \
      (0x0003) ///< Disable the comparator and put ALERT/RDY in high state (default)
    #define ADS1X15_REG_CONFIG_CLAT_NONLAT                                         \
      (0x0000) ///< Non-latching comparator (default)
    #define ADS1X15_REG_CONFIG_CPOL_ACTVLOW                                        \
      (0x0000) ///< ALERT/RDY pin is low when active (default)
    #define ADS1X15_REG_CONFIG_CMODE_TRAD                                          \
      (0x0000) ///< Traditional comparator with hysteresis (default)
    #define ADS1X15_REG_CONFIG_MODE_SINGLE                                         \
      (0x0100) ///< Power-down single-shot mode (default)
    
    #define ADS1X15_REG_CONFIG_PGA_6_144V (0x0000) ///< +/-6.144V range = Gain 2/3
    #define ADS1X15_REG_CONFIG_PGA_4_096V (0x0200) ///< +/-4.096V range = Gain 1
    #define ADS1X15_REG_CONFIG_PGA_2_048V                                          \
      (0x0400) ///< +/-2.048V range = Gain 2 (default)
    #define ADS1X15_REG_CONFIG_PGA_1_024V (0x0600) ///< +/-1.024V range = Gain 4
    #define ADS1X15_REG_CONFIG_PGA_0_512V (0x0800) ///< +/-0.512V range = Gain 8
    #define ADS1X15_REG_CONFIG_PGA_0_256V (0x0A00) ///< +/-0.256V range = Gain 16
    
    #define RATE_ADS1015_1600SPS (0x0080) ///< 1600 samples per second (default)
    
    #define ADS1X15_REG_CONFIG_MUX_SINGLE_0 (0x4000) ///< Single-ended AIN0
    #define ADS1X15_REG_CONFIG_MUX_DIFF_0_1                                        \
      (0x0000) ///< Differential P = AIN0, N = AIN1 (default)
    #define ADS1X15_REG_CONFIG_OS_SINGLE                                           \
      (0x8000) ///< Write: Set to start a single-conversion
    #define ADS1X15_REG_POINTER_CONFIG (0x01)    ///< Configuration
    
    /**************************************************************************/
    /*!
        @brief  Writes 16-bits to the specified destination register
    
        @param reg register address to write to
        @param value value to write to register
    */
    /**************************************************************************/
    
    void ADS1X15_writeRegister(uint8_t reg, uint16_t value) {
    	static uint8_t ui8buffer[4] = {0};
      ui8buffer[0] = reg;
      ui8buffer[1] = value >> 8;
      ui8buffer[2] = value & 0xFF;
      i2c_write(ADS1X15_ADDRESS, ui8buffer, 3, true);//sizeof(ui8buffer));
    	
    	nrf_delay_ms(2);
    }
    
    /**************************************************************************/
    /*!
        @brief  Read 16-bits from the specified destination register
    
        @param reg register address to read from
    
        @return 16 bit register value read
    */
    /**************************************************************************/
    uint16_t ADS1X15_readRegister(uint8_t reg) {
    	uint8_t ui8RxBuff[3] = {0}; 
      i2c_write(ADS1X15_ADDRESS, &reg, 1, true); 
    	nrf_delay_ms(1);
      i2c_read(ADS1X15_ADDRESS, &reg, ui8RxBuff, 2);
    	
    	return (ui8RxBuff[0] << 8 | ui8RxBuff[1]);
    }
    	
    /**************************************************************************/
    /*!
        @brief  Returns true if conversion is complete, false otherwise.
    */
    /**************************************************************************/
    bool ADS1X115_conversionComplete() {
      return (ADS1X15_readRegister(ADS1X15_REG_POINTER_CONFIG) & 0x8000) != 0;
    }
    
    /**************************************************************************/
    /*!
        @brief  In order to clear the comparator, we need to read the
                conversion results.  This function reads the last conversion
                results without changing the config value.
    
        @return the last ADC reading
    */
    /**************************************************************************/
    int16_t ADS1X15_getLastConversionResults(void) {
      // Read the conversion results
    	//ADS1X15_readRegister(0x00);
    	//ADS1X15_readRegister(0x01);
    	//ADS1X15_readRegister(0x10);
    	//ADS1X15_readRegister(0x11);
      return ADS1X15_readRegister(ADS1X15_REG_POINTER_CONVERT);
    }
    	
    int16_t ADS1X15_readADC_Differential_0_1(void) {
      // Start with default values
      static uint16_t config =
          ADS1X15_REG_CONFIG_CQUE_NONE |    // Disable the comparator (default val)
          ADS1X15_REG_CONFIG_CLAT_NONLAT |  // Non-latching (default val)
          ADS1X15_REG_CONFIG_CPOL_ACTVLOW | // Alert/Rdy active low   (default val)
          ADS1X15_REG_CONFIG_CMODE_TRAD |   // Traditional comparator (default val)
          ADS1X15_REG_CONFIG_MODE_SINGLE;   // Single-shot mode (default)
    
      // Set PGA/voltage range
      config |= ADS1X15_REG_CONFIG_PGA_1_024V;
    
      // Set data rate
      config |= RATE_ADS1015_1600SPS;
    
      // Set channels
      config |= ADS1X15_REG_CONFIG_MUX_DIFF_0_1; // AIN0 = P, AIN1 = N
    
      // Set 'start single-conversion' bit
      config |= ADS1X15_REG_CONFIG_OS_SINGLE;
    
    	config = 0x818B; // Try the Arduino config 
      // Write config register to the ADC
      ADS1X15_writeRegister(ADS1X15_REG_POINTER_CONFIG, config);
    
      // Wait for the conversion to complete
      while (!ADS1X115_conversionComplete());
      // Read the conversion results
      return ADS1X15_getLastConversionResults();
    }
    
    const float FACTOR_1 = 20; //20A/1V from teh CT
    const float multiplier_1 = 0.00005;
    
    float getCurrent (void)
    {
    	float voltage_1;
      float current_1;
      float sum_1 = 0;
      long time_check_1 = 0;
      int counter_1 = 0;
    
      while (time_check_1++ < 200)
      {
        voltage_1 = (float)ADS1X15_readADC_Differential_0_1() * multiplier_1;
        current_1 = voltage_1 * FACTOR_1;
    
        sum_1 += pow(current_1, 2);
        counter_1 ++;
      }
    
      current_1 = sqrt(sum_1 / counter_1);
      return current_1;
    }


Related