最近写了一个小小的串口通信程序,逻辑上感觉没什么问题,但就是数据与设定值不对。最后发现应该是总线冲突的问题。
在RS485通信中,如果在接收完数据后立即发送数据,可能会引发总线冲突问题。这是因为在RS485通信中,设备是共享同一个通信总线的,当多个设备同时尝试在总线上发送数据时,可能会导致数据冲突和干扰,从而造成通信失败。
情景解释:
假设有两个设备 A 和 B,它们通过共享的RS485总线进行通信。设备 A 正在接收来自设备 B 的数据,当接收完成后,设备 A 需要发送一组响应数据给设备 B。然而,如果设备 A 在接收完数据后立即发送响应数据,而此时设备 B 正好也在尝试发送数据,就会发生总线冲突。
问题示例
下面代码的初衷是,如果我接收到 1F
,就发送0x1F, 0x08, 0x01, 0x06, 0x41, 0x43, 0x54, 0x34, 0x33, 0x30, 0xAE, 0x04
。
int main(void)
{
uint8_t send_data[] = {0x1F, 0x08, 0x01, 0x06, 0x41, 0x43, 0x54, 0x34, 0x33, 0x30, 0xAE, 0x04};
uint8_t read_data = 0x00;
/* Placeholder for user application code. The while loop below can be replaced with user application code. */
while(1U)
{
UART_Receive(&UART_0, &read_data, 1); //此函数定义由DAVE直接生成
//delay_100us();
if(read_data==0x1F)
{
UART_Transmit(&UART_0, send_data, sizeof(send_data));//此函数定义由DAVE直接生成
read_data = 0x00;
}
}
}
然而实际收到的数据是这样的,发送的数据完全不是设定值
解决办法:
为了解决总线冲突问题,通常会在发送数据之前引入一定的延时,以确保其他设备有足够的时间完成它们的操作。这样可以有效避免数据冲突,提高通信的可靠性。以下是一些可能的解决办法:
- 随机延时: 在发送数据之前,引入一个随机的短暂延时。这样可以减小多个设备在同一时间尝试发送数据的可能性,从而降低冲突的概率。
代码示例
只要在接收数据后加延时delay_5ms;
就可以解决这个问题。
void delay_100us(void)
{
for (volatile uint32_t i = 0; i < 1000; ++i)
{
// Empty loop to create delay
__NOP();
// `__NOP()` function is being called inside the loop which stands for "no operation"
// and is typically used to insert a delay or to pad out a loop.
// This means that the processor will execute a no-operation instruction `1000` times,
// which will introduce a delay in the loop.
// If don't write `__NOP()`, there is nothing inside the loop,
// so it will iterate `1000` times without executing any instructions.
// This will be a faster loop compared to the one with `__NOP()`,
// because there are no instructions to execute inside the loop body.
// Therefore, the loop with the `__NOP()` function will introduce a delay,
// while the loop without the `__NOP()` will not introduce any delay
// and will simply iterate `1000` times.
}
}
void delay_5ms(void)
{
for (volatile uint32_t i = 0; i < 50000; ++i)
{
// Empty loop to create delay
__NOP();
}
}
int main(void)
{
uint8_t send_data[] = {0x1F, 0x08, 0x01, 0x06, 0x41, 0x43, 0x54, 0x34, 0x33, 0x30, 0xAE, 0x04};
uint8_t read_data = 0x00;
/* Placeholder for user application code. The while loop below can be replaced with user application code. */
while(1U)
{
UART_Receive(&UART_0, &read_data, 1); //此函数定义由DAVE直接生成
delay_100us();
if(read_data==0x1F)
{
delay_5ms();
UART_Transmit(&UART_0, send_data, sizeof(send_data));//此函数定义由DAVE直接生成
read_data = 0x00;
}
}
}
如图
-
轮询方式: 设备之间可以通过轮询的方式进行通信,即每个设备在特定的时间间隔内依次发送数据。这样可以确保每个设备都有机会在总线上发送数据,减少冲突。
-
主从架构: 在系统中设定一个主设备,其他设备作为从设备,只有在主设备允许的情况下才能发送数据。主设备可以控制从设备发送数据的时机,从而避免冲突。
-
通信协议: 设计合理的通信协议,定义数据传输的时序和规则,确保每个设备都遵循协议要求,避免冲突发生。
总之,为了避免RS485通信中的总线冲突,需要在设计通信系统时充分考虑设备之间的通信时序,并采取适当的措施,如引入延时、采用轮询方式或设计合理的通信协议。这样可以确保通信的稳定性和可靠性。