Skip to content

Commit d38a86a

Browse files
committed
run code formatting and update API markdown file
1 parent 62f0068 commit d38a86a

File tree

5 files changed

+215
-130
lines changed

5 files changed

+215
-130
lines changed

examples/MoveTimed/MoveTimed.ino

Lines changed: 70 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66
#endif
77

88
// As in StepperDemo for Motor 1 on AVR
9-
#define dirPinStepperX 5
9+
#define dirPinStepperX 5
1010
#define enablePinStepperX 6
11-
#define stepPinStepperX stepPinStepper1A
11+
#define stepPinStepperX stepPinStepper1A
1212

1313
// As in StepperDemo for Motor 2 on AVR
14-
#define dirPinStepperY 7
14+
#define dirPinStepperY 7
1515
#define enablePinStepperY 8
16-
#define stepPinStepperY stepPinStepper1B
16+
#define stepPinStepperY stepPinStepper1B
1717

1818
FastAccelStepperEngine engine = FastAccelStepperEngine();
1919
FastAccelStepper *stepperX = NULL;
@@ -24,22 +24,25 @@ struct control_s {
2424
uint16_t phi;
2525
uint16_t drift; // time delta in ticks
2626
};
27-
struct control_s controlX = {
28-
.time = 0, .phi = 0, .drift = 0
29-
};
30-
struct control_s controlY = {
31-
.time = 0, .phi = 90, .drift = 0
32-
};
27+
struct control_s controlX = {.time = 0, .phi = 0, .drift = 0};
28+
struct control_s controlY = {.time = 0, .phi = 90, .drift = 0};
3329

3430
// This means, the circle is executed in 360*4ms = 1440ms
3531
#ifndef TIMESTEP_TICKS
36-
#define TIMESTEP_TICKS (TICKS_PER_S/250)
32+
#define TIMESTEP_TICKS (TICKS_PER_S / 250)
3733
#endif
3834

3935
// Table generated by python:
4036
// [int(math.sin(x*math.pi/180)*1600) for x in range(0,91)]
41-
int16_t steps[91] = { 0, 27, 55, 83, 111, 139, 167, 194, 222, 250, 277, 305, 332, 359, 387, 414, 441, 467, 494, 520, 547, 573, 599, 625, 650, 676, 701, 726, 751, 775, 799, 824, 847, 871, 894, 917, 940, 962, 985, 1006, 1028, 1049, 1070, 1091, 1111, 1131, 1150, 1170, 1189, 1207, 1225, 1243, 1260, 1277, 1294, 1310, 1326, 1341, 1356, 1371, 1385, 1399, 1412, 1425, 1438, 1450, 1461, 1472, 1483, 1493, 1503, 1512, 1521, 1530, 1538, 1545, 1552, 1558, 1565, 1570, 1575, 1580, 1584, 1588, 1591, 1593, 1596, 1597, 1599, 1599, 1600
42-
};
37+
int16_t steps[91] = {
38+
0, 27, 55, 83, 111, 139, 167, 194, 222, 250, 277, 305,
39+
332, 359, 387, 414, 441, 467, 494, 520, 547, 573, 599, 625,
40+
650, 676, 701, 726, 751, 775, 799, 824, 847, 871, 894, 917,
41+
940, 962, 985, 1006, 1028, 1049, 1070, 1091, 1111, 1131, 1150, 1170,
42+
1189, 1207, 1225, 1243, 1260, 1277, 1294, 1310, 1326, 1341, 1356, 1371,
43+
1385, 1399, 1412, 1425, 1438, 1450, 1461, 1472, 1483, 1493, 1503, 1512,
44+
1521, 1530, 1538, 1545, 1552, 1558, 1565, 1570, 1575, 1580, 1584, 1588,
45+
1591, 1593, 1596, 1597, 1599, 1599, 1600};
4346

4447
void setup() {
4548
Serial.begin(115200);
@@ -48,8 +51,8 @@ void setup() {
4851
stepperX = engine.stepperConnectToPin(stepPinStepperX);
4952
stepperY = engine.stepperConnectToPin(stepPinStepperY);
5053
if (!stepperX || !stepperY) {
51-
while(0==0) {
52-
Serial.println("Cannot initialize steppers");
54+
while (0 == 0) {
55+
Serial.println("Cannot initialize steppers");
5356
}
5457
}
5558
stepperX->setDirectionPin(dirPinStepperX);
@@ -66,61 +69,61 @@ void setup() {
6669
stepperY->enableOutputs();
6770

6871
// Fill the queue with 1ms delay and start the queues.
69-
stepperX->moveTimed(0,TICKS_PER_S/1000,NULL,false);
70-
stepperY->moveTimed(0,TICKS_PER_S/1000,NULL,false);
72+
stepperX->moveTimed(0, TICKS_PER_S / 1000, NULL, false);
73+
stepperY->moveTimed(0, TICKS_PER_S / 1000, NULL, false);
7174

7275
// Start the queues. Currently best but not perfect synchronicity
73-
stepperX->moveTimed(0,0,NULL,true);
74-
stepperY->moveTimed(0,0,NULL,true);
76+
stepperX->moveTimed(0, 0, NULL, true);
77+
stepperY->moveTimed(0, 0, NULL, true);
7578
}
7679

7780
void moveStepper(FastAccelStepper *stepper, struct control_s *control) {
78-
uint16_t phi = control->phi % 360;
79-
80-
// The table contains only one quadrant
81-
uint16_t index = phi;
82-
bool negate = false;
83-
if (index > 180) {
84-
negate = true;
85-
index = 360 - index;
86-
}
87-
if (index > 90) {
88-
index = 180 - index;
89-
}
90-
int16_t position = steps[index];
91-
if (negate) {
92-
position = -position;
93-
}
94-
int32_t current_position = stepper->getPositionAfterCommandsCompleted();
95-
int32_t steps = position - current_position;
96-
uint32_t actual;
97-
int8_t rc;
98-
uint32_t duration = TIMESTEP_TICKS + control->drift;
99-
rc = stepper->moveTimed(steps,duration,&actual,true);
100-
switch(rc) {
101-
case MOVE_TIMED_EMPTY:
102-
Serial.print("Empty:");
103-
Serial.println(stepper->getStepPin() == dirPinStepperX ? 'X':'Y');
104-
/* fallthrough */
105-
case MOVE_TIMED_OK:
106-
control->drift = duration-actual;
107-
control->time += actual;
108-
control->phi += 1;
109-
break;
110-
case MOVE_TIMED_BUSY:
111-
//Serial.println("Busy");
112-
break;
113-
case MOVE_TIMED_TOO_LARGE_ERROR:
114-
Serial.println("Too large");
115-
break;
116-
case AQE_ERROR_TICKS_TOO_LOW:
117-
Serial.print("Ticks too low:");
118-
Serial.print(duration/steps);
119-
Serial.print(" steps:");
120-
Serial.println(steps);
121-
default:
122-
Serial.println(rc);
123-
}
81+
uint16_t phi = control->phi % 360;
82+
83+
// The table contains only one quadrant
84+
uint16_t index = phi;
85+
bool negate = false;
86+
if (index > 180) {
87+
negate = true;
88+
index = 360 - index;
89+
}
90+
if (index > 90) {
91+
index = 180 - index;
92+
}
93+
int16_t position = steps[index];
94+
if (negate) {
95+
position = -position;
96+
}
97+
int32_t current_position = stepper->getPositionAfterCommandsCompleted();
98+
int32_t steps = position - current_position;
99+
uint32_t actual;
100+
int8_t rc;
101+
uint32_t duration = TIMESTEP_TICKS + control->drift;
102+
rc = stepper->moveTimed(steps, duration, &actual, true);
103+
switch (rc) {
104+
case MOVE_TIMED_EMPTY:
105+
Serial.print("Empty:");
106+
Serial.println(stepper->getStepPin() == dirPinStepperX ? 'X' : 'Y');
107+
/* fallthrough */
108+
case MOVE_TIMED_OK:
109+
control->drift = duration - actual;
110+
control->time += actual;
111+
control->phi += 1;
112+
break;
113+
case MOVE_TIMED_BUSY:
114+
// Serial.println("Busy");
115+
break;
116+
case MOVE_TIMED_TOO_LARGE_ERROR:
117+
Serial.println("Too large");
118+
break;
119+
case AQE_ERROR_TICKS_TOO_LOW:
120+
Serial.print("Ticks too low:");
121+
Serial.print(duration / steps);
122+
Serial.print(" steps:");
123+
Serial.println(steps);
124+
default:
125+
Serial.println(rc);
126+
}
124127
}
125128

126129
uint32_t last_millis = 0;
@@ -141,12 +144,11 @@ void loop() {
141144

142145
if (controlX.time < controlY.time) {
143146
moveStepper(stepperX, &controlX);
144-
}
145-
else {
147+
} else {
146148
moveStepper(stepperY, &controlY);
147149
}
148150
if ((controlX.phi != 361) && (controlY.phi != 451)) {
149-
return;
151+
return;
150152
}
151153
// Full round should be completed
152154
#ifdef SIMULATOR

extras/doc/FastAccelStepper_API.md

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@ controlled by a continuously running task. This task can be fixed to one
5454
CPU core with this modified init()-call. ESP32 implementation detail: For
5555
values 0 and 1, xTaskCreatePinnedToCore() is used, or else xTaskCreate()
5656
```cpp
57-
void init(uint8_t cpu_core);
57+
void init(uint8_t cpu_core = 255);
58+
#else
59+
void init();
5860
#endif
5961
```
6062
### Creation of FastAccelStepper
@@ -81,6 +83,35 @@ This call allows to select the respective driver
8183
uint8_t driver_type = DRIVER_DONT_CARE);
8284
#endif
8385
```
86+
For e.g. esp32 the repetition rate of the stepper task can be changed.
87+
The default delay is 4ms.
88+
89+
The steppertask is looping with:
90+
manageSteppers()
91+
wdt_reset()
92+
delay()
93+
94+
The actual repetition rate of the stepper task is delay + execution time of
95+
manageSteppers()
96+
97+
This function is primary of interest in conjunction with
98+
setForwardPlanningTimeInMs(). If the delay is larger then forward planning
99+
time, then the stepper queue will always run out of commands, which lead to
100+
a sudden stop of the motor. If the delay is 0, then the stepper task will
101+
constantly looping, which may lead to the task blocking other tasks.
102+
Consequently, this function is intended for advanced users.
103+
104+
There is not planned to test this functionality, because automatic testing
105+
is only available for avr devices and those continue to use fixed 4ms rate.
106+
107+
Please be aware, that the configured tick rate aka portTICK_PERIOD_MS is
108+
relevant. Apparently, arduino-esp32 has FreeRTOS configured to have a
109+
tick-rate of 1000Hz
110+
```cpp
111+
void task_rate(uint8_t delay_ms) { _delay_ms = delay_ms; };
112+
uint8_t _delay_ms;
113+
#endif
114+
```
84115
Comments to valid pins:
85116
86117
| Device | Comment |
@@ -572,6 +603,54 @@ suddenly stopping
572603
```cpp
573604
}
574605
```
606+
## Intermediate Level Stepper Control for Advanced Users
607+
608+
The main purpose is to bypass the ramp generator as mentioned in
609+
[#299](https:github.com/gin66/FastAccelStepper/issues/299).
610+
This shall allow to run consecutive small moves with fixed speed.
611+
The parameters are steps (which can be 0) and duration in ticks.
612+
steps=0 makes sense in order to keep the time running and not
613+
getting out of sync.
614+
Due to integer arithmetics the actual duration may be off by a small value.
615+
That's why the actual_duration in TICKS is returned.
616+
The application should consider this for the next runTimed move.
617+
618+
The optional parameter is a boolean called start. This allows for the first
619+
invocation to not start the queue yet. This is for managing steppers in
620+
parallel. It allows to fill all steppers' queues and then kick it off by a
621+
call to `moveTimed(0,0,NULL,true)`. Successive invocations can keep true.
622+
623+
In order to not have another lightweight ramp generator running in
624+
background interrupt, the expecation to the application is, that this
625+
function is frequently enough called without the queue being emptied.
626+
627+
The current implementation immediately starts with a step, if there should be
628+
one. Perhaps performing the step in the middle of the duration is more
629+
appropriate ?
630+
631+
Meaning of the return values - which are in addtion to AQE from below
632+
- OK: Move has been successfully appended to the queue
633+
- BUSY: Queue does not have sufficient entries to append this timed
634+
move.
635+
- EMPTY: The queue has run out of commands, but the move has been
636+
appended.
637+
- TOO_LARGE: The move request does not fit into the queue.
638+
Reasons: The queue depth is (32/16) for SAM+ESP32/AVR.
639+
Each queue entry can emit 255 steps => (8160/4080)
640+
steps If the time between steps is >65535 ticks, then
641+
pauses have to be generated. In this case only (16/8)
642+
steps can be generated...but the queue shall not be
643+
empty
644+
=> so even less steps can be done.
645+
Recommendation: keep the duration in the range of ms.
646+
```cpp
647+
#define MOVE_TIMED_OK ((int8_t)0)
648+
#define MOVE_TIMED_BUSY ((int8_t)5)
649+
#define MOVE_TIMED_EMPTY ((int8_t)6)
650+
#define MOVE_TIMED_TOO_LARGE_ERROR ((int8_t)-4)
651+
int8_t moveTimed(int16_t steps, uint32_t duration, uint32_t* actual_duration,
652+
bool start = true);
653+
```
575654
## Low Level Stepper Queue Management (low level access)
576655
577656
If the queue is already running, then the start parameter is obsolote.

0 commit comments

Comments
 (0)