| Author |
Message |
|
|
Posted: Apr 05, 2012 - 03:15 PM |
|


Joined: Jun 08, 2005
Posts: 102
Location: Hinsdale, Illinois
|
|
Hi all...
I'm working on a project where I'm inputting a signal to the T1 pin input of a Tiny44 to count pulses. I need to interrogate the '44 with another micro. I wanted to do this via SPI, but the T1 input pin is also the SPI CLK pin. How can I work around this? I'm programming this in C btw.
thanks... |
|
|
| |
|
|
|
|
|
Posted: Apr 05, 2012 - 03:26 PM |
|

Joined: Feb 12, 2005
Posts: 16259
Location: Wormshill, England
|
|
You can only use T1 or T0 to count pulses.
You can use any pin to bit-bash the SCK of a SPI Master.
It will be difficult to bit-bash a SPI slave.
I would use the T0 pin, and accept more TIM0_OVFs in the process.
This leaves the USI free for hardware SPI slave.
If your pulses are infrequent, you can count them with INT0 or PCINTn interrupts.
David. |
|
|
| |
|
|
|
|
|
Posted: Apr 05, 2012 - 03:47 PM |
|


Joined: Jun 08, 2005
Posts: 102
Location: Hinsdale, Illinois
|
|
| Thanks...The input is from a water flow meter that generates so many pulses per gallon of water flow. Max. frequency is probably around 2 khz, which isn't all that high |
|
|
| |
|
|
|
|
|
Posted: Apr 05, 2012 - 04:23 PM |
|


Joined: Feb 19, 2001
Posts: 25881
Location: Wisconsin USA
|
|
|
Quote:
The input is from a water flow meter that generates so many pulses per gallon of water flow. Max. frequency is probably around 2 khz, which isn't all that high
I do quite a bit of flowmeter work and it is rare for me to see one even approaching 1kHz at max flow. but of course yours may be different.
Example: 1000ppg, 10gpm, 166Hz.
I suppose you could have a high-flow situation, say 100gpm. I doubt a meter of that type would have a resolution of 1000ppg.
(Many of my flowmeter apps have a "bank" of flowmeters. The pulses come in to a single AVR port. I debounce them in parallel as with any other input, polling every millisecond or so. But indeed I also use edges in some apps and set up one channel on Tn pin for high-speed work--but that wouldn't be one of the ~100Hz max flowmeters.)
Oh, yeah--why not pick an AVR model that is more "pin friendly" for the features you need? E.g. a Mega48A is about $0.20 more than a Tiny44 qty. 100. And if a one-off a buck doesn't matter much. |
|
|
| |
|
|
|
|
|
Posted: Apr 05, 2012 - 04:36 PM |
|


Joined: Jun 08, 2005
Posts: 102
Location: Hinsdale, Illinois
|
|
| I have done this within my main controller before (Mega644 or Mega32), but I wanted to create a dedicated chip that does nothing but get the flow. That way I don't have to take the time to "read" the flow especially if it's really low flow, just interrogate the chip and go. The chip outputs the frequency and then the main controller applies the K factor determining the actual flow. This is mainly for use on some of the polymer controllers we design where a lot of "stuff" is happening. |
|
|
| |
|
|
|
|
|
Posted: Apr 05, 2012 - 04:53 PM |
|

Joined: Feb 12, 2005
Posts: 16259
Location: Wormshill, England
|
|
If your water meter was running at 2 million pulse a second, this would be the obvious route.
For 10Hz - 2kHz, it is probably simpler to do with your mega644. You know your app. We don't.
David. |
|
|
| |
|
|
|
|
|
Posted: Apr 05, 2012 - 05:05 PM |
|


Joined: Jun 08, 2005
Posts: 102
Location: Hinsdale, Illinois
|
|
| It's mainly for the low-end. I don't want to have to wait 1-2 seconds for flow pulses |
|
|
| |
|
|
|
|
|
Posted: Apr 05, 2012 - 05:16 PM |
|


Joined: Feb 19, 2001
Posts: 25881
Location: Wisconsin USA
|
|
|
Quote:
The chip outputs the frequency and then the main controller applies the K factor determining the actual flow. This is mainly for use on some of the polymer controllers we design where a lot of "stuff" is happening.
But you still haven't given me any hints as to the type of flowmeter that might even approach 2kHz pulse rate. The mention of "polymer" implies to me precision low-flow meters. (And yes, I know all about K factor.) I don't see any here: http://www.omega.com/toc_asp/subsection ... book=Green But yeah, I guess there could certainly be a high-precision device say 1 lpm and 120000 ppl.
Quote:
I don't want to have to wait 1-2 seconds for flow pulses
Why would your external device be any faster than integrating it into the main controller?!?
At the low end, you do a type of integrated rate. Simple example: You capture the pulses for each 100ms period. Your instantaneous flow rate is the total of the last 10 samples. (In practice I usually use a factor of 2 just to streamline things a bit--e.g. 1/8 or 1/64 second captures.)
I'm pounding on about the max pulse rate, because typically a periodic poll (say, 1ms) works quite well as the max rate I see in practice is no more than a few hundred Hz and typically no more than 150Hz. |
|
|
| |
|
|
|
|
|
Posted: Apr 05, 2012 - 05:24 PM |
|


Joined: Feb 19, 2001
Posts: 25881
Location: Wisconsin USA
|
|
Six-channel example, using a "fast" timer tick and a 3-stage debounce:
Code:
// Flowmeter polling/debouncing/pulse counting
// ===========================================
//
// Up to 6 pulse-output flowmeters (on PC1-PC6) are polled,
// debounced in parallel, and changed edges "counted" in the
// fast timer ISR.
//
// With 7.37MHz, /8, and 0xe5 KAVRCALC gives very close to 250us period.
// This is nearly identical to continuous ADC conversions at 57kHz ADC clock,
// and USART comms at 38.4kbps. The aim is to keep the ISR about the
// same time as a general ADC or UART ISR.
//
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 460.800 kHz
// Mode: CTC top=OCR0A
// OC0A output: Disconnected
// OC0B output: Disconnected
TCCR0A=0x02;
TCCR0B=0x02;
TCNT0=0x00;
OCR0A=0xE5; // ~250us at /8, 7.37MHz
OCR0B=0x00;
...
// **************************************************************************
// *
// * T I M E R 0 _ C O M P A _ I S R
// *
// **************************************************************************
//
// Sampling routine for flow sensor channels.
//
// Timer 0 output compare A interrupt service routine
interrupt [TIM0_COMPA] void timer0_compa_isr(void)
{
// Stage 2 (signal_bounce2) removed--add in if another stage is needed
signal_work = signal_bounce3; // move prior states closer to validated
signal_bounce3 = signal_bounce1;
signal_bounce1 = (PINC) & FLOW_MASK;// get the new strobe data -- "signal_fetch()"
signal_bounce1 ^= signal_current; // only concerned with changes from current
signal_work &= signal_bounce1; // make sure there are no bounces
signal_work &= signal_bounce3;
signal_current ^= signal_work; // make validated changes to current
signal_bounce1 ^= signal_work; // and remove from pending stages
signal_bounce3 ^= signal_work;
// signal_edge = signal_work & signal_current; // only want "on" transitions
// Flip-flop divides by two, so count both edges
// signal_edge = signal_work; // NOTE: could eliminate one of these...
if (signal_work & FLOW_1_MASK)
{
chan_raw[0]++;
}
// Channels 4 & 6 are swapped on the board--adjusted in FLOW_n_MASK
if (signal_work & FLOW_2_MASK) chan_raw[1]++;
if (signal_work & FLOW_3_MASK) chan_raw[2]++;
if (signal_work & FLOW_4_MASK) chan_raw[3]++;
if (signal_work & FLOW_5_MASK) chan_raw[4]++;
if (signal_work & FLOW_6_MASK) chan_raw[5]++;
}
... below is done every 100ms, calculating new values (to be applied to DAC in this app)
// Capture new channel counts from _raw
for (looper = 0; looper < NUM_CHANNELS; looper++)
{
// Get a channel count for the last 100ms, and clear.
// 8-bit value allows for 256x10=2560Hz; way more than spec.
#asm ("cli")
chan_samples[looper][clicks] = chan_raw[looper];
chan_raw[looper] = 0;
#asm ("sei")
chan_total[looper] = 0; // init for the summing below
}
// Calculate the full-second totals-- Hertz
ptr = chan_samples[0];
iptr = chan_total;
grand_total = 0;
for (looper = 0; looper < NUM_CHANNELS; looper++)
{
// Unroll the loop CLICKS_PER_SECOND times (should be faster than nested loop)
// Revised to nested loop
for (scratch = 0, worknum = 0; scratch < CLICKS_PER_SECOND; scratch++)
{
worknum += *ptr++;
}
*iptr++ = worknum;
grand_total += worknum;
}
// Calculate the DAC output value
// (could be merged into the loops above)
iptr = chan_total;
for (looper = 0; looper < NUM_CHANNELS; looper++)
{
// Get 4mA and 20mA 16-bit count numbers into register variables
workmin = ee_min[looper];
workmax = ee_max[looper];
// Note: ee_setpoint (for the 20mA max value) is in Hertz*10
worknum = ee_setpoint[looper];
// calculate setpoint in 32-bit for overflow purposes
worklong.u32 = (DAC_MAX * 10ul) * (unsigned long)*iptr / worknum;
if (worklong.u32 > DAC_MAX)
{
chan_setpoint[looper] = DAC_MAX;
}
else
{
chan_setpoint[looper] = worklong.u16[0]; // low 16 bits
}
iptr++;
}
|
|
|
| |
|
|
|
|
|
Posted: Apr 05, 2012 - 05:41 PM |
|


Joined: Jun 08, 2005
Posts: 102
Location: Hinsdale, Illinois
|
|
| I like that. As I look back over some notes your right that 2khz is pretty high. I did have one around 1.5 khz at one time, but most are under 1khz. My main problem is all the timers interrupts are being used to generate pulse outputs for LMI pumps and also generating a PWM signals for several 4-20 outputs. I was basically waiting for positive edges to occur and calc. the period/freq based on that. So I figured with the external devices things would be easier.... |
|
|
| |
|
|
|
|
|
Posted: Apr 05, 2012 - 05:52 PM |
|


Joined: Feb 19, 2001
Posts: 25881
Location: Wisconsin USA
|
|
The above is fast-poll and debounce. Below is a 4-channel that can handle speeds well into kHz on each channel, and the fourth channel goes into T1 so it is like a MHz. Analog comparator is used as a third configurable "external interrupt" on the Mega48.
Code:
//
// Interrupt vectors
//
// 10ms. Timer Tick
interrupt [TIM0_COMPA] void timer0_compa_isr (void);
// External Interrupt 1 service routine -- FLOW2 using INT1
interrupt [EXT_INT1] void ext_int1_isr (void);
// External Interrupt 0 service routine -- FLOW1 using INT0
interrupt [EXT_INT0] void ext_int0_isr (void);
// Analog Comparator interrupt service routine -- FLOW4 using AIN1
interrupt [ANA_COMP] void ana_comp_isr (void);
// Timer1 used as counter -- FLOW3 using T1
// (no ISR needed for each count; TCNT1 read/cleared every 10ms)
...
//
// **************************************************************************
// *
// * F L O W M E T E R P U L S E C O U N T I N G I S R S
// *
// **************************************************************************
//
// If a flowmeter ISR takes 5us to service and there are 4 active,
// and to use no more than ~50% of CPU (USART is continually going),
// then about 40ksps total is comforatble or 10ksps/flowmeter.
//
// Typical flowmeters are about 100pps so this is no problem. In
// addition, channel 4 uses T1 and can handle rates into the Msps range.
//
// External Interrupt 1 service routine -- FLOW2 using INT1
interrupt [EXT_INT1] void ext_int1_isr(void)
{
flow2++;
}
// External Interrupt 0 service routine -- FLOW1 using INT0
interrupt [EXT_INT0] void ext_int0_isr(void)
{
flow1++;
}
// Timer1 used as counter -- FLOW3 using T1
// (no ISR needed for each count; TCNT1 read/cleared every 10ms)
// Analog Comparator interrupt service routine -- FLOW4 using AIN1
interrupt [ANA_COMP] void ana_comp_isr(void)
{
flow4++;
}
... every 10ms, update the per-channel running 32-bit counts for the master's flow
// ***********
// Flowmeter interval counters
// ***********
//
// Flowmeter 1 -- INT1 rising edges
#asm ("cli")
worknum = flow1;
flow1 = 0;
#asm ("sei")
flow1_counts += worknum;
// Flowmeter 2 -- INT0 rising edges
#asm ("cli")
worknum = flow2;
flow2 = 0;
#asm ("sei")
flow2_counts += worknum;
// Flowmeter 3 -- T1 rising edges (timer1 as counter; TCNT1)
#asm ("cli")
retval = TCNT1L; // read low first, then high
scratch = TCNT1H;
TCNT1H=0x00; // write high first, then low
TCNT1L=0x00;
#asm ("sei")
worknum = ((unsigned int) scratch << 8) | retval;
flow3_counts += worknum;
// Flowmeter 4 -- AIN1 rising edges
#asm ("cli")
worknum = flow4;
flow4 = 0;
#asm ("sei")
flow4_counts += worknum;
// Store in om_frames[] to reply to the master with the per-channel running counts
om_frames[0].u32 = flow1_counts;
om_frames[1].u32 = flow2_counts;
om_frames[2].u32 = flow3_counts;
om_frames[3].u32 = flow4_counts;
There is another section of code that adjusts an LED blink for each channel in proportion to flow rate.
Anyway, IME a few channels of flowmeter shouldn't bring an AVR to processor saturation. And I don't understand about this "waiting for over a second" and how a secondary AVR eliminates that "problem" -- you can re-calculate flow rate as often as you like and in whatever manner, instantaneous over the last period or some kind of "look back" buffer as in my polled example or whatever mechanism works best for the situation. Note that with care, you can do instantaneous or look-back, keep the "remainders" each pass, and end up with no rounded-off pulses over time. |
|
|
| |
|
|
|
|
|
Posted: Apr 05, 2012 - 06:48 PM |
|


Joined: Jun 08, 2005
Posts: 102
Location: Hinsdale, Illinois
|
|
I was actually sitting there waiting for edges, so for low or NO flow I wound up sitting there for a second. I like your method.
So you let the flowmeter interrupt accumulate pulse counts, then look at the number of counts every 10mS? So 1 pulse @ 10mS = 100Hz...correct? What about reading rates less...extend the time? I have this one oval-gear flowmeter that ranges from 3 to 34 hz, so would I have to "gate" for 1 second...correct? |
|
|
| |
|
|
|
|
|
Posted: Apr 05, 2012 - 07:04 PM |
|


Joined: Feb 19, 2001
Posts: 25881
Location: Wisconsin USA
|
|
|
Quote:
...correct?
Not really. You gather interval counts, and totalize, as you see fit. See the first example where 1/10 second a new flow rate is computed over the last full second.
It depends on your app how long you wait for "no flow". In my apps, I use 5 or 6 seconds, and even then it needs to be below a threshold--these paddle-types can sit there and rock when the flow stops, but they don't count in both directions like a quadrature encoder.
The point is you don't just sit and wait in your main app for an edge. I see no difference how any "wait" is any different. |
|
|
| |
|
|
|
|
|
Posted: Apr 05, 2012 - 07:13 PM |
|


Joined: Jun 08, 2005
Posts: 102
Location: Hinsdale, Illinois
|
|
|
Quote:
The point is you don't just sit and wait in your main app for an edge. I see no difference how any "wait" is any different.
Gotcha....thanks for all your help! |
|
|
| |
|
|
|
|
|
Posted: Apr 05, 2012 - 07:25 PM |
|


Joined: Feb 19, 2001
Posts: 25881
Location: Wisconsin USA
|
|
It might be interesting to note that in most of my apps the aim is to capture total flow. Flow rate indication is a secondary.
Thus in my second example you see flowN_counts constantly incrementing. It is a 32-bit value so at 100Hz it will overflow in 40000000 seconds--over a year of continuous 24/7 operation at max flow rate. I anticipate a power cycle and start over every few years so I don't even worry about it.
When a "cycle" begins, the controller captures the starting counts. Then when it closes the app loop it uses the current counts and calculates the flow. Where flow amount is appropriate the pump/valve is adjusted accordingly. In many cases there is indeed a "no flow" watch for less than a few counts in a few seconds when the pump is on (or valve is open). This prevents the pump from running forever with e.g. an empty source drum. |
|
|
| |
|
|
|
|
|
Posted: Apr 05, 2012 - 07:29 PM |
|


Joined: Jun 08, 2005
Posts: 102
Location: Hinsdale, Illinois
|
|
| I see....I have to do flow rate so I can then pace the polymer pump to get a required concentration of polymer to water. |
|
|
| |
|
|
|
|
|
Posted: Apr 05, 2012 - 07:56 PM |
|


Joined: Feb 19, 2001
Posts: 25881
Location: Wisconsin USA
|
|
The first example I gave is indeed a proportional flow of a chemical concentrate injected into water flow being measured with the flowmeters. (In this case the output is a 4-20mA signal into a proportional needle valve.)
As you can see in that example I'm adjusting the output flow every 1/10 second.
Beyond that, it will depend on the app and the total accuracy required. |
|
|
| |
|
|
|
|
|
Posted: Apr 23, 2012 - 03:55 PM |
|


Joined: Jun 08, 2005
Posts: 102
Location: Hinsdale, Illinois
|
|
| How are you handling the jitter from the paddle wheels? I was trying to average it out using a "sliding window", but with my "gate" at 1 second, it takes forever to show loss of flow. |
|
|
| |
|
|
|
|
|
Posted: Apr 23, 2012 - 04:23 PM |
|


Joined: Feb 19, 2001
Posts: 25881
Location: Wisconsin USA
|
|
|
Quote:
but with my "gate" at 1 second, it takes forever to show loss of flow.
Indeed in my apps I use a threshold of a few counts over a 5/6 second interval to signify "no flow". In my apps, when the pump is turned off the "hose" might still be draining. |
|
|
| |
|
|
|
|
|
Posted: Apr 23, 2012 - 05:20 PM |
|


Joined: Jun 08, 2005
Posts: 102
Location: Hinsdale, Illinois
|
|
In a polymer system, I need to detect loss of flow ASAP, because the polymer will "goo" up the system with no water to dilute it.
Do you see any type of "jitter" when reading paddle wheel flowmeters using this method? It seems stable with the signal generator, but I haven't tested it in the "wet lab" yet. |
|
|
| |
|
|
|
|
|
Posted: Apr 23, 2012 - 06:02 PM |
|


Joined: Feb 19, 2001
Posts: 25881
Location: Wisconsin USA
|
|
| "ASAP" is in the eye of the beholder. At low flow rates there may only be a few pulses per second. It is up to you to determine the most appropriate method for >>your<< app. |
|
|
| |
|
|
|
|
|
Posted: Apr 23, 2012 - 06:06 PM |
|


Joined: Jun 08, 2005
Posts: 102
Location: Hinsdale, Illinois
|
|
|
|
|
|
|