Problems with I2C and AD7746

14 Jun 2012

Dear everyone ...

I´m trying to program an AD7746 Capacitive sensor via I2C. However, I´m having problems with the communication. The program that I´m loading is:

  1. include "mbed.h"

DigitalOut Led1_uP(LED1); DigitalOut Led2_uP(LED2); DigitalOut Led3_uP(LED3); DigitalOut Led4_uP(LED4);

I2C i2c(p28, p27); sda, scl Serial pc(USBTX, USBRX); tx, rx

Start Byte int AD7746_ADDR = 0x90; Datasheet: "The start byte address for the AD7745/AD7746 is 0x90 for a write and 0x91 for a read". So I suppose that the device address is 0x90, is this right??

Info Registers int REG_STATUS = 0x00; int REG_CAP_DATA = 0x01; int REG_VT_DATA = 0x04; int REG_CAP_SETUP = 0x07; int REG_VT_SETUP = 0x08; int REG_EXC_SETUP = 0x09; int REG_CONFIG = 0x0A; int REG_CAP_DAC_A = 0x0B; int REG_CAP_DAC_B = 0x0C; int REG_CAP_OFFSET = 0x0D; int REG_CAP_GAIN = 0x0F; int REG_CAP_V_GAIN = 0x11;

/*--------------*/ /*---- OTHER FUNCTIONS ----*/ /*------------*/

char getRegister(char deviceAddress, char registerAddress) {

char data[2];

data[0] = registerAddress; i2c.write(deviceAddress, data, 1); i2c.read(deviceAddress, data, 1);

return data[0];

}

void setRegister(char deviceAddress, char registerAddress, char value) {

char data[2];

data[0] = registerAddress; data[1] = value; i2c.write(deviceAddress, data, 2);

}

void Device_Config( void ) {

Led1_uP = 1; char reg;

setRegister(AD7746_ADDR, REG_CAP_SETUP, 0xA0); The register must be set 0b10100000 reg = getRegister(AD7746_ADDR, REG_CAP_SETUP); pc.printf("Register 0x%2d read: 0x%2d)\n", REG_CAP_SETUP, reg); wait(1);

Led1_uP = 0;

}

/*--------------*/ /*---- MAIN FUNCTION ----*/ /*------------*/

int main() {

Device_Config();

i2c.frequency(100000);

pc.baud(9600);

while(1) {} }

The code is quite intuitive but the result of this code is very strange because the hyperterminal shows the following message ... "Register 0x 7 read: 0x 7)".

I´d appreciate so much any help because I have tried to solve the problem several days ago.

Thanks in advance.

14 Jun 2012

Using code tags makes it more readable, which is now not really the case.

<<code>> 
your code here 
<</code>>

Anyway to start with a little thing, in your printf function you probably want to use 0x%02d, then it says "Register 0x07" instead of "Register 0x 7".

Your read function is a bit weird:

char getRegister(char deviceAddress, char registerAddress) {
 
char data[2];
 
data[0] = registerAddress; i2c.write(deviceAddress, data, 1); i2c.read(deviceAddress, data, 1);
 
return data[0];

You initialize an array with length 2, but you never do anything with it. The problem is however in your i2c.write part. By default the mbed i2c lib sends a stop condition after writing data, and from the datasheet:

Quote:

If a stop condition is ever encountered by the AD7745/AD7746, it returns to its idle condition and the address pointer is reset to Address 0x00.

Replace that line with:

i2c.write(deviceAddress, data, 1, true); 

The "true" in the end makes sure no stop condition is added at the end.

14 Jun 2012

Hi Erik ...

Thank you very much!! The system works properly now.

19 Jun 2012

I don´t want to make bigger the list of forums, so if the administrators allow me to follow with this link, I will do it. I´ve a little problem (maybe with syntax) reading data from a .txt file (in C:). The code is below:

#include "mbed.h"            

DigitalOut Led1_uP(LED1);
DigitalOut Led2_uP(LED2);
DigitalOut Led3_uP(LED3);
DigitalOut Led4_uP(LED4);

Serial pc(USBTX, USBRX);                        // tx, rx


/*------------------------------------------------------------------*/
/*------------------------ OTHER  FUNCTION -------------------------*/
/*------------------------------------------------------------------*/



void General_Config( void ) {
    
    int Off_Cap_A;
    int Off_Cap_B;
    
    Led1_uP = 1;
    
    FILE *Config = fopen("C:/SICE_Programs/CSV/Config.txt", "r");
    fscanf(Config,"%i %i", &Off_Cap_A, &Off_Cap_B);
    pc.printf("%i %i\n", Off_Cap_A, Off_Cap_B);

    Led1_uP = 0;

}


/*------------------------------------------------------------------*/
/*------------------------ MAIN  FUNCTION --------------------------*/
/*------------------------------------------------------------------*/


int main() {
    
    General_Config();
    
    pc.baud(9600);
    
    while(1) {}
} 

Could anyone tell me what´s wrong? Because I´ve been checking several webs and I cannot see the mistake!!

Thanks in advance.

PD: "Config.txt" is only two integer data "1 5".

19 Jun 2012

You cant read from the PC disksystem C. Instead read from a file stored on the local usb stick. See examples on

http://mbed.org/handbook/LocalFileSystem

Note that the cookbook page has a lib to read and parse config files.

19 Jun 2012

OK. That is a problem for me because this "Config.txt" file is dynamically changed by other program in Matlab (there is an interaction between both mbed and Matlab with this file). I´ll try to pass the data in another way.

Thank you very much Wim.

22 Jun 2012

Dear everyone ...

Taking into account the suggestions made in this forum as well as the comments read in other forums I´ve written this code. It drives basically the AD7746 (but unfortunately wrong). When I run the code I can see as the temperature is given well (the result agrees with the expected value). But the result of the capacitance is quite different with the expected one. In fact, placing a sample capacitance in one input and the other input without it, the results given for both ports are the same!! I mounted two different ICs and, in both cases, the results are the same as well. I cannot understand this. If the temperature is well given, I believe that the functions "getRegister" and "setRegister" are well written (well result means well configured). So, the problem could be in "Request_Cap_X" functions. In order to check this possibility I introduced the "C" choice in the "Main" function where basically I modify the capacitance offset, but obviously without any positive result (no change in the value and no change in the difference between both ports). Could anyone help me with the problem?

On the other hand, the people in some forums advises to reset the device before the use of the config register. I´m trying this by using the " i2c.write" line, but always generates an error message: "no instance of overloaded function "mbed::I2C::write" matches the argument list" in file "/CS_I2Cv1.cpp", Line: 77, Col: 34"". Does anyone know why the compiler is generating this error?

Thank you very much in advance!!

#include "mbed.h"            

DigitalOut Led1_uP(LED1);
DigitalOut Led2_uP(LED2);
DigitalOut Led3_uP(LED3);
DigitalOut Led4_uP(LED4);

I2C i2c(p28, p27);                              // sda, scl
Serial pc(USBTX, USBRX);                        // tx, rx

Timer t;

// Start Byte
int AD7746_ADDR    = 0x90;

// Info Registers
int REG_STATUS     = 0x00;
int REG_CAP_DATA   = 0x01;
int REG_VT_DATA    = 0x04;
int REG_CAP_SETUP  = 0x07;
int REG_VT_SETUP   = 0x08;
int REG_EXC_SETUP  = 0x09;
int REG_CONFIG     = 0x0A;
int REG_CAP_DAC_A  = 0x0B;
int REG_CAP_DAC_B  = 0x0C;
int REG_CAP_OFFSET = 0x0D;

// Special Registers
int RESET_ADDR     = 0xBF;



/*------------------------------------------------------------------*/
/*------------------------ OTHER  FUNCTION -------------------------*/
/*------------------------------------------------------------------*/


float getRegister(char deviceAddress, char registerAddress) {
    
    char data[4];
    float val;
    
    data[0] = registerAddress;
    i2c.write(deviceAddress, data, 1, true);
    i2c.read(deviceAddress, data, 3);               
    val = (data[0] << 16) + (data[1] << 8) + data[2];
    
    return val;}


void setRegister(char deviceAddress, char registerAddress, char value) {
  
    char data[2];
    data[0] = registerAddress;
    data[1] = value;
    i2c.write(deviceAddress, data, 2);}


void General_Config(void) {
    
    Led1_uP = 1;
    // i2c.write(AD7746_ADDR,RESET_ADDR,0);              // Reset only the device
    wait(0.1);
    setRegister(AD7746_ADDR, REG_VT_SETUP, 0x81);        // VT Setup
    wait(0.1);
    setRegister(AD7746_ADDR, REG_CONFIG, 0xA2);          // Config Setup
    wait(0.1);
    Led1_uP = 0;}


float Request_Cap_A(char Off_Cap_A){

    float C_A;
    
    Led2_uP = 1;  
    setRegister(AD7746_ADDR, REG_EXC_SETUP, 0x23);       // Exc_B Setup
    wait(0.1);
    setRegister(AD7746_ADDR, REG_CAP_SETUP, 0x80);       // Cap_A Setup
    wait(0.1);     
    setRegister(AD7746_ADDR, REG_CAP_DAC_A, Off_Cap_A);  // DAC_A Setup
    wait(0.1);       
    C_A = getRegister(AD7746_ADDR, REG_CAP_DATA);
    C_A = 4.88e-07 * C_A - 4.096;
    wait(0.1);
    Led2_uP = 0; 
    
    return C_A;}


float Request_Cap_B(char Off_Cap_B){

    float C_B;
    
    Led2_uP = 1;  
    setRegister(AD7746_ADDR, REG_EXC_SETUP, 0x0B);       // Exc_A Setup
    wait(0.1);
    setRegister(AD7746_ADDR, REG_CAP_SETUP, 0xC0);       // Cap_B Setup
    wait(0.1);           
    setRegister(AD7746_ADDR, REG_CAP_DAC_A, Off_Cap_B);  // DAC_A Setup
    wait(0.1); 
    C_B = getRegister(AD7746_ADDR, REG_CAP_DATA);
    C_B = 4.88e-07 * C_B - 4.096;
    wait(0.1);
    Led2_uP = 0; 
    
    return C_B;}


float Request_VT(void){
    
    float T;
    
    Led3_uP = 1;      
    T = getRegister(AD7746_ADDR, REG_VT_DATA);
    T = (T / 2048) - 4096;
    wait(0.1);
    Led3_uP = 0;  
    
    return T;}



/*------------------------------------------------------------------*/
/*------------------------ MAIN  FUNCTION --------------------------*/
/*------------------------------------------------------------------*/


int main() {

    int Off_Cap_A;
    int Off_Cap_B;
    
    Off_Cap_A = 0x80; 
    Off_Cap_B = 0x80;
    
    General_Config();
    
    i2c.frequency(100000);
    
    pc.baud(9600);
    
    while(1) {
        
        if (pc.readable()) {
        
                switch (pc.getc()) {
                
                            case 'C' :
                                Led4_uP = 1;
                                Off_Cap_A = 0x90; 
                                Off_Cap_B = 0x90;
                                wait(0.1);
                                Led4_uP = 0;
                                break;
                                
                            case 'M' :
                                Led4_uP = 1;
                                float C_A = Request_Cap_A(Off_Cap_A);
                                float C_B = Request_Cap_B(Off_Cap_B);
                                float T = Request_VT();
                                pc.printf("%f %f %f\n", C_A, C_B, T);
                                Led4_uP = 0;
                                break;}}}

}
22 Jun 2012

How do you use the i2c.write function to reset it? According to the datasheet you need to set the address pointer to 0xBF to reset the device, so it should simply be:

char rst_cmd = 0xBF;
i2c.write(deviceAddress, &rst_cmd, 1);
wait_us(200); //Time required to reset

the i2c.write function wants a pointer to the data you are going to send, maybe that went wrong?

On first glance while comparing with datasheet I dont see a problem with your code. Which data do you get back from the IC?(Probably easier to output the 24 bits in hexadecimal or something, without your calculations on it). That might give a hint what is going wrong.

22 Jun 2012

Hi Erik ...

You are right. Using your code the error doesn´t appear. But, if you define rst_cmd as "char" variable, should I change all the "int" variables (int REG_VT_DATA = 0x04 ...? Because all they have the same nature. And on the other hand... should I include "&" in all i2c.write/i2c.read instructions? For example ...

float getRegister(char deviceAddress, char registerAddress) {
    
    char data[4];
    float val;
    
    data[0] = &registerAddress;
    i2c.write(deviceAddress, data, 1, true);
    i2c.read(deviceAddress, data, 3);               
    val = (data[0] << 16) + (data[1] << 8) + data[2];
    
    return val;}
22 Jun 2012

I doubt it matters if you use int or char, it just converts it directly and will cut off the top 24 bits. I guess it is mainly a matter of personal preference if you define them ints or chars (the i2c functions even uses an integer for the device address, even though it can never be more than 8 bit, and it uses chars for the data.) I have done most of my programming on 8 bit uCs, so then you dont want to use integers when you dont need them, on a 32bit uC that is not a problem.

So you can also just use the reset variable you declared. The reason I declared a new one in that code was that I didnt see you already had one declared :)

About the "&", you are using arrays, an array is nothing else than a pointer. Your data is the equivalent of &data[0] (A pointer to the first element of the array). So there is no reason to change the original code, it is correct. In the code you made here you will send the location where the register address is stored in the mbed to the AD7746. It won't be able to do much with that ;)

22 Jun 2012

You may have a problem in getRegister with the calculation of the return value:

val = (data[0] << 16) + (data[1] << 8) + data[2];

Note that data[] is defined as char. The shift operation will probably result in a 0 since the type is still a char. Try something like this:

val = ((int) data[0] << 16) + ( (int) data[1] << 8) + (int) data[2];
25 Jun 2012

Hi Erik & Wim ...

Thanks for your suggestions. I´ve been working this weekend in all this issue and finally I´ve a solution; maybe it´s not the best one but, anyway it´s works. I reset the device before each "Request" function. In my opinion, it´s like the device "doesn´t close anything". I try to explain this (if any mbed user has my problem will be able to solve it). The probes were the following:

1.- If we don´t reset the device before each "Request" function, we use a sample capacitance in one port (and empty the other) and we type "M"... the first result got in the hyperterminal is "4.095 4.095 27.34". If we don´t reset the device before each "Request" function, we use a sample capacitance in one port (and empty the other) and we type "M" again... the second result got in the hyperterminal is "4.095 4.095 27.34" which is basically the same!! Remember, the IC can measure capacitances between [-4.095,4.095] pF.

2.- If we reset the device only before "Request_VT", we use a sample capacitance in one port (and empty the other) and we type "M"... the first result got in the hyperterminal is "4.095 4.095 26.62". If we reset the device only before "Request_VT", we use a sample capacitance in one port (and empty the other) and we type "M" again... the second result got in the hyperterminal is "4.095 4.095 27.12" This updates only the temperature value. The temperature works fine but the other to "Request" functions don´t look like.

3.- If we reset the device before all "Request" functions, we use a sample capacitance in one port (and empty the other) and we type "M"... the first result got in the hyperterminal is "4.095 0.252 27.73". If we reset the device before all "Request" functions, we place the sample capacitance in the other port (leaving the other empty) and we type "M"... the second result got in the hyperterminal is "0.275 4.095 27.78". If we reset the device before all "Request" functions, we don´t use a sample capacitance in any port and we type "M" again... the third result got in the hyperterminal is "0.275 0.252 27.86".

Obviously this confirms the proper system operation if we reset the device each time because the update in each situation is correct. Basically, the result is valid because the sample capacitance is saturating the converter.

The question behind, and which could explain the phenomena, is why the system works fine with reset and works wrong without it. I suppose the reason is something like Erik helped me before in my first post. Something should leave free the I2C line or something like that. This conclusion is my opinion. What do you think about?

25 Jun 2012

Where exactly do you use the reset instruction and does it also include a new call to General_Config(void).

Note that a reset changes the values of the registers that you set in General_Config. That may be the cause of the problem:

VT SET-UP REGISTER Default Value == 0x00. You change it into 0x81.

CONFIGURATION REGISTER Default Value == 0xA0. You change it into 0xA2.

CONFIGURATION REGISTER is set to single conversion rather than continuous. Perhaps single conversion means you can read only one new value? Datasheet is not clear. Maybe you need to rewrite the CONFIGURATION REGISTER with 0xA2 before you read.

Try CONFIGURATION REGISTER = 0xA1 (continuous mode) and without reset.

You could also test the statusbit to see if conversion is ready.