Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

WRITE READ SPI DATA in Oshonsoft

camerart

Well-Known Member
Hi, EDIT: As addresses appear to be a bad idea, I've changed the thread title
I'm setting up a SLAVE PIC to another MASTER PIC, and want to put a STRING of DATA into a specific memory locations.
So far I've succeded in adding the DATA into EEPROM memory, but is this the best place?
The DATA will be updated constantly and very fast.

Camerart.
 
Last edited:
So you've done what I suggested back in posts #23 and #34. That's a good first start. Congratulations!

If you like, post both sides of the code... if not done properly there are still multiple ways for this to go south.
Does the master set SS low and then high around each byte or does it just do it once around the whole block of 35 bytes?
 
So you've done what I suggested back in posts #23 and #34. That's a good first start. Congratulations!

If you like, post both sides of the code... if not done properly there are still multiple ways for this to go south.
Does the master set SS low and then high around each byte or does it just do it once around the whole block of 35 bytes?
Hi T,
Yes, they are long posts, and difficult to digest, but I suppose my subconsious must have be taking notes.

I'll post them later, as they are not finished yet.

The MASTER C/S-SS the whole block of 35 BYTES., and there are the odd mistakes.

I have other successful peripherals, where the MASTER sends the address of where each BYTE to be READ is and they work without an acknowledgment track, how is this possible?
C
 
Hi T,
Yes, they are long posts, and difficult to digest, but I suppose my subconsious must have be taking notes.

I'll post them later, as they are not finished yet.

The MASTER C/S-SS the whole block of 35 BYTES., and there are the odd mistakes.

I have other successful peripherals, where the MASTER sends the address of where each BYTE to be READ is and they work without an acknowledgment track, how is this possible?
C
What peripherals are they? - for example all I2C devices are just two wire, and you have to include the address of the device you're wanting to use - but SPI uses (at least) 3 wires, with the third wire the chip select for that particular device. So I2C just uses 2 wires, however many devices they are, while SPI use 2 wires, PLUS an extra chip select wire for each device.

But many devices have internal address and registers that you need to select, for example an EEPROM chip needs to be sent the memory address you want to read or write from, or SPI video display drivers have a range of registers you need to address and read/write.
 
An SPI hardware peripheral is different than a master-slave setup with two pics.

The hardware peripheral is MUCH faster since there's no code invoked on the peripheral end.

With a master-slave, the master pic has to allow the slave pic time to read each byte, process it, and prepare what it wants to send back next time before the master can send the next byte.

If you add a 'delay 100us' on the master side each time you send a byte that'll probably help. It'll be slower, but unless you arrange to have that new signal you added hardware-driven it's safer.
 
Hi N and T,
All of the other peripherals ar SPI controlled, using 4xwires C/S-CLOCK-MOSI-MISO. The PIC is now using 5xwires.

The PIC has an EEPROM, as mentioned earlier.

Some of the other peripherals have 1US delays, by experiment, and they all work just fine.
C
 
The PIC has an EEPROM, as mentioned earlier.
That doesn't matter here. Your problem isn't whether it has an address, or a register, or an array, or a message, or a while-wend loop.

It's purely timing in reading bytes as fast as the master sends them and detecting when messages begin and end.

Some of the other peripherals have 1US delays, by experiment, and they all work just fine.
As I would expect. There are ways to speed up the communications in a master-slave arrangement but that's way beyond your skill level. We're already struggle here, so best to just get it working reliably. That's hard enough.
 
An SPI hardware peripheral is different than a master-slave setup with two pics.

The hardware peripheral is MUCH faster since there's no code invoked on the peripheral end.

With a master-slave, the master pic has to allow the slave pic time to read each byte, process it, and prepare what it wants to send back next time before the master can send the next byte.

If you add a 'delay 100us' on the master side each time you send a byte that'll probably help. It'll be slower, but unless you arrange to have that new signal you added hardware-driven it's safer.
Hi P,
I see!
I've change the thread title to suit.
C
 
So you've done what I suggested back in posts #23 and #34. That's a good first start. Congratulations!

If you like, post both sides of the code... if not done properly there are still multiple ways for this to go south.
Does the master set SS low and then high around each byte or does it just do it once around the whole block of 35 bytes?
Hi T,
Here are both sides of the CODES:
The MASTER is receiving the BYTES, and showing them on a computer terminal. There are a couple of issues, but I can correct those, as I look through it. This doesn't affect anything, important.

When you say, 'adding adding 100us will be slower' What is you definition of slower? This must run quickly, say multiple times/second.
C.

I have manually set-up the M2S (MASTER to SLAVE BYTES) in MASTER:
------------------------ MASTER -----------------------------------------------------
m2s(0) = 0 '0=DUMMY +35 BYTES =36 BYTES
m2s(1) = 1 '=X for M2S tests.
m2s(2) = 2
m2s(3) = 3
m2s(4) = 4
m2s(5) = 5
m2s(6) = 6
m2s(7) = 7
m2s(8) = 8
m2s(9) = 9
m2s(10) = 10
m2s(11) = 11
m2s(12) = 12
m2s(13) = 13
m2s(14) = 14
m2s(15) = 15
m2s(16) = 16
m2s(17) = 17
m2s(18) = 18
m2s(19) = 19
m2s(20) = 20
m2s(21) = 21
m2s(22) = 22
m2s(23) = 23
m2s(24) = 24
m2s(25) = 25
m2s(26) = 26
m2s(27) = 27
m2s(28) = 28
m2s(29) = 29
m2s(30) = 30
m2s(31) = 31
m2s(32) = 32
m2s(33) = 33
m2s(34) = 34
m2s(35) = 35


slave4431_cs = 0 'CS ON

While Not s2m_ack
Wend

For spipsn = 0 To 35 'spipsn To 35 '=36 BYTES including 1x DUMMY BYTE 'GPS=31 $-W INC QEI=2 BATVOLT=1 SPARE DATA=1<<£
SSPBUF = m2s(spipsn) '(spipsn)
While Not SSPSTAT.BF
Wend
s2m(spipsn) = SSPBUF '(spipsn) = SSPBUF '£ ADDED
WaitMs 1
Next spipsn

slave4431_cs = 1 'CS OFF
-------------------------------------------------------------------------
S2M has been PARSED from a GPS buffer +QEI readings +2xBYTES
---------------------------- SLAVE -----------------------------------------------
s2m(0) = "£" '£ [TOT 35 BYTES ]
s2m(1) = gps(7) 'Time
s2m(2) = gps(8) 'Time
s2m(3) = gps(9) 'Time
s2m(4) = gps(10) 'Time
s2m(5) = gps(11) 'Time
s2m(6) = gps(12) 'Time
s2m(7) = gps(14) 'Time
s2m(8) = gps(15) 'Time
s2m(9) = gps(19) 'Lat
s2m(10) = gps(20) 'Lat
s2m(11) = gps(21) 'Lat
s2m(12) = gps(22) 'Lat
s2m(13) = gps(24) 'Lat
s2m(14) = gps(25) 'Lat
s2m(15) = gps(26) 'Lat
s2m(16) = gps(27) 'Lat
s2m(17) = gps(27) 'Lat
s2m(18) = gps(28) 'Lat
s2m(19) = gps(30) 'N
s2m(20) = gps(32) 'Lon
s2m(21) = gps(33) 'Lon
s2m(22) = gps(34) 'Lon
s2m(23) = gps(35) 'Lon
s2m(24) = gps(36) 'Lon
s2m(25) = gps(38) 'Lon
s2m(26) = gps(39) 'Lon
s2m(27) = gps(40) 'Lon
s2m(28) = gps(41) 'Lon
s2m(29) = gps(42) 'Lon
s2m(30) = gps(44) 'W
s2m(31) = POSCNTL 'QEIDEGLB
s2m(32) = POSCNTH 'QEIDEGHB
s2m(33) = 12 'Bat volt
s2m(34) = 123 'Spare DATA

If slave4431_cs = 0 Then 'if chip select on do the code otherwise skip

s2m_ack = 1 'Tell MASTER that SLAVE is ready.

For spipsn = 0 To 34 'GPS=30 $-W INC, QEI=2 BATVOLT=1 SPARE DATA=1
m2s(spipsn) = SSPBUF '(spipsn) = SSPBUF 'M2S
While Not SSPSTAT.BF
Wend
SSPBUF = s2m(spipsn) '(spipsn)
Next spipsn

s2m_ack = 0 'Set S2M_wire off till next loop

Endif 'slave4431_cs
----------------------------------------------------------------------------------------
 
Last edited:
On the slave side you've changed the order of operations and you're now reading the SSPBUF before BF is set to indicate there's data to be read.
 
On the slave side you've changed the order of operations and you're now reading the SSPBUF before BF is set to indicate there's data to be read.
Hi T,
As a none programmer, who is programming, I try all sorts of things that probably wouldn't be the accepted way.

In this case, I probably moved lines around till it worked.

How would you do it? I can try it, and post the results.
C
 
I try all sorts of things that probably wouldn't be the accepted way.
In this case, I probably moved lines around till it worked.
Which is how this thread and all the others on all the different forums you post on about the same issue get to be so long and convoluted.

'working' appears to be a relative term here.

How would you do it?
I certainly wouldn't read a register before the hardware told me it had valid data.

If you swap the order then you're going to complain that the transfers are 'off by one' because you don't understand how an SPI transfer works. Write and read happen at the same time... as the master sends a byte it reads the data that's already sitting in the slave's SSPBUF. When the slave receives a byte what it loads into the SSPBUF as a response will get transferred to the master on the NEXT incoming byte.

That means that either:
- you preload the SSPBUF with the first byte BEFORE the master starts a transmission (which means that you have to know in advance what the master wants to read),
or
- the master sends a dummy byte, knowing that the response will always be one byte out-of-step. The slave has to know about the dummy transfer so that it doesn't think it's real data from the master.

Since you now have a line that goes back to the master to say "I'm ready", when the master lowers the SS the slave can load the SSPBUF with the first byte before it raises the 'ready' line letting the master know it can continue.

The slave should have the SS input enabled in the init_spi routine. That way when the master sets SS high the slave bit counter will reset. You might also consider exiting the slave 'While Not SSPSTAT.BF - WEND' and FOR-NEXT loops if it sees the SS high... that's an indication something bad happened and you didn't get all the bytes you were expecting.
 
Which is how this thread and all the others on all the different forums you post on about the same issue get to be so long and convoluted.

'working' appears to be a relative term here.


I certainly wouldn't read a register before the hardware told me it had valid data.

If you swap the order then you're going to complain that the transfers are 'off by one' because you don't understand how an SPI transfer works. Write and read happen at the same time... as the master sends a byte it reads the data that's already sitting in the slave's SSPBUF. When the slave receives a byte what it loads into the SSPBUF as a response will get transferred to the master on the NEXT incoming byte.

That means that either:
- you preload the SSPBUF with the first byte BEFORE the master starts a transmission (which means that you have to know in advance what the master wants to read),
or
- the master sends a dummy byte, knowing that the response will always be one byte out-of-step. The slave has to know about the dummy transfer so that it doesn't think it's real data from the master.

Since you now have a line that goes back to the master to say "I'm ready", when the master lowers the SS the slave can load the SSPBUF with the first byte before it raises the 'ready' line letting the master know it can continue.

The slave should have the SS input enabled in the init_spi routine. That way when the master sets SS high the slave bit counter will reset. You might also consider exiting the slave 'While Not SSPSTAT.BF - WEND' and FOR-NEXT loops if it sees the SS high... that's an indication something bad happened and you didn't get all the bytes you were expecting.
Hi T,
I expect the 1st (0 BYTE to be a DUMMY) and the next BYTE to be a £ (163), which it is as can be seen in this data analysis.

My logic is that the MASTER sends it's M2S and when the SLAVE wakes up it receives the M2S and exchanges it's S2M, so this is the order I put them.

It will take me some time, to figure out from what you say, what the errors are.
C
 

Attachments

  • £.jpg
    £.jpg
    189.9 KB · Views: 128
On the slave side, as long as you don't care what the master's sending (which seems to be the case here), I suppose it doesn't matter if the slave reads garbage. You don't even need the m2s() array... you could replace that with 'WREG = SSPBUF' and it should give the same result. You do need to read SSPBUF so that it clears the BF flag, but you don't need to do anything with the result.

The 'delayms' call on the master side all depends on what else the slave might be doing. If there are no interrupts then that could be reduced to just a few usecs. If the slave is using interrupts (for something else) then that delay call would have to be as long as the worst-case interrupt execution time.
 
On the slave side, as long as you don't care what the master's sending (which seems to be the case here), I suppose it doesn't matter if the slave reads garbage. You don't even need the m2s() array... you could replace that with 'WREG = SSPBUF' and it should give the same result. You do need to read SSPBUF so that it clears the BF flag, but you don't need to do anything with the result.

The 'delayms' call on the master side all depends on what else the slave might be doing. If there are no interrupts then that could be reduced to just a few usecs. If the slave is using interrupts (for something else) then that delay call would have to be as long as the worst-case interrupt execution time.
Hi T,
Fort this pair of PICs, I don't think M2S is needed, the example I posted is an example, and a reference for me to watch in the analyser.

The GPS interrupts the SLAVE approx 5/sec and the Incremental endoder needs to count when moved, using an interrupt. I'll look deeper at them.
C.
 
Hi T,
I'm re-reading #51

Where you say:
That means that either:
- you preload the SSPBUF with the first byte BEFORE the master starts a transmission (which means that you have to know in advance what the master wants to read),


In #48, the 2x arrays M2S and S2M buffers hold the 2xDATA that is to be exchanged, as it is known before hand, it can be pre-loaded beforehand.
Does this mean that the method in #48 is ok.

I tried reversing the order to how it was earlier, and both work fine, why?

I've commented this, is it correct?
-----------------------------------------------------------
For spipsn = 0 To 35 'GPS=30 $-W INC, QEI=2 BATVOLT=1 SPARE DATA=1
SSPBUF = s2m(spipsn) 'Transfer S2M(spipsn) into SSPBUF
While Not SSPSTAT.BF 'Wait for M2S(spipsn) BYTE to transfer into SSPBUF
Wend
m2s(spipsn) = SSPBUF 'Transfer M2S(spipsn) BYTE from SSPBUF into M2S(spipsn)
Next spipsn
----------------------------------------------------------

C
 
Last edited:
Hi,
I tried a few itterations around my interpretation of #51

This one doesn't block but exchanges random BYTES instead of the correct ones, in each buffer.
C
==============================MASTER ==========================================
slave4431_cs = 0 'CS ON

While Not s2m_ack 'Wait for SLAVE S2M buffer to fill
Wend

For spipsn = 0 To 35 'spipsn To 35=36 BYTES including 1x DUMMY BYTE-GPS=31 $-W-QEI=2-BATVOLT=1-SPARE DATA=1
SSPBUF = m2s(spipsn) 'Transfer M2S(spipsn) BYTE into SSPBUF
While Not SSPSTAT.BF 'Wait for M2S BYTE to fill buffer
Wend
s2m(spipsn) = SSPBUF 'Transfer SSPBUF BYTE into S2M(spipsn)
Next spipsn

slave4431_cs = 1 'CS OFF


=========================== SLAVE ====================================================




If slave4431_cs = 0 Then 'if chip select on do the code otherwise skip

For spipsn = 0 To 35 'GPS=30 $-W INC, QEI=2 BATVOLT=1 SPARE DATA=1
SSPBUF = s2m(spipsn) 'Transfer S2M(spipsn) into SSPBUF
While Not SSPSTAT.BF 'Wait for M2S(spipsn) BYTE to transfer into SSPBUF
Wend
s2m_ack = 1 'Tell MASTER that SLAVE is ready.
m2s(spipsn) = SSPBUF 'Transfer M2S(spipsn) BYTE from SSPBUF into M2S(spipsn)
s2m_ack = 0 'Set S2M_ACK off till next loop
Next spipsn

Endif 'slave4431_cs

===============================================================================
 
Try this and see what happens.
With this one the master and slave use the READY/ack signal around each byte, which is what I think you were trying to do. It shouldn't need any dummy bytes. I also added an exit on the slave side in case something goes wrong and the master raises the CS in the middle of the transfer.

There's still a possible race condition in this...

Code:
==============================MASTER ==========================================
slave4431_cs = 0 'CS ON

For spipsn = 0 To 35     'spipsn To 35=36 BYTES including 1x DUMMY BYTE-GPS=31 $-W-QEI=2-BATVOLT=1-SPARE DATA=1
    While Not s2m_ack    'Wait for SLAVE to set its SSPBUF with valid S2M buffer data
    Wend
    SSPBUF = m2s(spipsn) 'Transfer M2S(spipsn) BYTE into SSPBUF
    While Not SSPSTAT.BF 'Wait for M2S BYTE to fill buffer
    Wend
    s2m(spipsn) = SSPBUF 'Transfer SSPBUF BYTE into S2M(spipsn)
    waitus 10
Next spipsn

slave4431_cs = 1 'CS OFF

=========================== SLAVE ====================================================
' be sure to set the ack output low as part of the setup code
s2m_ack = 0


If slave4431_cs = 0 Then    'if chip select on do the code otherwise skip
    For spipsn = 0 To 35    'GPS=30 $-W INC, QEI=2 BATVOLT=1 SPARE DATA=1
        SSPBUF = s2m(spipsn) 'Transfer S2M(spipsn) into SSPBUF
        s2m_ack = 1          'Tell MASTER that SLAVE is ready
        While Not SSPSTAT.BF 'Wait for M2S(spipsn) BYTE to transfer into SSPBUF
            If slave4431_cs = 1 Then    ' this should not happen, but if it does error
                Exit For
            Endif
        Wend
        s2m_ack = 0          'Set S2M_ACK off till next loop
        m2s(spipsn) = SSPBUF 'Transfer M2S(spipsn) BYTE from SSPBUF into M2S(spipsn)
    Next spipsn
Endif 'slave4431_cs
 
Try this and see what happens.
With this one the master and slave use the READY/ack signal around each byte, which is what I think you were trying to do. It shouldn't need any dummy bytes. I also added an exit on the slave side in case something goes wrong and the master raises the CS in the middle of the transfer.

There's still a possible race condition in this...

Code:
==============================MASTER ==========================================
slave4431_cs = 0 'CS ON

For spipsn = 0 To 35     'spipsn To 35=36 BYTES including 1x DUMMY BYTE-GPS=31 $-W-QEI=2-BATVOLT=1-SPARE DATA=1
    While Not s2m_ack    'Wait for SLAVE to set its SSPBUF with valid S2M buffer data
    Wend
    SSPBUF = m2s(spipsn) 'Transfer M2S(spipsn) BYTE into SSPBUF
    While Not SSPSTAT.BF 'Wait for M2S BYTE to fill buffer
    Wend
    s2m(spipsn) = SSPBUF 'Transfer SSPBUF BYTE into S2M(spipsn)
    waitus 10
Next spipsn

slave4431_cs = 1 'CS OFF

=========================== SLAVE ====================================================
' be sure to set the ack output low as part of the setup code
s2m_ack = 0


If slave4431_cs = 0 Then    'if chip select on do the code otherwise skip
    For spipsn = 0 To 35    'GPS=30 $-W INC, QEI=2 BATVOLT=1 SPARE DATA=1
        SSPBUF = s2m(spipsn) 'Transfer S2M(spipsn) into SSPBUF
        s2m_ack = 1          'Tell MASTER that SLAVE is ready
        While Not SSPSTAT.BF 'Wait for M2S(spipsn) BYTE to transfer into SSPBUF
            If slave4431_cs = 1 Then    ' this should not happen, but if it does error
                Exit For
            Endif
        Wend
        s2m_ack = 0          'Set S2M_ACK off till next loop
        m2s(spipsn) = SSPBUF 'Transfer M2S(spipsn) BYTE from SSPBUF into M2S(spipsn)
    Next spipsn
Endif 'slave4431_cs
Hi T,
Success!
There is a DUMMY BYTE to the left of £(163) then the time14;21:48 (Uk = +1)
That's fine, I can READ 1 instead of 0,
Thanks.

This is ok for me, but before I move on, I'm looking at other SPI error checks in the D/S, in case they should be added.

EDIT: Does [ Exit for ] mean Exit the 'for next' LOOP?
C
 
Last edited:
Does [ Exit for ] mean Exit the 'for next' LOOP?
Supposedly it does, but I can't check it...
Exit For statement provides a way to exit a For-Next loop. It transfers control to the statement following the Next statement.

I'm looking at other SPI error checks in the D/S, in case they should be added.
In SPI mode there's WCOL and SSPOV, neither of which should occur if the software is written properly.
SSPOV means the slave missed receiving a byte, and since every byte now has a 'ready' handshake around it that should never happen. That said, defensive programming is always a good thing.

One thing to watch out for is the slave 'ready/ack' signal doesn't get lowered until the BF 'while-wend' loop exits. If the slave is busy with other things (ie interrupts) that can still be an issue.

One way around that is to have the ready/ack signal driven by hardware so that it automatically gets cleared when it detects an SPI CLK. To do that I typically use a TMR + CCP module in compare mode to count external SPI clocks... connect SPI CLK -> TMR external input, and CCP out -> ready signal.
 
Supposedly it does, but I can't check it...



In SPI mode there's WCOL and SSPOV, neither of which should occur if the software is written properly.
SSPOV means the slave missed receiving a byte, and since every byte now has a 'ready' handshake around it that should never happen. That said, defensive programming is always a good thing.

One thing to watch out for is the slave 'ready/ack' signal doesn't get lowered until the BF 'while-wend' loop exits. If the slave is busy with other things (ie interrupts) that can still be an issue.

One way around that is to have the ready/ack signal driven by hardware so that it automatically gets cleared when it detects an SPI CLK. To do that I typically use a TMR + CCP module in compare mode to count external SPI clocks... connect SPI CLK -> TMR external input, and CCP out -> ready signal.
Hi T,
Ok, I'll look into it, and try to test if EXIT works.
C
 

Latest threads

New Articles From Microcontroller Tips

Back
Top