PIC18F8722: SPI Only Transmits 7 bits of an 8 bit byte!

joeyd999

Joined Jun 6, 2011
6,443
Photo 1: Positive clock, rising edge
Photo 2: Positive clock, falling edge
Photo 3: Negative clock, rising edge, 7 bit.

Either the hardware is fubar, or your code is unintentionally mucking with the config registers.

Edit: I choose the latter.
 
Last edited:

Thread Starter

daklone

Joined Dec 14, 2015
42
Does it disable the SPI when it has nothing more to send and zap the last clock?
No, it never gets out of this routine as it is stuck in the loop at lines 28-29 waiting for BF to be set for the third byte.

I don't know why you need the delay.. that's suspicious.
Yes, I agree.

.. and at 2.4us its about 1 clock time. I wonder if the original programmer has that to allow for one more clock time after reading BF==1 before loading the next char. Maybe he was losing a clock also. The problem with where the delay is is that it would NOT apply on the last character.... And if he turns the SPI off after the last character.....
Again, the SPI can't be turned off since the processor is stuck in this loop.
 

JohnInTX

Joined Jun 26, 2012
4,787
Photo 1: Positive clock, rising edge
Photo 2: Positive clock, falling edge
Photo 3: Negative clock, rising edge, 7 bit.

Either the hardware is fubar, or your code is unintentionally mucking with the config registers.

Edit: I choose the latter.
SPI_fu is strong in this one.. Good catch. I'm off for more coffee.
 

Thread Starter

daklone

Joined Dec 14, 2015
42
Photo 1: Positive clock, rising edge
Photo 2: Positive clock, falling edge
Photo 3: Negative clock, rising edge, 7 bit.

Either the hardware is fubar, or your code is unintentionally mucking with the config registers.

Edit: I choose the latter.
Hmm, but the two registers that control the port are set identically between bytes 1 and 2, so why does it change (I've checked that this is the case, both before and after the SSP1BUF = data_out line)?

I would not discount broken hardware, but how would I prove it?
 

joeyd999

Joined Jun 6, 2011
6,443
Hmm, but the two registers that control the port are set identically between bytes 1 and 2, so why does it change (I've checked that this is the case, both before and after the SSP1BUF = data_out line)?

I would not discount broken hardware, but how would I prove it?
This is where I hate C. An optimization my be causing a write to SSP1STAT and/or SSP1CON while you think you are only doing a read. Have you tried the simulator? I think you can set a trigger to stop simulation whenever a particular bit gets flipped.

Things like this usually happen in one of two cases:

1. An (possibly unassociated) interrupt is accessing the registers;
2. Access to a register in another bank is occurring without properly setting up the BSR register, or improper use of the BANKED/ACCESS flag.
 

spinnaker

Joined Oct 29, 2009
7,830
Are there any interrupts that might be changing things behind the scenes? Whenever I run into these weird and frustrating issues, I break out the problem code and write a whole new smaller test project with only the code relevant to the issue. Works for me almost every time. And if you. Still see an issue then you have simplified code you can present to microchip.
 

joeyd999

Joined Jun 6, 2011
6,443
Going back to the photos, two bits control the waveform:

CKP (SSP1CON.4) controls clock polarity;
CKE (SSP1STAT.6) controls the clocking edge.

Here are the presumed bit states for each photo:

Photo 1: CKP=0, CKE=0;
Photo 2: CKP=0, CKE=1;
Photo 3: CKP=1, CKE=1;

I cannot explain the 7 bits, except that with all the other screwiness going on, I assume the SPI is getting reset by the same code that is mucking the config bits.
 

JohnInTX

Joined Jun 26, 2012
4,787
I would not discount broken hardware, but how would I prove it?
You could infer it from the colossal errata sheet... Reread item #30. That's what the 1 clock cycle delay is about (don't load the next character until 1 clock cycle after detecting BF). You might try increasing that as a test.
I note that the last bit sent is different between chars 1-2 but same between 2-3. Combined with the errata item 30 I am getting bad vibes. The I2C mode has a LSbit of one char to MSbit to the next char problem that is data dependent if certain timings are not observed. Don't recall what it is right now but its the same hardware.

Look, here's what I would do. Scrap this and bit bang it. As I said the MSSP on this thing is a mess. Someone has applied some errata fixes in hope of making it work but as you can see (and @joeyd999) pointed out, the hardware is fubar. And as far as using MPSIM, uhhh.. unless they've fixed it, it hangs when you poll BF. I have switches in my code for MPSIM. I know, right? *wipes away tears*

As it is written, you would suffer MINIMAL performance consequences by bit banging. The code waits for each character to be sent anyway. It might even be better since you don't have to add the 2.4uS delay. You can spiff things up by doing things between the clock edges i.e. raise the clock, shift the data byte, lower the clock, decrement the bit counter etc. and it will be pretty zippy. That's how I do it.

Joey and @spinnaker posted while I was typing.. Spin makes a good point about interrupts. I agree with Joey to see what the compiler has generated - but I'd still be bit banging it. I think you are fighting silicon battles that you can't win.

You might open a ticket at uCHIP and see what they say. Maybe they have another workaround.

Good luck.
Improv final today. Gotta go practice.
 

Thread Starter

daklone

Joined Dec 14, 2015
42
This is where I hate C. An optimization my be causing a write to SSP1STAT and/or SSP1CON while you think you are only doing a read. Have you tried the simulator? I think you can set a trigger to stop simulation whenever a particular bit gets flipped.

Things like this usually happen in one of two cases:

1. An (possibly unassociated) interrupt is accessing the registers;
2. Access to a register in another bank is occurring without properly setting up the BSR register, or improper use of the BANKED/ACCESS flag.
But wouldn't I see anything that happens (inc. interrupts) by stepping through the code using MPLAB/PICKit3?
 

Thread Starter

daklone

Joined Dec 14, 2015
42
You could infer it from the colossal errata sheet... Reread item #30. That's what the 1 clock cycle delay is about (don't load the next character until 1 clock cycle after detecting BF). You might try increasing that as a test.
I note that the last bit sent is different between chars 1-2 but same between 2-3. Combined with the errata item 30 I am getting bad vibes. The I2C mode has a LSbit of one char to MSbit to the next char problem that is data dependent if certain timings are not observed. Don't recall what it is right now but its the same hardware.

Look, here's what I would do. Scrap this and bit bang it. As I said the MSSP on this thing is a mess. Someone has applied some errata fixes in hope of making it work but as you can see (and @joeyd999) pointed out, the hardware is fubar. And as far as using MPSIM, uhhh.. unless they've fixed it, it hangs when you poll BF. I have switches in my code for MPSIM. I know, right? *wipes away tears*

As it is written, you would suffer MINIMAL performance consequences by bit banging. The code waits for each character to be sent anyway. It might even be better since you don't have to add the 2.4uS delay. You can spiff things up by doing things between the clock edges i.e. raise the clock, shift the data byte, lower the clock, decrement the bit counter etc. and it will be pretty zippy. That's how I do it.

Joey and @spinnaker posted while I was typing.. Spin makes a good point about interrupts. I agree with Joey to see what the compiler has generated - but I'd still be bit banging it. I think you are fighting silicon battles that you can't win.

You might open a ticket at uCHIP and see what they say. Maybe they have another workaround.

Good luck.
Improv final today. Gotta go practice.
Thanks for your help. Good luck with your final :)
 

joeyd999

Joined Jun 6, 2011
6,443
But wouldn't I see anything that happens (inc. interrupts) by stepping through the code using MPLAB/PICKit3?
Maybe. Maybe not.

A few thousand, or more, lines of code my get executed before those bits get transitioned.

At this point, I agree with @spinnaker. Make a small project with only the relevant lines of code -- just the SPI. Get that to work to the exclusion of everything else. Then, incorporate the working code into your main program. If the problem reappears, at least that tells you the issue lies somewhere outside of your SPI code.
 

JohnInTX

Joined Jun 26, 2012
4,787
FWIW: Here's a code snippet of a project done when the 6622 was new - and supposedly better than the 6620 it replaced. The polling of BF problem is addressed by the compiler (HiTech PICC18) and it generates errata-aware code. Of note is the simplicity. Just like it should be. Its still in production with no SPI problems. It DOES use the watchdog to reset if the code hangs somewhere but its never had a problem. It makes me wonder why the original programmer had to put all of the errata stuff in (and still had problems) while I got away scott-free. Scary.

C:
//--------------------------------  WRITE 1 BYTE TO OPENED EE  ----------------------------
// Assumes EE is opened for writing.  Sends data byte, returns when its sent
unsigned char EE_WriteByte(unsigned char dat)
{
    SSP1BUF = dat;                // drop it on buf to send
    while(!BF1);                // wait for returned byte
    return SSP1BUF;                // read buf to clear, return byte
}

//--------------------------------  READ 1 BYTE FROM OPENED EE  ---------------------------
// Assumes EE is opened for reading.  Sends dummy byte, returns byte replied by EE
unsigned char EE_ReadByte(void)
{
    SSP1BUF = 0x55;                // send dummy byte
    while(!BF1);                // wait for reply
    return SSP1BUF;                // read and return reply   
}
 

Thread Starter

daklone

Joined Dec 14, 2015
42
Maybe. Maybe not.

A few thousand, or more, lines of code my get executed before those bits get transitioned.

At this point, I agree with @spinnaker. Make a small project with only the relevant lines of code -- just the SPI. Get that to work to the exclusion of everything else. Then, incorporate the working code into your main program. If the problem reappears, at least that tells you the issue lies somewhere outside of your SPI code.
I tried just the following code, in a project on its own:
C:
unsigned char Errata_WriteSPI( unsigned char data_out)
{ unsigned char temp;

  temp = SSP1STAT;
    while( (temp & 1) == 1)
     { temp = SSP1BUF;  temp = SSP1STAT; }  // clear BF before the operation starts

  SSP1BUF = data_out;          // write byte to SSP1BUF register

  if ( SSP1CON1 & 0x80 )       // test if write collision occurred
   return ( -1 );              // if WCOL bit is set return negative #
  else
  {
    temp = SSP1STAT;
    while( (temp & 1) == 0)
    {
   temp = SSP1STAT; // wait until bus cycle complete
    }
    }
  return ( 0 );                // if WCOL bit is not set return non-negative #
}

void main(void)
{ unsigned char runvar;

    runvar = 1;

    OpenSPI(SPI_FOSC_64, MODE_11, SMPMID);

    while(runvar == 1)
    {
    if (Errata_WriteSPI(0x55))
        runvar = 0;
    if (Errata_WriteSPI(0xAA))
        runvar = 0;
    }
  
    CloseSPI();
}
Worked like a charm, churning 0x55 followed by 0xAA out continuously...which is good, I suppose...except it means there is something more subtle and tricky to find at work in the main code :(
 
Last edited by a moderator:

joeyd999

Joined Jun 6, 2011
6,443
I tried just the following code, in a project on its own:

unsigned char Errata_WriteSPI( unsigned char data_out)
{ unsigned char temp;

temp = SSP1STAT;
while( (temp & 1) == 1)
{ temp = SSP1BUF; temp = SSP1STAT; } // clear BF before the operation starts

SSP1BUF = data_out; // write byte to SSP1BUF register

if ( SSP1CON1 & 0x80 ) // test if write collision occurred
return ( -1 ); // if WCOL bit is set return negative #
else
{
temp = SSP1STAT;
while( (temp & 1) == 0)
{
temp = SSP1STAT; // wait until bus cycle complete
}
}
return ( 0 ); // if WCOL bit is not set return non-negative #
}

void main(void)
{ unsigned char runvar;

runvar = 1;

OpenSPI(SPI_FOSC_64, MODE_11, SMPMID);

while(runvar == 1)
{
if (Errata_WriteSPI(0x55))
runvar = 0;
if (Errata_WriteSPI(0xAA))
runvar = 0;
}

CloseSPI();
}

Worked like a charm, churning 0x55 followed by 0xAA out continuously...which is good, I suppose...except it means there is something more subtle and tricky to find at work in the main code :(
Good. At least you don't have to waste time with your SPI code anymore.
 

spinnaker

Joined Oct 29, 2009
7,830
And do you have a similar chip maybe different chip number but same spi registers (not sure if microchip changed those across chips). You can try your test code on it if the main chip still has an issue.
 

Thread Starter

daklone

Joined Dec 14, 2015
42
OK, so I added INTCONbits.GIE = 0 to the begining and INTCONbits.GIE=1 to the end of the WriteSPI routine, which I believe should stop any interrupts from causing problems with the routine. No change with the problematic third byte though.

I'm open to other suggestions :)
 

joeyd999

Joined Jun 6, 2011
6,443
How many channels do you have on your scope?

If you've got some free I/O pins, you could copy the state of the CKE and CKP bits to the pins prior to each transmission. Then, monitor those pins along with the SCL/SDO.

This will at least confirm my suspicion that those bits are getting corrupted.
 

Thread Starter

daklone

Joined Dec 14, 2015
42
I have been checking them using MPLAB/PICKit3 by putting breakpoints before and after the SSP1BUF = data_out line. Neither register changes over the course of the instruction.
 

joeyd999

Joined Jun 6, 2011
6,443
I have been checking them using MPLAB/PICKit3 by putting breakpoints before and after the SSP1BUF = data_out line. Neither register changes over the course of the instruction.
How about immediately reconfiguring CKE/CKP prior to each transmission?
 
Top