pcnt_functions.c 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. #include "freertos/FreeRTOS.h"
  2. #include "freertos/task.h"
  3. #include "freertos/queue.h"
  4. #include "driver/ledc.h"
  5. #include "driver/pcnt.h"
  6. #include "esp_attr.h"
  7. #include "esp_log.h"
  8. #include "config.h"
  9. #include "soc/dport_reg.h"
  10. #ifdef CCFG_PCNT
  11. // Example-code: https://github.com/espressif/esp-idf/blob/master/examples/peripherals/pcnt/pulse_count_event/main/pcnt_event_example_main.c
  12. // PCNT unit counts incomming pulses by itself, but it can only count up to 32000, then an interrupt must poccur
  13. // So an interrupt is comming every 32kWh (1 to multiple times per day)
  14. // Counts interrupts that occus every 32K Pulses
  15. // The counter register is 16 bit signed so we count to 32K before doing an interrupt
  16. // This equals to 32kWH consumed energy (at 1000 pulses per kWh)
  17. static uint32_t Counter_32K_Pulses = 0;
  18. // A 32-bit unsigned (with resolution 1/1000 kWh) can hold 4294967kWh which is 214 years for us, so no problem
  19. const int pcntUnit = PCNT_UNIT_0;
  20. uint32_t getkWh() {
  21. volatile uint32_t firstRead, count, bigCounter;
  22. // Loop until we have two identical reads of the interrupt-updated 32K-counter
  23. // The 32K-counter is only updated maybe once per 24h, so should not be a problem
  24. do {
  25. firstRead = Counter_32K_Pulses;
  26. count = DPORT_REG_READ(0x3FF57060);
  27. bigCounter = Counter_32K_Pulses;
  28. } while( firstRead != bigCounter );
  29. return (bigCounter * 32000) + count;
  30. }
  31. static void IRAM_ATTR pcnt_example_intr_handler(void *arg)
  32. {
  33. Counter_32K_Pulses++;
  34. }
  35. void init_pcnt_unit()
  36. {
  37. /* Prepare configuration for the PCNT unit */
  38. pcnt_config_t pcnt_config = {
  39. // Set PCNT input signal and control GPIOs
  40. .pulse_gpio_num = PCNT_INPUT_SIG_IO,
  41. .ctrl_gpio_num = PCNT_INPUT_CTRL_IO,
  42. .channel = PCNT_CHANNEL_0,
  43. .unit = pcntUnit,
  44. // What to do on the positive / negative edge of pulse input?
  45. .pos_mode = PCNT_COUNT_INC, // Count up on the positive edge
  46. .neg_mode = PCNT_COUNT_DIS, // Keep the counter value on the negative edge
  47. // What to do when control input is low or high?
  48. .lctrl_mode = PCNT_MODE_REVERSE, // Reverse counting direction if low
  49. .hctrl_mode = PCNT_MODE_KEEP, // Keep the primary counter mode if high
  50. // Set the maximum and minimum limit values to watch
  51. .counter_h_lim = 32000,
  52. .counter_l_lim = 0,
  53. };
  54. /* Initialize PCNT unit */
  55. pcnt_unit_config(&pcnt_config);
  56. // How to read a periphial-register:
  57. //uint32_t c = p_pcnt_obj->hal.dev.hw.conf_unit[unit];
  58. //printf("Reg: %08x\n",(uint32_t)DPORT_REG_READ(0x3FF57000));
  59. //The length of ignored pulses is provided in APB_CLK clock cycles by calling pcnt_set_filter_value().
  60. //The current filter setting may be checked with pcnt_get_filter_value().
  61. //The APB_CLK clock is running at 80 MHz.
  62. /* Configure and enable the input filter */
  63. pcnt_set_filter_value(pcntUnit, 1023); // APB_CLK=80MHz * 1023 is filtered out = 0,0127875mS (????)
  64. pcnt_filter_enable(pcntUnit);
  65. /* Enable int on high count limit */
  66. pcnt_event_enable(pcntUnit, PCNT_EVT_H_LIM);
  67. /* Initialize PCNT's counter */
  68. pcnt_counter_pause(pcntUnit);
  69. pcnt_counter_clear(pcntUnit);
  70. /* Install interrupt service and add isr callback handler */
  71. pcnt_isr_service_install(0);
  72. pcnt_isr_handler_add(pcntUnit, pcnt_example_intr_handler, (void *)pcntUnit);
  73. /* Everything is set up, now go to counting */
  74. pcnt_counter_resume(pcntUnit);
  75. }
  76. #else
  77. void init_pcnt_unit() { }
  78. uint32_t getkWh() { return 0; }
  79. #endif