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; } }