Chapter 8: Interrupts
8.2 Interrupt Services
The OSEK/VDX OS provides multiple services to manage interrupts, most of which control masking responses to interrupts. The first two services — EnterISR() and LeaveISR() — were discussed in the previous section. Neither service can be used at task level or in a hook routine.
void EnterISR(void);
EnterISR() can be invoked only from within a category 3 ISR. This service tells the OS that an interrupt has been entered that will request an API service at some point. The OS sets up the necessary conditions to handle API calls from an interrupt. How this situation is han-dled is highly specific to the CPU architecture, which is a complication to the developer of the implementation but is transparent to the application.
void LeaveISR(void);
LeaveISR() also can be invoked only within a category 3 ISR. It is the counterpart to EnterISR() and must be the last instruction executed in the ISR after EnterISR() has been invoked. If EnterISR() has not been invoked, as in the previous example where the ISR dumps a buffer to a serial port and the buffer is not empty, LeaveISR() must not be invoked because the OS might or might not return from LeaveISR(), depending on the status of the system, which can change in response to the API functions invoked in the ISR.
Neither of these functions returns a StatusType because in many microcontrollers, it is impossible to determine from whence the service was invoked. Consequently, there is no way to know that the service was called from a task or hook routine and return E_OS_CALLEVEL, which is the expected return for a call from the wrong level.
In the example program, the IOSampleShuffleSwitch task has been eliminated and replaced with the ISR category 3 routine IOShuffleSwitchISR shown in Listing 8.1.
This routine is triggered on any rising edge of the shuffle switch input. Because this is a valid input only when the system is in the shuffling state, the ISR first checks for this state after resetting the interrupt. This switch is on the MDASM12 port of the MPC555, and the status register that is reset is defined in register.h. If the game state is GAME_SHUFFLING, the edge is processed and the counter is incremented. If the game state is any other state, the ISR immediately returns to the calling routine.
Interrupt Services
95
I also have added an alarm — ShufflingCompleteAlarm — that replaces SampleShuf-fleSwitchAlarm. This alarm is reset each time an edge is received. When it expires, the shuf-fling switch is considered released and the ShufflingComplete task (Listing 8.2) is activated.
It manually sets the two events set previously in IOSampleShuffleSwitch because an alarm can only activate one task or set one event.
Listing 8.2 ShufflingComplete task.
One important difference between the example here and the code on the accompanying CD is that the MPC555 processor has only one interrupt vector for all seven external and 24 internal interrupts. The OSEKWorks BSP has created a very efficient interrupt dispatch rou-tine, but to optimize it, interrupt category 3 is not supported. Because this category is optional, this is not a problem; however, in the sample code, I commented the calls to Enter-ISR() and LeaveISR() because calling the services twice in the same ISR does very nasty things to the program.
In a category 3 interrupt, the routine is a normal ISR with the addition of the EnterISR() andLeaveISR() API services. To define a category 2 ISR, the application must use the follow-ing method.
if(GetAlarm(ShuffleAlarm,(TickRefType)tick) == E_OS_NOFUNC){
SetAbsAlarm(ShuffleAlarm,5,3);
strcpy(displayBuffer,"\fSHUFFLING -");
ActivateTask(OutputDisplay);
}
CancelAlarm(ShufflingCompleteAlarm);
SetRelAlarm(ShufflingCompleteAlarm,1000,0);
IncrCounter(SHUFFLE_COUNTER);
LeaveISR();
96
Chapter 8: InterruptsThe hardware used with the example program has a keyboard decoding chip that provides an interrupt when a key is pressed. I used this capability to eliminate the IOSampleKeypad task, and I replaced it with the category 2 ISR IOReadKeypadISR (Listing 8.3) because the interrupt always activates the ProcessKeyPress task. The ISRs also must be defined in the OIL configuration file prior to use (Listing 8.4).
Listing 8.3 Keypad ISR.
The ISR object in the OIL configuration file has three possible attributes. The first, CATE-GORY, is required and has the value 1,2, or 3. The second attribute, RESOURCE, is optional and has exactly the same format as the RESOURCE attribute for the TASK object definition discussed in Chapter 7. The final attribute, ACCESSOR, is also optional and is discussed in Chapter 9,
“Interprocess Communication.” For the example ISR, I am not concerned with resources or interprocess messages.
The remaining API services associated with interrupts are concerned with masking inter-rupts and defining critical sections in application code. The services are divided into four cat-egories.
Listing 8.4 ISR OIL definition.
ISR(IOReadKeypadISR) {
char tempKey;
usiuReg.sipend = 0x00800000; /* Clear Interrupt Bit */
tempKey = HWGetValue(&KEYPAD);
if(tempKey != 0){
*keyBufferEnd = tempKey;
if((++keyBufferEnd) == keyBuffer + KEY_BUFFER_SIZE){
keyBufferEnd = keyBuffer;
}
SetEvent(ProcessKeyPress,KEYPRESS);
} }
/*******************************************************/
/* ISRs */
/*******************************************************/
ISR IOReadKeypadISR { CATEGORY = 2;
};
Interrupt Services
97
The first category of API services is a query category in which the application can check which interrupts are enabled or disabled. This category has one service, GetInterruptDe-scriptor().
StatusType GetInterruptDescriptor(IntDescriptorRefType descriptor);
For most implementations, this service sets the bits in the variable pointed to by the descrip-tor reference to correspond to the current state of the interrupts. If an interrupt is enabled, the bit corresponding to that interrupt is set to 1; otherwise, it is set to 0. The mapping of the interrupt sources to the bits in the descriptor variable is dependent on both processor and implementation. For example, a powerful 32-bit processor with more interrupt sources than there are bits in a machine word would result in a completely different definition and inter-pretation of IntDescriptorRefType and would include an implementation-specific service to control the interrupts individually. Use of GetInterruptDescriptor() should be limited to modules in an application that are specific to the microcontroller used, such as hardware device drivers.
This service always returns an E_OK status in both standard and extended modes. This ser-vice can be invoked from the task or interrupt level and from ErrorHook(),PreTaskHook(), andPostTaskHook().
To debug the system, I modified ErrorHook() (Listing 8.5), where errors are logged. In the modified routine, I now obtain the current interrupt descriptor and save it in the error log array. Because this routine is only used to debug a specific application on a given microcon-troller and a specific OSEK/VDX implementation, portability of the code is not a concern. In this hook routine, I have explicitly checked if the routine is called recursively, using the flag recursive. This is recommended if the OS version is 2.0 or earlier. In version 2.1, the OS is not allowed to call ErrorHook() recursively.
Listing 8.5 Modified ErrorHook() routine.
ISR IOShuffleSwitchISR { CATEGORY = 3;
};
void ErrorHook(StatusType error) {
static UINT8 recursive=0;
if(recursive==0){
recursive=1;
nextErrorLog->error = error;
GetTaskID(&nextErrorLog->task);
GetInterruptDescriptor(&nextErrorLog->descriptor);
++nextErrorLog;
98
Chapter 8: InterruptsThe second category of interrupt API services is a pair of interrupt masking services that are similar to the services provided in most OSs — EnableInterrupt() and DisableInter-rupt().
StatusType DisableInterrupt(IntDescriptorType descriptor);
StatusType EnableInterrupt(IntDescriptorType descriptor);
These services take the information passed in the descriptor parameter and either enable or disable the corresponding interrupt sources. If the bit in descriptor is set to 1, that interrupt source is either enabled or disabled. If the bit is set to 0, then the interrupt source is not affected. As with the service GetInterruptDescriptor(),DisableInterrupt() and EnableIn-terrupt() are specific to the particular microcontroller hardware and the OSEK/VDX imple-mentation chosen. Their use should also be limited in the application.
The return value from these services is
• E_OK if no error occurs or
• E_OS_NOFUNC in extended status mode if at least one interrupt source is not enabled or dis-abled, depending on the service.
These services can be invoked from the task or the interrupt level, but they cannot be invoked from any hook routines.
If the current state of the interrupts has to be restored after the critical section, it must be stored temporarily in a local variable using GetInterruptDescriptor() before disabling the interrupts.
The third category of interrupt masking services allows you to mask all interrupt sources for a critical section and then restore all interrupts to the prior status automatically. The two functions provided, new to the version 2.1 OS specification, are EnableAllInterrupts() and DisableAllInterrupts().
void DisableAllInterrupts( void );
void EnableAllInterrupts( void );
WhenDisableAllInterrupts() is invoked, the OS automatically saves the states of all inter-rupts. These states are restored when EnableAllInterrupts() is invoked. At this point, the application has entered a critical section. The OS standard requires you to observe the follow-ing limitations within the application.
• Your application must not invoke any API services from within the critical section.
• You cannot nest critical sections using these two services. If a nesting could occur (as is possible in a library function), the implementation should ignore it by using the next cate-gory of interrupt masking services that disables all ISR 2 or 3 interrupts.
if(nextErrorLog > errorLog + sizeof(errorLog)) {
Interrupt Services
99
These services may be called from the task or the interrupt level, but cannot be called from any hook routines.
In the example program, I modified one of the functions to enter a critical section while the display is updated; the OutputDisplay task calls OutputNewDisplay() (Listing 8.6).
Listing 8.6 OutputNewDisplay() with critical section.
In the task, I added DisableAllInterrupts() and EnableAllInterrupts() to create a critical section while the routine writes the current display value to the display. This critical section was added to ensure that the display is completely updated and not preempted in the middle of an update. If an interrupt occurs and the task is preempted, one-half of the display can be modified before a noticeable pause occurs and the rest of the display is updated. This inter-ruption appears as a flicker and can be annoying to the user, so the critical section eliminates that possibility.
void OutputNewDisplay(void) {
UINT8 *displayControl = DISPLAY_CONTROL_LOCATION;
UINT8 *display = DISPLAY_BUFFER_LOCATION;
UINT8 i,outputRow;
UINT8 translateRow = 0;
*displayControl = 0x01;
wait(30000);
DisableAllInterrupts();
while(translateRow < MAX_DISPLAY_ROWS) {
outputRow = RowTranslation[translateRow++];
for(i=0;i<MAX_DISPLAY_LINE_LENGTH;i++) {
*display = displayMessage[outputRow][i];
wait(1000);
} }
i=((cursorPosition.row&0x01)*0x40) +
((cursorPosition.row&0x02)/2*MAX_DISPLAY_LINE_LENGTH) + cursorPosition.column;
*displayControl = 0x80+i;
wait(1000);
EnableAllInterrupts();
}
100
Chapter 8: InterruptsThe fourth category of interrupt masking services disables all interrupts of category 2 or 3. This pair of services, SuspendOSInterrupts() and ResumeOSInterrupts(), have the follow-ing function prototypes.
void SuspendOSInterrupts(void);
void ResumeOSInterrupts(void);
When SuspendOSInterrupts() is invoked, it saves the current state of all interrupts. This state is restored when ResumeOSInterrupts() is invoked. Unlike the previous category of interrupt masking services, these two services can be nested. The original states of all inter-rupts that were saved when SuspendOSInterrupts() was first invoked will be restored when the final call to ResumeOSInterrupts() is performed. How this is actually performed in an implementation is dependent on the characteristics of the microprocessor.
As with the previous category of interrupt masking services, the application is not allowed to invoke any API service while in this critical section. These services can be called from the task or the interrupt level but cannot be called from any hook routines.
One point made in the OSEK/VDX OS standard is that these two services are only intended to disable category 2 and 3 interrupts. However, if the implementation cannot dis-able these interrupts in a limited and efficient manner, other interrupts could be disdis-abled, which indicates that there might be no differences between these final two categories of inter-rupt masking services in some implementations.
In the OutputDisplay task described with the previous category of services, the critical sec-tion also can be realized using this category of interrupt masking services. Because ISR cate-gory 1 interrupts are typically very short, this catecate-gory of interrupts should not affect the user’s perception of the display update.