2
votes

I am trying to communicate between two ATmega using SPI. First, I initialized SPI for both ATmega. Here ATmega8 is master and ATmega32 is slave.


MASTER

Master initialization - ATMEGA8:

#define SPI_ddr             DDRB
#define SPI_port            PORTB
#define PIN_sck             5
#define PIN_mosi            3
#define PIN_ss              2
#define PIN_miso            4

#define SPI_PORT_enable()   (SPI_ddr |= (1 << PIN_sck) | (1 << PIN_mosi) | (1 << PIN_ss))
#define CS_High             SPI_port |= (1 << PIN_ss);
#define CS_Low              SPI_port &= ~(1 << PIN_ss);

void SPI_Init()
{
    SPI_PORT_enable();
    SPI_ddr &= ~(1 << PIN_miso);
    SPCR |= (1 << MSTR) | (1 << SPR0) | (1 << SPR1) | (1 <<SPE);
}

Functions to send data from master - ATMEGA8:

void send_char_SPI(char c)
{
    SPDR = c;
    /* Wait for transmission complete */
    while(!(SPSR & (1 << SPIF)));
}

void send_string_SPI(char s[])
{
    int i = 0;
    while (s[i] != 0x00)
    {
        send_char_SPI(s[i]);
        i++;
    }
}

so now I have two strings which I want to send to slave one by one.

char freq_array[] = {0x01, '5', '0', '.', '0', '0', '\r'};
char volt_array[] = {0x02, '2', '9', '1', '.', '4', '\r'};  

Main of master- ATMEGA8:

int main(void)
{
    SPI_Init();
    while (1) 
    {
        CS_Low;
        _delay_ms(10);
        send_string_SPI(volt_array);
        CS_High;
        _delay_ms(1000);

        CS_Low;
        _delay_ms(10);
        send_string_SPI(freq_array);
        CS_High;
        _delay_ms(1000);
    }
}

Here is the main problem while I send only one array through SPI to slave I can get the data perfectly on slave side and further transmit it to the terminal via UART. But while I send both of the arrays, I don't receive both arrays perfectly. I rather receive one array most of the time and the other array sometimes. I want that the salve receives the array one by one. First, it will receive the first array and then again it should receive the next array. and should transmit it to UART simultaneously.


SLAVE

Slave initialization - ATMEGA32:

Here ATmega32 is receiving data using Interrupt

#define SPI_ddr             DDRB
#define SPI_port            PORTB
#define PIN_sck             7
#define PIN_mosi            5
#define PIN_ss              4
#define PIN_miso            6
#define SPI_PORT_enable()   (SPI_ddr &= ~((1 << PIN_sck) | (1 << PIN_mosi) | (1 << PIN_ss)) )

void SPI_Init()
{
    SPI_PORT_enable();
    SPI_ddr |= (1 << PIN_miso);
    SPCR &= ~(1 << MSTR);
    SPCR |= (1<<SPR0)|(1<<SPR1);       // divide clock by 128
    SPCR |= (1<<SPE);                  // Enable SPI
    SPCR |= (1<<SPIE);

}

ISR of slave- ATMEGA32:

Here I am checking for \r as every string i am sending from master ends with \r. So if I get \r I will set spi_data_recieved variable so that I can process it in main (i.e can send the received data to the terminal via UART).

ISR(SPI_STC_vect)
{
    data_array[data_index] = SPDR;
    if(data_array[data_index] == '\r')
        spi_data_recieved = 1;
    if (data_index > 10)
    {
        data_index = 0;
        Clear_Buffer(data_array, 10);
    }
    else
    {
        data_index++;
    }
}

UART functions on slave - ATMEGA32:

void Serial_Init(unsigned int baud)
{
    UBRRH = (unsigned char)(baud >> 8);
    UBRRL = (unsigned char)baud;

    UCSRB = (1 << RXEN)  | (1 << TXEN) ;
    UCSRC = (1 << URSEL) | (3 << UCSZ0);

}

void Serial_Transmit(unsigned char data)
{
    UDR = data;
    while ( !( UCSRA & (1 << UDRE) ) );
}

void Serial_Transmit_String(unsigned char *string)
{
    while (*string != 0)
    Serial_Transmit(*string++);
}

Main of slave - ATMEGA32:

int main(void)
{
    SPI_Init();
    Serial_Init(103);
    data_index = 0;
    spi_data_recieved = 0;
    sei();
    Serial_Transmit_String((unsigned char*)"TESTING SPI\r\n");
    unsigned char copyBuff[10];
    while (1) 
    {
        if (spi_data_recieved == 1)
        {
            spi_data_recieved = 0;
            strcpy((char*)copyBuff, (char*)data_array);
            Clear_Buffer(data_array, 10);
            data_index = 0;
            switch(copyBuff[0])
            {
                case 0x01: Serial_Transmit_String((unsigned char*)"Frequency: "); break;
                case 0x02: Serial_Transmit_String((unsigned char*)"Bus Voltage: "); break;
                default: Serial_Transmit_String((unsigned char*)"SPI Data: "); break;
            }

            Serial_Transmit_String(copyBuff);
        }

        _delay_ms(10);
    }
}

Expected output for this setup is:

Bus Voltage: 291.4

Frequency: 50.00

Bus Voltage: 291.4

Frequency: 50.00

and continuous in this manner

Getting the output as:

SPI Data: 0

Frequency: 50.00

SPI Data: 0

Frequency: 50.00

SPI Data: 0

Frequency: 50.00

And in very rare case I could see the bus voltage.

I figured out there is some synchronization issue between the ATmega's but I don't know how to solve this problem.

2

2 Answers

1
votes

The main problem of your code is you're transmitting simple arrays as zero-terminated strings:

char freq_array[] = {0x01, '5', '0', '.', '0', '0', '\r'};

there is no zero byte to terminate the string. But the sending function:

void send_string_SPI(char s[])
    ...
    while (s[i] != 0x00)
       ...

expecting a zero byte to appear. So, when sending the first array it will continue to transmit many many bytes out of the array bounds until there is occasionally a zero byte is met somewhere in the memory.

Also in the slave code, I do not see how data_array and spi_data_recieved declared. Are they volatile? Also, looking at Clear_Buffer(data_array, 10); in the ISR I suppose data_array is declared of length 10: data_array[10]. So, data_array[data_index] = SPDR; in the ISR can write out of the array bounds, two bytes behind it, damaging the content of the RAM.

0
votes

Try to create a communication protocol between them.

For example, for a college exercise, I had to create a File Transfer Client/Server which used Vigenere Cipher

For each transaction between the client and the server, there was a confirmation.

For example:

If the client sent a command to the server, then the server would confirm that it did received it. Next, the server would run the command and send the data back to the client. Finally, the client would send a confirmation that it got the data.

Also, here are some links that may help you: