Starting from:

$30

Lab 8: LCD Display and Interrupts

CSC 230 
1
Lab 8: LCD Display and Interrupts
Submit the modified timer_interrupt.asm from your project at the end of your lab.
I. Interrupts
An interrupt is a signal that can interrupt and alter the flow of current program execution. It is a
method that allows programs to respond immediately to external events. Interrupts can be triggered
by external or internal signals; they can be caused by software or hardware. When a program is
interrupted, the routine that is executed in response to the interrupt is called Interrupt Service
Routine (ISR), or interrupt handler. The process of interrupting (and pausing) the current
program, executing the interrupt handler, and returning back to the interrupted program is called
servicing the interrupt. Interrupts can be dedicated or shared. If interrupts are shared among
multiple devices (I/O), then the ISR further has to check which device or signal caused that
interrupt. Usually the processor has an interrupt vector (a set of bits), where each position in the
vector corresponds to a specific interrupt that can occur. When interrupts are enabled, during each
cycle and right before fetching the next instruction, the processor checks this vector to see if an
interrupt occurred. It starts the check at the first position in the vector and finishes at the last
position. This gives interrupts a natural ordering which determines their priority: first one in the
vector will be the first one serviced. When an interrupt occurs, the processor jumps to a pre-defined
location in the interrupt table assigned to that interrupt. Hence the interrupt table has to be setup to
jump and branch to the appropriate ISR to handle each type of interrupt. For completeness, the
ATMega2560 processor interrupt table from page 101 of the datasheet is included on the next two
pages.
II. Timer 1 in the AVR ATmega2560
In this course, we will use the AVR timers to periodically interrupt our program so that we can
perform certain actions based on time. There are 6 built-in timers (two 8-bit and four 16-bit
timer/counters) in the AVR processor. They can be setup in different modes. For the purposes of
this lab, we will only use the interrupts related to the overflow operation modes. Furthermore, in
this lab, we will only use the 16-bit TIMER1. Before using it, we need to configure the timer using
the relevant special purpose registers, listed below and explained further on:
TCCR1A = Timer/Counter 1 Control Register A
TCCR1B = Timer/Counter 1 Control Register B
TCCR1C = Timer/Counter 1 Control Register C
TIFR1 = Timer/Counter 1 Interrupt Flag Register
TIMSK1 = Timer/Counter 1 Interrupt Mask Register
TCNT1H = High byte of the Timer/Counter 1
TCNT1L = Low byte of the Timer/Counter 1
SREG = Status Register
CSC 230 Fall 2019
2
CSC 230 Fall 2019
3
CSC 230 Fall 2019
4
III. Timer-driven interrupt example with timer counter overflow.
We are going to use Time/Counter 1. This timer will be set to normal overflow mode. That is: the
16-bit timer/counter is initialized to a value (say 0) and a hardware clock signal is used to increment
the timer/counter. When the timer/counter overflows, it generates interrupt 21 (see the above table)
causing the processor to jump to a pre-defined location 0x0028 in program memory. Let’s learn the
structure of interrupt in AVR assembly language (see timer_interrupt.asm):






Initialization of the Interrupt
Vector Table (IVT)
Stack Pointer must be
initialized, since we’re using
functions.
Set up operation mode (norm.)
in Control Register A
Set up the pre-scaler value
in Control Register B
Initialize starting value for the
counter. It will overflow when
the counter reaches 0xFFFF.
Hence, this is a 16-bit integer
stored in two 8-bit registers,
which are latched together –
both must be written together.
Thus, the high byte must be
written first, because the value
of both will update only when
low byte is written.
CSC 230 Fall 2019
5






Below are the explanations of how each of the Timer 1 registers are used in this example.
Allow Timer 1 to interrupt the
CPU via the interrupt vector.
in Timer Mask Register
Enable interrupts by setting the
global interrupt flag in SREG
Inside the ISR, remember to
protect SREG along with the
others that this function affects.
This instruction sets the PC to
the top three bytes on stack,
adjusts the stack pointer, and
only then enables interrupts.
This order is important and
cannot be achieved otherwise.
Inside the ISR, reset the Timer
Counter register, since it’s
currently at 0 (after overflow).
CSC 230 Fall 2019
6
TCCR1A - Timer 1 Control Register A
TCCR1A is set to 0, which means in normal mode, disconnect Pin OC1 from Timer/Counter 1. The
other modes are: “COM1A1:COM1A0”: Compare Output Mode for Channel A,
“WGM11:WGM10”: Waveform Generation Mode. Only when one of the OC1A/B/C is connected
to the pin, the function of the COM1x1:0 bits is dependent of the WGM13:0 bits setting. It doesn’t
apply to this example and the values are set to all 0’s.
TCCR1B - Timer 1 Control Register B
The least significant three bits of TCCR1B are used to slow down the interval in our example.
Instead of counting 1 per clock cycle, we count 1 every 1024 clock cycles in this example.
TCCR1C - Timer 1 Control Register C
This register is not explicitly initialized in this example.
CSC 230 Fall 2019
7
TIMSK1 – Timer 1 Interrupt Mask Register
This register is set to 0x01. “Bit 0 – TOIEn: Timer/Counter Overflow Interrupt Enable
When this bit is written to one, and the I-flag in the Status Register is set (interrupts globally
enabled), the Timer/Counter Overflow interrupt is enabled. The corresponding Interrupt Vector is
executed by program control jumping to address 0x0028 and further jumping to timer1_isr.
TIFR1 – Timer 1 Interrupt Flag Register
This register is not explicitly initialized in this example.
TCNT1 – Timer 1 Counter Register
The TCNT1 is a 16-bit register and can be accessed by the AVR CPU via the 8-bit data bus. The
16-bit register must be byte accessed using two read or write operations. Each 16-bit timer has a
single 8-bit register for temporary storing of the high byte of the 16-bit access. Accessing the low
byte triggers the 16-bit read or write operation. When the low byte of a 16-bit register is written by
the CPU, the high byte stored in the Temporary Register, and the low byte written are both copied
into the 16-bit register in the same clock cycle. When the low byte of a 16-bit register is read by the
CPU, the high byte of the 16-bit register is copied into the Temporary Register in the same clock
cycle as the low byte is read.
IV. Exercise 1.
Download timer_interrupt.asm and complete the ISR implementation to toggle the two bits on
PORTB that drive the LEDs to make the two LEDs blink. This can be achieved in a few lines of
code using a masking operation. First retrieve the current PORTB values, then apply the mask and
store the result back to PORTB. When your code is implemented correctly, you will see the LEDs
on PORTB behave similarly to those on PORTL.
Note that the LEDs on both ports start in sync with each other but over time the LEDs on one of the
ports start to lag behind those on the other port. Questions to consider: can you make them
synchronized? which way is harder to achieve perfect timing – adjusting the delay loop or adjusting
the timer configuration? what affects the timing of the delay loop? what affects the timing of the
interrupts? Note that changing the number of instructions in the ISR affects the delay loop,
because the ISR can interrupt the delay loop and thus cause a delay in its execution!!
CSC 230 Fall 2019
8
V. Exercise 2.
Modify the program from Exercise 1 to display “hello, world!” on the LCD, using the LCD library
provided during the last lab. Then, using the timer-driven interrupts, make the exclamation mark on
the LCD display blink at a certain rate.
Play around with the timing of the blinking and make it blink once per second, twice per second, 10
Hz, 100 Hz, 1000 Hz. What happens when the blinking is too frequent? You may use the online
AVR timer calculator (https://eleccelerator.com/avr-timer-calculator/) to figure out the appropriate
settings for TIMER1 starting value and pre-scaler.
Submit the modified timer_interrupt.asm from your project at the end of your lab.