Commit 61bf7872 authored by Sam Calisch's avatar Sam Calisch
Browse files

added firmware

parent eefb2fae
Pipeline #1097 passed with stage
in 43 seconds
<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.
}
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
#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
}
}
*/
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment