Sie sind auf Seite 1von 56

Purdue University C

AVR Simulation with the ATMEL


AVR Studio 4

Updated: 8/3/2005

Page 1 9/27/2005
Purdue University C

Introduction

The AVR Studio 4 is an Integrated


Development Environment for debugging AVR
software. The AVR Studio allows chip
simulation and in-circuit emulation for the AVR
family of microcontrollers. The user interface
is specially designed to be easy to use and to
give complete information overview. The AVR
uses the same user interface for both
simulation and emulation providing a fast Figure 1. AVR Studio
learning curve.

Getting Started

The AVR Studio uses a COF object file for simulation. This file is created with through the C
compiler by selecting COF as the output file type. For more information on creating this file, see
the C compiler documentation. Launch the AVR Studio by either selecting it through the Start
Menu or by selecting the program icon (if available). Either method will produce the IDE shown
below in figure 2. Once the IDE is running, select File Open through either the File Pull-down
Menu or by clicking on the File Open Button.

1. File Open

Figure 2. The AVR IDE

Page 2 9/27/2005
Purdue University C

Select the desired COF file for simulation


through the File Open window. Note this
window uses standard Windows navigation.
Either double clicking on the file or by clicking
on the file and then selecting the Open Button
can open the file.

1. Select the proper


*.cof file

Figure 3. File Open

Device Selection
After the source file has been opened, the device and debugging platform must be specified.
When doing simulation, select the AVR Simulator option and ensure that the proper AVR target
device is selected. Once the correct target AVR microcontroller and platform have been selected,
click on the Finish Button.

2. Ensure the Device


is correct

1. Select the
AVR
Simulator

Figure 4. Device and Debug Platform


3. Click Finish

Page 3 9/27/2005
Purdue University C

IDE Windows
The IDE has several windows that provide important information to the user. These windows
may be opened automatically by the software or may need to be activated by the user.
Regardless of how the windows are activated, they can be moved and resized to fit the taste of
the user. The main windows of interest are the Workspace, Source Code, Output, and Watch
windows. These can be seen below in figure 5.

1. Watch 2. Workspace 3. Output Window


Window Window Icon Icon
Icon

Figure 5. IDE Windows


Output Source Code

Workspace Watch

Page 4 9/27/2005
Purdue University C

Workspace Window
The Workspace window shown at the right holds important
information about the microcontroller. Clicking on the
expand symbols will provide detailed information about the
selected item. For instance, expanding the I/O selection
produces detailed information about the microcontroller
ports, timers, USART, etc. These views are vital for
simulating microcontroller software. They allow the user to
monitor the values as well as introduce new information as
inputs into the system. Additional information may be
hidden from the user if the window is too narrow. Click
and drag the right side of the window to re-size it and
reveal possible hidden information. Double-clicking on the 1. Click to Expand
value of one of the PIN registers allows the user to enter or
set the input value for that particular port. Likewise, the
user may click on one of the boxes under the bit position to
set an input.

1. Expand
Additional
Layers

2. Expand to See Figure 6. Workspace


Additional Registers

3. Double-Click to 4. Click to Set


Change Value Inputs

Figure 7. Expanded Workspace Figure 8. Expanded Port View

Page 5 9/27/2005
Purdue University C

Watch Window
The Watch Window allows the user
to monitor the variables used in the
software. To Add a variable to be
monitored, right-click in the window
and select Add from the menu. The
next line or box in the Watch
window is bounded by a box and
has a flashing cursor present inside
of it. Type the name of the variable
EXACTLY as it appears in the
source code. It should be noted
that local variables are only valid
when inside the function where they
reside. Figure 9. The Watch Window

1. Right Click in
the Watch
Window

Figure 10. Watch Window Menu


2. Select Add
From
the Menu

3. Type
Variable Name
Here

Figure 11. Entering the Variable Name

Page 6 9/27/2005
Purdue University C

Output Window
The Output window provides feedback to the user. This includes messages about the
microcontroller, object file, etc.

Figure 12. The Output Window

Simulator Options

Before actually starting a simulation, the Frequency of the


target AVR should be set. The MegaAVR Development
board from PRLLC operates at a clock frequency of
6.0MHz. This frequency happens NOT to be one of the
selections from the pull-down menu. Therefore, the user
must manually type this value into the program.

1. Select the Debug Menu

3. Type the
Figure 13. Debug Pull-Down Menu Clock Frequency

2. Select
Simulator
Options Figure 14. Simulator Clock Frequency

Page 7 9/27/2005
Purdue University C

Source Code Simulation Controls


IDE Toolbar

Once the IDE has been configured and the windows are positioned to the satisfaction of the user,
the actual simulation of the source code can begin. The yellow arrow indicates the next
statement to be executed. The toolbar located in figure 14 shows a list of possible options to
execute the source code. These options include Step Into (Single Step), Step Over, Step Out,
Run to Cursor, Auto Step, and Run. In addition to the previous functions, the user can also Set
Breakpoints, Reset, and Stop Debugging.

Step Step Step Run to


Break Reset Into Over Out Cursor
Run
AutoStep

Stop
Toggle
Breakpoint

Figure 15. Simulator Toolbar

Next Statement

The yellow arrow shown below in Figure 15 points to the next instruction to be executed. This
provides the user a visual indication of the future instruction or function to be performed.

Figure 16. Source Code

Page 8 9/27/2005
Purdue University C

Step Into

Perhaps the most commonly used operation when simulating software is the Step Into
operation. The operation can also be referred to as Single Stepping. This command
allows a single instruction to be executed at a time. This includes instructions that are located
inside of a function call and thus the name Step Into. Executing a Step Into command is
accomplished by either clicking on the icon or by pressing the F11 key. Performing this
command on the sample program shown in Figure 17 causes the next instruction to be
performed.

Figure 17. Step Into Function

In this example, the next instruction is a function call to a function named init_hardware( ) that will
initialize the hardware for the microcontroller. Figure 18 shows the results of executing the Step
Into command. The yellow cursor is now pointing to the first statement of the function.
Performing an additional Step Into executes the current statement and moves to the next
statement in the function. This process can be repeated throughout the software. Figure 19
shows the value of the PORTA data register prior to and immediately after execution of the
instruction shown in Figure 17.

Figure 18. First Line in Function

Figure 20 shows the cursor pointing to the


last line in the function. Executing a single step again Figure 19. PORTA Values
takes the cursor and thus the program back to the next
statement in the main function shown in Figure 21.

Figure 20. Last Line of Function Figure 21. Return to Main Function

Page 9 9/27/2005
Purdue University C

Step Over

The Step Over command causes the simulator to give the illusion that it skipped over the
function without executing the individual statements located inside of it. For example,
revisiting the same sequence from the Step Into discussion, this time, instead of using the Step
Into command, a Step Over command will be used. Figure 22 shows the main function just prior
to the execution of the init_hardware( ) function. Figure 23 shows the status of the I/O
configuration registers prior to the execution of the function. Executing the function using the
Step Over command produces the results shown in Figures 24 and 25. Figure 24 shows the
status of the I/O configuration registers proving that all the instructions were executed from a

Figure 22. Main Prior to Function Call

Figure 23. I/O Status Prior Figure 24. I/O Status After

Figure 25. Main After Function Call

single command. This technique is extremely useful when the user isn’t interested or concerned
with watching each and every instruction in a function being executed. This command can also
be used as a time saving technique while performing a simulation. The Step Over command can
be executed by clicking on the Step Over icon or by pressing the F10 key.

Page 10 9/27/2005
Purdue University C

Step Out

The Step Out command is executed inside a function when the user wants to return back to
the calling function without having to execute each individual step of the function. For
instance, the user may be interested in single stepping through the first several instructions inside
a function. Once through the area of interest, the user wants to return to the function from which
the current function was called in a single command. Figure 26 shows the cursor located in the
middle of the init_hardware( ) function. Assuming that the user has already executed the

Figure 26. Inside the Function

instructions of interest, executing a Step Out command finishes the remainder of the instructions
inside the function and places the cursor at the next instruction in the calling function as shown in
Figure 27. This command can also be used to save time while simulating software. This
command is executed by clicking on the Step Out icon or by pressing the F11 key while holding
down the SHIFT key.

Figure 27. Returning to the Calling Function

Auto Step

The Auto Step function can be viewed as the PC executing a series of Step Into
commands automatically. This command can be executed by either clicking on the Auto
Step icon or by pressing the F5 key while holding down the ALT key. Once this command is
started, each statement will be executed in order. The cursor and yellow arrow will still indicate
the next statement to be executed. However, since the PC is executing single steps
continuously, the yellow arrow and cursor are also moving constantly. The views inside of the
Workspace window are also updated after each instruction is executed allowing the user to
actually see the changing values in registers. The ability to view the changing value is a very
important feature associated with this command. It must be noted that the simulator is capable of
changing the values located inside the Workspace but the user is not. The simulation must be
stopped prior to the user manipulating these values.

Page 11 9/27/2005
Purdue University C

Break

The Break function allows the user to stop a simulation that is under the control of the PC.
This command allows a user a stop or pause a simulation that is using the Auto Step
function, for example, to enter new data into the Workspace or to modify a variable located in the
Watch window. Once stopped, a simulation can be re-started by choosing any of the methods
previously described.

Reset

The Reset command will perform the same function as a Real reset on the actual hardware
would produce. The cursor and thus the next statement to be executed will be set as the
very first instruction as if the file was just opened. In addition, the values of the registers will also
be set to the reset state. The values of variables used in the software will not be affected by this
command. The variables will retain their values until a process overwrites them. A Reset can be
executed by either clicking on the Reset icon or by pressing the F5 key while holding down the
SHIFT key.

Breakpoints

Breakpoints allow the user to start a simulation free running and have the simulation stop
when a certain instruction or place in the program is reached. Breakpoints can be set by
placing the cursor at a specific line in the software and then selecting the breakpoint icon or by
pressing the F9 key. The breakpoint can be cleared by pressing the breakpoint icon again or by
pressing the F9 key (a second time) while the cursor is located on the line that the breakpoint is
located. A Red Dot indicates that the line has a breakpoint.

2. Red Dot Indicates Breakpoint


1. Cursor Indicates the Selected Line

Figure 28. Breakpoints

Run

The Run option allows the software to be simulated as quickly as possible. The downside
of this option is that none of the registers or variables will be updated (visually to the user)
while the simulation is in process. The user must manually stop the simulation by using the break
command. Alternatively, the user can set a breakpoint on a particular line of code that will stop
the simulation when encountered.

Page 12 9/27/2005
Purdue University C

Source Code Simulation of ATMEGA 16 Functions

Now that the AVR Studio Software has been discussed in detail, simulating individual functions of
the ATMEGA 16 will be explored. In order to walk through the steps necessary to simulate the
functions of the ATMEGA 16, one entire software project has been developed. The project will be
broken down into the individual functions and discussed. But before the discussion of the
functions begins, a description of the project is needed.

The project chosen for this tutorial was a version of the Simon Game. However, this version of
the Simon Game is a bit more complicated than the original. There are eight lights and
pushbuttons, a buzzer and two output displays involved in this game. Also included is an on/off
button. The basic operation of the game is as follows:

1st round – randomly one of the eight lights blinks. The game then waits for the
corresponding pushbutton to be pressed. Once the correct pushbutton is
pressed then the second round begins.
2nd round - the first light blinks and then a random second light blinks. The game
then waits for the two corresponding pushbuttons to be pressed. Once
the correct pushbuttons are pressed, the third round begins.
3rd – 20th round - the game keeps going in this vain with the sequence increasing by one
each round until the user completes the game or makes a mistake.

That describes the basic operation of the game but does not touch on the details. For instance,
at the end of each sequence, a short beep will be produced to indicate that the sequence pressed
by the user was indeed correct. The user must respond within 2 seconds by pressing one of the
pushbuttons. If a pushbutton is not pressed within that time frame the game ends. The game will
also end if the correct pushbutton is not pressed in the correct sequence.

Two output displays are involved in this game. One is an LCD and the other is a terminal window
in the PC connected to the system. There is nothing special about the LCD being used for this
project. Also the terminal window used with this project and discussed in this tutorial is assumed
to be the terminal that comes standard with the CodeVisionAVR software package, see AVR
documentation for details.

A toggle switch will be used to determine which display will be selected for use during any point of
the game. The two displays will show the exact same data. The list of things shown to the user
includes the number of lights in the current sequence, the state of the game, and the result of the
game when the game is complete. The result contains a message for the user dependent on
how well the user was able to keep up with the sequence of lights. The message identifies the
player as a ‘loser’ for getting 5 or less correct responses, a ‘nerd’ for getting more than 5 but less
than 12 correct responses, a ‘winner’ for getting more than 12 but less than 20 correct responses,
or as ‘Simon the Conqueror’ for completing the game’s 20 rounds successfully. For example
below are a couple of sample displays.

Sample Display: Playing 1, 2, 3 (example for a game in progress on the 3rd round)
Done. You are a loser!!!! (example of a completed game)

So now that the project has been described, the tutorial will take the user step-by-step through
some of the ATMEGA16 functions included in this project. Those functions will be basic
input/output, timers, and serial communication functions.

In order to make the process of simulation as simple as possible only segments of the code will
be used for each section of the tutorial. However, once the tutorial is finished, the user will be
able to simulate the entire project successfully by using the knowledge gained by each step of the
tutorial.

Page 13 9/27/2005
Purdue University C

Basic I/O Functions

The basic I/O functions of the ATMEGA16 consist of the ability to bring information into the
controller or send information from the controller. These functions are the simplest way the
controller has to communicate with the outside world. The ATMEGA16 contains 4 ports which
can be bit configured as either input ports or output ports. Both the input operation and the output
operation will be discussed.

In order to step the user through the process of input and output operations, a section of code
from the project must be created and loaded into the AVR Studio software. The code to load into
the Studio software is attached in Appendix A.

There will be two separate procedures discussed in this section of the tutorial. The first
procedure will walk through the steps necessary to simulate the output of a “light” on PortC. The
code being simulated assumes that PortC will be connected to eight individual LEDs. This
requires that a high (5 V) be placed on a bit to “light” the LED or a low (0 V) be placed on a bit to
“extinguish” the LED.

The second procedure will walk through the steps necessary to simulator the input of a “pressed”
pushbutton on PortA. The code assumes that PortA will be connected to eight individual
pushbuttons. The pushbuttons will output a high (5 V) when “pressed” or a low (0 V) when not
“pressed”.

One more thing must be discussed before the procedures can begin. One aspect of this project
is the random light generation. That is not within the scope of this tutorial. So for the procedures
described in this section, the random number generation will not be completely functional. The
number used in this section of code will start with 5. So the first time through the simulation, the
first light displayed is the 6th LED from the right or the LED “connected” to bit 5 of PortC. After the
first iteration the random generation will be functioning as intended. Also in the complete project
attached the random generation will be completely functional.

Studio Setup for Output or Input Simulation

Step 1 Load the code from Appendix A into the Studio software. Follow the instruction discussed
earlier in the tutorial to load the simulator.

Step 2 Once the code has been loaded and is ready to simulate the operation, expand the I/O
ATMEGA16 option in the Workspace Window. After that has expanded, also expand the
PORTA and PORTC options.

With both PORTA and PORTC expanded, the user will see three registers listed under
each port. The registers are PORTX, DDRX, and PINX, where X is the port designation,
A, B, C, or D.

PORTX - The register associated with


outputting information on the port if
configured as an output.

Figure 29. Port registers


DDRX - The register associated with
PINX - The register associated with setting the direction (in or out) the data
reading (inputting) information from the will flow through the microcontroller.
port if configured as an input. This register is also known as the data
direction register.

Page 14 9/27/2005
Purdue University C

Step 3 To make it easier to follow what is happening in the during the simulation, it may be
necessary to add a number of variables to the Watch Window. The main variables
recommended for addition to the Watch Window are state, random, game_index, press,
x, LED_seq[], and PB_seq[].

Figure 30. Watch Window variables

Output Function Procedures

This section will step the user through the process of simulating an output from the
microcontroller. Each step in this procedure signifies the user pressing the Step Into
option once.

Step Into This will configure the register DDRA as an input or the DDRA will now contain
0x00. The simulator shows zeros as an empty box. Therefore, you will not see
anything really change in the Workspace Window. Notice that the yellow arrow is
now pointing at the next line of code to be executed.

Figure 31. Status after first Step Into

DDRA is now 0x00. Cursor on next line.

Page 15 9/27/2005
Purdue University C

Step Into Now the PORTA register has been configured with an internal pull-up resister on
each bit. A solid check box signifies a 1 or high in that bit position.

Figure 32. Status after second Step Into

Step Into Now the DDRC register has been configured for output. A solid check box signifies
a 1 in that bit position.

Figure 33. Status after third Step Into

Step Into The next Step Into will configure the PORTC register to output all zeros. Therefore
the PORTC boxes will be empty.

Page 16 9/27/2005
Purdue University C

Step Into This step will place the value ‘5’


into the variable random. This
can be viewed in the Watch
Window.

Figure 34. Watch window update

Step Into The cursor will move down the code to the first case statement.

Step Into Again the only thing that really happened visually was that the cursor moved. But
the next line of code to be executed will actually do something this next time. So
why not get ready for it.

The first step is to look at what the line of code will be doing. This line of code will
be placing the value from random into the element designated by game_index of
array LED_seq[]. It might be beneficial to look at the variables of interest. The
array LED_seq[] has already been placed
in the Watch Window. But all of the
elements are not visible. To make those
elements visible, it is necessary to expand
the array by clicking on the + symbol in
front of the array. Figure 35. Arrays in Watch window

Now the array should look more like the picture below.

Figure 36. Expanded Array in Watch window

Step Into Now with the completion of the line of code, the array element 0 or array LED_seq[]
should contain a 5. Element 0 was used because the contents of game_index at
this point in the code is 0.

Figure 37. Array element filled in Watch window

NOTE: One thing that the Studio software does for the user that is very handy is to make any
changes appear in red. For instance, in the picture above, the ‘5’ showing in the first element of
the LED_seq[] array is in red. This means that this was the last change that occurred in the
simulator.

Page 17 9/27/2005
Purdue University C

Step Into The variable game_index will now be equal to 1.

Step Into Now the simulator will have entered a FOR loop. This line simply setup the FOR
loop elements for use. This FOR loop takes care of outputting the sequence of
lights on the LEDs connected to PORTC. This will be the first time through the
sequence so there will only be one output to “light”. The next time through this
FOR loop there will be two outputs to lights.

Step Into Here is the first time the program has communicated in any way with the outside
world. This is where the
output of the light actually
takes place. The LED to
be turned on will be the
LED connected to bit 5 of
PortC. The software
shows this by selecting or
filling in the box in the
Figure 38. Output of a high on Bit 5 position of bit 5 on PortC.
The register shown for this
is PORTC because that is the register that handles actually sending out the high (5
V) on the physical port bit.

Now an output on one of the port bit has occurred. The next process in this game
will be to read the game user’s response.

Input Function Procedures

This section will step the user through the process of simulating an input into the microcontroller.
This procedure will follow the same rules as the previous procedure. Every step will include the
user clicking on the Step Into button. The procedure will pick up where the last procedure left off
or with the cursor pointing to the end of the FOR loop in case 1 of the code in Appendix A.

Step Into This step will also display the output from the PORTC register in the PINC register.
Remember that the PINC
register is for reading
information from the
external world. However,
whether the port is
configured as an input or
Figure 39. PINC register still connected to hardware output, both registers,
PORTC and PINC, are
connected to the physical hardware pins. This means that anything showing up on
the physical pins will be placed in the PINC register. Although the PINC register
now contains information, because PortC is configured as an output port it means
nothing to the controller.

Step Into The case that the state machine will execute next is changed to 2. This will show
up in the Watch Window if a watch was placed on state.

Step Into The index for the array PB_seq[] is cleared to start at 0.

Step Into The break command is executed. The flow of the controller has now jumped out of
the state machine and back into just the while(1) statement.

Step Into The variable random is incremented here. Every time the controller drops out of
the state machine the random number generator will be incremented.

Page 18 9/27/2005
Purdue University C

Step Into The simulator has again entered the state machine.

Step Into Case 1 will be skipped in this line code because the state variable was changed to
the next state in the state machine.

Step Into The program will now enter Case 2. Case 2 will read PortA to see if any
pushbuttons have been pressed.

Step Into This line of code will look to see if anything has been pressed. But how will it do
that? Remember when the PINC register updated because a high was present on
the output pin? If a pushbutton is pressed, a high will appear on that bit’s pin. This
will be stored in the PINA register. Note the pushbuttons reside on PortA, not
PortC. So which ever pushbutton is “pressed” will be “displayed” in that bit position
in the PINA register. But there is no actual hardware connected to the simulator.
So the user must make the selection as to which pushbutton has been “pressed”
by simply clicking on the bit that the user wishes to store.

The cursor is placed


over the bit which
should be high and
then the cursor is
clicked. This will
select of darken the
box.

Figure 40. An input is selected for PortA

The value of the bit read will be


placed in the variable press. This can
be checked in the Watch window.

Figure 41. Value read into variable

Step Into State is changed to 3. This will take the state machine to the next state when the
program comes back again.

Step Into The program breaks out of the state machine.

Step Into Random is again incremented.

Step Into The state machine is reentered.

Step Into The first case is checked against the state variable. If state is equal to 1, this case
will be executed. According to the Watch window, state now equals 3.

Page 19 9/27/2005
Purdue University C

Figure 42. State variable contents


Step Into The second case is now evaluated the same as the first case.

Step Into Since state is equal to 3, case 3 will be executed. This case will determine which
pushbutton was “pressed”.

Step Into The variable x is now ready to help in the process of determining which pushbutton
was “pressed”.

Step Into The program will enter this WHILE statement as long as the pushbutton “pressed”
was not the first one or bit 0. Since the pushbutton “pressed” in this instance was
bit 5, the program will enter the while loop.

Step Into The contents of the variable press are shifted


to the right by one and then placed back into
the variable. The current status of press is 32
in the Watch Window.
Figure 43. Current state was press

Step Into Now that the press variable has


been shifted (Figure 44), the x
variable is incremented. This is
done to keep track of how many
times the variable press has been
shifted. The total number of times
press has been shifted will tell
you which bit position read the Figure 44. press after shift
“press” of the pushbutton.

Step Into until the cursor is sitting on line of code after the end bracket or } of the while loop as in
Figure 44 below. The value of press will continue to decrease until it is equal to 1.
Each time press is decreased, x will be increased by 1 as shown in Figure 45.

Figure 44. While loop ended

The variable x
contains ‘5’ a the
end of the
shifting WHILE
loop.
Figure 45. Final values of variables

Page 20 9/27/2005
Purdue University C

Again the next line of code does something to watch closely. The variables used in
this statement are x, pb_index, and the array PB_seq[]. So expand the Watch to
be able to see all of these variables.
You may need to reduce the
previous array by click on the – in
front of it.

Figure 46. Expanded array

Step Into The value of x, also the bit position of the pushbutton “pressed”, is stored in the first
element of the array PB_seq[]. Why was
it stored in the first element? Remember
a few lines above this the variable
pb_index was set to 0. But why?
Because the program is storing the first
pushbutton “pressed”. So the first
pushbutton will be stored in the first
element of the PB_seq[] array. This
Figure 47. Pushbutton “press” captured element also corresponds directly to the
first light from the array LED_seq[].

Step Into The case that the state machine will execute next is changed to 4. This will show
up in the Watch window if a watch was placed on state.

Step Into The break command is executed. The flow of the controller has now jumped out of
the state machine and back into just the while(1) statement.

Step Into The variable random is incremented.

Step Into The simulator has again entered the state machine.

Step Into Case 1, case 2, and case 3 will be skipped in this line code because the state
variable was changed to the next state in the state machine.

Step Into The program will now enter Case 4. Case 4 will hold the program is this state until
the pushbutton is “released”. This will stop multiple readings of the same
pushbutton from happening while the key is still pressed. If the pushbutton is still
“pressed”, the program will simply increment the random variable and then proceed
back to case 4 to check the pushbutton again.

Step Into In order to “release” the


pushbutton, simply follow the
instructions for “pressing” the
pushbutton. Place the cursor
over the previously “pressed”
pushbutton and click on the Figure 48. Pushbutton “released”
filled box. The box should now be empty which signifies a low or no pushbutton
“pressed”.

Page 21 9/27/2005
Purdue University C

Well, the process of simulating the output and input of information has been explained. However,
there is still more to the code that has not been explained yet. Using the techniques discussed
above, step through the rest of the code. Try to determine what is taking place in each line of
code. To help here is a small breakdown of what is left to the code:

Case 5 – This case will check to see if there are any more pushbutton “presses” to
capture. If there are more pushbuttons to read, then the program is sent back
to capture those. If there are not any more pushbuttons to read, the program
will proceed to the next case.

Case 6 - This case will determine if the pushbuttons pressed were the correct
pushbuttons for the lights. If the pushbuttons were correct, then the program
moves forward to case 7. However, if the pushbuttons were not correct, then
program starts over from the beginning.

Case 7 - This case simple checks to see if the game is over. If the game has reached
the 20th round, then the game is over and starts over. If the 20th round has not
been reach yet, the game continues.

Timer Functions

The timer functions of the ATMEGA16 allow for a variety of options. The options that will be
discussed in this tutorial will be the ability to create variable time delays using the timer interrupt
and to create waveforms using the output compare function. The ATMEGA16 has three timer
units - Timer0, Timer1, and Timer2. Timer0 and Timer2 are 8 bit timers and Timer1 is a 16 bit
timer. What this means to the user and the simulator is that the 8 bit timers can count up to 255
and the 16 bit timer can count up to 65,535.

The tutorial will describe two separate timer procedures. The first procedure is the creation of the
variable time delay using the timer interrupt. Once that is completed, the tutorial will move on to
the procedure to create a waveform using the output compare operation.

The first procedure discussed in this tutorial will be the time delay. A time delay can be created
by changing how fast the timer counts and how many counts the timer makes. A variable time
delay is created by setting the speed of the counts and then varying the total number of counts
used.

The timer interrupt will be used to create a time delay. A timer interrupt contains a block of code
that is executed whenever the timer reaches its maximum count, as mention earlier, and then
starts counting at zero again. The tutorial will examine the process of executing the interrupt
code, also called the interrupt service routine.

The output compare functionality will be discussed following the time delay. The ATMEGA16 has
incorporated the ability to manipulated specific port bits by using the output compare functionality.
The port bit connected to the specific timer being used will change its state (high or low) based on
the status of the Output Compare and Timer registers.

For instance, in this program the bit is set to toggle (go from high-to-low or from low-to-high) when
the timer reaches a set value. By toggling the port bit, the program has created a square
waveform. The frequency of the square wave is determined by the total number of counts the
timer executes and the speed with which each count occurs. For this program, the intention is to
have a speaker connected to the output bit so that the output compare operation creates a tone
at a set frequency.

Page 22 9/27/2005
Purdue University C

In order to step the user through the timer procedures, a section of code from the project must be
created and loaded into the AVR Studio software. The code to load into the Studio software is
attached in Appendix B.

Studio Setup for Timer Simulation

Step 1 Load the code from Appendix B into the Studio software. Follow the instruction discussed
earlier in the tutorial to load the simulator.

Step 2 Once the code has been loaded and is ready to simulate the operation, expand the I/O
ATMEGA16 option in the Workspace window. After that has expanded, also expand the
PORTA, PORTC, PORTD, TIMER_COUNTER_0, and TIMER_COUNTER_1. By
expanding this many options, not everything will be visible in the Workspace Window. It
will be necessary to use the scroll function to see what is needed.

For a description of the registers visible with PORTA, PORTC, and PORTD refer to the
section in this tutorial on Basic I/O Functions. Once the timer sections are expanded, the
user will see a number of registers and also a few flag registers.

Figure 49. Workspace with expanded timers

The timer registers are designated by the symbol and the flag registers are
designated by . The timer flag
registers are also expandable.
When the flag registers are
expanded, the individual flags
contained in that register are listed. Figure 50. Expanded Flag register

Page 23 9/27/2005
Purdue University C

There a lot of registers to monitor when dealing with the timers. But most of the registers
have to same functionality in each timer. Just as with PORTX and PINX from the I/O
function, the timer registers with the same functionality in the different timers can be
discussed as TCCRX, where X is the specific timer being discussed.

One thing for the user to notice is that there are some registers that show up exactly the
same in each timer. These registers are shown in that manner because the registers are
general timer registers. There is only one of these registers; not one for each Timer.

Here is a quick description of the main Timer registers:

TCCRX – Timer/Counter control register. This register sets up the operating


mode of the timer. It defines the speed that counter will count at along
with other functions. See the data sheet for more detail.
TCNTX - Timer/Counter register. This register contains the current count of the
timer.
OCRX - Output Compare register. This register holds the value that is being
compared with the Timer/Counter register value. If the two values
match, then the interrupt flag is set.
ICRX - Input Compare register. This register is used with input compare
functions. This register is not within the scope of this tutorial.
TIFR - Timer Interrupt Flag register. This register holds the status of the timer
interrupt flags.
TIMSK - Timer Interrupt Mask register. This register enables the timer
interrupts.

Step 3 To make it easier to follow what is happening


during the simulation, it may be necessary to
add a number of variables to the Watch
Window. The main variables recommended
for addition to the Watch Window are state,
random, game_index, press, x, LED_seq[],
PB_seq[], delay_time, beep_off, beep, and
total.

Figure 51. Watch Window variables

Page 24 9/27/2005
Purdue University C

Time Delay Function Procedures

This section will step the user through the process of simulating a time delay. Each step in this
procedure signifies the user pressing the Step Into option once unless told otherwise in the
procedure. There will some operations occurring while this procedure is executing the functioning
of the timer delay operation that will not be discussed in this procedure. These operations will be
pointed out when one occurs.

The time delay operation in this program is used to provide a start up time frame or to blink the
lights used in the previous section of the tutorial. A light will be turned on, a small delay will
occur, and then the light is turned back on. Thus a blink has been created for the game user.

NOTE: Step Into(x2) says that the next description is for the next two Step Into presses

Step Into(x6) The first few of the Step Into processes will be configuring the ports for operation
with the circuit. Refer to the I/O function portion of the tutorial for an indepth
description.

Step Into This command loads the TCCR0 register with a three which sets the clock divide
by to 64. This sets the timer to run at a 93.75 kHz rate. This is only for Timer0.

Step Into The counter is started at zero. So the count will transverse the entire 8 bit range,
unless changed later.

Step Into The Output Compare for Timer0 is configured here. This Output Compare unit will
not be used in this program.

NOTE: The next registers are doubled. This is because Timer1 is a 16 bit timer. However, the
ATMEGA16 is an 8 bit microcontroller. So the 16 bit registers for Timer1 must be
split into two 8 bit registers.

Step Into(x2) The speed of Timer1 and the operation of the Output Compare unit is now defined.
Timer1 will operate at 750 kHz and Output Compare unit B is to be used.

Step Into(x2) This timer will also start counting at zero.

Step Into(x2) If the Input Compare operations were going to be used in this program, the two
Input Compare register would be defined here.

Step Into(x2) Timer1 contains two Output Compare units, A and B. These two registers control
the compare value for Output Compare unit A.

Step Into(x2) Output Compare unit B compare value is set to zero. The operation of Output
Compare unit B will be further investigated in the next procedure.

Step Into The Timer0 overflow interrupt is enabled and the Timer1 Output Compare unit B
interrupt is enable in this line of code.

Step Into This is an assembly command which simply enables all interrupts that have been
configured.

Page 25 9/27/2005
Purdue University C

At this point the timers have been


configured. But remember that as soon as
the TCCRX register is no longer set to zero,
that timer starts counting. So if the TCNTX
registers have a value at this point, don’t
worry. The timer had already starting
counting.

One other thing the user may notice while


stepping through the program is that the two
timers don’t count at the same speed. This
is because the timers were configured to
count at a different time interval. Timer0
was set to 93.75 kHz and Timer1 was set to
750 kHz.

Figure 52. Timer register configurations

Step Into The variable random is now preset to a 5. This is only for the procedures. In the
complete code the random number generator will start with zero and function
normally.

Step Into(x2) As in the previous section in this tutorial the program has just entered the state
machine.

Step Into Because the state variable is a 1, the first case statement is entered.

Step Into(x2) The first light to be shown to the user is selected and the round is set.

Step Into Delay_time has now been set to


provide a 2 second time delay before
the game starts.

Step Into This line of code will keep the


program from progressing until 2
seconds have elapsed. But to
understand how a 2 second time
delay is created, the Timer0
Workspace registers must be
watched closely and the Timer0 Figure 53. delay_time set for 2 seconds
Overflow Interrupt must occur.

NOTE: There is one disadvantage to the simulator timers. There is no way to run the simulator
in “real time”. In other words, the timers will not run at the speed that would normally occur. This
allows the user to see the internal working of the timer. But the user can’t attempt to verify actual
time frames easily. Everything must be checked via clock cycles.

Page 26 9/27/2005
Purdue University C

Step Into The cursor will not move now until delay_time is equal to zero. So to examine what
is happening inside the
microcontroller, the
user needs to watch the
Workspace window,
TIMER_COUNTER_0.
Specifically the user
needs to watch the
TCNT0 register closely.

Figure 54. TCNT0 at the start of a delay

As the Step Into button is


pressed, clock cycles are
occurring within the
microcontroller. As the clock Figure 55. TCNT0 during the counting process
cycles proceed and based on
the configured speed of the timer, the count will increment. The user will see the
contents of TCNT0 increment by seeing the number of filled in boxes changing.

Step Into(x?) Continue to press Step Into until TCNT0 is at its maximum value. This can take a
while; so be patient. There are, however, a few ways around having to repeatedly
click on Step Into. Two of those procedures will be described here.

1) Remember, when the


pushbutton “press” was
created by clicking in the
box of PINX where the
button was “connected”?
By clicking on a box in
the Workspace, the user Figure 56. Timer0 count maxed
is able to change the
contents of that register. To shorten the time frame needed to make Timer0
rollover or hit the maximum and start over, click on each of the boxes that are
empty in the TCNT0 register. This should end with all the boxes being filled. In this
situation, the user may not see the Timer 0 Overflow Flag go high. The next
procedure will discuss the Timer Overflow Flag 0 or TOV0.

At this point, the


cursor should still
be sitting on the
while loop we
entered earlier.

Figure 57. Cursor waiting in while loop

2) This procedure for speeding up the time consuming process of stepping through
a time delay will use the Timer0 Overflow Flag or TOV0 bit. The TOV0 bit can be
found in the TIFR register. The
TOV0 bit signifies that an
interrupt has occurred because
the timer counter has rolled from
the maximum of its count back
Figure 58. TOV0 bit in the TIFR register to zero. In the case of Timer0,

Page 27 9/27/2005
Purdue University C

that means that the timer has reached 255 and started counting at zero again. The
user can utilize this operation to speed up the counting.

Since the variable time_delay is manipulated in the interrupt service routine for
Timer0 Overflow, the user simply needs to get the program into that service routine
faster. To do this using
TOV0, the user simply needs
to select the TOV0 bit in the
TIFR register by clicking on
the TOV0. Once this bit is
filled in, the next Step Into Figure 59. TOV0 manually selected for interrupt
will cause the program to
jump to the Timer0 OVF (overflow) interrupt service routine. When using this
procedure, the user should watch for when the TOV0 bit is cleared by the
microcontroller.

Step Into(x?) until the cursor has moved out of the main program. It should now be pointing to
the first line of the Timer0 Overflow interrupt service routine. Also TCNT0 is now
set back to zero.

Figure 58. Timer0 interrupt service routine has been entered

Step Into The Timer0 overflow interrupt service routine has now been entered.

Step Into Timer0 TCNT0 register has just been reloaded to start counting at 162. By setting
the timer to run from a 750 kHz
clock and setting the start count to
162, the program will provide a 1
ms time between Timer0
interrupts.
Figure 59. Reload of TCNT0

Step Into The IF statement that the program enters here controls whether there is a beep or
not. This will be discussed in further detail in the Output Compare section of the
tutorial. So skip this IF structure for now.

Step Into The cursor should now be


pointed at the IF
statement containing
delay_time.
This statement will check Figure 60. delay_time checked
to see if the variable
delay_time is nonzero. In other words, if the variable contains a value greater than
zero, the IF statement will be evaluated as TRUE. If the IF statement is TRUE,
then the variable delay_time will be decremented by 1.

Page 28 9/27/2005
Purdue University C

Step Into Now that the IF statement has been evaluated; the contents of delay_time have
been decremented. This can be seen in the
Watch window. Delay_time is now 1999.
This means that as long as delay_time is
greater than zero, the IF statement will be
Figure 61. Decremented delay_time entered. So how long will it take for the IF
statement to evaluate as FALSE? A total of
2000 interrupts. How long does it take for interrupt to occur? 1 ms. So what it the
total length of time the time delay will take?

Time = delay _ time × interrupt interval


Time = 2000 × 1 ms = 2 seconds
Step Into(x?) So again the program has entered a position where pressing Step Into will take a
considerable amount of time before the program can move on through the while(1)
loop. Again there are a few ways around having to repeatedly click on Step Into.
Only one of those procedures will be described here.

Earlier in this tutorial a description was given for how to change the contents of a
variable within the Watch window. This will come in handy here. Instead of having
to step through each iteration of the interrupt to make delay_time decrement to
zero, the user will make the variable contents change to a more desirable value.
However, before changing the variable, step through the complete process a
couple of times until comfortable with the workings of the program.

Now to change the contents of the


variable, double click on the
contents of the variable in the Watch
Window. Studio will highlight the
contents of the variable and place a
blinking insert cursor beside the contents. Figure 62. Changing the contents of delay_time

All the user needs to do the change


the contents is type in the new value.
To speed along the 2 second time
delay, change delay_time from its
current value to zero. But the user Figure 63. delay_time changed to 0
must hit the ENTER key after making
the change or the change will not register in the software. The user knows that the
change took place because the new value will be displayed in red.

Step Into The program will move past the delay_time while statement because the variable is
now 0. The
cursor moving
onto the next
statement in
the code signifies Figure 64. The delay is complete
that the 2 second delay has elapsed.

The user has now stepped through the entire process to simulate a time delay using the Studio
software. There are numerous time delays in this piece of code. The steps are the same for
each of these time delays. The only difference is the amount of time the delay is designed to
complete.

Page 29 9/27/2005
Purdue University C

Output Compare Procedures

This section will step the user through the process of simulating the creation of a waveform using
the Output Compare function. This procedure will follow the same rules as the previous
procedures. Every step will include the user clicking on the Step Into button once or the number
of times designated by the number in the parenthesis after the command. The procedure will pick
up where the last procedure left off or with the cursor pointing to the beginning of the FOR loop in
case 1 of the code in Appendix B.

However, the configuration used to set up the Output Compare function must be described first.
During the initialization of the timers, Timer1 was setup to utilize the Output Compare operation.
Timer1 contains two Output Compare units – A and B. One of those units must be chosen. In
this case, Timer1 was initialized to operate at 750 kHz using Output Compare B.

The Output Compare operation requires that the timer count from 0 to maximum and rollover
every time. So the user will not see the timer loaded to a start number. The Output Compare unit
operates by comparing the Output Compare register, in this program - OCR1B, to the
timer/counter register, TCNT1X. When ever those two registers match the output pin of the
Output Compare unit will be exercised as configured.

The output pin of the Output Compare unit is hardwired into the hardware. The datasheet should
be visited to determine which bit is being used. In this case, the program will be using the port bit
Port D bit 4. How the bit is exercised must be determined and configured by the user. The port
bit can be configured to toggle (high-to-low or low-to-high) on the compare match, clear (set to
low) on the compare match, or set (set to high) on the compare match. This program toggles the
bit on a compare match.

The program is running the Output Compare operation continuously throughout the operation of
the program. How does the program then create just a beep instead of a continuous tone? The
answer to that question is by manipulating the status of the Output Compare bit. If the Output
Compare bit, PortD bit 4, was set as an output at all times during the program then a continuous
tone would be created. However, if the bit is changed to an input when the user does not want a
tone to be heard, then the tone is effectively turned off. But the user needs to remember that the
creating of the tone is continuous, the program simply just turns the output bit on and off when
necessary.

The user needs to remember here that the TCNT1 and OCR1B registers that the procedure is
referring to are actually the concatenation of the TCNT1H and TCNT1L registers and OCR1BH
and OCR1BL registers. Studio prefers that the registers be used and manipulated in their un-
concatenated form.

One last thing to remember is that this is not like the time delay process. The main program will
not stop and wait for the Output Compare to happen. The code within the while(1) will continue to
be execute throughout the program. However, this procedure is only looking at the operation of
the Output Compare process.

Page 30 9/27/2005
Purdue University C

Step Into(x?) The FOR loop used here will output the
first light or the sequence of lights for
the game user. During this FOR loop
the light will be blinked. In other words,
the light will be turned on then a short
delay will occur. When the delay is
over, the light will be turned off and
another delay will occur. This will cause
the game user to see the light blink. So
this FOR loop will utilize two time
delays. Time delays were discussed in
the previous section and will not be Figure 65. End of FOR loop
covered here. Step past this to the
ending bracket of the FOR loop.

Step Into Through the previous step, the timer/counter for Timer1 will have been counting.
Notice that Timer1 counts at a
faster pace than Timer0 did.
That is because it is set to a
faster timer frequency.

Figure 66. Timer1 in operation

The line of code that will execute next is important for the Output Compare
operation. This line of code sets the Output Compare register for a value to be
compared against the timer/counter register, TCNT1L. This means that for every
187 counts of Timer1 the output (if enabled) will toggle its state.

Figure 67. Set the Output Compare register

Step Into If a watch was placed on beep_off, the user will be able to see that the variable has
been set to 187. If the user continues to simply step through the program, the
program will continue to execute the code in the while(1) until TCNT1X rolls from
its maximum to zero. This is true only the first time through the program. Why is
that? Didn’t the program just set beep_off to 187? Yes, beep_off has been set to
187. But beep_off is not utilized by the program until the Output Compare B
interrupt has occurred. What was the event that caused the interrupt to occur?
The match of TCNT1 and OCR1B is what causes the interrupt to occur. When the
program first initialized the registers, OCR1B was set to 0x00. That setting
required the TCNT1 to count through the entire sequence and rollover before the
first Output Compare interrupt occurs.

Why would this work? Hasn’t the program stepped through almost all of case 1
already? Yes, but remember that using the procedures from the time delay section
actually speed up the natural operation of the program artificially. So in the actual
program TCNT1 would be moving through its sequence much faster.

This is due to the fact that during the simulation of the Timer0 time delays, the user
skipped a large number of clock cycles by changing the TCNT0 register contents or
by setting the TOV0 flag. Timer1 would have been using those clock cycles to step
through its own count. If the clock cycles had run at their normal pace with

Page 31 9/27/2005
Purdue University C

everything happening at the exact same time it would have in the “real” world,
Timer1’s Output Compare interrupt would have occurred more quickly.

Step Into The variable beep has now been set to 100. This variable controls how long the
tone will be allowed to be heard by the game user. This process operates just as
the time_delay variable did. Every 1 ms the Timer0 OVF interrupt will occur and
check if the tone should be heard or not. This will be discussed in more detail
when the program gets into the Timer0 OVF interrupt.

Step Into(x?) Continue to press Step Into until TCNT1 is at its maximum value. This can take a
while; so be patient. There are, however, a few ways around having to repeatedly
click on Step Into. Two of those procedures will be described here.

1)The first way that will be


discussed is to use the
knowledge the tutorial has
already given to the user about
the match of TCNT1 and
OCR1B registers triggering an
interrupt. Because the TCNT1
and OCR1B registers have to
match in order to create an Figure 68. Manual match
interrupt, the simplest step is
for the user to set the OCR1B registers to match the next count of the TCNT1
registers. On the next step the two registers will match and the interrupt will
happen.

2) Just as in the time delay section, the Output Compare operation has an interrupt
flag. This flag, OCF1B, is also in
the TIFR register. So the other
option in this situation is to simply
enable this flag. The next step
will take the cursor into the Output
Compare interrupt service routine
for Output Compare unit B.
When using this procedure, the Figure 69. OCF1B located in TIFR
user should watch for when the
OCF1B bit is cleared by the
microcontroller.

Figure 70. OCF1B manually activated

Step Into The Output Compare B interrupt service routine is being entered. This routine will
take care of setting the OCR1B registers for the next trigger point. Each point will
be 187 counts past the current count. This will create a solid tone.

One thing for the user to remember is that the tone is being created using this
interrupt service routine. This interrupt will occur roughly every 250 µs. This
means that the Output Compare interrupt will happen multiple times between the
Timer0 OVF interrupt of 1 ms.

Page 32 9/27/2005
Purdue University C

This is important because while the Output Compare process is operating, the user
needs to be watching the variable beep. Remember, beep controls how long the
output pin for the tone is creating the tone for the game user. As of entering the
Output Compare interrupt, beep still contains 100 as shown in the Watch window.

Step Into The current contents of OCR1BH is loaded into the variable total. This can be seen
in the Watch Window.

Step Into The current contents of OCR1BL is added to the contents of total. This creates a
16 bit concatenated value of the current contents of OCR1B. This can also be seen
in the Watch window.

Step Into The 187 is now added to the current contents


of the Output Compare register. This sets up
the next match point.

Figure 71. Next match point set

Step Into(x2) The concatenated value of


the next match point is split
back into two 8 bit values and
placed into the correct Output
Compare Registers.
Figure 72. OCR1B registers set

The value loaded in the OCR1B registers will change every time the Output
Compare interrupt happens.

Step Into(x?) Now that the trip point has been set, the user just needs to step through the
program until the Timer0 1 ms interrupt occurs. Use the procedures detailed in the
time delay section to speed this process up.
Stop when the cursor is positioned on the
first line of the Timer0 OVF interrupt.

Figure 73. First line of code in Timer0 interrupt

Step Into This line reloads TCNT0 to the correct value for the 1 ms interrupt interval.

Step Into If the variable beep is anything other than zero, then the program will keep the
Output Compare port bit enabled for output to allow the game user to hear the
tone. In this case beep should be 100 which will cause the program to fall into the
IF statement.

Step Into Once in the IF statement, the program will


decrement beep by one.
Figure 74. Decrement beep

Step Into This line of code enables PortD bit 4, the Output Compare B output bit, as an
output. In this configuration, the
game user will be able to hear
the tone that is being created by
Timer1’s Output Compare B unit.
Figure 75. PortD bit 4 configured as output Now, the next time TCNT1 and
OCR1B match, the output bit
PortD bit 4 will toggle its state.

Page 33 9/27/2005
Purdue University C

Step Into(x?) The next order of business is to create the Output Compare match again. In this
situation to get the proper operation from the simulator, the user will need to make
the TCNT1 and OCR1B registers match. When they match, the output bit will
automatically toggle its state before entering the Output Compare interrupt service
routine.

Figure 76. Bit toggled high. (Not shown in PORTD register for unknown reason)

The user simply needs to keep using the steps covered in this section in order to see the output
bit toggle on and off when the variable beep is not zero. At this point, the user will be able to
simulate basic input/outputs functions and timer functions. The next function of the ATMEGA16
to be covered in this tutorial is the serial communication or USART functions.

Serial Communication Functions

Serial communication allows the microcontroller another avenue for communicating with the
outside world. The unit that allows for serial communication in the ATMEGA16 is the USART or
Universal Synchronous-Asynchronous Receiver/Transmitter. This unit uses three lines to
connect to other devices. The lines are ground, send (transmit), and receive. In this program,
the microcontroller will be communicating with a Terminal program. The Terminal program allows
characters sent to it by the microcontroller to be displayed for the user, generally on a computer
screen.

There is a draw back of using Studio to simulate USART communications. If the program uses
the STDIO.H header/library functions to exercise the USART, Studio will not simulate the
communication correctly. In other words, if the simulator is to be used, the user must create the
functions to communicate serially. Do not use printf, scanf, etc. This is because the simulator
does not have the ability to bring in functions include in the program via the #include or #define
commands.

There is a way around this limitation. There is another program that will connect to Studio in
order to show the user what would be sent or received via the USART unit. This program is
called HAPSIM. HAPSIM has functions other than the USART but those will not be discussed in
this tutorial. This program works with the STDIO.H library file and its functions – printf, scanf, etc.

Due this limitation this section of the tutorial will be split into two sections. One section will
discuss simulating the USART operations using programmer created communication functions.
The other section will discuss HAPSIM and its use with the STDIO.H functions.

Studio Setup for USART Simulation

Step 1 Load the code from Appendix C into the Studio software. Follow the instruction discussed
earlier in the tutorial to load the simulator.

Page 34 9/27/2005
Purdue University C

Step 2 Once the code has been loaded and is ready to simulate the operation, expand the I/O
ATMEGA16 option in the
Workspace window. After that
has expanded, also expand
the USART section.

Figure 77. Workspace with expanded USART

Here is a quick description of the main Timer registers:

UDR – USART I/O Data register. This register contains the current
information being received by or sent from the USART.
UCSRX - USART Control Status registers A, B and C. These registers contain
the different flag bit necessary to operate different types of
communication via the USART. The main register of interest for this
tutorial is UCSRA.
UBRRX -This is a 16 bit register (UBRRH and UBRRL) that controls the format
and speed of the data communication.

There is one other detail the users needs to be aware of when using the USART. The USART
contains a buffer for the input and output of information. This buffer is two 8 bit registers deep. In
other words, two 8 bit characters can be held in this buffer. This buffer will come into play as the
user steps through the tutorial.

Step 3 To make it easier to follow what is happening in the during the simulation, it may be
necessary to add a number of variables to the Watch Window. The main variables
recommended for addition to the Watch Window are ptr and msg. However, these
variables will only contain a value when
the cursor is within the function that
contains that variable. Otherwise the
variable contents will read “Not in
scope”.

Figure 78. Watch Window variables

User created USART Procedures

This section will step the user through the process of simulating serial communication when using
program created communication functions. Each step in this procedure signifies the user
pressing the Step Into option once unless told otherwise in the procedure.

Step Into(x6) The I/O ports A, C, and D are configured for operation with the program.

Step Into The register UCSRA is configured as 0x00.

Step Into The register UCSRB is configure to enable the transmit and receive functions of
the USART.

Step Into UCSRC is configured to provide and receive a data stream of 8 data bits and 1
stop bit with no parity bit. The communication is also set to be in asynchronous
mode.

Page 35 9/27/2005
Purdue University C

Step Into(x2) These lines of code configure the Baud rate (speed of communication) to 9600
baud.

Step Into The state machine is entered.

Step Into The first case is entered.

Step Into The next case is set to be executed.

Step Into The function show_message() is called with a 2 passed to the function.

Step Into The cursor should now have moved


to the show_message() function. The
first line of code to be executed will
call another function, write_serial(),
in order to move the display on the
Terminal program to the next line
down. This character, 0xa, will be the
first character sent out the USART to
the Terminal program.

Figure 79. Cursor ready to send first character

Step Into The program will now wait until the USART Data Register is Empty which is
signified by the UDRE flag of the UCSRA register being a high.

Figure 80. Waiting to transmit

The UDRE flag will start the program in the high state. This bit is set high
whenever the processor resets. It will stay high until either the buffer (two 8 bit
registers deep) is full or the UDRE flag is cleared by the program. Once the buffer
is full, the UDRE flag should be in its low state. It will stay in that state until the
information in the buffer has had at least one of its registers emptied.

The user should note that the UDRE flag will stay high until the first two characters
have been sent to the USART. After this each character sent to the USART will
cause the flag to change states to low signifying that the buffer is full and the first
character in the buffer will be sent through the USART. This then moves the
previous character into the buffer register to be sent next and the current character
into the buffer register waiting to be sent.

Step Into Once this bit goes high it tells the microcontroller that at least one register in the
buffer has been sent and that it is ready to hold the next data. But before the data
can be sent to the USART, the UDRE bit must be cleared by writing a high into that
bit of the UCSRA register.

Page 36 9/27/2005
Purdue University C

Step Into The next line of code simply writes the 8 bits of data to be sent to the Terminal
program into the UDR register. The UDR register is the store place for information
to be sent out prior to the being put
in the buffer or information that
was received through the buffer by
Figure 81. Data is ready to be sent the microcontroller.

Step Into The next step will send one


character from the buffer out
of the USART to the Terminal
program that is connected to
the microcontroller. Figure 82. Data has been sent

Step Into(x2) The next character to be sent is the command to move the cursor on the Terminal
program back to the beginning of the new line it is sitting on.

Also notice here that the variable msg is in scope and contains information that
tells this program which message will be sent to the Terminal program in the next
couple of statements in the show_message()
function.
Figure 83. Message to display is shown

Step Into(x5) Step through the process described above to send the return command character.
If the UDRE bit is not set, the
program will wait until it has
become set. The user simply
needs to click on that bit Figure 84. Manually setting UDRE
position in the UCSRA register
to cause that bit to be toggled high. This process is that same as setting a port bit
high.

Step Into The cursor should now be pointing to


the line of code that sets up the
pointer to the first character of the
message to be displayed on the
Terminal program.
Figure 85. Ready to start sending the message

This pointer will allow the program to send one character (8 bits) of the message at
a time to the USART to be sent to the Terminal program.

Step Into The message this pointer is pointing to is “Playing “. This


means that the first character of the message
to be sent should be ‘P’. In the Watch
window, the user will be able to see the
character that the pointer will be sending to
the UDR register and the buffer.
Figure 86. First letter to be sent

Page 37 9/27/2005
Purdue University C

Step Into This line sets up a


While loop to
continually send
one character at a
time of the
message to be
displayed to the
write_serial()
function. Each
character will be Figure 87. The declaration of the structure of messages
sent until the pointer
is pointing to the last character in that message or the last element in the structure
containing the messages. This last character will be a null character or zero when
the structure or array is declared in this fashion.

Step Into(x5) Again step through the write_serial() function. Notice that the ‘P’ or the ASCII
value 0x50 was sent to the
UDR. And again it may be
necessary to manually set the
UDRE flag in the UCSRA Figure 89. ‘P’ is being sent from the USART
Register, if the user doesn’t
want to wait through the buffer loading and unloading.

Step Into(x?) Continue to step through the show_message() function until the complete message
has been sent through the USART.

This process will continue every time something in the program is updating the display on the
Terminal program. But for each of the updates or changes in the display, the principles and
operations are the same when using the USART.

STDIO USART Procedures

This section will describe how to simulate the USART when using the functions provided in the
STDIO header file. Those functions include printf() and scanf(). Unfortunately, as mentioned
earlier in this section, those functions can not be simulated through the previous procedure.

But before the Studio procedures can be discussed, another software program needs to be
downloaded and installed. This program will allow the user to see the characters displayed to the
Terminal program as the Studio simulator is running.

The program is HAPSIM. This program contains a simulator for the USART functionality along
with other functions such as toggle switches, LEDs, and LCDs. This procedure will only describe
the operations of the USART simulator.

HAPSIM and Studio Setup for USART Simulation

Step 1 Download and unzip HAPSIM and the USART<->TWI Converter from
www.helmix.at/hapsim.htm.

Step 2 Load the code from Appendix D into Studio.

Page 38 9/27/2005
Purdue University C

Step 3 The next step will be to run the HAPSIM.exe file.

Figure 90. HAPSIM program upon open

Step 4 Now that HAPSIM is up and running, open


USART2TWI.xml from the HAPSIM program.

Figure 91. Open a file using HAPSIM

The HAPSIM workspace window will change from the LCD configuration to the USART
configuration. This new configuration contains two windows. The user will only be using
the USART window with the tutorial. So the user may close the other window if desired.
Otherwise, the user simply needs to click on the USART window to make it the active
window. The user will know which window is active by the label on the top of the HAPSIM
window. In this case, the HAPSIM window reads .

Page 39 9/27/2005
Purdue University C

Figure 92. USART window for HAPSIM open

Step 5 Configure the options on HAPSIM to work correctly with Sudio

a. Select Options on HAPSIM-USART.


AVRStudio Hook and Stay on Top
must be selected.

Figure 93. Options for USART HAPSIM

b. Select the USART Terminal.


Then select Terminal Settings
from Options. From the pull
down menu for Serial
Interface select USART.
Then click OK when ready.

Figure 94. Terminal Settings window from HAPSIM

Page 40 9/27/2005
Purdue University C

c. Select ATmega16 from the pull down on the


main window.

Figure 95. Selecting the correct microcontroller

Now you can run or step through your program in the AVRStudio. The user should remember that
Studio must be opened first for HAPSIM to run correctly. Otherwise, HAPSIM will give error
messages. Also if Studio is shut down before HAPSIM, HAPSIM will give error messages.

The user should also look closely at the differences between the code from Appendix C and
Appendix D. There are quite a number of changes in the USART operation. The user should try
to understand the differences.

One thing the user needs to be aware of through this portion of the tutorial is that the USART
registers and flags in the Studio Workspace will NOT work as before. The configuration will be
visible. But the user will not be able to see the workings of the UDRE flag or the UDR register.
The user must just understand how those two items are working together in the background.

Step Into(x6) The I/O ports A, C, and D are configured for operation with the program.

Step Into The register UCSRA is configured as 0x00.

Step Into The register UCSRB is configure to enable the transmit and receive functions of
the USART.

Step Into UCSRC is configured to provide and receive a data stream of 8 data bits, 1 stop bit
with no parity bit. The communication is also set to be in asynchronous mode.

Step Into(x2) These lines of code configure the Baud rate (speed of communication) to 9600
baud.

Step Into The state machine is entered.

Step Into The first case is entered.

Step Into The next case is set to be executed.

Step Into The character for the ASCII command to move to a new line is sent from the
USART. The user will not see anything happen in the HAPSIM here. It is invisible
to the viewer.

Step Into Again the user will not see anything happen here. The character for the ASCII
command to return the cursor to the home position is sent from the USART. It is
invisible to the viewer.

Page 41 9/27/2005
Purdue University C

Step Into The line of code


sends the string
contained in the
array message2[]
to the Terminal
program. The
user should be
looking at
HAPSIM when
this line is
executed.

Figure 96. The first line is shown in the Terminal simulator – HAPSIM

Step Into(x?) The user should step through the rest of the program, exercising the skills learned
throughout this tutorial, to see the program functioning.

References:
Images are taken from AVR Studio 4 and HAPSIM V2.05 using SNAGIT software

AVR Studio 4 is available for free from the Atmel web site, http://www.atmel.com.

HAPSIM is a free AVR periphery simulator from the Helmi web site,
www.helmix.at/hapsim.htm

Appendix:

Page 42 9/27/2005
Purdue University C

Appendix A

#include <mega16.h>
unsigned char state = 1; //set entry case
unsigned char random,game_index,pb_index,x,press,y;
unsigned char LED_seq[22]; //storage for LED output sequence
unsigned char PB_seq[22]; //storage for PB input sequence
unsigned char show[]={1,2,4,8,16,32,64,128}; //display bits
void main(void)
{
DDRA=0x00; //portA configured for input operation
PORTA=0xFF; //portA configured for internal pullup resistors
DDRC=0xFF; //portC configured for output operation
PORTC=0x00; //portC configured for initial state of off (low)
random=5; //initial condition for random
while (1)
{
switch (state)
{
case 1:
LED_seq[game_index] = random; //store new LED number
++game_index;
for (x=0; x<game_index;x++)
{
PORTC = show[LED_seq[x]]; //show LED
}
state = 2;
pb_index = 0; //clear pb index for next response string
break;
case 2:
if(press = PINA)
{
state = 3; //go to debounce
}
break;
case 3:
x = 0; //prepare to detect key
while (!(press & 0x01))
{
press = press >> 1; //shift to find bit
++x; //count shifts
}
PB_seq[pb_index++] = x; //store number of pb pressed
state = 4; //go to await release
break;
case 4:
if (PINA == 0) //all released
{
state = 5;
}
break;
case 5:
if (pb_index != game_index) state = 2; //go back for more
else state = 6;
break;
case 6:
y = 0; //clear error flag
for (x=0;x<game_index;x++) if (LED_seq[x] != PB_seq[x]) ++y;
//increment y if wrong
if (y)
{
state = 1; //exit if sequences don't match
game_index = 0; //start game over
}
else state = 7; //check for 20 sequence done
break;
case 7:
if (game_index == 20) state = 1; //game done if 20 right
else state = 1; //add one and do a new sequence
break;

Page 43 9/27/2005
Purdue University C

default:
break;
}
random = (++random & 0x7); //mod 8 random counter
};
}

Page 44 9/27/2005
Purdue University C

Appendix B
#include <mega16.h>

unsigned int delay_time;


unsigned int beep,beep_off;
unsigned char state = 1; //set entry case
unsigned char random,game_index,pb_index,x,press,y;
unsigned char LED_seq[22]; //storage for LED output sequence
unsigned char PB_seq[22]; //storage for PB input sequence
unsigned char show[]={1,2,4,8,16,32,64,128}; //display bits
unsigned int total;
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
TCNT0 = 256 - 94; //reload for 1 ms
if (beep)
{
--beep; //decr beep counter
DDRD = DDRD | 0x10; //set pd4 for output
}
else DDRD = DDRD & 0xEF; //set PD4 for input
if (delay_time) --delay_time; //count down delay
}

interrupt [TIM1_COMPB] void timer1_compb_isr(void)


{
total = OCR1BH;
total = (total << 8) + OCR1BL;
total = total + beep_off;
OCR1BH = (total & 0xFF00)>>8;
OCR1BL = total & 0x00FF;
}

void main(void)
{
PORTA=0xFF;
DDRA=0x00;
PORTC=0x00;
DDRC=0xFF;
PORTD=0x00;
DDRD=0x00;
TCCR0=0x03;
TCNT0=0x00;
OCR0=0x00;
TCCR1A=0x10;
TCCR1B=0x02;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;
TIMSK=0x09;
#asm("sei")
random = 5;
while (1)
{
switch (state)
{
case 1:
LED_seq[game_index] = random; //store new LED number
++game_index;
delay_time = 2000; //wait to start sequence
while(delay_time | delay_time);
for (x=0; x<game_index;x++)
{
PORTC = show[LED_seq[x]]; //show LED
delay_time = 500; //show LED for 1/2 second
while(delay_time | delay_time);

Page 45 9/27/2005
Purdue University C

PORTC = 0x00; //clear LEDs for .3 second


delay_time = 300;
while(delay_time | delay_time);
}
beep_off = 187; //set for pleasing beep
beep = 100; //longer beep to show end of sequence
state = 2;
pb_index = 0; //clear pb index for next response string
break;
case 2:
if(press = PINA)
{
state = 3; //go to debounce
beep = 15; //make a key beep
}
break;
case 3:
x = 0; //prepare to detect key
while (!(press & 0x01))
{
press = press >> 1; //shift to find bit
++x; //count shifts
}
PB_seq[pb_index++] = x; //store number of pb pressed
delay_time = 100; //debounce input
while(delay_time); //wait it out
state = 4; //go to await release
break;
case 4:
if (PINA == 0) //all released
{
state = 5;
}
break;
case 5:
if (pb_index != game_index) state = 2; //go back for more
else state = 6;
break;
case 6:
y = 0; //clear error flag
for (x=0;x<game_index;x++) if (LED_seq[x] != PB_seq[x]) ++y; //increment y
if wrong
if (y) state = 8; //exit if sequences don't match
else state = 7; //check for 20 sequence done
break;
case 7:
if (game_index == 20) state = 8; //game done if 20 right
else state = 1; //add one and do a new sequence
break;
case 8:
if(y) beep_off = 20000; //set for rude sound
beep = 500; //1/2 second tone
state = 1;
delay_time = 2000; //2 seconds game result display
while(delay_time | delay_time);
game_index=0;
break;
default:
break;
}
random = (++random & 0x7); //mod 8 random counter
};
}

Page 46 9/27/2005
Purdue University C

Appendix C
#include <mega16.h>

unsigned char state = 0; //set entry case


unsigned char random,previous_msg,game_index,pb_index,x,press,y;
unsigned char LED_seq[22]; //storage for LED output sequence
unsigned char PB_seq[22]; //storage for PB input sequence
unsigned char show[]={1,2,4,8,16,32,64,128}; //display bits

void write_serial(unsigned char chs)


{
while (!(UCSRA & 0x20)) ; //wait for UDRE
UCSRA = 0x20; //clear UDRE
UDR =chs; //output character
}

void show_message(unsigned char msg)


{
unsigned char message[7][25] =
{" ", //clear a line
"Press a button to play. ", //start message
"Playing ", //playing
"Done. You are a loser!!!",
"Done. You are a nerd!! ",
"Done. You are a winner! ",
"Done Simon Conqueror! "}; //big winner

unsigned char *ptr;


write_serial (0xa); //linefeed if serial
write_serial(0xd); //cr
ptr = &message[msg][0]; //point to message
while (*ptr > 0)
{
write_serial(*ptr++); //send if serial
}
previous_msg = msg; //retain information on previous message
}

void add_seq(unsigned char seq)


{
if (seq > 1) write_serial(','); //send a comma if needed
write_serial(' '); //send a space
if (seq > 19) write_serial(0x32) ; // write the 2 if needed
else if (seq > 9) write_serial(0x31); //write the 1 if needed
write_serial((seq%10)+0x30); //and the sequence number
}

void main(void)
{
PORTA=0xFF;
DDRA=0x00;
PORTC=0xFF;
DDRC=0xFF;
PORTD=0x00;
DDRD=0x00;
UCSRA=0x00;
UCSRB=0x18;
UCSRC=0x86;
UBRRH=0x00;
UBRRL=0x26;

while (1)
{
switch (state)
{
case 0:
state = 1; //go to display sequence
show_message(2); //playing
game_index = 0; //restart sequence at 0
break; //get outta here

Page 47 9/27/2005
Purdue University C

case 1:
LED_seq[game_index] = random; //store new LED number
add_seq(++game_index); //add & display index
for (x=0; x<game_index;x++)
{
PORTC = show[LED_seq[x]]; //show LED
}
state = 2;
pb_index = 0; //clear pb index for next response string
break;
case 2:
if(press = PINA)
{
state = 3; //go to debounce
}
break;
case 3:
x = 0; //prepare to detect key
while (!(press & 0x01))
{
press = press >> 1; //shift to find bit
++x; //count shifts
}
PB_seq[pb_index++] = x; //store number of pb pressed
state = 4; //go to await release
break;
case 4:
if (PINA == 0) //all released
{
state = 5;
}
break;
case 5:
if (pb_index != game_index) state = 2; //go back for more
else state = 6;
break;
case 6:
y = 0; //clear error flag
for (x=0;x<game_index;x++) if (LED_seq[x] != PB_seq[x]) ++y; //increment y
if wrong
if (y) state = 8; //exit if sequences don't match
else state = 7; //check for 20 sequence done
break;
case 7:
if (game_index == 20) state = 8; //game done if 20 right
else state = 1; //add one and do a new sequence
break;
case 8:
if (game_index == 20) show_message(6); //Simon Conqueror
else if (game_index > 12) show_message(5); //winner
else if (game_index > 5) show_message(4); //nerd
else show_message(3); //loser
state = 0;
show_message(0); //clear the screen for new game
break;
default:
break;
}
random = (++random & 0x7); //mod 8 random counter
};
}

Page 48 9/27/2005
Purdue University C

Appendix D
#include <mega16.h>
#include <stdio.h>

unsigned char state = 0; //set entry case


unsigned char random,game_index,pb_index,x,press,y;
unsigned char LED_seq[22]; //storage for LED output sequence
unsigned char PB_seq[22]; //storage for PB input sequence
unsigned char show[]={1,2,4,8,16,32,64,128}; //display bits
unsigned char message0[]= {" "}; //clear a line
unsigned char message1[]= {"Press a button to play. "}; //start message
unsigned char message2[]= {"Playing "}; //playing
unsigned char message3[]= {"Done. You are a loser!!!"};
unsigned char message4[]= {"Done. You are a nerd!! "};
unsigned char message5[]= {"Done. You are a winner! "};
unsigned char message6[]= {"Done Simon Conqueror! "}; //big winner

void add_seq(unsigned char seq)


{
if (seq > 1) printf("%c", 0x2c); //send a comma if needed
printf("%c", 0x20); //send a space
if (seq > 19) printf("%c", 0x32) ; // write the 2 if needed
else if (seq > 9) printf("%c", 0x31); //write the 1 if needed
printf("%c",((seq%10)+0x30)); //and the sequence number
}

void main(void)
{
PORTA=0xFF;
DDRA=0x00;
PORTC=0xFF;
DDRC=0xFF;
PORTD=0x00;
DDRD=0x00;
UCSRA=0x00;
UCSRB=0x18;
UCSRC=0x86;
UBRRH=0x00;
UBRRL=0x26;

while (1)
{
switch (state)
{
case 0:
state = 1; //go to display sequence
printf("%c",0xa); //linefeed if serial
printf("%c",0xd); //cr
printf("%s", message2); //playing
game_index = 0; //restart sequence at 0
break; //get outta here
case 1:
LED_seq[game_index] = random; //store new LED number
add_seq(++game_index); //add & display index
for (x=0; x<game_index;x++)
{
PORTC = show[LED_seq[x]]; //show LED
}
state = 2;
pb_index = 0; //clear pb index for next response string
break;
case 2:
if(press = PINA)
{
state = 3; //go to debounce
}
break;
case 3:
x = 0; //prepare to detect key
while (!(press & 0x01))

Page 49 9/27/2005
Purdue University C

{
press = press >> 1; //shift to find bit
++x; //count shifts
}
PB_seq[pb_index++] = x; //store number of pb pressed
state = 4; //go to await release
break;
case 4:
if (PINA == 0) //all released
{
state = 5;
}
break;
case 5:
if (pb_index != game_index) state = 2; //go back for more
else state = 6;
break;
case 6:
y = 0; //clear error flag
for (x=0;x<game_index;x++) if (LED_seq[x] != PB_seq[x]) ++y; //increment y
if wrong
if (y) state = 8; //exit if sequences don't match
else state = 7; //check for 20 sequence done
break;
case 7:
if (game_index == 20) state = 8; //game done if 20 right
else state = 1; //add one and do a new sequence
break;
case 8:
if (game_index == 20)
{
printf("%c",0xa); //linefeed if serial
printf("%c",0xd); //cr
printf("%s", message6); //playing
//printf("Done Simon Conqueror! "); //Simon Conqueror
}
else if (game_index > 12)
{
printf("%c",0xa); //linefeed if serial
printf("%c",0xd); //cr
printf("%s", message5); //playing
//printf("Done. You are a winner! "); //winner
}
else if (game_index > 5)
{
printf("%c",0xa); //linefeed if serial
printf("%c",0xd); //cr
printf("%s", message4); //playing
//printf("Done. You are a nerd!! "); //nerd
}
else
{
printf("%c",0xa); //linefeed if serial
printf("%c",0xd); //cr
printf("%s", message3); //playing
//printf("Done. You are a loser!!!"); //loser
}
state = 0;
printf("%c",0xa); //linefeed if serial
printf("%c",0xd); //cr
printf("%s", message0); //playing
//printf(" "); //clear the screen for new game
break;
default:
break;
}
random = (++random & 0x7); //mod 8 random counter
};
}

Page 50 9/27/2005
Purdue University C

Appendix E

NOTE: The user should remember that this code can not be completely simulated as is. The
user must remove the #defines and #includes for Studio to simulate the program properly.
/*****************************************************
This program was produced by the
CodeWizardAVR V1.24.2c Standard
Automatic Program Generator
© Copyright 1998-2004 Pavel Haiduc, HP InfoTech s.r.l.
http://www.hpinfotech.ro
e-mail:office@hpinfotech.ro

Project :
Version :
Date : 9/30/2004
Author : Dick Barnett
Company : Purdue University
Comments:
Chip type : ATmega16
Program type : Application
Clock frequency : 6.000000 MHz
Memory model : Small
External SRAM size : 0
Data Stack size : 256
*****************************************************/

#include <mega16.h>
// Standard Input/Output functions
#include <stdio.h>
#include <delay.h>

//define states for state machine


#define wait_pb 0
#define add_disp_seq 1
#define press_detect 2
#define press_debounce 3
#define release_detect 4
#define all_in_check 5
#define check_response 6
#define twenty_yet 7
#define game_over 8
#define start_up 9
#define beep_tone 187
#define rude_tone 20000

#define RS PORTD.7
#define RW PORTD.5
#define E PORTD.6

unsigned int delay_time, time_out_delay_time;


unsigned int beep,beep_off;
unsigned char state = 9; //set entry case
unsigned char random,previous_msg,game_index,pb_index,x,press,y;
unsigned char LED_seq[22]; //storage for LED output sequence
unsigned char PB_seq[22]; //storage for PB input sequence
unsigned char show[]={1,2,4,8,16,32,64,128}; //display bits

// Timer 0 overflow interrupt service routine


interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
TCNT0 = 256 - 94; //reload for 1 ms
if (beep)
{
--beep; //decr beep counter
DDRD = DDRD | 0x10; //set pd4 for output
}
else DDRD = DDRD & 0xEF; //set PD4 for input
if (delay_time) --delay_time; //count down delay
if (time_out_delay_time) --time_out_delay_time; //count down delay

Page 51 9/27/2005
Purdue University C

// Timer 1 output compare B interrupt service routine


interrupt [TIM1_COMPB] void timer1_compb_isr(void)
{
OCR1B = OCR1B + beep_off; //increment for next tone toggle
}

void write_lcd (unsigned char ch, unsigned char dat)


{
if (dat) RS = 1; //must be data
else RS = 0; //must be command
RW = 0; //read write = write
delay_time = 2; //delay at least 1 ms
while (delay_time);
E = 1; //enable high
PORTB = ch; //data to output
delay_time = 2; //delay a bit
while (delay_time);
E = 0; //drop enable
delay_time = 3; //delay at least 2 ms
while(delay_time);
}

void write_serial(unsigned char chs)


{
while (!(UCSRA & 0x20)) ; //wait for UDRE
UCSRA = 0x20; //clear UDRE
UDR =chs; //output character
}

void init_lcd (void)


{
unsigned char init[] = {0x38,0x38,0x38,0xc,0x1,0x6}; //init bytes
unsigned char x;
for (x=0;x<6;x++) write_lcd(init[x],0); //init lcd
}

void show_message(unsigned char msg)


{
unsigned char message[7][25] =
{" ", //clear a line
"Press a button to play. ", //start message
"Playing ", //playing
"Done. You are a loser!!!",
"Done. You are a nerd!! ",
"Done. You are a winner! ",
"Done Simon Conqueror! "}; //big winner

unsigned char *ptr;


if (PIND.3)
{
write_lcd(0x1,0); //clear and home if LCD
ptr = &message[previous_msg][0]; //point to previous message
while (*ptr) write_lcd(*ptr++,1); //show previous message in line 1
write_lcd(0xc0,0); //point to beginning of line 2
}
else
{
write_serial (0xa); //linefeed if serial
write_serial(0xd); //cr
}
ptr = &message[msg][0]; //point to message
while (*ptr > 0)
{
if (PIND.3) write_lcd(*ptr++,1); //send character if LCD
else write_serial(*ptr++); //send if serial
}
previous_msg = msg; //retain information on previous message
}

Page 52 9/27/2005
Purdue University C

void lcd_add_seq(unsigned char seq)


{
if (PIND.3)
{
write_lcd(0xc9,0); //place the cursor after 'playing'
if (seq > 19) write_lcd(0x32,1) ; // write the 2 if needed
else if (seq > 9) write_lcd(0x31,1); //write the 1 if needed
write_lcd((seq%10)+0x30,1); //and the sequence number
}
else
{
if (seq > 1) write_serial(','); //send a comma if needed
write_serial(' '); //send a space
if (seq > 19) write_serial(0x32) ; // write the 2 if needed
else if (seq > 9) write_serial(0x31); //write the 1 if needed
write_serial((seq%10)+0x30); //and the sequence number
}
}

// Declare your global variables here

void main(void)
{
// Declare your local variables here

// Input/Output Ports initialization


// Port A initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=P State6=P State5=P State4=P State3=P State2=P State1=P State0=P
PORTA=0xFF;
DDRA=0x00;

// Port B initialization
// Func7=Out Func6=Out Func5=Out Func4=Out Func3=Out Func2=Out Func1=Out Func0=Out
// State7=0 State6=0 State5=0 State4=0 State3=0 State2=0 State1=0 State0=0
PORTB=0x00;
DDRB=0xFF;

// Port C initialization
// Func7=Out Func6=Out Func5=Out Func4=Out Func3=Out Func2=Out Func1=Out Func0=Out
// State7=1 State6=1 State5=1 State4=1 State3=1 State2=1 State1=1 State0=1
PORTC=0xFF;
DDRC=0xFF;

// Port D initialization
// Func7=Out Func6=Out Func5=Out Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=0 State6=0 State5=0 State4=T State3=T State2=T State1=T State0=T
PORTD=0x00;
DDRD=0xE0;

// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 93.750 kHz
// Mode: Normal top=FFh
// OC0 output: Disconnected
TCCR0=0x03;
TCNT0=0x00;
OCR0=0x00;

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 750.000 kHz
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Toggle
// Noise Canceler: Off
// Input Capture on Falling Edge
TCCR1A=0x10;
TCCR1B=0x02;
TCNT1H=0x00;
TCNT1L=0x00;

Page 53 9/27/2005
Purdue University C

ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;

// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer 2 Stopped
// Mode: Normal top=FFh
// OC2 output: Disconnected
ASSR=0x00;
TCCR2=0x00;
TCNT2=0x00;
OCR2=0x00;

// External Interrupt(s) initialization


// INT0: Off
// INT1: Off
// INT2: Off
MCUCR=0x00;
MCUCSR=0x00;

// Timer(s)/Counter(s) Interrupt(s) initialization


TIMSK=0x09;

// USART initialization
// Communication Parameters: 8 Data, 1 Stop, No Parity
// USART Receiver: On
// USART Transmitter: On
// USART Mode: Asynchronous
// USART Baud rate: 9600
UCSRA=0x00;
UCSRB=0x18;
UCSRC=0x86;
UBRRH=0x00;
UBRRL=0x26;

// Analog Comparator initialization


// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
// Analog Comparator Output: Off
ACSR=0x80;
SFIOR=0x00;

// Global enable interrupts


#asm("sei")

while (1)
{
switch (state)
{
case start_up:
init_lcd();
show_message(1); //first message
PORTC=0x00; //LEDs off
beep_off = beep_tone; //set for pleasing beep
state = wait_pb;
break;
case wait_pb:
if (PINA) //wait any key pressed
{
state = add_disp_seq; //go to display sequence
beep = 15; //short beep to acknowledge button press
show_message(2); //playing
game_index = 0; //restart sequence at 0
}
break; //get outta here
case add_disp_seq:
LED_seq[game_index] = random; //store new LED number

Page 54 9/27/2005
Purdue University C

lcd_add_seq(++game_index); //add & display index


delay_time = 2000; //wait to start sequence
while(delay_time | delay_time);
for (x=0; x<game_index;x++)
{
PORTC = show[LED_seq[x]]; //show LED
delay_time = 500; //show LED for 1/2 second
while(delay_time | delay_time);
PORTC = 0x00; //clear LEDs for .3 second
delay_time = 300;
while(delay_time | delay_time);
}
beep = 100; //longer beep to show end of sequence
state = press_detect;
time_out_delay_time = 4000; //set for 4 second time-out of key press
pb_index = 0; //clear pb index for next response string
break;
case press_detect:
if(press = PINA)
{
state = press_debounce; //go to debounce
beep = 15; //make a key beep
time_out_delay_time = 4000; //set for 4 second time out on key press
}
if (!time_out_delay_time)
{
PB_seq[pb_index++] = 0xff; //put a known wrong value into PB sequence
state = check_response; //exit to check responses
}
break;
case press_debounce:
x = 0; //prepare to detect key
while (!(press & 0x01))
{
press = press >> 1; //shift to find bit
++x; //count shifts
}
PB_seq[pb_index++] = x; //store number of pb pressed
delay_time = 100; //debounce input
while(delay_time); //wait it out
state = release_detect; //go to await release
break;
case release_detect:
if (PINA == 0) //all released
{
state = all_in_check;
}
break;
case all_in_check:
if (pb_index != game_index) state = press_detect; //go back for more
else state = check_response;
break;
case check_response:
y = 0; //clear error flag
for (x=0;x<game_index;x++) if (LED_seq[x] != PB_seq[x]) ++y;
//increment y if wrong
if (y) state = game_over; //exit if sequences don't match
else state = twenty_yet; //check for 20 sequence done
break;
case twenty_yet:
if (game_index == 20) state = game_over; //game done if 20 right
else state = add_disp_seq; //add one and do a new sequence
break;
case game_over:
if(y) beep_off = rude_tone; //set for rude sound
beep = 500; //1/2 second tone
if (game_index == 20) show_message(6); //Simon Conqueror
else if (game_index > 12) show_message(5); //winner
else if (game_index > 5) show_message(4); //nerd
else show_message(3); //loser
state = start_up;

Page 55 9/27/2005
Purdue University C

delay_time = 2000; //2 seconds game result display


while(delay_time | delay_time);
show_message(0); //clear the screen for new game
break;
default:
break;
}
random = (++random & 0x7); //mod 8 random counter
};
}

Page 56 9/27/2005

Das könnte Ihnen auch gefallen