to Arhat109-2. Если я правильно понял. goto в ряде случаев может упростить написание кода, но ухудшть понимание структуры программы. И в случае ошибки (или даже опечатки) при использовании goto потом будет проблематично отловить такой глюк.
to Arhat109-2. Если я правильно понял. goto в ряде случаев может упростить написание кода, но ухудшть понимание структуры программы. И в случае ошибки (или даже опечатки) при использовании goto потом будет проблематично отловить такой глюк.
Если не собираетесь профессионально заниматься командным программированием, то кодьте как вам самому удобно и угодно, лишь бы работало. ИМХО
А кто, когда, и почему решил, что и спользование таких вещей как goto в коде это плохой тон?
Программной индустрии не один десяток лет. И за эти десятки лет не мало усилий было направлено на выяснение, какие программные конструкции чаще всего приводяит к ошибкам и какие существенно затрудняют поддержку проекта. Так вот, GOTO на одном из первых мест в обоих списках.
ПРактика, дорогой друг, ничего кроме практики.
Цитата:
Я то всегда думал, что грамотно закомментированный код может быть каким угодно.
Это либо от недостатка образования, либо от отсутствия опыта работы в большом проекте (в котором задействованы десятки людей и совершенно недопустимо, чтобы естественная текучесть кадров негативно влияла на процесс.)
Цитата:
И все будет в порядке. Не посадят же разбираться с чужим кодом обезьяну из зоопарка. Все равно это будет человек с мозгом в голове и который разбирается в своем деле. )). Хм... Правда, тут опять "если хорошо закомментированный.." и все же хочется понять чем так плох goto.
Простой вопрос:
Вот у нас есть цикл for, где будет его окончание - выше или ниже по коду? - очевидно, ниже. Есть условный оператор, где будет альтернативная ветка? - тоже очевидно, ниже. Есть оператор выбора... и т.д.
А вот если у Вас есть фрагмент программы на несколько сотен строк и есть переход GOTO, сколько Вам понадобится времени, чтобы выяснить, куда он пошел - выше или ниже текущей позиции?
А если в программе используется несколько десятков переходов, насколько легко ее будет понять?
И опять же, существует такое понятие как самодокументируемый код. Он означает, что при грамотном оформлении для того, чтобы понятно было, что именно делает программа, комментарии вообще не нужны. Комментарии, кстати, нужны для другого - объяснить, зачем она это делает. Но особеннсти реализации того или иного алгоритма должны быть ясны и без комментариев. Если способ записи алгоритма этого не обеспечивает - в топку такой способ!
Ну .. с "иди-на" программа пишется просто "влоб": "сделай так, теперь этак .. а потом .. если то, сделай, нет .. ой, чегобы? А иди-на. Там разберемся" Как раз с неделю назад присылали код, с непониманием "как тут применить автоматный подход" .. вот там этих "иди-на" .. как грязи.
Структурно же, пишется не так. А примерно так:
сделай то,
сделай это .. и вот тут надо бы "условие втыкнуть" .. так вот пишем не условие, а конструкцию условного оператора, всю и целиком:
IF(){}ELSE{}
мои пальцы это выколачивают сами и уже давно. Вернулись в круглые скобки - вписали условие, зашли в блок "то" вписали действия (собственно ради них и проверку устраивали) .. и вот тут, вам как раз вырисовывание всего оператора - "делает подсказку": а ежели оно НЕ истина, то "что?". И вот вместо вашего "идина" .. тут и прописывается то что надо. Стоит только начать прописывать сразу весь оператор .. как "иди-на" перестает быть востребован. В результате получаете такую ерунду:
и внезапно оказывается что .. а нет места для "иди-на".
.. ой, тут надо ТАК написать .. и тут надо тоже ТАК написать и, что два раза писать одно и тоже?!?
нет. Надо код оформит в процедуру и просто его вызвать. Присмотритесь к нему внимательно: скорее всего это некий набор обособленных действий и потребуется ещё не раз.
.. ой, if(){ if(){ if(){}else{}}else{} }else{} .. такие "вложенные проверки часто упрощаются до одной через логитческие "И" "ИЛИ", а ещё можно инвертировать условия доводя такие конструкции до банальных И-ИЛИ схем, как в ПЛИС. Теория гласит, что для любого набора условий можно их свести в И-ИЛИ матрицу из 2-х уровней.
... ну, для честности, надо сказать что место для "иди-на" таки есть: преждевременный выход из циклов и /или проброс вычислений внутри цикла к его следующему итератору. Для таких "особых" случаев, в каждом алгоритмическом языке есть 2 удобные конструкции: break и continue. При этом, помнится Дейкстра доказывал, что выход из внутреннего цикла не в его конец (не решается через break!) - свидетельствует о недостаточно продуманной структуре проги.. но, давно это было .. не ручаюсь.
Не ради холивара окаянного, а дабы не отвыкнуть. :)
ЕвгенийП пишет:
...Они выдвинули концепцию «структурного программирования» (СП), которая основывается на том факте, что любую программу можно написать с использованием всего только четырёх программных конструкций: последовательность, ветвление, цикл и вызов подпрограммы.
Собственно, по альтернативной концепции достаточно лишь двух конструкций: последовательность и условный переход. В принципе, не трудно сформулировать и концепцию с единственной конструкцией, вычисляющей адрес следующей исполняемой инструкции.
Ваше обсуждение проблем кодовой и алгоритмической оптимизации очень интересно и познавательно. Ну, по крайней мере для меня.
А можно ли попросить вас высказать своё мнение по оптимизации аппаратной, если можно так выразиться. Ведь реальная система редко когда состоит из одного контроллера, чаще всего их достаточно много. Не совсем ясны принципы построения таких систем, я столкнулся с двумя: в одном случае в системе много удалённых входов (в тч АЦП) и выходов (в тч ЦАП), подключенных к одному контроллеру по какому нибудь RS485-му интерфейсу, и лишь контроллер определяет алгоритм работы системы, для него же и пишется программа; а в другом случае множество более простых контроллеров общаются между собой в соответствии с какими-то своими программами, определяя алгоритм работы системы уже только в совокупности.
Мне кажется, что при выборе модели системы поле для оптимизации ещё шире, но рассматривать эти три аспекта одной и той же задачи независимо друг от друга мне представляется совсем неправильным.
В распределенных системах (а именно к ним относятся примеры), два ключевых момента - определение функций каждого компонента (вопрос вобщем то общетехнический, философский а не программистский) и разработка протокола взаимодействия - дело очень специфичное, требующее опыта и тоже может быть сделано не программистом, важна его строгость и однозначность. После этого каждое устройство становится вполне отдельно разрабатываемым, без особой специфики.
По моделям взаимодействия: как правило клиент (клиенты)- сервер (частный случай, самое простое запрос-ответ для двух устройств) , остальные много реже: клиенты - группа серверов , одноранговая - каждый клиент служит сервером остальным. Выбор модели зависит от функций системы.
Хмм.. хотя в принципе клиенты - группа серверов пожалуй не так и редки.
Да-да, вот так встретятся философ с техником и составят ТЗ программисту ))
А программист потом не может в принципе производительность шлюза увеличить, потому что протокол уже выбран. Нет бы на троих сообразить...
На самом деле конечно вопрос неоднозначный, и я, к сожалению, ещё ни разу не сталкивался со всесторонним решением таких задач. Даже хуже - бОльшая часть просто не подозревает о такой возможности. Вот я и спросил в надежде, может кто путь укажет ))
Да-да, вот так встретятся философ с техником и составят ТЗ программисту ))
А программист потом не может в принципе производительность шлюза увеличить, потому что протокол уже выбран. Нет бы на троих сообразить...
Та не. Если чел грамотный (а это как бы по умолчанию требуется), то он не будет изобретать велосипед, а напишет над прикладным уровне OSI или на крайняк на 5-7 уровнях, и это ляжет поверх любого подходящего по производительности канала. Процесс в идеале выглядит дето так: определяются способы адресации сообщений - на основе нижележащих уровней, например айпишником, URL.. Потом список типов сообщений (например "Запрос веса" - id=10, "Ответ Вес" - id=11,...) + список полей данных, допустимых для каждого типа сообщений с указанием типов этих полей: например "Идентификатор сообщения"- байт, согласно типу сообщения, "Имя"- 20 символов уникод завершаются нулем, "Вес" - байт,... Немного уточнений, типа "формат CSV, числа в HEX" или "бинарный, фиксированной длины, младший байт первый" или "TLV согласно стандарту такомуто"... И все, можна программисту отдавать.
Думаешь всерьез цепляли, помоему балбес обычный, не знающий чего хочет. Для установщиков сигналок такой трюк вроде без надобности, а не имея доступа к потрохам машины, не сделаешь.
Не знаю, я много тусовался на guns.ru - это форум любителей оружия. Там частенько появлялись с видом глупого лоха и вопросами типа "где можно купить" или там "нашёл, хочу продать". Были и истории, когда действительно после таких дел наиболее доверчивых форумчан заметали. Так что я после того форума "на воду дую".
Да вроде ничего особенного он не спрашивал. Если я правильно понял его вопрос. Те, кто знают, вряд ли скажут. Честно говоря я темы с авто обхожу принципиально мимо.
Да вроде ничего особенного он не спрашивал. Если я правильно понял его вопрос. Те, кто знают, вряд ли скажут. Честно говоря я темы с авто обхожу принципиально мимо.
Все верно, для опытного радиотехника вся эта кухня не представляет никакой сложности, ни сигналки, ни карты разные ни прочие электронные ключи. Как говорится: "Все запоры от честных людей".
Один пройдет мимо открытой форточки, а другой не сможет.
Я, так же, как и Вы, принципиально обхожу подобные темы стороной.
Гораздо экономит память, нежели использовать глобальные переменные. Думаю так можно?
Второй вопрос из этого. Стоит ли вообще использовать глобальные переменные, если обращение к ним можно сделать посредством прямого указания номера порта?
Второй вопрос из этого. Стоит ли вообще использовать глобальные переменные, если обращение к ним можно сделать посредством прямого указания номера порта?
digitalWrite(1,HIGH);
Удобнее через define присваивать пинам названия:
#define LED1_PIN 1
digitalWrite(LED1_PIN, HIGH);
При компиляции LED1_PIN просто заменяется на 1 (память при этом не расходуется).
Сожрет гораздо больше памяти, нежели предложенный мною вариант. (Это к первому)
Второе:
Так собсно в чем же удобство? Если можно напрямую забивать число экономя память.
Скажу так, за вас(это как я вижу): Если номер пина нельзя потерять в огромном коде и для удобства изменения оного манипуляцией с одной строкой,
то действительно целесообразней через define, нежели создавать интовую переменную.
Если же код мал, для экономии на повторяющихся функциях удобней не назначать глобальные переменные вообще, а использовать циклы.
Просто привыкли к ардуинке - там памяти хватает, а вот возьмем ATtiny13 и всё, сели на попку, память приходиться выцеживать.
А о работе с плавающей запятой вообще можно забыть!( Ну это так, к слову)
madalexfiesta, Один вызов digitalWrite() из wiring сожрет больше кода чем прямая инициализация всех пинов УНО макросами io.h "в лоб". Так в чем же "экономия кода"? :)
Сожрет гораздо больше памяти, нежели предложенный мною вариант. (Это к первому)
Второе:
Так собсно в чем же удобство? Если можно напрямую забивать число экономя память.
Рекомендую почитать книги по программированию на Си. Оператор #define не создаёт никаких переменных, он говорит компилятору заменить в тексте программы LED1_PIN на 1, поэтому приведённый мной пример равносилен: digitalWrite(1, HIGH);
Удобство же заключается в том, что функций с использованием LED1_PIN может быть несколько. И при необходимости поменять в них номер пина, нужно будет изменить его только в одном define.
Сожрет гораздо больше памяти, нежели предложенный мною вариант. (Это к первому)
Рекомендую почитать книги по программированию на Си. Оператор #define не создаёт никаких переменных, он говорит компилятору заменить в тексте программы LED1_PIN на 1, поэтому приведённый мной пример равносилен: digitalWrite(1, HIGH);
Вы его не поняли, я тоже сначала подумал как Вы. Потом понял, что он имел в виду то, что пачка вызовов digitalWrite сожрёт больше памяти, чем один вызов в цикле.
madalexfiesta, Один вызов digitalWrite() из wiring сожрет больше кода чем прямая инициализация всех пинов УНО макросами io.h "в лоб". Так в чем же "экономия кода"? :)
Спасибо за подсказку, углубился в этот вопрос и понял о чем Вы говорили.
Заморочился на Attiny13 собрать вольтметр, и памяти, как всегда, работая с float, просто не хватало. Как только не изощрялся. Так, собственно, и наткнулся на эту тему. Теперь же памяти хватает, даже 15% в запасе) Из скушанной памяти 48% под себя подмяла одна операция с float )))
float в микроконтроллерах как правило не нужен вовсе. Посмотрите как у меня реализовано вычисление по обратной матрице цветности для TCS3200 .. только целочисленная арифметика и сдвиги. Точность обращения .. 0.5% .. вполне достаточно.
Отсюда вопрос! Есть ли какая-нибудь возможность работать с плавающей запятой более экономней по ресурсам?
Задайте себе вопрос - зачем вам float, если можно легко обойтись без него? Где он востребован? Показывать температуру после запятой? Так ведь это только показывать, а вычислять и хранить температуру можно целочисленно, двумя байтами - один байт до запятой, другой - после. И никакого float не надо. И так много где можно обойтись без float. Если, конечно, будет устраивать точность.
Отсюда вопрос! Есть ли какая-нибудь возможность работать с плавающей запятой более экономней по ресурсам?
Задайте себе вопрос - зачем вам float, если можно легко обойтись без него? Где он востребован? Показывать температуру после запятой? Так ведь это только показывать, а вычислять и хранить температуру можно целочисленно, двумя байтами - один байт до запятой, другой - после. И никакого float не надо. И так много где можно обойтись без float. Если, конечно, будет устраивать точность.
Я не силен в математике) В программировании отдаю предпочтение java. с++ для меня лес со знакомыми деревьями. Работу со смещениями и т.п. ещё не проходил в своем обучении.
Я не силен в математике) В программировании отдаю предпочтение java. с++ для меня лес со знакомыми деревьями. Работу со смещениями и т.п. ещё не проходил в своем обучении.
Вот тут как обойтись без float?
voltage=analogRead_C(0);
long v=(long)(voltage*1000/2231);// так не катит - хрень получается
if(last!=voltage){...
а вот так работает
int v=voltage/2.231;
Дело в том, что по сути мне число после запятой не нужно, но в расчетах она используется, соответственно жрет память!
А там проблема в том, что voltage - это двухбайтовая переменная, соответственно, будут проблемы с переполнением при умножении. Навскидку, надо так:
long v=((long)voltage*1000/2231);
Вообще, правильный выбор - это включить в настройках все сообщения компилятора - тогда он покажет warning, если что-то может работать не так, как планировалось. Применительно к вашему примеру: если максимальное значение, получаемое в переменную unsigned int voltage, равно 1023, и вы перемножаете его с 1000, то получите значение, которое сильно больше максимального 65535 для выбранного типа переменной. Соответственно - переполнение со всеми вытекающими типа "хрень какая-то получается".
Юзайте правильные типы в нужных местах - и всё будет ок ;)
Я думал что все действия происходят в пространстве long v и voltage автоматически расширяется до long, либо используется оный int, но промежуточный результат умножения всё-таки присваивается long v.
Как так-то?) Почему моя логика не соответствует рельности?
Я думал что все действия происходят в пространстве long v и voltage автоматически расширяется до long, либо используется оный int, но промежуточный результат умножения всё-таки присваивается long v.
Как так-то?) Почему моя логика не соответствует рельности?
Потому что вы мыслите не так, как компилятор ;) Смотрите, в первом случае - вы приводите результат вычисления к типу long, что компилятор честно и сделает после вычисления. Однако, поскольку на этапе вычисления компилятор оперирует переменной, которая имеет разрядность в 2 байта - то он честно умножит, конечно, но - будет переполнение. В таких случаях компилятор предупреждает о подобном поведение варнингами, которые "заботливые" отцы Arduino IDE спрятали с глаз долой, как следствие - имеем массу кривых библиотек и кривого кода вообще.
Во втором же случае - мы ясно говорим компилятору, что перед вычислением надо понимать нашу двухбайтовую переменную как четырёхбайтовую, соответственно, компилятор генерирует другой код, где проблемы переполнения уже не будет. А уже результат такого вычисления присвоится переменной типа long.
Как-то так. В общем, приведение типов - это интересная штука, и с ним тоже надо обращаться правильно. К слову - есть более удобные инструменты в С++ для приведения типов - static_cast, reinterpret_cast и иже с ними - при их использовании проблем с тем, что не там привели - не будет, т.к. действие выполняется строго над переменной, в нашем случае - это может выглядеть примерно так:
long v=(reinterpret_cast<long>(voltage)*1000/2231);
Здравствуйте уважаемые гуру программирования, ардуину я начал осваивать недавно, познаний в программировании не много, но есть огромное желание научиться. Загорелся я идеей сделать тахометр - стробоскоп для установки зажигания ваз, для этого взял ардуино нано, и2с двустрочный дисплей, а так же выбрав из попавшихся на просторах интернета код подсчета оборотов который я смог понять. Поиздевался я над тем кодом изрядно в попытках решить свою задачу, вот что получилось:
[code]
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
float value = 0;
byte rev = 0;
int rpm;
int oldtime = 0;
int time;
float res = 0;
int reset = 0;
void isr() // Обработчик прерывания. ISR (interrupt service routine)
{
rev++;
}
void setup()
{
pinMode(8, OUTPUT); // выход включения светодиода
tone (7 , 50); // тестовый меандр 50 Гц
lcd.init();
lcd.backlight(); //initialize LCD
attachInterrupt(0, isr, RISING); //attaching the interrupt есть прерывание при смене значения на порту с LOW на HIGH
}
void loop()
{
reset = rev;
if (rev == 9 || rev == 18 || rev == 27 || rev == 36 || rev == 45)
{
digitalWrite(8, HIGH); // мигаем светодиодом, получается долго, надо исправлять
// delay(2); // задержка чтоб светодиод разгорелся
digitalWrite(8, LOW);
}
if (rev >= 50)
{
detachInterrupt(0); //отсоединяется прерывание detaches the interrupt
time = millis() - oldtime; //находит время finds the time
rpm = (3000000 / time); //расчета оборотов в минуту calculates rpmn
//rpm=(rev/time)*60000; //расчета оборотов в минуту calculates rpm
oldtime = millis(); //сохраняет текущее время saves the current time
metka:
lcd.clear();
lcd.setCursor(0, 0);
// lcd.print(res);
lcd.print("__TACHOMETER__");
lcd.setCursor(0, 1);
//lcd.print(" ");
lcd.print(rpm);
lcd.print(" RPM");
rev = 0;
attachInterrupt(0, isr, RISING);
//сброс тахометра на ноль если отсутствуют прерывания
}
if (rev == reset)
{
res++;
}
else {
res = 0;
}
if (res >= 270000) // количество циклов до обнуления
{
rpm = 0;
res = 0;
// delay(500);
goto metka; //переход к выводу на дисплей
}
}
[/code]
код не окончательный и будет дорабатываться по мере того как я буду набираться опыта, в планах мигать стробоскопом через регистры и добавить чуть математики чтобрассчитывать на лету следующее мигание... но это все в будущем, а сейчас раз решил осваивать, то хочется сразу правильно начинать.
По этому у меня вопрос что можно здесь оптимизировать ? ткните носом , только с пояснениями.
Как мне избавиться от злого "гото" и нужно ли это делать.
Сделайте отдельную функцию display(), которая будет выполнять lcd-команды (указанные после metka), и вызывайте её вместо metka и goto metka. Делать переход внутрь if - это неправильно!
Берёте себе звучные имена (кутузов) и позорите их - "не давно" пишется слитно.
Спасибо за ответы, тройка по русскому вылезла наружу, исправил. С функциями пока не знаком, выучу и попрактикуюсь. Остались у пеня еще пара вопросов, задержка через счетчик float res это не ошибка, может через millis нужно было? Так же сомневаюсь я по поводу 31 строки скетча, ни чего страшного что я просто перечислил, может надо было вычислять, скажем, сложением? Как лучше для ардуино будет?
При изготовлении движущихся роботов, или при рисовании окружностей и иных вещей на экране, частенько нужно вычислять квадратный корень (теорему Пифагора не может отменить даже Госдума!).
...
Основа этого решения – библиотечная функция sqrt. Мне лень сейчас лезть в исходники avr’овской библиотеки, но по опыту могу сказать, что на 95% она использует метод Ньютона, а на оставшиеся 5% – может быть разложение по формуле Тэйлора.
А ещё бывают красивые алгоритмические трюки, если хорошо поискать...
Вот, например, вычисление 1 / sqrt( x ) - раз начали тему с корня квадратного ... без всяких итераций:
float FastInvSqrt(float x) {
float xhalf = 0.5f * x;
int i = *(int*)&x; // представим биты float в виде целого числа
i = 0x5f3759df - (i >> 1); // какого черта здесь происходит ?
x = *(float*)&i;
x = x*(1.5f-(xhalf*x*x));
return x;
}
Кому интересно, подробности почитайте здесь ... смешно ...
Забавно, но здесь форум по ардуино, поэтому, если выкладываете код, старайтесь, чтобы он на ардуино таки работал. Тот, что Вы привели на большинстве ардуин без доработки работать не будет.
Тот, что Вы привели на большинстве ардуин без доработки работать не будет.
Хороший вопрос... А с чего бы ему не работать? Внутренние битовые представления float (как утверждается здесь же на Arduino.ru) на сегодня (на платформах там где они есть) стандартизованы IEEE754, а не кому как взбредётся ... насколько я предполагаю, в ATmega328 всё так же ... Да вот и в комментариях <math.h> (/usr/avr/include/math.h) видим такое:
Цитата:
... because IEEE 754 floating point allows zero to be signed.
Конечно, могут и потребоваться доработки, но они должны быть небольшими.
P.S. У меня сейчас нет Arduino чтобы проверить (были раньше, будет завтра-послезавтра - как рабочие дни пойдут)... позже проверю.
Да нет, как раз именно 328 ... а GCC (avr-gcc-c++) только повторяет и использует в /usr/avr/include/*.h внутреннее регистровое представление по IEEE 754.
И так же и по другим аппаратным платформам: MIPS, x68 ... В x86 - форматы double и long double (стандарты C99, C++11) повторяют форматы аппаратных регистров FPU по IEEE 754.
to Arhat109-2. Если я правильно понял. goto в ряде случаев может упростить написание кода, но ухудшть понимание структуры программы. И в случае ошибки (или даже опечатки) при использовании goto потом будет проблематично отловить такой глюк.
to Arhat109-2. Если я правильно понял. goto в ряде случаев может упростить написание кода, но ухудшть понимание структуры программы. И в случае ошибки (или даже опечатки) при использовании goto потом будет проблематично отловить такой глюк.
Если не собираетесь профессионально заниматься командным программированием, то кодьте как вам самому удобно и угодно, лишь бы работало. ИМХО
ПРактика, дорогой друг, ничего кроме практики.
Вот у нас есть цикл for, где будет его окончание - выше или ниже по коду? - очевидно, ниже. Есть условный оператор, где будет альтернативная ветка? - тоже очевидно, ниже. Есть оператор выбора... и т.д.
А вот если у Вас есть фрагмент программы на несколько сотен строк и есть переход GOTO, сколько Вам понадобится времени, чтобы выяснить, куда он пошел - выше или ниже текущей позиции?
А если в программе используется несколько десятков переходов, насколько легко ее будет понять?
И опять же, существует такое понятие как самодокументируемый код. Он означает, что при грамотном оформлении для того, чтобы понятно было, что именно делает программа, комментарии вообще не нужны. Комментарии, кстати, нужны для другого - объяснить, зачем она это делает. Но особеннсти реализации того или иного алгоритма должны быть ясны и без комментариев. Если способ записи алгоритма этого не обеспечивает - в топку такой способ!
Ну .. с "иди-на" программа пишется просто "влоб": "сделай так, теперь этак .. а потом .. если то, сделай, нет .. ой, чегобы? А иди-на. Там разберемся" Как раз с неделю назад присылали код, с непониманием "как тут применить автоматный подход" .. вот там этих "иди-на" .. как грязи.
Структурно же, пишется не так. А примерно так:
сделай то,
сделай это .. и вот тут надо бы "условие втыкнуть" .. так вот пишем не условие, а конструкцию условного оператора, всю и целиком:
IF(){}ELSE{}
мои пальцы это выколачивают сами и уже давно. Вернулись в круглые скобки - вписали условие, зашли в блок "то" вписали действия (собственно ради них и проверку устраивали) .. и вот тут, вам как раз вырисовывание всего оператора - "делает подсказку": а ежели оно НЕ истина, то "что?". И вот вместо вашего "идина" .. тут и прописывается то что надо. Стоит только начать прописывать сразу весь оператор .. как "иди-на" перестает быть востребован. В результате получаете такую ерунду:
и внезапно оказывается что .. а нет места для "иди-на".
.. ой, тут надо ТАК написать .. и тут надо тоже ТАК написать и, что два раза писать одно и тоже?!?
нет. Надо код оформит в процедуру и просто его вызвать. Присмотритесь к нему внимательно: скорее всего это некий набор обособленных действий и потребуется ещё не раз.
.. ой, if(){ if(){ if(){}else{}}else{} }else{} .. такие "вложенные проверки часто упрощаются до одной через логитческие "И" "ИЛИ", а ещё можно инвертировать условия доводя такие конструкции до банальных И-ИЛИ схем, как в ПЛИС. Теория гласит, что для любого набора условий можно их свести в И-ИЛИ матрицу из 2-х уровней.
... ну, для честности, надо сказать что место для "иди-на" таки есть: преждевременный выход из циклов и /или проброс вычислений внутри цикла к его следующему итератору. Для таких "особых" случаев, в каждом алгоритмическом языке есть 2 удобные конструкции: break и continue. При этом, помнится Дейкстра доказывал, что выход из внутреннего цикла не в его конец (не решается через break!) - свидетельствует о недостаточно продуманной структуре проги.. но, давно это было .. не ручаюсь.
Не ради холивара окаянного, а дабы не отвыкнуть. :)
Собственно, по альтернативной концепции достаточно лишь двух конструкций: последовательность и условный переход. В принципе, не трудно сформулировать и концепцию с единственной конструкцией, вычисляющей адрес следующей исполняемой инструкции.
Не ради холивара окаянного, а дабы не отвыкнуть. :)
Не отвыкнуть холиварить? :)))))))
Ну теперь-то с goto все стало кристально понятно.
Уважаемые господа!
Ваше обсуждение проблем кодовой и алгоритмической оптимизации очень интересно и познавательно. Ну, по крайней мере для меня.
А можно ли попросить вас высказать своё мнение по оптимизации аппаратной, если можно так выразиться. Ведь реальная система редко когда состоит из одного контроллера, чаще всего их достаточно много. Не совсем ясны принципы построения таких систем, я столкнулся с двумя: в одном случае в системе много удалённых входов (в тч АЦП) и выходов (в тч ЦАП), подключенных к одному контроллеру по какому нибудь RS485-му интерфейсу, и лишь контроллер определяет алгоритм работы системы, для него же и пишется программа; а в другом случае множество более простых контроллеров общаются между собой в соответствии с какими-то своими программами, определяя алгоритм работы системы уже только в совокупности.
Мне кажется, что при выборе модели системы поле для оптимизации ещё шире, но рассматривать эти три аспекта одной и той же задачи независимо друг от друга мне представляется совсем неправильным.
В распределенных системах (а именно к ним относятся примеры), два ключевых момента - определение функций каждого компонента (вопрос вобщем то общетехнический, философский а не программистский) и разработка протокола взаимодействия - дело очень специфичное, требующее опыта и тоже может быть сделано не программистом, важна его строгость и однозначность. После этого каждое устройство становится вполне отдельно разрабатываемым, без особой специфики.
По моделям взаимодействия: как правило клиент (клиенты)- сервер (частный случай, самое простое запрос-ответ для двух устройств) , остальные много реже: клиенты - группа серверов , одноранговая - каждый клиент служит сервером остальным. Выбор модели зависит от функций системы.
Хмм.. хотя в принципе клиенты - группа серверов пожалуй не так и редки.
А можно ли попросить вас высказать своё мнение по оптимизации аппаратной, если можно так выразиться.
Попросить-то можно, только я высказывать ничего не буду, т.к. не считаю себя компетентным в этом вопросе.
Другие коллеги ответят как сочтут нужным.
Да-да, вот так встретятся философ с техником и составят ТЗ программисту ))
А программист потом не может в принципе производительность шлюза увеличить, потому что протокол уже выбран. Нет бы на троих сообразить...
На самом деле конечно вопрос неоднозначный, и я, к сожалению, ещё ни разу не сталкивался со всесторонним решением таких задач. Даже хуже - бОльшая часть просто не подозревает о такой возможности. Вот я и спросил в надежде, может кто путь укажет ))
Имеет смысл рассматривать конкретные задачи, а абстрактные оптимизировать - только время терять.
kisoft, я тут вчера одного орла послал ( http://arduino.ru/forum/proekty/snyatie-signalizatsii-s-arduino-rfit ). Вы тут давно живёте, скажите. часто у Вас такая шушера околачивается?
Да-да, вот так встретятся философ с техником и составят ТЗ программисту ))
А программист потом не может в принципе производительность шлюза увеличить, потому что протокол уже выбран. Нет бы на троих сообразить...
Та не. Если чел грамотный (а это как бы по умолчанию требуется), то он не будет изобретать велосипед, а напишет над прикладным уровне OSI или на крайняк на 5-7 уровнях, и это ляжет поверх любого подходящего по производительности канала. Процесс в идеале выглядит дето так: определяются способы адресации сообщений - на основе нижележащих уровней, например айпишником, URL.. Потом список типов сообщений (например "Запрос веса" - id=10, "Ответ Вес" - id=11,...) + список полей данных, допустимых для каждого типа сообщений с указанием типов этих полей: например "Идентификатор сообщения"- байт, согласно типу сообщения, "Имя"- 20 символов уникод завершаются нулем, "Вес" - байт,... Немного уточнений, типа "формат CSV, числа в HEX" или "бинарный, фиксированной длины, младший байт первый" или "TLV согласно стандарту такомуто"... И все, можна программисту отдавать.
kisoft, я тут вчера одного орла послал ( http://arduino.ru/forum/proekty/snyatie-signalizatsii-s-arduino-rfit ). Вы тут давно живёте, скажите. часто у Вас такая шушера околачивается?
Думаешь всерьез цепляли, помоему балбес обычный, не знающий чего хочет. Для установщиков сигналок такой трюк вроде без надобности, а не имея доступа к потрохам машины, не сделаешь.
помоему балбес обычный, не знающий чего хочет
Не знаю, я много тусовался на guns.ru - это форум любителей оружия. Там частенько появлялись с видом глупого лоха и вопросами типа "где можно купить" или там "нашёл, хочу продать". Были и истории, когда действительно после таких дел наиболее доверчивых форумчан заметали. Так что я после того форума "на воду дую".
kisoft, я тут вчера одного орла послал ( http://arduino.ru/forum/proekty/snyatie-signalizatsii-s-arduino-rfit ). Вы тут давно живёте, скажите. часто у Вас такая шушера околачивается?
Да вроде ничего особенного он не спрашивал. Если я правильно понял его вопрос. Те, кто знают, вряд ли скажут. Честно говоря я темы с авто обхожу принципиально мимо.
Все верно, для опытного радиотехника вся эта кухня не представляет никакой сложности, ни сигналки, ни карты разные ни прочие электронные ключи. Как говорится: "Все запоры от честных людей".
Один пройдет мимо открытой форточки, а другой не сможет.
Я, так же, как и Вы, принципиально обхожу подобные темы стороной.
В сетапе определение портов:
Гораздо экономит память, нежели использовать глобальные переменные. Думаю так можно?
Второй вопрос из этого. Стоит ли вообще использовать глобальные переменные, если обращение к ним можно сделать посредством прямого указания номера порта?
Второй вопрос из этого. Стоит ли вообще использовать глобальные переменные, если обращение к ним можно сделать посредством прямого указания номера порта?
Удобнее через define присваивать пинам названия:
При компиляции LED1_PIN просто заменяется на 1 (память при этом не расходуется).
Удобнее через define присваивать пинам названия:
При компиляции LED1_PIN просто заменяется на 1 (память при этом не расходуется).
Удобней, я не спорю. Но:
Сожрет гораздо больше памяти, нежели предложенный мною вариант. (Это к первому)
Второе:
Так собсно в чем же удобство? Если можно напрямую забивать число экономя память.
Скажу так, за вас(это как я вижу): Если номер пина нельзя потерять в огромном коде и для удобства изменения оного манипуляцией с одной строкой,
то действительно целесообразней через define, нежели создавать интовую переменную.
Если же код мал, для экономии на повторяющихся функциях удобней не назначать глобальные переменные вообще, а использовать циклы.
Просто привыкли к ардуинке - там памяти хватает, а вот возьмем ATtiny13 и всё, сели на попку, память приходиться выцеживать.
А о работе с плавающей запятой вообще можно забыть!( Ну это так, к слову)
madalexfiesta, Один вызов digitalWrite() из wiring сожрет больше кода чем прямая инициализация всех пинов УНО макросами io.h "в лоб". Так в чем же "экономия кода"? :)
Удобнее через define присваивать пинам названия:
При компиляции LED1_PIN просто заменяется на 1 (память при этом не расходуется).
Удобней, я не спорю. Но:
Сожрет гораздо больше памяти, нежели предложенный мною вариант. (Это к первому)
Второе:
Так собсно в чем же удобство? Если можно напрямую забивать число экономя память.
Рекомендую почитать книги по программированию на Си. Оператор #define не создаёт никаких переменных, он говорит компилятору заменить в тексте программы LED1_PIN на 1, поэтому приведённый мной пример равносилен: digitalWrite(1, HIGH);
Удобство же заключается в том, что функций с использованием LED1_PIN может быть несколько. И при необходимости поменять в них номер пина, нужно будет изменить его только в одном define.
Сожрет гораздо больше памяти, нежели предложенный мною вариант. (Это к первому)
Рекомендую почитать книги по программированию на Си. Оператор #define не создаёт никаких переменных, он говорит компилятору заменить в тексте программы LED1_PIN на 1, поэтому приведённый мной пример равносилен: digitalWrite(1, HIGH);
Вы его не поняли, я тоже сначала подумал как Вы. Потом понял, что он имел в виду то, что пачка вызовов digitalWrite сожрёт больше памяти, чем один вызов в цикле.
madalexfiesta, Один вызов digitalWrite() из wiring сожрет больше кода чем прямая инициализация всех пинов УНО макросами io.h "в лоб". Так в чем же "экономия кода"? :)
Спасибо за подсказку, углубился в этот вопрос и понял о чем Вы говорили.
Действительно работать напрямую с портами типа:
Гораздо выгодней по ресурсам.
Заморочился на Attiny13 собрать вольтметр, и памяти, как всегда, работая с float, просто не хватало. Как только не изощрялся. Так, собственно, и наткнулся на эту тему. Теперь же памяти хватает, даже 15% в запасе) Из скушанной памяти 48% под себя подмяла одна операция с float )))
Отсюда вопрос! Есть ли какая-нибудь возможность работать с плавающей запятой более экономней по ресурсам?
float в микроконтроллерах как правило не нужен вовсе. Посмотрите как у меня реализовано вычисление по обратной матрице цветности для TCS3200 .. только целочисленная арифметика и сдвиги. Точность обращения .. 0.5% .. вполне достаточно.
Отсюда вопрос! Есть ли какая-нибудь возможность работать с плавающей запятой более экономней по ресурсам?
Задайте себе вопрос - зачем вам float, если можно легко обойтись без него? Где он востребован? Показывать температуру после запятой? Так ведь это только показывать, а вычислять и хранить температуру можно целочисленно, двумя байтами - один байт до запятой, другой - после. И никакого float не надо. И так много где можно обойтись без float. Если, конечно, будет устраивать точность.
Отсюда вопрос! Есть ли какая-нибудь возможность работать с плавающей запятой более экономней по ресурсам?
Задайте себе вопрос - зачем вам float, если можно легко обойтись без него? Где он востребован? Показывать температуру после запятой? Так ведь это только показывать, а вычислять и хранить температуру можно целочисленно, двумя байтами - один байт до запятой, другой - после. И никакого float не надо. И так много где можно обойтись без float. Если, конечно, будет устраивать точность.
Я не силен в математике) В программировании отдаю предпочтение java. с++ для меня лес со знакомыми деревьями. Работу со смещениями и т.п. ещё не проходил в своем обучении.
Так вот на счет float:
получаю аналоговые данные напряжения через делитель
Вот тут как обойтись без float?
а вот так работает
Дело в том, что по сути мне число после запятой не нужно, но в расчетах она используется, соответственно жрет память!
Я не силен в математике) В программировании отдаю предпочтение java. с++ для меня лес со знакомыми деревьями. Работу со смещениями и т.п. ещё не проходил в своем обучении.
Вот тут как обойтись без float?
а вот так работает
Дело в том, что по сути мне число после запятой не нужно, но в расчетах она используется, соответственно жрет память!
А там проблема в том, что voltage - это двухбайтовая переменная, соответственно, будут проблемы с переполнением при умножении. Навскидку, надо так:
Вообще, правильный выбор - это включить в настройках все сообщения компилятора - тогда он покажет warning, если что-то может работать не так, как планировалось. Применительно к вашему примеру: если максимальное значение, получаемое в переменную unsigned int voltage, равно 1023, и вы перемножаете его с 1000, то получите значение, которое сильно больше максимального 65535 для выбранного типа переменной. Соответственно - переполнение со всеми вытекающими типа "хрень какая-то получается".
Юзайте правильные типы в нужных местах - и всё будет ок ;)
Спасибо, всё получилось. Сэкономил ещё байтов.
Решение одного вопроса порождает другой)
long
v=((
long
)voltage*1000/2231);
Я думал что все действия происходят в пространстве long v и voltage автоматически расширяется до long, либо используется оный int, но промежуточный результат умножения всё-таки присваивается long v.
Как так-то?) Почему моя логика не соответствует рельности?
Спасибо, всё получилось. Сэкономил ещё байтов.
Решение одного вопроса порождает другой)
long
v=((
long
)voltage*1000/2231);
Я думал что все действия происходят в пространстве long v и voltage автоматически расширяется до long, либо используется оный int, но промежуточный результат умножения всё-таки присваивается long v.
Как так-то?) Почему моя логика не соответствует рельности?
Потому что вы мыслите не так, как компилятор ;) Смотрите, в первом случае - вы приводите результат вычисления к типу long, что компилятор честно и сделает после вычисления. Однако, поскольку на этапе вычисления компилятор оперирует переменной, которая имеет разрядность в 2 байта - то он честно умножит, конечно, но - будет переполнение. В таких случаях компилятор предупреждает о подобном поведение варнингами, которые "заботливые" отцы Arduino IDE спрятали с глаз долой, как следствие - имеем массу кривых библиотек и кривого кода вообще.
Во втором же случае - мы ясно говорим компилятору, что перед вычислением надо понимать нашу двухбайтовую переменную как четырёхбайтовую, соответственно, компилятор генерирует другой код, где проблемы переполнения уже не будет. А уже результат такого вычисления присвоится переменной типа long.
Как-то так. В общем, приведение типов - это интересная штука, и с ним тоже надо обращаться правильно. К слову - есть более удобные инструменты в С++ для приведения типов - static_cast, reinterpret_cast и иже с ними - при их использовании проблем с тем, что не там привели - не будет, т.к. действие выполняется строго над переменной, в нашем случае - это может выглядеть примерно так:
Не стал открывать новую тему, решил поделиться здесь. Попалась в Википедии статья про чарлиплексинг:
https://ru.wikipedia.org/wiki/%D0%A7%D0%B0%D1%80%D0%BB%D0%B8%D0%BF%D0%BB...
тут нагляднее и без простыни текста: http://www.pighixxx.com/test/portfolio-items/charlieplexing/
Здравствуйте уважаемые гуру программирования, ардуину я начал осваивать недавно, познаний в программировании не много, но есть огромное желание научиться. Загорелся я идеей сделать тахометр - стробоскоп для установки зажигания ваз, для этого взял ардуино нано, и2с двустрочный дисплей, а так же выбрав из попавшихся на просторах интернета код подсчета оборотов который я смог понять. Поиздевался я над тем кодом изрядно в попытках решить свою задачу, вот что получилось:
код не окончательный и будет дорабатываться по мере того как я буду набираться опыта, в планах мигать стробоскопом через регистры и добавить чуть математики чтобрассчитывать на лету следующее мигание... но это все в будущем, а сейчас раз решил осваивать, то хочется сразу правильно начинать.
Сделайте отдельную функцию display(), которая будет выполнять lcd-команды (указанные после metka), и вызывайте её вместо metka и goto metka. Делать переход внутрь if - это неправильно!
Берёте себе звучные имена (кутузов) и позорите их - "не давно" пишется слитно.
"не давно" пишется слитно.
Может человек имел в виду "не давно, а буквально вчера"?
Спасибо за ответы, тройка по русскому вылезла наружу, исправил. С функциями пока не знаком, выучу и попрактикуюсь. Остались у пеня еще пара вопросов, задержка через счетчик float res это не ошибка, может через millis нужно было? Так же сомневаюсь я по поводу 31 строки скетча, ни чего страшного что я просто перечислил, может надо было вычислять, скажем, сложением? Как лучше для ардуино будет?
Не нашел куда написать кроме офпочты, которая прислала мне мэйлделивери.
ЕвгенийП
Нельзя ли дополнить основной справочник в разделе программирования? Многие функции присутствующие на страницах других ардуиносайтов здесь не описаны.
А я-то каким боком? Я здесь рядовой юзер абсолютно безо всяких привилегий и дополнительных прав.
на первой странице автор сайта наверное:
Электропочта для связи: electropochta@arduino.ru
Я писал туда - получал отлупы. Разместил на форуме вот такую тему - никакого ответа от администрации не получил.
Нельзя ли дополнить основной справочник в разделе программирования? Многие функции присутствующие на страницах других ардуиносайтов здесь не описаны.
Модули avr-libc
Это?
При изготовлении движущихся роботов, или при рисовании окружностей и иных вещей на экране, частенько нужно вычислять квадратный корень (теорему Пифагора не может отменить даже Госдума!).
...
Основа этого решения – библиотечная функция sqrt. Мне лень сейчас лезть в исходники avr’овской библиотеки, но по опыту могу сказать, что на 95% она использует метод Ньютона, а на оставшиеся 5% – может быть разложение по формуле Тэйлора.
А ещё бывают красивые алгоритмические трюки, если хорошо поискать...
Вот, например, вычисление 1 / sqrt( x ) - раз начали тему с корня квадратного ... без всяких итераций:
Кому интересно, подробности почитайте здесь ... смешно ...
Нет, он имел в виду, что здесь описана версия среды 0.1, а на дворе уже 8.+ Там много чего поменялось. Например, в классах EEPROM, SPI и т.п.
Вот, например, вычисление 1 / sqrt( x )
Забавно, но здесь форум по ардуино, поэтому, если выкладываете код, старайтесь, чтобы он на ардуино таки работал. Тот, что Вы привели на большинстве ардуин без доработки работать не будет.
Тот, что Вы привели на большинстве ардуин без доработки работать не будет.
Хороший вопрос... А с чего бы ему не работать? Внутренние битовые представления float (как утверждается здесь же на Arduino.ru) на сегодня (на платформах там где они есть) стандартизованы IEEE754, а не кому как взбредётся ... насколько я предполагаю, в ATmega328 всё так же ... Да вот и в комментариях <math.h> (/usr/avr/include/math.h) видим такое:
Конечно, могут и потребоваться доработки, но они должны быть небольшими.
P.S. У меня сейчас нет Arduino чтобы проверить (были раньше, будет завтра-послезавтра - как рабочие дни пойдут)... позже проверю.
в ATmega328 всё так же ...
не в ATmega328, а в С++
не в ATmega328, а в С++
Да нет, как раз именно 328 ... а GCC (avr-gcc-c++) только повторяет и использует в /usr/avr/include/*.h внутреннее регистровое представление по IEEE 754.
И так же и по другим аппаратным платформам: MIPS, x68 ... В x86 - форматы double и long double (стандарты C99, C++11) повторяют форматы аппаратных регистров FPU по IEEE 754.
а GCC (avr-gcc-c++) только...
вот именно - и, не обязательно 328/32/16/8. и, даже, не обязательно avr. О_О