|
|
View previous topic :: View next topic |
Author |
Message |
jgrauer
Joined: 21 Sep 2006 Posts: 6
|
ADS8344 |
Posted: Mon Jan 08, 2007 7:31 am |
|
|
Hi,
I've got the PIC18F452 prototyping board, and I am trying to use a TI ADS8344 analog to digital converter.
My problem is that after sending the control byte to the ADS, I get the data back spread over three bytes instead of two. So at this point I am shifting the data and concatenating the bytes together.
Attached is my code. I have tried an open loop delay between the read and write functions, I have tried waiting for the BUSY signal (ADS pin 16) to go low before reading, and I have experimented with using the in_data = spi_read(out_data) method instead of separate spi_write() and spi_read() commands.
please help!
thanks
Code: |
#include <18F452.h>
#device ICD=TRUE ADC=16
#fuses HS,NOWDT,NOPROTECT,NOLVP, NOBROWNOUT
#use delay(clock=20000000)
#use rs232(baud=115200, xmit=PIN_C6, rcv=PIN_C7)
//Define Configuration Schematic
#define CS PIN_E0
//#define CLK PIN_C3
//#define DI PIN_C5
//#define DO PIN_C4
//#define BUSY PIN_E1
//Main Program
void main()
{
//declarations
int8 adc_chan0 = 0b10000100;
int8 adc_in1, adc_in2, adc_in3;
int16 an;
//setup the spi line
setup_spi(SPI_MASTER | SPI_L_TO_H | SPI_CLK_DIV_16);
output_high(CS);
//setup PIC adc
setup_adc_ports(ALL_ANALOG); //set up adc ports
setup_adc(ADC_CLOCK_INTERNAL); //set to internal clock
set_adc_channel(1); //set adc channel
while(1)
{
//communicate with ads8344
output_low(CS);
spi_write(0b10000111);
adc_in1 = spi_read(0x00);
adc_in2 = spi_read(0x00);
adc_in3 = spi_read(0x00);
output_high(CS);
//read built-in adc
an = read_adc();
//print to screen
printf("\r\n%u\t%u\t%u\t%lu",adc_in1,adc_in2,adc_in3,an);
}
}
|
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon Jan 08, 2007 12:35 pm |
|
|
The most important thing about SPI is to setup the correct mode.
Look at any of the SPI timing diagrams in the ADS8344 data sheet.
http://focus.ti.com/lit/ds/symlink/ads8344.pdf
You can see that the data is sampled on the rising edge of DCLK.
(The rising edge is in the middle of the data cell). Also, the idle state
of DCLK is a low level.
Now look at this chart of SPI modes:
http://www.totalphase.com/support/articles/article03/#modes
Notice that Mode 0 fits the timing relationship described above.
Now you know the SPI mode. The next question is, how to setup
the mode in CCS ? Look at this post:
http://www.ccsinfo.com/forum/viewtopic.php?t=29059&start=36
ckielstra has posted a convenient way of defining the SPI modes:
Code: |
#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)
|
From this, you can immediately see that your code is using the
wrong mode. You're using Mode 1. Looking back at the chart
that describes the modes, you can see that Mode 1 is completely
wrong for the ADS8344. It samples on the wrong edge.
To fix the mode, put the #defines for the mode statements above main().
Then change the setup_spi() function to use the constant for Mode 0:
Code: |
setup_spi(SPI_MASTER | SPI_MODE_0_0 | SPI_CLK_DIV_16);
|
There might be other problems in your code, but at least this will
get you using the correct SPI mode. |
|
|
jgrauer
Joined: 21 Sep 2006 Posts: 6
|
|
Posted: Mon Jan 08, 2007 2:08 pm |
|
|
Thanks for your reply.
So before I had two blank bits before the data started streaming, meaning I had to read the SPI bus a third time to get the 16 bits of data. After correcting the SPI mode, I have only one blank bit before the data streams, so that I still have to make three reads to capture the full 16 bit data stream. Reading the section 'External Clock Mode' in the ADS8344 manual, I guess this is to be expected. In the penultimate paragraph in that section they give another method which I don't completely understand how to implement; they say to read the LSB at the same time the new control bit is being written, which should result in 24 clock cycles per conversion. I figured this means I should use the following lines in my code:
Code: | output_low(CS); //select the ADS8344
spi_write(chan0); //send control byte
ads_in1 = spi_read(0x00); //read
ads_in2 = spi_read(0x00);
ads_in3 = spi_read(0x00);
output_high(CS); //release the ADS8344 |
When I do this, I get the same result as I described above, except I am reading the least significant byte before the other two. Does anyone have a suggestion? Is it possible to read these 16 bits with two reads, or am I doomed to using three bytes to concatenate and shift?
Attached is my code, which is slightly modified from what I posted before.
thanks!
Code: | // ADS8344_1channel.c
//
// This program compares one channel of the ads8344 and the
// PIC's internal adc
//
//PIC CONFIGURATION
#include <18F452.h>
#device ICD=TRUE ADC=16
#fuses HS,NOWDT,NOPROTECT,NOLVP, NOBROWNOUT
#use delay(clock=20000000)
#use rs232(baud=115200, xmit=PIN_C6, rcv=PIN_C7)
//CONFIGURATION SCHEMATIC
#define CS PIN_E0
#define CLK PIN_C3
#define DI PIN_C5
#define DO PIN_C4
#define BUSY PIN_E1
#define chan0 0b10000111
#define chan1 0b10010111
#define chan2 0b10100111
#define chan3 0b10110111
#define chan4 0b11000111
#define chan5 0b11010111
#define chan6 0b11100111
#define chan7 0b11110111
#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)
//FUNCTION PROTOTYPES
int16 ads8344_data(int8, int8, int8);
//MAIN PROGRAM
void main()
{
//declarations
int8 ads_in1, ads_in2, ads_in3;
int16 an, ads_16;
//setup the spi line
setup_spi(SPI_MASTER | SPI_MODE_0_0 | SPI_CLK_DIV_16);
output_high(CS);
//setup PIC adc
setup_adc_ports(ALL_ANALOG); //set up adc ports
setup_adc(ADC_CLOCK_INTERNAL); //set to internal clock
set_adc_channel(1); //set adc channel
while(1)
{
//communicate with ads8344
output_low(CS); //select the ADS8344
spi_write(chan0); //send control byte
ads_in1 = spi_read(0x00); //read
ads_in2 = spi_read(0x00);
ads_in3 = spi_read(0x00);
output_high(CS); //release the ADS8344
ads_16 = ads8344_data(ads_in1,ads_in2,ads_in3); //reduce the data
//read built-in adc
an = read_adc();
//print to screen
printf("\r\n%lu\t%lu",ads_16,an);
//printf("\r\n%u \t %u \t %u \t %lu",ads_in1,ads_in2,ads_in3,an);
}
}
//ADS8344 DATA REDUCTION
int16 ads8344_data(int8 byte2, int8 byte1, int8 byte0)
{
//Declarations
int16 output;
int32 temp;
temp = make32(byte2,byte1,byte0,0x00); //concatenate bytes
shift_left(&temp, 32, 0); //shift 1 byte to the left
output = make16( make8(temp,3), make8(temp,2) ); //mask out the data bits
return(output);
} |
|
|
|
Ttelmah Guest
|
|
Posted: Mon Jan 08, 2007 4:36 pm |
|
|
Top of page 14 of the data sheet.
The problem is that the 'busy' signal, does not drop till one clock is received. Hence 25 clocks are actually needed to transfer the three bytes. Eight for the control word, one for the busy transition, 16 for the data bytes. You can then optionally clock another 7 which just clock out zeros.
Don't fiddle around catenting the data. Get it like this:
Code: |
union {
int16 words[2];
int8 bytes[4];
} joiner
output_low(CS); //select the ADS8344
spi_write(chan0); //send control byte
joiner.bytes[3] = spi_read(0x00); //read
joiner.bytes[2] = spi_read(0x00);
joiner.bytes[1] = spi_read(0x00);
output_high(CS); //release the ADS8344
//Note that using the array costs nothing here in speed, since
//the values are directly addressed.
shift_left(&joiner.bytes[1],3,0);
printf("\r\n%lu",joiner.word[1]);
|
The trick here is that by reading the three returns inot three successive bytes, a single bit rotation, moves these to rebuild the required 16bit value. The union allows this to be accessed.
I think I have got the indexes, and things right, but 'no guarantees'...
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
|