如何使用 SPI 正确发送多个字符串

How to correctly send more than one string using SPI

我正在尝试使用 SPI 在两个 ATmega 之间进行通信。首先,我为两个 ATmega 初始化了 SPI。这里ATmega8是master,ATmega32是slave


硕士

主控初始化-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);
}

从主机发送数据的函数 - 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++;
    }
}

所以现在我有两个字符串要一一发送到 slave

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

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);
    }
}

这是主要问题,当我通过 SPI 只发送一个数组到从机时,我可以在 slave 端完美地获取数据,并通过 [=26 将其进一步传输到终端=].但是当我发送两个数组时,我并没有完美地接收到两个数组。我宁愿大部分时间接收一个数组,有时接收另一个数组。我希望 salve 一个一个地接收数组。首先,它将接收第一个数组,然后再次接收下一个数组。并且应该同时传输到UART。


奴隶

从机初始化-ATMEGA32:

此处ATmega32正在使用中断接收数据

#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);

}

slave-ATMEGA32 的 ISR:

这里我正在检查 \r,因为我从 master 发送的每个字符串都以 \r 结尾。因此,如果我得到 \r,我将设置 spi_data_recieved 变量,以便我可以在 main 中处理它(即可以通过 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 功能 - 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++);
}

主从-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);
    }
}

此设置的预期输出为:

Bus Voltage: 291.4

Frequency: 50.00

Bus Voltage: 291.4

Frequency: 50.00

并以这种方式连续

获取输出为:

SPI Data: 0

Frequency: 50.00

SPI Data: 0

Frequency: 50.00

SPI Data: 0

Frequency: 50.00

在极少数情况下,我可以看到总线电压。

我发现 ATmega 之间存在一些同步问题,但我不知道如何解决这个问题。

尝试在它们之间创建一个通信协议。

例如,对于大学练习,我必须创建一个 File Transfer Client/Server which used Vigenere Cipher

对于客户端和服务器之间的每笔交易,都有一个确认。

例如:

如果客户端向服务器发送命令,那么服务器会确认它确实收到了。接下来,服务器将 运行 命令并将数据发送回客户端。最后,客户端会发送一个它收到数据的确认信息。

此外,这里有一些链接可能会对您有所帮助:

您的代码的主要问题是您将简单数组作为零终止字符串传输:

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

没有零字节来终止字符串。 但是发送函数:

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

期待出现零字节。 因此,当发送第一个数组时,它将继续传输数组边界之外的许多字节,直到偶尔在内存中的某处遇到零字节。

同样在从代码中,我没有看到 data_arrayspi_data_recieved 是如何声明的。他们是volatile吗? 另外,查看 ISR 中的 Clear_Buffer(data_array, 10);,我想 data_array 声明的长度为 10:data_array[10]。 因此,ISR 中的 data_array[data_index] = SPDR; 可以写出数组边界,在它后面两个字节,破坏 RAM 的内容。