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

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

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

Недостатки и преимущества
Среди преимуществ 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++ под stm32
#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
#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 в проекты обеспечивает высокую производительность и надежность передачи данных.