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 support@ccsinfo.com

Hardware SPI best practice
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
blowtorch



Joined: 11 Jun 2013
Posts: 35
Location: Cape Town

View user's profile Send private message

Hardware SPI best practice
PostPosted: Thu Jun 21, 2018 7:56 am     Reply with quote

There have been a few posts recently regarding SPI, software vs hardware, #use SPI vs setup_spi, etc.

In the various posts there have also been examples provided that show the different usage scenarios. In this post https://www.ccsinfo.com/forum/viewtopic.php?t=56039&highlight=setupspi Ttelmah explains how #use spi is better and the differences between #use SPI and setup_spi.

What has confused me however in the provided examples, is I do not see the chip select line being used explicitly to select the appropriate SPI device. I have no problem selecting the various pins using #pin_select for SDI/SDO clock etc, but where does one choose the "chip select" pin, and is it pulled low automatically, or does one still do this manually?

I am using a PIC16LF18857 with 2 SPI devices sharing the SPI lines, each with their own chip select lines. At this point I am busy writing a library for the devices, and would like to follow best practice in the SPI routines. Can one call #use SPI twice for the same hardware, specifying a stream name and different chip select, or does one call it once and pull the appropriate line low when transferring data?
jeremiah



Joined: 20 Jul 2010
Posts: 1314

View user's profile Send private message

PostPosted: Thu Jun 21, 2018 8:10 am     Reply with quote

#use spi() has an enable option, but my experience on our versions of the compiler is that it is only low for a single byte (if you are using 8 bit mode), 2 bytes (if you are using 16bit mode), 3 bytes (if you are using 24bit mode), or 4 bytes (if you are using 32 bit mode). This is fine for some devices, but most of the ones I use have varying commands with different lengths, so I have to use a manual pin to do it (how else will the device or compiler know when a command is finished to raise the chip select again?).
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Thu Jun 21, 2018 12:05 pm     Reply with quote

The normal thing with a select, is you just drop it yourself (output_low), do the transactions (remember to read the last transaction to ensure it has completed), and then raise it again.
blowtorch



Joined: 11 Jun 2013
Posts: 35
Location: Cape Town

View user's profile Send private message

PostPosted: Thu Jun 21, 2018 2:33 pm     Reply with quote

Thanks Gents - agreed on dropping select prior to transacting. Previously I have used setup_spi and my read / write functions manually set the select line. I have not used #use SPI previously.

What I found confusing were these working examples of code in posts I mentioned, where #use SPI is used to set up SPI, and there is no use of chip select in the transact routines.

This then led me to assume the compiler had used the associated hardware default chip select line, as it would for SDI, SDO and CLK.
blowtorch



Joined: 11 Jun 2013
Posts: 35
Location: Cape Town

View user's profile Send private message

PostPosted: Fri Jun 22, 2018 7:03 am     Reply with quote

Further to this issue, sometimes one wishes to turn off SPI completely. For example, I have a peripheral RF module accessed via SPI that I wish to put to sleep (actually sometimes I wish to put it to sleep properly, like with a hammer). Anyway to continue, lowest current in sleep is achieved by holding SDI and SCK low, and SDO and CS high. Its not mentioned anywhere in the datasheet for the 16F18857 but presumably port output priority would prevent me manually driving the pins used by SPI.

So, the plan is to use spi_init(0) to disable the module, giving me back control of the port pins. Thereafter to wake up, call call spi_init(1000000) to re-initialise the SPI module at 1MHz, and for SPI to take back control of the port pins.

Correct strategy? Out of interest, in the help file CCS cunningly don't cross reference the other spi commands used in conjunction with #use SPI other than spi_xfer() Smile
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Fri Jun 22, 2018 9:31 am     Reply with quote

You can drive the pins _before_ turning off the SPI. The output latch is loaded, so when you switch the SPI 'off' the line goes to the state defined in the latch.

So:
Code:

   output_high(SCK); //set the SCK output latch to 1
   spi_init(FALSE); //turn off the SPI - SCK is now high


Normally only the clock line actually matters, since data is only transferred on this changing.

For your shutdown application just set the pins as you need for the low power on the RF module.
blowtorch



Joined: 11 Jun 2013
Posts: 35
Location: Cape Town

View user's profile Send private message

PostPosted: Fri Jun 22, 2018 4:50 pm     Reply with quote

Thanks Ttelmah. I will give it a go, and check results using scope / meters etc.

I am using the MRF89XAM9A and the datasheet for this says to pull some of the SPI lines low, and others high for the lowest current draw when the RF module is not required and sleeping.

First time using the module, no libraries available so I am trying to be proactive and putting together the spec for a module library as I work through the datasheet.

The RF module is an add on for an existing product, which does not have a physical on / off switch. Power off is selected via a menu and the PIC then shuts down pretty much everything, and goes to sleep, waking on a switch press.

I am right at the beginning of this project. Instead of diving head first into writing code, I have been working through the module and chip datasheets and making notes, several pages of notes at this point, as well as outlining various functions that will be required. At this point I have created various register read / write functions and a "module sleep" function that puts the RF module to sleep. This uses standard register commands via SPI to instruct the module to sleep, then disables SPI, then sets the various pins used to drive SPI into the relevant low current draw state, as per module datasheet.

Everything is working well so far on the stand alone version of the product that does not have the RF module. The trick is to integrate the RF module to allow comms to a master.
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Sat Jun 23, 2018 1:06 am     Reply with quote

Yes some modules are fun!..

In this case is I see that the 'sleep' state has CS high. Now since the chip should ignore everything happening on the other lines when this is high, this is really the 'key' line. I'd actually program this line high right at the start of your code before using the SPI at all. You can program all the lines to the required 'sleep' state at the start of your code, then whenever the SPI is disabled, this will be the state they go to. The only one you will be manually operating will be this CS line, and the TRIS for SDI. So:
Code:

//At start of your code:
output_low(SCK);
output_high(SDO);
output_high(CS);

//Then when you want to sleep:
output_high(CS);
spi_init(FALSE);
output_low(SDI);

//Then when you want to wake:
output_high(CS); //should already be high but better safe....
spi_init(1000000);
output_float(SDI);

Now on most of the PIC24/30's, when a peripheral is enabled, it overrides the TRIS. On your chip it doesn't. So you need to ensure that TRIS is set to '1' for the SDI line before using the SPI. Hence the output_float, after enabling the SPI.
blowtorch



Joined: 11 Jun 2013
Posts: 35
Location: Cape Town

View user's profile Send private message

PostPosted: Sat Jun 23, 2018 8:56 am     Reply with quote

Thanks Ttelmah. That is pretty much what I am doing, but extended to cover the other lines. From the manual: To achieve minimum Sleep mode current, the SDI pin (pin 17) and SCK pin (pin 18) must be held logic low, while CSCON pin (pin 14), CSDAT pin (pin 15) and
SDO pin (pin 16) must be held logic high.

The 2 CS lines are set high at start, and only ever pulled low when communicating, so no need to explicitly set them again for sleep but as you say better safe. So extended the code for the other lines...

Code:
//At start of your code:
output_low(SCK);
output_high(SDO);
output_high(CSDAT);
output_high(CSCON);

//Then when you want to sleep:
output_high(CSDAT);
output_high(CSCON);
spi_init(FALSE);
output_low(SDI);
output_high(SDO);
output_low(SCK);
 

//Then when you want to wake:
output_high(CSDAT);
output_high(CSCON);  //should already be high but better safe....
spi_init(1000000);
output_float(SDI);
output_float(SDO);



Does it not make more sense to set SDI and SDO as inputs before the call to spi_init?
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Sat Jun 23, 2018 10:24 am     Reply with quote

These are inputs to the PIC from the SPI device.
Now on your chip it is not clear what would happen if you set the TRIS to output while they are still connected to the SPI input. Probably a bit of random input data seen, though not clocked into the latch without a clock. I suspect it is safer to only drive them after the SPI is disconnected.
The output pins require the TRIS already set to output, and the pin diagrams show the data latch only being connected when the peripheral is disconnected, which is therefore OK.
blowtorch



Joined: 11 Jun 2013
Posts: 35
Location: Cape Town

View user's profile Send private message

PostPosted: Thu Jun 28, 2018 3:33 am     Reply with quote

Further to the above, picked up a strange problem when using SPI via #use SPI, to talk to a MRF89XA module...

The datasheet for the MRF89XA module says the following: When writing more than one register successively, it is not compulsory to toggle CSCON back high between two write sequences. The bytes are alternatively considered as address and value. In this instance, all new values become effective on the rising edge of CSCON.

On initial setup, one writes all 32 registers consecutively, so I created "normal" register read/write routines, and a "special" routine for setting all 32 registers on initialization where CS is dropped at the start, the 32 registers are written and CS brought back high. Problem was that the module was not working, so after some head scratching I wrote some code to dump all registers to a terminal, initialized the registers, dumped then again, and noted they were NOT as I had just set them. Very strange. I then wrote them 1 byte at a time, with each byte as a separate transaction (CS low, write, CS high), and they all OK.

I found this very strange, and I have not determined whether the fault lies in the MRX89XA or whether its perhaps some timing / sync issue with the SPI routines. I checked and rechecked my code to see if I was doing something silly but it all looked ok.

I am not spending more time on this, I just thought I would post it here in case it helps someone else. I see lots of posts with people battling to get the module working...
temtronic



Joined: 01 Jul 2010
Posts: 9081
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Thu Jun 28, 2018 4:53 am     Reply with quote

First, I'm happy you got a working solution to your problem ! Hope you have some hair left !!
Second, It seems the designers of 'chips' these days don't really test them well enough before they go to market. Admittedly the chips 4 decades ago were simpler BUT for the most part what was in the datasheet was accurate and you could wirewrap a bunch of parts up and they'd work as expected.

Jay
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Thu Jun 28, 2018 7:13 am     Reply with quote

I must admit from the description, I'd be dropping CS, writing an address byte, then a data byte, and then raising CS again. So two bytes per transaction. Having to be dropped for each one, seems to imply it is acting like a latch bit (which is what SPI_XFER can operate directly). Not at all like a normal CS.
As Jay says, glad you have got it working.....
blowtorch



Joined: 11 Jun 2013
Posts: 35
Location: Cape Town

View user's profile Send private message

PostPosted: Thu Jun 28, 2018 9:35 am     Reply with quote

Agreed - sorry my description was confusing, I meant of course address+data per transaction.
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Thu Jun 28, 2018 12:00 pm     Reply with quote

I went and pulled the data sheet from MicroChip, and it disagrees with what you post (but also agrees)!...
Quote:

Note: It is compulsory to toggle CSDAT back
high between each byte written. The byte
is pushed into the FIFO on the rising edge
of CSDAT


It says you can use continuous operation to _read_.

Quote:

Note: When reading more than one register successively,
it is not compulsory to toggle
CSCON back high between two read
sequences. The bytes are alternatively
considered as address and value.


It has the comment you post, higher up the code, but this is what it says as a note on the write section.
Looks as though they don't know themselves!.

Ammusingly it also says:
Quote:

Note: It is recommended to toggle CSDAT back
high between each byte read.


So for the reads they are also saying you both can and can't use continuous. Allowed but not recommended.
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