Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Read from PWM #19

Closed
gin66 opened this issue Nov 12, 2022 · 14 comments
Closed

Read from PWM #19

gin66 opened this issue Nov 12, 2022 · 14 comments

Comments

@gin66
Copy link

gin66 commented Nov 12, 2022

Nice project and thanks for sharing. In regard to this comment of yours:

I do not know how to digitalRead PWM signals directly from GPIOs that output them. Any useful idea would be greatly appreciated.

In my project FastAccelStepper, the mcpwm are used to output stepper signals. Those pulses are read back from the pin and fed into the pcnt modules. Perhaps similar approach works for your issue. The relevant code is:

void StepperQueue::connect_mcpwm_pcnt() {
  const struct mapping_s *mapping = (const struct mapping_s *)driver_data;
  mcpwm_unit_t mcpwm_unit = mapping->mcpwm_unit;
  mcpwm_gpio_init(mcpwm_unit, mapping->pwm_output_pin, _step_pin);
  // Doesn't work with gpio_matrix_in
  //  gpio_matrix_in(step_pin, mapping->input_sig_index, false);
  gpio_iomux_in(_step_pin, mapping->input_sig_index);
}

The key is gpio_iomux_in(). Via github search could not see, that you use this function. Perhaps new to you and a direction towards a solution. even though, no turn-key solution.

@BojanJurca
Copy link
Owner

Thank you. I've taken a look into your FastAccelStepper, I may need to use it some day.
You're right, I'm not familiar with gpio_iomux_in function. I tried to find some documentation but it is rather scarce. If I understand correctly it establishes an internal connection to another GPIO and the result would be similar to using a wire? Do you have an idea which one of the signals from gpio_sig_map.h (https://github.com/pycom/esp-idf-2.0/blob/master/components/esp32/include/soc/gpio_sig_map.h) should I use?

Bojan

@gin66
Copy link
Author

gin66 commented Nov 13, 2022

Quite good documentation is here from page 45: (https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf)

Eventually it is even easier:

4.2.3 Simple GPIO Input
The GPIO_IN_REG/GPIO_IN1_REG register holds the input values of each GPIO pad.
The input value of any GPIO pin can be read at any time without configuring the 
GPIO Matrix for a particular peripheral signal. However, it is necessary to enable 
the input in the IO_MUX by setting the FUN_IE bit in the IO_MUX_x_REG register 
corresponding to pad X, as mentioned in Section 4.2.2.

@gin66
Copy link
Author

gin66 commented Nov 13, 2022

The respective macro for setting FUN_IE seems to be:
#define PIN_INPUT_ENABLE(PIN_NAME) SET_PERI_REG_MASK(PIN_NAME,FUN_IE)

@BojanJurca
Copy link
Owner

I'm still having difficulties getting it to work with Arduino. If I PIN_INPUT_ENABLE weather in setup () or in loop () the result is the same - ESP32 reboots with:

Guru Meditation Error: Core  1 panic'ed (LoadProhibited). Exception was unhandled.

The code I used for testing:

#include <WiFi.h>
#include <soc/gpio_sig_map.h>

void setup () {
  Serial.begin (115200);
  
  #define PIN_INPUT_ENABLE(PIN_NAME) SET_PERI_REG_MASK(PIN_NAME,FUN_IE)
  PIN_INPUT_ENABLE (2);

  // start PWM on built-in LED
  ledcSetup (0, 1000, 10);
  ledcAttachPin (2, 0); // built-in LED
  ledcWrite (0, 307); // 1/3 duty cycle
}

void loop () {
  delay (1000);
  Serial.printf ("%i\n", digitalRead (2)); // expected: 1/3 of 1 and 2/3 of 0 on built-in LED
}

Do you have any ideas?

@gin66
Copy link
Author

gin66 commented Nov 19, 2022

Try this:

#include <soc/gpio_sig_map.h>
#include <soc/io_mux_reg.h>

#define LED 2

void setup () {
  Serial.begin (115200);
  
  // start PWM on built-in LED
  ledcSetup (0, 12, 10);
  ledcAttachPin (LED, 0); // built-in LED
  ledcWrite (0, 307); // 1/3 duty cycle

  PIN_INPUT_ENABLE (GPIO_PIN_MUX_REG[2]);
}

void loop () {
  delay(10);
  Serial.printf("%i", digitalRead(LED));
//  Serial.printf ("%i\n", gpio_input_get()); // This reads all 32 port bits at once
}

@gin66
Copy link
Author

gin66 commented Nov 23, 2022

Two more comments:

  • The assumption „expected: 1/3 of 1 and 2/3 of 0 on built-in LED“ may be only valid over a very long time. The original delay of 1000 and the frequency 1000 are based on the same clock, so the phase shift for each loop() is pretty low and if the internal implementation of delay() uses an interrupt, the delay may even be zero. This way the status of e.g. 0 can stay like this for a very long time until the phase has added up enough to get the high value. Then it will stay on 1 again for long time, That’s why I had to change the pwm frequency and the delay for less synchronicity to avoid this.
  • I have forgotten to check, if it works without the PIN_INPUT_ENABLE with the different frequency

@BojanJurca
Copy link
Owner

Indeed. There are some combinations that just don't work well. For example 1000 Hz PWM and 10 ms sampling.
It is strange though why 1200 Hz with 12 ms sampling works.

On the other hand oscilloscope normally does not use "delay" but rather "delayMicroseconds" at higher frequencies which I believe is not RTOS but an Arduino function so it doesn't have to do much with interrupters and everything works just fine. I'll include your solution to the Oscilloscope. Thank you.

image

@gin66 gin66 closed this as completed Nov 23, 2022
@gin66
Copy link
Author

gin66 commented Feb 18, 2023

"For example 1000 Hz PWM and 10 ms sampling."
=> 1000 Hz = 1ms, which is 1/10th with 10ms

"It is strange though why 1200 Hz with 12 ms sampling works."
=> 1200Hz = 0.833ms, which 1:14.4=5:72 of 12ms. So not strange at all

@BojanJurca
Copy link
Owner

Hi gin66.

I'm sorry to bother you again with the same topic. PIN_INPUT_ENABLE (GPIO_PIN_MUX_REG[...]) worked very well with IDF 4.x. After the upgrade to IDF 5.x I've been struggling for days to get it working, with no success. Although the code compiles without any problems, digitalRead only returns 0 if GPIO is not configured in INPUT mode (if it is configured as OUTPUT or PWM for example). Can you help me with this, please?

@gin66
Copy link
Author

gin66 commented Jun 12, 2024

Sorry, but the IDF5 gives me headaches, too, so I only can give you link to migration guide.

My lib is absolutely broken with IDF5 and porting mcpwm/pcnt/rmt drivers is close to a new development….for the EXACT same hardware. And your experience tells me, that there is chance, that this is not even possible anymore….on the EXACT same hardware.

@gin66 gin66 reopened this Jun 12, 2024
@BojanJurca
Copy link
Owner

Thank you for your information. I hope we'll find something out.

@BojanJurca
Copy link
Owner

This is just a partial success:

Using gpio_get_level (gpio_pin) instead of digitalRead (gpio_pin) helps reading pins configured as OUTPUT, but doesn't help with PWM signals at all.

I tried writing a modified version of gpio_get_level function that basically returns (_gpio_hal.dev->out >> gpio_num) & 0x1, instead original one that returns  (_gpio_hal.dev->in>> gpio_num) & 0x1, but this didn't help either.

I guess I'll go with this for now, until a better solution is found.

@BojanJurca
Copy link
Owner

This seems to be a full solution. Basically, instead of using PIN_INPUT_ENABLE (GPIO_PIN_MUX_REG[... we can now use gpio_hal_input_enable (...

Here is a whole test script:

#include "driver/gpio.h"
#include "hal/gpio_hal.h"

void setup () {
    Serial.begin (115200);

               
    #define PWM_PIN 16

    // generate PWM signal on PWM_PIN ...
    ledcAttach (PWM_PIN, 1000, 10);
    ledcWrite (PWM_PIN, 307); // 307 out of 1024 (10 bit resolution) = 1/3

    // ... or just set the output value of PWM_PIN
    // pinMode (PWM_PIN, OUTPUT);
    // digitalWrite (PWM_PIN, HIGH);

    // enable input on PWM_PIN
    static gpio_hal_context_t _gpio_hal = {
        .dev = GPIO_HAL_GET_HW (GPIO_PORT_0)
    };    
    gpio_hal_input_enable (&_gpio_hal, PWM_PIN);

    // test reading PWM_PIN
    for (int i = 0; i < 100; i ++) {
        delayMicroseconds (111);
        Serial.println (gpio_get_level ((gpio_num_t) PWM_PIN));
    }

}

void loop () {

}

@gin66
Copy link
Author

gin66 commented Jun 13, 2024

great News! Thanks for figuring out and sharing the solution. So I can make use of this in my lib, too.

@gin66 gin66 closed this as completed Jun 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants