# Tutorials: Microcontroller systems (MICSY)

## Combinational logic

last updated: 18/04/19

### Introduction

Song of this chapter: Supertramp > Breakfast in America > The logical song

We have seen in the first chapter, that digital circuits are often calculating (arithmetic). A processor or microcontroller needs one or more Arithmetic logic units (ALU). Now let's see for the logic part.

We can split the logic up in time-independent logic where no memory is needed called the combinational logic and the sequential logic, in which the output depends on the present input but also on the history of the input.

Combinational logic changes instantly, meaning the output of the circuit responds as soon as the input changes.

(Naturally there is a little delay because of the propagation of the signal through the circuit.)

### Digital logic (Boolean algebra) (wiki)

Digital logic is based on Boolean (George Boole) algebra. The Boolean algebra is based on the truth values true and false (1 and 0). The main operations of Boolean algebra are the `conjunction AND` (`∧`), the `disjunction OR` (`∨`), and the `negation NOT` (`¬`).

Boolean algebra has been fundamental in the developing of digital electronics and thus all modern computer systems. It is a system of rules that allow to make extremely complicated decisions based on simple yes or no questions.

Digital logic is important in programming and provided for in all modern programming languages. Today circuits are often replaced with software in microcontroller.

#### Basic operations AND, OR, NOT

Truth tables help to quickly understand Boolean operations:

` AND` ` OR` `NOT`
`B` `A` | `A ∧ B` `A ∨ B` ` Ā`
`0` `0` | `  0` `  0` ` 1`
`0` `1` | `  0` `  1` ` 0`
`1` `0` | `  0` `  1`
`1` `1` | `  1` `  1`

It is possible to to define the conjunction in terms of the negation and the disjunction, or to define the disjunction in terms of the negation and the conjunction (De Morgan's laws): So one basic operation with a NOT suffices to build all digital circuits. We will see that the combination of the conjunction AND with the negation NOT gives one basic gate (`NAND`) to be used as universal gate.

#### Boolean laws: ### Logic gates (wiki)

We have already build an AND-gate and OR-gate with diodes in electronics fundamentals. Logic gates are devices implementing a Boolean function. They perform a logical operation on one or more binary inputs and produce one binary output. They can be constructed with diodes and transistors, but also with electromagnetic relays (relay logic), mechanical elements, molecules, fluidic logic, pneumatic logic, optics ....

Amplified logic gates can be cascaded and so allow the construction of physical models using all of the Boolean algorithms and mathematics. Thus are created circuits like multiplexers, registers, ALUs, computer memory an even complete microprocessors, which may contain more than 100 million gates.

Our simple diode logic gates from electronic fundamentals had no amplifying element. So we need transistors. Gates using bipolar transistors and resistors (resistor–transistor logic RTL) were used in early integrated circuits. After diodes in replacement for the resistors (diode–transistor logic DTL with higher speed and better density) bipolar transistors replaced the diodes for transistor–transistor logic (TTL). To reduce power consumption today gates are made from MOSFET's (CMOS).

Prefabricated logic gates in integrated circuits as the famous TTL 74xx series from Texas Instruments simplified the life of the engineers.

Today these fixed-function logic gates are often replaced by programmable logic devices as Field-Programmable Gate Arrays (FPGA). With these, it is easily possible to change the logic design of a hardware system by reprogramming it.

#### Basic gates

To draw circuit diagrams, we need as in electronics standardized symbols. We will use `DIN EN 60617-12` (IEC 60617-12:1997). Here the most basic gates: `AND`, `OR` and `NOT`, and the important gates `NAND`, `NOR` and `XOR`: ###### "Just do it" CL1:
• We will use the IC's from the 74xx family to build our first circuits. Build a circuit with a NAND gate using an IC 74HC00 on a breadboard. Wires will do as switches for the 2 inputs and a LED as output. Look at the data sheet to power the IC. Does the IC deliver enough current for your LED? Draw the circuit and the truth table.

• Do the same thing for a NOR.

• Find a circuit to get a NOT from a NAND (look at the truth table).

• We want to build a gate whose output is true if both inputs are equal. We will use for this an exclusive OR (XOR). Write the truth table of XOR and the the logic equality gate. Build and document the circuit and test it. Research the name and symbol for such a logic equality gate?

#### Arithmetic circuits

##### Half and full adder

Let’s do a little arithmetic using logic :) :

Do you remember the first 4 rules (truth table) of the binary addition? A circuit doing this calculations is called a half adder. It has two inputs and two outputs.

S1 S2   Sum Carry (C)
` 0` ` 0` | ` 0` `  0`
` 0` ` 1` | ` 1` `  0`
` 1` ` 0` | ` 1` `  0`
` 1` ` 1` | ` 0` `  1`

For the sum we need an XOR-gate and for the carry an AND. To get the Boole formula of a truth table we look at the cases where the output is `1`. For these cases we make a conjunction (AND) of the inputs named a conjunction clause. We get the formula by a disjunction (OR) of these different clauses. The formula is called Disjunctive Normal Form (DNF) and is an OR of ANDs.

Let’s try it on our half adder: If we want to use NAND's we can transform our formulas. A double negation does not change the formula! The De Morgan’s law gives us our NAND gates:  Though the found solution is not necessarily the solution with the fewest gates. By using the Boolean laws we can also find the following solution: ###### "Just do it" CL2:
• Use the Boolean laws to demonstrate that the formula above is the formula of the half adder.

• Build a half adder with only 5 `NAND`-gates on a breadboard and test it. Draw the circuit.

To get circuits with a reduced number of gates we can use our intuition with the Boolean laws. An easier way are Karnaugh maps, a graphical method of simplifying Boolean algebra expressions. For big truth tables and more than 4 input variables the Karnaugh maps attain their limits. A similar method, the Quine–McCluskey algorithm can then be used. It is a tabulation method that makes it more efficient for use in computer algorithms.

To reduce formulas with the above method, you can use online tools like:

###### "Just do it" CL3:
• Document the truth table and the formulas of a 1-bit full adder (get the formulas from the truth table!).

• The formula for the sum can't be reduced, but we can reduce the formula for the Carry. Do it with an online tool.

• Let's simulate this circuit with https://www.multisim.com. Take LED's for the outputs and document the circuit (screenshot or export png). Test all combinations.

• A full adder can also be build with two half adder. We will use the NAND circuit from above. Build the circuit and simulate this circuit with Multisim. Document the circuit (screenshot or export png). Test all combinations.

###### "Just do it" CL4:
• Let's play a little bit :). Go to nandgame homepage http://www.nandgame.com/ and play all the logic and arithmetic levels. Document (screenshots) your progression.

#### Decoder circuits

We have already seen a decoder (in software) in our 7-segment display (SSD) example of the previous chapter. Naturally we could decode the SSD in hardware with logic gates. There exist actually complete SSD decoder in the he TTL 74xx series.

###### "Just do it" CL5:
• In Multisim we find under indicators an LED bar. Let's build a decoder that decodes binary numbers from 0 to 7 (3 inputs) to light the LED's 1 to 8 from the bar. Find the 8 formulas. Build the circuit and simulate this circuit with Multisim. Document the circuit (screenshot or export png). Test all combinations.

#### Multiplexer and demultiplexer (wiki)

Often we need to chose one signal from more signals in a digital system. Here an analogy with rotary switches: In digital electronics we use multiplexer and demultiplexer (mux and demux). They can easily be implemented with some basic gates. The truth table for a multiplexer 2 to 1 can be simplified intuitively even without using Boolean laws, because if C=0, Z = A and if C = 1, Z = B:

C B A   Z      C   Z
`0` `0` `0` | `0`      `0` | `A`
`0` `0` `1` | `1`      `1` | `B`
`0` `1` `0` | `0`
`0` `1` `1` | `1`
`1` `0` `0` | `0`
`1` `0` `1` | `0`
`1` `1` `0` | `1`
`1` `1` `1` | `1`

Using the disjunctive normal form we get: To demultiplex, we need only `AND`-gates to let pass the right signal and block the other signal with `NOT`'s on the control line. ###### "Just do it" CL6:
• Simulate a 3 to 1 mux and a 1 to 3 demux in Multisim.

#### Logic levels (wiki)

Logic Level are the voltages in which a signal can exist.

There are different logic families (e.g. DTL, TTL, CMOS) with varying logic levels. As seen, binary `1` is referred to as a `HIGH` signal and a binary `0` is referred to as a `LOW` signal. The logic level voltages can be found in the data sheets. We distinguish:

• `VIL` Maximum `Input` voltage level to be considered a `LOW`.
• `VIH` Minimum `Input` Voltage level to be considered a `HIGH`.
• `VOL` Maximum `Output` voltage level a device provides for a `LOW` signal.
• `VOL` Minimum `Output` voltage level a device provides for a `HIGH` signal.
##### TTL

One of the most used logic families is the Transistor-Transistor Logic TTL build with bipolar transistors and working with 5 V. The logic level voltages can be found in the data sheets. The minimum input voltage level for LOW `VIL` of a device is higher than the output voltage for LOW of a device `VOL`, because of possible noise adding to the signal. The maximum output voltage level for HIGH `VOH` is higher than the input voltage for HIGH of a device `VIH`, because of possible voltage losses on lengthy connection wires.

If the voltage lies in the `floating` zone, between `VIL` and `VIH`, we don't know if the chip decides for a HIGH or a LOW. The state can bounce arbitrarily between HIGH and LOW.

##### Arduino

The Arduino Uno uses an ATmega328 chip from ATMEL. Let's look at the ATmega328 data sheet. The typically supply voltage VCC is 5 V:

• `VIL` = 0.3VCC = 1.5 V
• `VIH` = 0.6VCC = 3 V
• `VOL` = 0.9 V
• `VOL` = 4.2 V The levels of Arduino make working with other hardware simpler because the invalid region of voltages is only between 1.5 V and 3.0 V. The noise margin is greater, as is the threshold for a LOW signal.

##### ESP8266 and ESP32 (3.3V)

The ESP8266 and the ESP32 both work with 3.3V, but their is a big difference! The ESP8266 is 5 V-tolerant means it will not be destroyed by 5 V on an input pin, and even works with 5V as HIGH. This is not true for an ESP32. An ESP32 will be destroyed by using voltages above 3.6V! Here we must use a simple voltage divider or a logic level shifter to get 3.3V from 5V.

###### "Just do it" CL7:
• Get the logic levels for the ESP8266 and ESP32 from their respectively data sheets.
• Calculate the resistances for a voltage divider (5V → 3.3V).
• Will the output logic levels of an ESP32 work on the inputs of an TTL gate?
##### Open collector (wiki)

Many gates and integrated circuits (IC) have as output no specific voltage or current. The output pin is connected to the collector of an internal NPN transistor (common emitter, working as a switch, but this collector is internally not connected to a pull-up resistance going to VCC (similar for a MOSFET where the output is called open drain). The pull-up resistor will be external, and so we can vary the current by the resistance and use another supply voltage. Often open-collector transistors withstand higher voltage than the chip supply voltage, so that motors or relays van be driven.

Another advantage is that multiple open-collector outputs can be connected to a single line.When all outputs attached to the line are in an high-impedance state, the pull-up resistor holds the wire on HIGH. If one or more devices outputs LOW (connection to ground) we get a low. This allows shared buses like I²C.

##### Active-low and active-high

When working with ICs and microcontrollers, we see sometimes pins that are active-low. The other "normal" pins are active-high, meaning they use the positive logic (activated with HIGH). For an active-low pin, we must pull that pin to LOW (ground) to activate the function (negative logic).

Often both active-low and active-high pins can be found on the same IC. The active-low pins have a NOT notation (line over pin description). For example, many ICs have a chip enable pin CE. If we find a line over CE then that pin is active-low and would need to be pulled to GND to get the chip to become enabled. With no line over CE, the pin is active high, and needs to be pulled to HIGH (VCC) to enable the Chip. ### Digital logic in programming

Very often programming is making decisions using Boolean logic: "if true, then do ...". So let's see how Boolean logic is used in programming.

#### Bitwise Logic

Our gates do their logic "bitwise". As we work with bytes the logic is applied bitwise on each single bit of our byte. The basic operators are:

• the single ampersand `&` for `AND`,
• the single pipe-sign `|` for `OR`,
• and the tilde `~` for `NOT`.

Other interesting operators are:

• the caret `^` for `XOR`,
• the double "greater then"-sign `>>` for a right shift,
• and the double "less then"-sign `<<` for a left shift

Here some examples in Arduino and the corresponding output:

``````    // bitwise operations

void setup() {
byte a = 0b01100110;
byte b = 0b10111010;
Serial.begin(115200);
delay(1000);
Serial.println ("01100110 &     01100110 |   ~(01100110)");
Serial.println ("10111010       10111010       -------- ");
Serial.print   ("--------       --------       ");
Serial.println (byte(~a),BIN);
Serial.print   ("00");
Serial.print   (a & b,BIN);
Serial.print   ("       ");
Serial.println (a ^ b,BIN);
Serial.println ();
Serial.println ("01100110 ^     01100110 >> 3  01100110 << 5");
Serial.println ("10111010       --------       -------- ");
Serial.print   ("--------       0000");
Serial.print   (a >> 3,BIN);
Serial.print   ("       ");
Serial.println (byte(a << 5),BIN);
Serial.println (a ^ b,BIN);
}

void loop() {}
``````

Output:

``````    01100110 &     01100110 |   ~(01100110)
10111010       10111010       --------
--------       --------       10011001
00100010       11011100

01100110 ^     01100110 >> 3  01100110) << 5
10111010       --------       --------
--------       00001100       11000000
11011100
``````

We use some (dirty) tricks here to get a decent output. The NOT-operation and the "left-shift"-operation use more than one byte for their result, so we force the result in a byte with the `byte()` function. The `BIN` parameter of `Serial.print()` omits leading zeroes, so we adjust a little bit for this result. The right way would be to write a function to return the corrected string.

###### "Just do it (if you feel like it)":
• Test the program and omit the tricks to see the results.
• Write a function that returns a string with binary digits and leading zeroes for a byte.

Masking is the voluntary setting of a bit or bits to `0` or `1` by using a bit-mask and a Boolean operation like `AND`, `OR` or `XOR` without changing the other bits of e.g a byte.

##### Masking with AND
A mask with AND sets bits to `0`

An `AND`-operation with `0` sets the bit to `0`!

old
value
`AND` mask   new
value
`  0` ` &` ` 0` `=` ` 0`
`  1` ` &` ` 0` `=` ` 0`

An `AND`-operation with `1` does not affect the state of a bit (new value = old value).

old
value
`AND` mask   new
value
`  0` ` &` ` 1` `=` ` 0`
`  1` ` &` ` 1` `=` ` 1`
##### Masking with OR
A mask with OR sets bits to `1`

An `OR`-operation with `1` sets the bit to `1`!

old
value
`OR` mask   new
value
`  0` `|` ` 1` `=` ` 1`
`  1` `|` ` 1` `=` ` 1`

An `OR`-operation with `0` does not affect the state of a bit (new value = old value).

old
value
`OR` mask   new
value
`  0` `|` ` 0` `=` ` 0`
`  1` `|` ` 0` `=` ` 1`
##### Masking with XOR
A mask with XOR toggles bits

An `XOR`-operation with `1` toggles the bit!

old
value
`XOR` mask   new
value
`  0` ` ^` ` 1` `=` ` 1`
`  1` ` ^` ` 1` `=` ` 0`

An `XOR`-operation with `XOR` does not affect the state of a bit (new value = old value).

old
value
`XOR` mask   new
value
`  0` ` ^` ` 0` `=` ` 0`
`  1` ` ^` ` 0` `=` ` 1`
##### Example: Masking SF-register in Arduino

As seen the Arduino Uno uses the ATmega328 (DIP) micro-controller. This controller has 23 programmable input/output (I/O, GPIO pins General Purpose Input Output) pins. Often GPIO's are grouped to 8 pins forming one byte named a port. ATmega328 has two full 8-bit ports, portB and portD, and one portC with 6 usable digital pins PC0-PC5 (PC6 is needed for Reset). The pins can also be used as analog input ports, and have the Arduino notation A0-A5. If we use the pins for digital input or output, this is done inside our micro-controller with 3 Special- Function-registers (SF-register) per port, sometimes named I/O-register.

• The Data Direction-Register PORTx (`DDRx`) is the first register used to determine if our digital pin is an output pin or an input pin. `0` means input, `1` means output.

• The PORTx INput Pins adress (`PINx`) is the input register reading directly from the pins. Don't let the pins float! Using switches or push-buttons you need a pull-up resistor.

• The PORTx data register (`PORTx`) has two functions!! If `DDRx` marks the pin as an output, `PORTx` is used to set the output to `0` or `1`. If `DDRx` marks the pin as an input, `PORTx` is used to activate an internal pull-up resistor with `1` or to deactivate the internal pull-up resistor with `0`.

Let's look at a simple setup from an Arduino program and do a little reverse engineering. We will use the Teensy 2.0 board because the code of the libraries is easier to understand and compiling generates a list file . Teensy 2.0 uses an ATmega32u4, the same chip as for the Arduino Leonardo. We get with this chip the whole portB and portD and 6 pins from portF. ``````    void setup() {
pinMode(2,INPUT);         // Teensy 2.0 digital pin 2 is PB2
pinMode(8,OUTPUT);        // Teensy 2.0 digital pin 8 is PD3
pinMode(A1,INPUT_PULLUP); // Teensy 2.0 digital pin 20 or analog pin A1 is PF1
}

void loop() {}
``````

After compiling, we find in a temporary folder (/tmp on linux) the hex file (.hex) and the list file (.lst) of our program. The hex file doesn't help much, because it's our machine program in hexadecimal. Here is the line with the setup commands, the interesting code is between the 2 vertical bars :) :

``````:1001B00027CF|22982A98539A8198899A|EEE7F0E0FF
``````

The stripped list file gives more information:

``````000001b2 <setup>:
1b2:    22 98           cbi 0x04, 2
1b4:    2a 98           cbi 0x05, 2 ; 5
1b6:    53 9a           sbi 0x0a, 3 ; 10
1b8:    81 98           cbi 0x10, 1 ; 16
1ba:    89 9a           sbi 0x11, 1 ; 17
...
1c6:    08 95           ret
``````

In the first column we see the address of the flash. The setup begins at 0x1B2. After the address comes the 2 byte machine code for the flash. And then the code in assembler language. There are only 2 assembler commands here, set bit (`sbi`) and clear bit (`cbi`). Both can handle single bits of our SF-register. So in assembly there is no masking needed for this register (but we need masks for other SF-register). After the commands we get the address of the SF-register. Here is a little table with the addresses for ATmega32u4:

0x03 PINB 0x09 PIND 0x0F PINF
0x04 DDRB 0x0A DDRD 0x10 DDRF
0x05 PORTB 0x0B PORTD 0x11 PORTF

So we get:

``````pinMode(2,INPUT);            cbi DDRB,PB2  ; set Arduino digital Pin 2 as input `0`
cbi PORTB,PB2 ; no internal pull-up resistor! PORTB = `0`
pinMode(8,OUTPUT);           sbi DDRD,PD3  ; set Arduino digital Pin 8 as output `1`
pinMode(A1,INPUT_PULLUP);    cbi DDRF,PF1  ; set Arduino digital Pin 20 (A1) as input `0`
sbi PORTF,PF1 ; connect internal pull-up resistor! PORTB = `1`
``````

The interesting part is the C-code in the list file used by the internal Arduino library:

``````    CORE_PIN2_DDRREG  &= ~CORE_PIN2_BITMASK;
(or CORE_PIN2_DDRREG  = CORE_PIN2_DDRREG & ~CORE_PIN2_BITMASK;)
``````

stands for `cbi DDRB,PB2` to clear bit 2 in DDRB register. So the `CORE_PIN2_DDRREG` stands for `DDRB`, and the `CORE_PIN2_BITMASK` must be `0b00000100` or `0x04` because the byte is inverted. So we get:

``````    DDRB  = DDRB & ~(0b00000100);
(or DDRB = DDRB & 0b11111011; or DDRB &= 0b11111011; or DDRB &= 0xFB;)
``````

So we can rewrite our Arduino program this way using SF-register and masking:

``````    void setup() {
DDRB =  DDRB & 0b11111011;   // Set PB2 in DDRB to 0 with and AND mask (PB2 = input)
PORTB = PORTB & 0b11111011;  // Set PB2 in PORTB to 0 with and AND mask (PB2 has no pull-up)
DDRD =  DDRD | 0b00001000;   // Set PD2 in DDRD to 1 with and OR mask (PD3 = output)
DDRF =  DDRF & 0b11111101;   // Set PF1 in DDRF to 0 with and AND mask (PF1 = input)
PORTF = PORTF | 0b00000010;  // Set PF1 in PORTF to 1 with and OR mask (PF1 has pull-up)
}

void loop() {}
``````

Naturally we would not do it this way, because the code is less readable and not portable (it will not run on another Arduino not using the ATmega32u4 chip). But masking is often necessary in our programs, and working with ports can also simplify the programs as seen in our SSD example from the previous chapter.

#### Bit shifting

A very useful bitwise operation is the arithmetic bit shift to right or left (Arduino reference). With the shift operators `>>` or `<<` we simply slide the data right or left by a certain number of places. The bits that are shifted out disappear. A zero is shifted in from the other end. This operation equates a `logical shift` in assembler (logical shift right `lsr` resp. logical shift right `lsl`).

``````    byte a = 0b10010110;
a = a>>3;  // right shift three positions: a = 0b00010010
a = a<<5;  // left shift five positions:   a = 0b01000000
``````

The shift of one position to the right equates a division by 2 in binary! A right shift of two positions is a division by 4 etc..

``````    byte a = 0b00010110;  // = 22
byte b = 0b00010110;  // = 22
a = a>>1;  // right-shift one positions:   a = 0b00001011 = 11 = 22/2
a = a>>1;  // right-shift two positions:   a = 0b00000101 = 5  = 22/2/2
a = a>>1;  // right-shift three positions: a = 0b00000010 = 2  = 22/2/2/2
a = b>>3;  // right-shift three positions: a = 0b00000010 = 2  = 22/8
``````

On the other hand a shift of one position to the left equates a multiplication by 2 in binary! A left shift of two positions is a multiplication by 4 etc..

``````    byte a = 0b00010110;  // = 22(10)
byte b = 0b00010110;  // = 22(10)
a = a<<1;  // left shift one positions:  a = 0b00101100 = 44 = 22*2
a = a<<1;  // left shift two positions:  a = 0b01011000 = 88 = 22*2*2
a = b<<4;  // left-shift four positions: a = 0b01100000 = 96 error
``````

In the last example we get an error because `22·16 = 352` and this can't fit in a byte. But shifting also works with integer or long!

#### Assignment operators

We often need to change a value on it's self (`a = a<<1` or `a = a + 1`) in programming. in C++ language and thus in Arduino we got a a shorthand notation. The operator is placed before the equal sign. This is true for most operators. Here some examples:

``````    int a = 2;  // 0b0000000000000010 = 2
a += 4;     // 0b0000000000000110 = 6 = 4+2
a <<= 4;    // 0b0000000001100000 = 6*16 = 96 = 64+32
a |= 3;     // 0b0000000001100011 = 99 (mask 3 = 0b11)
a &= ~64;   // 0b0000000000100011 = 35 (mask ~64 = 0b1111111110111111)
a ^= 7;     // 0b0000000000100100 = 36 (mask 7 = 0b111)
a ^= 7;     // 0b0000000000100011 = 35 (mask 7 = 0b111)
``````
###### "Just do it" CL8:
• Write an Arduino program simulating a `NOR` (2 inputs PB4 and PB5, 1 output PB6 with LED). Use bitwise logic to complete the program. Test the `NOR` on a breadboard. As we will see this program is better written with Boolean logic (more readable, less complicated) but we want to train masking and shifting. Here an example with an AND:

``````  // NAND with bitwise logic (Teensy 2.0)

byte in1, in2, in, out_NAND;

void setup() {
DDRB = DDRB & 0b11111100;   // PB0, PB1 input
PORTB = PORTB | 0b00000011; // PB0, PB1 pullup
DDRB = DDRB | 0b00001100;   // PB2 output
}

void loop() {
in = ~(PINB);  // invert to get positive logic (pull up)
in1 = in & 0b00000001; //
in2 = in & 0b00000010;
in2 = in2 >> 1; //shift one bit to the right
out_NAND = ~(in1 & in2);
out_NAND = out_NAND << 2; //shift two bit to the left
out_NAND = out_NAND & 0b00000100;
if (out_NAND == 0) {
PORTB = PORTB & 0b11111011;  // clear output
}
else {
PORTB = PORTB | 0b00000100;   // set output
}
}
``````
###### "Just do it" CL9:
• In our 7-segment display (SSD) example from the previous chapter we had no decimal point after the highest digit, so we saw our voltage in `mV`. Let's change this and connect the dot (Pin3) of the display with the highest bit of our segment port. Use an OR mask to switch the dot on the highest digit to ON. Document this line of code.
• We want to use the same SSD to view a 16 bit integer. To force 16 bit on all platforms we use the data type short. Write a program to extract the 4 nibbles of a short variable and display the value on the display.
###### "Just do it" CL10:
• Write a program to do a rotational shift to right on 8 LED's using masking and arithmetic shift operations.
• Add a button to switch between rotational shift to right and left.

#### Boolean (logical) and comparison operators

##### Boolean operators

It is important to not confound these Boolean operators with the bitwise operators!

• the double ampersand `&&` for `Boolean AND`
• the double pipe sign `||` for `Boolean OR`
• the exclamation mark `!` for `Boolean NOT`

Example with Boolean operators:

``````    // NAND and NOR gate

const byte in1_pin = 0;
const byte in2_pin = 1;
const byte out1_pin = 2;
const byte out2_pin = 3;

bool in1, in2, out1_NAND, out2_NOR;

void setup() {
pinMode(in1_pin,INPUT_PULLUP);
pinMode(in2_pin,INPUT_PULLUP);
pinMode(out1_pin,OUTPUT);
pinMode(out2_pin,OUTPUT);
}

void loop() {
in1 = !digitalRead(in1_pin); // invert to positive logic (Pull-Up!)
out1_NAND = !(in1 && in2);
out2_NOR  = !(in1 || in2); // alt. de Morgan !in1 && !in2
digitalWrite(out1_pin,out1_NAND);
digitalWrite(out2_pin,out2_NOR);
}
``````
##### Comparison operators

Often we have to compare values and need a Boolean `TRUE` or `FALSE`.

• the double equal sign `==` for `is equal to?`
• the exclamation mark equal sign `!=` for `is not equal to?`
• the greater than sign `>` for `is greater than?`
• the less than sign `<` for `is less than?`
• the greater than sign equal sign `>=` for `is greater than, or equal to?`
• the less than sign equal sign `<=` for `is less than, or equal to?`

These operators are often used for flow control in `if/else` or `switch/case` statements but also in `while()/do..while()` or `for()` loops.

``````    if ((raining != true) && (windSpeed <= 3) && (holiday == true)) {
flyDrone();
}
if ( holiday != true ) {
work();
}
else if ((raining != true) && (windSpeed <= 3)) {
flydrone();
}
else {
goForAWalk();
}
while (windy != true) {
flyDrone();
}
for (byte i = 0; i < 500; i++) {
}
``````

Use parentheses around the subclauses to keep your code as readable as possible by grouping subclauses together.

##### Lookup tables (LUT)

Every truth table can be programmed with a lookup table. The inputs are interpreted as number (case) and are used as index to access the table (e.g. truth table with 3 inputs gives the numbers from 0-7).

Here the same example as above using lookup tables.

``````    // NAND and NOR gate with Look Up Table (LUT)

const byte in1_pin = 0;
const byte in2_pin = 1;
const byte out1_pin = 2;
const byte out2_pin = 3;

// Look Up Tables (LUT's)
bool NAND = {1, 1, 1, 0};
bool NOR  = {1, 0, 0, 0};

byte in1, in2, in;
bool out1_NAND, out2_NOR;

void setup() {
pinMode(in1_pin,INPUT_PULLUP);
pinMode(in2_pin,INPUT_PULLUP);
pinMode(out1_pin,OUTPUT);
pinMode(out2_pin,OUTPUT);
}

void loop() {
in1 = !digitalRead(in1_pin); // invert to positive logic (Pull-Up!)