EECS 373 Lab 4: Interrupts

Copyright © 2010-2011, Thomas Schmid, Ye-Sheng Kuo, Matt Smith, Lohit Yerva, and Prabal Dutta

Revised 2/11/14

Schedule

See posted lab schedule for due dates, in-lab, and post-lab due dates.

Objectives

The purpose of this lab is to understand the concept of interrupts in embedded systems.

  1. Understand how interrupts get generated.
  2. Understand how interrupts get handled in the Actel's Microcontroller Subsystem (MSS).
  3. Understand latencies in processing interrupts in the MSS.
  4. Understand the ARM Nested Vectored Interrupt Controller (NVIC) and how it can assign priorities.

Background

Interrupts are similar to branch instructions in regular code - they are both used to redirect code flow in a program. Branches occur naturally and somewhat predictably in code. Interrupts are usually generated by external physical sources and are generally unpredicable. The most basic interrupt model would involve taking the 370 LC2K pipeline and adding a button input that, when pressed, makes the processor jump to "Button_Pressed" Interrupt Request (IRQ) Handler (basically a fancy function), which increments button pressed count. A basic Interrupt Controller (IC) would accept the button input, store the current PC register address in a "Link Register", and load the PC register with address of the "Button_Pressed" function (always in the same location) in the next cycle. When the interrupt is done, the IC can load the previous PC register address from the "Link Register" back into the PC register so that the main program can continue executing as before the interrupt. This is the fundemental idea behind the interrupts - random external events can be used to signal the processor to stop what it's doing, and do something else that is more important right now.

Additional Material

Overview

Interrupts are a fundamental mechanism in embedded systems to let the microcontroller know about events. On the Cortex-M3 core, the Nested Vectored Interrupt Controller (NVIC) allows the core to configure interrupt handling. Interrupts are also a key to low-power designs, where the core is in deep-sleep mode and only wakes up if it needs to handle an interrupt.

Pre-Lab Assignment

  1. Read through the Additional Material above.
  2. Read through the following entire lab 4.
  3. Review the lecture material on interrupts.

In-Lab Assignment

  1. Extend your GPIO FPGA Code with Interrupts
  2. Process interrupts on the MSS
  3. Measure Software Interrupt Latency
  4. Managing Multiple Interrupts
  5. NVIC and Interrupt Priorities

In-Lab Assignment

There are several questions that are raised during the In-Lab that need to be submitted for the post lab. Consider printing the answer sheet posted at end of this lab and fill it in as you work.

Extending the Lab 3 PWM Hardware with Interrupts

In Lab 3, you learned how to write a peripheral in the FPGA fabric and how to interface it over the APB3 bus to the microcontroller. So far, the only access you had to your peripheral was through memory mapped I/O, i.e., you either read or wrote a register to get data from or to the peripheral, respectively. For example, you had to specifically poll the switch status in order to see if it is pushed or released. This is not ideal as the core has to busy-loop and poll the I/O line continuously, wasting precious resources. It would be better if the microcontroller could go about its business but still get interrupted when a button is pushed.

In the following sections we will modify the lab 3 PWM hardware to generate interrupts from push button switches.

Adding the FABINT

There are several ways the FPGA can interrupt the Cortex-M3. The most common way will be through the Fabric Interrupt (FABINT). This is a dedicated interrupt line just for the fabric. Another way we will explore later is through the I/O lines of the Cortex-M3. We will use this method later, when we want more than one interrupt. The main difference between FABINT and interrupting through I/O lines is that FABINT is a level triggered interrupt, while the I/O lines can be configured for positive, negative, or both edge triggering.

We will have to add an interrupt and UART to the MSS instance in the PWM hardware of lab 3. Open up the MSS instance in your Lab 3 PWM project. First of all, add a checkmark to the UART_0 component. This doesn't have anything to do with interrupts. But we will use the UART_0 to plot some interesting information.

Now, go back to the canvas view and click on the Interrupt Management block on the lower left-hand corner of the configurator. Make a checkmark next to the FABINT line. This will enable the fabric interrupt and allow you to connect to it in the smart designer.

Update the MSS component and generate. 

Adding the Interrupt Generation Hardware

We are going to generate a fabric interrupt signal FABINT from either pushbutton switch 0 or 1. The FABINT signal must be a pulse so we will have to add some more Verilog to generate the pulse. The following Verilog uses debounced switch inputs (dbsw) to generate the pulses. Add this code to your lab 3 PWM module. 

//shift debounced switch inputs//
reg sw0_pulse[2:0], sw1_pulse[2:0];
always@( posedge PCLK)
begin
sw0_pulse[0] <= ~dbsw[0];
sw0_pulse[1] <= sw0_pulse[0];
sw0_pulse[2] <= sw0_pulse[1];

sw1_pulse[0] <= ~dbsw[1];
sw1_pulse[1] <= sw1_pulse[0];
sw1_pulse[2] <= sw1_pulse[1];
end

//use shifted switch inputs to create a one clock cycle pulse//
wire sw0_int, sw1_int;
assign sw0_int = (sw0_pulse[1] == 1'b1) & (sw0_pulse[2] == 1'b0);
assign sw1_int = (sw1_pulse[1] == 1'b1) & (sw1_pulse[2] == 1'b0);

//create a FABINT pulse if either sw0 or sw1 is pressed//
always@(posedge PCLK)
    if(~PRESERN)
        FABINT <= 1'b0;
    else if(sw0_int)
        FABINT <= 1;
    else if (sw1_int)
        FABINT <= 1;
    else
        FABINT <= 0;

Update the instance and make all the connections. When you are finished, the high level design should look somewhat like the following. The pwm component  in this example contains all the code include the switch debounce Verilog. Notice that we have ported FABINT to a FPGA IO pin. This can be useful to verify that you are getting a pulsed signal by observing it with the oscope.

 

Adding Software Interrupt Service Routines(ISR) to the code for FABINT

We are going to modify the previous assembly project to work with interrupts. We will provide a comprehensive interrupt table and stack initialization by including startupWithInt.s and an associated linker script   link.ld.   You will have to remove any references you might have in your main to the interrupt section and stack. These references are all taken care of in the startupWithInt.s and link.ld files. Your main should look something like this.

          
.global main
.text
.syntax unified
.type main, %function
main:
@your code goes here
.end

To use the printf373 see the softconsole reference posted with this lab. 

The Softconsole project directory should look like this when you are done.

First, add a printf373 call to print your name right at the begining of main. This will indicate that your application has started.

To receive the characters on your computer, start HyperTerminal or PuTTY and set the session to 57600 baud, 8 bits data, 1 stop bit, no parity, and no flow control. Follow the Fun Terminal or Putty setup in the 373 SoftConsole Assembly Reference.

Go ahead and launch your Fun Terminal or PuTTY session now. Once we start our application, you will see some text appear on it.

NOTE: If you run both terminal programs at the same time and try to access the same COM port they will not work.

Next, we will have to declare our interrupt handling service routine. Fortunately, the Actel SoftConsole already provisioned the interrupt vector with some weak links. Open the file startupWithInt.s. The first global definition you see, intVectors is the so called interrupt vector. It defines the link between interrupt number, and function to jump to if this interrupt happened. In our case, all the function names are already defined. However, the functions themselves only exist as weak links, i.e., you can overwrite them with your own functions. Look at Chapter 1: ARM Cortex-M3 Microcontroller -> Interrupts of Actel SmartFusion Microcontroller Subsystem User's Guide from prelab.

Scroll down until you find the name of the FABINT Interrupt Service Routine (ISR). Then scroll even further down until you find the weak link for this function. Next, create a new file called interrupts.s and create a new function with the fabric interrupt  handler name you found in startupWithInt.s.

Add the following starter code to the file. 

@ string to pass to printf
waitStr: .asciz "Handling IRQ FABINT %d\n\r"

.align 2

@ Global variables (must be in the .data section)

.data

count: .word 0 @ The label count is the address to a 32 bit (word) memory locaiton initialized to zero
@ The value of count must be passed to printf as separate argument (register)

.text
.syntax unified
.thumb

@ make label Fabric_IRQHandler availble to references outside of this file
.global Fabric_IRQHandler

@ function declaration that corresponds to week declaration in startupWithInt.s
.type Fabric_IRQHandler, %function

Fabric_IRQHandler:

@ your code

Inside the Fabric Interrupt Service Routine (ISR), add a printf373 statement that outputs the number of times this function was called so far using the waitStr defined above. This will help you visualize the UART output and understand how often the interrupt fired. Remember that an ISR automatically saves r0-r3, r12,  lr, pc and PSR (program status register) . It does not save the other registers.  Keep this in mind when modifying the Fabric ISR code.

There are two more things we have to do before the NVIC will treat our interrupts correctly.

  1. We have to enable the FABINT interrupt in our main function
  2. We have to clear the pending interrupt inside our IRQ Handler.

If we don't perform point 1., then our interrupt service routine will never get handled. If we don't perform point 2., then the interrupt will fire again as soon as you left your interrupt service routine, because the interrupt routine code never told the hardware that it accepted the interrupt request.

Enable FABINT Interrupt 

Read Chapter 6: Nested Vectored Interrupt Controller of Cortex-M3 Technical Reference Manual. Notice the eight Interrupt Set-Enable registers address under 6.3: NVIC programmers model. Read Chapter 1: ARM Cortex-M3 Microcontroller -> Interrupts of Actel SmartFusion Microcontroller Subsystem User's Guide. Notice the Nested Interrupt Vector Controller (NVIC) table. It lists the IRQ number x (INTISR[x]) for all the interrupt names defined in the startupWithInt.s assembly file that you added to your project.

  • Interrupt Set-Enable register(s) is used to enable a specific interrupt.
  • Each consecutive bit of the eight 32bit Interrupt Set-Enable and Clear-Pending registers represents a different interrupt.
  • For example, interrupt vector number 0 through 31 are mapped to bit0 to bit31 of the base Set-Enable register at address at address 0xE000E100. Interrupt vector number 33 (INTISR[33]) which is GPIO_1_IRQ will be the second bit in the second register starting from the base register (address).
  • In the interrupts.s file that includes the Fabric ISR add the following functions. Write the following assembly function based on lecture notes and reference guides.

    Please write the void EnableIRQ(int irq_num) function using the following template:

    @ Enable the IRQ
    @ Inputs: IRQ number in r0
    @ Output:
    .global EnableIRQ
    .type EnableIRQ, %function
    EnableIRQ:
    push {r4,r5} @ Callee Save

    @ Load NVIC_ISER (Set-Enable Registers) start address

    @ Select register to modify

    @ Select bit to set in the register

    @ Set bit

    pop {r4,r5}
    bx lr

    Clearing Pending Interrupts 

    It is not necessary to clear a pending FABINT interrupt with software. The NVIC will automatically clear the interrupt when the interrupt is processed. You will find it is necessary to clear interrupts generated throught the GPIO we will use a bit later in the lab.

    Try It

    Enable the Fabric interrupt in the main function just before your while(1) loop.

    Make sure that you are not overwriting registers (arguments) when you call these functions in existing code.

    Connect the terminal to the serial port and program your board with your application. Then, start the debugger and launch your application. Observe the output of the terminal. You should first see your name. Then, press the switches. You should see a new line for every time you press the switches.

    Debugging Tips

    Start debugging your interrupt by putting a break point in the interrupt. If it does not occur, consider the following:

    1) The FABINT signal must be a positive pulse on the order of 100ns. The supplied Verilog will for the most  part provide the correct pulse width. If the pulse does not go high you will not generate an interrupt. If the pulse is logical high all time, you will continuously generate interrupts. Port the FABINT to one of the user pins and observe on the scope for a valid pulse.

    2) You can see if you have a pending interrupt by looking at Interrupt Pending-Set register in the NVIC base location 0xE000E200. For example, for FABINT (interrupt number 31) location 0xE000e000 = 0x80000000 if an interrupt was set pending. You can use the memory viewer in Softconsole to observe this location or in the console window you can use the debug command examine (x). For example, x 0XE000e200 will display the contents of this location.

    3) The interrupt has to be enabled or it will not go to your handler. You can observe this control register in the NVIC base location 0xE000E100. For example, FABINT enabled should be 0xE000E100 = 0x80000000.

    If your print function does not occur or causes a hard fault, consider the following:

    1) The print functions uses one of the MSS UARTs UART0. Make sure it is instantiated in the top level design.
    2) Make you are using the latest UART drivers from your MSS in Softconsole
    3) Make sure you initialize the UART by calling the initialization function before using the printf.

    Generally, if you get  a hard fault it is probably because you are writing or reading with an illegal memory address. Consider the following to debug and correct.

    1) Run the code step by step until you get the hard fault. This may take several tries. You may have to step through the dissassembled code in the window on the right  to pinpoint the assembly instruction causing the problem.

    2) Examine the registers of the problematic instruction, especially addresses. For example, if r4 = 0 in the following instruction str r3, [r4, #0], you will get a hard fault if you execute it. Examine the code that sets up the address in r4 and see what the potential problem is.

    3) If you attempt to access custom MMIO (for example IO created in lab 3) as a byte or half world,  you will generate a hard fault. For example, str r3, [r4, #0] with r4 = 0x40050001 will generate a hard fault. Examples of word aligned boundaries are 0x40050000, 0x40050004, 0x40050008, etc.

    4) If you attempt to access custom MMIO that is not instantiated in the hardware, you will generate a hard fault.

    5) Returning through the link register (bx lr) can generate a hard fault if the link register contains a inaccessible memory address.

    If your software does not seem to read your switches or write your LEDs, use the Softconsole debug commands to check the hardware independent of the software. In the SoftConsole console window use the examine command to read the switches. For example, if the switches are located at address 0x40050004 type

    x 0x40050004 and you should get
    0x40050004 = 0x00000003

    if th switches are not pressed.

    To write a register you use the set command. For example, if the LEDS are at 0x40050000 type

    set *0x40050000 = 3     to set the lower 2 LEDs to high.

    If neither of these work, you should inspect your Verilog APB3 interface and connections to these devices for possible problems.

    Generally, don't assume anything regarding register usage.  Unless you wrote the function, you cannot be sure what the register usage will be across the function call unless it is defined to be ABI compliant (we haven't learned about this yet). Play it safe and you save and restore register across a function call. For example, branching and linking to a function will overwrite the lr register. If you need this register to return from the function or interrupt you just branched and linked from, save the lr!

    Wait for Interrupt (Idle the processor)

    It is now time to change the application behavior. Until now, the LED's are still controlled by polling the sw_reg register. This introduces a significant latency. Change the application such that the reading of sw_reg is done inside the interrupt service routine. Then and replace the code inside the while1 loop with the following code and add the "waitStr" to your code:

    while1:
    movw r0, #:lower16:waitStr @ "Wait For Interrupt\n\r"
    movt r0, #:upper16:waitStr
    bl printf373
    wfi @Wait For Interrupt; Stop execution until an interrupt fires
    b while1

    The Wait for Interrupt instruction tells the processor to stop executing any more instructions until an interrupt fires. This idles the processor and saves power.

    Program your new application on your board and start it. Your output on the terminal should look similar to the picture below.

    The Wait For Interrupt instruction (wfi) will dissable the debugger connection to SoftConsole. You will not be able to use the debugger once it executes and you will NOT be able to debug your code. To start a new debugging session you may have to power cycle the kit by removing the USB connections.

     

    Concurrent Interrupts

    Replace your while loop in your main function with an empty busy loop:

    while1:
    b while1

    Until now, we only had to deal with one interrupt source. However, what happens if we have several potential interrupt sources? Especially in the case where they fire concurrently, which one will be treated first? Fortunately, the NVIC has many possibilities to configure its behavior. But first, we have to reconfigure our hardware.

    The FABINT is only one way to interrupt the Cortex-M3. Another interrupt source are the I/O lines. They can be configured to fire an interrupt on a specific edge (falling or rising), or on both edges. And since we can connect the I/O lines to the fabric, we can use them to provide multiple interrupt lines from the FPGA to the Cortex-M3.

    Open up the MSS Configurator and double-click on the GPIO module. Add one I/O line as input, and one line as output.

    Edit the pwm component and add 2 additional outputs. Assign the value of FABINT to both the outputs. This will assure that all three interrupts get generated at the same time.

    Go back to the canvas of your Smart Designer block diagram and update both components. Connect the second FABINT interrupt output to the GPIO input of the MSS, promote the third FABINT interrupt output to the top, and promote the GPIO output to the top. Your connections should look similar to the following:


    You can assign M2F_GPO_0 output to USER I/O pin 1 (J19) and connect FABINT_OUT2 to USER I/O pin 2 (J20) or any other convenient pin. 

    Handling Concurrent Interrupts on the Cortex-M3

    Once you programmed the FPGA, open up SoftConsole to edit your application. First, we add the GPIO drivers created by the MSS Configurator. Perform the same steps as you did for the UART drivers. However, this time select the mss_gpio directory instead of the mss_uart directory.

    Make sure that you are not overwriting registers (arguments) when you call these functions in existing code.

    In our main function, we have to initialize the GPIO driver. Add the following commands. All the MSS_GPIO_x functions are defined in drivers/mss_gpio/mss_gpio.h.

    bl	MSS_GPIO_init 	@no arguments
    Next, we have to configure the GPIO pins. Note that this is similar to what you performed in your gpio library written in Lab 2. First, we configure the output GPIO:
    @ Config GPIO 0 as output
    mov r0, #0 @ MSS_GPIO_0
    mov r1, #0x000000005 @ MSS_GPIO_OUTPUT_MODE
    bl MSS_GPIO_config
    Then, we configure the input as an interrupt. The interrupt should fire on a rising edge.
    @ Config GPIO 1 as input with interrupts
    mov r0, #1 @ MSS_GPIO_1
    mov r1, #0x000000042 @ MSS_GPIO_INPUT_MODE | MSS_GPIO_IRQ_EDGE_POSITIVE
    bl MSS_GPIO_config
    bl MSS_GPIO_enable_irq @ 1 argument, MSS_GPIO_1
    mov r0, #33 @ GPIO1_IRQHandler
    bl EnableIRQ
    Now all we have left is to add the interrupt service routine for GPIO1 input to interrupt.s:
    	.global	GPIO1_IRQHandler
    .type GPIO1_IRQHandler, %function

    GPIO1_IRQHandler:
    @ Pulse GPIO0 output
    push {lr}
    mov r0, #0 @ MSS_GPIO_0
    mov r1, #0x1 @ set GPIO0 output high
    bl MSS_GPIO_set_output

    @ Print interrupt string
    @ remember to initialize the UART in the main
    movw r0, #:lower16:GPIO1intStr @ "Interrupt occurred - GPIO 1 \n"
    movt r0, #:upper16:GPIO1intStr
    bl printf373

    @ Clear MSS GPIO pending
    @ While is not necessary to clear a pending interrupt in the NVIC,
    @ it is necessary to clear the respective MSS GPIO interrupting source.
    @ Be sure to do this or your interrupt will occur continuously.
    mov r0, #1
    bl MSS_GPIO_clear_irq

    mov r0, #0 @ MSS_GPIO_0
    mov r1, #0x0 @ set GPIO0 output low
    bl MSS_GPIO_set_output
    pop {lr}
    bx lr

    A series of questions follow that you will need turn in as part of your post lab. There is a anwer sheet linked here that you can use a starting point or create your own and provide answers or notes as you work. 

    Q1: Run the application you just wrote on your board and connect a serial terminal to the serial port. Push the switch several times. Which of the two interrupts fires first? Compare it to your answer that you just wrote down. Explain in two sentences why this interrupt gets handled before the other one.

    Measuring Interrupt Latency

    As you can imagine, it takes time from the interrupt generation in the fabric, until the Cortex-M3 actually processes the interrupt in software. This is usually called "Interrupt Latency". We will next investigate this latency to better understand its characteristics.

    Use the oscilloscope and connect a probe to each of the User I/O lines created above (M2F_GPO_0 and FABINT_OUT2). Configure the trigger to be sourced from the FABINT_OUT2 line, and use the "Normal" or "One Shot" mode. Then, press a switch and measure the time between fab interrupt and toggling of the I/O line from the MSS.

    The interrupt latency measurements you will make are significantly shorter than 1ms and the variations in interrupt latency much shorter than that. If there is any delay source between the interrupt event (the pushbutton switch) and the output marker in the interrupt handler (GPIO out), it will make it very difficulty to measure small changes in latency on the scope. The 373print routine can take 3 -4ms to execute!. Be sure to comment out any print call between the interrupt event and the GPIO output. 

    Q2: Perform 5 latency measurements. What is the mean time and standard deviation of your measurements?

    Your measurements should be fairly consistent, without too much jitter. This is because the Cortex-M3 is not very busy. Especially, in our current application, we do not have any blocks that disable interrupts.

    Sometimes, an embedded system has to execute a code sequence that should not be interrupted. For example, if we modify a global variable that could also be modified in an interrupt routine, then we should disable interrupts before we start modifying, and re-enable them once done. This avoids concurrency issues. To turn off all interrupts, except hard faults, on the Cortex-M3, you can use the following assembler command:

    cpsid i
    To re-enable them, use the following command
    cpsie i
    This code modifies the PRIMASK.

    Let's measure the interrupt latency again, but this time we add a non interruptable for loop into the main while loop. Modify the infinite while loop to

       
    while1:
    cpsid i
    mov r0, #0
    loop:
    cmp r0, #100
    add r0, r0, #1
    bne.n loop
    cpsie i
    b while1
    and measure the interrupt latency.

    Q3: Perform 5 latency measurements with PRIMASK. What is the mean time and standard deviation of your measurements? Explain the behavior in two sentences.

    Interrupt Priorities

    In class you learned about the Nested Vectored Interrupt Controller (NVIC) and how it can handle priorities and groups for different interrupt sources. We will now use the NVIC to change the interrupt priority such that the interrupt that came first, now becomes second.

    Write the following assembly function based on lecture notes and reference guides. Use the above EnableIRQ as an example.

    The NVIC allows you to change the preemption priority for all the interrupts it manages. Each interrupt has a 1 byte register associated with it to do this. For example, the FABINT interrupt  priority register (interrupt 31)  address is NVIC priority base address + 31. These registers are byte addressable so you can use  a stb instruction to write them. This processor only implements the top 5 bits 7-3 to encode priority. The lower the number, the higher the preemption priority.  The 5 bit field is divided into the preemption priority and sub priority fields by the group number set in the Application Interrupt and Reset Control register. The group number is set to zero by default (on power up reset). A priority group 0 specifies no sub priority for bits 7-3 of the register, so there is no sub priority field for a group 0 setting. We will not use sub priority for this exercise, so the default setting is fine. We will only manipulate preemption priority using the bits 7-3. For more detail, see  Chapter 6: Nested Vectored Interrupt Controller of Cortex-M3 Technical Reference Manual, the interrupt lecture notes and Chapter8: The Nested Interrupt Controller of The Definitive Guide To The ARM CORTEX-M3 

    void SetIRQPriority (int IRQn, char PriorityGroup)

    IRQn is the interrupt number and char is int8

    Modify your code using the above function such that the interrupt that was first, now becomes second. Execute your application and verify that it works using the terminal.

    Make sure that you are not overwriting registers (arguments) when you call these functions in existing code.

    Interrupt Preemption

    Changing the priority of an interrupt changes the order at which interrupts get handled. But what happens if an interrupt fires while another interrupt is still getting processed? Interrupt service routines are not protected from other interrupts if they have a higher priority (lower priority number). To illustrate this, let's add a delay between the two interrupts that so far fired at the same time, such that one of the interrupts comes later than the other.

    The following code is a delay loop using a counter to the ReadSW_WriteLED module. Add it to your Verilog module code and adapt it such that the second interrupt (named FABINT_OUT in my code) fires later than the FABINT interrupt.

    reg [31:0]  counter; 
    reg counter_en;
    reg fabintOut;

    assign FABINT_OUT = fabintOut;

    always@(posedge clk)
    begin
    if(~nreset)
    begin
    counter <= 32'h00000000;
    fabintOut <= 1'b0;
    counter_en <= 1'b0;
    end
    else if(FABINT)
    begin
    counter <= 32'h00000000;
    counter_en <= 1'b1;
    fabintOut <= 1'b0;
    end
    else if(counter_en)
    begin
    if(counter == 32'h00004FFF)
    begin
    counter <= counter + 1;
    counter_en <= 1'b0;
    fabintOut <= 1'b1;
    end
    else if(counter == 32'h00005000)
    begin
    counter <= 32'h00000000;
    counter_en <= 1'b0;
    fabintOut <= 1'b0;
    end
    else
    begin
    counter <= counter + 1;
    counter_en <= 1'b1;
    fabintOut <= 1'b0;
    end
    end
    else
    begin
    counter <= 32'h00000000;
    counter_en <= 1'b0;
    fabintOut <= 1'b0;
    end
    end
    Program your SmartFusion with this addition, and look at the output of the terminal. Make sure that the later interrupt has a higher priority (lower priority number) in your SoftConsole code. Your output should look similar to the following picture. If it doesn't then most likely some of your clock subsystem is differently configured. If this is the case, ask a lab assistant for help.

    Q4: Explain the output behavior in 2 sentences. The character shifts are due to slight time drift in the system.

    Q5: Explain at least two methods on how to avoid this problem. Hint: PRIMASK

    Post-Lab Assignment 

    Create a simple timer that will generate a FABINT interrupt approximately every 1/2 second. Remember the FABINT signal must be a pulse of one clock cycle Use the interrupt in conjunction with your software to  increase or decrease the position of the servo by ~10 degrees steps automatically every 1/2 second. When the servo position gets to either extreme (0 or 180 degrees), it should reverse direction. The servo should appear to scan back and forth. Use one of the switches to start and stop the incrementing process as a simple toggle action. The switch should be interrupt driven.  

    Demonstrate your working lab to the lab staff.

    Submit answers to the In-Lab. Use this answer sheet for convenience.  Answer Sheet