Подтолкните в нужном направлении

Тема в разделе "Глядите, что я сделал", создана пользователем никто, 28 апр 2013.

  1. никто

    никто Нерд

    Нужна помощь.

    Я начинающий ардуинщик. Пока читаю, смотрю примеры, пробую писать небольшие скетчи.

    Уже наметил небольшой проект.

    Имеем:

    Arduino UNO

    Motor Shield

    4WD RoboPlatform

    + мелочевка (диоды, резисторы, аккумуляторы, конденсаторы ......)


    Написал 2 простые программы, первая заставляет робота ездить(движение вперед - 5сек, остановка, поворот, движение вперед - 3 сек, поворот), а вторая считывает значение с фоторезистора и, если мало света, плавно зажигает 2 светодиода (аля фары у машины).

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

    Писать за меня программу не прошу, ткните где посмотреть.

    Скетчи соответственно такие:
    1) двидение
    Код (Text):
    #define SPEED_LEFT  6
    #define SPEED_RIGHT 5
    #define DIR_LEFT    7
    #define DIR_RIGHT  4
     
    void setup()
    {
      pinMode(4, OUTPUT);
      pinMode(5, OUTPUT);
      pinMode(6, OUTPUT);
      pinMode(7, OUTPUT);
    }
     
    void loop()
    {
      delay(3000);
      analogWrite(SPEED_LEFT, 150);
      analogWrite(SPEED_RIGHT, 150);
      digitalWrite(DIR_LEFT, HIGH);
      digitalWrite(DIR_RIGHT, HIGH);
      delay(5000);
     
      analogWrite(SPEED_LEFT, 0);
      analogWrite(SPEED_RIGHT, 0);
     
      delay(500);
     
      analogWrite(SPEED_LEFT, 255);
      analogWrite(SPEED_RIGHT, 255);
      digitalWrite(DIR_LEFT, LOW);
      digitalWrite(DIR_RIGHT, HIGH);
      delay(1500);
     
      analogWrite(SPEED_LEFT, 0);
      analogWrite(SPEED_RIGHT, 0);
     
      delay(500);
     
      analogWrite(SPEED_LEFT, 150);
      analogWrite(SPEED_RIGHT, 150);
      digitalWrite(DIR_LEFT, HIGH);
      digitalWrite(DIR_RIGHT, HIGH);
      delay(1500);
     
      analogWrite(SPEED_LEFT, 0);
      analogWrite(SPEED_RIGHT, 0);
     
      delay(500);
     
      analogWrite(SPEED_LEFT, 255);
      analogWrite(SPEED_RIGHT, 255);
      digitalWrite(DIR_LEFT, LOW);
      digitalWrite(DIR_RIGHT, HIGH);
      delay(1500);
     
      analogWrite(SPEED_LEFT, 0);
      analogWrite(SPEED_RIGHT, 0);
     
    }
    2) розжиг светодиодов

    Код (Text):
    #define LED1 9  // 1й светодиод на 9м пине
    #define LED2 10 // 2й светодиод на 10м пине
    int val = 0;    // переменная для хранения значений с сенсора
    int i = 0;    // переменная для разгарания
     
    void setup()
    {
      pinMode(LED1, OUTPUT); // говорим, что 9й пин это выход
      pinMode(LED2, OUTPUT); // говорим, что 10й пин это выход
      pinMode(2, INPUT);    // говорим, что 2ой пин это вход (он же кнопка)
    }
     
    void loop()
    {
      val = digitalRead(2); // считываем значение с датчика
     
      if (val < 1) // если значение меньше единицы(т.е.нет света) и диод не горел
        {
        i++;              // для разгорания прибавляем по единице с каждым циклом
        i = min(255, i);  // не даем i быть больше 255
        analogWrite(LED1, i); // диод 1 должен светить с яркостью i
        analogWrite(LED2, i); // диод 2 должен светить с яркостью i
        delay(20);
        }
       
        else // выполнять если ни одно из условий не выполняется
        {
        analogWrite(LED1, 0); // гасим диод 1
        analogWrite(LED2, 0); // гасим диод 2
        i = 0;              // ОБЯЗАТЕЛЬНО приравниваем к нулю иначе не будет разгараться в следующем цикле
        delay(10);
        }
    }
     
    nailxx, roggedhorse, Megakoteyka и ещё 1-му нравится это.
  2. pasha08

    pasha08 Нерд

    Либо считывать пока стоит либо прерывания
     
  3. никто

    никто Нерд

    Считывать пока стоит не вариант т.к. пока он двигается 5 секунд, свет может пагаснуть несколько раз, а диоды так и не загорятся.
    А что есть "прерывания"?
     
  4. Unixon

    Unixon Оракул Модератор

    Слава роботам! Хоть кто-то начал писать аккуратный код... :)
     
    никто нравится это.
  5. pasha08

    pasha08 Нерд

    Ну, если вкратце, то дело так. Какая нибудь длинная программа работает. При каком то условии выполняется действие и программа после его окончания возвращается на то место где закончила. Для прерываний есть специальная функция
    attachInterrupt. Подробнее про функцию — http://arduino.ru/Reference/AttachInterrupt .
     
    никто нравится это.
  6. никто

    никто Нерд

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

    никто Нерд

    Ознакомился. Насколько я понял в этой функции нельзя использовать delay, да и мне нужна цикличность.
     
  8. roggedhorse

    roggedhorse Гик

    На мой взгляд использование delay() допустимо на этапе изучения сабжа.
    Вы продвинулись уже достаточно далеко хотя бы потому, что ваш робот уже ездит.
    Посему предлагаю исключить delay() из вашего кода.

    Исходя из кода, описанного в loop "Движения" в движении вашего робота сейчас есть некая жестко запрограммированная цикличность. Это не гибко. Предлагаю помимо исключения delay() исключить также хардкод. Ибо движение - есть динамика. А хардкод и delay - суть статика.



    Набросал то же самое, что у вас только с возможностью параллелить все и более-менее гибко описывать программу действий:
    Код (Text):

    #define SPEED_LEFT  6
    #define SPEED_RIGHT 5
    #define DIR_LEFT    7
    #define DIR_RIGHT  4
     
    // шаблон команды для одного исполнительного устройства
    struct TDrive {
      int16_t speed;
      int16_t direction;
      int16_t duration;
    };
     
    // описание действия для всех исполнительных устройств
    struct TAction {
      TDrive left;
      TDrive right;
    };
     
    // тип НАПРАВЛЕНИЕ
    enum EDirections {
      FORWARD = HIGH,
      BACKWARD = LOW
    };
     
    // предопределенная программа действий
    TAction actions[] = {
      {{150, FORWARD, 5000}, {150, FORWARD, 5000}},
      {{0, FORWARD, 500},   {0, FORWARD, 500}},
      {{255, BACKWARD, 1500},{255, FORWARD, 1500}},
      {{0, FORWARD, 500},   {0, FORWARD, 500}},
      {{150, FORWARD, 1500}, {150, FORWARD, 1500}},
      {{0, FORWARD, 500},   {0, FORWARD, 500}},
      {{255, BACKWARD, 1500},{255, FORWARD, 1500}},
      {{0, FORWARD, 500},   {0, FORWARD, 500}}
    };
     
    // всего предопределено команд
    int16_t totalActions = sizeof(actions) / (sizeof(TAction));
    // текущая команда
    int16_t currentAction = 0;// totalActions - 1;
     
    // количество каналов управления
    #define TOTAL_DRIVE_CHANNELS sizeof(TAction) / sizeof(TDrive)
    // канальный счетчик
    uint32_t nextActionAt[TOTAL_DRIVE_CHANNELS];
     
     
    void setup() {
      pinMode(4, OUTPUT);
      pinMode(5, OUTPUT);
      pinMode(6, OUTPUT);
      pinMode(7, OUTPUT);
      // инициализируем канальные счетчики
      memset(nextActionAt, 0, sizeof(nextActionAt));
      pinMode(13, OUTPUT);
      Serial.begin(9600);
      Serial.print("Total movements count:");Serial.println(totalActions);
      Serial.print("Total drive channels :");Serial.println(TOTAL_DRIVE_CHANNELS);
    }
     
     
    void driveControl() {
      // текущее время в миллисек
      uint32_t currentTime = millis();
      // текущее действие
      TDrive *currentDrive = (TDrive *) &actions[currentAction];
      // счетчик каналов, изменивших свое состояние
      // если все каналы изменили свое состояние - переключаемся на следующую команду (действие)
      int16_t channelsDone = 0;
     
      // пробежимся по каналам, может какой-то пришло время включить/выключить
      for (int i = 0; i < TOTAL_DRIVE_CHANNELS; i++) {
        if (currentTime > nextActionAt[i]) {
          // выбрать канал
          nextActionAt[i] += currentDrive->duration;
          Serial.print("\t");
          Serial.print(currentTime);
          Serial.print(":: drive#");Serial.print(i);
          if (currentDrive->direction == FORWARD) Serial.print(" forward"); else Serial.print(" backward");
          Serial.print(" with speed ");Serial.println(currentDrive->speed);
     
          analogWrite(SPEED_LEFT, currentDrive->speed);
          analogWrite(SPEED_RIGHT, currentDrive->speed);
          digitalWrite(DIR_LEFT, currentDrive->direction);
          digitalWrite(DIR_RIGHT, currentDrive->direction);
     
          channelsDone++;
     
        }
        // выбираем следующий привод для контроля состояния
        currentDrive++;
      }
      if (channelsDone == TOTAL_DRIVE_CHANNELS) {
        currentAction++;
        if (currentAction == totalActions) {
          currentAction = 0;
        }
        Serial.print("step to action ID#");Serial.println(currentAction);
      }
     
    }
     
    void loop() {
      driveControl();
     
    /*
     
      тут пишем код измерения фоторезистора и розжига :) светодиодов
     
    */
    }

    Программа задается в массиве произвольного (условно произвольного) размера:
    Код (Text):
    TAction actions[] = {
      {{150, FORWARD, 5000}, {150, FORWARD, 5000}},
    ....
    Где левая половинка каждой строки описывает действия для одного привода, правая - для второго:
    Код (Text):
     {{left.speed, left.direction, left.duration}, {right.speed, right.direction, right.duration}}

    delay() не используем

    Посмотрите на вывод в терминал - скорее всего все работает также точно, как и в вашем скетче, только можно параллельно выполнять другие вычисления и не использовать Interrupts (что по-моему не очень правильно). Но для начала вполне пойдет


    Пример вывода:


    где первое число - текущее время
     
    Megakoteyka и никто нравится это.
  9. никто

    никто Нерд

    После первого прочтения кода на лице нарисовалась улыбка от непонимания происходящего))
    Читаю уже 3й раз, что-то прояснилось.
    Вобщем большое спасибо, на майские праздники Вы мне задание определили.
    Буду вникать.
     
  10. roggedhorse

    roggedhorse Гик

    Спрашивайте, если что-то будет сооооовсем оочень не понятно :)
     
  11. roggedhorse

    roggedhorse Гик

    Упс.
    Нашел ошибку в своем коде (следствие копипаста) :(

    Код (Text):
      analogWrite(SPEED_LEFT, currentDrive->speed);
      analogWrite(SPEED_RIGHT, currentDrive->speed);
      digitalWrite(DIR_LEFT, currentDrive->direction);
      digitalWrite(DIR_RIGHT, currentDrive->direction);
     
    Завтра исправлю
     
  12. никто

    никто Нерд

    Ломаю голову.
    расписал код как мог. Много не понял, в чем-то сомневаюсь.
    Так ли я понимаю код?
    Код (Text):

    #define SPEED_LEFT  6 // 6й пин выдает скорость для левых двигателей
    #define SPEED_RIGHT 5 // 5й пин выдает скорость для правых двигателей
    #define DIR_LEFT  7 // 7й пин задает направление вращения для левых двигателей
    #define DIR_RIGHT  4 // 4й пин задает направление вращения для правых двигателей
     
    // шаблон команды для одного исполнительного устройства
    // почему int16, а не byte для speed и direction?
    struct TDrive {
      int16_t speed;      // отводим 16 бит под значение скорости
      int16_t direction;  // отводим 16 бит под значение направления движения
      int16_t duration;  // отводим 16 бит под значение времени движения
    };
     
    // описание действия для всех исполнительных устройств
    struct TAction {
      TDrive left;  // задаем исполнительное действие для левых двигателей
      TDrive right; // задаем исполнительное действие для правых двигателей
    };
     
    // тип НАПРАВЛЕНИЕ
    // А разве без этой функции ардуино не поемет что FORWARD = HIGH, а BACKWARD = LOW
    enum EDirections {
      FORWARD = HIGH,
      BACKWARD = LOW
    };
     
    // предопределенная программа действий
    // итого 8 actions (8 действий совершит робот)
    TAction actions[] = {
      {{150, FORWARD, 5000}, {150, FORWARD, 5000}}, // actions #1 движение вперед со "скоростью" 150 в течении 5 секунд
      {{0, FORWARD, 500},   {0, FORWARD, 500}}, // actions #2 0,5 секунды стоим на месте
      {{255, BACKWARD, 1500},{255, FORWARD, 1500}}, // actions #3 левые двигатели назад со скоростью 255 в течении 1,5 секунд, правые так же, но в обратном направлении
      {{0, FORWARD, 500},   {0, FORWARD, 500}}, // actions #4 0,5 секунды стоим на месте
      {{150, FORWARD, 1500}, {150, FORWARD, 1500}}, // actions #5 левые двигатели назад со скоростью 255 в течении 1,5 секунд, правые так же, но в обратном направлении
      {{0, FORWARD, 500},   {0, FORWARD, 500}}, // actions #6 0,5 секунды стоим на месте
      {{255, BACKWARD, 1500},{255, FORWARD, 1500}}, // actions #7 левые двигатели назад со скоростью 255 в течении 1,5 секунд, правые так же, но в обратном направлении
      {{0, FORWARD, 500},   {0, FORWARD, 500}}    // actions #8 0,5 секунды стоим на месте
    };
     
    // всего предопределено команд
    int16_t totalActions = sizeof(actions) / (sizeof(TAction)); // не осилил(
    // текущая команда
    int16_t currentAction = 0;// totalActions - 1;  // ??
     
    // количество каналов управления
    #define TOTAL_DRIVE_CHANNELS sizeof(TAction) / sizeof(TDrive)  // так же не осилил (не понимаю что выдает sizeof) поидее должен выдавать значение 2, но почему??
    // канальный счетчик
    uint32_t nextActionAt[TOTAL_DRIVE_CHANNELS];  // аналогично ??
     
     
    void setup() {
      pinMode(4, OUTPUT);  // 4-й пин - выход
      pinMode(5, OUTPUT);  // 5-й пин - выход
      pinMode(6, OUTPUT);  // 6-й пин - выход
      pinMode(7, OUTPUT);  // 7-й пин - выход
      // инициализируем канальные счетчики
      memset(nextActionAt, 0, sizeof(nextActionAt));  // ??
      pinMode(13, OUTPUT);  // 13-й пин - выход
      Serial.begin(9600);  // соединение с портом
      Serial.print("Total movements count:");Serial.println(totalActions);        // вывод в терминал того, что получится в totalActions
      Serial.print("Total drive channels :");Serial.println(TOTAL_DRIVE_CHANNELS);  // вывод в терминал того, что получится в TOTAL_DRIVE_CHANNELS
    }
     
    // функция выполнения движения
    void driveControl() {
      // текущее время в миллисек
      uint32_t currentTime = millis();  // счетчик времени
      // текущее действие
      TDrive *currentDrive = (TDrive *) &actions[currentAction];  // вроде и понятно, но свести не могу
      // счетчик каналов, изменивших свое состояние
      // если все каналы изменили свое состояние - переключаемся на следующую команду (действие)
      int16_t channelsDone = 0;
     
      // пробежимся по каналам, может какой-то пришло время включить/выключить
      for (int i = 0; i < TOTAL_DRIVE_CHANNELS; i++) {  // для i от 0 до 2 выполнять следующие действия????
        if (currentTime > nextActionAt[i]) {        // ??
          // выбрать канал
          nextActionAt[i] += currentDrive->duration;  // не понимаю что есть "->"
          Serial.print("\t");
          Serial.print(currentTime);
          Serial.print(":: drive#");Serial.print(i);
          if (currentDrive->direction == FORWARD) Serial.print(" forward"); else Serial.print(" backward");
          Serial.print(" with speed ");Serial.println(currentDrive->speed);
     
          analogWrite(SPEED_LEFT, currentDrive->speed); // на 6й пин выдаем значение скорости
          analogWrite(SPEED_RIGHT, currentDrive->speed);    // на 5й пин выдаем значение скорости
          digitalWrite(DIR_LEFT, currentDrive->direction);  // на 7й пин выдаем значение направления
          digitalWrite(DIR_RIGHT, currentDrive->direction); // на 4й пин выдаем значение направления
     
          channelsDone++;  // каждый раз прибавляем к channelsDon по единичке
     
        }
        // выбираем следующий привод для контроля состояния
        currentDrive++; // ??
      }
      if (channelsDone == TOTAL_DRIVE_CHANNELS) {  // когда значение channelsDone достигло TOTAL_DRIVE_CHANNELS т.е. 2
        currentAction++;                      // увеличиваем на 1 currentAction
        if (currentAction == totalActions) {  // если currentAction достигнет totalActions т.е. 8
          currentAction = 0;                  // приравнять currentAction к 0, чтобы выполнять движение заново
        }
        Serial.print("step to action ID#");Serial.println(currentAction);  // выводим в терминал намер текущего действия
      }
     
    }
     
    void loop() {
      driveControl(); // зацикливаем выполнение функции driveControl
     
    /*
     
      тут пишем код измерения фоторезистора и розжига :) светодиодов
     
    */
    }
    Немного решил изменить задачи своего робота и купил серву + ультразвуковой дальномер, но понял что туда лезть мне пока рановато. Эксперементирую потихоньку с сервой. Может потом дорасту.
     
  13. Unixon

    Unixon Оракул Модератор

    Компилятор то всё поймет, а вот потенциальный читатель этого кода будет расцарапывать затылок, пытаясь понять, что это за направления движения такие "HIGH" и "LOW". Вот "FORWARD" и "BACKWARD" - это понятно, что "вперед" и "назад". А вот "HIGH" это куда, в космос? А с помощью "LOW" тогда огороды видимо копать нужно ну или нефть добывать... :)
     
    никто и roggedhorse нравится это.
  14. Unixon

    Unixon Оракул Модератор

    Если разделить длину электички на длину одного вагона можно и количество вагонов сосчитать.
    sizeof() возвращает размер аргумента в байтах.

    Решаем откуда начинать представление: 0 - первый номер, totalActions-1 последний.
     
    никто и roggedhorse нравится это.
  15. Unixon

    Unixon Оракул Модератор

    Зубрим содержимое libc. Стандартные функции неплохо бы знать, хотябы самые-самые.
    Тут тупо забиваем нулями весь nextActionAt.
     
    никто и roggedhorse нравится это.
  16. Unixon

    Unixon Оракул Модератор

    А это чтобы здесь delay() не ночевал.
     
    никто и roggedhorse нравится это.
  17. Unixon

    Unixon Оракул Модератор

    Снова открываем учебник по "С" и читаем про указатели.
     
    никто и roggedhorse нравится это.
  18. никто

    никто Нерд

    Спасибо!
    Видимо стоит всетаки углубиться в изучение С.
    Посоветуйте какого автора почитать новичку? Но чтобы совсем с нуля.
    На работе, в библиотеке, нашел стааааарую книгу. Для меня очень сложно написана.