Assembly Robot Prelab 5 – GPIO Registers and Interrupts
Table of Contents
Introduction
The focus of this prelab is to go over how to work with the GPIO registers and to test your understanding of interrupts. We have gone over some of these details in previous labs but not to this level.
Review on GPIO Registers
This section will summarize the key details from the GPIO lecture (Lecture #8). There is usually a lot of initial confusion over the naming of the different parts of the GPIO registers, which will be addressed here.
GPIO stands for General Purpose Input / Output and that is not the same as your General Purpose registers (GP registers which are R0 to R31). The key difference between the two is that the GP registers are within the CPU while the GPIO registers are outside of it. The GP registers are meant for holding data for arithmetic and logical operations while the GPIO registers can have a wide range of purposes such as holding the data from sensor readings or the data that needs to be outputted to generate a specific signal. Given the possibilities, it is important to understand how to configure the GPIO registers for the specific application needed.
As you may remember from our discussion of the basic memory models for computers, the input and output are treated as separate components. In order to make things more efficient and compact, they have been combined for nearly all modern microcontrollers. It is similar to comparing it to a 2-way street. Data can either flow into the microcontroller or out of it depending on what is needed. With this increased complexity, understanding the different terminology used to refer to each part is important.
There are several names that are used interchangeably when referring to the GPIO such as port, pin, and register. We will attempt to distinguish these from each other as best as possible.
First, we have to separate the hardware related terms from the software related terms. Each physical connection that can act as a general purpose input/output is referred to as a pin. They can be referred to individually by number or grouped together based on what they can be used for. These groupings are given unique names to differentiate them. This is where you have the Port B pins, Port C pins, Port D pins, and Port F pins. Each of the ports have a maximum of 8 pins each with some exceptions where certain pins are not available. The pins are usually referred to by their location within each port, which is why we have names such as PF5 and PF6 which stand for Port F pin 5 and Port F pin 6 respectively.
Now, we have the confusing part with the software terms. Because each pin can act as either an input or output, the direction and value has to be defined within the code. That is why there are three GPIO registers associated with each Port. These registers are the Data Direction Register (DDR), PORT register, and PIN register. While they share the same name, the PORT and PIN registers do not have the same meaning as the hardware related terms. Each of these GPIO registers serves a specific purpose.
- The DDR register is used to configure the direction of the pins for that Port. For example, if all of the pins for Port D need to be inputs, the value of the DDRD register would be 0b00000000. Each pin can be configured individually, so you can have combinations such as 0b10010010. A value of 1 indicates it will function as an output while a value of 0 is for inputs.
- The PIN register is used to indicate the current value on the pin. This is important for inputs as value here represents the input value that the microcontroller will see. You will typically only read(load) data from this register as writing to it (set or clear) will override any inputs.
- The PORT register serves different purposes depending on whether the pin is being used as an input or an output. It would be best to remember this register as the configuration for the GPIO pin. If the pin is to be used as an input, the value in this register determines if the input will have a pull-up resistor or not. A zero means no pull-up and a one means there is one. On the other hand, if the pin is to be used as an output, the value in this register determines the output value that is seen. If the output needs to be low, then a zero needs to be here. If the output needs to be high, then a one needs to be here.
Now that you know what each register is used for, you can put together that information to determine the appropriate value needed to configure the GPIO pins for the desired application. Make sure to utilize the CBR and SBR instructions as needed. In some cases, it may be better to use LDI.
Review on Interrupts
We will briefly go over how the interrupt handling process works. You can find additional details and examples in Lectures 10, 11, and 12.
Interrupts are used in order to allow the microcontroller to temporarily stop running the main program and handle any urgent situations that come up. It can then execute code that is specifically for that scenario before resuming normal operation. There are three key details that have to be present in order for this to occur.
1) Interrupts have to be globally enabled by setting the I bit to 1. They are disabled by default and all interrupt flags will be ignored.
2) The desired interrupt needs to be configured and enabled. Each subsystem has its own interrupt and configuration scheme. It is all off by default and will need to be set up by the programmer.
3) The interrupt flag will need to be set in order to initiate the process. The user does not need to handle this as each interrupt will trigger in a specific situation. For example, the timer overflow interrupt will set the timer overflow flag (TOV1) when the timer resets from its maximum value to 0.
If all of these conditions are met, it will go through the following process. (NOTE: This is a condensed version. It can be expanded on)
- Current instruction will be completed
- Jump to location in Interrupt Vector Table based on which interrupt flag was triggered.
- Execute Interrupt Service Routine while global interrupts are disabled
- Return from Interrupt Service Routine and re-enable global interrupts.
- Return to main program
One key detail that many people forget about when implementing the interrupts is that the Interrupt Vector Table (IVT) and Interrupt Service Routine (ISR) are not automatically defined. It is the programmer’s responsibility to add in what is needed.
As mentioned in lecture, the IVT is located at the very beginning of flash program memory. Each interrupt is allocated two flash program words, which is not enough in most cases. The reason for this is that you are expected have a jump instruction here that will go to the ISR for the interrupt.
Once you have taken care of all of those details, the final thing to do is enable interrupts globally. This is controlled by the value of the I-bit in the Status Register. We have to use the sei instruction to do set the bit and enable the interrupts.
Questions
1) What binary values are required to configure the pins for the following application? PB5 and PB3 are to be used as inputs that need pull up resistors. PB6 and PB0 are to be used as outputs with an initial value of 1. All other bits are assumed to be 0.
DDR_ = _________
PORT_ = _________
2) Which interrupt out of the following list has the highest priority?
INT4, RESET, PCINT0, TIMER3 OVF
3) What type of instruction should be placed within the interrupt vector table?
4) Assume that the pin change interrupt request 0 and Timer 1 overflow are both configured and enabled. The program is currently handling an interrupt for Timer 1 overflow and is within the interrupt service routine for it. The flag for the pin change interrupt is set during this period. What happens next?
5) What is the instruction to globally enable interrupts?
Prelab 5 Deliverable(s)
Page 1 – Title Page with Name, lab title, and photo
Page 2 – Answers to questions