Директивы условной компиляции
Многие
микроконтроллеры отличаются лишь некоторыми параметрами, количеством выводов, размером
памяти и размещением регистров. Это позволяет создавать на языке C программный код для всего модельного ряда. Однако
для этого следует каким-то образом заменить те параметры, которые отличаются у
разных моделей. Для таких целей используются директивы условной компиляции.
#ifdef, #ifndef, #else и #endif, а
также #if и #elif.
Синтаксис для директивы #ifdef:
#ifdef имя_макроса
последовательность_операторов_1
#else последовательность_операторов_2
#endif
Если имя макроса определено в программе, то компилируется первая последовательность
операторов, в противном случае — вторая последовательность (ветка #else может и отсутствовать).
Пример
использования (для компилятора CCS-PICC):
#define DEBUGGINGJDN
#ifdef DEBUGGING_ON
fuse rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)
#endif
Синтаксис для директивы #ifndef:
#ifndef имя_макроса последовательность_операторов_1
#else последовательность_операторов_2
#endif
В
данном случае, в отличие от директивы #ifdef, первая последовательность операторов выполняется в
том случае, если имя макроса не определено в программе.
Для условной компиляции можно также воспользоваться
директивами #if, #elif.
Их синтаксис:
#if
выражение1 последовательность_операторов_1
#elif
выражение2
последовательность_операторов_2
#else последовательноеть_операторов_3
#endif
Эта
конструкция работает аналогично условному оператору if-else. Компилятор
оценивает выражения после #if и #elif до тех пор, пока одно из них не даст в результате TRUE, после чего в текст программы подставляется
соответствующая последовательность операторов. Если оба выражения дают false, то
подставляется последовательность операторов после директивы #else (если она присутствует).
Допустим,
для передачи и приема данных через UART
у абстрактного микроконтроллера SomeMicl6
используются выводы 12 и 13, у микроконтроллера SomeMic8 — выводы 6 и 14, а у SomeMic4 — выводы 1 и 2. Тогда мы можем создать
заголовочный файл SomeMic.h и включить в
него следующие директивы.
#if SomeMicX == 16
#define
TXD 12
#define
RXD 13
#elif SomeMicX == 8
#define
TXD 6
#define
RXD 14
#elif SomeMicX == 4
#define
TXD 1
#define
RXD 2
#else
#error
"Pins TXD и RXD for SomeMicX are not defined"
#endif
Теперь,
если программный проект будет построен на микроконтроллере SomeMic8, то в заголовочный файл следует поместить
следующий текст.
#ifndef SomeMicX
#define SomeMicX
= 8
#include <SomeMic.h>
#endif
Встретив директиву #ifndef, препроцессор включит в текст программы #define SomeMicX = 8 и #include <SomeMic.h>. Поскольку после этого элемент SomeMicX получит значение, равное 8, то любая повторная
обработка директивы #ifndef не приведет
к дублированию в выходном тексте соответствующей информации. Другими словами,
содержимое заголовочного файла SomeMic.h будет помещено в исходный код файлов, использующих
заголовочный файл с #ifndef, только один
раз.
Использование
директивы #ifndef с последующими директивами #define и #include — стандартный прием, позволяющий избежать
дублирования информации из заголовочных файлов в исходном коде проекта. Если
этого не сделать, то при дублировании компилятор обычно выдает множество
сообщений об ошибках, связанных с многократным объявлением переменных.
Директива #error
Директива
#error используется совместно с директивами условной
компиляции. Встретив ее, компилятор сгенерирует сообщение об ошибке, указанное
справа от директивы (см. пример в предыдущем подразделе).
Форум
Программа состоит из основного модуля
и вспомогательного модуля со служебными
функциями (сейчас там функции для обслуживания LED и KBD).
Во
вспомогательном модуле описан ряд переменных и процедур, которые должны быть
видны из главного модуля. В главном модуле есть пара переменных, которые должны
быть глобальными, то есть видеться из всех модулей. Еще во вспомогательном
модуле есть несколько макроопределений, которые надо видеть в главном модуле,
например, число разрядов в примененном индикаторе, его надо знать и программе,
обслуживающей индикаторы, и тому, кто на них что-то полезное выводит.
Хотелось бы
все это описать один раз и в одном месте, например, в заголовочном файле,
который включался бы воба исходника
и объявлял в них нужные переменные и имена, а если надо что-то изменить, это
делать в одном месте и не лезть в исходники. Только
вот как это грамотно сделать? Если просто тупо включить хедер и туда и туда -
получается ругань при компановке на повторное определние. Можно в принципе в начале вспомогательного мдуля отдефайнить какое-нибудь
уникальное значение, а в хедере устроить условную компиляцию, в которой, если
определено (#ifdef) - то описывать по-
нормальному, а если нет - то как extern. Но это не очень красиво, так как определения
повторяются 2 раза. А вот как это делается правильно?
Обычно это делается так:
// module.h
#ifndef _MODULE_H_ /*если_имя_макроса_не_определенно имя_макроса последовательность_операторов_1*/
#define _MODULE_H_ /*#define заменяемая_последовательность фрагмент_подстановки
*/
extern char a; /* повторное объявление переменной a */
extern int b; /*
повторное объявление переменной b */
void
do_something(void); /*функция «делать_кое-что» */
#endif /*_MODULE_H_ */
---------------------------
// module.c
#include "module.h" /* Подключаем модуль module.h */
char a; /*
объявление переменной a */
int b; /*
объявление переменной b */
void
do_something(void) /* функция «делать_кое-что» */
{
b = a;
}
---------------------------
//
main.c
#include
<io.h>
#include
"module.h"
void main(void)
{
a=2;
do_something(); // Вызов функции «делать_кое-что»
}
---------------------------
То есть придется написать дважды про
глобальные переменные : в хедере описать экстерном, в сишнике
объявить. И не надо никаких ветвлений по #ifdef. :)