|
|
View previous topic :: View next topic |
Author |
Message |
carl
Joined: 06 Feb 2008 Posts: 240 Location: Chester
|
Help with LTC2480/81 (16-bit ADC) |
Posted: Tue Jun 28, 2011 3:14 am |
|
|
Hi All,
Has anyone used a LTC2480 before or have opinions on its use:
http://www.linear.com/product/LTC2480
The project is a wireless strain gauge. I have already done the wireless part.
I have looked for a driver/code and found a little bit, but as I am near the start of a project - would love some input from you also.
The 80 is a 4-wire SPI, and the 81 is a i2c. What are the advantages of using each - I cannot use an external clock for the pic in my application - so am I correct in saying i2c is ruled out?
The LTC2480 has an internal oscillator - i think this can be used as the clk signal when connecting between LTC2480 (clk pin) and PIC - is this correct? In other words the slave is driver the master?
Thanks in advance for any help
Carl |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Jun 28, 2011 2:57 pm |
|
|
Quote: |
The 80 is a 4-wire SPI, and the 81 is a i2c. What are the advantages of using each ?
|
See ckielstra's explanation here:
http://www.ccsinfo.com/forum/viewtopic.php?t=21489
Quote: |
I cannot use an external clock for the pic in my application - so am I
correct in saying i2c is ruled out?
|
Don't know what you mean by this. i2c always has two lines, SCL and
SDA. One is clock and the other is data. SPI also has two lines for
SCLK and SDO, plus another data line, SDI, plus a Chip Select line.
The data sheets for the LTC2480 and LTC2481 both have sample CCS
code in the back of the data sheet. Though, the code for the LTC2481
has a minor bug. They don't have the 0 parameter on the last i2c read.
I have edited their code to add it below:
Quote: |
i2c_write(addr | READ);
adc_code.by.te3 = i2c_read();
adc_code.by.te2 = i2c_read();
adc_code.by.te1 = i2c_read(0); // Last i2c read must have 0 parameter
adc_code.by.te0 = 0;
i2c_stop() |
The 0 parameter does a "NACK", which is required by the i2c specification
for the last read operation. |
|
|
carl
Joined: 06 Feb 2008 Posts: 240 Location: Chester
|
|
Posted: Wed Jun 29, 2011 4:58 am |
|
|
Thanks for your quick response.
For previous projects, I have always used an external crystal - up to 40MHz for example. But for this project I don't think I can use any external crystals to derive the MPU/4 clock.
The desired PIC is 16F628A.
Am I going to have problems communicating between these two devices without the external Clock. I understand that the ADC has a an internal oscillator - can't this be used to drive the PIC when communicating between the two devices?
Best Regards
Carl |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
|
carl
Joined: 06 Feb 2008 Posts: 240 Location: Chester
|
|
Posted: Fri Aug 05, 2011 3:43 am |
|
|
Hi There,
Sorry to trouble you, but if possible I need your expert help on this ADC program. Compiler Version: 4.038
I have decided to use the LTC2480 with a 'software spi' program - which basically means I can use any pins - which is neccasary because I have already done the PCB.
The code below has been pieced together from the LTC2480 and LTC2481 datasheets below and then adapted to use software spi code.
http://www.linear.com/product/ltc2481
http://www.linear.com/product/LTC2480
The code compiles correctly and the output is displayed on the standard 2x16 LCD. But there is obviously something wrong with my code - because the output is always '0' - it does not seem to read the ADC.
I have it wired as below:
LTC2480 ¦ PIC16F628A
PIN1 = SDI PIN20 = A1
PIN2 = VCC = 5V
PIN3 = VREF = 5V
PIN4 = IN+ = 5V
PIN5 = IN- = 0V
PIN6 = 'CS PIN17 = A6
PIN7 = SDO PIN18 = A7
PIN8 = GND
PIN9 = SCK PIN19 = A0
PIN10 = FO = 0V
the code is here:
Code: |
#include <16F628A.h> // Device
#include <flex_lcd.c> // Rohs Flex LCD
#fuses INTRC_IO, NOWDT, NOPROTECT, NOPUT, NOBROWNOUT, NOLVP, DEBUG,
#use delay(clock=4000000)
#use standard_io(a)
#use standard_io(b)
#use spi(MASTER, MODE=0, CLK=PIN_A0, DO=PIN_A7, DI=PIN_A1, BITS=8)
#define SPI_MODE_0 (SPI_L_TO_H | SPI_XMIT_L_TO_H)
#define SPI_MODE_1 (SPI_L_TO_H)
#define SPI_MODE_2 (SPI_H_TO_L)
#define SPI_MODE_3 (SPI_H_TO_L | SPI_XMIT_L_TO_H)
#define CS PIN_A6 // Chip Select
// char config = 0b11110000; // External Input, Gain = 256, Autocalibration, Simultaneous 50Hz/60Hz Rejection, Slow Speed
char config = 0b10000000; // External Input, Gain = 1, Autocalibration, Simultaneous 50Hz/60Hz Rejection, Slow Speed
void initialize(void){ // General initialization stuff.
output_low(LCD_RW); // Set R/W pin on LCD to a low level
lcd_init(); // Initialize LCD
lcd_putc("\fHello\n"); // Obligatory hello message
delay_ms(5000); // for half a second
} // End of initialize()
struct fourbytes { // Define structure of four consecutive bytes
int8 te0; // To allow byte access to a 32 bit int or float.
int8 te1; // The make32() function in this compiler will
int8 te2; // also work, but a union of 4 bytes and a 32 bit int
int8 te3; // is probably more portable.
};
//*******************************************************************************/
long int read_LTC2480(){
union { // adc_code.bits32 all 32 bits
// adc_code.by.te0 byte 0
long int bits32; // adc_code.by.te1 byte 1
struct fourbytes by; // adc_code.by.te2 byte 2
} adc_code; // adc_code.by.te3 byte 3
output_low(CS); // Enable LTC2480 SPI interface
while(input(PIN_A7)) {} // Wait for end of conversion. The longest
// you will ever wait is one whole conversion period
// Now is the time to switch any multiplexers because the conversion is finished
// and you have the whole data output time for things to settle.
adc_code.by.te3 = 0; // Set upper byte to zero.
adc_code.by.te2 = spi_xfer(config); // Read first byte, send config byte
adc_code.by.te1 = spi_xfer(0); // Read 2nd byte, send speed bit
adc_code.by.te0 = spi_xfer(0); // Read 3rd byte. ‘0’ argument is necessary
// to act as SPI master!! (compiler
// and processor specific.)
output_high(CS); // Disable LTC2480 SPI interface
adc_code.by.te0 = adc_code.by.te0 & 0xF0; // Clear configuration bits and subtract offset. This results in
adc_code.bits32 = adc_code.bits32 - 0x00200000; // a 2’s complement 32 bit integer with the LTC2480’s MSB in the 2^20 position
return adc_code.bits32;
} // End of read_LTC2480()
//*******************************************************************************/
void main(){
long int x; // Integer result from LTC2480
float voltage; // Variable for floating point math
int timeout;
initialize(); // Hardware initialization
while(1){
delay_ms(1); // Pace the main loop to something more than 1 ms
// This is a basic error detection scheme. The LTC2485 will never take more than
// 163.5ms, 149.9ms, or 136.5ms to complete a conversion in the 50Hz, 55Hz, and 60Hz
// rejection modes, respectively.
// If read_LTC2485() does not return non-zero within this time period, something
// is wrong.
x = read_LTC2480(); // Read ADC
printf(lcd_putc, "\f %Lu\n" x);
delay_ms(500);
if(x!=0)
{ // No timeout, everything is okay
timeout = 0; // reset timer
voltage = (float) x; // convert to float
voltage = voltage * 5.0 / 2147483648.0; // Multiply by Vref, divide by 2^31
lcd_putc("\f\n"); // Obligatory hello message
delay_ms(500); // Clear screen
lcd_gotoxy(1,1); // Goto home position
printf(lcd_putc, "\f %01.6f\n " voltage); // Display voltage
delay_ms(500);
}
else{
++timeout;}
if(timeout > 200){
timeout = 200; // Prevent rollover
lcd_gotoxy(1,1);
lcd_putc("\Error - Timeout\n");
delay_ms(500);
}
} // End of main loop
} // End of main
THanks in advance for any help |
|
|
|
RF_Developer
Joined: 07 Feb 2011 Posts: 839
|
|
Posted: Fri Aug 05, 2011 5:01 am |
|
|
carl wrote: |
http://www.linear.com/product/LTC2480
The code compiles correctly and the output is displayed on the standard 2x16 LCD. But there is obviously something wrong with my code - because the output is always '0' - it does not seem to read the ADC.
I have it wired...
|
I'm assuming for the moment your wiring is OK. Where I'd concentrate my efforts is in the reading routine. I do a lot of these sort of things, and yes, data formats from ADCs can be very confusing and cause lots of errors, especially to the unwary and inexperienced.
Quote: |
Code: |
struct fourbytes { // Define structure of four consecutive bytes
int8 te0; // To allow byte access to a 32 bit int or float.
int8 te1; // The make32() function in this compiler will
int8 te2; // also work, but a union of 4 bytes and a 32 bit int
int8 te3; // is probably more portable.
};
//*******************************************************************************/
long int read_LTC2480(){
union { // adc_code.bits32 all 32 bits
// adc_code.by.te0 byte 0
long int bits32; // adc_code.by.te1 byte 1
struct fourbytes by; // adc_code.by.te2 byte 2
} adc_code; // adc_code.by.te3 byte 3
output_low(CS); // Enable LTC2480 SPI interface
while(input(PIN_A7)) {} // Wait for end of conversion. The longest
// you will ever wait is one whole conversion period
// Now is the time to switch any multiplexers because the conversion is finished
// and you have the whole data output time for things to settle.
adc_code.by.te3 = 0; // Set upper byte to zero.
adc_code.by.te2 = spi_xfer(config); // Read first byte, send config byte
adc_code.by.te1 = spi_xfer(0); // Read 2nd byte, send speed bit
adc_code.by.te0 = spi_xfer(0); // Read 3rd byte. ‘0’ argument is necessary
// to act as SPI master!! (compiler
// and processor specific.)
output_high(CS); // Disable LTC2480 SPI interface
adc_code.by.te0 = adc_code.by.te0 & 0xF0; // Clear configuration bits and subtract offset. This results in
adc_code.bits32 = adc_code.bits32 - 0x00200000; // a 2’s complement 32 bit integer with the LTC2480’s MSB in the 2^20 position
return adc_code.bits32;
} // End of read_LTC2480()
|
|
The things I'm worried about here include endianness, and hence the use of the union, and the code conversion at the end. You also don't set a SPI bit rate. The LTC2480 will quite happily run as fast as your PIC so there shouldn't be any problem there. Of course you need to set the SPI baudrate to that accepted by the slowest device on the SPI "bus".
I'm also assuming you've got the SPI mode right. If not your bit alignment could be out by one bit, messing everything up.
I'd much prefer to use the compiler to sort out the byte ordering for me. I'd read in the three data bytes separately and then use make32() to put them back together. Your byte ordering may well be correct, but then again... Unions are often troublesome, as issues such as alignment and padding come into play. For example four bytes can be stored starting at any address, a 32 bit int probably can only be stored starting at a four byte boundary. It *may* be that your byte structure and long int don't line up as you expect. It may even change from one compilation to another as you edit/add to your code.
Finally I'm not at all sure your final code conversion is correct. From my reading of the LTC2480 data sheet, the result is straightforward 2s compliment 16 bit. All you need to do is align it. As a consequence I'd make the result of your routine signed int16. The SGN bit, seems to be primarily intended for overflow indication and is not really a sign bit, at least not in the numerical sense.
To keep my mind straight I like to do these this sort of conversions in simple steps:
Get the raw data - read the bytes and convert them back to a understandable form, in this case an int32.
Deal with status information - look for overflow and other error states
Extract and convert (If required) the data.
Here;s the sort of code I'd use. Note I haven't tested this, or even compiled it.
Code: |
signed int16 read_LTC2480()
{
int32 adc_code; // Raw adc data
int8 te0, te1, te2;
output_low(CS); // Enable LTC2480 SPI interface
while(input(PIN_A7)) {} // Wait for end of conversion. The longest
// you will ever wait is one whole conversion period
// Now is the time to switch any multiplexers because the conversion is finished
// and you have the whole data output time for things to settle.
te2 = spi_xfer(config); // Read first byte, send config byte
te1 = spi_xfer(0); // Read 2nd byte, send speed bit
te0 = spi_xfer(0); // Read 3rd byte. ‘0’ argument is necessary
// to act as SPI master!! (compiler
// and processor specific.)
output_high(CS); // Disable LTC2480 SPI interfac
adc_code = make32(0, te2, te1, te0); // Reassemble the full code word.
// adc_code = ((int32)te2 << 16) | ((int32)te1 << 8) | te0; // Alternative version.
// Deal with errors, e.g. overflow, in here by interpreting status bits.
return (int16)((adc_code >> 4) & 0xFFFF); // Return 16 bit data portion of adc code.
} // End of read_LTC2480()
|
From there on your arithmetic should work as normal.
There again none of this may solve the problem. Are you certain your hardware is working? Have you got an oscilloscope out and looked at the SPI signals?
RF Developer. |
|
|
carl
Joined: 06 Feb 2008 Posts: 240 Location: Chester
|
|
Posted: Fri Aug 05, 2011 11:23 am |
|
|
Hi RF_Developer,
Thank you so much for your very informative answer.
I will absorb everything you have said and fully reply ASAP - I haven't got access to this work until Monday now but will read later what you have said.
I'm quite sure the hardware is correct, and the configurations - but even if they were not - I would still get something other than '0'.
I did look at all four lines with the scope - and the 'CS and SDO lines were the only lines that were active - and only went high and low over a certain period.
The SDI and CLK lines were constantly low.
Quote: | Finally I'm not at all sure your final code conversion is correct. From my reading of the LTC2480 data sheet, the result is straightforward 2s compliment 16 bit. All you need to do is align it. As a consequence I'd make the result of your routine signed int16. The SGN bit, seems to be primarily intended for overflow indication and is not really a sign bit, at least not in the numerical sense. |
In regards to the above point - the first four lsb bits have to be cleared first - and the result/position of the resulting 16bit starts from bit5. The code was taken off the datasheet and I have looked over it - I think it is OK?
I will get back to you soon - thanks again
Carl |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Fri Aug 05, 2011 12:28 pm |
|
|
a different question??
why are you so wedded to this particular chip??
the ADS8326 has the same accuracy, diff inputs, and very low power
and is so simple to use. whats the allure of this part?
here is all the driver needed for that tiny converter
Code: |
#define ADCclock PIN_E2
#define ADCstart PIN_E0
#define ADCdata PIN_E1
// ADC_result unsigned int16 holds converter count
//
// NOTE: hardware - the ADCdata pin should have a 10k weak pull up to +5V
//
unsigned int16 adcresult=0;
// ADS 8326 is unipolar - the ADS8317 is +/- 15 bits
// there are many other members of this fine family
//
void AdcResult(void){
unsigned int8 i;
ADC_result=0;
// Start a conversion with a lo pulse on ADC_CONVERT
output_low (ADCstart); // LOWER NOT-CS
// NOW CYCLE TILL WE GET THE ACTIVE FIRST BIT ZERO READY
// ADCclock is clock DEFLT LOW
output_LOW(ADCclock); // clock low
delay_cycles(4);
OUTPUT_LOW (ADCstart); // START CONVERT not CS line
delay_cycles(4);
for ( i = 0 ; i < 8 ; i++ ){
output_HIGH(ADCclock); delay_cycles(4);
IF ( !input(ADCdata)) BREAK;
ELSE output_LOW(ADCclock);
delay_cycles(4);
}
IF (8==i) BADDIE(51); // GLOBAL TIMED OUT WARN
output_low(ADCclock); // produce a new lo-to-high clock
delay_cycles(4); // wait for a bit
// now read data
for (i=0;i<16;i++){
output_high(ADCclock); // now after rising edge
delay_cycles(8); // OR input pin with 16bit int
ADC_result |= input(ADCdata); // OR input pin with 16bit int
output_low(ADCclock); // produce a new lo-to-high clock
if (i !=15) ADC_result=ADC_result<<1; // and shift that bit along toward MSB
delay_cycles(4);
}
// PREP FOR NEXT CYCLE
output_LOW(ADCclock);
delay_cycles(8);
OUTPUT_HIGH(ADCstart); // SHUT OFF CONVERTER
}
|
|
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Fri Aug 05, 2011 12:29 pm |
|
|
i should add - the cpu cycles delays
delays were based on using a 20 mhz PIC clock |
|
|
carl
Joined: 06 Feb 2008 Posts: 240 Location: Chester
|
|
Posted: Fri Aug 05, 2011 1:04 pm |
|
|
Hi Asmboy,
Thankyou very much for your reply and an interesting question.
The answer really is related to ensuring that it has the following:
gain selection (min 256)
Vref and Analogue input range are independant
true rail to rail inoput (5V)
24bit (option on same footprint - with different IC in same family)
it had sample code
low power and voltage
I like Burr-Brown (no more unfortunately!!) parts and had a look at your part - but it didn't seem to match the above spec's.
I am nearly a beginner at coding (do it infrequently) so I don't know if my code looks good - especially as it is in 'software spi' - which there is not a lot of examples in this forum. Your code looks like 'hardware spi' - excuse me if i'm incorrect.
Unless I cannot get this working - I would not consider trying something different because the PCB is already done and I like the look of this part.
Any feedback is appreciated
Carl |
|
|
carl
Joined: 06 Feb 2008 Posts: 240 Location: Chester
|
|
Posted: Sun Aug 07, 2011 3:48 pm |
|
|
Hi RF_Developer,
I am going to continue looking at this tomorrow and before going any further I was just wondering what you think of the fact that I am not reading anything on the clk and sdi lines - wouldn't this indicate that I have not written my code correctly especially the read instruction below Code: |
output_low(CS); // Enable LTC2480 SPI interface
while(input(PIN_A7)) {} // Wait for end of conversion. The longest
// you will ever wait is one whole conversion period
// Now is the time to switch any multiplexers because the conversion is finished
// and you have the whole data output time for things to settle.
adc_code.by.te3 = 0; // Set upper byte to zero.
adc_code.by.te2 = spi_xfer(config); // Read first byte, send config byte
adc_code.by.te1 = spi_xfer(0); // Read 2nd byte, send speed bit
adc_code.by.te0 = spi_xfer(0); // Read 3rd byte. ‘0’ argument is necessary
// to act as SPI master!! (compiler
// and processor specific.)
output_high(CS); // Disable LTC2480 SPI interface
|
Its the spi_xfer part that I am concerned about - is this correct? and used in the correct way? I understand all your points about changing from a union to a make 32, and will try it. But first I think that because i am getting no clock signal when checking with the scope - I am doing something simple that is wrong?
any help would be appreciated...
Carl |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Aug 07, 2011 4:42 pm |
|
|
Quote: | #use spi(MASTER, MODE=0, CLK=PIN_A0, DO=PIN_A7, DI=PIN_A1, BITS=8)
LTC2480 ¦ PIC16F628A
PIN1 = SDI PIN20 = A1
PIN2 = VCC = 5V
PIN3 = VREF = 5V
PIN4 = IN+ = 5V
PIN5 = IN- = 0V
PIN6 = 'CS PIN17 = A6
PIN7 = SDO PIN18 = A7
PIN8 = GND
PIN9 = SCK PIN19 = A0
PIN10 = FO = 0V |
Your connections for SDO and SDI are wrong. You have the wires
swapped. SPI is consists of two shift registers (one in the master,
and another one in the slave) that are connected in a big circle. The
DO of the master goes to the DI of the slave, and the DO of the slave
goes to the DI of the master.
You have DI connected to DI, and DO to DO. That can't work. |
|
|
carl
Joined: 06 Feb 2008 Posts: 240 Location: Chester
|
|
Posted: Sun Aug 07, 2011 4:46 pm |
|
|
Hi PCM Programmer,
Thankyou so much,
That is a silly mistake - I will change the pins in software then to
Code: |
#use spi(MASTER, MODE=0, CLK=PIN_A0, DI=PIN_A7, DO=PIN_A1, BITS=8)
|
So would this effect the clock signal then?
And apart from this does the software code I have used look correctly written? |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Aug 07, 2011 5:00 pm |
|
|
Your code is largely copied out of the sample CCS code in the LTC2480
data sheet. So I hope the tech writer did it correctly.
There is one more thing I noticed. You need to add the MSB_FIRST
parameter to your #use spi() statement, as shown below. That's
because your old version of the compiler defaults to LSB first.
Most SPI devices, including the LTC2480, use MSB first protocol.
So do this:
Quote: | #use spi(MASTER, MODE=0, CLK=PIN_A0, DO=PIN_A7, DI=PIN_A1, BITS=8, MSB_FIRST) |
|
|
|
carl
Joined: 06 Feb 2008 Posts: 240 Location: Chester
|
|
Posted: Sun Aug 07, 2011 5:07 pm |
|
|
I checked that in the ccs manual (it said MSB first like in the datasheet) - but that was the 2011 manual - not the older version - so thanks I will change it.
It is the use of the 'software spi' code below which is what I am concerned about Code: |
output_low(CS); // Enable LTC2480 SPI interface
while(input(PIN_A7)) {} // Wait for end of conversion. The longest
// you will ever wait is one whole conversion period
// Now is the time to switch any multiplexers because the conversion is finished
// and you have the whole data output time for things to settle.
adc_code.by.te3 = 0; // Set upper byte to zero.
adc_code.by.te2 = spi_xfer(config); // Read first byte, send config byte
adc_code.by.te1 = spi_xfer(0); // Read 2nd byte, send speed bit
adc_code.by.te0 = spi_xfer(0); // Read 3rd byte. ‘0’ argument is necessary
// to act as SPI master!! (compiler
// and processor specific.)
output_high(CS); // Disable LTC2480 SPI interface |
is the spi_xfer used in this way?
Thanks
carl |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|