Pi pico control PWM with arduino IDE? #184
-
Hi, |
Beta Was this translation helpful? Give feedback.
Replies: 10 comments 5 replies
-
analogWrite is the equivalent. See the documentation from Arduino. In a nutshell, you can set the PWM frequency and then the duty cycle with the actual analogWrite command. |
Beta Was this translation helpful? Give feedback.
-
I found we can use analogWriteFreq(frequency) to set the PWM frequency, as per the ESP8266 Arduino Core. That core also has analogWriteRange(PWMRANGE) too, and that also appears to work - you can set it to 255 or 1023. I haven't checked it with an oscilloscope, to see if it really changes, but I found we're allowed to set it to 1023, or at least try to. I made a version of the simple siren tone() example, to use PWM instead. On my first attempts, I just got silence. It seems calling analogWriteFreq() has that effect, so I'm guessing it resets the waveform each time, causing an oscillator sync effect. Here's a very crude attempt at a rising tone siren, with some sort of stereo effect which I've yet to hear convincingly. If I tweak constants enough, I probably will in the end. I can hear a PWM sweep effect, which sounds a little like moving the filter cut off control on a synth. There are some odd chirps at the beginning of each sweep of the siren, which seem to be related to resetting the wave and starting again each time I call analogWriteFreq
|
Beta Was this translation helpful? Give feedback.
-
Interesting. Yes, there are a lot of clicks and pops. I've also noticed that analogWriteFreq() doesn't give the frequency asked for, for part of the range - I haven't yet worked out whether it's lower frequencies or higher ones, but slowing down how often I update it, I can hear it's playing a pseudo random tune, at one end of the sweep. The tone() example sounds more musical, but of course it has limitations too. I've been meaning to ask how interrupts can be used, for arduino-pico (I found I can't use ISR(), for example), but that's off topic, so I may start a new discussion when I've looked into it more. |
Beta Was this translation helpful? Give feedback.
-
analogWriteFreq() doesn't give the frequency I asked for, and tuning Pico's hardware timer seems too difficult to understand, that's the reason why I asked the question. Maybe by counting the DigitalWrite() HIGH and LOW time, frequency and duty cycle of the PWM can be programmable? |
Beta Was this translation helpful? Give feedback.
-
I made a start on making a PWM tone generator, which is the same problem but intended more for music. The idea is to just watch the microseconds timer, and decide if it's time to flip the state of the output, yet. The downside is that if an interrupt happens, the state will stay as it is for a little longer than it should, but will flip the next time the code inside the loop is called. I originally intended to add code to make a siren, so there are some variables in this that don't do anything, yet. Everything is global, and not even in structures, because I wanted to keep it as cycle efficient as possible. Since this doesn't wait, you could rename the loop() as something else, e.g. pwm_update() and just call it from loop() as often as possible. The rest of the code in loop could then get on with doing other things, provided they don't take so long that it delays calling the pwm_update() function too much. It currently just plays a square wave test tone, at 435Hz. I went with milli Hz as a unit of frequency, so I can add accurate pitch bending and detuning. Next, I'll probably add knobs to change the frequency and mark/space ratio, but as far as I know, that should already work by just changing constants to try it. The calcPeriods() that's called in setup(), also needs to be run, each time the mark and space changes... It might be more convenient to change those to just a ratio too. At the moment, they're meant to be two numbers that add up to markSpacePrecision, which is currently 1024.
|
Beta Was this translation helpful? Give feedback.
-
One thing to note is that the micros and millis timers loop, so you have to be careful how you do the comparison. You have to think of it in terms of modulo arithmetic aka clock arithmetic. You need to avoid addition, followed by a comparison, because the number after the addition could have wrapped around to just past zero. For the micros timer, that happens about every 70 minutes, apparently, so if you do the comparison the wrong way, it's likely to work okay for 70 minutes, then stall for another 70 minutes. That's why it's done like this:
where utime is the current value, from micros(), lastStateChangeTime is the last time the output was flipped, and stateChangePeriod is the amount of time to wait, in microseconds, before flipping it again. By doing it as a subtraction, we avoid the roll-over problem where the timer goes back to zero. That works because both variables, utime and lastStateChangeTime, are unsigned, and have the same number of bits, as does the answer. It's modulo arithmetic with a base of 2 to the power of the number of bits. |
Beta Was this translation helpful? Give feedback.
-
You can try my new hardware-based RP2040_PWM library which can support PWM frequency from very low (7.5Hz) to very high (Mhz). With frequency lower than 7.5Hz, try this ISR-based RP2040_Slow_PWM Library |
Beta Was this translation helpful? Give feedback.
-
I found that according to the documentation, |
Beta Was this translation helpful? Give feedback.
-
Great! It would probably be a good idea to mention these limits (frequency * range <= 125 MHz) in the documentation, as they may not be obvious to everyone. |
Beta Was this translation helpful? Give feedback.
-
What is the reason for the 60 kHz limit, by the way? For audio purposes, that's a bit low (only 1.5 cyles per sample if we set a suitable sampling rate of 40 kHz to cover the maximum audible frequency of 20 kHz). Shouldn't it be possible to go up to, say, 488 kHz? That would still allow a PWM level resolution of 8 bits. As far as I can see, the Pico SDK does not set an upper limit except the 125 MHz counter frequency. |
Beta Was this translation helpful? Give feedback.
I made a start on making a PWM tone generator, which is the same problem but intended more for music. The idea is to just watch the microseconds timer, and decide if it's time to flip the state of the output, yet.
The downside is that if an interrupt happens, the state will stay as it is for a little longer than it should, but will flip the next time the code inside the loop is called.
I originally intended to add code to make a siren, so there are some variables in this that don't do anything, yet. Everything is global, and not even in structures, because I wanted to keep it as cycle efficient as possible.
Since this doesn't wait, you could rename the loop() as something else, e.g. pwm_u…