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

SPI #use 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
miro



Joined: 15 Jan 2011
Posts: 62

View user's profile Send private message

SPI #use spi()
PostPosted: Mon Jan 17, 2011 7:25 am     Reply with quote

Hi, I am running a code (r/w to an external fram) with pic24hj128gp502 in 4.104 with this setting:
Code:

...
#pin_select SDI1=PIN_B14
#pin_select SCK1OUT=PIN_B13
#pin_select SDO1=PIN_B12
//#use spi(FORCE_HW) - commented out in 4.104
setup_spi(SPI_MASTER | SPI_H_TO_L | SPI_CLK_DIV_4 | SPI_SS_DISABLED );
setup_spi2( FALSE )
...

and it works well as expected. When using 4.114 and
a) spi(FORCE_HW) is commented out - I read 0x00 only
b) spi(FORCE_HW) is not commented out - I get an error "Option invalid SPI DIN not assigned, use #PIN_SELECT"
c) with #use spi(SPI1,FORCE_HW) - I read "random" data.

I am using this spi_x routine (works well in 4.104 and C30):
Code:

unsigned int16 spi_x(unsigned int16 data_out)
{   
SPI1BUF = data_out;     
while(!SPI1RBF);       
return (SPI1BUF);
}

What shall be the correct setting for #use spi() in 4.114?
Thanks, Miro
FvM



Joined: 27 Aug 2008
Posts: 2337
Location: Germany

View user's profile Send private message

PostPosted: Mon Jan 17, 2011 11:14 am     Reply with quote

Do you expect any advantages from #use SPI?

So far, I see only restrictions, e.g. it's impossible to change speed and SPI modes, as required for some
applications. So I didn't feel yet a need to explore possible bugs related to this function.
ckielstra



Joined: 18 Mar 2004
Posts: 3680
Location: The Netherlands

View user's profile Send private message

PostPosted: Tue Jan 18, 2011 12:20 am     Reply with quote

'#use spi' is supposed to be the 'newer and better' replacement for the setup_spi command. _Never_ use these two SPI configuration commands for the same SPI port or undefined behaviour might be the result. So, your demonstration program is bad coding practice.

The advantage of the '#use spi' directive is that it is more versatile, i.e. it accepts more parameters than the setup_spi command and if the hardware module doesn't support the given parameters, then a software emulation is created. This allows for example to have 12-bit SPI with bit-reversal on a pin that has no hardware SPI support.
The big problem with '#use spi' however is that it is new and most likely contains bugs (at least the last time I checked it in v4.087).

Code:
setup_spi(SPI_MASTER | SPI_H_TO_L | SPI_CLK_DIV_4 | SPI_SS_DISABLED );
The SPI_SS_DISABLED parameter is only valid for a SPI-slave configuration. On a SPI-master it results in an invalid SPI configuration with undefined behaviour. Change to:
Code:
setup_spi(SPI_MASTER | SPI_H_TO_L | SPI_CLK_DIV_4 );


To make this code easier to read it is advised to add a few more defines replacing the incomprehensible CCS defines:
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)
Now your setup code would become:
Code:
setup_spi(SPI_MASTER | SPI_MODE_2 | SPI_CLK_DIV_4 );
Which makes me wonder which FRAM model you are using... The ones I know only support mode 0 & 3... Question
FvM



Joined: 27 Aug 2008
Posts: 2337
Location: Germany

View user's profile Send private message

PostPosted: Tue Jan 18, 2011 2:50 am     Reply with quote

Quote:
The advantage of the '#use spi' directive is that it is more versatile.

May be, if software SPI is intended. With regular hardware SPI, that's based on a well-defined processor functionality, #use spi only introduces another level of indirection, with possible bugs, and as said, some serious restrictions. Speed change is e.g. required for SD interface, mode switch possibly when interfacing different peripherals on a single bus.
miro



Joined: 15 Jan 2011
Posts: 62

View user's profile Send private message

PostPosted: Thu Jan 20, 2011 7:24 am     Reply with quote

Thanks guys, after some experimenting I've learned the best solution is to hardcode it directly:
Code:

..
#WORD SPI1STAT =0x240
#WORD SPI1CON1 =0x242
#WORD SPI1BUF =0x248
#BIT SPI1EN  =0x240.15
#BIT SPI1IF  =0x084.10
#BIT SPI1TBF  =0x240.1
#BIT SPI1ROV  =0x240.6
#BIT SPI1RBF  =0x240.0
..
   SPI1CON1=0x027e;       //setup_spi()
   SPI1EN=1;             // #use spi()

The use of setup_spi() and #use spi() did not provide required results (when compared the inputs to the disassembled outputs). Miro
FvM



Joined: 27 Aug 2008
Posts: 2337
Location: Germany

View user's profile Send private message

PostPosted: Thu Jan 20, 2011 10:38 am     Reply with quote

SPI1CON1=0x027e doesn't correspond to the above said SPI setup, it sets mode 3 with additional late sampling.
SPI_H_TO_L sets mode 2 (SPI1CON1=0x017e) in recent PCD versions, and mode 3 in previous (SPI1CON1=0x07e), e.g. in V4.104. But which SPI mode do you intend?
miro



Joined: 15 Jan 2011
Posts: 62

View user's profile Send private message

PostPosted: Wed Jan 26, 2011 4:18 pm     Reply with quote

@FvM: These two setups work for me:
SPI1CON1=0x007e; // or 0x027e works as well
Using the "setup_spi(SPI_MASTER | SPI_MODE_3 | SPI_CLK_DIV_4 );"
does not work for any "SPI_MODE_x" ..
The Fram FM25H20 needs the spi mode 0 or 3.
Miro
victorcastro89



Joined: 31 Oct 2011
Posts: 11

View user's profile Send private message

How to setup Slave mode?!
PostPosted: Thu Nov 03, 2011 1:26 pm     Reply with quote

I'm new in hardcode, i'm reading datasheets many times and can't configure SPI slave and mode=1 with SS.
Anyone can help me?

miro wrote:
Thanks guys, after some experimenting I've learned the best solution is to hardcode it directly:
Code:

..
#WORD SPI1STAT =0x240
#WORD SPI1CON1 =0x242
#WORD SPI1BUF =0x248
#BIT SPI1EN  =0x240.15
#BIT SPI1IF  =0x084.10
#BIT SPI1TBF  =0x240.1
#BIT SPI1ROV  =0x240.6
#BIT SPI1RBF  =0x240.0
..
   SPI1CON1=0x027e;       //setup_spi()
   SPI1EN=1;             // #use spi()

The use of setup_spi() and #use spi() did not provide required results (when compared the inputs to the disassembled outputs). Miro
miketwo



Joined: 04 Aug 2010
Posts: 24

View user's profile Send private message

PostPosted: Mon Nov 07, 2011 3:39 pm     Reply with quote

Not sure if this will help, but this is the code I use to change spi settings between calls to my flash and my gyro. This is all on a PIC24FJ256GA110, but it should be similar to yours:

Code:

ReturnErr_t SetSPIMode(unsigned char mode)
{
   switch(mode)
   {
      case FLASH_SPI_MODE:
         bit_clear(SPISTAT,15);      // Clear the SPI Enable bit, thus allowing rewrites to the control register.
         SPICON1=0x033F;            // Write the control register with new settings
         bit_clear(SPISTAT,6);      // Clear the SPIROV bit.
         bit_set(SPISTAT,15);      // Set the SPI Enable bit.   
//         sendDBGALL(userPort,"\r\nF Mode");
         return SUCCESS;
         break;
   
      case GYRO_SPI_MODE:
         bit_clear(SPISTAT,15);      // Clear the SPI Enable bit, thus allowing rewrites to the control register.
         SPICON1=0x00FD;      // Write the control register with new settings
         bit_clear(SPISTAT,6);      // Clear the SPIROV bit.
         bit_set(SPISTAT,15);      // Set the SPI Enable bit.   
//         sendDBGALL(userPort,"\r\nG Mode");
         return SUCCESS;
         break;

      default: return FAILURE;  break;
   }
}
victorcastro89



Joined: 31 Oct 2011
Posts: 11

View user's profile Send private message

PostPosted: Tue Nov 08, 2011 9:27 am     Reply with quote

miketwo wrote:
Not sure if this will help, but this is the code I use to change spi settings between calls to my flash and my gyro. This is all on a PIC24FJ256GA110, but it should be similar to yours:

Code:

ReturnErr_t SetSPIMode(unsigned char mode)
{
   switch(mode)
   {
      case FLASH_SPI_MODE:
         bit_clear(SPISTAT,15);      // Clear the SPI Enable bit, thus allowing rewrites to the control register.
         SPICON1=0x033F;            // Write the control register with new settings
         bit_clear(SPISTAT,6);      // Clear the SPIROV bit.
         bit_set(SPISTAT,15);      // Set the SPI Enable bit.   
//         sendDBGALL(userPort,"\r\nF Mode");
         return SUCCESS;
         break;
   
      case GYRO_SPI_MODE:
         bit_clear(SPISTAT,15);      // Clear the SPI Enable bit, thus allowing rewrites to the control register.
         SPICON1=0x00FD;      // Write the control register with new settings
         bit_clear(SPISTAT,6);      // Clear the SPIROV bit.
         bit_set(SPISTAT,15);      // Set the SPI Enable bit.   
//         sendDBGALL(userPort,"\r\nG Mode");
         return SUCCESS;
         break;

      default: return FAILURE;  break;
   }
}


Hello thank you for your help!
but you may not understand my question, I have in PROTEUS simulation of two microcontrollers, one acting as MASTER and the other as SPI.
I succeeded to configure DSPIC33FJ12MC202 as master and slave as PIC18F4550, could send and receive information.
But when trying to use DSPIC33FJ12MC202 as master and another DSPIC33FJ12MC202 as a slave, I had no success. That is I could not use the PDSPIC33FJ12MC202 as Slave.
The registers of the family of PIC24 and DSPIC are the same.
Anyone who has used one of the two as SLAVE could help me?
RckRllRfg



Joined: 20 Jul 2011
Posts: 60

View user's profile Send private message

Got the #SPI working, if your interested...
PostPosted: Tue Nov 08, 2011 12:19 pm     Reply with quote

Hi Everyone -

I've spent a fair amount of time on the SPI for the PIC16F1947. Granted, I have the luxury of the SPI being a one way communication, from the PIC to the slave component.

If is VERY important to note that the #use spi and setup_spi commands are not compatible (nor is setup_spi and #use i2c, but that's another story... Confused )

I originally used a bit banging method to get it to work; however, I really need to use the hardware SPI since I must provide an update via DAC 30,000 times a second. In this case, all I want to do is provide the 16 bit word to the hardware SPI and let the hardware do the rest. I don't want to spend any time hassling with the registers within the code (beyond configuration). But, then again, the limitations of the #use spi may force me to consider otherwise.

Just to note, most of my learning on the SPI was done via O-scope. If you have one, I high recommend that to connect your CS, LOAD, CLK and DATA lines to see what you are sending or receiving.

In my particular case, I am targeting a MCP4822(SPI to DAC). The PIC provides dedicated pins for CLK, DI and DO. My CS pin had to be declared in the #use SPI declaration. The #use SPI would not accept my LOAD signal, claiming that it wasn't allowed when declaring the use of the Hardware SPI. It's a non issue for me, given that I can keep the LOAD signal active at all times;however, if multiple slaves were involved this could be an issue.

I do have some minor issues and I may comment on them in another post. However, it looks like it will do what I need done within my window of 33us (approximately 18us, though I would like to condense this further). I am using compiler version 4.124.

Here's my code, if you wish to give it go. Note that I stripped out other code, so hopefully I did not compromise it. The code is set up to create an ascending staircase on ChannelA and a descending staircase on ChannelB.

Code:


#include <16F1947.h>
#device ICD = TRUE;
#fuses HS,PLL_SW, NOWDT,NOPROTECT,NOLVP
#use delay( clock=20000000 )

// HARDWARE SPI

#define SPI_DO     PIN_C5
#define SPI_DI      PIN_C4
#define SPI_CLK     PIN_C3
#define SPI_CS      PIN_C2
#define SPI_LOAD   PIN_A5

#USE SPI (  FORCE_HW, BITS=16, ENABLE=SPI_CS, SAMPLE_RISE, stream=SPI_STREAM)
#USE SPI (  FORCE_HW, BITS=16, ENABLE=SPI_CS, SAMPLE_RISE, stream=SPI_STREAM2)

/*
FROM THE CCS compiler manual:  SPI options

MASTER Set the device as the master. (default)
SLAVE Set the device as the slave.
BAUD=n Target bits per second, default is as fast as possible.
CLOCK_HIGH=n High time of clock in us (not needed if BAUD= is used). (default=0)
CLOCK_LOW=n Low time of clock in us (not needed if BAUD= is used). (default=0)
DI=pin Optional pin for incoming data.
DO=pin Optional pin for outgoing data.
CLK=pin Clock pin.
MODE=n The mode to put the SPI bus.
ENABLE=pin Optional pin to be active during data transfer.
LOAD=pin Optional pin to be pulsed active after data is transferred.
DIAGNOSTIC=pin Optional pin to the set high when data is sampled.
SAMPLE_RISE Sample on rising edge.
SAMPLE_FALL Sample on falling edge (default).
BITS=n Max number of bits in a transfer. (default=32)
SAMPLE_COUNT=n Number of samples to take (uses majority vote). (default=1
LOAD_ACTIVE=n Active state for LOAD pin (0, 1).
ENABLE_ACTIVE=n Active state for ENABLE pin (0, 1). (default=0)
IDLE=n Inactive state for CLK pin (0, 1). (default=0)
ENABLE_DELAY=n Time in us to delay after ENABLE is activated. (default=0)
DATA_HOLD=n Time between data change and clock change
LSB_FIRST LSB is sent first.
MSB_FIRST MSB is sent first. (default)
STREAM=id Specify a stream name for this protocol.
SPI1 Use the hardware pins for SPI Port 1
SPI2 Use the hardware pins for SPI Port 2
FORCE_HW Use the pic hardware SPI.
*/

//====================================
void main()
{
 unsigned int16 xcount   = 0;
 unsigned int16 ycount   = 0;
 unsigned int8  lobyte   = 0;
 unsigned int8  lobyte2  = 0;
 unsigned int8  hibyte   = 0;
 unsigned int8  hibyte2  = 0;
 unsigned int16 spiword  = 0;
 unsigned int16 spiword2 = 0;

// SPI - Load to be kept low to avoid any loss of processor time.
output_low(SPI_LOAD);

 while(1)
  {

lobyte  = xcount;
lobyte2 = ycount;
hibyte  = xcount>>8;     // Push the high byte of xcount into hibyte   
hibyte2 = ycount>>8;     // Push the high byte of ycound into hibyte   

hibyte  = hibyte & 0x0F;   // Clear the upper nibble   
hibyte  = hibyte | 0x10;   // Write command '1' to enable DACa, 2x Voltage
                           // and preserve the rest of the word
hibyte2 = hibyte2 & 0x0F;  // Clear the upper nibble   
hibyte2 = hibyte2 | 0x90;  // Write command '1' to enable DACa, 2x Voltage

spiword  = make16(hibyte,  lobyte );
spiword2 = make16(hibyte2, lobyte2);

spi_xfer(SPI_STREAM,  spiword );
spi_xfer(SPI_STREAM2, spiword2);

xcount++;
ycount--;

  }
}




RckRllRfg
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Tue Nov 08, 2011 1:12 pm     Reply with quote

Quote:

#USE SPI ( FORCE_HW, BITS=16, ENABLE=SPI_CS, SAMPLE_RISE, stream=SPI_STREAM)
#USE SPI ( FORCE_HW, BITS=16, ENABLE=SPI_CS, SAMPLE_RISE, stream=SPI_STREAM2)

This doesn't completely nail down the setup. Sample rise could mean
either SPI Mode 0 or Mode 3. The compiler defaults to Mode 0.
http://www.totalphase.com/support/kb/10045/#modes

It also doesn't set the SCLK speed. The compiler defaults to Fosc/4,
which would be 20MHz / 4 = 5 MHz for your program.
asmboy



Joined: 20 Nov 2007
Posts: 2128
Location: albany ny

View user's profile Send private message AIM Address

PostPosted: Tue Nov 08, 2011 5:26 pm     Reply with quote

Let me add to all thats been said:

I've done a variety of SPI peripheral drivers, and when the
slave device is input only - I've never had issues with setup_SPI();
and using the hardware send SPI_write function.

And there really is no issue with ANY SPI word length either, in spite of having to clock a byte at a time.

I've clocked 4,8,12,16,20,24,30,32 and 36 bits using the hardware interface.

The secret is in understanding that you can pass MORE bits to an spi device
than it needs - so LONG as the LAST 'n' bits are in the right order.

Once you grasp that notion, delivering those bits with hardware ,
I IMHO, can be the most efficient way to get it done.
RF_Developer



Joined: 07 Feb 2011
Posts: 839

View user's profile Send private message

PostPosted: Wed Nov 09, 2011 3:32 am     Reply with quote

I've been experimenting. Using PCWH 4.125 targetting PIC18F6722 (for no particular reason other than I had a handy test project, and it has two MSSPs) and lookng at the listing file to inspect the assembler produced. These trials confirm what I wrote on another recent thread. That is that setup_spi() and spi_read()/spi_write() use the hardware SPI, #use spi and spi_xfer() implement a software virtual SPI.

More importantly, in my tests, FORCE_HW in #use spi had no effect (it may be a place holder for later use...). No matter what I did if I put in #use spi I got a software implementation. If I used setup spi() I got hardware initialisation code, and spi_read() gave me a hardware transfer.

The MSSP hardware that implements SPI on the PICs is byte based. It can only transfer bytes at a time. If you want to transfer 16 bits then you have to programmatically issue two transfer requests, each transferring a byte. As Asmboy says you can always send more than you need, say 16 bits when you needed a 12 bit transfer, but you cannot send anything other than multiples of 8 bits. You should set the SPI bit rate for hardware SPI as the PIC may (unlikely to be fair but *may*) be able to transfer faster that the external peripherals are able to cope with. SPI peripherals always have a maximum SPI clock rate specified in the datasheet: run the SPI at the speed of the slowest device, or slower. The hardware SPI cannot run faster than 1/4 of the processor clock. In your case at 20MHz that's 5Mbps, which is as fast as most ADCs and DAC can handle. Memory tends to be faster, being typically capable of 20MHz and more, 40-50MHz is not uncommon. PICs simply cannot run the SPI that fast. Crying or Very sad I managed to run an NXP ARM SPI at 20MHz, but that was fully buffered and could transfer blocks of bytes without any software intervention, which is how serial memories work most effectively. The PIC SPI hardware is strictly a byte at a time.

The hardware SPI is fast and processor efficient. It should be able to be fully interrupt driven giving asynchronous (non-blocking) transfers but I've not seen any code that does that, and you'd have to roll your own. 30,000 transfers a second is very fast, suicidally so for a PIC. Its probably doable, just, but the PIC will literally not be doing anything else. I have code that does something that fast, it reads an ADC asynchronously. I read then start the next conversion immediately. The ADC converts while I'm processing the previous sample. I maybe change a few bits - six to a digital attenuator - if required. That's all. I have a CAN interface and that runs on interrupts. If I have to respond to a message the loop stalls for quite a while.

#use spi generates software SPI. That runs much more slowly than the hardware can. This means that speed is rarely an issue and most folk simply don't bother to set the speed and thus will tend to wonder what on earth I was talking about in the paragraph above. I doubt you'd be able to get more than 1Mbps from a software SPI on PIC but I haven't actually timed them to confirm that suspicion. While the PIC is transferring data by software it can't be doing anything else, unlike hardware which can be asynchronous. That's fine for a SPI master, but not so good for an SPI slave.

The software SPI is much more flexible than the hardware. You can transfer any number of bits you like, on any pins. Enables can be done for you. Its simple... but slow. Its not so good for slaves which have to be prepared for transfers at any time, and when they start simply have to keep up with the clock rate, otherwise they corrupt or loose data.

So, why exactly do you need 30,000 DAC updates per second via SPI? That's frighteningly fast for a PIC. There's no way you'll get anywhere close without hardware, and FORCE_HW simply doesn't work (and would be incompatible with sending 16 bits at a time).

RF Developer
RckRllRfg



Joined: 20 Jul 2011
Posts: 60

View user's profile Send private message

I'll will revisit the setup_spi
PostPosted: Wed Nov 09, 2011 8:05 am     Reply with quote

RF_Developer et. al. -

Thank you for the further insight between the #USE SPI and the setup_spi. As a side note, the CCS manual needs to make some clarifications on this subject... but this is why we have the forums.

Quote:

These trials confirm what I wrote on another recent thread. That is that setup_spi() and spi_read()/spi_write() use the hardware SPI, #use spi and spi_xfer() implement a software virtual SPI.


I should have looked at the .lst file since this would have told me if the code is handling the SPI.

I will revisit the setup_spi and let you know how it goes. Every microsecond that I can slice off buys me more time for calculations.

To answer your question about the 30,000 DAC updates, I am under a NDA so I cannot provide the reason for it; however, as you alluded, the chip's primary function is to deal with the 2 DACs. This will all be time interrupt driven.

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
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