В цій статті розглядаються деякі ключові концепції програмування мовою C для мікроконтролерів AVR.
- Регістри AVR
- Біти та байти
- Бітові операції
- Очищення та встановлення бітів
- Макрос керування значенням біту _BV()
Регістри AVR
Регістри — це спеціальний простір для збереження даних, в якому кожен окремий біт може мати своє, особливе призначення. Більшість регістрів 8-розрядні (але є декілька винятків). Кожен регістр та кожен біт регістру мають власні імена. Всі імена регістрів та бітів описані в документації до мікроконтролера, яку можна знайти на сайті Atmel. Також, в AVR Libc визначені ідентифікатори для імен регістрів та бітів, тому вони можуть бути легко використані в коді на C. Операції з бітами в регістрах AVR — фундамент програмування для мікроконтролерів AVR. Все, від конфігурації вбудованих периферійних пристроїв, закінчуючи використанням контактів мікроконтролера для цифрового вводу/виводу, здійснюється за допомогою операцій з бітами регістрів.
Біти та Байти
Біт може приймати одне з двох можливих значень: 1 або 0 (аналогічно: увімк/вимк, встановлено/очищено, високий/низький логічний рівень). Декілька бітів утворюють число в двійковому вигляді, де кожен біт є одним розрядом даного числа. В мікроконтролерах AVR 8 бітів з'єднані разом, формують один байт, в якому молодший біт (LSB — Least Significant Bit) знаходиться справа. Нумерація бітів починається з нуля від молодшого біта. Для прикладу розглянемо десяткове число 15. Його можна відобразити у двійковій 8-бітній формі:
00001111
Чотири молодші біти встановлені.
Якщо є потреба більш докладно зупинитися на темі перетворень між десятковими, двійковими та шістнадцятковими числами, то можна пошукати статті до цієї теми, або скористатись цією таблицею. Інший приклад, десяткове число 40, відображене у двійковій формі:
00101000
Біти 3 та 5 встановленні.
У програмах мовою C для мікроконтролерів AVR числові значення можуть виражатися у трьох формах, залежно від контексту або вибору програміста. Шістнадцяткові числа визначаються з використанням 0x-префіксу, двійкові — 0b-префіксу. Наступний C код демонструє три варіанти ініціалізації змінних десятковим значенням 15.
uint8_t a = 15; // десяткове значення
uint8_t b = 0x0F; // шістнадцяткове
uint8_t c = 0b00001111; // двійкове
uint8_t
— один з типів даних рівноширокого цілого типу, зі стандарту C99. Стандарт передбачає 8-бітний беззнаковий цілий тип. Типи даних стандарту C99 будуть і надалі використовуватись у статті.
Бітові операції
Виходячи з того, що кожен окремий біт — носій важливої інформації при програмуванні для AVR мікроконтролерів, бітові операції є значною складовою цього процесу.
У результаті виконання побітової операції AND, вихідні біти буде встановлено лише у випадку, коли у обох операндах вони дорівнюють одиниці. Іншими словами, біт n буде встановлено, якщо у першому операнді та (AND) у другому операнді біт n також встановлено.
У мові програмування C побітовий оператор AND позначається одинарним знаком амперсанд.
uint8_t a = 0xAA; // 10101010
uint8_t b = 0x0F; // 00001111
uint8_t c = a & b; // 00001010
У результаті виконання побітової операції OR, вихідні біти буде встановлено у випадку, якщо хоча б в одному з операндів вони також були встановлені. Іншими словами, біт n буде встановлено, якщо у першому операнді або (OR) у другому операнді біт n також встановлено.
У мові програмування C для позначення побітової операції OR використовується одинарна вертикальна риска (|).
uint8_t a = 0xAA; // 10101010
uint8_t b = 0x0F; // 00001111
uint8_t c = a | b; // 10101111
У результаті виконання побітової операції XOR(виключне OR) вихідні біти буде встановлено тільки в тому разі, якщо в одному з операндів вони були встановлені, а в іншому ні. Іншими словами, біт n буде встановлено, якщо **виключно **в одному з операндів біт n також встановлено.
Оператор XOR у мові C позначається символом каретки (^).
uint8_t a = 0xAA; // 10101010
uint8_t b = 0x0F; // 00001111
uint8_t c = a ^ b; // 10100101
Операція NOT, відома як порозрядне доповнення, є унарною операцією. Це означає, що така операція потребує лише одного операнда, а не двох, як інші. NOT просто перетворює кожен біт на протилежний. Тобто, кожен біт, що дорівнює 1, перетворюється на 0, а кожен, що дорівнює 0, перетворюється на 1.
У мові програмування C операція NOT позначається знаком тильда (~).
uint8_t a = 0xAA; // 10101010
uint8_t b = ~a; // 01010101
Операція зсуву (shift), переміщує всі біти вліво або вправо. При зсуві вліво, біти зсуваються "назовні" зліва, та нульові біти зсуваються "всередину" справа.
В мові програмування C два знаки "менше ніж" () позначають операцію зсуву вправо. З правої сторони від оператора вказується числове значення — кількість розрядів для зсуву.
uint8_t a = 0x99; // 10011001
uint8_t b = a<<1; // 00110010
uint8_t c = a>>3; // 00010011
Очищення та встановлення бітів
Встановлення та очищення окремого біту, без зміни всіх інших бітів, одна з найважливіших задач при програмуванні мікроконтролерів AVR. Ви будете користуватись цією схемою знов і знов.
Отже, загалом для керування окремо взятим бітом, зазвичай, потрібен байт в якому нас цікавить лише один встановлений біт. Цей байт надалі, за допомогою побітових операцій, можна використовувати для керування потрібним бітом. Такий принцип керування бітами називається бітова маска. Для прикладу, розглянемо бітову маску для біта номер 2: 00000100
, та бітову маску для біту номер 6: 01000000
.
Якщо взяти число 1, то маємо двійкове число в якому встановлено лише нульовий розряд, але за допомогою зсуву вліво на деяку кількість розрядів, можна отримати потрібну маску. Для прикладу наведена бітова маска для біту номер 2, яку отримали з числа 1 за допомогою зсуву вліво на два розряди.
Для встановлення потрібного біту у мові C, застосовується операція OR до потрібного байту разом з бітовою маскою.
uint8_t a = 0x08; // 00001000
// встановлення біту 2
a |= (1<<2); // 00001100
Для встановлення більше ніж одного біту використовується декілька операторів OR.
uint8_t a = 0x08; // 00001000
// встановлення бітів 1 та 2
a |= (1<<2)|(1<<1); // 00001110
Для очищення біту застосовується операція NOT до бітової маски, у результаті тільки потрібний біт буде не встановлено, після цього потрібно застосувати операцію AND до потрібного байту разом з бітовою маскою.
uint8_t a = 0x0F; // 00001111
// очищення біту 2
a &= ~(1<<2); // 00001011
Так само — для очищення більше ніж одного біту використовується декілька операторів OR.
uint8_t a = 0x0F; // 00001111
// очищення бітів 1 та 2
a &= ~((1<<2)|(1<<1)); // 00001001
Для того, щоб, так би мовити, перемикати потрібний біт, встановлюючи його, або в 1, або в 0, можна скористатись операцією XOR та, знов ж таки, — бітовою маскою.
uint8_t a = 0x0F; // 00001111
// змінення значення біту 2
a ^= (1<<2); // 00001011
a ^= (1<<2); // 00001111
Макрос керування значенням біту _BV()
В AVR Libc визначено макрос _BV()
для керування значенням біту. Цей макрос зручно використовувати для отримання потрібної бітової маски. Головна ідея в тому, щоб зробити код більш читабельним використовуючи побітовий зсув вліво. Застосування _BV(n)
еквівалентно вживанню (1<<n
).
// встановлення біту 0, використовуючи _BV()
a |= _BV(0);
// встановлення біту 0, використовуючи зсув
a |= (1<<0);
Який метод використовувати — залежить від вас. Ви побачили обидва методи в дії і тепер зможете вибрати, що для вас зручніше. Також, треба зазначити, що оскільки макрос _BV()
є унікальним для GCC, то такий метод, на відміну від (1<<n
), не сумісний з іншими компіляторами. Але цим зазвичай не дуже переймаються аматори, та й нерідко _BV()
виявляється зручнішим для початківців.
Коментарі (2)