Tutorials: Microcontroller systems (MICSY)

Numbers and codes

last updated: 07/04/19

For students:

Beginning with this chapter we will come across many "Just do it" tasks. These tasks have to be carried out and have to be documented thoroughly!
Your marks will depend on the documentation!

To better understand parts of this module, first work through the module electronics fundamentals.

Introduction

Song of this chapter: Kraftwerk > Computerwelt > Nummern

We live in a digital world. Digital comes from digit or digiti in Latin meaning "fingers". Fingers are used to count and nine of them correspond for us to the ten digits 0-9 (symbols) of the decimal system.

If we use numbers we quantize a value, making the step from analog (continuous) to digital (sequence of discrete values). Computers need numbers. We will see later in this course how analog signals are converted to digital signals.

So why did digital electronics won over analog electronics? Analog signals are subject to electronic noise and distortion which can progressively degrade the signal. Digital signals have a finite resolution and can be processed and transmitted without introducing significant additional noise or distortion. Degradation can even be detected and corrected.

digital vs analog


With the increasing speed of digital circuits and their miniaturization the analog world didn't stand a chance in most applications.

OK it's not the most tingling subject to begin with, but without a good understanding how digital systems calculate and use only two states (high voltage and low voltage) to do all their job, you will not really progress if problems occur in your projects (and they will occur!).

Numeral systems (wiki)

We have ten fingers, to count from one to ten, an unary numeral system in which every natural number is represented by a corresponding number of symbols (e.g fingers). After the invention of the new symbol 0 (zero), it was possible to develop a positional numeral system because zero is of crucial importance to be able to "skip" a power. Today we use a base-10 numeral system called decimal system with 10 digits from zero to nine. Mankind had other possibilities ;). If we would use our fingers in a binary positional number system with each finger as a position we could count from zero to 1023. Or why don't we use today a base-20 (vigesimal, fingers and toes) numeral system like in the Mesoamerican Long Count calendar from the Maya?

In a positional base b numeral system we get b basic digits (symbols). As zero is included, b-1 natural numerals are used.

Base digits
2 0-1
8 0-7
10 0-9
16 0-9, A-F

Numeral systems with different bases can have sometimes funny names like Quaternary, Nonary, Duodecimal, Tritridecimal or Duosexadecimal. For a list look here.

The binary (base 2) and hexadecimal (base 16) systems are, extensively used in computer science. Sometimes also the octal (base 8) system. The binary system uses only the digits 0 and 1. The hexadecimal system uses all the digits from the decimal system, plus the letters A through F (decimal numerals 10 to 15).

A numeral is a sequence of digits, which may be of arbitrary length. Each position in the sequence has a decimal place value. Place values for different bases:

Base place
b 6 5 4 3 2 ones place
Base values
b b5 b4 b3 b2 b1 b0
2 32 16 8 4 2 1
8 32768 4096 512 64 8 1
10 100000 10000 1000 100 10 1
16 1048576 65536 4096 256 16 1

The place value can be calculated with the base:

Place value = b(place number-1)

The value of the numeral is calculated by multiplying each digit's value in the sequence by its place value, and summing the results. Let's see some examples:

Base example
2 1101 = 1·8 + 1·4 + 0·2 + 1·1 = 13(10)
8 1101 = 1·512 + 1·64 + 0·8 + 1·1 = 577(10)
10 1101 = 1·1000 + 1·100 + 0·10 + 1·1 = 1101(10)
16 1101 = 1·4096 + 1·256 + 0·16 + 1·1 = 4353(10)

For mathematicians a number is a count that is an idea in our minds. A numeral is a symbol or name that stands for a number. In short: the number is an idea, the numeral is how we write it. In normal language number and numeral are used for the same. But we use always digit when talking about the single symbols that make up numerals.

Because we don't want to confound numbers of different numeral systems, it's a good idea to use subscribed parentheses with the base included: 1001(2) ≠ 1001(10), 1001(2) = 9(10).

"Just do it" NC1:
    Base number
  b 4 3 2 1 -1 -2
  Base values
  b b3 b2 b1 b0 b-1 b-2


binary clock

Binary (wiki)

Computers and electronics have no fingers but only two ways to represent the state of anything: high voltage and low (or no) voltage (current, field). So all calculations and manipulations of computers rely on a base-2 number system, the binary numeral system.

In binary we have only two symbols: 0 and 1. But even by using only two symbols we can create any number that a decimal system can.

Bit (wiki)

The basic unit of information used in computing and digital communications is the bit (BInary Digit). The bit can only have one of two values. To work with bits we use two-state devices like per example a switch. The unit symbol for the bit is bit.

bit

The two physical states of a binary digit represented by a device can be interpreted as logical values (true/false, yes/no), activation states (on/off), any other two-valued attribute. The assignment is a matter of convention, and different assignments may be used even within the same device or program.

In our image the convention is that of the positive logic, meaning the higher voltage level represents the value 1 and the lower voltage level represents the value 0.

"Just do it" NC2:

Counting and converting in binary and hexadecimal

Any number can be represented by a sequence of bits. As seen above we use a positional system, the greater the number the more positions we need. As the number of positions in a binary system can get very high, the hexadecimal system (base 16) is often used by humans to ease the handling. By grouping 4 positions in binary we get one position in hexadecimal.

Binary Hex. Dec. | Binary Hex. Dec. | Binary Hex. Dec. | Binary Hex. Dec.
   0 0 0 | 10000 10 16 | 100000 20 32 | 110000 30 48
   1 1 1 | 10001 11 17 | 100001 21 33 | 110001 31 49
  10 2 2 | 10010 12 18 | 100010 22 34 | 110010 32 50
  11 3 3 | 10011 13 19 | 100011 23 35 | 110011 33 51
 100 4 4 | 10100 14 20 | 100100 24 36 | 110100 34 52
 101 5 5 | 10101 15 21 | 100101 25 37 | 110101 35 53
 110 6 6 | 10110 16 22 | 100110 26 38 | 110110 36 54
 111 7 7 | 10111 17 23 | 100111 27 39 | 110111 37 55
1000 8 8 | 11000 18 24 | 101000 28 40 | 111000 38 56
1001 9 9 | 11001 19 25 | 101001 29 41 | 111001 39 57
1010 A 10 | 11010 1A 26 | 101010 2A 42 | 111010 3A 58
1011 B 11 | 11011 1B 27 | 101011 2B 43 | 111011 3B 59
1100 C 12 | 11100 1C 28 | 101100 2C 44 | 111100 3C 60
1101 D 13 | 11101 1D 29 | 101101 2D 45 | 111101 3D 61
1110 E 14 | 11110 1E 30 | 101110 2E 46 | 111110 3E 62
1111 F 15 | 11111 1F 31 | 101111 2F 47 | 111111 3F 63

The length of a binary number may be referred to as its bit-length.

From binary or hexadecimal to decimal

As seen above the value of the numeral is calculated by multiplying each digit's value in the sequence by its place value, and summing the results.

Base example
2 1101 = 1·8 + 1·4 + 0·2 + 1·1 = 13(10)
16 1101 = 1·4096 + 1·256 + 0·16 + 1·1 = 4353(10)
From decimal to binary or hexadecimal (integer)

One method to to convert a decimal number to binary is repeatedly dividing the decimal number by 2, until it is reduced to zero. Every time we divide the remainder of the division becomes a digit in the binary number. To get the remainder is easy because we are dividing by two: If the dividend is even, the remainder will be 0 and if the dividend is odd the remainder is 1.

Example converting 187(10) to binary:

               remainder direction to read
187 / 2 = 93 1
93 / 2 = 46 1
46 / 2 = 23 0
23 / 2 = 11 1
11 / 2 = 5 1
5 / 2 = 2 1
2 / 2 = 1 0
1 / 2 = 0 1

187(10) = 10111011(2)

By grouping 4 bits we get the hexadecimal number: 1011 1011(2) = BB(16) = 11·16 + 11 = 187(10).

The same dividing method used for binary numbers can be used to convert decimal numbers to hexadecimal numbers:

Example converting 2019(10) to hexadezimal:

               remainder direction to read
2019 / 16 = 126 3
126 / 16 = 7 14
7 / 16 = 0 7

2019(10) = 7E3(16)

"Just do it" NC3:

Calculating in binary and hexadecimal

Binary addition

Summand + summand = sum (addend + addend = sum).

Rules:

S1 + S2 = Sum Carry (C)
0 + 0 = 0 0
0 + 1 = 1 0
1 + 0 = 1 0
1 + 1 = 0 1


S1 + S2 + Ci = Sum Carry (C)
1 + 1 + 1 = 1 1

Ci stands for Carry in.

Examples:

 1011(2) +     control in decimal:   11(10) +
10011(2) 19(10)
  11 C 1 C
----- --
11110(2) 30(10)


  1111(2) +   control in decimal:   15(10) +
  1111(2) 15(10)
 10101(2) 21(10)
 1
 1 11 C 1 C
------ --
110011(2) 51(10)

To build a binary adder we need a digital adder circuit. We will see such a circuit in the next chapter.

Binary subtraction (direct)

Minuend-subtrahend = difference.

Rules:

M - S = Difference Borrower (B)
0 - 0 = 0 0
0 - 1 = 1 1
1 - 0 = 1 0
1 - 1 = 0 0


M - S1 - S2 = Difference Borrower (B)
0 - 1 - 1 = 0 1
1 - 1 - 1 = 1 1

Examples:

11101(2) -           control in decimal:   29(10) -
 1010(2) 10(10)
  1 B B
----- --
10011(2) 19(10)


111000111(2) -   control in decimal:   455(10) -
101001011(2) 331(10)
 1111 B B
------ --
001111100(2) 124(10)
Binary subtraction with two's complement

One drawback of direct subtraction is that borrowing frequently over many positions is tedious and confusing. As our processor has only an digital adders it's by ways more convenient to use a method doing the subtraction by adding the b's (base) complement of the subtrahend (this can be done in all positional numeral systems: e.g tenth complement in a decimal system).

To get the complement the easiest way is often to get b-1-complement and add the number one afterwards. This gets very easy in a binary system because we get the b-1-complement (one's complement) by inverting the subtrahend. To get the two's complement we add the number one.

Examples:

Subtrahend One's complement Two's complement
11101(2) 00010(2) + 1 = 00011(2)
0000100(2) 1111011(2) + 1 = 1111100(2)

Another advantage of the complements method is that we the minuend can be lesser than the subtrahend which is not possible in the direct method.

Rules:

Examples:

We use the same examples as in the direct subtraction:

29(10) - 10(10) = 19(10)

        stuffed 1's complement 2's complement
  11101(2) -
   1010(2)     01010(2) 10101(2) 10110(2)
 
  11101(2) +
  10110(2)
1 11 C positive
  -----
  10011(2)

455(10) - 331(10) = 124(10)

        1's complement 2's complement
  111000111(2) -
  101001011(2)   010110100(2)  010110101(2)
 
  111000111(2) +
  010110101(2)
1 11   111 C positive
  ---------
  001111100(2)

In both examples a carry occurred, so the results are positive. Let' try the last example by changing minuend and subtrahend, so that the minuend is lesser:

331(10) - 455(10) = -124(10)

        1's complement 2's complement
  101001011(2) -
  111000111(2)   000111000(2)  000111001(2)
 
  101001011(2) +
  000111001(2)
0  1111 11 C negative
  ---------
  110000100(2)

No carry, so the result is negative. We get the absolute value with the two's complement of the result:

Result:            110000100(2)
One's complement:  001111011(2)
Two's complement:  001111100(2) = 124(10)

To build a binary subtraction circuit we need the digital adder from above and an inverter circuit.

Binary multiplication

Multiplicand·multiplier = product (factor·factor = product).

Rules:

Md · Mr = Product
0 · 0 = 0
0 · 1 = 0
1 · 0 = 0
1 · 1 = 1

This is the truth table of an AND gate. The AND gate us a one bit multiplier circuit.

Example:

    11011(2) ·      control in decimal:    27(10) ·
     1101(2)  13(10)
--------- ---
    11011  81
   00000 27
  11011 1 C
 11011 ---
11111 C 351(10)
---------
101011111(2)

To build a binary multiplier we need AND gates, shift registers and adder circuits.

Binary division

Dividend / by divisor = quotient (numerator / denominator = quotient).

The division is a successive subtraction.

Rules:

Example:

Let's do the following division: 1011101,11(2) / 110(2). The two's complement of the divisor is 001(2)+1 = 010(2). If we do a subtraction with 3 positions and 1001(2)+1 = 1010(2) if we do a subtraction with 4 positions:

 1011101,110(2)/ 110(2) = 1111,101(2)   decimal control: 93,75(10) / 6(10) = 15,625(10)
 010|||| ||| 6
 ---|||| ||| ---
0111|||| ||| C no carry n+1: discard 0 ⇩ 33
 1011||| ||| 30
 1010||| ||| --
1 1  ||| ||| C carry n+1: positive 1 ⇩  3 7
 ----||| |||  3 6
 01011|| |||  ---
  1010|| |||    15
 1 1  || ||| C carry n+1: positive 1 ⇩    12
  ----|| |||    --
  01010| |||     30
   1010| |||     30
  1 1  | ||| C carry n+1: positive 1 ⇩     --
   ----| |||      0
   01001 |||
    1010 |||
   1     ||| C carry n+1: positive 1 ⇩
    ---- |||
    0011 1||
     101 0||
    111   || C carry n+1: positive 1 ⇩
     -----||
     000 11|
      10 10|
     0 1   | C no carry n+1: discard 0 ⇩
      -----|
     011 01|
       0 110
       1 010
      11 1 C carry n+1: positive 1 ⇩
       -----
       0 000

To do a binary division we need shift registers, adders ts and inverters.

Basic arithmetic operations in hexadecimal

The rules are the same as in decimal calculations. Here two examples:

Addition

 ABC(16) +     control in decimal:   2748(10) +
 983(16) 2435(10)
11 C 1 1 C
----- ----
143F(16) 5183(10)


Subtraction

ABC(16) -           control in decimal:   2748(10) -
9F3(16) 2547(10)
1 B B
--- ----
0C9(16)  201(10)


Multiplication and Division

As child we learn the decimal multiplication table by heart. To facilitate the hex multiplication you can use a table for hexadecimal multiplication. The division also works as for decimal numbers.

It's easier to do the calculations in binary and convert the results in hexadecimal numbers.

Conclusion

To do the four basic arithmetic operations we need:

  1. Inverter
  2. Adder
  3. AND-gates
  4. Shift registers

All these circuits are implemented in the Arithmetic Logic Unit (ALU) of modern processors and controllers.

"Just do it" NC4:

Nibble, Byte and Word

Nibble

Digital circuits, processors and micro-controller are often built to work with a fixed length of bits. First processors worked with 4 bits called a nibble, half a byte and big enough to hold a Binary-Coded Decimal (BCD, digits 0-9). A nibble is also one digit in hexadecimal!

Byte

The byte was historically the number of bits used to encode a single character of text in a computer. Often it was also the smallest addressable unit of memory in many computer architectures. As no standards existed until 1993, the byte could have different length (e.g. six bit or 9 bit).

The international standard IEC 80000-13 defined as unit symbol for the byte the upper-case character B. A byte has 8 bit permitting the values 0 through 255 (28 = 256).

Byte


A multiple of the unit byte is not the kilobyte, because kilo is defined as thausend (1000 decimal) and does not fit in the binary system. For for quantities of digital information, the binary prefix Ki(kibi) (bi for binary!) was defined and is part of part of the International System of Quantities.

Kibi means 210, or 1024. One kibibyte is 1024 bytes and the unit symbol for the kibibyte is KiB. Other binary prefixes are mebi, gibi and tebi.

binary prefix unit symbol value difference to decimal in %
kibi KiB 210 = 1024 2.4
mebi MiB 220 = 10242 = 1048576 4.9
gibi GiB 230 = 10243 = 1073741824 7.4
tebi TiB 240 = 10244 = 1099511627776 10
pebi PiB 250 = 10245 = 1125899906842624 12.6
exbi EiB 260 = 10246 = 1152921504606846976 15.3


Word

The word is the piece of data used by by the instruction set respectively the hardware of a processor. The bit-length of the word (word length) is an important characteristic of the used processor or computer architecture.

The word size varies in modern processors and micro-controllers from 8 to 64 bits (steps of 8 bits).

Leading zeros

As we often use fixed length (mostly multiple of 1 byte) to store data in computers, leading zeros are necessary for numbers with less bits. If we per example one byte, the number 101(2) (5(10)) will be written 00000101(2).

Leading zeros are not required but they do help by showing the the bit-length of the number.

Negative numbers

Negative numbers are created with the two’s complement (see later). The highest bit ("sign-bit"), is needed to flag the number as a negative number. To get the 2’s complement, the rest of the bits are inverted and 1 is added. As the highest bit is no longer available, the maximal positive number we can get with a byte is only 127.

bytes range unsigned range signed
1 0 to 255 (28-1) -128 to 127
2 0 to 65535 (216-1) -32768 to 32767
4 0 to 4294967295 (232-1) -2147483648 to 2147483647
"Just do it" NC5:

Binary in programming

As seen binary is what drives all electronics. The electronic circuit knows only two states. An important point is the storing of the states respectively our information. In the image above the information (one byte) is stored mechanically in our switches. In computers we need a circuit capable of storing the information. This can be done e.g. with one transistor and one capacitor for volatile dynamic random-access memory (DRAM) or with more transistors to build memory cells with bistable flip-flops in static random-access memory (SRAM). This will be a chapter of it's own in this course :).

Our circuits memorize states. The interpretation of this states is arbitrary. Let's look at an example. We take a double register of our Arduino (ATmega328), filled with the following ones and zeros:

1011101001100010(2)

"Just do it" NC6:

We see that the same bits can mean a number, even in different numeral systems, or character(s) or an instruction of our programming language:

The flash of the micro-controller used for Arduino Uno (where the program is stored) is organized in words of 16 bit. If we put the same ones and zeros in the flash, they will be interpreted as one command line of a program written in machine code. In assembly language this would be the command out PORTD,r6, to copy the byte of register six to the eight pins of port D.

machine code


The assignment of a unique sequence of bits to the representation of each one of a set of numbers, letters or instructions is called a binary code (see next chapter).

Data types in Arduino

While programming, it is important to use the right data type! A wrong data type can give erratic results. It's very difficult to find these errors, because they can show up only at certain numbers. So think twice before using a data type. All data needs to be stored, and the memory of micro-controller is very limited. Also the selection of how to store data makes an impact on performance. So select always the smallest variable that is large enough, and make the datatype unsigned where suited. To hold e.g. a digital pin number in Arduino (0-54) an int (-32768 to 32767) is overkill. A byte is largely enough (0-255).

The reference is here.


Table with byte-length of data types

data type   Uno (T2.0) Teensy 3.6 ESP8266 ESP32
unsigned whole numbers:
  bool 1 1 1 1
  byte 1 1 1 1
  uint16_t 2 2 2 2
  word 2 2 4 4
  unsigned int 2 4 4 4
  unsigned long 4 4 4 4
  unsigned long long 8 8 8 8
signed whole numbers:
  char 1 1 1 1
  short 2 2 2 2
  int 2 4 4 4
  long 4 4 4 4
  long long 8 8 8 8
floating point numbers:
  float 4 4 4 4
  double 4 8 8 8
special:
  size_t 2 4 4 4
Constants and variables in Arduino

Our data is normally stored in variables, because we mostly want to work with the data, and so change this data. In our micro-controller all bytes in the random access memory (RAM) are variables (Arduino Uno 2 kibibyte).

Variables are values that are changing or have the ability to change.

Constants are values which remain unchanged (read-only).

Our read-only memory (ROM, flash) is much bigger (Arduino Uno 32 kibibyte). So if we use constants, it is a good practice to add the const keyword to our data type. The compiler will store this data in the flash, saving memory in the SRAM.

In Arduino (C) we must always assign a data type to a variable ore a constant. Her an example with a variable and a constant:

    const float pi = 3.1415;
    float circ, r;

    void setup() {
      Serial.begin(115200);
      r = 11;
      circ = 2*pi*r;
      Serial.println(circ);
    }

    void loop() {}

In our sketch we use also the number 2 as a constant. We can use integer constants or floating point constants.

Without further indications the number is treated as an integer. To make it a floating point constant we have to add a decimal point:

    circ = 2.0*pi*r;

If we want to specify an integer constant with another data type we add a u or U to force the constant into an unsigned data format, a l or L to force the constant into a long data format, or both for an unsigned long constant.

    a = 33u+v;
    b = 33L*w;
    c = 100000000UL/d;

If we want to add values in other bases than the base 10, we use the special formatters 0b for binary and 0x for hexadecimal (B like in the reference is not convenient, because of its limitation to one byte, we use the C++ variant "0b" instead):

    int bina = 0b11011101;
    uint16_t hexa = 0xFAC3;

If we need to convert one data type to another (also known as cast) we get 6 different functions to do this: byte(),char(),float(),int(),long() and word().

Converting with Serial.print()

Often we use the serial terminal for debugging or for showing results if no display is available. The function Serial.print() has the ability to convert a number with a second parameter to another base. Try the following program:

    int decNumber = 1458;
    int negDecNumber = -1458;

    void setup() {
      Serial.begin(115200);
      delay(500);
      Serial.println(decNumber,BIN);
      Serial.println(negDecNumber,BIN);
      Serial.println(decNumber,HEX);
      Serial.println(negDecNumber,HEX);
    }

    void loop() {}
"Just do it" NC7:
"Just do it" NC8:
    ------------ ADDITIONS -------------------
    37 + 425 = 462
    25 + 1A9 = 1CE
    100101 + 110101001 = 111001110
    ------------ SUBTRACTIONS ----------------
    4477 - 425 = 4052
    117D - 1A9 = FD4
    1000101111101 - 110101001 = 111111010100
    ------------ MULTIPLICATIONS -------------
    121 · 425 = 51425
    79 · 1A9 = C8E1
    1111001 · 110101001 = 1100100011100001
    ------------ DIVISIONS -------------------
    4477 / 37 = 121
    117D / 25 = 79
    1000101111101 / 100101 = 1111001

Binary codes (wiki)

The assignment of a unique sequence of bits to the representation of each one of a set of numbers, letters or instructions is called a binary code.

Even the assignment of bits to the binary numeral system is a code. A binary-code working with 8 bit can represent the binary numbers from 0-255. It can also represent 256 different characters or 256 different machine code instructions.

BCD-code (wiki)

An often needed code, that is close family with the binary numeral system is the BCD-code. BCD stands for Binary Coded Decimals.

Decimal-
number
23
8
22
4
21
2
20
1
0 0 0 0 0
1 0 0 0 1
2 0 0 1 0
3 0 0 1 1
4 0 1 0 0
5 0 1 0 1
6 0 1 1 0
7 0 1 1 1
8 1 0 0 0
9 1 0 0 1
  1 0 1 0
  1 0 1 1
  1 1 0 0
  1 1 0 1
  1 1 1 0
  1 1 1 1

One decimal number is represented with a nibble (4 bit) also known as tetrade. From the 16 tetrades only 10 are used. The don't care-states (unused tetrades) are named pseudo-tetrad(e)s.

Addition in BCD

To show that working with codes is not always straightforward here as example the addition in BCD.

For results less than 10(10) we have no problems. If the result is bigger, we have to deal with carry or a pseudo-tertade. In both cases a correction with the number 6(10) is needed.

Examples

0001 1000(BCD) +   control in decimal:   18(10) +
0101 0100(BCD) 54(10)
  1 C 1 C
---- ---- --
0110 1100 + 72(10)
     0110 correction! (pseudo-tetrade)
   1 1 C
---- ----
0111 0010(BCD)


0001 1000(BCD) +   control in decimal:   18(10) +
0011 1000(BCD) 38(10)
 111 C 1 C
---- ---- --
0101 0000 + 56(10)
     0110 correction! (carry)              
  C
---- ----
0101 0110(BCD)


Other Tetrade-Codes are e.g. the Gray-code, the Excess-3-code or the Aiken-code.

ASCII (wiki)

A very important (and old :)) code, because it is used to represent text in computers, telecommunications equipment, and other devices is the American Standard Code for Information Interchange ASCII.

Most modern character-encoding Codes support many additional characters, but support the basic set of 128 ASCII characters (7 Bit).

Interesting are the first 32 codes. These were used for control characters to control devices (such as teletype machines or printers).

Two important control characters used even today are line feed (10(10), 0x0A, LF, '\n'),and carriage return (13(10), 0x0D, CR, '\r').

In the physical media of typewriters and printers, line feed meant "down" and carriage return "across". (two axes of motion).

Line feed, also named newline is used to signify the end of a line of text and the start of a new one (↵ Enter key on the keyboard). But carriage return can mean the same in software, so newline in character encoding can be defined as LF or CR or LF and CR combined into one (commonly called CR+LF or CRLF).

Linux and Unix-like systems use LF as newline. Windows and OS/2 (Apple) use CRLF (older systems like Apple II or Commodore C64 used CR only).

ASCII-Table

Dec. Oct. Hex Binary Value   Dec. Oct. Hex Binary Value
000 000 000 00000000 NUL (Null char.) 064 100 040 01000000 @
001 001 001 00000001 SOH (Start of Header) 065 101 041 01000001 A
002 002 002 00000010 STX (Start of Text) 066 102 042 01000010 B
003 003 003 00000011 ETX (End of Text) 067 103 043 01000011 C
004 004 004 00000100 EOT (End of Transmission) 068 104 044 01000100 D
005 005 005 00000101 ENQ (Enquiry) 069 105 045 01000101 E
006 006 006 00000110 ACK (Acknowledgment) 070 106 046 01000110 F
007 007 007 00000111 BEL (Bell) 071 107 047 01000111 G
008 010 008 00001000 BS (Backspace) 072 110 048 01001000 H
009 011 009 00001001 HT (Horizontal Tab) 073 111 049 01001001 I
010 012 00A 00001010 LF (Line Feed) 074 112 04A 01001010 J
011 013 00B 00001011 VT (Vertical Tab) 075 113 04B 01001011 K
012 014 00C 00001100 FF (Form Feed) 076 114 04C 01001100 L
013 015 00D 00001101 CR (Carriage Return) 077 115 04D 01001101 M
014 016 00E 00001110 SO (Shift Out) 078 116 04E 01001110 N
015 017 00F 00001111 SI (Shift In) 079 117 04F 01001111 O
016 020 010 00010000 DLE (Data Link Escape) 080 120 050 01010000 P
017 021 011 00010001 DC1 (XON) (Device Control 1) 081 121 051 01010001 Q
018 022 012 00010010 DC2 (Device Control 2) 082 122 052 01010010 R
019 023 013 00010011 DC3 (XOFF)(Device Control 3) 083 123 053 01010011 S
020 024 014 00010100 DC4 (Device Control 4) 084 124 054 01010100 T
021 025 015 00010101 NAK (Negative Acknowledgement) 085 125 055 01010101 U
022 026 016 00010110 SYN (Synchronous Idle) 086 126 056 01010110 V
023 027 017 00010111 ETB (End of Trans. Block) 087 127 057 01010111 W
024 030 018 00011000 CAN (Cancel) 088 130 058 01011000 X
025 031 019 00011001 EM (End of Medium) 089 131 059 01011001 Y
026 032 01A 00011010 SUB (Substitute) 090 132 05A 01011010 Z
027 033 01B 00011011 ESC (Escape) 091 133 05B 01011011 [
028 034 01C 00011100 FS (File Separator) 092 134 05C 01011100 \
029 035 01D 00011101 GS (Group Separator) 093 135 05D 01011101 ]
030 036 01E 00011110 RS (Req to Send)(Rec Sep) 094 136 05E 01011110 ^
031 037 01F 00011111 US (Unit Separator) 095 137 05F 01011111 _
032 040 020 00100000 SP (Space) 096 140 060 01100000 `
033 041 021 00100001 ! 097 141 061 01100001 a
034 042 022 00100010 " 098 142 062 01100010 b
035 043 023 00100011 # 099 143 063 01100011 c
036 044 024 00100100 $ 100 144 064 01100100 d
037 045 025 00100101 % 101 145 065 01100101 e
038 046 026 00100110 & 102 146 066 01100110 f
039 047 027 00100111 ' 103 147 067 01100111 g
040 050 028 00101000 ( 104 150 068 01101000 h
041 051 029 00101001 ) 105 151 069 01101001 i
042 052 02A 00101010 * 106 152 06A 01101010 j
043 053 02B 00101011 + 107 153 06B 01101011 k
044 054 02C 00101100 , 108 154 06C 01101100 l
045 055 02D 00101101 - 109 155 06D 01101101 m
046 056 02E 00101110 . 110 156 06E 01101110 n
047 057 02F 00101111 / 111 157 06F 01101111 o
048 060 030 00110000 0 112 160 070 01110000 p
049 061 031 00110001 1 113 161 071 01110001 q
050 062 032 00110010 2 114 162 072 01110010 r
051 063 033 00110011 3 115 163 073 01110011 s
052 064 034 00110100 4 116 164 074 01110100 t
053 065 035 00110101 5 117 165 075 01110101 u
054 066 036 00110110 6 118 166 076 01110110 v
055 067 037 00110111 7 119 167 077 01110111 w
056 070 038 00111000 8 120 170 078 01111000 x
057 071 039 00111001 9 121 171 079 01111001 y
058 072 03A 00111010 : 122 172 07A 01111010 z
059 073 03B 00111011 ; 123 173 07B 01111011 {
060 074 03C 00111100 < 124 174 07C 01111100
061 075 03D 00111101 = 125 175 07D 01111101 }
062 076 03E 00111110 > 126 176 07E 01111110 ~
063 077 03F 00111111 ? 127 177 07F 01111111 Nul

Escape sequences

An escape sequence is a combination of characters which represents no text. They are supposed to be intercepted by the program and perform a special function. In the C resp. C++ (Arduino) languages, it's a series of 2 or more characters, starting with a backslash (\). They can be used directly in a string. Important escape characters:

Special character Escape sequence
line feed \n
carriage return \r
tabulator \t

Example:

    Serial.print("Hello\tWorld\n");

Seven-segment display (wiki)

Even with always better and cheaper dot matrix displays, seven-segment displays (SSD's) with LED's are a cheap and effective possibility for displaying decimal numerals even in the dark.

A single byte can encode the full state of a 7-segment-display including a dot (8 LED's).

7-seg labeled


"Just do it" NC9:
    Bits of PortB: 7 6 5 4 3 2 1 0
    Segment: dp g f e d c b a Byte:
    7-seg 0 0 1 1 1 1 1 1 0x3F
    7-seg 0
    7-seg 0
    7-seg 0
    7-seg 0
    7-seg 0
    7-seg 0
    7-seg 0
    7-seg 0
    7-seg 0
    7-seg 0
    7-seg 0
    7-seg 0
    7-seg 0
    7-seg 0
    7-seg 0
"Just do it" NC10:

Interesting links: