Pulse Counter and GPIO routing

_ray_hill
Posts: 2
Joined: Mon May 15, 2023 10:14 am

Pulse Counter and GPIO routing

Postby _ray_hill » 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:
  1. setting up the pcnt to count the pulses:
    • defining on which edge to count
    • setting thresholds, limits, filter
  2. 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
This is the code that SHOULD do these steps:
  1. #include <Arduino.h>
  2.  
  3. #define RX_PIN2 (25)
  4.  
  5. // pulse counter 0-0 for counting receiving bits
  6. #define PCNT_U0_CONF0_REG (DR_REG_PCNT_BASE)
  7. #define PCNT_U0_CONF1_REG (PCNT_U0_CONF0_REG + 0x04)
  8. #define PCNT_U0_CONF2_REG (PCNT_U0_CONF0_REG + 0x08)
  9. #define PCNT_U0_CNT_REG (PCNT_U0_CONF0_REG + 0x60)
  10. #define PCNT_INT_RAW_REG    (PCNT_U0_CONF0_REG + 0x80)
  11. #define PCNT_INT_ENA_REG    (PCNT_U0_CONF0_REG + 0x88)
  12. #define PCNT_CRTL_REG   (PCNT_U0_CONF0_REG + 0xb0)
  13. // pulse counter configuration0 register bits
  14. #define PCNT_FILTER_THRES_Ux    (0)
  15. #define PCNT_FILTER_EN_Ux   (10)
  16. #define PCNT_THR_ZERO_EN_Ux (11)
  17. #define PCNT_THR_H_LIM_EN_Ux    (12)
  18. #define PCNT_THR_L_LIM_EN_Ux    (13)
  19. #define PCNT_THR_THRES0_EN_Ux   (14)
  20. #define PCNT_THR_THRES1_EN_Ux   (15)
  21. #define PCNT_CH0_NEG_MODE_Ux    (16)
  22. #define PCNT_CH0_POS_MODE_Ux    (18)
  23. #define PCNT_CH0_HCTRL_MODE_Ux  (20)
  24. #define PCNT_CH0_LCTRL_MODE_Ux  (22)
  25. // pulse counter configuration1 register bits
  26. #define PCNT_CNT_THRES0_Ux  (0)
  27. #define PCNT_CNT_THRES1_Ux  (16)
  28. // pulse counter configuration2 register bits
  29. #define PCNT_CNT_H_LIM_Ux   (0)
  30. #define PCNT_CNT_L_LIM_Ux   (16)
  31. // pulse counter control register bits
  32. #define PCNT_PLUS_CNT_RST_U0    (0)
  33. #define PCNT_CNT_PAUSE_U0   (1)
  34. // pcnt interrupts
  35. #define PCNT_CNT_THR_EVENT_U0_INT_RAW   (0)
  36. #define PCNT_CNT_THR_EVENT_U0_INT_ENA (0)
  37. // gpio matrix routing to use pulse counter input on RX_PIN2
  38. #define GPIO_FUNC39_IN_SEL_CFG_REG  (DR_REG_GPIO_BASE + 0x130 + (0x004 * 39)) // pcnt_sig_ch0_in0
  39. #define GPIO_FUNC41_IN_SEL_CFG_REG  (DR_REG_GPIO_BASE + 0x130 + (0x004 * 41)) // pcnt_ctrl_ch0_in0
  40. #define GPIO_FUNCRX2_OUT_SEL_CFG_REG    (DR_REG_GPIO_BASE + 0x530 + (0x004 * RX_PIN2)) // gpio output
  41. // gpio func in sel cfg reg bits
  42. #define GPIO_FUNC_IN_SEL    (0)
  43. #define GPIO_FUNC_IN_INV_SEL    (6)
  44. #define GPIO_SIG_IN_SEL (7)
  45. #define GPIO_FUNC_IN_SEL_ALWAYS_LO  (0x30)
  46. #define GPIO_FUNC_IN_SEL_ALWAYS_HI  (0x38)
  47. // gpio func out sel cfg reg bits
  48. #define GPIO_FUNC_OUT_SEL   (0)
  49. #define GPIO_FUNC_OUT_INV_SEL   (9)
  50. #define GPIO_FUNC_OEN_SEL   (10)
  51. #define GPIO_FUNC_OEN_INV_SEL   (11)
  52. // io mux reg bits
  53. #define MCU_OE  (0)
  54. #define SLP_SEL (1)
  55. #define MCU_WPD (2)
  56. #define MCU_WPU (3)
  57. #define MCU_IE  (4)
  58. #define MCU_DRV (5)
  59. #define FUN_WPD (7)
  60. #define FUN_WPU (8)
  61. #define FUN_IE  (9)
  62. #define FUN_DRV (10)
  63. #define MCU_SEL (12)
  64.  
  65. bool printConfig = false;
  66. uint32_t volatile pcntVal = 0;
  67.  
  68. void IRAM_ATTR isr(void) {
  69.   pcntVal = REG_READ(PCNT_U0_CNT_REG); // reading the pcnt counter value from the register
  70.   printConfig = true;
  71. }
  72.  
  73. void setup() {
  74.     ///// PULSE COUNTER SETUP  
  75.     uint32_t pcnt_ctrl_reg = REG_READ(PCNT_CRTL_REG);
  76.     pcnt_ctrl_reg &= ~bit(PCNT_PLUS_CNT_RST_U0);    // removing reset bit in control register
  77.     REG_WRITE(PCNT_CRTL_REG, pcnt_ctrl_reg);
  78.  
  79.     uint32_t pcnt_config1_reg = REG_READ(PCNT_U0_CONF1_REG);
  80.     pcnt_config1_reg |= 3 << PCNT_CNT_THRES0_Ux;    // setting up a threshold (trigger) to count value 3
  81.     REG_WRITE(PCNT_U0_CONF1_REG, pcnt_config1_reg);
  82.  
  83.     uint32_t pcnt_config2_reg = REG_READ(PCNT_U0_CONF2_REG);
  84.     pcnt_config2_reg |= 5 << PCNT_CNT_H_LIM_Ux; // setting higher counter limit to 5
  85.     REG_WRITE(PCNT_U0_CONF2_REG, pcnt_config2_reg);
  86.  
  87.     uint32_t pcnt_config0_reg = REG_READ(PCNT_U0_CONF0_REG);
  88.     pcnt_config0_reg &= ~(bit(PCNT_THR_ZERO_EN_Ux) | bit(PCNT_THR_L_LIM_EN_Ux)); // disable 0 and lower limit trigger
  89.     pcnt_config0_reg |= bit(PCNT_THR_H_LIM_EN_Ux);  // enable higher limit trigger
  90.     pcnt_config0_reg &= ~(0b1111111111 << PCNT_FILTER_THRES_Ux);    // reset 10 bit threshold for filter in APB_CLK cycles
  91.     pcnt_config0_reg |= 0b1111111111 << PCNT_FILTER_THRES_Ux;   // 10 bit threshold for filter in APB_CLK cycles (1023 -> 12.8 us)
  92.     pcnt_config0_reg &= ~bit(PCNT_FILTER_EN_Ux);    // disable filter
  93.     pcnt_config0_reg |= 0b01 << PCNT_CH0_POS_MODE_Ux;   // enable counting on rising edge
  94.     pcnt_config0_reg |= 0b01 << PCNT_CH0_NEG_MODE_Ux;   // enable counting on falling edge
  95.     pcnt_config0_reg &= ~(0b11 << PCNT_CH0_LCTRL_MODE_Ux);  // reset counting on low control input (0)
  96.     pcnt_config0_reg |= 0b00 << PCNT_CH0_LCTRL_MODE_Ux; // enable counting on low control input (0)
  97.     pcnt_config0_reg &= ~(0b11 << PCNT_CH0_HCTRL_MODE_Ux);  // reset counting on high control input (0)
  98.     pcnt_config0_reg |= 0b00 << PCNT_CH0_HCTRL_MODE_Ux; // enable counting on high control input (0)
  99.     REG_WRITE(PCNT_U0_CONF0_REG, pcnt_config0_reg);
  100.    
  101.     ///// GPIO ROUTING TO PCNT PERIPHERAL INPUT
  102.     // input signal from GPIO25 to pcnt unit 0 channel 0 signal input (function 39)
  103.     uint32_t gpio_pcnt_signal_in_reg = REG_READ(GPIO_FUNC39_IN_SEL_CFG_REG);
  104.     gpio_pcnt_signal_in_reg |= bit(GPIO_SIG_IN_SEL);    // enable signal input in gpio matrix
  105.     gpio_pcnt_signal_in_reg |= RX_PIN2 << GPIO_FUNC_IN_SEL; // signal comes in on RX_PIN2
  106.    
  107.     // HIGH signal to pcnt unit 0 channel 0 control input (function 41)
  108.     uint32_t gpio_pcnt_control_in_reg = REG_READ(GPIO_FUNC41_IN_SEL_CFG_REG);
  109.     gpio_pcnt_control_in_reg |= bit(GPIO_SIG_IN_SEL);   // enable (control) signal input in gpio matrix
  110.     gpio_pcnt_control_in_reg |= GPIO_FUNC_IN_SEL_ALWAYS_HI << GPIO_FUNC_IN_SEL; // signal should be always high (ctrl enabled)
  111.    
  112.     // set output status to be determined by gpio_enable_reg bit
  113.     uint32_t gpio_rxpin2_out_reg = REG_READ(GPIO_FUNCRX2_OUT_SEL_CFG_REG);
  114.     gpio_rxpin2_out_reg |= bit(GPIO_FUNC_OEN_SEL);  // set output status to be determined by gpio_enable_reg bit
  115.     REG_WRITE(GPIO_ENABLE_W1TC_REG, bit(RX_PIN2));  // disable output for rx2 pin (bit 25 = 0)
  116.    
  117.     // configuring GPIO25 for receiving the input signal
  118.     io_mux_rx2_reg = REG_READ(IO_MUX_GPIO25_REG);
  119.     io_mux_rx2_reg &= ~(0b111 << MCU_SEL);  // reset function for the io pad
  120.     io_mux_rx2_reg |= 0b010 << MCU_SEL; // set gpio function for the io pad
  121.     io_mux_rx2_reg |= bit(FUN_IE);  // set gpio as input
  122.     io_mux_rx2_reg |= bit(FUN_WPU); // enable pull up
  123.     io_mux_rx2_reg &= ~bit(FUN_WPD);    // disable pull down
  124.    
  125.     // writing to the registers
  126.     REG_WRITE(GPIO_FUNC39_IN_SEL_CFG_REG, gpio_pcnt_signal_in_reg);
  127.     REG_WRITE(GPIO_FUNC41_IN_SEL_CFG_REG, gpio_pcnt_control_in_reg);
  128.     REG_WRITE(GPIO_FUNCRX2_OUT_SEL_CFG_REG, gpio_rxpin2_out_reg);
  129.     REG_WRITE(IO_MUX_GPIO25_REG, io_mux_rx2_reg);
  130.  
  131.     // attach a button to pull RX_PIN2 low on button press (== negative edge on pcnt signal input)
  132.     // and print out the register value
  133.     attachInterrupt(RX_PIN2, &isr, FALLING);
  134. }
  135.  
  136. void loop() {
  137.     // just printing the register counter value bits
  138.     if (printConfig) {
  139.         printf("pcntVal: ");
  140.         for (int i = 0; i < 32; i++) {
  141.             if (pcntVal & bit(31 - i)) {
  142.                 printf("1");
  143.             } else {
  144.                 printf("0");
  145.             }
  146.             if (i % 4 == 3) {
  147.                 printf(" ");
  148.             }
  149.         }
  150.         printf("\n");
  151.         printConfig = false;
  152.     }
  153.     delay(500);
  154. }
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

Code: Select all

pinMode(RX_PIN2, INPUT_PULLDOWN)
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!

_ray_hill
Posts: 2
Joined: Mon May 15, 2023 10:14 am

Re: Pulse Counter and GPIO routing

Postby _ray_hill » Thu May 25, 2023 10:05 am

I found out that this might be due to REG_WRITE() not writing to the register somehow.. When I print the contents of let's say pcnt_config0_reg I write to PCNT_U0_CONF0_REG (line 99 in the code, contains 0000 0000 0000 0101 0001 0011 1111 1111) and read from this register again using REG_READ() and print that, it shows me just the default register contents (0000 0000 0000 0000 0011 1100 0001 0000). So, obviously PCNT did not get configured...
I wrote to the TIMG0_T0CONFIG_REG Timer 0 group 0 Config registry before, which worked as expected.
Does anyone have any idea why writing to PCNT_U0_CONF0_REG does not work while writing to TIMG0_T0CONFIG_REG works without any issues? :?: According to the TRM the registry should be writable. This resetting behaviour applies to all the registries I used in my code example.

Thank you very much!

MicroController
Posts: 1708
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Pulse Counter and GPIO routing

Postby MicroController » Fri May 26, 2023 6:23 pm

Sounds much like an issue we had here last month or so.
The solution there was that a peripheral needs to be enabled/powered up first, else writing to its registers would seem to be ignored.

Who is online

Users browsing this forum: No registered users and 102 guests