Директиви умовної компіляції

Директиви умовної компіляції

Багато мікроконтролерів відрізняються лише деякими параметрами, кількістю висновків, розміром пам'яті та розміщенням регістрів. Це дозволяє створювати мовою 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. :)