• AVR Freaks

Problem with PIC18F4550 SPI Slave communication

Author
Roberto93
New Member
  • Total Posts : 16
  • Reward points : 0
  • Joined: 2018/04/27 09:24:18
  • Location: 0
  • Status: offline
2018/10/30 18:06:49 (permalink)
0

Problem with PIC18F4550 SPI Slave communication

Hi, I'm trying to test the SPI interface of my 40 pin PDIP PIC18F4550 as a slave, with a master Arduino UNO. The basic functioning of the PIC code is to send a counter through SPI to the Arduino, and read the data the Arduino is sending (in this case, a constant). I'm having no luck receiving the data in the Arduino, as I can only see 0 in the serial monitor. Here's the code for the PIC set as Slave, using a 10MHz crystal:
 
*For some reason, posting the PIc code gives me an Access denied error, so I'll attach it as a file, sorry!*

Where Config_bits.h is the following (generated by the XC8 compiler). By the way, I noticed that PLLDIV = 3 implies a 12MHz crystal, but it doesn't have a divider for a 10MHz one, can it be the problem?:



// CONFIG1L
#pragma config PLLDIV = 3       // PLL Prescaler Selection bits (Divide by 3 (12 MHz oscillator input))
#pragma config CPUDIV = OSC2_PLL3// System Clock Postscaler Selection bits ([Primary Oscillator Src: /2][96 MHz PLL Src: /3])
#pragma config USBDIV = 1       // USB Clock Selection bit (used in Full-Speed USB mode only; UCFG:FSEN = 1) (USB clock source comes directly from the primary oscillator block with no postscale)

// CONFIG1H
#pragma config FOSC = HS        // Oscillator Selection bits (HS oscillator (HS))
#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 BOR = ON         // Brown-out Reset Enable bits (Brown-out Reset enabled in hardware only (SBOREN is disabled))
#pragma config BORV = 3         // Brown-out Reset Voltage bits (Minimum setting 2.05V)
#pragma config VREGEN = OFF     // USB Voltage Regulator Enable bit (USB voltage regulator disabled)

// CONFIG2H
#pragma config WDT = ON         // Watchdog Timer Enable bit (WDT enabled)
#pragma config WDTPS = 32768    // Watchdog Timer Postscale Select bits (1:32768)

// CONFIG3H
#pragma config CCP2MX = ON      // CCP2 MUX bit (CCP2 input/output is multiplexed with RC1)
#pragma config PBADEN = ON      // PORTB A/D Enable bit (PORTB<4:0> pins are configured as analog input channels on Reset)
#pragma config LPT1OSC = OFF    // Low-Power Timer 1 Oscillator Enable bit (Timer1 configured for higher power operation)
#pragma config MCLRE = ON       // MCLR Pin Enable bit (MCLR pin enabled; RE3 input pin 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 ICPRT = OFF      // Dedicated In-Circuit Debug/Programming Port (ICPORT) Enable bit (ICPORT disabled)
#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 bit (Block 0 (000800-001FFFh) is not code-protected)
#pragma config CP1 = OFF        // Code Protection bit (Block 1 (002000-003FFFh) is not code-protected)
#pragma config CP2 = OFF        // Code Protection bit (Block 2 (004000-005FFFh) is not code-protected)
#pragma config CP3 = OFF        // Code Protection bit (Block 3 (006000-007FFFh) is not code-protected)

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

// CONFIG6L
#pragma config WRT0 = OFF       // Write Protection bit (Block 0 (000800-001FFFh) is not write-protected)
#pragma config WRT1 = OFF       // Write Protection bit (Block 1 (002000-003FFFh) is not write-protected)
#pragma config WRT2 = OFF       // Write Protection bit (Block 2 (004000-005FFFh) is not write-protected)
#pragma config WRT3 = OFF       // Write Protection bit (Block 3 (006000-007FFFh) is not write-protected)

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

// CONFIG7L
#pragma config EBTR0 = OFF      // Table Read Protection bit (Block 0 (000800-001FFFh) is not protected from table reads executed in other blocks)
#pragma config EBTR1 = OFF      // Table Read Protection bit (Block 1 (002000-003FFFh) is not protected from table reads executed in other blocks)
#pragma config EBTR2 = OFF      // Table Read Protection bit (Block 2 (004000-005FFFh) is not protected from table reads executed in other blocks)
#pragma config EBTR3 = OFF      // Table Read Protection bit (Block 3 (006000-007FFFh) is not protected from table reads executed in other blocks)

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

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

#include <stdio.h>
#include <stdlib.h>



The Arduino is operating on MSB first, 10MHz on SPI clock and SPI_Mode0, which is explained in the attached image.

In case you need the Arduino code, it's the following:



#include <SPI.h>

#define SS_PIN 10

void setup()
{
    Serial.begin(9600);

    pinMode(SS_PIN, OUTPUT);
    digitalWrite(SS_PIN, HIGH);

    SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0));
}

char msg[100];
byte i, n;
void loop()
{
    i = 5;
    digitalWrite(SS_PIN, LOW);
    n = SPI.transfer(i);
    digitalWrite(SS_PIN, HIGH);

    sprintf(msg, "%d/%d, ", n, i);
    Serial.print(msg);
}



Returning, as mentioned before, 0 on the MISO and 5 on the MOSI, that is, in the sprintf format: 0/5. Arduino SPI connections are, as stated in the SPI library:

MOSI: pin 11, MISO: pin 12, SCK: pin 13 and SS: pin 10.

So, the connections with the PIC18F4550 are the following (I checked them before posting of course):

PIC pin 26 (SDO) <-> Arduino pin 12 (MISO)

PIC pin 33 (SDI) <-> Arduino pin 11 (MOSI)

PIC pin 34 (SCK) <-> Arduino pin 13 (SCK)

PIC pin 7 (SS) <-> Arduino pin 10 (SS)

Maybe it's a timing problem because of the 10MHz crystal? Or any of the configuration bits?

Any help will be appreciated,

Roberto
post edited by Roberto93 - 2018/10/30 18:13:02

Attached Image(s)

#1

7 Replies Related Threads

    Aussie Susan
    Super Member
    • Total Posts : 3638
    • Reward points : 0
    • Joined: 2008/08/18 22:20:40
    • Location: Melbourne, Australia
    • Status: offline
    Re: Problem with PIC18F4550 SPI Slave communication 2018/10/30 18:43:49 (permalink)
    0
    You need to understand that the SPI protocol will EXCHANGE values - therefore for the slave device you must write to the SSPBUF before the exchange takes place, and read from it afterwards.
    As for your setup in 'SPI_Slave_init', you have a number of issues:
    - the MSSP peripheral must be enabled AFTER the pins are correctly configured - see Note #2 on page 199 of the data sheet
    - the SPI 'modes' do not correspond directly with the CKP and CKE settings - there are a number of references about this but see https://www.microchip.com/forums/m368991.aspx
    - your settings for ADCON0 and CMCON are wrong - leave them at the Power On Reset settings (i.e. don't alter them at all) = BTW your setting for ADCON1 is correct
    - the comments on the settings for the TRISx registers are misleading. The default is for all bits to be '1' (input) but your comment about 'keeping all others unchanged' is wrong for many pins. All you need to do is 'TRISCbits.TRISC7 = 0;' to set the SDO pin to 'output'.
    With regard to the maximum SCK frequency, look at table 28-15 of the data sheet and check things out for yourself.
    Susan
    #2
    Roberto93
    New Member
    • Total Posts : 16
    • Reward points : 0
    • Joined: 2018/04/27 09:24:18
    • Location: 0
    • Status: offline
    Re: Problem with PIC18F4550 SPI Slave communication 2018/10/31 07:00:51 (permalink)
    0
    Thanks for the help, I'll fix the issues and come back to let you know if it solved the problem.
    #3
    Roberto93
    New Member
    • Total Posts : 16
    • Reward points : 0
    • Joined: 2018/04/27 09:24:18
    • Location: 0
    • Status: offline
    Re: Problem with PIC18F4550 SPI Slave communication 2018/10/31 08:48:19 (permalink)
    0
    Okay, so I believe everything is correctly set up this time, even changed the crystal to a 16MHz one to make sure the configuration bits match the crystal I'm using. The fixed code is the following (only posted the configuration bits that changed because of the new crystal):

    // CONFIG1L
    #pragma config PLLDIV = 4       // PLL Prescaler Selection bits (Divide by 4 (16 MHz oscillator input))
    #pragma config CPUDIV = OSC1_PLL2// System Clock Postscaler Selection bits ([Primary Oscillator Src: /1][96 MHz PLL Src: /2])
     
    #include <xc.h>

    #define _XTAL_FREQ 16000000

    void SPI_Slave_Init()
    {
        //SDI automatically controlled by SPI module
        TRISCbits.TRISC7 = 0;   //SDO outut
        TRISBbits.TRISB1 = 1;   //SCK input
        TRISAbits.TRISA5 = 1;   //SS input
        
        SSPSTAT = 0x00; //CKE from idle to active
        ADCON1 = 0x0F;  //RAi to digital
        
        SSPCON1 = 0x24; //No col, no overflow, ckp idle low, SS control active, clock on SCK pin
    }

    unsigned char SPI_read(unsigned char tx_data)
    {
        unsigned char rx_data;
        SSPBUF = tx_data;   //Write to buffer prior to reading
        while(!SSPSTATbits.BF); //Wait for reception to complete
        rx_data = SSPBUF;   //Read the data received in the SSPBUF
        SSPIF = 0;          //Clear the serial port interrupt flag
        
        return rx_data;
    }

    unsigned char data_tx = 200;
    unsigned char data_rx;

    void main() {
        SPI_Slave_Init();
        while(1){
            data_rx = SPI_read(data_tx);
            __delay_ms(100);
        }
    }

    But I'm still not able to communicate with the Arduino. I'm still receiving 0 on the master's side, even when trying with all SPI modes, just in case that was the problem. I'm pretty lost in what the error could be, maybe a timing thing, but checking the timing tables I think I've correctly set things up.
    Maybe it's just a problem with the PIC-Arduino communication and should try a PIC-PIC one instead?
    Roberto
     
    #4
    rodims
    Super Member
    • Total Posts : 1532
    • Reward points : 0
    • Joined: 2009/02/10 11:08:59
    • Location: 51.9627, 7.6262
    • Status: offline
    Re: Problem with PIC18F4550 SPI Slave communication 2018/10/31 09:51:23 (permalink)
    0
    I did not check any details, but if you think that the Arduino is doing its part as the master correctly,- did you already (debug)  set a breakpoint on the PIC / slave side ?
     
    Set a breakpoint on SPI_read  on SSPBUF = tx_data.
    Then start debugging, it should break now in that location.
    Then set a breakpoint on  the line rx_data = SSPBUF  and press F5 to continue execution.
    Only now let the Arduino perform its first SPI transfer. 
    Does your PIC break ? Step over the rx_data = SSPBUF and then look into rx_data. Does it contain the value 5 which you passed from the Arduino ?
    #5
    NorthGuy
    Super Member
    • Total Posts : 5817
    • Reward points : 0
    • Joined: 2014/02/23 14:23:23
    • Location: Northern Canada
    • Status: offline
    Re: Problem with PIC18F4550 SPI Slave communication 2018/10/31 10:09:21 (permalink)
    0
    I would first make sure that your PIC is running - you use external crystal and PLL. Blink a LED to make sure it runs, and make sure the speed is what you desire. Then start with SPI.
     
    <edit> Also, disable watchdog.
    #6
    Roberto93
    New Member
    • Total Posts : 16
    • Reward points : 0
    • Joined: 2018/04/27 09:24:18
    • Location: 0
    • Status: offline
    Re: Problem with PIC18F4550 SPI Slave communication 2018/10/31 12:25:12 (permalink)
    0
    Hi, thanks for your answers. I tested the speed and proper functioning of the PIC with a simple led blinking code, and everything worked fine. I also disabled the watchdog. Now, since there's a while loop in the code (for SSPSTATbits.BF), the debugger gets stuck in that line, so what I did was just set the PC right after it on every iteration (is there another way of doing this?). I did what rodims says, set a breakpoint on the SPI Tx and then on the SPI Rx, and then I try to send some test data, in this case, an unsigned char data_tx = 200 (or 0xc8). Now, for some reason, the data_rx, which should contain the value 5 sent from the Arduino, gets stuck on 200, which is the data_tx. It's like the data is never sent and I just read the buffer I wrote two lines before. If I change the data_tx, the data_rx changes to that new value on the next iteration. In an oscilloscope I can see the SS going up and down when I drive the line with a cable, but there's no data on any line after I set it low. What can be going on here?
     
    #7
    NorthGuy
    Super Member
    • Total Posts : 5817
    • Reward points : 0
    • Joined: 2014/02/23 14:23:23
    • Location: Northern Canada
    • Status: offline
    Re: Problem with PIC18F4550 SPI Slave communication 2018/10/31 12:50:39 (permalink)
    +1 (1)
    Roberto93
    In an oscilloscope I can see the SS going up and down when I drive the line with a cable, but there's no data on any line after I set it low. What can be going on here?



    Clock is supposed to be sent by Master (Arduino in your case). If you don't see the clock, look at the Arduino side until you get clock and and data on MOSI. Then come back to the PIC.
     
    PIC should never go through "while(!SSPSTATbits.BF);" until it sees 8 clock pulses.
     
    #8
    Jump to:
    © 2019 APG vNext Commercial Version 4.5