基礎 · 読了まで18分

論理ゲートの解説:AND、OR、NOT、XORの真理値表とArduino

論理ゲートとは?AND、OR、NOT、NAND、NOR、XOR、XNORの記号、真理値表、74HC08と74HC00のIC、半加算器、Arduino・ESP32・Picoのコードを紹介します。

論理ゲートはデジタル回路の基本の部品です。それぞれが1つ以上のデジタル入力を見て、決まったルールに従って1つのデジタル出力を出します。入力は0(LOW)か1(HIGH)だけで、出力も同じです。基本のゲートはAND、OR、NOT、NAND、NOR、XOR、XNORの7つです。スマートフォンのプロセッサから洗濯機の制御基板まで、Arduinoの中のATmega328Pから電卓まで、どのデジタル機器も、何百万もの小さな論理ゲートを組み合わせて動いています。

このガイドでは、すべてのゲートの記号と真理値表、74HC08、74HC32、74HC00などの本物のIC、2つの押しボタンで作るブレッドボードの実験、NANDだけでほかのすべてのゲートを作る方法、2つのビットを足す半加算器の回路を学びます。最後の章では、同じロジックをArduinoで&&、||、!の演算子とビット演算でコードにし、ESP32とRaspberry Pi Pico(MicroPython)の例に進みます。

論理ゲートの実験用のブレッドボード
論理ゲートのICは、ブレッドボードの中央の溝をまたいで差せば、はんだ付けなしで試せます。

論理ゲートとは?そのしくみ

デジタル電子回路では、すべての信号が1(HIGH、真)か0(LOW、偽)の2つの状態のどちらかにあります。この2つの値を扱う数学をブール代数と呼びます。1854年に出版された本で、ジョージ・ブールは論理的な推論をまさにこの方法で代数として表しました。1937年にはクロード・シャノンが、この代数がリレーで作るスイッチ回路と1対1で対応することを示し、デジタル回路の設計の基礎を築きました。今では論理ゲートの中にはほんの数個のトランジスタしかありません。CMOS技術では、NOTゲートは2個のMOSFET、NANDゲートは4個でできています。トランジスタがスイッチとして働くしくみはトランジスタとは?で解説しています。

では、回路はどの電圧を1とし、どの電圧を0とするのでしょうか。それはボードの電源電圧とICのファミリーによります。下の表は、入力が確実に1、または0とみなされる限界です。2つの限界の間は不定領域で、入力がそこにとどまると、出力がでたらめに動くことがあります。何もつながっていない浮いた入力は、まさにこの不定領域をさまよいます。そのため、すべての入力は信号につなぐか、抵抗を通して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
74HCのIC5 V≥ 3.5 V≤ 1.5 V
74HCTのIC5 V≥ 2.0 V≤ 0.8 V
CD4000のIC5 V≥ 3.5 V≤ 1.5 V

7つの基本の論理ゲートとその記号

回路図では、ゲートを2つの異なる規格で描きます。アメリカの規格(ANSI/IEEE)では、ゲートごとに形が違います。ANDはDの文字のような形、ORはとがった盾のような形、NOTは三角形です。出力の小さな丸(バブル)は「反転」を意味し、NANDは実は先端にバブルの付いたANDです。XORがORと違うのは、入力側にある2本目の曲線です。ヨーロッパの規格(IEC)では、すべてのゲートが長方形で、中に書かれた記号で区別します。ANDは&、ORは≥1、XORは=1、NOTは1です。教科書では、NANDを否定論理積、NORを否定論理和、XORを排他的論理和、XNORを否定排他的論理和とも書きます。

ゲート別名ANSIの記号IECの記号論理式出力が1になるのは?
AND論理積Dの形&Y = A · B両方の入力が1
OR論理和とがった盾≥1Y = A + B少なくとも1つの入力が1
NOTインバーター(否定)三角形+バブル1(出力にバブル)Y = A'入力が0
NAND否定論理積Dの形+バブル&(出力にバブル)Y = (A · B)'少なくとも1つの入力が0
NOR否定論理和盾+バブル≥1(出力にバブル)Y = (A + B)'両方の入力が0
XOR排他的論理和二重の曲線の盾=1Y = A ⊕ B入力が違う
XNOR否定排他的論理和二重の曲線+バブル=1(出力にバブル)Y = (A ⊕ B)'入力が同じ

論理ゲートの真理値表

真理値表は、入力のすべての組み合わせに対してゲートが出す出力を並べた表です。n個の入力のゲートには2ⁿ行あります。入力2つなら4行、3つなら8行、4つなら16行です。行を2進数の数える順番(00、01、10、11)で書けば、組み合わせを飛ばすことはありません。2入力の6つのゲートを1つの表にまとめました。

ABANDORNANDNORXORXNOR
00001101
01011010
10011010
11110001

NOTゲートは入力が1つなので、表は2行です。A = 0なら出力は1、A = 1なら出力は0です。表をよく見ると、規則性に気づきます。NANDの列はANDのちょうど逆、NORはORの逆、XNORはXORの逆です。覚えるには、次の短いルールを使いましょう。ANDは「全部1?」、ORは「少なくとも1つは1?」、XORは「入力が違う?」と問います。名前がNで始まるゲートは、同じ問いに逆の答えを返します。ゲートを自分で試して記号をリアルタイムで見るには、論理ゲートツールでAとBの入力をタップするだけです。

  • AND:直列の2つのスイッチです。ランプは両方のスイッチが閉じたときだけ点きます。2本の別々の鍵で開く銀行の金庫も、ANDのルールに従っています。
  • OR:並列の2つの押しボタンです。玄関のボタンを押しても裏口のボタンを押しても、同じベルが鳴ります。
  • NOT:冷蔵庫の明かりです。ドアが閉まっている間はスイッチが押されていてランプは消え、ドアが開くとスイッチが離れてランプが点きます。
  • NAND:「すべて問題がなければ警告しない」というルールです。機械の2つの安全カバーのうち1つでも開いていると、警告ランプが点きます。
  • NOR:「どれも起きていない」というルールです。煙もガスも検知されていないときだけ点く、緑の「異常なし」のランプです。
  • XOR:階段の3路スイッチです。下と上のどちらのスイッチを切り替えても、ランプの状態が変わります。2つのスイッチの位置が違うとランプが点き、同じだと消えます。
  • XNOR:一致の確認です。2つのビットが同じときに1を出し、デジタルの比較器やエラー検出の回路に使われます。

論理ゲートのIC:74HC08、74HC32、74HC00

論理ゲートは、1つずつのトランジスタで作る代わりに既製のICを使います。ホビーの電子工作で最も一般的なファミリーは74HCシリーズです。14ピンのDIPパッケージに2入力のゲートが4つ入り、電源はいつも14番ピン(VCC)に、GNDは7番ピン(GND)につなぎます。74HCのICは2〜6 Vで動き、入力にはほとんど電流が流れず、5 Vでは1つのゲートの遅延は約10ナノ秒と、ArduinoのdigitalWriteを1回呼ぶより何百倍も速いです。各出力は4 mAの負荷で保証された電圧レベルを保ち、絶対最大定格は1出力あたり25 mA、IC全体で50 mAです。

ICゲート中身ピン配置CMOS 4000の同等品
74HC08AND2入力 × 4標準(下の表)CD4081
74HC32OR2入力 × 4標準CD4071
74HC00NAND2入力 × 4標準CD4011
74HC86XOR2入力 × 4標準CD4070
74HC02NOR2入力 × 4異なる:出力は1、4、10、13CD4001
74HC04NOT1入力 × 6入力は1、3、5、9、11、13CD4069
74HC14NOT(シュミットトリガー)1入力 × 674HC04と同じCD40106
CD4077 / 74HCS7266XNOR2入力 × 4入手しにくい。ふつうは74HC86+74HC04CD4077

74HC08、74HC32、74HC00、74HC86はピン配置がまったく同じで、中のゲートの種類だけが違います。ICの上面の切り欠きか点が1番ピンを示します。切り欠きを左にしてICを上から見ると、ピンの番号は反時計回りに並びます。下の列が左から右へ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)につなぎます。浮いた入力はノイズで切り替わり続け、ICの消費電流を増やし、隣のゲートを乱すことさえあります。一方、使わない出力は開放のままでかまいません。VCCとGNDの間に、ICのできるだけ近くに100 nFのセラミックコンデンサを入れるのを忘れないでください。

同じ仕事をするICにも、いくつかのファミリーがあります。74LSシリーズは古いTTL技術で、4.75〜5.25 Vでしか動かず、消費電流が多く、浮いた入力をたいてい1と読みます。74HCTシリーズは、TTL互換の入力を持つ74HCです。5 Vで動きますが、2 V以上を確実に1とみなすので、ESP32やPicoの3.3 Vの信号も問題なく読めます。CD4000シリーズ(CD4081、CD4011など)は3〜18 Vの広い範囲で動きますが遅く、ピン配置も74HCと違うので、CD4011は74HC00の代わりにそのまま使えません。

押しボタンでブレッドボードの実験:74HC08でANDゲート

論理ゲートを理解する一番の方法は、2つの押しボタンで自分で試すことです。この実験にマイコンはなく、必要なのは74HC08、押しボタン2個、10 kΩの抵抗2本、1 kΩの抵抗1本、LED 1個、100 nFのコンデンサ1個だけです。電源には、Arduinoの5VとGNDのピン、ブレッドボード用の電源モジュール、単3電池3本(4.5 V)のどれでもかまいません。ブレッドボードの内部のつながりがよくわからなければ、先にブレッドボードとは?を読みましょう。

ブレッドボードの実験用の押しボタン
押しボタンは押している間、両側をつなぎます。4本足の型では足が内部で2本ずつつながっているので、中央の溝をまたいで置くのが最も安全です。
  1. 74HC08を、ピンが両側に来るようにブレッドボードの中央の溝をまたいで差し、切り欠きを左に向けます。
  2. 14番ピンを+のレール(5 V)に、7番ピンを−のレール(GND)につなぎ、この2本のピンの間に100 nFのコンデンサを入れます。
  3. ボタンAの片側を+のレールに、もう片側を1番ピンにつなぎます。同じピンからGNDへ10 kΩの抵抗を入れます。このプルダウン抵抗が、ボタンを押していない間、入力をしっかり0に保ちます。
  4. ボタンBも同じように2番ピンにつなぎ、専用の10 kΩのプルダウン抵抗を付けます。
  5. 出力の3番ピンから、1 kΩの抵抗を通してLEDの長い足(アノード)へつなぎ、LEDの短い足(カソード)をGNDにつなぎます。
  6. 使わない入力の4、5、9、10、12、13番ピンをGNDにつなぎ、出力の6、8、11番は開放のままにします。
  7. 電源を入れ、4つの組み合わせを順に試します。LEDは両方のボタンを一緒に押したときだけ点くはずです。結果を真理値表と比べましょう。
接続74HC08のピン接続先
電源14(VCC)+のレール、5 V
グラウンド7(GND)−のレール
パスコン14と7の間100 nFのセラミック、ICの近くに
入力A1ボタンA → 5 V、10 kΩ → GND
入力B2ボタンB → 5 V、10 kΩ → GND
出力31 kΩ → LEDのアノード、LEDのカソード → GND
使わない入力4、5、9、10、12、13GND
使わない出力6、8、11開放
回路が動いたら、ここからが楽しいところです。74HC08を抜いて、同じピン配置の74HC32を差すと、どちらかのボタンを押すとLEDが点きます(OR)。74HC00なら両方のボタンを一緒に押すまで点いたまま(NAND)、74HC86ならボタンを1つだけ押したときだけ点きます(XOR)。1 kΩの抵抗なら赤のLEDに流れるのは約3 mAで、74HCの出力に安全な値です。違う色のLEDなら、LEDの抵抗ツールで抵抗を計算できます。

NANDですべてのゲートを作る:万能ゲート

NANDは、NANDゲートだけでほかのすべてのゲートを作れるので万能ゲートと呼ばれます。これには実用的な面もあります。CMOS技術ではNANDはわずか4個のトランジスタで作れる最も簡単なゲートの1つですが、ANDは実はNANDのあとにNOTが続いたもので、6個必要です。そのため、チップの設計者はNANDを好みます。USBメモリのNANDフラッシュも、メモリセルがNANDゲートのトランジスタのように直列につながっていることから名前が付いています。NORも万能です。月へ飛んだアポロ誘導コンピューターは、3入力のNORゲートを2つずつ持つ約2,800個のICで作られていました。

作るゲートNANDでどう作る?必要なNANDの数
NOTA NAND A(両方の入力をつなぐ)1
AND(A NAND B)の出力をNOTに通す2
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になります。ド・モルガンの第2法則は(A + B)' = A' · B'です。どちらの法則も、コードの条件を簡単にするのに役立ちます。Arduinoでは、!(a && b)はいつも(!a || !b)と同じ結果になり、!(a || b)は(!a && !b)と同じになります。

4つのNANDゲートでXORを作るのは、定番の試験問題と実習の課題で、1つの74HC00の4つのゲートでちょうど足ります。まずN1 = A NAND Bを求めます。次にN2 = A NAND N1とN3 = B NAND N1を求め、最後のゲートの出力Y = N2 NAND N3がXORになります。下の配線で、ブレッドボードの実験を4つのNANDのXORに変えられます。

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Ω + LEDY = A XOR B
14 / 75 V / GND電源

もう一歩進めましょう。2つのNANDゲートの出力を互いの入力に交差させてつなぐと、SRラッチという1ビットのメモリになります。この回路は、ボタンを離したあとも最後の状態を覚えています。プロセッサのレジスタ、カウンター、74HC595シフトレジスタのようなICは、ゲートで作ったこのようなメモリセルをつないで作られています。

半加算器:ゲートで2つのビットを足す

論理ゲートができることで最も印象的なのは、数を足すことです。2進数では、0 + 0 = 0、0 + 1 = 1、1 + 0 = 1、1 + 1 = 10(10進数の2)です。結果の右の桁を和、左にあふれる桁を桁上がり(キャリー)と呼びます。下の表を見ると、和はちょうどXOR、桁上がりはちょうどANDであることがわかります。つまり、XORゲートとANDゲートを並べると、1ビットの数を2つ足す半加算器になります。

AB桁上がり(C = A AND B)和(S = A XOR B)10進数の結果
00000
01011
10011
11102(2進数の10)

ブレッドボードで作るには、ボタンAとBを74HC86と74HC08の両方の1番と2番ピンにつなぎます。74HC86の3番ピンが和のLEDを、74HC08の3番ピンが桁上がりのLEDを駆動します。両方のボタンを押すと、和のLEDが消えて桁上がりのLEDが点きます。LEDは2進数の10、つまり2を表しています。74HC00だけを使いたいなら、XORに4つのNANDが、桁上がりを得るためにN1を反転する5つ目のNANDが必要です。つまり、半加算器には5つのNANDゲートが要ります。

半加算器は、下の桁から来る桁上がりを考えられません。そのためには全加算器が必要です。式はS = A ⊕ B ⊕ CinとCout = (A · B) + (Cin · (A ⊕ B))で、半加算器2つとORゲート1つになります。全加算器を4つつなぎ、それぞれが桁上がりを次に渡すようにすれば、4ビットの加算器になります。既製の74HC283のICも同じ4ビットの足し算をしますが、桁上がりがチェーンを伝わるのを待つのではなく、内部の高速な回路(キャリー先見)で求めます。パソコンのプロセッサの演算装置は、同じ考え方を64ビットではるかに速くしたものです。

Arduinoでの論理ゲート:&&、||、!とビット演算

Arduinoのコードでは、それぞれの論理ゲートに2つの対応があります。論理演算子(&&、||、!)は条件を組み合わせ、真か偽の結果を返し、if文やwhile文で使います。ビット演算子(&、|、^、~)は、数のすべてのビットに列ごとに一度に適用されます。1バイトを扱うのは、8つのゲートを同時に動かすようなものです。表の例では0b1100と0b1010の数を使っています。

ゲート論理(bool)ビット演算例結果
ANDa && bx & y0b1100 & 0b10100b1000
ORa || bx | y0b1100 | 0b10100b1110
NOT!a~x~0b00001100(byte)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

2種類の違いは小さく見えますが、気づきにくいバグの原因になります。&&と||は短絡評価を使います。a && bでaが偽なら、bはまったく評価されません。&はいつも両側を評価し、数のビットを見ます。2 && 1は真ですが、2 & 1は0です。0b10と0b01には共通のビットがないからです。C++には論理XORの専用の演算子はなく、2つのboolならa != bと書くのが最も読みやすい方法です。ビット演算、<<と>>のシフト、2進数について詳しくは数の表し方とビット演算をご覧ください。

では、ブレッドボードの実験をArduino Unoに移しましょう。2つの押しボタンを2番と3番のピンに、7つのLED(それぞれ330 Ωの抵抗付き)を4〜10番のピンにつなぎます。各ボタンの片側をピンに、もう片側をGNDにつなぎます。内部プルアップ抵抗をオンにするので、追加の抵抗は不要です。1つのスケッチで、7つのゲートすべてを一度に動かします。

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にします。それから各LEDが自分のゲートの結果を表示します。4がAND、5がOR、6がNOT A、7がNAND、8がNOR、9がXOR、10がXNORです。シリアルモニタを9600ボーで開くと、ボタンが変わるたびに1行全体が表示されます。4つの組み合わせをすべて試せば、自分の手で真理値表を作ったことになります。プルアップについて詳しくはArduinoに押しボタンをつなぐ方法にあります。

ビット演算の力を見るために、2つ目の例では真理値表全体を数行で求めます。コツは、Aの列を0b1100という数、Bの列を0b1010と書くことです。各ビットが表の1行に対応するので、1回の&でANDの列の4行すべてが一度に得られます。スケッチの後半では、全加算器の式を使って2つの4ビットの数を1ビットずつ足し、プロセッサの加算回路をソフトウェアでまねします。このスケッチにハードウェアは不要で、シリアルモニタを開くだけです。

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にはいつも!を使いましょう。もう1つの落とし穴は演算子の優先順位です。if (x & 1 == 0)では先に1 == 0が評価されるので、正しい形はif ((x & 1) == 0)です。また、byteの変数に~を使うと結果はintに格上げされるので、コードのように& 0x0Fや& 0xFFのマスクで切り詰めましょう。

Arduinoで74HCのICテスター

ICの印字が消えてしまったときや、まだ動くか知りたいときは、Arduinoをテスターにできます。このスケッチは、74HC00、74HC08、74HC32、74HC86(標準のピン配置のIC)の4つのゲートすべてに4つの入力の組み合わせを順に加え、出力を読み、結果を既知のパターンと比べます。ICの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の出力がICの出力とぶつかり合うことがあります。これを防ぐには、ICの出力(3、6、8、11)とArduinoのピンの間に1 kΩの抵抗を入れましょう。テストの結果は変わりませんが、配線を間違えても両側が守られます。74HC02と74HC04はピン配置が違うので、このスケッチでは直接テストできません。データシートに合わせてGATESの表を変える必要があります。

ESP32でプログラムできる論理ゲート

同じ演算子はESP32でもそのまま動きます。この例では、3つ目のボタンでゲートの種類を切り替え、1つのLEDで選んだゲートの出力を表示します。これなら、教室や発表で1つの回路で6つのゲートを見せられます。ボタンはGPIO 25、26、27に(もう片側はGNDに)、LEDは220 Ωの抵抗を通してGPIO 23につなぎます。選んだゲートの名前は、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のICを使うなら、ICも3.3 Vで給電しましょう。74HCは2 Vから動くので問題ありません。ICを5 Vで給電すると、出力の5 Vがボードの入力を壊すことがあります。逆方向では、3.3 Vの信号は5 Vで動く74HCが確実に1とみなすしきい値の3.5 Vを下回ります。5 Vの回路とやり取りする必要があるなら、74HCTのICかレベル変換モジュールを使いましょう。

Raspberry Pi PicoとMicroPythonで論理ゲート

MicroPythonでは、論理演算子を英語の単語で書きます:and、or、not。ビット演算子はC++と同じで、&、|、^、~です。下のコードは、GP14とGP15の2つの押しボタンを読み、AND、OR、NAND、NOR、XOR、XNORの結果を、GP16〜GP21の6つのLEDに表示します。各ボタンのもう片側はGNDに、各LEDは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を使いましょう。また、PythonのandとorはいつもTrueかFalseを返すとは限らず、最後に評価した値を返すので、0 or 5は5になります。そのため、コードではint()で変換して、LEDにはいつも0か1だけが渡るようにしています。

論理ゲートを使ったプロジェクトのアイデア

  • スマートなナイトライト(AND):暗い、かつ動きを検知したときに明かりを点けます。LDRとPIRセンサーを1つの&&の条件で組み合わせます。
  • 階段の明かり(XOR):階段の下と上の2つのボタンで1つのランプを制御します。ランプの状態をXORで求め、リレーモジュールで商用電源のランプを切り替えます。
  • 家庭用アラーム(AND + OR):システムが作動中に、ドアか窓が開いたらサイレンを鳴らします。ドアと窓はリードスイッチで検知します。詳しくはArduinoのアラームシステムのガイドをご覧ください。
  • 2つの鍵の金庫(AND):2人の別々の人が同時にボタンを押すか、キーパッドの番号が確認されるまで、ロックが開かないようにします。
  • 4ビットの計算機:スイッチで入力した2つの数を全加算器のロジックで足し、結果を7セグメントディスプレイに表示します。
  • 早押しクイズのブザー:最初に押した解答者のLEDを固定し、ほかの人をブロックします。これはNANDを使ったラッチのソフトウェア版です。
  • パリティチェック:1バイトのすべてのビットをXORします。結果が1なら、1の数は奇数です。シリアル通信のパリティビットは、まさにこの方法で計算されます。

よくある失敗と対処法

症状考えられる原因対処法
LEDが勝手にちらつき、手を近づけると変わる入力が浮いている、プルダウン抵抗がないすべての入力に10 kΩのプルダウン抵抗かプルアップ抵抗を入れる
ICが熱くなるVCCとGNDが逆、または2つの出力がつながっている14番ピンをVCC、7番をGNDにする。出力同士は決してつながない
どのゲートもまったく反応しない電源がない、またはICが溝をまたいで差さっていないテスターで14番と7番のピンの間を測り、ICを溝をまたいで差す
74HC02やCD4011で結果がおかしいピン配置が74HC08と違うデータシートのピンの表に合わせて配線する
ICの消費電流が多すぎる使わない入力が浮いたまま使わない入力をすべてGNDにつなぐ
ESP32やPicoで結果が不安定5 Vの74HCには3.3 Vの信号では足りない74HCを3.3 Vで給電するか、74HCTを使う
if (a & b)が思いがけず偽になる&はビットごとに働く。2 & 1 = 0条件には&&を使う
if (x & 1 == 0)が決して実行されない==が&より先に評価されるかっこを使う:(x & 1) == 0
1回押しただけでゲートが何回も変わる接点のチャタリングソフトウェアで20〜50 ms待つか、RCフィルタ+74HC14を使う
回路を組む前に真理値表を紙に書き、それから各行を1つずつテストしましょう。LEDが期待どおりにならない行があれば、問題はその行で使うゲートか、そのゲートの配線にあるとわかります。表をすばやく確かめるために論理ゲートツールを開いておき、組む前にTinkercadで回路を仮想的に試すこともできます。

論理ゲートは、LEDを点けることからプロセッサが数を足すことまで、デジタルの世界全体のアルファベットです。AND、OR、NOTがわかれば、if文の条件、アラームのルール、シフトレジスタを読むのがずっと楽になります。BlueGraysアプリには、押しボタンとLEDのプロジェクト、ESP32とRaspberry Pi Picoのボタンのプロジェクト、スマートな階段の照明のプロジェクト、PIRとLDRを組み合わせたスマート照明のプロジェクトが、ステップごとの回路図とコード付きで用意されていて、この記事のゲートのロジックを本物のプロジェクトで使えます。

よくある質問

論理ゲートの種類と用途は?

基本の論理ゲートはAND、OR、NOT、NAND、NOR、XOR、XNORの7つです。それぞれ0か1の入力を見て、独自のルールで1つの出力を出します。プロセッサ、メモリ、カウンター、電卓を含め、すべてのデジタル回路はこれらのゲートを組み合わせて作られています。

真理値表はどう作りますか?

まず入力の数から行の数を求めます。n個の入力には2ⁿ行が必要です。入力を2進数の数える順番(00、01、10、11)で書き、各行にゲートのルールを当てはめて出力の列を埋めます。複数のゲートのある回路では、途中の出力ごとに別の列を加えると作業が楽になります。

NANDが万能ゲートと呼ばれるのはなぜですか?

NANDゲートだけで、NOT、AND、OR、NOR、XOR、XNORを作れるからです。たとえばNANDの2つの入力をつなぐとNOT、NAND 2つを続けるとAND、NAND 4つでXORになります。NORゲートにも同じ性質があるので、NORも万能ゲートです。

XORとXNORの違いは何ですか?

XORは入力が違うときに1を出し、XNORは入力が同じときに1を出します。XNORはXORの反転で、2つのビットが等しいかを確かめるのに使います。XORは加算回路、パリティチェック、ビットの反転に使われます。

74HC08と74LS08の違いは何ですか?

どちらも2入力のANDゲートを4つ持ち、ピン配置も同じです。74HC08はCMOS技術で作られ、2〜6 Vで動き、消費電流がとても少なくなっています。74LS08はTTLで、5 V前後でしか動かず、電流も多く使います。新しいプロジェクトには74HCを、3.3 Vの信号を5 Vの回路につなぐなら74HCTを選びましょう。

Arduinoの&&と&の違いは何ですか?

&&は論理ANDの演算子で、両方の条件が真かを確かめ、真か偽を返します。&はビットごとのANDで、2つの数の各ビットを別々に比べます。そのため2 && 1は真ですが、2 & 1は0です。if文の条件には&&を使いましょう。

全プロジェクトを見る
ボタンでLEDを点灯(押している間のみ)
#9初級

ボタンでLEDを点灯(押している間のみ)

呼び鈴のように、押している間は点灯し、離すと消灯します。Arduinoはボタンの状態を常に読み取り、押されている間はLEDを点灯させ、指を離した瞬間に消灯させます。このシンプルな回路で、ボードが外部からの操作を検知する仕組みを学べます。

4.8(113)
229
ボタンでLEDの点灯・消灯を切り替え
#10初級

ボタンでLEDの点灯・消灯を切り替え

部屋の照明スイッチのように、ボタンを1回押すと点灯し、もう1回押すと消灯します。Arduinoはボタンが押されるたびにLEDの状態を反転させます。ボタン内部の小さな接点の振動で誤動作しないよう、短い待ち時間も設けます。1つのボタンでオン・オフを切り替える、シンプルな仕組みです。

4.8(100)
167
ESP32でボタンによるLED制御(押している間だけ点灯)
#104初級

ESP32でボタンによるLED制御(押している間だけ点灯)

ボタンを押している間はLEDが点灯し、離すと消灯します。ESP32のデジタル入力ピンと内蔵プルアップ抵抗の使い方を学びます。INPUT_PULLUPモードを使うため、外付け抵抗は不要です。

4.6(36)
55
ESP32でボタンによるLED制御(トグル)
#105初級

ESP32でボタンによるLED制御(トグル)

ボタンを押すたびにLEDの状態が切り替わり、点灯中なら消灯し、消灯中なら点灯します。基本的なトグル動作です。チャタリング対策でボタンの機械的なノイズを除去し、1回の操作で1回だけ切り替わるようにします。

4.6(39)
57
Raspberry Pi Picoでボタンを押してLEDを点灯
#155初級
プレミアム

Raspberry Pi Picoでボタンを押してLEDを点灯

Picoで初めての入力プロジェクト、ボタンの読み取りです!ボタンを押している間はLEDが点灯し、離すと消灯します。LEDアニメーションから一歩進んで、外部からPicoに信号を入力する方法を学びます。Pin.IN(入力モード)、Pin.PULL_UP(ボタンで一般的に使う内蔵プルアップ抵抗)、button.value()による読み取りを扱います。LEDはGP14、ボタンはGP2(内蔵PULL_UP)に接続します。押すとLOW(0)、離すとHIGH(1)になります。この逆の論理は最初は戸惑いますが、業界で標準的に使われています。

0.0(0)
11
Raspberry Pi Picoで2個のボタンから2個のLEDを個別に制御
#157初級
プレミアム

Raspberry Pi Picoで2個のボタンから2個のLEDを個別に制御

2個のボタンと2個のLEDが、それぞれ独立して動作します!各ボタンが対応するLEDを制御し、BTN1を押すと赤色LED、BTN2を押すと緑色LEDが点灯します。両方を同時に押すと、2個とも点灯します(並列動作)。複数のPinオブジェクトの管理(複数の入出力を同時に扱う方法)、三項演算子(1 if x else 0というPythonの簡潔なif-else)、独立したI/O制御(並列動作の仕組み)、GPIOの割り当て(GP12/13をLED出力、GP4/5をボタン入力に分ける基本的な構成)を学びます。

0.0(0)
7
階段・廊下のスマート照明
#82中級
プレミアム

階段・廊下のスマート照明

階段や廊下に足を踏み入れると、進む方向に沿って照明が1段ずつ点灯します。両端に距離センサーがあり、どちらの前を通っても、その端から8個のLEDが0.5秒間隔で順番に点灯し、その後、同じ方向に順番に消灯します。夜の暗がりで足元を照らす、おしゃれなホームオートメーションのアイデアです。

5.0(35)
51
ESP32スマート照明(PIR + LDR + リレー + OLED)
#141上級
プレミアム

ESP32スマート照明(PIR + LDR + リレー + OLED)

ESP32で廊下やトイレ、食品庫の照明をスマート化します!PIRモーションセンサー、LDR光センサー、リレーモジュールを使い、「暗い場所で動きを検知すると点灯する」定番の仕組みを作ります。周囲が明るければ動きを検知しても点灯しないため、日中の無駄な電力消費を抑えられます。照明は30秒間点灯し、動きが続くとタイマーがリセットされて点灯時間が延びます。SSD1306 OLEDには大きな文字で状態(ON / DAYTIME / WAITING)とカウントダウンを表示します。

4.3(6)
11