Bläddra i källkod

✨ MAX7219 idle profiler (#24375)

tombrazier 1 år sedan
förälder
incheckning
2266f1ad67
Inget konto är kopplat till bidragsgivarens mejladress
4 ändrade filer med 150 tillägg och 48 borttagningar
  1. 8
    4
      Marlin/Configuration_adv.h
  2. 4
    0
      Marlin/src/MarlinCore.cpp
  3. 68
    36
      Marlin/src/feature/max7219.cpp
  4. 70
    8
      Marlin/src/feature/max7219.h

+ 8
- 4
Marlin/Configuration_adv.h Visa fil

@@ -3595,7 +3595,7 @@
3595 3595
                                                // ESP32: If SPINDLE_LASER_PWM_PIN is onboard then <=78125Hz. For I2S expander
3596 3596
                                                //  the frequency determines the PWM resolution. 2500Hz = 0-100, 977Hz = 0-255, ...
3597 3597
                                                //  (250000 / SPINDLE_LASER_FREQUENCY) = max value.
3598
-#endif
3598
+  #endif
3599 3599
 
3600 3600
   //#define AIR_EVACUATION                     // Cutter Vacuum / Laser Blower motor control with G-codes M10-M11
3601 3601
   #if ENABLED(AIR_EVACUATION)
@@ -4289,7 +4289,8 @@
4289 4289
   #define MAX7219_NUMBER_UNITS 1   // Number of Max7219 units in chain.
4290 4290
   #define MAX7219_ROTATE       0   // Rotate the display clockwise (in multiples of +/- 90°)
4291 4291
                                    // connector at:  right=0   bottom=-90  top=90  left=180
4292
-  //#define MAX7219_REVERSE_ORDER  // The individual LED matrix units may be in reversed order
4292
+  //#define MAX7219_REVERSE_ORDER  // The order of the LED matrix units may be reversed
4293
+  //#define MAX7219_REVERSE_EACH   // The LEDs in each matrix unit row may be reversed
4293 4294
   //#define MAX7219_SIDE_BY_SIDE   // Big chip+matrix boards can be chained side-by-side
4294 4295
 
4295 4296
   /**
@@ -4297,12 +4298,15 @@
4297 4298
    * If you add more debug displays, be careful to avoid conflicts!
4298 4299
    */
4299 4300
   #define MAX7219_DEBUG_PRINTER_ALIVE    // Blink corner LED of 8x8 matrix to show that the firmware is functioning
4300
-  #define MAX7219_DEBUG_PLANNER_HEAD  3  // Show the planner queue head position on this and the next LED matrix row
4301
-  #define MAX7219_DEBUG_PLANNER_TAIL  5  // Show the planner queue tail position on this and the next LED matrix row
4301
+  #define MAX7219_DEBUG_PLANNER_HEAD  2  // Show the planner queue head position on this and the next LED matrix row
4302
+  #define MAX7219_DEBUG_PLANNER_TAIL  4  // Show the planner queue tail position on this and the next LED matrix row
4302 4303
 
4303 4304
   #define MAX7219_DEBUG_PLANNER_QUEUE 0  // Show the current planner queue depth on this and the next LED matrix row
4304 4305
                                          // If you experience stuttering, reboots, etc. this option can reveal how
4305 4306
                                          // tweaks made to the configuration are affecting the printer in real-time.
4307
+  #define MAX7219_DEBUG_PROFILE       6  // Display the fraction of CPU time spent in profiled code on this LED matrix
4308
+                                         // row. By default idle() is profiled so this shows how "idle" the processor is.
4309
+                                         // See class CodeProfiler.
4306 4310
 #endif
4307 4311
 
4308 4312
 /**

+ 4
- 0
Marlin/src/MarlinCore.cpp Visa fil

@@ -776,6 +776,10 @@ inline void manage_inactivity(const bool no_stepper_sleep=false) {
776 776
  *  - Handle Joystick jogging
777 777
  */
778 778
 void idle(bool no_stepper_sleep/*=false*/) {
779
+  #ifdef MAX7219_DEBUG_PROFILE
780
+    CodeProfiler idle_profiler;
781
+  #endif
782
+
779 783
   #if ENABLED(MARLIN_DEV_MODE)
780 784
     static uint16_t idle_depth = 0;
781 785
     if (++idle_depth > 5) SERIAL_ECHOLNPGM("idle() call depth: ", idle_depth);

+ 68
- 36
Marlin/src/feature/max7219.cpp Visa fil

@@ -62,6 +62,15 @@
62 62
   #error "MAX7219_ROTATE must be a multiple of +/- 90°."
63 63
 #endif
64 64
 
65
+#ifdef MAX7219_DEBUG_PROFILE
66
+  CodeProfiler::Mode CodeProfiler::mode = ACCUMULATE_AVERAGE;
67
+  uint8_t CodeProfiler::instance_count = 0;
68
+  uint32_t CodeProfiler::last_calc_time = micros();
69
+  uint8_t CodeProfiler::time_fraction = 0;
70
+  uint32_t CodeProfiler::total_time = 0;
71
+  uint16_t CodeProfiler::call_count = 0;
72
+#endif
73
+
65 74
 Max7219 max7219;
66 75
 
67 76
 uint8_t Max7219::led_line[MAX7219_LINES]; // = { 0 };
@@ -69,7 +78,7 @@ uint8_t Max7219::suspended; // = 0;
69 78
 
70 79
 #define LINE_REG(Q)     (max7219_reg_digit0 + ((Q) & 0x7))
71 80
 
72
-#if _ROT == 0 || _ROT == 270
81
+#if (_ROT == 0 || _ROT == 270) == DISABLED(MAX7219_REVERSE_EACH)
73 82
   #define _LED_BIT(Q)   (7 - ((Q) & 0x7))
74 83
 #else
75 84
   #define _LED_BIT(Q)   ((Q) & 0x7)
@@ -266,26 +275,27 @@ void Max7219::set(const uint8_t line, const uint8_t bits) {
266 275
 #endif // MAX7219_NUMERIC
267 276
 
268 277
 // Modify a single LED bit and send the changed line
269
-void Max7219::led_set(const uint8_t x, const uint8_t y, const bool on) {
278
+void Max7219::led_set(const uint8_t x, const uint8_t y, const bool on, uint8_t * const rcm/*=nullptr*/) {
270 279
   if (x >= MAX7219_X_LEDS || y >= MAX7219_Y_LEDS) return error(F("led_set"), x, y);
271 280
   if (BIT_7219(x, y) == on) return;
272 281
   XOR_7219(x, y);
273 282
   refresh_unit_line(LED_IND(x, y));
283
+  if (rcm != nullptr) *rcm |= _BV(LED_IND(x, y) & 0x07);
274 284
 }
275 285
 
276
-void Max7219::led_on(const uint8_t x, const uint8_t y) {
286
+void Max7219::led_on(const uint8_t x, const uint8_t y, uint8_t * const rcm/*=nullptr*/) {
277 287
   if (x >= MAX7219_X_LEDS || y >= MAX7219_Y_LEDS) return error(F("led_on"), x, y);
278
-  led_set(x, y, true);
288
+  led_set(x, y, true, rcm);
279 289
 }
280 290
 
281
-void Max7219::led_off(const uint8_t x, const uint8_t y) {
291
+void Max7219::led_off(const uint8_t x, const uint8_t y, uint8_t * const rcm/*=nullptr*/) {
282 292
   if (x >= MAX7219_X_LEDS || y >= MAX7219_Y_LEDS) return error(F("led_off"), x, y);
283
-  led_set(x, y, false);
293
+  led_set(x, y, false, rcm);
284 294
 }
285 295
 
286
-void Max7219::led_toggle(const uint8_t x, const uint8_t y) {
296
+void Max7219::led_toggle(const uint8_t x, const uint8_t y, uint8_t * const rcm/*=nullptr*/) {
287 297
   if (x >= MAX7219_X_LEDS || y >= MAX7219_Y_LEDS) return error(F("led_toggle"), x, y);
288
-  led_set(x, y, !BIT_7219(x, y));
298
+  led_set(x, y, !BIT_7219(x, y), rcm);
289 299
 }
290 300
 
291 301
 void Max7219::send_row(const uint8_t row) {
@@ -448,7 +458,7 @@ void Max7219::register_setup() {
448 458
   pulse_load();                               // Tell the chips to load the clocked out data
449 459
 }
450 460
 
451
-#ifdef MAX7219_INIT_TEST
461
+#if MAX7219_INIT_TEST
452 462
 
453 463
   uint8_t test_mode = 0;
454 464
   millis_t next_patt_ms;
@@ -536,13 +546,9 @@ void Max7219::init() {
536 546
 
537 547
   register_setup();
538 548
 
539
-  LOOP_LE_N(i, 7) {  // Empty registers to turn all LEDs off
540
-    led_line[i] = 0x00;
541
-    send(max7219_reg_digit0 + i, 0);
542
-    pulse_load();                     // Tell the chips to load the clocked out data
543
-  }
549
+  clear();
544 550
 
545
-  #ifdef MAX7219_INIT_TEST
551
+  #if MAX7219_INIT_TEST
546 552
     start_test_pattern();
547 553
   #endif
548 554
 }
@@ -554,41 +560,47 @@ void Max7219::init() {
554 560
  */
555 561
 
556 562
 // Apply changes to update a marker
557
-void Max7219::mark16(const uint8_t pos, const uint8_t v1, const uint8_t v2) {
563
+void Max7219::mark16(const uint8_t pos, const uint8_t v1, const uint8_t v2, uint8_t * const rcm/*=nullptr*/) {
558 564
   #if MAX7219_X_LEDS > 8    // At least 16 LEDs on the X-Axis. Use single line.
559
-    led_off(v1 & 0xF, pos);
560
-     led_on(v2 & 0xF, pos);
565
+    led_off(v1 & 0xF, pos, rcm);
566
+     led_on(v2 & 0xF, pos, rcm);
561 567
   #elif MAX7219_Y_LEDS > 8  // At least 16 LEDs on the Y-Axis. Use a single column.
562
-    led_off(pos, v1 & 0xF);
563
-     led_on(pos, v2 & 0xF);
568
+    led_off(pos, v1 & 0xF, rcm);
569
+     led_on(pos, v2 & 0xF, rcm);
564 570
   #else                     // Single 8x8 LED matrix. Use two lines to get 16 LEDs.
565
-    led_off(v1 & 0x7, pos + (v1 >= 8));
566
-     led_on(v2 & 0x7, pos + (v2 >= 8));
571
+    led_off(v1 & 0x7, pos + (v1 >= 8), rcm);
572
+     led_on(v2 & 0x7, pos + (v2 >= 8), rcm);
567 573
   #endif
568 574
 }
569 575
 
570 576
 // Apply changes to update a tail-to-head range
571
-void Max7219::range16(const uint8_t y, const uint8_t ot, const uint8_t nt, const uint8_t oh, const uint8_t nh) {
577
+void Max7219::range16(const uint8_t y, const uint8_t ot, const uint8_t nt, const uint8_t oh,
578
+  const uint8_t nh, uint8_t * const rcm/*=nullptr*/) {
572 579
   #if MAX7219_X_LEDS > 8    // At least 16 LEDs on the X-Axis. Use single line.
573 580
     if (ot != nt) for (uint8_t n = ot & 0xF; n != (nt & 0xF) && n != (nh & 0xF); n = (n + 1) & 0xF)
574
-      led_off(n & 0xF, y);
581
+      led_off(n & 0xF, y, rcm);
575 582
     if (oh != nh) for (uint8_t n = (oh + 1) & 0xF; n != ((nh + 1) & 0xF); n = (n + 1) & 0xF)
576
-       led_on(n & 0xF, y);
583
+       led_on(n & 0xF, y, rcm);
577 584
   #elif MAX7219_Y_LEDS > 8  // At least 16 LEDs on the Y-Axis. Use a single column.
578 585
     if (ot != nt) for (uint8_t n = ot & 0xF; n != (nt & 0xF) && n != (nh & 0xF); n = (n + 1) & 0xF)
579
-      led_off(y, n & 0xF);
586
+      led_off(y, n & 0xF, rcm);
580 587
     if (oh != nh) for (uint8_t n = (oh + 1) & 0xF; n != ((nh + 1) & 0xF); n = (n + 1) & 0xF)
581
-       led_on(y, n & 0xF);
588
+       led_on(y, n & 0xF, rcm);
582 589
   #else                     // Single 8x8 LED matrix. Use two lines to get 16 LEDs.
583 590
     if (ot != nt) for (uint8_t n = ot & 0xF; n != (nt & 0xF) && n != (nh & 0xF); n = (n + 1) & 0xF)
584
-      led_off(n & 0x7, y + (n >= 8));
591
+      led_off(n & 0x7, y + (n >= 8), rcm);
585 592
     if (oh != nh) for (uint8_t n = (oh + 1) & 0xF; n != ((nh + 1) & 0xF); n = (n + 1) & 0xF)
586
-       led_on(n & 0x7, y + (n >= 8));
593
+       led_on(n & 0x7, y + (n >= 8), rcm);
587 594
   #endif
588 595
 }
589 596
 
590 597
 // Apply changes to update a quantity
591
-void Max7219::quantity16(const uint8_t pos, const uint8_t ov, const uint8_t nv) {
598
+void Max7219::quantity(const uint8_t pos, const uint8_t ov, const uint8_t nv, uint8_t * const rcm/*=nullptr*/) {
599
+  for (uint8_t i = _MIN(nv, ov); i < _MAX(nv, ov); i++)
600
+    led_set(i, pos, nv >= ov, rcm);
601
+}
602
+
603
+void Max7219::quantity16(const uint8_t pos, const uint8_t ov, const uint8_t nv, uint8_t * const rcm/*=nullptr*/) {
592 604
   for (uint8_t i = _MIN(nv, ov); i < _MAX(nv, ov); i++)
593 605
     led_set(
594 606
       #if MAX7219_X_LEDS > 8    // At least 16 LEDs on the X-Axis. Use single line.
@@ -599,6 +611,7 @@ void Max7219::quantity16(const uint8_t pos, const uint8_t ov, const uint8_t nv)
599 611
         i >> 1, pos + (i & 1)
600 612
       #endif
601 613
       , nv >= ov
614
+      , rcm
602 615
     );
603 616
 }
604 617
 
@@ -636,16 +649,20 @@ void Max7219::idle_tasks() {
636 649
     register_setup();
637 650
   }
638 651
 
639
-  #ifdef MAX7219_INIT_TEST
652
+  #if MAX7219_INIT_TEST
640 653
     if (test_mode) {
641 654
       run_test_pattern();
642 655
       return;
643 656
     }
644 657
   #endif
645 658
 
659
+  // suspend updates and record which lines have changed for batching later
660
+  suspended++;
661
+  uint8_t row_change_mask = 0x00;
662
+
646 663
   #if ENABLED(MAX7219_DEBUG_PRINTER_ALIVE)
647 664
     if (do_blink) {
648
-      led_toggle(MAX7219_X_LEDS - 1, MAX7219_Y_LEDS - 1);
665
+      led_toggle(MAX7219_X_LEDS - 1, MAX7219_Y_LEDS - 1, &row_change_mask);
649 666
       next_blink = ms + 1000;
650 667
     }
651 668
   #endif
@@ -655,7 +672,7 @@ void Max7219::idle_tasks() {
655 672
     static int16_t last_head_cnt = 0xF, last_tail_cnt = 0xF;
656 673
 
657 674
     if (last_head_cnt != head || last_tail_cnt != tail) {
658
-      range16(MAX7219_DEBUG_PLANNER_HEAD, last_tail_cnt, tail, last_head_cnt, head);
675
+      range16(MAX7219_DEBUG_PLANNER_HEAD, last_tail_cnt, tail, last_head_cnt, head, &row_change_mask);
659 676
       last_head_cnt = head;
660 677
       last_tail_cnt = tail;
661 678
     }
@@ -665,7 +682,7 @@ void Max7219::idle_tasks() {
665 682
     #ifdef MAX7219_DEBUG_PLANNER_HEAD
666 683
       static int16_t last_head_cnt = 0x1;
667 684
       if (last_head_cnt != head) {
668
-        mark16(MAX7219_DEBUG_PLANNER_HEAD, last_head_cnt, head);
685
+        mark16(MAX7219_DEBUG_PLANNER_HEAD, last_head_cnt, head, &row_change_mask);
669 686
         last_head_cnt = head;
670 687
       }
671 688
     #endif
@@ -673,7 +690,7 @@ void Max7219::idle_tasks() {
673 690
     #ifdef MAX7219_DEBUG_PLANNER_TAIL
674 691
       static int16_t last_tail_cnt = 0x1;
675 692
       if (last_tail_cnt != tail) {
676
-        mark16(MAX7219_DEBUG_PLANNER_TAIL, last_tail_cnt, tail);
693
+        mark16(MAX7219_DEBUG_PLANNER_TAIL, last_tail_cnt, tail, &row_change_mask);
677 694
         last_tail_cnt = tail;
678 695
       }
679 696
     #endif
@@ -684,11 +701,26 @@ void Max7219::idle_tasks() {
684 701
     static int16_t last_depth = 0;
685 702
     const int16_t current_depth = (head - tail + BLOCK_BUFFER_SIZE) & (BLOCK_BUFFER_SIZE - 1) & 0xF;
686 703
     if (current_depth != last_depth) {
687
-      quantity16(MAX7219_DEBUG_PLANNER_QUEUE, last_depth, current_depth);
704
+      quantity16(MAX7219_DEBUG_PLANNER_QUEUE, last_depth, current_depth, &row_change_mask);
688 705
       last_depth = current_depth;
689 706
     }
690 707
   #endif
691 708
 
709
+  #ifdef MAX7219_DEBUG_PROFILE
710
+    static uint8_t last_time_fraction = 0;
711
+    const uint8_t current_time_fraction = (uint16_t(CodeProfiler::get_time_fraction()) * MAX7219_NUMBER_UNITS + 8) / 16;
712
+    if (current_time_fraction != last_time_fraction) {
713
+      quantity(MAX7219_DEBUG_PROFILE, last_time_fraction, current_time_fraction, &row_change_mask);
714
+      last_time_fraction = current_time_fraction;
715
+    }
716
+  #endif
717
+
718
+  // batch line updates
719
+  suspended--;
720
+  if (!suspended)
721
+    LOOP_L_N(i, 8) if (row_change_mask & _BV(i))
722
+      refresh_line(i);
723
+
692 724
   // After resume() automatically do a refresh()
693 725
   if (suspended == 0x80) {
694 726
     suspended = 0;

+ 70
- 8
Marlin/src/feature/max7219.h Visa fil

@@ -73,6 +73,67 @@
73 73
 #define max7219_reg_shutdown    0x0C
74 74
 #define max7219_reg_displayTest 0x0F
75 75
 
76
+#ifdef MAX7219_DEBUG_PROFILE
77
+  // This class sums up the amount of time for which its instances exist.
78
+  // By default there is one instantiated for the duration of the idle()
79
+  // function. But an instance can be created in any code block to measure
80
+  // the time spent from the point of instantiation until the CPU leaves
81
+  // block. Be careful about having multiple instances of CodeProfiler as
82
+  // it does not guard against double counting. In general mixing ISR and
83
+  // non-ISR use will require critical sections but note that mode setting
84
+  // is atomic so the total or average times can safely be read if you set
85
+  // mode to FREEZE first.
86
+  class CodeProfiler {
87
+  public:
88
+    enum Mode : uint8_t { ACCUMULATE_AVERAGE, ACCUMULATE_TOTAL, FREEZE };
89
+
90
+  private:
91
+    static Mode mode;
92
+    static uint8_t instance_count;
93
+    static uint32_t last_calc_time;
94
+    static uint32_t total_time;
95
+    static uint8_t time_fraction;
96
+    static uint16_t call_count;
97
+
98
+    uint32_t start_time;
99
+
100
+  public:
101
+    CodeProfiler() : start_time(micros()) { instance_count++; }
102
+    ~CodeProfiler() {
103
+      instance_count--;
104
+      if (mode == FREEZE) return;
105
+
106
+      call_count++;
107
+
108
+      const uint32_t now = micros();
109
+      total_time += now - start_time;
110
+
111
+      if (mode == ACCUMULATE_TOTAL) return;
112
+
113
+      // update time_fraction every hundred milliseconds
114
+      if (instance_count == 0 && ELAPSED(now, last_calc_time + 100000)) {
115
+        time_fraction = total_time * 128 / (now - last_calc_time);
116
+        last_calc_time = now;
117
+        total_time = 0;
118
+      }
119
+    }
120
+
121
+    static void set_mode(Mode _mode) { mode = _mode; }
122
+    static void reset() {
123
+      time_fraction = 0;
124
+      last_calc_time = micros();
125
+      total_time = 0;
126
+      call_count = 0;
127
+    }
128
+    // returns fraction of total time which was measured, scaled from 0 to 128
129
+    static uint8_t get_time_fraction() { return time_fraction; }
130
+    // returns total time in microseconds
131
+    static uint32_t get_total_time() { return total_time; }
132
+
133
+    static uint16_t get_call_count() { return call_count; }
134
+  };
135
+#endif
136
+
76 137
 class Max7219 {
77 138
 public:
78 139
   static uint8_t led_line[MAX7219_LINES];
@@ -110,10 +171,10 @@ public:
110 171
   #endif
111 172
 
112 173
   // Set a single LED by XY coordinate
113
-  static void led_set(const uint8_t x, const uint8_t y, const bool on);
114
-  static void led_on(const uint8_t x, const uint8_t y);
115
-  static void led_off(const uint8_t x, const uint8_t y);
116
-  static void led_toggle(const uint8_t x, const uint8_t y);
174
+  static void led_set(const uint8_t x, const uint8_t y, const bool on, uint8_t * const rcm=nullptr);
175
+  static void led_on(const uint8_t x, const uint8_t y, uint8_t * const rcm=nullptr);
176
+  static void led_off(const uint8_t x, const uint8_t y, uint8_t * const rcm=nullptr);
177
+  static void led_toggle(const uint8_t x, const uint8_t y, uint8_t * const rcm=nullptr);
117 178
 
118 179
   // Set all LEDs in a single column
119 180
   static void set_column(const uint8_t col, const uint32_t val);
@@ -147,11 +208,12 @@ private:
147 208
   static void set(const uint8_t line, const uint8_t bits);
148 209
   static void send_row(const uint8_t row);
149 210
   static void send_column(const uint8_t col);
150
-  static void mark16(const uint8_t y, const uint8_t v1, const uint8_t v2);
151
-  static void range16(const uint8_t y, const uint8_t ot, const uint8_t nt, const uint8_t oh, const uint8_t nh);
152
-  static void quantity16(const uint8_t y, const uint8_t ov, const uint8_t nv);
211
+  static void mark16(const uint8_t y, const uint8_t v1, const uint8_t v2, uint8_t * const rcm=nullptr);
212
+  static void range16(const uint8_t y, const uint8_t ot, const uint8_t nt, const uint8_t oh, const uint8_t nh, uint8_t * const rcm=nullptr);
213
+  static void quantity(const uint8_t y, const uint8_t ov, const uint8_t nv, uint8_t * const rcm=nullptr);
214
+  static void quantity16(const uint8_t y, const uint8_t ov, const uint8_t nv, uint8_t * const rcm=nullptr);
153 215
 
154
-  #ifdef MAX7219_INIT_TEST
216
+  #if MAX7219_INIT_TEST
155 217
     static void test_pattern();
156 218
     static void run_test_pattern();
157 219
     static void start_test_pattern();

Laddar…
Avbryt
Spara