I am developing app on MC56F83000-EVK using CW 11.1. My app has two ISRs, one ISR is at Priority 1 and the other ISR is at Prioirty 2. Of course, I expect the higher priority ISR to interrupt the lower priority ISR. I am using Quad Timer A. What I have discovered is that I have to protect the lower priority ISR from being interrupted in certain sections of its code. I present the code below with questions interspersed.
First, here is the relevant configuration of the Quad Timers in appconfig.h:
/*.
INTC Configuration
--------------------------------------------
.*/
#define INTC_ICTL_INIT 0x0000U
#define INT_VECTOR_ADDR_26 ISRDRV_A2
#define INT_PRIORITY_LEVEL_26 INTC_LEVEL2
#define INT_VECTOR_ADDR_27 ISRDRV_A1
#define INT_PRIORITY_LEVEL_27 INTC_LEVEL1
/*.
QT_A1 Configuration
--------------------------------------------
Count mode: Counting mode, count rising edges of primary source
Timer Channel Enabled (counter starts counting immediatelly after initialized): No
Primary source: Prescaler (IPB clock/ 16) ,
Input polarity: True , Output polarity: True
Input capture mode: Capture disabled, Input Edge Flag disabled
Output mode: Clear OFLAG output on successful compare
Count stop mode: Count repeatedly , Count length: Count until compare and reinitialize , Count direction: Count down
Output enable (OFLAG to pin): No
Force OFLAG output at startup: No , Forced OFLAG value: 0
Master mode (broadcast compare event): Disable
Enable external OFLAG force (on broadcasted event): No
Co-channel initialization (on broadcasted event): No
Preload Control: 1: Upon successful compare in CMP2 , Load Reg: 0
2: Upon successful compare in CMP2 , Load Reg: 0
Interrupts: Overflow: Disabled
Input edge: Disabled
Compare: Disabled
Cmp 1: Disabled
Cmp 2: Enabled
Input Filter: Input Signal Sampling [timer clocks] : 0
Consecutive Samples Required to Agree: 3
Input Signal Latency: OFF
Fault function: No
Alternative Load: No
Reload on Capture: Yes
Debug action: Continue
Enable DMA write for Comparator Preload Register CMPLD1: No
Enable DMA write for Comparator Preload Register CMPLD2: No
Enable DMA read for Input Edge Flag: No
.*/
#define QT_A1_CTRL_INIT 0x3831U
#define QT_A1_CNTR_INIT 0x9896U
#define QT_A1_COMSCR_INIT 0x088AU
#define QT_A1_ENBL_INIT 0x0000U
/*.
QT_A2 Configuration
--------------------------------------------
Count mode: Counting mode, count rising edges of primary source
Timer Channel Enabled (counter starts counting immediatelly after initialized): No
Primary source: Prescaler (IPB clock/ 1) ,
Input polarity: True , Output polarity: True
Input capture mode: Capture disabled, Input Edge Flag disabled
Output mode: Asserted while counter is active
Count stop mode: Count repeatedly , Count length: Count until compare and reinitialize , Count direction: Count down
Output enable (OFLAG to pin): No
Force OFLAG output at startup: No , Forced OFLAG value: 0
Master mode (broadcast compare event): Disable
Enable external OFLAG force (on broadcasted event): No
Co-channel initialization (on broadcasted event): No
Preload Control: 1: Upon successful compare in CMP2 , Load Reg: 2500
2: Upon successful compare in CMP2 , Load Reg: 0
Interrupts: Overflow: Disabled
Input edge: Disabled
Compare: Disabled
Cmp 1: Disabled
Cmp 2: Enabled
Input Filter: Input Signal Sampling [timer clocks] : 0
Consecutive Samples Required to Agree: 3
Input Signal Latency: OFF
Fault function: No
Alternative Load: No
Reload on Capture: No
Debug action: Continue
Enable DMA write for Comparator Preload Register CMPLD1: No
Enable DMA write for Comparator Preload Register CMPLD2: No
Enable DMA read for Input Edge Flag: No
.*/
#define QT_A2_CTRL_INIT 0x3030U
#define QT_A2_CMP1_INIT 0x09C4U
#define QT_A2_LOAD_INIT 0x09C4U
#define QT_A2_CNTR_INIT 0x09C4U
#define QT_A2_CMPLD1_INIT 0x09C4U
#define QT_A2_COMSCR_INIT 0x008AU
Here is the top level main.c:
void main(void)
{
ioctl(SYS, SYS_INIT, NULL);
ioctl(GPIO_LED_R2, GPIO_SETAS_GPIO, LED_B2);
ioctl(GPIO_LED_R2, GPIO_SETAS_OUTPUT, LED_B2);
ioctl(GPIO_LED_R2, GPIO_SET_PIN, LED_B2);
ioctl(COP, COP_INIT, NULL);
// Disable all A timers
ioctl( QTIMER_A0, QT0_MASS_DISABLE, QT_CH0 | QT_CH1 | QT_CH2 | QT_CH3 );
// Initialise Timers A1 & A2.
ioctl( QTIMER_A1, QT_INIT, NULL );
ioctl( QTIMER_A2, QT_INIT, NULL );
// configure Interrupt Controller
ioctl(INTC, INTC_INIT, NULL);
archEnableInt();
// Enable Timers A1 & A2 to start count down.
TIMER_API_TimerStart( T2 ); //ioctl( QTIMER_A0, QT0_MASS_ENABLE, QT_CH1 );
TIMER_API_TimerStart( T3 ); //ioctl( QTIMER_A0, QT0_MASS_ENABLE, QT_CH2 );
// Enable watchdog
ioctl( COP, COP_DEVICE, COP_ENABLE );
uint32_t delayCnt = 0;
// This while loop runs as a background task when the two ISRs are not executing.
while(1)
{
/* service COP watchdog (if enabled) */
WD_API_Service(); // ioctl(COP, COP_CLEAR_COUNTER, NULL);
delayCnt++;
if (delayCnt > 1000000) delayCnt = 0;
archDelay(60000);
if ((delayCnt%200) == 0 )
{
/* toggle blue LED indicator */
ioctl(GPIO_LED_B2, GPIO_TOGGLE_PIN, LED_B2);
}
} // end while(1)
} // end main()
Now here are the two ISRs:
#pragma interrupt on
void ISRDRV_A2() // 50 microsecond timer ISR, priority 2
{
TIMER_API_ClearInterrupt( T3 ); //ioctl(QTIMER_A2, QT_CLEAR_COMPARE_FLAG, QT_COMPARE2_FLAG );
if ( timerA2Cntr > 1000000 ) timerA2Cntr = 0;
timerA2Cntr++;
} // end ISRDRV_ControlLaw()
void ISRDRV_A1() // 5000 microsecond timer ISR, priority 1
{
ioctl( IntruptController.intruptBase, INTC_INTERRUPTS, INTC_DISABLE );
TIMER_API_TimerStop( T2 );
TIMER_API_ClearInterrupt( T2 ); //ioctl(QTIMER_A1, QT_CLEAR_COMPARE_FLAG, QT_COMPARE2_FLAG );
ioctl( IntruptController.intruptBase, INTC_INTERRUPTS, INTC_ENABLE );
if ( timerA1Cntr > 1000000 ) timerA1Cntr = 0;
timerA1Cntr++;
ioctl( IntruptController.intruptBase, INTC_INTERRUPTS, INTC_DISABLE );
TIMER_API_TimerSet( T2, 5000 ); // 5000 microseconds
TIMER_API_TimerStart( T2 );
ioctl( IntruptController.intruptBase, INTC_INTERRUPTS, INTC_ENABLE );
} // end ISRDRV_Scheduler( )
#pragma interrupt off
The critical code are the calls to TIMER_API_TimerStop/TimerSet/TimerStart/TimerClearInterrupt functions.
The above coding scheme works since timerA2Cntr is always 100 times greater than timerA1Cntr whenever I pause to reveal their values.. Note that timerA2 = 50 usec, timer21 = 5000 us, so timerA2Cntr divided by timerA1Cntr = 100.
Notice that I am dynamically stopping, setting, and starting Timer A1. This is the critical section of code that must be protected. Without this protection, Timer A1 ISR stops being called.
Okay, so things work with the proper protection around the critical code. BUT, why I can't I move the protection into the TIMER_API_Stop/Set/Start functions. For example,
void TIMERDRV_TimerStop( UCTIMER_Obj_t* pThis )
{
ioctl( IntruptController.intruptBase, INTC_INTERRUPTS, INTC_DISABLE );
arch_sTimerChannel* timerA0Channel1Base = CPUTimer[0]->timerChannelBase;
ioctl( timerA0Channel1Base, QT0_MASS_DISABLE, pThis->channelBitMask );
ioctl( IntruptController.intruptBase, INTC_INTERRUPTS, INTC_ENABLE );
} // end TimerStop()
Note that the TIMER_API functions are just pass through functions to matching TIMERDRV functions. Similarly, I add the same protection for all TIMERDRV functions.
BUT much to my surprise, this does not work. WHY? It should. I have only moved the protection from the calling routine to the called routine wherein the real critical code resides.