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 CCS Technical Support

Buffer for spi_xfer?

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
SkeeterHawk



Joined: 16 Oct 2010
Posts: 41
Location: Florissant, CO

View user's profile Send private message

Buffer for spi_xfer?
PostPosted: Thu Mar 27, 2025 5:13 pm     Reply with quote

Hello again, fellow programmers.

I have been trying to utilize the SPI hardware on the PIC on my current project because I have varying data sizes, and a bunch of peripherals to talk to quickly.

Both the Master and the Slave are PIC18F57Q84

My issue is with my Slave. When I am writing data to it, the register gets populated just fine as seen on the debugger, but when I go to read that data back to my Master, it transmits the address of the register that I am trying to read. There is obviously a buffer that I need to clear as it is completely disregarding the Data component of my spi_xfer statement. Here is my initialization code for this port:
Code:
#pin_select SS1IN=PIN_D0
#pin_select SDO1=PIN_C2
#pin_select SCK1IN=PIN_C3
#pin_select SDI1=PIN_C4
#use spi (STREAM = SPI, SLAVE, SPI1, ENABLE=PIN_D0, MODE=3)


And here is the code that is sending or receiving data:
Code:
void  CheckPumpCommunication(void)
{
   int8  AddressIn,                                                        //  Register to hold the address byte that the Pump Master is sending
         MaskedAddress;                                                    //  We mask the R/W bit, and then use this register for the basic address

   if(input(PumpMaster_CS))                                                //  Check to see if the Pump Master is asserting our Chip Select input, and if it is high (not asserted) return back to the calling program
   {
      return;                                                              //  Nope, no chip select here, so head on back
   }
   AddressIn = spi_xfer(SPI, 0x00, 8);                                     //  Load the address register with the data that is on the SPI bus so we can see where we go from here
   MaskedAddress = (AddressIn & (0xFF ^ Bit_SPI_Mask_Read));               //  Mask the MSB of the address to nullify the R/W bit so we can see what the root address is
   switch(MaskedAddress)
   {
      case 1:                                                              //  There is no case "0" because that is the present/not-preent bit that is kept local to the Main Pump Board, so we are starting with the Valve Set Point byte
         if ((AddressIn & Bit_SPI_Mask_Read) > 0)                          //  Check to see if the MSB is set, meaning that this is a Read operation
         {
            spi_xfer(SPI, Reg_ValveSP, 8);                                 //  Send the current valve setpoint over to the Pump Master Module
         }
         else                                                              //  If the MSBit is not set, this must be a Write operation
         {
            Reg_ValveSP = spi_xfer(SPI, 0x00, 8);                          //  Read the register that the Pump Master wants to write to the current valve setpoint
            SetUpValves();                                                 //  Call the routine to set the valves to the current state
         }
         break;


So, the MSB is my "Read" bit and the whole address is one byte. When I write this register, I am sending 0x01, and when reading I am sending 0x81. When writing, the register gets written perfectly, but every time I read on any register, it only reads back the address of the register, so if I read 0x01, it will output 0x81 to the Master...which is the address for the Read command for that register. If I read a 16-bit register, there is still an 8-bit address, so it comes out with the address as the MSByte and zeros as the remaining two nybbles. I have tried to write a HEX number to the Master and the address is still sent. I removed my references to the data size and stream name, and it still didn't work with just the data component.

If I was writing this in assembly, I would know where to go by clearing the Buffer Full Flag and making sure that the Buffer had the right data, but I am not sure where to go from here with the spi_xfer command? I didn't see in the documentation that it has the ability to "echo" the previous command.

I am using compiler version 5.114.
_________________
Life is too short to only write code in assembly...
Ttelmah



Joined: 11 Mar 2010
Posts: 19765

View user's profile Send private message

PostPosted: Fri Mar 28, 2025 7:58 am     Reply with quote

You have a fundamental misunderstanding of the difference between slave
select, and enable.

In SPI, on a slave, the slave select line is a signal that needs to go true
before a transaction to say that the slave hardware should be selected on
this device. The enable line is a line operated on a per byte basis to
synchronise the transferred bytes. You are trying to use the slave select
line as an enable.
SkeeterHawk



Joined: 16 Oct 2010
Posts: 41
Location: Florissant, CO

View user's profile Send private message

PostPosted: Fri Mar 28, 2025 11:42 am     Reply with quote

Quote:
You have a fundamental misunderstanding of the difference between slave
select, and enable.

You are absolutely right, Ttelmah. I have been reading the forum like a crazy person and this post (https://www.ccsinfo.com/forum/viewtopic.php?t=60199&highlight=spi) made me think that I had to assign the Enable to the same pin as the SS.

I am assuming that I don't need the Enable line then, but pardon my ignorance, I am not understanding how a framing signal for a byte would cause the buffer to echo the last command. Thanks so much for your patience!
_________________
Life is too short to only write code in assembly...
Ttelmah



Joined: 11 Mar 2010
Posts: 19765

View user's profile Send private message

PostPosted: Sat Mar 29, 2025 11:50 pm     Reply with quote

What you need is the line you are sending as a chip select, going to the
slave select on the slave (logical). Don't setup enable in the #use.
Have a read of section 36.4.2 and onwards in the data sheet. Particularly
the diagram about SS (though this shows SS going high, and it's default
configuration is to go low). The polarity is controlled by the SSP bit.
Now for #use spa, CCS don't mention the slave select. For setup_spi
it is an option. However they do default to enabling the SSET bit which
means that if it is setup with pin select it will work.
There is one annoyance though. The data sheet says that the default for
the SSP polarity bit is '1', which means the select will be active low.
For some (unknown) reason CCS set this to 0, so the select becomes
active high. To use the more normal active low chip select, you have to
add:
Code:

#bit SSP=getenv("SSP1CON1").2


//Then at the start of your main:
    SSP=1;

This then gives the active low select.

I've suggested to CCS, that they add support for this with polarity
control, and also to handle the option for a hardware select output
that this chip implements.
SkeeterHawk



Joined: 16 Oct 2010
Posts: 41
Location: Florissant, CO

View user's profile Send private message

Where are the Assembly constants defined???
PostPosted: Tue Apr 01, 2025 10:26 pm     Reply with quote

I am still trying to get this thing to work, but I have a fundamental question, Ttelmah. When looking at the #bit pre-processor command it says that X is a "Constant or C Variable", so obviously your command is the latter. I added your code to mine, and it compiled, but would you mind letting me know where the assembly register names called out? For example, I can't find "*SSP1*" anything anywhere in the "Find in files" command. I understand what you did and why you did it, but I don't know where to find where the "C Variables" are defined so that I can assure that my syntax is correct. Thanks so much!!
_________________
Life is too short to only write code in assembly...
Ttelmah



Joined: 11 Mar 2010
Posts: 19765

View user's profile Send private message

PostPosted: Wed Apr 02, 2025 1:07 am     Reply with quote

At heart, the data sheet!...
However the key tool is the device editor.
In the IDE, Tools, Device editor.
Then the top right column 'Registers.
This then gives a huge table of all the chip registers, and the bit names
in them.
Unfortunately, there is a problem when dealing with devices where (for
example), you have two UARTs, and there is an SPEN for each. On some
chips the compiler does give a numbered version of the option (so SPEN1
and SPEN2), but on others you have to use the register name and a bit
selection in this as I show.
SkeeterHawk



Joined: 16 Oct 2010
Posts: 41
Location: Florissant, CO

View user's profile Send private message

PostPosted: Wed Apr 02, 2025 12:05 pm     Reply with quote

Knowing where the "Device Editor" is and what it does was a very helpful tip. Thank you again, Ttelmah!!

It still isn't working, and is now "dead as a doornail". Looking at the SFRs within the debugger shows that SPI1CON1 isn't believing the SSP = 1; statement. It is reading 0x20 when it should be 0x24.

I put the #bit declaration right after my #use spi preprocessor and the SSP = 1; statement is in my initialization sub routine that happens after everything else is declared and turned on.

I am going through this thing bit by bit. I guess if all else fails, I could revert to Assembly with an #asm, but I'd prefer to figure out how to get this thing working without having to go there. It seems silly to get hung up on something as simple as SPI, but here I am.
_________________
Life is too short to only write code in assembly...
jeremiah



Joined: 20 Jul 2010
Posts: 1380

View user's profile Send private message

PostPosted: Wed Apr 02, 2025 1:53 pm     Reply with quote

Ttelmah wrote:

Code:
#bit SSP=getenv("SSP1CON1").2



SkeeterHawk wrote:
Knowing where the "Device Editor" is and what it does was a very helpful tip. Thank you again, Ttelmah!!

It still isn't working, and is now "dead as a doornail". Looking at the SFRs within the debugger shows that SPI1CON1 isn't believing the SSP = 1; statement. It is reading 0x20 when it should be 0x24.

I put the #bit declaration right after my #use spi preprocessor and the SSP = 1; statement is in my initialization sub routine that happens after everything else is declared and turned on.

I am going through this thing bit by bit. I guess if all else fails, I could revert to Assembly with an #asm, but I'd prefer to figure out how to get this thing working without having to go there. It seems silly to get hung up on something as simple as SPI, but here I am.


It's most likely because the example Ttelmah gave had a slight error. You need to do either:
Code:

#bit SSP = getenv("BIT:SPI1SSP")

OR
Code:

#bit SSP = getenv("SFR:SPI1CON1").2



There's another syntax that works on some chips but I couldn't get it to work on this chip. On chips that support it, you can do:
Code:

#bit SSP = getenv("BIT:SPI1CON1.SSP")

It works on some of my PIC24s, but I couldn't get it to work for you chip, so stick to one of the above two methods.

I don't have v5.114, but I have v5.113. The example Ttelmah gave assigns SSP to register 0x00 bit 2. I guess CCS doesn't catch it as a bad identifier for the function. Using one of the two above ones assigns SSP to register 0x85 bit 2. I don't have the datasheet handy, but that sounds better.

You can see the addresses of all of your variables in the *.sym file that is generated when you compile
SkeeterHawk



Joined: 16 Oct 2010
Posts: 41
Location: Florissant, CO

View user's profile Send private message

Some notes on the status at this time
PostPosted: Wed Apr 02, 2025 2:15 pm     Reply with quote

Hello everyone,

For documentations sake, I am making a note of where all this stuff is so it may help some of my programming brothers and sisters in the future. As the system compiles, here is how it is setting up the stuff for the SPI specifically:

Code:
#include <18F57Q84.h>

#pin_select SS1IN=PIN_D0
#pin_select SDO1=PIN_C2
#pin_select SCK1IN=PIN_C3
#pin_select SDI1=PIN_C4
#use spi (STREAM = SPI, SLAVE, SPI1, MODE=3)

//  SPI Bit assignments
#define  Bit_SPI_Mask_Read             0b10000000                          //  Bit that gets set when this is a Read operation
#bit SSP=getenv("SSP1CON1").2                                              //  Assign SSP to Bit #2 of the SSP1CON1

void SystemInit(void)
{
    SSP=1;                                                                 //  Then at the start of your main:
}

void  CheckPumpCommunication(void)
{
   int8  AddressIn,                                                        //  Register to hold the address byte that the Pump Master is sending
         MaskedAddress;                                                    //  We mask the R/W bit, and then use this register for the basic address

   if(input(PumpMaster_CS))                                                //  Check to see if the Pump Master is asserting our Chip Select input, and if it is high (not asserted) return back to the calling program
   {
      return;                                                              //  Nope, no chip select here, so head on back
   }
   AddressIn = spi_xfer(SPI, 0x00, 8);                                     //  Load the address register with the data that is on the SPI bus so we can see where we go from here


I am still a little unsure how I am supposed to poll the Client Select line. I understand the interrupt, so perhaps that is what I should poll instead.

All this said, running this on a debugger, I paused it after initialization and recorded the SFRs respective to SPI1.
    SPI1CON0 is 0x81 - EN-SPI is Enabled & BMODE-SPIxTWIDTH setting applies to every byte
    SPI1CON1 is 0x20 - CKP-Idle state for CLK is high (Right for Mode 3), but Bit #2 should be high too
    SPI1CON2 is 0x03 - TXR-TxFIFO data is required & RXR-Data transfers are suspended when the RxFIFO is full
    SPI1STATUS is 0x20 - TXBE-Transmit buffer TxFIFO is empty
    SPI1INTF is 0x30 - SOSIF-SS_in transitioned from false to true & EOSIF-SS_in transitioned from true to false
    TRISC is 0xC8 - SDO1(C2) is output, SCK1IN(C3) in Input, but SDI1(C4) in also Output
    TrisD is 0xFF - My SS pin is on D0, and this is an input as it should be

Those were the only registers that changed after initialization.

An interesting thing about this data to note is that if you look at Table 36-2 in the datasheet for the part, it mentions that when TXR Bit #1 of SPI1CON1 is high and when TXBE Bit #5 in SPI1STATUS is high, and if the SS is True like when I was trying to use the Enable line it says that SDO will "Output the most recently received byte", which explains why the chip was echoing the address byte that it just received.

There are obviously a few things messed up about what is going on here. The fact that the TRIS bit is wrong for the Data In pin is concerning to me as those will logically be butting heads.

So, my compiler is giving me a hard time to get this "simple" communications scheme implemented and I am getting close to "opening up a can of Assembly" on this to make it go. I'd prefer to stick to the compiler, so the feedback is appreciated.
_________________
Life is too short to only write code in assembly...
SkeeterHawk



Joined: 16 Oct 2010
Posts: 41
Location: Florissant, CO

View user's profile Send private message

PostPosted: Wed Apr 02, 2025 2:31 pm     Reply with quote

jeremiah wrote:

Code:

#bit SSP = getenv("SFR:SPI1CON1").2



Thank you SOO much for the feedback, jeremiah!! The code above worked like a champ, and now SPI1CON1 is now 0x24 like it is supposed to be.

I too was surprised that the compiler let me go. The SS line is working again now, so I am able to get back into the routine, but it is echoing the address again/some more. At least it is not just looking stupid at me anymore.

I sincerely appreciate all the help/feedback, and hopefully these notes will help someone else in the future. I am inching toward the finish line... Very Happy
_________________
Life is too short to only write code in assembly...
SkeeterHawk



Joined: 16 Oct 2010
Posts: 41
Location: Florissant, CO

View user's profile Send private message

PostPosted: Wed Apr 02, 2025 2:40 pm     Reply with quote

Something I just noticed is that TRISC is now 0xD8, so it made Bit #4 an input, like it was supposed to be, so fixing the state of the SS line fixed the tri-state setting of the DIN pin...go figure... Rolling Eyes
_________________
Life is too short to only write code in assembly...
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
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