|
@@ -0,0 +1,723 @@
|
|
1
|
+////////////////////////////////////////////////////////////
|
|
2
|
+//ORIGINAL CODE 12/12/2011- Mike Hord, SparkFun Electronics
|
|
3
|
+//LIBRARY Created by Adam Meyer of bildr Aug 18th 2012
|
|
4
|
+//Released as MIT license
|
|
5
|
+////////////////////////////////////////////////////////////
|
|
6
|
+
|
|
7
|
+#include <Arduino.h>
|
|
8
|
+#include "L6470.h"
|
|
9
|
+#include <SPI.h>
|
|
10
|
+
|
|
11
|
+#define ENABLE_RESET_PIN 0
|
|
12
|
+#define K_VALUE 100
|
|
13
|
+
|
|
14
|
+L6470::L6470(int SSPin){
|
|
15
|
+ _SSPin = SSPin;
|
|
16
|
+ // Serial.begin(9600);
|
|
17
|
+}
|
|
18
|
+
|
|
19
|
+void L6470::init(int k_value){
|
|
20
|
+ // This is the generic initialization function to set up the Arduino to
|
|
21
|
+ // communicate with the dSPIN chip.
|
|
22
|
+
|
|
23
|
+ // set up the input/output pins for the application.
|
|
24
|
+ pinMode(SLAVE_SELECT_PIN, OUTPUT); // The SPI peripheral REQUIRES the hardware SS pin-
|
|
25
|
+ // pin 10- to be an output. This is in here just
|
|
26
|
+ // in case some future user makes something other
|
|
27
|
+ // than pin 10 the SS pin.
|
|
28
|
+
|
|
29
|
+ pinMode(_SSPin, OUTPUT);
|
|
30
|
+ digitalWrite(_SSPin, HIGH);
|
|
31
|
+ pinMode(MOSI, OUTPUT);
|
|
32
|
+ pinMode(MISO, INPUT);
|
|
33
|
+ pinMode(SCK, OUTPUT);
|
|
34
|
+ pinMode(BUSYN, INPUT);
|
|
35
|
+#if (ENABLE_RESET_PIN == 1)
|
|
36
|
+ pinMode(RESET, OUTPUT);
|
|
37
|
+ // reset the dSPIN chip. This could also be accomplished by
|
|
38
|
+ // calling the "L6470::ResetDev()" function after SPI is initialized.
|
|
39
|
+ digitalWrite(RESET, HIGH);
|
|
40
|
+ delay(10);
|
|
41
|
+ digitalWrite(RESET, LOW);
|
|
42
|
+ delay(10);
|
|
43
|
+ digitalWrite(RESET, HIGH);
|
|
44
|
+ delay(10);
|
|
45
|
+#endif
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+ // initialize SPI for the dSPIN chip's needs:
|
|
49
|
+ // most significant bit first,
|
|
50
|
+ // SPI clock not to exceed 5MHz,
|
|
51
|
+ // SPI_MODE3 (clock idle high, latch data on rising edge of clock)
|
|
52
|
+ SPI.begin();
|
|
53
|
+ SPI.setBitOrder(MSBFIRST);
|
|
54
|
+ SPI.setClockDivider(SPI_CLOCK_DIV16); // or 2, 8, 16, 32, 64
|
|
55
|
+ SPI.setDataMode(SPI_MODE3);
|
|
56
|
+
|
|
57
|
+ // First things first: let's check communications. The CONFIG register should
|
|
58
|
+ // power up to 0x2E88, so we can use that to check the communications.
|
|
59
|
+ if (GetParam(CONFIG) == 0x2E88){
|
|
60
|
+ //Serial.println('good to go');
|
|
61
|
+ }
|
|
62
|
+ else{
|
|
63
|
+ //Serial.println('Comm issue');
|
|
64
|
+ }
|
|
65
|
+
|
|
66
|
+#if (ENABLE_RESET_PIN == 0)
|
|
67
|
+ resetDev();
|
|
68
|
+#endif
|
|
69
|
+ // First, let's set the step mode register:
|
|
70
|
+ // - SYNC_EN controls whether the BUSY/SYNC pin reflects the step
|
|
71
|
+ // frequency or the BUSY status of the chip. We want it to be the BUSY
|
|
72
|
+ // status.
|
|
73
|
+ // - STEP_SEL_x is the microstepping rate- we'll go full step.
|
|
74
|
+ // - SYNC_SEL_x is the ratio of (micro)steps to toggles on the
|
|
75
|
+ // BUSY/SYNC pin (when that pin is used for SYNC). Make it 1:1, despite
|
|
76
|
+ // not using that pin.
|
|
77
|
+ //SetParam(STEP_MODE, !SYNC_EN | STEP_SEL_1 | SYNC_SEL_1);
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+ SetParam(KVAL_RUN, k_value);
|
|
81
|
+ SetParam(KVAL_ACC, k_value);
|
|
82
|
+ SetParam(KVAL_DEC, k_value);
|
|
83
|
+ SetParam(KVAL_HOLD, k_value);
|
|
84
|
+
|
|
85
|
+ // Set up the CONFIG register as follows:
|
|
86
|
+ // PWM frequency divisor = 1
|
|
87
|
+ // PWM frequency multiplier = 2 (62.5kHz PWM frequency)
|
|
88
|
+ // Slew rate is 290V/us
|
|
89
|
+ // Do NOT shut down bridges on overcurrent
|
|
90
|
+ // Disable motor voltage compensation
|
|
91
|
+ // Hard stop on switch low
|
|
92
|
+ // 16MHz internal oscillator, nothing on output
|
|
93
|
+ SetParam(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);
|
|
94
|
+ // Configure the RUN KVAL. This defines the duty cycle of the PWM of the bridges
|
|
95
|
+ // during running. 0xFF means that they are essentially NOT PWMed during run; this
|
|
96
|
+ // MAY result in more power being dissipated than you actually need for the task.
|
|
97
|
+ // Setting this value too low may result in failure to turn.
|
|
98
|
+ // There are ACC, DEC, and HOLD KVAL registers as well; you may need to play with
|
|
99
|
+ // those values to get acceptable performance for a given application.
|
|
100
|
+ //SetParam(KVAL_RUN, 0xFF);
|
|
101
|
+ // Calling GetStatus() clears the UVLO bit in the status register, which is set by
|
|
102
|
+ // default on power-up. The driver may not run without that bit cleared by this
|
|
103
|
+ // read operation.
|
|
104
|
+ getStatus();
|
|
105
|
+
|
|
106
|
+ hardStop(); //engage motors
|
|
107
|
+}
|
|
108
|
+
|
|
109
|
+boolean L6470::isBusy(){
|
|
110
|
+ int status = getStatus();
|
|
111
|
+ return !((status >> 1) & 0b1);
|
|
112
|
+}
|
|
113
|
+
|
|
114
|
+void L6470::setMicroSteps(int microSteps){
|
|
115
|
+ byte stepVal = 0;
|
|
116
|
+
|
|
117
|
+ for(stepVal = 0; stepVal < 8; stepVal++){
|
|
118
|
+ if(microSteps == 1) break;
|
|
119
|
+ microSteps = microSteps >> 1;
|
|
120
|
+ }
|
|
121
|
+
|
|
122
|
+ SetParam(STEP_MODE, !SYNC_EN | stepVal | SYNC_SEL_1);
|
|
123
|
+}
|
|
124
|
+
|
|
125
|
+void L6470::setThresholdSpeed(float thresholdSpeed){
|
|
126
|
+ // Configure the FS_SPD register- this is the speed at which the driver ceases
|
|
127
|
+ // microstepping and goes to full stepping. FSCalc() converts a value in steps/s
|
|
128
|
+ // to a value suitable for this register; to disable full-step switching, you
|
|
129
|
+ // can pass 0x3FF to this register.
|
|
130
|
+
|
|
131
|
+ if(thresholdSpeed == 0.0){
|
|
132
|
+ SetParam(FS_SPD, 0x3FF);
|
|
133
|
+ }
|
|
134
|
+ else{
|
|
135
|
+ SetParam(FS_SPD, FSCalc(thresholdSpeed));
|
|
136
|
+ }
|
|
137
|
+}
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+void L6470::setCurrent(int current){}
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+void L6470::setMaxSpeed(int speed){
|
|
145
|
+ // Configure the MAX_SPEED register- this is the maximum number of (micro)steps per
|
|
146
|
+ // second allowed. You'll want to mess around with your desired application to see
|
|
147
|
+ // how far you can push it before the motor starts to slip. The ACTUAL parameter
|
|
148
|
+ // passed to this function is in steps/tick; MaxSpdCalc() will convert a number of
|
|
149
|
+ // steps/s into an appropriate value for this function. Note that for any move or
|
|
150
|
+ // goto type function where no speed is specified, this value will be used.
|
|
151
|
+ SetParam(MAX_SPEED, MaxSpdCalc(speed));
|
|
152
|
+}
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+void L6470::setMinSpeed(int speed){
|
|
156
|
+ // Configure the MAX_SPEED register- this is the maximum number of (micro)steps per
|
|
157
|
+ // second allowed. You'll want to mess around with your desired application to see
|
|
158
|
+ // how far you can push it before the motor starts to slip. The ACTUAL parameter
|
|
159
|
+ // passed to this function is in steps/tick; MaxSpdCalc() will convert a number of
|
|
160
|
+ // steps/s into an appropriate value for this function. Note that for any move or
|
|
161
|
+ // goto type function where no speed is specified, this value will be used.
|
|
162
|
+ SetParam(MIN_SPEED, MinSpdCalc(speed));
|
|
163
|
+}
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+void L6470::setAcc(float acceleration){
|
|
169
|
+ // Configure the acceleration rate, in steps/tick/tick. There is also a DEC register;
|
|
170
|
+ // both of them have a function (AccCalc() and DecCalc() respectively) that convert
|
|
171
|
+ // from steps/s/s into the appropriate value for the register. Writing ACC to 0xfff
|
|
172
|
+ // sets the acceleration and deceleration to 'infinite' (or as near as the driver can
|
|
173
|
+ // manage). If ACC is set to 0xfff, DEC is ignored. To get infinite deceleration
|
|
174
|
+ // without infinite acceleration, only hard stop will work.
|
|
175
|
+ unsigned long accelerationBYTES = AccCalc(acceleration);
|
|
176
|
+ SetParam(ACC, accelerationBYTES);
|
|
177
|
+}
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+void L6470::setDec(float deceleration){
|
|
181
|
+ unsigned long decelerationBYTES = DecCalc(deceleration);
|
|
182
|
+ SetParam(DEC, decelerationBYTES);
|
|
183
|
+}
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+long L6470::getPos(){
|
|
187
|
+ unsigned long position = GetParam(ABS_POS);
|
|
188
|
+ return convert(position);
|
|
189
|
+}
|
|
190
|
+
|
|
191
|
+float L6470::getSpeed(){
|
|
192
|
+ /*
|
|
193
|
+ SPEED
|
|
194
|
+ The SPEED register contains the current motor speed, expressed in step/tick (format unsigned fixed point 0.28).
|
|
195
|
+ In order to convert the SPEED value in step/s the following formula can be used:
|
|
196
|
+ Equation 4
|
|
197
|
+ where SPEED is the integer number stored into the register and tick is 250 ns.
|
|
198
|
+ The available range is from 0 to 15625 step/s with a resolution of 0.015 step/s.
|
|
199
|
+ Note: The range effectively available to the user is limited by the MAX_SPEED parameter.
|
|
200
|
+ */
|
|
201
|
+
|
|
202
|
+ return (float) GetParam(SPEED);
|
|
203
|
+ //return (float) speed * pow(8, -22);
|
|
204
|
+ //return FSCalc(speed); NEEDS FIX
|
|
205
|
+}
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+void L6470::setOverCurrent(unsigned int ma_current){
|
|
209
|
+ // Configure the overcurrent detection threshold.
|
|
210
|
+ byte OCValue = floor(ma_current / 375);
|
|
211
|
+ if(OCValue > 0x0F)OCValue = 0x0F;
|
|
212
|
+ SetParam(OCD_TH, OCValue);
|
|
213
|
+}
|
|
214
|
+
|
|
215
|
+void L6470::setStallCurrent(float ma_current){
|
|
216
|
+ byte STHValue = (byte)floor(ma_current / 31.25);
|
|
217
|
+ if(STHValue > 0x80)STHValue = 0x80;
|
|
218
|
+ if(STHValue < 0)STHValue = 0;
|
|
219
|
+ SetParam(STALL_TH, STHValue);
|
|
220
|
+}
|
|
221
|
+
|
|
222
|
+void L6470::SetLowSpeedOpt(boolean enable){
|
|
223
|
+ // Enable or disable the low-speed optimization option. If enabling,
|
|
224
|
+ // the other 12 bits of the register will be automatically zero.
|
|
225
|
+ // When disabling, the value will have to be explicitly written by
|
|
226
|
+ // the user with a SetParam() call. See the datasheet for further
|
|
227
|
+ // information about low-speed optimization.
|
|
228
|
+ Xfer(SET_PARAM | MIN_SPEED);
|
|
229
|
+ if (enable) Param(0x1000, 13);
|
|
230
|
+ else Param(0, 13);
|
|
231
|
+}
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+void L6470::run(byte dir, float spd){
|
|
235
|
+ // RUN sets the motor spinning in a direction (defined by the constants
|
|
236
|
+ // FWD and REV). Maximum speed and minimum speed are defined
|
|
237
|
+ // by the MAX_SPEED and MIN_SPEED registers; exceeding the FS_SPD value
|
|
238
|
+ // will switch the device into full-step mode.
|
|
239
|
+ // The SpdCalc() function is provided to convert steps/s values into
|
|
240
|
+ // appropriate integer values for this function.
|
|
241
|
+ unsigned long speedVal = SpdCalc(spd);
|
|
242
|
+
|
|
243
|
+ Xfer(RUN | dir);
|
|
244
|
+ if (speedVal > 0xFFFFF) speedVal = 0xFFFFF;
|
|
245
|
+ Xfer((byte)(speedVal >> 16));
|
|
246
|
+ Xfer((byte)(speedVal >> 8));
|
|
247
|
+ Xfer((byte)(speedVal));
|
|
248
|
+}
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+void L6470::Step_Clock(byte dir){
|
|
252
|
+ // STEP_CLOCK puts the device in external step clocking mode. When active,
|
|
253
|
+ // pin 25, STCK, becomes the step clock for the device, and steps it in
|
|
254
|
+ // the direction (set by the FWD and REV constants) imposed by the call
|
|
255
|
+ // of this function. Motion commands (RUN, MOVE, etc) will cause the device
|
|
256
|
+ // to exit step clocking mode.
|
|
257
|
+ Xfer(STEP_CLOCK | dir);
|
|
258
|
+}
|
|
259
|
+
|
|
260
|
+void L6470::move(long n_step){
|
|
261
|
+ // MOVE will send the motor n_step steps (size based on step mode) in the
|
|
262
|
+ // direction imposed by dir (FWD or REV constants may be used). The motor
|
|
263
|
+ // will accelerate according the acceleration and deceleration curves, and
|
|
264
|
+ // will run at MAX_SPEED. Stepping mode will adhere to FS_SPD value, as well.
|
|
265
|
+
|
|
266
|
+ byte dir;
|
|
267
|
+
|
|
268
|
+ if(n_step >= 0){
|
|
269
|
+ dir = FWD;
|
|
270
|
+ }
|
|
271
|
+ else{
|
|
272
|
+ dir = REV;
|
|
273
|
+ }
|
|
274
|
+
|
|
275
|
+ long n_stepABS = abs(n_step);
|
|
276
|
+
|
|
277
|
+ Xfer(MOVE | dir); //set direction
|
|
278
|
+ if (n_stepABS > 0x3FFFFF) n_step = 0x3FFFFF;
|
|
279
|
+ Xfer((byte)(n_stepABS >> 16));
|
|
280
|
+ Xfer((byte)(n_stepABS >> 8));
|
|
281
|
+ Xfer((byte)(n_stepABS));
|
|
282
|
+}
|
|
283
|
+
|
|
284
|
+void L6470::goTo(long pos){
|
|
285
|
+ // GOTO operates much like MOVE, except it produces absolute motion instead
|
|
286
|
+ // of relative motion. The motor will be moved to the indicated position
|
|
287
|
+ // in the shortest possible fashion.
|
|
288
|
+
|
|
289
|
+ Xfer(GOTO);
|
|
290
|
+ if (pos > 0x3FFFFF) pos = 0x3FFFFF;
|
|
291
|
+ Xfer((byte)(pos >> 16));
|
|
292
|
+ Xfer((byte)(pos >> 8));
|
|
293
|
+ Xfer((byte)(pos));
|
|
294
|
+}
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+void L6470::goTo_DIR(byte dir, long pos){
|
|
298
|
+ // Same as GOTO, but with user constrained rotational direction.
|
|
299
|
+
|
|
300
|
+ Xfer(GOTO_DIR);
|
|
301
|
+ if (pos > 0x3FFFFF) pos = 0x3FFFFF;
|
|
302
|
+ Xfer((byte)(pos >> 16));
|
|
303
|
+ Xfer((byte)(pos >> 8));
|
|
304
|
+ Xfer((byte)(pos));
|
|
305
|
+}
|
|
306
|
+
|
|
307
|
+void L6470::goUntil(byte act, byte dir, unsigned long spd){
|
|
308
|
+ // GoUntil will set the motor running with direction dir (REV or
|
|
309
|
+ // FWD) until a falling edge is detected on the SW pin. Depending
|
|
310
|
+ // on bit SW_MODE in CONFIG, either a hard stop or a soft stop is
|
|
311
|
+ // performed at the falling edge, and depending on the value of
|
|
312
|
+ // act (either RESET or COPY) the value in the ABS_POS register is
|
|
313
|
+ // either RESET to 0 or COPY-ed into the MARK register.
|
|
314
|
+ Xfer(GO_UNTIL | act | dir);
|
|
315
|
+ if (spd > 0x3FFFFF) spd = 0x3FFFFF;
|
|
316
|
+ Xfer((byte)(spd >> 16));
|
|
317
|
+ Xfer((byte)(spd >> 8));
|
|
318
|
+ Xfer((byte)(spd));
|
|
319
|
+}
|
|
320
|
+
|
|
321
|
+void L6470::releaseSW(byte act, byte dir){
|
|
322
|
+ // Similar in nature to GoUntil, ReleaseSW produces motion at the
|
|
323
|
+ // higher of two speeds: the value in MIN_SPEED or 5 steps/s.
|
|
324
|
+ // The motor continues to run at this speed until a rising edge
|
|
325
|
+ // is detected on the switch input, then a hard stop is performed
|
|
326
|
+ // and the ABS_POS register is either COPY-ed into MARK or RESET to
|
|
327
|
+ // 0, depending on whether RESET or COPY was passed to the function
|
|
328
|
+ // for act.
|
|
329
|
+ Xfer(RELEASE_SW | act | dir);
|
|
330
|
+}
|
|
331
|
+
|
|
332
|
+void L6470::goHome(){
|
|
333
|
+ // GoHome is equivalent to GoTo(0), but requires less time to send.
|
|
334
|
+ // Note that no direction is provided; motion occurs through shortest
|
|
335
|
+ // path. If a direction is required, use GoTo_DIR().
|
|
336
|
+ Xfer(GO_HOME);
|
|
337
|
+}
|
|
338
|
+
|
|
339
|
+void L6470::goMark(){
|
|
340
|
+ // GoMark is equivalent to GoTo(MARK), but requires less time to send.
|
|
341
|
+ // Note that no direction is provided; motion occurs through shortest
|
|
342
|
+ // path. If a direction is required, use GoTo_DIR().
|
|
343
|
+ Xfer(GO_MARK);
|
|
344
|
+}
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+void L6470::setMark(long value){
|
|
348
|
+
|
|
349
|
+ Xfer(MARK);
|
|
350
|
+ if (value > 0x3FFFFF) value = 0x3FFFFF;
|
|
351
|
+ if (value < -0x3FFFFF) value = -0x3FFFFF;
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+ Xfer((byte)(value >> 16));
|
|
355
|
+ Xfer((byte)(value >> 8));
|
|
356
|
+ Xfer((byte)(value));
|
|
357
|
+}
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+void L6470::setMark(){
|
|
361
|
+ long value = getPos();
|
|
362
|
+
|
|
363
|
+ Xfer(MARK);
|
|
364
|
+ if (value > 0x3FFFFF) value = 0x3FFFFF;
|
|
365
|
+ if (value < -0x3FFFFF) value = -0x3FFFFF;
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+ Xfer((byte)(value >> 16));
|
|
369
|
+ Xfer((byte)(value >> 8));
|
|
370
|
+ Xfer((byte)(value));
|
|
371
|
+}
|
|
372
|
+
|
|
373
|
+void L6470::setAsHome(){
|
|
374
|
+ // Sets the ABS_POS register to 0, effectively declaring the current
|
|
375
|
+ // position to be "HOME".
|
|
376
|
+ Xfer(RESET_POS);
|
|
377
|
+}
|
|
378
|
+
|
|
379
|
+void L6470::resetDev(){
|
|
380
|
+ // Reset device to power up conditions. Equivalent to toggling the STBY
|
|
381
|
+ // pin or cycling power.
|
|
382
|
+ Xfer(RESET_DEVICE);
|
|
383
|
+}
|
|
384
|
+
|
|
385
|
+void L6470::softStop(){
|
|
386
|
+ // Bring the motor to a halt using the deceleration curve.
|
|
387
|
+ Xfer(SOFT_STOP);
|
|
388
|
+}
|
|
389
|
+
|
|
390
|
+void L6470::hardStop(){
|
|
391
|
+ // Stop the motor right away. No deceleration.
|
|
392
|
+ Xfer(HARD_STOP);
|
|
393
|
+}
|
|
394
|
+
|
|
395
|
+void L6470::softFree(){
|
|
396
|
+ // Decelerate the motor and disengage
|
|
397
|
+ Xfer(SOFT_HIZ);
|
|
398
|
+}
|
|
399
|
+
|
|
400
|
+void L6470::free(){
|
|
401
|
+ // disengage the motor immediately with no deceleration.
|
|
402
|
+ Xfer(HARD_HIZ);
|
|
403
|
+}
|
|
404
|
+
|
|
405
|
+int L6470::getStatus(){
|
|
406
|
+ // Fetch and return the 16-bit value in the STATUS register. Resets
|
|
407
|
+ // any warning flags and exits any error states. Using GetParam()
|
|
408
|
+ // to read STATUS does not clear these values.
|
|
409
|
+ int temp = 0;
|
|
410
|
+ Xfer(GET_STATUS);
|
|
411
|
+ temp = Xfer(0)<<8;
|
|
412
|
+ temp |= Xfer(0);
|
|
413
|
+ return temp;
|
|
414
|
+}
|
|
415
|
+
|
|
416
|
+unsigned long L6470::AccCalc(float stepsPerSecPerSec){
|
|
417
|
+ // The value in the ACC register is [(steps/s/s)*(tick^2)]/(2^-40) where tick is
|
|
418
|
+ // 250ns (datasheet value)- 0x08A on boot.
|
|
419
|
+ // Multiply desired steps/s/s by .137438 to get an appropriate value for this register.
|
|
420
|
+ // This is a 12-bit value, so we need to make sure the value is at or below 0xFFF.
|
|
421
|
+ float temp = stepsPerSecPerSec * 0.137438;
|
|
422
|
+ if( (unsigned long) long(temp) > 0x00000FFF) return 0x00000FFF;
|
|
423
|
+ else return (unsigned long) long(temp);
|
|
424
|
+}
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+unsigned long L6470::DecCalc(float stepsPerSecPerSec){
|
|
428
|
+ // The calculation for DEC is the same as for ACC. Value is 0x08A on boot.
|
|
429
|
+ // This is a 12-bit value, so we need to make sure the value is at or below 0xFFF.
|
|
430
|
+ float temp = stepsPerSecPerSec * 0.137438;
|
|
431
|
+ if( (unsigned long) long(temp) > 0x00000FFF) return 0x00000FFF;
|
|
432
|
+ else return (unsigned long) long(temp);
|
|
433
|
+}
|
|
434
|
+
|
|
435
|
+unsigned long L6470::MaxSpdCalc(float stepsPerSec){
|
|
436
|
+ // The value in the MAX_SPD register is [(steps/s)*(tick)]/(2^-18) where tick is
|
|
437
|
+ // 250ns (datasheet value)- 0x041 on boot.
|
|
438
|
+ // Multiply desired steps/s by .065536 to get an appropriate value for this register
|
|
439
|
+ // This is a 10-bit value, so we need to make sure it remains at or below 0x3FF
|
|
440
|
+ float temp = stepsPerSec * .065536;
|
|
441
|
+ if( (unsigned long) long(temp) > 0x000003FF) return 0x000003FF;
|
|
442
|
+ else return (unsigned long) long(temp);
|
|
443
|
+}
|
|
444
|
+
|
|
445
|
+unsigned long L6470::MinSpdCalc(float stepsPerSec){
|
|
446
|
+ // The value in the MIN_SPD register is [(steps/s)*(tick)]/(2^-24) where tick is
|
|
447
|
+ // 250ns (datasheet value)- 0x000 on boot.
|
|
448
|
+ // Multiply desired steps/s by 4.1943 to get an appropriate value for this register
|
|
449
|
+ // This is a 12-bit value, so we need to make sure the value is at or below 0xFFF.
|
|
450
|
+ float temp = stepsPerSec * 4.1943;
|
|
451
|
+ if( (unsigned long) long(temp) > 0x00000FFF) return 0x00000FFF;
|
|
452
|
+ else return (unsigned long) long(temp);
|
|
453
|
+}
|
|
454
|
+
|
|
455
|
+unsigned long L6470::FSCalc(float stepsPerSec){
|
|
456
|
+ // The value in the FS_SPD register is ([(steps/s)*(tick)]/(2^-18))-0.5 where tick is
|
|
457
|
+ // 250ns (datasheet value)- 0x027 on boot.
|
|
458
|
+ // Multiply desired steps/s by .065536 and subtract .5 to get an appropriate value for this register
|
|
459
|
+ // This is a 10-bit value, so we need to make sure the value is at or below 0x3FF.
|
|
460
|
+ float temp = (stepsPerSec * .065536)-.5;
|
|
461
|
+ if( (unsigned long) long(temp) > 0x000003FF) return 0x000003FF;
|
|
462
|
+ else return (unsigned long) long(temp);
|
|
463
|
+}
|
|
464
|
+
|
|
465
|
+unsigned long L6470::IntSpdCalc(float stepsPerSec){
|
|
466
|
+ // The value in the INT_SPD register is [(steps/s)*(tick)]/(2^-24) where tick is
|
|
467
|
+ // 250ns (datasheet value)- 0x408 on boot.
|
|
468
|
+ // Multiply desired steps/s by 4.1943 to get an appropriate value for this register
|
|
469
|
+ // This is a 14-bit value, so we need to make sure the value is at or below 0x3FFF.
|
|
470
|
+ float temp = stepsPerSec * 4.1943;
|
|
471
|
+ if( (unsigned long) long(temp) > 0x00003FFF) return 0x00003FFF;
|
|
472
|
+ else return (unsigned long) long(temp);
|
|
473
|
+}
|
|
474
|
+
|
|
475
|
+unsigned long L6470::SpdCalc(float stepsPerSec){
|
|
476
|
+ // When issuing RUN command, the 20-bit speed is [(steps/s)*(tick)]/(2^-28) where tick is
|
|
477
|
+ // 250ns (datasheet value).
|
|
478
|
+ // Multiply desired steps/s by 67.106 to get an appropriate value for this register
|
|
479
|
+ // This is a 20-bit value, so we need to make sure the value is at or below 0xFFFFF.
|
|
480
|
+
|
|
481
|
+ float temp = stepsPerSec * 67.106;
|
|
482
|
+ if( (unsigned long) long(temp) > 0x000FFFFF) return 0x000FFFFF;
|
|
483
|
+ else return (unsigned long)temp;
|
|
484
|
+}
|
|
485
|
+
|
|
486
|
+unsigned long L6470::Param(unsigned long value, byte bit_len){
|
|
487
|
+ // Generalization of the subsections of the register read/write functionality.
|
|
488
|
+ // We want the end user to just write the value without worrying about length,
|
|
489
|
+ // so we pass a bit length parameter from the calling function.
|
|
490
|
+ unsigned long ret_val=0; // We'll return this to generalize this function
|
|
491
|
+ // for both read and write of registers.
|
|
492
|
+ byte byte_len = bit_len/8; // How many BYTES do we have?
|
|
493
|
+ if (bit_len%8 > 0) byte_len++; // Make sure not to lose any partial byte values.
|
|
494
|
+ // Let's make sure our value has no spurious bits set, and if the value was too
|
|
495
|
+ // high, max it out.
|
|
496
|
+ unsigned long mask = 0xffffffff >> (32-bit_len);
|
|
497
|
+ if (value > mask) value = mask;
|
|
498
|
+ // The following three if statements handle the various possible byte length
|
|
499
|
+ // transfers- it'll be no less than 1 but no more than 3 bytes of data.
|
|
500
|
+ // L6470::Xfer() sends a byte out through SPI and returns a byte received
|
|
501
|
+ // over SPI- when calling it, we typecast a shifted version of the masked
|
|
502
|
+ // value, then we shift the received value back by the same amount and
|
|
503
|
+ // store it until return time.
|
|
504
|
+ if (byte_len == 3) {
|
|
505
|
+ ret_val |= long(Xfer((byte)(value>>16))) << 16;
|
|
506
|
+ //Serial.println(ret_val, HEX);
|
|
507
|
+ }
|
|
508
|
+ if (byte_len >= 2) {
|
|
509
|
+ ret_val |= long(Xfer((byte)(value>>8))) << 8;
|
|
510
|
+ //Serial.println(ret_val, HEX);
|
|
511
|
+ }
|
|
512
|
+ if (byte_len >= 1) {
|
|
513
|
+ ret_val |= Xfer((byte)value);
|
|
514
|
+ //Serial.println(ret_val, HEX);
|
|
515
|
+ }
|
|
516
|
+ // Return the received values. Mask off any unnecessary bits, just for
|
|
517
|
+ // the sake of thoroughness- we don't EXPECT to see anything outside
|
|
518
|
+ // the bit length range but better to be safe than sorry.
|
|
519
|
+ return (ret_val & mask);
|
|
520
|
+}
|
|
521
|
+
|
|
522
|
+byte L6470::Xfer(byte data){
|
|
523
|
+ // This simple function shifts a byte out over SPI and receives a byte over
|
|
524
|
+ // SPI. Unusually for SPI devices, the dSPIN requires a toggling of the
|
|
525
|
+ // CS (slaveSelect) pin after each byte sent. That makes this function
|
|
526
|
+ // a bit more reasonable, because we can include more functionality in it.
|
|
527
|
+ byte data_out;
|
|
528
|
+ digitalWrite(_SSPin,LOW);
|
|
529
|
+ // SPI.transfer() both shifts a byte out on the MOSI pin AND receives a
|
|
530
|
+ // byte in on the MISO pin.
|
|
531
|
+ data_out = SPI.transfer(data);
|
|
532
|
+ digitalWrite(_SSPin,HIGH);
|
|
533
|
+ return data_out;
|
|
534
|
+}
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+void L6470::SetParam(byte param, unsigned long value){
|
|
539
|
+ Xfer(SET_PARAM | param);
|
|
540
|
+ ParamHandler(param, value);
|
|
541
|
+}
|
|
542
|
+
|
|
543
|
+unsigned long L6470::GetParam(byte param){
|
|
544
|
+ // Realize the "get parameter" function, to read from the various registers in
|
|
545
|
+ // the dSPIN chip.
|
|
546
|
+ Xfer(GET_PARAM | param);
|
|
547
|
+ return ParamHandler(param, 0);
|
|
548
|
+}
|
|
549
|
+
|
|
550
|
+long L6470::convert(unsigned long val){
|
|
551
|
+ //convert 22bit 2s comp to signed long
|
|
552
|
+ int MSB = val >> 21;
|
|
553
|
+
|
|
554
|
+ val = val << 11;
|
|
555
|
+ val = val >> 11;
|
|
556
|
+
|
|
557
|
+ if(MSB == 1) val = val | 0b11111111111000000000000000000000;
|
|
558
|
+ return val;
|
|
559
|
+}
|
|
560
|
+
|
|
561
|
+unsigned long L6470::ParamHandler(byte param, unsigned long value){
|
|
562
|
+ // Much of the functionality between "get parameter" and "set parameter" is
|
|
563
|
+ // very similar, so we deal with that by putting all of it in one function
|
|
564
|
+ // here to save memory space and simplify the program.
|
|
565
|
+ unsigned long ret_val = 0; // This is a temp for the value to return.
|
|
566
|
+ // This switch structure handles the appropriate action for each register.
|
|
567
|
+ // This is necessary since not all registers are of the same length, either
|
|
568
|
+ // bit-wise or byte-wise, so we want to make sure we mask out any spurious
|
|
569
|
+ // bits and do the right number of transfers. That is handled by the dSPIN_Param()
|
|
570
|
+ // function, in most cases, but for 1-byte or smaller transfers, we call
|
|
571
|
+ // Xfer() directly.
|
|
572
|
+ switch (param)
|
|
573
|
+ {
|
|
574
|
+ // ABS_POS is the current absolute offset from home. It is a 22 bit number expressed
|
|
575
|
+ // in two's complement. At power up, this value is 0. It cannot be written when
|
|
576
|
+ // the motor is running, but at any other time, it can be updated to change the
|
|
577
|
+ // interpreted position of the motor.
|
|
578
|
+ case ABS_POS:
|
|
579
|
+ ret_val = Param(value, 22);
|
|
580
|
+ break;
|
|
581
|
+ // EL_POS is the current electrical position in the step generation cycle. It can
|
|
582
|
+ // be set when the motor is not in motion. Value is 0 on power up.
|
|
583
|
+ case EL_POS:
|
|
584
|
+ ret_val = Param(value, 9);
|
|
585
|
+ break;
|
|
586
|
+ // MARK is a second position other than 0 that the motor can be told to go to. As
|
|
587
|
+ // with ABS_POS, it is 22-bit two's complement. Value is 0 on power up.
|
|
588
|
+ case MARK:
|
|
589
|
+ ret_val = Param(value, 22);
|
|
590
|
+ break;
|
|
591
|
+ // SPEED contains information about the current speed. It is read-only. It does
|
|
592
|
+ // NOT provide direction information.
|
|
593
|
+ case SPEED:
|
|
594
|
+ ret_val = Param(0, 20);
|
|
595
|
+ break;
|
|
596
|
+ // ACC and DEC set the acceleration and deceleration rates. Set ACC to 0xFFF
|
|
597
|
+ // to get infinite acceleration/decelaeration- there is no way to get infinite
|
|
598
|
+ // deceleration w/o infinite acceleration (except the HARD STOP command).
|
|
599
|
+ // Cannot be written while motor is running. Both default to 0x08A on power up.
|
|
600
|
+ // AccCalc() and DecCalc() functions exist to convert steps/s/s values into
|
|
601
|
+ // 12-bit values for these two registers.
|
|
602
|
+ case ACC:
|
|
603
|
+ ret_val = Param(value, 12);
|
|
604
|
+ break;
|
|
605
|
+ case DEC:
|
|
606
|
+ ret_val = Param(value, 12);
|
|
607
|
+ break;
|
|
608
|
+ // MAX_SPEED is just what it says- any command which attempts to set the speed
|
|
609
|
+ // of the motor above this value will simply cause the motor to turn at this
|
|
610
|
+ // speed. Value is 0x041 on power up.
|
|
611
|
+ // MaxSpdCalc() function exists to convert steps/s value into a 10-bit value
|
|
612
|
+ // for this register.
|
|
613
|
+ case MAX_SPEED:
|
|
614
|
+ ret_val = Param(value, 10);
|
|
615
|
+ break;
|
|
616
|
+ // MIN_SPEED controls two things- the activation of the low-speed optimization
|
|
617
|
+ // feature and the lowest speed the motor will be allowed to operate at. LSPD_OPT
|
|
618
|
+ // is the 13th bit, and when it is set, the minimum allowed speed is automatically
|
|
619
|
+ // set to zero. This value is 0 on startup.
|
|
620
|
+ // MinSpdCalc() function exists to convert steps/s value into a 12-bit value for this
|
|
621
|
+ // register. SetLowSpeedOpt() function exists to enable/disable the optimization feature.
|
|
622
|
+ case MIN_SPEED:
|
|
623
|
+ ret_val = Param(value, 12);
|
|
624
|
+ break;
|
|
625
|
+ // FS_SPD register contains a threshold value above which microstepping is disabled
|
|
626
|
+ // and the dSPIN operates in full-step mode. Defaults to 0x027 on power up.
|
|
627
|
+ // FSCalc() function exists to convert steps/s value into 10-bit integer for this
|
|
628
|
+ // register.
|
|
629
|
+ case FS_SPD:
|
|
630
|
+ ret_val = Param(value, 10);
|
|
631
|
+ break;
|
|
632
|
+ // KVAL is the maximum voltage of the PWM outputs. These 8-bit values are ratiometric
|
|
633
|
+ // representations: 255 for full output voltage, 128 for half, etc. Default is 0x29.
|
|
634
|
+ // The implications of different KVAL settings is too complex to dig into here, but
|
|
635
|
+ // it will usually work to max the value for RUN, ACC, and DEC. Maxing the value for
|
|
636
|
+ // HOLD may result in excessive power dissipation when the motor is not running.
|
|
637
|
+ case KVAL_HOLD:
|
|
638
|
+ ret_val = Xfer((byte)value);
|
|
639
|
+ break;
|
|
640
|
+ case KVAL_RUN:
|
|
641
|
+ ret_val = Xfer((byte)value);
|
|
642
|
+ break;
|
|
643
|
+ case KVAL_ACC:
|
|
644
|
+ ret_val = Xfer((byte)value);
|
|
645
|
+ break;
|
|
646
|
+ case KVAL_DEC:
|
|
647
|
+ ret_val = Xfer((byte)value);
|
|
648
|
+ break;
|
|
649
|
+ // INT_SPD, ST_SLP, FN_SLP_ACC and FN_SLP_DEC are all related to the back EMF
|
|
650
|
+ // compensation functionality. Please see the datasheet for details of this
|
|
651
|
+ // function- it is too complex to discuss here. Default values seem to work
|
|
652
|
+ // well enough.
|
|
653
|
+ case INT_SPD:
|
|
654
|
+ ret_val = Param(value, 14);
|
|
655
|
+ break;
|
|
656
|
+ case ST_SLP:
|
|
657
|
+ ret_val = Xfer((byte)value);
|
|
658
|
+ break;
|
|
659
|
+ case FN_SLP_ACC:
|
|
660
|
+ ret_val = Xfer((byte)value);
|
|
661
|
+ break;
|
|
662
|
+ case FN_SLP_DEC:
|
|
663
|
+ ret_val = Xfer((byte)value);
|
|
664
|
+ break;
|
|
665
|
+ // K_THERM is motor winding thermal drift compensation. Please see the datasheet
|
|
666
|
+ // for full details on operation- the default value should be okay for most users.
|
|
667
|
+ case K_THERM:
|
|
668
|
+ ret_val = Xfer((byte)value & 0x0F);
|
|
669
|
+ break;
|
|
670
|
+ // ADC_OUT is a read-only register containing the result of the ADC measurements.
|
|
671
|
+ // This is less useful than it sounds; see the datasheet for more information.
|
|
672
|
+ case ADC_OUT:
|
|
673
|
+ ret_val = Xfer(0);
|
|
674
|
+ break;
|
|
675
|
+ // Set the overcurrent threshold. Ranges from 375mA to 6A in steps of 375mA.
|
|
676
|
+ // A set of defined constants is provided for the user's convenience. Default
|
|
677
|
+ // value is 3.375A- 0x08. This is a 4-bit value.
|
|
678
|
+ case OCD_TH:
|
|
679
|
+ ret_val = Xfer((byte)value & 0x0F);
|
|
680
|
+ break;
|
|
681
|
+ // Stall current threshold. Defaults to 0x40, or 2.03A. Value is from 31.25mA to
|
|
682
|
+ // 4A in 31.25mA steps. This is a 7-bit value.
|
|
683
|
+ case STALL_TH:
|
|
684
|
+ ret_val = Xfer((byte)value & 0x7F);
|
|
685
|
+ break;
|
|
686
|
+ // STEP_MODE controls the microstepping settings, as well as the generation of an
|
|
687
|
+ // output signal from the dSPIN. Bits 2:0 control the number of microsteps per
|
|
688
|
+ // step the part will generate. Bit 7 controls whether the BUSY/SYNC pin outputs
|
|
689
|
+ // a BUSY signal or a step synchronization signal. Bits 6:4 control the frequency
|
|
690
|
+ // of the output signal relative to the full-step frequency; see datasheet for
|
|
691
|
+ // that relationship as it is too complex to reproduce here.
|
|
692
|
+ // Most likely, only the microsteps per step value will be needed; there is a set
|
|
693
|
+ // of constants provided for ease of use of these values.
|
|
694
|
+ case STEP_MODE:
|
|
695
|
+ ret_val = Xfer((byte)value);
|
|
696
|
+ break;
|
|
697
|
+ // ALARM_EN controls which alarms will cause the FLAG pin to fall. A set of constants
|
|
698
|
+ // is provided to make this easy to interpret. By default, ALL alarms will trigger the
|
|
699
|
+ // FLAG pin.
|
|
700
|
+ case ALARM_EN:
|
|
701
|
+ ret_val = Xfer((byte)value);
|
|
702
|
+ break;
|
|
703
|
+ // CONFIG contains some assorted configuration bits and fields. A fairly comprehensive
|
|
704
|
+ // set of reasonably self-explanatory constants is provided, but users should refer
|
|
705
|
+ // to the datasheet before modifying the contents of this register to be certain they
|
|
706
|
+ // understand the implications of their modifications. Value on boot is 0x2E88; this
|
|
707
|
+ // can be a useful way to verify proper start up and operation of the dSPIN chip.
|
|
708
|
+ case CONFIG:
|
|
709
|
+ ret_val = Param(value, 16);
|
|
710
|
+ break;
|
|
711
|
+ // STATUS contains read-only information about the current condition of the chip. A
|
|
712
|
+ // comprehensive set of constants for masking and testing this register is provided, but
|
|
713
|
+ // users should refer to the datasheet to ensure that they fully understand each one of
|
|
714
|
+ // the bits in the register.
|
|
715
|
+ case STATUS: // STATUS is a read-only register
|
|
716
|
+ ret_val = Param(0, 16);
|
|
717
|
+ break;
|
|
718
|
+ default:
|
|
719
|
+ ret_val = Xfer((byte)(value));
|
|
720
|
+ break;
|
|
721
|
+ }
|
|
722
|
+ return ret_val;
|
|
723
|
+}
|