Custom PWM Frequency Example


#1

I figured it must be doable from the Sparkulator, so I set off tonight to try and make it possible to change the PWM frequency to whatever you want. Default in the Spark Core is 500Hz and I tested this up to 64000Hz on all 8 PWM outputs. It works flawlessly and I’m sure it can go higher. You’ll notice in the comments I also have fixed the PWM glitch by keying off of the fact that if the pin_mode is set to AF_OUTPUT_PUSHPULL it is already setup as a PWM. If this mode is only used for PWM, we’re good… if not, some other state variable will have to be used. When the PWM is already set up, we just update the duty cycle on successive calls to analowWrite2(). PWM also achieves true 100% because there is no glitch now.

Because it’s so easy to change the frequency, maybe we can gain access to this in an easier way? Spark.pwmFreq(1000);

cc: @zach @zachary


Photon PWM resolution
Questions about PWM
Timers & interrupts PWM
#2

Awesome! This article seems to indicate that there are 4 separate channels that can be triggered from the same timer, which presumably means it’s possible to have 4 different PWM frequencies?


#3

+1 for Spark.pwmFreq(1000); // or something like it - at least up to 96kHz for possible SoftAudio - once the lag caused by the cloud processes does not interfere with the user process anymore.

One possible problem with changing the timer frequency might arise, if that very same timer is used for any other standard function (i. e. delay() or so).
As I recall this was to consider when messing with the timers on Arduino.

But I have not got down that far into all of the Core FW source, so it might be easier if our highly honoured Sparkies (:wink: @zach, @zachary, …) could answer this from the top of their heads.


#4

Based on what I saw in the code, yes there are 4 different timers that are all set up the same way. You COULD set them up differently. Are we starting to see the power of the Spark Core vs. Arduino? I hope so!

Good point @ScruffR … needs investigation!


#5

You all are just awesome. Added to backlog. :smile:


#6

This is truly awesome and really validates my decision to go with a Spark Core for my project!


#7

Not to sound naive… But unless the STM32f is different from the STM32l, proper analog output is built into the chip and is possible. Why isn’t the built in analog output being used?

On the topic of the PWM frequency, the chip definitely allows for any calculated frequency to be set using the correct PSC, ARR, and CCR values.

– A Curious Developer


#8

Hi @JakeDOD

The ARM on the Spark core does not have any DACs, just two ADCs. You need to go to the “high-density” STM32F103’s the get 2 DACs. The three parts in the datasheet with DACs are 256k to 512k Flash and 48k to 64k RAM, but also come in a physically larger package.

The STM32F103 datasheet is here:

https://github.com/spark/core/blob/master/Datasheets/ST_STM32F103CB.pdf

Arduino also does not have DACs and the analogWrite() command has traditionally been used for PWM outputs that when coupled with an appropriate low-pass filter, can generate an analog output of sorts.


#9

Gottcha, thanks for the clarification. I’m not used to Arduino, so your help on the background of analogWrite() is appreciated as well.


#10

I think if we were to expose this, we’d want to make it part of either pinMode() or analogWrite(). Maybe like this:

// Regular analogWrite
analogWrite(PIN, LEVEL);

// Custom frequency analogWrite
analogWrite(PIN, LEVEL, FREQUENCY);

#11

That would work, assuming we don’t re-initialize the PWM every time we call that function. It just needs to be a little bit smart about the fact that it already set it up, and if it has… then if it needs to change the LEVEL (duty cycle) it just updates the corresponding register, but if it needs to change the FREQUENCY then I’m pretty sure it will have to re-init.

And you can easily initialize the FREQUENCY value to 500 in case someone doesn’t specify all 3 arguments.

I’ll work on this when I can get some time, unless Satish beats me to it. That guy is a machine! :wink:


#12

See this issue: https://github.com/spark/firmware/issues/213


#13

This is what’s going on for the pin when i set it to 1Khz


#14

Hi @kennethlimcp

Not sure which things are you worried about here…

The high-time is pretty short–is the period right?

Are you worried about the ringing on the rising and falling edges? That can probably be fixed with a few external parts.


#15

Hey @bko sorry for the lack of description!

Was in the midst of using the scope and uploaded it quick before I forget about it.

The period is fine up to 10Mhz but the waveform is not exactly square.

So the issue is only at 1Khz we start to observe this and at higher frequency it gets worst.

Shouldn’t the core be able to safely generate PWM of higher than the 500Hz norm freq?


#16

@kennethlimcp, the image only shows half the cycle. As @bko pointed out, the period is off for a 1KHz output and looks more like a 1MHz output. Can you show the settings you used for the timer and channel?

As for the noise, I am not surprised especially if it’s sitting on a breadboard and you have a jumper connected to the board and then your probe. Also, if your probe is not tuned, it affects the “edges”.


#17

As @peekay123 says, step one on any oscilloscope is to calibrate your probes to the channels you’ll be using them on. Hook the scope probe tip and ground to the reference waveform output (usually some test hook/clips that stick out of the scope down near the BNC connectors). Adjust the vertical and horizontal controls to view one complete rising and falling edge or a whole cycle if you wish. Using a non-metallic screwdriver, adjust the probe set screw until the waveform has no over or undershoot on the rising/falling edges.

It will never be “square” really, since all wires have inductance, capacitance, resistance and all outputs have a finite amount of current that is sourced. These components along with the frequency of the output waveform affect the slope of the rising and falling edges. Typically if everything is fixed, the slope will decrease inverse-proportionally to the output frequency.

It also helps to have a high frequency (aka expensive) scope probe. Most probes that come with cheap scopes are rated for 25 - 50MHz. You need a >300MHz probe to really be able to even see some of the high frequency ringing and noise present in most analog and digital signals, otherwise it’s just attenuated by the probe and never shows up at the scope.

I don’t know about 10MHz, but I have tested the Core’s PWM up to 64kHz. I don’t remember how square it was though :wink:


#18

There’s a lot of learning and it’s awesome!

Honestly, the only time I used a scope is in 2011 and no one taught this even till the day I graduated. It was more of self tinkering but that’s not an excuse to not be aware of such stuff :wink:


#19

Your code helped me out and I was able to extend what you wrote to include changing the resolution as well. Its up to the user to ensure that a frequency and resolution are a valid combo.

Thanks for your code!

Variable PWM frequency and resolution


#20

Can someone tell me if this has been added to the Core’s firmware, or is additional code still necessary? I’m trying to change my Core PWM to about 30 kHz.