|
|
View previous topic :: View next topic |
Author |
Message |
bela
Joined: 31 Aug 2008 Posts: 27 Location: Bedford, BEDS UK
|
PIC18F8722 SPI |
Posted: Sun Aug 31, 2008 5:23 am |
|
|
Hi.
I'm currently trying to impliment a CS5525 ADC with an 8722 pic over spi.
The problem is, I don't quite understand how to generate a clock for 24 bits of data.
Basically, I should be able to send an 0x94 to read the cfg regs and that will return 24bits.
If I do this:
spi_write(0x94); // read the config register (24 bits)
data1=spi_read(); //read hi byte
data2=spi_read(); // read mid byte
data3=spi_read(); //read low byte
The first byte is usually "something" where as the second / third are 00s or FFs.
If I do this:
spi_write(0x94); // read the config register (24 bits)
data1=spi_read(); // the clock for this byte was generated by spi_write
// according to the help file
data2=spi_read(0);
data3=spi_read(0);
It freezes up, assuming that it's waiting for data on data2?
I've also tried this:
spi_write(0x94);
data1=spi_read();
spi_write(0x00);
data2=spi_read();
spi_write(0x00);
data3=spi_read();
and it still freezes on data2.
Can anyone help wth this. It seems a difficult chip to use!
Also, the whole spi comms (with this chip) only seems to work if I use SPI_L_TO_H. I understand that passing a zero to spi_read will mean that it will generate a clock but send no data. Does this Zero change based on SPI_X_TO_X? For example, does the zero then need to be FF?
If I use SPI_H_TO_L, the spi_write()s freeze the program.
Thanks for any help; it's very much appreciated.
Darren. |
|
|
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Sun Aug 31, 2008 5:58 am |
|
|
There are different questiions raised with your posting.
1. Correct syntax for the SPI built-in functions. Clearly either a spi_write() or a spi_read() with data argument is necessary to generate a read clock.
2. Selecting the appropriate SPI mode. This is basically independant of the other points and must be done according to periperal requirements.
3. Freezing the chip with SPI communication should never happen! You may have managed to create the issue in other parts of your code, e. g. by defining unhandled interrupts, inappropriate watchdog settings or similar. Basically, the SPI driver in master mode doesn't wait for anything, it sends the output clock and reads SDI. |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Sun Aug 31, 2008 6:39 am |
|
|
How are you setting up the SPI hardware in the PIC? Are you using the setup_spi() function or the new '#use SPI' directive? I prefer to use setup_spi() as this is tested functionality.
Note that you can't mix the new and old SPI methods.
From the CS5525 data sheet, figure 6:
- Clock level in inactive state is low.
- Data is clocked in at the positive clock edge.
Then read the SPI specifications at: http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus
From this you can conclude the required SPI mode = 0 (CPOL=0 / CPHA=0)
I always have troubles understanding the non-standard CCS terminology for setting up SPI, so I created the defines below: Code: | #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) |
The setup for your CS5525 A/D chip then becomes: Code: | setup_spi(SPI_MASTER | SPI_MODE_0); | This will work for processor speeds up to 8MHz, for higher clock speeds you have to delay the SPI bus to be within specifications again for your A/D converter: Code: | setup_spi(SPI_MASTER | SPI_MODE_0 | SPI_CLK_DIV_4); // up to 8MHz PIC clock (same effect as leaving the SPI_CLK_DIV_x parameter out)
setup_spi(SPI_MASTER | SPI_MODE_0 | SPI_CLK_DIV_16); // up to 32MHz PIC clock |
Please post a small (max 20 lines) but complete test program demonstrating your problem. The program should be complete so we can copy/paste it into our compiler, i.e. the program should contain the #include line, #fuses, etc.
Also post your compiler version number (looks like 3.xxx or 4.xxx and can be found at the top of the *.lst file).
Edit 01-Sept-2008: Fixed an error in the SPI clock rate being wrong by a factor 4. Defining SPI_CLK_DIV_4 has the same effect as leaving the parameter out (it is defined as zero).
Last edited by ckielstra on Sun Aug 31, 2008 5:02 pm; edited 1 time in total |
|
|
bela
Joined: 31 Aug 2008 Posts: 27 Location: Bedford, BEDS UK
|
RE SPI |
Posted: Sun Aug 31, 2008 1:02 pm |
|
|
Hello, here is my code. Sorry, it's more than 20 line but I believe that I need all of this in this order to correctly operate the cs5525 chip. Thanks for your help.
I have added comments so you can see what I'm trying to do.
Code: |
#fuses HS
#include <18f8722.h>
#use delay (clock=32000000)
#use rs232(baud=115200, xmit=PIN_C6, rcv=PIN_C7, ERRORS)
main()
{
int data1, data2, data3;
output_high(PIN_J3); /* turns the 5 volt on */
delay_ms(100);
setup_spi(spi_master |SPI_L_TO_H | SPI_XMIT_L_TO_H | spi_clk_div_16 );
delay_ms(800); /* data sheet reccomends 600ms+ power up time */
output_high(PIN_F1); //pin f1 is adc chip select
delay_us(150);
output_low(PIN_F1);
delay_us(150);
/* data sheet says send 16 11111111 and 1 11111110 to start up */
for(data2=0;data2<15;data2++)spi_write(0xFF);
spi_write(0xFE);
output_high(PIN_F1); //pin f1 is adc chip select
delay_us(150);
output_low(PIN_F1);
delay_us(150);
printf("config regs: ");
spi_write(0x94);
data1=spi_read();
data2=spi_read();
data3=spi_read();
printf("data1 %X\n\r",data1);
printf("data2 %X\n\r",data2);
printf("data3 %X\n\r\n\r",data3);
/* write to the calibration and gain registers.
don't really care what these are, just want some sensible looking data
back from the adc */
output_high(PIN_F1); //pin f1 is adc chip select
delay_us(150);
output_low(PIN_F1);
delay_us(150);
printf("cal:\n\r");
spi_write(0x84);
spi_write(0x00);
spi_write(0x00);
spi_write(0x01);
output_high(PIN_F1); //pin f1 is adc chip select
delay_us(150);
output_low(PIN_F1);
delay_us(150);
printf("gain\n\r");
spi_write(0x82);
spi_write(0x80);
spi_write(0x00);
spi_write(0x00);
output_high(PIN_F1); //pin f1 is adc chip select
delay_us(150);
/* infinate loop to keep sampling the conversion regsiter */
while(TRUE)
{
output_low(PIN_F1);
delay_us(150);
spi_write(0xC0); /* send the instruction to do a single conversion */
spi_write(0x96); /* send the instruction to read the conversion regs */
/* to do this properly, I should poll the 'done' bit in the cfg regs
until it indicated data ready, then read the conversion register
(0x96) but I just want something other than 00s or FFs at the moment!
*/
data1=spi_read();
data2=spi_read();
data3=spi_read();
output_high(PIN_F1);
printf("\n\r\n\r data1=%X \n\r data2=%X \n\r data3=%X",data1,data2,data3);
}
}
|
|
|
|
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Sun Aug 31, 2008 1:50 pm |
|
|
The code can't work, cause no clock is send out with spi_read(). Use spi_read(0) instead.
Quote: | If this device supplies the clock then either do a SPI_WRITE(data) followed by a SPI_READ() or do a SPI_READ(data). These both do the same thing and will generate a clock. If there is no data to send just do a SPI_READ(0) to get the clock. |
|
|
|
bela
Joined: 31 Aug 2008 Posts: 27 Location: Bedford, BEDS UK
|
|
Posted: Mon Sep 01, 2008 1:59 am |
|
|
Hello. Thanks for you reply.
I should have made it clearer:
It's the spi_read(0)s that make the program halt, exactly at the spi_read(0), I've tested it by doing printfs between each line to see where it gets. This is why I changed the spi_read(0)s to spi_read()s. Now, the program doesn't halt but the second and third bytes are invalid.
I take it that because of the quote about spi_write(data) followed by spi_read(), the first byte will have a clock but the second and third byte will not? So, if it was to work, the code I should have is:
Code: |
spi_write(0x94); //cfg register
data1=spi_read();
data2=spi_read(0);
data3=spi_read(0);
|
?
The above block of code does not work. Do you have any ideas?
Thanks
Darren. |
|
|
Ttelmah Guest
|
|
Posted: Mon Sep 01, 2008 3:15 am |
|
|
First thing, the code as shown, will return the byte returned by the chip, _when you wrote to the config register_, in the first read. This is almost certainly not what you want. The 'problem' with SPI, is that it transfers data in both directions _at once_. This is good in terms of performance, but does make working out what you receive quite complex. I'd expect that in fact you need to generate 32 clocks, _not_ 24. The chip can't know what it is to send 'back', till it has received the first byte. So the sequence has to be:
Clock one control byte.
Throw away the return.
Repeat three times:
Clock one byte
Store the return
If you check the data sheet, you will see that this is what they show.
Now, SPI, for the master, _cannot_ stop on a SPI_READ(0)!... This command tells your hardware to generate the clock, and as soon as this is done, reads the byte. It only waits for the hardware to say that the data has been clocked. It doesn't wait for anything at all from the other end.
Before looking further at this, I have to query your oscillator selection.
You have 'HS' oscillator selection, and 32Mhz as the frequency. The _maximum_ supported frequency for the HS oscillator, is _25Mhz_. Parameter 1, Table 28-6. The chip is happy to _run_ at 32Mhz, but this requires that you either use an external oscillator (EC mode), or a crystal at 8MHz, and enable the PLL (HSPLL). This may be what is causing the problem. The SPI hardware, may be the first part 'complaining' about this section being incorrectly driven. You need to correct this.
Next comment, is 'what compiler version'. If the hardware is working correctly, the only way that the SPI_READ(0) can hang, is if something is incorrectly configured. If you are using an early 4.0xx compiler, then I'm afraid this may be causing the problem by not setting up the SPI correctly. Be 'dubious' of anything below perhaps 4.03x.
So, correct your clock, and check your compiler version, updating if it is an early V4 version, then see what happens.
Best Wishes |
|
|
bela
Joined: 31 Aug 2008 Posts: 27 Location: Bedford, BEDS UK
|
|
Posted: Mon Sep 01, 2008 6:06 am |
|
|
Hello.
Thank you for your reply. I will look into the clock speed problem and try again.
The compiler version is 4.066.
Daz. |
|
|
bela
Joined: 31 Aug 2008 Posts: 27 Location: Bedford, BEDS UK
|
|
Posted: Wed Sep 03, 2008 7:51 am |
|
|
Hello,
I have added all the things that were advised here. It no longer freezes, this fix was selecting the correct clock fuse.
I still get 00 when I try to read things. Can anyone help?
To prove a point, I followed part and then ALL of AN88 from Crystal/Cirrus Logic. I wrote a value to the gain register and then read it back and 3 bytes were 00s. What a suprise.
Code: |
//write to the gain regs
output_high(PIN_F1); //pin f1 is adc chip select
delay_ms(150);
output_low(PIN_F1);
delay_us(150);
spi_write(0x82);
spi_write(0x80);
spi_write(0x00);
spi_write(0x00);
output_high(PIN_F1); //pin f1 is adc chip select
delay_ms(150);
printf("gain regs: ");
output_low(PIN_F1);
delay_us(150);
spi_write(0x92);
dummy=spi_read();
data1=spi_read(0);
data2=spi_read(0);
data3=spi_read(0);
output_high(PIN_F1); //pin f1 is adc chip select
delay_ms(150);
printf("data1 %X\n\r",data1); // gain
printf("data2 %X\n\r",data2);
printf("data3 %X\n\r\n\r",data3);
|
Thank you
Daz. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Sep 03, 2008 11:36 am |
|
|
You did not follow AN88 on the Cirrus website.
http://www.cirrus.com/en/pubs/appNote/an88.pdf
AN88 uses routines. You are using inline code. Using routines (functions)
is much better, because allows your program to be organized.
First, AN88 calls the 'initialize' routine. The 'initialize' routine does this:
1. It delays for 661 ms.
2. Then it sets \CS low.
3. It sends the value of 0xFF to the CS5525 chip, 15 times.
4. Then it sends one byte with a value of 0xFE.
5. Then it sets \CS high.
Your code does not do this.
My suggestion is, convert the AN88 code to CCS. Use routines, the
same as AN88 does. Then assuming that your hardware connections
to the CS5525 are correct, you will have a good chance to get the chip
working. |
|
|
bela
Joined: 31 Aug 2008 Posts: 27 Location: Bedford, BEDS UK
|
|
Posted: Thu Sep 04, 2008 10:23 am |
|
|
Hello
Below is the code I have written to attempt to copy the AN88 demo code to the best of my ability (I don't know assembler that well.)
It didn't work.
This version gets stuck in the "poll the done flag" section because the data is 00s. As expected.
I already know about what I could improve on in this code but I am not concerned with that now. It should work but doesn't. Can anyone offer their kind help?
Code: |
#include <18f8722.h>
#fuses EC_IO
#define FOSC 32000000
#use delay (clock=FOSC)
/** USB Setup **/
#use rs232(baud=115200, xmit=PIN_C6, rcv=PIN_C7, ERRORS)
#use fast_io(A)
#use fast_io(B)
#use fast_io(C)
#use fast_io(D)
#use fast_io(E)
#use fast_io(F)
#use fast_io(G)
#use fast_io(H)
#use fast_io(J)
int spi_out=0x00;
int spi_in=0x00;
int COMMANDBYTE=0x00;
int HIGHBYTE=0x00;
int MIDBYTE=0x00;
int LOWBYTE=0x00;
int cfg1,cfg2,cfg3;
void send_spi();
void receive_spi();
void convert();
void write_register();
void read_register();
void calibrate();
void initialize();
void main();
void send_spi()
{
/* transmit 1 byte in spi_out out the spi */
spi_write(spi_out);
return;
}
/***************************************************************************/
void receive_spi()
{
/* generate the clock and get a byte from the spi */
spi_in=spi_read(0);
return;
}
/***************************************************************************/
void convert()
{
/* perform a conversion */
spi_out=0xC0; /* single conversion mode */
output_low(PIN_F1); /* clear chip select */
send_spi(); /* write it out */
COMMANDBYTE=0x94; /* Config read */
while(!bit_test(LOWBYTE,3))read_register();
COMMANDBYTE=0x96; /* read conv reg */
read_register(); /* acquire the conversion */
output_high(PIN_F1); /* set chip select */
return;
}
/***************************************************************************/
void write_register()
{
/* send the command, hi, mid and low bytes */
output_low(PIN_F1); /* clear cs */
spi_out=COMMANDBYTE;
send_spi();
spi_out=HIGHBYTE;
send_spi();
spi_out=MIDBYTE;
send_spi();
spi_out=LOWBYTE;
send_spi();
output_high(PIN_F1);
}
/***************************************************************************/
void read_register()
{
/* send a command and receive the 24 bit data back */
output_low(PIN_F1); /* clear cs */
spi_out=COMMANDBYTE;
send_spi();
receive_spi(); /* get the byte into spi_in */
HIGHBYTE=spi_in; /* move into HI */
receive_spi(); /* get the byte into spi_in */
MIDBYTE=spi_in; /* move into MID */
receive_spi(); /* get the byte into spi_in */
LOWBYTE=spi_in; /* move into LO */
output_high(PIN_F1); /* clear cs */
}
/***************************************************************************/
void calibrate()
{
/* intruct the AD to do an offset self cal */
COMMANDBYTE=0x84; /* set command for cfg write */
HIGHBYTE=0x00; /* clear Hi */
MIDBYTE=0x00; /* clear mid */
LOWBYTE=0x01; /* Set lsb to 0x01 for self cal */
write_register(); /* send the above over */
/* read the config and poll the done flag */
COMMANDBYTE=0x94;
while(!bit_test(LOWBYTE,3))read_register(); /* poll the done flag */
cfg1=HIGHBYTE; /* save these for later */
cfg2=MIDBYTE;
cfg3=LOWBYTE;
return;
}
/***************************************************************************/
void initialize()
{
/* Routine to initialize the crappy CS5525
1. Initialize Port C
2. Disable all the other chip selects
3. Enable the 5v FET - power for all devices except the PIC
4. Delay for 661ms power up
5. send initialize sequence to the AD
*/
int loop;
set_tris_c(0b10010000);
setup_spi(spi_master |SPI_L_TO_H | SPI_XMIT_L_TO_H | spi_clk_div_64 );
output_high(PIN_H3); /* Deselect IO exapnder */
output_high(PIN_H2); /* Deselect Wireless */
output_high(PIN_H7); /* Deselect external flash */
output_high(PIN_J3); /* turns the 5 volt on */
output_low(PIN_C5); /* Clear SDO */
delay_ms(661); /* AD power up */
output_low(PIN_F1); /* clear chip select */
spi_out=0xFF;
for(loop=0;loop<15;loop++)send_spi();
spi_out=0xFE;
send_spi();
output_high(PIN_F1); /* set chip select */
return;
}
/***************************************************************************/
void main()
{
int gain1,gain2,gain3;
initialize(); /* init system */
calibrate(); /* perfrom self offset cal */
/* write to the gain regs */
COMMANDBYTE=0x82; /* command for write to gain */
HIGHBYTE=0x80; /* prepare hi byte */
MIDBYTE=0x00; /* clear mid byte */
LOWBYTE=0x00; /* clear low byte */
write_register(); /* write to gain reg */
/* read from the gain register */
COMMANDBYTE=0x92; /* prepare command byte */
read_register(); /* read the gain reg */
gain1=HIGHBYTE; /* save the output H*/
gain2=MIDBYTE; /* save the output M*/
gain3=LOWBYTE; /* save the output L*/
/* perform single conversions */
while(TRUE)
{
convert();
printf("\n\rGain: H %X M %X L %X\n\r", gain1,gain2,gain3);
printf("Conversion: H %X M %X L %X\n\r", HIGHBYTE, MIDBYTE, LOWBYTE);
delay_ms(10); /* slow down the loop so we can see the output better */
}
}
|
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Sep 04, 2008 1:26 pm |
|
|
I just glanced at your code and I see a problem. You have specified
"fast i/o" mode for all ports, but you have only set the TRIS for port C.
That means other all other pins will be left in the power-on state, which
is as "all inputs". This means, for example, that your Chip Select
signal will not work.
I suggest that you delete all the "fast io" statements and also delete
the set_tris_c() statement. Let the compiler handle setting the TRIS.
It will do it automatically. It's much easier to program the PIC when
you let the compiler handle the TRIS. |
|
|
bela
Joined: 31 Aug 2008 Posts: 27 Location: Bedford, BEDS UK
|
|
Posted: Fri Sep 05, 2008 1:53 am |
|
|
Hello. Thanks for pointing that out.
I could see the chip select toggle on a scope at the correct times but made no difference when I removed the #use fast io and set_tris_c.
Is there anything else apparent?
Thanks. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Sep 05, 2008 2:17 am |
|
|
In two places in your program, you have this test:
Code: | while(!bit_test(LOWBYTE,3))read_register(); |
But the ASM code in AN88 reads the registers before it tests the bit.
I think you should change it to this:
Code: | do{
read_register();
}while(!bit_test(LOWBYTE,3)) |
This needs to be fixed in the convert() and calibrate() routines.
Also, can you post the list of connections for the SPI interface between
the PIC and the Crystal chip ? I want to make sure that SDO on the
PIC goes to SDI on the Crystal chip, etc. Also post the Vdd voltage
that each chip is running at. |
|
|
bela
Joined: 31 Aug 2008 Posts: 27 Location: Bedford, BEDS UK
|
|
Posted: Fri Sep 05, 2008 4:14 am |
|
|
Hello PCM programmer,
Thanks for looking at this for me. The schematic for the micro page is here:
http://www.dazsworld.com/Tl10MicroPage.pdf
There's one other place the the SPI is connected to which is a wireless tansceiver. This device is curently not fitted.
EDIT One other thing: I am aware of the AIN+ and AIN- Double diode problem. The BAV199 is not fitted. Also, The Port Label +5V REF is actually +2.5V REF.
Thanks for your help.
Daz. |
|
|
|
|
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
|