
Для задач робототехники, когда хочется применять вычисления на Python, использовать компьютерное зрение, ROS возникает необходимость быстрого и надежного обмена данными с микроконтроллером, который уже рулит всевозможными моторами, сервоприводами и датчиками.
Первое, о чем пришлось позаботиться — это согласование логических уровней двух устройств. Arduino работает на 5V, Raspberry на 3.3V. Для этого используется устройство LogicLevelConverter на 4 канала.

Порты для подключения на устройствах строго определены
Arduino Uno(Nano):
-
13 — SCK — тактовые импульсы для работы протокола SPI
-
12 — MISO (Master Input Slave Output) — передача данных от ведомого устройства (Arduino) к ведущему (Raspberry)
-
11 — MOSI (Master Output Slave Input) — передача данных от ведущего устройства (Raspberry) к ведомому (Arduino)
-
10 — CS или SS (Chip Select или Slave Select) — выбор устройства для работы. Raspberry может работать с 2 устройствами SPI сразу, и этот порт используется для указания, с каким идет обмен данными
-

Arduino Mega:
-
52 — SCK — тактовые импульсы для работы протокола SPI
-
50 — MISO (Master Input Slave Output) — передача данных от ведомого устройства (Arduino) к ведущему (Raspberry)
-
51 — MOSI (Master Output Slave Input) — передача данных от ведущего устройства (Raspberry) к ведомому (Arduino)
-
53 — CS или SS (Chip Select или Slave Select) — выбор устройства для работы. Raspberry может работать с 2 устройствами SPI сразу, и этот порт используется для указания, с каким идет обмен данными
-

Raspberry PI:
-
23 — SCK — тактовые импульсы для работы протокола SPI
-
21 — MISO (Master Input Slave Output) — передача данных от ведомого устройства (Arduino) к ведущему (Raspberry)
-
19 — MOSI (Master Output Slave Input) — передача данных от ведущего устройства (Raspberry) к ведомому (Arduino)
-
24 — CS или SS (Chip Select или Slave Select) — выбор устройства для работы. Raspberry может работать с 2 устройствами SPI сразу, и этот порт используется для указания, с каким идет обмен данными
-

Также к Logic level converter подключается рабочее напряжение каждого устройства и земля
Теперь к коду:
На Raspberry Pi необходимо включить SPI:
-
sudo raspi-config -
Interfacing options — SPI

-
Включаем SPI

Далее устанавливаем библиотеку spidev
pip3 install spidev
И используем заготовку кода для передачи данных
import spidev import time from camer2 import getCherry def list_int_to_bytes(input_list): # Split list int values to list ready for transfer by SPI # every value from -32768 to 32767 # will be replaced two values from -255 to 255 # Original values must be collected by Arduino after transmission output_list = [] for int_data in input_list: output_list.append(int_data >> 8) output_list.append(int_data & 255) return output_list def spi_send(txData): # Send and recieve 40 bytes N = 40 spi = spidev.SpiDev() spi.open(0, 0) spi.max_speed_hz = 1000000 txData = list_int_to_bytes(txData) txData = txData+[0]*(N-len(txData)) rxData = [] _ = spi.xfer2([240]) # 240 - b11110000 - start byte for i in range(40): rxData.append(spi.xfer2([txData[i]])[0]) spi.close() return rxData recieved_data = spi_send([1,2,3,4,5,6])
Функция spi_send принимает на вход список до 20 значений от -32768 до 32767, которые разбиваются в 40 байт и передаются в Arduino. В ответ функция возвращает 40 байт, полученных из Arduino
Код для Arduino:
#include <SPI.h> #define DATA_SIZE 40 byte data[DATA_SIZE];//массив, в которые получаем исходные данные int int_data[DATA_SIZE / 2];//массив в котором будут значения, полученные от Raspberry byte sendData[DATA_SIZE];//массив, значения которого будут переданы на Raspberry volatile byte counter = 0; volatile byte in_byte = 0; volatile byte spiTranferEnd = 0; volatile byte spiTranferStarted = 0; void fillSendData() {//заполняем массив числами, чтобы проверить корректность передачи for (byte i = 1; i < 40; i++) { sendData[i] = i; } } void setup() { Serial.begin(9600); pinMode(MISO, OUTPUT); SPCR |= _BV(SPE);//переводим SPI в режим Slave SPI.attachInterrupt();//включаем прерывания по SPI fillSendData(); } ISR (SPI_STC_vect)//обработка прерывания, получение и передача данных { in_byte = SPDR; if (in_byte == 240 and !spiTranferStarted) { spiTranferStarted = 1; counter = 0; SPDR = sendData[counter]; } if (spiTranferStarted and counter > 0) { data[counter - 1] = in_byte; SPDR = sendData[counter]; } counter++; if (counter == DATA_SIZE) { SPDR = sendData[counter - 1]; counter = 0; spiTranferStarted = 0; spiTranferEnd = 1; } } void joinRecievedBytes() {//функция, которая собирает 40 байт в 20 значений, которые передавались for (int i = 0; i < DATA_SIZE; i += 2) { int_data[i / 2] = data[i] << 8 | data[i + 1]; } spiTranferEnd = 0; } void printSpiData() {//вывод в монитор порта полученных значений for (int i = 0; i < DATA_SIZE / 2; i++) { Serial.print(int_data[i]); Serial.print(" "); } Serial.println(); } void loop () { if (spiTranferEnd) {//если эта переменная стала равна true, значит мы получили все 40 байт joinRecievedBytes();//собираем из 40 байт 20 значений // Тут можно написать действия с массивом int_data // if (int_data[0]==1) { // что-то делаем //} printSpiData();//выводим данные в монитор порта. Только для тестов! //ПОТОМ ОТКЛЮЧИТЬ, Т.К. ЗАМЕДЛЯЕТ РАБОТУ ПРОГРАММЫ } }
Такие дела! Успехов!
ссылка на оригинал статьи https://habr.com/ru/post/708844/



Добавить комментарий