• Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

PIC18F26k20 SPI module

Status
Not open for further replies.

Cantafford

Member
Hello,

I'm learning to use the SPI module. I wrote this piece of code inspired by a tutorial. I have 3 PIC's one is master and the other 2 are slaves. The master has two switches and the slaves have 3 leds connected on PORTB. When switch1 is closed and switch2 is open, led of slave 1 should light up. When switch2 is closed and switch2 is open, led of slave 1 should light up. In all other cases no leds should light up.
This is the schematic:
http://postimg.org/image/c1m7xjlv5/

Code for the master:
Code:
/*
 * File:   main.c
 * Author: Jarvis
 *
 * Created on June 7, 2016, 7:01 PM
 */


#include <xc.h>
#include "header.h"


void main()
{
    TRISCbits.RC0 = 0; // master's cs1
    TRISCbits.RC1 = 0; // master's cs2
    TRISCbits.RC3 = 0; // sck is output
    TRISCbits.RC4 = 0; // sdo is output
    TRISB = TRISB & 0xC0; // RB0 and RB1 are inputs(buttons)
   
    SMP = 1; // input data sampled at end of data output time
    CKE = 0; // output data changes on clock transition from idle to active
    SSPEN = 1; // enables serial port and configures SCK, SDO, SDI and SS as serial ports
    CKP = 0; // idle state for clock is at low level
    SSPM3 = 0; // SPI MASTER MODE
    SSPM2 = 0; // clock 
    SSPM1 = 0; // equals 
    SSPM0 = 0; // FOSC/64
   
    while(1)
    {
        if(PORTBbits.RB0==0 && PORTBbits.RB1==0) // both switches pressed
        {
            LATCbits.LATC0 = 0; // select slave1(active low)
            SSPBUF = 0; // send 0 to SLAVE1
            LATCbits.LATC0 = 1; // deselect slave1
           
            LATCbits.LATC1 = 0; // select slave2
            SSPBUF = 0; // send 0 to SLAVE2
            LATCbits.LATC1 = 1; // deselect slave2
        }
       
        if(PORTBbits.RB0==1 && PORTBbits.RB1==1) // both switches not pressed
        {
            LATCbits.LATC0 = 0; // select slave1(active low)
            SSPBUF = 0; // send 0 to SLAVE1
            LATCbits.LATC0 = 1; // deselect slave1
           
            LATCbits.LATC1 = 0; // select slave2
            SSPBUF = 0; // send 0 to SLAVE2
            LATCbits.LATC1 = 1; // deselect slave2
        }
       
        if(PORTBbits.RB0==0 && PORTBbits.RB1==1) // switch 1 is pressed
        {
            LATCbits.LATC0 = 0; // select slave1(active low)
            SSPBUF = 1; // send 1 to SLAVE1
            LATCbits.LATC0 = 1; // deselect slave1
           
            LATCbits.LATC1 = 0; // select slave2
            SSPBUF = 0; // send 0 to SLAVE2
            LATCbits.LATC1 = 1; // deselect slave2
        }
       
        if(PORTBbits.RB0==1 && PORTBbits.RB1==0) // switch 2 is pressed
        {
            LATCbits.LATC0 = 0; // select slave1(active low)
            SSPBUF = 0; // send 0 to SLAVE1
            LATCbits.LATC0 = 1; // deselect slave1
           
            LATCbits.LATC1 = 0; // select slave2
            SSPBUF = 1; // send 0 to SLAVE2
            LATCbits.LATC1 = 1; // deselect slave2
        }
    }
}
Code for the slave:
Code:
/*
 * File:   main.c
 * Author: Jarvis
 *
 * Created on June 7, 2016, 8:58 PM
 */


#include <xc.h>
#include "header.h"

void main()
{
    TRISAbits.RA5 = 1; // cs is input
    TRISCbits.RC3 = 1; // SCK is input
    TRISCbits.RC5 = 0;
    TRISB = 0; // LEDS on trisb
    LATB = 0; //all leds are off initially
   
    SMP = 0; // cleared in slave mode
    CKE = 0; // output data changes on clock transition from idle to active
    SSPEN = 1; // enables serial port and configures SCK, SDO, SDI and SS as serial ports
    CKP = 0; // idle state for clock is at low level
    SSPM3 = 0; // SPI SLAVE MODE
    SSPM2 = 1; // ss pin control enabled
    SSPM1 = 0; 
    SSPM0 = 0;
   
    while(1)
    {
        if(BF == 1)
        {
            if(SSPBUF==1) TRISBbits.RB0 = 1;
            else TRISBbits.RB0 = 0;
        }
    }
}
Header file(same for both):
Code:
// PIC18F26K20 Configuration Bit Settings

// 'C' source line config statements

#include <xc.h>

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

// CONFIG1H
#pragma config FOSC = INTIO67   // Oscillator Selection bits (Internal oscillator block, port function on RA6 and RA7)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor disabled)
#pragma config IESO = OFF       // Internal/External Oscillator Switchover bit (Oscillator Switchover mode disabled)

// CONFIG2L
#pragma config PWRT = OFF       // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable bits (Brown-out Reset disabled in hardware and software)
#pragma config BORV = 18        // Brown Out Reset Voltage bits (VBOR set to 1.8 V nominal)

// CONFIG2H
#pragma config WDTEN = OFF      // Watchdog Timer Enable bit (WDT is controlled by SWDTEN bit of the WDTCON register)
#pragma config WDTPS = 32768    // Watchdog Timer Postscale Select bits (1:32768)

// CONFIG3H
#pragma config CCP2MX = PORTC   // CCP2 MUX bit (CCP2 input/output is multiplexed with RC1)
#pragma config PBADEN = OFF     // PORTB A/D Enable bit (PORTB<4:0> pins are configured as digital I/O on Reset)
#pragma config LPT1OSC = OFF    // Low-Power Timer1 Oscillator Enable bit (Timer1 configured for higher power operation)
#pragma config HFOFST = ON      // HFINTOSC Fast Start-up (HFINTOSC starts clocking the CPU without waiting for the oscillator to stablize.)
#pragma config MCLRE = OFF      // MCLR Pin Enable bit (RE3 input pin enabled; MCLR disabled)

// CONFIG4L
#pragma config STVREN = ON      // Stack Full/Underflow Reset Enable bit (Stack full/underflow will cause Reset)
#pragma config LVP = ON         // Single-Supply ICSP Enable bit (Single-Supply ICSP enabled)
#pragma config XINST = OFF      // Extended Instruction Set Enable bit (Instruction set extension and Indexed Addressing mode disabled (Legacy mode))

// CONFIG5L
#pragma config CP0 = OFF        // Code Protection Block 0 (Block 0 (000800-003FFFh) not code-protected)
#pragma config CP1 = OFF        // Code Protection Block 1 (Block 1 (004000-007FFFh) not code-protected)
#pragma config CP2 = OFF        // Code Protection Block 2 (Block 2 (008000-00BFFFh) not code-protected)
#pragma config CP3 = OFF        // Code Protection Block 3 (Block 3 (00C000-00FFFFh) not code-protected)

// CONFIG5H
#pragma config CPB = OFF        // Boot Block Code Protection bit (Boot block (000000-0007FFh) not code-protected)
#pragma config CPD = OFF        // Data EEPROM Code Protection bit (Data EEPROM not code-protected)

// CONFIG6L
#pragma config WRT0 = OFF       // Write Protection Block 0 (Block 0 (000800-003FFFh) not write-protected)
#pragma config WRT1 = OFF       // Write Protection Block 1 (Block 1 (004000-007FFFh) not write-protected)
#pragma config WRT2 = OFF       // Write Protection Block 2 (Block 2 (008000-00BFFFh) not write-protected)
#pragma config WRT3 = OFF       // Write Protection Block 3 (Block 3 (00C000h-00FFFFh) not write-protected)

// CONFIG6H
#pragma config WRTC = OFF       // Configuration Register Write Protection bit (Configuration registers (300000-3000FFh) not write-protected)
#pragma config WRTB = OFF       // Boot Block Write Protection bit (Boot Block (000000-0007FFh) not write-protected)
#pragma config WRTD = OFF       // Data EEPROM Write Protection bit (Data EEPROM not write-protected)

// CONFIG7L
#pragma config EBTR0 = OFF      // Table Read Protection Block 0 (Block 0 (000800-003FFFh) not protected from table reads executed in other blocks)
#pragma config EBTR1 = OFF      // Table Read Protection Block 1 (Block 1 (004000-007FFFh) not protected from table reads executed in other blocks)
#pragma config EBTR2 = OFF      // Table Read Protection Block 2 (Block 2 (008000-00BFFFh) not protected from table reads executed in other blocks)
#pragma config EBTR3 = OFF      // Table Read Protection Block 3 (Block 3 (00C000-00FFFFh) not protected from table reads executed in other blocks)

// CONFIG7H
#pragma config EBTRB = OFF      // Boot Block Table Read Protection bit (Boot Block (000000-0007FFh) not protected from table reads executed in other blocks)

#define _XTAL_FREQ 8000000
My LED's won't light up when I run the code. Please help me correct this. Thank you.
 

Ian Rogers

User Extraordinaire
Forum Supporter
Most Helpful Member
WOW!!!! That took a while....
Observations.....

A)
if(SSPBUF==1) TRISBbits.RB0 = 1;
else TRISBbits.RB0 = 0;

Should be LATBbits.LATB = 1...

B) You open the CS pin and send the data... When you shut down the CS pins the data is corrupted
A small delay for the data to actually go was needed...

C) You specified an internal OSC but left the default at 500khz.... Set OSCCON to 0x70..

D) ( The one that foxed me... ) In the master... You wrote TRISBbits,RC4 = 0..... SD0 is on RC5...

C:
/*
 * File:  main.c
 * Author: Jarvis
 *
 * Created on June 7, 2016, 7:01 PM
 */


#include <xc.h>
#include "header.h"


void main()
{
   unsigned char DUMMY;
   OSCCON = 0x70;
   
  TRISCbits.RC0 = 0; // master's cs1
  TRISCbits.RC1 = 0; // master's cs2
  TRISCbits.RC3 = 0; // sck is output
  TRISCbits.RC5 = 0; // sdo is output
  TRISB = TRISB & 0x3; // RB0 and RB1 are inputs(buttons)
     ANSEL = ANSELH = 0;
   LATCbits.LATC0 = LATCbits.LATC1 = 1;
  SMP = 0; // input data sampled at end of data output time
  CKE = 0; // output data changes on clock transition from idle to active
  SSPEN = 1; // enables serial port and configures SCK, SDO, SDI and SS as serial ports
  CKP = 0; // idle state for clock is at low level
  SSPM3 = 0; // SPI MASTER MODE
  SSPM2 = 0; // clock
  SSPM1 = 0; // equals
  SSPM0 = 0; // FOSC/64
   LATCbits.LATC0 = LATCbits.LATC1 = 1;
   
  while(1)
  {
  if(PORTBbits.RB0==0 && PORTBbits.RB1==0) // both switches pressed
  {
  LATCbits.LATC0 = 0; // select slave1(active low)
  SSPBUF = 0; // send 0 to SLAVE1   
     __delay_ms(80);
       __delay_ms(80);
       LATCbits.LATC0 = 1; // deselect slave1
       DUMMY = SSPBUF;
  LATCbits.LATC1 = 0; // select slave2
  SSPBUF = 0; // send 0 to SLAVE2
  __delay_ms(80);
       __delay_ms(80);
       LATCbits.LATC1 = 1; // deselect slave2
       DUMMY = SSPBUF;
  }
   
  if(PORTBbits.RB0==1 && PORTBbits.RB1==1) // both switches not pressed
  {
  LATCbits.LATC0 = 0; // select slave1(active low)
  SSPBUF = 0; // send 0 to SLAVE1
  __delay_ms(80);
       __delay_ms(80);   
       LATCbits.LATC0 = 1; // deselect slave1
       DUMMY = SSPBUF;   
  LATCbits.LATC1 = 0; // select slave2
  SSPBUF = 0; // send 0 to SLAVE2
     __delay_ms(80);
       __delay_ms(80);
  LATCbits.LATC1 = 1; // deselect slave2
       DUMMY = SSPBUF;
  }
   
  if(PORTBbits.RB0==0 && PORTBbits.RB1==1) // switch 1 is pressed
  {
  LATCbits.LATC0 = 0; // select slave1(active low)
  SSPBUF = 1; // send 1 to SLAVE1
     __delay_ms(80);
       __delay_ms(80);
       LATCbits.LATC0 = 1; // deselect slave1
       DUMMY = SSPBUF;
  LATCbits.LATC1 = 0; // select slave2
  SSPBUF = 0; // send 0 to SLAVE2
     __delay_ms(80);
       __delay_ms(80);
  LATCbits.LATC1 = 1; // deselect slave2
       DUMMY = SSPBUF;
  }
   
  if(PORTBbits.RB0==1 && PORTBbits.RB1==0) // switch 2 is pressed
  {
  LATCbits.LATC0 = 0; // select slave1(active low)
  SSPBUF = 0; // send 0 to SLAVE1
     __delay_ms(80);
       __delay_ms(80);  
  LATCbits.LATC0 = 1; // deselect slave1
       DUMMY = SSPBUF;   
  LATCbits.LATC1 = 0; // select slave2
  SSPBUF = 1; // send 0 to SLAVE2
     __delay_ms(80);
       __delay_ms(80);
  LATCbits.LATC1 = 1; // deselect slave2
       DUMMY = SSPBUF;
  }
  }
}
C:
/*
 * File:  main.c
 * Author: Jarvis
 *
 * Created on June 7, 2016, 8:58 PM
 */


#include <xc.h>
#include "header.h"

void main()
{
   OSCCON = 0x70;
  TRISAbits.RA5 = 1; // cs is input
  TRISCbits.RC3 = 1; // SCK is input
  TRISCbits.RC5 = 0;
   ANSEL = ANSELH = 0;
  TRISB = 0; // LEDS on trisb
  LATB = 0; //all leds are off initially
   
  SMP = 0; // cleared in slave mode
  CKE = 0; // output data changes on clock transition from idle to active
  SSPEN = 1; // enables serial port and configures SCK, SDO, SDI and SS as serial ports
  CKP = 0; // idle state for clock is at low level
  SSPM3 = 0; // SPI SLAVE MODE
  SSPM2 = 1; // ss pin control enabled
  SSPM1 = 0;
  SSPM0 = 0;
   
  while(1)
  {
  if(BF == 1)
  {
  if(SSPBUF)
         LATBbits.LATB0 = 1;
  else
         LATBbits.LATB0 = 0;
  }
  }
}
Seems to work as expected...
 

Cantafford

Member
Thank you. Works fine. Can you please explain why we need to send some dummy data after deselecting one of the slaves?
 
Last edited:

DirtyLude

Well-Known Member
WOW!!!! That took a while....
Observations.....

B) You open the CS pin and send the data... When you shut down the CS pins the data is corrupted
A small delay for the data to actually go was needed...
I took a quick look at the spec as there is always some status bit to tell you when a transmission is complete. Spec says if you are polling like this to use the SSPSTAT.BF (Buffer Full) indicator to indicate when a transmission is complete. Otherwise the MSSP interrupt is used.

There's an example, unfortunately only in Assem.
LOOP BTFSS SSPSTAT, BF ;Has data been received (transmit complete)?

Thank you. Works fine. Can you please explain why we need to send some dummy data after deselecting one of the slaves?
He isn't sending dummy data, he is reading in the incoming data to clear the buffer.

Technically this isn't necessary according to the spec, unless you want to read the data or use the SSPSTAT.BF status bit as it only gets cleared on a read.
 

Ian Rogers

User Extraordinaire
Forum Supporter
Most Helpful Member
Thank you. Works fine. Can you please explain why we need to send some dummy data after deselecting one of the slaves?
I was just clearing the BF just in case it was giving me grief.... You can try deleting the line and see if it works...

It doesn't hamper the code so leave it for future debugging purposes..
 
Status
Not open for further replies.

Latest threads

EE World Online Articles

Loading
Top