element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products & Partners
    Products & Partners
    • Arduino
    • Avnet & Tria Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • About Us
    About the element14 Community
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Japan
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      •  Vietnam
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Embedded and Microcontrollers
  • Technologies
  • More
Embedded and Microcontrollers
Blog MSPM0 project with VS Code, CMake and GCC - part 7c: stepper motor control with S-shaped ramps
  • Blog
  • Forum
  • Documents
  • Quiz
  • Polls
  • Files
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Join Embedded and Microcontrollers to participate - click to join for free!
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: Jan Cumps
  • Date Created: 8 Oct 2026 12:09 PM Date Created
  • Views 41 views
  • Likes 2 likes
  • Comments 2 comments
  • MSPM0L1105
  • MSPM0
  • easyL1105
  • VS Code
  • MSPM0L1306
  • vscode
Related
Recommended

MSPM0 project with VS Code, CMake and GCC - part 7c: stepper motor control with S-shaped ramps

Jan Cumps
Jan Cumps
8 Oct 2026

In this blog series, I build a Texas Instruments MSPM0 example with VS Code as IDE, and CMake as build infrastructure. In the next series of posts, I try to create a stepper motor project. It will be based on TI's stepper motor middleware.

In the previous post, I got constant step speed working, with an Allegro A4988 stepper motor IC. In this post, a nice s-shaped ramp-up and ramp-down curve

image

this table contains AI generated content

I asked AI:

image

[...]

image

[...]

image


Object Oriented approach

I created a class for the constant stepper (see previous post). This class can manage the motor while it's performing the requested number of steps:

  • (in this case constant) speed
  • direction
  • start
  • step until done (count and stop)

I based a new s-curve capable class based on that one. It adds this functionality:

  • accept a ramp profile ( set of speeds)
  • decide when to ramp up or down
  • handle the ramp speeds

image

additional controller resources needed

The logic needs an additional timer. A traditional one this time, not a PWM one like the one that generates the pulses. The firmware will have to provide a handler for that timer, that calls the classes check_ramp() method:

    void TIMER_RAMP_INST_IRQHandler(void) {
        if (DL_Timer_getPendingInterrupt(TIMER_RAMP_INST) == DL_TIMER_IIDX_ZERO) {
            pwm_stepper_driver1.check_ramp();
        }
    }

I've set up the timer to tick each 10 ms. The ISR then asks the class to "manage the ramp". 

image

Based on the state (ramping up / ramping down / cruise), it either ramps up, ramps down, or does nothing.

    void check_ramp() {
        switch (profile_state) {
            case motor_profile::motor_accel:
                // Move forward through the S-curve array
                if (current_lut_index < (s_curve_lut.size() - 1)) {
                    current_lut_index++;
                    set_speed(s_curve_lut[current_lut_index]); // Double the speed for acceleration to maintain smoothness
                } else {
                    profile_state = motor_profile::motor_cruise;
                }
                break;

            case motor_profile::motor_decel:
                // Walk backward through the S-curve array for smooth braking
                if (current_lut_index > 0) {
                    current_lut_index--;
                    set_speed(s_curve_lut[current_lut_index]); // Double the speed for deceleration to maintain smoothness
                }
                break;

            default:
                break;
        }
    }

A override version of the base classes check_step() (that just counts steps and stops when done) , validates if we need to ramp up, stay steady (cruise) or ramp down.
It doesn't do anything else based on that info. It just computes it, so that the ramp handling method 
check_ramp() (see above) can act upon it.

    void check_step() {
        if(control_mode_ == control_mode::step) {
            uint32_t this_step = step_count + 1;
            uint32_t steps_remaining = set_step_ - this_step;

            switch (profile_state) {
                case motor_profile::motor_accel:
                    accel_steps_taken++;
                    // Handle Short Moves: If half the steps are spent, start braking immediately
                    if (steps_remaining <= accel_steps_taken) {
                        profile_state = motor_profile::motor_decel;
                    }
                    break;

                case motor_profile::motor_cruise:
                    // Start deceleration when remaining space perfectly matches acceleration footprints
                    if (steps_remaining <= accel_steps_taken) {
                        profile_state = motor_profile::motor_decel;
                    }
                    break;

                case motor_profile::motor_decel:
                    if (steps_remaining == 0) {
                        // // TODO: Turn off PWM generation cleanly at the final step boundary
                        profile_state = motor_profile::motor_idle;
                    }
                    break;

                case motor_profile::motor_idle:
                    // No action needed; motor is idle
                    break;
                default:
                    break;
            }
            mspm0_pwm_stepper_driver::check_step(); // Call base class check_step() to handle step counting
        }
    }

 

 I used embedded friendly (read: very little overhead) C++ constructs.

  • There is no code or data related to fact that I use a base class, a derived class, or inheritance. Nothing is virtual, so it has the same CPU impact as C function style API.
  • I use an STL array to contain the s-curve and a span to pass that curve to the logic. Both have very little overhead.

You don't have permission to edit metadata of this video.
Edit media
x
image
Upload Preview
image

Guarantee that the correct number of steps are taken

The ramp up and down steps have to be part of the total number of steps. When you ask for 5000 steps, that's what the motor should exactly do. So the logic has to deal with situations where the number of steps are smaller than the amount of steps both ramps require.

For that scenario, the logic always checks is we are halfway the requested number of steps. If yes, then directly over to the related point of the down ramp. In this post, I use an identical ramp for start and stop. Just inversed. The related point in the down-ramp will be at similar speed as the up-ramp, so that 'll be acceptable. Nothing stops yopu to write a class that uses different profiles for the start and end, and to write custom logic for smooth transition between up-and down-ramp when needed. 

Bring it all together

main code is still simple. Very comparable to the previous blog.

The main changes:

  • a ramp profile is defined, in an array of steps per second
  • instead of calling set_speed(), the method set_ramp() is called.

using pwm_stepper_driver_t = mspm0_pwm_stepper_driver::mspm0_pwm_stepper_ramp_driver;
pwm_stepper_driver_t pwm_stepper_driver1(dir, GPIO_GRP_DRIVER_PORT, max_step_freq);

const unsigned int RAMP_STEPS = 100;
// S-curve pre-calculated profile (Speed values in Pulses Per Second / Hz)
// TODO: translate to raw Hardware Timer Ticks (Highly Recommended for Performance)

inline static const std::array<uint32_t, RAMP_STEPS> s_curve_lut = {
    150,  150,  151,  154,  160,  169,  181,  197,  217,  242,
    272,  308,  349,  397,  451,  512,  581,  656,  739,  830,
    928,  1033, 1146, 1266, 1393, 1526, 1666, 1812, 1963, 2119,
    2279, 2443, 2610, 2779, 2949, 3120, 3290, 3459, 3625, 3788,
    3946, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000,
    4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000,
    4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000,
    4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000,
    4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000,
    4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000
};   

int main() {

    // ...
    
    pwm_stepper_driver1.init();
    pwm_stepper_driver1.stop_motor();
    // pwm_stepper_driver1.set_speed(1); // CURVE determines speed for ramp style motion, so set_speed() is not used in this example
    pwm_stepper_driver1.set_control_mode(mspm0_pwm_stepper_driver::control_mode::step);
    pwm_stepper_driver1.set_ramp(s_curve_lut); // the vector has to stay alive for the duration of the motor run, so it is stored in a static variable in this example 
    
    /* Set the priority for interrupts */
    NVIC_SetPriority(GENERIC_PWM_CHANNEL_0_INST_INT_IRQN, 1);
    NVIC_SetPriority(TIMER_RAMP_INST_INT_IRQN, 2);
    
    while (1) {
        driver1.enable(true);
        
        pwm_stepper_driver1.set_dir(true);
        pwm_stepper_driver1.set_step(5000);
        pwm_stepper_driver1.start_motor();
        
        while (pwm_stepper_driver1.is_motor_running()) {
            __WFI(); // Enter low-power sleep mode; wake on timer interrupts
        }
        
        pwm_stepper_driver1.set_dir(false);
        pwm_stepper_driver1.set_step(5000);
        pwm_stepper_driver1.start_motor();       
        
        while (pwm_stepper_driver1.is_motor_running()) {
            __WFI(); // Enter low-power sleep mode; wake on timer interrupts
        }

        driver1.enable(false);
    }
}

 

The image below shows a scope trace of a 5000 steps command, with ramps. The more intense the yellow, the more steps per second.

image

Thank you for reading.

VS Code project: mspm0_stepper_ramp_20261008.zip
(check this post for the expected VS Code workspace settings: MSPM0 project with VS Code, CMake and GCC - part 6: empty "template" project 
)

previous post:   MSPM0 project with VS Code, CMake and GCC - part 7b: middleware stepper motor control with A4988 stepper driver 
part of the EasyL1105 series. 
all posts

  • Sign in to reply
Parents
  • kk99
    kk99 4 hours ago

    Good series of the blogs.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
Comment
  • kk99
    kk99 4 hours ago

    Good series of the blogs.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
Children
No Data
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2026 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube