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.
This problem has mysteriously disappeared. I have removed the interrupt protection and the ISR and critical code work as expected.
Hi,
Pls download the DSP56800EX core reference manual DSP56800ERM.pdf from the link:
Pls refer to section 9.3.1 Interrupt Priority Structure as following.
If you set the priority of timer, for example set TMRA_1 as 1, TMRA_1 as 2, you can not implement the nested interrupt, because when TA1 ISR is being executed, the CCPL bits will add 1 automatically, which forbids that the same priority ISR preempts it.
You can set the TMRA_1 as 1, TMRA_1 as 3 to implement the nested interrupt.
Regarding the phenomenon that "Without this protection, Timer A1 ISR stops being called.", I suppose that TA2 period is 50uS, the period is too small, the TA2 will occupy all the source so that the TA1 has not opportunity to be executed.
Pls try to set the period of TA2 as 50mS, TA1 500mS and have a try.
Every interrupt source has an associated priority level. For some interrupt sources, such as the SWI
instructions and non-maskable interrupts, the interrupt level is pre-assigned. Other interrupt sources, such
as on-chip peripherals, support a programmable priority level. Programmable interrupt sources other than
those in the debug port can be set to one of the maskable priority levels (0, 1, or 2) or be disabled.
Enhanced OnCE interrupt sources can be programmed as level 1, 2, or 3 or as disabled. The CCPL is set to
level 3 on reset.
When an exception or interrupt is recognized and the CCPL is low enough to allow it to be processed, the
CCPL is automatically updated to be one higher than the level of the interrupt (except for the case of
SWILP, which does not update the CCPL, or the case of level 3 interrupts, which leave the priority level at
level 3). This updating prevents interrupts that have the same or a lower priority level from interrupting the
handler for the current interrupt. When the interrupt service routine finishes, the CCPL is set back to its
original value.
To better understand the interrupt priority structure, consider a simple example with nested interrupts.
Assume that the following have already taken place:
1. A serial port on a chip has requested a level 1 interrupt when the core’s CCPL was at level 0.
2. The core has recognized this interrupt and entered the exception processing state. The
CCPL was updated from level 0 to level 2, which is one level higher than the priority of the
recognized interrupt (level 1).
3. Program flow has been transferred to the interrupt handler for the serial port.
Hope it can help you
BR
XiangJun Rong
The problem I am having is not TMRA1 (priority 2) properly interrupting TMRA0 (priority 1). The problem is that TMRA0 contains code that does not execute properly if it is interrupted, ie, the so-called critical code.The critical code is the call to ioctl(TIMERA0.........) which is nothing more than reading or writing a device register. I have found that if I surround the calls to ioctl() with disable and enable interrupt ioctl calls, eg,
ioctl( IntruptController.intruptBase, INTC_INTERRUPTS, INTC_DISABLE );
arch_sTimerChannel* timerA0Channel1Base = CPUTimer[0]->timerChannelBase;
ioctl( timerA0Channel1Base, QT0_MASS_ENABLE, pThis->channelBitMask );
ioctl( IntruptController.intruptBase, INTC_INTERRUPTS, INTC_ENABLE );
the code works, but without the interrupt protection the code does not work.