Раздельное управление двумя устройствами с одной платы

Darwetra
Offline
Зарегистрирован: 18.04.2016

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

Вот написал такое:

//2 светодиода плавно зажигаются и затухают

const int RED=9;
const int GR=10;

void setup()
{
  pinMode (RED, OUTPUT);
  pinMode (GR, OUTPUT);
}

void loop()

{
  
for (int i=0; i<256; i=i+1)
  {
  analogWrite(RED, i);
  delay(5);
}

for (int i=255; i>=0; i=i-1)
{
  analogWrite(RED, i);
  delay(20);
  }

for (int j=0; j<256; j=j+1)
  {
  analogWrite(GR, j);
  delay(15);
}

for (int j=255; j>=0; j=j-1)
{
  analogWrite(GR, j);
  delay(5);
  }

}

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

И да, я чайник.

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

Ну, понятное дело, они ждут друг друга в Ваших delay'ах. Избавьтесь от delay и всё будет.

Tomasina
Tomasina аватар
Offline
Зарегистрирован: 09.03.2013
Arhat109-2
Offline
Зарегистрирован: 24.09.2015

А ещё лучше, вместо организации классов с сохранением номера пина использовать макрокоманды для обозначения ножек и everyMillis() для повышения прозрачности. :)

James
Offline
Зарегистрирован: 26.02.2016

Arhat109-2 пишет:

А ещё лучше, вместо организации классов с сохранением номера пина использовать макрокоманды для обозначения ножек и everyMillis() для повышения прозрачности. :)

А еще лучше сразу arhat.h?:))) 

faeton
faeton аватар
Offline
Зарегистрирован: 21.03.2016

Darwetra пишет:

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

Вот написал такое:

//2 светодиода плавно зажигаются и затухают

const int RED=9;
const int GR=10;

void setup()
{
  pinMode (RED, OUTPUT);
  pinMode (GR, OUTPUT);
}

void loop()

{
  
for (int i=0; i<256; i=i+1)
  {
  analogWrite(RED, i);
  delay(5);
}

for (int i=255; i>=0; i=i-1)
{
  analogWrite(RED, i);
  delay(20);
  }

for (int j=0; j<256; j=j+1)
  {
  analogWrite(GR, j);
  delay(15);
}

for (int j=255; j>=0; j=j-1)
{
  analogWrite(GR, j);
  delay(5);
  }

}

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

И да, я чайник.

Все были чайниками - не расстраивайтесь :) 

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

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

1. Запоминаем текущее время в переменной в начале цикла.
2. Внутри цикла проверяем прирост текущего времени, вычитая из самого текущего времени значение, запомненное в п.1. Если это изменение уже дошло до нужного значения, выполняем некоторое быстрое действие, не останавливающее выполнение цикла (включаем светодиод или выключаем), далее запоминаем в переменной п.1 новое время. Если же ещё не натикало сколько надо, ничего не делам. И такую проверку условия ставим на все светодиоды.

Есть готовые решение для этого, но несложно и самостоятельно реализовать. :)

 

faeton
faeton аватар
Offline
Зарегистрирован: 21.03.2016

Кстати, объявлять ноги переменными вовсе не обязательно и даже не желательно, ибо для доступа к этим переменным требуются ресурсы процессора. Вместо "const int red=9;" намного более эффективно написать "#define red 9" тогда компилятор при использовании red не обращение к переменной будет писать процессору, а подставит числовую константу, что будет эффективнее.

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

faeton пишет:

Вместо "const int red=9;" намного более эффективно написать "#define red 9" 

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

Tomasina
Tomasina аватар
Offline
Зарегистрирован: 09.03.2013

вы удивитесь, но "const int red=9;" и "#define red 9" на выходе компилятора дадут абсолютно одинаковый результат: "9". Они поумнели за последнее время :)

А вот если написать "int red=9;", то тогда да, разница будет существенной.

Mr.Privet
Mr.Privet аватар
Offline
Зарегистрирован: 17.11.2015

как то так

//2 светодиода плавно зажигаются и затухают

const int RED=9;
const int GR=10;
unsigned long red_time=0;
unsigned long gr_time=0;
bool rising_red=1;
bool rising_gr=1;
unsigned long time=0;
byte i=0;
byte j=0;
void setup()
{
  pinMode (RED, OUTPUT);
  pinMode (GR, OUTPUT);
  Serial.begin(9600);
}

void loop()

{
  if(millis()-red_time>=5&&rising_red)
  {
    i=i+1;
    if (i==255)rising_red=!rising_red;
    red_time=millis();
  }
  if(millis()-red_time>=20&&!rising_red)
  {
    i=i-1;
    if (i==0)rising_red=!rising_red;
    red_time=millis();
  }
  if(millis()-gr_time>=20&&rising_gr)
  {
    j=j+1;
    if (j==255)rising_gr=!rising_gr;
    gr_time=millis();
  }    
  if(millis()-gr_time>=5&&!rising_gr)
  {
    j=j-1;
    if (j==0)rising_gr=!rising_gr;
    gr_time=millis();
  }    
analogWrite(RED, i);
analogWrite(GR, j);
  delay(5);
}

в конце задержка в 5 потому что у нас все кратное 5, можно убрать)

faeton
faeton аватар
Offline
Зарегистрирован: 21.03.2016

Tomasina пишет:

вы удивитесь, но "const int red=9;" и "#define red 9" на выходе компилятора дадут абсолютно одинаковый результат: "9". Они поумнели за последнее время :)

А вот если написать "int red=9;", то тогда да, разница будет существенной.

Согласен. :)

Darwetra
Offline
Зарегистрирован: 18.04.2016

Спасибо за подробные разъяснения, у меня тоже были сомнения в многозадачности процессора ардуино, но я почему-то считал, что эта проблема как-то аппаратно в ардуино обходится. Теперь понял, что решать надо программно.

Сейчас экспериментирую на светодиодах, но в "программе-максимум" у меня изготовление копировального станочка с ЧПУ.

faeton
faeton аватар
Offline
Зарегистрирован: 21.03.2016

Darwetra пишет:

Спасибо за подробные разъяснения, у меня тоже были сомнения в многозадачности процессора ардуино, но я почему-то считал, что эта проблема как-то аппаратно в ардуино обходится. Теперь понял, что решать надо программно.

Сейчас экспериментирую на светодиодах, но в "программе-максимум" у меня изготовление копировального станочка с ЧПУ.

У процессоров нет многозадачности, есть лишь средства её реализации. :) А в копировальном станке, я думаю, не потребуется многозадачность - линейная "черепашья графика". :) Одновременное движение по нескольким осям не требует многозадачности и даже наоброт - требует последовательного исполнения, обеспечивающего синхронноть.

Darwetra
Offline
Зарегистрирован: 18.04.2016

faeton пишет:

У процессоров нет многозадачности, есть лишь средства её реализации.

Точно, вспомнил, процессор все обрабатывает последовательно, а многозадачность поддерживает (или не поддерживает) ОС.

faeton пишет:
Одновременное движение по нескольким осям не требует многозадачности и даже наоброт - требует последовательного исполнения, обеспечивающего синхронноть.

Да, ваша правда, иначе согласовать движение по осям будет проблемой. Где бы про это можно посмотреть, хотя бы по каким словам искать информацию?

Darwetra
Offline
Зарегистрирован: 18.04.2016

Пока что сделал вот так:

const int RED =  9;
const int GR =10;
// номер выхода, подключенного к светодиоду
// Variables will change:
int ledState = LOW;             // этой переменной устанавливаем состояние светодиода
long previousMillis = 0;        // храним время последнего переключения светодиода
 
long interval = 1000;           // интервал между включение/выключением светодиода (1 секунда)
 
void setup() {
  // задаем режим выхода для порта, подключенного к светодиоду
  pinMode(RED, OUTPUT);  
  pinMode(GR, OUTPUT);  
}
 
void loop()
{
  // здесь будет код, который будет работать постоянно
  // и который не должен останавливаться на время между переключениями свето
  unsigned long currentMillis = millis();
  
  //проверяем не прошел ли нужный интервал, если прошел то
  if(currentMillis - previousMillis > interval) {
    // сохраняем время последнего переключения
    previousMillis = currentMillis; 
 
    // если светодиод не горит, то зажигаем, и наоборот
    if (ledState == LOW)
      ledState = HIGH;
    else
      ledState = LOW;
 
    // устанавливаем состояния выхода, чтобы включить или выключить светодиод
    digitalWrite(RED, ledState);
   // digitalWrite(GR, ledState);
  }
  for (int j=0; j<256; j=j+1) //зажигает
  {
  analogWrite(GR, j);
  delay(15);
}
 
for (int j=255; j>=0; j=j-1) //тухнет
{
  analogWrite(GR, j);
  delay(5);
  }
}

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

 

Tomasina
Tomasina аватар
Offline
Зарегистрирован: 09.03.2013

в посте #2 было приведено решение. Надо только вникнуть ;)

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Я бы написал примерно так:


#include "arhat.h" // подключаем everyMillis() и заодно заменяем тяжелые вызовы на однокомандный ногодрыг :)

#define RED 9
#define GR 10
// номер выхода, подключенного к светодиоду
// Variables will change:
int ledState = LOW;             // этой переменной устанавливаем состояние светодиода
long interval = 1000;           // интервал между включение/выключением светодиода (1 секунда)
 
void setup() {
  // задаем режим выхода для порта, подключенного к светодиоду
  pinModeOut(RED);  
  pinModeOut(GR);
  pwmSet(GR);
}

uint8_t wait=15, where=0;
int       j=0;

void loop()
{
  // здесь будет код, который будет работать постоянно
  // и который не должен останавливаться на время между переключениями свето
  
  //проверяем не прошел ли нужный интервал, если прошел то
  everyMillis(interval,
  {
    // если светодиод не горит, то зажигаем, и наоборот
    ledState = (ledState == LOW? HIGH : LOW);
 
    // устанавливаем состояния выхода, чтобы включить или выключить светодиод
    digitalWrite(RED, ledState);
    // pinOut(RED, ledState); // я бы использовал это, но можно как и у вас
  });
  everyMillis(wait,
  {
    if( where==0 ) { if( ++j == 255 ) {where==1; wait=5;} } // потом тухнет
    else           { if( --j == 0   ) {where==0; wait=15;} } // и снова зажигает

    analogWrite(GR, j);  //зажигает или тухнет
    // pwmOut(GR, j); // замещающий аналог: единая мнемоника и явно связанная с ШИМ
 });
}

В этом случае, у вас и зажигание и тухление светодиода идет неблокирующим действием и начальный код будет исполняться И в процессе изменения яркости тоже. А у вас, пока он не загорится waitAll = 256*15 мсек = 3.84 секунды а потом пока не погаснет ещё 255*5 = 1.28сек, итого 3,84+1,28=5,12сек .. весь код "стоит колом" и МК всё это время основные команды в loop() не отрабатывает.

Скомпилял. Скетч 882 байта. Но, номера пинов надо всеж таки определять через #define

Заодно нашел ошибку в пересчетах, поправил и даже упростилось. :)

Darwetra
Offline
Зарегистрирован: 18.04.2016

faeton пишет:

Кстати, объявлять ноги переменными вовсе не обязательно и даже не желательно, ибо для доступа к этим переменным требуются ресурсы процессора. Вместо "const int red=9;" намного более эффективно написать "#define red 9" тогда компилятор при использовании red не обращение к переменной будет писать процессору, а подставит числовую константу, что будет эффективнее.

Немного уточнить хочу, ибо мои знания о программировании идут из глубины веков.))) И это так, потому как программировал последний раз на языке PL-1 в восьмидесятых годах последнего столетия прошлого тысячелетия. Поэтому для понимания некоторых вещей мне требуется отдельное объяснение, хотя всем кажется, что это само собой разумеющиеся.

Поэтому скажите, правильно ли я понимаю, что функция "const int red=9; выполняет такие действия"

- объявляет тип данных как константу,

- константу целочисленную,

- задает ее имя,

- имя текстовое,

- выделяет под ее размещение 2 байта в регистрах памяти,

- ставит ее значение равным 9.

Не пойму только, как компилятор понимает, что это не просто число 9 под именем red, а именно девятый вывод на плате?

А "define red 9" говорит компилятору, что когда в тексте встретится "red", то нужно просто вместо текста вставить число 9, выделив при этом необходимые регистры памяти под его размещение. И, кстати, тот же вопрос, почему эта 9 определяется именно как вывод?

Mr.Privet
Mr.Privet аватар
Offline
Зарегистрирован: 17.11.2015

Darwetra пишет:

Не пойму только, как компилятор понимает, что это не просто число 9 под именем red, а именно девятый вывод на плате?

А "define red 9" говорит компилятору, что когда в тексте встретится "red", то нужно просто вместо текста вставить число 9, выделив при этом необходимые регистры памяти под его размещение. И, кстати, тот же вопрос, почему эта 9 определяется именно как вывод?

pinMode(RED, OUTPUT); 
digitalWrite(RED, ledState);

ответ на оба вопроса задаем пин RED тобишь 9 как выходной и делаем запись переменной ledState в пин RED.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Объявление const int red = 9;

В С/С++ читается примерно так (и это НЕ функция!): имя "red" является знаковой целочисленной константой (нормально 2 байта, но бывают "экзотические системы") с начальным значением "9".

Компилятор помечает в таблице имен это имя как неизменяемое и везде вместо него подставляет константу 9.

К какому пину платы относится эта конктанта - компилятору "по-барабану". Он чисто С-ишный. А вот во включаемых файлах (их список указан в комментариях к arhat.h - доступно на гитхабе), есть специальные макросы, которые связывают некоторые операции с таким числом с соответствующим АДРЕСОМ памяти (регистром), который .. соответствует требуемому действию с заданным пином.

Все операции с пинами платы преобразуются в конечном счете в операции И/ИЛИ логики над соответствующими битами нужного адреса.

Все пины отображены на "регистры" микроконтроллера, которые сами "отображены" на адреса памяти. Их список есть в каждом даташите на каждый микроконтроллер Атмела. .. и у каждого - по своему.

digitalWrite() делает такое преобразование через вызов спец. функции, которая перекодирует по таблице номер пина в конкретный адрес и бит в нем. Можно, если задано константно через #define эту перекодировку возложить на препроцессор "С" и в код встыкать только одну команду И/ИЛИ логики по управлению пином. Что и сделано в arhat.h. Несколько вычурно, но работает исправно.

Дополню, некоторые адреса памяти, отведенной под регистры МК имеют "короткие команды" ввода-вывода, но они байтовые. То есть ими можно изменять сразу 8 пинов или устанавливать некое значение в неком регистре управления .. типа analogWrite().

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

Darwetra пишет:

Не пойму только, как компилятор понимает, что это не просто число 9 под именем red, а именно девятый вывод на плате?

Никак не понимает. Для него это просто число 9. Для кого-то, может быть, это количество дырок в кармане, а для другого - миллиардов в банке, а для компилятора - просто число 9. Как и для чего это число использовать - это Ваше дело, компилятору это А) неизвестно и Б) безразлично.

Maik
Offline
Зарегистрирован: 15.04.2016

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

Tomasina
Tomasina аватар
Offline
Зарегистрирован: 09.03.2013

думаю, ошибка в строке 13.

Mr.Privet
Mr.Privet аватар
Offline
Зарегистрирован: 17.11.2015

Tomasina пишет:

думаю, ошибка в строке 13.

Если Вы это про программу уважаемого Arhat109-2 , то там же написано 

#include "arhat.h" // подключаем everyMillis() и заодно заменяем тяжелые вызовы на однокомандный ногодрыг :)

 

Tomasina
Tomasina аватар
Offline
Зарегистрирован: 09.03.2013

нет, я про скетч, написанный Maik

 
faeton
faeton аватар
Offline
Зарегистрирован: 21.03.2016

Чтобы синхронно двигаться по нескольким осям одновременно мотором, который требует давать ему N шагов, необходимо либо создать таблицу шагов по осям равной размерности для всех осей и в неё записать все шаги, учитывая что по одной оси на 3 шага у другой оси будет 2 шага разных и один стоять на месте и т.п. Либо задавать перемещение функцией и на кадую единицу времени выдавать шаги. Но в этом случае всё равно моторы будет каждый шаг делать отдельно по осям. Иногда и чаще всего это неприемлемо - будет дрейф. Для этого необходимо делать буффер по каждой оси, сначала его заполнять значениями каждого шала, а потом давать уже синхронно команду на вывод сразу всег регистров осей. Возможно, скороть записи в 3 порта процессора настолько высока, что с этим не стоит заморачиваться, т.е. процессор записал в один порт шаг на перемещение по одной оси и мотор ещё не успел поехать, как он уже и для второй оси порт записал, но мне приходилось давно-давно через LPT-порт управлять осями и там пришлось городить регистры.

А ещё у дуньки есть ШИМ управляемые приводы - ему дал, грубо говоря, положение 26 и он сам туда поедет, да ещё и держать это положение будет, если враги отодвинут. :)