Выбор протокола (интерфейса) связи между микроконтроллером и периферийными устройствами является важной частью встраиваемых систем. От правильности его выбора зависят многие параметры подобной системы: стоимость, скорость передачи данных, максимальное расстояние связи и т.д.
В одной из предыдущих статей на нашем сайте мы рассмотрели последовательную связь с помощью RS-485 между двумя платами Arduino, в этой же статье мы рассмотрим связь с помощью RS-485 между Raspberry Pi и Arduino Uno.
В данном проекте мы будем управлять углом поворота сервомотора, подключенного к плате Arduino Uno, с помощью передачи данных от платы Raspberry Pi к плате Arduino Uno с помощью интерфейса последовательной связи RS-485. Raspberry Pi будет ведущим устройством (Master), а плата Arduino Uno – ведомым (slave). На экране ЖК дисплея 16x2 мы будем показывать текущий угол поворота оси сервомотора.
Принципы работы интерфейса последовательной связи RS-485
RS-485 представляет собой асинхронный интерфейс последовательной связи, не требующий для своей работы импульсов синхронизации. Для передачи двоичных данных от одного устройства к другому протокол использует дифференциальный сигнал.
Если следовать определению из википедии, дифференциальный сигнал представляет собой способ электрической передачи информации с помощью двух противофазных сигналов. В данном методе один электрический сигнал передаётся в виде дифференциальной пары сигналов, каждый по своему проводнику, но один представляет инвертированный сигнал другого, противоположный по знаку. Пара проводников может представлять собой витую пару, твинаксиальный кабель или разводиться по печатной плате. Приёмник дифференциального сигнала реагирует на разницу между двумя сигналами, а не на различие между одним проводом и потенциалом земли.
В нашем случае дифференциальный сигнал образуется при помощи использования положительного и отрицательного напряжения 5V. Интерфейс RS-485 обеспечивает полудуплексную связь (Half-Duplex) при использовании 2-х линий (проводов) и полноценную дуплексную связь (Full-Duplex) при использовании 4-х линий (проводов).
Основные особенности данного интерфейса:
- Максимальная скорость передачи данных в интерфейсе RS-485 – 30 Мбит/с.
- Максимальная дистанция связи – 1200 метров, что значительно больше чем в интерфейсе RS-232.
- Основным достоинством интерфейса RS-485 по сравнению с RS-232 является использование нескольких ведомых (multiple slave) при одном ведущем (single master) в то время как RS-232 поддерживает только одного ведомого.
- Максимальное число устройств, которое можно подключить по интерфейсу RS-485 – 32.
- Также к достоинствам интерфейса RS-485 относится хорошая помехоустойчивость вследствие использования дифференциального сигнала.
- RS-485 обеспечивает более высокую скорость передачи по сравнению с интерфейсом I2C.
Общие принципы работы модуля MAX485 TTL to RS485
Для использования протокола RS-485 в данном проекте мы будем использовать модуль 5V MAX485 TTL to RS485, в основе которого лежит микросхема Maxim MAX485. Модуль является двунаправленным и обеспечивает последовательную связь на расстояние до 1200 метров. В полудуплексном режиме он обеспечивает скорость передачи данных 2,5 Мбит/с.
Модуль 5V MAX485 TTL to RS485 использует питающее напряжение 5V и логический уровень напряжения также 5V, что позволяет без проблем подключать его к платам Arduino.
Данный модуль имеет следующие особенности:
- работает с напряжениями 5V;
- имеет в своем составе чип MAX485;
- отличается низким энергопотреблением;
- всеми его контактами можно управлять с помощью микроконтроллера;
- размеры платы модуля: 44 x 14mm.
Внешний вид модуля RS-485 показан на следующем рисунке.
Назначение контактов (распиновка) модуля RS-485 приведена в следующей таблице.
Название контакта | Назначение контакта |
VCC | 5V |
A | вход/выход линии RS-485 |
B | вход/выход линии RS-485 |
GND | GND (0V) |
R0 | выход приемника (RX pin) |
RE | разрешение работы приемника |
DE | разрешение работы передатчика |
DI | вход передатчика (TX pin) |
Необходимые компоненты
- Плата Arduino Uno (купить на AliExpress).
- Raspberry Pi 3 B+ или другая аналогичная (купить на AliExpress).
- MAX485 TTL to RS485 Converter Module (модуль преобразования логики TTL в RS485, купить на AliExpress) – 2 шт.
- Потенциометр 10 кОм (купить на AliExpress).
- ЖК дисплей 16х2 (купить на AliExpress).
- Сервомотор SG-90 (купить на AliExpress).
- Макетная плата.
- Соединительные провода.
Реклама: ООО "АЛИБАБА.КОМ (РУ)" ИНН: 7703380158
Схема проекта
Для подключения модуля MAX485 TTL to RS-485 к плате Raspberry Pi мы будем использовать контакты GPIO14 и GPIO15 как показано на следующем рисунке.
Схема проекта последовательной связи с помощью интерфейса RS-485 между Raspberry Pi и Arduino Uno представлена на следующем рисунке.
В следующей таблице представлены необходимые соединения между платой Raspberry Pi 3 B+ (Master) и модулем RS-485.
Модуль RS-485 | Raspberry Pi 3 B+ |
DI | GPIO14 (TX) |
DE RE | GPIO4 |
R0 | GPIO15(RX) |
VCC | 5V |
GND | GND |
A | To A of Slave RS-485 |
B | To B of Slave RS-485 |
В следующей таблице представлены необходимые соединения между платой Arduino Uno (Slave) и модулем RS-485.
Модуль RS-485 | Arduino Uno |
DI | 1 (TX) |
DE RE | 2 |
R0 | 0 (RX) |
VCC | 5V |
GND | GND |
A | To A of Master RS-485 |
B | To B of Master RS-485 |
В следующей таблице представлены необходимые соединения между платой Arduino Uno и сервомотором SG-90.
Сервомотор SG-90 | Arduino Uno |
RED (красный) | +5V |
ORANGE (PWM) (оранжевый) | 3 |
BROWN (коричневый) | GND |
В следующей таблице представлены необходимые соединения между платой Arduino Uno и ЖК дисплеем 16x2.
ЖК дисплей 16x2 | Arduino Uno |
VSS | GND |
VDD | +5V |
V0 | к среднему контакту потенциометра для управления контрастностью ЖК дисплея |
RS | 8 |
RW | GND |
E | 9 |
D4 | 10 |
D5 | 11 |
D6 | 12 |
D7 | 13 |
A | +5V |
K | GND |
Схему проекта мы рассмотрели, можем приступать к написанию программ.
Объяснение программы для Raspberry Pi
В плате Raspberry Pi значения угла поворота оси сервомотора (0,10,45,90,135,180,135,90,45,10,0) будут передаваться с помощью протокола RS-485 через последовательный порт плате Arduino Uno и управлять сервомотором, подключенным к ней. Для осуществления последовательной связи в Raspberry Pi в ней необходимо включить последовательный порт (UART). Для этого вам нужно выполнить следующую последовательность шагов:
1. Откройте терминал и напечатайте в нем sudo raspi-config.
2. В открывшемся меню выберите Interfacing options (настройки подключения).
3. В настройках подключения выберите serial (последовательную связь).
4. Затем нажмите на ‘No’ – этим мы отключаем консоль последовательной связи (UART console) в Linux.
5. После этого выйдите из настроек.
6. Перезагрузите Raspberry Pi.
Теперь последовательный порт готов к использованию.
Примечание: перед передачей данных в модуль RS-485 необходимо на контакты DE и RE данного модуля подать напряжение высокого уровня (HIGH).
Первым делом в программе необходимо подключить (импортировать) все необходимые библиотеки: time, serial (для последовательной связи), GPIO (для использования контактов ввода-вывода) и sleep.
1 2 3 4 |
import time import serial import RPi.GPIO as GPIO from time import sleep |
Далее с помощью параметра GPIO.BOARD мы укажем плате, что мы будем обращаться к контактам платы путем указания их номера.
1 |
GPIO.setmode(GPIO.BOARD) |
На контакт GPIO с номером 7 платы Raspberry Pi нам необходимо подать напряжение высокого уровня (HIGH) поскольку к данному контакту подсоединены контакты DE и RE модуля RS-485 – это необходимо сделать для того чтобы разрешить передачу данных по протоколу RS-485.
1 |
GPIO.setup(7, GPIO.OUT, initial=GPIO.HIGH) |
Далее мы должны инициализировать класс для последовательной связи в плате Raspberry Pi на контактах GPIO14 и GPIO 15 (Serial0 Port) и указать в параметрах инициализации необходимую бодовую скорость, биты четности и стоповые биты.
1 2 3 4 5 6 7 8 |
send = serial.Serial( port='/dev/serial0', baudrate = 9600, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS, timeout=1 ) |
Далее инициализируем массив ‘i’, в котором будут храниться значения углов поворота оси сервомотора – их мы будем передавать с помощью последовательной связи.
1 |
i = [0,10,45,90,135,180,135,90,45,10,0] |
Затем в цикле с помощью функции send.write(str(x)) мы будем передавать данные последовательно модулю RS-485. Данные будут передаваться с задержкой 1.5 секунды.
1 2 3 4 5 |
while True: for x in i: send.write(str(x)) print(x) time.sleep(1.5) |
На этом код программы для платы Raspberry Pi, которая в данном случае используется в качестве ведущего устройства для осуществления последовательной связи по протоколу RS485, заканчивается.
Объяснение программы для Arduino Uno
В качестве ведомой стороны (Slave) в нашем проекте выступает плата Arduino Uno, которая будет получать данные от ведущей стороны (Master). Сервомотор будет поворачиваться на угол, значение которого будет приниматься от ведущей стороны. Также значение этого угла будет высвечиваться на экране ЖК дисплея. Таким образом, в программе для Arduino нам необходимо использовать библиотеку для работы с сервомотором и библиотеку для работы с ЖК дисплеем. Для программирования платы Arduino будет использоваться среда Arduino IDE.
Примечание: поскольку плата Arduino Uno будет принимать с ведомого модуля RS-485, то на контакты DE & RE модуля необходимо подать напряжение низкого уровня (LOW).
Первым делом в программе нам необходимо подключить используемые библиотеки.
1 2 |
#include <LiquidCrystal.h> #include <Servo.h> |
Далее нам необходимо сообщить плате Arduino к каким ее контактам подключен ЖК дисплей и инициализировать объект сервомотора.
1 2 |
LiquidCrystal lcd(8,9,10,11,12,13); // Define LCD display pins RS,E,D4,D5,D6,D7 Servo servo; |
Затем на экране ЖК дисплея покажем приветственное сообщение и после этого очистим его.
1 2 3 4 5 6 |
lcd.begin(16,2); lcd.print("CIRCUIT DIGEST"); lcd.setCursor(0,1); lcd.print("RS_485"); delay(3000); lcd.clear(); |
Далее инициализируем последовательную связь со скоростью 9600 бод/с.
1 |
Serial.begin(9600); |
Подаем на контакты DE & RE модуля RS-485 напряжение низкого уровня (LOW) чтобы он работал в качестве ведомого устройства (slave).
1 |
digitalWrite(enablePin, LOW); |
Сервомотор подключен к контакту 3 платы Arduino (на нем можно использовать ШИМ), поэтому подключаем к нему объект сервомотора в программе.
1 |
servo.attach(3); |
Далее в бесконечном цикле loop мы будем использовать функцию Serial.paseInt() чтобы принимать данные целого типа (Angle) из последовательного порта, которые передаются нам платой Raspberry Pi.
1 |
int angle = Serial.parseInt(); |
Затем принятое значение угла используем в команде управления сервомотором.
1 |
servo.write(angle); |
И, наконец, показываем принятое значение угла поворота сервомотора на экране ЖК дисплея.
1 2 3 4 |
lcd.setCursor(0,0); lcd.print("Angle From RPi "); lcd.setCursor(0,1); lcd.print(angle); |
Тестирование работы проекта
После того как аппаратная часть проекта будет готова, загрузите программу в плату Arduino и используйте терминал чтобы запустить код python на Raspberry Pi. Значение угла поворота сервомотора будет передаваться от Raspberry Pi к Arduino Uno с помощью протокола RS-485.
На следующих рисунках показаны примеры работы проекта при различных углах поворота сервомотора.
1. При угле 0.
2. При угле 90.
3. При угле 135.
4. При угле 180.
Исходный код программы (скетча)
Код программы для платы Raspberry Pi (ведущей)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import time import serial import RPi.GPIO as GPIO from time import sleep GPIO.setwarnings(False) GPIO.setmode(GPIO.BOARD) GPIO.setup(7, GPIO.OUT, initial=GPIO.HIGH) send = serial.Serial( port='/dev/serial0', baudrate = 9600, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS, timeout=1 ) i = [0,10,45,90,135,180,135,90,45,10,0] while True: for x in i: send.write(str(x)) print(x) time.sleep(1.5) |
Код программы для платы Arduino Uno (ведомой)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
#include <LiquidCrystal.h> //библиотека для работы с ЖК дисплеем #include <Servo.h> //библиотека для работы с сервомотором int enablePin = 2; LiquidCrystal lcd(8,9,10,11,12,13); // контакты, к которым подключен ЖК дисплей Servo servo; void setup() { lcd.begin(16,2); lcd.print("CIRCUIT DIGEST"); lcd.setCursor(0,1); lcd.print("RS_485"); delay(3000); lcd.clear(); Serial.begin(9600); // инициализируем последовательную связь со скоростью 9600 бод/с pinMode(enablePin, OUTPUT); delay(10); digitalWrite(enablePin, LOW); // (на Pin 2 всегда LOW чтобы принимать данные от Master’а) servo.attach(3); // (к Pin 3 (PWM pin) платы Arduino подключен сервомотор) } void loop() { while (Serial.available()) //если по последовательному порту принимаются какие либо данные { lcd.clear(); int angle = Serial.parseInt(); //принимаем значение целого типа (INTEGER) от Master’а по протоколу RS-485 servo.write(angle); // используем принятое значение для вращения оси сервомотора lcd.setCursor(0,0); lcd.print("Angle From RPi "); lcd.setCursor(0,1); lcd.print(angle); //показываем значение угла на ЖК дисплее } } |
==Преобразователь уровня== в гугле набейте 😉
Думаете, гугл все подскажет? ))
На модуле RS-485 TTL уровень 5 VDC, а IO порты Raspberry Pi 3.3 VDC. Почему на стороне Raspberry Pi TTL уровни не согласованы?
Да, суть проблемы я понимаю. Но был какой то нюанс, который позволяет так работать. И в какой то статье на нашем сайте он достаточно подробно описан. Только вот никак не могу вспомнить в какой, к сожалению
Я в своих опытах использовал 5-ти вольтовые уровни на 3-х вольтовых портах Raspberry и всё работало с обоих сторон. Но, так делать не рекомендуется т.к. низковольтные порты могут выйти из строя в неподходящий момент.
Да, согласен. Возможно, если небольшой промежуток времени, то ничего страшного не произойдет, а вот если на долговременную работу ставить, то лучше понизить уровень напряжения хотя бы с помощью резистивных делителей