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

от автора

Для задач робототехники, когда хочется применять вычисления на Python, использовать компьютерное зрение, ROS возникает необходимость быстрого и надежного обмена данными с микроконтроллером, который уже рулит всевозможными моторами, сервоприводами и датчиками.

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

Logic level converter.
Logic level converter.

Порты для подключения на устройствах строго определены
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:

  1. sudo raspi-config

  2. Interfacing options — SPI

  3. Включаем 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/


Комментарии

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *