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

Understanding the SPI in context with CCS coding

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
RckRllRfg



Joined: 20 Jul 2011
Posts: 60

View user's profile Send private message

Understanding the SPI in context with CCS coding
PostPosted: Wed Nov 02, 2011 11:09 am     Reply with quote

Hi everyone -

On my project, I am using a SPI bus to communicate between a PIC16F1947 and a Microchip MCP4822. I am using the 4.124 compiler.

As I am looking at various code examples, I see several different ways of handing this. Based on what I am reading in the forums, there is a bit of a history. I am also looking at the CCS manual. Confusing all of this is the fact that I2C is similar to a point in which both SPI and I2C can share the same pins.

Here are methods I have seen thus far:

Method 1 - Brute Force - The user's code is basically controlling the signals. A perfect example of this is mcp4921.c (found in the PICC/drivers directory) in which all of the signalling is controlled by the use of output_high or output_low. No use of #use SPI or spi_setup in these functions.

Method 2 - spi_setup() function - This is declared one's initiation code. It seems to be the traditional way of declaring the use of the SPI.

Method 3 - #USE SPI - This is declared in the pre-compiler statements. It gives a good deal of options for to the user, in particular, the ability to control enable and load signals; However, I have read in prior postings that it is a new feature and to tread cautiously.

Question 1 - Are the #USE SPI and the spi_setup() function used together? I tend to believe that they are not, given that the code I have seen uses one or the other. Most of the options of each tend to overlap. If you can confirm, that would be appreciated. Are there functions associated to one method that are not associated or best used with the another? (Question 3 may be related)

Question 2 - Why would one use spi_start and spi_stop? I have seen code with and without these commands being used. My target device has simple requirements - it simply stops looks after the 16th bit has been sent and it does NOT have a read function.

Question 3 - spi_xfer seems to be a variable option for sending more than 8 bits (as oppose to the spi_write) which specifically handles 8 bits; however, spi_xfer seems to be associated with #use spi. Am I understanding this correctly? As I have read in prior postings, the tradition way to write is to use spi_write.

Thoughts and commentary on this would be greatly appreciated.

Thanks -

RckRllRfg
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Wed Nov 02, 2011 11:32 am     Reply with quote

Quote:

Are the #USE SPI and the spi_setup() function used together?

No.
Also, setup_spi() is for hardware SPI only. #use spi() can do hardware
or software SPI, but it requires careful specification of the parameters.


Quote:

Are there functions associated to one method that are not associated or
best used with the another?

Use spi_xfer() with #use spi(). All the other functions are used with setup_spi().


Quote:

Why would one use spi_start and spi_stop? I have seen code with and
without these commands being used.

They are not used with CCS. They may be used with some other compiler to enable/disable the SPI module.


Quote:

spi_xfer seems to be a variable option for sending more than 8 bits (as oppose to the spi_write) which specifically handles 8 bits; however, spi_xfer seems to be associated with #use spi. Am I understanding this correctly?

Yes.

Quote:
As I have read in prior postings, the traditional way to write is to use spi_write.

That's right. (with setup_spi() to setup the hardware SPI module first).
RF_Developer



Joined: 07 Feb 2011
Posts: 839

View user's profile Send private message

Re: Understanding the SPI in context with CCS coding
PostPosted: Thu Nov 03, 2011 4:38 am     Reply with quote

RckRllRfg wrote:
Hi everyone -
Method 1 - Brute Force - The user's code is basically controlling the signals.


This is called "bit bashing". Its slow, clumsy and prone to coding errors. Once it was the only way to do it. Now we have hardware to help. BUT if the hardware can't be used then bit bashing allows the use of any handy IO pins. Note CCS provides software SPI, which *is* actually bit bashing but with the hard bit, the coding, done for you "behind the scenes" as it were.

Quote:

Method 2 - spi_setup() function - This is declared one's initiation code. It seems to be the traditional way of declaring the use of the SPI.


Traditional? Not really. Its the CCS way of leveraging the SPI hardware built in to most PICs. Spi_setup() just adds initialisation code for the SPI according to the parameters you specify. You then use spi_read() and spi_write() to do the actual transfers. The key point is that spi_setup() is used in conjunction with spi_read() and spi_write() to use hardware SPI.

Quote:

Method 3 - #USE SPI - This is declared in the pre-compiler statements. It gives a good deal of options for to the user, in particular, the ability to control enable and load signals; However, I have read in prior postings that it is a new feature and to tread cautiously.


I don't know how "new" it is. #use spi causes CCS supplied bit-bashing code to be used for spi transfers. Its in some ways more flexible than the hardware SPI peripheral: for example the hardware is byte based and can only transfer 8 bits at a time. A software (bit bashed) SPI can do almost any number of bits at once. The way to transfer data using software SPI is with spi_xfer().

Some folks prefer to not use the CCS support for SPI, and use the relevant SFRs directly or implement their own bit-bashed SPI support. This may be a hangover from legacy coding - it was the only way to do it in assembler - and also a wariness over using "black boxes" may have something to do with it. Personally I like to leverage whatever I can to make my life simpler and get my product to market sooner and more error free. That's why I program in C these days, and use CCS support facilities whenever I can. I also use hardware support whenever possible.

The SPI and I2C uses the same peripheral hardware module, normally known as a MSSP (MASTER SYNCHRONOUS
SERIAL PORT). That means unless there are two such ports, as provided by some PICS, you cannot do both hardware I2C and SPI. Instead you can the the hardware for one, and software (CCS provided or not as you prefer) for the other. I suggest using the MSSP for I2C and software for SPI as I2C is rather more complicated. One naff side effect of using one peripheral for both is that the setup_spi(SPI_SS_DISABLED) call that the PIC wizard generally inserts is "wrong" and messes up I2C if you try to use it.

RF Developer
RckRllRfg



Joined: 20 Jul 2011
Posts: 60

View user's profile Send private message

Thank you for the details
PostPosted: Thu Nov 03, 2011 10:57 am     Reply with quote

Thank you very much for the responses. These are details that puts it all into perspective.

As for the method, I was fortunate to find code in the CCS driver directory called mcp4921. It is the exact same method of SPI handling that is used by the mcp4822. I like this method because it does give me full control of the signal switching and more importantly, the timing of the signals.

All the best to you in your endeavors,

RckRllRfg
RF_Developer



Joined: 07 Feb 2011
Posts: 839

View user's profile Send private message

Re: Thank you for the details
PostPosted: Fri Nov 04, 2011 2:54 am     Reply with quote

RckRllRfg wrote:
Thank you very much for the responses. These are details that puts it all into perspective.

As for the method, I was fortunate to find code in the CCS driver directory called mcp4921. It is the exact same method of SPI handling that is used by the mcp4822. I like this method because it does give me full control of the signal switching and more importantly, the timing of the signals.

All the best to you in your endeavors,

RckRllRfg


I've used the mcp4822 or at least I inherited code that did. I rewrote the "driver" for it. I did a lot of tidying up. We use setup_spi() and spi_read() and spi_write(). It all works no problem. Certainly simpler and way more portable than direct control. You might be attracted to the idea of having "full control", but it leads to non-portable code and is more difficult to develop.

RF Developer.

Here's our driver. Note I've had to consider hardware timing at differing processor speeds. This runs at both 10MHz and 40MHz on PIC18F8585.

Code:

/*
write_mcp4822.c

This function writes two 12-bit words to a Microchip MCP4822 DAC and then
simultaneously latches the data to the outputs.
The two 16-bit words passed to the function are limited to 0xFFF to avoid any
rollovers.
*/


// Use with:
// 10MHz operation at 40MHz processor clock   
// setup_spi(SPI_MASTER|SPI_H_TO_L|SPI_XMIT_L_TO_H|SPI_CLK_DIV_4);
// Typical Defines for the select pin and latch
// #define DAC_SELECT PIN_C1
// #define latch_dac PIN_C2

void write_mcp4822(int16 DAC_SELECT, int16 value_1, int16 value_2)
{
   // Prepare the first value
   if(value_1 > 0x0FFF)            //Limit value of DAC A value with no rollover
   {
      value_1 = 0x0FFF;
   }

   // Set control bits
   bit_clear(value_1, 15);           // Write to DAC A
   bit_clear(value_1, 13);            // Output gain = 2
   bit_set(value_1, 12);            // Output enabled
   
   //write the first value. The MCP4822 treats its two
   // channels effectively as two DACS. We have to issue TWO
   // SPI sequences. We cannot send one long one with both
   // sets of data.
   output_low(DAC_SELECT);            //select DAC IC /CS
   spi_write(make8(value_1,1));      //write upper byte
   spi_write(make8(value_1,0));      //write lower byte
   output_high(DAC_SELECT);         //unselect DAC /CS   
   
   // Prepare the second
   if(value_2 > 0x0FFF)               //Limit value of DAC B value with no rollover
   {
      value_2 = 0x0FFF;
   }

   // Set control bits
   bit_set(value_2, 15);            // Write to DAC B
   bit_clear(value_2, 13);            // Output gain = 2
   bit_set(value_2, 12);            // Output enabled

   output_low(DAC_SELECT);            //select DAC IC /CS
   spi_write(make8(value_2,1));      //write upper byte
   spi_write(make8(value_2,0));      //write lower byte
   output_high(DAC_SELECT);         //unselect DAC /CS

   // Latch both values onto the analogue outputs
   output_low(latch_dac);
   // At 10MHz there is sufficient time between instructions to
   // meet the MCP4822's timing requirements without any additional
   // delays. The analogue output changed when we drove the latch
   // low. All we need to do now is bring it high again.
   
   // That was true at 10MHz, but not at 40MHz! So we have to add a few NOPs
   // to ensure correct timing. 1 "wait state" gives 200ns at 40MHz, or 500ns at 10MHz                             
   delay_cycles(1);
   output_high(latch_dac);
}
RckRllRfg



Joined: 20 Jul 2011
Posts: 60

View user's profile Send private message

Much Appreciated.
PostPosted: Sat Nov 05, 2011 12:04 am     Reply with quote

RF_Developer -

This is much appreciated. I was able to get the code example provided by CCS up and running for both channels of the mcp4822. So far, so good. However, we need to update this information 30,000 times a second (using a 20MHz clock), so I know that my code will need to be tight. I've been chasing other aspects of the firmware code, so I will need to revisit this soon.

Thank you for the guidance.

Regards -

RckRllRfg
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
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