Jump to content
chicken

[Energia Library] Hardware Counter Library for MSP430

Recommended Posts

From time to time, threads pop up where someone tries to count very fast pulses in the hundreds of kHz or even MHz range. There is a solution for the hardcore C-coders among us, but to my surprise there was no Energia library for this simple problem.

 

I herewith present the CounterLib for Energia

Download

Source code and detailed instructions are also available on GitHub:

https://github.com/astuder/CounterLib-Energia

 

Currently the library supports MSP430G2553, MSP430F5529 and MSP430FR5969.

 

To create an instance of the counter, simply declare it as a global variable like this:

Counter<> MyCounter;    // create a counter that counts pulses on pin P1.0
Once created, the counter has 5 functions:
  • start() initializes the timer peripheral and I/O pin and starts the counter
  • stop() stops the counter, but does not reset the counter value
  • read() reads the current value of the counter
  • reset() resets the counter to 0, the counter keeps running
  • readAndReset() reads the current value and resets the counter to 0
And a basic example, which should work for signals lower than 65 kHz:

Counter<> MyCounter;    // create counter that counts pulses on pin P1.0

void setup()
{
  Serial.begin(9600);
  MyCounter.start();    // start counter
}

void loop()
{
  MyCounter.reset();                // reset counter to zero
  delay(1000);                      // wait one second
  Serial.println(MyCounter.read()); // read number of pulses during the last second
  delay(1000);                      // wait another second
}
The library also supports dividers to measure much faster signals. For more detailed instructions see GitHub.

 

The library uses the external clock input of the timer peripheral. This enables the library to measure very fast signals (MHz range). On the downside, each timer only has a specific pin assigned, and the G2553 only has one timer with an external pin. It is also possible, that other Energia libraries or built-in functionality use the same timer, which won't work.

 

Here's a list of the timers supported by the library and their pins:

| Timer      | G2553,|       |        |        |
|            | G2452,| F5529 | FR5969 | FR6989 |
|            | G2231 |       |        |        |
|------------|-------|-------|--------|--------| 
| CL_TimerA0 |  P1.0 |  P1.0 |  P1.2  |  P1.2* |
| CL_TimerA1 |  n/a  |  P1.6 |  P1.1* |  P1.1* |
| CL_TimerA2 |  n/a  |  P2.2 |  n/a   |  n/a   |
| CL_TimerB0 |  n/a  |  P7.7*|  P2.0* |  P2.0  |
Pins marked with * are not broken out on the LaunchPad or are difficult to access.

 

The library probably works on many other MSP430's, but you'll need to adjust the #defines in the library. Please report back if you successfully tested with other devices, so that I can extend the library.

 

Please report any bugs.

 

And also let me know if you break any speed records. So far I only tested it up to 750 kHz. :rock:

 

Edit 9/3/15: Added support for FR5969. Thanks @@Fmilburn

Edit 9/4/15: Refactored to make it easier to add more MCUs. Several bug fixes, thanks to all the eagle-eyed members of 43oh

Edit 3/13/16: Replaced attached ZIP file with link to GitHub to always give up-to-date version

Share this post


Link to post
Share on other sites

Hello @@chicken

 

Very elegant and nicely documented.  I did a quick and dirty test to see what I could see...

 

Staying with the Energia theme, I wrote this little sketch and ran it on a EK-TM4C123GXL LaunchPad running at 80 MHz:

// toggles a pin LOW to HIGH over and over...

const int pin = 10;           

void setup()
{
  pinMode(pin, OUTPUT);
  digitalWrite(pin, LOW);
}

void loop()
{
  digitalWrite(pin, HIGH);
  digitalWrite(pin, LOW);
}

Here is what I saw on the oscilloscope:

 

post-45284-0-00093900-1441214734_thumb.jpg

 

The reading is 623.7 kHz on the oscilloscope so your speed record is still safe :) .  Then I loaded the many kilohertz example and changed to timer A1 on pin 1.6 just to be different.

 

CounterLib gives me a steady 632 kHz, an error of about 1% so not too bad.  At lower frequencies I get even more accurate results of course.  Great work.  I may have a go at implementing this on another device and will let you know if I am successful.

 

 

 

Share this post


Link to post
Share on other sites

@@Fmilburn thanks for testing Timer A1.

 

As long as you avoid overflow of the 16bit counter, you can improve precision by lengthening the measurement period. Alternatively reducing the divider might also help some, as it increases the "resolution" of the measurement.

Share this post


Link to post
Share on other sites

 

As long as you avoid overflow of the 16bit counter, you can improve precision by lengthening the measurement period

 

You are right of course.  I nominate you for the honorific title of "Count Chicken" ;)

 

I rewrote my signal generation sketch to run on the F5529:

// toggles a pin LOW to HIGH over and over...
// test on MSP-EXP30F5529LP
//
#include <msp430.h>
     

void setup()
{
  P1DIR |= 0x01;               // Set P1.0 to output
}

void loop()
{
  P1OUT ^= 0x01;               // Toggle P1.0 using exclusive OR
}

By getting a bit closer to the metal and getting rid of some Energia overhead it is running faster than before, even on a slower device.  On the oscilloscope I see:

 

post-45284-0-80757000-1441226668_thumb.jpg

 

811 kHz!  We are in new territory.  I doubled the count time and halved the clock (also changed to Timer A2 just to test it).  Here is the new counting sketch for the F5529:

#include "CounterLib_t.h"

Counter<CL_TimerA2> MyCounter;  // create counter that counts pulses on pin P2.2

void setup()
{
  Serial.begin(9600);
  MyCounter.start(CL_Div8,2);   // start counter, divide clock by 16
} 

void loop()
{
  MyCounter.reset();           // reset counter to zero
  delay(200);                  // wait 200 milliseconds
  Serial.print((MyCounter.read() + 5) / 200 * 16);   // read counter, calculate kHz
  Serial.println(" kHz");
  delay(1000);                 // wait one second

I get a very regular 816 kHz.  That is a new speed record and around 0.6 % error.  I'm sure it could be improved with some more tweaking of the parameters.  Alternatively, if the pulses are regular it wouldn't be difficult to put together a function with correction factors.

Share this post


Link to post
Share on other sites

@@Fmilburn you're cracking me up :D

 

Re increasing precision: You moved both parameters at once. Given that 800 kHz / 8 creates only 10K pulses per 100 ms, you could have gone to 200 ms or more without adjusting the divider. Pushing it past 320 ms will be interesting to see whether there's a singed/unsigned int mess-up.

 

I will have to do the math, but I think there's a way to determine and cancel out the overhead with two measurements:

  1. A = count at interval t
  2. B = count at interval 2 * t

If we assume that the overhead X is the same for all intervals, and call C the number of cycles during t we get:

 

A = X + C

B = X + 2 * C

 

Solve for C.

 

B = A - C + 2C
B - A = 2C - C
B - A = C

 

Well dooh! Seems so obvious now, but at least we have the mathematical proof :P

Share this post


Link to post
Share on other sites

Source code and detailed instructions are also available on GitHub:

https://github.com/astuder/CounterLib-Energia

 

Currently the library only supports MSP430G2533 and MSP430F5529.

 

To create an instance of the counter, simply declare it as a global variable like this:

Counter<> MyCounter;    // create a counter that counts pulses on pin P1.0

Nice to see someone else using C++ templates.  +1

 

-rick

Share this post


Link to post
Share on other sites

@@Fmilburn contributed support for the MSP430FR5969 Launchpad.

 

So the supported devices table now looks like this:

| Timer      | G2553 | F5529 | FR5969 |
|------------|-------|-------|--------|
| CL_TimerA0 |  P1.0 |  P1.0 |  P1.2  |
| CL_TimerA1 |  n/a  |  P1.6 |  P1.1  |
| CL_TimerA2 |  n/a  |  P2.2 |  n/a   |
| CL_TimerB0 |  n/a  |  P7.7 |  P2.0  |

Updated GitHub and original post.

 

Edit: Fixed typo

Share this post


Link to post
Share on other sites

Gaah! G2553 of course, got it right in the code, but wrong in the documentation and comments.

 

I'm working on a version that looks at the timer peripheral version anyways, so it should be easier to adapt for more MSP430's soon.

Share this post


Link to post
Share on other sites

Pushed a new version to GitHub and updated the first port with the latest copy.

 

- Refactored #defines to make it easier to add MCUs, especially when sharing the same peripheral versions and pin-outs as an already support MSP430.

- Squashed numerous bugs in documentation and code, thanks to @@spirilis and @@Fmilburn

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×