[SOLVED] Comp2300 Lab 9

35.00 $

Category:
Click Category Button to View Your Next Assignment | Homework

You will receive the following solution file(s) instantly after successful payment:

zip file icon comp2300-2021-lab-9-kmpgq3.zip (97.6 KB)
Assignment Instructions Updated Recently? Submit Below and we will provide new Solution!
Submit New Instructions
πŸ”’ Securely Powered by:
Secure Checkout
5/5 - (1 vote)

Before you attend this week’s lab, make sure:

  1. you understandΒ control flowβ€”what factors influence the order in which instructions get executed in your program (we have been talking about this since week 2!)
  2. you have attended (or watched) theΒ lecturesΒ on the basics of interrupts
  3. you’re able to browse around and understand new assembly code (e.g. provided in a library) with the help of theΒ assembler documentation

In this week’s lab you will:

  1. configure a timer interrupt to periodically β€œhijack” the control flow of your program
  2. configure the GPIO pins connected to the joystick on your discoboard (the little blue diamond thingy) so that pressing down on the joystick triggers an interrupt
  3. write an interrupt handler function toΒ do something usefulΒ when you press the joystick button
  4. use interrupt priorities to control what happens when different interrupts come in at the same time

Introduction

Discuss with your neighbourβ€”what does it mean for your program to have a β€œmain loop”? On your discoboard, does your main loop have toΒ doΒ anything for the program to be useful?

So far, following the control flow through your program has been easy. In most cases, the execution (which you can track through theΒ pcΒ register) just flows from one assembly instruction (i.e. a line of assembly code) to the next. Sometimes you jump around with branch instructions (e.g.Β bΒ andΒ bl), and in certain cases you even makeΒ conditionalΒ branches using the condition flags in the status register (e.g.Β beq,Β bgtΒ orΒ bmi).

In today’s lab, this all changes. You’re going to configure aΒ timer interruptΒ which will periodically β€œinterrupt” the flow of your program, execute a special interrupt handler function, and then return back to where your β€œmain” program was executing. Then you’ll go further by showing how the discoboard can handleΒ multipleΒ interrupts, each with their own handler function, and how each interrupt has aΒ priorityΒ so thatΒ interrupts can interrupt one another. ItΒ soundsΒ confusing… but it’s not, really. You’ll get the hang of it πŸ™‚

Plug in your discoboard, fork & clone theΒ lab 9 templateΒ and let’s get started.

Exercise 1: enabling the SysTick timer

A timer is a hardware component which holds a value (like a register) which counts down (or up) over time. Timers come in various shapes and sizes; some are simple and don’t have much potential for configuration, while others areΒ extremelyΒ configurable, e.g. counting down to zero vs counting up from zero, counting at different rates, etc. Any given microcontroller can include many different timers, all with different names and configuration options, and multiple timers can be used simultaneously.

Your discoboard has a timer called theΒ SysTickΒ timer, described in theΒ ARM reference manualΒ inΒ Section B3.3. As with all things on your discoboard, you configure the SysTick timer by reading and writing to special hardware registers. To configure and use the SysTick timer your program needs to:

  1. enable the timer using theΒ SysTick Control and Status RegisterΒ (SYST_CSR), (also set theΒ CLKSOURCEΒ bit to use the processor clock);
  2. set theΒ SysTick Reload Value RegisterΒ (SYST_RVR)β€”this is the value which gets loaded into the register when it is β€œreloaded”, i.e. after it runs down to zero;
  3. read the current value of the timer register using theΒ SysTick Current Value RegisterΒ (SYST_CVR).

For example, if the SysTick timer is enabled (inΒ SYST_CSR) and the value ofΒ SYST_RVRΒ isΒ 0x4000Β then the timer will take 16384 cycles to count down to zero. How long this takes in wall-clock time depends on the CPU frequency (cycles per second) of the board.

To configure the SysTick timer you’ll need to use theΒ load-twiddle-storeΒ pattern from lab 5 all over again. This time, the relevant information (addresses, offsets, bits) starts atΒ Section B3.3.2Β on page 677 of theΒ ARMv7 reference manualΒ and includes the next couple of sections as well.

We’re in week 9 now, so you now have the tools to read the manual and figure it out for yourself (although don’t be afraid to ask your tutor for help). Here are a few things to be mindful of:

  • remember that these are memory-mapped registers, so e.g. to read the current value into a general-purpose CPU register (e.g.Β r0) you need to use anΒ ldrΒ instruction with the appropriate memory address
  • you can find the memory-mapped addresses for both of these registers in the table inΒ Section B3.3.2
  • to enable the timer, you’ll need to set theΒ enableΒ bit inΒ SYST_CSRΒ and also set the clock source to use the processor clock
  • even though the timer will count down automatically (once tick per clock cycle) your program still needs to be running, so make sure you’ve got an infinite β€œrun” loop in your program
  • the initial clock speed of your discoboard when you first turn it on is 4MHz so keep that in mind when you’re setting theΒ SYST_RVRΒ reload value

For exercise 1, all you need to do is enable the SysTick timer, start it running, and watch the values from theΒ SYST_CVR.

Write an assembly program which configures the SysTick timer to count down fromΒ 4000000, and goes into aΒ finishedΒ infinite loop when the timer reaches zero. Commit and push your program to GitLab.

Exercise 2: configuing the interrupt

You may have noticed that there’s another bit in theΒ SYST_CSRΒ configuration register which you didn’t set in the last exercise, but which looks interesting: theΒ TICKINTΒ bit. TheΒ ARMv7 refernce manualΒ says that this particular bit:

indicates whether counting to 0 causes the status of the SysTick exception to change to pending

So what does this mean, exactly? Well, as discussed in lectures, an interrupt/exception is β€œa signal to the processor emitted by hardware or software indicating an event that needs immediate attention” (fromΒ Wikipedia). If theΒ TICKINTΒ bit is set inΒ SYST_CSR, then the SysTick timer triggers an interrupt every time it counts down to zero. Your CPU handles this interrupt by branching to anΒ interrupt handlerΒ which will (hopefully) branch back when it’s finished. In words, when an interrupt comes in then the CPU stops what it’s doing and branches somewhere else.

The ARM CPU in your discoboard recognises many different types of interrupts. Some are triggered by timers, some are triggered by external peripherals (like the joystick), some are triggered by other chips or wires connected to the discoboard.

All interrupts on your discoboard have:

  1. an index (which is just a number for identifying the source of the interrupt)
  2. a priority
  3. an entry in theΒ vector table, which is a region of the discoboard’s memory where the addresses (i.e. the place to branch to) of theΒ handlerΒ routine for each interrupt

You might be wonderingβ€”whereΒ does my code branch to when the interrupt comes in? Well, that’s what theΒ vector tableΒ is for. It’s a special part of theΒ memory address spaceΒ (starting atΒ 0x0) where the addresses of the different interrupt handler functions are stored. Think of it like a bunch of β€œjump-off points”—theΒ codeΒ for handling the interrupt will be stored somewhere else, the vector table just has the address of the starting point for that code.

You can see your program’s vector table in theΒ lib/startup.SΒ file starting at around line 60

.section .rodata.vtable
    .word _stack_end
    .word Reset_Handler
    .word NMI_Handler
    .word HardFault_Handler
    .word MemManage_Handler
    .word BusFault_Handler
    .word UsageFault_Handler
    .word 0
    .word 0
    .word 0
    .word 0
    .word SVC_Handler
    .word DebugMon_Handler
    .word 0
    .word PendSV_Handler
    .word SysTick_Handler
    @
    @ more entries follow...
    @

What does it mean if there’s aΒ 0Β in a particular β€œslot” in the vector table?

Try and find the vector table for yourself in the startup file. Look for theΒ .section .rodata.vtableΒ directiveβ€”can you see how it mirrors the table fromΒ Section B1.5.2? You can see that there’s already aΒ SysTick_HandlerΒ label in there in the 16th slot in the vector table, but my β€œhot tip” to you is that theΒ SysTick_HandlerΒ function isn’t very interesting at the moment, it’s just defined to be equal to theΒ Default_HandlerΒ (which is just an infinite loop) down at the bottom of the file.

Your job in Exercise 2 is to build on the counter program you wrote inΒ Exercise 1Β and add a couple of things:

  1. when you configure the timer, set theΒ TICKINTΒ bit as well
  2. somewhere in your program, write aΒ functionΒ (i.e. something which you canΒ blΒ to and which does aΒ bx lrΒ at the end) calledΒ SysTick_Handler

If you set it up correctly, yourΒ Systick_HandlerΒ function will get called every time the counter gets to zero.

Again, here are a couple of things to be careful of:

  • you’ll need to declareΒ SysTick_HandlerΒ as a label withΒ .globalΒ visibility so that the address ofΒ yourΒ SysTick_HandlerΒ function will get used in the vector table inΒ src/startup.S, not the boring default one down the bottom of that file)
  • similarly, make sureΒ SysTick_HandlerΒ is declared as a function with the usualΒ .type SysTick_Handler, %function1
  • remember that the interrupt handler (in this caseΒ SysTick_Handler) needs to be a function, and also to play nice and obey theΒ AAPCSΒ (otherwise it might mess with other parts of your program)

Using theΒ led.SΒ library provided, write a program which uses theΒ SysTick_HandlerΒ interrupt to toggle the red LED on and off with a frequency of 1Hz (two toggles per second). Commit and push your program to GitLab.

Exercise 3: GPIO interrupts

Ok, so theΒ SysTick_HandlerΒ looks after the SysTick timer interrupt, but what about the other peripherals on your discoboard? Is there aΒ Joystick_HandlerΒ for handling presses on the joystick? If not, whereΒ canΒ you put your code to be executed when the joystick is pressed?

The discoboard includes a Nested Vectored Interrupt Controller (NVIC), a special bit of hardware which is responsible for watching the various bits of hardware (and software) which can trigger interrupts in your discoboard.

A brief recap: remember that interrupts are a method of triggering anΒ interruptionΒ to the sequence of assembly instructions being executed by the discoboard. Configuring interrupts requires (at a minimum) enabling the interrupt and creating anΒ interrupt handlerβ€”the function which gets called when the interrupt is triggered.

Single-interrupt timeline

In this exercise we’re going to configure an interrupt based on the GPIO pins. In this lab, we’re going to be using our GPIO pins asΒ inputΒ devices to register a β€œclick” on the discoboard’s blue diamond-shaped joystick (pictured below). Finally, you can give (physical) input to your discoboard!

Discoboard joystick

Compared to the SysTick interrupt, there’s a slightly different process in configuring GPIO pins as sources of interrupts. This is because SysTick interrupt is one of the 16 β€œbuilt-in” ARM Cortex interruptsβ€”it’s not just something which ST decided to put in when they designed your discoboard, it’s part of the ARM standard. The GPIO pins, on the other hand, aren’t part of a standardβ€”each microcontroller manufacturer is free to include (or not) any number of GPIO pins on their board, and the way that they are wired into the CPU is up to them (although there are some conventions, so most of them do things in pretty much the same way).

On your discoboard, the GPIO pins are managed through the Extended Interrupts and Events Controller (EXTI), which is described in detail inΒ Section 12Β of theΒ discoboard reference manual. From that section:

The extended interrupts and events controller (EXTI) manages the external and internal asynchronous events/interrupts and generates the event request to the CPU/Interrupt Controller (the NVIC) and a wake-up request to the Power Controller.

This means that raising a GPIO-triggered interrupt is really a two-stage process (at least from the hardware’s perspective):

  • the EXTI notices the hardware event (e.g. an edge trigger on a GPIO line, or a timer event from one of the discoboard’s many timers) and raises an interrupt line into the NVIC
  • the NVIC deals with that interrupt,Β potentiallyΒ saving the current register context to the stack and switching to the handler function (depending on whether the interrupt is currently enabled, whether any higher priority interrupts are already running, etc.)

So, to configure your discoboard so that when you press the central joystick button an interrupt is triggered (which you can then write a handler for) you need to enable & configure the interrupt in both the EXTI and the NVIC. As for most things on your discoboard, this is done by reading & writing the right bits in the right places to the various EXTI & NVIC configuration registers.

If this is still a bit confusing, you’ll get another chance to work through it (in more detail) inΒ next week’s lab.

There are often more things to configure (i.e. GPIO pins) than there are bits in a 32-bit registerβ€”can you guess how the designers of the discoboard get around this limitation?

Since you’re hopefully well acquainted with the load-twiddle-store process for writing the configuration registers like this, to start this lab we’ve provided some files with the initialisation code to save you some time. It’s worth having a look through, thoughβ€”if you don’t understand what they’re doing (and you can’t figure it out from theΒ assembler manual) then you need to ask for helpβ€”it’s not too late!

The template provides some starter code for using the joystick inΒ joystick.S. Make sure you read and understand what the functions in that library are doingβ€”if you don’t, you’ll have trouble later on. Here are a few things worth noticing:

  1. the LED library (which builds on the LED code from the last couple of labs) now supports the green LED as wellβ€”have a look at the functions markedΒ .globalΒ at the top ofΒ led.sΒ to get an idea of the things you can do with this library
  2. the joystick init code sets the GPIO pins toΒ input modeΒ using theΒ GPIOA_MODERΒ register (this is different to theΒ blinky lab, where we only used the pins in output mode)
  3. the central joystick button is connected toΒ PA0Β which (at least as configured in the setup code) will trigger theΒ EXTI0_IRQHandlerΒ you’ll need to writeβ€”don’t forget to declare the handler asΒ .type EXTI0_IRQHandler, %function
  4. unlike the SysTick interrupt, the EXTI interrupts are not enabled by defaultβ€”they must be enabled by setting the relevant bit in the interrupt mask registerΒ EXTI_IMR1Β (described inΒ Section 12.5.1Β of theΒ disco-boardΒ reference manual, mapped to addressΒ 0x40010400)
  5. the EXTI controller can use either aΒ rising edgeΒ (when the signal goes fromΒ 0Β toΒ 1) orΒ falling edgeΒ (when it goes fromΒ 1Β toΒ 0) orΒ bothΒ as a trigger for the interruptβ€”it’s currently set to rising edge in theΒ joystick_initΒ function (think: what will the difference be if you set a falling edge trigger instead/as well?)
  6. once the interrupt handler function has done whatever it needs to do, it needs to tell the EXTI controller that it’s finished handling interruptΒ nΒ by writing theΒ nth bit in theΒ EXTI_PR1Β β€œpending” register (0x40010414)

Write a program where pressing the central joystick button toggles the green LED on and off. Hint: the interrupt configuration & enabling is already done inΒ joystick_initβ€”for this Exercise you only need to write the interrupt handler function and add it’s symbol to the vector table. Commit & push your program to GitLab.

In this exercise you’ve turned on the β€œcentre” button on the joystick (PA0), but the β€œdirections” (up/down/left/right) don’t workβ€”they’re connected to pins 1 to 5 of the same GPIO port (GPIOA). If you’re keen, you can turn on the other ones (same process as before except some of the register addresses & bit indexes will be different).

Exercise 4: interrupt priorities

What happens when you are busy handling interrupt and another interrupt happens? In this exercise you will construct such a scenario and see how interrupt priorities work.

Use the following code as theΒ SysTick_HandlerΒ function:

.type SysTick_Handler, %function
SysTick_Handler:
  bl red_led_on
SysTick_Handler_infloop:
  nop
  b SysTick_Handler_infloop
.size SysTick_Handler, .-SysTick_Handler

This handler just turns on the red LED and then goes into an infinite loop. The effect is that when the first SysTick interrupt happens, control flow will get stuck in this handler code.

This is actually aΒ badΒ idea for writing an interrupt handler. Usually you want the interrupt handling to be quick (it’s an interrupt, not the right place to do computationally intensive work). But it’s useful to do it this way to see how interrupt priorities work.

Now if you press the joystick button when the red LED is on, what happens?

Do you remember that theΒ NΒ inΒ NVIC stands forΒ nested? This means that the interrupts can happen inside of one another. Here’s a diagram to show what it might look like:

Multi-interrupt timeline

This isn’t the full story, thoughβ€”the discoboard doesn’t always β€œkick out” the currently running interrupt for the new one, it depends on the priority. On the discoboard (as in life) some things are more important than others, and each interrupt has a priority associated with it. On your discoboard, this priority is represented by a 4-bit number, with 0 being the highest priority and 15 being the lowest. When an interrupt handler is running and a new interrupt is triggered, it will only preempt (i.e. interrupt) the currently running interrupt handler if the priority is lower. If it’s the same or higher, that interrupt handler will be run once the currently running one finishes (i.e. returns withΒ bx lr).

If your green LED doesn’t turn on when you press the joystick and the red LED is on, this means that either the SysTick interrupt has the same or higher priority (i.e. a smaller number as the priority value) than the EXTI0 interrupt. To change the interrupt priority so that you can click the green LED on even when the red one is blinking (i.e. when the SysTick interrupt handler is running) you’ll need to lower the priority (give a higher number) to the SysTick interrupt.

Because the two interrupts (the SysTick timer interrupt and the EXTI0 joystick interrupt) have some differences as mentioned earlier (one is part of the core ARM Cortex standard, one is a discoboard-specific thing) you need to set their interrupt priorities in slightly different places:

  • for the SysTick interrupt, you can set the interrupt priority by writing bits 28-31 of the System Handler Priority Register 3 (SHPR3, base addressΒ 0xE000ED20) described in B3.2.12 of theΒ ARM architecture reference manual
  • for the PA0 interrupt, you can set the interrupt priority by writing bits 20-23 of the NVIC interrupt priority register (NVIC_IPR1, base addressΒ =0xE000E404)

Modify the priority of your SysTick interrupt handler so that itΒ doesΒ get preempted by the EXTI0 handler and the green light comes on when the red LED is on. Commit and push your program to GitLab. Experiment with different priority valuesβ€”what happens if they’re the same?

If you’re wondering how to figure out exactly which bits to set to control the priorities, thenΒ here’s an article which might help you out. It’s for a different ARM Cortex-M board (i.e. not your discoboard) the main principle is the same.

Exercise 5: QuickClick

In the final exercise, your job is to take your new knowledge of interrupts and make a game calledΒ QuickClick. It’s a simple game:

  1. you blink the red LED on your discoboard for a short time every 5 seconds
  2. the player’s goal is then to press the joystick button when the red LED is on
  3. if you get the timing right (i.e. the red LEDΒ isΒ on when the button is pressed) the green LED comes on
  4. each time you get it right, the red blink duration gets shorter (so that it’s harder to get the timing right for the next round).

When you’re clicking your joystick, try and be aΒ bitΒ gentle on your discoboard. Make sure you’re on a flat surface (and that none of the pins are likely to get bent). Don’t get too carried away and smash your fist down on the joystickβ€”it’s not built to handle that πŸ™‚

For this exercise, you can take advantage of your ability to enable & disable different interrupts in software to make it easy to implement the β€œis the red light on? if so, then clicking the button will turn on the green” logic:

  1. perform all the configurations steps necessary (including defining the handler function) to use the joystick as an input device
  2. in yourΒ SysTick_Handler:
    • enable the joystick interrupt by setting the bit in the interruptΒ setΒ enable registerΒ NVIC_ISER0Β (address:Β 0xE000E100) The bit you are looking to set can be found in the position column of the interrupt vector table in section 11.3 of theΒ discoboard reference manual
    • You may also want to clearΒ bothΒ EXTI and NVIC interrupt pending bitsΒ beforeΒ enabling the EXTI interrupt. Otherwise the pending interrupt will trigger when you enable it, causing the green LED to turn on as soon as the red is on.
    • blink the red LED in aΒ blockingΒ fashion (i.e. use a delay, so that the red LED goes on and then off again before theΒ SysTick_HandlerΒ exits)
    • beforeΒ SysTick_HandlerΒ exits, disable the joystick interrupt by setting the bit in the interruptΒ clearΒ enable registerΒ NVIC_ICER0Β (address:Β 0xE000E180)

There’s one more β€œgotcha” to be aware of when dealing with theΒ clearΒ enableΒ andΒ clearΒ pendingΒ NVIC control registers (e.g.Β NVIC_ICER0Β orΒ NVIC_ICPR0). As described above, to disable an interrupt (ICER) or clear a pending interrupt (ICPR) you write aΒ 1Β to the corresponding bit (e.g. to disable the interrupt in position 6 of the NVIC you write aΒ 1Β to the 7th bit from the right inΒ NVIC_ICER0).

However, you might have noticed something if you were reading Sections B3.4.5 (p684) and B3.4.7 (p685) theΒ ARM reference manualΒ really closely. In the description for those registers it says:

1: On reads, interrupt enabled

which means that if an interrupt is enabled, then aΒ readΒ from that (memory-mapped) register will show the corresponding bit asΒ 1.

This is a problem for theΒ load-twiddle-storeΒ pattern, because the point of the load twiddle store is to leave all the bits unchanged except for the one you’re interested in. However, this means that all of the currently enabled interrupts (whose bits will read asΒ 1Β in theΒ loadΒ phase) will be disabled when you write the bits back in theΒ storeΒ phaseβ€”which (almost certainly) isn’t what you want!

Again, here’s an example: say there are 3 interrupts currently enabled, then a load from the corresponding register would haveΒ 1s in those three positions, andΒ 0s elsewhere. If you load/twiddle/store the value, then all three of those interrupts would beΒ clearedΒ by the store operation.

This means that for theΒ clearΒ enable/pendingΒ registers you should just write aΒ 1Β for the particular interrupt you’re interested in, and aΒ 0Β in all the other bits.

Next week’s lab goes into more detail about how the interrupts are routed in your discoboard, so if you’re still confused/curious thenΒ jump aheadΒ and see how it works in more detail.

Can you see how you can use this technique to temporarily enable the joystick interrupt in the SysTick interrupt handler so that the joystick will only work when the red LED is on?

Implement the QuickClick game following the steps above (you can use as much of the startup code provided earlier as you like). Commit and push your program to GitLab.

Extension ideas

There areΒ heapsΒ of things you can do to stretch yourself further:

  • can you modify QuickClick to make it more fun? (e.g. using the direction buttons on the joystick as well?)
  • can you re-implement the QuickClick game without interrupts?
  • can you turn on the discoboard’s random number generator (RNG) and use it so that the red LED blinks on randomly, rather than at regular intervals? Hint:Β Section 24Β of theΒ discoboard reference manualΒ is the place to find the configuration steps required to get the RNG workingβ€”it’s not too difficult.

1 file Β· 97.6 KB

  • comp2300-2021-lab-9-kmpgq3.zip 97.6 KB