Соединение двух Arduino Uno по SPI...

Тема в разделе "Проводная и беспроводная связь", создана пользователем matwaysha, 2 май 2019.

  1. matwaysha

    matwaysha Нерд

    Здравствуйте!
    Я делаю проект по системе стабилизации для беспилотника (он сможет стать на половину беспилотником, если я закончу проект, до настоящего ещё автопилот). Вопрос касается исключительно системы "отслеживания положения", алгоритм стабилизации есть. Использую для этого две ардуино уно - одна целиком идет на работу с гироскопом и акселерометром, чтобы не сбивать показания этих приборов задержкой на другую работу платы, а вторая пока делает всё остальное. В начале решил передать по UART один угол (я передаю именно углы) передать удалось, но нужно два - крен и тангаж (до рысканья ещё далеко...) пытался различать два приходящих угла по номеру, то есть кто пришел первый тот один угол, кто второй - тот второй угол и так постоянно считать до двух, но почему - то информация на экран (использую LCD дисплей, подключенный по I2C, т.к. UART занят) выводится абсолютно не анализируемая (для одного угла всё нормально), соответственно чтоб различать эти данные, точно их не путать и не плодить ошибки, решил пользоваться другой шиной, выбор пал на SPI т.к. I2C заняты на обеих платах (использую модуль акселерометра и гироскопа GY-521, он подключается только по I2C). Соответственно возник вопрос как написать программу для ведомой ардуино. По сети поискал, чуть - чуть нашел, но там прям слишком сложно (занимаюсь этим месяца два...), поэтому очень прошу помощи!!! Мне нужно понять как писать самую простую программу по отправке данных по этой шине, так же есть несколько вопросов и по ведущему устройству, но это уже после. Помогите пожалуйста.

    Спасибо.
     
    Последнее редактирование: 2 май 2019
  2. Daniil

    Daniil Гуру

    Ойёй!
    Лучше почитайте про modbus ascii (не rtu) и вернитесь к 1ому варианту.
    Будете передавать вместе с числом ещё и маркеры, надо будет распарсить пришедшие данные и всё.
    Ещё лучше работать с подтверждением приёма или с запросом, а не плеваться данными постоянно.
    Вы же сами знаете как;)
     
    matwaysha и b707 нравится это.
  3. b707

    b707 Гуру

    вопрос не в шине. На SPI у вас будет ровно та же проблема с передачей нескольких параметров, которую вы описали - будет трудно понять, где какие данные.
    Чтобы решить эту задачу (неважно - для I2c SPI или UART) , надо придумать свой собственный протокол передачи данных. Например, сначала стартовая комбинация из всегда одинаковых 3-6 байт, чтобы можно было точно отличить заголовк пакета. Потом данные. Потом признак конца данных и для надежности контрольная сумма.
     
    matwaysha и Daniil нравится это.
  4. matwaysha

    matwaysha Нерд

    Я, собственно вторую шину хотел использовать под второй угол, а первую под первый. Первая идея была прибавлять к одному значению 1000, к другому 2000, и так их различать, потом эти числа из них вычитать, но там видимо что - то с максимальной длиной числа связано (пока что плохо разбираюсь в этих битах байтах), не больше 4^4 получается, поэтому действительно передавать опознавательные знаки мне бы очень помогло. Сейчас почитаю про этот modbus.

    P.S. Спасибо всем, кто ответил.
     
  5. Daniil

    Daniil Гуру

    Конечно, так можно сделать, но это очень расточительно и не эффективно.

    Вам не нужно реализовывать modbus, просто, посмотрите как там делается. Берите его как отправную точку для своего протокола.
     
    matwaysha нравится это.
  6. parovoZZ

    parovoZZ Гуру

    Передавай двумя байтами. Но лучше организовать через запрос-ответ - запрашиваешь определенный угол через его признак. Например, 100 - тангаж, 101 - крен, 102 - рысканье.
     
    matwaysha и Daniil нравится это.
  7. matwaysha

    matwaysha Нерд

    Для меня во всех предложенных вариантах остается непонятным (в целом) как все данные вместе связать, то есть я что - то отправил с помощью Serial.write, что - то принял с помощью Serial.read, а как сделать так, чтоб или в одну отправку вместить и сигнал и данные и понять, что это всё вместе идет. Вопрос - ответ сейчас попробую.
     
  8. Daniil

    Daniil Гуру

    Вам нужно отправлять байты. Любое число в МК представляется как набор байтов.
    Строки представляются как набор символов (те же байты, только называем их по-другому, чтобы понимать что байт 0х31 - это символ "1" (см. ascii таблицу), а число 49).
    Допустим, вам нужно отправить число pi=3.14159265. Договоримся, что нас не интересуют точность более 5 знаков после зпт. Тогда можно пи*1е5 => 314159.265. Округлим до целого => 314159.
    Далее формируем пачку байт:
    Я пользуюсь символом ":" как начало пачки, а символ конца строки "0x13" (см. аскии таблицу) как конец пачки. Маркер числа я обозначу первым символом пачки "А", чтобы приёмник знал, что я отправляю данные с канала А, а не В.
    Код будет выглядеть так:
    Код (C++):
    Serial.write(":");\\начало пачки
    Serial.write("A");\\маркер числа
    Serial.write((uint32_t)(pi*1е5),HEX);\\число в hex, умноженное на 10^5 и округлённое
    Serial.writeln();\\конец пачки
    На приёмнике нужно принимать байты до тех пор пока не встретится символ конца строки, а затем нужно будет распарсить строку. Найти начало пачки, по следующему символу определить к какой переменной обращаемся, запихнуть в неё всё что идёт далее до символа конца строки.

    Я постарался всё упросить. Придраться есть к чему (число можно передавать иначе, не обязательно обозначать канал как человекочитабельный символ..), но для начала, для понимания происходящего, пойдёт.
     
    matwaysha нравится это.
  9. matwaysha

    matwaysha Нерд

    А как тогда на приеме это разделить? Записывать всё в строку (которая string) и потом просто как то по ней идти? Или есть какие - то другие способы? И как распознать конец строки?
    (попытался сделать вопрос - ответ, видимо сделал что - то неправильно и выводятся нули)
     
  10. b707

    b707 Гуру

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

    Посмотрите код, например. вот этой библиотеки -
    http://forum.amperka.ru/threads/-wirelessuart-
    как тут организована передача пакета и его прием. не обращайте внимание на технические детали - то что это УАРт и то что это радио - это все не важно. Разберитесь с общими программными принципами построения протокола.
     
    matwaysha нравится это.
  11. Daniil

    Daniil Гуру

    Да. Записывайте в строку каждый байт (заведите счётчик принятых байтов, он будет показывать длину пачки - строки). Если принятый байт равен "символу конца пачки" (у меня в примере это 0х13), то начать обрабатывать полученную пачку данных.
     
    matwaysha нравится это.
  12. Daniil

    Daniil Гуру

    Я бы добавил к обязательному просмотру пример из ардуино IDE - SerialEvent, благо проще разобрать.
    Там по прерыванию по Serial набирается массив. Как только пришёл байт '\n' идёт его (массива) обработка.
     
    matwaysha нравится это.
  13. matwaysha

    matwaysha Нерд

    Попробовал переписать программы под пакет данных. Всё ещё выводит нули (значения по умолчанию для углов). Участок кода для принимающей (идея была такая - стоп сигнал - три нуля, после них маркер данных)
    Код (C++):
    void loop() {
      lcd.setBacklight(255);
      lcd.home(); lcd.clear();
      if(stringComplete = true){
        m = inputString.toInt();
        k = m % 10;
        if(k == 1){
          x = m / 10000;
        }
        if(k == 2){
          y = m / 10000;
        }
      }
      if(y > 100){
        y = y - 256;
      }
      if(x > 100){
        x = x - 256;
      }
      lcd.setCursor(0,1);
      lcd.print(x);
      lcd.setCursor(6,1);
      lcd.print(y);
     
    }
    void serialEvent(){
      while(Serial.available()){
        int inint = (int)Serial.read();
        inputString += inint;
        if((n == 3) && (inint == 0)){
          n = 2;
        }
        if(n == 3){
          stringComplete = true;
        }
        if(inint == 0){
          n++;
        }
        if((n < 3) && (inint != 0)){
          n = 0;
        }
      }
    }
    участок кода для отправляющей (эта функция вызывается для отправки данных)
    Код (C++):
    void cout(){
      if(n == 1){
        Serial.write(x);
        Serial.write(k);
        Serial.write(k);
        Serial.write(k);
        Serial.write(n);
        n = 2;
        return;
      }
       if(n == 2){
        Serial.write(y);
        Serial.write(k);
        Serial.write(k);
        Serial.write(k);
        Serial.write(n);
        n = 1;
        return;
      }
    }
    Пока вообще не понимаю в чем проблема...
     
  14. Daniil

    Daniil Гуру

    Давайте, начнём.
    В передатчике, что такое x, y, k и n? приведите, пожалуйста примеры.
    (я надеюсь для теста они у вас заранее известны?).
    В приёмнике в SerialEvent у вас int сколько занимает места (в байтах)? Строка это массив чаров - это не особо то ошибка, но бросается в глаза.
    Что происходит тут?
     
    matwaysha нравится это.
  15. matwaysha

    matwaysha Нерд

    Да, переменные не описал. x, y - это углы, с помощью этого датчика они меряются только до + или - 90 градусов (именно в градусах), n - переменная для подсчета нулей (стоп - сигнала), k - это я не был уверен в том, как правильно выводить нули, поэтому выводил переменную, значение которой - 0. В приведенном участке кода идет проверка, действительно ли последние пришедшие нули, это нули стоп - сигнала, или нули в значении угла ( на всякий случай).
    По поводу String, я просто по опыту работы с ним в С++, знаю, что там, по крайней мере, можно в String записывать int меньше десяти (0 - 9) в один символ (элемент массива).
     
  16. Belkin

    Belkin Гик

    А почему обязательно по SPI обмениваться ?
    Портов достаточно, можно и в параллельном виде, даже быстрее будет в разы.
    Тем более, что в вашем случае скорость обмена имеет значение.
    Используются 9 пинов (8 информация + 1 сигнал запись/чтение) вместо 4 (SPI), но это оправдывает себя.
     
  17. Daniil

    Daniil Гуру

    В разных системах типы разные, где-то инт - 32 бита, где-то 16, а чар всегда 8.
    Тип. инт? флоат или ещё чего? Нужно знать заранее что отправляете, чтобы знать заранее что принимать.
    Как по-вашему выглядит выходная строка?
    Знаете почему используют в фильмах зелёный/синий экран, почему иногда в текстурах игр много фиолетового цвета? Потому что на актёрах этих цветов нет и на пост обработке в ПО выделяют в кадре всё зелёное и заменяют на то что нужно в данный момент. Так и получается спец. эффект.
    Только, на самом деле, не потому что на актёрах такого цвета нет, а потому что договорились "этот цвет будем удалять".
    Тут аналогично, символом конца строки нужно выбрать наиболее ненужный символ. Вы выбрали нуль...
     
  18. matwaysha

    matwaysha Нерд

    Да, углы в инт, по поводу последнего символа признаю, что ноль - не самый подходящий. но я не знаю как правильно отправить другие, как потом работать с этой строкой (выделить из неё сам угол) поэтому выбрал ноль. Я попытался избавиться от проблем, связанных с использованием нуля.
     
  19. Belkin

    Belkin Гик

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

    matwaysha Нерд

    А тут ещё одна проблема (возможно только для меня), нужно понять, какое количество памяти какой символ занимает и как потом эту посылку разбирать.