Да не знаю. Те, кому это было бы полезно - не читают. Уже третий этю и не было ни одного вопроса от "целевой аудитории".
Приходят два типа читателей: спецы, которые ждут от статьи чего-то другого и возмущаются. что не дождались. И "полуспецы", которые изо всех сил стараются "умность свою показать".
Мне бы пофигу, но беда в том, что целевая аудитория как раз не читает вовсе, а тогда какой смысл писать?
Много где в рекомендациях к программированию для Ардуино пишут о том, что номера пинов правильнее определять как константы через #define, а не как переменные в памяти через int или другой целый тип. Типа:
#define pinLed 13 // так правильно
int pinLed = 13; // так НЕ правильно
byte pinLed = 13; // так и с любым иным типом тоже НЕ правильно.
А про вариант "const byte pinLed = 13;" что скажете?
> Размер скетча в двоичном коде: 1 100 байт (из 14 336 байт максимум)
Видно, что компилятор не тупой и видя константу, самостоятельно заменяет ее числовым значением.
Так что в данном частном случае это без разницы. Но не факт, что это применимо для остальных случаев.
Не, примеров я приводить не буду, ибо ленив до неимоверности .. увы, но сами, сами...
Обойтись можно легко: взять либу arhat.h .. там в "автономном" режиме компиляции (без wiring) все эти функции переопределены как возвращающие uint16_t, так шта - даже менять ничего не требуется.
Ну а "блин без делай" там ваще организован на счетчике переполнений и называется everyOvf() .. он даже millis() не требует. Да и вся автоматная часть реализована через тип TSC_Time, который .. опять же uint16_t. :)
Вы не поняли. в ваших примерах используются функции из wiring. Они и требуют 1100 байт. В atmel это 3 команды, не считая delay() (он ещё 4-6, не помню). Итого в правильном варианте этот код занимает .. менее 20 байт. :)
Проверить можно так:
#include "arhat.h"
..
ну и далее по тексту.
в итого сейчас получите 368 байт или около того (минимально 490 для Мега2560), ибо delay() реализован через таймер, а он теперь "вумный" и позволяет исполнять из под себя функции юзверя без остановки счета времени. :)
Piskunov: "А про вариант "const byte pinLed = 13;" что скажете?"
Согласно стандарту, ежели мне не изменяет память, то должно быть эквивалентно #define, но как это компиляет avr-gcc - не смотрел за ненадобностью. Ранние компиляторы с С++ так и делали: заменяли все ООП прибамбасы опциями препроцессора и не парились. :)
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() {}
Да не знаю. Те, кому это было бы полезно - не читают. Уже третий этю и не было ни одного вопроса от "целевой аудитории".
Приходят два типа читателей: спецы, которые ждут от статьи чего-то другого и возмущаются. что не дождались. И "полуспецы", которые изо всех сил стараются "умность свою показать".
Мне бы пофигу, но беда в том, что целевая аудитория как раз не читает вовсе, а тогда какой смысл писать?
Как не читают? Еще как читают. А то что читает и не целевая аудитория тоже хорошо, каждый из прочитанного делает собственные выводы. Это еще Пухлявый кудато пропал, если его сюда вернуть то воды и маразма былоб в разы больше, но было бы и веселее, а то мы тут без него как дума без Милонова, Мизулиной, Жириновского.
И главное язык изложения у вас понятен. Сохряняю посты себе, и думаю вопросы появятся когда дойдут руки до применения в моих проектах. А так же что нравится не надо менять ничего в стандартных библиотеках, что в принчипе актуально для повторения разработок другими людьми.
Я далек от програмирования, соглашусь с kisoft что писать надо как можно проще, чтобы не только я понимал текст программы, а и любой не профессионал который попытается ее повторить, это если говорить о устройствах DIY.
И без обид но вот Arhat109-2 пишет полезные вещи в плане оптимизации, но чтоб понять перечитываю несколько раз. Видимо для меня оптимизация это не просто. Или просто много специфики.
Согласно стандарту, ежели мне не изменяет память, то должно быть эквивалентно #define, но как это компиляет avr-gcc - не смотрел за ненадобностью. Ранние компиляторы с С++ так и делали: заменяли все ООП прибамбасы опциями препроцессора и не парились. :)
Не так давно закончил чтение известной книги Скотта Мэйерса (первый раз).
Наверняка это было сказано в каком-то контексте. Каждый инструмент хорош, когда его используют по назначению и правильно. В каких-то местах надо предпочитать одно, а в каких-то другое. Ну, а кроме того, никто не отменял личные предпочтения.
Вы же помните бессмертную цитату из Милна (цитирую по памяти): "Всякому овощу своё время и своя грядка - сказала герцогиня - но если тебе это трудно понять, я могу объяснить проще: Никогда не думай, что ты иная, чем могла бы быть, иначе как будучи иной в тех случаях, когда иною нельзя не быть" :-))))
Нет. Очень хорошая книга и автор тоже. Просто те рекомендации, которые даются для программирования "систем общего пользования" далеко не всегда подходят для микроконтроллеров. Большой и развесистый код банально требует, чтобы вы пользовали ООП, классы и пр. "дребедень", иначе Вы рискуете никогда не завершить проект .. ну и его время жизни - велико и он "растет" по мере своей жизни. Качество оформления исходного текста начинает играть превалирующую роль над компактностью и скоростью кода. "Комп позволяет" как не экономить память .. там её гигабайты уже ни заниматься ерундой ловя блох в микросекундах (только отдельные .. специфичные приложения) ..
а тут все наоборот: место и скорость - главнее "красоты текста". И часто те самые 100байт за красоту вам не позволят применить микроконтроллер меньшей линейки. Абыдна, правда? :)
Да, именно такой подход или похожий (смотрите реализации таких "автоподстановок" в Cyberlib или arhat.h) и должен был быть реализован в wiring изначально. Развитие этого метода позволяет писать так:
При повторной проверке обнаружил, что у моего старого осциллографа барахлит переключатель диапазонов "мкс", поэтому за 8 МГц теперь не ручаюсь. Частотометра у меня нет.
Проверьте, у кого есть качественное оборудование, с какой частотой дергается пин.
Да, именно такой подход или похожий (смотрите реализации таких "автоподстановок" в Cyberlib или arhat.h) и должен был быть реализован в wiring изначально. Развитие этого метода позволяет писать так:
.. и получить понятный, компактный и шустрый скетч .. что и сделано в 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
Это сообщение будет содержать больше информации чем
"Отображать вывод во время компиляции"
включено в Файл > Настройки
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
Это сообщение будет содержать больше информации чем
"Отображать вывод во время компиляции"
включено в Файл > Настройки
Внесу-ка и я свою лепту в обсуждение и приведу в качестве примера
Отличная примера!! Так и пишет здоровая часть общества. Сам бы так писал. Токо вместо 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, паралельной, подключения сдвиговых регистров. В общем именно там где и нужно ускорятся. Для более простых, типа сервы класс вырождается в функцию. Вариант с калбеком на функцию, завернутую в дефайник тогда проще, и нет неприятностей виртуального метода. Еще иногда инклудю в либы файл с распиновкой, но метод отстойный, хотя результат понятно просто идеальный.
А я вас поддержу. То что мною сделано, было сделано по сути "на коленке" и прошу рассматривать не как рекламу, только как вариант и конечно же не самый удачный. правильное применение препроцессора и С++ даст куда как более красивое решение. Но, ещё раз, ядро arhat.h (то самое, что тут попалось под обсуждение) писалось исключительно с целью "вспомнить язык после 17-летнего перерыва" :)
Да, я просто не был уверен что gcc пихает виртуальные таблицы в оперативу .. единственным оправданием этого может быть как раз (теоретическая) возможность их изменения "на лету" (смена класса объектом, вроде как ещё запрещено, но..) а также .. скорость. Команды lpm таки тормознее чем ld
красивым выходом являются TEMPLATE либы на С++. :)
P.S. Ну есть же подобное .. просто на С и без конструкторов. :)
красивым выходом являются TEMPLATE либы на С++. :)
И да и нет. Если не принять мер - будет дублироваение кода. Принять меры - разделить на 2 части: общая для всех обектов и специфическая, обеспечивающая быстрый ногодрыг, причем спецефических может быть несколько по числу одинаковых внешних устройств. По стути обсуждать нужно как организовать связи между этими частями: статичиски связать (излишнее дублирование), наследование(выше обсосали), калбеки и их таблицы (должно быть весьма быстро но смахивает на изобретение велосипеда наследования) может еще как.
И вопрос как это оформить - шаблоны оч. хороше. Завернуть их чтоб из скетча всё это было не видно. Хоть я до конца не уверен что их возможностей хватит. В либе все наверняка будет выглядеть просто треш, ну и фиг с ним.
Сам бы так писал. Токо вместо 1U << ( написал бы _BV( - это системный макрос разворачивающийся в то же самое. Вместо P_PORT |= P_BIT1; написал бы скорей bitSet( или sbi( то же макросы тоже разварачивается в такое же.
В моём скетче все define и const находятся на своих местах. Если убрать все ухищрения, то получится следующий код:
Но выполняться в данном варианте цикл будет примерно 17 тактов, вместо 2 из моего скетча. И макросы типа _BV( и bitSet( при этом не помогут.
Logik пишет:
А #if DATA_PIN > 7 писать бы не стал... я бы его скопипастил из другого проекта, он у меня так выглядит #define ARDUINO_PORT(a) ((a<8)?PORTD:(a<14?PORTB:PORTC)).
С точки зрения получаемого кода обе записи одинаковы, но в моем скетче запись проще и (возможно) понятнее.
Понимаете в чем дело .. (это я так, начну "издалека" :), если бы существовали рекомендации по оптимизации программ "на все случаи жизни" .. то этому можно было бы научить компилятор и дело было бы "в шляпе". Тема, поднятая Топик Стартером - сама по себе холиварная и со склонностью к взаимным обвинениям "кулцхаккер". Я уже молчу про то, что есть ряд по-просту противоречивых требований к оптимизации..
Так вот (ближе к делу), если про эти моменты помнить, то "всегда надо платить". А именно, в данном конкретном случае программирования микроконтроллеров, плата в виде дублирования кода для разных пинов - весьма невелика, ибо на практике "каждый пин" - уникален. Вот (и опять тот пример что под руками, не реклама!) к примеру, управление серводвигателями: есть (arhat.h) функции аппаратного серво- на базе 16-битных счетчиков и у Мега2560 их .. аж 4 шт по 3 пина для сервы .. казалось бы самое то место, где можно таки номер пина запихать в память и сделать "универсальный" код.. Однако, заметим, что собственно управление - это .. увы ровно 1 команда TOCCRxy или как там её .. и? Чего мы "добьемся" пиханием пина в память? А только доп. расходов на таблицы перекодировки и вызовы функций. А ежели посмотреть на реализации скетчей с сервами .. то можно внезапно обнаружить, что каждая серва управляет чем-то своим и имеет .. упс. уникальную логику этого управления. И? какой смысл в "обощенной" либе по серводвигателям? :)
Вот из таких соображений, я и делал этот arhat.h. Это микроконтроллер. И кто, хотя бы раз упихивал алгоритм и его данные в заданный объем - тот хорошо поймет мои доводы. Очень печально когда оно "чуть-чуть" а не лезет. А с wiring "не лезет" практически ничего и никуда. И тот, кто писал хотя бы один протокол, с частотными ограничениями - тоже хорошо поймет мои доводы: что если можно сделать "проще И быстрее" то это НАДО так сделать. Чем загрузить проц - задачи всегда найдутся. Как и чем занять место в его памяти.
А тот, кто писал проги для управления устройствами - меня поймет вдвойне. Ибо не бывает "обощенных" двигателей, серв, датчиков расстояний .. каждый в проекте уникален и решает ровно свою задачу управления. Даже если они из одной партии с одного завода.
Вот для таких моя либа и написана. А вовсе не для "начинающих" или "опытных" программистов .. общего плана. Программирование микроконтроллеров - это очень своеобразная ниша. И то, что полезно универсальщику - тут приводит к смерти проекта. И довольно часто.
Piskunov, Ну конечно не будет работать. Если указывать первую строчку #define Ardunio_h 1 , то вы тем самым "отключаете" использование Wiring, а это приводит к тому, что остальная часть кода перестает компилироваться. "Надо или крестик снять или трусы одеть", уж не в обиду сказано. В том смысле, что или отказаться совсем от функций Wiring или ТАК не писать. Нельзя быть "частично беременным". :)
Ну и как рекомендация: не использовать без необходимости длинные целые числа, помня о том, что они резко просаживают общую производительность микроконтроллера.
Примеры, где примеры? Как можно обойтись без 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);
}
}
Не помоч не могут, помагают всем, облегчают жизнь, позволяют не изобретать велосипеды. Там рядом вобще много полезного. К стати и ТС на этом примере полезно изучить технику написания 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 пишет:
С точки зрения получаемого кода обе записи одинаковы, но в моем скетче запись проще и (возможно) понятнее.
Ну вот как раз в связи с этим и хотел предложить следующую темку про "оптимизацию": а именно - глобалы против локалов.
Что лучше для МК: повсеместное использование глобалов или локальные переменные? Кучу и malloc() давайте пока обойдем стороной. :)
Кто что думает по этому поводу?
Я думаю, что если каждой функции нужен свой набор переменных, то в случае, когда они будут глобальными, все переменные для всех функций будут висеть в памяти одновременно, а если переменные будут локальные, то они при последовательном вызове для каждой функции будут по очереди выделяться в одном и том же участке стека.
думаю , что вам надо написать подобный этюдик на тему
"Что лучше для МК: повсеместное использование глобалов или локальные переменные ?" , а не в этой ветке....
И если в скетче ничего более нет - должно работать.
Arhat109-2 пишет:
Piskunov, Ну конечно не будет работать. Если указывать первую строчку #define Ardunio_h 1 , то вы тем самым "отключаете" использование Wiring, а это приводит к тому, что остальная часть кода перестает компилироваться. "Надо или крестик снять или трусы одеть", уж не в обиду сказано. В том смысле, что или отказаться совсем от функций Wiring или ТАК не писать. Нельзя быть "частично беременным". :)
Как вы опишете константу без разницы: define, cons byte или прямо цифра в коде даст одинаковый код в ассемблере.
Увы факты говорят обратное: во втором примере были убраны define и const и получился другой код (более медленный к тому же).
Logik пишет:
Макросы описаны в Arduino.h
Не помоч не могут, помагают всем, облегчают жизнь, позволяют не изобретать велосипеды.
Увы макросы не всегда помогают.
С помощью Ардуино сейчас управляю светодиодными RGB SPI лентами. Для передачи данных в эти ленты требуется частота около 1 МГц. С макросами из Arduino.h такую частоту не получишь, вот и приходится изобретать свой код. Вышеприведённый первый скетч лишь побочный продукт.
Не помоч не могут, помагают всем, облегчают жизнь, позволяют не изобретать велосипеды.
Увы макросы не всегда помогают.
Да. Не всегда. От кривизны рук не помогут, но случайных ошибок с ними меньше. Они только облегчают жизнь, позволяют не изобретать велосипеды. Выше видно, дают идеальный код. Напишете короче, не стесняйтесь постите :))
arduinec пишет:
С помощью Ардуино сейчас управляю светодиодными RGB SPI лентами. Для передачи данных в эти ленты требуется частота около 1 МГц.
Это на WS2801 шоле? От любит наш народ старе!! Где Вы его только находите! Давно уже WS2812 рулят.
думаю , что вам надо написать подобный этюдик на тему
"Что лучше для МК: повсеместное использование глобалов или локальные переменные ?" , а не в этой ветке....
Работа с памятью: стек, куча, фрагментация, возможно, чистка мусора - это тема нового этюда, который я сейчас готовлю.
думаю , что вам надо написать подобный этюдик на тему
"Что лучше для МК: повсеместное использование глобалов или локальные переменные ?" , а не в этой ветке....
прошу пардона - не адресовал сообщение :(
правильно :
Arhat109-2 , думаю , что вам надо написать подобный этюдик на тему "Что лучше для МК: повсеместное использование глобалов или локальные переменные ?" , а не в этой ветке....
Как вы опишете константу без разницы: define, cons byte или прямо цифра в коде даст одинаковый код в ассемблере.
Предлагаю заменить в моём первом скетче "#define DATA_PIN 7" на "const byte DATA_PIN = 8;". Скетч откомпилируется и даже какой-то ассеблерный код получится, только ногодрыга на 8 пине не будет.
Logik пишет:
Напишете короче
Куда уж короче: цикл в моем первом скетче выполняется за 2 такта. Может Logik с помощью макросов сможет его до 1 такта сократить?
Logik пишет:
Давно уже WS2812 рулят.
Мне вообще-то денежки платят за то, что на ленты с WS2812 (на видео она показана) я разные эффекты вывожу (скоро Новый год однако).
Предлагаю заменить в моём первом скетче "#define DATA_PIN 7" на "const byte DATA_PIN = 8;". Скетч откомпилируется и даже какой-то ассеблерный код получится, только ногодрыга на 8 пине не будет.
Действительно, чего же оно это так ))) А как нумеруются пины в пределах регистра?
arduinec пишет:
Мне вообще-то денежки платят за то, что на ленты с WS2812 .
А SPI куда делся 8))
За твои познания токо пенделей выписывают. Учи матчасть, прийдеш как выучиш.
Господа, товарищи, народ, други .. уж не знаю как в наше время обращаться .. я уже писал, что тема изначально холиварная со склонностью перехода на взаимные оскорбления .. не стоит оно того. :)
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 - ваще надо забыть и пользовать тогда И только тогда, когда задача не решаема в целочисленных вычислениях от слова НИКАК.
Если пользуем "рюшечки" - эту тему читать не стоит ни при каком уровне знаний, навыков и опыта. Ни начинающему в МК, ни со стажем. Не поможет. Ибо в первом посту писано верно: никакая кодовая оптимизация не поможет при неэффективном алгоритме решения задачи.
P.S. Да, и если народ беспокоит, что "распиарил свою либу в каждом посту" .. как скажете. Писано было для вас, но раз "не требуется" как мне уже писали .. дальнейших обновлений на гитхабе не будет.
Как вы опишете константу без разницы: define, cons byte или прямо цифра в коде даст одинаковый код в ассемблере.
Предлагаю заменить в моём первом скетче "#define DATA_PIN 7" на "const byte DATA_PIN = 8;". Скетч откомпилируется и даже какой-то ассеблерный код получится, только ногодрыга на 8 пине не будет.
Поясню почему так получается. Элементы типа #define компилируются в самом начале, когда const и byte ещё не существуют (об этом в книгах по Си говорится). Поэтому в замененном коде:
Главное продолжайте тему.
Да не знаю. Те, кому это было бы полезно - не читают. Уже третий этю и не было ни одного вопроса от "целевой аудитории".
Приходят два типа читателей: спецы, которые ждут от статьи чего-то другого и возмущаются. что не дождались. И "полуспецы", которые изо всех сил стараются "умность свою показать".
Мне бы пофигу, но беда в том, что целевая аудитория как раз не читает вовсе, а тогда какой смысл писать?
Я читаю. Остальные прочитают, когда придет время.
...Мне бы пофигу, но беда в том, что целевая аудитория как раз не читает вовсе, а тогда какой смысл писать?
А может быть Вы не совсем верно определили "целевую аудиторию"? :-)
Вона как возбудились, трясут
седыми я...регалиями, костылями размахивают!От себя лично хотел бы попросить Вас несколько подробнее остановиться на оптимизации деления, а особенно на вычислении остатка.
Много где в рекомендациях к программированию для Ардуино пишут о том, что номера пинов правильнее определять как константы через #define, а не как переменные в памяти через int или другой целый тип. Типа:
А про вариант "const byte pinLed = 13;" что скажете?
>Размер скетча в двоичном коде: 1 108 байт (из 14 336 байт максимум)
>Размер скетча в двоичном коде: 1 100 байт (из 14 336 байт максимум)
>Размер скетча в двоичном коде: 1 100 байт (из 14 336 байт максимум)
> Размер скетча в двоичном коде: 1 100 байт (из 14 336 байт максимум)
Так что в данном частном случае это без разницы. Но не факт, что это применимо для остальных случаев.
Не, примеров я приводить не буду, ибо ленив до неимоверности .. увы, но сами, сами...
Обойтись можно легко: взять либу arhat.h .. там в "автономном" режиме компиляции (без wiring) все эти функции переопределены как возвращающие uint16_t, так шта - даже менять ничего не требуется.
Ну а "блин без делай" там ваще организован на счетчике переполнений и называется everyOvf() .. он даже millis() не требует. Да и вся автоматная часть реализована через тип TSC_Time, который .. опять же uint16_t. :)
Вы не поняли. в ваших примерах используются функции из wiring. Они и требуют 1100 байт. В atmel это 3 команды, не считая delay() (он ещё 4-6, не помню). Итого в правильном варианте этот код занимает .. менее 20 байт. :)
Проверить можно так:
в итого сейчас получите 368 байт или около того (минимально 490 для Мега2560), ибо delay() реализован через таймер, а он теперь "вумный" и позволяет исполнять из под себя функции юзверя без остановки счета времени. :)
Piskunov: "А про вариант "const byte pinLed = 13;" что скажете?"
Согласно стандарту, ежели мне не изменяет память, то должно быть эквивалентно #define, но как это компиляет avr-gcc - не смотрел за ненадобностью. Ранние компиляторы с С++ так и делали: заменяли все ООП прибамбасы опциями препроцессора и не парились. :)
Piskunov: "А про вариант "const byte pinLed = 13;" что скажете?"
Согласно стандарту, ежели мне не изменяет память, то должно быть эквивалентно #define
Слово "эквивалентно" здесь не уместно. У этих записей иногда (в частных случаях) совпадает семантика, но по сути они принципиально разные. Описание "const ... " определяет именно константу, а "#define ..." орпеделяет контекст подстановки.
Для того, чтобы прочувствовать разницу, посмотрите на вот такой пример:
Главное продолжайте тему.
Да не знаю. Те, кому это было бы полезно - не читают. Уже третий этю и не было ни одного вопроса от "целевой аудитории".
Приходят два типа читателей: спецы, которые ждут от статьи чего-то другого и возмущаются. что не дождались. И "полуспецы", которые изо всех сил стараются "умность свою показать".
Мне бы пофигу, но беда в том, что целевая аудитория как раз не читает вовсе, а тогда какой смысл писать?
Как не читают? Еще как читают. А то что читает и не целевая аудитория тоже хорошо, каждый из прочитанного делает собственные выводы. Это еще Пухлявый кудато пропал, если его сюда вернуть то воды и маразма былоб в разы больше, но было бы и веселее, а то мы тут без него как дума без Милонова, Мизулиной, Жириновского.
И главное язык изложения у вас понятен. Сохряняю посты себе, и думаю вопросы появятся когда дойдут руки до применения в моих проектах. А так же что нравится не надо менять ничего в стандартных библиотеках, что в принчипе актуально для повторения разработок другими людьми.
Я далек от програмирования, соглашусь с kisoft что писать надо как можно проще, чтобы не только я понимал текст программы, а и любой не профессионал который попытается ее повторить, это если говорить о устройствах DIY.
И без обид но вот Arhat109-2 пишет полезные вещи в плане оптимизации, но чтоб понять перечитываю несколько раз. Видимо для меня оптимизация это не просто. Или просто много специфики.
Ну уж звиняйте за косноязычность .. обучением студентов последний раз занимался в году так 1992-м. :)
Это понятно. Там был контекст определения констант, а не подстановок. У меня #define юзается и не в таком ключе.. :)
Согласно стандарту, ежели мне не изменяет память, то должно быть эквивалентно #define, но как это компиляет avr-gcc - не смотрел за ненадобностью. Ранние компиляторы с С++ так и делали: заменяли все ООП прибамбасы опциями препроцессора и не парились. :)
Не так давно закончил чтение известной книги Скотта Мэйерса (первый раз).
Цитата оттуда: "Правило 2: Предпочитайте const, enum и inline использованию #define"
Я зря потратил время??? :-((
Цитата оттуда: "Правило 2: Предпочитайте const, enum и inline использованию #define"
Я зря потратил время??? :-((
Наверняка это было сказано в каком-то контексте. Каждый инструмент хорош, когда его используют по назначению и правильно. В каких-то местах надо предпочитать одно, а в каких-то другое. Ну, а кроме того, никто не отменял личные предпочтения.
Вы же помните бессмертную цитату из Милна (цитирую по памяти): "Всякому овощу своё время и своя грядка - сказала герцогиня - но если тебе это трудно понять, я могу объяснить проще: Никогда не думай, что ты иная, чем могла бы быть, иначе как будучи иной в тех случаях, когда иною нельзя не быть" :-))))
Нет. Очень хорошая книга и автор тоже. Просто те рекомендации, которые даются для программирования "систем общего пользования" далеко не всегда подходят для микроконтроллеров. Большой и развесистый код банально требует, чтобы вы пользовали ООП, классы и пр. "дребедень", иначе Вы рискуете никогда не завершить проект .. ну и его время жизни - велико и он "растет" по мере своей жизни. Качество оформления исходного текста начинает играть превалирующую роль над компактностью и скоростью кода. "Комп позволяет" как не экономить память .. там её гигабайты уже ни заниматься ерундой ловя блох в микросекундах (только отдельные .. специфичные приложения) ..
а тут все наоборот: место и скорость - главнее "красоты текста". И часто те самые 100байт за красоту вам не позволят применить микроконтроллер меньшей линейки. Абыдна, правда? :)
Ещё могу порекомендовать Майерса Гленфорда .. у него тоже много полезных книг, как и у Скотта Д. Майерса. :)
Внесу-ка и я свою лепту в обсуждение и приведу в качестве примера с оптимизацией программку с define и const.
Программка дергает пин с частотой 8 МГц (при частоте Ардуины 16 МГц):
Осциллограф показал интервал между импульсами 0,12 мкс (= 8 МГц), то есть цикл выполняется за 2 такта.
Комбинация из if-define в начале скетча позволяет использовать любой дата-пин от 0 до 13 (на всех не проверял) на Arduino Uno и Nano.
Зачем строки 20 и 23 с goto???
Да, именно такой подход или похожий (смотрите реализации таких "автоподстановок" в Cyberlib или arhat.h) и должен был быть реализован в wiring изначально. Развитие этого метода позволяет писать так:
.. и получить понятный, компактный и шустрый скетч .. что и сделано в arhat.h только с возможностью всего арсенала ног Мега2560. :)
Зачем строки 20 и 23 с goto???
С goto получается быстрее.
При повторной проверке обнаружил, что у моего старого осциллографа барахлит переключатель диапазонов "мкс", поэтому за 8 МГц теперь не ручаюсь. Частотометра у меня нет.
Проверьте, у кого есть качественное оборудование, с какой частотой дергается пин.
Да, именно такой подход или похожий (смотрите реализации таких "автоподстановок" в Cyberlib или arhat.h) и должен был быть реализован в wiring изначально. Развитие этого метода позволяет писать так:
.. и получить понятный, компактный и шустрый скетч .. что и сделано в arhat.h только с возможностью всего арсенала ног Мега2560. :)
Не работает ((
C Arduino.h и не будет работать. :)
Первой строкой добавьте
#define Arduino_h 1
И если в скетче ничего более нет - должно работать.
Всё равно не работает. Правда сообщений об ошибках стало меньше ))
Вот ошибки:
Внесу-ка и я свою лепту в обсуждение и приведу в качестве примера
Отличная примера!! Так и пишет здоровая часть общества. Сам бы так писал. Токо вместо 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, паралельной, подключения сдвиговых регистров. В общем именно там где и нужно ускорятся. Для более простых, типа сервы класс вырождается в функцию. Вариант с калбеком на функцию, завернутую в дефайник тогда проще, и нет неприятностей виртуального метода. Еще иногда инклудю в либы файл с распиновкой, но метод отстойный, хотя результат понятно просто идеальный.
А я вас поддержу. То что мною сделано, было сделано по сути "на коленке" и прошу рассматривать не как рекламу, только как вариант и конечно же не самый удачный. правильное применение препроцессора и С++ даст куда как более красивое решение. Но, ещё раз, ядро arhat.h (то самое, что тут попалось под обсуждение) писалось исключительно с целью "вспомнить язык после 17-летнего перерыва" :)
Да, я просто не был уверен что gcc пихает виртуальные таблицы в оперативу .. единственным оправданием этого может быть как раз (теоретическая) возможность их изменения "на лету" (смена класса объектом, вроде как ещё запрещено, но..) а также .. скорость. Команды lpm таки тормознее чем ld
красивым выходом являются TEMPLATE либы на С++. :)
P.S. Ну есть же подобное .. просто на С и без конструкторов. :)
красивым выходом являются TEMPLATE либы на С++. :)
И да и нет. Если не принять мер - будет дублироваение кода. Принять меры - разделить на 2 части: общая для всех обектов и специфическая, обеспечивающая быстрый ногодрыг, причем спецефических может быть несколько по числу одинаковых внешних устройств. По стути обсуждать нужно как организовать связи между этими частями: статичиски связать (излишнее дублирование), наследование(выше обсосали), калбеки и их таблицы (должно быть весьма быстро но смахивает на изобретение велосипеда наследования) может еще как.
И вопрос как это оформить - шаблоны оч. хороше. Завернуть их чтоб из скетча всё это было не видно. Хоть я до конца не уверен что их возможностей хватит. В либе все наверняка будет выглядеть просто треш, ну и фиг с ним.
Сам бы так писал. Токо вместо 1U << ( написал бы _BV( - это системный макрос разворачивающийся в то же самое. Вместо P_PORT |= P_BIT1; написал бы скорей bitSet( или sbi( то же макросы тоже разварачивается в такое же.
В моём скетче все define и const находятся на своих местах. Если убрать все ухищрения, то получится следующий код:
Но выполняться в данном варианте цикл будет примерно 17 тактов, вместо 2 из моего скетча. И макросы типа _BV( и bitSet( при этом не помогут.
А #if DATA_PIN > 7 писать бы не стал... я бы его скопипастил из другого проекта, он у меня так выглядит #define ARDUINO_PORT(a) ((a<8)?PORTD:(a<14?PORTB:PORTC)).
С точки зрения получаемого кода обе записи одинаковы, но в моем скетче запись проще и (возможно) понятнее.
Понимаете в чем дело .. (это я так, начну "издалека" :), если бы существовали рекомендации по оптимизации программ "на все случаи жизни" .. то этому можно было бы научить компилятор и дело было бы "в шляпе". Тема, поднятая Топик Стартером - сама по себе холиварная и со склонностью к взаимным обвинениям "кулцхаккер". Я уже молчу про то, что есть ряд по-просту противоречивых требований к оптимизации..
Так вот (ближе к делу), если про эти моменты помнить, то "всегда надо платить". А именно, в данном конкретном случае программирования микроконтроллеров, плата в виде дублирования кода для разных пинов - весьма невелика, ибо на практике "каждый пин" - уникален. Вот (и опять тот пример что под руками, не реклама!) к примеру, управление серводвигателями: есть (arhat.h) функции аппаратного серво- на базе 16-битных счетчиков и у Мега2560 их .. аж 4 шт по 3 пина для сервы .. казалось бы самое то место, где можно таки номер пина запихать в память и сделать "универсальный" код.. Однако, заметим, что собственно управление - это .. увы ровно 1 команда TOCCRxy или как там её .. и? Чего мы "добьемся" пиханием пина в память? А только доп. расходов на таблицы перекодировки и вызовы функций. А ежели посмотреть на реализации скетчей с сервами .. то можно внезапно обнаружить, что каждая серва управляет чем-то своим и имеет .. упс. уникальную логику этого управления. И? какой смысл в "обощенной" либе по серводвигателям? :)
Вот из таких соображений, я и делал этот arhat.h. Это микроконтроллер. И кто, хотя бы раз упихивал алгоритм и его данные в заданный объем - тот хорошо поймет мои доводы. Очень печально когда оно "чуть-чуть" а не лезет. А с wiring "не лезет" практически ничего и никуда. И тот, кто писал хотя бы один протокол, с частотными ограничениями - тоже хорошо поймет мои доводы: что если можно сделать "проще И быстрее" то это НАДО так сделать. Чем загрузить проц - задачи всегда найдутся. Как и чем занять место в его памяти.
А тот, кто писал проги для управления устройствами - меня поймет вдвойне. Ибо не бывает "обощенных" двигателей, серв, датчиков расстояний .. каждый в проекте уникален и решает ровно свою задачу управления. Даже если они из одной партии с одного завода.
Вот для таких моя либа и написана. А вовсе не для "начинающих" или "опытных" программистов .. общего плана. Программирование микроконтроллеров - это очень своеобразная ниша. И то, что полезно универсальщику - тут приводит к смерти проекта. И довольно часто.
Piskunov, Ну конечно не будет работать. Если указывать первую строчку #define Ardunio_h 1 , то вы тем самым "отключаете" использование Wiring, а это приводит к тому, что остальная часть кода перестает компилироваться. "Надо или крестик снять или трусы одеть", уж не в обиду сказано. В том смысле, что или отказаться совсем от функций Wiring или ТАК не писать. Нельзя быть "частично беременным". :)
Да, Вы правы. Увлеклись оптимизацией ногодрыгов .. :)
Предлагаю продолжить тему далее.
Ну и как рекомендация: не использовать без необходимости длинные целые числа, помня о том, что они резко просаживают общую производительность микроконтроллера.
Примеры, где примеры? Как можно обойтись без unsigned long в задаче Blink withour delay?
А что, собственно, мешает? (правил прямо здесь, не проверял)
Но выполняться в данном варианте цикл будет примерно 17 тактов, вместо 2 из моего скетча. И макросы типа _BV( и bitSet( при этом не помогут.
А вот убирать goto как раз не нужно. Как вы опишете константу без разницы: define, cons byte или прямо цифра в коде даст одинаковый код в ассемблере.
Макросы описаны в Arduino.h так.
Не помоч не могут, помагают всем, облегчают жизнь, позволяют не изобретать велосипеды. Там рядом вобще много полезного. К стати и ТС на этом примере полезно изучить технику написания define и где в нем скобки ставить, чтоб не получать разный результат у const byte 2+3; и #define 2+3 и выводить на этом теории.
А #if DATA_PIN > 7 писать бы не стал... я бы его скопипастил из другого проекта, он у меня так выглядит #define ARDUINO_PORT(a) ((a<8)?PORTD:(a<14?PORTB:PORTC)).
С точки зрения получаемого кода обе записи одинаковы, но в моем скетче запись проще и (возможно) понятнее.
Нет, есть еще PORTC о котором Вы забыли.
А что, собственно, мешает? (правил прямо здесь, не проверял)
Ниче не мешает. А если разницу предыдущего и текущего сохранить в word, то в if-ах, если их много, будет еще красивше
Иногда лучше жевать? Ах да, Вам же ТС запретил отвечать. Попкорн - верный выбор )))
Иногда лучше жевать? Ах да, Вам же ТС запретил отвечать. Попкорн - верный выбор )))
иногда лучше читать, что я ответил ТС #36 ...и, каким боком ТС к цирку, который вы тут устроили?
Ну вот как раз в связи с этим и хотел предложить следующую темку про "оптимизацию": а именно - глобалы против локалов.
Что лучше для МК: повсеместное использование глобалов или локальные переменные? Кучу и malloc() давайте пока обойдем стороной. :)
Кто что думает по этому поводу?
Ну вот как раз в связи с этим и хотел предложить следующую темку про "оптимизацию": а именно - глобалы против локалов.
Что лучше для МК: повсеместное использование глобалов или локальные переменные? Кучу и malloc() давайте пока обойдем стороной. :)
Кто что думает по этому поводу?
знаю, что все должны мне в личку подавать челобитные в письменном виде на бересте.
Ну вот как раз в связи с этим и хотел предложить следующую темку про "оптимизацию": а именно - глобалы против локалов.
Что лучше для МК: повсеместное использование глобалов или локальные переменные? Кучу и malloc() давайте пока обойдем стороной. :)
Кто что думает по этому поводу?
Я думаю, что если каждой функции нужен свой набор переменных, то в случае, когда они будут глобальными, все переменные для всех функций будут висеть в памяти одновременно, а если переменные будут локальные, то они при последовательном вызове для каждой функции будут по очереди выделяться в одном и том же участке стека.
Вы хотели услышать именно это?
думаю , что вам надо написать подобный этюдик на тему
"Что лучше для МК: повсеместное использование глобалов или локальные переменные ?" , а не в этой ветке....
Первой строкой добавьте
#define Arduino_h 1
И если в скетче ничего более нет - должно работать.
Piskunov, Ну конечно не будет работать. Если указывать первую строчку #define Ardunio_h 1 , то вы тем самым "отключаете" использование Wiring, а это приводит к тому, что остальная часть кода перестает компилироваться. "Надо или крестик снять или трусы одеть", уж не в обиду сказано. В том смысле, что или отказаться совсем от функций Wiring или ТАК не писать. Нельзя быть "частично беременным". :)
Экий Вы, батенька, загадочный и противоречивый!
Прямо Печорин.
Как вы опишете константу без разницы: define, cons byte или прямо цифра в коде даст одинаковый код в ассемблере.
Увы факты говорят обратное: во втором примере были убраны define и const и получился другой код (более медленный к тому же).
Макросы описаны в Arduino.h
Не помоч не могут, помагают всем, облегчают жизнь, позволяют не изобретать велосипеды.
Увы макросы не всегда помогают.
С помощью Ардуино сейчас управляю светодиодными RGB SPI лентами. Для передачи данных в эти ленты требуется частота около 1 МГц. С макросами из Arduino.h такую частоту не получишь, вот и приходится изобретать свой код. Вышеприведённый первый скетч лишь побочный продукт.
Видео с бегущей строкой: https://yadi.sk/i/KjPlzioNjskYL
Как вы опишете константу без разницы: define, cons byte или прямо цифра в коде даст одинаковый код в ассемблере.
Увы факты говорят обратное: во втором примере были убраны define и const и получился другой код (более медленный к тому же).
То не факты, то кривые руки.
Вот как это скомпелируется.
Макросы описаны в Arduino.h
Не помоч не могут, помагают всем, облегчают жизнь, позволяют не изобретать велосипеды.
Увы макросы не всегда помогают.
Да. Не всегда. От кривизны рук не помогут, но случайных ошибок с ними меньше. Они только облегчают жизнь, позволяют не изобретать велосипеды. Выше видно, дают идеальный код. Напишете короче, не стесняйтесь постите :))
С помощью Ардуино сейчас управляю светодиодными RGB SPI лентами. Для передачи данных в эти ленты требуется частота около 1 МГц.
Это на WS2801 шоле? От любит наш народ старе!! Где Вы его только находите! Давно уже WS2812 рулят.
Я тут както предлагал командой запилять. http://arduino.ru/forum/proekty/pokhvalimsya-khudozhestvennoi-samodeyatelnostyu-na-ws2812, дак нет же. Код вывода в ленту кстати там не на макросах, Вам понравится ;)
С макросами из Arduino.h такую частоту не получишь.
1МГц на SPI проблему делает?! Отправить 1 байт за 128 тактов аппаратно?! Чего ж там делать то...
думаю , что вам надо написать подобный этюдик на тему
"Что лучше для МК: повсеместное использование глобалов или локальные переменные ?" , а не в этой ветке....
Работа с памятью: стек, куча, фрагментация, возможно, чистка мусора - это тема нового этюда, который я сейчас готовлю.
думаю , что вам надо написать подобный этюдик на тему
"Что лучше для МК: повсеместное использование глобалов или локальные переменные ?" , а не в этой ветке....
прошу пардона - не адресовал сообщение :(
правильно :
Arhat109-2 , думаю , что вам надо написать подобный этюдик на тему
"Что лучше для МК: повсеместное использование глобалов или локальные переменные ?" ,
а не в этой ветке....
Как вы опишете константу без разницы: define, cons byte или прямо цифра в коде даст одинаковый код в ассемблере.
Предлагаю заменить в моём первом скетче "#define DATA_PIN 7" на "const byte DATA_PIN = 8;". Скетч откомпилируется и даже какой-то ассеблерный код получится, только ногодрыга на 8 пине не будет.
Напишете короче
Куда уж короче: цикл в моем первом скетче выполняется за 2 такта. Может Logik с помощью макросов сможет его до 1 такта сократить?
Давно уже WS2812 рулят.
Мне вообще-то денежки платят за то, что на ленты с WS2812 (на видео она показана) я разные эффекты вывожу (скоро Новый год однако).
Предлагаю заменить в моём первом скетче "#define DATA_PIN 7" на "const byte DATA_PIN = 8;". Скетч откомпилируется и даже какой-то ассеблерный код получится, только ногодрыга на 8 пине не будет.
Действительно, чего же оно это так ))) А как нумеруются пины в пределах регистра?
Мне вообще-то денежки платят за то, что на ленты с WS2812 .
А SPI куда делся 8))
За твои познания токо пенделей выписывают. Учи матчасть, прийдеш как выучиш.
Господа, товарищи, народ, други .. уж не знаю как в наше время обращаться .. я уже писал, что тема изначально холиварная со склонностью перехода на взаимные оскорбления .. не стоит оно того. :)
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 - ваще надо забыть и пользовать тогда И только тогда, когда задача не решаема в целочисленных вычислениях от слова НИКАК.
Если пользуем "рюшечки" - эту тему читать не стоит ни при каком уровне знаний, навыков и опыта. Ни начинающему в МК, ни со стажем. Не поможет. Ибо в первом посту писано верно: никакая кодовая оптимизация не поможет при неэффективном алгоритме решения задачи.
:)
P.S. Да, и если народ беспокоит, что "распиарил свою либу в каждом посту" .. как скажете. Писано было для вас, но раз "не требуется" как мне уже писали .. дальнейших обновлений на гитхабе не будет.
Как вы опишете константу без разницы: define, cons byte или прямо цифра в коде даст одинаковый код в ассемблере.
Предлагаю заменить в моём первом скетче "#define DATA_PIN 7" на "const byte DATA_PIN = 8;". Скетч откомпилируется и даже какой-то ассеблерный код получится, только ногодрыга на 8 пине не будет.
Поясню почему так получается. Элементы типа #define компилируются в самом начале, когда const и byte ещё не существуют (об этом в книгах по Си говорится). Поэтому в замененном коде:
на момент копиляции "#if DATA_PIN > 7" ещё не определён DATA_PIN и условие не выполнится, то есть ногодрыга на пине D8 не будет.
Если же использовать #define DATA_PIN 8 , то скетч:
будет полностью работоспособным с ногодрыгом на пине D8.
А 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...
Напишете короче
Куда уж короче: цикл в моем первом скетче выполняется за 2 такта. Может Logik с помощью макросов сможет его до 1 такта сократить?
Ну и где же более короткий и быстрый скетч на макросах от Logik?