Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

[SOLVED] SPI Communication using Interrupt

Status
Not open for further replies.

Hayee

Member level 4
Member level 4
Joined
Mar 4, 2022
Messages
68
Helped
1
Reputation
2
Reaction score
3
Trophy points
8
Activity points
706
Hi Guys
I am using two PIC18f46K22 microcontrollers with MPLAB XC8 IDE.
I write a code in which Master sends 1 byte and in return I am receiving 1 byte and display it on LCD. It is working fine. I am receiving from slave letter "H" and displaying it on LCD as the following image shows.
Now I want to receive two bytes from Slave by sending only 1 byte from Master. How can I do that.
I tried it but not successful. even i tried to receive the 2nd byte only but now able to do so.
Can you figure me out where I am making a mistake or how to do it?
1714734808805.png

I am unable to upload the code files so I am copy pasting here

Master.c

Code:
#include <xc.h>
#include <string.h>
#include "SPI_Host_V2.h"

void OSCILLATOR_Init(){
    /*========= 20MHz External Crystal Oscillator Configuration =========*/
    OSCCON = 0x38;
    OSCCON2 = 0x00;
    OSCTUNE = 0x00;
}

/* Settings PIN's Direction for SPI-1 */
void PORT_Init(){
    
    TRISC = 0b00010000; /* SDI as Input, SDO SCK SS1 as Output*/
    ANSELC = 0x00;      /* PORT C all Pins configured as Digital Pins*/
    
    TRISD = 0b10000000; /* PORT D all Pins configured as Input Pins*/
    ANSELD = 0x00;      /* PORT D all Pins configured as Digital Pins */
    
    TRISB = 0b00000000; /* PORT B all Pins configured as Output Pins*/
    ANSELB = 0x00;      /* PORT B all Pins configured as Digital Pins*/
}

// 'putch' function is required for the 'printf' function to work
// here it is used to print a character on the LCD using the function 'LCD_PutC'
void putch(char c)
{
    LCD_PutC(c);
}

/* Set the desired setting for SPI-1*/
void SPI1_Init(uint8_t registerValue){
    SSP1CON1 = registerValue;
}

/* Routine to send data */
void SPI1_SendData (uint8_t data){
    SSP1BUF = data;             // Sends the value of data
    
    while(!PIR1bits.SSP1IF);    // wait for the transmit/receive to be complete.
    PIR1bits.SSP1IF = 0;        // reset the SSP1IF register.
}


void Slave_HighLow(uint8_t state){
    if(state == LOW)    LATCbits.LATC2 = 0;
    else                LATCbits.LATC2 = 1;
}

void main(void) {
    
    char RcvByte[2];
    
    OSCILLATOR_Init();
    PORT_Init();
    SPI1_Init(WCOL_DIS|SSP1OV_DIS|SSP1EN_EN|CKP_LOW|SPI_HOST_FOSC_64);
    
    LCD(&PORTD, 0,1,2,3,4,5);   // set the connection between the MCU and the LCD
    LCD_Begin(16,2);            // initialize the LCD module with 16 rows and 2 columns (1602 LCD)
    
    while(1)
    {
        Slave_HighLow(LOW);

        SPI1_SendData(0);
        while(!SSP1STATbits.BF);
        SSP1STATbits.BF = 0;
        RcvByte[0] = SSP1BUF;
        
//        SPI1_SendData(1);
//        while(!SSP1STATbits.BF);
//        SSP1STATbits.BF = 0;
//        RcvByte[1] = SSP1BUF;

        Slave_HighLow(HIGH);
        __delay_ms(1);
        
        LCD_Goto(1,1);
        printf("Hello");
        
        LCD_Goto(1,2);
        printf("%s", RcvByte);
    }
    return;
}

Master.h
Code:
#include <stdio.h>
#include "LCD.h"

#define LOW     0
#define HIGH    1

#define SLAVE_1 1

#define WCOL_EN             (1 << 7)
#define WCOL_DIS            (0 << 7)
#define SSP1OV_EN           (1 << 6)
#define SSP1OV_DIS          (0 << 6)
#define SSP1EN_EN           (1 << 5)
#define SSP1EN_DIS          (0 << 5)
#define CKP_HIGH            (1 << 4)
#define CKP_LOW             (0 << 4)
#define SPI_HOST_FOSC_64    2
#define SPI_CLIENT_SS_EN    4

#define ENABLE  1
#define DISABLE 0

uint32_t _XTAL_FREQ = 20000000;

Slave.c
Code:
#include "SPI_Slave1_V2.h"

void OSCILLATOR_Init(){
    /*========= 20MHz External Crystal Oscillator Configuration =========*/
    OSCCON = 0x38;
    OSCCON2 = 0x00;
    OSCTUNE = 0x00;
}

/* Set the desired setting for SPI-1*/
void SPI1_Init(uint8_t registerValue){
    SSP1CON1 = registerValue;
}

/* Function for Enable or Disable the SPI Interrupt*/
void SPI1_INTERRUPT(uint8_t EnorDi){
    if(EnorDi)  PIE1bits.SSP1IE = 1;
    else        PIE1bits.SSP1IE = 0;
}

/* Function for Enable or Disable the Global Interrupt*/
void GLOBAL_INTERRUPT(uint8_t EnorDi){
    if(EnorDi)  INTCONbits.GIE = 1;
    else        INTCONbits.GIE = 0;
    
}
/* Function for Enable or Disable the Peripheral Interrupt*/
void PERIPHERAL_INTERRUPT(uint8_t EnorDi){
    if(EnorDi)  INTCONbits.PEIE = 1;
    else        INTCONbits.PEIE = 0;
}

/* Settings PIN's Direction for SPI-1 */
void PORT_Init(){
    
    TRISC = 0b00011000; /* SDI SCK as Input, SDO as Output */
    ANSELC = 0x00;      /* All Pins Configured as Digital */
        
    ANSELA = 0x00;      /* SS Pin Configured as Digital*/
    
    TRISD = 0b11111111; /* PORTD all pins configured as Output Pins*/
    ANSELD = 0x00;      /* PORTD all pins configured as Digital Pins*/
}

/* Routine to send data */
void SPI1_SendData (char data){
    SSP1BUF = data;             // Sends the value of data
    
    while(!PIR1bits.SSP1IF);    // wait for the transmit/receive to be complete.
    PIR1bits.SSP1IF = 0;        // reset the SSP1IF register.
}

/*SPI Interrupt Routine.*/
void __interrupt() SPI_ISR(){
    if(PIR1bits.SSP1IF) // If SPI Interrupt Receive
    {
        if(SSP1STATbits.BF){
            receivedByte = SSP1BUF; // store buffer value in a variable
            
            if(receivedByte == 0) SPI1_SendData(message[0]);    //Send Array position 0 value
            if(receivedByte == 1) SPI1_SendData(message[1]);    //Send Array position 1 value
        }
        PIR1bits.SSP1IF = 0;        // Clear the Interrupt Flag.
    }
}

void main(void) {
    OSCILLATOR_Init();
    PORT_Init();
    SPI1_Init(WCOL_DIS|SSP1OV_DIS|SSP1EN_EN|CKP_LOW|SPI_CLIENT_SS_EN);
    SPI1_INTERRUPT(ENABLE);
    GLOBAL_INTERRUPT(ENABLE);
    PERIPHERAL_INTERRUPT(ENABLE);
    
    while(1){
    }
    return;
}

Slave.h
Code:
#include <xc.h>
#include <stdio.h>
#include <string.h>

#define _XTAL_FREQ 20000000

#define WCOL_EN             (1 << 7)
#define WCOL_DIS            (0 << 7)
#define SSP1OV_EN           (1 << 6)
#define SSP1OV_DIS          (0 << 6)
#define SSP1EN_EN           (1 << 5)
#define SSP1EN_DIS          (0 << 5)
#define CKP_HIGH            (1 << 4)
#define CKP_LOW             (0 << 4)
#define SPI_HOST_FOSC_64    2
#define SPI_CLIENT_SS_EN    4

#define ENABLE  1
#define DISABLE 0

uint8_t receivedByte;

char message[2] = "HI";
 
Last edited:

Now I want to receive two bytes from Slave by sending only 1 byte from Master. How can I do that.
Impossible.

Read about basic SPI interface operation.

--> If the MASTER wants to receive two bytes, it needs to send two bytes.

So the first thing you need to do is to decide/write down a useful protocol.
If you want only the first byte (M-->S) to be valid, then all consecutive bytes are called "dummy".
The master needs to send them.
The slave needs to ignore them.

Klaus
 
Yes I read some articles about SPI.
I tried polling based communication and in that Master every time send data to slave in order to receive all the bytes.
Now I was implementing the interrupt routine.

Clear me 2 things
1- when Master sends data then Clock signal also be generated. right? Not always clock signal is available.
2- when Interrupt is received in Slave then when complete 8 bits are received then SSP1IF will be high. right?
 

Clear me 2 things
1- when Master sends data then Clock signal also be generated. right? Not always clock signal is available.
2- when Interrupt is received in Slave then when complete 8 bits are received then SSP1IF will be high. right?

1) yes. This is basic SPI operation. Please give a link to the documents you have read.
2) This should be documented in the PIC datasheet. I´m not experienced with PICs. I don´t have the datasheet .. you gave no link to the datasheet.

Klaus
 

1) yes. This is basic SPI operation. Please give a link to the documents you have read.

This should be documented in the PIC datasheet. I´m not experienced with PICs. I don´t have the datasheet .. you gave no link to the datasheet.

1714800010184.png


https://www.microchip.com/en-us/product/pic18f46k22#document-table
from here you can find out the datasheet.

I tried to make a program as follows
First Master send RESET_POSITION command (RESET_POSITION = 1, when slave first receive this then index will be reset)
In response Slave send a dummy value

Then Master send dummy data.
In response Slave have to send Letter 'H'

Then again Master send dummy data.
In response Slave have to send Letter 'I'

but what is happening Master is receiving 'I' Letter first, then dummy value, then 'H' Letter. I don't know why.

Master.c
Code:
#include <xc.h>
#include <string.h>
#include "SPI_Host_V2.h"

void OSCILLATOR_Init(){
    /*========= 20MHz External Crystal Oscillator Configuration =========*/
    OSCCON = 0x38;
    OSCCON2 = 0x00;
    OSCTUNE = 0x00;
}

/* Settings PIN's Direction for SPI-1 */
void PORT_Init(){
    
    TRISC = 0b00010000; /* SDI as Input, SDO SCK SS1 as Output*/
    ANSELC = 0x00;      /* PORT C all Pins configured as Digital Pins*/
    
    TRISD = 0b10000000; /* PORT D all Pins configured as Input Pins*/
    ANSELD = 0x00;      /* PORT D all Pins configured as Digital Pins */
    
    TRISB = 0b00000000; /* PORT B all Pins configured as Output Pins*/
    ANSELB = 0x00;      /* PORT B all Pins configured as Digital Pins*/
}

// 'putch' function is required for the 'printf' function to work
// here it is used to print a character on the LCD using the function 'LCD_PutC'
void putch(char c)
{
    LCD_PutC(c);
}

/* Set the desired setting for SPI-1*/
void SPI1_Init(uint8_t registerValue){
    SSP1CON1 = registerValue;
}

/* Routine to send data */
void SPI1_SendData (uint8_t data){
    SSP1BUF = data;             // Sends the value of data
    
    while(!PIR1bits.SSP1IF);    // wait for the transmit/receive to be complete.
    PIR1bits.SSP1IF = 0;        // reset the SSP1IF register.
}


void Slave_HighLow(uint8_t state){
    if(state == LOW)    LATCbits.LATC2 = 0;
    else                LATCbits.LATC2 = 1;
}

void main(void) {
    
    char RcvByte[3];
    
    OSCILLATOR_Init();
    PORT_Init();
    SPI1_Init(WCOL_DIS|SSP1OV_DIS|SSP1EN_EN|CKP_LOW|SPI_HOST_FOSC_64);
    
    LCD(&PORTD, 0,1,2,3,4,5);   // set the connection between the MCU and the LCD
    LCD_Begin(16,2);            // initialize the LCD module with 16 rows and 2 columns (1602 LCD)
    
    while(1)
    {
        Slave_HighLow(LOW);

        SPI1_SendData(RESET_POSITION);
        LCD_Goto(6,1);
        printf("%u",SSP1BUF);       // should receive dummy data from Slave
        
        SPI1_SendData(DUMMY_DATA);
        RcvByte[0] = SSP1BUF;       // should receive Letter 'H'
        LCD_Goto(9,1);
        printf("%u",SSP1BUF);

        SPI1_SendData(DUMMY_DATA);
        RcvByte[1] = SSP1BUF;       // should receive Letter 'I'
        LCD_Goto(12,1);
        printf("%u",SSP1BUF);
        
        Slave_HighLow(HIGH);
        __delay_ms(1);
        
        LCD_Goto(1,1);
        printf("Hello");
        
        LCD_Goto(1,2);  printf("%c", RcvByte[0]);
        LCD_Goto(3,2);  printf("%c", RcvByte[1]);
    }
    return;
}

Slave.h
Code:
#include "SPI_Slave1_V2.h"

void OSCILLATOR_Init(){
    /*========= 20MHz External Crystal Oscillator Configuration =========*/
    OSCCON = 0x38;
    OSCCON2 = 0x00;
    OSCTUNE = 0x00;
}

/* Set the desired setting for SPI-1*/
void SPI1_Init(uint8_t registerValue){
    SSP1CON1 = registerValue;
}

/* Function for Enable or Disable the SPI Interrupt*/
void SPI1_INTERRUPT(uint8_t EnorDi){
    if(EnorDi)  PIE1bits.SSP1IE = 1;
    else        PIE1bits.SSP1IE = 0;
}

/* Function for Enable or Disable the Global Interrupt*/
void GLOBAL_INTERRUPT(uint8_t EnorDi){
    if(EnorDi)  INTCONbits.GIE = 1;
    else        INTCONbits.GIE = 0;
    
}
/* Function for Enable or Disable the Peripheral Interrupt*/
void PERIPHERAL_INTERRUPT(uint8_t EnorDi){
    if(EnorDi)  INTCONbits.PEIE = 1;
    else        INTCONbits.PEIE = 0;
}

/* Settings PIN's Direction for SPI-1 */
void PORT_Init(){
    
    TRISC = 0b00011000; /* SDI SCK as Input, SDO as Output */
    ANSELC = 0x00;      /* All Pins Configured as Digital */
        
    ANSELA = 0x00;      /* SS Pin Configured as Digital*/
    
    TRISD = 0b11111111; /* PORTD all pins configured as Output Pins*/
    ANSELD = 0x00;      /* PORTD all pins configured as Digital Pins*/
}

/* Routine to send data */
void SPI1_SendData (char data){
    SSP1BUF = data;             // Sends the value of data
    
    while(!PIR1bits.SSP1IF);    // wait for the transmit/receive to be complete.
    PIR1bits.SSP1IF = 0;        // reset the SSP1IF register.
}

/*SPI Interrupt Routine.*/
void __interrupt() SPI_ISR(){
    if(PIR1bits.SSP1IF) // If SPI Interrupt Receive
    {
        if(SSP1STATbits.BF){
            
            receivedByte = SSP1BUF; // store buffer value in a variable
            
            if(receivedByte == 1){  // if RESET_POSITION value received then reset spi_counter and send dummy value
                LATCbits.LATC7 = ~LATCbits.LATC7;
                spi_counter = 0;    //Send Array position 0 value
                SPI1_SendData(10);
            }
            else{
                LATCbits.LATC6 = ~LATCbits.LATC6;
                SPI1_SendData(message[spi_counter]);
                spi_counter++;
            }
        }
        SSP1STATbits.BF = 0;
        PIR1bits.SSP1IF = 0;        // Clear the Interrupt Flag.
    }
}

void main(void) {
    OSCILLATOR_Init();
    PORT_Init();
    SPI1_Init(WCOL_DIS|SSP1OV_DIS|SSP1EN_EN|CKP_LOW|SPI_CLIENT_SS_EN);
    SPI1_INTERRUPT(ENABLE);
    GLOBAL_INTERRUPT(ENABLE);
    PERIPHERAL_INTERRUPT(ENABLE);
    
    while(1){
    }
    return;
}

1714801061504.png



Kindly tell me what I am doing wrong.
 

73 belongs to Char I, 72 belongs to char H

The pattern have to be like this ----- dummy value (10), then H, then I
But I am receiving like this ----- I, then dummy value (10) and then H

I am not getting this what mistake I have made.
 

You are apparently out of sync . I'd expect that you read SSP1STAT to sync with bus actions, specifically AD and RW bit.

Sorry, I was assuming I2C communication. But out of sync anyway.
--- Updated ---

Probably a misunderstanding how responses are send. A byte transaction is essentially read and write in one. Respectively response data has to be loaded on the slave side before the transaction starts.
 
Last edited:

    Hayee

    Points: 2
    Helpful Answer Positive Rating
You need to make sure that the slave has a value in its buffer BEFORE the master initiates the transfer.
This is one of the unusual things about SPI exchanges (as opposed to UART connections where you can send a command and later receive a response).
Therefore:
- slave sets up a dummy value to send when the master sends the command
- the master initiates the exchange so that the slave gets the command and the master receives the dummy value
- the slave then needs to react to the command and prepare the response
- the master might need to wait while the slave responds to the command - this is all part of your design
- the master then initiates another exchange sending a dummy value (or perhaps the next command) and receiving the response to the first command from the slave
Repeat the last 3 steps as long as you need to.
Susan
 

    Hayee

    Points: 2
    Helpful Answer Positive Rating
Status
Not open for further replies.

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top