Pull to refresh

Comments 154

Так и есть.
В процессе заводских испытаний было принято к устранению замечание Представителя Заказчика о недостаточной частоте моргания.
Обоснование отступления от ГОСТа и внесения изменений в программный код программно-аппаратного комплекса железобетонное: "Папа, я так хочу".

Вот так всегда у нас: Заказчик сказал, а на ГОСТы можно и покласть…

PS: Шутка, если что.
Там все равно «рекомендуется»
Вот утконосу сейчас было бы обидно.
принципиальная схема простого светофора — огонь! Земля нарисована на все деньги!
Да и статья — космос!)
Хорошо — дочка, а то такое бы могло получиться)))

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

Косяк. Обидно, что давно исправленный, в "./schematic/traffic light ver0.dch" схема верная, вот такая.

А в скриншотах к статье пропустил давнюю, неисправленную.

Что такое нужно делать с многострадальным си, чтобы переменная заняла 50 байт? Поставьте уже AtmelStudio и не позорьтесь.
Добавление копеечной микросхемы сдвигового регистра позволит расширить число выводов без программного садо-мазо.

Предположу, Вы о ремарке Утащить внутрь функции main глобальные переменные — каждая глобальная переменная в С — примерно +50 байт требуемого ROM..
Очень подозреваю, что результат AtmelStudio вряд ли сильно будет отличаться от GCC, рост расхода памяти с глобальными переменными в сравнении с локальными не обусловлен конкретным тулчейном.
Самому любопытно, и буду благодарен, если кто-то проведет эксперимент по компиляции под AtmelStudio и в комментарии расскажет результаты.

Сдвиговый регистр?
Для иллюстративной самодельной игрушки, занимательной головоломки, как видите, он не требуется.
Для (предположим) схемы серийного производства игрушки, не то что добавлять регистр, а от 6 токоограничивающих резисторов избавлялся бы — при импульсной схеме питания светодиодов они не необходимы, а каждая точка пайки, не говоря о стоимости самого элемента, это постоянные расходы.
AtmelStudio содержит компилятор для AVR на основе GCC, как в прочем и WinAVR, и псевдосреда разработки для «дуино». И смысла ставить еще одну монструозную среду на основе VisualStudio нет.
Глобальная переменная в C занимает ровно столько байт, сколько положено размерностью её типа.

Накладные расходы могут возникать при активном использовании volatile-переменных, но это не связано с переменными как таковыми — это следствие необходимости при каждом обращении к ней тянуть её из памяти.
Нет, не так.
Глобальная переменная gcc инициализируется, локальная нет — накладные расходы в .bss.
#include <avr/io.h>  
uint8_t cnt;	//global
int main(){
	//uint8_t cnt;	//local
    cnt=0;      // < setup()
    while(1){
        cnt++;  // < loop()
    }
}

Только что проверил: при компиляции с флагом -O0 (без оптимизации) размер прошивки с глобальной переменной 78 байт, с локальной 58.
При оптимизации по размеру (-Os) 60 байт и 40 соответственно.
Дизассемблирование показывает — куда уходит память, слева — вариант с глобальной переменной.
image
Если вам настолько нужно экономить ROM, можно сказать AVR-GCC, чтобы тот поместил глобальную переменную в область .noinit, а не в .bss, как это он делает по-умолчанию:
int foo __attribute__ ((section (".noinit")));
Вариант отличный, от инициализации .bss избавляет.
Но дальше всё равно по расходу памяти уступит локальным переменным.
Тот же микрокод, где счетчик как
uint8_t cnt __attribute__ ((section (".noinit")));

Компиляция с -О0, локальная переменная прошивка 58 байт, глобальная в секции .noinit — 62 байта.
Слева — вариант с глобальной переменной в .noinit, справа — локальное объявление, остальные части листинга практически одинаковы.
Обратите внимание: по смещению 2e (начало while(1) слева) чтение значения из адреса — это 4 байта, инкремент (ага, вычесть 255), затем опять 4 байта на сохранение значения.
image
Чем больше раз мы будем обращаться к переменной, тем больше вырастет код.
Для переменных с типами размером больше байта ситуация еще грустнее.

Кстати, наверное, стоило поделиться хитростью в статье.
Чтобы по-быстрому откомпилировать-залить, или посмотреть асм-листинг не запуская IDE, я открываю cpp/c/ino в notepad++, и прямо из него по F5 (или меню Запуск-Запуск) запускаю коммандный файл из директории ./gcc.
"....path/gcc/0_MAKE & asm-O0.cmd" $(FULL_CURRENT_PATH)
Добавил «0_MAKE & asm-O0.cmd» в репозиторий на github.
Ну да, do_clear_bss заливает нулями всю область bss в ОЗУ.

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

И как уже сказали, есть .noinit.
Простите великодушно, но заменив формулировку на "накладные расходы на обнуление .bss будут распределены на все переменные", или так же "при суммарном размере переменных более 20 байт, накладные расходы на каждую переменную на обнуление .bss станут меньше размера переменной" Вы получите гораздо более убедительную позицию в споре.
Правда при этом Вы вынуждены будете расстаться с безапелляционностью первичных суждений.
Давайте я буду краток?

Утверждение «каждая глобальная переменная в С — примерно +50 байт требуемого ROM» в корне неверно.

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

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

Более того, я без труда могу показать вам ситуацию, когда локальные переменные сожрут у вас радикально больше ОЗУ, чем глобальные.
Интригующе смотрится Ваша осведомленность в том, что и как мне представляется. И желание оставить вне поля зрения факт, что переменные не только читаются, но и пишутся в память при изменении — те же +2 байта.
Досадно, что Вы сейчас приводите свою экспертную оценку, споря с результатами работы компилятора на конкретном примере.
Но, может, на «большом» исходнике TrafficLight13.ino всё будет по отстаиваемой Вами точке зрения?
Напомню её "Глобальная переменная в C занимает ровно столько байт, сколько положено размерностью её типа."
Там есть подходящая локальная переменная, перенесем ее в глобальные для проверки.
int main() {
	uint8_t	current_signal;				// 1 байт на текущее состояние, номер в traffic_signals

При компиляции с нулевой оптимизацией -О0 размер прошивки 2038 bytes (199.0% Full). С параметром -Os 938 bytes (91.6% Full).
Если переменную вынести в глобальные размер прошивки 2066 bytes (201.8% Full) с флагом О0, а с оптимизацией по размеру 974 bytes (95.1% Full).
И если описать её как глобальную в секции .noinit размер прошивки те же
2066 bytes (201.8% Full) (что логично — остальные глобальные в .noinit не попали, 20 байт на его обнуление все равно задействованы) и с оптимизацией по размеру -Оs те же 974 bytes (95.1% Full).
Компиляция без оптимизации интересна тем, что asm файл довольно точно следует исходному коду на С, никакой отсебятины и экономии от компилятора не добавляется.
Однако всё равно 2046 больше, чем 2038, а разница при оптимизации по размеру 974 и 938 еще более впечатляющая — на фоне суммарного размера ROM.
Увы.
Буду краток.
— Перенос глобальной переменной в локальную дает экспериментально точно выявленную экономию: 36 байт. Ранее, напомню, моя оценка такого действия была «примерно добавит 50 байт». Если все глобальные вынести в .noinit, вероятно, экономия будет +20. Суммарно на все, но среди остальных несколько двухбайтовых типов, где экономия обещает быть больше, то на то.
— Ваше мнение о том, что место в памяти для глобальной переменной расходуется только на размер типа не подвердилось, упущены расходы на инициализацию .bss и повышенные расходы на чтение и запись, извините.
— Вы не путаете виды памяти, обещая мне показать перерасход ОЗУ в контексте беседы о экономии ROM?
— Самое забавное, Вы, ошибаясь конкретно в этом примере, по общей теории практически во многом правы. Но правы «вообще», не по обсуждаемой конкретной задаче минимизации конкретного кода в конкретном килобайтном кристалле.
Вот это как раз моя делянка, только я смотрю не размер кода вообще, а конкретный ассемблер.
Если байтовая переменная глобальная, то любой доступ к ней занимает 2 слова и 3 цикла, для статической переменной — то же самое, а вот для локальной на стеке — 1 слово и 2 цикл, ну а для регистровой — 0 слов и 0 тактов.
При этом размер переменной как таковой нисколько не меняется, меняется только совокупная стоимость владения, отраженная в объеме памяти программ, но никак не памяти данных, причем конкретное значение предсказать нельзя от слова совсем.
В то же время обращение к элементу массива длинной более 64 байт приведет к катастрофической просадке варианта с локальным массивом. Поэтому и Олег прав, но и Вы тоже правы, но по своему, вопрос в трактовке.
Мало чего добавить можно к комментарию. Спасибо разве сказать, хорошо сказано.
Первоначальное заявление Олега было настолько воскитительно бескопромиссным и безапелляционным, что было сложно удержаться и не показать, что жесткие формулировки обязаны быть еще и всеохватывающими. Заявлять, что снег всегда только белый — дискредетировать свою же информацию, хотя в подавляющей массе своей действительно не желтый. Переменная С в памяти требует не только место для себя, любимой, но существуют и накладные расходы, в стесненных рамках надо понимать и помнить, что они есть и какие именно. Чтобы была возможность выбирать чем платить будем — лишним словом ROM при обращении или лишним байтом оперативки или регистром. Я не просто так у каждой глобальной переменной писал в комментарии потребный размер, это ценники. Тинька с её 64 байтами RAM и 1к ROM отлично иллюстрирует необходимостью ощущать узкие стенки коридора решений.
Мне казалось очевидным, что фраза по которой начался спор, наряду со своими соседками в перечислении, является одной из спекулятивных гипотез, требующих проверки и (возможно) обеспечивающую экономию программной памяти. В случае, если это потребуется, и какой случай так же описан.
Но если я в цирке смеюсь, это не значит, что все такие же, и так же воспримут очевидные мне вещи.
Смешно тут то, что в конкретном программном коде оценка навскидку оказалось верной. А верное в базе мнение оппонента из-за своей неполноты и жесткости конструкции определения наоборот было опровергнуто экспериментом.
Но что еще ожидать от циркового представления?
Тинька с её 64 байтами RAM и 1к ROM отлично иллюстрирует необходимостью ощущать узкие стенки коридора решений


Иллюстрирует. Только вы их не ощущаете, вы тыкаетесь наугад и делаете неверные выводы.

int foo(void) {
   static int x = 0;
   x++;
   return x;
}


Всё ещё считаете, что локальная переменная не требует ваших «50 байт»?..
Олег, при всем уважении, Вы слегка передергиваете, статические переменные — это не локальные переменные, и работает с ними компилятор точно также, как и с глобальными, для настоящей примитивной локальной переменной на стеке код действительно будет короче, пока их не станет более 64.
Статические переменные могут быть как локальными, так и глобальными. В коде выше — статическая локальная переменная.
Технически, локальная переменная — переменная с локальной областью видимости.
Область видимости и расположение это разные характеристики.
Что такое «настоящая» локальная переменная? Локальные переменные могут быть на стеке, в памяти(статические), в регистре. Можно еще thread local storage вспомнить.
Вы ниже пишете, что хотите показать, как оно, программировать без ардуины — и тут же демонстрируете подход в лучших традициях ардуины: выдвигаете предположение, от которого у любого программиста волосы на всех частях тела должны шевелиться и сон с аппетитом пропадать, и начинаете его обосновывать на уровне «сунул код в компилятор, и он мне выдал».

То есть, серьёзно, в насколько изменённом состоянии сознания надо пребывать, чтобы спокойно, мимоходом отметить, что на один байт каждой переменной у вас непонятно откуда берутся аж 50 байт кода?..

Ещё раз: нет, это не так.

Перенос глобальной переменной в локальную дает экспериментально точно выявленную экономию: 36 байт


Вы понимаете, что вот это конкретное ваше утверждение — оно безусловно ложное, несмотря на то, что вы даже можете показать пример, в котором оно кажется истинным?

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

упущены расходы на инициализацию .bss и повышенные расходы на чтение и запись


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

Потому что я вам могу, в конце концов, показать программу, в которой глобальных переменных нет вообще, а инициализация bss и чтение/запись переменных в ОЗУ — есть.

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

Я вообще не очень понимаю, почему вы не хотите отделить мух от котлет. Вопрос о правильном использовании локальных и глобальных переменных, не говоря уже об их модификаторах, и его влиянии на футпринты во флэше, ОЗУ и процессорных циклах — он заметно более сложный, чем простое и неверное утверждение «каждая глобальная переменная занимает 50 байт».
Вы чудесны и удивительны. Нет, на самом деле я так вижу.

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

Я запускаю руку меж ланит ассистентки и достаю кролика, в большой программе достаю из main() локальную переменную в глобальную, прошивка от этого вырастает на 36 байт — Вы требуете признать, что кролик был не в той норе, о которой могут подумать. Что я просто обязан беспокоиться о травматизме средних ардуинщиков, которые просто обязательно начнут засовывать кроликов.

Нельзя?
Я знаю.
Нельзя делить на ноль, нельзя не защищать GPIO резисторами, нельзя тормозить после входа в поворот на мотоцикле, нельзя допускать срыва машины в занос, нельзя все переменные засовывать в стек, нельзя купаться при температуре -15, нельзя…
Да много чего нельзя.
Возможно, это может показаться немножечко чудом, или ересью, в зависимости от состояния желчного пузыря, но всё это можно, если понимать когда можно и как можно и почему нельзя.

Здесь я уже просто на фактах, на экспериментальных данных, доступных к повторению Вами показал — вот, следите за руками, переносится переменная в локальные, вот asm, вот откуда «появляется» «лишняя» память.
Но нет, Вы мне пытаетесь доказать, что знаете, как можно потратить больше памяти (я про foo-пример со static int), правда, не очень понял цели, у меня в прошивке нет функций со статическими переменными. Но ведь классно, что Вы знаете, что такие бывают.

Нельзя вот так просто взять и отказаться от чуда.
Погладьте кролика, он существует.
Просто гладьте.

Посмотрите исходник — практически все переменные можно исхитриться задействовать вообще без памяти и без флеша, это тоже, кстати, будет чудо. Основных их всего то 9 байт — переписать на ассемблер целиком или вставками — регистров хватит.
Нет.

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

А я вам сообщаю, что кролик у вас в шляпе был всего один, второго там уже нет.
ланиты, это ж щеки, верно? вот прям руку запускаете? о_О

Жаль, конечно, что спор вышел на неровном месте. Но что-то полезное удастся вынести из статьи. Спасибо.
Вот объясните мне: «Почему?»
Код весь показать не могу по ряду причин, лень было создавать новый проект.

Есть два массива
unsigned char DataIn[512]; 
unsigned char DataOut[512];
int main (void)
{
   ...
}

Собираю проект: Code=2572 RO-data=224 RW-data=12 ZI-data=2148

Код тот же самый, но только

int main (void)
{
   unsigned char DataIn[512]; 
   unsigned char DataOut[512];
   ...
}

Собираю проект:Code=2568 RO-data=224 RW-data=12 ZI-data=1124

Я вижу экономию в ZI ровно 1024?
Что я делаю не так?
Zero-Initialized Data уменьшился на размер массивов, всё верно.
А что, простите, Вы ожидали увидеть?
В первом варианте оперативку зарезервирует компилятор, и добавит еще забой нолями.
Во втором случае оперативка будет выделена после начала работы/исполнения main(). Будет ли далее память инициализироваться, нет ли жизни на Марсе — наука пока данных не получила.

Вот интереснее, что Code уменьшился на 4 байта — но тут я предположить ничего не готов.
Продолжаем эксперимент.
Массивы
unsigned char DataIn[512]; 
unsigned char DataOut[512];

остались в коде и расположены в функции main()
Добавляем в код следующий массив
short Sinewave[384] = {
    0, 402, 804, 1206, 1608, 2009, 2410, 2811, 
    3212, 3612, 4011, 4410, 4808, 5205, 5602, 5998, 
    6393, 6786, 7179, 7571, 7962, 8351, 8739, 9126, 
    9512, 9896, 10278, 10659, 11039, 11417, 11793,
    ...
}

Размещаем до функции main().
Наблюдаем результат компиляции: Code=2572 RO-data=224 RW-data=780 ZI-data=2148
Total ROM Size (Code + RO Data + RW Data) 3576 ( 3.49kB)
Размещаем внутри функции main().
Наблюдаем результат компиляции: Code=2592 RO-data=992 RW-data=12 ZI-data=2148
Total ROM Size (Code + RO Data + RW Data) 3596 ( 3.51kB)

Меняем тип массива с short на int массив до main(), видим Code=2636 RO-data=224 RW-data=1548 ZI-data=2148

Вот никак у меня не получается найти лишние байты на глобальных переменных.
Постановка задачи — посмотреть, что будет, если выпить касторки при кашле, потому что при запоре она помогала?
Поправьте меня, если ошибаюсь, но Вы сейчас компилируете пример под ARM архитектуру (32бита?) для проверки (? нет?) описанного уменьшения размера в программе под 8бит AVR?

Выше, в беседе с Олегом я вставлял картинку сравнения двух дизассемблированных вариантов. Сравнивая ассемблерные файлы здорово видно, что уменьшение размеров кода прошивки (постоянной памяти) произошло из-за конкретных особенностей архитектуры AVR.
— уменьшение кода за счет исчезновения инициализации .bss на 20 байт.
— уменьшение кода за счет того, что длинна инструкции чтения/записи из/в конкретного адреса памяти по конкретному адресу занимает по 4 байта, а длинна команды для чтения из памяти меньше — загрузка Y регистра 2 байта, затем (ldd r24, Y+1) чтение по косвенной адресации размером в 2 байта, запись обратно — тоже только 2 байта.

Чтение/запись во время выполнения программы происходят многократно. Для volatile при любом обращении к переменным, для остальных — перечитывается значение из памяти (4 байт) при первом обращении и при последующих, если между обращениями регистр с переменной был занят другой, записывается в память (4 байт) — каждый раз при изменении.

Но.
Размеры, занимаемые переменными в ОПЕРАТИВНОЙ памяти зависят исключительно от типа переменной. Для глобальных и статических ячейки памяти будут заняты постоянно, а для локальных переменных возможны варианты, причем глобальные переменные будут «внизу» памяти, а локальные на стеке будут «свисать» с потолка, что может привести при нарастании к их пересечению, но это сейчас не важно, как и фрагментация. Адресация же в 32 и 8 битной архитектуре, понятно, фундаментально разная, Игорь использовал более корректный термин «слово» вместо «байта».
Возможен ли для 32битной архитектуры ARM описанный полушутливый трюк — тут смотреть надо, точно так же взять простенький тестовый файл и откомпилировать в разных вариантах, посмотреть размеры прошивки, посмотреть получающийся asm.
Вот видите, вы уже начали двигаться в правильном направлении — думать, почему вы видите то, что вы видите.

Правда, пока не до конца:

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


Нет, не надо.

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

Можно этот код просто посмотреть.

И для ARM его точно так же можно посмотреть (и даже проще, с большой вероятностью он будет не на ассемблере).

И никакой магии, никаких волшебных шляп с кроликами.
Олег, вы полагаете найти какая-то иная причина, вероятно, отличающаяюся от моего описания фокуса с кроликом, прячется где то в этом 70 килобайтном файле?

Я не силён в ассемблерах, могу попросить Вас самого описать — отчего, что было причиной, что в конкретных примерах программ, случилось уменьшение размеров кода при переносе переменной из глобальных в локальные?

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

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

Вы в публичном пространстве пишете чушь и изобретаете магию на ровном месте, запутывая потенциальных читателей, которые вот этого начитаются и начнут верить в бред типа «каждая глобальная переменная занимает 36 байт» или «неясно, возможен ли этот трюк для архитектуры ARM».
Ответ на первый вопрос очевиден, история разговора перед Вами: Вы постоянно пытаетесь рассказать мне о чем я думаю, чего хочу и что ощущаю. Мне, признаюсь, несколько неуютно, когда это делает не моя жена.

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

Предлагаю эксперимент. В свободное/ближайшее время я в исходник светофора добавлю глобальную переменную. Буду в ней считать что-нибудь, неважно, бесполезное. Количество переключений, или сравнивать с чем то, не суть. Просто новая переменная.
Затем сравню размеры прошивок — где переменная как глобальная, и переменная, та же самая, как локальная. Код выложу на github — чтобы любой мог повторить эксперимент.
Моё мнение — что размеры прошивок будут разными, причем в случае с локальной переменной размер будет меньше.

Вы готовы поддержать противоположную точку зрения, сказать, что по-вашему мнению, размер прошивки не уменьшится?

Всё просто, честно, наглядно.
Да или нет?
Господи, вы ведь до сих пор даже не смогли понять, что до вас пытаются донести.

Всё-таки ардуина — это не платформа, это диагноз. «В контроллере творится магия, сейчас я выложу код с трюком».
Олег, оставьте патетику гимназисткам, а диагнозы врачам.
Вопрос несложный, бинарный, зачем вилять, просто ответьте — да или нет?
Это вопрос уровня «перестали ли вы пить коньяк по утрам». Вам уже три раза объяснили, почему увеличение футпринта при использовании в конкретной программе одной глобальной переменной не означает, что каждая глобальная переменная будет так же увеличивать код, что локальная переменная не будет увеличивать код, и т.д. и т.п.

Попробуйте перечитать эти объяснения столько раз, сколько вам будет необходимо для понимания их содержания.

Засим откланиваюсь.
Действительно, моя искренняя благодарность Moduvator и GarryC за участие, пояснения и объяснения.

А теперь результаты эксперимента.
Diff исходника с добавленной бесполезной переменной.

Глобально объявляю uint16_t useless_var __attribute__ ((section (".noinit")));
Размер прошивки 2192 и 1060 байт для компиляции без оптимизации и с оптимизацией по размеру.
В main локально объявляю uint16_t useless_var.
Программа 2146 для компиляции без оптимизации и 982 байт (Ура, влазит (95.9% Full)) с флагом -Os.
Экономия на переносе 78 байт стала позволять поместиться прошивке в ROM тиньки.

Выводы, постараюсь сформулировать аккуратно.
У ATtiny13 (а вероятно у 8-бит AVR вообще) каждая глобальная переменная, которая используется только в основной функции, и может быть перенесена внутрь неё как локальная — вызывает перерасход программной памяти. Объявление хотя бы одной глобальной переменной без директивы расположения в секции ".noinit" так же вызывает перерасход программной памяти. Размер экономии памяти при перемещении переменной в локальную зависит от частоты использования переменной в программе, от количества других переменных, но даже в вырожденном случае, при единственной переменной во всей программе, состоящей из инкремента в цикле для однобайтного типа минимальная экономия будет 4 байта, для двухбайтного типа 10 байт.
Формулировка верна как минимум до двух (Привет, второй кролик) таких переменных.
GarryC намекал, что до 64, почему — мне пока не понятно.
Причина — практически дизассемблированием .elf обнаружил, что avr-gcc\5.4.0-atmel3.6.1 генерит различный код для организации доступа к глобальным и локальным переменным для ATtyny13.
Дело в том, что команда LDD хорошо работает при смещении не более 63 байта относительно индекса, в противном случае начинается честная индексная арифметика со сложениями, а это очень долго. Ну да и более 64 байтов локалей — это явно плохо, поэтому разработчики МК на это и не закладывались.
Спасибо.
Ограничение страницы в 64к очевидно, а этот нюанс нет.
Вы не поверите, провел эксперименты на 8 разрядном микроконтроллере STM8. И представьте себе не увидел, что переменные занимают больше места в .bss, чем положено (char — 1Байт, short — 2Байта). Это к разговору о разрядности архитектуры.
Я не знаю, что я делаю не так?!

З.Ы.
Использовал Ваш код с гитхаба.
Можно объяснить существующее явление, а не отсутствующее.
Я сейчас, не поверите, ровно так же сидел и проверял.
Что происходит в AVR 8-битном, из-за чего уменьшается потребная программная память мне понятно: глобальная и локальная переменные. Достаточно посмотреть-сравнить размеры памяти на строки 33-34 и дальше.
У глобальных переменных копируется значение из адреса, прямо указываемого в операторе, 4 байта на чтение, 4 байта на запись.
А для чтения локальной — адрес грузится в регистровую пару Y, один раз. А операции чтения и потом записи — двубайтовые.

И какой напрашивается вывод?

Компилятор avr-gcc (GCC) версии 5.4.0 для МК AVR ATtiny13 и ATMega328 (локальная и глобальная) генерирует различный машинный код при оперировании глобальными и локальными переменными.
На примере инкремента переменной
cnt++;

Для глобального её определения код занимает 10 байт:
...
2e:  80 91 60 00   lds    r24, 0x0060  ; 0x800060 
32:  8f 5f         subi   r24, 0xFF    ; 255
34:  80 93 60 00   sts    0x0060, r24  ; 0x800060 

При определении переменной как локальной 6 байт:
...
2e:    89 81           ldd    r24, Y+1   ; 0x01
30:    8f 5f           subi   r24, 0xFF  ; 255
32:    89 83           std    Y+1, r24   ; 0x01

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

С теоретической точки зрения нет никаких оснований ожидать, что для фактически одного действия — загрузки/выгрузки переменной из ОЗУ в регистры компилятор будет выдавать различный машинный код для локальных и глобальных вариантов использования переменной. Здесь я целиком разделяю мнение Олега.

Реальное же поведение как минимум конкретной версии компилятора, как минимум для двух target МК, которые я проверил — вот тут кроличья нора. Как назвать этот факт — чудо, фокус или баг — зависит лишь от состояния желчного пузыря.
При необходимости сэкономить программную память на AVR, можно вспомнить и попробовать использовать этот трюк. Или сразу при написании программы стараться НЕ использовать глобальные переменные.

Собственно вывод.
Гипотеза, которая высказана в заключительной главе — что можно уменьшить размер прошивки светофора, минимизируя использование глобальных переменных, прошла экспериментальную проверку, подтвердилась, и получила логичное объяснение.
С теоретической точки зрения нет никаких оснований ожидать, что для фактически одного действия — загрузки/выгрузки переменной из ОЗУ в регистры компилятор будет выдавать различный машинный код для локальных и глобальных вариантов использования переменной. Здесь я целиком разделяю мнение Олега.


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

И считаете это всё трюками и прочей магией.

А в реальности у вас в одном случае операция чтения из ОЗУ, а в другом — из стека, который хоть физически и расположен в ОЗУ, но с точки зрения компилятора работает совершенно не так, как хранилище в произвольной локации.

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

Заодно отпадёт необходимость проверять «как минимум для двух МК» — где-то в момент осознания, что компилятор не то что «минимум на двух», а на всей линейке с одним и тем же ядром работает одинаково.

Или сразу при написании программы стараться НЕ использовать глобальные переменные.


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

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

Очень надеюсь, что Вы просто увлеклись полемикой, и не считаете так в реальности.
Ну не может же быть, что во входном буфере професионала Вашего уровня остается последнее прочитанное предложение. И что в ответ на утверждение, на формулировку, выделенную жирным, заявлять, что следствием переноса глобальной переменной в локальные в main() может стать нехватка памяти.
Это же ересь.
Судя по минусу на комментарии, в этой оценке я не одинок (это не мой, с моей точки зрения ставить минус собеседнику — признак отсутствия аргументов при наличии эмоций).

Суммарно размер занимаемой и требуемой SRAM при этой рокировке не изменится.
Переменная будет аллоцирована по другому адресу, не снизу, а сверху адресного пространства, но занимать будет ровно столько же памяти.
А вот программный код в 8-битных AVR при этом уменьшится.


Вы и сейчас не готовы отказаться от точки зрения, что размер прошивки не уменьшится при переносе глобальной переменной внутрь main()?

(часть Вашей цитаты про static переменные я удалил, потому что в контексте обсуждения они сущность привнесенная и излишняя. Маловероятно, что кто-либо будет пытаться сохранять в static-ах что-либо между вызовами main())

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

Можно я обосную отсутствие ответа на эти вопросы Вашим заявлением, что что моя личность для Вас вообще не имеет какое-то принципиальное значение?
Если это не так, и позиция изменилась, дайте знать в ответе, я удивлюсь, но расскажу.
Суммарно размер занимаемой и требуемой SRAM при этой рокировке не изменится.


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

Глобальная переменная всегда одна, локальной переменной — столько, сколько экземпляров использующей её функции компилятор считает незавершёнными. Чисто технически вы можете взять uint8_t и им одним съесть мегабайт ОЗУ.
Чисто технически вы можете взять uint8_t и им одним съесть мегабайт ОЗУ.

Вообще, если честно, я не смогу: не представляю как.
Если Вам не очень сложно, покажите, как это возможно, пожалуйста, код.

Как можно в одной функции main одной переменной uint8_t съесть мегабайт ОЗУ.
Это правда, что экземпляров функции main действительно может быть более одного?

Если Вам не очень сложно, покажите, как это возможно, пожалуйста, код.


Рекурсивным вызовом функции, внутри которой есть эта локальная переменная. Код сами напишете, я в вас верю.

Как можно в одной функции main одной переменной uint8_t съесть мегабайт ОЗУ.


А вас вообще ничего не смущает, когда вы в мои фразы вставляете слова от себя — и требуете от меня обосновать не моё утверждение, а ваше?

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

olartamonov Да, спасибо, вижу, +.

А вас вообще ничего не смущает, когда вы в мои фразы вставляете слова от себя — и требуете от меня обосновать не моё утверждение, а ваше?

С самого начала мы обсуждаем конкретную фразу из статьи, с которой Вы решительно отказываетесь соглашаться. augorelov даже набрался храбрости потребовать убрать эту формулировку, он так же считает, что размеры кода или вырастут или станут больше. Он даже (спасибо ему) проверил на STM8 мой код с github, и говорит, что разницы локальная ли, глобальная ли переменная — нет. Доверяя его опыту, свою формулировку я ограничил конкретной архитектурой и конкретным компилятором.

Утащить внутрь функции main глобальные переменные — каждая глобальная переменная в С — примерно +50 байт требуемого ROM.
Высказанную среди других гипотез возможного уменьшения объёма программной памяти конкретной программы.

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


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

Обычное ардуиновское магическое мышление, ничего более. Если вы хотели показать, как за пределы ардуино-мирка вырваться — то нет, вы показываете ровно обратное: как, крепко зажмурившись, изо всех сил стараться в нём остаться.
olartamonov, спасибо за Ваше мнение, я услышал.
Я высказал своё мнение, для уменьшения размеров прошивки можно
Утащить внутрь функции main глобальные переменные — каждая глобальная переменная в С — примерно +50 байт требуемого ROM.

Вы не готовы согласиться с мнением, вы назвали его "безусловно ложным" — это Ваше право на собственное мнение, не оспариваю.

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

Мои аргументы, проверяемые, численные, доступные к независимой проверке Вам не интересны, основной аргумент — «чушь», и пожелания почитать какие-нибудь учебники, и примеры, которые не имеют никакого отношения к обсуждаемой теме.
Если чтение рекомендуемых учебников приводит именно к такому modus vivendi, я воздержусь от развития образования в эту сторону.

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

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

Тем не менее, спасибо за спор: его результатом стало моё понимание почему именно моё первоначальное предположение верно.

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

Вы вчера так красиво попрощались.
Стоит ли тратить Вам дальше время и красноречие на откровенную чушь, которую Вы не хотите ни видеть, ни слышать от человека с магическим мышлением?
каждая глобальная переменная в С — примерно +50 байт требуемого ROM


Мои аргументы, проверяемые, численные, доступные к независимой проверке Вам не интересны


Ок, продемонстрируйте перерасход примерно 250 байт на 5 глобальных переменных типа char.
Вас не затруднит, цитируя меня, НЕ ОБРЕЗАТЬ часть предложения, отчего оно полностью меняет смысл?
А то мне приходится гадать — то ли вы держите в голове фразу, и тогда у вопроса один смысл.
То ли перевираете и передёргиваете тему — и тогда смысл совершенно другой.

Верну в русло, как обычно.
Утащить внутрь функции main глобальные переменные — каждая глобальная переменная в С — примерно +50 байт требуемого ROM.

В результирующем варианте кода светофор есть пара глобальных переменных
uint16_t tl_flash_end, tl_signal_end;
// Program size: 976 bytes (used 95% of a 1 024 byte maximum)

Вас устроит демонстрация перерасхода на них, я сделаю локальными в main()?

Естественно, при этом придётся чуть переписать и остальной код — в нескольких местах вызывается функция, в которой они задействованы.
Но экономия от переноса будет настолько значительна, что покроют даже перенос этой несколько раз вызываемой функции в main().
Я так понял, слово «каждая» вы согласитесь из вашего поста убрать где-то через две-три недели оживлённой дискуссии, а на добавление в эту же фразу слов «в данном конкретном случае» нужно будет не меньше полугода.

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

Хотя тех, кто вас будет читать и написанному верить, немного жалко, конечно.
Единственная проблема обсуждаемой фразы — слово «каждая». Корректнее:
Использование глобальных переменных увеличивает размер ROM. Расходы складываются из инициализации и загрузки значения из памяти в регистр. В данному случае, если утащить внутрь функции main глобальную переменную, то размер ROM уменьшится на 50 байт.
Показательно, что за три дня вас так ничто и не навело на мысль хотя бы немного включать голову перед тем, как писать. Хотя, казалось бы, уже ну просто всё разжевали и в клюв положили.

Использование глобальных переменных увеличивает размер ROM


Код раз:

#include <stdio.h>
char str[100];

int main() {
    for (size_t i=0; i<sizeof(str); i++) {
        str[i] = '0' + i;
    }
    printf("%c", str[sizeof(str)-1]);
    return 0;
}


Код два:

#include <stdio.h>
int main() {
    char str[100];

    for (size_t i=0; i<sizeof(str); i++) {
        str[i] = '0' + i;
    }
    printf("%c", str[sizeof(str)-1]);
    return 0;
}


Не подскажете, что именно случилось вот конкретно на вашем AVR вот конкретно на этом примере с размером в ROM?
Для printf добавляется вычисление абсолютного адреса?

Мне так думается.
И если для глобального варианта адрес = сложение указателя на начало с номером.
Для локального, в стеке, подозреваю несколько помудрёнее.

Хороший пример, ага.
Вам думается неправильно.

Вам здесь в комментариях даже объяснили, почему, но в бескомпромиссной борьбе за право не править в посте откровенную чушь вы это явно пропустили.
Сформулируйте, отчего код с локальными переменными больше, чем с глобальными.
Достаточно просто ссылки на комментарий, где, как Вы считаете, объяснили такое явление.
Да, но единственная проблема обсуждаемой фразы — её выдернутость из контекста.

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

Кстати, бессмысленно и беспощадно утащив пару переменных внутрь уменьшил требуемое место на кристалле
uint16_t tl_flash_end, tl_signal_end;

Даже с дублированием строк функции, ранее пару раз вызывавшейся в main() получилась -76 байт.
//Program size: 976 bytes (used 95%)
стало
//Program size: 900 bytes (used 88% of a 1 024 byte maximum)
Контекст мыслей автора всегда очевиднее автору. Всё что может быть истолковано не так — будет истолковано не так. А в данном случае, субъективно, видится более общий вывод. В данном случае эффект таков. Для самых младших кристаллов это действительно важно. Но в общем случае всё несколько сложнее.
Поэтому, если вам важно быть понятым правильно, было бы лучше несколько аккуратнее переформулировать эту фразу.
splav_asv
Поэтому, если вам важно быть понятым правильно, было бы лучше несколько аккуратнее переформулировать эту фразу.

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

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

Обсуждаемая фраза провокационна, но не более, чем предыдущая «В освободившееся место и пины впихнуть обмен данными для управления несколькими светофорами по ИК/UART.» 100% гарантии, что функционал влезет — нет. Она точно так же в виде гипотезы-TODO к конкретному коду написана, и не сформулирована с посылом "В любой, без исключения программе, в любой архитектуре, ни в коем случае не используйте глобальные переменные", как пытается представить сейчас мой собеседник.
Написать без провокационной окраски? Выбьется из ряда и стиля.

Она описывает предположительные возможные действия, необходимые для уменьшения размеров прошивки конкретной программы. И, порядок дельты бюджета ROM глобальной/локальной переменной в ней отражен верно.
Более того, в последней версии на github размер компилирующейся прошивки на 76 байт меньше — как следствие проверки и реализации этой TODO.

Естественно, абсолютно все глобальные переменные в обязательном порядке перетаскивать в локаль не нужно — но в спорной фразе этого экстремизма и нет. А в комментариях достаточно развернуты факты, конкретезирующие, что же конкретно я имел в виду, и объясняющие, за счет чего экономия ROM случится.

Но Вы, повторюсь, правы, и экономия от переноса глобали в локаль неочевидна для многих.
На правах идеи: можно в конце статьи сделать раздел «Исправления и примечания». И текст исходный не изменится, и на видном месте уточнения и исправления будут.
Вариант вполне годный, да.
Уберите из статью это фразу: "каждая глобальная переменная в С — примерно +50 байт требуемого ROM."
Так как она в корне не верна.
Поясните почему происходит увеличение прошивки при использовании переменных с различными областями видимости, в результате работы конкретного компилятора и конкретных его настроек, и конкретного скрипта компоновщика. Почему нельзя злоупотреблять локальными переменными, чем это вредит.
А так это выглядит: «я тут ткнул пальцем в небо и решил».
Убрать не уберу, естественно: это моя статья, если Вы заметили. Моё право прислушаться к мнению, вежливой просьбе, совету, но не к требованию.
Вы же вольны выразить отношение в комментариях.

Я буду благодарен Вам, и обещаю добавить в статью, сохранив копирайт, Ваш ответ на вопрос: «чем же всё таки вреден перенос глобальной переменной, задействованной только в main() в пространство имен main()?»

Если он будет отличаться от моего мнения: «по расходу RAM ничем, для AVR8 — полезен уменьшением программного кода».
Статья Ваша. В целом написана не плохо, но некоторый моменты вводят в заблуждения неокрепшие умы, которые не до конца понимают устройство и принципы работы микроконтроллера (большинство «ардуинщиков»). И эти неокрепшие умы поверят Вам на слово про ~50Байт на каждую глобальную переменную и будут все пихать в стек, пока не получат переполнение стека. Или Вам все равно на таких, что принижает Ваши профессиональные навыки, как программиста микроконтроллеров, или Вы не до конца разобрались и не хотите принять правильную точку зрения.
Утащить внутрь функции main глобальные переменные — каждая глобальная переменная в С — примерно +50 байт требуемого ROM.

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

Надеюсь, неокрепшие умы из статьи запомнять, что перенос глобальных переменных в main высвободит заметную часть ROM. И никаких отрицательных последствий у этого действия нет.
Если эти глобальные переменные используются не только в main, там есть варианты, например, или не трогать, или реврайтить код на единичный вызов функции и перенос функции в main же — очень вероятно, что суммарно код уменьшится.

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

Вы уверены, что это верный посыл?!

Я не вижу смысла продолжать разговор с человеком, который не понимает, чем чревато использование большого количества локальных переменных, это еще раз говорит о Вашей низкой профпригодности, как разработчика встраиваемых систем. Я все больше склоняюсь, что Вы не далеко ушли от «ардуинщиков». И Ваша статья из раздела: «Смотрите как могут „ардунщики“, который освоили FSM».
Я просил сформулировать Ваш ответ на вопрос, но его не вижу.

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

Диагноз: «Ардуино головного мозга.»
по расходу RAM ничем

Как Вы сильно заблуждаетесь! С таким подходом Вам не стоит заниматься программированием для микроконтроллеров и не писать об этом статьи.
Не в области данных, там все остается неизменным, в области программного кода наблюдается увеличение размера.
Увольте, сударь?
Увеличение размера ROM от переноса глобальной переменной в локальную в main?
Не затруднит ли Вас привести пример архитектуры, на которой такое может произойти?
Не то, чтобы я ставлю под сомнение Ваши слова, но они расходятся с моим опытом, и я не прочь узнать конкретный пример.
UPD: моё упущение, не поставил адресата, GarryC вопрос и просьбу этого комментария адресую Вам.
Увеличение размера ROM от переноса глобальной переменной в локальную в main?

Вы путаетесь в своих показаниях.
Мне ждать Вашего ответа на вопрос «чем же всё таки вреден перенос глобальной переменной, задействованной только в main() в пространство имен main()?»?
Вполне могу себе представить такую архитектуру, в которой от переноса глобала в локаль (честную локаль, не статическую) вырастет размер кода. Допустим, у вас есть непосредственная адресация, тогда чтение глобали выглядит так:
mov r0,@#Var1,
но у вас нет индексной адресации, тогда чтение локали выглядит так:
mov r0,SP; add r0,#var1; mov r0,@r0
— очевидно, что второй вариант намного длинее.

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

А если речь идет о существующих МК, то попробуйте завести в AVR локаль общей длиной более 64 байт (например, char c[65]) и обратитесь к последнему элементу — обещаю кучу развлечения, хотя готов признать, что пример несколько надуманный.
Да и обращение просто к элементу массива в локали иногда получается медленнее для AVR, хотя иногда быстрее, заранее не узнаешь. Это я Archy_Kld
отвечал :)
А если речь идет о существующих МК, то попробуйте завести в AVR локаль общей длиной более 64 байт


Более 62, на самом деле.

Во-первых, максимальное смещение у LDD — это 63, во-вторых, начало стека — это Y+1.
GarryC
Классный пример. Правда.

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

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

чтение глобали выглядит так:
512 бит r0,@#Var1,
но у вас нет индексной адресации, тогда чтение локали выглядит так:
mov r0,SP; add r0,#var1; mov r0,@r0
— очевидно, что второй вариант намного длинее.

Чисто ради прикола, не придирка, не обижайтесь, но: не абсолютно очевидно.
Если не оговорена разрядность. А вот пусть для контрпримера адрес 64 бит, кто не любит много-много-много памяти.
@#Var1 — это же чисто адрес. Абсолютный, определяемый разрядностю шины адреса. 8 байт + пусть 1 или 2 байт на инструкцию mov.(9-10)
mov r0,SP; — 1 или 2 байта. Скорее 1, индексную адресацию мы выкинули, 256 операндов не на всё хватит? (1-2)
add r0,#var1; — #var1; тип #var определяет, конечно. Может байт, а может qword (2-10)
mov r0,@r0 — 1-2 байта.

Итого от 4 до 14 байт кода для локали против 9-10 байт для глобали.
Операций больше, занимаемого кодом места — не факт.
Я запускаю руку меж ланит ассистентки и достаю кролика… Погладьте кролика, он существует.
Просто гладьте.
Ну уж нет! Кто вас знает, откуда вы перед этим его вытащили.
В утверждении «Утащить внутрь функции main глобальные переменные — каждая глобальная переменная в С — примерно +50 байт требуемого ROM… „ есть вопросы к слову каждая
Скорее “Утащить внутрь функции main глобальные переменные — использование глобальных переменных в С — примерно +50 байт требуемого ROM… „
Если читать фразу по отношению к конкретной программе, и слово каждая, и оценка размера ~+50байт таки являются верными. Что было подверждено
Если говорить про абстнактную программу на С — то из формулировки необходимо убирать численную оценку.
В идеале, конечно, еще лучше отдельно расписать механизм, обуславливающий больший расход ROM на глобальные переменные. Из этого описания видно, отчего использовано слово «каждые», и какие при этом могут случаться исключения.
Собственно, это выводы из сравнения asm, но это кому-то очевидно, кому-то требуется много дополнительных букв.
У настоящих светофоров никогда не делают световозвращатели
Не путайте понятия отражатель и световозвращатель.
Я не уверен, внесено сейчас в гост или это локальная нормативка, но у правильного светофора сейчас включение зеленого смещено на пару секунд, какое-то время двойной красный горит.
Только после пешеходного. Те самые три секунды недостающего желтого сигнала.
Большое спасибо! Отличная статья.

P.S. На настоящую игрушку, может и не дотягивает, но великолепно подходит как заготовка для целой серии игрушек…
Еще не дочитал, но уже нравится. Оставляю как чтиво после работы, вместо Донцовой.
Хорошая длинная статья, написано интересно, спасибо.
Один момент: если кто-то будет учиться программированию AVR по ней, прошу обратить внимание на использование глобальных volatile uint16_t переменных.
У автора основной цикл спит между прерываниями таймера, но вообще есть опасность нарваться на неатомарный доступ к таким переменным, когда команды чтения в основном цикле прерываются записью в прерывании.
Верно, не акцентировал внимания, исправляюсь.
Квалификатор volatile не только компилятору запрещает кешировать переменную в регистре, но и накладывает обязательство на автора программы — рассмотреть последствия возможных изменений.
Здесь volatile только переменная глобального таймера, основной цикл выполняется за время многократно меньшее промежутка её инкремента от переполнения, а выполнение цикла инициируется её изменением от старта прерывания переполнения счетчика.
А вот при просыпании из POWER_DOWN_MODE, хотя она обнуляется, но таймер не реинициализируется, и, естественно, её значение может скакнуть в любой момент.
Последствиями будет возможное изменение периода самого первого после просыпания сигнала на дельту времени до 1/37 секунды. (Или до 1/64, если выполняется на МК с частотой 16МГц).
Критической проблема может стать, если случится переполнение целого беззнакового, uint16_t из 65535 превратится в 0: вся логика сравнений превратится в тыкву. Для недопущения этого при достижении безопасного порога в половину максимально возможного значения типа переменной в самом начале основного цикла идет корректировка глобального таймера и значений, которые с ним сравниваются.

#define MAX_GLOBAL_TIMER_VALUE	(USHRT_MAX / 2)		// uint16_t globalTimer - защита от переполнения. 65535 /2 
// любой период должен быть меньше, чем MAX_GLOBAL_TIMER_VALUE - 1
...
// переполнение глобального таймера? 
		if(globalTimer > MAX_GLOBAL_TIMER_VALUE){
			globalTimer -= MAX_GLOBAL_TIMER_VALUE;			// откатить глобальный таймер
...
Статья неполная! Тема сисек трассировки печатной платы и ее вытравливания не раскрыта!
Сделайте схему фонарика, пожалуйста, он проще, но более… хм… распространен. Один главный светодиод, парочка цветных, чтобы указывать где лежит фонарь в темноте и моргать когда пора заряжать батарейку. Ну и несколько режимов, конечно: яркий, средний, слабый.
Мне почему то кажется, что при аккуратном обращении в данном конкретном случае волшебное слово «Чарлиплексинг» нивелировало бы некоторые свои минусы, хотя особых плюсов и не дало, помимо учебных целей.

Ну и в порядке занудства, почему
#define PERIOD_0    ONE_SECOND * 10 //R G R G   0. красный --- зеленый  (10 сек)
, а не
#define PERIOD_RED_GREEN    ONE_SECOND * 10 //R G R G   (10 сек)
Неужели мы экономим символы?
Чарлиплексинг я упомянул, как один из возможных вариантов, вовсе не подразумевая, что он негоден. Не упомянул его преимущество (теоретически, проверять все же надо) возможности отказаться от токоограничивающих резисторов для LED, т.к. их питание пойдет в импульсном режиме. И подрастет код (сейчас 96% кристалла), задача уместить не выглядит невозможной, но это будет уже иная конструкция.

Дефайны PERIOD_0 — PERIOD_7 используются только при инициализации массива состояний traffic_signals[], и я счел более важным их наименованием подчеркнуть небесспорный и неочевидный момент: номера в массиве используются в коде. Перемещение их на другие места не допускается. Переименовывание их в предложенный вариант — потеря информации. Конечно, можно сохранить отсылку к нумерации и добавить информацию о цветах режима.
#define PERIOD_0_RED_GREEN    ONE_SECOND * 10 //R G R G   (10 сек)

Но нормальная IDE при наведении курсора на дефайн показывает и его значение, и комментарий рядом.
Ну я имел в виду, что при аккуратном размещении, наверное, можно сделать все режимы статическими, но это в Вашем случае не особо нужно.
А вот о второй части Вашего предложения — не учите детей плохому, НЕЛЬЗЯ отказываться от токо-ограничивающих резисторов, от слова совсем нельзя, и неважно, какой у Вас режим — статика либо динамика.
Производитель МК НИЧЕГО не гарантирует при превышении тока на ножку, и ссылка на конкретного Дядю Ляо, который себе это позволяет, не прокатывает.

А по поводу имени дефайна — говорят, что хорошо написанный код само-документирован и это как раз тот случай.
UFO just landed and posted this here
Искренне сожалею, что вы в статье увидели использование Ардуино для уменьшения потребления с 3мА до 1мА.
На самом деле мне хотелось написать другие цифры, на порядок большие, и рассказать в каких ситуациях они возникают. Хотелось рассказать, что можно программировать без Ардуино и какие плюсы при этом пожинать. Мечталось показать порядок влияния на энергопотребление всего устройства разных его частей, акцентировать, что потребление самого МК может стать меньше погрешности ошибок измерения. Показать, что из практически веток, травы и лесного ветра можно сделать забавную игрушку, причем значительная часть работы будет вместе со своими детьми.

Извините, что у меня не получилось всё это показать и сделать для Вас.
Я, честное слово, старался.
UFO just landed and posted this here
Можно было бы уполовинить статью, поставив stm32f030f4p6. Площадь на плате как у attiny13, стоимость примерно схожая. Остались бы ножки для объединения светофоров в сеть для сложных перекрестков. DIP Trace плата вполне трассируется, утюжится не хуже.
Судите сами: стоимость ATtiny13 = 0 (ноль) рублей, я там выше писал, мне её жаба выдала со склада. Любая положительная цена, хоть одна копейка за десяток или сотню stm — это в миллиарды, в триллионы раз большая сумма.
А если серьёзно, нужно смотреть по первоначальным требованиям. При необходимости разработки тиражируемого устройства, и DC-DC лучше свой на плате расположить, и МК я выбирал бы другой. И сравнивал бы наверное сначала по критерию энергопотребления различные варианты, по удобству заливки прошивки на потоке и обновления, по фаршу, а затем уже по цене. Возможно, что что-то из TI MSP430 оказалось бы интереснее STM32. Но в любом МК, какого бы размера не были ROM и RAM всегда может случится ситуация нехватки десятка байт. Или пары ножек. Ресурсы — они конечны по определению, хорошо, когда понимаешь какиеконкретно есть варианты более рационального использования.

Проблемы с трассировкой решать меня научили еще на DOSовском PCADе когда-то. И изготавление ПП по ЛУТ для FT232RL с её шириной проводников и зазорами АФАИР 0.3мм тоже пройденный этап. Да и сейчас подготовка гербера и сдача на производство услуга очень распространенная.
Ну, если ваше время бесплатно, то можно Хоть на Z80 делать.
FT232R шаг 0.65, там SSOP.
Это новая для меня мысль, что время для возни с игрушкой для ребенка (а если быть точным, то даже для двух дочек: для самой старшей, которой интересны будут и статья и нюансы программного кода, и для младшей, которой нравится красочные яркие огоньки на светофоре и интерактив), вот это время — его можно рассматривать как платное.

Ширина проводников 0.3 + зазор 0.3 = 0.6 мм.
Да, Вы правы, действительно не 0.65, получается приврал на лапку семнадцатого воробушка.
Да ладно! Дочи-ж помогали папе?
миша, все херня. давай по новой, на ФПГА!

пс. а вообще-то мне (как специалисту по миганию светодиодами) проект понравился.
Светодиоды светят на отражатель (как в некоторых старых фонариках)… Ня!

Автор, вот это я и называю культурой! Хотя никогда особо не считал себя "в теме", но из N прочитанных статей про AVR это первая, где, черт возьми, сделан акцент на энергопотреблении и отказ от раздражающего бесконечного цикла.


… Когда-то давно было у меня учебное задание на супергарварде, и я успокоился только тогда, когда главный цикл выродился в HALT (без DI:), а всё буферное I/O ушло в прерывания и DMA. Хотя мог и не выпендриваться, а сделать как все… Но мой перфекционизм, конечно, тут нервно курит в сторонке:)

богатый внутренний мир простого светофора.
спасибо, було интересно.
Неприятно, когда статья написана на довольно высоком уровне, автор довольно полно описал что и как он делал, обращая внимание на многие (некоторые) тонкости своего проекта, но обязательно появляются ядовитые комментарии в одно предложение. При этом суть этих комментариев сводится (образно говоря) к тому, что если добавить esp то можно было бы «забабахать» крутую метеостанцию, а так проект просто дилетанский: тинька не та, дорожки не те, потребление не в красную армию и вообще лучше бы вообще не надо было начинать…
Раньше китайцы утягивали идеи из журналов «Наука и жизнь», «Радио», «Техника молодежи»… Не удивлюсь, если к следующему НГ будут продаваться такие вот китайские светофоры с прошивкой от автора.
Благодарю за подробную статью с оригинальным стилем изложения — сложно оторваться от чтения. Да и каменты подстать. Желаю дальнейших творческих успехов.
Мигать лампочками как blink или щелкая клавишами можно и без микроконтроллера, а вот светофор — действительно уже представляет логику управления. Собирал такой на ATtiny2313 (ATtiny13 не оказалось, а тех была пригоршня, но остались свободные ноги) в штекере автомобильного прикуривателя.
image
Думалось помигает недолго как легкая елочная игрушка (вместо гирлянды) на еловых ветках в вазе на полке с машинками. Поэтому питал CR2032, держалка которой стала основанием. А вот режим работы предполагал при включении кнопкой сверху: помигать 5 сек желтыми и перейти в рабочий режим на 15 минут, потом ещё 7 минут помигать желтыми и уснуть.

Теперь этот светофор держу на столе и когда нужно уделить вопросу 15 минут — жму клавишу. Вроде время тикает, но не торопит как секундомер и не утекает как песочные часы. Может стоило фанерку лазером выжечь, платку сваять и буззер финальный воткнуть…
Нифига не понял момент с системными клоками. Откуда 9600000? Я нигде не встретил (может невнимательно смотрел?) указания на то, что вы меняли умолчательные фьюзы Tiny13. Но минутное курение даташита сообщает нам, что выпускается с фабрики он с внутренним RC, настроенным на 9,6 МГц при установленном CKDIV8, что означает клоки в 1,2 МГц. Поправьте меня, если я ошибаюсь (но я помню, что все AVR выпускаются с настройкой на внутреннее тактирование 1 МГц. Или в данном случае, близко к тому).

При такой частоте смысл нагружать программу режимом IDLE пропадает полностью. Tiny13 при 1 МГц и 5 вольтах, согласно стр. 125 даташита, потребляет 0,7 мА, что само по себе намного меньше светодиодов. IDLE снизит эти 0,7 мА хорошо, если до 0,5 мА — а оно важно? Если уж sleep, то Power-down всегда, а тактировать можно вообще от прерываний Watchdog, что будет проще.

И еще есть много замечаний по неоптимальному построению программы, но для этого стоит перейти для начала на чистый ассемблер. Эта ваша задачка — однозначно для ассемблерной реализации. Я почитал там вашу с Олегом полемику — вы боретесь с компилятором, а не проектируете устройство. Я прикинул — гарантирую, что можно запихать весь код в полкилобайта Flash (а скорее всего меньше), а SRAM, вероятно, вообще не потребуется, кроме стека для прерываний и подпрограмм.
Я прикинул — гарантирую, что можно запихать весь код в полкилобайта Flash (а скорее всего меньше), а SRAM, вероятно, вообще не потребуется, кроме стека для прерываний и подпрограмм.

Абсолютно с Вами согласен.
Позволю себе привести цитату себя же из этой же статьи
— Перевести на ассемблер — по ROM ужаться раза в 2 можно, переменные сделать регистровыми — не надо будет постоянно читать-писать в память при работе с ними.
Правда, я тешу себя надеждой, что средний ардуинщик сможет разобраться с приведенным С кодом, а вот с ASM иллюзий особых не испытываю. Статья стала бы сильно менее интересной для многих. Задачка же решенная использованием 95% кристалла, или 50%, или 10% на выходе имеет один и тот же результат. Как Вы наверняка прочитали, не ставилось целью оптимизировать программу по критерию «как можно меньший программный код».
«Ничего, ничего, и без паровозика на пиках неплохо вышло»(с).
Поправьте меня, если я ошибаюсь (но я помню, что все AVR выпускаются с настройкой на внутреннее тактирование 1 МГц.

Необъяснимо. Эта тинька валялась в чулане, при заливке на неё прошивки (видно, что avrdude.exe из пакета MicroCore фьюзы не трогает), в которой я определил #define F_CPU 9600000UL, и по этой частоте считал соответствие переполнения счетчика для глобального таймера, оказалось, что временные интервалы отсчитываются верно. Выяснять отчего она работает так, как ожидал я, а не как должна по настройкам с завода, согласитесь, было бы бессмысленно.

Безусловно, вариант цикла через watchdog по энергопортреблению именно МК (а еще и уход в минимальную частоту, да и Brown-Out Detector отключить полезно) бьёт что угодно. Но как видите, на фоне потребления LED и DC-DC потребление самой тиньки — копейки. Для кратного увеличения времени жизни батарейки лучшее решение, по-моему, через полевые транзисторы вообще отключать батарейку на время «выключения», но это другая схема и другая прошивка.

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

А про такты — действительно необъяснимо. Смиримся.

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

Это ведь не ассемблер.

Процессорозависимых мест совсем чуть, и они тривиальные по решению.
Тезисно задачи навскидку, без конкретизации конкретного stm:
— инициализация периферии и установка тактирования добавится в stm. Через куб, как заготовку.
— globalTimer на прерывании переполнении счетчика — регистрами будет отличаться, смысл и работа те же.
— Количество тиков для одной секунды и четверти ONE_SECOND и QT_SECOND другое.
— Режимы засыпания устанавливаются по своему, но смысл тот же.
— статей stm blink как грязи, установка значений пинов регистра/потяжки тут в отдельной функции — вся возня в одном месте.
— внешнее прерывание и его поведение в самом глубоком режиме сна, да.
— факт нажатия кнопки — чтение пина на вход.

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

Но, простите, в лоб перетаскивать решение — оно не эффективно по энергопотреблению.
Этот проект вообще проект из серии «упавшее дерево» — пилить несложно, но смысл, возможная цель просто повтора мне непонятны.
Моя цель была выяснить, «а можно ли уместить в тиньку?», и чтобы результат был красивым.
Признаться честно, подготовка статьи была более трудозатратна и отняла больше времени, чем сами светофоры.
После прочтения снова захотелось написать статью, как я делал в качестве эксперимента ПЛК на тини13 с 8 входами и 6 выходами без сдвиговых регистров.
Многие вспоминают про регистры и прочие микросхемы расширения. Я одно время тоже занимался тини13, выжимал из неё всё что можно с помощью регистров, потом подумал: тини+регистр стоит почти как Атмега8 в смд, ещё всё это соединить, а где смысл? Теперь продолжаю иногда извращаться, но на практике предпочитаю ставить самую дешёвенькую атмегу, места меньше, чем тини с регистром, цена та же, памяти больше.
Хотя, повторюсь, спортивный интерес отвергает все логические доводы.
ПЛК на тини13

Вы уверены, что именно ПЛК Вы делали?!
Программируемый логический контроллер.
Да, уверен. Управлять серьёзным оборудованием, где подвергаются опасности люди, я бы ему не доверил. Но с простыми задачами, где есть 8 входных датчиков (включено-выключено), и 6 выходных реле или клапанов, он бы вполне справился. Делал только в макете.
И на каком же языке он программируется, этот контроллер?..
Слышу иронию в Вашем голосе )
Так как используется Attiny13, то можно программировать на Ассемблере (который не знаю) и на Си++, который знаю, но не очень. Так как прошивал в среде Ардуино (в этот момент слышу злобное шипение комментаторов), то можно программировать на Wiring (который тот же С++, по сути). Но так как в качестве посредника я использовал FLProg, (низкий поклон его создателю), то мой ПЛК программируется в FBD или LAD, я лично предпочитаю последний, несмотря на очень неплохие познания в цифровой логике.
А вообще ПЛК остаётся ПЛК вне зависимости от того, на каком языке он программируется, язык просто облегчает общение. Хороший работник остаётся хорошим, даже если Вы не знаете язык, который он понимает.
Ваш ПЛК для программирования использовал стандартизированные языки МЭК (IEC) стандарта IEC61131-3?
Вы ещё про сертификации спросите )
Ключевое слово «делал». То есть не «сделал», не внедрил, и не использую. Задачей (себе поставленной) было доказать, что это технически возможно, что хватит ресурсов Тиньки. Попробовал, убедился. Но экономически целесообразней поставить Атмегу, она всё то же сделает без напряга.
Это были вопросы в термину ПЛК. Обычно ПЛК устройство называют если оно программируется в соответствии с указанными стандартами. Есть обоснованные сомнения, что это возможно на tiny13. Потому и вопросы.
То, что у вас(как предполагаю) — контроллер, выполняющий сходные задачи, но с фиксированной программой.
В некоторых случаях для программирования ПЛК используются нестандартные языки
Свободно программируемые: программа загружается в ПЛК через его специальный интерфейс с Персонального компьютера используя специальное ПО производителя, иногда с помощью программатора.

Статья в Википедии каждой строкой, за небольшими исключениями, подходит под описание моего устройства. Возможно, оно Программируемое (интеллектуальные) реле. Я не претендую на название.
в отличие от встраиваемых систем ПЛК изготавливаются как самостоятельные изделия, отдельные от управляемого при его помощи оборудования.

У вас — встраиваемая система.
Но согласен, что грань тонкая. ПЛК — универсальный контроллер общего назначения, программируемый для работы с конкретным оборудованием, как правило на специфических упрощенных языках. Т.е. инженерами или прикладными программистами. Уровень абстракции разный у программ. В одном случае биты регистров. В другом — сигналы на интерфейсах.
Возможно, мы просто друг друга не понимаем, но я не согласен. Посмотрел, что является встраиваемой системой, не подходит совсем. Но спорить не хочу. Если устройство может выполнять все функции ПЛК, но при этом не является ПЛК, пусть не является.
Явно не хватает собственно предмета обсуждения.

Как мне видится, на тиньку впритык, но можно впихнуть прием данных
(формально нет разницы, ПЛК по Modbus или UART их принимает), и эти данные можно и для внутрикристального программирования использовать, и как матрицу поведения).
Да и термин «встраиваимая система» при штатно задуманном и реализованном перепрограммировании через перепрошивку вполне себе ПЛК, по интерфейсу не полностью соблюдающий IEC стандарт.

Самодельный самолет, не сертифицированный, не поставленный на учет, без радио, приборов, без соблюдения РД ICAO — все равно будет называться самолетом, а не «устройством для перемещения по воздусям», хот нафик-нафик эту конструкцию выпускать в воздух.
Не настаиваю, что у вас не ПЛК (не зная точно, о чем речь — настаивать глупо). Просто призываю аккуратнее использовать термин ПЛК, можно нарваться на… в общем на излишние обсуждения на повышенных тонах.
Не вижу причин, почему статья про ваш опыт не может быть представлена здесь. Просто аккуратнее с терминами и выводами.
Желаю творческих успехов!
Не надо называть ПЛК, что им не является. Одно из основных характеристик ПЛК является «программируемый». Следовательно Ваше устройство должно было поддерживать внешнее программирование под различные варианты технологического процесса, а не смену прошивки для каждого нового технологического процесса. Вы скорее разрабатывали центральное управляющее устройство.
Те ПЛК, что применяются на моём (и не только) предприятии (пример), при любом изменении управляющей программы (или как она правильно называется, это не моя прерогатива, но часто присутствую при этом процессе по роду деятельности) сначала полностью очищает память контроллера, и только после этого заливает новую программу. Чем это отличается от перепрошивки в моём случае? Хотя сама Тини, конечно, плк не является.
seri0shka
Мне была бы интересна Ваша статья, и я знаю, что не только мне.
И, кстати, не подумайте, что Ваши слова «ПЛК на тини13» можно как-либо не так понять. По описанию четко определен уровень изделия. Да, на издевательские встречные «вопросы» Вашего визави большинство читателей брезгливо поморщится и просто не захочат влезать-пачкаться.

Мне по этой статье люди задавали вопросы в личке.
В сообщениях, представляете?
Хорошие были вопросы, умный человек, разбирается в С, по тонкостям реализации логики, по машине состояний, по очередности вызовов.
— Почему не в комментариях? — А в комментах заклюют.

Достаточно хорошо изученное и описанное явление: профессиональная опасность программирования вовсе не воспаление лучезапясного сустава, а психологическая недоразвитость, низкий эмоциональный интеллект (EI, emotional intelligence) при высоком IQ.

Подросток день и ночь сидит в мире цифр: здесь хорошо, уютно и понятно. Здесь волшебное окошко чата, в котором он может представиться хоть Мерлин Монро, хоть Юлом Бриннером, и вести себя, ну словно настоящий альфа-самец. Где такие же гики, которых он, правда, никогда не видел вживую, с такими же проблемами, и вся социализация выхолащивается в «кто лучше напишет код», за счет реальной жизни. Необходимо компенсировать случаи жизни в реальном мире, в котором построить отношения с окружающими людьми так и не научается.

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

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

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

Archy_Kld, спасибо за поддержку. Вторая причина, почему не написал ещё (первая- лень, по-простому) — это именно неприятие Ардуино на Хабре и «знатоки» в комментариях. Тем более, что мой уровень знаний в программировании микроконтроллеров меньше, чем у Вас. Но если я всё-таки напишу статью, то благодаря Вашему комментарию.
Замечательно, читается как художественная литература. Не оторваться!

диод для зеленого пояса — без него все 4 зеленых LED последовательно немного светились

А остальные цепочки без диода не светились, почему? Он установлен между цепочкой и землёй? А яркость плечей разной не стала от этого? А почему на схеме в зелёной цепочке красный светодиод?

Очень понравилось оформление рисунка машины состояний. В графическом редакторе рисовали или в специальной программе? Скажите название, тоже такую хочу!
А остальные цепочки без диода не светились, почему? Он установлен между цепочкой и землёй? А яркость плечей разной не стала от этого?

Для самого загадка. У зеленых падение напряжения обычно с запасом больше, чем у красных. Разница яркости плечей незаметна на глаз, иначе пришлось бы ставить в другое плечо. У остальных цветов всё хорошо и ровно по теории — превышение суммарного падения напряжения больше 5 — не светит, не греет, допдиодов не просит.

А почему на схеме в зелёной цепочке красный светодиод?

А я то думал, никто так и не заметит. В файле схемотехники всё верно.

В графическом редакторе рисовали или в специальной программе? Скажите название, тоже такую хочу!

В специальной очень профессиональной программе: Microsoft Office Visio 2003. Исходник рисунка визио машины состояний на гитхабе в ./docs.
18000 просмотров, и никто не попробовал проверить код компилятором? Я что, один такой уникальный?
У Вас в окончательном исходном коде не хватает одной "}" после
и в самом конце бесконечного главного цикла — уходим в сон.
Я чуть голову не поломал, хотя компилятор пытался мне объяснить.
Проверил и окончательный исходный код из статьи, и предпоследний пример из текста — так же нормально компилируется.
Простите, повторить описанное не удалось.
Скрины:
Заголовок спойлера
image
Да, я из статьи копировал. Добавьте в коде в статье вторую фигурную скобку в указанном месте после «и в самом конце бесконечного главного цикла — уходим в сон.» Должно быть две, стоит одна.
Кстати, не подскажете, где найти нормальное описание работы с github? Недавно пытался начать, что-то ничего не получается, кроме «Hello-Git».
Да, было, поправил.

где найти нормальное описание работы с github?
Я бы сказал, обучение работы с git — скорее обучению образа мышления.
Сначала просто поставить git, разобраться с идеологией работы с локальным репозиторием, как посмотреть историю, как откатиться на конкретный коммит.
Потом — работа с удаленным репозиторием, не важно bitbucket или github или сервер в интрасети, синхронизацией состояний, работа с конфликами версий.
Потом — какие удобства даёт ветвление при разработке.
Не буду брать на себя ответственность рекомендовать конкретную статью, на хабре их немало.

А по конкретным командам я себе, например, форкнул шпаргалку. И теперь в два клика она доступна у себя в репозиториях на GitHub.
Собственно я искал уже, но вот именно на хабре ещё не искал, надо попробовать.
Sign up to leave a comment.

Articles