Arduino Microcontroller Processing For Everyone-Steven F. Barrett
Arduino Microcontroller Processing For Everyone-Steven F. Barrett
Embedded Systems Interfacing for Engineers using the Freescale HCS08 Microcontroller
II: Digital and Analog Hardware Interfacing
Douglas H. Summerville
2009
iv
Embedded Systems Interfacing for Engineers using the Freescale HCS08 Microcontroller
I: Assembly Language Programming
Douglas H.Summerville
2009
Pragmatic Power
William J. Eccles
2008
Pragmatic Logic
William J. Eccles
2007
v
PSpice for Filters and Transmission Lines
Paul Tobin
2007
All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, or transmitted in
any form or by any means—electronic, mechanical, photocopy, recording, or any other except for brief quotations in
printed reviews, without the prior permission of the publisher.
DOI 10.2200/S00280ED1V01Y201005DCS028
Lecture #28
Series Editor: Mitchell A. Thornton, Southern Methodist University
Series ISSN
Synthesis Lectures on Digital Circuits and Systems
Print 1932-3166 Electronic 1932-3174
Arduino Microcontroller
Processing for Everyone!
Part I
Steven F. Barrett
University of Wyoming, Laramie, WY
M
&C Morgan & cLaypool publishers
ABSTRACT
This book is about the Arduino microcontroller and the Arduino concept. The visionary Arduino
team of Massimo Banzi, David Cuartielles,Tom Igoe, Gianluca Martino, and David Mellis launched
a new innovation in microcontroller hardware in 2005, the concept of open source hardware. Their
approach was to openly share details of microcontroller-based hardware design platforms to stimulate
the sharing of ideas and promote innovation. This concept has been popular in the software world
for many years. This book is intended for a wide variety of audiences including students of the fine
arts, middle and senior high school students, engineering design students, and practicing scientists
and engineers. To meet this wide audience, the book has been divided into sections to satisfy the
need of each reader. The book contains many software and hardware examples to assist the reader in
developing a wide variety of systems. For the examples, the Arduino Duemilanove and the Atmel
ATmega328 is employed as the target processor.
KEYWORDS
Arduino microcontroller, Arduino Duemilanove, Atmel microcontroller, Atmel AVR,
ATmega328, microcontroller interfacing, embedded systems design
ix
Contents
Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiii
1 Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1
1.3 Arduino Duemilanove . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.3.1 Arduino host processor — the ATmega328 3
1.4 Example: Autonomous Maze Navigating Robot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.4.1 Structure chart 6
1.4.2 UML activity diagrams 8
1.4.3 Arduino Duemilanove Systems 9
1.5 Arduino open source schematic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.6 Other Arduino-based platforms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.7 Extending the hardware features of the Arduino platform . . . . . . . . . . . . . . . . . . . . . . .9
1.8 Arduino Software . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.9 Arduino Duemilanove/ATmega328 hardware features . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.9.1 Memory 13
1.9.2 Port System 15
1.9.3 Internal Systems 16
1.10 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
1.11 References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
1.12 Chapter Problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2 Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.2 The Big Picture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
x
2.3 Anatomy of a Program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.3.1 Comments 24
2.3.2 Include files 25
2.3.3 Functions 25
2.3.4 Program constants 28
2.3.5 Interrupt handler definitions 29
2.3.6 Variables 29
2.3.7 Main program 30
2.4 Fundamental programming concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
2.4.1 Operators 30
2.4.2 Programming constructs 34
2.4.3 Decision processing 36
2.5 Arduino Development Environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
2.5.1 Background 39
2.5.2 Arduino Development Environment overview 40
2.5.3 Sketchbook concept 41
2.5.4 Arduino software, libraries, and language references 41
2.6 Application 1: Robot IR sensor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
2.7 Application 2: Art piece illumination system . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
2.8 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
2.9 References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
2.10 Chapter Problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
Author’s Biography . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
Preface
This book is about the Arduino microcontroller and the Arduino concept. The visionary
Arduino team of Massimo Banzi, David Cuartielles, Tom Igoe, Gianluca Martino, and David Mellis
launched a new innovation in microcontroller hardware in 2005, the concept of open source hardware.
There approach was to openly share details of microcontroller-based hardware design platforms to
stimulate the sharing of ideas and innovation. This concept has been popular in the software world
for many years.
This book is written for a number of audiences. First, in keeping with the Arduino concept,
the book is written for practitioners of the arts (design students, artists, photographers, etc.) who may
need processing power in a project but do not have an in depth engineering background. Second, the
book is written for middle school and senior high school students who may need processing power
for a school or science fair project. Third, we write for engineering students who require processing
power for their senior design project but do not have the background in microcontroller-based appli-
cations commonly taught in electrical and computer engineering curricula. Finally, the book provides
practicing scientists and engineers an advanced treatment of the Atmel AVR microcontroller.
Steve Barrett
1
CHAPTER 1
Getting Started
Objectives: After reading this chapter, the reader should be able to the following:
• Name and describe the different features aboard the Arduino Duemilanove processor board.
• Download, configure, and successfully execute a test program using the Arduino software.
1.1 OVERVIEW
Welcome to the world of Arduino! The Arduino concept of open source hardware was developed
by the visionary Arduino team of Massimo Banzi, David Cuartilles, Tom Igoe, Gianluca Martino,
and David Mellis in Ivrea, Italy. The team’s goal was to develop a line of easy-to-use microcontroller
hardware and software such that processing power would be readily available to everyone.
In keeping with the Arduino concept, this book is intended for a wide variety of readers. For
those wanting a quick exposure to an Arduino microcontroller board and its easy-to-use software,
Chapters 1 and 2 are for you. If you need to tap into some of the other features of the processing
power of the ATmega328 host microcontroller, Chapters 3 through 8 are for you.
In keeping with the Arduino open source spirit, you will find a plethora of hardware and
software examples throughout the book. I hope you enjoy reading the book, and I also hope you will
find it a useful resource in developing Arduino-based projects.
Figure 1.1: Arduino Duemilanove starter kit. (Used with permission from SparkFun Electronics.)
Power supply. The Arduino processing board may be powered from the USB port during
project development. However, it is highly recommended that an external power supply be employed.
1.3. ARDUINO DUEMILANOVE 3
This will allow developing projects beyond the limited current capability of the USB port. SparkFun
Electronics recommends a power supply from 7-12 VDC with a 2.1 mm center positive plug. A
power supply of this type is readily available from a number of electronic parts supply companies.
For example, the Jameco #133891 power supply is a 9 VDC model rated at 300 mA and equipped
with a 2.1 mm center positive plug. It is available for under US$10.
Arduino software. You will also need the Arduino software called the Arduino Development
Environment. It is available as a free download from the Arduino homepage (www.arduino.cc). In
the Application section at the end of this chapter, we describe how to load the software and drivers
and get a sample program operating on the Arduino Duemilanove board.
In the next several sections, we provide information on the layout and capabilities of the
Arduino Duemilanove board and its host the Atmel ATmega328 processor. We also discuss other
Arduino-based processing boards and how to extend the features of the Arduino Duemilanove board
using the shield concept.
e
USB-to-serial LED
nc
re
m
converter
fe
TX LED
om
re
timebase
lc
og
RX LED
ria
al
PW
PW
an
se
LED power
indicator
switch
USB
connector
(to PC)
ISP programming
connector
power supply
connector
(7-12 VDC)
power supply analog inputs
terminals
Figure 1.2: Arduino Duemilanove layout. (Figure adapted and used with permission of Arduino Team
(www.arduino.cc).)
• Memory system,
• Port system,
• Timer system,
• Analog-to-digital converter (ADC),
• Interrupt system,
• and the Serial communications.
Arduino Duemilanove
hosted on the
ATmega328
Memory System Timer System Analog-to-digital converter Serial Communications
- 32K byte, ISP - Two 8-bit timer/counter - 6 channel 10-bit ADC - Serial USART
programmable flash - One 16-bit timer/counter (PDIP) - Serial peripheral interface
- 1K byte, byte - Six PWM channels - Two wire interface (TWI)
addressable EEPROM
- 2K byte RAM
Port System Interrupt System
- 14 digital I/O pins - 26 total interrupts
-- 6 provide PWM - 2 external pin interrupts
- 6 analog input pins
Figure 1.3: Arduino Duemilanove systems.
6 1. GETTING STARTED
In this example, we will equip the Blinky 602A robot platform with three Sharp GP12D IR
sensors as shown in Figure 1.4. The robot will be placed in a maze with white reflective walls. The
goal is for the robot to detect wall placement and navigate through the maze. (Figure 1.5.) The robot
will not be provided any information about the maze. The control algorithm for the robot will be
hosted on the Arduino Duemilanove.
Sharp GP2D12
Center IR sensor
IR sensor
Left Right
IR sensor IR sensor
Arduino
Duemilanove
powered powered
wheel wheel
prototype
area
turn signals
controlling the robot. The structure chart for the robot project is provided in Figure 1.6. As you
can see, the robot has three main systems: the motor control system, the sensor system, and the
digital input/output system. These three systems interact with the main control algorithm to allow
the robot to autonomously (by itself ) navigate through the maze by sensing and avoiding walls.
determine_robot
_action
robot sensor
action data
digital
motor_control ADC
input/output
desired
motor ch for conv
action conv data
left running right
PWM_left ADC
PWM_right ReadADC turn turn
Initialize lights
signal signal
include files
global variables
function prototypes
initialize ports
initialize ADC
initialize PWM
while(1)
determine robot
action
issue motor
control signals
O
R
T
B
P
O
R
D
T
P
O
R
C
T
P
ATmega328
USB-to-USART
converter
Figure 1.8: Arduino Duemilanove open source schematic. (Figure adapted and used with permission of
the Arduino Team (www.arduino.cc).)
1.7. EXTENDING THE HARDWARE FEATURES OF THE ARDUINO PLATFORM 11
Figure 1.9: Arduino variants. (Used with permission from SparkFun Electronics.)
12 1. GETTING STARTED
Figure 1.10: Arduino shield. (Used with permission from SparkFun Electronics.)
Figure 1.11: ATmega328 pin out. (Figure used with permission of Atmel, Incorporated.)
1.9.1 MEMORY
The ATmega328 is equipped with three main memory sections: flash electrically erasable pro-
grammable read only memory (EEPROM), static random access memory (SRAM), and byte-
addressable EEPROM for data storage. We discuss each memory component in turn.
14 1. GETTING STARTED
Figure 1.12: ATmega328 block diagram. (Figure used with permission of Atmel, Incorporated.)
7 0
Port x Data Direction Register - DDRx
7 0
Port x Input Pins Address - PINx
7 0
a) port associated registers
Serial USART The serial USART is used for full duplex (two way) communication between
a receiver and transmitter. This is accomplished by equipping the ATmega328 with independent
hardware for the transmitter and receiver. The USART is typically used for asynchronous commu-
nication. That is, there is not a common clock between the transmitter and receiver to keep them
synchronized with one another. To maintain synchronization between the transmitter and receiver,
framing start and stop bits are used at the beginning and end of each data byte in a transmission
sequence.
The ATmega328 USART is quite flexible. It has the capability to be set to a variety of data
transmission rates known as the Baud (bits per second) rate. The USART may also be set for data
bit widths of 5 to 9 bits with one or two stop bits. Furthermore, the ATmega328 is equipped with
a hardware generated parity bit (even or odd) and parity check hardware at the receiver. A single
parity bit allows for the detection of a single bit error within a byte of data. The USART may
also be configured to operate in a synchronous mode. We discuss the operation, programming, and
application of the USART later in the book.
Serial Peripheral Interface—SPI The ATmega328 Serial Peripheral Interface (SPI) can also be
used for two-way serial communication between a transmitter and a receiver. In the SPI system,
the transmitter and receiver share a common clock source. This requires an additional clock line
between the transmitter and receiver but allows for higher data transmission rates as compared to
the USART.
The SPI may be viewed as a synchronous 16-bit shift register with an 8-bit half residing in
the transmitter and the other 8-bit half residing in the receiver. The transmitter is designated the
master since it is providing the synchronizing clock source between the transmitter and the receiver.
The receiver is designated as the slave. We discuss the operation, programming, and application of
the SPI later in the book.
Two-wire Serial Interface—TWI The TWI subsystem allows the system designer to network a
number of related devices (microcontrollers, transducers, displays, memory storage, etc.) together
into a system using a two wire interconnecting scheme. The TWI allows a maximum of 128 devices
to be connected together. Each device has its own unique address and may both transmit and receive
over the two wire bus at frequencies up to 400 kHz. This allows the device to freely exchange
information with other devices in the network within a small area. We discuss the TWI system later
in the book.
1.9.3.6 Interrupts
The normal execution of a program step follows a designated sequence of instructions. However,
sometimes this normal sequence of events must be interrupted to respond to high priority faults
and status both inside and outside the microcontroller. When these higher priority events occur, the
microcontroller must temporarily suspend normal operation and execute event specific actions called
an interrupt service routine. Once the higher priority event has been serviced, the microcontroller
returns and continues processing the normal program.
The ATmega328 is equipped with a complement of 26 interrupt sources.Two of the interrupts
are provided for external interrupt sources while the remaining interrupts support the efficient oper-
ation of peripheral subsystems aboard the microcontroller. We discuss the operation, programming,
and application of the interrupt system later in the book.
1.10 SUMMARY
In this chapter, we have provided an overview of the Arduino concept of open source hardware.
This was followed by a description of the Arduino Duemilanove processor board powered by the
ATmega328. An overview of ATmega328 systems followed. We then investigated various processing
boards in the Arduino line and concluded with brief guidelines on how to download and run the
Arduino software environment.
1.11 REFERENCES
• SparkFun Electronics, 6175 Longbow Drive, Suite 200, Boulder, CO 80301
(www.sparkfun.com)
• Atmel 8-bit AVR Microcontroller with 4/8/16/32K Bytes In-System Programmable Flash, AT-
mega48PA, 88PA, 168PA, 328P data sheet: 8161D-AVR-10/09, Atmel Corporation, 2325
Orchard Parkway, San Jose, CA 95131.
2. Sketch a block diagram of the ATmega328 and its associated systems. Describe the function
of each system.
20 1. GETTING STARTED
3. What is the purpose of a structure chart?
5. Describe the different types of memory components within the ATmega328. Describe appli-
cations for each memory type.
6. Describe the three different register types associated with each port.
CHAPTER 2
Programming
Objectives: After reading this chapter, the reader should be able to do the following:
• Discuss different programming constructs used for program control and decision processing.
• Describe what features of the Arduino Development Environment ease the program devel-
opment process.
• List the programming support information available at the Arduino home page.
2.1 OVERVIEW
To the novice, programming a microcontroller may appear mysterious, complicated, overwhelming,
and difficult. When faced with a new task, one often does not know where to start. The goal of
this chapter is to provide a tutorial on how to begin programming. We will use a top-down design
approach. We begin with the “big picture” of the chapter followed by an overview of the major
pieces of a program. We then discuss the basics of the C programming language. Only the most
fundamental concepts will be covered. We then discuss the Ardunio Development Environment and
how it may be used to develop a program for the Arduino Duemilanove processor board.Throughout
the chapter, we provide examples and also provide references to a number of excellent references.
22 2. PROGRAMMING
filename.c compiler
filename.h
filename.asm
assembler
filename.hex
filename.eep
computer
Arduino
Duemilanove
Arduino Development
Environment USB
ISP
or
C compiler
filename.hex
filename.eep
ISP
Figure 2.1: Programming the Arduino Duemilanove. (Used with permission from SparkFun Electronics,
and Atmel, Incorporated.)
24 2. PROGRAMMING
//include files
#include<file_name.h>
//function prototypes
A list of functions and their format used within the program
//program constants
#define TRUE 1
#define FALSE 0
#define ON 1
#define OFF 0
//global variables
Listing of variables used throughout the program
//main program
void main(void)
{
//function definitions
A detailed function body and definition
for each function used within the program
2.3.1 COMMENTS
Comments are used throughout the program to document what and how things were accomplished
within a program. The comments help you reconstruct your work at a later time. Imagine that you
wrote a program a year ago for a project. You now want to modify that program for a new project.
The comments will help you remember the key details of the program.
Comments are not compiled into machine code for loading into the microcontroller.Therefore,
the comments will not fill up the memory of your microcontroller. Comments are indicated using
2.3. ANATOMY OF A PROGRAM 25
double slashes (//). Anything from the double slashes to the end of a line is then considered a
comment. A multi-line comment can be constructed using a /∗ at the beginning of the comment
and a ∗/ at the end of the comment.
At the beginning of the program, comments may be extensive. Comments may include some
of the following information:
• file name
• program author
• program description
//include files
#include<file_name1.h>
#include<file_name2.h>
In an upcoming section, we see how the Arduino Development Environment makes it quite
easy to include a header file within a program.
2.3.3 FUNCTIONS
In the next chapter, we discuss in detail the top down design, bottom up implementation approach to
designing microcontroller based systems. In this approach, a microcontroller based project including
both hardware and software is partitioned into systems, subsystems, etc.The idea is to take a complex
project and break it into doable pieces with a defined action.
26 2. PROGRAMMING
We use the same approach when writing computer programs. At the highest level is the main
program which calls functions that have a defined action. When a function is called, program control
is released from the main program to the function. Once the function is complete, program control
reverts back to the main program.
Functions may in turn call other functions as shown in Figure 2.2. This approach results in a
collection of functions that may be reused over and over again in various projects. Most importantly,
the program is now subdivided into doable pieces, each with a defined action. This makes writing
the program easier but also makes it much easier to modify the program since every action is in a
known location.
void main(void)
{
There are three different pieces of code required to properly configure and call the function:
Function prototypes are provided early in the program as previously shown in the program
template. The function prototype provides the name of the function and any variables required by
the function and any variable returned by the function.
The function prototype follows this format:
//return variable
return return_variable;
}
Example: In this example, we describe how to configure the ports of the microcontroller to act
as input or output ports. Briefly, associated with each port is a register called the data direction register
(DDR). Each bit in the DDR corresponds to a bit in the associated PORT. For example, PORTB
28 2. PROGRAMMING
has an associated data direction register DDRB. If DDRB[7] is set to a logic 1, the corresponding
port pin PORTB[7] is configured as an output pin. Similarly, if DDRB[7] is set to logic 0, the
corresponding port pin is configured as an input pin.
During some of the early steps of a program, a function is called to initialize the ports as input,
output, or some combination of both. This is illustrated in Figure 2.3.
//function prototypes
void initialize_ports(void);
//main function
void main(void)
{
//program constants
#define TRUE 1
#define FALSE 0
#define ON 1
#define OFF 0
2.3. ANATOMY OF A PROGRAM 29
2.3.5 INTERRUPT HANDLER DEFINITIONS
Interrupts are functions that are written by the programmer but usually called by some hardware
event during system operation. We discuss interrupts and how to properly configure them in an
upcoming chapter.
2.3.6 VARIABLES
There are two types of variables used within a program: global variables and local variables. A global
variable is available and accessible to all portions of the program. Whereas, a local variable is only
known and accessible within the function where it is declared.
When declaring a variable in C, the number of bits used to store the operator is also specified.
In Figure 2.4, we provide a list of common C variable sizes used with the ImageCraft ICC AVR
compiler. The size of other variables such as pointers, shorts, longs, etc. are contained in the compiler
documentation [ImageCraft].
Figure 2.4: C variable sizes used with the ImageCraft ICC AVR compiler [ImageCraft].
//global variables
unsigned int loop_iterations = 6;
2.4.1 OPERATORS
There are a wide variety of operators provided in the C language. An abbreviated list of common
operators are provided in Figures 2.5 and 2.6. The operators have been grouped by general category.
The symbol, precedence, and brief description of each operator are provided.The precedence column
indicates the priority of the operator in a program statement containing multiple operators. Only
the fundamental operators are provided. For more information on this topic, see Barrett and Pack
in the Reference section at the end of the chapter.
General
Symbol Precedence Description
{} 1 Brackets, used to group program statements
() 1 Parenthesis, used to establish precedence
= 12 Assignment
Arithmetic Operations
Symbol Precedence Description
* 3 Multiplication
/ 3 Division
+ 4 Addition
- 4 Subtraction
Logical Operations
Symbol Precedence Description
< 6 Less than
<= 6 Less than or equal to
> 6 Greater
>= 6 Greater than or equal to
== 7 Equal to
!= 7 Not equal to
&& 9 Logical AND
|| 10 Logical OR
Unary Operations
Symbol Precedence Description
! 2 Unary negative
~ 2 One’s complement (bit-by-bit inversion)
++ 2 Increment
-- 2 Decrement
type(argument) 2 Casting operator (data type conversion)
The assignment operator (=) is used to assign the argument(s) on the right-hand side of an
equation to the left-hand side variable. It is important to insure that the left and the right-hand side
of the equation have the same type of arguments. If not, unpredictable results may occur.
return sum;
2.4. FUNDAMENTAL PROGRAMMING CONCEPTS 33
}
Answer: Variable “variable1” is declared as an eight bit unsigned char and assigned the hex-
adecimal value of (73)16 . In binary this is (0111_0011)2 . The << 2 operator provides a left shift of
the argument by two places. After two left shifts of (73)16 , the result is (cc)16 and will be assigned
to the variable “variable2.”
Note that the left and right shift operation is equivalent to multiplying and dividing the
variable by a power of two.
The bitwise operators perform the desired operation on a bit-by-bit basis. That is, the least
significant bit of the first argument is bit-wise operated with the least significant bit of the second
argument and so on.
Example: Given the following code segment, what will the value of variable3 be after execu-
tion?
Answer: Variable “variable1” is declared as an eight bit unsigned char and assigned the hex-
adecimal value of (73)16 . In binary, this is (0111_0011)2 . Variable “variable2” is declared as an eight
bit unsigned char and assigned the hexadecimal value of (f a)16 . In binary, this is (1111_1010)2 .
34 2. PROGRAMMING
The bitwise AND operator is specified. After execution variable “variable3,” declared as an eight bit
unsigned char, contains the hexadecimal value of (72)16 .
i++;
Example: It is not uncommon in embedded system design projects to have every pin on a
microcontroller employed. Furthermore, it is not uncommon to have multiple inputs and outputs
assigned to the same port but on different port input/output pins. Some compilers support specific
pin reference. Another technique that is not compiler specific is bit twiddling. Figure 2.7 provides
bit twiddling examples on how individual bits may be manipulated without affecting other bits using
bitwise and unary operators.The information provided here was extracted from the ImageCraft ICC
AVR compiler documentation [ImageCraft].
a&b bitwise and if ((PINA & 0x81) == 0) // check bit 7 and bit 0
In the following code fragment the for loop is executed ten times.
The for loop begins with the variable “loop_ctr” equal to 0. During the first pass through the
loop, the variable retains this value. During the next pass through the loop, the variable “loop_ctr”
is incremented by one. This action continues until the “loop_ctr” variable reaches the value of ten.
Since the argument to continue the loop is no longer true, program execution continues after the
close bracket for the for loop.
In the previous example, the for loop counter was incremented at the beginning of each loop
pass. The “loop_ctr” variable can be updated by any amount. For example, in the following code
fragment the “loop_ctr” variable is increased by three for every pass of the loop.
The “loop_ctr” variable may also be initialized at a high value and then decremented at the
beginning of each pass of the loop.
}
36 2. PROGRAMMING
As before, the “loop_ctr” variable may be decreased by any numerical value as appropriate for
the application at hand.
The while loop is another programming construct that allows multiple passes through a
portion of code. The while loop will continue to execute the statements within the open and close
brackets while the condition at the beginning of the loop remains logically true. The code snapshot
below will implement a ten iteration loop. Note how the “loop_ctr” variable is initialized outside of
the loop and incremented within the body of the loop. As before, the variable may be initialized to
a greater value and then decremented within the loop body.
loop_ctr = 0;
while(loop_ctr < 10)
{
//loop body
loop_ctr++;
}
Frequently, within a microcontroller application, the program begins with system initialization
actions. Once initialization activities are complete, the processor enters a continuous loop. This may
be accomplished using the following code fragment.
while(1)
{
• the if statement,
• switch statement.
The if statement will execute the code between an open and close bracket set should the
condition within the if statement be logically true.
Example: To help develop the algorithm for steering the Blinky 602A robot through a maze,
a light emitting diode (LED) is connected to PORTB pin 1 on the ATmega328. The robot’s center
2.4. FUNDAMENTAL PROGRAMMING CONCEPTS 37
IR sensor is connected to an analog-to-digital converter at PORTC, pin 1. The IR sensor provides
a voltage output that is inversely proportional to distance of the sensor from the maze wall. It is
desired to illuminate the LED if the robot is within 10 cm of the maze wall. The sensor provides an
output voltage of 2.5 VDC at the 10 cm range. The following if statement construct will implement
this LED indicator. We provide the actual code to do this later in the chapter.
In the example provided, there is no method to turn off the LED once it is turned on. This
will require the else portion of the construct as shown in the next code fragment.
The if–else if—else construct may be used to implement a three LED system. In this exam-
ple, the left, center, and right IR sensors are connected to analog-to-digital converter channels on
PORTC pins 2, 1, and 0, respectively. The LED indicators are connected to PORTB pins 2, 1, and
0. The following code fragment implements this LED system.
switch(new_PORTD)
{ //process change in PORTD input
case 0x01: //PD0
//PD0 related actions
break;
That completes our brief overview of the C programming language. In the next section, we provide
an overview of the Arduino Development Environment. You will see how this development tool pro-
vides a user-friendly method of quickly developing code applications for the Arduino Duemilanove
processing board.
2.5.1 BACKGROUND
The first version of the Arduino Development Environment was released in August 2005. It was
developed at the Interaction Design Institute in Ivrea, Italy to allow students the ability to quickly put
40 2. PROGRAMMING
processing power to use in a wide variety of projects. Since that time, newer versions incorporating
new features, have been released on a regular basis [www.arduino.cc].
At its most fundamental level, the Arduino Development Environment is a user friendly
interface to allow one to quickly write, load, and execute code on a microcontroller. A barebones
program need only consist of a setup() and loop() function.The Arduino Development Environment
adds the other required pieces such as header files and the main program construct. The ADE is
written in Java and has its origins in the Processor programming language and the Wiring Project
[www.arduino.cc].
In the next several sections, we introduce the user interface and its large collection of user
friendly tools. We also provide an overview of the host of built-in C and C++ software functions
that allows the project developer to quickly put the features of the Arduino Duemilanove processing
board to work for them.
sketch_feb15a
Open
Verify/Compile
Save
Stop
Serial monitor
Menu
repeated here. Instead, we refer to these features at appropriate places throughout the remainder of
the book as we discuss related hardware systems.
Keep in mind the Arduino open source concept. Users throughout the world are constantly
adding new built-in features. As new features are added, they will be released in future Arduino
Development Environment versions. As an Arduino user, you too may add to this collection of
useful tools. In the next section, we illustrate how to use the Arduino Duemilanova board in several
applications.
Display EEPROM Library Liquid Crystal Display Servo Library Stepper Library
- LED bar graph - EEPROM clear Library - Knob - Motor knob
- Row column scanning - EEPROM read - Hello World - Sweep
- EEPROM write - Blink
- Cursor
- Display
- Text Direction
- Scroll
- Serial Input
- SetCursor
6
1-7/16
5 VDC
220
10K
2N2222
3 21 0 9 8 76 5 4 32 1 0
1 1 1 1 DIGITAL
Ground
Arduino
Duemilanove
ANALOG IN
5VGnd 0 123 4 5
R Y B
IR sensor
The IR sensor’s power (red wire) and ground (black wire) connections are connected to the 5V
and Gnd pins on the Arduino Duemilanove board, respectively. The IR sensor’s output connection
(yellow wire) is connected to the ANALOG IN 5 pin on the Arduino Duemilanove board. The
LED circuit shown in the top right corner of the diagram is connected to the DIGITAL 0 pin on
the Arduino Duemilanove board. We discuss the operation of this circuit in the Interfacing chapter
later in the book.
46 2. PROGRAMMING
Earlier in the chapter, we provided a framework for writing the if-else statement to turn the
LED on and off. Here is the actual sketch to accomplish this.
//*************************************************************************
#define LED_PIN 0 //digital pin - LED connection
#define IR_sensor_pin 5 //analog pin - IR sensor
void setup()
{
pinMode(LED_PIN, OUTPUT); //configure pin 0 for digital output
}
void loop()
{
//read analog output from IR sensor
IR_sensor_value = analogRead(IR_sensor_pin);
The sketch begins by providing names for the two Arduino Duemilanove board pins that will
be used in the sketch. This is not required but it makes the code easier to read. We define the pin
for the LED as “LED_PIN.” Any descriptive name may be used here. Whenever the name is used
within the sketch, the number “0” will be substituted for the name by the compiler.
After providing the names for pins, the next step is to declare any variables required by the
sketch. In this example, the output from the IR sensor will be converted from an analog to a digital
value using the built-in Arduino “analogRead” function. A detailed description of the function may
be accessed via the Help menu. It is essential to carefully review the support documentation for a
built-in Arduino function the first time it is used. The documentation provides details on variables
required by the function, variables returned by the function, and an explanation on function operation.
2.7. APPLICATION 2: ART PIECE ILLUMINATION SYSTEM 47
The “analogRead” function requires the pin for analog conversion variable passed to it and
returns the analog signal read as an integer value (int) from 0 to 1023. So, for this example, we
need to declare an integer value to receive the returned value. We have called this integer variable
“IR_sensor_value.”
Following the declaration of required variables are the two required functions for an Arduino
Duemilanove program: setup and loop. The setup function calls an Arduino built-in function, pin-
Mode, to set the “LED_PIN” as an output pin. The loop function calls several functions to read the
current analog value on pin 5 (the IR sensor output) and then determine if the reading is above 512
(2.5 VDC). If the reading is above 2.5 VDC, the LED on DIGITAL pin 0 is illuminated, else it is
turned off.
After completing writing the sketch with the Arduino Development Environment, it must be
compiled and then uploaded to the Arduino Duemilanove board. These two steps are accomplished
using the “Sketch – Verify/Compile” and the “File – Upload to I/O Board” pull down menu selections.
In the next example, we adapt the IR sensor project to provide custom lighting for an art
piece.
2.8 SUMMARY
The goal of this chapter was to provide a tutorial on how to begin programming. We used a top-
down design approach. We began with the “big picture” of the chapter followed by an overview
of the major pieces of a program. We then discussed the basics of the C programming language.
Only the most fundamental concepts were covered. We then discussed the Arduino Development
Environment and how it may be used to develop a program for the Arduino Duemilanove processor
48 2. PROGRAMMING
Figure 2.14: Lac Laronge, Saskatchewan. Image used with permission, Jonny Barrett, Closer to the Sun
Fine Art and Design, Park City, Utah. [www.closertothesunfineartndesign.com]
board. Throughout the chapter, we provided examples and also provided references to a number of
excellent references.
2.9 REFERENCES
• ImageCraft Embedded Systems C Development Tools, 706 Colorado Avenue, #10-88, Palo
Alto, CA, 94303, www.imagecraft.com
• S. F. Barrett and D.J. Pack, Embedded Systems Design and Applications with the 68HC12
and HCS12, Pearson Prentice Hall, 2005.
• Jonny Barrett, Closer to the Sun Fine Art and Design, Park City, UT,
www.closertothesunfineartndesign.com
2.10. CHAPTER PROBLEMS 49
• Barrett S, Pack D (2006) Microcontrollers Fundamentals for Engineers and Scientists. Morgan
and Claypool Publishers. DOI: 10.2200/S00025ED1V01Y200605DCS001
• Barrett S and Pack D (2008) Atmel AVR Microcontroller Primer Programming and Inter-
facing. Morgan and Claypool Publishers. DOI: 10.2200/S00100ED1V01Y200712DCS015
• Barrett S (2010) Embedded Systems Design with the Atmel AVR Microcontroller. Morgan
and Claypool Publishers. DOI: 10.2200/S00225ED1V01Y200910DCS025
5. What are the three pieces of code required for a program function?
7. Provide the C program statement to set PORTB pins 1 and 7 to logic one. Use bit-twiddling
techniques.
8. Provide the C program statement to reset PORTB pins 1 and 7 to logic zero. Use bit-twiddling
techniques.
10. When should a switch statement be used versus the if-then statement construct?
11. What is the serial monitor feature used for in the Arduino Development Environment?
12. Describe what variables are required and returned and the basic function of the following
built-in Arduino functions: Blink, Analog Input.
51
CHAPTER 3
To answer these questions, the designer interacts with the client to ensure clear agreement
on what is to be done. If you are completing this project for yourself, you must still carefully and
thoughtfully complete this step. The establishment of clear, definable system requirements may
require considerable interaction between the designer and the client. It is essential that both parties
agree on system requirements before proceeding further in the design process. The final result of
this step is a detailed listing of system requirements and related specifications.
Project Description
- What is the system supposed to do?
- Operating conditions and environment
- Formal requirements
Background Research
- Thoroughly understand desired requirements and features
- Determine applicable codes, guidelines, and protocols
- Determine interface requirements
Pre-Design
- Brainstorm possible solutions
- Thoroughly investigate alternatives
- Choose best possible solution
- Identify specific target microcontroller
- Choose a design approach
Implement Prototype
- Top down versus bottom up
- Develop low risk hardware test platform
- Software implementation
Preliminary Testing
- Develop test plan to insure requirements
have been met
- Test under anticipated conditions
- Test under abusive conditions
- Redo testing if errors found
- Test in low cost, low risk environment
- Full up test
no
Complete and Accurate Documentation
- System description
- Requirements
- Structure chart
- UML activity diagram
- Circuit diagram
- Well-documented code
- Test plan
Deliver Prototype
3.2.3 PRE-DESIGN
The goal of the pre-design step is to convert a thorough understanding of the project into possible
design alternatives. Brainstorming is an effective tool in this step. Here, a list of alternatives is devel-
oped. Since an embedded system typically involves both hardware and/or software, the designer can
investigate whether requirements could be met with a hardware only solution or some combination
of hardware and software. Generally, speaking a hardware only solution executes faster; however, the
design is fixed once fielded. On the other hand, a software implementation provides flexibility and
a typically slower execution speed. Most embedded design solutions will use a combination of both
hardware and software to capitalize on the inherent advantages of each.
Once a design alternative has been selected, the general partition between hardware and
software can be determined. It is also an appropriate time to select a specific hardware device to
implement the prototype design. If a microcontroller technology has been chosen, it is now time to
select a specific controller. This is accomplished by answering the following questions:
• What microcontroller systems or features i.e., ADC, PWM, timer, etc.) are required by the
design?
• How many input and output pins are required by the design?
• What is the maximum anticipated operating speed of the microcontroller expected to be?
Recall from Chapter 1 there are a wide variety of Arduino-based microcontrollers available
to the designer.
3.2.4 DESIGN
With a clear view of system requirements and features, a general partition determined between
hardware and software, and a specific microcontroller chosen, it is now time to tackle the actual
design. It is important to follow a systematic and disciplined approach to design. This will allow
for low stress development of a documented design solution that meets requirements. In the design
step, several tools are employed to ease the design process. They include the following:
• Using a Unified Modeling Language (UML) activity diagram to work out program flow, and
• Propelled through the maze using the two powered wheels provided in the Blinky 602A kit
and a third drag wheel for stability.
• Equipped with LEDs, one for each IR sensor, to indicate a wall has been detected by a specific
sensor.
Pre-design. With requirements clearly understood, the next step is normally to brainstorm
possible solutions. In this example, we have already decided to use the Arduino Duemilanove pro-
cessing board. Other alternatives include using analog or digital hardware to determine robot action
or another microcontroller.
Circuit diagram. The circuit diagram for the robot is provided in Figure 3.4. The three IR
sensors (left, middle, and right) will be mounted on the leading edge of the robot to detect maze
walls. The output from the sensors is fed to three Arduino Duemilanove ADC channels (ANALOG
IN 0-2). The robot motors will be driven by PWM channels (PWM: DIGITAL 11 and PWM:
DIGITAL 10). The Arduino Duemilanove is interfaced to the motors via a transistor (2N2222)
with enough drive capability to handle the maximum current requirements of the motor. Since the
microcontroller is powered at 5 VDC and the motors are rated at 3 VDC, two 1N4001 diodes are
placed in series with the motor. This reduces the supply voltage to the motor to be approximately 3
3.3. EXAMPLE: BLINKY 602A AUTONOMOUS MAZE NAVIGATING ROBOT SYSTEM DESIGN 59
Sharp GP2D12
IR sensor
ANALOG IN
0 123 4 5
3 21 0 9 8 76 5 4 32 1 0
Duemilanove
Arduino
1 1 1 1 DIGITAL
5VGnd
prototype
wall detected LEDs area
turn signals
Figure 3.3: Robot layout with the Arduino Duemilanove processing board.
60 3. EMBEDDED SYSTEMS DESIGN
VDC. The robot will be powered by a 9 VDC battery which is fed to a 5 VDC voltage regulator.
The details of the interface electronics are provided in a later chapter. To save on battery expense, it
is recommended to use a 9 VDC, 2A rated inexpensive, wall-mount power supply to provide power
to the 5 VDC voltage regulator. A power umbilical of braided wire may be used to provide power
to the robot while navigating about the maze.
Structure chart: The structure chart for the robot project is provided in Figure 3.5.
UML activity diagrams: The UML activity diagram for the robot is provided in Figure 3.6.
Arduino Duemilanove Program: We will develop the entire control algorithm for the Ar-
duino Duemilanove board in the Application sections in the remainder of the book. We get started
on the control algorithm in the next section.
ANALOG IN
0 123 4 5
1N4001 1N4001
1N4001 + + 1N4001
Arduino
5VGnd
3 VDC 3 VDC protection
Duemilanove
M M diode
at 100 mA at 100 mA
-
3 21 0 9 8 76 5 4 32 1 0
1 1 1 1 DIGITAL
-
240 240
D10 2N2222 motor
2N2222 D11
current
right motor/wheel
left motor/wheel interface
interface
determine_robot
_action
robot sensor
action data
digital
motor_control ADC
input/output
desired
motor ch for conv
action conv data
left wall right
PWM_left ADC
PWM_right ReadADC turn detect turn
Initialize
signal LEDS signal
turns signals are flashed and a 1.5 s total delay is provided. This provides the robot 1.5 s to render a
turn. This delay may need to be adjusted during the testing phase.
//*************************************************************************
//analog input pins
#define left_IR_sensor 0 //analog pin - left IR sensor
include files
global variables
function prototypes
initialize ports
initialize ADC
initialize PWM
setup()
- configure pins for output
define global variables
while(1)
loop()
read sensor outputs
(left, middle, right)
determine robot
illuminate LEDs action
- wall detected
illuminate LEDs
issue motor - wall detected
control signals
issue motor
illuminate LEDs control signals
- turn signals
illuminate LEDs
- turn signals
delay - delay
Left Middle Right Wall Wall Wall Left Right Left Right
Sensor Sensor Sensor Left Middle Right Motor Motor Signal Signal Comments
0 0 0 0 0 0 0 1 1 0 0 Forward
1 0 0 1 0 0 1 1 1 0 0 Forward
2 0 1 0 0 1 0 1 0 0 1 Right
3 0 1 1 0 1 1 0 1 1 0 Left
4 1 0 0 1 0 0 1 1 0 0 Forward
5 1 0 1 1 0 1 1 1 0 0 Forward
6 1 1 0 1 1 0 1 0 0 1 Right
7 1 1 1 1 1 1 1 0 0 1 Right
//motor outputs
#define left_motor 11 //digital pin - left_motor
#define right_motor 10 //digital pin - right_motor
void setup()
{
//LED indicators - wall detectors
pinMode(wall_left, OUTPUT); //configure pin 1 for digital output
pinMode(wall_center, OUTPUT); //configure pin 2 for digital output
pinMode(wall_right, OUTPUT); //configure pin 3 for digital output
void loop()
{
//read analog output from IR sensors
left_IR_sensor_value = analogRead(left_IR_sensor);
center_IR_sensor_value = analogRead(center_IR_sensor);
right_IR_sensor_value = analogRead(right_IR_sensor);
Testing the control algorithm: It is recommended that the algorithm be first tested without
the entire robot platform. This may be accomplished by connecting the three IR sensors and LEDS
to the appropriate pins on the Arduino Duemilanove as specified in Figure 3.4. In place of the two
motors and their interface circuits, two LEDs with the required interface circuitry may be used. The
LEDs will illuminate to indicate the motors would be on during different test scenarios. Once this
algorithm is fully tested in this fashion, the Arduino Duemilanove may be mounted to the robot
platform and connected to the motors. Full up testing in the maze may commence. Enjoy!
3.5 SUMMARY
In this chapter, we discussed the design process, related tools, and applied the process to a real world
design. As previously mentioned, this design example will be periodically revisited throughout the
text. It is essential to follow a systematic, disciplined approach to embedded systems design to
successfully develop a prototype that meets established requirements.
3.6 REFERENCES
• M. Anderson, Help Wanted: Embedded Engineers Why the United States is losing its edge
in embedded systems, IEEE-USA Today’s Engineer, Feb 2008.
• Barrett S, Pack D (2006) Microcontrollers Fundamentals for Engineers and Scientists. Morgan
and Claypool Publishers. DOI: 10.2200/S00025ED1V01Y200605DCS001
• Barrett S and Pack D (2008) Atmel AVR Microcontroller Primer Programming and Inter-
facing. Morgan and Claypool Publishers. DOI: 10.2200/S00100ED1V01Y200712DCS015
• Barrett S (2010) Embedded Systems Design with the Atmel AVR Microcontroller. Morgan
and Claypool Publishers. DOI: 10.2200/S00225ED1V01Y200910DCS025
• M. Fowler with K. Scott “UML Distilled - A Brief Guide to the Standradr Object Modeling
Language,” 2nd edition. Boston:Addison-Wesley, 2000.
72 3. EMBEDDED SYSTEMS DESIGN
• N. Dale and S.C. Lilly “Pascal Plus Data Structures,” 4th edition. Englewood Cliffs, NJ: Jones
and Bartlett, 1995.
3. What is the purpose of the structure chart, UML activity diagram, and circuit diagram?
4. Why is a system design only as good as the test plan that supports it?
5. During the testing process, when an error is found and corrected, what should now be accom-
plished?
9. Update the robot action truth table if the robot was equipped with four IR sensors.
73
CHAPTER 4
Serial Communication
Subsystem
Objectives: After reading this chapter, the reader should be able to
• Describe the differences between serial and parallel communication.
• Provide definitions for key serial communications terminology.
• Describe the operation of the Universal Synchronous and Asynchronous Serial Receiver and
Transmitter (USART).
• Program the USART for basic transmission and reception using the built-in features of the
Arduino Development Environment.
• Program the USART for basic transmission and reception using C.
• Describe the operation of the Serial Peripheral Interface (SPI).
• Program the SPI system using the built-in features of the Arduino Development Environment.
• Program the SPI system using C.
• Describe the purpose of the Two Wire Interface (TWI).
• Program the Arduino Duemilanove processing board using ISP programming techniques.
4.1 OVERVIEW
Serial communication techniques provide a vital link between the Arduino Duemilanove processing
board an certain input devices, output devices, and other microcontrollers. In this chapter, we inves-
tigate the serial communication features beginning with a review of serial communication concepts
and terminology. We then investigate in turn the following serial communication systems available
on the Arduino Duemilanove processing board: the Universal Synchronous and Asynchronous Se-
rial Receiver and Transmitter (USART), the Serial Peripheral Interface (SPI) and the Two Wire
Interface (TWI). We provide guidance on how to program the USART and SPI using built-in
Arduino Development Environment features and the C programming language. We conclude the
chapter with examples on how to connect an SD card to the Arduino Duemilanove and also how
to program using In System Programming (ISP) techniques.
74 4. SERIAL COMMUNICATION SUBSYSTEM
Figure 4.1: ASCII Code.The ASCII code is used to encode alphanumeric characters.The “0x” indicates
hexadecimal notation in the C programming language.
the beginning and end of each data byte in a transmission sequence. The Atmel USART also has
synchronous features. Space does not permit a discussion of these USART enhancements.
The ATmega328 USART is quite flexible. It has the capability to be set to a variety of data
transmission or Baud (bits per second) rates. The USART may also be set for data bit widths of
5 to 9 bits with one or two stop bits. Furthermore, the ATmega328 is equipped with a hardware
generated parity bit (even or odd) and parity check hardware at the receiver. A single parity bit allows
for the detection of a single bit error within a byte of data. The USART may also be configured to
operate in a synchronous mode. We now discuss the operation, programming, and application of
the USART. Due to space limitations, we cover only the most basic capability of this flexible and
powerful serial communication system.
Figure 4.2: Atmel AVR ATmega328 USART block diagram. (Figure used with permission of Atmel,
Incorporated.)
78 4. SERIAL COMMUNICATION SUBSYSTEM
4.4.1.1 USART Clock Generator
The USART Clock Generator provides the clock source for the USART system and sets the
Baud rate for the USART. The Baud Rate is derived from the overall microcontroller clock source.
The overall system clock is divided by the USART Baud rate Registers UBRR[H:L] and several
additional dividers to set the Baud rate. For the asynchronous normal mode (U2X bit = 0), the Baud
Rate is determined using the following expression:
UBRRL
UBRR7 UBRR6 UBRR5 UBRR4 UBRR3 UBRR2 UBRR1 UBRR0
7 0
USART Control and Status Register A (UCSRA) The UCSRA register contains the RXC, TXC,
and the UDRE bits. The function of these bits have already been discussed.
USART Control and Status Register B (UCSRB) The UCSRB register contains the Receiver
Enable (RXEN) bit and the Transmitter Enable (TXEN) bit. These bits are the “on/off ” switch
for the receiver and transmitter, respectively. The UCSRB register also contains the UCSZ2 bit.
80 4. SERIAL COMMUNICATION SUBSYSTEM
The UCSZ2 bit in the UCSRB register and the UCSZ[1:0] bits contained in the UCSRC register
together set the data character size.
USART Control and Status Register C (UCSRC) The UCSRC register allows the user to customize
the data features to the application at hand. It should be emphasized that both the transmitter and
receiver be configured with the same data features for proper data transmission.The UCSRC contains
the following bits:
• USART Parity Mode (UPM[1:0])- 00: no parity, 10: even parity, 11: odd parity
• USART Character Size (data width) (UCSZ[2:0]) – 000: 5-bit, 001: 6-bit, 010: 7-bit, 011:
8-bit, 111: 9-bit
Command Description
Serial.end() Disables serial communication. Allows Digital 1(TX) and Digital (0) RX
to be used for digital input and output.
Serial.available() Determines how many bytes have already been received and stored in the
128 byte buffer.
Serial.print() Prints data to the serial port as ASCII text. An optional second parameter
specifies the format for printing (BYTE, BIN, OCT, DEC, HEX).
Serial.println() Prints data to the serial port as ASCII text followed by a carriage return.
Serial.write() Writes binary data to the serial port. A single byte, a series of bytes, or an
array of bytes may be sent.
algorithm to provide a status update. These status updates are handy during sketch development.
These status updates would not be available while the robot is progressing through the maze since
the robot would no longer be connected to the host PC via the USB cable.
//*************************************************************************
//analog input pins
#define left_IR_sensor 0 //analog pin - left IR sensor
//motor outputs
#define left_motor 11 //digital pin - left_motor
#define right_motor 10 //digital pin - right_motor
void setup()
{
Serial.begin(9600); //set USART Baud rate to 9600
//LED indicators - wall detectors
pinMode(wall_left, OUTPUT); //configure pin 1 for digital output
pinMode(wall_center, OUTPUT); //configure pin 2 for digital output
pinMode(wall_right, OUTPUT); //configure pin 3 for digital output
void loop()
{
//read analog output from IR sensors
left_IR_sensor_value = analogRead(left_IR_sensor);
center_IR_sensor_value = analogRead(center_IR_sensor);
right_IR_sensor_value = analogRead(right_IR_sensor);
Set USART
no Has UDRE communication parameters no Has RXC
flag set? (data bits, stop bit, parity) flag set?
yes yes
Load UDR register with Turn on transmitter Retrieve received data
data byte for transmission and/or receiver from UDR register
b) USART initialization
To program the USART, we implement the flow diagrams provided in Figure 4.5. In the
sample code provided, we assume the ATmega328 is operating at 10 MHz, and we desire a Baud
Rate of 9600, asynchronous operation, no parity, one stop bit, and eight data bits.
To achieve 9600 Baud with an operating frequency of 10 MHz requires that we set the UBRR
registers to 64 which is 0x40.
//*************************************************************************
//USART_init: initializes the USART system
//*************************************************************************
void USART_init(void)
{
UCSRA = 0x00; //control register initialization
UCSRB = 0x08; //enable transmitter
UCSRC = 0x86; //async, no parity, 1 stop bit, 8 data bits
//Baud Rate initialization
UBRRH = 0x00;
UBRRL = 0x40;
4.6. SYSTEM OPERATION AND PROGRAMMING IN C 85
}
//*************************************************************************
//USART_transmit: transmits single byte of data
//*************************************************************************
//*************************************************************************
//USART_receive: receives single byte of data
//*************************************************************************
//*************************************************************************
MISO MISO
SPI Data Register (SDR) (PB6) (PB6) SPI Data Register (SDR)
MSB LSB MSB LSB
MOSI MOSI
(PB5) (PB5)
SCK SCK shift
system enable
clock SPI Clock Generator SCK SCK
(PB7) (PB7)
SPI Status Register (SPSR)
SPI Control Register (SPCR) The SPI Control Register (SPCR) contains the “on/off ” switch for
the SPI system. It also provides the flexibility for the SPI to be connected to a wide variety of devices
with different data formats. It is important that both the SPI master and slave devices be configured
for compatible data formats for proper data transmission. The SPCR contains the following bits:
• SPI Enable (SPE) is the “on/off ” switch for the SPI system. A logic one turns the system on
and logic zero turns it off.
• Data Order (DORD) allows the direction of shift from master to slave to be controlled. When
the DORD bit is set to one, the least significant bit (LSB) of the SPI Data Register (SPDR)
is transmitted first. When the DORD bit is set to zero the Most Significant Bit (MSB) of the
SPDR is transmitted first.
• The Master/Slave Select (MSTR) bit determines if the SPI system will serve as a master (logic
one) or slave (logic zero).
• The Clock Polarity (CPOL) bit allows determines the idle condition of the SCK pin. When
CPOL is one, SCK will idle logic high; whereas, when CPOL is zero, SCK will idle logic
zero.
• The Clock Phase (CPHA) determines if the data bit will be sampled on the leading (0) or
trailing (1) edge of the SCK.
88 4. SERIAL COMMUNICATION SUBSYSTEM
• The SPI SCK is derived from the microcontroller’s system clock source. The system clock
is divided down to form the SPI SCK. The SPI Clock Rate Select bits SPR[1:0] and the
Double SPI Speed Bit (SPI2X) are used to set the division factor. The following divisions may
be selected using SPI2X, SPR1, SPR0:
SPI Status Register (SPSR) The SPSR contains the SPI Interrupt Flag (SPIF). The flag sets when
eight data bits have been transferred from the master to the slave. The SPIF bit is cleared by first
reading the SPSR after the SPIF flag has been set and then reading the SPI Data Register (SPDR).
The SPSR also contains the SPI2X bit used to set the SCK frequency.
SPI Data Register (SPDR) As previously mentioned, writing a data byte to the SPDR initiates
SPI transmission.
• dataPin: the Arduino Duemilanove DIGITAL pin to be used for serial output.
• clockPin: the Arduino Duemilanove DIGITAL pin to be used for the clock.
• bitOrder: indicates whether the data byte will be sent most significant bit first (MSBFIRST)
or least significant bit first (LSBFIRST).
To use the shiftOut command, the appropriate pins are declared as output using the pinMode
command in the setup() function. The shiftOut command is then called at the appropriate place
within the loop() function using the following syntax:
4.8. SPI PROGRAMMING IN C 89
shiftOut(dataPin, clockPin, LSBFIRST, value);
As a result of the this command, the value specified will be serially shifted out of the data pin
specified, least significant bit first, at the clock rate provided at the clock pin.
//*************************************************************************
//spi_init: initializes spi system
//*************************************************************************
//*************************************************************************
//spi_write: Used by SPI master to transmit a data byte
//*************************************************************************
//*************************************************************************
//spi_read: Used by SPI slave to receive data byte
//*************************************************************************
return SPDR;
}
//*************************************************************************
1 20 5 VDC
SOUT TXD
SIN RXD
GND GND
DNLD SDIN 10K
VCC
RST
10 11
SD Card
3 21 0 9 8 76 5 4 32 1 0
1 1 1 1 DIGITAL
RX
TX
Arduino
Duemilanove
ANALOG IN
5VGnd 0 123 4 5
Figure 4.8: Arduino Duemilanove and SD/MMC card interface circuit [Comfile Technology].
stored and the file option. In this example we have used the “w” option to indicate a write to the file.
The phrase for storage is then provided followed by a carriage return (\r) and a line feed (\n).
Before data can be written to the file, some preparatory steps are required:
• Set the Baud rate for communication.
• Set the SD/MMC for MCU (microcontroller) mode.This mode provides simplified responses
back to the Arduino Duemilanove.
• Initialize the SD/MMC card.
• Create a file.
Commands are provided for each of these actions in Figure 4.9. We will provide a complete
sketch to communicate with the SD/MMC in the Applications section of the next chapter.
Once data has been written to an SD/MMC card, it may be removed from the card socket
in the breakout board and read via a PC using a universal card reader. Universal card readers are
92 4. SERIAL COMMUNICATION SUBSYSTEM
Command format:
Command [Filename] [Option] [Data] [CR] [LF]
3. Connect the STK500 as shown in Figure 4.10. Note: For ISP programming, the 6-wire ribbon
cable is connected from the ISP6PIN header pin on the STK500 to the 6-pin header pin on
the Arduino Duemilanove, not the position of the red guide wire in the diagram.
ATMEL to power
red
AVR supply
wire
VTARGET
RESET
PORTB RS232
cable
XTAL1 to host PC
ribbon
cables
OSCSEL
PORTC RS232
CTRL
red
wire BSEL2
PORTD
SPROG3 PJUMP
RS232
LEDS
SPARE
Note: red wire ISP6PIN
PORTE ISP10PIN
ribbon cable
3 21 0 9 8 76 5 4 32 1 0 connecting
1 11 1 STK500 ISP6PIN
to Arduino Duemilanove
6-pin header
Figure 4.10: Programming the ATmega328 onboard the Arduino Duemilanove with the STK500.
4.12. SUMMARY 95
5. Start up AVR Studio on your PC.
6. Pop up window “Welcome to AVR Studio” should appear. Close this window by clicking on
the “Cancel button.”
7. Click on the “AVR icon.” It looks like the silhouette of an integrated circuit. It is on the second
line of the toolbar about half way across the screen.
8. This should bring up a STK500 pop up window with eight tabs (Main, Program, Fuses, Lock-
bits, Advanced, HW Settings, HW Info). At the bottom of the Main tab window, verify that
the STK500 was autodetected. Troubleshoot as necessary to ensure STK500 was autodetected
by AVR Studio.
• Main:
– Device and Signature Bytes: ATmega328P
– Programming Mode and Target Setting: ISP Mode
– Depress “Read Signature” to insure the STK500 is communicating with the Arduino
Duemilanove
• Program:
– Flash: Input HEX file, Browse and find machine code file: <yourfilename.hex>
– EEPROM: Input HEX file, Browse and find machine code file:
<yourfilename.EEP>
11. Power down the STK500. Disconnect the STK500 from the Arduino Duemilanove processing
board.
4.12 SUMMARY
In this chapter, we have discussed the differences between parallel and serial communications and
key serial communication related terminology. We then in turn discussed the operation of USART,
SPI and TWI serial communication systems. We also provided basic code examples to communicate
with the USART and SPI systems.
96 4. SERIAL COMMUNICATION SUBSYSTEM
4.13 REFERENCES
• Atmel 8-bit AVR Microcontroller with 4/8/16/32K Bytes In-System Programmable Flash, AT-
mega48PA/88PA/168PA/328P data sheet: 8161D-AVR-10/09, Atmel Corporation, 2325 Or-
chard Parkway, San Jose, CA 95131.
• Barrett S, Pack D (2006) Microcontrollers Fundamentals for Engineers and Scientists. Morgan
and Claypool Publishers. DOI: 10.2200/S00025ED1V01Y200605DCS001
• Barrett S and Pack D (2008) Atmel AVR Microcontroller Primer Programming and Inter-
facing. Morgan and Claypool Publishers. DOI: 10.2200/S00100ED1V01Y200712DCS015
• Barrett S (2010) Embedded Systems Design with the Atmel AVR Microcontroller. Morgan
and Claypool Publishers. DOI: 10.2200/S00225ED1V01Y200910DCS025
2. Summarize the differences between the USART, SPI, and TWI methods of serial communi-
cation.
3. Draw a block diagram of the USART system, label all key registers, and all keys USART flags.
4. Draw a block diagram of the SPI system, label all key registers, and all keys USART flags.
7. Draw the schematic of a system consisting of two ATmega328s that will exchange data via
the SPI system.
8. Write the code to implement the system described in the question above.
97
Author’s Biography
STEVEN F. BARRETT
Steven F. Barrett, Ph.D., P.E., received the BS Electronic Engineering Technology from the
University of Nebraska at Omaha in 1979, the M.E.E.E. from the University of Idaho at Moscow
in 1986, and the Ph.D. from The University of Texas at Austin in 1993. He was formally an active
duty faculty member at the United States Air Force Academy, Colorado and is now the Associate
Dean of Academic Programs at the University of Wyoming. He is a member of IEEE (senior)
and Tau Beta Pi (chief faculty advisor). His research interests include digital and analog image
processing, computer-assisted laser surgery, and embedded controller systems. He is a registered
Professional Engineer in Wyoming and Colorado. He co-wrote with Dr. Daniel Pack six textbooks
on microcontrollers and embedded systems. In 2004, Barrett was named “Wyoming Professor of the
Year” by the Carnegie Foundation for the Advancement of Teaching and in 2008 was the recipient of
the National Society of Professional Engineers (NSPE) Professional Engineers in Higher Education,
Engineering Education Excellence Award.
99
Index
Embedded Systems Interfacing for Engineers using the Freescale HCS08 Microcontroller
II: Digital and Analog Hardware Interfacing
Douglas H. Summerville
2009
Embedded Systems Interfacing for Engineers using the Freescale HCS08 Microcontroller
I: Assembly Language Programming
Douglas H.Summerville
2009
Pragmatic Power
William J. Eccles
2008
All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, or transmitted in
any form or by any means—electronic, mechanical, photocopy, recording, or any other except for brief quotations in
printed reviews, without the prior permission of the publisher.
DOI 10.2200/S00283ED1V01Y201005DCS029
Lecture #29
Series Editor: Mitchell A. Thornton, Southern Methodist University
Series ISSN
Synthesis Lectures on Digital Circuits and Systems
Print 1932-3166 Electronic 1932-3174
Arduino Microcontroller
Processing for Everyone!
Part II
Steven F. Barrett
University of Wyoming, Laramie, WY
M
&C Morgan & cLaypool publishers
ABSTRACT
This book is about the Arduino microcontroller and the Arduino concept. The visionary Arduino
team of Massimo Banzi, David Cuartielles,Tom Igoe, Gianluca Martino, and David Mellis launched
a new innovation in microcontroller hardware in 2005, the concept of open source hardware. Their
approach was to openly share details of microcontroller-based hardware design platforms to stimulate
the sharing of ideas and promote innovation. This concept has been popular in the software world
for many years. This book is intended for a wide variety of audiences including students of the fine
arts, middle and senior high school students, engineering design students, and practicing scientists
and engineers. To meet this wide audience, the book has been divided into sections to satisfy the
need of each reader. The book contains many software and hardware examples to assist the reader in
developing a wide variety of systems. For the examples, the Arduino Duemilanove and the Atmel
ATmega328 is employed as the target processor.
KEYWORDS
Arduino microcontroller, Arduino Duemilanove, Atmel microcontroller, Atmel AVR,
ATmega328, microcontroller interfacing, embedded systems design
ix
Contents
Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .xv
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323
Preface
This book is about the Arduino microcontroller and the Arduino concept. The visionary
Arduino team of Massimo Banzi, David Cuartielles, Tom Igoe, Gianluca Martino, and David Mellis
launched a new innovation in microcontroller hardware in 2005, the concept of open source hardware.
There approach was to openly share details of microcontroller-based hardware design platforms to
stimulate the sharing of ideas and innovation. This concept has been popular in the software world
for many years.
This book is written for a number of audiences. First, in keeping with the Arduino concept,
the book is written for practitioners of the arts (design students, artists, photographers, etc.) who may
need processing power in a project but do not have an in depth engineering background. Second, the
book is written for middle school and senior high school students who may need processing power
for a school or science fair project. Third, we write for engineering students who require processing
power for their senior design project but do not have the background in microcontroller-based appli-
cations commonly taught in electrical and computer engineering curricula. Finally, the book provides
practicing scientists and engineers an advanced treatment of the Atmel AVR microcontroller.
Steve Barrett
97
CHAPTER 5
• Assess the quality of analog-to-digital conversion using the metrics of sampling rate, quanti-
zation levels, number of bits used for encoding and dynamic range.
• Program the Arduino Duemilanove processing board to perform an ADC using the built-in
features of the Arduino Development Environment.
5.1 OVERVIEW
A microcontroller is used to process information from the natural world, decide on a course of action
based on the information collected, and then issue control signals to implement the decision. Since
the information from the natural world, is analog or continuous in nature, and the microcontroller
is a digital or discrete based processor, a method to convert an analog signal to a digital form is
required. An ADC system performs this task while a digital to analog converter (DAC) performs
the conversion in the opposite direction. We will discuss both types of converters in this chapter.
Most microcontrollers are equipped with an ADC subsystem; whereas, DACs must be added as an
external peripheral device to the controller.
In this chapter, we discuss the ADC process in some detail. In the first section, we discuss
the conversion process itself, followed by a presentation of the successive-approximation hardware
implementation of the process. We then review the basic features of the ATmega328 ADC system
98 5. ANALOG TO DIGITAL CONVERSION (ADC)
followed by a system description and a discussion of key ADC registers.We conclude our discussion of
the analog-to-digital converter with several illustrative code examples. We show how to program the
ADC using the built-in features of the Arduino Development Environment and C. We conclude the
chapter with a discussion of the DAC process and interface a multi-channel DAC to the ATmega328.
We also discuss the Arduino Development Environment built-in features that allow generation of
an output analog signal via pulse width modulation (PWM) techniques. Throughout the chapter,
we provide detailed examples.
In this subsection, we provide an abbreviated discussion of the ADC process. This discussion was
condensed from “Atmel AVR Microcontroller Primer Programming and Interfacing.” The interested
reader is referred to this text for additional details and examples [Barrett and Pack]. We present three
important processes associated with the ADC: sampling, quantization, and encoding.
Sampling. We first start with the subject of sampling. Sampling is the process of taking ‘snap
shots’ of a signal over time. When we sample a signal, we want to sample it in an optimal fashion such
that we can capture the essence of the signal while minimizing the use of resources. In essence, we
want to minimize the number of samples while retaining the capability to faithfully reconstruct the
original signal from the samples. Intuitively, the rate of change in a signal determines the number of
samples required to faithfully reconstruct the signal, provided that all adjacent samples are captured
with the same sample timing intervals.
Sampling is important since when we want to represent an analog signal in a digital system,
such as a computer, we must use the appropriate sampling rate to capture the analog signal for a
faithful representation in digital systems. Harry Nyquist from Bell Laboratory studied the sampling
process and derived a criterion that determines the minimum sampling rate for any continuous
analog signals. His, now famous, minimum sampling rate is known as the Nyquist sampling rate,
which states that one must sample a signal at least twice as fast as the highest frequency content
of the signal of interest. For example, if we are dealing with the human voice signal that contains
frequency components that span from about 20 Hz to 4 kHz, the Nyquist sample theorem requires
that we must sample the signal at least at 8 kHz, 8000 ‘snap shots’ every second. Engineers who work
for telephone companies must deal with such issues. For further study on the Nyquist sampling rate,
refer to Pack and Barrett listed in the References section.
When a signal is sampled a low pass anti-aliasing filter must be employed to insure the Nyquist
sampling rate is not violated. In the example above, a low pass filter with a cutoff frequency of 4
KHz would be used before the sampling circuitry for this purpose.
Quantization. Now that we understand the sampling process, let’s move on to the second
process of the analog-to-digital conversion, quantization. Each digital system has a number of bits it
uses as the basic unit to represent data. A bit is the most basic unit where single binary information,
one or zero, is represented. A nibble is made up of four bits put together. A byte is eight bits.
5.2. SAMPLING, QUANTIZATION AND ENCODING 99
We have tacitly avoided the discussion of the form of captured signal samples. When a signal
is sampled, digital systems need some means to represent the captured samples. The quantization
of a sampled signal is how the signal is represented as one of the quantization levels. Suppose you
have a single bit to represent an incoming signal. You only have two different numbers, 0 and 1. You
may say that you can distinguish only low from high. Suppose you have two bits. You can represent
four different levels, 00, 01, 10, and 11. What if you have three bits? You now can represent eight
different levels: 000, 001, 010, 011, 100, 101, 110, and 111. Think of it as follows. When you had
two bits, you were able to represent four different levels. If we add one more bit, that bit can be one
or zero, making the total possibilities eight. Similar discussion can lead us to conclude that given n
bits, we have 2n unique numbers or levels one can represent.
Figure 5.1 shows how n bits are used to quantize a range of values. In many digital systems,
the incoming signals are voltage signals. The voltage signals are first obtained from physical signals
(pressure, temperature, etc.) with the help of transducers, such as microphones, angle sensors, and
infrared sensors. The voltage signals are then conditioned to map their range with the input range of
a digital system, typically 0 to 5 volts. In Figure 5.1, n bits allow you to divide the input signal range
of a digital system into 2n different quantization levels. As can be seen from the figure, the more
quantization levels means the better mapping of an incoming signal to its true value. If we only had
a single bit, we can only represent level 0 and level 1. Any analog signal value in between the range
had to be mapped either as level 0 or level 1, not many choices. Now imagine what happens as we
increase the number of bits available for the quantization levels. What happens when the available
number of bits is 8? How many different quantization levels are available now? Yes, 256. How about
10, 12, or 14? Notice also that as the number of bits used for the quantization levels increases for a
given input range the ‘distance’ between two adjacent levels decreases accordingly.
Finally, the encoding process involves converting a quantized signal into a digital binary num-
ber. Suppose again we are using eight bits to quantize a sampled analog signal. The quantization
levels are determined by the eight bits and each sampled signal is quantized as one of 256 quanti-
zation levels. Consider the two sampled signals shown in Figure 5.1. The first sample is mapped to
quantization level 2 and the second one is mapped to quantization level 198. Note the amount of
quantization error introduced for both samples. The quantization error is inversely proportional to
the number of bits used to quantize the signal.
Encoding. Once a sampled signal is quantized, the encoding process involves representing
the quantization level with the available bits. Thus, for the first sample, the encoded sampled value
is 0000_0001, while the encoded sampled value for the second sample is 1100_0110. As a result of
the encoding process, sampled analog signals are now represented as a set of binary numbers. Thus,
the encoding is the last necessary step to represent a sampled analog signal into its corresponding
digital form, shown in Figure 5.1.
100 5. ANALOG TO DIGITAL CONVERSION (ADC)
Analog
Signal
sampled value 1
Xmax
V1max V2max
V1min
K
V2min
Xmin Scalar
Multiplier
Input Transducer ADC Input
B
(Bias)
Figure 5.2: A block diagram of the signal conditioning for an analog-to-digital converter. The range of
the sensor voltage output is mapped to the analog-to-digital converter input voltage range. The scalar
multiplier maps the magnitudes of the two ranges and the bias voltage is used to align two limits.
The output of the input transducer is first scaled by constant K. In the figure, we use a
microphone as the input transducer whose output ranges from -5 VDC to + 5 VDC. The input
to the analog-to-digital converter ranges from 0 VDC to 5 VDC. The box with constant K maps
the output range of the input transducer to the input range of the converter. Naturally, we need to
multiply all input signals by 1/2 to accommodate the mapping. Once the range has been mapped,
the signal now needs to be shifted. Note that the scale factor maps the output range of the input
transducer as -2.5 VDC to +2.5 VDC instead of 0 VDC to 5 VDC. The second portion of the circuit
shifts the range by 2.5 VDC, thereby completing the correct mapping. Actual implementation of
the TID circuit components is accomplished using operational amplifiers.
In general, the scaling and bias process may be described by two equations:
5.3. ANALOG-TO-DIGITAL CONVERSION (ADC) PROCESS 103
V2max = (V1max × K) + B
V2min = (V1min × K) + B
The variable V1max represents the maximum output voltage from the input transducer. This
voltage occurs when the maximum physical variable (Xmax ) is presented to the input transducer.
This voltage must be scaled by the scalar multiplier (K) and then have a DC offset bias voltage (B)
added to provide the voltage V2max to the input of the ADC converter [USAFA].
Similarly, The variable V1min represents the minimum output voltage from the input trans-
ducer. This voltage occurs when the minimum physical variable (Xmin ) is presented to the input
transducer. This voltage must be scaled by the scalar multiplier (K) and then have a DC offset bias
voltage (B) added to produce voltage V2min to the input of the ADC converter.
Usually, the values of V1max and V1min are provided with the documentation for the transducer.
Also, the values of V2max and V2min are known. They are the high and low reference voltages for
the ADC system (usually 5 VDC and 0 VDC for a microcontroller). We thus have two equations
and two unknowns to solve for K and B. The circuits to scale by K and add the offset B are usually
implemented with operational amplifiers.
Example: A photodiode is a semiconductor device that provides an output current corre-
sponding to the light impinging on its active surface. The photodiode is used with a transimpedance
amplifier to convert the output current to an output voltage. A photodiode/transimpedance ampli-
fier provides an output voltage of 0 volts for maximum rated light intensity and -2.50 VDC output
voltage for the minimum rated light intensity. Calculate the required values of K and B for this light
transducer so it may be interfaced to a microcontroller’s ADC system.
V2max = (V1max × K) + B
V2min = (V1min × K) + B
5.0 V = (0 V × K) + B
0 V = (−2.50 V × K) + B
The values of K and B may then be determined to be 2 and 5 VDC, respectively.
Vcc
Vo
In
Vn - Vcc saturation
Ideal conditions:
-- In = Ip = 0
-Vcc
-- Vp = Vn saturation
-- Avol >> 50,000
-- Vo = Avol (Vp - Vn)
The op amp is an active device (requires power supplies) equipped with two inputs, a single
output, and several voltage source inputs. The two inputs are labeled Vp, or the non-inverting input,
and Vn, the inverting input. The output of the op amp is determined by taking the difference
between Vp and Vn and multiplying the difference by the open loop gain (Avol ) of the op amp
which is typically a large value much greater than 50,000. Due to the large value of Avol , it does not
take much of a difference between Vp and Vn before the op amp will saturate. When an op amp
saturates, it does not damage the op amp, but the output is limited to the supply voltages ±Vcc .
This will clip the output, and hence distort the signal, at levels slightly less than ±Vcc . Op amps
are typically used in a closed loop, negative feedback configuration. A sample of classic operational
amplifier configurations with negative feedback are provided in Figure 5.4 [Faulkenberry].
It should be emphasized that the equations provided with each operational amplifier circuit
are only valid if the circuit configurations are identical to those shown. Even a slight variation in the
circuit configuration may have a dramatic effect on circuit operation. It is important to analyze each
operational amplifier circuit using the following steps:
5.3. ANALOG-TO-DIGITAL CONVERSION (ADC) PROCESS 105
Rf
+Vcc
+Vcc -
Ri Vout = Vin
- +
Vin Vout = - (Rf / Ri)(Vin)
+ Vin -Vcc
-Vcc
Rf Ri Rf
Ri
+Vcc V1 +Vcc
- -
Vout = ((Rf + Ri)/Ri)(Vin) Vout = (Rf/Ri)(V2 -V1)
+ +
Vin -Vcc V2 -Vcc
Ri Rf
c) Non-inverting amplifier d) Differential input amplifier
R1
Rf Rf
V1
R2
V2 +Vcc +Vcc
R3
V3 - -
Vout = - (Rf / R1)(V1) Vout = - (I Rf)
+ +
- (Rf / R2)(V2) I
-Vcc -Vcc
- (Rf / R3)(V3)
Rf C
+Vcc Rf +Vcc
C
- -
Vin Vout = - Rf C (dVin/dt) Vin Vout = - 1/(Rf C) (Vindt)
+ +
-Vcc -Vcc
g) Differentiator h) Integrator
As an example, we provide the analysis of the non-inverting amplifier circuit in Figure 5.5.
This same analysis technique may be applied to all of the circuits in Figure 5.4 to arrive at the
equations for Vout provided.
Vn +Vcc
Ri Apply ideal conditions:
In
- In = Ip = 0
Vin Vout
Ip
+ Vn = Vp = 0 (since Vp is grounded)
Vp -Vcc
Solve node equation for Vout:
Figure 5.5: Operational amplifier analysis for the non-inverting amplifier. Adapted from [Faulkenberry].
Example: In the previous section, it was determined that the values of K and B were 2
and 5 VDC, respectively. The two-stage op amp circuitry provided in Figure 5.6 implements these
values of K and B. The first stage provides an amplification of -2 due to the use of the non-inverting
amplifier configuration. In the second stage, a summing amplifier is used to add the output of the
first stage with a bias of – 5 VDC. Since this stage also introduces a minus sign to the result, the
overall result of a gain of 2 and a bias of +5 VDC is achieved.
Rf = 20K
Rf = 10K
+Vcc
Ri = 10K +Vcc
- Ri = 10K
Vin -
+ -Vcc Vout
Ri = 10K +
-Vcc
bias = 5 VDC -Vcc
10K
Figure 5.6: Operational amplifier implementation of the transducer interface design (TID) example
circuit.
5.4.1 SUCCESSIVE-APPROXIMATION
The ATmega328 microcontroller is equipped with a successive-approximation ADC converter. The
successive-approximation technique uses a digital-to-analog converter, a controller, and a comparator
to perform the ADC process. Starting from the most significant bit down to the least significant
bit, the controller turns on each bit at a time and generates an analog signal, with the help of the
digital-to-analog converter, to be compared with the original input analog signal. Based on the
result of the comparison, the controller changes or leaves the current bit and turns on the next most
significant bit. The process continues until decisions are made for all available bits. Figure 5.7 shows
the architecture of this type of converter. The advantage of this technique is that the conversion time
is uniform for any input, but the disadvantage of the technology is the use of complex hardware for
implementation.
• 10-bit resolution
Analog
input signal
Analog
reference Digital-to-analog
signal converter Comparator
Start Controller
Conversion Serial digital output
Successive-approximation converter
Let’s discuss each feature in turn. The first feature of discussion is “10-bit resolution.” Reso-
lution is defined as:
5.5.2 REGISTERS
The key registers for the ADC system are shown in Figure 5.9. It must be emphasized that the ADC
system has many advanced capabilities that we do not discuss here. Our goal is to review the basic
ADC conversion features of this powerful system. We have already discussed many of the register
setting already. We will discuss each register in turn [Atmel].
ADMUX = 0x07;
The REFS[1:0] bits of the ADMUX register are also used to determine the reference voltage
source for the ADC system. These bits may be set to the following values:
Figure 5.8: Atmel AVR ATmega328 ADC block diagram. (Figure used with permission of Atmel,
Incorporated.)
5.5. THE ATMEL ATMEGA328 ADC SYSTEM 111
• REFS[1:1] = 11: Internal 1.1 VDC voltage reference with an external capacitor at the AREF
pin
return_value = analogRead(analog_pin_read);
The function returns an unsigned integer value from 0 to 1023, corresponding to the voltage
span from 0 to 5 VDC.
Recall that we introduced the use of this function in the Application section at the end of
Chapter 3. The analogRead function was used to read the analog output values from the three IR
sensors on the Blinky 602A robot.
//*************************************************************************
//InitADC: initialize analog-to-digital converter
//*************************************************************************
//*************************************************************************
//ReadADC: read analog voltage from analog-to-digital converter -
//the desired channel for conversion is passed in as an unsigned
//character variable.
//The result is returned as a right justified, 10 bit binary result.
//The ADC prescalar must be set to 8 to slow down the ADC clock at higher
//external clock frequencies (10 MHz) to obtain accurate results.
//*************************************************************************
//*************************************************************************
//*************************************************************************
5.8. EXAMPLE: ADC RAIN GAGE INDICATOR 115
Vcc = 5 V
220
Vcc = 5 V
2N2222
10K 220
2N2222
10K
Vcc = 5 V
220
Vcc = 5 V
2N2222
10K 220
2N2222
10K
Vcc = 5 V
220
Vcc = 5 V
2N2222
10K 220
3 21 0 9 8 76 5 4 32 1 0 2N2222
1 1 1 1 DIGITAL 10K
Arduino
Duemilanove
Vcc = 5 V
220
Vcc = 5 V
2N2222
10K 220
ANALOG IN
5VGnd 0 123 4 5
2N2222
10K
Vcc = 5 V
10K
void setup()
{
//LED indicators - wall detectors
pinMode(LED0, OUTPUT); //configure pin 0 for digital output
pinMode(LED1, OUTPUT); //configure pin 1 for digital output
pinMode(LED2, OUTPUT); //configure pin 2 for digital output
pinMode(LED3, OUTPUT); //configure pin 3 for digital output
pinMode(LED4, OUTPUT); //configure pin 4 for digital output
pinMode(LED5, OUTPUT); //configure pin 5 for digital output
pinMode(LED6, OUTPUT); //configure pin 6 for digital output
pinMode(LED7, OUTPUT); //configure pin 7 for digital output
}
void loop()
{
//read analog output from trim pot
trim_pot_reading = analogRead(trim_pot);
}
else
{
digitalWrite(LED0, HIGH);
digitalWrite(LED1, HIGH);
digitalWrite(LED2, HIGH);
5.8. EXAMPLE: ADC RAIN GAGE INDICATOR 119
digitalWrite(LED3, HIGH);
digitalWrite(LED4, HIGH);
digitalWrite(LED5, HIGH);
digitalWrite(LED6, HIGH);
digitalWrite(LED7, HIGH);
}
delay(500); //delay 500 ms
}
//*************************************************************************
Vcc = 5 V
220
Vcc = 5 V
2N2222
10K 220
2N2222
10K
Atmega328
1 PUR - PC6 PC5 28
Vcc = 5 V Vcc = 5 V
2 RXD1 - PD0 PC4 27
3 TXD1 - PD1 PC3 26 220
4 PD2 PC2 25 10K
VDD 1M sys reset 5 PD3 PC1 24
1.0 uF 6 PD4 PCO 23
7 Vcc Vcc = 5 V
GND 22 2N2222
8 GND AREF 21 VDD 10K 220
9 PB6 AVCC 20
10 PB7 PB5 19
11 PD5 PB4 18
12 PD6 PB3 17 2N2222
13 PD7 PB2 16 10K
14 PB0 PB1 15
Vcc = 5 V
220
Vcc = 5 V
2N2222
10K 220
2N2222
10K
Vcc = 5 V
220
Vcc = 5 V
2N2222
10K 220
2N2222
10K
//function prototypes
void display_increment(void); //Function
to display increment to PORTD
void display_decrement(void); //Function
to display decrement to PORTD
void rain_gage(void)
void InitADC(void); //Initialize ADC converter
unsigned int ReadADC(); //Read specified ADC channel
void delay_30ms(void); //Function to delay 30 ms
//*************************************************************************
int main(void)
{
display_increment(); //Display incrementing binary on
InitADC();
while(1)
{
rain_gage(); //Display gage info on PORTD
delay_30ms(); //Delay 30 ms
}
return 0;
}
122 5. ANALOG TO DIGITAL CONVERSION (ADC)
//*************************************************************************
//function definitions
//*************************************************************************
//*************************************************************************
//30 ms delay based on an 8MHz clock
//*************************************************************************
void delay_30ms(void)
{
int i, k;
//*************************************************************************
//Displays incrementing binary count from 0 to 255
//*************************************************************************
void display_increment(void)
{
int i;
unsigned char j = 0x00;
//*************************************************************************
//Displays decrementing binary count from 255 to 0
//*************************************************************************
void display_decrement(void)
{
int i;
unsigned char j = 0xFF;
//*************************************************************************
//Initializes ADC
//*************************************************************************
void InitADC(void)
{
ADMUX = 0; //Select channel 0
ADCSRA = 0xC3; //Enable ADC & start dummy conversion
//Set ADC module prescalar
//to 8 critical for
//accurate ADC results
while (!(ADCSRA & 0x10)); //Check if conversation is ready
ADCSRA |= 0x10; //Clear conv rdy flag - set the bit
}
//*************************************************************************
//ReadADC: read analog voltage from analog-to-digital converter -
//the desired channel for conversion is passed in as an unsigned
124 5. ANALOG TO DIGITAL CONVERSION (ADC)
//character variable. The result is returned as a right justified,
//10 bit binary result.
//The ADC prescalar must be set to 8 to slow down the ADC clock at
//higher external clock frequencies (10 MHz) to obtain accurate results.
//*************************************************************************
//*************************************************************************
//Displays voltage magnitude as LED level on PORTB
//*************************************************************************
void rain_gage(void)
{
unsigned int ADCValue;
ADCValue = readADC(0x00);
//*************************************************************************
#define trim_pot 0 //analog input pin
void setup()
{
DDRD = 0xFF; //Set PORTD (DIGITAL pins 7 to 0)
} //as output
void loop()
{
//read analog output from trim pot
trim_pot_reading = analogRead(trim_pot);
//*************************************************************************
Vo
Vcc saturation
+Vcc= 5 V
Vp
Vi + Vi > Vth
Vout
+Vcc Vn saturation
- Vth
Vi
Vth
+5 VDC
voltage level
+5 VDC for comparison
10K (4) +5 VDC
4/5 supply (2)
(1)
(3)
(11) 220
10K small LED
3/5 supply (6)
(7)
(5)
220
10K small LED
2/5 supply (9)
(8)
(10)
220
10K small LED
1/5 supply (13)
(14)
(12)
220
10K small LED
threshold
detectors
scalar
comparators multipliers
MSB
1/2
1/4
quantized
analog
signal
1/8
adder
LSB
1/2n
2.5 v
Figure 5.13: A summation method to convert a digital signal into a quantized analog signal. Comparators
are used to clean up incoming signals and the resulting values are multiplied by a scalar multiplier and the
results are added to generate the output signal. For the final analog signal, the quantized analog signal
should be connected to a low pass filter followed by a transducer interface circuit.
• Quad channel, 8-bit DAC connected via a parallel port (e.g., Analog Devices AD7305)
• Quad channel, 8-bit DAC connected via the SPI (e.g., Analog Devices AD7304)
• Octal channel, 8-bit DAC connected via the SPI (e.g., Texas Instrument TLC5628)
Space does not allow an in depth look at each configuration, but we will examine the TLC5628
in more detail.
void setup()
{
pinMode(illumination_output, OUTPUT); //config. pin 0 for dig. output
}
void loop()
{ //read analog output from
//IR sensors
viewer_sensor_reading = analogRead(viewer_sensor);
132 5. ANALOG TO DIGITAL CONVERSION (ADC)
buffer
REF1
DAC gain DACA
latch latch
buffer
buffer RNG
REF2
DAC gain DACE
latch latch
RNG buffer
ATmega164/TLC5628
SCK/CLK CLK CLK
LOW LOW
MOSI/DATA A2 A1 A0 RNG D7 D6 D5 D4 D3 D2 D1 D0
PORTB[2]/Load
b) TLC5628 timing diagram
220
10K
2N2222
3 21 0 9 8 76 5 4 32 1 0
1 1 1 1 DIGITAL
Ground
Arduino
Duemilanove
ANALOG IN
5VGnd 0 123 4 5
R Y B
IR sensor
//*************************************************************************
5.12 SUMMARY
In this chapter, we presented the differences between analog and digital signals and used this knowl-
edge to discuss three sub-processing steps involved in analog to digital converters: sampling, quan-
tization, and encoding. We also presented the quantization errors and the data rate associated with
the ADC process. The dynamic range of an analog-to-digital converter, one of the measures to
describe a conversion process, was also presented. We then presented the successive-approximation
converter. Transducer interface design concepts were then discussed along with supporting informa-
tion on operational amplifier configurations. We then reviewed the operation, registers, and actions
required to program the ADC system aboard the ATmega328. We concluded the chapter with a
discussion of the ADC process and an implementation using a multi-channel DAC connected to
the ATmega328 SPI system.
5.13. REFERENCES 135
5.13 REFERENCES
• Atmel 8-bit AVR Microcontroller with 16K Bytes In-System Programmable Flash, ATmega328,
ATmega328L, data sheet: 2466L-AVR-06/05, Atmel Corporation, 2325 Orchard Parkway,
San Jose, CA 95131.
• Barrett S, Pack D (2006) Microcontrollers Fundamentals for Engineers and Scientists. Morgan
and Claypool Publishers. DOI: 10.2200/S00025ED1V01Y200605DCS001
• Barrett S and Pack D (2008) Atmel AVR Microcontroller Primer Programming and Inter-
facing. Morgan and Claypool Publishers. DOI: 10.2200/S00100ED1V01Y200712DCS015
• Barrett S (2010) Embedded Systems Design with the Atmel AVR Microcontroller. Morgan
and Claypool Publishers. DOI: 10.2200/S00225ED1V01Y200910DCS025
• Roland Thomas and Albert Rosa, The Analysis and Design of Linear Circuits, Fourth Edition,
Wiley & Sons, Inc., New York, 2003.
• M.A. Hollander, editor, Electrical Signals and Systems, Fourth Edition, McGraw-Hill Compa-
nies, Inc, 1999.
• Daniel Pack and Steven Barrett, Microcontroller Theory and Applications: HC12 and S12, Pren-
tice Hall, 2ed, Upper Saddle River, New Jersey 07458, 2008.
• Alan Oppenheim and Ronald Schafer, Discrete-time Signal Processing, Second Edition, Prentice
Hall, Upper Saddle River, New Jersey, 1999.
• John Enderle, Susan Blanchard, and Joseph Bronzino, Introduction to Biomedical Engineering,
Academic Press, 2000.
• L. Faulkenberry, An Introduction to Operational Amplifiers, John Wiley & Sons, New York,
1977.
• P. Horowitz and W. Hill, The Art of Electronics, Cambridge University Press, 1989.
• S. Franco, Design with Operational Amplifiers and Analog Integrated Circuits, third edition,
McGraw-Hill Book Company, 2002.
2. If 12 bits are used to quantize a sampled signal, what is the number of available quantized
levels? What will be the resolution of such a system if the input range of the analog-to-digital
converter is 10V?
3. Given the 12 V input range of an analog-to-digital converter and the desired resolution of
0.125 V, what should be the minimum number of bits used for the conversion?
4. Investigate the analog-to-digital converters in your audio system. Find the sampling rate, the
quantization bits, and the technique used for the conversion.
5. A flex sensor provides 10K ohm of resistance for 0 degrees flexure and 40K ohm of resistance
for 90 degrees of flexure. Design a circuit to convert the resistance change to a voltage change
(Hint: consider a voltage divider). Then design a transducer interface circuit to convert the
output from the flex sensor circuit to voltages suitable for the ATmega328 ADC system.
7. Derive each of the characteristic equations for the classic operation amplifier configurations
provided in Figure 5.4.
8. If a resistor was connected between the non-inverting terminal and ground in the inverting
amplifier configuration of Figure 5.4a), how would the characteristic equation change?
9. A photodiode provides a current proportional to the light impinging on its active area. What
classic operational amplifier configuration should be used to current the diode output to a
voltage?
10. Does the time to convert an analog input signal to a digital representation vary in a successive-
approximation converter relative to the magnitude of the input signal?
137
CHAPTER 6
Interrupt Subsystem
Objectives: After reading this chapter, the reader should be able to
• Properly configure and program an interrupt event for the Arduino Duemilanove using built-in
features of the Arduino Development Environment.
6.1 OVERVIEW
A microcontroller normally executes instructions in an orderly fetch-decode-execute sequence as
dictated by a user-written program as shown in Figure 6.1. However, the microcontroller must be
equipped to handle unscheduled (although planned), higher priority events that might occur inside
or outside the microcontroller.To process such events, a microcontroller requires an interrupt system.
The interrupt system onboard a microcontroller allows it to respond to higher priority events.
Appropriate responses to these events may be planned, but we do not know when these events will
occur. When an interrupt event occurs, the microcontroller will normally complete the instruction
it is currently executing and then transition program control to interrupt event specific tasks. These
tasks, which resolve the interrupt event, are organized into a function called an interrupt service
routine (ISR). Each interrupt will normally have its own interrupt specific ISR. Once the ISR is
complete, the microcontroller will resume processing where it left off before the interrupt event
occurred.
In this chapter, we discuss the ATmega328 interrupt system in detail. We provide several
examples on how to program an interrupt in C and also using the built-in features of the Arduino
Development Environment.
138 6. INTERRUPT SUBSYSTEM
Interrupt
Fetch Service
Routine
Decode
Execute
The ATmega328 is equipped with a powerful and flexible complement of 26 interrupt sources.Two of
the interrupts originate from external interrupt sources while the remaining 24 interrupts support the
efficient operation of peripheral subsystems aboard the microcontroller. The ATmega328 interrupt
sources are shown in Figure 6.2. The interrupts are listed in descending order of priority. As you can
see, the RESET has the highest priority, followed by the external interrupt request pins INT0 (pin
4) and INT1 (pin 5). The remaining interrupt sources are internal to the ATmega328.
When an interrupt occurs, the microcontroller completes the current instruction, stores the
address of the next instruction on the stack, and starts executing instructions in the designated
interrupt service routine (ISR) corresponding to the particular interrupt source. It also turns off
the interrupt system to prevent further interrupts while one is in progress. The execution of the
ISR is performed by loading the beginning address of the interrupt service routine specific for that
interrupt into the program counter. The interrupt service routine will then commence. Execution of
139
1 0x0000 RESET External pin, power-on reset, brown-out reset, watchdog system reset
2 0x0002 INT0 External interrupt request 0 INT0_vect
3 0x0004 INT1 External interrupt request 1 INT1_vect
4 0x0006 PCINT0 Pin change interrupt request 0 PCINT0_vect
5 0x0008 PCINT1 Pin change interrupt request 1 PCINT1_vect
6 0x000A PCINT2 Pin change interrupt request 2 PCINT2_vect
7 0x000C WDT Watchdog time-out interrupt WDT_vect
8 0x000E TIMER2 COMPA Timer/Counter2 Compare Match A TIMER2_COMPA_vect
9 0x0010 TIMER2 COMPB Timer/Counter2 Compare Match B TIMER2_COMPB_vect
10 0x0012 TIMER2 OVF Timer/Counter2 Overflow TIMER2_OVF_vect
11 0x0014 TIMER1 CAPT Timer/Counter1 Capture Event TIMER1_CAPT_vect
12 0x0016 TIMER1 COMPA Timer/Counter1 Compare Match A TIMER1_COMPA_vect
13 0x0018 TIMER1 COMPB Timer/Counter1 Compare Match B TIMER1_COMPB_vect
14 0x001A TIMER1 OVF Timer/Counter1 Overflow TIMER1_OVF_vect
15 0x001C TIMER0 COMPA Timer/Counter0 Compare Match A TIMER0_COMPA_vect
16 0x001E TIMER0 COMPB Timer/Counter0 Compare Match B TIMER0_COMPB_vect
17 0x0020 TIMER0 OVF Timer/Counter0 Overflow TIMER0_OVF_vect
18 0x0022 SPI, STC SPI Serial Transfer Complete SPI_STC_vect
19 0x0024 USART, RX USART Rx Complete USART_RX_vect
20 0x0026 USART, UDRE USART, Data Register Empty USART_UDRE_vect
21 0x0028 USART, TX USART, Tx Complete USART_TX_vect
22 0x002A ADC ADC Conversion Complete ADC_vect
23 0x002C EE READY EEPROM Ready EE_READY_vect
24 0x002E ANALOG COMP Analog Comparator ANALOG_COMP_vect
25 0x0030 TWI 2-wire Serial Interface TWI_vect
26 0x0032 SPM READY Store Program Memory Ready SPM_ready_vect
Figure 6.2: Atmel AVR ATmega328 Interrupts. (Adapted from figure used with permission of Atmel, Incorporated.)
140 6. INTERRUPT SUBSYSTEM
the ISR continues until the return from interrupt instruction (reti) is encountered. Program control
then reverts back to the main program.
void timer_handler(void)
{
:
:
}
As you can see, the #pragma with the reserved word interrupt_handler is used to communicate
to the compiler that the routine name that follows is an interrupt service routine. The number that
follows the ISR name corresponds to the interrupt vector number in Figure 6.2. The ISR is then
written like any other function. It is important that the ISR name used in the #pragma instruction
identically matches the name of the ISR in the function body. Since the compiler knows the function
is an ISR, it will automatically place the assembly language RETI instruction at the end of the ISR.
6.4. PROGRAMMING INTERRUPTS IN C AND THE ARDUINO ENVIRONMENT 141
6.4.1 EXTERNAL INTERRUPT PROGRAMMING
The external interrupts INT0 (pin 4) and INT1 (pin 5) trigger an interrupt within the ATmega328
when an user-specified external event occurs at the pin associated with the specific interrupt. In-
terrupts INT0 and INT1 may be triggered with a falling or rising edge or a low level signal. The
specific settings for each interrupt is provided in Figure 6.3.
//function prototypes
void int0_ISR(void);
void initialize_interrupt0(void);
//*************************************************************************
:
initialize_interrupt_int0();
:
//*************************************************************************
//function definitions
//*************************************************************************
//initialize_interrupt_int0: initializes interrupt INT0.
//Note: stack is automatically initialized by the compiler
//*************************************************************************
//*************************************************************************
6.4. PROGRAMMING INTERRUPTS IN C AND THE ARDUINO ENVIRONMENT 143
//int0_ISR: interrupt service routine for INT0
//*************************************************************************
void int0_ISR(void)
{
}
The INT0 interrupt is reset by executing the associated interrupt service routine or writing a
logic one to the INTF0 bit in the External Interrupt Flag Register (EIFR).
6.4.1.2 Programming external interrupts using the Arduino Development Environment built-in
features
The Arduino Development Environment has four built-in functions to support external the INT0
and INT1 external interrupts [www.arduino.cc].
These are the four functions:
• interrupts(). This function enables interrupts.
• attachInterrupt(interrupt, function, mode). This function links the interrupt to the appro-
priate interrupt service routine.
• mode. Mode specifies what activity on the interrupt pin will initiate the interrupt: LOW level
on pin, CHANGE in pin level, RISING edge, or FALLING edge.
To illustrate the use of these built-in Arduino Development Environment features, we revisit
the previous example.
//*************************************************************************
144 6. INTERRUPT SUBSYSTEM
void setup()
{
attachInterrupt(0, int0_ISR, RISING);
}
void loop()
{
//*************************************************************************
//int0_ISR: interrupt service routine for INT0
//*************************************************************************
void int0_ISR(void)
{
}
//*************************************************************************
//function prototypes******************************************************
//delay specified number 6.55ms
void delay(unsigned int number_of_6_55ms_interrupts);
void init_timer0_ovf_interrupt(void); //initialize timer0 overf.interrupt
//global variables*********************************************************
unsigned int input_delay; //counts number of Timer/Counter0
//Overflow interrupts
//main program*************************************************************
void main(void)
{
init_timer0_ovf_interrupt(); //initialize Timer/Counter0 Overflow
}
146 6. INTERRUPT SUBSYSTEM
//*************************************************************************
//int_timer0_ovf_interrupt(): The Timer/Counter0 Overfl. interrupt is being
//employed as a time base for a master timer for this project. The ceramic
//resonator operating at 10 MHz is divided by 256.
//The 8-bit Timer0 register
//(TCNT0) overflows every 256 counts or every 6.55 ms.
//*************************************************************************
void init_timer0_ovf_interrupt(void)
{
TCCR0B = 0x04; //divide timer0 timebase by 256,
//overflow occurs every 6.55ms
TIMSK0 = 0x01; //enable timer0 overflow interrupt
asm("SEI"); //enable global interrupt
}
//*************************************************************************
//timer0_interrupt_isr:
//Note: Timer overflow 0 is cleared automatically
//when executing the corresponding interrupt handling vector.
//*************************************************************************
void timer0_interrupt_isr(void)
{
input_delay++; //increment overflow counter
}
//*************************************************************************
//delay(unsigned int num_of_6_55ms_interrupts): this generic delay function
//provides the specified delay as the number of 6.55 ms "clock ticks"
//from the Timer/Counter0 Overflow interrupt.
//
//Note: this function is only valid when using a 10 MHz crystal or ceramic
//resonator. If a different source frequency is used, the clock
//tick delay value must be recalculated.
//*************************************************************************
//*************************************************************************
//*************************************************************************
#include <avr/interrupt.h>
void setup()
{
init_timer0_ovf_interrupt(); //initialize Timer/Counter0 Overfl.
}
void loop()
{
//*************************************************************************
// ISR(TIMER0_OVF_vect) - increments counter on every interrupt.
//*************************************************************************
ISR(TIMER0_OVF_vect)
{
input_delay++; //increment overflow counter
}
//*************************************************************************
//int_timer0_ovf_interrupt(): The Timer/Counter0 Overflow interrupt is
//being employed as a time base for a master timer for this project.
//The crystal //resonator operating at 16 MHz is divided by 256.
//The 8-bit Timer0 register (TCNT0) overflows every 256 counts or every
//4.1 ms.
//*************************************************************************
void init_timer0_ovf_interrupt(void)
{
TCCR0B = 0x04; //divide timer0 timebase by 256, overflow occurs every 4.1 ms
TIMSK0 = 0x01; //enable timer0 overflow interrupt
asm("SEI"); //enable global interrupt
}
//*************************************************************************
//delay(unsigned int num_of_4_1ms_interrupts): this generic delay function
//provides the specified delay as the number of 4.1 ms "clock ticks" from
//the Timer/Counter0 Overflow interrupt.
//
//Note: this function is only valid when using a 16 MHz crystal or ceramic
//resonator. If a different source frequency is used, the clock
//tick delay value must be recalculated.
//*************************************************************************
6.5. FOREGROUND AND BACKGROUND PROCESSING 149
void delay(unsigned int number_of_4_1ms_interrupts)
{
TCNT0 = 0x00; //reset timer0
input_delay = 0; //reset timer0 overflow counter
//*************************************************************************
Background Processing
periodic periodic
interrupt interrupt
Foreground Processing
Figure 6.4: Interrupt used for background processing. The microcontroller responds to user input status
in the foreground while monitoring safety related status in the background using interrupts.
be displayed on a liquid crystal display. This is also a useful technique for generating very long delays
in a microcontroller.
//function prototypes******************************************************
//delay specified number 6.55ms
//global variables*********************************************************
unsigned int days_ctr, hrs_ctr, mins_ctr, sec_ctr, ms_ctr;
//main program*************************************************************
6.6. INTERRUPT EXAMPLES 151
void main(void)
{
day_ctr = 0; hr_ctr = 0; min_ctr = 0; sec_ctr = 0; ms_ctr = 0;
//*************************************************************************
//int_timer0_ovf_interrupt(): The Timer/Counter0 Overflow interrupt is
//being employed as a time base for a master timer for this project.
//The ceramic resonator operating at 10 MHz is divided by 256.
//The 8-bit Timer0 register (TCNT0) overflows every 256 counts or
//every 6.55 ms.
//*************************************************************************
void init_timer0_ovf_interrupt(void)
{
TCCR0B = 0x04; //divide timer0 timebase by 256, overfl. occurs every 6.55ms
//*************************************************************************
//timer0_interrupt_isr:
//Note: Timer overflow 0 is cleared by hardware when executing the
//corresponding interrupt handling vector.
//*************************************************************************
void timer0_interrupt_isr(void)
152 6. INTERRUPT SUBSYSTEM
{
//*************************************************************************
6.6. INTERRUPT EXAMPLES 153
6.6.2 REAL TIME CLOCK USING THE ARDUINO DEVELOPMENT
ENVIRONMENT
In this example, we reconfigure the previous example using the Arduino Development Environment.
The timing functions in the previous example assumed a time base of 10 MHz. The Arduino
Duemilanove is clocked with a 16 MHz crystal. Therefore, some of the parameters in the sketch are
adjusted to account for this difference in time base.
//*************************************************************************
#include <avr/interrupt.h>
//global variables*********************************************************
unsigned int days_ctr, hrs_ctr, mins_ctr, sec_ctr, ms_ctr;
void setup()
{
day_ctr = 0; hr_ctr = 0; min_ctr = 0; sec_ctr = 0; ms_ctr = 0;
init_timer0_ovf_interrupt(); //init. Timer/Counter0 Overflow
}
void loop()
{
:
: //wait for interrupts
:
}
//*************************************************************************
// ISR(TIMER0_OVF_vect) Timer0 interrupt service routine.
//
//Note: Timer overflow 0 is cleared by hardware when executing the
//corresponding interrupt handling vector.
//*************************************************************************
ISR(TIMER0_OVF_vect)
{
//Update millisecond counter
ms_ctr = ms_ctr + 1; //increment ms counter
//*************************************************************************
//int_timer0_ovf_interrupt(): The Timer/Counter0
//Overflow interrupt is being employed as a time base for a master timer
//for this project. The ceramic resonator operating at 16 MHz is
//divided by 256.
//The 8-bit Timer0 register (TCNT0) overflows every 256 counts or
//every 4.1 ms.
//*************************************************************************
void init_timer0_ovf_interrupt(void)
6.6. INTERRUPT EXAMPLES 155
{
TCCR0B = 0x04; //divide timer0 timebase by 256, overfl. occurs every 4.1 ms
TIMSK0 = 0x01; //enable timer0 overflow interrupt
asm("SEI"); //enable global interrupt
}
//*************************************************************************
byte ID = 1
status bit 1
status bit 2
b10
b11
b0
b1
b2
b3
b4
b5
b6
b7
b8
b9
Figure 6.5: Encoder data format. The position data is sent serially at 9600 Baud as two sequential bytes.
Since the actual encoder is not available for evaluation, another Atmel ATmega328 will be used
to send signals in identical format and Baud rate as the encoder. The test configuration is illustrated
in Figure 6.6. The ATmega328 on the bottom serves as the positional encoder. The microcontroller
is equipped with two pushbuttons at PD2 and PD3. The pushbutton at PD2 provides a debounced
input to open a simulated door and increment the positional encoder. The pushbutton at PD3
provides a debounced input to close a simulated door and decrement the positional encoder. The
current count of the encoder (eight most significant bits) is fed to a digital-to-analog converter
(DAC0808) to provide an analog representation.
The positional data from the encoder is sent out via the USART in the format described
in Figure 6.5. The top ATmega328 receives the positional data using interrupt driven USART
techniques. The current position is converted to an analog signal via the DAC. The transmitted and
received signals may be compared at the respective DAC outputs.
156
USART Receiver
1 PUR - PC6 PC5 28
2 RXD - PD0 PC4 27
3 TXD - PD1 PC3 26
4 PD2 PC2 25
5 PD3 PC1 24
VDD 6 PD4
1M PCO 23
7 Vcc Atmega328 GND 22
1uf
8 GND AREF 21 VDD
sys reset 9 PB6 AVCC 20
10 PB7 PB5 19
11 PD5 PB4 18
12 PD6 PB3 17 +5 VDC
13 PD7 PB2 16
14 PB0 PB1 15 1K
(13)
(14) Vref = 5 VDC
A1(5)
6. INTERRUPT SUBSYSTEM
A2(6) (15)
2K
A3(7) DAC0808 1K
A4(8) (1)
A5(9) 400
VEE = - 5 VDC
6.6. INTERRUPT EXAMPLES 157
Provided below is the code for the ATmega328 that serves as the encoder simulator followed
by the code for receiving the data.
//*************************************************************************
//author: Steven Barrett, Ph.D., P.E.
//last revised: April 15, 2010
//file: encode.c
//target controller: ATMEL ATmega328
//
//ATmega328 clock source: internal 8 MHz clock
//
//ATMEL AVR ATmega328PV Controller Pin Assignments
//Chip Port Function I/O Source/Dest Asserted Notes
//
//Pin 1 to system reset circuitry
//Pin 2 PD0: USART receive pin (RXD)
//Pin 3 PD1: USART transmit pin (TXD)
//Pin 4 PD2 to active high RC debounced switch - Open
//Pin 5 PD3 to active high RC debounced switch - Close
//Pin 7 Vcc - 1.0 uF to ground
//Pin 8 Gnd
//Pin 9 PB6 to pin A7(11) on DAC0808
//Pin 10 PB7 to pin A8(12) on DAC0808
//Pin 14 PB0 to pin A1(5) on DAC0808
//Pin 15 PB1 to pin A2(6) on DAC0808
//Pin 16 PB2 to pin A3(7) on DAC0808
//Pin 17 PB3 to pin A4(8) on DAC0808
//Pin 18 PB4 to pin A5(9) on DAC0808
//Pin 19 PB5 to pin A6(10) on DAC0808
//Pin 20 AVCC to 5 VDC
//Pin 21 AREF to 5 VDC
//Pin 22 Ground
//*************************************************************************
//include files************************************************************
#include<iom328v.h>
#include<macros.h>
//function prototypes******************************************************
158 6. INTERRUPT SUBSYSTEM
//delay specified number 6.55ms int
void initialize_ports(void); //initializes ports
void USART_init(void);
void USART_TX(unsigned char data);
//main program*************************************************************
//global variables
unsigned char old_PORTD = 0x08; //present value of PORTD
unsigned char new_PORTD; //new values of PORTD
unsigned int door_position = 0;
void main(void)
{
initialize_ports(); //return LED configuration to default
USART_init();
while(1)
{
_StackCheck(); //check for stack overflow
read_new_input();
//read input status changes on PORTB
}
}//end main
//Function definitions
//*************************************************************************
//initialize_ports: provides initial configuration for I/O ports
//*************************************************************************
void initialize_ports(void)
{
//PORTB
DDRB=0xff; //PORTB[7-0] output
PORTB=0x00; //initialize low
6.6. INTERRUPT EXAMPLES 159
//PORTC
DDRC=0xff; //set PORTC[7-0] as output
PORTC=0x00; //initialize low
//PORTD
DDRD=0xf2; //set PORTD[7-4, 0] as output
PORTD=0x00; //initialize low
}
//*************************************************************************
//*************************************************************************
//read_new_input: functions polls PORTD for a change in status. If status
//change has occurred, appropriate function for status change is called
//Pin 4 PD2 to active high RC debounced switch - Open
//Pin 5 PD3 to active high RC debounced switch - Close
//*************************************************************************
void read_new_input(void)
{
unsigned int gate_position; //measure instantaneous position of gate
unsigned int i;
unsigned char ms_door_position, ls_door_position, DAC_data;
PORTB = DAC_data;
//*************************************************************************
//USART_init: initializes the USART system
//
//Note: ATmega328 clocked by internal 8 MHz clock
//*************************************************************************
void USART_init(void)
{
UCSRA = 0x00; //control
register initialization
UCSRB = 0x08; //enable transmitter
UCSRC = 0x86; //async, no parity, 1 stop bit, 8 data bits
//Baud Rate initialization
//8 MHz clock requires UBRR value of 51
// or 0x0033 to achieve 9600 Baud rate
UBRRH = 0x00;
UBRRL = 0x33;
}
//*************************************************************************
//USART_transmit: transmits single byte of data
//*************************************************************************
//*************************************************************************
162 6. INTERRUPT SUBSYSTEM
Receive ATmega328 code follows.
//*************************************************************************
//author: Steven Barrett, Ph.D., P.E.
//last revised: April 15, 2010
//file: receive.c
//target controller: ATMEL ATmega328
//
//ATmega328 clock source: internal 8 MHz clock
//
//ATMEL AVR ATmega328PV Controller Pin Assignments
//Chip Port Function I/O Source/Dest Asserted Notes
//
//Pin 1 to system reset circuitry
//Pin 2 PD0: USART receive pin (RXD)
//Pin 3 PD1: USART transmit pin (TXD)
//Pin 4 PD2 to active high RC debounced switch - Open
//Pin 5 PD3 to active high RC debounced switch - Close
//Pin 7 Vcc - 1.0 uF to ground
//Pin 8 Gnd
//Pin 9 PB6 to pin A7(11) on DAC0808
//Pin 10 PB7 to pin A8(12) on DAC0808
//Pin 14 PB0 to pin A1(5) on DAC0808
//Pin 15 PB1 to pin A2(6) on DAC0808
//Pin 16 PB2 to pin A3(7) on DAC0808
//Pin 17 PB3 to pin A4(8) on DAC0808
//Pin 18 PB4 to pin A5(9) on DAC0808
//Pin 19 PB5 to pin A6(10) on DAC0808
//Pin 20 AVCC to 5 VDC
//Pin 21 AREF to 5 VDC
//Pin 22 Ground
//*************************************************************************
//include files************************************************************
#include<iom328v.h>
#include<macros.h>
#include<eeprom.h> //EEPROM support functions
//function prototypes******************************************************
void initialize_ports(void); //initializes ports
void InitUSART(void);
unsigned char USART_RX(void);
//main program*************************************************************
unsigned int door_position = 0;
unsigned char data_rx;
unsigned int dummy1 = 0x1234;
unsigned int keep_going =1;
unsigned int loop_counter = 0;
unsigned int ms_position, ls_position;
void main(void)
{
initialize_ports(); //return LED configuration to deflt.
USART_init();
//limited startup features
//main activity loop - processor will
//continually cycle through loop
//waiting for USART data
while(1)
{
//continuous loop waiting for
//interrupts
//Function definitions
//*************************************************************************
164 6. INTERRUPT SUBSYSTEM
//initialize_ports: provides initial configuration for I/O ports
//*************************************************************************
void initialize_ports(void)
{
//PORTB
DDRB=0xff; //PORTB[7-0] output
PORTB=0x00; //initialize low
//PORTC
DDRC=0xff; //set PORTC[7-0] as output
PORTC=0x00; //initialize low
//PORTD
DDRD=0xff; //set PORTD[7-0] as output
PORTD=0x00; //initialize low
}
//*************************************************************************
//USART_init: initializes the USART system
//
//Note: ATmega328 clocked by internal 8 MHz clock
//*************************************************************************
void USART_init(void)
{
UCSRA = 0x00; //control
register initialization
UCSRB = 0x08; //enable transmitter
UCSRC = 0x86; //async, no parity, 1 stop bit, 8 data bits
//Baud Rate initialization
//8 MHz clock requires UBRR value of 51
// or 0x0033 to achieve 9600 Baud rate
UBRRH = 0x00;
UBRRL = 0x33;
}
//*************************************************************************
//USART_RX_interrupt_isr
6.6. INTERRUPT EXAMPLES 165
//*************************************************************************
void USART_RX_interrupt_isr(void)
{
unsigned char data_rx, DAC_data;
unsigned int ls_position, ms_position;
//Update bit 7
if((ms_position & 0x0020)==0x0020) //Test for logic 1
door_position = door_position | 0x0080; //Set bit 7 to 1
else
door_position = door_position & 0xff7f; //Reset bit 7 to 0
PORTB= DAC_data;
}
//*************************************************************************
//end of file
//*************************************************************************
6.7 SUMMARY
In this chapter, we provided an introduction to the interrupt features available aboard the ATmega328
and the Arduino Duemilanove processing board. We also discussed how to program an interrupt
for proper operation and provided representative samples for an external interrupt and an internal
interrupt.
6.8 REFERENCES
• Atmel 8-bit AVR Microcontroller with 4/8/16/32K Bytes In-System Programmable Flash, AT-
mega48PA, 88PA, 168PA, 328P data sheet: 8161D-AVR-10/09, Atmel Corporation, 2325
Orchard Parkway, San Jose, CA 95131.
• Barrett S, Pack D (2006) Microcontrollers Fundamentals for Engineers and Scientists. Morgan
and Claypool Publishers. DOI: 10.2200/S00025ED1V01Y200605DCS001
• Barrett S and Pack D (2008) Atmel AVR Microcontroller Primer Programming and Inter-
facing. Morgan and Claypool Publishers. DOI: 10.2200/S00100ED1V01Y200712DCS015
• Barrett S (2010) Embedded Systems Design with the Atmel AVR Microcontroller. Morgan
and Claypool Publishers. DOI: 10.2200/S00225ED1V01Y200910DCS025
6.9. CHAPTER PROBLEMS 167
4. Describe the built-in interrupt features available with the Arduino Development Environment.
6. What steps are required by the system designer to properly configure an interrupt?
8. A 10 MHz ceramic resonator is not available. Redo the example of the Timer/Counter0
Overflow interrupt provided with a timebase of 1 MHz and 8 MHz.
9. What is the maximum delay that may be generated with the delay function provided in the
text without modification? How could the function be modified for longer delays?
10. In the text, we provided a 24 hour timer (hh:mm:ss:ms) using the Timer/Counter0 Overflow
interrupt. What is the accuracy of the timer? How can it be improved?
11. Adapt the 24 hour timer example to generate an active high logic pulse on a microcontroller
pin of your choice for three seconds. The pin should go logic high three weeks from now.
12. What are the concerns when using multiple interrupts in a given application?
13. How much time can background processing relative to foreground processing be implemented?
15. Can the USART transmit and interrupt receive system provided in the chapter be adapted to
operate in the Arduino Development Environment? Explain in detail.
169
CHAPTER 7
Timing Subsystem
Objectives: After reading this chapter, the reader should be able to
• Compute the frequency and the period of a periodic signal using a microcontroller.
• Describe the four operating modes of the Atmel ATmega328 timing system.
• Describe the register configurations for the ATmega328’s Timer 0, Timer 1, and Timer 2.
• Program the Arduino Duemilanove using the built-in timer features of the Arduino Devel-
opment Environment.
7.1 OVERVIEW
One of the most important reasons for using microcontrollers is their capability to perform time
related tasks. In a simple application, one can program a microcontroller to turn on or turn off an
external device at a specific time. In a more involved application, we can use a microcontroller to
generate complex digital waveforms with varying pulse widths to control the speed of a DC motor.
In this chapter, we review the capabilities of the Atmel ATmega328 microcontroller to perform
time related functions. We begin with a review of timing related terminology. We then provide an
overview of the general operation of a timing system followed by the timing system features aboard
the ATmega328. Next, we present a detailed discussion of each of its timing channels: Timer 0,
Timer 1, and Timer 2 and their different modes of operation. We then review the built-in timing
functions of the Arduino Development Environment and conclude the chapter with a variety of
examples.
170 7. TIMING SUBSYSTEM
7.2.1 FREQUENCY
Consider signal x(t) that repeats itself. We call this signal periodic with period T, if it satisfies
x(t) = x(t + T ).
To measure the frequency of a periodic signal, we count the number of times a particular event
repeats within a one second period. The unit of frequency is the Hertz or cycles per second. For
example, a sinusoidal signal with the 60 Hz frequency means that a full cycle of a sinusoid signal
repeats itself 60 times each second or every 16.67 ms.
7.2.2 PERIOD
The reciprocal of frequency is the period of a waveform. If an event occurs with a rate of 1 Hz,
the period of that event is 1 second. To find a period, given the frequency of a signal, or vice versa,
we simply need to remember their inverse relationship f = T1 where f and T represent a frequency
and the corresponding period, respectively. Both periods and frequencies of signals are often used
to specify timing constraints of microcontroller-based systems. For example, when your car is on a
wintery road and slipping, the engineers who designed your car configured the anti-slippage unit
to react within some millisecond period, say 20 milliseconds. The constraint then forces the design
team that monitors the slippage to program their monitoring system to check a slippage at a rate of
50 Hz.
50 %
100 %
25 % (a)
100 %
(b)
Figure 7.1: Two signals with the same period but different duty cycles.The top figure (a) shows a periodic
signal with a 50% duty cycle and the lower figure (b) displays a periodic signal with a 25% duty cycle.
count up (increment) each time it sees a rising edge (or a falling edge) of a clock signal. Thus, if a
clock is running at the rate of 2 MHz, the free running counter will count up or increment each
0.5 microseconds. All other timer related units reference the contents of the free running counter
to perform input and output time related activities: measurement of time periods, capture of timing
events, and generation of time related signals.
The ATmega328 may be clocked internally using a user-selectable resistor capacitor (RC)
time base or it may be clocked externally. The RC internal time base is selected using programmable
fuse bits. You may choose an internal fixed clock operating frequency of 1, 2, 4 or 8 MHz.
To provide for a wider range of frequency selections an external time source may be used. The
external time sources, in order of increasing accuracy and stability, are an external RC network, a
ceramic resonator, and a crystal oscillator. The system designer chooses the time base frequency and
clock source device appropriate for the application at hand. As previously mentioned, the maximum
operating frequency of the ATmega328P with a 5 VDC supply voltage is 20 MHz. The Arduino
Duemilanove processing board is equipped with a 16 MHz crystal oscillator time base.
172 7. TIMING SUBSYSTEM
For input time related activities, all microcontrollers typically have timer hardware compo-
nents that detect signal logic changes on one or more input pins. Such components rely on a free
running counter to capture external event times. We can use such ability to measure the period of
an incoming signal, the width of a pulse, and the time of a signal logic change.
For output timer functions, a microcontroller uses a comparator, a free running counter, logic
switches, and special purpose registers to generate time related signals on one or more output pins.
A comparator checks the value of the free running counter for a match with the contents of another
special purpose register where a programmer stores a specified time in terms of the free running
counter value. The checking process is executed at each clock cycle and when a match occurs, the
corresponding hardware system induces a programmed logic change on a programmed output port
pin. Using such capability, one can generate a simple logic change at a designated time incident, a
pulse with a desired time width, or a pulse width modulated signal to control servo or Direct Current
(DC) motors.
You can also use the timer input system to measure the pulse width of an aperiodic signal. For
example, suppose that the times for the rising edge and the falling edge of an incoming signal are
1.5 sec and 1.6 sec, respectively. We can use these values to easily compute the pulse width of 0.1
second.
The second overall goal of the timer system is to generate signals to control external devices.
Again an event simply means a change of logic states on an output pin of a microcontroller at a
specified time. Now consider Figure 7.2. Suppose an external device connected to the microcontroller
requires a pulse signal to turn itself on. Suppose the particular pulse the external device needs is 2
millisecond wide. In such situations, we can use the free running counter value to synchronize the
time of desired logic state changes. Naturally, extending the same capability, we can also generate a
periodic pulse with a fixed duty cycle or a varying duty cycle.
From the examples we discussed above, you may have wondered how a microcontroller can
be used to compute absolute times from the relative free running counter values, say 1.5 second and
1.6 second. The simple answer is that we can not do so directly. A programmer must use the system
clock values and derive the absolute time values. Suppose your microcontroller is clocked by a 2
MHz signal and the system clock uses a 16-bit free running counter. For such a system, each clock
period represents 0.5 microsecond and it takes approximately 32.78 milliseconds to count from 0
to 216 (65,536). The timer input system then uses the clock values to compute frequencies, periods,
and pulse widths. For example, suppose you want to measure a pulse width of an incoming aperiodic
signal. If the rising edge and the falling edge occurred at count values $0010 and $0114, 1 can you
find the pulse width when the free running counter is counting at 2 MHz? Let’s first convert the
two values into their corresponding decimal values, 276 and 16. The pulse width of the signal in the
number of counter value is 260. Since we already know how long it takes for the system to increment
by one, we can readily compute the pulse width as 260 × 0.5 microseconds = 130 microseconds.
Programmed
Event
Timer Output
- Toggle
Flag
- Logic high
- Logic low
Our calculations do not take into account time increments lasting longer than the rollover
time of the counter. When a counter rolls over from its maximum value back to zero, a status flag is
set to notify the processor of this event. The rollover events may be counted to correctly determine
the overall elapsed time of an event.
To calculate the total elapsed time of an event; the event start time, stop time, and the number
of timer overflows (n) that occurred between the start time and stop time must be known. Elapsed
time may be calculated using:
In this first equation, “n” is the number of Timer Overflow Flag (TOF) events that occur
between the start and stop events and “b” is the number of bits in the timer counter. The equation
174 7. TIMING SUBSYSTEM
yields the elapsed time in clock ticks. To convert to seconds the number of clock ticks are multiplied
by the period of the clock source of the free running counter.
7.4 APPLICATIONS
In this section, we consider some important uses of the timer system of a microcontroller to (1)
measure an input signal timing event, termed input capture, (2) to count the number of external
signal occurrences, (3) to generate timed signals — termed output compare, and, finally, (4) to
generate pulse width modulated signals. We first start with a case of measuring the time duration of
an incoming signal.
Microcontroller
External
Timer Output Port
Device
Figure 7.3: Use of the timer input and output systems of a microcontroller. The signal on top is fed into
a timer input port. The captured signal is subsequently used to compute the input signal frequency. The
signal on the bottom is generated using the timer output system. The signal is used to control an external
device.
The first necessary step for the current task is to turn on the timer system. To reduce power
consumption a microcontroller usually does not turn on all of its functional systems after reset until
they are needed. In addition to a separate timer module, many microcontroller manufacturers allow
a programmer to choose the rate of a separate timer clock that governs the overall functions of a
timer module.
Once the timer is turned on and the clock rate is selected, a programmer must configure the
physical port to which the incoming signal arrives. This step is done using a special input timer port
7.4. APPLICATIONS 175
configuration register. The next step is to program the input event to capture. In our current example,
we should capture two consecutive rising edges or falling edges of the incoming signal. Again, the
programming portion is done by storing an appropriate setup value to a special register.
Now that the input timer system is configured appropriately, you now have two options to
accomplish the task. The first one is the use of a polling technique; the microcontroller continuously
polls a flag, which holds a logic high signal when a programmed event occurs on the physical pin.
Once the microcontroller detects the flag, it needs to clear the flag and record the time when the flag
was set using another special register that captures the time of the associated free running counter
value. The program needs to continue to wait for the next flag, which indicates the end of one
period of the incoming signal. A programmer then needs to record the newly acquired captured
time represented in the form of a free running counter value again. The period of the signal can now
be computed by computing the time difference between the two captured event times, and, based
on the clock speed of the microcontroller’s timer system, the programmer can compute the actual
time changes and consequently the frequency of the signal.
In many cases, a microcontroller can’t afford the time to poll for one event. Such situation in-
troduces the second method: interrupt systems. Most microcontroller manufacturers have developed
built-in interrupt systems with their timer input modules. Instead of continuously polling for a flag,
a microcontroller performs other tasks and relies on its interrupt system to detect the programmed
event. The task of computing the period and the frequency is the same as the first method, except
that the microcontroller will not be tied down to constantly checking the flag, increasing the efficient
use of the microcontroller resources. To use interrupt systems, of course, we must pay the price by
appropriately configuring the interrupt systems to be triggered when a desired event is detected.
Typically, additional registers must be configured, and a special program called an interrupt service
routine must be written.
Suppose that for an input capture scenario the two captured times for the two rising edges are
$1000 and $5000, respectively. Note that these values are not absolute times but the representations
of times reflected as the values of the free running counter. The period of the signal is $4000 or
16384 in a decimal form. If we assume that the timer clock runs at 10 MHz, the period of the signal
is 1.6384 msec, and the corresponding frequency of the signal is approximately 610.35 Hz.
Speed Profile
Constant Speed
Period
Motor Velocity
Acceleration Deceleration
Period Period
Time
Pulse Width Modulated Signal
DC Motor
GND
Figure 7.4: The figure shows the speed profile of a DC motor over time when a pulse-width-modulated
signal is applied to the motor.
the figure, we want to accelerate the speed, maintain the speed, and decelerate the speed for a fixed
amount of time.
As an example, an elevator control system does not immediately operate the elevator motor
at full speed. The elevator motor speed will ramp up gradually from stop to desired speed. As the
elevator approaches, the desired floor it will gradually ramp back down to stop.
The first task necessary is again to turn on the timer system, configure a physical port, and
program the event to be a rising edge. As a part of the initialization process, we need to put $0000 to
the output timer register we discussed in the previous subsection. Once the rising edge is generated,
the program then needs to modify the event to a falling edge and change the contents of the output
timer register to a value proportional to a desired duty cycle. For example, if we want to start off
with 25% duty cycle, we need to input $4000 to the register, provided that we are using a 16 bit free
running counter. Once the falling edge is generated, we now need to go back and change the event
to be a rising edge and the contents of the output timer counter value back to $0000. If we want
178 7. TIMING SUBSYSTEM
to continue to generate a 25% duty cycle signal, then we must repeat the process indefinitely. Note
that we are using the time for a free running counter to count from $0000 to $FFFF as one period.
Now suppose we want to increase the duty cycle to 50% over 1 sec and that the clock is
running at 2 MHz. This means that the free running counter counts from $0000 to $FFFF every
32.768 milliseconds, and the free running counter will count from $0000 to $FFFF approximately
30.51 times over the period of one second. That is we need to increase the pulse width from $4000
to $8000 in approximately 30 turns, or approximately 546 clock counts every turn. This technique
may be used to generate any desired duty cycle.
The TCCR0A register contains the WGM0[1:0] bits and the COM0A[1:0] (and B) bits. Whereas,
the TCCR0B register contains the WGM0[2] bit.These bits are used to select the mode of operation
for Timer 0 as well as tailor waveform generation for a specific application.
The timer clock source (clkT n ) is fed to the 8-bit Timer/Counter Register (TCNT0). This
register is incremented (or decremented) on each clkT n clock pulse. Timer 0 is also equipped with
two 8-bit comparators that constantly compares the numerical content of TCNT0 to the Output
Compare Register A (OCR0A) and Output Compare Register B (OCR0B). The compare signal
from the 8-bit comparator is fed to the waveform generators. The waveform generators have a
number of inputs to perform different operations with the timer system.
The BOTTOM signal for the waveform generation and the control logic, shown in Figure
7.6, is asserted when the timer counter TCNT0 reaches all zeroes (0x00). The MAX signal for
the control logic unit is asserted when the counter reaches all ones (0xFF). The TOP signal for
the waveform generation is asserted by either reaching the maximum count values of 0xFF on the
TCNT0 register or reaching the value set in the Output Compare Register 0 A (OCR0A) or B.
The setting for the TOP signal will be determined by the Timer’s mode of operation.
Timer 0 also uses certain bits within the Timer/Counter Interrupt Mask Register 0 (TIMSK0)
and the Timer/Counter Interrupt Flag Register 0 (TIFR0) to signal interrupt related events.
180 7. TIMING SUBSYSTEM
Figure 7.6: Timer 0 block diagram. (Figure used with permission Atmel, Inc.)
Normal Mode
fOC0 = (fclk_I/O)/ (2 x N x (1 + OCR0))
OC0
TOP OCR0
OCR0
OCR0
TCNT0 TCNT0
BOTTOM
TOV0 TOV0 TOV0 OC0 OC0 OC0 OC0 OC0
inter
flag
Fast PWM Mode Phase Correct PWM Mode
fOC0PWM = fclk_I/O/ (N x 510)
fOC0PWM = fclk_I/O/ (N x 256)
TOV0 TOV0 TOV0 TOV0 TOV0 TOV0
TOP TOP
OCR0
TCNT0 OCR0 OCR0 TCNT0 OCR0 OCR0
BOTTOM BOTTOM
OC0 OC0 OC0 OC0 OC0 OC0
OC0 OC0
Figure 7.7: Timer 0 modes of operation.
182 7. TIMING SUBSYSTEM
(TOV0) will be set. The normal mode is useful for generating a periodic “clock tick” that may be
used to calculate elapsed real time or provide delays within a system. We provide an example of this
application in Section 5.9.
7 0
Output Compare Register A (OCR0A)
7 0
Output Compare Register B (OCR0B)
7 0
Timer/Counter Interrupt Mask Register 0 (TIMSK0)
--- --- --- --- --- OCIE0B OCIE0A TOIE0
7 0
Timer/Counter Interrupt Flag REgister 0 (TIFR0)
--- --- --- --- --- OCF0B OCF0A TOV0
7 0
Waveform Generation
Mode
Mode WGM[02:00] Mode
0 000 Normal
1 001 PWM, Phase Correct
2 010 CTC
3 011 Fast PWM
4 100 Reserved
5 101 PWM, Phase Correct
6 110 Reserved
7 111 Fast PWM
Compare Output Mode, non-PWM Mode Compare Output Mode, non-PWM Mode
COM0A[1:0] Description COM0B[1:0] Description
00 Normal, OC0A disconnected 00 Normal, OC0B disconnected
01 Toggle OC0A on compare match 01 Toggle OC0B on compare match
10 Clear OC0A on compare match 10 Clear OC0B on compare match
11 Set OC0A on compare match 11 Set OC0B on compare match
Compare Output Mode, Fast PWM Mode Compare Output Mode, Fast PWM Mode
COM0A[1:0] Description COM0B[1:0] Description
00 Normal, OC0A disconnected 00 Normal, OC0B disconnected
01 WGM02 = 0: normal operation, 01 Reserved
OC0A disconnected
WGM02 = 1: Toggle OC0A on
compare match
10 Clear OC0A on compare match, 10 Clear OC0B on compare match,
set OC0A at Bottom set OC0B at Bottom
(non-inverting mode) (non-inverting mode)
11 Set OC0A on compare match, 11 Set OC0B on compare match,
clear OC0A at Bottom clear OC0B at Bottom
(inverting mode) (inverting mode)
Compare Output Mode, Phase Correct PWM Compare Output Mode, Phase Correct PWM
COM0A[1:0] Description COM0B[1:0] Description
00 Normal, OC0A disconnected 00 Normal, OC0B disconnected
01 WGM02 = 0: normal operation, 01 Reserved
OC0A disconnected
WGM02 = 1: Toggle OC0A on
compare match
Clear OC0A on compare match, 10 Clear OC0B on compare match,
10 when upcounting. Set OC0A on when upcounting. Set OC0B on
compare match when down counting compare match when down counting
Set OC0A on compare match, 11 Set OC0B on compare match,
11 when upcounting. Set OC0A on when upcounting. Set OC0B on
compare match when down counting compare match when down counting
Figure 7.9: Timer/Counter Control Registers A and B (TCCR0A and TCCR0B) bit settings.
7.7. TIMER 1 185
or B bit and the I-bit in the Status Register are both set to one, the Timer/Counter 0 Compare
Match interrupt is enabled. When the TOIE0 bit and the I-bit in the Status Register are both set
to one, the Timer/Counter 0 Overflow interrupt is enabled.
7.7 TIMER 1
Timer 1 is a 16-bit timer/counter. It shares many of the same features of the Timer 0 channel.
Due to limited space the shared information will not be repeated. Instead, we concentrate on the
enhancements of Timer 1 which include an additional output compare channel and also the capability
for input capture. The block diagram for Timer 1 is shown in Figure 7.10.
As discussed earlier in the chapter, the input capture feature is used to capture the character-
istics of an input signal including period, frequency, duty cycle, or pulse length. This is accomplished
by monitoring for a user-specified edge on the ICP1 microcontroller pin. When the desired edge
occurs, the value of the Timer/Counter 1 (TCNT1) register is captured and stored in the Input
Capture Register 1 (ICR1).
• Select the operational mode of Timer 1 using the Waveform Mode Generation (WGM1[3:0])
bits,
• Determine the operation of the timer within a specific mode with the Compare Match Output
Mode (Channel A: COM1A[1:0] and Channel B: COM1B[1:0]) bits, and
• Select the source of the Timer 1 clock using Clock Select (CS1[2:0]) bits.
The bit settings for the TCCR1A and TCCR1B registers are summarized in Figure 7.12.
Figure 7.10: Timer 1 block diagram. (Figure used with Permission, Atmel,Inc.)
7.7. TIMER 1 187
7 0
Timer/Counter 1 Control Register B (TCCR1B)
ICNC1 ICES1 --- WGM13 WGM12 CS12 CS11 CS10
7 0
Timer/Counter 1 Control Register C (TCCR1C)
FOC1A FOC1B --- --- --- --- --- ---
7 0
Clock Select
Timer/Counter 1 Control Register B (TCCR1B)
ICNC1 ICES1 --- WGM13 WGM12 CS12 CS11 CS10
7 0
Timer/Counter 1 Control Register A (TCCR1A)
COM1A1 COM1A0 COM1B1 COM1B0 --- --- WGM11 WGM10
7 0
Waveform Generation Mode Normal, CTC
Mode WGM[13:12:11:10] Mode COMx[1:0] Description
0 0000 Normal 00 Normal, OC1A/1B disconnected
1 0001 PWM, Phase Correct, 8-bit 01 Toggle OC1A/1B on compare match
2 0010 PWM, Phase Correct, 9-bit 10 Clear OC1A/1B on compare match
3 0011 PWM, Phase Correct, 10-bit 11 Set OC1A/1B on compare match
4 0100 CTC
PWM, Phase Correct, Phase & Freq Correct
5 0101 Fast PWM, 8-bit
COMx[1:0] Description
6 0110 Fast PWM, 9-bit
00 Normal, OC0 disconnected
7 0111 Fast PWM, 10-bit
01 WGM1[3:0] = 9 or 14: toggle OCnA
8 1000 PWM, Phase & Freq Correct
on compare match, OCnB discon-
9 1001 PWM, Phase & Freq Correct
nected
10 1010 PWM, Phase Correct
WGM1[3:0]= other settings,
11 1011 PWM, Phase Correct
OC1A/1B disconnected
12 1100 CTC
10 Clear OC0 on compare match
13 1101 Reserved
when up-counting. Set OC0
14 1110 Fast PWM
on compare match when
15 1111 Fast PWM
down counting
11 Set OC0 on compare match
when up-counting. Clear OC0
on compare match when
down counting.
Fast PWM
COMx[1:0] Description
00 Normal, OC1A/1B disconnected
01 WGM1[3:0] = 9 or 11, toggle OC1A on
compare match OC1B disconnected
WGM1[3:0] = other settings,
OC1A/1B disconnected
10 Clear OC1A/1B on compare match,
set OC1A/1B on Compare Match when
down counting
11 Set OC1A/1B on compare match when
upcounting. Clear OC1A/1B on Compare
Match when upcounting
7.7.1.4 OCR1BH/OCR1BL
The OCR1B register holds a user-defined 16-bit value that is continuously compared to the TCNT1
register when Channel B is used.
7.8 TIMER 2
Timer 2 is another 8-bit timer channel similar to Timer 0. The Timer 2 channel block diagram is
provided in Figure 7.13. Its registers are summarized in Figure 7.14.
• Select the operational mode of Timer 2 using the Waveform Mode Generation (WGM2[2:0])
bits,
• Determine the operation of the timer within a specific mode with the Compare Match Output
Mode (COM2A[1:0] and B) bits, and
190 7. TIMING SUBSYSTEM
Figure 7.13: Timer 2 block diagram. (Figure used with Permission, Atmel,Inc.)
7.8. TIMER 2 191
7 0
Output Compare Register A (OCR2A)
7 0
Output Compare Register B (OCR2B)
7 0
Timer/Counter 2 Interrupt Mask Register (TIMSK2)
--- --- --- --- --- OCIE2B OCIE2A TOIE2
7 0
Timer/Counter 2 Interrupt Flag REgister (TIFR2)
--- --- --- --- --- OCF2B OCF2A TOV2
7 0
Compare Output Mode, Phase Correct PWM Compare Output Mode, Phase Correct PWM
COM2A[1:0] Description COM2B[1:0] Description
00 Normal, OC2A disconnected 00 Normal, OC2B disconnected
01 WGM22 = 0: normal operation, 01 Reserved
OC2A disconnected
WGM22 = 1: Toggle OC2A on
compare match
Clear OC2A on compare match, 10 Clear OC2B on compare match,
10 when upcounting. Set OC2A on when upcounting. Set OC2B on
compare match when down counting compare match when down counting
Set OC2A on compare match, 11 Set OC2B on compare match,
11 when upcounting. Set OC2A on when upcounting. Set OC2B on
compare match when down counting compare match when down counting
Figure 7.15: Timer/Counter Control Register A and B (TCCR2A and B) bit settings.
194 7. TIMING SUBSYSTEM
We have already used the analogWrite function in an earlier chapter to control the motor
speed of the Blinky 602A robot.
//Function prototypes
void delay(unsigned int number_of_6_55ms_interrupts);
void init_timer0_ovf_interrupt(void);
void timer0_interrupt_isr(void);
//*************************************************************************
//int_timer0_ovf_interrupt(): The Timer0 overflow interrupt is being
//employed as a time base for a master timer for this project.
//The ceramic resonator operating at 10 MHz is divided by 256.
//The 8-bit Timer0 register (TCNT0) overflows every 256 counts
//or every 6.55 ms.
//*************************************************************************
void init_timer0_ovf_interrupt(void)
{
TCCR0B = 0x04; //divide timer0 timebase by 256, overfl. occurs every 6.55ms
7.10. PROGRAMMING THE TIMER SYSTEM IN C 195
TIMSK0 = 0x01; //enable timer0 overflow interrupt
asm("SEI"); //enable global interrupt
}
//*************************************************************************
//*************************************************************************
//timer0_interrupt_isr:
//Note: Timer overflow 0 is cleared by hardware when executing the
//corresponding interrupt handling vector.
//*************************************************************************
void timer0_interrupt_isr(void)
{
input_delay++; //input delay processing
}
//*************************************************************************
//*************************************************************************
//delay(unsigned int num_of_6_55ms_interrupts): this generic delay function
//provides the specified delay as the number of 6.55 ms "clock ticks" from
//the Timer0 interrupt.
//Note: this function is only valid when using a 10 MHz crystal or ceramic
// resonator
//*************************************************************************
//*************************************************************************
196 7. TIMING SUBSYSTEM
7.10.2 PULSE WIDTH MODULATION IN C
The function provided below is used to configure output compare channel B to generate a pulse
width modulated signal. An analog voltage provided to ADC Channel 3 is used to set the desired
duty cycle from 50 to 100 percent. Note how the PWM ramps up from 0 to the desired speed.
//Function Prototypes
void PWM(unsigned int PWM_incr)
{
unsigned int Open_Speed_int;
float Open_Speed_float;
int gate_position_int;
PWM_duty_cycle = 0;
InitADC(); //Initialize ADC
//*************************************************************************
//*************************************************************************
//initialize_ICP_interrupt: Initialize Timer/Counter 1 for input capture
//*************************************************************************
void initialize_ICP_interrupt(void)
{
TIMSK=0x20; //Allows input capture interrupts
SFIOR=0x04; //Internal pull-ups disabled
TCCR1A=0x00; //No output comp or waveform
//generation mode
TCCR1B=0x45; //Capture on rising edge,
//clock prescalar=1024
198 7. TIMING SUBSYSTEM
TCNT1H=0x00; //Initially clear timer/counter 1
TCNT1L=0x00;
asm("SEI"); //Enable global interrupts
}
//*************************************************************************
void Input_Capture_ISR(void)
{
if(first_edge==0)
{
ICR1L=0x00; //Clear ICR1 and TCNT1 on first edge
ICR1H=0x00;
TCNT1L=0x00;
TCNT1H=0x00;
first_edge=1;
}
else
{
ICR1L=TCNT1L; //Capture time from TCNT1
ICR1H=TCNT1H;
TCNT1L=0x00;
TCNT1H=0x00;
first_edge=0;
}
//*************************************************************************
void heart_rate(void)
{
if(first_edge==0)
{
7.11. SERVO MOTOR CONTROL WITH THE PWM SYSTEM IN C 199
time_pulses_low = ICR1L; //Read 8 low bits first
time_pulses_high = ((unsigned int)(ICR1H << 8));
time_pulses = time_pulses_low | time_pulses_high;
if(time_pulses!=0) //1 counter increment = 1.024 ms
{ //Divide by 977 to get seconds/pulse
HR=60/(time_pulses/977); //(secs/min)/(secs/beat) =bpm
}
else
{
HR=0;
}
}
else
{
HR=0;
}
}
//*************************************************************************
128 kHz
ceramic resonator
1 PUR - PC6 PC5 28
2 RXD - PD0 PC4 27
3 TXD - PD1 PC3 26
4 PD2 PC2 25
sys reset
VDD 5 PD3 PC1 24
1M 6 PD4 PCO 23
7 Vcc Atmega328 GND 22
1uf
8 GND AREF 21 VDD
9 PB6 AVCC 20
10 PB7 PB5 19
Vcc = 5.0 volts 11 PD5 PB4 18
74HC14 12 PD6 PB3 17
4.7 K 100K 13 PD7 PB2 16
PB0
14 PB0 PB1 15
CW 470K (1) (2)
0.1uF
3K
Vcc = 5 VDC
Red
White Black
CCW
Vcc = 5 VDC
(3) (4)
(1) CW
(2) LM324
(11)
6
=8 =1
% 5%
.25 o= 6.2
o =3
= 0 = 180
s s
1m 2m
2 ms 30 ms
T = 32 ms, f = 31.25 Hz
//*************************************************************************
//include files************************************************************
//ATMEL register definitions for ATmega328
#include<iom328pv.h>
#include<macros.h>
//function prototypes******************************************************
void initialize_ports(void); //initializes ports
void read_new_input(void); //used to read input change on PORTB
void init_timer0_ovf_interrupt(void); //used to initialize timer0 overflow
//main program*************************************************************
//The main program checks PORTB for user input activity.
//If new activity is found, the program responds.
//global variables
unsigned char old_PORTB = 0x08; //present value of PORTB
unsigned char new_PORTB; //new values of PORTB
unsigned int PWM_duty_cycle;
202 7. TIMING SUBSYSTEM
void main(void)
{
initialize_ports();
//return LED configuration to default
while(1)
{
_StackCheck(); //check for stack overflow
read_new_input(); //read input status changes on PORTB
}
}//end main
//Function definitions
//*************************************************************************
//initialize_ports: provides initial configuration for I/O ports
7.11. SERVO MOTOR CONTROL WITH THE PWM SYSTEM IN C 203
//*************************************************************************
void initialize_ports(void)
{
//PORTB
DDRB=0xfc; //PORTB[7-2] output, PORTB[1:0] input
PORTB=0x00; //disable PORTB pull-up resistors
//PORTC
DDRC=0xff; //set PORTC[7-0] as output
PORTC=0x00; //init low
//PORTD
DDRD=0xff; //set PORTD[7-0] as output
PORTD=0x00; //initialize low
}
//*************************************************************************
//*************************************************************************
void read_new_input(void)
{
new_PORTB = (PINB);
if(new_PORTB != old_PORTB){
switch(new_PORTB){ //process change in PORTB input
case 0x01: //CW
while(PINB == 0x01)
{
PWM_duty_cycle = PWM_duty_cycle + 1;
if(PWM_duty_cycle > 16) PWM_duty_cycle = 16;
OCR1BH = 0x00;
OCR1BL = (unsigned char)(PWM_duty_cycle);
}
204 7. TIMING SUBSYSTEM
break;
//*************************************************************************
7.12 SUMMARY
In this chapter, we considered a microcontroller timer system, associated terminology for timer
related topics, discussed typical functions of a timer subsystem, studied timer hardware operations,
and considered some applications where the timer subsystem of a microcontroller can be used. We
then took a detailed look at the timer subsystem aboard the ATmega328 and reviewed the features,
operation, registers, and programming of the three timer channels. We then investigated the built-
in timing features of the Arduino Development Environment. We concluded with an example
employing a servo motor controlled by the ATmega328 PWM system.
7.13 REFERENCES
• Kenneth Short, Embedded Microprocessor Systems Design: An Introduction Using the INTEL
80C188EB, Prentice Hall, Upper Saddle River, 1998.
• Frederick Driscoll, Robert Coughlin, and Robert Villanucci, Data Acquisition and Process Con-
trol with the M68HC11 Microcontroller, Second Edition, Prentice Hall, Upper Saddle River,
2000.
7.14. CHAPTER PROBLEMS 205
• Todd Morton, Embedded Microcontrollers, Prentice Hall, Upper Saddle River, Prentice Hall,
2001.
• Atmel 8-bit AVR Microcontroller with 16K Bytes In-System Programmable Flash, ATmega328,
ATmega328L, data sheet: 2466L-AVR-06/05, Atmel Corporation, 2325 Orchard Parkway,
San Jose, CA 95131.
• Barrett S, Pack D (2006) Microcontrollers Fundamentals for Engineers and Scientists. Morgan
and Claypool Publishers. DOI: 10.2200/S00025ED1V01Y200605DCS001
• Barrett S and Pack D (2008) Atmel AVR Microcontroller Primer Programming and Inter-
facing. Morgan and Claypool Publishers. DOI: 10.2200/S00100ED1V01Y200712DCS015
• Barrett S (2010) Embedded Systems Design with the Atmel AVR Microcontroller. Morgan
and Claypool Publishers. DOI: 10.2200/S00225ED1V01Y200910DCS025
2. If we desire to generate periodic signals with periods ranging from 125 nanoseconds to 500
microseconds, what is the minimum frequency of the system clock?
3. Describe how you can compute the period of an incoming signal with varying duty cycles.
4. Describe how one can generate an aperiodic pulse with a pulse width of 2 minutes?
5. Program the output compare system of the ATmega328 to generate a 1 kHz signal with a 10
percent duty cycle.
6. Design a microcontroller system to control a sprinkler controller that performs the following
tasks. We assume that your microcontroller runs with 10 MHz clock and it has a 16 bit
free running counter. The sprinkler controller system controls two different zones by turning
sprinklers within each zone on and off. To turn on the sprinklers of a zone, the controller needs
to receive a 152.589 Hz PWM signal from your microcontroller. To turn off the sprinklers of
the same zone, the controller needs to receive the PWM signal with a different duty cycle.
(a) Your microcontroller needs to provide the PWM signal with 10% duty cycle for 10
millisecond to turn on the sprinklers in zone one.
(b) After 15 minutes, your microcontroller must send the PWM signal with 15% duty cycle
for 10 millisecond to turn off the sprinklers in zone one.
(c) After 15 minutes, your microcontroller must send the PWM signal with 20% duty cycle
for 10 millisecond to turn on the sprinklers in zone two.
206 7. TIMING SUBSYSTEM
(d) After 15 minutes, your microcontroller must send the PWM signal with 25% duty cycle
for 10 millisecond to turn off the sprinklers in zone two.
7. Modify the servo motor example to include a potentiometer connected to PORTA[0]. The
servo will deflect 0 degrees for 0 VDC applied to PORTA[0] and 180 degrees for 5 VDC.
8. For the automated cooling fan example, what would be the effect of changing the PWM
frequency applied to the fan?
9. Modify the code of the automated cooling fan example to also display the set threshold
temperature.
207
CHAPTER 8
• Describe the voltage and current parameters for the Arduino Duemilanove and the Atmel
AVR HC CMOS type microcontroller.
• Specify a battery system to power an Arduino Duemilanove and the Atmel AVR based system.
• Apply the voltage and current parameters toward properly interfacing input and output devices
to the Arduino Duemilanove and the Atmel AVR microcontroller.
• Interface a wide variety of input and output devices to the Arduino Duemilanove and the
Atmel AVR microcontroller.
• Describe the special concerns that must be followed when the Arduino Duemilanove and the
Atmel AVR microcontroller is used to interface to a high power DC or AC device.
8.1 OVERVIEW
The textbook for Morgan & Claypool Publishers (M&C) titled, “Microcontrollers Fundamentals
for Engineers and Scientists,” contains a chapter entitled “Operating Parameters and Interfacing.”
With M&C permission, we repeated portions of the chapter here for your convenience. However,
we have customized the information provided to the Arduino Duemilanove and the Atmel AVR
line of microcontrollers and have also expanded the coverage of the chapter to include interface
techniques for a number of additional input and output devices.
In this chapter, we introduce you to the extremely important concepts of the operating envelope
for a microcontroller. We begin by reviewing the voltage and current electrical parameters for the HC
CMOS based Atmel AVR line of microcontrollers. We then show how to apply this information
to properly interface input and output devices to the Arduino Duemilanove and the ATmega328
208 8. ATMEL AVR OPERATING PARAMETERS AND INTERFACING
microcontroller. We then discuss the special considerations for controlling a high power DC or AC
load such as a motor and introduce the concept of an optical interface. Throughout the chapter, we
provide a number of detailed examples.
The importance of this chapter can not be emphasized enough. Any time an input or an output
device is connected to a microcontroller, the interface between the device and the microcontroller
must be carefully analyzed and designed.This will ensure the microcontroller will continue to operate
within specified parameters. Should the microcontroller be operated outside its operational envelope,
erratic, unpredictable, and unreliable system may result.
IIL VIL
VOL IOL
VSS = 0 VDC VSS = 0 VDC
Iout [mA]
0 0
0 Vout [V] 5 5 Vout [V] 0
c) CMOS loading curves
8.4.1 SWITCHES
Switches come in a variety of types. As a system designer it is up to you to choose the appropriate
switch for a specific application. Switch varieties commonly used in microcontroller applications are
illustrated in Figure 8.3(a). Here is a brief summary of the different types:
• Slide switch: A slide switch has two different positions: on and off. The switch is manually
moved to one position or the other. For microcontroller applications, slide switches are available
212 8. ATMEL AVR OPERATING PARAMETERS AND INTERFACING
I O
7805 +5 VDC
C
9 VDC 0.33 uF 0.1 uF
I O
7805 +5 VDC
C
9 VDC 0.33 uF 0.1 uF
Figure 8.2: Battery supply circuits employing a 9 VDC battery with a 5 VDC regulators.
8.4. INPUT DEVICES 213
that fit in the profile of a common integrated circuit size dual inline package (DIP). A bank
of four or eight DIP switches in a single package is commonly available.
• Push on/push off switches: These type of switches are also available in a normally open or
normally closed configuration. For the normally open configuration, the switch is depressed to
make connection between the two switch contacts. The pushbutton must be depressed again
to release the connection.
• Hexadecimal rotary switches: Small profile rotary switches are available for microcontroller
applications. These switches commonly have sixteen rotary switch positions. As the switch is
rotated to each position, a unique four bit binary code is provided at the switch contacts.
A common switch interface is shown in Figure 8.3(b). This interface allows a logic one or zero
to be properly introduced to a microcontroller input port pin.The basic interface consists of the switch
in series with a current limiting resistor. The node between the switch and the resistor is provided
to the microcontroller input pin. In the configuration shown, the resistor pulls the microcontroller
input up to the supply voltage VDD . When the switch is closed, the node is grounded and a logic
zero is provided to the microcontroller input pin. To reverse the logic of the switch configuration,
the position of the resistor and the switch is simply reversed.
VDD VDD
microcontroller
4.7 kohm pullup resistor
activated
To microcontroller input
- Logic one when switch open
- Logic zero when switch is closed
b) Switch interface
VDD
0.1 μF
8.4.4 KEYPADS
A keypad is simply an extension of the simple switch configuration. A typical keypad configuration
and interface are shown in Figure 8.4. As you can see the keypad is simply multiple switches in the
same package. A hexadecimal keypad is provided in the figure. A single row of keypad switches are
asserted by the microcontroller and then the host keypad port is immediately read. If a switch has
been depressed, the keypad pin corresponding to the column the switch is in will also be asserted.
The combination of a row and a column assertion can be decoded to determine which key has
been pressed as illustrated in the table. Keypad rows are continually asserted one after the other in
sequence. Since the keypad is a collection of switches, debounce techniques must also be employed.
The keypad may be used to introduce user requests to a microcontroller. A standard keypad
with alphanumeric characters may be used to provide alphanumeric values to the microcontroller
such as providing your personal identification number (PIN) for a financial transaction. However,
some keypads are equipped with removable switch covers such that any activity can be associated
with a key press.
In Figure 8.5, we have connected the ATmega328 to a hexadecimal keypad via PORTB.
PORTB[3:0] is configured as output to selectively assert each row. PORTB[7:4] is configured as
input. Each row is sequentially asserted low. Each column is then read via PORTB[7:4] to see if
any switch in that row has been depressed. If no switches have been depressed in the row, an “F”
will be read from PORTB[7:4]. If a switch has been depressed, some other value than “F” will be
read. The read value is then passed into a switch statement to determine the ASCII equivalent of
the depressed switch. The function is not exited until the switch is released. This prevents a switch
“double hit.”
//*************************************************************************
216 8. ATMEL AVR OPERATING PARAMETERS AND INTERFACING
0 1 2 3
assert
0 keypad row 0
PORTx[0]
4 5 6 7
assert
1 keypad row 1
PORTx[1]
8 9 A B
assert
2 keypad row 2
PORTx[2]
C D E F
assert
Microcontroller PORTx
3 keypad row 3
PORTx[3]
Vcc
4 5 6 7
10K
read keypad column 0
PORTx[4]
Vcc
10K
read keypad column 1
PORTx[5]
Vcc
10K
read keypad column 2
PORTx[6]
Vcc
10K
read keypad column 3 PORTx[7]
Atmega328
1 PUR - PC6 PC5 28
2 RXD1 - PD0 PC4 27
3 TXD1 - PD1 PC3 26
4 PD2 PC2 25
VDD 1M sys reset 5 PD3 PC1 24
1.0 uF 6 PD4 PCO 23
7 Vcc GND 22
8 GND AREF 21 VDD
9 PB6 AVCC 20
10 PB7 PB5 19
11 PD5 PB4 18
12 PD6 PB3 17
13 PD7 PB2 16
14 PB0 PB1 15
0 1 2 3
PB0
0
4 5 6 7
PB1
1
8 9 A B
PB2
2
C D E F
PB3
3
DDRC = 0x0F;
//set PORTB[7:4] to input,
//PORTB[3:0] to output
if(PORTB_value_masked != 0xf0)
{
switch(PORTB_value_masked)
{
case 0xEE: ascii_value = ’0’;
break;
default:;
}
while(PORTB_value_masked != 0xf0);
//wait for key to be released
return ascii_value;
}
//*************************************************************************
8.4.5 SENSORS
A microcontroller is typically used in control applications where data is collected, the data is as-
similated and processed by the host algorithm, and a control decision and accompanying signals are
provided by the microcontroller. Input data for the microcontroller is collected by a complement of
input sensors. These sensors may be digital or analog in nature.
stationary optical
source and detector
rotating pair
disk D
Detector output
Ch A
Ch B
Optical encoders are available in a variety of types depending on the information desired.
There are two major types of optical encoders: incremental encoders and absolute encoders. An
absolute encoder is used when it is required to retain position information when power is lost. For
example, if you were using an optical encoder in a security gate control system, an absolute encoder
would be used to monitor the gate position. An incremental encoder is used in applications where
a velocity or a velocity and direction information is required.
The incremental encoder types may be further subdivided into tachometers and quadrature
encoders. An incremental tachometer encoder consists of a single track of etched opaque lines as
shown in Figure 8.6(a). It is used when the velocity of a rotating device is required. To calculate
222 8. ATMEL AVR OPERATING PARAMETERS AND INTERFACING
velocity, the number of detector pulses are counted in a fixed amount of time. Since the number of
pulses per encoder revolution is known, velocity may be calculated.
The quadrature encoder contains two tracks shifted in relationship to one another by 90
degrees. This allows the calculation of both velocity and direction. To determine direction, one
would monitor the phase relationship between Channel A and Channel B as shown in Figure
8.6(b). The absolute encoder is equipped with multiple data tracks to determine the precise location
of the encoder disk [Sick Stegmann].
VDD = 5 VDC
10K fixed
resistor
flex sensor:
-- 0 degree flex, 10K
-- 90 degree flex, 30-40K
b) flex action
c) equivalent circuit
I R2
I
R 220
+ + +
common cathode
VOH: 2.0 VDC 7-segment display
DIP
IOH : 15 mA (Vf 1.85 VDC @ If 12mA)
resistor
a a
microcontroller port
b
c 74LS244 f b
d octal buffer/
e line driver
f g
g
e c
R = (VOH - Vf) / If
d
R = (2.0 - 1.85)/ 12 mA
R = 12.5 ohms
10K (13)
PORTD[0]
(3) (5) (10) (12)
a PORTC[6]
b PORTC[5]
d PORTC[3]
g PORTC[0]
c PORTC[4]
e PORTC[2]
f PORTC[1]
numeral
12 7
hex rep
a a a a
f b f b f b f b
0 1 1 1 1 1 1 0 0x7E g g g g
e c e c e c e c
1 0 1 1 0 0 0 0 0x30 d d d d
2 1 1 0 1 1 0 1 0x6D
1 6
3 1 1 1 1 0 0 1 0x79 d) quad seven segment display pinout
4 0 1 1 0 0 1 1 0x33 UN(M)5624-11 EWRS
5 1 0 1 1 0 1 1 0x5D
6 0 0 1 1 1 1 1 0x1F
7 1 1 1 0 0 0 0 0x70
8 1 1 1 1 1 1 1 0x7F
9 1 1 1 0 0 1 1 0x73
c) numeral to segment converion
//*************************************************************************
void LED_character_display(unsigned int numeral, unsigned int position,
unsigned int decimal_point)
{
unsigned char output_value;
default:;
}
if(decimal_point != 0)
PORTB = output_value | 0x80; //illuminate decimal point
default:;
}
}
//*************************************************************************
47 G
47 G
47 G
R
microcontroller port
47 G
47 G
47 G
47 G
47 G
VDD VDD
3.0 K 2N2222
-
+
LM324 2N2907
3.0 K
C2
column
interface
select
C1
circuitry
C0
microcontroller
R6
R5
row select
R4
interface
circuitry
R3
R2
R1
R0
5 x 7 dot
matrix display
a) dot matrix display layout
74HC137
column
C2:C1:C0
select
3 1:8 decoder
R6
5 VDC
row select
R0
5 VDC
ta
n d/da
ma ble ta
com ena da
Vcc
10K
R/W-5
GND-1
VDD-2
Vo-3
RS-4
E-6
DB0-7
DB1-8
DB2-9
DB3-10
DB4-11
DB5-12
DB6-13
DB7-14
line1 line2
AND671GST
//*************************************************************************
//Pin 1: /Reset
//Pin 2: PD0 to DB0 LCD
//Pin 3: PD1 to DB1 LCD
//Pin 4: PD2 to DB2 LCD
//Pin 5: PD3 to DB3 LCD
//Pin 6: PD4 to DB4 LCD
//Pin 7: Vcc
//Pin 8: Gnd
//Pin 11: PD5 to DB6 LCD
//Pin 12: PD6 to DB6 LCD
//Pin 13: PD7 to DB7 LCD
//Pin 20: AVCC to Vcc
//Pin 21: AREF to Vcc
//Pin 22 Gnd
//Pin 27 PC4 to LCD Enable (E)
//Pin 28 PC5 to LCD RS
//include files************************************************************
//function prototypes******************************************************
void delay(unsigned int number_of_65_5ms_interrupts);
void init_timer0_ovf_interrupt(void);
void initialize_ports(void); //initializes ports
void power_on_reset(void); //returns system to startup state
void clear_LCD(void); //clears LCD display
void LCD_Init(void); //initialize AND671GST LCD
void putchar(unsigned char c); //send character to LCD
void putcommand(unsigned char c); //send command to LCD
void timer0_interrupt_isr(void);
void perform_countdown(void);
void clear_LCD(void);
void systems_A_OK(void);
void print_mission_complete(void);
void convert_int_to_string_display_LCD(unsigned int total_integer_value);
//program constants
#define TRUE 1
#define FALSE 0
#define OPEN 1
#define CLOSE 0
#define YES 1
#define NO 0
#define SAFE 1
#define UNSAFE 0
#define ON 1
#define OFF 0
//main program*************************************************************
//global variables
void main(void)
{
8.5. OUTPUT DEVICES 233
init_timer0_ovf_interrupt();
//initialize Timer0 to serve as elapsed
initialize_ports(); //initialize ports
perform_countdown();
delay(46);
:
:
:
systems_A_OK();
}//end main
//function definitions*****************************************************
//*************************************************************************
//initialize_ports: provides initial configuration for I/O ports
//*************************************************************************
void initialize_ports(void)
{
DDRB = 0xff; //PORTB[7:0] as output
PORTB= 0x00; //initialize low
DDRC = 0xff; //PORTC[7:0] as output
PORTC= 0x00; //initialize low
//*************************************************************************
//delay(unsigned int num_of_65_5ms_interrupts): this generic delay function
//provides the specified delay as the number of 65.5 ms "clock ticks" from
//the Timer0 interrupt.
//Note: this function is only valid when using a 1 MHz crystal or ceramic
// resonator
//*************************************************************************
//*************************************************************************
//int_timer0_ovf_interrupt(): The Timer0 overflow interrupt is being
//employed as a time base for a master timer for this project.
//The internal time base is set to operate at 1 MHz and then
//is divided by 256. The 8-bit Timer0
//register (TCNT0) overflows every 256 counts or every 65.5 ms.
//*************************************************************************
void init_timer0_ovf_interrupt(void)
{
TCCR0 = 0x04; //divide timer0 timebase by 256, overflow occurs every 65.5ms
TIMSK = 0x01; //enable timer0 overflow interrupt
asm("SEI"); //enable global interrupt
}
//*************************************************************************
//LCD_Init: initialization for an LCD connected in the following manner:
//LCD: AND671GST 1x16 character display
//LCD configured as two 8 character lines in a 1x16 array
//LCD data bus (pin 14-pin7) ATMEL 8: PORTD
//LCD RS (pin 28) ATMEL 8: PORTC[5]
//LCD E (pin 27) ATMEL 8: PORTC[4]
//*************************************************************************
void LCD_Init(void)
{
delay(1);
delay(1);
delay(1);
// output command string to initialize LCD
8.5. OUTPUT DEVICES 235
putcommand(0x38); //function set 8-bit
delay(1);
putcommand(0x38); //function set 8-bit
putcommand(0x38); //function set 8-bit
putcommand(0x38); //one line, 5x7 char
putcommand(0x0C); //display on
putcommand(0x01); //display clear-1.64 ms
putcommand(0x06); //entry mode set
putcommand(0x00); //clear display, cursor at home
putcommand(0x00); //clear display, cursor at home
}
//*************************************************************************
//putchar:prints specified ASCII character to LCD
//*************************************************************************
//*************************************************************************
//putcommand: performs specified LCD related command
//*************************************************************************
//*************************************************************************
//clear_LCD: clears LCD
//*************************************************************************
void clear_LCD(void)
{
putcommand(0x01);
}
//*************************************************************************
//void timer0_interrupt_isr(void)
//*************************************************************************
void timer0_interrupt_isr(void)
{
delay_timer++;
}
//*************************************************************************
//void perform_countdown(void)
//*************************************************************************
void perform_countdown(void)
{
clear_LCD();
putcommand(0x01); //cursor home
putcommand(0x80); //DD RAM location 1 - line 1
putchar(’1’); putchar (’0’); //print 10
delay(15); //delay 1s
//BLASTOFF!
putcommand(0x01); //cursor home
//*************************************************************************
//void systems_A_OK(void)
//*************************************************************************
void systems_A_OK(void)
{
clear_LCD();
putcommand(0x01); //cursor home
putcommand(0x80); //DD RAM location 1 - line 1
putchar(’S’); putchar(’Y’); putchar(’S’); putchar(’T’); putchar(’E’);
putchar(’M’); putchar(’S’); putchar(’ ’); putchar(’A’); putchar(’-’);
putchar(’O’); putchar(’K’); putchar(’!’); putchar(’!’); putchar(’!’);
}
//*************************************************************************
//void print_mission_complete(void)
//*************************************************************************
void print_mission_complete(void)
{
clear_LCD();
putcommand(0x01); //cursor home
putcommand(0x80); //DD RAM location 1 - line 1
putchar(’M’); putchar(’I’); putchar(’S’); putchar(’S’); putchar(’I’);
putchar(’O’); putchar(’N’);
8.5. OUTPUT DEVICES 239
//*************************************************************************
//end of file
//*************************************************************************
• Hello World: This function displays the greeting “hello world” and also displays elapsed time
since the last reset.
• Cursor: This function provides control over the underscore style cursor.
• Display: This function blanks the display without the loss of displayed information.
• Text Direction: This function controls which direction text flows from the cursor.
• Serial Input: This function accepts serial input and displays it on the LCD.
Rather than include the excellent Arduino resource here, the interested reader is referred to
the Arduino website for sample code and full documentation [www.arduino.cc].
VDD
load
Drain
Iload
from Gate
micro Source
Often the MOSFET is used to control a high power motor load. A motor is a notorious source
of noise. To isolate the microcontroller from the motor noise an optical isolator may be used as an
interface as shown in Figure 8.13(b). The link between the control signal from the microcontroller
to the high power load is via an optical link contained within a Solid State Relay (SSR). The SSR
is properly biased using techniques previously discussed.
DC solenoid
supply voltage
+
VDD protection
diode
-
I R
D
MOSFET
ILOAD
from G
S
micro RG
7404 Solid State Relay
• DC motor: A DC motor has a positive and negative terminal. When a DC power supply
of suitable current rating is applied to the motor it will rotate. If the polarity of the supply is
switched with reference to the motor terminals, the motor will rotate in the opposite direction.
The speed of the motor is roughly proportional to the applied voltage up to the rated voltage
of the motor.
• Servo motor: A servo motor provides a precision angular rotation for an applied pulse width
modulation duty cycle. As the duty cycle of the applied signal is varied, the angular displacement
of the motor also varies. This type of motor is used to change mechanical positions such as
the steering angle of a wheel.
• Stepper motor: A stepper motor as its name implies provides an incremental step change in
rotation (typically 2.5 degree per step) for a step change in control signal sequence. The motor
is typically controlled by a two or four wire interface. For the four wire stepper motor, the
242 8. ATMEL AVR OPERATING PARAMETERS AND INTERFACING
Vmotor
+
Veff
a) DC motor
b) Servo motor
1 step
4 control interface
signals circuitry
power
ground
c) Stepper motor
microcontroller provides a four bit control sequence to rotate the motor clockwise. To turn the
motor counterclockwise, the control sequence is reversed. The low power control signals are
interfaced to the motor via MOSFETs or power transistors to provide for the proper voltage
and current requirements of the pulse sequence.
DC motor
supply voltage
+
VDD protection
M diode
-
I R
D
MOSFET
ILOAD
G
S
7404 RG
from Solid State Relay
micro
12 VDC
1000uF
200 200
11DQ06
ZTX451 ZTX451
M
+
-
11DQ06
to PD4 to PD5
470 ZTX551 ZTX551
470
step
12 VDC
PORTD[7] 10K
TIP130
PORTD[6] TIP130
10K
PORTD[5] TIP130
10K
PORTD[4] TIP130
10K
//*************************************************************************
//target controller: ATMEL ATmega328
//
//ATMEL AVR ATmega328PV Controller Pin Assignments
//Pin 1 Reset - 1M resistor to Vdd, tact switch to ground, 1.0 uF to ground
//Pin 6 PD4 - to stepper motor coil
//Pin 7 Vdd - 1.0 uF to ground
//Pin 8 Gnd
//Pin 11 PD5 - to stepper motor coil
//Pin 12 PD6 - to stepper motor coil
//Pin 13 PD7 - to stepper motor coil
//Pin 14 PB0 to active high RC debounced switch - CW
//Pin 20 AVcc to Vdd
//Pin 21 ARef to Vcc
//Pin 22 Gnd to Ground
//*************************************************************************
//include files************************************************************
Atmega328
1 PUR - PC6 PC5 28
2 RXD1 - PD0 PC4 27
3 TXD1 - PD1 PC3 26
4 PD2 PC2 25
VDD 1M sys reset 5 PD3 PC1 24
1.0 uF 6 PD4 PCO 23
7 Vcc GND 22
8 GND AREF 21 VDD
9 PB6 AVCC 20
10 PB7 PB5 19
Vcc = 5.0 volts 11 PD5 PB4 18
12 PD6 PB3 17
74HC14
4.7 K 100K 13 PD7 PB2 16
PB0 14 PB0 PB1 15
CW 470K (1) (2)
0.1uF
3K
12 VDC
TIP130
TIP130 BC E
TIP130
TIP130
(top view)
TIP130
//function prototypes******************************************************
void initialize_ports(void); //initializes ports
void read_new_input(void); //used to read input change on PORTB
void init_timer0_ovf_interrupt(void); //used to initialize timer0 overflow
void timer0_interrupt_isr(void);
void delay(unsigned int);
//main program*************************************************************
//The main program checks PORTB for user input activity. If new activity
//is found, the program responds.
//global variables
unsigned char old_PORTB = 0x08; //present value of PORTB
unsigned char new_PORTB; //new values of PORTB
unsigned int input_delay; //delay counter - increment via Timer0
//overflow interrupt
void main(void)
{
initialize_ports(); //return LED configuration to default
init_timer0_ovf_interrupt(); //used to initialize timer0 overflow
while(1)
{
_StackCheck(); //check for stack overflow
read_new_input(); //read input status changes on PORTB
}
}//end main
//Function definitions
//*************************************************************************
//initialize_ports: provides initial configuration for I/O ports
//*************************************************************************
8.7. DC MOTOR SPEED AND DIRECTION CONTROL 249
void initialize_ports(void)
{
//PORTB
DDRB=0xfc; //PORTB[7-2] output, PORTB[1:0] input
PORTB=0x00; //disable PORTB pull-up resistors
//PORTC
DDRC=0xff; //set PORTC[7-0] as output
PORTC=0x00; //init low
//PORTD
DDRD=0xff; //set PORTD[7-0] as output
PORTD=0x00; //initialize low
}
//*************************************************************************
//read_new_input: functions polls PORTB for a change in status. If status
//change has occurred, appropriate function for status change is called
//Pin 1 PB0 to active high RC debounced switch - CW
//Pin 2 PB1 to active high RC debounced switch - CCW
//*************************************************************************
void read_new_input(void)
{
new_PORTB = (PINB & 0x03);
if(new_PORTB != old_PORTB){
switch(new_PORTB){ //process change in PORTB input
PORTD = 0x40;
delay(15);
PORTD = 0x00;
250 8. ATMEL AVR OPERATING PARAMETERS AND INTERFACING
delay(1);
PORTD = 0x20;
delay(15);
PORTD = 0x00;
delay(1);
PORTD = 0x10;
delay(15);
PORTD = 0x00;
delay(1);
}
break;
PORTD = 0x20;
delay(15);
PORTD = 0x00;
delay(1);
PORTD = 0x40;
delay(15);
PORTD = 0x00;
delay(1);
PORTD = 0x80;
delay(15);
PORTD = 0x00;
delay(1);
}
break;
8.7. DC MOTOR SPEED AND DIRECTION CONTROL 251
//*************************************************************************
//int_timer0_ovf_interrupt(): The Timer0 overflow interrupt is being
//employed as a time base for a master timer for this project. The internal
//oscillator of 8 MHz is divided internally by 8 to provide a 1 MHz time
//base and is divided by 256. The 8-bit Timer0 register (TCNT0) overflows
//every 256 counts or every 65.5 ms.
//*************************************************************************
void init_timer0_ovf_interrupt(void)
{
TCCR0B = 0x04; //divide timer0 timebase by 256, overfl. occurs every 65.5ms
TIMSK0 = 0x01; //enable timer0 overflow interrupt
asm("SEI"); //enable global interrupt
}
//*************************************************************************
//timer0_interrupt_isr:
//Note: Timer overflow 0 is cleared by hardware when executing the
//corresponding interrupt handling vector.
//*************************************************************************
void timer0_interrupt_isr(void)
{
input_delay++; //input delay processing
}
//*************************************************************************
//void delay(unsigned int number_of_65_5ms_interrupts)
//this generic delay function provides the specified delay as the number
//of 65.5 ms "clock ticks" from the Timer0 interrupt.
//Note: this function is only valid when using a 1 MHz crystal or ceramic
// resonator
252 8. ATMEL AVR OPERATING PARAMETERS AND INTERFACING
//*************************************************************************
//*************************************************************************
8.7.5 AC DEVICES
In a similar manner, a high power alternating current (AC) load may be switched on and off using
a low power control signal from the microcontroller. In this case, a Solid State Relay is used as the
switching device. Solid state relays are available to switch a high power DC or AC load [Crydom].
For example, the Crydom 558-CX240D5R is a printed circuit board mounted, air cooled, single
pole single throw (SPST), normally open (NO) solid state relay. It requires a DC control voltage of
3-15 VDC at 15 mA. However, this small microcontroller compatible DC control signal is used to
switch 12-280 VAC loads rated from 0.06 to 5 amps [Crydom] as shown in Figure 8.20.
To vary the direction of an AC motor you must use a bi-directional AC motor. A bi-directional
motor is equipped with three terminals: common, clockwise, and counterclockwise.To turn the motor
clockwise, an AC source is applied to the common and clockwise connections. In like manner, to
turn the motor counterclockwise, an AC source is applied to the common and counterclockwise
connections. This may be accomplished using two of the Crydom SSRs.
fuse
Atmega328 115 VAC
1 PUR - PC6 PC5 28
2 RXD1 - PD0 PC4 27
3 TXD1 - PD1 PC3 26
4 PD2 PC2 25
VDD sys reset 5 PD3 PC1 24
1M
6 PD4 PCO 23 1
1.0 uF
7 Vcc GND 22
8 GND AREF 21 2
VDD
9 PB6 AVCC 20
10 PB7 PB5 19
11 PD5 PB4 18
CRYDOM
12 PD6 PB3 17 AC fan
13 PD7 PB2 16
14 PB0 PB1 15 3:DC+
4:DC-
Figure 8.20: AC motor control circuit.
254 8. ATMEL AVR OPERATING PARAMETERS AND INTERFACING
220 220
5 VDC
1N4001
1N4001
+ 1N4001
M 3 VDC
-
240
temperature of the LM34 cools to a value as set by another potentiometer (PORTC[1]). When the
temperature of the LM34 falls below the set level, the cooling fan is shut off. If the temperature falls
while the fan is active, the PWM signal should gently return to zero, and wait for further temperature
changes.
Provided below is the embedded code for the system. This solution was developed by Geoff
Luke, UW MSEE, as a laboratory assignment for an Industrial Control class.
//*************************************************************************
//Geoff Luke
//EE 5880 - Industrial Controls
//PWM Fan Control
//Last Updated: April 10, 2010
//*************************************************************************
//Description: This program reads the voltage from an LM34 temperature
//sensor then sends the corresponding temperature to an LCD.
//If the sensed temperature is greater than the temperature designated
256 8. ATMEL AVR OPERATING PARAMETERS AND INTERFACING
Vcc = 5 V
cooling fan speed
Atmega328 10K
1 PUR - PC6 PC5 28
2 RXD1 - PD0 PC4 27
3 TXD1 - PD1 PC3 26
4 PD2 PC2 25 Vcc = 5 V
sys reset 5 PD3 PC1 24
VDD 1M
6 PD4 PCO 23
threshold temperature setting
1.0 uF 10K
7 Vcc GND 22
8 GND AREF 21 VDD
9 PB6 AVCC 20 Vcc = 5 V
10 PB7 PB5 19
11 PD5 PB4 18 LM34 temperature sensor
12 PD6 PB3 17 75
13 PD7 PB2 16 1uF
14 PB0 PB1 15
to PORTB[7:0]
Vcc
10K
data
GND-1
VDD-2
Vo-3
RS-4
R/W-5
E-6
DB0-7
DB1-8
DB2-9
DB3-10
DB4-11
DB5-12
DB6-13
DB7-14
AND671GST
DC motor
supply voltage
+
VDD protection
M diode
-
I R
D
MOSFET
ILOAD
G
S
7404 RG
Solid State Relay
from
micro
//include files************************************************************
#include<iom328pv.h>
//function prototypes******************************************************
void initializePorts();
void initializeADC();
unsigned int readADC(unsigned char);
void LCD_init();
void putChar(unsigned char);
void putcommand(unsigned char);
void voltageToLCD(unsigned int);
void temperatureToLCD(unsigned int);
void PWM(unsigned int);
void delay_5ms();
int main(void)
{
unsigned int tempVoltage, tempThreshold;
initializePorts();
initializeADC();
LCD_init();
while(1)
{
258 8. ATMEL AVR OPERATING PARAMETERS AND INTERFACING
tempVoltage = readADC(0);
temperatureToLCD(tempVoltage);
tempThreshold = readADC(1);
if(tempVoltage > tempThreshold)
{
PWM(1);
while(tempVoltage > tempThreshold)
{
tempVoltage = readADC(0);
temperatureToLCD(tempVoltage);
tempThreshold = readADC(1);
}
OCR1BL = 0x00;
}
}
return 0;
}
//*************************************************************************
void initializePorts()
{
DDRD = 0xFF;
DDRC = 0xFF;
DDRB = 0xFF;
}
//*************************************************************************
void initializeADC()
{
//select channel 0
ADMUX = 0;
ADCSRA |= 0x10;
}
//*************************************************************************
binary_weighted_voltage_low = ADCL;
//Read 8 low bits first (important)
//Read 2 high bits, multiply by 256
binary_weighted_voltage_high = ((unsigned int)(ADCH << 8));
binary_weighted_voltage = binary_weighted_voltage_low +
binary_weighted_voltage_high;
//*************************************************************************
//LCD_Init: initialization for an LCD connected in the following manner:
//LCD: AND671GST 1x16 character display
//LCD configured as two 8 character lines in a 1x16 array
//LCD data bus (pin 14-pin7) ATMEL ATmega16: PORTB
//LCD RS (pin 4) ATMEL ATmega16: PORTD[7]
//LCD E (pin 6) ATMEL ATmega16: PORTD[6]
260 8. ATMEL AVR OPERATING PARAMETERS AND INTERFACING
//*************************************************************************
void LCD_init(void)
{
delay_5ms();
delay_5ms();
delay_5ms();
// output command string to
//initialize LCD
putcommand(0x38); //function set 8-bit
delay_5ms();
putcommand(0x38); //function set 8-bit
delay_5ms();
putcommand(0x38); //function set 8-bit
putcommand(0x38); //one line, 5x7 char
putcommand(0x0E); //display on
putcommand(0x01); //display clear-1.64 ms
putcommand(0x06); //entry mode set
putcommand(0x00); //clear display, cursor at home
putcommand(0x00); //clear display, cursor at home
}
//*************************************************************************
//putchar:prints specified ASCII character to LCD
//*************************************************************************
//*************************************************************************
//delays for 5 ms with a clock speed of 1 MHz
//*************************************************************************
void delay_5ms(void)
{
unsigned int i;
//*************************************************************************
void voltageToLCD(unsigned int ADCValue)
{
float voltage;
unsigned int ones, tenths, hundredths;
voltage = (float)ADCValue*5.0/1024.0;
262 8. ATMEL AVR OPERATING PARAMETERS AND INTERFACING
ones = (unsigned int)voltage;
tenths = (unsigned int)((voltage-(float)ones)*10);
hundredths = (unsigned int)(((voltage-(float)ones)*10-(float)tenths)*10);
putcommand(0x80);
putChar((unsigned char)(ones)+48);
putChar(’.’);
putChar((unsigned char)(tenths)+48);
putChar((unsigned char)(hundredths)+48);
putChar(’V’);
putcommand(0xC0);
}
//*************************************************************************
{
float voltage,temperature;
unsigned int tens, ones, tenths;
voltage = (float)ADCValue*5.0/1024.0;
temperature = voltage*100;
putcommand(0x80);
putChar((unsigned char)(tens)+48);
putChar((unsigned char)(ones)+48);
putChar(’.’);
putChar((unsigned char)(tenths)+48);
putChar(’F’);
}
//*************************************************************************
8.9. EXTENDED EXAMPLE 1: AUTOMATED FAN COOLING SYSTEM 263
void PWM(unsigned int PWM_incr)
{
fan_Speed_float = ((float)(fan_Speed_int)/(float)(0x0400));
//*************************************************************************
void setup()
{
//LED indicators - wall detectors
pinMode(col_0_control, OUTPUT); //configure pin 0 for digital output
pinMode(col_1_control, OUTPUT); //configure pin 1 for digital output
9 VDC 9 VDC 9 VDC
R Column 1 R Column 2 R
Column 0
control control
control
Column 2 control
Column 1 control
Column 0 control
ANALOG IN R R R
5VGnd 0 123 4 5
void loop()
{
//read analog output from IR sensors
left_IR_sensor_value = analogRead(left_IR_sensor);
center_IR_sensor_value = analogRead(center_IR_sensor);
right_IR_sensor_value = analogRead(right_IR_sensor);
//*************************************************************************
8.11. EXTENDED EXAMPLE 3: FLIGHT SIMULATOR PANEL 269
Joystick
Status Panel
(PB5) (PB6)
(PB3) (PB4)
(PB2)
(PB1)
Trip MAIN
Duration (PB0) PWR CB
O2 AUX
SYS
Reset CB FUEL CB
DB7-14
DB6-13
DB5-12
DB4-11
DB3-10
DB2-9
DB1-8
DB0-7
E-6
R/W-5
RS-4
Vo-3
VDD-2
GND-1
LED A-15
LED K-16
LED6 9-PB6 AVCC-20 Vcc
RS
E piezo buzzer 10-PB7 PB5-19 LED5
data
DB5 11-PD5 PB4-18 LED4
LED
5V DB6 12-PD6 PB3-17 LED3
contrast
DB7 Vcc
13-PD7 PB2-16 LED2 buzzer
LED0 14-PB0 PB1-15 LED1 3850 Hz
10K DIP
resistor
PB0 PB1 PB2 PB3 PB4 PB5 PB6 PB7
8.11. EXTENDED EXAMPLE 3: FLIGHT SIMULATOR PANEL
271
272
initialize_timer
initialize_ports
initialize_ADC
initialize_LCD
no main pwr
set?
yes
read_ADC
calculate_trip_inc (25...)
perform_countdown
LED_blastoff_seq
SYSTEMS A-OK
while(1)
flt25_actions no
***LOW O2*** yes
8. ATMEL AVR OPERATING PARAMETERS AND INTERFACING
25% trip time? sound_alarm flash_LED_panel actions_complete? SYS A-OK reset_alarm restore_panel
RESET O2 CB
no
flt50_actions
***LOW FUEL*** yes
50% trip time? sound_alarm flash_LED_panel actions_complete? SYS A-OK reset_alarm restore_panel
ASSERT AUX FUEL
no flt75_actions
ENGINE OVERHEAT yes
75% trip time? sound_alarm flash_LED_panel actions_complete? SYS A-OK reset_alarm restore_panel
POWER DOWN 30S
no flt100_actions
no FUEL EXPENDED
100% trip time? sound_alarm flash_LED_panel LED_power_down_seq
MISSION ABORT
8.11. EXTENDED EXAMPLE 3: FLIGHT SIMULATOR PANEL 273
//last revised: April 10, 2010
//function: Controls Flight Simulator Control Panel for Larimer County
// School District #1
//
//ATMEL AVR ATmega328
//Chip Port Function I/O Source/Dest Asserted Notes
//*************************************************************************
//Pin 1: /Reset
//Pin 2: PD0 to DB0 LCD
//Pin 3: PD1 to DB1 LCD
//Pin 4: PD2 to DB2 LCD
//Pin 5: PD3 to DB3 LCD
//Pin 6: PD4 to DB4 LCD
//Pin 7: Vcc
//Pin 8: Gnd
//Pin 9: PB6 to LED6
//Pin 10: PB7 to piezo buzzer
//Pin 11: PD5 to DB6 LCD
//Pin 12: PD6 to DB6 LCD
//Pin 13: PD7 to DB7 LCD
//Pin 14: PB0 to LED0
//Pin 15: PB1 to LED1
//Pin 16: PB2 to LED2
//Pin 17: PB3 to LED3
//Pin 18: PB4 to LED4
//Pin 19: PB5 to LED5
//Pin 20: AVCC to Vcc
//Pin 21: AREF to Vcc
//Pin 22 Gnd
//Pin 23 ADC0 to trip duration potentiometer
//Pin 24 PC1 Engine Power Switch
//Pin 25 PC2 AUX Fuel circuit breaker
//Pin 26 PC3 O2 circuit breaker
//Pin 27 PC4 to LCD Enable (E)
//Pin 28 PC5 to LCD RS
//
//include files************************************************************
274 8. ATMEL AVR OPERATING PARAMETERS AND INTERFACING
//ATMEL register definitions for ATmega8
#include<iom328v.h>
//function prototypes******************************************************
void delay(unsigned int number_of_65_5ms_interrupts);
void init_timer0_ovf_interrupt(void);
void InitADC(void); //initialize ADC
void initialize_ports(void); //initializes ports
void power_on_reset(void);
//returns system to startup state
unsigned int ReadADC(unsigned char chan);//read value from ADC results
void clear_LCD(void); //clears LCD display
void LCD_Init(void); //initialize AND671GST LCD
void putchar(unsigned char c); //send character to LCD
void putcommand(unsigned char c); //send command to LCD
unsigned int ReadADC(unsigned char chan);//read value from ADC results
void timer0_interrupt_isr(void);
void flt25_actions(void);
void flt50_actions(void);
void flt75_actions(void);
void flt100_actions(void);
void sound_alarm(void);
void turn_off_LEDs(void);
void reset_alarm(void);
void restore_panel(void);
void LED_blastoff_sequence(void);
void LED_power_down_sequence(void);
void monitor_main_power_CB(void);
void monitor_O2_CB_reset(void);
void monitor_aux_fuel_CB(void);
void perform_countdown(void);
void print_LOWO2(void);
void print_LOW_FUEL(void);
void print_fuel_expended(void);
void print_OVERHEAT(void);
void print_trip_dur(void);
void flash_LED_panel(void);
void clear_LCD(void);
8.11. EXTENDED EXAMPLE 3: FLIGHT SIMULATOR PANEL 275
void calculate_trip_int(void);
void systems_A_OK(void);
//program constants
#define TRUE 1
#define FALSE 0
#define OPEN 1
#define CLOSE 0
#define YES 1
#define NO 0
#define SAFE 1
#define UNSAFE 0
#define ON 1
#define OFF 0
//main program*************************************************************
//global variables
unsigned int flt_25, flt_50, flt_75, flt_100;
unsigned int action25_done=NO, action50_done=NO;
unsigned int action75_done=NO, action100_done=NO;
unsigned int achieved25=NO, achieved50=NO;
unsigned int achieved75=NO, achieved100=NO;
unsigned int flt_timer=0;
unsigned int trip_duration_volt;
unsigned char PORTC_pullup_mask = 0x0e;
unsigned int flash_timer;
unsigned int PORTB_LEDs;
unsigned int flash_panel=NO;
unsigned int delay_timer;
unsigned int troubleshooting = 1;
void convert_display_voltage_LCD(int trip_duration_volt);
void convert_int_to_string_display_LCD(unsigned int total_integer_value);
void main(void)
{
276 8. ATMEL AVR OPERATING PARAMETERS AND INTERFACING
init_timer0_ovf_interrupt();
//initialize Timer0 to serve as elapsed
initialize_ports(); //initialize ports
InitADC(); //initialize ADC
LCD_Init(); //initialize LCD
print_trip_dur();
//prompt user to enter trip duration
monitor_main_power_CB();
clear_LCD();
while(1)
{
if(flt_timer > flt_25) achieved25 = YES;
if(flt_timer > flt_50) achieved50 = YES;
if(flt_timer > flt_75) achieved75 = YES;
if(flt_timer > flt_100) achieved100 = YES;
if((achieved25==YES)&&(action25_done==NO))
//25% flight complete
{
8.11. EXTENDED EXAMPLE 3: FLIGHT SIMULATOR PANEL 277
flt25_actions();
action25_done=YES;
systems_A_OK();
}
if((achieved50==YES)&&(action50_done==NO))
//50% flight complete
{
flt50_actions();
action50_done=YES;
systems_A_OK();
}
if((achieved75==YES)&&(action75_done==NO))
//75% flight complete
{
flt75_actions();
action75_done=YES;
systems_A_OK();
}
if((achieved100==YES)&&(action100_done==NO))
//100% flight complete
{
flt100_actions();
action100_done=YES;
}
}//end while
}//end main
//function definitions*****************************************************
//*************************************************************************
//initialize_ports: provides initial configuration for I/O ports
//
//Note: when the RSTDISBL fuse is unprogrammed, the RESET circuitry is
// connected to the pin, and the pin can not be used as an I/O pin.
//*************************************************************************
278 8. ATMEL AVR OPERATING PARAMETERS AND INTERFACING
void initialize_ports(void)
{
DDRB = 0xff; //PORTB[7:0] as output
PORTB= 0x00; //initialize low
DDRC = 0xb0; //set PORTC as output OROO_IIII 1011_0000
PORTC= PORTC_pullup_mask; //initialize pullups PORTC[3:1]
DDRD = 0xff; //set PORTD as output
PORTD =0x00; //initialize low
}
//*************************************************************************
//delay(unsigned int num_of_65_5ms_interrupts): this generic delay function
//provides the specified delay as the number of 65.5 ms "clock ticks"
//from the Timer0 interrupt.
//Note: this function is only valid when using a 1 MHz crystal or ceramic
// resonator
//*************************************************************************
//*************************************************************************
//InitADC: initialize ADC converter
//*************************************************************************
//*************************************************************************
//ReadADC: read analog voltage from ADC-the desired channel for conversion
//is passed in as an unsigned character variable. The result is returned
//as a left justified, 10 bit binary result.
//The ADC prescalar must be set to 8 to slow down the ADC clock at higher
//external clock frequencies (10 MHz) to obtain accurate results.
//*************************************************************************
//*************************************************************************
//int_timer0_ovf_interrupt(): The Timer0 overflow interrupt is being
//employed as a time base for a master timer for this project.
//The internal time base is set to operate at 1 MHz and then
//is divided by 256. The 8-bit Timer0 register (TCNT0) overflows
//every 256 counts or every 65.5 ms.
//*************************************************************************
280 8. ATMEL AVR OPERATING PARAMETERS AND INTERFACING
void init_timer0_ovf_interrupt(void)
{
TCCR0 = 0x04; //divide timer0 timebase by 256, overfl. occurs every 65.5ms
TIMSK = 0x01; //enable timer0 overflow interrupt
asm("SEI"); //enable global interrupt
}
//*************************************************************************
//LCD_Init: initialization for an LCD connected in the following manner:
//LCD: AND671GST 1x16 character display
//LCD configured as two 8 character lines in a 1x16 array
//LCD data bus (pin 14-pin7) ATMEL 8: PORTD
//LCD RS (pin 28) ATMEL 8: PORTC[5]
//LCD E (pin 27) ATMEL 8: PORTC[4]
//*************************************************************************
void LCD_Init(void)
{
delay(1);
delay(1);
delay(1);
// output command string to initialize LCD
putcommand(0x38); //function set 8-bit
delay(1);
putcommand(0x38); //function set 8-bit
putcommand(0x38); //function set 8-bit
putcommand(0x38); //one line, 5x7 char
putcommand(0x0C); //display on
putcommand(0x01); //display clear-1.64 ms
putcommand(0x06); //entry mode set
putcommand(0x00); //clear display, cursor at home
putcommand(0x00); //clear display, cursor at home
}
//*************************************************************************
//putchar:prints specified ASCII character to LCD
//*************************************************************************
//*************************************************************************
//putcommand: performs specified LCD related command
//*************************************************************************
//*************************************************************************
//clear_LCD: clears LCD
//*************************************************************************
void clear_LCD(void)
{
putcommand(0x01);
}
//*************************************************************************
//*void calculate_trip_int(void)
//*************************************************************************
void calculate_trip_int(void)
282 8. ATMEL AVR OPERATING PARAMETERS AND INTERFACING
{
unsigned int trip_duration_sec;
unsigned int trip_duration_int;
trip_duration_sec=(unsigned int)(((double)(trip_duration_volt)/1024.0)*
60.0*60.0);
trip_duration_int = (unsigned int)((double)(trip_duration_sec)/0.0655);
flt_25 = (unsigned int)((double)(trip_duration_int) * 0.25);
flt_50 = (unsigned int)((double)(trip_duration_int) * 0.50);
flt_75 = (unsigned int)((double)(trip_duration_int) * 0.75);
flt_100 = trip_duration_int;
}
//*************************************************************************
//void timer0_interrupt_isr(void)
//*************************************************************************
void timer0_interrupt_isr(void)
{
delay_timer++;
flt_timer++; //increment flight timer
if(flash_panel==YES)
{
if(flash_timer <= 8)
{
flash_timer++;
}
else
{
flash_timer = 0;
if(PORTB_LEDs == OFF)
{
PORTB = 0xff;
PORTB_LEDs = ON;
}
else
{
PORTB = 0x00;
8.11. EXTENDED EXAMPLE 3: FLIGHT SIMULATOR PANEL 283
PORTB_LEDs = OFF;
}
}
}
else
{
flash_timer = 0;
}
}
//*************************************************************************
//void flt25_actions(void)
//*************************************************************************
void flt25_actions(void)
{
sound_alarm();
flash_LED_panel();
print_LOWO2();
monitor_O2_CB_reset();
reset_alarm();
restore_panel();
action25_done = YES;
}
//*************************************************************************
//void flt50_actions(void)
//*************************************************************************
void flt50_actions(void)
{
sound_alarm();
flash_LED_panel();
print_LOW_FUEL();
monitor_aux_fuel_CB();
reset_alarm();
restore_panel();
action50_done = YES;
}
284 8. ATMEL AVR OPERATING PARAMETERS AND INTERFACING
//*************************************************************************
//void flt75_actions(void)
//*************************************************************************
void flt75_actions(void)
{
sound_alarm();
flash_LED_panel();
print_OVERHEAT();
delay(458); //delay 30s
monitor_main_power_CB();
reset_alarm();
restore_panel();
action75_done = YES;
}
//*************************************************************************
//void flt100_actions(void)
//*************************************************************************
void flt100_actions(void)
{
sound_alarm();
flash_LED_panel();
print_fuel_expended();
turn_off_LEDs();
action100_done = YES;
}
//*************************************************************************
//void sound_alarm(void)
//*************************************************************************
void sound_alarm(void)
{
PORTB = PORTB | 0x80;
}
8.11. EXTENDED EXAMPLE 3: FLIGHT SIMULATOR PANEL 285
//*************************************************************************
//void turn_off_LEDs(void)
//*************************************************************************
void turn_off_LEDs(void)
{
PORTB = PORTB & 0x80;
}
//*************************************************************************
//void reset_alarm(void)
//*************************************************************************
void reset_alarm(void)
{
PORTB = PORTB & 0x7F;
}
//*************************************************************************
//void restore_panel(void)
//*************************************************************************
void restore_panel(void)
{
flash_panel = NO;
PORTB = PORTB | 0x7F;
}
//*************************************************************************
//void LED_blastoff_sequence(void)
//*************************************************************************
void LED_blastoff_sequence(void)
{
PORTB = 0x00; //0000_0000
delay(15); //delay 1s
PORTB = 0x01; //0000_0001
286 8. ATMEL AVR OPERATING PARAMETERS AND INTERFACING
delay(15); //delay 1s
PORTB = 0x03; //0000_0011
delay(15); //delay 1s
PORTB = 0x07; //0000_0111
delay(15); //delay 1s
PORTB = 0x1F; //0001_1111
delay(15); //delay 1s
PORTB = 0x7F; //0111_1111
delay(15); //delay 1s
}
//*************************************************************************
//void LED_power_down_sequence(void)
//*************************************************************************
void LED_power_down_sequence(void)
{
PORTB = 0x7F; //0111_1111
delay(15); //delay 1s
PORTB = 0x1F; //0001_1111
delay(15); //delay 1s
PORTB = 0x07; //0000_0111
delay(15); //delay 1s
PORTB = 0x03; //0000_0011
delay(15); //delay 1s
PORTB = 0x01; //0000_0001
delay(15); //delay 1s
PORTB = 0x00; //0000_0000
delay(15); //delay 1s
}
//*************************************************************************
//void monitor_main_power_CB(void)
//*************************************************************************
void monitor_main_power_CB(void)
{
while((PINC & 0x02) == 0x02)
{
8.11. EXTENDED EXAMPLE 3: FLIGHT SIMULATOR PANEL 287
; //wait for PC1 to be exerted low
}
}
//*************************************************************************
//void monitor_O2_CB_reset(void)
//*************************************************************************
void monitor_O2_CB_reset(void)
{
while((PINC & 0x08) == 0x08)
{
; //wait for PC3 to be exerted low
}
}
//*************************************************************************
//void monitor_aux_fuel_CB(void)
//*************************************************************************
void monitor_aux_fuel_CB(void)
{
while((PINC & 0x04) == 0x04)
{
; //wait for PC2 to be exerted low
}
}
//*************************************************************************
//void perform_countdown(void)
//*************************************************************************
void perform_countdown(void)
{
clear_LCD();
putcommand(0x01); //cursor home
putcommand(0x80); //DD RAM location 1 - line 1
putchar(’1’); putchar (’0’); //print 10
delay(15); //delay 1s
288 8. ATMEL AVR OPERATING PARAMETERS AND INTERFACING
//BLASTOFF!
putcommand(0x01); //cursor home
putcommand(0x80); //DD RAM location 1 - line 1
putchar(’B’); putchar(’L’); putchar(’A’); putchar(’S’); putchar(’T’);
putchar(’O’); putchar(’F’); putchar(’F’); putchar(’!’);
//*************************************************************************
//void print_LOWO2(void)
//*************************************************************************
void print_LOWO2(void)
{
clear_LCD();
putcommand(0x01); //cursor home
putcommand(0x80); //DD RAM location 1 - line 1
putchar(’L’); putchar(’O’); putchar(’W’); putchar(’ ’); putchar(’O’);
putchar(’2’);
//*************************************************************************
//void print_LOW_FUEL(void)
//*************************************************************************
void print_LOW_FUEL(void)
{
clear_LCD();
putcommand(0x01); //cursor home
putcommand(0x80); //DD RAM location 1 - line 1
putchar(’L’); putchar(’O’); putchar(’W’); putchar(’ ’); putchar(’F’);
putchar(’U’); putchar(’E’); putchar(’L’);
//*************************************************************************
//void print_fuel_expended(void)
//*************************************************************************
void print_fuel_expended(void)
{
clear_LCD();
putcommand(0x01); //cursor home
putcommand(0x80); //DD RAM location 1 - line 1
putchar(’F’); putchar(’U’); putchar(’E’); putchar(’L’); putchar(’ ’);
putchar(’E’); putchar(’X’); putchar(’P’); putchar(’E’); putchar(’N’);
putchar(’D’); putchar(’E’); putchar(’D’);
//*************************************************************************
//void print_trip_dur(void);
//*************************************************************************
void print_trip_dur(void)
{
clear_LCD();
putcommand(0x01); //cursor home
putcommand(0x80); //DD RAM location 1 - line 1
putchar(’T’); putchar(’R’); putchar(’I’); putchar(’P’);
putchar(’T’); putchar(’I’); putchar(’M’); putchar(’E’); putchar(’:’);
putchar(’0’); putchar(’-’); putchar(’6’); putchar(’0’);
//*************************************************************************
//void print_OVERHEAT(void)
//*************************************************************************
void print_OVERHEAT(void)
{
clear_LCD();
putcommand(0x01); //cursor home
putcommand(0x80); //DD RAM location 1 - line 1
putchar(’E’); putchar(’N’); putchar(’G’); putchar(’I’); putchar(’N’);
putchar(’E’); putchar(’ ’); putchar(’O’); putchar(’V’); putchar(’E’);
putchar(’R’); putchar(’H’); putchar(’E’); putchar(’A’); putchar(’T’);
//*************************************************************************
//void systems_A_OK(void)
//*************************************************************************
void systems_A_OK(void)
{
clear_LCD();
putcommand(0x01); //cursor home
putcommand(0x80); //DD RAM location 1 - line 1
putchar(’S’); putchar(’Y’); putchar(’S’); putchar(’T’); putchar(’E’);
putchar(’M’); putchar(’S’); putchar(’ ’); putchar(’A’); putchar(’-’);
putchar(’O’); putchar(’K’); putchar(’!’); putchar(’!’); putchar(’!’);
}
//*************************************************************************
//void flash_LED_panel(void)
//*************************************************************************
void flash_LED_panel(void)
{
flash_panel = YES;
flash_timer = 0;
PORTB = 0x00;
PORTB_LEDs = OFF;
}
//*************************************************************************
//convert_display_voltage_LCD: converts binary weighted voltage to ASCII
//representation and prints result to LCD screen
//*************************************************************************
//*************************************************************************
//convert_int_to_string_display_LCD: converts 16 bit to unsigned integer
//values range from 0 to 65,535
//prints result to LCD screen
//*************************************************************************
//*************************************************************************
//end of file: flight_sim.c
//*************************************************************************
8.12 SUMMARY
In this chapter, we discussed the voltage and current operating parameters for the Arduino Duemi-
lanove processing board and the Atmel ATmega328 microcontroller. We discussed how this infor-
mation may be applied to properly design an interface for common input and output circuits. It must
be emphasized a properly designed interface allows the microcontroller to operate properly within its
parameter envelope. If due to a poor interface design, a microcontroller is used outside its prescribed
8.13. REFERENCES 295
operating parameter values, spurious and incorrect logic values will result. We provided interface in-
formation for a wide range of input and output devices. We also discussed the concept of interfacing
a motor to a microcontroller using PWM techniques coupled with high power MOSFET or SSR
switching devices. We closed the chapter with three extended examples.
8.13 REFERENCES
• Pack D, Barrett S (2002) 68HC12 Microcontroller: Theory and Applications. Prentice-Hall
Incorporated, Upper Saddle River, NJ.
• Barrett S, Pack D (2004) Embedded Systems Design with the 68HC12 and HCS12. Prentice-
Hall Incorporated, Upper Saddle River, NJ.
• Crydom Corporation, 2320 Paseo de las Americas, Suite 201, San Diego, CA (www.crydom.
com).
• Atmel 8-bit AVR Microcontroller with 16/32/64K Bytes In-System Programmable Flash, AT-
mega328P/V, ATmega324P/V, 644P/V data sheet: 8011I-AVR-05/08, Atmel Corporation,
2325 Orchard Parkway, San Jose, CA 95131.
• Barrett S, Pack D (2006) Microcontrollers Fundamentals for Engineers and Scientists. Morgan
and Claypool Publishers. DOI: 10.2200/S00025ED1V01Y200605DCS001
• Barrett S and Pack D (2008) Atmel AVR Microcontroller Primer Programming and Inter-
facing. Morgan and Claypool Publishers. DOI: 10.2200/S00100ED1V01Y200712DCS015
• Barrett S (2010) Embedded Systems Design with the Atmel AVR Microcontroller. Morgan
and Claypool Publishers. DOI: 10.2200/S00225ED1V01Y200910DCS025
2. Discuss the difference between the terms “sink” and “source” as related to current loading of
a microcontroller.
3. Can an LED with a series limiting resistor be directly driven by the Atmel microcontroller?
Explain.
296 8. ATMEL AVR OPERATING PARAMETERS AND INTERFACING
4. In your own words, provide a brief description of each of the microcontroller electrical param-
eters.
7. What is the difference between an incremental encoder and an absolute encoder? Describe
applications for each type.
8. What must be the current rating of the 2N2222 and 2N2907 transistors used in the tri-state
LED circuit? Support your answer.
9. Draw the circuit for a six character seven segment display. Fully specify all components. Write
a program to display “ATmega328. ”
12. What is the difference between a unipolar and bipolar stepper motor?
14. A stepper motor provides and angular displacement of 1.8 degrees per step. How can this
resolution be improved?
APPENDIX A
Figure A.1: Atmel AVR ATmega328 Register Set. (Figure used with permission of Atmel, Incorpo-
rated.)
298 A. ATMEGA328 REGISTER SET
Figure A.2: Atmel AVR ATmega328 Register Set. (Figure used with permission of Atmel, Incorpo-
rated.)
299
Figure A.3: Atmel AVR ATmega328 Register Set. (Figure used with permission of Atmel, Incorpo-
rated.)
300 A. ATMEGA328 REGISTER SET
Figure A.4: Atmel AVR ATmega328 Register Set. (Figure used with permission of Atmel, Incorpo-
rated.)
301
APPENDIX B
#ifndef __iom328pv_h
#define __iom328pv_h
/* 2006/10/01 created
*/
/* Port D */
#define PIND (*(volatile unsigned char *)0x29)
#define DDRD (*(volatile unsigned char *)0x2A)
#define PORTD (*(volatile unsigned char *)0x2B)
/* Port C */
#define PINC (*(volatile unsigned char *)0x26)
#define DDRC (*(volatile unsigned char *)0x27)
#define PORTC (*(volatile unsigned char *)0x28)
/* Port B */
#define PINB (*(volatile unsigned char *)0x23)
#define DDRB (*(volatile unsigned char *)0x24)
302 B. ATMEGA328 HEADER FILE
#define PORTB (*(volatile unsigned char *)0x25)
/* Port A */
#define PINA (*(volatile unsigned char *)0x20)
#define DDRA (*(volatile unsigned char *)0x21)
#define PORTA (*(volatile unsigned char *)0x22)
/* Timer/Counter Interrupts */
#define TIFR0 (*(volatile unsigned char *)0x35)
#define OCF0B 2
#define OCF0A 1
#define TOV0 0
#define TIMSK0 (*(volatile unsigned char *)0x6E)
#define OCIE0B 2
#define OCIE0A 1
#define TOIE0 0
#define TIFR1 (*(volatile unsigned char *)0x36)
#define ICF1 5
#define OCF1B 2
#define OCF1A 1
#define TOV1 0
#define TIMSK1 (*(volatile unsigned char *)0x6F)
#define ICIE1 5
#define OCIE1B 2
#define OCIE1A 1
#define TOIE1 0
#define TIFR2 (*(volatile unsigned char *)0x37)
#define OCF2B 2
#define OCF2A 1
#define TOV2 0
#define TIMSK2 (*(volatile unsigned char *)0x70)
#define OCIE2B 2
#define OCIE2A 1
#define TOIE2 0
/* External Interrupts */
#define EIFR (*(volatile unsigned char *)0x3C)
#define INTF2 2
#define INTF1 1
303
#define INTF0 0
#define EIMSK (*(volatile unsigned char *)0x3D)
#define INT2 2
#define INT1 1
#define INT0 0
#define EICRA (*(volatile unsigned char *)0x69)
#define ISC21 5
#define ISC20 4
#define ISC11 3
#define ISC10 2
#define ISC01 1
#define ISC00 0
/* GPIOR */
#define GPIOR0 (*(volatile unsigned char *)0x3E)
#define GPIOR1 (*(volatile unsigned char *)0x4A)
#define GPIOR2 (*(volatile unsigned char *)0x4B)
/* EEPROM */
#define EECR (*(volatile unsigned char *)0x3F)
#define EEPM1 5
#define EEPM0 4
#define EERIE 3
304 B. ATMEGA328 HEADER FILE
#define EEMPE 2
#define EEMWE 2
#define EEPE 1
#define EEWE 1
#define EERE 0
#define EEDR (*(volatile unsigned char *)0x40)
#define EEAR (*(volatile unsigned int *)0x41)
#define EEARL (*(volatile unsigned char *)0x41)
#define EEARH (*(volatile unsigned char *)0x42)
/* GTCCR */
#define GTCCR (*(volatile unsigned char *)0x43)
#define TSM 7
#define PSRASY 1
#define PSR2 1
#define PSRSYNC 0
#define PSR10 0
/* Timer/Counter 0 */
#define OCR0B (*(volatile unsigned char *)0x48)
#define OCR0A (*(volatile unsigned char *)0x47)
#define TCNT0 (*(volatile unsigned char *)0x46)
#define TCCR0B (*(volatile unsigned char *)0x45)
#define FOC0A 7
#define FOC0B 6
#define WGM02 3
#define CS02 2
#define CS01 1
#define CS00 0
#define TCCR0A (*(volatile unsigned char *)0x44)
#define COM0A1 7
#define COM0A0 6
#define COM0B1 5
#define COM0B0 4
#define WGM01 1
#define WGM00 0
/* SPI */
#define SPCR (*(volatile unsigned char *)0x4C)
305
#define SPIE 7
#define SPE 6
#define DORD 5
#define MSTR 4
#define CPOL 3
#define CPHA 2
#define SPR1 1
#define SPR0 0
#define SPSR (*(volatile unsigned char *)0x4D)
#define SPIF 7
#define WCOL 6
#define SPI2X 0
#define SPDR (*(volatile unsigned char *)0x4E)
/* OCDR */
#define OCDR (*(volatile unsigned char *)0x51)
#define IDRD 7
/* MCU */
#define MCUSR (*(volatile unsigned char *)0x54)
#define JTRF 4
#define WDRF 3
#define BORF 2
#define EXTRF 1
#define PORF 0
#define MCUCR (*(volatile unsigned char *)0x55)
#define JTD 7
#define PUD 4
306 B. ATMEGA328 HEADER FILE
#define IVSEL 1
#define IVCE 0
/* Stack Pointer */
#define SP (*(volatile unsigned int *)0x5D)
#define SPL (*(volatile unsigned char *)0x5D)
#define SPH (*(volatile unsigned char *)0x5E)
/* Status REGister */
#define SREG (*(volatile unsigned char *)0x5F)
/* PRR */
#define PRR0 (*(volatile unsigned char *)0x64)
#define PRTWI 7
#define PRTIM2 6
#define PRTIM0 5
#define PRUSART1 4
#define PRTIM1 3
#define PRSPI 2
#define PRUSART0 1
#define PRADC 0
/* ADC */
#define ADC (*(volatile unsigned int *)0x78)
#define ADCL (*(volatile unsigned char *)0x78)
#define ADCH (*(volatile unsigned char *)0x79)
#define ADCSRA (*(volatile unsigned char *)0x7A)
#define ADEN 7
#define ADSC 6
#define ADATE 5
#define ADIF 4
#define ADIE 3
#define ADPS2 2
#define ADPS1 1
#define ADPS0 0
#define ADCSRB (*(volatile unsigned char *)0x7B)
#define ACME 6
#define ADTS2 2
308 B. ATMEGA328 HEADER FILE
#define ADTS1 1
#define ADTS0 0
#define ADMUX (*(volatile unsigned char *)0x7C)
#define REFS1 7
#define REFS0 6
#define ADLAR 5
#define MUX4 4
#define MUX3 3
#define MUX2 2
#define MUX1 1
#define MUX0 0
/* DIDR */
#define DIDR0 (*(volatile unsigned char *)0x7E)
#define ADC7D 7
#define ADC6D 6
#define ADC5D 5
#define ADC4D 4
#define ADC3D 3
#define ADC2D 2
#define ADC1D 1
#define ADC0D 0
#define DIDR1 (*(volatile unsigned char *)0x7F)
#define AIN1D 1
#define AIN0D 0
/* Timer/Counter1 */
#define ICR1 (*(volatile unsigned int *)0x86)
#define ICR1L (*(volatile unsigned char *)0x86)
#define ICR1H (*(volatile unsigned char *)0x87)
#define OCR1B (*(volatile unsigned int *)0x8A)
#define OCR1BL (*(volatile unsigned char *)0x8A)
#define OCR1BH (*(volatile unsigned char *)0x8B)
#define OCR1A (*(volatile unsigned int *)0x88)
#define OCR1AL (*(volatile unsigned char *)0x88)
#define OCR1AH (*(volatile unsigned char *)0x89)
#define TCNT1 (*(volatile unsigned int *)0x84)
#define TCNT1L (*(volatile unsigned char *)0x84)
#define TCNT1H (*(volatile unsigned char *)0x85)
309
#define TCCR1C (*(volatile unsigned char *)0x82)
#define FOC1A 7
#define FOC1B 6
#define TCCR1B (*(volatile unsigned char *)0x81)
#define ICNC1 7
#define ICES1 6
#define WGM13 4
#define WGM12 3
#define CS12 2
#define CS11 1
#define CS10 0
#define TCCR1A (*(volatile unsigned char *)0x80)
#define COM1A1 7
#define COM1A0 6
#define COM1B1 5
#define COM1B0 4
#define WGM11 1
#define WGM10 0
/* Timer/Counter2 */
#define ASSR (*(volatile unsigned char *)0xB6)
#define EXCLK 6
#define AS2 5
#define TCN2UB 4
#define OCR2AUB 3
#define OCR2BUB 2
#define TCR2AUB 1
#define TCR2BUB 0
#define OCR2B (*(volatile unsigned char *)0xB4)
#define OCR2A (*(volatile unsigned char *)0xB3)
#define TCNT2 (*(volatile unsigned char *)0xB2)
#define TCCR2B (*(volatile unsigned char *)0xB1)
#define FOC2A 7
#define FOC2B 6
#define WGM22 3
#define CS22 2
#define CS21 1
#define CS20 0
#define TCCR2A (*(volatile unsigned char *)0xB0)
310 B. ATMEGA328 HEADER FILE
#define COM2A1 7
#define COM2A0 6
#define COM2B1 5
#define COM2B0 4
#define WGM21 1
#define WGM20 0
/* 2-wire SI */
#define TWBR (*(volatile unsigned char *)0xB8)
#define TWSR (*(volatile unsigned char *)0xB9)
#define TWPS1 1
#define TWPS0 0
#define TWAR (*(volatile unsigned char *)0xBA)
#define TWGCE 0
#define TWDR (*(volatile unsigned char *)0xBB)
#define TWCR (*(volatile unsigned char *)0xBC)
#define TWINT 7
#define TWEA 6
#define TWSTA 5
#define TWSTO 4
#define TWWC 3
#define TWEN 2
#define TWIE 0
#define TWAMR (*(volatile unsigned char *)0xBD)
/* USART0 */
#define UBRR0H (*(volatile unsigned char *)0xC5)
#define UBRR0L (*(volatile unsigned char *)0xC4)
#define UBRR0 (*(volatile unsigned int *)0xC4)
#define UCSR0C (*(volatile unsigned char *)0xC2)
#define UMSEL01 7
#define UMSEL00 6
#define UPM01 5
#define UPM00 4
#define USBS0 3
#define UCSZ01 2
#define UCSZ00 1
#define UCPOL0 0
#define UCSR0B (*(volatile unsigned char *)0xC1)
311
#define RXCIE0 7
#define TXCIE0 6
#define UDRIE0 5
#define RXEN0 4
#define TXEN0 3
#define UCSZ02 2
#define RXB80 1
#define TXB80 0
#define UCSR0A (*(volatile unsigned char *)0xC0)
#define RXC0 7
#define TXC0 6
#define UDRE0 5
#define FE0 4
#define DOR0 3
#define UPE0 2
#define U2X0 1
#define MPCM0 0
#define UDR0 (*(volatile unsigned char *)0xC6)
/* USART1 */
#define UBRR1H (*(volatile unsigned char *)0xCD)
#define UBRR1L (*(volatile unsigned char *)0xCC)
#define UBRR1 (*(volatile unsigned int *)0xCC)
#define UCSR1C (*(volatile unsigned char *)0xCA)
#define UMSEL11 7
#define UMSEL10 6
#define UPM11 5
#define UPM10 4
#define USBS1 3
#define UCSZ11 2
#define UCSZ10 1
#define UCPOL1 0
#define UCSR1B (*(volatile unsigned char *)0xC9)
#define RXCIE1 7
#define TXCIE1 6
#define UDRIE1 5
#define RXEN1 4
#define TXEN1 3
#define UCSZ12 2
312 B. ATMEGA328 HEADER FILE
#define RXB81 1
#define TXB81 0
#define UCSR1A (*(volatile unsigned char *)0xC8)
#define RXC1 7
#define TXC1 6
#define UDRE1 5
#define FE1 4
#define DOR1 3
#define UPE1 2
#define U2X1 1
#define MPCM1 0
#define UDR1 (*(volatile unsigned char *)0xCE)
/* bits */
/* Port A */
#define PORTA7 7
#define PORTA6 6
#define PORTA5 5
#define PORTA4 4
#define PORTA3 3
#define PORTA2 2
#define PORTA1 1
#define PORTA0 0
#define PA7 7
#define PA6 6
#define PA5 5
#define PA4 4
#define PA3 3
#define PA2 2
#define PA1 1
#define PA0 0
#define DDA7 7
#define DDA6 6
#define DDA5 5
#define DDA4 4
#define DDA3 3
#define DDA2 2
313
#define DDA1 1
#define DDA0 0
#define PINA7 7
#define PINA6 6
#define PINA5 5
#define PINA4 4
#define PINA3 3
#define PINA2 2
#define PINA1 1
#define PINA0 0
/* Port B */
#define PORTB7 7
#define PORTB6 6
#define PORTB5 5
#define PORTB4 4
#define PORTB3 3
#define PORTB2 2
#define PORTB1 1
#define PORTB0 0
#define PB7 7
#define PB6 6
#define PB5 5
#define PB4 4
#define PB3 3
#define PB2 2
#define PB1 1
#define PB0 0
#define DDB7 7
#define DDB6 6
#define DDB5 5
#define DDB4 4
#define DDB3 3
#define DDB2 2
#define DDB1 1
#define DDB0 0
#define PINB7 7
#define PINB6 6
#define PINB5 5
314 B. ATMEGA328 HEADER FILE
#define PINB4 4
#define PINB3 3
#define PINB2 2
#define PINB1 1
#define PINB0 0
/* Port C */
#define PORTC7 7
#define PORTC6 6
#define PORTC5 5
#define PORTC4 4
#define PORTC3 3
#define PORTC2 2
#define PORTC1 1
#define PORTC0 0
#define PC7 7
#define PC6 6
#define PC5 5
#define PC4 4
#define PC3 3
#define PC2 2
#define PC1 1
#define PC0 0
#define DDC7 7
#define DDC6 6
#define DDC5 5
#define DDC4 4
#define DDC3 3
#define DDC2 2
#define DDC1 1
#define DDC0 0
#define PINC7 7
#define PINC6 6
#define PINC5 5
#define PINC4 4
#define PINC3 3
#define PINC2 2
#define PINC1 1
#define PINC0 0
315
/* Port D */
#define PORTD7 7
#define PORTD6 6
#define PORTD5 5
#define PORTD4 4
#define PORTD3 3
#define PORTD2 2
#define PORTD1 1
#define PORTD0 0
#define PD7 7
#define PD6 6
#define PD5 5
#define PD4 4
#define PD3 3
#define PD2 2
#define PD1 1
#define PD0 0
#define DDD7 7
#define DDD6 6
#define DDD5 5
#define DDD4 4
#define DDD3 3
#define DDD2 2
#define DDD1 1
#define DDD0 0
#define PIND7 7
#define PIND6 6
#define PIND5 5
#define PIND4 4
#define PIND3 3
#define PIND2 2
#define PIND1 1
#define PIND0 0
/* PCMSK3 */
#define PCINT31 7
#define PCINT30 6
#define PCINT29 5
316 B. ATMEGA328 HEADER FILE
#define PCINT28 4
#define PCINT27 3
#define PCINT26 2
#define PCINT25 1
#define PCINT24 0
/* PCMSK2 */
#define PCINT23 7
#define PCINT22 6
#define PCINT21 5
#define PCINT20 4
#define PCINT19 3
#define PCINT18 2
#define PCINT17 1
#define PCINT16 0
/* PCMSK1 */
#define PCINT15 7
#define PCINT14 6
#define PCINT13 5
#define PCINT12 4
#define PCINT11 3
#define PCINT10 2
#define PCINT9 1
#define PCINT8 0
/* PCMSK0 */
#define PCINT7 7
#define PCINT6 6
#define PCINT5 5
#define PCINT4 4
#define PCINT3 3
#define PCINT2 2
#define PCINT1 1
#define PCINT0 0
/* lock bits */
#define BLB12 5
#define BLB11 4
317
#define BLB02 3
#define BLB01 2
#define LB2 1
#define LB1 0
/* extended fuses */
#define BODLEVEL2 2
#define BODLEVEL1 1
#define BODLEVEL0 0
#define iv_RESET 1
#define iv_INT0 2
#define iv_EXT_INT0 2
#define iv_INT1 3
#define iv_EXT_INT1 3
#define iv_INT2 4
318 B. ATMEGA328 HEADER FILE
#define iv_EXT_INT2 4
#define iv_PCINT0 5
#define iv_PCINT1 6
#define iv_PCINT2 7
#define iv_PCINT3 8
#define iv_WDT 9
#define iv_TIMER2_COMPA 10
#define iv_TIMER2_COMPB 11
#define iv_TIMER2_OVF 12
#define iv_TIM2_COMPA 10
#define iv_TIM2_COMPB 11
#define iv_TIM2_OVF 12
#define iv_TIMER1_CAPT 13
#define iv_TIMER1_COMPA 14
#define iv_TIMER1_COMPB 15
#define iv_TIMER1_OVF 16
#define iv_TIM1_CAPT 13
#define iv_TIM1_COMPA 14
#define iv_TIM1_COMPB 15
#define iv_TIM1_OVF 16
#define iv_TIMER0_COMPA 17
#define iv_TIMER0_COMPB 18
#define iv_TIMER0_OVF 19
#define iv_TIM0_COMPA 17
#define iv_TIM0_COMPB 18
#define iv_TIM0_OVF 19
#define iv_SPI_STC 20
#define iv_USART0_RX 21
#define iv_USART0_RXC 21
#define iv_USART0_DRE 22
#define iv_USART0_UDRE 22
#define iv_USART0_TX 23
#define iv_USART0_TXC 23
#define iv_ANA_COMP 24
#define iv_ANALOG_COMP 24
#define iv_ADC 25
#define iv_EE_RDY 26
#define iv_EE_READY 26
#define iv_TWI 27
319
#define iv_TWSI 27
#define iv_SPM_RDY 28
#define iv_SPM_READY 28
#define iv_USART1_RX 29
#define iv_USART1_RXC 29
#define iv_USART1_DRE 30
#define iv_USART1_UDRE 30
#define iv_USART1_TX 31
#define iv_USART1_TXC 31
/* */
#endif
321
Author’s Biography
STEVEN F. BARRETT
Steven F. Barrett, Ph.D., P.E., received the BS Electronic Engineering Technology from the
University of Nebraska at Omaha in 1979, the M.E.E.E. from the University of Idaho at Moscow
in 1986, and the Ph.D. from The University of Texas at Austin in 1993. He was formally an active
duty faculty member at the United States Air Force Academy, Colorado and is now the Associate
Dean of Academic Programs at the University of Wyoming. He is a member of IEEE (senior)
and Tau Beta Pi (chief faculty advisor). His research interests include digital and analog image
processing, computer-assisted laser surgery, and embedded controller systems. He is a registered
Professional Engineer in Wyoming and Colorado. He co-wrote with Dr. Daniel Pack six textbooks
on microcontrollers and embedded systems. In 2004, Barrett was named “Wyoming Professor of the
Year” by the Carnegie Foundation for the Advancement of Teaching and in 2008 was the recipient of
the National Society of Professional Engineers (NSPE) Professional Engineers in Higher Education,
Engineering Education Excellence Award.
323
Index