cancel
Showing results for 
Search instead for 
Did you mean: 

Input Capture - Trouble Measuring Frequency

toti
Associate

Hello, i have been trying to measure the frequency of a pwm signal using input capture. I am using Nucleo-F429ZI I want to print the values of the frequency of the signal in question with UART. The frequency of my signal is 100 kHz but I read stuff like these on my serial monitor: (I printed diff instead of frequency to see what is the issue, it is the same with frequency just with bigger values.)

The oldest data was removed. Continue...
6
371
305
108
628
174
10578
482
410
364
375
355
365
365
365
13157
516
385
285
50218
20130
20048
20005
19942
20044
20016
19963

(I cut most of the data so that the post isn't too long but it is roughly like this. Constant values near 365 and occasianl continous irrelevant high values)


Below is the relevant parts of my code:

 
...


/* USER CODE BEGIN 0 */
uint32_t captureValue = 0;
uint32_t previousCaptureValue = 0;
uint16_t diff;
uint16_t frequency = 0;
char uart_buffer[32];
static uint8_t first_capture_flag = 1;
/* USER CODE END 0 */

...

int main(void){

...

  HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1);

...

}

...

/* USER CODE BEGIN 4 */


//CK_CNT = TIM_CLK / (PSC + 1) = PCLK1 * 2 / (PSC + 1) = 90 / (90 - 1 +1 ) = 1 MHz => counter_period = 1/1 MHz = 1 us
//
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
        captureValue = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
        if(first_capture_flag){
        	previousCaptureValue = captureValue;
        	first_capture_flag = 0;
        }
        if(captureValue > previousCaptureValue){
        	diff = captureValue - previousCaptureValue;
        	frequency = 1000000 / diff;
        }
        else if(captureValue < previousCaptureValue){
        	diff = 65535 - previousCaptureValue + captureValue;
        	frequency = 1000000 / diff;
        }
        previousCaptureValue = captureValue;


    if (frequency > 90000 && frequency < 110000){ //freq = 100 kHz
//    	sprintf(uart_buffer, "%hu\n", frequency);
//    	HAL_UART_Transmit(&huart4, (uint8_t*)uart_buffer, strlen(uart_buffer), 100);
    	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_SET);
    }

    else if (frequency > 45000 && frequency < 55000){ //freq = 50 kHz
//    	sprintf(uart_buffer, "%hu\n", frequency);
//    	HAL_UART_Transmit(&huart4, (uint8_t*)uart_buffer, strlen(uart_buffer), 100);
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_RESET);
	}
    sprintf(uart_buffer, "%hu\n", diff);
    HAL_UART_Transmit(&huart4, (uint8_t*)uart_buffer, strlen(uart_buffer), 100);


}
/* USER CODE END 4 */

...

 

3 REPLIES 3
waclawek.jan
Super User

Don't printf() nor HAL_UART_Transmit() in the interrupt.

JW

TDK
Super User

With a 100 kHz signal, you're going to need to use DMA to capture the edges into a buffer and read them back as the buffer fills up. Have a method for handling overflow, or not processing buffers quickly enough. Perhaps do single-shot DMA and restart it after you process the buffer.

Interrupting at 100 kHz isn't going to work, even if you remove the printf statements.

If you feel a post has answered your question, please click "Accept as Solution".
waclawek.jan
Super User

> Interrupting at 100 kHz isn't going to work

100kHz interrupt rate in a 180MHz processor is perfectly fine, unless you choke it by using "libraries" such as Cube/HAL and inadequate optimization (and of course any other interrupt of equal or higher priority). Even with Cube/HAL that rate should be IMO feasible with optimization. Of course, the processing needs to be minimal (insert Clive's mantra about utilizing modulo arithmetic), but lighting up a LED is IMO fine. We can discuss if it's wise to spend most of the computational power at this issue, but that depends on the application.

That measuring frequency can utilize the self-reset (with taking into account the small undocumented delay), is just another thing.

JW