Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Note:
When file avr/io.h is included in the .c file of a ATmega32A device C/C++ project, the file iom32.h is
inhibitly included in the source file. This file contain declaration of constants values for keywords of
ATmega32 registers address, pins names, bitnames and other entities. The copy of the file iom32.h can
be found in the same folder of this document.
Figure A.1
Note
Notice the alternate name of the Ports pins in brackets. The AVR microcontollers are designed to
allow dual use of most of its pins (though a few has single or triple use). This has the advantage
of allowing a developer to use these pins as I/O pins if the function they are provided for is not
being utilized.
In this AVR tutorial we are only concern with the pins in there Digital I/O function, so for now
just forget they have alternate capabilities.
The pins of the AVR microcontroller are not fixed as input or output at the manufacturing stage,
these pins are software configurable which is the topic of the section below.
DDRx is an 8-bit register which stores configuration information for the pins of Portx.
Writing a 1 in the pin location in the DDRx makes the physical pin of that port an output pin
and writing a 0 makes that pin an input pin.
Note: Each physical pin of a port is configured independently and thus a port can have some
of its pins configured as input and the others as output pins.
PINx is an 8-bit register that stores the logic value, the current state, of the physical pins
on Portx when DDRx is 0. So to read the values on the pins of Portx, you read the values
that are in its PIN register.
Logic values from the pins on Portx that are configured as input pins are read
from the PINx register.
PORTx is an 8-bit register which stores the logic values that currently being outputted on
the physical pins of Portx if the pins are configured as output pins. So to write values to a
port, you write the values to the PORT register of that port when DDRx is 1.
Logic values written to the PORTx register is outputted to the pins on Portx that are
configured as output pins.
Logic values read from the PINx register is equivalent to the values presently on the
pins of Portx which are configured as input pins.
The AVR assembly code below shows how to configure the pins on portA of an AVR
ATMega32A micocontroller.
Code section below shows how to write to or read from the pins of an AVR microcontroller
port once they are configured. Assume here the configurations from the assembly code
above.
In C/C++ Language:
#include <avr/io.h>
Code section below shows how to write to or read from the pins of an AVR
microcontroller port once they are configured. Assume here the configurations from the
C/C++ code above.
Important Note: Be reminded that in order for the recognize the I/O registers aliases,
such as DDRA, DDRB, PORTA, PINB, etc., you must include the header file avr/io.h.
1. The ports must be configured before they are used. The first four (4) lines of the
program are for configuration.
2. Once the ports are configured you can then write to or read from them, were
applicable.
The keywords of I/O Registers (in this case PORTD DDRD, PORTB and DDRB) are
declared in file m32defA.inc which by default are included by Atmel Studio 6 after
you have selected ATmega32A as the device in the project.
For simplicity, we will focus on pull-ups since they are more common than pull-downs. They operate
using the same concepts, except the pull-up resistor is connected to the high voltage (this is usually
3.3V or 5V and is often referred to as VCC) and the pull-down resistor is connected to ground.
With a pull-up resistor, the input pin will read a high state when the button is not pressed. In
other words, a small amount of current is flowing between VCC and the input pin (not to
ground), thus the input pin reads close to VCC. When the button is pressed, it connects the input
pin directly to ground. The current flows through the resistor to ground, thus the input pin reads
a low state. Keep in mind, if the resistor wasnt there, your button would connect VCC to ground,
which is very bad and is also known as a short.
Since pull-up resistors are so commonly needed, many MCUs, like the ATmega32A
microcontroller, have internal pull-ups that can be enabled and disabled.
All Input/Output port pins have individually selectable pull-up resistors with a supply-voltage
invariant resistance. Referring to Figure A.2 which is an excerpt from page 51 of file Atmega32
Reference manual.pdf, if PORTxn is written logic one when the pin is configured as an input pin,
the pull-up resistor is activated. To switch the pull-up resistor off, PORTxn has to be written logic
zero or the pin has to be configured as an output pin. The port pins are tri-stated when a reset
condition becomes active, even if no clocks are running.
If PORTxn is written logic one when the pin is configured as an output pin, the port pin is driven
high (one). If PORTxn is written logic zero when the pin is configured as an output pin, the port
pin is driven low (zero).
A Digital I/O pin configured as an active low interrupt pin or an input pin, pull-up resistor
are necessary so that a stable logic I/high level state when the input on open circuit
condition.
By default PUD=0 and is specifically programmed to change it.
Normally, the pull-up enabled state is fully acceptable, as a high-impedant environment will
not notice the difference between a strong high driver and a pull-up. If this is not the case,
the PUD bit in the SFIOR Register can be set to disable all pull-ups in all ports.
Port pins can provide internal pull-up resistors (selected for each bit). If PORTxn is written
logic one when the pin is configured as an input pin, the pull-up resistor is activated. To
switch the pull-up resistor off, PORTxn has to be written logic zero or the pin has to be
configured as an output pin. The port pins are tri-stated when a reset condition becomes
active, even if no clocks are running..
To enable internal pull-ups on PortA which will function as Input port, you can use the
following line of code in your setup process:
Since Timer works independently of CPU it can be used to measure time accurately. Timer upon
certain conditions takes some action automatically or informs CPU. One of the basic conditions is
the situation when timer OVERFLOWS i.e. its counted up to its maximum value (255 for 8 BIT
timers) and rolled back to 0. In this situation timer will issue an Overflow Flag (TOVn i.e. TOV0
for TIMER0), or can invoke an interrupt (TIMERn OVF i.e. TIMER1 OVF for TIMER0) but you
must write an Interrupt Service Routine (ISR) to handle the event.
TOVn 0 (TOVn 1)
TOVx 0
TOVx = 1
Timer (TCNTx) can be initialise between 0 and 255 (for 8 bit TIMER)
Figure A.2(a)(i)The waveform of TCNT0 which is initialised to start from count from
value 0x05 every time it reaches peak value 0xFF
In principle, a timer is a simple counter. The input clock of microcontroller and operation
of the timer is independent of the program execution. The deterministic clock makes it
possible to measure time by counting the elapsed cycles and take the input frequency of
the timer into account.
All the Atmel microcontrollers have Timers as an inbuilt peripheral. In this article we are
going to use the target controller ATmega32. ATmega32 comes with two 8 bit and one
16 bit timer. This means that there are 3 sets of timers, each with the ability to count at
different rates. The two 8-bit counters can count to 255 whilst the 16 bit counter can
count to 65,535. Timers can run asynchronous to the main AVR core it means timers are
totally independent of CPU. A timer is usually specified by the maximum value to which it
can count called MAX beyond which it overflows and resets to zero is called BOTTOM.
The speed of counting can be controlled by varying the speed of clock input to it. In
ATmega32 we have three different kinds of timers as follows
However the total count taken must be added by 1 considering 1 count is take for the roll
over of count from MAX to 0
Figure A.2(a)(ii)
Taking
Figure A.2(a)(iii)
iii. The Prescaler
In short, the prescaler divide the CPU frequency (F_CPU ) drive a lower frequency into the
Timer, without effecting actual F_CPU. Thus the Prescaler allow limit of delay derived from
the CPU clock be increased.
By decreasing the Timer Clock we can increase the Clock Time Period proportionally. Thus we
can get a longer delay limited to the MAX count of the Timer
Assume we supply a 1MHz signal to timer and an 8 bit timer which can count max up to
255. Using the formula, we discussed that we can get a maximum delay of 256 s. Just
imagine, what if we need delay greater than 250ms ? One of the easiest thing we can do is to
use the timers prescaler that reduces the Timer0 clock. Prescaler allows us to divide up the
incoming clock signal by power of 2. It reduces the resolution which means that the accuracy
has decreased but giving us the longer timer range.
Prescalar can be set to produce the following clocks for the Timers:
1. No Clock (Timer Stop).
2. No Prescaling (Clock = FCPU)
3. F_CPU/8
4. F_CPU/32
5. F_CPU/64
6. F_CPU/256
7. F_CPU/1024
8. Timer can also be externally clocked
v. Choosing Prescalers
10
Now out of these four prescalers, 8 cannot be used as the timer value exceeds the limit of
65535. Also, since the timer always takes up integer values, 1024 is not accurate as the timer
count is a decimal digit (no decimal point) so either a count 717 or 718 (the nearest) can be
chosen. Prescaler values of 64 and 256 are feasible if accurate timing is needed. But out of
these two, prescaler value of 64 provides greater resolution. Prescaler value of 256 if chosen
proved the timer a greater duration if needed.
Thus, choose prescaler which gives the counter value within the feasible limit (255 for 8
bit timer or 65535 16 bit timer) and the counter value should always be an integer.
11
In this Lab the following registers are of concern because they are used in the program:
i. TCNT0/TCNT2 Register
The Timer/Counter Register (TCNT0/TCNT1) itself is what all timer modes base on. It
counts System Clock ticks, prescaled from system clock or from the external clock.
In TCNT0 will be filled will value (255-Time Count) to start count of TCNT0 so that
Time Count is counted before the TOV0 flag is set.
12
The Timer/Counter Control register is used to set the timer mode, prescaler and to Start or Stop
Timer.
In Normal Mode and we are taking Timer 0 as the example, we will concentrate on the
highlighted bits. The other bits will be discussed as and when necessary. By selecting
these three Clock Select Bits, CS02:00, we set the timer up by choosing proper
prescaler. The possible combinations are shown below and the functions of each
selection are described in red font..
The highlighted three Clock Select Bits, CS22:20 in TCCR2 also has similar
function as in TCCR0 which are specified as follows:
13
In TIMER0 the prescalers available are 8, 64, 256 and 1024, whereas in TIMER2, we
have 8, 32, 64, 128, 256 and 1024
Here we are concerned with the 0th bit - TOV0 bit (Timer/Counter0 Overflow Flag) and the 6th bit
TOV2 (Timer/Counter2 Overflow Flag). These bits is set (one) whenever the corresponding timer
overflows. It is cleared automatically whenever the corresponding Interrupt Service Routine (ISR) is
executed. Alternatively, we can clear it by writing 1 to it.
TIFR register holds the TOV0/TOV2 (and the TOV1) bits that indicates that Timer overflow
occurred when the count has reached maximum (0xFF) and then rolls over to 0x00 (0xFFFF
for Timer 1).
v. TIMSK
The Timer/Counter Interrupt Mask TIMSK Register is a common register for all the
three timers. For TIMER0, bits 1 and 0 are allotted. For TIMER1 bits 2, 3, 4 and 5 are
allotted. For TIMER2, bits 6 and 7 are allotted.
Setting 0th bit - TOIE0 bit to 1 enables the TIMER0 overflow Flag interrupt. When we
enable the overflow interrupt, TIMER0_OVF interrupt will be triggered when TOV0
changes from 0 to 1.
14
Timer 1 Description
TCNT1 16-bit counter register
TCCR1A Mode of operation and other settings
TCCR1B Mode of operation, prescaler and other settings
OCR1A 16-bit Compare Register A
OCR1B 16 bit Compare Register B
TIMSK Interrupt Mask Register
TIFR0 Timer/Counter Interrupt Flag Register
In this Lab the following registers are of concern because they are used in the program:
The Timer/Counter Register (TCNT1) itself is what all timer modes base on. It counts
System Clock ticks, prescaled from system clock or from the external clock.
In TCNT1 will be filled will value (65535-Time Count) to start count of TCNT1 so that
Time Count is counted before the TOV1 flag is set.
The TCCR1B Timer/Counter Control register is used to set the timer mode, prescaler and to Start
or Stop Timer.
The bit 2:0 CS12:10 are the Clock Select Bits of TIMER1. Their selection is as follows:
15
iii. TIFR
It holds the TOV1 bit that indicates that Timer overflow occurred (count has reached
maximum (0xFFFF) and then rolls over to 0x0000.
This is a common register for all the timers. The bits associated with other timers are greyed out.
Bits 5:2 correspond to TIMER1. Right now, we are interested in the yellow bit only. Other bits are
related to CTC mode which we will discuss later. Bit 2 TOIE1 Timer/Counter1 Overflow
Interrupt Enable bit enables the overflow interrupt of TIMER1. When we enable the overflow
interrupt TIMER1_OVF interrupt will be triggered when TOV1 changes from 0 to 1.
.
Normal
CTC
Fast PWM
Phase correct PWM
To use the Timer to generate Delay, Normal mode needs to be used. To select Normal mode, all
WGMxx bits in the respective TCCRx registers must be cleared. This can be done when selecting
the Prescaler of the respective Timers TCCRx Registers.
16
Figure A.4(f)
Referring to Section A.3(g) in this document, in a subroutine (if in assembly language) or function
(if in C/C++ language), we start TCNT0 with (255-243) i.e. 12 and starts the counting up to the
255 (by setting TCCR with prescaler=1024) , after this state (count) TOV0 will be generated, the
time taken will be 250 mSec. We will keep check the TOV1 bit to identify the 250sec delay.
Then we can Clear TOV0 bit, and restart Timer when needed.
Delay250msUsingTimer0:
/*Referring to the Theory and calculations in Section A.3
of "Appendix A For SKEE3732 Laboratory 2 Sheet"*/
// Refer Section A.3(k)
ldi r16,255-243
out TCNT0,r16 //start TCNT0 with (255-243) i.e. 12
ldi r16,(1<<CS02) | (1<<CS00)
out TCCR0,r16 //Set Prescaler =1024
wait: //Do
// {
in r16,TIFR
sbrs r16,TOV0 //Read status of TOV0 bit in TIFR
rjmp wait // }while TOV0==0
ldi r16,1>>TOV0
out TIFR,r16 //Clear TOV0 bit
ret
17
void Delay250msUsingTimer0(void)
{
// Refer Section A.3(k)
TCNT0 = 255-243; /*start TCNT0 with (255-243) i.e. 12
*/
TCCR0 = (1<<CS02) | (1<<CS00); //Set Prescaler =1024
volatile uint8_t TIFRdata;
do //Wait
{
TIFRdata=TIFR&(1<<TOV0); //Read status of TOV0 bit in TIFR
A.5. Interrupts.
Any reasonable microprocessor system must have several external devices connected to it and
must be able to communicate with these devices. All of these devices must have some sort of
AVAILABLE signal telling the microprocessor that data is available for it or that it is available for
use. There are two methods for a microprocessor to communicate with external devices: Polling
and interrupts.
Polling is the act of periodically querying the AVAILABLE lines on all the input devices to see if data is
available. The disadvantage of polling is that the polling routine must be entered periodically (very
often if high speed data is coming through) and there may be too little time for the microprocessor
to execute its program.
Interrupts is an alternative, more efficient method of communication. Here, when the I/O device
has data for the microprocessor or requires some other service it issues an interrupt request. An
interrupt request can come at any time during the execution of a program and generally comes
while the microprocessor is executing an instruction. The following figure shows the
microprocessor executing its main program when an interrupt request occurs.
18
An interrupt service routine in other words is just like a subroutine; except that it is not anticipated
by the processor to occur at a particular time, since there are no explicitly placed calls to it in the
program.
In order for the microcontroller to respond to an interrupt event the interrupt feature of the
microcontroller must be enabled along with the specific interrupt. This is done by setting the
Interrupt Enable bit of the specific interrupt and the Global Interrupt Enabled bit.
a. Interrupt vector table (Location of information to tell CPU where to find the service routine
of the respective interrupt)
An interrupt vector table: a table of interrupt vectors (pointers to Interrupt Service Routine
that handle interrupts). When source of Interrupt is generated (after it has been enabled and
Global Interrupt Enabled bit is set), PC (program Counter) will be initialised with value
Vector Address defined under the Program Address column of the interrupt generated. The
event of interrupt is given under the Interrupt Definition column.
For Example when RESET occur, PC will be initialised with $0000 (though the number is 3 hex
digit under Program Address, a 4 hex digit number will be stored to PC because PC is a 16 bit
register). If INT2 interrupt occurs, PC will be initialised with $0006. Except RESET that does
not have a current instruction that it is executing, when an interrupt occur, the CPU will
complete the current instruction that it is executing and save the address of PC (next
instruction to be executed) to the STACK before PC is initialised with interrupt vector Address.
Refer Section A.5(b) for example of implementation.
19
Figure A.5 (a)(i): Interrupt Vector Table reserved in Program Memory for ATmega32 (ATmega32A)
20
Figure A.5 (a)(ii): Atmel Studio C/C++ Interrupt Service Routine Keywords for the ATmega32/ATmega32A
Vector Table (Refer Figure A.5 (e) for pins defined for vector 1 thru 4)
21
An Interrupt Service Routine (ISR) or Interrupt Handler is a piece of code that should be
executed when an interrupt is triggered. Usually each enabled interrupt has its own ISR. In
AVR assembly language each ISR MUST end with the RETI instruction which indicates the
end of the ISR.
In C language programming, when the Interrupt Service Routine Keywords is used as the
name of the function, the RETI instruction will be inserted by default by the Atmel Studio
C/C++ (GCC) compiler.
Example in Setting Up INT0, INT1 and INT2 ISR (Interrupt Service Routine)
Int1ISR:
//Codes here
reti
Int2ISR:
//Codes here
reti
The interrupt service routine will be set to be called by executing a jump <address of ISR> at
the vector address of interrupt defined in the Interrupt vector Table. Example of
implementation:
In C language programming, when the Interrupt Service Routine Keywords is used as the
jump <address of ISR> will be inserted by default by the C/C++ (GCC) compiler.
Example in Setting Up INT0, INT1 and INT2 ISR (Interrupt Service Routine)
22
c. Interrupt Flags and Enabled bits (Tell CPU that particular interrupts are enabled)
Each interrupt is associated with two (2) bits, an Interrupt Flag Bit and an Interrupt
Enabled Bit. These bits are located in the I/O registers associated with the specific interrupt:
The interrupt flag bit is set whenever the interrupt event occurs, whether or not
the interrupt is enabled.
The interrupt enabled bit is used to enable or disable a specific interrupt. Basically
is tells the microcontroller whether or not it should respond to the interrupt if it is
triggered.
In summary basically both the Interrupt Flag and the Interrupt Enabled are required for
an interrupt request to be generated as shown in the Figure A.5 (c).
d. Global Interrupt Enabled Bit (Tell CPU to service all interrupts are enabled)
Apart from the enabled bits for the specific interrupts the global interrupt enabled bit MUST
be enabled for interrupts to be activated in the microcontroller.
For the AVR 8-bits microcontroller this bit is located in the Status I/O Register (SREG). The
Global Interrupt Enabled is bit 7, the I bit, in the SREG.
In assembly language the SEI instruction set the I bit and the CLI instruction clear the bit.
In C/C++ language the sei() function set the I bit and the cli() function clears the bit. The functions
are declared in avr/interrupt.h which must be included in the program.
23
1. The RESET interrupt - Triggered from pin 9 (executed like an interrupt but does not
operate as an interrupt because RETI instruction cannot be used in RESET
Interrupt service routine.
2. External Interrupt 0 (INT0) - Triggered from pin 16.
3. External Interrupt 1 (INT1) - Triggered from pin 17.
4. External Interrupt 2 (INT2) - Triggered from pin 3.
Important Notes:
Please note here that the memory location for interrupt vector table which should be
reserved for storing pointers to interrupt handlers (ISR) are not used to store regular
programs code. For ATmega32/ ATMega32A should not use address below 0x2A i.e. the
vector interrupt table memory area.
f. Very Important When Writing Assembly Codes Utilizing the Interrupt Feature
When writing assembly codes for ATmega32A utilizing the interrupt feature the following
MUST be observed:
The interrupt MUST be enabled by setting its enabled bit in the appropriate I/O
register i.e. the General Interrupt Control Register (GICR).
24
RETI will also set I bit so that Global interrupt is enabled because I bit is cleared
when CPU service any interrupt.
25
Figure A.6: JTAGICE mklI Device and its JTAG connector, USB port and USB cable
26
The board will power up when with 5V supply via the USB port. The ATmega32A RESET pin
switch connected to a push button as shown in Figure A.6(a) and Figure A.6(b). The VREF
voltage can be adjusted from 0 5V as shown in Figure A.6(c) and Figure A.6(d). Two pins
jumper terminals is connect to every I/O pins of the ATmega32A chip as shown in Figure A.6(d)
which will allow user to optionally connects it to peripheral devices.
Reset Button:
Figure A.6(a)
27
Figure A.6(f)
Figure A.6(e)
The board is built-in with the following peripherals which can be connected to the ATmega32A
using jumper wires:
4. Potentiometers
6. Buzzer
28
Figure A.6(g)
29
The seven LEDs in the seven segment display are labelled with the letters a, b, c, d, e, f and as indicated in Fig A6(d) below. Some seven segment
display may include 1 or 2 additional LEDs which are used as decimal point(s).
Fig A.6(j)
Similarly
31
1. Common cathode display - As we know seven segment display are made by combining 8 LED's. So in this configuration negative lead of all
LED's are connected together and remaining leads used as segment display. Current limiting resistors are connected in series with each
individual segment. To switch ON any of the LED, the segment lead must be given a logical HIGH voltage.
2. Common anode display- In this configuration positive pin of all 8 LED's connected together and negative leads are used as segment display
pin's. To switch ON any of the LED, the segment lead must be given a logical HIGH voltage.
Now suppose you want to make numeric "2" than segment which we have to switch ON are "a", "b", "g", "e", "d".
Now suppose you want to display numeric "4", the segment which are switched ON is "4" are "f", "g", "b", "c".
Now suppose you want to display alphabet "L", then segment which are switch ON are "f", "e", "d".
Figure A.6(ii) show possible alphabet character that can be display by a 7-segment Display
Fig A6(k)
Example Seven Segment (SS) code for Active Low Characters A b and c are 0x77, 0b01111100 and 0x4e respectively.
32
Figure A.6(l)
33
Figure A.6(m)
Figure A.6(n)
34
Layout
Figure A.6(p)
vi. Buffer
s
ULN2
803
Figure A.6(r)Circuit
Figure A.6(q): Layout
35
Correct JTAG connection: The red stripe of the cable must be pointing towards the edge of the
board.
Important, if JTAGICE MkII JTAG connected to the Gotronik Atmega32A Target Board:
1. Always switch ON JTAGICE MkII first before the Gotronik Atmega32A Target Board.
2. Always switch OFF the Gotronik Atmega32A first Target Board before JTAGICE MkII
3. Or remove JTAG connection and either device can be independently switch ON of OFF.
4. You may at any time JTAG connect or disconnect them if both device already ON of OFF.
36
Table A.8(a)
Atmega32A Chip Peripheral connectors
PortA pins 8 bits LED panel connector (Leftmost LED connected to MSB of PortA)
(External logical connection to PORTA)
37
This basic operating system can be described with the following flowchart:
38
Reset
Comman
d
Get Command
Execute Command
Figure A.8(b)
i. Environment Variables
ii. Prompt
Some kind of output, e.g. buzzer, LED blinks, cursor blinks, clock display, etc. etc. showing
that the system is alive and in a shell waiting for a command (input)
The heart of the OS that repetitively check for command and execute command received.
These two processes complement each other as the brain of the OS. Without these two
processes the OS has no intelligence receiving command or executing commands received.
Command may not be from human. It can be from switches, sensors or communication device.
output may be to transducers, display device or communication device.
v. A Simple OS that uses Polling method to receive command for the ATmega32 Target board
Based on the connections given in Table A.8(a), and flowchart in Figure A.8(b) we will build a
OS on the ATmega32 which will have the following functions:
39
2. The counting from 0 to 9 and roll over to 0 is stored in a variable which ore updated in
the Shell Loop.
3. If any of the following condition occur on any of the Push Button switches (become the
command), the respective Function as shown in Table A.8(b) will occur (executed):
Table A.8(b)
Push Button # Function at LED panels (at pace of 250 millisecond)
pressed
SW7 Display Running Light sequence
SW6 Display Flashing Light sequence (to be implemented by
Students)
SW5 Display Knight Rider Lights sequence
SW4 Display Alternate Light sequence (to be implemented by
Students)
40
The Program of the firmware, Lab2Pre.asm.c is given in Figure A.8(d) which is built based on the
flowchart in Figure A.8(c) .
Table A.8(c)
Description of objects in Flowchart
41
On Reset
Abbreviation:
T = True
Initialise all Environment variables F = False
T F
Any
Switch
Pressed?
SW7
pressed T RunningLights()
?
F
Display Count character at DS1
Wait 250ms
SW6
T FlashingLights()
pressed?
Update Count
F
SW5
pressed? T KRiderLights()
SW4
pressed? T AlternatLights()
Figure A.8(c)
42
/*
When Debugger and Target board is already connected,
you can check the CPU clock from the Tools|Device Programming and after JTAGICE MkII ATmega32A,
JTAG,
Apply and Read if the Device Signature display 0x1E9502 then select Fuses Tab, and under Fuse Name
in SUT_CKSEL Tab. INTRCOSC_1MHZ_6CK_64MS_DEFAULT denotes the 1Mz CPU clock
*/
/*
Connections:
Refer A.8 of "Appendix A For SKEE3732 Laboratory 2 Sheet.pdf"
Wiring connections are specified in Table A.8(a)
The Seven Segment Display Panel SEGMENT lines (dp, g, f, e, d, c, b and a) are
connected to Port B (PB7, PB6, PB5, PB4, PB3, PB2, PB1 and PB0 pins respectively).
8 bit LEDs of the LED panel is connected to Port A pins of ATmega32 chip
SW7, SW6, SW5 and SW4 which are connected to PD7, PD6, PD5 and PD4 respectively.
SW1 and SW0 is connected to PD3(INT1) and PD2(INT0)
SW7, Sw6, Sw5, SW4, SW3, SW2, SW1 and SW0 are Active Low Inputs.
Input from SW3, SW2, SW1 and SW0 are ignored in this program
SW7 Pressed: Perform Running Light sequence on LED Panel at a 1 sec pace
SW6 Pressed: Perform Flashing Light sequence on LED Panel
SW5 Pressed: Perform Knight Rider Light sequence on LED Panel
SW4 Pressed: Perform Alternate Light sequence on LED Panel
None pressed: Last LED display when switch is released
*/
/*In this Laboratory 2a, we will use standard C99 data types instead of the ANSi C
defined as follows:
int8_t instead of char
uint8_t instead of unsigned char
int16_t instead of int
uint16_t instead of unsigned int
uint32_t instead of unsigned long
int32_t instead of long
int64_t instead of long long
uint64_t instead of unsigned long long
*/
/*
Headers of Library used
Refer http://www.nongnu.org/avr-libc/user-manual/modules.html
*/
#include <avr/io.h>
43
/*
Identifier that represent constant value which logically represent
the functional use of the constant
*/
/*
Push Button switches Panel Data
A '0' denotes that switch on the respective bit is pressed
*/
#define SW7Pressed 0b01111111
//#define SW6Pressed 0b10111111
#define SW5Pressed 0b11011111
//#define SW4Pressed 0b11101111
/*
Look-up table in Program Memory for Numeric Digit
Refer Section C.2 of "Appendix C For SKEE3732 Laboratory 1 Sheet.pdf"
*/
const uint8_t SS_table[10] PROGMEM = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d, 0x07, 0x7f, 0x6f};
/*
Prototypes declaration
*/
void OutLED(volatile uint8_t);
void DelaymsUsingTimer2(volatile uint8_t);
/*
Function name: Delay2sUsingTimer1()
-----------------------------------
Operation: Wait for 1 sec before exiting
*/
/*
Theory:
Referring to the Theory and calculations in Section A.2
of "Appendix A For SKEE3732 Laboratory 2 Sheet"
Note: Maximum count (highest value +1) for 16 bit register = 65536 .
44
Since 1 count is added in the count process (changes from 0xFFFF to 0x0000
before the TOV2 bit is set)
Actual count = Count -1
*/
void Delay2sUsingTimer1(void)
{
// Refer Section A.3 and A.4 of "Appendix A For SKEE3732 Laboratory 2 Sheet"
TCNT1 = 65535-31249; /*start TCNT1 with (65535-31249) i.e. 34286
so that counter TCNT1 will count 31249*/
TCCR1B = (1<<CS11) | (1<<CS10); //Set Prescaler =64
static volatile uint8_t TIFRdata;
do //Wait
{
TIFRdata=TIFR&(1<<TOV1); //Read status of TOV0 bit in TIFR
} while (TIFRdata==0); //until TOV0==1
TCCR1B = 0; //Set Prescaler = 0, no clock and thus stop timer
TIFR=TIFR|1<<TOV1; //Clear TOV0 bit by writing 1 to TOV0 bit;
}
/*
Function name: OutDataDS1()
---------------------------
Operation: Display character in received in "data" at DS1 Seven Segment
*/
void OutDataDS1(uint8_t data)
{
volatile uint8_t SSData;
SSData=pgm_read_byte_near(&SS_table[data]); //Get Seven Segment Character Code for data
DDRB=OutPort;
DDRC=OutPort;
PORTB=SSData^ActveLowOutput; //Write to Port B from !tempdata
PORTC=OnDS1;
}
/*
Function name: DoRunningLight()
-------------------------------
Operation: Display one pattern of the current sequence, wait 250ms,
update RunningLEDdata with the next pattern of the sequence and exit
Input: Environment Data "RunningLEDdata"
output: updated "RunningLEDdata"
*/
void DoRunningLight(void)
{
OutLED(RunningLEDdata); //Write RunningLEDdata at LED panel
/*
Update RunningLEDdata for next sequence
*/
if (RunningLeft==True) //If LEDs is running Left
{
if (RunningLEDdata==0b10000000) // If Leftmost is reached
{
45
/*
Function name: DoKnightRiderLight()
-----------------------------------
Operation: Display one pattern of the current sequence, wait 250ms,
update KnightRiderLEDdata with the next pattern of the sequence and exit
Input: Environment Data "KnightRiderLEDdata"
output: updated "KnightRiderLEDdata"
*/
void DoKnightRiderLight(void)
{
OutLED(KnightRiderLEDdata); //Write KnightRiderLEDdata at LED panel
DelaymsUsingTimer2(250); //wait for 250ms
if (RunningInwards==True) //If sequence is running Inwards
{
switch(KnightRiderLEDdata)
{
case 0b10000001:
KnightRiderLEDdata=0b01000010;
break;
case 0b01000010:
KnightRiderLEDdata=0b00100100;
break;
case 0b00100100:
KnightRiderLEDdata=0b00011000;
break;
case 0b00011000:
RunningInwards=False;
break;
}
}
else //Else (sequence is running Oytwards)
{
switch(KnightRiderLEDdata)
{
case 0b00011000:
KnightRiderLEDdata=0b00100100;
break;
case 0b00100100:
KnightRiderLEDdata=0b01000010;
break;
case 0b01000010:
KnightRiderLEDdata=0b10000001;
break;
case 0b10000001:
RunningInwards=True;
break;
}
46
/*
Function name: OutLED()
-----------------------
Operation: Write inverted content of "data" to Port A
*/
void OutLED(volatile uint8_t data)
{
DDRA=OutPort; //Set PortA as Output port
PORTA=data^ActveLowOutput; //Port A after inverting "data" (because active low output)
}
/*
Function name: DelaymsUsingTimer2()
-----------------------------------
Receives value delay in parameter Delayms
Operation: Wait for Delayms msec before exiting
*/
/*
Referring to the Theory and calculations in Section A.2(b) and (c)
of "Appendix A For SKEE3732 Laboratory 2 Sheet"
*/
void DelaymsUsingTimer2(volatile uint8_t Delayms)
{
47
//Main Program
int main(void)
{
volatile uint8_t SWData; //Storage to store Switch data
volatile uint8_t dummy; //Storage to store dummy data
//Refer Section A.1
//Initialised PD7, PD6, PD5 and PD4 as Input Port
DDRD = 0x00; //A 8 bit of Port D bits set as input
//and enable respective pull-up resistor
PORTD = 0xff; //Enable pull-up resistor on PIND
//Dummy use of DelaymsUsingTimer2() so that it be used to
//determine actual delay during debug
DelaymsUsingTimer2(250);
//Initialise all Environment variables
MSBlookahead=False;
LSBlookahead=False;
RunningLeft=True;
RunningInwards=True;
RunningLEDdata=0b00000001;
// FlashingLEDdata=0x0;
KnightRiderLEDdata=0b10000001;
// AlternateLEDdata=0b00001111;
DS1Count=0;
while(1)
{
SWData=PIND|MaskOffLowerNibbleHigh; /*SWdata = PIND|0b00001111
lower nibbles of PIND set to 0b1111*/
if (SWData!=0b11111111) //If Any Switch pressed
{
switch (SWData) //{
{
case SW7Pressed: // If SW7 Pressed
DoRunningLight(); // Do RunningLight sequence
break;
case SW5Pressed: // If SW5 Pressed
DoKnightRiderLight(); // Do Knight Rider Light sequence
break;
default:
dummy=DoNothing(dummy); //Dummy function to allow breakpoint be set here
break;
}
} //}
else //else
{ //{
OutDataDS1(DS1Count); //Display DS1Count at DS1 of Seven
DelaymsUsingTimer2(250); // Segment Panel
DS1Count++; // Increment DS1Count
if(DS1Count>9) // If (DS1Count>9)
DS1Count=0; // DS1Count=0
} //}
}
}
Figure A.8(d)
48
i. Select Tools" Tab and the click "Selected Programmer/Debugger" pull down menu
to select available simulator/hardware (which is JTAGICE mkII)
If the selection of JTAGICE mkII07000004699 is not available, make sure that the JTAGICE mkII
is connected via USB interface and is switched ON.
49
iii. To Confirm connection with JTAG ICE MkII, after selecting ToolsDevice Programming, then
select Tool=JTAGICE MK II and Interface=JTAG, click Apply and finally click Read.
50
To Check/Set Clock Speed, after select ToolsDevice Programming if tools selected is JTAG
mkII and Device selected is ATmega32A click "Apply" and then click "Read".
If connection is OK you will get a signature reading "0x1e9502" Select "Fuses" tab and scroll
down the "Fuse name" to view the SUT_CKSEL which should read
"INTRCOSC_1MHZ_6CK_64MS_DEFAULT which should be as follows.
Otherwise makes sure Fuse Register value is set at High = 0x91 and Low = 0xE1. Inform
the technician before making changes of the Fuses by clicking Program button.
51
52
This will build the solution (if any changes are made) and program the target device without
starting a debug session. Any breakpoints set will have no effect.
Start without Debugging uses the tool and interface settings specified in the project options. This
is different from what takes place when using the stand-alone Programming Dialog, which is not
related to the project at all.
i. If the following message is displayed, then remove USB cable to JTAGICE mkii and reconnect. Then
repeat Procedure A.9(c).
ii. Otherwise ATmega32A programming completed and can be confirmed by noting that the Digit 1
on the Gotronik Atmega32A Target Board flash a - character (the system is alive) every a
250ms interval and when any of the switch specified in Table A.8(b) is pressed, the corresponding
specified Function at LED panels is displayed at the LED panel .
Without setting any breakpoint, run the program by selecting F5 or the the button. Note the
display on the Seven Segment LED and confirm the result is as in Table A.9(f)
53
SW7 pressed
SW6 pressed
SW5 pressed
SW4 pressed
SW3 pressed
SW2 pressed
SW1 pressed
SW0 pressed
You can open any of the above mention debugging windows, when and only if you are in
Debugging mode by selecting Debug|Window |<select the window yo want to open> as
shown below.
If you want to continue debugging by running through the program until another breakpoint is
Breakpoints are a method of halting execution flow. By adding a breakpoint in the assembly code we
can run the program at full speed, and it will be stopped at the line with the breakpoint. We will add a
breakpoint at the by placing the cursor on the instruction in the source view window and press <F9> (or
the "Toggle Breakpoint" in the "Debug" menu ). A red circle will appear in the left margin of the source
view window as shown. By pressing <F5> or "Run" from the "Debug" menu the program will start
running and break (stop) at the instruction with the breakpoint.
There may be situations where we have single stepped through far in the program but you may need to
reverse a few steps back to review a result. We will need to Stop Debugging and start over sequential
single stepping again until we reach the instruction we want to review which can be unnecessarily
tedious.
The best method to overcome this is to set a breakpoint at the instruction we want to stop, RUN the
program so that it will stop at the breakpoint it meets. Then you may single step through from there.
To set a breakpoint, click on any row to put the cursor on the line of the statement where you want to
break (stop) and then press F9 (or click the leftmost column on the row of the statement as shown in
Figure A.10). If the program runs by the breakpoint, it will stop at the statement.
Notes:
55
In Figure A.10 and referring to Table 3 of SKEE3732 uP Laboratory 2 Sheet 20162017, break points at
respective first statement of codes for the processes (which were prefilled) of Initialise peripherals
controls, Shell Loop, Get Command and Execute Command(If SW7 Pressed).
56
Figure A.10
57
i. For the breakpoint settings in Figure A.10, when start debugging command is selected, the
program will first stop at breakpoint set at statement DDRA = 0xFF as shown in Figure
A.10(a)(i).
Figure A.10(a)(i)
58
With the Gotronik Atmega32A Board is JTAG connected to the AVR JTAGICE mkII, and the AVR
JTAGICE mkII is USB connected to the PC running the Lab2Exp1 project, on the Atmel Studio IDE,
press F9 or click the Start Debugging button. The program should break at the first breakpoint
that it passes which is DDRD= 0x00 as shown in Figure A.10(b). Note that on the left window the
IO view has been selected and the I/O PORTD is selected so the all of the Port registers is displayed
at the bottom left window. The respective memory address and contents are given under the
Address and Value column. The Binary digit of Value is iconised as boxes under the Bits
column. You will need to expand the Bits column (after expanding the IO view window) so that
you will see complete 8 bit icons as shown in the Figure A.10(a)(ii).
Figure A.10(a)(ii)
59
Figure A.10(a)(iiii)
Select Debug|Continue and the program will stop the next break point it passes which is at
at statement of SWData=PIND|MaskofLowerNibbleHigh.
60
If you inspect IO View, PIND = 0xFF and if you open the Watch 1 window and adding the
SWData under the Name column, you will find that content of SWData (under the
Value column) = 0x00 (0b00000000) as shown in Figure A.10(a)(iv).
Figure A.10(a)(iv)
61
While holding the SW7 button, select Debug|Continue which will executes statement of
SWData=PIND|MaskoffLowerNibbleHigh.
The program will stop the next break point it passes which is at at statement of switch (SWData)
as shown in Figure A.10(a)(v).
Checking the IO View and Watch, we will find that PIND=0x7f (0b01111111) and SWData also
equals 0x7f. This is because when statement SWData=PIND|MaskofLowerNibbleHigh, PIND
which is connected to the active low Push Button switches is read (refer Table A.8(a) and Figure
A.6(l)).
Since SW7 which is pressed (at logic 0) that PIND bit 7 get 0 which give the value PIND=0x7F.
Lower nibble of PIND is being mask off to force bit 3 thru 0 of SWData to be set to all 1s. no
matter what the respective value are in PIND.
By masking OFF the lower nibbles, any input from the mask bits is omitted,
vi. Statement switch (SWData) if executed will compare with its cases parameter declared under
the switch structure. These value the parameter for each case is different from each other are
equal to the data from the Push Button switch panel connected to PIND (declared under Push
Button switches Panel Data comment at top of program LAB2Exp1.c).
The statement under case whose parameter equals the SWData of the statement switch
(SWData) will be executed.
Then if you select Debug|Continue program will breaks at statement DoRunningLight() which is
under the case SW7Pressed of which value SW7Pressed equals data in SWData
DoRunningLight()
DoFlashingLight()
DoKnightRiderLight()
DoAlternateLight()
62
iv. The program will be in Running mode and you will notice that a - is flashing at the DS1 Seven
Segment.
v. If you press any of the buttons of SW7, SW6, SW5 and SW4 you will notice that the program
breaks. Record the value of SWdata for each different Swicth pressed.
63
Figure A.10(f)(ii)
64
ii. Then set a breakpoint at the statement DelaymsUsingTimer2() and a second one after the
statement DelaymsUsingTimer2().
Figure A.10(b)(i)
65
ii. Restart the program, and when it breaks at statement the DelaymsUsingTimer2 note the Stop
Watch Counter on the Processor window.
iii. Step over the statement DelaymsUsingTimer2 and after a short while, the program will stop at
the next statement. Note the Stop Watch Counter on the Processor window.
iv. The difference between the two Stop Watch Counter will give the duration to execute the
statement in this case DelaymsUsingTimer2.
Figure A.10(c)(i)
66
67