CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

PIC18F8722 SPI
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
bela



Joined: 31 Aug 2008
Posts: 27
Location: Bedford, BEDS UK

View user's profile Send private message Visit poster's website

PIC18F8722 SPI
PostPosted: Sun Aug 31, 2008 5:23 am     Reply with quote

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

View user's profile Send private message

PostPosted: Sun Aug 31, 2008 5:58 am     Reply with quote

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

View user's profile Send private message

PostPosted: Sun Aug 31, 2008 6:39 am     Reply with quote

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

View user's profile Send private message Visit poster's website

RE SPI
PostPosted: Sun Aug 31, 2008 1:02 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Sun Aug 31, 2008 1:50 pm     Reply with quote

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

View user's profile Send private message Visit poster's website

PostPosted: Mon Sep 01, 2008 1:59 am     Reply with quote

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







PostPosted: Mon Sep 01, 2008 3:15 am     Reply with quote

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

View user's profile Send private message Visit poster's website

PostPosted: Mon Sep 01, 2008 6:06 am     Reply with quote

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

View user's profile Send private message Visit poster's website

PostPosted: Wed Sep 03, 2008 7:51 am     Reply with quote

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

View user's profile Send private message

PostPosted: Wed Sep 03, 2008 11:36 am     Reply with quote

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

View user's profile Send private message Visit poster's website

PostPosted: Thu Sep 04, 2008 10:23 am     Reply with quote

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

View user's profile Send private message

PostPosted: Thu Sep 04, 2008 1:26 pm     Reply with quote

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

View user's profile Send private message Visit poster's website

PostPosted: Fri Sep 05, 2008 1:53 am     Reply with quote

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

View user's profile Send private message

PostPosted: Fri Sep 05, 2008 2:17 am     Reply with quote

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

View user's profile Send private message Visit poster's website

PostPosted: Fri Sep 05, 2008 4:14 am     Reply with quote

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.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
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