Anyone know how to control whether or not the SPI library considers the CS pin active high or active low for any given device? I'm using it on a Due but I'd imagine this question applies to the Uno as well.
I don't have a copy of the SPI library to hand, but isn't SS controlled from outside the library? In other words, you select the required slave and then call the SPI library to perform the transfer(s).
Apparently not anymore. Supposedly it manages bring CS high and low as you issue calls to the transfer method.
Look up the SPI spec, after all the lib just implements the spec.
Mark
Well thank you for the response, but not sure what do with that...hmm, I see you're a bit flippant so I'll respond with sarcasm
Yeah, right...because every chip vendor out there implements "the spec" exactly the same, right? If only it were that simple. The SPI "spec" is not much of a formally developed industry standard like many other standards nor did it come from a standards body such as IEEE. Oh no, instead it's more like Motorola, in traditional Motorola style, went off and did something that others had to work with and yet others sort of loosely adopted. Given that, surely you can do better than "go read the spec."
I already know the "spec." It says CS is active low. Now I'll refer you to the pin-out diagram of the chip I'm stuck with. I've attached it to this message and as you can clearly see, it is active HIGH. Thus my question still remains about how to modify and/or reconfigure the SPI library...which may require SAM3X knowledge since the port/pin output looks substantially different on that platform. I've read the code but I don't know the Due hardware well enough to make sense of it so I was hoping to find someone on here more familiar with it. FYI, I've also posted the same question in the Due forum in the event I'm right and it's going to come down to a hardware issue but I posted it here as well hoping to make the case for the need to be able to configure this regardless of platform since not all SPI devices are created equal
Something like an additional overloaded method that goes like this:
SPI.begin(_pin, level)
So for a CS pin of 4 for example, using a screwy chip like this, one would call:
SPI.begin(4, HIGH)
Then the other single argument pin param version would default to LOW. Or maybe a setCSActiveWhen(level) method or similar. Would be useful to help deal with whacky hardware SPI implementations like we're dealing with here.
The way I read this code from library SPI.cpp, one calls SPI.begin() and it sets SS as an output and high.
You are free after to set it low and use as a High select, or not at all even
void SPIClass::begin() {
// Set SS to high so a connected chip will be "deselected" by default
digitalWrite(SS, HIGH);
// When the SS pin is set as OUTPUT, it can be used as
// a general purpose output port (it doesn't influence
// SPI operations).
pinMode(SS, OUTPUT);
// Warning: if the SS pin ever becomes a LOW INPUT then SPI
// automatically switches to Slave, so the data direction of
// the SS pin MUST be kept as OUTPUT.
SPCR |= _BV(MSTR);
SPCR |= _BV(SPE);
// Set direction register for SCK and MOSI pin.
// MISO pin automatically overrides to INPUT.
// By doing this AFTER enabling SPI, we avoid accidentally
// clocking in a single bit since the lines go directly
// from "input" to SPI control.
// http://code.google.com/p/arduino/issues/detail?id=888
pinMode(SCK, OUTPUT);
pinMode(MOSI, OUTPUT);
}
Please keep in mind I'm using the Due board and the SAM3X implementation has two begin methods and they actually looks like this:
void SPIClass::begin() {
// NPCS control is left to the user
// Default speed set to 4Mhz
setClockDivider(BOARD_SPI_DEFAULT_SS, 21);
setDataMode(BOARD_SPI_DEFAULT_SS, SPI_MODE0);
setBitOrder(BOARD_SPI_DEFAULT_SS, MSBFIRST);
}
void SPIClass::begin(uint8_t _pin) {
uint32_t spiPin = BOARD_PIN_TO_SPI_PIN(_pin);
PIO_Configure(
g_APinDescription[spiPin].pPort,
g_APinDescription[spiPin].ulPinType,
g_APinDescription[spiPin].ulPin,
g_APinDescription[spiPin].ulPinConfiguration);
// Default speed set to 4Mhz
setClockDivider(_pin, 21);
setDataMode(_pin, SPI_MODE0);
setBitOrder(_pin, MSBFIRST);
}
Also, according to the docs, calling transfer with the method that accepts pin, byte, mode arguments supposedly "twiddles" the slave select for you.
My board does actually use PIN 10 but the default on the Due is pin 52 or 53 I think (don't recall now which one.) The problem with that is those pins are all used for digital inputs. I have absolutely no available pins. Using pin 10 for SS would be great because that is the one my board uses. This board and firmware worked great with the UNO. The only problem is IT IS NOT WORKING with the Due and I'm pretty sure it's because on the SAM3X implementation of SPI the transfer method is messing with CS and undoing what I'm doing with it.
I have looked at it and that's where it starts to go south for me. The _pin argument is the SS pin and in my case that is pin 10. So I call it like this:
SPI.transfer(10, b1, SPI_CONTINUE);
SPI.transfer(10, b2);
This is based upon an example right out of the examples provided by the lib itself.
I'm wondering if this line is part of the issue:
uint32_t d = _data | SPI_PCS(ch);
Here is the entire function:
byte SPIClass::transfer(byte _pin, uint8_t _data, SPITransferMode _mode) {
uint32_t ch = BOARD_PIN_TO_SPI_CHANNEL(_pin);
// Reverse bit order
if (bitOrder[ch] == LSBFIRST)
_data = __REV(__RBIT(_data));
uint32_t d = _data | SPI_PCS(ch);
if (_mode == SPI_LAST)
d |= SPI_TDR_LASTXFER;
// SPI_Write(spi, _channel, _data);
while ((spi->SPI_SR & SPI_SR_TDRE) == 0)
;
spi->SPI_TDR = d;
// return SPI_Read(spi);
while ((spi->SPI_SR & SPI_SR_RDRF) == 0)
;
d = spi->SPI_RDR;
// Reverse bit order
if (bitOrder[ch] == LSBFIRST)
d = __REV(__RBIT(d));
return d & 0xFF;
}
I haven't had a chance to chase down what BOARD_PIN_TO_SPI_CHANNEL does.