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

Discussion in 'Embedded Systems and Microcontrollers' started by daklone, Dec 14, 2015.

  1. daklone

    Thread Starter Member

    Dec 14, 2015
    36
    2
    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
     
  2. Papabravo

    Expert

    Feb 24, 2006
    10,136
    1,786
    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?
     
  3. daklone

    Thread Starter Member

    Dec 14, 2015
    36
    2
    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.
     
  4. Papabravo

    Expert

    Feb 24, 2006
    10,136
    1,786
    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.
     
  5. daklone

    Thread Starter Member

    Dec 14, 2015
    36
    2
    Indeed, welcome to my land of confusion!
     
  6. daklone

    Thread Starter Member

    Dec 14, 2015
    36
    2
    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.
     
  7. joeyd999

    AAC Fanatic!

    Jun 6, 2011
    2,675
    2,722
    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.
     
  8. daklone

    Thread Starter Member

    Dec 14, 2015
    36
    2
    Thanks for your reply joeyd999.

    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 :)
     
  9. Picbuster

    Member

    Dec 2, 2013
    373
    50
    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
    Code (C):
    1.  
    2. //============  SPI interrupt =========================
    3.  
    4. if  (SSPIF)
    5. {
    6.   SSPIF=0;  // flag off
    7.   Spi_rec[Spi_pointer]=SSPBUF;
    8.   Spi_pointer++;
    9. }
    10.  
    11. // -------------------spi bus master ------------------
    12.    SSPSTAT= 0b01000000;
    13. CKP=0; //0=idle clock state is low
    14. CKE=0; //0=Data transmitted on falling edge of SCK
    15. SMP=1; //1=SPI mode-Input data sampled at end of data output time
    16. SSPM3=0; //FOSC/4
    17. SSPM2=0; //FOSC/4
    18. SSPM1=0; //FOSC/64
    19. SSPM0=1; //FOSC/4  1=/16
    20. SSPIE=1;
    21. SSPEN=1;
    22. //----------------------------------------------
    23. PEIE=1;   // Enable peripheral int.
    24. GIE=1;   // Global int. enable
    25.  
    26. //================ zend een byte naar SPI bus ==============
    27.  
    28. //------ chipselect done ----------------
    29.  
    30. void Xt_SPI(unsigned char byte)
    31.  
    32. {
    33. SSPEN=1; // enable
    34.  
    35. clock_on
    36.  
    37. SSPBUF=byte;  
    38.  
    39. __delay_ms(5); // wait <== this could be yr trouble maker
    40.  
    41. deselect
    42.  
    43. SSPEN=0; // enable
    44.  
    45. }
    Mod edit: added code tags
     
    Last edited by a moderator: Dec 14, 2015
  10. daklone

    Thread Starter Member

    Dec 14, 2015
    36
    2
    Hmm, think you might have cross-posted that, Picbuster! :)
     
  11. joeyd999

    AAC Fanatic!

    Jun 6, 2011
    2,675
    2,722
    See item #21 in this document. Possibility?
     
  12. daklone

    Thread Starter Member

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

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

    Moderator

    Jun 26, 2012
    2,341
    1,023
    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: Dec 14, 2015
  14. joeyd999

    AAC Fanatic!

    Jun 6, 2011
    2,675
    2,722
    I didn't even scroll far enough to see those!
     
  15. JohnInTX

    Moderator

    Jun 26, 2012
    2,341
    1,023
    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: Dec 14, 2015
  16. daklone

    Thread Starter Member

    Dec 14, 2015
    36
    2
    Thanks for your reply, JohnInTX.

    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.
     
  17. JohnInTX

    Moderator

    Jun 26, 2012
    2,341
    1,023
    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: Dec 14, 2015
  18. daklone

    Thread Starter Member

    Dec 14, 2015
    36
    2
    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.

    Byte1_0x00.jpg Byte2_0x20.jpg Byte3_0x08.jpg
     
    Last edited: Dec 15, 2015
  19. daklone

    Thread Starter Member

    Dec 14, 2015
    36
    2
    Here is the code for the routine that transmits the data:
    Code (C):
    1.  
    2. unsigned char Errata_WriteSPI( unsigned char data_out)
    3. { unsigned char temp;
    4.  
    5.   temp = SSP1STAT;
    6.     while( (temp & 1) == 1)
    7.      { temp = SSP1BUF;  temp = SSP1STAT; }  // clear BF before the operation starts
    8.  
    9.    // Assembly loop to delay just 2.4 uS. (24 instructions)
    10.   _asm
    11.   MOVLW  0xFA
    12. Delay_2_5:
    13.   ADDLW     1
    14.   BZ Delay_2_5x
    15.   BRA Delay_2_5
    16. Delay_2_5x:
    17.    _endasm
    18.  
    19.   SSP1BUF = data_out;          // write byte to SSP1BUF register
    20.  
    21.   temp = SSP1BUF;
    22.  
    23.   if ( SSP1CON1 & 0x80 )       // test if write collision occurred
    24.    return ( -1 );              // if WCOL bit is set return negative #
    25.   else
    26.   {
    27.     temp = SSP1STAT;
    28.     while( (temp & 1) == 0)
    29.        temp = SSP1STAT; // wait until bus cycle complete
    30.   }
    31.   return ( 0 );                // if WCOL bit is not set return non-negative #
    32. }
     
    Last edited by a moderator: Dec 15, 2015
  20. JohnInTX

    Moderator

    Jun 26, 2012
    2,341
    1,023
    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.

    Code (C):
    1. temp = SSP1STAT;
    2.     while( (temp & 1) == 1)
    3.      { temp = SSP1BUF;  temp = SSP1STAT; }  // clear BF before the operation starts
    4.  
    5. // could be cleaned up with a do loop to avoid the need for a preset.
    6. do{
    7.    temp = SSP1STAT;
    8. } while ( (temp & 1) == 1);
    9. SSP1BUF;  // this reads SSP1BUF only after BF==1 and throws away the value
    10.  
    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: Dec 15, 2015
Loading...