Основы · 16 мин чтения

Логические элементы: таблицы истинности AND, OR, NOT, XOR и Arduino

Что такое логические элементы? Обозначения AND, OR, NOT, NAND, NOR, XOR и XNOR, таблицы истинности, микросхемы 74HC08 и 74HC00, полусумматор и код для Arduino, ESP32 и Pico.

Логические элементы — базовые кирпичики цифровых схем: каждый смотрит на один или несколько цифровых входов и по фиксированному правилу выдаёт один цифровой выход. Входы могут быть только 0 (LOW) или 1 (HIGH), и выход тоже. Базовых элементов семь: AND, OR, NOT, NAND, NOR, XOR и XNOR (И, ИЛИ, НЕ, И-НЕ, ИЛИ-НЕ, исключающее ИЛИ и исключающее ИЛИ-НЕ). От процессора в твоём телефоне до платы управления стиральной машиной, от ATmega328P внутри Arduino до карманного калькулятора — любое цифровое устройство работает, соединяя миллионы крошечных логических элементов.

В этом руководстве ты узнаешь обозначение и таблицу истинности каждого элемента, настоящие микросхемы вроде 74HC08, 74HC32 и 74HC00, опыт на макетной плате с двумя кнопками, как собрать любой другой элемент только из NAND, и схему полусумматора, складывающую два бита. В последних разделах превратим ту же логику в код на Arduino с операторами &&, || и ! и побитовыми операциями, а затем продолжим примерами для ESP32 и Raspberry Pi Pico (MicroPython).

Макетная плата для опытов с логическими элементами
Микросхемы логических элементов можно опробовать без пайки, поставив их поперёк выемки посередине макетной платы.

Что такое логический элемент и как он работает?

В цифровой электронике каждый сигнал находится в одном из двух состояний: 1 (HIGH, истина) или 0 (LOW, ложь). Математика работы с этими двумя значениями называется булевой алгеброй; в книге, вышедшей в 1854 году, Джордж Буль именно так выразил логические рассуждения в виде алгебры. В 1937 году Клод Шеннон показал, что эта алгебра один в один ложится на переключательные схемы из реле, заложив основу проектирования цифровых схем. Сегодня логический элемент содержит всего несколько транзисторов: в технологии CMOS элемент NOT состоит из 2 MOSFET, а NAND — из 4. Как транзистор работает ключом, объясняем в статье Что такое транзистор?

Так какое напряжение схема считает 1, а какое 0? Это зависит от напряжения питания платы и семейства микросхем. Таблица ниже показывает границы, при которых вход гарантированно считается 1 или 0. Область между двумя границами — неопределённая зона; если вход остаётся в ней, выход может вести себя случайно. Вход, ни к чему не подключённый, то есть висящий (floating), блуждает как раз в этой неопределённой зоне. Поэтому каждый вход нужно подключить к сигналу или через резистор к GND или VCC.

Плата / семействоПитаниеВход гарантированно 1Вход гарантированно 0
Arduino Uno (ATmega328P)5 V≥ 3,0 V≤ 1,5 V
ESP323,3 V≥ 2,5 V≤ 0,8 V
Raspberry Pi Pico (RP2040)3,3 V≥ 2,0 V≤ 0,8 V
Микросхема 74HC5 V≥ 3,5 V≤ 1,5 V
Микросхема 74HCT5 V≥ 2,0 V≤ 0,8 V
Микросхема CD40005 V≥ 3,5 V≤ 1,5 V

7 базовых логических элементов и их обозначения

На схемах элементы рисуют по двум разным стандартам. В американском стандарте (ANSI/IEEE) у каждого элемента своя форма: AND похож на букву D, OR — на заострённый щит, а NOT — на треугольник. Маленький кружок (bubble) на выходе означает «инвертировать»; NAND — это, по сути, AND с кружком на конце. От OR элемент XOR отличает вторая изогнутая линия со стороны входов. В европейском стандарте (IEC), принятом и в российских схемах, все элементы — прямоугольники, различающиеся знаком внутри: & для AND, ≥1 для OR, =1 для XOR и 1 для NOT. В учебниках встретятся и названия И-НЕ для NAND, ИЛИ-НЕ для NOR, исключающее ИЛИ для XOR и исключающее ИЛИ-НЕ для XNOR.

ЭлементДругое названиеОбозначение ANSIЗнак IECБулево выражениеКогда выход 1?
ANDКонъюнкция (И)Форма D&Y = A · BОба входа 1
ORДизъюнкция (ИЛИ)Заострённый щит≥1Y = A + BХотя бы один вход 1
NOTИнвертор (НЕ)Треугольник + кружок1 (кружок на выходе)Y = A'Вход 0
NANDИ-НЕФорма D + кружок& (кружок на выходе)Y = (A · B)'Хотя бы один вход 0
NORИЛИ-НЕЩит + кружок≥1 (кружок на выходе)Y = (A + B)'Оба входа 0
XORИсключающее ИЛИЩит с двойной дугой=1Y = A ⊕ BВходы различаются
XNORИсключающее ИЛИ-НЕДвойная дуга + кружок=1 (кружок на выходе)Y = (A ⊕ B)'Входы равны

Таблицы истинности логических элементов

Таблица истинности перечисляет выход элемента для каждой комбинации входов. У элемента с n входами 2ⁿ строк: 4 строки для двух входов, 8 для трёх и 16 для четырёх. Если записывать строки в порядке двоичного счёта (00, 01, 10, 11), ни одна комбинация не потеряется. Вот все шесть двухвходовых элементов в одной таблице:

ABANDORNANDNORXORXNOR
00001101
01011010
10011010
11110001

У элемента NOT один вход, поэтому в его таблице две строки: при A = 0 выход 1, при A = 1 выход 0. Присмотрись к таблице, и увидишь закономерность: столбец NAND в точности противоположен AND, NOR — OR, а XNOR — XOR. Чтобы запомнить, используй короткие правила: AND спрашивает «все ли равны 1?», OR — «есть ли хотя бы одна 1?», XOR — «различаются ли входы?»; элемент, чьё название начинается с N, даёт на тот же вопрос противоположный ответ. Чтобы самому попробовать элементы и увидеть обозначения вживую, просто нажимай на входы A и B в утилите «Логические элементы».

  • AND: два выключателя последовательно. Лампа горит, только когда замкнуты оба; банковское хранилище, открывающееся двумя разными ключами, тоже следует правилу AND.
  • OR: две кнопки параллельно. Один и тот же звонок звенит, нажмёт ли кто-то кнопку у парадной двери или у задней.
  • NOT: свет в холодильнике. Пока дверь закрыта, выключатель прижат, и лампа не горит; когда дверь открывается, выключатель отпускается, и лампа загорается.
  • NAND: правило «предупреждай, если не всё в порядке». Если открыт хотя бы один из двух защитных кожухов станка, загорается предупреждающая лампа.
  • NOR: правило «ничего не случилось». Зелёная лампа «всё в порядке», которая горит, только когда не обнаружены ни дым, ни газ.
  • XOR: проходные выключатели на лестнице. Переключение нижнего или верхнего выключателя меняет состояние лампы; лампа горит, когда выключатели в разных положениях, и не горит, когда в одинаковых.
  • XNOR: проверка на равенство. Выдаёт 1, когда два бита одинаковы; используется в цифровых компараторах и схемах проверки ошибок.

Микросхемы логических элементов: 74HC08, 74HC32, 74HC00

Вместо того чтобы собирать логические элементы из отдельных транзисторов, используют готовые микросхемы. Самое распространённое семейство в любительской электронике — серия 74HC: в 14-выводном корпусе DIP четыре двухвходовых элемента, питание всегда подаётся на вывод 14 (VCC), а земля — на вывод 7 (GND). Микросхемы 74HC работают от 2 до 6 V, их входы почти не потребляют ток, а при 5 V задержка одного элемента около 10 наносекунд — в сотни раз быстрее одного вызова digitalWrite на Arduino. Каждый выход держит гарантированные уровни напряжения при нагрузке 4 mA; абсолютный предел — 25 mA на выход и 50 mA на всю микросхему.

МикросхемаЭлементСодержитРаспиновкаАналог CMOS 4000
74HC08AND4 × 2 входаСтандартная (таблица ниже)CD4081
74HC32OR4 × 2 входаСтандартнаяCD4071
74HC00NAND4 × 2 входаСтандартнаяCD4011
74HC86XOR4 × 2 входаСтандартнаяCD4070
74HC02NOR4 × 2 входаДругая: выходы на 1, 4, 10, 13CD4001
74HC04NOT6 × 1 входВходы на 1, 3, 5, 9, 11, 13CD4069
74HC14NOT (триггер Шмитта)6 × 1 входКак у 74HC04CD40106
CD4077 / 74HCS7266XNOR4 × 2 входаТрудно найти; обычно 74HC86 + 74HC04CD4077

У 74HC08, 74HC32, 74HC00 и 74HC86 в точности одинаковая распиновка; меняется только тип элементов внутри. Выемка или точка сверху на микросхеме отмечает вывод 1. Если смотреть на микросхему сверху выемкой влево, выводы нумеруются против часовой стрелки: нижний ряд идёт 1–7 слева направо, а верхний — 8–14 справа налево. A и B — входы, Y — выход, а цифра впереди — номер элемента.

ВыводНазначениеВыводНазначение
11A (вход элемента 1)14VCC (+2…6 V)
21B (вход элемента 1)134B (вход элемента 4)
31Y (выход элемента 1)124A (вход элемента 4)
42A (вход элемента 2)114Y (выход элемента 4)
52B (вход элемента 2)103B (вход элемента 3)
62Y (выход элемента 2)93A (вход элемента 3)
7GND83Y (выход элемента 3)
Никогда не оставляй входы CMOS висящими. Соедини входы неиспользуемых элементов с GND (или VCC): висящий вход постоянно переключается от помех, увеличивает ток потребления микросхемы и даже может мешать соседним элементам. Неиспользуемые выходы, наоборот, можно оставить свободными. Не забудь керамический конденсатор 100 nF между VCC и GND как можно ближе к микросхеме.

Микросхемы, выполняющие одну и ту же работу, бывают разных семейств. Серия 74LS — более старая технология TTL: она работает только от 4,75–5,25 V, потребляет больше тока и обычно читает висящий вход как 1. Серия 74HCT — это 74HC с входами, совместимыми с TTL; она питается от 5 V, но гарантированно принимает за 1 всё от 2 V, поэтому без проблем читает сигналы 3,3 V от ESP32 или Pico. Серия CD4000 (CD4081, CD4011 и так далее) работает в широком диапазоне 3–18 V, но медленнее, а её распиновка отличается от 74HC: CD4011 нельзя просто поставить вместо 74HC00.

Опыт на макетной плате с кнопками: элемент AND на 74HC08

Лучший способ понять логический элемент — самому попробовать его с двумя кнопками. В этом опыте нет микроконтроллера; нужны только 74HC08, две кнопки, два резистора 10 kΩ, резистор 1 kΩ, светодиод и конденсатор 100 nF. Для питания подойдут пины 5V и GND у Arduino, модуль питания макетной платы или три батарейки AA (4,5 V). Если непонятно, как макетная плата соединена внутри, сначала прочитай Что такое макетная плата?

Кнопка для опыта на макетной плате
Кнопка, пока нажата, соединяет две свои стороны. У моделей с четырьмя ножками ножки внутри попарно соединены, поэтому надёжнее всего ставить кнопку поперёк средней выемки.
  1. Поставь 74HC08 поперёк выемки посередине макетной платы, чтобы выводы оказались по обе стороны, выемкой влево.
  2. Подключи вывод 14 к шине + (5 V), а вывод 7 — к шине − (GND) и поставь между этими выводами конденсатор 100 nF.
  3. Одну сторону кнопки A подключи к шине +, а другую — к выводу 1. С того же вывода добавь резистор 10 kΩ на GND: этот стягивающий (pull-down) резистор надёжно держит вход в 0, пока кнопка не нажата.
  4. Так же подключи кнопку B к выводу 2 и дай ей свой стягивающий резистор 10 kΩ.
  5. От вывода 3, выхода, проведи через резистор 1 kΩ к длинной ножке (аноду) светодиода; короткую ножку (катод) подключи к GND.
  6. Неиспользуемые входы, выводы 4, 5, 9, 10, 12 и 13, соедини с GND; выходы 6, 8 и 11 оставь свободными.
  7. Подай питание и по очереди попробуй четыре комбинации: светодиод должен гореть, только когда обе кнопки нажаты вместе. Сравни результаты с таблицей истинности.
ПодключениеВывод 74HC08Куда подключать
Питание14 (VCC)Шина +, 5 V
Земля7 (GND)Шина −
Развязывающий конденсаторМежду 14 и 7Керамический 100 nF, рядом с микросхемой
Вход A1Кнопка A → 5 V, 10 kΩ → GND
Вход B2Кнопка B → 5 V, 10 kΩ → GND
Выход31 kΩ → анод светодиода, катод светодиода → GND
Неиспользуемые входы4, 5, 9, 10, 12, 13GND
Неиспользуемые выходы6, 8, 11Свободны
Когда схема заработала, начинается самое интересное: вытащи 74HC08 и вставь 74HC32 с той же распиновкой — светодиод загорается при нажатии любой кнопки (OR). С 74HC00 он горит, пока не нажмёшь обе кнопки вместе (NAND), а с 74HC86 горит, только когда нажата одна кнопка (XOR). С резистором 1 kΩ красный светодиод потребляет около 3 mA — безопасное значение для выхода 74HC. Для светодиода другого цвета резистор можно рассчитать утилитой «Резистор для светодиода».

Любой элемент из NAND: универсальный элемент

NAND называют универсальным элементом, потому что из одних только элементов NAND можно собрать любой другой. У этого есть и практическая сторона: в технологии CMOS NAND — один из самых простых элементов, ему нужно всего 4 транзистора, а AND — это на самом деле NAND с NOT после него, и ему нужно 6. Поэтому разработчики микросхем так любят NAND. Флеш-память NAND в USB-флешках тоже названа так из-за того, что её ячейки соединены последовательно, как транзисторы в элементе NAND. NOR тоже универсален: бортовой компьютер «Аполлона» (Apollo Guidance Computer), летавшего на Луну, был собран примерно из 2800 микросхем, в каждой из которых два трёхвходовых элемента NOR.

Какой элемент собратьКак из NAND?Нужно NAND
NOTA NAND A (соедини оба входа вместе)1
ANDПропусти выход (A NAND B) через NOT2
OR(A NAND A) NAND (B NAND B)3
NOROR + NOT4
XORN1 = A NAND B; (A NAND N1) NAND (B NAND N1)4
XNORXOR + NOT5

Строка OR в таблице опирается на закон де Моргана: (A' · B')' = A + B. Иначе говоря, если сначала инвертировать оба входа, а потом подать их на NAND, получится OR. Второй закон де Моргана: (A + B)' = A' · B'. Оба закона помогают упрощать условия и в коде: на Arduino !(a && b) всегда даёт тот же результат, что (!a || !b), а !(a || b) — то же, что (!a && !b).

Сборка XOR из четырёх элементов NAND — классическое экзаменационное и лабораторное упражнение, и четырёх элементов одной 74HC00 ровно хватает. Сначала вычисляется N1 = A NAND B. Затем находятся N2 = A NAND N1 и N3 = B NAND N1, а выход последнего элемента, Y = N2 NAND N3, даёт XOR. С подключением ниже опыт на макетной плате превращается в XOR на четырёх NAND:

Вывод 74HC00ПодключениеРоль
1 и 4Кнопка AВход A (элементы 1 и 2)
2 и 10Кнопка BВход B (элементы 1 и 3)
3 → 5 и 9ПеремычкаN1 = A NAND B
6 → 12ПеремычкаN2 = A NAND N1
8 → 13ПеремычкаN3 = B NAND N1
111 kΩ + светодиодY = A XOR B
14 / 75 V / GNDПитание

Пойдём на шаг дальше: если перекрёстно соединить выходы двух элементов NAND со входами друг друга, получится 1-битная память — RS-защёлка (SR latch). Эта схема помнит последнее состояние даже после того, как отпустишь кнопку. Регистры в процессорах, счётчики и микросхемы вроде сдвигового регистра 74HC595 сделаны из таких ячеек памяти на элементах, соединённых цепочкой.

Полусумматор: складываем два бита элементами

Самое впечатляющее, что умеют логические элементы, — складывать числа. В двоичной системе 0 + 0 = 0, 0 + 1 = 1, 1 + 0 = 1 и 1 + 1 = 10 (2 в десятичной). Правая цифра результата называется суммой, а цифра, переходящая влево, — переносом. Из таблицы ниже видно, что сумма — это в точности XOR, а перенос — в точности AND. Значит, элементы XOR и AND рядом образуют полусумматор, складывающий два однобитных числа.

ABПеренос (C = A AND B)Сумма (S = A XOR B)Десятичный результат
00000
01011
10011
11102 (двоичное 10)

Чтобы собрать его на макетной плате, подключи кнопки A и B к выводам 1 и 2 и 74HC86, и 74HC08. Вывод 3 микросхемы 74HC86 управляет светодиодом суммы, а вывод 3 микросхемы 74HC08 — светодиодом переноса. Когда нажимаешь обе кнопки, светодиод суммы гаснет, а светодиод переноса загорается: светодиоды показывают двоичное 10, то есть 2. Если хочешь обойтись только 74HC00, нужны четыре NAND для XOR и пятый NAND, инвертирующий N1, чтобы получить перенос; иначе говоря, полусумматору нужно 5 элементов NAND.

Полусумматор не может учесть перенос, пришедший из младшего разряда; для этого нужен полный сумматор. Его формулы: S = A ⊕ B ⊕ Cin и Cout = (A · B) + (Cin · (A ⊕ B)), то есть два полусумматора плюс элемент OR. Соедини четыре полных сумматора так, чтобы каждый передавал перенос следующему, и получится 4-битный сумматор; готовая микросхема 74HC283 делает то же 4-битное сложение, но вычисляет переносы быстрой внутренней схемой (ускоренный перенос, carry look-ahead), а не ждёт, пока они пробегут по цепочке. Арифметический блок процессора в твоём компьютере — 64-битная и гораздо более быстрая версия той же идеи.

Логические элементы на Arduino: &&, ||, ! и побитовые операции

В коде Arduino у каждого логического элемента два аналога. Логические операторы (&&, ||, !) объединяют условия и дают результат true или false; их используют в if и while. Побитовые операторы (&, |, ^, ~) применяются сразу ко всем битам числа, столбец за столбцом; работа с байтом — как восемь элементов одновременно. В примерах таблицы используются числа 0b1100 и 0b1010:

ЭлементЛогический (bool)ПобитовыйПримерРезультат
ANDa && bx & y0b1100 & 0b10100b1000
ORa || bx | y0b1100 | 0b10100b1110
NOT!a~x~0b00001100 (байт)0b11110011
NAND!(a && b)~(x & y)~(0b1100 & 0b1010) & 0x0F0b0111
NOR!(a || b)~(x | y)~(0b1100 | 0b1010) & 0x0F0b0001
XORa != bx ^ y0b1100 ^ 0b10100b0110
XNORa == b~(x ^ y)~(0b1100 ^ 0b1010) & 0x0F0b1001

Разница между двумя видами кажется маленькой, но порождает коварные ошибки. && и || вычисляются по короткой схеме: в a && b, если a ложно, b вообще не вычисляется. & всегда вычисляет обе стороны и смотрит на биты чисел: 2 && 1 — истина, а 2 & 1 — это 0, ведь у 0b10 и 0b01 нет общих битов. Отдельного логического оператора XOR в C++ нет; для двух bool понятнее всего написать a != b. Подробнее о побитовых операциях, сдвигах << и >> и двоичной системе — в статье Системы счисления и побитовые операции.

Теперь перенесём опыт с макетной платы на Arduino Uno. Две кнопки идут на пины 2 и 3, а семь светодиодов (каждый с резистором 330 Ω) — на пины 4–10. Одна сторона каждой кнопки идёт на пин, другая — на GND; поскольку включена встроенная подтяжка, дополнительный резистор не нужен. Один скетч оживляет сразу все семь элементов:

C++ (Arduino IDE)
1// Mantık kapısı simülatörü: 2 buton, 7 LED / Logic gate simulator: 2 buttons, 7 LEDs
2const byte PIN_A = 2;   // A butonu, diğer ucu GND / button A, other leg to GND
3const byte PIN_B = 3;   // B butonu, diğer ucu GND / button B, other leg to GND
4
5// Sıra: AND, OR, NOT A, NAND, NOR, XOR, XNOR / Order: AND, OR, NOT A, NAND, NOR, XOR, XNOR
6const byte LED_PINS[7] = {4, 5, 6, 7, 8, 9, 10};
7const char* const GATE_NAMES[7] = {"AND", "OR", "NOT A", "NAND", "NOR", "XOR", "XNOR"};
8
9byte lastInputs = 0xFF;   // ilk turda yazdırmayı zorlar / forces a print on the first pass
10
11void setup() {
12  pinMode(PIN_A, INPUT_PULLUP);
13  pinMode(PIN_B, INPUT_PULLUP);
14  for (byte i = 0; i < 7; i++) {
15    pinMode(LED_PINS[i], OUTPUT);
16  }
17  Serial.begin(9600);
18}
19
20void loop() {
21  // Basılı buton LOW okunur, ! ile 1 yapılır / A pressed button reads LOW, ! turns it into 1
22  bool a = !digitalRead(PIN_A);
23  bool b = !digitalRead(PIN_B);
24
25  bool out[7];
26  out[0] = a && b;      // AND
27  out[1] = a || b;      // OR
28  out[2] = !a;          // NOT A
29  out[3] = !(a && b);   // NAND
30  out[4] = !(a || b);   // NOR
31  out[5] = a != b;      // XOR: girişler farklı mı? / are the inputs different?
32  out[6] = a == b;      // XNOR: girişler aynı mı? / are the inputs equal?
33
34  for (byte i = 0; i < 7; i++) {
35    digitalWrite(LED_PINS[i], out[i] ? HIGH : LOW);
36  }
37
38  // Yalnız girişler değişince yaz / Print only when the inputs change
39  byte inputs = (a << 1) | b;
40  if (inputs != lastInputs) {
41    Serial.print("A=");
42    Serial.print(a);
43    Serial.print(" B=");
44    Serial.print(b);
45    for (byte i = 0; i < 7; i++) {
46      Serial.print("  ");
47      Serial.print(GATE_NAMES[i]);
48      Serial.print('=');
49      Serial.print(out[i]);
50    }
51    Serial.println();
52    lastInputs = inputs;
53  }
54  delay(20);   // basit sıçrama önleme / simple debounce
55}

С INPUT_PULLUP нажатая кнопка читается как LOW, поэтому код инвертирует показание через !, и нажатая кнопка становится 1. Каждый светодиод затем показывает результат своего элемента: 4 AND, 5 OR, 6 NOT A, 7 NAND, 8 NOR, 9 XOR и 10 XNOR. Если открыть монитор порта на скорости 9600 бод, при каждом изменении кнопки выводится вся строка; попробовав все четыре комбинации, ты своими руками получишь таблицу истинности. Подробности о подтяжке — в статье Как подключить кнопку к Arduino.

Чтобы увидеть силу побитовых операций, второй пример вычисляет всю таблицу истинности в несколько строк. Хитрость: если записать столбец A числом 0b1100, а столбец B — числом 0b1010, каждый бит соответствует одной строке таблицы, и один & сразу даёт все четыре строки столбца AND. Вторая половина скетча побитно складывает два 4-битных числа по формулам полного сумматора, программно имитируя схему сумматора процессора. Этому скетчу оборудование не нужно; просто открой монитор порта.

C++ (Arduino IDE)
1// Bit işlemleriyle doğruluk tablosu ve 4 bitlik toplayıcı / Truth tables with bitwise ops and a 4-bit adder
2// Her bit bir satırdır: bit 0 = (A=0, B=0) ... bit 3 = (A=1, B=1) / Each bit is one row: bit 0 = (A=0, B=0) ... bit 3 = (A=1, B=1)
3const byte COL_A = 0b1100;
4const byte COL_B = 0b1010;
5const byte MASK  = 0b1111;   // yalnız 4 satır / only 4 rows
6
7void printRow(const char* name, byte column) {
8  Serial.print(name);
9  Serial.print('\t');
10  for (byte row = 0; row < 4; row++) {
11    Serial.print(bitRead(column, row));   // satır satır yaz / print row by row
12    Serial.print("  ");
13  }
14  Serial.println();
15}
16
17// Tam toplayıcı: iki bit + gelen elde / Full adder: two bits + carry in
18void fullAdder(byte a, byte b, byte carryIn, byte &sum, byte &carryOut) {
19  sum = a ^ b ^ carryIn;                         // XOR'lar toplamı verir / XORs give the sum
20  carryOut = (a & b) | (carryIn & (a ^ b));      // AND + OR eldeyi verir / AND + OR give the carry
21}
22
23// 4 tam toplayıcı zincirlenir (ripple carry) / 4 full adders chained (ripple carry)
24byte add4(byte x, byte y, byte &carryOut) {
25  byte result = 0;
26  byte carry = 0;
27  for (byte i = 0; i < 4; i++) {
28    byte s, c;
29    fullAdder(bitRead(x, i), bitRead(y, i), carry, s, c);
30    bitWrite(result, i, s);
31    carry = c;
32  }
33  carryOut = carry;
34  return result;
35}
36
37void setup() {
38  Serial.begin(9600);
39  Serial.println(F("AB:\t00 01 10 11"));
40  printRow("AND",  COL_A & COL_B);
41  printRow("OR",   COL_A | COL_B);
42  printRow("NAND", ~(COL_A & COL_B) & MASK);   // ~ sonrası maske şart / mask is required after ~
43  printRow("NOR",  ~(COL_A | COL_B) & MASK);
44  printRow("XOR",  COL_A ^ COL_B);
45  printRow("XNOR", ~(COL_A ^ COL_B) & MASK);
46
47  byte carry;
48  byte s = add4(9, 12, carry);   // 1001 + 1100
49  Serial.print(F("9 + 12 = "));
50  Serial.print(s + carry * 16);  // elde 16'lar basamağıdır / the carry is the 16s place
51  Serial.print(F("  (toplam / sum = "));
52  Serial.print(s, BIN);
53  Serial.print(F(", elde / carry = "));
54  Serial.print(carry);
55  Serial.println(')');
56}
57
58void loop() {
59}
Не применяй ~ к значениям bool: ~ переворачивает все биты и превращает true (1) в -2, а поскольку -2 не ноль, это всё равно считается истиной. Для логического NOT всегда используй !. Ещё одна ловушка — приоритет операторов: в if (x & 1 == 0) сначала вычисляется 1 == 0, поэтому правильно писать if ((x & 1) == 0). Кроме того, ~ над переменной byte повышает результат до int, так что обрезай его маской вроде & 0x0F или & 0xFF, как сделано в коде.

Тестер микросхем 74HC на Arduino

Если маркировка на микросхеме стёрлась или хочется узнать, жива ли она, Arduino можно превратить в тестер. Этот скетч по очереди подаёт четыре комбинации входов на все четыре элемента 74HC00, 74HC08, 74HC32 или 74HC86 (микросхем со стандартной распиновкой), читает выходы и сравнивает результат с известными шаблонами. Подключи вывод 14 микросхемы к 5V, вывод 7 к GND, а остальные выводы — к пинам Arduino из таблицы в коде.

C++ (Arduino IDE)
1// 74HC00 / 08 / 32 / 86 tanıyıcı ve test cihazı / 74HC00 / 08 / 32 / 86 identifier and tester
2// Entegre: bacak 14 -> 5V, bacak 7 -> GND / Chip: pin 14 -> 5V, pin 7 -> GND
3// Her kapı: {A, B, Y} Arduino pinleri / Each gate: {A, B, Y} Arduino pins
4const byte GATES[4][3] = {
5  {2, 3, 4},      // kapı 1: entegre 1, 2 -> 3 / gate 1: chip 1, 2 -> 3
6  {5, 6, 7},      // kapı 2: entegre 4, 5 -> 6 / gate 2: chip 4, 5 -> 6
7  {8, 9, 10},     // kapı 3: entegre 9, 10 -> 8 / gate 3: chip 9, 10 -> 8
8  {11, 12, A0}    // kapı 4: entegre 12, 13 -> 11 / gate 4: chip 12, 13 -> 11
9};
10
11// Desenin bit sırası: bit 0 = (0,0), bit 3 = (1,1) / Pattern bit order: bit 0 = (0,0), bit 3 = (1,1)
12const char* nameOf(byte pattern) {
13  switch (pattern) {
14    case 0b1000: return "AND (74HC08)";
15    case 0b1110: return "OR (74HC32)";
16    case 0b0111: return "NAND (74HC00)";
17    case 0b0110: return "XOR (74HC86)";
18    default:     return "?";
19  }
20}
21
22byte testGate(byte g) {
23  byte pattern = 0;
24  for (byte row = 0; row < 4; row++) {
25    digitalWrite(GATES[g][0], bitRead(row, 1));   // A = satırın üst biti / A = high bit of the row
26    digitalWrite(GATES[g][1], bitRead(row, 0));   // B = satırın alt biti / B = low bit of the row
27    delayMicroseconds(10);                        // kapı ~10 ns gecikir, bol pay / the gate needs ~10 ns, plenty of margin
28    bitWrite(pattern, row, digitalRead(GATES[g][2]));
29  }
30  return pattern;
31}
32
33void setup() {
34  Serial.begin(9600);
35  for (byte g = 0; g < 4; g++) {
36    pinMode(GATES[g][0], OUTPUT);
37    pinMode(GATES[g][1], OUTPUT);
38    pinMode(GATES[g][2], INPUT);
39  }
40
41  byte first = testGate(0);
42  bool allSame = true;
43  for (byte g = 0; g < 4; g++) {
44    byte p = testGate(g);
45    Serial.print(F("Kapi / Gate "));
46    Serial.print(g + 1);
47    Serial.print(F(": "));
48    Serial.print(nameOf(p));
49    Serial.print(F("  desen / pattern 0b"));
50    Serial.println(p, BIN);
51    if (p != first) allSame = false;
52  }
53
54  if (allSame && nameOf(first)[0] != '?') {
55    Serial.println(F("Sonuc / Result: entegre saglam / chip OK"));
56  } else {
57    Serial.println(F("Sonuc / Result: bozuk kapi ya da baglanti hatasi / faulty gate or wiring error"));
58  }
59}
60
61void loop() {
62}
Если ошибиться в подключении, выход Arduino может оказаться в борьбе с выходом микросхемы. Чтобы этого избежать, между выходами микросхемы (3, 6, 8, 11) и пинами Arduino можно поставить резисторы 1 kΩ; результат теста не изменится, а обе стороны будут защищены при ошибке в подключении. У 74HC02 и 74HC04 другая распиновка, поэтому этот скетч не может проверить их напрямую; таблицу GATES нужно изменить по их даташитам.

Программируемый логический элемент на ESP32

Те же операторы без изменений работают на ESP32. В этом примере третья кнопка перебирает типы элементов, а один светодиод показывает выход выбранного элемента. Так одной схемой можно продемонстрировать шесть элементов на уроке или презентации. Кнопки идут на GPIO 25, 26 и 27 (другие их стороны — на GND), а светодиод — на GPIO 23 через резистор 220 Ω; название выбранного элемента появляется в мониторе порта на скорости 115200 бод.

C++ (Arduino IDE)
1// ESP32 programlanabilir mantık kapısı / ESP32 programmable logic gate
2const int PIN_A = 25;      // A butonu, diğer ucu GND / button A, other leg to GND
3const int PIN_B = 26;      // B butonu / button B
4const int PIN_MODE = 27;   // kapı seçme butonu / gate select button
5const int PIN_LED = 23;    // çıkış LED'i (220 ohm) / output LED (220 ohm)
6
7const char* const NAMES[] = {"AND", "OR", "NAND", "NOR", "XOR", "XNOR"};
8const int GATE_COUNT = 6;
9const unsigned long DEBOUNCE_MS = 30;   // sıçrama süresi / bounce time
10
11int gate = 0;
12int modeStable = HIGH;            // kararlı buton durumu / stable button state
13int modeRaw = HIGH;               // son ham okuma / last raw reading
14unsigned long modeChangedAt = 0;  // ham değerin son değiştiği an / when the raw value last changed
15
16bool evaluate(int g, bool a, bool b) {
17  switch (g) {
18    case 0:  return a && b;
19    case 1:  return a || b;
20    case 2:  return !(a && b);
21    case 3:  return !(a || b);
22    case 4:  return a != b;     // XOR
23    default: return a == b;     // XNOR
24  }
25}
26
27void setup() {
28  Serial.begin(115200);
29  pinMode(PIN_A, INPUT_PULLUP);
30  pinMode(PIN_B, INPUT_PULLUP);
31  pinMode(PIN_MODE, INPUT_PULLUP);
32  pinMode(PIN_LED, OUTPUT);
33  Serial.printf("Kapi / Gate: %s\n", NAMES[gate]);
34}
35
36void loop() {
37  int reading = digitalRead(PIN_MODE);
38  if (reading != modeRaw) {          // ham değer değişti, süreyi yeniden başlat / raw value changed, restart the timer
39    modeRaw = reading;
40    modeChangedAt = millis();
41  }
42  // 30 ms boyunca sabit kaldıysa gerçek değişimdir / steady for 30 ms means a real change
43  if (millis() - modeChangedAt > DEBOUNCE_MS && reading != modeStable) {
44    modeStable = reading;
45    if (modeStable == LOW) {         // yeni basış (bırakma sayılmaz) / new press (release is ignored)
46      gate = (gate + 1) % GATE_COUNT;
47      Serial.printf("Kapi / Gate: %s\n", NAMES[gate]);
48    }
49  }
50
51  bool a = !digitalRead(PIN_A);   // basılı = 1 / pressed = 1
52  bool b = !digitalRead(PIN_B);
53  digitalWrite(PIN_LED, evaluate(gate, a, b) ? HIGH : LOW);
54}
ESP32 и Pico работают на 3,3 V, и их пины не выдерживают 5 V. Если используешь с этими платами микросхему 74HC, питай её тоже от 3,3 V; 74HC работает уже от 2 V, так что это не проблема. Если микросхема питается от 5 V, 5 V на её выходе могут повредить вход платы; в обратную сторону сигнал 3,3 V не дотягивает до 3,5 V — порога гарантированной 1 у 74HC при питании 5 V. Если нужно общаться со схемой на 5 V, используй микросхему 74HCT или преобразователь логических уровней.

Логические элементы на Raspberry Pi Pico и MicroPython

В MicroPython логические операторы пишутся английскими словами: and, or, not. Побитовые операторы такие же, как в C++: &, |, ^ и ~. Код ниже читает две кнопки на GP14 и GP15 и показывает результаты AND, OR, NAND, NOR, XOR и XNOR на шести светодиодах на GP16–GP21. Другая сторона каждой кнопки идёт на GND, а каждый светодиод подключается через резистор 220–330 Ω. Если сохранить код в Thonny как main.py, он запускается при каждом включении Pico; если Pico для тебя в новинку, начни со статьи Что такое Raspberry Pi Pico?

MicroPython
1# Pico ile 6 mantık kapısı / 6 logic gates on the Pico
2from machine import Pin
3import time
4
5btn_a = Pin(14, Pin.IN, Pin.PULL_UP)   # A butonu, diğer ucu GND / button A, other leg to GND
6btn_b = Pin(15, Pin.IN, Pin.PULL_UP)   # B butonu / button B
7
8# GP16..GP21: AND, OR, NAND, NOR, XOR, XNOR
9leds = [Pin(n, Pin.OUT) for n in range(16, 22)]
10gates = [
11    ("AND",  lambda a, b: a and b),
12    ("OR",   lambda a, b: a or b),
13    ("NAND", lambda a, b: not (a and b)),
14    ("NOR",  lambda a, b: not (a or b)),
15    ("XOR",  lambda a, b: a != b),
16    ("XNOR", lambda a, b: a == b),
17]
18
19last = None
20while True:
21    a = int(not btn_a.value())   # basılı = 1 / pressed = 1
22    b = int(not btn_b.value())
23    results = [int(f(a, b)) for _, f in gates]
24    for led, r in zip(leds, results):
25        led.value(r)
26    if (a, b) != last:           # yalnız değişince yaz / print only on change
27        line = " ".join("%s=%d" % (g[0], r) for g, r in zip(gates, results))
28        print("A=%d B=%d  %s" % (a, b, line))
29        last = (a, b)
30    time.sleep_ms(20)

В Python есть похожая ловушка: ~True даёт -2, потому что bool на самом деле целое число. Чтобы перевернуть бит, используй not x, 1 - x или x ^ 1. Кроме того, операторы and и or в Python не всегда возвращают True или False; они возвращают последнее вычисленное значение, так что 0 or 5 даёт 5. Поэтому в коде используются преобразования int(), чтобы светодиоды получали только 0 или 1.

Идеи проектов с логическими элементами

  • Умный ночник (AND): включай свет, когда темно И замечено движение; объедини фоторезистор (LDR) и PIR-датчик в одном условии с &&.
  • Свет на лестнице (XOR): управляй одной лампой двумя кнопками внизу и вверху лестницы; вычисляй состояние лампы через XOR и коммутируй сетевую лампу модулем реле.
  • Домашняя сигнализация (AND + OR): пока система на охране, включай сирену, если открылась дверь ИЛИ окно. Дверь и окно определяй герконом; подробности в руководстве Сигнализация на Arduino.
  • Сейф с двумя ключами (AND): не давай замку открыться, пока два разных человека одновременно не нажмут свои кнопки или пока не подтверждён код на матричной клавиатуре.
  • 4-битный калькулятор: складывай два числа, введённые переключателями, по логике полного сумматора и показывай результат на 7-сегментном индикаторе.
  • Кнопка для викторины: фиксируй светодиод участника, нажавшего первым, и блокируй остальных; это программная версия защёлки на NAND.
  • Проверка чётности: сложи через XOR все биты байта; если результат 1, число единиц нечётное. Бит чётности в последовательной связи вычисляется именно так.

Частые ошибки и как их исправить

СимптомВероятная причинаРешение
Светодиод мерцает сам и меняется, если поднести рукуВисящий вход, нет стягивающего резистораДобавь к каждому входу стягивающий или подтягивающий резистор 10 kΩ
Микросхема греетсяПерепутаны VCC и GND или соединены два выходаВывод 14 должен быть VCC, а 7 — GND; никогда не соединяй выходы
Ни один элемент не реагируетНет питания, или микросхема не стоит поперёк выемкиИзмерь мультиметром между выводами 14 и 7, поставь микросхему поперёк выемки
Неверные результаты с 74HC02 или CD4011Распиновка отличается от 74HC08Подключай по таблице выводов из даташита
Микросхема потребляет больше тока, чем должнаНеиспользуемые входы оставлены висящимиСоедини все неиспользуемые входы с GND
Нестабильные результаты с ESP32 или PicoСигнала 3,3 V не хватает для 74HC при 5 VПитай 74HC от 3,3 V или используй 74HCT
if (a & b) неожиданно ложно& работает побитово; 2 & 1 = 0Используй && в условиях
if (x & 1 == 0) никогда не выполняется== вычисляется раньше &Используй скобки: (x & 1) == 0
Одно нажатие переключает элемент несколько разДребезг контактовПодожди программно 20–50 мс или используй RC-фильтр + 74HC14
Прежде чем собирать схему, запиши её таблицу истинности на бумаге, а потом проверяй строки по одной. Строка, где светодиод не совпадает с ожидаемым результатом, подскажет, что проблема в элементе, который эта строка использует, или в его подключении. Держи открытой утилиту «Логические элементы», чтобы быстро сверять таблицу, а перед сборкой схему можно опробовать виртуально в Tinkercad.

Логические элементы — алфавит всего цифрового мира, от зажигания светодиода до процессора, складывающего числа. Когда понятны AND, OR и NOT, читать условие if, правило сигнализации или сдвиговый регистр становится гораздо проще. В приложении BlueGrays есть проекты со светодиодом на кнопке, проекты с кнопками на ESP32 и Raspberry Pi Pico, проект умного освещения лестницы и проект умного освещения с PIR-датчиком и фоторезистором — каждый с пошаговыми схемами и кодом, так что логику элементов из этой статьи можно сразу применить в настоящих проектах.

Частые вопросы

Какие бывают логические элементы и для чего они нужны?

Базовых логических элементов семь: AND, OR, NOT, NAND, NOR, XOR и XNOR. Каждый смотрит на входы со значениями 0 или 1 и по своему правилу выдаёт один выход. Любая цифровая схема, включая процессоры, память, счётчики и калькуляторы, построена из сочетания этих элементов.

Как составить таблицу истинности?

Сначала по числу входов определи число строк: для n входов нужно 2ⁿ строк. Запиши входы в порядке двоичного счёта (00, 01, 10, 11), затем примени к каждой строке правило элемента и заполни столбец выхода. В схемах из нескольких элементов работу облегчает отдельный столбец для каждого промежуточного выхода.

Почему NAND называют универсальным элементом?

Потому что из одних только элементов NAND можно собрать NOT, AND, OR, NOR, XOR и XNOR. Например, соединив два входа NAND вместе, получаешь NOT, два NAND подряд дают AND, а четыре NAND — XOR. Тем же свойством обладает элемент NOR, так что он тоже считается универсальным.

Чем отличаются XOR и XNOR?

XOR выдаёт 1, когда входы различаются, а XNOR — когда входы одинаковы. XNOR — это инверсия XOR, и им проверяют, равны ли два бита. XOR используется в сумматорах, проверках чётности и для переворачивания бита.

Чем отличаются 74HC08 и 74LS08?

Обе содержат четыре двухвходовых элемента AND и имеют одинаковую распиновку. 74HC08 сделана по технологии CMOS, работает от 2–6 V и потребляет очень мало тока; 74LS08 — это TTL, она работает только около 5 V и потребляет больше. Для новых проектов выбирай 74HC, а 74HCT — когда подключаешь сигналы 3,3 V к схеме на 5 V.

Чем отличаются && и & в Arduino?

&& — логический оператор AND; он проверяет, истинны ли оба условия, и возвращает true или false. & — побитовое AND, которое сравнивает каждый бит двух чисел отдельно. Поэтому 2 && 1 — истина, а 2 & 1 — это 0; в условиях if используй &&.

Все проекты
Управление светодиодом кнопкой (удержание)
#9Новичок

Управление светодиодом кнопкой (удержание)

Как дверной звонок: свет горит, пока ты нажимаешь, и гаснет, когда отпускаешь. Arduino всё время слушает кнопку; пока она нажата, светодиод горит, а как только ты убираешь палец, он гаснет. Эта просте

4.8(113)
229
Включение и выключение светодиода кнопкой (вкл/выкл)
#10Новичок

Включение и выключение светодиода кнопкой (вкл/выкл)

Нажми кнопку один раз — свет загорится, нажми ещё раз — погаснет, совсем как выключатель в твоей комнате. Arduino замечает каждое нажатие и переводит светодиод в противоположное состояние, выжидая кор

4.8(100)
167
Управление светодиодом кнопкой на ESP32 (удержание)
#104Новичок

Управление светодиодом кнопкой на ESP32 (удержание)

Светодиод горит, когда кнопка нажата, и гаснет, когда её отпускают. Показывает, как используются цифровой входной пин ESP32 и встроенный подтягивающий резистор. Режим INPUT_PULLUP избавляет от внешнего резистора.

4.6(36)
55
Управление светодиодом кнопкой на ESP32 (переключение)
#105Новичок

Управление светодиодом кнопкой на ESP32 (переключение)

При каждом нажатии кнопки состояние светодиода меняется: если горел — гаснет, если не горел — загорается. Классический шаблон переключения. Антидребезг убирает механический шум кнопки, чтобы одно нажатие давало одно переключение.

4.6(39)
57
Светодиод с управлением кнопкой на Pico (удержание)
#155Новичок
Premium

Светодиод с управлением кнопкой на Pico (удержание)

Первый проект Pico с входом — чтение кнопки! Светодиод горит, пока кнопка нажата, и гаснет после отпускания. Это большой шаг после светодиодных анимаций: сигнал из внешнего мира в Pico. Изучи Pin.IN (режим входа), Pin.PULL_UP (встроенный подтягивающий резистор — стандарт для кнопок) и чтение button.value(). Светодиод на GP14, кнопка на GP2 (встроенный PULL_UP). Нажата = LOW (0), отпущена = HIGH (1) — обратная логика сначала путает, но это промышленный стандарт.

0.0(0)
11
Pico: независимое управление 2 светодиодами с помощью 2 кнопок
#157Новичок
Premium

Pico: независимое управление 2 светодиодами с помощью 2 кнопок

2 кнопки, 2 светодиода — полностью независимо друг от друга! Каждая кнопка управляет своим светодиодом: при нажатии BTN1 загорается красный, при нажатии BTN2 — зелёный; нажмёшь обе — горят оба. Изучишь управление несколькими объектами пинов, тернарный оператор (1 if x else 0 — компактный if-else в Python), независимое управление вводом-выводом и карту GPIO.

0.0(0)
7
Умное освещение лестницы и коридора
#82Средний
Premium

Умное освещение лестницы и коридора

Ступи на лестницу или в коридор, и свет загорается ступенька за ступенькой в ту сторону, куда ты идёшь. На каждом конце стоит датчик расстояния; мимо какого ни пройдёшь, восемь светодиодов загораются

5.0(35)
51
Умное освещение на ESP32 (PIR + LDR + реле + OLED)
#141Продвинутый
Premium

Умное освещение на ESP32 (PIR + LDR + реле + OLED)

Сделай умное освещение для коридора, ванной или кладовки на ESP32! С PIR-датчиком движения, датчиком освещённости LDR и модулем реле собирается классический сценарий «свет включается при движении в темноте»: если вокруг уже светло, движение свет НЕ включает, и днём энергия экономится. Свет горит 30 секунд; если движение продолжается, таймер сбрасывается, и свет горит дольше. OLED SSD1306 крупно показывает состояние (ON / DAYTIME / WAITING) и обратный отсчёт.

4.3(6)
11