Директивы #ifdef и #ifndef

ank-sw
Offline
Зарегистрирован: 20.03.2014

Есть некоторый код. Для примера, сократил до минимума, вместе с комментариями.

У меня 2 платформы. Одна на базе UNO, вторая на базе MEGA. В зависимости от директивы условной компиляции должен прошиваться или UNO или MEGA. Затык в том, что не компилирует. Даю примеры:

 1. Компиляция проходит. Все OK.

#define board_MEGA

#ifdef board_MEGA
const int TestConst = 12;
#else
const int TestConst = 2;
#endif


void setup() {
}
void loop() {
}

void Func_1() {
  Func_2();
}

void Func_2() {
}

 2. Комментируем строку //#define board_MEGA. Не компилирует. Выдает ошибку:

Arduino: 1.5.5 (Mac OS X), Board: "Arduino Uno"

Test_03_UNO_MEGA.ino: In function 'void Func_1()':

Test_03_UNO_MEGA:22: error: 'Func_2' was not declared in this scope
//#define board_MEGA

#ifdef board_MEGA
const int TestConst = 12;
#else
const int TestConst = 2;
#endif


void setup() {
}
void loop() {
}

void Func_1() {
  Func_2();
}

void Func_2() {
}

3. В третьей строке меняем директиву на #ifndef. Опять компилирует.

//#define board_MEGA

#ifndef board_MEGA
const int TestConst = 12;
#else
const int TestConst = 2;
#endif


void setup() {
}
void loop() {
}

void Func_1() {
  Func_2();
}

void Func_2() {
}
В чем проблема? Не понятно. 
 
 
 
ank-sw
Offline
Зарегистрирован: 20.03.2014

PS

Если в третьем примере раскомментировать первую строку - опять ошибка компиляции.

GraninDm
Offline
Зарегистрирован: 01.08.2013

'Func_2' was not declared in this scope

Func_2 нужно объявить до использования 

void Func_2() {
}

void Func_1() {
  Func_2();
}

Вообще почитайте что-нибудь по C

leshak
Offline
Зарегистрирован: 29.09.2011

Вообщем похоже на заглюк именно самой ArduinoIDE/компилятора.

Что-то похожее у нее происходит при импользовании struct в качестве параметров/возвращаемых значений.

Вынесении объявления struct  (в вашем случае этого #ifdef) в отдельный .h файл и его подключение решает проблему (видимо тогда оно понимает что "это честый с-и и не пытается че-то там его перекалапуцать).

Кстати, #define board_MEGA IMHO не самый удобный вариант.

что-то типа

#if defined (__AVR_ATmega1280__) || defined (__AVR_ATmega2560__)
для меги
и 
#if defined (__AVR_ATmega328P__)

для уны

Будет более удобно. Вот эти макросы __AVR_..... компилятор сам определяет в зависимости от того какая плата сейчас выбрана в Tools/Board. Не нужно ничего править в коде, при компиляции под другую плату.

 

leshak
Offline
Зарегистрирован: 29.09.2011

GraninDm пишет:

Вообще почитайте что-нибудь по C

Оп-па... а ну подскажите вот какое "что-нибудь" нужно почитать?  По C? Где сказанно что порядок объявления фунций, внутри одного scope имеет значение? Вы часом с javascript не перепутали язык? Мы вообще-то имеет дело с типизированным, компилируемым языком. Который далеко не "в один проход" работает. Вначале лексический анализатор проходит, потом синтаксический, потом из этого строититься AST-дерево, потом.... вообщем тоже можете что-нибудь про компиляторы почитать.

А если из func1() у меня вызывается func2(), а из func2() может вызываться func1(), тогда что читать нужно будет?

P.S. В .ino файлах, особенно когда их несколько, действительно порядок ИНОГДА имеет значение. Но это именно БАГА, а не "ограничение языка". Лечится либо через внешние .h файлы, либо с помощью ключевого слова extern где-нибудь выше использования.

 

ank-sw
Offline
Зарегистрирован: 20.03.2014

leshak пишет:

что-то типа

#if defined (__AVR_ATmega1280__) || defined (__AVR_ATmega2560__)
для меги
и 
#if defined (__AVR_ATmega328P__)

для уны Будет более удобно. 

А вот это интересно. Будем играться с этими определениями.

 

По поводу языка C и места функций, то я, хоть и турист в этом, тоже знал, что место расположения функции в программе значения не имеет. В том коде, который глючил с моими #ifdef, я так и сделал, вынес все мои функции перед Setup. И расположил их в обратном порядке по обращению к ним. Т.е. сначала Func_2, за ней Funk_1 вызывающая Func_2. Но, понимая что это не правильно, все же задал вопрос знатокам.

Спасибо. Будем играться и искать глючки Arduino IDE.

К стати, интересно, у меня Mac OS. А под Windows в IDE те же проблемы?

leshak
Offline
Зарегистрирован: 29.09.2011

ank-sw пишет:

К стати, интересно, у меня Mac OS. А под Windows в IDE те же проблемы?

Да. Я первым делом проверил ваш код (у меня windows). И функции местами попереставлял (да, хоть "по стандарту" и не играет рояля, а иногда помогает) и еще всякое попробовал. Например если Serial.println(TestConst), то мне кричало что "unknown Serial".... вообщем совершенно невообразимые ошибки валились. Похоже что, почему-то срывает ему башню и какая конкретно ошибка вылезет - значения не имеет.
И вынос в .h файл попробовал - убедился что "это помогает" в вашем случае.
 

 

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

Есть параллельная тема, где, возможно, подобная лажа (проблема ArduinoIDE)

http://arduino.ru/forum/programmirovanie/oshchibka

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

Если кто не знает или не помнит, то добавляем в preferences.txt строку (ArduinoIDE должен быть закрыт, кто не знает где лежит preferences.txt гуглите):

build.path=build

Создаем в каталоге, где установлена ArduinoIDE подкаталог build.

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

Возможно я ошибаюсь, но можно попинать монитор, если кто хочет.

 

ank-sw
Offline
Зарегистрирован: 20.03.2014

Сделал и с этими определениями #if defined (__AVR_ATmega1280__) || defined (__AVR_ATmega2560__)

и с заголовками .h

Вес проблемы решены. Стало супер удобно: меняешь тип платы в IDE и дальше ни о чем не заботишься.

leshak
Offline
Зарегистрирован: 29.09.2011

ank-sw пишет:

Вес проблемы решены. Стало супер удобно: меняешь тип платы в IDE и дальше ни о чем не заботишься.

А фиг там.... в вашем случае - да. А у меня, скажем, нужно отличать Leonardo и Pro Micro. И оба на 32u4. Но при этом какие ноги выведены, а какие нет - различается.... вообщем-то есть идеи как.... но нужно будет еще разбираться с этим.

 

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

leshak, да уж, задачка.. Думаю Вы уже заглянули в каталог

D:\Programs\Arduino-1.0.5\hardware\arduino\variants\micro

хотя по этим файлам ничего не определишь, всё как то через одно место..

И еще есть комментарий в загрузчике, что у Микро полярность светодиодов на RX TX обратная :) Ага это сильно поможет, конечно :)

UPD Разве что если загрузчик можно поменять, то было бы проще. Еще вариант с EEPROM, но это хуже, ИМХО

leshak
Offline
Зарегистрирован: 29.09.2011

kisoft пишет:

leshak, да уж, задачка.. Думаю Вы уже заглянули в каталог

D:\Programs\Arduino-1.0.5\hardware\arduino\variants\micro

 

Да бегло смотрел, где-то там видел что дефайнится что-то типа ___coreXXXX, но вот попробовать - не успел.
Или у tennesy что-то такое было...., ну в конечном итоге думаю в любом случае через variants можно будет реализовать. Вопрос только в том "сколько самому дописывать".
Через variants думаю и две одинаковые платы можно будет отличать (выбирая их в Tools/Boards), которые вообще отличаются "только именами". Но тогда прийдется эти variants плодить....
Хотя... в идеале конечно бы-бы найти какой-то дефайн который прямо из board.txt берет имя секции....
nano328.name=Arduino Nano w/ ATmega328

Но подозреваю, что вряд ли это есть в arduinoIDE.

А если бы они еще реализовали, в boards.txt "наследование плат....". Хотя, вообщем-то можно и препроцессор какой-топ прикрутить, который из boards.tempalte генерит boards.txt :) и там уже "что душа пожелает твори..".

А еще хочется какой-то PreBuild Event и PostBuildEvent иметь и вешать на них свои скрипты... Вот тогда точно "делай что хочешь".... :)

 

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

Заканчивая оффтоп, пришла такая простая мысль, можно сделать в pins_arduino в каталоге micro дефайн, который точно определит, что это micro :) Самое тупое, на мой взгляд.

 

 

leshak
Offline
Зарегистрирован: 29.09.2011

kisoft пишет:

Заканчивая оффтоп, пришла такая простая мысль, можно сделать в pins_arduino в каталоге micro дефайн, который точно определит, что это micro :) Самое тупое, на мой взгляд.

Ну дык это и есть то что я описал "через variants"... но не хочется плодить сущности. Вначале хочу убедитяся/полазить что точно нет "способа из коробки", не внося изменений в папку Arduino....
Бо потом дашь кому-то... а у него нет "таких поправок", или сам в соседнюю папочку распакуешь еще одну версию ардиуны..., а там "магической добавочки" - нет (у меня их уже как у бобика блох версий ArduinoIDE/библиотеки :)

 

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

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

Ну раз не оффтоп, тогда делаем открытия. Я, собственно и сам не знал, спасибо за науку, нашел решение.

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

#include "Arduino.h"

#if (USB_PID == 0x8037)
#error "IsMicro"
#else
#error "NOT IsMicro"
#endif

#if defined(__AVR_ATmega32U4__)
#error "Defined __AVR_ATmega32U4__"
#endif

void setup()
{
}

void loop()
{
}

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

D:\Programs\Arduino-1.0.5\hardware\tools\avr\bin\avr-g++ -c -g -Os -Wall -fno-exceptions -ffunction-sections -fdata-sections -mmcu=atmega32u4 -DF_CPU=16000000L -MMD -DUSB_VID=0x2341 -DUSB_PID=0x8036 -DARDUINO=105 -ID:\Programs\Arduino-1.0.5\hardware\arduino\cores\arduino -ID:\Programs\Arduino-1.0.5\hardware\arduino\variants\leonardo D:\Programs\Arduino-1.0.5\build\MicroTest.cpp -o D:\Programs\Arduino-1.0.5\build\MicroTest.cpp.o

Разумеется смотрим на эту часть (здесь для Леонардо):

-DUSB_VID=0x2341 -DUSB_PID=0x8036

Это явно берется из boards.txt (я не пробовал)

Пробовал в среде выбирать нужный board и получал разные сообщения.

UPD: Кусок из board.txt для Леонардо

leonardo.build.vid=0x2341
leonardo.build.pid=0x8036

 

GraninDm
Offline
Зарегистрирован: 01.08.2013

Да. Действительно. Наврал. Можете не принимать к сведению мой комментарий. :)

GraninDm
Offline
Зарегистрирован: 01.08.2013

Вот во что разворачивает Arduino IDE скетч

#line 1 "sketch_jul24a.ino"
//#define board_MEGA
 
#ifdef board_MEGA11
#include "Arduino.h"     //вот тут проблема
void Func_2();
void Func_1();
void setup();
void loop();
#line 4
const int TestConst1 = 12;
#else
const int TestConst1 = 2;
#endif
 
void Func_2() {
}
 
void Func_1() {
  Func_2();
}
 
void setup() {
}
void loop() {
}
 
 
Поставьте в начале скетча
#include "arduino.h"
У меня компиляцию после этого прошла в обоих случаях.
Vovik15
Offline
Зарегистрирован: 10.01.2018

Подскажите, откуда компилятор узнает про эти директивы?

#if defined (__AVR_ATmega1280__) || defined (__AVR_ATmega2560__)

конкретно __AVR_ATmega1280__ и __AVR_ATmega2560__. Мне очень интересно в каком файле это написано. Как компилятор узнает что это AVR_ATmega1280?

 

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

Читайте Кернигана и Ритчи

nik182
Offline
Зарегистрирован: 04.05.2015

Вы сами ему сообщаете, выбирая в среде процессор под который компилируется программа.

Vovik15
Offline
Зарегистрирован: 10.01.2018

Спасибо,почитал я Кернигана и Ритчи, но не нашел там ответа на свой вопрос. Дело в том что я ковыряю библиотеку RF24,там есть такие строки 

 
#if defined (RF24_LINUX) || defined (LITTLEWIRE)
  #include "utility/includes.h"
#elif defined SOFTSPI
  #include <RF24_config.h"> 
#endif
 
тут говорится что если RF24_LINUX или LITTLEWIRE подключить вот эту библиотеку  "utility/includes.h".
только мне вот непонятно,как компилятор узнает что эта за система. конкретно вот это RF24_LINUX непонятно
qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016
/**/
/* запусти сначало так, а потом раскомметируй обе,или одну из строк*/
//#define qwone;
//#define no_qwone;

void setup() {
  Serial.begin(9600);
  Serial.println("start");
#if defined (qwone)
  Serial.println("qwone");
#elif defined no_qwone
  Serial.println("no_qwone");
#endif

}

void loop() {


}

 

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

Vovik15 пишет:

Спасибо,почитал я Кернигана и Ритчи, но не нашел там ответа на свой вопрос. 

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

Cthulhu
Offline
Зарегистрирован: 26.06.2018

Вот так работает:
 

#if defined(ARDUINO_AVR_UNO)
  // Uno pin assignments
#elif defined(ARDUINO_AVR_PRO)
  // Pro Mini assignments
#else
  #error Unsupported board selection.
#endif

Подсмотрел тут