DocsTech
/
Интерфейсы
/

~ cd 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, который может учитывать или исключать части кода через следующие параметры:

Код контроллера использует принцип работы 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 в проекты обеспечивает высокую производительность и надежность передачи данных.

Главная
Курсы
Вебинары
Описание интерфейса I2C: принцип работы, протокол и применение
15 наиболее используемых аппаратных интерфейсов
Интерфейс DMX512: описание и принцип работы
RS485: обзор, примеры на Verilog и С++
UART: обзор, примеры на Verilog и C++
SPI: обзор, примеры на Verilog и C++
Закрыть