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

Gippopotam
Gippopotam аватар
Offline
Зарегистрирован: 12.09.2014

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

Gippopotam пишет:

Главное продолжайте тему.

Да не знаю. Те, кому это было бы полезно - не читают. Уже третий этю и не было ни одного вопроса от "целевой аудитории".

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

Мне бы пофигу, но беда в том, что целевая аудитория как раз не читает вовсе, а тогда какой смысл писать?

Я читаю. Остальные прочитают, когда придет время.

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

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

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

А может быть Вы не совсем верно определили "целевую аудиторию"? :-)

Вона как возбудились, трясут седыми я... регалиями, костылями размахивают!

От себя лично хотел бы попросить Вас несколько подробнее остановиться на оптимизации деления, а особенно на вычислении остатка.

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

Arhat109-2 пишет:

Много где в рекомендациях к программированию для Ардуино пишут о том, что номера пинов правильнее определять как константы через #define, а не как переменные в памяти через int или другой целый тип. Типа:

#define pinLed 13 // так правильно
int        pinLed = 13; // так НЕ правильно
byte     pinLed = 13; // так и с любым иным типом тоже НЕ правильно.

А про вариант "const byte pinLed = 13;" что скажете?

Tomasina
Tomasina аватар
Offline
Зарегистрирован: 09.03.2013
int led = 13;
void setup() {pinMode(led, OUTPUT);}
void loop() {digitalWrite(led, HIGH); delay(1000); digitalWrite(led, LOW); delay(1000); }
>Размер скетча в двоичном коде: 1 108 байт (из 14 336 байт максимум)
 
 
byte led = 13;
void setup() {pinMode(led, OUTPUT);}
void loop() {digitalWrite(led, HIGH); delay(1000); digitalWrite(led, LOW); delay(1000); }

>Размер скетча в двоичном коде: 1 108 байт (из 14 336 байт максимум)

 
const int led = 13;
void setup() {pinMode(led, OUTPUT);}
void loop() {digitalWrite(led, HIGH); delay(1000); digitalWrite(led, LOW); delay(1000); }

>Размер скетча в двоичном коде: 1 100 байт (из 14 336 байт максимум)

 
const byte led = 13;
void setup() {pinMode(led, OUTPUT);}
void loop() {digitalWrite(led, HIGH); delay(1000); digitalWrite(led, LOW); delay(1000); }

>Размер скетча в двоичном коде: 1 100 байт (из 14 336 байт максимум)

 
#define led 13
void setup() {pinMode(led, OUTPUT);}
void loop() {digitalWrite(led, HIGH); delay(1000); digitalWrite(led, LOW); delay(1000); }

Размер скетча в двоичном коде: 1 100 байт (из 14 336 байт максимум)

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

 

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

Не, примеров я приводить не буду, ибо ленив до неимоверности .. увы, но сами, сами...

Обойтись можно легко: взять либу arhat.h .. там в "автономном" режиме компиляции (без wiring) все эти функции переопределены как возвращающие uint16_t, так шта - даже менять ничего не требуется.

Ну а "блин без делай" там ваще организован на счетчике переполнений и называется everyOvf() .. он даже millis() не требует. Да и вся автоматная часть реализована через тип TSC_Time, который .. опять же uint16_t. :)

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

Вы не поняли. в ваших примерах используются функции из wiring. Они и требуют 1100 байт. В atmel это 3 команды, не считая delay() (он ещё 4-6, не помню). Итого в правильном варианте этот код занимает .. менее 20 байт. :)

Проверить можно так:

#include "arhat.h"
..
ну и далее по тексту.

в итого сейчас получите 368 байт или около того (минимально 490 для Мега2560), ибо delay() реализован через таймер, а он теперь "вумный" и позволяет исполнять из под себя функции юзверя без остановки счета времени. :)

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

Piskunov: "А про вариант "const byte pinLed = 13;" что скажете?"

Согласно стандарту, ежели мне не изменяет память, то должно быть эквивалентно #define, но как это компиляет avr-gcc - не смотрел за ненадобностью. Ранние компиляторы с С++ так и делали: заменяли все ООП прибамбасы опциями препроцессора и не парились. :)

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

Arhat109-2 пишет:

Piskunov: "А про вариант "const byte pinLed = 13;" что скажете?"

Согласно стандарту, ежели мне не изменяет память, то должно быть эквивалентно #define

Слово "эквивалентно" здесь не уместно. У этих записей иногда (в частных случаях) совпадает семантика, но по сути они принципиально разные. Описание "const ... " определяет именно константу, а "#define ..." орпеделяет контекст подстановки.

Для того, чтобы прочувствовать разницу, посмотрите на вот такой пример:

#define	A	2+3
const int a = 2+3;

void setup() {
	Serial.begin(115200);
	Serial.println(A*2);
	Serial.println(a*2);
}

void loop() {}

 

Andrey12
Andrey12 аватар
Offline
Зарегистрирован: 26.12.2014

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

Gippopotam пишет:

Главное продолжайте тему.

Да не знаю. Те, кому это было бы полезно - не читают. Уже третий этю и не было ни одного вопроса от "целевой аудитории".

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

Мне бы пофигу, но беда в том, что целевая аудитория как раз не читает вовсе, а тогда какой смысл писать?

Как не читают? Еще как читают. А то что читает и не целевая аудитория тоже хорошо, каждый из прочитанного делает собственные выводы. Это еще Пухлявый кудато пропал, если его сюда вернуть то воды и маразма былоб в разы больше, но было бы и веселее, а то мы тут без него как дума без Милонова, Мизулиной, Жириновского.

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

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

И без обид но вот Arhat109-2 пишет полезные вещи в плане оптимизации, но чтоб понять перечитываю несколько раз. Видимо для меня оптимизация это не просто. Или просто много специфики.

 

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

Ну уж звиняйте за косноязычность .. обучением студентов последний раз занимался в году так 1992-м. :)

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

Это понятно. Там был контекст определения констант, а не подстановок. У меня #define юзается и не в таком ключе.. :)

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

Arhat109-2 пишет:

Согласно стандарту, ежели мне не изменяет память, то должно быть эквивалентно #define, но как это компиляет avr-gcc - не смотрел за ненадобностью. Ранние компиляторы с С++ так и делали: заменяли все ООП прибамбасы опциями препроцессора и не парились. :)

Не так давно закончил чтение известной книги Скотта Мэйерса (первый раз).

Цитата оттуда: "Правило 2: Предпочитайте const, enum и inline использованию #define"

Я зря потратил время??? :-((

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

Piskunov пишет:

Цитата оттуда: "Правило 2: Предпочитайте const, enum и inline использованию #define"

Я зря потратил время??? :-((

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

Вы же помните бессмертную цитату из Милна (цитирую по памяти): "Всякому овощу своё время и своя грядка - сказала герцогиня - но если тебе это трудно понять, я могу объяснить проще: Никогда не думай, что ты иная, чем могла бы быть, иначе как будучи иной в тех случаях, когда иною нельзя не быть" :-))))

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

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

а тут все наоборот: место и скорость - главнее "красоты текста". И часто те самые 100байт за красоту вам не позволят применить микроконтроллер меньшей линейки. Абыдна, правда? :)

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

Ещё могу порекомендовать Майерса Гленфорда .. у него тоже много полезных книг, как и у Скотта Д. Майерса. :)

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

Внесу-ка и я свою лепту в обсуждение и приведу в качестве примера с оптимизацией программку с define и const.

Программка дергает пин с частотой 8 МГц (при частоте Ардуины 16 МГц):

#define DATA_PIN 7

#if DATA_PIN > 7
#define P_PORT PORTB
#else
#define P_PORT PORTD
#endif

const byte P_BIT1 = 1U << (DATA_PIN % 8);
const byte P_BIT0 = ~P_BIT1;

void setup()
{
  pinMode(DATA_PIN, OUTPUT);
  digitalWrite(DATA_PIN, LOW);
}

void loop()
{
metka:
  P_PORT |= P_BIT1;  // High
  P_PORT &= P_BIT0;  // Low
  goto metka;
}

Осциллограф показал интервал между импульсами 0,12 мкс (= 8 МГц), то есть цикл выполняется за 2 такта.

Комбинация из if-define в начале скетча позволяет использовать любой дата-пин от 0 до 13 (на всех не проверял) на Arduino Uno и Nano.

 

vk007
Offline
Зарегистрирован: 16.06.2015

Зачем строки 20 и 23 с goto???

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

Да, именно такой подход или похожий (смотрите реализации таких "автоподстановок" в Cyberlib или arhat.h) и должен был быть реализован в wiring изначально. Развитие этого метода позволяет писать так:

#define DATA_PIN 7

#define LOW 0
#define HIGH 1
	 
#define P_PORT(pin)  ((pin)>7? PORTB : PORTD)
#define P_BIT1(pin) = (1U << ((pin) % 8));
#define P_BIT0(pin) = ~P_BIT1(pin);

// и теперь:
#define digitalWrite(pin, val)   ((val)==HIGH? P_PORT(pin) |= P_BIT1(pin) : P_PORT(pin) &= P_BIT0(pin))

void setup()
{
  pinMode(DATA_PIN, OUTPUT);
  digitalWrite(DATA_PIN, LOW);
}
	 
void loop()
{
  digitalWrite(DATA_PIN, HIGH);
  digitalWrite(DATA_PIN, LOW);
}

 .. и получить понятный, компактный и шустрый скетч .. что и сделано в arhat.h только с возможностью всего арсенала ног Мега2560. :)

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

vk007 пишет:

Зачем строки 20 и 23 с goto???

С goto получается быстрее.

При повторной проверке обнаружил, что у моего старого осциллографа барахлит переключатель диапазонов "мкс", поэтому за 8 МГц теперь не ручаюсь. Частотометра у меня нет.

Проверьте, у кого есть качественное оборудование, с какой частотой дергается пин.

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

Arhat109-2 пишет:

Да, именно такой подход или похожий (смотрите реализации таких "автоподстановок" в Cyberlib или arhat.h) и должен был быть реализован в wiring изначально. Развитие этого метода позволяет писать так:

#define DATA_PIN 7

#define LOW 0
#define HIGH 1
	 
#define P_PORT(pin)  ((pin)>7? PORTB : PORTD)
#define P_BIT1(pin) = (1U << ((pin) % 8));
#define P_BIT0(pin) = ~P_BIT1(pin);

// и теперь:
#define digitalWrite(pin, val)   ((val)==HIGH? P_PORT(pin) |= P_BIT1(pin) : P_PORT(pin) &= P_BIT0(pin))

void setup()
{
  pinMode(DATA_PIN, OUTPUT);
  digitalWrite(DATA_PIN, LOW);
}
	 
void loop()
{
  digitalWrite(DATA_PIN, HIGH);
  digitalWrite(DATA_PIN, LOW);
}

 .. и получить понятный, компактный и шустрый скетч .. что и сделано в arhat.h только с возможностью всего арсенала ног Мега2560. :)

Не работает ((

Arduino: 1.6.5 (Windows 8.1), Плата"Arduino/Genuino Uno"

sketch_dec14a:11: error: expected ')' before '==' token
C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino/Arduino.h:126:6: note: in expansion of macro 'digitalWrite'
 void digitalWrite(uint8_t, uint8_t);
      ^
sketch_dec14a:11: error: expected unqualified-id before ':' token
C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino/Arduino.h:126:6: note: in expansion of macro 'digitalWrite'
 void digitalWrite(uint8_t, uint8_t);
      ^
sketch_dec14a:11: error: expected unqualified-id before ')' token
C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino/Arduino.h:126:6: note: in expansion of macro 'digitalWrite'
 void digitalWrite(uint8_t, uint8_t);
      ^
sketch_dec14a.ino: In function 'void setup()':
sketch_dec14a:7: error: expected primary-expression before '=' token
sketch_dec14a.ino:11:63: note: in expansion of macro 'P_BIT1'
sketch_dec14a.ino:16:3: note: in expansion of macro 'digitalWrite'
sketch_dec14a:7: error: expected ':' before ';' token
sketch_dec14a.ino:11:63: note: in expansion of macro 'P_BIT1'
sketch_dec14a.ino:16:3: note: in expansion of macro 'digitalWrite'
sketch_dec14a:7: error: expected primary-expression before ';' token
sketch_dec14a.ino:11:63: note: in expansion of macro 'P_BIT1'
sketch_dec14a.ino:16:3: note: in expansion of macro 'digitalWrite'
sketch_dec14a:7: error: expected ')' before ';' token
sketch_dec14a.ino:11:63: note: in expansion of macro 'P_BIT1'
sketch_dec14a.ino:16:3: note: in expansion of macro 'digitalWrite'
sketch_dec14a:11: error: expected primary-expression before ':' token
sketch_dec14a.ino:16:3: note: in expansion of macro 'digitalWrite'
sketch_dec14a:11: error: expected ';' before ':' token
sketch_dec14a.ino:16:3: note: in expansion of macro 'digitalWrite'
sketch_dec14a:11: error: expected primary-expression before ')' token
sketch_dec14a.ino:16:3: note: in expansion of macro 'digitalWrite'
sketch_dec14a:11: error: expected ';' before ')' token
sketch_dec14a.ino:16:3: note: in expansion of macro 'digitalWrite'
sketch_dec14a.ino: In function 'void loop()':
sketch_dec14a:7: error: expected primary-expression before '=' token
sketch_dec14a.ino:11:63: note: in expansion of macro 'P_BIT1'
sketch_dec14a.ino:21:3: note: in expansion of macro 'digitalWrite'
sketch_dec14a:7: error: expected ':' before ';' token
sketch_dec14a.ino:11:63: note: in expansion of macro 'P_BIT1'
sketch_dec14a.ino:21:3: note: in expansion of macro 'digitalWrite'
sketch_dec14a:7: error: expected primary-expression before ';' token
sketch_dec14a.ino:11:63: note: in expansion of macro 'P_BIT1'
sketch_dec14a.ino:21:3: note: in expansion of macro 'digitalWrite'
sketch_dec14a:7: error: expected ')' before ';' token
sketch_dec14a.ino:11:63: note: in expansion of macro 'P_BIT1'
sketch_dec14a.ino:21:3: note: in expansion of macro 'digitalWrite'
sketch_dec14a:11: error: expected primary-expression before ':' token
sketch_dec14a.ino:21:3: note: in expansion of macro 'digitalWrite'
sketch_dec14a:11: error: expected ';' before ':' token
sketch_dec14a.ino:21:3: note: in expansion of macro 'digitalWrite'
sketch_dec14a:11: error: expected primary-expression before ')' token
sketch_dec14a.ino:21:3: note: in expansion of macro 'digitalWrite'
sketch_dec14a:11: error: expected ';' before ')' token
sketch_dec14a.ino:21:3: note: in expansion of macro 'digitalWrite'
sketch_dec14a:7: error: expected primary-expression before '=' token
sketch_dec14a.ino:11:63: note: in expansion of macro 'P_BIT1'
sketch_dec14a.ino:22:3: note: in expansion of macro 'digitalWrite'
sketch_dec14a:7: error: expected ':' before ';' token
sketch_dec14a.ino:11:63: note: in expansion of macro 'P_BIT1'
sketch_dec14a.ino:22:3: note: in expansion of macro 'digitalWrite'
sketch_dec14a:7: error: expected primary-expression before ';' token
sketch_dec14a.ino:11:63: note: in expansion of macro 'P_BIT1'
sketch_dec14a.ino:22:3: note: in expansion of macro 'digitalWrite'
sketch_dec14a:7: error: expected ')' before ';' token
sketch_dec14a.ino:11:63: note: in expansion of macro 'P_BIT1'
sketch_dec14a.ino:22:3: note: in expansion of macro 'digitalWrite'
sketch_dec14a:11: error: expected primary-expression before ':' token
sketch_dec14a.ino:22:3: note: in expansion of macro 'digitalWrite'
sketch_dec14a:11: error: expected ';' before ':' token
sketch_dec14a.ino:22:3: note: in expansion of macro 'digitalWrite'
sketch_dec14a:11: error: expected primary-expression before ')' token
sketch_dec14a.ino:22:3: note: in expansion of macro 'digitalWrite'
sketch_dec14a:11: error: expected ';' before ')' token
sketch_dec14a.ino:22:3: note: in expansion of macro 'digitalWrite'
expected ')' before '==' token

  Это сообщение будет содержать больше информации чем
  "Отображать вывод во время компиляции"
  включено в Файл > Настройки

 

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

C Arduino.h и не будет работать. :)

Первой строкой добавьте

#define Arduino_h 1

И если в скетче ничего более нет - должно работать.

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

Всё равно не работает. Правда сообщений об ошибках стало меньше ))

#define Arduino_h 1
#define DATA_PIN 7

#define LOW 0
#define HIGH 1
   
#define P_PORT(pin)  ((pin)>7? PORTB : PORTD)
#define P_BIT1(pin) /*=*/ (1U << ((pin) % 8))//; //часть символов убрал
#define P_BIT0(pin) /*=*/ ~P_BIT1(pin)//;

// и теперь:
#define digitalWrite(pin, val)   ((val)==HIGH? P_PORT(pin) |= P_BIT1(pin) : P_PORT(pin) &= P_BIT0(pin))

void setup()
{
  pinMode(DATA_PIN, OUTPUT);
  digitalWrite(DATA_PIN, LOW);
}
   
void loop()
{
  digitalWrite(DATA_PIN, HIGH);
  digitalWrite(DATA_PIN, LOW);
}

Вот ошибки:

Arduino: 1.6.5 (Windows 8.1), Плата"Arduino/Genuino Uno"

sketch_dec14a.ino: In function 'void setup()':
sketch_dec14a:16: error: 'OUTPUT' was not declared in this scope
sketch_dec14a:16: error: 'pinMode' was not declared in this scope
sketch_dec14a:7: error: 'PORTB' was not declared in this scope
sketch_dec14a.ino:12:48: note: in expansion of macro 'P_PORT'
sketch_dec14a.ino:17:3: note: in expansion of macro 'digitalWrite'
sketch_dec14a:7: error: 'PORTD' was not declared in this scope
sketch_dec14a.ino:12:48: note: in expansion of macro 'P_PORT'
sketch_dec14a.ino:17:3: note: in expansion of macro 'digitalWrite'
sketch_dec14a.ino: In function 'void loop()':
sketch_dec14a:7: error: 'PORTB' was not declared in this scope
sketch_dec14a.ino:12:48: note: in expansion of macro 'P_PORT'
sketch_dec14a.ino:22:3: note: in expansion of macro 'digitalWrite'
sketch_dec14a:7: error: 'PORTD' was not declared in this scope
sketch_dec14a.ino:12:48: note: in expansion of macro 'P_PORT'
sketch_dec14a.ino:22:3: note: in expansion of macro 'digitalWrite'
'OUTPUT' was not declared in this scope

  Это сообщение будет содержать больше информации чем
  "Отображать вывод во время компиляции"
  включено в Файл > Настройки

 

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

arduinec пишет:

Внесу-ка и я свою лепту в обсуждение и приведу в качестве примера

Отличная примера!! Так и пишет здоровая часть общества. Сам бы так писал. Токо вместо 1U << ( написал бы _BV( - это системный макрос разворачивающийся в то же самое. Вместо P_PORT |= P_BIT1; написал бы скорей bitSet( или sbi( то же макросы тоже разварачивается в такое же.  А #if DATA_PIN > 7 писать бы не стал... я бы его скопипастил из другого проекта, он у  меня так выглядит #define  ARDUINO_PORT(a)    ((a<8)?PORTD:(a<14?PORTB:PORTC)).

Обратите внимание, оптимизация применена именно там где надо, а где не надо - там не надо. И пусть digitalWrite( тупит себе как хочет, на 8МГц не влияет. В этом как бы намек на то что в реальном проекте усилия на оптимизацию должны быть приложены именно там, где они дадут результат. В контексте ардуинки это как раз ногодрыг. Паталогию с делай по всему коду, long int для номера ноги и рекурсией для метода половинного деления не трогаем, с ней и так все ясно. 

И все бы было так хороше аж замечательно если бы не либы. С ними такой фокус, как в примере, не проходит. Номера ног передаются в конструктор сохраняются в переменной чем создают препятствие для быстрой работы. Зато как просто и красиво в скетче запись HCSR04 hcsr04(5, 6); Отказатся от такого не получится так просто, так что Arhat109-2  подвязывайте тут свои наработки рекламировать через пост ;) Пока у Вас нет подобного HCSR04 hcsr04(5, 6); интерфейса новичкам это не интересно, а пока код дублируется для каждого устройства не интересно "зубрам".

Какой выход из обозначенной проблемы выглядит оптимальным - ИМХО таки класс интерфейс (в терминологии Arhat109-2), наследник базового класса работы с устройством реализующий виртуальные методы работы с пинами, но не на нижнем уровне, типа SetBit, а на среднем уровне, типа ОutByte или OutBuf. Так получается и максимальная скорость работы с пином, одной командой, и снижается влияние проблемы вызова виртуальных методов, описанная выше Arhat109-2 еще и не полностю, есть вот это - http://habrahabr.ru/company/amperka/blog/264041/.  Эта проблема компилятора сильно печалит. И похоже не только меня, может поправят. И в скетче все крвсиво до одурения - #include <SSD1306.h> ssd1306_i2c(OLED_SCL_PIN, OLED_SDA_PIN) myOLED; При этом дублирование кода при подключении нескольких одинаковых устройств минимальное. И переписать какую либку таким путем как правило не долго. 

Такой подход особо радует на устройствах i2c, oneware, паралельной, подключения сдвиговых регистров. В общем именно там где и нужно ускорятся. Для более простых, типа сервы класс вырождается в функцию. Вариант с калбеком на функцию, завернутую в дефайник тогда проще, и нет неприятностей виртуального метода. Еще иногда инклудю в либы файл с распиновкой, но метод отстойный, хотя результат понятно просто идеальный. 

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

А я вас поддержу. То что мною сделано, было сделано по сути "на коленке" и прошу рассматривать не как рекламу, только как вариант и конечно же не самый удачный. правильное применение препроцессора и С++ даст куда как более красивое решение. Но, ещё раз, ядро arhat.h (то самое, что тут попалось под обсуждение) писалось исключительно с целью "вспомнить язык после 17-летнего перерыва" :)

Да, я просто не был уверен что gcc пихает виртуальные таблицы в оперативу .. единственным оправданием этого может быть как раз (теоретическая) возможность их изменения "на лету" (смена класса объектом, вроде как ещё запрещено, но..) а также .. скорость. Команды lpm таки тормознее чем ld

красивым выходом являются TEMPLATE либы на С++. :)

P.S. Ну есть же подобное .. просто на С и без конструкторов. :)

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

Arhat109-2 пишет:

красивым выходом являются TEMPLATE либы на С++. :)

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

И вопрос как это оформить - шаблоны оч. хороше. Завернуть их чтоб из скетча всё это было не видно.  Хоть я до конца не уверен что их возможностей хватит. В либе все наверняка будет выглядеть просто треш, ну и фиг с ним.

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

Logik пишет:

Сам бы так писал. Токо вместо 1U << ( написал бы _BV( - это системный макрос разворачивающийся в то же самое. Вместо P_PORT |= P_BIT1; написал бы скорей bitSet( или sbi( то же макросы тоже разварачивается в такое же.

В моём скетче все define и const находятся на своих местах. Если убрать все ухищрения, то получится следующий код:

void setup()
{
  pinMode(7, OUTPUT);
  digitalWrite(7, LOW);
}

void loop()
{
  PORTD |= (1U << 7);
  PORTD &= ~(1U << 7);
}

Но выполняться в данном варианте цикл будет примерно 17 тактов, вместо 2 из моего скетча. И макросы типа _BV( и bitSet( при этом не помогут.

Logik пишет:

А #if DATA_PIN > 7 писать бы не стал... я бы его скопипастил из другого проекта, он у  меня так выглядит #define  ARDUINO_PORT(a)    ((a<8)?PORTD:(a<14?PORTB:PORTC)).

С точки зрения получаемого кода обе записи одинаковы, но в моем скетче запись проще и (возможно) понятнее.

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

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

Так вот (ближе к делу), если про эти моменты помнить, то "всегда надо платить". А именно, в данном конкретном случае программирования микроконтроллеров, плата в виде дублирования кода для разных пинов - весьма невелика, ибо на практике "каждый пин" - уникален. Вот (и опять тот пример что под руками, не реклама!) к примеру, управление серводвигателями: есть (arhat.h) функции аппаратного серво- на базе 16-битных счетчиков и у Мега2560 их .. аж 4 шт по 3 пина для сервы .. казалось бы самое то место, где можно таки номер пина запихать в память и сделать "универсальный" код.. Однако, заметим, что собственно управление - это .. увы ровно 1 команда TOCCRxy или как там её .. и? Чего мы "добьемся" пиханием пина в память? А только доп. расходов на таблицы перекодировки и вызовы функций. А ежели посмотреть на реализации скетчей с сервами .. то можно внезапно обнаружить, что каждая серва управляет чем-то своим и имеет .. упс. уникальную логику этого управления. И? какой смысл в "обощенной" либе по серводвигателям? :)

Вот из таких соображений, я и делал этот arhat.h. Это микроконтроллер. И кто, хотя бы раз упихивал алгоритм и его данные в заданный объем - тот хорошо поймет мои доводы. Очень печально когда оно "чуть-чуть" а не лезет. А с wiring "не лезет" практически ничего и никуда. И тот, кто писал хотя бы один протокол, с частотными ограничениями - тоже хорошо поймет мои доводы: что если можно сделать "проще И быстрее" то это НАДО так сделать. Чем загрузить проц - задачи всегда найдутся. Как и чем занять место в его памяти.

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

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

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

Piskunov, Ну конечно не будет работать. Если указывать первую строчку #define Ardunio_h 1 , то вы тем самым "отключаете" использование Wiring, а это приводит к тому, что остальная часть кода перестает компилироваться. "Надо или крестик снять или трусы одеть", уж не в обиду сказано. В том смысле, что или отказаться совсем от функций Wiring или ТАК не писать. Нельзя быть "частично беременным". :)

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

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

Да, Вы правы. Увлеклись оптимизацией ногодрыгов .. :)

Предлагаю продолжить тему далее.

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

Tomasina пишет:

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

Примеры, где примеры? Как можно обойтись без unsigned long в задаче Blink withour delay?

А что, собственно, мешает? (правил прямо здесь, не проверял)

#define ledPin 13      // номер выхода, подключенного к светодиоду
bool ledState = LOW;             // этой переменной устанавливаем состояние светодиода
unsigned int previousMillis = 0;        // храним время последнего переключения светодиода
 
unsigned int interval = 1000;           // интервал между включение/выключением светодиода (1 секунда)
 
void setup() {
  // задаем режим выхода для порта, подключенного к светодиоду
  pinMode(ledPin, OUTPUT);     
}
 
void loop()
{
  // здесь будет код, который будет работать постоянно
  // и который не должен останавливаться на время между переключениями свето
  unsigned int currentMillis = millis() && 0xffff;
  
  //проверяем не прошел ли нужный интервал, если прошел то
  if(currentMillis - previousMillis > interval) {
    // сохраняем время последнего переключения
    previousMillis = currentMillis; 
 
    // если светодиод не горит, то зажигаем, и наоборот
    ledState = !ledState;
 
    // устанавливаем состояния выхода, чтобы включить или выключить светодиод
    digitalWrite(ledPin, ledState);
  }
}

 

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

arduinec пишет:

Но выполняться в данном варианте цикл будет примерно 17 тактов, вместо 2 из моего скетча. И макросы типа _BV( и bitSet( при этом не помогут.

А вот убирать goto как раз не нужно. Как вы опишете константу без разницы: define, cons byte или прямо цифра в коде даст одинаковый код в ассемблере.

Макросы описаны в Arduino.h так.

#define bitRead(value, bit) (((value) >> (bit)) & 0x01)
#define bitSet(value, bit) ((value) |= (1UL << (bit)))
#define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
#define bitWrite(value, bit, bitvalue) (bitvalue ? bitSet(value, bit) : bitClear(value, bit))

Не помоч не могут, помагают всем, облегчают жизнь, позволяют не изобретать велосипеды. Там рядом вобще много полезного. К стати и ТС на этом примере  полезно изучить технику написания define и где в нем скобки ставить, чтоб не получать разный результат у const byte 2+3; и #define 2+3 и выводить на этом теории.

Logik пишет:

А #if DATA_PIN > 7 писать бы не стал... я бы его скопипастил из другого проекта, он у  меня так выглядит #define  ARDUINO_PORT(a)    ((a<8)?PORTD:(a<14?PORTB:PORTC)).

arduinec пишет:

С точки зрения получаемого кода обе записи одинаковы, но в моем скетче запись проще и (возможно) понятнее.

Нет, есть еще PORTC о котором Вы забыли.

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

andriano пишет:

А что, собственно, мешает? (правил прямо здесь, не проверял)

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

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

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

Иногда лучше жевать? Ах да, Вам же ТС запретил отвечать. Попкорн - верный выбор )))

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

Logik пишет:

Иногда лучше жевать? Ах да, Вам же ТС запретил отвечать. Попкорн - верный выбор )))

иногда лучше читать, что я ответил ТС #36 ...и, каким боком ТС к цирку, который вы тут устроили?

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

Ну вот как раз в связи с этим и хотел предложить следующую темку про "оптимизацию": а именно - глобалы против локалов.

Что лучше для МК: повсеместное использование глобалов или локальные переменные? Кучу и malloc() давайте пока обойдем стороной. :)

Кто что думает по этому поводу?

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

Arhat109-2 пишет:

Ну вот как раз в связи с этим и хотел предложить следующую темку про "оптимизацию": а именно - глобалы против локалов.

Что лучше для МК: повсеместное использование глобалов или локальные переменные? Кучу и malloc() давайте пока обойдем стороной. :)

Кто что думает по этому поводу?

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

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

Arhat109-2 пишет:

Ну вот как раз в связи с этим и хотел предложить следующую темку про "оптимизацию": а именно - глобалы против локалов.

Что лучше для МК: повсеместное использование глобалов или локальные переменные? Кучу и malloc() давайте пока обойдем стороной. :)

Кто что думает по этому поводу?

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

Вы хотели услышать именно это?

SU-27-16
SU-27-16 аватар
Offline
Зарегистрирован: 13.08.2012

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

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

Arhat109-2 пишет:

Первой строкой добавьте

#define Arduino_h 1

И если в скетче ничего более нет - должно работать.

Arhat109-2 пишет:

Piskunov, Ну конечно не будет работать. Если указывать первую строчку #define Ardunio_h 1 , то вы тем самым "отключаете" использование Wiring, а это приводит к тому, что остальная часть кода перестает компилироваться. "Надо или крестик снять или трусы одеть", уж не в обиду сказано. В том смысле, что или отказаться совсем от функций Wiring или ТАК не писать. Нельзя быть "частично беременным". :)

Экий Вы, батенька, загадочный и противоречивый!

Прямо Печорин.

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

Logik пишет:

Как вы опишете константу без разницы: define, cons byte или прямо цифра в коде даст одинаковый код в ассемблере.

Увы факты говорят обратное: во втором примере были убраны define и const и получился другой код (более медленный к тому же).

Logik пишет:

Макросы описаны в Arduino.h

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

Увы макросы не всегда помогают.

С помощью Ардуино сейчас управляю светодиодными RGB SPI лентами. Для передачи данных в эти ленты требуется частота около 1 МГц. С макросами из Arduino.h такую частоту не получишь, вот и приходится изобретать свой код. Вышеприведённый первый скетч лишь побочный продукт.

Видео с бегущей строкой: https://yadi.sk/i/KjPlzioNjskYL

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

arduinec пишет:

Logik пишет:

Как вы опишете константу без разницы: define, cons byte или прямо цифра в коде даст одинаковый код в ассемблере.

Увы факты говорят обратное: во втором примере были убраны define и const и получился другой код (более медленный к тому же).

То не факты, то кривые руки.

#define a 1
const byte b=2;
void fun1(void)
{
 bitSet(PORTB, a);
 bitSet(PORTB, b);
 bitSet(PORTB, 3);
}

void setup() 
{
  fun1();
}

void loop() {}

Вот как это скомпелируется.


000000a6 <setup>:
  a6:	29 9a       	sbi	0x05, 1	; 5
  a8:	2a 9a       	sbi	0x05, 2	; 5
  aa:	2b 9a       	sbi	0x05, 3	; 5
  ac:	08 95       	ret

arduinec пишет:

Logik пишет:

Макросы описаны в Arduino.h

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

Увы макросы не всегда помогают.

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

arduinec пишет:

С помощью Ардуино сейчас управляю светодиодными RGB SPI лентами. Для передачи данных в эти ленты требуется частота около 1 МГц.

Это на WS2801 шоле? От любит наш народ старе!! Где Вы его только находите! Давно уже WS2812 рулят.

Я тут както предлагал командой запилять. http://arduino.ru/forum/proekty/pokhvalimsya-khudozhestvennoi-samodeyatelnostyu-na-ws2812, дак нет же. Код вывода в ленту кстати там не на макросах, Вам понравится ;)

 

arduinec пишет:

С макросами из Arduino.h такую частоту не получишь.

1МГц на SPI проблему делает?! Отправить 1 байт за 128 тактов аппаратно?! Чего ж там делать то...

 

 

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

SU-27-16 пишет:

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

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

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

SU-27-16
SU-27-16 аватар
Offline
Зарегистрирован: 13.08.2012

SU-27-16 пишет:

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

прошу пардона - не адресовал сообщение :(
правильно :

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

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

Logik пишет:

Как вы опишете константу без разницы: define, cons byte или прямо цифра в коде даст одинаковый код в ассемблере.

Предлагаю заменить в моём первом скетче "#define DATA_PIN 7" на "const byte DATA_PIN = 8;". Скетч откомпилируется и даже какой-то ассеблерный код получится, только ногодрыга на 8 пине не будет.

Logik пишет:

Напишете короче

Куда уж короче: цикл в моем первом скетче выполняется за 2 такта. Может Logik с помощью макросов сможет его до 1 такта сократить?

Logik пишет:

Давно уже WS2812 рулят.

Мне вообще-то денежки платят за то, что на ленты с WS2812 (на видео она показана) я разные эффекты вывожу (скоро Новый год однако).
 

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

arduinec пишет:

Предлагаю заменить в моём первом скетче "#define DATA_PIN 7" на "const byte DATA_PIN = 8;". Скетч откомпилируется и даже какой-то ассеблерный код получится, только ногодрыга на 8 пине не будет.

Действительно, чего же оно это так ))) А как нумеруются пины в пределах регистра? 

arduinec пишет:

Мне вообще-то денежки платят за то, что на ленты с WS2812 .

А SPI куда делся 8))

За твои познания токо пенделей выписывают. Учи матчасть, прийдеш как выучиш.

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

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

1. Тот, кто утверждает что на 16Мгц МК ногодрыг работает с 8Мгц - неправ однозначно. Даже в самом примитивном варианте там не 2, а ТРИ команды. Есть ещё переход "взад". А для обеспечения заданного количества повторов - ещё и декремент счетчику нужен .. итого имеем 4 команды .. или 4Мгц. Превысить этот предел на AVR 16Мгц - программно - невозможно "теоретически". А если счетчик 16 битный, то 4 Мгц - это только обслуживание его пересчета. Смотрите реализацию delayMicro16() в arhat.h (ну лень мне лезть в код и вырезать сюда как пример!) :)

2. Уже писал, что Atmel сделал всё, чтобы управление периферией занимало 1-2 команды: ногодрыг, шим, интерфейсы .. в нем вообще всё можно делать одной-двумя командами и разные Attiny с 1-2 килобайтами памяти тому подтверждение.  Код МК - это в первую очередь управление (иначе можно взять проц "общего пользования"). Соответственно этому и надо подходить к вопросам оптимизации.

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

Так что, единственно верный подход - реализация "общих" либ в виде template или "настраиваемых" интерфейсов по типу hcsr04.h. И нет там никакого избыточного "дублирования кода". Есть его настройка на конкретные пины куда прикручен датчик в конкретном устройстве. Да и когда у вас 2 датчика будет 2 процедуры управления .. и это ни разу не дублирование кода .. каждая управляет своими пинами и ровно одной командой. Что там "обобщать"? Сам подход "номер пина в памяти" - ущербен для целей оптимизации тут.

Wiring в целом - наглядная демонстрация того КАК НЕ НАДО ПИСАТЬ для МК.

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

Как выводы из темы:

1. Всё что можно вынести на более ранний этап принятия решений - должно быть вынесено из исполнения в runtime:

а) Предвычисляемые константные выражение - в #define, const int;

б) Все данные, гоняемые из функции в функцию как параметры - в глобалы. Экономия места тут - весьма призрачна;

в) замена виртуальных методов - простыми, а простых - глобальными функциями с как можно меньшим числом параметров;

г) про параметры отдельно: если их суммарный размер превышает 5-6 байт, то это признак того что функция будет неэффективной. Отсюда и предпочтение глобалам;

д) буферизация данных на указателях и перенос решения о размере буфера в настроечные константы на как можно выше уровнем, лучше в головной скетч. Как пример: Реализация Wire.h имеет двойную буферизацию, причем 80% буферов не используются в 99% применений. Писано жирно, толсто и НЕ ДЛЯ МК. А сверху накручен ещё "ряд классов"..

е) Минимизация уровней наследования. Класс отнаследованный от базового кода в "третий раз" можно выбрасывать и не использовать. Для того Wire.h - есть прямая реализация автомата core/../utility/twi.c - это базовый уровень и он УЖЕ содержит 96 байт буферизации данных. Можно пользовать его напрямую. Те 2 килобайта, что наверчены сверху - плата за "рюшечки".

д) Как можно меньшие размеры типов данных. int16 - на 2 порядка предпочтительнее int32, а int8 - на порядок предпочтительнее int16. int32 надо пользовать только "строго по назначению", а про float - ваще надо забыть и пользовать тогда И только тогда, когда задача не решаема в целочисленных вычислениях от слова НИКАК.

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

:)

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

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

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

arduinec пишет:

Logik пишет:

Как вы опишете константу без разницы: define, cons byte или прямо цифра в коде даст одинаковый код в ассемблере.

Предлагаю заменить в моём первом скетче "#define DATA_PIN 7" на "const byte DATA_PIN = 8;". Скетч откомпилируется и даже какой-то ассеблерный код получится, только ногодрыга на 8 пине не будет.

Поясню почему так получается. Элементы типа #define компилируются в самом начале, когда const и byte ещё не существуют (об этом в книгах по Си говорится). Поэтому в замененном коде:

const byte DATA_PIN = 8;

#if DATA_PIN > 7
#define P_PORT PORTB
#else
#define P_PORT PORTD
#endif

на момент копиляции "#if DATA_PIN > 7" ещё не определён DATA_PIN и условие не выполнится, то есть ногодрыга на пине D8 не будет.

Если же использовать #define DATA_PIN 8 , то скетч:

#define DATA_PIN 8

#if DATA_PIN > 7
#define P_PORT PORTB
#else
#define P_PORT PORTD
#endif

const byte P_BIT1 = 1U << (DATA_PIN % 8);
const byte P_BIT0 = ~P_BIT1;

void setup()
{
  pinMode(DATA_PIN, OUTPUT);
  digitalWrite(DATA_PIN, LOW);
}

void loop()
{
metka:
  P_PORT |= P_BIT1;  // High
  P_PORT &= P_BIT0;  // Low
  goto metka;
}

будет полностью работоспособным с ногодрыгом на пине D8.

Logik пишет:

А SPI куда делся

Очень пряморукий специалист по макросам Logik видимо плохо изучал матчасть и не знает, что эти ленты называются SPI 2-5000 5V RGB (5060,150 LEDx1, 2812, W):
http://www.arlight.ru/catalog/otkrytye-spi-5v-30-5060-685/lenta-spi-2-50...

arduinec пишет:

Logik пишет:

Напишете короче

Куда уж короче: цикл в моем первом скетче выполняется за 2 такта. Может Logik с помощью макросов сможет его до 1 такта сократить?

Ну и где же более короткий и быстрый скетч на макросах от Logik?