1
0

pcnt_functions.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  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 "pcnt_functions.h"
  10. #include "displayAndSend.h"
  11. #include "soc/dport_reg.h"
  12. #ifdef CCFG_PCNT
  13. /** Electric meter:
  14. * 10000 impulses per kWh
  15. * Some normal max number could be 120 kWh/24h = 5 kWh/hour = 50000 pulses/hour = 833 pulses/min = 14 pulses/sec
  16. *
  17. * Counter is 16 bit signed = Max 32768 pulses = Will NOT work for one hour !!!!!
  18. * So we need some other way of counting higher numbers that will last for one h
  19. * Probably we need to use interrupts !
  20. */
  21. // Example-code: https://github.com/espressif/esp-idf/blob/master/examples/peripherals/pcnt/pulse_count_event/main/pcnt_event_example_main.c
  22. typedef struct {
  23. double kWh;
  24. uint32_t pulses32K;
  25. uint32_t pulses;
  26. } pulseData_t;
  27. // Counts interrupts that occus every 32K Pulses
  28. // The counter register is 16 bit signed so we count to 32K before doing an interrupt
  29. // This equals to 32kWH consumed energy (at 1000 pulses per kWh)
  30. static uint32_t Counter_32K_Pulses = 0;
  31. static const int pcntUnit = PCNT_UNIT_0;
  32. static void pCntMonitorTask(void *pvParameters);
  33. static pulseData_t getkWh() {
  34. pulseData_t pd = {0.0,0,0};
  35. pcnt_intr_disable(pcntUnit);
  36. volatile int32_t count = DPORT_REG_READ(0x3FF57060);
  37. volatile uint32_t bigCounter = Counter_32K_Pulses;
  38. pcnt_intr_enable(pcntUnit);
  39. pd.kWh = ((double)bigCounter*32)+((double)count / PULSES_PER_KWH);
  40. pd.pulses32K = bigCounter;
  41. pd.pulses = (uint32_t)count;
  42. return pd;
  43. }
  44. static void IRAM_ATTR pcnt_example_intr_handler(void *arg)
  45. {
  46. Counter_32K_Pulses++;
  47. }
  48. void init_pcnt_unit()
  49. {
  50. /* Prepare configuration for the PCNT unit */
  51. pcnt_config_t pcnt_config = {
  52. // Set PCNT input signal and control GPIOs
  53. .pulse_gpio_num = PCNT_INPUT_SIG_IO,
  54. .ctrl_gpio_num = PCNT_PIN_NOT_USED,
  55. .channel = PCNT_CHANNEL_0,
  56. .unit = pcntUnit,
  57. // What to do on the positive / negative edge of pulse input?
  58. .pos_mode = PCNT_COUNT_INC, // Count up on the positive edge
  59. .neg_mode = PCNT_COUNT_DIS, // Keep the counter value on the negative edge
  60. // What to do when control input is low or high?
  61. .lctrl_mode = PCNT_MODE_REVERSE, // Reverse counting direction if low
  62. .hctrl_mode = PCNT_MODE_KEEP, // Keep the primary counter mode if high
  63. // Set the maximum and minimum limit values to watch
  64. .counter_h_lim = 32000,
  65. .counter_l_lim = 0,
  66. };
  67. /* Initialize PCNT unit */
  68. pcnt_unit_config(&pcnt_config);
  69. // How to read a periphial-register:
  70. //uint32_t c = p_pcnt_obj->hal.dev.hw.conf_unit[unit];
  71. //printf("Reg: %08x\n",(uint32_t)DPORT_REG_READ(0x3FF57000));
  72. //The length of ignored pulses is provided in APB_CLK clock cycles by calling pcnt_set_filter_value().
  73. //The current filter setting may be checked with pcnt_get_filter_value().
  74. //The APB_CLK clock is running at 80 MHz.
  75. /* Configure and enable the input filter */
  76. pcnt_set_filter_value(pcntUnit, 1023); // APB_CLK=80MHz * 1023 is filtered out = 0,0127875mS (????)
  77. pcnt_filter_enable(pcntUnit);
  78. /* Enable int on high count limit */
  79. pcnt_event_enable(pcntUnit, PCNT_EVT_H_LIM);
  80. /* Initialize PCNT's counter */
  81. pcnt_counter_pause(pcntUnit);
  82. pcnt_counter_clear(pcntUnit);
  83. /* Install interrupt service and add isr callback handler */
  84. pcnt_isr_service_install(0);
  85. pcnt_isr_handler_add(pcntUnit, pcnt_example_intr_handler, (void *)pcntUnit);
  86. /* Everything is set up, now go to counting */
  87. pcnt_counter_resume(pcntUnit);
  88. }
  89. static void pCntMonitorTask(void *pvParameters) {
  90. ESP_LOGI("PCNT", "pCntMonitorTask starting. Core:%d",xPortGetCoreID());
  91. vTaskDelay(5000 / portTICK_PERIOD_MS); // Wait for display to get started
  92. while( 1 ) {
  93. pulseData_t pd = getkWh();
  94. addDataToQueue(type_kWh, pd.kWh, pd.pulses );
  95. vTaskDelay(60000 / portTICK_PERIOD_MS);
  96. }
  97. }
  98. void initPCNT() {
  99. ESP_LOGI("PCNT", "initPCNT");
  100. init_pcnt_unit();
  101. xTaskCreate(pCntMonitorTask, "pCntMonitorTask", 1024*10, NULL, 2, NULL);
  102. }
  103. #endif