This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Help with SPI code for ADS1231

Other Parts Discussed in Thread: ADS1231, MSP430BT5190, ADS1231REF

Hi guys,

I'm working on my school project and I was wondering if I can get some help with my SPI code for the ADS1231? Im using the MSP430BT5190 microcontroller(the one included in EZ430 RF2560). The way the ADS1231 works is that it will pull its DRDY line low when it is ready to start shifting out data and it is at this time the serial clock is started. When 24 bits are shifted out, the DRDY goes back to high and, from my understanding, the Serial Clock stops. I have a diagram of how I plan on connecting the pins on my microcontroller to the ADS below as well. I plan on connecting both p3.0 and p3.2 to the DRDY/DATAOUT and the P3.3 to the SCLK. P3.0 will handle the DRDY signal and P3.2 will handle the SOMI. Anyway, I was wondering about a few details.

With the code that I have below, am I correctly stopping and starting the Serial clock? The Serial clocks should only be applied when DRDY is low so I  did this by stating if(!P3IN). It will turn on again when DRDY is high so if(P3IN). I stopped and started the SCLK by doing:  UCB0CTL1 &= ~UCSWRST; 

 

Secondly, isn't there some conversion that I need to do so that the data that I'm getting is standardized? I remember dividing by the Vref...but I can' t seem to find this in the ADS1231 datasheet anywhere.

 

Appreciate any help.

 

Wesley

 

//                         MSP430BT5190
//    -----------------
//   /|\|              XIN|-
//       ADS1231      | |                 |
//    -------------    --|RST          XOUT|-
//   | DRDY/DATAOUT|--->|P3.0                         |
//   | DRDY/DATAOUT|--->|P3.2/UCB0SOMI    |
//     ~>|AIN+  I/O CLK|<---|P3.3/UCB0CLK     |
//   |                     |    |             P1.0|--> LED

void main(void)
{
 unsigned int data1, data2, data3;
 boolean dataComplete = false;

 WDTCTL = WDTPW + WDTHOLD;                 // Stop watchdog timer
 P1DIR |= 0x01;                         

   // P1.0 output
 P3SEL |= 0x0C;                            // P3.2,3 USCI_B0 option select
 P3DIR |= 0x00;                            // P3.0 input direction, this will be connected to the DRDY/DATAOUT pin, the purpose of this pin is just to check when DRDY is low
                                                  //the DRDY/DOUT pin is still connected to 3.2 which is the SOMI pin
 UCB0CTL0 |= UCMSB + UCMST + UCSYNC;       // 3-pin, 8-bit SPI mstr, MSB 1st
 UCB0CTL1 |= UCSSEL_2;                     // SMCLK
 UCB0BR0 = 0x02;
 UCB0BR1 = 0;
 UCB0CTL1 &= ~UCSWRST;                     // *Initialize USCI state machine*

/* The msp430 should only go in this loop everytime DRDY is low. This means that it needs to get the 24 bits of data and then DRDY should be high again. It will stay high until
the ADS is ready to send more data out.*/
 while(1)
 {
   if(P3IN && dataComplete){
     UCB0CTL1 &= ~UCSWRST;             //stop the serial clock
     dataComplete = false;
   }
  
   if(!P3IN){
   UCB0CTL1 &= ~UCSWRST;
   UCB0TXBUF = 0x00;                       // Dummy write to start SPI
   UCB0TXBUF = 0x00;
   while (!(IFG2 & UCB0RXIFG));            // USCI_B0 TX buffer ready?
   data1 = UCB0RXBUF;                      // R15 = 00|MSB
   data1 = data1 << 8;
   while (!(IFG2 & UCB0RXIFG));            // USCI_B0 TX buffer ready?
   data2 = UCB0RXBUF;
   data2=data2<<8;
   while (!(IFG2 & UCB0RXIFG));            // USCI_B0 TX buffer ready?
   data3 = UCB0RXBUF;
   data1 = data1 + data2+data3;                  // R14 = 00|LSB

   P1OUT &= ~0x01;                         // P1.0 = 0

   if (data1 > 0x7FE0)                     // Test for correct character RX'd
     P1OUT |= 0x01;                        // P1.0 = 1
  
   dataComplete = true;

 }

 }
}
  • Hi Wesley,

    I'm going to answer your second question first. One page 3 of the datasheet:

    Which shows that the input ranges +/- within the common-mode input range.  The codes read  back are binary twos complement and follow the table 3.

    So, now on to your code.  The basic structure is ok for the most part.  One thing though is that the SCLK works in bytes, so you really don't need the datacomplete stuff.  Just read three consecutive bytes, then wait for DRDY to go low again. Data1 should be shifted 16 instead of 8 and should be signed 32...or you need to account for the sign in some way.  If you use a signed 32 , you will need to make sure that you properly sign extend from 3 bytes to 4 bytes.  The end result can be changed from a code to a voltage using the LSB size  of one code multiplied by the result.

    Once you think you have things working, you really need to verify that what you think is happening truly is and you do this by hooking things up to an o-scope.  Measure the results from the scope and compare it to the readings you get from the micro.

    Best regards,

    Bob B

  • Thanks Bob for the fast answer! But I have a few questions about the code. I see what you're saying about shifting it by 16(otherwise data1 will get erased by data2) and declaring it as a signed int(since it is in binary 2's). I went ahead and updated by code based on your advice (shifted data1 by 16, changed data1 to a long int, and added the part about sign extension). However, I'm not sure why I need to sign extend it to 4 bytes? Is it just to fill in the 24-32nd bits?

    Also, Im still unsure about the SCLK. I thought I was suppose to stop the SCLK based on Figure 19 in the ads1231 datasheet? What I got from the datasheet is that I was suppose to apply the 25th SCLK and then stop the SCLK from running. So what you're saying it that I can leave the SCLK running even when DRDY is high?

     

    Anyway, my team is hoping to get the headers and expander kit hooked up so that we can start testing it later this week. I'm just trying to get rid of as many errors before testing as I can! Appreciate any help

    void main(void)
    {
     unsigned int data2, data3, signExtension;
     long int data1;
     int sign;
     

     WDTCTL = WDTPW + WDTHOLD;                 // Stop watchdog timer
     P1DIR |= 0x01;                        
       // P1.0 output
     P3SEL |= 0x0C;                            // P3.2,3 USCI_B0 option select
     P3DIR |= 0x00;                            // P3.0 input direction, this will be connected to the DRDY/DATAOUT pin, the purpose of this pin is just to check when DRDY is low
                                                      //the DRDY/DOUT pin is still connected to 3.2 which is the SOMI pin
     UCB0CTL0 |= UCMSB + UCMST + UCSYNC;       // 3-pin, 8-bit SPI mstr, MSB 1st
     UCB0CTL1 |= UCSSEL_2;                     // SMCLK
     UCB0BR0 = 0x02;
     UCB0BR1 = 0;
                     

    /* The msp430 should only go in this loop everytime DRDY is low. This means that it needs to get the 24 bits of data and then DRDY should be high again. It will stay high until
    the ADS is ready to send more data out.*/
     while(1)
     {
     
       if(!P3IN){
       UCB0CTL1 &= ~UCSWRST;         // *Initialize USCI state machine*
       UCB0TXBUF = 0x00;                       // Dummy write to start SPI
       UCB0TXBUF = 0x00;
       while (!(IFG2 & UCB0RXIFG));            // USCI_B0 TX buffer ready?
       data1 = UCB0RXBUF;                      // R15 = 00|MSB
       sign = data1 >>7;
       if(sign==1){
          signExtension=11111111;
       }
       else
       {
         signExtension=00000000;
       }
       signExtension << 24;
       data1 = data1 << 16;
       while (!(IFG2 & UCB0RXIFG));            // USCI_B0 TX buffer ready?
       data2 = UCB0RXBUF;
       data2=data2<<8;
       while (!(IFG2 & UCB0RXIFG));            // USCI_B0 TX buffer ready?
       data3 = UCB0RXBUF;
       data1 = data1 + data2 + data3 + signExtension;                  // R14 = 00|LSB

       P1OUT &= ~0x01;                         // P1.0 = 0

       if (data1 > 0x7FE0)                     // Test for correct character RX'd
         P1OUT |= 0x01;                        // P1.0 = 1
     

     }

     }
    }

  • Wesley,

    On our EVM, ADS1231REF, I use an interrupt driven system and not a polling method, so I guess I wasn't quite thinking in the same terms.  I parallel the DOUT/DRDY signal to another  pin and I set the interrupt to trigger on the high to low transition.  Within the interrupt, I first disable the interrupt, then read 3 bytes and then re-enable the interrupt.  I always know when the read is taking place.  Using the polling method at a slow sampling rate works, but you never really know for sure when the read is taking place.  It is quite possible to get corrupted data if it is not read out fast enough.

    So yes, you really should force the DRDY high if you poll.  If you use the SPI peripheral it will clock in groups of 8 clocks representing one byte, to get the 25th clock you would basically need to read a dummy byte.  This will result in a total of 32 clocks.  The part has not been tested, to my knowledge, driving it with extended clocks.  I will have to test this when I can, and if I have a problem with this I will let you know.  Otherwise you will have to bit-bang the SPI and that is a hand coded process and you will not be able to use the built in USCI peripheral.

    As far as the sign-extension, I've never heard of a signed 24-bit number.  You can have three bytes of data, but to manipulate it as a number it needs to be a signed 32 bit value (signed long.)

    Best regards,

    Bob B

  • Oh ok, my sample rate is low (SPR=10 sps) so i guess i was doing the polling method with my original code. I was trying to stop the SCLK's after the 25th one. That's what the dataComplete was for. Here is how my code works:
     while(1)
     {
       if(P3IN && dataComplete){                    //the code should go into this for loop at the 25th SCLK and then stop the  SCLK
         UCB0CTL1 &= ~UCSWRST;             //stop the serial clock
         dataComplete = false;
       }
      
       if(!P3IN){

    UCB0CTL1 &= ~UCSWRST;       //start the serial clock 

    //Code to get the 24 bits of data from ADC and sign extend as well
      
       dataComplete = true;                //makes sure that it only stops the SCLK once by only going inside the if statement once

     }

    I intend to only go into if(P3IN && dataComplete) statement only once, that is right after the the msp430 gets the 24 bits of data. Hopefully, by that time the 25th SCLK has been applied. Then when P3IN goes low again, I go into if(!P3IN) and start the SCLK up by saying UCB0CTL1 &= ~UCSWRST;   . The only thing I'm worried about is that by resetting the UCWRST I'm resetting more parameters than I need to and, thus, my SPI will somehow mess up.

  • Wesley,

    There is no need to start and stop the SCLK.  Start the SCLK as part of the peripheral.  When you poll the input and it goes low, just read 4 bytes.  The first three bytes read is your ADC value.  The fourth sets SCLK high.  It is really just that simple.  There is absolutely no need for datacomplete.

    Best regards,

    Bob B

  • Hi Bob,

    Is it possible to attached for the interrupt driven method? Do you use custom interrupt method with gpio or do you follow the spi protocol ?

    Best regards,

    Theo

  • Hi Theo,

    Welcome to the forum! The code used for the reference design (ADS1231REF) can be found on the public ftp server:

    ftp://ftp.ti.com/pub/data_acquistion/ADS1x31REF/Firmware/Firmware_Source_Code/ads1x31ref_fwsrc_1.0.0.zip

    The file you will be interested in is 'ads1231.c' and we use a GPIO interrupt falling edge to trigger the read of results.

    Best regards,

    Bob B

  • Hi Bob,

    I followed your instructions as well as the logic of the code for the ADS1x31REF and I managed to output raw data to the UART.

    Now I'm trying to convert those data in a viewable (Kgr) string format.I have stored the ADC data to the signed 32 variable (as shown in the code). Which function should I use know ? 

    Best regards,

    Theo

  • Hi Bob,

    Recently, I used ADS1231 in my project too. And now I cannot get the zip file from the ftp.

    Can you sent it to me? My email is tclwl@163.com

    Best regards;

    tclwl L

  • Hi Bob,
    Regarding the raw data output from the ADS1231 I store those as unsigned 8 bit char and by using the statement uint8_t *cptr=(uint8_t *)(&_code); I end up with cptr[2],cptr[1],cptr[0].   _code is a signed long.
    Question1. What should I expect to read from the cptr[2],cptr[1],cptr[0] ?
    Question2. Can I print the value of _code * Vref /16777216 as a float and have a good result?
    Question3. What is the proposed SCLK frequency ?
    
    
    Best regards,
    Theo
    
    
    PS: I use LPC1115 MCU from NXP.
  • Hi Theo,

    In the code you are discussing, _code is a signed long as you mentioned which is 32 bits.  This makes up 4 bytes in memory.  The cptr array is a pointer to the 4 bytes that make up _code.  The way this code is works is by placing the data read from the ADS1231 directly into the locations that make up the 32 bit signed value.  When reading in the data you need to make sure that the 24 bit value from the ADS1231 has been signed extended properly for negative codes.  So the answer to question 1 is you would read from _code not cptr, and you would read a signed 32 bit value.

    For question 2, you will get the result relative to the precision of the floating point value. You can get a good result.

    Question 3, the maximum SCLK frequency is 5MHz.  You can go slower, but basically you need to go at least fast enough to read all the conversion result before the next conversion completes.  This is dependent on which data rate you will use.

    Best regards,

    Bob B

  • Hi Bob,

    Sorry to disturb you again. As you said there is a file named 'adc.c' in the zip file. But when I unzip the file I can't find the adc.c file in it. I really want to know why and how can I get the adc.c file.

    Best regards,

    Loki Liang

  • Hi Loki,

    I corrected my earlier post.  For this project the file name is actually 'ads1231.c'.

    Best regards,

    Bob B

  • Hi Bob,

    Thank you so much!

    Best regards,

    Loki Liang

  • Hi Bob,

    Could you please email me the code for 1231REF.  Seems  like the ftp is down...

    my email : gsmsby@yahoo.com

    Thank you

  • Hi Siauhwa,

    The code is on the way.

    Best regards,

    Bob B

  • Hi Bob,

    Thanks for the code.

    By the way, is there any way to get the the output of the 128x gain amplifier ?

    Seems like the output of 1231REF is the output of load cell ...?

    But I need the output of the 128x amplifier

    thank you

  • Hi Siauhwa,

    I'm not clear about your question.  The ADS1231 has a fixed gain of 128.  As long as the input is within the correct common mode range you should be able to get the correct code.  With a 5V reference and AVDD at 5V, the full scale range is +/- 19.5mV.  The actual input must reside between AVDD-1.5V and GND+1.5V.

    A balanced bridge circuit will be 1/2 the excitation for common mode, and with 5V excitation a 2mV/V load cell will have a full range of 10mV.

    What type of sensor are you using?  Why do you think you are not getting a gain of 128?

    Best regards,

    Bob B

  • Hi Bob,

    I use a custom load cell, made of 4 strain gauges that form a bridge.

    with 5 volts excitation,  the output of the bridge is 0.069 mV (and the 1231ref shows 75 uV)  when  no load applied to the load cell.

    I haven't looked at your code, but I get the impression that the output on th elabview software is basically the output of my bridge (load cell). 

    But, it could be just because your code has divided the output of the 128x amplifier by 128 to give the original input , instead of the voltage of the amplifier ...?

    Thanks

  • Hi Siauhwa,

    The output code is the output of the ADS1231 based on the reference voltage used.  If you have a load cell connected, then yes this is the measurement of the load cell.  Within your microcode you will have to account for any gain/offset error.  The code for the ADS1231REF scale mode uses a two point calibration.  First point is a no load condition, and the second point is with using a calibrated weight.  If you follow the user's guide for scale operation you should be able to see how the offset code is subtracted from the measurement.  From the datasheet the typical offset is 10uV.  If the output of the bridge is 69uV, then you are well within the expected range.

    One of my colleagues suggested to me that you want to measure the output of the PGA directly.  If you look at figure 13 of the datasheet the CAP pins are post gain of 128.  Measurement across this CAP may give you some indication, but may not tell you the whole story.  Adding a meter at this point may hinder the reading by adding an additional current path of the meter itself.

    One other thing I should mention is the GUI software is only meant for analysis mode and will not work for the scale mode. When you switch from analysis mode to scale mode, a different set of code structures are used for device operation. 

    Best regards,
    Bob B

  • Hi Bob,

    I just check  the datasheet - table 3 :

    (+0.5VREF/128)/(223 – 1)  = 000001h

    so, in this case the output of the ADC is the original input, not the output of the amplifier.

    Thank you so much for your help !

    and have a good week end :)

  • Hi Bob,

    I have a problem with /DRDY on ADS1231.  When VREFP is  connected to either 3.3 or 5V then /DRDY stays at 0 or High level.  But, when I disconnect VREFP (leave it Open), I can see the /DRDY pulses.

    Following is my configuration :

    clkin =0 (internal Osc)

    speed = 0, (10Hz)

    SCLK = 0;

    /PWDN = 0 and then after around 200us /PWDN = 1 to form a reset sequence.

    Is there anything that I missed ...?

    thank you

    Siau Hwa

  • Hello Bob,


    As the server's link seems to be down, can you please e-mail the code for me as well (at address roy@gen-x-tech.com)?

    Thank you,

    Roy

  • The link provided doesn't seem to be currently available. I am looking for the firmware for the 'ADS1231REF'. Where may i find it?