Этюды для начинающих: мрамор и штукатурка эффективности

Seva1800
Offline
Зарегистрирован: 18.12.2015

to Arhat109-2. Если я правильно понял. goto в ряде случаев может упростить написание кода, но ухудшть понимание структуры программы. И в случае ошибки (или даже опечатки) при использовании goto потом будет проблематично отловить такой глюк.

bwn
Offline
Зарегистрирован: 25.08.2014

Seva1800 пишет:

to Arhat109-2. Если я правильно понял. goto в ряде случаев может упростить написание кода, но ухудшть понимание структуры программы. И в случае ошибки (или даже опечатки) при использовании goto потом будет проблематично отловить такой глюк.

Если не собираетесь профессионально заниматься командным программированием, то кодьте как вам самому удобно и угодно, лишь бы работало. ИМХО

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Seva1800 пишет:
А кто, когда, и почему решил, что и спользование таких вещей как goto в коде это плохой тон?
Программной индустрии не один десяток лет. И за эти десятки лет не мало усилий было направлено на выяснение, какие программные конструкции чаще всего приводяит к ошибкам и какие существенно затрудняют поддержку проекта. Так вот, GOTO на одном из первых мест в обоих списках.

ПРактика, дорогой друг, ничего кроме практики.

Цитата:
Я то всегда думал, что грамотно закомментированный код может быть каким угодно.
Это либо от недостатка образования, либо от отсутствия опыта работы в большом проекте (в котором задействованы десятки людей и совершенно недопустимо, чтобы естественная текучесть кадров негативно влияла на процесс.)
Цитата:
И все будет в порядке. Не посадят же разбираться с чужим кодом обезьяну из зоопарка. Все равно это будет человек с мозгом в голове и который разбирается в своем деле. )). Хм... Правда, тут опять "если хорошо закомментированный.." и все же хочется понять чем так плох goto.
Простой вопрос:

Вот у нас есть цикл for, где будет его окончание - выше или ниже по коду? - очевидно, ниже. Есть условный оператор, где будет альтернативная ветка? - тоже очевидно, ниже. Есть оператор выбора... и т.д.

А вот если у Вас есть фрагмент программы на несколько сотен строк и есть переход GOTO, сколько Вам понадобится времени, чтобы выяснить, куда он пошел - выше или ниже текущей позиции?

А если в программе используется несколько десятков переходов, насколько легко ее будет понять?

И опять же, существует такое понятие как самодокументируемый код. Он означает, что при грамотном оформлении для того, чтобы понятно было, что именно делает программа, комментарии вообще не нужны. Комментарии, кстати, нужны для другого - объяснить, зачем она это делает. Но особеннсти реализации того или иного алгоритма должны быть ясны и без комментариев. Если способ записи алгоритма этого не обеспечивает - в топку такой способ!

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

Ну .. с "иди-на" программа пишется просто "влоб": "сделай так, теперь этак .. а потом .. если то, сделай, нет .. ой, чегобы? А иди-на. Там разберемся" Как раз с неделю назад присылали код, с непониманием "как тут применить автоматный подход" .. вот там этих "иди-на" .. как грязи.

Структурно же, пишется не так. А примерно так:

сделай то,

сделай это .. и вот тут надо бы "условие втыкнуть" .. так вот пишем не условие, а конструкцию условного оператора, всю и целиком:

IF(){}ELSE{}

мои пальцы это выколачивают сами и уже давно. Вернулись в круглые скобки - вписали условие, зашли в блок "то" вписали действия (собственно ради них и проверку устраивали) .. и вот тут, вам как раз вырисовывание всего оператора - "делает подсказку": а ежели оно НЕ истина, то "что?". И вот вместо вашего "идина" .. тут и прописывается то что надо. Стоит только начать прописывать сразу весь оператор .. как "иди-на" перестает быть востребован. В результате получаете такую ерунду:

1 шаг: if(){}else{}

2 шаг: if( cond1 ){ ещё_сделай; }else{ ой_ошибка(1); return; } 

3 шаг (добавляем переключатель - не забываем про default!):

if( cond1 ){
  ещё_сделай;
  switch(){ default: ; }
}else{ ой_ошибка(1); return; } 

4 шаг:

if( cond1 ){
  ещё_сделай;
  switch( посмотри_тут() ){
    case 1: break;
    default: не знаю ишо;
  }
}else{ ой_ошибка(1); return; } 

...

и внезапно оказывается что .. а нет места для "иди-на".

.. ой, тут надо ТАК написать .. и тут надо тоже ТАК написать и, что два раза писать одно и тоже?!?

нет. Надо код оформит в процедуру и просто его вызвать. Присмотритесь к нему внимательно: скорее всего это некий набор обособленных действий и потребуется ещё не раз.

.. ой, if(){ if(){ if(){}else{}}else{} }else{} .. такие "вложенные проверки часто упрощаются до одной через логитческие "И" "ИЛИ", а ещё можно инвертировать условия доводя такие конструкции до банальных И-ИЛИ схем, как в ПЛИС. Теория гласит, что для любого набора условий можно их свести в И-ИЛИ матрицу из 2-х уровней.

... ну, для честности, надо сказать что место для "иди-на" таки есть: преждевременный выход из циклов и /или проброс вычислений внутри цикла к его следующему итератору. Для таких "особых" случаев, в каждом алгоритмическом языке есть 2 удобные конструкции: break и continue. При этом, помнится Дейкстра доказывал, что выход из внутреннего цикла не в его конец (не решается через break!) - свидетельствует о недостаточно продуманной структуре проги.. но, давно это было .. не ручаюсь.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Не ради холивара окаянного, а дабы не отвыкнуть. :)

ЕвгенийП пишет:
...Они выдвинули концепцию «структурного программирования» (СП), которая основывается на том факте, что любую программу можно написать с использованием всего только четырёх программных конструкций: последовательность, ветвление, цикл и вызов подпрограммы.

Собственно, по альтернативной концепции достаточно лишь двух конструкций: последовательность и условный переход. В принципе, не трудно сформулировать и концепцию с единственной конструкцией, вычисляющей адрес следующей исполняемой инструкции.

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

andriano пишет:

Не ради холивара окаянного, а дабы не отвыкнуть. :)

Не отвыкнуть холиварить? :)))))))

Seva1800
Offline
Зарегистрирован: 18.12.2015

Ну теперь-то с goto все стало кристально понятно.

Piskunov
Offline
Зарегистрирован: 13.02.2014

Уважаемые господа!

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

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

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

Logik
Offline
Зарегистрирован: 05.08.2014

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

По моделям взаимодействия: как правило клиент (клиенты)- сервер (частный случай, самое простое запрос-ответ для двух устройств) , остальные много реже: клиенты - группа серверов , одноранговая - каждый клиент служит сервером остальным. Выбор модели зависит от функций системы.

Хмм.. хотя в принципе  клиенты - группа серверов пожалуй не так и редки.

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

Piskunov пишет:

А можно ли попросить вас высказать своё мнение по оптимизации аппаратной, если можно так выразиться.

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

Другие коллеги ответят как сочтут нужным.

Piskunov
Offline
Зарегистрирован: 13.02.2014

Да-да, вот так встретятся философ с техником и составят ТЗ программисту ))

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

На самом деле конечно вопрос неоднозначный, и я, к сожалению, ещё ни разу не сталкивался со всесторонним решением таких задач. Даже хуже - бОльшая часть просто не подозревает о такой возможности. Вот я и спросил в надежде, может кто путь укажет ))

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Имеет смысл рассматривать конкретные задачи, а абстрактные оптимизировать - только время терять.

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

kisoft, я тут вчера одного орла послал ( http://arduino.ru/forum/proekty/snyatie-signalizatsii-s-arduino-rfit  ). Вы тут давно живёте, скажите. часто у Вас такая шушера околачивается?

Logik
Offline
Зарегистрирован: 05.08.2014

Piskunov пишет:

Да-да, вот так встретятся философ с техником и составят ТЗ программисту ))

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

Та не. Если чел грамотный (а это как бы по умолчанию требуется), то он не будет изобретать велосипед, а напишет над прикладным уровне OSI или на крайняк на 5-7 уровнях, и это ляжет поверх любого подходящего по производительности канала. Процесс в идеале выглядит дето так: определяются способы адресации сообщений - на основе нижележащих уровней, например айпишником, URL.. Потом список типов сообщений (например "Запрос веса" - id=10, "Ответ Вес" - id=11,...) + список полей данных, допустимых для каждого типа сообщений с указанием типов этих полей: например "Идентификатор сообщения"- байт, согласно типу сообщения, "Имя"- 20 символов уникод завершаются нулем, "Вес" - байт,... Немного уточнений, типа "формат CSV, числа в HEX" или "бинарный, фиксированной длины, младший байт первый" или "TLV согласно стандарту такомуто"... И все, можна программисту отдавать.

bwn
Offline
Зарегистрирован: 25.08.2014

ЕвгенийП пишет:

kisoft, я тут вчера одного орла послал ( http://arduino.ru/forum/proekty/snyatie-signalizatsii-s-arduino-rfit  ). Вы тут давно живёте, скажите. часто у Вас такая шушера околачивается?

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

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

bwn пишет:

помоему балбес обычный, не знающий чего хочет

Не знаю, я много тусовался на guns.ru - это форум любителей оружия. Там частенько появлялись с видом глупого лоха и вопросами типа "где можно купить" или там "нашёл, хочу продать". Были и истории, когда действительно после таких дел наиболее доверчивых форумчан заметали. Так что я после того форума "на воду дую".

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

ЕвгенийП пишет:

kisoft, я тут вчера одного орла послал ( http://arduino.ru/forum/proekty/snyatie-signalizatsii-s-arduino-rfit  ). Вы тут давно живёте, скажите. часто у Вас такая шушера околачивается?


Да вроде ничего особенного он не спрашивал. Если я правильно понял его вопрос. Те, кто знают, вряд ли скажут. Честно говоря я темы с авто обхожу принципиально мимо.

Gres
Gres аватар
Offline
Зарегистрирован: 26.03.2013

kisoft пишет:
Да вроде ничего особенного он не спрашивал. Если я правильно понял его вопрос. Те, кто знают, вряд ли скажут. Честно говоря я темы с авто обхожу принципиально мимо.

Все верно, для опытного радиотехника вся эта кухня не представляет никакой сложности, ни сигналки, ни карты разные ни прочие электронные ключи. Как говорится: "Все запоры от честных людей".

Один пройдет мимо открытой форточки, а другой не сможет.

Я, так же, как и Вы, принципиально обхожу подобные темы стороной.

madalexfiesta
madalexfiesta аватар
Offline
Зарегистрирован: 30.03.2016

В сетапе определение портов:

  for(byte i=0;i<4;i++){
    pinMode(i,OUTPUT);
    digitalWrite(i,LOW);
  }

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

Второй вопрос из этого. Стоит ли вообще использовать глобальные переменные, если обращение к ним можно сделать посредством прямого указания номера порта?

digitalWrite(1,HIGH);
arduinec
Offline
Зарегистрирован: 01.09.2015

madalexfiesta пишет:

Второй вопрос из этого. Стоит ли вообще использовать глобальные переменные, если обращение к ним можно сделать посредством прямого указания номера порта?

digitalWrite(1,HIGH);

Удобнее через define присваивать пинам названия:

#define LED1_PIN 1
digitalWrite(LED1_PIN, HIGH);

При компиляции LED1_PIN просто заменяется на 1 (память при этом не расходуется).

madalexfiesta
madalexfiesta аватар
Offline
Зарегистрирован: 30.03.2016

arduinec пишет:

Удобнее через define присваивать пинам названия:

#define LED1_PIN 1
digitalWrite(LED1_PIN, HIGH);

При компиляции LED1_PIN просто заменяется на 1 (память при этом не расходуется).

Удобней, я не спорю. Но:

#define LED1_PIN 1
......
#define LED8_PIN 8
digitalWrite(LED1_PIN, HIGH);
digitalWrite(LED2_PIN, HIGH);
digitalWrite(LED3_PIN, HIGH);
digitalWrite(LED4_PIN, HIGH);
digitalWrite(LED5_PIN, HIGH);
digitalWrite(LED6_PIN, HIGH);
digitalWrite(LED7_PIN, HIGH);
digitalWrite(LED8_PIN, HIGH);

Сожрет гораздо больше памяти, нежели предложенный мною вариант. (Это к первому)
Второе:
Так собсно в чем же удобство? Если можно напрямую забивать число экономя память.

Скажу так, за вас(это как я вижу): Если номер пина нельзя потерять в огромном коде и для удобства изменения оного манипуляцией с одной строкой,
то действительно целесообразней через define, нежели создавать интовую переменную.
Если же код мал, для экономии на повторяющихся функциях удобней  не назначать глобальные переменные вообще, а использовать циклы.
Просто привыкли к ардуинке - там памяти хватает, а вот возьмем ATtiny13 и всё, сели на попку, память приходиться выцеживать.
А о работе с плавающей запятой вообще можно забыть!( Ну это так, к слову)

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

madalexfiesta, Один вызов digitalWrite() из wiring сожрет больше кода чем прямая инициализация всех пинов УНО макросами io.h "в лоб". Так в чем же "экономия кода"? :)

arduinec
Offline
Зарегистрирован: 01.09.2015

madalexfiesta пишет:

arduinec пишет:

Удобнее через define присваивать пинам названия:

#define LED1_PIN 1
digitalWrite(LED1_PIN, HIGH);

При компиляции LED1_PIN просто заменяется на 1 (память при этом не расходуется).

Удобней, я не спорю. Но:

#define LED1_PIN 1
......
#define LED8_PIN 8
digitalWrite(LED1_PIN, HIGH);
digitalWrite(LED2_PIN, HIGH);
digitalWrite(LED3_PIN, HIGH);
digitalWrite(LED4_PIN, HIGH);
digitalWrite(LED5_PIN, HIGH);
digitalWrite(LED6_PIN, HIGH);
digitalWrite(LED7_PIN, HIGH);
digitalWrite(LED8_PIN, HIGH);

Сожрет гораздо больше памяти, нежели предложенный мною вариант. (Это к первому)
Второе:
Так собсно в чем же удобство? Если можно напрямую забивать число экономя память.

Рекомендую почитать книги по программированию на Си. Оператор #define не создаёт никаких переменных, он говорит компилятору заменить в тексте программы LED1_PIN на 1, поэтому приведённый мной пример равносилен: digitalWrite(1, HIGH);

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

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

arduinec пишет:

madalexfiesta пишет:

Сожрет гораздо больше памяти, нежели предложенный мною вариант. (Это к первому)

Рекомендую почитать книги по программированию на Си. Оператор #define не создаёт никаких переменных, он говорит компилятору заменить в тексте программы LED1_PIN на 1, поэтому приведённый мной пример равносилен: digitalWrite(1, HIGH);

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

madalexfiesta
madalexfiesta аватар
Offline
Зарегистрирован: 30.03.2016

Arhat109-2 пишет:

madalexfiesta, Один вызов digitalWrite() из wiring сожрет больше кода чем прямая инициализация всех пинов УНО макросами io.h "в лоб". Так в чем же "экономия кода"? :)

Спасибо за подсказку, углубился в этот вопрос и понял о чем Вы говорили.

Действительно работать напрямую с портами типа:

#include <avr/io.h>
int main( void )
{
DDRB=0b011110;
PORTB=0b000000;
...
}

Гораздо выгодней по ресурсам.

Заморочился на Attiny13 собрать вольтметр, и памяти, как всегда, работая с float, просто не хватало. Как только не изощрялся.  Так, собственно, и наткнулся на эту тему. Теперь же памяти хватает, даже 15% в запасе) Из скушанной памяти 48% под себя подмяла одна операция с float )))

madalexfiesta
madalexfiesta аватар
Offline
Зарегистрирован: 30.03.2016

Отсюда вопрос! Есть ли какая-нибудь возможность работать с плавающей запятой более экономней по ресурсам?

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

float в микроконтроллерах как правило не нужен вовсе. Посмотрите как у меня реализовано вычисление по обратной матрице цветности для TCS3200 .. только целочисленная арифметика и сдвиги. Точность обращения .. 0.5% .. вполне достаточно.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

madalexfiesta пишет:

Отсюда вопрос! Есть ли какая-нибудь возможность работать с плавающей запятой более экономней по ресурсам?

Задайте себе вопрос - зачем вам float, если можно легко обойтись без него? Где он востребован? Показывать температуру после запятой? Так ведь это только показывать, а вычислять и хранить температуру можно целочисленно, двумя байтами - один байт до запятой, другой - после. И никакого float не надо. И так много где можно обойтись без float. Если, конечно, будет устраивать точность.

madalexfiesta
madalexfiesta аватар
Offline
Зарегистрирован: 30.03.2016

DIYMan пишет:

madalexfiesta пишет:

Отсюда вопрос! Есть ли какая-нибудь возможность работать с плавающей запятой более экономней по ресурсам?

Задайте себе вопрос - зачем вам float, если можно легко обойтись без него? Где он востребован? Показывать температуру после запятой? Так ведь это только показывать, а вычислять и хранить температуру можно целочисленно, двумя байтами - один байт до запятой, другой - после. И никакого float не надо. И так много где можно обойтись без float. Если, конечно, будет устраивать точность.

Я не силен в математике) В программировании отдаю предпочтение java. с++ для меня лес со знакомыми деревьями. Работу со смещениями и т.п. ещё не проходил в своем обучении.

Так вот на счет float:

unsigned int analogRead_C(byte channel){ 
  ADMUX = channel; // ADC pin
  ADCSRA |= 1<<ADEN; //Включаем аналогово-цифровой конвертер
  ADCSRA |= 1<<ADSC;//конвертируем данные в 10-бит
  while(!(ADCSRA & (1<<ADIF)));
  ADCSRA |= 1<<ADIF;
  byte low  = ADCL;
  byte high = ADCH;
  ADCSRA &= ~(1 << ADEN);  // отключаем АЦП, для уменьшения энергопотребления
  return (high << 8) | low;
}

получаю аналоговые данные напряжения через делитель

Вот тут как обойтись без float?

 voltage=analogRead_C(0);
long v=(long)(voltage*1000/2231);// так не катит - хрень получается
 if(last!=voltage){...

а вот так работает

int v=voltage/2.231;

Дело в том, что по сути мне число после запятой не нужно,  но в расчетах она используется, соответственно жрет память!

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

madalexfiesta пишет:

Я не силен в математике) В программировании отдаю предпочтение 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 для выбранного типа переменной. Соответственно - переполнение со всеми вытекающими типа "хрень какая-то получается".

Юзайте правильные типы в нужных местах - и всё будет ок ;)

madalexfiesta
madalexfiesta аватар
Offline
Зарегистрирован: 30.03.2016

Спасибо, всё получилось.  Сэкономил ещё байтов.

Решение одного вопроса порождает другой)

long v=(long)(voltage*1000/2231);

long v=((long)voltage*1000/2231);

Я думал что все действия происходят в пространстве long v и voltage автоматически расширяется до long, либо используется оный int, но промежуточный результат умножения всё-таки присваивается long v.

Как так-то?) Почему моя логика не соответствует рельности?

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

madalexfiesta пишет:

Спасибо, всё получилось.  Сэкономил ещё байтов.

Решение одного вопроса порождает другой)

long v=(long)(voltage*1000/2231);

long v=((long)voltage*1000/2231);

Я думал что все действия происходят в пространстве long v и voltage автоматически расширяется до long, либо используется оный int, но промежуточный результат умножения всё-таки присваивается long v.

Как так-то?) Почему моя логика не соответствует рельности?

Потому что вы мыслите не так, как компилятор ;) Смотрите, в первом случае - вы приводите результат вычисления к типу long, что компилятор честно и сделает после вычисления. Однако, поскольку на этапе вычисления компилятор оперирует переменной, которая имеет разрядность в 2 байта - то он честно умножит, конечно, но - будет переполнение. В таких случаях компилятор предупреждает о подобном поведение варнингами, которые "заботливые" отцы Arduino IDE спрятали с глаз долой, как следствие - имеем массу кривых библиотек и кривого кода вообще.

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

Как-то так. В общем, приведение типов - это интересная штука, и с ним тоже надо обращаться правильно. К слову - есть более удобные инструменты в С++ для приведения типов - static_cast, reinterpret_cast и иже с ними - при их использовании проблем с тем, что не там привели - не будет, т.к. действие выполняется строго над переменной, в нашем случае - это может выглядеть примерно так:

long v=(reinterpret_cast<long>(voltage)*1000/2231);

 

arduinec
Offline
Зарегистрирован: 01.09.2015

Не стал открывать новую тему, решил поделиться здесь. Попалась в Википедии статья про чарлиплексинг:
https://ru.wikipedia.org/wiki/%D0%A7%D0%B0%D1%80%D0%BB%D0%B8%D0%BF%D0%BB...

 

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

тут нагляднее и без простыни текста: http://www.pighixxx.com/test/portfolio-items/charlieplexing/

кутузов
Offline
Зарегистрирован: 22.06.2016

Здравствуйте уважаемые гуру программирования, ардуину я начал осваивать недавно, познаний в программировании не много, но есть огромное желание научиться. Загорелся я идеей сделать тахометр - стробоскоп для установки зажигания ваз, для этого взял ардуино нано, и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]

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

По этому у меня вопрос что можно здесь оптимизировать ? ткните носом , только с пояснениями.
Как мне избавиться от злого "гото" и нужно ли это делать.
Заранее благодарен.
arduinec
Offline
Зарегистрирован: 01.09.2015

Сделайте отдельную функцию display(), которая будет выполнять lcd-команды (указанные после metka), и вызывайте её вместо metka и goto metka. Делать переход внутрь if - это неправильно!

Берёте себе звучные имена (кутузов) и позорите их - "не давно" пишется слитно.

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

arduinec пишет:

"не давно" пишется слитно.

Может человек имел в виду "не давно, а буквально вчера"?

кутузов
Offline
Зарегистрирован: 22.06.2016

Спасибо за ответы, тройка по русскому вылезла наружу, исправил. С функциями пока не знаком,  выучу и попрактикуюсь. Остались у пеня еще пара вопросов, задержка через счетчик float res это не ошибка, может через millis нужно было? Так же сомневаюсь я по поводу 31 строки скетча, ни чего страшного что я просто перечислил, может надо было вычислять, скажем, сложением? Как лучше для ардуино будет?

andrikll
Offline
Зарегистрирован: 04.08.2013

 Не нашел куда написать кроме офпочты, которая прислала мне мэйлделивери.

ЕвгенийП

Нельзя ли дополнить основной справочник в разделе программирования? Многие функции присутствующие на страницах других ардуиносайтов здесь не описаны.

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

А я-то каким боком? Я здесь рядовой юзер абсолютно безо всяких привилегий и дополнительных прав. 

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

на первой странице автор сайта наверное:

Электропочта для связи: electropochta@arduino.ru

 

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

Я писал туда - получал отлупы. Разместил на форуме вот такую тему - никакого ответа от администрации не получил.

Olej
Olej аватар
Offline
Зарегистрирован: 05.03.2018

andrikll пишет:

Нельзя ли дополнить основной справочник в разделе программирования? Многие функции присутствующие на страницах других ардуиносайтов здесь не описаны.

Модули avr-libc

Это?

 

Olej
Olej аватар
Offline
Зарегистрирован: 05.03.2018

ЕвгенийП пишет:

При изготовлении движущихся роботов, или при рисовании окружностей и иных вещей на экране, частенько нужно вычислять квадратный корень (теорему Пифагора не может отменить даже Госдума!).

...

Основа этого решения – библиотечная функция 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;
}

Кому интересно, подробности почитайте здесь ... смешно ...

 

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

Нет, он имел в виду, что здесь описана версия среды 0.1, а на дворе уже 8.+ Там много чего поменялось. Например, в классах EEPROM, SPI и т.п.

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

Olej пишет:

Вот, например, вычисление 1 / sqrt( x )

Забавно, но здесь форум по ардуино, поэтому, если выкладываете код, старайтесь, чтобы он на ардуино таки работал. Тот, что Вы привели на большинстве ардуин без доработки работать не будет.

Olej
Olej аватар
Offline
Зарегистрирован: 05.03.2018

ЕвгенийП пишет:

Тот, что Вы привели на большинстве ардуин без доработки работать не будет.

Хороший вопрос... А с чего бы ему не работать? Внутренние битовые представления 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 чтобы проверить (были раньше, будет завтра-послезавтра - как рабочие дни пойдут)... позже проверю. 

 

Клапауций 555
Offline
Зарегистрирован: 10.03.2018

Olej пишет:

в ATmega328 всё так же ... 

не в ATmega328, а в С++

Olej
Olej аватар
Offline
Зарегистрирован: 05.03.2018

Клапауций 555 пишет:

не в ATmega328, а в С++

Да нет, как раз именно 328 ... а GCC (avr-gcc-c++) только повторяет и использует в /usr/avr/include/*.h внутреннее регистровое представление по IEEE 754.

И так же и по другим аппаратным платформам: MIPS, x68 ... В x86 - форматы double и long double (стандарты C99, C++11) повторяют форматы аппаратных регистров FPU по IEEE 754.

 

 

Клапауций 555
Offline
Зарегистрирован: 10.03.2018

Olej пишет:

а GCC (avr-gcc-c++) только...

вот именно - и, не обязательно 328/32/16/8. и, даже, не обязательно avr. О_О