 |
 |
View previous topic :: View next topic |
Author |
Message |
SkeeterHawk
Joined: 16 Oct 2010 Posts: 41 Location: Florissant, CO
|
Buffer for spi_xfer? |
Posted: Thu Mar 27, 2025 5:13 pm |
|
|
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
|
|
Posted: Fri Mar 28, 2025 7:58 am |
|
|
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
|
|
Posted: Fri Mar 28, 2025 11:42 am |
|
|
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
|
|
Posted: Sat Mar 29, 2025 11:50 pm |
|
|
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
|
Where are the Assembly constants defined??? |
Posted: Tue Apr 01, 2025 10:26 pm |
|
|
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
|
|
Posted: Wed Apr 02, 2025 1:07 am |
|
|
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
|
|
Posted: Wed Apr 02, 2025 12:05 pm |
|
|
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
|
|
Posted: Wed Apr 02, 2025 1:53 pm |
|
|
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
|
Some notes on the status at this time |
Posted: Wed Apr 02, 2025 2:15 pm |
|
|
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
|
|
Posted: Wed Apr 02, 2025 2:31 pm |
|
|
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...  _________________ Life is too short to only write code in assembly... |
|
 |
SkeeterHawk
Joined: 16 Oct 2010 Posts: 41 Location: Florissant, CO
|
|
Posted: Wed Apr 02, 2025 2:40 pm |
|
|
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...  _________________ Life is too short to only write code in assembly... |
|
 |
|
|
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
|