ammolytics / projects

Source code and instructions for do-it-yourself projects.
https://blog.ammolytics.com
MIT License
52 stars 20 forks source link

PWM control for Vibro Motor #33

Closed Shooter0423 closed 3 years ago

Shooter0423 commented 4 years ago

Wonder if anyone has worked on using pwm to control the vibration motor. A few weeks back someone mentioned it in a post they made, but I cant seem to find it now, and @erichiggins has mention it a time or two. I am trying a rewrite of motor.js to use pwm as implemented in rpio. This doc is what i'm using https://www.npmjs.com/package/rpio reference. I have got the program to not throw any errors when loading but not to actually run the vibro motor. I am unsure what numbers to use for .pwmSetClockDivider (), I used 4096 which is the max allowed and gives the minimum freq of 4.7 kHz. And for .pwmSetRange I used 100, then a point you select in .pwmSetData becomes the On Time (duty cycle)

erichiggins commented 4 years ago

I created a separate branch for you called feature/pwm-motor which includes a PWM-based rewrite of the motor control.

Hopefully that helps you to explore.

erichiggins commented 4 years ago

Edit: I corrected a few statements I made in my original post to reflect that PWM is still useful, but the frequencies available are too fast to match those that the Open Trickler uses today. All this means is that PWM can't be a drop-in replacement to achieve the same ON/OFF cycles that I coded by hand. Instead, PWM could potentially be used to speed up and slow down the motor more smoothly.


I just did a deep-dive to investigate PWM a bit more, hoping that I could incorporate it and close this out. There's a problem: PWM control runs too fast to match the current ON/OFF frequencies.

I'll provide an explanation w/ the simplest math I can provide, but first, some facts upfront:

Let's run through an example using PWM to make the motor run as fast as it can. Put another way, the shortest possible ON pulse. First, we have to set the refresh rate using rpio.pwmSetClockDivider(). The value that gives us the fastest option is 2. Here's the math:

19,200,000 hz / 2 = 9,600,000 hz (9.6 Mhz)
Total pulse width = 1 second / 9.6Mhz = 0.000000104 seconds * 1000 = 0.000104 milliseconds

Regardless of what we do next, a 50% duty cycle here would turn the motor ON for half of this total pulse width, in this case 0.000104ms / 2 = 0.000052 ms. Obviously this is way too dang fast and the numbers are too crazy to even focus on the rest of our controls.

Let's look at the slowest possible setting, to use PWM to make the motor run as slow as it can. Put another way, the longest OFF cycle. The highest possible value we can use to set the refresh rate is 4096.

19,200,000 hz / 4096 = 4687.5 hz (4.7 Khz)
Total pulse width = 1 second / 4687.5 = 0.000213  seconds * 1000 = 0.213 milliseconds

We'll continue with the other functions in a moment, but realize that with this pulse width (the slowest we can get), a 50% duty cycle would turn the motor ON for 0.1 ms and OFF for 0.1 ms. If we used a 1% duty cycle, the motor would be ON for 0.002 ms and OFF for 0.211 ms.

This is still way way way too fast. Regardless, let's keep learning about the other two functions.

The usual next step would be to set the Range with rpio.pwmSetRange(). This basically just gives you more resolution from the total pulse width you just set by dividing it by the value you use here.

pulse resolution = total pulse width / range

For a simple example, let's do 100:

4687.5 hz / 100 = 468.8 hz
Pulse resolution = 0.213 milliseconds / 100 = 0.00213 milliseconds

What you use to set the range is meaningless on it's own, but it is useful with the next function. You can use rpio.pwmSetData() to set the number of pulse resolutions that the motor should be ON. Valid options are from 0 (off) to the Range you set.

Carrying on with our example:

So, I hope this helps others to understand what I just learned, which is that PWM probably isn't a great drop-in fit because (as far as I can tell) it's not capable of running at as as slow of a frequency as it runs now.

References:

erichiggins commented 4 years ago

One more clarification here:

The clock division setting seems to define the actual PWM frequency, which isn't affected by other settings.

e.g. If you divide the 19.2MHz clock by 512, the result is a PWM frequency of 37.5kHz. No matter what Range you use (10, 100, or 1000), and no matter what Data you use to set a duty cycle, there will only be one ON and one OFF setting for each waveform. That means that the frequency doesn't really change with the Range and Data functions.

Changing the Range gives you more or less resolution for the Data setting. Changing the Data setting is what controls the duty cycle (0% to 100% of the Range). The duty cycle roughly translates to an average output voltage of the 3.3V or 5V supply (50% of 3.3V = 1.65V).

I need to conduct some actual tests, but based on a little Googling, the ideal frequency for these motors should be in the range of 3-30kHz, though it may depend on the motor used. That limits the clock division options to the following:

I don't know how much the Range actually matters. It seems like using 100 would make the duty cycle % math pretty easy to understand so I'll probably start there.

erichiggins commented 4 years ago

With the motors I'm using, the clock divisions of 256 and 512 seemed to give the best results at a 10% duty cycle. They were barely audible, but perhaps enough to slowly trickle single kernels. Anything higher than that (1024 +) seemed to be too fast to even activate the motor with a 10% duty cycle.

erichiggins commented 3 years ago

PWM motor control is now fully supported!