Microcontroller Unit Lab 5
BiPolar Stepper Motor Control
Using a Step-Direction Board with
CPU step signal & Pulse Width Modulation signal
Summary
Use a microcontoller to provide a step & direction control siginal to a bipolar stepper motor driver board.
Setup
a. Setup Dual Voltage (12v / 5v) Power Circuit using a 5v Regulator.
b. Solder Stepper Motor Wires onto 4 pin Male Header.
c. Wire Microcontroller, Stepper Driver and Stepper Motor.
Pt. 1 Repeated CPU-timed sequences
a. Create a repeated control sequence that verifies repeatable movement.
b. Determine a practical upper limit of pulse rate (i.e. rotational speed).
c. Determine the effects of microstep settings on accuracy and maximum speed.
Pt. 2 Add three buttons - speed up, speed down and direction change.
Pt. 3 Replace the CPU timing with Pulse Width Modulation control where the PWM freqency controls the motor speed.
Required Equipment and Supplies
- NEMA 17 Stepper Motor - datasheet
- A4988 Bipolar Stepper Driver Board - pinout diagram IC Datasheet
- LM7805 or LM323 5V Regulator LM7805 datasheet LM323 datasheet
- Rasperry Pi Pico with Headers - pinout diagram
- Momentary Switch (Push Button) [x3]
- 15kΩ Resistor [x3]
- USB Micro Cable
- Breadboard
- Cables and 22ga wire as needed
Example Code For Pt. 1
/// A CPU timed sequence of repeating movements
#include "pico/stdlib.h"
const uint STEP = 17;
const uint DIR = 16;
uint PERIOD = 1500;
int main() {
// setup and initialize
gpio_init(STEP);
gpio_init(DIR);
gpio_set_dir(STEP,GPIO_OUT);
gpio_set_dir(DIR,GPIO_OUT);
// primary loop
while (true) {
gpio_put(DIR, 0);
//1/4 turn
for(int i = 0; i < 200; i++){
//single pulse @ 200 Hz
// 200Hz = 5ms = 5000 us
gpio_put(STEP, 1);
sleep_us(100);
gpio_put(STEP, 0);
sleep_us(PERIOD-100);
}
sleep_ms(1000);
gpio_put(DIR, 1);
//1/4 turn
for(int i = 0; i < 200; i++){
gpio_put(STEP, 1);
sleep_us(100);
gpio_put(STEP, 0);
sleep_us(PERIOD-100);
}
sleep_ms(1000);
}
}
Example Code For Pt. 2
/// A CPU timed demonstration using three buttons
#include "pico/stdlib.h"
const uint STEP = 17;
const uint DIR = 16;
const uint BTN_SPEED_UP = 20;
const uint BTN_SPEED_DN = 19;
const uint BTN_MODE = 18;
const uint STEP_PERIOD_MAX = 10000;
const uint STEP_PERIOD_MIN = 1500;
const uint STEP_PERIOD_INC = 500;
uint period;
int mode = 0; // 0 stop, 1 forward, 2 stop, 3 reverse
const uint DEBOUNCE_DELAY = 150;
void check_buttons();
void step(uint per, int n_steps);
int main() {
// setup and initialize
gpio_init(STEP);
gpio_set_dir(STEP,GPIO_OUT);
gpio_init(DIR);
gpio_set_dir(DIR,GPIO_OUT);
gpio_init(BTN_MODE);
gpio_set_dir(BTN_MODE, GPIO_IN);
gpio_init(BTN_SPEED_UP);
gpio_set_dir(BTN_SPEED_UP, GPIO_IN);
gpio_init(BTN_SPEED_DN);
gpio_set_dir(BTN_SPEED_DN, GPIO_IN);
period = (STEP_PERIOD_MAX - STEP_PERIOD_MIN)/2;
// primary loop
while (true) {
check_buttons();
step(period, 20);
}
}
void check_buttons(){
if (gpio_get(BTN_MODE) == 0) {
mode++;
if(mode > 3) mode = 0;
if(mode == 1) gpio_put(DIR, 1); // mode 1 is forward
if(mode == 3) gpio_put(DIR, 0); // mode 3 is reverse
sleep_ms(DEBOUNCE_DELAY); //debounce delay
return;
}
if (gpio_get(BTN_SPEED_UP) == 0) {
period -= STEP_PERIOD_INC;
if(period < STEP_PERIOD_MIN) period = STEP_PERIOD_MIN;
sleep_ms(DEBOUNCE_DELAY); //debounce delay
return;
}
if(gpio_get(BTN_SPEED_DN) == 0) {
period += STEP_PERIOD_INC;
if(period > STEP_PERIOD_MAX) period = STEP_PERIOD_MAX;
sleep_ms(DEBOUNCE_DELAY); //debounce delay
return;
}
}
void step(uint per, int n_steps){
if(n_steps < 0) return;
if(mode == 1 || mode == 3) {
//take n steps
for(int i = 0; i < n_steps; i++){
gpio_put(STEP, 1);
sleep_us(100);
gpio_put(STEP, 0);
sleep_us(per-100);
}
}
}
Example Code For Pt. 3
/// A PWM timed demonstration using three buttons
#include "pico/stdlib.h"
#include "hardware/pwm.h"
const uint STEP = 17;
const uint DIR = 16;
const uint BTN_SPEED_UP = 20;
const uint BTN_SPEED_DN = 19;
const uint BTN_MODE = 18;
const uint WRAP_MAX = 60000u;
const uint WRAP_MIN = 20000u;
const uint WRAP_INC = 2500u;
uint16_t wrap = 35000u;
int mode = 0; // 0 stop, 1 forward, 2 stop, 3 reverse
const uint DEBOUNCE_DELAY = 150;
void pin_setup();
void check_buttons();
void pwm_step();
int main() {
// setup and initialize
pin_setup();
// primary loop
while (true) {
check_buttons();
pwm_step();
}
}
void pin_setup() {
//pwm on this pin. Start in off position
gpio_set_function(STEP, GPIO_FUNC_PWM);
pwm_set_enabled(pwm_gpio_to_slice_num(STEP), true);
pwm_set_gpio_level(STEP,0);
gpio_init(DIR);
gpio_set_dir(DIR,GPIO_OUT);
gpio_init(BTN_MODE);
gpio_set_dir(BTN_MODE, GPIO_IN);
gpio_init(BTN_SPEED_UP);
gpio_set_dir(BTN_SPEED_UP, GPIO_IN);
gpio_init(BTN_SPEED_DN);
gpio_set_dir(BTN_SPEED_DN, GPIO_IN);
}
void check_buttons(){
if (gpio_get(BTN_MODE) == 0) {
mode++;
if(mode > 3) mode = 0;
if(mode == 1) gpio_put(DIR, 1); // mode 1 is forward
if(mode == 3) gpio_put(DIR, 0); // mode 3 is reverse
sleep_ms(DEBOUNCE_DELAY); //debounce delay
return;
}
if (gpio_get(BTN_SPEED_UP) == 0) {
wrap -= WRAP_INC;
if(wrap < WRAP_MIN) wrap = WRAP_MIN;
sleep_ms(DEBOUNCE_DELAY); //debounce delay
return;
}
if(gpio_get(BTN_SPEED_DN) == 0) {
wrap += WRAP_INC;
if(wrap > WRAP_MAX) wrap = WRAP_MAX;
sleep_ms(DEBOUNCE_DELAY); //debounce delay
return;
}
}
void pwm_step() {
uint8_t int_divider = 10;
uint8_t frac_divider = 0; //divider fraction not used
pwm_set_wrap(pwm_gpio_to_slice_num(STEP), wrap);
pwm_set_clkdiv_int_frac(pwm_gpio_to_slice_num(STEP), int_divider, frac_divider);
if(mode == 1 || mode == 3){
pwm_set_gpio_level(STEP,1000);
} else {
pwm_set_gpio_level(STEP,0);
}
}