![](templates/subSilver/images/CCSLogo.jpg) |
![CCS C Software and Maintenance Offers](templates/subSilver/images/forumAd6.jpg) |
View previous topic :: View next topic |
Author |
Message |
blowtorch
Joined: 11 Jun 2013 Posts: 35 Location: Cape Town
|
Hardware SPI best practice |
Posted: Thu Jun 21, 2018 7:56 am |
|
|
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? |
|
![](templates/subSilver/images/spacer.gif) |
jeremiah
Joined: 20 Jul 2010 Posts: 1328
|
|
Posted: Thu Jun 21, 2018 8:10 am |
|
|
#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?). |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19359
|
|
Posted: Thu Jun 21, 2018 12:05 pm |
|
|
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. |
|
![](templates/subSilver/images/spacer.gif) |
blowtorch
Joined: 11 Jun 2013 Posts: 35 Location: Cape Town
|
|
Posted: Thu Jun 21, 2018 2:33 pm |
|
|
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. |
|
![](templates/subSilver/images/spacer.gif) |
blowtorch
Joined: 11 Jun 2013 Posts: 35 Location: Cape Town
|
|
Posted: Fri Jun 22, 2018 7:03 am |
|
|
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](images/smiles/icon_smile.gif) |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19359
|
|
Posted: Fri Jun 22, 2018 9:31 am |
|
|
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. |
|
![](templates/subSilver/images/spacer.gif) |
blowtorch
Joined: 11 Jun 2013 Posts: 35 Location: Cape Town
|
|
Posted: Fri Jun 22, 2018 4:50 pm |
|
|
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. |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19359
|
|
Posted: Sat Jun 23, 2018 1:06 am |
|
|
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. |
|
![](templates/subSilver/images/spacer.gif) |
blowtorch
Joined: 11 Jun 2013 Posts: 35 Location: Cape Town
|
|
Posted: Sat Jun 23, 2018 8:56 am |
|
|
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? |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19359
|
|
Posted: Sat Jun 23, 2018 10:24 am |
|
|
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. |
|
![](templates/subSilver/images/spacer.gif) |
blowtorch
Joined: 11 Jun 2013 Posts: 35 Location: Cape Town
|
|
Posted: Thu Jun 28, 2018 3:33 am |
|
|
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... |
|
![](templates/subSilver/images/spacer.gif) |
temtronic
Joined: 01 Jul 2010 Posts: 9170 Location: Greensville,Ontario
|
|
Posted: Thu Jun 28, 2018 4:53 am |
|
|
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 |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19359
|
|
Posted: Thu Jun 28, 2018 7:13 am |
|
|
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..... |
|
![](templates/subSilver/images/spacer.gif) |
blowtorch
Joined: 11 Jun 2013 Posts: 35 Location: Cape Town
|
|
Posted: Thu Jun 28, 2018 9:35 am |
|
|
Agreed - sorry my description was confusing, I meant of course address+data per transaction. |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19359
|
|
Posted: Thu Jun 28, 2018 12:00 pm |
|
|
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. |
|
![](templates/subSilver/images/spacer.gif) |
|
|
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
|