Lecture 02

Download as pdf or txt
Download as pdf or txt
You are on page 1of 58

zyBooks https://learn.zybooks.

com/zybook/Ak78Fta73x/chapter/2/print

2.1 C in embedded systems


C is a popular programming language in embedded systems due to the language's simplicity and
e5ciency.
©zyBooks 02/12/24 09:32 231883
Lawrence Nderu
Table 2.1.1: Embedded systems programming languages. Ak78Fta73x

% 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%

Source: www.embedded.com, 2019 Embedded Markets Study.

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

Table 2.1.2: Worldwide programming language usage (not just


embedded systems).

"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%

Source: TIOBE Programming Community Index (June 2017).

PARTICIPATION
ACTIVITY 2.1.1: Embedded systems.

1) C and the related C++ language


represented the main language for
nearly 80% of embedded
programmers (from 2019 survey).
True ©zyBooks 02/12/24 09:32 231883
Lawrence Nderu
False Ak78Fta73x

2) C was originally designed for


embedded systems.
True
False

2 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print

3) Assembly language is the main


language for nearly half of embedded
programmers.
True
False

4) Fluency in C is rarely useful outside ©zyBooks 02/12/24 09:32 231883


embedded systems. Lawrence Nderu
Ak78Fta73x
True
False

Exploring further:

• Wikipedia: C language
• Wikipedia: C++ language
• cplusplus.com: Excellent resource for C++ as well as C
• cprogramming.com

2.2 C data types


Several C data types are commonly used to represent integers in embedded system programs:

Table 2.2.1: Data types.

Type Minimum size* Range Notes

signed char to
©zyBooks 02/12/24 09:32 231883
unsigned char to
Lawrence Nderu
Ak78Fta73x
signed short to
is
unsigned short to

signed long to is about 4

3 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print

unsigned long billion


to

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.

unsigned char numA = 7;


94 00001000 numA
unsigned long numB = 7; 95 ALU
00000111
numA = numA + 1; 96 00000111 00000000
numB = numB + 1; 97 00000000 00000000
numB 00000000
98 00000000
char: only 1 byte 99 00000000 char: only 1 byte operation

long: 4 bytes long: 4 byte operation


(move bytes to ALU,
do adds, move bytes back)

©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
Animation captions: Ak78Fta73x

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

Figure 2.2.1: Variable declarations.

unsigned char ucI1;


unsigned short usI2;
signed long slI3;
unsigned char bMyBitVar;

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:

Figure 2.2.2: Bit representation.


©zyBooks 02/12/24 09:32 231883
Lawrence Nderu
ucI1 = 1; // underlying bits will be Ak78Fta73x
00000001
ucI1 = 12; // underlying bits will be
00001100
ucI1 = 127; // underlying bits will be
01111111
ucI1 = 255; // underlying bits will be
11111111

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

Table 2.2.2: Choosing appropriate type.

Purpose Variable declaration

Store a ©zyBooks 02/12/24 09:32 231883


person's age unsigned char age; // Not < 0, not > 255Lawrence Nderu
Ak78Fta73x
in years

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.

1) How many bits is an unsigned


char?

Check Show answer ©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
Ak78Fta73x
2) How many bits is a signed char?

Check Show answer

7 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print

3) What is 15 in 8-bit binary?

Check Show answer

4) What are the bits stored in


©zyBooks 02/12/24 09:32 231883
memory for unsigned char x = Lawrence Nderu
5? Ak78Fta73x

Check Show answer

5) What are the bits stored in


memory for unsigned char x =
199?

Check Show answer

6) DeXne a variable "volts" that can


range from -100 to +100
(integers only). End with ;.

Check Show answer

7) DeXne a variable "height" that


will hold a human's height in
inches (integers only). End with
;.

©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
Check Show answer Ak78Fta73x

8) DeXne a variable "birthYear" that


will hold the year a person's birth
occurred, range is 1. A.D. to
today.

8 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print

Check Show answer

9) DeXne a variable "distMoon" that


holds the distance in miles that
the moon is from the earth on a ©zyBooks 02/12/24 09:32 231883
given day. Lawrence Nderu
Ak78Fta73x

Check Show answer

10) DeXne a variable "button" that


will indicate whether a button
is pressed or not.

Check Show answer

2.3 RIMS implicitly deLned I/O variables


In RIMS, each microcontroller input and output (I/O) is implicitly deXned as a global variable:
unsigned char A0;, unsigned char A1;, ..., unsigned char B0;, etc. An item intended
to represent a single bit, such as B0 in RIMS, should be set to only 0 or 1, as in: B0 = 1.

RIMS implicitly deXnes two additional global variables: A represents the 8-bit input as a decimal
number, and B the 8-bit output:

Figure 2.3.1: RIMS implicit variables.


©zyBooks 02/12/24 09:32 231883
unsigned char A; // Built-in variable A, representing RIMS' 8 input Lawrence Nderu
// pins as a single 8-bit variable Ak78Fta73x
unsigned char B; // Built-in variable B, representing RIMS' 8 output
// pins as a single 8-bit variable

Implicitly-deXned global variables are commonplace in microcontroller programming environments,

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

A5 0 ©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
A6 0 Ak78Fta73x

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 }

©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
Ak78Fta73x

B0 0

B1 0

B2 0

B3 0

B4 0

B5 0

B6 0

B7 0
B = 0

Compile

Simulation speed: Normal

0.00 s ©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
Ak78Fta73x

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 }

©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
Ak78Fta73x

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

Simulation speed: Normal

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 }

©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
Ak78Fta73x

B0 0

B1 0

B2 0

B3 0

B4 0

B5 0

B6 0

B7 0
B = 0

Compile

Simulation speed: Normal

0.00 s ©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
Ak78Fta73x

C variables can be initialized when declared, as in: unsigned char i1 = 5;.

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 }

©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
Ak78Fta73x

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

Simulation speed: Normal

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.

1) Set RIMS' Xrst output pin to 1. ©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
Ak78Fta73x

Check Show answer

2) Set RIMS' 8 output bits to


decimal 0.

16 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print

Check Show answer

3) Set RIMS' Xrst output to RIMS'


last input.
©zyBooks 02/12/24 09:32 231883
Lawrence Nderu
Ak78Fta73x
Check Show answer

4) Set RIMS' outputs to RIMS's


inputs minus 1, treating each as
a decimal number.

Check Show answer

5) Set A7 to 1.

Check Show answer

6) Set variable x to B7's current


value.

Check Show answer

7) Write an expression (without


parentheses) that evaluates to
true if A5 is 1.
©zyBooks 02/12/24 09:32 231883
if ( ) { Lawrence Nderu
x = B0; Ak78Fta73x
}

Check Show answer

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.

Table 2.4.1: Hex/binary representations.

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

©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
Ak78Fta73x
In the C language, a hex constant is preceded by 0x (the Xrst character 0 is a zero, not the letter O).
Thus, 0xFF represents 11111111 in binary. Each hex digit corresponds to four bits (four bits is
called a nibble). 0xff may also be used; hex constants are not case sensitive. (Wikipedia:
Hexadecimal)

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

1 hex digit for every 4 binary digits


(because 2^4 is 16)

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.

©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
CHALLENGE
2.4.2: Choose binary based on hex. Ak78Fta73x
ACTIVITY

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).

1) Set RIMS' Xrst output pin to 1.

Check Show answer

2) Set RIMS' Xrst output pin to 1


and all other pins to 0.

Check Show answer

3) Set RIMS' last output pin to 1


©zyBooks 02/12/24 09:32 231883
and all other pins to 0. Lawrence Nderu
Ak78Fta73x

Check Show answer

4) Set RIMS' outputs to all 1s.

21 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print

Check Show answer


5) Set RIMS' outputs to 01010101.

©zyBooks 02/12/24 09:32 231883


Check Show answer
Lawrence Nderu
Ak78Fta73x

6) Fill in the blank of this


expression to detect that all 8
RIMS inputs are 1s:
A == ____.

Check Show answer

7) What expression detects that


RIMS' Xrst input pin is 1?

Check Show answer

8) What expression detects that A0


is the only RIMS input that is 1?

Check Show answer

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 }

©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
Ak78Fta73x

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 }

©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
Ak78Fta73x

B0 0

B1 0

B2 0

B3 0

B4 0

B5 0

B6 0

B7 0
B = 0

Compile

Simulation speed: Normal

©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
0.00 s Ak78Fta73x

Example 2.4.1: Simple hexadecimal example.

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

to 10101010 when A1A0=10, and to 11111111 when A1A0=11.


#include "RIMS.h"

// Set B to 00000000 when A1A0=00, to 01010101 when A1A0=01,


// to 10101010 when A1A0=10, and to 11111111 when A1A0=11
void main()
{
while (1) {
if (!A1 && !A0) {
B = 0x00; // 0000 0000 ©zyBooks 02/12/24 09:32 231883
} Lawrence Nderu
else if (!A1 && A0) { Ak78Fta73x
B = 0x55; // 0101 0101
}
else if (A1 && !A0) {
B = 0xAA; // 1010 1010
}
else if (A1 && A0) {
B = 0xFF; // 1111 1111
}
}
}

Example 2.4.2: 7-segment display.

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:

©zyBooks 02/12/24 09:32 231883


Below is a (partial) RIM C program that appropriately sets the display forLawrence Nderu
the given dial
Ak78Fta73x
position:

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.
}
}

1) What B_ outputs should be set


to 1 for case 3? List in
ascending order separated by
spaces, i.e., B0 B2 ...

Check Show answer

2) To what should B be set for


case 3? B = ____; Use uppercase
letters for the hex literal.

Check Show answer

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.

©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
Ak78Fta73x

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 }

©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
Ak78Fta73x

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

2.5 Bitwise operators


An important programming skill for an embedded C programmer is manipulating bits within an
integer variable. C's bitwise operators enable such bit manipulation.

• & : bitwise AND — 1 if both bit-operands are 1s.


• | : bitwise OR — 1 if either or both bit-operands are 1s.
• ^ : bitwise XOR — 1 if exactly one of the two bit-operands is 1 (eXclusive OR)
• ~ : bitwise NOT — 1 if the bit-operand is 0; 0 if bit-operand is 1.

Bitwise operators operate on the operands' corresponding bits, as shown below.

PARTICIPATION
ACTIVITY 2.5.1: Bitwise operations.

©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
1 1 1 0 Ak78Fta73x
and & 1 or | 1 | 0 | 1

1 1 1 1

0x0E 00001110 00001110


0x18 & 00011000 | 00011000
0 0 0 010 0 0 0 00 1 1 11 0

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.

Enter the result of the given bitwise operation.

1) 00001111 & 10101010

Check Show answer

2) 00001111 | 10101010

Check Show answer

3) 00001111 ^ 10101010

Check Show answer

©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
4) ~00001111
Ak78Fta73x

Check Show answer

5) Type a statement that sets B's

30 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print

bits to the opposite of A's bits,


so if A is 11110000, B will be
00001111. End with ;

Check Show answer

©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
Logical operators &&, ||, and ! (there is no logical XOR operator) treat operands as zero (false) or
Ak78Fta73x
non-zero (true). So while 0x0F & 0xF0 (bitwise AND) evaluates to 0 because each AND of
corresponding operand bits evaluates to 0, in contrast 0x0F && 0xF0 (logical AND) evaluates to 1
because both operands are non-zero.

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.

Figure 2.5.1: RIMS I/O and bitwise operator examples.

B = A | 0x01; // Sets B to A, except forces B0 to 1


B = A | 0x04; // Sets B to A, except forces B2 to 1
B = A & 0xF7; // Sets B to A, except that B3 is cleared
to 0
B = A | 0xF0; // Sets B7..B4 to 1111, and B3..B0 to
A3..A0
B = A & 0x0F; // Sets B7..B4 to 0000, and B3..B0 to
A3..A0
B = B | 0x0F; // Keeps B7..B4 the same, forces B3..B0 to
1s
B = B & 0xF0; // Keeps B7..B4 the same, forces B3..B0 to
0s

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

Check Show answer

2) A | 0xF0

Check Show answer

3) A & 0x18

Check Show answer

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

Check Show answer

PARTICIPATION
ACTIVITY 2.5.4: RIMS I/O applications.

Suppose expressions given below are used in an if statement as in


©zyBooks 02/12/24 09:32 231883
if (expression) { ... }. Recall that a non-zero value is considered true, while zero
Lawrence Nderu
is considered false. Ak78Fta73x

1) The expression (A & 0x0F) evaluates


to true if any of A3, A2, A1, or A0 is 1.
True
False

2) The expression (A && 0xF0) evaluates


to true if any of A7, A6, A5, or A4 is 1.
True
False

3) The expression (A == 0x0F) is a


correct way to check if A3, A2, A1, and
A0 are all 1s, and A7 through A4 don't
matter.
True
False

4) The expression (A == 0xF) is a correct


way to check if A3, A2, A1, and A0 are
all 1s, and A7 through A4 don't matter.
True
False

5) The expression ( (A & 0x0F) == 0x0F )


is a correct way to check if A3, A2, A1, ©zyBooks 02/12/24 09:32 231883
Lawrence Nderu
and A0 are all 1s, and A7 through A4 Ak78Fta73x
don't matter.
True
False

6) The expression ( A == 0xFC ) is a


correct way to check if A7 through A2

34 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print

are all 1s, and A1 and A0 are 0s.


True
False

7) The expression ( !A1 && !A0 ) is a


correct way to check if A7 through A2
are all 1s, and A1 and A0 are 0s. ©zyBooks 02/12/24 09:32 231883
Lawrence Nderu
True Ak78Fta73x
False

8) B = A | 0xC0 sets B's bits to A's


bits except that B7 and B6 are forced
to 1s.
True
False

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 }

©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
Ak78Fta73x

B0 0

B1 0

B2 0

B3 0

B4 0

B5 0

B6 0

B7 0
B = 0

Compile

Simulation speed: Normal

0.00 s ©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
Ak78Fta73x

Exploring further:

36 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print

• Wikipedia: Bit Manipulation


• Wikipedia: Bitwise Operation
• Wikipedia: Mask

©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
2.6 Shift operators Ak78Fta73x

Embedded programmers commonly use two more operators when manipulating bits:

• bitpat << amt : Left shift bitpat by amt positions


• bitpat >> amt : Right shift bitpat by amt positions

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.

Figure 2.6.1: Bitwise shift operators.

0x0F << 2: 0x0F >> 3:


00001111 00001111
<< 2 >> 3
------------ ------------
00111100 00000001

Note that vacated positions have 0s shifted in. Below are some examples of using shift operators
and RIMS I/O.

Figure 2.6.2: Bitwise shift examples with RIMS I/O.

B = A << 1; // Sets B7 to A6, B6 to A5, ..., B1 to A0, and B0


to 0 ©zyBooks 02/12/24 09:32 231883
B = A >> 4; // Sets B7..B4 to 0000, and B3..B0 to A7..A4Lawrence Nderu
B = B << 1; // Sets B7 to B6, B6 to B5, ..., and B0 to 0 Ak78Fta73x

Example 2.6.1: 4-bit addition using shifts.

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.

#include "RIMS.h" ©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
void main() Ak78Fta73x
{
unsigned char sound1 = 0;
unsigned char sound2 = 0;

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.

Assume A and B refer to RIMS' 8-bit inputs and outputs, respectively.

1) Given A is 01100111, what is A


<< 1?

Check Show answer


©zyBooks 02/12/24 09:32 231883
Lawrence Nderu
Ak78Fta73x
2) Given A is 01100111, what is A
>> 3?

Check Show answer

38 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print

3) Type a shift expression that


results in bit A4 being in the
rightmost bit. Type answer in
this form: A >> 7

Check Show answer ©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
Ak78Fta73x
4) Type a single statement
(including the ending semicolon)
that sets B3..B0 to A7..A4, while
setting B7..B4 to 0s. Use a shift
operator and no other bitwise
operators.

Check Show answer

5) Suppose A3..A0 hold a 4-bit


binary number, and A7..A4 are
used for another purpose. Fill in
the blank with an expression to
mask A so that this statement
sets B to A3..A0 plus 5: B =
(____) + 5;

Check Show answer

6) Suppose A7..A4 hold a 4-bit


binary number, and A3..A0 are
used for another purpose. Fill in
©zyBooks 02/12/24 09:32 231883
the blank so that this statement
Lawrence Nderu
sets B to A7..A4 plus 5: B = Ak78Fta73x
(____) + 5;

Check Show answer

39 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print

7) Type a statement (including the


ending semicolon) that sets
B7..B4 to the value in variable
num, which is an unsigned char
that only holds values from 0 to
15. B3..B0 can be set to 0. End
with ; ©zyBooks 02/12/24 09:32 231883
Lawrence Nderu
Ak78Fta73x

Check Show answer

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

©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
Ak78Fta73x

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 }

©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
Ak78Fta73x

B0 0

B1 0

B2 0

B3 0

B4 0

B5 0

B6 0

B7 0
B = 0

Compile

Simulation speed: Normal

©zyBooks 02/12/24 09:32 231883


PARTICIPATION Lawrence
ACTIVITY 2.6.3: Parking lot sensors.  Full Nderu
screen
Ak78Fta73x

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 }

©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
Ak78Fta73x

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

©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
Ak78Fta73x

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

©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
Ak78Fta73x

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:

• Wikipedia: Arithmetic Shift


• Wikipedia: Logical Shift

2.7 Bit access functions


DeXning C functions that get or set a particular bit of a variable can make programs easier to read,
especially on microcontrollers that don't have variables for speciXc input and output pins like RIMS'
A0..A7 and B0..B7 variables.

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:

Figure 2.7.1: Expression to set a particular bit to 1.

x | (0x01 << k) // Evaluates to x but with k'th bit 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:

Figure 2.7.2: Expression to set a particular bit to 0.

45 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print

x & ~(0x01 << k) // Evaluates to x but with k'th bit set


to 0

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:

Figure 2.7.3: SetBit() function to set a particular bit to 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.

x = b11110101 k=5 b=0


unsigned char SetBit(unsigned char x, unsigned char k, unsigned char b) {
return (b ? (x | (0x01 << k)) : (x & ~(0x01 << k)) );©zyBooks 02/12/24 09:32 231883
} 0 (x & ~(0x01 << k)) Lawrence Nderu
Ak78Fta73x
... 11110101
B = 0xF1;
& 11011111
B = SetBit(B, 2, 1);
B = SetBit(B, 5, 0); B = 11010101

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

©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
An example of using the SetBit function involves setting B's lowest four bits Ak78Fta73x
to the value of A0,
without changing B's highest four bits (for RIMS).

Figure 2.7.4: Example using SetBit() function.

unsigned char i, val;


val = A0;
for (i = 0; i < 4; i++)
{
B = SetBit(B, i,
val);
}

PARTICIPATION
ACTIVITY 2.7.2: SetBit() function.

1) Call SetBit() to return RIMS' A


input but with the lowest bit set
to 1.

Check Show answer

2) Call SetBit() to return RIMS' A


input but with the highest bit set
to 0. ©zyBooks 02/12/24 09:32 231883
Lawrence Nderu
Ak78Fta73x

Check Show answer

3) The expression x | (0x01 <<


k) sets a speciXc bit to what

47 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print

value? Answer: 1 or 0.

Check Show answer

4) In the expression x & ~(0x01


<< k) does &'s right operand ©zyBooks 02/12/24 09:32 231883
have a 1 or a 0 in position k? Lawrence Nderu
Ak78Fta73x

Check Show answer

5) If A is 00001111, what value will


x get for: x = (A & 0x04) ?
1 : 0;

Check Show answer

Similarly, a function can be created that gets (rather than sets) the value of a particular bit in an
integer variable:

Figure 2.7.5: GetBit() function to get a particular bit.

unsigned char GetBit(unsigned char x, unsigned char


k) {
return ((x & (0x01 << k)) != 0);
}

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

return ((x & (0x01 << k)) != 0);


}

©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
Ak78Fta73x

Animation captions:

1. Bit B3 is retrieved from B.

Example 2.7.1: Parking lot example using bit access functions.

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"

unsigned char GetBit(unsigned char x, unsigned char k) {


return ((x & (0x01 << k)) != 0);
}

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
}

In RIMS, the sum could also be obtained as: B = A0 + A1 + A2 + A3 + A4 + A5 + A6 + A7.


However, some microcontrollers don't provide variables for a port's individual bits, but
rather only for the grouped bits.

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 }

©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
Ak78Fta73x

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.

1) GetBit(A, 0) is a correct way to


determine whether the bit in A's
position 0 is 1.
True
False

2) GetBit(A, 7, 1) is a correct way to


determine whether the bit in A's
position 7 is 1.
True
False ©zyBooks 02/12/24 09:32 231883
Lawrence Nderu
3) A == 0x01 is a correct way to Ak78Fta73x
determine whether the bit in A's
position 0 is 1.
True
False

4) (A & 0x04) == 0x04 is a correct way to

52 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print

determine whether the bit in A's


position 2 is 1.
True
False

5) B = SetBit(B, 7, GetBit(B, 0)) sets B's


highest bit to B's lowest bit. ©zyBooks 02/12/24 09:32 231883
Lawrence Nderu
True Ak78Fta73x
False

6) If B is originally 11110000, then after


SetBit(B, 2, 1), B is updated to
11110100.
True
False

7) If B is originally 00001111, then after


B = SetBit(B, 7, 1), B is updated to
10000000.
True
False

8) Trying to set a bit to 1 that is already 1


using SetBit is an error.
True
False

Exploring further:

• Wikipedia: ?: (Ternary) Operator

©zyBooks 02/12/24 09:32 231883


Lawrence Nderu

2.8 Rounding and overUow Ak78Fta73x

Rounding during integer division

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:

Figure 2.8.1: Rounding error in a Celsius to Fahrenheit program.


©zyBooks 02/12/24 09:32 231883
#include "RIMS.h" Lawrence Nderu
Ak78Fta73x
unsigned char C2F_uc(unsigned char
C) {
unsigned char F;
F = (9/5)*C + 32;
return F;
}

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:

Table 2.8.1: Celsius to Fahrenheit values, showing rounding error.

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

3 37.4 37 35 37©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
Ak78Fta73x
4 39.2 39 36 39

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.

1) Given x is 50, what is the result


of the expression (3/2) * x?

Check Show answer

2) Rewrite the expression (3/2) * x


to reduce the impact of
rounding. Use one set of
©zyBooks 02/12/24 09:32 231883
parentheses.
Lawrence Nderu
Ak78Fta73x

Check Show answer

3) Rewrite the expression to


reduce rounding error: (a/2) +

55 of 58 12/02/2024, 09:32
zyBooks https://learn.zybooks.com/zybook/Ak78Fta73x/chapter/2/print

(b/2) + (c/2). Use one set of


parentheses.

Check Show answer

©zyBooks 02/12/24 09:32 231883


OverRow Lawrence Nderu
Ak78Fta73x

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.

... 1 00000000000000000000000000000001 hrsUploadedTotal


int hrsUploadedTotal; (32 bits)
Overflow occurs
hrsUploadedTotal = 4294967297;

Animation captions:

1. hrsUploadedTotal is a variable of type int.


2. Assigning a value greater than the maximum value the variable can store results in
overjow. The leftmost bit is lost, so hrsUploadedTotal actually stores a value of 1.

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.

unsigned char C2F_uc(unsigned char C) {


unsigned char F;
unsigned short Csi, Fsi;

Csi = C;
Fsi = (9*Csi)/5 + 32; // Can't possibly overflow

if (Fsi <= 255) {


F = (char)Fsi;
}
else {
// Print error message or indicate error if
possible
F = 0; // Set F to a unique "error" value if
possible
}
return F;
}

©zyBooks 02/12/24 09:32 231883


Lawrence Nderu
A further improvement that can reduce rounding error, made possible by theAk78Fta73x
larger data type, would
be to set Csi = 10*C, compute Fsi as above, round Fsi to the 10s place (if the 1s column is 5 or
greater, increment the 10s column), and then divide Fsi by 10, before checking for overjow.

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

2) Given unsigned char variables a, b, ©zyBooks 02/12/24 09:32 231883


and c, if each may range from 0-100, Lawrence Nderu
Ak78Fta73x
then (a+b+c)/3 may overjow.
True
False

3) Given unsigned char variables a and


b, if each may range from 1-100, then
(a + b) / (2 * a) may overjow.
True
False

4) If a function's parameters p1 and p2


and return type are all unsigned short
types, casting parameters Xrst to
unsigned long types may reduce
overjow.
True
False

5) If a function has a parameter that has


been cast to a larger type to prevent
overjow, multiplying the parameter by
10, then dividing a Xnal result by 10,
can further reduce overjow.
True
False
©zyBooks 02/12/24 09:32 231883
Lawrence Nderu
Ak78Fta73x

58 of 58 12/02/2024, 09:32

You might also like