Starting from:
$30

$24

Laboratory Exercise #8 Timers, Interrupts, and Semaphores Solution




1 Introduction




In this laboratory you will develop a Task that combines Timers, Interrupts, and FreeRTOS Semaphores to implement a time-of-day (TOD) clock in hours, minutes, seconds, and centi-seconds.




You should be able to find the information you need in the documents available to the class. You should expect to spend time outside of class and lab to organize and plan your task before starting to write your program (Task).




This document will guide you through what to do. Your job is to figure out how to accomplish the job. The following sections guide you through incremental development of your Task.




You will write two subroutines. The first, called “Task_TimeOfDay” is similar to other FreeRTOS tasks you have worked with. The second subroutine will be an Interrupt Service Routine (ISR) “Timer_0_A_ISR_Handler” to handle interrupts from the hardware timer. The two subroutines should be in the same file, “Task_TimerInterrupt.c”




2 First Experiment




2.1 Clock Variables




You need to declare a TimerCount variable and a set of variables for Hours, Minutes, Seconds, and Centi-Seconds. The TimerCount variable should be global in your module, i.e. accessible from your Task and your Interrupt Service Routine (ISR). The Hours, Minutes, Seconds, and Centi-Seconds variables can be local to your Task_TimeOfDay subroutine.




2.2 Configure the Timer




You will use Timer_0_A. This is a 16-bit timer that counts down from a specified value. When the timer reaches 0 a flag is set and, if enabled, an interrupt generated. In this first experiment, we will not enable interrupts initially and just check the timer 0 flag.




The timer should “expire” every 10 mS (centi-second). That is, every 10 mS the timer should reach 0 and set a flag. The timer should automatically reload a count value to set a flag 10 mS in the future. Read the description of timer behavior in: LM3S1968 datasheet, section 9.3.3.1. When the timer “expires” you need to clear the flag.










EECS 388 Laboratory #8
1
 
Version B







One of your jobs is to determine what the count value should be. You will need to consider the processor clock (50 MHz with 20 nS period), the timer clock scale factors, and determine a count value to give you a 10 mS interval.




Steps to configure the timer:




Enable Timer_0 with SysCtlPeripheralEnable



Configure Timer_0_A as a 16-bit timer with periodic behavior using



TimerConfigure




Compute and set a pre-scale divisor to establish 10 mS intervals. Note that



you will have to consider the processor clock (20 nS) and the range of the 16-bit timer (0 – 65,535). Use TimerPrescaleSet to set the prescale value for Timer_0_A.




Set the re-load value. You will need to consider the processor clock (50 MHz with 20 nS period), the timer clock scale factors, and determine a count value to give you a 10 mS interval. Use TimerLoadSet to set the re-load value.



Enable Timer_0_A with TimerEnable.



The above configuration steps should be placed in the initialization section of your Task_TimeOfDay.




To test your timer configuration you might consider the following code within your




Task while ( 1 ) {} loop:




while (((TimerStatus_1 = TimerIntStatus( TIMER0_BASE, 0)) & TIMER_TIMA_TIMEOUT ) == 0 ) {}




TimerIntClear( TIMER0_BASE, TIMER_TIMA_TIMEOUT ); TimeCount++;




This code will wait for a timer flag, clear the flag, and increment TimeCount.




This code does tie up the CPU, but that is OK during testing.




Once Timer_0_A expires then you should appropriately increment Centi-Seconds, then Second, then Minutes, and then Hours. These are nested if statements.




Verify that this code is working by displaying the time of day on the OLED display or sending data to the UART.




3 Second Experiment




In the second experiment you will write a Timer Interrupt Service Routine (Timer_0_A_ISR_Handler) and enable interrupts.




The ISR is written (coded) like a subroutine. The ARM CPU will invoke the ISR subroutine when the timer expires.




Steps to code a Timer ISR:




EECS 388 Laboratory #8
2
 
Version B







Write a Timer Interrupt Service Routine.



The ISR should clear the Timer interrupt with TimerIntClear.



The ISR should increment TimeCount.



Declare the Timer ISR as a global (extern) function.



You should remove the TimerIntClear and incrementing TimeCount



from Task_TimeOfDay.




You will need to enable Timer 0 subtimer A interrupts in the Timer Peripheral and in the ARM Nested Vector Interrupt Controller. Use the following code after setting up the timer in Task_TimeOfDay and before enabling the timer in the initialization section of your task.




You will need to map the Timer_0_.




// Register Timer_0_A ISR




IntRegister( INT_TIMER0A, Timer_0_A_ISR );




Enable Timer_0_A interrupt in the peripheral TimerIntEnable( TIMER0_BASE, TIMER_TIMA_ TimerIntEnable( TIMER0_BASE, TIMER_TIMA_TIMEOUT );



Enable Timer_0_A interrupt in LM3S NVIC IntEnable( INT_TIMER0A );
IntMasterEnable();




Build and test your Task. While the code in the Task while ( 1 ) {…} loop will run intermittently and the Hours, Minutes, Seconds, Centi-Seconds will not increment properly, your TimeCount variable should increment on each interrupt. You can pause your program and examine the TimerCount value in the debugger.




4 Third Experiment




In your third experiment you will use a semaphore to communicate from the Timer ISR to your Task. First declare a binary semaphore in your in Task_TimeOfDay module with the code below. Declare the semaphore before you initialize Timer_0_A.























































EECS 388 Laboratory #8
3
 
Version B







#include "semphr.h"




SemaphoreHandle_t Timer_0_A_Semaphore;




In your task initialization section, create a binary semaphore with:




//




Initialize Timer_0_A_Semaphore



 



vSemaphoreCreateBinary( Timer_0_A_Semaphore );




In your Timer ISR you must set, or in FreeRTOS parlance Give, the semaphore. The code for this is:




//




"Give" the Timer_0_A_Semaphore



 



xSemaphoreGiveFromISR( Timer_0_A_Semaphore, &xHigherPriorityTaskWoken );




//




If xHigherPriorityTaskWoken was set to true,



we should yield. The actual macro used here is



port specific.



//




//




portYIELD_FROM_ISR( xHigherPriorityTaskWoken );




The variable xHigherPriorityTaskWoken is declared in the Timer ISR with:







portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;




In your Task while ( 1 ) {} loop you now wait for the semaphore to be set with:




xSemaphoreTake( Timer_0_A_Semaphore, portMAX_DELAY );




FreeRTOS will suspend your Task when you call xSemaphoreTake and re-start your task when the semaphore is set, i.e. set by the Timer ISR.




Build and test your third experiment.




5 Demonstrations




Demonstrate each of your experiments to your GTA.

























EECS 388 Laboratory #8
4
 
Version B



More products