Jump to content
View in the app

A better way to browse. Learn more.

T.M.I IThub

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.

Почему RS-485, а не что-то современное

Каждый год появляются новые промышленные протоколы: EtherCAT, PROFINET, IO-Link, TSN. Но RS-485 не умирает. По данным IHS Markit, ежегодно продаётся более 1 миллиарда чипов RS-485. Новые установки продолжают использовать этот интерфейс.

Причины живучести просты:

  • Дешевизна: кабель — витая пара $0.1/м, трансивер MAX485 — $0.3

  • Надёжность: дифференциальный сигнал устойчив к помехам, работает на расстояниях до 1200 м

  • Простота: понять и реализовать RS-485 можно за один день

  • Совместимость: поддерживается абсолютно всеми промышленными устройствами

Modbus RTU, BACnet MS/TP, DMX512, DALI — всё это работает поверх RS-485. Если вы занимаетесь промышленной автоматизацией, знание RS-485 обязательно.


Сравнение: RS-232 vs RS-485

Параметр

RS-232

RS-485

Тип сигнала

Однополярный, ±3–15В

Дифференциальный, ±200мВ–5В

Количество устройств

1:1 (точка-точка)

1:32 без репитеров (до 247 с)

Максимальное расстояние

15 м

1200 м

Скорость

До 115 200 бод (практически)

До 10 Мбит/с (при короткой линии)

Устойчивость к помехам

Низкая

Высокая

Дуплекс

Полный (отдельные TX/RX)

Полу (одна пара) или полный (2 пары)

Применение

Отладка, локальные устройства

Промышленные сети, длинные линии


Физический уровень: как работает дифференциальный сигнал

RS-485 использует дифференциальную пару проводников A и B:

Состояние MARK (логическая 1, рецессивное):
  A > B: разность (A-B) = +200мВ...+5В

Состояние SPACE (логическая 0, доминантное):
  B > A: разность (B-A) = +200мВ...+5В, то есть (A-B) = -200мВ...-5В

Типичное напряжение при передаче:
  A ≈ +3.5В, B ≈ -3.5В → разность = +7В (гарантированная "1")
  A ≈ -3.5В, B ≈ +3.5В → разность = -7В (гарантированная "0")

Устойчивость к синфазным помехам:
  Помеха +5В добавляется на ОБА провода:
  A = +3.5 + 5 = +8.5В, B = -3.5 + 5 = +1.5В
  Разность = +8.5 - 1.5 = +7В — сигнал не изменился!

Пороги приёмника: если разность (A-B) > +200мВ — принимает "1"; если (A-B) < -200мВ — принимает "0". Диапазон ±200мВ — мёртвая зона (неопределённость).


Выбор трансивера RS-485

Бюджетные (для начала):

MAX485 / MAX485E (Maxim)

  • Самый популярный, $0.3–0.5

  • Полудуплекс, 2.5 Мбит/с

  • Нет защиты от ESD (добавьте TVS-диоды!)

  • Нет защиты от перегрева

  • Питание 5В

SP3485 (Sipex/Exar)

  • Клон MAX485, питание 3.3В

  • Совместимость с STM32, ESP32 напрямую (5В-tolerant входы)

Профессиональные (для промышленности):

MAX3485 / MAX3488

  • Расширенный диапазон ESD: ±15 кВ (HBM)

  • Работает от 3.3В

SN65HVD1780 (Texas Instruments)

  • Встроенная защита от отказа шины (failsafe)

  • ESD: ±16 кВ IEC 61000-4-2

  • Для жёстких промышленных условий

ADM2587E (Analog Devices)

  • Встроенная гальваническая изоляция 2500 VRMS

  • Изолированный DC-DC для питания изолированной стороны

  • Для применений с разным заземлением узлов


Схема подключения: правильно и неправильно

Минимальная схема (Arduino + MAX485):

Arduino         MAX485          RS-485 шина
  TX ─────────── DI
  RX ─────────── RO
  D2 ────┬────── DE            A ──── Шина A
         └────── RE_           B ──── Шина B
  GND ─────────── GND          GND ── Общий провод (обязательно!)
  5V ──────────── Vcc

Резисторы на шине:
  A ──[120 Ом]── B   ← Терминатор на каждом конце шины
  
  Pullup/Pulldown для определённого состояния в паузах:
  +5В ──[560 Ом]── A   ← Pull-up
  B ──[560 Ом]── GND   ← Pull-down

Ключевые правила:

ПРАВИЛЬНО: [Устройство A]──────[Устройство B]──────[Устройство C] [Term 120Ом] [Term 120Ом] Строго линейная шина, терминаторы только на концах

НЕПРАВИЛЬНО — "звезда": [Устройство A] │ [Уст.B]──[Hub]──[Уст.C] │ [Устройство D] Отражения на каждом разветвлении разрушат сигнал!

НЕПРАВИЛЬНО — терминаторы не там: [Term]──[Уст.A]──[Уст.B]──[Term]──[Уст.C] Терминатор посередине создаёт проблемы!

Допустимые ответвления (stub):

Короткие отводы от основной шины допустимы при условии:

  • Длина stub < λ/10, где λ — длина волны на рабочей скорости

  • При 9600 бод: λ ≈ 12 км → stub до 1200 м (практически неограничен)

  • При 115200 бод: stub не более 1 м

  • При 1 Мбит/с: stub не более 15 см!


Управление направлением передачи

RS-485 в полудуплексном режиме требует переключения между передачей и приёмом через сигнал DE/RE:

// Arduino: управление направлением через GPIO

#define RS485_DE_RE_PIN 2
#define RS485_BAUDRATE  9600

void rs485_init() {
    Serial.begin(RS485_BAUDRATE);
    pinMode(RS485_DE_RE_PIN, OUTPUT);
    rs485_receive_mode();  // По умолчанию — приём
}

void rs485_receive_mode() {
    digitalWrite(RS485_DE_RE_PIN, LOW);  // DE=0, RE=0 → приём
}

void rs485_transmit_mode() {
    digitalWrite(RS485_DE_RE_PIN, HIGH);  // DE=1, RE=1 → передача
}

void rs485_send(uint8_t *data, uint8_t len) {
    rs485_transmit_mode();
    
    Serial.write(data, len);
    Serial.flush();  // ЖДЁМ пока все байты уйдут в UART TX буфер!
    
    // После flush() данные ещё в UART, нужно дождаться физической передачи
    // Расчёт времени: N_байт × 10_бит / baudrate
    uint32_t delay_us = (uint32_t)len * 10 * 1000000UL / RS485_BAUDRATE + 100;
    delayMicroseconds(delay_us);
    
    rs485_receive_mode();
}

Аппаратное управление DE/RE (лучше!):

На STM32 USART имеет аппаратный сигнал DE для RS-485. Переключение происходит автоматически — с точностью до такта, без программных задержек:

// STM32 HAL: аппаратное управление DE через USART
// В CubeMX: USART → Mode = Asynchronous, Hardware Flow Control = RS-485 Driver Enable

// CubeMX настроит:
// huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_DE_INIT;
// huart2.AdvancedInit.DEPolarity = UART_DE_POLARITY_HIGH;
// huart2.AdvancedInit.DEAssertionTime = 16;  // тактов предвключения
// huart2.AdvancedInit.DEDeassertionTime = 16; // тактов послевыключения

// После этого просто передаём — DE управляется автоматически!
HAL_UART_Transmit(&huart2, data, len, 100);
// STM32 сам поднял DE перед передачей и снял после!

Расчёт нагрузки на шину

RS-485 трансивер создаёт нагрузку на шину. Стандарт RS-485 определяет "единицу нагрузки" (Unit Load, UL) = 12 кОм.

Драйвер должен обеспечить минимум 32 UL. Это значит: максимум 32 "классических" устройства на шине.

Современные трансиверы с низким потреблением имеют 1/8 UL или 1/4 UL:

Тип трансивера

Нагрузка

Устройств на шине

Стандартный (MAX485)

1 UL

32

1/2 UL (MAX3430)

0.5 UL

64

1/4 UL (MAX3471)

0.25 UL

128

1/8 UL (MAX3491)

0.125 UL

256

Также нагрузку создают терминирующие резисторы:

  • 2 × 120 Ом = 60 Ом = 200 UL (!) — это доминирующая нагрузка

  • Учитывайте это при расчёте суммарной нагрузки


Кабель: выбор и прокладка

Требования к кабелю RS-485:

Обязательно:

  • Витая пара (не просто два провода!)

  • Волновое сопротивление 120 Ом (терминируется парными резисторами 120 Ом)

Рекомендуемые типы кабелей:

  • КВВГЭ 1×2×0.75 — отечественный, экранированная витая пара

  • Belden 9842 — американский стандарт, 120 Ом, двойной экран

  • LiYCY 2×0.5 мм² — гибкий, для подвижных установок

  • Cat5e / Cat6 — работает! (120 Ом, но без промышленной изоляции)

Сечение проводника:

Падение напряжения на кабеле:
ΔU = 2 × R_кабеля × I_нагрузка

R = ρ × L / S = 0.0175 (Ом·мм²/м) × 1000 м / 0.5 мм² = 35 Ом

При токе утечки 100 мА:
ΔU = 2 × 35 × 0.1 = 7В — это уже критично!

Для длинных линий выбирайте кабель 1.0 мм² и более.

Экранирование:

Правила заземления экрана:

Заземлять в ОДНОЙ точке — предотвращает контурные токи Обычно: на стороне мастера/ПЛК

Заземлять с ОБОИХ концов — контурный ток протекает по экрану! При разных потенциалах земли создаёт синфазные помехи. Исключение: при частотах > 100 кГц экран заземляют с обоих концов (через конденсатор 10 нФ с одной стороны).


Практика: полный пример Modbus RTU на Python

import serial
import struct
import time
from typing import Optional

class RS485Master:
    """
    Мастер RS-485 с ручным управлением DE/RE через GPIO (для Raspberry Pi).
    Или с автоматическим через RTS (для USB-RS485 адаптеров).
    """
    
    def __init__(self, port: str, baudrate: int = 9600,
                 use_rts: bool = True, rts_level: bool = True):
        """
        port: '/dev/ttyUSB0', 'COM3' и т.д.
        use_rts: использовать RTS для управления DE/RE (USB-адаптеры)
        rts_level: уровень RTS при передаче (True = HIGH)
        """
        self.ser = serial.Serial(
            port     = port,
            baudrate = baudrate,
            bytesize = serial.EIGHTBITS,
            parity   = serial.PARITY_NONE,
            stopbits = serial.STOPBITS_ONE,
            timeout  = 0.1
        )
        
        if use_rts:
            self.ser.rts = not rts_level  # Начальное состояние — приём
        
        self.use_rts   = use_rts
        self.rts_level = rts_level
        
        # Время передачи одного символа (для задержки после отправки)
        self.char_time = 10.0 / baudrate  # 10 бит на символ
    
    def _tx_enable(self):
        if self.use_rts:
            self.ser.rts = self.rts_level
            time.sleep(0.0001)  # 100 мкс предвключение
    
    def _rx_enable(self):
        if self.use_rts:
            # Ждём физической передачи последнего байта
            time.sleep(self.char_time)
            self.ser.rts = not self.rts_level
    
    def _crc16(self, data: bytes) -> int:
        crc = 0xFFFF
        for byte in data:
            crc ^= byte
            for _ in range(8):
                crc = (crc >> 1) ^ 0xA001 if crc & 1 else crc >> 1
        return crc
    
    def send_raw(self, data: bytes):
        """Отправка сырых данных"""
        self.ser.reset_input_buffer()  # Очищаем входной буфер перед отправкой
        self._tx_enable()
        self.ser.write(data)
        self.ser.flush()
        self._rx_enable()
    
    def recv_raw(self, expected_len: int, timeout: float = 0.5) -> Optional[bytes]:
        """Приём данных с таймаутом"""
        deadline = time.time() + timeout
        buf = b''
        
        while time.time() < deadline:
            chunk = self.ser.read(expected_len - len(buf))
            buf += chunk
            if len(buf) >= expected_len:
                break
            time.sleep(0.001)
        
        return buf if len(buf) == expected_len else None
    
    def modbus_read_registers(self, slave: int, func: int,
                               start_addr: int, count: int) -> Optional[list]:
        """
        Чтение Holding (FC=3) или Input (FC=4) регистров.
        Возвращает список значений или None при ошибке.
        """
        # Формируем запрос
        request = struct.pack('>BBHH', slave, func, start_addr, count)
        crc = self._crc16(request)
        request += struct.pack('<H', crc)  # CRC little-endian!
        
        self.send_raw(request)
        
        # Ожидаемый размер ответа: addr(1)+fc(1)+byte_count(1)+data(count×2)+crc(2)
        expected = 5 + count * 2
        response = self.recv_raw(expected)
        
        if response is None:
            return None  # Таймаут
        
        # Проверка CRC
        recv_crc = struct.unpack('<H', response[-2:])[0]
        calc_crc = self._crc16(response[:-2])
        if recv_crc != calc_crc:
            return None  # CRC ошибка
        
        # Проверка Exception
        if response[1] & 0x80:
            exc_code = response[2]
            print(f"Modbus Exception: slave={slave}, code={exc_code}")
            return None
        
        # Распаковываем данные
        byte_count = response[2]
        values = list(struct.unpack(f'>{count}H', response[3:3+byte_count]))
        return values
    
    def modbus_write_register(self, slave: int, addr: int, value: int) -> bool:
        """Запись одного Holding регистра (FC=6)"""
        request = struct.pack('>BBHH', slave, 6, addr, value)
        crc = self._crc16(request)
        request += struct.pack('<H', crc)
        
        self.send_raw(request)
        
        # Ответ = эхо запроса (8 байт)
        response = self.recv_raw(8)
        if response is None:
            return False
        
        recv_crc = struct.unpack('<H', response[-2:])[0]
        return recv_crc == self._crc16(response[:-2])
    
    def close(self):
        self.ser.close()


# ===== ПРИМЕР ИСПОЛЬЗОВАНИЯ =====

def demo_poll_vfd():
    """Опрос частотника по Modbus RTU через RS-485"""
    
    master = RS485Master('/dev/ttyUSB0', baudrate=9600, use_rts=True)
    
    try:
        print("Опрос частотника (адрес 1)...")
        
        while True:
            # Читаем 6 Input регистров: статус, частота, ток, напряжение, мощность, fault
            regs = master.modbus_read_registers(slave=1, func=4,
                                                 start_addr=0, count=6)
            
            if regs is None:
                print("Нет ответа от устройства")
            else:
                status   = regs[0]
                freq_hz  = regs[1] / 100.0
                curr_a   = regs[2] / 10.0
                volts    = regs[3]
                power_kw = regs[4] / 10.0
                fault    = regs[5]
                
                running = bool(status & 0x0001)
                faulted = bool(status & 0x0008)
                
                print(f"{'

' if running else ''} " f"f={freq_hz:.1f}Гц " f"I={curr_a:.1f}А " f"U={volts}В " f"P={power_kw:.1f}кВт " f"{'🔴АВАРИЯ' if faulted else ''}") if faulted: print(f"Код аварии: {fault}") time.sleep(1.0) except KeyboardInterrupt: print("Остановлено") finally: master.close() demo_poll_vfd()


Диагностика: осциллограф и мультиметр

Измерения мультиметром:

Линия A-B без сигнала (все устройства молчат):
  Должно быть: A > B на 200мВ+ (если есть pull-up/down)
  Плохо:       A = B (неопределённое состояние — нужны резисторы смещения)

Во время передачи:
  Осциллограф: чёткие уровни ±3..4В, без выбросов
  Плохо: размытые фронты → слишком длинная линия или нет терминаторов
  Плохо: выбросы >±7В → нет снаббера или плохое заземление
  
Измерение дифференциального сигнала:
  Щуп A → канал 1, Щуп B → канал 2
  Включить Math: CH1 - CH2
  Должен быть чёткий прямоугольник ±(3..5)В

Типичные осциллограммы проблем:

Нет терминаторов:

    ┌──────┐
    │      │  ← Нормальный фронт
    │      │╲  ← Отражение (заброс)
────┘      └─╲──────
                └──  ← Повторное отражение

Слишком длинный stub:

    ┌──────┐
    │      ╲___/  ← Паразитное колебание после фронта
────┘            ────

Хорошая линия:

    ┌──────────┐
────┘          └────  ← Чёткие фронты без выбросов

Гальваническая развязка: когда обязательна

В промышленных установках "земля" в разных точках может иметь разный потенциал — десятки и даже сотни вольт. Без развязки:

  • Контурный ток по GND-проводу шины RS-485 разрушает трансиверы

  • Помехи от силового оборудования проникают в логику

  • Один неисправный узел выводит из строя всю сеть

Когда нужна обязательно:

  • Устройства питаются от разных источников

  • Расстояние между устройствами > 50 м

  • Рядом с шиной есть мощные электродвигатели или сварочное оборудование

  • Разные здания или разные распределительные щиты

Варианты реализации:

Вариант 1: Изолированный трансивер (ADM2587E, ISO3082)
  UART ──[Изолированный трансивер]── RS-485 шина
  Встроенная изоляция 2500 VRMS, нет внешних компонентов

Вариант 2: Оптопара + отдельный трансивер
  UART_TX ──[HCPL2630]── MAX485 ── RS-485 шина
  UART_RX ──[HCPL2630]──┘
  Дешевле, но нужен изолированный DC-DC конвертер питания

Вариант 3: Цифровой изолятор + трансивер
  UART ──[ISO7720]── MAX485 ── RS-485 шина
  ISO7720: 5 кВ изоляция, 100 Мбит/с, без светодиодов (долговечнее оптопар)

Число устройств больше 32: репитеры и конвертеры

Если нужно более 32 устройств или протяжённость линии превышает допустимую:

Репитер RS-485:
[Сегмент 1: 32 уст., 600 м]──[Репитер]──[Сегмент 2: 32 уст., 600 м]

Репитер переусиливает и переформирует сигнал.
Каждый сегмент — отдельная нагрузка.

Популярные репитеры:
- ADAM-4510 (Advantech): изолированный, DIN-рейка
- Moxa MB-9000: с диагностикой
- Homemade: MAX487 + MAX487 с DE/RE управлением

Защита от грозовых разрядов и ESD

При длинных линиях между зданиями — молниезащита обязательна:

Схема защиты RS-485 линии:
                                         RS-485
Шина ──[Предохранитель]──[GDT или MOV]──[TVS-диод]──[Трансивер]
 A                                        P6KE6.8CA

Уровни защиты:
1. GDT (газоразрядник) или MOV: ограничивают до 100..500В за наносекунды
2. TVS-диод P6KE6.8CA: ограничивает до ±6.8В, рассеивает 600 Вт импульсно
3. Последовательный резистор 10..22 Ом: ограничивает ток через TVS

Готовые модули молниезащиты RS-485:
- MTL5000: промышленный барьер
- Phoenix Contact TRABTECH: DIN-рейка
- УЗИП-485 (отечественный): Microsemi/аналоги

Частые вопросы и ответы

Q: Можно ли использовать обычный кабель UTP Cat5e вместо специального? A: Можно, и это работает на практике. Cat5e имеет волновое сопротивление 100 Ом (термinator нужен 100 Ом), паразитные ёмкости немного хуже. До 300м/115200 бод — без проблем. Но в промышленной среде с помехами — лучше экранированный кабель.

Q: Нужен ли третий провод GND? A: Для корректной работы трансивера входная синфазная помеха не должна выходить за пределы -7В...+12В (Vcm). При длинных линиях и разных заземлениях это нарушается. GND-провод удерживает синфазное напряжение в пределах допустимого. Включайте GND во все промышленные установки.

Q: У устройства нет RS-485, только UART. Как подключить? A: Добавить внешний трансивер MAX485/SP3485 + резистор 300 Ом на DE/RE от GPIO.

Q: Что делать если устройства разных производителей не видят друг друга? A: Проверить: 1) Скорость/parity/stopbits совпадают. 2) Полярность A/B (иногда производители маркируют наоборот). 3) Адреса устройств уникальны. 4) Нет конфликта адресов.


Заключение

RS-485 — это не устаревший протокол, а надёжный, проверенный инструмент для промышленных приложений. Правильная линейная топология с терминаторами, экранированная витая пара, правильное управление DE/RE и гальваническая развязка там, где нужно — всё это обеспечит годы надёжной работы.

Вложите время в понимание физического уровня: осциллограф + хорошая книга по физике передачи сигналов. Большинство проблем RS-485 решаются на физическом уровне, а не в программе.

User Feedback

Create an account or sign in to leave a review

There are no reviews to display.

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.