ref_clock_impl_rmt_pcnt.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. /*
  2. * SPDX-FileCopyrightText: 2017-2022 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. /**
  7. * Some unit test cases need to have access to reliable timestamps even when CPU and APB clock frequencies change over time.
  8. * This reference clock is built upon two peripherals: one RMT channel and one PCNT channel (hopefully we can have these two peripherals in all ESP chips).
  9. *
  10. * +---------------------+ 500KHz Square Wave +--------------------------+
  11. * | RMT (channel 0, TX) +----------------------------------->+ PCNT (unit 0, channel 0) |
  12. * +---------------------+ +--------------------------+
  13. *
  14. * RMT TX channel is configured to use a fixed clock (e.g. REF_TICK, XTAL) as clock source, so that our ref clock won't be affected during APB/CPU clock switch.
  15. * Configure RMT channel to generate a 500KHz square wave (using carrier feature) to one GPIO.
  16. * PCNT takes the input signal from the GPIO and counts the edges (which occur at 1MHz frequency).
  17. * PCNT counter is only 16 bit wide, an interrupt is configured to trigger when the counter reaches 30000,
  18. * incrementing a 32-bit millisecond counter maintained by software.
  19. */
  20. #include "sdkconfig.h"
  21. #include "unity.h"
  22. #include "test_utils.h"
  23. #include "freertos/FreeRTOS.h"
  24. #include "driver/pulse_cnt.h"
  25. #include "driver/rmt_tx.h"
  26. #if !CONFIG_IDF_TARGET_ESP32
  27. #error "RMT+PCNT timestamp workaround is only for ESP32"
  28. #endif
  29. #define REF_CLOCK_GPIO 0 // GPIO used to combine RMT out signal with PCNT input signal
  30. #define REF_CLOCK_PRESCALER_MS 30 // PCNT high threshold interrupt fired every 30ms
  31. static pcnt_unit_handle_t s_pcnt_unit;
  32. static pcnt_channel_handle_t s_pcnt_chan;
  33. static rmt_channel_handle_t s_rmt_chan;
  34. static rmt_encoder_handle_t s_rmt_encoder;
  35. static volatile uint32_t s_milliseconds;
  36. static bool on_reach_watch_point(pcnt_unit_handle_t unit, const pcnt_watch_event_data_t *edata, void *user_ctx)
  37. {
  38. s_milliseconds += REF_CLOCK_PRESCALER_MS;
  39. return false;
  40. }
  41. void ref_clock_init(void)
  42. {
  43. // Initialize PCNT
  44. pcnt_unit_config_t unit_config = {
  45. .high_limit = REF_CLOCK_PRESCALER_MS * 1000,
  46. .low_limit = -100, // any minus value is OK, in this case, we don't count down
  47. };
  48. TEST_ESP_OK(pcnt_new_unit(&unit_config, &s_pcnt_unit));
  49. pcnt_chan_config_t chan_config = {
  50. .edge_gpio_num = REF_CLOCK_GPIO,
  51. .level_gpio_num = -1,
  52. .flags.io_loop_back = true,
  53. };
  54. TEST_ESP_OK(pcnt_new_channel(s_pcnt_unit, &chan_config, &s_pcnt_chan));
  55. // increase count on both edges
  56. TEST_ESP_OK(pcnt_channel_set_edge_action(s_pcnt_chan, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_INCREASE));
  57. // don't care level change
  58. TEST_ESP_OK(pcnt_channel_set_level_action(s_pcnt_chan, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_KEEP));
  59. // add watch point
  60. TEST_ESP_OK(pcnt_unit_add_watch_point(s_pcnt_unit, REF_CLOCK_PRESCALER_MS * 1000));
  61. // register watch event
  62. pcnt_event_callbacks_t cbs = {
  63. .on_reach = on_reach_watch_point,
  64. };
  65. TEST_ESP_OK(pcnt_unit_register_event_callbacks(s_pcnt_unit, &cbs, NULL));
  66. // enable pcnt
  67. TEST_ESP_OK(pcnt_unit_enable(s_pcnt_unit));
  68. // start pcnt
  69. TEST_ESP_OK(pcnt_unit_start(s_pcnt_unit));
  70. // Initialize RMT
  71. rmt_tx_channel_config_t tx_chan_config = {
  72. .clk_src = RMT_CLK_SRC_REF_TICK, // REF_TICK clock source
  73. .gpio_num = REF_CLOCK_GPIO,
  74. .mem_block_symbols = 64,
  75. .resolution_hz = 10000, // channel resolution doesn't really matter, because we only utilize the carrier
  76. .trans_queue_depth = 1,
  77. .flags.io_loop_back = true,
  78. };
  79. TEST_ESP_OK(rmt_new_tx_channel(&tx_chan_config, &s_rmt_chan));
  80. // set carrier configuration
  81. rmt_carrier_config_t carrier_config = {
  82. .duty_cycle = 0.5,
  83. .frequency_hz = 500 * 1000, // 500 KHz
  84. };
  85. TEST_ESP_OK(rmt_apply_carrier(s_rmt_chan, &carrier_config));
  86. // enable rmt channel
  87. TEST_ESP_OK(rmt_enable(s_rmt_chan));
  88. // create a copy encoder to copy the RMT symbol into RMT HW memory
  89. rmt_copy_encoder_config_t encoder_config = {};
  90. TEST_ESP_OK(rmt_new_copy_encoder(&encoder_config, &s_rmt_encoder));
  91. // control the tx channel to output a fixed high level by constructing the following RMT symbol
  92. // the carrier is modulated to the high level by default, which results in a 500KHz carrier on the `REF_CLOCK_GPIO`
  93. rmt_symbol_word_t data = {
  94. .level0 = 1,
  95. .duration0 = 1,
  96. .level1 = 1,
  97. .duration1 = 0,
  98. };
  99. rmt_transmit_config_t trans_config = {
  100. .loop_count = 0, // no loop
  101. .flags.eot_level = 1,
  102. };
  103. TEST_ESP_OK(rmt_transmit(s_rmt_chan, s_rmt_encoder, &data, sizeof(data), &trans_config));
  104. s_milliseconds = 0;
  105. }
  106. void ref_clock_deinit(void)
  107. {
  108. // Deinitialize PCNT
  109. TEST_ESP_OK(pcnt_unit_stop(s_pcnt_unit));
  110. TEST_ESP_OK(pcnt_unit_disable(s_pcnt_unit));
  111. TEST_ESP_OK(pcnt_unit_remove_watch_point(s_pcnt_unit, REF_CLOCK_PRESCALER_MS * 1000));
  112. TEST_ESP_OK(pcnt_del_channel(s_pcnt_chan));
  113. TEST_ESP_OK(pcnt_del_unit(s_pcnt_unit));
  114. // Deinitialize RMT
  115. TEST_ESP_OK(rmt_disable(s_rmt_chan));
  116. TEST_ESP_OK(rmt_del_channel(s_rmt_chan));
  117. TEST_ESP_OK(rmt_del_encoder(s_rmt_encoder));
  118. }
  119. uint64_t ref_clock_get(void)
  120. {
  121. int microseconds = 0;
  122. TEST_ESP_OK(pcnt_unit_get_count(s_pcnt_unit, &microseconds));
  123. return 1000 * (uint64_t)s_milliseconds + (uint64_t)microseconds;
  124. }