Low Frequency PWM

DrBanana
Fri Apr 22, 2016 11:23 am
I want to try STM32F103C8T6 Dev board, but before I buy, can I generate PWM of 5 to 100 Hz with 1ms to 100ms pulse width ? I need to create a very precise function generator. And Hardware PWM will be best. Is it possible with STM32F103C8T6 having Arduino ?

ahull
Fri Apr 22, 2016 11:29 am
You would need to check the ST documentation to see how low the PMS frequency can go, however if it turns out the native PMW wont do the trick, with that sort of slow rate, you could instead use a timer interrupt and a counter to toggle your gpio pin(s) at the rate you want. Bit banged PWM should be fairly simple to set up using this trick. The advantage of using an interrupt and a counter is that you can tweak things like mark space ratios and synchronisation/phase shifting between pins with a fairly high degree of precision, without tying up the processor.

mrburnette
Fri Apr 22, 2016 1:15 pm
@DrBanana: welcome.

Your use of the term, “very precise” makes me think that Arduino is not a viable core. Such things really need to be implemented in C or asm and outside of an environment that will have by design an interrupt at the system level for the micros() timer. Thus with interruptions at 1 uS you need to determine if you can work within the (phase) jitter on your PWM that would be introduced by the lower-level interrupt.

A better approach, IMO, would be a GPS receiver with a 1 PPS square wave output. Jitter is now in the low 2-digit nS range. Trigger your hardware dividers (phase lock loop is an option here) off of the leading/trailing edge and you have a relatively inexpensive approach, non-software, for generating a decent variable pulse width; you can then uses the hardware clocks to generate interrupts on a uC to manage capture, display, and other higher-level functionality.

There are also rather inexpensive dedicated chips for DDS these days… but I have not personally played in this sandbox.

Ray


DrBanana
Fri Apr 22, 2016 1:20 pm
That is why I want to use hardware PWM, would be awesome if stm supports it.

mrburnette
Fri Apr 22, 2016 1:23 pm
DrBanana wrote:That is why I want to use hardware PWM, would be awesome if stm supports it.

martinayotte
Fri Apr 22, 2016 1:29 pm
Maybe the PCA9685 can suit your need ?

the frequency used for PWM control in the PCA9685 is adjustable from about 24 Hz to 1526 Hz


mrburnette
Fri Apr 22, 2016 1:45 pm
martinayotte wrote:Maybe the PCA9685 can suit your need ?

the frequency used for PWM control in the PCA9685 is adjustable from about 24 Hz to 1526 Hz


DrBanana
Fri Apr 22, 2016 2:22 pm
The frequency is not the problem actually. But it’s the pulse width that’s ratio is high with frequency.

mrburnette
Fri Apr 22, 2016 2:48 pm
DrBanana wrote:The frequency is not the problem actually. But it’s the pulse width that’s ratio is high with frequency.

Vassilis
Fri Apr 22, 2016 3:20 pm
DrBanana wrote:I want to try STM32F103C8T6 Dev board, but before I buy, can I generate PWM of 5 to 100 Hz with 1ms to 100ms pulse width ? I need to create a very precise function generator. And Hardware PWM will be best. Is it possible with STM32F103C8T6 having Arduino ?

Pito
Fri Apr 22, 2016 4:02 pm
I messed with PWM on the F4 recently (see http://www.stm32duino.com/viewtopic.php?f=39&t=1031 ).
There is a function called setPeriod(pwmPeriod_in_useconds) which returns a max_duty value. This maxduty value is important..
The function calculates internally values for a hw prescaler (1..65536) and also “timer_overflow” values, and it sets up the hw timer such it fits your pwm period values as best as possible.
For example>
maxduty = timer.setPeriod(20000);
sets the PWM period to 20ms (pwm freq=50Hz) and it returns for example maxduty=64515.
Thus you can go with your duty from 0..64515.

F4 Examples only:
pwmWrite(pwm_pin, x); // where 0<=x<=64515 for pwm period=20000us (50Hz)
pwmWrite(pwm_pin, x); // where 0<=x<=56000 for pwm period=1000us (1kHz)
pwmWrite(pwm_pin, x); // where 0<=x<=16800 for pwm period=100us (10kHz)

In F4 there is a bug in the setPeriod() function somewhere (not somewhere frankly – it calculates with a wrong clock_per_usecs value actually), so the pwm period I measured with my LA is twice (half the pwm frequency) of the one set by the setPeriod function..

Mind the maxduty depends on the pwm Period, so it is NOT always “65535” (ie only 16800 for pwm period=100usecs).

Here is the complete PWM source I messed with for the F407disco, it may work with F1 too..
http://www.stm32duino.com/viewtopic.php … 001#p11792


RogerClark
Fri Apr 22, 2016 10:29 pm
When people have specs that say “very precise”, it makes me wonder why they are using the Arduino system.

IMHO its not designed for precision. You can get it to be precise, but I would assume that it is not precise.

For precision, I suspect you would be better off using the STMCube to export code that just does the function you require, and not have all the things going on under the hood that the Arduino system hides from casual users.

Or like several people have suggested, use a separate dedicated PMW generator IC


DrBanana
Sat Apr 23, 2016 12:31 am
Or like several people have suggested, use a separate dedicated PMW generator IC
Could you please name some of them ? The suggested only have 12 bit resolution as my requirement is in microseconds and milliseconds.
Like

200000uS Period(5Hz)
900uS Pulse Width

250000 Period(4Hz)
900uS Pulse Width

and like this. The range can go from 4Hz – 100Hz and Pulse Width can be 900uS to 100ms. See the difference ratio is way high between pulse width and period. I think 12 bit resolution will not work. Even 16 bit wont.


RogerClark
Sat Apr 23, 2016 1:13 am
What accuracy do you require

e.g. 1uS


martinayotte
Sat Apr 23, 2016 1:27 am
DrBanana wrote:
Could you please name some of them ? The suggested only have 12 bit resolution as my requirement is in microseconds and milliseconds.

Vassilis
Sat Apr 23, 2016 8:27 am
To achieve the lower frequency (4 Hz) you need to modify the clock configuration that is predefined in Arduino_STM32 core to 72 MHz.
As an example I show you the above picture from my CubeMX clock configuration.
The APB1 Timer clocks needs to be 0.25 MHz (250 kHz) to get the 65535 period. That gives you a 262ms period length that is close to the 250ms you want. A small adjustment to the 65535 value will give you the exact 250ms period you want.

I used Timer 3 to run a few tests
htim3.Init.Period = 62500 gives you 250 ms period length = 4 Hz
htim3.Init.Period = 2500 gives you 10ms period length = 100 Hz

As other people suggested, you either have to use an additional chip to do that or use the CubeMX software to write the source code according to your needs because in my opinion, that case is something that goes out from the limits of arduino core.


Pito
Sat Apr 23, 2016 9:59 am
Excel is your friend. You may find the setPeriod() function in HardwareTimer.cpp in STM32F1. So you may use it in your code easily.
This is what you get based on calculation the setPeriod() provides for you:

F1 PWM.JPG
F1 PWM.JPG (126.28 KiB) Viewed 3802 times

DrBanana
Sat Apr 23, 2016 10:26 am
The easiest way would be an FPGA or CPLD. The libraries usually include everything you may need
What they would be? Google seems no help. Could you please explain a bit about those 2.

Thank you.

PS: nvm I think they are some sort of components. Got more stuff on Google.



RogerClark
Sat Apr 23, 2016 11:24 am
DrBanana wrote:The easiest way would be an FPGA or CPLD. The libraries usually include everything you may need
What they would be? Google seems no help. Could you please explain a bit about those 2.

Thank you.

PS: nvm I think they are some sort of components. Got more stuff on Google.


Vassilis
Sat Apr 23, 2016 12:08 pm
I totally forgot the timer2 prescaler :o
Nice catch Pito!

@DrBanana Some code to work on
const int pwmOutPin = PA1;
HardwareTimer pwmtimer(2);

void setup() {
pinMode(pwmOutPin, PWM);
pwmtimer.pause();
pwmtimer.setPrescaleFactor(220); //Prescaler
pwmtimer.setOverflow(65455); //Period width
pwmtimer.setCompare(TIMER_CH2, 32768); //Pulse width
pwmtimer.refresh();
pwmtimer.resume();
pinMode(pwmOutPin, PWM);
}

void loop() {
}


DrBanana
Sat Apr 23, 2016 12:20 pm
You guys are all awesome. One more thing, the value 65k is being used as max shows its 16bit timer. I think stm32 has 32 bit timer too, so if that’s true can’t I use it?

Besides the FPGA and other one seems to be another awesome way.


martinayotte
Sat Apr 23, 2016 12:40 pm
For FPGA, there are some opencores :

http://opencores.org/project,pwm
http://opencores.org/project,ptc

Generic VHDL implementation :

https://eewiki.net/pages/viewpage.actio … d=20939345

For CPLD, here is the general idea (although only 8 bits) :

http://dangerousprototypes.com/docs/CPL … _Generator

I’ve done such thing in past (in 199x), an 8 bits too, with GALs and TTLs…


Pito
Sat Apr 23, 2016 5:30 pm
This works too, using the setPeriod().
Maple Mini.

const int pwmOutPin = PA1; // pin10
HardwareTimer pwmtimer(2);

uint16 maxduty, duty;
uint32 period, mypulse;

void setup() {
pwmtimer.pause();
period = 250000; // PWM period in useconds, freq 4Hz
maxduty = pwmtimer.setPeriod(period);
pwmtimer.refresh();
pwmtimer.resume();
pinMode(pwmOutPin, PWM);
}

void loop() {
mypulse = 23456; // 0<=mypulse<=period, this is the High pulse of my length in useconds
duty = map((int32)mypulse, 0, (int32)period, 0, (int32)maxduty);
pwmWrite(pwmOutPin, duty); // 0<=duty<=maxduty
while(1){};
}


RogerClark
Sat Apr 23, 2016 9:56 pm
martinayotte wrote:For FPGA, there are some opencores :

http://opencores.org/project,pwm
http://opencores.org/project,ptc

Generic VHDL implementation :

https://eewiki.net/pages/viewpage.actio … d=20939345

For CPLD, here is the general idea (although only 8 bits) :

http://dangerousprototypes.com/docs/CPL … _Generator

I’ve done such thing in past (in 199x), an 8 bits too, with GALs and TTLs…


martinayotte
Sun Apr 24, 2016 1:25 am
Hi Roger,

Back in these days, PALs been superseded by GALs (with EEPROM fuses), but than the CPLD and FPGA superseded them…

That make me think that I didn’t played with my Cyclone IV and Spartan6 FPGAs since more than a year …

Since then, I’ve upgraded my workstation OS, so I will have to re-install those respective IDEs, hoping with not much troubles.

Challenge ? 32 outputs individual PWM accessed by I2C Slave register map, syncrhonized with Zero-Crossing from 60HZ Main Power !
Yes, 32 Dimmers channels, like this 199x project, but with FPGA …


RogerClark
Sun Apr 24, 2016 3:29 am
Hi Martin

I recall working with GAL devices, but that was years ago :-)

I’m actually building a dimmer using an STM32 at the moment, but not doing anything fancy. Just detect the crossing then use a hardware timer to delay the trigger.
So I have both a zero crossing and trigger ISR. I know if I dig into the docs, I don’t need to use a ISR for the zero crossing, I should be able to get a one short timer triggered by the zero crossing.
But I just wanted to get something working, and separate ISRs is easier.

Getting an effecient zero crossing detector is as complex as the software. I dislike the ones which just use 2 large (1 W) resistors to drive an opto. As the whole detector circuit gets hot. It’s also prone to spikes, miss-triggering the software.

So I’m using a more complex circuit I found on the web using a bridge, a transistor, a capacitor and some resistors.
It’s power usage is virtually nothing and it has a 3db low pass filter at 300hz, which helps suppress noise


Pito
Sun Apr 24, 2016 6:36 am
Back to the DIY high precision PWM design – as the OP has never heard about FPGA and CPLD it is a NoGo for him as the messing with FPGA a CPLD requires certain knowledge in several areas he does not possess yet. So the solution is to stay with the MCU and w/ the precision as depicted in the table above. In parallel he may investigate whether there is an off-the-shelf solution available..

RogerClark
Sun Apr 24, 2016 10:18 pm
I just came across this timer calculator ( windows exe)

http://libstock.mikroe.com/index.php?ur … calculator

I’ve not tried it, and Im sure you can do the same calculators in excel etc.
But it may be useful.


Leave a Reply

Your email address will not be published. Required fields are marked *