【RS485 - 总线冲突】

最近写了一个小小的串口通信程序,逻辑上感觉没什么问题,但就是数据与设定值不对。最后发现应该是总线冲突的问题。

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

然而实际收到的数据是这样的,发送的数据完全不是设定值
在这里插入图片描述

解决办法:

为了解决总线冲突问题,通常会在发送数据之前引入一定的延时,以确保其他设备有足够的时间完成它们的操作。这样可以有效避免数据冲突,提高通信的可靠性。以下是一些可能的解决办法:

  1. 随机延时: 在发送数据之前,引入一个随机的短暂延时。这样可以减小多个设备在同一时间尝试发送数据的可能性,从而降低冲突的概率。

代码示例

只要在接收数据后加延时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;
      }
 }
}

如图
在这里插入图片描述

  1. 轮询方式: 设备之间可以通过轮询的方式进行通信,即每个设备在特定的时间间隔内依次发送数据。这样可以确保每个设备都有机会在总线上发送数据,减少冲突。

  2. 主从架构: 在系统中设定一个主设备,其他设备作为从设备,只有在主设备允许的情况下才能发送数据。主设备可以控制从设备发送数据的时机,从而避免冲突。

  3. 通信协议: 设计合理的通信协议,定义数据传输的时序和规则,确保每个设备都遵循协议要求,避免冲突发生。

总之,为了避免RS485通信中的总线冲突,需要在设计通信系统时充分考虑设备之间的通信时序,并采取适当的措施,如引入延时、采用轮询方式或设计合理的通信协议。这样可以确保通信的稳定性和可靠性。

RS485是一种差分传输的串行总线协议,常用于工业控制、自动化控制等领域。在STM32上实现RS485通信,需要使用USART串口模块,并利用GPIO控制芯片的发送和接收模式。下面是一个简单的RS485通信程序设计。 1. 配置USART串口模块 首先,需要对USART进行配置,包括波特率、数据位、停止位、奇偶校验等参数。以USART1为例,代码如下: ```c USART_InitTypeDef USART_InitStructure; // 使能USART1时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); // 配置USART参数 USART_InitStructure.USART_BaudRate = 9600; // 波特率 USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 数据位 USART_InitStructure.USART_StopBits = USART_StopBits_1; // 停止位 USART_InitStructure.USART_Parity = USART_Parity_No; // 奇偶校验 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 硬件流控制 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 发送接收模式 USART_Init(USART1, &USART_InitStructure); // 配置USART1 ``` 2. 配置GPIO控制芯片的发送和接收模式 在RS485总线上,需要控制芯片的发送和接收模式,以避免多个设备同时发送数据导致冲突。一般采用一个控制引脚来控制芯片的发送和接收模式。以GPIOA Pin8为例,代码如下: ```c GPIO_InitTypeDef GPIO_InitStructure; // 使能GPIOA时钟 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); // 配置GPIOA Pin8为推挽输出 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOA, &GPIO_InitStructure); ``` 3. 发送数据 在发送数据时,需要将芯片的发送模式置为高电平,并使用USART发送数据。代码如下: ```c // 置芯片发送模式为高电平 GPIO_SetBits(GPIOA, GPIO_Pin_8); // 发送数据 USART_SendData(USART1, data); while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); // 等待发送完成 ``` 4. 接收数据 在接收数据时,需要将芯片的发送模式置为低电平,并使用USART接收数据。代码如下: ```c // 置芯片发送模式为低电平 GPIO_ResetBits(GPIOA, GPIO_Pin_8); // 接收数据 while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET); // 等待接收完成 data = USART_ReceiveData(USART1); ``` 以上是一个简单的RS485通信程序设计,可以根据实际需求进行修改和优化。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

六月悉茗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值