PWM duty cycle + freq measurement

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
MaBe_4065826
Level 1
Level 1

I followed this thread: https://community.cypress.com/servlet/JiveServlet/download/134688-28018/Pulse%20Width%20and%20Period... and use this example on PSoC 5 (using CYKIT-059). I checked that PWM ~1kHZ and removed from code lcd display and add UARt  TX component to see output. When capturing I get strange values like:

pulse:9000

pulse:870

pulse:4000

pulse:238830

pulse:4000

pulse:1000

pulse:1000

pulse:1000

pulse:0

pulse:1000

pulse:1000

pulse:1000

pulse:1000

pulse:16000

pulse:1000

pulse:1000

pulse:1010

pulse:1000

pulse:8000

pulse:990

any ideas how to check that? I also plan to monitor 5 PWm input signals is there enough resources on this dev board? Thanks.

BR,

marek

0 Likes
1 Solution
lock attach
Attachments are accessible only for community members.
Len_CONSULTRON
Level 9
Level 9
Beta tester 500 solutions authored 1000 replies posted

marek,

I've modified the original program intended for the 050 eval board with LCD for your 059-KIT and the USB-UART.

Here is a summary of the modifications:

  • I added 4 additional stimulus sources
    • (see the schematic snippet below).  The stimulus outputs are on P3.0, P3.1, P3.2 and P3.3.  The original test stim is on P0.7.

pastedImage_2.png

  • I deleted the LCD code references and added the UART calls to the USB_UART.
  • I converted the ISR calls to cyapicallbacks.  I believe this is a 'safer' way to implement component level ISRs.

Question:

When you were performing your original measurements, were you externally connecting the test stim output (P0.7) to the pulse monitor input (P0.5)?   I get similar 'floating' reads if I don't connect P0.5 to a stimulus source.   Otherwise, when I connect the 5 stimulus sources to P0.5 (one at a time) I get a reasonably value period and duty cycle.

Note:

  • If the output frequency is too close to the sampling frequency (100KHz), the SW can't keep up reliably.  As I get closer to the sampling frequency, the period is not as accurate.  Eventually as the measure frequency is much nearer the sampling frequency, the SW appears to lockup (spending too much time in the ISRs) until I open up the input or switch to another slower stimulus.
  • If the output frequency is too low, the wrap-around effect appears to become more significant and the period is not correct and/or the duty cycle reads greater than 100% which is definitely wrong.

Attached is my modified code if you want to check it out.

If you need to support frequency/period and duty-cycle measurements with more accuracy and reliability, you should set up HW counters with capture inputs.  This allows the Counter HW to save the count value on the capture edge.  Using the SW ISRs as being used in the project, is good if the inputs are relatively slow (not too slow) and you can support all the inputs you need with enough CPU speed.

Using the capture counters allow for more accurate results.  This is because the counter count is loaded into a FIFO storage at the edge of capture.   This lowers CPU usage and since there are four FIFO'd capture counts, the time criticality is not as bad.  Using the ISRs means that the CPU tries to read the counter value as quickly as possible but the count might advance before the CPU can read it in the ISR.

Len

Len
"Engineering is an Art. The Art of Compromise."

View solution in original post

0 Likes
10 Replies
Len_CONSULTRON
Level 9
Level 9
Beta tester 500 solutions authored 1000 replies posted

marek,

I just downloaded the project and will get back to you if I can reproduce your results.

Is this your application design?

I noticed you are using a 16bit counter with a 100KHz clock.  This means the count value wraps back to 0 every 65536/100000 = 0.65536 seconds.  I see there is counter rollover compensation.  Therefore this shouldn't be an issue if your frequency is not too slow.

"I also plan to monitor 5 PWm input signals is there enough resources on this dev board?"

Using an ISR for each PWM input you could support an infinite number of inputs.  You're only limited by the CPU processing frequency and RAM resources.

Len

Len
"Engineering is an Art. The Art of Compromise."
0 Likes

Hi Len,

I've pushed modified project here: Pulse Width Period Dutry Cycle.zip - Google Drive

it's original project where there is 1kHz pwm generator and for capturing I'm getting this strange results. I'm not sure yet if I need to monitor them in parallel or not but thanks for hint.

For monitoring values I connected USB->serial converter (TX ping is P1.7) and GND and get this values. Also duty cycle is 0 which is not good. Thanks.

Marek

0 Likes
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Hi,

FYI, in the following topic, I tried something similar.

PSoC 5LP Duty Cycle

But I'm afraid that my approach won't allow you to measure 5 PWMs.

moto

0 Likes

Thanks. I think original approach should work just there is some issue why it's not working always. Anyway thanks for sharing.

marek

0 Likes
lock attach
Attachments are accessible only for community members.
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Hi,

OK, I tried with your strategy.

To take care of roll over I added another interrupt for TC

000-schematic.JPG

As I want to have ISRs in my source file I modified main.c as below

=======================

#include <project.h>

#include <stdio.h>

#include <stdlib.h>

#include <cytypes.h>

volatile uint16 PulseHighTime      = 0; // pulsetime capture

volatile uint16 PulsePeriodStart    = 0; // period time capture start

volatile uint16 PulsePeriodEnd      = 0; // period time capture end

volatile uint8 flgPerdstart        = 0;    // Flag indicating + edge of pulse in has occured

volatile uint8 flgPerdend          = 0;    // Flag indicating + edge of next pulse has occured, signifying end of pepriod

char dispbuff[8];                        // LCD display buffer 8 bytes long

uint8 lenstrg;                        // result of string length

uint8 i                            = 0; // loop index

float dutycycle                    = 0.0;  // Variable to hold duty cycle computation

float pulseperd                    = 0.0;  // Pulse period in mS

uint16 perdcalc                    = 0;    // Intermediate value to aid in period measurement

float dutycalc                      = 0;    // Intermediate value to aid in duty cycle measurement

volatile uint32_t tc_count                    = 0 ;

volatile uint32_t duty_count                  = 0 ;

volatile uint32_t duty_tc                    = 0 ;

uint32_t          duty_cycle                  = 0 ;

volatile uint32_t period_count                = 0 ;

volatile uint32_t period_tc                  = 0 ;

uint32_t          period_cycle                = 0 ;

volatile int      measuring                  = 0 ;

#define STR_LEN 128

char              str[STR_LEN+1] ; /* print buffer */

CY_ISR(counter_tc_isr)

{

    Counter_ReadStatusRegister() ;

    tc_count++ ;

}

CY_ISR(rising_isr)

{

    PeriodISR_ClearPending() ;

    if (measuring) { /* end measure */

        period_count = Counter_ReadCounter() ;

        period_tc    = tc_count ;

        measuring = 0 ;

        Counter_Stop() ;

        Counter_WriteCounter(0) ;

    } else {        /* start measure */

        measuring = 1 ;

        tc_count = 0 ;

        Counter_Enable() ;

    }

}

CY_ISR(falling_isr)

{

    PulsehiISR_ClearPending() ;

    if (measuring) {

        duty_count = Counter_ReadCounter() ;

        duty_tc = tc_count ;

    }

}

int main()

{

    int prev_measuring = 0 ;

    uint16_t period  ;

  

    UART_Start() ;

  

    PeriodISR_StartEx(rising_isr);    // Start period time ISR

    PulsehiISR_StartEx(falling_isr);    // Start high pulse time ISR

    isr_tc_StartEx(counter_tc_isr) ;        // tc counter ISR

    Counter_Init();                // Init measurement counter, but not start

    CyGlobalIntEnable;                      // Uncomment this line to enable global interrupts

  

    UART_PutString("Period, High, Duty\n") ;

  

    for(;;) {                // Infinite for-loop

        if ((prev_measuring == 1)&&(measuring == 0)) { // measuring done

            period = Counter_ReadPeriod() ;

            period_cycle = (period - period_count) + period_tc * period ;

            duty_cycle    = (period - duty_count) -  duty_tc * period ;

            snprintf(str, STR_LEN, "Cycle = %d ", period_cycle) ;

            UART_PutString(str) ;

            snprintf(str, STR_LEN, "High = %d ", duty_cycle) ;

            UART_PutString(str) ;

            snprintf(str, STR_LEN, "Duty = %d.%02 %\n",

                100 * duty_cycle / period_cycle,

                (10000 * duty_cycle / period_cycle) % 100

            ) ;

            UART_PutString(str) ;

        }

prev_measuring = measuring ;

    } // end of infinity for-loop

i='F'; //  for debugging only

} // end of main

=======================

Then when I ran with Clock_1 = 1kHz, I got around 21.0%, although expected 25.0%

000-TeraTerm-log.JPG

Then I changed Clock_1 to 1MHz, the duty is now 24.0% (getting close to 25.0%)

001-TeraTerm-log-1MHz.JPG

Then I tried Clock_1 = 12MHz, but duty accuracy did not get improved

002-TeraTerm-log-12MHz.JPG

So I think that either the ramp slope of the signal or the overhead of isr()s makes the accuracy not as good.

The sample I posted in the earlier response had accuracy of .01 % but required much more resource(s).

Attached is your project with my modification.

moto

0 Likes

marek,

Make sure you connect P0.7 (test stimulus output) to the input P0.5 (PWMxIN) externally.  The results you posted might be a floating P0.5.  One way to confirm this is to configure PWMxIN as an input with pull-up or pull-down.  If PWMxIN is left unconnected, then the output will always be constant.

Len

Len
"Engineering is an Art. The Art of Compromise."
0 Likes

Hi,

OK this works then but when PWM is not at input then program report random values. I think maybe code should be extended that if IRQ is not happening then disable capturing or is there other way how to detect if signal is present on pin? Thanks.

0 Likes
lock attach
Attachments are accessible only for community members.
Len_CONSULTRON
Level 9
Level 9
Beta tester 500 solutions authored 1000 replies posted

marek,

I've modified the original program intended for the 050 eval board with LCD for your 059-KIT and the USB-UART.

Here is a summary of the modifications:

  • I added 4 additional stimulus sources
    • (see the schematic snippet below).  The stimulus outputs are on P3.0, P3.1, P3.2 and P3.3.  The original test stim is on P0.7.

pastedImage_2.png

  • I deleted the LCD code references and added the UART calls to the USB_UART.
  • I converted the ISR calls to cyapicallbacks.  I believe this is a 'safer' way to implement component level ISRs.

Question:

When you were performing your original measurements, were you externally connecting the test stim output (P0.7) to the pulse monitor input (P0.5)?   I get similar 'floating' reads if I don't connect P0.5 to a stimulus source.   Otherwise, when I connect the 5 stimulus sources to P0.5 (one at a time) I get a reasonably value period and duty cycle.

Note:

  • If the output frequency is too close to the sampling frequency (100KHz), the SW can't keep up reliably.  As I get closer to the sampling frequency, the period is not as accurate.  Eventually as the measure frequency is much nearer the sampling frequency, the SW appears to lockup (spending too much time in the ISRs) until I open up the input or switch to another slower stimulus.
  • If the output frequency is too low, the wrap-around effect appears to become more significant and the period is not correct and/or the duty cycle reads greater than 100% which is definitely wrong.

Attached is my modified code if you want to check it out.

If you need to support frequency/period and duty-cycle measurements with more accuracy and reliability, you should set up HW counters with capture inputs.  This allows the Counter HW to save the count value on the capture edge.  Using the SW ISRs as being used in the project, is good if the inputs are relatively slow (not too slow) and you can support all the inputs you need with enough CPU speed.

Using the capture counters allow for more accurate results.  This is because the counter count is loaded into a FIFO storage at the edge of capture.   This lowers CPU usage and since there are four FIFO'd capture counts, the time criticality is not as bad.  Using the ISRs means that the CPU tries to read the counter value as quickly as possible but the count might advance before the CPU can read it in the ISR.

Len

Len
"Engineering is an Art. The Art of Compromise."
0 Likes

Hi,

thanks for update. Hmm I didn't have anything connected on P0.5 (PWM input). I think test generator from P0.7 would be used for that purpose? But I have no clue how to connect those 2 pins.  But when put input signal from external PWM to P0.5 then it works . Thanks a lot.

marek

0 Likes

marek,

A floating P0.5 input.  That would explain the results.

Since you are using the CY8CKIT-059, see the diagram below.  (Note:  In this case, GND is already internally connected on the Kit)

pastedImage_0.png

I'm glad you got it working.

Len

Len
"Engineering is an Art. The Art of Compromise."