спасибище, а то от этих жмотов не дождешься….
Этот код представляет собой чрезвычайно изощренный пример метапрограммирования на препроцессоре C, написанный специально для Arduino. Давайте разберем его подробно.
Общая структура кода
Код создает эффект цикла for (от 0 до 7) на уровне препроцессора, то есть еще до компиляции. Он генерирует 8 вызовов pinMode в setup() и 8 последовательностей операций с задержками в loop().
Подробный разбор макросов
Базовые строительные блоки
c
#define dr(n,X) (n,X)
#define cat(a, ...) primitive_cat(a, __VA_ARGS__)
#define primitive_cat(a, ...) a ## __VA_ARGS__
dr просто создает пару значений, используется для передачи аргументов
cat и primitive_cat выполняют конкатенацию токенов - ключевая операция для генерации кода
Условные конструкции
c
#define iif(c) primitive_cat(iif_, c)
#define iif_0(t, ...) __VA_ARGS__
#define iif_1(t, ...) t
#define _not_(x) check(primitive_cat(not_, x))
#define not_0 probe(~)
#define _compl(b) primitive_cat(_compl_, b)
#define _compl_0 1
#define _compl_1 0
#define _bool_(x) _compl(_not_(x))
#define _if_(c) iif(_bool_(c))
Это реализация условных выражений в препроцессоре:
iif(1)(a, b) вернет a, iif(0)(a, b) вернет b
_not_, _compl и _bool_ реализуют булеву логику
not_0 определен как probe(~), что позволяет проверять на 0
Проверка и логические операции
c
#define check_n(x, n, ...) n
#define check(...) check_n(__VA_ARGS__, 0,)
#define probe(x) x, 1,
#define empty()
#define defer(id) id empty()
#define obstruct(...) __VA_ARGS__ defer(empty)()
Эти макросы реализуют хитрый механизм отложенного выполнения:
probe создает пару с числом 1, что позволяет проверять наличие аргументов
defer и obstruct предотвращают немедленное раскрытие макросов, создавая задержку выполнения
Работа с числами
c
#define dec1(x) primitive_cat(dec1_, x)
#define dec1_0 0
#define dec1_1 0
#define dec1_2 1
#define dec1_3 2
#define dec1_4 3
#define dec1_5 4
#define dec1_6 5
#define dec1_7 6
#define dec1_8 7
#define dec1_9 8
Это реализация декремента (уменьшения на 1) для чисел 0-9. Обратите внимание: dec1_1 равно 0, dec1_2 равно 1 и т.д.
Многократное выполнение (цикл)
c
#define repeat(count, macro, ...) when(count) (obstruct(repeat_indirect) () (dec1(count), macro, __VA_ARGS__) obstruct(macro) (dec1(count), __VA_ARGS__))
#define repeat_indirect() repeat
#define when(c) _if_(c)(expand, eat)
#define eat(...)
#define expand(...) __VA_ARGS__
Это рекурсивный макрос, который выполняет другой макрос заданное количество раз:
when(count) проверяет, нужно ли продолжать (count > 0)
obstruct предотвращает немедленную рекурсию
Вызывает macro с текущим индексом, затем рекурсивно вызывает repeat с уменьшенным счетчиком
Многоуровневое раскрытие
c
#define eval(...) eval1(eval1(eval1(__VA_ARGS__)))
#define eval1(...) eval2(eval2(eval2(__VA_ARGS__)))
#define eval2(...) eval3(eval3(eval3(__VA_ARGS__)))
#define eval3(...) eval4(eval4(eval4(__VA_ARGS__)))
#define eval4(...) eval5(eval5(eval5(__VA_ARGS__)))
#define eval5(...) __VA_ARGS__
Поскольку препроцессор имеет ограничение на количество проходов раскрытия, эти макросы принудительно выполняют много проходов, чтобы полностью раскрыть все рекурсивные макросы.
Функции работы с пинами
c
#define p6(n) (n+6)
#define m6(_) (p6(_),_>=0)
#define m20(_) ((_+1)*(20-_))
#define drr(n) dr(n,!digitalRead(n))
p6(n) добавляет 6 к индексу (используются пины 6-13)
m6 создает проверку индекса
m20 вычисляет задержку: (i+1)*(20-i)
drr создает пару для digitalRead с инверсией
Основные макросы программы
c
#define M(i, _) pinMode m6(i);
#define S(i, _) digitalWrite drr(p6(i));delay m20(i);digitalWrite drr(p6(i));
M генерирует pinMode(6+i, OUTPUT) для i от 0 до 7
S генерирует последовательность: установить пин, задержка, сбросить пин
Итоговый сгенерированный код
После работы препроцессора код превращается примерно в следующее:
cpp
void setup(void) {
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);
pinMode(10, OUTPUT);
pinMode(11, OUTPUT);
pinMode(12, OUTPUT);
pinMode(13, OUTPUT);
}
void loop(void) {
digitalWrite(6, !digitalRead(6)); delay(20); digitalWrite(6, !digitalRead(6));
digitalWrite(7, !digitalRead(7)); delay(36); digitalWrite(7, !digitalRead(7));
digitalWrite(8, !digitalRead(8)); delay(48); digitalWrite(8, !digitalRead(8));
digitalWrite(9, !digitalRead(9)); delay(56); digitalWrite(9, !digitalRead(9));
digitalWrite(10, !digitalRead(10)); delay(60); digitalWrite(10, !digitalRead(10));
digitalWrite(11, !digitalRead(11)); delay(60); digitalWrite(11, !digitalRead(11));
digitalWrite(12, !digitalRead(12)); delay(56); digitalWrite(12, !digitalRead(12));
digitalWrite(13, !digitalRead(13)); delay(48); digitalWrite(13, !digitalRead(13));
}
Зачем так сложно?
Этот код - гимн избыточности и демонстрация возможностей. В реальной практике такое писать не нужно, потому что:
Обычный цикл проще: for(int i=0; i<8; i++) понятнее и короче
Сложность отладки: Ошибки в таких макросах очень трудно отследить
Производительность: На микроконтроллерах с ограниченной памятью лучше иметь простой код
Что делает программа на Arduino?
Она создает эффект "бегущей волны" на пинах 6-13:
Сначала все пины настраиваются как выходы
В цикле каждый пин переключается (инвертируется), затем идет задержка, затем снова переключается
Задержки разные: сначала растут до середины, потом убывают (пик на 10-11 пинах)
Фактически, это создает эффект движения по светодиодам туда и обратно с ускорением/замедлением в середине.
Этот код - блестящий пример того, как можно заставить препроцессор C делать невероятные вещи, которые он никогда не должен был делать!
ну вообще надо сформировать на что надо обратить внимание, но примерно такой ответ, дипсик)))
лень заниматься одному не понятной фигней))))