From 61bf787234eb5b5bfee6eccdc6f000656326d4f0 Mon Sep 17 00:00:00 2001 From: Sam Calisch <s.calisch@gmail.com> Date: Wed, 1 Nov 2017 09:08:30 -0400 Subject: [PATCH] added firmware --- nrf52-stepper/index.html | 134 ++++++++++++++++++ nrf52-stepper/nrf52-drv8825/nrf52-drv8825.ino | 133 +++++++++++------ nrf52-stepper/nrf52-drv8825/radio.h | 114 +++++++++++++++ 3 files changed, 340 insertions(+), 41 deletions(-) create mode 100644 nrf52-stepper/nrf52-drv8825/radio.h diff --git a/nrf52-stepper/index.html b/nrf52-stepper/index.html index 7c56394..f9df329 100644 --- a/nrf52-stepper/index.html +++ b/nrf52-stepper/index.html @@ -1,8 +1,142 @@ <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 diff --git a/nrf52-stepper/nrf52-drv8825/nrf52-drv8825.ino b/nrf52-stepper/nrf52-drv8825/nrf52-drv8825.ino index 4183de5..1e79dd0 100644 --- a/nrf52-stepper/nrf52-drv8825/nrf52-drv8825.ino +++ b/nrf52-stepper/nrf52-drv8825/nrf52-drv8825.ino @@ -1,5 +1,6 @@ - +#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. +} + +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() { + + //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 - NRF_GPIO->OUTSET = (1<<pin_direction); //set nrst/nslp high to enable drv8825 while (true) { - //Serial.println("hi there"); - NRF_GPIO->OUTSET = (1<<pin_step); - delay(10); - NRF_GPIO->OUTCLR = (1<<pin_step); - delay(1); + 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 -void loop() {} + //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 diff --git a/nrf52-stepper/nrf52-drv8825/radio.h b/nrf52-stepper/nrf52-drv8825/radio.h new file mode 100644 index 0000000..b09208b --- /dev/null +++ b/nrf52-stepper/nrf52-drv8825/radio.h @@ -0,0 +1,114 @@ +#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 + } +} +*/ + + -- GitLab