Merge remote-tracking branch 'public_remote/MK3' into PFW-556

This commit is contained in:
MRprusa3d 2019-01-24 19:39:34 +01:00
commit 46e2bdaf62
40 changed files with 4128 additions and 3815 deletions

14
.gitignore vendored
View file

@ -16,3 +16,17 @@ Firmware/Doc
/html
/latex
/Doxyfile
/lang/*.bin
/lang/*.hex
/lang/*.dat
/lang/*.tmp
/lang/*.out
/lang/not_tran.txt
/lang/not_used.txt
/lang/progmem1.chr
/lang/progmem1.lss
/lang/progmem1.txt
/lang/progmem1.var
/lang/text.sym
/lang/textaddr.txt
/build-env/

19
.travis.yml Normal file
View file

@ -0,0 +1,19 @@
dist: trusty
before_install:
- sudo apt-get install -y ninja-build
script:
- bash -x test.sh
- cp Firmware/variants/1_75mm_MK3-EINSy10a-E3Dv6full.h Firmware/Configuration_prusa.h
- bash -x build.sh || { echo "1_75mm_MK3-EINSy10a-E3Dv6full variant failed" && false; }
- rm Firmware/Configuration_prusa.h
- cp Firmware/variants/1_75mm_MK25-RAMBo13a-E3Dv6full.h Firmware/Configuration_prusa.h
- bash -x build.sh || { echo "1_75mm_MK25-RAMBo13a-E3Dv6full variant failed" && false; }
- rm Firmware/Configuration_prusa.h
- cp Firmware/variants/1_75mm_MK25-RAMBo10a-E3Dv6full.h Firmware/Configuration_prusa.h
- bash -x build.sh || { echo "1_75mm_MK25-RAMBo10a-E3Dv6full variant failed" && false; }
- rm Firmware/Configuration_prusa.h
- cp Firmware/variants/1_75mm_MK2-RAMBo13a-E3Dv6full.h Firmware/Configuration_prusa.h
- bash -x build.sh || { echo "1_75mm_MK2-RAMBo13a-E3Dv6full variant failed" && false; }
- rm Firmware/Configuration_prusa.h
- cp Firmware/variants/1_75mm_MK2-RAMBo10a-E3Dv6full.h Firmware/Configuration_prusa.h
- bash -x build.sh || { echo "1_75mm_MK2-RAMBo10a-E3Dv6full variant failed" && false; }

View file

@ -1,4 +1,6 @@
cmake_minimum_required(VERSION 3.0)
cmake_minimum_required(VERSION 3.1)
set (CMAKE_CXX_STANDARD 11)
project(cmake_test)
@ -12,7 +14,9 @@ set(TEST_SOURCES
Tests/tests.cpp
Tests/Example_test.cpp
Tests/Timer_test.cpp
Tests/AutoDeplete_test.cpp
Firmware/Timer.cpp
Firmware/AutoDeplete.cpp
)
add_executable(tests ${TEST_SOURCES})
target_include_directories(tests PRIVATE Tests)

79
Firmware/AutoDeplete.cpp Normal file
View file

@ -0,0 +1,79 @@
//! @file
//! @author: Marek Bel
//! @date Jan 3, 2019
#include "AutoDeplete.h"
#include "assert.h"
//! @brief bit field marking depleted filaments
//!
//! binary 1 marks filament as depleted
//! Zero initialized value means, that no filament is depleted.
static uint8_t depleted;
static const uint8_t filamentCount = 5;
//! @return binary 1 for all filaments
//! @par fCount number of filaments
static constexpr uint8_t allDepleted(uint8_t fCount)
{
return fCount == 1 ? 1 : ((1 << (fCount - 1)) | allDepleted(fCount - 1));
}
//! @brief Is filament available for printing?
//! @par filament Filament number to be checked
//! @retval true Filament is available for printing.
//! @retval false Filament is not available for printing.
static bool loaded(uint8_t filament)
{
if (depleted & (1 << filament)) return false;
return true;
}
//! @brief Mark filament as not available for printing.
//! @par filament filament to be marked
void ad_markDepleted(uint8_t filament)
{
assert(filament < filamentCount);
if (filament < filamentCount)
{
depleted |= 1 << filament;
}
}
//! @brief Mark filament as available for printing.
//! @par filament filament to be marked
void ad_markLoaded(uint8_t filament)
{
assert(filament < filamentCount);
if (filament < filamentCount)
{
depleted &= ~(1 << filament);
}
}
//! @brief Get alternative filament, which is not depleted
//! @par filament filament
//! @return Filament, if it is depleted, returns next available,
//! if all filaments are depleted, returns filament function parameter.
uint8_t ad_getAlternative(uint8_t filament)
{
assert(filament < filamentCount);
for (uint8_t i = 0; i<filamentCount; ++i)
{
uint8_t nextFilament = (filament + i) % filamentCount;
if (loaded(nextFilament)) return nextFilament;
}
return filament;
}
//! @brief Are all filaments depleted?
//! @retval true All filaments are depleted.
//! @retval false All filaments are not depleted.
bool ad_allDepleted()
{
if (allDepleted(filamentCount) == depleted)
{
return true;
}
return false;
}

17
Firmware/AutoDeplete.h Normal file
View file

@ -0,0 +1,17 @@
//! @file
//! @author: Marek Bel
//! @brief Filament auto deplete engine for multi-material prints with MMUv2 (Now marketed as SpoolJoin)
//!
//! Interface for marking MMUv2 filaments as depleted and getting alternative filament for printing.
#ifndef AUTODEPLETE_H
#define AUTODEPLETE_H
#include <stdint.h>
void ad_markDepleted(uint8_t filament);
void ad_markLoaded(uint8_t filament);
uint8_t ad_getAlternative(uint8_t filament);
bool ad_allDepleted();
#endif /* AUTODEPLETE_H */

View file

@ -7,8 +7,8 @@
#define STR(x) STR_HELPER(x)
// Firmware version
#define FW_VERSION "3.5.0"
#define FW_COMMIT_NR 1749
#define FW_VERSION "3.5.1"
#define FW_COMMIT_NR 1778
// FW_VERSION_UNKNOWN means this is an unofficial build.
// The firmware should only be checked into github with this symbol.
#define FW_DEV_VERSION FW_VERSION_UNKNOWN
@ -126,13 +126,11 @@
// Comment the following line to disable PID and enable bang-bang.
#define PIDTEMP
#define BANG_MAX 255 // limits current to nozzle while in bang-bang mode; 255=full current
#define PID_MAX BANG_MAX // limits current to nozzle while PID is active (see PID_FUNCTIONAL_RANGE below); 255=full current
#define PID_MAX BANG_MAX // limits current to nozzle while PID is active; 255=full current
#ifdef PIDTEMP
//#define PID_DEBUG // Sends debug data to the serial port.
//#define PID_OPENLOOP 1 // Puts PID in open loop. M104/M140 sets the output power from 0 to PID_MAX
//#define SLOW_PWM_HEATERS // PWM with very low frequency (roughly 0.125Hz=8s) and minimum state time of approximately 1s useful for heaters driven by a relay
#define PID_FUNCTIONAL_RANGE 10 // If the temperature difference between the target temperature and the actual temperature
// is more then PID_FUNCTIONAL_RANGE then the PID will be shut off and the heater will be set to min/max.
#define PID_INTEGRAL_DRIVE_MAX PID_MAX //limit for the integral term
#define K1 0.95 //smoothing factor within the PID
#define PID_dT ((OVERSAMPLENR * 10.0)/(F_CPU / 64.0 / 256.0)) //sampling period of the temperature routine

View file

@ -78,6 +78,7 @@
#include <avr/pgmspace.h>
#include "Dcodes.h"
#include "AutoDeplete.h"
#ifdef SWSPI
@ -129,6 +130,7 @@
#include "sound.h"
#include "cmdqueue.h"
#include "io_atmega2560.h"
// Macros for bit masks
#define BIT(b) (1<<(b))
@ -394,6 +396,8 @@ static bool saved_extruder_relative_mode = false;
static int saved_fanSpeed = 0; //!< Print fan speed
//! @}
static int saved_feedmultiply_mm = 100;
//===========================================================================
//=============================Routines======================================
//===========================================================================
@ -639,6 +643,8 @@ void failstats_reset_print()
eeprom_update_byte((uint8_t *)EEPROM_CRASH_COUNT_Y, 0);
eeprom_update_byte((uint8_t *)EEPROM_FERROR_COUNT, 0);
eeprom_update_byte((uint8_t *)EEPROM_POWER_COUNT, 0);
eeprom_update_byte((uint8_t *)EEPROM_MMU_FAIL, 0);
eeprom_update_byte((uint8_t *)EEPROM_MMU_LOAD_FAIL, 0);
}
@ -685,6 +691,12 @@ if((eSoundMode==e_SOUND_MODE_LOUD)||(eSoundMode==e_SOUND_MODE_ONCE))
eeprom_update_word((uint16_t *)EEPROM_FERROR_COUNT_TOT, 0);
eeprom_update_word((uint16_t *)EEPROM_POWER_COUNT_TOT, 0);
eeprom_update_word((uint16_t *)EEPROM_MMU_FAIL_TOT, 0);
eeprom_update_word((uint16_t *)EEPROM_MMU_LOAD_FAIL_TOT, 0);
eeprom_update_byte((uint8_t *)EEPROM_MMU_FAIL, 0);
eeprom_update_byte((uint8_t *)EEPROM_MMU_LOAD_FAIL, 0);
lcd_menu_statistics();
break;
@ -711,6 +723,11 @@ if((eSoundMode==e_SOUND_MODE_LOUD)||(eSoundMode==e_SOUND_MODE_ONCE))
eeprom_update_word((uint16_t *)EEPROM_FERROR_COUNT_TOT, 0);
eeprom_update_word((uint16_t *)EEPROM_POWER_COUNT_TOT, 0);
eeprom_update_word((uint16_t *)EEPROM_MMU_FAIL_TOT, 0);
eeprom_update_word((uint16_t *)EEPROM_MMU_LOAD_FAIL_TOT, 0);
eeprom_update_byte((uint8_t *)EEPROM_MMU_FAIL, 0);
eeprom_update_byte((uint8_t *)EEPROM_MMU_LOAD_FAIL, 0);
#ifdef FILAMENT_SENSOR
fsensor_enable();
fsensor_autoload_set(true);
@ -1385,6 +1402,12 @@ void setup()
if (eeprom_read_word((uint16_t*)EEPROM_CRASH_COUNT_X_TOT) == 0xffff) eeprom_write_word((uint16_t*)EEPROM_CRASH_COUNT_X_TOT, 0);
if (eeprom_read_word((uint16_t*)EEPROM_CRASH_COUNT_Y_TOT) == 0xffff) eeprom_write_word((uint16_t*)EEPROM_CRASH_COUNT_Y_TOT, 0);
if (eeprom_read_word((uint16_t*)EEPROM_FERROR_COUNT_TOT) == 0xffff) eeprom_write_word((uint16_t*)EEPROM_FERROR_COUNT_TOT, 0);
if (eeprom_read_word((uint16_t*)EEPROM_MMU_FAIL_TOT) == 0xffff) eeprom_update_word((uint16_t *)EEPROM_MMU_FAIL_TOT, 0);
if (eeprom_read_word((uint16_t*)EEPROM_MMU_LOAD_FAIL_TOT) == 0xffff) eeprom_update_word((uint16_t *)EEPROM_MMU_LOAD_FAIL_TOT, 0);
if (eeprom_read_byte((uint8_t*)EEPROM_MMU_FAIL) == 0xff) eeprom_update_byte((uint8_t *)EEPROM_MMU_FAIL, 0);
if (eeprom_read_byte((uint8_t*)EEPROM_MMU_LOAD_FAIL) == 0xff) eeprom_update_byte((uint8_t *)EEPROM_MMU_LOAD_FAIL, 0);
#ifdef SNMM
if (eeprom_read_dword((uint32_t*)EEPROM_BOWDEN_LENGTH) == 0x0ffffffff) { //bowden length used for SNMM
int _z = BOWDEN_LENGTH;
@ -2791,10 +2814,15 @@ bool gcode_M45(bool onlyZ, int8_t verbosity_level)
lcd_puts_P(_T(MSG_FIND_BED_OFFSET_AND_SKEW_LINE2));
}
bool endstops_enabled = enable_endstops(false);
current_position[Z_AXIS] -= 1; //move 1mm down with disabled endstop
plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS], homing_feedrate[Z_AXIS] / 40, active_extruder);
st_synchronize();
// Move the print head close to the bed.
current_position[Z_AXIS] = MESH_HOME_Z_SEARCH;
bool endstops_enabled = enable_endstops(true);
enable_endstops(true);
#ifdef TMC2130
tmc2130_home_enter(Z_AXIS_MASK);
#endif //TMC2130
@ -4524,10 +4552,20 @@ if((eSoundMode==e_SOUND_MODE_LOUD)||(eSoundMode==e_SOUND_MODE_ONCE))
lcd_calibrate_z_end_stop_manual(true); // Z-leveling (X-assembly stay up!!!)
#endif // TMC2130
// ~ Z-homing (can not be used "G28", because X & Y-homing would have been done before (Z-homing))
bState=enable_z_endstop(true);
bState=enable_z_endstop(false);
current_position[Z_AXIS] -= 1;
plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS], homing_feedrate[Z_AXIS] / 40, active_extruder);
st_synchronize();
enable_z_endstop(true);
#ifdef TMC2130
tmc2130_home_enter(Z_AXIS_MASK);
#endif // TMC2130
current_position[Z_AXIS] = MESH_HOME_Z_SEARCH;
plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS], homing_feedrate[Z_AXIS] / 40, active_extruder);
st_synchronize();
#ifdef TMC2130
tmc2130_home_exit();
#endif // TMC2130
enable_z_endstop(bState);
} while (st_get_position_mm(Z_AXIS) > MESH_HOME_Z_SEARCH); // i.e. Z-leveling not o.k.
// plan_set_z_position(MESH_HOME_Z_SEARCH); // is not necessary ('do-while' loop always ends at the expected Z-position)
@ -6078,12 +6116,20 @@ Sigma_Exit:
SERIAL_ECHOLN("");
}break;
#endif
case 220: // M220 S<factor in percent>- set speed factor override percentage
{
if(code_seen('S'))
if (code_seen('B')) //backup current speed factor
{
saved_feedmultiply_mm = feedmultiply;
}
if(code_seen('S'))
{
feedmultiply = code_value() ;
}
if (code_seen('R')) { //restore previous feedmultiply
feedmultiply = saved_feedmultiply_mm;
}
}
break;
case 221: // M221 S<factor in percent>- set extrude factor override percentage
@ -6493,6 +6539,7 @@ if((eSoundMode==e_SOUND_MODE_LOUD)||(eSoundMode==e_SOUND_MODE_ONCE))
#endif //FILAMENTCHANGEENABLE
case 601: //! M601 - Pause print
{
cmdqueue_pop_front(); //trick because we want skip this command (M601) after restore
lcd_pause_print();
}
break;
@ -6847,8 +6894,10 @@ if((eSoundMode==e_SOUND_MODE_LOUD)||(eSoundMode==e_SOUND_MODE_ONCE))
//! if extruder is "?", open menu to let the user select extruder/filament
//!
//! For MMU_V2:
//! @n T<n> Gcode to extrude must follow immediately to load to extruder wheels
//! @n T? Gcode to extrude doesn't have to follow, load to extruder wheels is done automatically
//! @n T<n> Gcode to extrude at least 38.10 mm at feedrate 19.02 mm/s must follow immediately to load to extruder wheels.
//! @n T? Gcode to extrude shouldn't have to follow, load to extruder wheels is done automatically
//! @n Tx Same as T?, except nozzle doesn't have to be preheated. Tc must be placed after extruder nozzle is preheated to finish filament load.
//! @n Tc Load to nozzle after filament was prepared by Tc and extruder nozzle is already heated.
else if(code_seen('T'))
{
int index;
@ -6864,16 +6913,20 @@ if((eSoundMode==e_SOUND_MODE_LOUD)||(eSoundMode==e_SOUND_MODE_ONCE))
if (mmu_enabled)
{
tmp_extruder = choose_menu_P(_T(MSG_CHOOSE_FILAMENT), _T(MSG_FILAMENT));
if ((tmp_extruder == mmu_extruder) && mmu_fil_loaded) {
printf_P(PSTR("Duplicit T-code ignored.\n"));
return; //dont execute the same T-code twice in a row
}
st_synchronize();
mmu_command(MMU_CMD_T0 + tmp_extruder);
manage_response(true, true);
manage_response(true, true, MMU_TCODE_MOVE);
}
}
else if (*(strchr_pointer + index) == 'c') { //load to from bondtech gears to nozzle (nozzle should be preheated)
if (mmu_enabled)
{
st_synchronize();
mmu_command(MMU_CMD_C0);
mmu_continue_loading();
mmu_extruder = tmp_extruder; //filament change is finished
mmu_load_to_nozzle();
}
@ -6892,17 +6945,25 @@ if((eSoundMode==e_SOUND_MODE_LOUD)||(eSoundMode==e_SOUND_MODE_ONCE))
}
else {
tmp_extruder = code_value();
if (mmu_enabled && lcd_autoDepleteEnabled())
{
tmp_extruder = ad_getAlternative(tmp_extruder);
}
}
st_synchronize();
snmm_filaments_used |= (1 << tmp_extruder); //for stop print
if (mmu_enabled)
{
if ((tmp_extruder == mmu_extruder) && mmu_fil_loaded) {
printf_P(PSTR("Duplicit T-code ignored.\n"));
return; //dont execute the same T-code twice in a row
}
mmu_command(MMU_CMD_T0 + tmp_extruder);
manage_response(true, true);
mmu_command(MMU_CMD_C0);
mmu_extruder = tmp_extruder; //filament change is finished
manage_response(true, true, MMU_TCODE_MOVE);
mmu_continue_loading();
mmu_extruder = tmp_extruder; //filament change is finished
if (load_to_nozzle)// for single material usage with mmu
{

View file

@ -43,8 +43,8 @@
#define W25X20CL_SPSR SPI_SPSR(W25X20CL_SPI_RATE)
//LANG - Multi-language support
#define LANG_MODE 0 // primary language only
//#define LANG_MODE 1 // sec. language support
//#define LANG_MODE 0 // primary language only
#define LANG_MODE 1 // sec. language support
#define LANG_SIZE_RESERVED 0x2f00 // reserved space for secondary language (12032 bytes)

View file

@ -148,6 +148,12 @@
#define EEPROM_FSENS_OQ_MEASS_ENABLED (EEPROM_AUTO_DEPLETE - 1) //bool
#define EEPROM_MMU_FAIL_TOT (EEPROM_FSENS_OQ_MEASS_ENABLED - 2) //uint16_t
#define EEPROM_MMU_FAIL (EEPROM_MMU_FAIL_TOT - 1) //uint8_t
#define EEPROM_MMU_LOAD_FAIL_TOT (EEPROM_MMU_FAIL - 2) //uint16_t
#define EEPROM_MMU_LOAD_FAIL (EEPROM_MMU_LOAD_FAIL_TOT - 1) //uint8_t
// !!!!!
// !!!!! this is end of EEPROM section ... all updates MUST BE inserted before this mark !!!!!
// !!!!!

View file

@ -1,4 +1,4 @@
//mmu.cpp
//! @file
#include "mmu.h"
#include "planner.h"
@ -13,6 +13,8 @@
#include "sound.h"
#include "printers.h"
#include <avr/pgmspace.h>
#include "io_atmega2560.h"
#include "AutoDeplete.h"
#ifdef TMC2130
#include "tmc2130.h"
@ -22,25 +24,31 @@
#define MMU_TODELAY 100
#define MMU_TIMEOUT 10
#define MMU_CMD_TIMEOUT 300000ul //5min timeout for mmu commands (except P0)
#define MMU_CMD_TIMEOUT 45000ul //5min timeout for mmu commands (except P0)
#define MMU_P0_TIMEOUT 3000ul //timeout for P0 command: 3seconds
#define MMU_MAX_RESEND_ATTEMPTS 2
#ifdef MMU_HWRESET
#define MMU_RST_PIN 76
#endif //MMU_HWRESET
bool mmu_enabled = false;
bool mmu_ready = false;
bool mmu_fil_loaded = false; //if true: blocks execution of duplicit T-codes
static int8_t mmu_state = 0;
uint8_t mmu_cmd = 0;
uint8_t mmu_extruder = 0;
//idler ir sensor
uint8_t mmu_idl_sens = 0;
bool mmu_idler_sensor_detected = false;
bool mmu_loading_flag = false;
uint8_t mmu_extruder = MMU_FILAMENT_UNKNOWN;
//! This variable probably has no meaning and is planed to be removed
uint8_t tmp_extruder = 0;
uint8_t tmp_extruder = MMU_FILAMENT_UNKNOWN;
int8_t mmu_finda = -1;
@ -51,6 +59,9 @@ int16_t mmu_buildnr = -1;
uint32_t mmu_last_request = 0;
uint32_t mmu_last_response = 0;
uint8_t mmu_last_cmd = 0;
uint16_t mmu_power_failures = 0;
//clear rx buffer
void mmu_clr_rx_buf(void)
@ -106,11 +117,31 @@ void mmu_init(void)
_delay_ms(10); //wait 10ms for sure
mmu_reset(); //reset mmu (HW or SW), do not wait for response
mmu_state = -1;
PIN_INP(MMU_IDLER_SENSOR_PIN); //input mode
PIN_SET(MMU_IDLER_SENSOR_PIN); //pullup
}
//returns true if idler IR sensor was detected, otherwise returns false
bool check_for_idler_sensor()
{
bool detected = false;
//if MMU_IDLER_SENSOR_PIN input is low and pat9125sensor is not present we detected idler sensor
if ((PIN_GET(MMU_IDLER_SENSOR_PIN) == 0) && fsensor_not_responding)
{
detected = true;
//printf_P(PSTR("Idler IR sensor detected\n"));
}
else
{
//printf_P(PSTR("Idler IR sensor not detected\n"));
}
return detected;
}
//mmu main loop - state machine processing
void mmu_loop(void)
{
static uint8_t mmu_attempt_nr = 0;
int filament = 0;
// printf_P(PSTR("MMU loop, state=%d\n"), mmu_state);
switch (mmu_state)
@ -158,9 +189,9 @@ void mmu_loop(void)
if ((PRINTER_TYPE == PRINTER_MK3) || (PRINTER_TYPE == PRINTER_MK3_SNMM))
{
#ifdef MMU_DEBUG
#if defined MMU_DEBUG && defined MMU_FINDA_DEBUG
puts_P(PSTR("MMU <= 'P0'"));
#endif //MMU_DEBUG
#endif //MMU_DEBUG && MMU_FINDA_DEBUG
mmu_puts_P(PSTR("P0\n")); //send 'read finda' request
mmu_state = -4;
}
@ -178,9 +209,9 @@ void mmu_loop(void)
case -5:
if (mmu_rx_ok() > 0)
{
#ifdef MMU_DEBUG
#if defined MMU_DEBUG && defined MMU_FINDA_DEBUG
puts_P(PSTR("MMU <= 'P0'"));
#endif //MMU_DEBUG
#endif //MMU_DEBUG && MMU_FINDA_DEBUG
mmu_puts_P(PSTR("P0\n")); //send 'read finda' request
mmu_state = -4;
}
@ -189,11 +220,13 @@ void mmu_loop(void)
if (mmu_rx_ok() > 0)
{
fscanf_P(uart2io, PSTR("%hhu"), &mmu_finda); //scan finda from buffer
#ifdef MMU_DEBUG
#if defined MMU_DEBUG && defined MMU_FINDA_DEBUG
printf_P(PSTR("MMU => '%dok'\n"), mmu_finda);
#endif //MMU_DEBUG
#endif //MMU_DEBUG && MMU_FINDA_DEBUG
puts_P(PSTR("MMU - ENABLED"));
mmu_enabled = true;
//if we have filament loaded into the nozzle, we can decide if printer has idler sensor right now; otherwise we will will wait till start of T-code so it will be detected on beginning of second T-code
if(check_for_idler_sensor()) mmu_idler_sensor_detected = true;
mmu_state = 1;
}
return;
@ -208,6 +241,8 @@ void mmu_loop(void)
#endif //MMU_DEBUG
mmu_printf_P(PSTR("T%d\n"), filament);
mmu_state = 3; // wait for response
mmu_fil_loaded = true;
mmu_idl_sens = 1;
}
else if ((mmu_cmd >= MMU_CMD_L0) && (mmu_cmd <= MMU_CMD_L4))
{
@ -225,6 +260,7 @@ void mmu_loop(void)
#endif //MMU_DEBUG
mmu_puts_P(PSTR("C0\n")); //send 'continue loading'
mmu_state = 3;
mmu_idl_sens = 1;
}
else if (mmu_cmd == MMU_CMD_U0)
{
@ -232,6 +268,7 @@ void mmu_loop(void)
printf_P(PSTR("MMU <= 'U0'\n"));
#endif //MMU_DEBUG
mmu_puts_P(PSTR("U0\n")); //send 'unload current filament'
mmu_fil_loaded = false;
mmu_state = 3;
}
else if ((mmu_cmd >= MMU_CMD_E0) && (mmu_cmd <= MMU_CMD_E4))
@ -241,6 +278,7 @@ void mmu_loop(void)
printf_P(PSTR("MMU <= 'E%d'\n"), filament);
#endif //MMU_DEBUG
mmu_printf_P(PSTR("E%d\n"), filament); //send eject filament
mmu_fil_loaded = false;
mmu_state = 3; // wait for response
}
else if (mmu_cmd == MMU_CMD_R0)
@ -251,13 +289,23 @@ void mmu_loop(void)
mmu_puts_P(PSTR("R0\n")); //send recover after eject
mmu_state = 3; // wait for response
}
else if (mmu_cmd == MMU_CMD_S3)
{
#ifdef MMU_DEBUG
printf_P(PSTR("MMU <= 'S3'\n"));
#endif //MMU_DEBUG
mmu_puts_P(PSTR("S3\n")); //send power failures request
mmu_state = 4; // power failures response
}
mmu_last_cmd = mmu_cmd;
mmu_cmd = 0;
}
else if ((mmu_last_response + 300) < millis()) //request every 300ms
{
#ifdef MMU_DEBUG
if(check_for_idler_sensor()) mmu_idler_sensor_detected = true;
#if defined MMU_DEBUG && defined MMU_FINDA_DEBUG
puts_P(PSTR("MMU <= 'P0'"));
#endif //MMU_DEBUG
#endif //MMU_DEBUG && MMU_FINDA_DEBUG
mmu_puts_P(PSTR("P0\n")); //send 'read finda' request
mmu_state = 2;
}
@ -266,15 +314,22 @@ void mmu_loop(void)
if (mmu_rx_ok() > 0)
{
fscanf_P(uart2io, PSTR("%hhu"), &mmu_finda); //scan finda from buffer
#ifdef MMU_DEBUG
#if defined MMU_DEBUG && MMU_FINDA_DEBUG
printf_P(PSTR("MMU => '%dok'\n"), mmu_finda);
#endif //MMU_DEBUG
#endif //MMU_DEBUG && MMU_FINDA_DEBUG
//printf_P(PSTR("Eact: %d\n"), int(e_active()));
if (!mmu_finda && CHECK_FINDA && fsensor_enabled) {
fsensor_stop_and_save_print();
enquecommand_front_P(PSTR("FSENSOR_RECOVER")); //then recover
if (lcd_autoDepleteEnabled()) enquecommand_front_P(PSTR("M600 AUTO")); //save print and run M600 command
else enquecommand_front_P(PSTR("M600")); //save print and run M600 command
ad_markDepleted(mmu_extruder);
if (lcd_autoDepleteEnabled() && !ad_allDepleted())
{
enquecommand_front_P(PSTR("M600 AUTO")); //save print and run M600 command
}
else
{
enquecommand_front_P(PSTR("M600")); //save print and run M600 command
}
}
mmu_state = 1;
if (mmu_cmd == 0)
@ -286,11 +341,57 @@ void mmu_loop(void)
}
return;
case 3: //response to mmu commands
if (mmu_idl_sens)
{
if (PIN_GET(MMU_IDLER_SENSOR_PIN) == 0 && mmu_loading_flag)
{
#ifdef MMU_DEBUG
printf_P(PSTR("MMU <= 'A'\n"));
#endif //MMU_DEBUG
mmu_puts_P(PSTR("A\n")); //send 'abort' request
mmu_idl_sens = 0;
//printf_P(PSTR("MMU IDLER_SENSOR = 0 - ABORT\n"));
}
//else
//printf_P(PSTR("MMU IDLER_SENSOR = 1 - WAIT\n"));
}
if (mmu_rx_ok() > 0)
{
#ifdef MMU_DEBUG
printf_P(PSTR("MMU => 'ok'\n"));
#endif //MMU_DEBUG
mmu_attempt_nr = 0;
mmu_last_cmd = 0;
mmu_ready = true;
mmu_state = 1;
}
else if ((mmu_last_request + MMU_CMD_TIMEOUT) < millis())
{ //resend request after timeout (5 min)
if (mmu_last_cmd)
{
if (mmu_attempt_nr++ < MMU_MAX_RESEND_ATTEMPTS) {
#ifdef MMU_DEBUG
printf_P(PSTR("MMU retry attempt nr. %d\n"), mmu_attempt_nr - 1);
#endif //MMU_DEBUG
mmu_cmd = mmu_last_cmd;
}
else {
mmu_cmd = 0;
mmu_last_cmd = 0; //check
mmu_attempt_nr = 0;
}
}
mmu_state = 1;
}
return;
case 4:
if (mmu_rx_ok() > 0)
{
fscanf_P(uart2io, PSTR("%d"), &mmu_power_failures); //scan power failures
#ifdef MMU_DEBUG
printf_P(PSTR("MMU => 'ok'\n"));
#endif //MMU_DEBUG
mmu_last_cmd = 0;
mmu_ready = true;
mmu_state = 1;
}
@ -298,7 +399,6 @@ void mmu_loop(void)
{ //resend request after timeout (5 min)
mmu_state = 1;
}
return;
}
}
@ -323,64 +423,134 @@ int8_t mmu_set_filament_type(uint8_t extruder, uint8_t filament)
return timeout?1:0;
}
//! @brief Enqueue MMUv2 command
//!
//! Call manage_response() after enqueuing to process command.
//! If T command is enqueued, it disables current for extruder motor if TMC2130 driver present.
//! If T or L command is enqueued, it marks filament loaded in AutoDeplete module.
void mmu_command(uint8_t cmd)
{
#ifdef TMC2130
if ((cmd >= MMU_CMD_T0) && (cmd <= MMU_CMD_T4))
{
//disable extruder motor
#ifdef TMC2130
tmc2130_set_pwr(E_AXIS, 0);
//printf_P(PSTR("E-axis disabled\n"));
}
#endif //TMC2130
//printf_P(PSTR("E-axis disabled\n"));
ad_markLoaded(cmd - MMU_CMD_T0);
}
if ((cmd >= MMU_CMD_L0) && (cmd <= MMU_CMD_L4))
{
ad_markLoaded(cmd - MMU_CMD_L0);
}
mmu_cmd = cmd;
mmu_ready = false;
}
bool mmu_get_response(void)
void mmu_load_step() {
current_position[E_AXIS] = current_position[E_AXIS] + MMU_LOAD_FEEDRATE * 0.1;
plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS], MMU_LOAD_FEEDRATE, active_extruder);
st_synchronize();
}
//! @brief Is nozzle hot enough to move extruder wheels and do we have idler sensor?
//!
//! Do load steps only if temperature is higher then min. temp for safe extrusion and
//! idler sensor present.
//! Otherwise "cold extrusion prevented" would be send to serial line periodically
//! and watchdog reset will be triggered by lack of keep_alive processing.
//!
//! @retval true temperature is high enough to move extruder
//! @retval false temperature is not high enough to move extruder, turned
//! off E-stepper to prevent over-heating and allow filament pull-out if necessary
bool can_extrude()
{
// printf_P(PSTR("mmu_get_response - begin\n"));
if ((degHotend(active_extruder) < EXTRUDE_MINTEMP) || !mmu_idler_sensor_detected)
{
disable_e0();
delay_keep_alive(100);
return false;
}
return true;
}
bool mmu_get_response(uint8_t move)
{
mmu_loading_flag = false;
printf_P(PSTR("mmu_get_response - begin move:%d\n"), move);
KEEPALIVE_STATE(IN_PROCESS);
while (mmu_cmd != 0)
{
// mmu_loop();
delay_keep_alive(100);
}
while (!mmu_ready)
{
// mmu_loop();
if (mmu_state != 3)
if ((mmu_state != 3) && (mmu_last_cmd == 0))
break;
delay_keep_alive(100);
switch (move) {
case MMU_LOAD_MOVE:
mmu_loading_flag = true;
if (can_extrude()) mmu_load_step();
//don't rely on "ok" signal from mmu unit; if filament detected by idler sensor during loading stop loading movements to prevent infinite loading
if (PIN_GET(MMU_IDLER_SENSOR_PIN) == 0) move = MMU_NO_MOVE;
break;
case MMU_UNLOAD_MOVE:
if (PIN_GET(MMU_IDLER_SENSOR_PIN) == 0) //filament is still detected by idler sensor, printer helps with unlading
{
if (can_extrude())
{
printf_P(PSTR("Unload 1\n"));
current_position[E_AXIS] = current_position[E_AXIS] - MMU_LOAD_FEEDRATE * MMU_LOAD_TIME_MS*0.001;
plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS], MMU_LOAD_FEEDRATE, active_extruder);
st_synchronize();
}
}
else //filament was unloaded from idler, no additional movements needed
{
printf_P(PSTR("Unloading finished 1\n"));
disable_e0(); //turn off E-stepper to prevent overheating and alow filament pull-out if necessary
move = MMU_NO_MOVE;
}
break;
case MMU_TCODE_MOVE: //first do unload and then continue with infinite loading movements
if (PIN_GET(MMU_IDLER_SENSOR_PIN) == 0) //filament detected by idler sensor, we must unload first
{
if (can_extrude())
{
printf_P(PSTR("Unload 2\n"));
current_position[E_AXIS] = current_position[E_AXIS] - MMU_LOAD_FEEDRATE * MMU_LOAD_TIME_MS*0.001;
plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS], MMU_LOAD_FEEDRATE, active_extruder);
st_synchronize();
}
}
else //delay to allow mmu unit to pull out filament from bondtech gears and then start with infinite loading
{
printf_P(PSTR("Unloading finished 2\n"));
disable_e0(); //turn off E-stepper to prevent overheating and alow filament pull-out if necessary
delay_keep_alive(MMU_LOAD_TIME_MS);
move = MMU_LOAD_MOVE;
printf_P(PSTR("mmu_get_response - begin move:%d\n"), move);
}
break;
case MMU_NO_MOVE:
default:
delay_keep_alive(100);
break;
}
}
printf_P(PSTR("mmu_get_response() returning: %d\n"), mmu_ready);
bool ret = mmu_ready;
mmu_ready = false;
// printf_P(PSTR("mmu_get_response - end %d\n"), ret?1:0);
return ret;
/* //waits for "ok" from mmu
//function returns true if "ok" was received
//if timeout is set to true function return false if there is no "ok" received before timeout
bool response = true;
LongTimer mmu_get_reponse_timeout;
KEEPALIVE_STATE(IN_PROCESS);
mmu_get_reponse_timeout.start();
while (mmu_rx_ok() <= 0)
{
delay_keep_alive(100);
if (timeout && mmu_get_reponse_timeout.expired(5 * 60 * 1000ul))
{ //5 minutes timeout
response = false;
break;
}
}
printf_P(PSTR("mmu_get_response - end %d\n"), response?1:0);
return response;*/
}
void manage_response(bool move_axes, bool turn_off_nozzle)
void manage_response(bool move_axes, bool turn_off_nozzle, uint8_t move)
{
bool response = false;
mmu_print_saved = false;
@ -390,11 +560,18 @@ void manage_response(bool move_axes, bool turn_off_nozzle)
float x_position_bckp = current_position[X_AXIS];
float y_position_bckp = current_position[Y_AXIS];
uint8_t screen = 0; //used for showing multiscreen messages
while(!response)
{
response = mmu_get_response(); //wait for "ok" from mmu
response = mmu_get_response(move); //wait for "ok" from mmu
if (!response) { //no "ok" was received in reserved time frame, user will fix the issue on mmu unit
if (!mmu_print_saved) { //first occurence, we are saving current position, park print head in certain position and disable nozzle heater
uint8_t mmu_fail = eeprom_read_byte((uint8_t*)EEPROM_MMU_FAIL);
uint16_t mmu_fail_tot = eeprom_read_word((uint16_t*)EEPROM_MMU_FAIL_TOT);
if(mmu_fail < 255) eeprom_update_byte((uint8_t*)EEPROM_MMU_FAIL, mmu_fail + 1);
if(mmu_fail_tot < 65535) eeprom_update_word((uint16_t*)EEPROM_MMU_FAIL_TOT, mmu_fail_tot + 1);
if (lcd_update_enabled) {
lcd_update_was_enabled = true;
lcd_update_enable(false);
@ -424,6 +601,7 @@ void manage_response(bool move_axes, bool turn_off_nozzle)
//set nozzle target temperature to 0
setAllTargetHotends(0);
}
disable_e0(); //turn off E-stepper to prevent overheating and alow filament pull-out if necessary
}
//first three lines are used for printing multiscreen message; last line contains measured and target nozzle temperature
@ -438,19 +616,21 @@ void manage_response(bool move_axes, bool turn_off_nozzle)
}
lcd_set_degree();
lcd_set_cursor(0, 4); //line 4
//Print the hotend temperature (9 chars total) and fill rest of the line with space
int chars = lcd_printf_P(_N("%c%3d/%d%c"), LCD_STR_THERMOMETER[0],(int)(degHotend(active_extruder) + 0.5), (int)(degTargetHotend(active_extruder) + 0.5), LCD_STR_DEGREE[0]);
lcd_space(9 - chars);
//5 seconds delay
for (uint8_t i = 0; i < 50; i++) {
for (uint8_t i = 0; i < 5; i++) {
if (lcd_clicked()) {
setTargetHotend(hotend_temp_bckp, active_extruder);
/// mmu_cmd = mmu_last_cmd;
break;
}
delay_keep_alive(100);
}
//Print the hotend temperature (9 chars total) and fill rest of the line with space
lcd_set_cursor(0, 4); //line 4
int chars = lcd_printf_P(_N("%c%3d/%d%c"), LCD_STR_THERMOMETER[0],(int)(degHotend(active_extruder) + 0.5), (int)(degTargetHotend(active_extruder) + 0.5), LCD_STR_DEGREE[0]);
lcd_space(9 - chars);
delay_keep_alive(1000);
}
}
else if (mmu_print_saved) {
@ -507,7 +687,14 @@ void mmu_load_to_nozzle()
bool saved_e_relative_mode = axis_relative_modes[E_AXIS];
if (!saved_e_relative_mode) axis_relative_modes[E_AXIS] = true;
current_position[E_AXIS] += 7.2f;
if (mmu_idler_sensor_detected)
{
current_position[E_AXIS] += 3.0f;
}
else
{
current_position[E_AXIS] += 7.2f;
}
float feedrate = 562;
plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS], feedrate / 60, active_extruder);
st_synchronize();
@ -574,7 +761,7 @@ void mmu_M600_load_filament(bool automatic)
#endif //MMU_M600_SWITCH_EXTRUDER
}
else {
tmp_extruder = (tmp_extruder+1)%5;
tmp_extruder = ad_getAlternative(tmp_extruder);
}
lcd_update_enable(false);
lcd_clear();
@ -587,9 +774,10 @@ void mmu_M600_load_filament(bool automatic)
// mmu_printf_P(PSTR("T%d\n"), tmp_extruder);
mmu_command(MMU_CMD_T0 + tmp_extruder);
manage_response(false, true);
mmu_command(MMU_CMD_C0);
manage_response(false, true, MMU_LOAD_MOVE);
mmu_continue_loading();
mmu_extruder = tmp_extruder; //filament change is finished
mmu_load_to_nozzle();
load_filament_final_feed();
st_synchronize();
@ -791,13 +979,14 @@ void extr_unload()
lcd_clear();
lcd_set_cursor(0, 1); lcd_puts_P(_T(MSG_UNLOADING_FILAMENT));
lcd_print(" ");
lcd_print(mmu_extruder + 1);
if (mmu_extruder == MMU_FILAMENT_UNKNOWN) lcd_print(" ");
else lcd_print(mmu_extruder + 1);
filament_ramming();
mmu_command(MMU_CMD_U0);
// get response
manage_response(false, true);
manage_response(false, true, MMU_UNLOAD_MOVE);
lcd_update_enable(true);
#else //SNMM
@ -1093,8 +1282,8 @@ void lcd_mmu_load_to_nozzle(uint8_t filament_nr)
lcd_print(" ");
lcd_print(tmp_extruder + 1);
mmu_command(MMU_CMD_T0 + tmp_extruder);
manage_response(true, true);
mmu_command(MMU_CMD_C0);
manage_response(true, true, MMU_TCODE_MOVE);
mmu_continue_loading();
mmu_extruder = tmp_extruder; //filament change is finished
mmu_load_to_nozzle();
load_filament_final_feed();
@ -1130,7 +1319,7 @@ void mmu_eject_filament(uint8_t filament, bool recover)
plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS], 2500 / 60, active_extruder);
st_synchronize();
mmu_command(MMU_CMD_E0 + filament);
manage_response(false, false);
manage_response(false, false, MMU_UNLOAD_MOVE);
if (recover)
{
lcd_show_fullscreen_message_and_wait_P(_i("Please remove filament and then press the knob."));
@ -1150,3 +1339,47 @@ void mmu_eject_filament(uint8_t filament, bool recover)
puts_P(PSTR("Filament nr out of range!"));
}
}
void mmu_continue_loading()
{
if (mmu_idler_sensor_detected) {
for (uint8_t i = 0; i < MMU_IDLER_SENSOR_ATTEMPTS_NR; i++) {
if (PIN_GET(MMU_IDLER_SENSOR_PIN) == 0) return;
#ifdef MMU_DEBUG
printf_P(PSTR("Additional load attempt nr. %d\n"), i);
#endif // MMU_DEBUG
mmu_command(MMU_CMD_C0);
manage_response(true, true, MMU_LOAD_MOVE);
}
if (PIN_GET(MMU_IDLER_SENSOR_PIN) != 0) {
uint8_t mmu_load_fail = eeprom_read_byte((uint8_t*)EEPROM_MMU_LOAD_FAIL);
uint16_t mmu_load_fail_tot = eeprom_read_word((uint16_t*)EEPROM_MMU_LOAD_FAIL_TOT);
if(mmu_load_fail < 255) eeprom_update_byte((uint8_t*)EEPROM_MMU_LOAD_FAIL, mmu_load_fail + 1);
if(mmu_load_fail_tot < 65535) eeprom_update_word((uint16_t*)EEPROM_MMU_LOAD_FAIL_TOT, mmu_load_fail_tot + 1);
char cmd[3];
//pause print, show error message and then repeat last T-code
stop_and_save_print_to_ram(0, 0);
//lift z
current_position[Z_AXIS] += Z_PAUSE_LIFT;
if (current_position[Z_AXIS] > Z_MAX_POS) current_position[Z_AXIS] = Z_MAX_POS;
plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS], 15, active_extruder);
st_synchronize();
//Move XY to side
current_position[X_AXIS] = X_PAUSE_POS;
current_position[Y_AXIS] = Y_PAUSE_POS;
plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS], 50, active_extruder);
st_synchronize();
//set nozzle target temperature to 0
setAllTargetHotends(0);
lcd_setstatuspgm(_i("MMU load failed "));////MSG_RECOVERING_PRINT c=20 r=1
mmu_fil_loaded = false; //so we can retry same T-code again
isPrintPaused = true;
}
}
else { //mmu_idler_sensor_detected == false
mmu_command(MMU_CMD_C0);
}
}

View file

@ -1,19 +1,33 @@
//mmu.h
//! @file
#include <inttypes.h>
extern bool mmu_enabled;
extern bool mmu_fil_loaded;
extern uint8_t mmu_extruder;
extern uint8_t tmp_extruder;
extern int8_t mmu_finda;
extern bool mmu_idler_sensor_detected;
extern bool mmu_loading_flag;
extern int16_t mmu_version;
extern int16_t mmu_buildnr;
extern uint16_t mmu_power_failures;
#define MMU_FILAMENT_UNKNOWN 255
#define MMU_NO_MOVE 0
#define MMU_UNLOAD_MOVE 1
#define MMU_LOAD_MOVE 2
#define MMU_TCODE_MOVE 3
#define MMU_LOAD_FEEDRATE 19.02f //mm/s
#define MMU_LOAD_TIME_MS 2000 //should be fine tuned to load time for shortest allowed PTFE tubing and maximum loading speed
#define MMU_CMD_NONE 0
#define MMU_CMD_T0 0x10
@ -34,7 +48,7 @@ extern int16_t mmu_buildnr;
#define MMU_CMD_E3 0x53
#define MMU_CMD_E4 0x54
#define MMU_CMD_R0 0x60
#define MMU_CMD_S3 0x73
extern int mmu_puts_P(const char* str);
@ -42,6 +56,7 @@ extern int mmu_printf_P(const char* format, ...);
extern int8_t mmu_rx_ok(void);
extern bool check_for_idler_sensor();
extern void mmu_init(void);
@ -54,9 +69,9 @@ extern int8_t mmu_set_filament_type(uint8_t extruder, uint8_t filament);
extern void mmu_command(uint8_t cmd);
extern bool mmu_get_response(void);
extern bool mmu_get_response(uint8_t move = 0);
extern void manage_response(bool move_axes, bool turn_off_nozzle);
extern void manage_response(bool move_axes, bool turn_off_nozzle, uint8_t move = MMU_NO_MOVE);
extern void mmu_load_to_nozzle();
@ -103,3 +118,4 @@ extern void mmu_eject_fil_1();
extern void mmu_eject_fil_2();
extern void mmu_eject_fil_3();
extern void mmu_eject_fil_4();
extern void mmu_continue_loading();

View file

@ -121,6 +121,7 @@
#define TACH_0 79 // !!! changed from 81 (EINY03)
#define TACH_1 80
#define MMU_IDLER_SENSOR_PIN 62 //idler sensor @PK0 (digital pin 62/A8)
// Support for an 8 bit logic analyzer, for example the Saleae.
// Channels 0-2 are fast, they could generate 2.667Mhz waveform with a software loop.

View file

@ -102,7 +102,7 @@
#define SDCARDDETECT 72
#define MMU_IDLER_SENSOR_PIN 62 //idler sensor @PK0 (digital pin 62/A8)
// Support for an 8 bit logic analyzer, for example the Saleae.
// Channels 0-2 are fast, they could generate 2.667Mhz waveform with a software loop.

View file

@ -102,7 +102,7 @@
#define SDCARDDETECT 15
#define MMU_IDLER_SENSOR_PIN 62 //idler sensor @PK0 (digital pin 62/A8)
// Support for an 8 bit logic analyzer, for example the Saleae.
// Channels 0-2 are fast, they could generate 2.667Mhz waveform with a software loop.

View file

@ -45,6 +45,12 @@
#include "Configuration_prusa.h"
extern "C" {
extern void timer02_init(void);
extern void timer02_set_pwm0(uint8_t pwm0);
}
//===========================================================================
//=============================public variables============================
//===========================================================================
@ -106,15 +112,15 @@ static volatile bool temp_meas_ready = false;
#ifdef PIDTEMP
//static cannot be external:
static float temp_iState[EXTRUDERS] = { 0 };
static float temp_dState[EXTRUDERS] = { 0 };
static float iState_sum[EXTRUDERS] = { 0 };
static float dState_last[EXTRUDERS] = { 0 };
static float pTerm[EXTRUDERS];
static float iTerm[EXTRUDERS];
static float dTerm[EXTRUDERS];
//int output;
static float pid_error[EXTRUDERS];
static float temp_iState_min[EXTRUDERS];
static float temp_iState_max[EXTRUDERS];
static float iState_sum_min[EXTRUDERS];
static float iState_sum_max[EXTRUDERS];
// static float pid_input[EXTRUDERS];
// static float pid_output[EXTRUDERS];
static bool pid_reset[EXTRUDERS];
@ -257,6 +263,7 @@ static void temp_runaway_stop(bool isPreheat, bool isBed);
if (extruder<0)
{
soft_pwm_bed = (MAX_BED_POWER)/2;
timer02_set_pwm0(soft_pwm_bed << 1);
bias = d = (MAX_BED_POWER)/2;
}
else
@ -293,7 +300,10 @@ static void temp_runaway_stop(bool isPreheat, bool isBed);
if(millis() - t2 > 5000) {
heating=false;
if (extruder<0)
{
soft_pwm_bed = (bias - d) >> 1;
timer02_set_pwm0(soft_pwm_bed << 1);
}
else
soft_pwm[extruder] = (bias - d) >> 1;
t1=millis();
@ -347,7 +357,10 @@ static void temp_runaway_stop(bool isPreheat, bool isBed);
}
}
if (extruder<0)
{
soft_pwm_bed = (bias + d) >> 1;
timer02_set_pwm0(soft_pwm_bed << 1);
}
else
soft_pwm[extruder] = (bias + d) >> 1;
pid_cycle++;
@ -418,7 +431,7 @@ void updatePID()
{
#ifdef PIDTEMP
for(int e = 0; e < EXTRUDERS; e++) {
temp_iState_max[e] = PID_INTEGRAL_DRIVE_MAX / cs.Ki;
iState_sum_max[e] = PID_INTEGRAL_DRIVE_MAX / cs.Ki;
}
#endif
#ifdef PIDTEMPBED
@ -587,6 +600,12 @@ void checkExtruderAutoFans()
#endif // any extruder auto fan pins set
// ready for eventually parameters adjusting
void resetPID(uint8_t) // only for compiler-warning elimination (if function do nothing)
//void resetPID(uint8_t extruder)
{
}
void manage_heater()
{
#ifdef WATCHDOG
@ -598,6 +617,7 @@ void manage_heater()
if(temp_meas_ready != true) //better readability
return;
// more precisely - this condition partially stabilizes time interval for regulation values evaluation (@ ~ 230ms)
updateTemperaturesFromRawValues();
@ -619,38 +639,42 @@ void manage_heater()
pid_input = current_temperature[e];
#ifndef PID_OPENLOOP
pid_error[e] = target_temperature[e] - pid_input;
if(pid_error[e] > PID_FUNCTIONAL_RANGE) {
pid_output = BANG_MAX;
pid_reset[e] = true;
}
else if(pid_error[e] < -PID_FUNCTIONAL_RANGE || target_temperature[e] == 0) {
if(target_temperature[e] == 0) {
pid_output = 0;
pid_reset[e] = true;
}
else {
if(pid_reset[e] == true) {
temp_iState[e] = 0.0;
} else {
pid_error[e] = target_temperature[e] - pid_input;
if(pid_reset[e]) {
iState_sum[e] = 0.0;
dTerm[e] = 0.0; // 'dState_last[e]' initial setting is not necessary (see end of if-statement)
pid_reset[e] = false;
}
#ifndef PonM
pTerm[e] = cs.Kp * pid_error[e];
temp_iState[e] += pid_error[e];
temp_iState[e] = constrain(temp_iState[e], temp_iState_min[e], temp_iState_max[e]);
iTerm[e] = cs.Ki * temp_iState[e];
//K1 defined in Configuration.h in the PID settings
iState_sum[e] += pid_error[e];
iState_sum[e] = constrain(iState_sum[e], iState_sum_min[e], iState_sum_max[e]);
iTerm[e] = cs.Ki * iState_sum[e];
// K1 defined in Configuration.h in the PID settings
#define K2 (1.0-K1)
dTerm[e] = (cs.Kd * (pid_input - temp_dState[e]))*K2 + (K1 * dTerm[e]);
pid_output = pTerm[e] + iTerm[e] - dTerm[e];
dTerm[e] = (cs.Kd * (pid_input - dState_last[e]))*K2 + (K1 * dTerm[e]); // e.g. digital filtration of derivative term changes
pid_output = pTerm[e] + iTerm[e] - dTerm[e]; // subtraction due to "Derivative on Measurement" method (i.e. derivative of input instead derivative of error is used)
if (pid_output > PID_MAX) {
if (pid_error[e] > 0 ) temp_iState[e] -= pid_error[e]; // conditional un-integration
if (pid_error[e] > 0 ) iState_sum[e] -= pid_error[e]; // conditional un-integration
pid_output=PID_MAX;
} else if (pid_output < 0){
if (pid_error[e] < 0 ) temp_iState[e] -= pid_error[e]; // conditional un-integration
} else if (pid_output < 0) {
if (pid_error[e] < 0 ) iState_sum[e] -= pid_error[e]; // conditional un-integration
pid_output=0;
}
#else // PonM ("Proportional on Measurement" method)
iState_sum[e] += cs.Ki * pid_error[e];
iState_sum[e] -= cs.Kp * (pid_input - dState_last[e]);
iState_sum[e] = constrain(iState_sum[e], 0, PID_INTEGRAL_DRIVE_MAX);
dTerm[e] = cs.Kd * (pid_input - dState_last[e]);
pid_output = iState_sum[e] - dTerm[e]; // subtraction due to "Derivative on Measurement" method (i.e. derivative of input instead derivative of error is used)
pid_output = constrain(pid_output, 0, PID_MAX);
#endif // PonM
}
temp_dState[e] = pid_input;
dState_last[e] = pid_input;
#else
pid_output = constrain(target_temperature[e], 0, PID_MAX);
#endif //PID_OPENLOOP
@ -667,7 +691,7 @@ void manage_heater()
SERIAL_ECHO(" iTerm ");
SERIAL_ECHO(iTerm[e]);
SERIAL_ECHO(" dTerm ");
SERIAL_ECHOLN(dTerm[e]);
SERIAL_ECHOLN(-dTerm[e]);
#endif //PID_DEBUG
#else /* PID off */
pid_output = 0;
@ -677,16 +701,14 @@ void manage_heater()
#endif
// Check if temperature is within the correct range
if(current_temperature[e] < maxttemp[e])
if((current_temperature[e] < maxttemp[e]) && (target_temperature[e] != 0))
{
soft_pwm[e] = (int)pid_output >> 1;
}
else
{
{
soft_pwm[e] = 0;
}
if(target_temperature[e]==0)
soft_pwm[e] = 0;
#ifdef WATCH_TEMP_PERIOD
if(watchmillis[e] && millis() - watchmillis[e] > WATCH_TEMP_PERIOD)
@ -772,9 +794,11 @@ void manage_heater()
if(current_temperature_bed < BED_MAXTEMP)
{
soft_pwm_bed = (int)pid_output >> 1;
timer02_set_pwm0(soft_pwm_bed << 1);
}
else {
soft_pwm_bed = 0;
timer02_set_pwm0(soft_pwm_bed << 1);
}
#elif !defined(BED_LIMIT_SWITCHING)
@ -784,15 +808,18 @@ void manage_heater()
if(current_temperature_bed >= target_temperature_bed)
{
soft_pwm_bed = 0;
timer02_set_pwm0(soft_pwm_bed << 1);
}
else
{
soft_pwm_bed = MAX_BED_POWER>>1;
timer02_set_pwm0(soft_pwm_bed << 1);
}
}
else
{
soft_pwm_bed = 0;
timer02_set_pwm0(soft_pwm_bed << 1);
WRITE(HEATER_BED_PIN,LOW);
}
#else //#ifdef BED_LIMIT_SWITCHING
@ -802,15 +829,18 @@ void manage_heater()
if(current_temperature_bed > target_temperature_bed + BED_HYSTERESIS)
{
soft_pwm_bed = 0;
timer02_set_pwm0(soft_pwm_bed << 1);
}
else if(current_temperature_bed <= target_temperature_bed - BED_HYSTERESIS)
{
soft_pwm_bed = MAX_BED_POWER>>1;
timer02_set_pwm0(soft_pwm_bed << 1);
}
}
else
{
soft_pwm_bed = 0;
timer02_set_pwm0(soft_pwm_bed << 1);
WRITE(HEATER_BED_PIN,LOW);
}
#endif
@ -987,7 +1017,6 @@ static void updateTemperaturesFromRawValues()
CRITICAL_SECTION_END;
}
void tp_init()
{
#if MB(RUMBA) && ((TEMP_SENSOR_0==-1)||(TEMP_SENSOR_1==-1)||(TEMP_SENSOR_2==-1)||(TEMP_SENSOR_BED==-1))
@ -1001,8 +1030,8 @@ void tp_init()
// populate with the first value
maxttemp[e] = maxttemp[0];
#ifdef PIDTEMP
temp_iState_min[e] = 0.0;
temp_iState_max[e] = PID_INTEGRAL_DRIVE_MAX / cs.Ki;
iState_sum_min[e] = 0.0;
iState_sum_max[e] = PID_INTEGRAL_DRIVE_MAX / cs.Ki;
#endif //PIDTEMP
#ifdef PIDTEMPBED
temp_iState_min_bed = 0.0;
@ -1054,10 +1083,12 @@ void tp_init()
adc_init();
timer02_init();
// Use timer0 for temperature measurement
// Interleave temperature interrupt with millies interrupt
OCR0B = 128;
TIMSK0 |= (1<<OCIE0B);
OCR2B = 128;
TIMSK2 |= (1<<OCIE2B);
// Wait for temperature measurement to settle
delay(250);
@ -1365,6 +1396,7 @@ void disable_heater()
#if defined(TEMP_BED_PIN) && TEMP_BED_PIN > -1
target_temperature_bed=0;
soft_pwm_bed=0;
timer02_set_pwm0(soft_pwm_bed << 1);
#if defined(HEATER_BED_PIN) && HEATER_BED_PIN > -1
WRITE(HEATER_BED_PIN,LOW);
#endif
@ -1529,8 +1561,8 @@ void adc_ready(void) //callback from adc when sampling finished
} // extern "C"
// Timer 0 is shared with millies
ISR(TIMER0_COMPB_vect) // @ 1kHz ~ 1ms
// Timer2 (originaly timer0) is shared with millies
ISR(TIMER2_COMPB_vect)
{
static bool _lock = false;
if (_lock) return;
@ -1597,7 +1629,7 @@ ISR(TIMER0_COMPB_vect) // @ 1kHz ~ 1ms
#endif
#if defined(HEATER_BED_PIN) && HEATER_BED_PIN > -1
soft_pwm_b = soft_pwm_bed;
if(soft_pwm_b > 0) WRITE(HEATER_BED_PIN,1); else WRITE(HEATER_BED_PIN,0);
//if(soft_pwm_b > 0) WRITE(HEATER_BED_PIN,1); else WRITE(HEATER_BED_PIN,0);
#endif
#ifdef FAN_SOFT_PWM
soft_pwm_fan = fanSpeedSoftPwm / 2;
@ -1731,7 +1763,7 @@ ISR(TIMER0_COMPB_vect) // @ 1kHz ~ 1ms
state_timer_heater_b = MIN_STATE_TIME;
}
state_heater_b = 1;
WRITE(HEATER_BED_PIN, 1);
//WRITE(HEATER_BED_PIN, 1);
}
} else {
// turn OFF heather only if the minimum time is up

View file

@ -27,8 +27,8 @@
#include "stepper.h"
#endif
#define ENABLE_TEMPERATURE_INTERRUPT() TIMSK0 |= (1<<OCIE0B)
#define DISABLE_TEMPERATURE_INTERRUPT() TIMSK0 &= ~(1<<OCIE0B)
#define ENABLE_TEMPERATURE_INTERRUPT() TIMSK2 |= (1<<OCIE2B)
#define DISABLE_TEMPERATURE_INTERRUPT() TIMSK2 &= ~(1<<OCIE2B)
// public functions
void tp_init(); //initialize the heating
@ -87,6 +87,8 @@ extern int current_voltage_raw_bed;
extern volatile int babystepsTodo[3];
#endif
void resetPID(uint8_t extruder);
inline void babystepsTodoZadd(int n)
{
if (n != 0) {
@ -137,11 +139,15 @@ FORCE_INLINE float degTargetBed() {
FORCE_INLINE void setTargetHotend(const float &celsius, uint8_t extruder) {
target_temperature[extruder] = celsius;
resetPID(extruder);
};
static inline void setTargetHotendSafe(const float &celsius, uint8_t extruder)
{
if (extruder<EXTRUDERS) target_temperature[extruder] = celsius;
if (extruder<EXTRUDERS) {
target_temperature[extruder] = celsius;
resetPID(extruder);
}
}
static inline void setAllTargetHotends(const float &celsius)

103
Firmware/timer02.c Normal file
View file

@ -0,0 +1,103 @@
//timer02.c
// use atmega timer2 as main system timer instead of timer0
// timer0 is used for fast pwm (OC0B output)
// original OVF handler is disabled
#include <avr/io.h>
#include <avr/interrupt.h>
#include <Arduino.h>
uint8_t timer02_pwm0 = 0;
void timer02_set_pwm0(uint8_t pwm0)
{
if (timer02_pwm0 == pwm0) return;
if (pwm0)
{
TCCR0A |= (2 << COM0B0);
OCR0B = pwm0 - 1;
}
else
{
TCCR0A &= ~(2 << COM0B0);
OCR0B = 0;
}
}
void timer02_init(void)
{
//save sreg
uint8_t _sreg = SREG;
//disable interrupts for sure
cli();
//mask timer0 interrupts - disable all
TIMSK0 &= ~(1<<TOIE0);
TIMSK0 &= ~(1<<OCIE0A);
TIMSK0 &= ~(1<<OCIE0B);
//setup timer0
TCCR0A = 0x00; //COM_A-B=00, WGM_0-1=00
TCCR0B = (1 << CS00); //WGM_2=0, CS_0-2=011
//switch timer0 to fast pwm mode
TCCR0A |= (3 << WGM00); //WGM_0-1=11
//set OCR0B register to zero
OCR0B = 0;
//disable OCR0B output (will be enabled in timer02_set_pwm0)
TCCR0A &= ~(2 << COM0B0);
//setup timer2
TCCR2A = 0x00; //COM_A-B=00, WGM_0-1=00
TCCR2B = (3 << CS20); //WGM_2=0, CS_0-2=011
//mask timer2 interrupts - enable OVF, disable others
TIMSK2 |= (1<<TOIE2);
TIMSK2 &= ~(1<<OCIE2A);
TIMSK2 &= ~(1<<OCIE2B);
//set timer2 OCR registers (OCRB interrupt generated 0.5ms after OVF interrupt)
OCR2A = 0;
OCR2B = 128;
//restore sreg (enable interrupts)
SREG = _sreg;
}
//following code is OVF handler for timer 2
//it is copy-paste from wiring.c and modified for timer2
//variables timer0_overflow_count and timer0_millis are declared in wiring.c
// the prescaler is set so that timer0 ticks every 64 clock cycles, and the
// the overflow handler is called every 256 ticks.
#define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(64 * 256))
// the whole number of milliseconds per timer0 overflow
#define MILLIS_INC (MICROSECONDS_PER_TIMER0_OVERFLOW / 1000)
// the fractional number of milliseconds per timer0 overflow. we shift right
// by three to fit these numbers into a byte. (for the clock speeds we care
// about - 8 and 16 MHz - this doesn't lose precision.)
#define FRACT_INC ((MICROSECONDS_PER_TIMER0_OVERFLOW % 1000) >> 3)
#define FRACT_MAX (1000 >> 3)
extern volatile unsigned long timer0_overflow_count;
extern volatile unsigned long timer0_millis;
unsigned char timer0_fract = 0;
ISR(TIMER2_OVF_vect)
{
// copy these to local variables so they can be stored in registers
// (volatile variables must be read from memory on every access)
unsigned long m = timer0_millis;
unsigned char f = timer0_fract;
m += MILLIS_INC;
f += FRACT_INC;
if (f >= FRACT_MAX)
{
f -= FRACT_MAX;
m += 1;
}
timer0_fract = f;
timer0_millis = m;
timer0_overflow_count++;
}

View file

@ -38,7 +38,7 @@
#include "mmu.h"
#include "static_assert.h"
#include "io_atmega2560.h"
extern bool fans_check_enabled;
@ -136,6 +136,11 @@ static void lcd_menu_extruder_info();
static void lcd_menu_xyz_y_min();
static void lcd_menu_xyz_skew();
static void lcd_menu_xyz_offset();
static void lcd_menu_fails_stats_mmu();
static void lcd_menu_fails_stats_mmu_print();
static void lcd_menu_fails_stats_mmu_total();
static void lcd_menu_show_sensors_state();
#if defined(TMC2130) || defined(FILAMENT_SENSOR)
static void lcd_menu_fails_stats();
#endif //TMC2130 or FILAMENT_SENSOR
@ -195,6 +200,9 @@ static void menu_action_sddirectory(const char* filename);
#define ENCODER_FEEDRATE_DEADZONE 10
#define STATE_NA 255
#define STATE_OFF 0
#define STATE_ON 1
/*
#define MENU_ITEM(type, label, args...) do { \
@ -537,10 +545,15 @@ void lcdui_print_percent_done(void)
void lcdui_print_extruder(void)
{
int chars = 0;
if (mmu_extruder == tmp_extruder)
chars = lcd_printf_P(_N(" F%u"), mmu_extruder+1);
if (mmu_extruder == tmp_extruder) {
if (mmu_extruder == MMU_FILAMENT_UNKNOWN) chars = lcd_printf_P(_N(" F?"));
else chars = lcd_printf_P(_N(" F%u"), mmu_extruder + 1);
}
else
chars = lcd_printf_P(_N(" %u>%u"), mmu_extruder+1, tmp_extruder+1);
{
if (mmu_extruder == MMU_FILAMENT_UNKNOWN) chars = lcd_printf_P(_N(" ?>%u"), tmp_extruder + 1);
else chars = lcd_printf_P(_N(" %u>%u"), mmu_extruder + 1, tmp_extruder + 1);
}
lcd_space(5 - chars);
}
@ -1914,6 +1927,48 @@ static void lcd_menu_extruder_info()
menu_back_if_clicked();
}
static void lcd_menu_fails_stats_mmu()
{
MENU_BEGIN();
MENU_ITEM_BACK_P(_T(MSG_MAIN));
MENU_ITEM_SUBMENU_P(_i("Last print"), lcd_menu_fails_stats_mmu_print);
MENU_ITEM_SUBMENU_P(_i("Total"), lcd_menu_fails_stats_mmu_total);
MENU_END();
}
static void lcd_menu_fails_stats_mmu_print()
{
//01234567890123456789
//Last print failures
// MMU fails 000
// MMU load fails 000
//
//////////////////////
lcd_timeoutToStatus.stop(); //infinite timeout
uint8_t fails = eeprom_read_byte((uint8_t*)EEPROM_MMU_FAIL);
uint16_t load_fails = eeprom_read_byte((uint8_t*)EEPROM_MMU_LOAD_FAIL);
// lcd_printf_P(PSTR(ESC_H(0,0) "Last print failures" ESC_H(1,1) "Power failures %-3d" ESC_H(1,2) "Filam. runouts %-3d" ESC_H(1,3) "Crash X %-3d Y %-3d"), power, filam, crashX, crashY);
lcd_printf_P(PSTR(ESC_H(0,0) "%S" ESC_H(1,1) "%S %-3d" ESC_H(1,2) "%S %-3d" ESC_H(1,3)), _i("Last print failures"), _i("MMU fails"), fails, _i("MMU load fails"), load_fails);
menu_back_if_clicked_fb();
}
static void lcd_menu_fails_stats_mmu_total()
{
//01234567890123456789
//Last print failures
// MMU fails 000
// MMU load fails 000
//
//////////////////////
mmu_command(MMU_CMD_S3);
lcd_timeoutToStatus.stop(); //infinite timeout
uint8_t fails = eeprom_read_byte((uint8_t*)EEPROM_MMU_FAIL_TOT);
uint16_t load_fails = eeprom_read_byte((uint8_t*)EEPROM_MMU_LOAD_FAIL_TOT);
// lcd_printf_P(PSTR(ESC_H(0,0) "Last print failures" ESC_H(1,1) "Power failures %-3d" ESC_H(1,2) "Filam. runouts %-3d" ESC_H(1,3) "Crash X %-3d Y %-3d"), power, filam, crashX, crashY);
lcd_printf_P(PSTR(ESC_H(0,0) "%S" ESC_H(1,1) "%S %-3d" ESC_H(1,2) "%S %-3d" ESC_H(1,3) "%S %-3d"), _i("Total failures"), _i("MMU fails"), fails, _i("MMU load fails"), load_fails, _i("MMU power fails"), mmu_power_failures);
menu_back_if_clicked_fb();
}
#if defined(TMC2130) && defined(FILAMENT_SENSOR)
static void lcd_menu_fails_stats_total()
{
@ -1950,6 +2005,7 @@ static void lcd_menu_fails_stats_print()
lcd_printf_P(PSTR(ESC_H(0,0) "%S" ESC_H(1,1) "%S %-3d" ESC_H(1,2) "%S %-3d" ESC_H(1,3) "%S X %-3d Y %-3d"), _i("Last print failures"), _i("Power failures"), power, _i("Filam. runouts"), filam, _i("Crash"), crashX, crashY);
menu_back_if_clicked_fb();
}
/**
* @brief Open fail statistics menu
*
@ -1965,6 +2021,7 @@ static void lcd_menu_fails_stats()
MENU_ITEM_SUBMENU_P(_i("Total"), lcd_menu_fails_stats_total);
MENU_END();
}
#elif defined(FILAMENT_SENSOR)
/**
* @brief Print last print and total filament run outs
@ -2180,16 +2237,17 @@ static void lcd_support_menu()
#ifndef MK1BP
MENU_ITEM_BACK_P(STR_SEPARATOR);
MENU_ITEM_SUBMENU_P(_i("XYZ cal. details"), lcd_menu_xyz_y_min);////MSG_XYZ_DETAILS c=19 r=1
MENU_ITEM_SUBMENU_P(_i("Extruder info"), lcd_menu_extruder_info);////MSG_INFO_EXTRUDER c=15 r=1
MENU_ITEM_SUBMENU_P(_i("Extruder info"), lcd_menu_extruder_info);////MSG_INFO_EXTRUDER c=18 r=1
MENU_ITEM_SUBMENU_P(_i("Show sensors"), lcd_menu_show_sensors_state);////MSG_INFO_SENSORS c=18 r=1
#ifdef TMC2130
MENU_ITEM_SUBMENU_P(_i("Belt status"), lcd_menu_belt_status);////MSG_MENU_BELT_STATUS c=15 r=1
MENU_ITEM_SUBMENU_P(_i("Belt status"), lcd_menu_belt_status);////MSG_MENU_BELT_STATUS c=18 r=1
#endif //TMC2130
MENU_ITEM_SUBMENU_P(_i("Temperatures"), lcd_menu_temperatures);////MSG_MENU_TEMPERATURES c=15 r=1
MENU_ITEM_SUBMENU_P(_i("Temperatures"), lcd_menu_temperatures);////MSG_MENU_TEMPERATURES c=18 r=1
#if defined (VOLT_BED_PIN) || defined (VOLT_PWR_PIN)
MENU_ITEM_SUBMENU_P(_i("Voltages"), lcd_menu_voltages);////MSG_MENU_VOLTAGES c=15 r=1
MENU_ITEM_SUBMENU_P(_i("Voltages"), lcd_menu_voltages);////MSG_MENU_VOLTAGES c=18 r=1
#endif //defined VOLT_BED_PIN || defined VOLT_PWR_PIN
#ifdef DEBUG_BUILD
@ -2351,7 +2409,7 @@ void lcd_alright() {
enc_dif = lcd_encoder_diff;
lcd_consume_click();
while (lcd_change_fil_state == 0) {
manage_heater();
@ -3239,6 +3297,7 @@ void lcd_show_fullscreen_message_and_wait_P(const char *msg)
const char *msg_next = lcd_display_message_fullscreen_P(msg);
bool multi_screen = msg_next != NULL;
lcd_set_custom_characters_nextpage();
lcd_consume_click();
KEEPALIVE_STATE(PAUSED_FOR_USER);
// Until confirmed by a button click.
for (;;) {
@ -3283,7 +3342,7 @@ bool lcd_wait_for_click_delay(uint16_t nDelay)
{
bool bDelayed;
long nTime0 = millis()/1000;
lcd_consume_click();
KEEPALIVE_STATE(PAUSED_FOR_USER);
for (;;) {
manage_heater();
@ -3332,6 +3391,7 @@ int8_t lcd_show_multiscreen_message_two_choices_and_wait_P(const char *msg, bool
// Wait for user confirmation or a timeout.
unsigned long previous_millis_cmd = millis();
int8_t enc_dif = lcd_encoder_diff;
lcd_consume_click();
//KEEPALIVE_STATE(PAUSED_FOR_USER);
for (;;) {
for (uint8_t i = 0; i < 100; ++i) {
@ -3421,6 +3481,7 @@ int8_t lcd_show_fullscreen_message_yes_no_and_wait_P(const char *msg, bool allow
// Wait for user confirmation or a timeout.
unsigned long previous_millis_cmd = millis();
int8_t enc_dif = lcd_encoder_diff;
lcd_consume_click();
KEEPALIVE_STATE(PAUSED_FOR_USER);
for (;;) {
if (allow_timeouting && millis() - previous_millis_cmd > LCD_TIMEOUT_TO_STATUS)
@ -3555,6 +3616,7 @@ static void menu_show_end_stops() {
void lcd_diag_show_end_stops()
{
lcd_clear();
lcd_consume_click();
for (;;) {
manage_heater();
manage_inactivity(true);
@ -3567,28 +3629,60 @@ void lcd_diag_show_end_stops()
lcd_return_to_status();
}
#ifdef TMC2130
static void lcd_show_pinda_state()
static void lcd_print_state(uint8_t state)
{
lcd_set_cursor(0, 0);
lcd_puts_P((PSTR("P.I.N.D.A. state")));
lcd_set_cursor(0, 2);
lcd_puts_P(READ(Z_MIN_PIN)?(PSTR("Z1 (LED off)")):(PSTR("Z0 (LED on) "))); // !!! both strings must have same length (due to dynamic refreshing)
switch (state) {
case STATE_ON:
lcd_puts_P(_i("On "));
break;
case STATE_OFF:
lcd_puts_P(_i("Off"));
break;
default:
lcd_puts_P(_i("N/A"));
break;
}
}
static void menu_show_pinda_state()
static void lcd_show_sensors_state()
{
lcd_timeoutToStatus.stop();
lcd_show_pinda_state();
if(LCD_CLICKED)
{
lcd_timeoutToStatus.start();
menu_back();
}
//0: N/A; 1: OFF; 2: ON
uint8_t chars = 0;
uint8_t pinda_state = STATE_NA;
uint8_t finda_state = STATE_NA;
uint8_t idler_state = STATE_NA;
pinda_state = READ(Z_MIN_PIN);
if (mmu_enabled) {
finda_state = mmu_finda;
}
if (mmu_idler_sensor_detected) {
idler_state = !PIN_GET(MMU_IDLER_SENSOR_PIN);
}
lcd_puts_at_P(0, 0, _i("Sensor state"));
lcd_puts_at_P(1, 1, _i("PINDA:"));
lcd_set_cursor(LCD_WIDTH - 4, 1);
lcd_print_state(pinda_state);
lcd_puts_at_P(1, 2, _i("FINDA:"));
lcd_set_cursor(LCD_WIDTH - 4, 2);
lcd_print_state(finda_state);
lcd_puts_at_P(1, 3, _i("IR:"));
lcd_set_cursor(LCD_WIDTH - 4, 3);
lcd_print_state(idler_state);
}
#endif // defined TMC2130
static void lcd_menu_show_sensors_state()
{
lcd_timeoutToStatus.stop();
lcd_show_sensors_state();
if(LCD_CLICKED)
{
lcd_timeoutToStatus.start();
menu_back();
}
}
void prusa_statistics(int _message, uint8_t _fil_nr) {
#ifdef DEBUG_DISABLE_PRUSA_STATISTICS
@ -4313,6 +4407,7 @@ void lcd_v2_calibration()
}
else {
lcd_display_message_fullscreen_P(_i("Please load PLA filament first."));////MSG_PLEASE_LOAD_PLA c=20 r=4
lcd_consume_click();
for (int i = 0; i < 20; i++) { //wait max. 2s
delay_keep_alive(100);
if (lcd_clicked()) {
@ -4944,9 +5039,7 @@ static void lcd_calibration_menu()
MENU_ITEM_SUBMENU_P(_i("Bed level correct"), lcd_adjust_bed);////MSG_BED_CORRECTION_MENU c=0 r=0
MENU_ITEM_SUBMENU_P(_i("PID calibration"), pid_extruder);////MSG_PID_EXTRUDER c=17 r=1
#ifdef TMC2130
MENU_ITEM_SUBMENU_P(_i("Show pinda state"), menu_show_pinda_state);
#else
#ifndef TMC2130
MENU_ITEM_SUBMENU_P(_i("Show end stops"), menu_show_end_stops);////MSG_SHOW_END_STOPS c=17 r=1
#endif
#ifndef MK1BP
@ -4979,7 +5072,7 @@ void bowden_menu() {
}
enc_dif = lcd_encoder_diff;
lcd_consume_click();
while (1) {
manage_heater();
@ -5086,6 +5179,7 @@ static char snmm_stop_print_menu() { //menu for choosing which filaments will be
char cursor_pos = 1;
int enc_dif = 0;
KEEPALIVE_STATE(PAUSED_FOR_USER);
lcd_consume_click();
while (1) {
manage_heater();
manage_inactivity(true);
@ -5241,7 +5335,7 @@ char reset_menu() {
lcd_clear();
lcd_set_cursor(0, 0);
lcd_print(">");
lcd_consume_click();
while (1) {
for (int i = 0; i < 4; i++) {
@ -5517,7 +5611,7 @@ unsigned char lcd_choose_color() {
lcd_print(">");
active_rows = items_no < 3 ? items_no : 3;
lcd_consume_click();
while (1) {
lcd_puts_at_P(0, 0, PSTR("Choose color:"));
for (int i = 0; i < active_rows; i++) {
@ -5852,7 +5946,9 @@ static void lcd_main_menu()
#if defined(TMC2130) || defined(FILAMENT_SENSOR)
MENU_ITEM_SUBMENU_P(_i("Fail stats"), lcd_menu_fails_stats);
#endif
if (mmu_enabled) {
MENU_ITEM_SUBMENU_P(_i("Fail stats MMU"), lcd_menu_fails_stats_mmu);
}
MENU_ITEM_SUBMENU_P(_i("Support"), lcd_support_menu);////MSG_SUPPORT c=0 r=0
#ifdef LCD_TEST
MENU_ITEM_SUBMENU_P(_i("W25x20CL init"), lcd_test_menu);////MSG_SUPPORT c=0 r=0
@ -6052,6 +6148,7 @@ static void lcd_sd_updir()
void lcd_print_stop()
{
saved_printing = false;
cancel_heatup = true;
#ifdef MESH_BED_LEVELING
mbl.active = false;
@ -6085,7 +6182,7 @@ void lcd_print_stop()
void lcd_sdcard_stop()
{
lcd_set_cursor(0, 0);
lcd_puts_P(_T(MSG_STOP_PRINT));
lcd_set_cursor(2, 2);

View file

@ -441,4 +441,6 @@ THERMISTORS SETTINGS
//#define SUPPORT_VERBOSITY
#endif
#define MMU_IDLER_SENSOR_ATTEMPTS_NR 21 //max. number of attempts to load filament if first load failed; value for max bowden length and case when loading fails right at the beginning
#endif //__CONFIGURATION_PRUSA_H

View file

@ -441,4 +441,6 @@ THERMISTORS SETTINGS
//#define SUPPORT_VERBOSITY
#endif
#define MMU_IDLER_SENSOR_ATTEMPTS_NR 21 //max. number of attempts to load filament if first load failed; value for max bowden length and case when loading fails right at the beginning
#endif //__CONFIGURATION_PRUSA_H

View file

@ -496,4 +496,6 @@
#define MMU_REQUIRED_FW_BUILDNR 132
//#define MMU_DEBUG //print communication between MMU2 and printer on serial
#define MMU_IDLER_SENSOR_ATTEMPTS_NR 21 //max. number of attempts to load filament if first load failed; value for max bowden length and case when loading fails right at the beginning
#endif //__CONFIGURATION_PRUSA_H

View file

@ -497,4 +497,6 @@
#define MMU_REQUIRED_FW_BUILDNR 132
//#define MMU_DEBUG //print communication between MMU2 and printer on serial
#define MMU_IDLER_SENSOR_ATTEMPTS_NR 21 //max. number of attempts to load filament if first load failed; value for max bowden length and case when loading fails right at the beginning
#endif //__CONFIGURATION_PRUSA_H

View file

@ -622,6 +622,8 @@
#define MMU_REQUIRED_FW_BUILDNR 83
#define MMU_HWRESET
//#define MMU_DEBUG //print communication between MMU2 and printer on serial
#define MMU_DEBUG //print communication between MMU2 and printer on serial
#define MMU_IDLER_SENSOR_ATTEMPTS_NR 21 //max. number of attempts to load filament if first load failed; value for max bowden length and case when loading fails right at the beginning
#endif //__CONFIGURATION_PRUSA_H

View file

@ -1,23 +1,32 @@
# Table of contents
<!--ts-->
* [Development environment preparation](#1-development-environment-preparation)
* [Source code compilation](#2-source-code-compilation)
* [Linux build](#linux)
* [Windows build](#windows)
* [Automated tests](#3-automated-tests)
* [Documentation](#4-documentation)
<!--te-->
# 1. Development environment preparation
1. install `"Arduino Software IDE"` for your preferred operating system
# Build
## Linux
Run shell script build.sh to build for MK3 and flash with Sli3er.
If you have different printel model, follow step [2.b](#2b) from Windows build first.
If you wish to flash from Arduino, follow step [2.c](#2c) from Windows build first.
The script downloads Arduino with our modifications and Rambo board support installed, unpacks it into folder PF-build-env-\<version\> on the same level, as your Prusa-Firmware folder is located, builds firmware for MK3 using that Arduino in Prusa-Firmware-build folder on the same level as Prusa-Firmware, runs secondary language support scripts. Firmware with secondary language support is generated in lang subfolder. Use firmware.hex for MK3 variant. Use firmware_\<lang\>.hex for other printers. Don't forget to follow step [2.b](#2b) first for non-MK3 printers.
## Windows
### 1. Development environment preparation
a. install `"Arduino Software IDE"` for your preferred operating system
`https://www.arduino.cc -> Software->Downloads`
it is recommended to use older version `"1.6.9"`, as it is used on out build server to produce official builds.
it is recommended to use version `"1.8.5"`, as it is used on out build server to produce official builds.
_note: in the case of persistent compilation problems, check the version of the currently used C/C++ compiler (GCC) - should be `4.8.1`; version can be verified by entering the command
`avr-gcc --version`
if you are not sure where the file is placed (depends on how `"Arduino Software IDE"` was installed), you can use the search feature within the file system_
_note: name collision for `"LiquidCrystal"` library known from previous versions is now obsolete (so there is no need to delete or rename original file/-s)_
2. add (`UltiMachine`) `RAMBo` board into the list of Arduino target boards
b. add (`UltiMachine`) `RAMBo` board into the list of Arduino target boards
`File->Preferences->Settings`
into text field `"Additional Boards Manager URLs"`
type location
@ -34,16 +43,19 @@ _note: select this item for any variant of board used in printers `'Prusa i3 MKx
'clicking' the item will display the installation button; select choice `"1.0.1"` from the list(last known version as of the date of issue of this document)
_(after installation, the item is labeled as `"INSTALLED"` and can then be used for target board selection)_
3. modify platform.txt to enable float printf support:
c. modify platform.txt to enable float printf support:
add "-Wl,-u,vfprintf -lprintf_flt -lm" to "compiler.c.elf.flags=" before existing flag "-Wl,--gc-sections"
example:
`"compiler.c.elf.flags=-w -Os -Wl,-u,vfprintf -lprintf_flt -lm -Wl,--gc-sections"`
# 2. Source code compilation
### 2. Source code compilation
place the source codes corresponding to your printer model obtained from the repository into the selected directory on your disk
a. place the source codes corresponding to your printer model obtained from the repository into the selected directory on your disk
`https://github.com/prusa3d/Prusa-Firmware/`
in the subdirectory `"Firmware/variants/"` select the configuration file (`.h`) corresponding to your printer model, make copy named `"Configuration_prusa.h"` (or make simple renaming) and copy them into `"Firmware/"` directory
b.<a name="2b"></a> In the subdirectory `"Firmware/variants/"` select the configuration file (`.h`) corresponding to your printer model, make copy named `"Configuration_prusa.h"` (or make simple renaming) and copy it into `"Firmware/"` directory.
c.<a name="2c"></a> In file `"Firmware/config.h"` set LANG_MODE to 0.
run `"Arduino IDE"`; select the file `"Firmware.ino"` from the subdirectory `"Firmware/"` at the location, where you placed the source codes
`File->Open`

146
Tests/AutoDeplete_test.cpp Normal file
View file

@ -0,0 +1,146 @@
/**
* @file
* @author Marek Bel
*/
#include "catch.hpp"
#include "../Firmware/AutoDeplete.h"
TEST_CASE( "AutoDeplete test.", "[AutoDeplete]" )
{
CHECK(ad_allDepleted() == false);
CHECK(ad_getAlternative(0) == 0);
CHECK(ad_getAlternative(1) == 1);
CHECK(ad_getAlternative(2) == 2);
CHECK(ad_getAlternative(3) == 3);
CHECK(ad_getAlternative(4) == 4);
ad_markDepleted(1);
CHECK(ad_getAlternative(0) == 0);
CHECK(ad_getAlternative(1) == 2);
CHECK(ad_getAlternative(2) == 2);
CHECK(ad_getAlternative(3) == 3);
CHECK(ad_getAlternative(4) == 4);
CHECK(ad_allDepleted() == false);
ad_markDepleted(3);
CHECK(ad_getAlternative(0) == 0);
CHECK(ad_getAlternative(1) == 2);
CHECK(ad_getAlternative(2) == 2);
CHECK(ad_getAlternative(3) == 4);
CHECK(ad_getAlternative(4) == 4);
CHECK(ad_allDepleted() == false);
ad_markDepleted(4);
CHECK(ad_getAlternative(0) == 0);
CHECK(ad_getAlternative(1) == 2);
CHECK(ad_getAlternative(2) == 2);
CHECK(ad_getAlternative(3) == 0);
CHECK(ad_getAlternative(4) == 0);
CHECK(ad_allDepleted() == false);
ad_markDepleted(4);
CHECK(ad_getAlternative(0) == 0);
CHECK(ad_getAlternative(1) == 2);
CHECK(ad_getAlternative(2) == 2);
CHECK(ad_getAlternative(3) == 0);
CHECK(ad_getAlternative(4) == 0);
CHECK(ad_allDepleted() == false);
ad_markDepleted(0);
CHECK(ad_getAlternative(0) == 2);
CHECK(ad_getAlternative(1) == 2);
CHECK(ad_getAlternative(2) == 2);
CHECK(ad_getAlternative(3) == 2);
CHECK(ad_getAlternative(4) == 2);
CHECK(ad_allDepleted() == false);
ad_markDepleted(2);
CHECK(ad_getAlternative(0) == 0);
CHECK(ad_getAlternative(1) == 1);
CHECK(ad_getAlternative(2) == 2);
CHECK(ad_getAlternative(3) == 3);
CHECK(ad_getAlternative(4) == 4);
CHECK(ad_allDepleted() == true);
ad_markDepleted(2);
CHECK(ad_getAlternative(0) == 0);
CHECK(ad_getAlternative(1) == 1);
CHECK(ad_getAlternative(2) == 2);
CHECK(ad_getAlternative(3) == 3);
CHECK(ad_getAlternative(4) == 4);
CHECK(ad_allDepleted() == true);
ad_markLoaded(4);
CHECK(ad_getAlternative(0) == 4);
CHECK(ad_getAlternative(1) == 4);
CHECK(ad_getAlternative(2) == 4);
CHECK(ad_getAlternative(3) == 4);
CHECK(ad_getAlternative(4) == 4);
CHECK(ad_allDepleted() == false);
ad_markLoaded(0);
CHECK(ad_getAlternative(0) == 0);
CHECK(ad_getAlternative(1) == 4);
CHECK(ad_getAlternative(2) == 4);
CHECK(ad_getAlternative(3) == 4);
CHECK(ad_getAlternative(4) == 4);
CHECK(ad_allDepleted() == false);
ad_markLoaded(3);
CHECK(ad_getAlternative(0) == 0);
CHECK(ad_getAlternative(1) == 3);
CHECK(ad_getAlternative(2) == 3);
CHECK(ad_getAlternative(3) == 3);
CHECK(ad_getAlternative(4) == 4);
CHECK(ad_allDepleted() == false);
ad_markLoaded(3);
CHECK(ad_getAlternative(0) == 0);
CHECK(ad_getAlternative(1) == 3);
CHECK(ad_getAlternative(2) == 3);
CHECK(ad_getAlternative(3) == 3);
CHECK(ad_getAlternative(4) == 4);
CHECK(ad_allDepleted() == false);
ad_markLoaded(2);
CHECK(ad_getAlternative(0) == 0);
CHECK(ad_getAlternative(1) == 2);
CHECK(ad_getAlternative(2) == 2);
CHECK(ad_getAlternative(3) == 3);
CHECK(ad_getAlternative(4) == 4);
CHECK(ad_allDepleted() == false);
ad_markLoaded(1);
CHECK(ad_getAlternative(0) == 0);
CHECK(ad_getAlternative(1) == 1);
CHECK(ad_getAlternative(2) == 2);
CHECK(ad_getAlternative(3) == 3);
CHECK(ad_getAlternative(4) == 4);
CHECK(ad_allDepleted() == false);
ad_markLoaded(1);
CHECK(ad_getAlternative(0) == 0);
CHECK(ad_getAlternative(1) == 1);
CHECK(ad_getAlternative(2) == 2);
CHECK(ad_getAlternative(3) == 3);
CHECK(ad_getAlternative(4) == 4);
CHECK(ad_allDepleted() == false);
}

40
build.sh Executable file
View file

@ -0,0 +1,40 @@
#!/bin/bash
BUILD_ENV="1.0.1"
SCRIPT_PATH="$( cd "$(dirname "$0")" ; pwd -P )"
if [ ! -d "build-env" ]; then
mkdir build-env || exit 1
fi
cd build-env || exit 2
if [ ! -f "PF-build-env-Linux64-$BUILD_ENV.zip" ]; then
wget https://github.com/mkbel/PF-build-env/releases/download/$BUILD_ENV/PF-build-env-Linux64-$BUILD_ENV.zip || exit 3
fi
if [ ! -d "../../PF-build-env-$BUILD_ENV" ]; then
unzip -q PF-build-env-Linux64-$BUILD_ENV.zip -d ../../PF-build-env-$BUILD_ENV || exit 4
fi
cd ../../PF-build-env-$BUILD_ENV || exit 5
BUILD_ENV_PATH="$( pwd -P )"
cd ..
if [ ! -d "Prusa-Firmware-build" ]; then
mkdir Prusa-Firmware-build || exit 6
fi
cd Prusa-Firmware-build || exit 7
BUILD_PATH="$( pwd -P )"
if [ ! -f "$SCRIPT_PATH/Firmware/Configuration_prusa.h" ]; then
cp $SCRIPT_PATH/Firmware/variants/1_75mm_MK3-EINSy10a-E3Dv6full.h $SCRIPT_PATH/Firmware/Configuration_prusa.h || exit 8
fi
$BUILD_ENV_PATH/arduino $SCRIPT_PATH/Firmware/Firmware.ino --verify --board rambo:avr:rambo --pref build.path=$BUILD_PATH || exit 9
export ARDUINO=$BUILD_ENV_PATH
cd $SCRIPT_PATH/lang
./lang-build.sh || exit 10
./fw-build.sh || exit 11

View file

@ -5,17 +5,19 @@
# This file is 'included' in all scripts.
#
# Arduino main folder:
export ARDUINO=C:/arduino-1.8.5
if [ -z "$ARDUINO" ]; then
export ARDUINO=C:/arduino-1.8.5
fi
#
# Arduino builder:
export BUILDER=$ARDUINO/arduino_debug.exe
export BUILDER=$ARDUINO/arduino-builder
#
# AVR gcc tools:
export OBJCOPY=$ARDUINO/hardware/tools/avr/bin/avr-objcopy.exe
export OBJDUMP=$ARDUINO/hardware/tools/avr/bin/avr-objdump.exe
export OBJCOPY=$ARDUINO/hardware/tools/avr/bin/avr-objcopy
export OBJDUMP=$ARDUINO/hardware/tools/avr/bin/avr-objdump
#
# Output folder:
export OUTDIR="../../output"
export OUTDIR="../../Prusa-Firmware-build"
#
# Objects folder:
export OBJDIR="$OUTDIR/sketch"

0
lang/fw-build.sh Normal file → Executable file
View file

0
lang/lang-build.sh Normal file → Executable file
View file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

0
lang/progmem.sh Normal file → Executable file
View file

0
lang/textaddr.sh Normal file → Executable file
View file

0
lang/update_lang.sh Normal file → Executable file
View file

12
test.sh Executable file
View file

@ -0,0 +1,12 @@
#!/bin/bash
cd .. || exit 5
if [ ! -d "Prusa-Firmware-test" ]; then
mkdir Prusa-Firmware-test || exit 10
fi
cd Prusa-Firmware-test || exit 20
cmake -G "Eclipse CDT4 - Ninja" ../Prusa-Firmware || exit 30
cmake --build . || exit 35
./tests || exit 40