Raspberry PI 3 + OpenCV + Arduino UNO + ......(поледнее решение и вопрос в последнем сообщении)

Тема в разделе "Флудилка", создана пользователем Igor68, 21 ноя 2016.

  1. Igor68

    Igor68 Гуру

    Собственно не только один вопрос...
    Вопрос 1. Начал опыты со зверьком... точнее с применением камеры по USB в составе малины:
    snapshot1.png
    т.е. разворот камеры производится по Serial с помощью ардуино + мультисерво. ...На картинке видно исходное изображение и адапривное выделение линий. Перепробовал ряд примеров обработки:
    segment_cpp_work.zip
    Данный пример взят из инета, но несколько изменён... клавишами 1...8 выбирается отображение того или иного метода в окне "out". Интересен опыт выделения фрагментов изображения с камеры, передачи данных другой малине (имеется ввиду упрощённое представление картинки). Есть ли у кого опыт построения "карты" пространства для передвижения "зверька" (Он имеет 4 колеса и умеет поворачивать и поднимать камеру). Понятное дело что одна малина не сможет всё это, так что потребуется стационарная "батарея" этих самых малин (пока что только две дополнительные в наличии). Обмен данными как таковой определён (клиент-сервер). Как оптимальнее сформировать данные(их представление)? Как выделить фрагменты и объекты... если можно так выразиться?
    Вопрос 2. Строить карту и вырабатывать команды должна "батарея" и этот вопрос относится к конструкции. Вот присмотрел:
    125813613_GeauxRobot_Raspberry_Pi_2_B_4layer_Dog_Bone_Stack_Clear_Case.jpg

    maxresdefault.jpg
    T800_Raspberry Pi Second Cluster 3.jpg
    Но в моём случае все они будут по WI-FI соединены с одной... так, что монтаж между собой только по питанию. Ну вроде кластера... Этот вопрос собственно к конструкции... точнее сборке однотипных плат воедино. Есть у кого опыт? Что доступно для покупки? И что проще и/или удобнее во всех отношениях?
    Вопрос 3. Со стороны Arduino пробую "поднять" протокол Modbus RTU. Ничего общего с аппаратной связью по RS-485. Только протокол обмена. По USB-SERIAL. По тому самому порту, что использую для заливки. Может так и надо - ведь Arduino IDE тоже находиться на Raspberry... ну и заливка им же.
    Спасибо!
     

    Вложения:

    Последнее редактирование: 23 ноя 2016
    Alex19 и ИгорьК нравится это.
  2. Igor68

    Igor68 Гуру

    Доброго времени суток!
    По Вопросу 3 вот что получается:
    - Реализована пока только команда 6 (Modbus RTU)- запись в единичный регистр. Но есть не реализованные пункты для самого протокола:
    время ожидания, после чего передача считается прерванной = 3.5 длительности передачи одного байта (для скорости передачи 9600) сек = ( (1/ 9600) * (1 + 8 +1) * 3.4). Соответственно стартовый бит, 8 бит данных, стоп бит размером в 1 (может быть 1, 1.5, 2).
    Со стороны Arduino (отладочный вариант исходя из параметра компиляции - в файле config.h:
    Код (C++):
    //при выбранных вариантах комптляция для соответствующего теста
    /*
    *****************************
    * параметры компиляции *
    * для теста                        *
    *****************************
    реализация теста:
    0x10-connect;
    0x20-ComMotion;
    0x21-comMotion status
    0x30-Multiservo
    0x40-MultiservoCam
    0x70-ComMotion+Multiservo
    0x71-ComMotion+Multiservo+Serial
    0x72-ComMotion+Multiservo+Serial+modbus
    */

    #define  _test  0 //0x71
     
    работает Arduino в связке с ComMotion driver for 4 motors и Multiservo(Amperka) по I2C ). В архиве:
    r1_uno_plus.zip

    Со стороны Raspberry PI на Си:
    cmd6.zip

    Связь между Raspberry PI и Arduino по USB-SERIAL с применением того же соединения, что и для заливки в Arduino. Arduino IDE так же установлена на малине. Для тестирования применён скрипт на BASH(файл test):
    Код (Bash):
    #!/bin/bash

    ./cmd6 /dev/ttyUSB0 1 48 0
    echo "...wait 5 sec"
    sleep 5
    echo "motor M1 = -100"
    ./cmd6 /dev/ttyUSB0 1 48 -100
    echo "motor M2 = -100"
    ./cmd6 /dev/ttyUSB0 1 49 -100
    echo "motor M3 = -100"
    ./cmd6 /dev/ttyUSB0 1 50 -100
    echo "motor M4 = -100"
    ./cmd6 /dev/ttyUSB0 1 51 -100
    #
    echo "...wait 5 sec"
    sleep 5
    #
    echo "motor M1 = 0"
    ./cmd6 /dev/ttyUSB0 1 48 0
    echo "motor M2 = 0"
    ./cmd6 /dev/ttyUSB0 1 49 0
    echo "motor M3 = 0"
    ./cmd6 /dev/ttyUSB0 1 50 0
    echo "motor M4 = 0"
    ./cmd6 /dev/ttyUSB0 1 51 0
    #
    echo "...wait 5 sec"
    sleep 5
    #
    echo "motor M1 = 100"
    ./cmd6 /dev/ttyUSB0 1 48 100
    echo "motor M2 = 100"
    ./cmd6 /dev/ttyUSB0 1 49 100
    echo "motor M3 = 100"
    ./cmd6 /dev/ttyUSB0 1 50 100
    echo "motor M4 = 100"
    ./cmd6 /dev/ttyUSB0 1 51 100
    #
    echo "...wait 5 sec"
    sleep 5
    #
    echo "motor M1 = 0"
    ./cmd6 /dev/ttyUSB0 1 48 0
    echo "motor M2 = 0"
    ./cmd6 /dev/ttyUSB0 1 49 0
    echo "motor M3 = 0"
    ./cmd6 /dev/ttyUSB0 1 50 0
    echo "motor M4 = 0"
    ./cmd6 /dev/ttyUSB0 1 51 0
     
    , который использует откоммпилированный код.
    Собственно вопрос:
    - Есть ли возможность "распараллелить" обмен по Serial и I2C со стороны Arduino. И если есть, то как можно это сделать.
    Данная необходимость обусловлена "задержками" в обмене по I2C, что в свою очередь влияет на связь между Arduino и Raspberry.


    Была тема на форуме про организацию потоков в Arduino... что-то найти никак не получается!
    Дайте ссылочку если не затруднит!

    Спасибо!
     

    Вложения:

    • r1_uno_plus.zip
      Размер файла:
      11,4 КБ
      Просмотров:
      135
    • cmd6.zip
      Размер файла:
      3,7 КБ
      Просмотров:
      145
    Последнее редактирование: 24 ноя 2016
    ИгорьК нравится это.
  3. Igor68

    Igor68 Гуру

    Собственно вопрос остаётся по потокам. Была тема про потоки... я делал комментарий(пост)... но никак не могу найти. Просто "Потоки" - похожая тема.. но..
    Сейчас для Arduino изменённый архив:
    r1_uno_plus.zip
    Где добавлено обращение к Multiservo и для "разгрузки" сделана работа по I2C циклически:
    Код (C++):
      switch(dnum)
      {
        //Multiservo
        case 0:
          servo[0].write(dservo[0]);
          dnum++;
          break;
         case 1:
          servo[1].write(dservo[1]);
          dnum++;
          break;
        case 2:
          servo[2].write(dservo[2]);
          dnum++;
          break;
        case 3:
          servo[3].write(dservo[3]);
          dnum++;
          break;
        case 4:
          servo[4].write(dservo[4]);
          dnum++;
          break;
        case 5:
          servo[5].write(dservo[5]);
          dnum++;
          break;
        case 6:
          servo[6].write(dservo[6]);
          dnum++;
          break;
        case 7:
          servo[7].write(dservo[7]);
          dnum++;
          break;
        case 8:
          servo[8].write(dservo[8]);
          dnum++;
          break;
        case 9:
          servo[9].write(dservo[9]);
          dnum++;
          break;
        case 10:
          servo[10].write(dservo[10]);
          dnum++;
          break;
        case 11:
          servo[11].write(dservo[11]);
          dnum++;
          break;
        case 12:
          servo[12].write(dservo[12]);
          dnum++;
          break;
        case 13:
          servo[13].write(dservo[13]);
          dnum++;
          break;
        case 14:
          servo[14].write(dservo[14]);
          dnum++;
          break;
        case 15:
          servo[15].write(dservo[15]);
          dnum++;
          break;
        case 16:
          servo[16].write(dservo[16]);
          dnum++;
          break;
        case 17:
          servo[17].write(dservo[17]);
          dnum++;
          break;
        //ComMotion driver for 4 motors - значения моторов
        case 18:
          IndividualMotorControl(dmotor[0], dmotor[1], dmotor[2], dmotor[3]);
          dnum = 0;
          break;
        default:
          dnum = 0;
          break;
      }
      //
      delay(20);
      MRTU_rWORK();
     
    Но это несколько замедляет реакцию для устройств по I2C. А при реализации команды 16 - запись в несколько регистров, время станет ощутимо больше.

    Со стороны Raspberry в тесте поменялся только файл test:
    cmd6.zip

    Спасибо!
     

    Вложения:

    • r1_uno_plus.zip
      Размер файла:
      11,7 КБ
      Просмотров:
      142
    • cmd6.zip
      Размер файла:
      3,9 КБ
      Просмотров:
      144
    Последнее редактирование: 24 ноя 2016
  4. AlexU

    AlexU Гуру

    http://forum.amperka.ru/threads/Потоки-в-avr.9856/unread
    Но насколько понял планируется два потока: первый -- работа с последовательным портом; второй -- взаимодействие через I2C. Такую задачу можно решить и без привлечения avr-threads. Я бы весь функционал, отвечающий за взаимодействие с последовательным портом, повесил бы на прерывания -- чтение данных и формирование команд с постановкой в очередь. А основной код программы отвечал бы за чтение команд из очереди и взаимодействие по I2C.
     
    Igor68 нравится это.
  5. Igor68

    Igor68 Гуру

    С прерыванием именно на Arduino говорю откровенно... не очень. Я про Arduino IDE а не Keil и др. Потом повесив прерывание как быть с возможностью загрузки по USB-SERIAL... конфузов не будет?
    За потоки... Ай спасибочки!!!!!!!!!:D
    Именно та ссылка. В моём случае Arduino ничего не делает кроме
    Raspberry <--usbserial--> Arduino <---I2C---> ComMotion+Multiservo
    Это именно та ссылочка! Спасибо!
     
  6. Igor68

    Igor68 Гуру

    По работе Arduino PThread пока временный стоп, но то что уже есть вполне готово для управлением приводами(по cmd6 Modbus RTU) ну... хотя бы из веб сервера... коли работает в BASH. Это будет работать аналогично и в PHP. Конечно не планирую это рассматривать. Пока "стыкую" это по схеме "универсальный" сервер. В данном случае производится тест взаимодействие PC(пограмма в Win) <-- ETHERNET(WiFI)--><Rarpberry(сервер)><--USBSERIAL--><Arduino UNO><--I2C--><Multiservo+ComMotion>
    Но остаётся ещё кроме этого вопросы по конструкции. В данный момент "зверёк" на "пузе" когда колёса в воздухе и умеет крутить их и поворачивать веб камеру, а питание и связь на столе.
    20161125_183643.jpg
    20161125_183654.jpg
    Так что с конструкцией пока не понятно. Особенно с питанием и размещением малины.
    Спасибо!

    Есть замечание по разводке платы Multisero - всегда... всегда надо делать по мере возможности полигоном делать цепи "VIN"("BAT"), "GND". Плата не многослойная... а всего двухсторонняя. Сохраняйте массу меди по мере возможности - тут не гигагерцы "таскаюся". И если методом травления - то дешевле. И естественная монтажная ёмкость по питанию - это только плюс.
     
    Последнее редактирование: 25 ноя 2016
  7. Igor68

    Igor68 Гуру

    С прерываниями... не то что боюсь для Arduino... побаиваюсь отвыкнуть от SAM3U4... это на "слуху", правда в Keil и в основном привычка к ассемблерным "блокам". Глубоко изучение платформы Arduino не планирую (включая применения без необходимости самой идеи AVR), не планируется. ARM с самого начала, после PIC, значит ARM(А уж Cortex и т.д. не принципиально). Но уважаю глубоко сторонников AVR и Arduino в их числе.
    По идее Малина на самом "зверьке" даст возможность "перепрошивать" Ардуино по мере надобности.
     
    Последнее редактирование: 26 ноя 2016
  8. Igor68

    Igor68 Гуру

    Вот курил... курил... работу с потоками в Arduino, а меж тем не терпелось испытать связь с PC.
    Таким образом:
    1) Программа на PC qtarpi передаёт и принимает параметры серверу/от сервера;
    2) Сервер либо на Raspberry либо на иной линукс машине (я применял локально) принимает параметры от qtarpi и передаёт их обратно(подтверждение). Он же(сервер) передаёт параметры в Arduino по последовательному порту (я применял /dev/ttyUSB0) по логическому(не аппаратному) протоколу ModbusRTU (пока команда 6 - запись в один регистр).
    3) Arduino принимает параметры по логическому(не аппаратному) протоколу ModbusRTU (пока команда 6 - запись в один регистр) и передаёт параметры в шилды: ComMotion drivers for 4 motors и Multiservo (Amperka) по шине I2C.

    Несколько изменил со стороны Arduino:
    r1_uno_plus.zip

    Ну понятное дело для связи сервер со стороны Raspberry потребуется. За основу взял тот самый "универсальный" сервер, где изменил только MainWork:

    Код (C++):
    //рабочий модуль
    void    MainWork(void)
    {
        I16    val;
        int    count;
        int    addr;
        //
        pthread_mutex_lock(&ServerMutex);
        //копирование заданий регистров
        addr     = 0;
        count    = 0;
        while(count < _cnt_regs)
        {
            regs[count] = (*(I16*)(&wbuffrom[addr]));
            addr += 2;
            count++;
        }
        //копировние значений регистров
        addr     = 0;
        count    = 0;
        while(count < _cnt_regs)
        {
            (*(I16*)(&wbufto[addr])) = regs[count];
            addr += 2;
            count++;
        }
        //
        switch(num_work)
        {
            case 0:    //привод 1
                val = regs[_num_vol_m1];
                pthread_mutex_unlock(&ServerMutex);
                MRTU_CMD_6(1, _num_vol_m1, val);
                num_work++;
                break;
            case 1:    //привод 2
                val = regs[_num_vol_m2];
                pthread_mutex_unlock(&ServerMutex);
                MRTU_CMD_6(1, _num_vol_m2, val);
                num_work++;
                break;
            case 2:    //привод 3
                val = regs[_num_vol_m3];
                pthread_mutex_unlock(&ServerMutex);
                MRTU_CMD_6(1, _num_vol_m3, val);
                num_work++;
                break;
            case 3:    //привод 4
                val = regs[_num_vol_m4];
                pthread_mutex_unlock(&ServerMutex);
                MRTU_CMD_6(1, _num_vol_m4, val);
                num_work++;
                break;
            case 4:    //горизонтальная позиция камеры
                val = regs[_num_pos_s1];
                pthread_mutex_unlock(&ServerMutex);
                MRTU_CMD_6(1, _num_pos_s1, val);
                num_work++;
                break;
            case 5:    //вертикальная позиция камеры
                val = regs[_num_pos_s2];
                pthread_mutex_unlock(&ServerMutex);
                MRTU_CMD_6(1, _num_pos_s2, val);
                num_work++;
                break;
            default:
                num_work = 0;
                pthread_mutex_unlock(&ServerMutex);
                break;
        }
    }
     
    И добавил файлы (пока только тестируемые) mrtu.c и mrtu.h, в котрорых реализована только команда 6:
    arbot.zip
    Это и есть заготовка рабочей программы. Скажу сразу на моём рабочем месте бардак и соответственно нехватка мета. Потому компилировал на ПК и все испытания проводил на нём же. Соответственно для связи применял локальный IP адрес. Тут же и запускал программу для управления, сделанную в QtCreator:
    qtarpi.zip
    Уж больно всё компактно по сравнению с Visual Studio. Да и с виртуальным Windows ковыряться не требуется. В результате - при запуске (в поле IP указать IP адрес - в моём случае локальный, а в поле Port указать порт сервера):
    snapshot2.png
    Тут после запуска и соединения устанавливаются и передаются серверу, а далее зверьку значения по умолчанию. Получилось... так сказать телеуправление:
    snapshot3.png
    Соответственно кнопками задаётся режим движения для ComMotion driver for 4 motors. "Ползунками" задаются скорость движения в ComMotion и поворот камеры по горизонтали и вертикали для Multiservo.
    Управление движением надо делать через стоп. Иначе изменение нагрузки на приводах приводит к перегрузки драйверов. В моём случае сбой по питанию.
    Сейчас время реакции на управление около секунды (менее).

    Для запуска сервера на малине потребуется откомпилировать его на самой малине, а старт переделать на свой применяемый TCP порт и последовательный порт в скрипте start:
    Код (Bash):
    #!/bin/bash

    echo "...starting Arduino & Raspberry conrol"
    ./arbot -p 8971 -sp /dev/ttyUSB0
     
    "Прошивка" для ардуино не отличается устойчивостью... в процессе доработки, а если точнее то в стадии эксперимента.

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

    Спасибо!
     

    Вложения:

    • r1_uno_plus.zip
      Размер файла:
      11,8 КБ
      Просмотров:
      140
    • arbot.zip
      Размер файла:
      29,3 КБ
      Просмотров:
      121
    • qtarpi.zip
      Размер файла:
      49,2 КБ
      Просмотров:
      130
    Последнее редактирование: 29 ноя 2016
  9. Igor68

    Igor68 Гуру

    Переделка "по нормальному" для заливки в Arduino.... удалив мусор коего много собралось в процессе опытов.
    Пытаюсь повесить работу по I2C на таймер 2...
    в setup:
    Код (C++):
     //
      MsTimer2::set(_t2_i2c_interval, I2C_Work);
      MsTimer2::start();
      //
     
    где: _t2_i2c_interval = 100 - я думаю в милисекундах... согласно этому:
    http://arduino.net.ua/file_archive/Arduino Library/MsTimer2 Library/
    но вот работа основного цикла
    Код (C++):
    void loop()
    {
    //штатная работа
      Serial.println("123"); //проверка работы цикла
      //
      delay(20);
      MRTU_rWORK();
    }
    умерла
    Это какой-то конфликт и мне не следует использовать MsTimer2 ? ...или что-то не так делаю. Может надо каждый раз реинициализировать таймер MsTimer2::set(_t2_i2c_interval, I2C_Work);
    Подскажите если не затруднит!
    С глубоким уважением!
    Спасибо!
     
    Последнее редактирование: 29 ноя 2016
  10. Igor68

    Igor68 Гуру

    Вот блин дубина!!! Балбес... как есть балбес!!!
    Вот ведь умные люди (AlexU) нормально сказали:
    Я бы весь функционал, отвечающий за взаимодействие с последовательным портом, повесил бы на прерывания -- чтение данных и формирование команд с постановкой в очередь. А основной код программы отвечал бы за чтение команд из очереди и взаимодействие по I2C.
    А я всё воду в ступе толку... да ещё таймер который нельзя... прикрутить вздумал. А serialEvent применить не судьба? Хоть это почти не прерывание... но всё таки.
     
  11. Alex19

    Alex19 Гуру

    Интересный проект.

    Если Вы хотите организовать Modbus RTU, может есть смысл взять готовую библиотеку к примеру эту - https://github.com/smarmengol/Modbus-Master-Slave-for-Arduino/tree/master/examples и допилить ее по желанию. У нее есть небольшие ограничения, адресация до 255 и еще по мелочи. Она поддерживает UART, RS485, сам использую ее, основной плюс простота.

    Так же существуют библиотеки для Raspbery на Си, к примеру эта - https://www.cooking-hacks.com/docum...orial-for-arduino-raspberry-pi-intel-galileo/, с ней не работал.

    Возможно Вам будет проще взять готовую библиотеку и допилить ее по вкусу.
    Сам делаю так:).

    UART там и так на прерывания, отправка и получение. Но если очень хочется самому, можно использовать и свои реализации UART.

    Бегло глянул Multiservo, с ним проблем не должно быть. А вот исходники ComMotion с ходу не нашел.

    Есть несколько Вариантов.
    1. Потоки, сам не пробовал.
    2. Библиотека I2C не использует прерывания, но их можно добавить. К примеру как сделано тут - http://forum.amperka.ru/threads/Библиотека-twi-i2c-ds1307-at24c256-mpu9150-raw-pcf8574-pcf8575-ms5803-30ba.5973/. Но у данного решения есть очевидный минус, нужно будет вникать в pdf и описания модулей. К тому же
    Ну и конечно не плохо бы локализовать проблему., где именно, на каком модуле.
     
    Igor68 нравится это.
  12. Igor68

    Igor68 Гуру

    Проект то он конечно... давно собирался.
    Что касается
    то уже курю! и пилю! Уходить от стандартного Serial не планирую. В "прошивке" очень много мусора. Чищу!
    По поводу
    тут вопросов нет... для линукс устройств уже наделал серверов для работы с Modbus RTU - master. Вот Slave делаю впервые на Arduino.
    По I2C с прерываниями интересно... надо посмотреть. Хотя если бы Multiservo был в состоянии принимать не задание одного регистра для сервомашины, а "блок" заданий для нескольких серв было бы отлично. Правда есть ещё гвоздик в том, что ComMotion производит ещё обмен между своими двумя контроллерами по той же общей I2C, имея на ней два адреса (с матером работает по одному адресу). Так что молчание соблюдать придётся.
    Спасибо что откликнулись! Думал я один тут!
    С глубоким уважением!
     
  13. Alex19

    Alex19 Гуру

    Сам неделю тому сделал тестовый Slave, пока сильно не переписывал, сделал поддержку адресации до unsigned int, перевел на UART, и убрал лишние задержки для RS485. Но несколько костылей осталось и 1 добавил:), спешил, надо идти дальше.

    Вам и не нужно уходить Serial, написан он хорошо.

    Если есть опыт, то Вы можете это исправить получить новый hex и залить, заодно и попробовать поднять скорость работы I2C c 100Khz до 400Khz. Вот его описание - http://forum.amperka.ru/threads/arduino-uno-mutliservo-sheild-от-амперки.3753/#post-31305. Хотя проблем с ним не должно быть Multiservo сделан хорошо.

    Не люблю закрытые коробки.

    Тут нужно читать документацию, как они работают, страница продукта - https://www.sparkfun.com/products/retired/13257, описание - https://cdn.sparkfun.com/datasheets/Robotics/ComMotion-Manual.pdf (его можно перешивать, там описана процедура), код - https://sites.google.com/site/dagup.../ComMotion_Shield_V2_1.zip?attredirects=0&d=1.

    Посмотрите код, все файлы с расширением .ino, ни чего не понял:). Где исходники, hex, которые можно подправить и залить, другими словами черная коробка:(.

    Сама тема OpenCV мне любопытна, но вот добраться до нее могу, все не хватает времени.
     
    Последнее редактирование: 29 ноя 2016
    Igor68 нравится это.
  14. Igor68

    Igor68 Гуру

    В ComMotion точно одни .ino , но я перешивал его - хотел по I2C читать значения тока на приводах и т.п. Перепрошивал через avrdude... правда когда был жив RPi2 с помощью ARPI600. Собирается нормально... когда основной файл с setup() и loop() имеет название директории. Кстати остальные файлы не имеют этих ключевых функций. Потому и воспринимаются как .cpp.
    ComMotion_shield_main.zip
    Вот мой собранный. как говорил хотел читать параметры, а они и так передаются... только в два Serial порта на шилде. И бросил эту идею.
    Про OpenCV ... надо сначала завершить конструкцию... ну начал до данного проекта.
     

    Вложения:

  15. Alex19

    Alex19 Гуру

    Не знал.

    Но там 2 ATmega328P, а ino с setup() и loop() один, выходят они идентичны. Первое и самое простое можно увеличить скорость до 400Khz, тут устанавливаем регистр скорости
    Код (C++):
    if(i2cfreq==0)                                                              // thanks to Nick Gammon: http://gammon.com.au/i2c
      {
        TWBR=72;                                                                  // default I²C clock is 100kHz
      }
      else
      {
        TWBR=12;                                                                  // change the I²C clock to 400kHz
      }
    У Вас установлено i2cfreq в 1, но в функции EEPROMload() он загружается из памяти.

    А ардуина, которая управляет данным модулем при чтении данного модуля использовать TWBR = ((F_CPU / 400000UL) - 16) / 2;, перед чтением Multiservo аналогично TWBR = ((F_CPU / 100000UL) - 16) / 2;

    Так же можно переписать I2C Slave сделать его на прерываниях за основу взять - https://github.com/amperka/Multiservo/blob/master/firmware/atmega48-src/twi-slave.c.

    Но все это нужно делать после того, как Вы уберете все delay в Вашем коде - r1_uno_plus.zip.

    UPD. А у Вас там, различные варианты компиляции, если они используются тестовых, тогда можно оставить. Не сразу увидел.

    UPD 2. Надо отдыхать, а то открыл в блокноте и не увидел очевидного, у Вас там только тесты. Штатный режим, только инициализация моторов, лишний delay(20) и не законченный Modbus RTU на Serial со скоростью 9600.
     
    Последнее редактирование: 30 ноя 2016
    Igor68 нравится это.
  16. Igor68

    Igor68 Гуру

    - Тесты были (уже убрал) для проверки устройств... это было с самого начала. Я говорил, что там много мусора.
    - По поводу скорости I2C... пока не могу сказать... по причине возможного дополнения устройств на шину. особое беспокойство в Multiservo:
    Код (C++):
    /****************************************************************************
      TWI Status/Control register definitions
    ****************************************************************************/


    #define TWI_BUFFER_SIZE 4      // Reserves memory for the drivers transceiver buffer.
                                   // Set this to the largest message size that will be sent including
                                   // address byte.
    всего 4 байта для обмена... может имеет смысл дописать функцию блочного обмена и увеличить буфер. По аналогии с ComMotion доделывал функцию индивидуального управления 4-мя приводами без наличия энкодера. Ранее работали только три хоть тресни. Они забыли или не стали делать.
    - по поводу выбора скорости для ComMotion:
    Код (C++):
    if(i2cfreq==0)                                                              // thanks to Nick Gammon: http://gammon.com.au/i2c
      {
        TWBR=72;                                                                  // default I²C clock is 100kHz
      }
      else
      {
        TWBR=12;                                                                  // change the I²C clock to 400kHz
      }
     
    Выбирается при конфигурации по I2C ведущим. Может они опять что-то забыли... но вот:
    morors_driver_commotion-manual_rus.pdf
    То есть шилд конфигурируемый по I2C и Serial.
    - Взамодействие и "прошивка". В двух контроллерах прошивка идентична. При запуске каждый определяет свой адрес переключателями на плате и по монтажу определяется номер контроллера:
    Код (C++):
      mcu=digitalRead(IDpin);                                                     // low = MCU 1    high = MCU 2
      address=((PIND&B11110000)>>3)+addroffset+mcu;                               // I²C address is selected by dip switches + offset + state of ID pin (MCU1 or MCU2).
      motora=mcu*2;
      motorb=mcu*2+1;
     
    ну согласно схеме:
    SCH.pdf
    С этой платой всё понятно... в Multiservo блочная передача бы! Но похоже там приоритет для формирования длительности на выходах управления сервомашинами. Контроллер то один на всё и больше ничего! Похоже программное формирование по таймеру(прерывание). Иначе стабильности времени импульсов не будет. Проект на На Multiservo уже ранее копал.
    Про таймер вру конечно:
    Код (C++):
    int main(void)
    {
        /*wdt_enable(WDTO_8S);*/

        initNodes();
        initOutputs();
        initTimer();

        // init TWI
        TWI_Slave_Initialise(TWI_ADDRESS << TWI_ADR_BITS);
        TWI_Start_Transceiver();

        // enable interrupts
        sei();

        uint8_t rxMessage[TWI_BUFFER_SIZE];

        for(;;) {
            /*wdt_reset();*/
            if (!TWI_Transceiver_Busy() && TWI_statusReg.RxDataInBuf) {
                TWI_Get_Data_From_Transceiver(rxMessage, TWI_BUFFER_SIZE);
                processMessage(rxMessage);
                TWI_Start_Transceiver();
            }

            if (TIFR1 & _BV(OCF1A)) {
                makePulses();

                // clear the CTC flag (writing a logic one to the set flag clears it)
                TIFR1 = _BV(OCF1A);
            }
        }
    }
     
    Но перезапуск связи тоже в цикле
     

    Вложения:

    Последнее редактирование: 30 ноя 2016
  17. Alex19

    Alex19 Гуру

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

    Гляну вечером, сейчас увы работа.

    Да в I2C скорость задается только у ведущего, Вы говорили, что они общаются между собой. Но в коде нет перехода на ведущего, есть только слейв в setup Wire.begin(address);

    Вы уверены, что они вообще общаются между собой?
    В коде этого не вижу.

    Не очень понимаю, о какой блочной передаче Вы говорите. В Multiservo I2C сделано правильно, через прерывания I2C в буфер. Они взяли AVR311: TWI Slave Implementation - http://www.atmel.com/Images/atmel-2565-using-the-twi-module-as-i2c-slave_applicationnote_avr311.pdf.

    Если у Вас 1 Мастер I2C, Ардуина которая управляет этими шильдами, а остальные слейв. То в принципе проблем не должно быть. Что не мне не понравилось и как бы сделал сам в r1_uno_plus.zip, вначале получение данных с Modbus, затем проверка изменились ли значения dmotor[0], dmotor[1], dmotor[2], dmotor[3] и если хоть одна переменная изменилась запускаем IndividualMotorControl. Каждый раз вызывать IndividualMotorControl, очень дорого и конечно убираем delay.
     
    Igor68 нравится это.
  18. Igor68

    Igor68 Гуру

    - про delay убрал и вызов изменил для Serial:
    Код (C++):
    //
    void serialEvent()
    {
      MRTU_rWORK();
    }
    - про Multisero (Amperka) прерывание:
    Код (C++):
    /**
    * This function is the Interrupt Service Routine (ISR), and called when the TWI interrupt is
    * triggered; that is whenever a TWI event has occurred. This function should not be called
    * directly from the main application.
    * ---------------------------------------------------------------------------------------------- */

    SIGNAL(SIG_2WIRE_SERIAL)
    {
        static unsigned char TWI_bufPtr;
     
        switch (TWSR)
        {
            // Own SLA+R has been received; ACK has been returned
            case TWI_STX_ADR_ACK:
                // Set buffer pointer to first data location
                TWI_bufPtr   = 0;
     
            // Data byte in TWDR has been transmitted; ACK has been received
            case TWI_STX_DATA_ACK:
                TWDR = TWI_buf[TWI_bufPtr++];
             
                // TWI Interface enabled
                // Enable TWI Interupt and clear the flag to send byte
                TWCR = (1<<TWEN) | (1<<TWIE)|(1<<TWINT)| (1<<TWEA)|(0<<TWSTA)|(0<<TWSTO)| (0<<TWWC);
                break;
             
            // Data byte in TWDR has been transmitted; NACK has been received.
            // I.e. this could be the end of the transmission.
            case TWI_STX_DATA_NACK:        
                // Have we transceived all expected data?
                if (TWI_bufPtr == TWI_msgSize)
                {
                    // Set status bits to completed successfully.
                    TWI_statusReg.lastTransOK = true;
                }
                else
                {
                    // Master has sent a NACK before all data where sent.
                    // Store TWI State as errormessage.
                    TWI_state = TWSR;    
                }
             
                // Put TWI Transceiver in passive mode.
                // Enable TWI-interface and release TWI pins
                TWCR = (1<<TWEN)|
                       (0<<TWIE)|(0<<TWINT)|                // Disable Interupt
                       (0<<TWEA)|(0<<TWSTA)|(0<<TWSTO)|     // Do not acknowledge on any new requests.
                       (0<<TWWC);
               break;  
             
            // General call address has been received; ACK has been returned
            case TWI_SRX_GEN_ACK:
                TWI_statusReg.genAddressCall = true;
            // Own SLA+W has been received ACK has been returned
            case TWI_SRX_ADR_ACK:          
                // Dont need to clear TWI_S_statusRegister.generalAddressCall due to that it is the default state.
                TWI_statusReg.RxDataInBuf = true;    
             
                // Set buffer pointer to first data location
                TWI_bufPtr   = 0;
             
                // Reset the TWI Interupt to wait for a new event.
             
                // TWI Interface enabled
                // Enable TWI Interupt and clear the flag to send byte
                // Expect ACK on this transmission
                TWCR = (1<<TWEN)| (1<<TWIE)|(1<<TWINT)| (1<<TWEA)|(0<<TWSTA)|(0<<TWSTO)| (0<<TWWC);                                 //    
            break;
         
        // Previously addressed with own SLA+W; data has been received; ACK has been returned
        // Previously addressed with general call; data has been received; ACK has been returned
        case TWI_SRX_ADR_DATA_ACK:
        case TWI_SRX_GEN_DATA_ACK:
            TWI_buf[TWI_bufPtr++] = TWDR;
            // Set flag transmission successfull.
            TWI_statusReg.lastTransOK = true;
         
            // Reset the TWI Interupt to wait for a new event.
            TWCR = (1<<TWEN)|                          // TWI Interface enabled
                   (1<<TWIE)|(1<<TWINT)|               // Enable TWI Interupt and clear the flag to send byte
                   (1<<TWEA)|(0<<TWSTA)|(0<<TWSTO)|    // Send ACK after next reception
                   (0<<TWWC);
            break;
         
        // A STOP condition or repeated START condition has been received while still addressed as Slave
        case TWI_SRX_STOP_RESTART:  
            // Put TWI Transceiver in passive mode.
            TWCR = (1<<TWEN)|                          // Enable TWI-interface and release TWI pins
                   (0<<TWIE)|(0<<TWINT)|               // Disable Interupt
                   (0<<TWEA)|(0<<TWSTA)|(0<<TWSTO)|    // Do not acknowledge on any new requests.
                   (0<<TWWC);
            break;
         
        // Previously addressed with own SLA+W; data has been received; NOT ACK has been returned
        case TWI_SRX_ADR_DATA_NACK:
        // Previously addressed with general call; data has been received; NOT ACK has been returned
        case TWI_SRX_GEN_DATA_NACK:
        // Last data byte in TWDR has been transmitted (TWEA = �0�); ACK has been received
        case TWI_STX_DATA_ACK_LAST_BYTE:
        // Bus error due to an illegal START or STOP condition
        case TWI_BUS_ERROR:
        default:
            // Store TWI State as errormessage, operation also clears the Success bit.
            //TWI_state = TWSR;    

            // transmit a stop condition. With this, the master gets in a valid state again
            // (because he may not get an bus error)
            TWCR = (1<<TWEN)|                          // Enable TWI-interface and release TWI pins
                   (1<<TWIE)|(1<<TWINT)|               // Disable Interupt
                   (1<<TWEA)|(0<<TWSTA)|(1<<TWSTO)|    // Do not acknowledge on any new requests.
                   (0<<TWWC);

        }
    }
    Потом в цикле (функция main) перезапускаем
    Код (C++):
    void TWI_Start_Transceiver( void )
    {
        // Wait until TWI is ready for next transmission.
        while ( TWI_Transceiver_Busy() );
     
        TWI_statusReg.all = 0;    
        TWI_state         = TWI_NO_STATE ;
        TWCR = (1<<TWEN)|                             // TWI Interface enabled.
               (1<<TWIE)|(1<<TWINT)|                  // Enable TWI Interupt and clear the flag.
               (1<<TWEA)|(0<<TWSTA)|(0<<TWSTO)|       // Prepare to ACK next time the Slave is addressed.
               (0<<TWWC);                             //
    }
     
    - про ComMotion там нет перехода с ведущего на ведомый контроллеры - потому я и не смог сделать команды чтения параметров оттуда. Связь определяется только по битовому параметру ACK соглачно I2C. Одним словом MCU1 получив команду выполняет её сам и передаёт для MCU2 по I2C... в это время мастер (Arduino) должен "молчать" на I2C пока выполняется это (и не только это):
    Код (C++):
    .....................
    .....................
    byte returnaddress=master;                                               // return address is I²C master by default
        if((request&127) && mcu==0) returnaddress=address+1;                     // bit 7 indicates internal request - return to other processor
        if((request&127) && mcu==1) returnaddress=address-1;                     // bit 7 indicates internal request - return to other processor
     
        Wire.beginTransmission(returnaddress);
        Wire.write(sendpack,spsize);
        Wire.endTransmission();
    ...........................
    ...........................
    по команде 3 ComMotion принимает параметры для всех 4-х двигателей. MCU1 принимает все 4 параметра и устанавлиывет PWM для своих двух и передаёт в MCU2 для двух других. Я понял так.
    Потому как выполнение для каждого MCU своё.
     
    Последнее редактирование: 30 ноя 2016
  19. Igor68

    Igor68 Гуру

    Потому как:
    Код (C++):
    void Trigonometry()
    {
      if(configuration==0)                                                                        // 3 Omni wheels
      {
        if(mcu==0)
        {
          mspeed[0]=int(sin((angle+120.0)*radconvert)*float(velocity))+rotation;                  // calculate desired speed
          mspeed[1]=int(sin((angle)*radconvert)*float(velocity))+rotation;                        // calculate desired speed
        }
        else
        {
          mspeed[2]=int(sin((angle-120.0)*radconvert)*float(velocity))+rotation;                  // calculate desired speed
          mspeed[3]=0;                                                                            // 4th motor not used
        }
        return;
      }

      if(configuration==1)                                                                        // 4 Omni wheels
      {
        if(mcu==0)
        {
          mspeed[0]=int(sin((angle)*radconvert)*velocity)+rotation;                               // calculate desired speed
          mspeed[1]=int(sin((angle+90)*radconvert)*velocity)+rotation;                            // calculate desired speed
        }
        else
        {
          mspeed[2]=int(sin((angle+180)*radconvert)*velocity)+rotation;                           // calculate desired speed
          mspeed[3]=int(sin((angle+270)*radconvert)*velocity)+rotation;                           // calculate desired speed
        }
        return;
      }

      if(configuration==2)                                                                        // Mecanum Wheels
      {
        if(mcu==0)
        {
          mspeed[0]=int((-sin(angle*radconvert)+cos(angle*radconvert))*float(velocity))+rotation; // calculate desired speed
          mspeed[1]=int((+sin(angle*radconvert)+cos(angle*radconvert))*float(velocity))+rotation; // calculate desired speed
        }
        else
        {
          mspeed[2]=int((-sin(angle*radconvert)+cos(angle*radconvert))*float(velocity))-rotation; // calculate desired speed
          mspeed[3]=int((+sin(angle*radconvert)+cos(angle*radconvert))*float(velocity))-rotation; // calculate desired speed
        }
        return;
      }
    }
     
    выполняется для каждого MCU индивидуально. Ардуино обращается только к одному адресу... согласно установкам. Кстати сканирование I2C несколько раз подряд приводит к тому, что ComMotion начинает откликаться на все адреса и надо делать после этого ресет. По всей вероятности сканирование (перебор) адресов без интервала для обмена по I2C между MCU1 и MCU2
     
    Последнее редактирование: 30 ноя 2016
  20. Igor68

    Igor68 Гуру

    Вот убрал мусор, изменил немного:
    r1_uno_plus.zip
    Конечно быстрее стало. Но команду 16 пока не делал. Устойчивость при длительной работе... пока нормально.
     

    Вложения:

    • r1_uno_plus.zip
      Размер файла:
      13,9 КБ
      Просмотров:
      136