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 not working on Slave

Status
Not open for further replies.

ki@n

Newbie level 5
Joined
Aug 13, 2022
Messages
10
Helped
0
Reputation
0
Reaction score
0
Trophy points
1
Activity points
64
Hi Everyone
In an AVR project, I wanted to transfer data between master and slave and meanwhile,show both transmitted and received data on two LCDs.
The Master micro works fine but for slave micro it doesn't show the received data on lcd and I can't really figure out why.
Here are my codes (The master is an atmega16 and slave is an atmega32)
C:
//Master Code for ATmega16
#define F_CPU 8000000UL
//--------------------Libraries--------------------
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include <string.h>
#include "lcd1/lcd.h"


//-------------------Functions-----------------------------------------------------------------
void SPI_init()
{
    DDRB = (1<<DDB7)|(0<<DDB6)|(1<<DDB5)|(1<<DDB4);
    PORTB = (1<<PB4);
    DDRD = 0xff;
    SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0);
}

void SPI_data_write(char data)
{
    char flush_buffer;
    SPDR = data;
    while(!(SPSR & (1<<SPIF)));
    flush_buffer = SPDR;
}

char SPI_read_data()
{
    SPDR = 0xff;
    while(!(SPSR & (1<<SPIF)));
    return(SPDR); 
}
//-------------------Functions-----------------------------------------------------------------

int main(void)
{
    uint8_t count;
    char buffer[5];
    lcd_init(LCD_DISP_ON);
    lcd_clrscr();
    SPI_init();
    lcd_gotoxy(0,0);
    lcd_puts("Master Device");
    lcd_gotoxy(0,1);
    lcd_puts("Data Sent:");
    count = 0;
    while (1)
    {
        SPI_data_write(count);
        sprintf(buffer,"%d",count);
        lcd_gotoxy(12,1);
        lcd_puts(buffer);
        count++;
        _delay_us(25000);
  
    }
}

C:
#define F_CPU 8000000UL
//-------------------------Libraries-------------------------
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include <string.h>
#include "lcd/lcd.h"
//-------------------------Functions-------------------------
void SPI_init()
{
    DDRB = (0<<DDB7)|(1<<DDB6)|(0<<DDB5)|(0<<DDB4);
    DDRD = 0xff;
    SPCR = (1<<SPE);
}

char spi_read()
{
    while(!(SPSR & (1<<SPIF)));
    return(SPDR);
}
//------------------------------------------------------------
int main(void)
{
    uint8_t count;
    char buffer[5];
    lcd_init(LCD_DISP_ON);
    SPI_init();
    lcd_clrscr();
    lcd_gotoxy(0,0);
    lcd_puts("Slave Device");
    lcd_gotoxy(0,1);
    lcd_puts("Rec Data:");
    while (1)
    {
        count = spi_read();
        sprintf(buffer,"%d",count); 
        lcd_gotoxy(10,1);
        lcd_puts(buffer);
    }
}

Schematic in Proteus:
Screenshot 2022-09-03 105823.png

I have also attached my LCD libraries
 

Attachments

  • lcdlib.zip
    7.8 KB · Views: 115

Solution
Hi,

How is /SS handled?
If not implemented yet, please do so. You need it for (byte-) synchronizing both devices.

form naming I recommend to use more clear names for your functions:
like:
mRead for MasterRead
sRead for SlaveRead
and so on.

Afaik: the phrase "flush" is used to clear/empty a buffer.

Btw: your code is completely uncommented, thus hard to understand. I don´t spend my time to find out what each line of your code is intended to do.

Klaus
Hi,

How is /SS handled?
If not implemented yet, please do so. You need it for (byte-) synchronizing both devices.

form naming I recommend to use more clear names for your functions:
like:
mRead for MasterRead
sRead for SlaveRead
and so on.

Afaik: the phrase "flush" is used to clear/empty a buffer.

Btw: your code is completely uncommented, thus hard to understand. I don´t spend my time to find out what each line of your code is intended to do.

Klaus
 
Solution
Hi,

How is /SS handled?
If not implemented yet, please do so. You need it for (byte-) synchronizing both devices.

form naming I recommend to use more clear names for your functions:
like:
mRead for MasterRead
sRead for SlaveRead
and so on.

Afaik: the phrase "flush" is used to clear/empty a buffer.

Btw: your code is completely uncommented, thus hard to understand. I don´t spend my time to find out what each line of your code is intended to do.

Klaus
Hi
I wrote some comments and I hope it is more apparent now
C:
//Master Code for ATmega16
#define F_CPU 8000000UL
//--------------------Libraries--------------------
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include <string.h>
#include "lcd1/lcd.h"


//-------------------Functions-----------------------------------------------------------------
void masterSPI_init()//SPI Ports initialization
{
    DDRB = (1<<DDB7)|(0<<DDB6)|(1<<DDB5)|(1<<DDB4);//SCK->Out MISO->In MOSI & SS -> Out
    PORTB = (1<<PB4);//SS held high
    DDRD = 0xff;//for sending data to LCD
    SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0); //enable SPI and Master mode,
}

void master_data_write(char data)//witing the data to SPDR
{
    SPDR = data;
    while(!(SPSR & (1<<SPIF)));//wait untill the data is sent,wihtout this while loop data overrun will occurs
}

char master_read_data()//for storing junk data recived from slave
{
    SPDR = 0xff;
    while(!(SPSR & (1<<SPIF)));
    return(SPDR);  
}
//-------------------Functions-----------------------------------------------------------------

int main(void)
{
    uint8_t count;
    char buffer[5];
    lcd_init(LCD_DISP_ON);
    lcd_clrscr();//LCD clear
    masterSPI_init();
    lcd_gotoxy(0,0);
    lcd_puts("Master Device");
    lcd_gotoxy(0,1);
    lcd_puts("Data Sent:");
    count = 0;
    while (1)
    {
        master_data_write(count);
        sprintf(buffer,"%d",count);//storing int in a string
        lcd_gotoxy(12,1);
        lcd_puts(buffer);
        count++;
        _delay_us(25000);
   
    }
}
C:
//Slave Code for ATmega 32
#define F_CPU 8000000UL
//-------------------------Libraries-------------------------
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include <string.h>
#include "lcd/lcd.h"
//-------------------------Functions-------------------------
void slaveSPI_init()//SPI Ports initialization
{
    DDRB = (0<<DDB7)|(1<<DDB6)|(0<<DDB5)|(0<<DDB4);//SCK->Input Miso->Out MOSI & SS -> Input
    PORTB = (0<<PB4);//for slave SS is held low
    DDRD = 0xff;//for sending data to LCD
    SPCR = (1<<SPE);//enable SPI
}

char slavespi_read()//Reading the received data
{
    while(!(SPSR & (1<<SPIF)));//wait until the data is received(if it is received, the interrupt flag will be set)
    return(SPDR);
}
//------------------------------------------------------------
int main(void)
{
    uint8_t count;
    char buffer[5];
    lcd_init(LCD_DISP_ON);
    slaveSPI_init();
    lcd_clrscr();//LCD clear screen
    lcd_gotoxy(0,0);
    lcd_puts("Slave Device");
    lcd_gotoxy(0,1);
    lcd_puts("Rec Data:");
    while (1)
    {
        count = slavespi_read();
        sprintf(buffer,"%d",count);//the counter variable is changed to string    
        lcd_gotoxy(10,1);
        lcd_puts(buffer);
    }
}
 

As stated, we don't see SS operated on the master side. See ATmega data sheet:
When configured as a Master, the SPI interface has no automatic control of the SS line. This must be handled by user software before communication can start.
 
...

Additionally to FvM..
Slave side:
//for slave SS is held low
This is not true. Your code makes the pin to float, which is fine, since it should be controlled by the master.
But you need to implement a synchronizing mechanism.
* when HIGH: SPI is meant inactive. Data may be flushed, interface may be reset
* when LOW: SPI active. Data will be received.

Btw: I'd never code the slave to do a "busy wait" for data receive. This consumes 100% processing power and it's impossible for the microcontroller to do anything else. In this condition the microcontroller is blocked, frozen.
I'd do it with interrupts. But you may do it polled style: (pseudo code)
Code:
Loop{
   If spi data available {
      Get and process the spi data
   }
   Do other stuff
}

Klaus
 
As stated, we don't see SS operated on the master side. See ATmega data sheet:
Here I tried to set DDRB4 and PORTB4 to 1, so isn't it enough?
C:
DDRB = (1<<DDB7)|(0<<DDB6)|(1<<DDB5)|(1<<DDB4);//SCK->Out MISO->In MOSI & SS -> Out
PORTB = (1<<PB4);//SS held high
 

Here I tried to set DDRB4 and PORTB4 to 1, so isn't it enough?
Read dataheets,
read what I wrote: 1 = HIGH = SPI inactive

How do you think the slave receives data while the master says SPI is inactive?

As already written SS should tell when to start and when to stop communication. No steady state on /SS.
Again: you need /SS for byte synchronizing.

Klaus
 
Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top