Skip to content
Snippets Groups Projects
Commit 61bf7872 authored by Sam Calisch's avatar Sam Calisch
Browse files

added firmware

parent eefb2fae
Branches
No related tags found
No related merge requests found
Pipeline #
<html>
<head>
<style>
pre code {
background-color: #eee;
border: 1px solid #999;
display: block;
padding: 20px;
}
</style>
</head>
<body>
<h1>nRF52 + DRV8825 Stepper Driver</h1>
<a href='nrf52-stepper-layout.png'><img src='nrf52-stepper-layout.png' height=300px></a>
<a href='nrf52-stepper-traces.png'><img src='nrf52-stepper-traces.png' height=300px></a>
<a href='nrf52-stepper-interior.png'><img src='nrf52-stepper-interior.png' height=300px></a>
<p>This is a stepper motor controller built around the DRV8825 and the nRF52.</p>
<p>Some sample firmware is available below (or as files: <a href='nrf52-drv8825/nrf52-drv8825.ino'>nrf52-drv8825.ino</a> and <a href='nrf52-drv8825/radio.h'>radio.h</a>). 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.</p>
<pre>
<code>
#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() {}
</code>
</pre>
</body>
</html>
\ No newline at end of file
#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
......@@ -8,77 +9,127 @@ 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 setup() {
Serial.begin(115200);
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);
//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) {}
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
//NRF_TIMER1->MODE = (TIMER_MODE_MODE_Timer << TIMER_MODE_MODE_Pos) & TIMER_MODE_MODE_Msk;
//NRF_TIMER1->BITMODE = (TIMER_BITMODE_BITMODE_16Bit << TIMER_BITMODE_BITMODE_Pos) & TIMER_BITMODE_BITMODE_Msk;
//NRF_TIMER1->PRESCALER = (7 << TIMER_PRESCALER_PRESCALER_Pos) & TIMER_PRESCALER_PRESCALER_Msk;
//NRF_TIMER1->SHORTS = ((TIMER_SHORTS_COMPARE0_CLEAR_Enabled << TIMER_SHORTS_COMPARE0_CLEAR_Pos) & TIMER_SHORTS_COMPARE0_CLEAR_Msk);
//NRF_TIMER1->CC[0] = (1 << 15); //50% duty cycle to test
//enable PPI channel 0 for compare task setting pin high
//NRF_PPI->CHEN = (PPI_CHEN_CH0_Enabled << PPI_CHEN_CH0_Pos) & PPI_CHEN_CH0_Msk;
//NRF_PPI->CH[0].EEP = (uint32_t)&NRF_TIMER1->EVENTS_COMPARE[0];
//NRF_PPI->CH[0].TEP = (uint32_t)&NRF_GPIOTE->TASKS_OUT[0];
//enable PPI channel 1 for compare overflow setting pin low
//NRF_PPI->CHEN = (PPI_CHEN_CH1_Enabled << PPI_CHEN_CH1_Pos) & PPI_CHEN_CH1_Msk;
//NRF_PPI->CH[0].EEP = (uint32_t)&NRF_TIMER1->EVENTS_;
//NRF_PPI->CH[0].TEP = (uint32_t)&NRF_GPIOTE->TASKS_OUT[1];
//NRF_TIMER1->TASKS_START = 1; //start timer
//Use PWM module
//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); //1 kHz pwm freq.
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] = 1500; //50% duty cycle to test
delay(1);
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.
}
NRF_GPIO->OUTSET = (1<<pin_nrst); //set nrst/nslp high to enable drv8825
NRF_GPIO->OUTSET = (1<<pin_direction); //set nrst/nslp high to enable drv8825
while (true) {
//Serial.println("hi there");
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);
delay(10);
delayMicroseconds(step_period);
NRF_GPIO->OUTCLR = (1<<pin_step);
delay(1);
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() {
//Serial.begin(115200);
//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(); //todo: move stepping to non-blocking so can receive commands during moves
}
}
void loop() {}
// draft for using timer for pwm
//NRF_TIMER1->MODE = (TIMER_MODE_MODE_Timer << TIMER_MODE_MODE_Pos) & TIMER_MODE_MODE_Msk;
//NRF_TIMER1->BITMODE = (TIMER_BITMODE_BITMODE_16Bit << TIMER_BITMODE_BITMODE_Pos) & TIMER_BITMODE_BITMODE_Msk;
//NRF_TIMER1->PRESCALER = (7 << TIMER_PRESCALER_PRESCALER_Pos) & TIMER_PRESCALER_PRESCALER_Msk;
//NRF_TIMER1->SHORTS = ((TIMER_SHORTS_COMPARE0_CLEAR_Enabled << TIMER_SHORTS_COMPARE0_CLEAR_Pos) & TIMER_SHORTS_COMPARE0_CLEAR_Msk);
//NRF_TIMER1->CC[0] = (1 << 15); //50% duty cycle to test
//enable PPI channel 0 for compare task setting pin high
//NRF_PPI->CHEN = (PPI_CHEN_CH0_Enabled << PPI_CHEN_CH0_Pos) & PPI_CHEN_CH0_Msk;
//NRF_PPI->CH[0].EEP = (uint32_t)&NRF_TIMER1->EVENTS_COMPARE[0];
//NRF_PPI->CH[0].TEP = (uint32_t)&NRF_GPIOTE->TASKS_OUT[0];
//enable PPI channel 1 for compare overflow setting pin low
//NRF_PPI->CHEN = (PPI_CHEN_CH1_Enabled << PPI_CHEN_CH1_Pos) & PPI_CHEN_CH1_Msk;
//NRF_PPI->CH[0].EEP = (uint32_t)&NRF_TIMER1->EVENTS_;
//NRF_PPI->CH[0].TEP = (uint32_t)&NRF_GPIOTE->TASKS_OUT[1];
//NRF_TIMER1->TASKS_START = 1; //start timer
#define PACKET_BASE_ADDRESS_LENGTH (2UL) //Packet base address length field size in bytes
#define PACKET_LENGTH 4
#define REDUNDANCY_COUNT 10 //number of transmissions to ensure delivery... hack.
static int16_t radio_buffer[PACKET_LENGTH] = {0};
//static int16_t reference_buffer[PACKET_LENGTH] = {0}; //for checking against receipt
//
//RADIO
//
void radio_setup(){
NRF_RADIO->POWER = RADIO_POWER_POWER_Disabled; //turn off radio to reset registers
delay(10);
NRF_RADIO->POWER = RADIO_POWER_POWER_Enabled; //turn on radio
delay(10);
NRF_RADIO->TXPOWER = (RADIO_TXPOWER_TXPOWER_Pos3dBm << RADIO_TXPOWER_TXPOWER_Pos);
NRF_RADIO->FREQUENCY = 11UL; // 2400 + X MHz
NRF_RADIO->MODE = (RADIO_MODE_MODE_Nrf_2Mbit << RADIO_MODE_MODE_Pos);
NRF_RADIO->PREFIX0 = ((uint32_t)0xC0 << 0); // Prefix byte of address 0
NRF_RADIO->BASE0 = 0x01234567UL; // Base address for prefix 0
NRF_RADIO->BASE1 = 0x02345678UL; // Base address for prefix 0
NRF_RADIO->TXADDRESS = 0x00UL; // Set device address 0 to use when transmitting
//NRF_RADIO->RXADDRESSES = 0x01UL; // X Enable device address 0 to use to select which addresses to receive
NRF_RADIO->RXADDRESSES = 0x02UL; //Y Enable device address 1 to use to select which addresses to receive
// Packet configuration
NRF_RADIO->PCNF0 = (0 << RADIO_PCNF0_S1LEN_Pos) | (0 << RADIO_PCNF0_S0LEN_Pos) | (0 << RADIO_PCNF0_LFLEN_Pos);
NRF_RADIO->PCNF1 = (RADIO_PCNF1_WHITEEN_Enabled << RADIO_PCNF1_WHITEEN_Pos) |
((PACKET_LENGTH) << RADIO_PCNF1_STATLEN_Pos) |
(RADIO_PCNF1_ENDIAN_Big << RADIO_PCNF1_ENDIAN_Pos) |
(2 << RADIO_PCNF1_BALEN_Pos);
NRF_RADIO->CRCCNF = (RADIO_CRCCNF_LEN_Three << RADIO_CRCCNF_LEN_Pos); // Number of checksum bits
NRF_RADIO->CRCINIT = 0xFFFFUL; // Initial value
NRF_RADIO->CRCPOLY = 0x18D; //x8 + x7 + x3 + x2 + 1 = 110001101
NRF_RADIO->MODECNF0 |= RADIO_MODECNF0_RU_Fast << RADIO_MODECNF0_RU_Pos; //turn on fast ramp up
NRF_RADIO->SHORTS = 0; //turn off all shortcuts, for debug
NRF_RADIO->PACKETPTR = (uint32_t)&radio_buffer; //set pointer to packet buffer
//start HFCLK
NRF_CLOCK->TASKS_HFCLKSTART = 1;
while(!(NRF_CLOCK->HFCLKSTAT & CLOCK_HFCLKSTAT_STATE_Msk)); //wait for hfclk to start
delay(10);
}
void radio_wait_for_end(){ while(!(NRF_RADIO->EVENTS_END)); NRF_RADIO->EVENTS_END = 0;} //clear end event
void radio_wait_for_ready(){ while(!(NRF_RADIO->EVENTS_READY)); NRF_RADIO->EVENTS_READY = 0;} //clear ready event
void radio_disable(){
NRF_RADIO->EVENTS_DISABLED = 0; //clear disabled event
NRF_RADIO->TASKS_DISABLE = 1;
while(!(NRF_RADIO->EVENTS_DISABLED));
}
void radio_send(){
NRF_RADIO->EVENTS_READY = 0; //clear ready event
NRF_RADIO->TASKS_TXEN=1; //trigger tx enable task
delayMicroseconds(20);
//radio_wait_for_ready(); //only generated when actually switching to tx mode
NRF_RADIO->TASKS_START=1; //start
radio_wait_for_end();
}
void radio_send_redundant(){
for(int i=0; i<REDUNDANCY_COUNT; i++){
radio_send();
}
}
int radio_recv(){
//return number of packets before CRC match
NRF_RADIO->EVENTS_CRCOK = 0;
//NRF_RADIO->EVENTS_CRCERROR = 0;
int i=1;
while(true){
NRF_RADIO->EVENTS_READY = 0; //clear ready event
NRF_RADIO->TASKS_RXEN=1; //trigger rx enable task
delayMicroseconds(20);
//radio_wait_for_ready(); //only generated when actually switching to rx mode
NRF_RADIO->TASKS_START=1;
radio_wait_for_end();
if (NRF_RADIO->EVENTS_CRCOK == 1){ break;}
i++;
}
return i;
}
/*
// start of 3 way handshake implementation
void copy_buffer_to_reference(){
for(int i=0; i++; i<PACKET_LENGTH){
reference_buffer[i] = radio_buffer[i];
}
}
bool buffer_matches_reference(){
bool match = true;
for(int i=0; i++; i<PACKET_LENGTH){
if (reference_buffer[i] != radio_buffer[i]){
match = false;
break;
}
}
return match;
}
void radio_send_with_handshake(){
copy_buffer_to_reference();
radio_send(); //send packet
int crc_match = radio_recv(); //receive ack
if crc_match && buffer_matches_reference(){
radio_send(); //send ack
}
}
*/
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment