Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
M
MarlinKimbra
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
machinery
MarlinKimbra
Commits
cf380d0d
Commit
cf380d0d
authored
May 08, 2015
by
MagoKimbra
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'origin/master' into Development
parents
a985527c
e9d95297
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
83 additions
and
117 deletions
+83
-117
Configuration.h
MarlinKimbra/Configuration.h
+24
-31
Configuration_adv.h
MarlinKimbra/Configuration_adv.h
+1
-1
Marlin_main.cpp
MarlinKimbra/Marlin_main.cpp
+3
-4
comunication.h
MarlinKimbra/comunication.h
+2
-1
stepper.cpp
MarlinKimbra/stepper.cpp
+23
-51
temperature.cpp
MarlinKimbra/temperature.cpp
+30
-29
No files found.
MarlinKimbra/Configuration.h
View file @
cf380d0d
...
@@ -280,44 +280,37 @@
...
@@ -280,44 +280,37 @@
//===========================================================================
//===========================================================================
//========================
===== Thermal Runaway Protection
==================
//========================
Thermal Runaway Protection =====
==================
//===========================================================================
//===========================================================================
/*
This is a feature to protect your printer from burn up in flames if it has
a thermistor coming off place (this happened to a friend of mine recently and
motivated me writing this feature).
The issue: If a thermistor come off, it will read a lower temperature than actual.
/**
The system will turn the heater on forever, burning up the filament and anything
* Thermal Runaway Protection protects your printer from damage and fire if a
else around.
* thermistor falls out or temperature sensors fail in any way.
*
After the temperature reaches the target for the first time, this feature will
* The issue: If a thermistor falls out or a temperature sensor fails,
start measuring for how long the current temperature stays below the target
* Marlin can no longer sense the actual temperature. Since a disconnected
minus _HYSTERESIS (set_temperature - THERMAL_RUNAWAY_PROTECTION_HYSTERESIS).
* thermistor reads as a low temperature, the firmware will keep the heater on.
*
If it stays longer than _PERIOD, it means the thermistor temperature
* The solution: Once the temperature reaches the target, start observing.
cannot catch up with the target, so something *may be* wrong. Then, to be on the
* If the temperature stays too far below the target (hysteresis) for too long,
safe side, the system will he halt.
* the firmware will halt as a safety precaution.
*
Bear in mind the count down will just start AFTER the first time the
* Note that because the countdown starts only AFTER the temperature reaches
thermistor temperature is over the target, so you will have no problem if
* the target, this will not catch a thermistor that is already disconnected
your extruder heater takes 2 minutes to hit the target on heating.
* when the print starts!
*
*/
* To enable for all extruder heaters, uncomment the two defines below:
// If you want to enable this feature for all your extruder heaters,
*/
// uncomment the 2 defines below:
// Parameters for all extruder heaters
// Parameters for all extruder heaters
//#define THERMAL_RUNAWAY_PROTECTION_PERIOD 40 //
in seconds
#define THERMAL_RUNAWAY_PROTECTION_PERIOD 40 //
in seconds
//
#define THERMAL_RUNAWAY_PROTECTION_HYSTERESIS 4 // in degree Celsius
#define THERMAL_RUNAWAY_PROTECTION_HYSTERESIS 4 // in degree Celsius
// If you want to enable this feature for your bed heater,
// To enable for the bed heater, uncomment the two defines below:
// uncomment the 2 defines below:
// Parameters for the bed heater
// Parameters for the bed heater
//#define THERMAL_RUNAWAY_PROTECTION_BED_PERIOD 20 //in seconds
#define THERMAL_RUNAWAY_PROTECTION_BED_PERIOD 20 // in seconds
//#define THERMAL_RUNAWAY_PROTECTION_BED_HYSTERESIS 2 // in degree Celsius
#define THERMAL_RUNAWAY_PROTECTION_BED_HYSTERESIS 2 // in degree Celsius
//===========================================================================
//===========================================================================
...
...
MarlinKimbra/Configuration_adv.h
View file @
cf380d0d
...
@@ -261,7 +261,7 @@
...
@@ -261,7 +261,7 @@
//#define ENCODER_RATE_MULTIPLIER_DEBUG // If defined, output the encoder steps per second value
//#define ENCODER_RATE_MULTIPLIER_DEBUG // If defined, output the encoder steps per second value
//#define CHDK 4 //Pin for triggering CHDK to take a picture see how to use it here http://captain-slow.dk/2014/03/09/3d-printing-timelapses/
//#define CHDK 4 //Pin for triggering CHDK to take a picture see how to use it here http://captain-slow.dk/2014/03/09/3d-printing-timelapses/
#define CHDK_DELAY 50 //How long in ms the pin should stay HIGH before going LOW again
#define CHDK_DELAY 50
//How long in ms the pin should stay HIGH before going LOW again
#ifdef SDSUPPORT
#ifdef SDSUPPORT
...
...
MarlinKimbra/Marlin_main.cpp
View file @
cf380d0d
...
@@ -4134,11 +4134,11 @@ inline void gcode_M105() {
...
@@ -4134,11 +4134,11 @@ inline void gcode_M105() {
#if HAS_TEMP_0 || HAS_TEMP_BED || defined(HEATER_0_USES_MAX6675)
#if HAS_TEMP_0 || HAS_TEMP_BED || defined(HEATER_0_USES_MAX6675)
ECHO_S
(
OK
);
ECHO_S
(
OK
);
#if HAS_TEMP_0
#if HAS_TEMP_0
ECHO_MV
(
" T:"
,
degHotend
(
target_extruder
),
1
);
ECHO_MV
(
MSG_T
,
degHotend
(
target_extruder
),
1
);
ECHO_MV
(
" /"
,
degTargetHotend
(
target_extruder
),
1
);
ECHO_MV
(
" /"
,
degTargetHotend
(
target_extruder
),
1
);
#endif
#endif
#if HAS_TEMP_BED
#if HAS_TEMP_BED
ECHO_MV
(
" B:"
,
degBed
(),
1
);
ECHO_MV
(
MSG_B
,
degBed
(),
1
);
ECHO_MV
(
" /"
,
degTargetBed
(),
1
);
ECHO_MV
(
" /"
,
degTargetBed
(),
1
);
#endif
#endif
for
(
int8_t
e
=
0
;
e
<
EXTRUDERS
;
++
e
)
{
for
(
int8_t
e
=
0
;
e
<
EXTRUDERS
;
++
e
)
{
...
@@ -6266,8 +6266,7 @@ void ClearToSend() {
...
@@ -6266,8 +6266,7 @@ void ClearToSend() {
#ifdef SDSUPPORT
#ifdef SDSUPPORT
if
(
fromsd
[
cmd_queue_index_r
])
return
;
if
(
fromsd
[
cmd_queue_index_r
])
return
;
#endif
#endif
ECHO_S
(
OK
);
ECHO_L
(
OK
);
ECHO_E
;
#ifdef ADVANCED_OK
#ifdef ADVANCED_OK
ECHO_MV
(
" N"
,
gcode_LastN
);
ECHO_MV
(
" N"
,
gcode_LastN
);
ECHO_EMV
(
" S"
,
commands_in_queue
);
ECHO_EMV
(
" S"
,
commands_in_queue
);
...
...
MarlinKimbra/comunication.h
View file @
cf380d0d
...
@@ -67,18 +67,19 @@ FORCE_INLINE void PS_PGM(const char *str) {
...
@@ -67,18 +67,19 @@ FORCE_INLINE void PS_PGM(const char *str) {
#define ECHO_V SERIAL_PRINT
#define ECHO_V SERIAL_PRINT
#define ECHO_C SERIAL_WRITE
#define ECHO_C SERIAL_WRITE
#define ECHO_S(srt) ECHO_PGM(srt)
#define ECHO_S(srt) ECHO_PGM(srt)
#define ECHO_E ECHO_ENDL
#define ECHO_SM(srt, msg) ECHO_S(srt),ECHO_M(msg)
#define ECHO_SM(srt, msg) ECHO_S(srt),ECHO_M(msg)
#define ECHO_SV(srt, val, args...) ECHO_S(srt),ECHO_V(val, ##args)
#define ECHO_SV(srt, val, args...) ECHO_S(srt),ECHO_V(val, ##args)
#define ECHO_SMV(srt, msg, val, args...) ECHO_S(srt),ECHO_MV(msg, val, ##args)
#define ECHO_SMV(srt, msg, val, args...) ECHO_S(srt),ECHO_MV(msg, val, ##args)
#define ECHO_SVM(srt, val, msg, args...) ECHO_S(srt),ECHO_VM(val, msg, ##args)
#define ECHO_SVM(srt, val, msg, args...) ECHO_S(srt),ECHO_VM(val, msg, ##args)
#define ECHO_E ECHO_ENDL
#define ECHO_EM(msg) ECHO_M(msg),ECHO_E
#define ECHO_EM(msg) ECHO_M(msg),ECHO_E
#define ECHO_EV(val, args...) ECHO_V(val, ##args),ECHO_E
#define ECHO_EV(val, args...) ECHO_V(val, ##args),ECHO_E
#define ECHO_EMV(msg, val, args...) ECHO_MV(msg, val, ##args),ECHO_E
#define ECHO_EMV(msg, val, args...) ECHO_MV(msg, val, ##args),ECHO_E
#define ECHO_EVM(val, msg, args...) ECHO_VM(val, msg, ##args),ECHO_E
#define ECHO_EVM(val, msg, args...) ECHO_VM(val, msg, ##args),ECHO_E
#define ECHO_L(srt) ECHO_S(srt),ECHO_E
#define ECHO_LM(srt, msg) ECHO_S(srt),ECHO_M(msg),ECHO_E
#define ECHO_LM(srt, msg) ECHO_S(srt),ECHO_M(msg),ECHO_E
#define ECHO_LV(srt, val) ECHO_S(srt),ECHO_V(val),ECHO_E
#define ECHO_LV(srt, val) ECHO_S(srt),ECHO_V(val),ECHO_E
#define ECHO_LMV(srt, msg, val, args...) ECHO_S(srt),ECHO_MV(msg, val, ##args),ECHO_E
#define ECHO_LMV(srt, msg, val, args...) ECHO_S(srt),ECHO_MV(msg, val, ##args),ECHO_E
...
...
MarlinKimbra/stepper.cpp
View file @
cf380d0d
...
@@ -539,7 +539,7 @@ ISR(TIMER1_COMPA_vect) {
...
@@ -539,7 +539,7 @@ ISR(TIMER1_COMPA_vect) {
{
// -direction
{
// -direction
#ifdef DUAL_X_CARRIAGE
#ifdef DUAL_X_CARRIAGE
// with 2 x-carriages, endstops are only checked in the homing direction for the active extruder
// with 2 x-carriages, endstops are only checked in the homing direction for the active extruder
if
((
current_block
->
active_
extruder
==
0
&&
X_HOME_DIR
==
-
1
)
||
(
current_block
->
active_extrud
er
!=
0
&&
X2_HOME_DIR
==
-
1
))
if
((
current_block
->
active_
driver
==
0
&&
X_HOME_DIR
==
-
1
)
||
(
current_block
->
active_driv
er
!=
0
&&
X2_HOME_DIR
==
-
1
))
#endif
#endif
{
{
#if HAS_X_MIN
#if HAS_X_MIN
...
@@ -723,62 +723,33 @@ ISR(TIMER1_COMPA_vect) {
...
@@ -723,62 +723,33 @@ ISR(TIMER1_COMPA_vect) {
#endif //ADVANCE
#endif //ADVANCE
#define _COUNTER(axis) counter_## axis
#define _COUNTER(axis) counter_## axis
#define _WRITE_STEP(AXIS, HIGHLOW) AXIS ##_STEP_WRITE(HIGHLOW)
#define _APPLY_STEP(AXIS) AXIS ##_APPLY_STEP
#define _APPLY_STEP(AXIS) AXIS ##_APPLY_STEP
#define _INVERT_STEP_PIN(AXIS) INVERT_## AXIS ##_STEP_PIN
#define _INVERT_STEP_PIN(AXIS) INVERT_## AXIS ##_STEP_PIN
#if defined(CONFIG_STEPPERS_TOSHIBA) || MB(ALLIGATOR)
#define STEP_START(axis, AXIS) \
/**
_COUNTER(axis) += current_block->steps[_AXIS(AXIS)]; \
* The Toshiba stepper controller require much longer pulses.
if (_COUNTER(axis) > 0) { \
* So we 'stage' decompose the pulses between high and low
_APPLY_STEP(AXIS)(!_INVERT_STEP_PIN(AXIS),0); \
* instead of doing each in turn. The extra tests add enough
_COUNTER(axis) -= current_block->step_event_count; \
* lag to allow it work with without needing NOPs
count_position[_AXIS(AXIS)] += count_direction[_AXIS(AXIS)]; }
*/
#define STEP_ADD(axis, AXIS) \
STEP_START
(
x
,
X
);
_COUNTER(axis) += current_block->steps[_AXIS(AXIS)]; \
STEP_START
(
y
,
Y
);
if (_COUNTER(axis) > 0) { _WRITE_STEP(AXIS, HIGH); }
STEP_START
(
z
,
Z
);
STEP_ADD
(
x
,
X
);
#ifndef ADVANCE
STEP_ADD
(
y
,
Y
);
STEP_START
(
e
,
E
);
STEP_ADD
(
z
,
Z
);
#endif
#ifndef ADVANCE
STEP_ADD
(
e
,
E
);
#endif
_delay_us
(
1U
);
// Add delay us
#define STEP_IF_COUNTER(axis, AXIS) \
if (_COUNTER(axis) > 0) { \
_COUNTER(axis) -= current_block->step_event_count; \
count_position[_AXIS(AXIS)] += count_direction[_AXIS(AXIS)]; \
_WRITE_STEP(AXIS, LOW); \
}
STEP_IF_COUNTER
(
x
,
X
);
STEP_IF_COUNTER
(
y
,
Y
);
STEP_IF_COUNTER
(
z
,
Z
);
#ifndef ADVANCE
STEP_IF_COUNTER
(
e
,
E
);
#endif
#else // !CONFIG_STEPPERS_TOSHIBA || MB(ALLIGATOR)
delayMicroseconds
(
1
);
#define APPLY_MOVEMENT(axis, AXIS) \
#define STEP_END(axis, AXIS) _APPLY_STEP(AXIS)(_INVERT_STEP_PIN(AXIS),0)
_COUNTER(axis) += current_block->steps[_AXIS(AXIS)]; \
if (_COUNTER(axis) > 0) { \
_APPLY_STEP(AXIS)(!_INVERT_STEP_PIN(AXIS),0); \
_COUNTER(axis) -= current_block->step_event_count; \
count_position[_AXIS(AXIS)] += count_direction[_AXIS(AXIS)]; \
_APPLY_STEP(AXIS)(_INVERT_STEP_PIN(AXIS),0); \
}
APPLY_MOVEMENT
(
x
,
X
);
STEP_END
(
x
,
X
);
APPLY_MOVEMENT
(
y
,
Y
);
STEP_END
(
y
,
Y
);
APPLY_MOVEMENT
(
z
,
Z
);
STEP_END
(
z
,
Z
);
#ifndef ADVANCE
#ifndef ADVANCE
APPLY_MOVEMENT
(
e
,
E
);
STEP_END
(
e
,
E
);
#endif
#endif
#endif // CONFIG_STEPPERS_TOSHIBA
step_events_completed
++
;
step_events_completed
++
;
if
(
step_events_completed
>=
current_block
->
step_event_count
)
break
;
if
(
step_events_completed
>=
current_block
->
step_event_count
)
break
;
...
@@ -1096,6 +1067,7 @@ void st_init() {
...
@@ -1096,6 +1067,7 @@ void st_init() {
#endif
#endif
#define _STEP_INIT(AXIS) AXIS ##_STEP_INIT
#define _STEP_INIT(AXIS) AXIS ##_STEP_INIT
#define _WRITE_STEP(AXIS, HIGHLOW) AXIS ##_STEP_WRITE(HIGHLOW)
#define _DISABLE(axis) disable_## axis()
#define _DISABLE(axis) disable_## axis()
#define AXIS_INIT(axis, AXIS, PIN) \
#define AXIS_INIT(axis, AXIS, PIN) \
...
...
MarlinKimbra/temperature.cpp
View file @
cf380d0d
...
@@ -208,7 +208,15 @@ void PID_autotune(float temp, int hotend, int ncycles)
...
@@ -208,7 +208,15 @@ void PID_autotune(float temp, int hotend, int ncycles)
return
;
return
;
}
}
ECHO_LM
(
OK
,
MSG_PID_AUTOTUNE_START
);
ECHO_LM
(
DB
,
MSG_PID_AUTOTUNE_START
);
if
(
hotend
<
0
)
{
ECHO_SM
(
DB
,
"BED"
);
}
else
{
ECHO_SMV
(
DB
,
"Hotend: "
,
hotend
);
}
ECHO_MV
(
" Temp: "
,
temp
);
ECHO_EMV
(
" Cycles: "
,
ncycles
);
disable_all_heaters
();
// switch off all heaters.
disable_all_heaters
();
// switch off all heaters.
...
@@ -260,7 +268,7 @@ void PID_autotune(float temp, int hotend, int ncycles)
...
@@ -260,7 +268,7 @@ void PID_autotune(float temp, int hotend, int ncycles)
bias
=
constrain
(
bias
,
20
,
max_pow
-
20
);
bias
=
constrain
(
bias
,
20
,
max_pow
-
20
);
d
=
(
bias
>
max_pow
/
2
)
?
max_pow
-
1
-
bias
:
bias
;
d
=
(
bias
>
max_pow
/
2
)
?
max_pow
-
1
-
bias
:
bias
;
ECHO_
SMV
(
OK
,
MSG_BIAS
,
bias
);
ECHO_
MV
(
MSG_BIAS
,
bias
);
ECHO_MV
(
MSG_D
,
d
);
ECHO_MV
(
MSG_D
,
d
);
ECHO_MV
(
MSG_T_MIN
,
min
);
ECHO_MV
(
MSG_T_MIN
,
min
);
ECHO_MV
(
MSG_T_MAX
,
max
);
ECHO_MV
(
MSG_T_MAX
,
max
);
...
@@ -268,32 +276,15 @@ void PID_autotune(float temp, int hotend, int ncycles)
...
@@ -268,32 +276,15 @@ void PID_autotune(float temp, int hotend, int ncycles)
Ku
=
(
4.0
*
d
)
/
(
3.14159265
*
(
max
-
min
)
/
2.0
);
Ku
=
(
4.0
*
d
)
/
(
3.14159265
*
(
max
-
min
)
/
2.0
);
Tu
=
((
float
)(
t_low
+
t_high
)
/
1000.0
);
Tu
=
((
float
)(
t_low
+
t_high
)
/
1000.0
);
ECHO_MV
(
MSG_KU
,
Ku
);
ECHO_MV
(
MSG_KU
,
Ku
);
ECHO_MV
(
MSG_TU
,
Tu
);
ECHO_
E
MV
(
MSG_TU
,
Tu
);
Kp_temp
=
0.6
*
Ku
;
Kp_temp
=
0.6
*
Ku
;
Ki_temp
=
2
*
Kp_temp
/
Tu
;
Ki_temp
=
2
*
Kp_temp
/
Tu
;
Kd_temp
=
Kp_temp
*
Tu
/
8
;
Kd_temp
=
Kp_temp
*
Tu
/
8
;
ECHO_M
(
MSG_CLASSIC_PID
);
ECHO_
E
M
(
MSG_CLASSIC_PID
);
ECHO_MV
(
MSG_KP
,
Kp_temp
);
ECHO_MV
(
MSG_KP
,
Kp_temp
);
ECHO_MV
(
MSG_KI
,
Ki_temp
);
ECHO_MV
(
MSG_KI
,
Ki_temp
);
ECHO_EMV
(
MSG_KD
,
Kd_temp
);
ECHO_EMV
(
MSG_KD
,
Kd_temp
);
/*
Kp = 0.33*Ku;
Ki = Kp_temp / Tu;
Kd = Kp_temp * Tu / 3;
ECHO_SMV(DB," Some overshoot ");
ECHO_MV(" Kp: ", Kp_temp);
ECHO_MV(" Ki: ", Ki_temp);
ECHO_MV(" Kd: ", Kd_temp);
Kp = 0.2 * Ku;
Ki = 2 * Kp_temp / Tu;
Kd = Kp_temp * Tu / 3;
ECHO_M(" No overshoot ");
ECHO_MV(" Kp: ", Kp_temp);
ECHO_MV(" Ki: ", Ki_temp);
ECHO_EMV(" Kd: ", Kd_temp);
*/
}
}
else
{
else
{
ECHO_E
;
ECHO_E
;
...
@@ -312,6 +303,7 @@ void PID_autotune(float temp, int hotend, int ncycles)
...
@@ -312,6 +303,7 @@ void PID_autotune(float temp, int hotend, int ncycles)
ECHO_LM
(
ER
,
MSG_PID_TEMP_TOO_HIGH
);
ECHO_LM
(
ER
,
MSG_PID_TEMP_TOO_HIGH
);
return
;
return
;
}
}
// Every 2 seconds...
// Every 2 seconds...
if
(
ms
>
temp_ms
+
2000
)
{
if
(
ms
>
temp_ms
+
2000
)
{
int
p
;
int
p
;
...
@@ -328,20 +320,29 @@ void PID_autotune(float temp, int hotend, int ncycles)
...
@@ -328,20 +320,29 @@ void PID_autotune(float temp, int hotend, int ncycles)
temp_ms
=
ms
;
temp_ms
=
ms
;
}
// every 2 seconds
}
// every 2 seconds
// Over 2 minutes?
// Over 2 minutes?
if
(((
ms
-
t1
)
+
(
ms
-
t2
))
>
(
10L
*
60L
*
1000L
*
2L
))
{
if
(((
ms
-
t1
)
+
(
ms
-
t2
))
>
(
10L
*
60L
*
1000L
*
2L
))
{
ECHO_LM
(
ER
,
MSG_PID_TIMEOUT
);
ECHO_LM
(
ER
,
MSG_PID_TIMEOUT
);
return
;
return
;
}
}
if
(
cycles
>
ncycles
)
{
if
(
cycles
>
ncycles
)
{
ECHO_LM
(
OK
,
MSG_PID_AUTOTUNE_FINISHED
);
ECHO_LM
(
DB
,
MSG_PID_AUTOTUNE_FINISHED
);
#ifdef PIDTEMP
if
(
hotend
>=
0
)
{
if
(
hotend
>=
0
)
{
PID_PARAM
(
Kp
,
hotend
)
=
Kp_temp
;
PID_PARAM
(
Kp
,
hotend
)
=
Kp_temp
;
PID_PARAM
(
Ki
,
hotend
)
=
scalePID_i
(
Ki_temp
);
PID_PARAM
(
Ki
,
hotend
)
=
scalePID_i
(
Ki_temp
);
PID_PARAM
(
Kd
,
hotend
)
=
scalePID_d
(
Kd_temp
);
PID_PARAM
(
Kd
,
hotend
)
=
scalePID_d
(
Kd_temp
);
updatePID
();
}
#endif
ECHO_SMV
(
DB
,
MSG_KP
,
PID_PARAM
(
Kp
,
hotend
));
ECHO_MV
(
MSG_KI
,
unscalePID_i
(
PID_PARAM
(
Ki
,
hotend
)));
ECHO_EMV
(
MSG_KD
,
unscalePID_d
(
PID_PARAM
(
Kd
,
hotend
)));
}
else
{
ECHO_LMV
(
DB
,
"#define DEFAULT_bedKp "
,
Kp_temp
);
ECHO_LMV
(
DB
,
"#define DEFAULT_bedKi "
,
unscalePID_i
(
Ki_temp
));
ECHO_LMV
(
DB
,
"#define DEFAULT_bedKd "
,
unscalePID_d
(
Kd_temp
));
}
return
;
return
;
}
}
lcd_update
();
lcd_update
();
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment