Pulse Counter and GPIO routing
Posted: Mon May 15, 2023 10:41 am
Hi everyone,
Intro: I want to use the pulse counter (pcnt) functionality of the ESP32 to count single bits of a frequency modulated bit signal coming from an ATMega328. To do this I need to route the GPIO input to one of the pcnt units/channels.
Environment: ESP-Wroom-32 DevKit, VSCode with PlatformIO and Arduino Framework
Problem description: I first studied the pcnt possibilities by reading the ESP32 TRM. Since I had huge fun configuring the timer for sending the signal by writing to the registers directly, I wanted to do it the same way for this task.
However, following the instructions given in the TRM, I am not able to get this to work and need someone looking at this, probably directly seeing, what I am doing wrong..
The steps that need to be done according to the TRM are:
Expectation: On pressing the button, the counter should increase.
Observation: The counter of the pcnt stays at 0 all the time. Furthermore when I change the IO_MUX of the pin to pulldown (FUN_WPD = 0), the pin is still at 3.2V. When I put a at the beginning of the setup function, it is at 1.3V, so I guess it is just floating. So, for me it seems as if the IO_MUX config is not even working as I expected it to do.
I also played around with other pcnt configurations, read the TRM again and again, used other pins, but was not able to get this running. When outputting the content I wrote to the registers, it all looks fine, so I guess there must be something else I need to configure, that is not mentioned in the TRM?
I also know, that I could include driver/pcnt.h, and I did and also counld not get it to work (might have been not configured correctly), but this is kind of a fundamental/educational point: "I did what was mentioned in the TRM, so it should work! Why does it not work?"
I hope someone could maybe test this and/or knows what I did wrong because not understanding this is driving me crazy..
TRM: https://www.espressif.com/sites/default ... ual_en.pdf
chapter 4 for IO_MUX and GPIO Matrix
chapter 17 for Pulse Counter Control
Thank you!
Intro: I want to use the pulse counter (pcnt) functionality of the ESP32 to count single bits of a frequency modulated bit signal coming from an ATMega328. To do this I need to route the GPIO input to one of the pcnt units/channels.
Environment: ESP-Wroom-32 DevKit, VSCode with PlatformIO and Arduino Framework
Problem description: I first studied the pcnt possibilities by reading the ESP32 TRM. Since I had huge fun configuring the timer for sending the signal by writing to the registers directly, I wanted to do it the same way for this task.
However, following the instructions given in the TRM, I am not able to get this to work and need someone looking at this, probably directly seeing, what I am doing wrong..
The steps that need to be done according to the TRM are:
- setting up the pcnt to count the pulses:
- defining on which edge to count
- setting thresholds, limits, filter
- routing the input signal to the pcnt:
- adjusting GPIO matrix to route GPIO25 input to the signal input of unit 0 channel 0 (function 39)
- adjusting GPIO matrix to route special "GPIO" 0x38 (always HIGH) to the control input of unit 0 channel 0 (function 41)
- setting the GPIO25 output to be determined by the GPIO enable register (+ setting the GPIO enable to 0 in that reg)
- adjusting IO_MUX of GPIO25 to work as GPIO (value 2) and as input with pullup
- #include <Arduino.h>
- #define RX_PIN2 (25)
- // pulse counter 0-0 for counting receiving bits
- #define PCNT_U0_CONF0_REG (DR_REG_PCNT_BASE)
- #define PCNT_U0_CONF1_REG (PCNT_U0_CONF0_REG + 0x04)
- #define PCNT_U0_CONF2_REG (PCNT_U0_CONF0_REG + 0x08)
- #define PCNT_U0_CNT_REG (PCNT_U0_CONF0_REG + 0x60)
- #define PCNT_INT_RAW_REG (PCNT_U0_CONF0_REG + 0x80)
- #define PCNT_INT_ENA_REG (PCNT_U0_CONF0_REG + 0x88)
- #define PCNT_CRTL_REG (PCNT_U0_CONF0_REG + 0xb0)
- // pulse counter configuration0 register bits
- #define PCNT_FILTER_THRES_Ux (0)
- #define PCNT_FILTER_EN_Ux (10)
- #define PCNT_THR_ZERO_EN_Ux (11)
- #define PCNT_THR_H_LIM_EN_Ux (12)
- #define PCNT_THR_L_LIM_EN_Ux (13)
- #define PCNT_THR_THRES0_EN_Ux (14)
- #define PCNT_THR_THRES1_EN_Ux (15)
- #define PCNT_CH0_NEG_MODE_Ux (16)
- #define PCNT_CH0_POS_MODE_Ux (18)
- #define PCNT_CH0_HCTRL_MODE_Ux (20)
- #define PCNT_CH0_LCTRL_MODE_Ux (22)
- // pulse counter configuration1 register bits
- #define PCNT_CNT_THRES0_Ux (0)
- #define PCNT_CNT_THRES1_Ux (16)
- // pulse counter configuration2 register bits
- #define PCNT_CNT_H_LIM_Ux (0)
- #define PCNT_CNT_L_LIM_Ux (16)
- // pulse counter control register bits
- #define PCNT_PLUS_CNT_RST_U0 (0)
- #define PCNT_CNT_PAUSE_U0 (1)
- // pcnt interrupts
- #define PCNT_CNT_THR_EVENT_U0_INT_RAW (0)
- #define PCNT_CNT_THR_EVENT_U0_INT_ENA (0)
- // gpio matrix routing to use pulse counter input on RX_PIN2
- #define GPIO_FUNC39_IN_SEL_CFG_REG (DR_REG_GPIO_BASE + 0x130 + (0x004 * 39)) // pcnt_sig_ch0_in0
- #define GPIO_FUNC41_IN_SEL_CFG_REG (DR_REG_GPIO_BASE + 0x130 + (0x004 * 41)) // pcnt_ctrl_ch0_in0
- #define GPIO_FUNCRX2_OUT_SEL_CFG_REG (DR_REG_GPIO_BASE + 0x530 + (0x004 * RX_PIN2)) // gpio output
- // gpio func in sel cfg reg bits
- #define GPIO_FUNC_IN_SEL (0)
- #define GPIO_FUNC_IN_INV_SEL (6)
- #define GPIO_SIG_IN_SEL (7)
- #define GPIO_FUNC_IN_SEL_ALWAYS_LO (0x30)
- #define GPIO_FUNC_IN_SEL_ALWAYS_HI (0x38)
- // gpio func out sel cfg reg bits
- #define GPIO_FUNC_OUT_SEL (0)
- #define GPIO_FUNC_OUT_INV_SEL (9)
- #define GPIO_FUNC_OEN_SEL (10)
- #define GPIO_FUNC_OEN_INV_SEL (11)
- // io mux reg bits
- #define MCU_OE (0)
- #define SLP_SEL (1)
- #define MCU_WPD (2)
- #define MCU_WPU (3)
- #define MCU_IE (4)
- #define MCU_DRV (5)
- #define FUN_WPD (7)
- #define FUN_WPU (8)
- #define FUN_IE (9)
- #define FUN_DRV (10)
- #define MCU_SEL (12)
- bool printConfig = false;
- uint32_t volatile pcntVal = 0;
- void IRAM_ATTR isr(void) {
- pcntVal = REG_READ(PCNT_U0_CNT_REG); // reading the pcnt counter value from the register
- printConfig = true;
- }
- void setup() {
- ///// PULSE COUNTER SETUP
- uint32_t pcnt_ctrl_reg = REG_READ(PCNT_CRTL_REG);
- pcnt_ctrl_reg &= ~bit(PCNT_PLUS_CNT_RST_U0); // removing reset bit in control register
- REG_WRITE(PCNT_CRTL_REG, pcnt_ctrl_reg);
- uint32_t pcnt_config1_reg = REG_READ(PCNT_U0_CONF1_REG);
- pcnt_config1_reg |= 3 << PCNT_CNT_THRES0_Ux; // setting up a threshold (trigger) to count value 3
- REG_WRITE(PCNT_U0_CONF1_REG, pcnt_config1_reg);
- uint32_t pcnt_config2_reg = REG_READ(PCNT_U0_CONF2_REG);
- pcnt_config2_reg |= 5 << PCNT_CNT_H_LIM_Ux; // setting higher counter limit to 5
- REG_WRITE(PCNT_U0_CONF2_REG, pcnt_config2_reg);
- uint32_t pcnt_config0_reg = REG_READ(PCNT_U0_CONF0_REG);
- pcnt_config0_reg &= ~(bit(PCNT_THR_ZERO_EN_Ux) | bit(PCNT_THR_L_LIM_EN_Ux)); // disable 0 and lower limit trigger
- pcnt_config0_reg |= bit(PCNT_THR_H_LIM_EN_Ux); // enable higher limit trigger
- pcnt_config0_reg &= ~(0b1111111111 << PCNT_FILTER_THRES_Ux); // reset 10 bit threshold for filter in APB_CLK cycles
- pcnt_config0_reg |= 0b1111111111 << PCNT_FILTER_THRES_Ux; // 10 bit threshold for filter in APB_CLK cycles (1023 -> 12.8 us)
- pcnt_config0_reg &= ~bit(PCNT_FILTER_EN_Ux); // disable filter
- pcnt_config0_reg |= 0b01 << PCNT_CH0_POS_MODE_Ux; // enable counting on rising edge
- pcnt_config0_reg |= 0b01 << PCNT_CH0_NEG_MODE_Ux; // enable counting on falling edge
- pcnt_config0_reg &= ~(0b11 << PCNT_CH0_LCTRL_MODE_Ux); // reset counting on low control input (0)
- pcnt_config0_reg |= 0b00 << PCNT_CH0_LCTRL_MODE_Ux; // enable counting on low control input (0)
- pcnt_config0_reg &= ~(0b11 << PCNT_CH0_HCTRL_MODE_Ux); // reset counting on high control input (0)
- pcnt_config0_reg |= 0b00 << PCNT_CH0_HCTRL_MODE_Ux; // enable counting on high control input (0)
- REG_WRITE(PCNT_U0_CONF0_REG, pcnt_config0_reg);
- ///// GPIO ROUTING TO PCNT PERIPHERAL INPUT
- // input signal from GPIO25 to pcnt unit 0 channel 0 signal input (function 39)
- uint32_t gpio_pcnt_signal_in_reg = REG_READ(GPIO_FUNC39_IN_SEL_CFG_REG);
- gpio_pcnt_signal_in_reg |= bit(GPIO_SIG_IN_SEL); // enable signal input in gpio matrix
- gpio_pcnt_signal_in_reg |= RX_PIN2 << GPIO_FUNC_IN_SEL; // signal comes in on RX_PIN2
- // HIGH signal to pcnt unit 0 channel 0 control input (function 41)
- uint32_t gpio_pcnt_control_in_reg = REG_READ(GPIO_FUNC41_IN_SEL_CFG_REG);
- gpio_pcnt_control_in_reg |= bit(GPIO_SIG_IN_SEL); // enable (control) signal input in gpio matrix
- gpio_pcnt_control_in_reg |= GPIO_FUNC_IN_SEL_ALWAYS_HI << GPIO_FUNC_IN_SEL; // signal should be always high (ctrl enabled)
- // set output status to be determined by gpio_enable_reg bit
- uint32_t gpio_rxpin2_out_reg = REG_READ(GPIO_FUNCRX2_OUT_SEL_CFG_REG);
- gpio_rxpin2_out_reg |= bit(GPIO_FUNC_OEN_SEL); // set output status to be determined by gpio_enable_reg bit
- REG_WRITE(GPIO_ENABLE_W1TC_REG, bit(RX_PIN2)); // disable output for rx2 pin (bit 25 = 0)
- // configuring GPIO25 for receiving the input signal
- io_mux_rx2_reg = REG_READ(IO_MUX_GPIO25_REG);
- io_mux_rx2_reg &= ~(0b111 << MCU_SEL); // reset function for the io pad
- io_mux_rx2_reg |= 0b010 << MCU_SEL; // set gpio function for the io pad
- io_mux_rx2_reg |= bit(FUN_IE); // set gpio as input
- io_mux_rx2_reg |= bit(FUN_WPU); // enable pull up
- io_mux_rx2_reg &= ~bit(FUN_WPD); // disable pull down
- // writing to the registers
- REG_WRITE(GPIO_FUNC39_IN_SEL_CFG_REG, gpio_pcnt_signal_in_reg);
- REG_WRITE(GPIO_FUNC41_IN_SEL_CFG_REG, gpio_pcnt_control_in_reg);
- REG_WRITE(GPIO_FUNCRX2_OUT_SEL_CFG_REG, gpio_rxpin2_out_reg);
- REG_WRITE(IO_MUX_GPIO25_REG, io_mux_rx2_reg);
- // attach a button to pull RX_PIN2 low on button press (== negative edge on pcnt signal input)
- // and print out the register value
- attachInterrupt(RX_PIN2, &isr, FALLING);
- }
- void loop() {
- // just printing the register counter value bits
- if (printConfig) {
- printf("pcntVal: ");
- for (int i = 0; i < 32; i++) {
- if (pcntVal & bit(31 - i)) {
- printf("1");
- } else {
- printf("0");
- }
- if (i % 4 == 3) {
- printf(" ");
- }
- }
- printf("\n");
- printConfig = false;
- }
- delay(500);
- }
Observation: The counter of the pcnt stays at 0 all the time. Furthermore when I change the IO_MUX of the pin to pulldown (FUN_WPD = 0), the pin is still at 3.2V. When I put a
Code: Select all
pinMode(RX_PIN2, INPUT_PULLDOWN)
I also played around with other pcnt configurations, read the TRM again and again, used other pins, but was not able to get this running. When outputting the content I wrote to the registers, it all looks fine, so I guess there must be something else I need to configure, that is not mentioned in the TRM?
I also know, that I could include driver/pcnt.h, and I did and also counld not get it to work (might have been not configured correctly), but this is kind of a fundamental/educational point: "I did what was mentioned in the TRM, so it should work! Why does it not work?"
I hope someone could maybe test this and/or knows what I did wrong because not understanding this is driving me crazy..
TRM: https://www.espressif.com/sites/default ... ual_en.pdf
chapter 4 for IO_MUX and GPIO Matrix
chapter 17 for Pulse Counter Control
Thank you!