如何使用 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
对于客户端和服务器之间的每笔交易,都有一个确认。
例如:
如果客户端向服务器发送命令,那么服务器会确认它确实收到了。接下来,服务器将 运行 命令并将数据发送回客户端。最后,客户端会发送一个它收到数据的确认信息。
此外,这里有一些链接可能会对您有所帮助:
- https://circuitdigest.com/microcontroller-projects/uart-communication-between-two-atmega8-microcontrollers
- https://www.instructables.com/id/Atmega32-Atmega8-Master-Slave-SPI-Communication/
- http://exploreembedded.com/wiki/Serial_UART_Interface_with_AVR
- https://www.researchgate.net/publication/281509696_Serial_Communications_with_AVR_Microcontroller
您的代码的主要问题是您将简单数组作为零终止字符串传输:
char freq_array[] = {0x01, '5', '0', '.', '0', '0', '\r'};
没有零字节来终止字符串。
但是发送函数:
void send_string_SPI(char s[])
...
while (s[i] != 0x00)
...
期待出现零字节。
因此,当发送第一个数组时,它将继续传输数组边界之外的许多字节,直到偶尔在内存中的某处遇到零字节。
同样在从代码中,我没有看到 data_array
和 spi_data_recieved
是如何声明的。他们是volatile
吗?
另外,查看 ISR 中的 Clear_Buffer(data_array, 10);
,我想 data_array
声明的长度为 10:data_array[10]
。
因此,ISR 中的 data_array[data_index] = SPDR;
可以写出数组边界,在它后面两个字节,破坏 RAM 的内容。
我正在尝试使用 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
对于客户端和服务器之间的每笔交易,都有一个确认。
例如:
如果客户端向服务器发送命令,那么服务器会确认它确实收到了。接下来,服务器将 运行 命令并将数据发送回客户端。最后,客户端会发送一个它收到数据的确认信息。
此外,这里有一些链接可能会对您有所帮助:
- https://circuitdigest.com/microcontroller-projects/uart-communication-between-two-atmega8-microcontrollers
- https://www.instructables.com/id/Atmega32-Atmega8-Master-Slave-SPI-Communication/
- http://exploreembedded.com/wiki/Serial_UART_Interface_with_AVR
- https://www.researchgate.net/publication/281509696_Serial_Communications_with_AVR_Microcontroller
您的代码的主要问题是您将简单数组作为零终止字符串传输:
char freq_array[] = {0x01, '5', '0', '.', '0', '0', '\r'};
没有零字节来终止字符串。 但是发送函数:
void send_string_SPI(char s[])
...
while (s[i] != 0x00)
...
期待出现零字节。 因此,当发送第一个数组时,它将继续传输数组边界之外的许多字节,直到偶尔在内存中的某处遇到零字节。
同样在从代码中,我没有看到 data_array
和 spi_data_recieved
是如何声明的。他们是volatile
吗?
另外,查看 ISR 中的 Clear_Buffer(data_array, 10);
,我想 data_array
声明的长度为 10:data_array[10]
。
因此,ISR 中的 data_array[data_index] = SPDR;
可以写出数组边界,在它后面两个字节,破坏 RAM 的内容。