SPI TinyClr

I know TinyClr is not mature, but I’ve problem with SPI.
I want to code a driver for nRF8001 BLE module.
I need to share CS pin between SPI and Gpio. Is there a way to do that ?

In NETMF 4.3, when declaring SPI, in configuration GPIO_NONE can be passed:


_rst = new OutputPort(rstPin, true); 
_req = new OutputPort(reqPin, true); 
_rdy = new InterruptPort(rdyPin, false, Port.ResistorMode.PullUp, Port.InterruptMode.InterruptEdgeLow); 
_spi = new SPI(new SPI.Configuration(Cpu.Pin.GPIO_NONE, false, 0, 0, false, true, 100, spiModule)); 

So CS (_req in code), musn’t be shared.

@ Bauland - Using a SPI chip select as a GPIO could cause problems in several areas:

  1. If the state of the SPI clock, MOSI changes when in GPIO mode, this may cause side effects to the SPI device(s).
  2. Just changing state on the chip select signal could have side effects on the internal state of the SPI device.

Is you design I/O pin bound?

Phil

@ PHITEK - No that’s not my design but design of module…

@ Bauland - Interesting, can you provide a schematic that shows the shared SPI chip select and GPIO. I know your question is about TinyClr not been able to share the these signals. But I want to understand the hardware case for the designing a module where there is a possibility of side-effects.

Phil

Thanks PHITEK to take time.

Full documentation of chip is here: [url]https://www.nordicsemi.com/eng/nordic/download_resource/17534/16/19304811/2981[/url].

Here some extract:
However, nRF8001 does not behave as a pure SPI slave device; nRF8001 can receive new data over-theair
at any time or be busy processing a connection event or new data. Consequently, the traditional CSN
signal used to initiate an SPI transaction is replaced by two active low hand-shake signals; RDYN and
REQN.
These hand shake signals allow nRF8001 to notify the application controller when it has received new data
over-the-air and also to hold new data exchanges initiated by the application controller until it is ready to
accept and process them. The ACI connections are shown in Figure 7.

@ Bauland - After looking at the datasheet for the nRF8001, their use of the “chip select” does have a dual purpose which is a request a transaction and wait for the “RDYN” from the nRF8001 to indicate it is ready for a SPI transfer to begin. This is not a typical SPI bus transaction, hence your request to share a GPIO line with the SPI chip select seemed odd to me.

In looking at the Trr timing (page 26) there is no max. time giving to wait to the “RDYN” signal to indicate that the nRF8001 is ready. Note 1, does say this will take up to the time of a “radio event” plus the typical response time. I could not find any timing for “radio event” so this maximum time to wait is unknown to me. It may exist in the Bluetooth protocol how ever.

I was going to suggest that .NETMF typically sets the SPI chip select active well in advance of the data transfer, but with out knowing what the maximum time to expect before the nRF8001 will respond with “RDYN” this would not be reliable.

The one solution I can see is to build you own SPI bus protocol in software accounting for the “RDYN” pin. Yes would be quite slow.
Another solution would be to select an unused GPIO pin for the nRF8001 chip select line in the SPI.Confifguration (or possibly your code of GPIO_NONE may work), the before starting a SPI bus transaction, have a software bring “REQN” active (low) and then have the software poll for the “RDYN” signal to go active (low) then call the _spi.WriteRead method. There would be some extra delay, as the .NETMF time to assert the fake chip select is kind of wasted, but likely faster then the software SPI solution.

Good luck
Phil

P.S. This was kind of inline with my original post concerns that the SPI chip select can cause the SPI device to change state, at least the vendor described the behavior in your case.

1 Like

from a hardware perspective, can you not jumper two pins together so you can use one as the traditional CS and one as the IO pin?

1 Like

The unused chip select pin declared in the SPI.Configuration statement, in the second solution does not connect to any other device. The software controlled “REQN” signal connects to the nRF8001 chip select. The “REQN” signal request the start of a SPI transaction, the software must then wait for the “RDYN” pin to go active, the “REQN” is also held active (low) till after the WriteRead method returns when it is then returned to the inactive state (hi).

The “REQN” is an output from the .NETMF hardware and the “RDYN” is an input to the .NETMF hardware. It is the “RDYN” signal that causes the issue, as this signal is used to do flow control of the SPI bus transactions.

1 Like

So if I understand, I must have a :
[ul]REQN signal as digital output signal,
RDY signal as input signal,
CS for SPI as an unused pin (just to let SPI think it is a true pin)
MOSI/MISO/CLK as usual SPI pin[/ul]

I prefer to not jumper pin together to avoid case where one is 1 and other is 0 !!!

@ Bauland - Yes, that is correct hardware connections.
You may or may not be able to get away with the GPIO_NONE parameter in the SPI.Configuration statement.

Phil

It is working !!! :dance: :dance:

I received my first event ! After some byte order problems, I can now implement these complete module.

Many thanks to PHITEK, John, and Brett for tips, advices …

If I can propose a functionnality for TinyClr is adding a paramter to specify in which order data are read (Msb or Lsb), it would be very nice. As a workaround, I added this extensions:

 readBuffer)
        {
            spi.Read(readBuffer);

            InvertBytes(readBuffer);
        }

        private static void InvertBytes(byte[] bytes)
        {
            // Iterate over all bytes
            for (var i = 0; i < bytes.Length; i++)
            {
                if (bytes[i] == 0 || bytes[i] == 0xFF)
                    continue;

                byte output = 0;

                for (var j = 0; j < 8; j++)
                {
                    output <<= 1;
                    output |= (byte)(bytes[i] & 0x01);
                    bytes[i] >>= 1;
                }

                bytes[i] = output;
            }
        }

@ Bauland - If you can find somewhere in the existing desktop/IoT UWP API where data order can be specified for SPI, we’d have an easier time implementing it.

@ Bauland - Just curious did the GPIO_NONE work or did you have to use an actual GPIO pin?

Nice work!

Phil

@ John - I find it nowhere that’s why I said it is a suggestion of functionnality :smiley:

@ PHITEK: I use an unused pin as, for now, I can’t find an equivalent to GPIO_NONE in TinyClr.

For the actual reversing of the byte, I found the following to be a nice tradeoff between speed, size and maintainability…

unsigned char reverse(unsigned char b) {
   b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
   b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
   b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
   return b;
}

(http://www.hackersdelight.org/)

A lookup table will probably be the fastest but has the biggest size.
Array stepping is the most understandable (maintainability), but is probably the slowest.

1 Like

Lookup table seems to me a bad idea indeed for microcontroller.
Your idea seems fine. Only test can say what is best solution. I don’t know if I’ll have time to that.