Prusa MINI Firmware overview
mixing.h
Go to the documentation of this file.
1 /**
2  * Marlin 3D Printer Firmware
3  * Copyright (c) 2019 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 <http://www.gnu.org/licenses/>.
20  *
21  */
22 #pragma once
23 
24 #include "../inc/MarlinConfig.h"
25 
26 //#define MIXER_NORMALIZER_DEBUG
27 
28 #ifndef __AVR__ // || DUAL_MIXING_EXTRUDER
29  // Use 16-bit (or fastest) data for the integer mix factors
30  typedef uint_fast16_t mixer_comp_t;
31  typedef uint_fast16_t mixer_accu_t;
32  #define COLOR_A_MASK 0x8000
33  #define COLOR_MASK 0x7FFF
34 #else
35  // Use 8-bit data for the integer mix factors
36  // Exactness is sacrificed for speed
37  #define MIXER_ACCU_SIGNED
38  typedef uint8_t mixer_comp_t;
39  typedef int8_t mixer_accu_t;
40  #define COLOR_A_MASK 0x80
41  #define COLOR_MASK 0x7F
42 #endif
43 
44 typedef int8_t mixer_perc_t;
45 
46 #ifndef MIXING_VIRTUAL_TOOLS
47  #define MIXING_VIRTUAL_TOOLS 1
48 #endif
49 
50 enum MixTool {
55  #if ENABLED(RETRACT_SYNC_MIXING)
56  MIXER_AUTORETRACT_TOOL,
57  #endif
59 };
60 
61 #if ENABLED(RETRACT_SYNC_MIXING)
62  #define MAX_VTOOLS 254
63 #else
64  #define MAX_VTOOLS 255
65 #endif
66 static_assert(NR_MIXING_VIRTUAL_TOOLS <= MAX_VTOOLS, "MIXING_VIRTUAL_TOOLS must be <= " STRINGIFY(MAX_VTOOLS) "!");
67 
68 #define MIXER_STEPPER_LOOP(VAR) \
69  for (uint_fast8_t VAR = 0; VAR < MIXING_STEPPERS; VAR++)
70 
71 #define MIXER_BLOCK_FIELD mixer_comp_t b_color[MIXING_STEPPERS]
72 #define MIXER_POPULATE_BLOCK() mixer.populate_block(block->b_color)
73 #define MIXER_STEPPER_SETUP() mixer.stepper_setup(current_block->b_color)
74 
75 #if ENABLED(GRADIENT_MIX)
76 
77  typedef struct {
78  bool enabled; // This gradient is enabled
79  mixer_comp_t color[MIXING_STEPPERS]; // The current gradient color
80  float start_z, end_z; // Region for gradient
81  int8_t start_vtool, end_vtool; // Start and end virtual tools
82  mixer_perc_t start_mix[MIXING_STEPPERS], // Start and end mixes from those tools
83  end_mix[MIXING_STEPPERS];
84  #if ENABLED(GRADIENT_VTOOL)
85  int8_t vtool_index; // Use this virtual tool number as index
86  #endif
87  } gradient_t;
88 
89 #endif
90 
91 /**
92  * @brief Mixer class
93  * @details Contains data and behaviors for a Mixing Extruder
94  */
95 class Mixer {
96  public:
97 
98  static float collector[MIXING_STEPPERS]; // M163 components, also editable from LCD
99 
100  static void init(); // Populate colors at boot time
101 
102  static void reset_vtools();
103  static void refresh_collector(const float proportion=1.0, const uint8_t t=selected_vtool, float (&c)[MIXING_STEPPERS]=collector);
104 
105  // Used up to Planner level
106  FORCE_INLINE static void set_collector(const uint8_t c, const float f) { collector[c] = _MAX(f, 0.0f); }
107 
108  static void normalize(const uint8_t tool_index);
109  FORCE_INLINE static void normalize() { normalize(selected_vtool); }
110 
111  FORCE_INLINE static uint8_t get_current_vtool() { return selected_vtool; }
112 
113  FORCE_INLINE static void T(const uint_fast8_t c) {
114  selected_vtool = c;
115  #if ENABLED(GRADIENT_VTOOL)
116  refresh_gradient();
117  #endif
118  #if DUAL_MIXING_EXTRUDER
119  update_mix_from_vtool();
120  #endif
121  }
122 
123  // Used when dealing with blocks
124  FORCE_INLINE static void populate_block(mixer_comp_t b_color[MIXING_STEPPERS]) {
125  #if ENABLED(GRADIENT_MIX)
126  if (gradient.enabled) {
127  MIXER_STEPPER_LOOP(i) b_color[i] = gradient.color[i];
128  return;
129  }
130  #endif
131  MIXER_STEPPER_LOOP(i) b_color[i] = color[selected_vtool][i];
132  }
133 
134  FORCE_INLINE static void stepper_setup(mixer_comp_t b_color[MIXING_STEPPERS]) {
135  MIXER_STEPPER_LOOP(i) s_color[i] = b_color[i];
136  }
137 
138  #if DUAL_MIXING_EXTRUDER || ENABLED(GRADIENT_MIX)
139 
140  static mixer_perc_t mix[MIXING_STEPPERS]; // Scratch array for the Mix in proportion to 100
141 
142  static inline void copy_mix_to_color(mixer_comp_t (&tcolor)[MIXING_STEPPERS]) {
143  // Scale each component to the largest one in terms of COLOR_A_MASK
144  // So the largest component will be COLOR_A_MASK and the other will be in proportion to it
145  const float scale = (COLOR_A_MASK) * RECIPROCAL(_MAX(
146  LIST_N(MIXING_STEPPERS, mix[0], mix[1], mix[2], mix[3], mix[4], mix[5])
147  ));
148 
149  // Scale all values so their maximum is COLOR_A_MASK
150  MIXER_STEPPER_LOOP(i) tcolor[i] = mix[i] * scale;
151 
152  #ifdef MIXER_NORMALIZER_DEBUG
153  SERIAL_ECHOPGM("Mix [ ");
154  SERIAL_ECHOLIST_N(MIXING_STEPPERS, int(mix[0]), int(mix[1]), int(mix[2]), int(mix[3]), int(mix[4]), int(mix[5]));
155  SERIAL_ECHOPGM(" ] to Color [ ");
156  SERIAL_ECHOLIST_N(MIXING_STEPPERS, int(tcolor[0]), int(tcolor[1]), int(tcolor[2]), int(tcolor[3]), int(tcolor[4]), int(tcolor[5]));
157  SERIAL_ECHOLNPGM(" ]");
158  #endif
159  }
160 
161  static inline void update_mix_from_vtool(const uint8_t j=selected_vtool) {
162  float ctot = 0;
163  MIXER_STEPPER_LOOP(i) ctot += color[j][i];
164  //MIXER_STEPPER_LOOP(i) mix[i] = 100.0f * color[j][i] / ctot;
165  MIXER_STEPPER_LOOP(i) mix[i] = mixer_perc_t(100.0f * color[j][i] / ctot);
166 
167  #ifdef MIXER_NORMALIZER_DEBUG
168  SERIAL_ECHOPAIR("V-tool ", int(j), " [ ");
169  SERIAL_ECHOLIST_N(MIXING_STEPPERS, int(color[j][0]), int(color[j][1]), int(color[j][2]), int(color[j][3]), int(color[j][4]), int(color[j][5]));
170  SERIAL_ECHOPGM(" ] to Mix [ ");
171  SERIAL_ECHOLIST_N(MIXING_STEPPERS, int(mix[0]), int(mix[1]), int(mix[2]), int(mix[3]), int(mix[4]), int(mix[5]));
172  SERIAL_ECHOLNPGM(" ]");
173  #endif
174  }
175 
176  #endif // DUAL_MIXING_EXTRUDER || GRADIENT_MIX
177 
178  #if DUAL_MIXING_EXTRUDER
179 
180  // Update the virtual tool from an edited mix
181  static inline void update_vtool_from_mix() {
182  copy_mix_to_color(color[selected_vtool]);
183  #if ENABLED(GRADIENT_MIX)
184  refresh_gradient();
185  #endif
186  // MIXER_STEPPER_LOOP(i) collector[i] = mix[i];
187  // normalize();
188  }
189 
190  #endif // DUAL_MIXING_EXTRUDER
191 
192  #if ENABLED(GRADIENT_MIX)
193 
194  static gradient_t gradient;
195  static float prev_z;
196 
197  // Update the current mix from the gradient for a given Z
198  static void update_gradient_for_z(const float z);
199  static void update_gradient_for_planner_z();
200  static inline void gradient_control(const float z) {
201  if (gradient.enabled) {
202  if (z >= gradient.end_z)
203  T(gradient.end_vtool);
204  else
205  update_gradient_for_z(z);
206  }
207  }
208 
209  static inline void update_mix_from_gradient() {
210  float ctot = 0;
211  MIXER_STEPPER_LOOP(i) ctot += gradient.color[i];
212  MIXER_STEPPER_LOOP(i) mix[i] = (mixer_perc_t)CEIL(100.0f * gradient.color[i] / ctot);
213 
214  #ifdef MIXER_NORMALIZER_DEBUG
215  SERIAL_ECHOPGM("Gradient [ ");
216  SERIAL_ECHOLIST_N(MIXING_STEPPERS, int(gradient.color[0]), int(gradient.color[1]), int(gradient.color[2]), int(gradient.color[3]), int(gradient.color[4]), int(gradient.color[5]));
217  SERIAL_ECHOPGM(" ] to Mix [ ");
218  SERIAL_ECHOLIST_N(MIXING_STEPPERS, int(mix[0]), int(mix[1]), int(mix[2]), int(mix[3]), int(mix[4]), int(mix[5]));
219  SERIAL_ECHOLNPGM(" ]");
220  #endif
221  }
222 
223  // Refresh the gradient after a change
224  static void refresh_gradient() {
225  #if ENABLED(GRADIENT_VTOOL)
226  const bool is_grd = (gradient.vtool_index == -1 || selected_vtool == (uint8_t)gradient.vtool_index);
227  #else
228  constexpr bool is_grd = true;
229  #endif
230  gradient.enabled = is_grd && gradient.start_vtool != gradient.end_vtool && gradient.start_z < gradient.end_z;
231  if (gradient.enabled) {
232  mixer_perc_t mix_bak[MIXING_STEPPERS];
233  COPY(mix_bak, mix);
234  update_mix_from_vtool(gradient.start_vtool);
235  COPY(gradient.start_mix, mix);
236  update_mix_from_vtool(gradient.end_vtool);
237  COPY(gradient.end_mix, mix);
238  update_gradient_for_planner_z();
239  COPY(mix, mix_bak);
240  prev_z = -1;
241  }
242  }
243 
244  #endif // GRADIENT_MIX
245 
246  // Used in Stepper
247  FORCE_INLINE static uint8_t get_stepper() { return runner; }
249  for (;;) {
250  if (--runner < 0) runner = MIXING_STEPPERS - 1;
251  accu[runner] += s_color[runner];
252  if (
253  #ifdef MIXER_ACCU_SIGNED
254  accu[runner] < 0
255  #else
256  accu[runner] & COLOR_A_MASK
257  #endif
258  ) {
259  accu[runner] &= COLOR_MASK;
260  return runner;
261  }
262  }
263  }
264 
265  private:
266 
267  // Used up to Planner level
268  static uint_fast8_t selected_vtool;
269  static mixer_comp_t color[NR_MIXING_VIRTUAL_TOOLS][MIXING_STEPPERS];
270 
271  // Used in Stepper
272  static int_fast8_t runner;
273  static mixer_comp_t s_color[MIXING_STEPPERS];
274  static mixer_accu_t accu[MIXING_STEPPERS];
275 };
276 
277 extern Mixer mixer;
SERIAL_CHAR
#define SERIAL_CHAR(x)
Definition: serial.h:69
MIXING_VIRTUAL_TOOLS
#define MIXING_VIRTUAL_TOOLS
Definition: mixing.h:47
NOLESS
#define NOLESS(v, n)
Definition: macros.h:127
SERIAL_ECHO
#define SERIAL_ECHO(x)
Definition: serial.h:70
FIRST_USER_VIRTUAL_TOOL
Definition: mixing.h:51
mixer
Mixer mixer
LAST_USER_VIRTUAL_TOOL
Definition: mixing.h:52
MIXER_DIRECT_SET_TOOL
Definition: mixing.h:54
MixTool
MixTool
Definition: mixing.h:50
Mixer::reset_vtools
static void reset_vtools()
RECIPROCAL
#define RECIPROCAL(x)
Definition: macros.h:273
mixer_comp_t
uint_fast16_t mixer_comp_t
Definition: mixing.h:30
_MAX
#define _MAX(V...)
Definition: macros.h:346
Planner::get_axis_position_mm
static float get_axis_position_mm(const AxisEnum axis)
Definition: planner.cpp:1526
SERIAL_ECHOPAIR
#define SERIAL_ECHOPAIR(V...)
Definition: serial.h:114
COLOR_A_MASK
#define COLOR_A_MASK
Definition: mixing.h:32
Mixer::set_collector
static FORCE_INLINE void set_collector(const uint8_t c, const float f)
Definition: mixing.h:106
i
uint8_t i
Definition: screen_test_graph.c:72
STRINGIFY
#define STRINGIFY(M)
Definition: macros.h:73
Mixer::normalize
static FORCE_INLINE void normalize()
Definition: mixing.h:109
NOMORE
#define NOMORE(v, n)
Definition: macros.h:133
ZERO
#define ZERO(a)
Definition: macros.h:201
FORCE_INLINE
#define FORCE_INLINE
Definition: macros.h:40
mixer_accu_t
uint_fast16_t mixer_accu_t
Definition: mixing.h:31
MAX_VTOOLS
#define MAX_VTOOLS
Definition: mixing.h:64
COPY
Definition: inflate.h:36
mixing.h
CEIL
#define CEIL(x)
Definition: macros.h:283
COLOR_MASK
#define COLOR_MASK
Definition: mixing.h:33
Mixer::refresh_collector
static void refresh_collector(const float proportion=1.0, const uint8_t t=selected_vtool, float(&c)[MIXING_STEPPERS]=collector)
NR_MIXING_VIRTUAL_TOOLS
Definition: mixing.h:58
SERIAL_ECHO_F
#define SERIAL_ECHO_F(V...)
Definition: serial.h:71
SERIAL_ECHOPGM
#define SERIAL_ECHOPGM(S)
Definition: serial.h:173
MIXER_STEPPER_LOOP
#define MIXER_STEPPER_LOOP(VAR)
Definition: mixing.h:68
uint8_t
const uint8_t[]
Definition: 404_html.c:3
SERIAL_ECHOLIST_N
#define SERIAL_ECHOLIST_N(N, V...)
Definition: serial.h:171
LIST_N
#define LIST_N(N, V...)
Definition: macros.h:226
Mixer::T
static FORCE_INLINE void T(const uint_fast8_t c)
Definition: mixing.h:113
Mixer::init
static void init()
Mixer::populate_block
static FORCE_INLINE void populate_block(mixer_comp_t b_color[MIXING_STEPPERS])
Definition: mixing.h:124
Z_AXIS
Definition: types.h:39
Mixer::get_next_stepper
static FORCE_INLINE uint8_t get_next_stepper()
Definition: mixing.h:248
mixer_perc_t
int8_t mixer_perc_t
Definition: mixing.h:44
Mixer::get_stepper
static FORCE_INLINE uint8_t get_stepper()
Definition: mixing.h:247
NR_USER_VIRTUAL_TOOLS
Definition: mixing.h:53
SERIAL_ECHOLNPGM
#define SERIAL_ECHOLNPGM(S)
Definition: serial.h:174
Mixer::stepper_setup
static FORCE_INLINE void stepper_setup(mixer_comp_t b_color[MIXING_STEPPERS])
Definition: mixing.h:134
Mixer
Mixer class.
Definition: mixing.h:95
Mixer::get_current_vtool
static FORCE_INLINE uint8_t get_current_vtool()
Definition: mixing.h:111
Mixer::collector
static float collector[MIXING_STEPPERS]
Definition: mixing.h:98
planner
Planner planner
Definition: planner.cpp:111