|
|
View previous topic :: View next topic |
Author |
Message |
Owais
Joined: 23 Sep 2010 Posts: 3
|
Stuck with LTC1854 ADC Driver |
Posted: Thu Sep 23, 2010 4:16 am |
|
|
I am trying to interface two LTC1854 12bit ADC devices over SPI bus to a PIC18F8680 device. The constraint here is that SPI must be used in hardware mode (not software bit banging) and SPI mode has to be mode 0 or mode 3. I am using ADCs in single ended acquisition
The ADCs seem to be working fine if configured for SPI mode 3, but for channel 0 only. It seems the ADC device is unable to configure itself for acquisition of other channels. Code returned for +5 input on channel 0 gives 0x7DE0 code, ~= 4.91 V, but still garbage result for other channels.
If configured for mode 0,0 the value returned is not correct at all. For +5 input on channel 0 0x1DE0 code is returned ~= 1.16 V
Can someone help after looking at the LTC1854 datasheet.
Code: |
//Program to acquire analog data from 2x LTC1854
#include <18F8680.h>
#FUSES NOWDT //No Watch Dog Timer
#FUSES WDT128 //Watch Dog Timer uses 1:128 Postscale
#FUSES HS //High speed Osc (> 4mhz for PCM/PCH) (>10mhz for PCD)
#FUSES STVREN //Stack full/underflow will cause reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES MCLR //Master Clear pin enabled
#use delay(clock=20000000)
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)
#define SPI_MODE_0_0 (SPI_L_TO_H | SPI_XMIT_L_TO_H)
#define SPI_MODE_0_1 (SPI_L_TO_H)
#define SPI_MODE_1_0 (SPI_H_TO_L)
#define SPI_MODE_1_1 (SPI_H_TO_L | SPI_XMIT_L_TO_H)
#define TRISTC 0xf94
#define SSPSTAT 0xfc7
#define SSPCON1 0xfc6
#define SSPCON2 0xfc5
#define ADC1START PIN_E5 //75
#define _ADC1RD PIN_E6 //74
#define ADC2START PIN_E3 //77
#define _ADC2RD PIN_E4 //76
//Local Functions
void Initialize_ADCs(void);
void ReadLTC1854_1(int8 channel, unsigned int8* dataoutm, unsigned int8* dataoutl);
void ReadLTC1854_2(int8 channel, unsigned int8* dataoutm, unsigned int8* dataoutl);
unsigned int8 ChannelSelect[8]=
{
//for single ended acquisition
//SGL ODD SELECT1 SELECT0 x x NAP Sleep
0b10000000,//80
0b11000000,//c0
0b10010000,//90
0b11010000,//d0
0b10100000,//a0
0b11100000,//e0
0b10110000,//b0
0b11110000 //f0
};
void main()
{
int8 i;
unsigned int16 adc2smagnitude;//unsigned magnitude
unsigned int8 adcvaluem,adcvaluel;//conversion result
int1 adc2ssign;//sign bit
float adcresult;//floating point value for user printed as string
setup_adc_ports(NO_ANALOGS|VSS_VDD);
setup_adc(ADC_OFF);
setup_psp(PSP_DISABLED);
setup_spi(SPI_MASTER|SPI_MODE_0_0|SPI_CLK_DIV_4|SPI_SAMPLE_AT_END);
setup_wdt(WDT_OFF);
setup_timer_0(RTCC_INTERNAL);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DISABLED,0,1);
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
//Setup_Oscillator parameter not selected from Intr Oscillator Config tab
printf("Testing SPI Port for ADC (LTC1854). Uart setting 9600-8-N-1.\n");
Initialize_ADCs();
printf("TRISC = %X\n", *TRISTC);
printf("SSPSTAT = %X\n", *SSPSTAT);
printf("SSPCON1 = %X\n", *SSPCON1);
printf("SSPCON2 = %X\n", *SSPCON2);
while(TRUE)
{
//Acquiring ADC 1 channels
printf("\nAcquiring ADC 1 channels.\n");
for(i=0;i<8;i++)
{
ReadLTC1854_1(i, &adcvaluem, &adcvaluel);//Using third diagram in figure 7 in datasheet
adc2smagnitude = (adcvaluem & 0x7F)<<8; //sign not needed
adc2smagnitude += adcvaluel;
adc2smagnitude = adc2smagnitude >> 4;//4 unused nibbles for 12 bit device
adc2ssign = bit_test(adcvaluem,7);
if(adc2ssign==1)
adcresult = -5.0f; // span is -5 to +5 volts, MSB = 1 means -5v weight here
else
adcresult = 0;
adcresult += adc2smagnitude*0.00244140625;// magnitude * 10v / 2^12 (12 bit device)
printf("ADC1-Channel %d, MSB = %X, LSB = %X, Result in Volts = %f\n",i,adcvaluem,adcvaluel,adcresult);
delay_ms(10);//test to settle adc pins
}
delay_ms(400);
printf("\n");
//Acquiring ADC 2 channels
printf("\nAcquiring ADC 2 channels.\n");
for(i=0;i<8;i++)
{
ReadLTC1854_2(i, &adcvaluem, &adcvaluel);
adc2smagnitude = (adcvaluem & 0x7F)<<8; //sign not needed
adc2smagnitude += adcvaluel;
adc2smagnitude = adc2smagnitude >> 4;//4 unused nibbles for 12 bit device
adc2ssign = bit_test(adcvaluem,7);
if(adc2ssign==1)
adcresult = -5.0f; // span is -5 to +5 volts, MSB = 1 means -5v weight here
else
adcresult = 0;
adcresult += adc2smagnitude*0.00244140625;// magnitude * 10v / 2^12 (12 bit device)
printf("ADC2-Channel %d, MSB = %X, LSB = %X, Result in Volts = %f\n",i,adcvaluem,adcvaluel,adcresult);
delay_ms(10);//test to settle adc pins
}
delay_ms(400);
printf("\n");
}//while
}//main
void Initialize_ADCs(void)
{
output_low(ADC1START);//ADC1 conversion start signal, active high
output_low(ADC2START);//ADC2 conversion start signal, active high
output_high(_ADC1RD);//ADC 1 RD or CS signal, active low
output_high(_ADC2RD);//ADC 2 RD or CS signal, active low
}
//Read ADC 1
//channel = 0-7
void ReadLTC1854_1(int8 channel, int8* dataoutm, int8* dataoutl)
{
printf("Sampling Channel Command %X: ", ChannelSelect[channel]);
output_low(_ADC1RD);//enable chip
spi_write(ChannelSelect[channel]);//read command
delay_us(1);//hold
spi_write(ChannelSelect[channel]);//read command
output_high(_ADC1RD);//disable chip
delay_us(5);//hold
output_high(ADC1START);
delay_us(2);//hold
output_low(ADC1START);
delay_us(2);//wait for conversion
output_low(_ADC1RD);//enable chip
*dataoutm = spi_read(0);
*dataoutl = spi_read(0);
output_high(_ADC1RD);//disable chip
}
//Read ADC 2
//channel = 0-7
void ReadLTC1854_2(int8 channel, int8* dataoutm, int8* dataoutl)
{
printf("Sampling Channel Command %X: ", ChannelSelect[channel]);
output_low(_ADC2RD);//enable chip
spi_write(ChannelSelect[channel]);//read command
delay_us(1);//hold
spi_write(ChannelSelect[channel]);//read command
output_high(_ADC2RD);//disable chip
delay_us(5);//hold
output_high(ADC2START);
delay_us(2);//hold
output_low(ADC2START);
delay_us(2);//wait for conversion
output_low(_ADC2RD);//enable chip
*dataoutm = spi_read(0);
*dataoutl = spi_read(0);
output_high(_ADC2RD);//disable chip
}
|
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Sep 23, 2010 5:20 pm |
|
|
Quote: |
The ADCs seem to be working fine if configured for SPI mode 3, but for
channel 0 only. It seems the ADC device is unable to configure itself for
acquisition of other channels. Code returned for +5 input on channel 0
gives 0x7DE0 code, ~= 4.91 V, but still garbage result for other channels. |
The text description of the SPI communication in the data sheet sounds
like Mode 3 to me, so your experience matches what I read. The data
transfer diagrams don't show mode 3. They don't match the text
description. I don't know what the intention of their tech writers is.
My advice is to cut down your program so it only tests channel 1 (for
example). If possible, get rid of at least 75% of your code. Then I think
it would be simple enough to trouble-shoot.
For an example of cutting it down, get rid of all this:
Quote: |
setup_adc_ports(NO_ANALOGS|VSS_VDD);
setup_adc(ADC_OFF);
setup_psp(PSP_DISABLED);
setup_spi(SPI_MASTER|SPI_MODE_0_0|SPI_CLK_DIV_4|SPI_SAMPLE_AT_END);
setup_wdt(WDT_OFF);
setup_timer_0(RTCC_INTERNAL);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DISABLED,0,1);
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
|
Replace it with this. It uses Mode 3 and it also slows the clock way down.
Code: |
setup_spi(SPI_MASTER | SPI_MODE_1_1 | SPI_CLK_DIV_16);
|
There is a lot more code that can be removed (for this test). |
|
|
Owais
Joined: 23 Sep 2010 Posts: 3
|
|
Posted: Mon Sep 27, 2010 3:55 am |
|
|
I tried the suggestion and reduced code to: (beginning code is same)
Code: |
void main()
{
unsigned int16 adc2smagnitude;//unsigned magnitude
unsigned int8 adcvaluem,adcvaluel;//conversion result
int1 adc2ssign;//sign bit
float adcresult;//floating point value for user printed as string
setup_spi(SPI_MASTER | SPI_MODE_1_1 | SPI_CLK_DIV_64);
output_high(_ADC1RD);//enable chip
while(TRUE)
{
printf("Command word for Channel 1 (single ended) = %X: ", ChannelSelect[1]);
output_low(_ADC1RD);//enable chip
spi_write(ChannelSelect[1]);//read command
delay_us(1);//hold
spi_write(0);//read command, dont care by LTC this byte
output_high(_ADC1RD);//disable chip
delay_us(5);//hold
output_high(ADC1START);
delay_us(2);//hold
output_low(ADC1START);
delay_us(2);//wait for conversion
output_low(_ADC1RD);//enable chip
adcvaluem = spi_read(0);
adcvaluel = spi_read(0);
output_high(_ADC1RD);//disable chip
adc2smagnitude = (adcvaluem & 0x7F)<<8; //sign not needed
adc2smagnitude += adcvaluel;
adc2smagnitude = adc2smagnitude >> 4;//4 unused nibbles for 12 bit device
adc2ssign = bit_test(adcvaluem,7);
if(adc2ssign==1)
adcresult = -5.0f; // span is -5 to +5 volts, MSB = 1 means -5V weight here
else
adcresult = 0;
adcresult += adc2smagnitude*0.00244140625;// magnitude * 10v / 2^12 (12 bit device)
printf("MSB = %X, LSB = %X, Result in Volts = %f\n",adcvaluem,adcvaluel,adcresult);
delay_ms(400);
}//while
}//main
|
But still only channel no 0 is sampled and i cant get the ADC to be configured for sampling other channels. I have used two different LTC1854 devices and both shouldn't be damaged. Also the controller (on a small header PCB) is functional as i can drive some serial memories just fine on another board with it.
any other suggestions anyone ? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19496
|
|
Posted: Mon Sep 27, 2010 4:36 am |
|
|
First, I'd encapsulate the 16bit transfers. So something like:
Code: |
union combiner {
int8 b[2]
int16 w;
}
int16 talk_to_adc(union combiner command) {
union combiner result;
//Trigger last configured conversion
output_high(ADC1START);
delay_cycles(1);//Only 40nSec required high
output_low(ADC1START);
delay_us(5);//wait for conversion - note tconv, is 5uSec worst case
output_low(_ADC1RD);//enable chip
result.b[1] = spi_read(command.b[1]);
result.b[0] = spi_read(command.b[0]);
output_high(_ADC1RD);//disable chip
return result.w;
}
|
Then the single command, reads the last configured conversion, and sets the new configuration.
Then your test loop becomes:
Code: |
int16 value;
while(TRUE) {
printf("Command word for Channel 1 (single ended) = %X: ", ChannelSelect[1]);
value=talk_to_adc(ChannelSelect[1]*256L); //Send 16bit command, get
//16bit value from last command.
//Then for testing, don't fiddle around converting to volts, but print
//the value returned
printf("%ld",value);
delay_ms(400);
}//while
}//main
|
Then I'd also try channel 2. Given that the 'odd' channels are effectively the -ve inputs in differential mode, it is possible the problem is switching to single ended operation, rather than with retrieving the other channels - worth testing.
I'm using the internal 'cast' abilities of C here, to convert a 16bit value, into a union to access the bytes, then to treat the MSB of this as the sign for output.
Best Wishes |
|
|
|
|
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
|