Page 1 of 1

Very accurate pulse counting

Posted: Mon Nov 25, 2019 10:23 am
by SergeyMinin
There is a need to calibrate the radio transmitter using GPS. To do this, we get very accurate pulses every one second from the GPS receiver. Using XTHAL_GET_CCOUNT, we determine the deviation in the frequency of the ESP32. In order to determine the deviation from the side of the radio transmitter, we get pulses equal to 1/30 of the frequency of its crystal, which is equal to the frequency of 1.066666 MHz. Crystal frequency of the radio transmitter is 32 MHz. To obtain the necessary accuracy, we need 1,000,000 counts. Using XTHAL_GET_CCOUNT at the first and last count, we can determine the frequency deviation of the radio transmitter with high accuracy and make a correction.

But we have a problem that we cannot solve. ESP32 loses some of the pulses, which is why it is not possible to perform accurate measurements. The measurement time varies in the wide range that is not acceptable for the required accuracy. Measurement of pulses from a radio transmitter using an oscilloscope showed their high stability (0.1V / 3.3V, flat edges, 1.066666 MHz). At the same time, there are no problems with measuring pulses from GPS. Jitter is about 10 ESP32 cycles, which is enough for the necessary accuracy.

We used "driver/pcnt.h" to count pulses from the radio transmitter. In conjunction with the ISR. The PCNT counted 10,000 (int16_t counter) samples and caused an interrupt. The interrupt counted 100 samples, recording the ESP tick (XTHAL_GET_CCOUNT) of the first and last samples. The output from the radio transmitter used to generate the pulses is connected to GPIO_NUM_34 ESP32.

We will be very grateful for your advice on how to solve this problem.

  1. volatile uint32_t xthal1 = 0;
  2. volatile static uint32_t xthal_counter = 0;
  3.  
  4. static void IRAM_ATTR Radio_CAL_ISR_handler(void* arg)
  5. {
  6.     static BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  7.     static uint32_t xthal_val = 0;
  8.  
  9.     xthal_val =  XTHAL_GET_CCOUNT();
  10.     PCNT.int_clr.val = 1;
  11.     if (xthal_counter == 0) {
  12.         xthal1 = xthal_val;
  13.     } else {
  14.         if (xthal_counter >= 100)
  15.         {
  16.             pcnt_counter_pause(((Radio_device_t*)arg)->pcnt_unit);
  17.             ((Radio_device_t*)arg)->cal_xthal = xthal_val - xthal1;
  18.             xSemaphoreGiveFromISR( ((Radio_device_t*)arg)->Cal.sem, &xHigherPriorityTaskWoken );
  19.             if( xHigherPriorityTaskWoken != pdFALSE ) { portYIELD_FROM_ISR(); }
  20.         }
  21.     }
  22.     ++xthal_counter;
  23. }
  24.  
  25. void Radio_PCNT_init(Radio_device_t* device)
  26. {
  27.     pcnt_config_t pcnt_config = {
  28.         // Set PCNT input signal and control GPIOs
  29.         .pulse_gpio_num = device->DIO0_pin,
  30.         .ctrl_gpio_num = PCNT_PIN_NOT_USED,
  31.         .channel = device->pcnt_channel,
  32.         .unit = device->pcnt_unit,
  33.         // What to do on the positive / negative edge of pulse input?
  34.         .pos_mode = PCNT_COUNT_INC,   // Count up on the positive edge
  35.         .neg_mode = PCNT_COUNT_DIS,   // Keep the counter value on the negative edge
  36.         // What to do when control input is low or high?
  37.         .lctrl_mode = PCNT_MODE_KEEP, // Reverse counting direction if low
  38.         .hctrl_mode = PCNT_MODE_KEEP,    // Keep the primary counter mode if high
  39.         // Set the maximum and minimum limit values to watch
  40.         .counter_h_lim = 10000,
  41.         .counter_l_lim = 0,
  42.     };
  43.     /* Initialize PCNT unit */
  44.     pcnt_unit_config(&pcnt_config);
  45.  
  46.     /* Initialize PCNT's counter */
  47.     pcnt_counter_pause(device->pcnt_unit);
  48.     pcnt_counter_clear(device->pcnt_unit);
  49.  
  50.     /* Configure and enable the input filter */
  51. //    pcnt_set_filter_value(device->pcnt_unit, 20);
  52. //    pcnt_filter_enable(device->pcnt_unit);
  53.  
  54.     pcnt_event_enable(device->pcnt_unit, PCNT_EVT_H_LIM);
  55.  
  56.     device->Cal.counter = 0;
  57.     device->cal_xthal = 0;
  58.  
  59.     /* Register ISR handler and enable interrupts for PCNT unit */
  60.     // I tried levels above 3, but it does not work with them, the semaphore task does not receive
  61.     pcnt_isr_register(&Radio_CAL_ISR_handler, device, ESP_INTR_FLAG_LEVEL3, &device->pcnt_isr_handle_Dio0);
  62.     pcnt_intr_enable(device->pcnt_unit);
  63.  
  64.     /* Everything is set up, now go to counting */
  65.     pcnt_counter_resume(device->pcnt_unit);
  66. }
  67.  
  68. void Radio_cal_task (void *pvParameters)
  69. {
  70.     Radio_device_t* device = pvParameters;
  71.     xSemaphoreTake( device->Cal.sem, 0);
  72.     device->cal_xthal = 0;
  73.  
  74.     if (xSemaphoreTake(device->Cal.sem, pdMS_TO_TICKS(5000)) != pdTRUE )
  75.     {
  76.         ESP_LOGE(TAG, "Calibration timeout error");
  77.     }
  78.  
  79.     pcnt_counter_pause(device->pcnt_unit);
  80.     pcnt_event_disable(device->pcnt_unit, PCNT_EVT_H_LIM);
  81.     pcnt_intr_disable(device->pcnt_unit);
  82.     esp_intr_free(device->pcnt_isr_handle_Dio0);
  83.  
  84.     double freq = (double)device->cal_xthal / (240000000 + 0);
  85.     freq = freq / 100 / 10000 / 30;
  86.     freq = 1 / freq;
  87.  
  88.     printf("Radio XTHAL diff: %u; ISR counter: %u; Freq: %.3f\n", device->cal_xthal, xthal_counter, freq);
  89.     vTaskDelete(NULL);
  90. }
Result (the calculated frequency should be in the region of 32 MHz +- 500 Hz, XTHAL diff: 240,000,000 +- 3750):
Radio XTHAL diff: 275949921; ISR counter: 101; Freq: 26091690.746
Radio XTHAL diff: 276206703; ISR counter: 101; Freq: 26067433.997
Radio XTHAL diff: 277011921; ISR counter: 101; Freq: 25991661.204
Radio XTHAL diff: 276995640; ISR counter: 101; Freq: 25993188.918
Radio XTHAL diff: 277033722; ISR counter: 101; Freq: 25989615.806

Re: Very accurate pulse counting

Posted: Mon Nov 25, 2019 8:07 pm
by SergeyMinin
I'm sorry, everything works as it should. The problem turned out to be in the operating mode of the radio module, measurements were carried out at an unstable output frequency.