Нужна помощь. Я начинающий ардуинщик. Пока читаю, смотрю примеры, пробую писать небольшие скетчи. Уже наметил небольшой проект. Имеем: 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); } }
Считывать пока стоит не вариант т.к. пока он двигается 5 секунд, свет может пагаснуть несколько раз, а диоды так и не загорятся. А что есть "прерывания"?
Ну, если вкратце, то дело так. Какая нибудь длинная программа работает. При каком то условии выполняется действие и программа после его окончания возвращается на то место где закончила. Для прерываний есть специальная функция attachInterrupt. Подробнее про функцию — http://arduino.ru/Reference/AttachInterrupt .
На мой взгляд использование 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 (что по-моему не очень правильно). Но для начала вполне пойдет Пример вывода: где первое число - текущее время
После первого прочтения кода на лице нарисовалась улыбка от непонимания происходящего)) Читаю уже 3й раз, что-то прояснилось. Вобщем большое спасибо, на майские праздники Вы мне задание определили. Буду вникать.
Упс. Нашел ошибку в своем коде (следствие копипаста) Код (Text): analogWrite(SPEED_LEFT, currentDrive->speed); analogWrite(SPEED_RIGHT, currentDrive->speed); digitalWrite(DIR_LEFT, currentDrive->direction); digitalWrite(DIR_RIGHT, currentDrive->direction); Завтра исправлю
Ломаю голову. расписал код как мог. Много не понял, в чем-то сомневаюсь. Так ли я понимаю код? Код (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 /* тут пишем код измерения фоторезистора и розжига :) светодиодов */ } Немного решил изменить задачи своего робота и купил серву + ультразвуковой дальномер, но понял что туда лезть мне пока рановато. Эксперементирую потихоньку с сервой. Может потом дорасту.
Компилятор то всё поймет, а вот потенциальный читатель этого кода будет расцарапывать затылок, пытаясь понять, что это за направления движения такие "HIGH" и "LOW". Вот "FORWARD" и "BACKWARD" - это понятно, что "вперед" и "назад". А вот "HIGH" это куда, в космос? А с помощью "LOW" тогда огороды видимо копать нужно ну или нефть добывать...
Если разделить длину электички на длину одного вагона можно и количество вагонов сосчитать. sizeof() возвращает размер аргумента в байтах. Решаем откуда начинать представление: 0 - первый номер, totalActions-1 последний.
Зубрим содержимое libc. Стандартные функции неплохо бы знать, хотябы самые-самые. Тут тупо забиваем нулями весь nextActionAt.
Спасибо! Видимо стоит всетаки углубиться в изучение С. Посоветуйте какого автора почитать новичку? Но чтобы совсем с нуля. На работе, в библиотеке, нашел стааааарую книгу. Для меня очень сложно написана.