NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Support Forum » Pulse counting, what am I actually seeing?

January 05, 2011
by Ralphxyz
Ralphxyz's Avatar

This is in reference to my Calculating RPM thread.

What exactly is DutyCycle?

This originates here

And here is the working code:

#define F_CPU 14745600

#include <stdio.h>
#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include "../libnerdkits/uart.h"
#include "../libnerdkits/lcd.h"

#include <avr/pgmspace.h>
//#include <inttypes.h>

//#include "../libnerdkits/io_328p.h"
//#include "../libnerdkits/delay.h"

//Counts overflovs
volatile uint16_t T1Ovs1, T1Ovs2;
//Variables holding three timestamps
volatile uint16_t Capt1, Capt2, Capt3; 
//capture Flag
volatile uint8_t Flag;
//Initialize timer
void InitTimer1(void)
{
//Set Initial Timer value
TCNT1=0;
//First capture on rising edge
TCCR1B|=(1<<ICES1);
//Enable input capture and overflow interrupts
TIMSK1|=(1<<ICIE1)|(1<<TOIE1);
}
void StartTimer1(void)
{
//Start timer without prescaller
TCCR1B|=(1<<CS10);
//Enable global interrutps
sei();
}

//capture ISR
ISR(TIMER1_CAPT_vect)
{
if (Flag==0)
    {
        //save captured timestamp
        Capt1=ICR1;
        //change capture on falling edge
        TCCR1B&=~(1<<ICES1);
        //reset overflows
        T1Ovs2=0;
    }
if (Flag==1)
    {
        Capt2=ICR1;
        //change capture on rising edge
        TCCR1B|=(1<<ICES1);
        //save first overflow counter
        T1Ovs1=T1Ovs2;
    }
if (Flag==2)
    {
        Capt3=ICR1;
        //stop input capture and overflow interrupts
        TIMSK1&=~((1<<ICIE1)|(1<<TOIE1));
    }
//increment Flag
Flag++;
}
//Overflow ISR
ISR(TIMER1_OVF_vect)
{
//increment overflow counter
T1Ovs2++;
}
int main(void)
{
//dutycycle result holder
volatile uint8_t DutyCycle;
// init lcd
lcd_init();
FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
lcd_home();

// start up the serial port
uart_init();
FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
stdin = stdout = &uart_stream;

InitTimer1();
StartTimer1();
    while(1)
    {
        //calculate duty cycle if all timestamps captured
        if (Flag==3)
        {
                DutyCycle=(uint8_t)((((uint32_t)(Capt2-Capt1)+((uint32_t)T1Ovs1*0x10000L))*100L) 
                    /((uint32_t)(Capt3-Capt1)+((uint32_t)T1Ovs2*0x10000L)));

                //send Duty Cycle value to LCD or USART
                lcd_home();
                fprintf_P(&lcd_stream, PSTR("DutyCycle %16.2d"), DutyCycle);

                // init serial port
                uart_init();

                // write message to serial port
                //printf_P(PSTR("Sensor:\n                    "));

                //clear flag
                Flag=0; 
                //clear overflow counters;
                T1Ovs1=0;
                T1Ovs2=0;
                //clear interrupt flags to avoid any pending interrupts
                TIFR1|=(1<<ICF1)|(1<<TOV1);
                //enable input capture and overflow interrupts 
                TIMSK1|=(1<<ICIE1)|(1<<TOIE1);
        }
    }
}

So my question is what is DutyCycle?

Is it processor clock ticks?

void StartTimer1(void)
{
//Start timer without prescaller
TCCR1B|=(1<<CS10);
//Enable global interrutps
sei();
}

Thanks,

Ralph

January 05, 2011
by bretm
bretm's Avatar

Duty cycle is the percentage of time that a periodic digital signal is in the high state. It's the "on" time divided by the "total" time. A square wave has a duty cycle of 50%. A signal that is on 1/4 of the time and off 3/4 of the time has a duty cycle of 25%. The key bit of code here is (Capt2-Capt1)/(Capt3-Capt1). The numerator is the "on" time and the denominator is the total time.

January 05, 2011
by Ralphxyz
Ralphxyz's Avatar

Thanks bretm I really need that, but what is DutyCycle in the code?

When I see on the LCD "DutyCycle = 455" 455 what?

DutyCycle=(uint8_t)((((uint32_t)(Capt2-Capt1)+((uint32_t)T1Ovs1*0x10000L))*100L) 
                    /((uint32_t)(Capt3-Capt1)+((uint32_t)T1Ovs2*0x10000L)));

Is this giving a percentage and therefore a duty cycle? I do not understand the logic or the math or why the cast are needed.

Using my RPMsimulator code which has the DutyCycle code I see this:

@ ADC:    0 of 1024 the LED flashes rapidly and I get a DutyCycle between 395 and 415.
@ ADC:  521 of 1024 the LED flashes slower  and I get a DutyCycle of 715
@ ADC: 1023 of 1024 the LED flashes slowly   and I get a DutyCycle of 485

I am using the tempsensor code (modified) to generate LED flash (pulse) and capturing the pulse with the Input Capture Pin (PB0) which the DutyCycle code is using as it's basis.

Ralph

January 05, 2011
by bretm
bretm's Avatar

DutyCycle is a uint8_t. It's impossible for it to be more than 255 because of that. I'd guess that what you're seeing is 39, 41, 71, 48, and not 395, 415, 715, 485.

The '5' at the end seems to be display garbage coming from somewhere else. You should add two spaces before the closing quote, e.g. "Duty Cycle: %d " to see if that helps. That way if a 100 is followed by a single-digit number, the two 0's will get erased.

If it does help, I expect there's some timing issue causing a capture event to be missed and wrong values calculated, e.g. "105" which would write the "5" in the third digit position.

January 05, 2011
by bretm
bretm's Avatar

The casts force the compiler to do 32-bit multiplication, which is necessary because the two multiplicands are 16 bits each so the compiler would normally use 16-bit multiplication. But these intermediate results would overflow a 16-bit value.

January 06, 2011
by Ralphxyz
Ralphxyz's Avatar

bretm thank you so much, this is actually starting to make sense!!

I added the two spaces before the closing quote and now I see much better results.

ADC:    0 of 1024 DutyCycle is 39 - 43
ADC:  502 of 1024 DutyCycle is 8
ADC: 1023 of 1024 DutyCycle is 4

It is interesting that DutyCycle is not linear to the ADC: input but that is not what I need to be concerned with at the moment.

Thanks for explaining the cast now I see it.

I wish I had a scope to see the pulses but just flashing a LED I can grasp the time/cycle.

Maybe in my spare time I'll send this to my pc and use Python to generate a graph.

Ralph

Post a Reply

Please log in to post a reply.

Did you know that you can aim and fire a servo-controlled water gun from your computer? Learn more...