RS485: обзор, примеры на Verilog и С++

Содержание

Сводка

RS485 — это популярный интерфейс для передачи данных, который широко применяется в различных областях электроники и автоматизации. В этой статье мы рассмотрим принцип работы RS485, его отличия от RS232 и RS422, типы разъемов, а также выявим недостатки и преимущества этого интерфейса.

Принцип работы RS485

RS485 представляет собой стандартный интерфейс для передачи данных между устройствами по дифференциальной шине. Дифференциальная линия передачи данных является эффективным средством для обеспечения надежной передачи информации на большие расстояния в шумных средах. Особенностью этой технологии является возможность работы в полудуплексном режиме и поддержка множества устройств на одной линии, что делает ее идеальным выбором для построения распределенных сетей. Благодаря высокой надежности данных и устойчивости к воздействию внешних помех RS485 широко используется в различных областях автоматизации, контроля и связи.

Схема подключения линии RS485
Рисунок 1. Дифференциальная пара RS485 с боковыми резисторами(терминаторами)

Полудуплексный режим – это специальный режим передачи данных, при котором устройства могут обмениваться информацией в разные моменты времени, но не одновременно. Этот режим позволяет оптимизировать использование ресурсов, улучшая эффективность передачи данных в сети. Важно отметить, что полудуплексный режим часто используется в сферах, где требуется эффективное управление потоком информации, например, в радиосвязи, телекоммуникациях и автоматизации. Изучив особенности полудуплексного режима, можно значительно улучшить процессы передачи данных и сделать сеть более надежной и эффективной.

Структура передачи
Рисунок 2. Структура передачи при RS485 схожа с UART

Отличия от RS232 и RS422

RS485 отличается от RS232 и RS422 не только тем, что может работать в полудуплексном режиме, но и тем, что поддерживает более длинные расстояния передачи данных (до 1200 метров) и до 32 устройств на одной линии. RS232, напротив, предназначен для более коротких расстояний (обычно до 15 метров), а RS422 использует сбалансированные сигналы для более надежной передачи данных на расстояния до 1200 метров.

Типы разъемов RS485

Существует несколько типов разъемов для подключения устройств через UART, включая DB9, DB25 и другие стандартные разъемы. Выбор типа разъема зависит от того, какой протокол передачи данных будет использоваться и требований конкретной системы.

Пример: DB-9FA — Разъем(розетка() с монтажом с врезание, 9 контактами, соединительной частью типом A.

Таблица 1. Важные характеристики разъемов RS485, RS422, RS232.
ПозицияНаименованиеЕд.Значение
1МонтажаВрезание(не добавляется буква),
пайка(прямой или угловой) добавление буквы R
2Контактышт9, 15, 25, 37 или 50
3ТипПапа(штыревой): добавление буквы M
Мама(розетка): добавление буквы F
4Screw type(Тип винта)A, B, C, D, E, F
Отличие заключается в длине соединительной части
Таблица 2. Другие важные характеристики разъемов RS485, RS422, RS232.
НомерНаименованиеЕд.Значение
1Рядышт2 или 3
2Шаг контактов в рядумм2.77
3Шаг между рядамимм2.84 или 2.50
4Предельное напряжение не болееВУ всех по разному
4Предельный токАУ всех по разному
Типы разъемов RS485, RS422, RS232
Рисунок 3. Типы разъемов RS485, RS422, RS232

Недостатки и преимущества

Среди преимуществ RS485 можно выделить возможность передачи данных на большие расстояния, надежность работы в шумных средах, поддержку множества устройств на одной линии и низкую стоимость компонентов. Однако среди недостатков стоит отметить более сложную настройку сети из-за требования правильной терминированности линий, а также отсутствие гальванической изоляции, что может повлечь за собой проблемы с защитой от помех.

Примеры

Пример контроллера RS485 на языке Verilog под ПЛИС

Контроллер RS485 написан на языке Verilog(документация) для общения с микросхемой ISO1176DW. В коде состоит из модуля UART и блока проверки встречной работы. Для большей функциональности используется блок generate, который может учитывать или исключать части кода через следующие параметры:

  • BP_ENABLE — значение 1 включает проверку встречной работы, а при 0 нет.
  • D_ENABLE — значение 1 оставляет функцию передачи, а 0 — нет.
  • R_ENABLE — при значение 1 прослушивает линию R, а при 0 наоборот.
  • PARITY_ENABLE — при единице вставляет 9 битом бит четности, а при 0 — обычная посылка.

Код контроллера использует принцип работы UART с комментариями к коду. Имеется только одно отличие: невозможность одновременной передачи и приема. Однако, достоинством этой схемы с кодом интерфейса является помехоустойчивость и передача сообщений на большие расстояния. Данный код использует все преимущества и может использоваться конвейерными блоками для передачи.

module ISO1176DW_CON #( // параметр включения встречной работы parameter BP_ENABLE = 0, // параметр включения линии посылки parameter D_ENABLE = 1, // параметр включения линии приема parameter R_ENABLE = 1, // вставляет бит четности 9 битом parameter PARITY_ENABLE = 0 ) ( input wire [7:0] data_i, // данные на отправку input wire vd_i, // валидация данных на отправку input wire clk, // тактовый сигнал системы input wire clk_rs, // тактовый сигнал RS485 input wire data_rsr, // линия R ISO1176 output wire data_rsd, // линия D ISO1176 output wire rsre, // линия RE ISO1176 output wire rsde, // линия DE ISO1176 output wire [7:0] data_o, // данных на получение output wire vd_o, // валидация данных на получение output wire ready, // готовность контроллера output reg bp // встречная работа ); wire vd_o_wire; // линия валидации с UART wire ready_wire; // линия готовности с UART wire data_rsr_wire; // промежуточная линия R wire _first_bit_rx; // начала кадра получения с UART wire [3:0] count_r_kadr; initial begin bp <= 0; end assign data_rsr_wire = data_rsr & ~rsde; UART_VERILOG #(D_ENABLE, R_ENABLE, PARITY_ENABLE) uart(clk, clk_rs, data_rsr_wire, data_o, vd_o_wire, data_rsd, data_i, vd_i, ready_wire, _first_bit_rx, count_r_kadr); generate if (D_ENABLE == 1) begin assign ready = ready_wire & ~_first_bit_rx; assign rsde = ~ready_wire; end if (R_ENABLE == 1) begin assign rsre = 0; assign vd_o = vd_o_wire; end else begin assign rsre = ~rsde; end if (BP_ENABLE) begin always @(negedge clk_rs) begin if (rsde) begin bp <= bp | (data_rsr != data_rsd); end else begin bp <= 0; end end end endgenerate endmodule module UART_VERILOG #( parameter TX_ENABLE = 1, parameter RX_ENABLE = 1, parameter PARITY_ENABLE = 1 ) ( input wire clk, input wire clk_br, input wire rx_serial, output wire [7:0] rx_data, output wire rx_vd, output wire tx_serial, input wire [7:0] tx_data, input wire tx_vd, output wire ready_tx, output reg _first_bit_rx, output reg [3:0] counter_bits_rx ); reg [3:0] counter_bits_tx = 0; reg [11:0] tx_data_reg = 0; reg [7:0] tx_termclk = 0; reg ready_trns = 1; reg [8:0] rx_data_reg; reg parity = 0; reg flag_start_tx = 0; reg first_clk_vd = 0; reg limit_vd = 0; reg edge_clk_br = 0; wire wire_vd_rx; initial begin _first_bit_rx <= 0; counter_bits_rx <= 0; end assign rx_data = rx_data_reg[7:0], rx_vd = limit_vd; generate if (PARITY_ENABLE == 1) begin assign wire_vd_rx = ~parity & (counter_bits_rx == 9) & rx_serial & ~clk_br; end else begin assign wire_vd_rx = counter_bits_rx[3] & rx_serial; end endgenerate assign ready_tx = ready_trns, tx_serial = tx_data_reg[0] | ready_trns; always @(posedge clk) begin if (tx_vd & ready_trns) begin tx_termclk <= tx_data; flag_start_tx <= 1; if (clk_br) edge_clk_br <= 1; else edge_clk_br <= 0; end else begin if (clk_br & ~edge_clk_br) begin flag_start_tx <= 0; end if (~clk_br & edge_clk_br) begin edge_clk_br <= 0; end end if (wire_vd_rx) begin if (~first_clk_vd) begin first_clk_vd <= 1; limit_vd <= 1; end else begin limit_vd <= 0; end end else begin first_clk_vd <= 0; end end integer i; generate if (RX_ENABLE) begin always @(posedge clk_br) begin if (PARITY_ENABLE == 1) begin rx_data_reg[8] <= rx_serial; for (i = 1; i < 9; i = i + 1) begin rx_data_reg[i - 1] <= rx_data_reg[i]; end end else begin rx_data_reg[7] <= rx_serial; for (i = 1; i < 8; i = i + 1) begin rx_data_reg[i - 1] <= rx_data_reg[i]; end end if (~rx_serial & ~_first_bit_rx) begin _first_bit_rx <= 1; end else if (_first_bit_rx) begin if (PARITY_ENABLE == 0) begin if (~counter_bits_rx[3]) begin counter_bits_rx <= counter_bits_rx + 1; end else begin counter_bits_rx <= 0; _first_bit_rx <= 0; end end else begin if (counter_bits_rx < 10) begin counter_bits_rx <= counter_bits_rx + 1; parity <= parity ^ rx_serial; end else begin counter_bits_rx <= 0; parity <= 0; _first_bit_rx <= 0; end end end end end endgenerate integer j; generate if (TX_ENABLE) begin always @(posedge clk_br) begin if (flag_start_tx) begin if (PARITY_ENABLE == 1) begin tx_data_reg[0] <= 0; tx_data_reg[8:1] <= tx_termclk; tx_data_reg[9] <= ^ tx_termclk; tx_data_reg[10] <= 1; tx_data_reg[11] <= 1; end else begin tx_data_reg[0] <= 0; tx_data_reg[8:1] <= tx_termclk; tx_data_reg[9] <= 1; tx_data_reg[10] <= 1; end ready_trns <= 0; end if (~ready_trns & ~tx_vd) begin if (PARITY_ENABLE == 0) begin for (j = 0; j < 10; j = j + 1) begin tx_data_reg[j] <= tx_data_reg[j + 1]; end if (counter_bits_tx == 9) begin ready_trns <= 1; end end else begin for (j = 0; j < 11; j = j + 1) begin tx_data_reg[j] <= tx_data_reg[j + 1]; end if (counter_bits_tx == 10) begin ready_trns <= 1; end end counter_bits_tx = counter_bits_tx + 1; end else begin counter_bits_tx = 0; end end end endgenerate endmodule
Временная диаграмма контроллера RS485 c микросхемой ISO1176 и отчеты о компиляции
Рисунок 4. Временная диаграмма контроллера RS485 c микросхемой ISO1176 и отчеты о компиляции

Пример контроллера RS485 на языке C++ под stm32

Код написанный на языке C++ для общения с микросхемой ISO1176DW, которая является изолированным RS485 трансивером, нужно подключить ее к STM32 через UART. Не забудьте настроить правильную скорость обмена данными (baud rate) и другие параметры в соответствии с требованиями микросхемы ISO1176DW.
#include "stm32f4xx.h" #define RS485_DE_GPIO_PORT GPIOA #define RS485_DE_PIN GPIO_Pin_1 void RS485_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; USART_InitTypeDef USART_InitStruct; RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3; // USART2 TX and RX pins GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOA, &GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin = RS485_DE_PIN; // DE pin GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; GPIO_Init(RS485_DE_GPIO_PORT, &GPIO_InitStruct); GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2); GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2); USART_InitStruct.USART_BaudRate = 9600; USART_InitStruct.USART_WordLength = USART_WordLength_8b; USART_InitStruct.USART_StopBits = USART_StopBits_1; USART_InitStruct.USART_Parity = USART_Parity_No; USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_Init(USART2, &USART_InitStruct); USART_Cmd(USART2, ENABLE); } void RS485_SendData(uint8_t* data, uint16_t len) { // Установка пина DE в лог. 0 для передачи GPIO_ResetBits(RS485_DE_GPIO_PORT, RS485_DE_PIN); for (uint16_t i = 0; i < len; i++) { USART_SendData(USART2, data[i]); // ожидание завершения передачи while (USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET); } // установка пина DE лог 1 для починенного GPIO_SetBits(RS485_DE_GPIO_PORT, RS485_DE_PIN); } uint8_t RS485_ReceiveData(void) { // ожидание данных с подчиненного while (USART_GetFlagStatus(USART2, USART_FLAG_RXNE) == RESET); return USART_ReceiveData(USART2); }

Пример контроллера RS485 на языке C++ под esp32

Для общения с микросхемой ISO1176 для RS485 на микроконтроллере ESP32 без использования Arduino, вам необходимо настроить прямое взаимодействие с портами ввода-вывода микроконтроллера. Этот код на языке C++ инициализирует RS485 с использованием микросхемы ISO1176 на микроконтроллере ESP32 и демонстрирует отправку и прием данных через UART.
#include <stdio.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "driver/uart.h" #include "driver/gpio.h" #define TXD_PIN 17 #define RXD_PIN 16 #define DE_PIN 18 #define UART_NUM UART_NUM_2 #define BUF_SIZE 1024 void rs485_init() { uart_config_t uart_config = { .baud_rate = 9600, .data_bits = UART_DATA_8_BITS, .parity = UART_PARITY_DISABLE, .stop_bits = UART_STOP_BITS_1, .flow_ctrl = UART_HW_FLOWCTRL_DISABLE }; uart_param_config(UART_NUM, &uart_config); uart_set_pin(UART_NUM, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); uart_driver_install(UART_NUM, BUF_SIZE, 0, 0, NULL, 0); gpio_pad_select_gpio(DE_PIN); gpio_set_direction(DE_PIN, GPIO_MODE_OUTPUT); } void rs485_send_data(const char* data, int len) { // Установка направления на передачу gpio_set_level(DE_PIN, 1); uart_write_bytes(UART_NUM, data, len); // Ожидание завершения передачи uart_wait_tx_done(UART_NUM, portMAX_DELAY); // Возврат направления на прием gpio_set_level(DE_PIN, 0); } void rs485_receive_data() { uint8_t* data = (uint8_t*)malloc(BUF_SIZE); while (1) { int len = uart_read_bytes(UART_NUM, data, BUF_SIZE, 20 / portTICK_RATE_MS); if (len > 0) { // Обработка принятых данных printf("Received data: %.*s\n", len, data); } } free(data); } void app_main() { rs485_init(); xTaskCreate(rs485_receive_data, "rs485_receive_data", 2048, NULL, 10, NULL); while(1) { const char* data = "Hello, RS485!"; rs485_send_data(data, strlen(data)); vTaskDelay(2000 / portTICK_PERIOD_MS); } }

Заключение

RS485 — мощный и универсальный интерфейс для передачи данных, который нашел широкое применение в различных областях промышленности и автоматизации. Знание особенностей этого интерфейса позволяет инженерам и разработчикам создавать надежные и эффективные системы связи между устройствами. Внедрение RS485 в проекты обеспечивает высокую производительность и надежность передачи данных.