This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TMS320C28346: Temporarily disable Timer 2 interrupt does not work

Part Number: TMS320C28346
Other Parts Discussed in Thread: CONTROLSUITE

Hello

I have an interrupt that is called by Timer 2 interrupt.

I need to disable this interrupt temporarily while executing some "atomic" code.

I do this by the following lines:

CpuTimer2.RegsAddr->TCR.bit.TIE = 0;

Some Code

CpuTimer2.RegsAddr->TCR.bit.TIE = 1;

But when I do this, sometimes the interrupt is not called. I assume it is when the timer overflows at exactly the time when the interrupt is disabled/enabled.

Has anybody a clue how to workaround this?

Regards,
Daniel

  • In the meantime, I thought I found out what is the problem.
    Using CpuTimer2.RegsAddr->TCR.bit.TIE = 0; actuall reads the whole 16bits, changes the TIE bit, and writes all 16bits back. This potentially clears the TIF bit in case it is set when the interrupt is disabled/enabled.
    Now I changed the code so that the IF bit is always written with a 0, but I still loose interrupts.

    Regards,
    Daniel
  • Daniel,

    Can you confirm your interrupts are configured correctly? It sounds like you have nested interrupts within your code. Here is a wiki page that talks about how to nest interrupts.

    processors.wiki.ti.com/.../Interrupt_FAQ_for_C2000

    Regards,
    Ozino
  • Hi Ozino

    Thanks for your reply.
    Yes we are using nested interrupts, but that should not be the problem.

    I now created a test program based on the Example_2834xCpuTimer example program. The program is attached.

    The problem is reproducable there with only one interrupt.

    The program sets up Timer 2 as the millisecond interrupt. The interrupt will measure the time difference between the last call and the current call. It will build a maximum value of this time difference and write the value to the variable called 'maxDifference'.

    Timer 1 is set up as microsecond timer to measure time exactly. It does not call an interrupt.

    Then there is the main loop which will clear the TIE flag of timer 2, wait 1 miicrosecond, and set the TIE flag again.
    Then it waits for 950us and start all over again.

    When the program is started, the variable 'maxDifference' should be monitored. 
    It can be seen that the value will soon rise to 2000, while it should stay at 1000.

    If the MS_INT_DISABLE/ENABLE lines are commented out, the value will stay at 1000.

    Is there another way to temporarily disable the timer 2 interrupt?
    I have to add that we use the Ti software interrupt priorization code, so I don't think I can just disable group 14 interrupt by a sequence like

    {
    IER &= ~M_INT14;
    some Code
    IER |= M_INT14;
    }

    because I think it would interfere with the priorization code? The code I am referring to looks is this in each ISR:

    interrupt void cpu_timer0_isr (void)
    {

    IER |= M_INT1; // Set "global" priority
    IER &= MINT1;
    PieCtrlRegs.PIEIER1.all &= MG17; // Set "group" priority
    PieCtrlRegs.PIEACK.all = 0xFFFF; // Enable PIE interrupts
    EINT;

    CpuTimer0.InterruptCount++;

    PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;

    // Restore registers saved:
    DINT;
    PieCtrlRegs.PIEIER1.all = TempPIEIER;
    }

    Thanks and best regards,
    Daniel

    // TI File $Revision: /main/4 $
    // Checkin $Date: June 28, 2010   09:18:01 $
    //###########################################################################
    //
    // FILE:    Example_2834xCpuTimer.c
    //
    // TITLE:   DSP2834x Device Getting Started Program.
    //
    // ASSUMPTIONS:
    //
    //    This program requires the DSP2834x header files.
    //
    //    Other then boot mode configuration, no other hardware configuration
    //    is required.
    //
    //
    //    As supplied, this project is configured for "boot to SARAM"
    //    operation.  The 2834x Boot Mode table is shown below.
    //    For information on configuring the boot mode of an eZdsp,
    //    please refer to the documentation included with the eZdsp,
    //
    //       $Boot_Table:
    //
    //         GPIO87   GPIO86     GPIO85   GPIO84
    //          XA15     XA14       XA13     XA12
    //           PU       PU         PU       PU
    //        ==========================================
    //            1        1          1        1    TI Test Only
    //            1        1          1        0    SCI-A boot
    //            1        1          0        1    SPI-A boot
    //            1        1          0        0    I2C-A boot timing 1
    //            1        0          1        1    eCAN-A boot timing 1
    //            1        0          1        0    McBSP-A boot
    //            1        0          0        1    Jump to XINTF x16
    //            1        0          0        0    Jump to XINTF x32
    //            0        1          1        1    eCAN-A boot timing 2
    //            0        1          1        0    Parallel GPIO I/O boot
    //            0        1          0        1    Parallel XINTF boot
    //            0        1          0        0    Jump to SARAM	    <- "boot to SARAM"
    //            0        0          1        1    Branch to check boot mode
    //            0        0          1        0    I2C-A boot timing 2
    //            0        0          0        1    Reserved
    //            0        0          0        0    TI Test Only
    //                                              Boot_Table_End$$
    //
    // DESCRIPTION:
    //
    //    This example configures CPU Timer0, 1, and 2 and increments
    //    a counter each time the timers assert an interrupt.
    //
    //       Watch Variables:
    //          CpuTimer0.InterruptCount
    //          CpuTimer1.InterruptCount
    //          CpuTimer2.InterruptCount
    //
    //###########################################################################
    // $TI Release: 2834x Header Files V1.12 $
    // $Release Date: March 2011 $
    //###########################################################################
    
    
    
    #include "DSP28x_Project.h"     // Device Headerfile and Examples Include File
    
    //#define  MS_INT_ENABLE(key)                  if(key) CpuTimer2.RegsAddr->TCR.bit.TIE = 1;
    //#define  MS_INT_DISABLE()                    CpuTimer2.RegsAddr->TCR.bit.TIE; CpuTimer2.RegsAddr->TCR.bit.TIE = 0; //This variant of interrupt disable should be called when only the millisecond interrupt must be blocked
    //#define  MS_INT_ENABLE(key)                  if(key) CpuTimer2.RegsAddr->TCR.all = ((CpuTimer2.RegsAddr->TCR.all | 0x4000) & (~0x8000));
    //#define  MS_INT_DISABLE()                    CpuTimer2.RegsAddr->TCR.bit.TIE; CpuTimer2.RegsAddr->TCR.all = ((CpuTimer2.RegsAddr->TCR.all & (~0x4000)) & (~0x8000)); //This variant of interrupt disable should be called when only the millisecond interrupt must be blocked
    
    #define  MS_INT_ENABLE()                  	CpuTimer2.RegsAddr->TCR.all = 0x4001;
    #define  MS_INT_DISABLE()                   CpuTimer2.RegsAddr->TCR.all = 0x0001;
    
    
    // Prototype statements for functions found within this file.
    interrupt void cpu_timer1_isr(void);
    interrupt void cpu_timer2_isr(void);
    
    void ConfigMicrosecondTimer(struct CPUTIMER_VARS *Timer, unsigned long Freq);
    
    
    unsigned long lastTime;
    unsigned long maxDifference = 0;
    
    
    void main(void)
    { 
    
    // Step 1. Initialize System Control:
    // PLL, WatchDog, enable Peripheral Clocks
    // This example function is found in the DSP2834x_SysCtrl.c file.
       InitSysCtrl();
    
    // Step 2. Initalize GPIO:
    // This example function is found in the DSP2834x_Gpio.c file and
    // illustrates how to set the GPIO to it's default state.
    // InitGpio();  // Skipped for this example
    
    
    // Step 3. Clear all interrupts and initialize PIE vector table:
    // Disable CPU interrupts
       DINT;
    
    // Initialize the PIE control registers to their default state.
    // The default state is all PIE interrupts disabled and flags
    // are cleared.
    // This function is found in the DSP2834x_PieCtrl.c file.
       InitPieCtrl();
    
    // Disable CPU interrupts and clear all CPU interrupt flags:
       IER = 0x0000;
       IFR = 0x0000;
    
    // Initialize the PIE vector table with pointers to the shell Interrupt
    // Service Routines (ISR).
    // This will populate the entire table, even if the interrupt
    // is not used in this example.  This is useful for debug purposes.
    // The shell ISR routines are found in DSP2834x_DefaultIsr.c.
    // This function is found in DSP2834x_PieVect.c.
       InitPieVectTable();
    
    // Interrupts that are used in this example are re-mapped to
    // ISR functions found within this file.
       EALLOW;  // This is needed to write to EALLOW protected registers
       PieVectTable.TINT2 = &cpu_timer2_isr;
       EDIS;    // This is needed to disable write to EALLOW protected registers
    
    // Step 4. Initialize the Device Peripheral. This function can be
    //         found in DSP2834x_CpuTimers.c
       InitCpuTimers();   // For this example, only initialize the Cpu Timers
    
    
       // Configure CPU-Timer 2 to interrupt every ms (1kHz)
       ConfigCpuTimer(&CpuTimer2, 300, 1000L);   // XXXMHz CPU Freq, 1 milli second Period (in uSeconds)
       // Configure CPU-Timer 1 as microsecond timer
       ConfigMicrosecondTimer(&CpuTimer1, 300);
    
    
    // To ensure precise timing, use write-only instructions to write to the entire register. Therefore, if any
    // of the configuration bits are changed in ConfigCpuTimer and InitCpuTimers (in DSP2834x_CpuTimers.h), the
    // below settings must also be updated.
    
       CpuTimer1Regs.TCR.all = 0x4001; // Use write-only instruction to set TSS bit = 0
       CpuTimer2Regs.TCR.all = 0x4001; // Use write-only instruction to set TSS bit = 0
    
       lastTime = -(ReadCpuTimer1Counter());
    
    // Step 5. User specific code, enable interrupts:
    
    
    // Enable CPU int1 which is connected to CPU-Timer 0, CPU int13
    // which is connected to CPU-Timer 1, and CPU int 14, which is connected
    // to CPU-Timer 2:
       //IER |= M_INT1;
       //IER |= M_INT13;
       IER |= M_INT14;
    
    // Enable TINT0 in the PIE: Group 1 interrupt 7
       PieCtrlRegs.PIEIER1.bit.INTx7 = 1;
    
    // Enable global Interrupts and higher priority real-time debug events:
       EINT;   // Enable Global interrupt INTM
       ERTM;   // Enable Global realtime interrupt DBGM
    
    // Step 6. IDLE loop. Just sit and loop forever (optional):
       for(;;)
       {
    	   MS_INT_DISABLE();
           DELAY_US(1);
           MS_INT_ENABLE();
           DELAY_US(950);
       }
    }
    
    interrupt void cpu_timer2_isr(void)
    {
    	unsigned long newTime, difference;
    	EALLOW;
    	CpuTimer2.InterruptCount++;
    	// The CPU acknowledges the interrupt.
    	EDIS;
    
    	newTime = -(ReadCpuTimer1Counter());
    
    	difference = newTime - lastTime;
    
    	if(difference > maxDifference)
    		maxDifference = difference;
    
    	lastTime = newTime;
    
    }
    
    void ConfigMicrosecondTimer(struct CPUTIMER_VARS *Timer, unsigned long Freq)
    {
        // Initialize timer period:
    	// Counter decrements once every TPR+1 System Clock Cycles
        //Period = Period - 1;
        Timer->CPUFreqInMHz = (float) 1.0*Freq;
        Timer->PeriodInUSec = 0xFFFFFFFF;
    
        Timer->RegsAddr->PRD.all = 0xFFFFFFFF;
        Timer->RegsAddr->TPR.bit.TDDR = (Freq-1) & 0xFF;
        Timer->RegsAddr->TPRH.bit.TDDRH = ((Freq-1) >> 8) & 0xFF;
    
        // Initialize timer control register:
        Timer->RegsAddr->TCR.bit.TSS = 1;      // 1 = Stop timer, 0 = Start/Restart Timer
        Timer->RegsAddr->TCR.bit.TRB = 1;      // 1 = reload timer
        Timer->RegsAddr->TCR.bit.SOFT = 1;
        Timer->RegsAddr->TCR.bit.FREE = 1;     // Timer Free Run
        Timer->RegsAddr->TCR.bit.TIE = 0;      // 0 = Disable/ 1 = Enable Timer Interrupt
    
        // Reset interrupt counter:
        Timer->InterruptCount = 0;
    }
    
    //===========================================================================
    // No more.
    //===========================================================================
    

  • Daniel,

    You can disable timer 2 interrupt with "CpuTimer2Regs.TCR.bit.TIE = 0x0;", and likewise enable timer 2 interrupt with "CpuTimer2Regs.TCR.bit.TIE = 0x1;", or by using the .all version and set to not affect the other bits.  Also, note that TINT2 is directly and exclusively connected to core interrupt line 14.  So, you could also disable this interrupt line with "IER &= 0xDFFF;" and enable with "IER |= 0x2000;".

    Additionally, I have attached a comparison report of your "Example_2834xCpuTimer.c" file to the original "Example_2834xCpuTimer.c" file in controlSUITE.  I am not sure if this is relevant or not, but in your "cpu_timer2_isr" ISR you did not acknowledges the interrupt with "PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;", and if you are using Timer1 you have the IER commented out: "//IER |= M_INT13;".

    /cfs-file/__key/communityserver-discussions-components-files/171/CompareReport.pdf

    I hope this helps. If this answers your question, please click the green "Verified Answer" button. Thanks.

    - Ken

  • Hi Ken,

    Thanks for your reply.

    The changes I made to the example program were by purpose, to reproduce the problem I have with the timer interrupt.

    If you try the program, you will see that the timer interrupt is sometimes lost for 1ms. It seems that writing to the TCR register either clears the interrupt flag, even if TIF is written with a 0. Or the interrupt does not trigger for some reason, if the TIE is 0 at the time when the timer overflows.

    This I wanted to prove with my test program and ask for a solution.

    On the other hand, I do not dare to modify the IER register at various places in the code, because I fear in interferes with the Ti Software Priorization we implemented according to this page. You do not see this code in my example, but we use it in our real application.

    Cpu timer 2 is in group 14, so PIEACK_GROUP1 won't help. The comment in the example says, that the CPU will acknowledge the interrrupt of timer 2.

    Regards,
    Daniel

  • Daniel,

    Here is a forum post that walks through a similar a use case as yours. e2e.ti.com/.../357624

    Do remember that enable bits do NOT affect interrupt flag bits. The flags will stay pending. You can also reference the sw_prioritized_interrupts example in controlSUITE on how prioritization using software works.

    Regards,
    Ozino
  • Hello Ozino

    Thanks for your reply.
    The post you pointed at goes in the same direction. But the person there does not use the .TIE bit, at least he did not before the end of the thread. Therefore we don't know the outcome.

    However, I attached a test program in a previous post which reproduces the problem. It can be run on any 28346 platform. 

    It would be nice if somebody at Ti would run it and reproduce the problem. Then we can see if there is an error in my code or in the H/W.

    Thanks and regards,
    Daniel

  • Hi Daniel,

    I went through the code you attached, and have a question:

    How do you guarantee synchronization of Interrupts enable / disable with the Cpu Timer1 & Timer2? Timers are still running when you enable/disable the interrupts in the "for" loop, so when you enable the interrupt that does no guarantee the timers starting at 0. Maybe I am missing some detail.

    Regards,
    Nirav
  • Hi Nirav

    There is no synchronisation, that is not the point of the problem.

    Timer 1 is only used to be able to measure the time in microseconds.

    Timer 2 is running free and should generate an interrupt every millisecond.

    The for loop simulates our "main" thread, in which we have some sections of code that needs to be protected from the interrupt service routine of timer 2.
    Now sometimes it happens, that timer 2 interrupt is not triggered, so it takes a full 2 milliseconds between two timer 2 interrupts, which can be seen in the variable maxDifference.

    So the essence of the problem is: 
    When I disable TIE and enable it again, I would expect that the interrupt call is not lost, but only delayed. But this is not the case, the interrupt call is sometimes lost. That is what the example program demonstrates.

    Regards,
    Daniel

  • Hi Daniel,

    Yes, I understood the problem. But, I dont see any data that concludes that "the interrupt call is sometimes lost".

    I think that enabling and disabling of the interrupt is not synchronous to timer counts, and hence you are missing the window of interrupt generation and hence the delay is longer than you expect. This is what I think is happening.

    When you enable the timer by using bellow configuration, timer starts counting

     CpuTimer2Regs.TCR.all = 0x4001; // Use write-only instruction to set TSS bit = 0 

    now the counter is running and then you enter into your main "for loop": 

    // Step 6. IDLE loop. Just sit and loop forever (optional):
    for(;;)
    {

    1st loop


    MS_INT_DISABLE();  -- assume timer2count=20
    DELAY_US(1);   -- after this delay the timer2 count is 22 
    MS_INT_ENABLE(); -- now you enable the interrupt, and the timer2 is still running, after executing this lets assume timer2count is 30
    DELAY_US(950); -- after 950us of delay the timer2count is 990, still no interrupt generation

    2nd loop

    MS_INT_DISABLE(); -- now the interrupt is disabled while the timer2count is still around 999
    DELAY_US(1);  -- what if the timer2count expires, and gets reloaded, but the interrupt generation is disabled, so does not go to the isr
    MS_INT_ENABLE(); -- next cycle of timer2count is already started
    DELAY_US(950);  -- at the end of this cycle timer 2 count is at 960

    3rd loop

    MS_INT_DISABLE(); -- timer2count will be around 970
    DELAY_US(1);  -- timer2count will be around 980
    MS_INT_ENABLE(); -- timer2count will be around 990
    DELAY_US(950);  -- timer2count expires and generates an interrupt, but the timer1counter has counted 2000 us by now

    }

    Again I am just using assumption, because I do not know the timer2counter values, what you can do is in your code also read timer2count values before and after enabling and disabling of the interrupt.

    It will be good if you can log the timer2counter values inside the for loop.

    Regards,

    Nirav

  • Hi Nirav

    Thanks for your reply.

    Okay, I see your explanation, and it shows where I got a different understanding of how TIE and TIF are supposed to work.

    My understanding was, the TIF flag is also supposed to be set, if the timer overflows while the TIE flag is disabled. Is this not the case? 

    Then furthermore my expection was, that if the TIF flag was set while the TIE flag was 0, and afterwards TIE is set again, this will trigger the ISR of cpu timer2.

    Is this not correct? I write in the comments below how I thought the bits would behave.

    Best regards,

    Daniel

    Nirav Ginwala said:

    // Step 6. IDLE loop. Just sit and loop forever (optional):
    for(;;)
    {

    1st loop

    ++ TIE = 1, TIF = 0
    MS_INT_DISABLE();  -- assume timer2count=20
    ++ TIE = 0, TIF = 0

    DELAY_US(1);   -- after this delay the timer2 count is 22 
    ++ TIE = 0, TIF = 0
    MS_INT_ENABLE(); -- now you enable the interrupt, and the timer2 is still running, after executing this lets assume timer2count is 30
    ++ TIE = 1, TIF = 0
    DELAY_US(950); -- after 950us of delay the timer2count is 990, still no interrupt generation

    2nd loop

    MS_INT_DISABLE(); -- now the interrupt is disabled while the timer2count is still around 999
    ++ TIE = 0, TIF = 0
    DELAY_US(1);  -- what if the timer2count expires, and gets reloaded, but the interrupt generation is disabled, so does not go to the isr
    ++ TIE = 0, TIF = 1
    MS_INT_ENABLE(); -- next cycle of timer2count is already started
    ++ TIE = 1, TIF = 1, ISR is expected to be called, but is not
    DELAY_US(950);  -- at the end of this cycle timer 2 count is at 960

    3rd loop

    MS_INT_DISABLE(); -- timer2count will be around 970
    DELAY_US(1);  -- timer2count will be around 980
    MS_INT_ENABLE(); -- timer2count will be around 990
    DELAY_US(950);  -- timer2count expires and generates an interrupt, but the timer1counter has counted 2000 us by now

    }

  • Hi Daniel,

    TIF is just an overflow status flag, it does not trigger an interrupt. Once you enable TIE=1, and when the timer expires/overflows that is when interrupt gets generated.

    Hopefully this solves your problem. Let me know if you have any more questions. If not please close this thread.

    Thanks,

    Nirav

  • Hi Nirav

    Okay, that is interesting. In the manual this bit is called CPU-Timer interrupt flag, so I was guessing that this really IS the interrupt flag to the CPU.
    This means I have to disable/enable the CPU Timer 2 interrupt directly in the IER register.


    Thanks and regards,
    Daniel