This is a stepper motor controller built around the DRV8825 and the nRF52.
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.
#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() {}