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

#### daklone

Joined Dec 14, 2015
42
Hi All,

I'm having a problem with a project based on the PIC18F8722 that I would appreciate some help with.

I'm using the SPI to interface to some other components in Master mode, but something extremely odd is happening. I set the port up fine, transmit the first byte, fine, transmit second byte, fine, transmit the third byte...the device only clocks out 7 bits!

I can see on the scope that there are only 7 clock pulses for the last byte, and my code then goes into a never-ending loop waiting for BF to be set, which of course doesn't happen because the transmission is not completed (nor is IF triggered, for the same reason).

I can't even imagine how this is possible, let alone what might cause it. Any suggestions?

Regards,
dK

#### Papabravo

Joined Feb 24, 2006
14,201
Sounds like something is disabling the SPI peripheral. The process that starts the clocking is writing to the data register. Normally you don't wait for the transmission to be completed; you go off to do other stuff until you're ready to read the reply or send the next byte. What are you doing with the incoming data from sending bytes 1 and 2? Are you emptying the read data register? It might have a two byte FIFO and be aborting the transmission when the third byte is about to overwrite the two that are already there.

You did know that SPI transactions are always bi-directional even if the incoming data is meaningless -- right?

#### daklone

Joined Dec 14, 2015
42
Thank you for such a quick reply, Papabravo

I am reading SSP1BUF after each transmission (and discarding the data in the this case) and the PIC isn't throwing an overflow error, so I guess this is OK.

I'm not sure what you mean about a 2-byte FIFO: do you mean the PIC or the slave device? I would have imagined in Master mode any slave device is unable to interrupt the PIC's transmissions, regardless of it's FIFO state. As I mentioned, there is no overflow on the PIC.

#### Papabravo

Joined Feb 24, 2006
14,201
A FIFO is a first in first out buffer used by peripheral devices to reduce the requirement that a processor service the device immediately when data is ready. They show up in UARTS for asynchronous serial communications all the time. In this case I was thinking that the master might be able to hold two bytes coming from the slave device before deciding to abort before the third byte coming from the slave device could overwrite the two that were there. Then the only other thing that comes to mind is inadvertently clearing the SPI device enable bit in the control register. I'm sure you are not doing this on purpose. I don't see anything at all that might be magic about the third byte if 1 and 2 are OK.

#### daklone

Joined Dec 14, 2015
42
I don't see anything at all that might be magic about the third byte if 1 and 2 are OK.
Indeed, welcome to my land of confusion!

#### daklone

Joined Dec 14, 2015
42
The only other thing I forgot to mention is that the data line doesn't return to idle after the 7th bit of the third byte is clocked out. If the 7th bit is high, it stays high and if the 7th bit is low, it says low.

I also double-checked that I'm reading back the same data from SSP1BUF, which I am...even for the incomplete third byte.

#### joeyd999

Joined Jun 6, 2011
4,425
Remember that SPI data is clocked on the edge, not level. Depending upon device, the clocking edge can be either positive-going or negative-going. There is a setting for this in one of the SPI config registers.

The number of edges is fixed in hardware. You will get 8, unless you are somehow disabling the SPI hardware in the middle of a byte.

An improper edge polarity, combined with triggering your scope off the wrong edge, may make it appear that you are missing a pulse.

#### daklone

Joined Dec 14, 2015
42

It's a good thought, but I got a colleague to check that, just in case...nope, the scope is set up correctly and I am capturing all the events. Besides, this wouldn't explain why the data line doesn't go back to idle.

I am just as incredulous as you; as I said in my first post, I cannot imagine how this can happen and if I wasn't looking at it with my own eyes I'd be inclined to disbelieve it or put it down to "operator error" myself

#### Picbuster

Joined Dec 2, 2013
1,020
I used in all pic with SPI functions (including pic18f8722/960/22k etc)
Yes indeed some names are different when more than one spibus is available
I disabled the wcol for you due to unwanted effects during test
C:
//============  SPI interrupt =========================

if  (SSPIF)
{
SSPIF=0;  // flag off
Spi_rec[Spi_pointer]=SSPBUF;
Spi_pointer++;
}

// -------------------spi bus master ------------------
SSPSTAT= 0b01000000;
CKP=0; //0=idle clock state is low
CKE=0; //0=Data transmitted on falling edge of SCK
SMP=1; //1=SPI mode-Input data sampled at end of data output time
SSPM3=0; //FOSC/4
SSPM2=0; //FOSC/4
SSPM1=0; //FOSC/64
SSPM0=1; //FOSC/4  1=/16
SSPIE=1;
SSPEN=1;
//----------------------------------------------
PEIE=1;   // Enable peripheral int.
GIE=1;   // Global int. enable

//================ zend een byte naar SPI bus ==============

//------ chipselect done ----------------

void Xt_SPI(unsigned char byte)

{
SSPEN=1; // enable

clock_on

SSPBUF=byte;

__delay_ms(5); // wait <== this could be yr trouble maker

deselect

SSPEN=0; // enable

}

Last edited by a moderator:

#### daklone

Joined Dec 14, 2015
42
Hmm, think you might have cross-posted that, Picbuster!

#### joeyd999

Joined Jun 6, 2011
4,425
See item #21 in this document. Possibility?

#### daklone

Joined Dec 14, 2015
42
Good thinking, but not using TMR2 as a clock source :-(

I'm using SPI Master Mode, Fosc/64.

#### JohnInTX

Joined Jun 26, 2012
4,198
See item #21 in this document. Possibility?
Don't overlook the other ones, particularly #29 and #30.
There are different errata sheets depending on the silicon version. Joey posted the one for rev A1. Rev B1 and Timer 1 errata is here.

What debugger/emulator are you using? ICE2K/4K all had their own errata affecting problems with their SPI. I put switches in my code that apply patches if I am debugging with one of these. PICkit and RealICE are less problematic since the debugging is native to the chip. Be SURE to look through the MPLAB help file for the debugger/compiler/chip. There are more issues listed there.

I have a lot of experience with this family and its even-worse predecessor the 6720 et al. IMHO, its the worst part uCHIP has ever released. The MSSP is somewhat problematic - I am being polite.

From a coding perspective, you probably shouldn't wait on BF, even though its 'guaranteed' to work. (As a general programming practice I never allow blocking code on any peripheral or IO). As the errata says, you sometimes shouldn't poll it. You might want to make it an interrupt driven thing or consider bit banging the SPI. If you are just updating some SPI DAC with a few bytes now and then, you might as well bit bang it. You are not saving much time by using the SPI master if you wait on BF. A handful of instructions and you are done. That's what I do for all peripheral SPI stuff. If you had to process incoming data from another chip, that's another thing but I use I2C for that.

A final note, it sometimes takes a long time for a reported issue to find its way into the errata, and not all problems are known. Consider the number of issues in even the latest silicon revision. Do you think all of those hit the errata sheet at the same time? Nope. B1 silicon had a pretty clean errata sheet when released. The issues listed showed up later. Maybe you have a new one OR one that you don't expect to result from the published errata. Review your compiler's assembler output to be sure it does not generate code that fouls BF. IIRC, I put some inline assembler that satisfied the errata when the compiler's code didn't work.

EDIT: depending on where you are in your project, you might consider replacing the 6722 with another series - I starting using the 67J22 (3.3V but 5Vtolerant in many ways). I think that there are some 'K' parts that are similar as well.
<snip> Ignore the 6527 idea if you read it. It has the same MSSP SPI issues.

Good luck.

Last edited:

#### joeyd999

Joined Jun 6, 2011
4,425
Don't overlook the other ones, particularly #29 and #30.
I didn't even scroll far enough to see those!

#### JohnInTX

Joined Jun 26, 2012
4,198
I didn't even scroll far enough to see those!
Its moot. There are likely more that aren't listed. Its really a POS - expensive too.

EDIT: For the TS: IIRC, lots of these had issues in inadvertently clearing BF when any instruction ending in C9h with BSR=15 was executed. As it happens, SSPBUF is at FC9h so reading it in the direct mode could clear BF. The workaround was to fully specify the address as FC9h so that the assembler could get at it through the access bank or use one of the FSRs to get it indirectly. I don't know if this would account for your 7of 8 bit problem, or if its still an issue but you might try it. Hopefully your compiler knows about these things and codes around them.

Last edited:

#### daklone

Joined Dec 14, 2015
42

Sadly, this is a legacy product so no chance of changing the part. The code was delivered to me as "working", but it doesn't.

I'm using PICKit3 for debugging.

Looking through the later errata, nothing jumps out as relevant either.

#### JohnInTX

Joined Jun 26, 2012
4,198

Sadly, this is a legacy product so no chance of changing the part. The code was delivered to me as "working", but it doesn't.

I'm using PICKit3 for debugging.

Looking through the later errata, nothing jumps out as relevant either.
Can you post the relevant code with the assembler output from the compiler?
Which compiler and MPLAB version are you using?

EDIT: make sure that your code does not disable the SPI before the last char has been fully shifted out. Its an easy mistake to make.

Last edited:

#### daklone

Joined Dec 14, 2015
42
I took some pictures of the scope screen, capturing each byte as it is transmitted.

Byte 1 (SSP1CON1=0x22, SSP1STAT=0x40, data=0x00) seems to me to be fine, with the data line (upper trace) asserted, then the clock (lower trace) shifting each bit out.

Byte 2 (SSP1CON1=0x22, SSP1STAT=0x40, data=0x20) on first look seems good too, but I now notice that the data line and first clock pulse are co-incident, which doesn't seem quite right.

Byte 3 (SSP1CON1=0x32, SSP1STAT=0x00, data=0x08) also has this data/clock co-incidence problem, along with the obvious 7-bit byte issue.

Last edited:

#### daklone

Joined Dec 14, 2015
42
Here is the code for the routine that transmits the data:
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

// Assembly loop to delay just 2.4 uS. (24 instructions)
_asm
MOVLW  0xFA
Delay_2_5:
BZ Delay_2_5x
BRA Delay_2_5
Delay_2_5x:
_endasm

SSP1BUF = data_out;          // write byte to SSP1BUF register

temp = SSP1BUF;

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 #
}

Last edited by a moderator:

#### JohnInTX

Joined Jun 26, 2012
4,198
Sure enough.
Thanks for the code. Let's see...

Nothing that would account for 7 bits on the last character jumps right out. Its polling BF but you'd want to see what the underlying assembler is before worrying about that.

Its kind of out of sync. At the end after sending, it should read SSPBUF right there to read the byte received when one was sent. It leaves it for the next call. Same kind of thing at 19-21, it should read SSPBUF before writing the next character.

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

// could be cleaned up with a do loop to avoid the need for a preset.
do{
temp = SSP1STAT;
} while ( (temp & 1) == 1);
SSP1BUF;  // this reads SSP1BUF only after BF==1 and throws away the value
Warts and all, it gets through the first 2 chars and fails on the last. What happens when you send 4 chars, do you get 3 good ones and the 4th bad? In that case, I'd look at what calls this. Does it disable the SPI when it has nothing more to send and zap the last clock?

For me, I'd clean this up a bit then see what it does - probably the same thing but it will be easier to understand. Then I'd look for what happens in the caller when it doesn't have any more SPI to do.

EDIT: I don't know why you need the delay.. that's suspicious.
EDIT2: .. 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.....

Good luck.

Last edited: