ARDUINO задержка без delay помогите.

Arhat109

★★★★✩✩✩
9 Июн 2019
473
203
Поправил опечатку .. (хоть бы кто заметил) .. в первом применении происходит гистерезис по времени включения и выключения, соответственно условие - одно и тоже или вообще некоторое значение типа bool. :)

Впрочем, можно комбинировать и так и этак и как угодно.. :)
 

Arhat109

★★★★✩✩✩
9 Июн 2019
473
203
Кстати .. такой вариант применения тоже уместен:

C:
#define everyMillis(interval, action) \
{                                     \
  static unsigned long t = 0UL;       \
  if( millis() - t > (interval) )     \
  {                                   \
    { action; }                       \
    t = millis();                     \
  }                                   \
}

...
    
everyMillis(1000, {
    if( someCondition ){ doSomething(p1,p2..); }
    else{ doOther(p3, p4..) }
})
Проверяем каждую секунду условие и если оно сработало (напр. 31-е декабря), то "идем в баню" .. или идем куда-нить ишо.
:)
 
  • Лойс +1
Реакции: Sascha.

VasiliyP

✩✩✩✩✩✩✩
15 Дек 2020
1
1
а если так..

C++:
static uint32_t btnTimer = 0;               //не нужна глобальная переменная, достаточно статической в текущей функции
if (temp >= 24) {                                     
    if (!btnTimer ) btnTimer  = millis();   // при первом вызове, запоминаем текущее время
    if (millis() - btnTimer > 2000)  {      //проверка не вышло ли время задержки
        digitalWrite(13, HIGH);
   }
} else {                                    //температура упала ниже 24гр, выкл. светодиод, сброс btnTimer
   digitalWrite(13, LOW);
   btnTimer = 0;                            //обнуляем, чтобы при следующем запуске (когда temp>=24), запомнить новое время и начать новый отсчет
}
 
  • Лойс +1
Реакции: Sascha.

kostyamat

★★★★★★✩
29 Окт 2019
1,097
630
Что static, что глобал контроллеру все едино. Поэтому в системах, где памяти мало, лучше таки глобал. Ее хотя бы переиспользовать можно для других целей, в других функциях. С атмегой 328 я к такому выводу пришёл. Это так, замечание просто.

А вообще, с точки зрения экономии памяти, если не нужны большие периоды, к примеру нужны в пределах 0-255 мс, можно и нужно применять такую конструкцию
byte timer = (byte)millis();
if ((byte)millis() - timer > 200 { что-то делаем
} else timer = (byte)millis();
Таким образом экономим 3 байта. А вся магия продолжает работать.
Для периодов больше можно вместо byte приводить к uint16_t. Короче, по потребностям. А не сразу на uint32_t разгонятся.

Еще одна хитрость на счет экономии памяти - вместо bool переменные лучше объявлять как byte (uint8_t). При желании ею можно пользоватся как булевой типа
byte flag;
flag = true; или flag = false;
А можно переиспользовать еще раз где-то, если она сейчас свободна. И хранить там что-то.
При этом компилятор даже не поморщится.
Зачем такой изврат? А за тем, что bool все равно byte занимает, хоть тресни. То хоть с пользой тогда. Когда в системе 2кБ озу, еще и не такое в голову придет. :)
 
Изменено:
  • Лойс +1
Реакции: Sascha. и Arhat109

kDn

★★★★★✩✩
18 Ноя 2019
1,103
437
Зачем такой изврат? А за тем, что bool все равно byte занимает, хоть тресни. То хоть с пользой тогда. Когда в системе 2кБ озу, еще и не такое в голову придет. :)
Можно булевые типы упаковывать в битовые структуры. Так в 1 байт их поместится 8 штук. Поглядите как у нас сделано.
 

poty

★★★★★★✩
19 Фев 2020
3,003
898
Можно булевые типы упаковывать в битовые структуры. Так в 1 байт их поместится 8 штук. Поглядите как у нас сделано.
С точки зрения хранения - да, но не с точки зрения использования. В программном коде, чтобы "дешифровать" код, Вам придётся использовать битовую маску, которая занимает дополнительный байт. Да, этот байт можно разместить в программной памяти (если, допустим, использовать непосредственные операнды-константы), но вхождений таких сравнений в код может быть значительным, зависит от сложности кода, конечно.
 

Arhat109

★★★★✩✩✩
9 Июн 2019
473
203
if ((byte)millis() - timer > 200 { что-то делаем
} else timer = (byte)millis();
Все верно. Приведенный макрос у меня очень часто урезается до нужного размера, вплоть до 5 бит, в одном из применений со сдвигом влево, для увеличения длительности "базового интервала" с 1миллисекунды до 64. :)

Хочу обратить внимание на этот кусок кода, т.к. в нем есть грабли, а именно integer propagation стандарта языка. В такой записи он может работать не верно, т.к. результат вычитания однобайтового значений будет увеличен до int, которое все таки слово. И, если сравниваем с байтом, то сравнение также будет расширено до int .. возможны накладки.

Как понимаю, правильная запись такая:
C:
uint8_t startTime // время храним в байте .. где-то в коде..
const uint8_t period = 100   
    
if( (uint8_t)((uint8_t)millis() - startTime) > period ){
    // что-то делаем
}
То есть результат байтовой операции надо возвращать к байтовому значению.. точно не помню, но грабли тут есть.
 
  • Лойс +1
Реакции: kostyamat

Arhat109

★★★★✩✩✩
9 Июн 2019
473
203
С точки зрения хранения - да, но не с точки зрения использования. В программном коде, чтобы "дешифровать" код, Вам придётся использовать битовую маску, которая занимает дополнительный байт. Да, этот байт можно разместить в программной памяти (если, допустим, использовать непосредственные операнды-константы), но вхождений таких сравнений в код может быть значительным, зависит от сложности кода, конечно.
Это не всегда так. Битовые маски применяемые для вычленения отдельного бита как булева значения могут компилироваться в специальные битовые команды ветвления у AVR, но .. требует проверки в каких конкретно случаях компилятор это применяет .. уже не помню, где сталкивался.

В любом случае, всегда можно через #define определить "свою функцию", которая будет подставлять в код АСМ-проверку нужного бита..
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,198
1,283
Москва
то static, что глобал контроллеру все едино. Поэтому в системах, где памяти мало, лучше таки глобал. Ее хотя бы переиспользовать можно для других целей, в других функциях. С атмегой 328 я к такому выводу пришёл. Это так, замечание просто.
Но глобальная переменная при этом несет подвох, если функции с ее использованием выполняются друг за другом. статичная переменная более безопасна в этом смысле
 
  • Лойс +1
Реакции: VasiliyP

kostyamat

★★★★★★✩
29 Окт 2019
1,097
630
@kDn, это я знаю. Можно и так. Но это в случае если таких булек много надо. А если одна/две - это бессмысленно. К тому же, путем джедая, можно просто биты выставлять в 1 или 0. Я так в одном проекте состояния входов охраны храню, и выходов на реле. В таком же виде и между пультом и центральным блоком гоняю, и на сервер передаю. При этом, так как входов 4, и реле 4, то на все про все одна переменная byte.
 
Изменено:

Arhat109

★★★★✩✩✩
9 Июн 2019
473
203
Битовые операции как и хранение в битиках часто не имеют никакой повышенной сложности, а в мелкоконтроллерах как правило имеют свои очень эффективные команды. Сложность часто сильно преувеличена.. "не так страшен чёрт, как его Малютка (Скуратов)" :)
 

poty

★★★★★★✩
19 Фев 2020
3,003
898
Проведите пример. Сложности нет, но для проведения битовой операции нужно два операнда (битовый массив в виде байта или слова и маска), а для проверки на ноль - только один. В результате, каждая "битовая" операция занимает на один байт больше, чем простая проверка.
 

kDn

★★★★★✩✩
18 Ноя 2019
1,103
437
Проведите пример. Сложности нет, но для проведения битовой операции нужно два операнда (битовый массив в виде байта или слова и маска), а для проверки на ноль - только один. В результате, каждая "битовая" операция занимает на один байт больше, чем простая проверка.
Так для начала нужно определиться какую именно память экономим - память программы или RAM. Обычно вторая гораздо ценнее, т.к. меньше по объему. Битовые операцию как раз дают экономию RAM за счет FLASH.
 

kostyamat

★★★★★★✩
29 Окт 2019
1,097
630
@poty, если вопрос ко мне, то в том проекте я особо голову себе не сушил. А использовал функции bitRead() и bitSet() из ардуино. Как оно работает - даже не интересовался тогда. Уверен, что это не оптимально, но удобно.
 

kDn

★★★★★✩✩
18 Ноя 2019
1,103
437
@poty, если вопрос ко мне, то в том проекте я особо голову себе не сушил. А использовал функции bitRead() и bitSet() из ардуино. Как оно работает - даже не интересовался тогда. Уверен, что это не оптимально, но удобно.
Но вообще я изначально имел ввиду нечто вида:

C++:
typedef union {
  uint8_t raw;
  struct {
  uint8_t b0:1;
  uint8_t b1:1;
  uint8_t b2:1;
  uint8_t b3:1;
  uint8_t b4:1;
  uint8_t b5:1;
  uint8_t b6:1;
  uint8_t b7:1;
  };
} thisis8bits;
C++:
thisis8bits val;
val.b5 =true;
Таким макаром все битовые операция от пользователя скрыты и выполняются прозрачно, по raw имеем полный байт. :)
 

Arhat109

★★★★✩✩✩
9 Июн 2019
473
203
Проведите пример. Сложности нет, но для проведения битовой операции нужно два операнда (битовый массив в виде байта или слова и маска), а для проверки на ноль - только один. В результате, каждая "битовая" операция занимает на один байт больше, чем простая проверка.
Мне казалось, что программист микроконтроллеров все же должен хотя бы заглядывать в Ассемблер своей железки ..

SBRS - Skip if Bit in Register is Set -
If Rr(b) = 1 then PC< PC + 2 (or 3) else PC< PC + 1
SBRS Rr,b
0 < r <31, 0 < b < 7

Ну и там ещё целая их кучка. Все делается одной командой, если у вас проверяемое значение в регистре .. опция register от компилятора - тоже в помощь. Ну и понимание когда и какой конкретно код на С/С++ компилятор превращает в то или иное..
 

poty

★★★★★★✩
19 Фев 2020
3,003
898
@Arhat109, ну и что Вы хотите этим сказать? Команда одна, количество занимаемых ей байт - 2. И это - если речь идёт о регистрах: для них выделены номера команд. А если Вы пытаетесь выделить бит из произвольного байта в памяти?
 

Arhat109

★★★★✩✩✩
9 Июн 2019
473
203
@poty, не понял ничего, что Вы хотите сказать. Там же написано - "и ещё кучка" .. любая команда в RISC архитектуре ВСЕГДА работает исключительно с регистрами .. почти совсем любая (кроме загрузить/выгрузить в/из памяти). Честно-честно. Не знали? :)

Ну и детальней, раз уж не представляете с чем имеете дело наяву:

bool flag = true -- имеем байт, занимаемый в памяти, который в конечном (общем виде) проверяется примерно так (RISC):

0. Вычислить адрес памяти (для элемента массива в куче - может быть даже сложно .. команд 10-12..
1. Прочитать байт из памяти в регистр (для AVR это 2 такта CPU!)
2. ВЫЧЕСТЬ/сложить логически регистр сам с собою (1 такт)
3. Проверить результат и флаги и перейти если надо или пропустить пару байт (команду) ещё 1-2 такта

.. итого 4-5 тактов. Занимаемых байт (без шага 0) .. 6-10 в зависимости от набора :)

Битовая операция командой повторяет п.0, 1 и остальное делает ОДНОЙ командой в 1 такт, итого 3 такта. Занимаемых байт программой 6-8..
:)
 
Изменено:
  • Лойс +1
Реакции: Sascha.

poty

★★★★★★✩
19 Фев 2020
3,003
898
Справедливости ради нужно сказать, что команда SBRC также выполняется за 1-2 такта, но, согласен, что всё равно получается по времени на 1 такт меньше. Правда, если не использовать SBRC для анализа булевой переменной, тогда получается паритет.
Однако вопрос стоял в экономии памяти, а не времени, получается, что здесь тоже экономии нет.

Я нашёл! 😅 Для изменения флага в массиве бит нужно использовать загрузку байта в регистр, изменение бита и сохранение результата в память. При использовании булевой переменной можно установить младший бит в регистре (или записать константу 0/1) и сохранить его в память.
 
Изменено:
  • Лойс +1
Реакции: Arhat109

Arhat109

★★★★✩✩✩
9 Июн 2019
473
203
Осталось разобраться КАКУЮ память Вы хотите "икономить" :)

Т.к. у АВР их аж две (и даже три!) и промеж себя они .. не пересекаются, о как. Честно-честно.
ИДЕ внизу даже пишет их отдельно: в одной памяти хранится код программы (программа занимает стока-то килобайт - у Мега 2560 аж до 256 кило байт), а ишо пишет (и не зря ведь!) что переменные тож чего-то там занимают. И вот это оно и есть то самое ОЗУ, которого мало, и которое надо икономить .. так что "размер кода программы" на экономию влияет крайне слабо .. 6 там или 12 байт - в общем-то "пофиг". Честно.

А кроме этого (хорошо, что нашли Ассемблер) там есть команды, которые способны проверить битик в .. регистре периферии. Напрямую. Например "приключилось ли чтение байта интерфейсом?" .. всего ОДНА команда .. и никаких "битовых операций", "чтения-записи в память" по 2 такта ..
:)

Осваивайте. Это реально интересно и поучительно. Высший пилотаж - писать на Си так, чтобы компилятор рисовал Ассемблер "почти как руками специалиста". Мне - удавалось .. когда-то.
 
  • Лойс +1
Реакции: Sascha.

poty

★★★★★★✩
19 Фев 2020
3,003
898
Да уж ладно, сарказм здесь ни к чему. Пусть не на RISC-архитектуре, но ассемблер использовал ещё в 90-х и вплоть до 2000-ых, так что открыт он давно.
А память нужно экономить любую. И - да, использую всякую.