Робота з регістрами avr мікроконтролера на сі, бітові операції

Показано принципи роботи з окремими бітами регістра порту в AVR мікроконтролері. Детально розглянуті бітові операції і операції зсуву бітів в мові Сі. Наведені приклади установки і скидання бітів в регістрі порту, читання стану бітів і їх інверсія.

Для запису і читання окремих бітів в портах мікроконтролера необхідно навчитися виробляти бітові операції, використовувати бітові маски і записувати відео у порт.

структура байта

Ми знаємо що один байт представляє собою 8 біт, а кожен біт це - 1 або 0, біти в байті вважаються справа наліво. Біт 1 є молодшим, а біт 8 - старшим.

У файлі констант і визначень в бібліотеці avr-libc для кожного типу мікроконтролера вказані значення для констант PD0, PD1, PB5, PC4, і інші. Наприклад для виведення значень всіх констант з IO-файлу для мікроконтролера ATmega8 і де зустрічається поєднання "PD" (шукаємо константи для порту D), досить виконати команду:

Отримаємо ось такий результат:

Тепер при використанні константи PD0 ви знаєте що в ній міститься число 0, а в PD1 - 1 і т.д.

Операції бітового зсуву

А зараз давайте більш детально за прикладами розберемося з операторами бітового зсуву.

Всього існує кілька різновидів операцій бітового зсуву, наприклад:

  • логічний (зсунуті біти втрачаються, а з іншого боку вільні позиції заповнюються нулями);
  • арифметичний (зрушення вліво - так само як і при логічному, зрушення вправо - заповнюються значеннями крайнього лівого біта, який ще називають знаковим);
  • циклічний (зсунуті біти з одного боку переміщаються на вакантні позиції з іншого, як замкнутий кільце).

Оператори бітового зсуву ">>" і "<<" в языке программирования Си выполняют сдвиг битов в переменной вправо и влево на указанное число элементов. Биты которые были сдвинуты теряются, а с другой стороны появляются нули - выполняется логический сдвиг.

Важливий нюанс: при зсуві вправо ( ">>") числа в змінної з негативним знаком (signed) виконується арифметичний зрушення - вакантні позиції зліва заповнюються одиницями (перенесення знака). Це важливо пам'ятати!

Для прикладу виконаємо зрушення бітів в різних числах, попередньо представивши їх в двійковому вигляді.

Для числа 1 (Dec, в десятковій системі 1) - 00000001 (Bin, в двійковій системі 0b00000001):

Зрушення вліво на один розряд виконує множення числа на 2, а зрушення вправо - розподіл числа на 2.

Для числа 209 (Dec, в десятковій системі 209) - 11010001 (Bin, в двійковій системі 0b11010001):

Для перерахунку з однієї системи числення в іншу в Linux зручно використовувати калькулятор. про це я вже раніше писав.

Бітові оператори в мові Сі

Те як рухати біти в байті ми тепер знаємо, далі розберемося з бітовими операторами в Сі:

  • " "(Логічне І, AND) або множення - бінарна операція, результат якої дорівнює 1 тільки в тому випадку якщо обидва операнда рівні 1, в іншому випадку будемо мати 0;
  • "|" (Логічне АБО, OR) або складання - бінарна операція, результат якої дорівнює 1 в тому випадку якщо хоча б один з операндів дорівнює 1;
  • "
"(Логічне НЕ) або інверсія - унарна операція, результат якої дорівнює 0 якщо операнд дорівнює 1, і навпаки - результат дорівнює 1, якщо операнд дорівнює 0;
  • "^" (Виключає АБО, XOR) - бінарна операція, результат якої дорівнює 1 в тому випадку якщо тільки один з двох операндів дорівнює 1.
  • Розглянемо приклади бітових операцій над числами 209, 7 і їх бітовими уявленнями:

    Як бачите, бітові операції дозволяють встановити або скинути окремі біти числа.

    Установка бітів в регістрі порту

    А тепер трохи практики, давайте зробимо установку 6-го біта в регістрі порту PORTB що в свою чергу встановить високий рівень для каналу PB5 (6-й біт в регістрі). Припустимо що зараз в регістрі PORTB міститься число 136. яке в бітовому поданні виглядає як 10001000 (високий рівень на каналах PB7 і PB3).

    Щоб встановити 6-й біт (100 01000) ми будемо використовувати бітову операцію логічного АБО в комплексі з бітової маскою. Для отримання бітової маски. за допомогою якої пізніше буде встановлено один біт, ми виконаємо лівобічний зсув бітів числа 1 (00000001) на 5 розрядів:

    В результаті бітової операції отримаємо число 32 (00100000), двійка в 5-го ступеня, кожен зрушення розряду примножував результат на 2.

    Чи залишиться виконати бітову операцію АБО над поточним числом в регістрі і вийшов числом-маскою:

    А тепер порівняйте стан регістра перед операцією і після - їхні капітали бітів збережені і додатково встановлено 6-й біт.

    Для установки 6-го біта і подальшого запису числа в регістр порту PORTB (установка високого рівня для каналу PB5) в нашому прикладі можна використовувати будь-яку з наступних конструкцій операторів, вони все виконують ідентичну завдання:

    • PORTB = PORTB | 32;
    • PORTB = PORTB | (1 <<5);
    • PORTB = PORTB | (1 <
    • PORTB | = (1 <

    Найбільш зручно використовувати останню коротку запис, де використовується комбінування операція логічного АБО і присвоєння, в даному випадку PB5. Наприклад константа PB5 (канал 5 порту B, 6-й біт регістра) визначена у файлі /usr/lib/avr/include/avr/iom8.h для мікроконтролера ATmega8 і вона дорівнює числу 5.

    Як встановити кілька біт в регістрі? - можна викликати по черзі дві конструкції з операторами, а можна все виконати однією командою. Припустимо потрібно встановити 2-й і 6-й біти в регістрі порту PORTD, що відповідають каналам PD1 і PD5:

    Скидання бітів в регістрі порту

    Для скидання розрядів в регістрі порту ми будемо використовувати бітову операцію "" (логічне "І"), яка застосовується до двох бітів (бінарна операція) і дає одиницю тільки в тому випадку якщо обидва вихідних біти мають одиничне значення, також нам знадобиться бітова операція "

    "(Логічне" НЕ ", інверсія).

    Давайте виконаємо скидання 5-го біта в регістрі порту PORTD. що в свою чергу виконає установку низького рівня на каналі PD4. Припустимо що зараз в регістрі PORTD міститься число 157. яке в бітовому поданні виглядає як 10011101.

    Для того щоб скинути 5-й біт (1001 1101) в регістрі порту PORTD ми підготуємо маску (як при установці бітів), зробимо її інверсію "

    "І виконаємо бітову операцію" "над поточним значенням регістра і отриманої інвертованою маскою.

    Для підготовки маски виконаємо зрушення бітів на 4 розрядів в числі 1 (00000001).

    Маска готова, отримали число 16 (00010000), 2 в 4-го ступеня. Виконаємо інверсію бітів:

    Готово, залишилося застосувати маску до вмісту регістра порту PORTB використовуючи бітову операцію "":

    Тепер у вмісті регістру PORTD значення 5-го біта встановлено в 0 зі збереженням положень інших біт. У мові Сі дані операції можна виконати використовуючи будь-яку з наведених нижче ідентичних по результату команд:

    • PORTD = PORTD
    16;
  • PORTD = PORTD 239;
  • PORTD = PORTD (1 <<4 );
  • PORTD = PORTD (1 <
  • PORTD =

    В даному випадку найбільш зручною та інформативною формою команди буде останній укорочений варіант.

    Для одночасного скидання декількох бітів регістра можна використовувати ось такі конструкції з операторів:

    • PORTD = PORTD
    ((1 <
  • PORTD =

    Перевірка розрядів регістра

    Тепер розберемося яким чином можна перевірити розряди регістра на наявність в них 1 або 0. Це може знадобитися якщо потрібно отримати значення бітів в регістрах спеціального призначення (прапорів) мікропроцесора, а також для читання стану різних пристроїв і модулів, які передають свої статки використовуючи бітову структуру .

    Як перевірити значення встановленого біта в регістрі на Сі? - для цього потрібно підібрати спеціальне вираз з використанням бітових операцій, результатом роботи якого буде значення: правда (True) або брехня (False). Маючи булево (bool) значення виразу ми можемо використовувати для роботи умовні оператори мови Сі.

    Наприклад нам потрібно перевірити чи є одиниця (1) в 3-м бите регістру PORTD. тим самим ми перевіримо чи є високий рівень на каналі PD2 порту PORTD. Приймемо що поточне значення регістра - 10010101 (149).

    Для перевірки використовуємо вираз, яке складається з бітової маски з встановленим бітом для перевірки, і перевіряється регістра, до яких застосовано бітовий оператор "" (логічне І).

    Готовим маску, в якій тільки 3-й біт встановлений в 0. Для цього виконаємо зрушення числа 1 на 2 розряду:

    Тепер застосуємо бітову операцію "" (логічне І) до вмісту регістра PORTD і вийшла масці:

    В результаті вираження отримаємо число 4 (0000 0100).

    У мові Сі все числа які НЕ рівні "нулю" (-100, -5, 1, 500) є логічною істиною (True), а 0 - логічної брехнею (False).

    Тому, результат нашого вираження - число 4 є логічною істиною (True), а це означає що 3-й розряд регістра PORTD містить одиницю. Ось як буде виглядати цей вислів на мові Сі:

    Такий вираз можна використовувати в умовних операторах (if) і операторах циклів (while), наприклад:

    Для перевірки вмісту біта в регістрі на нуль (0) використовуємо таку ж конструкцію, тільки до результату виразу застосуємо логічну операцію інверсії "!" (Логічне НЕ). Логічна операція "!" перевертає логічне значення з правди (True) на брехню (False), і навпаки. На відміну від бітової операції інверсії. яка перевертає біти з 1 на 0 і навпаки, логічна операція інверсії оперує з логічними значеннями: правда (True) на брехню (False).

    Приклад вираження для перевірки на нуль (0) 3-го біта (канал PD2) в регістрі порту PORTD:

    Тут ми вираз "PORTD (1 <<2)" берем в круглые дужки и таким образом получаем комплексный результат выражения, к которому потом применяем оператор логической инверсии.

    Інверсія стану біта в регістрі

    Іноді може знадобитися змінити стан певного бита в регістрі на протилежне - виконати інверсію стану біта.

    Для подібної операції відмінно підходить бітовий оператор "^" (виключає АБО, XOR). Щоб виконати інверсію певного біта в регістрі потрібно створити маску, в якій цей біт встановлений, а потім застосувати до вмісту регістра і отриманої масці бінарний оператор "^", потім залишиться записати отриманий результат в регістр і готово.

    Візьмемо, наприклад, що потрібно погасити світлодіод, який підключений до каналу PD5 порту PORTD. Якщо світлодіод світиться то це значить що в на каналі PD5 присутній високий рівень, відповідно це значить що в регістрі порту PORTD біт під номером 6 (PD5 = 5, 6-й біт в байті регістра) встановлено в 1. Припустимо що вміст регістра порту PORTD зараз - 10111010 (число 186, 1 байт, 8 розрядів, 6-й розряд = 1).

    Підготуємо маску, для установки 6-го біта нам необхідно зрушити все біти числа 1 на 5 розрядів:

    Застосуємо маску до вмісту регістра порту PORTD:

    Як бачите, 6-й біт в байті регістра, який раніше був 1, зараз встановлено в 0 (100 1 1010). Тепер залишилося записати число в регістр порту і завдання можна вважати виконаною. Приклади використання такої конструкції на мові Сі:

    • PORTD = PORTD ^ 32;
    • PORTD = PORTD ^ (1<<5);
    • PORTD = PORTD ^ (1<
    • PORTD ^ = (1<

    Як і в попередніх прикладах з установки і скидання бітів, остання коротка конструкція є найбільш простою і зрозумілою для використання.

    висновок

    З першого погляду дуже просто заплутатися в операторах і значеннях таких як "", "!", "PD1", ">>" і інших, але один раз добре розібравшись і спробувавши на практиці ви завжди будете мати поняття що і як працює, звідки береться і що містить.

    Логічні та бітові операції присутні в багатьох мовах програмування, не тільки в Сі, так що даний досвід буде вам корисний якщо ви також будете працювати і з іншими мовами програмування.