Hi!
Just want to share my findings regarding ramp generation with PWM. In case somebody wants to make a library out of the following stuff please feel free to proceed - but as there is knowledge of other people involved I would like to ask for publication in this thread.
As mentioned in this thread I started a while ago with basic PWM ramps, driven by this PWM library for the DUE. Here an example showing 2 stepper motors, travelling synchronised but with different acceleration:
#include <pwm01.h>
float stepper1_min = 50; // Stepper1 start speed in Hz
float stepper1_max = 400; // Stepper1 desired speed in Hz
float stepper1_actual; // Stepper1 actual speed
float stepper2_min = 25; // Stepper2 start speed in Hz
float stepper2_max = 200; // Stepper1 desired speed in Hz
float stepper2_actual; // Stepper2 actual speed
float ramp_delay = 500; // ramp stair-step duration in micros
float ramp_relation; // speed relation Stepper1 / Stepper2
float ramp_val = 0; // ramp stair-step
void setup() {
pwm_set_resolution(16);
pwm_setup(8, 0, 1); // setup PWM clockA at pin 8
pwm_write_duty(8, 32767); // setup 50% duty cycle
pwm_setup(9, 0, 2); // setup PWM clockB at pin 9
pwm_write_duty(9, 32767); // setup 50% duty cycle
ramp_relation = (stepper1_max - stepper1_min) / (stepper2_max - stepper2_min);
delay(100);
}
void loop() {
accel();
deccel();
while (1);
}
void accel() {
while (stepper1_actual < stepper1_max - 1) {
stepper1_actual = stepper1_min + ramp_val;
stepper2_actual = stepper2_min + ramp_val / ramp_relation;
pwm_set_clockAB_freqs(stepper1_actual, stepper2_actual);
delayMicroseconds(ramp_delay);
ramp_val++;
}
pwm_set_clockAB_freqs(stepper1_max, stepper2_max);
}
void deccel() {
while (stepper1_actual > stepper1_min) {
stepper1_actual = stepper1_min + ramp_val - 1;
stepper2_actual = stepper2_min + (ramp_val - 1) / ramp_relation;
pwm_set_clockAB_freqs(stepper1_actual, stepper2_actual);
if (stepper1_actual <= stepper1_min) {
pwm_set_clockAB_freqs(0, 0);
ramp_val = 0.0;
stepper1_actual = 0.0;
stepper2_actual = 0.0;
}
else {
delayMicroseconds(ramp_delay);
ramp_val--;
}
}
}
I modified the library a little bit, see attachment. I was able to accelerate the steppers up to ~3000 RPM. To verify the PWM frequencies I put together the following sketch:
// PWM Frequency verifyer for Arduino DUE
// Uses PWM library pwm01 by randomvibe and Collin Kidder
// Uses Frequency Counter Dave Curran based on http://forum.arduino.cc/index.php?topic=64219.msg1433217#msg1433217
// frequency calculation inspired by Arduino libsam FindClockConfiguration()
#include <math.h>
#include <pwm01.h>
// PWM Pin: 8 // PWM output at Pin8
int input_pin = 12; // frequency meter input at Pin12
uint32_t mck = VARIANT_MCK;
uint32_t pwm_duty = 32767; // = 50% duty cycle
uint32_t pwm_frequency = 400;
void setup() {
Serial.begin(115200);
pwm_set_resolution(16);
pwm_setup(8, pwm_frequency, 1);
pwm_write_duty(8, pwm_duty);
delay(100);
Serial.println(" ");
Serial.println(" ");
Serial.println(" ");
}
// volatile variables used during isr calls
volatile unsigned long startTime;
volatile unsigned long endTime;
volatile unsigned long count;
// trigger isr, sometimes there is an initial false trigger
void trigger()
{
attachInterrupt(input_pin, start, RISING);
}
// initial isr, record the start time and enable the pulse isr
void start()
{
startTime = micros();
attachInterrupt(input_pin, pulse, RISING);
}
// subsequent isr, record the time and increment the count
void pulse()
{
endTime = micros();
count++;
}
// calculate the frequency as the number of pulses received during the sample interval
float getFrequency(unsigned int sampleTime)
{
// start counter
count = 0;
attachInterrupt(input_pin, trigger, RISING);
// delay, isr calls count pulses during this time
delay(sampleTime);
// no more isr calls
detachInterrupt(input_pin);
// calculate the result, note not depended on sampleTime, using more accurate start and end times of pulses received
if (count == 0)
{
return 0;
}
else
{
return float(1000000 * count) / float(endTime - startTime);
}
}
// storage for the last frequency reading
float lastFreq = 0;
void loop()
{
pwm_set_clockA_freq(pwm_frequency);
float freq = 0;
float period = 0;
// check for low frequencies
if (lastFreq > 5000 || lastFreq == 0)
{
// 1Hz sample rate
freq = 1000 * getFrequency(1000); // mHz @ 1Hz
}
else
{
// drop sample rate to 0.3Hz for lower frequencies < 5Hz, can now read down to 1Hz
freq = 1000 * getFrequency(3333); // mHz @ 0.3Hz
}
lastFreq = freq;
// don't divide by zero
if (freq > 0)
{
period = 1000000000 / freq; // uS
}
// split it up into 1000 chunks
unsigned int MHz = freq / 1000000000;
unsigned int KHz = (freq / 1000000) - (MHz * 1000);
unsigned int Hz = (freq / 1000) - (KHz * 1000) - (MHz * 1000000);
unsigned int mHz = freq - (Hz * 1000) - (KHz * 1000000) - (MHz * 1000000000);
unsigned int mS = (period / 1000);
unsigned int uS = period - (mS * 1000);
unsigned int nS = (period * 1000) - (uS * 1000) - (mS * 1000000);
unsigned int pS = (period * 1000000) - (nS * 1000) - (uS * 1000000) - (mS * 1000000000);
Serial.println(" ");
Serial.print("Input Frequency: ");
Serial.print(pwm_frequency);
Serial.print(" Calculated Frequency: ");
Serial.print(calc_freq(pwm_frequency), 3);
Serial.print(" Measured Frequency:");
if (MHz > 0)
{
print3digit(MHz, ' ');
Serial.print(',');
print3digit(KHz, '0');
Serial.print(',');
print3digit(Hz, '0');
}
else if (KHz > 0)
{
print3digit(KHz, ' ');
Serial.print(',');
print3digit(Hz, '0');
Serial.print('.');
print3digit(mHz, '0');
}
else
{
Serial.print(" ");
print3digit(Hz, ' ');
Serial.print('.');
print3digit(mHz, '0');
}
Serial.print(" Hz");
Serial.print(" ");
Serial.print("Measured Period:");
if (mS > 100)
{
Serial.print(" ");
print3digit(mS, ' ');
Serial.print('.');
print3digit(uS, '0');
Serial.print(" mS");
}
else if (mS > 0)
{
print3digit(mS, ' ');
Serial.print(',');
print3digit(uS, '0');
Serial.print('.');
print3digit(nS, '0');
Serial.print(" uS");
}
else if (uS > 0)
{
Serial.print(" ");
print3digit(uS, ' ');
Serial.print('.');
print3digit(nS, '0');
Serial.print(" uS");
}
else
{
Serial.print(" ");
print3digit(nS, ' ');
Serial.print('.');
print3digit(pS, '0');
Serial.print(" nS");
}
pwm_frequency++;
}
// display a three digit number with padding if necessary
void print3digit(int n, char leadingchar)
{
if (n < 100)
{
Serial.print(leadingchar);
}
if (n < 10)
{
Serial.print(leadingchar);
}
Serial.print(n);
}
float calc_freq(uint32_t des_freq) {
des_freq *= 255; // period = 255 as in pwm01 library
uint32_t divisors[11] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
uint8_t divisor = 0;
uint32_t prescaler;
prescaler = (mck / divisors[divisor]) / des_freq;
while ((prescaler > 255) && (divisor < 11)) {
divisor++;
prescaler = (mck / divisors[divisor]) / des_freq;
}
if (divisor < 11) {
return mck / pow(2, divisor) / prescaler / 255;
}
else {
return 0;
}
}
EDIT: minor fixes to first sketch.
pwm01.zip (6.99 KB)