This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

PWM Duty Cycle Measurement Spike

The servo I use in my application uses a PWM signal  duty cycle to represent servo amperage level.

So, to measure servo amperage, I use a GPIOTE channel to capture the time between rising and falling edges of the servo's PWM  amperage signal.  I then have an interrupt handler that reads the GPIO pin to determine whether or not the time was for a peak or valley and I calculate duty cycle appropriately.

The problem is at low duty cycles, when triggered by a peak,  the signal occasionally  changes prior to reading the pin in the interrupt handler routine.   This result is an inaccurate spike in servo amperage.   As I use the valley routine to calculate duty cycle when I should have used a peak routine.

I recognize that it is a timing issue and would like to overcome this with a robust solution.  

Can I rely on the latch function instead of reading the pin to determine if I'm  measuring the width of the peak or the width of a valley?

Or alternatively, Can a I make a pin read and GPIOTE event via PPI?

I've attached the code of my interrupt handler in case you need more detail.

void servo_amps_input_handler (nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
{
		static uint32_t servo_first_edge_capture, servo_second_edge_capture;
		static float32_t last_good_servo_amps;
		static uint16_t state; 

// PPI (channel3) channel event captures NRF_TIMER4 CC[0]
		
    switch (servo_edge)
    {
				case 0UL:
						servo_first_edge_capture = NRF_TIMER4->CC[0];
						finished_getting_servo_data = false;
						state = nrf_gpio_pin_read(I_OUT);
						if (state == 1) hi_state = true;
						else hi_state = false;
						servo_edge++;
						//SEGGER_RTT_printf(0," 1st I_OUT edge capture %d,  \r\n", servo_first_edge_capture);				
						break;
 
        case (1UL):
						nrf_drv_gpiote_in_event_disable(I_OUT);    // disable I_OUT pin interrupt
						nrf_drv_timer_compare_int_disable(&TIMER_AMPS, NRF_TIMER_CC_CHANNEL2); // disable NO_PULSE interupt
						servo_second_edge_capture = NRF_TIMER4->CC[0];
						servo_pulse_count = servo_second_edge_capture - servo_first_edge_capture;
						if (servo_pulse_count == 0)
							{
								servo_AMPS = last_good_servo_amps;								
							}
						else
							{
								if (state == 1) //we measured duration of a high pulse
								{
									servo_AMPS = servo_duty_amps * (float32_t)servo_pulse_count / servo_period_count;
								}
								else  //we measured duration of a low pulse
								{
									servo_AMPS = servo_duty_amps * (1.F - ((float32_t)servo_pulse_count / servo_period_count));
									
								}
								last_good_servo_amps = servo_AMPS;
							}
						IFLG_get_servo_amps = false;
						finished_getting_servo_data = true; 
						servo_edge++;
						//SEGGER_RTT_printf(0," 2nd I_OUT edge capture %d,  \r\n", servo_second_edge_capture);	
							
						break;
     
        default:  //We should never hit this
						servo_edge++;
						break;             
    } 	
	
	
	
}

Parents Reply Children
No Data
Related