From 6a3d6127d84bbe54823d9aa4e85ae0ec9e8f18b1 Mon Sep 17 00:00:00 2001 From: Bob Kuhn Date: Wed, 23 Jan 2019 18:31:48 -0600 Subject: [PATCH 01/14] Add SPI chain, individual SPI pins (#11) --- L6470/keywords.txt | 53 --- .../single_motor/single_motor.ino | 15 +- examples/two_motors/two_motors.ino | 60 +++ .../two_motors_synchronized.ino | 177 ++++++++ keywords.txt | 69 ++++ library.json | 2 +- .../library.properties => library.properties | 2 +- {L6470 => src}/L6470.cpp | 378 ++++++++++++------ {L6470 => src}/L6470.h | 93 +++-- 9 files changed, 625 insertions(+), 224 deletions(-) delete mode 100755 L6470/keywords.txt rename L6470/examples/L6470/L6470.ino => examples/single_motor/single_motor.ino (63%) create mode 100644 examples/two_motors/two_motors.ino create mode 100644 examples/two_motors_synchronized/two_motors_synchronized.ino create mode 100644 keywords.txt rename L6470/library.properties => library.properties (96%) mode change 100755 => 100644 rename {L6470 => src}/L6470.cpp (69%) mode change 100755 => 100644 rename {L6470 => src}/L6470.h (80%) mode change 100755 => 100644 diff --git a/L6470/keywords.txt b/L6470/keywords.txt deleted file mode 100755 index 0629ef5..0000000 --- a/L6470/keywords.txt +++ /dev/null @@ -1,53 +0,0 @@ -####################################################### -# keywords.txt - keywords file for the L6470 library -# -# ORIGINAL CODE 12/12/2011- Mike Hord, SparkFun Electronics -# Library by Adam Meyer of bildr Aug 18th 2012 -# -# Released as MIT license -####################################################### - -####################################### -# Datatypes (KEYWORD1) -####################################### - -L6470 KEYWORD1 - -####################################### -# Methods and Functions (KEYWORD2) -####################################### - -L6470 KEYWORD2 -init KEYWORD2 -setMicroSteps KEYWORD2 -setCurrent KEYWORD2 -setMaxSpeed KEYWORD2 -setMinSpeed KEYWORD2 -setAcc KEYWORD2 -setDec KEYWORD2 -setOverCurrent KEYWORD2 -setThresholdSpeed KEYWORD2 -setStallCurrent KEYWORD2 -ParamHandler KEYWORD2 -SetLowSpeedOpt KEYWORD2 -run KEYWORD2 -Step_Clock KEYWORD2 -goHome KEYWORD2 -goMark KEYWORD2 -move KEYWORD2 -goTo KEYWORD2 -goTo_DIR KEYWORD2 -goUntil KEYWORD2 -isBusy KEYWORD2 -releaseSW KEYWORD2 -resetPos KEYWORD2 -resetDev KEYWORD2 -softStop KEYWORD2 -hardStop KEYWORD2 -softHiZ KEYWORD2 -hardHiZ KEYWORD2 -getStatus KEYWORD2 - -####################################### -# Constants (LITERAL1) -####################################### \ No newline at end of file diff --git a/L6470/examples/L6470/L6470.ino b/examples/single_motor/single_motor.ino similarity index 63% rename from L6470/examples/L6470/L6470.ino rename to examples/single_motor/single_motor.ino index 5ab255f..3920b05 100644 --- a/L6470/examples/L6470/L6470.ino +++ b/examples/single_motor/single_motor.ino @@ -1,11 +1,16 @@ -#include #include -L6470 stepper(10); // SS Pin 10 +#define SS_PIN 10 +#define SCK_PIN 11 +#define MOSI_PIN 12 +#define MISO_PIN 13 +#define RESET_PIN 14 +#define BUSYN_PIN 15 -void setup(){ - Serial.begin(9600); +L6470 stepper(SS_PIN); // create stepper object +void setup() { + stepper.set_pins(SCK_PIN, MOSI_PIN, MISO_PIN, RESET_PIN, BUSYN_PIN); //use library's soft SPI stepper.init(); stepper.setAcc(100); // Set acceleration stepper.setMaxSpeed(800); @@ -22,7 +27,7 @@ void setup(){ stepper.goTo(200); } -void loop(){ +void loop() { while (stepper.isBusy()) delay(10); stepper.goTo(-200); while (stepper.isBusy()) delay(10); diff --git a/examples/two_motors/two_motors.ino b/examples/two_motors/two_motors.ino new file mode 100644 index 0000000..89f4185 --- /dev/null +++ b/examples/two_motors/two_motors.ino @@ -0,0 +1,60 @@ +// two motors using independent SPIs + + +#include + +#define A_SS_PIN 10 +#define A_SCK_PIN 11 +#define A_MOSI_PIN 12 +#define A_MISO_PIN 13 +#define A_RESET_PIN 14 +#define A_BUSYN_PIN 15 + +#define B_SS_PIN 20 +#define B_SCK_PIN 21 +#define B_MOSI_PIN 22 +#define B_MISO_PIN 23 +#define B_RESET_PIN 24 +#define B_BUSYN_PIN 25 + +L6470 stepperA(A_SS_PIN); +L6470 stepperB(B_SS_PIN); + +void setup() { + + stepperA.set_pins(A_SCK_PIN, A_MOSI_PIN, A_MISO_PIN, A_RESET_PIN, A_BUSYN_PIN); //use library's soft SPI + stepperA.init(); + stepperA.setAcc(100); // Set acceleration + stepperA.setMaxSpeed(800); + stepperA.setMinSpeed(1); + stepperA.setMicroSteps(2); // 1,2,4,8,16,32,64 or 128 + stepperA.setThresholdSpeed(1000); + stepperA.setOverCurrent(6000); // Set overcurrent protection + stepperA.setStallCurrent(3000); + + stepperB.set_pins(B_SCK_PIN, B_MOSI_PIN, B_MISO_PIN, B_RESET_PIN, B_BUSYN_PIN); //use library's soft SPI + stepperB.init(); + stepperB.setAcc(100); // Set acceleration + stepperB.setMaxSpeed(800); + stepperB.setMinSpeed(1); + stepperB.setMicroSteps(2); // 1,2,4,8,16,32,64 or 128 + stepperB.setThresholdSpeed(1000); + stepperB.setOverCurrent(6000); // Set overcurrent protection + stepperB.setStallCurrent(3000); + + //stepper.run(1, 200); + + //stepper.softStop(); + + stepperA.goTo(200); // stepperA starts as soon as it's SS_PIN goes high + stepperB.goTo(200); // stepperB's start is delayed by SPI transmission time +} + +void loop() { + while (stepperB.isBusy()) delay(10); + stepperA.goTo(-200); + stepperB.goTo(-200); + while (stepperB.isBusy()) delay(10); + stepperA.goTo(2000); + stepperB.goTo(2000); +} diff --git a/examples/two_motors_synchronized/two_motors_synchronized.ino b/examples/two_motors_synchronized/two_motors_synchronized.ino new file mode 100644 index 0000000..2cb106e --- /dev/null +++ b/examples/two_motors_synchronized/two_motors_synchronized.ino @@ -0,0 +1,177 @@ +/** + * This example synchronizes the actions of two motors by using + * SPI daisy chaining. + * + * The hardware setup is: + * MOSI from controller tied to SDI on the first device + * SDO of the first device is tied to SDI of the next device + * SDO of the last device is tied to MISO of the controller + * all devices share the same SCK, SS_PIN and RESET_PIN + * + * Each L6470 passes the data it saw on its SDI to its neighbor + * on the NEXT SPI cycle (8 bit delay). + * + * Each L6470 acts on the last SPI data it saw when the SS_PIN goes high. + */ + +/** + * Two different SPI routines are used. One routine is used to send + * commands to individual devices. A different one is used for motion + * commands so that all devices act at the same time. + */ + +/** + * The array "chain[]" is used to tell the software how the hardware is hooked up + * [0] - number of drivers in chain + * [1] - axis index for first device in the chain (closest to MOSI) + * [2] - axis index for next device in the chain + * + * Axis index is an arbitrary identifier assigned by the user + */ + +#include + +#define SS_PIN 10 +#define SCK_PIN 11 +#define MOSI_PIN 12 +#define MISO_PIN 13 +#define RESET_PIN 14 + +uint8_t L6470_SpiTransfer_Mode_3(uint8_t b) { // using Mode 3 + uint8_t bits = 8; + do { + digitalWrite(SCK_PIN, LOW); + digitalWrite(MOSI_PIN, b & 0x80); + //DELAY_NS(125); + digitalWrite(SCK_PIN, HIGH); + b <<= 1; // little setup time + b |= (digitalRead(MISO_PIN) != 0); + } while (--bits); + //DELAY_NS(125); + return b; +} + +/** + * This is the routine involved in all non-motion commands/transfers + * + * This routine sends/receives one uint8_t to the target device. All other + * Devices are sent the NOOP command. + * + * Note that the data for the last device in the chain is sent out first. + * + * The library will automatically link to "uint8_t L6470_Transfer(uint8_t,int16_t,uint8_t)" + */ + +uint8_t L6470_transfer(uint8_t data, int16_t ss_pin, uint8_t chain_position) { + #define CMD_NOP 0 + uint8_t data_out = 0; + data--; + // first device in chain has data sent last + digitalWrite(ss_pin, LOW); + + for (uint8_t i = L6470::chain[0]; i >= 1; i--) { + uint8_t temp = L6470_SpiTransfer_Mode_3(uint8_t(i == chain_position ? data : CMD_NOP)); + if (L6470::chain[i] == chain_position) data_out = temp; + } + + digitalWrite(ss_pin, HIGH); + return data_out; +} + +/** + * This is the routine that sends the motion commands. + * + * This routine sends a buffer of data that is filled by the application. The + * library is not involved with it. + */ + +//uint8_t buffer[number of steppers + 1]; + // [0] - not used + // [1] - command for first device + // [2] - command for second device + +void Buffer_Transfer(uint8_t buffer[] , uint8_t length) { + // first device in chain has data sent last + digitalWrite(SS_PIN, LOW); + for (uint8_t i = length; i >= 1; i--) + buffer[i] = L6470_SpiTransfer_Mode_3(uint8_t (buffer[i])); + digitalWrite(SS_PIN, HIGH); +} + +/** + * Initialize pins for non-library SPI software + * + * The library will automatically link to "void L6470::spi_init()" + */ + +void L6470::spi_init() { + pinMode(SS_PIN, OUTPUT); + pinMode(SCK_PIN, OUTPUT); + pinMode(MOSI_PIN, OUTPUT); + digitalWrite(SS_PIN, HIGH); + digitalWrite(SCK_PIN, HIGH); + digitalWrite(MOSI_PIN, HIGH); + pinMode(MISO_PIN, INPUT); +} + +void goTo(long location_1, long location_2) { + // the command string to move a stepper to an absolute position is + // four uint8_t long so four arraya are used for convenience + + uint8_t buffer_command[3] = { dSPIN_GOTO, dSPIN_GOTO }; // create and fill buffer with commands + + if (location_1 > 0x3FFFFF) location_1 = 0x3FFFFF; // limit to 22 bits + if (location_1 > 0x3FFFFF) location_1 = 0x3FFFFF; + + uint8_t addr21_16[3] = { 0, uint8_t(location_1 >> 16), uint8_t(location_2 >> 16) }; + uint8_t addr15_8[3] = { 0, uint8_t(location_1 >> 8), uint8_t(location_2 >> 8) }; + uint8_t addr7_0[3] = { 0, uint8_t(location_1) , uint8_t(location_2) }; + + Buffer_Transfer(buffer_command, L6470::chain[0]); // send the commands + Buffer_Transfer(addr21_16 , L6470::chain[0]); // send the MSB of the position + Buffer_Transfer(addr15_8 , L6470::chain[0]); + Buffer_Transfer(addr7_0 , L6470::chain[0]); // this one results in the motors moving +} + +////////////////////////////////////////////////////////////////////// + +L6470 stepperA(SS_PIN); // create first stepper object +L6470 stepperB(SS_PIN); // create second stepper object + +void setup() { + + pinMode(RESET_PIN,OUTPUT); // reset all drivers + digitalWrite(RESET_PIN, LOW); // do this before any setup commands are sent to the drivers + delay(10); + digitalWrite(RESET_PIN, HIGH); + + stepperA.set_chain_info(56, 1); // completely setup chain[] before + stepperB.set_chain_info(56, 2); // any SPI traffic is sent + + stepperA.init(); + stepperA.setAcc(100); // Set acceleration + stepperA.setMaxSpeed(800); + stepperA.setMinSpeed(1); + stepperA.setMicroSteps(2); // 1,2,4,8,16,32,64 or 128 + stepperA.setThresholdSpeed(1000); + stepperA.setOverCurrent(6000); // Set overcurrent protection + stepperA.setStallCurrent(3000); + + stepperB.init(); + stepperB.setAcc(100); // Set acceleration + stepperB.setMaxSpeed(800); + stepperB.setMinSpeed(1); + stepperB.setMicroSteps(2); // 1,2,4,8,16,32,64 or 128 + stepperB.setThresholdSpeed(1000); + stepperB.setOverCurrent(6000); // Set overcurrent protection + stepperB.setStallCurrent(3000); + + goTo(200,200); // spin the motors at the same time +} + +void loop() { + while (stepperB.isBusy()) delay(10); + goTo(-200,-200); + while (stepperB.isBusy()) delay(10); + goTo(2000,2000); +} diff --git a/keywords.txt b/keywords.txt new file mode 100644 index 0000000..47d4a7d --- /dev/null +++ b/keywords.txt @@ -0,0 +1,69 @@ +####################################################### +# keywords.txt - keywords file for the L6470 library +# +# ORIGINAL CODE 12/12/2011- Mike Hord, SparkFun Electronics +# Library by Adam Meyer of bildr Aug 18th 2012 +# +# Released as MIT license +####################################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +L6470 KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +AccCalc KEYWORD2 +DecCalc KEYWORD2 +convert KEYWORD2 +free KEYWORD2 +FSCalc KEYWORD2 +GetParam KEYWORD2 +getPos KEYWORD2 +getStatus KEYWORD2 +goHome KEYWORD2 +goMark KEYWORD2 +goTo KEYWORD2 +goTo_DIR KEYWORD2 +goUntil KEYWORD2 +hardHiZ KEYWORD2 +hardStop KEYWORD2 +init KEYWORD2 +IntSpdCalc KEYWORD2 +isBusy KEYWORD2 +L6470 KEYWORD2 +MaxSpdCalc KEYWORD2 +MinSpdCalc KEYWORD2 +move KEYWORD2 +Param KEYWORD2 +ParamHandler KEYWORD2 +releaseSW KEYWORD2 +resetDev KEYWORD2 +resetPos KEYWORD2 +run KEYWORD2 +setAcc KEYWORD2 +setAsHome KEYWORD2 +setCurrent KEYWORD2 +setDec KEYWORD2 +SetLowSpeedOpt KEYWORD2 +setMark KEYWORD2 +setMaxSpeed KEYWORD2 +setMicroSteps KEYWORD2 +setMinSpeed KEYWORD2 +setOverCurrent KEYWORD2 +setParam KEYWORD2 +setStallCurrent KEYWORD2 +setThresholdSpeed KEYWORD2 +softFree KEYWORD2 +softStop KEYWORD2 +SpdCalc KEYWORD2 +Step_Clock KEYWORD2 +Xfer KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### diff --git a/library.json b/library.json index 0d08611..7d5d6f4 100755 --- a/library.json +++ b/library.json @@ -7,7 +7,7 @@ "type": "git", "url": "https://github.com/ameyer/Arduino-L6470.git" }, - "version": "0.6.2", + "version": "0.7.0", "authors": [ { diff --git a/L6470/library.properties b/library.properties old mode 100755 new mode 100644 similarity index 96% rename from L6470/library.properties rename to library.properties index a9b7c82..b86948d --- a/L6470/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=Arduino-L6470 -version=0.6.2 +version=0.7.0 author=Adam Meyer maintainer=Adam Meyer , Scott Lahteine sentence=L6470 stepper driver library diff --git a/L6470/L6470.cpp b/src/L6470.cpp old mode 100755 new mode 100644 similarity index 69% rename from L6470/L6470.cpp rename to src/L6470.cpp index ce2a19b..5cd4978 --- a/L6470/L6470.cpp +++ b/src/L6470.cpp @@ -3,50 +3,65 @@ // ORIGINAL CODE 12 Dec 2011 Mike Hord, SparkFun Electronics // // // // LIBRARY Created by Adam Meyer (@ameyer) of bildr 18 Aug 2012 // -// Modified by Scott Lahteine (@thinkyhead) 6 Mar 2018 // -// Released as MIT license // +// Modified by Scott Lahteine (@thinkyhead) 6 Mar 2018 // +// Chain and SPI updates by Bob Kuhn (@bob-the-kuhn) 6 Jan 2019 // +// Released as MIT license // // // //////////////////////////////////////////////////////////////////// #include "L6470.h" #include -#include -L6470::L6470(const int SSPin) { - _SSPin = SSPin; +uint8_t L6470::chain[21]; + // [0] - number of drivers in chain + // [1]... axis index for first device in the chain (closest to MOSI) + +// placeholders to prevent compiler errors if using internal SPI functions + +// Define WEAK attribute +#ifdef __CC_ARM // Keil µVision 4 + #define WEAK __attribute__ ((weak)) +#elif defined(__ICCARM__) // IAR Ewarm 5.41+ + #define WEAK __weak +#elif defined(__GNUC__) // GCC CS3 2009q3-68 + #define WEAK __attribute__ ((weak)) +#else + #define WEAK +#endif + +void L6470_SPI_init() WEAK ; +void L6470_SPI_init() {} // called whenever a stepper object is created +uint8_t L6470_transfer(uint8_t data, int16_t ss_pin) WEAK ; +uint8_t L6470_transfer(uint8_t data, int16_t ss_pin) { return 0; } +uint8_t L6470_transfer(uint8_t data, int16_t ss_pin, uint8_t axis) WEAK ; +uint8_t L6470_transfer(uint8_t data, int16_t ss_pin, uint8_t axis) { return 0; } + // "data" is the data to be sent to the target device + // "ss_pin" is the slave select pin to be used for this transfer + // "axis" is the index of the axis the data is meant for. Only used in SPI daisy chain systems + +L6470::L6470(const int16_t ss_pin) { + pin_SS = ss_pin; // Serial.begin(9600); + chain[0] = 0; // init chain array } // Generic init function to set up communication with the dSPIN chip. -void L6470::init() { - - pinMode(_SSPin, OUTPUT); - digitalWrite(_SSPin, HIGH); - pinMode(MOSI, OUTPUT); - pinMode(MISO, INPUT); - pinMode(SCK, OUTPUT); - pinMode(BUSYN, INPUT); - pinMode(RESET, OUTPUT); +// call this after setting up the SPI(s) (after all "L6470::set_pins" and "set_chain_info" commands) +void L6470::init() { - // reset the dSPIN chip. This could also be accomplished by - // calling the "L6470::ResetDev()" function after SPI is initialized. - digitalWrite(RESET, HIGH); - delay(10); - digitalWrite(RESET, LOW); - delay(10); - digitalWrite(RESET, HIGH); - delay(10); + if (pin_SS >= 0) { //init pin_SS if it has been set for this chip + pinMode(pin_SS, OUTPUT); + digitalWrite(pin_SS, HIGH); + } // initialize SPI for the dSPIN chip's needs: // most significant bit first, // SPI clock not to exceed 5MHz, // SPI_MODE3 (clock idle high, latch data on rising edge of clock) - SPI.begin(); - SPI.setBitOrder(MSBFIRST); - SPI.setClockDivider(SPI_CLOCK_DIV16); // or 2, 8, 16, 32, 64 - SPI.setDataMode(SPI_MODE3); + if (pin_SCK < 0) spi_init(); // using external SPI to init it + // internal SPI already initialized // First things first: let's check communications. The L6470_CONFIG register should // power up to 0x2E88, so we can use that to check the communications. @@ -62,10 +77,6 @@ void L6470::init() { // not using that pin. //SetParam(L6470_STEP_MODE, !SYNC_EN | STEP_SEL_1 | SYNC_SEL_1); - SetParam(L6470_KVAL_RUN, 255); - SetParam(L6470_KVAL_ACC, 255); - SetParam(L6470_KVAL_DEC, 255); - // Set up the L6470_CONFIG register as follows: // PWM frequency divisor = 1 // PWM frequency multiplier = 2 (62.5kHz PWM frequency) @@ -75,14 +86,18 @@ void L6470::init() { // Hard stop on switch low // 16MHz internal oscillator, nothing on output SetParam(L6470_CONFIG, CONFIG_PWM_DIV_1 | CONFIG_PWM_MUL_2 | CONFIG_SR_290V_us| CONFIG_OC_SD_DISABLE | CONFIG_VS_COMP_DISABLE | CONFIG_SW_HARD_STOP | CONFIG_INT_16MHZ); - // Configure the dSPIN_RUN KVAL. This defines the duty cycle of the PWM of the bridges + + // Configure the dSPIN_RUN KVAL. This defines the duty cycle of the PWM of the bridges // during running. 0xFF means that they are essentially NOT PWMed during run; this // MAY result in more power being dissipated than you actually need for the task. // Setting this value too low may result in failure to turn. // There are L6470_ACC, L6470_DEC, and HOLD KVAL registers as well; you may need to play with // those values to get acceptable performance for a given application. SetParam(L6470_KVAL_RUN, 0xFF); - // Calling GetStatus() clears the UVLO bit in the status register, which is set by + SetParam(L6470_KVAL_ACC, 0xFF); + SetParam(L6470_KVAL_DEC, 0xFF); + +// Calling GetStatus() clears the UVLO bit in the status register, which is set by // default on power-up. The driver may not run without that bit cleared by this // read operation. getStatus(); @@ -90,10 +105,60 @@ void L6470::init() { hardStop(); //engage motors } +// add to the chain array and save chain info for this stepper +void L6470::set_chain_info(const uint8_t axis, const uint8_t chain_position) { + if (chain_position) { + chain[0]++; + chain[chain_position] = axis; + position = chain_position; + axis_index = axis; + } + else + chain[0] = 0; //reset array back to uninitialized +} + +// Sets optional pins for this stepper +// pin_SS is set by the instantiation call. +void L6470::set_pins(const int16_t sck, const int16_t mosi, const int16_t miso, const int16_t reset, const int16_t busyn) { + pin_SCK = sck; + pin_MOSI = mosi; + pin_MISO = miso; + pin_RESET = reset; + pin_BUSYN = busyn; + + if (pin_SCK >= 0) { // init SPI pins if they are valid + pinMode(pin_MOSI, OUTPUT); + pinMode(pin_MISO, INPUT); + pinMode(pin_SCK, OUTPUT); + digitalWrite(pin_SCK, HIGH); + } + + if (pin_BUSYN >= 0) + pinMode(pin_BUSYN, INPUT); + + // reset the dSPIN chip. This could also be accomplished by + // calling the "L6470::ResetDev()" function after SPI is initialized. + // + // Reset should be done here ONLY if each chip has a dedicated reset pin. Otherwise + // the already initialized chip(s) will be set back to power up values. + + // This need to be done BEFORE calling L6470::init() or the data written by the init + // will be ceared to power up values. + if (pin_RESET >= 0) { + pinMode(pin_RESET, OUTPUT); + digitalWrite(pin_RESET, HIGH); + delay(10); + digitalWrite(pin_RESET, LOW); + delay(10); + digitalWrite(pin_RESET, HIGH); + delay(10); + } +} + boolean L6470::isBusy() { return !(getStatus() & 0x0002); } -void L6470::setMicroSteps(int microSteps) { - byte stepVal; +void L6470::setMicroSteps(int16_t microSteps) { + uint8_t stepVal; for (stepVal = 0; stepVal < 8; stepVal++) { if (microSteps == 1) break; microSteps >>= 1; @@ -109,7 +174,7 @@ void L6470::setThresholdSpeed(const float thresholdSpeed) { SetParam(L6470_FS_SPD, thresholdSpeed ? FSCalc(thresholdSpeed) : 0x3FF); } -void L6470::setCurrent(const int current) { (void)(current); } +void L6470::setCurrent(const int16_t current) { (void)(current); } // Configure the L6470_MAX_SPEED register- this is the maximum number of (micro)steps per // second allowed. You'll want to mess around with your desired application to see @@ -117,7 +182,7 @@ void L6470::setCurrent(const int current) { (void)(current); } // passed to this function is in steps/tick; MaxSpdCalc() will convert a number of // steps/s into an appropriate value for this function. Note that for any move or // goto type function where no speed is specified, this value will be used. -void L6470::setMaxSpeed(const int speed) { SetParam(L6470_MAX_SPEED, MaxSpdCalc(speed)); } +void L6470::setMaxSpeed(const int16_t speed) { SetParam(L6470_MAX_SPEED, MaxSpdCalc(speed)); } // Configure the L6470_MAX_SPEED register- this is the maximum number of (micro)steps per // second allowed. You'll want to mess around with your desired application to see @@ -125,7 +190,7 @@ void L6470::setMaxSpeed(const int speed) { SetParam(L6470_MAX_SPEED, MaxSpdCalc( // passed to this function is in steps/tick; MaxSpdCalc() will convert a number of // steps/s into an appropriate value for this function. Note that for any move or // goto type function where no speed is specified, this value will be used. -void L6470::setMinSpeed(const int speed) { SetParam(L6470_MIN_SPEED, MinSpdCalc(speed)); } +void L6470::setMinSpeed(const int16_t speed) { SetParam(L6470_MIN_SPEED, MinSpdCalc(speed)); } // Configure the acceleration rate, in steps/tick/tick. There is also a L6470_DEC register; // both of them have a function (AccCalc() and DecCalc() respectively) that convert @@ -153,14 +218,14 @@ float L6470::getSpeed() { } // Configure the overcurrent detection threshold. -void L6470::setOverCurrent(const unsigned int ma_current) { - const byte OCValue = (byte)floor(ma_current / 375); +void L6470::setOverCurrent(const uint16_t ma_current) { + const uint8_t OCValue = (uint8_t)floor(ma_current / 375 - 1); SetParam(L6470_OCD_TH, OCValue < 0x0F ? OCValue : 0x0F); } void L6470::setStallCurrent(const float ma_current) { - const byte STHValue = (byte)floor(ma_current / 31.25); - SetParam(L6470_STALL_TH, STHValue < 0x80 ? STHValue : 0x80); + const uint8_t STHValue = (uint8_t)floor(ma_current / 31.25 - 1); + SetParam(L6470_STALL_TH, STHValue < 0x80 ? STHValue : 0x7F); } // Enable or disable the low-speed optimization option. If enabling, @@ -179,13 +244,13 @@ void L6470::SetLowSpeedOpt(const boolean enable) { // will switch the device into full-step mode. // The SpdCalc() function is provided to convert steps/s values into // appropriate integer values for this function. -void L6470::run(const byte dir, const float spd) { - unsigned long speedVal = SpdCalc(spd); +void L6470::run(const uint8_t dir, const float spd) { + uint32_t speedVal = SpdCalc(spd); Xfer(dSPIN_RUN | dir); if (speedVal > 0xFFFFF) speedVal = 0xFFFFF; - Xfer((byte)(speedVal >> 16)); - Xfer((byte)(speedVal >> 8)); - Xfer((byte)(speedVal)); + Xfer(uint8_t(speedVal >> 16)); + Xfer(uint8_t(speedVal >> 8)); + Xfer(uint8_t(speedVal)); } // dSPIN_STEP_CLOCK puts the device in external step clocking mode. When active, @@ -193,7 +258,7 @@ void L6470::run(const byte dir, const float spd) { // the direction (set by the dSPIN_FWD and dSPIN_REV constants) imposed by the call // of this function. Motion commands (dSPIN_RUN, dSPIN_MOVE, etc) will cause the device // to exit step clocking mode. -void L6470::Step_Clock(const byte dir) { +void L6470::Step_Clock(const uint8_t dir) { Xfer(dSPIN_STEP_CLOCK | dir); } @@ -202,13 +267,13 @@ void L6470::Step_Clock(const byte dir) { // will accelerate according the acceleration and deceleration curves, and // will run at L6470_MAX_SPEED. Stepping mode will adhere to L6470_FS_SPD value, as well. void L6470::move(const long n_step) { - const byte dir = n_step >= 0 ? dSPIN_FWD : dSPIN_REV; + const uint8_t dir = n_step >= 0 ? dSPIN_FWD : dSPIN_REV; Xfer(dSPIN_MOVE | dir); // Set direction long n_stepABS = abs(n_step); if (n_stepABS > 0x3FFFFF) n_stepABS = 0x3FFFFF; - Xfer((byte)(n_stepABS >> 16)); - Xfer((byte)(n_stepABS >> 8)); - Xfer((byte)(n_stepABS)); + Xfer(uint8_t(n_stepABS >> 16)); + Xfer(uint8_t(n_stepABS >> 8)); + Xfer(uint8_t(n_stepABS)); } // dSPIN_GOTO operates much like dSPIN_MOVE, except it produces absolute motion instead @@ -217,18 +282,18 @@ void L6470::move(const long n_step) { void L6470::goTo(long pos) { Xfer(dSPIN_GOTO); if (pos > 0x3FFFFF) pos = 0x3FFFFF; - Xfer((byte)(pos >> 16)); - Xfer((byte)(pos >> 8)); - Xfer((byte)(pos)); + Xfer(uint8_t(pos >> 16)); + Xfer(uint8_t(pos >> 8)); + Xfer(uint8_t(pos)); } // Same as dSPIN_GOTO, but with user constrained rotational direction. -void L6470::goTo_DIR(const byte dir, long pos) { +void L6470::goTo_DIR(const uint8_t dir, long pos) { Xfer(dSPIN_GOTO_DIR | dir); if (pos > 0x3FFFFF) pos = 0x3FFFFF; - Xfer((byte)(pos >> 16)); - Xfer((byte)(pos >> 8)); - Xfer((byte)(pos)); + Xfer(uint8_t(pos >> 16)); + Xfer(uint8_t(pos >> 8)); + Xfer(uint8_t(pos)); } // GoUntil will set the motor running with direction dir (dSPIN_REV or @@ -237,12 +302,12 @@ void L6470::goTo_DIR(const byte dir, long pos) { // performed at the falling edge, and depending on the value of // act (either RESET or COPY) the value in the L6470_ABS_POS register is // either RESET to 0 or COPY-ed into the L6470_MARK register. -void L6470::goUntil(const byte act, const byte dir, unsigned long spd) { +void L6470::goUntil(const uint8_t act, const uint8_t dir, uint32_t spd) { Xfer(dSPIN_GO_UNTIL | act | dir); if (spd > 0x3FFFFF) spd = 0x3FFFFF; - Xfer((byte)(spd >> 16)); - Xfer((byte)(spd >> 8)); - Xfer((byte)(spd)); + Xfer(uint8_t(spd >> 16)); + Xfer(uint8_t(spd >> 8)); + Xfer(uint8_t(spd)); } // Similar in nature to GoUntil, ReleaseSW produces motion at the @@ -252,7 +317,7 @@ void L6470::goUntil(const byte act, const byte dir, unsigned long spd) { // and the L6470_ABS_POS register is either COPY-ed into L6470_MARK or RESET to // 0, depending on whether RESET or COPY was passed to the function // for act. -void L6470::releaseSW(const byte act, const byte dir) { +void L6470::releaseSW(const uint8_t act, const uint8_t dir) { Xfer(dSPIN_RELEASE_SW | act | dir); } @@ -270,9 +335,9 @@ void L6470::setMark(long value) { Xfer(L6470_MARK); if (value > 0x3FFFFF) value = 0x3FFFFF; if (value < -0x3FFFFF) value = -0x3FFFFF; - Xfer((byte)(value >> 16)); - Xfer((byte)(value >> 8)); - Xfer((byte)(value)); + Xfer(uint8_t(value >> 16)); + Xfer(uint8_t(value >> 8)); + Xfer(uint8_t(value)); } void L6470::setMark() { @@ -280,9 +345,9 @@ void L6470::setMark() { Xfer(L6470_MARK); if (value > 0x3FFFFF) value = 0x3FFFFF; if (value < -0x3FFFFF) value = -0x3FFFFF; - Xfer((byte)(value >> 16)); - Xfer((byte)(value >> 8)); - Xfer((byte)(value)); + Xfer(uint8_t(value >> 16)); + Xfer(uint8_t(value >> 8)); + Xfer(uint8_t(value)); } // Sets the L6470_ABS_POS register to 0, effectively declaring the current @@ -308,7 +373,7 @@ void L6470::free() { Xfer(dSPIN_HARD_HIZ); } // Fetch and return the 16-bit value in the L6470_STATUS register. Resets // any warning flags and exits any error states. Using GetParam() // to read L6470_STATUS does not clear these values. -int L6470::getStatus() { +int16_t L6470::getStatus() { Xfer(dSPIN_GET_STATUS); return Xfer(0) << 8 | Xfer(0); } @@ -317,83 +382,83 @@ int L6470::getStatus() { // 250ns (datasheet value)- 0x08A on boot. // Multiply desired steps/s/s by .137438 to get an appropriate value for this register. // This is a 12-bit value, so we need to make sure the value is at or below 0xFFF. -unsigned long L6470::AccCalc(const float stepsPerSecPerSec) { - unsigned long temp = (unsigned long)(stepsPerSecPerSec * 0.137438); +uint32_t L6470::AccCalc(const float stepsPerSecPerSec) { + uint32_t temp = (uint32_t)(stepsPerSecPerSec * 0.137438); return temp < 0x00000FFF ? temp : 0x00000FFF; } -unsigned long L6470::DecCalc(float stepsPerSecPerSec) { +uint32_t L6470::DecCalc(float stepsPerSecPerSec) { // The calculation for L6470_DEC is the same as for L6470_ACC. Value is 0x08A on boot. // This is a 12-bit value, so we need to make sure the value is at or below 0xFFF. - unsigned long temp = (unsigned long)(stepsPerSecPerSec * 0.137438); + uint32_t temp = (uint32_t)(stepsPerSecPerSec * 0.137438); return temp < 0x00000FFF ? temp : 0x00000FFF; } -unsigned long L6470::MaxSpdCalc(const float stepsPerSec) { +uint32_t L6470::MaxSpdCalc(const float stepsPerSec) { // The value in the MAX_SPD register is [(steps/s)*(tick)]/(2^-18) where tick is // 250ns (datasheet value)- 0x041 on boot. // Multiply desired steps/s by .065536 to get an appropriate value for this register // This is a 10-bit value, so we need to make sure it remains at or below 0x3FF - unsigned long temp = (unsigned long)(stepsPerSec * .065536); + uint32_t temp = (uint32_t)(stepsPerSec * .065536); return temp < 0x000003FF ? temp : 0x000003FF; } -unsigned long L6470::MinSpdCalc(const float stepsPerSec) { +uint32_t L6470::MinSpdCalc(const float stepsPerSec) { // The value in the MIN_SPD register is [(steps/s)*(tick)]/(2^-24) where tick is // 250ns (datasheet value)- 0x000 on boot. // Multiply desired steps/s by 4.1943 to get an appropriate value for this register // This is a 12-bit value, so we need to make sure the value is at or below 0xFFF. - unsigned long temp = (unsigned long)(stepsPerSec * 4.1943); + uint32_t temp = (uint32_t)(stepsPerSec * 4.1943); return temp < 0x00000FFF ? temp : 0x00000FFF; } -unsigned long L6470::FSCalc(const float stepsPerSec) { +uint32_t L6470::FSCalc(const float stepsPerSec) { // The value in the L6470_FS_SPD register is ([(steps/s)*(tick)]/(2^-18))-0.5 where tick is // 250ns (datasheet value)- 0x027 on boot. // Multiply desired steps/s by .065536 and subtract .5 to get an appropriate value for this register // This is a 10-bit value, so we need to make sure the value is at or below 0x3FF. - unsigned long temp = (unsigned long)(stepsPerSec * .065536 - .5); + uint32_t temp = (uint32_t)(stepsPerSec * .065536 - .5); return temp < 0x000003FF ? temp : 0x000003FF; } -unsigned long L6470::IntSpdCalc(const float stepsPerSec) { +uint32_t L6470::IntSpdCalc(const float stepsPerSec) { // The value in the L6470_INT_SPD register is [(steps/s)*(tick)]/(2^-24) where tick is // 250ns (datasheet value)- 0x408 on boot. // Multiply desired steps/s by 4.1943 to get an appropriate value for this register // This is a 14-bit value, so we need to make sure the value is at or below 0x3FFF. - unsigned long temp = (unsigned long)(stepsPerSec * 4.1943); + uint32_t temp = (uint32_t)(stepsPerSec * 4.1943); return temp < 0x00003FFF ? temp : 0x00003FFF; } -unsigned long L6470::SpdCalc(const float stepsPerSec) { +uint32_t L6470::SpdCalc(const float stepsPerSec) { // When issuing dSPIN_RUN command, the 20-bit speed is [(steps/s)*(tick)]/(2^-28) where tick is // 250ns (datasheet value). // Multiply desired steps/s by 67.106 to get an appropriate value for this register // This is a 20-bit value, so we need to make sure the value is at or below 0xFFFFF. - unsigned long temp = (unsigned long)(stepsPerSec * 67.106); + uint32_t temp = (uint32_t)(stepsPerSec * 67.106); return temp < 0x000FFFFF ? temp : 0x000FFFFF; } -unsigned long L6470::Param(unsigned long value, const byte bit_len) { +uint32_t L6470::Param(uint32_t value, const uint8_t bit_len) { // Generalization of the subsections of the register read/write functionality. // We want the end user to just write the value without worrying about length, // so we pass a bit length parameter from the calling function. - const byte byte_len = (bit_len + 7) / 8; + const uint8_t uint8_t_len = (bit_len + 7) / 8; // Ensure the value has no spurious bits set, and apply limit. - unsigned long mask = 0xFFFFFFFF >> (32 - bit_len); + uint32_t mask = 0xFFFFFFFF >> (32 - bit_len); if (value > mask) value = mask; - // The following three if statements handle the various possible byte length - // transfers- it'll be no less than 1 but no more than 3 bytes of data. - // L6470::Xfer() sends a byte out through SPI and returns a byte received + // The following three if statements handle the various possible uint8_t length + // transfers- it'll be no less than 1 but no more than 3 uint8_ts of data. + // L6470::Xfer() sends a uint8_t out through SPI and returns a uint8_t received // over SPI- when calling it, we typecast a shifted version of the masked // value, then we shift the received value back by the same amount and // store it until return time. - unsigned long ret_val; - switch (byte_len) { - case 3: ret_val = long(Xfer((byte)(value >> 16))) << 16; break; - case 2: ret_val = long(Xfer((byte)(value >> 8))) << 8; break; - case 1: ret_val = Xfer((byte)value); break; - default: ret_val = 0; break; + uint32_t ret_val = 0; + switch (uint8_t_len) { + case 3: ret_val = long(Xfer(uint8_t(value >> 16))) << 16; + case 2: ret_val |= long(Xfer(uint8_t(value >> 8))) << 8; + case 1: ret_val |= Xfer(uint8_t(value)); + default: break; } //Serial.println(ret_val, HEX); @@ -403,33 +468,71 @@ unsigned long L6470::Param(unsigned long value, const byte bit_len) { return ret_val & mask; } -byte L6470::Xfer(byte data) { - // This simple function shifts a byte out over SPI and receives a byte over - // SPI. Unusually for SPI devices, the dSPIN requires a toggling of the - // CS (slaveSelect) pin after each byte sent. That makes this function - // a bit more reasonable, because we can include more functionality in it. - digitalWrite(_SSPin, LOW); - // SPI.transfer() both shifts a byte out on the MOSI pin AND receives a - // byte in on the MISO pin. - const byte data_out = SPI.transfer(data); - digitalWrite(_SSPin, HIGH); - return data_out; +uint8_t L6470::Xfer(uint8_t data) { + + if (pin_SCK < 0) { // External SPI + return (uint8_t)( + position ? L6470_transfer(data, pin_SS, position) // ... in a chain + : L6470_transfer(data, pin_SS) // ... not chained + ); + } + + // if pin_SCK is set use internal soft SPI. + + if (position == 0) { // Internal soft SPI, not in a chain + if (pin_SS >= 0) digitalWrite(pin_SS, LOW); // Allow external code to control SS_PIN + uint8_t bits = 8; + do { + digitalWrite(pin_SCK, LOW); + digitalWrite(pin_MOSI, data & 0x80); + delay(0); // 10 cycles @ 84mhz + digitalWrite(pin_SCK, HIGH); + data <<= 1; // little setup time + data |= (digitalRead(pin_MISO) != 0); + } while (--bits); + delay(0); // 10 cycles @ 84mhz + if (pin_SS >= 0) digitalWrite(pin_SS, HIGH); + return data; + } + else { // internal soft SPI SPI, and in a chain + #define CMD_NOP 0 + uint8_t out_data = 0; + uint8_t return_data = 0; + digitalWrite(pin_SS, LOW); + for (uint8_t i = chain[0]; i >= 1; i--) { + out_data = (chain[i] == position ? data : CMD_NOP); + uint8_t bits = 8; + do { + digitalWrite(pin_SCK, LOW); + digitalWrite(pin_MOSI, out_data & 0x80); + delay(0); // 10 cycles @ 84mhz + digitalWrite(pin_SCK, HIGH); + out_data <<= 1; // little setup time + out_data |= (digitalRead(pin_MISO) != 0); + } while (--bits); + delay(0); // 10 cycles @ 84mhz + if (chain[i] == position) return_data = out_data; + } + digitalWrite(pin_SS, HIGH); + return return_data; + } + return 0; // never executed but prevents a compiler warning } -void L6470::SetParam(const byte param, const unsigned long value) { +void L6470::SetParam(const uint8_t param, const uint32_t value) { Xfer(dSPIN_SET_PARAM | param); ParamHandler(param, value); } // Read from the various registers in the dSPIN chip. -unsigned long L6470::GetParam(const byte param) { +uint32_t L6470::GetParam(const uint8_t param) { Xfer(dSPIN_GET_PARAM | param); return ParamHandler(param, 0); } -long L6470::convert(unsigned long val) { +long L6470::convert(uint32_t val) { // Convert 22bit 2s comp to signed long - const int MSB = val >> 21; + const int16_t MSB = val >> 21; val >>= 11; val <<= 11; if (MSB == 1) val |= 0b11111111111000000000000000000000; @@ -439,12 +542,16 @@ long L6470::convert(unsigned long val) { // Much of the functionality between "get parameter" and "set parameter" is // very similar, so we deal with that by putting all of it in one function // here to save memory space and simplify the program. -unsigned long L6470::ParamHandler(const byte param, const unsigned long value) { + +// ParamHandler() does not list the powerSTEP01 current mode registers because +// these registers are the same length as their voltage mode counterparts. + +uint32_t L6470::ParamHandler(const uint8_t param, const uint32_t value) { // This switch structure handles the appropriate action for each register. // This is necessary since not all registers are of the same length, either - // bit-wise or byte-wise, so we want to make sure we mask out any spurious + // bit-wise or uint8_t-wise, so we want to make sure we mask out any spurious // bits and do the right number of transfers. That is handled by the dSPIN_Param() - // function, in most cases, but for 1-byte or smaller transfers, we call + // function, in most cases, but for 1-uint8_t or smaller transfers, we call // Xfer() directly. switch (param) { // L6470_ABS_POS is the current absolute offset from home. It is a 22 bit number expressed @@ -452,15 +559,19 @@ unsigned long L6470::ParamHandler(const byte param, const unsigned long value) { // the motor is running, but at any other time, it can be updated to change the // interpreted position of the motor. case L6470_ABS_POS: return Param(value, 22); + // L6470_EL_POS is the current electrical position in the step generation cycle. It can // be set when the motor is not in motion. Value is 0 on power up. case L6470_EL_POS: return Param(value, 9); + // L6470_MARK is a second position other than 0 that the motor can be told to go to. As // with L6470_ABS_POS, it is 22-bit two's complement. Value is 0 on power up. case L6470_MARK: return Param(value, 22); + // L6470_SPEED contains information about the current speed. It is read-only. It does // NOT provide direction information. case L6470_SPEED: return Param(0, 20); + // L6470_ACC and L6470_DEC set the acceleration and deceleration rates. Set L6470_ACC to 0xFFF // to get infinite acceleration/decelaeration- there is no way to get infinite // deceleration w/o infinite acceleration (except the HARD STOP command). @@ -469,12 +580,14 @@ unsigned long L6470::ParamHandler(const byte param, const unsigned long value) { // 12-bit values for these two registers. case L6470_ACC: case L6470_DEC: return Param(value, 12); + // L6470_MAX_SPEED is just what it says- any command which attempts to set the speed // of the motor above this value will simply cause the motor to turn at this // speed. Value is 0x041 on power up. // MaxSpdCalc() function exists to convert steps/s value into a 10-bit value // for this register. case L6470_MAX_SPEED: return Param(value, 10); + // L6470_MIN_SPEED controls two things- the activation of the low-speed optimization // feature and the lowest speed the motor will be allowed to operate at. LSPD_OPT // is the 13th bit, and when it is set, the minimum allowed speed is automatically @@ -482,41 +595,49 @@ unsigned long L6470::ParamHandler(const byte param, const unsigned long value) { // MinSpdCalc() function exists to convert steps/s value into a 12-bit value for this // register. SetLowSpeedOpt() function exists to enable/disable the optimization feature. case L6470_MIN_SPEED: return Param(value, 12); + // L6470_FS_SPD register contains a threshold value above which microstepping is disabled // and the dSPIN operates in full-step mode. Defaults to 0x027 on power up. // FSCalc() function exists to convert steps/s value into 10-bit integer for this // register. case L6470_FS_SPD: return Param(value, 10); + // KVAL is the maximum voltage of the PWM outputs. These 8-bit values are ratiometric // representations: 255 for full output voltage, 128 for half, etc. Default is 0x29. // The implications of different KVAL settings is too complex to dig into here, but // it will usually work to max the value for dSPIN_RUN, L6470_ACC, and L6470_DEC. Maxing the value for // HOLD may result in excessive power dissipation when the motor is not running. - case L6470_KVAL_HOLD: return Xfer((byte)value); - case L6470_KVAL_RUN: return Xfer((byte)value); - case L6470_KVAL_ACC: return Xfer((byte)value); - case L6470_KVAL_DEC: return Xfer((byte)value); + case L6470_KVAL_HOLD: return Xfer(uint8_t(value)); + case L6470_KVAL_RUN: return Xfer(uint8_t(value)); + case L6470_KVAL_ACC: return Xfer(uint8_t(value)); + case L6470_KVAL_DEC: return Xfer(uint8_t(value)); + // L6470_INT_SPD, L6470_ST_SLP, L6470_FN_SLP_ACC and L6470_FN_SLP_DEC are all related to the back EMF // compensation functionality. Please see the datasheet for details of this // function- it is too complex to discuss here. Default values seem to work // well enough. case L6470_INT_SPD: return Param(value, 14); - case L6470_ST_SLP: return Xfer((byte)value); - case L6470_FN_SLP_ACC: return Xfer((byte)value); - case L6470_FN_SLP_DEC: return Xfer((byte)value); + case L6470_ST_SLP: return Xfer(uint8_t(value)); + case L6470_FN_SLP_ACC: return Xfer(uint8_t(value)); + case L6470_FN_SLP_DEC: return Xfer(uint8_t(value)); + // L6470_K_THERM is motor winding thermal drift compensation. Please see the datasheet // for full details on operation- the default value should be okay for most users. - case L6470_K_THERM: return Xfer((byte)value & 0x0F); + case L6470_K_THERM: return Xfer(uint8_t(value) & 0x0F); + // L6470_ADC_OUT is a read-only register containing the result of the ADC measurements. // This is less useful than it sounds; see the datasheet for more information. case L6470_ADC_OUT: return Xfer(0); + // Set the overcurrent threshold. Ranges from 375mA to 6A in steps of 375mA. // A set of defined constants is provided for the user's convenience. Default // value is 3.375A- 0x08. This is a 4-bit value. - case L6470_OCD_TH: return Xfer((byte)value & 0x0F); + case L6470_OCD_TH: return Xfer(uint8_t(value) & 0x0F); + // Stall current threshold. Defaults to 0x40, or 2.03A. Value is from 31.25mA to // 4A in 31.25mA steps. This is a 7-bit value. - case L6470_STALL_TH: return Xfer((byte)value & 0x7F); + case L6470_STALL_TH: return Xfer(uint8_t(value) & 0x7F); + // L6470_STEP_MODE controls the microstepping settings, as well as the generation of an // output signal from the dSPIN. Bits 2:0 control the number of microsteps per // step the part will generate. Bit 7 controls whether the BUSY/SYNC pin outputs @@ -525,22 +646,25 @@ unsigned long L6470::ParamHandler(const byte param, const unsigned long value) { // that relationship as it is too complex to reproduce here. // Most likely, only the microsteps per step value will be needed; there is a set // of constants provided for ease of use of these values. - case L6470_STEP_MODE: return Xfer((byte)value); + case L6470_STEP_MODE: return Xfer(uint8_t(value)); + // L6470_ALARM_EN controls which alarms will cause the FLAG pin to fall. A set of constants // is provided to make this easy to interpret. By default, ALL alarms will trigger the // FLAG pin. - case L6470_ALARM_EN: return Xfer((byte)value); + case L6470_ALARM_EN: return Xfer(uint8_t(value)); + // L6470_CONFIG contains some assorted configuration bits and fields. A fairly comprehensive // set of reasonably self-explanatory constants is provided, but users should refer // to the datasheet before modifying the contents of this register to be certain they // understand the implications of their modifications. Value on boot is 0x2E88; this // can be a useful way to verify proper start up and operation of the dSPIN chip. case L6470_CONFIG: return Param(value, 16); + // L6470_STATUS contains read-only information about the current condition of the chip. A // comprehensive set of constants for masking and testing this register is provided, but // users should refer to the datasheet to ensure that they fully understand each one of // the bits in the register. case L6470_STATUS: return Param(0, 16); // L6470_STATUS is a read-only register } - return Xfer((byte)(value)); + return Xfer(uint8_t(value)); } diff --git a/L6470/L6470.h b/src/L6470.h old mode 100755 new mode 100644 similarity index 80% rename from L6470/L6470.h rename to src/L6470.h index 095a9f7..f342854 --- a/L6470/L6470.h +++ b/src/L6470.h @@ -1,23 +1,27 @@ -//////////////////////////////////////////////////////////// -//ORIGINAL CODE 12/12/2011- Mike Hord, SparkFun Electronics -//LIBRARY Created by Adam Meyer of bildr Aug 18th 2012 -//Released as MIT license -//////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////// +// // +// ORIGINAL CODE 12 Dec 2011 Mike Hord, SparkFun Electronics // +// // +// LIBRARY Created by Adam Meyer (@ameyer) of bildr 18 Aug 2012 // +// Modified by Scott Lahteine (@thinkyhead) 6 Mar 2018 // +// Chain and SPI updates by Bob Kuhn (@bob-the-kuhn) 6 Jan 2019 // +// Released as MIT license // +// // +//////////////////////////////////////////////////////////////////// #ifndef _L6470_H_ #define _L6470_H_ #include -#include -#define L6470_LIBRARY_VERSION 0x000602 +#define L6470_LIBRARY_VERSION 0x000700 //#define SCK 10 // Wire this to the CSN pin //#define MOSI 11 // Wire this to the SDI pin //#define MISO 12 // Wire this to the SDO pin //#define SS_PIN 16 // Wire this to the CK pin -#define RESET 6 // Wire this to the STBY line -#define BUSYN 4 // Wire this to the BSYN line +#define PIN_RESET 6 // Wire this to the STBY line +#define PIN_BUSYN 4 // Wire this to the BSYN line #define STAT1 14 // Hooked to an LED on the test jig #define STAT2 15 // Hooked to an LED on the test jig @@ -236,28 +240,30 @@ #define dSPIN_ACTION_COPY 0x01 class L6470 { +public: - public: + static uint8_t chain[21]; // 0 - number of drivers in chain, 1... axis index for first device in the chain (closest to MOSI) - L6470(const int SSPin); // TODO: Configurable SPI pins + L6470(const int16_t pin_SS); void init(); + void spi_init(); - void setMicroSteps(int microSteps); - void setCurrent(const int current); // NOT IMPLEMENTED - void setMaxSpeed(const int speed); - void setMinSpeed(const int speed); + void setMicroSteps(int16_t microSteps); + void setCurrent(const int16_t current); // NOT IMPLEMENTED + void setMaxSpeed(const int16_t speed); + void setMinSpeed(const int16_t speed); void setAcc(const float acceleration); void setDec(const float deceleration); - void setOverCurrent(unsigned int ma_current); + void setOverCurrent(uint16_t ma_current); void setThresholdSpeed(const float threshold); void setStallCurrent(float ma_current); - unsigned long ParamHandler(const byte param, const unsigned long value); + uint32_t ParamHandler(const uint8_t param, const uint32_t value); void SetLowSpeedOpt(boolean enable); - void run(const byte dir, const float spd); - void Step_Clock(const byte dir); + void run(const uint8_t dir, const float spd); + void Step_Clock(const uint8_t dir); void goHome(); void setAsHome(); @@ -265,12 +271,12 @@ class L6470 { void goMark(); void move(const long n_step); void goTo(long pos); - void goTo_DIR(const byte dir, long pos); - void goUntil(const byte act, const byte dir, unsigned long spd); + void goTo_DIR(const uint8_t dir, long pos); + void goUntil(const uint8_t act, const uint8_t dir, uint32_t spd); boolean isBusy(); - void releaseSW(const byte act, const byte dir); + void releaseSW(const uint8_t act, const uint8_t dir); float getSpeed(); long getPos(); @@ -283,26 +289,39 @@ class L6470 { void hardStop(); void softFree(); void free(); - int getStatus(); + int16_t getStatus(); - private: + void SetParam(const uint8_t param, const uint32_t value); + uint32_t GetParam(const uint8_t param); - long convert(unsigned long val); + void set_chain_info(const uint8_t axis_index, const uint8_t position); - void SetParam(const byte param, const unsigned long value); - unsigned long GetParam(const byte param); + void set_pins(const int16_t SCK, const int16_t MOSI, const int16_t MISO, const int16_t RESET, const int16_t BUSYN); - unsigned long AccCalc(const float stepsPerSecPerSec); - unsigned long DecCalc(const float stepsPerSecPerSec); - unsigned long MaxSpdCalc(const float stepsPerSec); - unsigned long MinSpdCalc(const float stepsPerSec); - unsigned long FSCalc(const float stepsPerSec); - unsigned long IntSpdCalc(const float stepsPerSec); - unsigned long SpdCalc(const float stepsPerSec); - unsigned long Param(unsigned long value, const byte bit_len); - byte Xfer(byte data); +private: - int _SSPin; + long convert(uint32_t val); + + uint32_t AccCalc(const float stepsPerSecPerSec); + uint32_t DecCalc(const float stepsPerSecPerSec); + uint32_t MaxSpdCalc(const float stepsPerSec); + uint32_t MinSpdCalc(const float stepsPerSec); + uint32_t FSCalc(const float stepsPerSec); + uint32_t IntSpdCalc(const float stepsPerSec); + uint32_t SpdCalc(const float stepsPerSec); + uint32_t Param(uint32_t value, const uint8_t bit_len); + uint8_t Xfer(uint8_t data); + uint8_t Xfer(uint8_t data, int16_t ss_pin, uint8_t position); + + int16_t pin_SS = -1, + pin_SCK = -1, + pin_MOSI = -1, + pin_MISO = -1, + pin_RESET = -1, + pin_BUSYN = -1; + + uint8_t axis_index; + uint8_t position = 0; // 0 - not part of a chain }; #endif // _L6470_H_ From bbe7627dfdd52674acb4f23448ff8006af2d2eeb Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Fri, 1 Feb 2019 23:32:53 -0600 Subject: [PATCH 02/14] Update two_motors_synchronized.ino --- examples/two_motors_synchronized/two_motors_synchronized.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/two_motors_synchronized/two_motors_synchronized.ino b/examples/two_motors_synchronized/two_motors_synchronized.ino index 2cb106e..4f94ef7 100644 --- a/examples/two_motors_synchronized/two_motors_synchronized.ino +++ b/examples/two_motors_synchronized/two_motors_synchronized.ino @@ -129,7 +129,7 @@ void goTo(long location_1, long location_2) { Buffer_Transfer(buffer_command, L6470::chain[0]); // send the commands Buffer_Transfer(addr21_16 , L6470::chain[0]); // send the MSB of the position - Buffer_Transfer(addr15_8 , L6470::chain[0]); + Buffer_Transfer(addr15_8 , L6470::chain[0]); Buffer_Transfer(addr7_0 , L6470::chain[0]); // this one results in the motors moving } From c906cc6d2967dfcb2242297e9c830a7545c83a18 Mon Sep 17 00:00:00 2001 From: Bob Kuhn Date: Tue, 26 Feb 2019 17:58:11 -0600 Subject: [PATCH 03/14] Add powerSTEP01/L6480 support (#12) --- library.json | 2 +- library.properties | 2 +- src/L6470.cpp | 189 ++++++++++++++++++++--------------------- src/L6470.h | 203 +++++++++++++++++++++++++++++++++++---------- 4 files changed, 258 insertions(+), 138 deletions(-) diff --git a/library.json b/library.json index 7d5d6f4..9001085 100755 --- a/library.json +++ b/library.json @@ -7,7 +7,7 @@ "type": "git", "url": "https://github.com/ameyer/Arduino-L6470.git" }, - "version": "0.7.0", + "version": "0.8.0", "authors": [ { diff --git a/library.properties b/library.properties index b86948d..119278a 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=Arduino-L6470 -version=0.7.0 +version=0.8.0 author=Adam Meyer maintainer=Adam Meyer , Scott Lahteine sentence=L6470 stepper driver library diff --git a/src/L6470.cpp b/src/L6470.cpp index 5cd4978..2e448c7 100644 --- a/src/L6470.cpp +++ b/src/L6470.cpp @@ -13,43 +13,31 @@ #include -uint8_t L6470::chain[21]; +uint8_t L64XX_chain[21]; // [0] - number of drivers in chain // [1]... axis index for first device in the chain (closest to MOSI) -// placeholders to prevent compiler errors if using internal SPI functions - -// Define WEAK attribute -#ifdef __CC_ARM // Keil µVision 4 - #define WEAK __attribute__ ((weak)) -#elif defined(__ICCARM__) // IAR Ewarm 5.41+ - #define WEAK __weak -#elif defined(__GNUC__) // GCC CS3 2009q3-68 - #define WEAK __attribute__ ((weak)) -#else - #define WEAK -#endif - -void L6470_SPI_init() WEAK ; -void L6470_SPI_init() {} // called whenever a stepper object is created -uint8_t L6470_transfer(uint8_t data, int16_t ss_pin) WEAK ; -uint8_t L6470_transfer(uint8_t data, int16_t ss_pin) { return 0; } -uint8_t L6470_transfer(uint8_t data, int16_t ss_pin, uint8_t axis) WEAK ; -uint8_t L6470_transfer(uint8_t data, int16_t ss_pin, uint8_t axis) { return 0; } - // "data" is the data to be sent to the target device - // "ss_pin" is the slave select pin to be used for this transfer - // "axis" is the index of the axis the data is meant for. Only used in SPI daisy chain systems +L64XX::L64XX() {} L6470::L6470(const int16_t ss_pin) { pin_SS = ss_pin; // Serial.begin(9600); - chain[0] = 0; // init chain array +} + +L6480::L6480(const int16_t ss_pin) { + pin_SS = ss_pin; + // Serial.begin(9600); +} + +powerSTEP01::powerSTEP01(const int16_t ss_pin) { + pin_SS = ss_pin; + // Serial.begin(9600); } // Generic init function to set up communication with the dSPIN chip. -// call this after setting up the SPI(s) (after all "L6470::set_pins" and "set_chain_info" commands) -void L6470::init() { +// call this after setting up the SPI(s) (after all "L64XX::set_pins" and "set_chain_info" commands) +void L64XX::init() { if (pin_SS >= 0) { //init pin_SS if it has been set for this chip pinMode(pin_SS, OUTPUT); @@ -80,12 +68,12 @@ void L6470::init() { // Set up the L6470_CONFIG register as follows: // PWM frequency divisor = 1 // PWM frequency multiplier = 2 (62.5kHz PWM frequency) - // Slew rate is 290V/us + // Slew rate is 110V/us // Do NOT shut down bridges on overcurrent // Disable motor voltage compensation // Hard stop on switch low // 16MHz internal oscillator, nothing on output - SetParam(L6470_CONFIG, CONFIG_PWM_DIV_1 | CONFIG_PWM_MUL_2 | CONFIG_SR_290V_us| CONFIG_OC_SD_DISABLE | CONFIG_VS_COMP_DISABLE | CONFIG_SW_HARD_STOP | CONFIG_INT_16MHZ); + SetParam(L6470_CONFIG, CONFIG_PWM_DIV_1 | CONFIG_PWM_MUL_2 | CONFIG_SR_110V_us | CONFIG_OC_SD_DISABLE | CONFIG_VS_COMP_DISABLE | CONFIG_SW_HARD_STOP | CONFIG_INT_16MHZ); // Configure the dSPIN_RUN KVAL. This defines the duty cycle of the PWM of the bridges // during running. 0xFF means that they are essentially NOT PWMed during run; this @@ -106,20 +94,20 @@ void L6470::init() { } // add to the chain array and save chain info for this stepper -void L6470::set_chain_info(const uint8_t axis, const uint8_t chain_position) { +void L64XX::set_chain_info(const uint8_t axis, const uint8_t chain_position) { if (chain_position) { - chain[0]++; - chain[chain_position] = axis; + L64XX_chain[0]++; + L64XX_chain[chain_position] = axis; position = chain_position; axis_index = axis; } else - chain[0] = 0; //reset array back to uninitialized + L64XX_chain[0] = 0; //reset array back to uninitialized } // Sets optional pins for this stepper // pin_SS is set by the instantiation call. -void L6470::set_pins(const int16_t sck, const int16_t mosi, const int16_t miso, const int16_t reset, const int16_t busyn) { +void L64XX::set_pins(const int16_t sck, const int16_t mosi, const int16_t miso, const int16_t reset, const int16_t busyn) { pin_SCK = sck; pin_MOSI = mosi; pin_MISO = miso; @@ -137,12 +125,12 @@ void L6470::set_pins(const int16_t sck, const int16_t mosi, const int16_t miso, pinMode(pin_BUSYN, INPUT); // reset the dSPIN chip. This could also be accomplished by - // calling the "L6470::ResetDev()" function after SPI is initialized. + // calling the "L64XX::ResetDev()" function after SPI is initialized. // // Reset should be done here ONLY if each chip has a dedicated reset pin. Otherwise // the already initialized chip(s) will be set back to power up values. - // This need to be done BEFORE calling L6470::init() or the data written by the init + // This need to be done BEFORE calling L64XX::init() or the data written by the init // will be ceared to power up values. if (pin_RESET >= 0) { pinMode(pin_RESET, OUTPUT); @@ -155,9 +143,9 @@ void L6470::set_pins(const int16_t sck, const int16_t mosi, const int16_t miso, } } -boolean L6470::isBusy() { return !(getStatus() & 0x0002); } +boolean L64XX::isBusy() { return !(getStatus() & 0x0002); } -void L6470::setMicroSteps(int16_t microSteps) { +void L64XX::setMicroSteps(int16_t microSteps) { uint8_t stepVal; for (stepVal = 0; stepVal < 8; stepVal++) { if (microSteps == 1) break; @@ -170,19 +158,17 @@ void L6470::setMicroSteps(int16_t microSteps) { // microstepping and goes to full stepping. FSCalc() converts a value in steps/s // to a value suitable for this register; to disable full-step switching, you // can pass 0x3FF to this register. -void L6470::setThresholdSpeed(const float thresholdSpeed) { +void L64XX::setThresholdSpeed(const float thresholdSpeed) { SetParam(L6470_FS_SPD, thresholdSpeed ? FSCalc(thresholdSpeed) : 0x3FF); } -void L6470::setCurrent(const int16_t current) { (void)(current); } - // Configure the L6470_MAX_SPEED register- this is the maximum number of (micro)steps per // second allowed. You'll want to mess around with your desired application to see // how far you can push it before the motor starts to slip. The ACTUAL parameter // passed to this function is in steps/tick; MaxSpdCalc() will convert a number of // steps/s into an appropriate value for this function. Note that for any move or // goto type function where no speed is specified, this value will be used. -void L6470::setMaxSpeed(const int16_t speed) { SetParam(L6470_MAX_SPEED, MaxSpdCalc(speed)); } +void L64XX::setMaxSpeed(const int16_t speed) { SetParam(L6470_MAX_SPEED, MaxSpdCalc(speed)); } // Configure the L6470_MAX_SPEED register- this is the maximum number of (micro)steps per // second allowed. You'll want to mess around with your desired application to see @@ -190,7 +176,7 @@ void L6470::setMaxSpeed(const int16_t speed) { SetParam(L6470_MAX_SPEED, MaxSpdC // passed to this function is in steps/tick; MaxSpdCalc() will convert a number of // steps/s into an appropriate value for this function. Note that for any move or // goto type function where no speed is specified, this value will be used. -void L6470::setMinSpeed(const int16_t speed) { SetParam(L6470_MIN_SPEED, MinSpdCalc(speed)); } +void L64XX::setMinSpeed(const int16_t speed) { SetParam(L6470_MIN_SPEED, MinSpdCalc(speed)); } // Configure the acceleration rate, in steps/tick/tick. There is also a L6470_DEC register; // both of them have a function (AccCalc() and DecCalc() respectively) that convert @@ -198,11 +184,11 @@ void L6470::setMinSpeed(const int16_t speed) { SetParam(L6470_MIN_SPEED, MinSpdC // sets the acceleration and deceleration to 'infinite' (or as near as the driver can // manage). If L6470_ACC is set to 0xFFF, L6470_DEC is ignored. To get infinite deceleration // without infinite acceleration, only hard stop will work. -void L6470::setAcc(const float acceleration) { SetParam(L6470_ACC, AccCalc(acceleration)); } +void L64XX::setAcc(const float acceleration) { SetParam(L6470_ACC, AccCalc(acceleration)); } -void L6470::setDec(const float deceleration) { SetParam(L6470_DEC, DecCalc(deceleration)); } +void L64XX::setDec(const float deceleration) { SetParam(L6470_DEC, DecCalc(deceleration)); } -long L6470::getPos() { return convert(GetParam(L6470_ABS_POS)); } +long L64XX::getPos() { return convert(GetParam(L6470_ABS_POS)); } // L6470_SPEED // The L6470_SPEED register contains the current motor speed, expressed in step/tick (format unsigned fixed point 0.28). @@ -211,21 +197,23 @@ long L6470::getPos() { return convert(GetParam(L6470_ABS_POS)); } // where L6470_SPEED is the integer number stored into the register and tick is 250 ns. // The available range is from 0 to 15625 step/s with a resolution of 0.015 step/s. // Note: The range effectively available to the user is limited by the L6470_MAX_SPEED parameter. -float L6470::getSpeed() { +float L64XX::getSpeed() { return (float)GetParam(L6470_SPEED); //return (float) speed * pow(8, -22); //return FSCalc(speed); NEEDS FIX } // Configure the overcurrent detection threshold. -void L6470::setOverCurrent(const uint16_t ma_current) { - const uint8_t OCValue = (uint8_t)floor(ma_current / 375 - 1); - SetParam(L6470_OCD_TH, OCValue < 0x0F ? OCValue : 0x0F); +void L64XX::setOverCurrent(float ma_current) { + if (ma_current > OCD_CURRENT_CONSTANT_INV * (OCD_TH_MAX +1)) ma_current = OCD_CURRENT_CONSTANT_INV * (OCD_TH_MAX +1); // keep the OCD_TH calc from overflowing 8 bits + const uint8_t OCValue = (uint8_t)floor(uint8_t(ma_current * OCD_CURRENT_CONSTANT - 1)); + SetParam(L6470_OCD_TH, OCValue < OCD_TH_MAX ? OCValue : OCD_TH_MAX); } -void L6470::setStallCurrent(const float ma_current) { - const uint8_t STHValue = (uint8_t)floor(ma_current / 31.25 - 1); - SetParam(L6470_STALL_TH, STHValue < 0x80 ? STHValue : 0x7F); +void L64XX::setStallCurrent(float ma_current) { + if (ma_current > STALL_CURRENT_CONSTANT_INV * (STALL_TH_MAX +1)) ma_current = STALL_CURRENT_CONSTANT_INV * (STALL_TH_MAX +1); // keep the STALL_TH calc from overflowing 8 bits + const uint8_t STHValue = (uint8_t)floor(uint8_t(ma_current * STALL_CURRENT_CONSTANT - 1)); + SetParam(L6470_STALL_TH, STHValue < STALL_TH_MAX ? STHValue : STALL_TH_MAX); } // Enable or disable the low-speed optimization option. If enabling, @@ -233,7 +221,7 @@ void L6470::setStallCurrent(const float ma_current) { // When disabling, the value will have to be explicitly written by // the user with a SetParam() call. See the datasheet for further // information about low-speed optimization. -void L6470::SetLowSpeedOpt(const boolean enable) { +void L64XX::SetLowSpeedOpt(const boolean enable) { Xfer(dSPIN_SET_PARAM | L6470_MIN_SPEED); Param(enable ? 0x1000 : 0, 13); } @@ -244,7 +232,7 @@ void L6470::SetLowSpeedOpt(const boolean enable) { // will switch the device into full-step mode. // The SpdCalc() function is provided to convert steps/s values into // appropriate integer values for this function. -void L6470::run(const uint8_t dir, const float spd) { +void L64XX::run(const uint8_t dir, const float spd) { uint32_t speedVal = SpdCalc(spd); Xfer(dSPIN_RUN | dir); if (speedVal > 0xFFFFF) speedVal = 0xFFFFF; @@ -258,7 +246,7 @@ void L6470::run(const uint8_t dir, const float spd) { // the direction (set by the dSPIN_FWD and dSPIN_REV constants) imposed by the call // of this function. Motion commands (dSPIN_RUN, dSPIN_MOVE, etc) will cause the device // to exit step clocking mode. -void L6470::Step_Clock(const uint8_t dir) { +void L64XX::Step_Clock(const uint8_t dir) { Xfer(dSPIN_STEP_CLOCK | dir); } @@ -266,7 +254,7 @@ void L6470::Step_Clock(const uint8_t dir) { // direction imposed by dir (dSPIN_FWD or dSPIN_REV constants may be used). The motor // will accelerate according the acceleration and deceleration curves, and // will run at L6470_MAX_SPEED. Stepping mode will adhere to L6470_FS_SPD value, as well. -void L6470::move(const long n_step) { +void L64XX::move(const long n_step) { const uint8_t dir = n_step >= 0 ? dSPIN_FWD : dSPIN_REV; Xfer(dSPIN_MOVE | dir); // Set direction long n_stepABS = abs(n_step); @@ -279,7 +267,7 @@ void L6470::move(const long n_step) { // dSPIN_GOTO operates much like dSPIN_MOVE, except it produces absolute motion instead // of relative motion. The motor will be moved to the indicated position // in the shortest possible fashion. -void L6470::goTo(long pos) { +void L64XX::goTo(long pos) { Xfer(dSPIN_GOTO); if (pos > 0x3FFFFF) pos = 0x3FFFFF; Xfer(uint8_t(pos >> 16)); @@ -288,7 +276,7 @@ void L6470::goTo(long pos) { } // Same as dSPIN_GOTO, but with user constrained rotational direction. -void L6470::goTo_DIR(const uint8_t dir, long pos) { +void L64XX::goTo_DIR(const uint8_t dir, long pos) { Xfer(dSPIN_GOTO_DIR | dir); if (pos > 0x3FFFFF) pos = 0x3FFFFF; Xfer(uint8_t(pos >> 16)); @@ -302,7 +290,7 @@ void L6470::goTo_DIR(const uint8_t dir, long pos) { // performed at the falling edge, and depending on the value of // act (either RESET or COPY) the value in the L6470_ABS_POS register is // either RESET to 0 or COPY-ed into the L6470_MARK register. -void L6470::goUntil(const uint8_t act, const uint8_t dir, uint32_t spd) { +void L64XX::goUntil(const uint8_t act, const uint8_t dir, uint32_t spd) { Xfer(dSPIN_GO_UNTIL | act | dir); if (spd > 0x3FFFFF) spd = 0x3FFFFF; Xfer(uint8_t(spd >> 16)); @@ -317,21 +305,21 @@ void L6470::goUntil(const uint8_t act, const uint8_t dir, uint32_t spd) { // and the L6470_ABS_POS register is either COPY-ed into L6470_MARK or RESET to // 0, depending on whether RESET or COPY was passed to the function // for act. -void L6470::releaseSW(const uint8_t act, const uint8_t dir) { +void L64XX::releaseSW(const uint8_t act, const uint8_t dir) { Xfer(dSPIN_RELEASE_SW | act | dir); } // GoHome is equivalent to GoTo(0), but requires less time to send. // Note that no direction is provided; motion occurs through shortest // path. If a direction is required, use GoTo_DIR(). -void L6470::goHome() { Xfer(dSPIN_GO_HOME); } +void L64XX::goHome() { Xfer(dSPIN_GO_HOME); } // GoMark is equivalent to GoTo(L6470_MARK), but requires less time to send. // Note that no direction is provided; motion occurs through shortest // path. If a direction is required, use GoTo_DIR(). -void L6470::goMark() { Xfer(dSPIN_GO_MARK); } +void L64XX::goMark() { Xfer(dSPIN_GO_MARK); } -void L6470::setMark(long value) { +void L64XX::setMark(long value) { Xfer(L6470_MARK); if (value > 0x3FFFFF) value = 0x3FFFFF; if (value < -0x3FFFFF) value = -0x3FFFFF; @@ -340,7 +328,7 @@ void L6470::setMark(long value) { Xfer(uint8_t(value)); } -void L6470::setMark() { +void L64XX::setMark() { long value = getPos(); Xfer(L6470_MARK); if (value > 0x3FFFFF) value = 0x3FFFFF; @@ -352,28 +340,28 @@ void L6470::setMark() { // Sets the L6470_ABS_POS register to 0, effectively declaring the current // position to be "HOME". -void L6470::setAsHome() { Xfer(dSPIN_RESET_POS); } +void L64XX::setAsHome() { Xfer(dSPIN_RESET_POS); } // Reset device to power up conditions. Equivalent to toggling the STBY // pin or cycling power. -void L6470::resetDev() { Xfer(dSPIN_RESET_DEVICE); } +void L64XX::resetDev() { Xfer(dSPIN_RESET_DEVICE); } // Bring the motor to a halt using the deceleration curve. -void L6470::softStop() { Xfer(dSPIN_SOFT_STOP); } +void L64XX::softStop() { Xfer(dSPIN_SOFT_STOP); } // Stop the motor right away. No deceleration. -void L6470::hardStop() { Xfer(dSPIN_HARD_STOP); } +void L64XX::hardStop() { Xfer(dSPIN_HARD_STOP); } // Decelerate the motor and disengage -void L6470::softFree() { Xfer(dSPIN_SOFT_HIZ); } +void L64XX::softFree() { Xfer(dSPIN_SOFT_HIZ); } // Disengage the motor immediately with no deceleration. -void L6470::free() { Xfer(dSPIN_HARD_HIZ); } +void L64XX::free() { Xfer(dSPIN_HARD_HIZ); } // Fetch and return the 16-bit value in the L6470_STATUS register. Resets // any warning flags and exits any error states. Using GetParam() // to read L6470_STATUS does not clear these values. -int16_t L6470::getStatus() { +int16_t L64XX::getStatus() { Xfer(dSPIN_GET_STATUS); return Xfer(0) << 8 | Xfer(0); } @@ -382,19 +370,19 @@ int16_t L6470::getStatus() { // 250ns (datasheet value)- 0x08A on boot. // Multiply desired steps/s/s by .137438 to get an appropriate value for this register. // This is a 12-bit value, so we need to make sure the value is at or below 0xFFF. -uint32_t L6470::AccCalc(const float stepsPerSecPerSec) { +uint32_t L64XX::AccCalc(const float stepsPerSecPerSec) { uint32_t temp = (uint32_t)(stepsPerSecPerSec * 0.137438); return temp < 0x00000FFF ? temp : 0x00000FFF; } -uint32_t L6470::DecCalc(float stepsPerSecPerSec) { +uint32_t L64XX::DecCalc(float stepsPerSecPerSec) { // The calculation for L6470_DEC is the same as for L6470_ACC. Value is 0x08A on boot. // This is a 12-bit value, so we need to make sure the value is at or below 0xFFF. uint32_t temp = (uint32_t)(stepsPerSecPerSec * 0.137438); return temp < 0x00000FFF ? temp : 0x00000FFF; } -uint32_t L6470::MaxSpdCalc(const float stepsPerSec) { +uint32_t L64XX::MaxSpdCalc(const float stepsPerSec) { // The value in the MAX_SPD register is [(steps/s)*(tick)]/(2^-18) where tick is // 250ns (datasheet value)- 0x041 on boot. // Multiply desired steps/s by .065536 to get an appropriate value for this register @@ -403,7 +391,7 @@ uint32_t L6470::MaxSpdCalc(const float stepsPerSec) { return temp < 0x000003FF ? temp : 0x000003FF; } -uint32_t L6470::MinSpdCalc(const float stepsPerSec) { +uint32_t L64XX::MinSpdCalc(const float stepsPerSec) { // The value in the MIN_SPD register is [(steps/s)*(tick)]/(2^-24) where tick is // 250ns (datasheet value)- 0x000 on boot. // Multiply desired steps/s by 4.1943 to get an appropriate value for this register @@ -412,7 +400,7 @@ uint32_t L6470::MinSpdCalc(const float stepsPerSec) { return temp < 0x00000FFF ? temp : 0x00000FFF; } -uint32_t L6470::FSCalc(const float stepsPerSec) { +uint32_t L64XX::FSCalc(const float stepsPerSec) { // The value in the L6470_FS_SPD register is ([(steps/s)*(tick)]/(2^-18))-0.5 where tick is // 250ns (datasheet value)- 0x027 on boot. // Multiply desired steps/s by .065536 and subtract .5 to get an appropriate value for this register @@ -421,7 +409,7 @@ uint32_t L6470::FSCalc(const float stepsPerSec) { return temp < 0x000003FF ? temp : 0x000003FF; } -uint32_t L6470::IntSpdCalc(const float stepsPerSec) { +uint32_t L64XX::IntSpdCalc(const float stepsPerSec) { // The value in the L6470_INT_SPD register is [(steps/s)*(tick)]/(2^-24) where tick is // 250ns (datasheet value)- 0x408 on boot. // Multiply desired steps/s by 4.1943 to get an appropriate value for this register @@ -430,7 +418,7 @@ uint32_t L6470::IntSpdCalc(const float stepsPerSec) { return temp < 0x00003FFF ? temp : 0x00003FFF; } -uint32_t L6470::SpdCalc(const float stepsPerSec) { +uint32_t L64XX::SpdCalc(const float stepsPerSec) { // When issuing dSPIN_RUN command, the 20-bit speed is [(steps/s)*(tick)]/(2^-28) where tick is // 250ns (datasheet value). // Multiply desired steps/s by 67.106 to get an appropriate value for this register @@ -439,7 +427,7 @@ uint32_t L6470::SpdCalc(const float stepsPerSec) { return temp < 0x000FFFFF ? temp : 0x000FFFFF; } -uint32_t L6470::Param(uint32_t value, const uint8_t bit_len) { +uint32_t L64XX::Param(uint32_t value, const uint8_t bit_len) { // Generalization of the subsections of the register read/write functionality. // We want the end user to just write the value without worrying about length, // so we pass a bit length parameter from the calling function. @@ -449,7 +437,7 @@ uint32_t L6470::Param(uint32_t value, const uint8_t bit_len) { if (value > mask) value = mask; // The following three if statements handle the various possible uint8_t length // transfers- it'll be no less than 1 but no more than 3 uint8_ts of data. - // L6470::Xfer() sends a uint8_t out through SPI and returns a uint8_t received + // L64XX::Xfer() sends a uint8_t out through SPI and returns a uint8_t received // over SPI- when calling it, we typecast a shifted version of the masked // value, then we shift the received value back by the same amount and // store it until return time. @@ -468,7 +456,7 @@ uint32_t L6470::Param(uint32_t value, const uint8_t bit_len) { return ret_val & mask; } -uint8_t L6470::Xfer(uint8_t data) { +uint8_t L64XX::Xfer(uint8_t data) { if (pin_SCK < 0) { // External SPI return (uint8_t)( @@ -499,8 +487,8 @@ uint8_t L6470::Xfer(uint8_t data) { uint8_t out_data = 0; uint8_t return_data = 0; digitalWrite(pin_SS, LOW); - for (uint8_t i = chain[0]; i >= 1; i--) { - out_data = (chain[i] == position ? data : CMD_NOP); + for (uint8_t i = L64XX_chain[0]; i >= 1; i--) { + out_data = (L64XX_chain[i] == position ? data : CMD_NOP); uint8_t bits = 8; do { digitalWrite(pin_SCK, LOW); @@ -511,7 +499,7 @@ uint8_t L6470::Xfer(uint8_t data) { out_data |= (digitalRead(pin_MISO) != 0); } while (--bits); delay(0); // 10 cycles @ 84mhz - if (chain[i] == position) return_data = out_data; + if (L64XX_chain[i] == position) return_data = out_data; } digitalWrite(pin_SS, HIGH); return return_data; @@ -519,18 +507,18 @@ uint8_t L6470::Xfer(uint8_t data) { return 0; // never executed but prevents a compiler warning } -void L6470::SetParam(const uint8_t param, const uint32_t value) { +void L64XX::SetParam(const uint8_t param, const uint32_t value) { Xfer(dSPIN_SET_PARAM | param); ParamHandler(param, value); } // Read from the various registers in the dSPIN chip. -uint32_t L6470::GetParam(const uint8_t param) { +uint32_t L64XX::GetParam(const uint8_t param) { Xfer(dSPIN_GET_PARAM | param); return ParamHandler(param, 0); } -long L6470::convert(uint32_t val) { +long L64XX::convert(uint32_t val) { // Convert 22bit 2s comp to signed long const int16_t MSB = val >> 21; val >>= 11; @@ -546,13 +534,20 @@ long L6470::convert(uint32_t val) { // ParamHandler() does not list the powerSTEP01 current mode registers because // these registers are the same length as their voltage mode counterparts. -uint32_t L6470::ParamHandler(const uint8_t param, const uint32_t value) { +uint32_t L64XX::ParamHandler(const uint8_t param, const uint32_t value) { // This switch structure handles the appropriate action for each register. // This is necessary since not all registers are of the same length, either // bit-wise or uint8_t-wise, so we want to make sure we mask out any spurious // bits and do the right number of transfers. That is handled by the dSPIN_Param() // function, in most cases, but for 1-uint8_t or smaller transfers, we call // Xfer() directly. + + // These defines help handle the register address differences between L6470, L6480 & powerSTEP01 + #define L6470_CONFIG_A 0x18 // L6470: L6470_CONFIG, L6480 & powerSTEP01: L6470_GATECFG1 + #define L6470_STATUS_A 0x19 // L6470: L6470_STATUS, L6480 & powerSTEP01: L6470_GATECFG2 + #define L6470_CONFIG_B 0x1A // L6480 & powerSTEP01: L6470_CONFIG + #define L6470_STATUS_B 0x1B // L6480 & powerSTEP01: L6470_STATUS + switch (param) { // L6470_ABS_POS is the current absolute offset from home. It is a 22 bit number expressed // in two's complement. At power up, this value is 0. It cannot be written when @@ -632,11 +627,10 @@ uint32_t L6470::ParamHandler(const uint8_t param, const uint32_t value) { // Set the overcurrent threshold. Ranges from 375mA to 6A in steps of 375mA. // A set of defined constants is provided for the user's convenience. Default // value is 3.375A- 0x08. This is a 4-bit value. - case L6470_OCD_TH: return Xfer(uint8_t(value) & 0x0F); - + case L6470_OCD_TH: return Xfer(uint8_t(value) & OCD_TH_MAX); // Stall current threshold. Defaults to 0x40, or 2.03A. Value is from 31.25mA to // 4A in 31.25mA steps. This is a 7-bit value. - case L6470_STALL_TH: return Xfer(uint8_t(value) & 0x7F); + case L6470_STALL_TH: return Xfer(uint8_t(value) & STALL_TH_MAX); // L6470_STEP_MODE controls the microstepping settings, as well as the generation of an // output signal from the dSPIN. Bits 2:0 control the number of microsteps per @@ -653,18 +647,27 @@ uint32_t L6470::ParamHandler(const uint8_t param, const uint32_t value) { // FLAG pin. case L6470_ALARM_EN: return Xfer(uint8_t(value)); + // GATECFG1 & GATECFG2 - L6480 and powerSTEP01 only, these registers control the slew rate and off time of the + // bridge power FETs. + // L6470_CONFIG contains some assorted configuration bits and fields. A fairly comprehensive // set of reasonably self-explanatory constants is provided, but users should refer // to the datasheet before modifying the contents of this register to be certain they // understand the implications of their modifications. Value on boot is 0x2E88; this // can be a useful way to verify proper start up and operation of the dSPIN chip. - case L6470_CONFIG: return Param(value, 16); + case L6470_CONFIG_A: if (L6470_status_layout) return Param(value, 16); // L6470 CONFIG register + else return Param(uint16_t(value & 0x07ff), 11); // L6480 & powerSTEP01 GATECFG1 register + case L6470_CONFIG_B: if (L6470_status_layout) return Param(value, 16); // L6480 & powerSTEP01 CONFIG register + // L6470_STATUS contains read-only information about the current condition of the chip. A // comprehensive set of constants for masking and testing this register is provided, but // users should refer to the datasheet to ensure that they fully understand each one of // the bits in the register. - case L6470_STATUS: return Param(0, 16); // L6470_STATUS is a read-only register + case L6470_STATUS_A: if (L6470_status_layout) return Param(value, 16); // L46470 STATUS register + else return Xfer(uint8_t(value)); // L6480 & powerSTEP01 GATECFG2 register + case L6470_STATUS_B: return Param(0, 16); // L6480 & powerSTEP01 STATUS register + } return Xfer(uint8_t(value)); } diff --git a/src/L6470.h b/src/L6470.h index f342854..5018b38 100644 --- a/src/L6470.h +++ b/src/L6470.h @@ -12,9 +12,23 @@ #ifndef _L6470_H_ #define _L6470_H_ +/** + * This library is aimed at the L6470 but it also can be used + * for other L647x devices, L648x devices and the powerSTEP01. + * + * Page numbers are for the L6470 data sheet. + */ + #include -#define L6470_LIBRARY_VERSION 0x000700 +#define L6470_LIBRARY_VERSION 0x000800 + +extern uint8_t L64XX_chain[21]; + // [0] - number of drivers in chain + // [1]... axis index for first device in the chain (closest to MOSI) + +uint8_t L6470_transfer(uint8_t data, int16_t ss_pin); // user supplied external SPI routine for single device system +uint8_t L6470_transfer(uint8_t data, int16_t ss_pin, uint8_t axis); // user supplied external SPI routine for chain system //#define SCK 10 // Wire this to the CSN pin //#define MOSI 11 // Wire this to the SDI pin @@ -27,7 +41,7 @@ #define STAT2 15 // Hooked to an LED on the test jig #define SWITCH 8 // Hooked to the switch input and a pB on the jig -// Constant definitions for overcurrent thresholds. Write these values to +// Constant definitions for L6470 overcurrent thresholds. Write these values to // register dSPIN_OCD_TH to set the level at which an overcurrent even occurs. #define OCD_TH_375mA 0x00 #define OCD_TH_750mA 0x01 @@ -115,24 +129,43 @@ #define CONFIG_SW_MODE 0x0010 // Mask for this bit. #define CONFIG_SW_HARD_STOP 0x0000 // Default; hard stop motor on switch. #define CONFIG_SW_USER 0x0010 // Tie to the GoUntil and ReleaseSW - // commands to provide jog function. - // See page 25 of datasheet. + // commands to provide jog function. + // See page 25 of datasheet. // Configure the motor voltage compensation mode (see page 34 of datasheet) -#define CONFIG_EN_VSCOMP 0x0020 // Mask for this bit. -#define CONFIG_VS_COMP_DISABLE 0x0000 // Disable motor voltage compensation. -#define CONFIG_VS_COMP_ENABLE 0x0020 // Enable motor voltage compensation. +#define CONFIG_EN_VSCOMP 0x0020 // Mask for this bit. +#define CONFIG_VS_COMP_DISABLE 0x0000 // Disable motor voltage compensation. +#define CONFIG_VS_COMP_ENABLE 0x0020 // Enable motor voltage compensation. // Configure overcurrent detection event handling -#define CONFIG_OC_SD 0x0080 // Mask for this bit. -#define CONFIG_OC_SD_DISABLE 0x0000 // Bridges do NOT shutdown on OC detect -#define CONFIG_OC_SD_ENABLE 0x0080 // Bridges shutdown on OC detect +#define CONFIG_OC_SD 0x0080 // Mask for this bit. +#define CONFIG_OC_SD_DISABLE 0x0000 // Bridges do NOT shutdown on OC detect +#define CONFIG_OC_SD_ENABLE 0x0080 // Bridges shutdown on OC detect // Configure the slew rate of the power bridge output -#define CONFIG_POW_SR 0x0300 // Mask for this bit field. -#define CONFIG_SR_180V_us 0x0000 // 180V/us -#define CONFIG_SR_290V_us 0x0200 // 290V/us -#define CONFIG_SR_530V_us 0x0300 // 530V/us +// L6470 +#define CONFIG_POW_SR 0x0300 // Mask for this bit field. +#define CONFIG_POW_SR_BIT 8 // starting bit of this field +#define CONFIG_SR_320V_us 0x0000 // 320V/us +#define CONFIG_SR_75V_us 0x0100 // 75V/us +#define CONFIG_SR_110V_us 0x0200 // 110V/us +#define CONFIG_SR_260V_us 0x0300 // 260V/us + +// L6480 & powerSTEP01 +#define CONFIG1_SR 0x00FF // Mask for this bit field. +#define CONFIG1_SR_220V_us 0x006C +#define CONFIG1_SR_400V_us 0x0087 +#define CONFIG1_SR_520V_us 0x00A6 +#define CONFIG1_SR_980V_us 0x00E2 +#define CONFIG2_SR_220V_us 0x10 +#define CONFIG2_SR_400V_us 0x10 +#define CONFIG2_SR_520V_us 0x10 +#define CONFIG2_SR_980V_us 0x30 + + +// L6480 & powerSTEP01 VCC setting +#define PWR_VCC_7_5V 0 +#define PWR_VCC_15V 0x0100 // Integer divisors for PWM sinewave generation // See page 32 of the datasheet for more information on this. @@ -162,18 +195,10 @@ #define STATUS_BUSY 0x0002 // mirrors BUSY pin #define STATUS_SW_F 0x0004 // low when switch open, high when closed #define STATUS_SW_EVN 0x0008 // active high, set on switch falling edge, - // cleared by reading L6470_STATUS + // cleared by reading L6470_STATUS #define STATUS_DIR 0x0010 // Indicates current motor direction. - // High is dSPIN_FWD, Low is dSPIN_REV. -#define STATUS_NOTPERF_CMD 0x0080 // Last command not performed. -#define STATUS_WRONG_CMD 0x0100 // Last command not valid. -#define STATUS_UVLO 0x0200 // Undervoltage lockout is active -#define STATUS_TH_WRN 0x0400 // Thermal warning -#define STATUS_TH_SD 0x0800 // Thermal shutdown -#define STATUS_OCD 0x1000 // Overcurrent detected -#define STATUS_STEP_LOSS_A 0x2000 // Stall detected on A bridge -#define STATUS_STEP_LOSS_B 0x4000 // Stall detected on B bridge -#define STATUS_SCK_MOD 0x8000 // Step clock mode is active + // High is dSPIN_FWD, Low is dSPIN_REV. + // Status register motor status field #define STATUS_MOT_STATUS 0x0060 // field mask @@ -207,8 +232,17 @@ #define L6470_STALL_TH 0x14 #define L6470_STEP_MODE 0x16 #define L6470_ALARM_EN 0x17 -#define L6470_CONFIG 0x18 -#define L6470_STATUS 0x19 +#define L6470_GATECFG1 0x18 // L6480 & powerSTEP01 only +#define L6470_GATECFG2 0x19 // L6480 & powerSTEP01 only + +#define PWR_TVAL_HOLD 0X09 // powerSTEP01 current mode register names +#define PWR_TVAL_RUN 0X0A +#define PWR_TVAL_ACC 0X0B +#define PWR_TVAL_DEC 0X0C +#define PWR_T_FAST 0X0E +#define PWR_TON_MIN 0X0F +#define PWR_TOFF_MIN 0X10 + // dSPIN commands #define dSPIN_NOP 0x00 @@ -239,23 +273,19 @@ #define dSPIN_ACTION_RESET 0x00 #define dSPIN_ACTION_COPY 0x01 -class L6470 { +class L64XX { public: - - static uint8_t chain[21]; // 0 - number of drivers in chain, 1... axis index for first device in the chain (closest to MOSI) - - L6470(const int16_t pin_SS); + L64XX(); void init(); void spi_init(); void setMicroSteps(int16_t microSteps); - void setCurrent(const int16_t current); // NOT IMPLEMENTED void setMaxSpeed(const int16_t speed); void setMinSpeed(const int16_t speed); void setAcc(const float acceleration); void setDec(const float deceleration); - void setOverCurrent(uint16_t ma_current); + void setOverCurrent(float ma_current); void setThresholdSpeed(const float threshold); void setStallCurrent(float ma_current); @@ -298,8 +328,17 @@ class L6470 { void set_pins(const int16_t SCK, const int16_t MOSI, const int16_t MISO, const int16_t RESET, const int16_t BUSYN); -private: + int16_t pin_SS = -1, + pin_SCK = -1, + pin_MOSI = -1, + pin_MISO = -1, + pin_RESET = -1, + pin_BUSYN = -1; + + uint8_t axis_index; + uint8_t position = 0; // 0 - not part of a chain +private: long convert(uint32_t val); uint32_t AccCalc(const float stepsPerSecPerSec); @@ -312,16 +351,94 @@ class L6470 { uint32_t Param(uint32_t value, const uint8_t bit_len); uint8_t Xfer(uint8_t data); uint8_t Xfer(uint8_t data, int16_t ss_pin, uint8_t position); +}; - int16_t pin_SS = -1, - pin_SCK = -1, - pin_MOSI = -1, - pin_MISO = -1, - pin_RESET = -1, - pin_BUSYN = -1; +class L6470 : public L64XX { +public: + L6470(const int16_t pin_SS); - uint8_t axis_index; - uint8_t position = 0; // 0 - not part of a chain + static constexpr uint8_t OCD_TH_MAX = 15; + static constexpr uint8_t STALL_TH_MAX = 127; + static constexpr float OCD_CURRENT_CONSTANT_INV = 375; // mA per count + static constexpr float OCD_CURRENT_CONSTANT = 1.0f / OCD_CURRENT_CONSTANT_INV; // counts per mA + static constexpr float STALL_CURRENT_CONSTANT_INV = 31.25; // mA per count + static constexpr float STALL_CURRENT_CONSTANT = 1.0f / STALL_CURRENT_CONSTANT_INV; // counts per mA + + static constexpr uint8_t L6470_CONFIG = 0x18; + static constexpr uint8_t L6470_STATUS = 0x19; + + static constexpr bool L6470_status_layout = true; + static constexpr uint16_t STATUS_NOTPERF_CMD = 0x0080; // Last command not performed. + static constexpr uint16_t STATUS_WRONG_CMD = 0x0100; // Last command not valid. + static constexpr uint16_t STATUS_CMD_ERR = 0x0180; // Command error + static constexpr uint16_t STATUS_UVLO = 0x0200; // Undervoltage lockout is active + static constexpr uint16_t STATUS_TH_WRN = 0x0400; // Thermal warning + static constexpr uint16_t STATUS_TH_SD = 0x0800; // Thermal shutdown + static constexpr uint16_t STATUS_OCD = 0x1000; // Overcurrent detected + static constexpr uint16_t STATUS_STEP_LOSS_A = 0x2000; // Stall detected on A bridge + static constexpr uint16_t STATUS_STEP_LOSS_B = 0x4000; // Stall detected on B bridge + static constexpr uint16_t STATUS_SCK_MOD = 0x8000; // Step clock mode is active +}; + +class L6480 : public L64XX { +public: + L6480(const int16_t pin_SS); + + static constexpr uint8_t OCD_TH_MAX = 31; + static constexpr uint8_t STALL_TH_MAX = 31; + static constexpr float OCD_CURRENT_CONSTANT_INV = 31.25; // mA per count + static constexpr float OCD_CURRENT_CONSTANT = 1.0f / OCD_CURRENT_CONSTANT_INV; // counts per mA + static constexpr float STALL_CURRENT_CONSTANT_INV = 31.25; // mA per count + static constexpr float STALL_CURRENT_CONSTANT = 1.0f / STALL_CURRENT_CONSTANT_INV; // counts per mA + + static constexpr uint8_t L6470_CONFIG = 0x1A; + static constexpr uint8_t L6470_STATUS = 0x1B; + + static constexpr bool L6470_status_layout = false; + static constexpr uint16_t STATUS_NOTPERF_CMD = 0x0080; // Last command not performed. + static constexpr uint16_t STATUS_WRONG_CMD = 0x0080; // Last command not valid. + static constexpr uint16_t STATUS_CMD_ERR = 0x0080; // Command error + static constexpr uint16_t STATUS_UVLO = 0x0200; // Undervoltage lockout is active + static constexpr uint16_t UVLO_ADC = 0x0400; // ADC undervoltage event + static constexpr uint16_t STATUS_TH_WRN = 0x0800; // Thermal warning + static constexpr uint16_t STATUS_TH_SD = 0x1000; // Thermal shutdown + static constexpr uint16_t STATUS_OCD = 0x2000; // Overcurrent detected + static constexpr uint16_t STATUS_STEP_LOSS_A = 0x4000; // Stall detected on A bridge + static constexpr uint16_t STATUS_STEP_LOSS_B = 0x8000; // Stall detected on B bridge + static constexpr uint16_t STATUS_SCK_MOD = 0x0100; // Step clock mode is active +}; + +class powerSTEP01 : public L64XX { +public: + powerSTEP01(const int16_t pin_SS); + + static constexpr uint8_t OCD_TH_MAX = 31; + static constexpr uint8_t STALL_TH_MAX = 31; + static constexpr float OCD_CURRENT_CONSTANT = 0.001; // counts per mA (empirically derived for powerSTEP01) + static constexpr float OCD_CURRENT_CONSTANT_INV = 1000; // mA per count (empirically derived for powerSTEP01) + static constexpr float STALL_CURRENT_CONSTANT = 0.005; // counts per mA (empirically derived for powerSTEP01) + static constexpr float STALL_CURRENT_CONSTANT_INV = 200; // mA per count (empirically derived for powerSTEP01) + //static constexpr float POWERSTEP_AVERAGE_RDS = 0.016; // Ohms - L648x use external FETs so this may be user modified + //static constexpr float OCD_CURRENT_CONSTANT = (POWERSTEP_AVERAGE_RDS/0.03125)/1000; // counts per mA (calc per data sheet - definitely wrong) + //static constexpr float OCD_CURRENT_CONSTANT_INV = (1000 * 0.03125)/(POWERSTEP_AVERAGE_RDS); // mA per count (calc per data sheet - definitely wrong) + //static constexpr float STALL_CURRENT_CONSTANT = OCD_CURRENT_CONSTANT; // counts per mA (calc per data sheet - definitely wrong) + //static constexpr float STALL_CURRENT_CONSTANT_INV = OCD_CURRENT_CONSTANT_INV; // mA per count (calc per data sheet - definitely wrong) + + static constexpr uint8_t L6470_CONFIG = 0x1A; + static constexpr uint8_t L6470_STATUS = 0x1B; + + static constexpr bool L6470_status_layout = false; + static constexpr uint16_t STATUS_NOTPERF_CMD = 0x0080; // Last command not performed. + static constexpr uint16_t STATUS_WRONG_CMD = 0x0080; // Last command not valid. + static constexpr uint16_t STATUS_CMD_ERR = 0x0080; // Command error + static constexpr uint16_t STATUS_UVLO = 0x0200; // Undervoltage lockout is active + static constexpr uint16_t UVLO_ADC = 0x0400; // ADC undervoltage event + static constexpr uint16_t STATUS_TH_WRN = 0x0800; // Thermal warning + static constexpr uint16_t STATUS_TH_SD = 0x1000; // Thermal shutdown + static constexpr uint16_t STATUS_OCD = 0x2000; // Overcurrent detected + static constexpr uint16_t STATUS_STEP_LOSS_A = 0x4000; // Stall detected on A bridge + static constexpr uint16_t STATUS_STEP_LOSS_B = 0x8000; // Stall detected on B bridge + static constexpr uint16_t STATUS_SCK_MOD = 0x0100; // Step clock mode is active }; #endif // _L6470_H_ From d32191fc345d5accd58bf7c255ada6d2b06fe3d3 Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Tue, 26 Feb 2019 18:31:51 -0600 Subject: [PATCH 04/14] Put 'chain' into the L64XX class --- src/L6470.cpp | 16 +++++++--------- src/L6470.h | 8 ++++---- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/L6470.cpp b/src/L6470.cpp index 2e448c7..a268790 100644 --- a/src/L6470.cpp +++ b/src/L6470.cpp @@ -13,9 +13,7 @@ #include -uint8_t L64XX_chain[21]; - // [0] - number of drivers in chain - // [1]... axis index for first device in the chain (closest to MOSI) +uint8_t L64XX::chain[21]; L64XX::L64XX() {} @@ -96,13 +94,13 @@ void L64XX::init() { // add to the chain array and save chain info for this stepper void L64XX::set_chain_info(const uint8_t axis, const uint8_t chain_position) { if (chain_position) { - L64XX_chain[0]++; - L64XX_chain[chain_position] = axis; + chain[0]++; + chain[chain_position] = axis; position = chain_position; axis_index = axis; } else - L64XX_chain[0] = 0; //reset array back to uninitialized + chain[0] = 0; //reset array back to uninitialized } // Sets optional pins for this stepper @@ -487,8 +485,8 @@ uint8_t L64XX::Xfer(uint8_t data) { uint8_t out_data = 0; uint8_t return_data = 0; digitalWrite(pin_SS, LOW); - for (uint8_t i = L64XX_chain[0]; i >= 1; i--) { - out_data = (L64XX_chain[i] == position ? data : CMD_NOP); + for (uint8_t i = chain[0]; i >= 1; i--) { + out_data = (chain[i] == position ? data : CMD_NOP); uint8_t bits = 8; do { digitalWrite(pin_SCK, LOW); @@ -499,7 +497,7 @@ uint8_t L64XX::Xfer(uint8_t data) { out_data |= (digitalRead(pin_MISO) != 0); } while (--bits); delay(0); // 10 cycles @ 84mhz - if (L64XX_chain[i] == position) return_data = out_data; + if (chain[i] == position) return_data = out_data; } digitalWrite(pin_SS, HIGH); return return_data; diff --git a/src/L6470.h b/src/L6470.h index 5018b38..cca723c 100644 --- a/src/L6470.h +++ b/src/L6470.h @@ -23,10 +23,6 @@ #define L6470_LIBRARY_VERSION 0x000800 -extern uint8_t L64XX_chain[21]; - // [0] - number of drivers in chain - // [1]... axis index for first device in the chain (closest to MOSI) - uint8_t L6470_transfer(uint8_t data, int16_t ss_pin); // user supplied external SPI routine for single device system uint8_t L6470_transfer(uint8_t data, int16_t ss_pin, uint8_t axis); // user supplied external SPI routine for chain system @@ -338,6 +334,10 @@ class L64XX { uint8_t axis_index; uint8_t position = 0; // 0 - not part of a chain + static uint8_t chain[21]; + // [0] - number of drivers in chain + // [1]... axis index for first device in the chain (closest to MOSI) + private: long convert(uint32_t val); From b4cd8257551ae521f337bd2accb0930ed76a6601 Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Tue, 26 Feb 2019 19:01:02 -0600 Subject: [PATCH 05/14] Move client methods into L64XX class --- .../two_motors_synchronized.ino | 10 ++-- src/L6470.cpp | 40 ++++++------- src/L6470.h | 59 +++++++++---------- 3 files changed, 55 insertions(+), 54 deletions(-) diff --git a/examples/two_motors_synchronized/two_motors_synchronized.ino b/examples/two_motors_synchronized/two_motors_synchronized.ino index 4f94ef7..c219f1e 100644 --- a/examples/two_motors_synchronized/two_motors_synchronized.ino +++ b/examples/two_motors_synchronized/two_motors_synchronized.ino @@ -59,10 +59,10 @@ uint8_t L6470_SpiTransfer_Mode_3(uint8_t b) { // using Mode 3 * * Note that the data for the last device in the chain is sent out first. * - * The library will automatically link to "uint8_t L6470_Transfer(uint8_t,int16_t,uint8_t)" + * The library will automatically link to "uint8_t L64XX::transfer(uint8_t,int16_t,uint8_t)" */ -uint8_t L6470_transfer(uint8_t data, int16_t ss_pin, uint8_t chain_position) { +uint8_t L64XX::transfer(uint8_t data, const int16_t ss_pin, const uint8_t chain_position) { #define CMD_NOP 0 uint8_t data_out = 0; data--; @@ -78,6 +78,8 @@ uint8_t L6470_transfer(uint8_t data, int16_t ss_pin, uint8_t chain_position) { return data_out; } +uint8_t L64XX::transfer(uint8_t data, const int16_t ss_pin) { } + /** * This is the routine that sends the motion commands. * @@ -101,10 +103,10 @@ void Buffer_Transfer(uint8_t buffer[] , uint8_t length) { /** * Initialize pins for non-library SPI software * - * The library will automatically link to "void L6470::spi_init()" + * The library will automatically link to "void L64XX::spi_init()" */ -void L6470::spi_init() { +void L64XX::spi_init() { pinMode(SS_PIN, OUTPUT); pinMode(SCK_PIN, OUTPUT); pinMode(MOSI_PIN, OUTPUT); diff --git a/src/L6470.cpp b/src/L6470.cpp index a268790..c2400fe 100644 --- a/src/L6470.cpp +++ b/src/L6470.cpp @@ -47,11 +47,11 @@ void L64XX::init() { // SPI clock not to exceed 5MHz, // SPI_MODE3 (clock idle high, latch data on rising edge of clock) if (pin_SCK < 0) spi_init(); // using external SPI to init it - // internal SPI already initialized + // internal SPI already initialized - // First things first: let's check communications. The L6470_CONFIG register should + // First things first: let's check communications. The L64XX_CONFIG register should // power up to 0x2E88, so we can use that to check the communications. - //Serial.println(GetParam(L6470_CONFIG) == 0x2E88 ? "good to go" : "Comm issue"); + //Serial.println(GetParam(L64XX_CONFIG) == 0x2E88 ? "good to go" : "Comm issue"); // First, let's set the step mode register: // - SYNC_EN controls whether the BUSY/SYNC pin reflects the step @@ -63,7 +63,7 @@ void L64XX::init() { // not using that pin. //SetParam(L6470_STEP_MODE, !SYNC_EN | STEP_SEL_1 | SYNC_SEL_1); - // Set up the L6470_CONFIG register as follows: + // Set up the L64XX_CONFIG register as follows: // PWM frequency divisor = 1 // PWM frequency multiplier = 2 (62.5kHz PWM frequency) // Slew rate is 110V/us @@ -71,7 +71,7 @@ void L64XX::init() { // Disable motor voltage compensation // Hard stop on switch low // 16MHz internal oscillator, nothing on output - SetParam(L6470_CONFIG, CONFIG_PWM_DIV_1 | CONFIG_PWM_MUL_2 | CONFIG_SR_110V_us | CONFIG_OC_SD_DISABLE | CONFIG_VS_COMP_DISABLE | CONFIG_SW_HARD_STOP | CONFIG_INT_16MHZ); + SetParam(L64XX_CONFIG, CONFIG_PWM_DIV_1 | CONFIG_PWM_MUL_2 | CONFIG_SR_110V_us | CONFIG_OC_SD_DISABLE | CONFIG_VS_COMP_DISABLE | CONFIG_SW_HARD_STOP | CONFIG_INT_16MHZ); // Configure the dSPIN_RUN KVAL. This defines the duty cycle of the PWM of the bridges // during running. 0xFF means that they are essentially NOT PWMed during run; this @@ -284,7 +284,7 @@ void L64XX::goTo_DIR(const uint8_t dir, long pos) { // GoUntil will set the motor running with direction dir (dSPIN_REV or // dSPIN_FWD) until a falling edge is detected on the SW pin. Depending -// on bit SW_MODE in L6470_CONFIG, either a hard stop or a soft stop is +// on bit SW_MODE in L64XX_CONFIG, either a hard stop or a soft stop is // performed at the falling edge, and depending on the value of // act (either RESET or COPY) the value in the L6470_ABS_POS register is // either RESET to 0 or COPY-ed into the L6470_MARK register. @@ -356,9 +356,9 @@ void L64XX::softFree() { Xfer(dSPIN_SOFT_HIZ); } // Disengage the motor immediately with no deceleration. void L64XX::free() { Xfer(dSPIN_HARD_HIZ); } -// Fetch and return the 16-bit value in the L6470_STATUS register. Resets +// Fetch and return the 16-bit value in the L64XX_STATUS register. Resets // any warning flags and exits any error states. Using GetParam() -// to read L6470_STATUS does not clear these values. +// to read L64XX_STATUS does not clear these values. int16_t L64XX::getStatus() { Xfer(dSPIN_GET_STATUS); return Xfer(0) << 8 | Xfer(0); @@ -458,8 +458,8 @@ uint8_t L64XX::Xfer(uint8_t data) { if (pin_SCK < 0) { // External SPI return (uint8_t)( - position ? L6470_transfer(data, pin_SS, position) // ... in a chain - : L6470_transfer(data, pin_SS) // ... not chained + position ? transfer(data, pin_SS, position) // ... in a chain + : transfer(data, pin_SS) // ... not chained ); } @@ -541,10 +541,10 @@ uint32_t L64XX::ParamHandler(const uint8_t param, const uint32_t value) { // Xfer() directly. // These defines help handle the register address differences between L6470, L6480 & powerSTEP01 - #define L6470_CONFIG_A 0x18 // L6470: L6470_CONFIG, L6480 & powerSTEP01: L6470_GATECFG1 - #define L6470_STATUS_A 0x19 // L6470: L6470_STATUS, L6480 & powerSTEP01: L6470_GATECFG2 - #define L6470_CONFIG_B 0x1A // L6480 & powerSTEP01: L6470_CONFIG - #define L6470_STATUS_B 0x1B // L6480 & powerSTEP01: L6470_STATUS + #define L64XX_CONFIG_A 0x18 // L6470: L64XX_CONFIG, L6480 & powerSTEP01: L6470_GATECFG1 + #define L64XX_STATUS_A 0x19 // L6470: L64XX_STATUS, L6480 & powerSTEP01: L6470_GATECFG2 + #define L64XX_CONFIG_B 0x1A // L6480 & powerSTEP01: L64XX_CONFIG + #define L64XX_STATUS_B 0x1B // L6480 & powerSTEP01: L64XX_STATUS switch (param) { // L6470_ABS_POS is the current absolute offset from home. It is a 22 bit number expressed @@ -648,23 +648,23 @@ uint32_t L64XX::ParamHandler(const uint8_t param, const uint32_t value) { // GATECFG1 & GATECFG2 - L6480 and powerSTEP01 only, these registers control the slew rate and off time of the // bridge power FETs. - // L6470_CONFIG contains some assorted configuration bits and fields. A fairly comprehensive + // L64XX_CONFIG contains some assorted configuration bits and fields. A fairly comprehensive // set of reasonably self-explanatory constants is provided, but users should refer // to the datasheet before modifying the contents of this register to be certain they // understand the implications of their modifications. Value on boot is 0x2E88; this // can be a useful way to verify proper start up and operation of the dSPIN chip. - case L6470_CONFIG_A: if (L6470_status_layout) return Param(value, 16); // L6470 CONFIG register + case L64XX_CONFIG_A: if (L6470_status_layout) return Param(value, 16); // L6470 CONFIG register else return Param(uint16_t(value & 0x07ff), 11); // L6480 & powerSTEP01 GATECFG1 register - case L6470_CONFIG_B: if (L6470_status_layout) return Param(value, 16); // L6480 & powerSTEP01 CONFIG register + case L64XX_CONFIG_B: if (L6470_status_layout) return Param(value, 16); // L6480 & powerSTEP01 CONFIG register - // L6470_STATUS contains read-only information about the current condition of the chip. A + // L64XX_STATUS contains read-only information about the current condition of the chip. A // comprehensive set of constants for masking and testing this register is provided, but // users should refer to the datasheet to ensure that they fully understand each one of // the bits in the register. - case L6470_STATUS_A: if (L6470_status_layout) return Param(value, 16); // L46470 STATUS register + case L64XX_STATUS_A: if (L6470_status_layout) return Param(value, 16); // L46470 STATUS register else return Xfer(uint8_t(value)); // L6480 & powerSTEP01 GATECFG2 register - case L6470_STATUS_B: return Param(0, 16); // L6480 & powerSTEP01 STATUS register + case L64XX_STATUS_B: return Param(0, 16); // L6480 & powerSTEP01 STATUS register } return Xfer(uint8_t(value)); diff --git a/src/L6470.h b/src/L6470.h index cca723c..26abc5a 100644 --- a/src/L6470.h +++ b/src/L6470.h @@ -23,9 +23,6 @@ #define L6470_LIBRARY_VERSION 0x000800 -uint8_t L6470_transfer(uint8_t data, int16_t ss_pin); // user supplied external SPI routine for single device system -uint8_t L6470_transfer(uint8_t data, int16_t ss_pin, uint8_t axis); // user supplied external SPI routine for chain system - //#define SCK 10 // Wire this to the CSN pin //#define MOSI 11 // Wire this to the SDI pin //#define MISO 12 // Wire this to the SDO pin @@ -101,7 +98,7 @@ uint8_t L6470_transfer(uint8_t data, int16_t ss_pin, uint8_t axis); // user sup #define ALARM_EN_SW_TURN_ON 0x40 #define ALARM_EN_WRONG_NPERF_CMD 0x80 -// L6470_CONFIG register renames. +// L64XX_CONFIG register renames. // Oscillator options. // The dSPIN needs to know what the clock frequency is because it uses that for some @@ -191,7 +188,7 @@ uint8_t L6470_transfer(uint8_t data, int16_t ss_pin, uint8_t axis); // user sup #define STATUS_BUSY 0x0002 // mirrors BUSY pin #define STATUS_SW_F 0x0004 // low when switch open, high when closed #define STATUS_SW_EVN 0x0008 // active high, set on switch falling edge, - // cleared by reading L6470_STATUS + // cleared by reading L64XX_STATUS #define STATUS_DIR 0x0010 // Indicates current motor direction. // High is dSPIN_FWD, Low is dSPIN_REV. @@ -274,7 +271,6 @@ class L64XX { L64XX(); void init(); - void spi_init(); void setMicroSteps(int16_t microSteps); void setMaxSpeed(const int16_t speed); @@ -338,25 +334,12 @@ class L64XX { // [0] - number of drivers in chain // [1]... axis index for first device in the chain (closest to MOSI) -private: - long convert(uint32_t val); - - uint32_t AccCalc(const float stepsPerSecPerSec); - uint32_t DecCalc(const float stepsPerSecPerSec); - uint32_t MaxSpdCalc(const float stepsPerSec); - uint32_t MinSpdCalc(const float stepsPerSec); - uint32_t FSCalc(const float stepsPerSec); - uint32_t IntSpdCalc(const float stepsPerSec); - uint32_t SpdCalc(const float stepsPerSec); - uint32_t Param(uint32_t value, const uint8_t bit_len); - uint8_t Xfer(uint8_t data); - uint8_t Xfer(uint8_t data, int16_t ss_pin, uint8_t position); -}; - -class L6470 : public L64XX { -public: - L6470(const int16_t pin_SS); + // These methods must be supplied by the client + void spi_init(); + uint8_t transfer(uint8_t data, const int16_t ss_pin); // single device system + uint8_t transfer(uint8_t data, const int16_t ss_pin, const uint8_t axis); // chain system + // L6470 placeholders may be overridden by sub-classes static constexpr uint8_t OCD_TH_MAX = 15; static constexpr uint8_t STALL_TH_MAX = 127; static constexpr float OCD_CURRENT_CONSTANT_INV = 375; // mA per count @@ -364,8 +347,8 @@ class L6470 : public L64XX { static constexpr float STALL_CURRENT_CONSTANT_INV = 31.25; // mA per count static constexpr float STALL_CURRENT_CONSTANT = 1.0f / STALL_CURRENT_CONSTANT_INV; // counts per mA - static constexpr uint8_t L6470_CONFIG = 0x18; - static constexpr uint8_t L6470_STATUS = 0x19; + static constexpr uint8_t L64XX_CONFIG = 0x18; + static constexpr uint8_t L64XX_STATUS = 0x19; static constexpr bool L6470_status_layout = true; static constexpr uint16_t STATUS_NOTPERF_CMD = 0x0080; // Last command not performed. @@ -378,8 +361,24 @@ class L6470 : public L64XX { static constexpr uint16_t STATUS_STEP_LOSS_A = 0x2000; // Stall detected on A bridge static constexpr uint16_t STATUS_STEP_LOSS_B = 0x4000; // Stall detected on B bridge static constexpr uint16_t STATUS_SCK_MOD = 0x8000; // Step clock mode is active + +private: + long convert(uint32_t val); + + uint32_t AccCalc(const float stepsPerSecPerSec); + uint32_t DecCalc(const float stepsPerSecPerSec); + uint32_t MaxSpdCalc(const float stepsPerSec); + uint32_t MinSpdCalc(const float stepsPerSec); + uint32_t FSCalc(const float stepsPerSec); + uint32_t IntSpdCalc(const float stepsPerSec); + uint32_t SpdCalc(const float stepsPerSec); + uint32_t Param(uint32_t value, const uint8_t bit_len); + uint8_t Xfer(uint8_t data); + uint8_t Xfer(uint8_t data, int16_t ss_pin, uint8_t position); }; +class L6470 : public L64XX { public: L6470(const int16_t pin_SS); }; + class L6480 : public L64XX { public: L6480(const int16_t pin_SS); @@ -391,8 +390,8 @@ class L6480 : public L64XX { static constexpr float STALL_CURRENT_CONSTANT_INV = 31.25; // mA per count static constexpr float STALL_CURRENT_CONSTANT = 1.0f / STALL_CURRENT_CONSTANT_INV; // counts per mA - static constexpr uint8_t L6470_CONFIG = 0x1A; - static constexpr uint8_t L6470_STATUS = 0x1B; + static constexpr uint8_t L64XX_CONFIG = 0x1A; + static constexpr uint8_t L64XX_STATUS = 0x1B; static constexpr bool L6470_status_layout = false; static constexpr uint16_t STATUS_NOTPERF_CMD = 0x0080; // Last command not performed. @@ -424,8 +423,8 @@ class powerSTEP01 : public L64XX { //static constexpr float STALL_CURRENT_CONSTANT = OCD_CURRENT_CONSTANT; // counts per mA (calc per data sheet - definitely wrong) //static constexpr float STALL_CURRENT_CONSTANT_INV = OCD_CURRENT_CONSTANT_INV; // mA per count (calc per data sheet - definitely wrong) - static constexpr uint8_t L6470_CONFIG = 0x1A; - static constexpr uint8_t L6470_STATUS = 0x1B; + static constexpr uint8_t L64XX_CONFIG = 0x1A; + static constexpr uint8_t L64XX_STATUS = 0x1B; static constexpr bool L6470_status_layout = false; static constexpr uint16_t STATUS_NOTPERF_CMD = 0x0080; // Last command not performed. From 5f251746c285c95c7bca47f3099992837dc37517 Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Tue, 26 Feb 2019 18:31:51 -0600 Subject: [PATCH 06/14] Put 'chain' into the L64XX class --- .../two_motors_synchronized.ino | 16 +++++++++------- src/L6470.cpp | 16 +++++++--------- src/L6470.h | 8 ++++---- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/examples/two_motors_synchronized/two_motors_synchronized.ino b/examples/two_motors_synchronized/two_motors_synchronized.ino index 4f94ef7..a155c91 100644 --- a/examples/two_motors_synchronized/two_motors_synchronized.ino +++ b/examples/two_motors_synchronized/two_motors_synchronized.ino @@ -57,7 +57,9 @@ uint8_t L6470_SpiTransfer_Mode_3(uint8_t b) { // using Mode 3 * This routine sends/receives one uint8_t to the target device. All other * Devices are sent the NOOP command. * - * Note that the data for the last device in the chain is sent out first. + * Send/receive one uint8_t to the target device. All other devices get a NOOP command. + * + * Data for the last device in the chain is sent out first! * * The library will automatically link to "uint8_t L6470_Transfer(uint8_t,int16_t,uint8_t)" */ @@ -69,9 +71,9 @@ uint8_t L6470_transfer(uint8_t data, int16_t ss_pin, uint8_t chain_position) { // first device in chain has data sent last digitalWrite(ss_pin, LOW); - for (uint8_t i = L6470::chain[0]; i >= 1; i--) { + for (uint8_t i = L64XX::chain[0]; i >= 1; i--) { uint8_t temp = L6470_SpiTransfer_Mode_3(uint8_t(i == chain_position ? data : CMD_NOP)); - if (L6470::chain[i] == chain_position) data_out = temp; + if (L64XX::chain[i] == chain_position) data_out = temp; } digitalWrite(ss_pin, HIGH); @@ -127,10 +129,10 @@ void goTo(long location_1, long location_2) { uint8_t addr15_8[3] = { 0, uint8_t(location_1 >> 8), uint8_t(location_2 >> 8) }; uint8_t addr7_0[3] = { 0, uint8_t(location_1) , uint8_t(location_2) }; - Buffer_Transfer(buffer_command, L6470::chain[0]); // send the commands - Buffer_Transfer(addr21_16 , L6470::chain[0]); // send the MSB of the position - Buffer_Transfer(addr15_8 , L6470::chain[0]); - Buffer_Transfer(addr7_0 , L6470::chain[0]); // this one results in the motors moving + Buffer_Transfer(buffer_command, L64XX::chain[0]); // send the commands + Buffer_Transfer(addr21_16 , L64XX::chain[0]); // send the MSB of the position + Buffer_Transfer(addr15_8 , L64XX::chain[0]); + Buffer_Transfer(addr7_0 , L64XX::chain[0]); // this one results in the motors moving } ////////////////////////////////////////////////////////////////////// diff --git a/src/L6470.cpp b/src/L6470.cpp index 2e448c7..a268790 100644 --- a/src/L6470.cpp +++ b/src/L6470.cpp @@ -13,9 +13,7 @@ #include -uint8_t L64XX_chain[21]; - // [0] - number of drivers in chain - // [1]... axis index for first device in the chain (closest to MOSI) +uint8_t L64XX::chain[21]; L64XX::L64XX() {} @@ -96,13 +94,13 @@ void L64XX::init() { // add to the chain array and save chain info for this stepper void L64XX::set_chain_info(const uint8_t axis, const uint8_t chain_position) { if (chain_position) { - L64XX_chain[0]++; - L64XX_chain[chain_position] = axis; + chain[0]++; + chain[chain_position] = axis; position = chain_position; axis_index = axis; } else - L64XX_chain[0] = 0; //reset array back to uninitialized + chain[0] = 0; //reset array back to uninitialized } // Sets optional pins for this stepper @@ -487,8 +485,8 @@ uint8_t L64XX::Xfer(uint8_t data) { uint8_t out_data = 0; uint8_t return_data = 0; digitalWrite(pin_SS, LOW); - for (uint8_t i = L64XX_chain[0]; i >= 1; i--) { - out_data = (L64XX_chain[i] == position ? data : CMD_NOP); + for (uint8_t i = chain[0]; i >= 1; i--) { + out_data = (chain[i] == position ? data : CMD_NOP); uint8_t bits = 8; do { digitalWrite(pin_SCK, LOW); @@ -499,7 +497,7 @@ uint8_t L64XX::Xfer(uint8_t data) { out_data |= (digitalRead(pin_MISO) != 0); } while (--bits); delay(0); // 10 cycles @ 84mhz - if (L64XX_chain[i] == position) return_data = out_data; + if (chain[i] == position) return_data = out_data; } digitalWrite(pin_SS, HIGH); return return_data; diff --git a/src/L6470.h b/src/L6470.h index 5018b38..cca723c 100644 --- a/src/L6470.h +++ b/src/L6470.h @@ -23,10 +23,6 @@ #define L6470_LIBRARY_VERSION 0x000800 -extern uint8_t L64XX_chain[21]; - // [0] - number of drivers in chain - // [1]... axis index for first device in the chain (closest to MOSI) - uint8_t L6470_transfer(uint8_t data, int16_t ss_pin); // user supplied external SPI routine for single device system uint8_t L6470_transfer(uint8_t data, int16_t ss_pin, uint8_t axis); // user supplied external SPI routine for chain system @@ -338,6 +334,10 @@ class L64XX { uint8_t axis_index; uint8_t position = 0; // 0 - not part of a chain + static uint8_t chain[21]; + // [0] - number of drivers in chain + // [1]... axis index for first device in the chain (closest to MOSI) + private: long convert(uint32_t val); From ccad93ad0d6681e6f33316254f05ccc31dd32304 Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Tue, 26 Feb 2019 19:01:02 -0600 Subject: [PATCH 07/14] Move client methods into L64XX class --- .../two_motors_synchronized.ino | 10 ++-- src/L6470.cpp | 40 ++++++------- src/L6470.h | 59 +++++++++---------- 3 files changed, 55 insertions(+), 54 deletions(-) diff --git a/examples/two_motors_synchronized/two_motors_synchronized.ino b/examples/two_motors_synchronized/two_motors_synchronized.ino index a155c91..86bcd68 100644 --- a/examples/two_motors_synchronized/two_motors_synchronized.ino +++ b/examples/two_motors_synchronized/two_motors_synchronized.ino @@ -61,10 +61,10 @@ uint8_t L6470_SpiTransfer_Mode_3(uint8_t b) { // using Mode 3 * * Data for the last device in the chain is sent out first! * - * The library will automatically link to "uint8_t L6470_Transfer(uint8_t,int16_t,uint8_t)" + * The library will automatically link to "uint8_t L64XX::transfer(uint8_t,int16_t,uint8_t)" */ -uint8_t L6470_transfer(uint8_t data, int16_t ss_pin, uint8_t chain_position) { +uint8_t L64XX::transfer(uint8_t data, const int16_t ss_pin, const uint8_t chain_position) { #define CMD_NOP 0 uint8_t data_out = 0; data--; @@ -80,6 +80,8 @@ uint8_t L6470_transfer(uint8_t data, int16_t ss_pin, uint8_t chain_position) { return data_out; } +uint8_t L64XX::transfer(uint8_t data, const int16_t ss_pin) { } + /** * This is the routine that sends the motion commands. * @@ -103,10 +105,10 @@ void Buffer_Transfer(uint8_t buffer[] , uint8_t length) { /** * Initialize pins for non-library SPI software * - * The library will automatically link to "void L6470::spi_init()" + * The library will automatically link to "void L64XX::spi_init()" */ -void L6470::spi_init() { +void L64XX::spi_init() { pinMode(SS_PIN, OUTPUT); pinMode(SCK_PIN, OUTPUT); pinMode(MOSI_PIN, OUTPUT); diff --git a/src/L6470.cpp b/src/L6470.cpp index a268790..c2400fe 100644 --- a/src/L6470.cpp +++ b/src/L6470.cpp @@ -47,11 +47,11 @@ void L64XX::init() { // SPI clock not to exceed 5MHz, // SPI_MODE3 (clock idle high, latch data on rising edge of clock) if (pin_SCK < 0) spi_init(); // using external SPI to init it - // internal SPI already initialized + // internal SPI already initialized - // First things first: let's check communications. The L6470_CONFIG register should + // First things first: let's check communications. The L64XX_CONFIG register should // power up to 0x2E88, so we can use that to check the communications. - //Serial.println(GetParam(L6470_CONFIG) == 0x2E88 ? "good to go" : "Comm issue"); + //Serial.println(GetParam(L64XX_CONFIG) == 0x2E88 ? "good to go" : "Comm issue"); // First, let's set the step mode register: // - SYNC_EN controls whether the BUSY/SYNC pin reflects the step @@ -63,7 +63,7 @@ void L64XX::init() { // not using that pin. //SetParam(L6470_STEP_MODE, !SYNC_EN | STEP_SEL_1 | SYNC_SEL_1); - // Set up the L6470_CONFIG register as follows: + // Set up the L64XX_CONFIG register as follows: // PWM frequency divisor = 1 // PWM frequency multiplier = 2 (62.5kHz PWM frequency) // Slew rate is 110V/us @@ -71,7 +71,7 @@ void L64XX::init() { // Disable motor voltage compensation // Hard stop on switch low // 16MHz internal oscillator, nothing on output - SetParam(L6470_CONFIG, CONFIG_PWM_DIV_1 | CONFIG_PWM_MUL_2 | CONFIG_SR_110V_us | CONFIG_OC_SD_DISABLE | CONFIG_VS_COMP_DISABLE | CONFIG_SW_HARD_STOP | CONFIG_INT_16MHZ); + SetParam(L64XX_CONFIG, CONFIG_PWM_DIV_1 | CONFIG_PWM_MUL_2 | CONFIG_SR_110V_us | CONFIG_OC_SD_DISABLE | CONFIG_VS_COMP_DISABLE | CONFIG_SW_HARD_STOP | CONFIG_INT_16MHZ); // Configure the dSPIN_RUN KVAL. This defines the duty cycle of the PWM of the bridges // during running. 0xFF means that they are essentially NOT PWMed during run; this @@ -284,7 +284,7 @@ void L64XX::goTo_DIR(const uint8_t dir, long pos) { // GoUntil will set the motor running with direction dir (dSPIN_REV or // dSPIN_FWD) until a falling edge is detected on the SW pin. Depending -// on bit SW_MODE in L6470_CONFIG, either a hard stop or a soft stop is +// on bit SW_MODE in L64XX_CONFIG, either a hard stop or a soft stop is // performed at the falling edge, and depending on the value of // act (either RESET or COPY) the value in the L6470_ABS_POS register is // either RESET to 0 or COPY-ed into the L6470_MARK register. @@ -356,9 +356,9 @@ void L64XX::softFree() { Xfer(dSPIN_SOFT_HIZ); } // Disengage the motor immediately with no deceleration. void L64XX::free() { Xfer(dSPIN_HARD_HIZ); } -// Fetch and return the 16-bit value in the L6470_STATUS register. Resets +// Fetch and return the 16-bit value in the L64XX_STATUS register. Resets // any warning flags and exits any error states. Using GetParam() -// to read L6470_STATUS does not clear these values. +// to read L64XX_STATUS does not clear these values. int16_t L64XX::getStatus() { Xfer(dSPIN_GET_STATUS); return Xfer(0) << 8 | Xfer(0); @@ -458,8 +458,8 @@ uint8_t L64XX::Xfer(uint8_t data) { if (pin_SCK < 0) { // External SPI return (uint8_t)( - position ? L6470_transfer(data, pin_SS, position) // ... in a chain - : L6470_transfer(data, pin_SS) // ... not chained + position ? transfer(data, pin_SS, position) // ... in a chain + : transfer(data, pin_SS) // ... not chained ); } @@ -541,10 +541,10 @@ uint32_t L64XX::ParamHandler(const uint8_t param, const uint32_t value) { // Xfer() directly. // These defines help handle the register address differences between L6470, L6480 & powerSTEP01 - #define L6470_CONFIG_A 0x18 // L6470: L6470_CONFIG, L6480 & powerSTEP01: L6470_GATECFG1 - #define L6470_STATUS_A 0x19 // L6470: L6470_STATUS, L6480 & powerSTEP01: L6470_GATECFG2 - #define L6470_CONFIG_B 0x1A // L6480 & powerSTEP01: L6470_CONFIG - #define L6470_STATUS_B 0x1B // L6480 & powerSTEP01: L6470_STATUS + #define L64XX_CONFIG_A 0x18 // L6470: L64XX_CONFIG, L6480 & powerSTEP01: L6470_GATECFG1 + #define L64XX_STATUS_A 0x19 // L6470: L64XX_STATUS, L6480 & powerSTEP01: L6470_GATECFG2 + #define L64XX_CONFIG_B 0x1A // L6480 & powerSTEP01: L64XX_CONFIG + #define L64XX_STATUS_B 0x1B // L6480 & powerSTEP01: L64XX_STATUS switch (param) { // L6470_ABS_POS is the current absolute offset from home. It is a 22 bit number expressed @@ -648,23 +648,23 @@ uint32_t L64XX::ParamHandler(const uint8_t param, const uint32_t value) { // GATECFG1 & GATECFG2 - L6480 and powerSTEP01 only, these registers control the slew rate and off time of the // bridge power FETs. - // L6470_CONFIG contains some assorted configuration bits and fields. A fairly comprehensive + // L64XX_CONFIG contains some assorted configuration bits and fields. A fairly comprehensive // set of reasonably self-explanatory constants is provided, but users should refer // to the datasheet before modifying the contents of this register to be certain they // understand the implications of their modifications. Value on boot is 0x2E88; this // can be a useful way to verify proper start up and operation of the dSPIN chip. - case L6470_CONFIG_A: if (L6470_status_layout) return Param(value, 16); // L6470 CONFIG register + case L64XX_CONFIG_A: if (L6470_status_layout) return Param(value, 16); // L6470 CONFIG register else return Param(uint16_t(value & 0x07ff), 11); // L6480 & powerSTEP01 GATECFG1 register - case L6470_CONFIG_B: if (L6470_status_layout) return Param(value, 16); // L6480 & powerSTEP01 CONFIG register + case L64XX_CONFIG_B: if (L6470_status_layout) return Param(value, 16); // L6480 & powerSTEP01 CONFIG register - // L6470_STATUS contains read-only information about the current condition of the chip. A + // L64XX_STATUS contains read-only information about the current condition of the chip. A // comprehensive set of constants for masking and testing this register is provided, but // users should refer to the datasheet to ensure that they fully understand each one of // the bits in the register. - case L6470_STATUS_A: if (L6470_status_layout) return Param(value, 16); // L46470 STATUS register + case L64XX_STATUS_A: if (L6470_status_layout) return Param(value, 16); // L46470 STATUS register else return Xfer(uint8_t(value)); // L6480 & powerSTEP01 GATECFG2 register - case L6470_STATUS_B: return Param(0, 16); // L6480 & powerSTEP01 STATUS register + case L64XX_STATUS_B: return Param(0, 16); // L6480 & powerSTEP01 STATUS register } return Xfer(uint8_t(value)); diff --git a/src/L6470.h b/src/L6470.h index cca723c..22dc349 100644 --- a/src/L6470.h +++ b/src/L6470.h @@ -23,9 +23,6 @@ #define L6470_LIBRARY_VERSION 0x000800 -uint8_t L6470_transfer(uint8_t data, int16_t ss_pin); // user supplied external SPI routine for single device system -uint8_t L6470_transfer(uint8_t data, int16_t ss_pin, uint8_t axis); // user supplied external SPI routine for chain system - //#define SCK 10 // Wire this to the CSN pin //#define MOSI 11 // Wire this to the SDI pin //#define MISO 12 // Wire this to the SDO pin @@ -101,7 +98,7 @@ uint8_t L6470_transfer(uint8_t data, int16_t ss_pin, uint8_t axis); // user sup #define ALARM_EN_SW_TURN_ON 0x40 #define ALARM_EN_WRONG_NPERF_CMD 0x80 -// L6470_CONFIG register renames. +// L64XX_CONFIG register renames. // Oscillator options. // The dSPIN needs to know what the clock frequency is because it uses that for some @@ -191,7 +188,7 @@ uint8_t L6470_transfer(uint8_t data, int16_t ss_pin, uint8_t axis); // user sup #define STATUS_BUSY 0x0002 // mirrors BUSY pin #define STATUS_SW_F 0x0004 // low when switch open, high when closed #define STATUS_SW_EVN 0x0008 // active high, set on switch falling edge, - // cleared by reading L6470_STATUS + // cleared by reading L64XX_STATUS #define STATUS_DIR 0x0010 // Indicates current motor direction. // High is dSPIN_FWD, Low is dSPIN_REV. @@ -274,7 +271,6 @@ class L64XX { L64XX(); void init(); - void spi_init(); void setMicroSteps(int16_t microSteps); void setMaxSpeed(const int16_t speed); @@ -338,25 +334,12 @@ class L64XX { // [0] - number of drivers in chain // [1]... axis index for first device in the chain (closest to MOSI) -private: - long convert(uint32_t val); - - uint32_t AccCalc(const float stepsPerSecPerSec); - uint32_t DecCalc(const float stepsPerSecPerSec); - uint32_t MaxSpdCalc(const float stepsPerSec); - uint32_t MinSpdCalc(const float stepsPerSec); - uint32_t FSCalc(const float stepsPerSec); - uint32_t IntSpdCalc(const float stepsPerSec); - uint32_t SpdCalc(const float stepsPerSec); - uint32_t Param(uint32_t value, const uint8_t bit_len); - uint8_t Xfer(uint8_t data); - uint8_t Xfer(uint8_t data, int16_t ss_pin, uint8_t position); -}; - -class L6470 : public L64XX { -public: - L6470(const int16_t pin_SS); + // These methods must be supplied by the client + static void spi_init(); + uint8_t transfer(uint8_t data, const int16_t ss_pin); // single device system + uint8_t transfer(uint8_t data, const int16_t ss_pin, const uint8_t axis); // chain system + // L6470 placeholders may be overridden by sub-classes static constexpr uint8_t OCD_TH_MAX = 15; static constexpr uint8_t STALL_TH_MAX = 127; static constexpr float OCD_CURRENT_CONSTANT_INV = 375; // mA per count @@ -364,8 +347,8 @@ class L6470 : public L64XX { static constexpr float STALL_CURRENT_CONSTANT_INV = 31.25; // mA per count static constexpr float STALL_CURRENT_CONSTANT = 1.0f / STALL_CURRENT_CONSTANT_INV; // counts per mA - static constexpr uint8_t L6470_CONFIG = 0x18; - static constexpr uint8_t L6470_STATUS = 0x19; + static constexpr uint8_t L64XX_CONFIG = 0x18; + static constexpr uint8_t L64XX_STATUS = 0x19; static constexpr bool L6470_status_layout = true; static constexpr uint16_t STATUS_NOTPERF_CMD = 0x0080; // Last command not performed. @@ -378,8 +361,24 @@ class L6470 : public L64XX { static constexpr uint16_t STATUS_STEP_LOSS_A = 0x2000; // Stall detected on A bridge static constexpr uint16_t STATUS_STEP_LOSS_B = 0x4000; // Stall detected on B bridge static constexpr uint16_t STATUS_SCK_MOD = 0x8000; // Step clock mode is active + +private: + long convert(uint32_t val); + + uint32_t AccCalc(const float stepsPerSecPerSec); + uint32_t DecCalc(const float stepsPerSecPerSec); + uint32_t MaxSpdCalc(const float stepsPerSec); + uint32_t MinSpdCalc(const float stepsPerSec); + uint32_t FSCalc(const float stepsPerSec); + uint32_t IntSpdCalc(const float stepsPerSec); + uint32_t SpdCalc(const float stepsPerSec); + uint32_t Param(uint32_t value, const uint8_t bit_len); + uint8_t Xfer(uint8_t data); + uint8_t Xfer(uint8_t data, int16_t ss_pin, uint8_t position); }; +class L6470 : public L64XX { public: L6470(const int16_t pin_SS); }; + class L6480 : public L64XX { public: L6480(const int16_t pin_SS); @@ -391,8 +390,8 @@ class L6480 : public L64XX { static constexpr float STALL_CURRENT_CONSTANT_INV = 31.25; // mA per count static constexpr float STALL_CURRENT_CONSTANT = 1.0f / STALL_CURRENT_CONSTANT_INV; // counts per mA - static constexpr uint8_t L6470_CONFIG = 0x1A; - static constexpr uint8_t L6470_STATUS = 0x1B; + static constexpr uint8_t L64XX_CONFIG = 0x1A; + static constexpr uint8_t L64XX_STATUS = 0x1B; static constexpr bool L6470_status_layout = false; static constexpr uint16_t STATUS_NOTPERF_CMD = 0x0080; // Last command not performed. @@ -424,8 +423,8 @@ class powerSTEP01 : public L64XX { //static constexpr float STALL_CURRENT_CONSTANT = OCD_CURRENT_CONSTANT; // counts per mA (calc per data sheet - definitely wrong) //static constexpr float STALL_CURRENT_CONSTANT_INV = OCD_CURRENT_CONSTANT_INV; // mA per count (calc per data sheet - definitely wrong) - static constexpr uint8_t L6470_CONFIG = 0x1A; - static constexpr uint8_t L6470_STATUS = 0x1B; + static constexpr uint8_t L64XX_CONFIG = 0x1A; + static constexpr uint8_t L64XX_STATUS = 0x1B; static constexpr bool L6470_status_layout = false; static constexpr uint16_t STATUS_NOTPERF_CMD = 0x0080; // Last command not performed. From 54f3887155c561c6b9e7019efddd944b19a7d07b Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Thu, 28 Feb 2019 17:35:30 -0600 Subject: [PATCH 08/14] Use handler pointers instead of class completion --- .../two_motors_synchronized.ino | 85 +++++++----- src/L6470.cpp | 35 ++--- src/L6470.h | 128 ++++++++++-------- 3 files changed, 132 insertions(+), 116 deletions(-) diff --git a/examples/two_motors_synchronized/two_motors_synchronized.ino b/examples/two_motors_synchronized/two_motors_synchronized.ino index 86bcd68..69174e0 100644 --- a/examples/two_motors_synchronized/two_motors_synchronized.ino +++ b/examples/two_motors_synchronized/two_motors_synchronized.ino @@ -52,10 +52,20 @@ uint8_t L6470_SpiTransfer_Mode_3(uint8_t b) { // using Mode 3 } /** - * This is the routine involved in all non-motion commands/transfers - * - * This routine sends/receives one uint8_t to the target device. All other - * Devices are sent the NOOP command. + * Initialize pins for non-library SPI software + */ +static void spi_init() { + pinMode(SS_PIN, OUTPUT); + pinMode(SCK_PIN, OUTPUT); + pinMode(MOSI_PIN, OUTPUT); + digitalWrite(SS_PIN, HIGH); + digitalWrite(SCK_PIN, HIGH); + digitalWrite(MOSI_PIN, HIGH); + pinMode(MISO_PIN, INPUT); +} + +/** + * Used in all non-motion commands/transfers * * Send/receive one uint8_t to the target device. All other devices get a NOOP command. * @@ -63,8 +73,7 @@ uint8_t L6470_SpiTransfer_Mode_3(uint8_t b) { // using Mode 3 * * The library will automatically link to "uint8_t L64XX::transfer(uint8_t,int16_t,uint8_t)" */ - -uint8_t L64XX::transfer(uint8_t data, const int16_t ss_pin, const uint8_t chain_position) { +static uint8_t chain_transfer(uint8_t data, const int16_t ss_pin, const uint8_t chain_position) { #define CMD_NOP 0 uint8_t data_out = 0; data--; @@ -80,7 +89,7 @@ uint8_t L64XX::transfer(uint8_t data, const int16_t ss_pin, const uint8_t chain_ return data_out; } -uint8_t L64XX::transfer(uint8_t data, const int16_t ss_pin) { } +static uint8_t transfer(uint8_t data, const int16_t ss_pin) { } /** * This is the routine that sends the motion commands. @@ -102,22 +111,6 @@ void Buffer_Transfer(uint8_t buffer[] , uint8_t length) { digitalWrite(SS_PIN, HIGH); } -/** - * Initialize pins for non-library SPI software - * - * The library will automatically link to "void L64XX::spi_init()" - */ - -void L64XX::spi_init() { - pinMode(SS_PIN, OUTPUT); - pinMode(SCK_PIN, OUTPUT); - pinMode(MOSI_PIN, OUTPUT); - digitalWrite(SS_PIN, HIGH); - digitalWrite(SCK_PIN, HIGH); - digitalWrite(MOSI_PIN, HIGH); - pinMode(MISO_PIN, INPUT); -} - void goTo(long location_1, long location_2) { // the command string to move a stepper to an absolute position is // four uint8_t long so four arraya are used for convenience @@ -139,43 +132,61 @@ void goTo(long location_1, long location_2) { ////////////////////////////////////////////////////////////////////// -L6470 stepperA(SS_PIN); // create first stepper object -L6470 stepperB(SS_PIN); // create second stepper object - void setup() { - pinMode(RESET_PIN,OUTPUT); // reset all drivers - digitalWrite(RESET_PIN, LOW); // do this before any setup commands are sent to the drivers + Serial.begin(115200); + + L6470 stepperA(SS_PIN, spi_init, transfer, chain_transfer); + //L6480 stepperB(SS_PIN, spi_init, transfer, chain_transfer); + //powerSTEP01 stepperC(SS_PIN); + + return; + + /* + pinMode(RESET_PIN,OUTPUT); // Reset all drivers + digitalWrite(RESET_PIN, LOW); // Do this before any setup commands are sent to the drivers delay(10); digitalWrite(RESET_PIN, HIGH); - stepperA.set_chain_info(56, 1); // completely setup chain[] before - stepperB.set_chain_info(56, 2); // any SPI traffic is sent + stepperA.set_chain_info(56, 1); // Completely setup chain[] before + stepperB.set_chain_info(56, 2); // Any SPI traffic is sent stepperA.init(); - stepperA.setAcc(100); // Set acceleration + stepperA.setAcc(100); // Set acceleration stepperA.setMaxSpeed(800); stepperA.setMinSpeed(1); - stepperA.setMicroSteps(2); // 1,2,4,8,16,32,64 or 128 + stepperA.setMicroSteps(2); // 1,2,4,8,16,32,64 or 128 stepperA.setThresholdSpeed(1000); - stepperA.setOverCurrent(6000); // Set overcurrent protection + stepperA.setOverCurrent(6000); // Set overcurrent protection stepperA.setStallCurrent(3000); stepperB.init(); - stepperB.setAcc(100); // Set acceleration + stepperB.setAcc(100); // Set acceleration stepperB.setMaxSpeed(800); stepperB.setMinSpeed(1); - stepperB.setMicroSteps(2); // 1,2,4,8,16,32,64 or 128 + stepperB.setMicroSteps(2); // 1,2,4,8,16,32,64 or 128 stepperB.setThresholdSpeed(1000); - stepperB.setOverCurrent(6000); // Set overcurrent protection + stepperB.setOverCurrent(6000); // Set overcurrent protection stepperB.setStallCurrent(3000); - goTo(200,200); // spin the motors at the same time + goTo(200,200); // Spin the motors at the same time + */ } void loop() { + + static uint32_t next_ms = millis() + 500UL; + uint32_t ms = millis(); + if (ms >= next_ms) { + Serial.println("tick"); + next_ms = ms + 500UL; + } + + return; + /* while (stepperB.isBusy()) delay(10); goTo(-200,-200); while (stepperB.isBusy()) delay(10); goTo(2000,2000); + */ } diff --git a/src/L6470.cpp b/src/L6470.cpp index c2400fe..e52cc35 100644 --- a/src/L6470.cpp +++ b/src/L6470.cpp @@ -13,24 +13,11 @@ #include -uint8_t L64XX::chain[21]; - -L64XX::L64XX() {} - -L6470::L6470(const int16_t ss_pin) { - pin_SS = ss_pin; - // Serial.begin(9600); -} +L64XX::spi_init_handler_t L64XX::spi_init = spi_init_noop; +L64XX::transfer_handler_t L64XX::transfer = transfer_noop; +L64XX::chain_transfer_handler_t L64XX::chain_transfer = chain_transfer_noop; -L6480::L6480(const int16_t ss_pin) { - pin_SS = ss_pin; - // Serial.begin(9600); -} - -powerSTEP01::powerSTEP01(const int16_t ss_pin) { - pin_SS = ss_pin; - // Serial.begin(9600); -} +uint8_t L64XX::chain[21]; // Generic init function to set up communication with the dSPIN chip. @@ -46,7 +33,7 @@ void L64XX::init() { // most significant bit first, // SPI clock not to exceed 5MHz, // SPI_MODE3 (clock idle high, latch data on rising edge of clock) - if (pin_SCK < 0) spi_init(); // using external SPI to init it + if (pin_SCK < 0) spi_init(); // Use external SPI init function to init it // internal SPI already initialized // First things first: let's check communications. The L64XX_CONFIG register should @@ -91,7 +78,7 @@ void L64XX::init() { hardStop(); //engage motors } -// add to the chain array and save chain info for this stepper +// Add to the chain array and save chain info for this stepper void L64XX::set_chain_info(const uint8_t axis, const uint8_t chain_position) { if (chain_position) { chain[0]++; @@ -100,10 +87,10 @@ void L64XX::set_chain_info(const uint8_t axis, const uint8_t chain_position) { axis_index = axis; } else - chain[0] = 0; //reset array back to uninitialized + chain[0] = 0; // Reset array back to uninitialized } -// Sets optional pins for this stepper +// Set optional pins for this stepper // pin_SS is set by the instantiation call. void L64XX::set_pins(const int16_t sck, const int16_t mosi, const int16_t miso, const int16_t reset, const int16_t busyn) { pin_SCK = sck; @@ -457,9 +444,9 @@ uint32_t L64XX::Param(uint32_t value, const uint8_t bit_len) { uint8_t L64XX::Xfer(uint8_t data) { if (pin_SCK < 0) { // External SPI - return (uint8_t)( - position ? transfer(data, pin_SS, position) // ... in a chain - : transfer(data, pin_SS) // ... not chained + return (uint8_t) ( + position ? chain_transfer(data, pin_SS, position) // ... in a chain + : transfer(data, pin_SS) // ... not chained ); } diff --git a/src/L6470.h b/src/L6470.h index 22dc349..9ca1743 100644 --- a/src/L6470.h +++ b/src/L6470.h @@ -236,7 +236,6 @@ #define PWR_TON_MIN 0X0F #define PWR_TOFF_MIN 0X10 - // dSPIN commands #define dSPIN_NOP 0x00 #define dSPIN_SET_PARAM 0x00 @@ -268,10 +267,50 @@ class L64XX { public: - L64XX(); + int16_t pin_SS = -1, + pin_SCK = -1, + pin_MOSI = -1, + pin_MISO = -1, + pin_RESET = -1, + pin_BUSYN = -1; + + static uint8_t chain[21]; + // [0] - number of drivers in chain + // [1]... axis index for first device in the chain (closest to MOSI) + + uint8_t axis_index; + uint8_t position = 0; // 0 - not part of a chain + + // These methods must be supplied by the client + typedef void (*spi_init_handler_t)(); + typedef uint8_t (*transfer_handler_t)(uint8_t data, const int16_t ss_pin); + typedef uint8_t (*chain_transfer_handler_t)(uint8_t data, const int16_t ss_pin, const uint8_t chain_position); + + static spi_init_handler_t spi_init; + static transfer_handler_t transfer; + static chain_transfer_handler_t chain_transfer; + + static inline void set_spi_init_handler(spi_init_handler_t _spi_init) { spi_init = _spi_init; } + static inline void set_transfer_handler(transfer_handler_t _transfer) { transfer = _transfer; } + static inline void set_chain_transfer_handler(chain_transfer_handler_t _chain_transfer) { chain_transfer = _chain_transfer; } + + static void set_handlers(spi_init_handler_t _spi_init, transfer_handler_t _transfer, chain_transfer_handler_t _chain_transfer) { + set_spi_init_handler(_spi_init); + set_transfer_handler(_transfer); + set_chain_transfer_handler(_chain_transfer); + } void init(); + inline void init(const uint16_t ss_pin) { pin_SS = ss_pin; } + inline void init(const uint16_t ss_pin, spi_init_handler_t _spi_init, transfer_handler_t _transfer, chain_transfer_handler_t _chain_transfer) { + pin_SS = ss_pin; + set_handlers(_spi_init, _transfer, _chain_transfer); + } + + void set_pins(const int16_t SCK, const int16_t MOSI, const int16_t MISO, const int16_t RESET, const int16_t BUSYN); + void set_chain_info(const uint8_t axis_index, const uint8_t position); + void setMicroSteps(int16_t microSteps); void setMaxSpeed(const int16_t speed); void setMinSpeed(const int16_t speed); @@ -316,29 +355,6 @@ class L64XX { void SetParam(const uint8_t param, const uint32_t value); uint32_t GetParam(const uint8_t param); - void set_chain_info(const uint8_t axis_index, const uint8_t position); - - void set_pins(const int16_t SCK, const int16_t MOSI, const int16_t MISO, const int16_t RESET, const int16_t BUSYN); - - int16_t pin_SS = -1, - pin_SCK = -1, - pin_MOSI = -1, - pin_MISO = -1, - pin_RESET = -1, - pin_BUSYN = -1; - - uint8_t axis_index; - uint8_t position = 0; // 0 - not part of a chain - - static uint8_t chain[21]; - // [0] - number of drivers in chain - // [1]... axis index for first device in the chain (closest to MOSI) - - // These methods must be supplied by the client - static void spi_init(); - uint8_t transfer(uint8_t data, const int16_t ss_pin); // single device system - uint8_t transfer(uint8_t data, const int16_t ss_pin, const uint8_t axis); // chain system - // L6470 placeholders may be overridden by sub-classes static constexpr uint8_t OCD_TH_MAX = 15; static constexpr uint8_t STALL_TH_MAX = 127; @@ -375,26 +391,26 @@ class L64XX { uint32_t Param(uint32_t value, const uint8_t bit_len); uint8_t Xfer(uint8_t data); uint8_t Xfer(uint8_t data, int16_t ss_pin, uint8_t position); -}; -class L6470 : public L64XX { public: L6470(const int16_t pin_SS); }; + static inline void spi_init_noop() { } + static inline uint8_t transfer_noop(uint8_t data, const int16_t ss_pin) { } + static inline uint8_t chain_transfer_noop(uint8_t data, const int16_t ss_pin, const uint8_t chain_position) { } +}; -class L6480 : public L64XX { +class L6470 : public L64XX { public: - L6480(const int16_t pin_SS); - - static constexpr uint8_t OCD_TH_MAX = 31; - static constexpr uint8_t STALL_TH_MAX = 31; - static constexpr float OCD_CURRENT_CONSTANT_INV = 31.25; // mA per count - static constexpr float OCD_CURRENT_CONSTANT = 1.0f / OCD_CURRENT_CONSTANT_INV; // counts per mA - static constexpr float STALL_CURRENT_CONSTANT_INV = 31.25; // mA per count - static constexpr float STALL_CURRENT_CONSTANT = 1.0f / STALL_CURRENT_CONSTANT_INV; // counts per mA + L6470(const int16_t ss_pin) { init(ss_pin); } + L6470(const int16_t ss_pin, spi_init_handler_t _spi_init, transfer_handler_t _transfer, chain_transfer_handler_t _chain_transfer) { + init(ss_pin, _spi_init, _transfer, _chain_transfer); + } +}; +class L6480_Base : public L64XX { +public: static constexpr uint8_t L64XX_CONFIG = 0x1A; static constexpr uint8_t L64XX_STATUS = 0x1B; static constexpr bool L6470_status_layout = false; - static constexpr uint16_t STATUS_NOTPERF_CMD = 0x0080; // Last command not performed. static constexpr uint16_t STATUS_WRONG_CMD = 0x0080; // Last command not valid. static constexpr uint16_t STATUS_CMD_ERR = 0x0080; // Command error static constexpr uint16_t STATUS_UVLO = 0x0200; // Undervoltage lockout is active @@ -407,9 +423,27 @@ class L6480 : public L64XX { static constexpr uint16_t STATUS_SCK_MOD = 0x0100; // Step clock mode is active }; -class powerSTEP01 : public L64XX { +class L6480 : public L6480_Base { +public: + L6480(const int16_t ss_pin) { init(ss_pin); } + L6480(const int16_t ss_pin, spi_init_handler_t _spi_init, transfer_handler_t _transfer, chain_transfer_handler_t _chain_transfer) { + init(ss_pin, _spi_init, _transfer, _chain_transfer); + } + + static constexpr uint8_t OCD_TH_MAX = 31; + static constexpr uint8_t STALL_TH_MAX = 31; + static constexpr float OCD_CURRENT_CONSTANT_INV = 31.25; // mA per count + static constexpr float OCD_CURRENT_CONSTANT = 1.0f / OCD_CURRENT_CONSTANT_INV; // counts per mA + static constexpr float STALL_CURRENT_CONSTANT_INV = 31.25; // mA per count + static constexpr float STALL_CURRENT_CONSTANT = 1.0f / STALL_CURRENT_CONSTANT_INV; // counts per mA +}; + +class powerSTEP01 : public L6480_Base { public: - powerSTEP01(const int16_t pin_SS); + powerSTEP01(const int16_t ss_pin) { init(ss_pin); } + powerSTEP01(const int16_t ss_pin, spi_init_handler_t _spi_init, transfer_handler_t _transfer, chain_transfer_handler_t _chain_transfer) { + init(ss_pin, _spi_init, _transfer, _chain_transfer); + } static constexpr uint8_t OCD_TH_MAX = 31; static constexpr uint8_t STALL_TH_MAX = 31; @@ -422,22 +456,6 @@ class powerSTEP01 : public L64XX { //static constexpr float OCD_CURRENT_CONSTANT_INV = (1000 * 0.03125)/(POWERSTEP_AVERAGE_RDS); // mA per count (calc per data sheet - definitely wrong) //static constexpr float STALL_CURRENT_CONSTANT = OCD_CURRENT_CONSTANT; // counts per mA (calc per data sheet - definitely wrong) //static constexpr float STALL_CURRENT_CONSTANT_INV = OCD_CURRENT_CONSTANT_INV; // mA per count (calc per data sheet - definitely wrong) - - static constexpr uint8_t L64XX_CONFIG = 0x1A; - static constexpr uint8_t L64XX_STATUS = 0x1B; - - static constexpr bool L6470_status_layout = false; - static constexpr uint16_t STATUS_NOTPERF_CMD = 0x0080; // Last command not performed. - static constexpr uint16_t STATUS_WRONG_CMD = 0x0080; // Last command not valid. - static constexpr uint16_t STATUS_CMD_ERR = 0x0080; // Command error - static constexpr uint16_t STATUS_UVLO = 0x0200; // Undervoltage lockout is active - static constexpr uint16_t UVLO_ADC = 0x0400; // ADC undervoltage event - static constexpr uint16_t STATUS_TH_WRN = 0x0800; // Thermal warning - static constexpr uint16_t STATUS_TH_SD = 0x1000; // Thermal shutdown - static constexpr uint16_t STATUS_OCD = 0x2000; // Overcurrent detected - static constexpr uint16_t STATUS_STEP_LOSS_A = 0x4000; // Stall detected on A bridge - static constexpr uint16_t STATUS_STEP_LOSS_B = 0x8000; // Stall detected on B bridge - static constexpr uint16_t STATUS_SCK_MOD = 0x0100; // Step clock mode is active }; #endif // _L6470_H_ From a8c98b1696c9f718a4d704cf98674a0257ad61b2 Mon Sep 17 00:00:00 2001 From: Bob-the-Kuhn Date: Tue, 19 Mar 2019 07:59:46 -0500 Subject: [PATCH 09/14] pointer method - for use with the 19 MAR 2019 code in Marlin PR #13053 --- src/L6470.cpp | 76 +++++++------ src/L6470.h | 309 ++++++++++++++++++++++++++++++++------------------ 2 files changed, 243 insertions(+), 142 deletions(-) diff --git a/src/L6470.cpp b/src/L6470.cpp index c2400fe..0e3df2f 100644 --- a/src/L6470.cpp +++ b/src/L6470.cpp @@ -3,38 +3,28 @@ // ORIGINAL CODE 12 Dec 2011 Mike Hord, SparkFun Electronics // // // // LIBRARY Created by Adam Meyer (@ameyer) of bildr 18 Aug 2012 // -// Modified by Scott Lahteine (@thinkyhead) 6 Mar 2018 // -// Chain and SPI updates by Bob Kuhn (@bob-the-kuhn) 6 Jan 2019 // // Released as MIT license // // // +// Changes: // +// Scott Lahteine (@thinkyhead) - Cleanup 06 Mar 2018 // +// Bob Kuhn (@bob-the-kuhn) - Chain / SPI 06 Jan 2019 // +// Scott Lahteine (@thinkyhead) - Handlers 01 Mar 2019 // +// // //////////////////////////////////////////////////////////////////// #include "L6470.h" #include -uint8_t L64XX::chain[21]; - -L64XX::L64XX() {} - -L6470::L6470(const int16_t ss_pin) { - pin_SS = ss_pin; - // Serial.begin(9600); -} - -L6480::L6480(const int16_t ss_pin) { - pin_SS = ss_pin; - // Serial.begin(9600); -} +uint8_t chain_transfer_dummy(uint8_t data, const int16_t ss_pin, const uint8_t chain_position) {return 0;} +uint8_t transfer_dummy(uint8_t data, const int16_t ss_pin){return 0;} +void spi_init_dummy() {} -powerSTEP01::powerSTEP01(const int16_t ss_pin) { - pin_SS = ss_pin; - // Serial.begin(9600); -} +uint8_t L64XX::chain[21]; // Generic init function to set up communication with the dSPIN chip. -// call this after setting up the SPI(s) (after all "L64XX::set_pins" and "set_chain_info" commands) +// Call this after setting up the SPI(s) (after all "L64XX::set_pins" and "set_chain_info" commands) void L64XX::init() { if (pin_SS >= 0) { //init pin_SS if it has been set for this chip @@ -46,8 +36,8 @@ void L64XX::init() { // most significant bit first, // SPI clock not to exceed 5MHz, // SPI_MODE3 (clock idle high, latch data on rising edge of clock) - if (pin_SCK < 0) spi_init(); // using external SPI to init it - // internal SPI already initialized + if (pin_SCK < 0) spi_init(); // Use external SPI init function to init it + // internal SPI already initialized // First things first: let's check communications. The L64XX_CONFIG register should // power up to 0x2E88, so we can use that to check the communications. @@ -73,7 +63,7 @@ void L64XX::init() { // 16MHz internal oscillator, nothing on output SetParam(L64XX_CONFIG, CONFIG_PWM_DIV_1 | CONFIG_PWM_MUL_2 | CONFIG_SR_110V_us | CONFIG_OC_SD_DISABLE | CONFIG_VS_COMP_DISABLE | CONFIG_SW_HARD_STOP | CONFIG_INT_16MHZ); - // Configure the dSPIN_RUN KVAL. This defines the duty cycle of the PWM of the bridges + // Configure the dSPIN_RUN KVAL. This defines the duty cycle of the PWM of the bridges // during running. 0xFF means that they are essentially NOT PWMed during run; this // MAY result in more power being dissipated than you actually need for the task. // Setting this value too low may result in failure to turn. @@ -83,15 +73,15 @@ void L64XX::init() { SetParam(L6470_KVAL_ACC, 0xFF); SetParam(L6470_KVAL_DEC, 0xFF); -// Calling GetStatus() clears the UVLO bit in the status register, which is set by + // Calling getStatus() clears the UVLO bit in the status register, which is set by // default on power-up. The driver may not run without that bit cleared by this // read operation. getStatus(); - hardStop(); //engage motors + hardStop(); // engage motors } -// add to the chain array and save chain info for this stepper +// Add to the chain array and save chain info for this stepper void L64XX::set_chain_info(const uint8_t axis, const uint8_t chain_position) { if (chain_position) { chain[0]++; @@ -100,12 +90,12 @@ void L64XX::set_chain_info(const uint8_t axis, const uint8_t chain_position) { axis_index = axis; } else - chain[0] = 0; //reset array back to uninitialized + chain[0] = 0; // Reset array back to uninitialized } -// Sets optional pins for this stepper +// Set optional pins for this stepper // pin_SS is set by the instantiation call. -void L64XX::set_pins(const int16_t sck, const int16_t mosi, const int16_t miso, const int16_t reset, const int16_t busyn) { +void L64XX::set_pins(const _pin_t sck, const _pin_t mosi, const _pin_t miso, const _pin_t reset, const _pin_t busyn) { pin_SCK = sck; pin_MOSI = mosi; pin_MISO = miso; @@ -456,13 +446,33 @@ uint32_t L64XX::Param(uint32_t value, const uint8_t bit_len) { uint8_t L64XX::Xfer(uint8_t data) { - if (pin_SCK < 0) { // External SPI - return (uint8_t)( - position ? transfer(data, pin_SS, position) // ... in a chain - : transfer(data, pin_SS) // ... not chained + if (pin_SCK < 0) { // External SPI + + return (uint8_t) ( + position ? chain_transfer(data, pin_SS, position) // ... in a chain + : transfer(data, pin_SS) // ... not chained ); + +// return (uint8_t) ( +// position ? chain_transfer(data, pin_SS, position) // ... in a chain +// : transfer(data, pin_SS) // ... not chained +// ); + +// if (position) return L64XXHelper->transfer_chain(data, pin_SS, position); +// else return L64XXHelper->transfer_single(data, pin_SS); + +// return uint8_t( +// position ? helper->transfer_chain(data, pin_SS, position) +// : helper->transfer_single(data, pin_SS) +// ); } + +// position ? L6470_transfer(data, pin_SS, position) // ... in a chain +// : L6470_transfer(data, pin_SS) // ... not chained +// ... in a chain +// ... not chained + // if pin_SCK is set use internal soft SPI. if (position == 0) { // Internal soft SPI, not in a chain diff --git a/src/L6470.h b/src/L6470.h index 26abc5a..9ec9fad 100644 --- a/src/L6470.h +++ b/src/L6470.h @@ -3,12 +3,17 @@ // ORIGINAL CODE 12 Dec 2011 Mike Hord, SparkFun Electronics // // // // LIBRARY Created by Adam Meyer (@ameyer) of bildr 18 Aug 2012 // -// Modified by Scott Lahteine (@thinkyhead) 6 Mar 2018 // -// Chain and SPI updates by Bob Kuhn (@bob-the-kuhn) 6 Jan 2019 // // Released as MIT license // // // +// Changes: // +// Scott Lahteine (@thinkyhead) - Cleanup 06 Mar 2018 // +// Bob Kuhn (@bob-the-kuhn) - Chain / SPI 06 Jan 2019 // +// Scott Lahteine (@thinkyhead) - Handlers 01 Mar 2019 // +// // //////////////////////////////////////////////////////////////////// +#pragma once + #ifndef _L6470_H_ #define _L6470_H_ @@ -21,7 +26,7 @@ #include -#define L6470_LIBRARY_VERSION 0x000800 +#define L6470_LIBRARY_VERSION 0x000700 //#define SCK 10 // Wire this to the CSN pin //#define MOSI 11 // Wire this to the SDI pin @@ -155,7 +160,6 @@ #define CONFIG2_SR_520V_us 0x10 #define CONFIG2_SR_980V_us 0x30 - // L6480 & powerSTEP01 VCC setting #define PWR_VCC_7_5V 0 #define PWR_VCC_15V 0x0100 @@ -192,7 +196,6 @@ #define STATUS_DIR 0x0010 // Indicates current motor direction. // High is dSPIN_FWD, Low is dSPIN_REV. - // Status register motor status field #define STATUS_MOT_STATUS 0x0060 // field mask #define STATUS_MOT_STATUS_STOPPED (0x0000<<13) // Motor stopped @@ -236,7 +239,6 @@ #define PWR_TON_MIN 0X0F #define PWR_TOFF_MIN 0X10 - // dSPIN commands #define dSPIN_NOP 0x00 #define dSPIN_SET_PARAM 0x00 @@ -266,12 +268,69 @@ #define dSPIN_ACTION_RESET 0x00 #define dSPIN_ACTION_COPY 0x01 +typedef int16_t _pin_t; + + +uint8_t chain_transfer_dummy(uint8_t data, const int16_t ss_pin, const uint8_t chain_position); +uint8_t transfer_dummy(uint8_t data, const int16_t ss_pin); +void spi_init_dummy(); + + class L64XX { public: - L64XX(); + _pin_t pin_SS = -1, + pin_SCK = -1, + pin_MOSI = -1, + pin_MISO = -1, + pin_RESET = -1, + pin_BUSYN = -1; + + static uint8_t chain[21]; + // [0] - number of drivers in chain + // [1]... axis index for first device in the chain (closest to MOSI) + + uint8_t axis_index; + uint8_t position = 0; // 0 - not part of a chain + + +////////////////////////////////////////////////////////////////////////////////////////////////// + + // These methods must be supplied by the client + + typedef void (*spi_init_handler_t)(); + typedef uint8_t (*transfer_handler_t)(uint8_t data, const int16_t ss_pin); + typedef uint8_t (*chain_transfer_handler_t)(uint8_t data, const int16_t ss_pin, const uint8_t chain_position); + +// uint8_t *chain_transfer_dummy(uint8_t data, const int16_t ss_pin, const uint8_t chain_position) {return 0;} +// uint8_t *transfer_dummy(uint8_t data, const int16_t ss_pin){return 0;} +// void *spi_init_dummy() {} + + chain_transfer_handler_t chain_transfer = chain_transfer_dummy; + transfer_handler_t transfer = transfer_dummy; + spi_init_handler_t spi_init = spi_init_dummy; + + inline void set_spi_init_handler(spi_init_handler_t _spi_init) { spi_init = _spi_init; } + inline void set_transfer_handler(transfer_handler_t _transfer) { transfer = _transfer; } + inline void set_chain_transfer_handler(chain_transfer_handler_t _chain_transfer) { chain_transfer = _chain_transfer; } + + void set_handlers(spi_init_handler_t _spi_init, transfer_handler_t _transfer, chain_transfer_handler_t _chain_transfer) { + set_spi_init_handler(_spi_init); + set_transfer_handler(_transfer); + set_chain_transfer_handler(_chain_transfer); + } + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + + void init(); + inline void init(const _pin_t ss_pin) { pin_SS = ss_pin; } + + void set_pins(const _pin_t SCK, const _pin_t MOSI, const _pin_t MISO, const _pin_t RESET, const _pin_t BUSYN); + void set_chain_info(const uint8_t axis_index, const uint8_t position); + void setMicroSteps(int16_t microSteps); void setMaxSpeed(const int16_t speed); void setMinSpeed(const int16_t speed); @@ -316,51 +375,36 @@ class L64XX { void SetParam(const uint8_t param, const uint32_t value); uint32_t GetParam(const uint8_t param); - void set_chain_info(const uint8_t axis_index, const uint8_t position); - - void set_pins(const int16_t SCK, const int16_t MOSI, const int16_t MISO, const int16_t RESET, const int16_t BUSYN); - - int16_t pin_SS = -1, - pin_SCK = -1, - pin_MOSI = -1, - pin_MISO = -1, - pin_RESET = -1, - pin_BUSYN = -1; - - uint8_t axis_index; - uint8_t position = 0; // 0 - not part of a chain - - static uint8_t chain[21]; - // [0] - number of drivers in chain - // [1]... axis index for first device in the chain (closest to MOSI) - - // These methods must be supplied by the client - void spi_init(); - uint8_t transfer(uint8_t data, const int16_t ss_pin); // single device system - uint8_t transfer(uint8_t data, const int16_t ss_pin, const uint8_t axis); // chain system - - // L6470 placeholders may be overridden by sub-classes - static constexpr uint8_t OCD_TH_MAX = 15; - static constexpr uint8_t STALL_TH_MAX = 127; - static constexpr float OCD_CURRENT_CONSTANT_INV = 375; // mA per count - static constexpr float OCD_CURRENT_CONSTANT = 1.0f / OCD_CURRENT_CONSTANT_INV; // counts per mA - static constexpr float STALL_CURRENT_CONSTANT_INV = 31.25; // mA per count - static constexpr float STALL_CURRENT_CONSTANT = 1.0f / STALL_CURRENT_CONSTANT_INV; // counts per mA - - static constexpr uint8_t L64XX_CONFIG = 0x18; - static constexpr uint8_t L64XX_STATUS = 0x19; - - static constexpr bool L6470_status_layout = true; - static constexpr uint16_t STATUS_NOTPERF_CMD = 0x0080; // Last command not performed. - static constexpr uint16_t STATUS_WRONG_CMD = 0x0100; // Last command not valid. - static constexpr uint16_t STATUS_CMD_ERR = 0x0180; // Command error - static constexpr uint16_t STATUS_UVLO = 0x0200; // Undervoltage lockout is active - static constexpr uint16_t STATUS_TH_WRN = 0x0400; // Thermal warning - static constexpr uint16_t STATUS_TH_SD = 0x0800; // Thermal shutdown - static constexpr uint16_t STATUS_OCD = 0x1000; // Overcurrent detected - static constexpr uint16_t STATUS_STEP_LOSS_A = 0x2000; // Stall detected on A bridge - static constexpr uint16_t STATUS_STEP_LOSS_B = 0x4000; // Stall detected on B bridge - static constexpr uint16_t STATUS_SCK_MOD = 0x8000; // Step clock mode is active + // + // L6470 placeholders - will be set by sub-classes + // + + // current warning definitions + uint8_t OCD_TH_MAX; // max value of OCD_TH bit field + uint8_t STALL_TH_MAX; // max value of STALL_TH bit field + float OCD_CURRENT_CONSTANT_INV; // mA per count + float OCD_CURRENT_CONSTANT; // counts per mA + float STALL_CURRENT_CONSTANT_INV; // mA per count + float STALL_CURRENT_CONSTANT ; // counts per mA + + uint8_t L64XX_CONFIG; // CONFIG register address + uint8_t L64XX_STATUS; // STATUS register address + + bool L6470_status_layout; // true: L6470 layout, false: L6480/powerSTEP01 layout + + // status bit locations + uint16_t STATUS_NOTPERF_CMD; // Last command not performed. + uint16_t STATUS_WRONG_CMD; // Last command not valid. + uint16_t STATUS_CMD_ERR; // Command error + uint16_t STATUS_UVLO; // Undervoltage lockout is active + uint16_t STATUS_TH_WRN; // Thermal warning + uint16_t STATUS_TH_SD; // Thermal shutdown + uint16_t STATUS_OCD; // Overcurrent detected + uint16_t STATUS_STEP_LOSS_A; // Stall detected on A bridge + uint16_t STATUS_STEP_LOSS_B; // Stall detected on B bridge + uint16_t STATUS_SCK_MOD; // Step clock mode is active + + uint16_t UVLO_ADC; // ADC undervoltage event private: long convert(uint32_t val); @@ -374,70 +418,117 @@ class L64XX { uint32_t SpdCalc(const float stepsPerSec); uint32_t Param(uint32_t value, const uint8_t bit_len); uint8_t Xfer(uint8_t data); - uint8_t Xfer(uint8_t data, int16_t ss_pin, uint8_t position); + uint8_t Xfer(uint8_t data, _pin_t ss_pin, uint8_t position); }; -class L6470 : public L64XX { public: L6470(const int16_t pin_SS); }; +class L6470 : public L64XX { +public: + L6470(const _pin_t ss_pin) { + init(ss_pin); + L6470::OCD_TH_MAX = 15; + L6470::STALL_TH_MAX = 127; + L6470::OCD_CURRENT_CONSTANT_INV = 375; // mA per count + L6470::OCD_CURRENT_CONSTANT = 1.0f / OCD_CURRENT_CONSTANT_INV; // counts per mA + L6470::STALL_CURRENT_CONSTANT_INV = 31.25; // mA per count + L6470::STALL_CURRENT_CONSTANT = 1.0f / STALL_CURRENT_CONSTANT_INV; // counts per mA + + L6470::L64XX_CONFIG = 0x18; + L6470::L64XX_STATUS = 0x19; + + L6470::L6470_status_layout = true; + + L6470::STATUS_NOTPERF_CMD = 0x0080; // Last command not performed. + L6470::STATUS_WRONG_CMD = 0x0100; // Last command not valid. + L6470::STATUS_CMD_ERR = 0x0180; // Command error + L6470::STATUS_UVLO = 0x0200; // Undervoltage lockout is active + L6470::STATUS_TH_WRN = 0x0400; // Thermal warning + L6470::STATUS_TH_SD = 0x0800; // Thermal shutdown + L6470::STATUS_OCD = 0x1000; // Overcurrent detected + L6470::STATUS_STEP_LOSS_A = 0x2000; // Stall detected on A bridge + L6470::STATUS_STEP_LOSS_B = 0x4000; // Stall detected on B bridge + L6470::STATUS_SCK_MOD = 0x0001; // Step clock mode is active + + L6470::UVLO_ADC = 0x0400; // ADC undervoltage event + } +}; + +class L6480_Base : public L64XX { +public: + + +}; -class L6480 : public L64XX { +class L6480 : public L6480_Base { public: - L6480(const int16_t pin_SS); - - static constexpr uint8_t OCD_TH_MAX = 31; - static constexpr uint8_t STALL_TH_MAX = 31; - static constexpr float OCD_CURRENT_CONSTANT_INV = 31.25; // mA per count - static constexpr float OCD_CURRENT_CONSTANT = 1.0f / OCD_CURRENT_CONSTANT_INV; // counts per mA - static constexpr float STALL_CURRENT_CONSTANT_INV = 31.25; // mA per count - static constexpr float STALL_CURRENT_CONSTANT = 1.0f / STALL_CURRENT_CONSTANT_INV; // counts per mA - - static constexpr uint8_t L64XX_CONFIG = 0x1A; - static constexpr uint8_t L64XX_STATUS = 0x1B; - - static constexpr bool L6470_status_layout = false; - static constexpr uint16_t STATUS_NOTPERF_CMD = 0x0080; // Last command not performed. - static constexpr uint16_t STATUS_WRONG_CMD = 0x0080; // Last command not valid. - static constexpr uint16_t STATUS_CMD_ERR = 0x0080; // Command error - static constexpr uint16_t STATUS_UVLO = 0x0200; // Undervoltage lockout is active - static constexpr uint16_t UVLO_ADC = 0x0400; // ADC undervoltage event - static constexpr uint16_t STATUS_TH_WRN = 0x0800; // Thermal warning - static constexpr uint16_t STATUS_TH_SD = 0x1000; // Thermal shutdown - static constexpr uint16_t STATUS_OCD = 0x2000; // Overcurrent detected - static constexpr uint16_t STATUS_STEP_LOSS_A = 0x4000; // Stall detected on A bridge - static constexpr uint16_t STATUS_STEP_LOSS_B = 0x8000; // Stall detected on B bridge - static constexpr uint16_t STATUS_SCK_MOD = 0x0100; // Step clock mode is active + L6480(const _pin_t ss_pin) { + init(ss_pin); + + L6480::OCD_TH_MAX = 31; + L6480::STALL_TH_MAX = 31; + L6480::OCD_CURRENT_CONSTANT_INV = 31.25; // mA per count + L6480::OCD_CURRENT_CONSTANT = 1.0f / L6480::OCD_CURRENT_CONSTANT_INV; // counts per mA + L6480::STALL_CURRENT_CONSTANT_INV = 31.25; // mA per count + L6480::STALL_CURRENT_CONSTANT = 1.0f / L6480::STALL_CURRENT_CONSTANT_INV; // counts per mA + L6480::L6470_status_layout = false; + + L6480::L64XX_CONFIG = 0x1A; + L6480::L64XX_STATUS = 0x1B; + + L6470_status_layout = false; + + L6480::STATUS_WRONG_CMD = 0x0080; // Last command not valid. + L6480::STATUS_CMD_ERR = 0x0080; // Command error + L6480::STATUS_UVLO = 0x0200; // Undervoltage lockout is active + L6480::UVLO_ADC = 0x0400; // ADC undervoltage event + L6480::STATUS_TH_WRN = 0x0800; // Thermal warning + L6480::STATUS_TH_SD = 0x1000; // Thermal shutdown + L6480::STATUS_OCD = 0x2000; // Overcurrent detected + L6480::STATUS_STEP_LOSS_A = 0x4000; // Stall detected on A bridge + L6480::STATUS_STEP_LOSS_B = 0x8000; // Stall detected on B bridge + L6480::STATUS_SCK_MOD = 0x0100; // Step clock mode is active + } + + //static constexpr float L6480_AVERAGE_RDS = 0.016; // Ohms - L6480 use external FETs so this may be user modified + //L6480::OCD_CURRENT_CONSTANT = (L6480_AVERAGE_RDS/0.03125)/1000; // counts per mA (calc per data sheet - definitely wrong) + //L6480::OCD_CURRENT_CONSTANT_INV = (1000 * 0.03125)/(L6480_AVERAGE_RDS); // mA per count (calc per data sheet - definitely wrong) + //L6480::STALL_CURRENT_CONSTANT = OCD_CURRENT_CONSTANT; // counts per mA (calc per data sheet - definitely wrong) + //L6480::STALL_CURRENT_CONSTANT_INV = OCD_CURRENT_CONSTANT_INV; // mA per count (calc per data sheet - definitely wrong) }; -class powerSTEP01 : public L64XX { +class powerSTEP01 : public L6480_Base { public: - powerSTEP01(const int16_t pin_SS); - - static constexpr uint8_t OCD_TH_MAX = 31; - static constexpr uint8_t STALL_TH_MAX = 31; - static constexpr float OCD_CURRENT_CONSTANT = 0.001; // counts per mA (empirically derived for powerSTEP01) - static constexpr float OCD_CURRENT_CONSTANT_INV = 1000; // mA per count (empirically derived for powerSTEP01) - static constexpr float STALL_CURRENT_CONSTANT = 0.005; // counts per mA (empirically derived for powerSTEP01) - static constexpr float STALL_CURRENT_CONSTANT_INV = 200; // mA per count (empirically derived for powerSTEP01) - //static constexpr float POWERSTEP_AVERAGE_RDS = 0.016; // Ohms - L648x use external FETs so this may be user modified - //static constexpr float OCD_CURRENT_CONSTANT = (POWERSTEP_AVERAGE_RDS/0.03125)/1000; // counts per mA (calc per data sheet - definitely wrong) - //static constexpr float OCD_CURRENT_CONSTANT_INV = (1000 * 0.03125)/(POWERSTEP_AVERAGE_RDS); // mA per count (calc per data sheet - definitely wrong) - //static constexpr float STALL_CURRENT_CONSTANT = OCD_CURRENT_CONSTANT; // counts per mA (calc per data sheet - definitely wrong) - //static constexpr float STALL_CURRENT_CONSTANT_INV = OCD_CURRENT_CONSTANT_INV; // mA per count (calc per data sheet - definitely wrong) - - static constexpr uint8_t L64XX_CONFIG = 0x1A; - static constexpr uint8_t L64XX_STATUS = 0x1B; - - static constexpr bool L6470_status_layout = false; - static constexpr uint16_t STATUS_NOTPERF_CMD = 0x0080; // Last command not performed. - static constexpr uint16_t STATUS_WRONG_CMD = 0x0080; // Last command not valid. - static constexpr uint16_t STATUS_CMD_ERR = 0x0080; // Command error - static constexpr uint16_t STATUS_UVLO = 0x0200; // Undervoltage lockout is active - static constexpr uint16_t UVLO_ADC = 0x0400; // ADC undervoltage event - static constexpr uint16_t STATUS_TH_WRN = 0x0800; // Thermal warning - static constexpr uint16_t STATUS_TH_SD = 0x1000; // Thermal shutdown - static constexpr uint16_t STATUS_OCD = 0x2000; // Overcurrent detected - static constexpr uint16_t STATUS_STEP_LOSS_A = 0x4000; // Stall detected on A bridge - static constexpr uint16_t STATUS_STEP_LOSS_B = 0x8000; // Stall detected on B bridge - static constexpr uint16_t STATUS_SCK_MOD = 0x0100; // Step clock mode is active + powerSTEP01(const _pin_t ss_pin) { + init(ss_pin); + + powerSTEP01::OCD_TH_MAX = 31; + powerSTEP01::STALL_TH_MAX = 31; + powerSTEP01::OCD_CURRENT_CONSTANT = 0.001; // counts per mA (empirically derived for powerSTEP01) + powerSTEP01::OCD_CURRENT_CONSTANT_INV = 1000; // mA per count (empirically derived for powerSTEP01) + powerSTEP01::STALL_CURRENT_CONSTANT = 0.005; // counts per mA (empirically derived for powerSTEP01) + powerSTEP01::STALL_CURRENT_CONSTANT_INV = 200; // mA per count (empirically derived for powerSTEP01) + + powerSTEP01::L64XX_CONFIG = 0x1A; + powerSTEP01::L64XX_STATUS = 0x1B; + + powerSTEP01::L6470_status_layout = false; + + powerSTEP01::STATUS_WRONG_CMD = 0x0080; // Last command not valid. + powerSTEP01::STATUS_CMD_ERR = 0x0080; // Command error + powerSTEP01::STATUS_UVLO = 0x0200; // Undervoltage lockout is active + powerSTEP01::UVLO_ADC = 0x0400; // ADC undervoltage event + powerSTEP01::STATUS_TH_WRN = 0x0800; // Thermal warning + powerSTEP01::STATUS_TH_SD = 0x1000; // Thermal shutdown + powerSTEP01::STATUS_OCD = 0x2000; // Overcurrent detected + powerSTEP01::STATUS_STEP_LOSS_A = 0x4000; // Stall detected on A bridge + powerSTEP01::STATUS_STEP_LOSS_B = 0x8000; // Stall detected on B bridge + powerSTEP01::STATUS_SCK_MOD = 0x0100; // Step clock mode is active + } + + //static constexpr float POWERSTEP_AVERAGE_RDS = 0.016; // Ohms + //powerSTEP01::OCD_CURRENT_CONSTANT = (POWERSTEP_AVERAGE_RDS/0.03125)/1000; // counts per mA (calc per data sheet - definitely wrong) + //powerSTEP01::OCD_CURRENT_CONSTANT_INV = (1000 * 0.03125)/(POWERSTEP_AVERAGE_RDS); // mA per count (calc per data sheet - definitely wrong) + //powerSTEP01::STALL_CURRENT_CONSTANT = OCD_CURRENT_CONSTANT; // counts per mA (calc per data sheet - definitely wrong) + //powerSTEP01::STALL_CURRENT_CONSTANT_INV = OCD_CURRENT_CONSTANT_INV; // mA per count (calc per data sheet - definitely wrong) }; #endif // _L6470_H_ From 6ef4e4b4312095bdf0c7391258b2985ab24f8341 Mon Sep 17 00:00:00 2001 From: Bob-the-Kuhn Date: Sat, 18 May 2019 01:10:19 -0500 Subject: [PATCH 10/14] cahnge to helper methd - same files as used for Marlin PR #13498 --- src/L6470.cpp | 37 ++++----------- src/L6470.h | 125 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 128 insertions(+), 34 deletions(-) diff --git a/src/L6470.cpp b/src/L6470.cpp index 928c6cc..d61d7ef 100644 --- a/src/L6470.cpp +++ b/src/L6470.cpp @@ -8,7 +8,7 @@ // Changes: // // Scott Lahteine (@thinkyhead) - Cleanup 06 Mar 2018 // // Bob Kuhn (@bob-the-kuhn) - Chain / SPI 06 Jan 2019 // -// Scott Lahteine (@thinkyhead) - Handlers 01 Mar 2019 // +// Scott Lahteine (@thinkyhead) - L64XXHelper 01 Mar 2019 // // // //////////////////////////////////////////////////////////////////// @@ -16,12 +16,16 @@ #include -uint8_t chain_transfer_dummy(uint8_t data, const int16_t ss_pin, const uint8_t chain_position) {return 0;} -uint8_t transfer_dummy(uint8_t data, const int16_t ss_pin){return 0;} -void spi_init_dummy() {} +L64XXHelper nullHelper; +L64XXHelper* L64XX::helper = &nullHelper; uint8_t L64XX::chain[21]; +// stub routines +uint8_t chain_transfer_dummy(uint8_t data, const int16_t ss_pin, const uint8_t chain_position) { return 0; } +uint8_t transfer_dummy(uint8_t data, const int16_t ss_pin) { return 0; } +void spi_init_dummy(){}; + // Generic init function to set up communication with the dSPIN chip. // Call this after setting up the SPI(s) (after all "L64XX::set_pins" and "set_chain_info" commands) @@ -36,7 +40,7 @@ void L64XX::init() { // most significant bit first, // SPI clock not to exceed 5MHz, // SPI_MODE3 (clock idle high, latch data on rising edge of clock) - if (pin_SCK < 0) spi_init(); // Use external SPI init function to init it + if (pin_SCK < 0) helper->spi_init(); // Use external SPI init function to init it // internal SPI already initialized // First things first: let's check communications. The L64XX_CONFIG register should @@ -446,34 +450,13 @@ uint32_t L64XX::Param(uint32_t value, const uint8_t bit_len) { uint8_t L64XX::Xfer(uint8_t data) { - if (pin_SCK < 0) { // External SPI + if (pin_SCK < 0) // External SPI return (uint8_t) ( position ? chain_transfer(data, pin_SS, position) // ... in a chain : transfer(data, pin_SS) // ... not chained ); -// return (uint8_t) ( -// position ? chain_transfer(data, pin_SS, position) // ... in a chain -// : transfer(data, pin_SS) // ... not chained -// ); - -// if (position) return L64XXHelper->transfer_chain(data, pin_SS, position); -// else return L64XXHelper->transfer_single(data, pin_SS); - -// return uint8_t( -// position ? helper->transfer_chain(data, pin_SS, position) -// : helper->transfer_single(data, pin_SS) -// ); - } - - -// position ? L6470_transfer(data, pin_SS, position) // ... in a chain -// : L6470_transfer(data, pin_SS) // ... not chained -// ... in a chain -// ... not chained - // if pin_SCK is set use internal soft SPI. - if (position == 0) { // Internal soft SPI, not in a chain if (pin_SS >= 0) digitalWrite(pin_SS, LOW); // Allow external code to control SS_PIN uint8_t bits = 8; diff --git a/src/L6470.h b/src/L6470.h index 53e365c..a8f468d 100644 --- a/src/L6470.h +++ b/src/L6470.h @@ -8,7 +8,7 @@ // Changes: // // Scott Lahteine (@thinkyhead) - Cleanup 06 Mar 2018 // // Bob Kuhn (@bob-the-kuhn) - Chain / SPI 06 Jan 2019 // -// Scott Lahteine (@thinkyhead) - Handlers 01 Mar 2019 // +// Scott Lahteine (@thinkyhead) - L64XXHelper 01 Mar 2019 // // // //////////////////////////////////////////////////////////////////// @@ -270,12 +270,31 @@ typedef int16_t _pin_t; +typedef void (*spi_init_handler_t)(); +typedef uint8_t (*transfer_handler_t)(uint8_t data, const int16_t ss_pin); +typedef uint8_t (*chain_transfer_handler_t)(uint8_t data, const int16_t ss_pin, const uint8_t chain_position); uint8_t chain_transfer_dummy(uint8_t data, const int16_t ss_pin, const uint8_t chain_position); uint8_t transfer_dummy(uint8_t data, const int16_t ss_pin); void spi_init_dummy(); +class L64XXHelper { +//protected: +public: + friend class L64XX; + friend class L6470; + friend class L6480; + friend class powerSTEP01; + static inline void spi_init() { } + static inline uint8_t transfer(uint8_t data, const _pin_t ss_pin) { return 0; } + static inline uint8_t transfer(uint8_t data, const _pin_t ss_pin, const uint8_t chain_position) { return 0; } + static inline uint8_t transfer_single(uint8_t data, const _pin_t ss_pin) { return 0; } + static inline uint8_t transfer_chain(uint8_t data, const _pin_t ss_pin, const uint8_t chain_position) { return 0; } +}; + +extern L64XXHelper nullHelper; + class L64XX { public: _pin_t pin_SS = -1, @@ -292,14 +311,14 @@ class L64XX { uint8_t axis_index; uint8_t position = 0; // 0 - not part of a chain -////////////////////////////////////////////////////////////////////////////////////////////////// + // This object must be supplied by the client + static L64XXHelper *helper; - // These methods must be supplied by the client + static inline void set_helper(L64XXHelper *_helper) { helper = _helper; } +////////////////////////////////////////////////////////////////////////////////////////////////// - typedef void (*spi_init_handler_t)(); - typedef uint8_t (*transfer_handler_t)(uint8_t data, const int16_t ss_pin); - typedef uint8_t (*chain_transfer_handler_t)(uint8_t data, const int16_t ss_pin, const uint8_t chain_position); + // These methods must be supplied by the client chain_transfer_handler_t chain_transfer = chain_transfer_dummy; transfer_handler_t transfer = transfer_dummy; @@ -315,6 +334,7 @@ class L64XX { set_chain_transfer_handler(_chain_transfer); } + //////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -322,9 +342,12 @@ class L64XX { void init(); inline void init(const _pin_t ss_pin) { pin_SS = ss_pin; } + inline void init(const _pin_t ss_pin, L64XXHelper *_helper) { + pin_SS = ss_pin; + set_helper(_helper); + } void set_pins(const _pin_t SCK, const _pin_t MOSI, const _pin_t MISO, const _pin_t RESET, const _pin_t BUSYN); - void set_chain_info(const uint8_t axis_index, const uint8_t position); void setMicroSteps(int16_t microSteps); @@ -446,6 +469,36 @@ class L6470 : public L64XX { L6470::UVLO_ADC = 0x0400; // ADC undervoltage event } + + L6470(const _pin_t ss_pin, L64XXHelper *_helper) { + init(ss_pin, _helper); + + L6470::OCD_TH_MAX = 15; + L6470::STALL_TH_MAX = 127; + L6470::OCD_CURRENT_CONSTANT_INV = 375; // mA per count + L6470::OCD_CURRENT_CONSTANT = 1.0f / OCD_CURRENT_CONSTANT_INV; // counts per mA + L6470::STALL_CURRENT_CONSTANT_INV = 31.25; // mA per count + L6470::STALL_CURRENT_CONSTANT = 1.0f / STALL_CURRENT_CONSTANT_INV; // counts per mA + + L6470::L64XX_CONFIG = 0x18; + L6470::L64XX_STATUS = 0x19; + + L6470::L6470_status_layout = true; + + L6470::STATUS_NOTPERF_CMD = 0x0080; // Last command not performed. + L6470::STATUS_WRONG_CMD = 0x0100; // Last command not valid. + L6470::STATUS_CMD_ERR = 0x0180; // Command error + L6470::STATUS_UVLO = 0x0200; // Undervoltage lockout is active + L6470::STATUS_TH_WRN = 0x0400; // Thermal warning + L6470::STATUS_TH_SD = 0x0800; // Thermal shutdown + L6470::STATUS_OCD = 0x1000; // Overcurrent detected + L6470::STATUS_STEP_LOSS_A = 0x2000; // Stall detected on A bridge + L6470::STATUS_STEP_LOSS_B = 0x4000; // Stall detected on B bridge + L6470::STATUS_SCK_MOD = 0x0001; // Step clock mode is active + + L6470::UVLO_ADC = 0x0400; // ADC undervoltage event + } + }; class L6480_Base : public L64XX { @@ -484,11 +537,41 @@ class L6480 : public L6480_Base { L6480::STATUS_SCK_MOD = 0x0100; // Step clock mode is active } + L6480(const _pin_t ss_pin, L64XXHelper *_helper) { + init(ss_pin, _helper); + + L6480::OCD_TH_MAX = 31; + L6480::STALL_TH_MAX = 31; + L6480::OCD_CURRENT_CONSTANT_INV = 31.25; // mA per count + L6480::OCD_CURRENT_CONSTANT = 1.0f / L6480::OCD_CURRENT_CONSTANT_INV; // counts per mA + L6480::STALL_CURRENT_CONSTANT_INV = 31.25; // mA per count + L6480::STALL_CURRENT_CONSTANT = 1.0f / L6480::STALL_CURRENT_CONSTANT_INV; // counts per mA + L6480::L6470_status_layout = false; + + L6480::L64XX_CONFIG = 0x1A; + L6480::L64XX_STATUS = 0x1B; + + L6470_status_layout = false; + + L6480::STATUS_WRONG_CMD = 0x0080; // Last command not valid. + L6480::STATUS_CMD_ERR = 0x0080; // Command error + L6480::STATUS_UVLO = 0x0200; // Undervoltage lockout is active + L6480::UVLO_ADC = 0x0400; // ADC undervoltage event + L6480::STATUS_TH_WRN = 0x0800; // Thermal warning + L6480::STATUS_TH_SD = 0x1000; // Thermal shutdown + L6480::STATUS_OCD = 0x2000; // Overcurrent detected + L6480::STATUS_STEP_LOSS_A = 0x4000; // Stall detected on A bridge + L6480::STATUS_STEP_LOSS_B = 0x8000; // Stall detected on B bridge + L6480::STATUS_SCK_MOD = 0x0100; // Step clock mode is active + } + //static constexpr float L6480_AVERAGE_RDS = 0.016; // Ohms - L6480 use external FETs so this may be user modified //L6480::OCD_CURRENT_CONSTANT = (L6480_AVERAGE_RDS/0.03125)/1000; // counts per mA (calc per data sheet - definitely wrong) //L6480::OCD_CURRENT_CONSTANT_INV = (1000 * 0.03125)/(L6480_AVERAGE_RDS); // mA per count (calc per data sheet - definitely wrong) //L6480::STALL_CURRENT_CONSTANT = OCD_CURRENT_CONSTANT; // counts per mA (calc per data sheet - definitely wrong) //L6480::STALL_CURRENT_CONSTANT_INV = OCD_CURRENT_CONSTANT_INV; // mA per count (calc per data sheet - definitely wrong) + + }; class powerSTEP01 : public L6480_Base { @@ -520,6 +603,34 @@ class powerSTEP01 : public L6480_Base { powerSTEP01::STATUS_SCK_MOD = 0x0100; // Step clock mode is active } + powerSTEP01(const _pin_t ss_pin, L64XXHelper *_helper) { + init(ss_pin, _helper); + + powerSTEP01::OCD_TH_MAX = 5; + powerSTEP01::STALL_TH_MAX = 5; + powerSTEP01::OCD_CURRENT_CONSTANT = 0.001; // counts per mA (empirically derived for powerSTEP01) + powerSTEP01::OCD_CURRENT_CONSTANT_INV = 1000; // mA per count (empirically derived for powerSTEP01) + powerSTEP01::STALL_CURRENT_CONSTANT = 0.005; // counts per mA (empirically derived for powerSTEP01) + powerSTEP01::STALL_CURRENT_CONSTANT_INV = 200; // mA per count (empirically derived for powerSTEP01) + + powerSTEP01::L64XX_CONFIG = 0x1A; + powerSTEP01::L64XX_STATUS = 0x1B; + + powerSTEP01::L6470_status_layout = false; + + powerSTEP01::STATUS_WRONG_CMD = 0x0080; // Last command not valid. + powerSTEP01::STATUS_CMD_ERR = 0x0080; // Command error + powerSTEP01::STATUS_UVLO = 0x0200; // Undervoltage lockout is active + powerSTEP01::UVLO_ADC = 0x0400; // ADC undervoltage event + powerSTEP01::STATUS_TH_WRN = 0x0800; // Thermal warning + powerSTEP01::STATUS_TH_SD = 0x1000; // Thermal shutdown + powerSTEP01::STATUS_OCD = 0x2000; // Overcurrent detected + powerSTEP01::STATUS_STEP_LOSS_A = 0x4000; // Stall detected on A bridge + powerSTEP01::STATUS_STEP_LOSS_B = 0x8000; // Stall detected on B bridge + powerSTEP01::STATUS_SCK_MOD = 0x0100; // Step clock mode is active + } + + //static constexpr float POWERSTEP_AVERAGE_RDS = 0.016; // Ohms //powerSTEP01::OCD_CURRENT_CONSTANT = (POWERSTEP_AVERAGE_RDS/0.03125)/1000; // counts per mA (calc per data sheet - definitely wrong) //powerSTEP01::OCD_CURRENT_CONSTANT_INV = (1000 * 0.03125)/(POWERSTEP_AVERAGE_RDS); // mA per count (calc per data sheet - definitely wrong) From 68be15e62a7b5476dd626a4ef4fd335e0f7d05d6 Mon Sep 17 00:00:00 2001 From: Bob-the-Kuhn Date: Mon, 30 Dec 2019 02:20:02 -0600 Subject: [PATCH 11/14] add L6474 support --- src/L6470.cpp | 18 ++++++-- src/L6470.h | 113 ++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 106 insertions(+), 25 deletions(-) diff --git a/src/L6470.cpp b/src/L6470.cpp index d61d7ef..c3b6baf 100644 --- a/src/L6470.cpp +++ b/src/L6470.cpp @@ -135,7 +135,7 @@ void L64XX::set_pins(const _pin_t sck, const _pin_t mosi, const _pin_t miso, con } } -boolean L64XX::isBusy() { return !(getStatus() & 0x0002); } +uint8_t L64XX::isBusy() { return !(getStatus() & 0x0002); } void L64XX::setMicroSteps(int16_t microSteps) { uint8_t stepVal; @@ -143,7 +143,11 @@ void L64XX::setMicroSteps(int16_t microSteps) { if (microSteps == 1) break; microSteps >>= 1; } - SetParam(L6470_STEP_MODE, !SYNC_EN | stepVal | SYNC_SEL_1); + + if (L6470_status_layout == L6474_STATUS_LAYOUT) + SetParam(L6470_STEP_MODE, 0x98 | stepVal); //no sync + else + SetParam(L6470_STEP_MODE, (!SYNC_EN | SYNC_SEL_1 | stepVal)); } // Configure the L6470_FS_SPD register- this is the speed at which the driver ceases @@ -208,12 +212,20 @@ void L64XX::setStallCurrent(float ma_current) { SetParam(L6470_STALL_TH, STHValue < STALL_TH_MAX ? STHValue : STALL_TH_MAX); } +// only in L6474 +// NOTE - TVAL register address is the same as the KVAL_HOLD register on the other L64xx devices +void L64XX::setTVALCurrent(float ma_current) { + if (ma_current > STALL_CURRENT_CONSTANT_INV * (STALL_TH_MAX +1)) ma_current = STALL_CURRENT_CONSTANT_INV * (STALL_TH_MAX +1); // keep the STALL_TH calc from overflowing 8 bits + const uint8_t STHValue = (uint8_t)floor(uint8_t(ma_current * STALL_CURRENT_CONSTANT - 1)); + SetParam(L6474_TVAL, STHValue < STALL_TH_MAX ? STHValue : STALL_TH_MAX); +} + // Enable or disable the low-speed optimization option. If enabling, // the other 12 bits of the register will be automatically zero. // When disabling, the value will have to be explicitly written by // the user with a SetParam() call. See the datasheet for further // information about low-speed optimization. -void L64XX::SetLowSpeedOpt(const boolean enable) { +void L64XX::SetLowSpeedOpt(const uint8_t enable) { Xfer(dSPIN_SET_PARAM | L6470_MIN_SPEED); Param(enable ? 0x1000 : 0, 13); } diff --git a/src/L6470.h b/src/L6470.h index a8f468d..c8ea5a0 100644 --- a/src/L6470.h +++ b/src/L6470.h @@ -26,7 +26,7 @@ #include -#define L6470_LIBRARY_VERSION 0x000700 +#define L6470_LIBRARY_VERSION 0x000800 //#define SCK 10 // Wire this to the CSN pin //#define MOSI 11 // Wire this to the SDI pin @@ -123,6 +123,8 @@ #define CONFIG_EXT_24MHZ_OSCOUT_INVERT 0x000E // External 24MHz crystal, output inverted #define CONFIG_EXT_32MHZ_OSCOUT_INVERT 0x000F // External 32MHz crystal, output inverted +#define CONFIG_EN_TQREG 0x0020 // L6474 only - enable setting output slew rate + // Configure the functionality of the external switch input #define CONFIG_SW_MODE 0x0010 // Mask for this bit. #define CONFIG_SW_HARD_STOP 0x0000 // Default; hard stop motor on switch. @@ -141,7 +143,7 @@ #define CONFIG_OC_SD_ENABLE 0x0080 // Bridges shutdown on OC detect // Configure the slew rate of the power bridge output -// L6470 +// L6470 & L6474 #define CONFIG_POW_SR 0x0300 // Mask for this bit field. #define CONFIG_POW_SR_BIT 8 // starting bit of this field #define CONFIG_SR_320V_us 0x0000 // 320V/us @@ -186,6 +188,12 @@ #define CONFIG_PWM_DIV_6 (0x05<<13) #define CONFIG_PWM_DIV_7 (0x06<<13) +// status register layouts +#define L6470_STATUS_LAYOUT 0x0000 +#define L6474_STATUS_LAYOUT 0x0001 +#define L6480_STATUS_LAYOUT 0x0003 + + // Status register bit renames- read-only bits conferring information about the // device to the user. #define STATUS_HIZ 0x0001 // high when bridges are in HiZ mode @@ -231,6 +239,8 @@ #define L6470_GATECFG1 0x18 // L6480 & powerSTEP01 only #define L6470_GATECFG2 0x19 // L6480 & powerSTEP01 only +#define L6474_TVAL 0x09 // L6474 only + #define PWR_TVAL_HOLD 0X09 // powerSTEP01 current mode register names #define PWR_TVAL_RUN 0X0A #define PWR_TVAL_ACC 0X0B @@ -256,6 +266,7 @@ #define dSPIN_RESET_DEVICE 0xC0 #define dSPIN_SOFT_STOP 0xB0 #define dSPIN_HARD_STOP 0xB8 +#define dSPIN_L6474_ENABLE dSPIN_HARD_STOP #define dSPIN_SOFT_HIZ 0xA0 #define dSPIN_HARD_HIZ 0xA8 #define dSPIN_GET_STATUS 0xD0 @@ -356,11 +367,12 @@ class L64XX { void setAcc(const float acceleration); void setDec(const float deceleration); void setOverCurrent(float ma_current); + void setTVALCurrent(float ma_current); void setThresholdSpeed(const float threshold); void setStallCurrent(float ma_current); uint32_t ParamHandler(const uint8_t param, const uint32_t value); - void SetLowSpeedOpt(boolean enable); + void SetLowSpeedOpt(uint8_t enable); void run(const uint8_t dir, const float spd); void Step_Clock(const uint8_t dir); @@ -374,7 +386,7 @@ class L64XX { void goTo_DIR(const uint8_t dir, long pos); void goUntil(const uint8_t act, const uint8_t dir, uint32_t spd); - boolean isBusy(); + uint8_t isBusy(); void releaseSW(const uint8_t act, const uint8_t dir); @@ -409,7 +421,7 @@ class L64XX { uint8_t L64XX_CONFIG; // CONFIG register address uint8_t L64XX_STATUS; // STATUS register address - bool L6470_status_layout; // true: L6470 layout, false: L6480/powerSTEP01 layout + uint8_t L6470_status_layout; // true: L6470 layout, false: L6480/powerSTEP01 layout // status bit locations uint16_t STATUS_NOTPERF_CMD; // Last command not performed. @@ -446,7 +458,7 @@ class L6470 : public L64XX { init(ss_pin); L6470::OCD_TH_MAX = 15; L6470::STALL_TH_MAX = 127; - L6470::OCD_CURRENT_CONSTANT_INV = 375; // mA per count + L6470::OCD_CURRENT_CONSTANT_INV = 375.0f; // mA per count L6470::OCD_CURRENT_CONSTANT = 1.0f / OCD_CURRENT_CONSTANT_INV; // counts per mA L6470::STALL_CURRENT_CONSTANT_INV = 31.25; // mA per count L6470::STALL_CURRENT_CONSTANT = 1.0f / STALL_CURRENT_CONSTANT_INV; // counts per mA @@ -454,7 +466,7 @@ class L6470 : public L64XX { L6470::L64XX_CONFIG = 0x18; L6470::L64XX_STATUS = 0x19; - L6470::L6470_status_layout = true; + L6470::L6470_status_layout = L6470_STATUS_LAYOUT; L6470::STATUS_NOTPERF_CMD = 0x0080; // Last command not performed. L6470::STATUS_WRONG_CMD = 0x0100; // Last command not valid. @@ -475,7 +487,7 @@ class L6470 : public L64XX { L6470::OCD_TH_MAX = 15; L6470::STALL_TH_MAX = 127; - L6470::OCD_CURRENT_CONSTANT_INV = 375; // mA per count + L6470::OCD_CURRENT_CONSTANT_INV = 375.0f; // mA per count L6470::OCD_CURRENT_CONSTANT = 1.0f / OCD_CURRENT_CONSTANT_INV; // counts per mA L6470::STALL_CURRENT_CONSTANT_INV = 31.25; // mA per count L6470::STALL_CURRENT_CONSTANT = 1.0f / STALL_CURRENT_CONSTANT_INV; // counts per mA @@ -483,7 +495,7 @@ class L6470 : public L64XX { L6470::L64XX_CONFIG = 0x18; L6470::L64XX_STATUS = 0x19; - L6470::L6470_status_layout = true; + L6470::L6470_status_layout = L6470_STATUS_LAYOUT; L6470::STATUS_NOTPERF_CMD = 0x0080; // Last command not performed. L6470::STATUS_WRONG_CMD = 0x0100; // Last command not valid. @@ -501,6 +513,70 @@ class L6470 : public L64XX { }; + + +class L6474 : public L64XX { +public: + L6474(const _pin_t ss_pin) { + init(ss_pin); + L6474::OCD_TH_MAX = 15; + L6474::STALL_TH_MAX = 127; // STALL function not implemented on L6474 + L6474::OCD_CURRENT_CONSTANT_INV = 375.0f; // mA per count + L6474::OCD_CURRENT_CONSTANT = 1.0f / OCD_CURRENT_CONSTANT_INV; // counts per mA + L6474::STALL_CURRENT_CONSTANT_INV = 31.25; // mA per count + L6474::STALL_CURRENT_CONSTANT = 1.0f / STALL_CURRENT_CONSTANT_INV; // counts per mA + + L6474::L64XX_CONFIG = 0x18; + L6474::L64XX_STATUS = 0x19; + + L6474::L6470_status_layout = L6474_STATUS_LAYOUT; + + L6474::STATUS_NOTPERF_CMD = 0x0080; // Last command not performed. + L6474::STATUS_WRONG_CMD = 0x0100; // Last command not valid + L6474::STATUS_CMD_ERR = 0x0000; // Command error - not implemented on L6474 + L6474::STATUS_UVLO = 0x0200; // Undervoltage lockout is active + L6474::STATUS_TH_WRN = 0x0400; // Thermal warning + L6474::STATUS_TH_SD = 0x0800; // Thermal shutdown + L6474::STATUS_OCD = 0x1000; // Overcurrent detected + L6474::STATUS_STEP_LOSS_A = 0x0000; // Stall detected on A bridge - not implemented on L6474 + L6474::STATUS_STEP_LOSS_B = 0x0000; // Stall detected on B bridge - not implemented on L6474 + L6474::STATUS_SCK_MOD = 0x0000; // Step clock mode is active - not implemented on L6474 + + L6474::UVLO_ADC = 0x0000; // ADC undervoltage event - not implemented on L6474 + } + + L6474(const _pin_t ss_pin, L64XXHelper *_helper) { + init(ss_pin, _helper); + + L6474::OCD_TH_MAX = 15; + L6474::STALL_TH_MAX = 127; + L6474::OCD_CURRENT_CONSTANT_INV = 375.0f; // mA per count + L6474::OCD_CURRENT_CONSTANT = 1.0f / OCD_CURRENT_CONSTANT_INV; // counts per mA + L6474::STALL_CURRENT_CONSTANT_INV = 31.25; // mA per count + L6474::STALL_CURRENT_CONSTANT = 1.0f / STALL_CURRENT_CONSTANT_INV; // counts per mA + + L6474::L64XX_CONFIG = 0x18; + L6474::L64XX_STATUS = 0x19; + + L6474::L6470_status_layout = L6474_STATUS_LAYOUT; + + L6474::STATUS_NOTPERF_CMD = 0x0080; // Last command not performed. + L6474::STATUS_WRONG_CMD = 0x0100; // Last command not valid + L6474::STATUS_CMD_ERR = 0x0000; // Command error - not implemented on L6474 + L6474::STATUS_UVLO = 0x0200; // Undervoltage lockout is active + L6474::STATUS_TH_WRN = 0x0400; // Thermal warning + L6474::STATUS_TH_SD = 0x0800; // Thermal shutdown + L6474::STATUS_OCD = 0x1000; // Overcurrent detected + L6474::STATUS_STEP_LOSS_A = 0x0000; // Stall detected on A bridge - not implemented on L6474 + L6474::STATUS_STEP_LOSS_B = 0x0000; // Stall detected on B bridge - not implemented on L6474 + L6474::STATUS_SCK_MOD = 0x0000; // Step clock mode is active - not implemented on L6474 + + L6474::UVLO_ADC = 0x0000; // ADC undervoltage event - not implemented on L6474 + } + +}; + + class L6480_Base : public L64XX { public: @@ -518,12 +594,12 @@ class L6480 : public L6480_Base { L6480::OCD_CURRENT_CONSTANT = 1.0f / L6480::OCD_CURRENT_CONSTANT_INV; // counts per mA L6480::STALL_CURRENT_CONSTANT_INV = 31.25; // mA per count L6480::STALL_CURRENT_CONSTANT = 1.0f / L6480::STALL_CURRENT_CONSTANT_INV; // counts per mA - L6480::L6470_status_layout = false; + L6480::L6470_status_layout = L6480_STATUS_LAYOUT; L6480::L64XX_CONFIG = 0x1A; L6480::L64XX_STATUS = 0x1B; - L6470_status_layout = false; + L6470_status_layout = L6480_STATUS_LAYOUT; L6480::STATUS_WRONG_CMD = 0x0080; // Last command not valid. L6480::STATUS_CMD_ERR = 0x0080; // Command error @@ -546,12 +622,12 @@ class L6480 : public L6480_Base { L6480::OCD_CURRENT_CONSTANT = 1.0f / L6480::OCD_CURRENT_CONSTANT_INV; // counts per mA L6480::STALL_CURRENT_CONSTANT_INV = 31.25; // mA per count L6480::STALL_CURRENT_CONSTANT = 1.0f / L6480::STALL_CURRENT_CONSTANT_INV; // counts per mA - L6480::L6470_status_layout = false; + L6480::L6470_status_layout = L6480_STATUS_LAYOUT; L6480::L64XX_CONFIG = 0x1A; L6480::L64XX_STATUS = 0x1B; - L6470_status_layout = false; + L6470_status_layout = L6480_STATUS_LAYOUT; L6480::STATUS_WRONG_CMD = 0x0080; // Last command not valid. L6480::STATUS_CMD_ERR = 0x0080; // Command error @@ -589,7 +665,7 @@ class powerSTEP01 : public L6480_Base { powerSTEP01::L64XX_CONFIG = 0x1A; powerSTEP01::L64XX_STATUS = 0x1B; - powerSTEP01::L6470_status_layout = false; + powerSTEP01::L6470_status_layout = L6480_STATUS_LAYOUT; powerSTEP01::STATUS_WRONG_CMD = 0x0080; // Last command not valid. powerSTEP01::STATUS_CMD_ERR = 0x0080; // Command error @@ -616,7 +692,7 @@ class powerSTEP01 : public L6480_Base { powerSTEP01::L64XX_CONFIG = 0x1A; powerSTEP01::L64XX_STATUS = 0x1B; - powerSTEP01::L6470_status_layout = false; + powerSTEP01::L6470_status_layout = L6480_STATUS_LAYOUT; powerSTEP01::STATUS_WRONG_CMD = 0x0080; // Last command not valid. powerSTEP01::STATUS_CMD_ERR = 0x0080; // Command error @@ -630,13 +706,6 @@ class powerSTEP01 : public L6480_Base { powerSTEP01::STATUS_SCK_MOD = 0x0100; // Step clock mode is active } - - //static constexpr float POWERSTEP_AVERAGE_RDS = 0.016; // Ohms - //powerSTEP01::OCD_CURRENT_CONSTANT = (POWERSTEP_AVERAGE_RDS/0.03125)/1000; // counts per mA (calc per data sheet - definitely wrong) - //powerSTEP01::OCD_CURRENT_CONSTANT_INV = (1000 * 0.03125)/(POWERSTEP_AVERAGE_RDS); // mA per count (calc per data sheet - definitely wrong) - //powerSTEP01::STALL_CURRENT_CONSTANT = OCD_CURRENT_CONSTANT; // counts per mA (calc per data sheet - definitely wrong) - //powerSTEP01::STALL_CURRENT_CONSTANT_INV = OCD_CURRENT_CONSTANT_INV; // mA per count (calc per data sheet - definitely wrong) - }; #endif // _L6470_H_ From 1bc39cc97594a7e960e15589bf23302bc71d2c14 Mon Sep 17 00:00:00 2001 From: Bob-the-Kuhn Date: Fri, 3 Jan 2020 14:03:27 -0600 Subject: [PATCH 12/14] L6474 step register - limit to valid range and add in fixed bits --- src/L6470.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/L6470.cpp b/src/L6470.cpp index c3b6baf..29d5f63 100644 --- a/src/L6470.cpp +++ b/src/L6470.cpp @@ -145,7 +145,10 @@ void L64XX::setMicroSteps(int16_t microSteps) { } if (L6470_status_layout == L6474_STATUS_LAYOUT) + { + if (stepVal > 4) stepVal = 4; // 16 microsteps max on L6474 SetParam(L6470_STEP_MODE, 0x98 | stepVal); //no sync + } else SetParam(L6470_STEP_MODE, (!SYNC_EN | SYNC_SEL_1 | stepVal)); } From 3941527fa87e982ac604514d040c938a2abbd75d Mon Sep 17 00:00:00 2001 From: Bob-the-Kuhn Date: Fri, 3 Jan 2020 20:15:31 -0600 Subject: [PATCH 13/14] make it obvious that only the one term is inverted --- src/L6470.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/L6470.cpp b/src/L6470.cpp index 29d5f63..9338664 100644 --- a/src/L6470.cpp +++ b/src/L6470.cpp @@ -150,7 +150,7 @@ void L64XX::setMicroSteps(int16_t microSteps) { SetParam(L6470_STEP_MODE, 0x98 | stepVal); //no sync } else - SetParam(L6470_STEP_MODE, (!SYNC_EN | SYNC_SEL_1 | stepVal)); + SetParam(L6470_STEP_MODE, ((!SYNC_EN) | SYNC_SEL_1 | stepVal)); } // Configure the L6470_FS_SPD register- this is the speed at which the driver ceases From 891879cb56aaded121e27f5aa175c94bae3dc389 Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Sun, 17 Mar 2019 02:13:08 -0500 Subject: [PATCH 14/14] Fix L6470 step mode busy/sync state --- src/L6470.cpp | 19 +++++++++---------- src/L6470.h | 1 + 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/L6470.cpp b/src/L6470.cpp index 912a51a..6b91203 100644 --- a/src/L6470.cpp +++ b/src/L6470.cpp @@ -55,7 +55,7 @@ void L64XX::init() { // - SYNC_SEL_x is the ratio of (micro)steps to toggles on the // BUSY/SYNC pin (when that pin is used for SYNC). Make it 1:1, despite // not using that pin. - //SetParam(L6470_STEP_MODE, !SYNC_EN | STEP_SEL_1 | SYNC_SEL_1); + //SetParam(L6470_STEP_MODE, BUSY_EN | STEP_SEL_1 | SYNC_SEL_1); // Set up the L64XX_CONFIG register as follows: // PWM frequency divisor = 1 @@ -138,19 +138,18 @@ void L64XX::set_pins(const _pin_t sck, const _pin_t mosi, const _pin_t miso, con uint8_t L64XX::isBusy() { return !(getStatus() & 0x0002); } void L64XX::setMicroSteps(int16_t microSteps) { - uint8_t stepVal; - for (stepVal = 0; stepVal < 8; stepVal++) { + uint8_t stepSel; + for (stepSel = 0; stepSel < 8; stepSel++) { if (microSteps == 1) break; microSteps >>= 1; } - if (L6470_status_layout == L6474_STATUS_LAYOUT) - { - if (stepVal > 4) stepVal = 4; // 16 microsteps max on L6474 - SetParam(L6470_STEP_MODE, 0x98 | stepVal); //no sync - } - else - SetParam(L6470_STEP_MODE, ((!SYNC_EN) | SYNC_SEL_1 | stepVal)); + if (L6470_status_layout == L6474_STATUS_LAYOUT) { + NOMORE(stepSel, 4); // 16 microsteps max on L6474 + SetParam(L6470_STEP_MODE, 0x98 | stepSel); // NO SYNC + } + else + SetParam(L6470_STEP_MODE, BUSY_EN | SYNC_SEL_1 | stepSel); } // Configure the L6470_FS_SPD register- this is the speed at which the driver ceases diff --git a/src/L6470.h b/src/L6470.h index 6f5d4a1..22ab904 100644 --- a/src/L6470.h +++ b/src/L6470.h @@ -75,6 +75,7 @@ // SYNC_SEL bits below. #define STEP_MODE_SYNC_EN 0x80 // Mask for this bit #define SYNC_EN 0x80 +#define BUSY_EN 0x00 // ...last, define the SYNC_SEL modes. The clock output is defined by // the full-step frequency and the value in these bits- see the datasheet