Embedded System Design Using Arduino 18ECO108J: Unit Iv

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

EMBEDDED SYSTEM

DESIGN USING ARDUINO

18ECO108J

UNIT IV

Prepared by R.Manohari (AP- Dept of ECE) 1


Contents
• I/O Programming
• Introduction to Timer/Counter
• Timer Programming
• Interrupts
• Interrupts Programming
• External Interrupts

2
I/O Programming

• Digital Output Circuitry


• Digital Input Circuitry
• pinMode()
• Arduino Uno Pin Definitions
• digitalWrite()
• delay()
• digitalRead()
• Analog Input Circuitry
• analogRead()
Digital Output Circuitry
• To fully appreciate the software interface
to microcontrollers and to gain insight
into how to interface controllers to
external devices, it is useful to examine
the underlying circuitry.
• In this chapter we shall investigate the
basic digital output circuitry.
• Anything that can be controlled with a
simple logical yes/no, true/false input can
be a target.
• Although controllers usually have limited
output current and voltage capability,
they can control devices such as power
transistors which can in turn control more
demanding loads.
• A simplified diagram of the General
Purpose Input-Output (GPIO) circuitry can
be seen in Figure 19.1.
• The circuit represents a single bit.
• An IO port consists of eight bits, typically.
• Therefore this circuitry would be replicated seven more times to
accommodate a single byte-wide port, of which there may be several.
• Also, in order to reduce the external pin count of the IC, pins may be
multiplexed with other functions.
• The multiplexers are not shown in this schematic.
• Pxn, at the far left of the schematic, represents the physical output pin.
• The data bus shown along the right edge is the source for data being
written to the physical pin and also the destination for data being read
from the pin.
• For now we shall focus on writing to the output and examine input
functionality in a later chapter.
• While this circuit is specific to the ATmega series, it is representative of
GPIO circuitry found in many microcontrollers.
• First off, the PUD (Pull Up Disable), SLEEP
and CLK (CLocK) control lines are common
to all bits and ports.
• We may ignore them for our purposes.
• The lowest section is removed because it
is involved with input functionality.
• The same goes for the upper section
surrounding the MOSFET.
• Finally, a series of two gates, an inverter
and a multiplexer clustered around the
middle section have been removed.
• These serve a particular (and perhaps
somewhat esoteric) function, namely the
ability to rapidly toggle a bit.
• Again, this section is not needed for
typical functioning.
Digital Input Circuitry
• In this chapter we shall investigate the
basic digital input circuitry.
• This can be used to sense the state of
external switches and other two-state
devices.
• These devices can be active, that is,
generating a high or low voltage, or
they can be simple passive switches
connecting to ground through the use
of an optional internal pull-up resistor.
• A simplified diagram of the General
Purpose Input-Output (GPIO) circuitry
can be seen in Figure 20.1.
• As in the previous chapter, this is for a
single bit.
• Once again, we shall remove sections
that are not pertinent to the input
function or for enhanced clarity.
• Figure 20.2 presents a further simplification
focusing solely on the input portion.
• Once again we have removed the gates
surrounding PORTxn that create the bit toggle
function.
• Further, we have removed the PUD, SLEEP
and CLK signals and simplified the lower
section leaving just the Schmitt Trigger.
• The read process will be similar to the write
process examined in the previous chapter.
• To read a signal on an external pin, we will
need to write a logic low to the data direction
bit DDxn.
• This will disconnect PORTxn from the physical
pin, Pxn, because the tri-state buffer will go to
high-Z state.
• In summary, to read from the external pin, a low is written to the
appropriate DDR bit, placing this circuit in read (input) mode.
• If a pull-up is desired, a high is written to the appropriate PORT bit.
• If a pull-up is not desired, a low is written to the PORT bit.
• Once this is done, the external value can be read from the PIN
register.
• As with the output circuitry, the DDR and PORT do not have to be
rewritten prior to each subsequent read from the PIN.
• The D flip-flops will hold their values until they are rewritten, thus
maintaining the current data direction and pull-up status.
pinMode()
• Digital IO will allow us to read the state of an input pin as well as produce a logical high or low at
an output pin. Examples include reading the state of an external switch and turning an LED or
motor on and off.
• pinMode()
• Description
Configures the specified pin to behave either as an input or an output.
As of Arduino 1.0.1, it is possible to enable the internal pullup resistors with the mode
INPUT_PULLUP.
Additionally, the INPUT mode explicitly disables the internal pullups.
• Syntax
pinMode(pin, mode)
• Parameters
pin: the number of the pin whose mode you wish to set
mode: INPUT, OUTPUT, or INPUT_PULLUP.
• Returns
None
Arduino Uno Pin Definitions
• So we’d first have to think in
terms of an Arduino pin number
instead of a port bit number.
• Below is a table of Arduino Uno
pin designations versus ATmega
328P port and pin naming.
• when originally we wrote
pinMode( 8, OUTPUT );
• The function decoded Arduino pin 8 as being bit 0 of port B (i.e.,
PORTB). It also determined that the corresponding DDR is DDRB.
• The bit 0 mask is 0x01 and it was ORed with the contents of DDRB,
thus selecting the direction for that bit as output mode.
digitalWrite()
• digitalWrite()
• Description
• Write a HIGH or a LOW value to a digital pin.
• If the pin has been configured as an OUTPUT with pinMode(), its voltage
will be set to the corresponding value: 5V (or 3.3V on 3.3V boards) for
HIGH, 0V (ground) for LOW.
• If the pin is configured as an INPUT, writing a HIGH value with digitalWrite()
will enable an internal 20K pullup resistor (see the tutorial on digital pins).
Writing LOW will disable the pullup.
• The pullup resistor is enough to light an LED dimly, so if LEDs appear to
work, but very dimly, this is a likely cause. The remedy is to set the pin to
an output with the pinMode() function.
• NOTE: Digital pin 13 is harder to use as a digital input than the other digital pins because
it has an LED and resistor attached to it that's soldered to the board on most boards. If
you enable its internal 20k pull-up resistor, it will hang at around 1.7 V instead of the
expected 5V because the onboard LED and series resistor pull the voltage level down,
meaning it always returns LOW.
• If you must use pin 13 as a digital input, use an external pull down resistor.
• Syntax
• digitalWrite(pin, value)
• Parameters
• pin: the pin number
• value: HIGH or LOW
• Returns
• none
• Example
delay()
• Description
• Pauses the program for the amount of time (in miliseconds) specified
as parameter. (There are 1000 milliseconds in a second.)
• Syntax
• delay( ms )
• Parameters
• ms: the number of milliseconds
to pause (unsigned long)
• Returns
• nothing
digitalRead()
• Description
• Reads the value
from a specified
digital pin, either
HIGH or LOW.
• Syntax
• digitalRead( pin )
• Parameters
• pin: the number of
the digital pin you
want to read (int)
• Returns
• HIGH or LOW
Analog Input Circuitry
• A block diagram of the ATmega ADC system is found in Figure.
• The heart of the system is a single successive approximation
DAC with 10 bit resolution. Maximum conversion speed at full
resolution is about 15 k samples per second.
• Clearly this is insufficient for applications such as CD quality
audio or video but is more than fast enough for most sensor
and user interface monitoring.
• The converter performance specifications include 0.5 LSB
integral non-linearity error and ―2 LSB absolute accuracy. An
on-board reference is available although an external reference
may also be used.
• The converter includes a sample-and-hold system to stabilize
the measurement for rapidly changing input signals.
• There is an eight channel mux although only six of these
channels are used on some packages (the package used on the
standard configuration of the Arduino Uno board uses six).
• The input circuitry is optimized for sources that have an output
impedance of 10 KΩ or less. It is also very important to
recognize that the system is unipolar and will only respond to
positive voltages. If the signal to be measured is bipolar, some
manner of of DC level shifting will be required.
• There are several registers associated with the ADC ADPS2-0: Prescaler bits. These bits are used to
system. These are shown in Figure. divide down the system clock to the ADC clock. 000
and 001 yield division by two and increasing values
• ADCSRA and ADCSRB are the Analog-Digital Control and double the clock divide with 111 yielding divide by
Status Registers. Here are their bit descriptions: 128.
ADEN: ENable. Set to one to enable to Analog-Digital ACME: Analog Comparator Multiplexer Enabled.
system. Setting these bits allows any of the eight multiplexed
ADSC : Start Conversion. Set to one to start a conversion. ADC pins to replace the negative input to the analog
When the conversion is complete, the hardware will reset comparator.
this bit to zero. Writing zero to this bit will have no effect. ADTS2-0: Trigger Source. If ADATE is set to one,
ADATE: Auto Trigger Enable. Set to one for auto trigger these bits determine the trigger source. 000 is free
mode. A rising edge on the selected trigger signal will start a running mode, 001 triggers from the analog
conversion. comparator, 010 uses External Interrupt Request 0,
and the remaining values set various timer/counter
The trigger source is selected via the ADTS bits in the sources.
ADCSRB register (below).
ADIF: Interrupt Flag. This is used with interrupt mode. This
bit is set when a conversion is complete and the data
registers are updated. This bit is cleared by the hardware
when executing the interrupt handler routine. Use with
ADIE.
ADIE: Interrupt Enabled. Set to one to enable the ADC
conversion interrupt. Use with ADIF.
• Finally we have ADCH and ADCL, the ADC High
• ADMUX is the Analog-Digital MUltipleXer and Low output registers. There are two
selection register. It is also used for reference registers because this is a 10 bit converter but
selection and data justification. the registers only hold eight bits each.
• The bit descriptions follow: • There are two modes of operation in
REFS1-0: REFerence voltages. 00 yields the AREF conjunction with the ADLAR bit in the ADMUX
pin, 01 yields AVCC, 11 yields internal 1.1 volt register.
reference and 10 is reserved. • If ADLAR is set then the top eight bits will be
ADLAR: Left Adjust Result. Setting this bit will placed into ADCH and the bottom two bits will
left justify the output bits in the ADCH/ADCL be placed at the top of ADCL.
registers. Setting to zero right justifies. • If ADLAR is cleared then the bottom eight bits
MUX3-0: MUltipleXer. These bits select the will be found in ADCL and the top two bits will
input pin. 000 selects ADC0, 001 selects ADC1 and be found at the bottom of ADCH. Just think of
so on to 1000 selecting ADC8 (the temperature the two registers as a single 16 bit register
sensor). 1110 selects the 1.1 volt reference and with the bits pushed to the left or right as
1111 selects ground. All other values are shown in Figure 26.3.
reserved.
analogRead()
• Description
• Reads the value from the specified analog pin. The Arduino board contains a 6 channel (8 channels on the Mini and Nano,
16 on the Mega), 10-bit analog to digital converter. This means that it will map input voltages between 0 and 5 volts into
integer values between 0 and 1023. This yields a resolution between readings of: 5 volts / 1024 units or, 0.0049 volts (4.9
mV) per unit. The input range and resolution can be changed using analogReference(). It takes about 100 microseconds
(0.0001 s) to read an analog input, so the maximum reading rate is about 10,000 times a second.
• Syntax
analogRead( pin )
• Parameters
pin: the number of the analog input pin to read from (0 to 5 on most boards, 0 to 7 on the Mini and Nano, 0 to 15 on the
Mega)
• Returns
int (0 to 1023)
• Note
• If the analog input pin is not connected to anything, the value returned by analogRead() will fluctuate based on a number of
factors (e.g. the values of the other analog inputs, how close your hand is to the board, etc.).
analogReference()
analogWrite()
• Description
Writes an analog value (PWM wave) to a pin. Can be used to light a LED at varying
brightnesses or drive a motor at various speeds. After a call to analogWrite(), the pin
will generate a steady square wave of the specified duty cycle until the next call to
analogWrite() (or a call to digitalRead() or digitalWrite() on the same pin). The
frequency of the PWM signal is approximately 490 Hz.
On most Arduino boards (those with the ATmega168 or ATmega328), this function
works on pins 3, 5, 6, 9, 10, and 11. On the Arduino Mega, it works on pins 2 through
13. Older Arduino boards with an ATmega8 only support analogWrite() on pins 9, 10,
and 11.
• The Arduino Due supports analogWrite() on pins 2 through 13, plus pins DAC0 and
DAC1. Unlike the PWM pins, DAC0 and DAC1 are Digital to Analog converters, and
act as true analog outputs.
• You do not need to call pinMode() to set the pin as an output before calling
analogWrite().
• The analogWrite function has nothing to do with the analog pins or the analogRead function.
• Syntax
analogWrite( pin, value )
• Parameters
pin: the pin to write to.
value: the duty cycle: between 0 (always off) and 255 (always on).
• Returns
nothing
• Notes and Known Issues
The PWM outputs generated on pins 5 and 6 will have higher-than-expected duty cycles. This is
because of interactions with the millis() and delay() functions, which share the same internal timer
used to generate those PWM outputs. This will be noticed mostly on low duty-cycle settings (e.g 0 -
10) and may result in a value of 0 not fully turning off the output on pins 5 and 6.
• Pins Configured as INPUT
• There are 20,000 pull-up resistors built into the Atmega chip that can
• Arduino pins are by default configured as inputs, so they do not need be accessed from software. These built-in pull-up resistors are
to be explicitly declared as inputs with pinMode() when you are using accessed by setting the pinMode() as INPUT_PULLUP. This effectively
them as inputs. Pins configured this way are said to be in a high- inverts the behavior of the INPUT mode, where HIGH means the
impedance state. Input pins make extremely small demands on the sensor is OFF and LOW means the sensor is ON. The value of this pull-
circuit that they are sampling, equivalent to a series resistor of 100 up depends on the microcontroller used. On most AVR-based boards,
megaohm in front of the pin. the value is guaranteed to be between 20kΩ and 50kΩ. On the
Arduino Due, it is between 50kΩ and 150kΩ. For the exact value,
• This means that it takes very little current to switch the input pin from consult the datasheet of the microcontroller on your board.
one state to another. This makes the pins useful for such tasks as
implementing a capacitive touch sensor or reading an LED as a • When connecting a sensor to a pin configured with INPUT_PULLUP,
photodiode. the other end should be connected to the ground. In case of a simple
switch, this causes the pin to read HIGH when the switch is open and
• Pins configured as pinMode(pin, INPUT) with nothing connected to LOW when the switch is pressed. The pull-up resistors provide enough
them, or with wires connected to them that are not connected to current to light an LED dimly connected to a pin configured as an
other circuits, report seemingly random changes in pin state, picking input. If LEDs in a project seem to be working, but very dimly, this is
up electrical noise from the environment, or capacitively coupling the likely what is going on.
state of a nearby pin.
• Same registers (internal chip memory locations) that control whether a
• Pull-up Resistors pin is HIGH or LOW control the pull-up resistors. Consequently, a pin
that is configured to have pull-up resistors turned on when the pin is
• Pull-up resistors are often useful to steer an input pin to a known state in INPUTmode, will have the pin configured as HIGH if the pin is then
if no input is present. This can be done by adding a pull-up resistor (to switched to an OUTPUT mode with pinMode(). This works in the other
+5V), or a pull-down resistor (resistor to ground) on the input. A 10K direction as well, and an output pin that is left in a HIGH state will
resistor is a good value for a pull-up or pull-down resistor. have the pull-up resistor set if switched to an input with pinMode().
• Using Built-in Pull-up Resistor with Pins Configured as Input
Example
pinMode(3,INPUT) ; // set pin to input without using built in pull up resistor
pinMode(5,INPUT_PULLUP) ; // set pin to input using built in pull up resistor
• Pins Configured as OUTPUT
• Pins configured as OUTPUT with pinMode() are said to be in a
low-impedance state. This means that they can provide a
substantial amount of current to other circuits. Atmega pins
can source (provide positive current) or sink (provide
negative current) up to 40 mA (milliamps) of current to other
devices/circuits. This is enough current to brightly light up an
LED (do not forget the series resistor), or run many sensors
but not enough current to run relays, solenoids, or motors.
• Attempting to run high current devices from the output pins,
can damage or destroy the output transistors in the pin, or
damage the entire Atmega chip. Often, this results in a
"dead" pin in the microcontroller but the remaining chips still
function adequately. For this reason, it is a good idea to
connect the OUTPUT pins to other devices through 470Ω or
1k resistors, unless maximum current drawn from the pins is
required for a particular application.
Introduction to Timer/Counter
• Virtually all microcontrollers contain one or more hardware timer/counter
function blocks.
• These can be used for a variety of functions including
generating time delays,
counting input events,
generating simple pulse or PWM waveforms, and
triggering software interrupts.
• In general, there are multiple ways to configure these blocks.
• Often, they are set up as simple up counters that are clocked by the system
clock.
• In some instances they may be run asynchronously from an external clock
source.
• The ATmega 328P contains three of these blocks: two eight bit units named
TC0 and TC2 (Timer Counter 0 and 2), and one 16 bit unit, TC1.
• A functional block
diagram of an eight
bit unit is shown in
Figure 28.1.
• The 16 bit unit is
similar but offers
extended abilities.
• Figure 28.1,
• First, there are a couple of registers used to program the operation of the block.
• These are TCCRnA and TCCRnB (Timer Counter Control Registers A and B) where n
is the timer/counter number (0, 1 or 2 here, although other microcontrollers in
the series may have more).
• These bits are usually “set and forget”, that is, they are set/cleared for a given use
and not touched again unless the unit needs to be reprogrammed for a different
use. More detail on these will be presented momentarily.
• There are also two registers that are used with software interrupts, TIFRn and
TIMSKn (Timer Interrupt Flag Register and Timer Interrupt MaSK register) that
aren’t shown here. We will examine these in the section on software interrupts.
• The other key registers are TCNTx (Timer CouNT) along with OCRnA and OCRnB
(Output Compare Registers A and B).
• In its most basic operation the unit increments the TCNTn register with each tick
of the system clock.
• Eventually, this register will reach its maximum value and overflow, resulting in
zero, effectively resetting the register and the count continues up from there.
• This maximum value is 255 for an eight bit unit (i.e., 11111111 binary) and 65535
for a 16 bit unit.
• Optionally, the unit may be programmed to inspect the values contained in the
OCRn registers and compare them to the current contents of TCNTn to see if they
match.
• Both the overflow and the compare match can be used trigger some action, e.g.,
a waveform level change or software interrupt.
• The compare match section feeds a pulse waveform generator that in turn feeds
an output pin. This section is shown in Figure 28.2.
Timer/counter compare match output circuitry
• The box marked “OCnx Pin” is a physical pin on the microcontroller.
• As it would be impractical to have single purpose pins for every
function in the microcontroller, the waveform function is mapped onto
the general purpose IO pins. Compare this section to the GPIO block
diagram discussed in chapter 19.
• Note the PORT and DDR flip-flops in particular.
• The waveform generator feeds a flip flop which then feeds a
multiplexer along with the PORT flip flop.
• This signal is then fed to a tri-state buffer controlled by the DDR which
leads to the physical output pin.
• As there are three timer/counters and each has two compare match
registers, there are a total of six physical output pins available for
waveform generation.
• The mapping assignments are shown in Figure 28.3.
• The following discussion will focus on timer/counter number two.
• This is because timer/counter units zero and one are already used for common
Arduino system calls such as delay().
• Reprogramming the timer/counters could alter the operation of those functions
and procedures.
• Timer/counter number two is a little safer to experiment with in that regard
although be aware that all three units are used to cover the six PWM capable
output pins on the Uno (i.e., via analogWrite()).
• The two control registers (TCCRnA/B) indicate the mode of operation.
• The registers are shown in Figures for unit number two. The control registers for
the other timer/counter units are similar.

TCCR2A register TCCR2B register


• The first items of interest are the CS bits.
• These are the Clock preScale bits and they allow for
longer timer periods by dividing down the system
clock before it feeds the counter.
• For example, if the controller is running at 16 MHz
then each clock tick represents 1/16 MHz or 62.5
nanoseconds.
• Counting up from 0 to 255 would yield a maximum
delay of only 62.5 nanoseconds times 256, or 16
microseconds.
• If the prescaler is set to eight, each tick is stretched
by a factor of eight yielding a maximum delay of 128
microseconds in this example.
• The prescaler settings for TC2 are shown in the Table
• Turning to the Waveform Generation
Mode (WGM) bits, there are normal
mode, a compare mode (CTC or Clear
Timer on Compare) and two forms of
pulse width modulation.
• We will examine Normal and Fast
PWM modes here and CTC mode in
the chapter covering interrupts.
• Note that the WGM bits are spread
across both control registers rather
than residing in a single register.
• The WGM bits are detailed in the
Table.
• Finally let’s consider the Compare Output Mode
(COM) bits found in TCCR2A. non-PWM, Compare Output Mode for TC2
• There are two bits for OC2A and two for OC2B, and
although the settings are similar they are not
identical.
• The settings for OC2A are found in Tables for three
waveform generation modes and describe how the
output pin signal responds. Fast PWM, Compare Output Mode for TC2
• Note that in all three of these modes if both COM
bits are clear then the output pin, OC2A, is
disconnected from the timer/counter and operates
in ordinary IO form.
• With other settings of the COM bits, OC2A is
controlled by the waveform generator.
• It is important to note that the associated DDR
must still be set for output mode in order for the
waveform to reach the output pin.

Phase Correct PWM, Compare Output Mode for TC2


Timer Programming- Normal Mode
• Normal mode (WGM22:0 are clear) is the simplest
mode of operation.
• The TC simply counts up until it overflows and
wraps around back to zero.
• On overflow, the TOV2 flag (Timer OVerflow) is set
in the TIFR2 flag register.
• More precisely, this bit will be set in the same clock
cycle as when the timer/counter register TCNT2
becomes zero.
• There are no access restrictions on TCNT2. Your
code may overwrite the contents of this register at
any time in order to alter the counting behavior.
• A short code example follows. The program uses
TC2 to create a time delay similar to the delay()
function. This delay is then used to blink an LED
connected to PORTB.0.
• /* Simple LED blinker using TC2 to create a time
delay Active high LED connected to PORTB.0 */
• The setup() function sets the direction for the LED driver pin to output, places TC2 into
normal mode and sets the clock prescaler to 1024.
• This means that each count will occur at a rate just slower than 16 kHz given the 16 MHz
clock of the Uno.
• The mydelay() function initializes the counter’s main register at a predefined value
(COUNTOFFSET).
• It will count from there to 255 after which point it will overflow and set the TOV2 flag.
• We ensure that this bit is clear before continuing using the seemingly backward
command TIFR2=1<<TOV2; It first appears that this command should set the flag bit but
it does indeed clear it (as verified in the Atmel documentation).
• At this point we busy-wait on this bit waiting for it to be set. In this example it will take
241 counts to overflow or about 15 milliseconds. This process is then iterated x times to
achieve a longer and variable delay. Note that larger values of COUNTOFFSET will yield
shorter blink times.
Timer Programming- Fast PWM Mode
• This is the “fast” pulse width modulation mode that can drive an output pin directly.
• Fast PWM is faster than phase correct PWM because fast PWM performs a single slope (i.e., up
only) count.
• Phase correct PWM uses an up-then-down dual slope counting technique. We will not examine
phase correct PWM here.
• To use fast PWM mode to drive an output pin, first remember to set the corresponding DDR bit to
output mode (see Figure 28.3). Following this, the WGM and COM bits need to be set according
to Figures 28.7 and 28.9 as well as the desired prescaler CS bits from Figure 28.6.
• Finally a value for the output compare registers (OCR2A and OCR2B) needs to be set. The value of
the OCR2x registers will determine the duty cycles of the resulting pulse waveform. Basically,
TCNT2 will count up in the normal fashion, overflowing and recycling as usual.
• When the contents of TCNT2 match those of an associated output compare register, then the
corresponding output pin will change to the appropriate state.
• The duty cycle is defined by the total count range (256) and the compare register value plus one.
An example is seen below
• An interesting new twist here is that there is
nothing in the main loop() function!
• Once the timer/counter is set up it runs
without further attention, truly “set and
forget” operation.
• In this case, the output frequency will be the
main clock of 16 MHz divided by the 64x
prescaler and then divided by the total count
of 256.
• This yields approximately 977 Hz. The duty
cycle for output A is (128+1)/256 or about
50.4%.
• For output B the result is (100+1)/256 or
approximately 39.5%.
• The preceding code was downloaded to
an Arduino Uno and an oscilloscope was
attached to the twooutput pins.
• The resulting waveforms are shown in
Figure 28.11. Note the relative accuracy
of the timing computations compared to
those measured by the oscilloscope.
• It is worthwhile to note that the values of
the output compare registers could be
changed programmatically, for example
via a potentiometer connected to an
analog input pin.
• This would effectively recreate an
alternate form of the analogWrite()
function.

Fast PWM output waveforms


Interrupts

• Interrupts may be thought of as small snippets of code that are of such high priority that
the main program flow is momentarily halted so that the interrupt code can be run.
• Once the interrupt code (referred to as an interrupt service routine, or ISR) is finished,
the main code picks up where it left off.
• Interrupts usually come from two sources: an externally triggered event such as a
physical pin changing state or via an internally triggered event.
• For example, an external switch could be used to force the program to reset itself from
the start.
• Alternately, the overflow and compare match events of internal timer/counters are often
used to create internal interrupts that can control the timing of other events.
• When an interrupt occurs, the current state and location in the executing code is saved.
• The microcontroller then jumps to the starting address of the ISR.
• The ISR code is executed and upon completion program flow reverts back to where it left
off in the main sequence.
• As the main program flow is being halted temporarily, ISRs are designed to be short and
to execute quickly so as to have minimal timing impact on the main code.
• As most microcontrollers have numerous sources for interrupts, interrupts are usually
prioritized, some being more important than others. This means it is possible for one
interrupt to interrupt another interrupt (i.e., nested interrupts having different
priorities).
• In this way, something like a fire alarm sensor could take precedence over a more
mundane sensor input such as a volume control.
• It is quite possible for a complicated application to have over a dozen possible interrupt
sources. Each of these will have its own ISR.
• The starting locations of the ISRs are found in a vector table. A vector table is basically an
array of pointers. In this case, these pointers point to the starting address of some code
rather than to the addresses of variables.
• Some programmers will use the word “vector” as a verb as in “the code flow vectors off
to the ISR”. To simplify programming, the names of the ISRs are predetermined.
• To simplify programming, the names of the ISRs are
predetermined. As a programmer you simply need to fill in
the code for the ISR. This might be as simple as setting a
global variable.
• When the program is compiled, the predetermined name is
noted in your code and then expanded into the vector table
for you automatically. Generally speaking, ISRs do not take
arguments nor do you need to create function prototypes for
them.
• A list of ISR names for the ATmega 328P series is shown in
Figure 29.1. This list is taken directly from the hardware
profile file include/avr/iom328p.h.
• Note that each ISR name ends in _vect while the first portion
of the name indicates the hardware with which it associated.
• A quick scan of the list should reveal a number of hardware
elements covered earlier such as the three timer/counters
and the analog to digital converter.
• Finally, before these interrupts can be used the desired
interrupt must be enabled by setting the appropriate bit in
the associated register (EIMSK, External Interrupt MaSK;
TIMSKx, Timer Interrupt MaSKx; etc.,
• Of course, the global interrupt enable must also be set
(which can be accomplished via the sei() call) although this is
already the case in the Arduino system.
Interrupts Programming

• In this section we shall examine a series of examples using interrupts


trigger by the internal timer/counters.
• This technique makes use of either the overflow or compare match events.
It is usually used when you wish to have events occur at predictable times
but without the penalty and limitations of using a simple delay() style
approach.
• That is, the triggers for these events will not need your attention in the
main program loop but will generally be of a “set and forget” variety.
• There is a Timer Interrupt MaSK register for each of the counters (TIMSK0,
TIMSK1 and TIMSK2).
• The appropriate bit(s) must be set to enable the desired interrupt.
Internal Interrupts: Blinking LED
• The first example shows a simple method of
blinking an LED “hands off”.
• The duty cycle of the blink will always be
50% although it is possible to alter this.
• The example also shows how to generate
long time intervals from an eight bit
timer/counter through the use of a global
variable.
• The example is shown below.
• Timer/counter 2 is used here in normal mode
meaning that it will count from 0 to 255 and
then overflow and repeat.
• The prescaler is set for 1024x, producing
ticks at about 16 kHz.
• The overflow interrupt for this counter is then enabled. This triggers the
TIMER2_OVF_vect.
• A full count from 0 to 255 will create an overflow at a rate of slightly over 60 Hz.
• This means that the LED will have a single on/off cycle at a rate of about 30 per
second which is a little too fast for the naked eye.
• Therefore we can’t simply toggle the LED as it will always appear on.
• Instead, we make use of a global variable, g_time, that will keep track of how
many times the interrupt has occurred.
• Once it hits our predetermined maximum, ISR_COUNT, it will be reset and the
LED toggled.
• We now have a blinking LED that is completely “hands off” as far as the main
code flow is concerned. Again, note that loop() is empty.
Internal Interrupts: Hand Wrought PWM
• Our next example shows how a
timer/counter overflow can be used to
create a PWM signal at an arbitrary pin
(i.e., not just at the OCnx pins).
• For this example we shall forego the use
of an LED indicator and simply inspect
the signal with an oscilloscope.
• The output frequency is slightly over 60
Hz and the duty cycle is set by a #define
although it could just as easily be set by
a variable.
• The duty cycle is
(OVF_COUNT_START+1)/256.
• Thus, a value of 128 yields
approximately 50% while a value of 50
yields nearly 20%. The output pin is set
to Uno pin 8 or PORTB.0.
• The code follows:
• The code is very similar to that of the prior example with the addition
of the alteration of the TCNT2 count register.
• Each interrupt causes the pin to toggle, however, unlike the previous
example the count register is set to a new starting value which
shortens the count time (i.e., the time to overflow).
• Further, the reset value is dependent on whether the current output
level is high or low.
Internal Interrupts: CTC Mode
• This example also involves toggling an
arbitrary output pin although the OCnx
pin could be piggybacked if desired.
• Instead of PWM, this example varies
the frequency of a square wave using
Clear Timer on Compare (CTC) mode.
• This mode simply counts up to the
value stored in the output compare
match register (OCR2A) and then resets
back to zero.
• Note that a new bit must be set in the
timer/counter interrupt mask register,
namely OCIE2A (Output Compare
Interrupt Enable for unit 2A).
• The Output Compare Register (OCR2A) is set with a fixed value but
again, this could be set by a variable.
• A COMPARE_MATCH value of 128 yields a square wave frequency of
slightly more than 60 Hz.
• Halving the value to 64 will roughly double the frequency to about
120 Hz.
External Interrupts
• In this case, the state change seen at
an external pin causes an interrupt.
• We will use External Interrupt
Request 0, also known as the INT0
vector.
• This interrupt examines state
changes at PORTD.2, Uno pin 2.
• Our example will be fairly simple: we
attach a passive switch using the
internal pull-up to pin 2.
• When the switch is activated it will
toggle an LED connected to Uno pin
8 (PORTB.0). The code follows:
• After setting the IO pins, the code sets the INT0 bit in the EIMSK register to
enable the interrupt.
• The External Interrupt Control Register (EICRA) is programmed to trigger on
a falling (negative going) edge.
• When the switch is activated, the falling edge triggers the ISR to run. The
ISR simply toggles the bit that the LED is tied to.
• Note that there is no port setting and pin examination in the main code.
• In fact, loop() is empty and does nothing.
• Among other things, an external interrupt such as this is very useful for
high importance items such as a panic switch.
References
• Embedded Controllers Using C and Arduino / 2E - James M. Fiore
• https://www.tutorialspoint.com/arduino/arduino_io_functions.htm

You might also like