Еще раз мигаем светодиодом без Delay
- Войдите на сайт для отправки комментариев
Я уж и не припомню сколько раз тут на форуме отсылал новичков к примеру Мигаем светодиодом без delay()
Но... не нравится мне он. Зато нравится процесс "критиковать чужое" :)
Кратко что не нравится:
1. Типы переменных выбранны "от балды"
2. Используются переменные там где можно было оботись без них.
3. Излишне засоряется глобальное простраство имен
Если интерестно подробней мои стоны "почему тут все плохо" - можно почитать в моем блоге МИГАЕМ СВЕТОДИОДОМ БЕЗ DELAY() или всегда ли ...
Если второй и третий пункт еще можно оправдать тем что "так новичкам понятней", то первый пункт череповат трудноуловимыми багами (в том числе и прямо в этом примере) и привитем стиля "память не экономим".
А потом форум пестрит скетчами где переменные объявляются по принципу "если не знаем что писать - напишем int".
Вообщем вот моя версия:
/* Blink without Delay 2013 by alxarduino@gmail.com http://alxarduino.blogspot.com/2013/08/delay.html */ #define LED_PIN 13 // номер выхода,подключенного к светодиоду #define INTERVAL 1000UL // интервал между включение/выключением светодиода (1 секунда) void setup() { // задаем режим выхода для порта, подключенного к светодиоду pinMode(LED_PIN, OUTPUT); } void loop() { // здесь будет код, который будет работать постоянно // и который не должен останавливаться на время между переключениями свето // обратите внимание на слово static static unsigned long previousMillis = 0; // храним время последнего переключения светодиода //проверяем не прошел ли нужный интервал, если прошел то if(millis() - previousMillis > INTERVAL) { // сохраняем время последнего переключения previousMillis = millis(); // меняем состояние выхода светодиода на противоположное текущему. // если горит - тушим, не горит - зажигаем. digitalWrite(LED_PIN,!digitalRead(LED_PIN)); } }
#define INTERVAL 1000UL
Алексей, почему 1000UL, а не просто 1000 ?
Спасибо.
Потому что INTERVAL сравнивается с выражением millis() - previousMillis результат которого имеет тип
unsigned
long
. Если вы напишите просто 1000 (int), то сравнение будет производиться с переменными разного типа.Спасибо.
#define INTERVAL 1000UL
Алексей, почему 1000UL, а не просто 1000 ?
Спасибо.
Давайте чуть-чуть конкретизируем, что именно вызывает непонимание
1. Что означают буквы "UL", какой они дают эффект или
2. Смысл букв понятен, но непонятно "зачем их тут употребили"
Что именно требует пояснения?
P.S. Я понимаю что вы старались проявить вежливость, но тут я сам назвался leshak :)
Вот только "тушим" - это как-то по-кухарски. :) Лучше уж тогда "гасим", или "выключаем"...
Давайте чуть-чуть конкретизируем, что именно вызывает непонимание
1. Что означают буквы "UL", какой они дают эффект или
2. Смысл букв понятен, но непонятно "зачем их тут употребили"
Что именно требует пояснения?
P.S. Я понимаю что вы старались проявить вежливость, но тут я сам назвался leshak :)
Я из Делфи/Паскаля, потому буквы "UL" удивили. Впрочем, maksim уже все объяснил.
P.S. OK, больше никакой вежливости :)
Я из Делфи/Паскаля, потому буквы "UL" удивили. Впрочем, maksim уже все объяснил.
Ну, что-бы понимать это не с чужих слов, а "из первоисточника" - оставлю ссылочку Целочисленные константы
На самом деле, вообще-то, в данном КОНКРЕТНОМ случае пример будет корректно работать и без этих букв.
Но написал их специально:
1. В надежде что кто-то задатся вопросом "что это за буквы" и погуглит. Ну не погуглит так спросит :) (еще такая же надежда на слово static :)
2. Что-бы "сразу привыкали" явно указывать, что-бы потом не ловит нежданчики..Что-бы если кому-то прийдет в голову поправить интервал на один час. Причем не в уме умножить 60*60*1000, а прямо так и написать #define INVERVAL 60*60*1000 - вот тут уже без явного указания типа с помощью UL - уже фигня выйдет. В четком соотвествии с объяснением Максима.
ммм...
А почему
static
unsigned
long
previousMillis внутри loop?
ещё можно чуть уменьшить количество вычислений в каждой итерации. Как то так ...
ммм...
А почему
static
unsigned
long
previousMillis внутри loop?
А что-бы не засорять глобальное пространство имен. В таком случае я могу скопировать loop() (или другую функцию где есть эта конструкция) в другой скетч, не переживая что "нужно еще не забыть previusMillis в начале скетча).
Плюс у меня может быть несколько функций и в каждой будет свой prevMilis и не будут друг-другу мешать. Вообщем чем меньше глобальных переменных - тем лучше. Каждый кусок кода - независим.
Скажем что-то такое:
Вообщем "по месту назначения" могу использовать имена переменных "как мне удобно" не переживая что они "пересекутся". И не нужно городить prevMillis1, prevMilis2, prevMillis3 , где то "ошибся цифру не поправил"...
ещё можно чуть уменьшить количество вычислений в каждой итерации. Как то так ...
Нельзя. Что в вашем варианте произойдет когда millis() пойдет к значению, скажем 4,294,967,000?
Ваше условие - начнет постоянно срабатывать. И "будет глючит" 295 миллисекунд подряд . Не дай бог по этому условию SMS-ска отправляется :) Вернее ровно за секунду до переполнения начнет глючит. А если интервал, скажем "раз в час", то целый час кусок кода будет выполняться при каждом проходе loop
Это в математике у нас "a-b>c" эквивалетно "a>b+c". И то не всегда, а когда a,b,c являются вещественным или натуральными числами. За счет того что числвая ось - бесконечна. А у нас числовые оси - конечны. Да еще "закольцованы". Поэтому "a-b>c" не эквивалетно "a>b+c". На больших значениях - мы получаемм переполнение.
А что-бы не засорять глобальное пространство имен. В таком случае я могу скопировать loop() (или другую функцию где есть эта конструкция) в другой скетч, не переживая что "нужно еще не забыть previusMillis в начале скетча).
Допустим, я новичок, и понял, что loop() - это такой аналог while (1)... но зачем мы тогда в цикле каждый раз инициализируем переменную?
Допустим, я новичок, и понял, что loop() - это такой аналог while (1)...
Это не аналог а "он и есть". Если бы вы писали программу на "чистом C", то знаете что у вас есть "главная фунция main()" которая вызвается при старте програмы.
На самом деле она и тут есть. Просто при компиляции ArduinoIDE, незаметно, за кулисами, к каждому скетчу добавляет примерно такую функцию main()
Попробуйте не объявить фунцию setup() или loop()
и вы увидите, в ошибках, что-то типа т"в функции main() была вызвана неизвестная функция loop()". Хотя сами вы, никакой main() не объявляли, а в ошибках - ее видно.
но зачем мы тогда в цикле каждый раз инициализируем переменную?
А мы не иницализируем ее каждый раз. Для этого у нас есть ключевое слово static. Причем в коментах предлагается на него обратить внимание. Если новичок не знает что оно делает - это тонкий намек "погуглить". Ну или - если не интерестно разбиратся, то можно просто копипастать этот код.
Ведь он же работает, правда? Преверили? Если работает - значит никаких "каждый раз инициализируем переменную" - нет :) Логично? Или не работает?
staitc нам как раз и дает эффект что переменная ведет себя как глобальная (инициализируется один раз, сохраняет свое значение при выходи из фунции), при этом не обладает минусами "глобальности" - видна только внутри функции в которой ее объявили. И в других функциях - тоже могут быть переменные с таким же именем.
На самом деле она и тут есть. Просто при компиляции ArduinoIDE, незаметно, за кулисами, к каждому скетчу добавляет примерно такую функцию main()
А, ну т.е. переменная действительно каждый раз в цикле объявляется!
А мы не иницализируем ее каждый раз. Для этого у нас есть ключевое слово static. Причем в коментах предлагается на него обратить внимание.
Если мы будем вставлять спереди ключевое слово static, то конструкция будет выполняться только один раз, да? И в середине loop тоже? И необязательно переменная?
Если новичок не знает что оно делает - это тонкий намек "погуглить". Ну или - если не интерестно разбиратся, то можно просто копипастать этот код.
Новичок в си и в программировании плохо разбирается, куда ему эти ваши "тонкие ардуиновские нюансы"...
Если мы будем вставлять спереди ключевое слово static, то конструкция будет выполняться только один раз, да?
Во первых - вы явно так и не погуглили static. Иначе вы знали бы что применить его можно только к ОБЪЯВЛЕНИЮ переменной, функции, класса... а не к любой произвольной конструкции.
Во вторых - это можно было не гуглить. А взять и поробовать. Набросать что-то типа
Зачем спрашивать кого-то, если можно спросить сам компилятор? Вдруг этот кто-то ошибется/соврет? Лично я - постоянно пишу вот такие маленькие скетчи-проверки, на 3-5 строк, когда у меня возникает какой-то пробел в понимании вида "а что будет если..." или "а как оно там себя ведет".
В третьих - не с того конца зашли к static. Его смысловая нагрузка не в "будет выполнятся один раз", а скорее в "переменная не будет уничтожена при выходе из функции". Поэтому при следующем заходе в функцию произойдет что-то типа "о... объявления переменной... а она у нас уже есть, память под нее уже выделена, значит второй раз выделять память под нее не нужно пользуемся тем что уже есть. раз память мы под нее не выделяем, значит и инициализацию переменной делать не нужно". В результате - в переменной сохранилось то значение которое было в нее записанно при прошлом вызове loop()
То есть - "инициализация будет выполнена один раз" это побочный эффект от "переменная не уничтожается при выходе из фунции".
Если говорить совсем строго, то смысл слова static в том что-бы указать компилятору "память под эту переменную нужно выделить не в стеке вызова, а в сегменте данных программы". Результатом того что переменная разместилась не в стеке - и является ее "неуничтожимость", что в свою очередь приводит к однократному вызову инициализации.
Но вникание в то что такое "стек вызовы", "сегмент данных" - в данном случае уже слегка избыточно. Это уже разбирательство "как компилятор устроен под капотом" (вернее в какой ASM код все это будет странслированно). На бытовом уровне можно воспринимать static именно как "защиту от уничтожения локальной переменной". По крайней мере я пользуюсь именно такой мысленной конструкцией :)
И в середине loop тоже?
Мы же вроде выше разобрались что loop() это обычная функция. Следовательно к ней применимо все что применимо к любой функции. В том числе и объявлять переменные в любом месте функции. Главное - объявление должно быть до того как переменная использована.
И необязательно переменная?
А попробовать-проверить?
Новичок в си и в программировании плохо разбирается, куда ему эти ваши "тонкие ардуиновские нюансы"...
Ну во первых я написал что "если не хочется разбиратся можно просто скопи-пастать". Если не хочет разбиратся-гуглить, то он так навсегда и останется новичком. Будет в состоянии только копировать чужой код, но шаг в право-лево будет все ломать и приводить к крикам "помогите новичку". А потом сидеть ждать что-то кто-нибудь снизойдет и поправит его код слепленный из куском без малейшего понимания.
Может кого-нибудь и устраивает постоянно попадать в такую ситуацию... это его дело. Лично мне - такие люди не интересны, я пишу не для них. У меня же тоже есть свобода выбора для кого писать, правда? ;)
Я же тоже не родился с этими знаниями. Более того - когда я купил ардуину опыта C/C++ у меня тоже небыло. Просто когда видел что-то не понятное в чужом коде/примере - лез гуглить и разбиратся. Даже если прямо сейчас оно мне было не нужно :) И сейчас поступаю так же. В железе я более слаб. Вон в соседней ветке увидел что человек использовал компараторы, начал выяснять "нафига и зачем". Когда понял - попытался решить задачу без них. Узнал что есть встроенные в камень, полез в даташит почитать, там еще сделал для себя парочку "открытий" :) Только так и перестают быть новичками ;)
А програмировать микроконтроллеры не вникая в "тонкости C/C++", "ардуиновские нюансы", "специфику железа" - все равно не выйдет. Все эти "не нужно знать програмирование" - это маркетинговый булшит. C/C++ - изначально создавались как "язык максимально близкий к железу и дающий полный контроль" (разве что ASM "еще ближе к железу"). Не хотите вникать как устроена память - C#/Java - вот ваши языки. Там знакомство с этим - можно "оттянуть" намного дольше. Не хотите разбиратся с железом - забудте про микроконтроллеры.
А если "разбиратся" не пугает, то.... на самом деле в микроконтролле/ардуине все намного проще, на самом деле, чем в "большом компьютере". "Мир" - намного меньше размера.
По мотивам кода из сообщения #10 написал следующую статью
Леший в мире Ардуины: Делаем несколько дел одновременно-переодически
Вот как этот код выглядит "целиком"
Чуть чуть больше примеров и намного больше воды - по ссылке выше :)
P.S. Буду рад вопросам :) Еще больше - критике/замечаниям (хотя, конечно буду огрызатся ;)
UPD: blinkLed поправлен (BLINK_INTERVAL --> interval) благодаря замечанию trembo в сообщении #25
А что будет с этим
if(if(millis() - prevTime > BLINK_INTERVAL)
когда millis перейдёт в 0 и начнёт новый период???
когда millis перейдёт в 0 и начнёт новый период???
А давайте спросим арудину что будет в этом случае:
А она ответит, что не учли переход через 0.
А она ответит, что не учли переход через 0.
А она ответит что вы не запускали этот скетч. Чему получилась равна разница millis()-previousMillis когда previousMillis подходит к границе переполнения, а milllis() уже перешла (имеет малые значения)?
>Чему получилась равна разница millis()-previousMillis
Чему и должна.
>Чему получилась равна разница millis()-previousMillis
Чему и должна.
Ну ведь никто же не знает чему она, по вашему, должна быть равна. Что скетч в логе написал? Какая цифра получилась? Сработало условие (то есть "проблема имеется") или False написало (значит лишних сработок не будет)?
Понятно "почему именно так получается" или нужно объяснить?
Во первых - вы явно так и не погуглили static. Иначе вы знали бы что применить его можно только к ОБЪЯВЛЕНИЮ переменной, функции, класса... а не к любой произвольной конструкции.
Я, как новичок не умею такие штуки гуглить :(
Зачем спрашивать кого-то, если можно спросить сам компилятор? Вдруг этот кто-то ошибется/соврет? Лично я - постоянно пишу вот такие маленькие скетчи-проверки, на 3-5 строк, когда у меня возникает какой-то пробел в понимании вида "а что будет если..." или "а как оно там себя ведет".
Ага, а когда я набью свой скетч такими "хитрыми штуками" и он не заработает, я приду в форум. Раз сами эти штуки посоветовали, то сами и разбирайтесь, почему они у меня не работают
В третьих - не с того конца зашли к static. Его смысловая нагрузка не в "будет выполнятся один раз", а скорее в "переменная не будет уничтожена при выходе из функции". Поэтому при следующем заходе в функцию произойдет что-то типа "о... объявления переменной... а она у нас уже есть, память под нее уже выделена, значит второй раз выделять память под нее не нужно пользуемся тем что уже есть. раз память мы под нее не выделяем, значит и инициализацию переменной делать не нужно". В результате -
Как все запутано в этой вашей ардуине... "оно как бы и работает, но так странно..."
Если говорить совсем строго, то смысл слова static в том что-бы указать компилятору "память под эту переменную нужно выделить не в стеке вызова, а в сегменте данных программы". Результатом того что переменная разместилась не в стеке - и является ее "неуничтожимость", что в свою очередь приводит к однократному вызову инициализации.
Ничччего не понятно. Какие-то стеки, сегменты, статики... а ведь мне всего лишь простой скетч немного доработать...
Мы же вроде выше разобрались что loop() это обычная функция.
Как это обычная, если она работает иначе, чем обычные функции?
Ну во первых я написал что "если не хочется разбиратся можно просто скопи-пастать". Если не хочет разбиратся-гуглить, то он так навсегда и останется новичком. Будет в состоянии только копировать чужой код, но шаг в право-лево будет все ломать и приводить к крикам "помогите новичку". А потом сидеть ждать что-то кто-нибудь снизойдет и поправит его код слепленный из куском без малейшего понимания.
Вообще-то новичку, как правило, достаточно достигнуть того уровня, когда он может писать свои скетчи, не углубляясь в нюансы. Потому что он не ставит перед собой задачу стать матерым скетчеписателем
А всякие мудреные штучки - это профессионалам и тем, кто решил идти дальше/вглубь
Т.е. если даем пример новичку, то он и должен быть написан так, чтобы был понятен новичку. И не требовать от него недели копания в интернетах в попытках понять "а что это здесь"
>Я, как новичок не умею
>Как все запутано в этой вашей ардуине..
>а ведь мне всего лишь
> достаточно достигнуть
>то сами и разбирайтесь
Эх... сел я тут писать по каждому вашему тезису опровержение писать... вы же не сомневаетесь что "мне есть что сказать"? :)
Но....
Что-то не понятно объяснил? Ну я не идеален. Стараюсь объяснить получше что сразу "донести не удалось". Не всегда с первого раза выходит. А спорить "зачем это нужно?" с тем кому это не нужно - не вижу смысла.
Такое впечатление что я вас чему-то уговариваю. Кто кто хочет разобратся - разбирается, пробует, задает вопросы. Не хотите - ну так зачем вы тратите мое (и свое) время? У вас есть официальный пример. Вам его достаточно, не волнует что типы перепутаны, не хотите узнать что-то новое? Да ради бога. Мы не в школе, я вам оценки не ставлю, успеваемость ваша - меня не волнует. Значит не для вас я писал. Только и всего.
Супер!
Долго соображал как работает static и почему при втором входе prevTime не "0", а предыдущее значение,
даже после " static unsigned long prevTime = 0; " принт вставлял....
А то я всё время SimpleTimer использовал,
хотя в нём есть и кое-какие примочки типа: количество повторов таймера и однократный таймер.
Для полного порядка надо исправить
А если ещё исправить > на >=
то очень даже круглые числа увидим в сериал мониторе.
Спасибо Leshak!
Такое впечатление что я вас чему-то уговариваю.
Да мне-то не надо ;) А вот новичкам придется разжевывать этот момент. (и, подозреваю, не один раз)
Так новичкам об этом лучше не говорить :) Пока разберутся уже будут знать
ещё можно чуть уменьшить количество вычислений в каждой итерации. Как то так ...
Нельзя. Что в вашем варианте произойдет когда millis() пойдет к значению, скажем 4,294,967,000?
oops, i did it again https://www.youtube.com/watch?v=CduA0TULnow
даже после " static unsigned long prevTime = 0; " принт вставлял....
Вот это я считаю самым правильным подходом к разбирательству с чем-то не понятным. "Игратся с кодом". Тыкать print-ты, менять и смотреть что выйдет, писать какие-то "проверочные скетчи".
А то я всё время SimpleTimer использовал,
хотя в нём есть и кое-какие примочки типа: количество повторов таймера и однократный таймер.
Ну так в этом ничего плохого нет. В принципе библиотеки для того и придуманы что-бы для часто повторяющихся задач использовать "удобную оберку над знаниями". Более того - правильно подобрать библиотеку под задачу, а не городить велосипед - это тоже хороший навык.
Другое дело что, я считаю, полезно все-таки, понимать как библиотека устроена (что-бы понимать область ее применимости, чем ты расплачиваешься за ее использование и т.п.). Вообщем для разных задач - разные инструменты. Где-то "памяти еще много" и можно для простоты подключить либу с килограммом плюшек, где-то проще завести пару переменых и руками сделать :)
Я тоже, в итоге, все это планирую сделать в ввиде "библиотеки" (1001-вой, наверное, для этих задач, SimpleTimer - не единственная). Только не в виде классов и функций, а в виде макроса. Плюс хелперы что-бы задавать вреям в секундах, часах, минутах.
Вообщем-то даже не "планирую", а "написал" :). Руки только не доходят выложить и описать "как пользоватся" :(. Дам маленький спойлер как оно будет выглятеть:
И все. А на этапе компиляции само "развернется" в объявление переменной prevMillis, if и т.п. (то есть, с точки зрения размера - будет в точности то-же самое как если бы писали руками).
Так что библиотеками можно и нужно пользоватся. Но это не отменяет "понимания". Часто "готовое решение" чуть-чуть не подходит и нужно уметь его "подпилить напильником".
Для полного порядка надо исправить
Ох... спасибо. Запутался с версиями, пошаговым "приходом к чему-то". Как оказалось писать в "виде статьи" намного труднее чем думал. Со всеми этими форматированиями, проставить ссылки и т.п. Рабочий код пишется 20-ть минут, а "оформить/расписать" - часы или даже дни :(
Щас поправлю.
А если ещё исправить > на >=
....
то очень даже круглые числа увидим в сериал мониторе.
В принципе тоже думал об этом. Но... почему-то захотелось это оставить как в оригинальном скетче. Тем более что "круглые числа" все равно у нас будут только до тех пор, пока остальной loop() выполняется быстрее чем за одну миллисекунду. Когда Loop() станет побольше/реальней - все равно не будет точности. Это все "примерно". Если нужно "совсем точно" - тут уже апаратные таймеры/прерывания нужно. Но это уже "другая история". Вообщем пока оставлю как есть, будем считать что ваш комментарий - уже достаточно для того кто захотел вникнуть :)
P.S. "Ты,дух, - правильный военный" (C) ДМБ. :)
Я тоже, в итоге, все это планирую сделать в ввиде "библиотеки" (1001-вой, наверное, для этих задач, SimpleTimer - не единственная). Только не в виде классов и функций, а в виде макроса. Плюс хелперы что-бы задавать вреям в секундах, часах, минутах.
Билиотеку брать тут: https://bitbucket.org/alxarduino/leshakutils
Подробность/графомания тут: http://alxarduino.blogspot.com/2013/09/ComfortablyBlinkWithoutDelay.html
Пример использования:
Очень часто возникает надобность отдельно задавать периуд HIGH и отдельно LOW , а не 50х50 как в стандартных вариациях .
Не планируете ли вы сделать что то подобное в своей библиотеке ?
Очень часто возникает надобность отдельно задавать периуд HIGH и отдельно LOW , а не 50х50 как в стандартных вариациях .
Ну так "стандартные вариации" предназначены для того что-бы понять принцип. И уж "напильником" подгонять под конкретную ситуацию. Ничего же не мешает взять, "заготовку" из Делаем несколько дел одновременно-периодически и чуть-чуть чуть подпилить ее под свои нужды:
Или использовать использовать DO_EVERY, тут чуток другой принцип можно применить. Берем наши два интервала, находим наибольший общий делитель, заводим какую-то переменную счетчик и в зависимости от ее значения - выводим LOW или HIGH.
То есть, если как в коде выше нужно гореть 3 сек, а "не гореть" 1 сек, то это означает что нам нужен один DO_EVERY. Просто будем каждую секунду решать "гореть нам или нет".
Не планируете ли вы сделать что то подобное в своей библиотеке ?
Нет, пока не планирую.
Во первых честно говоря даже попытавшись вообразить "зачем это нужно" - не смог. Вы погорячилсь с "часто требуется". Приходит в голову только генерация каких-то PWM, шим-ов и проч. Но - это плохой метод для подобных задач. Не та точность и "гарантированость" времени срабатывания. Тут лучше вешатся на прерывание таймера или апаратным ШИМ-мом пользоватся. millis() - не самый удачный вариант для этого.
Во вторых - это библиотека скорее "для работы со временем", а не "для мигания диодом". Даже то как поставлена сама задача уже подразумевается слишком конкретное применение. Хочется оставить ее "не специализированой". Потому что потом потребуется вещи типа "секунду горим, 2 не горим, 3 в пол-накала... и т.п." - монстр выйдет. Нужен баланс между "простотой пользования" и "универсальностью".
Возможно добавлю что-то типа "выполнить дейсвие чере какое-то время" (вот это действительно чаще требуется). Что-то типа DO_AFTER(1000,digitalWrite(HIGH)); Из комбинации двух таких DO_AFTER - возможно и получится слепить что-то типа того что вы хотели. Но на макросах это уже трудней будет реализовать. Потребуется с помощью классов. Боюсь в итоге это приведет к написанию еще одно SimpleTimer. А зачем если он уже написан?
По крайней мере мне так видится на данный момент. Возможно вы сможете убедить меня изменить мнение :) Често говоря даже заинтересовало где же такое может "часто требоватся" :)
Да вы не ошиблись именно в PWM модуляции , но не в формировании самого импульса PWM , а в управлении
режимом Н моста , где на каждую половину моста подается своя PWM 1 , PWM 2
Да вы не ошиблись именно в PWM модуляции , но не в формировании самого импульса PWM , а в управлении
режимом Н моста , где на каждую половину моста подается своя PWM 1 , PWM 2
Давайте определимся, это все-таки "частная задача" которая нужно в конкретном скетче или которая кочует из скетча в скетч? При этом обсолютно разнотипных.
>режимом Н моста , где на каждую половину моста подается своя PWM 1 , PWM 2
Честно говоря не очень понял. Ну на одну ногу выдать PWM с одного пина, на другую - с другой.
Вы не можете решить вообще эту задачу, или уже решили но хотелось бы с помощью библиотеки?
Может отдельную ветку заведете на свою задачу (если еще не завели) - дайте тут на нее ссылку. Попробуем посмотреть что можно сделать. Может и моей библиотекой можно решить это, может другой, возможно вообще библиотеки не нужны :)
Задача не частная ( по крайней мере стала месяц назад , но интерес остался ) - видел во очую немецкий двигатель постоянного тока с двумя выводами управляющийся контролером и вот когда я повесил на один вывод массу осцилогрофа , а на вторую щуп , я увидел ШИМ относительно нулевой линии как положительный так и отрицательный и мне стало интересно как это так ? и при двух одинаковых шим ротор двигателя не свернуть ( как у шаговика ).
Стал размышлять и так как две шим естественно не должны пересекаться ( приведет к КЗ. Н моста) получается их надо реверсировать постоянно , но с частотой в десятки герц (ротор при этом , как тенисный мячик) далее начинаем изменять наши шим , одну увеличиваем другую уменьшаем и ротор начинает вращаться , но даже если наш шим достигнет значения 255 - за счет работы реверса до двигателя дойдет только 50% от нее вот с этого момента и нужно менять прямоугольные импульсы на несиметричные в управлении реверсом и двигатель получит все 255 (100%)
по большому счету нужна таже шим , но с очень маленькой частотой смены периуда 30-40Hz
Задача не частная ( по крайней мере стала месяц назад , но интерес остался )
Из вашего описания - это именно "частная" задача. Тут, похоже немного путаница терминов. Похоже вы "частная" поняли как синоним приватная. Вы говорите про public/private задачи, а я имел ввиду generic/concrete. Вообщем IMHO это не generic-задача. У нее не достаточный уровень абстракции что-бы решать ее в рамках библиотеки. По крайней мере этой. Если бы писали библиотеку для управления именно этим конкретным движком - тогда да. Но даже в этом случае IMHO использование подхода loop()/millis() - плохой выбор. Тут нужно смотреть в сторону Arduino - SecretsOfArduinoPWM, » Fixing the Arduino’s PWM #2 JeeLabs, Creating a variable frequency PWM output on Arduino UNO | OxGadgets и т.п.
Вообщем я бы рекомендовал - отдельную ветку завести :)
Возможно ли это действие для экономии места в памяти прописать как : PORTD |=B00 , ! PORTD |=B11 ; - естественно этот пример не правильный !
Как я понимаю PORTX - где Х является в моем случае портом B
т е пишу PORTB&(1<<ЧТО ТУТ ПРОПИСАТЬ ? PB1 ) и в двух других вместо pin тоже самое - или все вообще не так ?
Все оказалось именно так Спасибо !
И еще вопросик , а как digitalWrite(pin , HIGH) - подключить подтягивающий резистор , также заменить PORT ?
А зачем так сложно? XOR же есть готовый.
PORTB^=(1<<7) - как-то намного легче понять (работать будет только на MEGA)
2 sp34
1. Если решили работать с прямой записью в порты не нужно путать их имена. Если я правильно помню, то светик ни на одной плате не висит на PORTD. У леонардо на PORTC, у астальных PORTB (правда на разных выходах у меги и uno/nano и т.п.)
2. Экономите на спичках при этом теряете переносимость. Сможете грамотно условной компиляцией добится что-бы оно работало на uno/mega/leonardo? Когда жестко встает вопрос таймингов, или нужно синхронно несколько выходов переключать - тут понятно ради чего жертва, а в данном случае....
3. Вам, возможно проще будет это "по ардуинистому" делать. Через bitRead()/bitSet()
И главное - это как-то очень далеко от темы ветки. Переодические действия, millis() и т.п тут обсуждаются. Тут уместней было отдельную ветку открыть с вопросом, про битовые манипуляции, прямой работой с портами и проч (ну либо просто погуглить/почитать). Ну или доку Arduino - BitwiseAnd почитать.
в ATtiny 2313 - пытаюсь скетч залить - здесь и на спичках приходится экономить !
По замечаниям - исправлюсь !
А зачем так сложно? XOR же есть готовый.
PORTB^=(1<<7) - как-то намного легче понять (работать будет только на MEGA)
А зачем так сложно? XOR же есть готовый.
PORTB^=(1<<7) - как-то намного легче понять (работать будет только на MEGA)
Я тоже, раза пару наступал на эти грабли. Вначале заимплеменчу, правда не через if, а через not и битовую маску того бита что нужно поменять, потом начинаю упрощать выражение, как в школе :), и в какой-то момент вижу "блин, опять XOR реализовал через &,| и ~ :)
Вообщем-то даже не "планирую", а "написал" :). Руки только не доходят выложить и описать "как пользоватся" :(. Дам маленький спойлер как оно будет выглятеть:
Алексей, прочитал Вашу статью на blogspot. Большое спасибо за простое и понятное объяснение. Возможно я задам дурацкий вопрос, но объясните, пожалуйста, подробнее про эту конструкцию:
Почему понадобилось DO_EVERY_V_1? Почему нельзя было сразу написать:
Почему понадобилось DO_EVERY_V_1? Почему нельзя было сразу написать:
А вы попробуйте так написать :) Не происходит резолвинг __LINE__ в номер строки
Попробуйте:
Оно не компильнется (специально так сделал) - будет ругатся что "неизвестная переменная". И скажет имя переменной. Так мы сможем увидеть "во что оно развернулось".
TEST_V даст нам ошибку "prefix__LINE__ was not declared in this scope".
TEST_V1 даст нам ошибку "prefix_6" was not declared in this scope".
Как видим, только во втором случае у нас __LINE__ распознался именно как "особое ключевое слово" и мы получили, как и хотели, переменную с уникальным именем. Каждое использование TEST_V1 будем нам генерировать уникальное имя (что-бы можно было DO_EVERY несолько раз использовать).
Почему __LINE__ резолвится/мерджится с префиксом только когда его протаскиваешь через промежуточный макрос - я уже не разбирался. Нашел workaround - и ладно.
А вот еще может кому пригодится: выполнение двух действий каждые N миллисекунд, причем с заданным интервалом M миллисекунд между действиями. Такая задача, в частности, возникает в широко известном примере работы с датчиком DS18B20 когда надо раз в 1 секунду подать команду на измерение температуры, подождать 750мс и дать команду на чтение результата.
В результате нескольких экспериментов было найдено такое решение:
Я тут немного ваш макрос поправил, теперь онбудет работать без дополнительных макросов
т.е. теперь вы можете повторять его сколько угодно раз и все будет работать как в версии с 3 макросами.
Если смущает, что имя "t" слишком короткое замените его на что-то заковыристое "__lastTimeOfActionHandling".
Супер. Спасибо.
Не сообразил что можно сделать "локальную переменную" не объявляя функцию, а просто блоком кода. Привычка от web-разработки, когда частенько приходится в каких-то репитерах генерировать для js-са уникальные ID-хи. Так и тут пошел по пути "генерации уникального имени".
Но "ваш вариант" - безусловно читабельнее, удобнее в сопровождении и т.д. и т.п. Чуть позже, обязательно, сплагиачу :) его себе и выложу;)
Но "ваш вариант" - безусловно читабельнее, удобнее в сопровождении и т.д. и т.п. Чуть позже, обязательно, сплагиачу :) его себе и выложу;)
Этот вариант хорош, только если надо делать что-то маленькое строк на 10 не более.
Но если надо хендлить что-то большое то я бы пошел вообще через темплейты, если производительность для Вас критична, или через виртуальную функцию если не критична.
Причем бинарный код вырасти не должен, если компилятор не сильно кривой.
Вообщем тут целое непаханное поле для игр :-)
Ну "что-то большое" (для данного контекста) уже есть библиотека SimpleTimer готовая. А тут именно "для мелочей" задумывалось. А "игратся" - согласен можно иного.