Você está na página 1de 144

Introduction to Microcontrollers

posted by Michael Silva Experienced embedded and PC software designer with good hardware background. Interested in helping embedded newcomers to get started. Welcome to this Introduction to Microcontroller Programming tutorial series. If ou are looking to learn the basics of embedded programming for microcontrollers !and a bit of embedded hardware design as well"# I hope these tutorials will help ou along that $ourne . %hese are m first postings here# and I am writing this tutorial series because over the ears I have seen countless newbies asking the same &uestions and tripping over the same stumbling blocks# and I thought I might be able to come up with something useful in answering those &uestions# and in avoiding those tripping points.

Target Audience
%his tutorial series is intended for students# hobb ists# programmers and hardware designers who want to learn the basics of microcontroller programming# or who want to fill in some gaps in their knowledge of such programming. %his tutorial will not teach ou programming in general# although it will discuss programming techni&ues of particular interest for microcontrollers. %his tutorial will also not teach ou hardware design# although it will illustrate hardware issues commonl faced in emplo ing microcontrollers. What it will do# I hope# is to help a newcomer understand what a microcontroller !'C" is# what capabilities it will t picall have# and how to use those capabilities. It will go from the beginnings ( how to wire up and program a 'C to blink and )E* !the microcontroller version of a +,ello Worldprogram"# on through the various features and peripherals t picall found on a 'C such as interrupts# timers.counters# /01%# SPI# I2C# 0*C# *0C# PWM# watchdog# and so forth. It will also examine common topics such as debouncing inputs# filtering 0*C values# driving character )C*s# and other similar chores.

Caveat Lector
Ever rule has one man exceptions. %his applies to $ust about ever thing ou will read in this tutorial. If ou read 343 here# don5t think it means 34 and onl 4# in ever possible situation# with no exceptions or &ualifications# now and forever.3 Microcontroller designers have come up with man different# interesting and sometimes $ust wierd wa s of doing things. 0nd as an microcontroller user and programmer ou too can come up with man different and interesting wa s of doing things too. In m experience# given an 67step program# there are probabl at least 67s&uared wa s of writing that program. %he goal of this tutorial is to tr and give ou a solid foundation for 'C programming# not to be a comprehensive enc clopedia of the field. 8or ever example program# I will tr and write it in a simple and understandable fashion and let ou discover our own clever tricks further down the road.

0nother possible source of confusion is in terminolog . *ifferent manufacturers &uite often use different terminolog for the same or similar features# registers and configuration.status options. In this tutorial sometimes I will adopt the terminolog used b one of the 'C families used in the tutorial# and other times I will use non7specific terminolog . I will use whatever seems to be suitable for each situation.

What is Embedded Programming?


Embedded programming is the term for the computer programming that lives in and operates the great man computer7controlled devices that surround us in our homes# cars# workplaces and communities. %o be clear# all microcontroller programming is embedded programming# but not all embedded programming is microcontroller programming. 0 little more will be said about this further along. Sometimes the terms will be used interchangeabl # but the focus of this tutorial series is alwa s on microcontrollers. 8or ever desktop or notebook or tablet computer ou have# ou ma have a do:en or more !perhaps a great deal more" microcontrollers &uietl doing their embedded dut # and with these devices man people don;t even reali:e the involve a tin computer running a program. <ut there is# and it is# and those programs had to be written# and that;s wh the world needs embedded programming. Microcontrollers add intelligence to countless devices and s stems# enabling those devices and s stems to operate better# faster# more safel # more efficientl # more convenientl # more usefull # and in man cases allowing the ver existence of devices and s stems that could not be built otherwise. Spend some time looking around ou and tr ing to recogni:e where 'Cs are working# and ou will begin to get a sense of how ubi&uitous the have become since their invention some =>? ears ago. @n top of all that# man people# m self included# find 'C programming a particularl fascinating and rewarding branch of the programming tree# and we $ust like to program embedded s stems.In wa s ver different from most desktop or mainframe programming# embedded programs make stuff do stuff# and to an embedded programmer# stuff doing stuff is endlessl cool.

What is an Embedded System?


%here;s no perfect answer to that &uestion# since ever answer will have some exceptions. ,owever# for our purposes let us declare that an embedded s stem is one that uses one or more microcomputers !that is# small to ver # ver small computers"# running custom dedicated programs and connected to speciali:ed hardware# to perform a dedicated set of functions. %his can be contrasted with a general7purpose computer such as the familiar desktop or notebook# which are not designed to run onl one dedicated program with one speciali:ed set of hardware. It;s not a perfect definition# but it;s a start. Some examples of embedded s stems areA B 0larm . securit s stem

B 0utomobile cruise control B ,eating . air conditioning thermostat B Microwave oven B 0nti7skid braking controller B %raffic light controller B Cending machine B Das pump B ,andheld Sudoku game B Irrigation s stem controller B Singing wall fish !or this gift season;s e&uivalent" B Multicopter B @scilloscope B Mars 1over

8or the most part I have listed example embedded applications on the less7complex end of the spectrum# since this is after all a beginning tutorial. < the end of this tutorial series ou should have a good general idea how most of these applications would be programmed# and in rough terms what kinds of I.@# timing# interrupt and communications hardware and functionalit the would re&uire. %here are a few things worth noticing about the above list. While man embedded s stems use fairl traditional user input7output devices !ke pads# displa s"# man others do not. 0lso# man embedded s stems interact directl with human beings# but others do not !and we;re still waiting to see if the Mars 1over will interact directl with an Martians".

What is different about Embedded Programming?


Embedded programs must work closel with the speciali:ed components and custom circuitr that makes up the hardware. /nlike programming on top of a full7function operating s stem# where the hardware details are removed as much as possible from the programmer;s notice and control# most embedded programming acts directl with and on the hardware. %his includes not onl the hardware of the CP/# but also the hardware which makes up all the peripherals !both on7chip and off7chip" of the s stem. %hus an embedded programmer must have a good knowledge of hardware# at least as it pertains to writing software that correctl E

interfaces with and manipulates that hardware. %his knowledge will often extend to specif ing ke components of the hardware !microcontroller# memor devices# I.@ devices# etc"# and in smaller organi:ations will sometimes go as far as designing and la ing out !as a printed circuit board" the hardware. 0n embedded programmer will also need to have a good understanding of debugging e&uipment such as multimeters# oscilloscopes# logic anal sers and the like. 0nother difference from general purpose computers is that most !but not all" embedded s stems are &uite limited as compared to the former. %he microcomputers used in embedded s stems ma have program memor si:es of a few thousand to a few hundred thousand b tes rather than the gigab tes in the desktop machine# and will t picall have even less data !10M" memor than program memor . 8urther# the CP/ will often be smaller F and 9G bit devices as opposed to the E2 bit and larger devices found in a desktop !although small E27bit microcontrollers are now under a dollar in moderate &uantities# which is ama:ingl ama:ing". 0 smaller CP/ word si:e means# among other things# that a program will re&uire more instructions !and thus more clock c cles" than an e&uivalent program running on a CP/ with a larger word si:e. 0nd finall # the speed at which smaller microcontrollers run is much less than the speed at which a PC runs. % pical smaller microcontroller clock rates are between 9 and 2>> M,:# not the D,: rates of PCs.

What are the differences between microcomputer, microprocessor and microcontro er?
0 microprocessor is usuall understood to be a single7chip central processing unit !CP/"# with the CP/ being the 3brains3 of a computer 7 the part of the computer that executes program instructions. 0 microcomputer is an computer built around a microprocessor# along with program and data memor # and I.@ devices and other peripherals as needed. 0 microcontroller !often shortened to 'C in this tutorial" is a single chip device which has built onto the chip not onl a microprocessor but also on the same chip# nonvolatile program !1@M" and volatile data !10M" memor # along with useful peripherals such as general7 purpose I.@ !DPI@"# timers and serial communications channels. %hus it follows that all microcontrollers are microcomputers# but not all microcomputers use microcontrollers. In smaller embedded s stems it is most common to use microcontrollers rather than microprocessor7based designs since microcontrollers give the most compact design and the lowest hardware cost. )arger embedded s stems# on the other hand# ma use one or more microprocessors if a microcontroller of suitable speed and functionalit cannot be found. %his can extend to the use of industrial PCs and even more powerful hardware. It is also possible to include both microprocessors and microcontrollers in a complex embedded s stem. %he onl real rules are# use whatever device!s" fit the task# given the constraints on budget# availabilit # time# tools# etc. It should also be pointed out that with most microcontrollers it is possible to add external memor and peripherals# should the on7board mix not take care of all the s stem needs. When it makes sense to add such external devices# as opposed to choosing a larger microcontroller with the needed resources on7board# is a choice that needs to be made on an individual design basis.

What is an !"bit CP#$microprocessor$microcontro er?


%here is some discussion about what it means to call a device an 67bit processor# but it5s fairl obvious in most cases. If the device can perform most of its data manipulation instructions on data words up to 6 bits in si:e# the device is an 67bit processor. < wa of example# a device ma have a full set of instructions that can operate on F bit data# along with a few instructions that operate on 9G bit data. %hat device should be considered an F7bit design# even if the marketing department sa s otherwise and calls it a 9G7bit chip. < volume# F7bit microcontrollers are the biggest segment of the embedded market. Man applications simpl don;t need an more power# and never will. 9G7bit devices are more powerful# but the are s&uee:ed between the F7bit devices on the low end and the E27bit devices on the high end. E27bit devices are at the high end of the embedded spectrum for all but the most complex or high7performance designs# but the are moving ever downward in price.

What microcontro er fami ies are used in these tutoria s?


%o give a bit of an overview of the different flavors of microcontrollers available# this tutorial will be written around one F7bit famil !the 0tmel 0C1" and one E27bit famil !the 01M Cortex ME architecture in the form of the S%ME2 famil ". %hese two families were chosen to give a fairl broad picture of the devices and approaches found in the world of microcontrollers. %he first few software examples will be written in assembl language for each of these families# as well as in C. 0fter that# examples will onl be written in C.

What e se is re%uired for these tutoria s?


While ou could# I suppose# work through much of this tutorial using $ust a microcontroller simulator# I strongl recommend that ou have either a microcontroller training.development board# or even $ust a bare 'C chip# assorted components and a powered breadboard. In addition ou will need a C compiler that targets our device# and optionall an assembler for our device. Hou should have no trouble finding a free assembler for our chip# and ou should also be able to find a free C compiler# even if it is a reduced7functionalit version of a commercial compiler. Hou will also need a method of downloading our programs into our 'C. %he details of this download process will depend intimatel on the particular 'C and board it is mounted on. 0s far as test e&uipment goes# digital multimeters are reall cheap# and there5s no excuse not to have one. Places like ,arbor 8reight sometimes have them on sale for a few dollars. %he other piece of e&uipment that an embedded engineer must have is a decent oscilloscope. *on5t panic# a scope is not re&uired for these tutorials. ,owever# if ou can get ahold of one# ou will learn more and save ourself a fair amount of time in the bargain. /S< scopes give good bang for the buck# as do some import scopes !or# of course# a working used scope". 0t

the end of last ear I treated m self to a beautiful 0gilent scope with a huge !to me" screen# and ever time I use it I5m glad I spent the mone . 1egarding the microcontrollers used in these tutorials# here are the details of the hardware and I will be using for each of the processor familiesA

AVR
B,ardwareA 0tmel S%J7I>> board with 0%megaFI9I installed B%oolsA 0tmel Studio G !free"

ARM Cortex M3
B,ardwareA S%ME2C)*iscover <oard# mounted on a custom docking board B%oolsA 1owle Crossworks !K9I> for personal license 7 suggest I01 Embedded Workbench Jickstart Edition for free toolset"

Which programming anguage?


%his is a good time to talk a bit about the various programming languages that one can use to write embedded software. %he two languages I will use in this tutorial are C and assembl language. %he first thing I want to point out is that these are not the onl two languages available to embedded programmers# and that in man cases other languages ma be a better choice. %hat being said# both C and assembl language are useful not onl for learning about 'C programming# but also for actuall doing productive 'C programming. %he are also ubi&uitous in that no matter what microcontroller ou choose# it will almost certainl have available both an assembler !for processing assembl language source code" and a C compiler !for processing C source code". %he same is definitel not the case for other languages. <ut I would encourage ou to consider other languages if ou are so inclined and# big I8# if the are available for our device famil . @n the sub$ect of assembl language# even if ou don;t plan on using assembl language in our embedded programming# I would strongl suggest that ou become at least somewhat familiar with the concepts# and with the instruction set of our 'C. %he reason for this is that# even if ou don;t end up writing an assembl language !I hardl ever do an more"# ou will find ourself at some point needing to examine the output of our compiler and.or our compiler7supplied startup files written or output in assembl language. 0lso note that the term 3assembl language3 will often be shortened# in this tutorial and elsewhere# to 3asm3 or 30SM.3

&ow does an embedded program run?


<efore talking much more about embedded programming# this is a good place to give a brief overview of how an embedded program starts up and runs. 0ssuming that ou have generated a program file and have loaded it into the 'C program memor !all steps that we will talk about in more detail later"# the good stuff happens when ou either turn on the device or ou push the 1ESE% button. When the 'C comes out of reset from either action it will alwa s go to a particular memor location# as defined b the manufacturer# to begin executing whatever code is found there# or pointed to there. Sometimes this memor location contains code directl L e.g. upon coming out of reset# program execution begins at program address >. @ther times the fixed memor location is a vector# a location that holds the actual address of the beginning of the programL e.g. upon coming out of reset# the controller will load its program counter with the value found at program address >x888E and thus start executing code at the address found in locations >x888E and >x8888 !assuming a 9G7bit program address# stored in 2 b tes". In the first instance ou will have to make sure that our program has loaded at the specified startup address# while in the second instance ou will load our program wherever the program memor has been placed in the controller address space# and ou will have to make sure that ou then load that startup address into the reset address vector. 6ote that the choice of startup method is not up to ou# but will be built in to the design of the 'C ou have chosen. 0C1 uses the first method# and Cortex ME uses the second. When an embedded program starts to run# there is usuall a fair amount of initiali:ation and housekeeping that must be done before the meat of the program begins. Most of this initiali:ation is something that the average desktop programmer never sees# since it is handled b the computer boot code and operating s stem. <ut in an embedded s stem# it is $ust as likel as not that there is no operating s stem# and all boot code and other startup code must be explicitl provided. Some ver critical hardware ma need to be initiali:ed first e.g. hardware that controls memor access times and address maps# as well as s stem clock hardware. %hen some software initiali:ation ma need to happen# such as setting up a stack pointer and perhaps cop ing data from nonvolatile memor to volatile memor where it can be accessed and perhaps modified. 0fter that will usuall come another round of hardware initiali:ation# setting up an peripheral devices that the s stem re&uires# and setting initial output states. 8inall # et another round of software initiali:ation ma occur. %his initiali:ation is usuall broken up into two sections# with the first hardware and software initiali:ation steps often being done in what is known as the startup code# and the later hardware and software steps being done in the user program. %his delineation is more distinct in a C program# where the startup code is invisible to the C program# being the code that happens before main!" is run# and ending in a $ump or call to main!" where the visible C program begins. In an assembler program all the initiali:ation steps ma be e&uall visible in the user code# although even then the first steps ma reside in a separate startup source file.

A !ote on the E'amp e Programs


Each tutorial section will include a number of short example programs. %he examples will start with the simplest concepts and add some concepts in each succeeding program. 0long the wa # some comments will be trimmed to tr and help keep the visual clutter down and keep the focus on the newer concepts being presented. 0s an example# comments to the effect M

that 3this bit.port.address needs to be ad$usted for our particular hardware3 will eventuall disappear# because b then ou should know that e.g. if I am discussing an )E* output on P@1%0 bit > and on our hardware ou are using an )E* on P@1%< bit M then ou5ll make that change accordingl . @r when I mention in the first programs that after a 3ret3 instruction that ou5d better have set up the stack first# after a while that comment and others like it will disappear.

What ne't?
<efore we can proceed to our first microcontroller program# our )E* blink 3,ello World#3 there are more details we need to cover concerning the design and operation of microcontrollers. %hat will be the sub$ect of the next tutorial in this series.

(asic CP# )rgani*ation


When ou write a program for our microcontroller ou are reall writing a program that is executed b the 'C CP/ !central processing unit"# executing various instructions designed into the CP/ instruction set# so it5s worth a bit of time to take a brief look at the innards of a t pical 'C CP/. If the program is written in 0SM# the programmer writes the program using these CP/ instructions directl . If the program is written in a high7level language# the compiler translates the ,)) lines into corresponding se&uences of CP/ instructions. What follows is not in an sense meant to be a comprehensive discussion of CP/ architecture design. 1ather# it is $ust enough detail !I hopeN" to make the sections that follow understandable. %here5s lots of additional detail on the net and in datasheets. In the simplest sense# a CP/ is that part of the microcontroller that executes instructions. It does this in a series of stepsA B 8etch an instruction from the 3next instruction3 memor location pointer B Execute that instruction B 0dvance the 3next instruction3 pointer accordingl Ever computer program is $ust a repetitive execution of this se&uence.

CP# +egisters
0n CP/ will have a set of onboard registers# which can be viewed as ver fast memor locations. %hese registers will either be addressed automaticall !Instruction I alwa s operates on register R"# or the will be addressed b a few bits in the instruction operation code !op code". 0 t pical CP/ will have the following t pes of registersA

Data Registers
%hese registers act as sources and destinations for arithmetic# logic and shifting instructions. Sometimes these registers are called accumulators. If the data register can also be used for general addressing functions# it becomes a 3general7purpose3 register# as described below.

Addressing Registers
%hese registers are used to hold addresses for memor data accessing. *epending on the CP/ design# these registers ma be full7fledged general purpose registers !the same register can be used for data manipulation or data addressing# or these registers ma be limited and designed onl for holding addresses used for data accesses.

Stack Pointer
%he stack pointer is an address register which points to a section of memor that is used for the CP/ hardware stack. %he hardware stack is the stack that is used b the hardware for subroutine calls and returns# and for interrupt calls and returns. It is also possible for the user program to use the same stack# pointed to b the stack pointer# for saving and restoring other data. @r# the user program ma use another stack# pointed to b another address register. %he difference between such software stacks and the hardware stack is that onl the hardware stack is invoked b the CP/ instructions for subroutine calls and returns# b the interrupt mechanism# and in man cases b instructions for pushing and popping stack data. 6ot all CP/ designs use a hardware stack pointer. Some $ust reserve a single address register# often called a link register# which serves as a one7deep stack on the CP/ itself# thus resulting in ver &uick subroutine calls and returns. If a deeper stack is needed !almost alwa s the case at some point in a program"# user or @S code must cop memor addresses between the link register and a software7maintained stack.

Program Counter (also kno n as Instruction Pointer!


%his is the address register that points to the current !or next" instruction to be executed. It ma be accessed b special instructions# or it ma be a standard address regiser or even a full general7purpose register. It will automaticall advance to point to the next instruction in a program# and will automaticall be ad$usted based on program $ump# call and return instructions.

CP# ,nstruction types


0 microcontroller CP/ will execute a range of instructions# some of which manipulate data# some of which affect program control or CP/ behavior# and some of which perform other actions. ,ere is a brief overview of the main t pes of CP/ instructions. %here will of course be variations in the instruction details# how the source operands are accessed# and where the result ends up. %he following list is prett minimal 7 different CP/ architectures ma execute a much richer set of instructions# but will alwa s execute variants of these basics.

-ata .anipu ation ,nstructions

Arit"emetic Instructions
0rithmetic instructions allow the CP/ to add and subtract numbers# and in man cases to multipl and divide numbers. %hese numbers ma be constants# fixed for all time as the program runs# or the ma be variable# modifiable values held in data memor or registers. Signed numbers are $ust about universall represented in 2s complement form. # ADD 0P<?C # S$%&RAC& 0P<7C # C'MPAR( <7C %he instruction to compare two values is actuall a subtraction instruction# but one in which onl the flags are set# with no values being altered. # )(*A&( 0P 7< %akes a signed number and converts it to the negative of that number. E&uivalent to 0P>7<. It is important to understand that# in the great ma$orit of cases# our CP/ will onl have instructions for such arithmetic operations up to its natural word si:e. So an F7bit 0C1 will have an instruction to add 9M?=E !both values fit into F bits# as does the result"# but not 9M>#>>>#>>>?=E#>>>#>>>. %he 0C1 is perfectl capable of performing the latter addition# but it $ust re&uires a se&uence of instructions# each working on individual F7bit pieces of the numbers. If writing in 0SM the programmer will have to explicitl write this se&uence of instructions. If writing in C or another language# the compiler will automaticall generate the instruction se&uence !which ou can see b looking at the 0SM listing output of the compiled program". It is also common for CP/s to have increment and decrement instructions# which are shorthand instructions for adding or subtracting 9 !or# in some cases# a small number up to ?.7 M or ?.7 9I". Since these operations are so common in programs# it is worthwhile for a CP/ to support efficient instructions for them. # I)C and D(C 0 P 0?9 9>

0 P 079

+ogic Instructions
)ike the arithmetic instructions# these instructions will t picall work on data that is the natural word si:e of the CP/# or smaller# e.g. F bits on an 0C1# and E2# 9G or F bits on an 01M Cortex ME. %o perform logic operations on larger data re&uires a se&uence of instructions $ust as is re&uired for arithmetic operations. 6ote that all the following examples show data in binar format !a 9 or > in each bit position"# not decimal formatN # C'MP+(M()& ()'&! 4 P Q0 !sets ever 9 bit in 0 to ># and ever > bit in 0 to 9" 0P>>99>99> 4P99>>9>>9 # A)D 4P0R< produces the bitwise 06* of 0 and <# e.g. 0P>>99>99> <P>99>99>> 4P>>9>>9>> A 9 in ever bit position where both 0 and < are 9# > elsewhere # 'R 4P0S< produces the bitwise @1 of 0 and <# e.g. 0P>>99>99> <P>99>99>> 4P>999999> A 9 in ever bit position where either 0 and < are 9# > elsewhere # (,C+$SIV( 'R 4P0T<

99

produces the bitwise E4C)/SICE @1 of 0 and <# e.g. 0P>>99>99> <P>99>99>> 4P>9>99>9>A > in ever bit position where 0 and < are the same# 9 where the are different

S"i-ting Instructions
# S.I/& +(/&0RI*.& Shifts each bit one position to the right or left. 8or left shifts the 3empt 3 bit position !*># least significant bit# )S<" will be loaded with >. 8or right shifts the empt bit position !most siginifcant bit# MS<" will usuall keep the value it had !sign extension" but there ma be versions of the instruction that shift a > into the MS<. 8or both directions# the bit that is shifted out is usuall shifted into the Carr bit# which provides the linkage for multi7stage shifts. # R'&A&( +(/&0RI*.& %he same as shift# except that the bit that is shifted out is shifted back into the empt bit position. It is also shifted into the Carr bit at the same time. # S.I/& +(/&0RI*.& &.R'$*. CARR1 %I& %he same as shift# except that the Carr bit is shifted into the empt bit position. # R'&A&( +(/&0RI*.& &.R'$*. CARR1 %I& %he same as rotate# except that the Carr bit is included in the rotate# so the Carr bit value is rotated into the empt bit# and the bit shifted out is shifted into the Carr bit !but not into the empt bit position".

Program /lo Instructions2 $nconditional 3umps


# 3$MP PC !program counter" P $ump destination address. Execution continues at this new address.

Program /lo Instructions2 Conditional %ranc"ing


# %RA)C. (4$A+ 0 )'& (4$A+ (&' 5(R'!

92

# %RA)C. CARR1 0 )' CARR1 # %RA)C. P'SI&IV( 0 )(*A&IV( if the condition is true# PC P branch destination address# otherwise PC P next instruction address

Program /lo Instructions2 Subroutine Calls and Returns


# CA++ Save return address PC P call destination address # R(&$R) PC P saved return address Subroutines are one of the most basic forms of code reuse. If ou have a piece of code that needs to be run at different times and.or with different data# a subroutine lets ou isolate that code and run it !3call it3" from an other section of code# at an time. In fact# a subroutine can even call itself# but that5s getting into recursion# which is not something we5ll be discussing. %he essence of a subroutine call instruction is that it is a $ump !$umping to the subroutine code"# but a $ump that remembers where it came from. < remembering the address that it came from# at the end of the subroutine it can $ump back to where it came from so the calling code can continue. %o be more precise# a subroutine call instruction is a $ump instruction which saves the address of the instruction following the subroutine call instruction# not the address of the subroutine call instruction itself. It is the next instruction that needs to be executed after the subroutine returns. If the subroutine returned to the subroutine call instruction# the program would find itself in an endless loop# calling the subroutine without end. %he call instruction needs to have a known location to save the PC value so the subroutine can return. 6ormall this known location can be one of two places. %he PC value can be saved in memor on a stack# which is pointed to b a CP/ register# or it can be saved directl in a register !t picall called the 3link3 register". %he advantage of the stack is that subroutines can call other subroutines# and the stack will $ust continue to grow to hold all the return addresses. %he advantage of the link register is that storing and retrieving data from registers is faster than storing and retrieving from memor . %he disadvantage of the link register# if ou want to call it that# is that if a subroutine wants to call another subroutine it needs to explicitl save the link register contents on a stack. 0 subroutine that calls other subroutines is often called a branch subroutine# which a subroutine that does not call an other subroutines is called a leaf subroutine. Calls to and returns from leaf subroutines can be faster with a link register# at the expense of re&uiring branch subroutines to manuall save and restore the link register. In an case# either method performs the necessar function of

9E

saving the PC value in a known location so that it can be retrieved and loaded back into the PC at the end of the subroutine. %o return from a subroutine# as noted# the PC must be loaded with the address that was saved when the subroutine was called. 0nd as mentioned# this address could be in memor on a stack# or it could be in alink register. Man CP/s have a special instruction called a 3return3 instruction that fetches the address and sticks it into the PC. Some CP/s $ust use a standard move instruction to move the saved address into the PC. <ut the result is the same in an case 7 the program continues to execute where it left off.

What !e't?
0fter all this background it5s finall time to think about writing our first embedded program# one that blinks an )E*. In deference to tradition I am calling this first program +Embedded ,ello World-. %hat will be the sub$ect of the next tutorial in this series. In the meantime ou might want to look over the datasheet or user manual for our microcontroller to get a better understanding of the particular instruction set it executes.

Embedded &e o Wor d


0 standard first program on an embedded platform is the blinking )E*. Detting an )E* to blink demonstrates that ou have our toolchain set up correctl # that ou are able to download our program code into the 'C# and that the 'C and associated circuitr !e.g. the power suppl " is all working. It can even give ou good evidence as to the clock rate that our microcontroller is running !something that trips up a great man people# believe it or not". 0 program to blink an )E* must do the following thingsA B Set a DPI@ !general purpose input.output" pin to be an output B *rive this output pin !connected to an )E*" alternatel high !9" and low !>" B *ela a human7discernable length of time between each output pin change

)ur Target &ardware


0s mentioned in an earlier tutorial chapter# this tutorial will focus on two microcontroller familiesA the 0C1# using !at least in the beginning" the 0%megaFI9I which comes included with the S%J7I>># and the 01M Cortex ME# using the S%ME289>>1< device found on the S%ME2C)*iscover board. 8or conciseness# in the future an 0C1 part will $ust be referred to as the 0C1# and the S%ME289>>1< will be referred to as the S%ME2. ,ere are links to the relevant datasheets and reference manualsA 0%megaFI9I *atasheet 9=

S%ME289>>1< *atasheet S%ME289 8amil 1eference Manual

Configuring /P,) pins


DPI@ pins are normall configured on reset as inputs# but the can be reconfigured under program control. DPI@ pin options can var from a few simple choices !input or output" to a fairl complex set of choices. %he 0C1 famil is on the simple side of the spectrum. 0n 0C1 DPI@ pin can be configured as an input or as an output. 0dditionall # if a pin is configured as an input# it can further be configured to enable a weak internal pullup resistor on the input. So the three choices are ouput# input# or input with internal pullup. 8or the S%ME2# there are man more options# including both internal pullups and internal pulldowns# as well as various output configurations# and speed ratings to offer tradeoffs between output switching speed and EMI !electromagnetic interference". In addition# before using an S%ME2 DPI@ port# that port5s clock must be enabled. It is worth noting here that the 0C1 ports are F bits wide# while the S%ME2 ports are 92 bits wide.

(it .anipu ation


Embedded programs will t picall do a fair amount of bit manipulation# both input and output. 0ll kinds of switches# sensors# drivers# actuators# and other input and output devices are represented in the program as individual bits or collections of bits. 6ot onl that# but a 'C will have man configuration and status registers which will call for the setting or clearing of individual configuration bits# and the reading of individual status bits. In our first example programs we will see )E*s and switches represented as individual bits# and later man other input and output devices will be added to that list. %hus it is important to understand how to manipulate bits# that is# to read individual bit states and to set or clear individual bit states. What makes bit manipulation non7trivial is that bits do not usuall exist alone# but exist within b tes !and 9G7bit words and E27bit words". %hus it becomes necessar # when writing individual bits# to avoid changing other bits within the same b te.word# and when reading individual bits# to avoid reading !or ignore" other bits within the same b te.word. %he CP/ logic instructions# as discussed in an earlier section# are what allow us to do these things. It must be noted that some 'C families also include instructions that directl act on individual bits# at least individual bits in certain registers or address ranges. %hese instructions are commonl used for reading and writing bits in configuration and status registers# and for maintaining bit flags in memor . %hese instructions# while nice to have# are not a necessit # and the standard logic instructions can alwa s do the same $ob !with one or two caveats that we will discuss in the future". What5s more# standard C and other high7level languages will 9I

not have language constructs designed to access individual bits# even if the 'C does have individual bit instructions. If ou are writing in a high7level language# it will be up to our compiler as to whether it recogni:es individual bit manipulations and uses speciali:ed instructions in those cases. It should also be pointed out that while C is good at manipulating bits# it it not as good as assembl language. %hat is to sa # there are some bit manipulations that can onl be done awkwardl # if at all# in C# that can be done easil in 0SM. Which manipulations fall into this categor will depend on the particular instruction set of the 'C in &uestion. 0 couple of general examples are# shifting or rotating data through arbitrar numbers of b tes# and using the value of the carr flag# sign flag or other condition flags directl as data bits. 0s I said# there ma be others as well# depending on the underl ing instruction set.

-ata Si*es
<efore we start talking about time dela s# this is probabl as good a place as an to examine and emphasi:e the range of values that can be represented b data variables of different b te si:es. %hese are numbers that 'C programmers will deal with throughout their careers# so get to know them wellA
1 byte (8 bits): 2 bytes (16 bits): signed 4 bytes (32 bits): 2,147,483,677 signed 0 to 255 unsigned 0 to 65,535 unsigned 0 to 4,294,967,295 unsigned -128 to 127 signed -32,768 to 32,767 -2,147,483,678 to

!for E2 bits I $ust think > to = billion unsigned# 72 billion to ?2 billion signed"

Simp e Time -e ays


While there are man wa s to execute time dela s and generate time intervals# the simplest !but not the best" is a software dela . %his $ust involves sitting in a do7nothing loop for the desired length of time# and then exiting the loop. Since microcontrollers are so fast# a one7 second dela ma loop hundreds of thousands or millions of times. %hese are hundreds of thousands or millions of CP/ clock c cles that are being wasted# not available for doing an other useful work. 8or this reason# software dela s are not generall a good idea# even though the are eas to understand for beginners. 8or a suitable )E* blink rate# a useful rule of thumb for estimating how man times a software dela should loop is to divide the 'C clock rate b 9>>. /se this count for both the )E* @6 and @88 times# so the total )E* dela for one blink c cle is twice this value. %hus an 0C1 running at 9M,: would dela loop 9>#>>> times while @6# then 9>#>>> times while @88# and the S%ME2 running at FM,: would dela loop F>#>>> times @6 and the same number @88. %his is $ust a rule of thumb# of course# and the numbers can be increased or decreased# but it5s a reall eas number to calculate# and it generall gives a number that results in a good blink rate. 0 good blink rate is simpl one where the total blink period is not so small that the )E* blinks too fast to notice it is blinking !sa 2> or more times per 9G

second"# nor so large that the )E* takes man seconds to blink# forcing ou to sit and stare at it to see if our program is working at all.

At Last, ,t ( in0s1 2A3+ version4


It5s taken a while to la the groundwork# but we5re finall read to blink our danged )E*. We will write our )E* blink program in C# and follow up with 0SM versions in a future tutorial chapter. 8or the 0C1# the C )E* blink program is dead simple# largel because the total 0C1 initiali:ation is one line of code. 8or the S%M the actual blink code is identical# but the configuration is a bit more complex. <oth the S%J7I>> and the S%ME2C)*iscover boards have built7in )E*s# but if ou5re using some other hardware that doesn5t have an built7in )E*s ou5ll need to wire one in. We will talk more about this in a future chapter# but for now ou can $ust connect our chosen DPI@ pin to a resistor of around =M> 7 9>>> @hms# connect the other end of the resistor to the anode of the )E*# and connect the cathode of the )E* to ground !D6*". 8or future reference# this makes our )E* 3active high.3 %o test our )E* wiring# disconnect power to the board# remove the resistor lead from the DPI@ pin and connect it to Ccc or Cdd !the same voltage that powers our microcontroller"# then appl power. %he )E* should light up. @nce ou5ve confirmed our )E* wiring is good# disconnect power# remove the resistor lead from Ccc.Cdd and reconnect the lead back to the DPI@ pin. /nder no circumstances have the DPI@ pin connected directl to Ccc.CddN ,ere is a drawing of an )E* wired active high !0"# and one wired active low !<"

So here is our 0C1 Embedded ,ello World blink program# step b step.

9M

Step 6 7 Create (mpty Program


0tmel Studio will create an empt program for ou# including all the chip7specific data for the microcontroller ou specif . If ou are using a different compiler# or even a different language# the documentation for the compiler will tell ou how to create an empt program.

Step 8 7 Add AVR Initiali9ation


Initiali:ing the 0C1 involves two componentsA setting up the 0C1 clock# and configuring the selected DPI@ pin as an output. %he first of these# setting up the clock# does not involve an program code# but is done b configuring the 0C1 fuses. 8or simplicit we will use the 0C1 clock settings as it comes from the factor . %hese settings select the internal F M,: 1C clock# and select a clock divider of F# resulting in the chip running off the internal clock at a speed of 9 M,:. Configuring the )E* DPI@ pin as an output is simplicit itself. Simpl write a 9 into the proper bit position of the chosen port. )et5s pick bit > of port < as our )E* pin. Setting this pin as an output takes the following line of codeA U 9DDRB
(1!!0)"

%his sets the pin corresponding to P@1% < bit > to an output# and all other P@1% < pins to inputs. @r# to leave the other bits of **1< unchanged !the ma have alread been set earlier in the program" U 9DDRB #
(1!!0)"

Step 3 7 Add a So-t are Delay /unction


%he software dela function will take an input value and count that value down to ># then return. %he larger the value the longer it will take to count down to ># hence the longer the dela . *epending on the range of input values needed# the input value will either be declared as a 9G7bit or a E27bit variable. 0s mentioned earlier# on an F bit device like the 0C1# decrementing a E27bit variable will take longer than decrementing a 9G7bit variable# but this is not reall an issue since it can be accounted for b using a smaller dela value. /sing our blink count rule of thumb# our blink dela will take 9#>>>#>>>.9>> P 9>#>>> c cle loops !9#>>>#>>> is our 0C1 clock rate". 1emembering our data si:e vs numeric range table# 9>#>>> can fit into a 9G bit variable# but later we will speed up our 0C1 to F M,: !and other 0C1s can run up to 2> M,:"# so we5ll use a E27bit variable to be able to accomodate those higher clock rates. So here is our dela functionA

9F

U 9$oid de%&y($o%&ti%e uint32't d) 2( E )*i%e (d-- + 0) " = , I %he declaration of the dela count 5d5 sets it to a E27bit unsigned variable !uintE2Vt"# and also uses the 5volatile5 ke word to prevent the compiler from possibl optimi:ing awa the 3useless3 function !the function doesn5t change an thing in the program and is therefore unnecessar # in the view of the compiler". Without 5volatile5 the entire dela function might get optimi:ed into nothing and our )E* would end up blinking hundreds of thousands of times a second.

Step : 7 ;rite t"e +(D %link +oop


%he obvious wa to write the blink loop would seem to be thisA

%urn )E* @6 *ela %urn )E* @88 *ela )oop <ack

<ut there5s another wa that eliminates almost half of the loop codeA

4@1 )E* bit *ela )oop <ack

%he penalt for this method is that the )E* @6 and @88 times will alwa s be the same# while with the first method one could use different values for the @6 and @88 dela s. 0s usual# there5s a tradeoff. 8or our )E* blink we5ll use this second method.

Step < 7 Do nload Program to 1our .ard are


%his step is ver dependent on the tools ou are using# both the language toolchain !in m case# 0tmel Studio" and the programming hardware !in m case# the S%J7I>>". @nce ou have the programming hardware connected# ou will tell our toolchain the name and location of the file ou are downloading# and download it. %he program should then start to run on our hardware. %he file ou download will be the output of our toolchain. It ma be a .hex file# a .elf file# or some other file. Hou will have to determine what t pes of files our toolchain can generate# and what t pes our programming hardware can work with. 8or the 0tmel Studio . S%J7I>> setup a .hex file is used.

9O

6ow we have all the parts we need for our 0C1 )E* blink program. ,ere it is 7 it has been run on the S%J7I>> board and blinks 2.sec using the default compiler optimi:ation of @s. 6ote that if ou are using the S%J7I>> ou will have to set up our S%J7I>> board to connect P@1%< to the )E*s# using one of the supplied 9> conductor ribbon cables. Hou will also have to connect the ISP connector to the correct connector for the 0%megaFI9I using the G conductor ribbon cable. 0ll of this is in the S%J7I>> documentation. If ou run our program and our )E* $ust lights up# this can mean a few different things. Hour dela loop ma be running much slower than ou think !resulting in a ver long @6 time that looks like the )E* is $ust stuck @6"# or ma be running much faster than ou think !resulting in an )E* blinking so fast it $ust looks like it5s @6". 0 &uick check for either condition is to tr our program with a dela 9> times shorter than ou calculated# and a dela 9> times longer than ou calculated. %his ma bring the blink rate into the visible range. If ou have a scope# $ust put it on the )E* and ou5ll see if it5s blinking too fast# but the scope can5t tell ou it5s blinking too slow an better than our e es can tell ou. @ther reasons our )E* ma $ust light up !or sta dark" are that ou5ve wired the )E* to a different port pin than ou are toggling in the program# or that ou5ve compiled for the wrong chip# or that ou had a compiler or build error that ou didn5t notice# or that our download mechanism isn5t working# or that our download cable or connector is bad or wired wrong# or that our hardware is dead !*o ou have Ccc to all power pins# and D6* to all ground pinsU". Enough of that. ,ere is the 0C1 )E* <link 3,ello World3A U 9 2 E = I G M F O 9 > 9 9 9 2 9 E 9 = 9 I 9 G 9 M
-- ./R'B01 -- B%in1 02D on 3B0 -- 4%o51 6uses set 6o7 1 89: inte7n&% 5%o51 ;in5%ude !&$7-io<*= ;in5%ude !stdint<*= $oid de%&y($o%&ti%e uint32't d) ( )*i%e (d-- + 0) -- %oo>s )*i%e non-0 &nd de57e?ents " , int ?&in($oid) ( DDRB (1!!3B0)"

-- 3B0 ( 0) is ou7 02D out>ut bit" (1!!3B0)

)*i%e(1) -- 6o7e$e7 %oo> ( 3@RAB B (1!!3B0)" -- togg%e 02D bit de%&y(10000)" -- 1 89: - 100 , ,

2>

9 F 9 O 2 > 2 9 2 2 2 E

At Last, ,t ( in0s1 2ST.56 version4


0s mentioned above# the actual blink loop for the S%ME2 is identical in operation to that for the 0C1. %he main difference in programs is that the S%ME2 re&uires more configuration to get up and running# and of course all the chip registers are different as is their configuration. %he other difference relates to clock rates. %he 0C1 is running on its internal oscillator at its out7of7the7box rate of 9 M,: !though the chip is capable of running at 9G M,:# and some 0C1s can run at 2> M,:". %he S%ME2 can run at a clock rate of 2= M,:# but runs out of the box at F M,: using an internal oscillator. So our dela loop count will be F#>>>#>>>.9>> or F>#>>>. %hats it# all the differences between the two programs. 6o special connection or configuration is re&uired for the S%ME2C)*iscover board. Wust connect it to our PC /S< port. %he programming interface will be S% )ink# which should be an option in our toolchain. ,ere is the S%ME2 version of the program. It blinks at about =.I.sec using the 36one3 optimi:er setting. U 9 2 E = I G M F O 9 > 9 9 9 2 9
-- CA832'B01 -- B%in1 02D on 349 -- De6&u%t 8 89: inte7n&% 5%o51 ;in5%ude !st?32610D<*= $oid de%&y($o%&ti%e uint32't d) ( )*i%e (d-- + 0) " , int ?&in($oid) ( R44-=.3B22ER R44'.3B22ER'F@342E" -- en&b%e 3@RA4 G3F@4-=4R9 (0b0010 !! 4)" -- 4EH 0, 8@D2 2 (289: out>ut) )*i%e (1) ( -- 6o7e$e7 %oo>

29

E 9 = 9 I 9 G 9 M G3F@4-=@DR B (1!!9)" 9 de%&y(80000)" F , 9 , O 2 > 2 9 2 2 2 E

-- togg%e g7een 02D

< the wa # when testing this code I had forgotten to add 5volatile5 to the declaration of the dela parameter# and the )E* $ust lit up and sta ed lit. I put the scope on the )E* pin and saw that the )E* was actuall blinking ever few microseconds# and immediatel knew what I had done wrong !without 5volatile5 the optimi:ation setting I was testing had optimi:ed awa the entire dela routine# exactl as mentioned above". Without a scope that would have been a much harder problem to solve# since the 9>x faster and 9>x slower fixes would not have made an difference. I wasn5t kidding when I said ou reall should tr to get a scope if at all possible. I can5t tell ou how man times I5ve seen a beginner write about how some problem has cost him da s or weeks# when a scope would have found the problem in minutes.

,t7s )! " !o, ,t7s )88 " !o, ,t7s )!999


I have to confess# I cheated. Well# let5s sa I glossed over something ver important. In our )E* <link program# we never cared about whether an output 595 or an output 5>5 turned on the )E*. Since we were $ust flashing with a I>.I> dut c cle# it was enough to simpl alternate between 9 and >. <ut that is one of the ver # ver few cases where ou won5t need to know our output polarities. <asicall # to turn on an )E* we need to drive current through it# which means putting a voltage differential across it. %hat differential is not fixed but varies with )E* color and current# but it5s about 2C at the low end and E.IC at the high end. We add a series resistor to drop the remaining voltage at our chosen )E* current !e.g. with a 2C )E* and a IC Ccc we need to drop that extra EC". Since microcontroller outputs drive near >C !D6*" for a 5>5 and near Ccc for a 595# ou can either turn on an )E* with a 595 b connecting the other end of the 22

)E*.resistor combination to D6*# or turn it on with a 5>5 b connecting the other end to Ccc 7 the )E* doesn5t care at all. %here are general terms that describe whether a 595 or a 5>5 turns on the circuitr connected to an output. Circuitr turned on b a 595 is called 3active high#3 and circuitr turned on b a 5>5 is called 3active low.3 0s it happens# the )E*s on the S%J7I>> are active low# and on the S%ME2C)*iscover the are active high. Even though the two boards have opposite polarit active )E* outputs# the same simple blink code works with both of them# as discussed above. 0 &uick wa to see the effect of )E* output polarit is to make the @6 dela much different !either shorter or longer" than the @88 dela . 0 difference of 9> to 9 will be clearl visible !though ou ma need to increase both of our dela s to slow down the blink enough for the difference to be apparent# while keeping the 9> to 9 ratio". 0 9> to 9 ratio also has the advantage that ou can change our long dela to a short dela b deleting a ># and our short dela to a long dela b adding a >. 6ow it is no longer enough to simpl 4@1 the )E* output bit# since we have two different dela s. So our )E* <link program now looks like this !0C1 shown# S%ME2 identical in operation# exercise left to the reader"A U 9 2 E = I G M F O 9 > 9 9 9 2 9 E 9 = 9 I 9 G 9 M 9 F 9 O 2 > 2
-- ./R'B02 -- B%in1 02D on 3B0, 1-10 -- 4%o51 6uses set 6o7 1 89: inte7n&% 5%o51 ;in5%ude !&$7-io<*= ;in5%ude !stdint<*= $oid de%&y($o%&ti%e uint32't d) ( )*i%e (d-- + 0) -- %oo>s )*i%e non-0 &nd de57e?ents " , int ?&in($oid) ( DDRB (1!!3B0)" )*i%e(1) ( 3@RAB # (1!!3B0)" &5ti$e %o)) de%&y(2000)" 3@RAB I J(1!!3B0)" &5ti$e %o)) de%&y(20000)" , ,

-- 3B0 ( 0) is 02D out>ut bit" (1!!3B0)

-- set 02D bit (02D @E 6o7 &5ti$e *ig*, @HH 6o7 -- s*o7t de%&y -- 5%e&7 02D bit (02D @HH 6o7 &5ti$e *ig*, @E 6o7 -- %ong de%&y

2E

9 2 2 2 E 2 = 2 I So now we are first using the @1 operator SP to set the output bit for a short time# then the 06* operator to clear the output bit for a 9>x longer time. 8or the S%J7I>> with active low )E*s this will result in a short )E* @88 and a longer )E* @6. 8or the S%ME2C)*iscover the same code would produce a short )E* @6 and a longer )E* @88. < running the program twice# once with the long dela after the SP followed b the short dela # and once with the short dela after the SP followed b the long dela # ou can confirm !or determine" whether our particular )E* setup is active high or active low.

What &appens ,f , Push This (utton?


Well# not a lot until ou program it. 0 button !or switch or ke # the 5re all switches# and ever thing that follows applies to buttons# switches and ke s" will be wired so as to drive an input pin# which is sitting at a certain state# to the opposite state when pushed or activated. Most commonl # a button will be wired so that the input is held high !595" when not pushed# and brings the input low !5>5" when pushed. %his is how ou should wire our buttons in most cases# but it is also possible to wire a button so that the input is held low !5>5" when not pushed# and brings the input high !595" when pushed. In either case# our software will look for the input state to go from the inactive state to the active state# indicating a button !.switch.ke " push. %he concept of active high and active low# discussed above for outputs# also applies to inputs. If our button is wired so that it goes to 5>5 when pushed# it is an active low button. If wired so that it goes to 595 when pushed# it is active high. Hou will have to account for the polarit of our button inputs in our button reading software# but this is &uite simple. 6ote that there is nothing to prevent ou from having some active high buttons and some active low buttons# even on the same input port. %his also applies to having inputs and outputs on the same port 7 it5s not a problem# and is in fact extremel common. 0nd to answer the next &uestion# es# ou can have active high outputs# active low outputs# active high inputs and active low inputs# all on the same port. ,ere is a diagram of a button wired active high !0" and one wired active low !<". %he resistor holds the input in the inactive state until the button is closed# at which point the input is pulled to the active state. If ou want ou can think of it as a resistive divider# with the switch being a billion @hms when open# and > @hms when closed. 0 t pical pullup or pulldown resistor# which might be built into the uC or might be an external component# would be in the range of 9> to 9>> k@hms.

2=

)ne (it, :ust )ne


Wust like outputs# inputs come in bunches. 0C1 inputs !and outputs" come in ports of F bits. S%ME2 inputs and outputs come in ports of 92 bits. We glossed over this when we were blinking our )E*# but it5s time to talk in more detail about isolating one bit out of a port# both in input and in output modes. 0nd keep in mind that ever thing that applies to a single bit can also appl to multiple input and output bits. 0s we saw with our )E* blink code# we can isolate a single output bit in a port using the bitwise @1 operators !S or SP" and the bitwise 06* operators !R or RP". %hese operators will map to corresponding CP/ @1 and 06* instructions. %o set a single bit# ou @1 a 9 into that bit position# and >s in all the other bit positions. )ikewise# to clear a single bit# ou 06* a > into that bit position# and 9s in all the other bit positions. %hese operations force a 9 or > into the selected bit position# while leaving all the other bit positions unchanged. %o isolate a single input bit# we use the 06* operator to read that bit position while putting >s into all the other read bit positions. %his gives us an 67bit word with one bit holding the value of the desired input bit# and all the other bits holding >s. %hat means the entire word will be > if the desired input bit was 5>5# or the entire word will be some non7:ero value if the desired input bit was 595. It is important to understand that the result will not# in general# be a word that contains a > or a 9# but a word that contains a > or some non7:ero value. %hus ou will not# in general# test the word for > or 9# but for > or non7:ero. 8or example# if the desired input bit is bit I# the word will either be > or 9XXI# that is# either > or E2. It is a common mistake to test for a 9 in such cases instead of testing for non7:ero. 0nd to reiterate# all of this applies to multiple bits as well as to single bits. %hus ou could set bits 9# E and =# and clear bits > and I# as followsA U 93@RA. # 23@RA. I

0b00011010 0b11011110

2I

In a future chapter we will discuss possible problems with the above# and wa s to solve those problems. 6ote that in the 2nd code line above we have >s in bits > and I# and 9s in all the other bits. %his is the correct wa to clear bits > and I# but in man cases ou want to indicate the bits in a more s mbolic wa such as U 93@RA. I
((1!!5) # (1!!0))" -- 7ig*t side is 0b00100001

6ow we5re being explicit about the bits we want to operate on# but the polarit is wrongN We5ve got >s where we want 9s# and 9s where we want >s. So we5ll use the ver hand C bitwise inversion operator 5Q5# which ma translate to a CP/ instruction called 6@% or C@M !for complement"A U 93@RA. I
J((1!!5) # (1!!0))" -- 7ig*t side is 0b11011110

Hou can also check for multiple inputs# such as bits ># 9 and 2# as followsA U 9in>uts
3@RAB I 0b00000111"

Since in man cases our buttons will be wired active low# this puts >s in all our unwanted bits# and also puts >s in our active button bits 7 the ones being pushed. %hat5s clums # since we have > representing both 3ignore3 and 3active3. %he solution is once again to use bitwise inversion# this time on the raw input before doing the maskingA U 9in>uts
J3@RAB I 0b00000111"

6ow when we do the mask# our active button bits will be 595# and both our inactive button bits and our 3ignore3 bits will be 5>5# which is a much more consistent representation.

#sing a (utton to Contro an LE%he simplest wa to have a button control an )E* is to turn the )E* @6 when the button is depressed !active"# and turn it @88 when the button is released. %here are other behaviors we could also program# such as push @6 . push @88# but we will leave those for later exploration. @ur simple button.)E* program consists of a loop that continuousl reads the button input and sets the )E* output accordingl . Such a loop will execute hundreds of thousands of times a second 7 far# far faster than it needs to execute to control an )E* with a response acceptable to a human. It would be perfectl fine to onl check the button and control the )E* ever 9> to 2Ims !the human response interval we will discuss in the next section"# but that takes more work because we have to add a dela # and a dela serves no purpose here. )ater we will learn how to perform useful work during those dela s# and then it will make 2G

sense to check human inputs and produce human outputs at the rate of 9>s per second# not hundreds of thousands per second. <ut for now we5ll $ust run the loop at full speed since that produces the simplest code. %o control our )E* with a button# we will take our )E* blinking program and change the code inside the infinte !or forever" loop. Each time through our loop we will check the button and# depending on whether the button is pushed or released 7 active or inactive 7 we will turn the )E* @6 or @88. ,ere is the 0C1 version of )E* control program. 6ote that we don5t configure our input bit# P0M# as an input. %his is because when the 0C1 comes out of 1ESE%# all of the DPI@ pins are automaticall configured as inputs. Input is the default DPI@ state. U 9 2 E = I G M F O 9 > 9 9 9 2 9 E 9 = 9 I 9 G 9 M 9 F 9 O 2 > 2 9 2 2 2
-- ./R'401 -- 4ont7o% 02D on 3B0 using button on 3.7 -- 4%o51 6uses set 6o7 1 89: inte7n&% 5%o51 ;in5%ude !&$7-io<*= ;in5%ude !stdint<*= int ?&in($oid) ( DDRB 1!!3B0"

-- 3B0 is out>ut, &%% ot*e7s in>ut

)*i%e(1) ( i6 (3FE. I (1!!3.7)) -- test 3.7 6o7 0 o7 non-0 ( 3@RAB # (1!!3B0)" -- 5%7 02D , e%se ( 3@RAB I J(1!!3B0)" -- set 02D , ,

2M

E 0nd here is the S%ME2 version. @nce again# we do not have to configure P0> as an input because all DPI@ pins are configured as inputs when the 'C comes out of 1ESE%. U 9 2 E = I G M F O 9 > 9 9 9 2 9 E 9 = 9 I 9 G 9 M 9 F 9 O 2 > 2 9 2 2 2 E

-- CA832'401 -- 4ont7o% 02D on 349 )it* button on 3.0 -- De6&u%t 8 89: inte7n&% 5%o51 ;in5%ude !st?32610D<*= int ?&in($oid) ( R44-=.3B22ER # R44'.3B22ER'F@342E" -- en&b%e 3@RA4 6o7 02D out>ut G3F@4-=4R9 (0b0010 !! 4)" -- 4EH 0, 8@D2 2 (289: out>ut) )*i%e (1) ( i6 (G3F@.-=FDR I (1!!0) ( G3F@4-=@DR # (1!!9)" , e%se ( G3F@4-=@DR I J(1!!9)" , ,

-- set g7een 02D

-- 5%7 g7een 02D

<oth of these programs behave identicall . Push the button and the )E* goes @6. 1elease the button and the )E* goes @88. <ut wait# the 0C1 )E* is active low# and the S%ME2 )E* is active high 7 how can the both behave the same wa U %he answer is that the S%J7 I>> button inputs are wired active low# and the S%ME2C)*iscover buttons are wired active high. 8or both boards# the polarit of the input buttons and the output )E*s is the same. 8or the 0C1 board both are active low# and for the S%ME2 board both are active high. %his is

2F

wh both programs behave the same. 0 5>5 on the input pin produces a 5>5 on the output pin# and a 595 produces a 595. If ou had a board where the polarit of the inputs was opposite to the polarit of the outputs !a perfectl reasonable possibilit "# ou would have to reverse the logic in the if7else statement# or else the )E* would be @6 when the button is not pushed# and would go @88 when the button is pushed. %he point of this is to alwa s know the polarit of the signals ou are dealing with# both inputs and outputs.

The #ser Can Push a (utton At Any Time 2the +at4


Inputs are fundamentall different than outputs in that outputs are under our program5s control 7 the happen when our program chooses for them to happen 7 while inputs can happen whenever and wherever. %o avoid missing an inputs# our program has to act as if it is alwa s watching the inputs# even while it is doing all the other stuff it needs to do. *epending on the number and speed of the inputs# and on what that 3other stuff3 is# this can be fairl simple or it can get rather complicated# but it must be done if our program is to be reliable. In the case of buttons# the ke !N" &uestions to ask are 3how &uickl can a user press and release a buttonU3 and 3how soon after the user presses a button does m program have to actU3 %he first &uestion is about making sure our program doesn5t completel miss a user button push. %he second &uestion is about making sure our program is properl responsive# not 3slow3 or 3lagging3 or 3reall anno ingN3 %o provide some numbers# a t pical real7life button push might be as short as I>79>> milliseconds !I $ust timed a bunch at G>79>>ms". 0 good response time might be 9>>ms. What this means is that ou have to be checking our buttons at least ever I>ms to make sure ou don5t miss an # and to have suitable response time. I would consider a checking interval of between 9> and 2Ims much better. %his means that whatever else our program is doing it needs to come back and check for button pushes => to 9>> times per second. )uckil this is not as hard as it might sound# but it does re&uire a bit of cleverness# as we will see in a future chapter.

(ut , )n y Pressed ,t )nce1


0s if needing to be sure to read buttons faster than a human can press them was not enough of an issue# it turns out that ou also need to be sure not to read buttons %@@ fast. %he reason for that is that most buttons and other mechanical contacts 3bounce.3 %his means that when the open or close# the mechanical bits inside twist and bounce and vibrate and the can actuall produce multiple make or break signals for a single real make or break. @ver a period of a few hundred microseconds# a button ma open and close 2 to 2> times or more# resulting in 2 to 2> or more button 3events.3 1emembering that we have microcontrollers that can execute 9> to 9>> or more instructions in a microsecond# it would be a trivial matter for a 'C to sense each of those 272> button events. Is this a problemU Well# imagine if ou

2O

sent somebod mone via Pa pal and ou ended up sending the same amount 9= separate times because our E6%E1 ke bounced. ,ere are E scope pictures of the same button pushed E different times. %he timescale is 9>>us . division. See the problem nowU

<ut waitN ou ob$ect. %he Control )E* program above works and it is reading the button extremel often# tens or hundreds of times per millisecond. %hat is correct. In this one case# it doesn5t matter# because if the )E* turns on and off a few times in less than a millisecond on button push or release# it doesn5t matter. %he phantom on and off )E* pulses happen far too fast for ou to notice them. <ut the *@ happen# as can be seen b the scope images below. %his shows the signal to the )E*# not the input signal at the switch. So ou can see that the Control )E* program loop is plent fast enough to detect the bouncing of the button and process it as multiple events. It5s $ust that having multiple )E* events doesn5t matter !once again we got luck # $ust as in the case of the )E* polarit for I>.I> blinking". <ut this is generall not the case 7 most of the time a single button push must onl register as one button event# and the code must be written so as to ignore an button bouncing. We will discuss wa s to ignore bouncing# or 3debounce3 an input# in a future chapter when we have laid the necessar groundwork. ,ere are some switch bounces that have been read b the 'C and sent to the output. Same 9>>us . division timescale. %his is exactl what ou *@65% want# in most casesA

E>

So b now ou should be convinced that reading lowl buttons is a bit more involved than it would seem. In a future chapter of this tutorial we will talk about how to read buttons reliabl # neither missing an real changes nor responding to an phantom changes# but before that we need to discover the wonderful world of timers# and before that we need to discover the e&uall wonderful world of interrupts. Dood stuff coming upN

,t7s Too Soon To Ta 0 About ,nterrupts1


%hat# at least# could be one reaction to this chapter. <ut over the ears I5ve become convinced that new microcontroller programmers should understand interrupts before being introduced to an complex peripherals such as timers# /01%s# 0*Cs# and all the other powerful function blocks found on a modern microcontroller. Since these peripherals are commonl used with interrupts# an introduction to them that does not include interrupts is an incomplete introduction# and each peripheral would have to be revisited after the concept of interrupts was finall introduced. %hat $ust strikes me as wasteful. So# let5s talk about interrupts. I used to be intimidated b them when I started programming 'Cs# and ma be ou are too. %hat is ver much the wrong attitude 7 interrupts are not some kind of magic# but $ust a ver powerful and ver manageable techni&ue for dealing with all kinds of things that need attention at 3unexpected3 times. Consider this scenario. We need to monitor a DPI@ input and respond to that input going active within a few microseconds. 6ot tens of milliseconds as was the case with our human button pushes# but a few microseconds. %he 'C program could be at an point in its execution when this input goes active. We need to do the e&uivalent of monitoring this signal not a hundred times per second# but hundreds of thousands of times per second# while still doing all of the other things our program needs to do. ,ow can we possibl respond so &uickl # and still have an time to do an other work in our programU Interrupts are the answer.

E9

!ow ;ou See ,t, !ow ;ou -on7t


0n interrupt is a signal !generall called an 3interrupt re&uest3" to the CP/ to immediatel begin executing different code# code that is written to respond to the cause of the interrupt. 3Immediatel 3 can be as soon as the end of the current instruction# in the best case. %he time between the generation of the interrupt re&uest and the entr into the IS1 is called the 3interrupt latenc #3 and faster !lower latenc " is alwa s better. %he CP/ will remember the location of the next instruction it was going to execute b storing that instruction address in a register or memor location# and will then $ump directl to the code designated b the programmer for that particular interrupt. In addition to saving the address of the next instruction# it will often also save the CP/ status register and disable further interrupts. It will also# in most cases# automaticall clear the interrupt re&uest that triggered the IS1 entr # so that a single interrupt re&uest does not result in the IS1 being entered multiple times. @ccasionall # depending on interrupt t pe and microcontroller design# it will re&uire user code in the IS1 to explicitl clear the interrupt re&uest. It is ver important to check the datasheet for a given interrupt source to see if the IS1 code must clear the interrupt re&uest# or the uC will $ust keep re7interrupting on the one interrupt re&uest event forever. 0 microcontroller CP/ will be designed to respond to a number of different interrupt sources !perhaps 9> to 9>> sources# t picall "# and each source can have specific user7written code which executes when that interrupt triggers. %he code that executes for an interrupt is called the 3interrupt service routine3 or IS1. %he 3now ou see it# now ou don5t3 heading refers to what ou would see if ou were watching the code execute in slow motion. %he program counter would be moving from one instruction to the next# and then when the interrupt triggered the PC would suddenl end up in some totall different area of the program !the entr point of the IS1". %hen# when the IS1 was complete# the PC would $ust as suddenl be pointing back to the next instruction# as if nothing had happened. Interrupts are alwa s off or disabled when coming out of 1ESE%. In fact# the are doubl disabled. Each individual interrupt source is disabled# and also the global CP/ interrupt flag is disabled. 8or an interrupt to happen !to have its IS1 run"# both the individual interrupt must be enabled# and the CP/ global interrupts must be enabled. 0nd finall # of course# the interrupt condition itself must occur e.g. a pin being driven low# or a timer rolling over# or whatever the particular interrupt is triggered b . It is up to the user program to select which interrupts to enable# and at what points in the program execution to enable them !and once enabled# an interrupt can be disabled again# then re7enabled# as often as needed". %he user program will also have an IS1 !ver similar to a function" for each interrupt that will be enabled# and each of these IS1s will be mapped to its corresponding interrupt source. It is crucial that an interrupt that is enabled# ever# has a valid IS1 mapped to it. @therwise# when the interrupt triggers# the CP/ will start tr ing to execute code in some garbage location 0J0 in the weeds# and that5s the end of that. @r# if ou5re luck # the compiler will have supplied an @opsN IS1 for all unused interrupts# and with our debugger ou5ll find the 'C looping in that IS1 and figure out where ou went wrong. %his diagram attempts to show how an interrupt# if enabled# pauses the background code and runs the associated IS1A

E2

0nd here is a diagram which shows an interrupt event occuring before the interrupt is enabled# and how the interrupt is onl responded to after it is enabledA

Where Are The +eturn Address And +egister -ata Saved?


0s part of the automatic response to an interrupt re&uest# the uC hardware will save the address of the next instruction that was to be executed. %his address is either saved onto the stack !as pointed to b the CP/ stack pointer"# or it ma be saved into a special CP/ register# often called the 3link register3. %he 0C1 uses the hardware stack pointer# while the S%ME2 uses a link register. Saving to a link register is faster because register7to7register moves are alwa s faster than register7to7memor moves# but when another level of interrupts !or subroutines" is re&uired# the link register must then be saved off to memor b user code !code that is generated b the compiler if ou5re using a ,))". 0s for an register data that is saved and restored# that data will also be saved to a stack 7 that is# out to memor . %hat stack ma be the stack maintained b the hardware stack pointer# or it ma be a stack maintained b software# $ust as is used for saving off the link register when necessar .

EE

,s an ,S+ a 8unction?
0s stated# an IS1 is similar to a function# but there are some critical differences for most microcontroller designs. Most of these differences revolve around the fact that an IS1 must be 3invisible3 to the background code that was executing when the IS1 is run. What this means is that no CP/ registers or flags can be altered that the background code ma be using. %he total state of the CP/# as far as the background code is aware# must be identical when the IS1 returns as when the interrupt triggered. So the IS1 must save the state of the CP/ on entr # and restore that same state on exit. If using a high level language# the compiler will take care of most or all of this state saving and restoring. %he programmer simpl has to tell the compiler that the IS1 function is an IS1# and what interrupt it maps to# and the compiler will fill in the code details invisibl . %he compiler will add the following code to the IS1A

<efore user IS1 code# code to save all flags and registers necessar to maintain main code state 0fter user IS1 code# code to restore all saved flags and registers in correct order 8inall # a return7from7interrupt instruction which performs an final cleanup such as restoring the saved status register# and returns program execution to the instruction after the interrupted instruction

%he compiler will also handle mapping the actual IS1 code to the associated interrupt. If writing in 0SM the programmer has to write all the state saving and restoring code explicitl # as well as explicitl place the address of the IS1 into the appropriate interrupt vector or slot. Each possible interrupt will have an entr in a region of memor usuall called the interrupt table or interrupt vector table. *epending on the CP/ design# these entries might contain actual code# or the might be data locations where the starting addresses of the associated IS1s are stored. 0s it happens# the 0C1 uses the first form of interrupt table !each interrupt vector location contains a $ump to the associated IS1"# while the S%ME2 uses the second form of table !each interrupt vector location contains the address of the associated IS1". Interrupt vector table entries for interrupts that will never be enabled can either be left empt # or can all be set to point to a dumm IS1 that $ust returns immediatel # or can all be set to point to an IS1 that somehow logs and reports the unexpected interrupt !was it a hardware glitchU did the programmer forget to write an IS1 for an enabled interruptU".

What Shou d An ,S+ -o?


In most cases the answer is# as little as possible. 0n IS1 should do what must be done immediatel # but should not do an thing that is better left for later# to be done in the background code. ,ere5s an analog A Hou5re carving an ice sculpture for our whist club when the doorbell rings !interruptN". Hou answer the door !start of IS1" and receive the 2>>> piece $igsaw pu::le ou5ve ordered. 6ow# do ou sit down and complete the pu::le at this momentU @r do ou put the pu::le in a safe place and get back to the sculpture before it meltsU Prudence sa s that ou put the pu::le aside and get back to the other work at hand# then come back to the pu::le when ou have nothing more pressing to do. %he same applies E=

to an IS1. *o what ou must in the IS1# and save off an data or state information so that our background code can do the rest of the work at a more appropriate time. %his becomes even more critical when multiple interrupts are enabled# as will usuall be the case in an embedded s stem. ,aving IS1s that take a long time to run !because the do too much inside the IS1" ma cause dela ed response to other interrupts that need fast handling.

Who +ang?
In some cases onl one event can trigger a given interrupt# so the IS1 for that interrupt knows exactl the event that has triggered it. ,owever# in man cases multiple events will all vector to a single IS1. 8or example# with the S%ME2 as man as = timer events can vector to a single timer IS1 !we will see this in the chapter on timers". In these cases the first task of the IS1 is to determine which event has caused the interrupt. It will do this b checking the appropriate flags for all the possible interrupting events until it finds the one that has caused the interrupt. %hen it will handle that interrupting event in the normal fashion.

,nterrupt"-riven LE- Program


@ne form of interrupt that all microcontrollers can respond to is a simple digital change of state on a specified pin !usuall called an 3external interrupt3 pin". 8or example# if the pin is held high b a pullup resistor# and then is brought low b some signal or action# the high7to7 low transition on the pin can trigger an interrupt. We can modif our button7controlled )E* program to detect the button transition via interrupt and change the )E* accordingl . %his will give us a simple introduction to interrupts# and also show wh reading buttons with external interrupts is not reall a good idea. Whether ou are using an 0C1 or an S%ME2# the first step is to look on the chip datasheet to see which pin or pins supports external interrupts. 8or the S%J7I>> with the 0%megaFI9I# there are three such pins# P*2# P*E and PE># so we will choose to connect the F buttons of the S%J7I>> to P@1%*# and use either button 2 or button E 7 let5s choose button 2 !P*2" which is external interrupt I6%>. 8or the S%ME2C)*iscover board# we onl have one button# wired to P0>. It turns out that the S%ME2 device is ver flexible and almost an DPI@ can be used as an external interrupt# so our button on P0> will be fine.

E'terna ,nterrupt Type And Po arity


%here are a few different wa s that an external interrupt can be configured# depending on what the particular microcontroller allows. In the first place# external interrupts can be edge7 triggered or level7triggered. 0n edge7triggered interrupt generates an interrupt re&uest onl on an edge 7 that is# when the interrupt line goes from one state to the opposite state !97Y> or >7Y9". 0 level7triggered interrupt generates an interrupt re&uest whenever the interrupt line is in the active state# so for example a low7level7trigger interrupt will generate re&uests whenever the line is low# and !this is important"# will continue to generate re&uests EI

until the line is brought high. If the software or the hardware is incorrectl designed# a single level7triggered interrupt could result in a furious c cle of interrupt..end interrupt..re7enter interrupt..end interrupt and so on# thousands or millions of times per second. %he second aspect of an external interrupt is the interrupt polarit . 8or an edge7triggered interrupt# the polarit could be rising edge triggered !>7Y9" or falling edge triggered !97Y>". 8or a level7triggered interrupt# the polarit could be low triggered !whenever the input is >" or high level triggered !whenever the input is 9". 0gain# these are possible configuration options# but not ever external interrupt input on ever microcontroller will support all of these options. It is even possible to have an external interrupt input that triggers on an logic change 7 that is# it will generate an interrupt re&uest on a high7low transition !97Y>"# or on a low7high transition !>7Y9". %his is $ust an edge7triggered interrupt that triggers automaticall on both possible edges rather than $ust on one specified edge. 6ote as well that the distinction between edge7triggered and level7triggered interrupts is a fundamental distinction for all t pes of interrupts# not $ust external interrupts.

Configuring ,nterrupts
In the general case# configuring a particular interrupt re&uires taking a good look at the chip data sheet. 8or external interrupts on fixed pins things are easier than the more configurable interrupts on parts like the S%ME2. %he 0C1 I6%> interrupt onl re&uires us to select the interrupt polarit and enable the interrupt. 0C1 I6%> can be configured for rising7edge# falling7edge# both7edge# or low7level triggering. 1emembering that S%J7I>> buttons are active low# we will choose falling7edge so as to be able to catch the initial button push !otherwise we would have to wait until the button was released before an interrupt was generated". )ooking at our 0C1 datasheet we see that we need to set bits ISC>9.ISC>> in register configuration MC/C1 to 9.> for negative7edge trigger. 6ow we need to enable the I6%> interrupt# so we need to set bit I6%> in interrupt enable register DIC1. 0t this point# if global interrupts are enabled# the CP/ will respond to an negative7edge input on I6%>. What5s more# if there was a previous edge that set the internal I6%> flag# that previousl set flag would now generate an interrupt. %his is almost alwa s a bad thing# generating an interrupt off a previousl set flag# so we need to do one other thing before enabling the interrupt. We need to write a 595 to bit I6%8> in interrupt flag register DI81. Writing a 595 clears an pending I6%> interrupt# which is what we want to do before enabling the interrupt. So our full 0C1 configuration is as followsA U 984K4R 2GFHR EGF4R

0b10 !! FC400" -- neg&ti$e edge t7igge7 1 !! FEAH0" -- 5%e&7 &ny >ending inte77u>t 1 !! FEA0" -- en&b%e FEA0

The ,S+
6ow it5s time to write the interrupt service routine. 8or the 0C1# a negative edge means the button has $ust been pushed# so we want to turn the )E* @6. )ikewise# a positive edge EG

means the button has been released and we want to turn the )E* @88. <ut wait# we5ve onl configured for a negative edge# how will we detect a positive edge. %his is actuall prett simple. Inside the IS1# if the interrupt is configured for negative edge# reconfigure it for positive edge. If configured for positive edge# reconfigure for negative edge. In the same code branches that reconfigure the interrupt# turn the )E* @6 or @88 depending on which edge has brought us into the IS1 this time. Marking the IS1 as an IS1# and mapping it to the I6%> vector# is ver compiler dependent as was mentioned. 8or 0tmel Studio5s version of the gcc compiler# it will look like thisA U 9FCR(FEA0'$e5t) 2( -- FCR 5ode *e7e E , =

The 8ina Program " A3+


,ere is the full program# with the configuration and IS1 as discussed above# and the global interrupt enable $ust before entering the forever loop. %he IS1 alternates the next interrupt edge and turns the )E* on or off according to the edge that $ust caused the interrupt. 0s discussed above# when the IS1 is entered the interrupt re&uest is automaticall cleared b the hardware# so there is no code re&uired in the IS1 for this task. %o simulate background code doing its own thing# the program has our old )E* <link loop running. %hus in action ou will see one )E* blinking via the background code# and another turning on and off via the button interrupt.

U 9 2 E = I G M F O 9 > 9 9 9 2 9 E

-----

./R'FEA1 B%in1 02D on 3B0 4ont7o% 02D on 3B1 $i& button inte77u>t on 3D3

;in5%ude !&$7-io<*= ;in5%ude !&$7-inte77u>t<*= -- b7ing in inte77u>t stu66 FCR(FEA0'$e5t) -( i6 (84K4R I (1!!FC400)) -( 3@RAB # (1!!3B1)" -84K4R I J(1!!FC400)" -, e%se -- neg< edge ( 3@RAB I J(1!!3B1)" -84K4R # (1!!FC400)" -, te%% 5o?>i%e7 t*is FCR is 6o7 FEA0 (3D2) t7ue i6 >os< edge t7igge7 02D 1 @HH set to neg< edge t7igge7 t7igge7 02D 1 @E set to >os< edge t7igge7

EM

9 = 9 I 9 G 9 M 9 F 9 O 2 > 2 9 2 2 2 E 2 = 2 I 2 G 2 M 2 F 2 O E > E 9 E 2 E E E = E I E G E M E F

, $oid de%&y($o%&ti%e uint32't d) ( )*i%e (d-- + 0) " , int ?&in($oid) ( DDRB (3!!3B0)" 3@RAB (3!!3B0)" 84K4R GFHR GF4R sei()" )*i%e(1) ( 3@RAB B (1!!3B0)" de%&y(80000)" , 0b10!!FC400" 1!!FEAH0" 1!!FEA0"

-- 02D out>ut on 3B0, 3B1 -- st&7t )it* 02Ds @HH -- neg&ti$e edge t7igge7 -- 5%e&7 &ny >ending inte77u>t -- en&b%e FEA0 -- en&b%e &%% inte77u>ts

-- togg%e 02D 0 in t*e b&51g7ound

EF

E O = > = 9 = 2 = E = = = I %his scope image shows the dela between the interrupt7triggering button push !green" and the )E* output inside the IS1 ! ellow" with the above program running. %he timescale is 2us . division# so the response time from external input to IS1 output is onl about E microseconds !with the 0C1 running at F M,:"A

The 8ina Program " ST.56


@nce again# the S%ME2 version of this program is fundamentall the same as the 0C1 version. What is different is mostl the re&uired interrupt configuration and IS1 declaration. Since the S%ME2 external interrupt capacit is so customi:able# it re&uires more initiali:ation than the 0C1. 1egarding the IS1 declaration# the 01M Cortex M famil is rather uni&ue in that it is designed so that regular functions can be IS1s. %his is possible because the Cortex M design automaticall saves all the status and register data necessar before entering an IS1# and restores it all at the end of the IS1. %hat is# the Cortex M does automaticall all the housekeeping that re&uires additional IS1 code in most other designs. %hus our IS1 is $ust a normal function# without an additional IS1 code# which is given the proper name for the E4%I> interrupt so that it overrides the weak declaration in the startup code. %hat ma be a bit advanced# so $ust know that it is enough to name the IS1 the correct name 7 I5m sure it5s in the documentation somewhere# but I $ust looked at the startup files to find the correct name. <eing able to look into our compiler startup files is a big advantage# and ou should not be afraid to poke around in them.

EO

0lso note that# unlike the 0C1# the S%ME2 re&uires that the interrupt re&uest be explicitl cleared inside the IS1. U 9 2 E = I G M F O 9 > 9 9 9 2 9 E 9 = 9 I 9 G 9 M 9 F 9 O 2 > 2 9 2 2 2 E 2 = 2 I 2 G 2 M 2
----CA832'FEA1 B%in1 02D on 349 4ont7o% 02D on 348 $i& button inte77u>t on 3.0

;in5%ude !st?32610D<*= $oid 2LAF0'FRM9&nd%e7($oid) -- FCR is Nust & 7egu%&7 6un5tion )it* 5o77e5t n&?e ( i6 (2LAF-=RACR I 1) -- )&s set 6o7 >ositi$e edge ( G3F@4-=@DR # (1!!8)" -- set b%ue 02D 2LAF-=RACR 0" 2LAF-=HACR 1" -- set 6o7 neg&ti$e edge , e%se -- )&s set 6o7 neg&ti$e edge ( G3F@4-=@DR I J(1!!8)" -- 5%7 b%ue 02D 2LAF-=HACR 0" 2LAF-=RACR 1" -- set 6o7 >ositi$e edge , 2LAF-=3R 1" -- 5%e&7 t*is inte77u>t 6%&g , $oid de%&y($o%&ti%e uint32't d) ( )*i%e (d-- + 0) " , int ?&in($oid) ( R44-=.3B22ER # R44'.3B22ER'F@3.2E" -- en&b%e 3@RA. 6o7 button in>ut G3F@.-=4R0 (0b0100)" -- 4EH 1, 8@D2 0 (6%o&ting in>ut) R44-=.3B22ER # R44'.3B22ER'F@342E" -- en&b%e 3@RA4 6o7 02D out>ut G3F@4-=4R9 0b0010 # (0b0010!!4)" -- 4EH 0, 8@D2 2 (289: out>ut) (348,349) .HF@-=2LAF4RO0P 0" 2LAF-=RACR 1" 2LAF-=F8R 1" E/F4-=FC2RO0P (1 !! 2LAF0'FRMn)" )*i%e (1) ( G3F@4-=@DR B (1!!9)" de%&y(80000)" , , ----2LAF0 is 3.0 7ising edge, 2LAF0 en&b%e 2LAF0 en&b%e 2LAF0 in E/F4

-- togg%e g7een 02D

=>

F 2 O E > E 9 E 2 E E E = E I E G E M E F E O = > = 9 = 2 = E = = = I = G = M = F = O

,nterrupts Can7t (e That Simp e, +ight?


1ight. %here are a lot of details and gotchas left to discuss regarding interrupts. 8or example# our interrupt programs above are both garbage# actuall 7 that is# the can give the =9

wrong results on occasion. Hou ma even notice that the )E* occasionall seems to miss a button push. <ut the 5re good enough to show ou how interrupts work# and to provide a foundation for further work with them. %he next tutorial chapter will go into some of the most important details and gotchas# including what is wrong with our interrupt programs and how to fix them. 0fter that we5ll start looking at timers# one of the most useful of all microcontroller peripherals# but one that prett much re&uires interrupts to be used effectivel .

A Litt e .ore -etai About The ,nterrupt .echanism


It5s time to look a little closer at what happens in an interrupt re&uest and response. 0gain this is in general terms# and different microcontroller designs ma do things somewhat differentl # but the basics remain the same. Most but not all interrupt re&uests are latched# which means the interrupt event sets a flag that sta s set even if the interrupt event then goes awa . It is this latched flag that actuall generates the interrupt re&uest. %he latched flag ma be cleared in two different wa s. 8irst# it can alwa s be cleared manuall b user code# usuall b writing a 595 to the flag bit position in the associated register. Second# the flag ma be cleared automaticall when the interrupt is responded to and the IS1 begins to run. If this is the case it means that there is no need for the IS1 to explicitl clear the flag# as it will alread be cleared b the time the IS1 starts to run# but check the datasheet for an particular interrupt source to see if this is the case. 0n interrupt re&uest flag that has been set# if the associated interrupt enable is set# is said to be 3pending.3 If the microcontroller global interrupts are enabled# and one or more separate interrupts are enabled# the CP/ hardware will automaticall check all of these separate interrupt flags during the execution of ever instruction. If onl one flag is found set# at the end of the current instruction the CP/ $umps to# or 3vectors to#3 that interrupt5s IS1. In doing so# the interrupt flag ma be automaticall cleared !depends on the design"# the return address !address of the next instruction that was going to be executed" is saved# and perhaps some CP/ flags are saved# and possibl some CP/ registers are switched# and the IS1 begins executing. )ots of perhapses and possibl s# so gotta read the datasheetN If the re&uest flags of more than one enabled interrupt are found set 7 that is# if more than one interrupt is pending 7 things get more complicated. %here will alwa s be a priorit assigned to each interrupt source. %his priorit ma be fixed in the hardware or it ma be configurable b the user program. %he pending interrupt with the highest priorit is the interrupt that will be serviced !that is# who5s IS1 will run". %he other interrupt re&uest flags will sta set 7 that is# the other interrupts will remain in a pending state. What happens next# with an IS1 running and other interrupts pending# also depends on the CP/ design !see a pattern hereU". Some microcontrollers# such as the 0C1# disable global interrupts as part of the interrupt vectoring process. %hus the IS1 begins running with global interrupts disabled and an other pending interrupts 3on hold.3 When the IS1 ends# the global interrupt state will be set as part of the state restoration that happens at the end of an IS1. Setting the global interrupt state here is legitimate since it had to be enabled for the IS1 to have been entered in the first place. So now the next instruction of the interrupted background code will start to run# and the CP/ will recogni:e that there are one or more

=2

pending interrupts# and again the highest priorit pending interrupt will be serviced at the end of that instruction# with an others remaining pending# and so it goes.

Come (ac0 Later " What &appens With An ,nterrupt +e%uest ,f ,nterrupts Are -isab ed?
0s mentioned# interrupt events are &uite often latched. %his means that when the event occurs# a flag is set and that flag remains set until some different action clears it. In particular# the flag remains set even when the setting event goes awa . 8or example# we ma get a low pulse on our external interrupt pin which sets the internal interrupt flag. Even if the pulse then ends !line goes back high" the interrupt re&uest flag will remain set. %his happens whether interrupts are enabled or disabled# either a particular interrupt or global interrupts. %he flag will remain set until it is explicit cleared b code !usuall b writing a 595 to the flag bit position in the associated register"# or until the interrupt is enabled and serviced# at which point either the hardware or the user IS1 will clear the flag. @ne conse&uence of latched interrupt flags is that one can get a burst of interrupts serviced when interrupts are first enabled# if the interrupt conditions had previousl been met. @ne situation where this could happen is with edge7triggered external interrupts# where the triggering edge ma have occurred as a side effect of s stem initiali:ation# or $ust due to fluctuations as all the electronics powers up. 8or reasons such as these it is often a good idea to clear an such interrupt flags before enabling global interrupts# to avoid responding to spurious interrupt re&uests. %he best approach is to make it a general rule to ask ourself if a particular interrupt flag should be cleared $ust before our code enables that interrupt. 8or a latched or edge7triggered interrupt# this drawing shows how the interrupt event sets the latched flag# which then generates an interrupt re&uest when the interrupt is enabled# even if the interrupt event has since gone awa . If this were a level7triggered interrupt# no interrupt re&uest would be generated because the interrupt event has gone awa b the time the interrupt is enabled.

=E

Pi ing )n " What &appens When An ,nterrupt +e%uest Comes ,nside An ,S+?
So far we5ve onl talked about interrupts that interrupt background code. <ut what about an interrupt that tries to interrupt an IS1U 8or example# suppose we have two external interrupts enabled# I6%9 and I6%2. I6%9 has interrupted and the I6%9 IS1 is executing# then along comes an interrupt re&uest on I6%2. What happens nowU Well# one of two things can happen. %he new interrupt ma be blocked until the I6%9 IS1 finishes# at which time it will be serviced. @r# if interrupts have been enabled inside the I6%9 IS1# !or# for some microcontroller designs# if I6%2 has been set to a higher priorit than I6%9" the I6%9 IS1 will be interrupted in $ust the same wa that the background code was interrupted# and the I6%2 IS1 will run and when it is finished the I6%9 IS1 will resume running. %his drawing shows the first behavior# with I6%2 being held off until IS19 finishes. 6otice how IS19 returns to execute one or more background code instructions before I6%2 is responded to. Some processor designs# including the 01M Cortex# have special hardware that avoids that little wasted effort and will run IS1 2 immediatel after IS19 ends# without the short return to background code.

%his drawing shows the second behavior# with I6%2 interrupting IS19. 8or an 0C1 this can happen if IS19 re7enables global interrupts inside of itself. 8or the S%ME2 this can happen if I6%2 is a higher priorit than I6%9.

==

8irst Things 8irst


It is important in writing our IS1 that ou have some sense of what needs to be done right awa # at the start of the IS1# and what can be done later. Zuite often ou5ll want to change one or more DPI@ outputs or registers# and.or read one or more DPI@ inputs or registers# at the ver start of the IS1. %hen ou can finish whatever other housekeeping must be done in the IS1. %his rule depends ver intimatel on what the interrupt source is# and what the desired IS1 action is# and in most cases on what the associated hardware needs. In the simple interrupt7driven )E* program from the last chapter# the )E* output is changed first# and the then next interrupt edge is changed and the current interrupt flag cleared. In the case of a blinking )E* this attention to se&uencing is of course totall unimportant# but it illustrates the concept# and there will be IS1s where it IS important. Wust keep in mind the general principle that ou want to do first things first# and second things after that.

The Scary Side of ,nterrupts


I said in the last chapter that interrupts weren5t magic. I take it back. Interrupts behave exactl like magic# and bad magic at that# if ou don5t reign them in. Interrupts can trash our data is wa s that seem impossible. %he can also refuse to execute code that is plainl written. <ut the 5re so useful 7 necessar # reall 7 that we $ust have to write code that avoids their dangers. Most of the problems with interrupts are related to their chief benefit# which is that running code can be interrupted at almost an time# and the code never knows it was interrupted. It is as if ou can free:e time for the background code# do an thing ou want to the data or I.@# and then restart time for the background code. %o offer an analog # imagine ou were about to sit in a chair. Hou look and see that the chair is available# then ou turn around and start to sit down. %hen time free:es for ou and Scamp the Interrupt moves the chair# or puts a cake on it# and then time begins for ou again. @nl now# as ou5re about to touchdown on what ou thought was a safe chair landing# the world as ou thought ou knew it has changed# and not for the better. %hat5s what badl designed software using interrupts can be like. Hour code can experience things like values becoming corrupted without warning# but onl once ever 9M da s. Dood luck debugging thatN <etter to save our sanit and understand the reasons behind such potential problems# and prevent them before the can happen.

-ata is the Prob em 2or, (ac0ground Code &ates Surprises14


In our interrupt7driven )E* program# we never tried to pass data between the background code and the IS1 !ignoring the settings of the configuration registers". %his is actuall a rare circumstance for an interrupt. Most IS1s will involve passing data to background code and.or retrieving data from background code# and this is where things can go bad. Problems are guaranteed to occur whenever background code is in the middle of accessing data and an interrupt comes along and the associated IS1 then accesses the same data.

=I

0s an example# imagine an s stem where an external trigger generates an interrupt# and in the IS1 a new temperature and pressure value pair is read in from some external sensors. In the background code# some valve positions are calculated based on the current temperature and pressure. 6ow imagine this se&uence in the codeA

<ackground code loads temperature Interrupt comes along# updates temperature and pressure <ackground code loads pressure <ackground code calculates valve positions based on temperature and pressure

What has $ust happened is that the background code has now loaded an old temperature and a new pressure# and will calculate valve positions based on this mismatched data# which is neither valid for the previous measurement nor for the current measurement. It is corrupted data. %he details ma change# but the basic problem is the sameA either the IS1 or the background code thinks it is getting a vaiid set of data# but in realit it gets corrupted data 7 some new# some old. %hings can even get worse than that. Even a single piece of data can be corrupted. Imagine an F7bit microcontroller that receives a 9G7bit data value from an IS1. %o load a 9G7bit data value on an F7bit device# two F7bit loads are re&uired. *epending on the CP/ design# the se&uence ma be to read the lo F bits !least significant b te or )S<" followed b the hi F bits !most significant b te or MS<"# or it ma be MS< followed b )S<. In an case# imagine that the background code has loaded the first F bits !let5s sa the )S<"# then an interrupt comes along and the IS1 writes out a new 9G7bit value to the memor variable. 0t the end of the interrupt# the background code resumes and loads the 2nd F bits !MS<"# but now it has a 9G7bit value built from the )S< of the previous value and the MS< of the new value. %his is neither the old value nor the new value# but some corrupted combination of the two. ,ere is a numeric exampleA

9G7bit variable 4 holds >x92E= <ackground code loads )S< of >xE= Interrupt triggers and IS1 sets 4 to >xE=29# then returns <ackground code loads MS< of >xE= 6ow background code contains an 4 value of >xE=E= !NNN"

%here5s et a third wa that an interrupt can result in corrupted data. 1emember our 2nd )E* <link program that contained the following innocent lineA U 93@RAB B
(1!!3B0)"

%his is what is called a read7modif 7write !1MW" se&uence. What actuall happens is thisA

1ead P@1% value into CP/ Modif the value in CP/ !4@1 with 9 in this case" Write modified CP/ value back out to P@1%

,ere is the actual 0C1 compiler output for the above line of C code !3in3 is a read instruction# 3out3 a write instruction# 3eor3 an exclusive or instruction"A

=G

91 88 24 88

3@RAB B (1!!3B0)" e0 %di 725, 0D01 b3 in 724, 0D18 89 27 eo7 724, 725 bb out 0D18, 724

" 1 " 24

Imagine that the background code has $ust read the P@1% value# which has P<> set to 5>5# and an interrupt occurs# and the interrupt IS1 writes out a new P@1% value setting P<9 to 595. %hen the background code resumes and writes out a modified P@1% value that has set P<> to 595 but has not changed an other bits. %his newl 7written output does not reflect the P@1% output change made in the IS1. @ur P@1% value is now trashed 7 P<9 is 5>5 and it should be 595A

P@1% contains value of >b>> <ackground code reads P@1% Interrupt triggers IS1 which sets P@1% to >b9># then returns <ackground code modifies its value of P@1% to >b>9 and writes it out 6ow P@1% contains >b>9 instead of the correct >b99

%he exact same thing can happen with a 1MW se&uence on a memor location. 0fter all# a P@1% is $ust a form of memor location. So here is et another wa that interrupts can result in corrupted data. 0re ou scared etU Hou should be. %hings ou never would think twice about doing in normal se&uential programming can blow up in our face when interrupts are involved. IS1s must share access to data with background code to be useful# but this ver shared access will clobber ou in multiple wa s if it is not strictl controlled.

-isab ing -angerous ,nterrupt Around Critica Section " A3+ 3ersion
%his code snippet shows $ust the main!" function of our previous 0C1VI6%9 program# but with code to disable and then re7enable the I6%> interrupt around the one7line critical section that performs a 1MW on the output port. %he S%ME2 disable and re7enable would be the same# but using the E4%IVIM1 register. U 9 2 E = I G M F O 9 > 9 9 9 2
int ?&in($oid) ( DDRB (3!!3B0)" (inte77u>t-d7i$en) 3@RAB (3!!3B0)" 84K4R GFHR GF4R sei()" 0b10!!FC400" 1!!FEAH0" 1!!FEA0"

-- 02D out>ut on 3B0 (b%in1ing) &nd 3B1

-- neg&ti$e edge t7igge7 -- 5%e&7 &ny >ending inte77u>t -- en&b%e FEA0 -- en&b%e &%% inte77u>ts

)*i%e(1) ( GF4R I J(1!!FEA0)" -- dis&b%e FEA0 - 5ou%d use 5%i() *e7e inste&d 3@RAB B (1!!3B0)" -- togg%e 3B0 in t*e b&51g7ound - 57iti5&% se5tion+

=M

9 E 9 = 9 I 9 G 9 M 9 F 9 O [

GF4R #

(1!!FEA0)"

-- 7e-en&b%e FEA0 - 5ou%d use sei() *e7e inste&d

de%&y(80000)"

.a0e ,t Stop1
@J# are ou put off from interrupts foreverU *on5t be# there is a solution. What is re&uired in all such cases is to simpl prevent such overlapping accesses to data. %o do this# an code# when beginning to access shared data# must be allowed to have uninterrupted access to that data. In the case where the data is shared b one or more interrupts# this means that those particular interrupts must not be allowed to run while the background code is accessing the shared data. Either the background code or the IS1 must wait its turn for access to the data. Such a safe data access# where there is no possibilit of interruption or corruption# is called an 3atomic access3# impl ing that the access is indivisible. 0n section of code that must have indivisible access to data !or an other resource# for that matter" is called a 3critical section.3 %he heav 7handed but eas wa to implement this solution is to simpl disable all interrupts when shared data is being accessed# and this ma be an ade&uate solution. 0 more well7 behaved solution is to $ust disable the interrupt!s" that would access the shared data in &uestion# while still leaving other interrupts to enabled. 0fter all# an interrupt that can5t corrupt the data being accessed cannot be a threat. 0nother solution is to prevent the sharing of data in the first place. If onl one piece of code !background or IS1" has access to a piece of data# that data can never be corrupted. 8or example# if only background code or the IS1 accessed the output P@1%# the situation above could never occur. Sometimes it is possible to rearrange a program so that sharing is avoided# but this ma $ust mean that some other data now needs to be shared instead. ,ere is our interrupt7driven )E* program for the 0C1 with suitable protection against 1MW corruption of P@1%<. 0ll that is done is to disable the data7sharing interrupt before background code modifies P@1%< and re7enable it immediatel after. %his actuall bu s us nothing over $ust disabling and enabling global interrupts in this simple case !because we have no other interrupts enabled an wa "# but it shows the techni&ue. Specific interrupts or global interrupts# do the anal sis and choose our approach.

=F

&ardware To The +escue


6ow that we5ve $ust seen how ou need to protect ourself from shared access to DPI@ outputs# we have good news. More and more microcontrollers are addressing the problem of atomic access to DPI@ outputs with special hardware. %he S%ME2 is a good example of this. <esides having the standard input and output DPI@ registers# the S%ME2 has two more registers per port. @ne# the port reset register# allows immediate clearing of selected bits without changing an other bits# and the other# the port set register# allows immediate setting of selected bits without changing an other bits. Since these actions are not interruptable there can be no 1MW corruption. <ut it gets even better. %he port set register uses the low 9G bits to identif the port bits to set# but it also uses the high 9G bits to identif port pins to clear in the same instruction. %his means that a single instruction can atomicall set or clear an designated combination of output bits. Cer niceN %his shows that sometimes ou need to move awa from our standard method !4@1ing the output port bit" and take advantage of whatever special goodies our particular uC has to offer.

#sing Chip"Specific &ardware To +emove Critica Section " ST.56 3ersion


%his code snippet also shows $ust the main!" function# this time of the S%ME2VI6%9 program. It adds some logic to use the atomic set and clear output port registers. < doing this we eliminate the critical section entirel # so we do not need to disable and re7enable the interrupt which could corrupt such a critical section. %he 0C1 version could do the same thing 7 eliminate the critical section entirel 7 b using either the write7to7PI67register method of toggling# or using the set7bit and clear7bit instructions !which the 0SG compiler should generate an wa # for single bit changes". It shows that sometimes ou need to move awa from our standard method !4@1ing the output port bit" and take advantage of whatever special goodies our particular uC has to offer. U 9 int ?&in($oid) 2 ( uint8't 6%&g 0" E = R44-=4HGR 0b100 !! 24" -- 9CF, 8 89:, CQC40R-=84@ I G R44-=.3B22ER # R44'.3B22ER'F@3.2E" -- en&b%e 3@RA. 6o7 button in>ut G3F@.-=4R0 (0b0100)" -- 4EH 1, 8@D2 0 (6%o&ting in>ut) M F R44-=.3B22ER # R44'.3B22ER'F@342E" -- en&b%e 3@RA4 6o7 02D out>ut O G3F@4-=4R9 0b0010 # (0b0010 !! 4)" -- 4EH 0, 8@D2 2 (289: out>ut) 9 (348,349) > 9 .HF@-=2LAF4RO0P 0" -- 2LAF0 is 3.0 9 2LAF-=RACR 1" -- 7ising edge, 2LAF0 2LAF-=F8R 1" -- en&b%e 2LAF0 9 E/F4-=FC2RO0P (1 !! 2LAF0'FRMn)" 2 9 =O

E 9 = 9 I 9 G 9 M 9 F 9 O 2 > 2 )*i%e (1) ( 9 i6 (6%&g) 2 ( 2 G3F@4-=BRR 2 6%&g 0" E , e%se 2 ( = G3F@4-=BCRR 2 6%&g 1" I , 2 de%&y(80000)" G , 2 M , 2 F 2 O E > E 9 E 2 E E E =

(1!!9)"

-- &to?i5 5%e&7 3@RA4 bit 9 -- togg%e 6%&g

(1!!9)"

-- &to?i5 set 3@RA4 bit 9 -- togg%e 6%&g

Some microcontrollers# including the 0C1# take a different approach to this problem. %he 0C1 has single instructions that can set or clear an individual bit in certain regions of memor # including the DPI@ ports. 0n good 0C1 compiler should generate those instructions when it sees that a single DPI@ bit is being 06*5ed to > or @15ed to 9. %hus for

I>

setting or clearing a single DPI@ bit# these instructions will do the $ob atomicall even though the high level code seems to impl a 1MW c cle. /nfortunatel # this onl applies to setting or clearing one bit at a time# unlike the S%ME2 capabilit to operate on multiple bits at one time. 6ewer models of the 0C1 also have another atomic trick# which is that b writing 595s to certain PI6 bits of a DPI@ register# those bits of that port which are configured as outputs will toggle. %hat is# b writing 595s to the port input register# an port bits set as outputs will toggle. It5s &uirk # but it can be useful. Man other uC registers# in various uC models# also have a form of atomic access. %his involves the abilit to clear an event or interrupt flag that has been set b hardware without touching an other bits in the register. 6ormall this would re&uire a 1MW c cle to :ero the desired bits# but as we have seen# 1MW can lead to data corruption. %hus# instead# this form of atomic access allows us to write a 595 to an bit we want to clear# and a 5>5 to an other bits !those bits not to be touched". %his works because we generall onl want to clear the bits in &uestion since the are usuall set b the hardware. Hou will run across this 3write 595 to clear a register bit3 paradigm rather often# so be aware of it. 0nd $ust because nothing should ever be eas # some uCs switch things around so that a 5>5 clears the register bit while a 595 does nothing. I can5t sa it enough 7 read the datasheetN

To 3o ati e )r !ot To 3o ati e


Wust when ou thought it was finall safe to use interrupts# here5s a new gotcha. %his is not a fundamental problem# but a warning about how some compilers work. In the case of some compilers# such as avr7gcc used in 0tmel Studio# if a variable is used in both background code and an IS1# that variable must be declared as 5volatile5 or the optimi:er will get overl helpful with the result that writes to the variable seem to not happen. So find out whether our compiler re&uires 5volatile5 for such shared variables# and if it does# post a big note on our monitor until using the ke word becomes second nature.

Timers " (ecause <When< .atters


Computer programs are odd things# for one reason because the have no concept of time. %he may have the concept of se&uential execution# but the time between instructions can be essentiall an number and the program won5t notice or care !unless assumptions about time have been built into the program b the programmer". <ut the real world is not like this. In the real world# especiall the real embedded world# time is alwa s a factor. 0 program has to fire a sparkplug so man microseconds before top dead center. %he oil pressure has to be in the safe range so man seconds after the engine starts. %he digital clock has to know exactl how long consitutes one second. %he sleep mode routine has to know how long since the last user input. %he list could go on indefinitel . <ecause time is so important in the embedded programming world# all microcontrollers have one or more built7in hardware timers# designed to run independentl of the CP/. 0ll timers are fundamentall pulse counters# and sometimes ou will see a timer referred to as a 3timer.counter.3 If the incoming pulses are of a fixed fre&uenc # counting them becomes a timing function. %imers can also be configured to count incoming pulses that are not of a fixed fre&uenc # but are essentiall random# such as e.g. the ticks of a Deiger counter. In that case the timer !which IS a counter an wa " is trul acting as a counter. We will talk about I9

using timers as counters in a future chapter. 8or this chapter# we will $ust discuss timers as timers. We will be considering $ust timers that count up in this chapter# since that is what both the 0C1 and the S%ME2 timers do# but some timers can be configured to count up or down !actuall # some S%ME2 timers can count up or down# but we will $ust use all S%ME2 counters in up7mode for now". Wust keep in mind that what follows assumes timers that are counting up.

&ow .any (its of Time?


%imers are specified b the number of bits their counters are comprised of. %imers are t picall F# 9G or E2 bits# making it eas to read and write them using standard F# 9G or E2 bit variables. It is not unusual to find timers of different si:es on the same microcontroller. 8or example# most 0C1s have both F7bit and 9G7bit timers# and some 64P Cortex ME parts have both 9G7bit and E27bit timers. 0s a reminder# an F7bit timer can count 2TF or 2IG input clocks# a 9G7bit timer 2T9G or GIIEG input clocks and a E27bit timer 2TE2 or over = billion input clocks. 0fter that the timers simpl roll over or 3overflow3 back to > and continue counting. )arger timers !more bits" are more versatile# but the are also more expensive in terms of chip area. With a 9 M,: input clock# an F7bit timer can onl time !without some tricker " a maximum of 2IG microseconds# while a 9G7bit timer can time a maximum of GI#IEG microseconds# and a E27bit timer 7 well# lotsN 8or our 0C1 examples in this chapter we will use timer 9# which is a 9G7bit timer. 8or the S%ME2 examples we will use timer 9# which is a 9G7bit timer with a 9G7bit prescaler.

C oc0 ,n, Tic0s )ut


%he stead stream of pulses that a timer counts is usuall called the timer 3clock.3 %he stead time output signals that a timer can be configured to produce are usuall called 3ticks.3 0 timer clock can be the same as the CP/ !or s stem" clock# or it can be a lower fre&uenc # or in some cases it can even be a higher fre&uenc . 8or now we will onl talk about the case where the timer clock is e&ual to or lower in fre&uenc than the s stem clock. Clock management in more complex microcontrollers can get complicated# but to start with# we5ll $ust imagine that the s stem clock signal !the clock driving the CP/" is what drives the timers. %his clock signal will go into a divider or counter called the 3prescaler.3 %he output of the prescaler is the clock input to the timer. < changing the number b which the prescaler divides the incoming clock# we can change the fre&uenc of the timer clock even though the s stem clock fre&uenc remains the same. %he abilit to change the timer clock fre&uenc lets us choose the most suitable fre&uenc for the timing $ob!s" at hand. 0 faster timer clock gives higher time resolution but a shorter maximum time# while a slower timer clock gives lower time resolution but a longer maximum time.

I2

Some prescalers are limited to $ust power7of72 divisions of the s stem clock# so ou can get 8 !the s stem clock" or 8.2 or 8.= or 8.F or 8.9G or ... to some maximum division 2T6. Some prescalers are even more limited than this# in that onl certain power7of72 divisions are available# not all of them. %he 0C1 prescalers are like this. 8or most 0C1s# the onl prescaler divisions ou get are 9# F# G=# 2IG and 9>2=. @ther prescalers are full7blown configurable counters that can divide the s stem clock b an integer value. %he S%ME2 prescalers are like this. %he are 9G7bit prescalers so the s stem clock can be divided b an value between 9 and GIIEG. Such prescalers are much nicer to work with due to their greater flexibilit # but are not a must7have.

Simp e Timer Tric0s


,ere are the main things we can do with a simple timerA

Select prescaler value Write timer register 1ead timer register Start and Stop timer *etect if timer overflows !goes from M04 count 2T679 to >"

%hese should all be prett self7explanator . It should be pointed out that when a timer overflows it sets a flag# and this flag can be inspected directl in code# or it can be used to generate an interrupt. %hus even a simple timer can generate a stead interrupt 3tick#3 useful for monitoring the passage of time in the user program. 0 timer tick IS1 is a fundamental building block for man # perhaps most# embedded s stems. 0 timer tick on the order of 9>ms is useful for a variet of tasks# since it provides good responsiveness for human actions while still allowing most of the CP/ c cles to go towards other work. 0 tick in this range could be achieved on a 9M,: 0C1# for example# b using an F7bit timer and a prescale value of G=# giving a timer overflow period of 2IG\G= or 9G#EF= microseconds 7 about 9G milliseconds# plent close enough to the 9>ms 3standard3 to do the $ob. %his is about the simplest timer interrupt ou can get# and especiall with a more configurable prescaler# it ma be all ou need for man applications. 0nother useful thing one can do with such a simple timer is to use the overflow interrupt to write a new starting value into the timer !the overflow itself will have set the timer to >". 8or instance# taking our example above# if on ever overflow we write a value of 9>> into the timer# now instead of having 2IG timer clocks between each overflow# we have 9IG timer clocks 7 the timer now ticks from 9>> to 2II# not from > to 2II. With a 9M,: s stem clock and a prescale of G=# we now have a timer overflow period of 9IG\G=us or OOF=us 7 much closer to our target of 9>ms# if we reall determined we needed to get as close as possible to that target. @r# perhaps using a more real7world consideration# we ma want to generate a tick fre&uenc that is an exact integer. %his will let us count ticks to get exactl 9 second time intervals# ver useful for creating and displa ing human7friendl time values. *oing a bit of math with our 9M,: clock and G= prescaler# we have 9#>>>#>>>.G= P 9IG2I# which is 92I\92I. If we set our timer to count 92I prescaled clocks that will give us an Fms tick !92I,: tick rate# an exact integer"# and if we count 92I of these ticks in software we get IE

exactl 9 second. %hus we would reload our timer with 2IG792IP9E9 to get our Fms tick. %he reason we need a tick fre&uenc that is an exact integer is that we can onl count b exact integers 7 we can5t count e.g. 92I.MI ticksN

There7s A ways A Catch


%his reloading techni&ue is not foolproof. 1emember that an IS1 does not run immediatel when an interrupt re&uest is generated. 8urther# if other interrupts are enabled# or if global interrupts are disabled at points during the program execution# the timer interrupt response ma be further dela ed. %hen there is the hidden state7saving code at the beginning of the IS1 which executes before an user IS1 code executes. 0ll of this adds up to the realit that it takes a while for the user IS1 code to execute after a timer interupt# and that time ma var from interrupt to interrupt. 0ll the while# the timer is running. %he timer has rolled over to > when the overflow interrupt re&uest is generated# but is it still at > when the user IS1 code finall reloads the timerU If the prescale value is low !the timer is ticking up ever CP/ instruction# or ever few instructions"# the timer ma have ticked ahead to 9 or 2 or I or who knows. Imagine that we are tr ing to generate a timer interval of 9>> timer clocks# b loading 9IG into our timer in the IS1. 0lso imagine that our timer has counted up to I before our IS1 code runs to reload the timer. %his means that instead of our next interval being 9>> timer clocks# it will be 9>I timer clocks 7 the 9>> we get b reloading the timer# and the I that ticked off before we got around to reloading the timer. 9>I ma be close to 9>># but often close is not enough. It turns out that we can overcome most# but not all# of this problem b subtracting the current timer value from our interval value. %his actuall involves adding the current timer value to our reload value# since a higher reload value results in a shorter interval for an up7counting timer. /sing our example numbers from above# if the timer was at I when we reloaded it# we would reload with a value of 9IG?IP9G9# giving us OI more timer clocks to overflow in addition to the I that have alread occurred. %his will reduce the cumulative timing error using the timer reload techni&ue# but it will not eliminate it# because the timer can alwa s tick forward while we5re doing this addition and assignment. )uck for us# there is a better wa . <ut first# let5s blink our )E* with a timer and see how it gives us back all those CP/ c cles we wasted in using software dela s.

( in0ing An LE- With A Timer


%his program will actuall blink two )E*s. %he first will be our old friend the software dela blinking )E*. %he second will be a timer7driven blinking )E*. Hou will see how the first approach uses literall millions of clock c cles for one )E* blink !most wasted in the dela function"# while the timer7driven approach uses a few do:en c cles to achieve the same result !and with more precisionN".

I=

LE- ( in0 #sing )verf ow " A3+ 3ersion


,ere we set up our 9G7bit timer to generate a 2I>ms overflow with interrupt. Since we are sharing the P@1%< resource# we have to protect P@1%< when we access it in the background loop b disabling and then re7enabling interrupts. 0lso note that we are getting awa from magic numbers like E92I> and using the relevant e&uation directl to determine the timer reload value. Since the number is a constant it will be calculated at compile time and does not have an runtime cost. U 9 2 E = I G M F O 9 > 9 9 9 2 9 E 9 = 9 I 9 G 9 M 9 F 9 O 2 > 2 9 2 2 2 E 2 = 2
----./R'AF81 togg%e 3B0 02D )it* so6t)&7e de%&y togg%e 3B1 02D in o$e76%o) FCR ./R 5%o51 is 889:

;in5%ude !&$7-io<*= ;in5%ude !&$7-inte77u>t<*= ;de6ine ;de6ine ;de6ine ;de6ine H'43K 3R2C4.02 32RF@D A1'40RC 8000000K0 -- 189: 64 250 -- ?i%%ise5onds - 1-4 se5ond de%&y ((H'43K-3R2C4.02S32RF@D)-1000) -- 1000?s >e7 s

FCR(AF82R1'@/H'$e5t) ( A4EA1 T (65536K0-A1'40RC)" 3@RAB B (1!!3B1)" , $oid de%&y($o%&ti%e uint32't d) ( )*i%e (d-- + 0) " , int ?&in($oid) ( DDRB 3" AF8CR (1!!A@F21)" A44R1. 0" A44R1B 3!!4C10" A4EA1 T (65536K0-A1'40RC)" sei()" )*i%e(1) ( 5%i()" 3@RAB B sei()"

-- 5ount u> 31250 to o$e76%o) -- togg%e 3B1

-- 3B0, 3B1 out>uts ----en&b%e o$e76%o) inte77u>t no7?&% ?ode -64 >7es5&%e7 initi&% %o&d

(1!!3B0)"

-- togg%e 3B0

de%&y(80000)" , ,

II

I 2 G 2 M 2 F 2 O E > E 9 E 2 E E E = E I E G E M E F E O = > = 9 = 2 = E = = = I

LE- ( in0 #sing )verf ow " ST.56 3ersion


Cer similar to the 0C1 version. 0ll the S%ME2 timer interrupt flags must be reset in their corresponding IS1s b writing a > to the flag. U IG

9 2 E = I G M F O 9 > 9 9 9 2 9 E 9 = 9 I 9 G 9 M 9 F 9 O 2 > 2 9 2 2 2 E 2 = 2 I 2 G 2 M 2 F 2 O E

-----

CA832'AF81 togg%e 02D on 349 using so6t)&7e de%&y togg%e 02D on 348 using ti?e7 1 )it* FCR 7e%o&d De6&u%t 8 89: inte7n&% 5%o51

;in5%ude !st?32610D<*= ;de6ine ;de6ine ;de6ine ;de6ine H'43K 3R2C4.02 32RF@D A40RC 8000000K0 -- 189: 64 250 -- ?i57ose5onds - 1-4 se5ond de%&y ((H'43K-3R2C4.02S32RF@D)-1000)

$oid AF81'K3'AF816'FRM9&nd%e7($oid) ( AF81-=4EA T (65536K0-A40RC)" -- 7e%o&d G3F@4-=@DR B (1!!8)" -- togg%e b%ue 02D AF81-=CR JAF8'CR'KFH" -- 5%e&7 u>d&te int 6%&g ()7ite 0) , $oid de%&y($o%&ti%e uint32't d) ( )*i%e (d-- + 0) " , int ?&in($oid) ( R44-=4HGR 0" R44-=.3B22ER #

-- 9CF, 8 89:, R44'.3B22ER'F@3.2E" -- en&b%e 3@RA.

R44-=.3B22ER # R44'.3B22ER'F@342E" -- en&b%e 3@RA4 G3F@4-=4R9 0b0010 # (0b0010 !! 4)"-- 4EH 0, 8@D2 2 (289: out>ut) 348,349 R44-=.3B22ER # R44'.3B22ER'AF812E" -- en&b%e Ai?e71

AF81-=3C4 3R2C4.02 - 1" AF81-=DF2R AF8'DF2R'KF2" AF81-=4EA T (65536K0-A40RC)" AF81-=4R1 AF8'4R1'42E" E/F4-=FC2RO0P )*i%e (1) ( AF81-=DF2R I G3F@4-=@DR B AF81-=DF2R # de%&y(80000)"

-- en&b%e u>d&te inte77u>t -- initi&% %o&d -- en&b%e ti?e7 -- en&b%e AF81 int in E/F4

(1 !! AF81'K3'AF816'FRMn)"

JAF8'DF2R'KF2" (1!!9)" AF8'DF2R'KF2"

-- dis&b%e u>d&te inte77u>t -- togg%e g7een 02D -- 7e-en&b%e u>d&te inte77u>t

, ,

IM

> E 9 E 2 E E E = E I E G E M E F E O = > = 9 = 2 = E = = = I = G = M = F = O I > I 9 I 2

IF

)ops " Another Shared +esource, Another Source of (ugs1


1emember all that nast interrupt7related corruption we learned aboutU Much the same kind of thing can happen with certain timer registers. 1emember# the timer register keeps on timing !counting up or sometimes down" while user code is running. Hou can think of the timer register as a data variable# which can be accessed b two different independent processes 7 the timer hardware# and the user code. @nce again we have a potential for data corruption. Imagine an F7bit device like the 0C1 reading the 9G7bit timer registerA

%imer register contains >x>>88 /ser code reads low b te of timer register !>x88" %imer ticks# now timer register contains >x>9>> /ser code reads high b te of timer register !>x>9"

6ow the user code has a corrupt timer register value of >x>988# because the timer hardware essentiall 3interrupted3 the 27stage timer register load process and changed the timer register data between those two stages. 1ats and radishesN %he same thing can also happen when reading an input capture register !suppose a new input capture event occurs during the 27stage capture register load"# and a similar problem can happen when updating a compare register in two stages. 0ll the problems in this case are caused b the fact that the uC must access d namicall changing registers using multi7stage accesses. If the uC can access those registers in a single access !F7bit timer for F7bit uC# E27bit timer for E27bit uC"# the problem does not exist. %he 0C1 designers understood this and provided a fix. 8or all 9G7bit hardware registers where such corruption can occur# the 0C1 hardware has a special trick. When reading e.g. the timer register# a read of the low b te of the register actuall reads both b tes at the same time !does a full 9G7bit atomic transfer from the hardware register# thus preventing an corruption of the read data". %he other# high# b te is saved in a temporar register# and when the user code reads the high b te address# it is the value in the temp register that is returned# not the actual high b te of the timer register !which ma have changed since reading the low b te". In the same manner# writing to the high b te of such a register saves the data in a temp register# and then writing to the low b te causes a full 9G7bit atomic transfer into the hardware register# using the written low b te and the high b te saved in the temp register. 0 good 0C1 compiler will know this se&uence and appl it automaticall . When programming in 0SM# or otherwise working with the register as 2 b tes rather than as a 9G7 bit value# it is up to ou to remember the se&uenceA read low then high# write high then low.

/etting 8ancy
1eal timers can do much more than what we have $ust discussed. %o add these capabilities the come with additional t pes of registers. @ne t pe of register that $ust about all timers will have is called a 3match3 or 3compare3 register. 0s the timer is counting up# when its value e&uals the value in a compare register another flag# the match or compare flag# is set# different than the overflow flag. Wust like the overflow flag# this flag can be inspected with code# or it can be used to generate an interrupt. 0nd# ver importantl # the chip designers can include as man compare registers as the want in a single timer# all acting independentl . In IO

addition# compare registers can generall be mapped to an output pin so that compare matches can set# clear or toggle the output. %his functionalit will be discussed more in a future PWM chapter. %he other t pe of register generall found is an 3input capture3 register. When an external input or other signal goes active# the value of the timer at that instant is saved in the input capture register. %his lets us take a snapshot of the timer value based on some event while the timer continues to run. 0gain# the chip designer can include as man capture registers as the want in a single timer# each with a separate capture trigger signal. It should be clear that all compare and capture registers are the same si:e !same number of bits" as the timer register itself. 0 F7bit timer will have F7bit compare and capture registers# and so forth. It should also be pointed out that in the S%ME2 in particular# the same register can be configured to be either a compare register or an input capture register.

The "= +u e
When loading timer registers with timing values# it is critical to understand how a timer compare works 7 that is# how a value in a timer !or prescaler" register is compared to the running timer !or prescaler" count. 8or a compare value of 6# the compare event onl takes place at the next timer clock after the timer has reached 6. %hus for example# for 6PE# if ou are using the compare event to reset the timer ou will have the following se&uenceA ># 9# 2# E# !compare happens here" ># 9# 2# E# !compare happens here"... 6ote that the timer actuall is counting = steps before resetting to > and starting over. %his is $ust the wa counters work# including the counters in timers. %he can5t count up to the next value and also do a compare for that next value on the same clock. 0lwa s read the datasheet# of course# but in general to count 6 timer clocks ou need to load the compare register with the value of 679. %hat is the 379 1ule3. %his applies to an user7configurable prescaler compare registers too. 8or example# to get a prescale of G= on the S%ME2 devices# we must load a value of GE into the timer prescale register.

LE- ( in0 #sing Compare +egister " A3+ 3ersion


%his time we set up the 0C1 timer for earl rollover using a compare register !0C1 calls this C%C mode". 6ow our interrupt is based on the compare event rather than the rollover event# and in the IS1 all we have to do is toggle the )E* 7 no timer fiddling is re&uired. U 9 2 E = I G
----./R'AF82 togg%e 3B0 02D )it* so6t)&7e de%&y togg%e 3B1 02D in 5o?>&7e (4A4) FCR ./R 5%o51 is 889:

;in5%ude !&$7-io<*= ;in5%ude !&$7-inte77u>t<*=

G>

M F O 9 > 9 9 9 2 9 E 9 = 9 I 9 G 9 M 9 F 9 O 2 > 2 9 2 2 2 E 2 = 2 I 2 G 2 M 2 F 2 O E > E 9 E 2 E

;de6ine ;de6ine ;de6ine ;de6ine

H'43K 3R2C4.02 32RF@D A1'40RC

8000000K0 -- 189: 64 250 -- ?i%%ise5onds - 1-4 se5ond de%&y ((H'43K-3R2C4.02S32RF@D)-1000)

FCR(AF82R1'4@83.'$e5t) ( 3@RAB B (1!!3B1)" , $oid de%&y($o%&ti%e uint32't d) ( )*i%e (d-- + 0) " , int ?&in($oid) ( DDRB 3" @4R1. AF8CR A44R1. A44R1B sei()" )*i%e(1) ( 5%i()" 3@RAB B sei()" A1'40RC-1" (1!!@4F21.)" 0" (1!!UG812) # (3!!4C10)"

-- togg%e 3B1

-- 3B0, 3B1 out>uts ----?ust %o&d )it* 5ount-1 en&b%e 5o?>&7e inte77u>t 4A4 ?ode 4A4 ?ode, -64 >7es5&%e7

(1!!3B0)"

, ,

de%&y(80000)"

G9

E E = E I E G E M E F E O = > = 9 = 2 = E = =

LE- ( in0 #sing Compare +egister " ST.56 3ersion


8or the S%ME2 we must use a special7purpose compare register# called the auto7reload register# to roll the timer over to >. 0lso# for the S%ME2# our interrupt source is not a compare but an overflow# as in the first S%ME2 timer program. %hat5s $ust the wa the S%ME2 timers work 7 not better or worse# $ust different. U 9 2 E = I G M F O 9 > 9 9 9 2 9
----CA832'AF82 togg%e 02D on 349 using so6t)&7e de%&y togg%e 02D on 348 using ti?e7 1 )it* &uto 7e%o&d De6&u%t 8 89: inte7n&% 5%o51

;in5%ude !st?32610D<*= ;de6ine ;de6ine ;de6ine ;de6ine H'43K 3R2C4.02 32RF@D A40RC 8000000K0 -- 189: 64 250 -- ?i%%ise5onds - 1-4 se5ond de%&y ((H'43K-3R2C4.02S32RF@D)-1000)

$oid AF81'K3'AF816'FRM9&nd%e7($oid) ( G3F@4-=@DR B (1!!8)" -- togg%e b%ue 02D AF81-=CR JAF8'CR'KFH" -- 5%e&7 u>d&te int 6%&g ()7ite 0) , $oid de%&y($o%&ti%e uint32't d)

G2

E 9 = 9 I 9 G 9 M 9 F 9 O 2 > 2 9 2 2 2 E 2 = 2 I 2 G 2 M 2 F 2 O E > E 9 E 2 E E E = E I E G E M E

( ,

)*i%e (d-- + "

0)

int ?&in($oid) ( R44-=4HGR 0" R44-=.3B22ER #

-- 9CF, 8 89:, R44'.3B22ER'F@3.2E" -- en&b%e 3@RA.

R44-=.3B22ER # R44'.3B22ER'F@342E" -- en&b%e 3@RA4 G3F@4-=4R9 0b0010 # (0b0010 !! 4)" -- 4EH 0, 8@D2 2 (289: out>ut) 348,349 R44-=.3B22ER # R44'.3B22ER'AF812E" -- en&b%e Ai?e71

AF81-=3C4 3R2C4.02 - 1" AF81-=DF2R AF8'DF2R'KF2" -- en&b%e o$e76%o) inte77u>t AF81-=448R1 0" -- 5*&n 1 is out>ut AF81-=.RR A40RC - 1" AF81-=4R1 AF8'4R1'42E" -- en&b%e ti?e7 E/F4-=FC2RO0P )*i%e (1) ( AF81-=DF2R I G3F@4-=@DR B AF81-=DF2R # de%&y(80000)" , , (1 !! AF81'K3'AF816'FRMn)" -- en&b%e AF81 int in E/F4

JAF8'DF2R'KF2" (1!!9)" AF8'DF2R'KF2"

-- dis&b%e u>d&te inte77u>t -- togg%e g7een 02D -- 7e-en&b%e u>d&te inte77u>t

GE

F E O = > = 9 = 2 = E = = = I = G = M = F = O I > I 9 I 2

.ore About Compare +egisters


@ne of the things that a compare register can be configured to do is to cause the timer to roll over to :ero upon a match. In some cases onl one special compare register can cause such rollover 7 the S%ME2 has such registers and calls them 3auto7rollover3 registers. %his 3earl rollover3 lets us create a timer of an length or period less than the maximum timer period# but one that does not suffer the weakness of the timer reload method discussed above. When we have a compare register rollover# the timer continues to run from > regardless of how long it takes an compare IS1 code to run. %he timer neither knows nor cares about how long it takes our IS1 to run. It simpl goes on generating exact time intervals and interrupts. Even if one timer IS1 entr is dela ed due to other IS1s running or global interrupts being disabled for a time# the next one will happen on time. %here will never be an long7term slippage of the timer interrupts# which is something we cannot guarantee with the earlier timer reload techni&ue. 6ow we finall have an ad$ustable source of completel stable timer ticks# thanks to the compare register. Instead of configuring a timer to roll over on a compare# we can also $ust generate a compare interrupt while letting the timer continue to run past the compare value. @ne interesting and G=

useful thing we can do with compare registers in such a case is to reload them in the compare IS1. In this case it is crucial to not enable the compare rollover option as we did above# but to let the timer run its full natural period of 2TF# 2T9G or 2TE2 timer clocks# and then overflow. In the compare IS1# we simpl add our desired interval value to the current compare register value and that becomes our next compare register value. It5s as simple as thisA U 95%o51s &6te7 t*is one
4o?>&7e'7eg T AF82'FEA2R/.0" -- neDt 5o?>&7e )i%% *&>>en AF82'FEA2R/.0

6ote that it does not matter how man counts the timer has advanced since the last compare !as long as it is less than %IMEVI6%E1C0)N". Even if this compare IS1 runs late for whatever reason# the next one will be back on schedule. %his techni&ue !sometimes called 3leapfrogging3 for rather obvious reasons" gives exactl the same rock7solid timing as the :ero7on7compare method# but it has even more versatilit because one can use multiple compare registers to generate multiple independent timing events# such asA U 9 2 E = I G M F O 9 > 9 9

FCR(4@831.'$e5t) ( @4R1. T 3000" <<< , FCR(4@831B'$e5t) ( @4R1B T 725" <<< ,

-- t*is FCR 7uns on5e e$e7y 3000 ti?e7 ti51s

-- )*i%e t*is FCR 7uns on5e e$e7y 725 ti51s

%his generates two unrelated but stable timing loops in a ver simple wa .

LE- ( in0 #sing Leapfrogging " A3+ 3ersion


%his time we have two compare interrupts and each IS1 advances !leapfrogs" the associated compare register and also sets a flag. <ackground code checks for both flags and toggles the appropriate )E* when it sees a flag set. 6ote that P@1%< is no longer shared between background code and IS1# so no disabling and re7enabling of interrupts is necessar . 0lso note that the background code is responsible for clearing an flags set b IS1s. U 9 2 E =
----./R'AF83 togg%e 3B0 02D )it* one %e&>67og de%&y togg%e 3B1 02D )it* & di66e7ent %e&>67og de%&y ./R 5%o51 is 889:

GI

I G M F O 9 > 9 9 9 2 9 E 9 = 9 I 9 G 9 M 9 F 9 O 2 > 2 9 2 2 2 E 2 = 2 I 2 G 2 M 2 F 2 O E > E 9 E

;in5%ude !&$7-io<*= ;in5%ude !&$7-inte77u>t<*= ;de6ine ;de6ine ;de6ine ;de6ine H'43K 3R2C4.02 32RF@D A1'40RC 8000000K0 -- 189: 64 250 -- ?i%%ise5onds - 1-4 se5ond de%&y ((H'43K-3R2C4.02S32RF@D)-1000) 0" 0"

$o%&ti%e uint8't 6%&g0 $o%&ti%e uint8't 6%&g1 FCR(AF82R1'4@83.'$e5t) ( 6%&g0 1" @4R1. T A1'40RC" ,

FCR(AF82R1'4@83B'$e5t) ( 6%&g1 1" @4R1B T (A1'40RC T 1000)" , int ?&in($oid) ( DDRB 3" @4R1. @4R1B AF8CR A44R1. A44R1B sei()" )*i%e(1) ( i6 (6%&g0) ( 3@RAB B (1!!3B0)" 6%&g0 0" , i6 (6%&g1) ( 3@RAB B (1!!3B1)" 6%&g1 0" , , ,

-- s%ig*t%y %onge7 de%&y t*&n 6%&g0

-- 3B0, 3B1 out>ut -- ?ust %o&d )it* 5ount-1 -- en&b%e o$e76%o) inte77u>ts -- no7?&% ?ode -- no7?&% ?ode, -64 >7es5&%e7

31250" 32000" (1!!@4F21.) # (1!!@4F21B)" 0" (3!!4C10)"

GG

2 E E E = E I E G E M E F E O = > = 9 = 2 = E = = = I = G = M = F = O I > I 9 I 2 I E I =

GM

LE- ( in0 #sing Leapfrogging " ST.56 3ersion


Similar to the 0C1 version# using two compare registers and a free7running timer# but both compare registers vector through the same interrupt# so in the IS1 a test must be made to determine which compare has generated the interrupt. U 9 2 E = I G M F O 9 > 9 9 9 2 9 E 9 = 9 I 9 G 9 M 9 F 9 O 2 > 2 9 2 2 2 E 2 = 2 I 2 G
----CA832'AF83 togg%e 02D on 349 using %e&>67og de%&y togg%e 02D on 348 using di66e7ent %e&>67og de%&y De6&u%t 8 89: inte7n&% 5%o51

;in5%ude !st?32610D<*= ;de6ine ;de6ine ;de6ine ;de6ine H'43K 3R2C4.02 32RF@D A40RC 8000000K0 -- 189: 64 250 -- ?i57ose5onds - 1-4 se5ond de%&y ((H'43K-3R2C4.02S32RF@D)-1000) 0" 0"

$o%&ti%e uint8't 6%&g1 $o%&ti%e uint8't 6%&g2

$oid AF81'44'FRM9&nd%e7($oid) ( i6 (AF81-=CR I AF8'CR'441FH) ( 6%&g1 1" AF81-=44R1 T A40RC" AF81-=CR JAF8'CR'441FH" , i6 (AF81-=CR I AF8'CR'442FH) ( 6%&g2 1" AF81-=44R2 T (A40RCT1000)" AF81-=CR JAF8'CR'442FH" , , int ?&in($oid) ( R44-=4HGR 0" R44-=.3B22ER #

-- 441 ?&t5*V -- %e&>67og to neDt inte77u>t ti?e -- 5%e&7 441 int 6%&g ()7ite 0) -- 442 ?&t5*V -- %e&>67og to neDt inte77u>t ti?e -- 5%e&7 442 int 6%&g ()7ite 0)

-- 9CF, 8 89:, R44'.3B22ER'F@3.2E" -- en&b%e 3@RA.

R44-=.3B22ER # R44'.3B22ER'F@342E" -- en&b%e 3@RA4 G3F@4-=4R9 0b0010 # (0b0010 !! 4)" -- 4EH 0, 8@D2 2 (289: out>ut) 348,349 R44-=.3B22ER # R44'.3B22ER'AF812E" -- en&b%e Ai?e71

AF81-=3C4 3R2C4.02 - 1" AF81-=DF2R AF8'DF2R'442F2 # AF8'DF2R'441F2" -- en&b%e 2 44 inte77u>t 5*&nne%s AF81-=448R1 0" -- 5*&n 1 is out>ut AF81-=4R1 AF8'4R1'42E" -- en&b%e ti?e7 E/F4-=FC2RO0P (1 !! AF81'44'FRMn)" -- en&b%e AF81 int in E/F4

GF

2 )*i%e (1) M ( 2 i6 (6%&g1) F ( 2 G3F@4-=@DR B 6%&g1 0" O , E i6 (6%&g2) > ( E G3F@4-=@DR B 9 6%&g2 0" , E , 2 , E E E = E I E G E M E F E O = > = 9 = 2 = E = = = I = G = M = F = O I > I 9

(1!!9)"

-- togg%e g7een 02D

(1!!8)"

-- togg%e b%ue 02D

GO

I 2 I E I = I I I G I M I F I O G > G 9 G 2 G E

(ut Sure y That Can7t Wor01


6eat# but ou5re concerned about what happens when the timer or the compare register addition rolls over# rightU Hou should be. 0ssuming a 9G7bit timer and 9G7bit @C19 registers# ou can5t $ust keep adding that E>>> to a 9G7bit register forever without an overflow. When we get up to GE#>>> !adding E>>> 29 times" and then add E>>> again# we go to our hand calculator program and see >x8G9F !PGE#>>>" ? >x<<F !PE>>>" P >x9>9*># or# with onl 9G bits to hold the result# >x9*> !P=G=". %he timer register overflowed# now whatU 6ow we get luck # or so it seems at first glance. Consider how man more counts from GE#>>> it will take for the timer to overflowA GI#IEG 7 GE#>>> P 2IEG# so we will have 2IEG more counts# then the timer will roll over# then# if we have loaded =G= into our compare register !the result of the overflowed addition of E>>>"# we will have another =G= counts before our compare match. <ut 2IEG?=G= P E>>># exactl the interval we wanted. We get this fortuitous result because the binar math involved in the 9G7bit timer overflow is exactl the same binar math involved in our adding E>>> to GE#>>> in a 9G7bit compare register. 1emember# a timer is $ust a counter# so our timer register is simpl adding 9 to itself E>>> times# which gives exactl the same result as adding E>>> to our compare register 9 time. In both cases# the overflow $ust takes care of itself. I had a real 3I5ll be darnedN3 moment when I first understood that. We have shown fixed intervals in all of the examples so far# but of course the intervals can be variable# or can be an assortment of fixed intervals# not $ust one. 0ll that matters is that the new compare register value must be 3further out3 than the actual timer register. If ou $ust M>

had a compare interrupt at timerP9>># and ou foolishl wrote a ver long IS1# and now the timer is at 2E= b the time our IS1 gets around to loading the compare register with a new value of 2>># well# ou5re hosed. 0t the ver least# ou need to load our compare register with a value further out than the timer will be when the IS1 finishes. ,ence# write short IS1sN

Counting Tic0s
We5ll end up this chapter on timers b setting up a timer to generate 9>ms ticks# and using those ticks to blink our two )E*s. What we are doing now# in essence# is extending the hardware counting found in the timer and its prescaler into software. %hat is# we are now doing some of our timer clock counting in the timer hardware# and the rest of it in software. %his lets us extend our timing capabilities to an possible interval ou would ever need. ,aving an timer7driven s stem tick is such a common re&uirement that all 01M Cortex parts have# as part of the 01M spec# a separate 2=7bit s stem tick timer. %his is the timer one would generall use for creating a s stem tick# but to sta in line with the other examples above we will continue to use the S%ME2 %imer 9. @ur programs will be similar to our Compare R Clear programs# but this time we will set up the timer for a 9>ms tick# and on ever tick set a flag to signal the background code. %he background code will spend $ust about all of its time watching for that flag# and when it finds the flag set it will count another tick. Ever 2= !or 2I" of those ticks the background code will toggle one !or the other" )E*# giving us two blinking )E*s with slightl different blink fre&uencies. %his sharing of work between IS1 and background code is a ver common paradigm# which is wh we5re introducing it now instead of e.g. $ust counting ticks inside the IS1 and toggling the )E*s inside the IS1# which might seem the more obvious approach. 6ote also that b using an F7bit flag# set in the IS1 and cleared in background code but never involved in a 1MW se&uence# there is no possibilit of flag corruption. Hou want to make it a habit to confirm lack of corruption with an such shared data.

LE- ( in0 #sing Tic0s and Counting " A3+ 3ersion


%his time the timer is set up to deliver 9>ms ticks to the background code. %he background code maintains two tick counters# one for each )E*# and toggles the appropriate )E* when its counter reaches a specified value. 0gain we have no P@1%< sharing in this method# so no need to worr about possible P@1%< corruption. U 9 2 E = I G M
----./R'AF84 togg%e 3B0 02D &6te7 24 10?s ti?e7 ti51s togg%e 3B1 02D &6te7 25 10?s ti?e7 ti51s ./R 5%o51 is 889:

;in5%ude !&$7-io<*= ;in5%ude !&$7-inte77u>t<*=

M9

F O 9 > 9 9 9 2 9 E 9 = 9 I 9 G 9 M 9 F 9 O 2 > 2 9 2 2 2 E 2 = 2 I 2 G 2 M 2 F 2 O E > E 9 E 2 E E

;de6ine ;de6ine ;de6ine ;de6ine

H'43K 3R2C4.02 32RF@D A1'40RC

8000000K0 -- 189: 64 250 -- ?i%%ise5onds - 1-4 se5ond de%&y ((H'43K-3R2C4.02S32RF@D)-1000) 0"

$o%&ti%e uint8't ti51 FCR(AF82R1'4@83.'$e5t) ( ti51 1" , int ?&in($oid) ( uint8't ti51'5nt0 uint8't ti51'5nt1 DDRB @4R1. AF8CR A44R1. A44R1B sei()" 3"

0" 0" -- 3B0,3B1 out>ut ----?ust %o&d )it* 5ount-1 en&b%e o$e76%o) inte77u>t 4A4 ?ode 4A4 ?ode, -64 >7es5&%e7

1250-1" (1!!@4F21.)" 0" (1!!UG812) # (3!!4C10)"

)*i%e(1) ( i6 (ti51) ( i6 (TTti51'5nt0 = 24) ( 3@RAB B (1!!3B0)" ti51'5nt0 0" , i6 (TTti51'5nt1 = 25) ( 3@RAB B (1!!3B1)" ti51'5nt1 0" , ti51 0" , , ,

-- 240?s

-- 250?s

M2

E = E I E G E M E F E O = > = 9 = 2 = E = = = I = G = M = F = O I > I 9 I 2

LE- ( in0 #sing Tic0s and Counting " ST.56 3ersion


0gain this S%ME2 program is ver similar to the 0C1 version. U 9 2 E =
----CA832'AF84 B%in1 02D on 349 using 24 10?s ti?e7 ti51s B%in1 02D on 348 using 25 10?s ti?e7 ti51s De6&u%t 8 89: inte7n&% 5%o51

ME

I G M F O 9 > 9 9 9 2 9 E 9 = 9 I 9 G 9 M 9 F 9 O 2 > 2 9 2 2 2 E 2 = 2 I 2 G 2 M 2 F 2 O E > E 9 E

;in5%ude !st?32610D<*= ;de6ine H'43K ;de6ine 3R2C4.02 ;de6ine AF4R'40RC $o%&ti%e uint8't ti51 8000000K0 -- 189: 64 ((H'43K-3R2C4.02S10)-1000) 0"

-- 10?s

$oid AF81'K3'AF816'FRM9&nd%e7($oid) ( ti51 1" AF81-=CR JAF8'CR'KFH" -- 5%e&7 u>d&te int 6%&g ()7ite 0) , int ?&in($oid) ( uint8't ti51'5nt1 uint8't ti51'5nt2 R44-=4HGR 0"

0" 0" -- 9CF, 8 89:,

R44-=.3B22ER # R44'.3B22ER'F@342E" -- en&b%e 3@RA4 G3F@4-=4R9 0b0010 # (0b0010 !! 4)" -- 4EH 0, 8@D2 2 (289: out>ut) 348,349 R44-=.3B22ER # R44'.3B22ER'AF812E" -- en&b%e Ai?e71

AF81-=3C4 3R2C4.02 - 1" AF81-=DF2R AF8'DF2R'KF2" AF81-=448R1 0" AF81-=.RR AF4R'40RC - 1" AF81-=4R1 AF8'4R1'42E" E/F4-=FC2RO0P

-- en&b%e o$e76%o) inte77u>t -- 5*&n 1 is out>ut -- en&b%e ti?e7

(1 !! AF81'K3'AF816'FRMn)" -- en&b%e AF81 int in E/F4

)*i%e (1) ( i6 (ti51) ( i6 (TTti51'5nt1 = 24) ( G3F@4-=@DR B (1!!9)" ti51'5nt1 0" , i6 (TTti51'5nt2 = 25) ( G3F@4-=@DR B (1!!8)" ti51'5nt2 0" , ti51 0" , ,

-- togg%e g7een 02D

-- togg%e b%ue 02D

M=

2 E E E = E I E G E M E F E O = > = 9 = 2 = E = = = I = G = M = F = O I > I 9 I 2 I E I = I I I G

MI

!e't
%he next tutorial chapter on timers will show a few more ver useful wa s to use a timer# and then will explore wa s of getting useful work done using a simple time7and7event driven scheduler. ,owever# before we get to that we have to make a ver important detour...

When 6 LE-s :ust -on7t Cut ,t Anymore


So far# we5ve done ever thing in this series using two )E*s and one button. I5m guessing that the thrill of blinking an )E* has worn off b now# hard as that is to imagine. What5s more# we5ve $ust about reached the limits of what we can learn with such limited I.@. We have come to the point where we need to add some hardware to our setup to continue with additional concepts and microcontroller peripherals. I ran into this problem ver &uickl after I had picked up m first S%ME2C)*iscover board. E2 bit 01M Cortex ME# 92Fk flash# Fk 10M# timers galore# this was gonna be greatN Well# not so much# at least not with the board all b itself. So I designed a docking board with a power source# =x2> )C* character displa # =x= button matrix# = )E*s# a potentiometer for 0*C work# = configuration switches# and a bunch of expansion connectors. %he S%ME2C)*iscover would plug into this board and give a fun and useful s stem for working with that chip. 0s soon as I got back the first protot pe boards I reali:ed !that being the main function of protot pes# to remind ou of things ou should have done the first time" I should have added connectors for the S%ME28>*iscover # the Cortex M> version of the *iscover board# so there went a second round of protot pes. %hen I decided I could build a board that plugs into the docking board and that holds an 0%mega92F9# making the docking board useful for Cortex ME# Cortex M> and 0C1 training. ,ere is the result !with S%ME2C)*iscover board plugged in"A

MG

It is this docking board that I will be using from this point forward in this tutorial series# for both 0C1 and S%ME2 examples. I5ve been pla ing with the idea of turning it into a Jickstarter pro$ect# but so far the entire world5s suppl sits in a drawer in m desk. %here is certainl no re&uirement for an person using this tutorial to have this docking board. What will be re&uired is that the specified hardware !character )C*# button matrix# potentiometer# etc" be available in some form on whatever hardware platform is being used. ,ere is a list of the hardware blocks to be addedA

> ' 6? LC- Character -isp ay


%hese are our common ,*==MF>7compatible displa s# available ever where. I recommend the larger =x2> displa s# but one could use smaller displa s such as 2x9G. Diven the extra

MM

information ou can fit on a =x2># I think it is ver much worth the few extra dollars for the larger displa . 0 ==MF> displa can connect using either = or F data lines# along with 2 or E control lines. I often use the minimalist connection of = data lines and 2 control lines# but with a bit more software work the Erd control line can speed up communication with the displa somewhat. If using = data lines# the best arrangement is to select the = upper bits of a port# e.g. P0=7 P0M. %he control lines can be an bits on an ports# but it is probabl easiest to use the lower bits of the data port. @n m docking board I also include a logic7level mosfet to turn the displa backlight off and on !and even change its brightness via software or hardware PWM". If ou want ou can dispense with this output# but then I would suggest wiring the backlight to be permanentl @6. Most such displa s with their backlights off are ver anno ing to view# IM@.

> ' > (utton .atri'


%he button matrix re&uires = DPI@ pins that can be configured as inputs and = that can be configured as outputs. It is easiest if each set of = is contiguous on the same port. %hus e.g. P09#P02#P0E#P0= is preferable to e.g. P0>#P0E#P0=#P0M !same port but not contiguous"# and even more preferable than e.g. P09# P0E# P<I# P<G !multiple ports". Since both the 0C1 and the S%ME2 can activate internal pullups on input pins# no external pullup resistors are necessar . If ou were working with a uC famil that did not have internal pullups# or if ou were working in a high electrical7noise environment where lower7 value !3stiffer3" pullups were desireable for noise reasons# then ou would have to add those pullups to each input line.

LE-s
%he two )E*s on the S%ME2 boards are prett minimal. %he docking board parallels those two )E*s !the DPI@ outputs that drive the onboard )E*s also drive )E*s on the docking board"# and adds two additional )E*s for a total of = )E*s# placed across the = columns of the button matrix.

Configuration Switches
Configuration *IP switches are a useful tool to have on an educational microcontroller board. %he user can use such switches to switch between algorithms to compare them# or to switch between master . slave# or transmitter . receiver# or to provide some form of board addressing# among other uses. = *IP switches are provided on the docking board. %he are isolated with resistors so that if the pins connected to them are set as outputs# the outputs cannot be damaged no matter how the switches are set.

MF

A-C 2Ana og to -igita Converter4 Potentiometer


0 simple 9>k or 2>k potentiometer is ver useful in learning how to work with our microcontroller5s 0*C. %he docking board has such a potentiometer with the center pin connected to a DPI@ pin which can be configured as an 0*C input. 6ote that not all DPI@ pins can be used as 0*C inputs 7 which ones can be used will be indicated on the datasheet. @ne end of the pot is connected to ground# and the other end is connected to the Ccc of the uC. Coltages on an 0*C input must alwa s be within the range of D6*7YCcc to prevent damage to the microcontroller. 8or further experimentation# additional 0*C7capable DPI@ are brought out to some of the docking board expansion connectors.

8irst Step " Adding An LC- -isp ay


0dding a displa is a smart first step because a displa lets us see what is going on inside our uC# and is also a ver useful debugging aid when things are going wrong. %he displa will re&uire a minimum of G DPI@ outputs# preferabl all on a single port as discussed above. 0lso re&uired is either a transistor !mosfet or b$t" to switch the backlight on or off# or else $ust hardwiring the backlight on. If using a transistor ou will need to select a DPI@ output to drive the transistor. %his can be an bit on an port. Man newbies seem to have difficult programming the ,*==MF>7compatible displa s. I5ve never had a problem with them once I5ve read the datasheet carefull ! ou have downloaded a datasheet off the internet# rightU". Perhaps the most crucial issue is to get the timing right. 0 particular dela is re&uired after each initiali:ation step# and whenever writing to the displa in general. In addition# the enable !5E5" pulses to the displa must exceed a minimum duration. Hou must get these all of dela s correct for the displa to work. )onger is @J# but shorter and our displa will not work at all# or it will be flake . So# we5re back to talking about dela s. In particular# we need a dela of about 9us for the 5E5 pulse !actuall # Y2E>ns"# a dela of about I>us between each character write# and some dela s in the milliseconds range. So let5s refine our old dela !" function to generate reasonabl accurate microsecond and millisecond dela s.

Tangent@ Software -e ays " .i iseconds


1emember that up to now we didn5t much care what numbers we had to feed our software dela function# as long as we could get a human7perceivable )E* blink. 6ow we have to do better. %o run our )C* displa # we have to produce a dela of around 9us# and a dela of around I>us# and dela s in the ms range. %he easier task is to write a msVdela function# one that takes an argument for the number of milliseconds to dela . %he reason it is easier is that there are a lot of clock c cles to work with in one millisecond. 0n wa # here is the function# followed b the explanationA U 9$oid ?s'de%&y(u16 d) MO

2 ( E )*i%e (d-- + 0) = ( $o%&ti%e u16 i 8C'4@KEA" I )*i%e (i-- + 0) G " M , F, O What we have done is to break the software dela loop into two loops. If we choose a value of MSVC@/6% that causes the inner loop to execute in exactl 9ms# then the entire function will execute in 3d3 ms# as desired. So all that is re&uired is to come up with a number for MSVC@/6%. 0s is alwa s the case with software dela s# that number will depend on the uC clock rate 7 a higher clock rate means a larger number. We could figure a number b looking at the generated 0SM code and counting the c cles for each instruction# but there5s an easier wa !for me# at least"A fire up our oscilloscope !or beg or borrow one"# and write a loop that calls msVdela with some convenient number like 9>>. %hen pick a number for MSVC@/6% 7 9>>> is a good start. %oggle an )E* $ust as before# using 3msVdela !9>>"3 for the dela # and look at the scope. Hou will see a s&uare wave where the signal toggles ever so often. What we want is to see that toggle exactl ever 9>>ms# but we will almost certainl not see that at first. We will have a longer or shorter interval# and from that we can make MSVC@/6% shorter or longer until we get exactl !well# ver close# which is good enough" to 9>>ms. 8or m 0C1 board# now running at a clock rate of 9=.M=IGM,:# that number turns out to be 9>IE. 6ow that we5ve done that work# we can actuall come up with a formula that relates MSVC@/6% to 8VCP/. 9=#M=I#G>> . 9>IE P 9=#>>E.=2. 6ow we have a simple definition of MSVC@/6%A U 9;de6ine 8C'4@KEA (H'43K-14003) 1emember# this is for 0C1# and is onl valid for the compiler I used !avr7gcc in 0tmel Studio G# which is 0C1DCC E.=.9.OI" and the optimi:ation settings I used. %hat5s one of the two curses of software dela s 7 an change to clock rate or toolchain or settings can break them !the other curse is that the waste valuable c cles that could be used for doing the real work of our s stem". %here5s another trick ou can use to figure out MSVC@/6% if ou don5t have access to a scope 7 become a scope ourself. %o do this we5ll extend the dela time wa out past 9>>ms# to values a human can measure with some accurac . Since the argument to msVdela is a u9G# we can time up to GI#IEIms. G>#>>>ms is exactl one minute 7 convenientN So if we run our )E* blink program using msVdela !G>>>>"# one entire blink c cle !@67@88" will take exactl 2 minutes. We can time 2 minutes prett easil b e e and b clock. %hen ad$ust our initialVcount as followsA MSVC@/6% P initialVcount\92>.measuredVtime So if the initial count was 9>>> and one complete @67@88 blink took 9>I seconds# MSVC@/6% would be 9>>>\92>.9>I P 99=E. F>

Tangent@ Software -e ays " .icroseconds


/nfortunatel we can5t take the same approach with microsecond dela s# because there $ust aren5t enough CP/ clocks to work with in a microsecond. %hat5s not a terrible problem because we onl need two dela s# Y9us and YI>us. %he simplest solution is to $ust use a function like our earlier 3dela 3 function# and use a scope to come up with the correct loop counts. %he onl change would be to modif the dela function argument from a uintE2Vt to a uintFVt# to get better time resolution !faster looping on an F7bit uC". /sing this approach ou will be able to come up with a dela count that will generate a dela ver close to I>us# and a ver small dela count !9 is what I use" that will generate a dela that is rather longer than 9us# but that5s not a problem# so $ust go with it. 8or m 0C1# clock rate# tool chain and settings# those dela counts are 9 and O>. Hou will see both of these dela values in the listing to follow.

(ut What About The ,nc uded -e ay 8unctions?


/sers of avr7gcc know that there are some accurate built7in dela functions# VusVdela !" and VmsVdela !"# so wh not use themU %here are no doubt similar built7in functions included in some other compilers. %he answer is# there is no reason at all not to use them# if ou have them available. <ut not ever compiler will have such functions# and it5s a good exercise in learning how to write something e&uivalent# even if not as accurate.

(ac0 To )ur LC- -isp ay


0n ,*==MF>7t pe displa can perform a number of functions# but we will $ust focus on the two most important functions we need# which is !a" init the displa # and !b" displa a line of text on the displa . %he ,*==MF> datasheet gives the re&uired initiali:ation se&uence for both =7bit and F7bit mode !remember# we are using =7bit"# as well as the commands to write a character to a specific 47H location on the screen.

LC- ,nitia i*ation


%he ,*==MF> datasheet gives a se&uence of instructions called 3Initiali:ation b Instruction3. %his se&uence is a universal initiali:ation se&uence# which will work no matter what state the displa is in. It consists of F commands sent to the displa # with specified dela s between commands. %here is a futher complication that some of the commands are sent as F7bit commands# and some as =7bit commands. I won5t list the commands here since ou can see them in the software listing to follow# in the function lcdVinit!".

F9

LC- Put String


%his function sets the displa address according to the desired 47H coordinates of the string# and then writes the string characters into the displa memor .

LC- C ear -isp ay


%he last important function is one to clear the entire displa . %his function sends out a single 3clear screen3 command.

The LC- Source Code " A3+ 3ersion


,ere is all the code to get the displa running in =7bit mode. %he two dela functions are included in this listing even thought the might be in another file in a t pical pro$ect.

U 9 2 E = I G M F O 9> 99 92 9E 9= 9I 9G 9M 9F 9O 2> 29 22 2E 2= 2I 2G 2M 2F

-- %5d<5 -- ./R $e7sion -- 4-bit inte76&5e ;de6ine 04D'KC2'BH -- de6ine to use busy 6%&g -- de>ends on 5%o51 s>eed, too%5*&in &nd

;de6ine 8C'4@KEA (H'43K-14003) settings $oid n&no'de%&y($oid) ( , $oid tiny'de%&y($o%&ti%e u8 d) ( )*i%e (--d + 0) " , $oid ?s'de%&y(u16 d) ( )*i%e (d-- + 0) ( $o%&ti%e u16 i 8C'4@KEA" )*i%e (i-- + 0) " , , -- 04D is 3o7t . ;de6ine 04D'3@RA ;de6ine 04D'DD'3@RA

3@RA. DDR.

F2

2O E> E9 E2 EE E= EI EG EM EF EO => =9 =2 =E == =I =G =M =F =O I> I9 I2 IE I= II IG IM IF IO G> G9 G2 GE G= GI GG GM GF GO M> M9 M2 ME M= MI MG MM MF

;de6ine 04D'FE'3@RA ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine 04D'D4 04D'D5 04D'D6 04D'D7 04D'RC 04D'RU 04D'2 04D'B0 04D'4AR0 04D'D.A. 04D'B0FA2 04D'BH DFC3'FEFA DFC3'4BFAC DFC3'@E DFC3'@HH DFC3'40R 4KR'9@82 DFC3'28C DFC3'4@EHFG DD'R.8'.DDR DD'R.8'.DDR2 DD'R.8'.DDR3 DD'R.8'.DDR4 4G'R.8'.DDR

3FE. 'B/(3.4) 'B/(3.5) 'B/(3.6) 'B/(3.7) 'B/(3.1) -- 0 48D, 1 D.A. 'B/(3.2) -- 0 UR, 1 RD 'B/(3.3) 'B/(3.0) (04D'RC # 04D'RU # 04D'2) (04D'D4 # 04D'D5 # 04D'D6 # 04D'D7) (04D'B0) (04D'D7) 0D30 0D20 0D05 0D08 0D01 0D02 0D06 0D28 0D00 0D40 (DD'R.8'.DDRT0D14) (DD'R.8'.DDR2T0D14) 0D40

;de6ine 04D'0FE2C 4 ;de6ine 04D'UFDA9 20

$oid %5d'st7obe($oid) ( 04D'3@RA # 04D'2" n&no'de%&y()" 04D'3@RA I J04D'2" n&no'de%&y()" ,

-- st&7t 2 >u%se --tiny'de%&y(04D'CAR@B2)" -- end 2 >u%se --tiny'de%&y(04D'CAR@B2)"

$oid 04D'3@RA'd&t&(u8 d) ( -- )7ite u>>e7 4 bits o6 d&t& to 04D d&t& %ines 04D'3@RA (04D'3@RA I J04D'D.A.) # (d I 0D60)" , ;i6de6 04D'KC2'BH $oid %5d')&it($oid) ( u8 d&t&" 04D'DD'3@RA I J04D'D.A." 04D'3@RA I J04D'RC" 04D'3@RA # 04D'RU" do -- &%% d&t& %ines to in>ut -- 5?d -- set to 7e&d

FE

MO F> F9 F2 FE F= FI FG FM FF FO O> O9 O2 OE O= OI OG OM OF OO 9> > 9> 9 9> 2 9> E 9> = 9> I 9> G 9> M 9> F 9> O 99 > 99 9 99 2 99 E 99

04D'3@RA # 04D'2" -- 1st st7obe, 7e&d BH n&no'de%&y()" --tiny'de%&y(04D'CAR@B2)" d&t& 04D'FE'3@RA" 04D'3@RA I J04D'2" n&no'de%&y()" --tiny'de%&y(04D'CAR@B2)" 04D'3@RA # 04D'2" -- 2nd st7obe, donWt 7e&d d&t& n&no'de%&y()" --tiny'de%&y(04D'CAR@B2)" 04D'3@RA I J04D'2" n&no'de%&y()" --tiny'de%&y(04D'CAR@B2)" , )*i%e (d&t& I 04D'BH)" -- %oo> )*i%e BH is set 04D'3@RA I J04D'RU" 04D'DD'3@RA # 04D'D.A." -- set to )7ite -- &%% d&t& %ines to ou>ut

, ;endi6

$oid %5d'send'5?d(u8 5?d) ( ;i6de6 04D'KC2'BH %5d')&it()" ;endi6 04D'3@RA I J04D'RC" 04D'3@RA'd&t&(5?d I 0D60)" %5d'st7obe()" 04D'3@RA'd&t&((5?d I 0D6) !! 4)" %5d'st7obe()" ;i6nde6 04D'KC2'BH tiny'de%&y(90)" ;endi6 , $oid %5d'>ut5(u8 5) ( ;i6de6 04D'KC2'BH %5d')&it()" ;endi6 04D'3@RA # 04D'RC" 04D'3@RA'd&t&(5 I 0D60)" %5d'st7obe()" 04D'3@RA'd&t&((5 !! 4) I 0D60)" %5d'st7obe()" ;i6nde6 04D'KC2'BH tiny'de%&y(90)" ;endi6 ,

-- send *i 4 bits o6 5?d -- send %o 4 bits o6 5?d

-- send *i 4 bits o6 d&t& -- send %o 4 bits o6 d&t&

$oid %5d'init($oid) ( 04D'DD'3@RA 04D'4AR0 # 04D'D.A. # 04D'B0FA2" 04D'3@RA I J04D'RU" -- set to )7ite, >e7?&nent%y 04D'3@RA I J04D'RC" 04D'3@RA I J04D'2" ?s'de%&y(15)" 04D'3@RA'd&t&(DFC3'FEFA)" %5d'st7obe()" ?s'de%&y(5)" 04D'3@RA'd&t&(DFC3'FEFA)" %5d'st7obe()" -- ?ust be = 15

-- >seudo 8-bit 5o??&nd -- ?ust be = 4<1 -- >seudo 8-bit 5o??&nd

F=

= 99 I 99 G 99 M 99 F 99 O 92 > 92 9 92 2 92 E 92 = 92 I 92 G 92 M 92 F 92 O 9E > 9E 9 9E 2 9E E 9E = 9E I 9E G 9E M 9E F 9E

?s'de%&y(1)" 04D'3@RA'd&t&(DFC3'FEFA)" %5d'st7obe()" ?s'de%&y(1)" 04D'3@RA'd&t&(DFC3'4BFAC)" %5d'st7obe()" ?s'de%&y(1)" %5d'send'5?d(DFC3'4@EHFG)" %5d'send'5?d(DFC3'@HH)" %5d'send'5?d(DFC3'40R)" %5d'send'5?d(DFC3'28C)" %5d'send'5?d(DFC3'@E)" %5d'set'b&51%ig*t(1)"

-- ?ust be =

100us

-- >seudo 8-bit 5o??&nd

-- >seudo 8-bit 5o??&nd

$oid %5d'5%e&7($oid) ( %5d'send'5?d(DFC3'40R)" , $oid %5d'dis>%&y(int D, int y, 5onst 5*&7 Sst7) ( int n 04D'UFDA9 - D" u8 &dd7" i6 ((y ! 0) ## (y = 7etu7n" 04D'0FE2C))

s)it5* (y) ( de6&u%t: 5&se 0: &dd7 DD'R.8'.DDR" b7e&1" 5&se 1: &dd7 DD'R.8'.DDR2" b7e&1" 5&se 2: &dd7 DD'R.8'.DDR3" b7e&1" 5&se 3: &dd7 DD'R.8'.DDR4" b7e&1" , %5d'send'5?d(&dd7 T D T 0D80)" )*i%e (Sst7 II n--) %5d'>ut5(Sst7TT)"

$oid %5d'set'b&51%ig*t(u8 on) ( i6 (on) 04D'3@RA # 04D'B0" e%se 04D'3@RA I J04D'B0" ,

-- tu7n on b&51%ig*t -- tu7n o66 b&51%ig*t

FI

O 9= > 9= 9 9= 2 9= E 9= = 9= I 9= G 9= M 9= F 9= O 9I > 9I 9 9I 2 9I E 9I = 9I I 9I G 9I M 9I F 9I O 9G > 9G 9 9G 2 9G E 9G

FG

= 9G I 9G G 9G M 9G F 9G O 9M > 9M 9 9M 2 9M E 9M = 9M I 9M G 9M M 9M F 9M O 9F > 9F 9 9F 2 9F E 9F = 9F I 9F G 9F M 9F F 9F

FM

O 9O > 9O 9 9O 2 9O E 9O = 9O I 9O G 9O M 9O F 9O O 2> > 2> 9 2> 2 2> E 2> = 2> I 2> G 2> M 2> F

(ut Wait, There7s .ore1


*id ou notice that I snuck a couple of freebees into the )C* codeU @ne is fairl minor# and one is more substantial. 8irst the minor oneA I created a nanoVdela function that does nothing# $ust immediatel returns. %his makes the entire dela $ust the time it takes to execute the function call# and to return. %his ields# in m setup# an E pulse of GMFns 7 well above the minimum of 2E>ns# but still nice and short. %his is $ust a little gimick to get a sub7

FF

microsecond E pulse. It is b no means necessar # and ou can see in the comments for those lines the calls to tin Vdela that can be used instead. %he other addition is the flag )C*V/SEV<8 and associated code. <8 is the displa bus flag. @ne can choose to wait for the bus flag to signal not7bus after each command# rather than wait the time specified in the datasheet. %his can result in somewhat faster displa operation since the actual time the displa takes to process a command will usuall be less than the absolute maximum time specified in the datasheet. 8or example# a message that takes a little over G>>us to send using the fixed dela method# takes $ust a shade over =>>us to send using the wait for bus flag method# a EE] speed improvement. %his speedup comes at the expense of a little more code !@%@,# the need for a I>us dela disappears"# and an extra DPI@ line !the 1.W line 7 if using the fixed dela method# this line can $ust be wired to D6*". %o use the bus flag# we need to read the displa command register !that5s two =7bit reads# remember" and watch for the bus flag !*M" to go from 595 to 5>5. When this happens the displa is read to receive another command.

A Litt e Tric0 8or Waiting Efficient y


1egarding our lcdVwait!" function# b placing the function before the command or data write instead of after# we don5t waste time waiting if the displa is immediatel read when we attempt the write. 0s a general rule# alwa s test a read .bus flag before doing the associated read or write# instead of doing the read or write and then waiting for the flag to signal read . If our code has been off doing other things# the flag will probabl indicate read right awa # avoiding an unnecessar wait. %he ke is# as much as possible# for our code to be off doing useful work while the flag is bus # rather than wasting c cles waiting for it to go read . <asicall # do thisA U 9)&it'6o7'7e&dy()" 2do't*ing()" 6ot thisA U 9do't*ing()" 2)&it'6o7'7e&dy()"

-- ?ig*t &%7e&dy be 7e&dy+

-- )i%% &%)&ys *&$e to )&it+

LC-"Spea0
So# let5s test this codeN If the displa is wired up correctl # and all the timing dela s are correct# then the displa should fire right up. I don5t find them at all finick when hardware and software are correct. So here5s a simple main!" that initiali:es the displa and exercises it. It uses sprintf to format output strings# because it5s eas . @thers might prefer to use itoa. FO

@nl main!" is shown here. Hou5ll need to include an re&uired files. I5d also suggest that ou put our dela code in a dela .c file with associated dela .h# and our )C* code in an lcd.c file with associated lcd.h. %hen ou5ll include those two .h files as well into our main C file 7 call it )C*9V0C1.c. U 9 2 E = I ?&in($oid) G int ( M 5*&7 bu6O04D'UFDA9T1P" F unsigned int 5ount 0" O %5d'init()" 9 %5d'dis>%&y(0, 0, X9e%%oX)" > %5d'dis>%&y(4, 1, XUo7%d+X)" -- s*i6ted o$e7 to test LQ 6un5tion&%ity 9 )*i%e (1) 9 ( 9 s>7int6(bu6, X4@KEA: Y5uX, 5ount)" %5d'dis>%&y(0, 3, bu6)" 2 5ountTT" 9 ?s'de%&y(1000)" -- one 5ount >e7 se5ond E , 9 , = 9 I 9 G If ou want ou can blink an )E* inside our loop as well# to let ou know the loop is running even if our )C* doesn5t work. Hou can even blink the )C* backlight if ou have wired it up to a DPI@ pin. In an case# here is a picture of the docking board# with 0C1 plug7 in# running the above codeA

O>

Some -etai s As Seen )n The Scope


,ere is a scope picture of some )C* control and data signals while writing out a string to the displa # using bus flag waiting. It ma help ou to better understand what is happening.

O9

%he signals are as followsA


0A *ata bit *M !also <8" <A E pulse CA 1W signal !1ead P 9# Write P >" *A 1S signal !*ate P 9# Command P >"

%he points of interest areA 9. <8 still high from last write !9st of the 2 E pulses for each displa status read" 2. <8 now low at the end of 9st of 2 E pulses for next displa status read"# displa is read E. Switch from 1E0* to W1I%E =. Switch from CM* to *0%0 I. Write one data character using 2 E pulses G. Switch to 1E0* and CM* to start checking for <8 M. <8 is high !bus " from the $ust7completed write# keep reading until it goes low

-o We &ave Any Potentia -ata Corruption ,ssues " A3+ 3ersion?


1emember# we must alwa s ask this &uestion when# among other things# we are doing 1MW on a DPI@ port# as we are doing here. %he answer# for the port pins we have chosen# is 6@# because we are using the entire F7bit port for the )C*# and thus there is no possibilit that an IS1 could be using an of the pins for other purposes. %his assumes that we aren5t also calling )C* functions in an IS1 7 that would be a problemN If# on the other hand# we were using# sa # = bits of one port for data# and = bits of another port for control# then it would be &uite possible that the other = bits of each of the two ports might be used in an IS1# and we would have to check for that. So we did ourselves a favor b using up all the bits of one port for our )C* connections.

O2

The LC- Source Code " ST.56 3ersion


8or now I5m not going to show an S%ME2 version of the )C* code. %he main reason is that I have a bit of a trick up m sleeve for the S%ME2 but it has to wait until the next chapter on timers. %he other reason is that it should be a trivial effort to convert the 0C1 code to S%ME2 code. %his will $ust involve configuring the appropriate DPI@ pins and accessing them as we5ve alread seen# and also figuring new dela values if not using the bus flag.

-o We &ave Any Potentia -ata Corruption ,ssues " ST.56 3ersion?


We can5t sa 6@ as &uickl in this case# since S%ME2 ports are 92 bits wide and we5re onl using F bits. So it is something we would need to keep in mind if and when we assigned an of those remaining = port pins. If an of them were manipulated in an IS1# then we would have to protect our )C* port accesses with some form of atomic action. 1emember that the S%ME2 has atomic set and reset registers that we can use# but when it comes to setting the = bits of data we can5t easil use those !we can# but it would take some additional code"# so we would be reduced to turning off the offending interrupt!s" while writing the = data bits. %he best choice# if possible# would be to assign the remaining = bits to duties that did not involve an IS1 manipulation.

!e't
%he next chapter will take another look at timers# and how the might help with our )C* code. 0fter that we will add additional hardware to our setup# and write some software for that hardware.

When 6 LE-s :ust -on7t Cut ,t Anymore


So far# we5ve done ever thing in this series using two )E*s and one button. I5m guessing that the thrill of blinking an )E* has worn off b now# hard as that is to imagine. What5s more# we5ve $ust about reached the limits of what we can learn with such limited I.@. We have come to the point where we need to add some hardware to our setup to continue with additional concepts and microcontroller peripherals. I ran into this problem ver &uickl after I had picked up m first S%ME2C)*iscover board. E2 bit 01M Cortex ME# 92Fk flash# Fk 10M# timers galore# this was gonna be greatN Well# not so much# at least not with the board all b itself. So I designed a docking board with a power source# =x2> )C* character displa # =x= button matrix# = )E*s# a potentiometer for 0*C work# = configuration switches# and a bunch of expansion connectors. %he S%ME2C)*iscover would plug into this board and give a fun and useful s stem for working with that chip. 0s soon as I got back the first protot pe boards I reali:ed !that being the main function of protot pes# to remind ou of things ou should have done the first time" I should have added connectors for the S%ME28>*iscover # the Cortex M> version of the *iscover board# so there went a second round of protot pes. %hen I decided I could build a board that plugs into the docking board and that holds an 0%mega92F9# making the

OE

docking board useful for Cortex ME# Cortex M> and 0C1 training. ,ere is the result !with S%ME2C)*iscover board plugged in"A

It is this docking board that I will be using from this point forward in this tutorial series# for both 0C1 and S%ME2 examples. I5ve been pla ing with the idea of turning it into a Jickstarter pro$ect# but so far the entire world5s suppl sits in a drawer in m desk. %here is certainl no re&uirement for an person using this tutorial to have this docking board. What will be re&uired is that the specified hardware !character )C*# button matrix# potentiometer# etc" be available in some form on whatever hardware platform is being used. ,ere is a list of the hardware blocks to be addedA

O=

> ' 6? LC- Character -isp ay


%hese are our common ,*==MF>7compatible displa s# available ever where. I recommend the larger =x2> displa s# but one could use smaller displa s such as 2x9G. Diven the extra information ou can fit on a =x2># I think it is ver much worth the few extra dollars for the larger displa . 0 ==MF> displa can connect using either = or F data lines# along with 2 or E control lines. I often use the minimalist connection of = data lines and 2 control lines# but with a bit more software work the Erd control line can speed up communication with the displa somewhat. If using = data lines# the best arrangement is to select the = upper bits of a port# e.g. P0=7 P0M. %he control lines can be an bits on an ports# but it is probabl easiest to use the lower bits of the data port. @n m docking board I also include a logic7level mosfet to turn the displa backlight off and on !and even change its brightness via software or hardware PWM". If ou want ou can dispense with this output# but then I would suggest wiring the backlight to be permanentl @6. Most such displa s with their backlights off are ver anno ing to view# IM@.

> ' > (utton .atri'


%he button matrix re&uires = DPI@ pins that can be configured as inputs and = that can be configured as outputs. It is easiest if each set of = is contiguous on the same port. %hus e.g. P09#P02#P0E#P0= is preferable to e.g. P0>#P0E#P0=#P0M !same port but not contiguous"# and even more preferable than e.g. P09# P0E# P<I# P<G !multiple ports". Since both the 0C1 and the S%ME2 can activate internal pullups on input pins# no external pullup resistors are necessar . If ou were working with a uC famil that did not have internal pullups# or if ou were working in a high electrical7noise environment where lower7 value !3stiffer3" pullups were desireable for noise reasons# then ou would have to add those pullups to each input line.

LE-s
%he two )E*s on the S%ME2 boards are prett minimal. %he docking board parallels those two )E*s !the DPI@ outputs that drive the onboard )E*s also drive )E*s on the docking board"# and adds two additional )E*s for a total of = )E*s# placed across the = columns of the button matrix.

Configuration Switches
Configuration *IP switches are a useful tool to have on an educational microcontroller board. %he user can use such switches to switch between algorithms to compare them# or to switch between master . slave# or transmitter . receiver# or to provide some form of board addressing# among other uses. = *IP switches are provided on the docking board. %he are OI

isolated with resistors so that if the pins connected to them are set as outputs# the outputs cannot be damaged no matter how the switches are set.

A-C 2Ana og to -igita Converter4 Potentiometer


0 simple 9>k or 2>k potentiometer is ver useful in learning how to work with our microcontroller5s 0*C. %he docking board has such a potentiometer with the center pin connected to a DPI@ pin which can be configured as an 0*C input. 6ote that not all DPI@ pins can be used as 0*C inputs 7 which ones can be used will be indicated on the datasheet. @ne end of the pot is connected to ground# and the other end is connected to the Ccc of the uC. Coltages on an 0*C input must alwa s be within the range of D6*7YCcc to prevent damage to the microcontroller. 8or further experimentation# additional 0*C7capable DPI@ are brought out to some of the docking board expansion connectors.

8irst Step " Adding An LC- -isp ay


0dding a displa is a smart first step because a displa lets us see what is going on inside our uC# and is also a ver useful debugging aid when things are going wrong. %he displa will re&uire a minimum of G DPI@ outputs# preferabl all on a single port as discussed above. 0lso re&uired is either a transistor !mosfet or b$t" to switch the backlight on or off# or else $ust hardwiring the backlight on. If using a transistor ou will need to select a DPI@ output to drive the transistor. %his can be an bit on an port. Man newbies seem to have difficult programming the ,*==MF>7compatible displa s. I5ve never had a problem with them once I5ve read the datasheet carefull ! ou have downloaded a datasheet off the internet# rightU". Perhaps the most crucial issue is to get the timing right. 0 particular dela is re&uired after each initiali:ation step# and whenever writing to the displa in general. In addition# the enable !5E5" pulses to the displa must exceed a minimum duration. Hou must get these all of dela s correct for the displa to work. )onger is @J# but shorter and our displa will not work at all# or it will be flake . So# we5re back to talking about dela s. In particular# we need a dela of about 9us for the 5E5 pulse !actuall # Y2E>ns"# a dela of about I>us between each character write# and some dela s in the milliseconds range. So let5s refine our old dela !" function to generate reasonabl accurate microsecond and millisecond dela s.

Tangent@ Software -e ays " .i iseconds


1emember that up to now we didn5t much care what numbers we had to feed our software dela function# as long as we could get a human7perceivable )E* blink. 6ow we have to do better. %o run our )C* displa # we have to produce a dela of around 9us# and a dela of around I>us# and dela s in the ms range. %he easier task is to write a msVdela function# one that takes an argument for the number of milliseconds to dela . %he reason it is easier is that OG

there are a lot of clock c cles to work with in one millisecond. 0n wa # here is the function# followed b the explanationA U 9 ?s'de%&y(u16 d) 2$oid ( E )*i%e (d-- + 0) = ( $o%&ti%e u16 i 8C'4@KEA" I )*i%e (i-- + 0) G " M , F, O What we have done is to break the software dela loop into two loops. If we choose a value of MSVC@/6% that causes the inner loop to execute in exactl 9ms# then the entire function will execute in 3d3 ms# as desired. So all that is re&uired is to come up with a number for MSVC@/6%. 0s is alwa s the case with software dela s# that number will depend on the uC clock rate 7 a higher clock rate means a larger number. We could figure a number b looking at the generated 0SM code and counting the c cles for each instruction# but there5s an easier wa !for me# at least"A fire up our oscilloscope !or beg or borrow one"# and write a loop that calls msVdela with some convenient number like 9>>. %hen pick a number for MSVC@/6% 7 9>>> is a good start. %oggle an )E* $ust as before# using 3msVdela !9>>"3 for the dela # and look at the scope. Hou will see a s&uare wave where the signal toggles ever so often. What we want is to see that toggle exactl ever 9>>ms# but we will almost certainl not see that at first. We will have a longer or shorter interval# and from that we can make MSVC@/6% shorter or longer until we get exactl !well# ver close# which is good enough" to 9>>ms. 8or m 0C1 board# now running at a clock rate of 9=.M=IGM,:# that number turns out to be 9>IE. 6ow that we5ve done that work# we can actuall come up with a formula that relates MSVC@/6% to 8VCP/. 9=#M=I#G>> . 9>IE P 9=#>>E.=2. 6ow we have a simple definition of MSVC@/6%A U 9;de6ine 8C'4@KEA (H'43K-14003) 1emember# this is for 0C1# and is onl valid for the compiler I used !avr7gcc in 0tmel Studio G# which is 0C1DCC E.=.9.OI" and the optimi:ation settings I used. %hat5s one of the two curses of software dela s 7 an change to clock rate or toolchain or settings can break them !the other curse is that the waste valuable c cles that could be used for doing the real work of our s stem". %here5s another trick ou can use to figure out MSVC@/6% if ou don5t have access to a scope 7 become a scope ourself. %o do this we5ll extend the dela time wa out past 9>>ms# to values a human can measure with some accurac . Since the argument to msVdela is a u9G# we can time up to GI#IEIms. G>#>>>ms is exactl one minute 7 convenientN So if we run our )E* blink program using msVdela !G>>>>"# one entire blink c cle !@67@88" will take exactl 2 minutes. We can time 2 minutes prett easil b e e and b clock. %hen ad$ust our initialVcount as followsA OM

MSVC@/6% P initialVcount\92>.measuredVtime So if the initial count was 9>>> and one complete @67@88 blink took 9>I seconds# MSVC@/6% would be 9>>>\92>.9>I P 99=E.

Tangent@ Software -e ays " .icroseconds


/nfortunatel we can5t take the same approach with microsecond dela s# because there $ust aren5t enough CP/ clocks to work with in a microsecond. %hat5s not a terrible problem because we onl need two dela s# Y9us and YI>us. %he simplest solution is to $ust use a function like our earlier 3dela 3 function# and use a scope to come up with the correct loop counts. %he onl change would be to modif the dela function argument from a uintE2Vt to a uintFVt# to get better time resolution !faster looping on an F7bit uC". /sing this approach ou will be able to come up with a dela count that will generate a dela ver close to I>us# and a ver small dela count !9 is what I use" that will generate a dela that is rather longer than 9us# but that5s not a problem# so $ust go with it. 8or m 0C1# clock rate# tool chain and settings# those dela counts are 9 and O>. Hou will see both of these dela values in the listing to follow.

(ut What About The ,nc uded -e ay 8unctions?


/sers of avr7gcc know that there are some accurate built7in dela functions# VusVdela !" and VmsVdela !"# so wh not use themU %here are no doubt similar built7in functions included in some other compilers. %he answer is# there is no reason at all not to use them# if ou have them available. <ut not ever compiler will have such functions# and it5s a good exercise in learning how to write something e&uivalent# even if not as accurate.

(ac0 To )ur LC- -isp ay


0n ,*==MF>7t pe displa can perform a number of functions# but we will $ust focus on the two most important functions we need# which is !a" init the displa # and !b" displa a line of text on the displa . %he ,*==MF> datasheet gives the re&uired initiali:ation se&uence for both =7bit and F7bit mode !remember# we are using =7bit"# as well as the commands to write a character to a specific 47H location on the screen.

LC- ,nitia i*ation


%he ,*==MF> datasheet gives a se&uence of instructions called 3Initiali:ation b Instruction3. %his se&uence is a universal initiali:ation se&uence# which will work no matter what state the displa is in. It consists of F commands sent to the displa # with specified dela s between commands. %here is a futher complication that some of the commands are

OF

sent as F7bit commands# and some as =7bit commands. I won5t list the commands here since ou can see them in the software listing to follow# in the function lcdVinit!".

LC- Put String


%his function sets the displa address according to the desired 47H coordinates of the string# and then writes the string characters into the displa memor .

LC- C ear -isp ay


%he last important function is one to clear the entire displa . %his function sends out a single 3clear screen3 command.

The LC- Source Code " A3+ 3ersion


,ere is all the code to get the displa running in =7bit mode. %he two dela functions are included in this listing even thought the might be in another file in a t pical pro$ect.

U 9 2 E = I G M F O 9> 99 92 9E 9= 9I 9G 9M 9F 9O 2> 29 22 2E

-- %5d<5 -- ./R $e7sion -- 4-bit inte76&5e ;de6ine 04D'KC2'BH -- de6ine to use busy 6%&g -- de>ends on 5%o51 s>eed, too%5*&in &nd

;de6ine 8C'4@KEA (H'43K-14003) settings $oid n&no'de%&y($oid) ( , $oid tiny'de%&y($o%&ti%e u8 d) ( )*i%e (--d + 0) " , $oid ?s'de%&y(u16 d) ( )*i%e (d-- + 0) ( $o%&ti%e u16 i 8C'4@KEA" )*i%e (i-- + 0) " ,

OO

2= 2I 2G 2M 2F 2O E> E9 E2 EE E= EI EG EM EF EO => =9 =2 =E == =I =G =M =F =O I> I9 I2 IE I= II IG IM IF IO G> G9 G2 GE G= GI GG GM GF GO M> M9 M2 ME

, -- 04D is 3o7t . ;de6ine 04D'3@RA ;de6ine 04D'DD'3@RA ;de6ine 04D'FE'3@RA ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine ;de6ine 04D'D4 04D'D5 04D'D6 04D'D7 04D'RC 04D'RU 04D'2 04D'B0 04D'4AR0 04D'D.A. 04D'B0FA2 04D'BH DFC3'FEFA DFC3'4BFAC DFC3'@E DFC3'@HH DFC3'40R 4KR'9@82 DFC3'28C DFC3'4@EHFG DD'R.8'.DDR DD'R.8'.DDR2 DD'R.8'.DDR3 DD'R.8'.DDR4 4G'R.8'.DDR

3@RA. DDR. 3FE. 'B/(3.4) 'B/(3.5) 'B/(3.6) 'B/(3.7) 'B/(3.1) -- 0 48D, 1 D.A. 'B/(3.2) -- 0 UR, 1 RD 'B/(3.3) 'B/(3.0) (04D'RC # 04D'RU # 04D'2) (04D'D4 # 04D'D5 # 04D'D6 # 04D'D7) (04D'B0) (04D'D7) 0D30 0D20 0D05 0D08 0D01 0D02 0D06 0D28 0D00 0D40 (DD'R.8'.DDRT0D14) (DD'R.8'.DDR2T0D14) 0D40

;de6ine 04D'0FE2C 4 ;de6ine 04D'UFDA9 20

$oid %5d'st7obe($oid) ( 04D'3@RA # 04D'2" n&no'de%&y()" 04D'3@RA I J04D'2" n&no'de%&y()" ,

-- st&7t 2 >u%se --tiny'de%&y(04D'CAR@B2)" -- end 2 >u%se --tiny'de%&y(04D'CAR@B2)"

$oid 04D'3@RA'd&t&(u8 d) ( -- )7ite u>>e7 4 bits o6 d&t& to 04D d&t& %ines 04D'3@RA (04D'3@RA I J04D'D.A.) # (d I 0D60)" , ;i6de6 04D'KC2'BH $oid %5d')&it($oid) ( u8 d&t&"

9>>

M= MI MG MM MF MO F> F9 F2 FE F= FI FG FM FF FO O> O9 O2 OE O= OI OG OM OF OO 9> > 9> 9 9> 2 9> E 9> = 9> I 9> G 9> M 9> F 9> O 99 > 99 9

04D'DD'3@RA I J04D'D.A." 04D'3@RA I J04D'RC" 04D'3@RA # 04D'RU" do (

-- &%% d&t& %ines to in>ut -- 5?d -- set to 7e&d

04D'3@RA # 04D'2" -- 1st st7obe, 7e&d BH n&no'de%&y()" --tiny'de%&y(04D'CAR@B2)" d&t& 04D'FE'3@RA" 04D'3@RA I J04D'2" n&no'de%&y()" --tiny'de%&y(04D'CAR@B2)" 04D'3@RA # 04D'2" -- 2nd st7obe, donWt 7e&d d&t& n&no'de%&y()" --tiny'de%&y(04D'CAR@B2)" 04D'3@RA I J04D'2" n&no'de%&y()" --tiny'de%&y(04D'CAR@B2)" , )*i%e (d&t& I 04D'BH)" -- %oo> )*i%e BH is set 04D'3@RA I J04D'RU" 04D'DD'3@RA # 04D'D.A." -- set to )7ite -- &%% d&t& %ines to ou>ut

, ;endi6

$oid %5d'send'5?d(u8 5?d) ( ;i6de6 04D'KC2'BH %5d')&it()" ;endi6 04D'3@RA I J04D'RC" 04D'3@RA'd&t&(5?d I 0D60)" %5d'st7obe()" 04D'3@RA'd&t&((5?d I 0D6) !! 4)" %5d'st7obe()" ;i6nde6 04D'KC2'BH tiny'de%&y(90)" ;endi6 , $oid %5d'>ut5(u8 5) ( ;i6de6 04D'KC2'BH %5d')&it()" ;endi6 04D'3@RA # 04D'RC" 04D'3@RA'd&t&(5 I 0D60)" %5d'st7obe()" 04D'3@RA'd&t&((5 !! 4) I 0D60)" %5d'st7obe()" ;i6nde6 04D'KC2'BH tiny'de%&y(90)" ;endi6 ,

-- send *i 4 bits o6 5?d -- send %o 4 bits o6 5?d

-- send *i 4 bits o6 d&t& -- send %o 4 bits o6 d&t&

$oid %5d'init($oid) ( 04D'DD'3@RA 04D'4AR0 # 04D'D.A. # 04D'B0FA2" 04D'3@RA I J04D'RU" -- set to )7ite, >e7?&nent%y 04D'3@RA I J04D'RC" 04D'3@RA I J04D'2" ?s'de%&y(15)" -- ?ust be = 15

9>9

99 2 99 E 99 = 99 I 99 G 99 M 99 F 99 O 92 > 92 9 92 2 92 E 92 = 92 I 92 G 92 M 92 F 92 O 9E > 9E 9 9E 2 9E E 9E = 9E I 9E G

04D'3@RA'd&t&(DFC3'FEFA)" %5d'st7obe()" ?s'de%&y(5)" 04D'3@RA'd&t&(DFC3'FEFA)" %5d'st7obe()" ?s'de%&y(1)" 04D'3@RA'd&t&(DFC3'FEFA)" %5d'st7obe()" ?s'de%&y(1)" 04D'3@RA'd&t&(DFC3'4BFAC)" %5d'st7obe()" ?s'de%&y(1)" %5d'send'5?d(DFC3'4@EHFG)" %5d'send'5?d(DFC3'@HH)" %5d'send'5?d(DFC3'40R)" %5d'send'5?d(DFC3'28C)" %5d'send'5?d(DFC3'@E)" %5d'set'b&51%ig*t(1)" , $oid %5d'5%e&7($oid) ( %5d'send'5?d(DFC3'40R)" ,

-- >seudo 8-bit 5o??&nd -- ?ust be = 4<1 -- >seudo 8-bit 5o??&nd -- ?ust be = 100us -- >seudo 8-bit 5o??&nd

-- >seudo 8-bit 5o??&nd

$oid %5d'dis>%&y(int D, int y, 5onst 5*&7 Sst7) ( int n 04D'UFDA9 - D" u8 &dd7" i6 ((y ! 0) ## (y = 7etu7n" 04D'0FE2C))

s)it5* (y) ( de6&u%t: 5&se 0: &dd7 DD'R.8'.DDR" b7e&1" 5&se 1: &dd7 DD'R.8'.DDR2" b7e&1" 5&se 2: &dd7 DD'R.8'.DDR3" b7e&1" 5&se 3: &dd7 DD'R.8'.DDR4" b7e&1" , %5d'send'5?d(&dd7 T D T 0D80)" )*i%e (Sst7 II n--) %5d'>ut5(Sst7TT)" , $oid %5d'set'b&51%ig*t(u8 on) ( i6 (on)

9>2

04D'3@RA # 9E e%se M 04D'3@RA I 9E , F 9E O 9= > 9= 9 9= 2 9= E 9= = 9= I 9= G 9= M 9= F 9= O 9I > 9I 9 9I 2 9I E 9I = 9I I 9I G 9I M 9I F 9I O 9G > 9G 9

04D'B0" J04D'B0"

-- tu7n on b&51%ig*t -- tu7n o66 b&51%ig*t

9>E

9G 2 9G E 9G = 9G I 9G G 9G M 9G F 9G O 9M > 9M 9 9M 2 9M E 9M = 9M I 9M G 9M M 9M F 9M O 9F > 9F 9 9F 2 9F E 9F = 9F I 9F G

9>=

9F M 9F F 9F O 9O > 9O 9 9O 2 9O E 9O = 9O I 9O G 9O M 9O F 9O O 2> > 2> 9 2> 2 2> E 2> = 2> I 2> G 2> M 2> F

9>I

(ut Wait, There7s .ore1


*id ou notice that I snuck a couple of freebees into the )C* codeU @ne is fairl minor# and one is more substantial. 8irst the minor oneA I created a nanoVdela function that does nothing# $ust immediatel returns. %his makes the entire dela $ust the time it takes to execute the function call# and to return. %his ields# in m setup# an E pulse of GMFns 7 well above the minimum of 2E>ns# but still nice and short. %his is $ust a little gimick to get a sub7 microsecond E pulse. It is b no means necessar # and ou can see in the comments for those lines the calls to tin Vdela that can be used instead. %he other addition is the flag )C*V/SEV<8 and associated code. <8 is the displa bus flag. @ne can choose to wait for the bus flag to signal not7bus after each command# rather than wait the time specified in the datasheet. %his can result in somewhat faster displa operation since the actual time the displa takes to process a command will usuall be less than the absolute maximum time specified in the datasheet. 8or example# a message that takes a little over G>>us to send using the fixed dela method# takes $ust a shade over =>>us to send using the wait for bus flag method# a EE] speed improvement. %his speedup comes at the expense of a little more code !@%@,# the need for a I>us dela disappears"# and an extra DPI@ line !the 1.W line 7 if using the fixed dela method# this line can $ust be wired to D6*". %o use the bus flag# we need to read the displa command register !that5s two =7bit reads# remember" and watch for the bus flag !*M" to go from 595 to 5>5. When this happens the displa is read to receive another command.

A Litt e Tric0 8or Waiting Efficient y


1egarding our lcdVwait!" function# b placing the function before the command or data write instead of after# we don5t waste time waiting if the displa is immediatel read when we attempt the write. 0s a general rule# alwa s test a read .bus flag before doing the associated read or write# instead of doing the read or write and then waiting for the flag to signal read . If our code has been off doing other things# the flag will probabl indicate read right awa # avoiding an unnecessar wait. %he ke is# as much as possible# for our code to be off doing useful work while the flag is bus # rather than wasting c cles waiting for it to go read . <asicall # do thisA U 9)&it'6o7'7e&dy()" 2do't*ing()" 6ot thisA U 9do't*ing()" 2)&it'6o7'7e&dy()"

-- ?ig*t &%7e&dy be 7e&dy+

-- )i%% &%)&ys *&$e to )&it+

9>G

LC-"Spea0
So# let5s test this codeN If the displa is wired up correctl # and all the timing dela s are correct# then the displa should fire right up. I don5t find them at all finick when hardware and software are correct. So here5s a simple main!" that initiali:es the displa and exercises it. It uses sprintf to format output strings# because it5s eas . @thers might prefer to use itoa. @nl main!" is shown here. Hou5ll need to include an re&uired files. I5d also suggest that ou put our dela code in a dela .c file with associated dela .h# and our )C* code in an lcd.c file with associated lcd.h. %hen ou5ll include those two .h files as well into our main C file 7 call it )C*9V0C1.c. U 9 2 E = I ?&in($oid) G int ( M 5*&7 bu6O04D'UFDA9T1P" F unsigned int 5ount 0" O %5d'init()" 9 %5d'dis>%&y(0, 0, X9e%%oX)" > %5d'dis>%&y(4, 1, XUo7%d+X)" -- s*i6ted o$e7 to test LQ 6un5tion&%ity 9 )*i%e (1) 9 ( 9 s>7int6(bu6, X4@KEA: Y5uX, 5ount)" %5d'dis>%&y(0, 3, bu6)" 2 5ountTT" 9 ?s'de%&y(1000)" -- one 5ount >e7 se5ond E , 9 , = 9 I 9 G If ou want ou can blink an )E* inside our loop as well# to let ou know the loop is running even if our )C* doesn5t work. Hou can even blink the )C* backlight if ou have wired it up to a DPI@ pin. In an case# here is a picture of the docking board# with 0C1 plug7 in# running the above codeA

9>M

Some -etai s As Seen )n The Scope


,ere is a scope picture of some )C* control and data signals while writing out a string to the displa # using bus flag waiting. It ma help ou to better understand what is happening.

9>F

%he signals are as followsA


0A *ata bit *M !also <8" <A E pulse CA 1W signal !1ead P 9# Write P >" *A 1S signal !*ate P 9# Command P >"

%he points of interest areA 9. <8 still high from last write !9st of the 2 E pulses for each displa status read" 2. <8 now low at the end of 9st of 2 E pulses for next displa status read"# displa is read E. Switch from 1E0* to W1I%E =. Switch from CM* to *0%0 I. Write one data character using 2 E pulses G. Switch to 1E0* and CM* to start checking for <8 M. <8 is high !bus " from the $ust7completed write# keep reading until it goes low

-o We &ave Any Potentia -ata Corruption ,ssues " A3+ 3ersion?


1emember# we must alwa s ask this &uestion when# among other things# we are doing 1MW on a DPI@ port# as we are doing here. %he answer# for the port pins we have chosen# is 6@# because we are using the entire F7bit port for the )C*# and thus there is no possibilit that an IS1 could be using an of the pins for other purposes. %his assumes that we aren5t also calling )C* functions in an IS1 7 that would be a problemN If# on the other hand# we were using# sa # = bits of one port for data# and = bits of another port for control# then it would be &uite possible that the other = bits of each of the two ports might be used in an IS1# and we would have to check for that. So we did ourselves a favor b using up all the bits of one port for our )C* connections.

9>O

The LC- Source Code " ST.56 3ersion


8or now I5m not going to show an S%ME2 version of the )C* code. %he main reason is that I have a bit of a trick up m sleeve for the S%ME2 but it has to wait until the next chapter on timers. %he other reason is that it should be a trivial effort to convert the 0C1 code to S%ME2 code. %his will $ust involve configuring the appropriate DPI@ pins and accessing them as we5ve alread seen# and also figuring new dela values if not using the bus flag.

-o We &ave Any Potentia -ata Corruption ,ssues " ST.56 3ersion?


We can5t sa 6@ as &uickl in this case# since S%ME2 ports are 92 bits wide and we5re onl using F bits. So it is something we would need to keep in mind if and when we assigned an of those remaining = port pins. If an of them were manipulated in an IS1# then we would have to protect our )C* port accesses with some form of atomic action. 1emember that the S%ME2 has atomic set and reset registers that we can use# but when it comes to setting the = bits of data we can5t easil use those !we can# but it would take some additional code"# so we would be reduced to turning off the offending interrupt!s" while writing the = data bits. %he best choice# if possible# would be to assign the remaining = bits to duties that did not involve an IS1 manipulation.

!e't
%he next chapter will take another look at timers# and how the might help with our )C* code. 0fter that we will add additional hardware to our setup# and write some software for that hardware.

What ,s A (utton?
%o our hardware# that is. 0s discussed in Introduction to Microcontrollers 7 More @n DPI@# a button !or ke # or switch# or an form of mechanical contact" is generall hooked up to a microcontroller so as to generate a certain logic level when pushed or closed or 3active#3 and the opposite logic level when unpushed or open or 3inactive.3 %he active logic level can be either 5>5 or 595# but for reasons both historical and electrical# an active level of 5>5 is more common. ,ere is the basic button connection to a DPI@ input# which we saw in the earlier tutorial chapterA

99>

When the switch is open# the pullup or pulldown causes the DPI@ input pin to see the inactive logic state# but when the switch is closed it overrides the resistor and causes the DPI@ input pin to see the active logic state. 0s a reminder# drawing 505 shows an active7high button# while drawing 5<5 shows an active7low button.

What -oes A (utton Loo0 Li0e?


%o our code# that is. %he simplest wa to represent a button is as a boolean# or as a single :ero or non7:ero b te. @ne button# one b te. Call this Method 9# or the <oolean7per7<utton method. It5s simple because there is no masking re&uired# either in setting the variable or in reading it. %he drawback of this approach is that it makes it difficult to check for all the different button possibilities in our s stem !9G buttons P 9G b tes to test". 0nother wa to represent a button is as a boolean which is is one bit of a b te or 9G7bit word. 6ow ou can have a single b te or word that can hold the state of up to F or 9G completel independent buttons !or E2 if ou want to go to a E27bit value". %his button aggregate is eas to process in a 3switch3 statement# and it also provides the abilit to check for multiple simultaneous button pushes. < 3independent3 buttons we mean buttons that can be pressed and recogni:ed regardless of the state of an of the other buttons. %his situation or re&uirement is actuall much more common for switches than for buttons# but remember we are treating switches and buttons and ke s all the same here. Call this Method 2# or the <it7 per7<utton method. 8inall # one can represent the state of multiple non7independent buttons as a single value. %ake a calculator ke board for example. With a calculator there is probabl onl one ke press allowed at an given time# so a calculator with 2> or E> buttons can represent all the possible valid button pushes with a single variable that can hold 2> or E> values. In this case# E> buttons can be represented b onl I bits# since the buttons are not independent and thus most !or even all" combinations of multiple buttons are illegal. Call this Method E# or the Single7Calue method.

999

8or the most part# all the examples in this tutorial section will represent buttons according to methods 2 or E 7 that is# as an F or 9G7bit variable that either represents multiple independent buttons or that represents a uni&ue value among non7independent buttons.

(utton -ebouncing, +evisited


Hou ma remember in Introduction to Microcontrollers 7 More @n DPI@ that we saw some scar scope images of switch bounces. Ever s stem that uses an kind of mechanical switch must deal with the issue of debouncing. %he ke task is to make sure that one mechanical switch or button action is onl read as one action b the microcontroller# even though the microcontroller will t picall be fast enough to detect the unwanted switch bounces and treat them as separate events. <ouncing can be eliminated b special ICs or b 1C circuitr # but in most cases debouncing is done in software because software is 3free.3 !a software person should alwa s carr tomatoes to throw at this point" Scar images of switch bouncing# reduxA

%he ke to debouncing is to establish a minimum criterion for a valid button push# one that can be implemented in software. %his criterion must involve differences in time 7 two button presses in 2>ms must be treated as one button event# while two button presses in 2 seconds

992

must be treated as two button events. So what are the relevant times we need to considerU %he are theseA

<ounce timeA most buttons seem to stop bouncing within 9>ms <utton press timeA the shortest time a user can press and release a button seems to be between I> and 9>>ms 1esponse timeA a user notices if the s stem response is 9>>ms after the button press# but not if it is I>ms after

Combining all of these times# we can set a few goals


Ignore all bouncing within 9>ms Provide a response within I>ms of detecting a button push !or release" <e able to detect a I>ms push and a I>ms release

%he simplest debouncing method is to examine the ke s !or buttons or switches" ever 6 milliseconds# where 6 Y 9>ms !our specified button bounce upper limit" and 6 XP I>ms !our specified response time". %his is the method we used in the last chapter with our example c clic executive program S%ME2V)C*2. We then have three possible outcomes ever time we read a buttonA 9. We read the button in the solid 5>5 state 2. We read the button in the solid 595 state E. We read the button while it is bouncing !so we will get either a 5>5 or a 595" @utcomes 9 and 2 pose no problems# as the are what we5d alwa s like to happen. @utcome E also poses no problem because during a bounce either state is acceptable. If we have $ust pressed an active7low button and we read a 595 as it bounces# the next time through we are guaranteed to read a 5>5 !remember# the next time through all bouncing will have ceased"# so we will $ust detect the button push a bit later. @therwise# if we read a 5>5 as the button bounces# it will still be 5>5 the next time after all bouncing has stopped# so we are $ust detecting the button push a bit earlier. %he same applies to releasing a button. 1eading a single bounce !with all bouncing over b the time of the next read" will never give us an invalid button state. It5s onl reading multiple bounces !multiple reads while bouncing is occurring" that can give invalid button states such as repeated push signals from one ph sical push. So if we guarantee that all bouncing is done b the time we next read the button# we5re good. Well# almost good# if we5re luck ...

!oise, We -on7t &ave !o Stin0ing !oise1


*on5t weU Well# ma be we don5t. @r ma be we do. Sometimes. Microcontrollers often live among high7energ beasts# and often control the beasts. ,igh energ devices make electrical noise# sometimes great amounts of electrical noise. %his noise can# at the worst possible moment# get into our delicate button7and7high7value7pullup circuit and act like a real button push. @ops# missile launched# sorr N

99E

If the noise is too intense we cannot filter it out using onl software# but will need hardware of some sort !or even a redesign". <ut if the noise is onl occasional# we can filter it out in software without too much bother. %he trick is that instead of regarding a single button 5make5 or 5break5 as valid# we insist on 6 contiguous makes or breaks to mark a valid button event. 6 will be a factor of our button scanning rate and the amount of filtering ou want to add. <igger 6 gives more filtering. %he simplest filter !but still a big improvement over no filtering" is $ust an 6 of 2# which means compare the current button state with the last button state# and onl if both are the same is the output valid. 6ote that now we have not two but three button statesA active !or pressed"# inactive !or released"# and indeterminate or invalid !in the middle of filtering# not et filtered". In most cases we can treat the invalid state the same as the inactive state# since we care in most cases onl about when we go active !from whatever state" and when we cease being active !to inactive or invalid". With that simplification we can look at simple 6P2 filtering !representing buttons using Method 2"# reading = buttons wired active7low to 0C1 PC>7PCEA U 9 2 E = I G M F O 9 > 9 9

u8 6i%te7'buttons'E2($oid) ( st&ti5 u8 %&st'buttons 0" u8 7&)'buttons" u8 6i%te7ed'buttons"

-- u> to 8 buttons, &%% in&5ti$e

7&)'buttons J3FE4 I 0D06" -- 7e&d 4 buttons (3FE4 is &5ti$e-%o)), &5ti$e st&te W1W 6i%te7ed'buttons 7&)'buttons I %&st'buttons" -- E 2 6i%te7ing %&st'buttons 7&)'buttons" -- s&$e 6o7 neDt 6i%te7ing 7etu7n 6i%te7ed'buttons" -- on%y &5ti$e i6 &5ti$e t*is ti?e &nd %&st ti?e ,

%he function filterVbuttonsV62!" must be called no more often than our debounce time !9>ms". % picall it would be called ever 9>72Ims. Each time it reads the = active7low buttons on PI6C# inverts them to active7high# and filters them b 06*ing them with the last reading. @nl if both readings had a given button high !pressed" will the result be high !pressed". 6ote that we can filter up to F buttons at a time# one per bit position# with this method !9G if we used u9G button variables". %o expand to greater filtering !larger 6"# keep in mind that the filtering techni&ue essentiall involves reading the current button state and then either counting or reseting the counter. We count if the current button state is the same as the last button state# and if our count reaches 6 we then report a valid new button state. We reset the counter if the current button state is different than the last button state# and we then save the current button state as the new button state to compare against the next time. 0lso note that the larger our value of 6 the more often our filtering routine must be called# so that we get a filtered response within our specified I>ms deadline. So for example with an 6 of F we should be calling our filtering routine ever 2 7 Ims# giving a response time of 9G 7 =>ms !Y9>ms and XI>ms".

99=

%he details of the filtering depend on whether the value to be filtered is a simple binar !a single button"# or is a multi7value variable such as the state of a button matrix. If the value is a simple binar # we can do some tricks with bit7shifting to find our 6 consecutive states. 8or simplicit # let us first choose 6 to be F or 9G or E2 7 that is# the number of bits in a uF# u9G or uE2. )et5s choose a uF for this example. 6ow# ever time we sample the button state# we simpl shift that state into a uF 3accumulator3 variable. %hus the uF variable will maintain the last F button samples. When the variable contains >b>>>>>>>> then we have F contiguous >s# and we have a valid filtered 5>5 output. Similarl # when the variable contains >b99999999 then we have a valid filtered 595 output. 0n other value is indeterminate. So all we have to do is shift the current state into the variable and compare for > or Q> !remember that the 5Q5 operator is the bitwise inversion operator". %hus Q> is Q>b>>>>>>>> is >b99999999. @J# to be more correct# !uF"Q> is >b99999999. Since we are now using an entire b te !or word" to represent one button# this is a variant of Method 9. In this case our button is assumed to be active7low# wired to PCE. U 9 2 E = I G M F O 9 > 9 9 9 2

u8 6i%te7'button'E8($oid) ( st&ti5 u8 button'&55 0" button'&55 !! 1"

-- st&7t )it* $&%id in&5ti$e -- s*i6t out o%dest st&te, ?&1e ne) st&te & -- button on 343 is &5ti$e (%o)), s*i6t in -- 0 is in&5ti$e, J0 is &5ti$e, ot*e7s

i6 (J3FE4 I (1!!3)) button'&55 # 1" & 1 7etu7n button'&55" indete7?in&te ---7etu7n (button'&55 0 ot*e7)ise -,

J0) V 1 : 0" -- &%te7n&te%y, 7etu7n 1 6o7 &5ti$e,

(ut , Want An ! )f A1
6 values of F# 9G or E2 are especiall eas to deal with# but the ma not fit in with our timing constraintsL especiall # our timer tick rate. %hat5s @J# since other 6 values are also useable. 8or example# let5s choose an 6 of I. %hat means we want to use the last = inputs along with the current input in our filtering. If we were to use the method described above# we would have a valid filtered 5>5 with the b te >bxxx>>>>># and a valid filtered 595 with the b te >bxxx99999. ,mm# those 5x5s are a problem# but not a big one. We have two approaches to deal with them. We can either $ust 06* off those bits and compare to >b>>>>> or >b99999# or we can stuff in more bits ever time we read the button state and continue to compare with > or Q> to look for a valid filtered input. /sing the latter approach in our 6PI example# instead of adding a single bit into the accumulating variable# we5ll add = bits 7 >b>>>> if the current button state is 5>5# or >b9999 if the current button state is 595. %he general rule is to add in F76?9 bits of all >s or all 9s. %his even works for 6P9 7 we would then add >b99999999 or >b>>>>>>>># which is $ust the > or Q> that we will compare with.

99I

< the wa # there5s a simple wa to define the values we will 06* or @1 to our accumulator variable# based on the value of 6. %his is one application of the useful pattern !9XX4"79# which will give a value or mask of all 595s in the rightmost !least significant" 4 bits. U 9;de6ine BKAA@E'/.0 ((1!!(8-ET1)-1)
-- 6o7 E 5, t*is is 0b00001111

6ow our input and filtering routine looks like thisA U 9 2 E = I G M F O 9 > 9 9 9 2 9 E 9 = 9 I 9 G 9 M

;de6ine E ;de6ine BKAA@E'/.0

5 ((1!!(8-ET1))-1)

-- 6o7 E 5 t*is is 0b00001111

u8 6i%te7'button'E5($oid) ( st&ti5 u8 button'&55 0"

-- st&7t )it* $&%id in&5ti$e

1s

button'&55 !! 1" -- s*i6t out o%dest st&te i6 (J3FE4 I (1!!3)) button'&55 # BKAA@E'/.0" -- button on 343 is &5ti$e (%o)), %o&d in

e%se button'&55 I JBKAA@E'/.0" -- button is in&5ti$e (*ig*), %o&d in 0s 7etu7n button'&55" -- 0 is in&5ti$e, J0 is &5ti$e, ot*e7s indete7?in&te --- 7etu7n (button'&55 J0) V 1 : 0" -- &%te7n&te%y, 7etu7n 1 6o7 &5ti$e, 0 ot*e7)ise -,

E'panding This 8i ter .ethod To .u tip e ,ndependent (uttons


0s ou can see above# the bit7shift filter method can onl deal with one button at a time !in our examples# we hardwired that button to be PCE". @f course in the usual case we want a more general abilit to filter multiple buttons. %o do this we can create suitable data structures that encapsulate all the necessar data for a given button# and pass in the associated structure to a filter program for each button. U 9 ty>ede6 st7u5t 2 ( u8 b'nu?" -- t*e bit >osition o6 t*is button on ou7 button >o7t E u8 b'&55" -- t*e button &55u?u%&to7 99G

= I G M F O 9 > 9 9 9 2 9 E 9 = 9 I 9 G 9 M 9 F

u8 b'$&%" , B'D.A."

-- t*e boo%e&n button $&% (&5ti$e o7 ot*e7)

u8 6i%te7'button'E8(B'D.A. S >'bd) ( >'bd-=b'&55 !! 1" -- s*i6t out o%dest 0 i6 (J3FE4 I (1!!>'bd-=b'nu?)) >'bd-=b'&55 # 1" -- button on 343 is & 1 7etu7n >'bd-=b'&55" -- 0 is in&5ti$e, J0 indete7?in&te --->'bd-=b'$&% (>'bd-=b'&55 J0) V 1 : 0" --7etu7n (>'bd-=b'$&%" -- &%te7n&te%y, 7etu7n 1 -,

st&te, ?&1e ne) st&te & &5ti$e (%o)), s*i6t in is &5ti$e, ot*e7s

6o7 &5ti$e, 0 ot*e7)ise

,ere we are still hard7wiring the button port !PI6C"# but not the bits within the port. We can now maintain an arra of <V*0%0 structures# one for each button# and filter all the buttons in a loop. U 9 2 E = I G M F O 9 >

;de6ine EK8'B 6 B'D.A. Button'd&t&OEK8'BP

-- e%e?ents ?ust be initi&%i:ed be6o7e use

$oid 6i%te7'&%%'buttons'E8($oid) ( u8 i" 6o7 (i 0" i ! EK8'B" iTT) 6i%te7'button'E8(IButton'd&t&OiP)" 6o7 e&5* i ,

-- 7esu%ts &7e in b'&55 &nd b'$&%

We can also collect all of our filtered buttons into a button b te or word !Method 2" if we like. ,ere we have altered filterVbuttonV6F!" to return a 595 if the filtered button is active and a 5>5 otherwise# to make our bit shifting easierA

99M

U 9 2 E = I G M F O 9 > 9 9 9 2 9 E 9 = 9 I 9 G 9 M 9 F 9 O 2 > 2 9 2 2

u8 6i%te7'button'E8(B'D.A. S >'bd) ( >'bd-=b'&55 !! 1" -- s*i6t out o%dest st&te, ?&1e ne) st&te & 0 i6 (J3FE4 I (1!!>'bd-=b'nu?)) >'bd-=b'&55 # 1" -- button on 343 is &5ti$e (%o)), s*i6t in & 1 >'bd-=b'$&% (>'bd-=b'&55 J0) V 1 : 0" 7etu7n >'bd-=b'$&%" -- 7etu7n 1 6o7 &5ti$e, 0 ot*e7)ise , u8 6i%te7'&%%'buttons'E8($oid) ( i8 i" u8 b" u8 buttons 0" 6o7 (i EK8'B - 1" i = 0" i--) -- noti5e 7e$e7se 5ount o7de7, to &55o?od&te %e6t s*i6t ( b 6i%te7'button'E8(IButton'd&t&OiP)" -- 7esu%ts &7e in b'&55 &nd b'$&% 6o7 e&5* i buttons (buttons !! 1) # b" -- b ?ust be 0 o7 1 , 7etu7n buttons" ,

When -oes A (utton Press End?


It5s prett clear when a button press begins# but when does it endU Imagine a perfect button 7 no bouncing# no noise. %his button is in a vending machine# and pressing the button once is intended to cause the machine to deliver one can of %a:manian ColaA U 9-- &ssu?e buttons 6i%te7ed &nd 5*e51ed e$e7y 50?s 6i%te7'buttons()" -- G3F@, 6i%te7ing det&i%s donWt ?&tte7 2u8 buttons E i6 (buttons DFC32EC2'A.C8.EF.E'4@0.'BKAA@E) = dis>ense't&s?&ni&n'5o%&()" 99F

I So how man cans of %asmanian Cola will ou getU %rust me# ou had better be ver thirst if ou5re using this vending machine# because ou5ll get 2> cans per second for as long as ou hold down the buttonN %he problem is that the software is looking to see if the button is pressed !looking at the button state"# when it should be looking to see if the button has been pressed once !looking at a button event". %he difference is critical. %he button state $ust is what it is# but a button event must be consumed when used so as not to deceive the code into thinking the multiple button events have occurred. In this# buttons !and ke s" are different from switches# where it is generall the state that one is interested in. %o turn button pushes into button events we need to think in terms of button flags. 0 flag will be set when a button push occurs# and cleared when that button push is consumed 7 that is# when acted on b the software. 6ote carefull that the flag is not set whenever the button is found to be pushed !active"# but onl when the button is first pushed !first goes active 7 the 3leading edge3 of the button push". We will revisit this restriction when we look at auto7 repeating# but for now we will hold to this restriction. <ut for nowA

8lag is set when button goes from inactive or indeterminate# to active 8lag is cleared when software consumes 7 that is# acts upon 7 the button

)et5s assume a b te of filtered buttons using Method 2A U 9 2 E = I G M F O 9 > 9 9 9 2 9 E 9 = 9 I 9 G 9


---u8 u8 u8 u8

dete5t button e$ents buttons" ne)'buttons" %&st'buttons button'6%&gs ----7&) 6i%te7ed buttons &%% buttons t*&t Nust )ent 67o? 0 to 1 %&st 7&) &5ti$e buttons &%% buttons t*&t *&$e gone &5ti$e

0" 0"

buttons get'6i%te7ed'buttons()" ne)'buttons buttons I J%&st'buttons" -- 1s 6o7 e&5* button t*&t )ent 67o? 0 to 1 button'6%&gs # ne)'buttons" -- &dd &%% ne) button >us*es to eDisting >us*es %&st'buttons buttons" -- 6o7 neDt ti?e t*7u --- 5onsu?e & button e$ent -i6 (button'6%&gs I (1!!8Q'BKAA@E)) -- dete5t 8Q'BKAA@E e$ent ( button'6%&gs I J(1!!8Q'BKAA@E)" -- 5onsu?e 8Q'BKAA@E e$ent <<>7o5ess 8Q'BKAA@E<< ,

99O

M 9 F 9 O 2 > 2 9

(uttons ,n Combination
So far we have been looking at button event data as being Method 9 or Method 2 data !that is# independent booleans"# but this is often not the case. Man times# such as with a ke pad or ke board# we want a single code or value to represent all of the buttons being pressed. %his also makes debouncing easier because we are $ust debouncing one value at a time# not 6 different buttons. 8or now let5s $ust assume a 9G7button ke pad and a scanning routine that returns a raw value from the button or buttons being pushed on the ke pad. )et5s also specif an example encodingA

>A 9 9A 2 2A E EA = =A I IA G GA M MA F FA O OA 9> EZ/0)A 99 C)E01A 92 ?A 9E 7A 9= \A 9I .A 9G 6o ke pushedA >

So these are the values returned b readVke pad!" below# and thus b filterVke pad!". %his is a prett simple calculator ke pad# without# for example# an decimal capabilit # but it will demonstrate the techni&ues. We will stipulate some function readVke pad!" which reads the ke pad buttons and returns one of the values specified above. 6ow how do we debounce thisU We will look for 6 contiguous identical ke pad values# $ust as we have been doing up to now. @nl the method will be a bit different.

92>

U 9 2 E = I G M F O 9 > 9 9 9 2 9 E 9 = 9 I 9 G 9 M 9 F 9 O 2 > 2 9 2 2 2 E 2 = 2 I

u8 6i%te7'1ey>&d($oid) ( st&ti5 u8 %&st'$&% 0" st&ti5 u8 5ount 0" u8 $&%" u8 6i%te7ed'$&% 0" $&% 7e&d'1ey>&d()" i6 ($&% + 0) ( i6 ($&% %&st'$&%) ( i6 ((5ount + 0) II (--5ount i6 done ( 6i%te7ed'$&% $&%" 7etu7n &s $&%id , , e%se ( 5ount HF0A2R'4@KEA" , , %&st'$&% $&%" 7etu7n 6i%te7ed'$&%" ,

-----

0 ?e&ns no buttons >us*ed deboun5e 6i%te7 5ount 7&) $&% t*is ti?e ou7 6i%te7ed 7etu7n $&%

-- get 7&) 1ey>&d $&%ue -- *&$e & 1ey>&d button >us* -- )eW7e 6i%te7ing t*is $&% 0)) -- 5ontinue 6i%te7ing I 5*e51

-- 6ound enoug* 5onse5uti$e $&%ues to

-- st&7t 6i%te7ing & ne) $&%

0s with all the other button filtering methods# this method will be called once ever filtering interval# which might be 979>ms. %he first check !if !val NP >"" checks for an button pushes. If none# the no7button7value of > is returned. %he second check looks to see if the new value

929

is the same as the last value. If it is then we are in the midst of filtering the value. If not !if a new val is detected" the filter count is loaded to its full filter value and filtering begins for the new value. %he third check !if we are in the midst of filtering" looks to see if the filter count has $ust been decremented to :ero. If so# the code reports this value as the filtered val# but will not continue to report it in subse&uent passes. %his method offers a few bonuses that ma not be readil apparent. In the first place# it returns true ke pad events. It will return the filtered ke pad value onl once !when count decrements from 9 to >"# as is desired# and will not continue to return it as buttons remain held down. In the second place# and related to the first# it allows us to add auto7repeat ver easil # as we will see in the next tutorial chapter. 0lso in the next tutorial chapter we will look at matrixed buttons and how to scan the matrix to read them.

Too .any (uttons, !ot Enough ,nputs


0ssigning one DPI@ input to each button can use up a lot of DPI@ pins. 6umeric input re&uires at least 9> buttons# plus however man additional control or function buttons. %his can &uickl get expensive# DPI@ pin7wise# and also connector7wise if the ke pad is off the uC PC< as it often would be. 0 ver common response to this expense is to wire buttons !ke s# etc" in a matrix. < connecting our buttons in an 1 b C matrix# we can read as man as 1\C buttons using $ust 1?C DPI@ lines. 8or our docking board =x= button matrix we re&uire = output pins and = input pins# for a total of F pins to read 9G buttons. ,ere is a schematic of a =x= button matrixA

922

In this schematic# our DPI@ outputs are connected to the rows# and our inputs are connected to the columns. %his could# of course# be reversed# with the pulldown.pullup resistors being moved to sta connected to the input lines. 6or does the la out of the buttons need to be a s&uare or rectangle 7 it can be an conceivable la out. Schematics are not mechanical drawings. 8inall # while we have shown pullup resistors here# the could $ust as easil be pulldown resistors. I used pullups because both the S%ME2 and the 0C1 have built7in pullups !the S%ME2 also has built7in pulldowns# but not so the 0C1". %his means that our active state or level will be 5>5 and our inactive state or level will be 595.

92E

So how does this all workU Well# with no button pushed !closed"# all of our inputs are pulled up to 9 !or down to ># if using pulldowns". 6ow suppose the user pushes the top left button. Columns 27= still remain pulled up to 9# but now column 9 is directl connected to row 9. If row 9 is either open !set as an input" or is an output set to 595# column 9 will be a 9 and indistinguishable from when the button is not pushed. <ut if row 9 is an output set to 5>5# column 9 will be a >. If we know that we are driving row 9# and onl row 9# to a 5>5# and we read a > on column 9 and onl on column 9# we can identif that the top left button is being pushed. So the wa to read a button matrix is to drive one row at a time to the active level !the opposite polarit of the pullup.pulldown resistors"# and read all the input columns# looking to see if an column is at the active level. %hen switch to the next row# and so on forever. %his is commonl called scanning the matrix. If we find a column at active level# we stop the matrix scan and generate a ke event 7 t picall we would number the event as !Column ? !1ow \ 6/MVC@)S"" or !1ow ? !Column \ 6/MV1@WS"" !either calculation is acceptable". %his encodes button event in the range > to 1\C79# assuming both 1ow and Column are counted up from >. Since it is common to want to reserve the > value as meaning no event !no buttons pushed"# we can add 9 to the event number to get events in the range 9 to 1\C. What do we do with the rows we are are not driving activeU We have two choices# as mentioned above. We can drive them to the inactive level# or we can set them as inputs and let the pullups.pulldowns drive the columns to the inactive level. If we drive them to the inactive level# we have the possibilit that 2 ke s are pressed at the same time such that the active row output is connected directl to an inactive row output. %his is not a good thing# but in m experience outputs can handle this condition briefl . 0 better hardware design in this case is to add low7value resistors !sa 2>> @hms" in series with each row line# which eliminates the possiblit of a dead short between outputs. @therwise# set the inactive row pins to inputs and then there can never be two outputs shorting together since there can never be two outputs enabled# onl one. So the choices are !a" do nothing and trust that the outputs can handle brief shorts# or !b" add small resistors in the row lines# or !c" onl set one row pin to output at a time.

Too 8ast ,s !ot Too /ood


It5s eas to forget that the world doesn5t react instantaneousl to our programs. Without thinking about the time scale involved# we ma set a row output high in one instruction and read the column inputs in the next instruction some I> or 9>>ns later# and expect to get a valid input. %he problem is# the world is full of parasitic 1s and Cs and )s# and signals don5t change instantl . If ou have a pullup of I>k@hms and a parasitic capacitance of 9>pf# that5s an 1C time constant of I>>ns# meaning our pullup is not going to produce a valid 5>5 until about 9.2 microsecond# or perhaps I7I> instructions# later. It5s important to remember $ust how fast our microcontrollers are# and to make sure we set outputs and switch inputs well before we expect to read valid inputs. We5ll see the same issue when switching 0*C input channels# for example. Plan ahead in our code la out to give our inputs plent of time to settle to their correct values before reading them. %his t picall involves rearranging code so that the outputs are set before a dela !ma be $ust the execution of some housekeeping code"

92=

or before leaving an IS1# and then the inputs are read at the end of the dela or at the beginning of the next triggering of the IS1.

Scanning A Beypad
So now we get down to some code. %he hardware has 9G buttons in = rows b = columns# as shown in the drawing above# but an number of rows and columns can be used depending on how man buttons must be read. 0s per the drawing above# we will use pullup resistors on the input columns 7 in particular# the pullup resistors built in to both the 0C1 and S%ME2 families of microcontrollers. Each row in turn will be driven low# and all columns will be scanned for a low input !a 595 after inverting the column data". 6ote finall that no debounce filtering is done in the ke pad scan routine. %hat is left to another routine# which $ust calls the ke pad scan routine to get the raw button data which it then filters. %his code is for 0C1# but the S%ME2 code will onl have minor differences in DPI@ setup and access.

U 9 2 E = I G M F O 9 > 9 9 9 2 9 E 9 = 9 I 9 G 9

;de6ine ;de6ine ;de6ine ;de6ine ;de6ine

R3'R@U'3@RA R3'R@U'DDR R3'4@0'3@RA R3'4@0'DDR R3'4@0'3FE

3@RA4 DDR4 3@RA4 DDR4 3FE4 4 4 4 0

-- 7o) %ines on >o7t 4 -- 5o% %ines on >o7t 4

;de6ine R3'R@UC ;de6ine R3'4@0C ;de6ine R3'HFRCA'R@U ;de6ine R3'HFRCA'4@0 ;de6ine R3'R@U'8.CR ;de6ine R3'4@0'8.CR u8 s5&n'1ey>&d($oid) ( u8 $&% 0" u8 7o)'nu?" u8 7o)'bit" u8 5o%'nu?" u8 5o%'bit" u8 5o%s"

-- 7o)s &7e 344-347 -- 5o%s &7e 340-343

0b11110000 0b00001111

-- &ssu?e no 1ey >7essed

R3'R@U'3@RA # JR3'R@U'8.CR" 7o)'bit 1 !! R3'HFRCA'R@U" 6o7 (7o)'nu?

-- set &%% 7o)s to 1 -- &5ti$e-*i bit 6o7 5u77ent &5ti$e 7o) -- 5*e51 e&5* 7o) in

0" 7o)'nu? ! R3'R@UC" 7o)'nu?TT)

92I

M 9 F 9 O 2 > 2 9 2 2 2 E 2 = 2 I 2 G 2 M 2 F 2 O E > E 9 E 2 E E E = E I E G E M E F E O = > = 9 =

tu7n ( R3'R@U'3@RA I J7o)'bit" -- set neDt 7o) to 0 .C.3 -- >ut &s ?u5* 5ode *e7e &s >ossib%e be6o7e 7e&ding 5o%u?n d&t& 7o)'bit !! 1" 5o%'bit 1 !! R3'HFRCA'4@0" -- bit?&s1 6o7 1st 5o%u?n 5o%s JR3'4@0'3FE I R3'4@0'8.CR" -- %oo1 6o7 &ny 0 5o%u?ns (1 &6te7 R3'R@U'3@RA # R3'R@U'8.CR" -- set &%% 7o)s to 1 .C.3

J)

6o7 (5o%'nu? 0" 5o%'nu? ! R3'4@0C" 5o%'nu?TT) -- 5*e51 e&5* 5o% 6o7 &5ti$e ( i6 (5o%s I 5o%'bit) -- t*is 5o% in &5ti$e st&te, 6ound & 1ey>7ess ( $&% (7o)'nu? S R3'4@0C) T 5o%'nu? T 1" -- T1 to di66e7enti&te 67o? no-1ey 7etu7n $&%" , e%se 5o%'bit !! 1" -- neDt 5o% ?&s1 , , 7etu7n $&%" -- no 1ey>7esses 6ound ,

92G

2 = E = = = I = G = M = F = O

)ur Sing e 3a ue (utton Code As State .achine


6ow that we have a ke pad scan routine# we can look at the debouncing and auto7repeat routines that can turn the raw ke pad scan data into single value button events. In the previous tutorial chapter we came up with this code for single value buttons !here using a E>ms filtering time 7 most times ou can probabl get b with 9I or 2>ms". 0ll timing is referenced to our tick interval %ICJVMS# which I have set to Ims in these examples $ust for some variet .

U 9 2 E = I G M F O 9 > 9 9 9 2 9 E 9 = 9

-- &%% de%&ys ?ust 6it in 8-bit $&%ue ;de6ine HF0A2R'4@KEA (30-AF4R'8C) u8 6i%te7'1ey>&d'o7igin&%($oid) ( st&ti5 u8 %&st'$&% 0" st&ti5 u8 5ount 0" u8 $&%" u8 6i%te7ed'$&% 0" $&% 7e&d'1ey>&d()" i6 ($&% + 0) ( i6 ($&% %&st'$&%) ( i6 ((5ount + 0) II (--5ount i6 done ( 6i%te7ed'$&% $&%" 7etu7n &s $&%id , , e%se (

-----

0 ?e&ns no buttons >us*ed deboun5e 6i%te7 5ount 7&) $&% t*is ti?e ou7 6i%te7ed 7etu7n $&%

-- get 7&) 1ey>&d $&%ue -- *&$e & 1ey>&d button >us* -- )eW7e 6i%te7ing t*is $&% 0)) -- 5ontinue 6i%te7ing I 5*e51

-- 6ound enoug* 5onse5uti$e $&%ues to

92M

I 9 G 9 M 9 F 9 O 2 > 5ount HF0A2R'4@KEA" 2 , 9 , 2 %&st'$&% $&%" 2 7etu7n 6i%te7ed'$&%" 2 , E 2 = 2 I 2 G 2 M 2 F

-- st&7t 6i%te7ing & ne) $&%

We can modif this code slightl to make the state machine action more explicit b adding an explicit state variable and explicit state changes. %his addition will give us pa back later when we add more complex features to our button code.

U 9 enu? (R'FD02 1, R'HF0A2RFEG, R'R2Q'2/2EA," 2 E u8 6i%te7'1ey>&d($oid) ( = st&ti5 u8 st&te R'FD02" I st&ti5 u8 5ount 0" -- deboun5e 6i%te7 5ount st&ti5 u8 %&st'$&% 0" -- 0 ?e&ns no buttons >us*ed G u8 $&%" -- 7&) $&% t*is ti?e M u8 6i%te7ed'$&% 0" -- ou7 6i%te7ed 7etu7n $&% F O $&% 7e&d'1ey>&d()" -- get 7&) 1ey>&d $&%ue 9 s)it5* (st&te) > ( 5&se R'FD02: -- )&iting 6o7 & 1ey 9 i6 ($&% + 0) -- *&$e & ne) 1ey 9 92F

( 9 5ount HF0A2R'4@KEA" 2 st&te R'HF0A2RFEG" 9 , E b7e&1" 9 5&se R'HF0A2RFEG: = i6 ($&% %&st'$&%) 9 ( I i6 ((5ount + 0) II (--5ount 9 ( G 6i%te7ed'$&% $&%" st&te R'R2Q'2/2EA" 9 , M , 9 e%se F st&te R'FD02" 9 t*7u b7e&1" O 2 5&se R'R2Q'2/2EA: > 1ey u> 2 i6 ($&% 0) 9 st&te R'FD02" b7e&1" 2 2 de6&u%t: 2 st&te R'FD02" E b7e&1" 2 , = %&st'$&% $&%" 7etu7n 6i%te7ed'$&%" 2 I , 2 G 2 M 2 F 2 O E > E 9 E 2 E E E = E I E G

-- st&7t 6i%te7ing t*is 1ey

-- 6i%te7ing & 1ey -- 1ee> 6i%te7ing t*is 1ey 0)) -- 6i%te7 5ount Nust )ent to 0

-- out>ut t*is 6i%te7ed 1ey $&% on5e

-- ne) 1ey 6ound, 5&t5* it neDt ti?e

-- &%7e&dy 7etu7ned & 1ey, )&it 6o7 -- 1ey *&s been %et u>, b&51 to id%e

-- s&6ety $&%$e -- &%)&ys 7e?e?be7 t*e %&st $&%

92O

E M E F E O = > = 9 = 2 = E = = = I = G Hou will see how the state moves from JVI*)E to JV8I)%E1I6D to JVJEHVECE6% and back to JVI*)E.

Auto +epeating
It is often desirable to generate multiple button events from a single button push. 8or example# our computer5s ke board auto7repeat does this. 0gain# each button event is used and consumed onl once# but multiple button events can flow from a single button push# triggering multiple button event responses. Since our button7event code is alread a small state machine# to add auto repeat $ust involves adding more states to cover the auto repeat behavior. %his auto repeat behavior involves two additional dela sA the dela from the first# immediate ke event to the first repeated ke event# and then the dela between each subse&uent repeated ke event. %he auto7repeat begins after F>>ms# and the repeat rate is =>>ms . ke .

U 9 2 E = I G M

-- &%% de%&ys ?ust 6it in 8-bit $&%ue ;de6ine HF0A2R'4@KEA (30-AF4R'8C) ;de6ine D20.Q'4@KEA (800-AF4R'8C) ;de6ine R3A'4@KEA (400-AF4R'8C) enu? (R'FD02 1, R'HF0A2RFEG, R'1CA'R2Q'2/2EA, R'R3A'2/2EA," u8 6i%te7'1ey>&d'&7($oid)

9E>

F O 9 > 9 9 9 2 9 E 9 = 9 I 9 G 9 M 9 F 9 O 2 > 2 9 2 2 2 E 2 = 2 I 2 G 2 M 2 F 2 O E > E 9 E 2 E E

st&ti5 u8 st&te R'FD02" st&ti5 u8 5ount 0" st&ti5 u8 %&st'$&% 0" u8 $&%" u8 6i%te7ed'$&% 0" $&% 7e&d'1ey>&d()" s)it5* (st&te) ( 5&se R'FD02: i6 ($&% + 0) ( 5ount HF0A2R'4@KEA" st&te R'HF0A2RFEG" , b7e&1"

-----

deboun5e 6i%te7 5ount 0 ?e&ns no buttons >us*ed 7&) $&% t*is ti?e ou7 6i%te7ed 7etu7n $&%

-- get 7&) 1ey>&d $&%ue -- )&iting 6o7 & 1ey -- *&$e & ne) 1ey -- st&7t 6i%te7ing t*is 1ey

5&se R'HF0A2RFEG: i6 ($&% %&st'$&%) ( i6 ((5ount + 0) II (--5ount ( 6i%te7ed'$&% $&%" 5ount D20.Q'4@KEA" st&te R'1CA'R2Q'2/2EA" , , e%se st&te R'FD02" t*7u b7e&1" 5&se R'1CA'R2Q'2/2EA: 1ey u> i6 ($&% %&st'$&%) ( i6 (--5ount 0) ( 6i%te7ed'$&% $&%" 5ount R3A'4@KEA" st&te R'R3A'2/2EA" , , e%se st&te R'FD02" b7e&1" 5&se R'R3A'2/2EA: 1ey u> i6 ($&% %&st'$&%) ( i6 (--5ount 0) ( 6i%te7ed'$&% $&%" 5ount R3A'4@KEA" , , e%se st&te R'FD02"

-- 6i%te7ing & 1ey -- 1ee> 6i%te7ing t*is 1ey 0)) -- 6i%te7 5ount Nust )ent to 0

-- out>ut t*is 6i%te7ed 1ey $&% on5e -- de%&y unti% 1st 7e>e&t 1ey

-- ne) 1ey 6ound, 5&t5* it neDt ti?e

-- &%7e&dy 7etu7ned & 1ey, )&it 6o7

-- 6i7st 7e>e&t 1ey -- 7e>e&t de%&y

-- &nyt*ing e%se, b&51 to id%e

-- &%7e&dy 7etu7ned & 1ey, )&it 6o7

-- &not*e7 7e>e&t 1ey

-- &nyt*ing e%se, b&51 to id%e

9E9

E = E I E G E , M E F E O = > = 9 = 2 = E = = = I = G = M = F = O I > I 9 I 2 I E I = I I I G I M I F

b7e&1" de6&u%t: st&te R'FD02" b7e&1" , %&st'$&% $&%" 7etu7n 6i%te7ed'$&%"

-- s&6ety $&%$e -- &%)&ys 7e?e?be7 t*e %&st $&%

9E2

I O G > G 9 G 2 G E G = G I G G G M G F G O M > M 9 M 2 M E M =

.u tip e +epeat +ates


0uto repeat buttons often have multiple repeat rates# whereb the repeat rate starts off slow and speeds up over time. %his too is fairl eas to add to our auto repeat code. ,ere is a 27 step auto repeat routine which speeds up the repeat rate after a specified number of repeated ke s. 0s before the auto7repeat begins after F>>ms# and the repeat rate is =>>ms . ke . 0fter I repeated ke s# the auto repeat rate increases to 2>>ms . ke . Since the example program for multiple repeat rates will be written for S%ME2 rather than 0C1# the multiple repeat rate routines below are written for S%ME2. Hou should be getting prett good at converting between 0C1 and S%ME2 code b now. %he scanVke pad!" routine as written for S%ME2 is also given below

9EE

U 9 2 E = I G M F O 9> 99 92 9E 9= 9I 9G 9M 9F 9O 2> 29 22 2E 2= 2I 2G 2M 2F 2O E> E9 E2 EE E= EI EG EM EF EO => =9 =2 =E == =I =G =M

-- &%% de%&ys ?ust 6it in 8-bit $&%ue ;de6ine HF0A2R'4@KEA (30-AF4R'8C) ;de6ine D20.Q'4@KEA (800-AF4R'8C) ;de6ine R3A'4@KEA (400-AF4R'8C) ;de6ine R3A'R2QC 5 ;de6ine R3A2'4@KEA (200-AF4R'8C) enu? (R'FD02 1, R'HF0A2RFEG, R'1CA'R2Q'2/2EA, R'R3A1'2/2EA, R'R3A2'2/2EA," ;de6ine R3'R@U'3@RA ;de6ine R3'4@0'3@RA ;de6ine R3'R@UC ;de6ine R3'4@0C ;de6ine R3'HFRCA'R@U ;de6ine R3'HFRCA'4@0 ;de6ine R3'R@U'8.CR ;de6ine R3'4@0'8.CR G3F@4 G3F@. 4 4 10 9 0b11110000000000 0b1111000000000 -- bits 10-13 set to W1W -- bits 9-12 set to W1W

u8 s5&n'1ey>&d($oid) ( -- sin5e CA832 >o7ts &7e 12 bits, )e ?ust use u16 $&7i&b%es to *o%d >o7t d&t&< u8 $&% 0" -- &ssu?e no 1ey >7essed u8 7o)'nu?" u16 7o)'bit" u8 5o%'nu?" u16 5o%'bit" u16 5o%s" R3'R@U'3@RA-=BCRR R3'R@U'8.CR" W1W 7o)'bit 1 !! R3'HFRCA'R@U" &5ti$e 7o) -- set &%% 7o) out>uts set to -- &5ti$e-*i bit 6o7 5u77ent

6o7 (7o)'nu? 0" 7o)'nu? ! R3'R@UC" 7o)'nu?TT) ( R3'R@U'3@RA-=BRR 7o)'bit" -- set neDt 7o) to 0 .C.3 -- >ut &s ?u5* 5ode *e7e &s >ossib%e be6o7e 7e&ding 5o%u?n d&t& 7o)'bit !! 1" 5o%'bit 1 !! R3'HFRCA'4@0" -- bit?&s1 6o7 1st 5o%u?n 5o%s JR3'4@0'3@RA-=FDR I R3'4@0'8.CR" -- %oo1 6o7 &ny 0 5o%u?ns (1 &6te7 J) R3'R@U'3@RA-=BCRR R3'R@U'8.CR"" -- set &%% 7o)s to 1 .C.3 6o7 (5o%'nu? 0" 5o%'nu? ! R3'4@0C" 5o%'nu?TT) ( i6 (5o%s I 5o%'bit) ( $&% (7o)'nu? S R3'4@0C) T 5o%'nu? T 1" -- T1 to di66e7enti&te 67o? no-1ey 7etu7n $&%"

9E=

=F =O I> I9 I2 IE I= II IG IM IF IO G> G9 G2 GE G= GI GG GM GF GO M> M9 M2 ME M= MI MG MM MF MO F> F9 F2 FE F= FI FG FM FF FO O> O9 O2 OE O= OI OG OM

, ,

, e%se 5o%'bit !!

1"

-- neDt 5o% ?&s1 -- no 1ey>7esses 6ound

, 7etu7n $&%"

u8 6i%te7'1ey>&d'&72($oid) ( st&ti5 u8 st&te R'FD02" st&ti5 u8 5ount" st&ti5 u8 1ey'5ount" st&ti5 u8 %&st'$&% 0" u8 $&%" u8 6i%te7ed'$&% 0" $&% 7e&d'1ey>&d()" s)it5* (st&te) ( 5&se R'FD02: i6 ($&% + 0) ( 5ount HF0A2R'4@KEA" st&te R'HF0A2RFEG" , b7e&1" 5&se R'HF0A2RFEG: i6 ($&% %&st'$&%) ( i6 ((5ount + 0) II (--5ount ( 6i%te7ed'$&% $&%" 5ount D20.Q'4@KEA" st&te R'1CA'R2Q'2/2EA"

-- deboun5e 6i%te7 5ount -- 0 ?e&ns no buttons >us*ed -- 7&) $&% t*is ti?e -- ou7 6i%te7ed 7etu7n $&% -- get 7&) 1ey>&d $&%ue -- )&iting 6o7 & 1ey -- *&$e & ne) 1ey -- st&7t 6i%te7ing t*is 1ey

-- 6i%te7ing & 1ey -- 1ee> 6i%te7ing t*is 1ey 0)) -- 6i%te7 5ount Nust )ent to

-- out>ut t*is 6i%te7ed 1ey $&% on5e

, , e%se st&te t*7u b7e&1"

R'FD02"

-- ne) 1ey 6ound, 5&t5* it neDt ti?e

5&se R'1CA'R2Q'2/2EA: 1ey u> i6 ($&% %&st'$&%) ( i6 (--5ount 0) ( 6i%te7ed'$&% $&%" 5ount R3A1'4@KEA" 1ey'5ount R3A'R2QC" st&te R'R3A1'2/2EA" , , e%se st&te R'FD02" b7e&1"

-- &%7e&dy 7etu7ned & 1ey, )&it 6o7

-- 6i7st 7e>e&t 1ey

-- &nyt*ing e%se, b&51 to id%e

9EI

5&se R'R3A1'2/2EA: OF 1ey u> OO i6 ($&% %&st'$&%) 9> ( > i6 (--5ount 0) ( 9> 6i%te7ed'$&% $&%" 9 i6 (--1ey'5ount 0) 9> ( 2 st&te R'R3A2'2/2EA" 9> 5ount R3A2'4@KEA" , E e%se 9> 5ount R3A1'4@KEA" = , 9> , e%se I st&te R'FD02" 9> b7e&1" G 9> 5&se R'R3A2'2/2EA: M 1ey u> 9> i6 ($&% %&st'$&%) ( F i6 (--5ount 0) 9> ( O 6i%te7ed'$&% $&%" 99 5ount R3A2'4@KEA" > , , 99 e%se 9 st&te R'FD02" 99 b7e&1" 2 99 de6&u%t: st&te R'FD02" E b7e&1" 99 , = %&st'$&% $&%" 99 7etu7n 6i%te7ed'$&%" I , 99 G 99 M 99 F 99 O 92 > 92 9 92 2 92 E

-- &%7e&dy 7etu7ned & 1ey, )&it 6o7

-- &not*e7 s%o) 7e>e&t 1ey -- st&7t 7e>e&ting 6&ste7

-- 5ontinue s%o) 7e>e&ts

-- &nyt*ing e%se, b&51 to id%e

-- &%7e&dy 7etu7ned & 1ey, )&it 6o7

-- &not*e7 6&st 7e>e&t 1ey

-- &nyt*ing e%se, b&51 to id%e

-- s&6ety $&%$e -- &%)&ys 7e?e?be7 t*e %&st $&%

9EG

92 = 92 I 92 G 92 M 92 F 92 O 9E > 9E 9 9E 2 9E E 9E = 9E I 9E G 9E M 9E F 9E O 9= > 9= 9 9= 2 9= E 9= = 9= I

9EM

Putting ,t A Together
,ere are two complete programs using our )C* to displa ke pad scanning# both using a Ims ke pad scan period. %he first is written for 0C1 and uses our basic filterVke pad!"# where one ke press produces one ke event. %he second is written for S%ME2 and uses our full 27stage auto7repeat code# filterVke padVar2!". %he code for the scan and filter functions will not be repeated here# $ust to keep the listing si:e down. %he function translateVke pad!" takes the output from the filter ke pad function and translates it into an 0SCII character. %his function would be modified to generate the encoding desired for a given application. Cideos are included that show the operation of both programs. ,ere is the 0C1 program. 0s each ke is pressed# it is displa ed on the )C*.

U 9 2 E = I G M F O 9 > 9 9 9 2 9 E 9 = 9 I 9 G 9 M 9 F 9 O 2 > 2 9 2

--- ./R'R2Q3.D -- s5&ns 4D4 1ey>&d -- using 5?s ti51 -;in5%ude ;in5%ude ;in5%ude ;in5%ude ;in5%ude ;in5%ude ;in5%ude ;in5%ude ;de6ine ;de6ine ;de6ine ;de6ine !stdio<*= !5ty>e<*= !&$7-io<*= !&$7-inte77u>t<*= Xde6s<*X Xde%&y<*X X%5d<*X X1ey>&d<*X H'43K A'3R2C4.02 AF4R'8C A'4A4 14745600 1024 5 (H'43K-A'3R2C4.02-(1000-AF4R'8C)) -- 72 6o7 5?s

$oid ti?e7'init($oid) ( @4R0. A'4A4 - 1" AF8CR0 (1!!@4F20.)" A44R0. (2!!UG800)" A44R0B (5!!4C00)" ,

-----

5?s ti51 en&b%e 4A4 inte77u>t 4A4 ?ode 1024 >7es5&%e, st&7t ti?e7

5*&7 t7&ns%&te'1ey>&d($oid) ( 5*&7 5" u8 $&% 6i%te7'1ey>&d1()" i6 ($&% 0) 5 0" e%se 5 W.W T $&% - 1" 7etu7n 5"

-- 7e&d $&%ue to t7&ns%&te -- 0 7e?&ins 0 -- 1<<16 be5o?es .<<3

9EF

2 2 E 2 = 2 I 2 G 2 M 2 F 2 O E > E 9 E 2 E E E = E I E G E M E F E O = > = 9 = 2 = E = = = I = G =

int ?&in($oid) ( 5*&7 bu6O04D'UFDA9T1P" 1ey>&d'init()" ti?e7'init()" sei()" %5d'init()" -- &6te7 sei, sin5e it 7eZui7es de%&ys %5d'dis>%&y(0, 0, XRKEEFEG<<<X)" -- >ut 6iDed teDt on5e %5d'dis>%&y(0, 2, XR2Q:X)" u16 1b'd% ?&1e'de&d%ine(0)" 5*&7 5" 5*&7 %&st'5 0" -- st&7t )it* i??edi&te de&d%ine

)*i%e(1) ( i6 (de&d%ine'7e&5*ed(1b'd%)) ( 5 t7&ns%&te'1ey>&d()" i6 (5 + %&st'5) ( s>7int6(bu6, XY5X, 5)" -- s>7int is o$e71i%% 6o7 t*is, but )e donWt 5&7e %5d'dis>%&y(5, 2, bu6)" %&st'5 5" , -- in t*is 5&se )e &7e 7e&ding 1ey>&d e$e7y ti51 -- but t*is is by no ?e&ns ne5ess&7y 1b'd% T AF4R'8C" , , ,

9EO

M = F = O I > I 9 I 2 I E I = I I I G I M I F I O G > G 9 G 2 G E G = G I G G G M G F G O M > M 9 M

9=>

2 M E M = %he next program is built for S%ME2 and uses filterVke padVar2!". < holding down a ke ou can see the initial ke press# then the I slow repeat ke s# then fast repeat ke s to the end of the line. Pressing a different ke will clear the displa to allow ou to see the repeat behavior as often as ou wish. <esides the necessar changes to the code to account for the S%ME2# there is one other change# which is that instead of using makeVdeadline!" and deadlineVreached!"# a simple tick flag is set in the tick IS1# and is checked for in the main loop. %his is much less versatile than the deadline approach# but where an action must be performed ever tick# it is clean and efficient# and a reminder that there are different wa s to do $ust about an thing. %here is also a small addition to the )C* librar # the function lcdVclearVrow!" which clears onl the designated row. 0s each ke is pressed it is immediatel displa ed. If the ke is held down# the auto7repeat action starts and the repeated ke s are displa ed to the end of the line. Pressing a different ke will clear the line and displa the new ke .

U 9 2 E = I G M F O 9> 99 92 9E 9= 9I 9G 9M 9F 9O 2> 29 22 2E 2=

--- CA832'R2Q3.D -- s5&ns 4D4 1ey>&d -- using 5?s ti51 -;in5%ude ;in5%ude ;in5%ude ;in5%ude ;in5%ude ;in5%ude !st?32610D<*= !stdio<*= Xde6s<*X X%5d<*X X1ey>&d<*X Xde%&y<*X 8

;de6ine 3R2C4.02

$oid set'G3F@(G3F@'Ay>eDe6 S >'>o7t, int bit, u32 $&%) ( i6 (bit ! 8) ( u32 >o7t'd&t& >'>o7t-=4R0 I J(0b1111 !! (4Sbit))" 5on6ig 6o7 t*is bit >'>o7t-=4R0 >o7t'd&t& # ($&% !! (4Sbit))" 5on6ig 6o7 t*is bit , e%se -- *i bits ( bit - 8"

-- 5%e&7 out -- &dd in ne)

9=9

2I 2G 2M 2F 2O E> E9 E2 EE E= EI EG EM EF EO => =9 =2 =E == =I =G =M =F =O I> I9 I2 IE I= II IG IM IF IO G> G9 G2 GE G= GI GG GM GF GO M> M9 M2 ME M=

u32 >o7t'd&t& >'>o7t-=4R9 I J(0b1111 !! (4Sbit))" 5on6ig 6o7 t*is bit >'>o7t-=4R9 >o7t'd&t& # ($&% !! (4Sbit))" 5on6ig 6o7 t*is bit , ,

-- 5%e&7 out -- &dd in ne)

$oid set'?u%ti>%e'G3F@(G3F@'Ay>eDe6 S >'>o7t, int 6i7st'bit, u32 $&%, int nu?'bits) ( )*i%e (nu?'bits-- + 0) ( set'G3F@(>'>o7t, 6i7st'bit, $&%)" 6i7st'bitTT" , , $oid 1ey>&d'init($oid) ( set'?u%ti>%e'G3F@(G3F@., 9, 0b1000, 4)" 3.9-12, >u%%u>s R3'4@0'3@RA-=@DR # R3'4@0'8.CR" set'?u%ti>%e'G3F@(G3F@4, 10, 0b0010, 4)" 3410-13 R3'R@U'3@RA-=BCRR R3'R@U'8.CR" ,

-- 4 button in>ut 5o%u?ns -- en&b%e 4 >u%%u>s -- 4 button out>ut 7o)s -- 4 7o) out>uts set to W1W

$oid ti?e7'init($oid) ( CysAi51-=0@.D (H'43K-1000)SAF4R'8C-1" -- AF4R'8C ti51 (-1 7u%e+) CysAi51-=4AR0 0b111" -- inte7n&% 5%o51, en&b%e ti?e7 &nd inte77u>t , $oid %5d'5%e&7'7o)(u8 7o)) ( 5*&7 bu6O04D'UFDA9T1P" ?e?set(bu6, W W, 04D'UFDA9)" bu6O04D'UFDA9P 0" %5d'dis>%&y(0, 7o), bu6)" , 5*&7 t7&ns%&te'1ey>&d($oid) ( 5*&7 5" u8 $&% 6i%te7'1ey>&d1()" i6 ($&% 0) 5 0" e%se 5 W.W T $&% - 1" 7etu7n 5" , int ?&in($oid) ( 5*&7 bu6O04D'UFDA9T1P" bu6O0P 0"

-- 7e&d $&%ue to t7&ns%&te -- 0 7e?&ins 0 -- 1<<16 be5o?es .<<3

9=2

MI MG MM MF MO F> F9 F2 FE F= FI FG FM FF FO O> O9 O2 OE O= OI OG OM OF OO 9> > 9> 9 9> 2 9> E 9> , = 9> I 9> G 9> M 9> F 9> O 99 > 99 9 99

R44-=4HGR

0"

-- 9CF, 8 89:, R44'.3B22ER'F@3.2E" -- en&b%e 3@RA. R44'.3B22ER'F@342E" -- en&b%e 3@RA4 R44'.3B22ER'F@3D2E" -- en&b%e 3@RAD 6o7 b&51%ig*t

R44-=.3B22ER # R44-=.3B22ER # R44-=.3B22ER # 1ey>&d'init()" ti?e7'init()" %5d'init()"

%5d'b&51%ig*t(@E)" %5d'dis>%&y(0, 0, XRKEEFEG<<<X)" 5*&7 %&st'5 0" 5*&7 %&st'%5d'5 )*i%e(1) ( 5*&7 5" i6 (Ai51'6%&g) ( 5 t7&ns%&te'1ey>&d()" i6 (5 II (5 + %&st'5)) ( i6 (5 + %&st'%5d'5) ( %5d'5%e&7'7o)(2)" bu6O0P 0" , s>7int6(bu6, XYsY5X, bu6, 5)" %5d'dis>%&y(0, 2, bu6)" %&st'%5d'5 5" , %&st'5 5" Ai51'6%&g 0" ,

0"

9=E

2 99 E 99 = 99 I 99 G 99 M 99 F 99 O

9==

Você também pode gostar