Директиви умовної компіляції
Директиви умовної компіляції
Багато мікроконтролерів відрізняються лише деякими параметрами, кількістю висновків, розміром пам'яті та розміщенням регістрів. Це дозволяє створювати мовою C програмний код всього модельного ряду. Однак для цього слід якимось чином замінити параметри, які відрізняються у різних моделей. З цією метою використовуються директиви умовної компіляції.
Синтаксис для директиви #ifdef:
# ifdef ім'я_макросу послідовність_операторів_1
Якщо ім'я макросу визначеноу програмі, то компілюється перша послідовність операторів, в іншому випадку - друга послідовність (гілка #else може і відсутня).
Приклад використання (для компілятора CCS - PICC):
Синтаксис для директиви #ifndef:
# ifndef ім'я_макросу послідовність_операторів_1
В даному випадку, на відміну від директиви # ifdef перша послідовність операторів виконується в тому випадку, якщо ім'я макросу не визначено в програмі.
Для умовної компіляції можна скористатися директивами #if, #elif.
# if выражение1 послідовність_операторів_1
# elif выражение2 послідовність_операторів_2
Ця конструкція працює аналогічно умовному оператору if-else. Компілятор оцінює вирази після # if і # elif до тих пір, поки одне з них не дасть в результаті TRUE , після чого текст програми підставляється відповідна послідовність операторів. Якщо обидва вирази дають false, то підставляється послідовність операторів після директиви #else (якщо вона присутня).
Допустимо, для передачі та прийому даних через UART у абстрактного мікроконтролераSomeMicl 6 використовуються висновки 12 і 13, у мікроконтролера SomeMic 8 - висновки 6 і 14, а у SomeMic 4 - висновки 1 і 2. Тоді ми можемо створити заголовний файл SomeMic. h та включити до нього наступні директиви.
#if SomeMicX == 16
#elif SomeMicX == 8
#elif SomeMicX == 4
#error "Pins TXD і RXD для SomeMicX не визначено"
Тепер, якщо програмний проект буде побудований на мікроконтролері SomeMic 8, то заголовний файл слід помістити наступний текст.
#define SomeMicX = 8
Зустрівши директиву #ifndef, препроцесор включить до тексту програми #define SomeMicX = 8 і #include . Оскільки після цього елемент SomeMicX отримає значення 8, то будь-яка повторна обробка директиви # ifndef не призведе до дублювання у вихідному тексті відповідної інформації. Іншими словами, вміст заголовного файлу SomeMic. h буде поміщено у вихідний код файлів, які використовують заголовковий файл з # ifndef лише один раз.
Директива # error використовується разом із директивами умовної компіляції. Зустрівши її, компілятор згенерує повідомлення про помилку, вказане праворуч директиви (див. приклад у попередньому підрозділі).
Програма складається з основного модуля та допоміжного модуля зі службовими функціями (зараз там функції обслуговування LED і KBD ).
У допоміжному модулі описано ряд змінних процедур, які повинні бути видні з головного модуля. У головному модулі є пара змінних, які мають бути глобальними, тобто бачитися з усіх модулів. Ще у допоміжному модулі є кілька макровизначень, які треба бачити в головному модулі, наприклад, кількість розрядів у застосованому індикаторі, його треба знати і програмі, яка обслуговує індикатори, і тому, хто на них щоськорисне виводить.
Хотілося б все це описати один раз і в одному місці, наприклад, у заголовному файлі, який включався б воба вихідного коду і оголошував у них потрібні змінні та імена, а якщо треба щось змінити, це робити в одному місці і не лізти в вихідні . Тільки як це грамотно зробити? Якщо просто тупо включити хедер і туди і туди - виходить лайка при компануванні на повторне визначення. Можна в принципі на початку допоміжного мдуля віддефайнувати якесь унікальне значення, а в хедері влаштувати умовну компіляцію, в якій, якщо визначено (# ifdef) - то описувати по-
нормальному, і якщо ні - як extern . Але це дуже красиво, оскільки визначення повторюються 2 разу. А як це робиться правильно?
Зазвичай це робиться так:
# ifndef _ MODULE _ H _ /* якщо_ім'я_макросу_не_визначено ім'я_макросу послідовність_операторів_1*/
# define _ MODULE _ H _ /*# define замінна_послідовність фрагмент_підстановки */
void do_something(void); /* функція « робити _ дещо - що » */
#include "module.h" /* Підключаємо модуль module.h */
void do_something(void) /* функція « робити _ дещо - що » */
do _ something (); // Виклик функції «робити _ дещо»
Тобто доведеться написати двічі про глобальні змінні: у хедері описати екстерном, у сишнику оголосити. І не треба ніяких розгалужень по #ifdef. :)