Escolar Documentos
Profissional Documentos
Cultura Documentos
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.
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".
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.
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"
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.
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.
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".
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
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.
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"
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.
So here is our 0C1 Embedded ,ello World blink program# step b step.
9M
%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)"
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.
<ut there5s another wa that eliminates almost half of the loop codeA
%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.
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)"
)*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
29
< 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.
)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)" , ,
-- 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.
2=
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"
)*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)" , ,
<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.
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
E9
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
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".
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.
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 , =
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
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
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
=>
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
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 .
=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.
==
=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"
-- 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)"
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
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)"
(1!!9)"
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
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.
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.
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
I=
;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()"
-- 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
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 #
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)"
, ,
IM
IF
%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.
G>
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 = =
;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
( ,
0)
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
GE
F E O = > = 9 = 2 = E = = = I = G = M = F = O I > I 9 I 2
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
%his generates two unrelated but stable timing loops in a ver simple wa .
GI
;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" , , ,
-- 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
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
;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"
$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)
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)"
(1!!8)"
GO
I 2 I E I = I I I G I M I F I O G > G 9 G 2 G E
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.
M9
$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
)*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
ME
;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"
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
)*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" , ,
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...
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
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@.
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
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>
F9
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
;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
$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 ,
$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
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
$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)"
FI
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
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.
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>
O9
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
O2
!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.
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=
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.
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.
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!".
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
, -- 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
$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'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 ,
$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
$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'B0" J04D'B0"
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
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
9>F
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
9>O
!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.
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.
%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
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 ...
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
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
-- 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 -,
(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
5 ((1!!(8-ET1))-1)
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 -,
= 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."
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
,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 >
$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 ,
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" ,
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
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
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
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.
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.
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 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"
0b11110000 0b00001111
-- set &%% 7o)s to 1 -- &5ti$e-*i bit 6o7 5u77ent &5ti$e 7o) -- 5*e51 e&5* 7o) in
92I
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
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
92M
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
-- 6i%te7ing & 1ey -- 1ee> 6i%te7ing t*is 1ey 0)) -- 6i%te7 5ount Nust )ent to 0
-- &%7e&dy 7etu7ned & 1ey, )&it 6o7 -- 1ey *&s been %et u>, b&51 to id%e
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>
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
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
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 =
9EE
-- &%% 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=
, ,
, e%se 5o%'bit !!
1"
, 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
R'FD02"
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"
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
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"
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
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"
9=9
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 , ,
$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"
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
%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==