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.

CC2541: Time 1 interrupt use in BLE stack

Part Number: CC2541
Other Parts Discussed in Thread: CC2540,

Hi,

I am using CC2540/41 Bluetooth Low Energy Software Development Kit Version 1.4.0 for a current project of mine. As I use IAR EW 8.20, I cant use any newer stacks available from TI

I want to use Timer 1 in my project. It seem hal_timer functionality is disabled in the stack and it's up to us to implement. So I tried to enable Timer 1 and enable its interrupt. I can see the timer1 count is updating but no interrupts. I set the IEN0.EA and IEN1.T1IE bits initially but when I stop the program later, both these bits are reset. I tried finding who resets these but I couldn't find any except may be OSAL sleep functions. Can anyone shed some light here as to whom may be disabling the EA and T1IE bits? Basically how to integrate hal timer interrupts with OSAL.

Thanks,

Kay

  • Hi Kay,
    You should be able to use Timer 1. Have you review the relevant chapters in the users guide;

    www.ti.com/.../swru191

    Also, this thread might help you;

    e2e.ti.com/.../262471
  • Hi Joakim,

    Many thanks for the reply mate. I did go through the Timer 1 section in the user guide. What I did was to copy parts of the hal_timer.c to implement the bare minimum. Following is my hal_timer.c. I have defined the constants in hal_timer.h

    unsigned int timeout = 0;

    void HalTimerInit (void)
    {
    uint16 count;

    T1CCTL0 = 0; /* Make sure interrupts are disabled */
    T1CCTL1 = 0; /* Make sure interrupts are disabled */
    T1CCTL2 = 0; /* Make sure interrupts are disabled */

    /* Setup prescale & clock for timer1 */
    T1CTL &= ~(HAL_TIMER1_16_TC_DIV_BITS); // clear devisor bits 3..2
    T1CTL |= HAL_TIMER1_16_PRESCALE; // 11: Tick frequency / 128 - yields 32MHz/128 = 250kHz
    T1CTL &= ~(HAL_TIMER1_OPMODE_BITS); // clear OPMODE bits 1..0
    T1CTL |= HAL_TIMER1_OPMODE_STOP; //timer stoped for now

    count = (uint16)((100 * HAL_TIMER_32MHZ) / HAL_TIMER1_16_PRESCALE_VAL); // for 100uS timer interrupt
    T1CC0H = (uint8) (count >> 8);
    T1CC0L = (uint8) count;

    //T1CCTL0 |= 0x40 ;
    TIMIF |= 0x40 ;
    IEN1 |= 0x02;
    IEN0 |= 0x80;
    }

    void HalTimerStart ()
    {
    T1CTL |= HAL_TIMER1_OPMODE_MODULO; //timer start in modulo mode for now
    }

    HAL_ISR_FUNCTION(halT1_TimerIsr, T1_VECTOR)
    {
    HAL_ENTER_ISR();

    LED1_SBIT = 1;

    if (timeout) timeout--;

    HAL_EXIT_ISR();

    return;
    }

    At the end of 'HalTimerInit ', I can see the T1IE and EN bits set as planned but once rest of the program executes, someone seems to reset these bits. If these are rest, I guess the Timer 1 interrupt wouldn't happen as mentioned in Section 9.10.

    Cheers,
    Kay
  • Also, we have a software package "CC2541/43/44/45 Peripherals Software Examples", including Timer 1 example, that can be found here: www.ti.com/.../toolssoftware
  • When do you run HalTimerInit and HalTimerStart?
  • In 'SimpleBLEPeripheral_Main.c' as follows.

    int main(void)
    {
    /* Initialize hardware */
    HAL_BOARD_INIT();

    // Initialize board I/O
    InitBoard( OB_COLD );

    /* Initialze the HAL driver */
    HalDriverInit();



    /* Initialize NV system */
    osal_snv_init();

    /* Initialize LL */

    /* Initialize the operating system */
    osal_init_system();

    /* Enable interrupts */
    HAL_ENABLE_INTERRUPTS();

    // Final board initialization
    InitBoard( OB_READY );

    #if defined ( POWER_SAVING )
    osal_pwrmgr_device( PWRMGR_BATTERY );
    #endif

    HalTimerInit (); // KS
    HalTimerStart ();

    /* Start OSAL */
    osal_start_system(); // No Return from here

    return 0;
    }
  • I think the OSAL turns off interrupts once in a while. This fact also highlighted in e2e.ti.com/.../1395427.
    Is there any way to implement real time interrupt handling with OSAL?
  • I managed to keep the two bits set by removing the compile time def 'POWER_SAVING' and building the project again. Now I see the T1CNTL increasing and wrapping around the value set in T1CC0 registers. But OVFIF in T1STAT doesn't get set!!
    What could be wrong here?
  • Hi,

    Correct, power saving must be disabled while running the timer. I suppose the ISR is never called then?

    The users guide states the following for modulo mode;

    If the timer is started with a value above T1CC0, the interrupt flag T1STAT.OVFIF is set when the terminal count value (0xFFFF) is

    reached, after which the counter wraps to 0x0000. An interrupt request is generated if enabled.

    So I suppose you observe the wrapping without the bit being set?

  • Hi, Thanks again. Yes I figured out that Modulo mode doesn't set the OVF flag when wrapping around. So now I run it in Up Down mode and I get the interrupt. Now the problem is to clear the interrupt flags. From Figure 2-4. Interrupt Overview, I can see we have to clear two interrupt flags.
    1. T1STAT.OVFIF
    2. IRCON.T1IF
    Please correct me if I am wrong.

    In the emulator, I can see both these bits are set once I am in the ISR. But I cant get them cleared!!

    User guide does not mention how to clear OVFIF. It is marked as 'W0', so I reckon whatever you write in its place, t will get reset?

    IRCON register description says 'Timer 1 interrupt flag. Set to 1 when Timer 1 interrupt occurs and cleared when CPU vectors to the interrupt service routine.' As I understand it, once we enter the ISR, this bit must be auto cleared. Doesn't seem so.

    My ISR is like this.
    HAL_ISR_FUNCTION(halT1_TimerIsr, T1_VECTOR)
    {
    HAL_ENTER_ISR();

    //LED1_SBIT = 1;

    if (LEDtimeout) LEDtimeout--;

    T1STAT &= ~BV(5);

    //T1IF = 0;
    IRCON &= ~BV(1);

    HAL_EXIT_ISR();

    return;
    }
  • Hi,
    IRCON is cleared by hardware. You need to manually clear the T1STAT interrupt flag.
  • Thanks again Joakim. Unfortunately it doesn't seem to clear, neither of them. Any ideas?
  • I managed to solve it by stopping the timer first and then clearing the OVFIF and then start the timer again. Dont know if correct way to do it. Following is the code

    HAL_ISR_FUNCTION(halT1_TimerIsr, T1_VECTOR)
    {
    HAL_ENTER_ISR();

    if (LEDtimeout) LEDtimeout--;

    T1CTL &= ~(HAL_TIMER1_OPMODE_BITS); // clear OPMODE bits 1..0
    T1CTL |= HAL_TIMER1_OPMODE_STOP; //timer stopped to clear OVFIF

    T1STAT &= ~T1CTL_OVFIF; // clear the OVFIF - doesnt seem to be cleared when timer is running
    T1CNTL=0; // set timer back to 0
    T1CTL |= HAL_TIMER1_OPMODE_UPDOWN; // start timer again

    HAL_EXIT_ISR();

    return;
    }
  • Hi,

    You should not have to start the timer in the interrupt as far as I understand. It might be the way you setup the timer I suppose. Below is from the example (t1_updown.c in the sample pack) I was referring to earlier. I suggest you verify the settings;

    /***************************************************************************
         * Setup interrupt
         */
    
        // Clear Timer 1 channel 0 and channel 2 interrupt flag
        // CPU interrupt flag (IRCON) for Timer 1 is cleared automatically by hardware.
        T1STAT = ~(T1STAT_CH2IF | T1STAT_CH0IF);
    
        // Set individual interrupt enable bit in the peripherals SFR
        T1CCTL2 |= T1CCTLn_IM;      // Enable interrupt on channel 2
        T1CCTL0 |= T1CCTLn_IM;      // Enable interrupt on channel 0
        T1CCTL1 &= ~T1CCTLn_IM;     // Disable interrupt on channel 1
        T1CCTL3 &= ~T1CCTLn_IM;     // Disable interrupt on channel 3
        T1CCTL4 &= ~T1CCTLn_IM;     // Disable interrupt on channel 4
        T1OVFIM = 0;                // Disable overflow interrupt
    
        // Enable Timer 1 interrupts by setting [IEN1.T1IE=1]
        T1IE = 1;
    
        // Enable global interrupt by setting the [IEN0.EA=1]
        EA = 1;
    
      
        /***************************************************************************
         * Setup Timer settings
         *
         * Here we will select which channel(s) that will be used. We can choose to
         * use them in compare mode or capture mode.
         *
         * We can also select what mode the Timer shall operate on. When the mode is
         * selected, the Timer will start to run. Please see the datasheet for more
         * information.
         *
         * Notes:
         * - T1CCO is used by channel 1 & 2 for some compare modes, in case channels
         *   are used simultaneously.
         * - In compare mode using modulo mode or up-down mode, channel 0 will
         *   generate spike signals when [T1CCTL0.CMP = 3 or 4] since T1CC0 will then
         *   be both the compare value and the overflow value.
         * - The input signal (pulse), when in capture mode, must have a duration
         *   longer than the system clock
         */
    
        // Set channel 2 to compare mode and to toggle on compare.
        T1CCTL2 = (T1CCTL2 & ~T1CCTLn_CMP) | T1CCTLn_MODE | T1CCTLn_CMP_TOG_ON_CMP;
    
        // Channel 0 must also be set to compare mode to get interrupts from channel 0.
        T1CCTL0 |= T1CCTLn_MODE;
    
        // Set compare register of channel 2 to 16383 ( 0xFFFF / 4 )
        T1CC2L = 0xFF;
        T1CC2H = 0x3F;
      
        // Set compare register of channel 0 to 32767 ( 0xFFFF / 2 )
        T1CC0L = 0xFF;
        T1CC0H = 0x7F;
    
        // Set prescalar divider value to 128 to get a tickspeed of 125 kHz and
        // set Timer 1 to up/down mode
        T1CTL = (T1CTL & ~(T1CTL_MODE | T1CTL_DIV)) | T1CTL_MODE_UPDOWN | T1CTL_DIV_128;
    
        // Timer will now start counting.
        while(1);
    }