Motor controller acceleration profiles with FTM PWM?

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Motor controller acceleration profiles with FTM PWM?

1,706 Views
andy1
Contributor II

I am using a Kinetis MK22FN512 and controlling some stepper motor drivers that take pulse inputs. I'm currently using the FTMs to generate 50% duty cycle pulse trains. It works perfectly.

Now I'm trying to implement an acceleration profile. This requires modulation of the PWM period while sending the pulse series (duty cycle fixed at 50%). The frequency of the signal will increase in a linear fashion, plateau and then decrease.

At first glance, it looks like this may be possible to do using the "Combine Mode" of the FTM. I was also thinking that DMA could be employed so that the processor doesn't need to be constantly servicing a routine.

Has anyone implemented something like this? Is it even possible? Any suggestions?

Thanks!

Labels (1)
Tags (2)
3 Replies

1,028 Views
mjbcswitzerland
Specialist V

Hi Andrew

The uTasker project has a Stepper Motor interface which allows DMA based FlexTimer operation to generate ramps and such. For optimal use of stepper motor torque exponential ramps can be programmed.

The interface is:

Prepare ramp tables (frequencies and the number of cycles each frequency is present for in the ramp):

static const unsigned short rampFrequencyValue[] = {
    PWM_FREQUENCY(4800, 16),                                             // 4800Hz
    PWM_FREQUENCY(4200, 16),                                             // 4200Hz
    PWM_FREQUENCY(3200, 16),                                             // 3200Hz
    PWM_FREQUENCY(1000, 16),                                             // 1000Hz
    PWM_FREQUENCY(100, 16),                                              // 100Hz
    PWM_FREQUENCY(800, 16),                                              // 800Hz
    PWM_FREQUENCY(1800, 16),                                             // 1800Hz
    PWM_FREQUENCY(20000, 16),                                            // 20kHz
};

static const unsigned short rampCountValue[] = {                         // accumulated
    10,                                                                  // 10 pulses at first frequency
    18,                                                                  // 8 pulses at next frequency
    22,
    24,
    25,
    27,
    33,
    53,
    0,                                                                   // end (dummy due to buffer delay)
};


Configure two FlexTimers - one generating a frequency and the other using this as clock input (eg. CLKIN0) to count the cycles (with no output).

    PWM_INTERRUPT_SETUP pwm_setup;
    pwm_setup.int_type = PWM_INTERRUPT;
    pwm_setup.pwm_mode = (PWM_EXTERNAL_CLK | PWM_PRESCALER_0 | PWM_NO_OUTPUT | PWM_FULL_BUFFER_DMA | PWM_DMA_CHANNEL_ENABLE | PWM_DMA_CONTROL_PWM); // clock PWM timer from external clock (use channel DMA)
    pwm_setup.int_handler = 0;                                           // no user interrupt call-back on PWM cycle
    pwm_setup.pwm_reference = (_TIMER_1 | 0);                            // timer module 1, channel 0
    pwm_setup.pwm_frequency = 0xffff;                                    // cause maximum timer count to be used
    pwm_setup.pwm_value = rampCountValue[0];                             // first match value
    pwm_setup.ucDmaChannel = 2;                                          // use DMA channel 2
    pwm_setup.dma_int_priority = 0;
    pwm_setup.usDmaTriggerSource = DMAMUX0_CHCFG_SOURCE_FTM1_C0;         // load next value on own timer match
    pwm_setup.ptrPWM_Buffer = (unsigned short *)&rampCountValue[1];      // buffer controlling the DMA trigger points
    pwm_setup.ulPWM_buffer_length = (sizeof(rampCountValue) - sizeof(unsigned short));
    pwm_setup.dma_int_handler = fnEndOfRamp;                             // call back on DMA termination
    fnConfigureInterrupt((void *)&pwm_setup);                            // enter configuration for DMA trigger generation

    pwm_setup.pwm_mode = (PWM_SYS_CLK | PWM_PRESCALER_16 | PWM_FULL_BUFFER_DMA | PWM_DMA_CHANNEL_ENABLE | PWM_DMA_CONTROL_FREQUENCY);
    pwm_setup.pwm_reference = (_TIMER_0 | 2);                            // timer module 0, channel 2
    pwm_setup.ucDmaChannel = 1;                                          // use DMA channel 1
    pwm_setup.pwm_frequency = rampFrequencyValue[0];                     // set start frequency
    pwm_setup.pwm_value = _PWM_PERCENT(50, PWM_FREQUENCY(20000, 16));    // 50% PWM (high/low) when the frequency is 20kHz
    pwm_setup.ptrPWM_Buffer = (unsigned short *)&rampFrequencyValue[1];  // buffer controlling the frequencies
    pwm_setup.ulPWM_buffer_length = (sizeof(rampFrequencyValue) - sizeof(unsigned short));
    pwm_setup.dma_int_handler = 0;                                       // no callback on DMA buffer transfer termination
    fnConfigureInterrupt((void *)&pwm_setup);                            // configure and start the PWM output


FTM1, channel 0 is used as DMA trigger each time the required cycles have been detected.
FTM0, channel 2 is used to generate an output frequency (50% MSR at the maximum frequency of 20kHz)

On each DMA trigger (after each cycle count match) the next frequency is set and the next cycle count value incremented.

At the end of the ramp the interrupt call back fnEndoOfRamp() is called, which can prepare the next job or stop the operation. Eg.

static void __callback_interrupt fnEndOfRamp(void)
{
    FTM0_SC = 0;                                                         // stop the PWM output when the sequence has completed
}


The stepper motor pulse is a fixed width one (not 50% duty cycle throughout) and the maximum ramp cycle count is 64k due to the 16 bit timer.

Regards

Mark

Complete Kinetis solutions for professional needs, training and support:http://www.utasker.com/kinetis.html
Kinetis K22:
- http://www.utasker.com/kinetis/FRDM-K22F.html
- http://www.utasker.com/kinetis/TWR-K22F120M.html
- http://www.utasker.com/kinetis/BLAZE_K22.html
- http://www.utasker.com/kinetis/tinyK22.html
uTasker: supporting >1'000 registered Kinetis users get products faster and cheaper to market
Request Free emergency remote desk-top consulting at http://www.utasker.com/services.html

https://github.com/uTasker/uTasker-Kinetis

1,028 Views
andy1
Contributor II

Thanks!

This definitely gives me something to start with. Unfortunately, all 4 of my FTMs are all being used (2 motors, 2 encoders). So I wouldn't be able to employ this particular solution if I wanted to move two motors at once. But this gets me closer to a solution.

I've only been using Kinetis and ARM for a few months. So, I'm still learning the ins and out of the architecture.

Perhaps the PIT module could be used instead of a second FTM to perform the counting/DMA-triggering...Any apparent reasons why that would not be possible?

0 Kudos

1,028 Views
mjbcswitzerland
Specialist V

Andrew

The PITs have neither an input nor an output so can't be used for counting PWM output cycles or generating output signals.

If you don't have high speeds and long ramping sequences you could use a single PWM output to update its next frequency on 'every' cycle (that is, when it needs to generate 10 cycles at a certain frequency it would have the same frequency value 10x in the table and then not need an input to count its output cycles.

It would however be less inefficient, especially for generating a controlled distance movement (say 1000 steps before generating a distance-moved interrupt) because it would need a 1000 entry table with the same "speed" value at each location - although DMA based operations are very efficient it still does load the bus slightly - eg. a 1MHz DMA trigger/transfer rate can become noticeable since it begins to starve the CPU's bus access.

Regards

Mark

0 Kudos