Новости:

SMF форум только что установлен!

Main Menu

Обмен данными по SPI между Raspberry Pi и Arduino

Автор ak167, 11 Марта 2023, 23:29:33

« предыдущая тема - следующая тема »

ak167

Для задач робототехники, когда хочется применять вычисления на 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])[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

#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;
}
}

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 << 8 | data[i + 1];
}
spiTranferEnd = 0;
}

void printSpiData() {//вывод в монитор порта полученных значений
for (int i = 0; i < DATA_SIZE / 2; i++) {
Serial.print(int_data);
Serial.print(" ");
}
Serial.println();
}

void loop () {
if (spiTranferEnd) {//если эта переменная стала равна true, значит мы получили все 40 байт
joinRecievedBytes();//собираем из 40 байт 20 значений

// Тут можно написать действия с массивом int_data
// if (int_data[0]==1) {
// что-то делаем
//}
printSpiData();//выводим данные в монитор порта. Только для тестов!
//ПОТОМ ОТКЛЮЧИТЬ, Т.К. ЗАМЕДЛЯЕТ РАБОТУ ПРОГРАММЫ
}
}


Такие дела! Успехов!