My Marlin configs for Fabrikator Mini and CTC i3 Pro B
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Delay.cpp 7.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. /**
  2. * Marlin 3D Printer Firmware
  3. * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
  4. *
  5. * Based on Sprinter and grbl.
  6. * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
  7. *
  8. * This program is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. *
  21. */
  22. #include "Delay.h"
  23. #include "../../inc/MarlinConfig.h"
  24. #if defined(__arm__) || defined(__thumb__)
  25. static uint32_t ASM_CYCLES_PER_ITERATION = 4; // Initial bet which will be adjusted in calibrate_delay_loop
  26. // Simple assembler loop counting down
  27. void delay_asm(uint32_t cy) {
  28. cy = _MAX(cy / ASM_CYCLES_PER_ITERATION, 1U); // Zero is forbidden here
  29. __asm__ __volatile__(
  30. A(".syntax unified") // is to prevent CM0,CM1 non-unified syntax
  31. L("1")
  32. A("subs %[cnt],#1")
  33. A("bne 1b")
  34. : [cnt]"+r"(cy) // output: +r means input+output
  35. : // input:
  36. : "cc" // clobbers:
  37. );
  38. }
  39. // We can't use CMSIS since it's not available on all platform, so fallback to hardcoded register values
  40. #define HW_REG(X) *(volatile uint32_t *)(X)
  41. #define _DWT_CTRL 0xE0001000
  42. #define _DWT_CYCCNT 0xE0001004 // CYCCNT is 32bits, takes 37s or so to wrap.
  43. #define _DEM_CR 0xE000EDFC
  44. #define _LAR 0xE0001FB0
  45. // Use hardware cycle counter instead, it's much safer
  46. void delay_dwt(uint32_t count) {
  47. // Reuse the ASM_CYCLES_PER_ITERATION variable to avoid wasting another useless variable
  48. uint32_t start = HW_REG(_DWT_CYCCNT) - ASM_CYCLES_PER_ITERATION, elapsed;
  49. do {
  50. elapsed = HW_REG(_DWT_CYCCNT) - start;
  51. } while (elapsed < count);
  52. }
  53. // Pointer to asm function, calling the functions has a ~20 cycles overhead
  54. DelayImpl DelayCycleFnc = delay_asm;
  55. void calibrate_delay_loop() {
  56. // Check if we have a working DWT implementation in the CPU (see https://developer.arm.com/documentation/ddi0439/b/Data-Watchpoint-and-Trace-Unit/DWT-Programmers-Model)
  57. if (!HW_REG(_DWT_CTRL)) {
  58. // No DWT present, so fallback to plain old ASM nop counting
  59. // Unfortunately, we don't exactly know how many iteration it'll take to decrement a counter in a loop
  60. // It depends on the CPU architecture, the code current position (flash vs SRAM)
  61. // So, instead of wild guessing and making mistake, instead
  62. // compute it once for all
  63. ASM_CYCLES_PER_ITERATION = 1;
  64. // We need to fetch some reference clock before waiting
  65. cli();
  66. uint32_t start = micros();
  67. delay_asm(1000); // On a typical CPU running in MHz, waiting 1000 "unknown cycles" means it'll take between 1ms to 6ms, that's perfectly acceptable
  68. uint32_t end = micros();
  69. sei();
  70. uint32_t expectedCycles = (end - start) * ((F_CPU) / 1000000UL); // Convert microseconds to cycles
  71. // Finally compute the right scale
  72. ASM_CYCLES_PER_ITERATION = (uint32_t)(expectedCycles / 1000);
  73. // No DWT present, likely a Cortex M0 so NOP counting is our best bet here
  74. DelayCycleFnc = delay_asm;
  75. }
  76. else {
  77. // Enable DWT counter
  78. // From https://stackoverflow.com/a/41188674/1469714
  79. HW_REG(_DEM_CR) = HW_REG(_DEM_CR) | 0x01000000; // Enable trace
  80. #if __CORTEX_M == 7
  81. HW_REG(_LAR) = 0xC5ACCE55; // Unlock access to DWT registers, see https://developer.arm.com/documentation/ihi0029/e/ section B2.3.10
  82. #endif
  83. HW_REG(_DWT_CYCCNT) = 0; // Clear DWT cycle counter
  84. HW_REG(_DWT_CTRL) = HW_REG(_DWT_CTRL) | 1; // Enable DWT cycle counter
  85. // Then calibrate the constant offset from the counter
  86. ASM_CYCLES_PER_ITERATION = 0;
  87. uint32_t s = HW_REG(_DWT_CYCCNT);
  88. uint32_t e = HW_REG(_DWT_CYCCNT); // (e - s) contains the number of cycle required to read the cycle counter
  89. delay_dwt(0);
  90. uint32_t f = HW_REG(_DWT_CYCCNT); // (f - e) contains the delay to call the delay function + the time to read the cycle counter
  91. ASM_CYCLES_PER_ITERATION = (f - e) - (e - s);
  92. // Use safer DWT function
  93. DelayCycleFnc = delay_dwt;
  94. }
  95. }
  96. #if ENABLED(MARLIN_DEV_MODE)
  97. void dump_delay_accuracy_check() {
  98. auto report_call_time = [](PGM_P const name, PGM_P const unit, const uint32_t cycles, const uint32_t total, const bool do_flush=true) {
  99. SERIAL_ECHOPGM("Calling ");
  100. SERIAL_ECHOPGM_P(name);
  101. SERIAL_ECHOLNPGM(" for ", cycles);
  102. SERIAL_ECHOPGM_P(unit);
  103. SERIAL_ECHOLNPGM(" took: ", total);
  104. SERIAL_ECHOPGM_P(unit);
  105. if (do_flush) SERIAL_FLUSHTX();
  106. };
  107. uint32_t s, e;
  108. SERIAL_ECHOLNPGM("Computed delay calibration value: ", ASM_CYCLES_PER_ITERATION);
  109. SERIAL_FLUSH();
  110. // Display the results of the calibration above
  111. constexpr uint32_t testValues[] = { 1, 5, 10, 20, 50, 100, 150, 200, 350, 500, 750, 1000 };
  112. for (auto i : testValues) {
  113. s = micros(); DELAY_US(i); e = micros();
  114. report_call_time(PSTR("delay"), PSTR("us"), i, e - s);
  115. }
  116. if (HW_REG(_DWT_CTRL)) {
  117. for (auto i : testValues) {
  118. s = HW_REG(_DWT_CYCCNT); DELAY_CYCLES(i); e = HW_REG(_DWT_CYCCNT);
  119. report_call_time(PSTR("runtime delay"), PSTR("cycles"), i, e - s);
  120. }
  121. // Measure the delay to call a real function compared to a function pointer
  122. s = HW_REG(_DWT_CYCCNT); delay_dwt(1); e = HW_REG(_DWT_CYCCNT);
  123. report_call_time(PSTR("delay_dwt"), PSTR("cycles"), 1, e - s);
  124. static PGMSTR(dcd, "DELAY_CYCLES directly ");
  125. s = HW_REG(_DWT_CYCCNT); DELAY_CYCLES( 1); e = HW_REG(_DWT_CYCCNT);
  126. report_call_time(dcd, PSTR("cycles"), 1, e - s, false);
  127. s = HW_REG(_DWT_CYCCNT); DELAY_CYCLES( 5); e = HW_REG(_DWT_CYCCNT);
  128. report_call_time(dcd, PSTR("cycles"), 5, e - s, false);
  129. s = HW_REG(_DWT_CYCCNT); DELAY_CYCLES(10); e = HW_REG(_DWT_CYCCNT);
  130. report_call_time(dcd, PSTR("cycles"), 10, e - s, false);
  131. s = HW_REG(_DWT_CYCCNT); DELAY_CYCLES(20); e = HW_REG(_DWT_CYCCNT);
  132. report_call_time(dcd, PSTR("cycles"), 20, e - s, false);
  133. s = HW_REG(_DWT_CYCCNT); DELAY_CYCLES(50); e = HW_REG(_DWT_CYCCNT);
  134. report_call_time(dcd, PSTR("cycles"), 50, e - s, false);
  135. s = HW_REG(_DWT_CYCCNT); DELAY_CYCLES(100); e = HW_REG(_DWT_CYCCNT);
  136. report_call_time(dcd, PSTR("cycles"), 100, e - s, false);
  137. s = HW_REG(_DWT_CYCCNT); DELAY_CYCLES(200); e = HW_REG(_DWT_CYCCNT);
  138. report_call_time(dcd, PSTR("cycles"), 200, e - s, false);
  139. }
  140. }
  141. #endif // MARLIN_DEV_MODE
  142. #else
  143. void calibrate_delay_loop() {}
  144. #if ENABLED(MARLIN_DEV_MODE)
  145. void dump_delay_accuracy_check() { SERIAL_ECHOPGM_P(PSTR("N/A on this platform")); }
  146. #endif
  147. #endif