Interrupt Lab - Cororado Edu

ECEN3000/3360 - Digital Design Lab - ecee.colorado.edu  

http://ecee.colorado.edu/ecen3000/labs/lab3/lab3.html

Lab 3: Arm Cortex M0 Initialization, Peripherals, and Interrupts

3 lab periods

Figure 1.  The LPCXpresso LPC1114 board.

...

Pre-lab

For the pre-lab, read the LPC1114 User Manual and Cortex-M0 materials, understand the structure of CMSIS Library and how NVIC works, then write the following 'GPIOInit()' function according to the Code Requirements. You may use physical address or CMSIS defined Register/GPIO symbol referring to a specific register or GPIO.

/* GPIO and GPIO Interrupt Initialization */

void GPIOInit() {

    /* Your code here */

}

Part 1 (Creating a New Project)

First, some background information.  You will be asked by the project creation wizard if you would like to include two modules in your code, the CMSIS and CRP.  For this lab, we will only be using the CMSIS library, as it provides many useful definitions to access the Nested Vector Interrupt Controller (NVIC).  Download the CMSIS archive here, and import it into your workspace following the same procedures you used to import the example projects in Lab 1.  You may read more about CMSIS and CRP in more detail below.  

Begin the new project creation process by File->New->Project, and then select the 'LPCXpresso C Project' wizard option under 'C/C++'.  

In the next window, select the first option, 'NXP LPC1100 C Project' and click next.  

Name your project how you wish, and keep your current workspace checked as the location to store your new project.  

Highlight our target board, the 'LPC1114/302' and click next. 

In the next screen, check the box to use CMSIS initialization code, but do not check the box for CRP features.  (if you properly installed the CMSIS library, it will create your project, if you did not properly install the CMSIS library, it will not find it and fail at this point).

Figure 2. CMSIS - Cortex Microcontroller Software Interface Standard.

... 

What is CMSIS? - Cortex Microcontroller Software Interface Standard

From ARM's website:  "The ARM Cortex™ Microcontroller Software Interface Standard (CMSIS) is a vendor-independent hardware abstraction layer for the Cortex-M processor series.  The CMSIS enables consistent and simple software interfaces to the processor and the peripherals, simplifying software re-use, reducing the learning curve for new microcontroller developers and reducing the time to market for new devices.  Creation of software is acknowledged as a major cost factor by the embedded industry.  By standardizing the software interfaces across all Cortex-M silicon vendor products, this cost is significantly reduced, especially when creating new projects or migrating existing software to a new device."

In other words, CMSIS provides an abstraction layer for us, mapping important control register addresses and masking (writing the proper control bits into these registers) to easy to use data structures.  Without CMSIS, our task of properly initializing, configuring, and even using most Arm M0 peripherals and features be extremely tedious.

What is CRP? - Code Read Protect

CRP is not important for us academics, but let's learn about it anyway! When releasing an embedded system as a product, the protection of the intellectual property held within it (i.e. the embedded software) is of paramount importance.  CRP is intended to provide this protection by preventing people from reading back any data contained in the code space of the Cortex M0.  Since our purposes are academic, we will not need to use this feature.  See this document from NXP to read more about CRP and the different levels of security offered by it.

Part 2 (An Interrupt-driven Digital Oscilloscope)

This lab revisits the digital oscilloscope portion of Lab 1. In that lab, we accomplished the task of driving our LED with a voltage supplied from the Function Generator on your lab bench through leveraging the ADC peripheral.  We polled the ADC on a fixed interval, read its value, and made a decision on how to light the LED based on that value.  

In this lab we wish to accomplish a similar effect, but in doing so we will follow a completely different approach (a purely interrupt-driven approach).  Using interrupts is incredibly powerful, as doing so allows the Arm to spend almost all of its time asleep.  

Being asleep is great and yields us a potentially massive power savings.  Because power usage is a critical design factor for embedded systems, we really need such savings and should structure our code with power at the forefront of our mind.  

The high-level goal of this lab assignment is this:  Light the LED when a square wave from 0V to 3.3V (logic low to logic high--rising edge) provided by the Function Generator. Change between a 25% and 75% LED duty cycle at a 30 second intervals, toggled by an internal process (a timer interrupt).  Have the GPIO port 2 pin 1 also configured as an interrupt, sensing for the period of the function generator waveform (see the Reference Materials section at the bottom for resources for where to find this information).  When the GPIO interrupt trigger condition occurs, the interrupt handler for the GPIO will execute, whereby we can set the frequency of the LED to match the frequency of the function generator square wave.  The Timer will interrupt each 30 seconds, and each time will toggle the duty cycle between 25% and 75%.   When the timer interrupts, toggle the duty cycle of the LED.  The result should be that the LED toggles at half the frequency of the square wave provided by the function generator, and either at a 25% or 75% duty cycle--all using only interrupts to do the work!

Hint: Since we will be sleeping almost all of the time, we need to periodically wake up to check this global variable to see if it has changed.  If so, we need to toggle the LED before going back to sleep.  Therefore, we need to set up a Timer Interrupt to wake us up with a fast enough period to catch frequencies of operation from the Function Generator of up to 100Hz.  Therefore, we suggest that your timer interrupts at a rate of every 1ms.

The Nested Vector Interrupt Control (NVIC)

Function definition Description

void NVIC_SystemReset (void) Resets the whole system including peripherals.

void NVIC_EnableIRQ(IRQn_Type IRQn) Enables the interrupt IRQn.

void NVIC_DisableIRQ (IRQn_Type IRQn) Disables the interrupt IRQn.

void NVIC_SetPriority (IRQn_Type IRQn, int32_t priority) Sets the priority for the interrupt IRQn.

uint32_t NVIC_GetPriority (IRQn_Type IRQn) Returns the priority for the specified interrupt.

void NVIC_SetPendingIRQ (IRQn_Type IRQn) Sets the interrupt IRQn pending.

IRQn_Type NVIC_GetPendingIRQ (IRQn_Type IRQn) Returns the pending status of the interrupt IRQn.

void NVIC_ClearPendingIRQ (IRQn_Type IRQn) Clears the pending status of the interrupt IRQn, if it is not already running or active.

Table 1.  Nested Vector Interrupt Control (NVIC) CMSIS functions.  Find their definitions in the file 'core_cm0.h' in your CMSIS directory.

This lab teaches us about the NVIC, its initialization, and its operation.  In Table 1, we see all of the NVIC related functions that are provided to you by the CMSIS library.  We will only need to leverage NVIC_EnableIRQ() to enable the correct interrupts and NVIC_SetPriority() to set the priority, but it is nice to see the others that are available to us.  It will be up to you to read the proper NXP example project's code to figure out how to leverage these to perform the task that we want using interrupts in both the Timer32 and GPIO peripherals.  Additionally, we will need to write our own initialization code for both the GPIO and Timer32 peripherals.

Producer/Consumer Model

Code Requirements

You must not copy any of the files from the example projects to your new project. i.e., you must write your own code! However, but you may find the example projects' code very helpful in doing so. Essentially, the example project's driver files, e.g. gpio.c, provide very general purpose functions to do the same things you want to do. Your task is to figure out which lines they use are the minimal (or close to it) to do the job you'd like to do, and then execute those lines directly in your code. These should be at the CMSIS level, which bitwise modify a memory-mapped IO control register (e.g. LPC_GPIO0-DIR) directly.

Use GPIO port 2, pin 1 for the interrupt input.

Use GPIO port 0, pin 7 for the LED control (you have to--they are hardwired!).

Use global variables for the Timer and GPIO interrupts to communicate.

The GPIO interrupt and Timer32 interrupt must be set to the same priority.

You will need 2 interrupt handlers to service each interrupt that comes in:  

TIMER32_0_IRQHandler() and PIOINT2_IRQHandler().

Your main function must look exactly like the below main function code.

Look carefully at this code--does it look weird to you?  After initialization, you will spend all your time in a while loop that repeatedly puts the Arm to sleep!  How does this code framework actually get anything done?

Resolve signals up to 100Hz from the Function Generator with the LED light--your timer will need to interrupt at a duration of at least 1/(2*100) seconds.  If you do this correctly, your end demonstration to the TAs or instructor will be that your LED blink rate is controllable at the Function Generator (similar to the Lab 1 result).

Optional:  Write both GPIOInit() and TIMERInit() in assembly.  Write them in C to make sure you get it working before attempting the assembly!

Code Template (Please copy and work from this exact template)

/* GPIO and GPIO Interrupt Initialization */
void GPIOInit() {

    /* Your code here */

}

/* TIMER32 and TIMER32 Interrupt Initialization */
void TIMERInit() {

    /* Your code here */

}

/* GPIO Interrupt Handler */
void PIOINT2_IRQHandler(void) {

    /* Your code here */

}

/* TIMER32 Interrupt Handler */
void TIMER32_0_IRQHandler(void) {

    /* Your code here */

}

int main(void) {

    /* Initialization code */
    GPIOInit();   // Initialize GPIO ports for both Interrupts and LED control
    TIMERInit();  // Initialize Timer and Generate a 1ms interrupt

    /* Infinite looping */
    while(1);       
    return 0;
}

***Reference Materials (Being able to read and interpret these documents (along with what's done in the example projects' code) is very, very important for this lab!)
.END

No comments:

Post a Comment