Author Topic: Duty Cycle detection  (Read 13093 times)

0 Members and 3 Guests are viewing this topic.

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1767
  • Country: se
Re: Duty Cycle detection
« Reply #25 on: October 14, 2017, 10:42:02 am »
You convinced  me.
I know AVR controllers (Atmega328, Arduino,..) and I have a bit of C++ experience.
Please give me some input o how to tackle this?
Oh drat! I now have to be helpful rather than snarky :scared:

The whole module consists of 1. the sensor module with duty cycle output 2. hard- or software to convert this duty cycle value to an analog value (or convert straight using a microcontroller) 3. a microcontroller to process this value for transmission 4. DS3231 RTC module to control the wake-up timings of the microcontroller for transmissions 5. a 464MHz HC12 transmission module 6. 6V battery, LDO voltage regulator and solar panel.

So if the duty cycle measurement can be done by this same microcontroller I am open for suggestions as you did, but please maybe a little more detailed because my programming knowledge is still lacking a lot?
Some programming will be needed for sure: your system already includes both I2C (for the RTC) and UART (for the RF module) interfaces, plus something else to wake up the sensor when it's time for a measurement.
A controller able to handle those will for sure have enough power to measure the duty cycle at 2kHz.

An ATmega would be I think OK: there's Atmel Application Note 135 that fits very well with what you're after, and other examples can be found on the web.
If you're familiar with Arduino it might even be possible to use its framework for the communications and sensor control, while resorting to direct register programming for duty cycle measurements.

I have more experience with STM32s, so my personal preference would be to use an el-cheapo STM32F103 board or a (more expensive) Nucleo and exploit the timer PWM input capture mode (see chapter 14.3.7 in the reference manual) to get period and on/off times. As those cheapies come with a 32kHz crystal, I would probably do away with the separate RTC module, unless the better accuracy or other time keeping functions of DS3231 are needed.

Of course, the way forward depends also on other constraints, e.g. time available vs. platform knowledge, single project or small/large volume product etc. etc...


Nandemo wa shiranai wa yo, shitteru koto dake.
 
The following users thanked this post: 84750Erik

Offline 84750ErikTopic starter

  • Contributor
  • Posts: 41
  • Country: be
    • My Safety
Re: Duty Cycle detection
« Reply #26 on: October 14, 2017, 11:56:25 am »

(...)

... while resorting to direct register programming for duty cycle measurements.

....I would probably do away with the separate RTC module, unless the better accuracy or other time keeping functions of DS3231 are needed.

Of course, the way forward depends also on other constraints, e.g. time available vs. platform knowledge, single project or small/large volume product etc. etc...
Direct register programming: out of my league.
Time constraints: not applicable
Willingness to learn: fully
RTC module: necessary because the module needs to be able to remain accurate within about a minute over a half year period

So unless I may receive some guidance I can just as well use the hardware solution. But I am willing to take up the challenge given the input I already received here.
 

Offline David Hess

  • Super Contributor
  • ***
  • Posts: 17115
  • Country: us
  • DavidH
Re: Duty Cycle detection
« Reply #27 on: October 14, 2017, 11:58:09 am »
If I did not want to tie up or use the timer/counters or any external logic, then I might use an I2C or SPI port as a 1 bit clocked ADC.  Count the number of 1s and 0s to get the duty cycle and do so over multiple cycles to get greater resolution if needed.  Or use any general purpose I/O pin to do the same thing in a software loop.
 
The following users thanked this post: 84750Erik

Offline 84750ErikTopic starter

  • Contributor
  • Posts: 41
  • Country: be
    • My Safety
Re: Duty Cycle detection
« Reply #28 on: October 14, 2017, 12:09:03 pm »
If I did not want to tie up or use the timer/counters or any external logic, then I might use an I2C or SPI port as a 1 bit clocked ADC.  Count the number of 1s and 0s to get the duty cycle and do so over multiple cycles to get greater resolution if needed.  Or use any general purpose I/O pin to do the same thing in a software loop.
Why should I have to give up I2C just to count 1s and 0's? Can this not be done in a digital input? During a short fixed amount of time read digital values from the sensor, summation of both and compare the amount of high versus low?
 

Offline David Hess

  • Super Contributor
  • ***
  • Posts: 17115
  • Country: us
  • DavidH
Re: Duty Cycle detection
« Reply #29 on: October 14, 2017, 01:25:56 pm »
If I did not want to tie up or use the timer/counters or any external logic, then I might use an I2C or SPI port as a 1 bit clocked ADC.  Count the number of 1s and 0s to get the duty cycle and do so over multiple cycles to get greater resolution if needed.  Or use any general purpose I/O pin to do the same thing in a software loop.

Why should I have to give up I2C just to count 1s and 0's? Can this not be done in a digital input? During a short fixed amount of time read digital values from the sensor, summation of both and compare the amount of high versus low?

Sure it can be done with a general purpose digital input which I said.  The advantage of using an I2C or SPI port is that a much higher sample rate may be attained for greater resolution.
 
The following users thanked this post: 84750Erik

Offline 84750ErikTopic starter

  • Contributor
  • Posts: 41
  • Country: be
    • My Safety
Re: Duty Cycle detection
« Reply #30 on: October 14, 2017, 03:03:05 pm »
What digital input is the SPI port on an Arduino?
I don't think to need the SPI port, programming will be over hardware serial.
 

Online T3sl4co1l

  • Super Contributor
  • ***
  • Posts: 22374
  • Country: us
  • Expert, Analog Electronics, PCB Layout, EMC
    • Seven Transistor Labs
Re: Duty Cycle detection
« Reply #31 on: October 14, 2017, 05:27:57 pm »
Note that a digital input pin is a 1-bit ADC (with a shitty threshold).  You can realize precisely the same functions that have been suggested here in analog terms, as digital structures.  Upside: counters are free, and numerical division is mere microseconds, so it's even more accessible to do the full, frequency independent, duty cycle calculation (again, not that you need to).

Considering your signal is more or less digital to begin with, there need not even be any quantization noise! :)

You do have to worry about sample rate and all that, but since you want statistics rather than reproducible waveforms, you only need to sample at a rate suitable to give the desired confidence margin within the specified read-out time.  The CPU could wake up every couple of minutes, burst a few samples, and sleep.  Then every say 16 of those cycles, crank the samples through the statistics function and prepare a new result.  This would then be polled (maybe filtered again, since you can afford it) at the hourly-ish rate, or whatever.

Tim
Seven Transistor Labs, LLC
Electronic design, from concept to prototype.
Bringing a project to life?  Send me a message!
 
The following users thanked this post: 84750Erik

Online Zero999

  • Super Contributor
  • ***
  • Posts: 19904
  • Country: gb
  • 0999
Re: Duty Cycle detection
« Reply #32 on: October 14, 2017, 07:27:06 pm »
Low pass filter the PWM and use a conventional comparator with feedback for hysteresis?

If the frequency is not stable then this is what I would do.  The input levels to the filter and the comparator need to either be stable or ratiometric which means truing up the TTL signal.  Note that TTL is *not* 0 to 5 volts.
(...)

Note that this circuit might benefit from a comparator with a higher common mode input range or run of a higher supply voltage, which might require changing the values of R1 to R4. The LM393 is only specified to 1.5V below the positive rail, yet this circuit takes the inputs on U2 to nearly 3.6V.
Can you suggest a comparator per your latest sentence please?
On second thoughts, the LM393 will do, with some modifications to the circuit. R11 is added, to reduce the maximum voltage on C1 to 4.545V and change the values of the resistors around U2 to compensate.
 
The following users thanked this post: 84750Erik

Offline 84750ErikTopic starter

  • Contributor
  • Posts: 41
  • Country: be
    • My Safety
Re: Duty Cycle detection
« Reply #33 on: October 14, 2017, 08:50:02 pm »
(...)

You do have to worry about sample rate and all that, but since you want statistics rather than reproducible waveforms, you only need to sample at a rate suitable to give the desired confidence margin within the specified read-out time.  The CPU could wake up every couple of minutes, burst a few samples, and sleep.  Then every say 16 of those cycles, crank the samples through the statistics function and prepare a new result.  This would then be polled (maybe filtered again, since you can afford it) at the hourly-ish rate, or whatever.

Tim
Hi, can you please help with something more specific? I have been reading here about SPI ports to be used, about interupts,.. I am a bit confused:
1. does it matter which digital input on an Arduino to use? If so, why and which one?
2. Your proposal sounds good but I would not even go that far; I would just take samples once an hour during 10 or so seconds after a 5 second warm-up and process the results (count number of LOW and HIGH readings and process). What is your take on that?
3. Can you get me some flow chart or sample code or a link to where I can find more specific coding examples or hints?

Thank you, Erik
 

Offline 84750ErikTopic starter

  • Contributor
  • Posts: 41
  • Country: be
    • My Safety
Re: Duty Cycle detection
« Reply #34 on: October 14, 2017, 08:54:27 pm »
(...)
On second thoughts, the LM393 will do, with some modifications to the circuit. R11 is added, to reduce the maximum voltage on C1 to 4.545V and change the values of the resistors around U2 to compensate.

ok, thanks; I have the hardware and the software route to experiment with: I will investigate both solutions, since time is not an issue -reliability is- I can afford it.
 

Online Zero999

  • Super Contributor
  • ***
  • Posts: 19904
  • Country: gb
  • 0999
Re: Duty Cycle detection
« Reply #35 on: October 14, 2017, 10:24:13 pm »
Personally I'd choose the software/digital solution. It should be trivial with a microcontroler and will be more accurate than an analogue solution.
 
The following users thanked this post: 84750Erik

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1767
  • Country: se
Re: Duty Cycle detection
« Reply #36 on: October 14, 2017, 10:34:19 pm »
(...)

You do have to worry about sample rate and all that, but since you want statistics rather than reproducible waveforms, you only need to sample at a rate suitable to give the desired confidence margin within the specified read-out time.  The CPU could wake up every couple of minutes, burst a few samples, and sleep.  Then every say 16 of those cycles, crank the samples through the statistics function and prepare a new result.  This would then be polled (maybe filtered again, since you can afford it) at the hourly-ish rate, or whatever.

Tim
Hi, can you please help with something more specific? I have been reading here about SPI ports to be used, about interupts,.. I am a bit confused:
1. does it matter which digital input on an Arduino to use? If so, why and which one?
2. Your proposal sounds good but I would not even go that far; I would just take samples once an hour during 10 or so seconds after a 5 second warm-up and process the results (count number of LOW and HIGH readings and process). What is your take on that?
3. Can you get me some flow chart or sample code or a link to where I can find more specific coding examples or hints?

Thank you, Erik
Using the SPI might be possible, but I don't see the advantage with respect to using one of the timers in the ATmega, it's a bit of a kludge and then one would have to count ones and zeros inside the received byte stream.

1. If you use a digital input, any unused digital (also analogue) pin of the Arduino would do, if you intend to use the comparator the choice is more limited: only one pin can the positive input, but the negative one can be assigned to any of the ADC inputs, if the ADC is disabled.

2. If that fits your precision and accuracy needs, I don't see any problem with your solution (but I'm not T3sl4co1l!).

3. As I pointed before the code associated with AN135 might be a good start: it's interrupt based, but well commented, and the AN explains the used algorithms.
It can be found here.

Using a SW only solution eschewing the timer peripheral and interrupts is also possible, just sampling the input in a tight loop and accumulating the zero and one results during your sampling time.
One must be careful, though:
  • Interrupts should be disabled, not to disturb the sampling. Not using Arduino myself I don't know if that's acceptable (but possibly yes, if no function relying on intterupts are used in the loop).
  • the code path for counting zeros and ones should be the same, otherwise results would be skewed.

Something like this:
Code: [Select]
    //  Untested code, just an incomplete sketch - pun partially intended

    // Variables 32 bit wide should be more than enough:
    // I would expect the sample rate to be quite less than 4MHz, giving
    // 1000 seconds as the longest possible sample window for a 32 bit
    // unsigned integer.
    uint32_t count = 0;                     // Counter for 0s + 1s
    uint32_t ones  = 0;                     // Counter for 1a
   
    noInterrupts();                         // Disable interrupts
    // Thw LOOPS_IN_5S constant should be tuned for the loop to last
    // about 5s, or whatever sampling time is needed
    while(count < LOOPS_IN_5S)
    {
        // Note that no "if" is used, the path for 0s and 1s is the same
        // To have an efficient loop, I would never use digitalRead(),
        // it should be changed to a simple register read.
        ones += digitalRead(inutPin);
        count++;
    }

    interrupts();                           // Re-enables interrupts.

    // Calculate the duty cycle in %. If more precision is needed
    // *1000 could be used, or floating point.
    uint16_t dutyPer100 = (ones*100)/count;

Hope this helps!
Nandemo wa shiranai wa yo, shitteru koto dake.
 
The following users thanked this post: 84750Erik

Online T3sl4co1l

  • Super Contributor
  • ***
  • Posts: 22374
  • Country: us
  • Expert, Analog Electronics, PCB Layout, EMC
    • Seven Transistor Labs
Re: Duty Cycle detection
« Reply #37 on: October 14, 2017, 11:08:43 pm »
Hi, can you please help with something more specific? I have been reading here about SPI ports to be used, about interupts,.. I am a bit confused:
1. does it matter which digital input on an Arduino to use? If so, why and which one?
2. Your proposal sounds good but I would not even go that far; I would just take samples once an hour during 10 or so seconds after a 5 second warm-up and process the results (count number of LOW and HIGH readings and process). What is your take on that?
3. Can you get me some flow chart or sample code or a link to where I can find more specific coding examples or hints?

Thank you, Erik

I don't know Arduino boards specifically, but the basic ATmega has options.  You're simply reading a digital pin state.  SPI can do that, USART can do that, GPIO can do that.

Serial might be less convenient, because you get bytes out, whereas you might want a unary bit stream.  You need to unpack the bytes to get a high-and-low count (which is kind of annoying to write out in C).

The basic operation might be this:
Code: [Select]
//  initialization
uint16_t mark = 0, period = 0;
while (read_pin == 0);

// pin just turned to 1, count 'on' duration
while (read_pin == 1) {
    mark++; period++;
    delay(SAMPLE_TIME);
}
// pin just turned to 0, count 'off' duration
while (read_pin == 1) {
    period++;
    delay(SAMPLE_TIME);
}
Repeat this a few times, sum up the marks and periods, and get duty_cycle = (uint16_t)((1 << 16) * mark / period) (for a 0.16 fixed point result).

Note that the above code hangs until an edge is received, and skips through rapidly if edges are much more common than expected.  This is a good example of fragile code.  Better to use for() to make a fixed duration loop -- it might be 10 second window you mentioned -- and count the occurrence of 1 conditionally.

The DSP method is similar, using accumulator variables that count up the input, but instead of doing a final division, the pin value is added (unconditionally) to the variable, then the variable's value is reduced proportionally each iteration.  That is, multiplied by a number slightly less than 1.0 -- the effect is to have a "leaky" accumulator, so that a constant stream of '1's will not make the accumulator eventually overflow, but instead it rises and levels off exponentially, just like the voltage on a capacitor.

Such a loop looks like,
Code: [Select]
uint32_t accumulator = 0;
const uint32_t GAIN_FACTOR = (1 << 16) * (;
for (NUM_SAMPLES) {
    accumulator += read_pin * (1 << 16);    //  read_pin should return 1 if true, 0 if false (e.g., (PORTA & (1 << PA3)) >> PA3) )
    accumulator = (accumulator * GAIN_FACTOR) >> 16;    //  the first multiplication technically has a 64-bit result.
    delay(SAMPLE_TIME);                                 //  Check the assembler output if it's doing the right thing
}                                                       //  or not, and add typecasts as needed.  My C is rusty.

Using interrupts, you'd have a counter running in the background (could be a hardware timer-counter) and sample it when an input edge occurs.  This gives you the mark and period counts without any extra thinking.

Make sure to filter the input, and prevent additional interrupt events from firing while the interrupt is being serviced (otherwise a nearby RF field will crash the MCU).

Using the timer-counter hardware, I believe you can use the input capture / gating pin to enable or trigger the counter, doing the same thing with almost no CPU involvement at all.

Obviously, the first two options chew CPU constantly, so if you don't need to do anything else, and you can interrupt this process to do other things (like serial or GPIO when reading out the data), that's fine.  The latter options can be tacked onto an existing build, assuming that build is, itself, not critical on timing (i.e. it can spare a few clock cycles to service the interrupts), and has the timer(s) and pins free.

The latter options are probably on the advanced side for someone only experienced with Arduino.  I would worry about the buginess of the Arduino libraries, as far as being able to support these kinds of functions, too.  At that point you might be better off with C in AVR Studio -- less buggy, much steeper learning curve.

Tim
« Last Edit: October 14, 2017, 11:40:25 pm by T3sl4co1l »
Seven Transistor Labs, LLC
Electronic design, from concept to prototype.
Bringing a project to life?  Send me a message!
 
The following users thanked this post: 84750Erik

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1767
  • Country: se
Re: Duty Cycle detection
« Reply #38 on: October 14, 2017, 11:34:30 pm »
This is another good way of counting, period by period.

Code: [Select]
//  initialization
uint16_t mark = 0, period = 0;
while (read_pin == 0);

// pin just turned to 1, count 'on' duration
while (read_pin == 1) {
    mark++; period++;
    delay(SAMPLE_TIME);
}
// pin just turned to 0, count 'off' duration
while (read_pin == 1) {
    period++;
    delay(SAMPLE_TIME);
}
But to have a fair measurement, the code path for counting zeros must not have a different length  from the one counting ones, the code here would favour zeroes, as I fear that SAMPLE_TIME cannot be so great to swamp the increment time, if any precision is needed.
Adding a "space++; " in the last loop would make them even. Make sure to use all the variables, or make them volatile, otherwise unused ones will be thrown away by the compiler.

Code: [Select]
My C is rusty.
QFT  >:D (I think some piece is missing in GAIN_FACTOR, and (1>>16) is 0)
But style points for using a const uint32_t: I mostly program in C, and tend to forget that Arduino is actually C++.
Nandemo wa shiranai wa yo, shitteru koto dake.
 
The following users thanked this post: 84750Erik

Online T3sl4co1l

  • Super Contributor
  • ***
  • Posts: 22374
  • Country: us
  • Expert, Analog Electronics, PCB Layout, EMC
    • Seven Transistor Labs
Re: Duty Cycle detection
« Reply #39 on: October 14, 2017, 11:42:44 pm »
Code: [Select]
My C is rusty.
QFT  >:D (I think some piece is missing in GAIN_FACTOR, and (1>>16) is 0)
But style points for using a const uint32_t: I mostly program in C, and tend to forget that Arduino is actually C++.

Fixed.  Obviously I meant to shift the whole result, not multiply ::) ;D

I even forget if const is in C or not... in any case, the intent is to use a constant or macro parameter there, which is also emphasized by the tradition of CAPITALS_AND_UNDERSCORES.

The use of int_t types is certainly intentional, giving explicit instruction that these variables are of the size they say they are.  I don't know if Arduino has an improved "int", but the idea of using "int" alone definitely feels wrong when an ATmega "int" might be 16 bits while an STM32 "int" might be 32 bits.  (On that note, 32 bits is fine for the 16 bit case, but the opposite is clearly not the case: that FIR filter needs the 16.16 fixed point to operate!)

Tim
« Last Edit: October 14, 2017, 11:45:24 pm by T3sl4co1l »
Seven Transistor Labs, LLC
Electronic design, from concept to prototype.
Bringing a project to life?  Send me a message!
 
The following users thanked this post: 84750Erik

Offline 84750ErikTopic starter

  • Contributor
  • Posts: 41
  • Country: be
    • My Safety
Re: Duty Cycle detection
« Reply #40 on: October 15, 2017, 01:59:23 am »
Personally I'd choose the software/digital solution. It should be trivial with a microcontroler and will be more accurate than an analogue solution.
I will pursue that path, I am convinced.

I wish to thank T3sl4co1l and newbrain for their contributions here in their last 4 posts. Since yesterday I am on vacation for a week with limited internet access and no way of doing any testing, and I will have to study in depth all of yours last four posts, so it may take a few days before I will be able to give any meaningfull response.
 

Online Ian.M

  • Super Contributor
  • ***
  • Posts: 13072
Re: Duty Cycle detection
« Reply #41 on: October 15, 2017, 03:17:10 am »
IMHO its well worth putting in the effort to off load as much as possible of the duty cycle measurement to on-chip peripheral modules, especially capturing edge times.
An ATmega would be I think OK: there's Atmel Application Note 135 that fits very well with what you're after, and other examples can be found on the web.
If you're familiar with Arduino it might even be possible to use its framework for the communications and sensor control, while resorting to direct register programming for duty cycle measurements.
A.N. AVR135 sourcecode:
http://www.atmel.com/images/AVR135.zip

It supports the ATmega328PB MCU, can be compiled with GCC and a quick inspection doesn't appear to have any serious conflicts with the Arduino framework - it doesn't use Timer 0, and the pins needed to link the two timers for the test version are available (PB3 - the test PWM out, is Arduino Uno digital pin 11 and PB0 - the capture input is digital pin 8 ) , so an Arduino ATmega328P port shouldn't be too difficult to put together and test.

Edit: added a space so pin 8 doesn't become a smiley
« Last Edit: October 25, 2017, 07:55:52 pm by Ian.M »
 

Offline ebclr

  • Super Contributor
  • ***
  • Posts: 2332
  • Country: 00
Re: Duty Cycle detection
« Reply #42 on: October 15, 2017, 06:10:08 am »
A double RC and a comparator with hysteresis is a simple an pure analog solution

Duty cycle AVG voltage proportional do duty cicle for fixed 2KhZ

 

Offline 84750ErikTopic starter

  • Contributor
  • Posts: 41
  • Country: be
    • My Safety
Re: Duty Cycle detection
« Reply #43 on: October 25, 2017, 07:47:28 pm »
I don't know Arduino boards specifically, but the basic ATmega has options.  You're simply reading a digital pin state.  SPI can do that, USART can do that, GPIO can do that.

Serial might be less convenient, because you get bytes out, whereas you might want a unary bit stream.  You need to unpack the bytes to get a high-and-low count (which is kind of annoying to write out in C).

The basic operation might be this:
Code: [Select]
//  initialization
uint16_t mark = 0, period = 0;
while (read_pin == 0);

// pin just turned to 1, count 'on' duration
while (read_pin == 1) {
    mark++; period++;
    delay(SAMPLE_TIME);
}
// pin just turned to 0, count 'off' duration
while (read_pin == 1) {
    period++;
    delay(SAMPLE_TIME);
}
Repeat this a few times, sum up the marks and periods, and get duty_cycle = (uint16_t)((1 << 16) * mark / period) (for a 0.16 fixed point result).

Note that the above code hangs until an edge is received, and skips through rapidly if edges are much more common than expected.  This is a good example of fragile code.  Better to use for() to make a fixed duration loop -- it might be 10 second window you mentioned -- and count the occurrence of 1 conditionally.

The DSP method is similar, using accumulator variables that count up the input, but instead of doing a final division, the pin value is added (unconditionally) to the variable, then the variable's value is reduced proportionally each iteration.  That is, multiplied by a number slightly less than 1.0 -- the effect is to have a "leaky" accumulator, so that a constant stream of '1's will not make the accumulator eventually overflow, but instead it rises and levels off exponentially, just like the voltage on a capacitor.

Such a loop looks like,
Code: [Select]
uint32_t accumulator = 0;
const uint32_t GAIN_FACTOR = (1 << 16) * (;
for (NUM_SAMPLES) {
    accumulator += read_pin * (1 << 16);    //  read_pin should return 1 if true, 0 if false (e.g., (PORTA & (1 << PA3)) >> PA3) )
    accumulator = (accumulator * GAIN_FACTOR) >> 16;    //  the first multiplication technically has a 64-bit result.
    delay(SAMPLE_TIME);                                 //  Check the assembler output if it's doing the right thing
}                                                       //  or not, and add typecasts as needed.  My C is rusty.

Using interrupts, you'd have a counter running in the background (could be a hardware timer-counter) and sample it when an input edge occurs.  This gives you the mark and period counts without any extra thinking.

Make sure to filter the input, and prevent additional interrupt events from firing while the interrupt is being serviced (otherwise a nearby RF field will crash the MCU).

Using the timer-counter hardware, I believe you can use the input capture / gating pin to enable or trigger the counter, doing the same thing with almost no CPU involvement at all.

Obviously, the first two options chew CPU constantly, so if you don't need to do anything else, and you can interrupt this process to do other things (like serial or GPIO when reading out the data), that's fine.  The latter options can be tacked onto an existing build, assuming that build is, itself, not critical on timing (i.e. it can spare a few clock cycles to service the interrupts), and has the timer(s) and pins free.

The latter options are probably on the advanced side for someone only experienced with Arduino.  I would worry about the buginess of the Arduino libraries, as far as being able to support these kinds of functions, too.  At that point you might be better off with C in AVR Studio -- less buggy, much steeper learning curve.

Tim
Hi, here we are back after a week vacation, I took some time studying your proposals and ideas but to be honest it seems a bit above my capacities currently.
For sure, I will pursue the software path using an MCU to count 1's and 0's in the duty cycle. I would need a precision and repeatability of measurements of better than 1% but that should be attainable when using fast direct register readings. Using the analog approach will not cut this.
However, I feel lost when trying to understand your code proposals, and this has to do with my lack of knowledge, and nothing with your educational skills which are excellent.

Basically I would propose to connect the signal containing the duty cycle to be measured to a digital input of an Arduino (no I2C since I need that, not to ISP due to correct previous reasoning against its use). Then on wake-up, once an hour, I would wait for a rising edge, count the length of time for the edge to rise again, calculate the period (which will be approx. 420 microseconds or a frequency of about 2.4kHz, and simultaneously count the number of 1's (or 0's) in that period. Repeat this a few hundred times (or tens of times) and calculate the averages, and from there the duty cycle.

The hard part for me is to translate this routine in C++ as well as using direct register addressing for that purpose.

Luckily the microcontroller does not have to do anything else in the meantime. All that is needed is
1. wakeup after a call from a DS3231 RTC
2. wait a shirt while for the sensor to settle (5 seconds?)
3. do the measurements at the digital input
4. convert this value to a string
5. transmit this string through hardware serial to a HC-12 434MHz transceiver.
6. go back to extended sleep

Now after studying for a few hours the code in saw in the posts since my last writing 10 days ago I realise my head is turning in circles. The application note AVR135 I read, however not in detail, is indeed right on the mark, but here too I get lost for the time being in its technical prowess.

I will delve in detail in that note the coming days, but any additional help will be very much appreciated!!

Thanks,
Erik
 

Offline 84750ErikTopic starter

  • Contributor
  • Posts: 41
  • Country: be
    • My Safety
Re: Duty Cycle detection
« Reply #44 on: October 25, 2017, 07:49:11 pm »
A double RC and a comparator with hysteresis is a simple an pure analog solution

Duty cycle AVG voltage proportional do duty cicle for fixed 2KhZ


Thanks ebclr but I decided to pursue the software route: the precision and repeatability of measurements will be a leap better.
 

Offline 84750ErikTopic starter

  • Contributor
  • Posts: 41
  • Country: be
    • My Safety
Re: Duty Cycle detection
« Reply #45 on: October 25, 2017, 07:53:31 pm »
This is another good way of counting, period by period.

But to have a fair measurement, the code path for counting zeros must not have a different length  from the one counting ones, the code here would favour zeroes, as I fear that SAMPLE_TIME cannot be so great to swamp the increment time, if any precision is needed.
Adding a "space++; " in the last loop would make them even. Make sure to use all the variables, or make them volatile, otherwise unused ones will be thrown away by the compiler.

(...)

QFT  >:D (I think some piece is missing in GAIN_FACTOR, and (1>>16) is 0)
But style points for using a const uint32_t: I mostly program in C, and tend to forget that Arduino is actually C++.
Hi newbrain, can you please elaborate on the use of "space++; "?
I also think that counting, period by period, will be the best solution. But why would that previous code favour zeroes?
And I also do not understand the use of "GAIN_FACTOR", and of "1>>16)"

Thank you for any further help!!
Erik
 

Offline technix

  • Super Contributor
  • ***
  • Posts: 3508
  • Country: cn
  • From Shanghai With Love
    • My Untitled Blog
Re: Duty Cycle detection
« Reply #46 on: October 25, 2017, 07:58:55 pm »
This seem to me like a perfect job for the less-than-15-cents-in-quantity-of-10 STC15W100... Although the method below can work on any chip that supports either triggering interrupts on both edges, or have two interrupt lines that can be tied together and configured into triggering on either edge.

Set a free-running timer at 30MHz, and interrupt on both edges of the signal. Now you can determine how many counts the timer have went through for each high and low period.
 

Offline 84750ErikTopic starter

  • Contributor
  • Posts: 41
  • Country: be
    • My Safety
Re: Duty Cycle detection
« Reply #47 on: October 25, 2017, 08:13:05 pm »
Hi Technix, nice to read, except: I have no idea how to program the STC15W100: if possible I would prefer to stay with Atmel chips, I already have a steep learning curve on those.
Would it be possible to help with somewhat more precise coding examples or hints, preferably for Atmel AVR?
 

Offline 84750ErikTopic starter

  • Contributor
  • Posts: 41
  • Country: be
    • My Safety
Re: Duty Cycle detection
« Reply #48 on: October 25, 2017, 08:38:49 pm »
IMHO its well worth putting in the effort to off load as much as possible of the duty cycle measurement to on-chip peripheral modules, especially capturing edge times.
An ATmega would be I think OK: there's Atmel Application Note 135 that fits very well with what you're after, and other examples can be found on the web.
If you're familiar with Arduino it might even be possible to use its framework for the communications and sensor control, while resorting to direct register programming for duty cycle measurements.
A.N. AVR135 sourcecode:
http://www.atmel.com/images/AVR135.zip

It supports the ATmega328PB MCU, can be compiled with GCC and a quick inspection doesn't appear to have any serious conflicts with the Arduino framework - it doesn't use Timer 0, and the pins needed to link the two timers for the test version are available (PB3 - the test PWM out, is Arduino Uno digital pin 11 and PB0 - the capture input is digital pin 8 ) , so an Arduino ATmega328P port shouldn't be too difficult to put together and test.

Edit: added a space so pin 8 doesn't become a smiley
I downloaded AVR135, if I understand you well this should do on a Atmega328P, with pin 8 as digital input capture?
I already have a 2.4kHz signal with variable duty cycle. I tried to compile the code for Arduino Uno but I get an error, missing inavr.h, and stdio.h, amongst others. Now I will try and find these libraries.
 

Offline technix

  • Super Contributor
  • ***
  • Posts: 3508
  • Country: cn
  • From Shanghai With Love
    • My Untitled Blog
Re: Duty Cycle detection
« Reply #49 on: October 25, 2017, 09:02:53 pm »
Hi Technix, nice to read, except: I have no idea how to program the STC15W100: if possible I would prefer to stay with Atmel chips, I already have a steep learning curve on those.
Would it be possible to help with somewhat more precise coding examples or hints, preferably for Atmel AVR?
AVR can be a lot more expensive than discrete solutions. STC15W100 is a 8051-core microcontroller, and super, super cheap.

I am going to assume the target chip being ATtiny13, which tend to be a lot cheaper.

Code: [Select]
/*
 * DutyRatio13.c
 *
 * Created: 2017/10/26 4:21:58
 * Author : technix
 */

#define F_CPU 9600000

#define THRESHOLD_LOW  75
#define THRESHOLD_HIGH 80

#include <avr/io.h>
#include <avr/interrupt.h>

/*
 * Pin allocations:
 *
 * PB3 = PWM input (PCINT3)
 * PB4 = triggered output
 */

uint8_t last_count = 0;

uint8_t last_h = 0;
uint8_t last_l = 0;

int main(void)
{
// Set up a free-running counter on Timer/Counter0 at 150kHz
TCNT0 = 0;
TCCR0A = 0;
TCCR0B = _BV(CS01) | _BV(CS00);

// Prepare PB3 and PB4
DDRB = _BV(DDB4);
PORTB = 0;

// Set up PCINT on PB3
GIFR |= _BV(PCIF);
PCMSK |= _BV(PCINT3);
GIMSK |= _BV(PCIE);
sei();

    while (1)
    {
; // Do nothing. Maybe enter sleep mode?
    }
}

ISR(PCINT0_vect)
{
uint8_t count = TCNT0;

if (PINB & _BV(PINB3))
{
last_l = count - last_count;
last_count = count;
}
else
{
last_h = count - last_count;
last_count = count;
}

uint16_t last_cycle = (uint16_t)last_l + (uint16_t)last_h;

if (last_cycle)
{
uint16_t duty = (uint16_t)last_h * 100 / last_cycle;

if ((PORTB & _BV(PORTB4)) && (duty < THRESHOLD_LOW))
PORTB &= ~_BV(PORTB4);
else if (!(PORTB & _BV(PORTB4)) && (duty >THRESHOLD_HIGH))
PORTB |= _BV(PORTB4);
}
}

FUSES =
{
.low = FUSE_SPIEN & FUSE_SUT0 & FUSE_CKSEL0, // 9.6MHz
.high = FUSE_SELFPRGEN
};

 
The following users thanked this post: 84750Erik


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf