This is a stepper motor controller built around the DRV8825 and the nRF52. The nRF52 receives commands over the radio and controls the motor, which means we only need to deliver power to the motor, no data lines.
This board uses a PWM output and a low pass filter to deliver a voltage level to control the current limiting value for the DRV8825. A 10K resistor and a 1uF capacitor do a reasonably good job smoothing the 10kHz PWM into a constant voltage level.
Some sample firmware is available below (or as files: nrf52-drv8825.ino and radio.h). This code defines a command set issued over the radio for moving a number of steps, changing stepping speed, setting microstepping parameters, and setting a current limit value.
For help getting started programming the nRF52, see this page.
#include "radio.h"
uint16_t pwms[1] = {0};
uint16_t step_period = 20000; //microseconds
const uint8_t pin_mode1 = 3; //A1
const uint8_t pin_mode0 = 0; //XL1
const uint8_t pin_step = 1; //XL2
const uint8_t pin_direction = 2; //A0
const uint8_t pin_nrst = 27; //p27
const uint8_t pin_ref = 26; //p26
int i=0; //counter
void stepper_setup(){
NRF_GPIO->DIRSET = (1 << pin_nrst); //set nrst/nslp pins as output
NRF_GPIO->OUTCLR = (1 << pin_nrst); //set nrst/nslp low to disable drv8825
NRF_GPIO->OUT = (0 << pin_nrst);
NRF_GPIO->DIRSET = (1 << pin_mode1) | (1 << pin_mode0); //set mode pins as output
NRF_GPIO->OUTCLR = (1 << pin_mode1) | (1 << pin_mode0); //set to full step mode
NRF_GPIO->DIRSET = (1 << pin_step) | (1 << pin_direction); //set step/dir pins as output
//Use PWM module to generate aref/bref
NRF_GPIO->DIRSET = (1 << pin_ref); //set ref pin as output
NRF_GPIO->OUTCLR = (1 << pin_ref); //set ref pin low
NRF_PWM0->PSEL.OUT[0] = (pin_ref << PWM_PSEL_OUT_PIN_Pos) | (PWM_PSEL_OUT_CONNECT_Connected << PWM_PSEL_OUT_CONNECT_Pos); //set aref pin to pwm out[0]
NRF_PWM0->ENABLE = (PWM_ENABLE_ENABLE_Enabled << PWM_ENABLE_ENABLE_Pos);
NRF_PWM0->MODE = (PWM_MODE_UPDOWN_Up << PWM_MODE_UPDOWN_Pos);
NRF_PWM0->PRESCALER = (PWM_PRESCALER_PRESCALER_DIV_1 << PWM_PRESCALER_PRESCALER_Pos); //16MHz tick
NRF_PWM0->COUNTERTOP = (1600 << PWM_COUNTERTOP_COUNTERTOP_Pos); //10 kHz pwm freq.
NRF_PWM0->LOOP = (PWM_LOOP_CNT_Disabled << PWM_LOOP_CNT_Pos);
NRF_PWM0->DECODER = (PWM_DECODER_LOAD_Common << PWM_DECODER_LOAD_Pos) | (PWM_DECODER_MODE_RefreshCount << PWM_DECODER_MODE_Pos);
pwms[0] = 1600-100; //100/1600 * 3.3v = .2V = 200 mA limit
NRF_PWM0->SEQ[0].PTR = ((uint32_t)(pwms) << PWM_SEQ_PTR_PTR_Pos);
NRF_PWM0->SEQ[0].CNT = (1 << PWM_SEQ_CNT_CNT_Pos);
NRF_PWM0->SEQ[0].REFRESH = 0;
NRF_PWM0->SEQ[0].ENDDELAY = 0;
NRF_PWM0->TASKS_SEQSTART[0] = 1;
delay(1); //give aref filter time to settle.
}
void parse_command(){
//interpret command from radio for stepper actions
if( radio_buffer[0] == 1 ){
//move by commanded number of steps
if (radio_buffer[1] > 0){
NRF_GPIO->OUTSET = (1 << pin_direction); //set direction forwards
} else {
NRF_GPIO->OUTCLR = (1 << pin_direction); //set direction backwards
}
radio_buffer[1] = radio_buffer[1] > 0 ? radio_buffer[1] : -radio_buffer[1];
for(i=0; i < radio_buffer[1]; i++){
NRF_GPIO->OUTSET = (1 << pin_step);
delayMicroseconds(step_period);
NRF_GPIO->OUTCLR = (1 << pin_step);
delayMicroseconds(10);
}
} else if (radio_buffer[0] == 2){
//change step speed
step_period = radio_buffer[1];
} else if (radio_buffer[0] == 3){
//change microstepping
if (radio_buffer[1] & 1){
NRF_GPIO->OUTSET = (1 << pin_mode0);
} else {
NRF_GPIO->OUTCLR = (1 << pin_mode0);
}
if (radio_buffer[1] & 2){
NRF_GPIO->OUTSET = (1 << pin_mode1);
} else {
NRF_GPIO->OUTCLR = (1 << pin_mode1);
}
} else if (radio_buffer[0] == 4){
//change current limit
pwms[0] = 1600-radio_buffer[1]; //100/1600 * 3.3v = .2V = 200 mA limit
NRF_PWM0->SEQ[0].REFRESH = 1;
}
else{
}
//unrecognized command, set radio buffer to all -1
//for(int i=0; i < PACKET_LENGTH; i++){
// radio_buffer[i] = -1;
//}
//reset radio buffer
for(i=0; i < PACKET_LENGTH; i++){
radio_buffer[i] = 0;
}
}
void setup() {
//Switch to internal LFCLK to disconnect from XL1 and XL2
NRF_CLOCK->LFCLKSRC = 0; //disconnect XL1 AND XL2 FROM LFCLK
NRF_CLOCK->EVENTS_LFCLKSTARTED = 0;
NRF_CLOCK->TASKS_LFCLKSTART = 1;
while (NRF_CLOCK->EVENTS_LFCLKSTARTED == 0) {}
stepper_setup();
radio_setup();
NRF_GPIO->OUTSET = (1 << pin_nrst); //set nrst/nslp high to enable drv8825
while (true) {
int result = radio_recv(); //wait until recieve
parse_command();
}
}
void loop() {}