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.

External timer with higher frequency (MHz)

Expert 1900 points

 

I am trying to use TACLK (external timer frequency). Up to now I am succesfull to obtain frequency

up to about 10kHz. Howver, when I try to obtain the waves with more than 20kHz frequency,

the reading of number of waves starts to fail (becomes smaller numbers e.g., 18328).

 

Following is my code. Could someone suggest some way to improve for the higher frequency

if that exists?

------------------------------------------------

 

#define CCR_MSEC 1049  // for every 1 msec count

#include <msp430x471x7.h> // F471x7

#define LED_OFF P5OUT &= ~0x02; // F471x7 D1 LED (P5.1)
#define LED_ON P5OUT |= 0x02;
#define LED_TOGGLE P5OUT ^= 0x02;

unsigned long list[11];
int g_idx = 0;
unsigned long g_msecCount, g_event, g_secCount;

void main(void)
{
   
    WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
    FLL_CTL0 |= XCAP14PF;                     // Configure load caps    

    P1SEL |= 0x20; // P1.5 (TACLK)      
       
    // External Freq. input
    TACCTL0 = CCIE; // for TIMERA0_VECTOR
    TACCR0 = 1;    //STB
    TACTL = TASSEL_0 + MC_1;     // TACLK (external freq.), Up mode
   
    // Internal Real Time Clock
    TBCCTL0 = CCIE; // for TIMERA0_VECTOR
    TBCCTL1 = CCIE;
    TBCCR0 = 65536 - 1;
    TBCCR1 = CCR_MSEC - 1;  // 1048.5
    TBCTL = TBSSEL_2 + MC_2 + TBIE;   
    //  ACLK = LFXT1 = 32768Hz, MCLK = SMCLK = default DCO = 32 x ACLK = 1048576Hz
   
    g_secCount = 0;
    g_msecCount = 0;
    g_event = 0;

    __bis_SR_register(LPM0_bits + GIE);  

}

#pragma vector=TIMERA0_VECTOR
__interrupt void Timer_A (void)
{
   
    g_event++;
   
}

#pragma vector=TIMERB1_VECTOR
__interrupt void Timer_B1(void)
{   
       
    switch( __even_in_range(TBIV, 10))
    {  
    case  2:                          // CCR1 1msec       
        TBCCR1 += CCR_MSEC - 1;
        TBCCR1 %= (65535 - 1);
        g_msecCount++;
        break;          
    case 10:                                 // overflow
        break;
    }
}

#pragma vector=TIMERB0_VECTOR
__interrupt void Timer_B0(void)
{
    static long sqNo = 1;
    static long preMsec;
    long diffMsec;

    if(sqNo == 17) {
        diffMsec = g_msecCount - preMsec;
        g_secCount++;       // every 1 sec
        LED_TOGGLE;
        sqNo = 1;
       
        preMsec = g_msecCount;
        list[g_idx++] = g_event * 2;
        g_event = 0;

        if(g_idx > 10) {
            _NOP();
            return;
        }
    }  

    sqNo++;
       
}

 

  • Hi,

    If I understand your code logic correctly, you increment g_event by 1 when your Timer_A counts to 1. This means for 2 clock edges of TACLK you increment g_event by 1. You use Timer_B to create a 1 second timer. At the end of 1 second you multiply g_event by 2 to directly get the frequency.

    A possible issue with your code could be that the TimerA interrupt you receive are a bit too quick for the CPU. For a 20KHz clock, you should receive the interrupt every 100us (50us period X 2). When you are running the CPU at almost 1MHz(slightly more than that), this means you can execute less than 100 instructions before the next interrupt occurs. I am saying less than 100 instructions because all MSP430 instructions are not single cycle. Plus you are in LPM0, and it will take a few microseconds before the CPU starts running at 1MHz. Please note that you are using "long" variables arithmetic, which may take up quite a few CPU cycles.

    One possible problem could be the TIMERB1_VECTOR. You are doing a "long" addition and then an "int" division to perform the "%" operation. If you are in that ISR and do not complete it within 100us then you will miss an interrupt and two events. Can you check how much cycles does your TIMERB1_VECTOR ISR take ? If it is more than 100 or so then you have a problem.

    To solve this problem, you can increase the value of TACCR0 so that TIMERA0_VECTOR interrupts occur after longer durations. For example we can set TACCR0 to 99 so that the interruption interval becomes 50us X 100 = 5millisecond.  At the end of 1 second we take the value of g_event, which increments by 1 on 100 events and the current value of Timer_A from TAR register. Next we clear both g_event & TAR register.

    See if this code helps:

    TACCR0 = 99;    // Counter counts up to 99...


    #pragma vector=TIMERA0_VECTOR
    __interrupt void Timer_A (void)
    {
      
        g_event++;    // Now g_event++ means 100 clock cycles...
    }

    #pragma vector=TIMERB0_VECTOR
    __interrupt void Timer_B0(void)
    {
        static long sqNo = 1;
        static long preMsec;
        long diffMsec;

        if(sqNo == 17) {
            diffMsec = g_msecCount - preMsec;
            g_secCount++;       // every 1 sec
            LED_TOGGLE;
            sqNo = 1;
          
            preMsec = g_msecCount;
           
        list[g_idx++] = g_event * 100 + TAR;    // Apart from g_event we also take current Timer_A value from TAR register...

        TAR = 0;    // Clear the counter...

       g_event = 0; // Clear the events as well...

            if(g_idx > 10) {
                _NOP();
                return;
            }
        } 

        sqNo++;
          
    }

     

    You will notice that you can increase the value of TACCR0 even further, to say 999 so that you get an event every 50millisecond. If you increase it sufficiently you will actually not even need g_event for frequencies up to 60Khz or so. If you set TACCR0 to 65000 then you will have to provide 65Khz signal for the counter to reach 65000 in 1 second.

    Regards,

    Praval

  • Thank you very much, Praval.

     

    I could obtain 500kHz clock by incorporating your suggestion. It helped much!

    For 1MHz frequency, it seems difficult to obtain correctly.

     

    Anyway, for my current purpose, I could solve the problem with your help.

     

    Best regards

     

  • Hi,

    Glad I could help.

    I think you can reach 1MHz if you increased the value of TACCR0. If you set TACCR0 to 65000 you will get an interrupt every 65ms (1us period X 65000). Every 1 second interval you could do g_event X 65000 + TAR. Your TIMERB1_VECTOR is an interrupt which occurs after every 1ms and I may be wrong, but due to the "%" operation it will take up quite a lot of time.  You should make sure that your TIMERA0_VECTOR interrupt rate is much lesser as compared the 1ms rate of TIMERB1_VECTOR.  So as you increase the TACLK frequency increase the value of TACCR0 as well.

    Regards,

    Praval

  • Dear Praval

     

    Thank you very much for your further assistance.

    I changed TACCR0 to 65000. Then, the counter was improved. However, still the "1MHz clock pick up" seems

    difficult.

    The code can recognize 500kHz as 500,033Hz (0.0066% error). For the 1MHz, the code recognizes as  

    around 996,300Hz.

     

    Following is the code incorporating your suggestion. Besides, I changed to use WDT for 1sec count for simple code.

     

    Anyway, thank you again for your reply.

    ---------------------------
    #include <msp430x471x7.h> // F471x7

    unsigned long list[20];
    int g_idx = 0;
    unsigned long g_event;

    void main(void)
    {
       
        WDTCTL = WDT_ADLY_1000; 
        IE1 |= WDTIE;                             // Enable WDT interrupt
       
        FLL_CTL0 |= XCAP14PF;                     // Configure load caps
       
        P1SEL |= 0x20; // P1.5 (TACLK) pin86 
       
        // External Freq. input
        TACCTL0 = CCIE; // for TIMERA0_VECTOR
        TACCR0 = 65000 - 1;    // Counter counts up to    
        TACTL = TASSEL_0 + MC_1;     // TACLK (external freq.), Up mode
       
        g_event = 0;
       
        __bis_SR_register(LPM0_bits + GIE);       // Enter LPM3 w/ interrupt       
       
    }

    #pragma vector=TIMERA0_VECTOR
    __interrupt void Timer_A (void)
    {  
        g_event++;    // Now g_event++ means 65000 clock cycles...
    }


    #pragma vector = WDT_VECTOR
    __interrupt void watchdog_timer(void)
    {
       
        list[g_idx++] = g_event * 65000 + TAR;    // Apart from g_event we also take current Timer_A value from TAR register...
       
        TAR = 0;    // Clear the counter...       
        g_event = 0; // Clear the events as well...
       
        if(g_idx > 10) {
            _NOP();
            return;
        }
       
    }

     

**Attention** This is a public forum