Stellaris timer example frequency counter

Stellaris Timer Example

In this Stellaris timer example two timers are used to code a basic frequency counter.

The code process is quite simple to implement, and as mentioned previously with other stellaris examples I would recommend using the StellarisWare Peripheral Driver Library PDF as a reference, which can be found here.

The first timer is configured to count the leading edge of the incoming square wave, the second timer is used to count for a fixed time and then generate an interrupt. Then a simple calculation is performed using the second fixed timer value, and the accumulated leading edges counted. The positive going edge count is then stored, and also reset every time the second timers interrupt is generated.  The image below outlines the basic operation of the example that will be shown.

Stellaris Timer Example Code frequency counter LM3S6965

 

As can be seen from the image timer 2 was chosen to generate an interrupt every 100mS, this was deemed as an adequate time to refresh the count, and can easily be increased or decreased pending on the application.  The stellaris LM3S clock frequency is running at 8MHz. Each clock pulse period can be calculated by taking the reciprocal of 8MHz (1/8MHz) which is 125nS, then dividing 100mS by 125nS we reach a value of 800,000.  The 800,000 is not used directly in this code, as the clock is simply divided by a constant, but this illustrates how the value was reached.

A signal generator was used to feed a square wave into the stellaris, this was then varied and the results of the stellaris code displayed on the on-board OLED.  A video of the frequency counter in action can be seen below.

So now lets dive into the code.  As mentioned before the system clock was running at 8MHz, using the on-board crystal, the code snippet below is used to configure the clock and also some OLED initialisation.

Then both of the timer peripherals need to be enabled.

In the code example Timer0 is used to generate an interrupt every 100mS, and Timer1 is used to measure the leading edge pulses from the signal generator.  Therefore a GPIO input needs to be setup and assigned to function with Timer1, the next section of code performs this.

An interrupt needs to be enabled and set-up for Timer0A.

Then the 2 timers need to be configured.  The first 2 lines of code set-up Timer0 as a full width periodic timer, and Timer1 as a half-width edge count capture.  Then Timer0 (TimerA) is loaded with the 100mS count period, and Timer1 is loaded with a value of 10000.  Timer1 is loaded with a value as it counts down for every positive going edge.  This timer example could effectively count upto 2MHz (a quarter of the 8MHz clock frequency), it was tested successfully with higher frequencies, to do so the value preloaded into Timer1 needs to be increased.  The final line in this snippet, sets Timer1 (TimerA) to trigger on positive going edges.

Finally the timers need to be enabled.

Now that the timers are configured and enabled the code for the interrupt handler for Timer0 can be configured.  This basically clears the interrupt, stores the value counted by Timer1 and then resets Timer1.

This is not necessarily the best way to do this, but it performed the task required of the test at the time.  As has been mentioned the code displays the frequency value on the OLED, to do this an itoa function is used to change an integer into a char and then print to the display, all this takes quite a few processor cycles, and in this way has been removed from delaying the interrupt handler.

The final code block shows the function used to calculate the timer frequency.  It could be improved upon using a modulus to display the frequency in kHz, and printing a decimal point in the appropriate place, this would allow displaying a greater range of frequencies with more ease.

The following image shows a screen capture from the YouTube video, which demonstrates the program works within a reasonable accuracy.

Stellaris Timer Example Code frequency counter LM3S6965

Example Code

The link below contains the zip file with the complete C code, there is a small advert page first via Adfly, which can be skipped and just takes a few seconds, but helps me to pay towards the hosting of the website.

Frequency Counter

I take great care when writing all the tutorials and articles, ensuring all the code is fully tested to avoid issues for my readers.  All this takes time and a great deal of work, so please support the site by using the Adfly links etc.  If you have found this useful or have any problems implementing, please feel free to leave a comment and I will do my best to help.

25 thoughts on “Stellaris Timer Example

  1. Mohamed

    Hello All !!
    not work !! 🙁 . what i need is read frecuencies from an function generator with MSP430G2553, I connect this generator in P1.0 Pin (Timer_A0) and I have code, but I can’t show nothing with the serial Port 🙁 . Help please.

    the code is :
    #include
    #define PWM_PIN P1_3
    #define PWM_FREQ 500 // the default
    #define PWM_RES (F_CPU / PWM_FREQ)
    int cont=0;
    int a=0;
    int n=1;
    Counter MyCounter; //Utilizo el contador de la patilla P2.0

    void setup() {
    /* set the PWM frequency */
    analogFrequency(PWM_FREQ);
    /* set the PWM resolution */
    analogResolution(PWM_RES);
    /* 490 Hz / 50% duty cycle on pin 11 */

    MyCounter.start(CL_Div2); // Cuenta cada 2 pulsos

    Serial.begin(9600);
    }

    void loop()
    {
    n=1;
    while(n<=1000){
    // put your main code here, to run repeatedly:
    //analogWrite(P1_3,8000);
    MyCounter.reset();
    delay(1000);
    Serial.print((MyCounter.read()+65536*27.7522));
    //32765.5 = 65535/2 **CL_Div2**
    //(MyCounter.read()+…) * 2 **CL_Div2**
    //32800 manual calibrate
    Serial.println(" Hz");
    // Serial.println(a-32600);
    delay(100);
    n=n++;
    }
    }

    I use ENERGIA.

    Thanks.

    Reply
  2. Mohamed

    If I need to Blink this code in TIVA C EK-TM4C123GXL, how can i do this ?
    I need a frencuancy counter.
    help please … some code for me ??

    Reply
    1. Ant Post author

      Hi Mohamed,

      The basic code format should work, there maybe some function calls for the Tiva C that differ. The TI forums and 430h.com is a very useful resource with some very knowledgeable users, also look for the peripheral Driver Library PDF for the Tiva C this will be a valuable resource.

      Regards,

      Ant

      Reply
  3. Jacek

    Hi,

    I’m a student from Germany and i’m not very good in programming microcontrolles. I try to do a Flow rate sensor working on my LM3S8962. The flow rate sensor is an YF-S201. It just changes his outputsignal from 0 to 4.7V (it isn’t any problem to decrease the voltage by a simple cirquit). It gives a max. The sensor outputs approximately 4.5 pulses per second per litre/minute of flow.

    Well now the question: Is it possible to combine this program with my flow rate sensor to count the signals higher than 0V and to count the time the water is flowing trough the Sensor? I wouldn’t ask if I had any idea of this stuff. If somebody has any idea I’d be happy as a child eating a chocolate cake.

    Thank you very much!

    Reply
    1. Ant Post author

      Hi,

      Ok from what I could see the YF-S201 outputs a pulse every 2.25 milliliters of water, the greater the water flow will mean more pulses per minute. So if you count the pulses in a given time, you can then calculate the flow rate.

      The basic frequency counter program should do this nicely, but you probably want to lengthen the sample period. Not sure how you want to present your data or the range of water flow you are measuring, but a possible solution could be this:

      Set the timer counter up to be 10 seconds and count how many pulses are in this period, then use the total pulse count as a multiplier for 2.25 milliliters and multiply that by 6 (6*10 seconds is a minute), you then get a flow rate per minute. You can try a 1 second timer as well and really depends on the amount of water flowing, this can all be converted to something like cubic metres per minute then.

      How this helps,

      Ant

      Reply
  4. Leo(quyencv)

    Dear all,

    I would like to ask you about my problem, what i am facing on frequency measurement. I hope somebody can help me clear my problem.

    I use TM4C.
    My code is based on you idea. I use bellow peripheral,
    1. Generate PWM on timer1 for PWM module.
    2. Timer2 for for timer each 1ms to caculate frequency.
    3. Timer0 for configuration count down rising edge.

    I have check on osiloscope the pwm and timer works fine. But only FREQUENCY COUNTER on timer0 does not work as i set up.
    Could you help me to have a check and share with me my problem i’m facing.
    Thank you very much.

    Reply
  5. Leo(quyencv)

    Dear Ant,

    Firstly, thanks for your article.
    Secondly, I would like to ask you about my problem, what i am facing on frequency measurement. I hope you can help me clear my problem.

    I use LM4F120XL.
    My code is based on you idea. I use bellow peripheral,
    1. Generate PWM on timer1 for PWM module.
    2. Timer2 for for timer each 1ms to caculate frequency.
    3. Timer0 for configuration count down rising edge.

    I have check on osiloscope the pwm and timer works fine. But only FREQUENCY COUNTER on timer0 does not work as i set up.
    Could you help me to have a check and share my problem in details.
    Thank you very much.
    ===============================================================
    #include “freq_measurement.h”

    void InitConsole(void){
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);

    GPIOPinConfigure(GPIO_PA0_U0RX);
    GPIOPinConfigure(GPIO_PA1_U0TX);

    GPIOPinTypeUART(GPIO_PORTA_BASE,GPIO_PIN_0 | GPIO_PIN_1);
    UARTStdioConfig(0, 115200, 16000000);
    }

    void init_pwm(void){
    SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER1);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);

    GPIOPinConfigure(GPIO_PB5_T1CCP1);
    GPIOPinTypeTimer(GPIO_PORTB_BASE,GPIO_PIN_5);

    TimerConfigure(TIMER1_BASE,TIMER_CFG_SPLIT_PAIR | TIMER_CFG_B_PWM);
    TimerLoadSet(TIMER1_BASE,TIMER_B,SysCtlClockGet()/2000);
    TimerMatchSet(TIMER1_BASE,TIMER_B,TimerLoadGet(TIMER1_BASE,TIMER_B)/2); // 66% duty.

    TimerEnable(TIMER1_BASE,TIMER_B);
    }

    void freq_measurement(void){
    SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
    GPIOPinTypeTimer(GPIO_PORTB_BASE,GPIO_PIN_6);
    GPIOPinConfigure(GPIO_PB6_T0CCP0);

    GPIOPadConfigSet(GPIO_PORTB_BASE,GPIO_PIN_6,GPIO_STRENGTH_2MA,GPIO_PIN_TYPE_STD_WPU);
    IntMasterEnable();

    TimerConfigure(TIMER0_BASE,TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_CAP_COUNT);
    TimerControlEvent(TIMER0_BASE,TIMER_A,TIMER_EVENT_POS_EDGE);
    TimerLoadSet(TIMER0_BASE,TIMER_A,99);
    TimerMatchSet(TIMER0_BASE,TIMER_A,0);

    TimerIntEnable(TIMER0_BASE,TIMER_CAPA_MATCH);
    IntEnable(INT_TIMER0A);

    TimerEnable(TIMER0_BASE,TIMER_A);
    }

    unsigned short count = 0;
    void
    Timer0IntHandler(void)
    {
    //
    // Clear the timer interrupt.
    //
    // TODO: Rework this for the timer you are using in your application.
    //
    TimerIntClear(TIMER0_BASE, TIMER_CAPA_MATCH);

    //
    // TODO: Do whatever your application needs to do when the relevant
    // number of edges have been counted.
    //
    //ProcessInterrupt();
    count++;
    TimerLoadSet(TIMER0_BASE,TIMER_A,99);
    UARTprintf(“[%s] count : %d, value : %d\n”,__FUNCTION__,count, TimerValueGet(TIMER0_BASE,TIMER_A));
    //
    // The timer is automatically stopped when it reaches the match value
    // so re-enable it here.
    //
    // TODO: Whether you reenable the timer here or elsewhere will be up to
    // your particular application.
    //
    TimerEnable(TIMER0_BASE, TIMER_A);
    // TimerDisable(TIMER0_BASE, TIMER_A);
    GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_1,GPIO_PIN_1);
    }

    void init_timer2(void){
    SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER2);
    TimerConfigure(TIMER2_BASE, TIMER_CFG_A_PERIODIC);
    TimerLoadSet(TIMER2_BASE, TIMER_A, SysCtlClockGet() / 1000);
    IntMasterEnable();
    TimerIntEnable(TIMER2_BASE, TIMER_TIMA_TIMEOUT);
    IntEnable(INT_TIMER2A);
    TimerEnable(TIMER2_BASE, TIMER_A);
    }

    int led_blinking = 1;
    _Bool check_freq = 0;
    int f_freq = 0;
    void Timer2AIntHandler(void){
    TimerIntClear(TIMER2_BASE,TIMER_TIMA_TIMEOUT);
    led_blinking++;
    check_freq = 1;
    f_freq = 1000*(count * 100 + 100 – TimerValueGet(TIMER0_BASE,TIMER_A));
    if(led_blinking<5){
    UARTprintf("[%s] Freq : %d, count : %d\n",__FUNCTION__,f_freq,count);
    }
    TimerLoadSet(TIMER2_BASE,TIMER_A,SysCtlClockGet()/1000);
    count = 0;
    TimerEnable(TIMER2_BASE,TIMER_B);
    }

    int main(void){
    unsigned int freq = 0;

    SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE,GPIO_PIN_1 |GPIO_PIN_2 | GPIO_PIN_3);
    GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_2, 1);
    InitConsole();

    init_pwm();
    init_timer2();
    freq_measurement();
    UARTprintf("setting up is finished!!!\n");

    while(1){
    if(led_blinking==3000){
    led_blinking = 0;
    //UARTprintf("[%s] Freq : %d, count : %d\n",__FUNCTION__,TimerValueGet(TIMER0_BASE,TIMER_A), count);
    GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_2, GPIOPinRead(GPIO_PORTF_BASE,GPIO_PIN_2)^GPIO_PIN_2);
    }

    }

    return 0;
    }
    ===============================================================
    #ifndef __FREQ_H__
    #define __FREQ_H__ 1

    #ifndef PART_TM4C1230H6PM
    #define PART_TM4C1230H6PM 1
    #endif

    #include
    #include
    #include “inc/hw_gpio.h”
    #include “inc/hw_ints.h”
    #include “inc/hw_memmap.h”
    #include “inc/hw_timer.h”
    #include “inc/hw_types.h”
    #include “driverlib/gpio.h”
    #include “driverlib/interrupt.h”
    #include “driverlib/pin_map.h”
    #include “driverlib/sysctl.h”
    #include “driverlib/timer.h”
    #include “driverlib/uart.h”
    #include “utils/uartstdio.h”

    #ifndef true
    #define true 1
    #endif

    #ifndef false
    #define false 0
    #endif

    #ifndef TRUE
    #define TRUE true
    #endif

    #ifndef FALSE
    #define FALSE false
    #endif

    #endif

    Reply
    1. Ant Post author

      Hi leo,

      Thanks for taking the time to look at the site and this post. I have not worked with the Stellaris for awhile and not had the opportunity to work with the Tiva C at all.

      I had a brief scan of the code but nothings jumps out as being an obvious problem. I would simplify it down, break it into steps:

      1) Find a Tiva C example where the timer is being used as a count down timer, then read through the code and look at the functions involved in this operation. Familiarise yourself with the code so you can happily change the parameters.

      2) Simplify your code to the basic operations and it maybe better to use an external frequency counter at first. Possibly even remove the UART code and view the variable that stores the frequency in CCS.

      3) Integrate the codes from steps 1 and 2 and once working then bring back the PWM generation.

      I know this is not an answer, I am little short on time at the moment due to exams and work commitments and intend to start posting back on the site more over this summer. I will be working on a project using a Tiva C towards the end of May, but I doubt this will be any help to you by that date.

      I would also highly recommend posting on the TI forums, there are experts on the Tiva C who will know the peripheral steps and function operations.

      Best regards,
      Ant

      Reply
      1. Leo(QuyenCV)

        Dear Ant,

        Thanks for your suggestion.
        I will check and do it as your recommendation. I hope it can be resolve soon :).
        because this is nice functionlity of Timer module :).

        Best regards,
        Quyen.

        Reply
    1. Ant Post author

      Hi,

      No sorry as I don’t have access to a LM3S8962 development board. You should be able to port it though, the function calls are highly likely the same as there are generic peripheral drivers for the whole Stellaris range. There is likely to be a different port and pin usage, but if you check the datasheet for your board it should provide this information.

      Regards,
      Ant

      Reply
        1. Ant Post author

          Hi,

          The code you posted is the interrupt handler, so you just paste what ever code you need to perform inside the interrupt handler.

          Regards,
          Ant

          Reply
  6. Thi

    Hi,

    I would like to ask you some questions. Based on your code:
    1. Why did you assign “float time = 0.1″? is that 100ms/1000ms conversion to second?
    2. Why did you set the Timer load value to 10000 but not another value.like 800000 maybe?
    3. How come ” count = (10000 – timer) * 100; // *100 converts kHz to Hz for easy displaying” really convert kHz to Hz (you only multiplied by 100 in this case)?
    4.How come the count in kHz?

    Thank you

    Reply
    1. Ant Post author

      Hi,

      In answer to your questions:

      1) This just divides the result by 10 but I chose to multiply by 0.1 which has the same effect. It is used to move the decimal place so the frequency is accurate, and as displayed on the function generator in the video. I didn’t bother using a decimal place in the code and calculating the modulus, as this was just a quick demonstration.
      2) A larger number could be used but this was fine for the demonstration and frequency range used, for higher frequencies you will need a larger count.
      3) See answer 1 and check the video for code operation with a function generator.
      4) As per answer 1, the function generator displays the frequency with a decimal place.

      Cheers,
      Ant

      Reply
      1. Ant Post author

        I have not included the itoa.h file as the function is not my own, it’s written by Lukás Chmela, searching for itoa and his name on Google. I must have forgot to include the write_pwm.h and write_pwm.c file so have copied these below, will add them to the downloadable zip file when I have time. The PWM function is not used in this example though, to see how the Stellaris PWM works see this tutorial http://coder-tronics.com/stellaris-lm3s6965-pwm-tutorial/

        ———- write_pwm.h from here ———-

        #ifndef WRITE_PWM_H_
        #define WRITE_PWM_H_
        void setup_PWM(void);
        void adjust_PWM( unsigned int duty);

        ———- write_pwm.c from here ———-

        #include "inc/hw_types.h"
        #include "driverlib/debug.h"
        #include "driverlib/sysctl.h"
        #include "driverlib/pwm.h"
        #include "inc/hw_memmap.h"
        #include "driverlib/pin_map.h"
        #include "driverlib/gpio.h"

        void setup_PWM(void)
        {
        SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM);
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
        GPIOPinTypePWM(GPIO_PORTF_BASE, GPIO_PIN_0);
        PWMGenConfigure(PWM_BASE, PWM_GEN_0,PWM_GEN_MODE_UP_DOWN | PWM_GEN_MODE_NO_SYNC);
        PWMGenPeriodSet(PWM_BASE, PWM_GEN_0, 333);
        PWMPulseWidthSet(PWM_BASE, PWM_OUT_0, 166);
        PWMGenEnable(PWM_BASE, PWM_GEN_0);
        PWMOutputState(PWM_BASE, PWM_OUT_0_BIT, 1);
        }
        void adjust_PWM(unsigned int duty)
        {PWMPulseWidthSet(PWM_BASE, PWM_OUT_0, duty);
        }

        Cheers,
        Ant

        Reply
    1. Ant Post author

      Hi,

      PinTypeTimer(); configures the specified timer pin to function as a timer pin. The parameters you can pass here are CCP1 to CCP7, depending on which GPIO pin and timer you are using.

      Regards,
      Ant

      Reply
  7. Bilcan

    Hello,

    I have a question for you. When you set the timer load (below i put the command)

    TimerLoadSet(TIMER0_BASE, TIMER_A, SysCtlClockGet()/10);//8000000/10=80000=100 milliseconds

    how is that 80000 load equals 100 ms 😀 how this works because i can’t find the link between the load set and the frequency of the interrupts. Please if you have time, leave a response to this or send me an email on bogdan.bilcan@outlook.com

    Reply
    1. Ant Post author

      Hey Bilcan,

      Ok it would help if I hadn’t missed a zero in my comments 🙂 should be 8MHz/10 = 800,000. If we now take the reciprocal of 8MHz we get 125nS, therefore 125nS*800,000 = 100mS. Have corrected my comments on the code!

      Cheers,

      Ant

      Reply
      1. Bilcan

        Hi again,

        Can you tell me how the SysCtlClockSet() function works and how to enter the parameters correctly?

        More what are the main steps in writing a program that uses timers or any other peripheral?

        Thank you.

        Reply
        1. Ant Post author

          Hey,

          No worries, I can run through the SysCtlClockSet() function. I will use two examples, one from the code in the Timer example SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN | SYSCTL_XTAL_8MHZ);, and a second showing a 50MHz clock.

          So the function only accepts 1 parameter which is an unsigned long, but you have multiple parameters here which are bitwise OR’ed together. The example function parameters have the following effect:
          SYSCTL_SYSDIV_1 This is the system clock divider, in this case it divides by 1.
          SYSCTL_USE_OSC | SYSCTL_OSC_MAIN Are used in combination with each other, this then configures the clock to be taken from an external source (8MHz crystal in this case).
          SYSCTL_XTAL_8MHZ This just selects the external crystal frequency, the maximum external crystal value is 25MHz.

          Now the second example can be used to show how to configure the clock to 50MHz.
          SysCtlClockSet( SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_8MHZ );
          The second example has 2 parameters which are different, but this changes the clock from 8MHz shown in the first example to 50MHz.
          SYSCTL_USE_PLL This then uses the PLL
          SYSCTL_SYSDIV_4 This then divides by 4, therefore when using the PLL (LM3S6965) it’s 200MHz divided by 4 hence 50MHz.

          With the Stellaris setting up the clocks and watchdog timer is first as with any microcontroller. Then enabling the peripheral modules like ADC or Timers, as by default they are off and then not using power or resources if not required. Then you set-up the peripherals and configure them as required, followed by enabling them.

          Hope this helps and if you have any further questions I will do my best to help.
          Ant

          Reply

Leave a Reply