Lecture 02
Lecture 02
Lecture 02
com/zybook/Ak78Fta73x/chapter/2/print
% of embedded programmers
Language with current project mostly in this language
(2013)
C 56%
C++ 23%
Assembly 4%
Java 2%
C# 1%
MATLAB/LabView 3%
Python 6%
Other 5%
C was not originally created for embedded systems, but rather was created in 1972 for mainframe
and desktop computers, which typically manipulate data in Xles such as integer or character data.
In contrast, embedded systems commonly manipulate bits, in addition to other
©zyBooks data 09:32
02/12/24 types.231883
This
chapter describes C's built-in data types and discusses how to manipulateLawrence
bits in C.Nderu
Most of the
Ak78Fta73x
discussion applies equally to the C++ language, which added object-oriented concepts, especially
useful in large programming projects .
The table below shows programming language usage for all types of systems. One sees that C and
its variants still dominate. Many people argue that, because C does not hide many low-level details
from a programmer, mastering C makes for a stronger programmer in any language.
1 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
"Popularity rating"
Language (see below source for
deXnition) ©zyBooks 02/12/24 09:32 231883
Lawrence Nderu
Java 15% Ak78Fta73x
C 7%
C++ 6%
Python 4%
Visual Basic
3%
.NET
Javascript 3%
PHP 3%
Perl 2%
PARTICIPATION
ACTIVITY 2.1.1: Embedded systems.
2 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
Exploring further:
• Wikipedia: C language
• Wikipedia: C++ language
• cplusplus.com: Excellent resource for C++ as well as C
• cprogramming.com
signed char to
©zyBooks 02/12/24 09:32 231883
unsigned char to
Lawrence Nderu
Ak78Fta73x
signed short to
is
unsigned short to
3 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
Though
to
signed int N commonly used,
we avoid these
due to unde5ned
unsigned int N to ©zyBooks 02/12/24 09:32 231883
width
Lawrence Nderu
Ak78Fta73x
*The size of integer numeric data types can vary between compilers, for reasons beyond our scope. The following table
lists the sizes for numeric integer data types used in this material along with the minimum size for those data types
deXned by the language standard.
Thus, a variable whose value may only range from 0 to 100 might be best declared as: unsigned
char. A variable whose value may only range from -999 to 999 might be best declared as: signed
short. Using the smallest possible data type ensures that the limited space in typical embedded
system designs is conserved. Note that unsigned data types represent positive integers, while
signed types represent negative and positive integers. If a variable is used to represent a series of
bits (rather than a number), then an unsigned type should be used.
PARTICIPATION
ACTIVITY 2.2.1: In embedded systems, the smallest data type is preferred.
1. Variable numA is declared as an unsigned char, so requires only 1 byte in memory. Variable
numB is declared as an unsigned long, so requires 4 bytes in memory.
2. Adding 1 to numA is a 1 byte operation.
3. Adding 1 to numB is a 4 byte operation, which requires moving each byte to the ALU,
performing the add operation, then moving the bytes back to memory.
4 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
Embedded systems commonly deal with 1-bit data items. C does not have a 1-bit data type, which
is unfortunate. Thus, 1-bit items are typically represented using an unsigned char, as in: unsigned
char myBitVar. The programmer only assigns the variable with either 0 or 1, e.g., myBitVar = 1, even
though the variable could be assigned integers up to 255. Checking whether such a variable is 1 or
0 is typically done without explicit comparison to 1 or 0, and is instead done as if (myBitVar)
or as if (!myBitVar).
©zyBooks 02/12/24 09:32 231883
Lawrence Nderu
Below are some example variable declarations:
Ak78Fta73x
C also has types like 5oat and double for joating-point numbers like or . Many
embedded programmers avoid using joating-point types. The reason is that microcontrollers
commonly lack joating-point hardware, to stay small, low cost, and low power. Thus, joating-point
arithmetic must be done in software, requiring hundreds of instructions even for operations like
addition or multiplication. Thus, we omit further discussion from this section.
A common practice is to name variables with a lower-case preXx indicating the data type, as above
— uc, us, and ul for unsigned char, short, and long; sc, ss, and sl for signed char, short, and long; or b
for bit — to help ensure that larger constants or variables aren't assigned to smaller variables. This
book often skips variable naming practices, for the clarity of short examples. Programmers of
larger projects should consider using such a variable naming convention.
char is called such because it is commonly used in desktop programming to represent the integer
value of an 8-bit ASCII character. Note however that char is actually an integer. Also, 8-bits is
sometimes called a byte.
In C, the word "signed" is optional for a signed type, so "char I1;" is the same as
"signed char I1;". However, for program clarity, we avoid that shortcut. Also, the word "int"
©zyBooks 02/12/24 09:32 231883
may follow the words short or long, but that word is superjuous so we usually omit it.
Lawrence Nderu
Ak78Fta73x
Unfortunately, although the above widths are quite common, C actually deXnes the above widths as
minimum widths, so a compiler could for example create a long as 64 bits. Thus, a programmer
should never assume an exact width, e.g., a program should not increment an "unsigned char" and
expect it to roll over from 255 to 0, because the char could be 16 bits. Another unfortunate fact is
that C allows a variable to be declared merely as type "int", where the width is compiler dependent.
Due to the unpredictability of int, we avoid using the int type almost entirely. Following these
5 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
conventions improves code portability, which is the ability to recompile code for a different
microprocessor without undesirable changes in program behavior.
The underlying representation of each data type is binary. For an 8-bit unsigned char ucI1:
In binary, the rightmost bit has weight 20, the next bit 21, then 22, etc. In other words, from left to
right, the 8 bits have weights 128, 64, 32, 16, 8, 4, 2, and 1. 0001100 is thus 8 + 4 = 12.
PARTICIPATION
ACTIVITY 2.2.2: Binary number tool.
0
128 64 32 16 8 4 2 1 (decimal value)
2⁷ 2⁶ 2⁵ 2⁴ 2³ 2² 2¹ 2⁰
Signed data types in C use two's complement representation. At the bit level, a variable of type char
set to 127 would have an internal representation of 01111111, while -128 would be 10000000, and
-1 would be 11111111. For the curious reader -- the binary representation of a negative number in
two's complement can be obtained by representing the number's magnitude in binary,
complementing all the bits, and adding 1. For example, -1 is 00000001 (magnitude is 1) -->
11111110 (complement all bits) --> 11111110+1 = 11111111 (add 1). -128 is 10000000 (magnitude
is 128) --> 011111111 (complement all bits) --> 011111111+1 = 100000000 (add 1). Note that the
©zyBooks
eighth bit will always be 1 for a negative 8-bit number and is thus called the 02/12/24 09:32 231883
sign bit. The
Lawrence Nderu
programmer generally need not deal directly with the binary representationsAk78Fta73x
of signed numbers,
because compilers/assemblers automatically create the proper constants (e.g., myVar = -1;
would store the two's complement representation in myVar). However, knowing whether an item is
signed or unsigned is important when assigning values, and for determining a variable's range.
(Wikipedia: Two's complement)
The following provides some examples of choosing an appropriate data type for a variable based
on the variable's intended purpose:
6 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
Store an
airplane's unsigned short speed; // Not < 0, not > 64k
speed
Store the
remaining
joules of unsigned long energy; // Not < 0, not > 4Gig
energy in a
car battery
Store feet of
elevation
above/below signed short elevation; // Could be < 0, not > 32K or < -32K.
sea level of
land
PARTICIPATION
ACTIVITY 2.2.3: C data types.
7 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
8 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
RIMS implicitly deXnes two additional global variables: A represents the 8-bit input as a decimal
number, and B the 8-bit output:
9 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
enabling access to I/O pins as well as other microcontroller resources. In the case of RIMS, the
RIMS.h Xle contains the deXnitions of A and B, and a programmer can enable their use via the line
#include "RIMS.h" at the top of a program.
Built-in grouped bits like A and B enable the programmer to treat RIMS' 8 input bits or 8 output bits
as an 8-bit decimal number. For example, A might represent a number like 12 coming from a
temperature sensor as an 8-bit binary number 00001100. The programmer might use A in an
arithmetic comparison as in A > 15, or an arithmetic calculation as©zyBooks 02/12/24 09:32 231883
in newTemp = A + 5. The
Lawrence Nderu
programmer can write a number to B, as in B = 15, which will cause 00001111 to appear on RIMS'
Ak78Fta73x
eight output pins. Setting RIM's outputs to RIM's inputs is achieved with just: B = A.
Because B is a global variable, a program can write as well as read that variable. However, input A is
automatically written by the microcontroller and should never be written by a program, only read. In
RIMS, writing to A results in a runtime error, causing execution to terminate. Caution:
Microcontrollers differ in how they treat input and output variables, especially because most
microcontrollers allow each pin to be conXgured as either input or output, so programmers should
read a microcontroller's datasheet or other instructions carefully.
PARTICIPATION
ACTIVITY 2.3.1: RIMS B outputs and A inputs. Full screen
Write a C program in RIMS that repeatedly executes: B = 7. Note that outputs B2, B1, and
B0 become 1s, because 7 is 00000111 in binary (note that 7 appears under the output
pins in RIMS). Next, set the input switches such that A3=1, A2=0, A1=0, and A0=1, with the
other inputs 0, and note that 9 appears below RIMS' input pins (because 00001001 is 9 in
binary).
A0 0
A1 0
A2 0
A3 0
A4 0
A7 0
A = 0
10 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
1 #include "RIMS.h"
2
3 void main() {
4
5 }
B0 0
B1 0
B2 0
B3 0
B4 0
B5 0
B6 0
B7 0
B = 0
Compile
PARTICIPATION
11 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
ACTIVITY
2.3.2: Set RIMS B output to A plus 1. Full screen
Write and execute a C program for RIMS that sets B equal to A plus 1.
A0 0
A1 0
©zyBooks 02/12/24 09:32 231883
A2 0 Lawrence Nderu
Ak78Fta73x
A3 0
A4 0
A5 0
A6 0
A7 0
A = 0
1 #include "RIMS.h"
2
3 void main() {
4
5 }
12 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
B0 0
B1 0
B2 0
B3 0
©zyBooks 02/12/24 09:32 231883
B4 0 Lawrence Nderu
Ak78Fta73x
B5 0
B6 0
B7 0
B = 0
Compile
PARTICIPATION
ACTIVITY 2.3.3: RIMS B output overjow. Full screen
Write a C program for RIMS that sets B = 300. Note that the value actually output on B is
not 300, because an unsigned char has a range of 0 to 255.
A0 0
A1 0
A2 0
A3 0
A4 0
A5 0
©zyBooks 02/12/24 09:32 231883
Lawrence Nderu
A6 0 Ak78Fta73x
A7 0
A = 0
13 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
1 #include "RIMS.h"
2
3 void main() {
4
5 }
B0 0
B1 0
B2 0
B3 0
B4 0
B5 0
B6 0
B7 0
B = 0
Compile
14 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
The keyword const, short for constant, can precede any variable declaration, as in:
const unsigned char i1 = 5;. A constant variable's value cannot be changed by later code,
and thus can help to prevent the introduction of future errors. A constant variable must therefore be
initialized when declared. Above, 5 is a constant, and i1 is a constant variable.
PARTICIPATION
ACTIVITY 2.3.4: Airbag system. Full screen
©zyBooks 02/12/24 09:32 231883
Lawrence Nderu
A car has a sensor that sets A to the passenger's weight (e.g., if the passenger weighs 130
Ak78Fta73x
pounds, A7..A0 will equal 10000010). Write a RIM C program that enables the car's airbag
system (B0=1) if the passenger's weight is 105 pounds or greater. Also, illuminate an
"Airbag off" light (by setting B1=1) if weight > 5 pounds but weight < 105 pounds.
A0 0
A1 0
A2 0
A3 0
A4 0
A5 0
A6 0
A7 0
A = 0
1 #include "RIMS.h"
2
3 void main() {
4
5 }
15 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
B0 0
B1 0
B2 0
B3 0
©zyBooks 02/12/24 09:32 231883
B4 0 Lawrence Nderu
Ak78Fta73x
B5 0
B6 0
B7 0
B = 0
Compile
0.00 s
PARTICIPATION
ACTIVITY 2.3.5: RIMS inputs and outputs.
Write a statement, ending with a semicolon, for each desired behavior (unless otherwise
speciXed). Write "not possible" if appropriate.
16 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
5) Set A7 to 1.
17 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
2.4 Hexadecimal
Commonly an 8-bit unsigned item is not used as a number but rather just as eight distinct bits. For
example, if RIM's eight outputs connected to eight light bulbs and the programmer wanted to light
all the bulbs, the programmer could write B = 255 (because 255 is 11111111 in binary). However,
255 does not directly convey the programmer's intent. Ideally, the ©zyBooks
programmer could09:32
02/12/24 write231883
B=
b11111111 or something similar, but C unfortunately has no binary constant support.
Lawrence But
Nderu
Ak78Fta73x
fortunately, C does support hexadecimal constants, which are closer to the ideal.
Hexadecimal, or hex, is a base 16 number, where each digit can have the value of 0, 1, ..., 8, 9, A, B,
C, D, E, or F. A is ten, B is eleven, C is twelve, D is thirteen, E is fourteen, and F is Xfteen.
Hexadecimal Binary
0 0000
1 0001
2 0010
3 0011
4 0100
5 0101
6 0110
7 0111
8 1000
9 1001
©zyBooks 02/12/24 09:32 231883
A 1010 Lawrence Nderu
Ak78Fta73x
B 1011
C 1100
D 1101
18 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
E 1110
F 1111
CHALLENGE
ACTIVITY 2.4.1: Binary and hex tool.
©zyBooks 02/12/24 09:32 231883
90524.463766.qx3zqy7 Lawrence Nderu
Ak78Fta73x
Start
1 2 3 4
Thus, B = 0xFF sets all RIM outputs to 1s. The intent of 0xFF is clearer than 255. Likewise, B = 0xAA
sets the outputs to 10101010, having much clearer intent than B = 170. Good practice is to always
19 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
use hex rather than decimal when intending to write a bit pattern, even when the bit-pattern
equivalent for a decimal number is known, to make the programming intent clear and prevent
future confusion. For example, if intending to set all outputs to 0s, use B = 0x00 rather than B = 0.
Likewise, to set B0 to 1 and B1-B7 to 0s, use B = 0x01 rather than B = 1.
PARTICIPATION
ACTIVITY 2.4.1: Hex represents desired bit patterns more directly than decimal.
©zyBooks 02/12/24 09:32 231883
Lawrence Nderu
Ak78Fta73x
binary 0 0 1 1 1 1 1 0 (62)
2 2 2 2 2 2 2 2 Desired bits
128 64 32 16 8 4 2 1 11110000
Range: 0-15 Range: 0-15
0000-1111 0000-1111
B = 240
hex 3 E (62) 240 doesn't convey
desired bits
16 16
16 1
1111 0000
Range: 0-15 Range: 0-15
0-F 0-F F 0
Animation captions:
1. Four bits have 16 possible combinations and can represent a range from 0 to 15.
2. Given a binary number, every four bits can be represented with one hex digit. 0011 in binary
is represented by the hex digit 3. 1110 in binary is represented by the hex digit E.
3. Setting B = 240 doesn't convey the desired pattern of 11110000. Rather, setting B = 0xF0
better conveys the desire to assign the Xrst four bits to 1 and the latter four bits to 0.
90524.463766.qx3zqy7
Start
20 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
Convert the hex value to binary before the hex value reaches
the binary numbers.
78
©zyBooks 02/12/24 09:32 231883
Lawrence Nderu
Ak78Fta73x
1 2 3 4 5
PARTICIPATION
ACTIVITY 2.4.2: RIMS and hex.
When setting outputs, use a statement ending with a semicolon. Use hex when
appropriate, and use uppercase (0xFF, not 0xff).
21 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
PARTICIPATION
ACTIVITY 2.4.3: RIMS and hex. Full screen
©zyBooks 02/12/24 09:32 231883
Lawrence Nderu
Write a single statement for RIMS that sets B7-B4 to 1s and B3-B0 to 0s, using a hex
Ak78Fta73x
constant.
22 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
A0 0
A1 0
A2 0
A3 0
©zyBooks 02/12/24 09:32 231883
A4 0 Lawrence Nderu
Ak78Fta73x
A5 0
A6 0
A7 0
A = 0
1 #include "RIMS.h"
2 int main() {
3 while(1) {
4 B0 = A0 && A1;
5 }
6 return 0;
7 }
23 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
B0 0
B1 0
B2 0
B3 0
©zyBooks 02/12/24 09:32 231883
B4 0 Lawrence Nderu
Ak78Fta73x
B5 0
B6 0
B7 0
B = 0
PARTICIPATION
ACTIVITY 2.4.4: Using hex again. Full screen
Write a single statement for RIMs that sets B0 to 1 if all eight A inputs are 1s, using a hex
constant.
A0 0
A1 0
A2 0
A3 0
A4 0
A5 0
A6 0
A7 0
©zyBooks 02/12/24 09:32 231883
Lawrence Nderu
A = 0 Ak78Fta73x
24 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
1 #include "RIMS.h"
2 int main() {
3 while(1) {
4 B0 = A0 && A1;
5 }
6 return 0;
7 }
B0 0
B1 0
B2 0
B3 0
B4 0
B5 0
B6 0
B7 0
B = 0
Compile
The following program sets B to 00000000 when A1A0=00, to 01010101 when A1A0=01,
25 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
Consider the following embedded system with a dial that can set A3..A0 to binary 0 to 9,
and a 7-segment display (Wikipedia: 7-Segment Display) connected to B6..B0 as shown:
26 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
#include "RIMS.h"
void main()
{
while (1) {
switch( A )
{
case 0 : B = 0x77; break; // 0111 0111 (0)
case 1 : B = 0x24; break; // 0010 0100 (1)
case 2 : B = 0x5D; break; // 0101 1101 (2)
//... ©zyBooks 02/12/24 09:32 231883
case 9 : B = 0x6F; break; // 0110 1111 (9) Lawrence Nderu
default: B = 0x5B; break; // 0101
PARTICIPATION
1011 (E for Error) Ak78Fta73x
}
ACTIVITY 2.4.5: 7-segment display.
}
}
PARTICIPATION
ACTIVITY 2.4.6: RIMS 7-segment display. Full screen
Complete the above 7-segment display program using hex constants to produce the
correct display for dial settings 3..8.
27 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
A0 0
A1 0
A2 0
A3 0
©zyBooks 02/12/24 09:32 231883
A4 0 Lawrence Nderu
Ak78Fta73x
A5 0
A6 0
A7 0
A = 0
1 #include "RIMS.h"
2 int main() {
3 while(1) {
4 B0 = A0 && A1;
5 }
6 return 0;
7 }
28 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
B0 0
B1 0
B2 0
B3 0
©zyBooks 02/12/24 09:32 231883
B4 0 Lawrence Nderu
Ak78Fta73x
B5 0
B6 0
B7 0
B = 0
PARTICIPATION
ACTIVITY 2.5.1: Bitwise operations.
1 1 1 1
29 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
Animation captions:
1. A bitwise AND results in 1 if both bit-operands are 1s. ©zyBooks 02/12/24 09:32 231883
2. 0x0E bitwise AND 0x18 results in 0x08 because AND is performedLawrence on each Nderu
pair of bits.
Ak78Fta73x
3. A bitwise OR results in 1 if either or both bit-operands are 1s. Ex: 0 or 1 results in 1. 1 or 0
results in 1. 1 or 1 results in 1.
4. 0x0E bitwise OR 0x18 results in 0x1E because OR is performed on each pair of bits.
PARTICIPATION
ACTIVITY 2.5.2: Bitwise operations.
2) 00001111 | 10101010
3) 00001111 ^ 10101010
30 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
Recall that, because C does not have a single-bit data type, a programmer uses an unsigned char to
represent a single-bit data type. Good practice is to only use logical operators with variables
representing single-bit data, even though a bitwise operator may yield the same result. For example,
a programmer should use A0 && A1 rather than A0 & A1, even though both yield the same result if
A0 and A1 can each only be 1 or 0 (e.g., 00000001 && 00000000 and 00000001 & 00000000 both
yield 00000000). However, using a bitwise operator may mislead someone in believing the intent is
to really compare each bit individually, which is not the case. Note, that !A0 yields a different result
than ~A0 (e.g., !00000001 becomes 00000000, while ~00000001 becomes 11111110), illustrating
a danger of being careless with use of logical versus bitwise operators. A common error is to
accidentally type a bitwise operator like | when intending to type a logical operator like ||, and vice-
versa.
CHALLENGE
ACTIVITY 2.5.1: Bitwise operations.
Choose the correct output. Select an output value as bX if the output depends on value of
the bX input bit.
90524.463766.qx3zqy7
Start
b0 b1 b2 b3
©zyBooks 02/12/24 09:32 231883
Lawrence Nderu
& 0 1 0 0 Ak78Fta73x
1 2 3 4 5 6
31 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
Embedded programmers make extensive use of masks. A mask is a constant pattern of 0s and 1s,
as in 0x0F, used with bitwise operators to manipulate a value. For example, setting B to A but with
B1 and B0 forced to 1s is accomplished with B = A | 0x03. Masks are based on the following
bitwise operation features:
©zyBooks 02/12/24 09:32 231883
Lawrence Nderu
• 0 & x yields 0 — Used to set a particular bit to 0. Ak78Fta73x
• 1 | x yields 1 — Used to set a particular bit to 1.
• 0 |x yields x, or 1 & x yields x — Used to pass a bit through unchanged.
Different combinations of masks and bitwise operators achieve speciXc goals. Below are some
examples of using bitwise operators along with RIMS I/O.
The term mask comes from the role of letting some parts through while blocking others, like a
mask someone wears on his face letting the eyes and mouth through while blocking other parts.
As a rule: To set bits to 1, think |; to set bits to 0, think &. Then, to pass bits through, use 0s if you
used |, use 1s if you used &.
CHALLENGE
ACTIVITY 2.5.2: Bit masking tool. ©zyBooks 02/12/24 09:32 231883
Lawrence Nderu
Ak78Fta73x
Select the masking bits to get the correct output.
90524.463766.qx3zqy7
Start
32 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
b0 b1 b2 b3
&
0 0 0 b3
©zyBooks 02/12/24 09:32 231883
Lawrence Nderu
1 2 3 4 5 6
Ak78Fta73x
PARTICIPATION
ACTIVITY 2.5.3: RIMS I/O and bitwise operations.
Given A is 00001111, type the 8-bit result of each operation (e.g., 11110000).
1) A & 0x03
2) A | 0xF0
3) A & 0x18
4) A0 && A1
©zyBooks 02/12/24 09:32 231883
Lawrence Nderu
Ak78Fta73x
Check Show answer
5) A0 & A1
33 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
PARTICIPATION
ACTIVITY 2.5.4: RIMS I/O applications.
34 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
PARTICIPATION
ACTIVITY 2.5.5: Setting RIMS output. Full screen
Write a while loop containing a single C statement for RIMS that sets B to A except that
B1 and B0 are always 0. Hint: Use bitwise AND. Test in RIMS.
A0 0
A1 0
A2 0
A3 0
A4 0
A5 0
A6 0
©zyBooks 02/12/24 09:32 231883
A7 0 Lawrence Nderu
Ak78Fta73x
A = 0
35 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
1 #include "RIMS.h"
2
3 void main() {
4
5 }
B0 0
B1 0
B2 0
B3 0
B4 0
B5 0
B6 0
B7 0
B = 0
Compile
Exploring further:
36 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
Embedded programmers commonly use two more operators when manipulating bits:
For unsigned integer types, a shift operator moves the Xrst operand's (the bit pattern) bits left or
right by the shift amount indicated by the second operand. The shift amount can be 0, 1, ..., N,
where N is the bit pattern's number of bits. The following Xgure illustrates.
Note that vacated positions have 0s shifted in. Below are some examples of using shift operators
and RIMS I/O.
37 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
Consider a system with a sound sensor with a 4-bit output connected to A3..A0, 0 (0000)
meaning no sound, 15 (1111) meaning loud sound. A second sensor connects with A7..A4.
A system should add those two 4-bit values, outputting the sum on B. The following
example uses a mask to isolate A3..A0 in variable sound1, and a shift to isolate A7..A4 in
variable sound2, before adding sound1 and sound2.
while (1) {
sound1 = A & 0x0F; // bits 7..4 = 0000, bits 3..0 = A3A2A1A0
sound2 = A >> 4; // bits 7..4 = 0000, bits 3..0 = A7A6A5A4
B = sound1 + sound2;
}
}
A common error is to fail to shift a binary number's bits to start with the rightmost bit, instead using
just a mask. For the above example, sound2 = A & 0xF0 yields A7A6A5A40000, which has
A7..A4 in the wrong positions. If A3..A0 is 0001 (1) and A7..A4 is 0011 (3), B should be 00000001
(1) + 00000011 (3) which is 00000100 (4). Failing to shift would instead set B to 00000001 (1) +
00110000 (48), which is 00110001 (49).
PARTICIPATION
ACTIVITY 2.6.1: Shifting.
38 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
39 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
PARTICIPATION
ACTIVITY 2.6.2: RIMS I/O with shifting. Full screen
Write a single C statement for RIMS that sets B3-B0 to A5-A2 and sets other output bits to
0s. Test in RIMS.
A0 0
A1 0
A2 0
A3 0
A4 0
A5 0
A6 0
A7 0
A = 0
40 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
1 #include "RIMS.h"
2
3 void main() {
4
5 }
B0 0
B1 0
B2 0
B3 0
B4 0
B5 0
B6 0
B7 0
B = 0
Compile
A parking lot has eight spaces, each with a sensor connected to RIM input A7, A6, ..., or
A0. A RIM input being 1 means a car is detected in the corresponding space. Spaces A7
and A6 are reserved handicapped parking. Write a RIM C program that: (1) Sets B0 to 1 if
both handicapped spaces are full, and (2) Sets B7..B5 equal to the number of available
non-handicapped spaces.
41 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
A0 0
A1 0
A2 0
A3 0
©zyBooks 02/12/24 09:32 231883
A4 0 Lawrence Nderu
Ak78Fta73x
A5 0
A6 0
A7 0
A = 0
1 #include "RIMS.h"
2
3 void main() {
4
5 }
42 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
B0 0
B1 0
B2 0
B3 0
©zyBooks 02/12/24 09:32 231883
B4 0 Lawrence Nderu
Ak78Fta73x
B5 0
B6 0
B7 0
B = 0
Shifting can be performed on signed integer types too, but we do not recommend such use. Such
shifting was previously popular because shifting a binary number left or right is equivalent to
multiplying or dividing by 2, respectively (just as shifting a decimal number left or right is equivalent
to multiplying or dividing by 10), and shifting could result in faster code execution than the slower *
and / operations on some processors. However, modern compilers automatically replace * and / by
shifts when possible, so today the programmer can emphasize understandable code rather than
such low-level speedup attempts. For the curious reader, shifting a signed number performs an
"arithmetic" shift that preserves the number's sign, rather than a "logical" shift that merely shifts all
bits. This material will never shift signed types.
CHALLENGE
ACTIVITY 2.6.1: RIMS bit manipulation. Full screen
90524.463766.qx3zqy7
Start
43 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
A0 0
A1 0
A2 0
A3 0
©zyBooks 02/12/24 09:32 231883
A4 0
Lawrence Nderu
Ak78Fta73x
A5 0
A6 0
A7 0
A = 0
1
44 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
B0 0
B1 0
B2 0
B3 0
©zyBooks 02/12/24 09:32 231883
Lawrence Nderu
Ak78Fta73x
Exploring further:
The following expression returns a value in which the kth bit (rightmost bit being the 0th bit) of an
unsigned char x is set to 1:
The expression shifts mask 0x01 left k positions to get a 1 bit into the kth position. The expression
then bitwise ORs the result with x, forcing the k'th bit to 1 while passing through x's other bits. For
example, if k is 2, the mask will be shifted to become 00000100. Similar expressions
©zyBooks can be
02/12/24 09:32 created
231883
for unsigned short or long types. Lawrence Nderu
Ak78Fta73x
Similarly, the following expression returns a value in which the kth bit of an unsigned char x is set to
0:
45 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
The expression shifts mask 0x01 left k positions to get a 1 bit into the kth position. The expression
then bitwise complements the result using ~ to yield a mask with ©zyBooks kth bit and
a 0 in the 02/12/24 1s in
09:32 the
231883
other bits. For example, when k is 1, the shifted mask will be 00000010, which will then
Lawrence be
Nderu
Ak78Fta73x
complemented into 11111101. The expression then bitwise ANDs the resulting mask with x, so that
in the result the kth bit is forced to 0, while x's bits pass through to the remaining bits.
The above expressions can be called by a function that can set any bit to either 0 or 1:
// x: 8-bit value. k: bit position to set, range is 0-7. b: set bit to this,
either 1 or 0
unsigned char SetBit(unsigned char x, unsigned char k, unsigned char b) {
return (b ? (x | (0x01 << k)) : (x & ~(0x01 << k)) );
// Set bit to 1 Set bit to 0
}
The function uses C's ternary conditional operator (?:). The operator checks the Xrst operand: If
non-zero, the expression evaluates to the second operand, else the expression evaluates to the
third operand. For example, for m = (n < 5) ? 44 : 99, if n < 5 then m will be assigned 44,
else m will be assigned 99.
PARTICIPATION
ACTIVITY 2.7.1: SetBit returns the given unsigned char with a single jipped bit.
46 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
Animation captions:
1. Bit B2 is set to 1
2. Bit B5 is set to 0
PARTICIPATION
ACTIVITY 2.7.2: SetBit() function.
47 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
value? Answer: 1 or 0.
Similarly, a function can be created that gets (rather than sets) the value of a particular bit in an
integer variable:
The function creates a mask containing a 1 in position k and 0s in all other positions, then performs
a bitwise AND to pass the kth bit of x through, resulting either in a zero
©zyBooks if the kth09:32
result02/12/24 bit was 0, or a
231883
th Lawrence Nderu
non-zero result if the k bit was 1. The function compares the result with 0, thus returning either a 1
Ak78Fta73x
or a 0.
PARTICIPATION
ACTIVITY 2.7.3: GetBit returns a 0 or 1 indicating the value of a single bit.
48 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
Animation captions:
A parking lot has eight parking spaces, each with a sensor connected to input A. The
following program sets B to the number of occupied spaces, by counting the number of 1s
using the GetBit() function.
#include "RIMS.h"
void main()
{
unsigned char i;
unsigned char cnt;
while (1) {
cnt=0;
for (i=0; i<8; i++) {
if (GetBit(A, i)) {
cnt++;
}
©zyBooks 02/12/24 09:32 231883
}
B = cnt;
Lawrence Nderu
} Ak78Fta73x
}
49 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
Note that the above bit access functions do not perform error checking (e.g., a call can attempt to
set the 9th bit of a variable).
The examples using the SetBit and GetBit functions may seem ine5cient due to computing the
mask, but today's optimizing compilers handle these very e5ciently. Furthermore, the inline
keyword can be prepended to each function declaration (e.g., "inline unsigned char GetBit(...)") to
©zyBooks 02/12/24 09:32 231883
encourage compilers to inline the function calls (though many compilers would do so anyways).
Lawrence Nderu
Inlining means to replace a function call by the function's internal statements. Compiler
Ak78Fta73x
optimizations may then eliminate most of the statements within the GetBit and SetBit functions.
A programmer may wish to copy the bit-access functions to the top of a Xle and then call those
functions in a program, as shown below.
Figure 2.7.6: Example using bit access functions to set B as the reverse
of A.
#include "RIMS.h"
// Bit-access functions
inline unsigned char SetBit(unsigned char x, unsigned char k, unsigned char
b) {
return (b ? (x | (0x01 << k)) : (x & ~(0x01 << k)) );
}
inline unsigned char GetBit(unsigned char x, unsigned char k) {
return ((x & (0x01 << k)) != 0);
}
void main(){
unsigned char i;
while(1){
for (i=0; i<8; i++) {
B = SetBit(B, 7-i, GetBit(A,i));
}
}
}
PARTICIPATION
ACTIVITY 2.7.4: Consecutive 1s. Full screen
©zyBooks 02/12/24 09:32 231883
Lawrence Nderu
Write a program that sets B0 = 1 if a sequence of three consecutive 1s appears anywhere
Ak78Fta73x
on input A (e.g., 11100000 and 10111101 have such sequences, while 11001100 does
not), using a C for loop and the GetBit() function.
Note: Inlining is currently only supported in the Windows-based RIMS. To use the above
GetBit function in the web-based RIMS, remove: inline
50 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
A0 0
A1 0
A2 0
A3 0
©zyBooks 02/12/24 09:32 231883
A4 0 Lawrence Nderu
Ak78Fta73x
A5 0
A6 0
A7 0
A = 0
1 #include "RIMS.h"
2 int main() {
3 while(1) {
4 B0 = A0 && A1;
5 }
6 return 0;
7 }
51 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
B0 0
B1 0
B2 0
B3 0
©zyBooks 02/12/24 09:32 231883
B4 0 Lawrence Nderu
Ak78Fta73x
B5 0
B6 0
B7 0
B = 0
PARTICIPATION
ACTIVITY 2.7.5: Bit access functions.
For the following, assume RIMS only has variables A and B available, and not the
individual bit variables like A0, A1, ... and B0, B1, ... — as is common in some
microcontrollers.
52 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
Exploring further:
Expressions involving integer division should be treated with extra care due to error that is
53 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
introduced from rounding during integer division, wherein any fraction is truncated. Consider the
formula for converting Celsius to Fahrenheit: F = (9/5)*C + 32. Suppose a RIMS program is coded
as follows:
void main() {
while (1) {
B = C2F_uc(A);
}
}
The table below shows the actual Fahrenheit values for Celsius values from 0 to 9, followed by
those values when rounded to integers, followed by values obtained from the above program:
Fahrenheit Fahrenheit
Fahrenheit Fahrenheit
Celsius (from above (altered to do division
(actual) (integer)
program) later)
0 32 32 32 32
1 33.8 34 33 33
2 35.6 36 34 35
5 41.0 41 37 41
6 42.8 43 38 42
7 44.8 45 39 44
54 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
8 46.4 46 40 46
9 48.2 48 41 48
The values obtained from the above program are wrong due to rounding. Because all values in the
©zyBooks
C2F_uc function's expression are integers, the term 9/5 is computed 02/12/24the
as an integer; 09:32 231883
value is 1.8
Lawrence Nderu
but is rounded to 1 because C rounds by truncating the fraction. Thus, the computation
Ak78Fta73x actually
being carried out is: F = 1*C + 32.
The rounding problem can be partially addressed by doing the division as late as possible. For the
above program, the line that computes F can be changed to:
F = (9*C)/5 + 32;
The values obtained after that change are shown in the last column of the above table. Those
values are much closer to the desired values; the differences are due to the C language truncating
any fractional part, rather than rounding up if the fractional part is 0.5 or greater.
Declaring items as joating-point type rather than integer type can reduce rounding error, but
embedded programmers commonly (but not always) avoid joating-point types due to slow
execution, especially on small microcontrollers.
PARTICIPATION
ACTIVITY 2.8.1: C data types.
55 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
On the other hand, when computing expressions, care must also be taken to avoid overjow during
intermediate calculations. Over5ow occurs when a value is too large to Xt into a variable's storage,
causing high-order bits to be lost. The following animation illustrates.
PARTICIPATION
ACTIVITY 2.8.2: Overjow error.
Animation captions:
Because embedded programmers commonly use the smallest possible data types to conserve
memory and improve execution speed on resource-limited microcontrollers, embedded
programmers must be even more attentive to overjow than traditional programmers.
©zyBooks
One way to reduce overjow is to compute divisions earlier. For example, 02/12/24 09:32
computing 231883of
the average
Lawrence Nderu
three variables might be done as: Ak78Fta73x
avg = (a + b + c) / 3
Assume all variables are unsigned short type. If the values in a, b, and c are potentially large (e.g.,
10,000 or more), then the sum could overjow (exceed 65,535), yielding an incorrect value that
would then be divided by 3. Rewriting the expression as follows eliminates the possibility of
56 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
overjow:
avg = (a/3) + (b/3) + (c/3)
Note that doing division earlier is the opposite solution as was proposed for reducing rounding
error, for which doing division later was suggested. Thus, a tradeoff is involved. The best
expression depends on the data that the programmer expects. For example, if unsigned short
variables will store numbers in the hundreds, reducing rounding may ©zyBooks 02/12/24
dominate 09:32 231883
a programmer's
Lawrence Nderu
concerns. But if those short variables will store numbers in the tens of thousands, avoiding
Ak78Fta73x
overjow may dominate. No general rule exists; the embedded programmer must think carefully
about a program's data.
Another way to reduce overjow within a calculation is to temporarily cast to a larger data type. A
function dealing with a particular data type might Xrst cast smaller types to larger ones, compute a
result, and then cast back to a smaller type. The function can also explicitly check for overjow, and
report an error, before returning a result. For example, the Celsius to Fahrenheit function might be
rewritten as follows.
Figure 2.8.2: Function that converts to larger data types to detect and/or
reduce overjow.
Csi = C;
Fsi = (9*Csi)/5 + 32; // Can't possibly overflow
PARTICIPATION
ACTIVITY 2.8.3: Overjow.
57 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print
1) Postponing division, as in (a * b * c) /
d, can reduce occurrences of
overjow.
True
False
58 of 58 12/02/2024, 09:32