Обмен по SPI: получение от слейва массива

Dimanoss
Offline
Зарегистрирован: 29.05.2016
День добрый, Уважаемые.
 
Воткнулся вот в какой камешек:  мастер по SPI пакетом отправляет int-массив и ждёт такого же массива от слейва.  Отправка проходит отлично, слейв всё обрабатывает, но вот обратно отослать получается только байт.  Отправку делаю по SPI.transfer(), в официальном доке ардуины даже не стоит возвращаемый тип (a стоит там лаконичное: Returns - the received data).
 
Попытался разобрать int[] на байты - тоже ничего обнадёживающего не происходит.
int sendToMaster[] = {10,12000,-801, 4321};

byte inBytes[sizeof(sendToMaster)*2];

unsigned int ii=0;
for (unsigned int i=0; i<sizeof(sendToMaster);i++)
{
  inBytes[ii] = highByte(sendToMaster[i]);
  ii++;
  inBytes[ii] = lowByte(sendToMaster[i]);
  ii++;
}

    SPDR = inBytes;

 
 
не подскажете, куда копать?
ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

А где код мастера и код слейва? Почему Вы их не привели? Как Вам помогать-то? Говорить, что у Вас ошибка в строке №ХЗ? Или как? Давайте код мастера и код слейва (только без лишней муры, если Вы туда ещё напихаете LCD, датчики и передачу на сервер, то сами всё и разгребайте). Компактные коды и пояснения что не так.

Кстати, в Вашем куске кода написан полный бред. Если и остальное такое же :-( Регистр SPRD - один байт. Чего Вы в него массив пихаете? Думаете, влезет?

Dimanoss
Offline
Зарегистрирован: 29.05.2016
код слейва я выше привёл
 
 
мастер:
      digitalWrite(SS, LOW);
      SPI.transfer(0x01);
      delay(10);

      int sendToSlaveOne[] = {2,6,0,1};  // mode, val1, val2, val3

      for (unsigned int i=0; i < sizeof(sendToSlaveOne); i++)
      {
          int ints = sendToSlaveOne[i];
          SPI.transfer(ints);
      }

      int back = SPI.transfer(0xAF);
      
      SPI.endTransaction();

      delay(10);

      String message = String("Back Data = ");
      message += String(back);
      Serial.println(message);

      digitalWrite(SS, HIGH);

 

со строкой 
int back = SPI.transfer(0xAF);
я чего только не пробовал - нужен, в принципе,  массив int, но корректно работает только byte.
 
 

ЕвгенийП пишет:
  Регистр SPRD - один байт.
- а где про это можно узнать?  В родном описании SPI ничего про это не сказано.

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Dimanoss пишет:

код слейва я выше привёл

Вы не привели ни кода слейва, ни кода мастера. Под кодом я понимаю то, что я могу запустить у себя - т.е. полный код, а не огрызки.

Dimanoss пишет:
я чего только не пробовал - нужен, в принципе,  массив int, но корректно работает только byte.

Так, правильно, весь процесс обмена по SPI заключается в том, что Вы посылаете байт и в ответ тут же принимаете байт. Затем посылаете второй байт и в ответ тут же принимаете байт. И так столько, сколько нужно. Количество переданных байтов всегда равно количсетву принятых. Если надо только принять (слейв ничего не ждёт), то ему посылается какой-нибудь ноль.

Т.е. Вы обмениваетесь по одному байту! Вы же даже попытки не делаете читать ответ после отправки.

Вот полный пример двустороннего обмена.

Dimanoss пишет:
а где про это можно узнать?  В родном описании SPI ничего про это не сказано.

В даташите Вашего контроллера и в любом описании SPI (не знаю, что Вы называете родным, но про то, что это по сути сдвиговый регистр, написано везде)

Dimanoss
Offline
Зарегистрирован: 29.05.2016
ЕвгенийП пишет:
Вот полный пример двустороннего обмена.
  
 
спасибо, код понятен, но опять я не нашёл ответа.  Вы слейву посылаете строку, получаете байт.  Это понятно.  С этим нет вопросов.  Меня интересует механика именно получения от слейва массива.  Как это происходит?  Должен ли я на стороне слейва разбирать массив (например - int[]) на байты и пересылать их по одному по нескольким единичным запросам от мастера, чтобы потом на стороне мастера опять эти байты собрать в интеджеры, а те, соответственно, - в массив?
andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Dimanoss пишет:

ЕвгенийП пишет:
Вот полный пример двустороннего обмена.
  
 
спасибо, код понятен, но опять я не нашёл ответа.  Вы слейву посылаете строку, получаете байт.  Это понятно.  С этим нет вопросов.  Меня интересует механика именно получения от слейва массива.  Как это происходит? 
Побайтно.
Цитата:
Должен ли я ...

Вы никому ничего не должны. Делайте так, как Вам удобнее.

b707
Онлайн
Зарегистрирован: 26.05.2017

Dimanoss пишет:

Должен ли я на стороне слейва разбирать массив (например - int[]) на байты и пересылать их по одному по нескольким единичным запросам от мастера, чтобы потом на стороне мастера опять эти байты собрать в интеджеры, а те, соответственно, - в массив?

налицо непонимание структур данных. Любой массив в Си - не важно, инт-ы это, или лонги, или флоаты... или даже какой-то навороченный класс - это всегда массив байт. Поэтому ничего "разбивать на байты" не надо. Берете ссылку на ваш массив, приводите к типу байт и начинаете посылать байты один за другим в цикле:

int array[20];

// сделать что-то со всеми байтами массива array, например отослать по SPI
byte *arr_ptr = (byte*) array;
for (int i =0; i< sizeof(array); i++) {
// отсылаем
SPI.write(*(arr_ptr + i));
}

 

Dimanoss
Offline
Зарегистрирован: 29.05.2016

b707 пишет:

// отсылаем

SPI.write(*(arr_ptr + i));

"отсылаем" - это здорово!  Как принимаем?  Конкретно: как master принимает от slave МАССИВ за один запрос?

b707
Онлайн
Зарегистрирован: 26.05.2017

Dimanoss пишет:

"отсылаем" - это здорово!  Как принимаем?  Конкретно: как master принимает от slave МАССИВ за один запрос?

принимает тоже побайтно, смотрите пример выше. что вам дал ЕвгенийП - вы же вроде написали, что "все понятно"

b707
Онлайн
Зарегистрирован: 26.05.2017

Dimanoss - не пытайтесь принять массив целиком. За раз можно принять только один байт.

Алгоритм простой. я бы даже сказал - ПРОСТЕЙШИЙ. Приняли один байт - сохраните в массив. Приняли следующий - сохраните его в следующую ячейку массива. И так до тех пор, пока не примете столько байт. сколько надо.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Dimanoss пишет:
Вы слейву посылаете строку, получаете байт.

Вы ничего не поняли. Никакой строки я не посылаю. Я посылают бай и получаю в ответ байт. Посылаю ещё байт и получаю в ответ ещё байт. И так пока не уйдёт вся строка. И никак по-другому. Я уже писал Вам об этом. Процитирую, а Вы читайте много раз до полного просветления

ЕвгенийП пишет:
весь процесс обмена по SPI заключается в том, что Вы посылаете байт и в ответ тут же принимаете байт. Затем посылаете второй байт и в ответ тут же принимаете байт. И так столько, сколько нужно. Количество переданных байтов всегда равно количеству принятых. Если надо только принять (слейв ничего не ждёт), то ему посылается какой-нибудь ноль.

Т.е. Вы обмениваетесь по одному байту!

Dimanoss пишет:
Меня интересует механика именно получения от слейва массива.  Как это происходит?

Ещё раз: посылаете байт, в ответ получаете байт. Посылаете ещё байт, в ответ получаете ещё байт. И так пока не пошлёте/получите весь Ваш массив.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Dimanoss пишет:
Конкретно: как master принимает от slave МАССИВ за один запрос?

Конкретно: никак!

Если бы и вправду поняли мой пост и пример, этого вопроса бы не было.

Dimanoss
Offline
Зарегистрирован: 29.05.2016

ЕвгенийП пишет:
Я посылают бай и получаю в ответ байт.

 
Ситуация, когда у мастера есть для отправки, например, пять байт, а ожидает он от слейва - двести, тут не рассматривается?  Нет, правда, спасибо, я понял Ваш код, я думал - есть варианты, когда по одному запросу приходит некоторый объём данных (желательно не-фиксированной длины).
b707
Онлайн
Зарегистрирован: 26.05.2017

Dimanoss пишет:

Ситуация, когда у мастера есть для отправки, например, пять байт, а ожидает он от слейва - двести, тут не рассматривается? 

блин, вы правда такой тупой или придуриваетесь? А может вообще не читаете, что вам пишут? Ну ведь на каждый ваш вопрос уже был ранее дан ответ:

ЕвгенийП пишет:

Количество переданных байтов всегда равно количсетву принятых. Если надо только принять (слейв ничего не ждёт), то ему посылается какой-нибудь ноль.

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

Dimanoss пишет:

Ситуация, когда у мастера есть для отправки, например, пять байт, а ожидает он от слейва - двести, тут не рассматривается?  Нет, правда, спасибо, я понял Ваш код, я думал - есть варианты, когда по одному запросу приходит некоторый объём данных (желательно не-фиксированной длины).

Первым байтом отправляете количество байт для приёма. Дальше принимаете сколько сказано - чего сложного?

b707
Онлайн
Зарегистрирован: 26.05.2017

mykaida пишет:

Первым байтом отправляете количество байт для приёма. Дальше принимаете сколько сказано - чего сложного?

для этого нужно, чтоб на обратной стороне тебя поняли - что означает твой "первый байт"

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

b707 пишет:

для этого нужно, чтоб на обратной стороне тебя поняли - что означает твой "первый байт"

А на принимающей стороне программу пишет неизвестный человек? Так нахера ему данные отдавать?

b707
Онлайн
Зарегистрирован: 26.05.2017

mykaida пишет:

А на принимающей стороне программу пишет неизвестный человек?

как раз известный - это ТС. Поэтому я и сомневаюсь...

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

b707 пишет:

как раз известный - это ТС. Поэтому я и сомневаюсь...

А, ну если так, то да - скорее всего неосилит...

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Dimanoss пишет:
Ситуация, когда у мастера есть для отправки, например, пять байт, а ожидает он от слейва - двести, тут не рассматривается?
Вы читаете, что Вам пишут? Я же Вам ещё вчера написал
ЕвгенийП пишет:
Если надо только принять (слейв ничего не ждёт), то ему посылается какой-нибудь ноль.
Т.е. если слейву от Вас ничего не надо - посылайте ему 200 нулей (или любой другой грязи) и принимайте двести байтов от него.

Но только байт на байт - по другому никак! Не работает по-другому - забудьте!

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

На всякий случай:

Если надо принять 201 байт - передаете 201 ноль,

если надо принять 202 байта - передаете 202 нуля,

если надо принять 203 байта - передаете 203 нуля.

Короче: напишите точно, сколько байт Вам надо передать, и я Вам подскажу, сколько надо передать нулей.

Dimanoss
Offline
Зарегистрирован: 29.05.2016

да, я примерно так и сделал, спасибо.  Сейчас бьюсь с какой-то непоняткой (почему-то первый байт приходит не тот), а так всё уже более-менее в пределах ожидаемого.

b707
Онлайн
Зарегистрирован: 26.05.2017

Dimanoss пишет:

Сейчас бьюсь с какой-то непоняткой (почему-то первый байт приходит не тот)

если это вопрос - показывайте код

Dimanoss
Offline
Зарегистрирован: 29.05.2016
вот, не могу найти где глючит. :-(  
 
проблемы: почему-то первый байт приходит вместо 0 - 116 и откуда-то берётся запрос на 9й пересылаемый байт.  Мастер посылает только 8 запросов SPI.transfer(0x16), но слейв откуда-то берёт 9й. :-( 
 
Мастер:
 
#include <Arduino.h>
#include "SPI.h"

// --------------- пара кнопок -----------------
const byte buttonUp = 11;
byte buttonStateUp = 0;
byte buttonStateFlagUp = 0;

const byte buttonDown = 12;
byte buttonStateDown = 0;
byte buttonStateFlagDown = 0;
//----------------------------------------------


const byte intDataArrSize = 4;            // длина int-массива
int recivedFromSlave[intDataArrSize];     // массив данных от сенсоров слейва
int sendToSlaveOne[] = {2009,6,0,-2321};  // данные для отсылки слейву


void setup (void)
{
  Serial.begin(115200);

  pinMode(buttonUp,INPUT);
  pinMode(buttonDown,INPUT);

  SPI.begin();
  SPI.setClockDivider(SPI_CLOCK_DIV8);
  digitalWrite(SS,HIGH);

  recivedFromSlave[0] = 19;
  recivedFromSlave[1] = 622;
  recivedFromSlave[2] = -10;
  recivedFromSlave[3] = 32000;

  // смотрим, что в int-массиве от слейва при инициализации
  String message = String("init data (INT) = ");
  for (byte i=0; i<intDataArrSize; i++)
  {
    if (i>0) message += String(", ");
    message += String(recivedFromSlave[i]);
  }
  Serial.println(message);
}


void examButtons()            // опрашиваем кнопки
{
    byte buttons[] = {buttonUp, buttonDown};
    byte *states[] = {&buttonStateUp, &buttonStateDown};
    byte *flags[] = {&buttonStateFlagUp, &buttonStateFlagDown};

    byte buttonState = 0;

    for (byte i=0; i<sizeof(buttons); i++)
    {
      buttonState = digitalRead(buttons[i]);
      if(buttonState == HIGH)
      {
        *states[i] = 1;
      } else {
        *states[i] = 0;
        *flags[i] = 0;
      }
    }
}

void sendData() {

      digitalWrite(SS, LOW);
      SPI.transfer(0x01);     // инициируем отсылку
      delay(10);

      for (unsigned int i=0; i < sizeof(sendToSlaveOne); i++)
      {
          int ints = sendToSlaveOne[i];
          SPI.transfer(ints);
      }

      byte recivedArr[sizeof(recivedFromSlave)];            // byte-массив для данных от слейва
      for (byte ii=0; ii<sizeof(recivedFromSlave); ii++)
      {
        recivedArr[ii] = SPI.transfer(0x16);    // маркер запроса байта данных от слейва

        // проверяем, что пришло
        String message = String("recivedArr[" + String(ii) + "] = ");
        message += String(recivedArr[ii]);
        Serial.println(message);

        delay(5);
      }

      SPI.transfer(0xAF);             // маркер конца пакета данных

      SPI.endTransaction();
      digitalWrite(SS, HIGH);

      byte ii=0;
      for (byte i=0; i<intDataArrSize; i++)
      {
        recivedFromSlave[i] = recivedArr[ii+1] | (recivedArr[ii]<<8);
        ii = ii+2;
      }

      // смотрим, что в int-массиве от слейва
      String message = String("back data (INT) = ");
      for (byte i=0; i<intDataArrSize; i++)
      {
        if (i>0) message += String(", ");
        message += String(recivedFromSlave[i]);
      }
      Serial.println(message);
}



void loop(void)
{
  examButtons();

  if (buttonStateUp==1)
  {
    if (buttonStateFlagUp == 0)
    {
      buttonStateFlagUp = 1;
      sendData();
    }
  }
}

Слейв:

#include <Arduino.h>
#include "SPI.h"


const byte intDataArrSize = 4;                    // длина int-массива
int dataFromSensors[] = {0,0,0,0};                // рабочий массив
byte sendDataToMaster[sizeof(dataFromSensors)];   // массив для передачи
byte sCur = 0;                                    // курсор передачи
byte bCur = 0;                                    // курсор буфера

volatile bool newDataFlag = false;              // флаг готовности новых данных от мастера

#define BUFFER_SIZE 50
char buffer[BUFFER_SIZE];         // буфер для данных от мастера



void makeDataToMaster()                        // конвертируем рабочий int-массив в byte-массив для передачи
{
  byte ii=0;
  for (byte i=0; i<intDataArrSize;i++)
  {
    sendDataToMaster[ii] = highByte(dataFromSensors[i]);
    ii++;
    sendDataToMaster[ii] = lowByte(dataFromSensors[i]);
    ii++;
  }
}


void setup()
{
  Serial.begin(115200);
  pinMode(MISO,OUTPUT);                   //Sets MISO as OUTPUT (Have to Send data to Master IN
  SPCR |= _BV(SPE);                       //Turn on SPI in Slave Mode
  SPI.attachInterrupt();                  //Interuupt ON is set for SPI commnucation

  // имитация сбора данных от сенсоров
  dataFromSensors[0] = 48;
  dataFromSensors[1] = -2001;
  dataFromSensors[2] = 99;
  dataFromSensors[3] = 21;
}




ISR (SPI_STC_vect)
{
  byte byteFromMaster = SPDR;

  if (bCur < sizeof(buffer)) {
          buffer[bCur++] = byteFromMaster;
          if (byteFromMaster == 0xAF)         // конец пакета
          {
              newDataFlag = true;          // данные от мастера обновлены
              sCur = 0;
          }

          if (byteFromMaster == 0x16)     // запрос мастера на отправку массива
          {
                if (sCur == 0)            // начало отправки массива мастеру
                {
                  makeDataToMaster();     // готовим массив для отправки мастеру
                }

                SPDR = sendDataToMaster[sCur];
                // проверяем, что послали
                String message = String("sent: ") + String(sendDataToMaster[sCur]) + String(" (sendDataToMaster[") + String(sCur) + String("])");
                Serial.println(message);

                sCur++;
          }
      }
}


void loop()
{
  if(newDataFlag)             // обрабатываем полученное от мастера
  {
    newDataFlag = false;      // ждём новый пакет от мастера
  }
}

вот итог:

байты

уходят от слейва

приходят к мастеру

0

0

116

1

48

48

2

248

248

3

47

47

4

0

0

5

99

99

6

0

0

7

21

21

8

8

 

 

 

b707
Онлайн
Зарегистрирован: 26.05.2017

опять какая-то жуть наворочена. В мастере в строчках 74-78 - что происходит? Вы берете массив из 4х элементов, а отсылаете восемь? -- опять путаете int и byte

В приемнике как увидел работу со String в прерывании и потом вывод этого в Сериал - дальше смотреть не стал. Оно у вас не виснет???

Запомните, вывод в Сериал сам использует прерывания. поэтому пользоваться им внутри другого прерывания нельзя.

 

Dimanoss
Offline
Зарегистрирован: 29.05.2016

вывод в сериал - исключительно для наглядности.  В рабочем коде всё это убирается.

Dimanoss
Offline
Зарегистрирован: 29.05.2016

b707 пишет:

В мастере в строчках 74-78 - что происходит? Вы берете массив из 4х элементов, а отсылаете восемь? -- опять путаете int и byte

а как я должен int'ы отсылать, как не побайтно?  Один int = два bytes, 4*2=8, не так разве?

Dimanoss
Offline
Зарегистрирован: 29.05.2016

ах, да, я докомпримировался (чтобы здесь выложить) до того, что убрал перевод пересылаемых int'ов в байты, сорри.  Этот кусок (74-78) вообще тут не нужен.

b707
Онлайн
Зарегистрирован: 26.05.2017

Dimanoss пишет:

ах, да, я докомпримировался (чтобы здесь выложить) до того, что убрал перевод пересылаемых int'ов в байты, сорри.  Этот кусок (74-78) вообще тут не нужен.

кроме этого куска вы вообще нигде ничего на слейв не отсылаете. Или вам это уже не нужно? - тогда не ясно. почему процедура называется SendData(), а не Receive...

Или вы опять что-то в коде потеряли? Выложите ПРАВИЛЬНЫЙ код, пожалуйста, чтобы не тратить время на обсуждения, что тут верно, а что нет

Dimanoss
Offline
Зарегистрирован: 29.05.2016

выходит, вся заковыка в названии функции? :-)

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

b707
Онлайн
Зарегистрирован: 26.05.2017

Dimanoss пишет:

выходит, вся заковыка в названии функции? :-)

заковыка в том, что вы не можете обьяснить. что хотите... Пишете, что что-то отсылаете слейву - а в коде этого нет. А когда не понятно. что же человек хочет добиться - помогать сложно.

Upper
Онлайн
Зарегистрирован: 23.06.2020

b707 пишет:

В приемнике как увидел работу со String в прерывании и потом вывод этого в Сериал - дальше смотреть не стал. Оно у вас не виснет???

Запомните, вывод в Сериал сам использует прерывания. поэтому пользоваться им внутри другого прерывания нельзя.

Согласен, что без четкого понимания лучше в прерывании лишнего не использовать, но

Сериал на отправку может работать внутри прерываний (По крайней мере на Arduino UNO). Пока есть место в буфере, он просто пишет  в буфер. Если в буфере место кончилось,  ждет когда освободится место и пока место не освободится блокирует весь остальной код.