소스 검색

Fix up stepper ISR with linear advance timing (#10853)

Co-Authored-By: ejtagle <ejtagle@hotmail.com>
Scott Lahteine 6 년 전
부모
커밋
01d37e00af
No account linked to committer's email address
1개의 변경된 파일91개의 추가작업 그리고 48개의 파일을 삭제
  1. 91
    48
      Marlin/src/module/stepper.cpp

+ 91
- 48
Marlin/src/module/stepper.cpp 파일 보기

@@ -642,7 +642,7 @@ void Stepper::set_directions() {
642 642
         A("mul %10,%9")             /* r1:r0 = 10*HI(v0-v1) */
643 643
         A("add %7,r0")              /* %7:%6:?? += 10*HI(v0-v1) << 16 */
644 644
         A("sts bezier_C+1, %6")
645
-        " sts bezier_C+2, %7"            /* bezier_C = %7:%6:?? = 10*(v0-v1) [65 cycles worst] */
645
+        " sts bezier_C+2, %7"       /* bezier_C = %7:%6:?? = 10*(v0-v1) [65 cycles worst] */
646 646
         : "+r" (r2),
647 647
           "+d" (r3),
648 648
           "=r" (r4),
@@ -1025,7 +1025,7 @@ void Stepper::set_directions() {
1025 1025
         A("add %3,r0")
1026 1026
         A("adc %4,r1")              /* %4:%3:%2:%9 += HI(bezier_A) * LO(f) << 16*/
1027 1027
         L("2")
1028
-        " clr __zero_reg__"              /* C runtime expects r1 = __zero_reg__ = 0 */
1028
+        " clr __zero_reg__"         /* C runtime expects r1 = __zero_reg__ = 0 */
1029 1029
         : "+r"(r0),
1030 1030
           "+r"(r1),
1031 1031
           "+r"(r2),
@@ -1152,16 +1152,8 @@ HAL_STEP_TIMER_ISR {
1152 1152
   // Call the ISR scheduler
1153 1153
   hal_timer_t ticks = Stepper::isr_scheduler();
1154 1154
 
1155
-  // Now 'ticks' contains the period to the next Stepper ISR.
1156
-  // Potential problem: Since the timer continues to run, the requested
1157
-  // compare value may already have passed.
1158
-  //
1159
-  // Assuming at least 6µs between calls to this ISR...
1160
-  // On AVR the ISR epilogue is estimated at 40 instructions - close to 2.5µS.
1161
-  // On ARM the ISR epilogue is estimated at 10 instructions - close to 200nS.
1162
-  // In either case leave at least 4µS for other tasks to execute.
1163
-  const hal_timer_t minticks = HAL_timer_get_count(STEP_TIMER_NUM) + hal_timer_t((HAL_TICKS_PER_US) * 4); // ISR never takes more than 1ms, so this shouldn't cause trouble
1164
-  NOLESS(ticks, MAX(minticks, hal_timer_t((STEP_TIMER_MIN_INTERVAL) * (HAL_TICKS_PER_US))));
1155
+  // Now 'ticks' contains the period to the next Stepper ISR - And we are
1156
+  // sure that the time has not arrived yet - Warrantied by the scheduler
1165 1157
 
1166 1158
   // Set the next ISR to fire at the proper time
1167 1159
   HAL_timer_set_compare(STEP_TIMER_NUM, ticks);
@@ -1178,54 +1170,105 @@ HAL_STEP_TIMER_ISR {
1178 1170
 hal_timer_t Stepper::isr_scheduler() {
1179 1171
   uint32_t interval;
1180 1172
 
1181
-  // Run main stepping pulse phase ISR if we have to
1182
-  if (!nextMainISR) Stepper::stepper_pulse_phase_isr();
1173
+  // Count of ticks for the next ISR
1174
+  hal_timer_t next_isr_ticks = 0;
1183 1175
 
1184
-  #if ENABLED(LIN_ADVANCE)
1185
-    // Run linear advance stepper ISR if we have to
1186
-    if (!nextAdvanceISR) nextAdvanceISR = Stepper::advance_isr();
1187
-  #endif
1176
+  // Limit the amount of iterations
1177
+  uint8_t max_loops = 10;
1178
+  
1179
+  // We need this variable here to be able to use it in the following loop
1180
+  hal_timer_t min_ticks;
1181
+  do {
1182
+    // Run main stepping pulse phase ISR if we have to
1183
+    if (!nextMainISR) Stepper::stepper_pulse_phase_isr();
1188 1184
 
1189
-  // ^== Time critical. NOTHING besides pulse generation should be above here!!!
1185
+    #if ENABLED(LIN_ADVANCE)
1186
+      // Run linear advance stepper ISR if we have to
1187
+      if (!nextAdvanceISR) nextAdvanceISR = Stepper::advance_isr();
1188
+    #endif
1190 1189
 
1191
-  // Run main stepping block processing ISR if we have to
1192
-  if (!nextMainISR) nextMainISR = Stepper::stepper_block_phase_isr();
1190
+    // ^== Time critical. NOTHING besides pulse generation should be above here!!!
1193 1191
 
1194
-  #if ENABLED(LIN_ADVANCE)
1195
-    // Select the closest interval in time
1196
-    interval = (nextAdvanceISR <= nextMainISR)
1197
-      ? nextAdvanceISR
1198
-      : nextMainISR;
1192
+    // Run main stepping block processing ISR if we have to
1193
+    if (!nextMainISR) nextMainISR = Stepper::stepper_block_phase_isr();
1199 1194
 
1200
-  #else // !ENABLED(LIN_ADVANCE)
1195
+    #if ENABLED(LIN_ADVANCE)
1196
+      // Select the closest interval in time
1197
+      interval = (nextAdvanceISR <= nextMainISR) ? nextAdvanceISR : nextMainISR;
1198
+    #else
1199
+      // The interval is just the remaining time to the stepper ISR
1200
+      interval = nextMainISR;
1201
+    #endif
1201 1202
 
1202
-    // The interval is just the remaining time to the stepper ISR
1203
-    interval = nextMainISR;
1204
-  #endif
1203
+    // Limit the value to the maximum possible value of the timer
1204
+    NOMORE(interval, HAL_TIMER_TYPE_MAX);
1205 1205
 
1206
-  // Limit the value to the maximum possible value of the timer
1207
-  if (interval > HAL_TIMER_TYPE_MAX)
1208
-    interval = HAL_TIMER_TYPE_MAX;
1206
+    // Compute the time remaining for the main isr
1207
+    nextMainISR -= interval;
1209 1208
 
1210
-  // Compute the time remaining for the main isr
1211
-  nextMainISR -= interval;
1209
+    #if ENABLED(LIN_ADVANCE)
1210
+      // Compute the time remaining for the advance isr
1211
+      if (nextAdvanceISR != ADV_NEVER) nextAdvanceISR -= interval;
1212
+    #endif
1212 1213
 
1213
-  #if ENABLED(LIN_ADVANCE)
1214
-    // Compute the time remaining for the advance isr
1215
-    if (nextAdvanceISR != ADV_NEVER)
1216
-      nextAdvanceISR -= interval;
1217
-  #endif
1214
+    /**
1215
+     * This needs to avoid a race-condition caused by interleaving
1216
+     * of interrupts required by both the LA and Stepper algorithms.
1217
+     *
1218
+     * Assume the following tick times for stepper pulses:
1219
+     *   Stepper ISR (S):  1 1000 2000 3000 4000
1220
+     *   Linear Adv. (E): 10 1010 2010 3010 4010
1221
+     *
1222
+     * The current algorithm tries to interleave them, giving:
1223
+     *  1:S 10:E 1000:S 1010:E 2000:S 2010:E 3000:S 3010:E 4000:S 4010:E
1224
+     *
1225
+     * Ideal timing would yield these delta periods:
1226
+     *  1:S  9:E  990:S   10:E  990:S   10:E  990:S   10:E  990:S   10:E
1227
+     *
1228
+     * But, since each event must fire an ISR with a minimum duration, the
1229
+     * minimum delta might be 900, so deltas under 900 get rounded up:
1230
+     *  900:S d900:E d990:S d900:E d990:S d900:E d990:S d900:E d990:S d900:E
1231
+     *
1232
+     * It works, but divides the speed of all motors by half, leading to a sudden
1233
+     * reduction to 1/2 speed! Such jumps in speed lead to lost steps (not even
1234
+     * accounting for double/quad stepping, which makes it even worse).
1235
+     */
1236
+
1237
+    // Compute the tick count for the next ISR
1238
+    next_isr_ticks += interval;
1239
+
1240
+    /**
1241
+     * Get the current tick value + margin
1242
+     * Assuming at least 6µs between calls to this ISR...
1243
+     * On AVR the ISR epilogue is estimated at 40 instructions - close to 2.5µS.
1244
+     * On ARM the ISR epilogue is estimated at 10 instructions - close to 200nS.
1245
+     * In either case leave at least 8µS for other tasks to execute - That allows
1246
+     * up to 100khz stepping rates
1247
+     */
1248
+    min_ticks = HAL_timer_get_count(STEP_TIMER_NUM) + hal_timer_t((HAL_TICKS_PER_US) * 8); // ISR never takes more than 1ms, so this shouldn't cause trouble
1218 1249
 
1219
-  return (hal_timer_t)interval;
1250
+    /**
1251
+     * NB: If for some reason the stepper monopolizes the MPU, eventually the
1252
+     * timer will wrap around (and so will 'next_isr_ticks'). So, limit the
1253
+     * loop to 10 iterations. Beyond that, there's no way to ensure correct pulse
1254
+     * timing, since the MCU isn't fast enough.
1255
+     */
1256
+    if (!--max_loops) next_isr_ticks = min_ticks;
1257
+
1258
+    // Advance pulses if not enough time to wait for the next ISR
1259
+  } while (next_isr_ticks < min_ticks);
1260
+
1261
+  // Return the count of ticks for the next ISR
1262
+  return (hal_timer_t)next_isr_ticks;
1220 1263
 }
1221 1264
 
1222
-// This part of the ISR should ONLY create the pulses for the steppers
1223
-// -- Nothing more, nothing less -- We want to avoid jitter from where
1224
-// the pulses should be generated (when the interrupt triggers) to the
1225
-// time pulses are actually created. So, PLEASE DO NOT PLACE ANY CODE
1226
-// above this line that can conditionally change that time (we are trying
1227
-// to keep the delay between the interrupt triggering and pulse generation
1228
-// as constant as possible!!!!
1265
+/**
1266
+ * This phase of the ISR should ONLY create the pulses for the steppers.
1267
+ * This prevents jitter caused by the interval between the start of the
1268
+ * interrupt and the start of the pulses. DON'T add any logic ahead of the
1269
+ * call to this method that might cause variation in the timing. The aim
1270
+ * is to keep pulse timing as regular as possible.
1271
+ */
1229 1272
 void Stepper::stepper_pulse_phase_isr() {
1230 1273
 
1231 1274
   // If we must abort the current block, do so!

Loading…
취소
저장