Software SPI (PIC18)

Discussion in 'Embedded Systems and Microcontrollers' started by blah2222, Jan 27, 2014.

  1. blah2222

    Thread Starter Well-Known Member

    May 3, 2010
    554
    33
    Hi all,

    In short, I need to have use of both UART Rx and SPI SDO functionality but unfortunately this device does has both of those located on the same pin with no re-mappable pins.

    With one being an input (Rx) from a Bluetooth module and the other being an output (SDO) to a DAC and they would be strictly operated with only one on at a time, I feel as though it would be somewhat clunky to keep it this way.

    Since I can't remap the pins, the only option I have, (besides getting a new IC, which isn't an option), is to implement software SPI.

    Wondering if anyone has any experience with this and might be able to point me in the right direction to getting this set up.

    Thanks,
    JP

    *EDIT*

    Found this code snippet from here:

    Code ( (Unknown Language)):
    1.  
    2. //Define all SPI Pins
    3. #define SPI_OUT     RC4_bit     /* Define SPI SDO signal to be PIC port RC4 */
    4. #define SPI_IN      RC5_bit     /* Define SPI SDI signal to be PIC port RC5 */
    5. #define SPI_CLK     RC3_bit     /* Define SPI CLK signal to be PIC port RC3 */
    6. #define SPI_CS      RB1_bit     /* Define SPI CS signal to be PIC port RB1 */
    7.  
    8. /**
    9.  * This function writes a byte out onto the SPI OUT port, and reads a byte from
    10.  * the SPI IN port.
    11.  *
    12.  * @param c Gives the byte to write out onto the SPI port
    13.  *
    14.  * @return Returns the byte read from the SPI IN port
    15.  */
    16. char spiPutGetByte(char c) {
    17.     char ret;
    18.     unsigned char mask;
    19.    
    20.     //SPI Mode 0. CS active low. Clock idle 0. Clock rising edge.
    21.     SPI_CLK = 0;
    22.    
    23.     //Enable SPI communication. The SPI Enable signal must be pulsed low for each byte sent!
    24.     SPI_CS = 0;
    25.    
    26.     //Ensure a minimum delay of 500ns between falling edge of SPI Enable signal
    27.     //and rising edge of SPI Clock!
    28.     Nop();
    29.     mask = 0x80;                //Initialize to write and read bit 7
    30.     ret = 0;                    //Initialize read byte with 0
    31.    
    32.     do  {
    33.         SPI_OUT = 0;                //Clock out current bit onto SPI Out line
    34.         if (c & mask) SPI_OUT = 1;
    35.         SPI_CLK = 1;                //Set SPI Clock line
    36.         if (SPI_IN) ret |= mask;    //Read current bit fromSPI In line
    37.         Nop();                      //Ensure minimum delay of 500nS between SPI Clock high and SPI Clock Low
    38.         SPI_CLK = 0;                //Set SPI Clock line
    39.         mask = mask >> 1;           //Shift mask so that next bit is written and read from SPI lines
    40.         Nop();                      //Ensure minimum delay of 1000ns between bits
    41.     } while (mask != 0);
    42.  
    43.  
    44.     //Ensure a minimum delay of 750ns between falling edge of SPI Clock signal
    45.     //and rising edge of SPI Enable!
    46.     Nop();Nop();
    47.  
    48.     //Disable SPI communication. The SPI Enable signal must be pulsed low for each byte sent!
    49.     SPI_CS = 1;
    50.    
    51.     return ret;
    52. }
    53.  
    Thoughts?
     
    Last edited: Jan 27, 2014
  2. shteii01

    AAC Fanatic!

    Feb 19, 2010
    3,387
    497
    Can you change port configuration during program execution?
     
  3. blah2222

    Thread Starter Well-Known Member

    May 3, 2010
    554
    33
    I can change the directionality (I/O) of the pin on the mcu but the BT module would be hardwired to the same pin as the DAC is hardwired to.

    I'm confident that this wouldn't be an issue with the DAC as it will not do anything unless I specifically toggle the CS pin, but I feel as though this setup might be a tad clunky.
     
  4. spinnaker

    AAC Fanatic!

    Oct 29, 2009
    4,884
    1,005
    Microchip has a library that support both hardware and software SPI.
     
  5. JohnInTX

    Moderator

    Jun 26, 2012
    2,345
    1,028
    Sure. First, dedicate the USART to the async RX and bit-bang the SPI on other pins.

    The code snippet should work but it can be improved:
    The SPI DAC is one-way so you can omit reading / shifting / returning a value.
    'mask' is used as a bit-selector AND counter. Clever but limits the DAC output to 8 bits unless its bumped to an unsigned int.
    The first two lines of the 'do' loop put unnecessary transitions on SDO. A better approach would be:
    Code ( (Unknown Language)):
    1. if (c & mask)
    2.  SPI_OUT = 1;
    3. else
    4.  SPI_OUT = 0;
    It doesn't make a difference to the DAC but its less cluttered to observe on a scope, etc.
    Take advantage of housekeeping tasks to provide short delays, for example in:
    Code ( (Unknown Language)):
    1.    //Ensure a minimum delay of 500ns between falling edge of SPI Enable signal
    2.     //and rising edge of SPI Clock!
    3.     Nop();
    4.     mask = 0x80;
    The Nop(); is likely unnecessary since loading mask takes more time than nop in the first place - it depends ultimately on the PIC speed but something to consider (and document!). EDIT: AND check the compiler output to see how many instructions are generated.

    Hope that helps. Good luck!

    EDIT: BTW, be sure to specify your 1 bit outputs as LATx not PORTx. Bit inputs are PORTx as always.
     
    Last edited: Jan 28, 2014
    blah2222 likes this.
  6. blah2222

    Thread Starter Well-Known Member

    May 3, 2010
    554
    33
    Thank you!
     
Loading...