|
|
View previous topic :: View next topic |
Author |
Message |
allenhuffman
Joined: 17 Jun 2019 Posts: 589 Location: Des Moines, Iowa, USA
|
Counting pulses (freq. counter) on an I/O pin example? |
Posted: Fri Apr 03, 2020 12:32 pm |
|
|
Hardware has finally caught up to software and now I am trying to implement a frequency counter based on pulses to an I/O pin. Our input signal gets divided a few times to be something we should be able to count with a 32-bit counter without rolling over, but I am having difficulty finding details on the CCS API to set this up. (Plenty of examples for talking straight to the chip online, but I'm trying to do it with CCS code if I can.)
I've been pointed to this in the manual:
((EDITED))
Code: |
setup_timer3(TMR_INTERNAL | TMR_DIV_BY_8);
setup_capture(2, CAPTURE_RE | CAPTURE_TIMER3);
while(TRUE) {
timerValue = get_capture(2, TRUE);
printf("Capture 2 occurred at: %LU", timerValue);
} |
I do have Timer 2 and 3 of my PIC24 available, but so far I am not getting anything out of get_capture(). I do believe I needed to add:
Code: | #PIN_SELECT IC2=PIN_C8 |
...to match the pin I am using, but that did not seem to change anything. I'm sure I'm just missing another setup call or #use or something, but I haven't found anything obvious in the PDF manual.
Any pointers on where I should be looking?
Thanks! _________________ Allen C. Huffman, Sub-Etha Software (est. 1990) http://www.subethasoftware.com
Embedded C, Arduino, MSP430, ESP8266/32, BASIC Stamp and PIC24 programmer.
http://www.whywouldyouwanttodothat.com ?
Using: 24FJ256GA106, 24EP256GP202 and 24FJ64GA002.
Last edited by allenhuffman on Fri Apr 03, 2020 12:59 pm; edited 2 times in total |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19590
|
|
Posted: Fri Apr 03, 2020 12:48 pm |
|
|
Yes, you need to have the #PIN_SELECT.
Then your second capture setup overrides the first. If you want to
capture both edges they need to be or'ed into the single setup.
Then you want to use get_capture32 for a 32bit value.
Then you need to program Timer3 with a clock source, and as a
cascaded 32bit timer.
Setup like this, the capture records the timer value, when the
capture event occurs. Is ths what you want?. It sounds more as if you
want to use the counter to actually count your external signal?.
If this is what you want, you can just program a 32bit timer, to be
clocked from your source. |
|
|
allenhuffman
Joined: 17 Jun 2019 Posts: 589 Location: Des Moines, Iowa, USA
|
|
Posted: Fri Apr 03, 2020 12:50 pm |
|
|
Ttelmah wrote: | Yes, you need to have the #PIN_SELECT.
Then your second capture setup overrides the first. If you want to
capture both edges they need to be or'ed into the single setup.
Then you want to use get_capture32 for a 32bit value.
Then you need to program Timer3 with a clock source, and as a
cascaded 32bit timer.
Setup like this, the capture records the timer value, when the
capture event occurs. Is ths what you want?. It sounds more as if you
want to use the counter to actually count your external signal?.
If this is what you want, you can just program a 32bit timer, to be
clocked from your source. |
Ah, I need to count the number of pulses over a time frame. I will be looking at the count every Xms and using that value to calculate frequency.
Can a timer use an I/O pin as the source? _________________ Allen C. Huffman, Sub-Etha Software (est. 1990) http://www.subethasoftware.com
Embedded C, Arduino, MSP430, ESP8266/32, BASIC Stamp and PIC24 programmer.
http://www.whywouldyouwanttodothat.com ?
Using: 24FJ256GA106, 24EP256GP202 and 24FJ64GA002. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Apr 03, 2020 12:51 pm |
|
|
The example in the CCS manual shows they setup Timer3. I don't see
that in your posted code. From the setup_capture() section in the manual:
Code: |
setup_timer3(TMR_INTERNAL | TMR_DIV_BY_8);
setup_capture(2, CAPTURE_FE | CAPTURE_TIMER3);
while(TRUE) {
timerValue = get_capture(2, TRUE);
printf(“Capture 2 occurred at: %LU”, timerValue);
} |
|
|
|
allenhuffman
Joined: 17 Jun 2019 Posts: 589 Location: Des Moines, Iowa, USA
|
|
Posted: Fri Apr 03, 2020 12:59 pm |
|
|
PCM programmer wrote: | The example in the CCS manual shows they setup Timer3. I don't see
that in your posted code. From the setup_capture() section in the manual:
|
Oops, copy/paste mistake. That's where the code I pasted in came from, but I couldn't get it to do anything. But now it sounds like it wouldn't be what I was after anyway. _________________ Allen C. Huffman, Sub-Etha Software (est. 1990) http://www.subethasoftware.com
Embedded C, Arduino, MSP430, ESP8266/32, BASIC Stamp and PIC24 programmer.
http://www.whywouldyouwanttodothat.com ?
Using: 24FJ256GA106, 24EP256GP202 and 24FJ64GA002. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19590
|
|
Posted: Fri Apr 03, 2020 1:21 pm |
|
|
So look at the timer setups.
You need the TMR_32_BIT setting, and TMR_DIV_BY_1 and
TMR_EXTERNAL.
Use PPS to route T2CK to the pin you want, then timer2/3will
merrily count your signal.
Use get_timer23 to get the 32bit value. |
|
|
allenhuffman
Joined: 17 Jun 2019 Posts: 589 Location: Des Moines, Iowa, USA
|
|
Posted: Fri Apr 03, 2020 1:40 pm |
|
|
Ttelmah wrote: | So look at the timer setups.
You need the TMR_32_BIT setting, and TMR_DIV_BY_1 and
TMR_EXTERNAL.
Use PPS to route T2CK to the pin you want, then timer2/3will
merrily count your signal.
Use get_timer23 to get the 32bit value. |
Okay, so I have this:
Code: | #PIN_SELECT T2CK=PIN_C8 |
Then in main, I set up the dual timers like this:
Code: | setup_timer2(TMR_32_BIT | TMR_DIV_BY_1 | TMR_EXTERNAL); |
Then I should be able to read the value using:
Code: | uint32_t value;
value=get_timer23(); |
Am I finally on the correct page? _________________ Allen C. Huffman, Sub-Etha Software (est. 1990) http://www.subethasoftware.com
Embedded C, Arduino, MSP430, ESP8266/32, BASIC Stamp and PIC24 programmer.
http://www.whywouldyouwanttodothat.com ?
Using: 24FJ256GA106, 24EP256GP202 and 24FJ64GA002. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19590
|
|
Posted: Sat Apr 04, 2020 1:08 am |
|
|
I hope so....
I'd have to have a play to be sure.
It should just count the pulses arriving on the clock pin.
However have to make a comment here.
There are two different 'approaches' to measuring a frequency.
Which is 'best' depends largely on the frequency of the signal and
of your processor, and what else it is doing.
Approach one. Basic obvious one. If you want to measure how may
'hertz' a signal is, simply count how many pulses it receives in one second.
This seems to be the way you are looking at going.
However there is approach two. Here instead of counting pulses, you
measure the input 'period'. Frequency is then 1/period.
Now the advantage of this is the measurement can be done in a single
cycle of the waveform. No having to wait for a large number of pulses.
Downsides are no damping (counting over a second, will ignore individual
mistimed pulses), and it involves extra maths.
Now the second can be done really efficiently using the input capture
peripheral. If you program the timer to run from your master CPU clock
assuming a DsPIC, you will probably count from something like 30MHz+.
So if your waveform was 100Hz, you would get a capture update 100*
per second. Then if you take the current capture value, minus the
last value, you will have something like 300000 counts (for a 30MHz
peripheral clock, and 100Hz pulse). The frequency is then:
30000000/300000 = 100Hz, and can be measured every single cycle.
The division is always simply a matter of taking peripheral clock divided
by the count. Makes it possible to use integer maths, which on the DsPIC
is extremely fast.
So two different approaches to consider, depending on how you want
to work.... |
|
|
jeremiah
Joined: 20 Jul 2010 Posts: 1358
|
|
Posted: Sat Apr 04, 2020 11:55 am |
|
|
I once had a scenario where I needed a 32bit capture value and I either I didn't know how to at the time or maybe it wasn't available (I don't recall....it's been over 10 years now). I ended up using two interrupts: an input capture one and one for its timer to manually build a 32bit capture value. I don't have my old work resources with me, so this is off the cuff/top of my head (it does compile), but the method was something similar to the following (for a PIC24 chip):
Code: |
#include <24fj64ga004.h> // I don't remember the actual PIC. Placeholder here
#use delay(clock=8000000) // Placeholder here
static volatile unsigned int32 g_period = 0;
static volatile unsigned int16 g_high = 0;
#pin_select IC1=PIN_C8
#int_timer2
void timer2_isr(){
g_high++;
}
#int_ic1
void ic1_isr()
{
// history variables are static to remember their last states
static unsigned int16 last_low = 0;
static unsigned int16 last_high = 0;
// Get most current updates
unsigned int16 current_low = get_capture(1);
unsigned int16 current_high = g_high;
unsigned int32 current, last;
// Check the edge case where we get an edge in at the same time
// as the timer period overflows but the input capture interrupt
// happens first (which means g_high has not been properly
// inremented yet, but it will be after this interrupt is done)
if((current_low <= last_low) && (current_high == last_high)){
current_high++;
}
// Make the 32bit values
current = make32(current_high,current_low);
last = make32(last_high, last_low);
// calculate the period ticks. math is unsigned, so if
// current < last, it will still give the correct delta
g_period = current - last;
// update the static variables
last_low = current_low;
last_high = current_high;
}
// Need a special function to get the number of period ticks
// for our input waveform
unsigned int32 get_capture_period_ticks(){
// Since g_period is not atomic, we need to do a special read
// loop to make sure we don't read it at the same time as int_ic1
// occurs and get half updated/erroneous data. You can alternately
// just disable/enable interrupts around reading g_period as well.
unsigned int32 result;
do{
result = g_period;
} while (result != g_period);
return result; // clock frequency divided by recorded period
}
void main()
{
setup_timer2(TMR_INTERNAL); // Start timer 2
setup_capture(1,CAPTURE_TIMER2 | CAPTURE_RE); // Configure CCP1 to capture rise
enable_interrupts(INT_IC1); // Setup interrupt on falling edge
enable_interrupts(INT_TIMER2);
enable_interrupts(GLOBAL);
while(TRUE) {
// your code here
}
}
|
NOTE: I'm not 100% certain the IF in the IC1 interrupt is needed, but I was being careful. I didn't want to risk random period values that were hard to track and I wasn't sure of the latency of the input_capture vs timer logic in terms of which would signal the interrupt first if they happen at the same time. If it isn't needed, then all the last stuff can probably simplified down to a single static unsigned int32 last declaration.
NOTE: Also note that the very first time the IC1 interrupt occurs, the period will be off because the initial value is zero. Once you get the second reading, the period ticks will be correct. This can be guarded by using a global boolean that gets set in the IC1 interrupt and read in the get_capture_period_ticks() (maybe return 0 if the boolean isn't set). This is just a proof of concept idea. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9271 Location: Greensville,Ontario
|
|
Posted: Sun Apr 05, 2020 5:35 am |
|
|
As Mr. T points out , two ways to do it, how is based on actual 'frequency'.
One thing I thought of, kinda important.....
You say 'the signal is divided down before the PIC'.
Be SURE that 'division' does NOT change ! I can see a hair pulling, very long night, if ,someone changes the divisor from say /4 to /5 and YOUR numbers aren't correct !
Also, add a comment in YOUR code for the 'frequency counter' that it is based upon an offboard 'predivider' of 'X'. While you know what it is now, 3 days or 3 months from now....well, we all forget the little details.....
Jay |
|
|
allenhuffman
Joined: 17 Jun 2019 Posts: 589 Location: Des Moines, Iowa, USA
|
|
Posted: Mon Apr 06, 2020 10:23 am |
|
|
Thanks, gang.
We read a range of frequencies from 900Mhz to maybe 3GHz, so we run through two sets of dividers to get our pulse count down to the 2-10MHz range for the PIC to count.
Code: |
#PIN_SELECT T2CK=PIN_C8
...
setup_timer2(TMR_32_BIT | TMR_DIV_BY_1 | TMR_EXTERNAL);
...
uint32_t Value;
Value = get_timer23();
|
This looks like it's getting me where I want to go and I hope to have something working later today when I get a new board.
CCS support also replied and sent me an example using setup_capture()/get_capture() so I may look at that too and see how it works. _________________ Allen C. Huffman, Sub-Etha Software (est. 1990) http://www.subethasoftware.com
Embedded C, Arduino, MSP430, ESP8266/32, BASIC Stamp and PIC24 programmer.
http://www.whywouldyouwanttodothat.com ?
Using: 24FJ256GA106, 24EP256GP202 and 24FJ64GA002. |
|
|
allenhuffman
Joined: 17 Jun 2019 Posts: 589 Location: Des Moines, Iowa, USA
|
|
Posted: Thu Apr 09, 2020 12:40 pm |
|
|
Back to the drawing board, maybe. When our signal gets up around 5Mhz on the pin, the count values start wobbling around and then they start going lower as the input frequency rises. It looks like I hit some limit of how fast the PIC24 counter can handle.
Our spec'd range (after going through two sets of hardware dividers) is to be able to count pulses to the I/O pin as fast as 12,500,00 Hz.
From what we have been seeing, it stops being reliable around 5,600,00 Hz.
Would those 16-bit capture timers even be able to do this? _________________ Allen C. Huffman, Sub-Etha Software (est. 1990) http://www.subethasoftware.com
Embedded C, Arduino, MSP430, ESP8266/32, BASIC Stamp and PIC24 programmer.
http://www.whywouldyouwanttodothat.com ?
Using: 24FJ256GA106, 24EP256GP202 and 24FJ64GA002. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9271 Location: Greensville,Ontario
|
|
Posted: Thu Apr 09, 2020 1:06 pm |
|
|
Interesting number 5.6MHz, it kinda rang a bell.
Same cutoff frequency as the LS7267 24 bit counter chips I used.
I don't use any PIC24s, but the 'spec' has to be somewhere in the 100's of pages of the datasheet.
You may need another 'prescaler' to knock down the counts first.
Jay |
|
|
allenhuffman
Joined: 17 Jun 2019 Posts: 589 Location: Des Moines, Iowa, USA
|
|
Posted: Thu Apr 09, 2020 1:41 pm |
|
|
Datasheet led us astray. It uses two cycles to do it, so it's half what we thought it could do.
I had to change our timer prescaler to DIV_8 and we are back on track at the loss of a few decimal places of precision. _________________ Allen C. Huffman, Sub-Etha Software (est. 1990) http://www.subethasoftware.com
Embedded C, Arduino, MSP430, ESP8266/32, BASIC Stamp and PIC24 programmer.
http://www.whywouldyouwanttodothat.com ?
Using: 24FJ256GA106, 24EP256GP202 and 24FJ64GA002. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19590
|
|
Posted: Thu Apr 09, 2020 1:49 pm |
|
|
The PIC24 can count over 20MHz. Have one doing this.
You are sure you are not enabling SYNC?. If this is enabled, it limits the
count frequency, because counts won't be recorded till the next edge on the
internal oscillator. The maximum supported with sync disabled is specified by
the maximum high and low times for the input pin. Typically 40nSec
for the cycle, so 25MHz. |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|