Giter VIP home page Giter VIP logo

khoih-prog / rp2040_pwm Goto Github PK

View Code? Open in Web Editor NEW
62.0 6.0 19.0 211 KB

This library enables you to use Hardware-based PWM channels on RP2040-based boards, such as Nano_RP2040_Connect, RASPBERRY_PI_PICO, with either Arduino-mbed (mbed_nano or mbed_rp2040) or arduino-pico core to create and output PWM any GPIO pin. The most important feature is they're purely hardware-based PWM channels, supporting very high PWM frequencies. Therefore, their executions are not blocked by bad-behaving functions or tasks. This important feature is absolutely necessary for mission-critical tasks. These hardware-based PWMs, still work even if other software functions are blocking. Moreover, they are much more precise (certainly depending on clock frequency accuracy) than other software-based PWM using ISR, millis() or micros(). That's necessary if you need to control devices requiring high precision. New efficient setPWM_manual function to facilitate waveform creation using PWM

License: MIT License

C 23.06% C++ 76.37% Shell 0.57%
pwm pwm-driver duty-cycle hardware-based-pwm pwm-frequency multi-channel-pwm non-blocking mission-critical pwm-slice rp2040

rp2040_pwm's Issues

Using PWM to step a stepper driver

Thank you for making your RP2040_PWM library available. It was super easy to use. Yesterday I tried using it to drive a single stepper motor with it using a STEP/DIR style stepper driver. A TMC2209 in my case. It worked perfectly at first try.

I discussed the details with the author of arduinocontinuousstepper to hear his take on it and he thinks it has merit.

However, looking ahead there might be an issue when multiple motors are used. From what I understand from your RP2040_PWM project is that we can have 8 independent PWM frequencies but will they all run in sync.

So my question is: Are all these frequencies derived from a common clock or is there a way to achieve synchronization between PWM channels?

[edit]

Since I posted this I came across a video explaining the pico pwm in laymans terms. From it I understand that each PWM channel is basically a hardware based counter counting pulses from a common125MHz clock using their own setpoint and wrappoint. That confirms to me that yes there is a common clock and my motors will be perfectly in sync.

But please correct me if I am wrong :-)

Wrong frequency

Describe the bug

I'm getting wrong results.

Steps to Reproduce

I'm running a modified version of your PWM_Multi example on a Arduino Nano RP2040 Connect.

Expected behavior

9 kHz output

Actual behavior

I get 11.81kHz according to the oscilloscope.

However if I change the code to 10kHz for instance, I get 10kHz.

Debug log

Starting PWM_Multi on Nano RP2040 Connect
RP2040_PWM v1.0.0
=============================================================
Index	Pin	PWM_freq	DutyCycle	Actual Freq
=============================================================
0	4	9000.00		50[PWM] bestfit_PWM_Freq = 8999.28, PWM_Freq_diff = 0.72, Top_p_1 - 1 = 13889, vsDiv/16.0 = 1.00
[PWM] bestfit_PWM_Freq = 8999.93, PWM_Freq_diff = 0.07, Top_p_1 - 1 = 13888, vsDiv/16.0 = 1.00
[PWM] bestfit_PWM_Freq = 9000.01, PWM_Freq_diff = 0.01, Top_p_1 - 1 = 10581, vsDiv/16.0 = 1.31
[PWM] PWM_Freq = 9000.00, _actualFrequency = 9000.01, PWM_Freq_diff = 0.01
[PWM] TOP = 1DIV = 1
		9000.01
[PWM] 
TOP = 10581, DIV = 1, CPU_freq = 125000000
=============================================================

Screenshots

Oscilloscope screenshot here.

Measuring Duty Cycle Example

I can't seem to find an example of measuring the duty cycle (or frequency) of an input signal.

Could some example code be provided please?

Duty cycle as integer rather than float

As far as I understand, currently the duty cycle needs to be specified as a float value bewteen 0.0 and 100.0 (percent), which is then turned into a counter compare value (integer) by the library. This means that several floating point operations are required each time the duty cycle is changed. This can add up to quite a lot of floating point operations if PWM is used, for example, to output analog waveforms at high frequencies, where the duty cycle can change thousands of times per second. And as far as I understand, these operations consume quite some time, as the RP2040 does not have a floating point unit.

Could it therefore make sense to add an option to provide the duty cycle as an integer directly (i.e. between 0 and the TOP value)? In many cases, e.g. when waveforms are read from a lookup table of samples, the programmer already knows the absolute integer values anyway, and it would make little sense to first convert them into a float percentage value and then immediately reverse that conversion in the library.

An error should then be issued if the duty cycle value exceeds the TOP value.

Setting duty cycle without disturbing frequency

Describe the bug

Added in 1.0.5:

  1. Permit changing dutyCycle and keep same frequency on-the-fly should now work but it's not clear how.
  2. Add example - Example is not clear and is not included in the downloaded library from Arduino
  3. Not sure why bards need to be listed but I'm using the WaveShare RP2040-Zero $5 and small and castellated, just what I need for including in my project. Only difference from a generic is the cute little WS2812 RGB LED which I can't get to work.
  4. Yep, I can lower the debug level, though I'm not sure what those warnings are about.

A clear and concise description of what the bug is.
What are the units you are using for duty cycle? Normally with 16 but PWM you would just load a 16 bit count value and be done with it. It looks like you might be using float of 0.0 - 100.0% - Trying to be fast here why mess with floating point numbers?
I tried both

    PWM_Instance[0]->setPWM(pin0, freq, (double(lookup8to16[i]), false);  //  Pass 16 bit value, but as a double (which is just a float?)
    PWM_Instance[2]->setPWM(pin2, freq, (double(lookup8to16[(i])/655.35, false);  // 
  // lookup8to16[] is a 256 value array of the gamma curve so brightness is properly log scale for human eye

Steps to Reproduce

Used your example PWM_Multi adding to the loop:

  for (uint i=255; i>0; i-- ) {
     PWM_Instance[0]->setPWM(pin0, freq, (double(lookup8to16[i]), false);  // 
    PWM_Instance[2]->setPWM(pin2, freq, (double(lookup8to16[(i+0x80)&0xff])/655.35, false);  // offset this one by 1/2 the table
    delay(30);
  } 

Also tried

// Do it by reaching in past the library to the PWM hardware
    pwm_set_counter (pwm_gpio_to_slice_num(pin0), (lookup8to16[i]/655.35));  // 0 to 100%?
   pwm_set_counter (pwm_gpio_to_slice_num(pin1), lookup8to16[(i+64)&0xff]); // 16 bit value?

Expected behavior

I'm looking for a smooth increase and then decrease in the duty cycle (and thus LED brightness) from 0 to 100% and back to 0

Actual behavior

Depending on which version I try I get some smooth action then big jumps in brightness (something is wrapping to zero at the wrong time). Even when it is working over some of the range it's flickering when it shouldn't (resetting the slice? IDK)

debug

set level to 3 don't get any reports
One odd thing is that when I enable the Serial Monitor it always says (in orange at the top of Monitor pane):
"Not Connected. Select a board and port to connect automatically"
But then it works anyway. Took me a while to realize it's in a pane of the program window not a separate window as in 1.8
1.8 couldn't find the PIO.h file

Screenshots

I could send a video of the LED or scope traces of the PWM (Have to set up the o'scope)

Information

  • Arduino IDE 2.0.0 rc3 (also tried 1.8.15 but messed that up installing arduino-pico)

  • `RP2040-Zero from WaveShare

  • Contextual information
    Smooth deep dimming down to 1/65536 full 16 bit dimming (most commercial dimmers are 10%-100% and "best" e.g. DALI are still only 1:1000 i.e. 0.1% Eyes can see much better than that.

  • Simplest possible steps to reproduce
    Using PWM_Multi but removed array for frequence fixed freq = 2000;
    Loop changed to above code

  • Anything that might be relevant in your opinion, such as:
    Aspire 5 core i5 2.4GHz 20GB Ram; Windows 11 Home 21H2 - if that matters
    I'd like a simple to use library, the whole point of a library is to hide complexity so once the PMW slice has been set up there should be simple routines like

  setPWMfreq(pin#,newfreq);
  setPWMdcPct(pin#,newdc); // In percent if people really want that
  setPWMdc16(pin#, uint newDCval);  // 16 bit value

I've been reading the Pico hardware manual, it's dense, this machine is so powerful!
I'm happy that you dug in and extracted a working PWM package, it just needs a little tweeking and documentation I think.

BTW another odd thing:
After working for quite a while loading code after compile just fine. It quit loading and now requires unplugging and holding boot button every time. Is that because I'm using all the PIO?

Attempting to Alter the Duty Cycle w/o changing any other values

Arduino IDE 1.8.19
RP2040_PWM - v1.0.4
Using Example - PWM_DynamicFreq

I'd like to modify the Duty Cycle w/o altering the Frequency. My goal is to change the speed of a DC Motor
in a small project. When I change the Duty Cycle in the example without changing the frequency, the Duty Cycle
remains the same as it was before the "SetPWM" call. I looked at your code to see if I was using the wrong call
but do not see a specific call for altering only the Duty Cycle. If I alter the Frequency in your example by any
amount, the Duty Cycle is changed as I specify in the SetPWM call. If I do not alter the Frequency, the Duty Cycle
remains unchanged.

Can you show me the proper way to alter the Duty Cycle w/o changing any other values?

Changing Duty Cycle Dynamically Creates Runt PWM pulse

Arduino IDE version 1.8.13
RP2040 Core Version arduino-pico core v2.5.2
RP2040 Board type RASPBERRY_PI_PICO
Using OSX 12.6

Contextual information

There was observed to be a runt or glitch generated when switching the duty cycle, that lasted only the cycle the update occurred at. This runt/glitch is detectable by our receiver, and although it only lasts one cycle, it causes jitter issues.

Is there a way to switch Duty Cycle precisely between the last complete cycle of the current PWM cycle and the next complete cycle of the new PWM duty cycle value.

Simplest possible steps to reproduce (see attached scope shot). You can pick different duty cycles and get the same effect.

const float PWMFREQ           = 100;      // PWM frequency on GPIO pin
const byte GPIO_PIN           = 19;

void setup()
{
  PWM_Instance = new RP2040_PWM(GPIO_PIN, PWMFREQ, 0);
  PWM_Instance->setPWM(GPIO_PIN, PWMFREQ, 0, true);
  ..
}
void loop()
{   
  PWM_Instance->setPWM(GPIO_PIN, 100, 50, true);
  ...
  PWM_Instance->setPWM(GPIO_PIN, 100, 20, true);
  ...
}

Attached Scope Screen Shot where the duty cycle changed.

SDS00007

Frequency and Duty Cycle Measurement

The "more information" part of your readme talks about measuring PWM frequency and duty cycle. It doesn't appear that that functionality has been implemented in your library. Assuming I'm not overlooking it, I wonder, is that something that you have considered implementing?

If not, are you aware of any examples of it being done? I have some Arduino experience but low-level embedded programming isn't my strong suit, so before I delve into the C/C++ SDK to see if I can learn to do it that way, I'm wondering if there are any alternatives.

Thanks for any insight you can provide.

Request for Clarification on PWM Slices and A/B sides

Thanks for this library and for providing a Duty Cycle change function. I am using version 1.12.0 now and tried creating 2 differing PWM pins on adjacent GPIO's (GP10 & GP11). According to the Programmer's Model, GP10 would use 5A and GP11 would use 5B so I assumed they could have differing Duty Cycles with the same Frequency. Alas, when I set GP10, GP11 stops and when I set GP11, GP10 stops. I changed to adjacent pins GP11 and GP12 which use 5B and 6A which now works. The board I am building controls two DC motors on a dual drive robot chassis. Because my board is custom, I have the flexibility to manage my own pin assignments. This is not the case with some commercial boards which have pre-assigned pin usage. My question is, am I correct in that dual duty cycle pins have to be on non-adjacent slices or is there a special way to generate differing duty cycles on adjacent pins using the same slice but differing A & B sides? Thank you - Austin

Change the PWM frequency

Hi Khoi,

Describe the bug

I'm not sure if this is a bug or a "feature", but I wanted to dynamically change the PWM frequency and I'm not able to do so using the setPWM function.

Steps to Reproduce

Example code: https://pastebin.com/raw/bAJ09Jn9

Expected behavior

I was expecting to have 2s of 1000Hz and then 2s of 2000Hz repeatedly.

Actual behavior

Frequency stays constant at 1000Hz.

Thanks,
Américo

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.