I2C and AD7991 and Teensy4

pd0lew

Well-known member
Hi all,

The Teensy 4...... my questions : the AD7991 works on pins 19 and 18 but not on 17 and 16 any suggestions?
Best,
Johan
 
Last edited:
Hi all,

The Teensy 4...... my questions : the AD7991 works on pins 19 and 18 but not on 17 and 16 any suggestions?
Best,
Johan

The Teensy 4 does not have alternate I2C pins like the previous Teensy LC/3.x microprocessors did. So for the first i2c port, you have to use pins 19 and 18.

However, the Teensy 4.0 does have a second i2c that are on pins 16 & 17. To use this port, you need to change references from Wire to Wire1 in both your code and in the driver. Typically you would need to clone the driver to have a new name, and change the include file in your .ino/.pde file. Some i2c drivers let you pass in the i2c bus to use as an optional argument.

As in the previous Teensys when using the alternate i2c pins, pin 17 is the SDA pin and pin 16 is the SCL pin (reversing the order from the standard i2c pins, where pin 19 is SCL and pin 18 is SDA).

There is a 3rd i2c bus on the Teensy 4.0 that you would need to use the underneath pads to use (pad 24 is SCL2 and pad 25 is SDA2).
 
Hi -
Sorry it's been a few years since I've played with my Teensy 4.0 stuff.

I have a new project that uses an AD7991 4-channel ADC for analog input. Is there some example code for this, including which libraries to include?

Thanks.
Dave
 
Hi Dave,
This is what I used for my power and SWR meter and changed it a bit for you.....

Hope it is usefull.
Best,
Johan


Code:
#include<Wire.h>

#define AD7991REF   2.60      
int8_t  ad7991_addr = 0;              // Address of AD7991 I2C connected A/D, 0 if none detected
#define AD7991_0  0x28                // I2C Address of AD7991-0
#define AD7991_1  0x29                // I2C Address of AD7991-1
#define I2C Wire
double      adc_ref;        // ADC reference (Teensy or external AD7991)
int16_t     fwd;            // AD input - 12 bit value, v-forward
int16_t     rev;            // AD input - 12 bit value, v-reverse
typedef struct {
          int16_t  fwd[256];                  // Circular buffer of Forward measurement values
          int16_t  rev[256];                  // Circular buffer of Reverse measurement values
          uint8_t  incount;                   // Pointer to most recent input value of circular buffer
          uint8_t  outcount;                  // Pointer to most recent output value of circular buffer
               }  adbuffer_t;

volatile adbuffer_t measure;                   

void setup()
{
  // Initialise I2C communication as MASTER
  Wire.begin();
  Wire.setClock(2000000);
  Serial.begin(115200);
  I2C_Init();
}

void loop()
{    
    adc_ref = 2.60;
    double    output_voltage;
    output_voltage = fwd * adc_ref / 2048;
   
    Serial.println(output_voltage,4);
    adc_poll_and_feed_circular(); 
     pswr_sync_from_interrupt();
}
uint8_t I2C_Init(void)
{
  uint8_t found;                      // Returns status of I2C bus scan

  // Scan for AD7991-0 or AD7991-1
  ad7991_addr = AD7991_1;             // We start testing for an AD7991-1
  found = 2;                          //  Assume it will be found
  I2C.beginTransmission(ad7991_addr);
  if(I2C.endTransmission() != 0)     // If error
  {
    ad7991_addr = AD7991_0;           // AD7991-0 was not detected
    found = 1;                        // We may have an AD7991-0  
  }
  I2C.beginTransmission(ad7991_addr);
  if(I2C.endTransmission() != 0)     // If error
  {
    ad7991_addr = 0;                  // AD7991-1 was not detected
    found = 0;                        // No I2C capable device was detected
  }

  // If Power & SWR Code and AD7991 12 bit ADC, the program it to use a 2.6V reference connected
  // to ADC channel 4 and only read ADC channels 1 and 2 consecutively.
  //
  if (found)                          // If AD7991 ADC was found, then configure it...
  {
    I2C.beginTransmission(ad7991_addr);
    I2C.write(0x38);                 // Set ADCs 1 and 2 for consecutive reads and REF_SEL = external
    I2C.endTransmission();
    adc_ref = 2.600;              // Readjust the ADC reference to the external REF voltage
  }
  return found;
}


//-----------------------------------------------------------------------------------------
//                            Poll the AD7991 2 x ADC chip
//                                  or alternately
//              use built-in 12 bit A/D converters if I2C device not present
//                 This function is called from the Interrupt function, and
//              reads the A/D inputs and transfers them to two circular buffers
//-----------------------------------------------------------------------------------------

void adc_poll_and_feed_circular(void)
{
  
    uint16_t          adc_in[4];
    uint8_t           read_B[4];
    uint8_t           i=0;
 
  // use I2C connected AD7991 12-bit AD converter, if it was detected during init
  // Each I2C poll of the AD7991 is measured to take 140us
  if (ad7991_addr)
  {
     I2C.requestFrom(ad7991_addr, 4);
    while (I2C.available()) read_B[i++] = I2C.read();

    // The output of the 12bit ADCs is contained in two consecutive byte pairs
    // read from the AD7991.  In theory, the second could be read before the first.
    // Each of the AD7991 four builtin ADCs has an address identifier (0 to 3)
    // in the uppermost 4 bits of the first byte.  The lowermost 4 bits of the first
    // byte are bits 8-12 of the A/D output.
    // In this routine we only read the two first ADCs, as set up in the I2C_Init()
    //
    adc_in[(read_B[0] >> 4) & 0x03] = (read_B[0] & 0x0f) * 0x100 + read_B[1];
    adc_in[(read_B[2] >> 4) & 0x03] = (read_B[2] & 0x0f) * 0x100 + read_B[3];
    measure.fwd[measure.incount] = adc_in[0]; // Input from AD7991 is 12 bit resolution
    measure.rev[measure.incount] = adc_in[1]; // contained in bit positions 0 to 11.
    measure.incount++;                        // 8 bit value, rolls over at 256
  }
  
}

//---------------------------------------------------------------------------------
// Process circular buffers being fed by the Interrupt function
//---------------------------------------------------------------------------------

void pswr_sync_from_interrupt(void)
{
  uint8_t in;

  noInterrupts();
  in = measure.incount;
  interrupts();

  while (measure.outcount != in)              // Read from circular buffer, while new input available
  {
    noInterrupts();
    fwd = measure.fwd[measure.outcount];      // Transfer data from circular buffers
    rev = measure.rev[measure.outcount];
    interrupts();
    measure.outcount++;                       // 8 bit value, rolls over at 256
 
    noInterrupts();                           // Perhaps a bit redundant - squeeze every last drop while we're at it
    in = measure.incount;
    interrupts();
  }
}
 
F
This must be added in WireIMXRT.cpp to get the I2C clock on 2 MHz

Code:
} else if (frequency < 1000000) {
    // 400 kHz
    port->MCCR0 = LPI2C_MCCR0_CLKHI(26) | LPI2C_MCCR0_CLKLO(28) |
      LPI2C_MCCR0_DATAVD(12) | LPI2C_MCCR0_SETHOLD(18);
    port->MCFGR1 = LPI2C_MCFGR1_PRESCALE(0);
    port->MCFGR2 = LPI2C_MCFGR2_FILTSDA(2) | LPI2C_MCFGR2_FILTSCL(2) |
      LPI2C_MCFGR2_BUSIDLE(3600); // idle timeout 150 us
    port->MCFGR3 = LPI2C_MCFGR3_PINLOW(CLOCK_STRETCH_TIMEOUT * 24 / 256 + 1);
  } else {
    // 2 MHz
                port->MCCR0 = LPI2C_MCCR0_CLKHI(3) | LPI2C_MCCR0_CLKLO(4) |
                LPI2C_MCCR0_DATAVD(2) | LPI2C_MCCR0_SETHOLD(3);
                port->MCFGR1 = LPI2C_MCFGR1_PRESCALE(0);
                port->MCFGR2 = LPI2C_MCFGR2_FILTSDA(1) | LPI2C_MCFGR2_FILTSCL(1) |
                LPI2C_MCFGR2_BUSIDLE(1200); // idle timeout 50 us
                port->MCFGR3 = LPI2C_MCFGR3_PINLOW(CLOCK_STRETCH_TIMEOUT * 24 / 256 + 1);
  }
 
Back
Top