#include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/queue.h" #include "driver/ledc.h" #include "driver/pcnt.h" #include "esp_attr.h" #include "esp_log.h" #include "config.h" #include "pcnt_functions.h" #include "displayAndSend.h" #include "soc/dport_reg.h" #ifdef CCFG_PCNT /** Electric meter: * 10000 impulses per kWh * Some normal max number could be 120 kWh/24h = 5 kWh/hour = 50000 pulses/hour = 833 pulses/min = 14 pulses/sec * * Counter is 16 bit signed = Max 32768 pulses = Will NOT work for one hour !!!!! * So we need some other way of counting higher numbers that will last for one h * Probably we need to use interrupts ! */ // Example-code: https://github.com/espressif/esp-idf/blob/master/examples/peripherals/pcnt/pulse_count_event/main/pcnt_event_example_main.c typedef struct { double kWh; uint32_t pulses32K; uint32_t pulses; } pulseData_t; // Counts interrupts that occus every 32K Pulses // The counter register is 16 bit signed so we count to 32K before doing an interrupt // This equals to 32kWH consumed energy (at 1000 pulses per kWh) static uint32_t Counter_32K_Pulses = 0; static const int pcntUnit = PCNT_UNIT_0; static void pCntMonitorTask(void *pvParameters); static pulseData_t getkWh() { pulseData_t pd = {0.0,0,0}; pcnt_intr_disable(pcntUnit); volatile int32_t count = DPORT_REG_READ(0x3FF57060); volatile uint32_t bigCounter = Counter_32K_Pulses; pcnt_intr_enable(pcntUnit); pd.kWh = ((double)bigCounter*32)+((double)count / PULSES_PER_KWH); pd.pulses32K = bigCounter; pd.pulses = (uint32_t)count; return pd; } static void IRAM_ATTR pcnt_example_intr_handler(void *arg) { Counter_32K_Pulses++; } void init_pcnt_unit() { /* Prepare configuration for the PCNT unit */ pcnt_config_t pcnt_config = { // Set PCNT input signal and control GPIOs .pulse_gpio_num = PCNT_INPUT_SIG_IO, .ctrl_gpio_num = PCNT_PIN_NOT_USED, .channel = PCNT_CHANNEL_0, .unit = pcntUnit, // What to do on the positive / negative edge of pulse input? .pos_mode = PCNT_COUNT_INC, // Count up on the positive edge .neg_mode = PCNT_COUNT_DIS, // Keep the counter value on the negative edge // What to do when control input is low or high? .lctrl_mode = PCNT_MODE_REVERSE, // Reverse counting direction if low .hctrl_mode = PCNT_MODE_KEEP, // Keep the primary counter mode if high // Set the maximum and minimum limit values to watch .counter_h_lim = 32000, .counter_l_lim = 0, }; /* Initialize PCNT unit */ pcnt_unit_config(&pcnt_config); // How to read a periphial-register: //uint32_t c = p_pcnt_obj->hal.dev.hw.conf_unit[unit]; //printf("Reg: %08x\n",(uint32_t)DPORT_REG_READ(0x3FF57000)); //The length of ignored pulses is provided in APB_CLK clock cycles by calling pcnt_set_filter_value(). //The current filter setting may be checked with pcnt_get_filter_value(). //The APB_CLK clock is running at 80 MHz. /* Configure and enable the input filter */ pcnt_set_filter_value(pcntUnit, 1023); // APB_CLK=80MHz * 1023 is filtered out = 0,0127875mS (????) pcnt_filter_enable(pcntUnit); /* Enable int on high count limit */ pcnt_event_enable(pcntUnit, PCNT_EVT_H_LIM); /* Initialize PCNT's counter */ pcnt_counter_pause(pcntUnit); pcnt_counter_clear(pcntUnit); /* Install interrupt service and add isr callback handler */ pcnt_isr_service_install(0); pcnt_isr_handler_add(pcntUnit, pcnt_example_intr_handler, (void *)pcntUnit); /* Everything is set up, now go to counting */ pcnt_counter_resume(pcntUnit); } static void pCntMonitorTask(void *pvParameters) { ESP_LOGI("PCNT", "pCntMonitorTask starting. Core:%d",xPortGetCoreID()); vTaskDelay(5000 / portTICK_PERIOD_MS); // Wait for display to get started while( 1 ) { pulseData_t pd = getkWh(); addDataToQueue(type_kWh, pd.kWh, pd.pulses ); vTaskDelay(60000 / portTICK_PERIOD_MS); } } void initPCNT() { ESP_LOGI("PCNT", "initPCNT"); init_pcnt_unit(); xTaskCreate(pCntMonitorTask, "pCntMonitorTask", 1024*10, NULL, 2, NULL); } #endif