diff --git a/examples/two_motors_synchronized/two_motors_synchronized.ino b/examples/two_motors_synchronized/two_motors_synchronized.ino index 9fb7028..5de4daa 100644 --- a/examples/two_motors_synchronized/two_motors_synchronized.ino +++ b/examples/two_motors_synchronized/two_motors_synchronized.ino @@ -37,146 +37,174 @@ #define MISO_PIN 13 #define RESET_PIN 14 -class TwoMotors : public L64XXHelper { -public: - TwoMotors() { L64XX::set_helper(*this); } - - /** - * 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); +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); - digitalWrite(MOSI_PIN, HIGH); - pinMode(MISO_PIN, INPUT); - } - - static inline uint8_t transfer(uint8_t data, const int16_t ss_pin) { } - - /** - * Used in all non-motion commands/transfers - * 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! - */ - static inline uint8_t transfer(uint8_t data, const int16_t ss_pin, const 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 = L64XX::chain[0]; i >= 1; i--) { - uint8_t temp = L6470_SpiTransfer_Mode_3(uint8_t(i == chain_position ? data : CMD_NOP)); - if (L64XX::chain[i] == chain_position) data_out = temp; - } - - digitalWrite(ss_pin, HIGH); - return data_out; - } + b <<= 1; // little setup time + b |= (digitalRead(MISO_PIN) != 0); + } while (--bits); + //DELAY_NS(125); + return b; +} - static inline 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; - } +/** + * 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); +} - /** - * This is the routine that sends the motion commands. - * - * This routine sends a buffer of data to be filled by the application. The - * library is not involved with it. - */ - static inline void Buffer_Transfer(uint8_t buffer[], const uint8_t length) { - //uint8_t buffer[number of steppers + 1]; - // [0] - not used - // [1] - command for first device - // [2] - command for second device - - // 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); - } +/** + * Used in all non-motion commands/transfers + * + * 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 L64XX::transfer(uint8_t,int16_t,uint8_t)" + */ - static inline 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 L64XX::transfer(uint8_t data, const int16_t ss_pin, const uint8_t chain_position) { - uint8_t buffer_command[3] = { dSPIN_GOTO, dSPIN_GOTO }; // create and fill buffer with commands + #define CMD_NOP 0 + uint8_t data_out = 0; + data--; + // first device in chain has data sent last + digitalWrite(ss_pin, LOW); - if (location_1 > 0x3FFFFF) location_1 = 0x3FFFFF; // limit to 22 bits - if (location_1 > 0x3FFFFF) location_1 = 0x3FFFFF; + 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 (L64XX::chain[i] == chain_position) data_out = temp; + } - 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) }; + digitalWrite(ss_pin, HIGH); + return data_out; +} - 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 - } +uint8_t L64XX::transfer(uint8_t data, const int16_t ss_pin) { } - static inline 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 - } +/** + * 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. + */ - static inline void _loop() { - while (stepperB.isBusy()) delay(10); - goTo(-200,-200); - while (stepperB.isBusy()) delay(10); - goTo(2000,2000); - } +//uint8_t buffer[number of steppers + 1]; + // [0] - not used + // [1] - command for first device + // [2] - command for second device -private: - static L6470 stepperA; - static L6480 stepperB; - static powerSTEP01 stepperC; -}; +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); +} -L6470 TwoMotors::stepperA(SS_PIN); -L6480 TwoMotors::stepperB(SS_PIN); -powerSTEP01 TwoMotors::stepperC(SS_PIN); +/** + * Initialize pins for non-library SPI software + * + * The library will automatically link to "void L64XX::spi_init()" + */ -TwoMotors two_motors; +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 + + 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, 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 +} + +////////////////////////////////////////////////////////////////////// + +void setup() { + + 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.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() { + + static uint32_t next_ms = millis() + 500UL; + uint32_t ms = millis(); + if (ms >= next_ms) { + Serial.println("tick"); + next_ms = ms + 500UL; + } -void setup() { two_motors._setup(); } -void loop() { two_motors._loop(); } + return; + /* + while (stepperB.isBusy()) delay(10); + goTo(-200,-200); + while (stepperB.isBusy()) delay(10); + goTo(2000,2000); + */ +} 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 084fa19..6b91203 100644 --- a/src/L6470.cpp +++ b/src/L6470.cpp @@ -17,10 +17,15 @@ #include L64XXHelper nullHelper; -L64XXHelper& L64XX::helper = 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) @@ -35,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) helper.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 @@ -50,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 @@ -130,15 +135,21 @@ 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; - for (stepVal = 0; stepVal < 8; stepVal++) { + uint8_t stepSel; + for (stepSel = 0; stepSel < 8; stepSel++) { if (microSteps == 1) break; microSteps >>= 1; } - SetParam(L6470_STEP_MODE, !SYNC_EN | stepVal | SYNC_SEL_1); + + 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 @@ -203,12 +214,19 @@ 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); } @@ -445,15 +463,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 - return uint8_t( - position ? helper.transfer(data, pin_SS, position) // ... in a chain - : helper.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 ); - } // 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 ba30b89..22ab904 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 @@ -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 @@ -123,6 +124,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 +144,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 @@ -160,7 +163,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 @@ -187,6 +189,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 @@ -196,8 +204,6 @@ // cleared by reading L64XX_STATUS #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 @@ -233,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 @@ -258,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 @@ -270,14 +279,29 @@ #define dSPIN_ACTION_RESET 0x00 #define dSPIN_ACTION_COPY 0x01 -typedef uint16_t _pin_t; +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: +//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) { } - static inline uint8_t transfer(uint8_t data, const _pin_t ss_pin, const uint8_t chain_position) { } + 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; @@ -299,14 +323,36 @@ class L64XX { uint8_t position = 0; // 0 - not part of a chain // This object must be supplied by the client - static L64XXHelper &helper; + static L64XXHelper *helper; + + static inline void set_helper(L64XXHelper *_helper) { helper = _helper; } + +////////////////////////////////////////////////////////////////////////////////////////////////// + + // These methods must be supplied by the client + + 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); + } + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// - static inline void set_helper(L64XXHelper & _helper) { helper = _helper; } void init(); inline void init(const _pin_t ss_pin) { pin_SS = ss_pin; } - inline void init(const _pin_t ss_pin, L64XXHelper &_helper) { + inline void init(const _pin_t ss_pin, L64XXHelper *_helper) { pin_SS = ss_pin; set_helper(_helper); } @@ -320,11 +366,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); @@ -337,8 +384,8 @@ class L64XX { void goTo(long pos); 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); @@ -358,28 +405,36 @@ class L64XX { void SetParam(const uint8_t param, const uint32_t value); uint32_t GetParam(const uint8_t param); - // 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 + + uint8_t 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); @@ -394,65 +449,260 @@ 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, _pin_t ss_pin, uint8_t position); - - static inline void spi_init_noop() { } - static inline uint8_t transfer_noop(uint8_t data, const _pin_t ss_pin) { } - static inline uint8_t chain_transfer_noop(uint8_t data, const _pin_t ss_pin, const uint8_t chain_position) { } }; class L6470 : public L64XX { public: - L6470(const _pin_t ss_pin) { init(ss_pin); } - L6470(const _pin_t ss_pin, L64XXHelper &_helper) { init(ss_pin, _helper); } + 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.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 + + L6470::L64XX_CONFIG = 0x18; + L6470::L64XX_STATUS = 0x19; + + 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. + 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 + } + + 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.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 + + L6470::L64XX_CONFIG = 0x18; + L6470::L64XX_STATUS = 0x19; + + 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. + 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 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: - 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_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 L6480 : public L6480_Base { public: - L6480(const _pin_t ss_pin) { init(ss_pin); } - L6480(const _pin_t ss_pin, L64XXHelper &_helper) { init(ss_pin, _helper); } - - 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 + 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 = L6480_STATUS_LAYOUT; + + L6480::L64XX_CONFIG = 0x1A; + L6480::L64XX_STATUS = 0x1B; + + L6470_status_layout = L6480_STATUS_LAYOUT; + + 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 + } + + 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 = L6480_STATUS_LAYOUT; + + L6480::L64XX_CONFIG = 0x1A; + L6480::L64XX_STATUS = 0x1B; + + L6470_status_layout = L6480_STATUS_LAYOUT; + + 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 { public: - powerSTEP01(const _pin_t ss_pin) { init(ss_pin); } - powerSTEP01(const _pin_t ss_pin, L64XXHelper &_helper) { init(ss_pin, _helper); } - - 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) + 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 = L6480_STATUS_LAYOUT; + + 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 + } + + 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 = L6480_STATUS_LAYOUT; + + 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 + } + }; #endif // _L6470_H_