-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathSR_stepper_thread.cpp
372 lines (350 loc) · 15.8 KB
/
SR_stepper_thread.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
#include "SR_stepper_thread.h"
/* *************************** Functions that are called from thread *********************
***************************************Initialization callback function ****************************************
Last Modified:
2018/10/22 by Jamie Boyd - initial Version */
int SR_stepper_init (void * initDataP, void * &taskDataP){
// task data pointer is a void pointer that needs to be initialized to a pointer to taskData and filled from our custom init structure
SR_StepperStructPtr taskData = new SR_StepperStruct;
taskDataP = taskData;
// initData is a pointer to our custom init structure
SR_StepperInitStructPtr initDataPtr = (SR_StepperInitStructPtr) initDataP;
// copy nMotors from init
taskData->nMotors = initDataPtr->nMotors;
// stuff copied and modified from SimpleGPIO
// calculate address to ON and OFF register as Hi or Lo as appropriate to save a level of indirection later
taskData->GPIOperiHi = (unsigned int *) (initDataPtr->GPIOperiAddr + 7);
taskData->GPIOperiLo = (unsigned int *) (initDataPtr->GPIOperiAddr + 10);
// calculate pin Bits
taskData->data_bit = 1 << initDataPtr->data_pin;
taskData->shift_reg_bit = 1 << initDataPtr->shift_reg_pin;
taskData->stor_reg_bit = 1 << initDataPtr->stor_reg_pin;
// initialize pins for output
*(initDataPtr->GPIOperiAddr + ((initDataPtr->data_pin) /10)) &= ~(7<<(((initDataPtr->data_pin) %10)*3));
*(initDataPtr->GPIOperiAddr + ((initDataPtr->data_pin)/10)) |= (1<<(((initDataPtr->data_pin)%10)*3));
*(initDataPtr->GPIOperiAddr + ((initDataPtr->shift_reg_pin) /10)) &= ~(7<<(((initDataPtr->shift_reg_pin) %10)*3));
*(initDataPtr->GPIOperiAddr + ((initDataPtr->shift_reg_pin)/10)) |= (1<<(((initDataPtr->shift_reg_pin)%10)*3));
*(initDataPtr->GPIOperiAddr + ((initDataPtr->stor_reg_pin) /10)) &= ~(7<<(((initDataPtr->stor_reg_pin) %10)*3));
*(initDataPtr->GPIOperiAddr + ((initDataPtr->stor_reg_pin)/10)) |= (1<<(((initDataPtr->stor_reg_pin)%10)*3));
// put pins in selected start state, shift_reg starts high, stor_reg starts low, data doesn't matter
*(taskData->GPIOperiHi ) = taskData->shift_reg_bit ;
*(taskData->GPIOperiLo ) = taskData->stor_reg_bit ;
// zero steps and direction arrays
for (int iMotor = 0; iMotor < taskData->nMotors; iMotor +=1){
taskData->nSteps[iMotor] = 0;
taskData->motorDir[iMotor] = 0;
}
taskData->iMotorAB =-1;
taskData-> iMotor = taskData->nMotors -1;
delete initDataPtr;
return 0; //
}
/* ********************************* High function, runs at start of pulse ************************************
sets shift_reg_pin low, sets value of data pin for the motor we are working on, sets up counters for next value
Last Modified:
2018/10/22 by Jamie Boyd - initial version */
void SR_stepper_Hi (void * taskDataP){
// cast void pointer to taskdataPtr
SR_StepperStructPtr taskData = (SR_StepperStructPtr) taskDataP;
// set shift register pin lo
*(taskData->GPIOperiLo ) = taskData->shift_reg_bit ;
// increment ABA/B/ position, iMotorAB
taskData->iMotorAB +=1;
// reset motorAB to 0 if we have done all 4 positions for this motor
if (taskData->iMotorAB == 3){
taskData->iMotorAB = 0;
// are we still moving this motor?
if (taskData->nSteps[taskData->iMotor] != 0){
// decrement needed steps for this motor, depending on direction
taskData->nSteps[taskData->iMotor] -= taskData->motorDir [taskData->iMotor];
// move to start of next set of 4 positions for this motor
// handle wrap-around for positive direction
if ((taskData->motorDataPos [taskData->iMotor] == 7) && (taskData->motorDir [taskData->iMotor] == 1)){
taskData->motorDataPos [taskData->iMotor] = 0;
}else{
// handle wrap-around for negative direction
if ((taskData->motorDataPos [taskData->iMotor] == 0) && (taskData->motorDir [taskData->iMotor] == -1)){
taskData->motorDataPos [taskData->iMotor] =7;
}else{
// handle normal progression of +1 or -1, or not going anywhere, based on motorDir
taskData->motorDataPos[taskData->iMotor] += taskData->motorDir [taskData->iMotor];
}
}
}
// decrement iMotor, as we have finished 4 positions for this motor
taskData->iMotor -= 1;
// if we have reached end of motors, reset to top of motors
if (taskData->iMotor < 0){
taskData->iMotor = taskData->nMotors -1;
}
}
// set data pin high or low, getting data for motor and position from shiftData
int dataBit = taskData->shiftData [(4 * taskData->motorDataPos [taskData->iMotor]) + taskData->iMotorAB];
if (dataBit){
*(taskData->GPIOperiHi ) = taskData->data_bit;
}else{
*(taskData->GPIOperiLo) = taskData->data_bit;
}
}
/* ******************************* Low Function **********************************************
sets shift_reg pin HIGH. Sets storage resgister low on first pulse of train
Last Modified:
2018/10/22 by Jamie Boyd - initial version */
void SR_stepper_Lo (void * taskDataP){
// cast void pointer to taskdataPtr
SR_StepperStructPtr taskData = (SR_StepperStructPtr) taskDataP;
// set shift register pin HIGH
*(taskData->GPIOperiHi ) = taskData->shift_reg_bit ;
// on 1st pulse of train (it could be any pulse), set storage register pin LOW
if ((taskData->iMotor == taskData->nMotors -1) && (taskData->iMotorAB == 0)) {
*(taskData->GPIOperiLo ) = taskData->stor_reg_bit;
}
}
/* ********************************** End function after each train **************************************
sets stor_reg pin high. Needs no endFunc data, uses same taskData used by hi and lo funcs
Last Modified:
2018/10/22 by Jamie Boyd - initial version */
void SR_stepper_EndFunc (void * endFuncData, taskParams * theTask){
SR_StepperStructPtr taskData = (SR_StepperStructPtr)theTask->taskData;
*(taskData->GPIOperiHi ) = taskData->stor_reg_bit;
}
/* ****************************** Custom delete Function ****************************************************
deletes taskData
Last Modified:
2018/10/23 by jamie Boyd - Initial verison */
void SR_stepper_delTask (void * taskData){
SR_StepperStructPtr taskPtr = (SR_StepperStructPtr) taskData;
delete (taskPtr);
}
/* ********************************* SR_stepper_thread class methods *****************************************
*************************************** Static thread maker ***********************************************
returns a new SR_stepper_thread
Last Modified:
2018/10/23 by Jamie Boyd - initial version */
SR_stepper_thread * SR_stepper_thread::SR_stepper_threadMaker (int data_pinP, int shift_reg_pinP, int stor_reg_pinP, int nMotorsP, float steps_per_secP, int accuracyLevel) {
// make and fill an init struct
SR_StepperInitStructPtr initStruct = new SR_StepperInitStruct;
initStruct->data_pin = data_pinP;
initStruct->shift_reg_pin = shift_reg_pinP;
initStruct->stor_reg_pin=stor_reg_pinP;
initStruct->nMotors = nMotorsP;
initStruct->GPIOperiAddr = useGpioPeri ();
if (initStruct->GPIOperiAddr == nullptr){
#if beVerbose
printf ("SR_stepper_threadMaker failed to map GPIO peripheral.\n");
#endif
return nullptr;
}
int errCode =0;
unsigned int durUsecs = (unsigned int) (5e05/(steps_per_secP * nMotorsP * 4));
unsigned int nPulses = nMotorsP * 4;
// call SR_stepper_thread constructor, which calls pulsedThread contructor
SR_stepper_thread * newSR_stepper = new SR_stepper_thread (durUsecs, nPulses, (void *) initStruct, accuracyLevel, errCode);
if (errCode){
#if beVerbose
printf ("SimpleGPIO_threadMaker failed to make SimpleGPIO_thread.\n");
#endif
return nullptr;
}
// set custom task delete function
newSR_stepper->setTaskDataDelFunc (&SR_stepper_delTask);
// Set the SR_StepperStructPtr used for easy direct access to thread task data
newSR_stepper->taskPtr = (SR_StepperStructPtr)newSR_stepper->getTaskData ();
// set the end function (it does not use any any special data, so no need to set endFunc data)
newSR_stepper->setEndFunc (&SR_stepper_EndFunc);
return newSR_stepper;
}
/* ***************************************** move function for all stepper motors **********************************
values in array of distances are steps relative to current position, negative values for negative distances
Last Modified:
2018/10/23 by Jamie Boyd - initial version */
void SR_stepper_thread::moveSteps (int mDists [MAX_MOTORS]){
// need maximum steps of all motors to get number of trains to request
// also need to set counter variables in thread data
//this->getTaskMutex(); // get taskMutex - can, in theroy, be calling move with trains left to do
int trainsInHand = this->isBusy();
int neededTrains =0;
for (int iMotor = 0; iMotor < this->taskPtr->nMotors; iMotor +=1){
if (mDists [iMotor] > 0){
this->taskPtr->motorDir [iMotor] = 1;
this->taskPtr->nSteps [iMotor] += mDists [iMotor] ;
}else{
if (mDists [iMotor] < 0){
this->taskPtr->motorDir [iMotor] = -1;
this->taskPtr->nSteps [iMotor] += mDists [iMotor] ;
}
}
neededTrains = neededTrains >= abs (this->taskPtr->nSteps [iMotor] ) ? neededTrains : abs (this->taskPtr->nSteps [iMotor] ) ;
//neededTrains = max (neededTrains, abs (this->taskPtr->nSteps [iMotor] )) ;
}
#if beVerbose
printf ("Needed trains = %d\n", neededTrains);
#endif
//this->giveUpTaskMutex(); // give up taskMutex
this->DoTasks (neededTrains - trainsInHand); // call doTasks with required number of trains
}
/* ********************************************** sets stepper motors free to move *********************************
mFree is an array where a 1 means unhold the motor by setting all 4 outputs to 0, and a 0 means to leave the motor as it is
not done in a threaded fashion, just done as class method using pulsedThread's sleep method 1 style, no great need for accuracy
Last modified:
2018/10/23 by Jamie Boyd - Initial Version */
int SR_stepper_thread::Free (int mFree [MAX_MOTORS]){
// wait until we are no longer moving
if (this->waitOnBusy(10)){
#if beVerbose
printf (" waited for 10 seconds and thread was still busy, so could not set Free state.\n");
#endif
return 1;
}
// get pulse timeing
int pulseDurUsecs = this->getpulseDurUsecs ();
// make a timespec for sleeping
struct timespec sleeper; // used to sleep between ticks of the clock
sleeper.tv_sec = pulseDurUsecs/1e06;
sleeper.tv_nsec = (pulseDurUsecs - (sleeper.tv_sec * 1e06))* 1e03;
// lock the thread.
this->getTaskMutex(); // lock the thread, but thread should not be active
// set storage register pin lo
*(this->taskPtr->GPIOperiLo ) = this->taskPtr->stor_reg_bit ;
// loop through A,B,A/,B/ for all motors
int iMotorAB =0;
int iMotor;
int dataBit;
for (iMotor = this->taskPtr->nMotors -1; iMotor >= 0; iMotor -=1){
if (mFree [iMotor]){ // zero this motor to free it
for (iMotorAB =0; iMotorAB < 4; iMotorAB += 1){
// set shift register pin low
*(this->taskPtr->GPIOperiLo ) = this->taskPtr->shift_reg_bit ;
// set data pin low
*(this->taskPtr->GPIOperiLo ) = this->taskPtr->data_bit ;
// sleep for a bit
nanosleep (&sleeper, NULL);
// set shift register pin high
*(this->taskPtr->GPIOperiHi) = this->taskPtr->shift_reg_bit ;
// sleep for a bit
nanosleep (&sleeper, NULL);
}
}else{ // leave this motor how it is by outputting current data
for (iMotorAB =0; iMotorAB < 4; iMotorAB += 1){
// set shift register pin low
*(this->taskPtr->GPIOperiLo ) = this->taskPtr->shift_reg_bit ;
// set data pin
dataBit = this->taskPtr->shiftData [(4 * this->taskPtr->motorDataPos [iMotor]) + iMotorAB];
if (dataBit){
*(this->taskPtr->GPIOperiHi) = this->taskPtr->data_bit ;
}else{
*(this->taskPtr->GPIOperiLo ) = this->taskPtr->data_bit ;
}
// sleep for a bit
nanosleep (&sleeper, NULL);
// set shift register pin hi
*(this->taskPtr->GPIOperiHi) = this->taskPtr->shift_reg_bit ;
// sleep for a bit
nanosleep (&sleeper, NULL);
}
}
}
// set storage register pin High
*(this->taskPtr->GPIOperiHi ) = this->taskPtr->stor_reg_bit ;
// unlock the thread
this->giveUpTaskMutex(); // give up taskMutex
return 0;
}
/* *********************************************** after freeing, this function holds motors steady in place *************************
mHolds is an array where a 1 means to hold the motor firm by setting to position 7 where both coils are energized, and 0 is to leave the motor as is
2018/10/23 by Jamie Boyd - Initial Version */
int SR_stepper_thread::Hold (int mHold [MAX_MOTORS]){
// wait until we are no longer moving
if (this->waitOnBusy(10)){
#if beVerbose
printf (" waited for 10 seconds and thread was still busy, so could not set Hold state.\n");
#endif
return 1;
}
// get pulse timeing
int pulseDurUsecs = this->getpulseDurUsecs ();
// make a timespec for sleeping
struct timespec sleeper; // used to sleep between ticks of the clock
sleeper.tv_sec = pulseDurUsecs/1e06;
sleeper.tv_nsec = (pulseDurUsecs - (sleeper.tv_sec * 1e06))* 1e03;
// lock the thread.
this->getTaskMutex(); // lock the thread, but thread should not be active
// set storage register pin lo
*(this->taskPtr->GPIOperiLo ) = this->taskPtr->stor_reg_bit ;
// loop through A,B,A/,B/ for all motors
int iMotorAB =0;
int iMotor;
int dataBit;
for (iMotor = this->taskPtr->nMotors -1; iMotor >= 0; iMotor -=1){
if (mHold [iMotor]){ // set this motor to position 7 in the array of A,B,A/,B/ positions
this->taskPtr->motorDataPos [iMotor] =7;
}
for (iMotorAB =0; iMotorAB < 4; iMotorAB += 1){
// set shift register pin low
*(this->taskPtr->GPIOperiLo ) = this->taskPtr->shift_reg_bit ;
// set data pin
dataBit = this->taskPtr->shiftData [(4 * this->taskPtr->motorDataPos [iMotor]) + iMotorAB];
if (dataBit){
*(this->taskPtr->GPIOperiHi) = this->taskPtr->data_bit ;
}else{
*(this->taskPtr->GPIOperiLo ) = this->taskPtr->data_bit ;
}
// sleep for a bit
nanosleep (&sleeper, NULL);
// set shift register pin hi
*(this->taskPtr->GPIOperiHi) = this->taskPtr->shift_reg_bit ;
// sleep for a bit
nanosleep (&sleeper, NULL);
}
}
// set storage register pin High
*(this->taskPtr->GPIOperiHi ) = this->taskPtr->stor_reg_bit ;
// unlock the thread
this->giveUpTaskMutex(); // give up taskMutex
return 0;
}
/* ********************************************* Emergency Stop ***********************************************
calls unDoTasks and sets nSteps to 0 for all motors
Last Modified:
2018/10/23 by Jamie Boyd - initial Version */
int SR_stepper_thread::emergStop (){
this->UnDoTasks ();
if (this->waitOnBusy(10)){
#if beVerbose
printf (" waited for 10 seconds and thread was still busy, emergeStop not successful.\n");
#endif
return 1;
}
// lock the thread.
this->getTaskMutex(); // lock the thread, but thread should not be active
// zero steps and direction arrays
for (int iMotor = 0; iMotor < this->taskPtr->nMotors; iMotor +=1){
this->taskPtr->nSteps[iMotor] = 0;
this->taskPtr->motorDir[iMotor] = 0;
}
this->taskPtr->iMotorAB =0;
this->taskPtr->iMotor = this->taskPtr->nMotors -1;
this->giveUpTaskMutex(); // give up taskMutex
return 0;
}
/* **************************** Setters and Getters ********************************************
*************************** gets motor speed in steps per second *******************************
All motors move at same number of steps/second; train pulse time decreases with increasing number of motors
Last Modified:
2018/10/23 by Jamie Boyd - initial Version */
float SR_stepper_thread::getStepsPerSec (){
return (float)this->getTrainFrequency()/((4 * this->taskPtr->nMotors));
}
/* ******************************* sets motor speed in steps per second *******************
Last Modified:
2018/10/23 by Jamie Boyd - initial Version */
int SR_stepper_thread::setStepsPerSec (float stepsPerSec){
return this->modFreq (stepsPerSec * 4 * this->taskPtr->nMotors);
}
/* ****************************** Destructor handles GPIO peripheral mapping*************************
Thread data is destroyed by the pulsedThread destructor. All we need to do here is take care of GPIO peripheral mapping
Last Modified:
2018/10/023 by Jamie Boyd - Initial Version */
SR_stepper_thread::~SR_stepper_thread (){
unUseGPIOperi();
}