Embedded Development--Problems of RS-485 Communication

RS-485 description

RS-485 is generally referred to as 485 bus, which is one of the most commonly used industrial buses. It generally adopts 2-wire half-duplex mode and uses differential mode to send and receive information. The highest speed can reach 10M BPS.

interface chip

When the microcontroller uses the 485 bus, it uses the UART or USART interface to complete the signal input and output through the RS-485 transceiver. Commonly used chips are MAX485, MAX3485, SP3485 and so on.

hardware connection

insert image description here
The hardware is very simple. RO and DI are connected to the UART interface of the single-chip microcomputer, which are data sending and receiving pins. RE and DE are connected and connected to the GPIO of the single-chip microcomputer to control the direction of data flow, whether it is input or output.

CubeMX settings

See the CubeMX tutorial here: Embedded Development – ​​CubeMX Getting Started Tutorial
insert image description here
Connect to UART2, the specific settings are as follows:
insert image description here
These parameters need to be set according to your setting requirements, asynchronous mode, baud rate, number of bits, parity, stop bits, other defaults That's it. Enable interrupts to facilitate receiving data.
insert image description here
UART does not require a high baud rate, and the error is within 5%. Therefore, the on-chip RC oscillator can be used for the crystal oscillator. Of course, it is recommended to use an external quartz crystal oscillator. The frequency is more accurate, and more importantly, the reliability is high.

code writing

pin definition

#define MAX485_OUT()      HAL_GPIO_WritePin(CTL_485_PORT,CTL_485_PIN, GPIO_PIN_SET)
#define MAX485_IN()       HAL_GPIO_WritePin(CTL_485_PORT,CTL_485_PIN, GPIO_PIN_RESET)

enable serial port

  HAL_UART_Receive_IT(&huart2, uart2_state_typedef.data, 1);	//开启串口,接收到的数据放到uart2_state_typedef.data,每次接收1个字节
  __HAL_UART_ENABLE_IT(&huart2, UART_IT_RXNE);	//启动RXNE中断
  __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);	//启动IDLE中断

RXNE interrupt is used to receive data, receive 1 byte each time, and open this interrupt again in the interrupt
IDLE is used to judge the end of the frame, 485 8 bytes per frame, an IDLE interrupt will be generated after the bus is idle , Entering this interrupt means that a frame is over.

interrupt function

void USART2_IRQHandler(void)
{
    
    
  /* USER CODE BEGIN USART2_IRQn 0 */

  /* USER CODE END USART2_IRQn 0 */
  HAL_UART_IRQHandler(&huart2);
  /* USER CODE BEGIN USART2_IRQn 1 */
  //RS485接口
  //收到1个字节的数据
  if(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_RXNE))
  {
    
    
    __HAL_UART_ENABLE_IT(&huart2, UART_IT_RXNE);	//启动RXNE中断
    uart2_state_typedef.data[uart2_state_typedef.len] = huart2.Instance->RDR;
    uart2_state_typedef.len++;
  }
  
  //总线空闲时,会发生一次IDLE中断,此时意味着数据接收完成
  //不同的内核,清除IDLEIE的方式不同,请查阅手册
  if(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE))
  {
    
    
    huart2.Instance->ICR |= USART_ICR_IDLECF;	 //向USART_CR1的IDLECF位写1,以清除IDLEIF标志,否则会一直进IDLEIE中断
  }

send data

void modbus_send(void)  //发送数据到串口,数据需要事先在modbus_send_array中准备好
 {
    
    
  MAX485_OUT();		//转换为输出模式
  delay_us(100); 	//延时,以等待接口芯片切换完成
  HAL_UART_Transmit(MODBUS_PORT, modbus_send_array,8, 100);   //从串口2发送数组命令
  MAX485_IN();  	//转换为输入模式
  delay_us(10);
}

Receive data

u8 modbus_receive(u16 timeout)        //发送指令后,读取伺服回传的数据,超时单位为ms
{
    
    
  u8 ret = 255;
  u8 i = 0;

  while(1)
  {
    
     
    HAL_Delay(1);
    i++;
    
    if(i>timeout)
      break;
    if(uart2_state_typedef.state == UART_RECEIVE_OK)
    {
    
    
      uart2_state_typedef.state = 0;
      ret = 0;
      huart2.Instance->ICR |= (USART_ICR_EOBCF|USART_ICR_TCCF|USART_ICR_FECF|USART_ICR_PECF);//eobf   txe  tc  fe  pe
      return ret;
      break;
    }
  }
    return ret;
}

Then you can send and receive.

There is a problem, an extra number was charged

So in the serial port interrupt, data reception, and IDLE, respectively trigger a level signal for observation. As shown below:

Waveform on the data line

The waveform of the first row is the sending pin
The waveform of the second row is the receiving pin
The waveform of the third row is the direction control pin
The fourth row is the receiving interrupt, and there is a pulse every time
the fifth row is the IDLE interrupt, the next step There is a pulse.
insert image description here
The figure below is enlarged, the waveform of a frame of data,
insert image description here
pay attention to the pulse in the red circle, the data has not been received yet, but it has already entered the interrupt and started receiving data once, which is an extra received character.
insert image description here

problem analysis

This pulse is where the pulse occurs. It is 5ms after the data transmission is completed and the direction of 485 is switched to reception. Obviously at that time, another RXNE interrupt is entered. The reason for the interrupt is also very simple. Because of the following figure Low level transition.
insert image description here
That is to say, when the data direction of the 485 changes from receiving to sending, the receiving port will detect a low level, which is considered as the start bit of the serial port receiving data, but there is no subsequent high level end bit, so The data received is definitely wrong, and the FE bit of the ISR register of UART2 also points out this point, as shown in the figure below
insert image description here

problem solved

Knowing the problem, you also know how to solve it. Modify the sending function slightly. After the sending is completed, clear the RXNE flag of the ISR register to solve the problem.

void modbus_send(void)  //发送读寄存器的指令到串口
{
    
    
  u32 i=0;
  
  
  HAL_UART_AbortReceive_IT(&huart2);
  __HAL_UART_DISABLE_IT(&huart2, UART_IT_RXNE);	//禁用RXNE中断

  MAX485_OUT();
  delay_us(100);
  HAL_UART_Transmit(MODBUS_PORT, modbus_send_array,8, 100);   //从串口2发送数组命令
  huart2.Instance->RQR |= USART_RQR_RXFRQ;	//清除485方向切换导致的RXNE标识
  huart2.Instance->ICR |= USART_ICR_IDLECF;	//清除空闲标识
  huart2.Instance->ICR |= USART_ICR_TCCF;	//清除发送完成标识

  __HAL_UART_ENABLE_IT(&huart2, UART_IT_RXNE);	//启动RXNE中断
  
  MAX485_IN();
  uart2_state_typedef.len = 0;
  uart2_state_typedef.state = UART_READY;
  delay_us(10);
}

insert image description here

insert image description here
Entered 8 receive interrupts, received 8 characters, and entered an IDLE interrupt, indicating that the frame is over and the work is normal.

Guess you like

Origin blog.csdn.net/13011803189/article/details/128206077